@oisincoveney/pipeline 3.15.0 → 3.15.2
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/defaults/profiles.yaml +2 -0
- package/dist/argo-graph.js +5 -4
- package/dist/cli/program.js +5 -2
- package/dist/commands/bench-command.js +1 -1
- package/dist/commands/pipeline-command.js +1 -1
- package/dist/commands/runner-command-command.js +1 -1
- package/dist/commands/ticket/create.js +2 -1
- package/dist/config/schema/catalog.d.ts +11 -0
- package/dist/config/schema/catalog.js +68 -0
- package/dist/config/schema/mcp.js +90 -0
- package/dist/config/schema/reference-validation.js +83 -0
- package/dist/config/schemas.d.ts +1 -8
- package/dist/config/schemas.js +5 -217
- package/dist/config/validate.js +2 -1
- package/dist/install-commands/opencode.js +1 -1
- package/dist/install-commands.js +2 -2
- package/dist/loop/argo-poll.js +3 -3
- package/dist/loop/merge.js +2 -2
- package/dist/mcp/gateway-config.js +47 -0
- package/dist/mcp/gateway-doctor.js +192 -0
- package/dist/mcp/gateway-error.js +2 -3
- package/dist/mcp/gateway-reconcile.js +54 -0
- package/dist/mcp/gateway-runtime.js +8 -0
- package/dist/mcp/host-config.js +69 -0
- package/dist/mcp/host-renderers.js +48 -0
- package/dist/pipeline-runtime.js +32 -232
- package/dist/planning/compile.d.ts +2 -2
- package/dist/planning/compile.js +18 -74
- package/dist/planning/generate.js +3 -2
- package/dist/planning/graph.d.ts +6 -0
- package/dist/planning/graph.js +108 -1
- package/dist/run-control/command-context.js +15 -0
- package/dist/run-control/commands.js +15 -334
- package/dist/run-control/contracts.js +1 -1
- package/dist/run-control/detach.js +1 -1
- package/dist/run-control/file-errors.js +6 -0
- package/dist/run-control/logical-segment.js +15 -0
- package/dist/run-control/resume-command.js +39 -0
- package/dist/run-control/run-artifacts-command.js +138 -0
- package/dist/run-control/run-command-domain.js +31 -0
- package/dist/run-control/run-control-store.js +2 -1
- package/dist/run-control/run-query-command.js +129 -0
- package/dist/run-control/run-state-lock.js +4 -0
- package/dist/run-control/runtime-event-projection.js +98 -0
- package/dist/run-control/runtime-reporter.js +26 -89
- package/dist/run-control/stop-command.js +60 -0
- package/dist/run-control/store-fs-effects.js +48 -0
- package/dist/run-control/store-manifest.js +82 -0
- package/dist/run-control/store-paths.js +47 -0
- package/dist/run-control/store.js +22 -175
- package/dist/run-control/supervisor.js +7 -6
- package/dist/runner/opencode-excludes.js +23 -0
- package/dist/runner/subprocess-result.js +62 -0
- package/dist/runner/subprocess.js +72 -0
- package/dist/runner/timeouts.js +19 -0
- package/dist/runner-command/finalize.js +1 -1
- package/dist/runner-command/lifecycle.js +1 -1
- package/dist/runner-command/run.js +4 -4
- package/dist/runner.d.ts +14 -32
- package/dist/runner.js +102 -263
- package/dist/runtime/agent-node/agent-node.js +4 -4
- package/dist/runtime/changed-files/changed-files.js +1 -1
- package/dist/runtime/context/context.js +1 -1
- package/dist/runtime/detached-race.js +27 -0
- package/dist/runtime/drain-merge/drain-merge.js +6 -6
- package/dist/runtime/durable-store/postgres/postgres-store.js +4 -3
- package/dist/runtime/json-validation/json-validation.js +1 -1
- package/dist/runtime/local-scheduler.js +1 -1
- package/dist/runtime/node-state-tracker.js +133 -58
- package/dist/runtime/open-pull-request/open-pull-request.js +11 -11
- package/dist/runtime/opencode-server.js +1 -1
- package/dist/runtime/opencode-session-executor.js +16 -15
- package/dist/runtime/parallel-node/parallel-node.js +2 -2
- package/dist/runtime/remediation/remediation.js +246 -0
- package/dist/runtime/scheduler.js +1 -1
- package/dist/runtime/services/agent-node-runtime-service.js +1 -1
- package/dist/runtime/services/backlog-service.d.ts +1 -1
- package/dist/runtime/services/backlog-service.js +1 -1
- package/dist/runtime/services/command-executor-service.js +1 -1
- package/dist/runtime/services/config-io-service.js +2 -2
- package/dist/runtime/services/drain-merge-git-service.js +1 -1
- package/dist/runtime/services/file-system-service.js +2 -2
- package/dist/runtime/services/git-porcelain-service.js +1 -1
- package/dist/runtime/services/kubernetes-argo-service.js +2 -2
- package/dist/runtime/services/mcp-gateway-service.js +2 -2
- package/dist/runtime/services/open-pull-request-git-service.js +1 -1
- package/dist/runtime/services/opencode-runtime-server-service.js +1 -1
- package/dist/runtime/services/opencode-sdk-service.js +1 -1
- package/dist/runtime/services/repo-io-service.js +2 -2
- package/dist/runtime/services/runner-command-io-service.js +4 -4
- package/dist/runtime/services/runner-event-sink-http-service.js +1 -1
- package/dist/runtime/services/worktree-service.js +2 -2
- package/dist/runtime/workflow-lifecycle.js +2 -2
- package/dist/schedule/backlog-context.js +6 -8
- package/dist/serialized-write-queue.js +35 -0
- package/dist/tickets/ticket-graph-dto.js +3 -5
- package/dist/tickets/ticket-graph.d.ts +1 -2
- package/dist/tickets/ticket-graph.js +13 -30
- package/dist/tickets/ticket-selection.js +1 -1
- package/docs/runtime-actor-model.md +30 -0
- package/package.json +3 -3
- package/dist/mcp/gateway.js +0 -386
package/defaults/profiles.yaml
CHANGED
|
@@ -154,6 +154,7 @@ profiles:
|
|
|
154
154
|
runner: opencode
|
|
155
155
|
scheduling_roles: [implementation]
|
|
156
156
|
description: Add focused failing tests for the requested behavior.
|
|
157
|
+
timeout_ms: 900000
|
|
157
158
|
instructions: { inline: "Add focused failing tests for the requested behavior only. Do not change production code. Only edit files matching test paths such as **/*.test.*, **/*.spec.*, **/*_test.*, **/__tests__/**, test/**, or tests/**. NEVER silence lint, type, complexity, or dead-code findings with suppression comments (no // fallow-ignore, // biome-ignore, eslint-disable, oxlint-disable, @ts-ignore, or @ts-expect-error); fix the underlying cause — if a gate flags your test, restructure the test (e.g. move restricted imports into shared support/fixture helpers) rather than suppressing it. Return only valid JSON with top-level changes and verification. Every changes entry must include summary, why, and files. Include risks, followups, and lessons when present. Do not use Markdown fences or prose outside the JSON object." }
|
|
158
159
|
skills: [test]
|
|
159
160
|
mcp_servers: [pipeline-gateway]
|
|
@@ -168,6 +169,7 @@ profiles:
|
|
|
168
169
|
runner: opencode
|
|
169
170
|
scheduling_roles: [implementation]
|
|
170
171
|
description: Implement production code until the failing tests pass.
|
|
172
|
+
timeout_ms: 900000
|
|
171
173
|
instructions: { inline: "Implement the smallest production change that satisfies the failing tests. NEVER silence lint, type, complexity, or dead-code findings with suppression comments (no // fallow-ignore, // biome-ignore, eslint-disable, oxlint-disable, @ts-ignore, or @ts-expect-error); fix the underlying cause — reduce complexity by extracting helpers, remove genuinely dead code, and migrate off deprecated APIs rather than suppressing the warning. Return only valid JSON with top-level changes and verification. Every changes entry must include summary, why, and files. Include risks, followups, and lessons when present. Do not use Markdown fences or prose outside the JSON object." }
|
|
172
174
|
skills: [trace, test, fix, library-first-development]
|
|
173
175
|
mcp_servers: [pipeline-gateway]
|
package/dist/argo-graph.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { uniqueStrings } from "./strings.js";
|
|
2
2
|
import { resolveExecutableDependencyIds } from "./planning/dependency-refs.js";
|
|
3
|
+
import { terminalDependencyItems } from "./planning/graph.js";
|
|
3
4
|
import { z } from "zod";
|
|
4
5
|
import { Data } from "effect";
|
|
5
6
|
//#region src/argo-graph.ts
|
|
@@ -43,10 +44,11 @@ var ArgoGraphCompiler = class {
|
|
|
43
44
|
}
|
|
44
45
|
compile() {
|
|
45
46
|
this.compileNodes(this.plan.topologicalOrder, []);
|
|
47
|
+
const terminalTasks = this.terminalTasks();
|
|
46
48
|
return {
|
|
47
|
-
terminalNodeIds:
|
|
49
|
+
terminalNodeIds: terminalTasks.map((task) => task.nodeId),
|
|
48
50
|
tasks: this.tasks,
|
|
49
|
-
terminalTaskNames:
|
|
51
|
+
terminalTaskNames: terminalTasks.map((task) => task.taskName),
|
|
50
52
|
workflowId: this.plan.workflowId
|
|
51
53
|
};
|
|
52
54
|
}
|
|
@@ -92,8 +94,7 @@ var ArgoGraphCompiler = class {
|
|
|
92
94
|
return uniqueStrings(resolveExecutableDependencyIds(this.nodeById, nodeIds).map((id) => argoTaskName(id)));
|
|
93
95
|
}
|
|
94
96
|
terminalTasks() {
|
|
95
|
-
|
|
96
|
-
return this.tasks.filter((task) => !dependedOn.has(task.taskName));
|
|
97
|
+
return terminalDependencyItems(this.tasks, (task) => task.taskName, (task) => task.dependencies);
|
|
97
98
|
}
|
|
98
99
|
};
|
|
99
100
|
function argoTaskName(nodeId) {
|
package/dist/cli/program.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import { flattenNodes } from "../planning/graph.js";
|
|
1
2
|
import { loadPipelineConfig } from "../config/load.js";
|
|
2
3
|
import "../config.js";
|
|
3
4
|
import { loadMokaGlobalConfig } from "../moka-global-config.js";
|
|
4
|
-
import { flattenNodes } from "../planning/graph.js";
|
|
5
5
|
import { createOrchestratorLaunchPlan, createRunnerLaunchPlan } from "../runner.js";
|
|
6
6
|
import { compileWorkflowPlan } from "../planning/compile.js";
|
|
7
7
|
import { compileScheduleArtifact, generateScheduleArtifact, parseScheduleArtifact } from "../planning/generate.js";
|
|
8
8
|
import { withRunControlStoreScoped } from "../run-control/run-control-store.js";
|
|
9
|
-
import {
|
|
9
|
+
import { renderGatewayConfig } from "../mcp/gateway-config.js";
|
|
10
10
|
import { generateRuntimeRunId, resolveWorkflowSelection } from "../runtime/context/context.js";
|
|
11
11
|
import "../runtime/context/index.js";
|
|
12
12
|
import { runPipelineFromConfig } from "../pipeline-runtime.js";
|
|
@@ -19,6 +19,9 @@ import { registerTicketCommand } from "../commands/ticket-command.js";
|
|
|
19
19
|
import { formatConfigLintWarning, lintPipelineConfig } from "../config/lint.js";
|
|
20
20
|
import { parseLoopFlags, runLoopSubmit } from "../loop/loop-command.js";
|
|
21
21
|
import { runLoopControllerEntrypoint } from "../loop/loop-controller-entrypoint.js";
|
|
22
|
+
import { runGatewayDoctor } from "../mcp/gateway-doctor.js";
|
|
23
|
+
import { localGatewayStatus, reconcileGateway, startLocalGateway } from "../mcp/gateway-reconcile.js";
|
|
24
|
+
import { configureGatewayHosts } from "../mcp/host-config.js";
|
|
22
25
|
import { formatPipelineInitResult, initPipelineProject } from "../pipeline-init.js";
|
|
23
26
|
import { registerRunControlCommands } from "../run-control/commands.js";
|
|
24
27
|
import { startDetachedRunController } from "../run-control/detach.js";
|
|
@@ -2,7 +2,7 @@ import { buildEvalReport, renderEvalReport } from "../bench/eval-report.js";
|
|
|
2
2
|
import { Context, Effect, Layer } from "effect";
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
//#region src/commands/bench-command.ts
|
|
5
|
-
var BenchCommandService = class extends Context.
|
|
5
|
+
var BenchCommandService = class extends Context.Service()("BenchCommandService") {};
|
|
6
6
|
const BenchCommandServiceLive = Layer.succeed(BenchCommandService, {
|
|
7
7
|
readResults: (path) => Effect.try(() => JSON.parse(readFileSync(path, "utf8"))),
|
|
8
8
|
writeReport: (report) => Effect.try(() => process.stdout.write(`${report}\n`))
|
|
@@ -12,7 +12,7 @@ const BUILTIN_PIPE_COMMANDS = new Set([
|
|
|
12
12
|
"runner-command",
|
|
13
13
|
"ticket"
|
|
14
14
|
]);
|
|
15
|
-
var EntrypointCommandService = class extends Context.
|
|
15
|
+
var EntrypointCommandService = class extends Context.Service()("EntrypointCommandService") {};
|
|
16
16
|
const createEntrypointCommandServiceLive = (runEntrypoint) => Layer.succeed(EntrypointCommandService, { runEntrypoint: (entrypoint, task, opts) => Effect.tryPromise({
|
|
17
17
|
catch: (error) => error,
|
|
18
18
|
try: () => runEntrypoint(entrypoint, task, opts)
|
|
@@ -3,7 +3,7 @@ import { runRunnerFinalize } from "../runner-command/finalize.js";
|
|
|
3
3
|
import { runRunnerLifecycle } from "../runner-command/lifecycle.js";
|
|
4
4
|
import { Context, Effect, Layer } from "effect";
|
|
5
5
|
//#region src/commands/runner-command-command.ts
|
|
6
|
-
var RunnerCommandService = class extends Context.
|
|
6
|
+
var RunnerCommandService = class extends Context.Service()("RunnerCommandService") {};
|
|
7
7
|
const RunnerCommandServiceLive = Layer.succeed(RunnerCommandService, {
|
|
8
8
|
finalize: (options) => Effect.tryPromise({
|
|
9
9
|
catch: (error) => error,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { parseTicketPlanEffect } from "../../tickets/ticket-plan.js";
|
|
2
2
|
import { loadPipelineConfig } from "../../config/load.js";
|
|
3
3
|
import "../../config.js";
|
|
4
|
-
import { createRunnerLaunchPlan
|
|
4
|
+
import { createRunnerLaunchPlan } from "../../runner.js";
|
|
5
|
+
import { runLaunchPlan } from "../../runner/subprocess.js";
|
|
5
6
|
import { normalizeRunnerOutput } from "../../runner-output.js";
|
|
6
7
|
import { BacklogServiceLive } from "../../runtime/services/backlog-service.js";
|
|
7
8
|
import { TicketCommandError, currentWorktreePath, errorMessage, writeLineEffect } from "./shared.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/config/schema/catalog.d.ts
|
|
2
|
+
declare const RUNNER_TYPES: readonly ["opencode", "command"];
|
|
3
|
+
declare const NODE_KINDS: readonly ["agent", "command", "builtin", "group", "parallel"];
|
|
4
|
+
declare const HOOK_EVENTS: readonly ["workflow.start", "workflow.success", "workflow.failure", "workflow.complete", "node.start", "node.success", "node.error", "node.finish", "gate.failure"];
|
|
5
|
+
declare const GATE_KINDS: readonly ["acceptance", "artifact", "builtin", "changed_files", "command", "json_schema", "verdict"];
|
|
6
|
+
declare const SCHEDULE_BASELINES: readonly ["execute", "quick"];
|
|
7
|
+
declare const SCHEDULING_ROLES: readonly ["coverage", "implementation"];
|
|
8
|
+
declare const MCP_GATEWAY_BACKEND_LOCALITIES: readonly ["repo-local", "repo-scoped-remote", "shared-remote"];
|
|
9
|
+
declare const MCP_GATEWAY_WORKSPACE_PATH_SOURCES: readonly ["PIPELINE_TARGET_PATH", "cwd"];
|
|
10
|
+
//#endregion
|
|
11
|
+
export { GATE_KINDS, HOOK_EVENTS, MCP_GATEWAY_BACKEND_LOCALITIES, MCP_GATEWAY_WORKSPACE_PATH_SOURCES, NODE_KINDS, RUNNER_TYPES, SCHEDULE_BASELINES, SCHEDULING_ROLES };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
//#region src/config/schema/catalog.ts
|
|
2
|
+
const ID_RE = /^[a-z][a-z0-9-]*$/;
|
|
3
|
+
const REASONING_EFFORTS = [
|
|
4
|
+
"none",
|
|
5
|
+
"low",
|
|
6
|
+
"medium",
|
|
7
|
+
"high",
|
|
8
|
+
"xhigh"
|
|
9
|
+
];
|
|
10
|
+
const RUNNER_TYPES = ["opencode", "command"];
|
|
11
|
+
const HOOK_EVENTS = [
|
|
12
|
+
"workflow.start",
|
|
13
|
+
"workflow.success",
|
|
14
|
+
"workflow.failure",
|
|
15
|
+
"workflow.complete",
|
|
16
|
+
"node.start",
|
|
17
|
+
"node.success",
|
|
18
|
+
"node.error",
|
|
19
|
+
"node.finish",
|
|
20
|
+
"gate.failure"
|
|
21
|
+
];
|
|
22
|
+
const TOOL_NAMES = [
|
|
23
|
+
"read",
|
|
24
|
+
"list",
|
|
25
|
+
"grep",
|
|
26
|
+
"glob",
|
|
27
|
+
"bash",
|
|
28
|
+
"edit",
|
|
29
|
+
"write",
|
|
30
|
+
"task"
|
|
31
|
+
];
|
|
32
|
+
const FILESYSTEM_MODES = ["read-only", "workspace-write"];
|
|
33
|
+
const NETWORK_MODES = ["inherit", "disabled"];
|
|
34
|
+
const OUTPUT_FORMATS = [
|
|
35
|
+
"text",
|
|
36
|
+
"json",
|
|
37
|
+
"jsonl",
|
|
38
|
+
"json_schema"
|
|
39
|
+
];
|
|
40
|
+
const BUILTIN_GATES = [
|
|
41
|
+
"duplication",
|
|
42
|
+
"fallow",
|
|
43
|
+
"lint",
|
|
44
|
+
"semgrep",
|
|
45
|
+
"test",
|
|
46
|
+
"typecheck"
|
|
47
|
+
];
|
|
48
|
+
const RETRY_REASONS = [
|
|
49
|
+
"exit_nonzero",
|
|
50
|
+
"gate_failure",
|
|
51
|
+
"timeout"
|
|
52
|
+
];
|
|
53
|
+
const SCHEDULE_BASELINES = ["execute", "quick"];
|
|
54
|
+
const SCHEDULE_STRATEGIES = ["planner"];
|
|
55
|
+
const SCHEDULING_ROLES = ["coverage", "implementation"];
|
|
56
|
+
const MCP_GATEWAY_BACKEND_LOCALITIES = [
|
|
57
|
+
"repo-local",
|
|
58
|
+
"repo-scoped-remote",
|
|
59
|
+
"shared-remote"
|
|
60
|
+
];
|
|
61
|
+
const MCP_GATEWAY_WORKSPACE_PATH_SOURCES = ["PIPELINE_TARGET_PATH", "cwd"];
|
|
62
|
+
const PIPELINE_GATEWAY_SERVER_ID = "pipeline-gateway";
|
|
63
|
+
const DEFAULT_RUNNER_COMMAND_GIT_COMMITTER = {
|
|
64
|
+
email: "git@oisin.ee",
|
|
65
|
+
name: "oisin-bot"
|
|
66
|
+
};
|
|
67
|
+
//#endregion
|
|
68
|
+
export { BUILTIN_GATES, DEFAULT_RUNNER_COMMAND_GIT_COMMITTER, FILESYSTEM_MODES, HOOK_EVENTS, ID_RE, MCP_GATEWAY_BACKEND_LOCALITIES, MCP_GATEWAY_WORKSPACE_PATH_SOURCES, NETWORK_MODES, OUTPUT_FORMATS, PIPELINE_GATEWAY_SERVER_ID, REASONING_EFFORTS, RETRY_REASONS, RUNNER_TYPES, SCHEDULE_BASELINES, SCHEDULE_STRATEGIES, SCHEDULING_ROLES, TOOL_NAMES };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { MCP_GATEWAY_BACKEND_LOCALITIES, MCP_GATEWAY_WORKSPACE_PATH_SOURCES } from "./catalog.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/config/schema/mcp.ts
|
|
4
|
+
const REPO_LOCAL_WORKSPACE_PATH_SOURCE_MESSAGE = [
|
|
5
|
+
"repo-local gateway backend must declare workspace_path_source",
|
|
6
|
+
"as",
|
|
7
|
+
"PIPELINE_TARGET_PATH or cwd"
|
|
8
|
+
].join(" ");
|
|
9
|
+
const mcpServerBaseSchema = z.object({
|
|
10
|
+
args: z.array(z.string()).optional(),
|
|
11
|
+
bearer_token_env_var: z.string().min(1).optional(),
|
|
12
|
+
command: z.string().min(1).optional(),
|
|
13
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
14
|
+
headers: z.record(z.string(), z.string()).optional(),
|
|
15
|
+
url: z.string().url().refine((value) => ["http:", "https:"].includes(new URL(value).protocol), { message: "MCP server url must use http or https" }).optional()
|
|
16
|
+
}).strict();
|
|
17
|
+
const hasCommand = (server) => Boolean(server.command);
|
|
18
|
+
const hasUrl = (server) => Boolean(server.url);
|
|
19
|
+
const hasAuthorizationHeader = (server) => Object.keys(server.headers ?? {}).some((key) => key.toLowerCase() === "authorization");
|
|
20
|
+
const mcpServerRefinements = [
|
|
21
|
+
{
|
|
22
|
+
message: "MCP server must declare exactly one of command or url",
|
|
23
|
+
path: (server) => hasCommand(server) ? ["url"] : ["command"],
|
|
24
|
+
violates: (server) => hasCommand(server) === hasUrl(server)
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
message: "args are only valid for command MCP servers",
|
|
28
|
+
path: () => ["args"],
|
|
29
|
+
violates: (server) => hasUrl(server) && Boolean(server.args)
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
message: "env is only valid for command MCP servers",
|
|
33
|
+
path: () => ["env"],
|
|
34
|
+
violates: (server) => hasUrl(server) && Boolean(server.env)
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
message: "headers are only valid for url MCP servers",
|
|
38
|
+
path: () => ["headers"],
|
|
39
|
+
violates: (server) => hasCommand(server) && Boolean(server.headers)
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
message: "bearer_token_env_var is only valid for url MCP servers",
|
|
43
|
+
path: () => ["bearer_token_env_var"],
|
|
44
|
+
violates: (server) => hasCommand(server) && Boolean(server.bearer_token_env_var)
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
message: "headers.Authorization cannot be combined with bearer_token_env_var",
|
|
48
|
+
path: () => ["bearer_token_env_var"],
|
|
49
|
+
violates: (server) => hasUrl(server) && Boolean(server.bearer_token_env_var) && hasAuthorizationHeader(server)
|
|
50
|
+
}
|
|
51
|
+
];
|
|
52
|
+
const mcpServerSchema = mcpServerBaseSchema.superRefine((server, ctx) => {
|
|
53
|
+
for (const refinement of mcpServerRefinements) if (refinement.violates(server)) ctx.addIssue({
|
|
54
|
+
code: "custom",
|
|
55
|
+
message: refinement.message,
|
|
56
|
+
path: refinement.path(server)
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
const mcpGatewayBackendSchema = z.object({
|
|
60
|
+
locality: z.enum(MCP_GATEWAY_BACKEND_LOCALITIES),
|
|
61
|
+
required: z.boolean().default(true),
|
|
62
|
+
tool_prefixes: z.array(z.string().min(1)).min(1),
|
|
63
|
+
workspace_path_source: z.enum(MCP_GATEWAY_WORKSPACE_PATH_SOURCES).optional()
|
|
64
|
+
}).strict().superRefine((backend, ctx) => {
|
|
65
|
+
if (backend.locality === "repo-local") {
|
|
66
|
+
if (!backend.workspace_path_source) ctx.addIssue({
|
|
67
|
+
code: "custom",
|
|
68
|
+
message: REPO_LOCAL_WORKSPACE_PATH_SOURCE_MESSAGE,
|
|
69
|
+
path: ["workspace_path_source"]
|
|
70
|
+
});
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (backend.workspace_path_source) ctx.addIssue({
|
|
74
|
+
code: "custom",
|
|
75
|
+
message: "workspace_path_source is only valid for repo-local gateway backends",
|
|
76
|
+
path: ["workspace_path_source"]
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
const mcpGatewaySchema = z.object({
|
|
80
|
+
backends: z.record(z.string(), mcpGatewayBackendSchema).default({}),
|
|
81
|
+
default_profile: z.string().min(1).optional(),
|
|
82
|
+
host_scope: z.enum(["project", "global"]).default("project"),
|
|
83
|
+
mode: z.enum(["hosted", "local"]),
|
|
84
|
+
provider: z.literal("toolhive"),
|
|
85
|
+
authorization_env: z.string().min(1).default("PIPELINE_MCP_GATEWAY_AUTHORIZATION"),
|
|
86
|
+
url: z.string().url().refine((value) => ["http:", "https:"].includes(new URL(value).protocol), { message: "MCP gateway url must use http or https" }).optional(),
|
|
87
|
+
url_env: z.string().min(1).default("PIPELINE_MCP_GATEWAY_URL")
|
|
88
|
+
}).strict();
|
|
89
|
+
//#endregion
|
|
90
|
+
export { mcpGatewaySchema, mcpServerSchema };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
//#region src/config/schema/reference-validation.ts
|
|
2
|
+
function validateConfigReferences(config, ctx) {
|
|
3
|
+
addConfigSchemaIssues(ctx, configReferenceIssues(config));
|
|
4
|
+
}
|
|
5
|
+
function configReferenceIssues(config) {
|
|
6
|
+
return [
|
|
7
|
+
...missingRegistryReferenceIssue({
|
|
8
|
+
message: (_field, value) => `default workflow '${value}' is not declared`,
|
|
9
|
+
path: ["default_workflow"],
|
|
10
|
+
registry: config.workflows,
|
|
11
|
+
value: config.default_workflow
|
|
12
|
+
}),
|
|
13
|
+
...registryReferenceIssues("entrypoints", config.entrypoints, [{
|
|
14
|
+
field: "workflow",
|
|
15
|
+
message: (entrypointId, value) => `entrypoint '${entrypointId}' references missing workflow '${value}'`,
|
|
16
|
+
read: (entrypoint) => entrypoint.workflow,
|
|
17
|
+
registry: config.workflows
|
|
18
|
+
}, {
|
|
19
|
+
field: "schedule",
|
|
20
|
+
message: (entrypointId, value) => `entrypoint '${entrypointId}' references missing schedule '${value}'`,
|
|
21
|
+
read: (entrypoint) => entrypoint.schedule,
|
|
22
|
+
registry: config.schedules
|
|
23
|
+
}]),
|
|
24
|
+
...registryReferenceIssues("schedules", config.schedules, [{
|
|
25
|
+
field: "planner_profile",
|
|
26
|
+
message: (scheduleId, value) => `schedule '${scheduleId}' references missing planner profile '${value}'`,
|
|
27
|
+
read: (schedule) => schedule.planner_profile,
|
|
28
|
+
registry: config.profiles
|
|
29
|
+
}, {
|
|
30
|
+
field: "node_catalog",
|
|
31
|
+
message: (scheduleId, value) => `schedule '${scheduleId}' references missing scheduler node catalog '${value}'`,
|
|
32
|
+
read: (schedule) => schedule.node_catalog,
|
|
33
|
+
registry: config.scheduler.node_catalogs
|
|
34
|
+
}]),
|
|
35
|
+
...registryReferenceIssues("scheduler.commands", config.scheduler.commands, [{
|
|
36
|
+
field: "catalog",
|
|
37
|
+
message: (commandId, value) => `scheduler command '${commandId}' references missing node catalog '${value}'`,
|
|
38
|
+
read: (command) => command.catalog,
|
|
39
|
+
registry: config.scheduler.node_catalogs
|
|
40
|
+
}, {
|
|
41
|
+
field: "schedule",
|
|
42
|
+
message: (commandId, value) => `scheduler command '${commandId}' references missing schedule '${value}'`,
|
|
43
|
+
read: (command) => command.schedule,
|
|
44
|
+
registry: config.schedules
|
|
45
|
+
}]),
|
|
46
|
+
...Object.entries(config.scheduler.node_catalogs).flatMap(([catalogId, catalog]) => registryReferenceIssues(`scheduler.node_catalogs.${catalogId}.nodes`, catalog.nodes, [{
|
|
47
|
+
field: "profile",
|
|
48
|
+
message: (nodeId, value) => `scheduler node '${catalogId}.${nodeId}' references missing profile '${value}'`,
|
|
49
|
+
read: (node) => node.profile,
|
|
50
|
+
registry: config.profiles
|
|
51
|
+
}]))
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
function registryReferenceIssues(registryPath, records, rules) {
|
|
55
|
+
return Object.entries(records).flatMap(([recordId, record]) => rules.flatMap((rule) => missingRegistryReferenceIssue({
|
|
56
|
+
message: (_field, value) => rule.message(recordId, value),
|
|
57
|
+
path: [
|
|
58
|
+
registryPath,
|
|
59
|
+
recordId,
|
|
60
|
+
rule.field
|
|
61
|
+
],
|
|
62
|
+
registry: rule.registry,
|
|
63
|
+
value: rule.read(record)
|
|
64
|
+
})));
|
|
65
|
+
}
|
|
66
|
+
function missingRegistryReferenceIssue({ message, path, registry, value }) {
|
|
67
|
+
return value && !Object.hasOwn(registry, value) ? [{
|
|
68
|
+
message: message(String(path.at(-1)), value),
|
|
69
|
+
path
|
|
70
|
+
}] : [];
|
|
71
|
+
}
|
|
72
|
+
function addConfigSchemaIssues(ctx, issues) {
|
|
73
|
+
for (const issue of issues) addConfigSchemaIssue(ctx, issue.path, issue.message);
|
|
74
|
+
}
|
|
75
|
+
function addConfigSchemaIssue(ctx, path, message) {
|
|
76
|
+
ctx.addIssue({
|
|
77
|
+
code: "custom",
|
|
78
|
+
path,
|
|
79
|
+
message
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
83
|
+
export { validateConfigReferences };
|
package/dist/config/schemas.d.ts
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
|
+
import { GATE_KINDS, HOOK_EVENTS, MCP_GATEWAY_BACKEND_LOCALITIES, MCP_GATEWAY_WORKSPACE_PATH_SOURCES, NODE_KINDS, RUNNER_TYPES, SCHEDULE_BASELINES, SCHEDULING_ROLES } from "./schema/catalog.js";
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
|
|
3
4
|
//#region src/config/schemas.d.ts
|
|
4
|
-
declare const RUNNER_TYPES: readonly ["opencode", "command"];
|
|
5
|
-
declare const NODE_KINDS: readonly ["agent", "command", "builtin", "group", "parallel"];
|
|
6
|
-
declare const HOOK_EVENTS: readonly ["workflow.start", "workflow.success", "workflow.failure", "workflow.complete", "node.start", "node.success", "node.error", "node.finish", "gate.failure"];
|
|
7
|
-
declare const GATE_KINDS: readonly ["acceptance", "artifact", "builtin", "changed_files", "command", "json_schema", "verdict"];
|
|
8
|
-
declare const SCHEDULE_BASELINES: readonly ["execute", "quick"];
|
|
9
|
-
declare const SCHEDULING_ROLES: readonly ["coverage", "implementation"];
|
|
10
|
-
declare const MCP_GATEWAY_BACKEND_LOCALITIES: readonly ["repo-local", "repo-scoped-remote", "shared-remote"];
|
|
11
|
-
declare const MCP_GATEWAY_WORKSPACE_PATH_SOURCES: readonly ["PIPELINE_TARGET_PATH", "cwd"];
|
|
12
5
|
type PipelineConfigErrorCode = "PIPELINE_CONFIG_LEGACY_UNSUPPORTED" | "PIPELINE_CONFIG_PARSE_ERROR" | "PIPELINE_CONFIG_VALIDATION_ERROR";
|
|
13
6
|
interface PipelineConfigIssue {
|
|
14
7
|
message: string;
|
package/dist/config/schemas.js
CHANGED
|
@@ -1,71 +1,10 @@
|
|
|
1
|
+
import { BUILTIN_GATES, DEFAULT_RUNNER_COMMAND_GIT_COMMITTER, FILESYSTEM_MODES, NETWORK_MODES, OUTPUT_FORMATS, REASONING_EFFORTS, RETRY_REASONS, RUNNER_TYPES, SCHEDULE_BASELINES, SCHEDULE_STRATEGIES, SCHEDULING_ROLES, TOOL_NAMES } from "./schema/catalog.js";
|
|
2
|
+
import { mcpGatewaySchema, mcpServerSchema } from "./schema/mcp.js";
|
|
3
|
+
import { validateConfigReferences } from "./schema/reference-validation.js";
|
|
1
4
|
import { z } from "zod";
|
|
2
5
|
import { Data } from "effect";
|
|
3
6
|
//#region src/config/schemas.ts
|
|
4
|
-
const
|
|
5
|
-
const reasoningEffortSchema = z.enum([
|
|
6
|
-
"none",
|
|
7
|
-
"low",
|
|
8
|
-
"medium",
|
|
9
|
-
"high",
|
|
10
|
-
"xhigh"
|
|
11
|
-
]);
|
|
12
|
-
const RUNNER_TYPES = ["opencode", "command"];
|
|
13
|
-
const HOOK_EVENTS = [
|
|
14
|
-
"workflow.start",
|
|
15
|
-
"workflow.success",
|
|
16
|
-
"workflow.failure",
|
|
17
|
-
"workflow.complete",
|
|
18
|
-
"node.start",
|
|
19
|
-
"node.success",
|
|
20
|
-
"node.error",
|
|
21
|
-
"node.finish",
|
|
22
|
-
"gate.failure"
|
|
23
|
-
];
|
|
24
|
-
const TOOL_NAMES = [
|
|
25
|
-
"read",
|
|
26
|
-
"list",
|
|
27
|
-
"grep",
|
|
28
|
-
"glob",
|
|
29
|
-
"bash",
|
|
30
|
-
"edit",
|
|
31
|
-
"write",
|
|
32
|
-
"task"
|
|
33
|
-
];
|
|
34
|
-
const FILESYSTEM_MODES = ["read-only", "workspace-write"];
|
|
35
|
-
const NETWORK_MODES = ["inherit", "disabled"];
|
|
36
|
-
const OUTPUT_FORMATS = [
|
|
37
|
-
"text",
|
|
38
|
-
"json",
|
|
39
|
-
"jsonl",
|
|
40
|
-
"json_schema"
|
|
41
|
-
];
|
|
42
|
-
const BUILTIN_GATES = [
|
|
43
|
-
"duplication",
|
|
44
|
-
"fallow",
|
|
45
|
-
"lint",
|
|
46
|
-
"semgrep",
|
|
47
|
-
"test",
|
|
48
|
-
"typecheck"
|
|
49
|
-
];
|
|
50
|
-
const RETRY_REASONS = [
|
|
51
|
-
"exit_nonzero",
|
|
52
|
-
"gate_failure",
|
|
53
|
-
"timeout"
|
|
54
|
-
];
|
|
55
|
-
const SCHEDULE_BASELINES = ["execute", "quick"];
|
|
56
|
-
const SCHEDULE_STRATEGIES = ["planner"];
|
|
57
|
-
const SCHEDULING_ROLES = ["coverage", "implementation"];
|
|
58
|
-
const MCP_GATEWAY_BACKEND_LOCALITIES = [
|
|
59
|
-
"repo-local",
|
|
60
|
-
"repo-scoped-remote",
|
|
61
|
-
"shared-remote"
|
|
62
|
-
];
|
|
63
|
-
const MCP_GATEWAY_WORKSPACE_PATH_SOURCES = ["PIPELINE_TARGET_PATH", "cwd"];
|
|
64
|
-
const PIPELINE_GATEWAY_SERVER_ID = "pipeline-gateway";
|
|
65
|
-
const DEFAULT_RUNNER_COMMAND_GIT_COMMITTER = {
|
|
66
|
-
email: "git@oisin.ee",
|
|
67
|
-
name: "oisin-bot"
|
|
68
|
-
};
|
|
7
|
+
const reasoningEffortSchema = z.enum(REASONING_EFFORTS);
|
|
69
8
|
var PipelineConfigError = class extends Data.TaggedError("PipelineConfigError") {
|
|
70
9
|
constructor(code, message, issues = []) {
|
|
71
10
|
super({
|
|
@@ -99,77 +38,6 @@ const pathRefSchema = z.object({
|
|
|
99
38
|
path: z.string().min(1),
|
|
100
39
|
source_root: z.enum(["package", "project"]).default("project")
|
|
101
40
|
}).strict();
|
|
102
|
-
const mcpServerSchema = z.object({
|
|
103
|
-
args: z.array(z.string()).optional(),
|
|
104
|
-
bearer_token_env_var: z.string().min(1).optional(),
|
|
105
|
-
command: z.string().min(1).optional(),
|
|
106
|
-
env: z.record(z.string(), z.string()).optional(),
|
|
107
|
-
headers: z.record(z.string(), z.string()).optional(),
|
|
108
|
-
url: z.string().url().refine((value) => ["http:", "https:"].includes(new URL(value).protocol), { message: "MCP server url must use http or https" }).optional()
|
|
109
|
-
}).strict().superRefine((server, ctx) => {
|
|
110
|
-
const hasCommand = Boolean(server.command);
|
|
111
|
-
const hasUrl = Boolean(server.url);
|
|
112
|
-
if (hasCommand === hasUrl) ctx.addIssue({
|
|
113
|
-
code: "custom",
|
|
114
|
-
message: "MCP server must declare exactly one of command or url",
|
|
115
|
-
path: hasCommand ? ["url"] : ["command"]
|
|
116
|
-
});
|
|
117
|
-
if (hasUrl && server.args) ctx.addIssue({
|
|
118
|
-
code: "custom",
|
|
119
|
-
message: "args are only valid for command MCP servers",
|
|
120
|
-
path: ["args"]
|
|
121
|
-
});
|
|
122
|
-
if (hasUrl && server.env) ctx.addIssue({
|
|
123
|
-
code: "custom",
|
|
124
|
-
message: "env is only valid for command MCP servers",
|
|
125
|
-
path: ["env"]
|
|
126
|
-
});
|
|
127
|
-
if (hasCommand && server.headers) ctx.addIssue({
|
|
128
|
-
code: "custom",
|
|
129
|
-
message: "headers are only valid for url MCP servers",
|
|
130
|
-
path: ["headers"]
|
|
131
|
-
});
|
|
132
|
-
if (hasCommand && server.bearer_token_env_var) ctx.addIssue({
|
|
133
|
-
code: "custom",
|
|
134
|
-
message: "bearer_token_env_var is only valid for url MCP servers",
|
|
135
|
-
path: ["bearer_token_env_var"]
|
|
136
|
-
});
|
|
137
|
-
if (hasUrl && server.bearer_token_env_var && Object.keys(server.headers ?? {}).some((key) => key.toLowerCase() === "authorization")) ctx.addIssue({
|
|
138
|
-
code: "custom",
|
|
139
|
-
message: "headers.Authorization cannot be combined with bearer_token_env_var",
|
|
140
|
-
path: ["bearer_token_env_var"]
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
const mcpGatewayBackendSchema = z.object({
|
|
144
|
-
locality: z.enum(MCP_GATEWAY_BACKEND_LOCALITIES),
|
|
145
|
-
required: z.boolean().default(true),
|
|
146
|
-
tool_prefixes: z.array(z.string().min(1)).min(1),
|
|
147
|
-
workspace_path_source: z.enum(MCP_GATEWAY_WORKSPACE_PATH_SOURCES).optional()
|
|
148
|
-
}).strict().superRefine((backend, ctx) => {
|
|
149
|
-
if (backend.locality === "repo-local") {
|
|
150
|
-
if (!backend.workspace_path_source) ctx.addIssue({
|
|
151
|
-
code: "custom",
|
|
152
|
-
message: "repo-local gateway backend must declare workspace_path_source as PIPELINE_TARGET_PATH or cwd",
|
|
153
|
-
path: ["workspace_path_source"]
|
|
154
|
-
});
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
if (backend.workspace_path_source) ctx.addIssue({
|
|
158
|
-
code: "custom",
|
|
159
|
-
message: "workspace_path_source is only valid for repo-local gateway backends",
|
|
160
|
-
path: ["workspace_path_source"]
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
const mcpGatewaySchema = z.object({
|
|
164
|
-
backends: strictRecord(mcpGatewayBackendSchema).default({}),
|
|
165
|
-
default_profile: z.string().min(1).optional(),
|
|
166
|
-
host_scope: z.enum(["project", "global"]).default("project"),
|
|
167
|
-
mode: z.enum(["hosted", "local"]),
|
|
168
|
-
provider: z.literal("toolhive"),
|
|
169
|
-
authorization_env: z.string().min(1).default("PIPELINE_MCP_GATEWAY_AUTHORIZATION"),
|
|
170
|
-
url: z.string().url().refine((value) => ["http:", "https:"].includes(new URL(value).protocol), { message: "MCP gateway url must use http or https" }).optional(),
|
|
171
|
-
url_env: z.string().min(1).default("PIPELINE_MCP_GATEWAY_URL")
|
|
172
|
-
}).strict();
|
|
173
41
|
const instructionsSchema = z.object({
|
|
174
42
|
inline: z.string().min(1).optional(),
|
|
175
43
|
path: z.string().min(1).optional()
|
|
@@ -553,86 +421,6 @@ const configSchema = z.object({
|
|
|
553
421
|
version: z.literal(1),
|
|
554
422
|
workflows: strictRecord(workflowSchema).default({})
|
|
555
423
|
}).strict().superRefine(validateConfigReferences);
|
|
556
|
-
function validateConfigReferences(config, ctx) {
|
|
557
|
-
addConfigSchemaIssues(ctx, configReferenceIssues(config));
|
|
558
|
-
}
|
|
559
|
-
function configReferenceIssues(config) {
|
|
560
|
-
return [
|
|
561
|
-
...missingRegistryReferenceIssue({
|
|
562
|
-
message: (_field, value) => `default workflow '${value}' is not declared`,
|
|
563
|
-
path: ["default_workflow"],
|
|
564
|
-
registry: config.workflows,
|
|
565
|
-
value: config.default_workflow
|
|
566
|
-
}),
|
|
567
|
-
...registryReferenceIssues("entrypoints", config.entrypoints, [{
|
|
568
|
-
field: "workflow",
|
|
569
|
-
message: (entrypointId, value) => `entrypoint '${entrypointId}' references missing workflow '${value}'`,
|
|
570
|
-
read: (entrypoint) => "workflow" in entrypoint ? entrypoint.workflow : void 0,
|
|
571
|
-
registry: config.workflows
|
|
572
|
-
}, {
|
|
573
|
-
field: "schedule",
|
|
574
|
-
message: (entrypointId, value) => `entrypoint '${entrypointId}' references missing schedule '${value}'`,
|
|
575
|
-
read: (entrypoint) => "schedule" in entrypoint ? entrypoint.schedule : void 0,
|
|
576
|
-
registry: config.schedules
|
|
577
|
-
}]),
|
|
578
|
-
...registryReferenceIssues("schedules", config.schedules, [{
|
|
579
|
-
field: "planner_profile",
|
|
580
|
-
message: (scheduleId, value) => `schedule '${scheduleId}' references missing planner profile '${value}'`,
|
|
581
|
-
read: (schedule) => schedule.planner_profile,
|
|
582
|
-
registry: config.profiles
|
|
583
|
-
}, {
|
|
584
|
-
field: "node_catalog",
|
|
585
|
-
message: (scheduleId, value) => `schedule '${scheduleId}' references missing scheduler node catalog '${value}'`,
|
|
586
|
-
read: (schedule) => schedule.node_catalog,
|
|
587
|
-
registry: config.scheduler.node_catalogs
|
|
588
|
-
}]),
|
|
589
|
-
...registryReferenceIssues("scheduler.commands", config.scheduler.commands, [{
|
|
590
|
-
field: "catalog",
|
|
591
|
-
message: (commandId, value) => `scheduler command '${commandId}' references missing node catalog '${value}'`,
|
|
592
|
-
read: (command) => command.catalog,
|
|
593
|
-
registry: config.scheduler.node_catalogs
|
|
594
|
-
}, {
|
|
595
|
-
field: "schedule",
|
|
596
|
-
message: (commandId, value) => `scheduler command '${commandId}' references missing schedule '${value}'`,
|
|
597
|
-
read: (command) => command.schedule,
|
|
598
|
-
registry: config.schedules
|
|
599
|
-
}]),
|
|
600
|
-
...Object.entries(config.scheduler.node_catalogs).flatMap(([catalogId, catalog]) => registryReferenceIssues(`scheduler.node_catalogs.${catalogId}.nodes`, catalog.nodes, [{
|
|
601
|
-
field: "profile",
|
|
602
|
-
message: (nodeId, value) => `scheduler node '${catalogId}.${nodeId}' references missing profile '${value}'`,
|
|
603
|
-
read: (node) => node.profile,
|
|
604
|
-
registry: config.profiles
|
|
605
|
-
}]))
|
|
606
|
-
];
|
|
607
|
-
}
|
|
608
|
-
function registryReferenceIssues(registryPath, records, rules) {
|
|
609
|
-
return Object.entries(records).flatMap(([recordId, record]) => rules.flatMap((rule) => missingRegistryReferenceIssue({
|
|
610
|
-
message: (_field, value) => rule.message(recordId, value),
|
|
611
|
-
path: [
|
|
612
|
-
registryPath,
|
|
613
|
-
recordId,
|
|
614
|
-
rule.field
|
|
615
|
-
],
|
|
616
|
-
registry: rule.registry,
|
|
617
|
-
value: rule.read(record)
|
|
618
|
-
})));
|
|
619
|
-
}
|
|
620
|
-
function missingRegistryReferenceIssue({ message, path, registry, value }) {
|
|
621
|
-
return value && !Object.hasOwn(registry, value) ? [{
|
|
622
|
-
message: message(String(path.at(-1)), value),
|
|
623
|
-
path
|
|
624
|
-
}] : [];
|
|
625
|
-
}
|
|
626
|
-
function addConfigSchemaIssues(ctx, issues) {
|
|
627
|
-
for (const issue of issues) addConfigSchemaIssue(ctx, issue.path, issue.message);
|
|
628
|
-
}
|
|
629
|
-
function addConfigSchemaIssue(ctx, path, message) {
|
|
630
|
-
ctx.addIssue({
|
|
631
|
-
code: "custom",
|
|
632
|
-
path,
|
|
633
|
-
message
|
|
634
|
-
});
|
|
635
|
-
}
|
|
636
424
|
function validationError(issues) {
|
|
637
425
|
return new PipelineConfigError("PIPELINE_CONFIG_VALIDATION_ERROR", ["Invalid pipeline config:", ...issues.map((issue) => issue.path ? `- ${issue.path}: ${issue.message}` : `- ${issue.message}`)].join("\n"), issues);
|
|
638
426
|
}
|
|
@@ -643,4 +431,4 @@ function configIssuesFromZodError(error) {
|
|
|
643
431
|
}));
|
|
644
432
|
}
|
|
645
433
|
//#endregion
|
|
646
|
-
export {
|
|
434
|
+
export { PipelineConfigError, configIssuesFromZodError, configSchema, pipelineFileSchema, profilesFileSchema, runnersFileSchema, validationError, workflowSchema };
|
package/dist/config/validate.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { HOOK_EVENTS, ID_RE, PIPELINE_GATEWAY_SERVER_ID
|
|
1
|
+
import { HOOK_EVENTS, ID_RE, PIPELINE_GATEWAY_SERVER_ID } from "./schema/catalog.js";
|
|
2
|
+
import { configIssuesFromZodError, configSchema, validationError } from "./schemas.js";
|
|
2
3
|
import { PACKAGE_ASSET_ROOT } from "../package-assets.js";
|
|
3
4
|
import { resolveFileReference } from "../path-refs.js";
|
|
4
5
|
import { standardOutputSchemaNameFromPath } from "../standard-output-schemas.js";
|
|
@@ -4,9 +4,9 @@ import "../config.js";
|
|
|
4
4
|
import { protectedPermissionOverlay } from "../runtime/protected-paths/protected-paths.js";
|
|
5
5
|
import { RepoIoService, runRepoIoSync } from "../runtime/services/repo-io-service.js";
|
|
6
6
|
import { compileWorkflowPlan } from "../planning/compile.js";
|
|
7
|
-
import { renderOpenCodeGatewayConfig } from "../mcp/gateway.js";
|
|
8
7
|
import { opencodeAgentName } from "../runtime/opencode-agent-name.js";
|
|
9
8
|
import { AGENTS_MD_END, AGENTS_MD_START, COMMAND_HOSTS, GENERATED_MARKER, GENERATED_TS_MARKER, OPENCODE_PROJECT_CONFIG_PATH, OWNER_MARKER_PREFIX, OWNER_TS_MARKER_PREFIX, SINGLE_OPENCODE_PLUGIN_ARRAY_RE, commandIdForHost, compactLines, entrypointDescription, entrypointEntries, instructionsPointer, invocationForHost, profileEntries } from "./shared.js";
|
|
9
|
+
import { renderOpenCodeGatewayConfig } from "../mcp/host-renderers.js";
|
|
10
10
|
import { mergeOpenCodeProjectConfig } from "../opencode-project-config.js";
|
|
11
11
|
import { Effect } from "effect";
|
|
12
12
|
import { basename } from "node:path";
|