@fieldwangai/agentflow 0.1.153 → 0.1.156

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 (193) 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 +277 -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 +303 -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 +1092 -18123
  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 +12 -2
  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-B9xslwI0.js → WorkflowAssistantThread-ClNxY2Wu.js} +1 -1
  115. package/builtin/web-ui/dist/assets/index-BJzMYRK3.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 +3 -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 +1 -1
  123. package/skills/agentflow-cli/SKILL.md +139 -24
  124. package/skills/agentflow-cli/agents/openai.yaml +2 -2
  125. package/skills/agentflow-cli/scripts/agentflow-cli.mjs +654 -20
  126. package/skills/agentflow-cli/scripts/agentflow-runtime.mjs +97 -0
  127. package/skills/agentflow-flow-add-instances/SKILL.md +1 -1
  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 +1 -1
  132. package/skills/agentflow-flow-recipes/SKILL.md +7 -4
  133. package/skills/agentflow-flow-recipes/references/recipes.md +97 -43
  134. package/skills/agentflow-flow-sync-ui/SKILL.md +1 -1
  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 +2 -2
  138. package/skills/agentflow-node-reference/references/builtin-nodes.md +166 -115
  139. package/skills/agentflow-runtime-reference/references/runtime.md +1 -1
  140. package/skills/agentflow-workspace-ascii/SKILL.md +9 -16
  141. package/skills/agentflow-workspace-graph/SKILL.md +62 -48
  142. package/skills/agentflow-workspace-html/SKILL.md +7 -2
  143. package/skills/agentflow-workspace-image/SKILL.md +6 -2
  144. package/skills/agentflow-workspace-markdown/SKILL.md +14 -21
  145. package/skills/agentflow-workspace-mermaid/SKILL.md +8 -16
  146. package/bin/lib/api-runner.mjs +0 -387
  147. package/bin/lib/apply.mjs +0 -903
  148. package/bin/lib/composer-flow-instances.mjs +0 -68
  149. package/bin/lib/composer-flow-skeleton.mjs +0 -334
  150. package/bin/lib/composer-flow-validate.mjs +0 -47
  151. package/bin/lib/composer-model-router.mjs +0 -185
  152. package/bin/lib/composer-node-schema.mjs +0 -303
  153. package/bin/lib/composer-planner.mjs +0 -751
  154. package/bin/lib/composer-script-ops.mjs +0 -233
  155. package/bin/lib/flow-static-preview.mjs +0 -104
  156. package/bin/lib/hub-login.mjs +0 -54
  157. package/bin/lib/hub-publish.mjs +0 -159
  158. package/bin/lib/hub-remote.mjs +0 -189
  159. package/bin/lib/hub.mjs +0 -299
  160. package/bin/lib/jenkins.mjs +0 -380
  161. package/bin/lib/node-execute.mjs +0 -539
  162. package/bin/lib/normalize-node-tool-command.mjs +0 -97
  163. package/bin/lib/runtime-context.mjs +0 -243
  164. package/bin/lib/scheduler.mjs +0 -601
  165. package/bin/lib/ui-print.mjs +0 -94
  166. package/bin/pipeline/build-node-prompt.mjs +0 -271
  167. package/bin/pipeline/check-cache.mjs +0 -191
  168. package/bin/pipeline/check-flow.mjs +0 -543
  169. package/bin/pipeline/collect-nodes.mjs +0 -212
  170. package/bin/pipeline/compute-cache-md5.mjs +0 -177
  171. package/bin/pipeline/ensure-run-dir.mjs +0 -71
  172. package/bin/pipeline/gc.mjs +0 -129
  173. package/bin/pipeline/get-env.mjs +0 -59
  174. package/bin/pipeline/get-resolved-values.mjs +0 -344
  175. package/bin/pipeline/load-key.mjs +0 -62
  176. package/bin/pipeline/parse-flow.mjs +0 -708
  177. package/bin/pipeline/post-process-control-if.mjs +0 -23
  178. package/bin/pipeline/post-process-node.mjs +0 -490
  179. package/bin/pipeline/pre-process-node.mjs +0 -1430
  180. package/bin/pipeline/resolve-inputs.mjs +0 -201
  181. package/bin/pipeline/run-tool-nodejs.mjs +0 -167
  182. package/bin/pipeline/save-key.mjs +0 -93
  183. package/bin/pipeline/snapshot-prior-round.mjs +0 -70
  184. package/bin/pipeline/validate-for-ui.mjs +0 -234
  185. package/bin/pipeline/validate-script-output.mjs +0 -130
  186. package/bin/pipeline/write-result.mjs +0 -182
  187. package/builtin/pipelines/module-migrate/flow.yaml +0 -819
  188. package/builtin/pipelines/new/flow.yaml +0 -545
  189. package/builtin/pipelines/new/scripts/check-flow.mjs +0 -9
  190. package/builtin/pipelines/new/scripts/collect-nodes.mjs +0 -211
  191. package/builtin/web-ui/dist/assets/index-DZ328oSo.css +0 -1
  192. package/builtin/web-ui/dist/assets/index-DmUV7ZCL.js +0 -888
  193. package/skills/agentflow-node-authoring/SKILL.md +0 -57
@@ -1,212 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * 收集所有节点(内置 + 当前流水线)的元数据,按 tool_nodejs 约定向 stdout 输出一行 JSON。
4
- * 用法:agentflow apply -ai collect-nodes <workspaceRoot> <flowName> [runDir]
5
- * 输出(仅 stdout 一行):{ "err_code": 0, "message": { "result": "<节点元数据 markdown>" } };err_code 0=成功 1=失败,无 next。
6
- * 不写任何文件,由 agentflow apply -ai run-tool-nodejs 根据 message 写入 output。
7
- */
8
-
9
- import fs from "fs";
10
- import path from "path";
11
- import { fileURLToPath } from "url";
12
- import yaml from "js-yaml";
13
-
14
- import { LEGACY_NODES_DIR, PIPELINES_DIR, PROJECT_NODES_DIR } from "../lib/paths.mjs";
15
- import { getFlowDir } from "../lib/workspace.mjs";
16
-
17
- const __dirnameCollect = path.dirname(fileURLToPath(import.meta.url));
18
- const PACKAGE_BUILTIN_NODES_DIR = path.join(path.resolve(__dirnameCollect, "..", ".."), "builtin", "nodes");
19
-
20
- function extractFrontmatter(raw) {
21
- const m = raw.match(/^---\s*\n([\s\S]*?)\n---/);
22
- return m ? m[1] : "";
23
- }
24
-
25
- function parseDescription(fm) {
26
- const match = fm.match(/\bdescription:\s*["']?([^"'\n#][^\n]*)["']?/);
27
- return match ? match[1].trim().replace(/^["']|["']$/g, "") : "";
28
- }
29
-
30
- function parseDisplayName(fm) {
31
- const match = fm.match(/\bdisplayName:\s*["']?([^"'\n#][^\n]*)["']?/);
32
- return match ? match[1].trim().replace(/^["']|["']$/g, "") : "";
33
- }
34
-
35
- /** 解析 frontmatter 中的 input/output 数组,返回 [{ type, name, defaultOrValue }, ...] */
36
- function parseSlots(fm, key) {
37
- const slots = [];
38
- const re = new RegExp(`\\b${key}:\\s*\\n([\\s\\S]*?)(?=\\n[a-zA-Z_][a-zA-Z0-9_]*\\s*:|---|$)`, "m");
39
- const blockMatch = fm.match(re);
40
- if (!blockMatch) return slots;
41
- const block = blockMatch[1];
42
- let current = {};
43
- for (const line of block.split("\n")) {
44
- const typeMatch = line.match(/^\s*-\s+type:\s*["']?([^"'\n]*)["']?/);
45
- if (typeMatch) {
46
- if (current.type) slots.push({ ...current });
47
- current = { type: typeMatch[1].trim() };
48
- continue;
49
- }
50
- const nameMatch = line.match(/^\s+name:\s*["']?([^"'\n]*)["']?/);
51
- if (nameMatch) {
52
- current.name = nameMatch[1].trim();
53
- continue;
54
- }
55
- const defaultMatch = line.match(/^\s+(?:default|value):\s*(.*)$/);
56
- if (defaultMatch) {
57
- current.defaultOrValue = defaultMatch[1].trim().replace(/^["']|["']$/g, "");
58
- slots.push({ ...current });
59
- current = {};
60
- }
61
- }
62
- if (current.type) slots.push({ ...current });
63
- return slots;
64
- }
65
-
66
- function readNodeMeta(filePath) {
67
- try {
68
- if (!fs.existsSync(filePath)) return null;
69
- const raw = fs.readFileSync(filePath, "utf-8");
70
- const fm = extractFrontmatter(raw);
71
- return {
72
- description: parseDescription(fm),
73
- displayName: parseDisplayName(fm),
74
- input: parseSlots(fm, "input"),
75
- output: parseSlots(fm, "output"),
76
- };
77
- } catch {
78
- return null;
79
- }
80
- }
81
-
82
- /** 从 flow.yaml 的 instances 得到节点列表,每项 { id, label, definitionId, input, output },input/output 为 [{ type, name, defaultOrValue }] */
83
- function loadFlowYamlNodes(flowDir) {
84
- const flowPath = path.join(flowDir, "flow.yaml");
85
- if (!fs.existsSync(flowPath)) return [];
86
- try {
87
- const raw = fs.readFileSync(flowPath, "utf-8");
88
- const data = yaml.load(raw);
89
- const instances = data?.instances && typeof data.instances === "object" ? data.instances : {};
90
- return Object.entries(instances).map(([id, inst]) => {
91
- const inp = Array.isArray(inst.input) ? inst.input : [];
92
- const out = Array.isArray(inst.output) ? inst.output : [];
93
- const input = inp.map((s) => ({
94
- type: (s && s.type != null) ? String(s.type).trim() : "",
95
- name: (s && s.name != null) ? String(s.name).trim() : "",
96
- defaultOrValue: (s && (s.value != null || s.default != null)) ? String(s.value ?? s.default ?? "").trim() : "",
97
- }));
98
- const output = out.map((s) => ({
99
- type: (s && s.type != null) ? String(s.type).trim() : "",
100
- name: (s && s.name != null) ? String(s.name).trim() : "",
101
- defaultOrValue: (s && (s.value != null || s.default != null)) ? String(s.value ?? s.default ?? "").trim() : "",
102
- }));
103
- return {
104
- id,
105
- label: (inst.label != null) ? String(inst.label) : id,
106
- definitionId: (inst.definitionId != null) ? String(inst.definitionId) : id,
107
- input,
108
- output,
109
- };
110
- });
111
- } catch (_) {
112
- return [];
113
- }
114
- }
115
-
116
- function main() {
117
- const args = process.argv.slice(2);
118
- if (args.length < 2) {
119
- const payload = { err_code: 1, message: { result: "Usage: agentflow apply -ai collect-nodes <workspaceRoot> <flowName> [runDir]" } };
120
- console.log(JSON.stringify(payload));
121
- process.exit(1);
122
- }
123
-
124
- const [workspaceRoot, flowName] = args.map((p) => path.resolve(p));
125
- const nodesDirLegacy = path.join(workspaceRoot, LEGACY_NODES_DIR);
126
- const nodesDirNew = path.join(workspaceRoot, PROJECT_NODES_DIR);
127
- const flowDir = getFlowDir(workspaceRoot, flowName) || path.join(workspaceRoot, PIPELINES_DIR, flowName);
128
-
129
- const out = [];
130
-
131
- // 1. 内置节点:包内 builtin/nodes + 项目 .workspace/agentflow/nodes(覆盖旧 .cursor/...),包内同 id 优先
132
- out.push("# 节点元数据(内置 + 当前流水线)\n");
133
- out.push("## 1. 内置节点元数据\n\n");
134
- const builtinFiles = new Map();
135
- if (fs.existsSync(PACKAGE_BUILTIN_NODES_DIR)) {
136
- for (const file of fs.readdirSync(PACKAGE_BUILTIN_NODES_DIR).filter((f) => f.endsWith(".md")).sort()) {
137
- builtinFiles.set(file.replace(/\.md$/, ""), path.join(PACKAGE_BUILTIN_NODES_DIR, file));
138
- }
139
- }
140
- const projectById = new Map();
141
- const mergeProjectDir = (dir) => {
142
- if (!fs.existsSync(dir)) return;
143
- for (const file of fs.readdirSync(dir).filter((f) => f.endsWith(".md")).sort()) {
144
- projectById.set(file.replace(/\.md$/, ""), path.join(dir, file));
145
- }
146
- };
147
- mergeProjectDir(nodesDirLegacy);
148
- mergeProjectDir(nodesDirNew);
149
- for (const [id, fp] of projectById) {
150
- if (!builtinFiles.has(id)) builtinFiles.set(id, fp);
151
- }
152
- for (const [definitionId, filePath] of builtinFiles) {
153
- const meta = readNodeMeta(filePath);
154
- if (!meta) continue;
155
- out.push(`### ${definitionId}\n`);
156
- out.push(`- **displayName**: ${meta.displayName || definitionId}\n`);
157
- out.push(`- **description**: ${meta.description || ""}\n`);
158
- out.push(`- **input** (handle: input-0, input-1, …):\n`);
159
- if (meta.input.length) meta.input.forEach((s, i) => out.push(` - \`${s.name || "?"}\` (${s.type || "?"}) → input-${i}\n`));
160
- else out.push(" - 无\n");
161
- out.push(`- **output** (handle: output-0, output-1, …):\n`);
162
- if (meta.output.length) meta.output.forEach((s, i) => out.push(` - \`${s.name || "?"}\` (${s.type || "?"}) → output-${i}\n`));
163
- else out.push(" - 无\n");
164
- out.push("\n");
165
- }
166
- if (builtinFiles.size === 0) out.push("(无内置节点目录)\n");
167
-
168
- // 2. 当前流水线节点(来自 flow.yaml instances)
169
- out.push("## 2. 当前流水线节点元数据\n\n");
170
- const flowNodes = loadFlowYamlNodes(flowDir);
171
- for (const node of flowNodes) {
172
- const { id, label, definitionId, input: nodeInput, output: nodeOutput } = node;
173
- let input = nodeInput || [];
174
- let output = nodeOutput || [];
175
- if (input.length === 0 && output.length === 0) {
176
- const tryPaths = [
177
- path.join(flowDir, "nodes", `${definitionId}.md`),
178
- path.join(nodesDirNew, `${definitionId}.md`),
179
- path.join(nodesDirLegacy, `${definitionId}.md`),
180
- path.join(PACKAGE_BUILTIN_NODES_DIR, `${definitionId}.md`),
181
- ];
182
- let meta = null;
183
- for (const defPath of tryPaths) {
184
- meta = readNodeMeta(defPath);
185
- if (meta) break;
186
- }
187
- if (meta) {
188
- input = meta.input;
189
- output = meta.output;
190
- }
191
- }
192
- out.push(`### ${id}\n`);
193
- out.push(`- **label**: ${label || id}\n`);
194
- out.push(`- **definitionId**: ${definitionId || ""}\n`);
195
- out.push(`- **input** (handle):\n`);
196
- if (input.length) input.forEach((s, i) => out.push(` - \`${s.name || "?"}\` (${s.type || "?"}) → input-${i}\n`));
197
- else out.push(" - 无\n");
198
- out.push(`- **output** (handle):\n`);
199
- if (output.length) output.forEach((s, i) => out.push(` - \`${s.name || "?"}\` (${s.type || "?"}) → output-${i}\n`));
200
- else out.push(" - 无\n");
201
- out.push("\n");
202
- }
203
-
204
- const content = out.join("");
205
- const payload = {
206
- err_code: 0,
207
- message: { result: content },
208
- };
209
- console.log(JSON.stringify(payload));
210
- }
211
-
212
- main();
@@ -1,177 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * 根据 prompt 内容 + resolvedInputs 计算缓存键 MD5 与 cacheInputInfo,供前处理与 check-cache 共用。
4
- * 用法(模块):import { computeCacheMd5 } from "./compute-cache-md5.mjs";
5
- * 返回 { cacheMd5, cacheInputInfo, payload } 或 { ok: false, error }。payload 为计算 MD5 的完整字符串,供 pre/now 落盘排查。
6
- *
7
- * 当前 .cache.json 文件结构(写入路径:intermediate/<instanceId>/<instanceId>.cache.json):
8
- * - cacheMd5: string — 对 payload 的 MD5 十六进制串
9
- * - cacheInputInfo: string — JSON 字符串,解析后为 { instanceId, definitionId, inputPaths }
10
- * - inputPaths: Array<{ slot: string, execId: number, value: string }>,即 forCache 各 key 及参与 MD5 的值
11
- * - execId: number — 本轮写入时的 execId
12
- * - inputHandlerExecIds?: Record<前驱节点 id, number> — 各前驱在本轮计算时用的 execId(可选)
13
- * - payload?: string — 用于算 MD5 的完整字符串:prompt 全文 + "\\n" + forCache 规范化串(可选,便于排查)
14
- */
15
-
16
- import crypto from "crypto";
17
- import fs from "fs";
18
- import path from "path";
19
-
20
- import { getRunDir, PIPELINES_DIR } from "../lib/paths.mjs";
21
- import { getFlowDir } from "../lib/workspace.mjs";
22
-
23
- import { buildNodePrompt } from "./build-node-prompt.mjs";
24
- import { getResolvedValues } from "./get-resolved-values.mjs";
25
- import { loadAllExecIds, latestResultExecId, intermediatePromptBasename, intermediateCacheBasename } from "./get-exec-id.mjs";
26
- import { loadFlowDefinition, instanceEntryToSlots, parseInstanceSlots } from "./parse-flow.mjs";
27
-
28
- /** 从 intermediate/<instanceId>/<instanceId>.cache.json 读取 cacheMd5,无则返回 "" */
29
- function readUpstreamCacheMd5(intermediateDir, instanceId, execId) {
30
- const basename = intermediateCacheBasename(instanceId, execId);
31
- const p = path.join(intermediateDir, instanceId, basename);
32
- if (!fs.existsSync(p)) return "";
33
- try {
34
- const o = JSON.parse(fs.readFileSync(p, "utf-8"));
35
- return o.cacheMd5 != null ? String(o.cacheMd5) : "";
36
- } catch {
37
- return "";
38
- }
39
- }
40
-
41
- /**
42
- * 计算节点的 cacheMd5 与 cacheInputInfo。
43
- * payload 不含 execId,使相同输入得到稳定 cache 键,避免缓存持续失效。
44
- * 约定:payload = prompt 内容 + "\n" + 规范化的 forCache(仅 resolvedInputs、upstreamMd5)。
45
- * @returns {{ ok: true, cacheMd5: string, cacheInputInfo: string, payload: string }} 或 {{ ok: false, error: string }}
46
- */
47
- export function computeCacheMd5(workspaceRoot, flowName, uuid, instanceId, execId = 1) {
48
- const runDir = getRunDir(workspaceRoot, flowName, uuid);
49
- const intermediateDir = path.join(runDir, "intermediate");
50
- const build = buildNodePrompt(workspaceRoot, flowName, uuid, instanceId, execId);
51
- if (!build.ok) {
52
- return { ok: false, error: build.error || "buildNodePrompt failed" };
53
- }
54
- const promptBasename = intermediatePromptBasename(instanceId, execId);
55
- const promptPath = path.join(intermediateDir, instanceId, promptBasename);
56
- if (!fs.existsSync(promptPath)) {
57
- return { ok: false, error: "Prompt file not found" };
58
- }
59
- const promptContent = fs.readFileSync(promptPath, "utf-8");
60
-
61
- /** 所有写 .cache.json 的节点(含 control_if、control_anyOne、tool_load_key、tool_save_key、tool_get_env、普通节点)均由此处统一解析输入;
62
- * 输入来自 getResolvedValues → computeResolvedInputsForInstance,按 flow.edges 的 targetHandle 与 instance 的 input 槽位一一对应赋值,无按 definitionId 的特殊分支。
63
- * 只要 flow 中该节点的 edge 与 instance 定义正确,各 input 槽会正确赋值。 */
64
- const data = getResolvedValues(workspaceRoot, flowName, uuid, instanceId);
65
- if (!data.ok) {
66
- return { ok: false, error: data.error || "getResolvedValues failed" };
67
- }
68
- const resolvedInputs = data.resolvedInputs || {};
69
-
70
- /** 上游 edge 对应 node 的 cacheMd5 与各 input handler 的 execId:从 flow.json 取 predecessors,再读各前驱的 .cache.json */
71
- let upstreamMd5 = "";
72
- /** 各 input handler(前驱节点)在本次计算时使用的 execId,用于落盘与参与 cache 计算 */
73
- let inputHandlerExecIds = {};
74
- const flowJsonPath = path.join(intermediateDir, "flow.json");
75
- if (fs.existsSync(flowJsonPath)) {
76
- try {
77
- const flow = JSON.parse(fs.readFileSync(flowJsonPath, "utf-8"));
78
- if (flow.ok) {
79
- let preds = flow.predecessors && flow.predecessors[instanceId];
80
- if (!preds && Array.isArray(flow.edges)) {
81
- preds = [];
82
- const seen = new Set();
83
- for (const e of flow.edges) {
84
- if (e.target !== instanceId || !e.source) continue;
85
- if (!seen.has(e.source)) {
86
- seen.add(e.source);
87
- preds.push(e.source);
88
- }
89
- }
90
- }
91
- if (Array.isArray(preds) && preds.length > 0) {
92
- const order = flow.order || [];
93
- const execIds = loadAllExecIds(workspaceRoot, flowName, uuid, order);
94
- const sorted = [...new Set(preds)].sort();
95
- upstreamMd5 = sorted
96
- .map((predId) => {
97
- const predExecId = latestResultExecId(execIds[predId] ?? 1);
98
- inputHandlerExecIds[predId] = predExecId;
99
- return `${predId}:${readUpstreamCacheMd5(intermediateDir, predId, 1)}`;
100
- })
101
- .join("\n");
102
- }
103
- }
104
- } catch (_) {}
105
- }
106
-
107
- /** forCache 仅含 resolvedInputs 与 upstreamMd5,不包含 execId,保证 payload 稳定 */
108
- const forCache = {
109
- ...resolvedInputs,
110
- upstreamMd5,
111
- };
112
- const keys = Object.keys(forCache).sort();
113
- const parts = keys.map((k) => `${k}:${String(forCache[k] ?? "")}`);
114
- const canonicalInputString = parts.join("\n");
115
-
116
- const payload = promptContent + "\n" + canonicalInputString;
117
- const cacheMd5 = crypto.createHash("md5").update(payload, "utf8").digest("hex");
118
-
119
- let definitionId = "";
120
- let flow = null;
121
- if (fs.existsSync(flowJsonPath)) {
122
- try {
123
- flow = JSON.parse(fs.readFileSync(flowJsonPath, "utf-8"));
124
- if (flow.ok && flow.nodes) {
125
- const node = flow.nodes.find((n) => n.id === instanceId);
126
- definitionId = node?.definitionId ?? "";
127
- }
128
- } catch (_) {}
129
- }
130
-
131
- /** 每个 slot 对应的 input handler 的 execId(该槽由哪次执行的 handler 提供);非 handler 槽用 0,本节点 execId 槽用当前 execId */
132
- const slotToHandlerExecId = {};
133
- if (flow?.ok && flow.edges && (inputHandlerExecIds && Object.keys(inputHandlerExecIds).length > 0)) {
134
- const predsWithHandle = (flow.edges || [])
135
- .filter((e) => e.target === instanceId && e.source)
136
- .map((e) => ({ source: e.source, targetHandle: e.targetHandle || "input-0" }));
137
- let flowDir = getFlowDir(workspaceRoot, flowName) || path.join(path.resolve(workspaceRoot), PIPELINES_DIR, flowName);
138
- if (flow?.flowDir && typeof flow.flowDir === "string" && flow.flowDir.trim()) {
139
- flowDir = path.isAbsolute(flow.flowDir) ? flow.flowDir : path.join(workspaceRoot, flow.flowDir);
140
- }
141
- const flowData = loadFlowDefinition(flowDir);
142
- const instanceDir = path.join(flowDir, "instance");
143
- const getSlotsFor = (id) => {
144
- if (flowData?.instances?.[id]) return instanceEntryToSlots(flowData.instances[id]);
145
- try {
146
- return parseInstanceSlots(path.join(instanceDir, `${id}.md`));
147
- } catch {
148
- return { input: {}, output: {}, inputTypes: {}, outputTypes: {} };
149
- }
150
- };
151
- const inputSlotNames = Object.keys(getSlotsFor(instanceId).input || {});
152
- for (let i = 0; i < inputSlotNames.length; i++) {
153
- const slotName = inputSlotNames[i];
154
- const targetHandle = `input-${i}`;
155
- const pred = predsWithHandle.find((p) => p.targetHandle === targetHandle);
156
- if (pred && inputHandlerExecIds[pred.source] != null) {
157
- slotToHandlerExecId[slotName] = inputHandlerExecIds[pred.source];
158
- }
159
- }
160
- }
161
- const currentExecId = execId ?? 1;
162
- const inputPaths = keys.map((k) => ({
163
- slot: k,
164
- execId: slotToHandlerExecId[k] ?? (k === "execId" ? currentExecId : 0),
165
- value: forCache[k],
166
- }));
167
- const cacheInputInfo = JSON.stringify({ instanceId, definitionId, inputPaths });
168
-
169
- return {
170
- ok: true,
171
- cacheMd5,
172
- cacheInputInfo,
173
- payload,
174
- execId: execId ?? 1,
175
- inputHandlerExecIds,
176
- };
177
- }
@@ -1,71 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * 生成本次运行 uuid(未传时)并创建 run 目录,避免由 Agent 每次生成。
4
- * 用法:node ensure-run-dir.mjs <workspaceRoot> [uuid] <flowName>
5
- * uuid 须为 14 位数字(YYYYMMDDhhmmss),否则视为未传并自动生成。
6
- * flowName 必传;run 目录为 <workspaceRoot>/.workspace/agentflow/runBuild/<flowName>/<uuid>/,其下创建 intermediate/、output/。
7
- * 若传入合法 uuid:仅确保上述目录存在。
8
- * 输出(stdout JSON):{ "ok": true, "uuid": "..." }
9
- */
10
-
11
- import fs from "fs";
12
- import path from "path";
13
-
14
- import { getRunDir } from "../lib/paths.mjs";
15
-
16
- /** 合法 uuid:14 位数字 YYYYMMDDhhmmss,避免误传 flow name 等当作 uuid */
17
- function isValidUuid(value) {
18
- return typeof value === "string" && /^\d{14}$/.test(value);
19
- }
20
-
21
- function generateUuid() {
22
- const now = new Date();
23
- const y = now.getFullYear();
24
- const m = String(now.getMonth() + 1).padStart(2, "0");
25
- const d = String(now.getDate()).padStart(2, "0");
26
- const h = String(now.getHours()).padStart(2, "0");
27
- const min = String(now.getMinutes()).padStart(2, "0");
28
- const s = String(now.getSeconds()).padStart(2, "0");
29
- return `${y}${m}${d}${h}${min}${s}`;
30
- }
31
-
32
- function main() {
33
- const args = process.argv.slice(2);
34
- if (args.length < 1) {
35
- console.error(
36
- JSON.stringify({
37
- ok: false,
38
- error: "Usage: node ensure-run-dir.mjs <workspaceRoot> [uuid] <flowName>",
39
- }),
40
- );
41
- process.exit(1);
42
- }
43
-
44
- const workspaceRoot = path.resolve(args[0]);
45
- const uuid = isValidUuid(args[1]) ? args[1] : generateUuid();
46
- const flowName = typeof args[2] === "string" && args[2].trim() !== "" ? args[2].trim() : null;
47
- if (!flowName) {
48
- console.error(
49
- JSON.stringify({ ok: false, error: "Usage: node ensure-run-dir.mjs <workspaceRoot> [uuid] <flowName> (flowName required)" }),
50
- );
51
- process.exit(1);
52
- }
53
-
54
- const runDir = getRunDir(workspaceRoot, flowName, uuid);
55
- const intermediateDir = path.join(runDir, "intermediate");
56
- const outputDir = path.join(runDir, "output");
57
-
58
- try {
59
- fs.mkdirSync(intermediateDir, { recursive: true });
60
- fs.mkdirSync(outputDir, { recursive: true });
61
- } catch (e) {
62
- console.error(
63
- JSON.stringify({ ok: false, error: e.message || "Failed to create run dir" }),
64
- );
65
- process.exit(1);
66
- }
67
-
68
- console.log(JSON.stringify({ ok: true, uuid }));
69
- }
70
-
71
- main();
@@ -1,129 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * 清理 <workspaceRoot>/.workspace/agentflow/runBuild/ 下各 flowName 的 uuid 临时 run 目录。
4
- * 用法:
5
- * agentflow apply -ai gc <workspaceRoot> [--list] 仅列出 flowName/uuid 目录
6
- * agentflow apply -ai gc <workspaceRoot> --dry-run [--keep N] [--older-than N] 预览将删除的
7
- * agentflow apply -ai gc <workspaceRoot> --delete [--keep N] [--older-than N] 执行删除
8
- * 输出(stdout 一行 JSON):{ "err_code": 0|1, "message": { "result": "<文本>" } }
9
- */
10
-
11
- import fs from "fs";
12
- import path from "path";
13
-
14
- import { listAllRunDirs } from "../lib/workspace.mjs";
15
-
16
- const UUID_DIR_PATTERN = /^\d{14}$/;
17
-
18
- function parseArgs(args) {
19
- const workspaceRoot = args[0] ? path.resolve(args[0]) : "";
20
- const rest = args.slice(1);
21
- const opts = { list: false, delete: false, dryRun: false, keep: null, olderThan: null };
22
- for (let i = 0; i < rest.length; i++) {
23
- if (rest[i] === "--list") opts.list = true;
24
- else if (rest[i] === "--delete") opts.delete = true;
25
- else if (rest[i] === "--dry-run") opts.dryRun = true;
26
- else if (rest[i] === "--keep" && rest[i + 1] != null) {
27
- opts.keep = Math.max(0, parseInt(rest[++i], 10));
28
- if (Number.isNaN(opts.keep)) opts.keep = null;
29
- } else if (rest[i] === "--older-than" && rest[i + 1] != null) {
30
- opts.olderThan = Math.max(0, parseInt(rest[++i], 10));
31
- if (Number.isNaN(opts.olderThan)) opts.olderThan = null;
32
- }
33
- }
34
- return { workspaceRoot, opts };
35
- }
36
-
37
- /** 收集所有 run 目录(新 per-flow + legacy 两种位置),按 mtime 倒序 */
38
- function getAllRunDirs(workspaceRoot) {
39
- const dirs = [];
40
- for (const { flowName, uuid, runDir } of listAllRunDirs(workspaceRoot)) {
41
- if (!UUID_DIR_PATTERN.test(uuid)) continue;
42
- try {
43
- const stat = fs.statSync(runDir);
44
- dirs.push({ flowName, uuid, path: runDir, mtime: stat.mtimeMs });
45
- } catch (_) {}
46
- }
47
- dirs.sort((a, b) => b.mtime - a.mtime);
48
- return dirs;
49
- }
50
-
51
- function main() {
52
- const argv = process.argv.slice(2);
53
- if (argv.length < 1) {
54
- const payload = {
55
- err_code: 1,
56
- message: {
57
- result:
58
- "Usage: agentflow apply -ai gc <workspaceRoot> [--list] | ... --dry-run [--keep N] [--older-than N] | ... --delete [--keep N] [--older-than N]",
59
- },
60
- };
61
- console.log(JSON.stringify(payload));
62
- process.exit(1);
63
- }
64
-
65
- const { workspaceRoot, opts } = parseArgs(argv);
66
- const allDirs = getAllRunDirs(workspaceRoot);
67
-
68
- // 默认仅列出
69
- if (!opts.delete && !opts.dryRun) {
70
- opts.list = true;
71
- }
72
-
73
- if (opts.list) {
74
- const lines = [`共 ${allDirs.length} 个 run 目录:`, ""];
75
- for (const d of allDirs) {
76
- const mtime = new Date(d.mtime).toISOString();
77
- lines.push(` ${d.flowName}/${d.uuid} (mtime: ${mtime})`);
78
- }
79
- const payload = { err_code: 0, message: { result: lines.join("\n") } };
80
- console.log(JSON.stringify(payload));
81
- process.exit(0);
82
- }
83
-
84
- // 计算待删除列表
85
- let toDelete = [...allDirs];
86
- if (opts.keep != null && opts.keep > 0) {
87
- toDelete = allDirs.slice(opts.keep);
88
- }
89
- if (opts.olderThan != null && opts.olderThan > 0) {
90
- const cutoff = Date.now() - opts.olderThan * 24 * 60 * 60 * 1000;
91
- toDelete = toDelete.filter((d) => d.mtime < cutoff);
92
- }
93
-
94
- if (opts.dryRun) {
95
- const msg =
96
- toDelete.length === 0
97
- ? "无符合条件的目录需要删除。"
98
- : `以下 ${toDelete.length} 个目录将被删除(dry-run):\n${toDelete.map((d) => ` ${d.flowName}/${d.uuid}`).join("\n")}`;
99
- const payload = { err_code: 0, message: { result: msg } };
100
- console.log(JSON.stringify(payload));
101
- process.exit(0);
102
- }
103
-
104
- // --delete:实际删除
105
- const deleted = [];
106
- const failed = [];
107
- for (const d of toDelete) {
108
- try {
109
- fs.rmSync(d.path, { recursive: true });
110
- deleted.push(`${d.flowName}/${d.uuid}`);
111
- } catch (e) {
112
- failed.push({ label: `${d.flowName}/${d.uuid}`, error: e.message || String(e) });
113
- }
114
- }
115
-
116
- const parts = [`已删除 ${deleted.length} 个 run 目录:${deleted.join(", ") || "无"}`];
117
- if (failed.length > 0) {
118
- parts.push(`删除失败 ${failed.length} 个:`);
119
- failed.forEach((f) => parts.push(` ${f.label}: ${f.error}`));
120
- }
121
- const payload = {
122
- err_code: failed.length > 0 ? 1 : 0,
123
- message: { result: parts.join("\n") },
124
- };
125
- console.log(JSON.stringify(payload));
126
- process.exit(failed.length > 0 ? 1 : 0);
127
- }
128
-
129
- main();
@@ -1,59 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * apply -ai get-env:按 key 从用户私有 env、系统环境变量与 ~/.cursor/config.json 读取 value。
4
- * 优先级:先查当前 AGENTFLOW_USER_ID 的私有 env,再查 process.env[key],最后查 ~/.cursor/config.json(支持点号路径如 openai.apiKey)。
5
- *
6
- * 用法(apply 步骤,由 CLI 调用):
7
- * agentflow apply -ai get-env <workspaceRoot> <flowName> <uuid> <instanceId> <execId> <key>
8
- * 将 value 写入当前 run 的 output 并写 result。
9
- *
10
- * 用法(仅 key,兼容/测试):
11
- * agentflow apply -ai get-env <key>
12
- * 或 node get-env.mjs <key>
13
- * 仅向 stdout 输出 JSON,不写文件。
14
- */
15
-
16
- import fs from "fs";
17
- import path from "path";
18
-
19
- import { getRunDir } from "../lib/paths.mjs";
20
- import { resolveUserEnvValue } from "../lib/user-env.mjs";
21
- import { writeResult } from "./write-result.mjs";
22
- import { outputDirForNode, outputNodeBasename } from "./get-exec-id.mjs";
23
-
24
- function resolveValue(keyStr) {
25
- if (!keyStr) return "";
26
- return resolveUserEnvValue(keyStr, process.env.AGENTFLOW_USER_ID || "");
27
- }
28
-
29
- function main() {
30
- const argv = process.argv.slice(2);
31
- const keyStr = (argv[argv.length - 1] != null ? String(argv[argv.length - 1]).trim() : "") || "";
32
- const value = resolveValue(keyStr);
33
-
34
- if (argv.length >= 6) {
35
- const [workspaceRoot, flowName, uuid, instanceId, execIdStr] = argv;
36
- const execId = parseInt(String(execIdStr), 10) || 1;
37
- const runDir = getRunDir(workspaceRoot, flowName, uuid);
38
- const outputDir = path.join(runDir, outputDirForNode(instanceId));
39
- fs.mkdirSync(outputDir, { recursive: true });
40
- // 备份由 snapshotPriorRoundIfNeeded 在 pre-process 入口统一处理。
41
- const valueFile = path.join(outputDir, outputNodeBasename(instanceId, execId, "value"));
42
- const resultFile = path.join(outputDir, outputNodeBasename(instanceId, execId, "result"));
43
- fs.writeFileSync(valueFile, value, "utf-8");
44
- fs.writeFileSync(resultFile, value, "utf-8");
45
- writeResult(workspaceRoot, flowName, uuid, instanceId, {
46
- status: "success",
47
- message: "执行完成",
48
- }, { preserveBody: true, execId });
49
- } else {
50
- console.log(
51
- JSON.stringify({
52
- err_code: 0,
53
- message: { result: value, value },
54
- }),
55
- );
56
- }
57
- }
58
-
59
- main();