@@ -48,8 +48,29 @@ def main() -> int:
4848 conn = sys .argv [1 ] if len (sys .argv ) > 1 else None
4949
5050 opener = build_opener (HTTPCookieProcessor (CookieJar ()))
51+ csrf_token : str | None = None
52+ csrf_header = "X-Hermes-CSRF-Token"
5153
52- def req (url : str , data = None , * , origin : str | None = None , timeout : int = 180 ):
54+ def fetch_agent_csrf () -> str | None :
55+ nonlocal csrf_token
56+ r = urllib .request .Request (
57+ f"{ frontend } /agent-api/api/auth/status" ,
58+ headers = {"Accept" : "application/json" },
59+ )
60+ with opener .open (r , timeout = 30 ) as resp :
61+ data = json .loads (resp .read ().decode () or "{}" )
62+ csrf_token = data .get ("csrf_token" ) or None
63+ return csrf_token
64+
65+ def req (
66+ url : str ,
67+ data = None ,
68+ * ,
69+ origin : str | None = None ,
70+ timeout : int = 180 ,
71+ _retried : bool = False ,
72+ ):
73+ nonlocal csrf_token
5374 body = None
5475 headers : dict [str , str ] = {}
5576 if data is not None :
@@ -58,15 +79,37 @@ def req(url: str, data=None, *, origin: str | None = None, timeout: int = 180):
5879 if origin :
5980 headers ["Origin" ] = origin
6081 headers ["Referer" ] = origin .rstrip ("/" ) + "/"
82+ # Browser Origin POSTs to /agent-api need the Hermes CSRF token once
83+ # trusted-auth is on — same contract as src/lib/api/agentClient.js.
84+ if data is not None and "/agent-api/" in url :
85+ if not csrf_token :
86+ fetch_agent_csrf ()
87+ if csrf_token :
88+ headers [csrf_header ] = csrf_token
6189 r = urllib .request .Request (
6290 url , data = body , headers = headers , method = "POST" if data is not None else "GET"
6391 )
64- with opener .open (r , timeout = timeout ) as resp :
65- raw = resp .read ().decode () or "null"
66- return json .loads (raw )
92+ try :
93+ with opener .open (r , timeout = timeout ) as resp :
94+ raw = resp .read ().decode () or "null"
95+ return json .loads (raw )
96+ except urllib .error .HTTPError as e :
97+ if e .code == 403 and not _retried and "/agent-api/" in url and data is not None :
98+ csrf_token = None
99+ fetch_agent_csrf ()
100+ return req (url , data , origin = origin , timeout = timeout , _retried = True )
101+ raise
67102
68103 print ("→ login" )
69- req (f"{ backend } /auth/login" , {"email" : email , "password" : password })
104+ # Prefer the frontend proxy so cookies match the Host /agent-api auth_request uses.
105+ try :
106+ req (
107+ f"{ frontend } /api/auth/login" ,
108+ {"email" : email , "password" : password },
109+ origin = frontend ,
110+ )
111+ except Exception :
112+ req (f"{ backend } /auth/login" , {"email" : email , "password" : password })
70113
71114 if not conn :
72115 conns = req (f"{ backend } /connections" )
@@ -81,6 +124,27 @@ def req(url: str, data=None, *, origin: str | None = None, timeout: int = 180):
81124 return 1
82125 print (f"→ connection { conn } " )
83126
127+ # Resolve the expected current_database() value from the connection record.
128+ # Hardcoding dba_agent falsely fails when the demo seed connection (demo_shop)
129+ # is selected — the agent is correct; the gate was wrong.
130+ expected_db = "dba_agent"
131+ try :
132+ conns = req (f"{ backend } /connections" )
133+ items = conns if isinstance (conns , list ) else (conns .get ("connections" ) or conns .get ("items" ) or [])
134+ for c in items :
135+ cid = c .get ("connectionId" ) or c .get ("id" )
136+ if str (cid ) == str (conn ):
137+ expected_db = (
138+ c .get ("databaseName" )
139+ or c .get ("database" )
140+ or c .get ("dbName" )
141+ or expected_db
142+ )
143+ break
144+ except Exception as e :
145+ print (f"WARN: could not resolve expected DB name ({ e } ); defaulting to { expected_db } " )
146+ print (f"→ expected current_database() = { expected_db } " )
147+
84148 # ── Agent tab ──────────────────────────────────────────────────────────
85149 print ("\n === Agent tab (browser → /agent-api → DeepSQL Agent → MCP) ===" )
86150 bridge = req (f"{ backend } /agent/session" , {"connectionId" : conn })
@@ -184,14 +248,14 @@ def req(url: str, data=None, *, origin: str | None = None, timeout: int = 180):
184248 )
185249 seen_failures = [m for m in failure_markers if m in answer_l ]
186250 called_sql = any ("execute_sql" in t for t in tools )
187- answered = "dba_agent" in answer_l
251+ answered = expected_db . lower () in answer_l
188252
189253 agent_ok = answered and called_sql and not seen_failures
190254 if not agent_ok :
191255 if not called_sql :
192256 print ("AGENT_FAIL: execute_sql was never called" )
193257 if not answered :
194- print ("AGENT_FAIL: reply lacks the expected database name 'dba_agent '" )
258+ print (f "AGENT_FAIL: reply lacks the expected database name '{ expected_db } '" )
195259 if seen_failures :
196260 print (f"AGENT_FAIL: reply reports tool failure { seen_failures } " )
197261 print ("AGENT_OK" , agent_ok )
0 commit comments