@fieldwangai/agentflow 0.1.153 → 0.1.154

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 (192) hide show
  1. package/README.md +16 -34
  2. package/README.zh-CN.md +14 -34
  3. package/agents/agentflow-node-executor-code.md +13 -3
  4. package/agents/agentflow-node-executor-planning.md +13 -3
  5. package/agents/agentflow-node-executor-requirement.md +13 -3
  6. package/agents/agentflow-node-executor-test.md +13 -3
  7. package/agents/agentflow-node-executor-ui.md +13 -3
  8. package/agents/agentflow-node-executor.md +13 -3
  9. package/agents/en/agentflow-node-executor.md +13 -3
  10. package/agents/zh/agentflow-node-executor.md +13 -3
  11. package/bin/agentflow.mjs +3 -3
  12. package/bin/lib/admin-builtin-pipelines.mjs +3 -2
  13. package/bin/lib/agent-runners.mjs +0 -929
  14. package/bin/lib/auth.mjs +2 -1
  15. package/bin/lib/catalog-agents.mjs +2 -1
  16. package/bin/lib/catalog-flows.mjs +165 -90
  17. package/bin/lib/composer-agent.mjs +3 -793
  18. package/bin/lib/composer-skill-router.mjs +3 -323
  19. package/bin/lib/exec-buffered.mjs +32 -0
  20. package/bin/lib/flow-dsl/cli.mjs +276 -0
  21. package/bin/lib/flow-dsl/codegen.mjs +358 -0
  22. package/bin/lib/flow-dsl/defs.mjs +103 -0
  23. package/bin/lib/flow-dsl/index.mjs +77 -0
  24. package/bin/lib/flow-dsl/ir.mjs +305 -0
  25. package/bin/lib/flow-dsl/layout.mjs +115 -0
  26. package/bin/lib/flow-dsl/legacy-yaml.mjs +268 -0
  27. package/bin/lib/flow-dsl/lint.mjs +241 -0
  28. package/bin/lib/flow-dsl/packages.mjs +251 -0
  29. package/bin/lib/flow-dsl/parser.mjs +444 -0
  30. package/bin/lib/flow-import.mjs +96 -26
  31. package/bin/lib/flow-write.mjs +48 -12
  32. package/bin/lib/help.mjs +8 -28
  33. package/bin/lib/html-escape.mjs +19 -0
  34. package/bin/lib/http-util.mjs +49 -0
  35. package/bin/lib/legacy-flow-execution.mjs +37 -0
  36. package/bin/lib/locales/en.json +1 -1
  37. package/bin/lib/locales/zh.json +1 -1
  38. package/bin/lib/main.mjs +152 -286
  39. package/bin/lib/marketplace.mjs +206 -61
  40. package/bin/lib/model-config.mjs +10 -0
  41. package/bin/lib/node-package-archive.mjs +240 -0
  42. package/bin/lib/node-package-bootstrap.mjs +72 -0
  43. package/bin/lib/node-package-manifest.mjs +174 -0
  44. package/bin/lib/paths.mjs +42 -19
  45. package/bin/lib/prd-workflow-routes.mjs +2949 -0
  46. package/bin/lib/prd-workflow-server.mjs +4884 -0
  47. package/bin/lib/run-events.mjs +0 -66
  48. package/bin/lib/schedule-config.mjs +4 -4
  49. package/bin/lib/skill-runtime.mjs +35 -0
  50. package/bin/lib/startup-storage-migrations.mjs +274 -0
  51. package/bin/lib/ui-server.mjs +1087 -18122
  52. package/bin/lib/user-env.mjs +16 -0
  53. package/bin/lib/workspace-auto-layout.mjs +234 -0
  54. package/bin/lib/workspace-flow-store.mjs +478 -0
  55. package/bin/lib/workspace-graph-merge.mjs +41 -34
  56. package/bin/lib/workspace-routes.mjs +3088 -0
  57. package/bin/lib/workspace-server.mjs +6199 -0
  58. package/bin/lib/workspace-state.mjs +331 -0
  59. package/bin/lib/workspace.mjs +2 -1
  60. package/builtin/nodes/agent_subAgent.md +1 -0
  61. package/builtin/nodes/control_agent_toBool.md +2 -0
  62. package/builtin/nodes/control_anyOne.md +2 -0
  63. package/builtin/nodes/control_cancelled.md +2 -0
  64. package/builtin/nodes/control_cd_workspace.md +5 -11
  65. package/builtin/nodes/control_delay.md +2 -0
  66. package/builtin/nodes/control_end.md +2 -0
  67. package/builtin/nodes/control_if.md +1 -0
  68. package/builtin/nodes/control_interval_loop.md +2 -0
  69. package/builtin/nodes/control_load_mcp.md +24 -0
  70. package/builtin/nodes/control_load_skills.md +8 -23
  71. package/builtin/nodes/control_start.md +2 -0
  72. package/builtin/nodes/control_toBool.md +2 -0
  73. package/builtin/nodes/control_user_workspace.md +2 -0
  74. package/builtin/nodes/control_wait_until.md +2 -0
  75. package/builtin/nodes/display_ascii.md +1 -0
  76. package/builtin/nodes/display_chart.md +1 -0
  77. package/builtin/nodes/display_html.md +1 -0
  78. package/builtin/nodes/display_image.md +1 -0
  79. package/builtin/nodes/display_markdown.md +1 -0
  80. package/builtin/nodes/display_mermaid.md +1 -0
  81. package/builtin/nodes/display_react_app.md +1 -0
  82. package/builtin/nodes/display_table.md +1 -0
  83. package/builtin/nodes/provide_bool.md +1 -0
  84. package/builtin/nodes/provide_file.md +1 -0
  85. package/builtin/nodes/provide_password.md +1 -0
  86. package/builtin/nodes/provide_str.md +1 -0
  87. package/builtin/nodes/tool_display_share_link.md +1 -0
  88. package/builtin/nodes/tool_get_env.md +2 -0
  89. package/builtin/nodes/tool_git_checkout.md +1 -0
  90. package/builtin/nodes/tool_git_worktree_load.md +1 -0
  91. package/builtin/nodes/tool_git_worktree_unload.md +1 -0
  92. package/builtin/nodes/tool_gitlab_create_mr.md +1 -0
  93. package/builtin/nodes/tool_jenkins_build.md +2 -0
  94. package/builtin/nodes/tool_load_key.md +2 -0
  95. package/builtin/nodes/tool_nodejs.md +17 -19
  96. package/builtin/nodes/tool_print.md +2 -0
  97. package/builtin/nodes/tool_save_key.md +2 -0
  98. package/builtin/nodes/tool_set_run_env.md +1 -0
  99. package/builtin/nodes/tool_user_ask.md +2 -0
  100. package/builtin/nodes/tool_user_check.md +2 -0
  101. package/builtin/nodes/tool_wecom_send_app_markdown.md +1 -0
  102. package/builtin/nodes/tool_wecom_send_group_markdown.md +1 -0
  103. package/builtin/nodes/workspace_one_click_task.md +44 -0
  104. package/builtin/nodes/workspace_run.md +16 -0
  105. package/builtin/nodes/workspace_scheduled_run.md +16 -0
  106. package/builtin/pipelines/module-migrate/scripts/gate.mjs +37 -0
  107. package/builtin/pipelines/module-migrate/scripts/static-check.mjs +82 -0
  108. package/builtin/pipelines/module-migrate/workspace.flow.js +172 -0
  109. package/builtin/pipelines/module-migrate/workspace.layout.json +134 -0
  110. package/builtin/pipelines/new/scripts/lint-flow.mjs +38 -0
  111. package/builtin/pipelines/new/workspace.flow.js +91 -0
  112. package/builtin/pipelines/new/workspace.layout.json +70 -0
  113. package/builtin/web-ui/dist/assets/{WorkflowAssistantThread-B9xslwI0.js → WorkflowAssistantThread-CKClwj96.js} +1 -1
  114. package/builtin/web-ui/dist/assets/index-BZ5KqLur.js +870 -0
  115. package/builtin/web-ui/dist/assets/index-CEXmmwM2.css +1 -0
  116. package/builtin/web-ui/dist/index.html +2 -2
  117. package/package.json +2 -1
  118. package/reference/flow-control-capabilities.md +77 -158
  119. package/reference/flow-layout.md +1 -1
  120. package/reference/flow-prompt-handler-check.md +2 -2
  121. package/skills/agentflow-author-flow/SKILL.md +1 -1
  122. package/skills/agentflow-cli/SKILL.md +139 -24
  123. package/skills/agentflow-cli/agents/openai.yaml +2 -2
  124. package/skills/agentflow-cli/scripts/agentflow-cli.mjs +654 -20
  125. package/skills/agentflow-cli/scripts/agentflow-runtime.mjs +97 -0
  126. package/skills/agentflow-flow-add-instances/SKILL.md +1 -1
  127. package/skills/agentflow-flow-dsl/SKILL.md +206 -0
  128. package/skills/agentflow-flow-dsl/agents/openai.yaml +4 -0
  129. package/skills/agentflow-flow-dsl/references/node-calls.md +39 -0
  130. package/skills/agentflow-flow-edit-node-fields/SKILL.md +1 -1
  131. package/skills/agentflow-flow-recipes/SKILL.md +7 -4
  132. package/skills/agentflow-flow-recipes/references/recipes.md +97 -43
  133. package/skills/agentflow-flow-sync-ui/SKILL.md +1 -1
  134. package/skills/agentflow-node-dsl/SKILL.md +212 -0
  135. package/skills/agentflow-node-dsl/agents/openai.yaml +4 -0
  136. package/skills/agentflow-node-reference/SKILL.md +2 -2
  137. package/skills/agentflow-node-reference/references/builtin-nodes.md +166 -115
  138. package/skills/agentflow-runtime-reference/references/runtime.md +1 -1
  139. package/skills/agentflow-workspace-ascii/SKILL.md +9 -16
  140. package/skills/agentflow-workspace-graph/SKILL.md +62 -48
  141. package/skills/agentflow-workspace-html/SKILL.md +7 -2
  142. package/skills/agentflow-workspace-image/SKILL.md +6 -2
  143. package/skills/agentflow-workspace-markdown/SKILL.md +14 -21
  144. package/skills/agentflow-workspace-mermaid/SKILL.md +8 -16
  145. package/bin/lib/api-runner.mjs +0 -387
  146. package/bin/lib/apply.mjs +0 -903
  147. package/bin/lib/composer-flow-instances.mjs +0 -68
  148. package/bin/lib/composer-flow-skeleton.mjs +0 -334
  149. package/bin/lib/composer-flow-validate.mjs +0 -47
  150. package/bin/lib/composer-model-router.mjs +0 -185
  151. package/bin/lib/composer-node-schema.mjs +0 -303
  152. package/bin/lib/composer-planner.mjs +0 -751
  153. package/bin/lib/composer-script-ops.mjs +0 -233
  154. package/bin/lib/flow-static-preview.mjs +0 -104
  155. package/bin/lib/hub-login.mjs +0 -54
  156. package/bin/lib/hub-publish.mjs +0 -159
  157. package/bin/lib/hub-remote.mjs +0 -189
  158. package/bin/lib/hub.mjs +0 -299
  159. package/bin/lib/jenkins.mjs +0 -380
  160. package/bin/lib/node-execute.mjs +0 -539
  161. package/bin/lib/normalize-node-tool-command.mjs +0 -97
  162. package/bin/lib/runtime-context.mjs +0 -243
  163. package/bin/lib/scheduler.mjs +0 -601
  164. package/bin/lib/ui-print.mjs +0 -94
  165. package/bin/pipeline/build-node-prompt.mjs +0 -271
  166. package/bin/pipeline/check-cache.mjs +0 -191
  167. package/bin/pipeline/check-flow.mjs +0 -543
  168. package/bin/pipeline/collect-nodes.mjs +0 -212
  169. package/bin/pipeline/compute-cache-md5.mjs +0 -177
  170. package/bin/pipeline/ensure-run-dir.mjs +0 -71
  171. package/bin/pipeline/gc.mjs +0 -129
  172. package/bin/pipeline/get-env.mjs +0 -59
  173. package/bin/pipeline/get-resolved-values.mjs +0 -344
  174. package/bin/pipeline/load-key.mjs +0 -62
  175. package/bin/pipeline/parse-flow.mjs +0 -708
  176. package/bin/pipeline/post-process-control-if.mjs +0 -23
  177. package/bin/pipeline/post-process-node.mjs +0 -490
  178. package/bin/pipeline/pre-process-node.mjs +0 -1430
  179. package/bin/pipeline/resolve-inputs.mjs +0 -201
  180. package/bin/pipeline/run-tool-nodejs.mjs +0 -167
  181. package/bin/pipeline/save-key.mjs +0 -93
  182. package/bin/pipeline/snapshot-prior-round.mjs +0 -70
  183. package/bin/pipeline/validate-for-ui.mjs +0 -234
  184. package/bin/pipeline/validate-script-output.mjs +0 -130
  185. package/bin/pipeline/write-result.mjs +0 -182
  186. package/builtin/pipelines/module-migrate/flow.yaml +0 -819
  187. package/builtin/pipelines/new/flow.yaml +0 -545
  188. package/builtin/pipelines/new/scripts/check-flow.mjs +0 -9
  189. package/builtin/pipelines/new/scripts/collect-nodes.mjs +0 -211
  190. package/builtin/web-ui/dist/assets/index-DZ328oSo.css +0 -1
  191. package/builtin/web-ui/dist/assets/index-DmUV7ZCL.js +0 -888
  192. package/skills/agentflow-node-authoring/SKILL.md +0 -57
package/bin/lib/main.mjs CHANGED
@@ -1,9 +1,7 @@
1
1
  import { spawn } from "child_process";
2
2
  import fs from "fs";
3
3
  import path from "path";
4
- import { fileURLToPath, pathToFileURL } from "node:url";
5
4
  import chalk from "chalk";
6
- import { apply, replay, resume } from "./apply.mjs";
7
5
  import {
8
6
  addRoleJson,
9
7
  copyBuiltinAgentJson,
@@ -20,22 +18,16 @@ import {
20
18
  readFlowJson,
21
19
  readNodeJson,
22
20
  } from "./catalog-flows.mjs";
23
- import { writeFlowYaml } from "./flow-write.mjs";
24
21
  import { printHelp } from "./help.mjs";
25
22
  import { LOG_LEVELS, log, setLogLevel, setMachineReadable } from "./log.mjs";
26
23
  import { updateModelLists } from "./model-lists.mjs";
27
- import { APPLY_AI_STEPS, LEGACY_PIPELINES_DIR, PIPELINES_DIR, USER_AGENTFLOW_PIPELINES_LABEL } from "./paths.mjs";
24
+ import { LEGACY_PIPELINES_DIR, PIPELINES_DIR, USER_AGENTFLOW_PIPELINES_LABEL } from "./paths.mjs";
28
25
  import { isValidUuid, runNodeScript } from "./pipeline-scripts.mjs";
29
26
  import { Table } from "./table.mjs";
30
27
  import { ensureReference, findFlowNameByUuid, getFlowDir, listRunsWithLogs } from "./workspace.mjs";
31
28
  import { startUiServer } from "./ui-server.mjs";
32
- import { hubLogin, hubLogout } from "./hub-login.mjs";
33
- import { hubPublish } from "./hub-publish.mjs";
34
- import { hubListRemote, hubDownload } from "./hub-remote.mjs";
35
- import { cancelScheduledRun, listScheduleStatuses, startScheduler } from "./scheduler.mjs";
36
- import { installFlowDependency, listMarketplacePackages, publishNodePackage } from "./marketplace.mjs";
29
+ import { listMarketplacePackages, publishNodePackage } from "./marketplace.mjs";
37
30
  import { startMcpServer } from "./mcp-server.mjs";
38
- import { writeStaticFlowPreview } from "./flow-static-preview.mjs";
39
31
  import { LEGACY_FLOW_EXECUTION_DISABLED, LEGACY_FLOW_EXECUTION_MESSAGE } from "./legacy-flow-execution.mjs";
40
32
 
41
33
  async function readStdin() {
@@ -44,6 +36,31 @@ async function readStdin() {
44
36
  return Buffer.concat(chunks).toString("utf8");
45
37
  }
46
38
 
39
+ /**
40
+ * 把 yaml 迁移的损耗清单打到 stderr。
41
+ *
42
+ * 迁移唯一的卖点就是「丢了什么当场说清楚」,所以这段在成功和拒绝两种结局下都要打——
43
+ * `--allow-loss` 迁过去了不代表没丢东西,只代表用户认了。
44
+ */
45
+ function writeMigrationLoss(result) {
46
+ for (const r of result.remapped || []) {
47
+ process.stderr.write(`${chalk.cyan("换词")} ${r.id}: ${r.from} -> ${r.to}\n`);
48
+ for (const rn of r.renamedSlots || []) process.stderr.write(` 槽位 ${rn}\n`);
49
+ for (const f of r.droppedFields || []) {
50
+ // 原文照打——新节点装不下,用户得能直接捡走贴到别处
51
+ process.stderr.write(` ${chalk.yellow("丢")} ${f.field}: ${JSON.stringify(f.text)}\n`);
52
+ }
53
+ if (r.caveat) process.stderr.write(` ${chalk.yellow("!")} ${r.caveat}\n`);
54
+ }
55
+ for (const d of result.dropped || []) {
56
+ process.stderr.write(`${chalk.red("丢节点")} ${d.id} (${d.definitionId}):${d.reason}\n`);
57
+ }
58
+ for (const e of result.droppedEdges || []) {
59
+ process.stderr.write(`${chalk.red("丢边")} ${e.source} -> ${e.target}:${e.reason}\n`);
60
+ }
61
+ for (const w of result.warnings || []) process.stderr.write(`${chalk.yellow("warn")} ${w}\n`);
62
+ }
63
+
47
64
  export async function main() {
48
65
  const argv = process.argv.slice(2);
49
66
  let workspaceRoot = process.cwd();
@@ -61,54 +78,26 @@ export async function main() {
61
78
  printHelp();
62
79
  process.exit(0);
63
80
  }
64
- const dryRun = argv.includes("--dry-run");
65
- if (dryRun) argv.splice(argv.indexOf("--dry-run"), 1);
81
+ if (argv.includes("--dry-run")) argv.splice(argv.indexOf("--dry-run"), 1);
66
82
  if (argv.includes("--debug")) {
67
83
  setLogLevel(LOG_LEVELS.debug);
68
84
  argv.splice(argv.indexOf("--debug"), 1);
69
85
  }
70
- let force = true;
71
- if (argv.includes("--no-force")) {
72
- force = false;
73
- argv.splice(argv.indexOf("--no-force"), 1);
74
- }
75
- if (argv.includes("--force")) {
76
- force = true;
77
- argv.splice(argv.indexOf("--force"), 1);
78
- }
79
- if (argv.includes("--yolo")) {
80
- force = true;
81
- argv.splice(argv.indexOf("--yolo"), 1);
82
- }
83
- let parallel = false;
84
- if (argv.includes("--parallel")) {
85
- parallel = true;
86
- argv.splice(argv.indexOf("--parallel"), 1);
87
- }
88
- if (argv.includes("--no-parallel")) {
89
- parallel = false;
90
- argv.splice(argv.indexOf("--no-parallel"), 1);
86
+ // 以下开关只服务已下线的 Start/End 执行,保留解析以免旧脚本把它们当成子命令。
87
+ for (const legacyFlag of ["--no-force", "--force", "--yolo", "--parallel", "--no-parallel"]) {
88
+ while (argv.includes(legacyFlag)) argv.splice(argv.indexOf(legacyFlag), 1);
91
89
  }
92
90
  if (argv.includes("--machine-readable")) {
93
91
  setMachineReadable(true);
94
92
  argv.splice(argv.indexOf("--machine-readable"), 1);
95
93
  }
96
94
  const jsonMode = argv.includes("--json");
97
- const cliInputs = {};
98
95
  while (argv.includes("--input")) {
99
96
  const idx = argv.indexOf("--input");
100
97
  const pair = argv[idx + 1];
101
98
  if (!pair || !pair.includes("=")) {
102
99
  throw new Error("Invalid --input format. Use: --input name=value");
103
100
  }
104
- const eqIdx = pair.indexOf("=");
105
- const name = pair.slice(0, eqIdx);
106
- const value = pair.slice(eqIdx + 1);
107
- if (value.startsWith("file:")) {
108
- cliInputs[name] = { type: "file", path: value.slice(5) };
109
- } else {
110
- cliInputs[name] = { type: "str", value };
111
- }
112
101
  argv.splice(idx, 2);
113
102
  }
114
103
  const sub = shift();
@@ -129,7 +118,6 @@ export async function main() {
129
118
  "list-flows",
130
119
  "list-nodes",
131
120
  "read-flow",
132
- "write-flow",
133
121
  "read-node",
134
122
  "copy-builtin",
135
123
  "list-agents",
@@ -140,12 +128,8 @@ export async function main() {
140
128
  if (jsonMode && jsonOnlySubs.includes(sub)) {
141
129
  argv.splice(argv.indexOf("--json"), 1);
142
130
  }
143
- let agentModel = process.env.CURSOR_AGENT_MODEL || null;
144
131
  const modelIdx = argv.indexOf("--model");
145
- if (modelIdx >= 0 && argv[modelIdx + 1]) {
146
- agentModel = argv[modelIdx + 1];
147
- argv.splice(modelIdx, 2);
148
- }
132
+ if (modelIdx >= 0 && argv[modelIdx + 1]) argv.splice(modelIdx, 2);
149
133
  if (sub === "list-flows" && jsonMode) {
150
134
  const list = listFlowsJson(workspaceRoot);
151
135
  process.stdout.write(JSON.stringify(list) + "\n");
@@ -243,19 +227,7 @@ export async function main() {
243
227
  else throw new Error(result.error || "publish-node failed");
244
228
  process.exit(result.ok ? 0 : 1);
245
229
  }
246
- if (action === "install-node") {
247
- const flowId = shift();
248
- const spec = shift();
249
- if (!flowId || !spec) throw new Error("Usage: agentflow marketplace install-node <flow> <nodeSpec> [--json]");
250
- const flowDir = getFlowDir(workspaceRoot, flowId);
251
- if (!flowDir) throw new Error(`Flow not found: ${flowId}`);
252
- const result = installFlowDependency(workspaceRoot, flowDir, spec);
253
- if (jsonMode) process.stdout.write(JSON.stringify(result) + "\n");
254
- else if (result.ok) process.stdout.write(`Installed ${result.definitionId} into ${flowId}\n`);
255
- else throw new Error(result.error || "install-node failed");
256
- process.exit(result.ok ? 0 : 1);
257
- }
258
- throw new Error("Usage: agentflow marketplace <list|publish-node|install-node> [--json]");
230
+ throw new Error("Usage: agentflow marketplace <list|publish-node> [--json]");
259
231
  }
260
232
  if (sub === "copy-builtin" && jsonMode) {
261
233
  const flowId = shift();
@@ -321,101 +293,105 @@ export async function main() {
321
293
  process.stdout.write(JSON.stringify(result) + "\n");
322
294
  process.exit(result.success ? 0 : 1);
323
295
  }
324
- if (sub === "write-flow" && jsonMode) {
325
- let flowSource = "user";
326
- const flowSourceIdx = argv.indexOf("--flow-source");
327
- if (flowSourceIdx >= 0 && argv[flowSourceIdx + 1]) {
328
- flowSource = argv[flowSourceIdx + 1];
329
- argv.splice(flowSourceIdx, 2);
330
- }
331
- if (flowSource === "builtin") {
332
- process.stderr.write(
333
- "agentflow: --flow-source builtin 已弃用(包内 builtin 不可写);已按 workspace 写入 .workspace/agentflow/pipelines。\n",
334
- );
335
- flowSource = "workspace";
336
- }
337
- if (flowSource !== "user" && flowSource !== "workspace") {
338
- process.stdout.write(
339
- JSON.stringify({ success: false, error: "Invalid --flow-source (use user or workspace)" }) + "\n",
340
- );
341
- process.exit(1);
342
- }
343
- const flowId = argv.find((a) => !a.startsWith("--"));
344
- if (!flowId) {
345
- process.stdout.write(JSON.stringify({ success: false, error: "Missing flowId" }) + "\n");
346
- process.exit(1);
347
- }
348
- const flowYaml = await readStdin();
349
- const result = writeFlowYaml(workspaceRoot, flowId, flowSource, flowYaml);
350
- process.stdout.write(JSON.stringify(result.success ? { success: true } : result) + "\n");
351
- process.exit(result.success ? 0 : 1);
352
- }
353
- if (sub === "flow" && argv[0] === "preview") {
296
+ if (sub === "flow" && argv[0] === "dsl") {
354
297
  shift();
298
+ const action = shift();
355
299
  const target = shift();
356
- if (!target) throw new Error("Usage: agentflow flow preview <FlowName|flow.yaml> [--output <preview.html>] [--no-open]");
357
- let outputPath = "";
358
- const outputIdx = argv.indexOf("--output");
359
- if (outputIdx >= 0 && argv[outputIdx + 1]) {
360
- outputPath = path.resolve(workspaceRoot, argv[outputIdx + 1]);
361
- argv.splice(outputIdx, 2);
362
- }
363
- const noOpen = argv.includes("--no-open");
364
- if (noOpen) argv.splice(argv.indexOf("--no-open"), 1);
365
- if (argv.length > 0) throw new Error(`Unknown flow preview option: ${argv[0]}`);
300
+ const usage = "Usage: agentflow flow dsl <export|import|lint|layout|migrate> <FlowName|dir> [--out <dir>] [--allow-loss] [--all]";
301
+ if (!action || !target) throw new Error(usage);
302
+ let outDir = "";
303
+ const outIdx = argv.indexOf("--out");
304
+ if (outIdx >= 0 && argv[outIdx + 1]) {
305
+ outDir = path.resolve(workspaceRoot, argv[outIdx + 1]);
306
+ argv.splice(outIdx, 2);
307
+ }
308
+ // 不叫 --force:那个名字在上面被当成已下线执行栈的遗留开关提前吃掉了
309
+ const lossIdx = argv.indexOf("--allow-loss");
310
+ const force = lossIdx >= 0;
311
+ if (force) argv.splice(lossIdx, 1);
312
+ const allIdx = argv.indexOf("--all");
313
+ const all = allIdx >= 0;
314
+ if (all) argv.splice(allIdx, 1);
315
+ if (argv.length > 0) throw new Error(`Unknown flow dsl option: ${argv[0]}`);
316
+ if (all && action !== "layout") throw new Error("--all 只用于 flow dsl layout");
366
317
 
367
- const targetPath = path.resolve(workspaceRoot, target);
368
- let previewFlowPath = "";
369
- if (fs.existsSync(targetPath)) {
370
- previewFlowPath = fs.statSync(targetPath).isDirectory()
371
- ? path.join(targetPath, "flow.yaml")
372
- : targetPath;
373
- } else {
374
- const flowDir = getFlowDir(workspaceRoot, target);
375
- if (flowDir) previewFlowPath = path.join(flowDir, "flow.yaml");
376
- }
377
- if (!previewFlowPath || !fs.existsSync(previewFlowPath) || !fs.statSync(previewFlowPath).isFile()) {
378
- throw new Error(`Flow not found: ${target}`);
379
- }
380
- previewFlowPath = fs.realpathSync(previewFlowPath);
381
- const flowId = path.basename(path.dirname(previewFlowPath)) || "local-preview";
382
- const safeFlowId = flowId.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "flow-preview";
383
- if (!outputPath) {
384
- outputPath = path.join(workspaceRoot, ".workspace", "agentflow", "previews", `${safeFlowId}.html`);
385
- }
386
- if (!/\.html?$/i.test(outputPath)) throw new Error("Preview output must be an HTML file");
387
- const nodeCatalog = listNodesJson(workspaceRoot, flowId, "", {
388
- staticFlowPath: previewFlowPath,
389
- marketplaceScope: "all",
390
- });
391
- const result = writeStaticFlowPreview({
392
- flowId,
393
- flowPath: previewFlowPath,
394
- nodeCatalog,
395
- outputPath,
396
- distDir: path.join(path.dirname(path.dirname(path.dirname(fileURLToPath(import.meta.url)))), "builtin", "web-ui", "dist"),
397
- });
398
- const url = pathToFileURL(result.outputPath).href;
399
- process.stderr.write(`AgentFlow static preview: ${result.outputPath}\nSource: ${previewFlowPath}\n`);
400
- if (!noOpen) {
401
- if (process.platform === "win32") {
402
- const child = spawn("cmd", ["/c", "start", "", url], { detached: true, stdio: "ignore" });
403
- child.unref();
404
- } else if (process.platform === "darwin") {
405
- const child = spawn("open", [url], { detached: true, stdio: "ignore" });
406
- child.unref();
318
+ const direct = path.resolve(workspaceRoot, target);
319
+ const dir = fs.existsSync(direct) && fs.statSync(direct).isDirectory()
320
+ ? direct
321
+ : getFlowDir(workspaceRoot, target);
322
+ if (!dir || !fs.existsSync(dir)) throw new Error(`Flow not found: ${target}`);
323
+
324
+ const { exportFlowDsl, importFlowDsl, layoutWorkspaceFlowDir, lintFlowDir, migrateFlowDirToDsl } = await import("./flow-dsl/cli.mjs");
325
+
326
+ if (action === "migrate") {
327
+ const result = migrateFlowDirToDsl(dir, { force, marketplaceRoot: workspaceRoot });
328
+ if (jsonMode) { process.stdout.write(JSON.stringify(result) + "\n"); return; }
329
+ if (result.format === "empty") process.stderr.write(`${chalk.yellow("skip")} ${dir}:没有图\n`);
330
+ else if (!result.migrated && result.format === "dsl") process.stderr.write(`${chalk.green("ok")} ${dir}:已经是代码形态\n`);
331
+ else if (result.migrated) {
332
+ process.stderr.write(`${chalk.green("ok")} ${dir} -> workspace.flow.js(来自 ${result.source})\n`);
333
+ for (const rel of result.externals) process.stderr.write(` ${rel}\n`);
334
+ } else if (result.leftYaml) {
335
+ // 够不着代码形态,但已经离开 yaml——图从此读得出、画得出、跑得动
336
+ process.stderr.write(`${chalk.green("ok")} ${dir} -> workspace.graph.json(来自 flow.yaml)\n`);
337
+ process.stderr.write(` ${chalk.dim(`还差一步到代码:${result.degradedReason}`)}\n`);
338
+ } else if (result.format === "yaml") {
339
+ process.stderr.write(`${chalk.red("keep")} ${dir}:${result.degradedReason}\n`);
407
340
  } else {
408
- const child = spawn("xdg-open", [url], { detached: true, stdio: "ignore" });
409
- child.unref();
341
+ process.stderr.write(`${chalk.red("keep")} ${dir}:${result.degradedReason},保留 workspace.graph.json\n`);
342
+ process.exitCode = 1;
410
343
  }
344
+ // 损耗清单在成功和拒绝两种结局下都要打出来——`--allow-loss` 迁过去了不代表没丢东西
345
+ writeMigrationLoss(result);
346
+ if (result.format === "yaml" && !result.migrated) process.exitCode = 1;
347
+ return;
411
348
  }
412
- return;
349
+
350
+ if (action === "export") {
351
+ const result = exportFlowDsl(dir, outDir);
352
+ if (jsonMode) { process.stdout.write(JSON.stringify(result) + "\n"); return; }
353
+ else {
354
+ process.stderr.write(`Exported to ${result.outDir}\n`);
355
+ for (const rel of result.written) process.stderr.write(` ${rel}\n`);
356
+ }
357
+ return;
358
+ }
359
+ if (action === "import") {
360
+ const result = importFlowDsl(dir, outDir);
361
+ if (jsonMode) { process.stdout.write(JSON.stringify(result) + "\n"); return; }
362
+ else {
363
+ process.stderr.write(`Wrote ${result.graphPath}\n ${result.nodeCount} 节点 / ${result.edgeCount} 边\n`);
364
+ for (const w of result.warnings) process.stderr.write(` warning: ${w}\n`);
365
+ }
366
+ return;
367
+ }
368
+ if (action === "lint") {
369
+ const result = lintFlowDir(dir, { workspaceRoot });
370
+ if (jsonMode) process.stdout.write(JSON.stringify({ errors: result.errors, warnings: result.warnings }) + "\n");
371
+ else {
372
+ for (const e of result.errors) process.stderr.write(`${chalk.red("error")} ${e}\n`);
373
+ for (const w of result.warnings) process.stderr.write(`${chalk.yellow("warn")} ${w}\n`);
374
+ if (!result.errors.length) process.stderr.write(`${chalk.green("ok")} lint 通过\n`);
375
+ }
376
+ if (result.errors.length) process.exitCode = 1;
377
+ return;
378
+ }
379
+ if (action === "layout") {
380
+ const result = layoutWorkspaceFlowDir(dir, { all, workspaceRoot });
381
+ if (jsonMode) process.stdout.write(JSON.stringify(result) + "\n");
382
+ else process.stderr.write(`${chalk.green("ok")} ${result.positioned}/${result.nodeCount} 个节点已${all ? "重新" : "补充"}排版\n ${result.layoutPath}\n`);
383
+ return;
384
+ }
385
+ throw new Error(usage);
386
+ }
387
+ if (sub === "flow") {
388
+ // `flow preview` 曾经在这儿。它把 flow.yaml 原文塞进页面,代码化流程没有 yaml 可塞;
389
+ // Web 的 /api/workspace/preview 接的是图对象,本来就通用,所以删掉而不是重写。
390
+ throw new Error("Usage: agentflow flow dsl <export|import|lint|layout|migrate> <FlowName|dir> [--out <dir>] [--all]");
413
391
  }
414
392
  if (sub === "ui") {
415
393
  let port = 8765;
416
394
  let host = process.env.AGENTFLOW_UI_HOST || "127.0.0.1";
417
- let schedulerEnabled = false;
418
- let schedulerPollMs;
419
395
  let hideCommunityLinks = /^(1|true|yes|on)$/i.test(String(process.env.AGENTFLOW_HIDE_COMMUNITY_LINKS || ""));
420
396
  const portIdx = argv.indexOf("--port");
421
397
  if (portIdx >= 0 && argv[portIdx + 1]) {
@@ -427,19 +403,6 @@ export async function main() {
427
403
  host = argv[hostIdx + 1];
428
404
  argv.splice(hostIdx, 2);
429
405
  }
430
- if (argv.includes("--scheduler")) {
431
- schedulerEnabled = true;
432
- argv.splice(argv.indexOf("--scheduler"), 1);
433
- }
434
- if (argv.includes("--no-scheduler")) {
435
- schedulerEnabled = false;
436
- argv.splice(argv.indexOf("--no-scheduler"), 1);
437
- }
438
- const schedulerPollIdx = argv.indexOf("--scheduler-poll-ms");
439
- if (schedulerPollIdx >= 0 && argv[schedulerPollIdx + 1]) {
440
- schedulerPollMs = parseInt(argv[schedulerPollIdx + 1], 10);
441
- argv.splice(schedulerPollIdx, 2);
442
- }
443
406
  const noOpen = argv.includes("--no-open");
444
407
  if (noOpen) argv.splice(argv.indexOf("--no-open"), 1);
445
408
  if (argv.includes("--hide-community-links")) {
@@ -453,11 +416,6 @@ export async function main() {
453
416
  throw new Error("Invalid --host");
454
417
  }
455
418
  await startUiServer({ workspaceRoot, port, host, hideCommunityLinks });
456
- if (schedulerEnabled) {
457
- startScheduler(workspaceRoot, { pollMs: schedulerPollMs }).catch((e) => {
458
- log.error("Scheduler failed: " + ((e && e.message) || String(e)));
459
- });
460
- }
461
419
  const browserHost = host === "0.0.0.0" || host === "::" ? "127.0.0.1" : host;
462
420
  const url = "http://" + (browserHost.includes(":") ? `[${browserHost}]` : browserHost) + ":" + port;
463
421
  process.stderr.write("AgentFlow UI: " + url + (browserHost === host ? "" : ` (listening on ${host}:${port})`) + "\n");
@@ -475,130 +433,12 @@ export async function main() {
475
433
  }
476
434
  await new Promise(() => {});
477
435
  }
478
- if (sub === "scheduler") {
479
- const action = shift();
480
- if (action === "start") {
481
- let pollMs;
482
- const pollIdx = argv.indexOf("--poll-ms");
483
- if (pollIdx >= 0 && argv[pollIdx + 1]) {
484
- pollMs = parseInt(argv[pollIdx + 1], 10);
485
- argv.splice(pollIdx, 2);
486
- }
487
- const once = argv.includes("--once");
488
- if (once) argv.splice(argv.indexOf("--once"), 1);
489
- await startScheduler(workspaceRoot, { pollMs, once });
490
- process.exit(0);
491
- }
492
- if (action === "status") {
493
- if (jsonMode) {
494
- process.stdout.write(JSON.stringify({ schedules: listScheduleStatuses(workspaceRoot) }) + "\n");
495
- process.exit(0);
496
- }
497
- const rows = listScheduleStatuses(workspaceRoot);
498
- const table = new Table({ head: ["flow", "source", "enabled", "cron", "timezone", "next", "running", "waiting", "lastRun", "error"], style: { head: [] } });
499
- for (const r of rows) {
500
- table.push([
501
- r.flowId,
502
- r.flowSource,
503
- r.enabled ? "yes" : "no",
504
- r.cron || "",
505
- r.timezone || "",
506
- r.nextRunAt || "",
507
- r.running ? "yes" : "no",
508
- String(r.waiting || 0),
509
- r.lastRunUuid || "",
510
- r.lastError || "",
511
- ]);
512
- }
513
- process.stdout.write(table.toString() + "\n");
514
- process.exit(0);
515
- }
516
- if (action === "cancel") {
517
- const flowId = shift();
518
- const uuid = shift();
519
- if (!flowId || !uuid) throw new Error("Usage: agentflow scheduler cancel <flow> <uuid> [--json]");
520
- const result = cancelScheduledRun(workspaceRoot, flowId, uuid);
521
- if (jsonMode) {
522
- process.stdout.write(JSON.stringify(result) + "\n");
523
- } else if (result.ok) {
524
- process.stdout.write(`Cancelled ${flowId}/${uuid}; updated waits: ${result.updatedWaits}\n`);
525
- } else {
526
- throw new Error(result.error || "cancel failed");
527
- }
528
- process.exit(result.ok ? 0 : 1);
529
- }
530
- throw new Error("Usage: agentflow scheduler <start|status|cancel> [--once] [--poll-ms <ms>] [--json]");
531
- }
532
436
  // ──── Hub commands ────
533
- if (sub === "login") {
534
- await hubLogin(argv);
535
- process.exit(0);
536
- }
537
- if (sub === "logout") {
538
- hubLogout();
539
- process.exit(0);
540
- }
541
- if (sub === "publish") {
542
- await hubPublish(workspaceRoot, argv);
543
- process.exit(0);
544
- }
545
- if (sub === "list-remote") {
546
- await hubListRemote(argv);
547
- process.exit(0);
548
- }
549
- if (sub === "download") {
550
- await hubDownload(argv);
551
- process.exit(0);
552
- }
553
437
  // ──── Local commands ────
554
438
  if (sub === "list") {
555
439
  listPipelines(workspaceRoot);
556
- } else if (sub === "apply") {
557
- if (LEGACY_FLOW_EXECUTION_DISABLED) throw new Error(LEGACY_FLOW_EXECUTION_MESSAGE);
558
- const aiMode = argv[0] === "-ai" || argv[0] === "--ai";
559
- if (aiMode) {
560
- argv.shift();
561
- const step = argv.shift();
562
- if (!step || !APPLY_AI_STEPS.includes(step)) {
563
- throw new Error(
564
- "Missing or invalid step. Usage: agentflow apply -ai <step> <args...>. Steps: " + APPLY_AI_STEPS.join(", "),
565
- );
566
- }
567
- if (argv.length === 0) {
568
- throw new Error("Missing args for step " + step + ". Example: agentflow apply -ai ensure-run-dir <workspaceRoot> [uuid] <flowName>");
569
- }
570
- const stepWorkspaceRoot = path.resolve(argv[0]);
571
- ensureReference(stepWorkspaceRoot);
572
- const scriptName = step + ".mjs";
573
- const result = runNodeScript(stepWorkspaceRoot, scriptName, argv, { captureStdout: false });
574
- process.exit(result.status ?? 0);
575
- }
576
- const first = shift();
577
- if (!first) throw new Error("Missing FlowName or uuid. Usage: agentflow apply <FlowName> [uuid] | agentflow apply <uuid>");
578
- let flowName, uuidArg;
579
- if (isValidUuid(first)) {
580
- flowName = findFlowNameByUuid(workspaceRoot, first);
581
- if (!flowName) throw new Error("No run found for uuid " + first + ". Run apply with FlowName first (e.g. agentflow apply <FlowName>).");
582
- uuidArg = first;
583
- } else {
584
- flowName = first;
585
- uuidArg = isValidUuid(argv[0]) ? shift() : undefined;
586
- }
587
- await apply(workspaceRoot, flowName, uuidArg, dryRun, agentModel, force, parallel, cliInputs);
588
- } else if (sub === "resume") {
589
- if (LEGACY_FLOW_EXECUTION_DISABLED) throw new Error(LEGACY_FLOW_EXECUTION_MESSAGE);
590
- const flowName = shift();
591
- const uuidArg = shift();
592
- if (!flowName || !uuidArg) throw new Error("Usage: agentflow resume <FlowName> <uuid> [instanceId]");
593
- const instanceIdOpt = argv.length > 0 && !argv[0].startsWith("--") ? shift() : undefined;
594
- await resume(workspaceRoot, flowName, uuidArg, instanceIdOpt, agentModel, force, parallel);
595
- } else if (sub === "replay") {
596
- if (LEGACY_FLOW_EXECUTION_DISABLED) throw new Error(LEGACY_FLOW_EXECUTION_MESSAGE);
597
- const a = shift(),
598
- b = shift(),
599
- c = shift();
600
- if (!a || !b) throw new Error("Usage: agentflow replay <uuid> <instanceId> or agentflow replay <flowName> <uuid> <instanceId>");
601
- await replay(workspaceRoot, a, b, c, agentModel, force);
440
+ } else if (sub === "apply" || sub === "resume" || sub === "replay") {
441
+ throw new Error(LEGACY_FLOW_EXECUTION_MESSAGE);
602
442
  } else if (sub === "run-status") {
603
443
  const flowName = shift();
604
444
  const uuidArg = shift();
@@ -663,6 +503,33 @@ export async function main() {
663
503
  ", or builtin)",
664
504
  );
665
505
  }
506
+ // Workspace 图归 flow dsl lint 管。validate-flow.mjs 校验的是 flow.yaml,而 Workspace
507
+ // 流程的 flow.yaml 只是个空壳,让它去校验只会得到「必须包含 instances 且至少一个节点」
508
+ // 这种必然失败的结论。
509
+ const { lintWorkspaceFlowDir } = await import("./flow-dsl/cli.mjs");
510
+ const workspaceLint = lintWorkspaceFlowDir(flowDir);
511
+ if (workspaceLint.format !== "empty") {
512
+ if (wantJson || process.stdout.isTTY !== true) {
513
+ process.stdout.write(JSON.stringify({
514
+ ok: workspaceLint.errors.length === 0,
515
+ target: "workspace",
516
+ errors: workspaceLint.errors,
517
+ warnings: workspaceLint.warnings,
518
+ }) + "\n");
519
+ } else {
520
+ process.stdout.write(`\n${chalk.bold("校验: ")}${flowName} ${
521
+ workspaceLint.errors.length ? chalk.red("✗ 未通过") : chalk.green("✓ 通过")
522
+ }\n`);
523
+ for (const e of workspaceLint.errors) process.stdout.write(`${chalk.red(" • ")}${e}\n`);
524
+ for (const w of workspaceLint.warnings) process.stdout.write(`${chalk.yellow(" ! ")}${w}\n`);
525
+ if (workspaceLint.format === "json") {
526
+ process.stdout.write(chalk.dim(" (这张图还是 workspace.graph.json;跑 agentflow flow dsl migrate 转成代码)\n"));
527
+ }
528
+ }
529
+ process.exit(workspaceLint.errors.length ? 1 : 0);
530
+ return;
531
+ }
532
+
666
533
  const args = [workspaceRoot, flowName, flowDir];
667
534
  if (uuidArg) args.push(uuidArg);
668
535
  const result = runNodeScript(workspaceRoot, "validate-flow.mjs", args, { captureStdout: true });
@@ -724,7 +591,6 @@ export async function main() {
724
591
  } else if (
725
592
  sub === "list-flows" ||
726
593
  sub === "read-flow" ||
727
- sub === "write-flow" ||
728
594
  sub === "read-node" ||
729
595
  sub === "copy-builtin" ||
730
596
  sub === "copy-builtin-agent" ||
@@ -736,7 +602,7 @@ export async function main() {
736
602
  throw new Error(
737
603
  "Unknown command: " +
738
604
  sub +
739
- ". Use login, logout, publish, list-remote, download, list, ui, apply, validate, resume, replay, run-status, extract-thinking.",
605
+ ". Use list, ui, validate, run-status, extract-thinking, flow, marketplace, mcp.",
740
606
  );
741
607
  }
742
608
  }