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.
Files changed (204) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +172 -0
  3. package/README.zh-CN.md +172 -0
  4. package/dist/src/app/app-context-env.js +51 -0
  5. package/dist/src/app/create-app-context.js +127 -0
  6. package/dist/src/app/data-dir.js +29 -0
  7. package/dist/src/app/pipeline-config.js +105 -0
  8. package/dist/src/app/pipeline-plugin-config.js +2 -0
  9. package/dist/src/app/pipeline-registry.js +502 -0
  10. package/dist/src/app/pipeline-runtime.js +202 -0
  11. package/dist/src/app/runtime-store.js +151 -0
  12. package/dist/src/app/user-config.js +37 -0
  13. package/dist/src/artifacts/artifact-cleanup.js +192 -0
  14. package/dist/src/artifacts/artifact-index.js +262 -0
  15. package/dist/src/artifacts/artifact-rebuilder.js +120 -0
  16. package/dist/src/artifacts/storage-service.js +371 -0
  17. package/dist/src/cli/bootstrap.js +226 -0
  18. package/dist/src/cli/commands/agent.js +126 -0
  19. package/dist/src/cli/commands/artifact.js +175 -0
  20. package/dist/src/cli/commands/init.js +150 -0
  21. package/dist/src/cli/commands/pipeline/errors.js +37 -0
  22. package/dist/src/cli/commands/pipeline/result.js +179 -0
  23. package/dist/src/cli/commands/pipeline/selector.js +51 -0
  24. package/dist/src/cli/commands/pipeline/types.js +2 -0
  25. package/dist/src/cli/commands/pipeline/watch.js +67 -0
  26. package/dist/src/cli/commands/pipeline.js +339 -0
  27. package/dist/src/cli/commands/scheduler.js +81 -0
  28. package/dist/src/cli/commands/server.js +70 -0
  29. package/dist/src/cli/commands/system.js +21 -0
  30. package/dist/src/cli/errors.js +71 -0
  31. package/dist/src/cli/help.js +184 -0
  32. package/dist/src/cli/index.js +65 -0
  33. package/dist/src/cli/output.js +19 -0
  34. package/dist/src/cli/renderers/engine/json.js +67 -0
  35. package/dist/src/cli/renderers/engine/markdown.js +95 -0
  36. package/dist/src/cli/renderers/engine/types.js +2 -0
  37. package/dist/src/cli/renderers/engine/utils.js +32 -0
  38. package/dist/src/cli/renderers/index.js +27 -0
  39. package/dist/src/cli/renderers/specs/agent.js +78 -0
  40. package/dist/src/cli/renderers/specs/artifact.js +32 -0
  41. package/dist/src/cli/renderers/specs/index.js +36 -0
  42. package/dist/src/cli/renderers/specs/init.js +25 -0
  43. package/dist/src/cli/renderers/specs/pipeline.js +561 -0
  44. package/dist/src/cli/renderers/specs/scheduler.js +46 -0
  45. package/dist/src/cli/renderers/specs/server.js +38 -0
  46. package/dist/src/cli/renderers/specs/system.js +36 -0
  47. package/dist/src/cli/router.js +199 -0
  48. package/dist/src/cli/server-runtime-client.js +780 -0
  49. package/dist/src/cli/types.js +2 -0
  50. package/dist/src/gateway/frame-sanitizer.js +78 -0
  51. package/dist/src/gateway/gateway-client.js +462 -0
  52. package/dist/src/gateway/index.js +18 -0
  53. package/dist/src/gateway/types.js +2 -0
  54. package/dist/src/index.js +123 -0
  55. package/dist/src/logs/run-log-reader.js +141 -0
  56. package/dist/src/logs/run-log-service.js +42 -0
  57. package/dist/src/logs/run-log-types.js +2 -0
  58. package/dist/src/pipeline/agent-activity.js +191 -0
  59. package/dist/src/pipeline/artifact-storage.js +208 -0
  60. package/dist/src/pipeline/diagnostics/dependency-diagnostic.js +105 -0
  61. package/dist/src/pipeline/diagnostics/index.js +6 -0
  62. package/dist/src/pipeline/dispatch/pipeline-inbound-queue.js +215 -0
  63. package/dist/src/pipeline/dispatch/pipeline-link-dispatcher.js +66 -0
  64. package/dist/src/pipeline/dispatch/pipeline-link-store.js +94 -0
  65. package/dist/src/pipeline/dispatch/pipeline-queue-drainer.js +71 -0
  66. package/dist/src/pipeline/execution/dependency-check.js +52 -0
  67. package/dist/src/pipeline/execution/execution-result.js +2 -0
  68. package/dist/src/pipeline/execution/group-item-executor.js +128 -0
  69. package/dist/src/pipeline/execution/index.js +5 -0
  70. package/dist/src/pipeline/execution/node-item-executor.js +58 -0
  71. package/dist/src/pipeline/execution/node-runner.js +159 -0
  72. package/dist/src/pipeline/execution/readiness-state.js +10 -0
  73. package/dist/src/pipeline/execution/reject-handler.js +94 -0
  74. package/dist/src/pipeline/execution/rejected-artifact-archiver.js +45 -0
  75. package/dist/src/pipeline/execution/route-item-manager.js +253 -0
  76. package/dist/src/pipeline/execution/run-abort-controller.js +66 -0
  77. package/dist/src/pipeline/execution/run-state-helpers.js +257 -0
  78. package/dist/src/pipeline/execution/service.js +165 -0
  79. package/dist/src/pipeline/execution/session-registry.js +96 -0
  80. package/dist/src/pipeline/execution/structured-node-runner.js +411 -0
  81. package/dist/src/pipeline/execution-status.js +96 -0
  82. package/dist/src/pipeline/execution-timeout.js +21 -0
  83. package/dist/src/pipeline/identity/index.js +32 -0
  84. package/dist/src/pipeline/identity/types.js +2 -0
  85. package/dist/src/pipeline/item-batch-controller.js +227 -0
  86. package/dist/src/pipeline/output/pipeline-output-resolver.js +91 -0
  87. package/dist/src/pipeline/output/pipeline-output-store.js +60 -0
  88. package/dist/src/pipeline/runtime-model.js +173 -0
  89. package/dist/src/pipeline/scheduler/dependency-state.js +144 -0
  90. package/dist/src/pipeline/scheduler-service.js +314 -0
  91. package/dist/src/pipeline/state/group-item-state.js +50 -0
  92. package/dist/src/pipeline/state/group-run-state.js +41 -0
  93. package/dist/src/pipeline/state/index.js +20 -0
  94. package/dist/src/pipeline/state/node-item-state.js +67 -0
  95. package/dist/src/pipeline/state/node-run-state.js +51 -0
  96. package/dist/src/pipeline/state/types.js +2 -0
  97. package/dist/src/pipeline/state-machine.js +101 -0
  98. package/dist/src/pipeline/structured-output/contract.js +133 -0
  99. package/dist/src/pipeline/structured-output/index.js +22 -0
  100. package/dist/src/pipeline/structured-output/parser.js +214 -0
  101. package/dist/src/pipeline/structured-output/prompt.js +290 -0
  102. package/dist/src/pipeline/structured-output/waiter.js +139 -0
  103. package/dist/src/pipeline/template.js +135 -0
  104. package/dist/src/pipeline/timeline-log-store.js +57 -0
  105. package/dist/src/pipeline/tool-activity.js +94 -0
  106. package/dist/src/pipeline/types/pipeline-link.js +7 -0
  107. package/dist/src/pipeline/types/pipeline-output.js +11 -0
  108. package/dist/src/pipeline/types/workflow.js +2 -0
  109. package/dist/src/pipeline/workflow/branch-rules.js +74 -0
  110. package/dist/src/pipeline/workflow/defaults.js +48 -0
  111. package/dist/src/pipeline/workflow/io.js +89 -0
  112. package/dist/src/pipeline/workflow/normalize.js +347 -0
  113. package/dist/src/pipeline/workflow/routes.js +16 -0
  114. package/dist/src/pipeline/workflow/template-mapper.js +113 -0
  115. package/dist/src/pipeline/workflow/validate.js +312 -0
  116. package/dist/src/pipeline/workflow-graph.js +165 -0
  117. package/dist/src/server/api-handler.js +163 -0
  118. package/dist/src/server/http-utils.js +34 -0
  119. package/dist/src/server/middleware.js +61 -0
  120. package/dist/src/server/router.js +105 -0
  121. package/dist/src/server/routes/agents.js +189 -0
  122. package/dist/src/server/routes/artifacts.js +163 -0
  123. package/dist/src/server/routes/gateway.js +18 -0
  124. package/dist/src/server/routes/health.js +16 -0
  125. package/dist/src/server/routes/logs.js +73 -0
  126. package/dist/src/server/routes/pipeline-batch.js +163 -0
  127. package/dist/src/server/routes/pipeline-diagnostics.js +33 -0
  128. package/dist/src/server/routes/pipeline-identity.js +24 -0
  129. package/dist/src/server/routes/pipeline-links.js +117 -0
  130. package/dist/src/server/routes/pipeline-outputs.js +27 -0
  131. package/dist/src/server/routes/pipeline-queue.js +62 -0
  132. package/dist/src/server/routes/pipeline-runtime.js +162 -0
  133. package/dist/src/server/routes/pipeline-scheduler.js +69 -0
  134. package/dist/src/server/routes/pipeline-workflow.js +180 -0
  135. package/dist/src/server/routes/pipelines.js +96 -0
  136. package/dist/src/server/routes/sessions.js +244 -0
  137. package/dist/src/server/routes/timeline.js +14 -0
  138. package/dist/src/server/serve-static.js +42 -0
  139. package/dist/src/server/types.js +2 -0
  140. package/dist/src/services/agent-service.js +79 -0
  141. package/dist/src/services/artifact-service.js +74 -0
  142. package/dist/src/services/gateway-read-helpers.js +10 -0
  143. package/dist/src/services/index.js +23 -0
  144. package/dist/src/services/pipeline-service.js +529 -0
  145. package/dist/src/services/pipeline-status.js +93 -0
  146. package/dist/src/services/read-services.js +60 -0
  147. package/dist/src/services/scheduler-service.js +37 -0
  148. package/dist/src/services/session-service.js +227 -0
  149. package/dist/src/services/system-service.js +26 -0
  150. package/dist/src/transport/ws-broker.js +48 -0
  151. package/dist/src/utils/array.js +17 -0
  152. package/dist/src/utils/guards.js +5 -0
  153. package/dist/src/utils/session.js +60 -0
  154. package/dist/src/version.js +5 -0
  155. package/package.json +61 -0
  156. package/web/dist/assets/index-CWnfhkn-.js +65 -0
  157. package/web/dist/assets/index-gZ0xOfSO.css +1 -0
  158. package/web/dist/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
  159. package/web/dist/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
  160. package/web/dist/assets/jetbrains-mono-cyrillic-500-normal-DJqRU3vO.woff +0 -0
  161. package/web/dist/assets/jetbrains-mono-cyrillic-500-normal-DmUKJPL_.woff2 +0 -0
  162. package/web/dist/assets/jetbrains-mono-cyrillic-700-normal-BWTpRfYl.woff2 +0 -0
  163. package/web/dist/assets/jetbrains-mono-cyrillic-700-normal-CEoEElIJ.woff +0 -0
  164. package/web/dist/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
  165. package/web/dist/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
  166. package/web/dist/assets/jetbrains-mono-greek-500-normal-D7SFKleX.woff +0 -0
  167. package/web/dist/assets/jetbrains-mono-greek-500-normal-JpySY46c.woff2 +0 -0
  168. package/web/dist/assets/jetbrains-mono-greek-700-normal-C6CZE3T8.woff2 +0 -0
  169. package/web/dist/assets/jetbrains-mono-greek-700-normal-DEigVDxa.woff +0 -0
  170. package/web/dist/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
  171. package/web/dist/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
  172. package/web/dist/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
  173. package/web/dist/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
  174. package/web/dist/assets/jetbrains-mono-latin-700-normal-BYuf6tUa.woff2 +0 -0
  175. package/web/dist/assets/jetbrains-mono-latin-700-normal-D3wTyLJW.woff +0 -0
  176. package/web/dist/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
  177. package/web/dist/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
  178. package/web/dist/assets/jetbrains-mono-latin-ext-500-normal-Cut-4mMH.woff2 +0 -0
  179. package/web/dist/assets/jetbrains-mono-latin-ext-500-normal-ckzbgY84.woff +0 -0
  180. package/web/dist/assets/jetbrains-mono-latin-ext-700-normal-CZipNAKV.woff2 +0 -0
  181. package/web/dist/assets/jetbrains-mono-latin-ext-700-normal-CxPITLHs.woff +0 -0
  182. package/web/dist/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
  183. package/web/dist/assets/jetbrains-mono-vietnamese-500-normal-DNRqzVM1.woff +0 -0
  184. package/web/dist/assets/jetbrains-mono-vietnamese-700-normal-BDLVIk2r.woff +0 -0
  185. package/web/dist/assets/space-grotesk-latin-400-normal-BnQMeOim.woff +0 -0
  186. package/web/dist/assets/space-grotesk-latin-400-normal-CJ-V5oYT.woff2 +0 -0
  187. package/web/dist/assets/space-grotesk-latin-500-normal-CNSSEhBt.woff +0 -0
  188. package/web/dist/assets/space-grotesk-latin-500-normal-lFbtlQH6.woff2 +0 -0
  189. package/web/dist/assets/space-grotesk-latin-700-normal-CwsQ-cCU.woff +0 -0
  190. package/web/dist/assets/space-grotesk-latin-700-normal-RjhwGPKo.woff2 +0 -0
  191. package/web/dist/assets/space-grotesk-latin-ext-400-normal-CfP_5XZW.woff2 +0 -0
  192. package/web/dist/assets/space-grotesk-latin-ext-400-normal-DRPE3kg4.woff +0 -0
  193. package/web/dist/assets/space-grotesk-latin-ext-500-normal-3dgZTiw9.woff +0 -0
  194. package/web/dist/assets/space-grotesk-latin-ext-500-normal-DUe3BAxM.woff2 +0 -0
  195. package/web/dist/assets/space-grotesk-latin-ext-700-normal-BQnZhY3m.woff2 +0 -0
  196. package/web/dist/assets/space-grotesk-latin-ext-700-normal-HVCqSBdx.woff +0 -0
  197. package/web/dist/assets/space-grotesk-vietnamese-400-normal-B7xT_GF5.woff2 +0 -0
  198. package/web/dist/assets/space-grotesk-vietnamese-400-normal-BIWiOVfw.woff +0 -0
  199. package/web/dist/assets/space-grotesk-vietnamese-500-normal-BTqKIpxg.woff +0 -0
  200. package/web/dist/assets/space-grotesk-vietnamese-500-normal-BmEvtly_.woff2 +0 -0
  201. package/web/dist/assets/space-grotesk-vietnamese-700-normal-DMty7AZE.woff2 +0 -0
  202. package/web/dist/assets/space-grotesk-vietnamese-700-normal-Duxec5Rn.woff +0 -0
  203. package/web/dist/favicon.svg +10 -0
  204. package/web/dist/index.html +14 -0
@@ -0,0 +1,262 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rebuildArtifactIndex = exports.toStoredArtifactItem = exports.listIndexRecords = exports.readIndexRecords = exports.appendIndexRecord = exports.getIndexPath = void 0;
4
+ const promises_1 = require("node:fs/promises");
5
+ const node_fs_1 = require("node:fs");
6
+ const node_readline_1 = require("node:readline");
7
+ const node_path_1 = require("node:path");
8
+ const INDEX_FILE_NAME = "index.jsonl";
9
+ const getIndexPath = (artifactDir) => (0, node_path_1.resolve)(artifactDir, INDEX_FILE_NAME);
10
+ exports.getIndexPath = getIndexPath;
11
+ const encodeCursorV2 = (cursor) => `v2:${Buffer.from(JSON.stringify(cursor), "utf8").toString("base64url")}`;
12
+ /** 兼容解码 v2 和旧版 cursor。返回 null 表示无效。 */
13
+ const decodeAnyCursor = (cursor) => {
14
+ if (cursor.startsWith("v2:")) {
15
+ try {
16
+ const parsed = JSON.parse(Buffer.from(cursor.slice(3), "base64url").toString("utf8"));
17
+ if (parsed.v === 2 && parsed.updatedAt && parsed.artifactId) {
18
+ return { updatedAt: parsed.updatedAt, artifactId: parsed.artifactId, indexUpdatedAt: parsed.indexUpdatedAt };
19
+ }
20
+ return null;
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ }
26
+ // 旧 v1 cursor 格式: base64url(updatedAt|artifactId)
27
+ try {
28
+ const raw = Buffer.from(cursor, "base64url").toString("utf8");
29
+ const idx = raw.lastIndexOf("|");
30
+ if (idx < 0)
31
+ return null;
32
+ return { updatedAt: raw.slice(0, idx), artifactId: raw.slice(idx + 1) };
33
+ }
34
+ catch {
35
+ return null;
36
+ }
37
+ };
38
+ const getIndexUpdatedAt = async (artifactDir) => {
39
+ try {
40
+ const s = await (0, promises_1.stat)((0, exports.getIndexPath)(artifactDir));
41
+ return s.mtime.toISOString();
42
+ }
43
+ catch {
44
+ return "";
45
+ }
46
+ };
47
+ /** 追加一条索引记录到 index.jsonl。追加失败不抛异常,返回失败信息供调用方观测。 */
48
+ const appendIndexRecord = async (artifactDir, record) => {
49
+ const indexPath = (0, exports.getIndexPath)(artifactDir);
50
+ try {
51
+ await (0, promises_1.appendFile)(indexPath, `${JSON.stringify(record)}\n`, "utf8");
52
+ return { ok: true };
53
+ }
54
+ catch (error) {
55
+ return {
56
+ ok: false,
57
+ warning: {
58
+ code: "ARTIFACT_INDEX_APPEND_FAILED",
59
+ artifactDir,
60
+ indexPath,
61
+ relativePath: record.relativePath,
62
+ message: error instanceof Error ? error.message : String(error),
63
+ },
64
+ };
65
+ }
66
+ };
67
+ exports.appendIndexRecord = appendIndexRecord;
68
+ /** 读取索引文件所有行,损坏行跳过。使用流式读取避免大索引一次性内存占用。 */
69
+ const readIndexRecords = async (artifactDir) => {
70
+ const indexPath = (0, exports.getIndexPath)(artifactDir);
71
+ const records = [];
72
+ try {
73
+ const reader = (0, node_readline_1.createInterface)({
74
+ input: (0, node_fs_1.createReadStream)(indexPath, { encoding: "utf8" }),
75
+ crlfDelay: Infinity,
76
+ });
77
+ for await (const line of reader) {
78
+ if (!line.trim())
79
+ continue;
80
+ try {
81
+ records.push(JSON.parse(line));
82
+ }
83
+ catch {
84
+ // 损坏行跳过
85
+ }
86
+ }
87
+ }
88
+ catch {
89
+ return [];
90
+ }
91
+ return records;
92
+ };
93
+ exports.readIndexRecords = readIndexRecords;
94
+ const dedupeLatestByArtifactId = (records) => {
95
+ const latest = new Map();
96
+ for (const record of records) {
97
+ const previous = latest.get(record.artifactId);
98
+ if (!previous || Date.parse(record.updatedAt) >= Date.parse(previous.updatedAt)) {
99
+ latest.set(record.artifactId, record);
100
+ }
101
+ }
102
+ return [...latest.values()];
103
+ };
104
+ /** 从索引读取、过滤、排序、分页。基于 readIndexRecords 之上提供查询能力。 */
105
+ const listIndexRecords = async (artifactDir, filter) => {
106
+ const records = dedupeLatestByArtifactId(await (0, exports.readIndexRecords)(artifactDir));
107
+ const limitRaw = filter.limit ?? 100;
108
+ const limit = limitRaw <= 0 ? Number.MAX_SAFE_INTEGER
109
+ : Number.isFinite(limitRaw) ? Math.max(1, Math.min(5000, Math.trunc(limitRaw)))
110
+ : 100;
111
+ const statusSet = filter.statuses?.length ? new Set(filter.statuses) : null;
112
+ const kindSet = filter.kinds?.length ? new Set(filter.kinds) : null;
113
+ const nodeIdSet = filter.nodeIds?.length ? new Set(filter.nodeIds) : null;
114
+ const dateFrom = filter.dateFrom ?? null;
115
+ const dateTo = filter.dateTo ?? null;
116
+ const matched = [];
117
+ for (const record of records) {
118
+ if (filter.pipelineId && record.pipelineId !== filter.pipelineId)
119
+ continue;
120
+ if (statusSet && !statusSet.has(record.status))
121
+ continue;
122
+ if (kindSet && !kindSet.has(record.kind))
123
+ continue;
124
+ if (nodeIdSet && (!record.nodeId || !nodeIdSet.has(record.nodeId)))
125
+ continue;
126
+ if (filter.batchRunId && record.batchRunId !== filter.batchRunId)
127
+ continue;
128
+ if (filter.runId && record.runId !== filter.runId)
129
+ continue;
130
+ if (dateFrom && record.dateBucket < dateFrom)
131
+ continue;
132
+ if (dateTo && record.dateBucket > dateTo)
133
+ continue;
134
+ matched.push(record);
135
+ }
136
+ // 按 updatedAt DESC, artifactId ASC 排序
137
+ matched.sort((a, b) => {
138
+ const dateDiff = Date.parse(b.updatedAt) - Date.parse(a.updatedAt);
139
+ if (dateDiff !== 0)
140
+ return dateDiff;
141
+ return a.artifactId.localeCompare(b.artifactId);
142
+ });
143
+ const total = matched.length;
144
+ // cursor 分页:v2 cursor 含 indexUpdatedAt,索引重建后旧 cursor 自动从头开始
145
+ let startIndex = 0;
146
+ const currentIndexUpdatedAt = await getIndexUpdatedAt(artifactDir);
147
+ if (filter.cursor) {
148
+ const decoded = decodeAnyCursor(filter.cursor);
149
+ if (decoded) {
150
+ // 索引代际变化 → cursor 过期,从头开始
151
+ if (decoded.indexUpdatedAt && decoded.indexUpdatedAt !== currentIndexUpdatedAt) {
152
+ startIndex = 0;
153
+ }
154
+ else {
155
+ const cursorPos = matched.findIndex((rec) => rec.updatedAt === decoded.updatedAt && rec.artifactId === decoded.artifactId);
156
+ if (cursorPos >= 0)
157
+ startIndex = cursorPos + 1;
158
+ }
159
+ }
160
+ }
161
+ const page = matched.slice(startIndex, startIndex + limit);
162
+ const nextCursor = page.length === limit && startIndex + limit < total
163
+ ? encodeCursorV2({ v: 2, updatedAt: page[page.length - 1].updatedAt, artifactId: page[page.length - 1].artifactId, indexUpdatedAt: currentIndexUpdatedAt })
164
+ : null;
165
+ return { items: page, nextCursor, total };
166
+ };
167
+ exports.listIndexRecords = listIndexRecords;
168
+ /** 从 StoredArtifactIndexRecord 转换为 StoredArtifactItem。 */
169
+ const toStoredArtifactItem = (record, pipelineId, pipelineTitle) => ({
170
+ pipelineId,
171
+ pipelineTitle,
172
+ status: record.status,
173
+ dateBucket: record.dateBucket,
174
+ runId: record.runId,
175
+ nodeId: record.nodeId,
176
+ relativePath: record.relativePath,
177
+ fileName: (0, node_path_1.basename)(record.relativePath),
178
+ sizeBytes: record.sizeBytes,
179
+ updatedAt: record.updatedAt,
180
+ artifactId: record.artifactId,
181
+ });
182
+ exports.toStoredArtifactItem = toStoredArtifactItem;
183
+ /**
184
+ * 扫描一条流水线的产物目录,重建 index.jsonl。
185
+ */
186
+ const rebuildArtifactIndex = async (definition, scan) => {
187
+ const warnings = [];
188
+ let indexed = 0;
189
+ let skipped = 0;
190
+ const indexPath = (0, exports.getIndexPath)(definition.artifactDir);
191
+ try {
192
+ const items = await scan(definition);
193
+ items.sort((a, b) => Date.parse(b.updatedAt) - Date.parse(a.updatedAt));
194
+ const tmpPath = `${indexPath}.tmp-${Date.now()}`;
195
+ const stream = [];
196
+ for (const item of items) {
197
+ const record = await enrichItemToIndexRecord(item, definition);
198
+ if (record) {
199
+ stream.push(JSON.stringify(record));
200
+ indexed += 1;
201
+ }
202
+ else {
203
+ skipped += 1;
204
+ }
205
+ }
206
+ await (0, promises_1.writeFile)(tmpPath, `${stream.join("\n")}\n`, "utf8");
207
+ await (0, promises_1.rename)(tmpPath, indexPath);
208
+ }
209
+ catch (error) {
210
+ warnings.push(`重建索引失败 (pipeline ${definition.id}): ${error instanceof Error ? error.message : String(error)}`);
211
+ }
212
+ return { indexed, skipped, warnings };
213
+ };
214
+ exports.rebuildArtifactIndex = rebuildArtifactIndex;
215
+ /** 从 StoredArtifactItem + 读取文件内容 补充为完整的 IndexRecord。 */
216
+ const enrichItemToIndexRecord = async (item, definition) => {
217
+ const filePath = (0, node_path_1.resolve)(definition.artifactDir, item.relativePath);
218
+ let parsed = null;
219
+ try {
220
+ const raw = await (0, promises_1.readFile)(filePath, "utf8");
221
+ parsed = JSON.parse(raw);
222
+ }
223
+ catch {
224
+ // 无法读取时用路径启发式信息
225
+ }
226
+ const artifactObj = parsed && typeof parsed.artifact === "object" && parsed.artifact !== null
227
+ ? parsed.artifact
228
+ : null;
229
+ const kind = typeof parsed?.kind === "string"
230
+ ? parsed.kind
231
+ : parsed && "envelope" in parsed
232
+ ? "envelope"
233
+ : item.relativePath.includes("/envelopes/")
234
+ ? "envelope"
235
+ : item.fileName.includes("-adapter-output")
236
+ ? "adapter"
237
+ : item.fileName.includes("-group-output")
238
+ ? "group"
239
+ : "artifact";
240
+ return {
241
+ schemaVersion: 1,
242
+ artifactId: typeof parsed?.artifactId === "string" ? parsed.artifactId : `legacy:${item.pipelineId}:${item.relativePath}`,
243
+ pipelineId: item.pipelineId,
244
+ status: item.status,
245
+ kind,
246
+ dateBucket: item.dateBucket,
247
+ runId: item.runId,
248
+ batchRunId: null,
249
+ nodeId: item.nodeId ?? (typeof parsed?.nodeId === "string" ? parsed.nodeId : null),
250
+ groupId: typeof parsed?.groupId === "string" ? parsed.groupId : null,
251
+ itemKey: typeof parsed?.itemKey === "string" ? parsed.itemKey : null,
252
+ requestId: typeof parsed?.requestId === "string" ? parsed.requestId : null,
253
+ type: typeof artifactObj?.type === "string" ? artifactObj.type : "unknown",
254
+ artifactSchemaVersion: typeof artifactObj?.schemaVersion === "number" ? artifactObj.schemaVersion : 1,
255
+ name: typeof artifactObj?.name === "string" ? artifactObj.name : item.fileName,
256
+ relativePath: item.relativePath,
257
+ sizeBytes: item.sizeBytes,
258
+ hash: "",
259
+ createdAt: item.updatedAt,
260
+ updatedAt: item.updatedAt,
261
+ };
262
+ };
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rebuildArtifactIndexIncremental = void 0;
4
+ const promises_1 = require("node:fs/promises");
5
+ const node_path_1 = require("node:path");
6
+ const artifact_index_1 = require("./artifact-index");
7
+ /**
8
+ * 增量重建产物索引 — 扫描磁盘目录,仅对缺失索引的文件补充记录。
9
+ * 已有索引记录的文件(按 relativePath 匹配)不会重复添加。
10
+ */
11
+ const rebuildArtifactIndexIncremental = async (rootDir, pipelineId) => {
12
+ const warnings = [];
13
+ let indexed = 0;
14
+ let skipped = 0;
15
+ // 收集所有目标 pipeline 的 artifact 目录
16
+ const dirs = [];
17
+ // 扫描 rootDir 下所有 pipeline 目录
18
+ try {
19
+ const entries = await (0, promises_1.readdir)(rootDir, { withFileTypes: true });
20
+ for (const entry of entries) {
21
+ if (!entry.isDirectory())
22
+ continue;
23
+ const pipelineDir = (0, node_path_1.resolve)(rootDir, entry.name);
24
+ const artifactsPath = (0, node_path_1.resolve)(pipelineDir, "artifacts");
25
+ try {
26
+ const artStat = await (0, promises_1.stat)(artifactsPath);
27
+ if (artStat.isDirectory()) {
28
+ if (!pipelineId || entry.name === pipelineId) {
29
+ dirs.push({ pipelineId: entry.name, artifactDir: artifactsPath });
30
+ }
31
+ }
32
+ }
33
+ catch {
34
+ // no artifacts dir
35
+ }
36
+ }
37
+ }
38
+ catch {
39
+ warnings.push(`无法扫描产物根目录: ${rootDir}`);
40
+ }
41
+ for (const dir of dirs) {
42
+ const existingRecords = await (0, artifact_index_1.readIndexRecords)(dir.artifactDir);
43
+ const indexedPaths = new Set(existingRecords.map((r) => r.relativePath));
44
+ // 递归扫描所有 artifact 文件
45
+ const scanDir = async (subPath) => {
46
+ let entries;
47
+ try {
48
+ entries = await (0, promises_1.readdir)(subPath, { withFileTypes: true });
49
+ }
50
+ catch {
51
+ return;
52
+ }
53
+ for (const entry of entries) {
54
+ const fullPath = (0, node_path_1.resolve)(subPath, entry.name);
55
+ if (entry.isDirectory()) {
56
+ await scanDir(fullPath);
57
+ }
58
+ else if (entry.isFile() && entry.name.endsWith(".json") && !entry.name.startsWith(".tmp-")) {
59
+ const relPath = fullPath.slice(dir.artifactDir.length + 1).replace(/\\/g, "/");
60
+ if (indexedPaths.has(relPath)) {
61
+ skipped += 1;
62
+ continue;
63
+ }
64
+ try {
65
+ const fileStat = await (0, promises_1.stat)(fullPath);
66
+ // 构建基础索引记录
67
+ const record = {
68
+ schemaVersion: 1,
69
+ artifactId: `rebuild:${dir.pipelineId}:${relPath}`,
70
+ pipelineId: dir.pipelineId,
71
+ status: "unknown",
72
+ kind: relPath.includes("/envelopes/") ? "envelope" : "artifact",
73
+ dateBucket: "",
74
+ runId: null,
75
+ batchRunId: null,
76
+ nodeId: null,
77
+ groupId: null,
78
+ itemKey: null,
79
+ requestId: null,
80
+ type: "unknown",
81
+ artifactSchemaVersion: 1,
82
+ name: (0, node_path_1.basename)(fullPath),
83
+ relativePath: relPath,
84
+ sizeBytes: fileStat.size,
85
+ hash: "",
86
+ createdAt: fileStat.mtime.toISOString(),
87
+ updatedAt: fileStat.mtime.toISOString(),
88
+ };
89
+ const result = await (0, artifact_index_1.appendIndexRecord)(dir.artifactDir, record);
90
+ if (result.ok) {
91
+ indexed += 1;
92
+ }
93
+ else {
94
+ warnings.push(result.warning.message);
95
+ skipped += 1;
96
+ }
97
+ }
98
+ catch {
99
+ skipped += 1;
100
+ }
101
+ }
102
+ }
103
+ };
104
+ const statusDirs = ["success", "failed", "rejected"];
105
+ for (const statusDir of statusDirs) {
106
+ const statusPath = (0, node_path_1.resolve)(dir.artifactDir, statusDir);
107
+ try {
108
+ const s = await (0, promises_1.stat)(statusPath);
109
+ if (s.isDirectory()) {
110
+ await scanDir(statusPath);
111
+ }
112
+ }
113
+ catch {
114
+ // dir doesn't exist
115
+ }
116
+ }
117
+ }
118
+ return { indexed, skipped, warnings };
119
+ };
120
+ exports.rebuildArtifactIndexIncremental = rebuildArtifactIndexIncremental;