@crewx/workflow 0.3.22-rc.192 → 0.3.22-rc.194
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.
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +17 -1190
- package/dist/src/card-state.d.ts +1 -1
- package/dist/src/card-state.js +1 -121
- package/dist/src/engine.d.ts +1 -1
- package/dist/src/engine.js +1 -379
- package/dist/src/failure.d.ts +1 -1
- package/dist/src/failure.js +1 -122
- package/dist/src/file-lock.d.ts +1 -1
- package/dist/src/file-lock.js +1 -97
- package/dist/src/list-view.d.ts +1 -1
- package/dist/src/list-view.js +3 -188
- package/dist/src/mermaid.d.ts +1 -1
- package/dist/src/mermaid.js +2 -133
- package/dist/src/parent-task-sync.d.ts +1 -1
- package/dist/src/parent-task-sync.js +1 -445
- package/dist/src/retry-policy.d.ts +1 -1
- package/dist/src/retry-policy.js +1 -140
- package/dist/src/run-manager.d.ts +1 -1
- package/dist/src/run-manager.js +4 -2165
- package/dist/src/types.d.ts +1 -1
- package/dist/src/types.js +1 -5
- package/dist/src/utils/output-format.d.ts +1 -1
- package/dist/src/utils/output-format.js +12 -206
- package/dist/src/utils/shell-security.d.ts +1 -1
- package/dist/src/utils/shell-security.js +1 -138
- package/dist/src/workflow-persistence.d.ts +1 -1
- package/dist/src/workflow-persistence.js +1 -224
- package/package.json +4 -4
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/src/card-state.d.ts.map +0 -1
- package/dist/src/card-state.js.map +0 -1
- package/dist/src/engine.d.ts.map +0 -1
- package/dist/src/engine.js.map +0 -1
- package/dist/src/failure.d.ts.map +0 -1
- package/dist/src/failure.js.map +0 -1
- package/dist/src/file-lock.d.ts.map +0 -1
- package/dist/src/file-lock.js.map +0 -1
- package/dist/src/list-view.d.ts.map +0 -1
- package/dist/src/list-view.js.map +0 -1
- package/dist/src/mermaid.d.ts.map +0 -1
- package/dist/src/mermaid.js.map +0 -1
- package/dist/src/parent-task-sync.d.ts.map +0 -1
- package/dist/src/parent-task-sync.js.map +0 -1
- package/dist/src/retry-policy.d.ts.map +0 -1
- package/dist/src/retry-policy.js.map +0 -1
- package/dist/src/run-manager.d.ts.map +0 -1
- package/dist/src/run-manager.js.map +0 -1
- package/dist/src/types.d.ts.map +0 -1
- package/dist/src/types.js.map +0 -1
- package/dist/src/utils/output-format.d.ts.map +0 -1
- package/dist/src/utils/output-format.js.map +0 -1
- package/dist/src/utils/shell-security.d.ts.map +0 -1
- package/dist/src/utils/shell-security.js.map +0 -1
- package/dist/src/workflow-persistence.d.ts.map +0 -1
- package/dist/src/workflow-persistence.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,191 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}) : (function(o, m, k, k2) {
|
|
11
|
-
if (k2 === undefined) k2 = k;
|
|
12
|
-
o[k2] = m[k];
|
|
13
|
-
}));
|
|
14
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
-
}) : function(o, v) {
|
|
17
|
-
o["default"] = v;
|
|
18
|
-
});
|
|
19
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
-
var ownKeys = function(o) {
|
|
21
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
-
var ar = [];
|
|
23
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
-
return ar;
|
|
25
|
-
};
|
|
26
|
-
return ownKeys(o);
|
|
27
|
-
};
|
|
28
|
-
return function (mod) {
|
|
29
|
-
if (mod && mod.__esModule) return mod;
|
|
30
|
-
var result = {};
|
|
31
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
-
__setModuleDefault(result, mod);
|
|
33
|
-
return result;
|
|
34
|
-
};
|
|
35
|
-
})();
|
|
36
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
-
exports.resolveWorkflow = resolveWorkflow;
|
|
38
|
-
exports.findNextNodeForCAL = findNextNodeForCAL;
|
|
39
|
-
exports.getFlowChain = getFlowChain;
|
|
40
|
-
exports.main = main;
|
|
41
|
-
const fs = __importStar(require("fs"));
|
|
42
|
-
const list_view_1 = require("./src/list-view");
|
|
43
|
-
const path = __importStar(require("path"));
|
|
44
|
-
const child_process_1 = require("child_process");
|
|
45
|
-
const sdk_1 = require("@crewx/sdk");
|
|
46
|
-
const repository_1 = require("@crewx/sdk/repository");
|
|
47
|
-
const engine_1 = require("./src/engine");
|
|
48
|
-
const run_manager_1 = require("./src/run-manager");
|
|
49
|
-
const workflow_persistence_1 = require("./src/workflow-persistence");
|
|
50
|
-
const parent_task_sync_1 = require("./src/parent-task-sync");
|
|
51
|
-
const knowledge_core_1 = require("@crewx/knowledge-core");
|
|
52
|
-
let PKG_VERSION = 'unknown';
|
|
53
|
-
try {
|
|
54
|
-
const pkgPath = fs.existsSync(path.join(__dirname, 'package.json'))
|
|
55
|
-
? path.join(__dirname, 'package.json')
|
|
56
|
-
: path.join(__dirname, '..', 'package.json');
|
|
57
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
58
|
-
if (pkg?.version)
|
|
59
|
-
PKG_VERSION = String(pkg.version);
|
|
60
|
-
}
|
|
61
|
-
catch { }
|
|
62
|
-
async function loadTracer() {
|
|
63
|
-
try {
|
|
64
|
-
return await Promise.resolve().then(() => __importStar(require('@crewx/shared/skill-tracer')));
|
|
65
|
-
}
|
|
66
|
-
catch {
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
const BOOLEAN_FLAGS = new Set([
|
|
71
|
-
'help', 'json', 'mermaid', 'version', 'no-exec', 'auto', 'no-run', 'apply', 'dry-run',
|
|
72
|
-
]);
|
|
73
|
-
function parseArgs(args) {
|
|
74
|
-
const result = { positional: [], options: {} };
|
|
75
|
-
for (let i = 0; i < args.length; i++) {
|
|
76
|
-
const arg = args[i];
|
|
77
|
-
if (arg === '-h') {
|
|
78
|
-
result.options['help'] = true;
|
|
79
|
-
}
|
|
80
|
-
else if (arg.startsWith('--')) {
|
|
81
|
-
const eqIdx = arg.indexOf('=');
|
|
82
|
-
if (eqIdx !== -1) {
|
|
83
|
-
result.options[arg.slice(2, eqIdx)] = arg.slice(eqIdx + 1);
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
const key = arg.slice(2);
|
|
87
|
-
if (BOOLEAN_FLAGS.has(key)) {
|
|
88
|
-
result.options[key] = true;
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
const nextArg = args[i + 1];
|
|
92
|
-
if (nextArg !== undefined && !nextArg.startsWith('--') && nextArg !== '-h') {
|
|
93
|
-
result.options[key] = nextArg;
|
|
94
|
-
i++;
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
result.options[key] = true;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
result.positional.push(arg);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return result;
|
|
107
|
-
}
|
|
108
|
-
function getProjectRoot() {
|
|
109
|
-
try {
|
|
110
|
-
return (0, child_process_1.execSync)('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();
|
|
111
|
-
}
|
|
112
|
-
catch {
|
|
113
|
-
return process.cwd();
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
function getWorkflowConfig() {
|
|
117
|
-
return (0, list_view_1.readWorkflowConfig)(process.env.CREWX_CONFIG ?? path.join(getProjectRoot(), 'crewx.yaml'));
|
|
118
|
-
}
|
|
119
|
-
function getNodeResultStatus(run, nodeId, nodeType, noExec) {
|
|
120
|
-
if (noExec)
|
|
121
|
-
return null;
|
|
122
|
-
const audit = [...(run.audit ?? [])]
|
|
123
|
-
.reverse()
|
|
124
|
-
.find((entry) => entry.node_id === nodeId && entry.type !== 'retry');
|
|
125
|
-
if (!audit)
|
|
126
|
-
return null;
|
|
127
|
-
if (audit.error || nodeType === 'error')
|
|
128
|
-
return 'failed';
|
|
129
|
-
if (nodeType === 'approval' || nodeType === 'parallel')
|
|
130
|
-
return 'paused';
|
|
131
|
-
return 'completed';
|
|
132
|
-
}
|
|
133
|
-
function validateSlugInput(nameOrPath) {
|
|
134
|
-
if (!nameOrPath.endsWith('.yaml') && !nameOrPath.endsWith('.yml')) {
|
|
135
|
-
if (/[/\\]|\.\./.test(nameOrPath)) {
|
|
136
|
-
throw new Error(`Invalid workflow name: "${nameOrPath}"`);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
function resolveWorkflow(nameOrPath, paths, engine, preloaded) {
|
|
141
|
-
if (nameOrPath.endsWith('.yaml') || nameOrPath.endsWith('.yml')) {
|
|
142
|
-
const resolved = path.resolve(process.cwd(), nameOrPath);
|
|
143
|
-
if (!fs.existsSync(resolved)) {
|
|
144
|
-
throw new Error(`Workflow file not found: ${resolved}`);
|
|
145
|
-
}
|
|
146
|
-
return { filePath: resolved, resolvedVia: 'path' };
|
|
147
|
-
}
|
|
148
|
-
validateSlugInput(nameOrPath);
|
|
149
|
-
const allWorkflows = preloaded ?? loadAllWorkflows(engine, paths);
|
|
150
|
-
const byId = allWorkflows.find(w => w.id === nameOrPath);
|
|
151
|
-
if (byId) {
|
|
152
|
-
const dupes = allWorkflows.filter(w => w.id === nameOrPath && w.filePath !== byId.filePath);
|
|
153
|
-
if (dupes.length > 0) {
|
|
154
|
-
throw new Error(`Duplicate workflow ID "${nameOrPath}" found in multiple files:\n` +
|
|
155
|
-
` - ${byId.filePath}\n` +
|
|
156
|
-
dupes.map(f => ` - ${f.filePath}`).join('\n') +
|
|
157
|
-
`\nUse file path to disambiguate: workflow run start <path.yaml>`);
|
|
158
|
-
}
|
|
159
|
-
return { filePath: byId.filePath, workflowId: byId.id, resolvedVia: 'id' };
|
|
160
|
-
}
|
|
161
|
-
for (const dir of paths) {
|
|
162
|
-
const absDir = path.resolve(process.cwd(), dir);
|
|
163
|
-
for (const ext of ['.yaml', '.yml']) {
|
|
164
|
-
const candidate = path.join(absDir, `${nameOrPath}${ext}`);
|
|
165
|
-
if (fs.existsSync(candidate)) {
|
|
166
|
-
return { filePath: candidate, resolvedVia: 'slug' };
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
const allIds = allWorkflows.map(w => w.id);
|
|
171
|
-
throw new Error(`Workflow "${nameOrPath}" not found (searched as ID and file slug).\n` +
|
|
172
|
-
` Searched directories: ${paths.join(', ')}\n` +
|
|
173
|
-
(allIds.length > 0
|
|
174
|
-
? ` Available workflows: ${allIds.join(', ')}\n`
|
|
175
|
-
: '') +
|
|
176
|
-
` Hint: use "workflow list" to see all workflow IDs`);
|
|
177
|
-
}
|
|
178
|
-
function loadAllWorkflows(engine, paths) {
|
|
179
|
-
return (0, list_view_1.loadWorkflowPaths)(paths, process.cwd());
|
|
180
|
-
}
|
|
181
|
-
function resolveWorkflowSpecForRun(run, engine) {
|
|
182
|
-
const runFile = path.resolve(run.workflow_file);
|
|
183
|
-
const workflows = engine.loadWorkflows(path.dirname(runFile));
|
|
184
|
-
return workflows?.find((workflow) => path.resolve(workflow.filePath) === runFile && workflow.id === run.workflow_id)?.spec;
|
|
185
|
-
}
|
|
186
|
-
function printUsage() {
|
|
187
|
-
console.log(`
|
|
188
|
-
@crewx/workflow@${PKG_VERSION} - Workflow management for CrewX
|
|
2
|
+
"use strict";var z=exports&&exports.__createBinding||(Object.create?(function(e,o,t,n){if(n===void 0)n=t;var s=Object.getOwnPropertyDescriptor(o,t);if(!s||("get"in s?!o.__esModule:s.writable||s.configurable)){s={enumerable:true,get:function(){return o[t]}}}Object.defineProperty(e,n,s)}):(function(e,o,t,n){if(n===void 0)n=t;e[n]=o[t]}));var Q=exports&&exports.__setModuleDefault||(Object.create?(function(e,o){Object.defineProperty(e,"default",{enumerable:true,value:o})}):function(e,o){e["default"]=o});var J=exports&&exports.__importStar||(function(){var e=function(o){e=Object.getOwnPropertyNames||function(t){var n=[];for(var s in t)if(Object.prototype.hasOwnProperty.call(t,s))n[n.length]=s;return n};return e(o)};return function(o){if(o&&o.__esModule)return o;var t={};if(o!=null){for(var n=e(o),s=0;s<n.length;s++)if(n[s]!=="default")z(t,o,n[s])}Q(t,o);return t}})();Object.defineProperty(exports,"__esModule",{value:true});exports.resolveWorkflow=A;exports.findNextNodeForCAL=X;exports.getFlowChain=H;exports.main=L;const S=J(require("fs"));const D=require("./src/list-view");const y=J(require("path"));const Z=require("child_process");const W=require("@crewx/sdk");const B=require("@crewx/sdk/repository");const V=require("./src/engine");const I=require("./src/run-manager");const R=require("./src/workflow-persistence");const K=require("./src/parent-task-sync");const $=require("@crewx/knowledge-core");let E="unknown";try{const e=S.existsSync(y.join(__dirname,"package.json"))?y.join(__dirname,"package.json"):y.join(__dirname,"..","package.json");const o=JSON.parse(S.readFileSync(e,"utf-8"));if(o?.version)E=String(o.version)}catch{}async function M(){try{return await Promise.resolve().then(()=>J(require("@crewx/shared/skill-tracer")))}catch{return null}}const ee=new Set(["help","json","mermaid","version","no-exec","auto","no-run","apply","dry-run"]);function oe(e){const o={positional:[],options:{}};for(let t=0;t<e.length;t++){const n=e[t];if(n==="-h"){o.options["help"]=true}else if(n.startsWith("--")){const s=n.indexOf("=");if(s!==-1){o.options[n.slice(2,s)]=n.slice(s+1)}else{const l=n.slice(2);if(ee.has(l)){o.options[l]=true}else{const a=e[t+1];if(a!==void 0&&!a.startsWith("--")&&a!=="-h"){o.options[l]=a;t++}else{o.options[l]=true}}}}else{o.positional.push(n)}}return o}function F(){try{return(0,Z.execSync)("git rev-parse --show-toplevel",{encoding:"utf8"}).trim()}catch{return process.cwd()}}function O(){return(0,D.readWorkflowConfig)(process.env.CREWX_CONFIG??y.join(F(),"crewx.yaml"))}function te(e,o,t,n){if(n)return null;const s=[...e.audit??[]].reverse().find(l=>l.node_id===o&&l.type!=="retry");if(!s)return null;if(s.error||t==="error")return"failed";if(t==="approval"||t==="parallel")return"paused";return"completed"}function se(e){if(!e.endsWith(".yaml")&&!e.endsWith(".yml")){if(/[/\\]|\.\./.test(e)){throw new Error(`Invalid workflow name: "${e}"`)}}}function A(e,o,t,n){if(e.endsWith(".yaml")||e.endsWith(".yml")){const i=y.resolve(process.cwd(),e);if(!S.existsSync(i)){throw new Error(`Workflow file not found: ${i}`)}return{filePath:i,resolvedVia:"path"}}se(e);const s=n??N(t,o);const l=s.find(i=>i.id===e);if(l){const i=s.filter(k=>k.id===e&&k.filePath!==l.filePath);if(i.length>0){throw new Error(`Duplicate workflow ID "${e}" found in multiple files:
|
|
3
|
+
- ${l.filePath}
|
|
4
|
+
`+i.map(k=>` - ${k.filePath}`).join("\n")+`
|
|
5
|
+
Use file path to disambiguate: workflow run start <path.yaml>`)}return{filePath:l.filePath,workflowId:l.id,resolvedVia:"id"}}for(const i of o){const k=y.resolve(process.cwd(),i);for(const g of[".yaml",".yml"]){const d=y.join(k,`${e}${g}`);if(S.existsSync(d)){return{filePath:d,resolvedVia:"slug"}}}}const a=s.map(i=>i.id);throw new Error(`Workflow "${e}" not found (searched as ID and file slug).
|
|
6
|
+
Searched directories: ${o.join(", ")}
|
|
7
|
+
`+(a.length>0?` Available workflows: ${a.join(", ")}
|
|
8
|
+
`:"")+` Hint: use "workflow list" to see all workflow IDs`)}function N(e,o){return(0,D.loadWorkflowPaths)(o,process.cwd())}function G(e,o){const t=y.resolve(e.workflow_file);const n=o.loadWorkflows(y.dirname(t));return n?.find(s=>y.resolve(s.filePath)===t&&s.id===e.workflow_id)?.spec}function U(){console.log(`
|
|
9
|
+
@crewx/workflow@${E} - Workflow management for CrewX
|
|
189
10
|
|
|
190
11
|
Usage:
|
|
191
12
|
workflow <command> [args] [options]
|
|
@@ -195,13 +16,13 @@ Commands:
|
|
|
195
16
|
workflow show <id|file> Show workflow detail
|
|
196
17
|
workflow validate [id|file] Validate workflow YAML
|
|
197
18
|
workflow run start <id|file> Create new execution (add --auto to run to completion)
|
|
198
|
-
# Resolution: .yaml path
|
|
19
|
+
# Resolution: .yaml path \u2192 workflow ID \u2192 filename slug
|
|
199
20
|
workflow run state <id> Get/set execution state
|
|
200
21
|
workflow run node <id> <node> Execute node (auto-invoke agent)
|
|
201
22
|
workflow run auto <id> Auto-run from current node until end/pause
|
|
202
23
|
workflow run reset <id> Reset execution
|
|
203
24
|
workflow run retry <id> Retry a failed run from the failed node (--no-run: transition only)
|
|
204
|
-
|
|
25
|
+
\u26A0\uFE0F re-executes the failed node; safe only if it is idempotent
|
|
205
26
|
workflow run list List all executions
|
|
206
27
|
workflow repair <run-id> Preview parent-task repair (use --apply to write)
|
|
207
28
|
workflow usage Show detailed guide
|
|
@@ -224,1004 +45,10 @@ Examples:
|
|
|
224
45
|
workflow validate workflows/standard-dev.yaml
|
|
225
46
|
|
|
226
47
|
For detailed usage guide: workflow usage
|
|
227
|
-
`);
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
process.stdout.write(fs.readFileSync(skillMd, "utf-8"));
|
|
233
|
-
|
|
234
|
-
else {
|
|
235
|
-
printUsage();
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
function formatFlow(nodes) {
|
|
239
|
-
const lines = [];
|
|
240
|
-
let idx = 1;
|
|
241
|
-
for (const [nodeId, node] of Object.entries(nodes)) {
|
|
242
|
-
if (!node || typeof node !== 'object')
|
|
243
|
-
continue;
|
|
244
|
-
const type = node.type ?? 'unknown';
|
|
245
|
-
let desc = `${nodeId} (${type}`;
|
|
246
|
-
if (node.agent)
|
|
247
|
-
desc += ` → ${node.agent}`;
|
|
248
|
-
desc += ')';
|
|
249
|
-
if (type === 'parallel' && Array.isArray(node.branches)) {
|
|
250
|
-
desc += ` → [${node.branches.join(', ')}]`;
|
|
251
|
-
if (node.join)
|
|
252
|
-
desc += ` → ${node.join}`;
|
|
253
|
-
}
|
|
254
|
-
else if (type === 'branch' && node.branches && typeof node.branches === 'object' && !Array.isArray(node.branches)) {
|
|
255
|
-
const parts = Object.entries(node.branches)
|
|
256
|
-
.map(([k, v]) => `${k}: ${v}`)
|
|
257
|
-
.join(' / ');
|
|
258
|
-
desc += ` → ${parts}`;
|
|
259
|
-
}
|
|
260
|
-
else if (node.next) {
|
|
261
|
-
desc += ` → ${node.next}`;
|
|
262
|
-
}
|
|
263
|
-
lines.push(` ${idx}. ${desc}`);
|
|
264
|
-
idx++;
|
|
265
|
-
}
|
|
266
|
-
return lines;
|
|
267
|
-
}
|
|
268
|
-
function cmdList(engine, jsonMode) {
|
|
269
|
-
const { paths } = getWorkflowConfig();
|
|
270
|
-
console.log((0, list_view_1.formatWorkflowList)({ paths, workflows: loadAllWorkflows(engine, paths), jsonMode }));
|
|
271
|
-
}
|
|
272
|
-
function cmdShow(engine, name, jsonMode, mermaidMode) {
|
|
273
|
-
const { paths } = getWorkflowConfig();
|
|
274
|
-
const workflows = loadAllWorkflows(engine, paths);
|
|
275
|
-
let wf;
|
|
276
|
-
try {
|
|
277
|
-
const resolved = resolveWorkflow(name, paths, engine, workflows);
|
|
278
|
-
if (resolved.workflowId) {
|
|
279
|
-
wf = workflows.find(w => w.id === resolved.workflowId);
|
|
280
|
-
}
|
|
281
|
-
else {
|
|
282
|
-
const fileWorkflows = workflows.filter(w => w.filePath === resolved.filePath);
|
|
283
|
-
if (fileWorkflows.length === 1) {
|
|
284
|
-
wf = fileWorkflows[0];
|
|
285
|
-
}
|
|
286
|
-
else if (fileWorkflows.length > 1) {
|
|
287
|
-
throw new Error(`File "${path.basename(resolved.filePath)}" contains ${fileWorkflows.length} workflows.\n` +
|
|
288
|
-
` Specify a workflow ID:\n` +
|
|
289
|
-
fileWorkflows.map(w => ` workflow show ${w.id}`).join('\n'));
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
catch (e) {
|
|
294
|
-
const msg = e.message;
|
|
295
|
-
if (jsonMode) {
|
|
296
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
297
|
-
}
|
|
298
|
-
else {
|
|
299
|
-
console.error(`\u274C ${msg}`);
|
|
300
|
-
}
|
|
301
|
-
process.exit(1);
|
|
302
|
-
}
|
|
303
|
-
if (!wf) {
|
|
304
|
-
const msg = `Workflow "${name}" not found in: ${paths.join(', ')}`;
|
|
305
|
-
if (jsonMode) {
|
|
306
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
307
|
-
}
|
|
308
|
-
else {
|
|
309
|
-
console.error(`\u274C ${msg}`);
|
|
310
|
-
}
|
|
311
|
-
process.exit(1);
|
|
312
|
-
}
|
|
313
|
-
if (mermaidMode) {
|
|
314
|
-
console.log(engine.toMermaid(wf.spec));
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
const { spec } = wf;
|
|
318
|
-
const meta = spec.metadata ?? {};
|
|
319
|
-
const nodes = spec.nodes ?? {};
|
|
320
|
-
const stateKeys = spec.state ? Object.keys(spec.state) : [];
|
|
321
|
-
if (jsonMode) {
|
|
322
|
-
console.log(JSON.stringify({
|
|
323
|
-
success: true,
|
|
324
|
-
workflow: {
|
|
325
|
-
id: wf.id,
|
|
326
|
-
name: meta.name ?? wf.id,
|
|
327
|
-
description: meta.description ?? '',
|
|
328
|
-
timeout: spec.timeout ?? null,
|
|
329
|
-
max_iterations: spec.max_iterations ?? null,
|
|
330
|
-
state: stateKeys,
|
|
331
|
-
node_count: Object.keys(nodes).length,
|
|
332
|
-
nodes: Object.entries(nodes).map(([id, n]) => ({
|
|
333
|
-
id,
|
|
334
|
-
type: n.type,
|
|
335
|
-
agent: n.agent ?? null,
|
|
336
|
-
next: n.next ?? null,
|
|
337
|
-
branches: n.branches ?? null,
|
|
338
|
-
join: n.join ?? null,
|
|
339
|
-
})),
|
|
340
|
-
},
|
|
341
|
-
}, null, 2));
|
|
342
|
-
}
|
|
343
|
-
else {
|
|
344
|
-
const desc = meta.description ? ` \u2014 ${meta.description}` : '';
|
|
345
|
-
console.log(`\uD83D\uDCC4 ${wf.id}${desc}`);
|
|
346
|
-
console.log(` \uD83D\uDCC1 ${wf.filePath}`);
|
|
347
|
-
const settings = [];
|
|
348
|
-
if (spec.timeout)
|
|
349
|
-
settings.push(`timeout: ${spec.timeout}s`);
|
|
350
|
-
if (spec.max_iterations)
|
|
351
|
-
settings.push(`max_iterations: ${spec.max_iterations}`);
|
|
352
|
-
if (settings.length > 0)
|
|
353
|
-
console.log(` ${settings.join(' | ')}`);
|
|
354
|
-
if (stateKeys.length > 0) {
|
|
355
|
-
console.log(`\n State: ${stateKeys.join(', ')}`);
|
|
356
|
-
}
|
|
357
|
-
const flowLines = formatFlow(nodes);
|
|
358
|
-
if (flowLines.length > 0) {
|
|
359
|
-
console.log(`\n Flow:`);
|
|
360
|
-
flowLines.forEach(l => console.log(l));
|
|
361
|
-
}
|
|
362
|
-
const showCtx = {
|
|
363
|
-
commandId: 'workflow.show',
|
|
364
|
-
params: { name },
|
|
365
|
-
resultCount: Object.keys(nodes).length,
|
|
366
|
-
};
|
|
367
|
-
const showActions = (0, knowledge_core_1.generateNextActions)(showCtx);
|
|
368
|
-
const showBlock = (0, knowledge_core_1.formatActions)(showActions);
|
|
369
|
-
if (showBlock)
|
|
370
|
-
console.log(showBlock);
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
function cmdValidate(engine, target, jsonMode) {
|
|
374
|
-
const { paths } = getWorkflowConfig();
|
|
375
|
-
let files;
|
|
376
|
-
let resolvedSlug;
|
|
377
|
-
if (target) {
|
|
378
|
-
try {
|
|
379
|
-
const resolvedWf = resolveWorkflow(target, paths, engine);
|
|
380
|
-
if (!fs.existsSync(resolvedWf.filePath)) {
|
|
381
|
-
const msg = `File not found: ${resolvedWf.filePath}`;
|
|
382
|
-
if (jsonMode) {
|
|
383
|
-
console.log(JSON.stringify({ success: false, error: msg, workflows: [] }));
|
|
384
|
-
}
|
|
385
|
-
else {
|
|
386
|
-
console.error(`\u274C ${msg}`);
|
|
387
|
-
}
|
|
388
|
-
process.exit(2);
|
|
389
|
-
}
|
|
390
|
-
files = [resolvedWf.filePath];
|
|
391
|
-
if (!target.endsWith('.yaml') && !target.endsWith('.yml')) {
|
|
392
|
-
resolvedSlug = target;
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
catch (e) {
|
|
396
|
-
const msg = e.message;
|
|
397
|
-
if (jsonMode) {
|
|
398
|
-
console.log(JSON.stringify({ success: false, error: msg, workflows: [] }));
|
|
399
|
-
}
|
|
400
|
-
else {
|
|
401
|
-
console.error(`\u274C ${msg}`);
|
|
402
|
-
}
|
|
403
|
-
process.exit(2);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
else {
|
|
407
|
-
files = [];
|
|
408
|
-
for (const dir of paths) {
|
|
409
|
-
const absDir = path.resolve(process.cwd(), dir);
|
|
410
|
-
if (!fs.existsSync(absDir) || !fs.statSync(absDir).isDirectory())
|
|
411
|
-
continue;
|
|
412
|
-
const dirFiles = fs.readdirSync(absDir)
|
|
413
|
-
.filter(f => f.endsWith('.yaml') || f.endsWith('.yml'))
|
|
414
|
-
.map(f => path.join(absDir, f));
|
|
415
|
-
files.push(...dirFiles);
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
if (files.length === 0) {
|
|
419
|
-
const msg = 'No workflow YAML files found';
|
|
420
|
-
if (jsonMode) {
|
|
421
|
-
console.log(JSON.stringify({ success: true, message: msg, workflows: [] }));
|
|
422
|
-
}
|
|
423
|
-
else {
|
|
424
|
-
console.log(`\u2139\uFE0F ${msg}`);
|
|
425
|
-
}
|
|
426
|
-
return;
|
|
427
|
-
}
|
|
428
|
-
let hasErrors = false;
|
|
429
|
-
const allResults = [];
|
|
430
|
-
let firstValidName;
|
|
431
|
-
for (const file of files) {
|
|
432
|
-
const { success, results, error } = engine.validateFile(file);
|
|
433
|
-
if (error) {
|
|
434
|
-
hasErrors = true;
|
|
435
|
-
if (jsonMode) {
|
|
436
|
-
allResults.push({ file, results: [{ name: path.basename(file), valid: false, errors: [{ path: file, message: error }] }] });
|
|
437
|
-
}
|
|
438
|
-
else {
|
|
439
|
-
console.error(`\u274C ${file}: ${error}\n`);
|
|
440
|
-
}
|
|
441
|
-
continue;
|
|
442
|
-
}
|
|
443
|
-
if (!success)
|
|
444
|
-
hasErrors = true;
|
|
445
|
-
if (jsonMode) {
|
|
446
|
-
allResults.push({ file, results });
|
|
447
|
-
}
|
|
448
|
-
else {
|
|
449
|
-
for (const r of results) {
|
|
450
|
-
if (r.valid) {
|
|
451
|
-
if (!firstValidName)
|
|
452
|
-
firstValidName = r.name;
|
|
453
|
-
console.log(`\u2705 workflow "${r.name}" \u2014 validation passed`);
|
|
454
|
-
}
|
|
455
|
-
else {
|
|
456
|
-
console.log(`\u274C workflow "${r.name}" \u2014 validation failed`);
|
|
457
|
-
for (const e of r.errors) {
|
|
458
|
-
console.log(` - ${e.path}: ${e.message}`);
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
console.log();
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
if (jsonMode) {
|
|
466
|
-
const flatResults = allResults.flatMap(r => r.results);
|
|
467
|
-
console.log(JSON.stringify({
|
|
468
|
-
success: !hasErrors,
|
|
469
|
-
workflows: flatResults,
|
|
470
|
-
}, null, 2));
|
|
471
|
-
}
|
|
472
|
-
if (!jsonMode && !hasErrors) {
|
|
473
|
-
const calName = resolvedSlug ?? firstValidName;
|
|
474
|
-
if (calName) {
|
|
475
|
-
const valCtx = {
|
|
476
|
-
commandId: 'workflow.validate',
|
|
477
|
-
params: { name: calName },
|
|
478
|
-
resultCount: 1,
|
|
479
|
-
};
|
|
480
|
-
const valActions = (0, knowledge_core_1.generateNextActions)(valCtx);
|
|
481
|
-
const valBlock = (0, knowledge_core_1.formatActions)(valActions);
|
|
482
|
-
if (valBlock)
|
|
483
|
-
console.log(valBlock);
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
if (hasErrors)
|
|
487
|
-
process.exit(1);
|
|
488
|
-
}
|
|
489
|
-
function repairChangeForOutput(change) {
|
|
490
|
-
return {
|
|
491
|
-
task_id: change.id,
|
|
492
|
-
kind: change.kind,
|
|
493
|
-
before: {
|
|
494
|
-
status: change.before.status,
|
|
495
|
-
error: change.before.error,
|
|
496
|
-
completed_at: change.before.completedAt,
|
|
497
|
-
duration_ms: change.before.durationMs,
|
|
498
|
-
},
|
|
499
|
-
after: {
|
|
500
|
-
status: change.after.status,
|
|
501
|
-
error: change.after.error,
|
|
502
|
-
completed_at: change.after.completedAt,
|
|
503
|
-
duration_ms: change.after.durationMs,
|
|
504
|
-
},
|
|
505
|
-
reason: change.reason,
|
|
506
|
-
};
|
|
507
|
-
}
|
|
508
|
-
function auditSnapshotChangeForOutput(change) {
|
|
509
|
-
return {
|
|
510
|
-
instance_id: change.instanceId,
|
|
511
|
-
before: change.before,
|
|
512
|
-
after: change.after,
|
|
513
|
-
};
|
|
514
|
-
}
|
|
515
|
-
function reportRepair(preview, mode, jsonMode, projectionErrors = []) {
|
|
516
|
-
const changes = preview.parentChanges.map(repairChangeForOutput);
|
|
517
|
-
const auditCardChanges = preview.auditSnapshotChanges.map(auditSnapshotChangeForOutput);
|
|
518
|
-
const errors = [...preview.errors, ...projectionErrors];
|
|
519
|
-
if (jsonMode) {
|
|
520
|
-
console.log(JSON.stringify({
|
|
521
|
-
success: true,
|
|
522
|
-
exec_id: preview.runId,
|
|
523
|
-
mode,
|
|
524
|
-
dry_run: mode === 'dry-run',
|
|
525
|
-
applied: mode === 'apply',
|
|
526
|
-
run_status: preview.runStatus,
|
|
527
|
-
changes,
|
|
528
|
-
audit_card_changes: auditCardChanges,
|
|
529
|
-
...(errors.length > 0 ? { errors } : {}),
|
|
530
|
-
}, null, 2));
|
|
531
|
-
return;
|
|
532
|
-
}
|
|
533
|
-
console.log(`🔧 Workflow repair (${mode}): ${preview.runId}`);
|
|
534
|
-
console.log(` Run status: ${preview.runStatus}`);
|
|
535
|
-
if (changes.length === 0 && auditCardChanges.length === 0) {
|
|
536
|
-
console.log(' No parent lifecycle changes needed.');
|
|
537
|
-
}
|
|
538
|
-
else {
|
|
539
|
-
for (const change of changes) {
|
|
540
|
-
console.log(` ${mode === 'apply' ? 'Updated' : 'Would update'} ${change.task_id} (${change.kind}): ${change.before.status} → ${change.after.status}`);
|
|
541
|
-
console.log(` Reason: ${change.reason}`);
|
|
542
|
-
}
|
|
543
|
-
for (const change of auditCardChanges) {
|
|
544
|
-
console.log(` ${mode === 'apply' ? 'Updated' : 'Would update'} audit card ${change.instance_id}: ${change.before.phase ?? '(unknown)'} → ${change.after.phase}`);
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
for (const error of errors) {
|
|
548
|
-
console.error(` Warning: ${error}`);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
function validateRepairRun(run, execId) {
|
|
552
|
-
if (run.id !== execId) {
|
|
553
|
-
throw new Error(`Execution file ID mismatch: requested ${execId}, found ${run.id || '(missing)'}`);
|
|
554
|
-
}
|
|
555
|
-
if (!run.workflow_id || !run.workflow_file || !run.created_at || !run.updated_at
|
|
556
|
-
|| typeof run.current_node !== 'string'
|
|
557
|
-
|| !Array.isArray(run.completed_nodes)
|
|
558
|
-
|| !run.state || typeof run.state !== 'object'
|
|
559
|
-
|| !run.initial_state || typeof run.initial_state !== 'object') {
|
|
560
|
-
throw new Error(`Run "${execId}" is incomplete and cannot be repaired`);
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
function cmdRepair(rest, jsonMode, apply, dryRun) {
|
|
564
|
-
const execId = rest[0];
|
|
565
|
-
if (!execId) {
|
|
566
|
-
const msg = 'Usage: workflow repair <run-id> [--dry-run|--apply]';
|
|
567
|
-
if (jsonMode)
|
|
568
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
569
|
-
else
|
|
570
|
-
console.error(`❌ ${msg}`);
|
|
571
|
-
process.exit(1);
|
|
572
|
-
}
|
|
573
|
-
if (apply && dryRun) {
|
|
574
|
-
const msg = 'Choose either --dry-run or --apply, not both';
|
|
575
|
-
if (jsonMode)
|
|
576
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
577
|
-
else
|
|
578
|
-
console.error(`❌ ${msg}`);
|
|
579
|
-
process.exit(1);
|
|
580
|
-
}
|
|
581
|
-
const projectRoot = getProjectRoot();
|
|
582
|
-
const workspaceId = (0, sdk_1.hashWorkspaceId)(projectRoot);
|
|
583
|
-
const taskRepo = new repository_1.TaskRepository();
|
|
584
|
-
const mgr = new run_manager_1.RunManager(undefined, projectRoot);
|
|
585
|
-
const engine = new engine_1.WorkflowEngine();
|
|
586
|
-
let uiInstanceStore;
|
|
587
|
-
let readOnlyUiInstanceStore;
|
|
588
|
-
const resolveWorkflowSpec = (run) => resolveWorkflowSpecForRun(run, engine);
|
|
589
|
-
const getUiInstanceStore = () => {
|
|
590
|
-
uiInstanceStore ?? (uiInstanceStore = new sdk_1.UiInstanceStore(taskRepo.resolveDbPath()));
|
|
591
|
-
return uiInstanceStore;
|
|
592
|
-
};
|
|
593
|
-
const getReadOnlyUiInstanceStore = () => {
|
|
594
|
-
readOnlyUiInstanceStore ?? (readOnlyUiInstanceStore = (0, sdk_1.openUiInstanceReadStore)(taskRepo.resolveDbPath()));
|
|
595
|
-
return readOnlyUiInstanceStore;
|
|
596
|
-
};
|
|
597
|
-
const persistenceProjection = (0, workflow_persistence_1.createWorkflowPersistenceProjection)({
|
|
598
|
-
taskRepo,
|
|
599
|
-
workspaceId,
|
|
600
|
-
resolveWorkflowSpec,
|
|
601
|
-
getUiInstanceStore,
|
|
602
|
-
});
|
|
603
|
-
const persistencePreviewOptions = {
|
|
604
|
-
taskRepo,
|
|
605
|
-
workspaceId,
|
|
606
|
-
resolveWorkflowSpec,
|
|
607
|
-
getUiInstanceStore: getReadOnlyUiInstanceStore,
|
|
608
|
-
};
|
|
609
|
-
try {
|
|
610
|
-
const observed = mgr.readRun(execId);
|
|
611
|
-
if (!observed)
|
|
612
|
-
throw new Error(`Execution not found: ${execId}`);
|
|
613
|
-
validateRepairRun(observed, execId);
|
|
614
|
-
if (!(0, parent_task_sync_1.deriveWorkflowParentLifecycle)(observed)) {
|
|
615
|
-
throw new Error(`Run "${execId}" has an unsupported or insufficient persisted status: ${observed.status ?? 'pending'}`);
|
|
616
|
-
}
|
|
617
|
-
if (!apply) {
|
|
618
|
-
reportRepair((0, workflow_persistence_1.previewWorkflowPersistenceProjection)(persistencePreviewOptions, observed), 'dry-run', jsonMode);
|
|
619
|
-
return;
|
|
620
|
-
}
|
|
621
|
-
const result = mgr.withRunLock(execId, (latest) => {
|
|
622
|
-
validateRepairRun(latest, execId);
|
|
623
|
-
if (!(0, parent_task_sync_1.deriveWorkflowParentLifecycle)(latest)) {
|
|
624
|
-
throw new Error(`Run "${execId}" has an unsupported or insufficient persisted status: ${latest.status ?? 'pending'}`);
|
|
625
|
-
}
|
|
626
|
-
const preview = (0, workflow_persistence_1.previewWorkflowPersistenceProjection)(persistencePreviewOptions, latest);
|
|
627
|
-
const projection = persistenceProjection(latest);
|
|
628
|
-
return { preview, projection };
|
|
629
|
-
});
|
|
630
|
-
reportRepair(result.preview, 'apply', jsonMode, result.projection.errors);
|
|
631
|
-
}
|
|
632
|
-
catch (e) {
|
|
633
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
634
|
-
if (jsonMode)
|
|
635
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
636
|
-
else
|
|
637
|
-
console.error(`❌ ${msg}`);
|
|
638
|
-
process.exit(1);
|
|
639
|
-
}
|
|
640
|
-
finally {
|
|
641
|
-
uiInstanceStore?.close();
|
|
642
|
-
readOnlyUiInstanceStore?.close();
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
async function cmdRun(rest, rawArgs, jsonMode, engine) {
|
|
646
|
-
const runSub = rest[0];
|
|
647
|
-
const runArgs = rest.slice(1);
|
|
648
|
-
const projectRoot = getProjectRoot();
|
|
649
|
-
const taskRepo = new repository_1.TaskRepository();
|
|
650
|
-
let uiInstanceStore;
|
|
651
|
-
const persistenceProjection = (0, workflow_persistence_1.createWorkflowPersistenceProjection)({
|
|
652
|
-
taskRepo,
|
|
653
|
-
workspaceId: (0, sdk_1.hashWorkspaceId)(projectRoot),
|
|
654
|
-
resolveWorkflowSpec: (run) => resolveWorkflowSpecForRun(run, engine),
|
|
655
|
-
getUiInstanceStore: () => {
|
|
656
|
-
uiInstanceStore ?? (uiInstanceStore = new sdk_1.UiInstanceStore(taskRepo.resolveDbPath()));
|
|
657
|
-
return uiInstanceStore;
|
|
658
|
-
},
|
|
659
|
-
});
|
|
660
|
-
const mgr = new run_manager_1.RunManager(undefined, projectRoot, {
|
|
661
|
-
onRunPersisted: persistenceProjection,
|
|
662
|
-
});
|
|
663
|
-
const { paths } = getWorkflowConfig();
|
|
664
|
-
switch (runSub) {
|
|
665
|
-
case 'start': {
|
|
666
|
-
const positional = runArgs.filter((a, i) => !a.startsWith('--') && runArgs[i - 1] !== '--set');
|
|
667
|
-
const nameOrPath = positional[0];
|
|
668
|
-
if (!nameOrPath) {
|
|
669
|
-
const msg = 'Usage: workflow run start <id|file> [--set key=value ...]';
|
|
670
|
-
if (jsonMode) {
|
|
671
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
672
|
-
}
|
|
673
|
-
else {
|
|
674
|
-
console.error(`\u274C ${msg}`);
|
|
675
|
-
}
|
|
676
|
-
process.exit(1);
|
|
677
|
-
}
|
|
678
|
-
let resolved;
|
|
679
|
-
try {
|
|
680
|
-
resolved = resolveWorkflow(nameOrPath, paths, engine);
|
|
681
|
-
}
|
|
682
|
-
catch (e) {
|
|
683
|
-
const msg = e.message;
|
|
684
|
-
if (jsonMode) {
|
|
685
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
686
|
-
}
|
|
687
|
-
else {
|
|
688
|
-
console.error(`\u274C ${msg}`);
|
|
689
|
-
}
|
|
690
|
-
process.exit(1);
|
|
691
|
-
}
|
|
692
|
-
const sets = (0, run_manager_1.parseSetArgs)(rawArgs);
|
|
693
|
-
const auto = rawArgs.includes('--auto');
|
|
694
|
-
try {
|
|
695
|
-
const run = mgr.start(resolved.filePath, resolved.workflowId ?? positional[1], Object.keys(sets).length > 0 ? sets : undefined);
|
|
696
|
-
if (auto) {
|
|
697
|
-
if (!jsonMode) {
|
|
698
|
-
console.log(`✅ Created execution: ${run.id}`);
|
|
699
|
-
console.log(` Workflow: ${run.workflow_id} (${resolved.filePath})`);
|
|
700
|
-
}
|
|
701
|
-
const result = await mgr.runAuto(run.id);
|
|
702
|
-
reportAutoResult(result, jsonMode);
|
|
703
|
-
break;
|
|
704
|
-
}
|
|
705
|
-
if (jsonMode) {
|
|
706
|
-
console.log(JSON.stringify({ success: true, exec_id: run.id, run }, null, 2));
|
|
707
|
-
}
|
|
708
|
-
else {
|
|
709
|
-
console.log(`\u2705 Created execution: ${run.id}`);
|
|
710
|
-
console.log(` Workflow: ${run.workflow_id} (${resolved.filePath})`);
|
|
711
|
-
console.log(` State keys: ${Object.keys(run.state).join(', ') || '(none)'}`);
|
|
712
|
-
console.log(` File: ${path.join('.crewx/workflow-runs', run.id + '.json')}`);
|
|
713
|
-
let firstNode = null;
|
|
714
|
-
try {
|
|
715
|
-
const allWfs = loadAllWorkflows(engine, paths);
|
|
716
|
-
const wf = allWfs.find(w => w.id === run.workflow_id);
|
|
717
|
-
if (wf) {
|
|
718
|
-
const flowChain = getFlowChain(wf.spec.nodes);
|
|
719
|
-
if (flowChain.length > 0) {
|
|
720
|
-
console.log(` Flow: ${flowChain.join(' \u2192 ')}`);
|
|
721
|
-
}
|
|
722
|
-
const nodeIds = Object.keys(wf.spec.nodes);
|
|
723
|
-
firstNode = nodeIds[0] ?? null;
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
catch { }
|
|
727
|
-
const startCtx = {
|
|
728
|
-
commandId: 'workflow.run.start',
|
|
729
|
-
params: { name: run.workflow_id },
|
|
730
|
-
resultIds: firstNode ? [run.id, firstNode] : [run.id],
|
|
731
|
-
resultCount: 1,
|
|
732
|
-
};
|
|
733
|
-
const startActions = (0, knowledge_core_1.generateNextActions)(startCtx);
|
|
734
|
-
const startBlock = (0, knowledge_core_1.formatActions)(startActions);
|
|
735
|
-
if (startBlock)
|
|
736
|
-
console.log(startBlock);
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
catch (e) {
|
|
740
|
-
const msg = e.message;
|
|
741
|
-
if (jsonMode) {
|
|
742
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
743
|
-
}
|
|
744
|
-
else {
|
|
745
|
-
console.error(`\u274C ${msg}`);
|
|
746
|
-
}
|
|
747
|
-
process.exit(1);
|
|
748
|
-
}
|
|
749
|
-
break;
|
|
750
|
-
}
|
|
751
|
-
case 'state': {
|
|
752
|
-
const positional = runArgs.filter((a, i) => !a.startsWith('--') && runArgs[i - 1] !== '--set');
|
|
753
|
-
const execId = positional[0];
|
|
754
|
-
if (!execId) {
|
|
755
|
-
const msg = 'Usage: workflow run state <exec-id> [--set key=value ...]';
|
|
756
|
-
if (jsonMode) {
|
|
757
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
758
|
-
}
|
|
759
|
-
else {
|
|
760
|
-
console.error(`\u274C ${msg}`);
|
|
761
|
-
}
|
|
762
|
-
process.exit(1);
|
|
763
|
-
}
|
|
764
|
-
const run = mgr.loadRun(execId);
|
|
765
|
-
if (!run) {
|
|
766
|
-
const msg = `Execution not found: ${execId}`;
|
|
767
|
-
if (jsonMode) {
|
|
768
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
769
|
-
}
|
|
770
|
-
else {
|
|
771
|
-
console.error(`\u274C ${msg}`);
|
|
772
|
-
}
|
|
773
|
-
process.exit(1);
|
|
774
|
-
}
|
|
775
|
-
const sets = (0, run_manager_1.parseSetArgs)(rawArgs);
|
|
776
|
-
const stdinKeys = Object.entries(sets).filter(([, v]) => v === '-').map(([k]) => k);
|
|
777
|
-
if (stdinKeys.length > 0 && !process.stdin.isTTY) {
|
|
778
|
-
const stdinRaw = await (0, run_manager_1.readStdin)();
|
|
779
|
-
let stdinValue;
|
|
780
|
-
try {
|
|
781
|
-
stdinValue = JSON.parse(stdinRaw);
|
|
782
|
-
}
|
|
783
|
-
catch {
|
|
784
|
-
stdinValue = stdinRaw;
|
|
785
|
-
}
|
|
786
|
-
for (const key of stdinKeys) {
|
|
787
|
-
sets[key] = stdinValue;
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
if (Object.keys(sets).length > 0) {
|
|
791
|
-
Object.assign(run.state, sets);
|
|
792
|
-
mgr.saveRun(run);
|
|
793
|
-
}
|
|
794
|
-
if (jsonMode) {
|
|
795
|
-
console.log(JSON.stringify({ success: true, exec_id: execId, state: run.state }, null, 2));
|
|
796
|
-
}
|
|
797
|
-
else {
|
|
798
|
-
if (Object.keys(sets).length > 0) {
|
|
799
|
-
console.log(`\u2705 Updated state for ${execId}`);
|
|
800
|
-
}
|
|
801
|
-
console.log(JSON.stringify(run.state, null, 2));
|
|
802
|
-
const nextNode = findNextNode(run, engine);
|
|
803
|
-
const stateCtx = {
|
|
804
|
-
commandId: 'workflow.run.state',
|
|
805
|
-
params: { execId },
|
|
806
|
-
resultIds: nextNode ? [nextNode] : [],
|
|
807
|
-
resultCount: Object.keys(run.state).length,
|
|
808
|
-
};
|
|
809
|
-
const stateActions = (0, knowledge_core_1.generateNextActions)(stateCtx);
|
|
810
|
-
const stateBlock = (0, knowledge_core_1.formatActions)(stateActions);
|
|
811
|
-
if (stateBlock)
|
|
812
|
-
console.log(stateBlock);
|
|
813
|
-
}
|
|
814
|
-
break;
|
|
815
|
-
}
|
|
816
|
-
case 'node': {
|
|
817
|
-
const positional = runArgs.filter(a => !a.startsWith('--'));
|
|
818
|
-
const execId = positional[0];
|
|
819
|
-
const nodeId = positional[1];
|
|
820
|
-
if (!execId || !nodeId) {
|
|
821
|
-
const msg = 'Usage: workflow run node <exec-id> <node-id> [--no-exec]';
|
|
822
|
-
if (jsonMode) {
|
|
823
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
824
|
-
}
|
|
825
|
-
else {
|
|
826
|
-
console.error(`\u274C ${msg}`);
|
|
827
|
-
}
|
|
828
|
-
process.exit(1);
|
|
829
|
-
}
|
|
830
|
-
const noExec = rawArgs.includes('--no-exec');
|
|
831
|
-
try {
|
|
832
|
-
const run = noExec
|
|
833
|
-
? mgr.moveNode(execId, nodeId)
|
|
834
|
-
: await mgr.executeNode(execId, nodeId);
|
|
835
|
-
const nodeStatus = getNodeResultStatus(run, nodeId, mgr.getNodeType(run.workflow_file, run.workflow_id, nodeId), noExec);
|
|
836
|
-
if (jsonMode) {
|
|
837
|
-
console.log(JSON.stringify({
|
|
838
|
-
success: true,
|
|
839
|
-
exec_id: execId,
|
|
840
|
-
current_node: run.current_node,
|
|
841
|
-
completed_nodes: run.completed_nodes,
|
|
842
|
-
status: run.status,
|
|
843
|
-
}, null, 2));
|
|
844
|
-
}
|
|
845
|
-
else {
|
|
846
|
-
if (run.status === 'completed') {
|
|
847
|
-
console.log(`Workflow completed.`);
|
|
848
|
-
}
|
|
849
|
-
else {
|
|
850
|
-
console.log(`\u2705 Node updated: ${nodeId}${noExec ? ' (move only)' : ''}`);
|
|
851
|
-
if (run.completed_nodes.length > 0) {
|
|
852
|
-
console.log(` Completed: ${run.completed_nodes.join(' \u2192 ')}`);
|
|
853
|
-
}
|
|
854
|
-
console.log(` Current: ${run.current_node}`);
|
|
855
|
-
if (nodeStatus) {
|
|
856
|
-
console.log(` Status: ${nodeStatus}`);
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
let nextNode = null;
|
|
860
|
-
try {
|
|
861
|
-
const { paths: wfPaths } = getWorkflowConfig();
|
|
862
|
-
const allWfs = loadAllWorkflows(engine, wfPaths);
|
|
863
|
-
const wf = allWfs.find(w => w.id === run.workflow_id);
|
|
864
|
-
if (wf) {
|
|
865
|
-
nextNode = findNextNodeForCAL(run.current_node, nodeId, wf.spec.nodes);
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
catch { }
|
|
869
|
-
const nodeCtx = {
|
|
870
|
-
commandId: 'workflow.run.node',
|
|
871
|
-
params: { execId, nodeId },
|
|
872
|
-
resultIds: nextNode ? [nextNode] : [],
|
|
873
|
-
resultCount: 1,
|
|
874
|
-
};
|
|
875
|
-
const nodeActions = (0, knowledge_core_1.generateNextActions)(nodeCtx);
|
|
876
|
-
const nodeBlock = (0, knowledge_core_1.formatActions)(nodeActions);
|
|
877
|
-
if (nodeBlock)
|
|
878
|
-
console.log(nodeBlock);
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
catch (e) {
|
|
882
|
-
const msg = e.message;
|
|
883
|
-
if (jsonMode) {
|
|
884
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
885
|
-
}
|
|
886
|
-
else {
|
|
887
|
-
console.error(`\u274C ${msg}`);
|
|
888
|
-
const fallbackCtx = {
|
|
889
|
-
commandId: 'workflow.run.node',
|
|
890
|
-
params: { execId, nodeId },
|
|
891
|
-
resultIds: [],
|
|
892
|
-
resultCount: 0,
|
|
893
|
-
};
|
|
894
|
-
const fallbackActions = (0, knowledge_core_1.generateNextActions)(fallbackCtx);
|
|
895
|
-
const fallbackBlock = (0, knowledge_core_1.formatActions)(fallbackActions);
|
|
896
|
-
if (fallbackBlock)
|
|
897
|
-
console.log(fallbackBlock);
|
|
898
|
-
}
|
|
899
|
-
process.exit(1);
|
|
900
|
-
}
|
|
901
|
-
break;
|
|
902
|
-
}
|
|
903
|
-
case 'reset': {
|
|
904
|
-
const positional = runArgs.filter(a => !a.startsWith('--'));
|
|
905
|
-
const execId = positional[0];
|
|
906
|
-
if (!execId) {
|
|
907
|
-
const msg = 'Usage: workflow run reset <exec-id>';
|
|
908
|
-
if (jsonMode) {
|
|
909
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
910
|
-
}
|
|
911
|
-
else {
|
|
912
|
-
console.error(`\u274C ${msg}`);
|
|
913
|
-
}
|
|
914
|
-
process.exit(1);
|
|
915
|
-
}
|
|
916
|
-
try {
|
|
917
|
-
const run = mgr.reset(execId);
|
|
918
|
-
if (jsonMode) {
|
|
919
|
-
console.log(JSON.stringify({ success: true, exec_id: execId, state: run.state }, null, 2));
|
|
920
|
-
}
|
|
921
|
-
else {
|
|
922
|
-
console.log(`\u2705 Reset execution: ${execId}`);
|
|
923
|
-
console.log(` State restored to initial values`);
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
catch (e) {
|
|
927
|
-
const msg = e.message;
|
|
928
|
-
if (jsonMode) {
|
|
929
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
930
|
-
}
|
|
931
|
-
else {
|
|
932
|
-
console.error(`\u274C ${msg}`);
|
|
933
|
-
}
|
|
934
|
-
process.exit(1);
|
|
935
|
-
}
|
|
936
|
-
break;
|
|
937
|
-
}
|
|
938
|
-
case 'retry': {
|
|
939
|
-
const positional = runArgs.filter(a => !a.startsWith('--'));
|
|
940
|
-
const execId = positional[0];
|
|
941
|
-
if (!execId) {
|
|
942
|
-
const msg = 'Usage: workflow run retry <exec-id> [--no-run]';
|
|
943
|
-
if (jsonMode) {
|
|
944
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
945
|
-
}
|
|
946
|
-
else {
|
|
947
|
-
console.error(`❌ ${msg}`);
|
|
948
|
-
}
|
|
949
|
-
process.exit(1);
|
|
950
|
-
}
|
|
951
|
-
const noRun = rawArgs.includes('--no-run');
|
|
952
|
-
try {
|
|
953
|
-
const run = mgr.retry(execId, { autoRun: !noRun });
|
|
954
|
-
if (noRun) {
|
|
955
|
-
if (jsonMode) {
|
|
956
|
-
console.log(JSON.stringify({
|
|
957
|
-
success: true,
|
|
958
|
-
exec_id: run.id,
|
|
959
|
-
current_node: run.current_node,
|
|
960
|
-
retry_count: run.retry_count,
|
|
961
|
-
status: run.status,
|
|
962
|
-
}, null, 2));
|
|
963
|
-
}
|
|
964
|
-
else {
|
|
965
|
-
console.log(`✅ Retry transition: ${execId}`);
|
|
966
|
-
console.log(` Resume node: ${run.current_node}`);
|
|
967
|
-
console.log(` Retry count: ${run.retry_count}`);
|
|
968
|
-
console.log(` (state transition only; run "workflow run auto ${execId}" to execute — re-runs the node, safe only if idempotent)`);
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
else {
|
|
972
|
-
const result = await mgr.runAuto(run.id);
|
|
973
|
-
reportAutoResult(result, jsonMode);
|
|
974
|
-
if (result.outcome === 'failed')
|
|
975
|
-
process.exit(1);
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
catch (e) {
|
|
979
|
-
const msg = e.message;
|
|
980
|
-
if (jsonMode) {
|
|
981
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
982
|
-
}
|
|
983
|
-
else {
|
|
984
|
-
console.error(`❌ ${msg}`);
|
|
985
|
-
}
|
|
986
|
-
process.exit(1);
|
|
987
|
-
}
|
|
988
|
-
break;
|
|
989
|
-
}
|
|
990
|
-
case 'list': {
|
|
991
|
-
const runs = mgr.listRuns();
|
|
992
|
-
if (jsonMode) {
|
|
993
|
-
console.log(JSON.stringify({
|
|
994
|
-
success: true,
|
|
995
|
-
runs: runs.map(r => ({
|
|
996
|
-
id: r.id,
|
|
997
|
-
workflow_id: r.workflow_id,
|
|
998
|
-
workflow_file: r.workflow_file,
|
|
999
|
-
created_at: r.created_at,
|
|
1000
|
-
current_node: r.current_node,
|
|
1001
|
-
completed_nodes: r.completed_nodes,
|
|
1002
|
-
})),
|
|
1003
|
-
}, null, 2));
|
|
1004
|
-
}
|
|
1005
|
-
else {
|
|
1006
|
-
if (runs.length === 0) {
|
|
1007
|
-
console.log('\u2139\uFE0F No workflow runs found.');
|
|
1008
|
-
return;
|
|
1009
|
-
}
|
|
1010
|
-
console.log(`\uD83D\uDCCB Workflow runs (${runs.length})`);
|
|
1011
|
-
for (const r of runs) {
|
|
1012
|
-
const node = r.current_node || '(not started)';
|
|
1013
|
-
const completed = r.completed_nodes?.length || 0;
|
|
1014
|
-
console.log(` ${r.id}`);
|
|
1015
|
-
console.log(` \uD83D\uDCC1 ${r.workflow_file} | node: ${node} | completed: ${completed}`);
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
break;
|
|
1019
|
-
}
|
|
1020
|
-
case 'auto': {
|
|
1021
|
-
const positional = runArgs.filter(a => !a.startsWith('--'));
|
|
1022
|
-
const execId = positional[0];
|
|
1023
|
-
if (!execId) {
|
|
1024
|
-
const msg = 'Usage: workflow run auto <exec-id>';
|
|
1025
|
-
if (jsonMode) {
|
|
1026
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
1027
|
-
}
|
|
1028
|
-
else {
|
|
1029
|
-
console.error(`❌ ${msg}`);
|
|
1030
|
-
}
|
|
1031
|
-
process.exit(1);
|
|
1032
|
-
}
|
|
1033
|
-
try {
|
|
1034
|
-
const result = await mgr.runAuto(execId);
|
|
1035
|
-
reportAutoResult(result, jsonMode);
|
|
1036
|
-
if (result.outcome === 'failed')
|
|
1037
|
-
process.exit(1);
|
|
1038
|
-
}
|
|
1039
|
-
catch (e) {
|
|
1040
|
-
const msg = e.message;
|
|
1041
|
-
if (jsonMode) {
|
|
1042
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
1043
|
-
}
|
|
1044
|
-
else {
|
|
1045
|
-
console.error(`❌ ${msg}`);
|
|
1046
|
-
}
|
|
1047
|
-
process.exit(1);
|
|
1048
|
-
}
|
|
1049
|
-
break;
|
|
1050
|
-
}
|
|
1051
|
-
default: {
|
|
1052
|
-
const msg = `Unknown run subcommand: ${runSub || '(none)'}\nUsage: workflow run <start|state|node|auto|reset|retry|list> [args...]`;
|
|
1053
|
-
if (jsonMode) {
|
|
1054
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
1055
|
-
}
|
|
1056
|
-
else {
|
|
1057
|
-
console.error(`\u274C ${msg}`);
|
|
1058
|
-
}
|
|
1059
|
-
process.exit(1);
|
|
1060
|
-
}
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
function reportAutoResult(result, jsonMode) {
|
|
1064
|
-
const { run, outcome, reason, executed } = result;
|
|
1065
|
-
const status = run.status;
|
|
1066
|
-
if (jsonMode) {
|
|
1067
|
-
console.log(JSON.stringify({
|
|
1068
|
-
success: outcome !== 'failed',
|
|
1069
|
-
exec_id: run.id,
|
|
1070
|
-
status,
|
|
1071
|
-
outcome,
|
|
1072
|
-
current_node: run.current_node,
|
|
1073
|
-
completed_nodes: run.completed_nodes,
|
|
1074
|
-
executed,
|
|
1075
|
-
...(reason ? { reason } : {}),
|
|
1076
|
-
}, null, 2));
|
|
1077
|
-
return;
|
|
1078
|
-
}
|
|
1079
|
-
const icon = outcome === 'failed' ? '❌' : outcome === 'paused' ? '⏸️' : '✅';
|
|
1080
|
-
console.log(`${icon} Auto run: ${run.id}`);
|
|
1081
|
-
if (executed.length > 0) {
|
|
1082
|
-
console.log(` Executed: ${executed.join(' → ')}`);
|
|
1083
|
-
}
|
|
1084
|
-
console.log(` Outcome: ${outcome}${reason ? ` (${reason})` : ''}`);
|
|
1085
|
-
console.log(` Current: ${run.current_node || '(none)'}`);
|
|
1086
|
-
console.log(` Status: ${status}`);
|
|
1087
|
-
}
|
|
1088
|
-
function findNextNodeForCAL(currentNode, executedNodeId, nodes) {
|
|
1089
|
-
const executedNode = nodes[executedNodeId];
|
|
1090
|
-
if (!executedNode)
|
|
1091
|
-
return null;
|
|
1092
|
-
switch (executedNode.type) {
|
|
1093
|
-
case 'agent_task':
|
|
1094
|
-
case 'skill_task':
|
|
1095
|
-
case 'shell_task':
|
|
1096
|
-
case 'expression':
|
|
1097
|
-
return typeof executedNode.next === 'string' ? executedNode.next : null;
|
|
1098
|
-
case 'branch':
|
|
1099
|
-
return (currentNode && currentNode !== executedNodeId) ? currentNode : null;
|
|
1100
|
-
case 'end':
|
|
1101
|
-
case 'parallel':
|
|
1102
|
-
case 'join':
|
|
1103
|
-
return null;
|
|
1104
|
-
default:
|
|
1105
|
-
return null;
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
function getFlowChain(nodes) {
|
|
1109
|
-
const chain = [];
|
|
1110
|
-
const nodeIds = Object.keys(nodes);
|
|
1111
|
-
if (nodeIds.length === 0)
|
|
1112
|
-
return chain;
|
|
1113
|
-
let current = nodeIds[0];
|
|
1114
|
-
const visited = new Set();
|
|
1115
|
-
while (current && !visited.has(current)) {
|
|
1116
|
-
visited.add(current);
|
|
1117
|
-
chain.push(current);
|
|
1118
|
-
const node = nodes[current];
|
|
1119
|
-
if (!node || node.type === 'end')
|
|
1120
|
-
break;
|
|
1121
|
-
if (typeof node.next === 'string') {
|
|
1122
|
-
current = node.next;
|
|
1123
|
-
}
|
|
1124
|
-
else {
|
|
1125
|
-
chain.push('...');
|
|
1126
|
-
break;
|
|
1127
|
-
}
|
|
1128
|
-
}
|
|
1129
|
-
return chain;
|
|
1130
|
-
}
|
|
1131
|
-
function findNextNode(run, engine) {
|
|
1132
|
-
try {
|
|
1133
|
-
const { paths } = getWorkflowConfig();
|
|
1134
|
-
const all = loadAllWorkflows(engine, paths);
|
|
1135
|
-
const wf = all.find(w => w.id === run.workflow_id);
|
|
1136
|
-
if (!wf)
|
|
1137
|
-
return null;
|
|
1138
|
-
const currentNode = run.current_node;
|
|
1139
|
-
if (!currentNode) {
|
|
1140
|
-
const nodeIds = Object.keys(wf.spec.nodes);
|
|
1141
|
-
return nodeIds[0] ?? null;
|
|
1142
|
-
}
|
|
1143
|
-
const node = wf.spec.nodes[currentNode];
|
|
1144
|
-
if (!node)
|
|
1145
|
-
return null;
|
|
1146
|
-
if (typeof node.next === 'string')
|
|
1147
|
-
return node.next;
|
|
1148
|
-
return null;
|
|
1149
|
-
}
|
|
1150
|
-
catch {
|
|
1151
|
-
return null;
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1154
|
-
function findNodeAgent(run, nodeId, engine) {
|
|
1155
|
-
try {
|
|
1156
|
-
const { paths } = getWorkflowConfig();
|
|
1157
|
-
const all = loadAllWorkflows(engine, paths);
|
|
1158
|
-
const wf = all.find(w => w.id === run.workflow_id);
|
|
1159
|
-
if (!wf)
|
|
1160
|
-
return null;
|
|
1161
|
-
const node = wf.spec.nodes[nodeId];
|
|
1162
|
-
return node?.agent ?? null;
|
|
1163
|
-
}
|
|
1164
|
-
catch {
|
|
1165
|
-
return null;
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
async function main(inputArgs) {
|
|
1169
|
-
const args = inputArgs ?? process.argv.slice(2);
|
|
1170
|
-
const { positional, options } = parseArgs(args);
|
|
1171
|
-
const [subcommand, ...rest] = positional;
|
|
1172
|
-
if (options.version) {
|
|
1173
|
-
console.log(PKG_VERSION);
|
|
1174
|
-
return;
|
|
1175
|
-
}
|
|
1176
|
-
if (options.help || subcommand === 'help' || !subcommand) {
|
|
1177
|
-
printUsage();
|
|
1178
|
-
return;
|
|
1179
|
-
}
|
|
1180
|
-
const jsonMode = !!options.json;
|
|
1181
|
-
const engine = new engine_1.WorkflowEngine();
|
|
1182
|
-
switch (subcommand) {
|
|
1183
|
-
case 'list':
|
|
1184
|
-
case 'ls':
|
|
1185
|
-
cmdList(engine, jsonMode);
|
|
1186
|
-
break;
|
|
1187
|
-
case 'show': {
|
|
1188
|
-
const name = rest[0];
|
|
1189
|
-
if (!name) {
|
|
1190
|
-
console.error('Error: workflow name is required\nUsage: workflow show <id|file> [--mermaid]');
|
|
1191
|
-
process.exit(1);
|
|
1192
|
-
}
|
|
1193
|
-
cmdShow(engine, name, jsonMode, !!options.mermaid);
|
|
1194
|
-
break;
|
|
1195
|
-
}
|
|
1196
|
-
case 'validate': {
|
|
1197
|
-
const targetFile = rest[0];
|
|
1198
|
-
cmdValidate(engine, targetFile, jsonMode);
|
|
1199
|
-
break;
|
|
1200
|
-
}
|
|
1201
|
-
case 'run': {
|
|
1202
|
-
await cmdRun(rest, args, jsonMode, engine);
|
|
1203
|
-
break;
|
|
1204
|
-
}
|
|
1205
|
-
case 'repair': {
|
|
1206
|
-
cmdRepair(rest, jsonMode, !!options.apply, !!options['dry-run']);
|
|
1207
|
-
break;
|
|
1208
|
-
}
|
|
1209
|
-
case 'usage':
|
|
1210
|
-
showDetailedUsage();
|
|
1211
|
-
break;
|
|
1212
|
-
default:
|
|
1213
|
-
console.error(`Unknown subcommand: ${subcommand}`);
|
|
1214
|
-
printUsage();
|
|
1215
|
-
process.exit(1);
|
|
1216
|
-
}
|
|
1217
|
-
}
|
|
1218
|
-
if (require.main === module) {
|
|
1219
|
-
loadTracer().then(tracer => {
|
|
1220
|
-
const opts = tracer ? { skillVersion: tracer.getOwnSkillVersion(__dirname) } : {};
|
|
1221
|
-
return tracer ? tracer.run('crewx/workflow', () => main(), opts) : main();
|
|
1222
|
-
}).catch((e) => {
|
|
1223
|
-
console.error(e.message);
|
|
1224
|
-
process.exit(1);
|
|
1225
|
-
});
|
|
1226
|
-
}
|
|
1227
|
-
//# sourceMappingURL=cli.js.map
|
|
48
|
+
`)}function ne(){const e=y.join(__dirname,"..","SKILL.md");if(S.existsSync(e)){process.stdout.write(S.readFileSync(e,"utf-8"))}else{U()}}function re(e){const o=[];let t=1;for(const[n,s]of Object.entries(e)){if(!s||typeof s!=="object")continue;const l=s.type??"unknown";let a=`${n} (${l}`;if(s.agent)a+=` \u2192 ${s.agent}`;a+=")";if(l==="parallel"&&Array.isArray(s.branches)){a+=` \u2192 [${s.branches.join(", ")}]`;if(s.join)a+=` \u2192 ${s.join}`}else if(l==="branch"&&s.branches&&typeof s.branches==="object"&&!Array.isArray(s.branches)){const i=Object.entries(s.branches).map(([k,g])=>`${k}: ${g}`).join(" / ");a+=` \u2192 ${i}`}else if(s.next){a+=` \u2192 ${s.next}`}o.push(` ${t}. ${a}`);t++}return o}function ie(e,o){const{paths:t}=O();console.log((0,D.formatWorkflowList)({paths:t,workflows:N(e,t),jsonMode:o}))}function le(e,o,t,n){const{paths:s}=O();const l=N(e,s);let a;try{const m=A(o,s,e,l);if(m.workflowId){a=l.find(c=>c.id===m.workflowId)}else{const c=l.filter(f=>f.filePath===m.filePath);if(c.length===1){a=c[0]}else if(c.length>1){throw new Error(`File "${y.basename(m.filePath)}" contains ${c.length} workflows.
|
|
49
|
+
Specify a workflow ID:
|
|
50
|
+
`+c.map(f=>` workflow show ${f.id}`).join("\n"))}}}catch(m){const c=m.message;if(t){console.log(JSON.stringify({success:false,error:c}))}else{console.error(`\u274C ${c}`)}process.exit(1)}if(!a){const m=`Workflow "${o}" not found in: ${s.join(", ")}`;if(t){console.log(JSON.stringify({success:false,error:m}))}else{console.error(`\u274C ${m}`)}process.exit(1)}if(n){console.log(e.toMermaid(a.spec));return}const{spec:i}=a;const k=i.metadata??{};const g=i.nodes??{};const d=i.state?Object.keys(i.state):[];if(t){console.log(JSON.stringify({success:true,workflow:{id:a.id,name:k.name??a.id,description:k.description??"",timeout:i.timeout??null,max_iterations:i.max_iterations??null,state:d,node_count:Object.keys(g).length,nodes:Object.entries(g).map(([m,c])=>({id:m,type:c.type,agent:c.agent??null,next:c.next??null,branches:c.branches??null,join:c.join??null}))}},null,2))}else{const m=k.description?` \u2014 ${k.description}`:"";console.log(`\u{1F4C4} ${a.id}${m}`);console.log(` \u{1F4C1} ${a.filePath}`);const c=[];if(i.timeout)c.push(`timeout: ${i.timeout}s`);if(i.max_iterations)c.push(`max_iterations: ${i.max_iterations}`);if(c.length>0)console.log(` ${c.join(" | ")}`);if(d.length>0){console.log(`
|
|
51
|
+
State: ${d.join(", ")}`)}const f=re(g);if(f.length>0){console.log(`
|
|
52
|
+
Flow:`);f.forEach(r=>console.log(r))}const u={commandId:"workflow.show",params:{name:o},resultCount:Object.keys(g).length};const w=(0,$.generateNextActions)(u);const p=(0,$.formatActions)(w);if(p)console.log(p)}}function ce(e,o,t){const{paths:n}=O();let s;let l;if(o){try{const g=A(o,n,e);if(!S.existsSync(g.filePath)){const d=`File not found: ${g.filePath}`;if(t){console.log(JSON.stringify({success:false,error:d,workflows:[]}))}else{console.error(`\u274C ${d}`)}process.exit(2)}s=[g.filePath];if(!o.endsWith(".yaml")&&!o.endsWith(".yml")){l=o}}catch(g){const d=g.message;if(t){console.log(JSON.stringify({success:false,error:d,workflows:[]}))}else{console.error(`\u274C ${d}`)}process.exit(2)}}else{s=[];for(const g of n){const d=y.resolve(process.cwd(),g);if(!S.existsSync(d)||!S.statSync(d).isDirectory())continue;const m=S.readdirSync(d).filter(c=>c.endsWith(".yaml")||c.endsWith(".yml")).map(c=>y.join(d,c));s.push(...m)}}if(s.length===0){const g="No workflow YAML files found";if(t){console.log(JSON.stringify({success:true,message:g,workflows:[]}))}else{console.log(`\u2139\uFE0F ${g}`)}return}let a=false;const i=[];let k;for(const g of s){const{success:d,results:m,error:c}=e.validateFile(g);if(c){a=true;if(t){i.push({file:g,results:[{name:y.basename(g),valid:false,errors:[{path:g,message:c}]}]})}else{console.error(`\u274C ${g}: ${c}
|
|
53
|
+
`)}continue}if(!d)a=true;if(t){i.push({file:g,results:m})}else{for(const f of m){if(f.valid){if(!k)k=f.name;console.log(`\u2705 workflow "${f.name}" \u2014 validation passed`)}else{console.log(`\u274C workflow "${f.name}" \u2014 validation failed`);for(const u of f.errors){console.log(` - ${u.path}: ${u.message}`)}}console.log()}}}if(t){const g=i.flatMap(d=>d.results);console.log(JSON.stringify({success:!a,workflows:g},null,2))}if(!t&&!a){const g=l??k;if(g){const d={commandId:"workflow.validate",params:{name:g},resultCount:1};const m=(0,$.generateNextActions)(d);const c=(0,$.formatActions)(m);if(c)console.log(c)}}if(a)process.exit(1)}function ae(e){return{task_id:e.id,kind:e.kind,before:{status:e.before.status,error:e.before.error,completed_at:e.before.completedAt,duration_ms:e.before.durationMs},after:{status:e.after.status,error:e.after.error,completed_at:e.after.completedAt,duration_ms:e.after.durationMs},reason:e.reason}}function fe(e){return{instance_id:e.instanceId,before:e.before,after:e.after}}function T(e,o,t,n=[]){const s=e.parentChanges.map(ae);const l=e.auditSnapshotChanges.map(fe);const a=[...e.errors,...n];if(t){console.log(JSON.stringify({success:true,exec_id:e.runId,mode:o,dry_run:o==="dry-run",applied:o==="apply",run_status:e.runStatus,changes:s,audit_card_changes:l,...a.length>0?{errors:a}:{}},null,2));return}console.log(`\u{1F527} Workflow repair (${o}): ${e.runId}`);console.log(` Run status: ${e.runStatus}`);if(s.length===0&&l.length===0){console.log(" No parent lifecycle changes needed.")}else{for(const i of s){console.log(` ${o==="apply"?"Updated":"Would update"} ${i.task_id} (${i.kind}): ${i.before.status} \u2192 ${i.after.status}`);console.log(` Reason: ${i.reason}`)}for(const i of l){console.log(` ${o==="apply"?"Updated":"Would update"} audit card ${i.instance_id}: ${i.before.phase??"(unknown)"} \u2192 ${i.after.phase}`)}}for(const i of a){console.error(` Warning: ${i}`)}}function Y(e,o){if(e.id!==o){throw new Error(`Execution file ID mismatch: requested ${o}, found ${e.id||"(missing)"}`)}if(!e.workflow_id||!e.workflow_file||!e.created_at||!e.updated_at||typeof e.current_node!=="string"||!Array.isArray(e.completed_nodes)||!e.state||typeof e.state!=="object"||!e.initial_state||typeof e.initial_state!=="object"){throw new Error(`Run "${o}" is incomplete and cannot be repaired`)}}function ue(e,o,t,n){const s=e[0];if(!s){const r="Usage: workflow repair <run-id> [--dry-run|--apply]";if(o)console.log(JSON.stringify({success:false,error:r}));else console.error(`\u274C ${r}`);process.exit(1)}if(t&&n){const r="Choose either --dry-run or --apply, not both";if(o)console.log(JSON.stringify({success:false,error:r}));else console.error(`\u274C ${r}`);process.exit(1)}const l=F();const a=(0,W.hashWorkspaceId)(l);const i=new B.TaskRepository;const k=new I.RunManager(void 0,l);const g=new V.WorkflowEngine;let d;let m;const c=r=>G(r,g);const f=()=>{d??(d=new W.UiInstanceStore(i.resolveDbPath()));return d};const u=()=>{m??(m=(0,W.openUiInstanceReadStore)(i.resolveDbPath()));return m};const w=(0,R.createWorkflowPersistenceProjection)({taskRepo:i,workspaceId:a,resolveWorkflowSpec:c,getUiInstanceStore:f});const p={taskRepo:i,workspaceId:a,resolveWorkflowSpec:c,getUiInstanceStore:u};try{const r=k.readRun(s);if(!r)throw new Error(`Execution not found: ${s}`);Y(r,s);if(!(0,K.deriveWorkflowParentLifecycle)(r)){throw new Error(`Run "${s}" has an unsupported or insufficient persisted status: ${r.status??"pending"}`)}if(!t){T((0,R.previewWorkflowPersistenceProjection)(p,r),"dry-run",o);return}const h=k.withRunLock(s,_=>{Y(_,s);if(!(0,K.deriveWorkflowParentLifecycle)(_)){throw new Error(`Run "${s}" has an unsupported or insufficient persisted status: ${_.status??"pending"}`)}const x=(0,R.previewWorkflowPersistenceProjection)(p,_);const b=w(_);return{preview:x,projection:b}});T(h.preview,"apply",o,h.projection.errors)}catch(r){const h=r instanceof Error?r.message:String(r);if(o)console.log(JSON.stringify({success:false,error:h}));else console.error(`\u274C ${h}`);process.exit(1)}finally{d?.close();m?.close()}}async function de(e,o,t,n){const s=e[0];const l=e.slice(1);const a=F();const i=new B.TaskRepository;let k;const g=(0,R.createWorkflowPersistenceProjection)({taskRepo:i,workspaceId:(0,W.hashWorkspaceId)(a),resolveWorkflowSpec:c=>G(c,n),getUiInstanceStore:()=>{k??(k=new W.UiInstanceStore(i.resolveDbPath()));return k}});const d=new I.RunManager(void 0,a,{onRunPersisted:g});const{paths:m}=O();switch(s){case"start":{const c=l.filter((r,h)=>!r.startsWith("--")&&l[h-1]!=="--set");const f=c[0];if(!f){const r="Usage: workflow run start <id|file> [--set key=value ...]";if(t){console.log(JSON.stringify({success:false,error:r}))}else{console.error(`\u274C ${r}`)}process.exit(1)}let u;try{u=A(f,m,n)}catch(r){const h=r.message;if(t){console.log(JSON.stringify({success:false,error:h}))}else{console.error(`\u274C ${h}`)}process.exit(1)}const w=(0,I.parseSetArgs)(o);const p=o.includes("--auto");try{const r=d.start(u.filePath,u.workflowId??c[1],Object.keys(w).length>0?w:void 0);if(p){if(!t){console.log(`\u2705 Created execution: ${r.id}`);console.log(` Workflow: ${r.workflow_id} (${u.filePath})`)}const h=await d.runAuto(r.id);q(h,t);break}if(t){console.log(JSON.stringify({success:true,exec_id:r.id,run:r},null,2))}else{console.log(`\u2705 Created execution: ${r.id}`);console.log(` Workflow: ${r.workflow_id} (${u.filePath})`);console.log(` State keys: ${Object.keys(r.state).join(", ")||"(none)"}`);console.log(` File: ${y.join(".crewx/workflow-runs",r.id+".json")}`);let h=null;try{const P=N(n,m);const C=P.find(v=>v.id===r.workflow_id);if(C){const v=H(C.spec.nodes);if(v.length>0){console.log(` Flow: ${v.join(" \u2192 ")}`)}const j=Object.keys(C.spec.nodes);h=j[0]??null}}catch{}const _={commandId:"workflow.run.start",params:{name:r.workflow_id},resultIds:h?[r.id,h]:[r.id],resultCount:1};const x=(0,$.generateNextActions)(_);const b=(0,$.formatActions)(x);if(b)console.log(b)}}catch(r){const h=r.message;if(t){console.log(JSON.stringify({success:false,error:h}))}else{console.error(`\u274C ${h}`)}process.exit(1)}break}case"state":{const c=l.filter((r,h)=>!r.startsWith("--")&&l[h-1]!=="--set");const f=c[0];if(!f){const r="Usage: workflow run state <exec-id> [--set key=value ...]";if(t){console.log(JSON.stringify({success:false,error:r}))}else{console.error(`\u274C ${r}`)}process.exit(1)}const u=d.loadRun(f);if(!u){const r=`Execution not found: ${f}`;if(t){console.log(JSON.stringify({success:false,error:r}))}else{console.error(`\u274C ${r}`)}process.exit(1)}const w=(0,I.parseSetArgs)(o);const p=Object.entries(w).filter(([,r])=>r==="-").map(([r])=>r);if(p.length>0&&!process.stdin.isTTY){const r=await(0,I.readStdin)();let h;try{h=JSON.parse(r)}catch{h=r}for(const _ of p){w[_]=h}}if(Object.keys(w).length>0){Object.assign(u.state,w);d.saveRun(u)}if(t){console.log(JSON.stringify({success:true,exec_id:f,state:u.state},null,2))}else{if(Object.keys(w).length>0){console.log(`\u2705 Updated state for ${f}`)}console.log(JSON.stringify(u.state,null,2));const r=we(u,n);const h={commandId:"workflow.run.state",params:{execId:f},resultIds:r?[r]:[],resultCount:Object.keys(u.state).length};const _=(0,$.generateNextActions)(h);const x=(0,$.formatActions)(_);if(x)console.log(x)}break}case"node":{const c=l.filter(p=>!p.startsWith("--"));const f=c[0];const u=c[1];if(!f||!u){const p="Usage: workflow run node <exec-id> <node-id> [--no-exec]";if(t){console.log(JSON.stringify({success:false,error:p}))}else{console.error(`\u274C ${p}`)}process.exit(1)}const w=o.includes("--no-exec");try{const p=w?d.moveNode(f,u):await d.executeNode(f,u);const r=te(p,u,d.getNodeType(p.workflow_file,p.workflow_id,u),w);if(t){console.log(JSON.stringify({success:true,exec_id:f,current_node:p.current_node,completed_nodes:p.completed_nodes,status:p.status},null,2))}else{if(p.status==="completed"){console.log(`Workflow completed.`)}else{console.log(`\u2705 Node updated: ${u}${w?" (move only)":""}`);if(p.completed_nodes.length>0){console.log(` Completed: ${p.completed_nodes.join(" \u2192 ")}`)}console.log(` Current: ${p.current_node}`);if(r){console.log(` Status: ${r}`)}}let h=null;try{const{paths:P}=O();const C=N(n,P);const v=C.find(j=>j.id===p.workflow_id);if(v){h=X(p.current_node,u,v.spec.nodes)}}catch{}const _={commandId:"workflow.run.node",params:{execId:f,nodeId:u},resultIds:h?[h]:[],resultCount:1};const x=(0,$.generateNextActions)(_);const b=(0,$.formatActions)(x);if(b)console.log(b)}}catch(p){const r=p.message;if(t){console.log(JSON.stringify({success:false,error:r}))}else{console.error(`\u274C ${r}`);const h={commandId:"workflow.run.node",params:{execId:f,nodeId:u},resultIds:[],resultCount:0};const _=(0,$.generateNextActions)(h);const x=(0,$.formatActions)(_);if(x)console.log(x)}process.exit(1)}break}case"reset":{const c=l.filter(u=>!u.startsWith("--"));const f=c[0];if(!f){const u="Usage: workflow run reset <exec-id>";if(t){console.log(JSON.stringify({success:false,error:u}))}else{console.error(`\u274C ${u}`)}process.exit(1)}try{const u=d.reset(f);if(t){console.log(JSON.stringify({success:true,exec_id:f,state:u.state},null,2))}else{console.log(`\u2705 Reset execution: ${f}`);console.log(` State restored to initial values`)}}catch(u){const w=u.message;if(t){console.log(JSON.stringify({success:false,error:w}))}else{console.error(`\u274C ${w}`)}process.exit(1)}break}case"retry":{const c=l.filter(w=>!w.startsWith("--"));const f=c[0];if(!f){const w="Usage: workflow run retry <exec-id> [--no-run]";if(t){console.log(JSON.stringify({success:false,error:w}))}else{console.error(`\u274C ${w}`)}process.exit(1)}const u=o.includes("--no-run");try{const w=d.retry(f,{autoRun:!u});if(u){if(t){console.log(JSON.stringify({success:true,exec_id:w.id,current_node:w.current_node,retry_count:w.retry_count,status:w.status},null,2))}else{console.log(`\u2705 Retry transition: ${f}`);console.log(` Resume node: ${w.current_node}`);console.log(` Retry count: ${w.retry_count}`);console.log(` (state transition only; run "workflow run auto ${f}" to execute \u2014 re-runs the node, safe only if idempotent)`)}}else{const p=await d.runAuto(w.id);q(p,t);if(p.outcome==="failed")process.exit(1)}}catch(w){const p=w.message;if(t){console.log(JSON.stringify({success:false,error:p}))}else{console.error(`\u274C ${p}`)}process.exit(1)}break}case"list":{const c=d.listRuns();if(t){console.log(JSON.stringify({success:true,runs:c.map(f=>({id:f.id,workflow_id:f.workflow_id,workflow_file:f.workflow_file,created_at:f.created_at,current_node:f.current_node,completed_nodes:f.completed_nodes}))},null,2))}else{if(c.length===0){console.log("\u2139\uFE0F No workflow runs found.");return}console.log(`\u{1F4CB} Workflow runs (${c.length})`);for(const f of c){const u=f.current_node||"(not started)";const w=f.completed_nodes?.length||0;console.log(` ${f.id}`);console.log(` \u{1F4C1} ${f.workflow_file} | node: ${u} | completed: ${w}`)}}break}case"auto":{const c=l.filter(u=>!u.startsWith("--"));const f=c[0];if(!f){const u="Usage: workflow run auto <exec-id>";if(t){console.log(JSON.stringify({success:false,error:u}))}else{console.error(`\u274C ${u}`)}process.exit(1)}try{const u=await d.runAuto(f);q(u,t);if(u.outcome==="failed")process.exit(1)}catch(u){const w=u.message;if(t){console.log(JSON.stringify({success:false,error:w}))}else{console.error(`\u274C ${w}`)}process.exit(1)}break}default:{const c=`Unknown run subcommand: ${s||"(none)"}
|
|
54
|
+
Usage: workflow run <start|state|node|auto|reset|retry|list> [args...]`;if(t){console.log(JSON.stringify({success:false,error:c}))}else{console.error(`\u274C ${c}`)}process.exit(1)}}}function q(e,o){const{run:t,outcome:n,reason:s,executed:l}=e;const a=t.status;if(o){console.log(JSON.stringify({success:n!=="failed",exec_id:t.id,status:a,outcome:n,current_node:t.current_node,completed_nodes:t.completed_nodes,executed:l,...s?{reason:s}:{}},null,2));return}const i=n==="failed"?"\u274C":n==="paused"?"\u23F8\uFE0F":"\u2705";console.log(`${i} Auto run: ${t.id}`);if(l.length>0){console.log(` Executed: ${l.join(" \u2192 ")}`)}console.log(` Outcome: ${n}${s?` (${s})`:""}`);console.log(` Current: ${t.current_node||"(none)"}`);console.log(` Status: ${a}`)}function X(e,o,t){const n=t[o];if(!n)return null;switch(n.type){case"agent_task":case"skill_task":case"shell_task":case"expression":return typeof n.next==="string"?n.next:null;case"branch":return e&&e!==o?e:null;case"end":case"parallel":case"join":return null;default:return null}}function H(e){const o=[];const t=Object.keys(e);if(t.length===0)return o;let n=t[0];const s=new Set;while(n&&!s.has(n)){s.add(n);o.push(n);const l=e[n];if(!l||l.type==="end")break;if(typeof l.next==="string"){n=l.next}else{o.push("...");break}}return o}function we(e,o){try{const{paths:t}=O();const n=N(o,t);const s=n.find(i=>i.id===e.workflow_id);if(!s)return null;const l=e.current_node;if(!l){const i=Object.keys(s.spec.nodes);return i[0]??null}const a=s.spec.nodes[l];if(!a)return null;if(typeof a.next==="string")return a.next;return null}catch{return null}}function pe(e,o,t){try{const{paths:n}=O();const s=N(t,n);const l=s.find(i=>i.id===e.workflow_id);if(!l)return null;const a=l.spec.nodes[o];return a?.agent??null}catch{return null}}async function L(e){const o=e??process.argv.slice(2);const{positional:t,options:n}=oe(o);const[s,...l]=t;if(n.version){console.log(E);return}if(n.help||s==="help"||!s){U();return}const a=!!n.json;const i=new V.WorkflowEngine;switch(s){case"list":case"ls":ie(i,a);break;case"show":{const k=l[0];if(!k){console.error("Error: workflow name is required\nUsage: workflow show <id|file> [--mermaid]");process.exit(1)}le(i,k,a,!!n.mermaid);break}case"validate":{const k=l[0];ce(i,k,a);break}case"run":{await de(l,o,a,i);break}case"repair":{ue(l,a,!!n.apply,!!n["dry-run"]);break}case"usage":ne();break;default:console.error(`Unknown subcommand: ${s}`);U();process.exit(1)}}if(require.main===module){M().then(e=>{const o=e?{skillVersion:e.getOwnSkillVersion(__dirname)}:{};return e?e.run("crewx/workflow",()=>L(),o):L()}).catch(e=>{console.error(e.message);process.exit(1)})}
|