5fe5d51b1b
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
47 lines
1.5 KiB
TypeScript
47 lines
1.5 KiB
TypeScript
/**
|
|
* @file newBuildReload.ts
|
|
* @description Reloads the tab onto the new UI build after the server was
|
|
* updated. Every update — the dashboard's own Update button in any tab,
|
|
* `/agenthub-update`, a restart onto a newer plugin — drops the WebSocket; when
|
|
* it comes back, `GET /api/health` names the server's version, and a version
|
|
* other than this bundle's `__APP_VERSION__` means the page is stale. At most
|
|
* one reload per version (sessionStorage), so a server whose UI build failed
|
|
* cannot make the tab reload on every reconnect.
|
|
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
|
*/
|
|
|
|
import { eventBus } from "./eventBus";
|
|
|
|
const KEY = "agenthub.reloadedFor";
|
|
|
|
export function reloadOnNewBuild({
|
|
bundle = __APP_VERSION__,
|
|
health = () => fetch("/api/health").then((r) => r.json() as Promise<{ version?: string }>),
|
|
reload = () => window.location.reload(),
|
|
storage = window.sessionStorage,
|
|
}: {
|
|
bundle?: string;
|
|
health?: () => Promise<{ version?: string }>;
|
|
reload?: () => void;
|
|
storage?: Pick<Storage, "getItem" | "setItem">;
|
|
} = {}): () => void {
|
|
let wasDown = false;
|
|
return eventBus.onConnection((up) => {
|
|
if (!up) {
|
|
wasDown = true;
|
|
return;
|
|
}
|
|
if (!wasDown) return;
|
|
wasDown = false;
|
|
health()
|
|
.then(({ version }) => {
|
|
if (!version || version === bundle || storage.getItem(KEY) === version) return;
|
|
storage.setItem(KEY, version);
|
|
reload();
|
|
})
|
|
.catch(() => {
|
|
/* the next reconnect asks again */
|
|
});
|
|
});
|
|
}
|