899028e20f
- RomConfig/RomCache on the kit's ConfigService/CacheStore; RomSync wires the pure domain into the kit SyncEngine (poll/watch/coalesce/fingerprint) and ProviderClient; EngineStatus assembly shared by REST + SSE - makeApi: HttpApiBuilder groups over live service values (handler runtime shares the plugin runtime's singletons) + sseRoute status feed - index.ts: definePluginKit entry (async-main boundary); headless-first (UI serve failure logged, engine continues) - cli.ts on the kit dispatcher: scan/detect/preview offline, sync/uninstall online; set-password is gone with the standalone server - dev.ts: auth-free loopback API server (:5885) — the dev:live proxy target; never bundled - verified live host-less: raw config round-trip on disk, status/platforms/ preview endpoints, soft-fail reconcile without a host - CI: workspace install, per-package typecheck, publish from plugin/ Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
72 lines
2.4 KiB
TypeScript
72 lines
2.4 KiB
TypeScript
#!/usr/bin/env bun
|
|
// Dev-only API server: the plugin's HttpApi on a plain loopback port (:5885), no auth,
|
|
// no static files — the `bun run dev:live` proxy target for the Vite UI. Connects to a
|
|
// running host when one is up (full sync path); otherwise runs host-less (scan, preview,
|
|
// detect, and config all work; reconcile fails softly in the engine's safeSync).
|
|
// This is NOT part of the shipped plugin (never bundled into dist/index.js or cli.js).
|
|
import { connect, type Punktfunk } from "@punktfunk/host";
|
|
import {
|
|
hostClientFromFacade,
|
|
loggingLayer,
|
|
pluginInfoLayer,
|
|
} from "@punktfunk/plugin-kit";
|
|
import { Effect, Layer, ManagedRuntime } from "effect";
|
|
import { HttpRouter } from "effect/unstable/http";
|
|
import pkg from "../package.json" with { type: "json" };
|
|
import { PLUGIN_NAME } from "./index.js";
|
|
import { romLayer } from "./layer.js";
|
|
import { makeApi } from "./services/api.js";
|
|
import { RomCache } from "./services/cache.js";
|
|
import { RomConfig } from "./services/config.js";
|
|
import { RomSync } from "./services/engine.js";
|
|
|
|
const PORT = Number(process.env.ROM_MANAGER_DEV_PORT ?? 5885);
|
|
|
|
const offlineFacade = (): Punktfunk =>
|
|
({
|
|
request: async (method: string, path: string) => {
|
|
throw new Error(`dev: no host running (tried ${method} ${path})`);
|
|
},
|
|
close: () => {},
|
|
}) as unknown as Punktfunk;
|
|
|
|
const pf = await connect().catch(() => {
|
|
console.log("dev: no punktfunk host reachable — running host-less");
|
|
return offlineFacade();
|
|
});
|
|
|
|
const base = Layer.mergeAll(
|
|
hostClientFromFacade(pf),
|
|
pluginInfoLayer({ name: PLUGIN_NAME, version: pkg.version }),
|
|
loggingLayer(PLUGIN_NAME),
|
|
);
|
|
const rt = ManagedRuntime.make(Layer.provideMerge(romLayer, base));
|
|
|
|
// No Effect.scoped here: the engine's loops belong to the runtime's layer scope and
|
|
// must outlive this acquisition effect (they close on rt.dispose()).
|
|
const { handler, dispose } = await rt.runPromise(
|
|
Effect.gen(function* () {
|
|
const sync = yield* RomSync;
|
|
const config = yield* RomConfig;
|
|
const cache = yield* RomCache;
|
|
yield* sync.engine.start;
|
|
return HttpRouter.toWebHandler(makeApi({ sync, config, cache }));
|
|
}),
|
|
);
|
|
|
|
const server = Bun.serve({
|
|
hostname: "127.0.0.1",
|
|
port: PORT,
|
|
idleTimeout: 0, // SSE
|
|
fetch: (req) => handler(req),
|
|
});
|
|
console.log(
|
|
`rom-manager dev API on http://127.0.0.1:${server.port}/api/status`,
|
|
);
|
|
|
|
process.once("SIGINT", () => {
|
|
void dispose()
|
|
.then(() => rt.dispose())
|
|
.finally(() => process.exit(0));
|
|
});
|