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,529 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createPipelineService = exports.matchPipelineIdentityTarget = exports.readPipelineIdentitySnapshot = exports.readNestedValueByPath = exports.extractKeywordPoolFromUnknown = exports.normalizePipelineRunIdentityTarget = void 0;
4
+ const pipeline_config_1 = require("../app/pipeline-config");
5
+ const execution_status_1 = require("../pipeline/execution-status");
6
+ const item_batch_controller_1 = require("../pipeline/item-batch-controller");
7
+ const pipeline_status_1 = require("./pipeline-status");
8
+ const getRuntimeByPipelineId = (app, pipelineId) => app.getPipelineRuntime(pipelineId);
9
+ const normalizeIdentityValue = (value) => typeof value === "string" && value.trim() ? value.trim() : null;
10
+ const normalizePipelineRunIdentityTarget = (target) => ({
11
+ runId: normalizeIdentityValue(target?.runId),
12
+ batchRunId: normalizeIdentityValue(target?.batchRunId),
13
+ });
14
+ exports.normalizePipelineRunIdentityTarget = normalizePipelineRunIdentityTarget;
15
+ const extractKeywordPoolFromUnknown = (value, depth = 0) => {
16
+ if (depth > 5)
17
+ return [];
18
+ const normalizedDirect = (0, item_batch_controller_1.normalizePoolItems)(value);
19
+ if (normalizedDirect.length > 0)
20
+ return normalizedDirect;
21
+ if (!value || typeof value !== "object" || Array.isArray(value))
22
+ return [];
23
+ const record = value;
24
+ // 远端关键词池优先按约定字段提取,兼容现有 list30/list/items 等结构。
25
+ const priorityKeys = ["list30", "list", "keywords", "items", "pool"];
26
+ for (const key of priorityKeys) {
27
+ const candidates = (0, exports.extractKeywordPoolFromUnknown)(record[key], depth + 1);
28
+ if (candidates.length > 0)
29
+ return candidates;
30
+ }
31
+ const mapValue = record.map;
32
+ if (mapValue && typeof mapValue === "object" && !Array.isArray(mapValue)) {
33
+ const mapCandidates = (0, item_batch_controller_1.normalizePoolItems)(Object.values(mapValue));
34
+ if (mapCandidates.length > 0)
35
+ return mapCandidates;
36
+ }
37
+ for (const nested of Object.values(record)) {
38
+ const candidates = (0, exports.extractKeywordPoolFromUnknown)(nested, depth + 1);
39
+ if (candidates.length > 0)
40
+ return candidates;
41
+ }
42
+ return [];
43
+ };
44
+ exports.extractKeywordPoolFromUnknown = extractKeywordPoolFromUnknown;
45
+ const readNestedValueByPath = (value, sourceField) => {
46
+ const normalizedPath = sourceField.trim();
47
+ if (!normalizedPath)
48
+ return null;
49
+ const segments = normalizedPath
50
+ .split(".")
51
+ .map((segment) => segment.trim())
52
+ .filter(Boolean);
53
+ if (segments.length === 0)
54
+ return null;
55
+ let current = value;
56
+ for (const segment of segments) {
57
+ if (!current || typeof current !== "object" || Array.isArray(current))
58
+ return null;
59
+ current = current[segment];
60
+ }
61
+ return current;
62
+ };
63
+ exports.readNestedValueByPath = readNestedValueByPath;
64
+ const normalizeBatchSize = (value) => {
65
+ if (typeof value !== "number" || !Number.isFinite(value))
66
+ return undefined;
67
+ return Math.max(1, Math.trunc(value));
68
+ };
69
+ const normalizeStartIndex = (startIndex, startBatch, batchSize) => {
70
+ if (typeof startIndex === "number" && Number.isFinite(startIndex)) {
71
+ return Math.max(0, Math.trunc(startIndex));
72
+ }
73
+ if (typeof startBatch === "number" && Number.isFinite(startBatch) && typeof batchSize === "number") {
74
+ return Math.max(0, (Math.trunc(startBatch) - 1) * batchSize);
75
+ }
76
+ return undefined;
77
+ };
78
+ const buildBatchRunId = (pipelineId, snapshot) => {
79
+ if (typeof snapshot.batchRunId === "string" && snapshot.batchRunId.trim()) {
80
+ return snapshot.batchRunId.trim();
81
+ }
82
+ const startedAt = typeof snapshot.startedAt === "string" && snapshot.startedAt.trim() ? snapshot.startedAt.trim() : String(Date.now());
83
+ return `batch:${pipelineId}:${startedAt}`;
84
+ };
85
+ const markRunningRunStopped = (run) => {
86
+ const now = new Date().toISOString();
87
+ // 停止是用户主动中止,不应继续让 queued/blocked 节点保持可调度状态。
88
+ for (const node of run.nodes) {
89
+ if (node.status === "success" || node.status === "failed" || node.status === "rejected" || node.status === "skipped")
90
+ continue;
91
+ node.status = "stopped";
92
+ node.finishedAt = node.finishedAt ?? now;
93
+ node.lastError = node.lastError ?? "用户手动停止流水线";
94
+ }
95
+ for (const item of run.itemRuns ?? []) {
96
+ if (item.status === "success" || item.status === "failed" || item.status === "rejected" || item.status === "skipped")
97
+ continue;
98
+ item.status = "stopped";
99
+ item.finishedAt = item.finishedAt ?? now;
100
+ item.lastError = item.lastError ?? "用户手动停止流水线";
101
+ }
102
+ for (const group of run.groups ?? []) {
103
+ if (group.status === "success" || group.status === "failed" || group.status === "rejected" || group.status === "skipped")
104
+ continue;
105
+ group.status = "stopped";
106
+ group.finishedAt = group.finishedAt ?? now;
107
+ group.lastError = group.lastError ?? "用户手动停止流水线";
108
+ }
109
+ for (const groupItem of run.groupItemRuns ?? []) {
110
+ if (groupItem.status === "success" || groupItem.status === "failed" || groupItem.status === "rejected" || groupItem.status === "skipped")
111
+ continue;
112
+ groupItem.status = "stopped";
113
+ groupItem.finishedAt = groupItem.finishedAt ?? now;
114
+ groupItem.lastError = groupItem.lastError ?? "用户手动停止流水线";
115
+ }
116
+ run.status = "stopped";
117
+ run.updatedAt = now;
118
+ };
119
+ const readPipelineIdentitySnapshot = (pipelineId, run, batchRun) => {
120
+ if (typeof batchRun?.batchRunId === "string" && batchRun.batchRunId.trim()) {
121
+ return {
122
+ pipelineId,
123
+ runId: normalizeIdentityValue(run?.id),
124
+ batchRunId: batchRun.batchRunId.trim(),
125
+ };
126
+ }
127
+ const startedAt = normalizeIdentityValue(batchRun?.startedAt);
128
+ return {
129
+ pipelineId,
130
+ runId: normalizeIdentityValue(run?.id),
131
+ batchRunId: startedAt ? `batch:${pipelineId}:${startedAt}` : null,
132
+ };
133
+ };
134
+ exports.readPipelineIdentitySnapshot = readPipelineIdentitySnapshot;
135
+ const matchPipelineIdentityTarget = (identity, target) => {
136
+ const normalizedTarget = (0, exports.normalizePipelineRunIdentityTarget)(target);
137
+ const metadata = {
138
+ ...identity,
139
+ requestedRunId: normalizedTarget.runId,
140
+ requestedBatchRunId: normalizedTarget.batchRunId,
141
+ matchedBy: null,
142
+ };
143
+ if (normalizedTarget.batchRunId) {
144
+ if (identity.batchRunId !== normalizedTarget.batchRunId) {
145
+ return { ok: false, metadata };
146
+ }
147
+ metadata.matchedBy = "batchRunId";
148
+ }
149
+ if (normalizedTarget.runId) {
150
+ if (identity.runId !== normalizedTarget.runId) {
151
+ return { ok: false, metadata };
152
+ }
153
+ metadata.matchedBy = metadata.matchedBy ?? "runId";
154
+ }
155
+ if (!normalizedTarget.batchRunId && !normalizedTarget.runId) {
156
+ metadata.matchedBy = "pipelineId";
157
+ }
158
+ return { ok: true, metadata };
159
+ };
160
+ exports.matchPipelineIdentityTarget = matchPipelineIdentityTarget;
161
+ const attachIdentityToPipelineStatusResult = (result, metadata) => {
162
+ return {
163
+ ...result,
164
+ ...metadata,
165
+ };
166
+ };
167
+ const createPipelineService = (app) => {
168
+ const listPipelines = () => app.listPipelines().map((definition) => ({
169
+ id: definition.id,
170
+ title: definition.title,
171
+ }));
172
+ const getPipeline = (pipelineId) => {
173
+ const definition = app.getPipelineDefinition(pipelineId);
174
+ const runtime = app.getPipelineRuntime(pipelineId);
175
+ if (!definition || !runtime)
176
+ return null;
177
+ // 只读 service 直接透出运行态快照,不承担任何写入行为。
178
+ return {
179
+ pipelineId: definition.id,
180
+ title: definition.title,
181
+ run: runtime.runtime.getRun(),
182
+ scheduler: runtime.pipeline.getSchedulerState(),
183
+ batchRun: runtime.pipeline.getBatchRunState(),
184
+ templateNodes: runtime.workflow.getTemplateNodes(),
185
+ workflow: runtime.workflow.getWorkflow(),
186
+ };
187
+ };
188
+ const getTimeline = () => app.runtime.getCombinedTimeline();
189
+ const startPipeline = async (pipelineId) => {
190
+ const runtime = getRuntimeByPipelineId(app, pipelineId);
191
+ if (!runtime)
192
+ return { ok: false, pipelineId, error: "pipeline_not_found" };
193
+ const batchState = runtime.pipeline.getBatchRunState();
194
+ if (batchState.status === "running") {
195
+ return { ok: false, pipelineId, error: "batch_run_in_progress", state: batchState };
196
+ }
197
+ const workflow = runtime.workflow.getWorkflow();
198
+ const remoteBatchPlugin = workflow.plugins.remoteBatch;
199
+ if (remoteBatchPlugin.enabled) {
200
+ const remoteUrl = remoteBatchPlugin.url.trim() || pipeline_config_1.DEFAULT_REMOTE_BATCH_URL;
201
+ if (!remoteUrl) {
202
+ return { ok: false, pipelineId, error: "remote_pool_url_empty" };
203
+ }
204
+ let remotePayload = null;
205
+ try {
206
+ const response = await fetch(remoteUrl, { method: "GET" });
207
+ if (!response.ok) {
208
+ return {
209
+ ok: false,
210
+ pipelineId,
211
+ error: "remote_pool_fetch_failed",
212
+ remoteUrl,
213
+ status: response.status,
214
+ };
215
+ }
216
+ const text = await response.text();
217
+ try {
218
+ remotePayload = JSON.parse(text);
219
+ }
220
+ catch {
221
+ // 非 JSON 返回也允许继续尝试解析,兼容逗号/换行文本池。
222
+ remotePayload = text;
223
+ }
224
+ }
225
+ catch (error) {
226
+ return {
227
+ ok: false,
228
+ pipelineId,
229
+ error: "remote_pool_fetch_error",
230
+ remoteUrl,
231
+ detail: error instanceof Error ? error.message : "unknown_error",
232
+ };
233
+ }
234
+ const preferredPayload = remoteBatchPlugin.sourceField
235
+ ? (0, exports.readNestedValueByPath)(remotePayload, remoteBatchPlugin.sourceField)
236
+ : null;
237
+ const items = (0, exports.extractKeywordPoolFromUnknown)(preferredPayload ?? remotePayload);
238
+ if (items.length === 0) {
239
+ return { ok: false, pipelineId, error: "remote_batch_items_empty", remoteUrl };
240
+ }
241
+ const batchSize = Math.max(1, remoteBatchPlugin.batchSize || 1);
242
+ const startIndex = Math.max(0, (Math.max(1, remoteBatchPlugin.startBatch || 1) - 1) * batchSize);
243
+ const started = runtime.pipeline.startBatchRun(items, batchSize, { startIndex });
244
+ if (!started.ok) {
245
+ const normalizedError = started.error === "batch_run_in_progress" ? "batch_run_in_progress" : "batch_items_empty";
246
+ return { ok: false, pipelineId, error: normalizedError, state: started.snapshot, remoteUrl };
247
+ }
248
+ runtime.runtime.pushTimeline(`[${pipelineId}] 远程关键词池批跑已启动: ${items.length} 个关键词, 每批 ${started.snapshot.batchSize} 个`, "info", { remoteUrl });
249
+ return {
250
+ ok: true,
251
+ mode: "remote_batch",
252
+ pipelineId,
253
+ accepted: true,
254
+ batchRunId: buildBatchRunId(pipelineId, started.snapshot),
255
+ runId: runtime.runtime.getRun()?.id ?? null,
256
+ remoteUrl,
257
+ totalFetched: items.length,
258
+ batchRun: started.snapshot,
259
+ templateNodes: runtime.workflow.getTemplateNodes(),
260
+ edges: runtime.workflow.getWorkflow().edges,
261
+ workflowNodes: runtime.workflow.getWorkflow().nodes,
262
+ };
263
+ }
264
+ const run = runtime.runtime.seedRun(runtime.workflow.getTemplateNodes());
265
+ runtime.runtime.setRun(run);
266
+ runtime.runtime.pushTimeline(`[${pipelineId}] 已启动新运行: ${run.id}`);
267
+ runtime.runtime.emitPipeline();
268
+ // start 只负责发起运行,不承诺在返回时已执行完成。
269
+ void runtime.pipeline.drainPipeline(`run:start:${run.id}`).then(() => {
270
+ runtime.runtime.touchRun(runtime.runtime.getRun());
271
+ });
272
+ return { ok: true, mode: "single", pipelineId, accepted: true, runId: run.id, run: runtime.runtime.getRun(), workflowNodes: runtime.workflow.getWorkflow().nodes };
273
+ };
274
+ const getPipelineExecutionStatus = (pipelineId, target) => {
275
+ const detail = getPipeline(pipelineId);
276
+ if (!detail) {
277
+ const missingIdentity = (0, exports.readPipelineIdentitySnapshot)(pipelineId, null, null);
278
+ return {
279
+ ...(0, exports.matchPipelineIdentityTarget)(missingIdentity, target).metadata,
280
+ ok: false,
281
+ error: "pipeline_not_found",
282
+ };
283
+ }
284
+ const identity = (0, exports.readPipelineIdentitySnapshot)(pipelineId, detail.run, detail.batchRun);
285
+ const matchedIdentity = (0, exports.matchPipelineIdentityTarget)(identity, target);
286
+ if (!matchedIdentity.ok) {
287
+ return {
288
+ ...matchedIdentity.metadata,
289
+ ok: false,
290
+ error: "run_not_found",
291
+ };
292
+ }
293
+ // status 命令只表达“当前是否仍在运行”,非运行态改为返回精简联合结构。
294
+ return attachIdentityToPipelineStatusResult((0, pipeline_status_1.buildPipelineStatusResult)({
295
+ pipelineId,
296
+ run: detail.run,
297
+ scheduler: detail.scheduler,
298
+ batchRun: detail.batchRun,
299
+ }), matchedIdentity.metadata);
300
+ };
301
+ const stopPipeline = (pipelineId, target) => {
302
+ const runtime = getRuntimeByPipelineId(app, pipelineId);
303
+ const missingIdentity = (0, exports.readPipelineIdentitySnapshot)(pipelineId, runtime?.runtime.getRun(), runtime?.pipeline.getBatchRunState());
304
+ if (!runtime) {
305
+ return {
306
+ ...(0, exports.matchPipelineIdentityTarget)(missingIdentity, target).metadata,
307
+ ok: false,
308
+ error: "pipeline_not_found",
309
+ };
310
+ }
311
+ const runState = runtime.runtime.getRun();
312
+ const batchRunState = runtime.pipeline.getBatchRunState();
313
+ const identity = (0, exports.readPipelineIdentitySnapshot)(pipelineId, runState, batchRunState);
314
+ const matchedIdentity = (0, exports.matchPipelineIdentityTarget)(identity, target);
315
+ if (!matchedIdentity.ok) {
316
+ return {
317
+ ...matchedIdentity.metadata,
318
+ ok: false,
319
+ error: "run_not_found",
320
+ };
321
+ }
322
+ if (batchRunState.status !== "running") {
323
+ const currentStatus = getPipelineExecutionStatus(pipelineId, target);
324
+ if (currentStatus.ok && "status" in currentStatus && currentStatus.status.runStatus === "running") {
325
+ if (runState.id) {
326
+ runtime.pipeline.abortRunControllers(runState.id);
327
+ }
328
+ markRunningRunStopped(runState);
329
+ runtime.runtime.pushTimeline(`[${pipelineId}] 已请求停止单次运行: ${runState.id}`, "warn");
330
+ runtime.runtime.emitPipeline();
331
+ const stoppedStatus = (0, execution_status_1.buildPipelineExecutionStatus)({
332
+ pipelineId,
333
+ run: runState,
334
+ scheduler: runtime.pipeline.getSchedulerState(),
335
+ batchRun: runtime.pipeline.getBatchRunState(),
336
+ });
337
+ return {
338
+ ...matchedIdentity.metadata,
339
+ ok: true,
340
+ mode: "single",
341
+ status: stoppedStatus,
342
+ };
343
+ }
344
+ return {
345
+ ...matchedIdentity.metadata,
346
+ ok: false,
347
+ error: "batch_run_not_running",
348
+ status: currentStatus.ok && "status" in currentStatus ? currentStatus.status : undefined,
349
+ };
350
+ }
351
+ const stopped = runtime.pipeline.stopBatchRun();
352
+ if (!stopped.ok) {
353
+ return {
354
+ ...matchedIdentity.metadata,
355
+ ok: false,
356
+ error: "batch_run_not_running",
357
+ };
358
+ }
359
+ // 中止当前正在执行的节点,同时向远端 agent 发送 /stop 命令
360
+ if (runState.id) {
361
+ runtime.pipeline.abortRunControllers(runState.id);
362
+ }
363
+ const currentStatus = getPipelineExecutionStatus(pipelineId, target);
364
+ return {
365
+ ...matchedIdentity.metadata,
366
+ ok: true,
367
+ mode: "remote_batch",
368
+ stopped,
369
+ status: currentStatus.ok && "status" in currentStatus
370
+ ? currentStatus.status
371
+ : {
372
+ pipelineId,
373
+ mode: "remote_batch",
374
+ running: true,
375
+ runId: null,
376
+ runStatus: "running",
377
+ activeNodeIds: [],
378
+ pendingNodeIds: [],
379
+ scheduler: runtime.pipeline.getSchedulerState(),
380
+ batchRun: runtime.pipeline.getBatchRunState(),
381
+ currentBatch: null,
382
+ lastError: null,
383
+ updatedAt: new Date().toISOString(),
384
+ },
385
+ };
386
+ };
387
+ const runPipeline = async (pipelineId) => {
388
+ // run 仅作为兼容入口,语义等价到 start。
389
+ return startPipeline(pipelineId);
390
+ };
391
+ const startBatchRun = (input) => {
392
+ const runtime = getRuntimeByPipelineId(app, input.pipelineId);
393
+ if (!runtime)
394
+ return { ok: false, pipelineId: input.pipelineId, error: "pipeline_not_found" };
395
+ if (input.items.length === 0)
396
+ return { ok: false, pipelineId: input.pipelineId, error: "batch_items_empty" };
397
+ const batchSize = normalizeBatchSize(input.batchSize);
398
+ const startIndex = normalizeStartIndex(input.startIndex, input.startBatch, batchSize);
399
+ const started = runtime.pipeline.startBatchRun(input.items, batchSize, startIndex !== undefined ? { startIndex } : undefined);
400
+ if (!started.ok) {
401
+ return {
402
+ ok: false,
403
+ pipelineId: input.pipelineId,
404
+ error: started.error === "batch_run_in_progress" ? "batch_run_in_progress" : "batch_items_empty",
405
+ state: started.snapshot,
406
+ };
407
+ }
408
+ return { ok: true, pipelineId: input.pipelineId, state: started.snapshot };
409
+ };
410
+ const startRemoteBatchRun = async (input) => {
411
+ const runtime = getRuntimeByPipelineId(app, input.pipelineId);
412
+ if (!runtime)
413
+ return { ok: false, pipelineId: input.pipelineId, error: "pipeline_not_found" };
414
+ const remoteBatchPlugin = runtime.workflow.getWorkflow().plugins.remoteBatch;
415
+ if (!remoteBatchPlugin.enabled) {
416
+ return { ok: false, pipelineId: input.pipelineId, error: "pipeline_plugin_disabled", plugin: "remoteBatch" };
417
+ }
418
+ const remoteUrl = typeof input.url === "string" && input.url.trim()
419
+ ? input.url.trim()
420
+ : remoteBatchPlugin.url.trim() || pipeline_config_1.DEFAULT_REMOTE_BATCH_URL;
421
+ if (!remoteUrl) {
422
+ return { ok: false, pipelineId: input.pipelineId, error: "remote_pool_url_empty" };
423
+ }
424
+ let remotePayload = null;
425
+ try {
426
+ const response = await fetch(remoteUrl, { method: "GET" });
427
+ if (!response.ok) {
428
+ return { ok: false, pipelineId: input.pipelineId, error: "remote_pool_fetch_failed", remoteUrl, status: response.status };
429
+ }
430
+ const text = await response.text();
431
+ try {
432
+ remotePayload = JSON.parse(text);
433
+ }
434
+ catch {
435
+ // 非 JSON 文本也允许继续解析,避免上游切到纯文本池时整条批跑入口不可用。
436
+ remotePayload = text;
437
+ }
438
+ }
439
+ catch (error) {
440
+ return {
441
+ ok: false,
442
+ pipelineId: input.pipelineId,
443
+ error: "remote_pool_fetch_error",
444
+ remoteUrl,
445
+ detail: error instanceof Error ? error.message : "unknown_error",
446
+ };
447
+ }
448
+ // sourceField 视为“优先路径”而非严格依赖:路径缺失时退回全量解析以保持兼容。
449
+ const preferredPayload = remoteBatchPlugin.sourceField
450
+ ? (0, exports.readNestedValueByPath)(remotePayload, remoteBatchPlugin.sourceField)
451
+ : null;
452
+ const items = (0, exports.extractKeywordPoolFromUnknown)(preferredPayload ?? remotePayload);
453
+ if (items.length === 0) {
454
+ return { ok: false, pipelineId: input.pipelineId, error: "remote_batch_items_empty", remoteUrl };
455
+ }
456
+ const batchSize = normalizeBatchSize(input.batchSize) ?? remoteBatchPlugin.batchSize ?? 5;
457
+ const startIndex = normalizeStartIndex(input.startIndex, input.startBatch, batchSize);
458
+ const started = runtime.pipeline.startBatchRun(items, batchSize, startIndex !== undefined ? { startIndex } : undefined);
459
+ if (!started.ok) {
460
+ return {
461
+ ok: false,
462
+ pipelineId: input.pipelineId,
463
+ error: started.error === "batch_run_in_progress" ? "batch_run_in_progress" : "batch_items_empty",
464
+ state: started.snapshot,
465
+ remoteUrl,
466
+ };
467
+ }
468
+ runtime.runtime.pushTimeline(`[${input.pipelineId}] 远程关键词池批跑已启动: ${items.length} 个关键词, 每批 ${started.snapshot.batchSize} 个`, "info", { remoteUrl });
469
+ return {
470
+ ok: true,
471
+ pipelineId: input.pipelineId,
472
+ state: started.snapshot,
473
+ remoteUrl,
474
+ totalFetched: items.length,
475
+ };
476
+ };
477
+ const retryNode = async (input) => {
478
+ const runtime = getRuntimeByPipelineId(app, input.pipelineId);
479
+ if (!runtime)
480
+ return { ok: false, pipelineId: input.pipelineId, error: "pipeline_not_found" };
481
+ // 中止当前正在执行的节点,避免旧节点与新重试冲突(同时向远端 agent 发送 /stop 命令)
482
+ const runState = runtime.runtime.getRun();
483
+ if (runState.id) {
484
+ runtime.pipeline.abortRunControllers(runState.id);
485
+ }
486
+ const retry = await runtime.pipeline.retryNodeExecution(input.nodeId, input.itemKey);
487
+ const run = runtime.runtime.getRun();
488
+ runtime.runtime.touchRun(run);
489
+ return { ok: true, pipelineId: input.pipelineId, run, retry };
490
+ };
491
+ const listOutputs = async (pipelineId) => {
492
+ const runtime = app.getPipelineRuntime(pipelineId);
493
+ if (!runtime)
494
+ return [];
495
+ return runtime.output.list();
496
+ };
497
+ const getOutput = async (pipelineId, runId) => {
498
+ const runtime = app.getPipelineRuntime(pipelineId);
499
+ if (!runtime)
500
+ return null;
501
+ if (runId)
502
+ return runtime.output.getByRunId(runId);
503
+ const outputs = await runtime.output.list();
504
+ return outputs.length > 0 ? outputs[outputs.length - 1] : null;
505
+ };
506
+ const listLinks = async () => app.dispatch.listLinks();
507
+ const getQueue = (pipelineId) => app.dispatch.getQueue(pipelineId);
508
+ const cancelJob = async (pipelineId, jobId, reason) => app.dispatch.cancelJob(jobId, reason ?? "canceled_by_user");
509
+ const retryJob = async (pipelineId, jobId) => app.dispatch.retryJob(jobId);
510
+ return {
511
+ listPipelines,
512
+ getPipeline,
513
+ getTimeline,
514
+ startPipeline,
515
+ getPipelineExecutionStatus,
516
+ stopPipeline,
517
+ runPipeline,
518
+ startBatchRun,
519
+ startRemoteBatchRun,
520
+ retryNode,
521
+ listOutputs,
522
+ getOutput,
523
+ listLinks,
524
+ getQueue,
525
+ cancelJob,
526
+ retryJob,
527
+ };
528
+ };
529
+ exports.createPipelineService = createPipelineService;
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildPipelineStatusResult = exports.readPipelineLastCompletedAt = void 0;
4
+ const execution_status_1 = require("../pipeline/execution-status");
5
+ const isNonEmptyString = (value) => {
6
+ return typeof value === "string" && value.trim().length > 0;
7
+ };
8
+ const parseTimestamp = (value) => {
9
+ if (!isNonEmptyString(value))
10
+ return null;
11
+ const parsed = Date.parse(value.trim());
12
+ return Number.isFinite(parsed) ? parsed : null;
13
+ };
14
+ const pickLatestTimestamp = (candidates) => {
15
+ let best = null;
16
+ for (const candidate of candidates) {
17
+ if (!isNonEmptyString(candidate))
18
+ continue;
19
+ const normalized = candidate.trim();
20
+ const ms = parseTimestamp(normalized);
21
+ if (ms === null)
22
+ continue;
23
+ if (!best || ms > best.ms) {
24
+ best = { iso: normalized, ms };
25
+ }
26
+ }
27
+ return best?.iso ?? null;
28
+ };
29
+ const collectFinishedAtValues = (items) => {
30
+ if (!Array.isArray(items))
31
+ return [];
32
+ return items
33
+ .map((item) => item.finishedAt)
34
+ .filter((value) => isNonEmptyString(value))
35
+ .map((value) => value.trim());
36
+ };
37
+ const isRunTerminal = (run) => {
38
+ return run.status === "success" || run.status === "failed" || run.status === "stopped";
39
+ };
40
+ const isBatchTerminal = (batchRun) => {
41
+ if (!batchRun || typeof batchRun !== "object")
42
+ return false;
43
+ const status = batchRun.status;
44
+ return status === "completed" || status === "failed" || status === "stopped";
45
+ };
46
+ const readPipelineLastCompletedAt = (run, batchRun) => {
47
+ const candidates = [];
48
+ // 只有确认 run 已进入终态后,才允许把 updatedAt 视作完成时间候选。
49
+ if (isRunTerminal(run) && isNonEmptyString(run.updatedAt)) {
50
+ candidates.push(run.updatedAt.trim());
51
+ }
52
+ candidates.push(...collectFinishedAtValues(run.nodes));
53
+ candidates.push(...collectFinishedAtValues(run.itemRuns));
54
+ candidates.push(...collectFinishedAtValues(run.groups));
55
+ candidates.push(...collectFinishedAtValues(run.groupItemRuns));
56
+ // 批跑完成时间应来自控制器真实终态,而不是运行中的快照更新时间。
57
+ if (isBatchTerminal(batchRun) && isNonEmptyString(batchRun.finishedAt)) {
58
+ candidates.push(batchRun.finishedAt.trim());
59
+ }
60
+ return pickLatestTimestamp(candidates);
61
+ };
62
+ exports.readPipelineLastCompletedAt = readPipelineLastCompletedAt;
63
+ const buildPipelineStatusResult = (input) => {
64
+ const status = (0, execution_status_1.buildPipelineExecutionStatus)({
65
+ pipelineId: input.pipelineId,
66
+ // 这里直接复用 execution-status 作为运行态快照构造器,保证 status 字段来源仍然唯一。
67
+ run: input.run,
68
+ scheduler: input.scheduler,
69
+ batchRun: input.batchRun,
70
+ });
71
+ if (status.running) {
72
+ // status.running=true 时,mode 只能是 single 或 remote_batch;这里显式收窄给 CLI/API 统一消费。
73
+ return {
74
+ ok: true,
75
+ status: {
76
+ ...status,
77
+ mode: status.mode === "remote_batch" ? "remote_batch" : "single",
78
+ running: true,
79
+ },
80
+ };
81
+ }
82
+ const batchRunRecord = input.batchRun && typeof input.batchRun === "object" ? input.batchRun : null;
83
+ return {
84
+ ok: true,
85
+ pipelineId: input.pipelineId,
86
+ running: false,
87
+ message: "no active pipeline run",
88
+ lastCompletedAt: (0, exports.readPipelineLastCompletedAt)(input.run, input.batchRun),
89
+ lastRunId: isNonEmptyString(input.run.id) ? input.run.id.trim() : null,
90
+ lastBatchRunId: isNonEmptyString(batchRunRecord?.batchRunId) ? String(batchRunRecord.batchRunId).trim() : null,
91
+ };
92
+ };
93
+ exports.buildPipelineStatusResult = buildPipelineStatusResult;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAppServices = exports.createReadonlyServices = void 0;
4
+ const agent_service_1 = require("./agent-service");
5
+ const artifact_service_1 = require("./artifact-service");
6
+ const pipeline_service_1 = require("./pipeline-service");
7
+ const scheduler_service_1 = require("./scheduler-service");
8
+ const session_service_1 = require("./session-service");
9
+ const system_service_1 = require("./system-service");
10
+ const createAllServices = (app) => ({
11
+ system: (0, system_service_1.createSystemService)(app),
12
+ pipeline: (0, pipeline_service_1.createPipelineService)(app),
13
+ agent: (0, agent_service_1.createAgentService)(app),
14
+ session: (0, session_service_1.createSessionService)(app),
15
+ artifact: (0, artifact_service_1.createArtifactService)(app),
16
+ scheduler: (0, scheduler_service_1.createSchedulerService)(app),
17
+ });
18
+ const createReadonlyServices = (app) => {
19
+ const services = createAllServices(app);
20
+ return {
21
+ // 统一工厂用于主线集成,CLI 入口只需传入 registry 即可拿到全部只读 service。
22
+ system: services.system,
23
+ pipeline: services.pipeline,
24
+ agent: services.agent,
25
+ session: services.session,
26
+ artifact: services.artifact,
27
+ };
28
+ };
29
+ exports.createReadonlyServices = createReadonlyServices;
30
+ const createAppServices = (app) => {
31
+ const services = createAllServices(app);
32
+ return {
33
+ readonly: {
34
+ system: services.system,
35
+ pipeline: services.pipeline,
36
+ agent: services.agent,
37
+ session: services.session,
38
+ artifact: services.artifact,
39
+ },
40
+ writable: {
41
+ pipeline: {
42
+ startPipeline: services.pipeline.startPipeline,
43
+ getPipelineExecutionStatus: services.pipeline.getPipelineExecutionStatus,
44
+ stopPipeline: services.pipeline.stopPipeline,
45
+ runPipeline: services.pipeline.runPipeline,
46
+ retryNode: services.pipeline.retryNode,
47
+ getOutput: services.pipeline.getOutput,
48
+ listOutputs: services.pipeline.listOutputs,
49
+ listLinks: services.pipeline.listLinks,
50
+ getQueue: services.pipeline.getQueue,
51
+ },
52
+ session: {
53
+ sendMessage: services.session.sendMessage,
54
+ sendMessageAndWaitForReply: services.session.sendMessageAndWaitForReply,
55
+ },
56
+ scheduler: services.scheduler,
57
+ },
58
+ };
59
+ };
60
+ exports.createAppServices = createAppServices;