mirror of
https://github.com/usestrix/strix.git
synced 2026-08-21 18:52:47 +02:00
Proxy tool sweep: drop send_request, fix Caido SDK gotchas
send_request was a thin wrapper over the Caido Replay API that the model
could replicate with a one-liner `curl` via exec_command. The sandbox's
HTTP_PROXY env captures all such traffic for free, so the tool was
adding bugs (duplicate dispatch, dropped responses) without adding
capability. Removed across factory, tools module, sandbox-importable
caido_api helper, TUI renderer, prompt template, skill doc, and public
docs. repeat_request stays — it operates on captured request IDs with
structured modifications, which curl can't replicate cleanly.
Three caido-sdk-client workarounds that were hitting us through both
send_request and repeat_request:
- replay_send_raw used to pass CreateReplaySessionFromRaw to
sessions.create(), which seeds a stored entry server-side, then
called send() — producing two history rows per call. Empty-create +
send produces one dispatched request.
- The same helper read result.entry.response_raw, an attribute that
doesn't exist on ReplayEntry, so response bytes were silently
dropped. Fixed to walk result.entry.response.raw with proper None
guards.
- get_request_with_client passed include_request_raw / include_response_raw
based on the requested part, but the SDK's generated pydantic models
declare raw as required even though the GraphQL fragment makes it
conditional via @include. Passing False crashed view_request with a
pydantic validation error. Always request both raw bodies; the caller
picks which to surface.
Also wrapped replay.send() in asyncio.wait_for(30s) so a stalled Caido
dispatch (notably loopback targets that don't route cleanly through the
sandbox proxy) fails fast with a model-readable error instead of
hanging the agent until the function_tool 120s budget expires.
Finally, list_requests now omits the roundtrip_ms field when Caido
reports 0 — proxy-captured unscoped traffic consistently reports 0
while scoped/replay traffic carries real measurements, so the absence
of the field is now informative ("Caido didn't measure this") rather
than misleading ("this request took 0ms").
This commit is contained in:
@@ -195,80 +195,6 @@ class ViewRequestRenderer(BaseToolRenderer):
|
||||
return Static(text, classes=css_classes)
|
||||
|
||||
|
||||
@register_tool_renderer
|
||||
class SendRequestRenderer(BaseToolRenderer):
|
||||
tool_name: ClassVar[str] = "send_request"
|
||||
css_classes: ClassVar[list[str]] = ["tool-call", "proxy-tool"]
|
||||
|
||||
@classmethod
|
||||
def render(cls, tool_data: dict[str, Any]) -> Static: # noqa: PLR0912, PLR0915
|
||||
args = tool_data.get("args", {})
|
||||
result = tool_data.get("result")
|
||||
status = tool_data.get("status", "running")
|
||||
|
||||
method = args.get("method", "GET")
|
||||
url = args.get("url", "")
|
||||
req_headers = args.get("headers")
|
||||
req_body = args.get("body", "")
|
||||
|
||||
text = Text()
|
||||
text.append(PROXY_ICON, style="dim")
|
||||
text.append(" sending request", style="#06b6d4")
|
||||
|
||||
text.append("\n")
|
||||
text.append(" >> ", style="#3b82f6")
|
||||
text.append(method, style="#a78bfa")
|
||||
text.append(f" {_truncate(url, 180)}", style="dim")
|
||||
|
||||
if req_headers and isinstance(req_headers, dict):
|
||||
for k, v in list(req_headers.items())[:5]:
|
||||
text.append("\n")
|
||||
text.append(" >> ", style="#3b82f6")
|
||||
text.append(f"{k}: ", style="dim")
|
||||
text.append(_sanitize(str(v), 150), style="dim")
|
||||
|
||||
if req_body and isinstance(req_body, str):
|
||||
text.append("\n")
|
||||
text.append(" >> ", style="#3b82f6")
|
||||
body_lines = req_body.split("\n")[:4]
|
||||
for i, line in enumerate(body_lines):
|
||||
if i > 0:
|
||||
text.append("\n")
|
||||
text.append(" ", style="dim")
|
||||
text.append(_truncate(line, MAX_LINE_LENGTH), style="dim")
|
||||
if len(req_body.split("\n")) > 4:
|
||||
text.append(" ...", style="dim italic")
|
||||
|
||||
if status == "completed" and isinstance(result, dict):
|
||||
if "error" in result:
|
||||
text.append(f"\n error: {_sanitize(str(result['error']), 150)}", style="#ef4444")
|
||||
else:
|
||||
code = result.get("status_code")
|
||||
time_ms = result.get("response_time_ms")
|
||||
|
||||
text.append("\n")
|
||||
text.append(" << ", style="#22c55e")
|
||||
if code:
|
||||
text.append(f"{code}", style=_status_style(code))
|
||||
if time_ms:
|
||||
text.append(f" ({time_ms}ms)", style="dim")
|
||||
|
||||
body = result.get("body", "")
|
||||
if body and isinstance(body, str):
|
||||
lines = body.split("\n")[:6]
|
||||
for line in lines:
|
||||
text.append("\n")
|
||||
text.append(" << ", style="#22c55e")
|
||||
text.append(_truncate(line, MAX_LINE_LENGTH - 5), style="dim")
|
||||
|
||||
if len(body.split("\n")) > 6:
|
||||
text.append("\n")
|
||||
text.append(" ...", style="dim italic")
|
||||
|
||||
css_classes = cls.get_css_classes(status)
|
||||
return Static(text, classes=css_classes)
|
||||
|
||||
|
||||
@register_tool_renderer
|
||||
class RepeatRequestRenderer(BaseToolRenderer):
|
||||
tool_name: ClassVar[str] = "repeat_request"
|
||||
|
||||
Reference in New Issue
Block a user