@fieldwangai/agentflow 0.1.25

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 (138) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +201 -0
  3. package/README.zh-CN.md +201 -0
  4. package/agents/agentflow-node-executor-code.md +32 -0
  5. package/agents/agentflow-node-executor-planning.md +32 -0
  6. package/agents/agentflow-node-executor-requirement.md +32 -0
  7. package/agents/agentflow-node-executor-test.md +32 -0
  8. package/agents/agentflow-node-executor-ui.md +32 -0
  9. package/agents/agentflow-node-executor.md +32 -0
  10. package/agents/agents.json +8 -0
  11. package/agents/en/agentflow-node-executor.md +32 -0
  12. package/agents/zh/agentflow-node-executor.md +32 -0
  13. package/bin/agentflow.mjs +52 -0
  14. package/bin/ensure-workspace-reference.mjs +35 -0
  15. package/bin/lib/agent-runners.mjs +1199 -0
  16. package/bin/lib/agents-path.mjs +61 -0
  17. package/bin/lib/api-runner.mjs +361 -0
  18. package/bin/lib/apply.mjs +852 -0
  19. package/bin/lib/catalog-agents.mjs +300 -0
  20. package/bin/lib/catalog-flows.mjs +532 -0
  21. package/bin/lib/composer-agent.mjs +884 -0
  22. package/bin/lib/composer-flow-instances.mjs +68 -0
  23. package/bin/lib/composer-flow-skeleton.mjs +334 -0
  24. package/bin/lib/composer-flow-validate.mjs +47 -0
  25. package/bin/lib/composer-log.mjs +197 -0
  26. package/bin/lib/composer-model-router.mjs +160 -0
  27. package/bin/lib/composer-node-schema.mjs +299 -0
  28. package/bin/lib/composer-planner.mjs +749 -0
  29. package/bin/lib/composer-script-ops.mjs +233 -0
  30. package/bin/lib/composer-skill-router.mjs +384 -0
  31. package/bin/lib/flow-import.mjs +305 -0
  32. package/bin/lib/flow-normalize.mjs +71 -0
  33. package/bin/lib/flow-write.mjs +395 -0
  34. package/bin/lib/help.mjs +139 -0
  35. package/bin/lib/hub-login.mjs +54 -0
  36. package/bin/lib/hub-publish.mjs +159 -0
  37. package/bin/lib/hub-remote.mjs +189 -0
  38. package/bin/lib/hub.mjs +299 -0
  39. package/bin/lib/i18n.mjs +233 -0
  40. package/bin/lib/locales/en.json +344 -0
  41. package/bin/lib/locales/zh.json +344 -0
  42. package/bin/lib/log.mjs +37 -0
  43. package/bin/lib/main.mjs +611 -0
  44. package/bin/lib/model-config.mjs +118 -0
  45. package/bin/lib/model-lists.mjs +188 -0
  46. package/bin/lib/node-exec-context.mjs +336 -0
  47. package/bin/lib/node-execute.mjs +513 -0
  48. package/bin/lib/normalize-node-tool-command.mjs +97 -0
  49. package/bin/lib/paths.mjs +216 -0
  50. package/bin/lib/pipeline-scripts.mjs +41 -0
  51. package/bin/lib/recent-runs.mjs +173 -0
  52. package/bin/lib/run-apply-active-lock.mjs +82 -0
  53. package/bin/lib/run-events.mjs +85 -0
  54. package/bin/lib/run-node-statuses-from-disk.mjs +85 -0
  55. package/bin/lib/schedule-config.mjs +227 -0
  56. package/bin/lib/scheduler.mjs +312 -0
  57. package/bin/lib/table.mjs +4 -0
  58. package/bin/lib/terminal.mjs +42 -0
  59. package/bin/lib/ui-print.mjs +94 -0
  60. package/bin/lib/ui-server.mjs +2113 -0
  61. package/bin/lib/workspace-tree.mjs +266 -0
  62. package/bin/lib/workspace.mjs +180 -0
  63. package/bin/pipeline/build-node-prompt.mjs +179 -0
  64. package/bin/pipeline/check-cache.mjs +191 -0
  65. package/bin/pipeline/check-flow.mjs +543 -0
  66. package/bin/pipeline/collect-nodes.mjs +212 -0
  67. package/bin/pipeline/compute-cache-md5.mjs +177 -0
  68. package/bin/pipeline/ensure-run-dir.mjs +71 -0
  69. package/bin/pipeline/extract-thinking.mjs +308 -0
  70. package/bin/pipeline/gc.mjs +129 -0
  71. package/bin/pipeline/get-env.mjs +83 -0
  72. package/bin/pipeline/get-exec-id.mjs +145 -0
  73. package/bin/pipeline/get-ready-nodes.mjs +435 -0
  74. package/bin/pipeline/get-resolved-values.mjs +337 -0
  75. package/bin/pipeline/load-key.mjs +62 -0
  76. package/bin/pipeline/parse-bool.mjs +33 -0
  77. package/bin/pipeline/parse-flow.mjs +698 -0
  78. package/bin/pipeline/post-process-control-if.mjs +23 -0
  79. package/bin/pipeline/post-process-node.mjs +490 -0
  80. package/bin/pipeline/pre-process-node.mjs +449 -0
  81. package/bin/pipeline/resolve-inputs.mjs +201 -0
  82. package/bin/pipeline/run-log.mjs +34 -0
  83. package/bin/pipeline/run-tool-nodejs.mjs +160 -0
  84. package/bin/pipeline/save-key.mjs +93 -0
  85. package/bin/pipeline/snapshot-prior-round.mjs +70 -0
  86. package/bin/pipeline/validate-flow.mjs +825 -0
  87. package/bin/pipeline/validate-for-ui.mjs +226 -0
  88. package/bin/pipeline/validate-script-output.mjs +130 -0
  89. package/bin/pipeline/write-result.mjs +182 -0
  90. package/builtin/nodes/agent_subAgent.md +14 -0
  91. package/builtin/nodes/control_agent_toBool.md +20 -0
  92. package/builtin/nodes/control_anyOne.md +17 -0
  93. package/builtin/nodes/control_end.md +11 -0
  94. package/builtin/nodes/control_if.md +20 -0
  95. package/builtin/nodes/control_start.md +11 -0
  96. package/builtin/nodes/control_toBool.md +21 -0
  97. package/builtin/nodes/provide_file.md +11 -0
  98. package/builtin/nodes/provide_str.md +11 -0
  99. package/builtin/nodes/tool_get_env.md +14 -0
  100. package/builtin/nodes/tool_load_key.md +20 -0
  101. package/builtin/nodes/tool_nodejs.md +40 -0
  102. package/builtin/nodes/tool_print.md +14 -0
  103. package/builtin/nodes/tool_save_key.md +20 -0
  104. package/builtin/nodes/tool_user_ask.md +23 -0
  105. package/builtin/nodes/tool_user_check.md +22 -0
  106. package/builtin/pipelines/module-migrate/flow.yaml +819 -0
  107. package/builtin/pipelines/module-migrate/scripts/check_imports.mjs +700 -0
  108. package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Makefile +362 -0
  109. package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/.deps/Release/obj.target/node_modules/node-addon-api/node_addon_api_except.stamp.d +1 -0
  110. package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/.deps/Release/obj.target/tree_sitter_kotlin_binding/bindings/node/binding.o.d +17 -0
  111. package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/.deps/Release/obj.target/tree_sitter_kotlin_binding/src/parser.o.d +5 -0
  112. package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/.deps/Release/obj.target/tree_sitter_kotlin_binding/src/scanner.o.d +8 -0
  113. package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/.deps/Release/tree_sitter_kotlin_binding.node.d +1 -0
  114. package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/obj.target/node_modules/node-addon-api/node_addon_api_except.stamp +0 -0
  115. package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/obj.target/tree_sitter_kotlin_binding/bindings/node/binding.o +0 -0
  116. package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/obj.target/tree_sitter_kotlin_binding/src/parser.o +0 -0
  117. package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/obj.target/tree_sitter_kotlin_binding/src/scanner.o +0 -0
  118. package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/tree_sitter_kotlin_binding.node +0 -0
  119. package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/binding.Makefile +6 -0
  120. package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/gyp-mac-tool +768 -0
  121. package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/node_modules/node-addon-api/node_addon_api.Makefile +6 -0
  122. package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/node_modules/node-addon-api/node_addon_api.target.mk +122 -0
  123. package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/node_modules/node-addon-api/node_addon_api_except.target.mk +126 -0
  124. package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/node_modules/node-addon-api/node_addon_api_maybe.target.mk +122 -0
  125. package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/tree_sitter_kotlin_binding.target.mk +203 -0
  126. package/builtin/pipelines/new/flow.yaml +545 -0
  127. package/builtin/pipelines/new/scripts/check-flow.mjs +9 -0
  128. package/builtin/pipelines/new/scripts/collect-nodes.mjs +211 -0
  129. package/builtin/pipelines/scripts/adjust-node-positions.mjs +113 -0
  130. package/builtin/web-ui/dist/agentflow-icon.svg +23 -0
  131. package/builtin/web-ui/dist/assets/index-CZkUPcXE.css +1 -0
  132. package/builtin/web-ui/dist/assets/index-DkkhNESc.js +190 -0
  133. package/builtin/web-ui/dist/index.html +24 -0
  134. package/package.json +67 -0
  135. package/reference/flow-control-capabilities.md +274 -0
  136. package/reference/flow-layout.md +84 -0
  137. package/reference/flow-prompt-handler-check.md +12 -0
  138. package/reference/flow-result-semantics.md +14 -0
@@ -0,0 +1,611 @@
1
+ import { spawn } from "child_process";
2
+ import path from "path";
3
+ import chalk from "chalk";
4
+ import { apply, replay, resume } from "./apply.mjs";
5
+ import {
6
+ addRoleJson,
7
+ copyBuiltinAgentJson,
8
+ copyBuiltinJson,
9
+ listAgentsJson,
10
+ listAgentsTable,
11
+ readAgentJson,
12
+ } from "./catalog-agents.mjs";
13
+ import {
14
+ listFlowsJson,
15
+ listNodesJson,
16
+ listPipelines,
17
+ printNodesTable,
18
+ readFlowJson,
19
+ readNodeJson,
20
+ } from "./catalog-flows.mjs";
21
+ import { writeFlowYaml } from "./flow-write.mjs";
22
+ import { printHelp } from "./help.mjs";
23
+ import { LOG_LEVELS, log, setLogLevel, setMachineReadable } from "./log.mjs";
24
+ import { updateModelLists } from "./model-lists.mjs";
25
+ import { APPLY_AI_STEPS, LEGACY_PIPELINES_DIR, PIPELINES_DIR, USER_AGENTFLOW_PIPELINES_LABEL } from "./paths.mjs";
26
+ import { isValidUuid, runNodeScript } from "./pipeline-scripts.mjs";
27
+ import { Table } from "./table.mjs";
28
+ import { ensureReference, findFlowNameByUuid, getFlowDir, listRunsWithLogs } from "./workspace.mjs";
29
+ import { startUiServer } from "./ui-server.mjs";
30
+ import { hubLogin, hubLogout } from "./hub-login.mjs";
31
+ import { hubPublish } from "./hub-publish.mjs";
32
+ import { hubListRemote, hubDownload } from "./hub-remote.mjs";
33
+ import { listScheduleStatuses, startScheduler } from "./scheduler.mjs";
34
+
35
+ async function readStdin() {
36
+ const chunks = [];
37
+ for await (const chunk of process.stdin) chunks.push(chunk);
38
+ return Buffer.concat(chunks).toString("utf8");
39
+ }
40
+
41
+ export async function main() {
42
+ const argv = process.argv.slice(2);
43
+ let workspaceRoot = process.cwd();
44
+ const shift = () => argv.shift();
45
+ const wrIdx = argv.indexOf("--workspace-root");
46
+ if (wrIdx >= 0 && argv[wrIdx + 1]) {
47
+ workspaceRoot = path.resolve(argv[wrIdx + 1]);
48
+ argv.splice(wrIdx, 2);
49
+ }
50
+ while (argv[0] === "--workspace-root") {
51
+ shift();
52
+ workspaceRoot = path.resolve(shift() || "");
53
+ }
54
+ if (argv.includes("--help") || argv.includes("-h")) {
55
+ printHelp();
56
+ process.exit(0);
57
+ }
58
+ const dryRun = argv.includes("--dry-run");
59
+ if (dryRun) argv.splice(argv.indexOf("--dry-run"), 1);
60
+ if (argv.includes("--debug")) {
61
+ setLogLevel(LOG_LEVELS.debug);
62
+ argv.splice(argv.indexOf("--debug"), 1);
63
+ }
64
+ let force = true;
65
+ if (argv.includes("--no-force")) {
66
+ force = false;
67
+ argv.splice(argv.indexOf("--no-force"), 1);
68
+ }
69
+ if (argv.includes("--force")) {
70
+ force = true;
71
+ argv.splice(argv.indexOf("--force"), 1);
72
+ }
73
+ if (argv.includes("--yolo")) {
74
+ force = true;
75
+ argv.splice(argv.indexOf("--yolo"), 1);
76
+ }
77
+ let parallel = false;
78
+ if (argv.includes("--parallel")) {
79
+ parallel = true;
80
+ argv.splice(argv.indexOf("--parallel"), 1);
81
+ }
82
+ if (argv.includes("--no-parallel")) {
83
+ parallel = false;
84
+ argv.splice(argv.indexOf("--no-parallel"), 1);
85
+ }
86
+ if (argv.includes("--machine-readable")) {
87
+ setMachineReadable(true);
88
+ argv.splice(argv.indexOf("--machine-readable"), 1);
89
+ }
90
+ const jsonMode = argv.includes("--json");
91
+ const cliInputs = {};
92
+ while (argv.includes("--input")) {
93
+ const idx = argv.indexOf("--input");
94
+ const pair = argv[idx + 1];
95
+ if (!pair || !pair.includes("=")) {
96
+ throw new Error("Invalid --input format. Use: --input name=value");
97
+ }
98
+ const eqIdx = pair.indexOf("=");
99
+ const name = pair.slice(0, eqIdx);
100
+ const value = pair.slice(eqIdx + 1);
101
+ if (value.startsWith("file:")) {
102
+ cliInputs[name] = { type: "file", path: value.slice(5) };
103
+ } else {
104
+ cliInputs[name] = { type: "str", value };
105
+ }
106
+ argv.splice(idx, 2);
107
+ }
108
+ const sub = shift();
109
+ if (!sub) {
110
+ printHelp();
111
+ process.exit(1);
112
+ }
113
+ if (sub === "update-model-lists") {
114
+ const result = await updateModelLists(workspaceRoot);
115
+ if (jsonMode) process.stdout.write(JSON.stringify(result) + "\n");
116
+ process.exit(0);
117
+ }
118
+ const jsonOnlySubs = [
119
+ "list-flows",
120
+ "list-nodes",
121
+ "read-flow",
122
+ "write-flow",
123
+ "read-node",
124
+ "copy-builtin",
125
+ "list-agents",
126
+ "copy-builtin-agent",
127
+ "read-agent",
128
+ "add-role",
129
+ ];
130
+ if (jsonMode && jsonOnlySubs.includes(sub)) {
131
+ argv.splice(argv.indexOf("--json"), 1);
132
+ }
133
+ let agentModel = process.env.CURSOR_AGENT_MODEL || null;
134
+ const modelIdx = argv.indexOf("--model");
135
+ if (modelIdx >= 0 && argv[modelIdx + 1]) {
136
+ agentModel = argv[modelIdx + 1];
137
+ argv.splice(modelIdx, 2);
138
+ }
139
+ if (sub === "list-flows" && jsonMode) {
140
+ const list = listFlowsJson(workspaceRoot);
141
+ process.stdout.write(JSON.stringify(list) + "\n");
142
+ process.exit(0);
143
+ }
144
+ if (sub === "list-nodes" && jsonMode) {
145
+ let flowId, flowSource;
146
+ const flowIdIdx = argv.indexOf("--flow-id");
147
+ if (flowIdIdx >= 0 && argv[flowIdIdx + 1]) {
148
+ flowId = argv[flowIdIdx + 1];
149
+ argv.splice(flowIdIdx, 2);
150
+ }
151
+ const flowSourceIdx = argv.indexOf("--flow-source");
152
+ if (flowSourceIdx >= 0 && argv[flowSourceIdx + 1]) {
153
+ flowSource = argv[flowSourceIdx + 1];
154
+ argv.splice(flowSourceIdx, 2);
155
+ }
156
+ const list = listNodesJson(workspaceRoot, flowId, flowSource);
157
+ process.stdout.write(JSON.stringify(list) + "\n");
158
+ process.exit(0);
159
+ }
160
+ if (sub === "list-nodes" && !jsonMode) {
161
+ let flowId, flowSource;
162
+ const flowIdIdx = argv.indexOf("--flow-id");
163
+ if (flowIdIdx >= 0 && argv[flowIdIdx + 1]) {
164
+ flowId = argv[flowIdIdx + 1];
165
+ argv.splice(flowIdIdx, 2);
166
+ }
167
+ const flowSourceIdx = argv.indexOf("--flow-source");
168
+ if (flowSourceIdx >= 0 && argv[flowSourceIdx + 1]) {
169
+ flowSource = argv[flowSourceIdx + 1];
170
+ argv.splice(flowSourceIdx, 2);
171
+ }
172
+ const list = listNodesJson(workspaceRoot, flowId, flowSource);
173
+ printNodesTable(list);
174
+ process.exit(0);
175
+ }
176
+ if (sub === "read-flow" && jsonMode) {
177
+ let flowSource = "user";
178
+ const flowSourceIdx = argv.indexOf("--flow-source");
179
+ if (flowSourceIdx >= 0 && argv[flowSourceIdx + 1]) {
180
+ flowSource = argv[flowSourceIdx + 1];
181
+ argv.splice(flowSourceIdx, 2);
182
+ }
183
+ const flowId = argv.find((a) => !a.startsWith("--"));
184
+ if (!flowId) {
185
+ process.stdout.write(JSON.stringify({ error: "Missing flowId" }) + "\n");
186
+ process.exit(1);
187
+ }
188
+ const result = readFlowJson(workspaceRoot, flowId, flowSource);
189
+ process.stdout.write(JSON.stringify(result) + "\n");
190
+ process.exit(result.error ? 1 : 0);
191
+ }
192
+ if (sub === "read-node" && jsonMode) {
193
+ let flowId, flowSource;
194
+ const flowIdIdx = argv.indexOf("--flow-id");
195
+ if (flowIdIdx >= 0 && argv[flowIdIdx + 1]) {
196
+ flowId = argv[flowIdIdx + 1];
197
+ argv.splice(flowIdIdx, 2);
198
+ }
199
+ const flowSourceIdx = argv.indexOf("--flow-source");
200
+ if (flowSourceIdx >= 0 && argv[flowSourceIdx + 1]) {
201
+ flowSource = argv[flowSourceIdx + 1];
202
+ argv.splice(flowSourceIdx, 2);
203
+ }
204
+ const nodeId = argv.find((a) => !a.startsWith("--"));
205
+ if (!nodeId) {
206
+ process.stdout.write(JSON.stringify({ error: "Missing nodeId" }) + "\n");
207
+ process.exit(1);
208
+ }
209
+ const result = readNodeJson(workspaceRoot, nodeId, flowId, flowSource);
210
+ process.stdout.write(JSON.stringify(result) + "\n");
211
+ process.exit(result.error ? 1 : 0);
212
+ }
213
+ if (sub === "copy-builtin" && jsonMode) {
214
+ const flowId = shift();
215
+ let targetFlowId;
216
+ const targetIdx = argv.indexOf("--target");
217
+ if (targetIdx >= 0 && argv[targetIdx + 1]) targetFlowId = argv[targetIdx + 1];
218
+ if (!flowId) {
219
+ process.stdout.write(JSON.stringify({ success: false, error: "Missing flowId" }) + "\n");
220
+ process.exit(1);
221
+ }
222
+ const result = copyBuiltinJson(workspaceRoot, flowId, targetFlowId);
223
+ process.stdout.write(JSON.stringify(result) + "\n");
224
+ process.exit(result.success ? 0 : 1);
225
+ }
226
+ if (sub === "list-agents" && jsonMode) {
227
+ const list = listAgentsJson(workspaceRoot);
228
+ process.stdout.write(JSON.stringify(list) + "\n");
229
+ process.exit(0);
230
+ }
231
+ if (sub === "list-agents") {
232
+ listAgentsTable(workspaceRoot);
233
+ process.exit(0);
234
+ }
235
+ if (sub === "copy-builtin-agent" && jsonMode) {
236
+ const builtinAgentId = shift();
237
+ let targetId;
238
+ const targetIdx = argv.indexOf("--target");
239
+ if (targetIdx >= 0 && argv[targetIdx + 1]) targetId = argv[targetIdx + 1];
240
+ if (!builtinAgentId) {
241
+ process.stdout.write(JSON.stringify({ success: false, error: "Missing builtinAgentId" }) + "\n");
242
+ process.exit(1);
243
+ }
244
+ const result = copyBuiltinAgentJson(workspaceRoot, builtinAgentId, targetId);
245
+ process.stdout.write(JSON.stringify(result) + "\n");
246
+ process.exit(result.success ? 0 : 1);
247
+ }
248
+ if (sub === "read-agent" && jsonMode) {
249
+ const agentId = argv.find((a) => !a.startsWith("--"));
250
+ if (!agentId) {
251
+ process.stdout.write(JSON.stringify({ error: "Missing agentId" }) + "\n");
252
+ process.exit(1);
253
+ }
254
+ const result = readAgentJson(workspaceRoot, agentId);
255
+ process.stdout.write(JSON.stringify(result) + "\n");
256
+ process.exit(result.error ? 1 : 0);
257
+ }
258
+ if (sub === "add-role" && jsonMode) {
259
+ let id, name, description, builtin = false, contentPath;
260
+ const idIdx = argv.indexOf("--id");
261
+ if (idIdx >= 0 && argv[idIdx + 1]) id = argv[idIdx + 1];
262
+ const nameIdx = argv.indexOf("--name");
263
+ if (nameIdx >= 0 && argv[nameIdx + 1]) name = argv[nameIdx + 1];
264
+ const descIdx = argv.indexOf("--description");
265
+ if (descIdx >= 0 && argv[descIdx + 1]) description = argv[descIdx + 1];
266
+ if (argv.includes("--builtin")) builtin = true;
267
+ const contentIdx = argv.indexOf("--content");
268
+ if (contentIdx >= 0 && argv[contentIdx + 1]) contentPath = argv[contentIdx + 1];
269
+ if (!id) {
270
+ process.stdout.write(JSON.stringify({ success: false, error: "Missing --id" }) + "\n");
271
+ process.exit(1);
272
+ }
273
+ const result = addRoleJson(workspaceRoot, { builtin, id, name, description, contentPath });
274
+ process.stdout.write(JSON.stringify(result) + "\n");
275
+ process.exit(result.success ? 0 : 1);
276
+ }
277
+ if (sub === "write-flow" && jsonMode) {
278
+ let flowSource = "user";
279
+ const flowSourceIdx = argv.indexOf("--flow-source");
280
+ if (flowSourceIdx >= 0 && argv[flowSourceIdx + 1]) {
281
+ flowSource = argv[flowSourceIdx + 1];
282
+ argv.splice(flowSourceIdx, 2);
283
+ }
284
+ if (flowSource === "builtin") {
285
+ process.stderr.write(
286
+ "agentflow: --flow-source builtin 已弃用(包内 builtin 不可写);已按 workspace 写入 .workspace/agentflow/pipelines。\n",
287
+ );
288
+ flowSource = "workspace";
289
+ }
290
+ if (flowSource !== "user" && flowSource !== "workspace") {
291
+ process.stdout.write(
292
+ JSON.stringify({ success: false, error: "Invalid --flow-source (use user or workspace)" }) + "\n",
293
+ );
294
+ process.exit(1);
295
+ }
296
+ const flowId = argv.find((a) => !a.startsWith("--"));
297
+ if (!flowId) {
298
+ process.stdout.write(JSON.stringify({ success: false, error: "Missing flowId" }) + "\n");
299
+ process.exit(1);
300
+ }
301
+ const flowYaml = await readStdin();
302
+ const result = writeFlowYaml(workspaceRoot, flowId, flowSource, flowYaml);
303
+ process.stdout.write(JSON.stringify(result.success ? { success: true } : result) + "\n");
304
+ process.exit(result.success ? 0 : 1);
305
+ }
306
+ if (sub === "ui") {
307
+ let port = 8765;
308
+ let host = process.env.AGENTFLOW_UI_HOST || "127.0.0.1";
309
+ let schedulerEnabled = false;
310
+ let schedulerPollMs;
311
+ const portIdx = argv.indexOf("--port");
312
+ if (portIdx >= 0 && argv[portIdx + 1]) {
313
+ port = parseInt(argv[portIdx + 1], 10);
314
+ argv.splice(portIdx, 2);
315
+ }
316
+ const hostIdx = argv.indexOf("--host");
317
+ if (hostIdx >= 0 && argv[hostIdx + 1]) {
318
+ host = argv[hostIdx + 1];
319
+ argv.splice(hostIdx, 2);
320
+ }
321
+ if (argv.includes("--scheduler")) {
322
+ schedulerEnabled = true;
323
+ argv.splice(argv.indexOf("--scheduler"), 1);
324
+ }
325
+ if (argv.includes("--no-scheduler")) {
326
+ schedulerEnabled = false;
327
+ argv.splice(argv.indexOf("--no-scheduler"), 1);
328
+ }
329
+ const schedulerPollIdx = argv.indexOf("--scheduler-poll-ms");
330
+ if (schedulerPollIdx >= 0 && argv[schedulerPollIdx + 1]) {
331
+ schedulerPollMs = parseInt(argv[schedulerPollIdx + 1], 10);
332
+ argv.splice(schedulerPollIdx, 2);
333
+ }
334
+ const noOpen = argv.includes("--no-open");
335
+ if (noOpen) argv.splice(argv.indexOf("--no-open"), 1);
336
+ if (Number.isNaN(port) || port <= 0 || port > 65535) {
337
+ throw new Error("Invalid --port (use 1–65535)");
338
+ }
339
+ if (!host) {
340
+ throw new Error("Invalid --host");
341
+ }
342
+ await startUiServer({ workspaceRoot, port, host });
343
+ if (schedulerEnabled) {
344
+ startScheduler(workspaceRoot, { pollMs: schedulerPollMs }).catch((e) => {
345
+ log.error("Scheduler failed: " + ((e && e.message) || String(e)));
346
+ });
347
+ }
348
+ const browserHost = host === "0.0.0.0" || host === "::" ? "127.0.0.1" : host;
349
+ const url = "http://" + (browserHost.includes(":") ? `[${browserHost}]` : browserHost) + ":" + port;
350
+ process.stderr.write("AgentFlow UI: " + url + (browserHost === host ? "" : ` (listening on ${host}:${port})`) + "\n");
351
+ if (!noOpen) {
352
+ if (process.platform === "win32") {
353
+ const child = spawn("cmd", ["/c", "start", "", url], { detached: true, stdio: "ignore" });
354
+ child.unref();
355
+ } else if (process.platform === "darwin") {
356
+ const child = spawn("open", [url], { detached: true, stdio: "ignore" });
357
+ child.unref();
358
+ } else {
359
+ const child = spawn("xdg-open", [url], { detached: true, stdio: "ignore" });
360
+ child.unref();
361
+ }
362
+ }
363
+ await new Promise(() => {});
364
+ }
365
+ if (sub === "scheduler") {
366
+ const action = shift();
367
+ if (action === "start") {
368
+ let pollMs;
369
+ const pollIdx = argv.indexOf("--poll-ms");
370
+ if (pollIdx >= 0 && argv[pollIdx + 1]) {
371
+ pollMs = parseInt(argv[pollIdx + 1], 10);
372
+ argv.splice(pollIdx, 2);
373
+ }
374
+ const once = argv.includes("--once");
375
+ if (once) argv.splice(argv.indexOf("--once"), 1);
376
+ await startScheduler(workspaceRoot, { pollMs, once });
377
+ process.exit(0);
378
+ }
379
+ if (action === "status") {
380
+ if (jsonMode) {
381
+ process.stdout.write(JSON.stringify({ schedules: listScheduleStatuses(workspaceRoot) }) + "\n");
382
+ process.exit(0);
383
+ }
384
+ const rows = listScheduleStatuses(workspaceRoot);
385
+ const table = new Table({ head: ["flow", "source", "enabled", "cron", "timezone", "next", "running", "lastRun", "error"], style: { head: [] } });
386
+ for (const r of rows) {
387
+ table.push([
388
+ r.flowId,
389
+ r.flowSource,
390
+ r.enabled ? "yes" : "no",
391
+ r.cron || "",
392
+ r.timezone || "",
393
+ r.nextRunAt || "",
394
+ r.running ? "yes" : "no",
395
+ r.lastRunUuid || "",
396
+ r.lastError || "",
397
+ ]);
398
+ }
399
+ process.stdout.write(table.toString() + "\n");
400
+ process.exit(0);
401
+ }
402
+ throw new Error("Usage: agentflow scheduler <start|status> [--once] [--poll-ms <ms>] [--json]");
403
+ }
404
+ // ──── Hub commands ────
405
+ if (sub === "login") {
406
+ await hubLogin(argv);
407
+ process.exit(0);
408
+ }
409
+ if (sub === "logout") {
410
+ hubLogout();
411
+ process.exit(0);
412
+ }
413
+ if (sub === "publish") {
414
+ await hubPublish(workspaceRoot, argv);
415
+ process.exit(0);
416
+ }
417
+ if (sub === "list-remote") {
418
+ await hubListRemote(argv);
419
+ process.exit(0);
420
+ }
421
+ if (sub === "download") {
422
+ await hubDownload(argv);
423
+ process.exit(0);
424
+ }
425
+ // ──── Local commands ────
426
+ if (sub === "list") {
427
+ listPipelines(workspaceRoot);
428
+ } else if (sub === "apply") {
429
+ const aiMode = argv[0] === "-ai" || argv[0] === "--ai";
430
+ if (aiMode) {
431
+ argv.shift();
432
+ const step = argv.shift();
433
+ if (!step || !APPLY_AI_STEPS.includes(step)) {
434
+ throw new Error(
435
+ "Missing or invalid step. Usage: agentflow apply -ai <step> <args...>. Steps: " + APPLY_AI_STEPS.join(", "),
436
+ );
437
+ }
438
+ if (argv.length === 0) {
439
+ throw new Error("Missing args for step " + step + ". Example: agentflow apply -ai ensure-run-dir <workspaceRoot> [uuid] <flowName>");
440
+ }
441
+ const stepWorkspaceRoot = path.resolve(argv[0]);
442
+ ensureReference(stepWorkspaceRoot);
443
+ const scriptName = step + ".mjs";
444
+ const result = runNodeScript(stepWorkspaceRoot, scriptName, argv, { captureStdout: false });
445
+ process.exit(result.status ?? 0);
446
+ }
447
+ const first = shift();
448
+ if (!first) throw new Error("Missing FlowName or uuid. Usage: agentflow apply <FlowName> [uuid] | agentflow apply <uuid>");
449
+ let flowName, uuidArg;
450
+ if (isValidUuid(first)) {
451
+ flowName = findFlowNameByUuid(workspaceRoot, first);
452
+ if (!flowName) throw new Error("No run found for uuid " + first + ". Run apply with FlowName first (e.g. agentflow apply <FlowName>).");
453
+ uuidArg = first;
454
+ } else {
455
+ flowName = first;
456
+ uuidArg = isValidUuid(argv[0]) ? shift() : undefined;
457
+ }
458
+ await apply(workspaceRoot, flowName, uuidArg, dryRun, agentModel, force, parallel, cliInputs);
459
+ } else if (sub === "resume") {
460
+ const flowName = shift();
461
+ const uuidArg = shift();
462
+ if (!flowName || !uuidArg) throw new Error("Usage: agentflow resume <FlowName> <uuid> [instanceId]");
463
+ const instanceIdOpt = argv.length > 0 && !argv[0].startsWith("--") ? shift() : undefined;
464
+ await resume(workspaceRoot, flowName, uuidArg, instanceIdOpt, agentModel, force, parallel);
465
+ } else if (sub === "replay") {
466
+ const a = shift(),
467
+ b = shift(),
468
+ c = shift();
469
+ if (!a || !b) throw new Error("Usage: agentflow replay <uuid> <instanceId> or agentflow replay <flowName> <uuid> <instanceId>");
470
+ await replay(workspaceRoot, a, b, c, agentModel, force);
471
+ } else if (sub === "run-status") {
472
+ const flowName = shift();
473
+ const uuidArg = shift();
474
+ if (!flowName || !uuidArg) throw new Error("Usage: agentflow run-status <flowName> <uuid>");
475
+ const result = runNodeScript(workspaceRoot, "get-ready-nodes.mjs", [workspaceRoot, flowName, uuidArg], { captureStdout: true });
476
+ if (result.stdout) process.stdout.write(result.stdout);
477
+ process.exit(result.status === 0 ? 0 : 1);
478
+ } else if (sub === "extract-thinking") {
479
+ const first = argv[0];
480
+ if (first === "-list" || first === "--list") {
481
+ shift();
482
+ const list = listRunsWithLogs(workspaceRoot);
483
+ const wantJson = argv.includes("--json");
484
+ if (wantJson) {
485
+ process.stdout.write(
486
+ JSON.stringify({ ok: true, runs: list.map((r) => ({ flowName: r.flowName, uuid: r.uuid, logPath: r.logPath, size: r.size, lines: r.lines })) }) + "\n",
487
+ );
488
+ } else {
489
+ if (list.length === 0) {
490
+ log.info("没有找到带 logs/log.txt 的 run。先执行 apply 产生日志后再用 extract-thinking <flowName> <uuid> 提取。");
491
+ } else {
492
+ const table = new Table({ head: ["flowName", "uuid", "lines", "size"], style: { head: [] } });
493
+ for (const r of list) {
494
+ table.push([r.flowName, r.uuid, String(r.lines), r.size >= 1024 ? (r.size / 1024).toFixed(1) + " KB" : r.size + " B"]);
495
+ }
496
+ log.info("\n" + chalk.bold("可提取 thinking 的 run(logs/log.txt 存在)\n"));
497
+ log.info(table.toString());
498
+ log.info("\n提取: agentflow extract-thinking <flowName> <uuid>");
499
+ }
500
+ }
501
+ process.exit(0);
502
+ return;
503
+ }
504
+ const flowName = shift();
505
+ const uuidArg = shift();
506
+ if (!flowName || !uuidArg) throw new Error("Usage: agentflow extract-thinking <flowName> <uuid> 或 agentflow extract-thinking -list");
507
+ const result = runNodeScript(workspaceRoot, "extract-thinking.mjs", [workspaceRoot, flowName, uuidArg], { captureStdout: false });
508
+ process.exit(result.status === 0 ? 0 : 1);
509
+ } else if (sub === "validate") {
510
+ const flowName = shift();
511
+ if (!flowName) throw new Error("Usage: agentflow validate <FlowName> [uuid]");
512
+ const wantJson = argv.includes("--json");
513
+ if (wantJson) argv.splice(argv.indexOf("--json"), 1);
514
+ const uuidArg = argv.length > 0 && !argv[0].startsWith("--") && isValidUuid(argv[0]) ? shift() : null;
515
+ const flowDir = getFlowDir(workspaceRoot, flowName);
516
+ if (!flowDir) {
517
+ throw new Error(
518
+ "Flow not found: " +
519
+ flowName +
520
+ " (no flow.yaml under " +
521
+ USER_AGENTFLOW_PIPELINES_LABEL +
522
+ "/" +
523
+ flowName +
524
+ ", " +
525
+ PIPELINES_DIR +
526
+ "/" +
527
+ flowName +
528
+ ", " +
529
+ LEGACY_PIPELINES_DIR +
530
+ "/" +
531
+ flowName +
532
+ ", or builtin)",
533
+ );
534
+ }
535
+ const args = [workspaceRoot, flowName, flowDir];
536
+ if (uuidArg) args.push(uuidArg);
537
+ const result = runNodeScript(workspaceRoot, "validate-flow.mjs", args, { captureStdout: true });
538
+ if (!result.stdout) {
539
+ process.exit(result.status ?? 1);
540
+ return;
541
+ }
542
+ const isTTY = process.stdout.isTTY === true;
543
+ if (wantJson || !isTTY) {
544
+ process.stdout.write(result.stdout);
545
+ process.exit(result.status ?? 0);
546
+ return;
547
+ }
548
+ let data;
549
+ try {
550
+ data = JSON.parse(result.stdout);
551
+ } catch {
552
+ process.stdout.write(result.stdout);
553
+ process.exit(result.status ?? 0);
554
+ return;
555
+ }
556
+ if (data.error) {
557
+ process.stderr.write(chalk.red("Error: ") + data.error + "\n");
558
+ process.exit(1);
559
+ return;
560
+ }
561
+ const ok = data.ok === true;
562
+ const errs = Array.isArray(data.errors) ? data.errors : [];
563
+ const warns = Array.isArray(data.warnings) ? data.warnings : [];
564
+ const v = data.validation || {};
565
+ const edgeErr = Array.isArray(v.edgeTypeMismatch) ? v.edgeTypeMismatch : [];
566
+ const roleErr = Array.isArray(v.nodeRoleMissing) ? v.nodeRoleMissing : [];
567
+ const modelErr = Array.isArray(v.nodeModelMissing) ? v.nodeModelMissing : [];
568
+ process.stdout.write("\n");
569
+ process.stdout.write(chalk.bold("校验: ") + flowName + " ");
570
+ process.stdout.write(ok ? chalk.green("✓ 通过") + "\n" : chalk.red("✗ 未通过") + "\n");
571
+ if (!ok || errs.length > 0) {
572
+ for (const e of errs) {
573
+ process.stdout.write(chalk.red(" • ") + e + "\n");
574
+ }
575
+ }
576
+ if (edgeErr.length) {
577
+ process.stdout.write(chalk.yellow(" 边类型不匹配: ") + edgeErr.join(", ") + "\n");
578
+ }
579
+ if (roleErr.length) {
580
+ process.stdout.write(chalk.yellow(" 节点角色缺失/无效: ") + roleErr.join(", ") + "\n");
581
+ }
582
+ if (modelErr.length) {
583
+ process.stdout.write(chalk.yellow(" 节点模型缺失/无效: ") + modelErr.join(", ") + "\n");
584
+ }
585
+ if (warns.length > 0) {
586
+ process.stdout.write(chalk.dim(" 警告: ") + "\n");
587
+ for (const w of warns) {
588
+ process.stdout.write(chalk.dim(" • ") + w + "\n");
589
+ }
590
+ }
591
+ if (!ok || errs.length > 0 || warns.length > 0) process.stdout.write("\n");
592
+ process.exit(result.status ?? 0);
593
+ } else if (
594
+ sub === "list-flows" ||
595
+ sub === "read-flow" ||
596
+ sub === "write-flow" ||
597
+ sub === "read-node" ||
598
+ sub === "copy-builtin" ||
599
+ sub === "copy-builtin-agent" ||
600
+ sub === "read-agent" ||
601
+ sub === "add-role"
602
+ ) {
603
+ throw new Error("Use --json with " + sub + ". Example: agentflow list-flows --json --workspace-root <path>");
604
+ } else {
605
+ throw new Error(
606
+ "Unknown command: " +
607
+ sub +
608
+ ". Use login, logout, publish, list-remote, download, list, ui, apply, validate, resume, replay, run-status, extract-thinking.",
609
+ );
610
+ }
611
+ }