@netlify/axis 1.1.6 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -15
- package/dist/adapters/base/agent-adapter.d.ts.map +1 -1
- package/dist/adapters/base/agent-adapter.js +43 -2
- package/dist/adapters/base/agent-adapter.js.map +1 -1
- package/dist/adapters/registry.d.ts +2 -0
- package/dist/adapters/registry.d.ts.map +1 -1
- package/dist/adapters/registry.js +6 -2
- package/dist/adapters/registry.js.map +1 -1
- package/dist/cli.js +109 -9
- package/dist/cli.js.map +1 -1
- package/dist/config/loader.d.ts +2 -2
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +282 -34
- package/dist/config/loader.js.map +1 -1
- package/dist/config/validator.d.ts +10 -1
- package/dist/config/validator.d.ts.map +1 -1
- package/dist/config/validator.js +150 -5
- package/dist/config/validator.js.map +1 -1
- package/dist/report-ui/index.html +201 -151
- package/dist/reports/html.d.ts.map +1 -1
- package/dist/reports/html.js +2 -1
- package/dist/reports/html.js.map +1 -1
- package/dist/reports/writer.d.ts.map +1 -1
- package/dist/reports/writer.js +3 -0
- package/dist/reports/writer.js.map +1 -1
- package/dist/runner/runner.d.ts.map +1 -1
- package/dist/runner/runner.js +156 -15
- package/dist/runner/runner.js.map +1 -1
- package/dist/scoring/index.d.ts.map +1 -1
- package/dist/scoring/index.js +4 -1
- package/dist/scoring/index.js.map +1 -1
- package/dist/scoring/sparse-index.d.ts +6 -1
- package/dist/scoring/sparse-index.d.ts.map +1 -1
- package/dist/scoring/sparse-index.js +43 -15
- package/dist/scoring/sparse-index.js.map +1 -1
- package/dist/transcript/extract.d.ts.map +1 -1
- package/dist/transcript/extract.js +33 -3
- package/dist/transcript/extract.js.map +1 -1
- package/dist/types/agent.d.ts +4 -0
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/config.d.ts +27 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/output.d.ts +12 -2
- package/dist/types/output.d.ts.map +1 -1
- package/dist/types/output.js.map +1 -1
- package/dist/types/report.d.ts +3 -1
- package/dist/types/report.d.ts.map +1 -1
- package/dist/types/scenario.d.ts +36 -1
- package/dist/types/scenario.d.ts.map +1 -1
- package/dist/types/scoring.d.ts +4 -0
- package/dist/types/scoring.d.ts.map +1 -1
- package/dist/ui/LiveStatus.js +10 -5
- package/dist/ui/LiveStatus.js.map +1 -1
- package/dist/ui/format.d.ts +4 -0
- package/dist/ui/format.d.ts.map +1 -1
- package/dist/ui/format.js +34 -18
- package/dist/ui/format.js.map +1 -1
- package/package.json +2 -1
package/dist/config/loader.js
CHANGED
|
@@ -1,9 +1,65 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { createJiti } from "jiti";
|
|
3
5
|
import { validateConfig, validateScenario } from "./validator.js";
|
|
4
6
|
import { formatError } from "../types/output.js";
|
|
7
|
+
/** Extensions probed when no explicit config path is given, in priority order. */
|
|
8
|
+
const DEFAULT_CONFIG_EXTENSIONS = [".ts", ".js", ".mjs", ".json"];
|
|
9
|
+
const DEFAULT_CONFIG_BASENAME = "axis.config";
|
|
10
|
+
const JS_EXTENSIONS = new Set([".js", ".mjs", ".cjs"]);
|
|
11
|
+
const TS_EXTENSIONS = new Set([".ts", ".mts", ".cts"]);
|
|
5
12
|
export async function loadConfig(configPath) {
|
|
6
|
-
const resolvedPath =
|
|
13
|
+
const resolvedPath = await resolveConfigPath(configPath);
|
|
14
|
+
const ext = path.extname(resolvedPath).toLowerCase();
|
|
15
|
+
let parsed;
|
|
16
|
+
if (ext === ".json" || ext === "") {
|
|
17
|
+
parsed = await loadJsonConfig(resolvedPath);
|
|
18
|
+
}
|
|
19
|
+
else if (JS_EXTENSIONS.has(ext) || TS_EXTENSIONS.has(ext)) {
|
|
20
|
+
parsed = await loadModuleConfig(resolvedPath);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
throw new Error(`Unsupported config file extension "${ext}" at ${resolvedPath}`);
|
|
24
|
+
}
|
|
25
|
+
// Support a default export that is either the config object or a (sync/async) function returning it.
|
|
26
|
+
if (typeof parsed === "function") {
|
|
27
|
+
parsed = await parsed();
|
|
28
|
+
}
|
|
29
|
+
validateConfig(parsed, resolvedPath);
|
|
30
|
+
normalizeConfigAgents(parsed);
|
|
31
|
+
// Default the scenarios source when omitted; downstream code can assume it is set.
|
|
32
|
+
if (parsed.scenarios === undefined) {
|
|
33
|
+
parsed.scenarios = "./scenarios";
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
config: parsed,
|
|
37
|
+
configDir: path.dirname(resolvedPath),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
async function resolveConfigPath(configPath) {
|
|
41
|
+
if (configPath) {
|
|
42
|
+
return path.resolve(configPath);
|
|
43
|
+
}
|
|
44
|
+
for (const ext of DEFAULT_CONFIG_EXTENSIONS) {
|
|
45
|
+
const candidate = path.resolve(`${DEFAULT_CONFIG_BASENAME}${ext}`);
|
|
46
|
+
if (await fileExists(candidate)) {
|
|
47
|
+
return candidate;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Nothing found — fall back to .json so the existing "Could not read" error fires with the expected path.
|
|
51
|
+
return path.resolve(`${DEFAULT_CONFIG_BASENAME}.json`);
|
|
52
|
+
}
|
|
53
|
+
async function fileExists(filePath) {
|
|
54
|
+
try {
|
|
55
|
+
const stat = await fs.stat(filePath);
|
|
56
|
+
return stat.isFile();
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async function loadJsonConfig(resolvedPath) {
|
|
7
63
|
let raw;
|
|
8
64
|
try {
|
|
9
65
|
raw = await fs.readFile(resolvedPath, "utf-8");
|
|
@@ -11,33 +67,79 @@ export async function loadConfig(configPath) {
|
|
|
11
67
|
catch (err) {
|
|
12
68
|
throw new Error(`Could not read config file at ${resolvedPath}: ${formatError(err)}`);
|
|
13
69
|
}
|
|
14
|
-
let parsed;
|
|
15
70
|
try {
|
|
16
|
-
|
|
71
|
+
return JSON.parse(raw);
|
|
17
72
|
}
|
|
18
73
|
catch {
|
|
19
74
|
throw new Error(`Failed to parse JSON in ${resolvedPath}`);
|
|
20
75
|
}
|
|
21
|
-
validateConfig(parsed, resolvedPath);
|
|
22
|
-
return {
|
|
23
|
-
config: parsed,
|
|
24
|
-
configDir: path.dirname(resolvedPath),
|
|
25
|
-
};
|
|
26
76
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
77
|
+
async function loadModuleConfig(resolvedPath) {
|
|
78
|
+
if (!(await fileExists(resolvedPath))) {
|
|
79
|
+
throw new Error(`Could not read config file at ${resolvedPath}: file not found`);
|
|
80
|
+
}
|
|
81
|
+
let mod;
|
|
30
82
|
try {
|
|
31
|
-
|
|
83
|
+
mod = await importModule(resolvedPath);
|
|
32
84
|
}
|
|
33
85
|
catch (err) {
|
|
34
|
-
throw new Error(`
|
|
86
|
+
throw new Error(`Failed to load config at ${resolvedPath}: ${formatError(err)}`);
|
|
87
|
+
}
|
|
88
|
+
if (!mod || typeof mod !== "object" || !("default" in mod) || mod.default === undefined) {
|
|
89
|
+
throw new Error(`Config at ${resolvedPath} must have a default export`);
|
|
35
90
|
}
|
|
36
|
-
|
|
37
|
-
|
|
91
|
+
return mod.default;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Imports a JS or TS file by extension. Native dynamic import for `.js`/`.mjs`/`.cjs`,
|
|
95
|
+
* jiti for `.ts`/`.mts`/`.cts`. Returns the module namespace; callers extract `default`.
|
|
96
|
+
*/
|
|
97
|
+
async function importModule(filePath) {
|
|
98
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
99
|
+
if (JS_EXTENSIONS.has(ext)) {
|
|
100
|
+
return (await import(pathToFileURL(filePath).href));
|
|
38
101
|
}
|
|
102
|
+
if (TS_EXTENSIONS.has(ext)) {
|
|
103
|
+
const jiti = createJiti(import.meta.url, { interopDefault: false });
|
|
104
|
+
return (await jiti.import(filePath));
|
|
105
|
+
}
|
|
106
|
+
throw new Error(`Unsupported module extension "${ext}" at ${filePath}`);
|
|
107
|
+
}
|
|
108
|
+
/** Lowercase all agent names in a validated config (mutates in place). */
|
|
109
|
+
function normalizeConfigAgents(config) {
|
|
110
|
+
for (let i = 0; i < config.agents.length; i++) {
|
|
111
|
+
const entry = config.agents[i];
|
|
112
|
+
if (typeof entry === "string") {
|
|
113
|
+
config.agents[i] = entry.toLowerCase();
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
entry.adapter = entry.adapter.toLowerCase();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
export async function discoverScenarios(configDir, scenariosInput, filter) {
|
|
121
|
+
// When omitted, fall back to the default scenarios directory.
|
|
122
|
+
const resolvedInput = scenariosInput ?? "./scenarios";
|
|
123
|
+
const entries = Array.isArray(resolvedInput) ? resolvedInput : [resolvedInput];
|
|
39
124
|
const scenarios = [];
|
|
40
|
-
|
|
125
|
+
for (const entry of entries) {
|
|
126
|
+
if (typeof entry === "string") {
|
|
127
|
+
await collectFromPath(path.resolve(configDir, entry), scenarios);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// Inline scenarios are validated by validateConfig; here we just normalize and expand.
|
|
131
|
+
scenarios.push(...expandInline(entry));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Check for duplicate keys (can happen when variant keys collide with other scenario keys
|
|
135
|
+
// or when inline keys collide with on-disk keys)
|
|
136
|
+
const seen = new Set();
|
|
137
|
+
for (const s of scenarios) {
|
|
138
|
+
if (seen.has(s.key)) {
|
|
139
|
+
throw new Error(`Duplicate scenario key "${s.key}". Ensure variant names do not collide with other scenarios.`);
|
|
140
|
+
}
|
|
141
|
+
seen.add(s.key);
|
|
142
|
+
}
|
|
41
143
|
// Sort by key for deterministic ordering
|
|
42
144
|
scenarios.sort((a, b) => a.key.localeCompare(b.key));
|
|
43
145
|
// Filter scenarios if agent specifies a subset
|
|
@@ -46,20 +148,80 @@ export async function discoverScenarios(configDir, scenariosPath, filter) {
|
|
|
46
148
|
}
|
|
47
149
|
return scenarios;
|
|
48
150
|
}
|
|
151
|
+
/** File extensions recognized by the scenarios walker. */
|
|
152
|
+
const SCENARIO_EXTENSIONS = new Set([".json", ".js", ".mjs", ".cjs", ".ts", ".mts", ".cts"]);
|
|
153
|
+
const SCENARIO_EXT_RE = /\.(json|js|mjs|cjs|ts|mts|cts)$/;
|
|
154
|
+
async function collectFromPath(absolutePath, scenarios) {
|
|
155
|
+
let stat;
|
|
156
|
+
try {
|
|
157
|
+
stat = await fs.stat(absolutePath);
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
throw new Error(`Could not read scenarios path at ${absolutePath}: ${formatError(err)}`);
|
|
161
|
+
}
|
|
162
|
+
if (stat.isDirectory()) {
|
|
163
|
+
await walkDir(absolutePath, absolutePath, scenarios);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (!stat.isFile()) {
|
|
167
|
+
throw new Error(`Scenarios path must be a directory or scenario file: ${absolutePath}`);
|
|
168
|
+
}
|
|
169
|
+
const ext = path.extname(absolutePath).toLowerCase();
|
|
170
|
+
if (!SCENARIO_EXTENSIONS.has(ext)) {
|
|
171
|
+
throw new Error(`Scenario file must end in one of ${[...SCENARIO_EXTENSIONS].join(", ")}: ${absolutePath}`);
|
|
172
|
+
}
|
|
173
|
+
// Single-file entry is explicit, so missing default exports are an error (not silent skip).
|
|
174
|
+
const baseKey = path.basename(absolutePath, ext);
|
|
175
|
+
const loaded = await loadScenarioFromPath(absolutePath, baseKey, false);
|
|
176
|
+
if (loaded)
|
|
177
|
+
scenarios.push(...loaded);
|
|
178
|
+
}
|
|
49
179
|
async function walkDir(dir, rootDir, scenarios) {
|
|
50
180
|
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
51
181
|
for (const entry of entries) {
|
|
52
182
|
const fullPath = path.join(dir, entry.name);
|
|
53
183
|
if (entry.isDirectory()) {
|
|
54
184
|
await walkDir(fullPath, rootDir, scenarios);
|
|
185
|
+
continue;
|
|
55
186
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
187
|
+
if (!entry.isFile())
|
|
188
|
+
continue;
|
|
189
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
190
|
+
if (!SCENARIO_EXTENSIONS.has(ext))
|
|
191
|
+
continue;
|
|
192
|
+
// Derive key from path relative to the walk root: scenarios/cms/create-post.ts → "cms/create-post"
|
|
193
|
+
const baseKey = path
|
|
194
|
+
.relative(rootDir, fullPath)
|
|
195
|
+
.replace(SCENARIO_EXT_RE, "")
|
|
196
|
+
.split(path.sep)
|
|
197
|
+
.join("/");
|
|
198
|
+
// Walking a directory: silently skip module files that don't default-export a scenario object,
|
|
199
|
+
// so user-authored helpers/utilities can live alongside scenarios without special handling.
|
|
200
|
+
const loaded = await loadScenarioFromPath(fullPath, baseKey, true);
|
|
201
|
+
if (loaded)
|
|
202
|
+
scenarios.push(...loaded);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Loads a single scenario from disk, dispatching by extension.
|
|
207
|
+
*
|
|
208
|
+
* @param silentSkip When true, JS/TS modules without a default object export return null
|
|
209
|
+
* instead of throwing. Used when walking a directory so non-scenario
|
|
210
|
+
* helper modules can coexist with scenario files.
|
|
211
|
+
*/
|
|
212
|
+
async function loadScenarioFromPath(filePath, baseKey, silentSkip) {
|
|
213
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
214
|
+
if (ext === ".json") {
|
|
215
|
+
return loadJsonScenario(filePath, baseKey);
|
|
216
|
+
}
|
|
217
|
+
if (JS_EXTENSIONS.has(ext) || TS_EXTENSIONS.has(ext)) {
|
|
218
|
+
return loadModuleScenario(filePath, baseKey, silentSkip);
|
|
60
219
|
}
|
|
220
|
+
if (silentSkip)
|
|
221
|
+
return null;
|
|
222
|
+
throw new Error(`Unsupported scenario file extension "${ext}" at ${filePath}`);
|
|
61
223
|
}
|
|
62
|
-
async function
|
|
224
|
+
async function loadJsonScenario(filePath, baseKey) {
|
|
63
225
|
const raw = await fs.readFile(filePath, "utf-8");
|
|
64
226
|
let parsed;
|
|
65
227
|
try {
|
|
@@ -68,30 +230,116 @@ async function loadScenarioFile(filePath, rootDir) {
|
|
|
68
230
|
catch {
|
|
69
231
|
throw new Error(`Failed to parse JSON in scenario file ${filePath}`);
|
|
70
232
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
233
|
+
return finalizeScenarioObject(parsed, filePath, baseKey);
|
|
234
|
+
}
|
|
235
|
+
async function loadModuleScenario(filePath, baseKey, silentSkip) {
|
|
236
|
+
let mod;
|
|
237
|
+
try {
|
|
238
|
+
mod = await importModule(filePath);
|
|
239
|
+
}
|
|
240
|
+
catch (err) {
|
|
241
|
+
throw new Error(`Failed to load scenario module at ${filePath}: ${formatError(err)}`);
|
|
242
|
+
}
|
|
243
|
+
let def = mod && typeof mod === "object" ? mod.default : undefined;
|
|
244
|
+
if (typeof def === "function") {
|
|
245
|
+
def = await def();
|
|
246
|
+
}
|
|
247
|
+
if (def === undefined || def === null || typeof def !== "object" || Array.isArray(def)) {
|
|
248
|
+
if (silentSkip)
|
|
249
|
+
return null;
|
|
250
|
+
throw new Error(`Scenario module at ${filePath} must default-export an object (or function returning one)`);
|
|
251
|
+
}
|
|
252
|
+
return finalizeScenarioObject(def, filePath, baseKey);
|
|
253
|
+
}
|
|
254
|
+
function finalizeScenarioObject(parsed, filePath, baseKey) {
|
|
255
|
+
validateScenario(parsed, filePath, "file");
|
|
256
|
+
normalizeScenarioAgents(parsed);
|
|
257
|
+
const scenario = parsed;
|
|
258
|
+
// If the file declares a key, it must match the key derived from its path.
|
|
259
|
+
// The path-derived key is always authoritative; this surfaces drift after a rename.
|
|
260
|
+
if (scenario.key !== undefined && scenario.key !== baseKey) {
|
|
261
|
+
throw new Error(`Scenario at ${filePath}: declared key "${scenario.key}" does not match path-derived key "${baseKey}". ` +
|
|
262
|
+
`Either remove the "key" field or set it to "${baseKey}".`);
|
|
263
|
+
}
|
|
264
|
+
if (!scenario.variants || scenario.variants.length === 0) {
|
|
265
|
+
scenario.key = baseKey;
|
|
266
|
+
return [scenario];
|
|
267
|
+
}
|
|
268
|
+
// Expand variants: each becomes a standalone Scenario, base does not run
|
|
269
|
+
return scenario.variants.map((variant) => expandVariant(scenario, variant, baseKey));
|
|
270
|
+
}
|
|
271
|
+
function expandInline(input) {
|
|
272
|
+
// Shallow clone so we don't mutate the caller's config object.
|
|
273
|
+
const scenario = { ...input };
|
|
274
|
+
normalizeScenarioAgents(scenario);
|
|
275
|
+
if (!scenario.variants || scenario.variants.length === 0) {
|
|
276
|
+
return [scenario];
|
|
277
|
+
}
|
|
278
|
+
return scenario.variants.map((variant) => expandVariant(scenario, variant, scenario.key));
|
|
279
|
+
}
|
|
280
|
+
/** Lowercase agent-name entries in a scenario and any variants (mutates in place). */
|
|
281
|
+
function normalizeScenarioAgents(scenario) {
|
|
282
|
+
if (scenario.agents) {
|
|
283
|
+
scenario.agents = scenario.agents.map((a) => a.toLowerCase());
|
|
284
|
+
}
|
|
285
|
+
if (scenario.variants) {
|
|
286
|
+
for (const v of scenario.variants) {
|
|
287
|
+
if (v.agents)
|
|
288
|
+
v.agents = v.agents.map((a) => a.toLowerCase());
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
function expandVariant(parent, variant, baseKey) {
|
|
293
|
+
const expanded = {
|
|
294
|
+
key: `${baseKey}@${variant.name}`,
|
|
295
|
+
name: `${parent.name} [${variant.name}]`,
|
|
296
|
+
prompt: variant.prompt ?? parent.prompt,
|
|
297
|
+
rubric: variant.rubric ?? parent.rubric,
|
|
298
|
+
skip: variant.skip ?? parent.skip,
|
|
299
|
+
setup: variant.setup ?? parent.setup,
|
|
300
|
+
teardown: variant.teardown ?? parent.teardown,
|
|
301
|
+
agents: variant.agents ?? parent.agents,
|
|
302
|
+
skills: variant.skills !== undefined ? variant.skills : parent.skills,
|
|
303
|
+
mcp_servers: variant.mcp_servers !== undefined
|
|
304
|
+
? { ...parent.mcp_servers, ...variant.mcp_servers }
|
|
305
|
+
: parent.mcp_servers,
|
|
306
|
+
limits: variant.limits ?? parent.limits,
|
|
307
|
+
};
|
|
308
|
+
// Strip undefined optional fields to keep objects clean
|
|
309
|
+
if (expanded.skip === undefined)
|
|
310
|
+
delete expanded.skip;
|
|
311
|
+
if (expanded.setup === undefined)
|
|
312
|
+
delete expanded.setup;
|
|
313
|
+
if (expanded.teardown === undefined)
|
|
314
|
+
delete expanded.teardown;
|
|
315
|
+
if (expanded.agents === undefined)
|
|
316
|
+
delete expanded.agents;
|
|
317
|
+
if (expanded.skills === undefined)
|
|
318
|
+
delete expanded.skills;
|
|
319
|
+
if (expanded.mcp_servers === undefined)
|
|
320
|
+
delete expanded.mcp_servers;
|
|
321
|
+
if (expanded.limits === undefined)
|
|
322
|
+
delete expanded.limits;
|
|
323
|
+
return expanded;
|
|
80
324
|
}
|
|
81
325
|
function matchesFilter(key, filter) {
|
|
326
|
+
const baseKey = key.includes("@") ? key.split("@")[0] : key;
|
|
82
327
|
return filter.some((pattern) => {
|
|
83
|
-
// Exact match
|
|
328
|
+
// Exact match (full key including variant)
|
|
84
329
|
if (pattern === key)
|
|
85
330
|
return true;
|
|
86
|
-
//
|
|
331
|
+
// Base key match: "cms/create-post" matches all its variants
|
|
332
|
+
if (pattern === baseKey && pattern !== key)
|
|
333
|
+
return true;
|
|
334
|
+
// Simple glob: "cms/*" matches "cms/create-post" and "cms/create-post@variant"
|
|
87
335
|
if (pattern.endsWith("/*")) {
|
|
88
336
|
const prefix = pattern.slice(0, -2);
|
|
89
|
-
return
|
|
337
|
+
return baseKey.startsWith(prefix + "/");
|
|
90
338
|
}
|
|
91
339
|
// Prefix glob: "cms/**" matches any depth under cms/
|
|
92
340
|
if (pattern.endsWith("/**")) {
|
|
93
341
|
const prefix = pattern.slice(0, -3);
|
|
94
|
-
return
|
|
342
|
+
return baseKey.startsWith(prefix + "/");
|
|
95
343
|
}
|
|
96
344
|
return false;
|
|
97
345
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import type { AxisConfig } from "../types/config.js";
|
|
2
2
|
import type { RubricCriterion, Scenario } from "../types/scenario.js";
|
|
3
3
|
export declare function validateConfig(data: unknown, filePath: string): asserts data is AxisConfig;
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Validates a scenario object.
|
|
6
|
+
*
|
|
7
|
+
* @param mode "file" — loaded from a JSON scenario file; the loader will assign
|
|
8
|
+
* the `key` from the file path and rejects any user-supplied `key`.
|
|
9
|
+
* "inline" — declared inline in `axis.config.*`; the user must
|
|
10
|
+
* provide a non-empty `key` string.
|
|
11
|
+
*/
|
|
12
|
+
export declare function validateScenario(data: unknown, filePath: string, mode?: "file" | "inline"): asserts data is Scenario;
|
|
13
|
+
export declare function validateMcpServers(data: unknown, filePath: string): void;
|
|
5
14
|
/**
|
|
6
15
|
* Resolve optional weights on rubric entries. Entries with explicit weights
|
|
7
16
|
* keep them; entries without a weight split the remaining budget equally.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/config/validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEtE,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,IAAI,UAAU,
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/config/validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEtE,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,IAAI,UAAU,CAkE1F;AAwBD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,MAAM,GAAG,QAAiB,GAC/B,OAAO,CAAC,IAAI,IAAI,QAAQ,CAmF1B;AA4FD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAqDxE;AAQD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,eAAe,EAAE,CAajF"}
|
package/dist/config/validator.js
CHANGED
|
@@ -3,9 +3,7 @@ export function validateConfig(data, filePath) {
|
|
|
3
3
|
throw new Error(`Invalid config at ${filePath}: must be a JSON object`);
|
|
4
4
|
}
|
|
5
5
|
const obj = data;
|
|
6
|
-
|
|
7
|
-
throw new Error(`Invalid config at ${filePath}: "scenarios" must be a string path`);
|
|
8
|
-
}
|
|
6
|
+
validateScenariosField(obj.scenarios, filePath);
|
|
9
7
|
if (!Array.isArray(obj.agents)) {
|
|
10
8
|
throw new Error(`Invalid config at ${filePath}: "agents" must be an array`);
|
|
11
9
|
}
|
|
@@ -39,6 +37,18 @@ export function validateConfig(data, filePath) {
|
|
|
39
37
|
throw new Error(`Invalid config at ${filePath}: "settings.concurrency" must be a positive integer`);
|
|
40
38
|
}
|
|
41
39
|
}
|
|
40
|
+
if (settings?.limits !== undefined) {
|
|
41
|
+
const limits = settings.limits;
|
|
42
|
+
if (typeof limits !== "object" || limits === null || Array.isArray(limits)) {
|
|
43
|
+
throw new Error(`Invalid config at ${filePath}: "settings.limits" must be an object`);
|
|
44
|
+
}
|
|
45
|
+
if (limits.run !== undefined) {
|
|
46
|
+
validateLimits(limits.run, filePath, "settings.limits.run");
|
|
47
|
+
}
|
|
48
|
+
if (limits.scenario !== undefined) {
|
|
49
|
+
validateLimits(limits.scenario, filePath, "settings.limits.scenario");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
42
52
|
if (obj.mcp_servers !== undefined) {
|
|
43
53
|
validateMcpServers(obj.mcp_servers, filePath);
|
|
44
54
|
}
|
|
@@ -46,11 +56,50 @@ export function validateConfig(data, filePath) {
|
|
|
46
56
|
validateSkillsSources(obj.skills, filePath, "skills");
|
|
47
57
|
}
|
|
48
58
|
}
|
|
49
|
-
|
|
59
|
+
function validateScenariosField(data, filePath) {
|
|
60
|
+
if (data === undefined)
|
|
61
|
+
return; // optional — loader fills in the default
|
|
62
|
+
if (typeof data === "string")
|
|
63
|
+
return;
|
|
64
|
+
if (!Array.isArray(data)) {
|
|
65
|
+
throw new Error(`Invalid config at ${filePath}: "scenarios" must be a string path or an array of paths and/or scenario objects`);
|
|
66
|
+
}
|
|
67
|
+
for (let i = 0; i < data.length; i++) {
|
|
68
|
+
const entry = data[i];
|
|
69
|
+
if (typeof entry === "string")
|
|
70
|
+
continue;
|
|
71
|
+
if (typeof entry !== "object" || entry === null) {
|
|
72
|
+
throw new Error(`Invalid config at ${filePath}: scenarios[${i}] must be a string path or a scenario object`);
|
|
73
|
+
}
|
|
74
|
+
validateScenario(entry, `${filePath} (scenarios[${i}])`, "inline");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Validates a scenario object.
|
|
79
|
+
*
|
|
80
|
+
* @param mode "file" — loaded from a JSON scenario file; the loader will assign
|
|
81
|
+
* the `key` from the file path and rejects any user-supplied `key`.
|
|
82
|
+
* "inline" — declared inline in `axis.config.*`; the user must
|
|
83
|
+
* provide a non-empty `key` string.
|
|
84
|
+
*/
|
|
85
|
+
export function validateScenario(data, filePath, mode = "file") {
|
|
50
86
|
if (typeof data !== "object" || data === null) {
|
|
51
87
|
throw new Error(`Invalid scenario at ${filePath}: must be a JSON object`);
|
|
52
88
|
}
|
|
53
89
|
const obj = data;
|
|
90
|
+
if (mode === "inline") {
|
|
91
|
+
if (typeof obj.key !== "string" || obj.key.length === 0) {
|
|
92
|
+
throw new Error(`Invalid scenario at ${filePath}: inline scenarios must include a non-empty "key" string`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
else if (obj.key !== undefined) {
|
|
96
|
+
// File-mode: an explicit `key` is allowed (helpers like `withSharedVariants` may
|
|
97
|
+
// require it on their input), but it must be a non-empty string. The loader
|
|
98
|
+
// verifies it matches the path-derived key — see finalizeScenarioObject in loader.ts.
|
|
99
|
+
if (typeof obj.key !== "string" || obj.key.length === 0) {
|
|
100
|
+
throw new Error(`Invalid scenario at ${filePath}: "key" must be a non-empty string`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
54
103
|
if (typeof obj.name !== "string") {
|
|
55
104
|
throw new Error(`Invalid scenario at ${filePath}: missing required field "name"`);
|
|
56
105
|
}
|
|
@@ -92,14 +141,94 @@ export function validateScenario(data, filePath) {
|
|
|
92
141
|
if (obj.skills !== undefined) {
|
|
93
142
|
validateSkillsSources(obj.skills, filePath, "skills");
|
|
94
143
|
}
|
|
144
|
+
if (obj.mcp_servers !== undefined) {
|
|
145
|
+
validateMcpServers(obj.mcp_servers, filePath);
|
|
146
|
+
}
|
|
147
|
+
if (obj.limits !== undefined) {
|
|
148
|
+
validateLimits(obj.limits, filePath, "limits");
|
|
149
|
+
}
|
|
95
150
|
if (obj.setup !== undefined) {
|
|
96
151
|
validateLifecycleActions(obj.setup, filePath, "setup");
|
|
97
152
|
}
|
|
98
153
|
if (obj.teardown !== undefined) {
|
|
99
154
|
validateLifecycleActions(obj.teardown, filePath, "teardown");
|
|
100
155
|
}
|
|
156
|
+
if (obj.variants !== undefined) {
|
|
157
|
+
validateVariants(obj.variants, filePath);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const VARIANT_NAME_RE = /^[a-zA-Z0-9_-]+$/;
|
|
161
|
+
function validateVariants(data, filePath) {
|
|
162
|
+
if (!Array.isArray(data)) {
|
|
163
|
+
throw new Error(`Invalid scenario at ${filePath}: "variants" must be an array`);
|
|
164
|
+
}
|
|
165
|
+
const names = new Set();
|
|
166
|
+
for (let i = 0; i < data.length; i++) {
|
|
167
|
+
const variant = data[i];
|
|
168
|
+
if (typeof variant !== "object" || variant === null) {
|
|
169
|
+
throw new Error(`Invalid scenario at ${filePath}: variants[${i}] must be an object`);
|
|
170
|
+
}
|
|
171
|
+
if (typeof variant.name !== "string" || !VARIANT_NAME_RE.test(variant.name)) {
|
|
172
|
+
throw new Error(`Invalid scenario at ${filePath}: variants[${i}].name must be a string matching /^[a-zA-Z0-9_-]+$/`);
|
|
173
|
+
}
|
|
174
|
+
if (names.has(variant.name)) {
|
|
175
|
+
throw new Error(`Invalid scenario at ${filePath}: duplicate variant name "${variant.name}"`);
|
|
176
|
+
}
|
|
177
|
+
names.add(variant.name);
|
|
178
|
+
if (variant.prompt !== undefined && typeof variant.prompt !== "string") {
|
|
179
|
+
throw new Error(`Invalid scenario at ${filePath}: variants[${i}].prompt must be a string`);
|
|
180
|
+
}
|
|
181
|
+
if (variant.rubric !== undefined) {
|
|
182
|
+
if (typeof variant.rubric === "string") {
|
|
183
|
+
// String rubric — freeform evaluation description
|
|
184
|
+
}
|
|
185
|
+
else if (Array.isArray(variant.rubric)) {
|
|
186
|
+
for (let j = 0; j < variant.rubric.length; j++) {
|
|
187
|
+
const entry = variant.rubric[j];
|
|
188
|
+
if (typeof entry.check !== "string") {
|
|
189
|
+
throw new Error(`Invalid scenario at ${filePath}: variants[${i}].rubric[${j}] missing "check" string`);
|
|
190
|
+
}
|
|
191
|
+
if (entry.weight !== undefined && typeof entry.weight !== "number") {
|
|
192
|
+
throw new Error(`Invalid scenario at ${filePath}: variants[${i}].rubric[${j}].weight must be a number`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
variant.rubric = resolveRubricWeights(variant.rubric);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
throw new Error(`Invalid scenario at ${filePath}: variants[${i}].rubric must be a string or array`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (variant.skip !== undefined && typeof variant.skip !== "boolean") {
|
|
202
|
+
throw new Error(`Invalid scenario at ${filePath}: variants[${i}].skip must be a boolean`);
|
|
203
|
+
}
|
|
204
|
+
if (variant.agents !== undefined) {
|
|
205
|
+
if (!Array.isArray(variant.agents) || variant.agents.length === 0) {
|
|
206
|
+
throw new Error(`Invalid scenario at ${filePath}: variants[${i}].agents must be a non-empty array of strings`);
|
|
207
|
+
}
|
|
208
|
+
for (let j = 0; j < variant.agents.length; j++) {
|
|
209
|
+
if (typeof variant.agents[j] !== "string") {
|
|
210
|
+
throw new Error(`Invalid scenario at ${filePath}: variants[${i}].agents[${j}] must be a string`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (variant.skills !== undefined) {
|
|
215
|
+
validateSkillsSources(variant.skills, filePath, `variants[${i}].skills`);
|
|
216
|
+
}
|
|
217
|
+
if (variant.mcp_servers !== undefined) {
|
|
218
|
+
validateMcpServers(variant.mcp_servers, filePath);
|
|
219
|
+
}
|
|
220
|
+
if (variant.setup !== undefined) {
|
|
221
|
+
validateLifecycleActions(variant.setup, filePath, `variants[${i}].setup`);
|
|
222
|
+
}
|
|
223
|
+
if (variant.teardown !== undefined) {
|
|
224
|
+
validateLifecycleActions(variant.teardown, filePath, `variants[${i}].teardown`);
|
|
225
|
+
}
|
|
226
|
+
if (variant.limits !== undefined) {
|
|
227
|
+
validateLimits(variant.limits, filePath, `variants[${i}].limits`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
101
230
|
}
|
|
102
|
-
function validateMcpServers(data, filePath) {
|
|
231
|
+
export function validateMcpServers(data, filePath) {
|
|
103
232
|
if (typeof data !== "object" || data === null || Array.isArray(data)) {
|
|
104
233
|
throw new Error(`Invalid config at ${filePath}: "mcp_servers" must be an object`);
|
|
105
234
|
}
|
|
@@ -171,6 +300,22 @@ export function resolveRubricWeights(rubric) {
|
|
|
171
300
|
const share = unspecified.length > 0 ? remaining / unspecified.length : 0;
|
|
172
301
|
return rubric.map((r) => (r.weight !== undefined ? r : { ...r, weight: share }));
|
|
173
302
|
}
|
|
303
|
+
function validateLimits(data, filePath, field) {
|
|
304
|
+
if (typeof data !== "object" || data === null || Array.isArray(data)) {
|
|
305
|
+
throw new Error(`Invalid config at ${filePath}: "${field}" must be an object`);
|
|
306
|
+
}
|
|
307
|
+
const obj = data;
|
|
308
|
+
if (obj.time_minutes !== undefined) {
|
|
309
|
+
if (typeof obj.time_minutes !== "number" || obj.time_minutes <= 0) {
|
|
310
|
+
throw new Error(`Invalid config at ${filePath}: "${field}.time_minutes" must be a positive number`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (obj.tokens !== undefined) {
|
|
314
|
+
if (typeof obj.tokens !== "number" || !Number.isInteger(obj.tokens) || obj.tokens <= 0) {
|
|
315
|
+
throw new Error(`Invalid config at ${filePath}: "${field}.tokens" must be a positive integer`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
174
319
|
function validateLifecycleActions(data, filePath, field) {
|
|
175
320
|
if (!Array.isArray(data)) {
|
|
176
321
|
throw new Error(`Invalid scenario at ${filePath}: "${field}" must be an array`);
|