taskmeld 0.1.1
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 +18 -0
- package/README.md +172 -0
- package/README.zh-CN.md +172 -0
- package/dist/src/app/app-context-env.js +51 -0
- package/dist/src/app/create-app-context.js +127 -0
- package/dist/src/app/data-dir.js +29 -0
- package/dist/src/app/pipeline-config.js +105 -0
- package/dist/src/app/pipeline-plugin-config.js +2 -0
- package/dist/src/app/pipeline-registry.js +502 -0
- package/dist/src/app/pipeline-runtime.js +202 -0
- package/dist/src/app/runtime-store.js +151 -0
- package/dist/src/app/user-config.js +37 -0
- package/dist/src/artifacts/artifact-cleanup.js +192 -0
- package/dist/src/artifacts/artifact-index.js +262 -0
- package/dist/src/artifacts/artifact-rebuilder.js +120 -0
- package/dist/src/artifacts/storage-service.js +371 -0
- package/dist/src/cli/bootstrap.js +226 -0
- package/dist/src/cli/commands/agent.js +126 -0
- package/dist/src/cli/commands/artifact.js +175 -0
- package/dist/src/cli/commands/init.js +150 -0
- package/dist/src/cli/commands/pipeline/errors.js +37 -0
- package/dist/src/cli/commands/pipeline/result.js +179 -0
- package/dist/src/cli/commands/pipeline/selector.js +51 -0
- package/dist/src/cli/commands/pipeline/types.js +2 -0
- package/dist/src/cli/commands/pipeline/watch.js +67 -0
- package/dist/src/cli/commands/pipeline.js +339 -0
- package/dist/src/cli/commands/scheduler.js +81 -0
- package/dist/src/cli/commands/server.js +70 -0
- package/dist/src/cli/commands/system.js +21 -0
- package/dist/src/cli/errors.js +71 -0
- package/dist/src/cli/help.js +184 -0
- package/dist/src/cli/index.js +65 -0
- package/dist/src/cli/output.js +19 -0
- package/dist/src/cli/renderers/engine/json.js +67 -0
- package/dist/src/cli/renderers/engine/markdown.js +95 -0
- package/dist/src/cli/renderers/engine/types.js +2 -0
- package/dist/src/cli/renderers/engine/utils.js +32 -0
- package/dist/src/cli/renderers/index.js +27 -0
- package/dist/src/cli/renderers/specs/agent.js +78 -0
- package/dist/src/cli/renderers/specs/artifact.js +32 -0
- package/dist/src/cli/renderers/specs/index.js +36 -0
- package/dist/src/cli/renderers/specs/init.js +25 -0
- package/dist/src/cli/renderers/specs/pipeline.js +561 -0
- package/dist/src/cli/renderers/specs/scheduler.js +46 -0
- package/dist/src/cli/renderers/specs/server.js +38 -0
- package/dist/src/cli/renderers/specs/system.js +36 -0
- package/dist/src/cli/router.js +199 -0
- package/dist/src/cli/server-runtime-client.js +780 -0
- package/dist/src/cli/types.js +2 -0
- package/dist/src/gateway/frame-sanitizer.js +78 -0
- package/dist/src/gateway/gateway-client.js +462 -0
- package/dist/src/gateway/index.js +18 -0
- package/dist/src/gateway/types.js +2 -0
- package/dist/src/index.js +123 -0
- package/dist/src/logs/run-log-reader.js +141 -0
- package/dist/src/logs/run-log-service.js +42 -0
- package/dist/src/logs/run-log-types.js +2 -0
- package/dist/src/pipeline/agent-activity.js +191 -0
- package/dist/src/pipeline/artifact-storage.js +208 -0
- package/dist/src/pipeline/diagnostics/dependency-diagnostic.js +105 -0
- package/dist/src/pipeline/diagnostics/index.js +6 -0
- package/dist/src/pipeline/dispatch/pipeline-inbound-queue.js +215 -0
- package/dist/src/pipeline/dispatch/pipeline-link-dispatcher.js +66 -0
- package/dist/src/pipeline/dispatch/pipeline-link-store.js +94 -0
- package/dist/src/pipeline/dispatch/pipeline-queue-drainer.js +71 -0
- package/dist/src/pipeline/execution/dependency-check.js +52 -0
- package/dist/src/pipeline/execution/execution-result.js +2 -0
- package/dist/src/pipeline/execution/group-item-executor.js +128 -0
- package/dist/src/pipeline/execution/index.js +5 -0
- package/dist/src/pipeline/execution/node-item-executor.js +58 -0
- package/dist/src/pipeline/execution/node-runner.js +159 -0
- package/dist/src/pipeline/execution/readiness-state.js +10 -0
- package/dist/src/pipeline/execution/reject-handler.js +94 -0
- package/dist/src/pipeline/execution/rejected-artifact-archiver.js +45 -0
- package/dist/src/pipeline/execution/route-item-manager.js +253 -0
- package/dist/src/pipeline/execution/run-abort-controller.js +66 -0
- package/dist/src/pipeline/execution/run-state-helpers.js +257 -0
- package/dist/src/pipeline/execution/service.js +165 -0
- package/dist/src/pipeline/execution/session-registry.js +96 -0
- package/dist/src/pipeline/execution/structured-node-runner.js +411 -0
- package/dist/src/pipeline/execution-status.js +96 -0
- package/dist/src/pipeline/execution-timeout.js +21 -0
- package/dist/src/pipeline/identity/index.js +32 -0
- package/dist/src/pipeline/identity/types.js +2 -0
- package/dist/src/pipeline/item-batch-controller.js +227 -0
- package/dist/src/pipeline/output/pipeline-output-resolver.js +91 -0
- package/dist/src/pipeline/output/pipeline-output-store.js +60 -0
- package/dist/src/pipeline/runtime-model.js +173 -0
- package/dist/src/pipeline/scheduler/dependency-state.js +144 -0
- package/dist/src/pipeline/scheduler-service.js +314 -0
- package/dist/src/pipeline/state/group-item-state.js +50 -0
- package/dist/src/pipeline/state/group-run-state.js +41 -0
- package/dist/src/pipeline/state/index.js +20 -0
- package/dist/src/pipeline/state/node-item-state.js +67 -0
- package/dist/src/pipeline/state/node-run-state.js +51 -0
- package/dist/src/pipeline/state/types.js +2 -0
- package/dist/src/pipeline/state-machine.js +101 -0
- package/dist/src/pipeline/structured-output/contract.js +133 -0
- package/dist/src/pipeline/structured-output/index.js +22 -0
- package/dist/src/pipeline/structured-output/parser.js +214 -0
- package/dist/src/pipeline/structured-output/prompt.js +290 -0
- package/dist/src/pipeline/structured-output/waiter.js +139 -0
- package/dist/src/pipeline/template.js +135 -0
- package/dist/src/pipeline/timeline-log-store.js +57 -0
- package/dist/src/pipeline/tool-activity.js +94 -0
- package/dist/src/pipeline/types/pipeline-link.js +7 -0
- package/dist/src/pipeline/types/pipeline-output.js +11 -0
- package/dist/src/pipeline/types/workflow.js +2 -0
- package/dist/src/pipeline/workflow/branch-rules.js +74 -0
- package/dist/src/pipeline/workflow/defaults.js +48 -0
- package/dist/src/pipeline/workflow/io.js +89 -0
- package/dist/src/pipeline/workflow/normalize.js +347 -0
- package/dist/src/pipeline/workflow/routes.js +16 -0
- package/dist/src/pipeline/workflow/template-mapper.js +113 -0
- package/dist/src/pipeline/workflow/validate.js +312 -0
- package/dist/src/pipeline/workflow-graph.js +165 -0
- package/dist/src/server/api-handler.js +163 -0
- package/dist/src/server/http-utils.js +34 -0
- package/dist/src/server/middleware.js +61 -0
- package/dist/src/server/router.js +105 -0
- package/dist/src/server/routes/agents.js +189 -0
- package/dist/src/server/routes/artifacts.js +163 -0
- package/dist/src/server/routes/gateway.js +18 -0
- package/dist/src/server/routes/health.js +16 -0
- package/dist/src/server/routes/logs.js +73 -0
- package/dist/src/server/routes/pipeline-batch.js +163 -0
- package/dist/src/server/routes/pipeline-diagnostics.js +33 -0
- package/dist/src/server/routes/pipeline-identity.js +24 -0
- package/dist/src/server/routes/pipeline-links.js +117 -0
- package/dist/src/server/routes/pipeline-outputs.js +27 -0
- package/dist/src/server/routes/pipeline-queue.js +62 -0
- package/dist/src/server/routes/pipeline-runtime.js +162 -0
- package/dist/src/server/routes/pipeline-scheduler.js +69 -0
- package/dist/src/server/routes/pipeline-workflow.js +180 -0
- package/dist/src/server/routes/pipelines.js +96 -0
- package/dist/src/server/routes/sessions.js +244 -0
- package/dist/src/server/routes/timeline.js +14 -0
- package/dist/src/server/serve-static.js +42 -0
- package/dist/src/server/types.js +2 -0
- package/dist/src/services/agent-service.js +79 -0
- package/dist/src/services/artifact-service.js +74 -0
- package/dist/src/services/gateway-read-helpers.js +10 -0
- package/dist/src/services/index.js +23 -0
- package/dist/src/services/pipeline-service.js +529 -0
- package/dist/src/services/pipeline-status.js +93 -0
- package/dist/src/services/read-services.js +60 -0
- package/dist/src/services/scheduler-service.js +37 -0
- package/dist/src/services/session-service.js +227 -0
- package/dist/src/services/system-service.js +26 -0
- package/dist/src/transport/ws-broker.js +48 -0
- package/dist/src/utils/array.js +17 -0
- package/dist/src/utils/guards.js +5 -0
- package/dist/src/utils/session.js +60 -0
- package/dist/src/version.js +5 -0
- package/package.json +61 -0
- package/web/dist/assets/index-CWnfhkn-.js +65 -0
- package/web/dist/assets/index-gZ0xOfSO.css +1 -0
- package/web/dist/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
- package/web/dist/assets/jetbrains-mono-cyrillic-500-normal-DJqRU3vO.woff +0 -0
- package/web/dist/assets/jetbrains-mono-cyrillic-500-normal-DmUKJPL_.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-cyrillic-700-normal-BWTpRfYl.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-cyrillic-700-normal-CEoEElIJ.woff +0 -0
- package/web/dist/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
- package/web/dist/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-greek-500-normal-D7SFKleX.woff +0 -0
- package/web/dist/assets/jetbrains-mono-greek-500-normal-JpySY46c.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-greek-700-normal-C6CZE3T8.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-greek-700-normal-DEigVDxa.woff +0 -0
- package/web/dist/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/web/dist/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
- package/web/dist/assets/jetbrains-mono-latin-700-normal-BYuf6tUa.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-latin-700-normal-D3wTyLJW.woff +0 -0
- package/web/dist/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
- package/web/dist/assets/jetbrains-mono-latin-ext-500-normal-Cut-4mMH.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-latin-ext-500-normal-ckzbgY84.woff +0 -0
- package/web/dist/assets/jetbrains-mono-latin-ext-700-normal-CZipNAKV.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-latin-ext-700-normal-CxPITLHs.woff +0 -0
- package/web/dist/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
- package/web/dist/assets/jetbrains-mono-vietnamese-500-normal-DNRqzVM1.woff +0 -0
- package/web/dist/assets/jetbrains-mono-vietnamese-700-normal-BDLVIk2r.woff +0 -0
- package/web/dist/assets/space-grotesk-latin-400-normal-BnQMeOim.woff +0 -0
- package/web/dist/assets/space-grotesk-latin-400-normal-CJ-V5oYT.woff2 +0 -0
- package/web/dist/assets/space-grotesk-latin-500-normal-CNSSEhBt.woff +0 -0
- package/web/dist/assets/space-grotesk-latin-500-normal-lFbtlQH6.woff2 +0 -0
- package/web/dist/assets/space-grotesk-latin-700-normal-CwsQ-cCU.woff +0 -0
- package/web/dist/assets/space-grotesk-latin-700-normal-RjhwGPKo.woff2 +0 -0
- package/web/dist/assets/space-grotesk-latin-ext-400-normal-CfP_5XZW.woff2 +0 -0
- package/web/dist/assets/space-grotesk-latin-ext-400-normal-DRPE3kg4.woff +0 -0
- package/web/dist/assets/space-grotesk-latin-ext-500-normal-3dgZTiw9.woff +0 -0
- package/web/dist/assets/space-grotesk-latin-ext-500-normal-DUe3BAxM.woff2 +0 -0
- package/web/dist/assets/space-grotesk-latin-ext-700-normal-BQnZhY3m.woff2 +0 -0
- package/web/dist/assets/space-grotesk-latin-ext-700-normal-HVCqSBdx.woff +0 -0
- package/web/dist/assets/space-grotesk-vietnamese-400-normal-B7xT_GF5.woff2 +0 -0
- package/web/dist/assets/space-grotesk-vietnamese-400-normal-BIWiOVfw.woff +0 -0
- package/web/dist/assets/space-grotesk-vietnamese-500-normal-BTqKIpxg.woff +0 -0
- package/web/dist/assets/space-grotesk-vietnamese-500-normal-BmEvtly_.woff2 +0 -0
- package/web/dist/assets/space-grotesk-vietnamese-700-normal-DMty7AZE.woff2 +0 -0
- package/web/dist/assets/space-grotesk-vietnamese-700-normal-Duxec5Rn.woff +0 -0
- package/web/dist/favicon.svg +10 -0
- package/web/dist/index.html +14 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readRunLogPage = void 0;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_readline_1 = require("node:readline");
|
|
6
|
+
const guards_1 = require("../utils/guards");
|
|
7
|
+
const MAX_LIMIT = 300;
|
|
8
|
+
const isRunLogLevel = (value) => value === "info" || value === "warn" || value === "error";
|
|
9
|
+
const normalizeLimit = (value) => {
|
|
10
|
+
// 服务端必须兜底分页,避免前端漏传 limit 时把大型日志一次性读入 V8 堆。
|
|
11
|
+
if (value === undefined)
|
|
12
|
+
return MAX_LIMIT;
|
|
13
|
+
if (!Number.isFinite(value))
|
|
14
|
+
return MAX_LIMIT;
|
|
15
|
+
return Math.max(1, Math.min(MAX_LIMIT, Math.trunc(value)));
|
|
16
|
+
};
|
|
17
|
+
const normalizeOffset = (value) => {
|
|
18
|
+
if (!Number.isFinite(value))
|
|
19
|
+
return 0;
|
|
20
|
+
return Math.max(0, Math.trunc(value ?? 0));
|
|
21
|
+
};
|
|
22
|
+
const parseRunLogLine = (line) => {
|
|
23
|
+
try {
|
|
24
|
+
const parsed = JSON.parse(line);
|
|
25
|
+
if (!(0, guards_1.isRecord)(parsed))
|
|
26
|
+
return null;
|
|
27
|
+
const id = typeof parsed.id === "string" ? parsed.id.trim() : "";
|
|
28
|
+
const ts = typeof parsed.ts === "string" ? parsed.ts.trim() : "";
|
|
29
|
+
const level = parsed.level;
|
|
30
|
+
const runId = typeof parsed.runId === "string" ? parsed.runId.trim() : "";
|
|
31
|
+
const text = typeof parsed.text === "string" ? parsed.text : "";
|
|
32
|
+
if (!id || !ts || !runId || !text || !isRunLogLevel(level))
|
|
33
|
+
return null;
|
|
34
|
+
return {
|
|
35
|
+
id,
|
|
36
|
+
ts,
|
|
37
|
+
level,
|
|
38
|
+
runId,
|
|
39
|
+
text,
|
|
40
|
+
...(parsed.detail === undefined ? {} : { detail: parsed.detail }),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const stringifyMatchTarget = (entry) => {
|
|
48
|
+
if (entry.detail === undefined)
|
|
49
|
+
return entry.text;
|
|
50
|
+
if (typeof entry.detail === "string")
|
|
51
|
+
return `${entry.text}\n${entry.detail}`;
|
|
52
|
+
try {
|
|
53
|
+
return `${entry.text}\n${JSON.stringify(entry.detail)}`;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return entry.text;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const matchesQuery = (entry, query) => {
|
|
60
|
+
if (entry.runId !== query.runId)
|
|
61
|
+
return false;
|
|
62
|
+
if (query.levels && !query.levels.includes(entry.level))
|
|
63
|
+
return false;
|
|
64
|
+
if (query.keyword) {
|
|
65
|
+
const haystack = stringifyMatchTarget(entry).toLowerCase();
|
|
66
|
+
if (!haystack.includes(query.keyword))
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
return true;
|
|
70
|
+
};
|
|
71
|
+
const scanRunLogLines = async (logFile, onLine) => {
|
|
72
|
+
const stream = (0, node_fs_1.createReadStream)(logFile, { encoding: "utf8" });
|
|
73
|
+
const reader = (0, node_readline_1.createInterface)({
|
|
74
|
+
input: stream,
|
|
75
|
+
crlfDelay: Infinity,
|
|
76
|
+
});
|
|
77
|
+
try {
|
|
78
|
+
for await (const line of reader) {
|
|
79
|
+
if (line.trim().length === 0)
|
|
80
|
+
continue;
|
|
81
|
+
onLine(line);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
reader.close();
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const readRunLogPage = async (logFile, query) => {
|
|
89
|
+
const limit = normalizeLimit(query.limit);
|
|
90
|
+
const offset = normalizeOffset(query.offset);
|
|
91
|
+
const keyword = query.keyword?.trim().toLowerCase() || null;
|
|
92
|
+
const levels = query.levels && query.levels.length > 0 ? [...new Set(query.levels)] : null;
|
|
93
|
+
const order = query.order === "asc" ? "asc" : "desc";
|
|
94
|
+
let parseErrorCount = 0;
|
|
95
|
+
let total = 0;
|
|
96
|
+
const matcher = (entry) => matchesQuery(entry, {
|
|
97
|
+
runId: query.runId,
|
|
98
|
+
levels,
|
|
99
|
+
keyword,
|
|
100
|
+
});
|
|
101
|
+
await scanRunLogLines(logFile, (line) => {
|
|
102
|
+
const entry = parseRunLogLine(line);
|
|
103
|
+
if (!entry) {
|
|
104
|
+
parseErrorCount += 1;
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (matcher(entry))
|
|
108
|
+
total += 1;
|
|
109
|
+
});
|
|
110
|
+
const start = order === "asc"
|
|
111
|
+
? offset
|
|
112
|
+
: Math.max(0, total - offset - limit);
|
|
113
|
+
const end = order === "asc"
|
|
114
|
+
? Math.min(total, offset + limit)
|
|
115
|
+
: Math.max(0, total - offset);
|
|
116
|
+
const items = [];
|
|
117
|
+
let matchedIndex = 0;
|
|
118
|
+
await scanRunLogLines(logFile, (line) => {
|
|
119
|
+
if (matchedIndex >= end)
|
|
120
|
+
return;
|
|
121
|
+
const entry = parseRunLogLine(line);
|
|
122
|
+
if (!entry || !matcher(entry))
|
|
123
|
+
return;
|
|
124
|
+
if (matchedIndex >= start)
|
|
125
|
+
items.push(entry);
|
|
126
|
+
matchedIndex += 1;
|
|
127
|
+
});
|
|
128
|
+
if (order === "desc")
|
|
129
|
+
items.reverse();
|
|
130
|
+
const nextOffset = offset + items.length < total ? offset + items.length : null;
|
|
131
|
+
return {
|
|
132
|
+
items,
|
|
133
|
+
total,
|
|
134
|
+
offset,
|
|
135
|
+
limit,
|
|
136
|
+
nextOffset,
|
|
137
|
+
hasMore: nextOffset !== null,
|
|
138
|
+
parseErrorCount,
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
exports.readRunLogPage = readRunLogPage;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createRunLogService = void 0;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const promises_1 = require("node:fs/promises");
|
|
6
|
+
const node_path_1 = require("node:path");
|
|
7
|
+
const run_log_reader_1 = require("./run-log-reader");
|
|
8
|
+
const TIMELINE_LOG_FILE = "timeline.log";
|
|
9
|
+
const createRunLogService = (options) => {
|
|
10
|
+
const getRunLogPath = (runId) => (0, node_path_1.join)(options.rootDir, runId, TIMELINE_LOG_FILE);
|
|
11
|
+
const assertRunLogExists = async (runId) => {
|
|
12
|
+
await (0, promises_1.access)(getRunLogPath(runId), node_fs_1.constants.F_OK);
|
|
13
|
+
};
|
|
14
|
+
const queryTimeline = async (query) => {
|
|
15
|
+
const runId = query.runId.trim();
|
|
16
|
+
await assertRunLogExists(runId);
|
|
17
|
+
return (0, run_log_reader_1.readRunLogPage)(getRunLogPath(runId), { ...query, runId });
|
|
18
|
+
};
|
|
19
|
+
const readRawTimeline = async (runId) => {
|
|
20
|
+
const normalizedRunId = runId.trim();
|
|
21
|
+
await assertRunLogExists(normalizedRunId);
|
|
22
|
+
return getRunLogPath(normalizedRunId);
|
|
23
|
+
};
|
|
24
|
+
const listRuns = async () => {
|
|
25
|
+
try {
|
|
26
|
+
const entries = await (0, promises_1.readdir)(options.rootDir, { withFileTypes: true });
|
|
27
|
+
return entries
|
|
28
|
+
.filter((entry) => entry.isDirectory())
|
|
29
|
+
.map((entry) => entry.name)
|
|
30
|
+
.sort((a, b) => b.localeCompare(a));
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
queryTimeline,
|
|
38
|
+
readRawTimeline,
|
|
39
|
+
listRuns,
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
exports.createRunLogService = createRunLogService;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createAgentActivityTracker = void 0;
|
|
4
|
+
const SESSION_AGENT_PATTERN = /^agent:([^:]+):/i;
|
|
5
|
+
const findStringByKeys = (value, keys, depth = 0) => {
|
|
6
|
+
if (depth > 5 || value === null || value === undefined)
|
|
7
|
+
return null;
|
|
8
|
+
if (typeof value === "string")
|
|
9
|
+
return value.trim() || null;
|
|
10
|
+
if (Array.isArray(value)) {
|
|
11
|
+
for (const item of value) {
|
|
12
|
+
const found = findStringByKeys(item, keys, depth + 1);
|
|
13
|
+
if (found)
|
|
14
|
+
return found;
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
if (typeof value !== "object")
|
|
19
|
+
return null;
|
|
20
|
+
const record = value;
|
|
21
|
+
for (const key of keys) {
|
|
22
|
+
const raw = record[key];
|
|
23
|
+
if (typeof raw === "string" && raw.trim())
|
|
24
|
+
return raw.trim();
|
|
25
|
+
}
|
|
26
|
+
for (const item of Object.values(record)) {
|
|
27
|
+
const found = findStringByKeys(item, keys, depth + 1);
|
|
28
|
+
if (found)
|
|
29
|
+
return found;
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
};
|
|
33
|
+
const inferLifecycle = (payload) => {
|
|
34
|
+
const marker = (findStringByKeys(payload, ["status", "state", "phase", "event", "type", "kind"]) ?? "").toLowerCase();
|
|
35
|
+
if (!marker)
|
|
36
|
+
return "unknown";
|
|
37
|
+
if (marker.includes("start") ||
|
|
38
|
+
marker.includes("running") ||
|
|
39
|
+
marker.includes("in_progress") ||
|
|
40
|
+
marker.includes("processing") ||
|
|
41
|
+
marker.includes("stream")) {
|
|
42
|
+
return "start";
|
|
43
|
+
}
|
|
44
|
+
if (marker.includes("done") ||
|
|
45
|
+
marker.includes("finish") ||
|
|
46
|
+
marker.includes("complete") ||
|
|
47
|
+
marker.includes("success") ||
|
|
48
|
+
marker.includes("failed") ||
|
|
49
|
+
marker.includes("error") ||
|
|
50
|
+
marker.includes("idle") ||
|
|
51
|
+
marker.includes("stop")) {
|
|
52
|
+
return "end";
|
|
53
|
+
}
|
|
54
|
+
return "unknown";
|
|
55
|
+
};
|
|
56
|
+
const createAgentActivityTracker = (deps) => {
|
|
57
|
+
const activeAgents = new Set();
|
|
58
|
+
const toActivityKey = (agentId, runId) => `${runId ?? "global"}::${agentId}`;
|
|
59
|
+
const runLabel = (runId) => (runId ? `run:${runId}` : "run:unknown");
|
|
60
|
+
const pushLifecycle = (agentId, runId, lifecycle, source, frame) => {
|
|
61
|
+
const detail = {
|
|
62
|
+
source,
|
|
63
|
+
agentId,
|
|
64
|
+
runId,
|
|
65
|
+
lifecycle,
|
|
66
|
+
seq: frame.seq ?? null,
|
|
67
|
+
stateVersion: frame.stateVersion ?? null,
|
|
68
|
+
};
|
|
69
|
+
if (lifecycle === "start") {
|
|
70
|
+
deps.pushTimeline(`Agent ${agentId} 开始工作 (${runLabel(runId)}, ${source})`, "info", detail);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
deps.pushTimeline(`Agent ${agentId} 结束工作 (${runLabel(runId)}, ${source})`, "info", detail);
|
|
74
|
+
};
|
|
75
|
+
const refreshAgentActivity = (agentId, source, runId, frame) => {
|
|
76
|
+
const activityKey = toActivityKey(agentId, runId);
|
|
77
|
+
if (!activeAgents.has(activityKey)) {
|
|
78
|
+
activeAgents.add(activityKey);
|
|
79
|
+
pushLifecycle(agentId, runId, "start", source, frame);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const endAgentActivity = (agentId, source, runId, frame) => {
|
|
83
|
+
const endAllByAgent = (labelRunId) => {
|
|
84
|
+
const keys = [...activeAgents].filter((key) => key.endsWith(`::${agentId}`));
|
|
85
|
+
for (const key of keys) {
|
|
86
|
+
activeAgents.delete(key);
|
|
87
|
+
}
|
|
88
|
+
if (keys.length > 0) {
|
|
89
|
+
pushLifecycle(agentId, labelRunId, "end", source, frame);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
if (!runId) {
|
|
93
|
+
endAllByAgent(null);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const activityKey = toActivityKey(agentId, runId);
|
|
97
|
+
if (activeAgents.has(activityKey)) {
|
|
98
|
+
activeAgents.delete(activityKey);
|
|
99
|
+
pushLifecycle(agentId, runId, "end", source, frame);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
// Some gateways emit different runId formats between start/end events.
|
|
103
|
+
// Fallback: end all active runs for this agent to avoid stale busy state.
|
|
104
|
+
endAllByAgent(runId);
|
|
105
|
+
};
|
|
106
|
+
const normalizeAgentId = (candidate) => {
|
|
107
|
+
if (!candidate)
|
|
108
|
+
return null;
|
|
109
|
+
const trimmed = candidate.trim();
|
|
110
|
+
return trimmed || null;
|
|
111
|
+
};
|
|
112
|
+
const inferAgentIdFromSessionId = (sessionId) => {
|
|
113
|
+
const mapped = deps.resolveAgentBySessionId(sessionId);
|
|
114
|
+
const normalizedMapped = normalizeAgentId(mapped);
|
|
115
|
+
if (normalizedMapped)
|
|
116
|
+
return normalizedMapped;
|
|
117
|
+
const matched = sessionId.match(SESSION_AGENT_PATTERN);
|
|
118
|
+
if (!matched)
|
|
119
|
+
return null;
|
|
120
|
+
return normalizeAgentId(matched[1]);
|
|
121
|
+
};
|
|
122
|
+
const resolveAgentFromPayload = (payload) => {
|
|
123
|
+
// For gateway agent/chat events, sessionKey is the stable source of agent identity.
|
|
124
|
+
// Do not trust generic payload agentId/runId fields (they may be per-run UUIDs).
|
|
125
|
+
const sessionId = findStringByKeys(payload, ["sessionKey", "sessionId", "key", "session"]);
|
|
126
|
+
if (!sessionId)
|
|
127
|
+
return null;
|
|
128
|
+
return inferAgentIdFromSessionId(sessionId);
|
|
129
|
+
};
|
|
130
|
+
const handleFrame = (frame) => {
|
|
131
|
+
if (frame.type !== "event")
|
|
132
|
+
return;
|
|
133
|
+
const eventFrame = frame;
|
|
134
|
+
const payload = eventFrame.payload;
|
|
135
|
+
if (!payload)
|
|
136
|
+
return;
|
|
137
|
+
const agentId = resolveAgentFromPayload(payload);
|
|
138
|
+
if (!agentId)
|
|
139
|
+
return;
|
|
140
|
+
const runIdRaw = payload.runId;
|
|
141
|
+
const runId = typeof runIdRaw === "string" && runIdRaw.trim() ? runIdRaw.trim() : null;
|
|
142
|
+
if (eventFrame.event === "agent") {
|
|
143
|
+
const stream = typeof payload.stream === "string" ? payload.stream : "";
|
|
144
|
+
if (stream !== "lifecycle")
|
|
145
|
+
return;
|
|
146
|
+
const lifecycle = inferLifecycle(payload.data);
|
|
147
|
+
if (lifecycle === "start") {
|
|
148
|
+
refreshAgentActivity(agentId, "event:agent.lifecycle", runId, eventFrame);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (lifecycle === "end") {
|
|
152
|
+
endAgentActivity(agentId, "event:agent.lifecycle", runId, eventFrame);
|
|
153
|
+
}
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (eventFrame.event === "sessions.changed") {
|
|
157
|
+
const phase = findStringByKeys(payload, ["phase"])?.toLowerCase() ?? "";
|
|
158
|
+
const status = findStringByKeys(payload, ["status"])?.toLowerCase() ?? "";
|
|
159
|
+
const isStart = phase === "start" || status === "running";
|
|
160
|
+
const isEnd = phase === "end" ||
|
|
161
|
+
status === "done" ||
|
|
162
|
+
status === "idle" ||
|
|
163
|
+
status === "completed" ||
|
|
164
|
+
status === "stopped" ||
|
|
165
|
+
status === "success" ||
|
|
166
|
+
status === "failed";
|
|
167
|
+
if (isStart) {
|
|
168
|
+
refreshAgentActivity(agentId, "event:sessions.changed", runId, eventFrame);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (isEnd) {
|
|
172
|
+
endAgentActivity(agentId, "event:sessions.changed", runId, eventFrame);
|
|
173
|
+
}
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (eventFrame.event === "chat") {
|
|
177
|
+
const state = findStringByKeys(payload, ["state"])?.toLowerCase() ?? "";
|
|
178
|
+
if (state === "final" || state === "done" || state === "end" || state === "completed") {
|
|
179
|
+
endAgentActivity(agentId, "event:chat.final", runId, eventFrame);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
const dispose = () => {
|
|
184
|
+
activeAgents.clear();
|
|
185
|
+
};
|
|
186
|
+
return {
|
|
187
|
+
handleFrame,
|
|
188
|
+
dispose,
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
exports.createAgentActivityTracker = createAgentActivityTracker;
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.persistEnvelopeFile = exports.appendMovedArtifactIndexRecord = exports.persistArtifactFile = exports.buildArtifactStorageDirs = void 0;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
const promises_1 = require("node:fs/promises");
|
|
6
|
+
const node_path_1 = require("node:path");
|
|
7
|
+
const artifact_index_1 = require("../artifacts/artifact-index");
|
|
8
|
+
const pad2 = (value) => String(value).padStart(2, "0");
|
|
9
|
+
const formatLocalDateBucket = (at) => `${at.getFullYear()}-${pad2(at.getMonth() + 1)}-${pad2(at.getDate())}`;
|
|
10
|
+
const buildArtifactStorageDirs = (rootDir, runId, status, savedAt = new Date(), batchRunId) => {
|
|
11
|
+
// 运行产物需要按"结果状态/日期/runId"归档,避免目录长期平铺后难以按日排障或批量清理。
|
|
12
|
+
// envelopes 与 artifacts 分桶存放,是为了把节点回执和实际产物内容分开,减少误读与误删风险。
|
|
13
|
+
const dateBucket = formatLocalDateBucket(savedAt);
|
|
14
|
+
const runDir = batchRunId
|
|
15
|
+
? (0, node_path_1.join)(rootDir, status, dateBucket, batchRunId, runId)
|
|
16
|
+
: (0, node_path_1.join)(rootDir, status, dateBucket, runId);
|
|
17
|
+
return {
|
|
18
|
+
dateBucket,
|
|
19
|
+
runDir,
|
|
20
|
+
envelopesDir: (0, node_path_1.join)(runDir, "envelopes"),
|
|
21
|
+
artifactsDir: (0, node_path_1.join)(runDir, "artifacts"),
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
exports.buildArtifactStorageDirs = buildArtifactStorageDirs;
|
|
25
|
+
const sanitizeFileSegment = (value) => {
|
|
26
|
+
const normalized = value
|
|
27
|
+
.trim()
|
|
28
|
+
.replace(/[<>:"/\\|?*\x00-\x1F]/g, "_")
|
|
29
|
+
.replace(/\s+/g, "_")
|
|
30
|
+
.replace(/\.+$/g, "_");
|
|
31
|
+
return normalized || "unnamed";
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* 产物文件统一写入入口。
|
|
35
|
+
* 负责:目录创建、原子写入(临时文件 + rename)、真实 SHA-256 哈希计算、索引追加、ArtifactManifest 构造。
|
|
36
|
+
*/
|
|
37
|
+
const persistArtifactFile = async (rootDir, status, ctx, artifact, opts) => {
|
|
38
|
+
const savedAt = opts?.savedAt ?? new Date();
|
|
39
|
+
const persistDirs = (0, exports.buildArtifactStorageDirs)(rootDir, ctx.runId, status, savedAt, ctx.batchRunId);
|
|
40
|
+
await (0, promises_1.mkdir)(persistDirs.artifactsDir, { recursive: true });
|
|
41
|
+
// 统一的文件序列化格式,kind 字段供读取端分发
|
|
42
|
+
const fileContent = {
|
|
43
|
+
schemaVersion: 1,
|
|
44
|
+
runId: ctx.runId,
|
|
45
|
+
batchRunId: ctx.batchRunId ?? null,
|
|
46
|
+
nodeId: ctx.nodeId ?? null,
|
|
47
|
+
groupId: ctx.groupId ?? null,
|
|
48
|
+
itemKey: ctx.itemKey ?? null,
|
|
49
|
+
requestId: ctx.requestId ?? null,
|
|
50
|
+
kind: ctx.kind,
|
|
51
|
+
savedAt: savedAt.toISOString(),
|
|
52
|
+
artifact,
|
|
53
|
+
};
|
|
54
|
+
const fileNameSegments = [sanitizeFileSegment(ctx.runId), sanitizeFileSegment(ctx.nodeId ?? ctx.groupId ?? "unknown")];
|
|
55
|
+
if (opts?.fileNameSuffix)
|
|
56
|
+
fileNameSegments.push(sanitizeFileSegment(opts.fileNameSuffix));
|
|
57
|
+
const fileName = `${fileNameSegments.join("-")}.json`;
|
|
58
|
+
// 原子写入: 先写临时文件,计算 hash,再 rename 到最终路径
|
|
59
|
+
const tmpFileName = `.tmp-${(0, node_crypto_1.randomUUID)()}.json`;
|
|
60
|
+
const tmpPath = (0, node_path_1.join)(persistDirs.artifactsDir, tmpFileName);
|
|
61
|
+
const json = JSON.stringify(fileContent, null, 2);
|
|
62
|
+
await (0, promises_1.writeFile)(tmpPath, json, "utf8");
|
|
63
|
+
const hash = `sha256:${(0, node_crypto_1.createHash)("sha256").update(json).digest("hex")}`;
|
|
64
|
+
const finalPath = (0, node_path_1.join)(persistDirs.artifactsDir, fileName);
|
|
65
|
+
await (0, promises_1.rename)(tmpPath, finalPath);
|
|
66
|
+
const fileStat = await (0, promises_1.stat)(finalPath);
|
|
67
|
+
const manifestId = (0, node_crypto_1.randomUUID)();
|
|
68
|
+
const createdAt = new Date().toISOString();
|
|
69
|
+
const updatedAt = fileStat.mtime.toISOString();
|
|
70
|
+
const relativePath = (0, node_path_1.relative)(rootDir, finalPath).replaceAll("\\", "/");
|
|
71
|
+
// 追加索引记录(best-effort,失败不阻断产物写入)
|
|
72
|
+
const pipelineId = ctx.pipelineId;
|
|
73
|
+
const indexRecord = {
|
|
74
|
+
schemaVersion: 1,
|
|
75
|
+
artifactId: manifestId,
|
|
76
|
+
pipelineId,
|
|
77
|
+
status,
|
|
78
|
+
kind: ctx.kind,
|
|
79
|
+
dateBucket: persistDirs.dateBucket,
|
|
80
|
+
runId: ctx.runId,
|
|
81
|
+
batchRunId: ctx.batchRunId ?? null,
|
|
82
|
+
nodeId: ctx.nodeId ?? null,
|
|
83
|
+
groupId: ctx.groupId ?? null,
|
|
84
|
+
itemKey: ctx.itemKey ?? null,
|
|
85
|
+
requestId: ctx.requestId ?? null,
|
|
86
|
+
type: artifact.type,
|
|
87
|
+
artifactSchemaVersion: artifact.schemaVersion,
|
|
88
|
+
name: artifact.name,
|
|
89
|
+
relativePath,
|
|
90
|
+
sizeBytes: fileStat.size,
|
|
91
|
+
hash,
|
|
92
|
+
createdAt,
|
|
93
|
+
updatedAt,
|
|
94
|
+
};
|
|
95
|
+
// 追加索引记录:文件已通过 tmp+rename 原子写入磁盘,索引追加失败不丢失产物文件
|
|
96
|
+
const _ir = await (0, artifact_index_1.appendIndexRecord)(rootDir, indexRecord);
|
|
97
|
+
void _ir;
|
|
98
|
+
return {
|
|
99
|
+
id: manifestId,
|
|
100
|
+
type: artifact.type,
|
|
101
|
+
schemaVersion: artifact.schemaVersion,
|
|
102
|
+
name: artifact.name,
|
|
103
|
+
path: finalPath,
|
|
104
|
+
hash,
|
|
105
|
+
sourceNodeId: ctx.nodeId ?? ctx.groupId ?? "unknown",
|
|
106
|
+
createdAt,
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
exports.persistArtifactFile = persistArtifactFile;
|
|
110
|
+
/** 产物移动(如 rejected 归档)后补记索引,使查询端通过 artifactId 去重拿到最新位置。 */
|
|
111
|
+
const appendMovedArtifactIndexRecord = async (rootDir, input) => {
|
|
112
|
+
const targetPath = (0, node_path_1.resolve)(rootDir, input.relativePath);
|
|
113
|
+
const fileStat = await (0, promises_1.stat)(targetPath);
|
|
114
|
+
const segments = input.relativePath.split("/").filter(Boolean);
|
|
115
|
+
const dateBucket = segments[1] ?? formatLocalDateBucket(input.movedAt);
|
|
116
|
+
const batchRunId = segments[2]?.startsWith("batch-") ? segments[2] : null;
|
|
117
|
+
const runId = batchRunId ? segments[3] ?? null : segments[2] ?? null;
|
|
118
|
+
const pipelineId = input.pipelineId;
|
|
119
|
+
const _ir2 = await (0, artifact_index_1.appendIndexRecord)(rootDir, {
|
|
120
|
+
schemaVersion: 1,
|
|
121
|
+
artifactId: input.manifest.id,
|
|
122
|
+
pipelineId,
|
|
123
|
+
status: input.status,
|
|
124
|
+
kind: "artifact",
|
|
125
|
+
dateBucket,
|
|
126
|
+
runId,
|
|
127
|
+
batchRunId,
|
|
128
|
+
nodeId: input.manifest.sourceNodeId,
|
|
129
|
+
groupId: null,
|
|
130
|
+
itemKey: null,
|
|
131
|
+
requestId: null,
|
|
132
|
+
type: input.manifest.type,
|
|
133
|
+
artifactSchemaVersion: input.manifest.schemaVersion,
|
|
134
|
+
name: input.manifest.name,
|
|
135
|
+
relativePath: input.relativePath,
|
|
136
|
+
sizeBytes: fileStat.size,
|
|
137
|
+
hash: input.manifest.hash,
|
|
138
|
+
createdAt: input.manifest.createdAt,
|
|
139
|
+
updatedAt: input.movedAt.toISOString(),
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
exports.appendMovedArtifactIndexRecord = appendMovedArtifactIndexRecord;
|
|
143
|
+
/** 保存 envelope 回执文件到 envelopes 子目录并追加索引,使 kind=envelope 可在列表中查询。 */
|
|
144
|
+
const persistEnvelopeFile = async (rootDir, status, ctx, envelope, opts) => {
|
|
145
|
+
const savedAt = opts?.savedAt ?? new Date();
|
|
146
|
+
const persistDirs = (0, exports.buildArtifactStorageDirs)(rootDir, ctx.runId, status, savedAt, ctx.batchRunId);
|
|
147
|
+
await (0, promises_1.mkdir)(persistDirs.envelopesDir, { recursive: true });
|
|
148
|
+
const fileName = `${sanitizeFileSegment(ctx.runId)}-${sanitizeFileSegment(ctx.nodeId ?? "unknown")}-${sanitizeFileSegment(ctx.requestId ?? "envelope")}-envelope.json`;
|
|
149
|
+
const fileContent = {
|
|
150
|
+
schemaVersion: 1,
|
|
151
|
+
runId: ctx.runId,
|
|
152
|
+
batchRunId: ctx.batchRunId ?? null,
|
|
153
|
+
nodeId: ctx.nodeId ?? null,
|
|
154
|
+
requestId: ctx.requestId ?? null,
|
|
155
|
+
kind: "envelope",
|
|
156
|
+
savedAt: savedAt.toISOString(),
|
|
157
|
+
envelope,
|
|
158
|
+
};
|
|
159
|
+
const tmpFileName = `.tmp-${(0, node_crypto_1.randomUUID)()}.json`;
|
|
160
|
+
const tmpPath = (0, node_path_1.join)(persistDirs.envelopesDir, tmpFileName);
|
|
161
|
+
const json = JSON.stringify(fileContent, null, 2);
|
|
162
|
+
await (0, promises_1.writeFile)(tmpPath, json, "utf8");
|
|
163
|
+
const hash = `sha256:${(0, node_crypto_1.createHash)("sha256").update(json).digest("hex")}`;
|
|
164
|
+
const finalPath = (0, node_path_1.join)(persistDirs.envelopesDir, fileName);
|
|
165
|
+
await (0, promises_1.rename)(tmpPath, finalPath);
|
|
166
|
+
const fileStat = await (0, promises_1.stat)(finalPath);
|
|
167
|
+
const manifestId = (0, node_crypto_1.randomUUID)();
|
|
168
|
+
const createdAt = new Date().toISOString();
|
|
169
|
+
const updatedAt = fileStat.mtime.toISOString();
|
|
170
|
+
const relativePathStr = (0, node_path_1.relative)(rootDir, finalPath).replaceAll("\\", "/");
|
|
171
|
+
const pipelineId = ctx.pipelineId;
|
|
172
|
+
const indexRecord = {
|
|
173
|
+
schemaVersion: 1,
|
|
174
|
+
artifactId: manifestId,
|
|
175
|
+
pipelineId,
|
|
176
|
+
status,
|
|
177
|
+
kind: "envelope",
|
|
178
|
+
dateBucket: persistDirs.dateBucket,
|
|
179
|
+
runId: ctx.runId,
|
|
180
|
+
batchRunId: ctx.batchRunId ?? null,
|
|
181
|
+
nodeId: ctx.nodeId ?? null,
|
|
182
|
+
groupId: null,
|
|
183
|
+
itemKey: null,
|
|
184
|
+
requestId: ctx.requestId ?? null,
|
|
185
|
+
type: "structured-output-envelope",
|
|
186
|
+
artifactSchemaVersion: 1,
|
|
187
|
+
name: "envelope",
|
|
188
|
+
relativePath: relativePathStr,
|
|
189
|
+
sizeBytes: fileStat.size,
|
|
190
|
+
hash,
|
|
191
|
+
createdAt,
|
|
192
|
+
updatedAt,
|
|
193
|
+
};
|
|
194
|
+
// 追加索引记录:文件已通过 tmp+rename 原子写入磁盘,索引追加失败不丢失产物文件
|
|
195
|
+
const _ir = await (0, artifact_index_1.appendIndexRecord)(rootDir, indexRecord);
|
|
196
|
+
void _ir;
|
|
197
|
+
return {
|
|
198
|
+
id: manifestId,
|
|
199
|
+
type: "structured-output-envelope",
|
|
200
|
+
schemaVersion: 1,
|
|
201
|
+
name: "envelope",
|
|
202
|
+
path: finalPath,
|
|
203
|
+
hash,
|
|
204
|
+
sourceNodeId: ctx.nodeId ?? "unknown",
|
|
205
|
+
createdAt,
|
|
206
|
+
};
|
|
207
|
+
};
|
|
208
|
+
exports.persistEnvelopeFile = persistEnvelopeFile;
|