@crewx/workflow 0.3.22-rc.21 → 0.3.22-rc.210
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/SKILL.md +4 -4
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +21 -1012
- package/dist/src/card-state.d.ts +20 -0
- package/dist/src/card-state.js +1 -0
- package/dist/src/engine.d.ts +10 -3
- package/dist/src/engine.js +1 -348
- package/dist/src/failure.d.ts +41 -0
- package/dist/src/failure.js +1 -0
- package/dist/src/file-lock.d.ts +1 -1
- package/dist/src/file-lock.js +1 -97
- package/dist/src/list-view.d.ts +22 -0
- package/dist/src/list-view.js +3 -0
- package/dist/src/mermaid.d.ts +1 -1
- package/dist/src/mermaid.js +2 -133
- package/dist/src/parent-task-sync.d.ts +139 -0
- package/dist/src/parent-task-sync.js +1 -0
- package/dist/src/retry-policy.d.ts +38 -0
- package/dist/src/retry-policy.js +1 -0
- package/dist/src/run-manager.d.ts +37 -3
- package/dist/src/run-manager.js +4 -1228
- package/dist/src/types.d.ts +37 -4
- package/dist/src/types.js +1 -3
- package/dist/src/utils/output-format.d.ts +1 -1
- package/dist/src/utils/output-format.js +12 -157
- 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 +48 -0
- package/dist/src/workflow-persistence.js +1 -0
- package/dist/workflow-schema.json +25 -4
- package/package.json +23 -4
- package/workflow-schema.json +25 -4
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/src/engine.d.ts.map +0 -1
- package/dist/src/engine.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/mermaid.d.ts.map +0 -1
- package/dist/src/mermaid.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/cli.js
CHANGED
|
@@ -1,190 +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 path = __importStar(require("path"));
|
|
43
|
-
const child_process_1 = require("child_process");
|
|
44
|
-
const yaml = __importStar(require("js-yaml"));
|
|
45
|
-
const engine_1 = require("./src/engine");
|
|
46
|
-
const run_manager_1 = require("./src/run-manager");
|
|
47
|
-
const knowledge_core_1 = require("@crewx/knowledge-core");
|
|
48
|
-
let PKG_VERSION = 'unknown';
|
|
49
|
-
try {
|
|
50
|
-
const pkgPath = fs.existsSync(path.join(__dirname, 'package.json'))
|
|
51
|
-
? path.join(__dirname, 'package.json')
|
|
52
|
-
: path.join(__dirname, '..', 'package.json');
|
|
53
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
54
|
-
if (pkg?.version)
|
|
55
|
-
PKG_VERSION = String(pkg.version);
|
|
56
|
-
}
|
|
57
|
-
catch { }
|
|
58
|
-
async function loadTracer() {
|
|
59
|
-
try {
|
|
60
|
-
return await Promise.resolve().then(() => __importStar(require('@crewx/shared/skill-tracer')));
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
const BOOLEAN_FLAGS = new Set(['help', 'json', 'mermaid', 'version', 'no-exec', 'auto']);
|
|
67
|
-
function parseArgs(args) {
|
|
68
|
-
const result = { positional: [], options: {} };
|
|
69
|
-
for (let i = 0; i < args.length; i++) {
|
|
70
|
-
const arg = args[i];
|
|
71
|
-
if (arg === '-h') {
|
|
72
|
-
result.options['help'] = true;
|
|
73
|
-
}
|
|
74
|
-
else if (arg.startsWith('--')) {
|
|
75
|
-
const eqIdx = arg.indexOf('=');
|
|
76
|
-
if (eqIdx !== -1) {
|
|
77
|
-
result.options[arg.slice(2, eqIdx)] = arg.slice(eqIdx + 1);
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
const key = arg.slice(2);
|
|
81
|
-
if (BOOLEAN_FLAGS.has(key)) {
|
|
82
|
-
result.options[key] = true;
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
const nextArg = args[i + 1];
|
|
86
|
-
if (nextArg !== undefined && !nextArg.startsWith('--') && nextArg !== '-h') {
|
|
87
|
-
result.options[key] = nextArg;
|
|
88
|
-
i++;
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
result.options[key] = true;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
result.positional.push(arg);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
return result;
|
|
101
|
-
}
|
|
102
|
-
function getProjectRoot() {
|
|
103
|
-
try {
|
|
104
|
-
return (0, child_process_1.execSync)('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();
|
|
105
|
-
}
|
|
106
|
-
catch {
|
|
107
|
-
return process.cwd();
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
function getWorkflowConfig() {
|
|
111
|
-
try {
|
|
112
|
-
const configPath = process.env.CREWX_CONFIG ?? path.join(getProjectRoot(), 'crewx.yaml');
|
|
113
|
-
const raw = fs.readFileSync(configPath, 'utf8');
|
|
114
|
-
const config = yaml.load(raw);
|
|
115
|
-
const settings = config?.settings;
|
|
116
|
-
const wf = settings?.workflow ?? {};
|
|
117
|
-
return { paths: wf.paths ?? ['workflows'] };
|
|
118
|
-
}
|
|
119
|
-
catch {
|
|
120
|
-
return { paths: ['workflows'] };
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
function validateSlugInput(nameOrPath) {
|
|
124
|
-
if (!nameOrPath.endsWith('.yaml') && !nameOrPath.endsWith('.yml')) {
|
|
125
|
-
if (/[/\\]|\.\./.test(nameOrPath)) {
|
|
126
|
-
throw new Error(`Invalid workflow name: "${nameOrPath}"`);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
function resolveWorkflow(nameOrPath, paths, engine, preloaded) {
|
|
131
|
-
if (nameOrPath.endsWith('.yaml') || nameOrPath.endsWith('.yml')) {
|
|
132
|
-
const resolved = path.resolve(process.cwd(), nameOrPath);
|
|
133
|
-
if (!fs.existsSync(resolved)) {
|
|
134
|
-
throw new Error(`Workflow file not found: ${resolved}`);
|
|
135
|
-
}
|
|
136
|
-
return { filePath: resolved, resolvedVia: 'path' };
|
|
137
|
-
}
|
|
138
|
-
validateSlugInput(nameOrPath);
|
|
139
|
-
const allWorkflows = preloaded ?? loadAllWorkflows(engine, paths);
|
|
140
|
-
const byId = allWorkflows.find(w => w.id === nameOrPath);
|
|
141
|
-
if (byId) {
|
|
142
|
-
const dupes = allWorkflows.filter(w => w.id === nameOrPath && w.filePath !== byId.filePath);
|
|
143
|
-
if (dupes.length > 0) {
|
|
144
|
-
throw new Error(`Duplicate workflow ID "${nameOrPath}" found in multiple files:\n` +
|
|
145
|
-
` - ${byId.filePath}\n` +
|
|
146
|
-
dupes.map(f => ` - ${f.filePath}`).join('\n') +
|
|
147
|
-
`\nUse file path to disambiguate: workflow run start <path.yaml>`);
|
|
148
|
-
}
|
|
149
|
-
return { filePath: byId.filePath, workflowId: byId.id, resolvedVia: 'id' };
|
|
150
|
-
}
|
|
151
|
-
for (const dir of paths) {
|
|
152
|
-
const absDir = path.resolve(process.cwd(), dir);
|
|
153
|
-
for (const ext of ['.yaml', '.yml']) {
|
|
154
|
-
const candidate = path.join(absDir, `${nameOrPath}${ext}`);
|
|
155
|
-
if (fs.existsSync(candidate)) {
|
|
156
|
-
return { filePath: candidate, resolvedVia: 'slug' };
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
const allIds = allWorkflows.map(w => w.id);
|
|
161
|
-
throw new Error(`Workflow "${nameOrPath}" not found (searched as ID and file slug).\n` +
|
|
162
|
-
` Searched directories: ${paths.join(', ')}\n` +
|
|
163
|
-
(allIds.length > 0
|
|
164
|
-
? ` Available workflows: ${allIds.join(', ')}\n`
|
|
165
|
-
: '') +
|
|
166
|
-
` Hint: use "workflow list" to see all workflow IDs`);
|
|
167
|
-
}
|
|
168
|
-
function loadAllWorkflows(engine, paths) {
|
|
169
|
-
const all = [];
|
|
170
|
-
const seenIds = new Set();
|
|
171
|
-
for (const dir of paths) {
|
|
172
|
-
const absDir = path.resolve(process.cwd(), dir);
|
|
173
|
-
const workflows = engine.loadWorkflows(absDir);
|
|
174
|
-
if (!workflows)
|
|
175
|
-
continue;
|
|
176
|
-
for (const wf of workflows) {
|
|
177
|
-
if (!seenIds.has(wf.id)) {
|
|
178
|
-
seenIds.add(wf.id);
|
|
179
|
-
all.push(wf);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
return all;
|
|
184
|
-
}
|
|
185
|
-
function printUsage() {
|
|
186
|
-
console.log(`
|
|
187
|
-
@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
|
|
188
10
|
|
|
189
11
|
Usage:
|
|
190
12
|
workflow <command> [args] [options]
|
|
@@ -194,18 +16,23 @@ Commands:
|
|
|
194
16
|
workflow show <id|file> Show workflow detail
|
|
195
17
|
workflow validate [id|file] Validate workflow YAML
|
|
196
18
|
workflow run start <id|file> Create new execution (add --auto to run to completion)
|
|
197
|
-
# Resolution: .yaml path
|
|
19
|
+
# Resolution: .yaml path \u2192 workflow ID \u2192 filename slug
|
|
198
20
|
workflow run state <id> Get/set execution state
|
|
199
21
|
workflow run node <id> <node> Execute node (auto-invoke agent)
|
|
200
22
|
workflow run auto <id> Auto-run from current node until end/pause
|
|
201
23
|
workflow run reset <id> Reset execution
|
|
24
|
+
workflow run retry <id> Retry a failed run from the failed node (--no-run: transition only)
|
|
25
|
+
\u26A0\uFE0F re-executes the failed node; safe only if it is idempotent
|
|
202
26
|
workflow run list List all executions
|
|
27
|
+
workflow repair <run-id> Preview parent-task repair (use --apply to write)
|
|
203
28
|
workflow usage Show detailed guide
|
|
204
29
|
|
|
205
30
|
Options:
|
|
206
31
|
--json JSON output
|
|
207
32
|
--mermaid Output Mermaid diagram (show only)
|
|
208
33
|
--no-exec Move cursor only, skip agent execution (run node)
|
|
34
|
+
--dry-run Preview workflow parent repair without writes (default)
|
|
35
|
+
--apply Apply workflow parent repair under the run lock
|
|
209
36
|
--version Show version
|
|
210
37
|
--help, -h Show this help
|
|
211
38
|
|
|
@@ -218,828 +45,10 @@ Examples:
|
|
|
218
45
|
workflow validate workflows/standard-dev.yaml
|
|
219
46
|
|
|
220
47
|
For detailed usage guide: workflow usage
|
|
221
|
-
`);
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
process.stdout.write(fs.readFileSync(skillMd, "utf-8"));
|
|
227
|
-
|
|
228
|
-
else {
|
|
229
|
-
printUsage();
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
function formatFlow(nodes) {
|
|
233
|
-
const lines = [];
|
|
234
|
-
let idx = 1;
|
|
235
|
-
for (const [nodeId, node] of Object.entries(nodes)) {
|
|
236
|
-
if (!node || typeof node !== 'object')
|
|
237
|
-
continue;
|
|
238
|
-
const type = node.type ?? 'unknown';
|
|
239
|
-
let desc = `${nodeId} (${type}`;
|
|
240
|
-
if (node.agent)
|
|
241
|
-
desc += ` → ${node.agent}`;
|
|
242
|
-
desc += ')';
|
|
243
|
-
if (type === 'parallel' && Array.isArray(node.branches)) {
|
|
244
|
-
desc += ` → [${node.branches.join(', ')}]`;
|
|
245
|
-
if (node.join)
|
|
246
|
-
desc += ` → ${node.join}`;
|
|
247
|
-
}
|
|
248
|
-
else if (type === 'branch' && node.branches && typeof node.branches === 'object' && !Array.isArray(node.branches)) {
|
|
249
|
-
const parts = Object.entries(node.branches)
|
|
250
|
-
.map(([k, v]) => `${k}: ${v}`)
|
|
251
|
-
.join(' / ');
|
|
252
|
-
desc += ` → ${parts}`;
|
|
253
|
-
}
|
|
254
|
-
else if (node.next) {
|
|
255
|
-
desc += ` → ${node.next}`;
|
|
256
|
-
}
|
|
257
|
-
lines.push(` ${idx}. ${desc}`);
|
|
258
|
-
idx++;
|
|
259
|
-
}
|
|
260
|
-
return lines;
|
|
261
|
-
}
|
|
262
|
-
function cmdList(engine, jsonMode) {
|
|
263
|
-
const { paths } = getWorkflowConfig();
|
|
264
|
-
const workflows = loadAllWorkflows(engine, paths);
|
|
265
|
-
if (workflows.length === 0) {
|
|
266
|
-
const msg = `No workflow YAML files found in: ${paths.join(', ')}`;
|
|
267
|
-
if (jsonMode) {
|
|
268
|
-
console.log(JSON.stringify({ success: true, message: msg, workflows: [] }));
|
|
269
|
-
}
|
|
270
|
-
else {
|
|
271
|
-
console.log(`\u2139\uFE0F ${msg}`);
|
|
272
|
-
}
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
if (jsonMode) {
|
|
276
|
-
console.log(JSON.stringify({
|
|
277
|
-
success: true,
|
|
278
|
-
workflows: workflows.map(wf => {
|
|
279
|
-
const meta = wf.spec.metadata ?? {};
|
|
280
|
-
const nodeCount = wf.spec.nodes ? Object.keys(wf.spec.nodes).length : 0;
|
|
281
|
-
return {
|
|
282
|
-
id: wf.id,
|
|
283
|
-
name: meta.name ?? wf.id,
|
|
284
|
-
description: meta.description ?? '',
|
|
285
|
-
node_count: nodeCount,
|
|
286
|
-
file: wf.filePath,
|
|
287
|
-
};
|
|
288
|
-
}),
|
|
289
|
-
}, null, 2));
|
|
290
|
-
}
|
|
291
|
-
else {
|
|
292
|
-
console.log(`\uD83D\uDCCB \uC6CC\uD06C\uD50C\uB85C \uBAA9\uB85D`);
|
|
293
|
-
workflows.forEach((wf, i) => {
|
|
294
|
-
const meta = wf.spec.metadata ?? {};
|
|
295
|
-
const nodeCount = wf.spec.nodes ? Object.keys(wf.spec.nodes).length : 0;
|
|
296
|
-
const descLine = meta.description ? `\n \uD83D\uDCDD ${meta.description.trim().split('\n')[0]}` : '';
|
|
297
|
-
console.log(` ${i + 1}. ${wf.id} \u2014 ${meta.name ?? wf.id} (${nodeCount} nodes)\n \uD83D\uDCC1 ${wf.filePath}${descLine}`);
|
|
298
|
-
});
|
|
299
|
-
const firstId = workflows[0]?.id;
|
|
300
|
-
if (firstId) {
|
|
301
|
-
console.log(`\n${'━'.repeat(43)}`);
|
|
302
|
-
console.log(`🔍 워크플로 상세: npx workflow show ${firstId}`);
|
|
303
|
-
console.log(`🔀 워크플로 실행: npx workflow run start <id> --set key="value"`);
|
|
304
|
-
}
|
|
305
|
-
const listCtx = {
|
|
306
|
-
commandId: 'workflow.list',
|
|
307
|
-
params: {},
|
|
308
|
-
resultIds: workflows.slice(0, 3).map(w => w.id),
|
|
309
|
-
resultCount: workflows.length,
|
|
310
|
-
};
|
|
311
|
-
const listActions = (0, knowledge_core_1.generateNextActions)(listCtx);
|
|
312
|
-
const listBlock = (0, knowledge_core_1.formatActions)(listActions);
|
|
313
|
-
if (listBlock)
|
|
314
|
-
console.log(listBlock);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
function cmdShow(engine, name, jsonMode, mermaidMode) {
|
|
318
|
-
const { paths } = getWorkflowConfig();
|
|
319
|
-
const workflows = loadAllWorkflows(engine, paths);
|
|
320
|
-
let wf;
|
|
321
|
-
try {
|
|
322
|
-
const resolved = resolveWorkflow(name, paths, engine, workflows);
|
|
323
|
-
if (resolved.workflowId) {
|
|
324
|
-
wf = workflows.find(w => w.id === resolved.workflowId);
|
|
325
|
-
}
|
|
326
|
-
else {
|
|
327
|
-
const fileWorkflows = workflows.filter(w => w.filePath === resolved.filePath);
|
|
328
|
-
if (fileWorkflows.length === 1) {
|
|
329
|
-
wf = fileWorkflows[0];
|
|
330
|
-
}
|
|
331
|
-
else if (fileWorkflows.length > 1) {
|
|
332
|
-
throw new Error(`File "${path.basename(resolved.filePath)}" contains ${fileWorkflows.length} workflows.\n` +
|
|
333
|
-
` Specify a workflow ID:\n` +
|
|
334
|
-
fileWorkflows.map(w => ` workflow show ${w.id}`).join('\n'));
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
catch (e) {
|
|
339
|
-
const msg = e.message;
|
|
340
|
-
if (jsonMode) {
|
|
341
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
342
|
-
}
|
|
343
|
-
else {
|
|
344
|
-
console.error(`\u274C ${msg}`);
|
|
345
|
-
}
|
|
346
|
-
process.exit(1);
|
|
347
|
-
}
|
|
348
|
-
if (!wf) {
|
|
349
|
-
const msg = `Workflow "${name}" not found in: ${paths.join(', ')}`;
|
|
350
|
-
if (jsonMode) {
|
|
351
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
352
|
-
}
|
|
353
|
-
else {
|
|
354
|
-
console.error(`\u274C ${msg}`);
|
|
355
|
-
}
|
|
356
|
-
process.exit(1);
|
|
357
|
-
}
|
|
358
|
-
if (mermaidMode) {
|
|
359
|
-
console.log(engine.toMermaid(wf.spec));
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
const { spec } = wf;
|
|
363
|
-
const meta = spec.metadata ?? {};
|
|
364
|
-
const nodes = spec.nodes ?? {};
|
|
365
|
-
const stateKeys = spec.state ? Object.keys(spec.state) : [];
|
|
366
|
-
if (jsonMode) {
|
|
367
|
-
console.log(JSON.stringify({
|
|
368
|
-
success: true,
|
|
369
|
-
workflow: {
|
|
370
|
-
id: wf.id,
|
|
371
|
-
name: meta.name ?? wf.id,
|
|
372
|
-
description: meta.description ?? '',
|
|
373
|
-
timeout: spec.timeout ?? null,
|
|
374
|
-
max_iterations: spec.max_iterations ?? null,
|
|
375
|
-
state: stateKeys,
|
|
376
|
-
node_count: Object.keys(nodes).length,
|
|
377
|
-
nodes: Object.entries(nodes).map(([id, n]) => ({
|
|
378
|
-
id,
|
|
379
|
-
type: n.type,
|
|
380
|
-
agent: n.agent ?? null,
|
|
381
|
-
next: n.next ?? null,
|
|
382
|
-
branches: n.branches ?? null,
|
|
383
|
-
join: n.join ?? null,
|
|
384
|
-
})),
|
|
385
|
-
},
|
|
386
|
-
}, null, 2));
|
|
387
|
-
}
|
|
388
|
-
else {
|
|
389
|
-
const desc = meta.description ? ` \u2014 ${meta.description}` : '';
|
|
390
|
-
console.log(`\uD83D\uDCC4 ${wf.id}${desc}`);
|
|
391
|
-
console.log(` \uD83D\uDCC1 ${wf.filePath}`);
|
|
392
|
-
const settings = [];
|
|
393
|
-
if (spec.timeout)
|
|
394
|
-
settings.push(`timeout: ${spec.timeout}s`);
|
|
395
|
-
if (spec.max_iterations)
|
|
396
|
-
settings.push(`max_iterations: ${spec.max_iterations}`);
|
|
397
|
-
if (settings.length > 0)
|
|
398
|
-
console.log(` ${settings.join(' | ')}`);
|
|
399
|
-
if (stateKeys.length > 0) {
|
|
400
|
-
console.log(`\n State: ${stateKeys.join(', ')}`);
|
|
401
|
-
}
|
|
402
|
-
const flowLines = formatFlow(nodes);
|
|
403
|
-
if (flowLines.length > 0) {
|
|
404
|
-
console.log(`\n Flow:`);
|
|
405
|
-
flowLines.forEach(l => console.log(l));
|
|
406
|
-
}
|
|
407
|
-
const showCtx = {
|
|
408
|
-
commandId: 'workflow.show',
|
|
409
|
-
params: { name },
|
|
410
|
-
resultCount: Object.keys(nodes).length,
|
|
411
|
-
};
|
|
412
|
-
const showActions = (0, knowledge_core_1.generateNextActions)(showCtx);
|
|
413
|
-
const showBlock = (0, knowledge_core_1.formatActions)(showActions);
|
|
414
|
-
if (showBlock)
|
|
415
|
-
console.log(showBlock);
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
function cmdValidate(engine, target, jsonMode) {
|
|
419
|
-
const { paths } = getWorkflowConfig();
|
|
420
|
-
let files;
|
|
421
|
-
let resolvedSlug;
|
|
422
|
-
if (target) {
|
|
423
|
-
try {
|
|
424
|
-
const resolvedWf = resolveWorkflow(target, paths, engine);
|
|
425
|
-
if (!fs.existsSync(resolvedWf.filePath)) {
|
|
426
|
-
const msg = `File not found: ${resolvedWf.filePath}`;
|
|
427
|
-
if (jsonMode) {
|
|
428
|
-
console.log(JSON.stringify({ success: false, error: msg, workflows: [] }));
|
|
429
|
-
}
|
|
430
|
-
else {
|
|
431
|
-
console.error(`\u274C ${msg}`);
|
|
432
|
-
}
|
|
433
|
-
process.exit(2);
|
|
434
|
-
}
|
|
435
|
-
files = [resolvedWf.filePath];
|
|
436
|
-
if (!target.endsWith('.yaml') && !target.endsWith('.yml')) {
|
|
437
|
-
resolvedSlug = target;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
catch (e) {
|
|
441
|
-
const msg = e.message;
|
|
442
|
-
if (jsonMode) {
|
|
443
|
-
console.log(JSON.stringify({ success: false, error: msg, workflows: [] }));
|
|
444
|
-
}
|
|
445
|
-
else {
|
|
446
|
-
console.error(`\u274C ${msg}`);
|
|
447
|
-
}
|
|
448
|
-
process.exit(2);
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
else {
|
|
452
|
-
files = [];
|
|
453
|
-
for (const dir of paths) {
|
|
454
|
-
const absDir = path.resolve(process.cwd(), dir);
|
|
455
|
-
if (!fs.existsSync(absDir) || !fs.statSync(absDir).isDirectory())
|
|
456
|
-
continue;
|
|
457
|
-
const dirFiles = fs.readdirSync(absDir)
|
|
458
|
-
.filter(f => f.endsWith('.yaml') || f.endsWith('.yml'))
|
|
459
|
-
.map(f => path.join(absDir, f));
|
|
460
|
-
files.push(...dirFiles);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
if (files.length === 0) {
|
|
464
|
-
const msg = 'No workflow YAML files found';
|
|
465
|
-
if (jsonMode) {
|
|
466
|
-
console.log(JSON.stringify({ success: true, message: msg, workflows: [] }));
|
|
467
|
-
}
|
|
468
|
-
else {
|
|
469
|
-
console.log(`\u2139\uFE0F ${msg}`);
|
|
470
|
-
}
|
|
471
|
-
return;
|
|
472
|
-
}
|
|
473
|
-
let hasErrors = false;
|
|
474
|
-
const allResults = [];
|
|
475
|
-
let firstValidName;
|
|
476
|
-
for (const file of files) {
|
|
477
|
-
const { success, results, error } = engine.validateFile(file);
|
|
478
|
-
if (error) {
|
|
479
|
-
hasErrors = true;
|
|
480
|
-
if (jsonMode) {
|
|
481
|
-
allResults.push({ file, results: [{ name: path.basename(file), valid: false, errors: [{ path: file, message: error }] }] });
|
|
482
|
-
}
|
|
483
|
-
else {
|
|
484
|
-
console.error(`\u274C ${file}: ${error}\n`);
|
|
485
|
-
}
|
|
486
|
-
continue;
|
|
487
|
-
}
|
|
488
|
-
if (!success)
|
|
489
|
-
hasErrors = true;
|
|
490
|
-
if (jsonMode) {
|
|
491
|
-
allResults.push({ file, results });
|
|
492
|
-
}
|
|
493
|
-
else {
|
|
494
|
-
for (const r of results) {
|
|
495
|
-
if (r.valid) {
|
|
496
|
-
if (!firstValidName)
|
|
497
|
-
firstValidName = r.name;
|
|
498
|
-
console.log(`\u2705 workflow "${r.name}" \u2014 validation passed`);
|
|
499
|
-
}
|
|
500
|
-
else {
|
|
501
|
-
console.log(`\u274C workflow "${r.name}" \u2014 validation failed`);
|
|
502
|
-
for (const e of r.errors) {
|
|
503
|
-
console.log(` - ${e.path}: ${e.message}`);
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
console.log();
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
if (jsonMode) {
|
|
511
|
-
const flatResults = allResults.flatMap(r => r.results);
|
|
512
|
-
console.log(JSON.stringify({
|
|
513
|
-
success: !hasErrors,
|
|
514
|
-
workflows: flatResults,
|
|
515
|
-
}, null, 2));
|
|
516
|
-
}
|
|
517
|
-
if (!hasErrors) {
|
|
518
|
-
const calName = resolvedSlug ?? firstValidName;
|
|
519
|
-
if (calName) {
|
|
520
|
-
const valCtx = {
|
|
521
|
-
commandId: 'workflow.validate',
|
|
522
|
-
params: { name: calName },
|
|
523
|
-
resultCount: 1,
|
|
524
|
-
};
|
|
525
|
-
const valActions = (0, knowledge_core_1.generateNextActions)(valCtx);
|
|
526
|
-
const valBlock = (0, knowledge_core_1.formatActions)(valActions);
|
|
527
|
-
if (valBlock)
|
|
528
|
-
console.log(valBlock);
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
if (hasErrors)
|
|
532
|
-
process.exit(1);
|
|
533
|
-
}
|
|
534
|
-
async function cmdRun(rest, rawArgs, jsonMode, engine) {
|
|
535
|
-
const runSub = rest[0];
|
|
536
|
-
const runArgs = rest.slice(1);
|
|
537
|
-
const mgr = new run_manager_1.RunManager();
|
|
538
|
-
const { paths } = getWorkflowConfig();
|
|
539
|
-
switch (runSub) {
|
|
540
|
-
case 'start': {
|
|
541
|
-
const positional = runArgs.filter((a, i) => !a.startsWith('--') && runArgs[i - 1] !== '--set');
|
|
542
|
-
const nameOrPath = positional[0];
|
|
543
|
-
if (!nameOrPath) {
|
|
544
|
-
const msg = 'Usage: workflow run start <id|file> [--set key=value ...]';
|
|
545
|
-
if (jsonMode) {
|
|
546
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
547
|
-
}
|
|
548
|
-
else {
|
|
549
|
-
console.error(`\u274C ${msg}`);
|
|
550
|
-
}
|
|
551
|
-
process.exit(1);
|
|
552
|
-
}
|
|
553
|
-
let resolved;
|
|
554
|
-
try {
|
|
555
|
-
resolved = resolveWorkflow(nameOrPath, paths, engine);
|
|
556
|
-
}
|
|
557
|
-
catch (e) {
|
|
558
|
-
const msg = e.message;
|
|
559
|
-
if (jsonMode) {
|
|
560
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
561
|
-
}
|
|
562
|
-
else {
|
|
563
|
-
console.error(`\u274C ${msg}`);
|
|
564
|
-
}
|
|
565
|
-
process.exit(1);
|
|
566
|
-
}
|
|
567
|
-
const sets = (0, run_manager_1.parseSetArgs)(rawArgs);
|
|
568
|
-
const auto = rawArgs.includes('--auto');
|
|
569
|
-
try {
|
|
570
|
-
const run = mgr.start(resolved.filePath, resolved.workflowId ?? positional[1], Object.keys(sets).length > 0 ? sets : undefined);
|
|
571
|
-
if (auto) {
|
|
572
|
-
if (!jsonMode) {
|
|
573
|
-
console.log(`✅ Created execution: ${run.id}`);
|
|
574
|
-
console.log(` Workflow: ${run.workflow_id} (${resolved.filePath})`);
|
|
575
|
-
}
|
|
576
|
-
const result = await mgr.runAuto(run.id);
|
|
577
|
-
reportAutoResult(result, jsonMode);
|
|
578
|
-
break;
|
|
579
|
-
}
|
|
580
|
-
if (jsonMode) {
|
|
581
|
-
console.log(JSON.stringify({ success: true, exec_id: run.id, run }, null, 2));
|
|
582
|
-
}
|
|
583
|
-
else {
|
|
584
|
-
console.log(`\u2705 Created execution: ${run.id}`);
|
|
585
|
-
console.log(` Workflow: ${run.workflow_id} (${resolved.filePath})`);
|
|
586
|
-
console.log(` State keys: ${Object.keys(run.state).join(', ') || '(none)'}`);
|
|
587
|
-
console.log(` File: ${path.join('.crewx/workflow-runs', run.id + '.json')}`);
|
|
588
|
-
let firstNode = null;
|
|
589
|
-
try {
|
|
590
|
-
const allWfs = loadAllWorkflows(engine, paths);
|
|
591
|
-
const wf = allWfs.find(w => w.id === run.workflow_id);
|
|
592
|
-
if (wf) {
|
|
593
|
-
const flowChain = getFlowChain(wf.spec.nodes);
|
|
594
|
-
if (flowChain.length > 0) {
|
|
595
|
-
console.log(` Flow: ${flowChain.join(' \u2192 ')}`);
|
|
596
|
-
}
|
|
597
|
-
const nodeIds = Object.keys(wf.spec.nodes);
|
|
598
|
-
firstNode = nodeIds[0] ?? null;
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
catch { }
|
|
602
|
-
const startCtx = {
|
|
603
|
-
commandId: 'workflow.run.start',
|
|
604
|
-
params: { name: run.workflow_id },
|
|
605
|
-
resultIds: firstNode ? [run.id, firstNode] : [run.id],
|
|
606
|
-
resultCount: 1,
|
|
607
|
-
};
|
|
608
|
-
const startActions = (0, knowledge_core_1.generateNextActions)(startCtx);
|
|
609
|
-
const startBlock = (0, knowledge_core_1.formatActions)(startActions);
|
|
610
|
-
if (startBlock)
|
|
611
|
-
console.log(startBlock);
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
catch (e) {
|
|
615
|
-
const msg = e.message;
|
|
616
|
-
if (jsonMode) {
|
|
617
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
618
|
-
}
|
|
619
|
-
else {
|
|
620
|
-
console.error(`\u274C ${msg}`);
|
|
621
|
-
}
|
|
622
|
-
process.exit(1);
|
|
623
|
-
}
|
|
624
|
-
break;
|
|
625
|
-
}
|
|
626
|
-
case 'state': {
|
|
627
|
-
const positional = runArgs.filter((a, i) => !a.startsWith('--') && runArgs[i - 1] !== '--set');
|
|
628
|
-
const execId = positional[0];
|
|
629
|
-
if (!execId) {
|
|
630
|
-
const msg = 'Usage: workflow run state <exec-id> [--set key=value ...]';
|
|
631
|
-
if (jsonMode) {
|
|
632
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
633
|
-
}
|
|
634
|
-
else {
|
|
635
|
-
console.error(`\u274C ${msg}`);
|
|
636
|
-
}
|
|
637
|
-
process.exit(1);
|
|
638
|
-
}
|
|
639
|
-
const run = mgr.loadRun(execId);
|
|
640
|
-
if (!run) {
|
|
641
|
-
const msg = `Execution not found: ${execId}`;
|
|
642
|
-
if (jsonMode) {
|
|
643
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
644
|
-
}
|
|
645
|
-
else {
|
|
646
|
-
console.error(`\u274C ${msg}`);
|
|
647
|
-
}
|
|
648
|
-
process.exit(1);
|
|
649
|
-
}
|
|
650
|
-
const sets = (0, run_manager_1.parseSetArgs)(rawArgs);
|
|
651
|
-
const stdinKeys = Object.entries(sets).filter(([, v]) => v === '-').map(([k]) => k);
|
|
652
|
-
if (stdinKeys.length > 0 && !process.stdin.isTTY) {
|
|
653
|
-
const stdinRaw = await (0, run_manager_1.readStdin)();
|
|
654
|
-
let stdinValue;
|
|
655
|
-
try {
|
|
656
|
-
stdinValue = JSON.parse(stdinRaw);
|
|
657
|
-
}
|
|
658
|
-
catch {
|
|
659
|
-
stdinValue = stdinRaw;
|
|
660
|
-
}
|
|
661
|
-
for (const key of stdinKeys) {
|
|
662
|
-
sets[key] = stdinValue;
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
if (Object.keys(sets).length > 0) {
|
|
666
|
-
Object.assign(run.state, sets);
|
|
667
|
-
mgr.saveRun(run);
|
|
668
|
-
}
|
|
669
|
-
if (jsonMode) {
|
|
670
|
-
console.log(JSON.stringify({ success: true, exec_id: execId, state: run.state }, null, 2));
|
|
671
|
-
}
|
|
672
|
-
else {
|
|
673
|
-
if (Object.keys(sets).length > 0) {
|
|
674
|
-
console.log(`\u2705 Updated state for ${execId}`);
|
|
675
|
-
}
|
|
676
|
-
console.log(JSON.stringify(run.state, null, 2));
|
|
677
|
-
const nextNode = findNextNode(run, engine);
|
|
678
|
-
const stateCtx = {
|
|
679
|
-
commandId: 'workflow.run.state',
|
|
680
|
-
params: { execId },
|
|
681
|
-
resultIds: nextNode ? [nextNode] : [],
|
|
682
|
-
resultCount: Object.keys(run.state).length,
|
|
683
|
-
};
|
|
684
|
-
const stateActions = (0, knowledge_core_1.generateNextActions)(stateCtx);
|
|
685
|
-
const stateBlock = (0, knowledge_core_1.formatActions)(stateActions);
|
|
686
|
-
if (stateBlock)
|
|
687
|
-
console.log(stateBlock);
|
|
688
|
-
}
|
|
689
|
-
break;
|
|
690
|
-
}
|
|
691
|
-
case 'node': {
|
|
692
|
-
const positional = runArgs.filter(a => !a.startsWith('--'));
|
|
693
|
-
const execId = positional[0];
|
|
694
|
-
const nodeId = positional[1];
|
|
695
|
-
if (!execId || !nodeId) {
|
|
696
|
-
const msg = 'Usage: workflow run node <exec-id> <node-id> [--no-exec]';
|
|
697
|
-
if (jsonMode) {
|
|
698
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
699
|
-
}
|
|
700
|
-
else {
|
|
701
|
-
console.error(`\u274C ${msg}`);
|
|
702
|
-
}
|
|
703
|
-
process.exit(1);
|
|
704
|
-
}
|
|
705
|
-
const noExec = rawArgs.includes('--no-exec');
|
|
706
|
-
try {
|
|
707
|
-
const run = noExec
|
|
708
|
-
? mgr.moveNode(execId, nodeId)
|
|
709
|
-
: await mgr.executeNode(execId, nodeId);
|
|
710
|
-
if (jsonMode) {
|
|
711
|
-
console.log(JSON.stringify({
|
|
712
|
-
success: true,
|
|
713
|
-
exec_id: execId,
|
|
714
|
-
current_node: run.current_node,
|
|
715
|
-
completed_nodes: run.completed_nodes,
|
|
716
|
-
status: run.status,
|
|
717
|
-
}, null, 2));
|
|
718
|
-
}
|
|
719
|
-
else {
|
|
720
|
-
if (run.status === 'completed') {
|
|
721
|
-
console.log(`Workflow completed.`);
|
|
722
|
-
}
|
|
723
|
-
else {
|
|
724
|
-
console.log(`\u2705 Node updated: ${nodeId}${noExec ? ' (move only)' : ''}`);
|
|
725
|
-
if (run.completed_nodes.length > 0) {
|
|
726
|
-
console.log(` Completed: ${run.completed_nodes.join(' \u2192 ')}`);
|
|
727
|
-
}
|
|
728
|
-
console.log(` Current: ${run.current_node}`);
|
|
729
|
-
if (run.status) {
|
|
730
|
-
console.log(` Status: ${run.status}`);
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
let nextNode = null;
|
|
734
|
-
try {
|
|
735
|
-
const { paths: wfPaths } = getWorkflowConfig();
|
|
736
|
-
const allWfs = loadAllWorkflows(engine, wfPaths);
|
|
737
|
-
const wf = allWfs.find(w => w.id === run.workflow_id);
|
|
738
|
-
if (wf) {
|
|
739
|
-
nextNode = findNextNodeForCAL(run.current_node, nodeId, wf.spec.nodes);
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
catch { }
|
|
743
|
-
const nodeCtx = {
|
|
744
|
-
commandId: 'workflow.run.node',
|
|
745
|
-
params: { execId, nodeId },
|
|
746
|
-
resultIds: nextNode ? [nextNode] : [],
|
|
747
|
-
resultCount: 1,
|
|
748
|
-
};
|
|
749
|
-
const nodeActions = (0, knowledge_core_1.generateNextActions)(nodeCtx);
|
|
750
|
-
const nodeBlock = (0, knowledge_core_1.formatActions)(nodeActions);
|
|
751
|
-
if (nodeBlock)
|
|
752
|
-
console.log(nodeBlock);
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
catch (e) {
|
|
756
|
-
const msg = e.message;
|
|
757
|
-
if (jsonMode) {
|
|
758
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
759
|
-
}
|
|
760
|
-
else {
|
|
761
|
-
console.error(`\u274C ${msg}`);
|
|
762
|
-
const fallbackCtx = {
|
|
763
|
-
commandId: 'workflow.run.node',
|
|
764
|
-
params: { execId, nodeId },
|
|
765
|
-
resultIds: [],
|
|
766
|
-
resultCount: 0,
|
|
767
|
-
};
|
|
768
|
-
const fallbackActions = (0, knowledge_core_1.generateNextActions)(fallbackCtx);
|
|
769
|
-
const fallbackBlock = (0, knowledge_core_1.formatActions)(fallbackActions);
|
|
770
|
-
if (fallbackBlock)
|
|
771
|
-
console.log(fallbackBlock);
|
|
772
|
-
}
|
|
773
|
-
process.exit(1);
|
|
774
|
-
}
|
|
775
|
-
break;
|
|
776
|
-
}
|
|
777
|
-
case 'reset': {
|
|
778
|
-
const positional = runArgs.filter(a => !a.startsWith('--'));
|
|
779
|
-
const execId = positional[0];
|
|
780
|
-
if (!execId) {
|
|
781
|
-
const msg = 'Usage: workflow run reset <exec-id>';
|
|
782
|
-
if (jsonMode) {
|
|
783
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
784
|
-
}
|
|
785
|
-
else {
|
|
786
|
-
console.error(`\u274C ${msg}`);
|
|
787
|
-
}
|
|
788
|
-
process.exit(1);
|
|
789
|
-
}
|
|
790
|
-
try {
|
|
791
|
-
const run = mgr.reset(execId);
|
|
792
|
-
if (jsonMode) {
|
|
793
|
-
console.log(JSON.stringify({ success: true, exec_id: execId, state: run.state }, null, 2));
|
|
794
|
-
}
|
|
795
|
-
else {
|
|
796
|
-
console.log(`\u2705 Reset execution: ${execId}`);
|
|
797
|
-
console.log(` State restored to initial values`);
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
catch (e) {
|
|
801
|
-
const msg = e.message;
|
|
802
|
-
if (jsonMode) {
|
|
803
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
804
|
-
}
|
|
805
|
-
else {
|
|
806
|
-
console.error(`\u274C ${msg}`);
|
|
807
|
-
}
|
|
808
|
-
process.exit(1);
|
|
809
|
-
}
|
|
810
|
-
break;
|
|
811
|
-
}
|
|
812
|
-
case 'list': {
|
|
813
|
-
const runs = mgr.listRuns();
|
|
814
|
-
if (jsonMode) {
|
|
815
|
-
console.log(JSON.stringify({
|
|
816
|
-
success: true,
|
|
817
|
-
runs: runs.map(r => ({
|
|
818
|
-
id: r.id,
|
|
819
|
-
workflow_id: r.workflow_id,
|
|
820
|
-
workflow_file: r.workflow_file,
|
|
821
|
-
created_at: r.created_at,
|
|
822
|
-
current_node: r.current_node,
|
|
823
|
-
completed_nodes: r.completed_nodes,
|
|
824
|
-
})),
|
|
825
|
-
}, null, 2));
|
|
826
|
-
}
|
|
827
|
-
else {
|
|
828
|
-
if (runs.length === 0) {
|
|
829
|
-
console.log('\u2139\uFE0F No workflow runs found.');
|
|
830
|
-
return;
|
|
831
|
-
}
|
|
832
|
-
console.log(`\uD83D\uDCCB Workflow runs (${runs.length})`);
|
|
833
|
-
for (const r of runs) {
|
|
834
|
-
const node = r.current_node || '(not started)';
|
|
835
|
-
const completed = r.completed_nodes?.length || 0;
|
|
836
|
-
console.log(` ${r.id}`);
|
|
837
|
-
console.log(` \uD83D\uDCC1 ${r.workflow_file} | node: ${node} | completed: ${completed}`);
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
break;
|
|
841
|
-
}
|
|
842
|
-
case 'auto': {
|
|
843
|
-
const positional = runArgs.filter(a => !a.startsWith('--'));
|
|
844
|
-
const execId = positional[0];
|
|
845
|
-
if (!execId) {
|
|
846
|
-
const msg = 'Usage: workflow run auto <exec-id>';
|
|
847
|
-
if (jsonMode) {
|
|
848
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
849
|
-
}
|
|
850
|
-
else {
|
|
851
|
-
console.error(`❌ ${msg}`);
|
|
852
|
-
}
|
|
853
|
-
process.exit(1);
|
|
854
|
-
}
|
|
855
|
-
try {
|
|
856
|
-
const result = await mgr.runAuto(execId);
|
|
857
|
-
reportAutoResult(result, jsonMode);
|
|
858
|
-
if (result.outcome === 'failed')
|
|
859
|
-
process.exit(1);
|
|
860
|
-
}
|
|
861
|
-
catch (e) {
|
|
862
|
-
const msg = e.message;
|
|
863
|
-
if (jsonMode) {
|
|
864
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
865
|
-
}
|
|
866
|
-
else {
|
|
867
|
-
console.error(`❌ ${msg}`);
|
|
868
|
-
}
|
|
869
|
-
process.exit(1);
|
|
870
|
-
}
|
|
871
|
-
break;
|
|
872
|
-
}
|
|
873
|
-
default: {
|
|
874
|
-
const msg = `Unknown run subcommand: ${runSub || '(none)'}\nUsage: workflow run <start|state|node|auto|reset|list> [args...]`;
|
|
875
|
-
if (jsonMode) {
|
|
876
|
-
console.log(JSON.stringify({ success: false, error: msg }));
|
|
877
|
-
}
|
|
878
|
-
else {
|
|
879
|
-
console.error(`\u274C ${msg}`);
|
|
880
|
-
}
|
|
881
|
-
process.exit(1);
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
function reportAutoResult(result, jsonMode) {
|
|
886
|
-
const { run, outcome, reason, executed } = result;
|
|
887
|
-
const status = run.status ?? (outcome === 'paused' ? 'paused' : 'running');
|
|
888
|
-
if (jsonMode) {
|
|
889
|
-
console.log(JSON.stringify({
|
|
890
|
-
success: outcome !== 'failed',
|
|
891
|
-
exec_id: run.id,
|
|
892
|
-
status,
|
|
893
|
-
outcome,
|
|
894
|
-
current_node: run.current_node,
|
|
895
|
-
completed_nodes: run.completed_nodes,
|
|
896
|
-
executed,
|
|
897
|
-
...(reason ? { reason } : {}),
|
|
898
|
-
}, null, 2));
|
|
899
|
-
return;
|
|
900
|
-
}
|
|
901
|
-
const icon = outcome === 'failed' ? '❌' : outcome === 'paused' ? '⏸️' : '✅';
|
|
902
|
-
console.log(`${icon} Auto run: ${run.id}`);
|
|
903
|
-
if (executed.length > 0) {
|
|
904
|
-
console.log(` Executed: ${executed.join(' → ')}`);
|
|
905
|
-
}
|
|
906
|
-
console.log(` Outcome: ${outcome}${reason ? ` (${reason})` : ''}`);
|
|
907
|
-
console.log(` Current: ${run.current_node || '(none)'}`);
|
|
908
|
-
console.log(` Status: ${status}`);
|
|
909
|
-
}
|
|
910
|
-
function findNextNodeForCAL(currentNode, executedNodeId, nodes) {
|
|
911
|
-
const executedNode = nodes[executedNodeId];
|
|
912
|
-
if (!executedNode)
|
|
913
|
-
return null;
|
|
914
|
-
switch (executedNode.type) {
|
|
915
|
-
case 'agent_task':
|
|
916
|
-
case 'skill_task':
|
|
917
|
-
case 'shell_task':
|
|
918
|
-
case 'expression':
|
|
919
|
-
return typeof executedNode.next === 'string' ? executedNode.next : null;
|
|
920
|
-
case 'branch':
|
|
921
|
-
return (currentNode && currentNode !== executedNodeId) ? currentNode : null;
|
|
922
|
-
case 'end':
|
|
923
|
-
case 'parallel':
|
|
924
|
-
case 'join':
|
|
925
|
-
return null;
|
|
926
|
-
default:
|
|
927
|
-
return null;
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
function getFlowChain(nodes) {
|
|
931
|
-
const chain = [];
|
|
932
|
-
const nodeIds = Object.keys(nodes);
|
|
933
|
-
if (nodeIds.length === 0)
|
|
934
|
-
return chain;
|
|
935
|
-
let current = nodeIds[0];
|
|
936
|
-
const visited = new Set();
|
|
937
|
-
while (current && !visited.has(current)) {
|
|
938
|
-
visited.add(current);
|
|
939
|
-
chain.push(current);
|
|
940
|
-
const node = nodes[current];
|
|
941
|
-
if (!node || node.type === 'end')
|
|
942
|
-
break;
|
|
943
|
-
if (typeof node.next === 'string') {
|
|
944
|
-
current = node.next;
|
|
945
|
-
}
|
|
946
|
-
else {
|
|
947
|
-
chain.push('...');
|
|
948
|
-
break;
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
return chain;
|
|
952
|
-
}
|
|
953
|
-
function findNextNode(run, engine) {
|
|
954
|
-
try {
|
|
955
|
-
const { paths } = getWorkflowConfig();
|
|
956
|
-
const all = loadAllWorkflows(engine, paths);
|
|
957
|
-
const wf = all.find(w => w.id === run.workflow_id);
|
|
958
|
-
if (!wf)
|
|
959
|
-
return null;
|
|
960
|
-
const currentNode = run.current_node;
|
|
961
|
-
if (!currentNode) {
|
|
962
|
-
const nodeIds = Object.keys(wf.spec.nodes);
|
|
963
|
-
return nodeIds[0] ?? null;
|
|
964
|
-
}
|
|
965
|
-
const node = wf.spec.nodes[currentNode];
|
|
966
|
-
if (!node)
|
|
967
|
-
return null;
|
|
968
|
-
if (typeof node.next === 'string')
|
|
969
|
-
return node.next;
|
|
970
|
-
return null;
|
|
971
|
-
}
|
|
972
|
-
catch {
|
|
973
|
-
return null;
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
function findNodeAgent(run, nodeId, engine) {
|
|
977
|
-
try {
|
|
978
|
-
const { paths } = getWorkflowConfig();
|
|
979
|
-
const all = loadAllWorkflows(engine, paths);
|
|
980
|
-
const wf = all.find(w => w.id === run.workflow_id);
|
|
981
|
-
if (!wf)
|
|
982
|
-
return null;
|
|
983
|
-
const node = wf.spec.nodes[nodeId];
|
|
984
|
-
return node?.agent ?? null;
|
|
985
|
-
}
|
|
986
|
-
catch {
|
|
987
|
-
return null;
|
|
988
|
-
}
|
|
989
|
-
}
|
|
990
|
-
async function main(inputArgs) {
|
|
991
|
-
const args = inputArgs ?? process.argv.slice(2);
|
|
992
|
-
const { positional, options } = parseArgs(args);
|
|
993
|
-
const [subcommand, ...rest] = positional;
|
|
994
|
-
if (options.version) {
|
|
995
|
-
console.log(PKG_VERSION);
|
|
996
|
-
return;
|
|
997
|
-
}
|
|
998
|
-
if (options.help || subcommand === 'help' || !subcommand) {
|
|
999
|
-
printUsage();
|
|
1000
|
-
return;
|
|
1001
|
-
}
|
|
1002
|
-
const jsonMode = !!options.json;
|
|
1003
|
-
const engine = new engine_1.WorkflowEngine();
|
|
1004
|
-
switch (subcommand) {
|
|
1005
|
-
case 'list':
|
|
1006
|
-
case 'ls':
|
|
1007
|
-
cmdList(engine, jsonMode);
|
|
1008
|
-
break;
|
|
1009
|
-
case 'show': {
|
|
1010
|
-
const name = rest[0];
|
|
1011
|
-
if (!name) {
|
|
1012
|
-
console.error('Error: workflow name is required\nUsage: workflow show <id|file> [--mermaid]');
|
|
1013
|
-
process.exit(1);
|
|
1014
|
-
}
|
|
1015
|
-
cmdShow(engine, name, jsonMode, !!options.mermaid);
|
|
1016
|
-
break;
|
|
1017
|
-
}
|
|
1018
|
-
case 'validate': {
|
|
1019
|
-
const targetFile = rest[0];
|
|
1020
|
-
cmdValidate(engine, targetFile, jsonMode);
|
|
1021
|
-
break;
|
|
1022
|
-
}
|
|
1023
|
-
case 'run': {
|
|
1024
|
-
await cmdRun(rest, args, jsonMode, engine);
|
|
1025
|
-
break;
|
|
1026
|
-
}
|
|
1027
|
-
case 'usage':
|
|
1028
|
-
showDetailedUsage();
|
|
1029
|
-
break;
|
|
1030
|
-
default:
|
|
1031
|
-
console.error(`Unknown subcommand: ${subcommand}`);
|
|
1032
|
-
printUsage();
|
|
1033
|
-
process.exit(1);
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
if (require.main === module) {
|
|
1037
|
-
loadTracer().then(tracer => {
|
|
1038
|
-
const opts = tracer ? { skillVersion: tracer.getOwnSkillVersion(__dirname) } : {};
|
|
1039
|
-
return tracer ? tracer.run('crewx/workflow', () => main(), opts) : main();
|
|
1040
|
-
}).catch((e) => {
|
|
1041
|
-
console.error(e.message);
|
|
1042
|
-
process.exit(1);
|
|
1043
|
-
});
|
|
1044
|
-
}
|
|
1045
|
-
//# 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)})}
|