@oisincoveney/pipeline 2.8.0 → 2.8.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 (37) hide show
  1. package/dist/argo-submit.d.ts +2 -4
  2. package/dist/argo-submit.js +80 -80
  3. package/dist/cluster-doctor.js +89 -101
  4. package/dist/config/defaults.js +9 -19
  5. package/dist/config/load.js +32 -39
  6. package/dist/config/schemas.d.ts +5 -5
  7. package/dist/mcp/gateway-error.js +15 -0
  8. package/dist/mcp/gateway.js +119 -220
  9. package/dist/moka-global-config.js +20 -20
  10. package/dist/moka-submit.d.ts +7 -7
  11. package/dist/pipeline-runtime.js +580 -371
  12. package/dist/run-state/git-refs.js +124 -94
  13. package/dist/runner-command-contract.d.ts +2 -2
  14. package/dist/runner-event-schema.d.ts +6 -6
  15. package/dist/runner-event-sink.js +37 -69
  16. package/dist/runtime/agent-node/agent-node.js +214 -173
  17. package/dist/runtime/changed-files/changed-files.js +15 -27
  18. package/dist/runtime/changed-files/index.js +2 -0
  19. package/dist/runtime/drain-merge/drain-merge.js +124 -82
  20. package/dist/runtime/gates/gates.js +45 -27
  21. package/dist/runtime/hooks/hooks.js +74 -29
  22. package/dist/runtime/opencode-server.js +27 -21
  23. package/dist/runtime/opencode-session-executor.js +101 -44
  24. package/dist/runtime/parallel-node/parallel-node.js +24 -5
  25. package/dist/runtime/select-candidate/select-candidate.js +45 -29
  26. package/dist/runtime/services/agent-node-runtime-service.js +15 -0
  27. package/dist/runtime/services/command-executor-service.js +8 -0
  28. package/dist/runtime/services/config-io-service.js +42 -0
  29. package/dist/runtime/services/drain-merge-git-service.js +10 -0
  30. package/dist/runtime/services/git-porcelain-service.js +38 -0
  31. package/dist/runtime/services/kubernetes-argo-service.d.ts +13 -0
  32. package/dist/runtime/services/kubernetes-argo-service.js +81 -0
  33. package/dist/runtime/services/mcp-gateway-service.js +184 -0
  34. package/dist/runtime/services/opencode-sdk-service.js +27 -0
  35. package/dist/runtime/services/runner-event-sink-http-service.js +80 -0
  36. package/dist/runtime/services/select-candidate-service.js +13 -0
  37. package/package.json +1 -1
@@ -226,8 +226,8 @@ declare const configSchema: z.ZodObject<{
226
226
  policy: z.ZodOptional<z.ZodObject<{
227
227
  commands: z.ZodOptional<z.ZodEnum<{
228
228
  allow: "allow";
229
- deny: "deny";
230
229
  "trusted-only": "trusted-only";
230
+ deny: "deny";
231
231
  }>>;
232
232
  modules: z.ZodOptional<z.ZodEnum<{
233
233
  allow: "allow";
@@ -255,8 +255,8 @@ declare const configSchema: z.ZodObject<{
255
255
  global: "global";
256
256
  }>>;
257
257
  mode: z.ZodEnum<{
258
- local: "local";
259
258
  hosted: "hosted";
259
+ local: "local";
260
260
  }>;
261
261
  provider: z.ZodLiteral<"toolhive">;
262
262
  authorization_env: z.ZodDefault<z.ZodString>;
@@ -299,10 +299,10 @@ declare const configSchema: z.ZodObject<{
299
299
  }, z.core.$strict>>;
300
300
  output: z.ZodOptional<z.ZodObject<{
301
301
  format: z.ZodEnum<{
302
- json_schema: "json_schema";
303
302
  text: "text";
304
303
  json: "json";
305
304
  jsonl: "jsonl";
305
+ json_schema: "json_schema";
306
306
  }>;
307
307
  repair: z.ZodOptional<z.ZodObject<{
308
308
  enabled: z.ZodOptional<z.ZodBoolean>;
@@ -371,10 +371,10 @@ declare const configSchema: z.ZodObject<{
371
371
  disabled: "disabled";
372
372
  }>>>;
373
373
  output_formats: z.ZodOptional<z.ZodArray<z.ZodEnum<{
374
- json_schema: "json_schema";
375
374
  text: "text";
376
375
  json: "json";
377
376
  jsonl: "jsonl";
377
+ json_schema: "json_schema";
378
378
  }>>>;
379
379
  rules: z.ZodOptional<z.ZodBoolean>;
380
380
  skills: z.ZodOptional<z.ZodBoolean>;
@@ -481,8 +481,8 @@ declare const configSchema: z.ZodObject<{
481
481
  schedules: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
482
482
  description: z.ZodOptional<z.ZodString>;
483
483
  baseline: z.ZodEnum<{
484
- execute: "execute";
485
484
  quick: "quick";
485
+ execute: "execute";
486
486
  }>;
487
487
  max_parallel_nodes: z.ZodOptional<z.ZodNumber>;
488
488
  node_catalog: z.ZodOptional<z.ZodString>;
@@ -0,0 +1,15 @@
1
+ import { Data } from "effect";
2
+ //#region src/mcp/gateway-error.ts
3
+ /**
4
+ * Tagged error for the MCP (ToolHive) gateway subsystem. Lives in its own module
5
+ * so both the gateway facade (src/mcp/gateway.ts) and the Effect service
6
+ * (src/runtime/services/mcp-gateway-service.ts) can import it without forming a
7
+ * circular dependency between them.
8
+ */
9
+ var PipelineMcpGatewayError = class extends Data.TaggedError("PipelineMcpGatewayError") {
10
+ constructor(message) {
11
+ super({ message });
12
+ }
13
+ };
14
+ //#endregion
15
+ export { PipelineMcpGatewayError };
@@ -1,23 +1,22 @@
1
+ import { PipelineMcpGatewayError } from "./gateway-error.js";
2
+ import { McpGatewayService, McpGatewayServiceLive } from "../runtime/services/mcp-gateway-service.js";
1
3
  import { resolveRepoLocalBackendSpecs } from "./repo-local-backends.js";
2
4
  import { renderToolHiveVmcpInventory } from "./toolhive-vmcp.js";
3
- import { Data } from "effect";
5
+ import { Effect } from "effect";
4
6
  import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
7
  import { dirname, join } from "node:path";
6
8
  import { homedir } from "node:os";
7
- import { execa } from "execa";
8
9
  //#region src/mcp/gateway.ts
9
10
  const PIPELINE_GATEWAY_SERVER_ID = "pipeline-gateway";
10
11
  const DEFAULT_LOCAL_GATEWAY_URL = "http://127.0.0.1:4483/mcp";
11
12
  const LEGACY_OPENCODE_MCP_RE = /"mcp"\s*:\s*{(?!\s*"pipeline-gateway")/s;
12
13
  const LEGACY_PIPELINE_MCP_RE = /path:\s*\.mcp\.json|uvx\s+mcpm|mcpm\s+run/;
13
- var PipelineMcpGatewayError = class extends Data.TaggedError("PipelineMcpGatewayError") {
14
- constructor(message) {
15
- super({ message });
16
- }
17
- };
18
14
  function profileNeedsMcpGateway(actor) {
19
15
  return (actor?.mcp_servers ?? []).length > 0;
20
16
  }
17
+ function runMcpGatewayEffect(program) {
18
+ return Effect.runPromise(Effect.provide(program, McpGatewayServiceLive));
19
+ }
21
20
  function gatewayServerForProfile(config, actor, env = process.env) {
22
21
  if (!(config && profileNeedsMcpGateway(actor))) return {};
23
22
  return { [PIPELINE_GATEWAY_SERVER_ID]: gatewayServer(config, env) };
@@ -88,128 +87,79 @@ function configureGatewayHosts(config, options) {
88
87
  };
89
88
  });
90
89
  }
91
- async function runGatewayDoctor(config, cwd) {
92
- const gateway = configuredGateway(config);
93
- const checks = [
94
- {
95
- detail: `${gateway.provider}/${gateway.mode}`,
96
- name: "gateway-config",
97
- passed: true
98
- },
99
- checkGatewayUrl(gateway),
100
- checkGatewayToken(gateway),
101
- ...gateway.mode === "local" ? [await checkThv(cwd)] : [],
102
- await checkGatewayHealth(gateway),
103
- await checkGatewayRequiredTools(gateway),
104
- checkLegacyDirectMcp(cwd)
105
- ];
106
- return {
107
- checks,
108
- passed: checks.every((check) => check.passed)
109
- };
90
+ function runGatewayDoctor(config, cwd) {
91
+ return runMcpGatewayEffect(Effect.gen(function* () {
92
+ const gateway = configuredGateway(config);
93
+ const checks = [
94
+ {
95
+ detail: `${gateway.provider}/${gateway.mode}`,
96
+ name: "gateway-config",
97
+ passed: true
98
+ },
99
+ checkGatewayUrl(gateway),
100
+ checkGatewayToken(gateway),
101
+ ...gateway.mode === "local" ? [yield* checkThv(cwd)] : [],
102
+ yield* checkGatewayHealth(gateway),
103
+ yield* checkGatewayRequiredTools(gateway),
104
+ checkLegacyDirectMcp(cwd)
105
+ ];
106
+ return {
107
+ checks,
108
+ passed: checks.every((check) => check.passed)
109
+ };
110
+ }));
110
111
  }
111
- async function startLocalGateway(config, cwd) {
112
- if (configuredGateway(config).mode !== "local") throw new PipelineMcpGatewayError("mcp gateway local-start is only valid when mcp_gateway.mode is local.");
113
- const result = await reconcileGateway(config, cwd);
114
- if (result.readinessFailures.length > 0) throw new PipelineMcpGatewayError(`Cannot start local MCP gateway; readiness failures: ${result.readinessFailures.join("; ")}`);
115
- await execa("thv", [
116
- "vmcp",
117
- "serve",
118
- "--config",
119
- result.configPath,
120
- "--host",
121
- "127.0.0.1",
122
- "--port",
123
- "4483"
124
- ], {
125
- cwd,
126
- env: await toolhiveEnv(cwd),
127
- stderr: "inherit",
128
- stdout: "inherit"
129
- });
112
+ function startLocalGateway(config, cwd) {
113
+ return runMcpGatewayEffect(Effect.gen(function* () {
114
+ if (configuredGateway(config).mode !== "local") return yield* Effect.fail(new PipelineMcpGatewayError("mcp gateway local-start is only valid when mcp_gateway.mode is local."));
115
+ const result = yield* reconcileGatewayEffect(config, cwd, process.env);
116
+ if (result.readinessFailures.length > 0) return yield* Effect.fail(new PipelineMcpGatewayError(`Cannot start local MCP gateway; readiness failures: ${result.readinessFailures.join("; ")}`));
117
+ yield* (yield* McpGatewayService).serveToolHiveVmcp(result.configPath, cwd);
118
+ }));
130
119
  }
131
- async function reconcileGateway(config, cwd, env = process.env) {
132
- const gateway = configuredGateway(config);
133
- if (gateway.provider !== "toolhive") throw new PipelineMcpGatewayError(`Unsupported MCP gateway provider '${gateway.provider}'.`);
134
- const workspacePath = env.PIPELINE_TARGET_PATH || cwd;
135
- const repoLocalBackends = resolveRepoLocalBackendSpecs(config, {
136
- cwd: workspacePath,
137
- env
138
- });
139
- const inventory = renderToolHiveVmcpInventory(config, {
140
- repoLocalBackends,
141
- toolHiveWorkloads: await listToolHiveGroupWorkloads(gateway.default_profile ?? "default", workspacePath)
142
- });
143
- const configPath = join(workspacePath, ".pipeline", "mcp-gateway", "vmcp.yaml");
144
- mkdirSync(dirname(configPath), { recursive: true });
145
- writeFileSync(configPath, inventory.yaml);
146
- await execa("thv", [
147
- "vmcp",
148
- "validate",
149
- "--config",
150
- configPath
151
- ], {
152
- cwd: workspacePath,
153
- env: await toolhiveEnv(workspacePath),
154
- stdin: "ignore"
155
- });
156
- return {
157
- backendCount: inventory.backends.length,
158
- configPath,
159
- readinessFailures: [...repoLocalBackends.filter((backend) => backend.enabled && !backend.readiness.ok).map((backend) => `${backend.id}: ${backend.readiness.reason}`), ...inventory.backends.filter((backend) => backend.enabled && backend.required && !backend.url).map((backend) => `${backend.name}: missing ToolHive workload`)],
160
- workspacePath
161
- };
120
+ function reconcileGateway(config, cwd, env = process.env) {
121
+ return runMcpGatewayEffect(reconcileGatewayEffect(config, cwd, env));
162
122
  }
163
- async function listToolHiveGroupWorkloads(group, cwd) {
164
- const result = await execa("thv", [
165
- "list",
166
- "--group",
167
- group,
168
- "--format",
169
- "json"
170
- ], {
171
- cwd,
172
- env: await toolhiveEnv(cwd),
173
- stdin: "ignore"
174
- });
175
- let parsed;
176
- const stdout = result.stdout.trim();
177
- if (!stdout) return [];
178
- try {
179
- parsed = JSON.parse(stdout);
180
- } catch {
181
- throw new PipelineMcpGatewayError("ToolHive list returned malformed JSON while reconciling MCP gateway workloads.");
182
- }
183
- if (!Array.isArray(parsed)) throw new PipelineMcpGatewayError("ToolHive list returned a non-array payload while reconciling MCP gateway workloads.");
184
- return parsed.flatMap((item) => {
185
- if (!item || typeof item.name !== "string") return [];
186
- return [{
187
- name: item.name,
188
- status: typeof item.status === "string" ? item.status : void 0,
189
- transport: toolHiveWorkloadTransport(item),
190
- url: typeof item.url === "string" ? item.url : void 0
191
- }];
123
+ function reconcileGatewayEffect(config, cwd, env) {
124
+ const gateway = configuredGateway(config);
125
+ if (gateway.provider !== "toolhive") return Effect.fail(new PipelineMcpGatewayError(`Unsupported MCP gateway provider '${gateway.provider}'.`));
126
+ return Effect.gen(function* () {
127
+ const service = yield* McpGatewayService;
128
+ const workspacePath = env.PIPELINE_TARGET_PATH || cwd;
129
+ const repoLocalBackends = resolveRepoLocalBackendSpecs(config, {
130
+ cwd: workspacePath,
131
+ env
132
+ });
133
+ const inventory = renderToolHiveVmcpInventory(config, {
134
+ repoLocalBackends,
135
+ toolHiveWorkloads: yield* service.listToolHiveGroupWorkloads(gateway.default_profile ?? "default", workspacePath)
136
+ });
137
+ const configPath = join(workspacePath, ".pipeline", "mcp-gateway", "vmcp.yaml");
138
+ mkdirSync(dirname(configPath), { recursive: true });
139
+ writeFileSync(configPath, inventory.yaml);
140
+ yield* service.validateToolHiveVmcp(configPath, workspacePath);
141
+ return {
142
+ backendCount: inventory.backends.length,
143
+ configPath,
144
+ readinessFailures: [...repoLocalBackends.filter((backend) => backend.enabled && !backend.readiness.ok).map((backend) => `${backend.id}: ${backend.readiness.reason}`), ...inventory.backends.filter((backend) => backend.enabled && backend.required && !backend.url).map((backend) => `${backend.name}: missing ToolHive workload`)],
145
+ workspacePath
146
+ };
192
147
  });
193
148
  }
194
- function toolHiveWorkloadTransport(item) {
195
- if (typeof item.transport_type === "string") return item.transport_type;
196
- if (typeof item.transport === "string") return item.transport;
197
- }
198
- async function localGatewayStatus(cwd) {
199
- return (await execa("thv", ["list"], {
200
- cwd,
201
- env: await toolhiveEnv(cwd)
202
- })).stdout.trim();
149
+ function localGatewayStatus(cwd) {
150
+ return runMcpGatewayEffect(Effect.gen(function* () {
151
+ return yield* (yield* McpGatewayService).localGatewayStatus(cwd);
152
+ }));
203
153
  }
204
- async function checkGatewayRequiredTools(gateway) {
154
+ function checkGatewayRequiredTools(gateway) {
205
155
  const requiredPrefixes = requiredGatewayToolPrefixes(gateway);
206
- if (requiredPrefixes.length === 0) return {
156
+ if (requiredPrefixes.length === 0) return Effect.succeed({
207
157
  detail: "no required tools declared",
208
158
  name: "gateway-required-tools",
209
159
  passed: true
210
- };
211
- try {
212
- const tools = await listGatewayTools(gateway);
160
+ });
161
+ return Effect.gen(function* () {
162
+ const tools = yield* listGatewayTools(gateway);
213
163
  const missing = requiredPrefixes.filter((prefix) => !tools.some((tool) => tool === prefix || tool.startsWith(`${prefix}_`)));
214
164
  return missing.length === 0 ? {
215
165
  detail: `found: ${requiredPrefixes.join(", ")}`,
@@ -220,54 +170,45 @@ async function checkGatewayRequiredTools(gateway) {
220
170
  name: "gateway-required-tools",
221
171
  passed: false
222
172
  };
223
- } catch (err) {
224
- return {
225
- detail: err instanceof Error ? err.message : String(err),
226
- name: "gateway-required-tools",
227
- passed: false
228
- };
229
- }
173
+ }).pipe(Effect.catchAll((error) => Effect.succeed({
174
+ detail: error instanceof Error ? error.message : String(error),
175
+ name: "gateway-required-tools",
176
+ passed: false
177
+ })));
230
178
  }
231
179
  function requiredGatewayToolPrefixes(gateway) {
232
180
  return [...new Set(Object.values(gateway.backends).filter((backend) => backend.required).flatMap((backend) => backend.tool_prefixes))].sort();
233
181
  }
234
- async function listGatewayTools(gateway) {
182
+ function listGatewayTools(gateway) {
235
183
  const url = gatewayUrl(gateway);
236
- await callGatewayRpc(gateway, url, {
237
- id: 1,
238
- jsonrpc: "2.0",
239
- method: "initialize",
240
- params: {
241
- capabilities: {},
242
- clientInfo: {
243
- name: "@oisincoveney/pipeline",
244
- version: "1"
245
- },
246
- protocolVersion: "2025-06-18"
247
- }
184
+ return Effect.gen(function* () {
185
+ yield* callGatewayRpc(gateway, url, {
186
+ id: 1,
187
+ jsonrpc: "2.0",
188
+ method: "initialize",
189
+ params: {
190
+ capabilities: {},
191
+ clientInfo: {
192
+ name: "@oisincoveney/pipeline",
193
+ version: "1"
194
+ },
195
+ protocolVersion: "2025-06-18"
196
+ }
197
+ });
198
+ const tools = (yield* callGatewayRpc(gateway, url, {
199
+ id: 2,
200
+ jsonrpc: "2.0",
201
+ method: "tools/list",
202
+ params: {}
203
+ })).result?.tools;
204
+ if (!Array.isArray(tools)) return yield* Effect.fail(new PipelineMcpGatewayError("Malformed tools/list response."));
205
+ return tools.flatMap((tool) => tool && typeof tool.name === "string" ? [tool.name] : []);
248
206
  });
249
- const tools = (await callGatewayRpc(gateway, url, {
250
- id: 2,
251
- jsonrpc: "2.0",
252
- method: "tools/list",
253
- params: {}
254
- })).result?.tools;
255
- if (!Array.isArray(tools)) throw new PipelineMcpGatewayError("Malformed tools/list response.");
256
- return tools.flatMap((tool) => tool && typeof tool.name === "string" ? [tool.name] : []);
257
- }
258
- async function callGatewayRpc(gateway, url, body) {
259
- const authorization = process.env[gateway.authorization_env];
260
- const response = await fetch(url, {
261
- body: JSON.stringify(body),
262
- headers: {
263
- Accept: "application/json",
264
- "Content-Type": "application/json",
265
- ...authorization ? { Authorization: authorization } : {}
266
- },
267
- method: "POST"
207
+ }
208
+ function callGatewayRpc(gateway, url, body) {
209
+ return Effect.gen(function* () {
210
+ return yield* (yield* McpGatewayService).callGatewayRpc(url, body, process.env[gateway.authorization_env]);
268
211
  });
269
- if (!response.ok) throw new PipelineMcpGatewayError(`Gateway MCP request failed: HTTP ${response.status}.`);
270
- return await response.json();
271
212
  }
272
213
  function selectedGatewayHosts(host) {
273
214
  return host === "all" ? ["opencode"] : [host];
@@ -314,86 +255,44 @@ function gatewayAuthorizationHeader(gateway) {
314
255
  function gatewayOpenCodeHeaders(gateway) {
315
256
  return { Authorization: gatewayAuthorizationHeader(gateway) };
316
257
  }
317
- async function checkThv(cwd) {
318
- try {
319
- await execa("thv", ["version"], {
320
- cwd,
321
- env: await toolhiveEnv(cwd),
322
- stdin: "ignore"
323
- });
258
+ function checkThv(cwd) {
259
+ return Effect.gen(function* () {
260
+ yield* (yield* McpGatewayService).runToolHiveVersion(cwd);
324
261
  return {
325
262
  detail: "available",
326
263
  name: "toolhive",
327
264
  passed: true
328
265
  };
329
- } catch (err) {
330
- const error = err;
331
- return {
332
- detail: (error.shortMessage || error.stderr || "not available").trim(),
333
- name: "toolhive",
334
- passed: false
335
- };
336
- }
337
- }
338
- async function toolhiveEnv(cwd) {
339
- if (process.env.DOCKER_HOST) return process.env;
340
- const dockerHost = await activeDockerHost(cwd);
341
- return dockerHost ? {
342
- ...process.env,
343
- DOCKER_HOST: dockerHost
344
- } : process.env;
345
- }
346
- async function activeDockerHost(cwd) {
347
- try {
348
- const result = await execa("docker", ["context", "inspect"], {
349
- cwd,
350
- stdin: "ignore"
351
- });
352
- const host = JSON.parse(result.stdout)[0]?.Endpoints?.docker?.Host;
353
- return typeof host === "string" && host.length > 0 ? host : void 0;
354
- } catch {
355
- return;
356
- }
266
+ }).pipe(Effect.catchAll((error) => Effect.succeed({
267
+ detail: error.message || "not available",
268
+ name: "toolhive",
269
+ passed: false
270
+ })));
357
271
  }
358
- async function checkGatewayHealth(gateway) {
272
+ function checkGatewayHealth(gateway) {
359
273
  let url;
360
274
  try {
361
275
  url = gatewayUrl(gateway);
362
276
  } catch (err) {
363
- return {
277
+ return Effect.succeed({
364
278
  detail: err instanceof Error ? err.message : String(err),
365
279
  name: "gateway-health",
366
280
  passed: false
367
- };
281
+ });
368
282
  }
369
- try {
370
- const response = await firstHealthyGatewayResponse(url, gateway);
283
+ return Effect.gen(function* () {
284
+ const response = yield* (yield* McpGatewayService).firstHealthyGatewayResponse(gatewayHealthUrls(url), process.env[gateway.authorization_env]);
371
285
  const passed = Boolean(response);
372
286
  return {
373
287
  detail: response ? `HTTP ${response.status} ${response.url}` : "gateway endpoint did not report healthy",
374
288
  name: "gateway-health",
375
289
  passed
376
290
  };
377
- } catch (err) {
378
- return {
379
- detail: err instanceof Error ? err.message : String(err),
380
- name: "gateway-health",
381
- passed: false
382
- };
383
- }
384
- }
385
- async function firstHealthyGatewayResponse(url, gateway) {
386
- const authorization = process.env[gateway.authorization_env];
387
- for (const healthUrl of gatewayHealthUrls(url)) {
388
- const response = await fetch(healthUrl, {
389
- headers: {
390
- Accept: "application/json, text/event-stream",
391
- ...authorization ? { Authorization: authorization } : {}
392
- },
393
- method: "GET"
394
- });
395
- if (response.status >= 200 && response.status < 300 || response.status === 405) return response;
396
- }
291
+ }).pipe(Effect.catchAll((error) => Effect.succeed({
292
+ detail: error instanceof Error ? error.message : String(error),
293
+ name: "gateway-health",
294
+ passed: false
295
+ })));
397
296
  }
398
297
  function gatewayHealthUrls(url) {
399
298
  const urls = [];
@@ -1,8 +1,8 @@
1
1
  import { PipelineConfigError } from "./config/schemas.js";
2
+ import { ConfigIoService, runConfigIoSync } from "./runtime/services/config-io-service.js";
2
3
  import "./config.js";
3
- import { parseDocument } from "yaml";
4
4
  import { z } from "zod";
5
- import { existsSync, readFileSync } from "node:fs";
5
+ import { Effect } from "effect";
6
6
  import { join } from "node:path";
7
7
  import { homedir } from "node:os";
8
8
  //#region src/moka-global-config.ts
@@ -30,27 +30,27 @@ function mokaGlobalConfigPath(homeDir = homedir()) {
30
30
  }
31
31
  function loadMokaGlobalConfig() {
32
32
  const configPath = mokaGlobalConfigPath();
33
- if (!existsSync(configPath)) return null;
34
- return parseMokaGlobalConfig(readFileSync(configPath, "utf8"), configPath);
33
+ return runConfigIoSync(Effect.gen(function* () {
34
+ const source = yield* (yield* ConfigIoService).readOptionalText(configPath);
35
+ return source === null ? null : yield* parseMokaGlobalConfigEffect(source, configPath);
36
+ }));
35
37
  }
36
38
  function parseMokaGlobalConfig(source, sourcePath) {
37
- const document = parseDocument(source, {
38
- prettyErrors: false,
39
- uniqueKeys: true
39
+ return runConfigIoSync(parseMokaGlobalConfigEffect(source, sourcePath));
40
+ }
41
+ function parseMokaGlobalConfigEffect(source, sourcePath) {
42
+ return Effect.gen(function* () {
43
+ const yaml = yield* (yield* ConfigIoService).parseYaml(source, sourcePath);
44
+ const parsed = mokaGlobalConfigSchema.safeParse(yaml);
45
+ if (!parsed.success) {
46
+ const issues = parsed.error.issues.map((issue) => ({
47
+ path: issue.path.join("."),
48
+ message: issue.message
49
+ }));
50
+ return yield* Effect.fail(new PipelineConfigError("PIPELINE_CONFIG_VALIDATION_ERROR", [`Invalid ${sourcePath}:`, ...issues.map((issue) => issue.path ? `- ${issue.path}: ${issue.message}` : `- ${issue.message}`)].join("\n"), issues));
51
+ }
52
+ return parsed.data;
40
53
  });
41
- if (document.errors.length > 0) throw new PipelineConfigError("PIPELINE_CONFIG_PARSE_ERROR", `Failed to parse ${sourcePath}`, document.errors.map((err) => ({
42
- message: err.message,
43
- path: sourcePath
44
- })));
45
- const parsed = mokaGlobalConfigSchema.safeParse(document.toJS());
46
- if (!parsed.success) {
47
- const issues = parsed.error.issues.map((issue) => ({
48
- path: issue.path.join("."),
49
- message: issue.message
50
- }));
51
- throw new PipelineConfigError("PIPELINE_CONFIG_VALIDATION_ERROR", [`Invalid ${sourcePath}:`, ...issues.map((issue) => issue.path ? `- ${issue.path}: ${issue.message}` : `- ${issue.message}`)].join("\n"), issues);
52
- }
53
- return parsed.data;
54
54
  }
55
55
  //#endregion
56
56
  export { MOKA_GLOBAL_CONFIG_PATH, loadMokaGlobalConfig, mokaGlobalConfigPath, mokaGlobalConfigSchema, parseMokaGlobalConfig };
@@ -5,13 +5,13 @@ import { z } from "zod";
5
5
  //#region src/moka-submit.d.ts
6
6
  declare const mokaSubmitDirectHooksSchema: z.ZodRecord<z.ZodEnum<{
7
7
  "workflow.start": "workflow.start";
8
- "node.finish": "node.finish";
9
- "node.start": "node.start";
10
8
  "workflow.success": "workflow.success";
11
9
  "workflow.failure": "workflow.failure";
12
10
  "workflow.complete": "workflow.complete";
11
+ "node.start": "node.start";
13
12
  "node.success": "node.success";
14
13
  "node.error": "node.error";
14
+ "node.finish": "node.finish";
15
15
  "gate.failure": "gate.failure";
16
16
  }> & z.core.$partial, z.ZodDiscriminatedUnion<[z.ZodObject<{
17
17
  failure: z.ZodDefault<z.ZodEnum<{
@@ -94,13 +94,13 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
94
94
  }, z.core.$strict>>;
95
95
  hooks: z.ZodOptional<z.ZodRecord<z.ZodEnum<{
96
96
  "workflow.start": "workflow.start";
97
- "node.finish": "node.finish";
98
- "node.start": "node.start";
99
97
  "workflow.success": "workflow.success";
100
98
  "workflow.failure": "workflow.failure";
101
99
  "workflow.complete": "workflow.complete";
100
+ "node.start": "node.start";
102
101
  "node.success": "node.success";
103
102
  "node.error": "node.error";
103
+ "node.finish": "node.finish";
104
104
  "gate.failure": "gate.failure";
105
105
  }> & z.core.$partial, z.ZodDiscriminatedUnion<[z.ZodObject<{
106
106
  failure: z.ZodDefault<z.ZodEnum<{
@@ -160,8 +160,8 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
160
160
  }, z.core.$strict>>;
161
161
  serviceAccountName: z.ZodOptional<z.ZodString>;
162
162
  mode: z.ZodEnum<{
163
- quick: "quick";
164
163
  full: "full";
164
+ quick: "quick";
165
165
  }>;
166
166
  schedulePath: z.ZodOptional<z.ZodString>;
167
167
  scheduleYaml: z.ZodOptional<z.ZodString>;
@@ -206,13 +206,13 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
206
206
  }, z.core.$strict>>;
207
207
  hooks: z.ZodOptional<z.ZodRecord<z.ZodEnum<{
208
208
  "workflow.start": "workflow.start";
209
- "node.finish": "node.finish";
210
- "node.start": "node.start";
211
209
  "workflow.success": "workflow.success";
212
210
  "workflow.failure": "workflow.failure";
213
211
  "workflow.complete": "workflow.complete";
212
+ "node.start": "node.start";
214
213
  "node.success": "node.success";
215
214
  "node.error": "node.error";
215
+ "node.finish": "node.finish";
216
216
  "gate.failure": "gate.failure";
217
217
  }> & z.core.$partial, z.ZodDiscriminatedUnion<[z.ZodObject<{
218
218
  failure: z.ZodDefault<z.ZodEnum<{