@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.
- package/dist/argo-submit.d.ts +2 -4
- package/dist/argo-submit.js +80 -80
- package/dist/cluster-doctor.js +89 -101
- package/dist/config/defaults.js +9 -19
- package/dist/config/load.js +32 -39
- package/dist/config/schemas.d.ts +5 -5
- package/dist/mcp/gateway-error.js +15 -0
- package/dist/mcp/gateway.js +119 -220
- package/dist/moka-global-config.js +20 -20
- package/dist/moka-submit.d.ts +7 -7
- package/dist/pipeline-runtime.js +580 -371
- package/dist/run-state/git-refs.js +124 -94
- package/dist/runner-command-contract.d.ts +2 -2
- package/dist/runner-event-schema.d.ts +6 -6
- package/dist/runner-event-sink.js +37 -69
- package/dist/runtime/agent-node/agent-node.js +214 -173
- package/dist/runtime/changed-files/changed-files.js +15 -27
- package/dist/runtime/changed-files/index.js +2 -0
- package/dist/runtime/drain-merge/drain-merge.js +124 -82
- package/dist/runtime/gates/gates.js +45 -27
- package/dist/runtime/hooks/hooks.js +74 -29
- package/dist/runtime/opencode-server.js +27 -21
- package/dist/runtime/opencode-session-executor.js +101 -44
- package/dist/runtime/parallel-node/parallel-node.js +24 -5
- package/dist/runtime/select-candidate/select-candidate.js +45 -29
- package/dist/runtime/services/agent-node-runtime-service.js +15 -0
- package/dist/runtime/services/command-executor-service.js +8 -0
- package/dist/runtime/services/config-io-service.js +42 -0
- package/dist/runtime/services/drain-merge-git-service.js +10 -0
- package/dist/runtime/services/git-porcelain-service.js +38 -0
- package/dist/runtime/services/kubernetes-argo-service.d.ts +13 -0
- package/dist/runtime/services/kubernetes-argo-service.js +81 -0
- package/dist/runtime/services/mcp-gateway-service.js +184 -0
- package/dist/runtime/services/opencode-sdk-service.js +27 -0
- package/dist/runtime/services/runner-event-sink-http-service.js +80 -0
- package/dist/runtime/services/select-candidate-service.js +13 -0
- package/package.json +1 -1
package/dist/config/schemas.d.ts
CHANGED
|
@@ -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 };
|
package/dist/mcp/gateway.js
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
132
|
-
|
|
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
|
-
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
|
195
|
-
|
|
196
|
-
|
|
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
|
-
|
|
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
|
-
|
|
212
|
-
const tools =
|
|
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
|
-
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
182
|
+
function listGatewayTools(gateway) {
|
|
235
183
|
const url = gatewayUrl(gateway);
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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
|
-
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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
|
-
|
|
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
|
-
|
|
370
|
-
const response =
|
|
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
|
-
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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 {
|
|
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
|
-
|
|
34
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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 };
|
package/dist/moka-submit.d.ts
CHANGED
|
@@ -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<{
|