fix: set httpx read timeout for long-running commands and handle
non-JSON error responses - Set per-request httpx timeout (command timeout + 10s buffer) in Commands.run() and AsyncCommands.run() for foreground exec calls, preventing HTTP read timeouts on long-running commands - Raise WrennInternalError instead of raw httpx.HTTPStatusError when handle_response() encounters a non-JSON error body (e.g. 502 from a reverse proxy)
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@ -175,3 +175,6 @@ cython_debug/
|
|||||||
.pypirc
|
.pypirc
|
||||||
|
|
||||||
CODE_EXECUTION.md
|
CODE_EXECUTION.md
|
||||||
|
|
||||||
|
.opencode/
|
||||||
|
.claude/
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import base64
|
|||||||
import json
|
import json
|
||||||
from collections.abc import AsyncIterator, Iterator
|
from collections.abc import AsyncIterator, Iterator
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import overload, Literal
|
from typing import Literal, overload
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
import httpx_ws
|
import httpx_ws
|
||||||
@ -197,7 +197,15 @@ class Commands:
|
|||||||
if tag is not None:
|
if tag is not None:
|
||||||
payload["tag"] = tag
|
payload["tag"] = tag
|
||||||
|
|
||||||
resp = self._http.post(f"/v1/capsules/{self._capsule_id}/exec", json=payload)
|
http_timeout: httpx.Timeout | None = None
|
||||||
|
if not background and timeout is not None:
|
||||||
|
http_timeout = httpx.Timeout(timeout + 10, connect=5.0)
|
||||||
|
|
||||||
|
resp = self._http.post(
|
||||||
|
f"/v1/capsules/{self._capsule_id}/exec",
|
||||||
|
json=payload,
|
||||||
|
timeout=http_timeout,
|
||||||
|
)
|
||||||
data = handle_response(resp)
|
data = handle_response(resp)
|
||||||
|
|
||||||
if background:
|
if background:
|
||||||
@ -374,8 +382,14 @@ class AsyncCommands:
|
|||||||
if tag is not None:
|
if tag is not None:
|
||||||
payload["tag"] = tag
|
payload["tag"] = tag
|
||||||
|
|
||||||
|
http_timeout: httpx.Timeout | None = None
|
||||||
|
if not background and timeout is not None:
|
||||||
|
http_timeout = httpx.Timeout(timeout + 10, connect=5.0)
|
||||||
|
|
||||||
resp = await self._http.post(
|
resp = await self._http.post(
|
||||||
f"/v1/capsules/{self._capsule_id}/exec", json=payload
|
f"/v1/capsules/{self._capsule_id}/exec",
|
||||||
|
json=payload,
|
||||||
|
timeout=http_timeout,
|
||||||
)
|
)
|
||||||
data = handle_response(resp)
|
data = handle_response(resp)
|
||||||
|
|
||||||
|
|||||||
@ -115,8 +115,11 @@ def handle_response(resp: httpx.Response) -> dict | list:
|
|||||||
try:
|
try:
|
||||||
body = resp.json()
|
body = resp.json()
|
||||||
except Exception:
|
except Exception:
|
||||||
resp.raise_for_status()
|
raise WrennInternalError(
|
||||||
raise
|
code="internal_error",
|
||||||
|
message=resp.text or f"HTTP {resp.status_code}",
|
||||||
|
status_code=resp.status_code,
|
||||||
|
)
|
||||||
|
|
||||||
err = body.get("error", {})
|
err = body.get("error", {})
|
||||||
code = err.get("code", "internal_error")
|
code = err.get("code", "internal_error")
|
||||||
|
|||||||
Reference in New Issue
Block a user