// The plugin entry. `main` is the plain-async form (NOT the Effect form): the packaged runner bundles // its own effect/SDK copy and cross-instance Effect identity is unverified, so the async facade // sidesteps it entirely. The runner supervises this with restart-on-crash and a structured SIGTERM // shutdown; a direct `bun src/index.ts` run works too (bottom of file). // // Lifecycle: connect (done by the runner/facade) → start the sync engine → serve the UI (console-hosted // by default, standalone if configured) → run until SIGTERM → deregister the UI and stop the engine. // Provider entries are deliberately LEFT in the library on shutdown so the games survive plugin // restarts and host reboots; a clean uninstall is the explicit `uninstall` CLI command. import { connect, definePlugin, type Punktfunk } from "@punktfunk/host"; import { PLUGIN_NAME } from "./const.js"; import { Engine } from "./engine/index.js"; import { log } from "./log.js"; import { serveConsoleUi } from "./server/index.js"; import { serveStandaloneUi } from "./server/standalone.js"; import { loadConfig } from "./state.js"; import { readVersion } from "./version.js"; const plugin = definePlugin({ name: PLUGIN_NAME, main: async (pf: Punktfunk) => { const engine = new Engine({ pf }); const config = loadConfig(); // Headless engine first — the library syncs even if the UI can't start. await engine.start(); let closeUi: () => Promise = async () => {}; try { if (config.ui.standalone) { const handle = serveStandaloneUi(engine, config); closeUi = handle.close; } else { const handle = await serveConsoleUi(pf, engine, { version: readVersion(), }); closeUi = handle.close; log(`UI registered with the console (nav entry "${PLUGIN_NAME}")`); } } catch (e) { log(`UI server failed to start (engine keeps running headless): ${e}`); } await new Promise((resolve) => { process.once("SIGINT", resolve); process.once("SIGTERM", resolve); }); log("shutting down"); await closeUi(); engine.stop(); }, }); export default plugin; // Allow a direct `bun src/index.ts` run outside the managed runner (dev). if (import.meta.main) { const pf = await connect(); await (plugin.main as (pf: Punktfunk) => Promise)(pf); pf.close(); }