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,173 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.inferSessionAgentIds = exports.parseAgentSessionMapFromEnv = exports.addTimeline = exports.seedRunWithItems = exports.seedRun = exports.touchRun = exports.computeRunStatus = exports.seedNodeItemRuns = exports.createGroupItemRun = exports.createNodeItemRun = exports.syncRunNodeStatusFromItemRuns = exports.aggregateNodeStatusFromItemRuns = exports.normalizeSession = exports.pickArray = void 0;
4
+ const node_crypto_1 = require("node:crypto");
5
+ const identity_1 = require("./identity");
6
+ var array_1 = require("../utils/array");
7
+ Object.defineProperty(exports, "pickArray", { enumerable: true, get: function () { return array_1.pickArray; } });
8
+ var session_1 = require("../utils/session");
9
+ Object.defineProperty(exports, "normalizeSession", { enumerable: true, get: function () { return session_1.normalizeSession; } });
10
+ const NODE_STATUS_PRIORITY = {
11
+ failed: 100,
12
+ running: 90,
13
+ rejected: 80,
14
+ waiting: 70,
15
+ queued: 60,
16
+ blocked: 50,
17
+ success: 40,
18
+ skipped: 30,
19
+ stopped: 20,
20
+ };
21
+ const aggregateNodeStatusFromItemRuns = (itemRuns) => {
22
+ if (itemRuns.length === 0)
23
+ return "blocked";
24
+ let best = itemRuns[0].status;
25
+ for (const item of itemRuns) {
26
+ if (NODE_STATUS_PRIORITY[item.status] > NODE_STATUS_PRIORITY[best]) {
27
+ best = item.status;
28
+ }
29
+ }
30
+ return best;
31
+ };
32
+ exports.aggregateNodeStatusFromItemRuns = aggregateNodeStatusFromItemRuns;
33
+ const syncRunNodeStatusFromItemRuns = (run) => {
34
+ const itemRuns = run.itemRuns ?? [];
35
+ if (itemRuns.length === 0)
36
+ return;
37
+ const itemsByNodeId = new Map();
38
+ for (const item of itemRuns) {
39
+ const current = itemsByNodeId.get(item.nodeId) ?? [];
40
+ current.push(item);
41
+ itemsByNodeId.set(item.nodeId, current);
42
+ }
43
+ for (const node of run.nodes) {
44
+ const related = itemsByNodeId.get(node.id);
45
+ if (!related || related.length === 0)
46
+ continue;
47
+ node.status = (0, exports.aggregateNodeStatusFromItemRuns)(related);
48
+ }
49
+ };
50
+ exports.syncRunNodeStatusFromItemRuns = syncRunNodeStatusFromItemRuns;
51
+ const createNodeItemRun = (nodeId, itemKey, initialStatus) => ({
52
+ id: (0, node_crypto_1.randomUUID)(),
53
+ nodeId,
54
+ itemKey,
55
+ status: initialStatus,
56
+ route: null,
57
+ attempt: 0,
58
+ loopCount: 0,
59
+ wakeAt: null,
60
+ startedAt: null,
61
+ finishedAt: null,
62
+ lastError: null,
63
+ artifacts: [],
64
+ });
65
+ exports.createNodeItemRun = createNodeItemRun;
66
+ const createGroupItemRun = (groupId, itemKey, initialStatus) => ({
67
+ id: (0, node_crypto_1.randomUUID)(),
68
+ groupId,
69
+ itemKey,
70
+ status: initialStatus,
71
+ attempt: 0,
72
+ startedAt: null,
73
+ finishedAt: null,
74
+ lastError: null,
75
+ artifacts: [],
76
+ });
77
+ exports.createGroupItemRun = createGroupItemRun;
78
+ const seedNodeItemRuns = (templateNodes, itemKeys) => {
79
+ const uniqueItemKeys = [...new Set(itemKeys.map((item) => item.trim()).filter(Boolean))];
80
+ if (uniqueItemKeys.length === 0)
81
+ return [];
82
+ const itemRuns = [];
83
+ for (const node of templateNodes) {
84
+ const initialStatus = node.dependsOn.length > 0 ? "blocked" : "queued";
85
+ for (const itemKey of uniqueItemKeys) {
86
+ itemRuns.push((0, exports.createNodeItemRun)(node.id, itemKey, initialStatus));
87
+ }
88
+ }
89
+ return itemRuns;
90
+ };
91
+ exports.seedNodeItemRuns = seedNodeItemRuns;
92
+ const computeRunStatus = (run) => {
93
+ if (run.nodes.some((node) => node.status === "failed"))
94
+ return "failed";
95
+ if ((run.groups ?? []).some((group) => group.status === "failed"))
96
+ return "failed";
97
+ if (run.nodes.some((node) => node.status === "stopped"))
98
+ return "stopped";
99
+ if ((run.groups ?? []).some((group) => group.status === "stopped"))
100
+ return "stopped";
101
+ if (run.nodes.every((node) => node.status === "success" || node.status === "skipped") &&
102
+ (run.groups ?? []).every((group) => group.status === "success" || group.status === "skipped")) {
103
+ return "success";
104
+ }
105
+ return "running";
106
+ };
107
+ exports.computeRunStatus = computeRunStatus;
108
+ const touchRun = (run) => {
109
+ run.updatedAt = new Date().toISOString();
110
+ run.status = (0, exports.computeRunStatus)(run);
111
+ };
112
+ exports.touchRun = touchRun;
113
+ const seedRun = (templateNodes) => {
114
+ const now = new Date().toISOString();
115
+ const run = {
116
+ id: (0, identity_1.buildRunId)(),
117
+ status: "running",
118
+ createdAt: now,
119
+ updatedAt: now,
120
+ input: { trigger: "manual" },
121
+ nodes: templateNodes.map((tpl) => ({
122
+ id: tpl.id,
123
+ title: tpl.title,
124
+ executor: tpl.executor,
125
+ instruction: tpl.instruction,
126
+ outputSpec: tpl.outputSpec,
127
+ allowReject: tpl.allowReject,
128
+ maxRejectCount: tpl.maxRejectCount,
129
+ status: tpl.dependsOn.length > 0 ? "blocked" : "queued",
130
+ dependsOn: tpl.dependsOn,
131
+ artifacts: [],
132
+ rejectFeedbacks: [],
133
+ attempt: 0,
134
+ rejectCount: 0,
135
+ startedAt: null,
136
+ finishedAt: null,
137
+ lastError: null,
138
+ })),
139
+ itemRuns: [],
140
+ groups: [],
141
+ groupItemRuns: [],
142
+ output: null,
143
+ };
144
+ (0, exports.touchRun)(run);
145
+ return run;
146
+ };
147
+ exports.seedRun = seedRun;
148
+ const seedRunWithItems = (templateNodes, itemKeys) => {
149
+ const run = (0, exports.seedRun)(templateNodes);
150
+ run.itemRuns = (0, exports.seedNodeItemRuns)(templateNodes, itemKeys);
151
+ (0, exports.touchRun)(run);
152
+ return run;
153
+ };
154
+ exports.seedRunWithItems = seedRunWithItems;
155
+ const addTimeline = (timeline, text, level = "info", detail, maxTimeline = 200) => {
156
+ const item = {
157
+ id: (0, node_crypto_1.randomUUID)(),
158
+ ts: new Date().toLocaleTimeString("zh-CN", { hour12: false }),
159
+ createdAt: new Date().toISOString(),
160
+ text,
161
+ level,
162
+ ...(detail === undefined ? {} : { detail }),
163
+ };
164
+ timeline.unshift(item);
165
+ if (timeline.length > maxTimeline) {
166
+ timeline.length = maxTimeline;
167
+ }
168
+ return item;
169
+ };
170
+ exports.addTimeline = addTimeline;
171
+ var session_2 = require("../utils/session");
172
+ Object.defineProperty(exports, "parseAgentSessionMapFromEnv", { enumerable: true, get: function () { return session_2.parseAgentSessionMapFromEnv; } });
173
+ Object.defineProperty(exports, "inferSessionAgentIds", { enumerable: true, get: function () { return session_2.inferSessionAgentIds; } });
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createDependencyState = void 0;
4
+ const runtime_model_1 = require("../runtime-model");
5
+ const readiness_state_1 = require("../execution/readiness-state");
6
+ const dependency_check_1 = require("../execution/dependency-check");
7
+ const state_1 = require("../state");
8
+ const createDependencyState = (options) => {
9
+ const getRun = () => options.runtimeStore.getRun();
10
+ const resolveDependencyPolicy = (nodeId) => {
11
+ const workflowNode = options.graph.getWorkflowNodeById(nodeId);
12
+ return workflowNode?.dependencyPolicy === "any" ? "any" : "all";
13
+ };
14
+ const resolveDependencyOutcome = (itemKey, incoming, policy) => {
15
+ let satisfiedCount = 0;
16
+ let impossibleCount = 0;
17
+ for (const edge of incoming) {
18
+ if ((0, dependency_check_1.isDependencySatisfied)(itemKey, edge, depCheckContext)) {
19
+ satisfiedCount += 1;
20
+ continue;
21
+ }
22
+ if ((0, dependency_check_1.canNeverSatisfy)(itemKey, edge, depCheckContext)) {
23
+ impossibleCount += 1;
24
+ }
25
+ }
26
+ // all/any 执行差异:
27
+ // - all: 只有全部依赖满足才入队;若剩余依赖都已不可能满足则跳过。
28
+ // - any: 任一依赖满足即可入队;仅当所有依赖都不可能满足时才跳过。
29
+ if (policy === "any") {
30
+ if (satisfiedCount > 0)
31
+ return "queued";
32
+ if (impossibleCount === incoming.length)
33
+ return "skipped";
34
+ return "waiting";
35
+ }
36
+ if (satisfiedCount === incoming.length)
37
+ return "queued";
38
+ if (satisfiedCount + impossibleCount === incoming.length && impossibleCount > 0)
39
+ return "skipped";
40
+ return "waiting";
41
+ };
42
+ const depCheckContext = {
43
+ isCrossBranchEdge: (edge) => options.graph.isCrossBranchEdge(edge),
44
+ isGroupId: (id) => options.graph.isGroupId(id),
45
+ isWorkflowNodeEnabled: (id) => options.graph.isWorkflowNodeEnabled(id),
46
+ isRoutePolicyNode: (id) => (options.graph.getWorkflowNodeById(id)?.routePolicy?.allowed.length ?? 0) > 0,
47
+ getGroupItemRun: options.getGroupItemRun,
48
+ getItemRun: options.getItemRun,
49
+ };
50
+ const wakeDueItems = () => {
51
+ const now = Date.now();
52
+ for (const item of getRun().itemRuns ?? []) {
53
+ if (item.status !== "waiting" || !item.wakeAt)
54
+ continue;
55
+ const wakeTs = Date.parse(item.wakeAt);
56
+ if (!Number.isFinite(wakeTs) || wakeTs > now)
57
+ continue;
58
+ (0, state_1.markItemWakeSuccess)(item, { reason: "sleep_expired" });
59
+ options.runtimeStore.pushTimeline(`等待节点已唤醒: ${item.nodeId}#${item.itemKey}`);
60
+ }
61
+ };
62
+ const markReadyItemsFromDependencies = () => {
63
+ options.ensureItemRuns();
64
+ options.graph.syncRunGroupsFromWorkflow(getRun());
65
+ wakeDueItems();
66
+ for (const item of getRun().itemRuns ?? []) {
67
+ if (!options.graph.isWorkflowNodeEnabled(item.nodeId)) {
68
+ (0, state_1.markItemSkipped)(item, { reason: "node_disabled" });
69
+ continue;
70
+ }
71
+ if (item.status === "running" || item.status === "success" || item.status === "failed" || item.status === "stopped" || (0, readiness_state_1.isSleepWaitingState)(item)) {
72
+ continue;
73
+ }
74
+ if (options.graph.getParallelGroupByMemberNodeId(item.nodeId)) {
75
+ (0, state_1.markItemBlocked)(item, { reason: "parallel_group_member", command: "dependency" });
76
+ continue;
77
+ }
78
+ const incoming = options.graph.getIncomingEdges(item.nodeId);
79
+ if (incoming.length === 0) {
80
+ if ((0, readiness_state_1.canPromoteToQueuedByDependency)(item))
81
+ (0, state_1.markItemQueued)(item, { reason: "no_dependencies", command: "dependency" });
82
+ continue;
83
+ }
84
+ const outcome = resolveDependencyOutcome(item.itemKey, incoming, resolveDependencyPolicy(item.nodeId));
85
+ if (outcome === "queued")
86
+ (0, state_1.markItemQueued)(item, { reason: "dependency_satisfied", command: "dependency" });
87
+ else if (outcome === "waiting")
88
+ (0, state_1.markItemWaiting)(item, { reason: "dependency_pending", command: "dependency" });
89
+ else
90
+ (0, state_1.markItemSkipped)(item, { reason: "dependency_impossible", command: "dependency" });
91
+ }
92
+ (0, runtime_model_1.syncRunNodeStatusFromItemRuns)(getRun());
93
+ };
94
+ const markReadyGroupsFromDependencies = () => {
95
+ options.graph.syncRunGroupsFromWorkflow(getRun());
96
+ for (const item of getRun().groupItemRuns ?? []) {
97
+ if (item.status === "running" || item.status === "success" || item.status === "failed" || item.status === "stopped") {
98
+ continue;
99
+ }
100
+ const incoming = options.graph.getIncomingEdges(item.groupId);
101
+ if (incoming.length === 0) {
102
+ if ((0, readiness_state_1.canPromoteToQueuedByDependency)(item))
103
+ (0, state_1.markGroupItemQueued)(item, { reason: "no_dependencies", command: "dependency" });
104
+ continue;
105
+ }
106
+ const outcome = resolveDependencyOutcome(item.itemKey, incoming, "all");
107
+ if (outcome === "queued")
108
+ (0, state_1.markGroupItemQueued)(item, { reason: "dependency_satisfied", command: "dependency" });
109
+ else if (outcome === "waiting")
110
+ (0, state_1.markGroupItemWaiting)(item, { reason: "dependency_pending", command: "dependency" });
111
+ else
112
+ (0, state_1.markGroupItemSkipped)(item, { reason: "dependency_impossible", command: "dependency" });
113
+ }
114
+ for (const group of getRun().groups ?? []) {
115
+ const related = (getRun().groupItemRuns ?? []).filter((item) => item.groupId === group.id);
116
+ if (related.length === 0)
117
+ continue;
118
+ // joinPolicy 仅支持 "all":全部 group item 成功才标记 group 成功,任一失败即失败
119
+ if (related.some((item) => item.status === "failed")) {
120
+ (0, state_1.markGroupFailed)(group, { reason: "member_failed", command: "group_aggregate", error: related.find((item) => item.status === "failed")?.lastError });
121
+ }
122
+ else if (related.some((item) => item.status === "running")) {
123
+ (0, state_1.markGroupRunning)(group, { reason: "members_running", command: "group_aggregate" });
124
+ }
125
+ else if (related.some((item) => item.status === "waiting")) {
126
+ (0, state_1.markGroupWaiting)(group, { reason: "members_waiting", command: "group_aggregate" });
127
+ }
128
+ else if (related.some((item) => item.status === "queued")) {
129
+ (0, state_1.markGroupQueued)(group, { reason: "members_queued", command: "group_aggregate" });
130
+ }
131
+ else if (related.every((item) => item.status === "success" || item.status === "skipped")) {
132
+ (0, state_1.markGroupSuccess)(group, { reason: "all_members_done", command: "group_aggregate" });
133
+ }
134
+ else {
135
+ (0, state_1.markGroupBlocked)(group, { reason: "members_blocked", command: "group_aggregate" });
136
+ }
137
+ }
138
+ };
139
+ return {
140
+ markReadyItemsFromDependencies,
141
+ markReadyGroupsFromDependencies,
142
+ };
143
+ };
144
+ exports.createDependencyState = createDependencyState;
@@ -0,0 +1,314 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createSchedulerService = void 0;
4
+ const item_batch_controller_1 = require("./item-batch-controller");
5
+ const runtime_model_1 = require("./runtime-model");
6
+ const dependency_state_1 = require("./scheduler/dependency-state");
7
+ const run_state_helpers_1 = require("./execution/run-state-helpers");
8
+ const state_1 = require("./state");
9
+ const identity_1 = require("./identity");
10
+ /**
11
+ * 流水线调度器。
12
+ * 负责:决定何时执行节点、管理并发、控制调度模式。
13
+ * 不负责:节点的具体执行逻辑。
14
+ */
15
+ const createSchedulerService = (deps) => {
16
+ const isSchedulerPluginEnabled = () => deps.graph.getWorkflow().plugins.scheduler.enabled;
17
+ const schedulerState = {
18
+ enabled: deps.graph.getWorkflow().scheduler.enabled,
19
+ mode: deps.graph.getWorkflow().scheduler.mode,
20
+ };
21
+ const getRun = () => deps.runtimeStore.getRun();
22
+ const state = (0, run_state_helpers_1.createRunStateHelpers)({
23
+ runtimeStore: deps.runtimeStore,
24
+ graph: deps.graph,
25
+ defaultItemKeys: deps.defaultItemKeys,
26
+ });
27
+ let drainInFlight = null;
28
+ let drainInFlightReason = null;
29
+ const dependencyState = (0, dependency_state_1.createDependencyState)({
30
+ runtimeStore: deps.runtimeStore,
31
+ graph: deps.graph,
32
+ ensureItemRuns: state.ensureItemRuns,
33
+ getItemRun: state.getItemRun,
34
+ getGroupItemRun: state.getGroupItemRun,
35
+ });
36
+ const { markReadyItemsFromDependencies, markReadyGroupsFromDependencies } = dependencyState;
37
+ const pickNextRunnableBatch = (maxBatchSize = Math.max(1, deps.graph.getWorkflow().scheduler.maxConcurrency || 1)) => {
38
+ const candidates = [];
39
+ for (const groupItem of getRun().groupItemRuns ?? []) {
40
+ if (groupItem.status !== "queued")
41
+ continue;
42
+ candidates.push(groupItem);
43
+ if (candidates.length >= maxBatchSize)
44
+ return candidates;
45
+ }
46
+ for (const item of getRun().itemRuns ?? []) {
47
+ if (item.status !== "queued")
48
+ continue;
49
+ const node = state.getNodeById(item.nodeId);
50
+ if (!node || !deps.graph.isWorkflowNodeEnabled(item.nodeId))
51
+ continue;
52
+ if (deps.graph.getParallelGroupByMemberNodeId(item.nodeId))
53
+ continue;
54
+ candidates.push(item);
55
+ if (candidates.length >= maxBatchSize)
56
+ return candidates;
57
+ }
58
+ return candidates;
59
+ };
60
+ const resetItemRunForRetry = (item) => {
61
+ (0, state_1.markItemReset)(item, state.computeInitialItemStatus(item.nodeId), { reason: "retry_reset" });
62
+ item.route = null;
63
+ item.artifacts = [];
64
+ };
65
+ const retryNodeExecution = async (nodeId, itemKey) => {
66
+ const node = state.getNodeById(nodeId);
67
+ if (!node) {
68
+ return { ok: false, error: "node_not_found" };
69
+ }
70
+ state.ensureItemRuns();
71
+ const affected = state.collectDownstreamSubgraph(nodeId);
72
+ const items = (getRun().itemRuns ?? []).filter((item) => affected.nodeIds.has(item.nodeId) && (!itemKey || item.itemKey === itemKey));
73
+ if (items.length === 0) {
74
+ return { ok: false, error: itemKey ? "node_item_not_found" : "node_item_runs_not_found" };
75
+ }
76
+ for (const current of items) {
77
+ resetItemRunForRetry(current);
78
+ }
79
+ for (const currentNode of getRun().nodes) {
80
+ if (!affected.nodeIds.has(currentNode.id))
81
+ continue;
82
+ state.resetNodeForReplay(currentNode, { clearRejectFeedbacks: currentNode.id !== nodeId });
83
+ }
84
+ for (const currentGroup of getRun().groups ?? []) {
85
+ if (!affected.groupIds.has(currentGroup.id))
86
+ continue;
87
+ (0, state_1.markGroupReset)(currentGroup, state.computeInitialGroupItemStatus(currentGroup.id), { reason: "retry_reset" });
88
+ currentGroup.artifacts = [];
89
+ }
90
+ for (const currentGroupItem of getRun().groupItemRuns ?? []) {
91
+ if (!affected.groupIds.has(currentGroupItem.groupId))
92
+ continue;
93
+ if (itemKey && currentGroupItem.itemKey !== itemKey)
94
+ continue;
95
+ (0, state_1.markGroupItemReset)(currentGroupItem, state.computeInitialGroupItemStatus(currentGroupItem.groupId), { reason: "retry_reset" });
96
+ currentGroupItem.attempt = 0;
97
+ currentGroupItem.artifacts = [];
98
+ }
99
+ // 手动重试模式下,目标节点可能已经满足依赖但被 reset 成 blocked。
100
+ // 这里先重新评估依赖,再决定是否立刻执行首个节点,避免稳定复现 node_retry_blocked。
101
+ markReadyItemsFromDependencies();
102
+ markReadyGroupsFromDependencies();
103
+ deps.runtimeStore.emitPipeline();
104
+ let drained = null;
105
+ if (schedulerState.mode === "manual") {
106
+ const first = items.find((candidate) => candidate.nodeId === nodeId && candidate.status === "queued") ?? null;
107
+ if (!first) {
108
+ (0, runtime_model_1.touchRun)(getRun());
109
+ deps.runtimeStore.emitPipeline();
110
+ return { ok: false, error: "node_retry_blocked", node, drained };
111
+ }
112
+ const exec = await deps.executionService.executeNodeItem(first);
113
+ (0, runtime_model_1.touchRun)(getRun());
114
+ deps.runtimeStore.emitPipeline();
115
+ return { ...exec, node, item: first, drained };
116
+ }
117
+ const drainSignal = deps.executionService.getOrCreateDrainSignal(getRun().id);
118
+ drained = await drainPipeline(`retry:${nodeId}${itemKey ? `#${itemKey}` : ""}`, drainSignal);
119
+ (0, runtime_model_1.touchRun)(getRun());
120
+ deps.runtimeStore.emitPipeline();
121
+ return { ok: true, node, drained };
122
+ };
123
+ /**
124
+ * 核心调度循环。遍历就绪节点并执行。这是调度器的核心职责。
125
+ *
126
+ * 传入的 AbortSignal 仅用于提前退出本地排水循环;
127
+ * 远端 agent 的停止由 executionService.abortRunControllers 通过 "/stop" 命令处理。
128
+ */
129
+ const drainPipeline = async (reason, signal) => {
130
+ const manualTick = reason.startsWith("manual_tick");
131
+ const forceBatchDrain = reason.startsWith("batch:");
132
+ const explicitRunStart = reason === "run" || reason.startsWith("run:");
133
+ const pipelineLinkDrain = reason.startsWith("pipeline_link:");
134
+ const retryDrain = reason.startsWith("retry:");
135
+ if (!manualTick && !forceBatchDrain && !explicitRunStart && !pipelineLinkDrain && !retryDrain) {
136
+ if (!isSchedulerPluginEnabled())
137
+ return { executed: 0, hardFailed: false };
138
+ if (!schedulerState.enabled)
139
+ return { executed: 0, hardFailed: false };
140
+ if (schedulerState.mode === "manual")
141
+ return { executed: 0, hardFailed: false };
142
+ }
143
+ if (drainInFlight) {
144
+ deps.runtimeStore.pushTimeline(`[调度排水锁] 调用方=${reason} 触发排水,但已有 ${drainInFlightReason} 排水正在执行,本次调用合并至现有排水`, "info");
145
+ return drainInFlight;
146
+ }
147
+ drainInFlight = (async () => {
148
+ drainInFlightReason = reason;
149
+ let executed = 0;
150
+ let hardFailed = false;
151
+ const maxIterations = deps.graph.getWorkflow().scheduler.loopGuard.maxGlobalIterations;
152
+ const maxConcurrency = Math.max(1, deps.graph.getWorkflow().scheduler.maxConcurrency || 1);
153
+ const active = new Set();
154
+ let stopScheduling = false;
155
+ const launchItem = (item) => {
156
+ const task = ("nodeId" in item ? deps.executionService.executeNodeItem(item) : deps.executionService.executeGroupItem(item))
157
+ .then((result) => ({ item, result }))
158
+ .finally(() => {
159
+ active.delete(task);
160
+ });
161
+ active.add(task);
162
+ };
163
+ while (true) {
164
+ // 客户端侧中止:外部调用 abortRunControllers 后,signal 变为 aborted,
165
+ // 排水循环提前退出,不再等待活跃节点自然完成。
166
+ if (signal?.aborted) {
167
+ stopScheduling = true;
168
+ }
169
+ if (executed >= maxIterations) {
170
+ deps.runtimeStore.pushTimeline(`调度达到全局迭代上限: ${maxIterations}`, "warn");
171
+ break;
172
+ }
173
+ while (!stopScheduling && !signal?.aborted && active.size < maxConcurrency && executed < maxIterations) {
174
+ markReadyItemsFromDependencies();
175
+ markReadyGroupsFromDependencies();
176
+ const batch = pickNextRunnableBatch(maxConcurrency - active.size);
177
+ if (batch.length === 0)
178
+ break;
179
+ const batchLabel = batch
180
+ .map((item) => ("nodeId" in item ? `${item.nodeId}#${item.itemKey}` : `group:${item.groupId}#${item.itemKey}`))
181
+ .join(", ");
182
+ deps.runtimeStore.pushTimeline(`流水线自动调度: ${batchLabel} (${reason})`);
183
+ for (const item of batch) {
184
+ launchItem(item);
185
+ }
186
+ executed += batch.length;
187
+ if (manualTick) {
188
+ stopScheduling = true;
189
+ break;
190
+ }
191
+ }
192
+ if (active.size === 0)
193
+ break;
194
+ const settled = await Promise.race(active);
195
+ if (signal?.aborted) {
196
+ stopScheduling = true;
197
+ }
198
+ if (!settled.result.ok && settled.result.finalStatus !== "rejected") {
199
+ const shouldHalt = settled.result.haltPipeline !== false;
200
+ if (shouldHalt) {
201
+ hardFailed = true;
202
+ stopScheduling = true;
203
+ }
204
+ }
205
+ if (manualTick && stopScheduling) {
206
+ await Promise.all(active);
207
+ break;
208
+ }
209
+ }
210
+ (0, runtime_model_1.syncRunNodeStatusFromItemRuns)(getRun());
211
+ (0, runtime_model_1.touchRun)(getRun());
212
+ deps.runtimeStore.emitPipeline();
213
+ deps.onRunCompleted?.(getRun());
214
+ return { executed, hardFailed };
215
+ })().finally(() => {
216
+ drainInFlight = null;
217
+ drainInFlightReason = null;
218
+ });
219
+ return drainInFlight;
220
+ };
221
+ /**
222
+ * 批量运行控制器。管理关键词池的分批执行生命周期(启动、停止、取消、状态查询)。
223
+ * 属于调度器职责——控制何时及如何分批执行。
224
+ */
225
+ const itemBatchController = (0, item_batch_controller_1.createItemBatchController)({
226
+ pipelineId: deps.pipelineId,
227
+ executeBatch: async ({ batchItems, batchIndex, totalBatches, totalItems }) => {
228
+ const batchItemKey = (0, identity_1.buildBatchItemKey)(batchIndex);
229
+ const nextRun = deps.runtimeStore.seedRun(deps.graph.getTemplateNodes(), [batchItemKey]);
230
+ deps.runtimeStore.setRun(nextRun);
231
+ deps.executionService.setActiveBatchKeywordItems([...batchItems]);
232
+ deps.graph.syncRunGroupsFromWorkflow(nextRun);
233
+ deps.runtimeStore.emitPipeline();
234
+ deps.runtimeStore.pushTimeline(`批量运行开始: 第 ${batchIndex}/${totalBatches} 批, 关键词 ${batchItems.length}/${totalItems}`);
235
+ let drained;
236
+ try {
237
+ const drainSignal = deps.executionService.getOrCreateDrainSignal(getRun().id);
238
+ drained = await drainPipeline(`batch:${batchIndex}/${totalBatches}`, drainSignal);
239
+ (0, runtime_model_1.touchRun)(getRun());
240
+ deps.runtimeStore.emitPipeline();
241
+ }
242
+ finally {
243
+ deps.executionService.setActiveBatchKeywordItems(null);
244
+ }
245
+ if (getRun().status === "running") {
246
+ deps.runtimeStore.pushTimeline(`批量运行结束: 第 ${batchIndex} 批无后续可执行节点,直接进入下一批`, "warn", {
247
+ batchIndex,
248
+ totalBatches,
249
+ drained,
250
+ runId: getRun().id,
251
+ });
252
+ return { ok: true };
253
+ }
254
+ // 仅在硬失败场景停止后续批次;业务型 failed(status=failed) 允许继续下一批。
255
+ if (drained.hardFailed) {
256
+ deps.runtimeStore.pushTimeline(`批量运行失败: 第 ${batchIndex} 批, 已停止后续批次`, "error", {
257
+ batchIndex,
258
+ totalBatches,
259
+ drained,
260
+ runId: getRun().id,
261
+ });
262
+ return { ok: false, error: `batch_${batchIndex}_failed`, hardStop: true };
263
+ }
264
+ deps.runtimeStore.pushTimeline(`批量运行完成: 第 ${batchIndex}/${totalBatches} 批, run=${getRun().id}`);
265
+ return { ok: true };
266
+ },
267
+ });
268
+ return {
269
+ getSchedulerState: () => ({
270
+ ...schedulerState,
271
+ // 调度器插件关闭时,对外统一表现为 disabled,避免界面和运行时状态不一致。
272
+ enabled: isSchedulerPluginEnabled() && schedulerState.enabled,
273
+ }),
274
+ setSchedulerEnabled: (enabled) => {
275
+ schedulerState.enabled = enabled;
276
+ },
277
+ setSchedulerMode: (mode) => {
278
+ schedulerState.mode = mode;
279
+ },
280
+ syncSchedulerStateFromWorkflow: () => {
281
+ schedulerState.enabled = deps.graph.getWorkflow().scheduler.enabled;
282
+ schedulerState.mode = deps.graph.getWorkflow().scheduler.mode;
283
+ },
284
+ getBatchRunState: () => itemBatchController.getSnapshot(),
285
+ startBatchRun: (items, batchSize, options) => {
286
+ const started = itemBatchController.start(items, batchSize, options);
287
+ if (started.ok) {
288
+ deps.runtimeStore.pushTimeline(`已启动关键词池批量运行: 共 ${started.snapshot.totalItems} 个, 每批 ${started.snapshot.batchSize} 个`);
289
+ }
290
+ return started;
291
+ },
292
+ stopBatchRun: () => {
293
+ const stopped = itemBatchController.stop();
294
+ if (stopped.ok) {
295
+ deps.runtimeStore.pushTimeline("已请求停止关键词池批量运行(当前批次结束后生效)", "warn");
296
+ }
297
+ return stopped;
298
+ },
299
+ cancelBatchRun: () => {
300
+ const canceled = itemBatchController.cancel();
301
+ if (canceled.ok) {
302
+ deps.runtimeStore.pushTimeline("已立即停止关键词池批量运行(插件已关闭)", "warn");
303
+ }
304
+ return canceled;
305
+ },
306
+ markReadyItemsFromDependencies,
307
+ markReadyGroupsFromDependencies,
308
+ retryNodeExecution,
309
+ drainPipeline,
310
+ abortRunControllers: deps.executionService.abortRunControllers,
311
+ getOrCreateDrainSignal: deps.executionService.getOrCreateDrainSignal,
312
+ };
313
+ };
314
+ exports.createSchedulerService = createSchedulerService;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.markGroupItemReset = exports.markGroupItemWaiting = exports.markGroupItemBlocked = exports.markGroupItemSkipped = exports.markGroupItemFailed = exports.markGroupItemSuccess = exports.markGroupItemRunning = exports.markGroupItemQueued = void 0;
4
+ const state_machine_1 = require("../state-machine");
5
+ const now = (ctx) => ctx.now ?? new Date().toISOString();
6
+ const markGroupItemQueued = (item, ctx) => {
7
+ item.status = (0, state_machine_1.transitionStatus)(item.status, "queued", ctx.command);
8
+ };
9
+ exports.markGroupItemQueued = markGroupItemQueued;
10
+ const markGroupItemRunning = (item, ctx) => {
11
+ item.status = (0, state_machine_1.transitionStatus)(item.status, "running", ctx.command);
12
+ item.attempt += 1;
13
+ item.startedAt = now(ctx);
14
+ item.finishedAt = null;
15
+ item.lastError = null;
16
+ };
17
+ exports.markGroupItemRunning = markGroupItemRunning;
18
+ const markGroupItemSuccess = (item, ctx) => {
19
+ item.status = (0, state_machine_1.transitionStatus)(item.status, "success", ctx.command);
20
+ item.finishedAt = now(ctx);
21
+ item.lastError = null;
22
+ };
23
+ exports.markGroupItemSuccess = markGroupItemSuccess;
24
+ const markGroupItemFailed = (item, ctx) => {
25
+ item.status = (0, state_machine_1.transitionStatus)(item.status, "failed", ctx.command);
26
+ item.finishedAt = now(ctx);
27
+ item.lastError = ctx.error ?? null;
28
+ };
29
+ exports.markGroupItemFailed = markGroupItemFailed;
30
+ const markGroupItemSkipped = (item, ctx) => {
31
+ item.status = (0, state_machine_1.transitionStatus)(item.status, "skipped", ctx.command);
32
+ item.finishedAt = now(ctx);
33
+ item.lastError = null;
34
+ };
35
+ exports.markGroupItemSkipped = markGroupItemSkipped;
36
+ const markGroupItemBlocked = (item, ctx) => {
37
+ item.status = (0, state_machine_1.transitionStatus)(item.status, "blocked", ctx.command);
38
+ };
39
+ exports.markGroupItemBlocked = markGroupItemBlocked;
40
+ const markGroupItemWaiting = (item, ctx) => {
41
+ item.status = (0, state_machine_1.transitionStatus)(item.status, "waiting", ctx.command);
42
+ };
43
+ exports.markGroupItemWaiting = markGroupItemWaiting;
44
+ const markGroupItemReset = (item, targetStatus, ctx) => {
45
+ item.status = (0, state_machine_1.transitionStatus)(item.status, targetStatus, ctx.command);
46
+ item.startedAt = null;
47
+ item.finishedAt = targetStatus === "skipped" ? now(ctx) : null;
48
+ item.lastError = null;
49
+ };
50
+ exports.markGroupItemReset = markGroupItemReset;