54 lines
2.7 KiB
TypeScript
54 lines
2.7 KiB
TypeScript
import type { FacadeResponse } from "./facade-envelope.js";
|
|
import { WEB_MODULE_REGISTRY, getWebModuleWorkerForCommand } from "./module-registry.generated.js";
|
|
import { WebModuleRouter } from "./module-router.js";
|
|
import { FEM_NATIVE_BOUNDARY } from "./fem-boundary.js";
|
|
import { nativeSourceDecision } from "./native-source-policy.js";
|
|
|
|
export interface WebCommandDescriptor {
|
|
id: string;
|
|
module: string;
|
|
command: string;
|
|
worker: string | null;
|
|
category: "create" | "inspect" | "python" | "persistence" | "format" | "boundary";
|
|
runnable: boolean;
|
|
reason: string;
|
|
}
|
|
|
|
function category(command: string): WebCommandDescriptor["category"] {
|
|
if (command.includes("save") || command.includes("reopen")) return "persistence";
|
|
if (command.includes("inspect")) return "inspect";
|
|
if (command.includes("python")) return "python";
|
|
if (command.includes("import") || command.includes("export")) return "format";
|
|
if (command.includes("boundary") || command.includes("server")) return "boundary";
|
|
return "create";
|
|
}
|
|
|
|
export function listWebCommands(query = ""): WebCommandDescriptor[] {
|
|
const normalized = query.trim().toLowerCase();
|
|
return WEB_MODULE_REGISTRY.flatMap((route) => route.commands.map((command) => {
|
|
const worker = getWebModuleWorkerForCommand(route.module, command) ?? route.primaryWorker;
|
|
const boundary = route.guiDependency && category(command) === "boundary";
|
|
const sourceDecision = nativeSourceDecision(route.module, command);
|
|
const nativeBlocked = route.module === "Fem";
|
|
return {
|
|
id: `${route.module}:${command}`,
|
|
module: route.module,
|
|
command,
|
|
worker: worker ?? null,
|
|
category: category(command),
|
|
runnable: !boundary && !sourceDecision,
|
|
reason: sourceDecision?.message ?? (nativeBlocked ? FEM_NATIVE_BOUNDARY.message : boundary ? "GUI/external capability boundary" : "registered Worker route"),
|
|
};
|
|
})).filter((descriptor) => !normalized || `${descriptor.module} ${descriptor.command} ${descriptor.category}`.toLowerCase().includes(normalized));
|
|
}
|
|
|
|
export class WebCommandRuntime {
|
|
readonly commands = listWebCommands();
|
|
constructor(private readonly router: WebModuleRouter) {}
|
|
|
|
async execute(descriptor: WebCommandDescriptor, payload: unknown, documentId = "workbench-document", signal?: AbortSignal): Promise<FacadeResponse> {
|
|
if (!descriptor.runnable) return { schema: "freecad-web-facade/v1", version: 1, kind: "error", requestId: `command-${descriptor.id}`, error: { code: "restricted", message: descriptor.reason, diagnostic: { module: descriptor.module, command: descriptor.command } } };
|
|
return this.router.request(descriptor.module, descriptor.command, payload, documentId, signal);
|
|
}
|
|
}
|