@phuetz/code-buddy 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/dist/agent/codebuddy-agent.js +1 -0
  2. package/dist/agent/codebuddy-agent.js.map +1 -1
  3. package/dist/agent/execution/agent-executor.js +5 -7
  4. package/dist/agent/execution/agent-executor.js.map +1 -1
  5. package/dist/agent/index.d.ts +3 -3
  6. package/dist/agent/index.js +3 -3
  7. package/dist/agent/index.js.map +1 -1
  8. package/dist/agent/isolation/agent-workspace.d.ts +1 -0
  9. package/dist/agent/isolation/agent-workspace.js +10 -0
  10. package/dist/agent/isolation/agent-workspace.js.map +1 -1
  11. package/dist/agent/specialized/index.d.ts +9 -8
  12. package/dist/agent/specialized/index.js +16 -8
  13. package/dist/agent/specialized/index.js.map +1 -1
  14. package/dist/browser/controller.js +8 -4
  15. package/dist/browser/controller.js.map +1 -1
  16. package/dist/browser-automation/browser-manager.js +8 -1
  17. package/dist/browser-automation/browser-manager.js.map +1 -1
  18. package/dist/codebuddy/client.js +66 -11
  19. package/dist/codebuddy/client.js.map +1 -1
  20. package/dist/codebuddy/tools.d.ts +1 -7
  21. package/dist/codebuddy/tools.js +2 -30
  22. package/dist/codebuddy/tools.js.map +1 -1
  23. package/dist/commands/cli/daemon-commands.d.ts +14 -0
  24. package/dist/commands/cli/daemon-commands.js +166 -0
  25. package/dist/commands/cli/daemon-commands.js.map +1 -0
  26. package/dist/commands/cli/speak-command.d.ts +10 -0
  27. package/dist/commands/cli/speak-command.js +97 -0
  28. package/dist/commands/cli/speak-command.js.map +1 -0
  29. package/dist/commands/cli/utility-commands.d.ts +10 -0
  30. package/dist/commands/cli/utility-commands.js +88 -0
  31. package/dist/commands/cli/utility-commands.js.map +1 -0
  32. package/dist/commands/handlers/vibe-handlers.js +0 -1
  33. package/dist/commands/handlers/vibe-handlers.js.map +1 -1
  34. package/dist/commands/index.d.ts +8 -7
  35. package/dist/commands/index.js +10 -8
  36. package/dist/commands/index.js.map +1 -1
  37. package/dist/config/hot-reload/watcher.js +4 -4
  38. package/dist/config/hot-reload/watcher.js.map +1 -1
  39. package/dist/context/index.d.ts +12 -12
  40. package/dist/context/index.js +25 -12
  41. package/dist/context/index.js.map +1 -1
  42. package/dist/errors/index.d.ts +4 -4
  43. package/dist/errors/index.js +8 -4
  44. package/dist/errors/index.js.map +1 -1
  45. package/dist/hooks/index.d.ts +4 -4
  46. package/dist/hooks/index.js +4 -4
  47. package/dist/hooks/index.js.map +1 -1
  48. package/dist/index.js +20 -333
  49. package/dist/index.js.map +1 -1
  50. package/dist/integrations/json-rpc/server.d.ts +9 -0
  51. package/dist/integrations/json-rpc/server.js +38 -8
  52. package/dist/integrations/json-rpc/server.js.map +1 -1
  53. package/dist/integrations/notification-integrations.d.ts +1 -0
  54. package/dist/integrations/notification-integrations.js +6 -1
  55. package/dist/integrations/notification-integrations.js.map +1 -1
  56. package/dist/memory/index.d.ts +2 -2
  57. package/dist/memory/index.js +2 -2
  58. package/dist/memory/index.js.map +1 -1
  59. package/dist/plugins/conflict-detection.js +2 -1
  60. package/dist/plugins/conflict-detection.js.map +1 -1
  61. package/dist/plugins/index.d.ts +3 -3
  62. package/dist/plugins/index.js +3 -3
  63. package/dist/plugins/index.js.map +1 -1
  64. package/dist/providers/local-llm-provider.js +7 -4
  65. package/dist/providers/local-llm-provider.js.map +1 -1
  66. package/dist/scripting/codebuddy-bindings.js +4 -3
  67. package/dist/scripting/codebuddy-bindings.js.map +1 -1
  68. package/dist/security/index.d.ts +7 -5
  69. package/dist/security/index.js +8 -7
  70. package/dist/security/index.js.map +1 -1
  71. package/dist/server/auth/index.d.ts +2 -2
  72. package/dist/server/auth/index.js +2 -2
  73. package/dist/server/auth/index.js.map +1 -1
  74. package/dist/server/middleware/index.d.ts +5 -5
  75. package/dist/server/middleware/index.js +5 -5
  76. package/dist/server/middleware/index.js.map +1 -1
  77. package/dist/server/middleware/rate-limit.js +15 -3
  78. package/dist/server/middleware/rate-limit.js.map +1 -1
  79. package/dist/server/websocket/handler.js +39 -1
  80. package/dist/server/websocket/handler.js.map +1 -1
  81. package/dist/tools/hooks/default-hooks.d.ts +1 -1
  82. package/dist/tools/hooks/default-hooks.js +2 -1
  83. package/dist/tools/hooks/default-hooks.js.map +1 -1
  84. package/dist/tools/index.d.ts +10 -10
  85. package/dist/tools/index.js +11 -11
  86. package/dist/tools/index.js.map +1 -1
  87. package/dist/types/errors.d.ts +1 -1
  88. package/dist/types/errors.js +2 -8
  89. package/dist/types/errors.js.map +1 -1
  90. package/dist/types/index.d.ts +2 -1
  91. package/dist/types/index.js +1 -2
  92. package/dist/types/index.js.map +1 -1
  93. package/dist/ui/index.d.ts +17 -21
  94. package/dist/ui/index.js +25 -22
  95. package/dist/ui/index.js.map +1 -1
  96. package/dist/utils/config-validation/schema.d.ts +15 -15
  97. package/dist/utils/logger.js +3 -9
  98. package/dist/utils/logger.js.map +1 -1
  99. package/dist/workflows/index.d.ts +4 -279
  100. package/dist/workflows/index.js +8 -822
  101. package/dist/workflows/index.js.map +1 -1
  102. package/dist/workflows/state-manager.d.ts +77 -0
  103. package/dist/workflows/state-manager.js +198 -0
  104. package/dist/workflows/state-manager.js.map +1 -0
  105. package/dist/workflows/step-manager.d.ts +39 -0
  106. package/dist/workflows/step-manager.js +196 -0
  107. package/dist/workflows/step-manager.js.map +1 -0
  108. package/dist/workflows/types.d.ts +87 -0
  109. package/dist/workflows/types.js +5 -0
  110. package/dist/workflows/types.js.map +1 -0
  111. package/dist/workflows/workflow-engine.d.ts +34 -0
  112. package/dist/workflows/workflow-engine.js +354 -0
  113. package/dist/workflows/workflow-engine.js.map +1 -0
  114. package/package.json +3 -1
@@ -8,827 +8,13 @@
8
8
  * - Conditional branching and error handling
9
9
  * - Workflow persistence and resume capability
10
10
  */
11
- import { EventEmitter } from 'events';
12
- import fs from 'fs';
13
- import path from 'path';
14
- import os from 'os';
15
- // ============================================================================
16
- // Step Manager - Manages individual step execution
17
- // ============================================================================
18
- export class StepManager extends EventEmitter {
19
- actionHandlers = new Map();
20
- constructor() {
21
- super();
22
- this.registerBuiltInActions();
23
- }
24
- /**
25
- * Register built-in action handlers
26
- */
27
- registerBuiltInActions() {
28
- // Log action
29
- this.registerAction('log', async (context) => {
30
- const message = context.variables.message || 'No message';
31
- console.log(`[Workflow ${context.instanceId}] ${message}`);
32
- return { success: true, output: message };
33
- });
34
- // Delay action
35
- this.registerAction('delay', async (context) => {
36
- const ms = context.variables.delay || 1000;
37
- await new Promise(resolve => setTimeout(resolve, ms));
38
- return { success: true, output: `Delayed ${ms}ms` };
39
- });
40
- // Set variable action
41
- this.registerAction('setVariable', async (context) => {
42
- const name = context.variables.varName;
43
- const value = context.variables.varValue;
44
- if (name) {
45
- context.variables[name] = value;
46
- }
47
- return { success: true, output: { [name]: value } };
48
- });
49
- // Conditional action (always succeeds, used for branching)
50
- this.registerAction('conditional', async (context) => {
51
- return { success: true, output: context.variables };
52
- });
53
- // Noop action
54
- this.registerAction('noop', async () => {
55
- return { success: true, output: 'No operation performed' };
56
- });
57
- }
58
- /**
59
- * Register a custom action handler
60
- */
61
- registerAction(name, handler) {
62
- this.actionHandlers.set(name, handler);
63
- }
64
- /**
65
- * Check if an action is registered
66
- */
67
- hasAction(name) {
68
- return this.actionHandlers.has(name);
69
- }
70
- /**
71
- * Get all registered action names
72
- */
73
- getRegisteredActions() {
74
- return Array.from(this.actionHandlers.keys());
75
- }
76
- /**
77
- * Execute a step
78
- */
79
- async executeStep(step, context, options = {}) {
80
- const startTime = Date.now();
81
- this.emit('step:start', { stepId: step.id, stepName: step.name });
82
- try {
83
- // Check condition
84
- if (step.condition) {
85
- const shouldRun = this.evaluateCondition(step.condition, context);
86
- if (!shouldRun) {
87
- this.emit('step:skipped', { stepId: step.id, reason: 'Condition not met' });
88
- return { success: true, output: 'Step skipped', metadata: { skipped: true } };
89
- }
90
- }
91
- // Execute action
92
- let result;
93
- if (typeof step.action === 'function') {
94
- result = await this.executeWithTimeout(step.action(context), options.timeout || step.timeout || 30000);
95
- }
96
- else if (typeof step.action === 'string') {
97
- const handler = this.actionHandlers.get(step.action);
98
- if (!handler) {
99
- throw new Error(`Unknown action: ${step.action}`);
100
- }
101
- result = await this.executeWithTimeout(handler(context), options.timeout || step.timeout || 30000);
102
- }
103
- else {
104
- throw new Error('Invalid action type');
105
- }
106
- result.duration = Date.now() - startTime;
107
- this.emit('step:complete', {
108
- stepId: step.id,
109
- stepName: step.name,
110
- success: result.success,
111
- duration: result.duration,
112
- });
113
- return result;
114
- }
115
- catch (error) {
116
- const duration = Date.now() - startTime;
117
- const errorMessage = error instanceof Error ? error.message : String(error);
118
- this.emit('step:error', {
119
- stepId: step.id,
120
- stepName: step.name,
121
- error: errorMessage,
122
- duration,
123
- });
124
- return {
125
- success: false,
126
- error: errorMessage,
127
- duration,
128
- };
129
- }
130
- }
131
- /**
132
- * Execute with timeout
133
- */
134
- async executeWithTimeout(promise, timeout) {
135
- return new Promise((resolve, reject) => {
136
- const timer = setTimeout(() => {
137
- reject(new Error(`Step execution timed out after ${timeout}ms`));
138
- }, timeout);
139
- promise
140
- .then((result) => {
141
- clearTimeout(timer);
142
- resolve(result);
143
- })
144
- .catch((error) => {
145
- clearTimeout(timer);
146
- reject(error);
147
- });
148
- });
149
- }
150
- /**
151
- * Evaluate a condition
152
- */
153
- evaluateCondition(condition, context) {
154
- if (typeof condition === 'function') {
155
- return condition(context);
156
- }
157
- // Simple condition evaluation for string conditions
158
- // Supports: "variable === value", "variable > value", etc.
159
- try {
160
- // Check for variable existence conditions
161
- if (condition.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/)) {
162
- return !!context.variables[condition];
163
- }
164
- // Check for comparison conditions
165
- const comparisonMatch = condition.match(/^(\w+)\s*(===|!==|==|!=|>=|<=|>|<)\s*(.+)$/);
166
- if (comparisonMatch) {
167
- const [, variable, operator, valueStr] = comparisonMatch;
168
- const actualValue = context.variables[variable];
169
- let expectedValue = valueStr;
170
- // Parse value
171
- if (valueStr === 'true')
172
- expectedValue = true;
173
- else if (valueStr === 'false')
174
- expectedValue = false;
175
- else if (valueStr === 'null')
176
- expectedValue = null;
177
- else if (valueStr === 'undefined')
178
- expectedValue = undefined;
179
- else if (!isNaN(Number(valueStr)))
180
- expectedValue = Number(valueStr);
181
- else if (valueStr.startsWith('"') && valueStr.endsWith('"')) {
182
- expectedValue = valueStr.slice(1, -1);
183
- }
184
- switch (operator) {
185
- case '===':
186
- case '==':
187
- return actualValue === expectedValue;
188
- case '!==':
189
- case '!=':
190
- return actualValue !== expectedValue;
191
- case '>':
192
- return actualValue > expectedValue;
193
- case '<':
194
- return actualValue < expectedValue;
195
- case '>=':
196
- return actualValue >= expectedValue;
197
- case '<=':
198
- return actualValue <= expectedValue;
199
- }
200
- }
201
- // Default to true for unrecognized conditions
202
- return true;
203
- }
204
- catch {
205
- return true;
206
- }
207
- }
208
- }
209
- // ============================================================================
210
- // Workflow State Manager - Persists and manages workflow state
211
- // ============================================================================
212
- export class WorkflowStateManager {
213
- statesDir;
214
- states = new Map();
215
- constructor(statesDir) {
216
- this.statesDir = statesDir || path.join(os.homedir(), '.codebuddy', 'workflows');
217
- this.ensureDir();
218
- this.loadStates();
219
- }
220
- /**
221
- * Ensure the states directory exists
222
- */
223
- ensureDir() {
224
- if (!fs.existsSync(this.statesDir)) {
225
- fs.mkdirSync(this.statesDir, { recursive: true });
226
- }
227
- }
228
- /**
229
- * Load existing states from disk
230
- */
231
- loadStates() {
232
- try {
233
- const files = fs.readdirSync(this.statesDir);
234
- for (const file of files) {
235
- if (!file.endsWith('.json'))
236
- continue;
237
- try {
238
- const statePath = path.join(this.statesDir, file);
239
- const data = JSON.parse(fs.readFileSync(statePath, 'utf-8'));
240
- const state = this.deserializeState(data);
241
- this.states.set(state.instanceId, state);
242
- }
243
- catch {
244
- // Skip invalid state files
245
- }
246
- }
247
- }
248
- catch {
249
- // Directory doesn't exist or can't be read
250
- }
251
- }
252
- /**
253
- * Serialize state for storage
254
- */
255
- serializeState(state) {
256
- return {
257
- ...state,
258
- context: {
259
- ...state.context,
260
- stepResults: Array.from(state.context.stepResults.entries()),
261
- },
262
- stepExecutions: Array.from(state.stepExecutions.entries()),
263
- createdAt: state.createdAt.toISOString(),
264
- startedAt: state.startedAt?.toISOString(),
265
- completedAt: state.completedAt?.toISOString(),
266
- pausedAt: state.pausedAt?.toISOString(),
267
- };
268
- }
269
- /**
270
- * Deserialize state from storage
271
- */
272
- deserializeState(data) {
273
- const contextData = data.context;
274
- return {
275
- ...data,
276
- context: {
277
- ...contextData,
278
- stepResults: new Map(contextData.stepResults),
279
- },
280
- stepExecutions: new Map(data.stepExecutions),
281
- createdAt: new Date(data.createdAt),
282
- startedAt: data.startedAt ? new Date(data.startedAt) : undefined,
283
- completedAt: data.completedAt ? new Date(data.completedAt) : undefined,
284
- pausedAt: data.pausedAt ? new Date(data.pausedAt) : undefined,
285
- };
286
- }
287
- /**
288
- * Generate unique instance ID
289
- */
290
- generateInstanceId() {
291
- const timestamp = Date.now().toString(36);
292
- const random = Math.random().toString(36).substring(2, 8);
293
- return `wf_${timestamp}_${random}`;
294
- }
295
- /**
296
- * Create a new workflow state
297
- */
298
- createState(workflowId, initialContext = {}) {
299
- const instanceId = this.generateInstanceId();
300
- const state = {
301
- instanceId,
302
- workflowId,
303
- status: 'pending',
304
- context: {
305
- workflowId,
306
- instanceId,
307
- variables: { ...initialContext },
308
- stepResults: new Map(),
309
- metadata: {},
310
- },
311
- stepExecutions: new Map(),
312
- currentStepIndex: 0,
313
- createdAt: new Date(),
314
- };
315
- this.states.set(instanceId, state);
316
- this.saveState(state);
317
- return state;
318
- }
319
- /**
320
- * Get a workflow state by instance ID
321
- */
322
- getState(instanceId) {
323
- return this.states.get(instanceId);
324
- }
325
- /**
326
- * Update a workflow state
327
- */
328
- updateState(instanceId, updates) {
329
- const state = this.states.get(instanceId);
330
- if (!state)
331
- return undefined;
332
- Object.assign(state, updates);
333
- this.saveState(state);
334
- return state;
335
- }
336
- /**
337
- * Save state to disk
338
- */
339
- saveState(state) {
340
- const statePath = path.join(this.statesDir, `${state.instanceId}.json`);
341
- fs.writeFileSync(statePath, JSON.stringify(this.serializeState(state), null, 2));
342
- }
343
- /**
344
- * Delete a workflow state
345
- */
346
- deleteState(instanceId) {
347
- const existed = this.states.delete(instanceId);
348
- if (existed) {
349
- const statePath = path.join(this.statesDir, `${instanceId}.json`);
350
- if (fs.existsSync(statePath)) {
351
- fs.unlinkSync(statePath);
352
- }
353
- }
354
- return existed;
355
- }
356
- /**
357
- * Get all states
358
- */
359
- getAllStates() {
360
- return Array.from(this.states.values());
361
- }
362
- /**
363
- * Get states by workflow ID
364
- */
365
- getStatesByWorkflow(workflowId) {
366
- return Array.from(this.states.values()).filter(s => s.workflowId === workflowId);
367
- }
368
- /**
369
- * Get states by status
370
- */
371
- getStatesByStatus(status) {
372
- return Array.from(this.states.values()).filter(s => s.status === status);
373
- }
374
- /**
375
- * Clear completed states
376
- */
377
- clearCompleted() {
378
- const completed = this.getStatesByStatus('completed');
379
- const failed = this.getStatesByStatus('failed');
380
- const cancelled = this.getStatesByStatus('cancelled');
381
- const toDelete = [...completed, ...failed, ...cancelled];
382
- for (const state of toDelete) {
383
- this.deleteState(state.instanceId);
384
- }
385
- return toDelete.length;
386
- }
387
- /**
388
- * Get statistics
389
- */
390
- getStats() {
391
- const states = this.getAllStates();
392
- return {
393
- total: states.length,
394
- pending: states.filter(s => s.status === 'pending').length,
395
- running: states.filter(s => s.status === 'running').length,
396
- paused: states.filter(s => s.status === 'paused').length,
397
- completed: states.filter(s => s.status === 'completed').length,
398
- failed: states.filter(s => s.status === 'failed').length,
399
- cancelled: states.filter(s => s.status === 'cancelled').length,
400
- };
401
- }
402
- }
403
- // ============================================================================
404
- // Workflow Engine - Main orchestrator for workflow execution
405
- // ============================================================================
406
- export class WorkflowEngine extends EventEmitter {
407
- workflows = new Map();
408
- stateManager;
409
- stepManager;
410
- runningWorkflows = new Set();
411
- constructor(statesDir) {
412
- super();
413
- this.stateManager = new WorkflowStateManager(statesDir);
414
- this.stepManager = new StepManager();
415
- this.registerBuiltInWorkflows();
416
- }
417
- /**
418
- * Register built-in workflows
419
- */
420
- registerBuiltInWorkflows() {
421
- // Sample validation workflow
422
- this.registerWorkflow({
423
- id: 'validation',
424
- name: 'Validation Workflow',
425
- description: 'Basic validation workflow template',
426
- version: '1.0.0',
427
- steps: [
428
- { id: 'validate-input', name: 'Validate Input', action: 'noop' },
429
- { id: 'process', name: 'Process', action: 'noop' },
430
- { id: 'complete', name: 'Complete', action: 'log' },
431
- ],
432
- });
433
- // Sample data pipeline workflow
434
- this.registerWorkflow({
435
- id: 'data-pipeline',
436
- name: 'Data Pipeline',
437
- description: 'Sample data processing pipeline',
438
- version: '1.0.0',
439
- steps: [
440
- { id: 'extract', name: 'Extract Data', action: 'noop' },
441
- { id: 'transform', name: 'Transform Data', action: 'noop' },
442
- { id: 'load', name: 'Load Data', action: 'noop' },
443
- ],
444
- });
445
- }
446
- /**
447
- * Register a workflow definition
448
- */
449
- registerWorkflow(workflow) {
450
- this.workflows.set(workflow.id, workflow);
451
- this.emit('workflow:registered', { workflowId: workflow.id, name: workflow.name });
452
- }
453
- /**
454
- * Unregister a workflow
455
- */
456
- unregisterWorkflow(workflowId) {
457
- const existed = this.workflows.delete(workflowId);
458
- if (existed) {
459
- this.emit('workflow:unregistered', { workflowId });
460
- }
461
- return existed;
462
- }
463
- /**
464
- * Get a workflow definition
465
- */
466
- getWorkflow(workflowId) {
467
- return this.workflows.get(workflowId);
468
- }
469
- /**
470
- * Get all registered workflows
471
- */
472
- getWorkflows() {
473
- return Array.from(this.workflows.values());
474
- }
475
- /**
476
- * Register a custom action
477
- */
478
- registerAction(name, handler) {
479
- this.stepManager.registerAction(name, handler);
480
- }
481
- /**
482
- * Start a new workflow instance
483
- */
484
- async startWorkflow(workflowId, options = {}) {
485
- const workflow = this.workflows.get(workflowId);
486
- if (!workflow) {
487
- return {
488
- success: false,
489
- instanceId: '',
490
- workflowId,
491
- status: 'failed',
492
- stepResults: new Map(),
493
- finalContext: {},
494
- duration: 0,
495
- error: `Workflow not found: ${workflowId}`,
496
- completedSteps: 0,
497
- totalSteps: 0,
498
- };
499
- }
500
- const state = this.stateManager.createState(workflowId, {
501
- ...workflow.initialContext,
502
- ...options.initialContext,
503
- });
504
- return this.executeWorkflow(workflow, state, options);
505
- }
506
- /**
507
- * Resume a paused workflow
508
- */
509
- async resumeWorkflow(instanceId) {
510
- const state = this.stateManager.getState(instanceId);
511
- if (!state) {
512
- return {
513
- success: false,
514
- instanceId,
515
- workflowId: '',
516
- status: 'failed',
517
- stepResults: new Map(),
518
- finalContext: {},
519
- duration: 0,
520
- error: `Workflow instance not found: ${instanceId}`,
521
- completedSteps: 0,
522
- totalSteps: 0,
523
- };
524
- }
525
- if (state.status !== 'paused') {
526
- return {
527
- success: false,
528
- instanceId,
529
- workflowId: state.workflowId,
530
- status: state.status,
531
- stepResults: state.context.stepResults,
532
- finalContext: state.context.variables,
533
- duration: state.totalDuration || 0,
534
- error: `Workflow is not paused (status: ${state.status})`,
535
- completedSteps: state.currentStepIndex,
536
- totalSteps: this.workflows.get(state.workflowId)?.steps.length || 0,
537
- };
538
- }
539
- const workflow = this.workflows.get(state.workflowId);
540
- if (!workflow) {
541
- return {
542
- success: false,
543
- instanceId,
544
- workflowId: state.workflowId,
545
- status: 'failed',
546
- stepResults: state.context.stepResults,
547
- finalContext: state.context.variables,
548
- duration: 0,
549
- error: `Workflow definition not found: ${state.workflowId}`,
550
- completedSteps: state.currentStepIndex,
551
- totalSteps: 0,
552
- };
553
- }
554
- return this.executeWorkflow(workflow, state, {});
555
- }
556
- /**
557
- * Pause a running workflow
558
- */
559
- pauseWorkflow(instanceId) {
560
- const state = this.stateManager.getState(instanceId);
561
- if (!state || state.status !== 'running') {
562
- return false;
563
- }
564
- this.stateManager.updateState(instanceId, {
565
- status: 'paused',
566
- pausedAt: new Date(),
567
- });
568
- this.emit('workflow:paused', { instanceId });
569
- return true;
570
- }
571
- /**
572
- * Cancel a workflow
573
- */
574
- cancelWorkflow(instanceId) {
575
- const state = this.stateManager.getState(instanceId);
576
- if (!state) {
577
- return false;
578
- }
579
- if (state.status === 'completed' || state.status === 'cancelled') {
580
- return false;
581
- }
582
- this.runningWorkflows.delete(instanceId);
583
- this.stateManager.updateState(instanceId, {
584
- status: 'cancelled',
585
- completedAt: new Date(),
586
- });
587
- this.emit('workflow:cancelled', { instanceId });
588
- return true;
589
- }
590
- /**
591
- * Execute a workflow
592
- */
593
- async executeWorkflow(workflow, state, options) {
594
- const startTime = Date.now();
595
- const instanceId = state.instanceId;
596
- // Mark as running
597
- this.runningWorkflows.add(instanceId);
598
- this.stateManager.updateState(instanceId, {
599
- status: 'running',
600
- startedAt: state.startedAt || new Date(),
601
- });
602
- this.emit('workflow:start', { instanceId, workflowId: workflow.id });
603
- // Find starting step
604
- let stepIndex = state.currentStepIndex;
605
- if (options.startFromStep) {
606
- const idx = workflow.steps.findIndex(s => s.id === options.startFromStep);
607
- if (idx !== -1) {
608
- stepIndex = idx;
609
- }
610
- }
611
- try {
612
- for (; stepIndex < workflow.steps.length; stepIndex++) {
613
- // Check if paused or cancelled
614
- const currentState = this.stateManager.getState(instanceId);
615
- if (!currentState || currentState.status === 'paused' || currentState.status === 'cancelled') {
616
- break;
617
- }
618
- const step = workflow.steps[stepIndex];
619
- state.context.currentStep = step.id;
620
- // Emit step start
621
- if (options.onStepStart) {
622
- options.onStepStart(step.id);
623
- }
624
- // Create step execution record
625
- const execution = {
626
- stepId: step.id,
627
- stepName: step.name,
628
- status: 'running',
629
- startedAt: new Date(),
630
- retries: 0,
631
- };
632
- state.stepExecutions.set(step.id, execution);
633
- // Execute step with retries
634
- let result = null;
635
- let retries = 0;
636
- const maxRetries = step.maxRetries || 0;
637
- while (retries <= maxRetries) {
638
- result = await this.stepManager.executeStep(step, state.context, {
639
- timeout: options.timeout || step.timeout,
640
- });
641
- if (result.success || !step.retryOnFailure) {
642
- break;
643
- }
644
- retries++;
645
- execution.retries = retries;
646
- }
647
- if (!result) {
648
- result = { success: false, error: 'No result from step execution' };
649
- }
650
- // Update execution record
651
- execution.status = result.success ? 'completed' : 'failed';
652
- execution.completedAt = new Date();
653
- execution.result = result;
654
- // Store result in context
655
- state.context.stepResults.set(step.id, result);
656
- // Update state
657
- this.stateManager.updateState(instanceId, {
658
- currentStepIndex: stepIndex + 1,
659
- context: state.context,
660
- stepExecutions: state.stepExecutions,
661
- });
662
- // Emit step complete
663
- if (options.onStepComplete) {
664
- options.onStepComplete(execution);
665
- }
666
- // Handle failure
667
- if (!result.success) {
668
- if (step.onFailure) {
669
- // Jump to failure step
670
- const failureIdx = workflow.steps.findIndex(s => s.id === step.onFailure);
671
- if (failureIdx !== -1) {
672
- stepIndex = failureIdx - 1; // -1 because loop will increment
673
- continue;
674
- }
675
- }
676
- // Default: fail the workflow
677
- throw new Error(result.error || `Step ${step.id} failed`);
678
- }
679
- // Handle success branching
680
- if (step.onSuccess && result.success) {
681
- const successIdx = workflow.steps.findIndex(s => s.id === step.onSuccess);
682
- if (successIdx !== -1) {
683
- stepIndex = successIdx - 1; // -1 because loop will increment
684
- }
685
- }
686
- }
687
- // Check final state
688
- const finalState = this.stateManager.getState(instanceId);
689
- if (finalState?.status === 'paused') {
690
- return this.buildResult(finalState, workflow, startTime);
691
- }
692
- if (finalState?.status === 'cancelled') {
693
- return this.buildResult(finalState, workflow, startTime);
694
- }
695
- // Mark as completed
696
- this.runningWorkflows.delete(instanceId);
697
- this.stateManager.updateState(instanceId, {
698
- status: 'completed',
699
- completedAt: new Date(),
700
- totalDuration: Date.now() - startTime,
701
- });
702
- this.emit('workflow:complete', { instanceId, success: true });
703
- return this.buildResult(this.stateManager.getState(instanceId), workflow, startTime);
704
- }
705
- catch (error) {
706
- const errorMessage = error instanceof Error ? error.message : String(error);
707
- this.runningWorkflows.delete(instanceId);
708
- this.stateManager.updateState(instanceId, {
709
- status: 'failed',
710
- completedAt: new Date(),
711
- error: errorMessage,
712
- totalDuration: Date.now() - startTime,
713
- });
714
- this.emit('workflow:error', { instanceId, error: errorMessage });
715
- return this.buildResult(this.stateManager.getState(instanceId), workflow, startTime, errorMessage);
716
- }
717
- }
718
- /**
719
- * Build workflow result
720
- */
721
- buildResult(state, workflow, startTime, error) {
722
- return {
723
- success: state.status === 'completed',
724
- instanceId: state.instanceId,
725
- workflowId: state.workflowId,
726
- status: state.status,
727
- stepResults: state.context.stepResults,
728
- finalContext: state.context.variables,
729
- duration: Date.now() - startTime,
730
- error: error || state.error,
731
- completedSteps: state.currentStepIndex,
732
- totalSteps: workflow.steps.length,
733
- };
734
- }
735
- /**
736
- * Get workflow state
737
- */
738
- getWorkflowState(instanceId) {
739
- return this.stateManager.getState(instanceId);
740
- }
741
- /**
742
- * Get all workflow instances
743
- */
744
- getWorkflowInstances() {
745
- return this.stateManager.getAllStates();
746
- }
747
- /**
748
- * Get running workflows
749
- */
750
- getRunningWorkflows() {
751
- return Array.from(this.runningWorkflows);
752
- }
753
- /**
754
- * Get statistics
755
- */
756
- getStats() {
757
- return this.stateManager.getStats();
758
- }
759
- /**
760
- * Format workflow result for display
761
- */
762
- formatResult(result) {
763
- const statusEmoji = result.success ? '✅' : '❌';
764
- let output = `\n${statusEmoji} Workflow Result: ${result.workflowId}\n`;
765
- output += '═'.repeat(50) + '\n\n';
766
- output += `Instance: ${result.instanceId}\n`;
767
- output += `Status: ${result.status.toUpperCase()}\n`;
768
- output += `Duration: ${(result.duration / 1000).toFixed(2)}s\n`;
769
- output += `Steps: ${result.completedSteps}/${result.totalSteps}\n`;
770
- if (result.error) {
771
- output += `Error: ${result.error}\n`;
772
- }
773
- if (result.stepResults.size > 0) {
774
- output += '\nStep Results:\n';
775
- for (const [stepId, stepResult] of result.stepResults) {
776
- const stepEmoji = stepResult.success ? ' ✓' : ' ✗';
777
- output += `${stepEmoji} ${stepId}: ${stepResult.success ? 'completed' : 'failed'}`;
778
- if (stepResult.duration) {
779
- output += ` (${stepResult.duration}ms)`;
780
- }
781
- output += '\n';
782
- }
783
- }
784
- output += '\n' + '═'.repeat(50) + '\n';
785
- return output;
786
- }
787
- /**
788
- * Format available workflows for display
789
- */
790
- formatWorkflows() {
791
- const workflows = this.getWorkflows();
792
- if (workflows.length === 0) {
793
- return 'No workflows registered.\n';
794
- }
795
- let output = 'Available Workflows:\n\n';
796
- for (const workflow of workflows) {
797
- output += ` 📋 ${workflow.name} (${workflow.id})\n`;
798
- output += ` ${workflow.description}\n`;
799
- output += ` Version: ${workflow.version} | Steps: ${workflow.steps.length}\n\n`;
800
- }
801
- return output;
802
- }
803
- /**
804
- * Clean up resources
805
- */
806
- dispose() {
807
- // Cancel all running workflows
808
- for (const instanceId of this.runningWorkflows) {
809
- this.cancelWorkflow(instanceId);
810
- }
811
- this.runningWorkflows.clear();
812
- this.workflows.clear();
813
- this.removeAllListeners();
814
- }
815
- }
816
- // ============================================================================
817
- // Singleton and Factory Functions
818
- // ============================================================================
819
- let workflowEngineInstance = null;
820
- export function getWorkflowEngine(statesDir) {
821
- if (!workflowEngineInstance) {
822
- workflowEngineInstance = new WorkflowEngine(statesDir);
823
- }
824
- return workflowEngineInstance;
825
- }
826
- export function resetWorkflowEngine() {
827
- if (workflowEngineInstance) {
828
- workflowEngineInstance.dispose();
829
- }
830
- workflowEngineInstance = null;
831
- }
11
+ // Step Manager
12
+ export { StepManager } from './step-manager.js';
13
+ // State Manager
14
+ export { WorkflowStateManager } from './state-manager.js';
15
+ // Workflow Engine
16
+ export { WorkflowEngine, getWorkflowEngine, resetWorkflowEngine, } from './workflow-engine.js';
832
17
  export { PipelineCompositor, getPipelineCompositor, resetPipelineCompositor, } from './pipeline.js';
833
- export { loadPipelineFile, validatePipelineDefinition, createPipelineCommand, } from '../commands/pipeline.js';
18
+ // Pipeline CLI Utilities import directly from 'src/commands/pipeline.js'
19
+ // (Re-export removed to break circular dependency: workflows/index → commands/pipeline → workflows/index)
834
20
  //# sourceMappingURL=index.js.map