@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.
Files changed (48) hide show
  1. package/SKILL.md +4 -4
  2. package/dist/cli.d.ts +1 -1
  3. package/dist/cli.js +21 -1012
  4. package/dist/src/card-state.d.ts +20 -0
  5. package/dist/src/card-state.js +1 -0
  6. package/dist/src/engine.d.ts +10 -3
  7. package/dist/src/engine.js +1 -348
  8. package/dist/src/failure.d.ts +41 -0
  9. package/dist/src/failure.js +1 -0
  10. package/dist/src/file-lock.d.ts +1 -1
  11. package/dist/src/file-lock.js +1 -97
  12. package/dist/src/list-view.d.ts +22 -0
  13. package/dist/src/list-view.js +3 -0
  14. package/dist/src/mermaid.d.ts +1 -1
  15. package/dist/src/mermaid.js +2 -133
  16. package/dist/src/parent-task-sync.d.ts +139 -0
  17. package/dist/src/parent-task-sync.js +1 -0
  18. package/dist/src/retry-policy.d.ts +38 -0
  19. package/dist/src/retry-policy.js +1 -0
  20. package/dist/src/run-manager.d.ts +37 -3
  21. package/dist/src/run-manager.js +4 -1228
  22. package/dist/src/types.d.ts +37 -4
  23. package/dist/src/types.js +1 -3
  24. package/dist/src/utils/output-format.d.ts +1 -1
  25. package/dist/src/utils/output-format.js +12 -157
  26. package/dist/src/utils/shell-security.d.ts +1 -1
  27. package/dist/src/utils/shell-security.js +1 -138
  28. package/dist/src/workflow-persistence.d.ts +48 -0
  29. package/dist/src/workflow-persistence.js +1 -0
  30. package/dist/workflow-schema.json +25 -4
  31. package/package.json +23 -4
  32. package/workflow-schema.json +25 -4
  33. package/dist/cli.d.ts.map +0 -1
  34. package/dist/cli.js.map +0 -1
  35. package/dist/src/engine.d.ts.map +0 -1
  36. package/dist/src/engine.js.map +0 -1
  37. package/dist/src/file-lock.d.ts.map +0 -1
  38. package/dist/src/file-lock.js.map +0 -1
  39. package/dist/src/mermaid.d.ts.map +0 -1
  40. package/dist/src/mermaid.js.map +0 -1
  41. package/dist/src/run-manager.d.ts.map +0 -1
  42. package/dist/src/run-manager.js.map +0 -1
  43. package/dist/src/types.d.ts.map +0 -1
  44. package/dist/src/types.js.map +0 -1
  45. package/dist/src/utils/output-format.d.ts.map +0 -1
  46. package/dist/src/utils/output-format.js.map +0 -1
  47. package/dist/src/utils/shell-security.d.ts.map +0 -1
  48. package/dist/src/utils/shell-security.js.map +0 -1
@@ -8,7 +8,7 @@ export interface RetryConfig {
8
8
  max: number;
9
9
  delay: 'exponential' | 'fixed';
10
10
  max_delay?: number;
11
- on?: string[];
11
+ on?: WorkflowFailureCode[];
12
12
  }
13
13
  export interface OnError {
14
14
  handler?: string;
@@ -26,6 +26,7 @@ export interface WorkflowNode {
26
26
  command?: string[];
27
27
  cwd?: string;
28
28
  env?: Record<string, string>;
29
+ stdin?: string;
29
30
  output?: string;
30
31
  output_format?: 'json';
31
32
  output_schema?: JsonSchema;
@@ -79,7 +80,7 @@ export interface ValidationResult {
79
80
  export interface AuditEntry {
80
81
  exec_id: string;
81
82
  node_id: string;
82
- type: NodeType | string;
83
+ type: NodeType | 'retry' | string;
83
84
  summary: string;
84
85
  started_at: string;
85
86
  ended_at: string;
@@ -88,6 +89,33 @@ export interface AuditEntry {
88
89
  error?: string;
89
90
  trigger: 'manual' | 'auto';
90
91
  }
92
+ export interface WorkflowAutoRunLease {
93
+ pid: number;
94
+ token: string;
95
+ node_id: string;
96
+ started_at: string;
97
+ }
98
+ export interface WorkflowManualResume {
99
+ node_id: string;
100
+ requested_at: string;
101
+ }
102
+ export type WorkflowFailureCode = 'CLI_UNRESOLVABLE' | 'SPAWN_FAILED' | 'NODE_TIMEOUT' | 'NODE_EXIT_NONZERO' | 'OUTPUT_INVALID' | 'RATE_LIMITED' | 'CONFIG_INVALID' | 'INTERRUPTED' | 'CANCELLED' | 'UNKNOWN';
103
+ export interface RetryPolicy {
104
+ max: number;
105
+ delay: 'exponential' | 'fixed';
106
+ max_delay: number;
107
+ on: WorkflowFailureCode[];
108
+ }
109
+ export declare const NEVER_RETRYABLE: readonly WorkflowFailureCode[];
110
+ export interface WorkflowNodeFailure {
111
+ failedNode: string;
112
+ attempt: number;
113
+ causeCode: WorkflowFailureCode;
114
+ causeMessage: string;
115
+ exitCode: number | null;
116
+ evidencePreview: string | null;
117
+ occurredAt: string;
118
+ }
91
119
  export interface RunExecution {
92
120
  id: string;
93
121
  workflow_id: string;
@@ -98,10 +126,15 @@ export interface RunExecution {
98
126
  completed_nodes: string[];
99
127
  state: Record<string, unknown>;
100
128
  initial_state: Record<string, unknown>;
101
- status?: 'running' | 'completed' | 'failed' | 'cancelled';
129
+ status?: 'pending' | 'running' | 'retrying' | 'completed' | 'failed' | 'cancelled';
102
130
  tasks?: Record<string, string>;
103
131
  error?: string;
132
+ last_failure?: WorkflowNodeFailure;
104
133
  audit?: AuditEntry[];
134
+ retry_count?: number;
135
+ node_attempts?: Record<string, number>;
136
+ auto_run?: WorkflowAutoRunLease;
137
+ manual_resume?: WorkflowManualResume;
105
138
  }
106
139
  export interface AutoRunResult {
107
140
  run: RunExecution;
@@ -109,4 +142,4 @@ export interface AutoRunResult {
109
142
  reason?: string;
110
143
  executed: string[];
111
144
  }
112
- //# sourceMappingURL=types.d.ts.map
145
+
package/dist/src/types.js CHANGED
@@ -1,3 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=types.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.NEVER_RETRYABLE=void 0;exports.NEVER_RETRYABLE=["CANCELLED","CONFIG_INVALID"];
@@ -15,4 +15,4 @@ export declare function buildJsonOutputSuffix(schema?: JsonSchema, strict?: bool
15
15
  export declare function extractJson(text: string): string;
16
16
  export declare function validateJsonSchema(value: unknown, schema: JsonSchema): Promise<void>;
17
17
  export declare function toOpenAIStrictSchema(schema: JsonSchema): JsonSchema;
18
- //# sourceMappingURL=output-format.d.ts.map
18
+
@@ -1,53 +1,8 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.WORKFLOW_SIGNAL_KEY = void 0;
37
- exports.buildWorkflowPreamble = buildWorkflowPreamble;
38
- exports.detectWorkflowSignal = detectWorkflowSignal;
39
- exports.buildJsonOutputSuffix = buildJsonOutputSuffix;
40
- exports.extractJson = extractJson;
41
- exports.validateJsonSchema = validateJsonSchema;
42
- exports.toOpenAIStrictSchema = toOpenAIStrictSchema;
43
- const node_crypto_1 = require("node:crypto");
44
- exports.WORKFLOW_SIGNAL_KEY = 'workflow_signal';
45
- function buildWorkflowPreamble(ctx) {
46
- return `<crewx.workflow>
1
+ "use strict";var y=exports&&exports.__createBinding||(Object.create?(function(n,e,r,t){if(t===void 0)t=r;var o=Object.getOwnPropertyDescriptor(e,r);if(!o||("get"in o?!e.__esModule:o.writable||o.configurable)){o={enumerable:true,get:function(){return e[r]}}}Object.defineProperty(n,t,o)}):(function(n,e,r,t){if(t===void 0)t=r;n[t]=e[r]}));var _=exports&&exports.__setModuleDefault||(Object.create?(function(n,e){Object.defineProperty(n,"default",{enumerable:true,value:e})}):function(n,e){n["default"]=e});var v=exports&&exports.__importStar||(function(){var n=function(e){n=Object.getOwnPropertyNames||function(r){var t=[];for(var o in r)if(Object.prototype.hasOwnProperty.call(r,o))t[t.length]=o;return t};return n(e)};return function(e){if(e&&e.__esModule)return e;var r={};if(e!=null){for(var t=n(e),o=0;o<t.length;o++)if(t[o]!=="default")y(r,e,t[o])}_(r,e);return r}})();Object.defineProperty(exports,"__esModule",{value:true});exports.WORKFLOW_SIGNAL_KEY=void 0;exports.buildWorkflowPreamble=j;exports.detectWorkflowSignal=E;exports.buildJsonOutputSuffix=P;exports.extractJson=O;exports.validateJsonSchema=W;exports.toOpenAIStrictSchema=S;const N=require("node:crypto");exports.WORKFLOW_SIGNAL_KEY="workflow_signal";function j(n){return`<crewx.workflow>
47
2
  ## Execution Context
48
- You are running headless as node "${ctx.nodeId}" of workflow "${ctx.workflowId}" (run ${ctx.execId}).
3
+ You are running headless as node "${n.nodeId}" of workflow "${n.workflowId}" (run ${n.execId}).
49
4
  - No human is watching this session. NEVER ask clarifying questions or wait for
50
- confirmation — state your assumptions and proceed, or report failure.
5
+ confirmation \u2014 state your assumptions and proceed, or report failure.
51
6
  - Your output is consumed verbatim by downstream nodes. Output the result only;
52
7
  skip greetings, progress notes, and meta commentary.
53
8
  - If the task cannot be completed (broken precondition, missing data, failed
@@ -57,118 +12,18 @@ You are running headless as node "${ctx.nodeId}" of workflow "${ctx.workflowId}"
57
12
  The engine will halt the workflow at this node and record the reason.
58
13
  </crewx.workflow>
59
14
 
60
- `;
61
- }
62
- function detectWorkflowSignal(output) {
63
- if (!output.includes(`"${exports.WORKFLOW_SIGNAL_KEY}"`))
64
- return null;
65
- const toSignal = (parsed) => {
66
- if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed) &&
67
- parsed[exports.WORKFLOW_SIGNAL_KEY] === 'fail') {
68
- const reason = parsed['reason'];
69
- return { signal: 'fail', ...(typeof reason === 'string' && reason ? { reason } : {}) };
70
- }
71
- return null;
72
- };
73
- try {
74
- const signal = toSignal(JSON.parse(extractJson(output)));
75
- if (signal)
76
- return signal;
77
- }
78
- catch { }
79
- const inline = output.match(new RegExp(`\\{[^{}]*"${exports.WORKFLOW_SIGNAL_KEY}"\\s*:\\s*"fail"[^{}]*\\}`));
80
- if (inline) {
81
- try {
82
- return toSignal(JSON.parse(inline[0]));
83
- }
84
- catch {
85
- return { signal: 'fail' };
86
- }
87
- }
88
- return null;
89
- }
90
- function buildJsonOutputSuffix(schema, strict = false) {
91
- const finalSchema = strict && schema ? toOpenAIStrictSchema(schema) : schema;
92
- const schemaStr = finalSchema ? `JSON Schema:\n${JSON.stringify(finalSchema, null, 2)}\n` : '';
93
- return `\n\n<crewx.workflow>
94
- ## ⚠️ Output Format (read first — drives branch evaluation)
15
+ `}function E(n){if(!n.includes(`"${exports.WORKFLOW_SIGNAL_KEY}"`))return null;const e=t=>{if(typeof t==="object"&&t!==null&&!Array.isArray(t)&&t[exports.WORKFLOW_SIGNAL_KEY]==="fail"){const o=t["reason"];return{signal:"fail",...typeof o==="string"&&o?{reason:o}:{}}}return null};try{const t=e(JSON.parse(O(n)));if(t)return t}catch{}const r=n.match(new RegExp(`\\{[^{}]*"${exports.WORKFLOW_SIGNAL_KEY}"\\s*:\\s*"fail"[^{}]*\\}`));if(r){try{return e(JSON.parse(r[0]))}catch{return{signal:"fail"}}}return null}function P(n,e=false){const r=e&&n?S(n):n;const t=r?`JSON Schema:
16
+ ${JSON.stringify(r,null,2)}
17
+ `:"";return`
18
+
19
+ <crewx.workflow>
20
+ ## \u26A0\uFE0F Output Format (read first \u2014 drives branch evaluation)
95
21
 
96
22
  **The first character of your output MUST be \`{\`. The last character MUST be \`}\`.**
97
23
  No other text is permitted (no explanations, greetings, progress notes, or analysis remarks).
98
24
  **NEVER use markdown code fences (\`\`\`json ... \`\`\`).** Do not output even a single backtick.
99
- Branch evaluation calls \`JSON.parse(state.<output>)\` directly —
25
+ Branch evaluation calls \`JSON.parse(state.<output>)\` directly \u2014
100
26
  even one non-JSON character at start or end will halt the workflow.
101
27
 
102
- ${schemaStr}Execute the task, then output **exactly one JSON object** matching the schema above. Nothing else.
103
- </crewx.workflow>`;
104
- }
105
- function extractJson(text) {
106
- let trimmed = text.trim();
107
- const fenceMatch = trimmed.match(/^```(?:json|JSON)?\s*\n?([\s\S]*?)\n?```\s*$/);
108
- if (fenceMatch && fenceMatch[1] !== undefined) {
109
- trimmed = fenceMatch[1].trim();
110
- }
111
- const objMatch = trimmed.match(/\{[\s\S]*\}/);
112
- const arrMatch = trimmed.match(/\[[\s\S]*\]/);
113
- if (objMatch && arrMatch) {
114
- return objMatch.index < arrMatch.index ? objMatch[0] : arrMatch[0];
115
- }
116
- return objMatch?.[0] ?? arrMatch?.[0] ?? trimmed;
117
- }
118
- async function validateJsonSchema(value, schema) {
119
- const { registerSchema, validate } = await Promise.resolve().then(() => __importStar(require('@hyperjump/json-schema/draft-2020-12')));
120
- const uri = `urn:crewx:schema:${(0, node_crypto_1.randomUUID)()}`;
121
- registerSchema({ $schema: 'https://json-schema.org/draft/2020-12/schema', ...schema }, uri);
122
- const result = await validate(uri, value);
123
- if (!result.valid) {
124
- const errors = (result.errors ?? [])
125
- .map((e) => e.instanceLocation)
126
- .join('; ');
127
- throw new Error(`Schema validation failed: ${errors || 'unknown error'}`);
128
- }
129
- }
130
- function toOpenAIStrictSchema(schema) {
131
- return transformStrict(schema);
132
- }
133
- const OPENAI_UNSUPPORTED_KEYWORDS = new Set([
134
- 'default', 'format', 'pattern', 'minLength', 'maxLength',
135
- 'minimum', 'maximum', 'not',
136
- ]);
137
- function transformStrict(schema) {
138
- const result = {};
139
- for (const [key, value] of Object.entries(schema)) {
140
- if (OPENAI_UNSUPPORTED_KEYWORDS.has(key))
141
- continue;
142
- if (key === 'properties' && isRecord(value)) {
143
- const transformed = {};
144
- for (const [propKey, propVal] of Object.entries(value)) {
145
- transformed[propKey] = transformStrict(propVal);
146
- }
147
- result['properties'] = transformed;
148
- result['required'] = Object.keys(transformed);
149
- result['additionalProperties'] = false;
150
- }
151
- else if (key === 'items' && isRecord(value)) {
152
- result['items'] = transformStrict(value);
153
- }
154
- else if (key === 'required') {
155
- if (!('properties' in schema)) {
156
- result['required'] = value;
157
- }
158
- }
159
- else if (key === 'additionalProperties') {
160
- result['additionalProperties'] = false;
161
- }
162
- else {
163
- result[key] = value;
164
- }
165
- }
166
- if (result['type'] === 'object' && !('additionalProperties' in result)) {
167
- result['additionalProperties'] = false;
168
- }
169
- return result;
170
- }
171
- function isRecord(val) {
172
- return typeof val === 'object' && val !== null && !Array.isArray(val);
173
- }
174
- //# sourceMappingURL=output-format.js.map
28
+ ${t}Execute the task, then output **exactly one JSON object** matching the schema above. Nothing else.
29
+ </crewx.workflow>`}function O(n){let e=n.trim();const r=e.match(/^```(?:json|JSON)?\s*\n?([\s\S]*?)\n?```\s*$/);if(r&&r[1]!==void 0){e=r[1].trim()}const t=(i,d,b)=>{const w=[];let h=false;let p=false;let l=0;let u=-1;for(let s=0;s<i.length;s++){const f=i[s];if(p){p=false;continue}if(h){if(f==="\\"){p=true;continue}if(f==='"'){h=false}continue}if(f==='"'){h=true;continue}if(f===d){if(l===0)u=s;l++}else if(f===b){if(l>0){l--;if(l===0&&u!==-1){w.push({text:i.slice(u,s+1),start:u,end:s});u=-1}}}}return w};const o=t(e,"{","}");const a=t(e,"[","]");const c=[...o,...a].sort((i,d)=>d.end-i.end);for(const i of c){try{JSON.parse(i.text);return i.text}catch{}}return e}async function W(n,e){const{registerSchema:r,validate:t}=await Promise.resolve().then(()=>v(require("@hyperjump/json-schema/draft-2020-12")));const o=`urn:crewx:schema:${(0,N.randomUUID)()}`;r({$schema:"https://json-schema.org/draft/2020-12/schema",...e},o);const a=await t(o,n);if(!a.valid){const c=(a.errors??[]).map(i=>i.instanceLocation).join("; ");throw new Error(`Schema validation failed: ${c||"unknown error"}`)}}function S(n){return m(n)}const J=new Set(["default","format","pattern","minLength","maxLength","minimum","maximum","not"]);function m(n){const e={};for(const[r,t]of Object.entries(n)){if(J.has(r))continue;if(r==="properties"&&g(t)){const o={};for(const[a,c]of Object.entries(t)){o[a]=m(c)}e["properties"]=o;e["required"]=Object.keys(o);e["additionalProperties"]=false}else if(r==="items"&&g(t)){e["items"]=m(t)}else if(r==="required"){if(!("properties"in n)){e["required"]=t}}else if(r==="additionalProperties"){e["additionalProperties"]=false}else{e[r]=t}}if(e["type"]==="object"&&!("additionalProperties"in e)){e["additionalProperties"]=false}return e}function g(n){return typeof n==="object"&&n!==null&&!Array.isArray(n)}
@@ -15,4 +15,4 @@ export interface CwdCheck {
15
15
  }
16
16
  export declare function normalizeCwd(cwd: string | undefined, projectRoot: string): CwdCheck;
17
17
  export declare function buildShellEnv(nodeEnv: Record<string, string> | undefined, parentEnv?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
18
- //# sourceMappingURL=shell-security.d.ts.map
18
+
@@ -1,138 +1 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.DANGEROUS_ENV_KEYS = exports.INLINE_CODE_INTERPRETERS = exports.SHELL_DENYLIST = exports.SKILL_NAME_RE = void 0;
37
- exports.validateShellCommand = validateShellCommand;
38
- exports.validateEnvKeys = validateEnvKeys;
39
- exports.normalizeCwd = normalizeCwd;
40
- exports.buildShellEnv = buildShellEnv;
41
- const path = __importStar(require("path"));
42
- const fs = __importStar(require("fs"));
43
- exports.SKILL_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
44
- exports.SHELL_DENYLIST = new Set([
45
- 'bash', 'sh', 'zsh', 'dash', 'fish',
46
- 'cmd', 'cmd.exe', 'powershell', 'powershell.exe', 'pwsh', 'pwsh.exe',
47
- ]);
48
- exports.INLINE_CODE_INTERPRETERS = new Set([
49
- 'python', 'python3', 'node', 'nodejs', 'deno', 'perl', 'ruby', 'php',
50
- ]);
51
- const INLINE_CODE_FLAGS = new Set(['-c', '-e', '--eval', '--exec']);
52
- exports.DANGEROUS_ENV_KEYS = new Set([
53
- 'LD_PRELOAD', 'LD_LIBRARY_PATH', 'LD_AUDIT',
54
- 'DYLD_INSERT_LIBRARIES', 'DYLD_LIBRARY_PATH', 'DYLD_FRAMEWORK_PATH',
55
- 'NODE_OPTIONS',
56
- ]);
57
- function baseName(cmd) {
58
- return path.basename(cmd).toLowerCase();
59
- }
60
- function validateShellCommand(command) {
61
- if (!Array.isArray(command)) {
62
- return { ok: false, error: 'shell_task.command must be an array of strings (argv), not a string' };
63
- }
64
- if (command.length === 0) {
65
- return { ok: false, error: 'shell_task.command must not be empty' };
66
- }
67
- if (!command.every((c) => typeof c === 'string')) {
68
- return { ok: false, error: 'shell_task.command must contain only strings' };
69
- }
70
- const bin = baseName(command[0]);
71
- const binNoExt = bin.replace(/\.exe$/, '');
72
- if (exports.SHELL_DENYLIST.has(bin) || exports.SHELL_DENYLIST.has(binNoExt)) {
73
- return { ok: false, error: `shell_task.command[0] "${command[0]}" is a blocked shell interpreter` };
74
- }
75
- if (exports.INLINE_CODE_INTERPRETERS.has(binNoExt)) {
76
- const hasInlineFlag = command
77
- .slice(1)
78
- .some((arg) => INLINE_CODE_FLAGS.has(String(arg)));
79
- if (hasInlineFlag) {
80
- return {
81
- ok: false,
82
- error: `shell_task.command "${binNoExt} ${command[1]}" uses an inline-code flag (escape vector blocked)`,
83
- };
84
- }
85
- }
86
- return { ok: true };
87
- }
88
- function validateEnvKeys(env) {
89
- if (!env)
90
- return { ok: true };
91
- for (const key of Object.keys(env)) {
92
- if (exports.DANGEROUS_ENV_KEYS.has(key)) {
93
- return { ok: false, error: `shell_task.env key "${key}" is not allowed` };
94
- }
95
- }
96
- return { ok: true };
97
- }
98
- function normalizeCwd(cwd, projectRoot) {
99
- let root;
100
- try {
101
- root = fs.realpathSync(projectRoot);
102
- }
103
- catch (error) {
104
- return { ok: false, error: `project root "${projectRoot}" is not accessible: ${error.message}` };
105
- }
106
- if (cwd === undefined || cwd === '') {
107
- return { ok: true, resolved: root };
108
- }
109
- const candidate = path.resolve(root, cwd);
110
- let resolved;
111
- try {
112
- resolved = fs.realpathSync(candidate);
113
- }
114
- catch (error) {
115
- return { ok: false, error: `cwd "${cwd}" is not accessible: ${error.message}` };
116
- }
117
- if (resolved !== root && !resolved.startsWith(root + path.sep)) {
118
- return { ok: false, error: `cwd "${cwd}" resolves outside the project root` };
119
- }
120
- return { ok: true, resolved };
121
- }
122
- function buildShellEnv(nodeEnv, parentEnv = process.env) {
123
- const allowlist = process.platform === 'win32'
124
- ? ['PATH', 'Path', 'SystemRoot', 'COMSPEC', 'TEMP', 'TMP']
125
- : ['PATH', 'HOME', 'LANG', 'TMPDIR'];
126
- const env = {};
127
- for (const key of allowlist) {
128
- if (parentEnv[key] !== undefined)
129
- env[key] = parentEnv[key];
130
- }
131
- if (nodeEnv) {
132
- for (const [key, value] of Object.entries(nodeEnv)) {
133
- env[key] = value;
134
- }
135
- }
136
- return env;
137
- }
138
- //# sourceMappingURL=shell-security.js.map
1
+ "use strict";var u=exports&&exports.__createBinding||(Object.create?(function(e,r,n,s){if(s===void 0)s=n;var t=Object.getOwnPropertyDescriptor(r,n);if(!t||("get"in t?!r.__esModule:t.writable||t.configurable)){t={enumerable:true,get:function(){return r[n]}}}Object.defineProperty(e,s,t)}):(function(e,r,n,s){if(s===void 0)s=n;e[s]=r[n]}));var f=exports&&exports.__setModuleDefault||(Object.create?(function(e,r){Object.defineProperty(e,"default",{enumerable:true,value:r})}):function(e,r){e["default"]=r});var l=exports&&exports.__importStar||(function(){var e=function(r){e=Object.getOwnPropertyNames||function(n){var s=[];for(var t in n)if(Object.prototype.hasOwnProperty.call(n,t))s[s.length]=t;return s};return e(r)};return function(r){if(r&&r.__esModule)return r;var n={};if(r!=null){for(var s=e(r),t=0;t<s.length;t++)if(s[t]!=="default")u(n,r,s[t])}f(n,r);return n}})();Object.defineProperty(exports,"__esModule",{value:true});exports.DANGEROUS_ENV_KEYS=exports.INLINE_CODE_INTERPRETERS=exports.SHELL_DENYLIST=exports.SKILL_NAME_RE=void 0;exports.validateShellCommand=E;exports.validateEnvKeys=h;exports.normalizeCwd=d;exports.buildShellEnv=S;const a=l(require("path"));const i=l(require("fs"));exports.SKILL_NAME_RE=/^[a-z0-9][a-z0-9-]*$/;exports.SHELL_DENYLIST=new Set(["bash","sh","zsh","dash","fish","cmd","cmd.exe","powershell","powershell.exe","pwsh","pwsh.exe"]);exports.INLINE_CODE_INTERPRETERS=new Set(["python","python3","node","nodejs","deno","perl","ruby","php"]);const c=new Set(["-c","-e","--eval","--exec"]);exports.DANGEROUS_ENV_KEYS=new Set(["LD_PRELOAD","LD_LIBRARY_PATH","LD_AUDIT","DYLD_INSERT_LIBRARIES","DYLD_LIBRARY_PATH","DYLD_FRAMEWORK_PATH","NODE_OPTIONS"]);function _(e){return a.basename(e).toLowerCase()}function E(e){if(!Array.isArray(e)){return{ok:false,error:"shell_task.command must be an array of strings (argv), not a string"}}if(e.length===0){return{ok:false,error:"shell_task.command must not be empty"}}if(!e.every(s=>typeof s==="string")){return{ok:false,error:"shell_task.command must contain only strings"}}const r=_(e[0]);const n=r.replace(/\.exe$/,"");if(exports.SHELL_DENYLIST.has(r)||exports.SHELL_DENYLIST.has(n)){return{ok:false,error:`shell_task.command[0] "${e[0]}" is a blocked shell interpreter`}}if(exports.INLINE_CODE_INTERPRETERS.has(n)){const s=e.slice(1).some(t=>c.has(String(t)));if(s){return{ok:false,error:`shell_task.command "${n} ${e[1]}" uses an inline-code flag (escape vector blocked)`}}}return{ok:true}}function h(e){if(!e)return{ok:true};for(const r of Object.keys(e)){if(exports.DANGEROUS_ENV_KEYS.has(r)){return{ok:false,error:`shell_task.env key "${r}" is not allowed`}}}return{ok:true}}function d(e,r){let n;try{n=i.realpathSync(r)}catch(o){return{ok:false,error:`project root "${r}" is not accessible: ${o.message}`}}if(e===void 0||e===""){return{ok:true,resolved:n}}const s=a.resolve(n,e);let t;try{t=i.realpathSync(s)}catch(o){return{ok:false,error:`cwd "${e}" is not accessible: ${o.message}`}}if(t!==n&&!t.startsWith(n+a.sep)){return{ok:false,error:`cwd "${e}" resolves outside the project root`}}return{ok:true,resolved:t}}function S(e,r=process.env){const n=process.platform==="win32"?["PATH","Path","SystemRoot","COMSPEC","TEMP","TMP"]:["PATH","HOME","LANG","TMPDIR"];const s={};for(const t of n){if(r[t]!==void 0)s[t]=r[t]}if(e){for(const[t,o]of Object.entries(e)){s[t]=o}}return s}
@@ -0,0 +1,48 @@
1
+ import type { JsonObject, UiAuditSnapshotInput, UiInstance } from '@crewx/sdk';
2
+ import { type WorkflowApprovalState } from './card-state.js';
3
+ import { type WorkflowParentLifecycleChange, type WorkflowParentPersistTaskRepository, type WorkflowParentTaskKind } from './parent-task-sync.js';
4
+ import type { RunExecution, WorkflowSpec } from './types.js';
5
+ export interface WorkflowAuditSnapshotStore {
6
+ get(instanceId: string): UiInstance | null;
7
+ updateAuditSnapshot(input: UiAuditSnapshotInput): UiInstance | null;
8
+ }
9
+ export interface WorkflowPersistenceProjectionResult {
10
+ runId: string;
11
+ changedParentTaskIds: string[];
12
+ projectedAuditInstanceIds: string[];
13
+ errors: string[];
14
+ }
15
+ export interface WorkflowAuditSnapshotChange {
16
+ instanceId: string;
17
+ before: JsonObject;
18
+ after: WorkflowApprovalState;
19
+ }
20
+ export interface WorkflowPersistenceProjectionPreview {
21
+ runId: string;
22
+ runStatus: NonNullable<RunExecution['status']>;
23
+ parentChanges: WorkflowParentLifecycleChange[];
24
+ auditSnapshotChanges: WorkflowAuditSnapshotChange[];
25
+ errors: string[];
26
+ }
27
+ export interface WorkflowPersistenceProjectionPreviewOptions {
28
+ taskRepo: Pick<WorkflowParentPersistTaskRepository, 'findWorkflowParentTask'>;
29
+ workspaceId: string;
30
+ resolveWorkflowSpec: (run: RunExecution) => WorkflowSpec | null | undefined;
31
+ uiInstanceStore?: Pick<WorkflowAuditSnapshotStore, 'get'>;
32
+ getUiInstanceStore?: () => Pick<WorkflowAuditSnapshotStore, 'get'>;
33
+ parentKinds?: readonly WorkflowParentTaskKind[];
34
+ }
35
+ export interface WorkflowPersistenceProjectionOptions {
36
+ taskRepo: WorkflowParentPersistTaskRepository;
37
+ workspaceId: string;
38
+ resolveWorkflowSpec: (run: RunExecution) => WorkflowSpec | null | undefined;
39
+ uiInstanceStore?: WorkflowAuditSnapshotStore;
40
+ getUiInstanceStore?: () => WorkflowAuditSnapshotStore;
41
+ parentKinds?: readonly WorkflowParentTaskKind[];
42
+ onChanged?: (result: WorkflowPersistenceProjectionResult) => void;
43
+ warn?: (message: string) => void;
44
+ }
45
+ export type WorkflowPersistenceProjection = (run: RunExecution) => WorkflowPersistenceProjectionResult;
46
+ export declare function createWorkflowPersistenceProjection(options: WorkflowPersistenceProjectionOptions): WorkflowPersistenceProjection;
47
+ export declare function previewWorkflowPersistenceProjection(options: WorkflowPersistenceProjectionPreviewOptions, run: RunExecution): WorkflowPersistenceProjectionPreview;
48
+
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.createWorkflowPersistenceProjection=b;exports.previewWorkflowPersistenceProjection=m;const g=require("./card-state.js");const S=require("./parent-task-sync.js");function h(e){return e instanceof Error?e.message:String(e)}function _(e){return e}function w(e){if(Array.isArray(e))return`[${e.map(r=>w(r)).join(",")}]`;if(e!==null&&typeof e==="object"){const r=e;return`{${Object.keys(r).sort().map(i=>`${JSON.stringify(i)}:${w(r[i])}`).join(",")}}`}return JSON.stringify(e)??"null"}function A(e,r){return w(e)===w(r)}function P(e,r){return{runId:e,changedParentTaskIds:[...r.changedParentTaskIds],projectedAuditInstanceIds:[],errors:[]}}function b(e){const r={taskRepo:e.taskRepo,workspaceId:e.workspaceId,...e.parentKinds?{parentKinds:e.parentKinds}:{}};const i=(0,S.createWorkflowParentPersistSink)(r);const o=e.warn??(n=>console.warn(n));return n=>{let u;try{u=i(n)}catch(s){const t=`workflow persistence parent projection failed for ${n.id}: ${h(s)}`;o(t);return{runId:n.id,changedParentTaskIds:[],projectedAuditInstanceIds:[],errors:[t]}}const c=P(n.id,u);const d=(s,t)=>{const a=`workflow persistence ${s} failed for ${n.id}: ${h(t)}`;c.errors.push(a);o(a)};const p=[...new Set(u.linkedAuditInstanceIds)];if(p.length>0){let s;try{s=e.resolveWorkflowSpec(n);if(!s)throw new Error(`workflow spec not found for ${n.workflow_id}`)}catch(t){d("spec resolution",t)}if(s){let t=null;try{t=(0,g.buildWorkflowApprovalState)(n,s)}catch(a){d("audit state build",a)}if(t){const a=Date.parse(n.updated_at);if(!Number.isSafeInteger(a)||a<0){d("audit snapshot",new Error(`invalid run updated_at: ${n.updated_at}`))}else{let I=e.uiInstanceStore;if(!I&&e.getUiInstanceStore){try{I=e.getUiInstanceStore()}catch(l){d("audit store creation",l)}}if(!I){d("audit snapshot",new Error("no UiInstanceStore was provided"))}else{for(const l of p){try{const f=I.get(l);const $=f?.domainRef?.workflowRunId;if(!f||f.workspaceId!==e.workspaceId||$!==n.id){continue}const j=w(f.state);const y=f.updatedAt;const k=I.updateAuditSnapshot({instanceId:l,workspaceId:e.workspaceId,updatedAt:a,state:_(t)});if(k&&(w(k.state)!==j||k.updatedAt!==y)){c.projectedAuditInstanceIds.push(l)}}catch(f){d(`audit snapshot ${l}`,f)}}}}}}}if((c.changedParentTaskIds.length>0||c.projectedAuditInstanceIds.length>0)&&e.onChanged){try{e.onChanged(c)}catch(s){d("change callback",s)}}return c}}function m(e,r){const i=(0,S.previewWorkflowParentRepair)({taskRepo:e.taskRepo,run:r,workspaceId:e.workspaceId,...e.parentKinds?{parentKinds:e.parentKinds}:{}});const o={runId:r.id,runStatus:i.runStatus,parentChanges:[...i.changes],auditSnapshotChanges:[],errors:[]};const n=(t,a)=>{o.errors.push(`workflow persistence preview ${t} failed for ${r.id}: ${h(a)}`)};const u=[...new Set(i.linkedAuditInstanceIds)];if(u.length===0)return o;let c;try{c=e.resolveWorkflowSpec(r);if(!c)throw new Error(`workflow spec not found for ${r.workflow_id}`)}catch(t){n("spec resolution",t)}if(!c)return o;let d;try{d=(0,g.buildWorkflowApprovalState)(r,c)}catch(t){n("audit state build",t);return o}const p=Date.parse(r.updated_at);if(!Number.isSafeInteger(p)||p<0){n("audit snapshot",new Error(`invalid run updated_at: ${r.updated_at}`));return o}let s=e.uiInstanceStore;if(!s&&e.getUiInstanceStore){try{s=e.getUiInstanceStore()}catch(t){n("audit store creation",t)}}if(!s){n("audit snapshot",new Error("no UiInstanceStore was provided"));return o}for(const t of u){try{const a=s.get(t);if(!a||a.workspaceId!==e.workspaceId||a.domainRef.workflowRunId!==r.id){continue}if(!A(a.state,d)){o.auditSnapshotChanges.push({instanceId:t,before:a.state,after:d})}}catch(a){n(`audit snapshot ${t}`,a)}}void p;return o}
@@ -81,7 +81,7 @@
81
81
  },
82
82
  "cwd": {
83
83
  "type": "string",
84
- "description": "skill_task/shell_task: working directory, normalized and constrained under the project root."
84
+ "description": "agent_task/skill_task/shell_task: working directory, normalized and constrained under the project root."
85
85
  },
86
86
  "env": {
87
87
  "type": "object",
@@ -90,6 +90,10 @@
90
90
  },
91
91
  "description": "shell_task: explicit env pass-through. Dangerous keys (LD_PRELOAD, etc.) are rejected."
92
92
  },
93
+ "stdin": {
94
+ "type": "string",
95
+ "description": "skill_task/shell_task: optional stdin payload. Supports {{template}} interpolation."
96
+ },
93
97
  "mode": {
94
98
  "type": "string",
95
99
  "enum": ["query", "execute"]
@@ -218,19 +222,36 @@
218
222
  "required": ["max", "delay"],
219
223
  "properties": {
220
224
  "max": {
221
- "type": "number"
225
+ "type": "integer",
226
+ "minimum": 0,
227
+ "maximum": 10,
228
+ "description": "Number of additional retries after the first attempt (0 disables retry)."
222
229
  },
223
230
  "delay": {
224
231
  "type": "string",
225
232
  "enum": ["exponential", "fixed"]
226
233
  },
227
234
  "max_delay": {
228
- "type": "number"
235
+ "type": "integer",
236
+ "minimum": 0,
237
+ "description": "Maximum retry delay in milliseconds; runtime hard-caps this at 60000."
229
238
  },
230
239
  "on": {
231
240
  "type": "array",
232
241
  "items": {
233
- "type": "string"
242
+ "type": "string",
243
+ "enum": [
244
+ "CLI_UNRESOLVABLE",
245
+ "SPAWN_FAILED",
246
+ "NODE_TIMEOUT",
247
+ "NODE_EXIT_NONZERO",
248
+ "OUTPUT_INVALID",
249
+ "RATE_LIMITED",
250
+ "CONFIG_INVALID",
251
+ "INTERRUPTED",
252
+ "CANCELLED",
253
+ "UNKNOWN"
254
+ ]
234
255
  }
235
256
  }
236
257
  }
package/package.json CHANGED
@@ -1,10 +1,23 @@
1
1
  {
2
2
  "name": "@crewx/workflow",
3
- "version": "0.3.22-rc.21",
3
+ "version": "0.3.22-rc.210",
4
4
  "description": "Workflow engine for CrewX - list, show, validate workflows with Mermaid visualization",
5
5
  "type": "commonjs",
6
6
  "main": "dist/src/engine.js",
7
7
  "types": "dist/src/engine.d.ts",
8
+ "typesVersions": {
9
+ "*": {
10
+ "run-manager": [
11
+ "dist/src/run-manager.d.ts"
12
+ ],
13
+ "types": [
14
+ "dist/src/types.d.ts"
15
+ ],
16
+ "list": [
17
+ "dist/src/list-view.d.ts"
18
+ ]
19
+ }
20
+ },
8
21
  "exports": {
9
22
  ".": {
10
23
  "types": "./dist/src/engine.d.ts",
@@ -28,6 +41,12 @@
28
41
  "import": "./dist/cli.js",
29
42
  "require": "./dist/cli.js",
30
43
  "default": "./dist/cli.js"
44
+ },
45
+ "./list": {
46
+ "types": "./dist/src/list-view.d.ts",
47
+ "import": "./dist/src/list-view.js",
48
+ "require": "./dist/src/list-view.js",
49
+ "default": "./dist/src/list-view.js"
31
50
  }
32
51
  },
33
52
  "bin": {
@@ -48,9 +67,9 @@
48
67
  "dependencies": {
49
68
  "@hyperjump/json-schema": "^1.9.4",
50
69
  "js-yaml": "^4.1.0",
51
- "@crewx/knowledge-core": "0.1.17",
52
- "@crewx/sdk": "0.8.8-rc.40",
53
- "@crewx/shared": "0.0.5"
70
+ "@crewx/knowledge-core": "0.1.17-rc.174",
71
+ "@crewx/sdk": "0.9.0-rc.164",
72
+ "@crewx/shared": "0.0.6-rc.79"
54
73
  },
55
74
  "devDependencies": {
56
75
  "@types/js-yaml": "^4.0.9",