import { writeFileSync } from "node:fs"; import { join } from "node:path"; import { setKeybindings, type TUI } from "@earendil-works/pi-tui"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { KeybindingsManager } from "../../../src/core/keybindings.ts"; import { ModelSelectorComponent } from "../../../src/modes/interactive/components/model-selector.ts"; import { initTheme } from "../../../src/modes/interactive/theme/theme.ts"; import { stripAnsi } from "../../../src/utils/ansi.ts"; import { createHarness, type Harness } from "../harness.ts"; function createFakeTui(): TUI { return { requestRender: () => {}, } as unknown as TUI; } function modelsJson(provider: string, model: string): Record { return { providers: { [provider]: { baseUrl: "https://example.test/v1", api: "openai-completions", apiKey: "test-key", models: [{ id: model }], }, }, }; } describe("issue #6999 models.json hot reload", () => { let harness: Harness | undefined; beforeAll(() => { initTheme("dark"); }); beforeEach(() => { setKeybindings(new KeybindingsManager()); }); afterEach(() => { harness?.cleanup(); harness = undefined; }); it("reloads models.json when opening /model", async () => { harness = await createHarness({ modelsJson: modelsJson("old-provider", "old-model") }); expect(harness.session.modelRuntime.getModel("old-provider", "old-model")).toBeDefined(); writeFileSync(join(harness.tempDir, "models.json"), JSON.stringify(modelsJson("new-provider", "new-model"))); const selector = new ModelSelectorComponent( createFakeTui(), harness.getModel(), harness.settingsManager, harness.session.modelRuntime, [], () => {}, () => {}, ); await vi.waitFor(() => { const rendered = stripAnsi(selector.render(120).join("\n")); expect(rendered).toContain("new-model [new-provider]"); expect(rendered).toContain("Model catalogs refreshed."); }); expect(harness.session.modelRuntime.getModel("old-provider", "old-model")).toBeUndefined(); }); });