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,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerPipelineQueueRoutes = void 0;
4
+ const registerPipelineQueueRoutes = (router) => {
5
+ // GET /api/pipelines/:pipelineId/queue
6
+ router.register("GET", "/api/pipelines/:pipelineId/queue", async (ctx) => {
7
+ const { app } = ctx.options;
8
+ const { pipelineId } = ctx.params;
9
+ if (!app.getPipelineDefinition(pipelineId)) {
10
+ ctx.sendJson(404, { ok: false, error: "pipeline_not_found", pipelineId });
11
+ return;
12
+ }
13
+ const items = app.dispatch.getQueue(pipelineId);
14
+ ctx.sendJson(200, { ok: true, pipelineId, items });
15
+ });
16
+ // POST /api/pipelines/:pipelineId/queue/:jobId/retry
17
+ router.register("POST", "/api/pipelines/:pipelineId/queue/:jobId/retry", async (ctx) => {
18
+ const { app } = ctx.options;
19
+ const { pipelineId, jobId } = ctx.params;
20
+ if (!app.getPipelineDefinition(pipelineId)) {
21
+ ctx.sendJson(404, { ok: false, error: "pipeline_not_found", pipelineId });
22
+ return;
23
+ }
24
+ const result = await app.dispatch.retryJob(jobId);
25
+ if (!result.ok) {
26
+ const statusCode = result.error === "pipeline_queue_job_not_found" ? 404 : 400;
27
+ ctx.sendJson(statusCode, { ok: false, error: result.error });
28
+ return;
29
+ }
30
+ ctx.sendJson(200, { ok: true, job: result.job });
31
+ });
32
+ // POST /api/pipelines/:pipelineId/queue/:jobId/cancel
33
+ router.register("POST", "/api/pipelines/:pipelineId/queue/:jobId/cancel", async (ctx) => {
34
+ const { app } = ctx.options;
35
+ const { pipelineId, jobId } = ctx.params;
36
+ if (!app.getPipelineDefinition(pipelineId)) {
37
+ ctx.sendJson(404, { ok: false, error: "pipeline_not_found", pipelineId });
38
+ return;
39
+ }
40
+ const body = await ctx.readBody();
41
+ const reason = typeof body.reason === "string" ? body.reason.trim() : "canceled_by_user";
42
+ const result = await app.dispatch.cancelJob(jobId, reason);
43
+ if (!result.ok) {
44
+ const statusCode = result.error === "pipeline_queue_job_not_found" ? 404 : 400;
45
+ ctx.sendJson(statusCode, { ok: false, error: result.error });
46
+ return;
47
+ }
48
+ ctx.sendJson(200, { ok: true });
49
+ });
50
+ // POST /api/pipelines/:pipelineId/queue/drain
51
+ router.register("POST", "/api/pipelines/:pipelineId/queue/drain", async (ctx) => {
52
+ const { app } = ctx.options;
53
+ const { pipelineId } = ctx.params;
54
+ if (!app.getPipelineDefinition(pipelineId)) {
55
+ ctx.sendJson(404, { ok: false, error: "pipeline_not_found", pipelineId });
56
+ return;
57
+ }
58
+ app.dispatch.drainQueue(pipelineId);
59
+ ctx.sendJson(200, { ok: true, pipelineId });
60
+ });
61
+ };
62
+ exports.registerPipelineQueueRoutes = registerPipelineQueueRoutes;
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerPipelineRuntimeRoutes = void 0;
4
+ const pipeline_identity_js_1 = require("./pipeline-identity.js");
5
+ /**
6
+ * 注册 Pipeline 运行态路由:
7
+ * GET /api/pipelines/:pipelineId/current — 当前运行信息
8
+ * GET /api/pipelines/:pipelineId/status — Pipeline 状态
9
+ * POST /api/pipelines/:pipelineId/run — 启动运行
10
+ * POST /api/pipelines/:pipelineId/stop — 停止运行(同时作为 /batch-run/stop 的别名入口)
11
+ * GET /api/pipelines/:pipelineId/executor-bindings — Executor 会话绑定
12
+ * POST /api/pipelines/:pipelineId/nodes/:nodeId/retry — 节点重试
13
+ */
14
+ const registerPipelineRuntimeRoutes = (router) => {
15
+ // GET /api/pipelines/:pipelineId/current
16
+ router.register("GET", "/api/pipelines/:pipelineId/current", (ctx) => {
17
+ const scope = ctx.getPipelineScope();
18
+ if (!scope) {
19
+ ctx.sendJson(404, { error: "pipeline_not_found", pipelineId: ctx.params.pipelineId });
20
+ return;
21
+ }
22
+ const run = scope.getRun();
23
+ scope.touchRun(run);
24
+ const workflow = scope.getWorkflow?.();
25
+ const nodes = workflow?.nodes && workflow.nodes.length > 0
26
+ ? run.nodes.map((node) => {
27
+ const matched = workflow.nodes.find((wNode) => wNode.id === node.id);
28
+ return {
29
+ ...node,
30
+ isMainline: matched?.isMainline ?? true,
31
+ lane: matched?.lane ?? "main",
32
+ parallelGroupId: matched?.parallelGroupId ?? null,
33
+ };
34
+ })
35
+ : run.nodes;
36
+ ctx.sendJson(200, {
37
+ run: { ...run, nodes },
38
+ runId: run.id,
39
+ nodes,
40
+ scheduler: scope.getSchedulerState ? scope.getSchedulerState() : null,
41
+ pipelineId: scope.pipelineId,
42
+ });
43
+ });
44
+ // GET /api/pipelines/:pipelineId/status
45
+ router.register("GET", "/api/pipelines/:pipelineId/status", (ctx) => {
46
+ const scope = ctx.getPipelineScope();
47
+ if (!scope) {
48
+ ctx.sendJson(404, { error: "pipeline_not_found", pipelineId: ctx.params.pipelineId });
49
+ return;
50
+ }
51
+ const { pipelineService } = ctx.services;
52
+ const result = pipelineService.getPipelineExecutionStatus(scope.pipelineId, (0, pipeline_identity_js_1.readIdentityTargetFromUrl)(ctx.url));
53
+ const statusCode = result.ok === false && result.error === "run_not_found" ? 404 : 200;
54
+ ctx.sendJson(statusCode, result);
55
+ });
56
+ // POST /api/pipelines/:pipelineId/run
57
+ router.register("POST", "/api/pipelines/:pipelineId/run", async (ctx) => {
58
+ const scope = ctx.getPipelineScope();
59
+ if (!scope) {
60
+ ctx.sendJson(404, { error: "pipeline_not_found", pipelineId: ctx.params.pipelineId });
61
+ return;
62
+ }
63
+ const { pipelineService } = ctx.services;
64
+ const started = await pipelineService.startPipeline(scope.pipelineId);
65
+ if (started.ok === false && started.error === "batch_run_in_progress") {
66
+ ctx.sendJson(409, { ok: false, error: started.error, state: started.state, pipelineId: scope.pipelineId });
67
+ return;
68
+ }
69
+ if (started.ok === false) {
70
+ const statusCode = started.error === "remote_pool_url_empty" || started.error === "remote_batch_items_empty"
71
+ ? 400
72
+ : started.error === "remote_pool_fetch_failed" || started.error === "remote_pool_fetch_error"
73
+ ? 502
74
+ : 409;
75
+ ctx.sendJson(statusCode, {
76
+ ok: false,
77
+ error: started.error,
78
+ state: started.state,
79
+ remoteUrl: started.remoteUrl,
80
+ status: started.status,
81
+ detail: started.detail,
82
+ pipelineId: scope.pipelineId,
83
+ });
84
+ return;
85
+ }
86
+ if (started.mode === "remote_batch") {
87
+ ctx.sendJson(200, {
88
+ ...started,
89
+ state: started.batchRun,
90
+ });
91
+ return;
92
+ }
93
+ ctx.sendJson(200, started);
94
+ });
95
+ // POST /api/pipelines/:pipelineId/stop
96
+ // 旧代码中 /stop 与 /batch-run/stop 共享同一处理逻辑;此处注册 /stop,/batch-run/stop 在 pipeline-batch.ts 中注册
97
+ router.register("POST", "/api/pipelines/:pipelineId/stop", async (ctx) => {
98
+ const scope = ctx.getPipelineScope();
99
+ if (!scope) {
100
+ ctx.sendJson(404, { error: "pipeline_not_found", pipelineId: ctx.params.pipelineId });
101
+ return;
102
+ }
103
+ const { pipelineService } = ctx.services;
104
+ const body = await ctx.readBody();
105
+ const target = (0, pipeline_identity_js_1.mergeIdentityTargets)((0, pipeline_identity_js_1.readIdentityTargetFromBody)(body), (0, pipeline_identity_js_1.readIdentityTargetFromUrl)(ctx.url));
106
+ const stopped = pipelineService.stopPipeline(scope.pipelineId, target);
107
+ if (stopped.ok === false && stopped.error === "run_not_found") {
108
+ ctx.sendJson(404, stopped);
109
+ return;
110
+ }
111
+ if (stopped.ok === false) {
112
+ ctx.sendJson(409, stopped);
113
+ return;
114
+ }
115
+ ctx.sendJson(200, {
116
+ ...stopped,
117
+ });
118
+ });
119
+ // GET /api/pipelines/:pipelineId/executor-bindings
120
+ router.register("GET", "/api/pipelines/:pipelineId/executor-bindings", (ctx) => {
121
+ const scope = ctx.getPipelineScope();
122
+ if (!scope) {
123
+ ctx.sendJson(404, { error: "pipeline_not_found", pipelineId: ctx.params.pipelineId });
124
+ return;
125
+ }
126
+ ctx.sendJson(200, {
127
+ bindings: Object.fromEntries(scope.executorSessionByAgentId.entries()),
128
+ sessions: scope.getSessionCache().map((s) => ({ id: s.id, title: s.title })),
129
+ pipelineId: scope.pipelineId,
130
+ });
131
+ });
132
+ // POST /api/pipelines/:pipelineId/nodes/:nodeId/retry
133
+ router.register("POST", "/api/pipelines/:pipelineId/nodes/:nodeId/retry", async (ctx) => {
134
+ const scope = ctx.getPipelineScope();
135
+ if (!scope) {
136
+ ctx.sendJson(404, { error: "pipeline_not_found", pipelineId: ctx.params.pipelineId });
137
+ return;
138
+ }
139
+ const { pipelineService } = ctx.services;
140
+ const body = await ctx.readBody();
141
+ const itemKey = typeof body.itemKey === "string" && body.itemKey.trim() ? body.itemKey.trim() : undefined;
142
+ const result = await pipelineService.retryNode({
143
+ pipelineId: scope.pipelineId,
144
+ nodeId: ctx.params.nodeId,
145
+ itemKey,
146
+ });
147
+ if (!result.ok) {
148
+ ctx.sendJson(404, result);
149
+ return;
150
+ }
151
+ if (!result.retry.ok && result.retry.error === "node_not_found") {
152
+ ctx.sendJson(404, { run: result.run, ...result.retry });
153
+ return;
154
+ }
155
+ if (!result.retry.ok && result.retry.error === "executor_session_not_found") {
156
+ ctx.sendJson(400, { run: result.run, ...result.retry });
157
+ return;
158
+ }
159
+ ctx.sendJson(200, { run: result.run, ...result.retry });
160
+ });
161
+ };
162
+ exports.registerPipelineRuntimeRoutes = registerPipelineRuntimeRoutes;
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerPipelineSchedulerRoutes = void 0;
4
+ /**
5
+ * 注册 Pipeline Scheduler 路由:
6
+ * POST /api/pipelines/:pipelineId/scheduler/toggle — 切换调度器开关
7
+ * POST /api/pipelines/:pipelineId/scheduler/mode — 设置调度模式
8
+ * POST /api/pipelines/:pipelineId/tick — 手动触发调度 tick
9
+ */
10
+ const registerPipelineSchedulerRoutes = (router) => {
11
+ // POST /api/pipelines/:pipelineId/scheduler/toggle
12
+ router.register("POST", "/api/pipelines/:pipelineId/scheduler/toggle", async (ctx) => {
13
+ const scope = ctx.getPipelineScope();
14
+ if (!scope) {
15
+ ctx.sendJson(404, { error: "pipeline_not_found", pipelineId: ctx.params.pipelineId });
16
+ return;
17
+ }
18
+ const { schedulerService } = ctx.services;
19
+ const body = await ctx.readBody();
20
+ const enabled = body.enabled !== false;
21
+ const toggled = schedulerService.toggleScheduler(scope.pipelineId, enabled);
22
+ if (!toggled.ok) {
23
+ // pipeline_plugin_disabled 时返回 403,pipeline_not_found 时返回 404
24
+ const statusCode = toggled.error === "pipeline_not_found" ? 404 : 403;
25
+ ctx.sendJson(statusCode, toggled);
26
+ return;
27
+ }
28
+ scope.pushTimeline(`[${scope.pipelineId}] 调度器已${toggled.scheduler.enabled ? "启用" : "停用"}`);
29
+ ctx.sendJson(200, toggled);
30
+ });
31
+ // POST /api/pipelines/:pipelineId/scheduler/mode
32
+ router.register("POST", "/api/pipelines/:pipelineId/scheduler/mode", async (ctx) => {
33
+ const scope = ctx.getPipelineScope();
34
+ if (!scope) {
35
+ ctx.sendJson(404, { error: "pipeline_not_found", pipelineId: ctx.params.pipelineId });
36
+ return;
37
+ }
38
+ const { schedulerService } = ctx.services;
39
+ const body = await ctx.readBody();
40
+ const mode = body.mode === "manual" ? "manual" : "auto";
41
+ const updated = schedulerService.setSchedulerMode(scope.pipelineId, mode);
42
+ if (!updated.ok) {
43
+ const statusCode = updated.error === "pipeline_not_found" ? 404 : 403;
44
+ ctx.sendJson(statusCode, updated);
45
+ return;
46
+ }
47
+ scope.pushTimeline(`[${scope.pipelineId}] 调度器模式切换为: ${updated.scheduler.mode}`);
48
+ ctx.sendJson(200, updated);
49
+ });
50
+ // POST /api/pipelines/:pipelineId/tick
51
+ router.register("POST", "/api/pipelines/:pipelineId/tick", async (ctx) => {
52
+ const scope = ctx.getPipelineScope();
53
+ if (!scope) {
54
+ ctx.sendJson(404, { error: "pipeline_not_found", pipelineId: ctx.params.pipelineId });
55
+ return;
56
+ }
57
+ const workflow = scope.getWorkflow?.();
58
+ // 保持 scheduler plugin disabled 时的 403 语义
59
+ if (!workflow || !workflow.plugins.scheduler.enabled) {
60
+ ctx.sendJson(403, { error: "pipeline_plugin_disabled", plugin: "scheduler", pipelineId: scope.pipelineId });
61
+ return;
62
+ }
63
+ const drained = await scope.drainPipeline("manual_tick");
64
+ const run = scope.getRun();
65
+ scope.touchRun(run);
66
+ ctx.sendJson(200, { ok: true, run, drained, pipelineId: scope.pipelineId });
67
+ });
68
+ };
69
+ exports.registerPipelineSchedulerRoutes = registerPipelineSchedulerRoutes;
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerPipelineWorkflowRoutes = void 0;
4
+ const template_js_1 = require("../../pipeline/template.js");
5
+ const pipeline_config_js_1 = require("../../app/pipeline-config.js");
6
+ // 将未知值安全转换为 Record<string, unknown>,非对象返回 null
7
+ const asRecord = (value) => value && typeof value === "object" ? value : null;
8
+ /**
9
+ * 注册 Pipeline Workflow / Template / Plugins 路由:
10
+ * GET /api/pipelines/:pipelineId/template — 读取模板节点
11
+ * GET /api/pipelines/:pipelineId/workflow — 读取工作流定义
12
+ * POST /api/pipelines/:pipelineId/workflow — 保存工作流定义
13
+ * GET /api/pipelines/:pipelineId/plugins — 读取插件配置
14
+ * POST /api/pipelines/:pipelineId/plugins — 写入插件配置
15
+ */
16
+ const registerPipelineWorkflowRoutes = (router) => {
17
+ // GET /api/pipelines/:pipelineId/plugins
18
+ router.register("GET", "/api/pipelines/:pipelineId/plugins", (ctx) => {
19
+ const scope = ctx.getPipelineScope();
20
+ if (!scope) {
21
+ ctx.sendJson(404, { error: "pipeline_not_found", pipelineId: ctx.params.pipelineId });
22
+ return;
23
+ }
24
+ const workflow = scope.getWorkflow();
25
+ if (!workflow) {
26
+ ctx.sendJson(501, { error: "workflow_api_not_enabled" });
27
+ return;
28
+ }
29
+ ctx.sendJson(200, { ok: true, state: workflow.plugins, pipelineId: scope.pipelineId });
30
+ });
31
+ // POST /api/pipelines/:pipelineId/plugins
32
+ router.register("POST", "/api/pipelines/:pipelineId/plugins", async (ctx) => {
33
+ const scope = ctx.getPipelineScope();
34
+ if (!scope) {
35
+ ctx.sendJson(404, { error: "pipeline_not_found", pipelineId: ctx.params.pipelineId });
36
+ return;
37
+ }
38
+ if (!scope.getWorkflow || !scope.setWorkflow) {
39
+ ctx.sendJson(501, { error: "workflow_api_not_enabled" });
40
+ return;
41
+ }
42
+ const body = await ctx.readBody();
43
+ const currentWorkflow = scope.getWorkflow();
44
+ const currentPlugin = currentWorkflow.plugins;
45
+ const remoteBatchBody = asRecord(body.remoteBatch) ?? body;
46
+ const schedulerBody = asRecord(body.scheduler);
47
+ const nextPlugin = {
48
+ remoteBatch: {
49
+ enabled: remoteBatchBody.enabled === true,
50
+ url: typeof remoteBatchBody.url === "string" && remoteBatchBody.url.trim()
51
+ ? remoteBatchBody.url.trim()
52
+ : currentPlugin.remoteBatch.url || pipeline_config_js_1.DEFAULT_REMOTE_BATCH_URL,
53
+ startBatch: typeof remoteBatchBody.startBatch === "number" && Number.isFinite(remoteBatchBody.startBatch)
54
+ ? Math.max(1, Math.trunc(remoteBatchBody.startBatch))
55
+ : currentPlugin.remoteBatch.startBatch,
56
+ batchSize: typeof remoteBatchBody.batchSize === "number" && Number.isFinite(remoteBatchBody.batchSize)
57
+ ? Math.max(1, Math.trunc(remoteBatchBody.batchSize))
58
+ : currentPlugin.remoteBatch.batchSize,
59
+ sourceField: typeof remoteBatchBody.sourceField === "string" && remoteBatchBody.sourceField.trim()
60
+ ? remoteBatchBody.sourceField.trim()
61
+ : currentPlugin.remoteBatch.sourceField,
62
+ },
63
+ scheduler: {
64
+ enabled: schedulerBody?.enabled === undefined ? currentPlugin.scheduler.enabled : schedulerBody.enabled === true,
65
+ },
66
+ };
67
+ const nextWorkflow = {
68
+ ...currentWorkflow,
69
+ plugins: {
70
+ ...currentWorkflow.plugins,
71
+ ...nextPlugin,
72
+ },
73
+ // 调度器插件关闭时同步落盘为 disabled,避免刷新后后台仍按旧 scheduler.enabled 自动运行
74
+ scheduler: nextPlugin.scheduler.enabled
75
+ ? currentWorkflow.scheduler
76
+ : {
77
+ ...currentWorkflow.scheduler,
78
+ enabled: false,
79
+ },
80
+ };
81
+ if (!nextPlugin.remoteBatch.enabled && scope.cancelBatchRun && scope.getBatchRunState?.().status === "running") {
82
+ scope.cancelBatchRun();
83
+ }
84
+ scope.setWorkflow(nextWorkflow);
85
+ (0, template_js_1.saveWorkflowDefinitionWithStorage)(nextWorkflow, { workflowFilePath: scope.workflowFilePath });
86
+ ctx.sendJson(200, { ok: true, state: nextPlugin, pipelineId: scope.pipelineId });
87
+ });
88
+ // GET /api/pipelines/:pipelineId/template
89
+ router.register("GET", "/api/pipelines/:pipelineId/template", (ctx) => {
90
+ const scope = ctx.getPipelineScope();
91
+ if (!scope) {
92
+ ctx.sendJson(404, { error: "pipeline_not_found", pipelineId: ctx.params.pipelineId });
93
+ return;
94
+ }
95
+ const workflow = scope.getWorkflow();
96
+ if (!workflow) {
97
+ ctx.sendJson(501, { error: "workflow_api_not_enabled" });
98
+ return;
99
+ }
100
+ ctx.sendJson(200, { nodes: (0, template_js_1.workflowToTemplateNodes)(workflow) });
101
+ });
102
+ // GET /api/pipelines/:pipelineId/workflow
103
+ router.register("GET", "/api/pipelines/:pipelineId/workflow", (ctx) => {
104
+ const scope = ctx.getPipelineScope();
105
+ if (!scope) {
106
+ ctx.sendJson(404, { error: "pipeline_not_found", pipelineId: ctx.params.pipelineId });
107
+ return;
108
+ }
109
+ if (!scope.getWorkflow) {
110
+ ctx.sendJson(501, { error: "workflow_api_not_enabled" });
111
+ return;
112
+ }
113
+ ctx.sendJson(200, { workflow: scope.getWorkflow(), pipelineId: scope.pipelineId });
114
+ });
115
+ // POST /api/pipelines/:pipelineId/workflow
116
+ router.register("POST", "/api/pipelines/:pipelineId/workflow", async (ctx) => {
117
+ const scope = ctx.getPipelineScope();
118
+ if (!scope) {
119
+ ctx.sendJson(404, { error: "pipeline_not_found", pipelineId: ctx.params.pipelineId });
120
+ return;
121
+ }
122
+ if (!scope.setWorkflow || !scope.getWorkflow) {
123
+ ctx.sendJson(501, { error: "workflow_api_not_enabled" });
124
+ return;
125
+ }
126
+ const body = await ctx.readBody();
127
+ const parseResult = (0, template_js_1.readWorkflowDefinitionFromRawDetailed)(body.workflow ?? body);
128
+ if (!parseResult.ok) {
129
+ ctx.sendJson(400, {
130
+ error: parseResult.error,
131
+ detail: parseResult.detail,
132
+ pipelineId: scope.pipelineId,
133
+ });
134
+ return;
135
+ }
136
+ const next = parseResult.workflow;
137
+ let normalized;
138
+ try {
139
+ normalized = (0, template_js_1.normalizeWorkflowFallbacksWithStorage)(next, { workflowFilePath: scope.workflowFilePath });
140
+ }
141
+ catch (error) {
142
+ // 透传读盘异常,保证「坏盘后修复提交」场景能得到结构化错误响应
143
+ const errorObj = error;
144
+ ctx.sendJson(400, {
145
+ error: errorObj.message || "invalid_persisted_workflow_definition",
146
+ detail: errorObj.detail ?? errorObj.message,
147
+ pipelineId: scope.pipelineId,
148
+ });
149
+ return;
150
+ }
151
+ const validation = (0, template_js_1.validateWorkflowDefinition)(normalized);
152
+ if (!validation.ok) {
153
+ ctx.sendJson(400, {
154
+ error: validation.error,
155
+ detail: validation.detail,
156
+ pipelineId: scope.pipelineId,
157
+ });
158
+ return;
159
+ }
160
+ scope.setWorkflow(normalized);
161
+ try {
162
+ (0, template_js_1.saveWorkflowDefinitionWithStorage)(normalized, { workflowFilePath: scope.workflowFilePath });
163
+ }
164
+ catch (error) {
165
+ const errorObj = error;
166
+ ctx.sendJson(400, {
167
+ error: errorObj.message || "invalid_workflow_definition",
168
+ detail: errorObj.detail ?? errorObj.message,
169
+ pipelineId: scope.pipelineId,
170
+ });
171
+ return;
172
+ }
173
+ const run = scope.seedRun(scope.getTemplateNodes());
174
+ scope.setRun(run);
175
+ scope.pushTimeline(`[${scope.pipelineId}] 工作流定义已更新,节点数: ${normalized.nodes.length}`);
176
+ scope.emitPipeline();
177
+ ctx.sendJson(200, { ok: true, workflow: normalized, run, pipelineId: scope.pipelineId });
178
+ });
179
+ };
180
+ exports.registerPipelineWorkflowRoutes = registerPipelineWorkflowRoutes;
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerPipelinesRoutes = void 0;
4
+ const PIPELINE_ID_PATTERN = /^[A-Za-z0-9_-]+$/;
5
+ // 从请求体读取流水线标题,若未提供则生成默认标题
6
+ const readPipelineTitle = (value, pipelineId) => typeof value === "string" && value.trim() ? value.trim() : `流水线 DAG-${pipelineId}`;
7
+ /**
8
+ * 注册 Pipeline CRUD 路由:
9
+ * GET /api/pipelines — 列出所有流水线
10
+ * POST /api/pipelines — 创建流水线
11
+ * PATCH /api/pipelines/:pipelineId — 重命名流水线
12
+ * DELETE /api/pipelines/:pipelineId — 删除流水线
13
+ */
14
+ const registerPipelinesRoutes = (router) => {
15
+ router.register("GET", "/api/pipelines", (ctx) => {
16
+ ctx.sendJson(200, {
17
+ items: ctx.options.app.listPipelines().map((definition) => ({
18
+ id: definition.id,
19
+ title: definition.title,
20
+ })),
21
+ });
22
+ });
23
+ router.register("POST", "/api/pipelines", async (ctx) => {
24
+ const body = await ctx.readBody();
25
+ const pipelineId = typeof body.id === "string" ? body.id.trim() : "";
26
+ const cloneFrom = typeof body.cloneFrom === "string" && body.cloneFrom.trim() ? body.cloneFrom.trim() : undefined;
27
+ if (!PIPELINE_ID_PATTERN.test(pipelineId)) {
28
+ ctx.sendJson(400, { ok: false, error: "pipeline_id_invalid" });
29
+ return;
30
+ }
31
+ try {
32
+ const item = await ctx.options.app.createPipeline({
33
+ id: pipelineId,
34
+ title: readPipelineTitle(body.title, pipelineId),
35
+ cloneFrom,
36
+ });
37
+ ctx.sendJson(200, {
38
+ ok: true,
39
+ item: {
40
+ id: item.id,
41
+ title: item.title,
42
+ },
43
+ });
44
+ }
45
+ catch (error) {
46
+ const detail = error instanceof Error ? error.message : "pipeline_create_failed";
47
+ const status = detail === "pipeline_already_exists"
48
+ ? 409
49
+ : detail === "pipeline_id_invalid" || detail === "pipeline_clone_source_not_found"
50
+ ? 400
51
+ : 500;
52
+ ctx.sendJson(status, { ok: false, error: detail });
53
+ }
54
+ });
55
+ router.register("PATCH", "/api/pipelines/:pipelineId", async (ctx) => {
56
+ const { pipelineId } = ctx.params;
57
+ const body = await ctx.readBody();
58
+ const title = typeof body.title === "string" ? body.title.trim() : "";
59
+ if (!title) {
60
+ ctx.sendJson(400, { ok: false, error: "pipeline_title_invalid", pipelineId: pipelineId || null });
61
+ return;
62
+ }
63
+ try {
64
+ const item = ctx.options.app.renamePipeline(pipelineId, title);
65
+ ctx.sendJson(200, {
66
+ ok: true,
67
+ item: {
68
+ id: item.id,
69
+ title: item.title,
70
+ },
71
+ });
72
+ }
73
+ catch (error) {
74
+ const detail = error instanceof Error ? error.message : "pipeline_rename_failed";
75
+ const status = detail === "pipeline_not_found" ? 404 : detail === "pipeline_title_invalid" ? 400 : 500;
76
+ ctx.sendJson(status, { ok: false, error: detail, pipelineId: pipelineId || null });
77
+ }
78
+ });
79
+ router.register("DELETE", "/api/pipelines/:pipelineId", async (ctx) => {
80
+ const { pipelineId } = ctx.params;
81
+ try {
82
+ const deleted = ctx.options.app.deletePipeline(pipelineId);
83
+ ctx.sendJson(200, { ok: true, pipelineId: deleted.pipelineId });
84
+ }
85
+ catch (error) {
86
+ const detail = error instanceof Error ? error.message : "pipeline_delete_failed";
87
+ const status = detail === "pipeline_not_found"
88
+ ? 404
89
+ : detail === "pipeline_delete_last_forbidden" || detail === "pipeline_delete_running_forbidden"
90
+ ? 409
91
+ : 500;
92
+ ctx.sendJson(status, { ok: false, error: detail, pipelineId: pipelineId || null });
93
+ }
94
+ });
95
+ };
96
+ exports.registerPipelinesRoutes = registerPipelinesRoutes;