@fieldwangai/agentflow 0.1.149 → 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 (195) hide show
  1. package/README.md +20 -33
  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 +167 -88
  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 +10 -30
  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 +43 -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 +153 -283
  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 +1106 -18013
  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-preview.mjs +74 -0
  57. package/bin/lib/workspace-routes.mjs +3088 -0
  58. package/bin/lib/workspace-server.mjs +6199 -0
  59. package/bin/lib/workspace-state.mjs +331 -0
  60. package/bin/lib/workspace.mjs +2 -1
  61. package/builtin/nodes/agent_subAgent.md +1 -0
  62. package/builtin/nodes/control_agent_toBool.md +2 -0
  63. package/builtin/nodes/control_anyOne.md +2 -0
  64. package/builtin/nodes/control_cancelled.md +2 -0
  65. package/builtin/nodes/control_cd_workspace.md +5 -11
  66. package/builtin/nodes/control_delay.md +2 -0
  67. package/builtin/nodes/control_end.md +2 -0
  68. package/builtin/nodes/control_if.md +1 -0
  69. package/builtin/nodes/control_interval_loop.md +2 -0
  70. package/builtin/nodes/control_load_mcp.md +24 -0
  71. package/builtin/nodes/control_load_skills.md +8 -23
  72. package/builtin/nodes/control_start.md +2 -0
  73. package/builtin/nodes/control_toBool.md +2 -0
  74. package/builtin/nodes/control_user_workspace.md +2 -0
  75. package/builtin/nodes/control_wait_until.md +2 -0
  76. package/builtin/nodes/display_ascii.md +1 -0
  77. package/builtin/nodes/display_chart.md +1 -0
  78. package/builtin/nodes/display_html.md +1 -0
  79. package/builtin/nodes/display_image.md +1 -0
  80. package/builtin/nodes/display_markdown.md +1 -0
  81. package/builtin/nodes/display_mermaid.md +1 -0
  82. package/builtin/nodes/display_react_app.md +1 -0
  83. package/builtin/nodes/display_table.md +1 -0
  84. package/builtin/nodes/provide_bool.md +1 -0
  85. package/builtin/nodes/provide_file.md +1 -0
  86. package/builtin/nodes/provide_password.md +1 -0
  87. package/builtin/nodes/provide_str.md +1 -0
  88. package/builtin/nodes/tool_display_share_link.md +1 -0
  89. package/builtin/nodes/tool_get_env.md +2 -0
  90. package/builtin/nodes/tool_git_checkout.md +1 -0
  91. package/builtin/nodes/tool_git_worktree_load.md +1 -0
  92. package/builtin/nodes/tool_git_worktree_unload.md +1 -0
  93. package/builtin/nodes/tool_gitlab_create_mr.md +1 -0
  94. package/builtin/nodes/tool_jenkins_build.md +2 -0
  95. package/builtin/nodes/tool_load_key.md +2 -0
  96. package/builtin/nodes/tool_nodejs.md +17 -19
  97. package/builtin/nodes/tool_print.md +2 -0
  98. package/builtin/nodes/tool_save_key.md +2 -0
  99. package/builtin/nodes/tool_set_run_env.md +1 -0
  100. package/builtin/nodes/tool_user_ask.md +2 -0
  101. package/builtin/nodes/tool_user_check.md +2 -0
  102. package/builtin/nodes/tool_wecom_send_app_markdown.md +1 -0
  103. package/builtin/nodes/tool_wecom_send_group_markdown.md +1 -0
  104. package/builtin/nodes/workspace_one_click_task.md +44 -0
  105. package/builtin/nodes/workspace_run.md +16 -0
  106. package/builtin/nodes/workspace_scheduled_run.md +16 -0
  107. package/builtin/pipelines/module-migrate/scripts/gate.mjs +37 -0
  108. package/builtin/pipelines/module-migrate/scripts/static-check.mjs +82 -0
  109. package/builtin/pipelines/module-migrate/workspace.flow.js +172 -0
  110. package/builtin/pipelines/module-migrate/workspace.layout.json +134 -0
  111. package/builtin/pipelines/new/scripts/lint-flow.mjs +38 -0
  112. package/builtin/pipelines/new/workspace.flow.js +91 -0
  113. package/builtin/pipelines/new/workspace.layout.json +70 -0
  114. package/builtin/web-ui/dist/assets/{WorkflowAssistantThread-DPbR2UaN.js → WorkflowAssistantThread-CKClwj96.js} +1 -1
  115. package/builtin/web-ui/dist/assets/index-BZ5KqLur.js +870 -0
  116. package/builtin/web-ui/dist/assets/index-CEXmmwM2.css +1 -0
  117. package/builtin/web-ui/dist/index.html +2 -2
  118. package/package.json +2 -1
  119. package/reference/flow-control-capabilities.md +77 -158
  120. package/reference/flow-layout.md +1 -1
  121. package/reference/flow-prompt-handler-check.md +2 -2
  122. package/skills/agentflow-author-flow/SKILL.md +8 -47
  123. package/skills/agentflow-cli/SKILL.md +158 -25
  124. package/skills/agentflow-cli/agents/openai.yaml +2 -2
  125. package/skills/agentflow-cli/scripts/agentflow-cli.mjs +668 -18
  126. package/skills/agentflow-cli/scripts/agentflow-runtime.mjs +97 -0
  127. package/skills/agentflow-flow-add-instances/SKILL.md +7 -248
  128. package/skills/agentflow-flow-dsl/SKILL.md +206 -0
  129. package/skills/agentflow-flow-dsl/agents/openai.yaml +4 -0
  130. package/skills/agentflow-flow-dsl/references/node-calls.md +39 -0
  131. package/skills/agentflow-flow-edit-node-fields/SKILL.md +4 -73
  132. package/skills/agentflow-flow-recipes/SKILL.md +7 -18
  133. package/skills/agentflow-flow-recipes/references/recipes.md +97 -43
  134. package/skills/agentflow-flow-sync-ui/SKILL.md +3 -54
  135. package/skills/agentflow-node-dsl/SKILL.md +212 -0
  136. package/skills/agentflow-node-dsl/agents/openai.yaml +4 -0
  137. package/skills/agentflow-node-reference/SKILL.md +5 -4
  138. package/skills/agentflow-node-reference/references/builtin-nodes.md +166 -115
  139. package/skills/agentflow-placeholder-reference/SKILL.md +2 -2
  140. package/skills/agentflow-runtime-reference/SKILL.md +2 -2
  141. package/skills/agentflow-runtime-reference/references/runtime.md +1 -1
  142. package/skills/agentflow-workspace-ascii/SKILL.md +9 -16
  143. package/skills/agentflow-workspace-graph/SKILL.md +62 -48
  144. package/skills/agentflow-workspace-html/SKILL.md +7 -2
  145. package/skills/agentflow-workspace-image/SKILL.md +6 -2
  146. package/skills/agentflow-workspace-markdown/SKILL.md +14 -21
  147. package/skills/agentflow-workspace-mermaid/SKILL.md +8 -16
  148. package/bin/lib/api-runner.mjs +0 -387
  149. package/bin/lib/apply.mjs +0 -903
  150. package/bin/lib/composer-flow-instances.mjs +0 -68
  151. package/bin/lib/composer-flow-skeleton.mjs +0 -334
  152. package/bin/lib/composer-flow-validate.mjs +0 -47
  153. package/bin/lib/composer-model-router.mjs +0 -185
  154. package/bin/lib/composer-node-schema.mjs +0 -303
  155. package/bin/lib/composer-planner.mjs +0 -751
  156. package/bin/lib/composer-script-ops.mjs +0 -233
  157. package/bin/lib/flow-static-preview.mjs +0 -104
  158. package/bin/lib/hub-login.mjs +0 -54
  159. package/bin/lib/hub-publish.mjs +0 -159
  160. package/bin/lib/hub-remote.mjs +0 -189
  161. package/bin/lib/hub.mjs +0 -299
  162. package/bin/lib/jenkins.mjs +0 -380
  163. package/bin/lib/node-execute.mjs +0 -539
  164. package/bin/lib/normalize-node-tool-command.mjs +0 -97
  165. package/bin/lib/runtime-context.mjs +0 -243
  166. package/bin/lib/scheduler.mjs +0 -601
  167. package/bin/lib/ui-print.mjs +0 -94
  168. package/bin/pipeline/build-node-prompt.mjs +0 -271
  169. package/bin/pipeline/check-cache.mjs +0 -191
  170. package/bin/pipeline/check-flow.mjs +0 -543
  171. package/bin/pipeline/collect-nodes.mjs +0 -212
  172. package/bin/pipeline/compute-cache-md5.mjs +0 -177
  173. package/bin/pipeline/ensure-run-dir.mjs +0 -71
  174. package/bin/pipeline/gc.mjs +0 -129
  175. package/bin/pipeline/get-env.mjs +0 -59
  176. package/bin/pipeline/get-resolved-values.mjs +0 -344
  177. package/bin/pipeline/load-key.mjs +0 -62
  178. package/bin/pipeline/parse-flow.mjs +0 -708
  179. package/bin/pipeline/post-process-control-if.mjs +0 -23
  180. package/bin/pipeline/post-process-node.mjs +0 -490
  181. package/bin/pipeline/pre-process-node.mjs +0 -1430
  182. package/bin/pipeline/resolve-inputs.mjs +0 -201
  183. package/bin/pipeline/run-tool-nodejs.mjs +0 -167
  184. package/bin/pipeline/save-key.mjs +0 -93
  185. package/bin/pipeline/snapshot-prior-round.mjs +0 -70
  186. package/bin/pipeline/validate-for-ui.mjs +0 -234
  187. package/bin/pipeline/validate-script-output.mjs +0 -130
  188. package/bin/pipeline/write-result.mjs +0 -182
  189. package/builtin/pipelines/module-migrate/flow.yaml +0 -819
  190. package/builtin/pipelines/new/flow.yaml +0 -545
  191. package/builtin/pipelines/new/scripts/check-flow.mjs +0 -9
  192. package/builtin/pipelines/new/scripts/collect-nodes.mjs +0 -211
  193. package/builtin/web-ui/dist/assets/index-BSCpd5la.js +0 -889
  194. package/builtin/web-ui/dist/assets/index-DZ328oSo.css +0 -1
  195. 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,17 @@ 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";
31
+ import { LEGACY_FLOW_EXECUTION_DISABLED, LEGACY_FLOW_EXECUTION_MESSAGE } from "./legacy-flow-execution.mjs";
39
32
 
40
33
  async function readStdin() {
41
34
  const chunks = [];
@@ -43,6 +36,31 @@ async function readStdin() {
43
36
  return Buffer.concat(chunks).toString("utf8");
44
37
  }
45
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
+
46
64
  export async function main() {
47
65
  const argv = process.argv.slice(2);
48
66
  let workspaceRoot = process.cwd();
@@ -60,54 +78,26 @@ export async function main() {
60
78
  printHelp();
61
79
  process.exit(0);
62
80
  }
63
- const dryRun = argv.includes("--dry-run");
64
- if (dryRun) argv.splice(argv.indexOf("--dry-run"), 1);
81
+ if (argv.includes("--dry-run")) argv.splice(argv.indexOf("--dry-run"), 1);
65
82
  if (argv.includes("--debug")) {
66
83
  setLogLevel(LOG_LEVELS.debug);
67
84
  argv.splice(argv.indexOf("--debug"), 1);
68
85
  }
69
- let force = true;
70
- if (argv.includes("--no-force")) {
71
- force = false;
72
- argv.splice(argv.indexOf("--no-force"), 1);
73
- }
74
- if (argv.includes("--force")) {
75
- force = true;
76
- argv.splice(argv.indexOf("--force"), 1);
77
- }
78
- if (argv.includes("--yolo")) {
79
- force = true;
80
- argv.splice(argv.indexOf("--yolo"), 1);
81
- }
82
- let parallel = false;
83
- if (argv.includes("--parallel")) {
84
- parallel = true;
85
- argv.splice(argv.indexOf("--parallel"), 1);
86
- }
87
- if (argv.includes("--no-parallel")) {
88
- parallel = false;
89
- 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);
90
89
  }
91
90
  if (argv.includes("--machine-readable")) {
92
91
  setMachineReadable(true);
93
92
  argv.splice(argv.indexOf("--machine-readable"), 1);
94
93
  }
95
94
  const jsonMode = argv.includes("--json");
96
- const cliInputs = {};
97
95
  while (argv.includes("--input")) {
98
96
  const idx = argv.indexOf("--input");
99
97
  const pair = argv[idx + 1];
100
98
  if (!pair || !pair.includes("=")) {
101
99
  throw new Error("Invalid --input format. Use: --input name=value");
102
100
  }
103
- const eqIdx = pair.indexOf("=");
104
- const name = pair.slice(0, eqIdx);
105
- const value = pair.slice(eqIdx + 1);
106
- if (value.startsWith("file:")) {
107
- cliInputs[name] = { type: "file", path: value.slice(5) };
108
- } else {
109
- cliInputs[name] = { type: "str", value };
110
- }
111
101
  argv.splice(idx, 2);
112
102
  }
113
103
  const sub = shift();
@@ -128,7 +118,6 @@ export async function main() {
128
118
  "list-flows",
129
119
  "list-nodes",
130
120
  "read-flow",
131
- "write-flow",
132
121
  "read-node",
133
122
  "copy-builtin",
134
123
  "list-agents",
@@ -139,12 +128,8 @@ export async function main() {
139
128
  if (jsonMode && jsonOnlySubs.includes(sub)) {
140
129
  argv.splice(argv.indexOf("--json"), 1);
141
130
  }
142
- let agentModel = process.env.CURSOR_AGENT_MODEL || null;
143
131
  const modelIdx = argv.indexOf("--model");
144
- if (modelIdx >= 0 && argv[modelIdx + 1]) {
145
- agentModel = argv[modelIdx + 1];
146
- argv.splice(modelIdx, 2);
147
- }
132
+ if (modelIdx >= 0 && argv[modelIdx + 1]) argv.splice(modelIdx, 2);
148
133
  if (sub === "list-flows" && jsonMode) {
149
134
  const list = listFlowsJson(workspaceRoot);
150
135
  process.stdout.write(JSON.stringify(list) + "\n");
@@ -242,19 +227,7 @@ export async function main() {
242
227
  else throw new Error(result.error || "publish-node failed");
243
228
  process.exit(result.ok ? 0 : 1);
244
229
  }
245
- if (action === "install-node") {
246
- const flowId = shift();
247
- const spec = shift();
248
- if (!flowId || !spec) throw new Error("Usage: agentflow marketplace install-node <flow> <nodeSpec> [--json]");
249
- const flowDir = getFlowDir(workspaceRoot, flowId);
250
- if (!flowDir) throw new Error(`Flow not found: ${flowId}`);
251
- const result = installFlowDependency(workspaceRoot, flowDir, spec);
252
- if (jsonMode) process.stdout.write(JSON.stringify(result) + "\n");
253
- else if (result.ok) process.stdout.write(`Installed ${result.definitionId} into ${flowId}\n`);
254
- else throw new Error(result.error || "install-node failed");
255
- process.exit(result.ok ? 0 : 1);
256
- }
257
- throw new Error("Usage: agentflow marketplace <list|publish-node|install-node> [--json]");
230
+ throw new Error("Usage: agentflow marketplace <list|publish-node> [--json]");
258
231
  }
259
232
  if (sub === "copy-builtin" && jsonMode) {
260
233
  const flowId = shift();
@@ -320,101 +293,105 @@ export async function main() {
320
293
  process.stdout.write(JSON.stringify(result) + "\n");
321
294
  process.exit(result.success ? 0 : 1);
322
295
  }
323
- if (sub === "write-flow" && jsonMode) {
324
- let flowSource = "user";
325
- const flowSourceIdx = argv.indexOf("--flow-source");
326
- if (flowSourceIdx >= 0 && argv[flowSourceIdx + 1]) {
327
- flowSource = argv[flowSourceIdx + 1];
328
- argv.splice(flowSourceIdx, 2);
329
- }
330
- if (flowSource === "builtin") {
331
- process.stderr.write(
332
- "agentflow: --flow-source builtin 已弃用(包内 builtin 不可写);已按 workspace 写入 .workspace/agentflow/pipelines。\n",
333
- );
334
- flowSource = "workspace";
335
- }
336
- if (flowSource !== "user" && flowSource !== "workspace") {
337
- process.stdout.write(
338
- JSON.stringify({ success: false, error: "Invalid --flow-source (use user or workspace)" }) + "\n",
339
- );
340
- process.exit(1);
341
- }
342
- const flowId = argv.find((a) => !a.startsWith("--"));
343
- if (!flowId) {
344
- process.stdout.write(JSON.stringify({ success: false, error: "Missing flowId" }) + "\n");
345
- process.exit(1);
346
- }
347
- const flowYaml = await readStdin();
348
- const result = writeFlowYaml(workspaceRoot, flowId, flowSource, flowYaml);
349
- process.stdout.write(JSON.stringify(result.success ? { success: true } : result) + "\n");
350
- process.exit(result.success ? 0 : 1);
351
- }
352
- if (sub === "flow" && argv[0] === "preview") {
296
+ if (sub === "flow" && argv[0] === "dsl") {
353
297
  shift();
298
+ const action = shift();
354
299
  const target = shift();
355
- if (!target) throw new Error("Usage: agentflow flow preview <FlowName|flow.yaml> [--output <preview.html>] [--no-open]");
356
- let outputPath = "";
357
- const outputIdx = argv.indexOf("--output");
358
- if (outputIdx >= 0 && argv[outputIdx + 1]) {
359
- outputPath = path.resolve(workspaceRoot, argv[outputIdx + 1]);
360
- argv.splice(outputIdx, 2);
361
- }
362
- const noOpen = argv.includes("--no-open");
363
- if (noOpen) argv.splice(argv.indexOf("--no-open"), 1);
364
- 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");
365
317
 
366
- const targetPath = path.resolve(workspaceRoot, target);
367
- let previewFlowPath = "";
368
- if (fs.existsSync(targetPath)) {
369
- previewFlowPath = fs.statSync(targetPath).isDirectory()
370
- ? path.join(targetPath, "flow.yaml")
371
- : targetPath;
372
- } else {
373
- const flowDir = getFlowDir(workspaceRoot, target);
374
- if (flowDir) previewFlowPath = path.join(flowDir, "flow.yaml");
375
- }
376
- if (!previewFlowPath || !fs.existsSync(previewFlowPath) || !fs.statSync(previewFlowPath).isFile()) {
377
- throw new Error(`Flow not found: ${target}`);
378
- }
379
- previewFlowPath = fs.realpathSync(previewFlowPath);
380
- const flowId = path.basename(path.dirname(previewFlowPath)) || "local-preview";
381
- const safeFlowId = flowId.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "flow-preview";
382
- if (!outputPath) {
383
- outputPath = path.join(workspaceRoot, ".workspace", "agentflow", "previews", `${safeFlowId}.html`);
384
- }
385
- if (!/\.html?$/i.test(outputPath)) throw new Error("Preview output must be an HTML file");
386
- const nodeCatalog = listNodesJson(workspaceRoot, flowId, "", {
387
- staticFlowPath: previewFlowPath,
388
- marketplaceScope: "all",
389
- });
390
- const result = writeStaticFlowPreview({
391
- flowId,
392
- flowPath: previewFlowPath,
393
- nodeCatalog,
394
- outputPath,
395
- distDir: path.join(path.dirname(path.dirname(path.dirname(fileURLToPath(import.meta.url)))), "builtin", "web-ui", "dist"),
396
- });
397
- const url = pathToFileURL(result.outputPath).href;
398
- process.stderr.write(`AgentFlow static preview: ${result.outputPath}\nSource: ${previewFlowPath}\n`);
399
- if (!noOpen) {
400
- if (process.platform === "win32") {
401
- const child = spawn("cmd", ["/c", "start", "", url], { detached: true, stdio: "ignore" });
402
- child.unref();
403
- } else if (process.platform === "darwin") {
404
- const child = spawn("open", [url], { detached: true, stdio: "ignore" });
405
- 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`);
406
340
  } else {
407
- const child = spawn("xdg-open", [url], { detached: true, stdio: "ignore" });
408
- child.unref();
341
+ process.stderr.write(`${chalk.red("keep")} ${dir}:${result.degradedReason},保留 workspace.graph.json\n`);
342
+ process.exitCode = 1;
409
343
  }
344
+ // 损耗清单在成功和拒绝两种结局下都要打出来——`--allow-loss` 迁过去了不代表没丢东西
345
+ writeMigrationLoss(result);
346
+ if (result.format === "yaml" && !result.migrated) process.exitCode = 1;
347
+ return;
410
348
  }
411
- 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]");
412
391
  }
413
392
  if (sub === "ui") {
414
393
  let port = 8765;
415
394
  let host = process.env.AGENTFLOW_UI_HOST || "127.0.0.1";
416
- let schedulerEnabled = false;
417
- let schedulerPollMs;
418
395
  let hideCommunityLinks = /^(1|true|yes|on)$/i.test(String(process.env.AGENTFLOW_HIDE_COMMUNITY_LINKS || ""));
419
396
  const portIdx = argv.indexOf("--port");
420
397
  if (portIdx >= 0 && argv[portIdx + 1]) {
@@ -426,19 +403,6 @@ export async function main() {
426
403
  host = argv[hostIdx + 1];
427
404
  argv.splice(hostIdx, 2);
428
405
  }
429
- if (argv.includes("--scheduler")) {
430
- schedulerEnabled = true;
431
- argv.splice(argv.indexOf("--scheduler"), 1);
432
- }
433
- if (argv.includes("--no-scheduler")) {
434
- schedulerEnabled = false;
435
- argv.splice(argv.indexOf("--no-scheduler"), 1);
436
- }
437
- const schedulerPollIdx = argv.indexOf("--scheduler-poll-ms");
438
- if (schedulerPollIdx >= 0 && argv[schedulerPollIdx + 1]) {
439
- schedulerPollMs = parseInt(argv[schedulerPollIdx + 1], 10);
440
- argv.splice(schedulerPollIdx, 2);
441
- }
442
406
  const noOpen = argv.includes("--no-open");
443
407
  if (noOpen) argv.splice(argv.indexOf("--no-open"), 1);
444
408
  if (argv.includes("--hide-community-links")) {
@@ -452,11 +416,6 @@ export async function main() {
452
416
  throw new Error("Invalid --host");
453
417
  }
454
418
  await startUiServer({ workspaceRoot, port, host, hideCommunityLinks });
455
- if (schedulerEnabled) {
456
- startScheduler(workspaceRoot, { pollMs: schedulerPollMs }).catch((e) => {
457
- log.error("Scheduler failed: " + ((e && e.message) || String(e)));
458
- });
459
- }
460
419
  const browserHost = host === "0.0.0.0" || host === "::" ? "127.0.0.1" : host;
461
420
  const url = "http://" + (browserHost.includes(":") ? `[${browserHost}]` : browserHost) + ":" + port;
462
421
  process.stderr.write("AgentFlow UI: " + url + (browserHost === host ? "" : ` (listening on ${host}:${port})`) + "\n");
@@ -474,127 +433,12 @@ export async function main() {
474
433
  }
475
434
  await new Promise(() => {});
476
435
  }
477
- if (sub === "scheduler") {
478
- const action = shift();
479
- if (action === "start") {
480
- let pollMs;
481
- const pollIdx = argv.indexOf("--poll-ms");
482
- if (pollIdx >= 0 && argv[pollIdx + 1]) {
483
- pollMs = parseInt(argv[pollIdx + 1], 10);
484
- argv.splice(pollIdx, 2);
485
- }
486
- const once = argv.includes("--once");
487
- if (once) argv.splice(argv.indexOf("--once"), 1);
488
- await startScheduler(workspaceRoot, { pollMs, once });
489
- process.exit(0);
490
- }
491
- if (action === "status") {
492
- if (jsonMode) {
493
- process.stdout.write(JSON.stringify({ schedules: listScheduleStatuses(workspaceRoot) }) + "\n");
494
- process.exit(0);
495
- }
496
- const rows = listScheduleStatuses(workspaceRoot);
497
- const table = new Table({ head: ["flow", "source", "enabled", "cron", "timezone", "next", "running", "waiting", "lastRun", "error"], style: { head: [] } });
498
- for (const r of rows) {
499
- table.push([
500
- r.flowId,
501
- r.flowSource,
502
- r.enabled ? "yes" : "no",
503
- r.cron || "",
504
- r.timezone || "",
505
- r.nextRunAt || "",
506
- r.running ? "yes" : "no",
507
- String(r.waiting || 0),
508
- r.lastRunUuid || "",
509
- r.lastError || "",
510
- ]);
511
- }
512
- process.stdout.write(table.toString() + "\n");
513
- process.exit(0);
514
- }
515
- if (action === "cancel") {
516
- const flowId = shift();
517
- const uuid = shift();
518
- if (!flowId || !uuid) throw new Error("Usage: agentflow scheduler cancel <flow> <uuid> [--json]");
519
- const result = cancelScheduledRun(workspaceRoot, flowId, uuid);
520
- if (jsonMode) {
521
- process.stdout.write(JSON.stringify(result) + "\n");
522
- } else if (result.ok) {
523
- process.stdout.write(`Cancelled ${flowId}/${uuid}; updated waits: ${result.updatedWaits}\n`);
524
- } else {
525
- throw new Error(result.error || "cancel failed");
526
- }
527
- process.exit(result.ok ? 0 : 1);
528
- }
529
- throw new Error("Usage: agentflow scheduler <start|status|cancel> [--once] [--poll-ms <ms>] [--json]");
530
- }
531
436
  // ──── Hub commands ────
532
- if (sub === "login") {
533
- await hubLogin(argv);
534
- process.exit(0);
535
- }
536
- if (sub === "logout") {
537
- hubLogout();
538
- process.exit(0);
539
- }
540
- if (sub === "publish") {
541
- await hubPublish(workspaceRoot, argv);
542
- process.exit(0);
543
- }
544
- if (sub === "list-remote") {
545
- await hubListRemote(argv);
546
- process.exit(0);
547
- }
548
- if (sub === "download") {
549
- await hubDownload(argv);
550
- process.exit(0);
551
- }
552
437
  // ──── Local commands ────
553
438
  if (sub === "list") {
554
439
  listPipelines(workspaceRoot);
555
- } else if (sub === "apply") {
556
- const aiMode = argv[0] === "-ai" || argv[0] === "--ai";
557
- if (aiMode) {
558
- argv.shift();
559
- const step = argv.shift();
560
- if (!step || !APPLY_AI_STEPS.includes(step)) {
561
- throw new Error(
562
- "Missing or invalid step. Usage: agentflow apply -ai <step> <args...>. Steps: " + APPLY_AI_STEPS.join(", "),
563
- );
564
- }
565
- if (argv.length === 0) {
566
- throw new Error("Missing args for step " + step + ". Example: agentflow apply -ai ensure-run-dir <workspaceRoot> [uuid] <flowName>");
567
- }
568
- const stepWorkspaceRoot = path.resolve(argv[0]);
569
- ensureReference(stepWorkspaceRoot);
570
- const scriptName = step + ".mjs";
571
- const result = runNodeScript(stepWorkspaceRoot, scriptName, argv, { captureStdout: false });
572
- process.exit(result.status ?? 0);
573
- }
574
- const first = shift();
575
- if (!first) throw new Error("Missing FlowName or uuid. Usage: agentflow apply <FlowName> [uuid] | agentflow apply <uuid>");
576
- let flowName, uuidArg;
577
- if (isValidUuid(first)) {
578
- flowName = findFlowNameByUuid(workspaceRoot, first);
579
- if (!flowName) throw new Error("No run found for uuid " + first + ". Run apply with FlowName first (e.g. agentflow apply <FlowName>).");
580
- uuidArg = first;
581
- } else {
582
- flowName = first;
583
- uuidArg = isValidUuid(argv[0]) ? shift() : undefined;
584
- }
585
- await apply(workspaceRoot, flowName, uuidArg, dryRun, agentModel, force, parallel, cliInputs);
586
- } else if (sub === "resume") {
587
- const flowName = shift();
588
- const uuidArg = shift();
589
- if (!flowName || !uuidArg) throw new Error("Usage: agentflow resume <FlowName> <uuid> [instanceId]");
590
- const instanceIdOpt = argv.length > 0 && !argv[0].startsWith("--") ? shift() : undefined;
591
- await resume(workspaceRoot, flowName, uuidArg, instanceIdOpt, agentModel, force, parallel);
592
- } else if (sub === "replay") {
593
- const a = shift(),
594
- b = shift(),
595
- c = shift();
596
- if (!a || !b) throw new Error("Usage: agentflow replay <uuid> <instanceId> or agentflow replay <flowName> <uuid> <instanceId>");
597
- 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);
598
442
  } else if (sub === "run-status") {
599
443
  const flowName = shift();
600
444
  const uuidArg = shift();
@@ -659,6 +503,33 @@ export async function main() {
659
503
  ", or builtin)",
660
504
  );
661
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
+
662
533
  const args = [workspaceRoot, flowName, flowDir];
663
534
  if (uuidArg) args.push(uuidArg);
664
535
  const result = runNodeScript(workspaceRoot, "validate-flow.mjs", args, { captureStdout: true });
@@ -720,7 +591,6 @@ export async function main() {
720
591
  } else if (
721
592
  sub === "list-flows" ||
722
593
  sub === "read-flow" ||
723
- sub === "write-flow" ||
724
594
  sub === "read-node" ||
725
595
  sub === "copy-builtin" ||
726
596
  sub === "copy-builtin-agent" ||
@@ -732,7 +602,7 @@ export async function main() {
732
602
  throw new Error(
733
603
  "Unknown command: " +
734
604
  sub +
735
- ". 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.",
736
606
  );
737
607
  }
738
608
  }