feat(ops): פאנל "מתאמי-סוכנים" ב-/operations — מעבר-אדפטר בכפתור (any→any)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 3s
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 3s
שלב-ה-UI של מנגנון מעבר-האדפטר (PR #247). הכפתור ב-/operations מריץ את scripts/migrate_agent_adapter.py בהוסט דרך גשר-court-fetch (הקונטיינר לא יכול לבצע — צריך FS-הוסט + DB-המובנה), בדיוק כמו כפתורי-pm2. - court_fetch_service/server.py: endpoint /adapter-migration מאומת (Bearer, COURT_FETCH_SHARED_SECRET) שמריץ את הסקריפט עם allowlist-פעולות ו-args אטומיים (exec, ללא shell; הסקריפט מאמת). symbol-light לשמירת G12 בשכבת mcp-server. - web/app.py: proxy POST /api/operations/agents/migrate-adapter → הגשר. - web-ui: useMigrateAdapter (operations.ts) + AgentAdaptersPanel לפי המוקאפ המאושר 02d-operations-adapters.html: roster per-role (מתאם נוכחי+מודל+בורר-יעד), סרגל-חירום גלובלי (הכל→Gemini/החזר→Claude), preflight בדיאלוג + toggle שחרור-כלים, דגלי מועבר/א-סימטרי. מחזר usePaperclipAgents לתצוגה. עיצוב אושר ע"י חיים בשער Claude Design (פרויקט IA Redesign X17). נבדק: tsc --noEmit נקי, eslint נקי. Invariants: G12 (גשר symbol-light; הסקריפט בשכבת scripts/ הוא שמדבר Paperclip), INV-MC1 (שתי החברות יחד), INV-IA "מקום אחד" (/operations). המשך FU-8a. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
45
web/app.py
45
web/app.py
@@ -6900,6 +6900,51 @@ async def operations_agent_reset_session(agent_id: str):
|
||||
return {"ok": True, "agent_id": agent_id, "result": result}
|
||||
|
||||
|
||||
class AdapterMigrationRequest(BaseModel):
|
||||
action: str # check | apply | revert | verify
|
||||
agent: str | None = None # agent display-name, or "all"
|
||||
to: str | None = None # target adapter (for check/apply)
|
||||
model: str | None = None # optional model override
|
||||
relax_tools: bool = False # free conflicting write tools from the global gemini excludeTools
|
||||
|
||||
|
||||
@app.post("/api/operations/agents/migrate-adapter")
|
||||
async def operations_agent_migrate_adapter(req: AdapterMigrationRequest):
|
||||
"""Migrate an agent (or 'all') to a target adapter, in both companies.
|
||||
|
||||
The migration is host-side (it needs the host filesystem — generated
|
||||
instruction copies, the gemini settings file — and the embedded board DB),
|
||||
so this proxies scripts/migrate_agent_adapter.py through the court-fetch host
|
||||
bridge, Bearer-authenticated exactly like the pm2 controls. The script's exit
|
||||
code + stdout/stderr are relayed verbatim so the dashboard can show preflight
|
||||
warnings (a non-zero --check is a refusal to render, not a transport error)."""
|
||||
if req.action not in {"check", "apply", "revert", "verify"}:
|
||||
raise HTTPException(400, "action חייב להיות check/apply/revert/verify")
|
||||
secret = os.environ.get("COURT_FETCH_SHARED_SECRET", "").strip()
|
||||
headers = {"Authorization": f"Bearer {secret}"} if secret else {}
|
||||
body = {
|
||||
"action": req.action,
|
||||
"agent": req.agent or "",
|
||||
"to": req.to or "",
|
||||
"model": req.model or "",
|
||||
"relax_tools": req.relax_tools,
|
||||
}
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=190.0) as client:
|
||||
r = await client.post(
|
||||
f"{_COURT_FETCH_SERVICE_URL}/adapter-migration", json=body, headers=headers,
|
||||
)
|
||||
except Exception as e: # host bridge down / unreachable
|
||||
raise HTTPException(502, f"לא ניתן להגיע לשירות-המארח: {e}") from e
|
||||
try:
|
||||
payload = r.json()
|
||||
except Exception:
|
||||
payload = {"ok": False, "error": r.text[:300]}
|
||||
if r.status_code >= 400:
|
||||
raise HTTPException(r.status_code, payload.get("error", "migration bridge failed"))
|
||||
return payload
|
||||
|
||||
|
||||
@app.get("/api/digests/{digest_id}")
|
||||
async def digest_get(digest_id: str):
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user