@oisincoveney/pipeline 3.15.1 → 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/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 +18 -25
- 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/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/moka-submit.d.ts +7 -7
- package/dist/planning/compile.d.ts +2 -2
- package/dist/planning/compile.js +18 -74
- package/dist/planning/generate.d.ts +20 -20
- 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/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/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/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-contract.d.ts +2 -2
- package/dist/runner-event-schema.d.ts +10 -10
- package/dist/runner.d.ts +14 -32
- package/dist/runner.js +102 -263
- package/dist/runtime/agent-node/agent-node.js +1 -1
- package/dist/runtime/context/context.js +1 -1
- package/dist/runtime/detached-race.js +27 -0
- package/dist/runtime/opencode-session-executor.js +2 -7
- package/dist/schedule/backlog-context.js +6 -8
- package/dist/tickets/ticket-graph-dto.js +2 -4
- 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/package.json +1 -1
- 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";
|
|
@@ -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;
|
|
@@ -99,10 +92,10 @@ declare const workflowNodeBaseSchema: z.ZodObject<{
|
|
|
99
92
|
models: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
100
93
|
needs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
101
94
|
reasoning_effort: z.ZodOptional<z.ZodEnum<{
|
|
102
|
-
high: "high";
|
|
103
|
-
medium: "medium";
|
|
104
|
-
low: "low";
|
|
105
95
|
none: "none";
|
|
96
|
+
low: "low";
|
|
97
|
+
medium: "medium";
|
|
98
|
+
high: "high";
|
|
106
99
|
xhigh: "xhigh";
|
|
107
100
|
}>>;
|
|
108
101
|
retries: z.ZodOptional<z.ZodObject<{
|
|
@@ -233,8 +226,8 @@ declare const configSchema: z.ZodObject<{
|
|
|
233
226
|
policy: z.ZodOptional<z.ZodObject<{
|
|
234
227
|
commands: z.ZodOptional<z.ZodEnum<{
|
|
235
228
|
allow: "allow";
|
|
236
|
-
"trusted-only": "trusted-only";
|
|
237
229
|
deny: "deny";
|
|
230
|
+
"trusted-only": "trusted-only";
|
|
238
231
|
}>>;
|
|
239
232
|
modules: z.ZodOptional<z.ZodEnum<{
|
|
240
233
|
allow: "allow";
|
|
@@ -262,8 +255,8 @@ declare const configSchema: z.ZodObject<{
|
|
|
262
255
|
global: "global";
|
|
263
256
|
}>>;
|
|
264
257
|
mode: z.ZodEnum<{
|
|
265
|
-
hosted: "hosted";
|
|
266
258
|
local: "local";
|
|
259
|
+
hosted: "hosted";
|
|
267
260
|
}>;
|
|
268
261
|
provider: z.ZodLiteral<"toolhive">;
|
|
269
262
|
authorization_env: z.ZodDefault<z.ZodString>;
|
|
@@ -307,10 +300,10 @@ declare const configSchema: z.ZodObject<{
|
|
|
307
300
|
}, z.core.$strict>>;
|
|
308
301
|
output: z.ZodOptional<z.ZodObject<{
|
|
309
302
|
format: z.ZodEnum<{
|
|
303
|
+
json_schema: "json_schema";
|
|
310
304
|
text: "text";
|
|
311
305
|
json: "json";
|
|
312
306
|
jsonl: "jsonl";
|
|
313
|
-
json_schema: "json_schema";
|
|
314
307
|
}>;
|
|
315
308
|
repair: z.ZodOptional<z.ZodObject<{
|
|
316
309
|
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -320,10 +313,10 @@ declare const configSchema: z.ZodObject<{
|
|
|
320
313
|
schema_path: z.ZodOptional<z.ZodString>;
|
|
321
314
|
}, z.core.$strict>>;
|
|
322
315
|
reasoning_effort: z.ZodOptional<z.ZodEnum<{
|
|
323
|
-
high: "high";
|
|
324
|
-
medium: "medium";
|
|
325
|
-
low: "low";
|
|
326
316
|
none: "none";
|
|
317
|
+
low: "low";
|
|
318
|
+
medium: "medium";
|
|
319
|
+
high: "high";
|
|
327
320
|
xhigh: "xhigh";
|
|
328
321
|
}>>;
|
|
329
322
|
rules: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -386,10 +379,10 @@ declare const configSchema: z.ZodObject<{
|
|
|
386
379
|
disabled: "disabled";
|
|
387
380
|
}>>>;
|
|
388
381
|
output_formats: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
382
|
+
json_schema: "json_schema";
|
|
389
383
|
text: "text";
|
|
390
384
|
json: "json";
|
|
391
385
|
jsonl: "jsonl";
|
|
392
|
-
json_schema: "json_schema";
|
|
393
386
|
}>>>;
|
|
394
387
|
rules: z.ZodOptional<z.ZodBoolean>;
|
|
395
388
|
skills: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -408,10 +401,10 @@ declare const configSchema: z.ZodObject<{
|
|
|
408
401
|
host_models: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
409
402
|
model: z.ZodOptional<z.ZodString>;
|
|
410
403
|
reasoning_effort: z.ZodOptional<z.ZodEnum<{
|
|
411
|
-
high: "high";
|
|
412
|
-
medium: "medium";
|
|
413
|
-
low: "low";
|
|
414
404
|
none: "none";
|
|
405
|
+
low: "low";
|
|
406
|
+
medium: "medium";
|
|
407
|
+
high: "high";
|
|
415
408
|
xhigh: "xhigh";
|
|
416
409
|
}>>;
|
|
417
410
|
type: z.ZodEnum<{
|
|
@@ -497,10 +490,10 @@ declare const configSchema: z.ZodObject<{
|
|
|
497
490
|
models: z.ZodArray<z.ZodString>;
|
|
498
491
|
profile: z.ZodString;
|
|
499
492
|
reasoning_effort: z.ZodOptional<z.ZodEnum<{
|
|
500
|
-
high: "high";
|
|
501
|
-
medium: "medium";
|
|
502
|
-
low: "low";
|
|
503
493
|
none: "none";
|
|
494
|
+
low: "low";
|
|
495
|
+
medium: "medium";
|
|
496
|
+
high: "high";
|
|
504
497
|
xhigh: "xhigh";
|
|
505
498
|
}>>;
|
|
506
499
|
}, z.core.$strict>>>;
|
|
@@ -510,8 +503,8 @@ declare const configSchema: z.ZodObject<{
|
|
|
510
503
|
schedules: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
511
504
|
description: z.ZodOptional<z.ZodString>;
|
|
512
505
|
baseline: z.ZodEnum<{
|
|
513
|
-
quick: "quick";
|
|
514
506
|
execute: "execute";
|
|
507
|
+
quick: "quick";
|
|
515
508
|
}>;
|
|
516
509
|
max_parallel_nodes: z.ZodOptional<z.ZodNumber>;
|
|
517
510
|
node_catalog: z.ZodOptional<z.ZodString>;
|