@workflow-code/cli 0.1.0
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/.env.example +5 -0
- package/README.md +8 -0
- package/dist/index.js +51 -0
- package/dist/local-XBULJ25O.js +337 -0
- package/dist/workspace-WJOCEGA6.js +1044 -0
- package/package.json +37 -0
package/.env.example
ADDED
package/README.md
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { pathToFileURL } from "url";
|
|
6
|
+
async function main(argv = process.argv.slice(2)) {
|
|
7
|
+
if (argv[0] === "workspace") {
|
|
8
|
+
const workspaceCli = await import("./workspace-WJOCEGA6.js");
|
|
9
|
+
await workspaceCli.main(argv.slice(1));
|
|
10
|
+
return typeof process.exitCode === "number" ? process.exitCode : 0;
|
|
11
|
+
}
|
|
12
|
+
if (argv.length === 0 || argv[0] === "help" || argv[0] === "--help" || argv[0] === "-h") {
|
|
13
|
+
process.stdout.write(`${formatHelp()}
|
|
14
|
+
`);
|
|
15
|
+
return 0;
|
|
16
|
+
}
|
|
17
|
+
const { register } = await import("tsx/esm/api");
|
|
18
|
+
register();
|
|
19
|
+
const localCli = await import("./local-XBULJ25O.js");
|
|
20
|
+
return localCli.main(argv);
|
|
21
|
+
}
|
|
22
|
+
function formatHelp() {
|
|
23
|
+
return [
|
|
24
|
+
"Usage:",
|
|
25
|
+
" workflow-code <run|json|structure|resolve-params> [workflow-dir] [args]",
|
|
26
|
+
" workflow-code workspace <command> [args]",
|
|
27
|
+
"",
|
|
28
|
+
"Local commands:",
|
|
29
|
+
" run Run a workflow locally",
|
|
30
|
+
" json Run locally and print the JSON report",
|
|
31
|
+
" structure Inspect workflow structure",
|
|
32
|
+
" resolve-params Resolve dynamic workflow parameters",
|
|
33
|
+
"",
|
|
34
|
+
"Workspace commands:",
|
|
35
|
+
" login, logout, status, health, pack, upload, preparation,",
|
|
36
|
+
" download, publish, run, debug-node, versions",
|
|
37
|
+
"",
|
|
38
|
+
"Run `workflow-code workspace --help` for workspace command details."
|
|
39
|
+
].join("\n");
|
|
40
|
+
}
|
|
41
|
+
if (process.argv[1] !== void 0 && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
|
|
42
|
+
main().then((exitCode) => {
|
|
43
|
+
process.exitCode = exitCode;
|
|
44
|
+
}).catch((error) => {
|
|
45
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
46
|
+
process.exitCode = 1;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
export {
|
|
50
|
+
main
|
|
51
|
+
};
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/local.ts
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { readFileSync } from "fs";
|
|
6
|
+
import { pathToFileURL } from "url";
|
|
7
|
+
import {
|
|
8
|
+
createCliOutputStreamHooks,
|
|
9
|
+
createWorkflowFailure,
|
|
10
|
+
formatWorkflowExecutionJson,
|
|
11
|
+
inspectLocalWorkflowStructure,
|
|
12
|
+
parseToolPermissionPoliciesJson,
|
|
13
|
+
resolveLocalWorkflowParams,
|
|
14
|
+
runLocalWorkflowWithReport
|
|
15
|
+
} from "workflow-code";
|
|
16
|
+
var WORKFLOW_APP_RUNTIME_EVENTS_ENV = "WORKFLOW_APP_RUNTIME_EVENTS";
|
|
17
|
+
var WORKFLOW_APP_RUNTIME_EVENT_PREFIX = "__WORKFLOW_CODE_RUNTIME_EVENT__";
|
|
18
|
+
var RESUME_RUNTIME_EVENT_AFTER_REQUEST_ID = "__workflowResumeRuntimeEventAfterRequestId";
|
|
19
|
+
async function main(argv = process.argv.slice(2)) {
|
|
20
|
+
disableAiSdkWarningLogs();
|
|
21
|
+
const abortController = createProcessAbortController();
|
|
22
|
+
const command = parseCommand(argv[0]);
|
|
23
|
+
const args = command === "help" ? argv.slice(1) : argv.slice(1);
|
|
24
|
+
if (command === "help") {
|
|
25
|
+
console.log(formatHelp());
|
|
26
|
+
return 0;
|
|
27
|
+
}
|
|
28
|
+
if (command === "structure") {
|
|
29
|
+
return runStructureCommand(args);
|
|
30
|
+
}
|
|
31
|
+
if (command === "resolve-params") {
|
|
32
|
+
return runResolveParamsCommand(args, abortController.signal);
|
|
33
|
+
}
|
|
34
|
+
const parsed = parseWorkflowArgs(args, {
|
|
35
|
+
json: command === "json"
|
|
36
|
+
});
|
|
37
|
+
const report = await runLocalWorkflowWithReport({
|
|
38
|
+
workflowDir: parsed.workflowDir,
|
|
39
|
+
args: parsed.args,
|
|
40
|
+
runId: parsed.runId,
|
|
41
|
+
conversationId: parsed.conversationId,
|
|
42
|
+
kvStoreDir: process.env.WORKFLOW_KV_STORE_DIR,
|
|
43
|
+
persistentValueStoreDir: process.env.WORKFLOW_PERSISTENT_VALUE_STORE_DIR,
|
|
44
|
+
persistentValueWorkflowName: process.env.WORKFLOW_PERSISTENT_VALUE_WORKFLOW_NAME,
|
|
45
|
+
fileStoreDir: parsed.fileStoreDir ?? process.env.WORKFLOW_FILE_STORE_DIR,
|
|
46
|
+
userInput: createCliUserInputContext(parsed.resolvedUserInputs),
|
|
47
|
+
toolPermissionPolicies: parsed.toolPermissionPolicies,
|
|
48
|
+
fileCreateStoreDir: process.env.WORKFLOW_FILE_CREATE_STORE_DIR,
|
|
49
|
+
nodeHooks: parsed.json ? void 0 : createCliOutputStreamHooks(),
|
|
50
|
+
onRuntimeEvent: parsed.json ? createAppRuntimeEventEmitter() : void 0,
|
|
51
|
+
abortSignal: abortController.signal
|
|
52
|
+
});
|
|
53
|
+
if (parsed.json) {
|
|
54
|
+
console.log(formatWorkflowExecutionJson(report));
|
|
55
|
+
} else if (report.status === "failed") {
|
|
56
|
+
console.error(formatWorkflowExecutionJson(report));
|
|
57
|
+
}
|
|
58
|
+
return getReportExitCode(report);
|
|
59
|
+
}
|
|
60
|
+
function createProcessAbortController() {
|
|
61
|
+
const controller = new AbortController();
|
|
62
|
+
let forcedExit;
|
|
63
|
+
const abort = () => {
|
|
64
|
+
if (!controller.signal.aborted) {
|
|
65
|
+
controller.abort(new Error("Workflow CLI process was interrupted."));
|
|
66
|
+
forcedExit = setTimeout(() => {
|
|
67
|
+
process.exit(130);
|
|
68
|
+
}, 5e3);
|
|
69
|
+
forcedExit.unref?.();
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
process.once("SIGINT", abort);
|
|
73
|
+
process.once("SIGTERM", abort);
|
|
74
|
+
process.once("beforeExit", () => clearTimeout(forcedExit));
|
|
75
|
+
return controller;
|
|
76
|
+
}
|
|
77
|
+
function parseCommand(value) {
|
|
78
|
+
if (value === void 0 || value === "--help" || value === "-h" || value === "help") {
|
|
79
|
+
return "help";
|
|
80
|
+
}
|
|
81
|
+
if (value === "run" || value === "json" || value === "structure" || value === "resolve-params") {
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
throw new Error(`Unknown workflow-code command: ${value}`);
|
|
85
|
+
}
|
|
86
|
+
async function runResolveParamsCommand(argv, abortSignal) {
|
|
87
|
+
const parsed = parseWorkflowArgs(argv, { json: true });
|
|
88
|
+
const request = parseParamResolveRequest(readFileSync(0, "utf8"));
|
|
89
|
+
const result = await resolveLocalWorkflowParams({
|
|
90
|
+
workflowDir: parsed.workflowDir,
|
|
91
|
+
request,
|
|
92
|
+
abortSignal
|
|
93
|
+
});
|
|
94
|
+
console.log(JSON.stringify(result));
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
97
|
+
function parseParamResolveRequest(value) {
|
|
98
|
+
const parsed = JSON.parse(value);
|
|
99
|
+
if (typeof parsed !== "object" || parsed === null || typeof parsed.draftText !== "string" || !Array.isArray(parsed.args) || !parsed.args.every((arg) => typeof arg === "string") || parsed.inputMode !== "form" && parsed.inputMode !== "raw" || typeof parsed.trigger !== "object" || parsed.trigger === null || parsed.trigger.kind !== "init" && parsed.trigger.kind !== "input" && parsed.trigger.kind !== "param" || parsed.trigger.kind === "param" && typeof parsed.trigger.paramName !== "string") {
|
|
100
|
+
throw new Error("resolve-params stdin must contain a valid WorkflowParamResolveRequest JSON object.");
|
|
101
|
+
}
|
|
102
|
+
return parsed;
|
|
103
|
+
}
|
|
104
|
+
function parseWorkflowArgs(argv, options) {
|
|
105
|
+
let json = options.json;
|
|
106
|
+
let runId;
|
|
107
|
+
let conversationId;
|
|
108
|
+
let fileStoreDir;
|
|
109
|
+
let resolvedUserInputs = [];
|
|
110
|
+
let toolPermissionPolicies;
|
|
111
|
+
const filteredArgv = [];
|
|
112
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
113
|
+
const arg = argv[index];
|
|
114
|
+
if (arg === "--") {
|
|
115
|
+
filteredArgv.push(...argv.slice(index));
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
if (arg === "--json") {
|
|
119
|
+
json = true;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (arg === "--conversation-id") {
|
|
123
|
+
conversationId = readWorkflowArgValue(argv, ++index, "--conversation-id");
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (arg === "--run-id") {
|
|
127
|
+
runId = readWorkflowArgValue(argv, ++index, "--run-id");
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (arg === "--file-store-dir") {
|
|
131
|
+
fileStoreDir = readWorkflowArgValue(argv, ++index, "--file-store-dir");
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (arg === "--resolved-user-inputs-json") {
|
|
135
|
+
resolvedUserInputs = parseResolvedUserInputs(
|
|
136
|
+
readWorkflowArgValue(argv, ++index, "--resolved-user-inputs-json")
|
|
137
|
+
);
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (arg === "--tool-permissions-json") {
|
|
141
|
+
toolPermissionPolicies = parseToolPermissionPoliciesJson(
|
|
142
|
+
readWorkflowArgValue(argv, ++index, "--tool-permissions-json")
|
|
143
|
+
);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
filteredArgv.push(arg);
|
|
147
|
+
}
|
|
148
|
+
const [workflowDirArg, ...rest] = filteredArgv;
|
|
149
|
+
if (workflowDirArg !== void 0 && workflowDirArg !== "--" && !workflowDirArg.startsWith("-")) {
|
|
150
|
+
return {
|
|
151
|
+
workflowDir: path.resolve(process.cwd(), workflowDirArg),
|
|
152
|
+
args: removePnpmRunSeparator(rest),
|
|
153
|
+
json,
|
|
154
|
+
runId,
|
|
155
|
+
conversationId,
|
|
156
|
+
fileStoreDir,
|
|
157
|
+
resolvedUserInputs,
|
|
158
|
+
toolPermissionPolicies
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
workflowDir: process.cwd(),
|
|
163
|
+
args: removePnpmRunSeparator(filteredArgv),
|
|
164
|
+
json,
|
|
165
|
+
runId,
|
|
166
|
+
conversationId,
|
|
167
|
+
fileStoreDir,
|
|
168
|
+
resolvedUserInputs,
|
|
169
|
+
toolPermissionPolicies
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
function createCliUserInputContext(resolvedInputs) {
|
|
173
|
+
return {
|
|
174
|
+
[RESUME_RUNTIME_EVENT_AFTER_REQUEST_ID]: resolvedInputs.at(-1)?.requestId,
|
|
175
|
+
getResolvedInput(requestId) {
|
|
176
|
+
return resolvedInputs.find((input) => input.requestId === requestId);
|
|
177
|
+
},
|
|
178
|
+
requestInput() {
|
|
179
|
+
throw new Error(
|
|
180
|
+
"Workflow is waiting for user input. Re-run with --run-id and --resolved-user-inputs-json to resume."
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function parseResolvedUserInputs(value) {
|
|
186
|
+
const parsed = JSON.parse(value);
|
|
187
|
+
if (!Array.isArray(parsed)) {
|
|
188
|
+
throw new Error("--resolved-user-inputs-json must be an array.");
|
|
189
|
+
}
|
|
190
|
+
return parsed.map((item) => {
|
|
191
|
+
if (typeof item !== "object" || item === null || typeof item.requestId !== "string" || typeof item.nodeName !== "string" || typeof item.values !== "object" || item.values === null || typeof item.submittedAt !== "string") {
|
|
192
|
+
throw new Error("Invalid resolved user input item.");
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
requestId: item.requestId,
|
|
196
|
+
nodeName: item.nodeName,
|
|
197
|
+
values: item.values,
|
|
198
|
+
submittedAt: new Date(item.submittedAt)
|
|
199
|
+
};
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
function createAppRuntimeEventEmitter() {
|
|
203
|
+
if (process.env[WORKFLOW_APP_RUNTIME_EVENTS_ENV] !== "1") {
|
|
204
|
+
return void 0;
|
|
205
|
+
}
|
|
206
|
+
return (event) => {
|
|
207
|
+
process.stdout.write(
|
|
208
|
+
`${WORKFLOW_APP_RUNTIME_EVENT_PREFIX}${JSON.stringify(event, createRuntimeEventJsonReplacer())}
|
|
209
|
+
`
|
|
210
|
+
);
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
function createRuntimeEventJsonReplacer() {
|
|
214
|
+
const ancestors = [];
|
|
215
|
+
return function replacer(_key, value) {
|
|
216
|
+
if (typeof value === "bigint") {
|
|
217
|
+
return `${value.toString()}n`;
|
|
218
|
+
}
|
|
219
|
+
if (typeof value === "function") {
|
|
220
|
+
return `[Function ${value.name || "anonymous"}]`;
|
|
221
|
+
}
|
|
222
|
+
if (typeof value === "symbol") {
|
|
223
|
+
return value.toString();
|
|
224
|
+
}
|
|
225
|
+
if (typeof value === "object" && value !== null) {
|
|
226
|
+
while (ancestors.length > 0 && ancestors.at(-1) !== this) {
|
|
227
|
+
ancestors.pop();
|
|
228
|
+
}
|
|
229
|
+
if (ancestors.includes(value)) {
|
|
230
|
+
return "[Circular]";
|
|
231
|
+
}
|
|
232
|
+
ancestors.push(value);
|
|
233
|
+
}
|
|
234
|
+
return value;
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
function readWorkflowArgValue(argv, index, name) {
|
|
238
|
+
const value = argv[index];
|
|
239
|
+
if (value === void 0) {
|
|
240
|
+
throw new Error(`Missing value for ${name}.`);
|
|
241
|
+
}
|
|
242
|
+
return value;
|
|
243
|
+
}
|
|
244
|
+
function runStructureCommand(argv) {
|
|
245
|
+
const parsed = parseWorkflowArgs(argv, { json: true });
|
|
246
|
+
try {
|
|
247
|
+
const report = inspectLocalWorkflowStructure({
|
|
248
|
+
workflowDir: parsed.workflowDir
|
|
249
|
+
});
|
|
250
|
+
console.log(JSON.stringify(report, null, 2));
|
|
251
|
+
return 0;
|
|
252
|
+
} catch (error) {
|
|
253
|
+
console.log(formatWorkflowStructureError(error));
|
|
254
|
+
return 1;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function removePnpmRunSeparator(argv) {
|
|
258
|
+
const separatorIndex = argv.indexOf("--");
|
|
259
|
+
if (separatorIndex < 0) {
|
|
260
|
+
return argv;
|
|
261
|
+
}
|
|
262
|
+
return [
|
|
263
|
+
...argv.slice(0, separatorIndex),
|
|
264
|
+
...argv.slice(separatorIndex + 1)
|
|
265
|
+
];
|
|
266
|
+
}
|
|
267
|
+
function getReportExitCode(report) {
|
|
268
|
+
if (report.status === "failed") {
|
|
269
|
+
return 1;
|
|
270
|
+
}
|
|
271
|
+
const result = report.result;
|
|
272
|
+
if (result.ok && typeof result.data === "object" && result.data !== null && "errCode" in result.data && typeof result.data.errCode === "number") {
|
|
273
|
+
return result.data.errCode === 0 ? 0 : 1;
|
|
274
|
+
}
|
|
275
|
+
return 0;
|
|
276
|
+
}
|
|
277
|
+
function formatWorkflowStructureError(error) {
|
|
278
|
+
return JSON.stringify(
|
|
279
|
+
{
|
|
280
|
+
workflow: {
|
|
281
|
+
name: ""
|
|
282
|
+
},
|
|
283
|
+
definitions: {
|
|
284
|
+
nodes: {},
|
|
285
|
+
apiCalls: [],
|
|
286
|
+
variables: {}
|
|
287
|
+
},
|
|
288
|
+
execution: {
|
|
289
|
+
kind: "sequence",
|
|
290
|
+
children: []
|
|
291
|
+
},
|
|
292
|
+
diagnostics: {
|
|
293
|
+
warnings: [],
|
|
294
|
+
violations: [],
|
|
295
|
+
error: createWorkflowFailure(error).error
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
null,
|
|
299
|
+
2
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
function formatHelp() {
|
|
303
|
+
return [
|
|
304
|
+
"Usage:",
|
|
305
|
+
" workflow-code run [workflow-dir] [--] [...args]",
|
|
306
|
+
" workflow-code json [workflow-dir] [--] [...args]",
|
|
307
|
+
" workflow-code json [workflow-dir] --conversation-id <id> [--] [...args]",
|
|
308
|
+
" workflow-code structure [workflow-dir]",
|
|
309
|
+
" workflow-code resolve-params [workflow-dir] < request.json",
|
|
310
|
+
"",
|
|
311
|
+
"Examples:",
|
|
312
|
+
" workflow-code run .",
|
|
313
|
+
" workflow-code run . -- --target aliyun --dry-run",
|
|
314
|
+
" workflow-code json . -- --target aliyun --dry-run",
|
|
315
|
+
" workflow-code json . --run-id <id> --resolved-user-inputs-json <json> -- [...args]",
|
|
316
|
+
" workflow-code structure .",
|
|
317
|
+
" workflow-code resolve-params . < request.json"
|
|
318
|
+
].join("\n");
|
|
319
|
+
}
|
|
320
|
+
function disableAiSdkWarningLogs() {
|
|
321
|
+
globalThis.AI_SDK_LOG_WARNINGS = false;
|
|
322
|
+
}
|
|
323
|
+
if (isCliEntrypoint()) {
|
|
324
|
+
main().then((exitCode) => {
|
|
325
|
+
process.exitCode = exitCode;
|
|
326
|
+
}).catch((error) => {
|
|
327
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
328
|
+
process.exitCode = 1;
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
function isCliEntrypoint() {
|
|
332
|
+
return process.argv[1] !== void 0 && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href;
|
|
333
|
+
}
|
|
334
|
+
export {
|
|
335
|
+
main,
|
|
336
|
+
parseWorkflowArgs
|
|
337
|
+
};
|