@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 ADDED
@@ -0,0 +1,5 @@
1
+ WORKFLOW_SERVER_URL=http://localhost:7125
2
+ WORKFLOW_SERVER_ADMIN_KEY=replace-with-admin-key
3
+ WORKFLOW_BOOTSTRAP_ADMIN_EMAIL=admin@example.com
4
+ WORKFLOW_BOOTSTRAP_ADMIN_PASSWORD=replace-with-admin-password
5
+ WORKFLOW_REPO_ROOT=
package/README.md ADDED
@@ -0,0 +1,8 @@
1
+ # @workflow-code/cli
2
+
3
+ Unified local and server workspace CLI for Workflow Code projects.
4
+
5
+ ```bash
6
+ pnpm add -D workflow-code @workflow-code/cli
7
+ workflow-code --help
8
+ ```
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
+ };