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,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.stopPipelineBySelector = exports.getPipelineStatusBySelector = exports.describePipelineSelector = exports.readPipelineSelector = exports.pickOptionalStringFlag = void 0;
4
+ const errors_1 = require("../../errors");
5
+ const pickOptionalStringFlag = (value) => {
6
+ if (typeof value !== "string")
7
+ return undefined;
8
+ const normalized = value.trim();
9
+ return normalized || undefined;
10
+ };
11
+ exports.pickOptionalStringFlag = pickOptionalStringFlag;
12
+ const readPipelineSelector = (input) => {
13
+ const pipelineId = input.args[0]?.trim() ? input.args[0].trim() : undefined;
14
+ const runId = (0, exports.pickOptionalStringFlag)(input.flags["run-id"]);
15
+ const batchRunId = (0, exports.pickOptionalStringFlag)(input.flags["batch-run-id"]);
16
+ if (pipelineId || runId || batchRunId) {
17
+ return { pipelineId, runId, batchRunId };
18
+ }
19
+ throw new errors_1.CliError("Missing pipeline selector: provide <pipelineId>, --run-id, or --batch-run-id", {
20
+ code: "INVALID_ARGUMENT",
21
+ exitCode: 2,
22
+ });
23
+ };
24
+ exports.readPipelineSelector = readPipelineSelector;
25
+ const describePipelineSelector = (selector) => {
26
+ if (selector.runId)
27
+ return `runId=${selector.runId}`;
28
+ if (selector.batchRunId)
29
+ return `batchRunId=${selector.batchRunId}`;
30
+ if (selector.pipelineId)
31
+ return `pipelineId=${selector.pipelineId}`;
32
+ return "pipeline selector";
33
+ };
34
+ exports.describePipelineSelector = describePipelineSelector;
35
+ const getPipelineStatusBySelector = async (ctx, selector) => {
36
+ const getPipelineStatus = ctx.app.pipelineService.getPipelineStatus;
37
+ if (!selector.runId && !selector.batchRunId && selector.pipelineId) {
38
+ return await getPipelineStatus(selector.pipelineId);
39
+ }
40
+ // daemon-first 迁移期允许 CLI 把 selector 整体下发,避免 status/watch/stop 再依赖“当前 pipeline 活跃运行”的隐式猜测。
41
+ return await getPipelineStatus(selector);
42
+ };
43
+ exports.getPipelineStatusBySelector = getPipelineStatusBySelector;
44
+ const stopPipelineBySelector = async (ctx, selector) => {
45
+ const stopPipeline = ctx.app.pipelineService.stopPipeline;
46
+ if (!selector.runId && !selector.batchRunId && selector.pipelineId) {
47
+ return await stopPipeline(selector.pipelineId);
48
+ }
49
+ return await stopPipeline(selector);
50
+ };
51
+ exports.stopPipelineBySelector = stopPipelineBySelector;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.watchPipelineUntilTerminal = void 0;
4
+ const errors_1 = require("../../errors");
5
+ const errors_2 = require("./errors");
6
+ const selector_1 = require("./selector");
7
+ const readFlagAsPositiveInteger = (value, fallback) => {
8
+ if (typeof value !== "string")
9
+ return fallback;
10
+ const parsed = Number.parseInt(value.trim(), 10);
11
+ if (!Number.isFinite(parsed) || parsed <= 0)
12
+ return fallback;
13
+ return parsed;
14
+ };
15
+ const delay = async (ms) => {
16
+ await new Promise((resolve) => setTimeout(resolve, ms));
17
+ };
18
+ const isTerminalStatus = (statusPayload) => {
19
+ if (statusPayload.ok === true && statusPayload.running === false)
20
+ return true;
21
+ const status = statusPayload.status;
22
+ if (!status)
23
+ return false;
24
+ if (status.running === false)
25
+ return true;
26
+ const runStatus = String(status.runStatus ?? "");
27
+ if (runStatus === "success" || runStatus === "failed" || runStatus === "stopped")
28
+ return true;
29
+ const batchStatus = String(status.batchRun?.status ?? "");
30
+ return batchStatus === "completed" || batchStatus === "failed" || batchStatus === "stopped";
31
+ };
32
+ const watchPipelineUntilTerminal = async (ctx, selector, timeoutFlag, intervalFlag) => {
33
+ const timeoutMs = readFlagAsPositiveInteger(timeoutFlag, 10 * 60 * 1000);
34
+ const intervalMs = readFlagAsPositiveInteger(intervalFlag, 1200);
35
+ const startAt = Date.now();
36
+ const selectorLabel = (0, selector_1.describePipelineSelector)(selector);
37
+ let wsSignalAvailable = typeof ctx.app.pipelineService.waitForPipelineWatchSignal === "function";
38
+ while (true) {
39
+ const statusResult = await (0, selector_1.getPipelineStatusBySelector)(ctx, selector);
40
+ if (statusResult.ok === false) {
41
+ (0, errors_2.throwSelectorScopedError)(statusResult, selector);
42
+ }
43
+ if (isTerminalStatus(statusResult)) {
44
+ return statusResult;
45
+ }
46
+ if (Date.now() - startAt > timeoutMs) {
47
+ throw new errors_1.CliError(`Pipeline watch timed out: ${selectorLabel}`, {
48
+ code: "PIPELINE_WATCH_TIMEOUT",
49
+ exitCode: 4,
50
+ details: { ...selector, timeoutMs },
51
+ });
52
+ }
53
+ if (wsSignalAvailable) {
54
+ try {
55
+ // daemon-first 路径优先基于 WS 事件触发下一次状态确认,减少无效轮询;
56
+ // 事件链路抖动时自动降级为 interval 轮询,避免 watch 因订阅失败直接中断。
57
+ await ctx.app.pipelineService.waitForPipelineWatchSignal?.(selector, Math.max(1, Math.min(intervalMs, timeoutMs - (Date.now() - startAt))));
58
+ continue;
59
+ }
60
+ catch {
61
+ wsSignalAvailable = false;
62
+ }
63
+ }
64
+ await delay(intervalMs);
65
+ }
66
+ };
67
+ exports.watchPipelineUntilTerminal = watchPipelineUntilTerminal;
@@ -0,0 +1,339 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pipelineRoutes = exports.pipelineDiagnoseCommand = exports.pipelineRetryNodeCommand = exports.pipelineStopCommand = exports.pipelineWatchCommand = exports.pipelineStatusCommand = exports.pipelineStartCommand = exports.pipelineGetCommand = exports.pipelineListCommand = void 0;
4
+ const errors_1 = require("../errors");
5
+ const errors_2 = require("./pipeline/errors");
6
+ const result_1 = require("./pipeline/result");
7
+ const selector_1 = require("./pipeline/selector");
8
+ const watch_1 = require("./pipeline/watch");
9
+ const pipelineListCommand = async (_input, ctx) => {
10
+ return ctx.app.pipelineService.listPipelines();
11
+ };
12
+ exports.pipelineListCommand = pipelineListCommand;
13
+ const pipelineGetCommand = async (input, ctx) => {
14
+ const pipelineId = (0, errors_1.assertRequiredArg)(input.args[0], "pipelineId");
15
+ const detail = await ctx.app.pipelineService.getPipelineById(pipelineId);
16
+ if (!detail) {
17
+ throw new errors_1.CliError(`Pipeline not found: ${pipelineId}`, {
18
+ code: "PIPELINE_NOT_FOUND",
19
+ exitCode: 3,
20
+ details: { pipelineId },
21
+ });
22
+ }
23
+ return detail;
24
+ };
25
+ exports.pipelineGetCommand = pipelineGetCommand;
26
+ const pipelineStartCommand = async (input, ctx) => {
27
+ const pipelineId = (0, errors_1.assertRequiredArg)(input.args[0], "pipelineId");
28
+ const result = await ctx.app.pipelineService.startPipeline(pipelineId);
29
+ const data = result;
30
+ if (data.ok === false && data.error === "pipeline_not_found") {
31
+ throw new errors_1.CliError(`Pipeline not found: ${pipelineId}`, {
32
+ code: "PIPELINE_NOT_FOUND",
33
+ exitCode: 3,
34
+ details: { pipelineId },
35
+ });
36
+ }
37
+ if (data.ok === false && data.error === "batch_run_in_progress") {
38
+ throw new errors_1.CliError(`Batch run already in progress: ${pipelineId}`, {
39
+ code: "BATCH_RUN_IN_PROGRESS",
40
+ exitCode: 4,
41
+ details: { pipelineId, state: data.state ?? null },
42
+ });
43
+ }
44
+ if (data.ok === false) {
45
+ throw new errors_1.CliError(`Pipeline run failed: ${data.error ?? "unknown_error"}`, {
46
+ code: String(data.error ?? "PIPELINE_RUN_FAILED").toUpperCase(),
47
+ exitCode: 4,
48
+ details: {
49
+ pipelineId,
50
+ state: data.state ?? null,
51
+ remoteUrl: data.remoteUrl ?? null,
52
+ status: data.status ?? null,
53
+ detail: data.detail ?? null,
54
+ },
55
+ });
56
+ }
57
+ const shouldWatch = input.flags.watch === true || String(input.flags.watch ?? "").trim().toLowerCase() === "true";
58
+ if (shouldWatch) {
59
+ const watchResult = await (0, watch_1.watchPipelineUntilTerminal)(ctx, { pipelineId }, input.flags.timeout, input.flags.interval);
60
+ return {
61
+ ...result,
62
+ watch: watchResult,
63
+ };
64
+ }
65
+ return result;
66
+ };
67
+ exports.pipelineStartCommand = pipelineStartCommand;
68
+ const pipelineStatusCommand = async (input, ctx) => {
69
+ const selector = (0, selector_1.readPipelineSelector)(input);
70
+ const result = await (0, selector_1.getPipelineStatusBySelector)(ctx, selector);
71
+ if (result.ok === false) {
72
+ (0, errors_2.throwSelectorScopedError)(result, selector);
73
+ }
74
+ return result;
75
+ };
76
+ exports.pipelineStatusCommand = pipelineStatusCommand;
77
+ const pipelineWatchCommand = async (input, ctx) => {
78
+ const selector = (0, selector_1.readPipelineSelector)(input);
79
+ return (0, watch_1.watchPipelineUntilTerminal)(ctx, selector, input.flags.timeout, input.flags.interval);
80
+ };
81
+ exports.pipelineWatchCommand = pipelineWatchCommand;
82
+ const pipelineStopCommand = async (input, ctx) => {
83
+ const selector = (0, selector_1.readPipelineSelector)(input);
84
+ const result = await (0, selector_1.stopPipelineBySelector)(ctx, selector);
85
+ if (result.ok === false && (result.error === "pipeline_not_found" || result.error === "run_not_found" || result.error === "batch_run_not_found")) {
86
+ (0, errors_2.throwSelectorScopedError)(result, selector);
87
+ }
88
+ if (result.ok === false && result.error === "batch_run_not_running") {
89
+ throw new errors_1.CliError(`Batch run not running: ${(0, selector_1.describePipelineSelector)(selector)}`, {
90
+ code: "BATCH_RUN_NOT_RUNNING",
91
+ exitCode: 4,
92
+ details: { ...selector, status: result.status ?? null },
93
+ });
94
+ }
95
+ return result;
96
+ };
97
+ exports.pipelineStopCommand = pipelineStopCommand;
98
+ const pipelineRetryNodeCommand = async (input, ctx) => {
99
+ const pipelineId = (0, errors_1.assertRequiredArg)(input.args[0], "pipelineId");
100
+ const nodeId = (0, errors_1.assertRequiredArg)(input.args[1], "nodeId");
101
+ const itemKey = (0, selector_1.pickOptionalStringFlag)(input.flags.item);
102
+ const result = await ctx.app.pipelineService.retryNode({ pipelineId, nodeId, itemKey });
103
+ const data = result;
104
+ if (data.ok === false && data.error === "pipeline_not_found") {
105
+ throw new errors_1.CliError(`Pipeline not found: ${pipelineId}`, {
106
+ code: "PIPELINE_NOT_FOUND",
107
+ exitCode: 3,
108
+ details: { pipelineId },
109
+ });
110
+ }
111
+ if (data.retry?.ok === false && data.retry.error === "node_not_found") {
112
+ throw new errors_1.CliError(`Node not found: ${nodeId}`, {
113
+ code: "NODE_NOT_FOUND",
114
+ exitCode: 3,
115
+ details: { pipelineId, nodeId, itemKey: itemKey ?? null },
116
+ });
117
+ }
118
+ if (data.retry?.ok === false) {
119
+ throw new errors_1.CliError(`Retry node failed: ${data.retry.error ?? "unknown_error"}`, {
120
+ code: "RETRY_NODE_FAILED",
121
+ exitCode: 4,
122
+ details: { pipelineId, nodeId, itemKey: itemKey ?? null, result },
123
+ });
124
+ }
125
+ return result;
126
+ };
127
+ exports.pipelineRetryNodeCommand = pipelineRetryNodeCommand;
128
+ const pipelineDiagnoseCommand = async (input, ctx) => {
129
+ const pipelineId = (0, errors_1.assertRequiredArg)(input.args[0], "pipelineId");
130
+ const nodeId = (0, errors_1.assertRequiredArg)(input.args[1], "nodeId");
131
+ const itemKey = (0, selector_1.pickOptionalStringFlag)(input.flags.item);
132
+ const result = await ctx.app.pipelineService.diagnoseNode({ pipelineId, nodeId, itemKey });
133
+ const data = result;
134
+ if (!data.diagnostics || (Array.isArray(data.diagnostics) && data.diagnostics.length === 0)) {
135
+ throw new errors_1.CliError(`No diagnostics available for node: ${nodeId}`, {
136
+ code: "DIAGNOSTICS_UNAVAILABLE",
137
+ exitCode: 3,
138
+ details: { pipelineId, nodeId, itemKey: itemKey ?? null },
139
+ });
140
+ }
141
+ return result;
142
+ };
143
+ exports.pipelineDiagnoseCommand = pipelineDiagnoseCommand;
144
+ exports.pipelineRoutes = [
145
+ ...result_1.pipelineResultRoutes,
146
+ {
147
+ key: "pipeline.list",
148
+ path: ["pipeline", "list"],
149
+ description: "输出流水线列表",
150
+ handler: exports.pipelineListCommand,
151
+ help: {
152
+ usage: "taskmeld pipeline list [--format <json|md>]",
153
+ summary: "输出流水线列表",
154
+ },
155
+ },
156
+ {
157
+ key: "pipeline.get",
158
+ path: ["pipeline", "get"],
159
+ description: "输出指定流水线详情",
160
+ handler: exports.pipelineGetCommand,
161
+ help: {
162
+ usage: "taskmeld pipeline get <id> [--format <json|md>]",
163
+ args: [{ name: "id", required: true, description: "流水线 ID" }],
164
+ summary: "输出指定流水线详情",
165
+ },
166
+ },
167
+ {
168
+ key: "pipeline.start",
169
+ path: ["pipeline", "start"],
170
+ description: "发起指定流水线运行(不承诺已完成)",
171
+ handler: exports.pipelineStartCommand,
172
+ bootstrap: { runtimeApiOnly: true, ensureServerReady: true },
173
+ help: {
174
+ usage: "taskmeld pipeline start <pipelineId> [--watch] [--timeout <ms>] [--interval <ms>] [--format <json|md>]",
175
+ args: [{ name: "pipelineId", required: true, description: "流水线 ID" }],
176
+ options: [
177
+ { flags: ["--watch"], description: "启动后继续等待运行完成" },
178
+ { flags: ["--timeout"], valueName: "ms", description: "watch 超时时间,默认 600000" },
179
+ { flags: ["--interval"], valueName: "ms", description: "watch 轮询间隔,默认 1200" },
180
+ ],
181
+ notes: [
182
+ "start 只负责发起运行请求,不承诺命令返回时业务已执行完成。",
183
+ "--watch 会在发起成功后进入等待流程。",
184
+ ],
185
+ },
186
+ },
187
+ {
188
+ key: "pipeline.status",
189
+ path: ["pipeline", "status"],
190
+ description: "输出指定流水线当前运行状态",
191
+ handler: exports.pipelineStatusCommand,
192
+ bootstrap: { runtimeApiOnly: true, ensureServerReady: true },
193
+ help: {
194
+ usage: "taskmeld pipeline status [<pipelineId>] [--run-id <id>] [--batch-run-id <id>] [--format <json|md>]",
195
+ args: [{ name: "pipelineId", description: "兼容入口,按流水线 ID 查询当前运行" }],
196
+ options: [
197
+ { flags: ["--run-id"], valueName: "id", description: "按单次运行 ID 精确查询" },
198
+ { flags: ["--batch-run-id"], valueName: "id", description: "按批跑 ID 精确查询" },
199
+ ],
200
+ examples: [
201
+ "taskmeld pipeline status A",
202
+ "taskmeld pipeline status A --run-id run-123",
203
+ "taskmeld pipeline status A --batch-run-id batch:A:2026-05-08T18:34:08.978Z",
204
+ ],
205
+ notes: [
206
+ "需要至少提供 <pipelineId>、--run-id、--batch-run-id 之一。",
207
+ "未提供 selector 时,默认按 pipelineId 查询当前活动运行。",
208
+ ],
209
+ },
210
+ },
211
+ {
212
+ key: "pipeline.watch",
213
+ path: ["pipeline", "watch"],
214
+ description: "监听指定流水线直到结束或超时",
215
+ handler: exports.pipelineWatchCommand,
216
+ bootstrap: { runtimeApiOnly: true, ensureServerReady: true },
217
+ help: {
218
+ usage: "taskmeld pipeline watch [<pipelineId>] [--run-id <id>] [--batch-run-id <id>] [--timeout <ms>] [--interval <ms>] [--format <json|md>]",
219
+ args: [{ name: "pipelineId", description: "兼容入口,监听该流水线当前运行" }],
220
+ options: [
221
+ { flags: ["--run-id"], valueName: "id", description: "按单次运行 ID 精确监听" },
222
+ { flags: ["--batch-run-id"], valueName: "id", description: "按批跑 ID 精确监听" },
223
+ { flags: ["--timeout"], valueName: "ms", description: "watch 超时时间,默认 600000" },
224
+ { flags: ["--interval"], valueName: "ms", description: "watch 轮询间隔,默认 1200" },
225
+ ],
226
+ examples: [
227
+ "taskmeld pipeline watch A",
228
+ "taskmeld pipeline watch --run-id run-123",
229
+ "taskmeld pipeline watch --batch-run-id batch:A:2026-05-08T18:34:08.978Z --timeout 900000",
230
+ ],
231
+ notes: [
232
+ "watch 是监听命令,不负责发起新运行。",
233
+ "需要至少提供 <pipelineId>、--run-id、--batch-run-id 之一。",
234
+ "当前 watch 语义为事件流优先,轮询作为兜底路径。",
235
+ ],
236
+ },
237
+ },
238
+ {
239
+ key: "pipeline.stop",
240
+ path: ["pipeline", "stop"],
241
+ description: "停止指定流水线批跑任务",
242
+ handler: exports.pipelineStopCommand,
243
+ bootstrap: { runtimeApiOnly: true, ensureServerReady: true },
244
+ help: {
245
+ usage: "taskmeld pipeline stop [<pipelineId>] [--run-id <id>] [--batch-run-id <id>] [--format <json|md>]",
246
+ args: [{ name: "pipelineId", description: "兼容入口,停止该流水线当前运行" }],
247
+ options: [
248
+ { flags: ["--run-id"], valueName: "id", description: "按单次运行 ID 精确停止" },
249
+ { flags: ["--batch-run-id"], valueName: "id", description: "按批跑 ID 精确停止" },
250
+ ],
251
+ notes: [
252
+ "需要至少提供 <pipelineId>、--run-id、--batch-run-id 之一。",
253
+ "当前 stop 主要用于停止批跑任务。",
254
+ "对非批跑运行会返回业务错误,而不是静默成功。",
255
+ ],
256
+ },
257
+ },
258
+ {
259
+ key: "pipeline.retry-node",
260
+ path: ["pipeline", "retry-node"],
261
+ description: "重试指定节点或节点条目",
262
+ handler: exports.pipelineRetryNodeCommand,
263
+ bootstrap: { gateway: "required" },
264
+ help: {
265
+ usage: "taskmeld pipeline retry-node <pipelineId> <nodeId> [--item <itemKey>] [--format <json|md>]",
266
+ args: [
267
+ { name: "pipelineId", required: true, description: "流水线 ID" },
268
+ { name: "nodeId", required: true, description: "节点 ID" },
269
+ ],
270
+ options: [{ flags: ["--item"], valueName: "itemKey", description: "指定重试的条目键" }],
271
+ summary: "重试指定节点或节点条目",
272
+ },
273
+ },
274
+ {
275
+ key: "pipeline.diagnose",
276
+ path: ["pipeline", "diagnose"],
277
+ description: "诊断指定节点依赖状态,输出阻塞原因",
278
+ handler: exports.pipelineDiagnoseCommand,
279
+ bootstrap: { runtimeApiOnly: true, ensureServerReady: true },
280
+ help: {
281
+ usage: "taskmeld pipeline diagnose <pipelineId> <nodeId> [--item <itemKey>] [--format <json|md>]",
282
+ args: [
283
+ { name: "pipelineId", required: true, description: "流水线 ID" },
284
+ { name: "nodeId", required: true, description: "节点 ID" },
285
+ ],
286
+ options: [{ flags: ["--item"], valueName: "itemKey", description: "可选,指定条目键精确诊断" }],
287
+ summary: "诊断指定节点依赖状态,输出阻塞原因",
288
+ },
289
+ },
290
+ {
291
+ key: "pipeline.output",
292
+ path: ["pipeline", "output"],
293
+ description: "查询流水线最终产物",
294
+ handler: async (input, ctx) => {
295
+ const pipelineId = (0, errors_1.assertRequiredArg)(input.args[0], "pipelineId");
296
+ const runId = (0, selector_1.pickOptionalStringFlag)(input.flags.run);
297
+ const output = await ctx.app.pipelineService.getOutput(pipelineId, runId);
298
+ if (!output) {
299
+ return { ok: true, pipelineId, items: [] };
300
+ }
301
+ const outputs = runId ? [output] : await ctx.app.pipelineService.listOutputs(pipelineId);
302
+ return { ok: true, pipelineId, items: outputs };
303
+ },
304
+ help: {
305
+ usage: "taskmeld pipeline output <pipelineId> [--run <runId>]",
306
+ args: [{ name: "pipelineId", required: true, description: "流水线 ID" }],
307
+ options: [{ flags: ["--run"], valueName: "runId", description: "按 runId 精确查询" }],
308
+ summary: "查询流水线最终产物",
309
+ },
310
+ },
311
+ {
312
+ key: "pipeline.link-list",
313
+ path: ["pipeline", "link", "list"],
314
+ description: "列出所有流水线投递链接",
315
+ handler: async (_input, ctx) => {
316
+ const links = await ctx.app.pipelineService.listLinks();
317
+ return { ok: true, items: links };
318
+ },
319
+ help: {
320
+ usage: "taskmeld pipeline link list",
321
+ summary: "列出所有流水线投递链接",
322
+ },
323
+ },
324
+ {
325
+ key: "pipeline.queue",
326
+ path: ["pipeline", "queue"],
327
+ description: "查询流水线接收队列",
328
+ handler: async (input, ctx) => {
329
+ const pipelineId = (0, errors_1.assertRequiredArg)(input.args[0], "pipelineId");
330
+ const items = ctx.app.pipelineService.getQueue(pipelineId);
331
+ return { ok: true, pipelineId, items };
332
+ },
333
+ help: {
334
+ usage: "taskmeld pipeline queue <pipelineId>",
335
+ args: [{ name: "pipelineId", required: true, description: "流水线 ID" }],
336
+ summary: "查询流水线接收队列",
337
+ },
338
+ },
339
+ ];
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.schedulerRoutes = exports.schedulerModeCommand = exports.schedulerToggleCommand = void 0;
4
+ const errors_1 = require("../errors");
5
+ const assertSchedulerMode = (value) => {
6
+ if (typeof value === "string") {
7
+ const normalized = value.trim().toLowerCase();
8
+ if (normalized === "auto" || normalized === "manual") {
9
+ return normalized;
10
+ }
11
+ }
12
+ throw new errors_1.CliError("Invalid scheduler mode: --mode", {
13
+ code: "INVALID_ARGUMENT",
14
+ exitCode: 2,
15
+ });
16
+ };
17
+ const normalizeSchedulerResult = (result, pipelineId) => {
18
+ const data = result;
19
+ if (data.ok === true) {
20
+ return result;
21
+ }
22
+ if (data.error === "pipeline_not_found") {
23
+ throw new errors_1.CliError(`Pipeline not found: ${pipelineId}`, {
24
+ code: "PIPELINE_NOT_FOUND",
25
+ exitCode: 3,
26
+ details: { pipelineId },
27
+ });
28
+ }
29
+ if (data.error === "pipeline_plugin_disabled") {
30
+ throw new errors_1.CliError(`Scheduler plugin disabled: ${pipelineId}`, {
31
+ code: "PIPELINE_PLUGIN_DISABLED",
32
+ exitCode: 4,
33
+ details: { pipelineId, plugin: data.plugin ?? "scheduler" },
34
+ });
35
+ }
36
+ throw new errors_1.CliError("Scheduler command failed", {
37
+ code: "SCHEDULER_COMMAND_FAILED",
38
+ exitCode: 4,
39
+ details: { pipelineId, result },
40
+ });
41
+ };
42
+ const schedulerToggleCommand = async (input, ctx) => {
43
+ const pipelineId = (0, errors_1.assertRequiredArg)(input.args[0], "pipelineId");
44
+ const enabled = (0, errors_1.assertBooleanFlag)(input.flags.enabled, "enabled");
45
+ const result = await ctx.app.schedulerService.toggleScheduler(pipelineId, enabled);
46
+ return normalizeSchedulerResult(result, pipelineId);
47
+ };
48
+ exports.schedulerToggleCommand = schedulerToggleCommand;
49
+ const schedulerModeCommand = async (input, ctx) => {
50
+ const pipelineId = (0, errors_1.assertRequiredArg)(input.args[0], "pipelineId");
51
+ const mode = assertSchedulerMode(input.flags.mode);
52
+ const result = await ctx.app.schedulerService.setSchedulerMode(pipelineId, mode);
53
+ return normalizeSchedulerResult(result, pipelineId);
54
+ };
55
+ exports.schedulerModeCommand = schedulerModeCommand;
56
+ exports.schedulerRoutes = [
57
+ {
58
+ key: "scheduler.toggle",
59
+ path: ["scheduler", "toggle"],
60
+ description: "启用或停用调度器",
61
+ handler: exports.schedulerToggleCommand,
62
+ help: {
63
+ usage: "taskmeld scheduler toggle <pipelineId> --enabled <true|false> [--format <json|md>]",
64
+ args: [{ name: "pipelineId", required: true, description: "流水线 ID" }],
65
+ options: [{ flags: ["--enabled"], valueName: "true|false", required: true, description: "是否启用调度器" }],
66
+ notes: ["<pipelineId> 与 --enabled 均为必填控制项。"],
67
+ },
68
+ },
69
+ {
70
+ key: "scheduler.mode",
71
+ path: ["scheduler", "mode"],
72
+ description: "切换调度器模式",
73
+ handler: exports.schedulerModeCommand,
74
+ help: {
75
+ usage: "taskmeld scheduler mode <pipelineId> --mode <auto|manual> [--format <json|md>]",
76
+ args: [{ name: "pipelineId", required: true, description: "流水线 ID" }],
77
+ options: [{ flags: ["--mode"], valueName: "auto|manual", required: true, description: "调度器模式" }],
78
+ notes: ["<pipelineId> 与 --mode 均为必填控制项。"],
79
+ },
80
+ },
81
+ ];
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.serverRoutes = exports.serverStopCommand = exports.serverStatusCommand = exports.serverStartCommand = exports.serverEnsureCommand = void 0;
4
+ const serverEnsureCommand = async (_input, ctx) => {
5
+ return ctx.app.serverService.ensureServerReady();
6
+ };
7
+ exports.serverEnsureCommand = serverEnsureCommand;
8
+ const serverStartCommand = async (_input, ctx) => {
9
+ return ctx.app.serverService.startServer();
10
+ };
11
+ exports.serverStartCommand = serverStartCommand;
12
+ const serverStatusCommand = async (_input, ctx) => {
13
+ return ctx.app.serverService.getServerStatus();
14
+ };
15
+ exports.serverStatusCommand = serverStatusCommand;
16
+ const serverStopCommand = async (_input, ctx) => {
17
+ return ctx.app.serverService.stopServer();
18
+ };
19
+ exports.serverStopCommand = serverStopCommand;
20
+ exports.serverRoutes = [
21
+ {
22
+ key: "server.ensure",
23
+ path: ["server", "ensure"],
24
+ description: "确保本地控制面后端已启动并健康",
25
+ handler: exports.serverEnsureCommand,
26
+ bootstrap: { runtimeApiOnly: true },
27
+ help: {
28
+ usage: "taskmeld server ensure [--format <json|md>]",
29
+ summary: "确保本地 control-plane daemon 已启动并健康",
30
+ notes: [
31
+ "daemon-first 语义:优先复用已存在且健康的本地实例。",
32
+ "仅在没有可复用健康实例时,才会启动新的 daemon。",
33
+ ],
34
+ },
35
+ },
36
+ {
37
+ key: "server.start",
38
+ path: ["server", "start"],
39
+ description: "显式启动本地控制面后端",
40
+ handler: exports.serverStartCommand,
41
+ bootstrap: { runtimeApiOnly: true },
42
+ help: {
43
+ usage: "taskmeld server start [--format <json|md>]",
44
+ summary: "显式启动本地 control-plane daemon",
45
+ },
46
+ },
47
+ {
48
+ key: "server.status",
49
+ path: ["server", "status"],
50
+ description: "输出本地控制面后端状态",
51
+ handler: exports.serverStatusCommand,
52
+ bootstrap: { runtimeApiOnly: true },
53
+ help: {
54
+ usage: "taskmeld server status [--format <json|md>]",
55
+ summary: "查看本地 control-plane daemon 的健康、ownership 与 pid 信息",
56
+ notes: ["状态输出用于确认本地 daemon 是否可复用以及当前 owner 元数据是否一致。"],
57
+ },
58
+ },
59
+ {
60
+ key: "server.stop",
61
+ path: ["server", "stop"],
62
+ description: "停止本地控制面后端",
63
+ handler: exports.serverStopCommand,
64
+ bootstrap: { runtimeApiOnly: true },
65
+ help: {
66
+ usage: "taskmeld server stop [--format <json|md>]",
67
+ summary: "停止本地 control-plane daemon",
68
+ },
69
+ },
70
+ ];
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.systemRoutes = exports.systemSnapshotCommand = void 0;
4
+ const systemSnapshotCommand = async (_input, ctx) => {
5
+ // 系统快照由主线 service 层提供,CLI 只做编排与输出。
6
+ return ctx.app.systemService.getSnapshot();
7
+ };
8
+ exports.systemSnapshotCommand = systemSnapshotCommand;
9
+ exports.systemRoutes = [
10
+ {
11
+ key: "system.snapshot",
12
+ path: ["system", "snapshot"],
13
+ description: "输出系统全局快照",
14
+ handler: exports.systemSnapshotCommand,
15
+ bootstrap: { gateway: "warmup" },
16
+ help: {
17
+ usage: "taskmeld system snapshot [--format <json|md>]",
18
+ summary: "输出系统全局快照",
19
+ },
20
+ },
21
+ ];