
    Tjl              	      \   d Z ddlmZ ddlZddlZddlmZ  ee      j                         j                  j                  Z
 eej                  j                  d ee
j                  dz                    Z ej                  dej                         ZddZdd	Zdd
Zd ZddZddZd Zy)u  Ultron master DB — in-process MCP giving Jarvis full Supabase/Postgres access.

Ultron scrapes Google-Maps business leads and stores EVERYTHING in one Supabase
Postgres (leads, plus the CRM: clients, contacts, lead_clients, app_users, notes,
scan coverage, scraper status). This module lets Jarvis (and any Claude on the
same box) run arbitrary SQL against it — read anything ("how many clients signed",
"who contacted whom", "fetch coffee-shop leads") and write anything (add leads,
create a client, make a user). Composes with the Drive + Smartlead tools: e.g.
query leads -> format a CSV -> drive_write it -> later load into a Smartlead campaign.

Connection mirrors Ultron's own src/supabase.js: db.<ref>.supabase.co:5432,
user postgres, SSL. Credentials come from the process env if present, else are
read from the Ultron repo's .env (the single source of truth). MCP_ULTRONDB=0
disables it.

SAFETY: full control, but a seatbelt on the irreversible stuff — DROP, TRUNCATE,
and DELETE/UPDATE with no WHERE need confirm="yes". Everything else runs freely.

Tools (mcp__ultron__<name>):
  ultron_query    run SQL (read or write) against the master DB
  ultron_schema   list tables (with row counts) or one table's columns
    )annotationsN)Path
ULTRON_ENVz.envz\b(drop|truncate)\bc                 J   t         j                  j                  dd      } t         j                  j                  dd      }| r|st        j	                         rt        j                         j                         D ]r  }|j                         }|j                  d      sd|vr)|j                  d      \  }}}|j                         |j                         }}|dk(  r| s|} h|dk(  sn|rq|}t | r|syt        j                  d|       }|syd|j                  d	       d
|fS )z@(host, password) from env, falling back to the Ultron repo .env.SUPABASE_URL SUPABASE_DB_PASSWORD#=Nz//([^.]+)\.zdb.   z.supabase.co)osenvironget_ULTRON_ENVexists	read_text
splitlinesstrip
startswith	partitionresearchgroup)urlpwlinek_vms          E/Users/ahmed/devFolder/Ultron/claude-voice/voice/ultron_db_control.py_credsr"   #   s    
**..
,C	.	3BBK..0))+668D::<Ds#s$nnS)GAq!779aggiqAN"3,,R 9 B
		.#&AL)2--    c                 b    t         j                  j                  dd      dk(  ryt               d uS )NMCP_ULTRONDB10F)r   r   r   r"    r#   r!   enabledr)   :   s)    	zz~~nc*c184r#   c                    dd| dgiS )Ncontenttext)typer,   r(   )msgs    r!   _textr/   @   s    5677r#   c                 Z    dd l } t               \  }}| j                  d| d| ddd      S )Nr   zhost=z" port=5432 user=postgres password=z  dbname=postgres sslmode=require   T)connect_timeout
autocommit)psycopgr"   connect)r4   hostr   s      r!   _connectr7   D   sE    xHD"??
v7t <* 	+<>4  Q Qr#   c                    | j                         }t        j                  |      ryt        j                  d|      r&t        j                  d|t        j
                        syy)NTz(?is)^\s*(delete|update)\bz	\bwhere\bF)r   _DANGERr   r   matchI)sqlss     r!   _needs_confirmr>   L   sE    		A~~a	xx-q1		,2440r#   c                   t               5 }|j                  |       }|j                  r|j                  D cg c]  }|j                   }}|j	                  d      }|s
	 d d d        ydj                  |      }dj                  d |D              }t        |      dk(  rdnd}| ddt        |      z   d| | }	|	d d	 t        |	      d	kD  rd
ndz   cd d d        S d|j                   dcd d d        S c c}w # 1 sw Y   y xY w)Ni  z(0 rows) | 
c              3  L   K   | ]  }d j                  d |D                yw)r@   c              3  :   K   | ]  }|dn
t        |        y w)Nr   )str).0r   s     r!   	<genexpr>z!_run.<locals>.<genexpr>.<genexpr>`   s     B12A6s   N)join)rE   rs     r!   rF   z_run.<locals>.<genexpr>_   s'      A 

BBBs   "$u   
…(showing first 500 rows)r   -i  u    …[truncated]u   OK — z row(s) affected.)r7   executedescriptionname	fetchmanyrG   lenrowcount)
r<   conncurdcolsrowsheadbodymoreouts
             r!   _runrY   V   s   	tll3??$'OO4OqAFFOD4==%D! 
 ::d#D99  D 7:$i36F2BDF"S3t9_-RvdV<Cu:c#ho!22N 
 &78 
 5 
s)   ,C;C6
C;)A*C;C;6C;;Dc                     dd l ddlm} m}  | ddt        t        d      dfd       } | ddd	t        i      dfd
       } |dd||g      S )Nr   )toolcreate_sdk_mcp_serverultron_queryu  Run SQL against the Ultron master database (Supabase Postgres) — full read AND write over every table: leads, clients, contacts, lead_clients, user_clients, lead_notes, app_users, scan_ledger, scraper_status. SELECT to fetch/aggregate (e.g. coffee-shop leads, how many clients signed, who contacted whom); INSERT/UPDATE to add leads, create a client, make a user; call RPCs via SELECT * FROM fn(...). Destructive statements (DROP/TRUNCATE, or DELETE/UPDATE with no WHERE) require confirm='yes'. Use ultron_schema first if unsure of columns.)r<   confirmc                  K   t        | j                  dd            j                         }|st        d      S t	        |      r7t        | j                  dd            j                         dk7  rt        d      S 	 t        j                  t        |       d {         S 7 # t        $ r$}t        dt        |      d d        cY d }~S d }~ww xY ww)	Nr<   r   zultron_query failed: no SQL.r^   yeszThat statement is irreversible (drop/truncate or a WHERE-less delete/update). Re-send with confirm='yes' if you really mean it.zultron_query error:    )	rD   r   r   r/   r>   lower	to_threadrY   	Exception)argsr<   easyncios      r!   r]   z"build_server.<locals>.ultron_queryl   s      $((5"%&,,.788#3txx	2'>#?#E#E#G5#P 2 3 3	@w00s;;<<; 	@/At~>??	@sH   A9C=B& B$B& #C$B& &	C/CC	CCCultron_schemau   Inspect the database structure. No `table` → list every table with its row count. With `table` → that table's columns (name + type) so you know exactly what to query/write. Use before composing a query on a table you're unsure about.tablec                  K   t        | j                  dd            j                         fd}	 t        j	                  |       d {         S 7 # t
        $ r$}t        dt        |      d d        cY d }~S d }~ww xY ww)Nri   r   c            	     <   t               5 } s| j                  d      j                         D cg c]  }|d   	 }}g }|D ]A  }	 | j                  d| d      j                         d   }|j                  d| d| d       C d	d
j                  |      z   cd d d        S | j                  df      j                         }|sddcd d d        S  dd
j                  d |D              z   cd d d        S c c}w # t        $ r d}Y w xY w# 1 sw Y   y xY w)NzWselect table_name from information_schema.tables where table_schema='public' order by 1r   zselect count(*) from ""?-  (z rows)zTables:
rA   zselect column_name, data_type from information_schema.columns where table_schema='public' and table_name=%s order by ordinal_positionzNo table named .z
 columns:
c              3  4   K   | ]  \  }}d | d| d  yw)rn   ro   )Nr(   )rE   cts      r!   rF   zDbuild_server.<locals>.ultron_schema.<locals>.work.<locals>.<genexpr>   s#     #Dttq!b2aSNts   )r7   rJ   fetchallfetchonerd   appendrG   )rP   rH   tabslinesrt   nrS   ri   s          r!   workz1build_server.<locals>.ultron_schema.<locals>.work   sV   t*.,,A+BBJ(*+M N +MQAaD +MD N E!$ $"81 =!??Gxz!!MA r!Bqc%89 " '5)99  ||B H	 'hj	 
 ,UIQ7) * !'-))#Dt#DDE+ N  ) $ #A$ sK   $DC<	D&D,-D#(DD<DDDDDDzultron_schema error: ra   )rD   r   r   r/   rc   rd   )re   r{   rf   ri   rg   s      @r!   rh   z#build_server.<locals>.ultron_schema   s}      DHHWb)*002	F0	Aw0066776 	A0Q?@@	AsF   /BA AA BA 	B!B :B;B BBultronz1.0.0)rL   versiontools)re   dictreturnr   )rg   claude_agent_sdkr[   r\   rD   )r[   r\   r]   rh   rg   s       @r!   build_serverr   h   s{    <	. "c2	4@	4@ 
/, /6s^	=
A=
A@ !w]+ r#   )r   ztuple[str, str] | None)r   bool)r.   rD   r   r   )r<   rD   r   r   )r<   rD   r   rD   )__doc__
__future__r   r   r   pathlibr   __file__resolveparent_PROJECTr   r   rD   r   compiler;   r9   r"   r)   r/   r7   r>   rY   r   r(   r#   r!   <module>r      s   , # 	 	 >!!#**112::>>,"%hoo&>"?A B
"**+RTT
2.. 8Q9$Cr#   