diff --git a/backend/apps/agents/agents.py b/backend/apps/agents/agents.py index 04765a31..8dda1ff5 100644 --- a/backend/apps/agents/agents.py +++ b/backend/apps/agents/agents.py @@ -273,3 +273,25 @@ async def subscriptions_models(): models = await get_models() return {"models": models} + +@agents.router.post("/subscriptions/disconnect") +async def subscriptions_disconnect(body: dict): + """Disconnect a subscription provider via 9Router.""" + import httpx + provider = body.get("provider", "") + if not provider: + raise HTTPException(status_code=400, detail="provider required") + + try: + from backend.apps.nine_router import NINE_ROUTER_API, get_providers + providers_data = await get_providers() + connections = providers_data.get("connections", []) if isinstance(providers_data, dict) else [] + conn = next((c for c in connections if c.get("provider") == provider), None) + if conn and conn.get("id"): + async with httpx.AsyncClient(timeout=10.0) as client: + await client.delete(f"{NINE_ROUTER_API}/providers/{conn['id']}") + return {"ok": True} + return {"ok": False, "error": "Connection not found"} + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + diff --git a/backend/apps/nine_router.py b/backend/apps/nine_router.py index 584f1aae..65158703 100644 --- a/backend/apps/nine_router.py +++ b/backend/apps/nine_router.py @@ -85,9 +85,14 @@ async def ensure_running(): if _is_packaged and _9router_dir: # Production mode — use pre-built standalone server - standalone_server = os.path.join(_9router_dir, ".next", "standalone", "server.js") + # In packaged app, build-staging copies .next/standalone/ contents to 9router/ + # So server.js is at 9router/server.js (not 9router/.next/standalone/server.js) + standalone_server = os.path.join(_9router_dir, "server.js") if not os.path.exists(standalone_server): - logger.warning("9Router standalone build not found at %s", standalone_server) + # Fallback: check nested path in case build layout changes + standalone_server = os.path.join(_9router_dir, ".next", "standalone", "server.js") + if not os.path.exists(standalone_server): + logger.warning("9Router standalone build not found in %s", _9router_dir) return node = _find_node() @@ -97,7 +102,7 @@ async def ensure_running(): logger.info("Starting 9Router (production) on port %d...", NINE_ROUTER_PORT) cmd = [node, standalone_server] - cwd = os.path.join(_9router_dir, ".next", "standalone") + cwd = os.path.dirname(standalone_server) env = {**os.environ, "PORT": str(NINE_ROUTER_PORT), "NODE_ENV": "production"} # If using Electron binary as node, enable ELECTRON_RUN_AS_NODE if node == os.environ.get("OPENSWARM_ELECTRON_PATH"): diff --git a/electron/package-lock.json b/electron/package-lock.json index dfecee44..76236be9 100644 --- a/electron/package-lock.json +++ b/electron/package-lock.json @@ -1,12 +1,12 @@ { "name": "openswarm", - "version": "1.0.12", + "version": "1.0.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "openswarm", - "version": "1.0.12", + "version": "1.0.13", "hasInstallScript": true, "dependencies": { "electron-updater": "^6.3.0", diff --git a/electron/package.json b/electron/package.json index 96a006db..b1e80979 100644 --- a/electron/package.json +++ b/electron/package.json @@ -1,6 +1,6 @@ { "name": "openswarm", - "version": "1.0.13", + "version": "1.0.14", "description": "OpenSwarm — AI Agent Orchestrator", "main": "main.js", "scripts": { diff --git a/frontend/src/app/pages/Settings/Settings.tsx b/frontend/src/app/pages/Settings/Settings.tsx index 0e3b0436..06590832 100644 --- a/frontend/src/app/pages/Settings/Settings.tsx +++ b/frontend/src/app/pages/Settings/Settings.tsx @@ -306,8 +306,15 @@ const SubscriptionCards: React.FC = () => { }; const handleDisconnect = async (providerId: string) => { - // TODO: implement disconnect via 9Router API - fetchStatus(); + try { + await fetch(`${API_BASE}/agents/subscriptions/disconnect`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ provider: providerId }), + }); + } catch {} + // Wait briefly for 9Router to process, then refresh + setTimeout(fetchStatus, 500); }; if (!status?.running) {