feat(FN-3763): add cli printing press wizard plugin

Implements a new `fusion-plugin-cli-printing-press` plugin with a multi-step wizard UI for authoring CLI commands, including draft storage, route registration, and validation — plus integration wiring in the dashboard's plugin view registry. Includes TypeScript build and ESM import fixes for the plu

Fusion-Task-Id: FN-3763
This commit is contained in:
Fusion
2026-05-10 15:01:54 -07:00
committed by gsxdsm
parent 6871c510a4
commit aa031ab601
24 changed files with 627 additions and 23 deletions

View File

@@ -0,0 +1,59 @@
import type { PluginContext, PluginRouteDefinition, PluginRouteResult } from "@fusion/core";
import { createDraftStore } from "../storage/draft-store.js";
import type { ServiceDraft } from "../wizard/types.js";
import { validateDraft } from "../wizard/validation.js";
interface RouteRequest {
params: Record<string, string>;
body?: unknown;
}
function asRequest(req: unknown): RouteRequest { return req as RouteRequest; }
function ok(body: unknown, status = 200): PluginRouteResult { return { status, body }; }
export function createCliPrintingPressRoutes(): PluginRouteDefinition[] {
return [
{
method: "POST",
path: "/drafts",
handler: async (req, ctx: PluginContext) => {
const request = asRequest(req);
const draft = request.body as ServiceDraft;
const result = validateDraft(draft);
if (!result.ok) return { status: 400, body: { error: Object.values(result.errors)[0] ?? "Validation failed", errors: result.errors } };
const store = createDraftStore({ rootDir: ctx.taskStore.getRootDir() });
const created = await store.create(draft);
return ok(created, 201);
},
},
{
method: "GET",
path: "/drafts",
handler: async (_req, ctx: PluginContext) => {
const store = createDraftStore({ rootDir: ctx.taskStore.getRootDir() });
return ok(await store.list());
},
},
{
method: "GET",
path: "/drafts/:id",
handler: async (req, ctx: PluginContext) => {
const request = asRequest(req);
const store = createDraftStore({ rootDir: ctx.taskStore.getRootDir() });
const draft = await store.get(request.params.id);
return draft ? ok(draft) : ok({ error: "Draft not found" }, 404);
},
},
{
method: "DELETE",
path: "/drafts/:id",
handler: async (req, ctx: PluginContext) => {
const request = asRequest(req);
const store = createDraftStore({ rootDir: ctx.taskStore.getRootDir() });
await store.delete(request.params.id);
return { status: 204 };
},
},
];
}