test-workflow-run-tool-step.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. #!/usr/bin/env node
  2. import assert from 'assert';
  3. import fs from 'fs';
  4. import os from 'os';
  5. import path from 'path';
  6. import { ToolRegistry } from './src/core/tool-registry.js';
  7. import { createReadFileTool } from './src/tools/read-file.js';
  8. import { createWorkflowRunTool } from './src/tools/workflow-run.js';
  9. function createTempProject() {
  10. const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'vlcode-workflow-run-tool-'));
  11. fs.mkdirSync(path.join(dir, '.vl-code'), { recursive: true });
  12. fs.writeFileSync(path.join(dir, '.vl-code', 'workspace.json'), '{}');
  13. return dir;
  14. }
  15. async function main() {
  16. const workDir = createTempProject();
  17. const workflowPath = path.join(workDir, 'tool-workflow.json');
  18. const inputPath = path.join(workDir, 'tool-input.txt');
  19. fs.writeFileSync(inputPath, 'workflow-run-tool-ok', 'utf8');
  20. fs.writeFileSync(workflowPath, JSON.stringify({
  21. version: '3.16',
  22. name: 'WorkflowRunToolStep',
  23. steps: [
  24. {
  25. id: 'Tool_010_ReadFile',
  26. input: { file_path: '="tool-input.txt"' },
  27. out: { '$content': '=_result' },
  28. next: 'Stop_End',
  29. },
  30. { id: 'Stop_End' },
  31. ],
  32. }, null, 2), 'utf8');
  33. const events = [];
  34. let doneResolve;
  35. let doneReject;
  36. const donePromise = new Promise((resolve, reject) => {
  37. doneResolve = resolve;
  38. doneReject = reject;
  39. });
  40. const toolRegistry = new ToolRegistry();
  41. toolRegistry.register('ReadFile', createReadFileTool({ workDir }));
  42. const config = {
  43. workDir,
  44. model: 'claude-opus-4-6',
  45. llmProvider: 'cli',
  46. toolRegistry,
  47. _broadcast: (event) => {
  48. events.push(event);
  49. if (event.type === 'wf_done') doneResolve(event);
  50. if (event.type === 'wf_error') doneReject(new Error(event.error || 'workflow error'));
  51. },
  52. _recordWorkflowEvent: (event) => events.push({ ...event, recorded: true }),
  53. _onWorkflowStart: () => {},
  54. _onWorkflowDone: () => {},
  55. _onWorkflowError: (_, msg) => doneReject(new Error(msg)),
  56. _onWorkflowNodeStatus: () => {},
  57. _setActiveExecutor: () => {},
  58. };
  59. const workflowTool = createWorkflowRunTool(config);
  60. const response = await workflowTool.execute({ mode: 'local', workflow_path: 'tool-workflow.json' });
  61. const parsed = JSON.parse(response);
  62. assert.equal(parsed.status, 'started');
  63. await Promise.race([
  64. donePromise,
  65. new Promise((_, reject) => setTimeout(() => reject(new Error('workflow tool timed out')), 5000)),
  66. ]);
  67. assert(events.some((event) => event.type === 'wf_tool_start' && event.name === 'ReadFile'), 'missing wf_tool_start');
  68. assert(events.some((event) => event.type === 'wf_tool_message' && event.name === 'ReadFile'), 'missing wf_tool_message');
  69. assert(events.some((event) => event.type === 'wf_tool_done' && event.name === 'ReadFile'), 'missing wf_tool_done');
  70. assert(events.some((event) => event.type === 'wf_done'), 'missing wf_done');
  71. fs.rmSync(workDir, { recursive: true, force: true });
  72. console.log('\n── WorkflowRun Tool Step ──');
  73. console.log('PASS test-workflow-run-tool-step.js');
  74. process.exit(0);
  75. }
  76. main().catch((err) => {
  77. console.error(err);
  78. process.exit(1);
  79. });