feat: v2.5.5 — AI Router renamed AI Proxy, reload onto new builds, update no longer spins

AI Proxy (was AI Router):
- server/lib/ai-proxies.js, proxy-apply.js, routes/ai-proxies.js mounted at
  /api/ai-proxies and the old /api/ai-routers; `router` still accepted on the
  start routes; profiles move to <CLAUDE_HOME>/agenthub-proxies/
- dashboard_runs.router renamed in place to proxy: an older build on the same
  DB fails on that column, so dev and prod sharing one DB update together
- a proxy's baseUrl stored as its root (a typed /v1 made /v1/v1/messages);
  Test probes Claude /v1/messages and Codex /v1/responses
- Settings → AI Proxy (/settings/ai-router redirects), ProxyPicker

Updates:
- every tab reloads onto the new UI build when its WebSocket reconnects to a
  server of another version (lib/newBuildReload.ts, once per version)
- Update now stops waiting on a plugin chain that failed or pulled nothing
  (the old server never goes down), and reloads as soon as a new version answers

Also:
- a pane whose command exits at once says so (EPANEEXITED) instead of
  "Command failed: tmux set-option … mouse on"
- the pane drag preview draws where the pane actually lands
This commit is contained in:
2026-10-06 22:48:09 +07:00
parent dd9b23106c
commit 5fe5d51b1b
82 changed files with 1792 additions and 1412 deletions
+13 -13
View File
@@ -577,13 +577,13 @@
"fieldLabelPlaceholder": "Dev box"
},
"cursorPathsNote": "Informational: Cursor sessions count here too — Cursor happens to use the same ~/.claude paths as Claude Code (locally and on synced remotes).",
"aiRouter": {
"title": "AI Router",
"aiProxy": {
"title": "AI Proxy",
"description": "For example 9Router, OpenRouter or a self-hosted gateway. Save one here, then pick it for a session in the workspace prompt box.",
"add": "Add profile",
"edit": "Edit",
"remove": "Remove",
"empty": "No router profile saved yet. Native runs against the vendor's own API.",
"empty": "No proxy profile saved yet. Native runs against the vendor's own API.",
"builtinHint": "Built in — this is the vendor's own API, and it cannot be edited or removed.",
"colLabel": "Profile",
"colBaseUrl": "Endpoint",
@@ -592,16 +592,16 @@
"keySet": "Saved",
"keyMissing": "None",
"fieldLabel": "Name",
"fieldLabelHint": "What you will see in the workspace form. Free text.",
"fieldLabelHint": "What you will see in the session's AI Proxy picker. Free text.",
"fieldBaseUrl": "Base URL",
"fieldBaseUrlHint": "The router's OpenAI/Anthropic-compatible endpoint, e.g. http://localhost:20128/v1",
"fieldBaseUrlHint": "The proxy's root URL, without /v1 — e.g. http://localhost:20128. Claude Code adds /v1/messages and Codex /v1/responses; a /v1 you type is dropped.",
"fieldApiKey": "API key",
"fieldApiKeyHint": "Stored on this machine at 0600 and never sent back to the browser.",
"fieldApiKeyKeep": "Leave blank to keep the saved key.",
"fieldModel": "Model",
"fieldModelHint": "Used for every tier unless you override one below. Many routers accept one symbolic name.",
"fieldModelHint": "Used for every tier unless you override one below. Many proxies accept one symbolic name.",
"fieldBehavesAs": "Behaves as (Claude Code)",
"fieldBehavesAsHint": "Optional. The Claude model id Claude Code should treat this router's model names as (effort defaults, context window), e.g. claude-opus-5-5. Fill it in when Claude Code says a model isn't described by its model catalog, and name the model the router really sends to. While set, it replaces your own modelPicker setting in these sessions.",
"fieldBehavesAsHint": "Optional. The Claude model id Claude Code should treat this proxy's model names as (effort defaults, context window), e.g. claude-opus-5-5. Fill it in when Claude Code says a model isn't described by its model catalog, and name the model the proxy really sends to. While set, it replaces your own modelPicker setting in these sessions.",
"perTier": "Per-tier overrides",
"perTierHint": "Leave a field blank to use the model above.",
"tier": {
@@ -613,12 +613,12 @@
},
"save": "Save",
"cancel": "Cancel",
"removeConfirm": "Remove the profile \"{{label}}\"? Every workspace using it is reset to the vendor's own API right away — on disk and in the dashboard.",
"removeConfirm": "Remove the profile \"{{label}}\"? Its session files go with it, and a session that ran on it resumes on the vendor's own API.",
"loading": "Loading profiles…",
"test": "Test",
"testing": "Testing…",
"testOk": "The endpoint answered — {{model}} is reachable with this key.",
"testFailed": "No answer ({{status}}): {{message}}"
"testOk": "answered — {{model}} is reachable with this key.",
"testFailed": "no answer ({{status}}): {{message}}"
},
"aiTools": {
"title": "AI Tools",
@@ -700,7 +700,7 @@
},
"codexHome": {
"title": "CODEX_HOME",
"description": "Root directory for Codex config and session journals. The dashboard reads it, writes AI Router layers into it, and starts every codex workspace with it.",
"description": "Root directory for Codex config and session journals. The dashboard reads it, writes AI Proxy layers into it, and starts every codex workspace with it.",
"restartNote": "A codex workspace already running keeps the home it started with, and shows as idle, until it is restarted.",
"placeholder": "/path/to/codex-home"
},
@@ -792,8 +792,8 @@
"tool": {
"description": "{{tool}}: path, usage, default model and config files."
},
"aiRouter": {
"title": "AI Router",
"aiProxy": {
"title": "AI Proxy",
"description": "Endpoints that stand in front of Claude Code and Codex."
},
"pricing": {