#!/usr/bin/env node /** * One-time bootstrap: import frontend builtin registry into backend canonical modules. * Not the workflow export script — see export-builtin-workflows.mjs. */ import { mkdirSync, writeFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; import { parseFrontendInventory, stableStringify, } from "./lib/parseFrontendBuiltinWorkflows.mjs"; const __dirname = dirname(fileURLToPath(import.meta.url)); const BACKEND = join(__dirname, ".."); const COLUMNS_DIR = join(BACKEND, "src/prompts/tabular/builtin-columns"); const GOLDEN_PATH = join( BACKEND, "tests/golden/builtin-workflows-frontend-bootstrap.json", ); const TABULAR_WORKFLOW_IDS = [ "builtin-coc-dd", "builtin-commercial-agreement", "builtin-credit-agreement", "builtin-ediscovery", "builtin-supply-agreement", "builtin-spa", "builtin-nda", "builtin-commercial-lease", "builtin-lpa", "builtin-shareholder-agreement", "builtin-employment-agreement", "builtin-zamil-proposal-radar", "builtin-zamil-subcontractor-radar", "builtin-zamil-peb-commercial-terms-radar", ]; function toExportName(workflowId) { return workflowId .replace(/^builtin-/, "") .replace(/-/g, "_") .toUpperCase(); } function escapeTemplateLiteral(value) { return value .replace(/\\/g, "\\\\") .replace(/`/g, "\\`") .replace(/\$\{/g, "\\${"); } function serializeColumn(col) { const lines = [ " {", ` index: ${col.index},`, ` name: ${JSON.stringify(col.name)},`, ]; if (col.format !== undefined) { lines.push(` format: ${JSON.stringify(col.format)},`); } if (col.tags !== undefined) { lines.push( ` tags: ${JSON.stringify(col.tags)},`, ); } lines.push( ` prompt: ${JSON.stringify(col.prompt)},`, " },", ); return lines.join("\n"); } function writeColumnModule(workflowId, columns) { const exportName = `${toExportName(workflowId)}_COLUMNS`; const fileName = `${workflowId}.ts`; const body = `/** Tabular built-in columns for ${workflowId} — canonical backend source. */\nimport type { BuiltinColumnConfig } from "./types";\n\nexport const ${exportName}: BuiltinColumnConfig[] = [\n${columns.map(serializeColumn).join("\n")}\n];\n`; const fullPath = join(COLUMNS_DIR, fileName); writeFileSync(fullPath, body, "utf8"); return { fileName, exportName }; } function writeMetadataModule(workflows) { const entries = workflows.map((wf) => { const fields = [ ` id: ${JSON.stringify(wf.id)},`, ` title: ${JSON.stringify(wf.title)},`, ` type: ${JSON.stringify(wf.type)},`, ` practice: ${JSON.stringify(wf.practice ?? null)},`, ]; if ("playbook_id" in wf) { fields.push( ` playbook_id: ${JSON.stringify(wf.playbook_id ?? null)},`, ); } if (wf.prompt_md) { fields.push( ` prompt_md: \`${escapeTemplateLiteral(wf.prompt_md)}\`,`, ); } else { fields.push(" prompt_md: null,"); } return ` {\n${fields.join("\n")}\n }`; }); const body = `/** Canonical built-in workflow metadata imported from frontend bootstrap inventory. */\nimport type { BuiltinWorkflowMetadata } from "./types";\n\nexport const BUILTIN_WORKFLOW_METADATA: BuiltinWorkflowMetadata[] = [\n${entries.join(",\n")}\n];\n`; writeFileSync(join(COLUMNS_DIR, "metadata.ts"), body, "utf8"); } function writeIndex(exportsList) { const imports = exportsList .map(({ fileName, exportName }) => { const base = fileName.replace(/\.ts$/, ""); return `import { ${exportName} } from "./${base}";`; }) .join("\n"); const mapEntries = exportsList .map(({ fileName, exportName }) => { const id = fileName.replace(/\.ts$/, ""); return ` "${id}": ${exportName},`; }) .join("\n"); const body = `${imports} import { BUILTIN_WORKFLOW_METADATA } from "./metadata"; export { BUILTIN_WORKFLOW_METADATA } from "./metadata"; export * from "./types"; export const BUILTIN_TABULAR_COLUMNS = { ${mapEntries} } as const; `; writeFileSync(join(COLUMNS_DIR, "index.ts"), body, "utf8"); } function writeTypes() { const body = `export type BuiltinColumnFormat = | "text" | "bulleted_list" | "number" | "currency" | "yes_no" | "date" | "tag" | "percentage" | "monetary_amount"; export interface BuiltinColumnConfig { index: number; name: string; prompt: string; format?: BuiltinColumnFormat; tags?: string[]; } export interface BuiltinWorkflowMetadata { id: string; title: string; type: "assistant" | "tabular" | "playbook"; practice: string | null; playbook_id?: string | null; prompt_md: string | null; } export interface BuiltinWorkflowExport { id: string; user_id: null; is_system: true; created_at: ""; title: string; type: "assistant" | "tabular" | "playbook"; practice: string | null; playbook_id?: string | null; prompt_md: string | null; columns_config: BuiltinColumnConfig[] | null; } `; writeFileSync(join(COLUMNS_DIR, "types.ts"), body, "utf8"); } const inventory = parseFrontendInventory(); mkdirSync(COLUMNS_DIR, { recursive: true }); writeTypes(); const columnExports = []; for (const workflowId of TABULAR_WORKFLOW_IDS) { const wf = inventory.workflows.find((w) => w.id === workflowId); if (!wf?.columns_config) { throw new Error(`Missing tabular columns for ${workflowId}`); } columnExports.push(writeColumnModule(workflowId, wf.columns_config)); } writeMetadataModule(inventory.workflows); writeIndex(columnExports); const golden = { orderedIds: inventory.orderedIds, totalWorkflowCount: inventory.totalWorkflowCount, perWorkflowColumnCount: inventory.perWorkflowColumnCount, totalColumnPromptCount: inventory.totalColumnPromptCount, sha256: inventory.sha256, workflows: inventory.workflows, }; writeFileSync(GOLDEN_PATH, stableStringify(golden), "utf8"); console.log( `bootstrap: ${inventory.totalWorkflowCount} workflows, ${inventory.totalColumnPromptCount} column prompts`, ); console.log(`bootstrap sha256: ${inventory.sha256}`); console.log(`wrote golden: ${GOLDEN_PATH}`);