* fix(gateway): pass actual version to Control UI client instead of "dev" The GatewayClient, CLI WS client, and browser Control UI all sent "dev" as their clientVersion during handshake, making it impossible to distinguish builds in gateway logs and health snapshots. - GatewayClient and CLI WS client now use the resolved VERSION constant - Control UI reads serverVersion from the bootstrap endpoint and forwards it when connecting - Bootstrap contract extended with serverVersion field Closes #35209 * Gateway: fix control-ui version version-reporting consistency * Control UI: guard deferred bootstrap connect after disconnect * fix(ui): accept same-origin http and relative gateway URLs for client version --------- Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
88 lines
2.5 KiB
TypeScript
88 lines
2.5 KiB
TypeScript
/* @vitest-environment jsdom */
|
|
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import { CONTROL_UI_BOOTSTRAP_CONFIG_PATH } from "../../../../src/gateway/control-ui-contract.js";
|
|
import { loadControlUiBootstrapConfig } from "./control-ui-bootstrap.ts";
|
|
|
|
describe("loadControlUiBootstrapConfig", () => {
|
|
it("loads assistant identity from the bootstrap endpoint", async () => {
|
|
const fetchMock = vi.fn().mockResolvedValue({
|
|
ok: true,
|
|
json: async () => ({
|
|
basePath: "/openclaw",
|
|
assistantName: "Ops",
|
|
assistantAvatar: "O",
|
|
assistantAgentId: "main",
|
|
serverVersion: "2026.3.2",
|
|
}),
|
|
});
|
|
vi.stubGlobal("fetch", fetchMock as unknown as typeof fetch);
|
|
|
|
const state = {
|
|
basePath: "/openclaw",
|
|
assistantName: "Assistant",
|
|
assistantAvatar: null,
|
|
assistantAgentId: null,
|
|
serverVersion: null,
|
|
};
|
|
|
|
await loadControlUiBootstrapConfig(state);
|
|
|
|
expect(fetchMock).toHaveBeenCalledWith(
|
|
`/openclaw${CONTROL_UI_BOOTSTRAP_CONFIG_PATH}`,
|
|
expect.objectContaining({ method: "GET" }),
|
|
);
|
|
expect(state.assistantName).toBe("Ops");
|
|
expect(state.assistantAvatar).toBe("O");
|
|
expect(state.assistantAgentId).toBe("main");
|
|
expect(state.serverVersion).toBe("2026.3.2");
|
|
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
it("ignores failures", async () => {
|
|
const fetchMock = vi.fn().mockResolvedValue({ ok: false });
|
|
vi.stubGlobal("fetch", fetchMock as unknown as typeof fetch);
|
|
|
|
const state = {
|
|
basePath: "",
|
|
assistantName: "Assistant",
|
|
assistantAvatar: null,
|
|
assistantAgentId: null,
|
|
serverVersion: null,
|
|
};
|
|
|
|
await loadControlUiBootstrapConfig(state);
|
|
|
|
expect(fetchMock).toHaveBeenCalledWith(
|
|
CONTROL_UI_BOOTSTRAP_CONFIG_PATH,
|
|
expect.objectContaining({ method: "GET" }),
|
|
);
|
|
expect(state.assistantName).toBe("Assistant");
|
|
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
it("normalizes trailing slash basePath for bootstrap fetch path", async () => {
|
|
const fetchMock = vi.fn().mockResolvedValue({ ok: false });
|
|
vi.stubGlobal("fetch", fetchMock as unknown as typeof fetch);
|
|
|
|
const state = {
|
|
basePath: "/openclaw/",
|
|
assistantName: "Assistant",
|
|
assistantAvatar: null,
|
|
assistantAgentId: null,
|
|
serverVersion: null,
|
|
};
|
|
|
|
await loadControlUiBootstrapConfig(state);
|
|
|
|
expect(fetchMock).toHaveBeenCalledWith(
|
|
`/openclaw${CONTROL_UI_BOOTSTRAP_CONFIG_PATH}`,
|
|
expect.objectContaining({ method: "GET" }),
|
|
);
|
|
|
|
vi.unstubAllGlobals();
|
|
});
|
|
});
|