@fieldwangai/agentflow 0.1.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +201 -0
- package/README.zh-CN.md +201 -0
- package/agents/agentflow-node-executor-code.md +32 -0
- package/agents/agentflow-node-executor-planning.md +32 -0
- package/agents/agentflow-node-executor-requirement.md +32 -0
- package/agents/agentflow-node-executor-test.md +32 -0
- package/agents/agentflow-node-executor-ui.md +32 -0
- package/agents/agentflow-node-executor.md +32 -0
- package/agents/agents.json +8 -0
- package/agents/en/agentflow-node-executor.md +32 -0
- package/agents/zh/agentflow-node-executor.md +32 -0
- package/bin/agentflow.mjs +52 -0
- package/bin/ensure-workspace-reference.mjs +35 -0
- package/bin/lib/agent-runners.mjs +1199 -0
- package/bin/lib/agents-path.mjs +61 -0
- package/bin/lib/api-runner.mjs +361 -0
- package/bin/lib/apply.mjs +852 -0
- package/bin/lib/catalog-agents.mjs +300 -0
- package/bin/lib/catalog-flows.mjs +532 -0
- package/bin/lib/composer-agent.mjs +884 -0
- package/bin/lib/composer-flow-instances.mjs +68 -0
- package/bin/lib/composer-flow-skeleton.mjs +334 -0
- package/bin/lib/composer-flow-validate.mjs +47 -0
- package/bin/lib/composer-log.mjs +197 -0
- package/bin/lib/composer-model-router.mjs +160 -0
- package/bin/lib/composer-node-schema.mjs +299 -0
- package/bin/lib/composer-planner.mjs +749 -0
- package/bin/lib/composer-script-ops.mjs +233 -0
- package/bin/lib/composer-skill-router.mjs +384 -0
- package/bin/lib/flow-import.mjs +305 -0
- package/bin/lib/flow-normalize.mjs +71 -0
- package/bin/lib/flow-write.mjs +395 -0
- package/bin/lib/help.mjs +139 -0
- package/bin/lib/hub-login.mjs +54 -0
- package/bin/lib/hub-publish.mjs +159 -0
- package/bin/lib/hub-remote.mjs +189 -0
- package/bin/lib/hub.mjs +299 -0
- package/bin/lib/i18n.mjs +233 -0
- package/bin/lib/locales/en.json +344 -0
- package/bin/lib/locales/zh.json +344 -0
- package/bin/lib/log.mjs +37 -0
- package/bin/lib/main.mjs +611 -0
- package/bin/lib/model-config.mjs +118 -0
- package/bin/lib/model-lists.mjs +188 -0
- package/bin/lib/node-exec-context.mjs +336 -0
- package/bin/lib/node-execute.mjs +513 -0
- package/bin/lib/normalize-node-tool-command.mjs +97 -0
- package/bin/lib/paths.mjs +216 -0
- package/bin/lib/pipeline-scripts.mjs +41 -0
- package/bin/lib/recent-runs.mjs +173 -0
- package/bin/lib/run-apply-active-lock.mjs +82 -0
- package/bin/lib/run-events.mjs +85 -0
- package/bin/lib/run-node-statuses-from-disk.mjs +85 -0
- package/bin/lib/schedule-config.mjs +227 -0
- package/bin/lib/scheduler.mjs +312 -0
- package/bin/lib/table.mjs +4 -0
- package/bin/lib/terminal.mjs +42 -0
- package/bin/lib/ui-print.mjs +94 -0
- package/bin/lib/ui-server.mjs +2113 -0
- package/bin/lib/workspace-tree.mjs +266 -0
- package/bin/lib/workspace.mjs +180 -0
- package/bin/pipeline/build-node-prompt.mjs +179 -0
- package/bin/pipeline/check-cache.mjs +191 -0
- package/bin/pipeline/check-flow.mjs +543 -0
- package/bin/pipeline/collect-nodes.mjs +212 -0
- package/bin/pipeline/compute-cache-md5.mjs +177 -0
- package/bin/pipeline/ensure-run-dir.mjs +71 -0
- package/bin/pipeline/extract-thinking.mjs +308 -0
- package/bin/pipeline/gc.mjs +129 -0
- package/bin/pipeline/get-env.mjs +83 -0
- package/bin/pipeline/get-exec-id.mjs +145 -0
- package/bin/pipeline/get-ready-nodes.mjs +435 -0
- package/bin/pipeline/get-resolved-values.mjs +337 -0
- package/bin/pipeline/load-key.mjs +62 -0
- package/bin/pipeline/parse-bool.mjs +33 -0
- package/bin/pipeline/parse-flow.mjs +698 -0
- package/bin/pipeline/post-process-control-if.mjs +23 -0
- package/bin/pipeline/post-process-node.mjs +490 -0
- package/bin/pipeline/pre-process-node.mjs +449 -0
- package/bin/pipeline/resolve-inputs.mjs +201 -0
- package/bin/pipeline/run-log.mjs +34 -0
- package/bin/pipeline/run-tool-nodejs.mjs +160 -0
- package/bin/pipeline/save-key.mjs +93 -0
- package/bin/pipeline/snapshot-prior-round.mjs +70 -0
- package/bin/pipeline/validate-flow.mjs +825 -0
- package/bin/pipeline/validate-for-ui.mjs +226 -0
- package/bin/pipeline/validate-script-output.mjs +130 -0
- package/bin/pipeline/write-result.mjs +182 -0
- package/builtin/nodes/agent_subAgent.md +14 -0
- package/builtin/nodes/control_agent_toBool.md +20 -0
- package/builtin/nodes/control_anyOne.md +17 -0
- package/builtin/nodes/control_end.md +11 -0
- package/builtin/nodes/control_if.md +20 -0
- package/builtin/nodes/control_start.md +11 -0
- package/builtin/nodes/control_toBool.md +21 -0
- package/builtin/nodes/provide_file.md +11 -0
- package/builtin/nodes/provide_str.md +11 -0
- package/builtin/nodes/tool_get_env.md +14 -0
- package/builtin/nodes/tool_load_key.md +20 -0
- package/builtin/nodes/tool_nodejs.md +40 -0
- package/builtin/nodes/tool_print.md +14 -0
- package/builtin/nodes/tool_save_key.md +20 -0
- package/builtin/nodes/tool_user_ask.md +23 -0
- package/builtin/nodes/tool_user_check.md +22 -0
- package/builtin/pipelines/module-migrate/flow.yaml +819 -0
- package/builtin/pipelines/module-migrate/scripts/check_imports.mjs +700 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Makefile +362 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/.deps/Release/obj.target/node_modules/node-addon-api/node_addon_api_except.stamp.d +1 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/.deps/Release/obj.target/tree_sitter_kotlin_binding/bindings/node/binding.o.d +17 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/.deps/Release/obj.target/tree_sitter_kotlin_binding/src/parser.o.d +5 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/.deps/Release/obj.target/tree_sitter_kotlin_binding/src/scanner.o.d +8 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/.deps/Release/tree_sitter_kotlin_binding.node.d +1 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/obj.target/node_modules/node-addon-api/node_addon_api_except.stamp +0 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/obj.target/tree_sitter_kotlin_binding/bindings/node/binding.o +0 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/obj.target/tree_sitter_kotlin_binding/src/parser.o +0 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/obj.target/tree_sitter_kotlin_binding/src/scanner.o +0 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/Release/tree_sitter_kotlin_binding.node +0 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/binding.Makefile +6 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/gyp-mac-tool +768 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/node_modules/node-addon-api/node_addon_api.Makefile +6 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/node_modules/node-addon-api/node_addon_api.target.mk +122 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/node_modules/node-addon-api/node_addon_api_except.target.mk +126 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/node_modules/node-addon-api/node_addon_api_maybe.target.mk +122 -0
- package/builtin/pipelines/module-migrate/scripts/node_modules/tree-sitter-kotlin/build/tree_sitter_kotlin_binding.target.mk +203 -0
- package/builtin/pipelines/new/flow.yaml +545 -0
- package/builtin/pipelines/new/scripts/check-flow.mjs +9 -0
- package/builtin/pipelines/new/scripts/collect-nodes.mjs +211 -0
- package/builtin/pipelines/scripts/adjust-node-positions.mjs +113 -0
- package/builtin/web-ui/dist/agentflow-icon.svg +23 -0
- package/builtin/web-ui/dist/assets/index-CZkUPcXE.css +1 -0
- package/builtin/web-ui/dist/assets/index-DkkhNESc.js +190 -0
- package/builtin/web-ui/dist/index.html +24 -0
- package/package.json +67 -0
- package/reference/flow-control-capabilities.md +274 -0
- package/reference/flow-layout.md +84 -0
- package/reference/flow-prompt-handler-check.md +12 -0
- package/reference/flow-result-semantics.md +14 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* 运行 tool_nodejs 脚本并将结果写入流水线。
|
|
4
|
+
*
|
|
5
|
+
* 成败判定(Unix 哲学):
|
|
6
|
+
* - 进程 exit code 0 → success,非 0 → failed
|
|
7
|
+
* - 若 stdout 为合法 JSON 且含 err_code,err_code 优先(向后兼容旧脚本)
|
|
8
|
+
*
|
|
9
|
+
* stdout 内容 → result 槽位:
|
|
10
|
+
* - JSON 模式:提取 message.result
|
|
11
|
+
* - 纯文本模式:整段 stdout 作为 result
|
|
12
|
+
*
|
|
13
|
+
* 用法:node run-tool-nodejs.mjs <workspaceRoot> <flowName> <uuid> <instanceId> [execId] -- <scriptCmd> [args...]
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { spawnSync } from "child_process";
|
|
17
|
+
import fs from "fs";
|
|
18
|
+
import path from "path";
|
|
19
|
+
import { fileURLToPath } from "url";
|
|
20
|
+
|
|
21
|
+
import { getRunDir } from "../lib/paths.mjs";
|
|
22
|
+
import { validateAndParse } from "./validate-script-output.mjs";
|
|
23
|
+
import { writeResult } from "./write-result.mjs";
|
|
24
|
+
import { loadExecId, outputNodeBasename, outputDirForNode } from "./get-exec-id.mjs";
|
|
25
|
+
import { nodeToolCommandToArgv } from "../lib/normalize-node-tool-command.mjs";
|
|
26
|
+
import { buildPipelineScriptPathHint } from "../lib/flow-normalize.mjs";
|
|
27
|
+
|
|
28
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
29
|
+
const MAX_RETRIES = 3;
|
|
30
|
+
const RETRY_DELAY_MS = 1000;
|
|
31
|
+
|
|
32
|
+
function runOnce(workspaceRoot, flowName, uuid, instanceId, execId, scriptArgs) {
|
|
33
|
+
const runDir = getRunDir(workspaceRoot, flowName, uuid);
|
|
34
|
+
const outputDir = path.join(runDir, outputDirForNode(instanceId));
|
|
35
|
+
|
|
36
|
+
const rawLine = scriptArgs.join(" ");
|
|
37
|
+
const { argv, commandLine: normalizedCmd } = nodeToolCommandToArgv(rawLine);
|
|
38
|
+
const child =
|
|
39
|
+
/^node\s/i.test(String(normalizedCmd).trim()) && argv.length >= 1
|
|
40
|
+
? spawnSync(process.execPath, argv, {
|
|
41
|
+
cwd: workspaceRoot,
|
|
42
|
+
shell: false,
|
|
43
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
44
|
+
})
|
|
45
|
+
: spawnSync(normalizedCmd, [], {
|
|
46
|
+
cwd: workspaceRoot,
|
|
47
|
+
shell: true,
|
|
48
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const stdout = child.stdout?.toString("utf-8") ?? "";
|
|
52
|
+
const stderr = child.stderr?.toString("utf-8") ?? "";
|
|
53
|
+
const exitCode = child.status ?? 1;
|
|
54
|
+
|
|
55
|
+
if (child.signal) {
|
|
56
|
+
persistStderr(outputDir, instanceId, execId, stderr);
|
|
57
|
+
return { success: false, fatal: true, detail: `Script killed: ${child.signal}` };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const { ok, errors, payload } = validateAndParse(stdout);
|
|
61
|
+
|
|
62
|
+
if (!ok) {
|
|
63
|
+
const baseDetail = exitCode !== 0
|
|
64
|
+
? `脚本退出码 ${exitCode}` + (stderr.trim() ? `:${stderr.trim().slice(0, 200)}` : "")
|
|
65
|
+
: (errors.length ? errors.join("; ") : "脚本无输出");
|
|
66
|
+
const detail = baseDetail + buildPipelineScriptPathHint(stderr);
|
|
67
|
+
writeResult(workspaceRoot, flowName, uuid, instanceId, {
|
|
68
|
+
status: "failed",
|
|
69
|
+
message: detail,
|
|
70
|
+
}, { preserveBody: true, execId });
|
|
71
|
+
persistStderr(outputDir, instanceId, execId, stderr);
|
|
72
|
+
return { success: false, fatal: false, detail };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const isSynthetic = Boolean(payload._synthetic);
|
|
76
|
+
const success = isSynthetic ? exitCode === 0 : (payload.err_code === 0);
|
|
77
|
+
const message = payload.message;
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
81
|
+
// 备份由 snapshotPriorRoundIfNeeded 在 pre-process 入口统一处理。
|
|
82
|
+
for (const slot of Object.keys(message)) {
|
|
83
|
+
if (slot === "_synthetic") continue;
|
|
84
|
+
const content = message[slot];
|
|
85
|
+
if (content == null) continue;
|
|
86
|
+
fs.writeFileSync(
|
|
87
|
+
path.join(outputDir, outputNodeBasename(instanceId, execId, slot)),
|
|
88
|
+
String(content),
|
|
89
|
+
"utf-8",
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
writeResult(workspaceRoot, flowName, uuid, instanceId, {
|
|
93
|
+
status: success ? "success" : "failed",
|
|
94
|
+
message: success ? "执行完成" : "执行未通过",
|
|
95
|
+
}, { preserveBody: true, execId });
|
|
96
|
+
} catch (e) {
|
|
97
|
+
writeResult(workspaceRoot, flowName, uuid, instanceId, {
|
|
98
|
+
status: "failed",
|
|
99
|
+
message: e.message || "写入 output/result 异常",
|
|
100
|
+
}, { preserveBody: true, execId });
|
|
101
|
+
persistStderr(outputDir, instanceId, execId, stderr);
|
|
102
|
+
return { success: false, fatal: false, detail: e.message };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
persistStderr(outputDir, instanceId, execId, stderr);
|
|
106
|
+
return { success, fatal: false, detail: success ? "" : `脚本退出码 ${exitCode}` };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function persistStderr(outputDir, instanceId, execId, stderr) {
|
|
110
|
+
if (!stderr) return;
|
|
111
|
+
try {
|
|
112
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
113
|
+
fs.writeFileSync(
|
|
114
|
+
path.join(outputDir, outputNodeBasename(instanceId, execId, "stderr")),
|
|
115
|
+
stderr,
|
|
116
|
+
"utf-8",
|
|
117
|
+
);
|
|
118
|
+
} catch (_) {}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function main() {
|
|
122
|
+
const args = process.argv.slice(2);
|
|
123
|
+
const sep = args.indexOf("--");
|
|
124
|
+
if (sep < 0 || args.length < sep + 2) {
|
|
125
|
+
console.error(
|
|
126
|
+
"Usage: node run-tool-nodejs.mjs <workspaceRoot> <flowName> <uuid> <instanceId> [execId] -- <scriptCmd> [args...]",
|
|
127
|
+
);
|
|
128
|
+
process.exit(2);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const workspaceRoot = path.resolve(args[0]);
|
|
132
|
+
const flowName = args[1];
|
|
133
|
+
const uuid = args[2];
|
|
134
|
+
const instanceId = args[3];
|
|
135
|
+
const execId =
|
|
136
|
+
sep >= 5 && args[4] !== "--"
|
|
137
|
+
? (parseInt(String(args[4]), 10) || 1)
|
|
138
|
+
: loadExecId(workspaceRoot, flowName, uuid, instanceId);
|
|
139
|
+
const scriptArgs = args.slice(sep + 1);
|
|
140
|
+
|
|
141
|
+
for (let attempt = 1; attempt <= MAX_RETRIES + 1; attempt++) {
|
|
142
|
+
const result = runOnce(workspaceRoot, flowName, uuid, instanceId, execId, scriptArgs);
|
|
143
|
+
if (result.success) {
|
|
144
|
+
process.exit(0);
|
|
145
|
+
}
|
|
146
|
+
if (result.fatal) {
|
|
147
|
+
console.error(result.detail);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
if (attempt <= MAX_RETRIES) {
|
|
151
|
+
console.error(`[tool_nodejs 自愈] ${instanceId} 第 ${attempt}/${MAX_RETRIES} 次重试:${result.detail?.slice(0, 200) || "unknown"}`);
|
|
152
|
+
spawnSync("sleep", [String(RETRY_DELAY_MS / 1000)], { stdio: "ignore" });
|
|
153
|
+
} else {
|
|
154
|
+
console.error(result.detail);
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
main();
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* tool_save_key 执行脚本:按 key 将 value 写入 run 目录下 memory 存储,stdout 输出 tool_nodejs 约定 JSON。
|
|
4
|
+
* 存储路径与格式由本脚本内部实现,节点不感知。key/value 由命令行参数传入,不读 flow。
|
|
5
|
+
* value 若为 run 目录内相对路径则读取文件内容后写入。
|
|
6
|
+
* 用法:node save-key.mjs <workspaceRoot> <flowName> <uuid> <key> [value]
|
|
7
|
+
* 输出(stdout 一行 JSON):成功时 result 为写入的 value;err_code 0=成功 1=失败,无 next。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import fs from "fs";
|
|
11
|
+
import path from "path";
|
|
12
|
+
|
|
13
|
+
import { getRunDir } from "../lib/paths.mjs";
|
|
14
|
+
|
|
15
|
+
const MEMORY_FILENAME = "memory.md";
|
|
16
|
+
|
|
17
|
+
function parseMemory(content) {
|
|
18
|
+
const map = new Map();
|
|
19
|
+
for (const line of (content || "").split(/\r?\n/)) {
|
|
20
|
+
const idx = line.indexOf(": ");
|
|
21
|
+
if (idx <= 0) continue;
|
|
22
|
+
const k = line.slice(0, idx).trim();
|
|
23
|
+
const v = line.slice(idx + 2).trim();
|
|
24
|
+
if (k) map.set(k, v);
|
|
25
|
+
}
|
|
26
|
+
return map;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function serializeMemory(map) {
|
|
30
|
+
return (
|
|
31
|
+
Array.from(map.entries())
|
|
32
|
+
.map(([k, v]) => `${k}: ${String(v).replace(/\r?\n/g, " ")}`)
|
|
33
|
+
.join("\n") + (map.size ? "\n" : "")
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function main() {
|
|
38
|
+
const [root, flowName, uuid, keyArg, valueArg] = process.argv.slice(2);
|
|
39
|
+
if (!root || !flowName || !uuid) {
|
|
40
|
+
console.log(
|
|
41
|
+
JSON.stringify({
|
|
42
|
+
err_code: 1,
|
|
43
|
+
message: { result: "" },
|
|
44
|
+
}),
|
|
45
|
+
);
|
|
46
|
+
process.exit(0);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const key = keyArg != null ? String(keyArg).trim() : "";
|
|
50
|
+
let value = valueArg != null ? String(valueArg).trim() : "";
|
|
51
|
+
|
|
52
|
+
const workspaceRoot = path.resolve(root);
|
|
53
|
+
const runDir = getRunDir(workspaceRoot, flowName, uuid);
|
|
54
|
+
const memoryPath = path.join(runDir, MEMORY_FILENAME);
|
|
55
|
+
|
|
56
|
+
if (!key) {
|
|
57
|
+
console.log(
|
|
58
|
+
JSON.stringify({
|
|
59
|
+
err_code: 0,
|
|
60
|
+
message: { result: "" },
|
|
61
|
+
}),
|
|
62
|
+
);
|
|
63
|
+
process.exit(0);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (
|
|
67
|
+
value &&
|
|
68
|
+
(value.includes("/") || value.startsWith("output") || value.startsWith("intermediate"))
|
|
69
|
+
) {
|
|
70
|
+
const valuePath = path.join(runDir, value);
|
|
71
|
+
if (fs.existsSync(valuePath)) {
|
|
72
|
+
try {
|
|
73
|
+
value = fs.readFileSync(valuePath, "utf-8").trim();
|
|
74
|
+
} catch (_) {}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const existing = fs.existsSync(memoryPath)
|
|
79
|
+
? parseMemory(fs.readFileSync(memoryPath, "utf-8"))
|
|
80
|
+
: new Map();
|
|
81
|
+
existing.set(key, value);
|
|
82
|
+
fs.mkdirSync(path.dirname(memoryPath), { recursive: true });
|
|
83
|
+
fs.writeFileSync(memoryPath, serializeMemory(existing), "utf-8");
|
|
84
|
+
|
|
85
|
+
console.log(
|
|
86
|
+
JSON.stringify({
|
|
87
|
+
err_code: 0,
|
|
88
|
+
message: { result: value },
|
|
89
|
+
}),
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
main();
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* 节点每轮执行进入 pre-process 时,统一把「上一轮遗留的当前文件」快照到
|
|
4
|
+
* <name>_<priorExecId>.<ext>
|
|
5
|
+
* 形式。这是唯一的备份入口,替代分散在 write-result / build-node-prompt /
|
|
6
|
+
* pre-process 内多种辅助 prompt / get-env / run-tool-nodejs 中的 rename 调用。
|
|
7
|
+
*
|
|
8
|
+
* 不变量:
|
|
9
|
+
* 1. 文件后缀 `_K` 严格对应「第 K 轮结束时的文件内容」,无错位。
|
|
10
|
+
* 2. unsuffixed(无 `_N` 后缀)= 当前最新版本,永远存在;snapshot 用 copy
|
|
11
|
+
* 生成 `_K` 历史快照,不破坏 unsuffixed。下游运行时只 resolve unsuffixed,
|
|
12
|
+
* 不应也不需要 fallback 到 `_K`。
|
|
13
|
+
* 3. 幂等:目标 `_K.ext` 已存在或源文件已经是 `_K.ext` 形态,直接跳过。
|
|
14
|
+
*
|
|
15
|
+
* 调用方:bin/pipeline/pre-process-node.mjs 在计算 execId 后、任何 write 前。
|
|
16
|
+
*/
|
|
17
|
+
import fs from "fs";
|
|
18
|
+
import path from "path";
|
|
19
|
+
|
|
20
|
+
import { intermediateDirForNode, outputDirForNode } from "./get-exec-id.mjs";
|
|
21
|
+
|
|
22
|
+
const BACKUP_SUFFIX_RE = /_\d+$/;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {string} runDir - .workspace/agentflow/runBuild/<flowName>/<uuid>
|
|
26
|
+
* @param {string} instanceId
|
|
27
|
+
* @param {number} priorExecId - memory 里记录的「上一轮已完成 execId」,首轮传 0/undefined 直接返回
|
|
28
|
+
*/
|
|
29
|
+
export function snapshotPriorRoundIfNeeded(runDir, instanceId, priorExecId) {
|
|
30
|
+
const prior = Number(priorExecId);
|
|
31
|
+
if (!Number.isFinite(prior) || prior < 1) return;
|
|
32
|
+
const suffix = `_${prior}`;
|
|
33
|
+
|
|
34
|
+
const interDir = path.join(runDir, intermediateDirForNode(instanceId));
|
|
35
|
+
snapshotDir(interDir, (f) => f.startsWith(instanceId + "."), suffix);
|
|
36
|
+
|
|
37
|
+
const outDir = path.join(runDir, outputDirForNode(instanceId));
|
|
38
|
+
snapshotDir(outDir, (f) => f.startsWith(`node_${instanceId}_`), suffix);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @param {string} dir
|
|
43
|
+
* @param {(filename: string) => boolean} predicate
|
|
44
|
+
* @param {string} suffix - `_<N>`
|
|
45
|
+
*/
|
|
46
|
+
function snapshotDir(dir, predicate, suffix) {
|
|
47
|
+
if (!fs.existsSync(dir)) return;
|
|
48
|
+
let files;
|
|
49
|
+
try {
|
|
50
|
+
files = fs.readdirSync(dir);
|
|
51
|
+
} catch {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
for (const f of files) {
|
|
55
|
+
if (!predicate(f)) continue;
|
|
56
|
+
const ext = path.extname(f);
|
|
57
|
+
const base = path.basename(f, ext);
|
|
58
|
+
// 已是备份文件(以 _\d+ 结尾),跳过
|
|
59
|
+
if (BACKUP_SUFFIX_RE.test(base)) continue;
|
|
60
|
+
const to = base + suffix + ext;
|
|
61
|
+
if (to === f) continue;
|
|
62
|
+
const toPath = path.join(dir, to);
|
|
63
|
+
if (fs.existsSync(toPath)) continue; // 幂等
|
|
64
|
+
try {
|
|
65
|
+
fs.copyFileSync(path.join(dir, f), toPath);
|
|
66
|
+
} catch {
|
|
67
|
+
// 并发或权限错误:放弃该文件,其它继续。unsuffixed 保留不动。
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|