@vexyo/cli 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/api.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { RunResult } from '@vexyo/core';
2
2
 
3
3
  interface ExecuteRunOptions {
4
- config: string;
4
+ /** Explicit config path; when omitted, auto-discovered in cwd. */
5
+ config?: string;
5
6
  specVersion?: string;
6
7
  regression?: boolean;
7
8
  failOn?: string;
package/dist/api.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  executeRun
3
- } from "./chunk-254CIBOC.js";
3
+ } from "./chunk-HMGCKVG7.js";
4
4
  export {
5
5
  executeRun
6
6
  };
@@ -12,11 +12,20 @@ import {
12
12
  } from "@vexyo/reporters";
13
13
 
14
14
  // src/config.ts
15
+ import { existsSync } from "fs";
16
+ import { createRequire } from "module";
15
17
  import { resolve } from "path";
16
18
  import { pathToFileURL } from "url";
17
19
  import { createJiti } from "jiti";
18
20
  import { z } from "zod";
19
21
  var SPEC_VERSIONS = ["2025-11-25", "2026-07-28"];
22
+ var DEFAULT_SPEC_VERSION = SPEC_VERSIONS[0];
23
+ var CONFIG_CANDIDATES = [
24
+ "vexyo.config.ts",
25
+ "vexyo.config.mts",
26
+ "vexyo.config.js",
27
+ "vexyo.config.mjs"
28
+ ];
20
29
  var stdioTargetSchema = z.object({
21
30
  transport: z.literal("stdio"),
22
31
  command: z.string().min(1),
@@ -68,14 +77,44 @@ var ConfigError = class extends Error {
68
77
  this.name = "ConfigError";
69
78
  }
70
79
  };
80
+ function resolveConfigPath(explicit, cwd = process.cwd()) {
81
+ if (explicit) {
82
+ return resolve(cwd, explicit);
83
+ }
84
+ for (const name of CONFIG_CANDIDATES) {
85
+ const candidate = resolve(cwd, name);
86
+ if (existsSync(candidate)) {
87
+ return candidate;
88
+ }
89
+ }
90
+ throw new ConfigError(
91
+ `No config file found in ${cwd}. Create one with \`vexyo init\`, or pass --config <path>.`
92
+ );
93
+ }
71
94
  function formatZodError(error) {
72
95
  return error.issues.map((issue) => ` - ${issue.path.join(".") || "(root)"}: ${issue.message}`).join("\n");
73
96
  }
97
+ function selfPackageAliases() {
98
+ const aliases = {};
99
+ let req;
100
+ try {
101
+ req = createRequire(import.meta.url);
102
+ } catch {
103
+ return aliases;
104
+ }
105
+ for (const spec of ["@vexyo/cli/config", "@vexyo/cli", "@vexyo/core", "@vexyo/reporters"]) {
106
+ try {
107
+ aliases[spec] = req.resolve(spec);
108
+ } catch {
109
+ }
110
+ }
111
+ return aliases;
112
+ }
74
113
  async function loadConfig(configPath) {
75
114
  const absolute = resolve(process.cwd(), configPath);
76
115
  let mod;
77
116
  try {
78
- const jiti = createJiti(pathToFileURL(absolute).href);
117
+ const jiti = createJiti(pathToFileURL(absolute).href, { alias: selfPackageAliases() });
79
118
  mod = await jiti.import(absolute, { default: true });
80
119
  } catch (err) {
81
120
  throw new ConfigError(
@@ -141,10 +180,11 @@ var REPORTERS = {
141
180
  junit: junitReporter
142
181
  };
143
182
  async function executeRun(opts) {
144
- const config = await loadConfig(opts.config);
183
+ const configPath = resolveConfigPath(opts.config);
184
+ const config = await loadConfig(configPath);
145
185
  const specVersion = resolveSpecVersion(opts.specVersion, config);
146
186
  const failOn = resolveFailOn(opts.failOn, config);
147
- const extraChecks = await buildRegressionPhase(opts, config, specVersion);
187
+ const extraChecks = await buildRegressionPhase(opts, configPath, config, specVersion);
148
188
  return runSuite({
149
189
  specVersion,
150
190
  target: buildTarget(config),
@@ -183,20 +223,22 @@ function resolveSpecVersion(override, config) {
183
223
  function resolveFailOn(override, config) {
184
224
  return override ?? config.regression?.failOn ?? "error";
185
225
  }
186
- async function buildRegressionPhase(opts, config, specVersion) {
226
+ async function buildRegressionPhase(opts, configPath, config, specVersion) {
187
227
  if (!opts.regression) {
188
228
  return void 0;
189
229
  }
190
230
  if (!config.regression) {
191
231
  throw new ConfigError("`--regression` requires a `regression` block in the config.");
192
232
  }
193
- const goldenSet = await readGoldenSet(resolveGoldenDir(opts.config, config.regression));
233
+ const goldenSet = await readGoldenSet(resolveGoldenDir(configPath, config.regression));
194
234
  const goldenCfg = toGoldenConfig(specVersion, config.regression);
195
235
  return (ctx) => runRegression(ctx.client, goldenSet, goldenCfg);
196
236
  }
197
237
 
198
238
  export {
239
+ DEFAULT_SPEC_VERSION,
199
240
  ConfigError,
241
+ resolveConfigPath,
200
242
  loadConfig,
201
243
  reportHarnessError,
202
244
  resolveGoldenDir,
@@ -205,4 +247,4 @@ export {
205
247
  executeRun,
206
248
  runCommand
207
249
  };
208
- //# sourceMappingURL=chunk-254CIBOC.js.map
250
+ //# sourceMappingURL=chunk-HMGCKVG7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/run.ts","../src/config.ts","../src/errors.ts","../src/regression.ts","../src/target.ts"],"sourcesContent":["import {\n readGoldenSet,\n runRegression,\n runSuite,\n type RunResult,\n type RunSuiteOptions,\n type Severity,\n type SpecVersion,\n} from '@vexyo/core';\nimport {\n consoleReporter,\n jsonReporter,\n junitReporter,\n markdownReporter,\n type Reporter,\n} from '@vexyo/reporters';\nimport { ConfigError, loadConfig, resolveConfigPath, type Config } from '../config';\nimport { reportHarnessError } from '../errors';\nimport { resolveGoldenDir, toGoldenConfig } from '../regression';\nimport { buildTarget } from '../target';\n\nexport interface RunCommandOptions {\n /** Explicit config path; when omitted, auto-discovered in cwd. */\n config?: string;\n specVersion?: string;\n reporter: string;\n regression?: boolean;\n failOn?: string;\n}\n\nexport interface ExecuteRunOptions {\n /** Explicit config path; when omitted, auto-discovered in cwd. */\n config?: string;\n specVersion?: string;\n regression?: boolean;\n failOn?: string;\n}\n\nconst REPORTERS: Record<string, Reporter> = {\n console: consoleReporter,\n json: jsonReporter,\n markdown: markdownReporter,\n junit: junitReporter,\n};\n\n/**\n * Load config, connect over the configured transport, run the conformance suite\n * (plus regression when requested), and return the {@link RunResult}. Throws\n * `ConfigError`/`GoldenError`/connection errors for anything the caller should\n * map to exit code 2. Shared by the `run` command and the GitHub Action.\n */\nexport async function executeRun(opts: ExecuteRunOptions): Promise<RunResult> {\n const configPath = resolveConfigPath(opts.config);\n const config = await loadConfig(configPath);\n const specVersion = resolveSpecVersion(opts.specVersion, config);\n const failOn = resolveFailOn(opts.failOn, config);\n const extraChecks = await buildRegressionPhase(opts, configPath, config, specVersion);\n\n return runSuite({\n specVersion,\n target: buildTarget(config),\n failOn,\n extraChecks,\n });\n}\n\n/**\n * Execute the `run` command. Returns the process exit code:\n * 0 = pass, 1 = findings at/above threshold, 2 = harness/config error.\n */\nexport async function runCommand(opts: RunCommandOptions): Promise<0 | 1 | 2> {\n const reporter = REPORTERS[opts.reporter];\n if (!reporter) {\n return reportHarnessError(\n new ConfigError(\n `Unknown reporter \"${opts.reporter}\". Available reporters: ${Object.keys(REPORTERS).join(', ')}.`,\n ),\n );\n }\n\n let result: RunResult;\n try {\n result = await executeRun(opts);\n } catch (err) {\n return reportHarnessError(err);\n }\n\n process.stdout.write(`${reporter.format(result)}\\n`);\n return result.exitCode;\n}\n\nfunction resolveSpecVersion(override: string | undefined, config: Config): SpecVersion {\n const specVersion = (override as SpecVersion | undefined) ?? config.specVersion;\n if (specVersion !== '2025-11-25') {\n throw new ConfigError(\n `Spec version \"${specVersion}\" is not wired up in this build. Only 2025-11-25 is supported.`,\n );\n }\n return specVersion;\n}\n\nfunction resolveFailOn(override: string | undefined, config: Config): Severity {\n return (override as Severity | undefined) ?? config.regression?.failOn ?? 'error';\n}\n\nasync function buildRegressionPhase(\n opts: ExecuteRunOptions,\n configPath: string,\n config: Config,\n specVersion: SpecVersion,\n): Promise<RunSuiteOptions['extraChecks']> {\n if (!opts.regression) {\n return undefined;\n }\n if (!config.regression) {\n throw new ConfigError('`--regression` requires a `regression` block in the config.');\n }\n const goldenSet = await readGoldenSet(resolveGoldenDir(configPath, config.regression));\n const goldenCfg = toGoldenConfig(specVersion, config.regression);\n return (ctx) => runRegression(ctx.client, goldenSet, goldenCfg);\n}\n","import { existsSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { createJiti } from 'jiti';\nimport { z } from 'zod';\n\nexport const SPEC_VERSIONS = ['2025-11-25', '2026-07-28'] as const;\n\n/** The spec version a run targets when the config doesn't override it. */\nexport const DEFAULT_SPEC_VERSION = SPEC_VERSIONS[0];\n\n/** Config filenames auto-discovered in cwd, in resolution order. */\nexport const CONFIG_CANDIDATES = [\n 'vexyo.config.ts',\n 'vexyo.config.mts',\n 'vexyo.config.js',\n 'vexyo.config.mjs',\n] as const;\n\nconst stdioTargetSchema = z.object({\n transport: z.literal('stdio'),\n command: z.string().min(1),\n args: z.array(z.string()).optional(),\n cwd: z.string().optional(),\n env: z.record(z.string(), z.string()).optional(),\n});\n\nconst httpTargetSchema = z.object({\n transport: z.literal('http'),\n url: z.string().min(1),\n /** Extra HTTP headers sent on every request (e.g. auth). */\n headers: z.record(z.string(), z.string()).optional(),\n});\n\nconst targetSchema = z.discriminatedUnion('transport', [stdioTargetSchema, httpTargetSchema]);\n\n/** A normalizer is a builtin name or an inline `{ name, apply }` object. */\nconst normalizerRefSchema = z.union([\n z.string(),\n z.object({\n name: z.string(),\n apply: z.custom<(value: unknown) => unknown>((v) => typeof v === 'function'),\n }),\n]);\n\nconst recordCaseSchema = z.object({\n case: z.string().min(1),\n arguments: z.record(z.string(), z.unknown()).default({}),\n});\n\nconst recordToolSchema = z.object({\n cases: z.array(recordCaseSchema).min(1),\n normalizers: z.array(normalizerRefSchema).optional(),\n});\n\nconst regressionSchema = z.object({\n /** Committed golden directory (relative to the config file's cwd). */\n goldenDir: z.string().default('vexyo/goldens'),\n /** Severity at/above which drift fails the run. */\n failOn: z.enum(['error', 'warning']).default('error'),\n /** Default normalizers applied to every recorded tool. */\n normalizers: z.array(normalizerRefSchema).default([]),\n /** Tools to record/compare, keyed by name. Empty = record nothing (opt-in). */\n record: z.record(z.string(), recordToolSchema).default({}),\n});\n\nexport const configSchema = z.object({\n specVersion: z.enum(SPEC_VERSIONS).default('2025-11-25'),\n target: targetSchema,\n security: z.boolean().default(false),\n regression: regressionSchema.optional(),\n});\n\nexport type RegressionConfig = z.infer<typeof regressionSchema>;\n\n/** Validated, defaults-applied config. */\nexport type Config = z.infer<typeof configSchema>;\n/** Shape a user writes in a config file (before defaults are applied). */\nexport type UserConfig = z.input<typeof configSchema>;\nexport type StdioTarget = Extract<Config['target'], { transport: 'stdio' }>;\n\n/** Raised for anything the user must fix in their config or invocation (exit 2). */\nexport class ConfigError extends Error {\n constructor(message: string, cause?: unknown) {\n super(message, cause !== undefined ? { cause } : undefined);\n this.name = 'ConfigError';\n }\n}\n\n/**\n * Resolve which config file to load: an explicit `--config` (relative to cwd),\n * otherwise the first {@link CONFIG_CANDIDATES} present in cwd. Throws a\n * {@link ConfigError} suggesting `vexyo init` when nothing is found. Returns an\n * absolute path.\n */\nexport function resolveConfigPath(\n explicit: string | undefined,\n cwd: string = process.cwd(),\n): string {\n if (explicit) {\n return resolve(cwd, explicit);\n }\n for (const name of CONFIG_CANDIDATES) {\n const candidate = resolve(cwd, name);\n if (existsSync(candidate)) {\n return candidate;\n }\n }\n throw new ConfigError(\n `No config file found in ${cwd}. Create one with \\`vexyo init\\`, or pass --config <path>.`,\n );\n}\n\nfunction formatZodError(error: z.ZodError): string {\n return error.issues\n .map((issue) => ` - ${issue.path.join('.') || '(root)'}: ${issue.message}`)\n .join('\\n');\n}\n\n/**\n * Map vexyo's own package specifiers to the running CLI's installation so a\n * config that imports `@vexyo/cli/config` (defineConfig) loads even when it\n * lives in a directory without a local `node_modules` (a bare project, `/tmp`,\n * etc.). Resolved relative to THIS module — the CLI — not the config's location,\n * which is the pattern vite/eslint use for their own defineConfig imports. A\n * local install, when present, still wins (jiti prefers it over the alias).\n */\nfunction selfPackageAliases(): Record<string, string> {\n const aliases: Record<string, string> = {};\n let req: ReturnType<typeof createRequire>;\n try {\n // Empty `import.meta.url` (e.g. the Action's esbuild CJS bundle) → no alias;\n // there the CLI always runs where `@vexyo/cli` is already resolvable.\n req = createRequire(import.meta.url);\n } catch {\n return aliases;\n }\n for (const spec of ['@vexyo/cli/config', '@vexyo/cli', '@vexyo/core', '@vexyo/reporters']) {\n try {\n aliases[spec] = req.resolve(spec);\n } catch {\n // Unresolvable from here (e.g. dev without a built dist); jiti then\n // resolves the specifier from the config's own location instead.\n }\n }\n return aliases;\n}\n\n/**\n * Load and validate a config file. Supports `.ts`/`.js`/`.mjs` via jiti so the\n * blueprint's `examples/*.config.ts` work without a build step. The default\n * export is validated against {@link configSchema}.\n */\nexport async function loadConfig(configPath: string): Promise<Config> {\n const absolute = resolve(process.cwd(), configPath);\n\n let mod: unknown;\n try {\n const jiti = createJiti(pathToFileURL(absolute).href, { alias: selfPackageAliases() });\n mod = await jiti.import(absolute, { default: true });\n } catch (err) {\n throw new ConfigError(\n `Could not load config file at ${absolute}. Check the path and that the file has a default export.`,\n err,\n );\n }\n\n const parsed = configSchema.safeParse(mod);\n if (!parsed.success) {\n throw new ConfigError(`Invalid config at ${absolute}:\\n${formatZodError(parsed.error)}`);\n }\n return parsed.data;\n}\n","/** Print a harness/config error (with its cause, if any) and return exit code 2. */\nexport function reportHarnessError(err: unknown): 2 {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`error: ${message}\\n`);\n const cause = err instanceof Error ? err.cause : undefined;\n if (cause !== undefined) {\n process.stderr.write(` cause: ${cause instanceof Error ? cause.message : String(cause)}\\n`);\n }\n return 2;\n}\n","import { dirname, resolve } from 'node:path';\nimport type { GoldenConfig } from '@vexyo/core';\nimport type { RegressionConfig } from './config';\n\n/** Resolve `goldenDir` relative to the config file's directory. */\nexport function resolveGoldenDir(configPath: string, regression: RegressionConfig): string {\n return resolve(dirname(resolve(process.cwd(), configPath)), regression.goldenDir);\n}\n\n/** Map the CLI's regression config onto the core engine's {@link GoldenConfig}. */\nexport function toGoldenConfig(specVersion: string, regression: RegressionConfig): GoldenConfig {\n return {\n specVersion,\n defaultNormalizers: regression.normalizers,\n tools: Object.fromEntries(\n Object.entries(regression.record).map(([name, spec]) => [\n name,\n { cases: spec.cases, normalizers: spec.normalizers },\n ]),\n ),\n };\n}\n","import type { ConnectTarget } from '@vexyo/core';\nimport type { Config } from './config';\n\n/** Map the validated config's target onto the core engine's {@link ConnectTarget}. */\nexport function buildTarget(config: Config): ConnectTarget {\n if (config.target.transport === 'http') {\n return {\n transport: 'http',\n http: { url: config.target.url, headers: config.target.headers },\n };\n }\n return { transport: 'stdio', stdio: config.target };\n}\n"],"mappings":"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","names":["resolve"]}
package/dist/index.js CHANGED
@@ -1,24 +1,105 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ConfigError,
4
+ DEFAULT_SPEC_VERSION,
4
5
  buildTarget,
5
6
  loadConfig,
6
7
  reportHarnessError,
8
+ resolveConfigPath,
7
9
  resolveGoldenDir,
8
10
  runCommand,
9
11
  toGoldenConfig
10
- } from "./chunk-254CIBOC.js";
12
+ } from "./chunk-HMGCKVG7.js";
11
13
 
12
14
  // src/index.ts
13
15
  import { BRAND } from "@vexyo/core";
14
16
  import { Command } from "commander";
15
17
 
18
+ // src/commands/init.ts
19
+ import { existsSync } from "fs";
20
+ import { writeFile } from "fs/promises";
21
+ import { resolve } from "path";
22
+ var CONFIG_FILENAME = "vexyo.config.ts";
23
+ function renderConfigTemplate(transport, specVersion) {
24
+ const activeTarget = transport === "http" ? ` target: {
25
+ transport: 'http',
26
+ url: 'http://127.0.0.1:3000/mcp',
27
+ // headers: { Authorization: 'Bearer <your-token>' },
28
+ },` : ` target: {
29
+ transport: 'stdio',
30
+ command: 'node',
31
+ args: ['path/to/your/mcp-server.js'],
32
+ // cwd: '.',
33
+ // env: { API_KEY: 'value' },
34
+ },`;
35
+ const alternatives = transport === "http" ? ` // --- Alternative targets (uncomment one, comment out the http target above) ---
36
+ //
37
+ // Node (stdio):
38
+ // target: { transport: 'stdio', command: 'node', args: ['path/to/your/mcp-server.js'] },
39
+ //
40
+ // Python / other runtimes (stdio):
41
+ // target: { transport: 'stdio', command: 'python', args: ['-m', 'your_mcp_server'] },` : ` // --- Alternative targets (uncomment one, comment out the stdio target above) ---
42
+ //
43
+ // Python / other runtimes (stdio):
44
+ // target: { transport: 'stdio', command: 'python', args: ['-m', 'your_mcp_server'] },
45
+ //
46
+ // Streamable HTTP (a running server):
47
+ // target: { transport: 'http', url: 'http://127.0.0.1:3000/mcp' },`;
48
+ return `import { defineConfig } from '@vexyo/cli/config';
49
+
50
+ export default defineConfig({
51
+ specVersion: '${specVersion}',
52
+
53
+ // The MCP server under test.
54
+ ${activeTarget}
55
+
56
+ ${alternatives}
57
+
58
+ // --- Regression testing (optional) ---
59
+ // Record golden tool outputs with \`vexyo record\`, then \`vexyo run --regression\`
60
+ // fails when behavior drifts.
61
+ // regression: {
62
+ // goldenDir: 'vexyo/goldens',
63
+ // normalizers: ['iso-timestamp', 'uuid'],
64
+ // record: {
65
+ // // greet: { cases: [{ case: 'basic', arguments: { name: 'world' } }] },
66
+ // },
67
+ // },
68
+ });
69
+ `;
70
+ }
71
+ async function initCommand(opts) {
72
+ const cwd = opts.cwd ?? process.cwd();
73
+ const target = resolve(cwd, CONFIG_FILENAME);
74
+ if (existsSync(target) && !opts.force) {
75
+ return reportHarnessError(
76
+ new ConfigError(`${CONFIG_FILENAME} already exists. Pass --force to overwrite it.`)
77
+ );
78
+ }
79
+ try {
80
+ await writeFile(target, renderConfigTemplate(opts.transport, DEFAULT_SPEC_VERSION));
81
+ } catch (err) {
82
+ return reportHarnessError(err);
83
+ }
84
+ process.stdout.write(
85
+ `Created ${CONFIG_FILENAME}.
86
+
87
+ Next steps:
88
+ 1. Edit the \`target\` to point at your MCP server.
89
+ 2. vexyo run --config ${CONFIG_FILENAME}
90
+ `
91
+ );
92
+ return 0;
93
+ }
94
+
16
95
  // src/commands/record.ts
17
96
  import { connectTarget, recordGoldens, writeGoldenSet } from "@vexyo/core";
18
97
  async function recordCommand(opts) {
98
+ let configPath;
19
99
  let config;
20
100
  try {
21
- config = await loadConfig(opts.config);
101
+ configPath = resolveConfigPath(opts.config);
102
+ config = await loadConfig(configPath);
22
103
  } catch (err) {
23
104
  return reportHarnessError(err);
24
105
  }
@@ -27,7 +108,7 @@ async function recordCommand(opts) {
27
108
  new ConfigError("No `regression` block in the config \u2014 nothing to record.")
28
109
  );
29
110
  }
30
- const goldenDir = resolveGoldenDir(opts.config, config.regression);
111
+ const goldenDir = resolveGoldenDir(configPath, config.regression);
31
112
  const goldenCfg = toGoldenConfig(config.specVersion, config.regression);
32
113
  const toolCount = Object.keys(goldenCfg.tools).length;
33
114
  process.stdout.write(
@@ -60,7 +141,10 @@ async function recordCommand(opts) {
60
141
  // src/index.ts
61
142
  var program = new Command();
62
143
  program.name(BRAND.bin).description("CI-native conformance harness for MCP servers");
63
- program.command("run").description("Run the conformance suite (and optionally regression) against an MCP server").requiredOption("-c, --config <path>", "path to the vexyo config file").option("-s, --spec-version <version>", "override the spec version to target").option("-r, --reporter <name>", "output reporter (console, json, markdown)", "console").option("--regression", "also diff current behavior against the recorded golden set").option("--fail-on <severity>", "severity at/above which findings fail the run (error, warning)").action(
144
+ program.command("run").description("Run the conformance suite (and optionally regression) against an MCP server").option(
145
+ "-c, --config <path>",
146
+ "path to the vexyo config file (auto-discovered in cwd if omitted)"
147
+ ).option("-s, --spec-version <version>", "override the spec version to target").option("-r, --reporter <name>", "output reporter (console, json, markdown)", "console").option("--regression", "also diff current behavior against the recorded golden set").option("--fail-on <severity>", "severity at/above which findings fail the run (error, warning)").action(
64
148
  async (opts) => {
65
149
  const code = await runCommand({
66
150
  config: opts.config,
@@ -72,10 +156,25 @@ program.command("run").description("Run the conformance suite (and optionally re
72
156
  process.exit(code);
73
157
  }
74
158
  );
75
- program.command("record").description("Record golden files for the tools configured under `regression.record`").requiredOption("-c, --config <path>", "path to the vexyo config file").action(async (opts) => {
159
+ program.command("record").description("Record golden files for the tools configured under `regression.record`").option(
160
+ "-c, --config <path>",
161
+ "path to the vexyo config file (auto-discovered in cwd if omitted)"
162
+ ).action(async (opts) => {
76
163
  const code = await recordCommand({ config: opts.config });
77
164
  process.exit(code);
78
165
  });
166
+ program.command("init").description("Scaffold a vexyo.config.ts in the current directory").option("-t, --transport <kind>", "target transport for the template (stdio, http)", "stdio").option("-f, --force", "overwrite an existing vexyo.config.ts").action(async (opts) => {
167
+ if (opts.transport !== "stdio" && opts.transport !== "http") {
168
+ process.stderr.write(`error: --transport must be "stdio" or "http".
169
+ `);
170
+ process.exit(2);
171
+ }
172
+ const code = await initCommand({
173
+ transport: opts.transport,
174
+ force: Boolean(opts.force)
175
+ });
176
+ process.exit(code);
177
+ });
79
178
  var argv = process.argv.slice();
80
179
  if (argv[2] === "--") argv.splice(2, 1);
81
180
  program.parseAsync(argv).catch((err) => {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/commands/record.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { BRAND } from '@vexyo/core';\nimport { Command } from 'commander';\nimport { recordCommand } from './commands/record';\nimport { runCommand } from './commands/run';\n\nconst program = new Command();\n\nprogram.name(BRAND.bin).description('CI-native conformance harness for MCP servers');\n\nprogram\n .command('run')\n .description('Run the conformance suite (and optionally regression) against an MCP server')\n .requiredOption('-c, --config <path>', 'path to the vexyo config file')\n .option('-s, --spec-version <version>', 'override the spec version to target')\n .option('-r, --reporter <name>', 'output reporter (console, json, markdown)', 'console')\n .option('--regression', 'also diff current behavior against the recorded golden set')\n .option('--fail-on <severity>', 'severity at/above which findings fail the run (error, warning)')\n .action(\n async (opts: {\n config: string;\n specVersion?: string;\n reporter: string;\n regression?: boolean;\n failOn?: string;\n }) => {\n const code = await runCommand({\n config: opts.config,\n specVersion: opts.specVersion,\n reporter: opts.reporter,\n regression: opts.regression,\n failOn: opts.failOn,\n });\n process.exit(code);\n },\n );\n\nprogram\n .command('record')\n .description('Record golden files for the tools configured under `regression.record`')\n .requiredOption('-c, --config <path>', 'path to the vexyo config file')\n .action(async (opts: { config: string }) => {\n const code = await recordCommand({ config: opts.config });\n process.exit(code);\n });\n\n// `pnpm vexyo -- run ...` forwards the `--` separator literally; drop a\n// leading one so both that and `pnpm vexyo run ...` parse identically.\nconst argv = process.argv.slice();\nif (argv[2] === '--') argv.splice(2, 1);\n\nprogram.parseAsync(argv).catch((err: unknown) => {\n process.stderr.write(`fatal: ${err instanceof Error ? err.message : String(err)}\\n`);\n process.exit(2);\n});\n","import { connectTarget, recordGoldens, writeGoldenSet } from '@vexyo/core';\nimport { ConfigError, loadConfig, type Config } from '../config';\nimport { reportHarnessError } from '../errors';\nimport { resolveGoldenDir, toGoldenConfig } from '../regression';\nimport { buildTarget } from '../target';\n\nexport interface RecordCommandOptions {\n config: string;\n}\n\n/**\n * Capture golden files. Connects over the configured transport, snapshots the\n * schema manifest (read-only), and calls only the tools explicitly listed in\n * `regression.record` — never \"call everything\". Returns 0 on success, 2 on any\n * harness/config error.\n */\nexport async function recordCommand(opts: RecordCommandOptions): Promise<0 | 2> {\n let config: Config;\n try {\n config = await loadConfig(opts.config);\n } catch (err) {\n return reportHarnessError(err);\n }\n\n if (!config.regression) {\n return reportHarnessError(\n new ConfigError('No `regression` block in the config — nothing to record.'),\n );\n }\n\n const goldenDir = resolveGoldenDir(opts.config, config.regression);\n const goldenCfg = toGoldenConfig(config.specVersion, config.regression);\n\n const toolCount = Object.keys(goldenCfg.tools).length;\n process.stdout.write(\n toolCount === 0\n ? 'No tools configured under `regression.record`; recording manifest only.\\n'\n : `Recording ${toolCount} tool(s): ${Object.keys(goldenCfg.tools).sort().join(', ')}\\n`,\n );\n\n let conn: Awaited<ReturnType<typeof connectTarget>>;\n try {\n conn = await connectTarget(buildTarget(config));\n } catch (err) {\n return reportHarnessError(err);\n }\n\n try {\n const set = await recordGoldens(conn.client, goldenCfg);\n const written = await writeGoldenSet(goldenDir, set);\n process.stdout.write(`Wrote ${written.length} golden file(s) to ${goldenDir}:\\n`);\n for (const path of written) {\n process.stdout.write(` ${path}\\n`);\n }\n return 0;\n } catch (err) {\n return reportHarnessError(err);\n } finally {\n await conn.close().catch(() => undefined);\n }\n}\n"],"mappings":";;;;;;;;;;;;AACA,SAAS,aAAa;AACtB,SAAS,eAAe;;;ACFxB,SAAS,eAAe,eAAe,sBAAsB;AAgB7D,eAAsB,cAAc,MAA4C;AAC9E,MAAI;AACJ,MAAI;AACF,aAAS,MAAM,WAAW,KAAK,MAAM;AAAA,EACvC,SAAS,KAAK;AACZ,WAAO,mBAAmB,GAAG;AAAA,EAC/B;AAEA,MAAI,CAAC,OAAO,YAAY;AACtB,WAAO;AAAA,MACL,IAAI,YAAY,+DAA0D;AAAA,IAC5E;AAAA,EACF;AAEA,QAAM,YAAY,iBAAiB,KAAK,QAAQ,OAAO,UAAU;AACjE,QAAM,YAAY,eAAe,OAAO,aAAa,OAAO,UAAU;AAEtE,QAAM,YAAY,OAAO,KAAK,UAAU,KAAK,EAAE;AAC/C,UAAQ,OAAO;AAAA,IACb,cAAc,IACV,8EACA,aAAa,SAAS,aAAa,OAAO,KAAK,UAAU,KAAK,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA,EACvF;AAEA,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,cAAc,YAAY,MAAM,CAAC;AAAA,EAChD,SAAS,KAAK;AACZ,WAAO,mBAAmB,GAAG;AAAA,EAC/B;AAEA,MAAI;AACF,UAAM,MAAM,MAAM,cAAc,KAAK,QAAQ,SAAS;AACtD,UAAM,UAAU,MAAM,eAAe,WAAW,GAAG;AACnD,YAAQ,OAAO,MAAM,SAAS,QAAQ,MAAM,sBAAsB,SAAS;AAAA,CAAK;AAChF,eAAW,QAAQ,SAAS;AAC1B,cAAQ,OAAO,MAAM,KAAK,IAAI;AAAA,CAAI;AAAA,IACpC;AACA,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,WAAO,mBAAmB,GAAG;AAAA,EAC/B,UAAE;AACA,UAAM,KAAK,MAAM,EAAE,MAAM,MAAM,MAAS;AAAA,EAC1C;AACF;;;ADtDA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QAAQ,KAAK,MAAM,GAAG,EAAE,YAAY,+CAA+C;AAEnF,QACG,QAAQ,KAAK,EACb,YAAY,6EAA6E,EACzF,eAAe,uBAAuB,+BAA+B,EACrE,OAAO,gCAAgC,qCAAqC,EAC5E,OAAO,yBAAyB,6CAA6C,SAAS,EACtF,OAAO,gBAAgB,4DAA4D,EACnF,OAAO,wBAAwB,gEAAgE,EAC/F;AAAA,EACC,OAAO,SAMD;AACJ,UAAM,OAAO,MAAM,WAAW;AAAA,MAC5B,QAAQ,KAAK;AAAA,MACb,aAAa,KAAK;AAAA,MAClB,UAAU,KAAK;AAAA,MACf,YAAY,KAAK;AAAA,MACjB,QAAQ,KAAK;AAAA,IACf,CAAC;AACD,YAAQ,KAAK,IAAI;AAAA,EACnB;AACF;AAEF,QACG,QAAQ,QAAQ,EAChB,YAAY,wEAAwE,EACpF,eAAe,uBAAuB,+BAA+B,EACrE,OAAO,OAAO,SAA6B;AAC1C,QAAM,OAAO,MAAM,cAAc,EAAE,QAAQ,KAAK,OAAO,CAAC;AACxD,UAAQ,KAAK,IAAI;AACnB,CAAC;AAIH,IAAM,OAAO,QAAQ,KAAK,MAAM;AAChC,IAAI,KAAK,CAAC,MAAM,KAAM,MAAK,OAAO,GAAG,CAAC;AAEtC,QAAQ,WAAW,IAAI,EAAE,MAAM,CAAC,QAAiB;AAC/C,UAAQ,OAAO,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,CAAI;AACnF,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/commands/init.ts","../src/commands/record.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { BRAND } from '@vexyo/core';\nimport { Command } from 'commander';\nimport { initCommand, type InitTransport } from './commands/init';\nimport { recordCommand } from './commands/record';\nimport { runCommand } from './commands/run';\n\nconst program = new Command();\n\nprogram.name(BRAND.bin).description('CI-native conformance harness for MCP servers');\n\nprogram\n .command('run')\n .description('Run the conformance suite (and optionally regression) against an MCP server')\n .option(\n '-c, --config <path>',\n 'path to the vexyo config file (auto-discovered in cwd if omitted)',\n )\n .option('-s, --spec-version <version>', 'override the spec version to target')\n .option('-r, --reporter <name>', 'output reporter (console, json, markdown)', 'console')\n .option('--regression', 'also diff current behavior against the recorded golden set')\n .option('--fail-on <severity>', 'severity at/above which findings fail the run (error, warning)')\n .action(\n async (opts: {\n config?: string;\n specVersion?: string;\n reporter: string;\n regression?: boolean;\n failOn?: string;\n }) => {\n const code = await runCommand({\n config: opts.config,\n specVersion: opts.specVersion,\n reporter: opts.reporter,\n regression: opts.regression,\n failOn: opts.failOn,\n });\n process.exit(code);\n },\n );\n\nprogram\n .command('record')\n .description('Record golden files for the tools configured under `regression.record`')\n .option(\n '-c, --config <path>',\n 'path to the vexyo config file (auto-discovered in cwd if omitted)',\n )\n .action(async (opts: { config?: string }) => {\n const code = await recordCommand({ config: opts.config });\n process.exit(code);\n });\n\nprogram\n .command('init')\n .description('Scaffold a vexyo.config.ts in the current directory')\n .option('-t, --transport <kind>', 'target transport for the template (stdio, http)', 'stdio')\n .option('-f, --force', 'overwrite an existing vexyo.config.ts')\n .action(async (opts: { transport: string; force?: boolean }) => {\n if (opts.transport !== 'stdio' && opts.transport !== 'http') {\n process.stderr.write(`error: --transport must be \"stdio\" or \"http\".\\n`);\n process.exit(2);\n }\n const code = await initCommand({\n transport: opts.transport as InitTransport,\n force: Boolean(opts.force),\n });\n process.exit(code);\n });\n\n// `pnpm vexyo -- run ...` forwards the `--` separator literally; drop a\n// leading one so both that and `pnpm vexyo run ...` parse identically.\nconst argv = process.argv.slice();\nif (argv[2] === '--') argv.splice(2, 1);\n\nprogram.parseAsync(argv).catch((err: unknown) => {\n process.stderr.write(`fatal: ${err instanceof Error ? err.message : String(err)}\\n`);\n process.exit(2);\n});\n","import { existsSync } from 'node:fs';\nimport { writeFile } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { ConfigError, DEFAULT_SPEC_VERSION } from '../config';\nimport { reportHarnessError } from '../errors';\n\nexport type InitTransport = 'stdio' | 'http';\n\nexport interface InitCommandOptions {\n transport: InitTransport;\n force: boolean;\n /** Directory to write into (defaults to cwd); injectable for tests. */\n cwd?: string;\n}\n\nconst CONFIG_FILENAME = 'vexyo.config.ts';\n\n/**\n * Render a `vexyo.config.ts` template. The chosen transport's target is active;\n * the other shapes (and a regression block) are included commented out. Pure —\n * no filesystem — so the scaffold output is snapshot-testable.\n */\nexport function renderConfigTemplate(transport: InitTransport, specVersion: string): string {\n const activeTarget =\n transport === 'http'\n ? ` target: {\n transport: 'http',\n url: 'http://127.0.0.1:3000/mcp',\n // headers: { Authorization: 'Bearer <your-token>' },\n },`\n : ` target: {\n transport: 'stdio',\n command: 'node',\n args: ['path/to/your/mcp-server.js'],\n // cwd: '.',\n // env: { API_KEY: 'value' },\n },`;\n\n const alternatives =\n transport === 'http'\n ? ` // --- Alternative targets (uncomment one, comment out the http target above) ---\n //\n // Node (stdio):\n // target: { transport: 'stdio', command: 'node', args: ['path/to/your/mcp-server.js'] },\n //\n // Python / other runtimes (stdio):\n // target: { transport: 'stdio', command: 'python', args: ['-m', 'your_mcp_server'] },`\n : ` // --- Alternative targets (uncomment one, comment out the stdio target above) ---\n //\n // Python / other runtimes (stdio):\n // target: { transport: 'stdio', command: 'python', args: ['-m', 'your_mcp_server'] },\n //\n // Streamable HTTP (a running server):\n // target: { transport: 'http', url: 'http://127.0.0.1:3000/mcp' },`;\n\n return `import { defineConfig } from '@vexyo/cli/config';\n\nexport default defineConfig({\n specVersion: '${specVersion}',\n\n // The MCP server under test.\n${activeTarget}\n\n${alternatives}\n\n // --- Regression testing (optional) ---\n // Record golden tool outputs with \\`vexyo record\\`, then \\`vexyo run --regression\\`\n // fails when behavior drifts.\n // regression: {\n // goldenDir: 'vexyo/goldens',\n // normalizers: ['iso-timestamp', 'uuid'],\n // record: {\n // // greet: { cases: [{ case: 'basic', arguments: { name: 'world' } }] },\n // },\n // },\n});\n`;\n}\n\n/**\n * Scaffold a `vexyo.config.ts` in cwd. Refuses to overwrite an existing file\n * without `--force`. Returns 0 on success, 2 on any error.\n */\nexport async function initCommand(opts: InitCommandOptions): Promise<0 | 2> {\n const cwd = opts.cwd ?? process.cwd();\n const target = resolve(cwd, CONFIG_FILENAME);\n\n if (existsSync(target) && !opts.force) {\n return reportHarnessError(\n new ConfigError(`${CONFIG_FILENAME} already exists. Pass --force to overwrite it.`),\n );\n }\n\n try {\n await writeFile(target, renderConfigTemplate(opts.transport, DEFAULT_SPEC_VERSION));\n } catch (err) {\n return reportHarnessError(err);\n }\n\n process.stdout.write(\n `Created ${CONFIG_FILENAME}.\\n\\n` +\n `Next steps:\\n` +\n ` 1. Edit the \\`target\\` to point at your MCP server.\\n` +\n ` 2. vexyo run --config ${CONFIG_FILENAME}\\n`,\n );\n return 0;\n}\n","import { connectTarget, recordGoldens, writeGoldenSet } from '@vexyo/core';\nimport { ConfigError, loadConfig, resolveConfigPath, type Config } from '../config';\nimport { reportHarnessError } from '../errors';\nimport { resolveGoldenDir, toGoldenConfig } from '../regression';\nimport { buildTarget } from '../target';\n\nexport interface RecordCommandOptions {\n /** Explicit config path; when omitted, auto-discovered in cwd. */\n config?: string;\n}\n\n/**\n * Capture golden files. Connects over the configured transport, snapshots the\n * schema manifest (read-only), and calls only the tools explicitly listed in\n * `regression.record` — never \"call everything\". Returns 0 on success, 2 on any\n * harness/config error.\n */\nexport async function recordCommand(opts: RecordCommandOptions): Promise<0 | 2> {\n let configPath: string;\n let config: Config;\n try {\n configPath = resolveConfigPath(opts.config);\n config = await loadConfig(configPath);\n } catch (err) {\n return reportHarnessError(err);\n }\n\n if (!config.regression) {\n return reportHarnessError(\n new ConfigError('No `regression` block in the config — nothing to record.'),\n );\n }\n\n const goldenDir = resolveGoldenDir(configPath, config.regression);\n const goldenCfg = toGoldenConfig(config.specVersion, config.regression);\n\n const toolCount = Object.keys(goldenCfg.tools).length;\n process.stdout.write(\n toolCount === 0\n ? 'No tools configured under `regression.record`; recording manifest only.\\n'\n : `Recording ${toolCount} tool(s): ${Object.keys(goldenCfg.tools).sort().join(', ')}\\n`,\n );\n\n let conn: Awaited<ReturnType<typeof connectTarget>>;\n try {\n conn = await connectTarget(buildTarget(config));\n } catch (err) {\n return reportHarnessError(err);\n }\n\n try {\n const set = await recordGoldens(conn.client, goldenCfg);\n const written = await writeGoldenSet(goldenDir, set);\n process.stdout.write(`Wrote ${written.length} golden file(s) to ${goldenDir}:\\n`);\n for (const path of written) {\n process.stdout.write(` ${path}\\n`);\n }\n return 0;\n } catch (err) {\n return reportHarnessError(err);\n } finally {\n await conn.close().catch(() => undefined);\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vexyo/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "CLI for vexyo — conformance and regression testing for MCP servers, for local dev and CI.",
5
5
  "keywords": [
6
6
  "mcp",
@@ -41,8 +41,8 @@
41
41
  "commander": "^13.1.0",
42
42
  "jiti": "^2.4.2",
43
43
  "zod": "^4.0.0",
44
- "@vexyo/core": "0.1.0",
45
- "@vexyo/reporters": "0.1.0"
44
+ "@vexyo/core": "0.1.1",
45
+ "@vexyo/reporters": "0.1.1"
46
46
  },
47
47
  "main": "./dist/index.js",
48
48
  "types": "./dist/index.d.ts",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/run.ts","../src/config.ts","../src/errors.ts","../src/regression.ts","../src/target.ts"],"sourcesContent":["import {\n readGoldenSet,\n runRegression,\n runSuite,\n type RunResult,\n type RunSuiteOptions,\n type Severity,\n type SpecVersion,\n} from '@vexyo/core';\nimport {\n consoleReporter,\n jsonReporter,\n junitReporter,\n markdownReporter,\n type Reporter,\n} from '@vexyo/reporters';\nimport { ConfigError, loadConfig, type Config } from '../config';\nimport { reportHarnessError } from '../errors';\nimport { resolveGoldenDir, toGoldenConfig } from '../regression';\nimport { buildTarget } from '../target';\n\nexport interface RunCommandOptions {\n config: string;\n specVersion?: string;\n reporter: string;\n regression?: boolean;\n failOn?: string;\n}\n\nexport interface ExecuteRunOptions {\n config: string;\n specVersion?: string;\n regression?: boolean;\n failOn?: string;\n}\n\nconst REPORTERS: Record<string, Reporter> = {\n console: consoleReporter,\n json: jsonReporter,\n markdown: markdownReporter,\n junit: junitReporter,\n};\n\n/**\n * Load config, connect over the configured transport, run the conformance suite\n * (plus regression when requested), and return the {@link RunResult}. Throws\n * `ConfigError`/`GoldenError`/connection errors for anything the caller should\n * map to exit code 2. Shared by the `run` command and the GitHub Action.\n */\nexport async function executeRun(opts: ExecuteRunOptions): Promise<RunResult> {\n const config = await loadConfig(opts.config);\n const specVersion = resolveSpecVersion(opts.specVersion, config);\n const failOn = resolveFailOn(opts.failOn, config);\n const extraChecks = await buildRegressionPhase(opts, config, specVersion);\n\n return runSuite({\n specVersion,\n target: buildTarget(config),\n failOn,\n extraChecks,\n });\n}\n\n/**\n * Execute the `run` command. Returns the process exit code:\n * 0 = pass, 1 = findings at/above threshold, 2 = harness/config error.\n */\nexport async function runCommand(opts: RunCommandOptions): Promise<0 | 1 | 2> {\n const reporter = REPORTERS[opts.reporter];\n if (!reporter) {\n return reportHarnessError(\n new ConfigError(\n `Unknown reporter \"${opts.reporter}\". Available reporters: ${Object.keys(REPORTERS).join(', ')}.`,\n ),\n );\n }\n\n let result: RunResult;\n try {\n result = await executeRun(opts);\n } catch (err) {\n return reportHarnessError(err);\n }\n\n process.stdout.write(`${reporter.format(result)}\\n`);\n return result.exitCode;\n}\n\nfunction resolveSpecVersion(override: string | undefined, config: Config): SpecVersion {\n const specVersion = (override as SpecVersion | undefined) ?? config.specVersion;\n if (specVersion !== '2025-11-25') {\n throw new ConfigError(\n `Spec version \"${specVersion}\" is not wired up in this build. Only 2025-11-25 is supported.`,\n );\n }\n return specVersion;\n}\n\nfunction resolveFailOn(override: string | undefined, config: Config): Severity {\n return (override as Severity | undefined) ?? config.regression?.failOn ?? 'error';\n}\n\nasync function buildRegressionPhase(\n opts: ExecuteRunOptions,\n config: Config,\n specVersion: SpecVersion,\n): Promise<RunSuiteOptions['extraChecks']> {\n if (!opts.regression) {\n return undefined;\n }\n if (!config.regression) {\n throw new ConfigError('`--regression` requires a `regression` block in the config.');\n }\n const goldenSet = await readGoldenSet(resolveGoldenDir(opts.config, config.regression));\n const goldenCfg = toGoldenConfig(specVersion, config.regression);\n return (ctx) => runRegression(ctx.client, goldenSet, goldenCfg);\n}\n","import { resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { createJiti } from 'jiti';\nimport { z } from 'zod';\n\nexport const SPEC_VERSIONS = ['2025-11-25', '2026-07-28'] as const;\n\nconst stdioTargetSchema = z.object({\n transport: z.literal('stdio'),\n command: z.string().min(1),\n args: z.array(z.string()).optional(),\n cwd: z.string().optional(),\n env: z.record(z.string(), z.string()).optional(),\n});\n\nconst httpTargetSchema = z.object({\n transport: z.literal('http'),\n url: z.string().min(1),\n /** Extra HTTP headers sent on every request (e.g. auth). */\n headers: z.record(z.string(), z.string()).optional(),\n});\n\nconst targetSchema = z.discriminatedUnion('transport', [stdioTargetSchema, httpTargetSchema]);\n\n/** A normalizer is a builtin name or an inline `{ name, apply }` object. */\nconst normalizerRefSchema = z.union([\n z.string(),\n z.object({\n name: z.string(),\n apply: z.custom<(value: unknown) => unknown>((v) => typeof v === 'function'),\n }),\n]);\n\nconst recordCaseSchema = z.object({\n case: z.string().min(1),\n arguments: z.record(z.string(), z.unknown()).default({}),\n});\n\nconst recordToolSchema = z.object({\n cases: z.array(recordCaseSchema).min(1),\n normalizers: z.array(normalizerRefSchema).optional(),\n});\n\nconst regressionSchema = z.object({\n /** Committed golden directory (relative to the config file's cwd). */\n goldenDir: z.string().default('vexyo/goldens'),\n /** Severity at/above which drift fails the run. */\n failOn: z.enum(['error', 'warning']).default('error'),\n /** Default normalizers applied to every recorded tool. */\n normalizers: z.array(normalizerRefSchema).default([]),\n /** Tools to record/compare, keyed by name. Empty = record nothing (opt-in). */\n record: z.record(z.string(), recordToolSchema).default({}),\n});\n\nexport const configSchema = z.object({\n specVersion: z.enum(SPEC_VERSIONS).default('2025-11-25'),\n target: targetSchema,\n security: z.boolean().default(false),\n regression: regressionSchema.optional(),\n});\n\nexport type RegressionConfig = z.infer<typeof regressionSchema>;\n\n/** Validated, defaults-applied config. */\nexport type Config = z.infer<typeof configSchema>;\n/** Shape a user writes in a config file (before defaults are applied). */\nexport type UserConfig = z.input<typeof configSchema>;\nexport type StdioTarget = Extract<Config['target'], { transport: 'stdio' }>;\n\n/** Raised for anything the user must fix in their config or invocation (exit 2). */\nexport class ConfigError extends Error {\n constructor(message: string, cause?: unknown) {\n super(message, cause !== undefined ? { cause } : undefined);\n this.name = 'ConfigError';\n }\n}\n\nfunction formatZodError(error: z.ZodError): string {\n return error.issues\n .map((issue) => ` - ${issue.path.join('.') || '(root)'}: ${issue.message}`)\n .join('\\n');\n}\n\n/**\n * Load and validate a config file. Supports `.ts`/`.js`/`.mjs` via jiti so the\n * blueprint's `examples/*.config.ts` work without a build step. The default\n * export is validated against {@link configSchema}.\n */\nexport async function loadConfig(configPath: string): Promise<Config> {\n const absolute = resolve(process.cwd(), configPath);\n\n let mod: unknown;\n try {\n const jiti = createJiti(pathToFileURL(absolute).href);\n mod = await jiti.import(absolute, { default: true });\n } catch (err) {\n throw new ConfigError(\n `Could not load config file at ${absolute}. Check the path and that the file has a default export.`,\n err,\n );\n }\n\n const parsed = configSchema.safeParse(mod);\n if (!parsed.success) {\n throw new ConfigError(`Invalid config at ${absolute}:\\n${formatZodError(parsed.error)}`);\n }\n return parsed.data;\n}\n","/** Print a harness/config error (with its cause, if any) and return exit code 2. */\nexport function reportHarnessError(err: unknown): 2 {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`error: ${message}\\n`);\n const cause = err instanceof Error ? err.cause : undefined;\n if (cause !== undefined) {\n process.stderr.write(` cause: ${cause instanceof Error ? cause.message : String(cause)}\\n`);\n }\n return 2;\n}\n","import { dirname, resolve } from 'node:path';\nimport type { GoldenConfig } from '@vexyo/core';\nimport type { RegressionConfig } from './config';\n\n/** Resolve `goldenDir` relative to the config file's directory. */\nexport function resolveGoldenDir(configPath: string, regression: RegressionConfig): string {\n return resolve(dirname(resolve(process.cwd(), configPath)), regression.goldenDir);\n}\n\n/** Map the CLI's regression config onto the core engine's {@link GoldenConfig}. */\nexport function toGoldenConfig(specVersion: string, regression: RegressionConfig): GoldenConfig {\n return {\n specVersion,\n defaultNormalizers: regression.normalizers,\n tools: Object.fromEntries(\n Object.entries(regression.record).map(([name, spec]) => [\n name,\n { cases: spec.cases, normalizers: spec.normalizers },\n ]),\n ),\n };\n}\n","import type { ConnectTarget } from '@vexyo/core';\nimport type { Config } from './config';\n\n/** Map the validated config's target onto the core engine's {@link ConnectTarget}. */\nexport function buildTarget(config: Config): ConnectTarget {\n if (config.target.transport === 'http') {\n return {\n transport: 'http',\n http: { url: config.target.url, headers: config.target.headers },\n };\n }\n return { transport: 'stdio', stdio: config.target };\n}\n"],"mappings":"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","names":["resolve"]}