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,780 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createPipelineRuntimeApiClient = exports.createServerLifecycleClient = void 0;
7
+ const node_fs_1 = require("node:fs");
8
+ const promises_1 = require("node:fs/promises");
9
+ const node_path_1 = require("node:path");
10
+ const node_child_process_1 = require("node:child_process");
11
+ const ws_1 = __importDefault(require("ws"));
12
+ const app_context_env_1 = require("../app/app-context-env");
13
+ const data_dir_1 = require("../app/data-dir");
14
+ const errors_1 = require("./errors");
15
+ const STARTUP_LOCK_STALE_MS = 15_000;
16
+ const STARTUP_LOCK_WAIT_MS = 250;
17
+ const STARTUP_TIMEOUT_MS = 15_000;
18
+ const STOP_TIMEOUT_MS = 10_000;
19
+ const buildApiBaseUrl = () => {
20
+ const config = (0, app_context_env_1.resolveAppContextConfig)();
21
+ // CLI 访问本地常驻后端时固定走 loopback,避免 0.0.0.0 作为 client 目标地址不可直连。
22
+ return `http://127.0.0.1:${config.apiPort}`;
23
+ };
24
+ const buildWsBaseUrl = () => {
25
+ const config = (0, app_context_env_1.resolveAppContextConfig)();
26
+ return `ws://127.0.0.1:${config.apiPort}`;
27
+ };
28
+ const buildPipelineApiUrl = (pipelineId, suffix) => `${buildApiBaseUrl()}/api/pipelines/${encodeURIComponent(pipelineId)}${suffix}`;
29
+ const buildPipelineApiUrlWithIdentity = (pipelineId, suffix, target) => {
30
+ const url = new URL(buildPipelineApiUrl(pipelineId, suffix));
31
+ if (typeof target?.runId === "string" && target.runId.trim()) {
32
+ url.searchParams.set("runId", target.runId.trim());
33
+ }
34
+ if (typeof target?.batchRunId === "string" && target.batchRunId.trim()) {
35
+ url.searchParams.set("batchRunId", target.batchRunId.trim());
36
+ }
37
+ return url.toString();
38
+ };
39
+ const resolveRuntimePipelineSelector = (selector) => {
40
+ if (typeof selector === "string" && selector.trim()) {
41
+ return { pipelineId: selector.trim() };
42
+ }
43
+ if (!selector || typeof selector === "string") {
44
+ throw new errors_1.CliError("Missing pipelineId for runtime API selector", {
45
+ code: "INVALID_ARGUMENT",
46
+ exitCode: 2,
47
+ details: {
48
+ runId: null,
49
+ batchRunId: null,
50
+ },
51
+ });
52
+ }
53
+ const pipelineId = typeof selector.pipelineId === "string" ? selector.pipelineId.trim() : "";
54
+ if (!pipelineId) {
55
+ // runtime API 的 status/stop 路由仍是 /api/pipelines/:pipelineId/*,runId/batchRunId 仅用于“精确命中当前 pipeline 运行”。
56
+ throw new errors_1.CliError("Missing pipelineId for runtime API selector", {
57
+ code: "INVALID_ARGUMENT",
58
+ exitCode: 2,
59
+ details: {
60
+ runId: selector?.runId ?? null,
61
+ batchRunId: selector?.batchRunId ?? null,
62
+ },
63
+ });
64
+ }
65
+ return {
66
+ pipelineId,
67
+ target: {
68
+ runId: selector?.runId,
69
+ batchRunId: selector?.batchRunId,
70
+ },
71
+ };
72
+ };
73
+ const waitForPipelineWatchSignal = async (selector, timeoutMs) => {
74
+ const wsUrl = `${buildWsBaseUrl()}/api/ws`;
75
+ const eventType = await new Promise((resolve, reject) => {
76
+ let settled = false;
77
+ let timeoutHandle = null;
78
+ const ws = new ws_1.default(wsUrl);
79
+ const finish = (error, payload) => {
80
+ if (settled)
81
+ return;
82
+ settled = true;
83
+ if (timeoutHandle)
84
+ clearTimeout(timeoutHandle);
85
+ try {
86
+ ws.close();
87
+ }
88
+ catch {
89
+ // Ignore close failure; caller only cares about wake-up semantics.
90
+ }
91
+ if (error) {
92
+ reject(error);
93
+ return;
94
+ }
95
+ resolve(payload?.eventType ?? "pipeline.updated");
96
+ };
97
+ const matchesPipelineUpdated = (payload) => {
98
+ const eventPipelineId = typeof payload.pipelineId === "string" ? payload.pipelineId.trim() : "";
99
+ const eventRunId = typeof payload.runId === "string" ? payload.runId.trim() : "";
100
+ const runRecord = payload.run && typeof payload.run === "object" ? payload.run : null;
101
+ const runIdFromRun = typeof runRecord?.id === "string" ? runRecord.id.trim() : "";
102
+ if (selector.pipelineId && selector.pipelineId !== eventPipelineId)
103
+ return false;
104
+ if (selector.runId) {
105
+ return selector.runId === eventRunId || selector.runId === runIdFromRun;
106
+ }
107
+ // batchRunId 在 pipeline.updated 中没有稳定字段;这里把更新当作“状态可能变化”的唤醒信号,
108
+ // 实际终态判断仍由后续 status 请求完成,避免因为事件字段缺失误判终态。
109
+ if (selector.batchRunId)
110
+ return true;
111
+ return Boolean(selector.pipelineId);
112
+ };
113
+ const matchesBootstrap = (payload) => {
114
+ const pipelines = payload.pipelines && typeof payload.pipelines === "object"
115
+ ? payload.pipelines
116
+ : null;
117
+ if (!pipelines)
118
+ return false;
119
+ if (selector.pipelineId && pipelines[selector.pipelineId])
120
+ return true;
121
+ if (selector.runId) {
122
+ for (const item of Object.values(pipelines)) {
123
+ if (!item || typeof item !== "object")
124
+ continue;
125
+ const runId = typeof item.runId === "string" ? String(item.runId) : "";
126
+ if (runId && runId === selector.runId)
127
+ return true;
128
+ }
129
+ }
130
+ return false;
131
+ };
132
+ ws.on("open", () => {
133
+ timeoutHandle = setTimeout(() => {
134
+ finish(new errors_1.CliError("Pipeline watch event-stream wait timed out", {
135
+ code: "PIPELINE_WATCH_WS_TIMEOUT",
136
+ exitCode: 4,
137
+ details: {
138
+ selector,
139
+ timeoutMs,
140
+ wsUrl,
141
+ },
142
+ }));
143
+ }, timeoutMs);
144
+ });
145
+ ws.on("message", (event) => {
146
+ const raw = typeof event === "string" ? event : event.toString();
147
+ if (!raw)
148
+ return;
149
+ let parsed = null;
150
+ try {
151
+ parsed = JSON.parse(raw);
152
+ }
153
+ catch {
154
+ return;
155
+ }
156
+ if (!parsed || typeof parsed.type !== "string")
157
+ return;
158
+ const payload = parsed.payload && typeof parsed.payload === "object" ? parsed.payload : null;
159
+ if (!payload)
160
+ return;
161
+ if (parsed.type === "pipeline.updated" && matchesPipelineUpdated(payload)) {
162
+ finish(undefined, { eventType: "pipeline.updated" });
163
+ return;
164
+ }
165
+ if (parsed.type === "bootstrap" && matchesBootstrap(payload)) {
166
+ finish(undefined, { eventType: "bootstrap" });
167
+ }
168
+ });
169
+ ws.on("error", () => {
170
+ finish(new errors_1.CliError("Pipeline watch event-stream unavailable", {
171
+ code: "PIPELINE_WATCH_WS_UNAVAILABLE",
172
+ exitCode: 4,
173
+ details: {
174
+ selector,
175
+ wsUrl,
176
+ },
177
+ }));
178
+ });
179
+ ws.on("close", () => {
180
+ if (!settled) {
181
+ finish(new errors_1.CliError("Pipeline watch event-stream closed", {
182
+ code: "PIPELINE_WATCH_WS_CLOSED",
183
+ exitCode: 4,
184
+ details: {
185
+ selector,
186
+ wsUrl,
187
+ },
188
+ }));
189
+ }
190
+ });
191
+ });
192
+ return { ok: true, source: "ws", eventType };
193
+ };
194
+ const sleep = async (ms) => {
195
+ await new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
196
+ };
197
+ const pathExists = async (filePath) => {
198
+ try {
199
+ await (0, promises_1.access)(filePath, node_fs_1.constants.F_OK);
200
+ return true;
201
+ }
202
+ catch {
203
+ return false;
204
+ }
205
+ };
206
+ const getWorkspaceRoot = () => {
207
+ let current = __dirname;
208
+ for (let depth = 0; depth < 8; depth += 1) {
209
+ if ((0, node_fs_1.existsSync)((0, node_path_1.join)(current, "package.json"))) {
210
+ return current;
211
+ }
212
+ const parent = (0, node_path_1.dirname)(current);
213
+ if (parent === current)
214
+ break;
215
+ current = parent;
216
+ }
217
+ return process.cwd();
218
+ };
219
+ const getServerRuntimeDir = () => {
220
+ const override = process.env.TASKMELD_SERVER_RUNTIME_DIR?.trim();
221
+ if (override)
222
+ return override;
223
+ return (0, data_dir_1.resolveTaskMeldDataPath)("server");
224
+ };
225
+ const getServerRuntimeMetadataPath = () => (0, node_path_1.join)(getServerRuntimeDir(), "runtime.json");
226
+ const getServerStartupLockPath = () => (0, node_path_1.join)(getServerRuntimeDir(), "startup.lock");
227
+ const normalizeOptionalString = (value) => typeof value === "string" && value.trim() ? value.trim() : null;
228
+ const writeRuntimeMetadata = async (metadata) => {
229
+ await (0, promises_1.mkdir)(getServerRuntimeDir(), { recursive: true });
230
+ await (0, promises_1.writeFile)(getServerRuntimeMetadataPath(), JSON.stringify(metadata, null, 2), "utf8");
231
+ };
232
+ const readRuntimeMetadata = async () => {
233
+ try {
234
+ const raw = await (0, promises_1.readFile)(getServerRuntimeMetadataPath(), "utf8");
235
+ const parsed = JSON.parse(raw);
236
+ if (typeof parsed.pid !== "number" ||
237
+ !Number.isFinite(parsed.pid) ||
238
+ typeof parsed.port !== "number" ||
239
+ !Number.isFinite(parsed.port) ||
240
+ typeof parsed.endpoint !== "string" ||
241
+ !parsed.endpoint.trim() ||
242
+ typeof parsed.startedAt !== "string" ||
243
+ !parsed.startedAt.trim()) {
244
+ return null;
245
+ }
246
+ return {
247
+ serverId: normalizeOptionalString(parsed.serverId),
248
+ pid: Math.trunc(parsed.pid),
249
+ port: Math.trunc(parsed.port),
250
+ endpoint: parsed.endpoint.trim(),
251
+ startedAt: parsed.startedAt.trim(),
252
+ };
253
+ }
254
+ catch {
255
+ return null;
256
+ }
257
+ };
258
+ const readServerHealth = async () => {
259
+ try {
260
+ const response = await fetch(`${buildApiBaseUrl()}/api/health`);
261
+ if (!response.ok)
262
+ return null;
263
+ const payload = await response.json();
264
+ if (payload.ok !== true ||
265
+ typeof payload.pid !== "number" ||
266
+ !Number.isFinite(payload.pid) ||
267
+ typeof payload.port !== "number" ||
268
+ !Number.isFinite(payload.port) ||
269
+ typeof payload.endpoint !== "string" ||
270
+ !payload.endpoint.trim() ||
271
+ typeof payload.startedAt !== "string" ||
272
+ !payload.startedAt.trim()) {
273
+ return null;
274
+ }
275
+ return {
276
+ ok: true,
277
+ serverId: normalizeOptionalString(payload.serverId),
278
+ pid: Math.trunc(payload.pid),
279
+ port: Math.trunc(payload.port),
280
+ endpoint: payload.endpoint.trim(),
281
+ startedAt: payload.startedAt.trim(),
282
+ };
283
+ }
284
+ catch {
285
+ return null;
286
+ }
287
+ };
288
+ const metadataMatchesHealth = (metadata, health) => {
289
+ if (!metadata)
290
+ return false;
291
+ if (metadata.pid !== health.pid || metadata.port !== health.port || metadata.endpoint !== health.endpoint) {
292
+ return false;
293
+ }
294
+ if (metadata.serverId && health.serverId) {
295
+ return metadata.serverId === health.serverId;
296
+ }
297
+ // 兼容旧 metadata:早期 runtime.json 不含 serverId,此时退回 pid/port/endpoint 交叉校验。
298
+ return true;
299
+ };
300
+ const buildMetadataFromHealth = (health) => ({
301
+ serverId: health.serverId,
302
+ pid: health.pid,
303
+ port: health.port,
304
+ endpoint: health.endpoint,
305
+ startedAt: health.startedAt,
306
+ });
307
+ const getExpectedRuntimeOwner = () => {
308
+ const endpoint = buildApiBaseUrl();
309
+ const parsed = new URL(endpoint);
310
+ return {
311
+ endpoint,
312
+ port: Number(parsed.port),
313
+ };
314
+ };
315
+ const probeRuntimeOwnership = async () => {
316
+ const [metadata, health] = await Promise.all([readRuntimeMetadata(), readServerHealth()]);
317
+ const expected = getExpectedRuntimeOwner();
318
+ const pid = metadata?.pid ?? health?.pid ?? null;
319
+ const pidRunning = isPidRunning(pid);
320
+ const metadataMatchesExpected = Boolean(metadata && metadata.endpoint === expected.endpoint && metadata.port === expected.port);
321
+ const healthMatchesExpected = Boolean(health && health.endpoint === expected.endpoint && health.port === expected.port);
322
+ const metadataMatchesLiveOwner = health ? metadataMatchesHealth(metadata, health) : false;
323
+ const ownership = !metadata
324
+ ? "metadata_missing"
325
+ : metadataMatchesExpected && (!health || metadataMatchesLiveOwner) && (!health || healthMatchesExpected)
326
+ ? "matched"
327
+ : "metadata_mismatch";
328
+ const metadataStale = Boolean(metadata && !pidRunning && !health);
329
+ let lockStale = false;
330
+ try {
331
+ const lockStat = await (0, promises_1.stat)(getServerStartupLockPath());
332
+ lockStale = Date.now() - lockStat.mtimeMs > STARTUP_LOCK_STALE_MS;
333
+ }
334
+ catch {
335
+ lockStale = false;
336
+ }
337
+ return {
338
+ endpoint: expected.endpoint,
339
+ expectedPort: expected.port,
340
+ ready: health !== null,
341
+ metadataPresent: metadata !== null,
342
+ ownership,
343
+ ownershipMatched: ownership === "matched",
344
+ metadataStale,
345
+ lockStale,
346
+ pid,
347
+ pidRunning,
348
+ startedAt: metadata?.startedAt ?? health?.startedAt ?? null,
349
+ metadata,
350
+ health,
351
+ };
352
+ };
353
+ const cleanupStaleRuntimeArtifacts = async (probe) => {
354
+ if (probe.metadataStale) {
355
+ await (0, promises_1.rm)(getServerRuntimeMetadataPath(), { force: true });
356
+ }
357
+ // startup.lock 是“开始拉起”的互斥标记,不是 owner 证据;只有健康实例不存在且锁超时才允许清理。
358
+ if (!probe.ready && probe.lockStale) {
359
+ await (0, promises_1.rm)(getServerStartupLockPath(), { force: true });
360
+ }
361
+ };
362
+ const reconcileRuntimeMetadataWithHealth = async (probe) => {
363
+ if (!probe.health)
364
+ return probe.metadata;
365
+ const nextMetadata = buildMetadataFromHealth(probe.health);
366
+ if (!metadataMatchesHealth(probe.metadata, probe.health)) {
367
+ // health 请求已经命中了当前 CLI 期望的 endpoint;只要这份 owner 信息完整,就应该回填 metadata,
368
+ // 否则 metadata_missing / metadata_mismatch 会在后续 ensure/status/stop 中反复被误判为异常状态。
369
+ await writeRuntimeMetadata(nextMetadata);
370
+ return nextMetadata;
371
+ }
372
+ return nextMetadata;
373
+ };
374
+ const isPidRunning = (pid) => {
375
+ if (typeof pid !== "number" || !Number.isFinite(pid) || pid <= 0)
376
+ return false;
377
+ try {
378
+ process.kill(Math.trunc(pid), 0);
379
+ return true;
380
+ }
381
+ catch {
382
+ return false;
383
+ }
384
+ };
385
+ const normalizeServerError = async (response) => {
386
+ let payload = null;
387
+ try {
388
+ payload = await response.json();
389
+ }
390
+ catch {
391
+ payload = null;
392
+ }
393
+ const code = typeof payload?.error === "string" && payload.error.trim() ? payload.error.trim().toUpperCase() : "SERVER_REQUEST_FAILED";
394
+ const message = typeof payload?.error === "string" && payload.error.trim()
395
+ ? payload.error.trim()
396
+ : `server request failed: ${response.status}`;
397
+ throw new errors_1.CliError(message, {
398
+ code,
399
+ exitCode: response.status === 404 ? 3 : response.status === 400 ? 2 : 4,
400
+ details: {
401
+ status: response.status,
402
+ payload,
403
+ },
404
+ });
405
+ };
406
+ const requestJson = async (url, init) => {
407
+ let response;
408
+ try {
409
+ response = await fetch(url, init);
410
+ }
411
+ catch (error) {
412
+ throw new errors_1.CliError("Local API server is unavailable", {
413
+ code: "LOCAL_API_UNAVAILABLE",
414
+ exitCode: 5,
415
+ details: {
416
+ url,
417
+ detail: error instanceof Error ? error.message : "unknown_error",
418
+ },
419
+ });
420
+ }
421
+ if (!response.ok) {
422
+ await normalizeServerError(response);
423
+ }
424
+ return response.json();
425
+ };
426
+ const isServerHealthy = async () => {
427
+ return (await readServerHealth()) !== null;
428
+ };
429
+ const waitForHealth = async (timeoutMs) => {
430
+ const startAt = Date.now();
431
+ while (Date.now() - startAt <= timeoutMs) {
432
+ if (await isServerHealthy())
433
+ return;
434
+ await sleep(250);
435
+ }
436
+ throw new errors_1.CliError("Local API server failed to become healthy", {
437
+ code: "LOCAL_API_START_TIMEOUT",
438
+ exitCode: 5,
439
+ details: {
440
+ endpoint: buildApiBaseUrl(),
441
+ timeoutMs,
442
+ },
443
+ });
444
+ };
445
+ const resolveServerLaunchSpec = async () => {
446
+ const workspaceRoot = getWorkspaceRoot();
447
+ const distEntry = (0, node_path_1.join)(workspaceRoot, "dist", "src", "index.js");
448
+ if (await pathExists(distEntry)) {
449
+ return {
450
+ command: process.execPath,
451
+ args: [distEntry],
452
+ cwd: workspaceRoot,
453
+ };
454
+ }
455
+ const srcEntry = (0, node_path_1.join)(workspaceRoot, "src", "index.ts");
456
+ const tsxCmd = process.platform === "win32"
457
+ ? (0, node_path_1.join)(workspaceRoot, "node_modules", ".bin", "tsx.cmd")
458
+ : (0, node_path_1.join)(workspaceRoot, "node_modules", ".bin", "tsx");
459
+ if (await pathExists(srcEntry) && await pathExists(tsxCmd)) {
460
+ return {
461
+ command: tsxCmd,
462
+ args: [srcEntry],
463
+ cwd: workspaceRoot,
464
+ };
465
+ }
466
+ throw new errors_1.CliError("Unable to resolve local API server entry", {
467
+ code: "LOCAL_API_ENTRY_NOT_FOUND",
468
+ exitCode: 5,
469
+ details: {
470
+ workspaceRoot,
471
+ checked: [distEntry, srcEntry, tsxCmd],
472
+ },
473
+ });
474
+ };
475
+ const acquireStartupLock = async () => {
476
+ const lockPath = getServerStartupLockPath();
477
+ await (0, promises_1.mkdir)(getServerRuntimeDir(), { recursive: true });
478
+ const startAt = Date.now();
479
+ while (Date.now() - startAt <= STARTUP_TIMEOUT_MS) {
480
+ try {
481
+ const handle = await (0, promises_1.open)(lockPath, "wx");
482
+ return async () => {
483
+ await handle.close();
484
+ await (0, promises_1.rm)(lockPath, { force: true });
485
+ };
486
+ }
487
+ catch (error) {
488
+ const code = error?.code;
489
+ if (code !== "EEXIST") {
490
+ throw new errors_1.CliError("Failed to acquire local API startup lock", {
491
+ code: "LOCAL_API_LOCK_FAILED",
492
+ exitCode: 5,
493
+ details: {
494
+ lockPath,
495
+ error: error instanceof Error ? error.message : String(error),
496
+ },
497
+ });
498
+ }
499
+ if (await isServerHealthy()) {
500
+ const probe = await probeRuntimeOwnership();
501
+ await reconcileRuntimeMetadataWithHealth(probe);
502
+ return async () => { };
503
+ }
504
+ try {
505
+ const lockStat = await (0, promises_1.stat)(lockPath);
506
+ if (Date.now() - lockStat.mtimeMs > STARTUP_LOCK_STALE_MS) {
507
+ await (0, promises_1.rm)(lockPath, { force: true });
508
+ continue;
509
+ }
510
+ }
511
+ catch {
512
+ continue;
513
+ }
514
+ await sleep(STARTUP_LOCK_WAIT_MS);
515
+ }
516
+ }
517
+ throw new errors_1.CliError("Timed out waiting for local API startup lock", {
518
+ code: "LOCAL_API_LOCK_TIMEOUT",
519
+ exitCode: 5,
520
+ details: {
521
+ lockPath,
522
+ timeoutMs: STARTUP_TIMEOUT_MS,
523
+ },
524
+ });
525
+ };
526
+ const startLocalApiServer = async () => {
527
+ const initialProbe = await probeRuntimeOwnership();
528
+ await cleanupStaleRuntimeArtifacts(initialProbe);
529
+ await reconcileRuntimeMetadataWithHealth(initialProbe);
530
+ if (initialProbe.ready) {
531
+ const verifiedProbe = await probeRuntimeOwnership();
532
+ return {
533
+ ok: true,
534
+ endpoint: buildApiBaseUrl(),
535
+ action: "already_running",
536
+ reused: true,
537
+ ownership: verifiedProbe.ownership,
538
+ metadataStale: verifiedProbe.metadataStale,
539
+ lockStale: verifiedProbe.lockStale,
540
+ pid: verifiedProbe.health?.pid ?? verifiedProbe.metadata?.pid ?? verifiedProbe.pid,
541
+ startedAt: verifiedProbe.health?.startedAt ?? verifiedProbe.metadata?.startedAt ?? verifiedProbe.startedAt,
542
+ };
543
+ }
544
+ const releaseLock = await acquireStartupLock();
545
+ try {
546
+ const lockedProbe = await probeRuntimeOwnership();
547
+ await cleanupStaleRuntimeArtifacts(lockedProbe);
548
+ await reconcileRuntimeMetadataWithHealth(lockedProbe);
549
+ if (lockedProbe.ready) {
550
+ const verifiedProbe = await probeRuntimeOwnership();
551
+ return {
552
+ ok: true,
553
+ endpoint: buildApiBaseUrl(),
554
+ action: "already_running",
555
+ reused: true,
556
+ ownership: verifiedProbe.ownership,
557
+ metadataStale: verifiedProbe.metadataStale,
558
+ lockStale: verifiedProbe.lockStale,
559
+ pid: verifiedProbe.health?.pid ?? verifiedProbe.metadata?.pid ?? verifiedProbe.pid,
560
+ startedAt: verifiedProbe.health?.startedAt ?? verifiedProbe.metadata?.startedAt ?? verifiedProbe.startedAt,
561
+ };
562
+ }
563
+ const launchSpec = await resolveServerLaunchSpec();
564
+ // 后台 daemon 必须脱离当前 CLI 生命周期运行,否则 watch/start 等命令一结束就会把宿主一并带死。
565
+ const child = (0, node_child_process_1.spawn)(launchSpec.command, launchSpec.args, {
566
+ cwd: launchSpec.cwd,
567
+ detached: true,
568
+ stdio: "ignore",
569
+ windowsHide: true,
570
+ });
571
+ child.unref();
572
+ await waitForHealth(STARTUP_TIMEOUT_MS);
573
+ const finalProbe = await probeRuntimeOwnership();
574
+ await reconcileRuntimeMetadataWithHealth(finalProbe);
575
+ const verifiedProbe = await probeRuntimeOwnership();
576
+ return {
577
+ ok: true,
578
+ endpoint: buildApiBaseUrl(),
579
+ action: "started",
580
+ reused: false,
581
+ ownership: verifiedProbe.ownership,
582
+ metadataStale: verifiedProbe.metadataStale,
583
+ lockStale: verifiedProbe.lockStale,
584
+ pid: verifiedProbe.health?.pid ?? verifiedProbe.metadata?.pid ?? child.pid ?? null,
585
+ startedAt: verifiedProbe.health?.startedAt ?? verifiedProbe.metadata?.startedAt ?? null,
586
+ };
587
+ }
588
+ finally {
589
+ await releaseLock();
590
+ }
591
+ };
592
+ const ensureServerReady = async () => {
593
+ const probe = await probeRuntimeOwnership();
594
+ await cleanupStaleRuntimeArtifacts(probe);
595
+ await reconcileRuntimeMetadataWithHealth(probe);
596
+ if (probe.ready) {
597
+ const verifiedProbe = await probeRuntimeOwnership();
598
+ return {
599
+ ok: true,
600
+ endpoint: buildApiBaseUrl(),
601
+ action: "ensured",
602
+ reused: true,
603
+ ownership: verifiedProbe.ownership,
604
+ metadataStale: verifiedProbe.metadataStale,
605
+ lockStale: verifiedProbe.lockStale,
606
+ pid: verifiedProbe.health?.pid ?? verifiedProbe.metadata?.pid ?? verifiedProbe.pid,
607
+ startedAt: verifiedProbe.health?.startedAt ?? verifiedProbe.metadata?.startedAt ?? verifiedProbe.startedAt,
608
+ };
609
+ }
610
+ const started = await startLocalApiServer();
611
+ return {
612
+ ...started,
613
+ action: started.action === "already_running" ? "ensured" : started.action,
614
+ };
615
+ };
616
+ const getServerStatus = async () => {
617
+ const probe = await probeRuntimeOwnership();
618
+ await cleanupStaleRuntimeArtifacts(probe);
619
+ await reconcileRuntimeMetadataWithHealth(probe);
620
+ const verifiedProbe = await probeRuntimeOwnership();
621
+ return {
622
+ ok: true,
623
+ endpoint: buildApiBaseUrl(),
624
+ ready: verifiedProbe.ready,
625
+ metadataPresent: verifiedProbe.metadataPresent,
626
+ ownership: verifiedProbe.ownership,
627
+ ownershipMatched: verifiedProbe.ownershipMatched,
628
+ metadataStale: verifiedProbe.metadataStale,
629
+ lockStale: verifiedProbe.lockStale,
630
+ pid: verifiedProbe.health?.pid ?? verifiedProbe.metadata?.pid ?? null,
631
+ pidRunning: isPidRunning(verifiedProbe.health?.pid ?? verifiedProbe.metadata?.pid ?? null),
632
+ startedAt: verifiedProbe.health?.startedAt ?? verifiedProbe.metadata?.startedAt ?? null,
633
+ };
634
+ };
635
+ const stopLocalApiServer = async () => {
636
+ const probe = await probeRuntimeOwnership();
637
+ await cleanupStaleRuntimeArtifacts(probe);
638
+ if (!probe.metadataPresent && !probe.health) {
639
+ return {
640
+ ok: true,
641
+ endpoint: buildApiBaseUrl(),
642
+ action: "not_running",
643
+ reused: false,
644
+ ownership: probe.ownership,
645
+ metadataStale: probe.metadataStale,
646
+ lockStale: probe.lockStale,
647
+ pid: null,
648
+ startedAt: null,
649
+ };
650
+ }
651
+ if (!probe.health) {
652
+ const pid = probe.metadata?.pid ?? null;
653
+ if (!isPidRunning(pid)) {
654
+ await (0, promises_1.rm)(getServerRuntimeMetadataPath(), { force: true });
655
+ return {
656
+ ok: true,
657
+ endpoint: buildApiBaseUrl(),
658
+ action: "not_running",
659
+ reused: false,
660
+ ownership: probe.ownership,
661
+ metadataStale: true,
662
+ lockStale: probe.lockStale,
663
+ pid: pid ?? null,
664
+ startedAt: probe.metadata?.startedAt ?? null,
665
+ };
666
+ }
667
+ throw new errors_1.CliError("Refusing to stop unverified local API owner", {
668
+ code: "LOCAL_API_OWNER_UNVERIFIED",
669
+ exitCode: 5,
670
+ details: {
671
+ pid,
672
+ endpoint: buildApiBaseUrl(),
673
+ ownership: probe.ownership,
674
+ },
675
+ });
676
+ }
677
+ const verifiedMetadata = await reconcileRuntimeMetadataWithHealth(probe);
678
+ if (!verifiedMetadata) {
679
+ throw new errors_1.CliError("Refusing to stop: healthy API owner metadata is unavailable", {
680
+ code: "LOCAL_API_OWNER_METADATA_MISSING",
681
+ exitCode: 5,
682
+ details: probe,
683
+ });
684
+ }
685
+ const pid = verifiedMetadata.pid;
686
+ if (!isPidRunning(pid)) {
687
+ await (0, promises_1.rm)(getServerRuntimeMetadataPath(), { force: true });
688
+ return {
689
+ ok: true,
690
+ endpoint: buildApiBaseUrl(),
691
+ action: "not_running",
692
+ reused: false,
693
+ ownership: probe.ownership,
694
+ metadataStale: true,
695
+ lockStale: probe.lockStale,
696
+ pid: pid ?? null,
697
+ startedAt: verifiedMetadata?.startedAt ?? probe.startedAt,
698
+ };
699
+ }
700
+ try {
701
+ process.kill(pid, "SIGTERM");
702
+ }
703
+ catch (error) {
704
+ throw new errors_1.CliError("Failed to stop local API server", {
705
+ code: "LOCAL_API_STOP_FAILED",
706
+ exitCode: 5,
707
+ details: {
708
+ pid,
709
+ detail: error instanceof Error ? error.message : String(error),
710
+ },
711
+ });
712
+ }
713
+ const startAt = Date.now();
714
+ while (Date.now() - startAt <= STOP_TIMEOUT_MS) {
715
+ if (!isPidRunning(pid) && !await isServerHealthy()) {
716
+ await (0, promises_1.rm)(getServerRuntimeMetadataPath(), { force: true });
717
+ return {
718
+ ok: true,
719
+ endpoint: buildApiBaseUrl(),
720
+ action: "stopped",
721
+ reused: false,
722
+ ownership: probe.ownership,
723
+ metadataStale: false,
724
+ lockStale: false,
725
+ pid,
726
+ startedAt: verifiedMetadata?.startedAt ?? probe.startedAt,
727
+ };
728
+ }
729
+ await sleep(250);
730
+ }
731
+ throw new errors_1.CliError("Timed out stopping local API server", {
732
+ code: "LOCAL_API_STOP_TIMEOUT",
733
+ exitCode: 5,
734
+ details: {
735
+ pid,
736
+ endpoint: buildApiBaseUrl(),
737
+ timeoutMs: STOP_TIMEOUT_MS,
738
+ },
739
+ });
740
+ };
741
+ const createServerLifecycleClient = () => ({
742
+ ensureServerReady,
743
+ startServer: startLocalApiServer,
744
+ getServerStatus,
745
+ stopServer: stopLocalApiServer,
746
+ });
747
+ exports.createServerLifecycleClient = createServerLifecycleClient;
748
+ const createPipelineRuntimeApiClient = () => ({
749
+ ensureServerReady,
750
+ startPipeline: async (pipelineId) => requestJson(buildPipelineApiUrl(pipelineId, "/run"), {
751
+ method: "POST",
752
+ }),
753
+ getPipelineStatus: async (selector) => {
754
+ const resolved = resolveRuntimePipelineSelector(selector);
755
+ return requestJson(buildPipelineApiUrlWithIdentity(resolved.pipelineId, "/status", resolved.target));
756
+ },
757
+ stopPipeline: async (selector) => {
758
+ const resolved = resolveRuntimePipelineSelector(selector);
759
+ return requestJson(buildPipelineApiUrlWithIdentity(resolved.pipelineId, "/stop", resolved.target), {
760
+ method: "POST",
761
+ });
762
+ },
763
+ waitForPipelineWatchSignal,
764
+ diagnoseNode: async (pipelineId, nodeId, itemKey) => {
765
+ const url = new URL(buildPipelineApiUrl(pipelineId, `/nodes/${encodeURIComponent(nodeId)}/diagnostics`));
766
+ if (itemKey)
767
+ url.searchParams.set("itemKey", itemKey);
768
+ return requestJson(url.toString());
769
+ },
770
+ getOutput: async (pipelineId, runId) => {
771
+ const url = new URL(buildPipelineApiUrl(pipelineId, "/outputs"));
772
+ if (runId)
773
+ url.searchParams.set("runId", runId);
774
+ return requestJson(url.toString());
775
+ },
776
+ listOutputs: async (pipelineId) => requestJson(buildPipelineApiUrl(pipelineId, "/outputs")),
777
+ listLinks: async () => requestJson("/api/pipeline-links"),
778
+ getQueue: async (pipelineId) => requestJson(buildPipelineApiUrl(pipelineId, "/queue")),
779
+ });
780
+ exports.createPipelineRuntimeApiClient = createPipelineRuntimeApiClient;