import { spawn } from "node:child_process"; import { mkdtemp, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import type { JsonValue } from "./types.js"; export interface WorkflowArtifact { extension: ".js" | ".json" | ".md"; content: string } export type WorkflowTui = { stop(): void; start(): void; requestRender(force?: boolean): void }; export function workflowScriptArtifact(script: string): WorkflowArtifact { return { extension: ".js", content: script }; } export function workflowPromptArtifact(prompt: string): WorkflowArtifact { return { extension: ".md", content: prompt }; } export function workflowResultArtifact(value: JsonValue): WorkflowArtifact { return typeof value === "string" ? { extension: ".md", content: value } : { extension: ".json", content: `${JSON.stringify(value, null, 2)}\n` }; } async function spawnWorkflowEditor(command: string, path: string): Promise { const [editor, ...editorArgs] = command.split(" "); if (!editor) return null; return new Promise((resolve) => { try { const child = spawn(editor, [...editorArgs, path], { stdio: "inherit", shell: process.platform === "win32" }); child.once("error", () => { resolve(null); }); child.once("close", (code) => { resolve(code); }); } catch { resolve(null); } }); } export async function openWorkflowArtifact(tui: WorkflowTui, command: string, artifact: WorkflowArtifact): Promise { const directory = await mkdtemp(join(tmpdir(), "pi-workflow-editor-")); const path = join(directory, `artifact${artifact.extension}`); try { await writeFile(path, artifact.content, { encoding: "utf8", mode: 0o600 }); tui.stop(); try { return await spawnWorkflowEditor(command, path); } finally { tui.start(); tui.requestRender(true); } } finally { await rm(directory, { recursive: true, force: true }); } }