#!/usr/bin/env node import assert from 'assert'; import fs from 'fs'; import os from 'os'; import path from 'path'; import { ToolRegistry } from './src/core/tool-registry.js'; import { WorkflowExecutor } from './src/vl/workflow-executor.js'; import { createReadFileTool } from './src/tools/read-file.js'; import { createWorkflowRunTool } from './src/tools/workflow-run.js'; function createTempProject() { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'vlcode-workflow-run-sync-')); fs.mkdirSync(path.join(dir, '.vl-code'), { recursive: true }); fs.writeFileSync(path.join(dir, '.vl-code', 'workspace.json'), '{}'); return dir; } async function main() { const workDir = createTempProject(); const outputDirRel = 'Artifacts'; const outputDir = path.join(workDir, 'Artifacts'); fs.mkdirSync(outputDir, { recursive: true }); const workerSpecPath = path.join(workDir, 'worker-spec.json'); const workerReviewPath = path.join(workDir, 'worker-review.json'); const supervisorPath = path.join(workDir, 'vlclaw-supervisor.json'); fs.writeFileSync(workerSpecPath, JSON.stringify({ version: '3.16', name: 'SpecWorker', steps: [ { id: 'Set_010_Content', target: '$content', value: '="SPEC:" + goal', next: 'Write_020_File' }, { id: 'Write_020_File', target: '=artifactPath', value: '=$content', mode: 'overwrite', next: 'Stop_End' }, { id: 'Stop_End' }, ], }, null, 2), 'utf8'); fs.writeFileSync(workerReviewPath, JSON.stringify({ version: '3.16', name: 'ReviewWorker', steps: [ { id: 'Set_010_Content', target: '$content', value: '="REVIEW:" + goal', next: 'Write_020_File' }, { id: 'Write_020_File', target: '=artifactPath', value: '=$content', mode: 'overwrite', next: 'Stop_End' }, { id: 'Stop_End' }, ], }, null, 2), 'utf8'); fs.writeFileSync(supervisorPath, JSON.stringify({ version: '3.16', name: 'VLClawSupervisorPrototype', steps: [ { id: 'Set_010_SpecPath', target: '$specPath', value: '=outputDir + "/spec.txt"', next: 'Set_020_ReviewPath' }, { id: 'Set_020_ReviewPath', target: '$reviewPath', value: '=outputDir + "/review.txt"', next: 'Fork_030_Dispatch' }, { id: 'Fork_030_Dispatch', children: ['Tool_031_RunSpecWorker', 'Tool_032_RunReviewWorker'], next: 'Tool_040_ReadSpec' }, { id: 'Tool_031_RunSpecWorker', tool: 'WorkflowRun', input: { mode: 'sync', workflow_path: '="worker-spec.json"', params: { goal: '=goal', artifactPath: '=$specPath' }, }, out: { '$specRun': '=_result' }, next: 'RETURN', }, { id: 'Tool_032_RunReviewWorker', tool: 'WorkflowRun', input: { mode: 'sync', workflow_path: '="worker-review.json"', params: { goal: '=goal', artifactPath: '=$reviewPath' }, }, out: { '$reviewRun': '=_result' }, next: 'RETURN', }, { id: 'Tool_040_ReadSpec', tool: 'ReadFile', input: { file_path: '=$specPath' }, out: { '$specText': '=_result' }, next: 'Tool_050_ReadReview', }, { id: 'Tool_050_ReadReview', tool: 'ReadFile', input: { file_path: '=$reviewPath' }, out: { '$reviewText': '=_result' }, next: 'Set_060_Summary', }, { id: 'Set_060_Summary', target: '$summary', value: '="goal=" + goal + "; spec=" + $specRun.variables["$content"] + "; review=" + $reviewRun.variables["$content"]', next: 'Write_070_Report', }, { id: 'Write_070_Report', target: '=outputDir + "/supervisor.txt"', value: '=$summary', mode: 'overwrite', next: 'Stop_End', }, { id: 'Stop_End' }, ], }, null, 2), 'utf8'); const toolRegistry = new ToolRegistry(); const config = { workDir, model: 'claude-opus-4-6', llmProvider: 'cli', toolRegistry, }; toolRegistry.register('ReadFile', createReadFileTool(config)); toolRegistry.register('WorkflowRun', createWorkflowRunTool(config)); const executor = new WorkflowExecutor({ workDir, model: 'claude-opus-4-6', llmProvider: 'cli', toolRegistry, }); const toolMessages = []; await executor.execute(JSON.parse(fs.readFileSync(supervisorPath, 'utf8')), { goal: 'Land the VLClaw manager workflow', outputDir: outputDirRel, }, { onToolMessage: (info) => toolMessages.push(info), }); const spec = fs.readFileSync(path.join(outputDir, 'spec.txt'), 'utf8'); const review = fs.readFileSync(path.join(outputDir, 'review.txt'), 'utf8'); const supervisor = fs.readFileSync(path.join(outputDir, 'supervisor.txt'), 'utf8'); assert.equal(spec, 'SPEC:Land the VLClaw manager workflow'); assert.equal(review, 'REVIEW:Land the VLClaw manager workflow'); assert.equal( supervisor, 'goal=Land the VLClaw manager workflow; spec=SPEC:Land the VLClaw manager workflow; review=REVIEW:Land the VLClaw manager workflow' ); assert(toolMessages.some((info) => info.name === 'WorkflowRun' && info.data?.workflowName === 'SpecWorker' && info.data?.event === 'node_start' ), 'missing nested SpecWorker node_start message'); assert(toolMessages.some((info) => info.name === 'WorkflowRun' && info.data?.workflowName === 'ReviewWorker' && info.data?.event === 'done' ), 'missing nested ReviewWorker done message'); fs.rmSync(workDir, { recursive: true, force: true }); console.log('\n── WorkflowRun Sync Orchestration ──'); console.log('PASS test-workflow-run-sync.js'); process.exit(0); } main().catch((err) => { console.error(err); process.exit(1); });