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
59 lines
1.8 KiB
TypeScript
59 lines
1.8 KiB
TypeScript
/**
|
|
* @file Tests for newBuildReload: after a reconnect the tab reloads once when
|
|
* the server's version is not this bundle's, never on the first connect,
|
|
* never for the same version, and not twice for one version.
|
|
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
|
*/
|
|
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { eventBus } from "../eventBus";
|
|
import { reloadOnNewBuild } from "../newBuildReload";
|
|
|
|
const flush = () => new Promise((r) => setTimeout(r, 0));
|
|
|
|
function setup(serverVersion: string) {
|
|
const reload = vi.fn();
|
|
const store = new Map<string, string>();
|
|
const storage = {
|
|
getItem: (k: string) => store.get(k) ?? null,
|
|
setItem: (k: string, v: string) => void store.set(k, v),
|
|
};
|
|
const off = reloadOnNewBuild({
|
|
bundle: "2.5.4",
|
|
health: () => Promise.resolve({ version: serverVersion }),
|
|
reload,
|
|
storage,
|
|
});
|
|
return { reload, off };
|
|
}
|
|
|
|
let off: (() => void) | undefined;
|
|
afterEach(() => off?.());
|
|
|
|
describe("reloadOnNewBuild", () => {
|
|
it("reloads once after a reconnect onto a new version", async () => {
|
|
const s = setup("2.5.5");
|
|
off = s.off;
|
|
eventBus.setConnected(true); // first connect: nothing to compare against yet
|
|
await flush();
|
|
expect(s.reload).not.toHaveBeenCalled();
|
|
eventBus.setConnected(false);
|
|
eventBus.setConnected(true);
|
|
await flush();
|
|
expect(s.reload).toHaveBeenCalledTimes(1);
|
|
eventBus.setConnected(false); // same version again: a failed UI build must not loop
|
|
eventBus.setConnected(true);
|
|
await flush();
|
|
expect(s.reload).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("stays put when the server is this bundle's version", async () => {
|
|
const s = setup("2.5.4");
|
|
off = s.off;
|
|
eventBus.setConnected(false);
|
|
eventBus.setConnected(true);
|
|
await flush();
|
|
expect(s.reload).not.toHaveBeenCalled();
|
|
});
|
|
});
|