Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,17 @@ def _node_read_impl(

if result.get("status") == "ok":
read_result = result.get("read_result", {})
import base64 as _base64

content_b64 = read_result.get("content_b64", "")
content = ""
if content_b64:
try:
content = _base64.b64decode(content_b64).decode("utf-8")
except Exception:
content = ""
return json.dumps({
"content": read_result.get("content", ""),
"content": content,
"size_bytes": read_result.get("size_bytes", 0),
"truncated": read_result.get("truncated", False),
"encoding": "utf-8",
Expand Down
44 changes: 29 additions & 15 deletions wsserver/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,31 @@ async def _safe_close(websocket: WebSocket, code: int) -> None:
pass


# ---------------------------------------------------------------------------
# Request body models for internal HTTP endpoints.
# Defined at module level to avoid the `from __future__ import annotations`
# + inline-BaseModel interaction that causes FastAPI/Pydantic to treat body
# params as query params → silent 422 (see skill §2.7.0).
# ---------------------------------------------------------------------------


class _ExecRequest(BaseModel):
command: str
cwd: str | None = None
env: dict[str, str] | None = None
timeout_ms: int | None = None


class _ReadRequest(BaseModel):
path: str


class _WriteRequest(BaseModel):
path: str
content: str
mode: str = "overwrite"


# ---------------------------------------------------------------------------
# FastAPI app factory
# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -509,12 +534,6 @@ async def server_status() -> dict[str, Any]:
}

# -- Internal exec endpoint --------------------------------------------
class _ExecRequest(BaseModel):
command: str
cwd: str | None = None
env: dict[str, str] | None = None
timeout_ms: int | None = None

@app.post("/nodes/{node_name}/exec")
async def nodes_exec(node_name: str, body: _ExecRequest) -> dict[str, Any]:
conn = await registry.get(node_name)
Expand Down Expand Up @@ -570,9 +589,6 @@ async def nodes_exec(node_name: str, body: _ExecRequest) -> dict[str, Any]:
return {"status": "error", "code": 500, "reason": str(e)}

# -- Internal read endpoint -------------------------------------------
class _ReadRequest(BaseModel):
path: str

@app.post("/nodes/{node_name}/read")
async def nodes_read(node_name: str, body: _ReadRequest) -> dict[str, Any]:
conn = await registry.get(node_name)
Expand Down Expand Up @@ -620,11 +636,6 @@ async def nodes_read(node_name: str, body: _ReadRequest) -> dict[str, Any]:
return {"status": "error", "code": 500, "reason": str(e)}

# -- Internal write endpoint -------------------------------------------
class _WriteRequest(BaseModel):
path: str
content: str
mode: str = "overwrite"

@app.post("/nodes/{node_name}/write")
async def nodes_write(node_name: str, body: _WriteRequest) -> dict[str, Any]:
conn = await registry.get(node_name)
Expand All @@ -647,12 +658,15 @@ async def nodes_write(node_name: str, body: _WriteRequest) -> dict[str, Any]:
}

try:
import base64 as _base64

content_b64 = _base64.b64encode(body.content.encode("utf-8")).decode("ascii")
await conn.websocket.send_json(
{
"type": "write",
"id": request_id,
"path": body.path,
"content": body.content,
"content_b64": content_b64,
"mode": body.mode,
}
)
Expand Down
Loading