diff --git a/apps/loopover-miner-ui/src/vite-chat-action-plugins.test.ts b/apps/loopover-miner-ui/src/vite-chat-action-plugins.test.ts new file mode 100644 index 0000000000..8571eb2b7f --- /dev/null +++ b/apps/loopover-miner-ui/src/vite-chat-action-plugins.test.ts @@ -0,0 +1,24 @@ +import { describe, expect, it } from "vitest"; + +import { chatDiscoverAttemptActionsPlugin } from "../vite-chat-discover-attempt-actions"; +import { chatGovernorActionsPlugin } from "../vite-chat-governor-actions"; + +// #7228: the app's documented persistent-service path (README + systemd/loopover-miner-ui.service.example) runs +// `npm run build && npm run preview`, and `vite preview` fires ONLY `configurePreviewServer`, never +// `configureServer`. These two chat-action plugins used to implement `configureServer` alone, so their governor +// and discover/attempt actions were silently unregistered in production — unlike every sibling vite-*-api.ts +// plugin, which registers for both hooks. Lock in that both plugins now wire both hooks. +describe("chat-action vite plugins register for dev AND preview servers (#7228)", () => { + const cases = [ + ["chatGovernorActionsPlugin", chatGovernorActionsPlugin], + ["chatDiscoverAttemptActionsPlugin", chatDiscoverAttemptActionsPlugin], + ] as const; + + for (const [label, factory] of cases) { + it(`${label} implements both configureServer and configurePreviewServer`, () => { + const plugin = factory(); + expect(plugin.configureServer).toBeTypeOf("function"); + expect(plugin.configurePreviewServer).toBeTypeOf("function"); + }); + } +}); diff --git a/apps/loopover-miner-ui/vite-chat-discover-attempt-actions.ts b/apps/loopover-miner-ui/vite-chat-discover-attempt-actions.ts index a9f3a08f7e..aba38e3b53 100644 --- a/apps/loopover-miner-ui/vite-chat-discover-attempt-actions.ts +++ b/apps/loopover-miner-ui/vite-chat-discover-attempt-actions.ts @@ -6,12 +6,21 @@ import type { Plugin } from "vite"; // vite-chat-governor-actions.ts). export function chatDiscoverAttemptActionsPlugin(): Plugin { + // Register on BOTH dev (`configureServer`) and preview (`configurePreviewServer`) start — `vite preview` (the + // persistent-service path in this app's README and systemd unit) only runs the preview hook, so without it + // these actions were silently missing in production, unlike every sibling vite-*-api.ts plugin (#7228). + const register = () => { + void import("./src/lib/chat-discover-attempt-actions").then((mod) => { + mod.registerDiscoverAttemptChatActions(); + }); + }; return { name: "loopover-miner-chat-discover-attempt-actions", configureServer() { - void import("./src/lib/chat-discover-attempt-actions").then((mod) => { - mod.registerDiscoverAttemptChatActions(); - }); + register(); + }, + configurePreviewServer() { + register(); }, }; } diff --git a/apps/loopover-miner-ui/vite-chat-governor-actions.ts b/apps/loopover-miner-ui/vite-chat-governor-actions.ts index 6b2e3cf8ab..90a18802c8 100644 --- a/apps/loopover-miner-ui/vite-chat-governor-actions.ts +++ b/apps/loopover-miner-ui/vite-chat-governor-actions.ts @@ -5,12 +5,22 @@ import type { Plugin } from "vite"; // buttons. No new /api/governor/* route is added here. export function chatGovernorActionsPlugin(): Plugin { + // Register into the shared registry on BOTH dev (`configureServer`) and preview (`configurePreviewServer`) + // start. `vite preview` — the persistent-service path in this app's README and systemd unit — only runs the + // preview hook, so without it these actions were silently missing in production, unlike every sibling + // vite-*-api.ts plugin (which registers for both) (#7228). + const register = () => { + void import("./src/lib/chat-governor-actions").then((mod) => { + mod.registerGovernorChatActions(); + }); + }; return { name: "loopover-miner-chat-governor-actions", configureServer() { - void import("./src/lib/chat-governor-actions").then((mod) => { - mod.registerGovernorChatActions(); - }); + register(); + }, + configurePreviewServer() { + register(); }, }; }