harness-dispatch 0.4.0 → 0.6.0
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/CHANGELOG.md +152 -0
- package/README.md +232 -87
- package/config.default.yaml +416 -397
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +2 -2
- package/dist/auth.js.map +1 -1
- package/dist/billing.d.ts +35 -4
- package/dist/billing.d.ts.map +1 -1
- package/dist/billing.js +48 -17
- package/dist/billing.js.map +1 -1
- package/dist/bin.d.ts.map +1 -1
- package/dist/bin.js +147 -91
- package/dist/bin.js.map +1 -1
- package/dist/breaker-store.d.ts +60 -9
- package/dist/breaker-store.d.ts.map +1 -1
- package/dist/breaker-store.js +246 -50
- package/dist/breaker-store.js.map +1 -1
- package/dist/circuit-breaker.d.ts +35 -0
- package/dist/circuit-breaker.d.ts.map +1 -1
- package/dist/circuit-breaker.js +68 -11
- package/dist/circuit-breaker.js.map +1 -1
- package/dist/config/coercions.d.ts +31 -0
- package/dist/config/coercions.d.ts.map +1 -0
- package/dist/config/coercions.js +169 -0
- package/dist/config/coercions.js.map +1 -0
- package/dist/config/env-interpolation.d.ts +36 -0
- package/dist/config/env-interpolation.d.ts.map +1 -0
- package/dist/config/env-interpolation.js +73 -0
- package/dist/config/env-interpolation.js.map +1 -0
- package/dist/config/protocol.d.ts +51 -0
- package/dist/config/protocol.d.ts.map +1 -0
- package/dist/config/protocol.js +300 -0
- package/dist/config/protocol.js.map +1 -0
- package/dist/config/route-fields.d.ts +72 -0
- package/dist/config/route-fields.d.ts.map +1 -0
- package/dist/config/route-fields.js +128 -0
- package/dist/config/route-fields.js.map +1 -0
- package/dist/config/validation.d.ts +72 -0
- package/dist/config/validation.d.ts.map +1 -0
- package/dist/config/validation.js +189 -0
- package/dist/config/validation.js.map +1 -0
- package/dist/config.d.ts +19 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +214 -635
- package/dist/config.js.map +1 -1
- package/dist/configure-yaml.d.ts +23 -0
- package/dist/configure-yaml.d.ts.map +1 -0
- package/dist/configure-yaml.js +193 -0
- package/dist/configure-yaml.js.map +1 -0
- package/dist/dispatch-log.d.ts.map +1 -1
- package/dist/dispatch-log.js +2 -3
- package/dist/dispatch-log.js.map +1 -1
- package/dist/dispatchers/base.d.ts +3 -0
- package/dist/dispatchers/base.d.ts.map +1 -1
- package/dist/dispatchers/base.js.map +1 -1
- package/dist/dispatchers/generic-cli.d.ts +15 -1
- package/dist/dispatchers/generic-cli.d.ts.map +1 -1
- package/dist/dispatchers/generic-cli.js +69 -5
- package/dist/dispatchers/generic-cli.js.map +1 -1
- package/dist/dispatchers/openai-compatible.d.ts.map +1 -1
- package/dist/dispatchers/openai-compatible.js +70 -2
- package/dist/dispatchers/openai-compatible.js.map +1 -1
- package/dist/dispatchers/shared/kill-tree.d.ts +17 -4
- package/dist/dispatchers/shared/kill-tree.d.ts.map +1 -1
- package/dist/dispatchers/shared/kill-tree.js +28 -4
- package/dist/dispatchers/shared/kill-tree.js.map +1 -1
- package/dist/dispatchers/shared/rate-limit-headers.d.ts +17 -0
- package/dist/dispatchers/shared/rate-limit-headers.d.ts.map +1 -1
- package/dist/dispatchers/shared/rate-limit-headers.js +27 -0
- package/dist/dispatchers/shared/rate-limit-headers.js.map +1 -1
- package/dist/dispatchers/shared/stream-subprocess.d.ts +11 -0
- package/dist/dispatchers/shared/stream-subprocess.d.ts.map +1 -1
- package/dist/dispatchers/shared/stream-subprocess.js +62 -35
- package/dist/dispatchers/shared/stream-subprocess.js.map +1 -1
- package/dist/dispatchers/shared/subprocess.d.ts.map +1 -1
- package/dist/dispatchers/shared/subprocess.js +3 -0
- package/dist/dispatchers/shared/subprocess.js.map +1 -1
- package/dist/dispatchers/shared/which-available.d.ts.map +1 -1
- package/dist/dispatchers/shared/which-available.js +16 -2
- package/dist/dispatchers/shared/which-available.js.map +1 -1
- package/dist/file-lock.d.ts +17 -0
- package/dist/file-lock.d.ts.map +1 -0
- package/dist/file-lock.js +143 -0
- package/dist/file-lock.js.map +1 -0
- package/dist/http/parse.d.ts +64 -0
- package/dist/http/parse.d.ts.map +1 -0
- package/dist/http/parse.js +230 -0
- package/dist/http/parse.js.map +1 -0
- package/dist/http/server.d.ts +2 -0
- package/dist/http/server.d.ts.map +1 -1
- package/dist/http/server.js +249 -286
- package/dist/http/server.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/job-runner.js +23 -5
- package/dist/job-runner.js.map +1 -1
- package/dist/jobs/context.d.ts +17 -0
- package/dist/jobs/context.d.ts.map +1 -0
- package/dist/jobs/context.js +84 -0
- package/dist/jobs/context.js.map +1 -0
- package/dist/jobs/store.d.ts +80 -0
- package/dist/jobs/store.d.ts.map +1 -0
- package/dist/jobs/store.js +279 -0
- package/dist/jobs/store.js.map +1 -0
- package/dist/jobs/types.d.ts +135 -0
- package/dist/jobs/types.d.ts.map +1 -0
- package/dist/jobs/types.js +11 -0
- package/dist/jobs/types.js.map +1 -0
- package/dist/jobs.d.ts +165 -85
- package/dist/jobs.d.ts.map +1 -1
- package/dist/jobs.js +824 -208
- package/dist/jobs.js.map +1 -1
- package/dist/leaderboard.d.ts +24 -1
- package/dist/leaderboard.d.ts.map +1 -1
- package/dist/leaderboard.js +94 -2
- package/dist/leaderboard.js.map +1 -1
- package/dist/mcp/config-hot-reload.d.ts.map +1 -1
- package/dist/mcp/config-hot-reload.js +1 -1
- package/dist/mcp/config-hot-reload.js.map +1 -1
- package/dist/mcp/dispatcher-factory.d.ts +16 -1
- package/dist/mcp/dispatcher-factory.d.ts.map +1 -1
- package/dist/mcp/dispatcher-factory.js +41 -3
- package/dist/mcp/dispatcher-factory.js.map +1 -1
- package/dist/mcp/tool-schemas.d.ts +194 -0
- package/dist/mcp/tool-schemas.d.ts.map +1 -0
- package/dist/mcp/tool-schemas.js +311 -0
- package/dist/mcp/tool-schemas.js.map +1 -0
- package/dist/mcp/tools.d.ts +26 -51
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +122 -151
- package/dist/mcp/tools.js.map +1 -1
- package/dist/observability/index.d.ts +16 -3
- package/dist/observability/index.d.ts.map +1 -1
- package/dist/observability/index.js +23 -7
- package/dist/observability/index.js.map +1 -1
- package/dist/observability/spans.d.ts.map +1 -1
- package/dist/observability/spans.js +4 -1
- package/dist/observability/spans.js.map +1 -1
- package/dist/quota.d.ts +106 -22
- package/dist/quota.d.ts.map +1 -1
- package/dist/quota.js +198 -58
- package/dist/quota.js.map +1 -1
- package/dist/route-policy.d.ts.map +1 -1
- package/dist/route-policy.js +13 -3
- package/dist/route-policy.js.map +1 -1
- package/dist/router.d.ts +29 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +102 -40
- package/dist/router.js.map +1 -1
- package/dist/safety.d.ts.map +1 -1
- package/dist/safety.js +11 -2
- package/dist/safety.js.map +1 -1
- package/dist/state-dir.d.ts +17 -0
- package/dist/state-dir.d.ts.map +1 -0
- package/dist/state-dir.js +21 -0
- package/dist/state-dir.js.map +1 -0
- package/dist/status.d.ts +49 -0
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +67 -4
- package/dist/status.js.map +1 -1
- package/dist/types.d.ts +74 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/working-dir.d.ts +13 -0
- package/dist/working-dir.d.ts.map +1 -1
- package/dist/working-dir.js +27 -0
- package/dist/working-dir.js.map +1 -1
- package/dist/workspace-lock.d.ts +40 -0
- package/dist/workspace-lock.d.ts.map +1 -0
- package/dist/workspace-lock.js +258 -0
- package/dist/workspace-lock.js.map +1 -0
- package/dist/workspace-resolve.d.ts +98 -0
- package/dist/workspace-resolve.d.ts.map +1 -0
- package/dist/workspace-resolve.js +382 -0
- package/dist/workspace-resolve.js.map +1 -0
- package/dist/workspaces.d.ts +16 -0
- package/dist/workspaces.d.ts.map +1 -1
- package/dist/workspaces.js +166 -22
- package/dist/workspaces.js.map +1 -1
- package/package.json +99 -96
- package/dist/dashboard/live.d.ts +0 -49
- package/dist/dashboard/live.d.ts.map +0 -1
- package/dist/dashboard/live.js +0 -149
- package/dist/dashboard/live.js.map +0 -1
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parsing for a route's `protocol:` block — how a CLI harness is actually
|
|
3
|
+
* invoked and how its output is read.
|
|
4
|
+
*
|
|
5
|
+
* Split out of config.ts. This is the largest single concern in that file and
|
|
6
|
+
* the most self-contained: it turns YAML into a CliProtocolConfig and knows
|
|
7
|
+
* nothing about routes, billing, or defaults merging.
|
|
8
|
+
*
|
|
9
|
+
* It is also the reason `harness: generic` works at all — every CLI harness,
|
|
10
|
+
* built-in or user-added, is driven by this structure rather than by
|
|
11
|
+
* TypeScript. That is why a malformed protocol block skips the whole entry
|
|
12
|
+
* with a warning instead of landing a route that would fail at dispatch time.
|
|
13
|
+
*/
|
|
14
|
+
import { str } from "./coercions.js";
|
|
15
|
+
const SAFETY_PROFILES = ["read_only", "workspace_edit", "full_auto"];
|
|
16
|
+
/** The full set of `{{name}}` tokens expandToken() in generic-cli.ts understands. */
|
|
17
|
+
export const KNOWN_ARG_PLACEHOLDERS = new Set([
|
|
18
|
+
"{{prompt}}",
|
|
19
|
+
"{{model}}",
|
|
20
|
+
"{{safety}}",
|
|
21
|
+
"{{working_dir}}",
|
|
22
|
+
"{{file_dirs}}",
|
|
23
|
+
"{{native_args}}",
|
|
24
|
+
]);
|
|
25
|
+
export function stringArrayFrom(raw) {
|
|
26
|
+
if (!Array.isArray(raw))
|
|
27
|
+
return undefined;
|
|
28
|
+
const out = raw.filter((v) => typeof v === "string");
|
|
29
|
+
return out.length > 0 ? out : undefined;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Parse `protocol:` for any route (see CliProtocolConfig in types.ts).
|
|
33
|
+
* Three shapes:
|
|
34
|
+
* - A string ("cursor", "codex", ...) — looked up in presets by
|
|
35
|
+
* name. Lets a config select a known protocol without retyping it, and
|
|
36
|
+
* is the extension point for "others add further protocols" — a new
|
|
37
|
+
* named entry in the shipped config.default.yaml is immediately selectable here,
|
|
38
|
+
* no code changes.
|
|
39
|
+
* - An object with `extends: <preset name>` — starts from that preset and
|
|
40
|
+
* overrides only the fields present, for the common "95% the same, one
|
|
41
|
+
* flag different" case. safety merges per-profile (overriding just
|
|
42
|
+
* full_auto doesn't erase read_only/workspace_edit from the preset).
|
|
43
|
+
* - A plain object — the full protocol, no preset involved (unchanged
|
|
44
|
+
* behavior from before presets existed).
|
|
45
|
+
*
|
|
46
|
+
* Returns undefined — with a warning — for anything malformed, so a broken
|
|
47
|
+
* block degrades to "route unusable" (isAvailable() checks for a missing
|
|
48
|
+
* protocol) rather than a half-built dispatcher silently doing the wrong
|
|
49
|
+
* thing.
|
|
50
|
+
*/
|
|
51
|
+
export function protocolFrom(raw, routeLabel, warnings,
|
|
52
|
+
// REQUIRED, deliberately not defaulted to {}. A default made a forgotten
|
|
53
|
+
// argument resolve every preset name to nothing — silently, at runtime,
|
|
54
|
+
// which is the exact failure mode this file's own history is made of. As a
|
|
55
|
+
// required parameter the compiler catches it instead.
|
|
56
|
+
presets) {
|
|
57
|
+
if (typeof raw === "string") {
|
|
58
|
+
const preset = presets[raw];
|
|
59
|
+
if (!preset) {
|
|
60
|
+
warnings.push(`${routeLabel}: protocol "${raw}" is not a known preset (expected one of: ` +
|
|
61
|
+
`${Object.keys(presets).join(", ")}) — entry ignored.`);
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
return preset;
|
|
65
|
+
}
|
|
66
|
+
if (raw === null || typeof raw !== "object")
|
|
67
|
+
return undefined;
|
|
68
|
+
const r = raw;
|
|
69
|
+
let base;
|
|
70
|
+
if (typeof r.extends === "string") {
|
|
71
|
+
base = presets[r.extends];
|
|
72
|
+
if (!base) {
|
|
73
|
+
warnings.push(`${routeLabel}: protocol.extends "${r.extends}" is not a known preset (expected one of: ` +
|
|
74
|
+
`${Object.keys(presets).join(", ")}) — entry ignored.`);
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return parseProtocolFields(r, routeLabel, warnings, base);
|
|
79
|
+
}
|
|
80
|
+
export function parseProtocolFields(r, routeLabel, warnings, base) {
|
|
81
|
+
const args = stringArrayFrom(r.args) ?? base?.args;
|
|
82
|
+
if (args === undefined) {
|
|
83
|
+
warnings.push(`${routeLabel}: protocol.args is required — entry ignored.`);
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
const outputRaw = r.output;
|
|
87
|
+
let output = base?.output;
|
|
88
|
+
if (outputRaw !== undefined) {
|
|
89
|
+
if (outputRaw === null || typeof outputRaw !== "object") {
|
|
90
|
+
warnings.push(`${routeLabel}: protocol.output must be an object — entry ignored.`);
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
const o = outputRaw;
|
|
94
|
+
if (o.mode !== "text" && o.mode !== "json_field" && o.mode !== "jsonl_stream") {
|
|
95
|
+
warnings.push(`${routeLabel}: protocol.output.mode must be one of text | json_field | jsonl_stream — entry ignored.`);
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
output = { mode: o.mode };
|
|
99
|
+
const fields = stringArrayFrom(o.fields);
|
|
100
|
+
if (fields !== undefined)
|
|
101
|
+
output.fields = fields;
|
|
102
|
+
const usageRaw = o.usage;
|
|
103
|
+
if (usageRaw !== null && typeof usageRaw === "object") {
|
|
104
|
+
const u = usageRaw;
|
|
105
|
+
const input = stringArrayFrom(u.input);
|
|
106
|
+
const outputTokens = stringArrayFrom(u.output);
|
|
107
|
+
if (input !== undefined && outputTokens !== undefined)
|
|
108
|
+
output.usage = { input, output: outputTokens };
|
|
109
|
+
}
|
|
110
|
+
const eventRulesRaw = o.event_rules;
|
|
111
|
+
if (Array.isArray(eventRulesRaw)) {
|
|
112
|
+
const eventRules = [];
|
|
113
|
+
for (const [i, ruleRaw] of eventRulesRaw.entries()) {
|
|
114
|
+
const rule = eventRuleFrom(ruleRaw, `${routeLabel}: protocol.output.event_rules[${i}]`, warnings);
|
|
115
|
+
if (rule)
|
|
116
|
+
eventRules.push(rule);
|
|
117
|
+
}
|
|
118
|
+
if (eventRules.length > 0)
|
|
119
|
+
output.eventRules = eventRules;
|
|
120
|
+
}
|
|
121
|
+
const errorRaw = o.error;
|
|
122
|
+
if (errorRaw !== null && typeof errorRaw === "object") {
|
|
123
|
+
const e = errorRaw;
|
|
124
|
+
const field = str(e.field);
|
|
125
|
+
if (field !== undefined) {
|
|
126
|
+
const messageFields = stringArrayFrom(e.message_fields);
|
|
127
|
+
output.error = messageFields !== undefined ? { field, messageFields } : { field };
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
warnings.push(`${routeLabel}: protocol.output.error.field is required — error detection ignored.`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (output === undefined) {
|
|
135
|
+
warnings.push(`${routeLabel}: protocol.output is required — entry ignored.`);
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
const protocol = { args, output };
|
|
139
|
+
const stdin = typeof r.stdin === "boolean" ? r.stdin : base?.stdin;
|
|
140
|
+
if (stdin !== undefined)
|
|
141
|
+
protocol.stdin = stdin;
|
|
142
|
+
const modelRaw = r.model;
|
|
143
|
+
if (modelRaw !== undefined && modelRaw !== null) {
|
|
144
|
+
const m = modelRaw;
|
|
145
|
+
if (typeof m.flag === "string" && m.flag) {
|
|
146
|
+
protocol.model = { flag: m.flag };
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
warnings.push(`${routeLabel}: protocol.model set but missing a "flag" string — ignored.`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
else if (base?.model) {
|
|
153
|
+
protocol.model = base.model;
|
|
154
|
+
}
|
|
155
|
+
const workingDirRaw = r.working_dir;
|
|
156
|
+
if (workingDirRaw !== undefined && workingDirRaw !== null) {
|
|
157
|
+
const wd = workingDirRaw;
|
|
158
|
+
if (typeof wd.flag === "string" && wd.flag) {
|
|
159
|
+
protocol.workingDir = { flag: wd.flag };
|
|
160
|
+
const extraArgsWhenSet = stringArrayFrom(wd.extra_args_when_set);
|
|
161
|
+
if (extraArgsWhenSet !== undefined)
|
|
162
|
+
protocol.workingDir.extraArgsWhenSet = extraArgsWhenSet;
|
|
163
|
+
if (wd.fallback === "home")
|
|
164
|
+
protocol.workingDir.fallback = "home";
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
warnings.push(`${routeLabel}: protocol.working_dir set but missing a "flag" string — ignored.`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else if (base?.workingDir) {
|
|
171
|
+
protocol.workingDir = base.workingDir;
|
|
172
|
+
}
|
|
173
|
+
const fileDirsRaw = r.file_dirs;
|
|
174
|
+
if (fileDirsRaw !== undefined && fileDirsRaw !== null) {
|
|
175
|
+
const fd = fileDirsRaw;
|
|
176
|
+
if (typeof fd.flag === "string" && fd.flag) {
|
|
177
|
+
protocol.fileDirs = { flag: fd.flag };
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
warnings.push(`${routeLabel}: protocol.file_dirs set but missing a "flag" string — ignored.`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
else if (base?.fileDirs) {
|
|
184
|
+
protocol.fileDirs = base.fileDirs;
|
|
185
|
+
}
|
|
186
|
+
const fileListHeader = str(r.file_list_header) ?? base?.fileListHeader;
|
|
187
|
+
if (fileListHeader !== undefined)
|
|
188
|
+
protocol.fileListHeader = fileListHeader;
|
|
189
|
+
const fileListBullet = (typeof r.file_list_bullet === "string" ? r.file_list_bullet : undefined) ?? base?.fileListBullet;
|
|
190
|
+
if (fileListBullet !== undefined)
|
|
191
|
+
protocol.fileListBullet = fileListBullet;
|
|
192
|
+
const apiKeyEnvVar = str(r.api_key_env_var) ?? base?.apiKeyEnvVar;
|
|
193
|
+
if (apiKeyEnvVar !== undefined)
|
|
194
|
+
protocol.apiKeyEnvVar = apiKeyEnvVar;
|
|
195
|
+
const safetyRaw = r.safety;
|
|
196
|
+
const safety = { ...base?.safety };
|
|
197
|
+
if (safetyRaw !== null && typeof safetyRaw === "object") {
|
|
198
|
+
for (const profile of SAFETY_PROFILES) {
|
|
199
|
+
const profileArgs = stringArrayFrom(safetyRaw[profile]);
|
|
200
|
+
if (profileArgs !== undefined)
|
|
201
|
+
safety[profile] = profileArgs;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (Object.keys(safety).length > 0)
|
|
205
|
+
protocol.safety = safety;
|
|
206
|
+
if (typeof r.success_requires_output === "boolean") {
|
|
207
|
+
protocol.successRequiresOutput = r.success_requires_output;
|
|
208
|
+
}
|
|
209
|
+
else if (base?.successRequiresOutput !== undefined) {
|
|
210
|
+
protocol.successRequiresOutput = base.successRequiresOutput;
|
|
211
|
+
}
|
|
212
|
+
const endpointNativeArgsRaw = r.endpoint_native_args;
|
|
213
|
+
if (endpointNativeArgsRaw !== null && typeof endpointNativeArgsRaw === "object") {
|
|
214
|
+
const ena = {};
|
|
215
|
+
for (const [k, v] of Object.entries(endpointNativeArgsRaw)) {
|
|
216
|
+
const args2 = stringArrayFrom(v);
|
|
217
|
+
if (args2 !== undefined)
|
|
218
|
+
ena[k] = args2;
|
|
219
|
+
}
|
|
220
|
+
if (Object.keys(ena).length > 0)
|
|
221
|
+
protocol.endpointNativeArgs = ena;
|
|
222
|
+
}
|
|
223
|
+
else if (base?.endpointNativeArgs) {
|
|
224
|
+
protocol.endpointNativeArgs = base.endpointNativeArgs;
|
|
225
|
+
}
|
|
226
|
+
// Placeholder sanity checks on the FINAL merged args (so `extends:` results
|
|
227
|
+
// are covered too). A typo'd placeholder is the most likely user error in a
|
|
228
|
+
// hand-written protocol, and without these warnings it doesn't just fail
|
|
229
|
+
// silently — it "succeeds": the CLI receives the literal "{{promt}}" token,
|
|
230
|
+
// never receives the prompt, exits 0, and the run reports ok.
|
|
231
|
+
// Matches embedded forms too ("--flag={{prompt}}"), not just whole-token
|
|
232
|
+
// typos — expansion only ever substitutes a token that IS a placeholder,
|
|
233
|
+
// so anything merely containing one goes through literally.
|
|
234
|
+
for (const token of protocol.args) {
|
|
235
|
+
if (token.includes("{{") && !KNOWN_ARG_PLACEHOLDERS.has(token)) {
|
|
236
|
+
warnings.push(`${routeLabel}: protocol.args contains unrecognized placeholder "${token}" — it will be ` +
|
|
237
|
+
`passed to the CLI as a literal argument, not substituted. Placeholders only work as ` +
|
|
238
|
+
`a whole standalone argument. Known placeholders: ` +
|
|
239
|
+
`${[...KNOWN_ARG_PLACEHOLDERS].join(", ")}.`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (!protocol.stdin && !protocol.args.includes("{{prompt}}")) {
|
|
243
|
+
warnings.push(`${routeLabel}: protocol.args has no {{prompt}} placeholder and stdin is not true — ` +
|
|
244
|
+
`the prompt is never sent to the CLI. Add "{{prompt}}" to args, or set stdin: true.`);
|
|
245
|
+
}
|
|
246
|
+
return protocol;
|
|
247
|
+
}
|
|
248
|
+
export function eventRuleFrom(raw, label, warnings) {
|
|
249
|
+
if (raw === null || typeof raw !== "object") {
|
|
250
|
+
warnings.push(`${label}: must be an object — ignored.`);
|
|
251
|
+
return undefined;
|
|
252
|
+
}
|
|
253
|
+
const r = raw;
|
|
254
|
+
// "when" is optional — omitted or {} means "matches every line" (e.g. a
|
|
255
|
+
// usage rule that should fire regardless of event type, matching Codex's
|
|
256
|
+
// original unconditional `if (event.usage) {...}` check).
|
|
257
|
+
const whenRaw = r.when ?? {};
|
|
258
|
+
if (whenRaw === null || typeof whenRaw !== "object" || Array.isArray(whenRaw)) {
|
|
259
|
+
warnings.push(`${label}: "when" must be a {field: value} map — ignored.`);
|
|
260
|
+
return undefined;
|
|
261
|
+
}
|
|
262
|
+
const when = {};
|
|
263
|
+
for (const [k, v] of Object.entries(whenRaw)) {
|
|
264
|
+
if (typeof v === "string")
|
|
265
|
+
when[k] = v;
|
|
266
|
+
}
|
|
267
|
+
const emit = r.emit;
|
|
268
|
+
if (emit !== "text" &&
|
|
269
|
+
emit !== "tool_use" &&
|
|
270
|
+
emit !== "thinking" &&
|
|
271
|
+
emit !== "usage" &&
|
|
272
|
+
emit !== "error") {
|
|
273
|
+
warnings.push(`${label}: "emit" must be one of text | tool_use | thinking | usage | error — ignored.`);
|
|
274
|
+
return undefined;
|
|
275
|
+
}
|
|
276
|
+
const rule = { when, emit };
|
|
277
|
+
const textField = str(r.text_field);
|
|
278
|
+
if (textField !== undefined)
|
|
279
|
+
rule.textField = textField;
|
|
280
|
+
const nameField = str(r.name_field);
|
|
281
|
+
if (nameField !== undefined)
|
|
282
|
+
rule.nameField = nameField;
|
|
283
|
+
const inputField = str(r.input_field);
|
|
284
|
+
if (inputField !== undefined)
|
|
285
|
+
rule.inputField = inputField;
|
|
286
|
+
const chunkField = str(r.chunk_field);
|
|
287
|
+
if (chunkField !== undefined)
|
|
288
|
+
rule.chunkField = chunkField;
|
|
289
|
+
const inputTokenFields = stringArrayFrom(r.input_token_fields);
|
|
290
|
+
if (inputTokenFields !== undefined)
|
|
291
|
+
rule.inputTokenFields = inputTokenFields;
|
|
292
|
+
const outputTokenFields = stringArrayFrom(r.output_token_fields);
|
|
293
|
+
if (outputTokenFields !== undefined)
|
|
294
|
+
rule.outputTokenFields = outputTokenFields;
|
|
295
|
+
const messageField = str(r.message_field);
|
|
296
|
+
if (messageField !== undefined)
|
|
297
|
+
rule.messageField = messageField;
|
|
298
|
+
return rule;
|
|
299
|
+
}
|
|
300
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/config/protocol.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One field contract shared by every route shape.
|
|
3
|
+
*
|
|
4
|
+
* THE DEFECT THIS EXISTS TO RETIRE. `clis:`, `endpoints:` and the legacy
|
|
5
|
+
* `services:` block each resolved route fields with their own hand-written
|
|
6
|
+
* list, so a key added to one was easy to forget in the others. That produced
|
|
7
|
+
* five separate silent-drop defects in one file, each the same shape: a
|
|
8
|
+
* correctly-spelled, correctly-valued setting written in config and read by
|
|
9
|
+
* nobody —
|
|
10
|
+
*
|
|
11
|
+
* workspace_policy honoured for services:/endpoints:, dropped for clis:
|
|
12
|
+
* api_keys honoured for clis:, dropped for endpoints:
|
|
13
|
+
* effective_safety honoured for clis:/services:, dropped for endpoints:
|
|
14
|
+
* escalate_model honoured for clis:, dropped for endpoints:
|
|
15
|
+
*
|
|
16
|
+
* These are safety and isolation controls, so "silently absent" means
|
|
17
|
+
* "silently less restrictive" — a route asked to run `read_only` running with
|
|
18
|
+
* write access, a route asked to run in an isolated copy running in the user's
|
|
19
|
+
* repository. Nothing warns, because nothing is wrong with what was written.
|
|
20
|
+
*
|
|
21
|
+
* WHY A TABLE RATHER THAN A SHARED FUNCTION. The obvious unification — have
|
|
22
|
+
* the CLI builder call the endpoint builder's `billingFields()` — is wrong,
|
|
23
|
+
* and tests/route-field-parity.test.ts says why: the CLI path resolves
|
|
24
|
+
* `entry.X ?? harnessDefaults.X` against the defaults shipped in
|
|
25
|
+
* config.default.yaml, and `billingFields(raw)` has no defaults layer at all.
|
|
26
|
+
* Collapsing them would silently drop the fallback every built-in harness
|
|
27
|
+
* relies on — trading five silent-drop bugs for a bigger one. A table carries
|
|
28
|
+
* the defaults layer explicitly: each field says how to parse it AND where its
|
|
29
|
+
* fallback comes from, and a shape with no defaults (endpoints) simply passes
|
|
30
|
+
* none.
|
|
31
|
+
*
|
|
32
|
+
* WHAT IS DELIBERATELY NOT HERE. Billing identity — provider, surface,
|
|
33
|
+
* auth_source, billing_kind, paid_usage_possible — resolves differently per
|
|
34
|
+
* shape for real reasons, not by accident: endpoints infer it from the base
|
|
35
|
+
* URL (localhost is local_compute), while CLI routes factor in whether an
|
|
36
|
+
* api_key is present (a key means a metered account exists whatever the
|
|
37
|
+
* declared kind says). Those stay in their builders. Pretending they are
|
|
38
|
+
* shared would be the same mistake in the other direction.
|
|
39
|
+
*
|
|
40
|
+
* tests/route-field-parity.test.ts still pins every key here against both
|
|
41
|
+
* shapes. It is no longer the only thing standing between a new field and a
|
|
42
|
+
* silent drop, but it is what proves this table is actually wired into both.
|
|
43
|
+
*/
|
|
44
|
+
import type { SafetyProfile, ServiceConfig } from "../types.js";
|
|
45
|
+
/**
|
|
46
|
+
* The subset of a harness's shipped defaults that shared fields fall back to.
|
|
47
|
+
*
|
|
48
|
+
* Structural on purpose: `CliDefaults` satisfies it without this module
|
|
49
|
+
* needing to import config.ts (which imports this one).
|
|
50
|
+
*/
|
|
51
|
+
export type RouteFieldDefaults = Partial<Pick<ServiceConfig, "leaderboardModel" | "thinkingLevel" | "maxOutputTokens" | "maxInputTokens" | "effectiveSafety" | "models" | "modelHint">>;
|
|
52
|
+
/**
|
|
53
|
+
* `effective_safety` is either one profile for every request, or a per-request
|
|
54
|
+
* map. Shared by all three shapes; lived in config.ts before this table.
|
|
55
|
+
*/
|
|
56
|
+
export declare function effectiveSafetyFrom(raw: unknown): SafetyProfile | Partial<Record<SafetyProfile, SafetyProfile>> | undefined;
|
|
57
|
+
/** The YAML keys this table owns — used by tests to prove both shapes are wired to it. */
|
|
58
|
+
export declare const SHARED_ROUTE_FIELD_KEYS: readonly string[];
|
|
59
|
+
/**
|
|
60
|
+
* Resolve every shared field for one route entry.
|
|
61
|
+
*
|
|
62
|
+
* A field is emitted only when it resolves to something, so callers can spread
|
|
63
|
+
* the result over a partially-built ServiceConfig without overwriting anything
|
|
64
|
+
* with `undefined`.
|
|
65
|
+
*
|
|
66
|
+
* @param raw the route entry as written in config.yaml
|
|
67
|
+
* @param defaults the harness's shipped defaults, for shapes that have them
|
|
68
|
+
* (`clis:`). Endpoint entries pass nothing — they have no defaults layer, and
|
|
69
|
+
* inventing one is the mistake this table's header warns about.
|
|
70
|
+
*/
|
|
71
|
+
export declare function resolveSharedRouteFields(raw: Record<string, unknown>, defaults?: RouteFieldDefaults): Partial<ServiceConfig>;
|
|
72
|
+
//# sourceMappingURL=route-fields.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-fields.d.ts","sourceRoot":"","sources":["../../src/config/route-fields.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAShE;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG,OAAO,CACtC,IAAI,CACF,aAAa,EACX,kBAAkB,GAClB,eAAe,GACf,iBAAiB,GACjB,gBAAgB,GAChB,iBAAiB,GACjB,QAAQ,GACR,WAAW,CACd,CACF,CAAC;AA0BF;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,OAAO,GACX,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC,GAAG,SAAS,CAW3E;AAiCD,0FAA0F;AAC1F,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAA0C,CAAC;AAEhG;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,QAAQ,CAAC,EAAE,kBAAkB,GAC5B,OAAO,CAAC,aAAa,CAAC,CAQxB"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One field contract shared by every route shape.
|
|
3
|
+
*
|
|
4
|
+
* THE DEFECT THIS EXISTS TO RETIRE. `clis:`, `endpoints:` and the legacy
|
|
5
|
+
* `services:` block each resolved route fields with their own hand-written
|
|
6
|
+
* list, so a key added to one was easy to forget in the others. That produced
|
|
7
|
+
* five separate silent-drop defects in one file, each the same shape: a
|
|
8
|
+
* correctly-spelled, correctly-valued setting written in config and read by
|
|
9
|
+
* nobody —
|
|
10
|
+
*
|
|
11
|
+
* workspace_policy honoured for services:/endpoints:, dropped for clis:
|
|
12
|
+
* api_keys honoured for clis:, dropped for endpoints:
|
|
13
|
+
* effective_safety honoured for clis:/services:, dropped for endpoints:
|
|
14
|
+
* escalate_model honoured for clis:, dropped for endpoints:
|
|
15
|
+
*
|
|
16
|
+
* These are safety and isolation controls, so "silently absent" means
|
|
17
|
+
* "silently less restrictive" — a route asked to run `read_only` running with
|
|
18
|
+
* write access, a route asked to run in an isolated copy running in the user's
|
|
19
|
+
* repository. Nothing warns, because nothing is wrong with what was written.
|
|
20
|
+
*
|
|
21
|
+
* WHY A TABLE RATHER THAN A SHARED FUNCTION. The obvious unification — have
|
|
22
|
+
* the CLI builder call the endpoint builder's `billingFields()` — is wrong,
|
|
23
|
+
* and tests/route-field-parity.test.ts says why: the CLI path resolves
|
|
24
|
+
* `entry.X ?? harnessDefaults.X` against the defaults shipped in
|
|
25
|
+
* config.default.yaml, and `billingFields(raw)` has no defaults layer at all.
|
|
26
|
+
* Collapsing them would silently drop the fallback every built-in harness
|
|
27
|
+
* relies on — trading five silent-drop bugs for a bigger one. A table carries
|
|
28
|
+
* the defaults layer explicitly: each field says how to parse it AND where its
|
|
29
|
+
* fallback comes from, and a shape with no defaults (endpoints) simply passes
|
|
30
|
+
* none.
|
|
31
|
+
*
|
|
32
|
+
* WHAT IS DELIBERATELY NOT HERE. Billing identity — provider, surface,
|
|
33
|
+
* auth_source, billing_kind, paid_usage_possible — resolves differently per
|
|
34
|
+
* shape for real reasons, not by accident: endpoints infer it from the base
|
|
35
|
+
* URL (localhost is local_compute), while CLI routes factor in whether an
|
|
36
|
+
* api_key is present (a key means a metered account exists whatever the
|
|
37
|
+
* declared kind says). Those stay in their builders. Pretending they are
|
|
38
|
+
* shared would be the same mistake in the other direction.
|
|
39
|
+
*
|
|
40
|
+
* tests/route-field-parity.test.ts still pins every key here against both
|
|
41
|
+
* shapes. It is no longer the only thing standing between a new field and a
|
|
42
|
+
* silent drop, but it is what proves this table is actually wired into both.
|
|
43
|
+
*/
|
|
44
|
+
import { normalizeSafetyProfile } from "../safety.js";
|
|
45
|
+
import { confidenceFrom, str, thinkingFrom, workspacePolicyFrom, } from "./coercions.js";
|
|
46
|
+
import { stringArrayFrom } from "./protocol.js";
|
|
47
|
+
/** A number only when it really is one — `int()`/`num()` coerce, and these must not. */
|
|
48
|
+
function numberOnly(raw) {
|
|
49
|
+
return typeof raw === "number" ? raw : undefined;
|
|
50
|
+
}
|
|
51
|
+
/** Likewise for booleans: an absent flag must stay absent, not become false. */
|
|
52
|
+
function booleanOnly(raw) {
|
|
53
|
+
return typeof raw === "boolean" ? raw : undefined;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* `effective_safety` is either one profile for every request, or a per-request
|
|
57
|
+
* map. Shared by all three shapes; lived in config.ts before this table.
|
|
58
|
+
*/
|
|
59
|
+
export function effectiveSafetyFrom(raw) {
|
|
60
|
+
const single = normalizeSafetyProfile(raw);
|
|
61
|
+
if (single !== undefined)
|
|
62
|
+
return single;
|
|
63
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw))
|
|
64
|
+
return undefined;
|
|
65
|
+
const out = {};
|
|
66
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
67
|
+
const requested = normalizeSafetyProfile(key);
|
|
68
|
+
const floor = normalizeSafetyProfile(value);
|
|
69
|
+
if (requested !== undefined && floor !== undefined)
|
|
70
|
+
out[requested] = floor;
|
|
71
|
+
}
|
|
72
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Every optional field that means the same thing on every route shape.
|
|
76
|
+
*
|
|
77
|
+
* Adding a route setting means adding ONE row here (plus its key to
|
|
78
|
+
* KNOWN_ROUTE_KEYS in validation.ts, which is what catches misspellings) and
|
|
79
|
+
* it works on `clis:`, `endpoints:` and `services:` at once. That is the whole
|
|
80
|
+
* point: the old failure mode was a field that existed in one list and not the
|
|
81
|
+
* others.
|
|
82
|
+
*/
|
|
83
|
+
const SHARED_ROUTE_FIELDS = [
|
|
84
|
+
{ key: "leaderboard_model", field: "leaderboardModel", parse: str, fromDefaults: (d) => d.leaderboardModel },
|
|
85
|
+
{ key: "thinking_level", field: "thinkingLevel", parse: thinkingFrom, fromDefaults: (d) => d.thinkingLevel },
|
|
86
|
+
{ key: "escalate_model", field: "escalateModel", parse: str },
|
|
87
|
+
{ key: "max_output_tokens", field: "maxOutputTokens", parse: numberOnly, fromDefaults: (d) => d.maxOutputTokens },
|
|
88
|
+
{ key: "max_input_tokens", field: "maxInputTokens", parse: numberOnly, fromDefaults: (d) => d.maxInputTokens },
|
|
89
|
+
{ key: "timeout_ms", field: "timeoutMs", parse: numberOnly },
|
|
90
|
+
{ key: "resource_weight", field: "resourceWeight", parse: numberOnly },
|
|
91
|
+
// Safety and isolation. Four of the five original silent drops were here.
|
|
92
|
+
{ key: "safety_profile", field: "safetyProfile", parse: normalizeSafetyProfile },
|
|
93
|
+
{ key: "effective_safety", field: "effectiveSafety", parse: effectiveSafetyFrom, fromDefaults: (d) => d.effectiveSafety },
|
|
94
|
+
{ key: "workspace_policy", field: "workspacePolicy", parse: workspacePolicyFrom },
|
|
95
|
+
// Billing fields that are plain declarations. The INFERRED ones
|
|
96
|
+
// (provider/surface/auth_source/billing_kind/paid_usage_possible) stay in
|
|
97
|
+
// the builders — see the header.
|
|
98
|
+
{ key: "allow_paid_usage", field: "allowPaidUsage", parse: booleanOnly },
|
|
99
|
+
{ key: "billing_confidence", field: "billingConfidence", parse: confidenceFrom },
|
|
100
|
+
{ key: "billing_notes", field: "billingNotes", parse: str },
|
|
101
|
+
{ key: "models", field: "models", parse: stringArrayFrom, fromDefaults: (d) => d.models },
|
|
102
|
+
{ key: "model_hint", field: "modelHint", parse: str, fromDefaults: (d) => d.modelHint },
|
|
103
|
+
];
|
|
104
|
+
/** The YAML keys this table owns — used by tests to prove both shapes are wired to it. */
|
|
105
|
+
export const SHARED_ROUTE_FIELD_KEYS = SHARED_ROUTE_FIELDS.map((f) => f.key);
|
|
106
|
+
/**
|
|
107
|
+
* Resolve every shared field for one route entry.
|
|
108
|
+
*
|
|
109
|
+
* A field is emitted only when it resolves to something, so callers can spread
|
|
110
|
+
* the result over a partially-built ServiceConfig without overwriting anything
|
|
111
|
+
* with `undefined`.
|
|
112
|
+
*
|
|
113
|
+
* @param raw the route entry as written in config.yaml
|
|
114
|
+
* @param defaults the harness's shipped defaults, for shapes that have them
|
|
115
|
+
* (`clis:`). Endpoint entries pass nothing — they have no defaults layer, and
|
|
116
|
+
* inventing one is the mistake this table's header warns about.
|
|
117
|
+
*/
|
|
118
|
+
export function resolveSharedRouteFields(raw, defaults) {
|
|
119
|
+
const out = {};
|
|
120
|
+
for (const spec of SHARED_ROUTE_FIELDS) {
|
|
121
|
+
const declared = spec.parse(raw[spec.key]);
|
|
122
|
+
const value = declared ?? (defaults !== undefined ? spec.fromDefaults?.(defaults) : undefined);
|
|
123
|
+
if (value !== undefined)
|
|
124
|
+
out[spec.field] = value;
|
|
125
|
+
}
|
|
126
|
+
return out;
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=route-fields.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-fields.js","sourceRoot":"","sources":["../../src/config/route-fields.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a config file is ALLOWED to say, and what to warn about when it says
|
|
3
|
+
* something else.
|
|
4
|
+
*
|
|
5
|
+
* Split out of config.ts. This module exists because of a specific, repeated
|
|
6
|
+
* failure: the parser reads what it recognises and cannot distinguish a key it
|
|
7
|
+
* does not know from a key that is absent. Four silent-drop defects came from
|
|
8
|
+
* that — a misspelled `workspace_policy` or `safety_profile` left the route
|
|
9
|
+
* running under the LESS restrictive default, silently.
|
|
10
|
+
*
|
|
11
|
+
* So the legal surface is enumerated here rather than implied by whatever the
|
|
12
|
+
* builders happen to read. Keeping it in one file is the point: a per-shape
|
|
13
|
+
* copy is what drifted last time.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Warn on any unrecognised value for an enum that FAILS OPEN.
|
|
17
|
+
*
|
|
18
|
+
* Every *From() validator returns undefined on a mismatch and the caller
|
|
19
|
+
* falls back to a default — silently. For most fields that is merely
|
|
20
|
+
* annoying, but for these three the default is LESS restrictive than what the
|
|
21
|
+
* operator asked for:
|
|
22
|
+
*
|
|
23
|
+
* safety_profile: read_onlyy -> undefined -> DEFAULT_SAFETY_PROFILE
|
|
24
|
+
* -> workspace_edit (write access)
|
|
25
|
+
* workspace_policy: coppy -> undefined -> shared_locked
|
|
26
|
+
* -> a SHARED workspace, not an
|
|
27
|
+
* isolated copy
|
|
28
|
+
*
|
|
29
|
+
* A typo therefore quietly grants more than intended, which is the wrong
|
|
30
|
+
* direction for a safety control. The same parser already warns loudly for a
|
|
31
|
+
* malformed `protocol` block, so this is consistency as much as safety.
|
|
32
|
+
*
|
|
33
|
+
* Walks the raw tree rather than hooking each builder: the same keys appear
|
|
34
|
+
* under services:, clis:, endpoints: and overrides:, and a single walk cannot
|
|
35
|
+
* miss a format the way four separate call sites can.
|
|
36
|
+
*/
|
|
37
|
+
export declare const FAIL_OPEN_ENUMS: Record<string, readonly string[]>;
|
|
38
|
+
/**
|
|
39
|
+
* Every key this parser understands at the top level of config.yaml.
|
|
40
|
+
*
|
|
41
|
+
* `doctor` reported "no unrecognized config entries" while a typo'd or
|
|
42
|
+
* invented top-level key produced no warning at all — the check's own name
|
|
43
|
+
* promised something it never did. A misspelled `max_concurrent_runs` is
|
|
44
|
+
* indistinguishable from not setting it, which is exactly the silent-default
|
|
45
|
+
* class that has bitten this file twice already.
|
|
46
|
+
*/
|
|
47
|
+
export declare const KNOWN_TOP_LEVEL_KEYS: Set<string>;
|
|
48
|
+
/**
|
|
49
|
+
* Every key a `clis:` or `endpoints:` entry may carry.
|
|
50
|
+
*
|
|
51
|
+
* ROOT-CAUSE FIX, not a fourth instance. Three separate silent-drop defects
|
|
52
|
+
* were found in this file in one day — workspace_policy ignored for `clis:`,
|
|
53
|
+
* api_keys ignored for `endpoints:`, unknown top-level keys unwarned — and
|
|
54
|
+
* each was patched individually. Measured afterwards: a route carrying
|
|
55
|
+
* `workspace_polcy`, `safety_profil` and `tierr` still produced ZERO warnings
|
|
56
|
+
* and silently got none of the three.
|
|
57
|
+
*
|
|
58
|
+
* The mechanism is that this parser reads what it recognises and cannot
|
|
59
|
+
* distinguish a key it does not know from a key that is absent. Nothing
|
|
60
|
+
* enumerated the legal surface, so every future misspelling was guaranteed to
|
|
61
|
+
* fail the same silent way — and these are safety and isolation controls, so
|
|
62
|
+
* "silently absent" means "silently less restrictive".
|
|
63
|
+
*
|
|
64
|
+
* Kept as one list for both entry shapes on purpose. Splitting it per shape
|
|
65
|
+
* would recreate the original defect, where two parallel field lists drifted
|
|
66
|
+
* and each was missing something the other had.
|
|
67
|
+
*/
|
|
68
|
+
export declare const KNOWN_ROUTE_KEYS: Set<string>;
|
|
69
|
+
export declare function warnUnknownRouteKeys(entry: Record<string, unknown>, label: string, warnings: string[]): void;
|
|
70
|
+
export declare function warnUnknownTopLevelKeys(raw: Record<string, unknown>, warnings: string[]): void;
|
|
71
|
+
export declare function warnUnknownSafetyEnums(node: unknown, warnings: string[], where?: string): void;
|
|
72
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/config/validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAsB7D,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,aAiB/B,CAAC;AAEH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,gBAAgB,aAU3B,CAAC;AAEH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAAE,GACjB,IAAI,CAQN;AAeD,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAgB9F;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,KAAK,SAAK,GAAG,IAAI,CAqC1F"}
|