@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,271 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * 执行占位符替换,组装最终 prompt 并写入 intermediate 文件。
4
- * 用法:node build-node-prompt.mjs <workspaceRoot> <flowName> <uuid> <instanceId>
5
- * 输出(stdout JSON):{ "ok": true, "promptPath": ".workspace/agentflow/runBuild/<flowName>/<uuid>/intermediate/<instanceId>.prompt.md" }
6
- */
7
-
8
- import fs from "fs";
9
- import path from "path";
10
- import { fileURLToPath } from "url";
11
-
12
- import { getRunDir, PIPELINES_DIR } from "../lib/paths.mjs";
13
- import { getFlowDir } from "../lib/workspace.mjs";
14
- import { isMarketplaceDefinitionId, resolveMarketplaceNodePackage } from "../lib/marketplace.mjs";
15
- import { loadFlowDefinition } from "./parse-flow.mjs";
16
- import { getResolvedValues, getOutputPathForSlot } from "./get-resolved-values.mjs";
17
- import { loadExecId } from "./get-exec-id.mjs";
18
- import { intermediatePromptBasename, intermediateDirForNode } from "./get-exec-id.mjs";
19
- import { normalizeSkillsContext, normalizeWorkspaceContext, renderSkillsContextForPrompt } from "../lib/runtime-context.mjs";
20
-
21
- function shellQuote(s) {
22
- if (s == null) return "''";
23
- return "'" + String(s).replace(/'/g, "'\\''") + "'";
24
- }
25
-
26
- function resolvePlaceholder(k, resolvedInputs, resolvedOutputs, opts) {
27
- const { instanceId, currentExecId, runDir, extra = {} } = opts;
28
- if (Object.prototype.hasOwnProperty.call(extra, k)) return extra[k];
29
- const execId = currentExecId ?? 1;
30
- const toAbs = (rel) => (runDir && rel ? path.join(runDir, rel) : rel);
31
- if (k.startsWith("input.")) {
32
- const slot = k.slice(6);
33
- return resolvedInputs[slot] ?? resolvedInputs._ ?? "";
34
- }
35
- if (k.startsWith("output.")) {
36
- const slot = k.slice(7);
37
- const v = resolvedOutputs[slot] ?? resolvedOutputs._ ?? "";
38
- if (v) return v;
39
- if (instanceId && slot in resolvedOutputs) {
40
- return toAbs(getOutputPathForSlot(instanceId, execId, slot));
41
- }
42
- return "";
43
- }
44
- let v = resolvedInputs[k] ?? resolvedOutputs[k] ?? "";
45
- if (!v && !k.includes(".")) {
46
- v = resolvedInputs[k + ".md"] ?? resolvedOutputs[k + ".md"] ?? "";
47
- }
48
- if (!v && instanceId && (k in resolvedOutputs || (k + ".md") in resolvedOutputs)) {
49
- const slot = k in resolvedOutputs ? k : k + ".md";
50
- v = toAbs(getOutputPathForSlot(instanceId, execId, slot));
51
- }
52
- return v;
53
- }
54
-
55
- function resolvePlaceholdersInText(
56
- text,
57
- resolvedInputs,
58
- resolvedOutputs,
59
- opts = {},
60
- ) {
61
- if (!text || typeof text !== "string") return "";
62
- return text.replace(/\$\{([^}]+)\}/g, (_, key) => {
63
- return resolvePlaceholder(key.trim(), resolvedInputs, resolvedOutputs, opts);
64
- });
65
- }
66
-
67
- /**
68
- * 解析 script 字段中的 ${} 占位符,每个替换值用 shell 单引号包裹,避免路径含空格/特殊字符时被 shell 误拆分。
69
- */
70
- function resolveScriptCommand(
71
- text,
72
- resolvedInputs,
73
- resolvedOutputs,
74
- opts = {},
75
- ) {
76
- if (!text || typeof text !== "string") return "";
77
- return text.replace(/\$\{([^}]+)\}/g, (_, key) => {
78
- return shellQuote(resolvePlaceholder(key.trim(), resolvedInputs, resolvedOutputs, opts));
79
- });
80
- }
81
-
82
- function marketplaceRuntimeCommand(marketplaceNode, resolvedInputs, resolvedOutputs, opts) {
83
- if (!marketplaceNode || !marketplaceNode.runtime) return "";
84
- const runtime = marketplaceNode.runtime;
85
- const packageDir = marketplaceNode.packageDir;
86
- const entry = runtime.entry ? path.join(packageDir, String(runtime.entry)) : "";
87
- const language = String(runtime.language || "").trim();
88
- const runner =
89
- language === "python"
90
- ? "python3"
91
- : language === "bash"
92
- ? "bash"
93
- : language === "nodejs" || entry
94
- ? "node"
95
- : "";
96
- const args = Array.isArray(runtime.args) ? runtime.args.map((x) => String(x)).join(" ") : "";
97
- const command = runtime.command
98
- ? String(runtime.command)
99
- : entry
100
- ? [runner, "${entry}", args].filter(Boolean).join(" ")
101
- : "";
102
- if (!command) return "";
103
- return resolveScriptCommand(command, resolvedInputs, resolvedOutputs, {
104
- ...opts,
105
- extra: {
106
- ...(opts.extra || {}),
107
- entry,
108
- packageDir,
109
- workspaceRoot: opts.workspaceRoot || "",
110
- runDir: opts.runDir || "",
111
- },
112
- });
113
- }
114
-
115
- function normalizePromptImages(images) {
116
- if (!Array.isArray(images)) return [];
117
- return images
118
- .filter((item) => item && typeof item === "object")
119
- .map((item, index) => ({
120
- label: String(item.label || `image ${index + 1}`),
121
- name: String(item.name || `image-${index + 1}`),
122
- mimeType: String(item.mimeType || item.type || "image/png"),
123
- dataUrl: String(item.dataUrl || item.data || ""),
124
- }))
125
- .filter((item) => item.dataUrl.startsWith("data:image/"));
126
- }
127
-
128
- function renderImagesForPrompt(images) {
129
- const list = normalizePromptImages(images);
130
- if (list.length === 0) return "";
131
- const blocks = list.map((item) => [
132
- `### [${item.label}]`,
133
- `name: ${item.name}`,
134
- `mimeType: ${item.mimeType}`,
135
- `dataUrl: ${item.dataUrl}`,
136
- ].join("\n"));
137
- return `## 图片附件\n\n${blocks.join("\n\n")}`;
138
- }
139
-
140
- /**
141
- * 执行占位符替换,组装 prompt 并写入 intermediate 文件(文件名带 _execId)。
142
- * @param {number} [execId] - 本轮 execId,缺省则从 memory 读取
143
- * @returns {{ ok: boolean, promptPath?: string, nodeContext?: string, taskBody?: string, error?: string }}
144
- */
145
- export function buildNodePrompt(workspaceRoot, flowName, uuid, instanceId, execId, opts = {}) {
146
- const runDir = getRunDir(workspaceRoot, flowName, uuid);
147
- const flowJsonPath = path.join(runDir, "intermediate", "flow.json");
148
- let flowDir = getFlowDir(workspaceRoot, flowName) || path.join(workspaceRoot, PIPELINES_DIR, flowName);
149
- if (fs.existsSync(flowJsonPath)) {
150
- try {
151
- const flow = JSON.parse(fs.readFileSync(flowJsonPath, "utf-8"));
152
- if (flow?.flowDir && typeof flow.flowDir === "string" && flow.flowDir.trim()) {
153
- flowDir = path.isAbsolute(flow.flowDir) ? flow.flowDir : path.join(workspaceRoot, flow.flowDir);
154
- }
155
- } catch (_) {}
156
- }
157
- const nodeIntermediateDir = path.join(runDir, intermediateDirForNode(instanceId));
158
- const e = execId ?? loadExecId(workspaceRoot, flowName, uuid, instanceId);
159
- const promptBasename = intermediatePromptBasename(instanceId, e);
160
- const promptPath = path.join(nodeIntermediateDir, promptBasename);
161
-
162
- const data = getResolvedValues(workspaceRoot, flowName, uuid, instanceId);
163
- if (!data.ok) {
164
- return { ok: false, error: data.error || "get-resolved-values failed" };
165
- }
166
-
167
- const flowData = loadFlowDefinition(flowDir);
168
- const inst = flowData?.instances?.[instanceId];
169
- const marketplaceNode =
170
- inst?.definitionId && isMarketplaceDefinitionId(inst.definitionId)
171
- ? resolveMarketplaceNodePackage(workspaceRoot, flowDir, inst.definitionId, flowData)
172
- : null;
173
- const instanceBody =
174
- inst?.body != null
175
- ? String(inst.body || "").trim()
176
- : "";
177
- const instanceScript =
178
- inst?.script != null
179
- ? String(inst.script || "").trim()
180
- : "";
181
- const imagePrompt = renderImagesForPrompt(inst?.images);
182
-
183
- const { resolvedInputs = {}, resolvedOutputs = {}, systemPrompt = "" } = data;
184
- const workspaceContext = normalizeWorkspaceContext(opts.workspaceContext || resolvedInputs.workspaceContext, workspaceRoot, flowName, { flowDir });
185
- const skillsContext = normalizeSkillsContext(opts.skillsContext || resolvedInputs.skillsContext);
186
- if (workspaceContext?.workspaceRoot) {
187
- resolvedInputs.workspaceRoot = workspaceContext.workspaceRoot;
188
- resolvedInputs.cwd = workspaceContext.cwd || workspaceContext.workspaceRoot;
189
- resolvedInputs.pipelineWorkspace = workspaceContext.pipelineWorkspace || path.resolve(workspaceRoot);
190
- }
191
- const resolveOpts = {
192
- instanceId,
193
- currentExecId: e,
194
- runDir,
195
- workspaceRoot: workspaceContext?.workspaceRoot || workspaceRoot,
196
- extra: {
197
- workspaceRoot: workspaceContext?.workspaceRoot || path.resolve(workspaceRoot),
198
- cwd: workspaceContext?.cwd || workspaceContext?.workspaceRoot || path.resolve(workspaceRoot),
199
- pipelineWorkspace: workspaceContext?.pipelineWorkspace || path.resolve(workspaceRoot),
200
- flowDir: path.resolve(flowDir),
201
- runDir,
202
- },
203
- };
204
- const taskBody = resolvePlaceholdersInText(
205
- instanceBody,
206
- resolvedInputs,
207
- resolvedOutputs,
208
- resolveOpts,
209
- );
210
-
211
- const resolvedScript = instanceScript
212
- ? resolveScriptCommand(instanceScript, resolvedInputs, resolvedOutputs, resolveOpts)
213
- : marketplaceNode
214
- ? marketplaceRuntimeCommand(marketplaceNode, resolvedInputs, resolvedOutputs, resolveOpts)
215
- : "";
216
-
217
- const skillsPrompt = renderSkillsContextForPrompt(skillsContext);
218
- const contextBlocks = [systemPrompt || "(无)", skillsPrompt].filter((x) => x && String(x).trim());
219
-
220
- const content = `## 节点上下文
221
-
222
- ${contextBlocks.join("\n\n")}
223
-
224
- ## 执行任务
225
-
226
- ${[taskBody || "(无)", imagePrompt].filter((x) => x && String(x).trim()).join("\n\n")}
227
- `;
228
-
229
- try {
230
- fs.mkdirSync(nodeIntermediateDir, { recursive: true });
231
- // 备份由 snapshotPriorRoundIfNeeded 在 pre-process 入口统一处理;
232
- // 本函数可能在单轮内被多次调用(pre-process + computeCacheMd5),
233
- // 但 round 内内容相同,直接覆盖写即可,不会产生伪历史备份。
234
- fs.writeFileSync(promptPath, content, "utf-8");
235
- } catch (e) {
236
- return { ok: false, error: e.message || "Failed to write prompt file" };
237
- }
238
-
239
- const relativePath = path.relative(workspaceRoot, promptPath);
240
- return {
241
- ok: true,
242
- promptPath: relativePath.replace(/\\/g, "/"),
243
- nodeContext: contextBlocks.join("\n\n") || "",
244
- taskBody: taskBody || "",
245
- script: resolvedScript || "",
246
- workspaceContext,
247
- skillsContext,
248
- };
249
- }
250
-
251
- function main() {
252
- const args = process.argv.slice(2);
253
- if (args.length < 4) {
254
- console.error(
255
- JSON.stringify({
256
- ok: false,
257
- error: "Usage: node build-node-prompt.mjs <workspaceRoot> <flowName> <uuid> <instanceId>",
258
- }),
259
- );
260
- process.exit(1);
261
- }
262
-
263
- const [root, flowName, uuid, instanceId] = args;
264
- const workspaceRoot = path.resolve(root);
265
- const result = buildNodePrompt(workspaceRoot, flowName, uuid, instanceId, undefined);
266
- console.log(JSON.stringify(result));
267
- if (!result.ok) process.exit(1);
268
- }
269
-
270
- const isMain = process.argv[1] && path.resolve(fileURLToPath(import.meta.url)) === path.resolve(process.argv[1]);
271
- if (isMain) main();
@@ -1,191 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * get-ready-nodes 前执行:对 status 为 success 且存在 .cache.json 的节点重算 MD5,
4
- * 若与 .cache.json 中记录不一致则将该节点 result 改为 cache_not_met,使其重新进入待执行。
5
- * result/cache 使用固定路径(不含 _execId)。
6
- * 用法:node check-cache.mjs <workspaceRoot> <flowName> <uuid>
7
- * 输出(stdout JSON):{ "ok": true };失败时 { "ok": false, "error": "..." }
8
- */
9
-
10
- import fs from "fs";
11
- import path from "path";
12
-
13
- import { getRunDir } from "../lib/paths.mjs";
14
- import { computeCacheMd5 } from "./compute-cache-md5.mjs";
15
- import { writeResult } from "./write-result.mjs";
16
- import { intermediateResultBasename, intermediateCacheBasename, loadExecId } from "./get-exec-id.mjs";
17
- import { logToRunTag } from "./run-log.mjs";
18
- import { snapshotPriorRoundIfNeeded } from "./snapshot-prior-round.mjs";
19
-
20
- function parseResultStatus(filePath) {
21
- try {
22
- const raw = fs.readFileSync(filePath, "utf-8");
23
- const m = raw.match(/^\s*status:\s*["']?(\w+)["']?/m);
24
- return m ? m[1] : null;
25
- } catch {
26
- return null;
27
- }
28
- }
29
-
30
- const PRE_CACHE_JSON = ".pre.cache.json";
31
- const NOW_CACHE_JSON = ".now.cache.json";
32
-
33
- /** 从 intermediate/<instanceId>/<instanceId>.cache.json 读取 cacheMd5(固定路径) */
34
- function readCacheFromSidecar(intermediateDir, instanceId) {
35
- const basename = intermediateCacheBasename(instanceId, 1);
36
- const p = path.join(intermediateDir, instanceId, basename);
37
- if (!fs.existsSync(p)) return { cacheMd5: null };
38
- try {
39
- const raw = fs.readFileSync(p, "utf-8");
40
- const o = JSON.parse(raw);
41
- return { cacheMd5: o.cacheMd5 ?? null };
42
- } catch {
43
- return { cacheMd5: null };
44
- }
45
- }
46
-
47
- /** 将当时的 .cache.json 存为 .pre.cache.json,当前重算结果存为 .now.cache.json,便于排查 */
48
- function savePreAndNowCache(intermediateDir, instanceId, current) {
49
- const nodeDir = path.join(intermediateDir, instanceId);
50
- const prePath = path.join(nodeDir, intermediateCacheBasename(instanceId, 1));
51
- const preSavePath = path.join(nodeDir, `${instanceId}${PRE_CACHE_JSON}`);
52
- const nowSavePath = path.join(nodeDir, `${instanceId}${NOW_CACHE_JSON}`);
53
- if (fs.existsSync(prePath)) {
54
- try {
55
- fs.copyFileSync(prePath, preSavePath);
56
- } catch (_) {}
57
- }
58
- try {
59
- const nowObj = { cacheMd5: current.cacheMd5, cacheInputInfo: current.cacheInputInfo ?? "" };
60
- if (current.payload !== undefined) nowObj.payload = current.payload;
61
- if (current.execId !== undefined) nowObj.execId = current.execId;
62
- if (current.inputHandlerExecIds != null && Object.keys(current.inputHandlerExecIds).length > 0) {
63
- nowObj.inputHandlerExecIds = current.inputHandlerExecIds;
64
- }
65
- fs.writeFileSync(nowSavePath, JSON.stringify(nowObj, null, 2), "utf-8");
66
- } catch (_) {}
67
- }
68
-
69
- function main() {
70
- const args = process.argv.slice(2);
71
- if (args.length < 3) {
72
- console.error(
73
- JSON.stringify({
74
- ok: false,
75
- error: "Usage: node check-cache.mjs <workspaceRoot> <flowName> <uuid>",
76
- })
77
- );
78
- process.exit(1);
79
- }
80
-
81
- const [root, flowName, uuid] = args;
82
- const workspaceRoot = path.resolve(root);
83
- const runDir = getRunDir(workspaceRoot, flowName, uuid);
84
- const flowJsonPath = path.join(runDir, "intermediate", "flow.json");
85
-
86
- if (!fs.existsSync(flowJsonPath)) {
87
- console.log(JSON.stringify({ ok: true }));
88
- return;
89
- }
90
-
91
- let flow;
92
- try {
93
- flow = JSON.parse(fs.readFileSync(flowJsonPath, "utf-8"));
94
- } catch (e) {
95
- console.error(JSON.stringify({ ok: false, error: e.message }));
96
- process.exit(1);
97
- }
98
- if (!flow.ok || !Array.isArray(flow.order)) {
99
- console.log(JSON.stringify({ ok: true }));
100
- return;
101
- }
102
-
103
- const order = flow.order;
104
- const edges = flow.edges || [];
105
- const nodeDefinitions = flow.nodeDefinitions || {};
106
- const outputSlotTypes = flow.outputSlotTypes || {};
107
- const inputSlotTypes = flow.inputSlotTypes || {};
108
- const intermediateDir = path.join(runDir, "intermediate");
109
-
110
- /** 仅对 pendingInstances 及其向后边能到达的节点做缓存校验,与 get-ready-nodes 候选集一致 */
111
- const isNodeEdge = (e) => {
112
- const outTypes = outputSlotTypes[e.source];
113
- const inTypes = inputSlotTypes[e.target];
114
- if (!outTypes || !inTypes) return false;
115
- const outNames = Object.keys(outTypes);
116
- const inNames = Object.keys(inTypes);
117
- const oidx = parseInt(String(e.sourceHandle || "output-0").replace("output-", ""), 10) || 0;
118
- const iidx = parseInt(String(e.targetHandle || "input-0").replace("input-", ""), 10) || 0;
119
- const outType = outNames[oidx] != null ? outTypes[outNames[oidx]] : null;
120
- const inType = inNames[iidx] != null ? inTypes[inNames[iidx]] : null;
121
- return (outType === "节点" || outType === "node") && (inType === "节点" || inType === "node");
122
- };
123
- const successors = {};
124
- for (const e of edges) {
125
- if (!e.source || e.target == null) continue;
126
- if (!isNodeEdge(e)) continue;
127
- if (!successors[e.source]) successors[e.source] = [];
128
- successors[e.source].push(e.target);
129
- }
130
- const starts = order.filter((id) => nodeDefinitions[id] === "control_start");
131
- const pendingInstances = Array.isArray(flow.pendingInstances) ? flow.pendingInstances : starts;
132
- const candidateSet = new Set();
133
- const candQueue = [...pendingInstances];
134
- while (candQueue.length) {
135
- const id = candQueue.shift();
136
- if (candidateSet.has(id)) continue;
137
- candidateSet.add(id);
138
- for (const next of successors[id] || []) {
139
- if (!candidateSet.has(next)) candQueue.push(next);
140
- }
141
- }
142
-
143
- logToRunTag(workspaceRoot, flowName, uuid, "check-cache", {
144
- event: "start",
145
- candidateSet: [...candidateSet],
146
- });
147
-
148
- for (const instanceId of candidateSet) {
149
- const resultBasename = intermediateResultBasename(instanceId, 1);
150
- const resultPath = path.join(intermediateDir, instanceId, resultBasename);
151
- if (!fs.existsSync(resultPath)) continue;
152
-
153
- const status = parseResultStatus(resultPath);
154
- const { cacheMd5: storedMd5 } = readCacheFromSidecar(intermediateDir, instanceId);
155
- if (status !== "success" || !storedMd5) continue;
156
-
157
- const current = computeCacheMd5(workspaceRoot, flowName, uuid, instanceId, 1);
158
- if (!current.ok) continue;
159
-
160
- if (current.cacheMd5 !== storedMd5) {
161
- savePreAndNowCache(intermediateDir, instanceId, current);
162
- const reason = `storedMd5=${storedMd5} currentMd5=${current.cacheMd5}`;
163
- logToRunTag(workspaceRoot, flowName, uuid, "check-cache", {
164
- event: "cache_not_met",
165
- instanceId,
166
- storedMd5,
167
- currentMd5: current.cacheMd5,
168
- reason,
169
- });
170
- // 覆写 result.md 为 cache_not_met 之前,先把上一轮的真实结果(success/failed)snapshot 到 _<priorExecId>
171
- // 否则下一轮 pre-process 的 snapshot 备份下来的只是 cache_not_met 占位,UI 永远看不到实际执行产物。
172
- const priorExecId = loadExecId(workspaceRoot, flowName, uuid, instanceId);
173
- snapshotPriorRoundIfNeeded(runDir, instanceId, priorExecId);
174
- try {
175
- writeResult(workspaceRoot, flowName, uuid, instanceId, {
176
- status: "cache_not_met",
177
- message: "缓存失效(prompt/input 已变更)",
178
- cacheNotMetReason: reason,
179
- }, { preserveBody: true });
180
- } catch (err) {
181
- console.error(JSON.stringify({ ok: false, error: err.message }));
182
- process.exit(1);
183
- }
184
- }
185
- }
186
-
187
- logToRunTag(workspaceRoot, flowName, uuid, "check-cache", { event: "done" });
188
- console.log(JSON.stringify({ ok: true }));
189
- }
190
-
191
- main();