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,371 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.exportStoredArtifactContents = exports.readStoredArtifactContent = exports.listStoredArtifacts = exports.scanStoredArtifacts = 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
+ const DATE_BUCKET_RE = /^\d{4}-\d{2}-\d{2}$/;
8
+ const NUMERIC_TOKEN_RE = /^\d+$/;
9
+ const toUnixPath = (value) => value.replaceAll("\\", "/");
10
+ const formatDateBucketFromIso = (iso) => {
11
+ const ms = Date.parse(iso);
12
+ if (!Number.isFinite(ms))
13
+ return "unknown";
14
+ const d = new Date(ms);
15
+ const yyyy = d.getFullYear();
16
+ const mm = String(d.getMonth() + 1).padStart(2, "0");
17
+ const dd = String(d.getDate()).padStart(2, "0");
18
+ return `${yyyy}-${mm}-${dd}`;
19
+ };
20
+ const normalizeDateBucketParam = (value) => {
21
+ if (!value)
22
+ return null;
23
+ const normalized = value.trim();
24
+ return DATE_BUCKET_RE.test(normalized) ? normalized : null;
25
+ };
26
+ const toRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
27
+ const parseNodeIdFromFileName = (fileName, runId, relativePath) => {
28
+ if (!runId?.trim())
29
+ return null;
30
+ const normalizedName = fileName.trim();
31
+ if (!normalizedName.toLowerCase().endsWith(".json"))
32
+ return null;
33
+ const nameWithoutExt = normalizedName.slice(0, -".json".length);
34
+ const runPrefix = `${runId}-`;
35
+ if (!nameWithoutExt.startsWith(runPrefix))
36
+ return null;
37
+ const tail = nameWithoutExt.slice(runPrefix.length);
38
+ if (!tail)
39
+ return null;
40
+ const normalizedPath = toUnixPath(relativePath).toLowerCase();
41
+ if (normalizedPath.includes("/envelopes/")) {
42
+ // envelope 文件名格式: <runId>-<nodeId>-node-<nodeId>-<uuid>-envelope.json
43
+ const marker = "-node-";
44
+ const markerIndex = tail.indexOf(marker);
45
+ if (markerIndex > 0) {
46
+ const nodeId = tail.slice(0, markerIndex).trim();
47
+ return nodeId || null;
48
+ }
49
+ return null;
50
+ }
51
+ // group/adpater 聚合文件格式不稳定(含 itemKey),这里不参与节点筛选,避免误判。
52
+ if (tail.endsWith("-adapter-output") || tail.endsWith("-group-output"))
53
+ return null;
54
+ const tokens = tail.split("-").filter(Boolean);
55
+ if (tokens.length < 3)
56
+ return null;
57
+ // 结构化节点产物命名格式: <runId>-<nodeId>-<artifactIndex>-<safeType>.json
58
+ let artifactIndexPos = -1;
59
+ for (let i = tokens.length - 1; i >= 0; i -= 1) {
60
+ if (NUMERIC_TOKEN_RE.test(tokens[i])) {
61
+ artifactIndexPos = i;
62
+ break;
63
+ }
64
+ }
65
+ if (artifactIndexPos <= 0 || artifactIndexPos >= tokens.length - 1)
66
+ return null;
67
+ const nodeId = tokens.slice(0, artifactIndexPos).join("-").trim();
68
+ return nodeId || null;
69
+ };
70
+ const walkFiles = async (rootDir) => {
71
+ const out = [];
72
+ const queue = [rootDir];
73
+ while (queue.length > 0) {
74
+ const current = queue.shift();
75
+ if (!current)
76
+ continue;
77
+ let entries;
78
+ try {
79
+ entries = await (0, promises_1.readdir)(current, { withFileTypes: true });
80
+ }
81
+ catch {
82
+ continue;
83
+ }
84
+ for (const entry of entries) {
85
+ const nextPath = `${current}${node_path_1.sep}${entry.name}`;
86
+ if (entry.isDirectory()) {
87
+ queue.push(nextPath);
88
+ continue;
89
+ }
90
+ if (entry.isFile())
91
+ out.push(nextPath);
92
+ }
93
+ }
94
+ return out;
95
+ };
96
+ const parseArtifactPathMeta = (artifactRootAbs, filePathAbs, updatedAtIso) => {
97
+ const rawRelative = toUnixPath(filePathAbs.slice(artifactRootAbs.length).replace(/^[/\\]+/, ""));
98
+ const segments = rawRelative.split("/").filter(Boolean);
99
+ const statusRaw = segments[0] ?? "unknown";
100
+ const status = statusRaw === "success" || statusRaw === "failed" || statusRaw === "rejected" ? statusRaw : "unknown";
101
+ const dateRaw = segments[1] ?? "";
102
+ const dateBucket = DATE_BUCKET_RE.test(dateRaw) ? dateRaw : formatDateBucketFromIso(updatedAtIso);
103
+ let runId = null;
104
+ if (segments[2]?.startsWith("batch-")) {
105
+ // 批跑产物路径: status/date/batchRunId/runId/...
106
+ runId = segments[3]?.startsWith("run-") ? segments[3] : null;
107
+ }
108
+ else {
109
+ runId = segments[2]?.startsWith("run-") ? segments[2] : null;
110
+ }
111
+ return { status, dateBucket, runId, relativePath: rawRelative };
112
+ };
113
+ const shouldIncludeArtifactFile = (relativePath) => {
114
+ const normalized = toUnixPath(relativePath).toLowerCase();
115
+ if (!normalized.endsWith(".json"))
116
+ return false;
117
+ return true;
118
+ };
119
+ /** 文件系统扫描(降级路径 / 索引重建用)。 */
120
+ const scanStoredArtifacts = async (definitions, options) => {
121
+ const pipelineIdFilter = options?.pipelineIds?.length ? new Set(options.pipelineIds) : null;
122
+ const nodeIdFilter = options?.nodeIds?.length ? new Set(options.nodeIds) : null;
123
+ const statusFilter = options?.statuses?.length ? new Set(options.statuses) : null;
124
+ const kindFilter = options?.kinds?.length ? new Set(options.kinds) : null;
125
+ const batchRunIdFilter = options?.batchRunId ?? null;
126
+ const runIdFilter = options?.runId ?? null;
127
+ const dateFrom = normalizeDateBucketParam(options?.dateFrom ?? null);
128
+ const dateTo = normalizeDateBucketParam(options?.dateTo ?? null);
129
+ const out = [];
130
+ for (const definition of definitions) {
131
+ if (pipelineIdFilter && !pipelineIdFilter.has(definition.id))
132
+ continue;
133
+ const artifactRootAbs = (0, node_path_1.resolve)(definition.artifactDir);
134
+ const files = await walkFiles(artifactRootAbs);
135
+ for (const filePath of files) {
136
+ const fileStat = await (0, promises_1.stat)(filePath).catch(() => null);
137
+ if (!fileStat || !fileStat.isFile())
138
+ continue;
139
+ const updatedAt = fileStat.mtime.toISOString();
140
+ const meta = parseArtifactPathMeta(artifactRootAbs, (0, node_path_1.resolve)(filePath), updatedAt);
141
+ if (!shouldIncludeArtifactFile(meta.relativePath))
142
+ continue;
143
+ const fileName = (0, node_path_1.basename)(filePath);
144
+ const effectiveRunId = meta.runId ?? (meta.status === "rejected"
145
+ ? (() => { const m = fileName.match(/^(run-.+?)(?:-n\d|-rejected-by)/); return m ? m[1] : null; })()
146
+ : null);
147
+ if (!effectiveRunId && meta.status !== "rejected")
148
+ continue;
149
+ if (statusFilter && !statusFilter.has(meta.status))
150
+ continue;
151
+ if (runIdFilter && effectiveRunId !== runIdFilter)
152
+ continue;
153
+ // batchRunId 扫描过滤:路径含 batch-xxx 段
154
+ if (batchRunIdFilter && !meta.relativePath.includes(`/${batchRunIdFilter}/`))
155
+ continue;
156
+ const nodeId = parseNodeIdFromFileName(fileName, effectiveRunId, meta.relativePath);
157
+ // kind 扫描过滤:通过文件名后缀和路径判断
158
+ if (kindFilter) {
159
+ const scanKind = meta.relativePath.includes("/envelopes/") ? "envelope"
160
+ : fileName.includes("-adapter-output") ? "adapter"
161
+ : fileName.includes("-group-output") ? "group"
162
+ : "artifact";
163
+ if (!kindFilter.has(scanKind))
164
+ continue;
165
+ }
166
+ if (nodeIdFilter && (!nodeId || !nodeIdFilter.has(nodeId)))
167
+ continue;
168
+ if (dateFrom !== null || dateTo !== null) {
169
+ const rowDateBucket = DATE_BUCKET_RE.test(meta.dateBucket) ? meta.dateBucket : formatDateBucketFromIso(updatedAt);
170
+ if (dateFrom !== null && rowDateBucket < dateFrom)
171
+ continue;
172
+ if (dateTo !== null && rowDateBucket > dateTo)
173
+ continue;
174
+ }
175
+ out.push({
176
+ pipelineId: definition.id,
177
+ pipelineTitle: definition.title,
178
+ status: meta.status,
179
+ dateBucket: meta.dateBucket,
180
+ runId: effectiveRunId,
181
+ nodeId,
182
+ relativePath: meta.relativePath,
183
+ fileName,
184
+ sizeBytes: fileStat.size,
185
+ updatedAt,
186
+ });
187
+ }
188
+ }
189
+ return out.sort((a, b) => Date.parse(b.updatedAt) - Date.parse(a.updatedAt));
190
+ };
191
+ exports.scanStoredArtifacts = scanStoredArtifacts;
192
+ const encodeListCursor = (updatedAt, runId, fileName) => Buffer.from(`${updatedAt}|${runId ?? "unknown"}|${fileName}`).toString("base64url");
193
+ const decodeListCursor = (cursor) => {
194
+ try {
195
+ const raw = Buffer.from(cursor, "base64url").toString("utf8");
196
+ const idx1 = raw.indexOf("|");
197
+ const idx2 = raw.lastIndexOf("|");
198
+ if (idx1 < 0 || idx2 <= idx1)
199
+ return null;
200
+ return { updatedAt: raw.slice(0, idx1), runId: raw.slice(idx1 + 1, idx2), fileName: raw.slice(idx2 + 1) };
201
+ }
202
+ catch {
203
+ return null;
204
+ }
205
+ };
206
+ /** 优先读索引,索引缺失时降级为文件系统扫描。支持 cursor 分页。 */
207
+ const listStoredArtifacts = async (definitions, options) => {
208
+ const pipelineIdFilter = options?.pipelineIds?.length ? new Set(options.pipelineIds) : null;
209
+ const limit = Number.isFinite(options?.limit) ? Math.max(1, Math.min(5000, Math.trunc(options?.limit))) : 100;
210
+ // 尝试从索引读取(不传 cursor,由外层统一分页)
211
+ const indexItems = [];
212
+ let indexAvailable = false;
213
+ for (const definition of definitions) {
214
+ if (pipelineIdFilter && !pipelineIdFilter.has(definition.id))
215
+ continue;
216
+ const filter = {
217
+ pipelineId: definition.id,
218
+ statuses: options?.statuses,
219
+ kinds: options?.kinds,
220
+ nodeIds: options?.nodeIds,
221
+ dateFrom: options?.dateFrom,
222
+ dateTo: options?.dateTo,
223
+ batchRunId: options?.batchRunId,
224
+ runId: options?.runId,
225
+ limit: 0, // 0 = 无限制,全量读出后统一排序分页
226
+ };
227
+ const result = await (0, artifact_index_1.listIndexRecords)(definition.artifactDir, filter);
228
+ if (result.total > 0 || result.items.length > 0) {
229
+ indexAvailable = true;
230
+ for (const record of result.items) {
231
+ indexItems.push((0, artifact_index_1.toStoredArtifactItem)(record, definition.id, definition.title));
232
+ }
233
+ }
234
+ }
235
+ // 统一的排序与 cursor 分页(index 和 scan 使用相同编码)
236
+ const sortItems = (items) => items.sort((a, b) => Date.parse(b.updatedAt) - Date.parse(a.updatedAt));
237
+ const paginateWithCursor = (items, source) => {
238
+ let startIndex = 0;
239
+ if (options?.cursor) {
240
+ const decoded = decodeListCursor(options.cursor);
241
+ if (decoded) {
242
+ const pos = items.findIndex((item) => item.updatedAt === decoded.updatedAt &&
243
+ (item.runId ?? "unknown") === decoded.runId &&
244
+ item.fileName === decoded.fileName);
245
+ if (pos >= 0)
246
+ startIndex = pos + 1;
247
+ }
248
+ }
249
+ const page = items.slice(startIndex, startIndex + limit);
250
+ const nextCursor = page.length === limit && startIndex + limit < items.length
251
+ ? encodeListCursor(page[page.length - 1].updatedAt, page[page.length - 1].runId, page[page.length - 1].fileName)
252
+ : null;
253
+ return { items: page, nextCursor, source };
254
+ };
255
+ if (indexAvailable) {
256
+ return paginateWithCursor(sortItems(indexItems), "index");
257
+ }
258
+ // 降级:文件系统扫描
259
+ const scanItems = await (0, exports.scanStoredArtifacts)(definitions, {
260
+ pipelineIds: options?.pipelineIds,
261
+ nodeIds: options?.nodeIds,
262
+ dateFrom: options?.dateFrom,
263
+ dateTo: options?.dateTo,
264
+ statuses: options?.statuses,
265
+ kinds: options?.kinds,
266
+ batchRunId: options?.batchRunId,
267
+ runId: options?.runId,
268
+ });
269
+ return paginateWithCursor(sortItems(scanItems), "scan");
270
+ };
271
+ exports.listStoredArtifacts = listStoredArtifacts;
272
+ const resolveArtifactFilePath = (definition, relativePath) => {
273
+ const rootAbs = (0, node_path_1.resolve)(definition.artifactDir);
274
+ const targetAbs = (0, node_path_1.resolve)(rootAbs, relativePath);
275
+ // 防止路径穿越,只允许读取当前流水线产物目录下文件。
276
+ if (targetAbs !== rootAbs && !targetAbs.startsWith(`${rootAbs}${node_path_1.sep}`))
277
+ return null;
278
+ return targetAbs;
279
+ };
280
+ const readStoredArtifactContent = async (definition, relativePath) => {
281
+ const targetAbs = resolveArtifactFilePath(definition, relativePath);
282
+ if (!targetAbs)
283
+ return null;
284
+ const fileStat = await (0, promises_1.stat)(targetAbs).catch(() => null);
285
+ if (!fileStat || !fileStat.isFile())
286
+ return null;
287
+ const rawText = await (0, promises_1.readFile)(targetAbs, "utf8");
288
+ let parsed = null;
289
+ try {
290
+ parsed = JSON.parse(rawText);
291
+ }
292
+ catch {
293
+ parsed = null;
294
+ }
295
+ const parsedObj = toRecord(parsed);
296
+ // 优先按 kind 字段分发,无 kind 时回退到旧启发式判断(向后兼容)
297
+ const kind = typeof parsedObj?.kind === "string" ? parsedObj.kind : null;
298
+ const isEnvelope = kind === "envelope" || (!kind && parsedObj && "envelope" in parsedObj);
299
+ const envelopeObj = isEnvelope ? toRecord(parsedObj?.envelope) : null;
300
+ const artifactObj = !isEnvelope && parsedObj && "artifact" in parsedObj ? toRecord(parsedObj.artifact) : null;
301
+ const content = (() => {
302
+ if (artifactObj)
303
+ return artifactObj.content ?? parsed ?? rawText;
304
+ if (envelopeObj) {
305
+ const artifacts = Array.isArray(envelopeObj.artifacts) ? envelopeObj.artifacts : [];
306
+ const contents = artifacts
307
+ .map((item) => toRecord(item)?.content)
308
+ .filter((item) => item !== undefined);
309
+ const logs = Array.isArray(envelopeObj.logs) ? envelopeObj.logs : [];
310
+ // envelope 预览返回 contents + logs,便于排错。
311
+ return {
312
+ contents,
313
+ logs,
314
+ };
315
+ }
316
+ return parsed ?? rawText;
317
+ })();
318
+ const meta = artifactObj ? toRecord(artifactObj.meta) : null;
319
+ return {
320
+ rawText,
321
+ parsed,
322
+ content,
323
+ meta,
324
+ };
325
+ };
326
+ exports.readStoredArtifactContent = readStoredArtifactContent;
327
+ const exportStoredArtifactContents = async (definitions, options) => {
328
+ // 优先从索引选候选文件,减少全量 scan;索引缺失时 listStoredArtifacts 自动降级 scan
329
+ const limit = Number.isFinite(options?.limit) ? Math.max(1, Math.trunc(options?.limit)) : 20000;
330
+ const listResult = await (0, exports.listStoredArtifacts)(definitions, {
331
+ pipelineIds: options?.pipelineIds,
332
+ nodeIds: options?.nodeIds,
333
+ dateFrom: options?.dateFrom,
334
+ dateTo: options?.dateTo,
335
+ statuses: options?.statuses,
336
+ kinds: options?.kinds,
337
+ batchRunId: options?.batchRunId,
338
+ runId: options?.runId,
339
+ limit,
340
+ });
341
+ const items = listResult.items;
342
+ const definitionById = new Map(definitions.map((definition) => [definition.id, definition]));
343
+ const out = {};
344
+ for (const item of items.slice(0, limit)) {
345
+ const definition = definitionById.get(item.pipelineId);
346
+ if (!definition)
347
+ continue;
348
+ const content = await (0, exports.readStoredArtifactContent)(definition, item.relativePath);
349
+ if (!content)
350
+ continue;
351
+ const dateKey = item.dateBucket || "unknown";
352
+ const pipelineKey = item.pipelineId || "unknown";
353
+ const nodeKey = item.nodeId?.trim() || "unknown";
354
+ if (!out[dateKey])
355
+ out[dateKey] = {};
356
+ if (!out[dateKey][pipelineKey])
357
+ out[dateKey][pipelineKey] = {};
358
+ if (!out[dateKey][pipelineKey][nodeKey])
359
+ out[dateKey][pipelineKey][nodeKey] = [];
360
+ // 导出只保留产物内容,不包含 runId、文件名等元信息。
361
+ // 若 content 本身是数组,则展开写入,避免导出结果出现“数组套数组”。
362
+ if (Array.isArray(content.content)) {
363
+ out[dateKey][pipelineKey][nodeKey].push(...content.content);
364
+ }
365
+ else {
366
+ out[dateKey][pipelineKey][nodeKey].push(content.content);
367
+ }
368
+ }
369
+ return out;
370
+ };
371
+ exports.exportStoredArtifactContents = exportStoredArtifactContents;
@@ -0,0 +1,226 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createMainCliBootstrap = void 0;
4
+ const create_app_context_1 = require("../app/create-app-context");
5
+ const user_config_1 = require("../app/user-config");
6
+ const server_runtime_client_1 = require("./server-runtime-client");
7
+ const errors_1 = require("./errors");
8
+ const normalizePipelineSelector = (selector) => {
9
+ if (typeof selector === "string") {
10
+ return { pipelineId: selector.trim() || undefined };
11
+ }
12
+ return {
13
+ pipelineId: typeof selector.pipelineId === "string" && selector.pipelineId.trim() ? selector.pipelineId.trim() : undefined,
14
+ runId: typeof selector.runId === "string" && selector.runId.trim() ? selector.runId.trim() : undefined,
15
+ batchRunId: typeof selector.batchRunId === "string" && selector.batchRunId.trim() ? selector.batchRunId.trim() : undefined,
16
+ };
17
+ };
18
+ const requirePipelineId = (selector) => {
19
+ if (selector.pipelineId)
20
+ return selector.pipelineId;
21
+ // 内嵌 runtime service 只能通过 pipelineId 定位 pipeline 实例;runId/batchRunId 在此层仅用于二次匹配。
22
+ throw new errors_1.CliError("Missing pipelineId for local runtime selector", {
23
+ code: "INVALID_ARGUMENT",
24
+ exitCode: 2,
25
+ details: {
26
+ runId: selector.runId ?? null,
27
+ batchRunId: selector.batchRunId ?? null,
28
+ },
29
+ });
30
+ };
31
+ const buildCliAppContext = (appContext) => {
32
+ const { readonly: readonlyServices, writable: writableServices } = appContext.services;
33
+ const serverLifecycleClient = (0, server_runtime_client_1.createServerLifecycleClient)();
34
+ return {
35
+ systemService: {
36
+ getSnapshot: async () => readonlyServices.system.getSnapshot(),
37
+ },
38
+ serverService: {
39
+ ensureServerReady: async () => serverLifecycleClient.ensureServerReady(),
40
+ startServer: async () => serverLifecycleClient.startServer(),
41
+ getServerStatus: async () => serverLifecycleClient.getServerStatus(),
42
+ stopServer: async () => serverLifecycleClient.stopServer(),
43
+ },
44
+ pipelineService: {
45
+ listPipelines: async () => readonlyServices.pipeline.listPipelines(),
46
+ getPipelineById: async (pipelineId) => readonlyServices.pipeline.getPipeline(pipelineId),
47
+ startPipeline: async (pipelineId) => writableServices.pipeline.startPipeline(pipelineId),
48
+ getPipelineStatus: async (selector) => {
49
+ const normalized = normalizePipelineSelector(selector);
50
+ const pipelineId = requirePipelineId(normalized);
51
+ return writableServices.pipeline.getPipelineExecutionStatus(pipelineId, {
52
+ runId: normalized.runId,
53
+ batchRunId: normalized.batchRunId,
54
+ });
55
+ },
56
+ stopPipeline: async (selector) => {
57
+ const normalized = normalizePipelineSelector(selector);
58
+ const pipelineId = requirePipelineId(normalized);
59
+ return writableServices.pipeline.stopPipeline(pipelineId, {
60
+ runId: normalized.runId,
61
+ batchRunId: normalized.batchRunId,
62
+ });
63
+ },
64
+ runPipeline: async (pipelineId) => writableServices.pipeline.runPipeline(pipelineId),
65
+ retryNode: async (input) => writableServices.pipeline.retryNode(input),
66
+ diagnoseNode: async (input) => {
67
+ const runtimeApiClient = (0, server_runtime_client_1.createPipelineRuntimeApiClient)();
68
+ return runtimeApiClient.diagnoseNode(input.pipelineId, input.nodeId, input.itemKey);
69
+ },
70
+ getOutput: async (pipelineId, runId) => writableServices.pipeline.getOutput(pipelineId, runId),
71
+ listOutputs: async (pipelineId) => writableServices.pipeline.listOutputs(pipelineId),
72
+ listLinks: async () => writableServices.pipeline.listLinks(),
73
+ getQueue: (pipelineId) => writableServices.pipeline.getQueue(pipelineId),
74
+ },
75
+ agentService: {
76
+ listAgents: async () => readonlyServices.agent.listAgents(),
77
+ listSessions: async () => readonlyServices.session.listSessions(),
78
+ filterSessionsByAgent: async (agentId) => {
79
+ const raw = await readonlyServices.session.listSessions();
80
+ const sessions = Array.isArray(raw) ? raw : [];
81
+ return sessions.filter((s) => {
82
+ const id = typeof s?.id === "string" ? s.id : "";
83
+ const parts = id.split(":");
84
+ return parts.length >= 3 && parts[0] === "agent" && parts[1] === agentId;
85
+ });
86
+ },
87
+ sendMessage: async (input) => writableServices.session.sendMessage(input),
88
+ getSessionHistory: async (sessionId) => readonlyServices.session.getSessionHistory(sessionId),
89
+ sendMessageAndWaitForReply: async (input, options) => writableServices.session.sendMessageAndWaitForReply(input, options),
90
+ },
91
+ sessionService: {
92
+ listSessions: async () => readonlyServices.session.listSessions(),
93
+ sendMessage: async (input) => writableServices.session.sendMessage(input),
94
+ },
95
+ artifactService: {
96
+ planCleanup: async (pipelineId, options) => readonlyServices.artifact.planCleanup(pipelineId, (options ?? {})),
97
+ executeCleanup: async (pipelineId, plan) => readonlyServices.artifact.executeCleanup(pipelineId, plan),
98
+ rebuildIndex: async (pipelineId) => readonlyServices.artifact.rebuildIndex(pipelineId),
99
+ listArtifacts: async (filter) => readonlyServices.artifact.listArtifacts({
100
+ pipelineIds: filter.pipelineId ? [filter.pipelineId] : undefined,
101
+ nodeIds: filter.nodeId ? [filter.nodeId] : undefined,
102
+ statuses: filter.status ? filter.status.split(",").map((s) => s.trim()).filter(Boolean) : undefined,
103
+ kinds: filter.kind ? filter.kind.split(",").map((k) => k.trim()).filter(Boolean) : undefined,
104
+ batchRunId: filter.batchRunId,
105
+ runId: filter.runId,
106
+ cursor: filter.cursor,
107
+ }),
108
+ getArtifactContent: async (filter) => readonlyServices.artifact.getArtifactContent({
109
+ pipelineId: filter.pipelineId,
110
+ relativePath: filter.relativePath,
111
+ }),
112
+ exportArtifacts: async (filter) => readonlyServices.artifact.exportArtifactContents({
113
+ pipelineIds: filter.pipelineId ? [filter.pipelineId] : undefined,
114
+ nodeIds: filter.nodeId ? [filter.nodeId] : undefined,
115
+ statuses: filter.status ? filter.status.split(",").map((s) => s.trim()).filter(Boolean) : undefined,
116
+ kinds: filter.kind ? filter.kind.split(",").map((k) => k.trim()).filter(Boolean) : undefined,
117
+ batchRunId: filter.batchRunId,
118
+ dateFrom: filter.dateFrom,
119
+ dateTo: filter.dateTo,
120
+ limit: filter.limit,
121
+ }),
122
+ },
123
+ schedulerService: {
124
+ toggleScheduler: async (pipelineId, enabled) => writableServices.scheduler.toggleScheduler(pipelineId, enabled),
125
+ setSchedulerMode: async (pipelineId, mode) => writableServices.scheduler.setSchedulerMode(pipelineId, mode),
126
+ },
127
+ };
128
+ };
129
+ const buildRuntimeApiOnlyContext = () => {
130
+ const runtimeApiClient = (0, server_runtime_client_1.createPipelineRuntimeApiClient)();
131
+ const serverLifecycleClient = (0, server_runtime_client_1.createServerLifecycleClient)();
132
+ const unsupported = async () => {
133
+ throw new Error("unsupported_cli_runtime_api_context");
134
+ };
135
+ return {
136
+ systemService: {
137
+ getSnapshot: unsupported,
138
+ },
139
+ serverService: {
140
+ ensureServerReady: async () => serverLifecycleClient.ensureServerReady(),
141
+ startServer: async () => serverLifecycleClient.startServer(),
142
+ getServerStatus: async () => serverLifecycleClient.getServerStatus(),
143
+ stopServer: async () => serverLifecycleClient.stopServer(),
144
+ },
145
+ pipelineService: {
146
+ listPipelines: unsupported,
147
+ getPipelineById: unsupported,
148
+ startPipeline: async (pipelineId) => runtimeApiClient.startPipeline(pipelineId),
149
+ getPipelineStatus: async (selector) => runtimeApiClient.getPipelineStatus(selector),
150
+ stopPipeline: async (selector) => runtimeApiClient.stopPipeline(selector),
151
+ waitForPipelineWatchSignal: async (selector, timeoutMs) => runtimeApiClient.waitForPipelineWatchSignal(selector, timeoutMs),
152
+ runPipeline: async (pipelineId) => runtimeApiClient.startPipeline(pipelineId),
153
+ retryNode: unsupported,
154
+ diagnoseNode: async (input) => runtimeApiClient.diagnoseNode(input.pipelineId, input.nodeId, input.itemKey),
155
+ getOutput: async (pipelineId, runId) => runtimeApiClient.getOutput(pipelineId, runId),
156
+ listOutputs: async (pipelineId) => runtimeApiClient.listOutputs(pipelineId),
157
+ listLinks: async () => runtimeApiClient.listLinks(),
158
+ getQueue: async (pipelineId) => runtimeApiClient.getQueue(pipelineId),
159
+ },
160
+ agentService: {
161
+ listAgents: unsupported,
162
+ listSessions: unsupported,
163
+ filterSessionsByAgent: unsupported,
164
+ sendMessage: unsupported,
165
+ getSessionHistory: unsupported,
166
+ sendMessageAndWaitForReply: unsupported,
167
+ },
168
+ sessionService: {
169
+ listSessions: unsupported,
170
+ sendMessage: unsupported,
171
+ },
172
+ artifactService: {
173
+ listArtifacts: unsupported,
174
+ getArtifactContent: unsupported,
175
+ exportArtifacts: unsupported,
176
+ planCleanup: unsupported,
177
+ executeCleanup: unsupported,
178
+ rebuildIndex: unsupported,
179
+ },
180
+ schedulerService: {
181
+ toggleScheduler: unsupported,
182
+ setSchedulerMode: unsupported,
183
+ },
184
+ };
185
+ };
186
+ const createMainCliBootstrap = () => {
187
+ return async ({ route }) => {
188
+ const bootstrap = route.bootstrap;
189
+ if (bootstrap?.runtimeApiOnly) {
190
+ // runtime-api only 路由必须复用 daemon 提供的 API 语义,避免误落到内嵌 service 路径。
191
+ if (bootstrap.ensureServerReady) {
192
+ const serverLifecycleClient = (0, server_runtime_client_1.createServerLifecycleClient)();
193
+ // 运行类命令必须先绑定到持久执行宿主,避免 CLI 临时进程误充当后台宿主。
194
+ await serverLifecycleClient.ensureServerReady();
195
+ }
196
+ return {
197
+ app: buildRuntimeApiOnlyContext(),
198
+ };
199
+ }
200
+ const gatewayConfig = await (0, user_config_1.resolveGatewayConfig)();
201
+ const appContext = (0, create_app_context_1.createAppContext)({
202
+ gatewayUrl: gatewayConfig.url ?? undefined,
203
+ gatewayToken: gatewayConfig.token ?? undefined,
204
+ });
205
+ await appContext.initialize();
206
+ if (bootstrap?.gateway === "required") {
207
+ await appContext.gateway.connect();
208
+ }
209
+ else if (bootstrap?.gateway === "warmup") {
210
+ // 系统快照尽量尝试建链,失败时退回当前缓存状态,避免只读命令被完全阻断。
211
+ try {
212
+ await appContext.gateway.connect();
213
+ }
214
+ catch {
215
+ // 保持只读命令可退化执行。
216
+ }
217
+ }
218
+ return {
219
+ app: buildCliAppContext(appContext),
220
+ dispose: () => {
221
+ appContext.dispose();
222
+ },
223
+ };
224
+ };
225
+ };
226
+ exports.createMainCliBootstrap = createMainCliBootstrap;