config / pids / validate / claude_accounts / launch / fork / routes_runs /
routes_accounts, mounted by a thin sidecar.py (same `sidecar:app`, same 24
routes, same startup hook). Mutable state keeps one owner module
(pids._procs, claude_accounts._account_status_cache). Includes the
_claude_args builder from 1de9426 and its test; the Dockerfile's explicit
COPY list gains the new modules.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
75 lines
2.7 KiB
Python
75 lines
2.7 KiB
Python
"""
|
|
Request validation shared by the routes: the bearer check, and the per-field
|
|
checks that turn a body value into a command-line argument (session id, model,
|
|
harness, thinking / effort flags).
|
|
"""
|
|
|
|
import hmac
|
|
import uuid as uuidlib
|
|
|
|
from fastapi import HTTPException
|
|
|
|
import pairing
|
|
from config import EFFORT_LEVELS, MODEL_RE, PI_MODEL_RE, THINKING_OFF, TOKEN
|
|
|
|
def _auth(authorization: str | None) -> None:
|
|
"""Bearer check. The token is SIDECAR_TOKEN (the host sidecar, the
|
|
in-container runner) or, on a paired worker, the one minted at pairing."""
|
|
token = TOKEN or pairing.token()
|
|
if not token:
|
|
raise HTTPException(500, "sidecar not configured (no SIDECAR_TOKEN, "
|
|
"not paired)")
|
|
if not hmac.compare_digest(authorization or "", f"Bearer {token}"):
|
|
raise HTTPException(401, "unauthorized")
|
|
|
|
|
|
def _valid_uuid(sid: str) -> str:
|
|
sid = (sid or "").strip()
|
|
try:
|
|
uuidlib.UUID(sid) # claude requires a valid UUID for --session-id/--resume
|
|
except ValueError:
|
|
raise HTTPException(400, "sessionId must be a valid UUID")
|
|
return sid
|
|
|
|
|
|
def _valid_model(model: str | None, harness: str = "claude") -> str | None:
|
|
"""Sanity-check a `--model` value (an alias or a model id). None = unset."""
|
|
model = (model or "").strip()
|
|
if not model:
|
|
return None
|
|
rx = PI_MODEL_RE if harness == "pi" else MODEL_RE
|
|
if not rx.match(model):
|
|
raise HTTPException(400, f"invalid model: {model!r}")
|
|
return model
|
|
|
|
|
|
def _valid_harness(harness: str | None) -> str:
|
|
h = (harness or "claude").strip().lower()
|
|
if h not in ("claude", "pi"):
|
|
raise HTTPException(400, f"invalid harness: {harness!r}")
|
|
return h
|
|
|
|
|
|
def _thinking_args(thinking: bool | None, harness: str) -> list[str]:
|
|
"""The `--thinking` flag for a run, in the harness's own vocabulary.
|
|
|
|
Only *off* is expressible: thinking on (or unset) emits nothing, leaving each
|
|
CLI on its own default level. Both harnesses take the same flag name, so this
|
|
is a straight append onto either argv."""
|
|
if thinking is False:
|
|
return ["--thinking", THINKING_OFF[harness]]
|
|
return []
|
|
|
|
|
|
def _effort_args(effort: str | None, harness: str) -> list[str]:
|
|
"""The `--effort` flag for a run. Claude-only, and validated as an
|
|
allow-list (unlike `--model`, the CLI's scale is a closed set), so a bogus
|
|
value is a 400 here rather than a run that dies on an unknown flag value.
|
|
Unset — or any pi run — emits nothing and the CLI keeps its default."""
|
|
effort = (effort or "").strip().lower()
|
|
if not effort or harness != "claude":
|
|
return []
|
|
if effort not in EFFORT_LEVELS:
|
|
raise HTTPException(400, f"invalid effort: {effort!r}")
|
|
return ["--effort", effort]
|