axiom 0.25.0 → 0.27.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.
Files changed (46) hide show
  1. package/dist/bin.cjs +831 -408
  2. package/dist/bin.cjs.map +1 -1
  3. package/dist/bin.js +460 -372
  4. package/dist/bin.js.map +1 -1
  5. package/dist/{chunk-BSZFDG3O.js → chunk-3YNZM3A7.js} +21 -9
  6. package/dist/chunk-3YNZM3A7.js.map +1 -0
  7. package/dist/chunk-CZJEEQDG.js +32 -0
  8. package/dist/chunk-CZJEEQDG.js.map +1 -0
  9. package/dist/chunk-GPMG4NO7.js +301 -0
  10. package/dist/chunk-GPMG4NO7.js.map +1 -0
  11. package/dist/chunk-NV3Y4T4G.js +22 -0
  12. package/dist/chunk-NV3Y4T4G.js.map +1 -0
  13. package/dist/{chunk-JGAXOVPZ.js → chunk-YCOR62XR.js} +35 -44
  14. package/dist/chunk-YCOR62XR.js.map +1 -0
  15. package/dist/chunk-ZLRPS5IN.js +400 -0
  16. package/dist/chunk-ZLRPS5IN.js.map +1 -0
  17. package/dist/{config-DT-xvV7w.d.cts → config-BsY1WraV.d.cts} +6 -0
  18. package/dist/{config-DT-xvV7w.d.ts → config-BsY1WraV.d.ts} +6 -0
  19. package/dist/config.cjs.map +1 -1
  20. package/dist/config.d.cts +1 -1
  21. package/dist/config.d.ts +1 -1
  22. package/dist/config.js +2 -1
  23. package/dist/evals/custom-runner.cjs +102 -0
  24. package/dist/evals/custom-runner.cjs.map +1 -0
  25. package/dist/evals/custom-runner.d.cts +17 -0
  26. package/dist/evals/custom-runner.d.ts +17 -0
  27. package/dist/evals/custom-runner.js +63 -0
  28. package/dist/evals/custom-runner.js.map +1 -0
  29. package/dist/evals.cjs +137 -86
  30. package/dist/evals.cjs.map +1 -1
  31. package/dist/evals.d.cts +85 -12
  32. package/dist/evals.d.ts +85 -12
  33. package/dist/evals.js +60 -42
  34. package/dist/evals.js.map +1 -1
  35. package/dist/index.cjs +20 -8
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.js +1 -1
  38. package/dist/token-5SBK3AVU.js +63 -0
  39. package/dist/token-5SBK3AVU.js.map +1 -0
  40. package/dist/token-util-KUW2O75G.js +6 -0
  41. package/dist/token-util-KUW2O75G.js.map +1 -0
  42. package/package.json +9 -7
  43. package/dist/chunk-BSZFDG3O.js.map +0 -1
  44. package/dist/chunk-JGAXOVPZ.js.map +0 -1
  45. package/dist/chunk-N7MOZLNM.js +0 -73
  46. package/dist/chunk-N7MOZLNM.js.map +0 -1
package/dist/bin.cjs CHANGED
@@ -36,360 +36,10 @@ __export(bin_exports, {
36
36
  program: () => program
37
37
  });
38
38
  module.exports = __toCommonJS(bin_exports);
39
- var import_commander5 = require("commander");
40
-
41
- // src/cli/commands/push.command.ts
42
- var import_commander = require("commander");
43
-
44
- // src/transpiler.ts
45
- var import_esbuild = require("esbuild");
46
- var import_node_os = __toESM(require("os"), 1);
47
- var import_promises = __toESM(require("fs/promises"), 1);
48
- var import_node_path = __toESM(require("path"), 1);
49
- var import_node_path2 = require("path");
50
- async function loadPromptModule(filePath) {
51
- const result = await (0, import_esbuild.build)({
52
- entryPoints: [filePath],
53
- bundle: true,
54
- write: false,
55
- platform: "node",
56
- format: "esm",
57
- target: ["node18"],
58
- sourcemap: false,
59
- external: [
60
- // Only Node.js built-ins should be external
61
- "fs",
62
- "fs/promises",
63
- "node:fs",
64
- "node:fs/promises",
65
- "readline",
66
- "node:readline",
67
- "path",
68
- "node:path",
69
- "os",
70
- "node:os",
71
- "url",
72
- "node:url",
73
- "util",
74
- "node:util",
75
- "crypto",
76
- "node:crypto",
77
- "events",
78
- "node:events",
79
- "stream",
80
- "node:stream",
81
- "buffer",
82
- "node:buffer",
83
- "process",
84
- "node:process"
85
- ]
86
- });
87
- const code = result.outputFiles[0].text;
88
- const tempDir = import_node_os.default.tmpdir();
89
- const tempFileName = `axiom-ai-prompt-${Date.now()}-${Math.random().toString(36).substring(2)}.mjs`;
90
- const tempFilePath = import_node_path.default.join(tempDir, tempFileName);
91
- try {
92
- await import_promises.default.writeFile(tempFilePath, code, "utf-8");
93
- const moduleUrl = `file://${tempFilePath}`;
94
- const module2 = await import(moduleUrl);
95
- return module2.default || module2;
96
- } finally {
97
- try {
98
- await import_promises.default.unlink(tempFilePath);
99
- } catch (error) {
100
- console.warn(`Failed to clean up temporary file ${tempFilePath}:`, error);
101
- }
102
- }
103
- }
104
- function convertTypeBoxArgumentsToJsonSchema(arguments_) {
105
- if (!arguments_ || typeof arguments_ !== "object") {
106
- return {
107
- type: "object",
108
- properties: {},
109
- required: [],
110
- additionalProperties: false
111
- };
112
- }
113
- const properties = {};
114
- const required = [];
115
- for (const [key, value] of Object.entries(arguments_)) {
116
- if (value && typeof value === "object" && value.type) {
117
- properties[key] = {
118
- type: value.type,
119
- ...value.description && { description: value.description },
120
- ...value.enum && { enum: value.enum },
121
- ...value.items && { items: value.items },
122
- ...value.properties && { properties: value.properties },
123
- ...value.required && { required: value.required }
124
- };
125
- required.push(key);
126
- }
127
- }
128
- return {
129
- type: "object",
130
- properties,
131
- required,
132
- additionalProperties: false
133
- };
134
- }
135
- function extractPromptFromModule(moduleContent, filePath) {
136
- const fileBaseName = (0, import_node_path2.basename)(filePath, ".ts");
137
- const defaultId = fileBaseName.toLowerCase().replace(/[^a-z0-9]/g, "-");
138
- const convertedArguments = convertTypeBoxArgumentsToJsonSchema(moduleContent.arguments);
139
- const prompt = {
140
- name: moduleContent.name || "Untitled Prompt",
141
- slug: moduleContent.slug || defaultId,
142
- messages: moduleContent.messages || [],
143
- model: moduleContent.model,
144
- options: moduleContent.options,
145
- arguments: convertedArguments,
146
- id: moduleContent.id || defaultId,
147
- version: moduleContent.version || "1.0.0",
148
- // Optional fields from API response
149
- ...moduleContent.promptId && { promptId: moduleContent.promptId },
150
- ...moduleContent.description && { description: moduleContent.description }
151
- };
152
- if (!prompt.name) {
153
- throw new Error("Prompt must have a name");
154
- }
155
- if (!prompt.slug) {
156
- throw new Error("Prompt must have a slug");
157
- }
158
- if (!Array.isArray(prompt.messages)) {
159
- throw new Error("Prompt messages must be an array");
160
- }
161
- if (!prompt.model) {
162
- throw new Error("Prompt must have a model");
163
- }
164
- return prompt;
165
- }
166
- function transformJsonSchemaToTypeBox(schema) {
167
- if (schema.type === "string") {
168
- if (schema.enum && Array.isArray(schema.enum)) {
169
- const literals = schema.enum.map((value) => `Type.Literal('${value}')`).join(", ");
170
- const options = schema.description ? `, { description: '${schema.description}' }` : "";
171
- return `Type.Union([${literals}]${options})`;
172
- } else {
173
- const options = schema.description ? `{ description: '${schema.description}' }` : "";
174
- return `Type.String(${options})`;
175
- }
176
- }
177
- if (schema.type === "number" || schema.type === "integer") {
178
- const typeMethod = schema.type === "integer" ? "Integer" : "Number";
179
- const options = schema.description ? `{ description: '${schema.description}' }` : "";
180
- return `Type.${typeMethod}(${options})`;
181
- }
182
- if (schema.type === "boolean") {
183
- const options = schema.description ? `{ description: '${schema.description}' }` : "";
184
- return `Type.Boolean(${options})`;
185
- }
186
- if (schema.type === "array") {
187
- const itemsType = schema.items ? transformJsonSchemaToTypeBox(schema.items) : "Type.String()";
188
- const options = schema.description ? `, { description: '${schema.description}' }` : "";
189
- return `Type.Array(${itemsType}${options})`;
190
- }
191
- if (schema.type === "object") {
192
- if (schema.properties) {
193
- const props = Object.entries(schema.properties).map(([key, value]) => {
194
- const isRequired = schema.required && schema.required.includes(key);
195
- const propType = transformJsonSchemaToTypeBox(value);
196
- return ` ${key}: ${isRequired ? propType : `Type.Optional(${propType})`}`;
197
- }).join(",\n");
198
- const options = schema.description ? `, { description: '${schema.description}' }` : "";
199
- return `Type.Object({
200
- ${props}
201
- }${options})`;
202
- } else {
203
- const options = schema.description ? `{ description: '${schema.description}' }` : "";
204
- return `Type.Object({}${options ? `, ${options}` : ""})`;
205
- }
206
- }
207
- return "Type.String()";
208
- }
209
- function generatePromptFileFromApiResponse(apiResponse) {
210
- const { prompt, version } = apiResponse;
211
- const { data, options } = version;
212
- let argumentsCode = "{}";
213
- if (data.arguments && data.arguments.properties) {
214
- const argEntries = Object.entries(data.arguments.properties).map(([key, schema]) => {
215
- const isRequired = data.arguments.required && data.arguments.required.includes(key);
216
- const typeCode = transformJsonSchemaToTypeBox(schema);
217
- return ` ${key}: ${isRequired ? typeCode : `Type.Optional(${typeCode})`}`;
218
- }).join(",\n");
219
- if (argEntries) {
220
- argumentsCode = `{
221
- ${argEntries}
222
- }`;
223
- }
224
- }
225
- return `import { Type } from 'axiom/ai';
226
-
227
- export default {
228
- name: '${prompt.name}',
229
- slug: '${prompt.slug}',
230
- description: '${prompt.description || ""}',
231
- messages: [${data.messages.map(
232
- (msg) => `
233
- {
234
- role: '${msg.role}',
235
- content: '${msg.content.replace(/'/g, "\\'")}',
236
- }`
237
- ).join(",")}
238
- ],
239
- model: '${data.model || "gpt-4"}',
240
- options: {
241
- ${options ? Object.entries(options).map(([key, value]) => ` ${key}: ${value}`).join(",\n") : ""}
242
- },
243
- arguments: ${argumentsCode},
244
- version: '${version.version}',
245
- promptId: '${prompt.promptId}',
246
- };
247
- `;
248
- }
249
-
250
- // src/cli/commands/push.command.ts
251
- var import_promises2 = __toESM(require("fs/promises"), 1);
252
- var import_node_readline = __toESM(require("readline"), 1);
253
- async function askConfirmation(message) {
254
- const rl = import_node_readline.default.createInterface({
255
- input: process.stdin,
256
- output: process.stdout
257
- });
258
- return new Promise((resolve3) => {
259
- rl.question(`${message} (y/N): `, (answer) => {
260
- rl.close();
261
- resolve3(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
262
- });
263
- });
264
- }
265
- var loadPushCommand = (program2) => {
266
- const push = new import_commander.Command("push").description("Push a new version of an object").argument(
267
- "<object>",
268
- "The object to push, could be a prompt, en eval, a monitor, a dashboard, etc."
269
- ).option("--prod", "Adds the production tag to the prompt").option("--yes", "Automatically confirm overwriting the file with server response").action(async (filePath, options) => {
270
- let content = null;
271
- if (!filePath.endsWith(".prompt.ts")) {
272
- console.error("Prompt files must end with .prompt.ts");
273
- process.exit(1);
274
- }
275
- try {
276
- const moduleContent = await loadPromptModule(filePath);
277
- const promptData = extractPromptFromModule(moduleContent, filePath);
278
- content = promptData;
279
- console.log(`Transpiled prompt: ${promptData.name} (${promptData.slug})`);
280
- } catch (error) {
281
- console.error("Failed to transpile prompt file:", error);
282
- process.exit(1);
283
- }
284
- if (!content) {
285
- console.error("No content found");
286
- process.exit(1);
287
- }
288
- let shouldProceed = options.yes;
289
- if (!shouldProceed) {
290
- shouldProceed = await askConfirmation(
291
- `This will push "${content.name}" to Axiom and overwrite ${filePath}, are you sure you want to continue?`
292
- );
293
- }
294
- if (!shouldProceed) {
295
- console.log("Push operation cancelled.");
296
- process.exit(0);
297
- }
298
- try {
299
- const response = await fetch(`${process.env.AXIOM_URL}/v1/prompts`, {
300
- method: "POST",
301
- headers: {
302
- Authorization: `Bearer ${process.env.AXIOM_TOKEN}`,
303
- "Content-Type": "application/json",
304
- "x-axiom-client": "axiom-ai-cli",
305
- "x-axiom-check": "good"
306
- },
307
- body: JSON.stringify({
308
- ...content,
309
- tags: options.yes ? ["production"] : []
310
- })
311
- });
312
- if (!response.ok) {
313
- try {
314
- const errorText = await response.clone().json();
315
- console.error(`Failed to fetch prompt: ${response.status} ${response.statusText}`);
316
- console.error(JSON.stringify(errorText, null, 2));
317
- process.exit(1);
318
- } catch (_error) {
319
- const errorText = await response.clone().text();
320
- console.error(`Failed to fetch prompt: ${response.status} ${response.statusText}`);
321
- console.error(errorText);
322
- process.exit(1);
323
- }
324
- }
325
- const apiResponse = await response.json();
326
- console.log(
327
- `Successfully pushed prompt: ${apiResponse.prompt.name} (${apiResponse.prompt.slug})`
328
- );
329
- console.log(`Version: ${apiResponse.version.version}`);
330
- const updatedTsContent = generatePromptFileFromApiResponse(apiResponse);
331
- await import_promises2.default.writeFile(filePath, updatedTsContent, "utf-8");
332
- console.log(`Successfully updated ${filePath}`);
333
- } catch (error) {
334
- console.error("Failed to push prompt:", error);
335
- process.exit(1);
336
- }
337
- });
338
- program2.addCommand(push);
339
- };
340
-
341
- // src/cli/commands/pull.command.ts
342
- var import_commander2 = require("commander");
343
- var fs3 = __toESM(require("fs/promises"), 1);
344
- var path2 = __toESM(require("path"), 1);
345
- var loadPullCommand = (program2) => {
346
- const pull = new import_commander2.Command("pull").description("Pull a version of an object").argument(
347
- "<slug>",
348
- "The object to pull, could be a prompt, en eval, a monitor, a dashboard, etc."
349
- ).option("--version <version>", "The version to pull, default: latest", "latest").option("--output <path>", "Output file path (optional, defaults to <slug>.prompt.ts)").action(async (slug, options) => {
350
- try {
351
- console.log(`Pulling prompt: ${slug} (version: ${options.version})`);
352
- const url = `${process.env.AXIOM_URL}/v1/prompts/${slug}`;
353
- const response = await fetch(url, {
354
- method: "GET",
355
- headers: {
356
- Authorization: `Bearer ${process.env.AXIOM_TOKEN}`,
357
- "Content-Type": "application/json",
358
- "x-axiom-client": "axiom-ai-cli",
359
- "x-axiom-check": "good"
360
- }
361
- });
362
- if (!response.ok) {
363
- try {
364
- const errorText = await response.clone().json();
365
- console.error(`Failed to fetch prompt: ${response.status} ${response.statusText}`);
366
- console.error(JSON.stringify(errorText, null, 2));
367
- process.exit(1);
368
- } catch (_error) {
369
- const errorText = await response.clone().text();
370
- console.error(`Failed to fetch prompt: ${response.status} ${response.statusText}`);
371
- console.error(errorText);
372
- process.exit(1);
373
- }
374
- }
375
- const apiResponse = await response.json();
376
- const tsContent = generatePromptFileFromApiResponse(apiResponse);
377
- const outputPath = options.output || `${slug}.prompt.ts`;
378
- const fullPath = path2.resolve(outputPath);
379
- await fs3.writeFile(fullPath, tsContent, "utf-8");
380
- console.log(`Successfully generated prompt file: ${fullPath}`);
381
- console.log(`Prompt: ${apiResponse.prompt.name} (${apiResponse.prompt.slug})`);
382
- console.log(`Version: ${apiResponse.version.version}`);
383
- } catch (error) {
384
- console.error("Failed to pull prompt:", error);
385
- process.exit(1);
386
- }
387
- });
388
- program2.addCommand(pull);
389
- };
39
+ var import_commander3 = require("commander");
390
40
 
391
41
  // src/cli/commands/eval.command.ts
392
- var import_commander3 = require("commander");
42
+ var import_commander = require("commander");
393
43
  var import_nanoid = require("nanoid");
394
44
 
395
45
  // ../../node_modules/.pnpm/tinyrainbow@2.0.0/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.js
@@ -477,7 +127,11 @@ var r = process.env.FORCE_TTY !== void 0 || (0, import_tty.isatty)(1);
477
127
  var u = p(r);
478
128
 
479
129
  // src/evals/run-vitest.ts
480
- var import_node_path3 = __toESM(require("path"), 1);
130
+ var import_node_url = require("url");
131
+ var import_node_path = require("path");
132
+ var import_node_fs = require("fs");
133
+ var import_node_os = require("os");
134
+ var import_node_path2 = __toESM(require("path"), 1);
481
135
  var import_node = require("vitest/node");
482
136
 
483
137
  // src/evals/context/storage.ts
@@ -604,7 +258,10 @@ var ATTR_EVAL_TYPE = "eval.type";
604
258
  var ATTR_EVAL_TAGS = "eval.tags";
605
259
  var ATTR_EVAL_BASELINE_ID = "eval.baseline.id";
606
260
  var ATTR_EVAL_BASELINE_NAME = "eval.baseline.name";
261
+ var ATTR_EVAL_BASELINE_VERSION = "eval.baseline.version";
607
262
  var ATTR_EVAL_METADATA = "eval.metadata";
263
+ var ATTR_EVAL_CAPABILITY_NAME = "eval.capability.name";
264
+ var ATTR_EVAL_STEP_NAME = "eval.step.name";
608
265
  var ATTR_EVAL_COLLECTION_ID = "eval.collection.id";
609
266
  var ATTR_EVAL_COLLECTION_SIZE = "eval.collection.size";
610
267
  var ATTR_EVAL_COLLECTION_NAME = "eval.collection.name";
@@ -819,7 +476,14 @@ var Attr = {
819
476
  Type: ATTR_EVAL_TYPE,
820
477
  Baseline: {
821
478
  ID: ATTR_EVAL_BASELINE_ID,
822
- Name: ATTR_EVAL_BASELINE_NAME
479
+ Name: ATTR_EVAL_BASELINE_NAME,
480
+ Version: ATTR_EVAL_BASELINE_VERSION
481
+ },
482
+ Capability: {
483
+ Name: ATTR_EVAL_CAPABILITY_NAME
484
+ },
485
+ Step: {
486
+ Name: ATTR_EVAL_STEP_NAME
823
487
  },
824
488
  Tags: ATTR_EVAL_TAGS,
825
489
  Metadata: ATTR_EVAL_METADATA,
@@ -879,7 +543,7 @@ var import_api4 = require("@opentelemetry/api");
879
543
  // package.json
880
544
  var package_default = {
881
545
  name: "axiom",
882
- version: "0.25.0",
546
+ version: "0.27.0",
883
547
  type: "module",
884
548
  author: "Axiom, Inc.",
885
549
  contributors: [
@@ -962,7 +626,8 @@ var package_default = {
962
626
  commander: "^14.0.0",
963
627
  defu: "^6.1.4",
964
628
  handlebars: "^4.7.8",
965
- nanoid: "^5.1.5"
629
+ nanoid: "^5.1.5",
630
+ open: "^10.1.0"
966
631
  },
967
632
  peerDependencies: {
968
633
  "@opentelemetry/api": "^1.9.0",
@@ -970,11 +635,11 @@ var package_default = {
970
635
  },
971
636
  devDependencies: {
972
637
  "@ai-sdk/anthropicv1": "npm:@ai-sdk/anthropic@^1.2.12",
973
- "@ai-sdk/anthropicv2": "npm:@ai-sdk/anthropic@2.0.0-beta.9",
974
- "@ai-sdk/openaiv1": "npm:@ai-sdk/openai@^1.3.23",
975
- "@ai-sdk/openaiv2": "npm:@ai-sdk/openai@2.0.0-beta.12",
638
+ "@ai-sdk/anthropicv2": "npm:@ai-sdk/anthropic@^2.0.44",
639
+ "@ai-sdk/openaiv1": "npm:@ai-sdk/openai@^1.3.24",
640
+ "@ai-sdk/openaiv2": "npm:@ai-sdk/openai@^2.0.67",
976
641
  "@ai-sdk/providerv1": "npm:@ai-sdk/provider@^1.1.3",
977
- "@ai-sdk/providerv2": "npm:@ai-sdk/provider@2.0.0-beta.1",
642
+ "@ai-sdk/providerv2": "npm:@ai-sdk/provider@^2.0.0",
978
643
  "@opentelemetry/api": "^1.9.0",
979
644
  "@opentelemetry/core": "^2.0.1",
980
645
  "@opentelemetry/sdk-trace-base": "^2.0.1",
@@ -983,9 +648,10 @@ var package_default = {
983
648
  "@types/node": "^22.15.29",
984
649
  "@vitest/coverage-v8": "^3.2.4",
985
650
  aiv4: "npm:ai@^4.3.19",
986
- aiv5: "npm:ai@^5.0.0",
651
+ aiv5: "npm:ai@^5.0.93",
987
652
  esbuild: "^0.25.8",
988
653
  eslint: "catalog:",
654
+ msw: "^2.12.2",
989
655
  prettier: "catalog:",
990
656
  tinyrainbow: "^2.0.0",
991
657
  tsup: "catalog:",
@@ -1095,17 +761,36 @@ function resolveAxiomConnection(config) {
1095
761
  url: config.eval.url,
1096
762
  consoleEndpointUrl,
1097
763
  token: config.eval.token,
1098
- dataset: config.eval.dataset
764
+ dataset: config.eval.dataset,
765
+ orgId: config.eval.orgId
1099
766
  };
1100
767
  }
1101
768
 
769
+ // src/cli/errors.ts
770
+ var AxiomCLIError = class extends Error {
771
+ constructor(message) {
772
+ super(message);
773
+ this.name = "AxiomCLIError";
774
+ }
775
+ };
776
+ function errorToString(error) {
777
+ if (typeof error === "string") {
778
+ return error;
779
+ }
780
+ if (error instanceof Error) {
781
+ return error.message;
782
+ }
783
+ return JSON.stringify(error);
784
+ }
785
+
1102
786
  // src/evals/eval.service.ts
1103
787
  var findEvaluationCases = async (evalId, config) => {
1104
- const { dataset, url, token } = resolveAxiomConnection(config);
788
+ const { dataset, url, token, orgId } = resolveAxiomConnection(config);
1105
789
  const apl = `['${dataset}'] | where trace_id == "${evalId}" | order by _time`;
1106
790
  const headers = new Headers({
1107
791
  Authorization: `Bearer ${token}`,
1108
- "Content-Type": "application/json"
792
+ "Content-Type": "application/json",
793
+ ...orgId ? { "X-AXIOM-ORG-ID": orgId } : {}
1109
794
  });
1110
795
  const resp = await fetch(`${url}/v1/datasets/_apl?format=legacy`, {
1111
796
  headers,
@@ -1698,23 +1383,6 @@ function printFinalReport({
1698
1383
  }
1699
1384
  }
1700
1385
 
1701
- // src/cli/errors.ts
1702
- var AxiomCLIError = class extends Error {
1703
- constructor(message) {
1704
- super(message);
1705
- this.name = "AxiomCLIError";
1706
- }
1707
- };
1708
- function errorToString(error) {
1709
- if (typeof error === "string") {
1710
- return error;
1711
- }
1712
- if (error instanceof Error) {
1713
- return error.message;
1714
- }
1715
- return JSON.stringify(error);
1716
- }
1717
-
1718
1386
  // src/evals/reporter.ts
1719
1387
  var AxiomReporter = class {
1720
1388
  constructor() {
@@ -1869,13 +1537,344 @@ var import_api10 = require("@opentelemetry/api");
1869
1537
  var import_c12 = require("c12");
1870
1538
  var import_defu = require("defu");
1871
1539
 
1540
+ // src/cli/auth/config.ts
1541
+ var import_fs = require("fs");
1542
+ var import_path = __toESM(require("path"), 1);
1543
+ var import_os = __toESM(require("os"), 1);
1544
+ var CONFIG_FILENAME = "config.json";
1545
+ var CONFIG_DIR_NAME = "axiom";
1546
+ function getConfigDir() {
1547
+ const platform = process.platform;
1548
+ const homeDir = import_os.default.homedir();
1549
+ const xdgConfigHome = process.env.XDG_CONFIG_HOME;
1550
+ if (xdgConfigHome) {
1551
+ return import_path.default.join(xdgConfigHome, CONFIG_DIR_NAME);
1552
+ }
1553
+ if (platform === "win32") {
1554
+ const appData = process.env.APPDATA;
1555
+ if (appData) {
1556
+ return import_path.default.join(appData, CONFIG_DIR_NAME);
1557
+ }
1558
+ return import_path.default.join(homeDir, "AppData", "Roaming", CONFIG_DIR_NAME);
1559
+ }
1560
+ return import_path.default.join(homeDir, ".config", CONFIG_DIR_NAME);
1561
+ }
1562
+ function getGlobalConfigPath() {
1563
+ return import_path.default.join(getConfigDir(), CONFIG_FILENAME);
1564
+ }
1565
+ async function loadGlobalConfig() {
1566
+ const configPath = getGlobalConfigPath();
1567
+ try {
1568
+ const content = await import_fs.promises.readFile(configPath, "utf-8");
1569
+ return JSON.parse(content);
1570
+ } catch (error) {
1571
+ if (error.code === "ENOENT") {
1572
+ return { profiles: {} };
1573
+ }
1574
+ throw error;
1575
+ }
1576
+ }
1577
+ async function saveGlobalConfig(config) {
1578
+ const configPath = getGlobalConfigPath();
1579
+ const configDir = import_path.default.dirname(configPath);
1580
+ const content = JSON.stringify(config, null, 2);
1581
+ await import_fs.promises.mkdir(configDir, { recursive: true, mode: 448 });
1582
+ await import_fs.promises.writeFile(configPath, content, "utf-8");
1583
+ await import_fs.promises.chmod(configPath, 384);
1584
+ }
1585
+ function getActiveProfile(config) {
1586
+ const profileName = config.active_profile;
1587
+ if (!profileName) return null;
1588
+ const profile = config.profiles[profileName];
1589
+ if (!profile) return null;
1590
+ return profile;
1591
+ }
1592
+
1593
+ // src/cli/auth/oauth.ts
1594
+ var import_crypto = require("crypto");
1595
+ var OAUTH_CLIENT_ID = "264d906a404efc209b027f6595e6b616";
1596
+ var OAUTH_AUTH_PATH = "/oauth/authorize";
1597
+ var OAUTH_TOKEN_PATH = "/oauth/token";
1598
+ var OAuth = class {
1599
+ constructor(oauthBaseUrl) {
1600
+ this.oauthBaseUrl = oauthBaseUrl;
1601
+ }
1602
+ static generateCodeVerifier() {
1603
+ return (0, import_crypto.randomBytes)(32).toString("base64url");
1604
+ }
1605
+ static generateCodeChallenge(verifier) {
1606
+ return (0, import_crypto.createHash)("sha256").update(verifier).digest("base64url");
1607
+ }
1608
+ static generateState() {
1609
+ return (0, import_crypto.randomBytes)(16).toString("hex");
1610
+ }
1611
+ buildAuthUrl(params) {
1612
+ const url = new URL(OAUTH_AUTH_PATH, this.oauthBaseUrl);
1613
+ url.searchParams.set("client_id", OAUTH_CLIENT_ID);
1614
+ url.searchParams.set("redirect_uri", params.redirectUri);
1615
+ url.searchParams.set("response_type", "code");
1616
+ url.searchParams.set("state", params.state);
1617
+ url.searchParams.set("code_challenge", params.codeChallenge);
1618
+ url.searchParams.set("code_challenge_method", "S256");
1619
+ url.searchParams.set("scope", "*");
1620
+ return url.toString();
1621
+ }
1622
+ async exchangeCodeForToken(params) {
1623
+ const tokenUrl = new URL(OAUTH_TOKEN_PATH, this.oauthBaseUrl);
1624
+ const body = new URLSearchParams({
1625
+ grant_type: "authorization_code",
1626
+ client_id: OAUTH_CLIENT_ID,
1627
+ code: params.code,
1628
+ redirect_uri: params.redirectUri,
1629
+ code_verifier: params.codeVerifier
1630
+ });
1631
+ const response = await fetch(tokenUrl.toString(), {
1632
+ method: "POST",
1633
+ headers: {
1634
+ "Content-Type": "application/x-www-form-urlencoded"
1635
+ },
1636
+ body: body.toString()
1637
+ });
1638
+ if (!response.ok) {
1639
+ const errorText = await response.text();
1640
+ throw new Error(`Token exchange failed: ${response.status} ${errorText}`);
1641
+ }
1642
+ const data = await response.json();
1643
+ return data.access_token;
1644
+ }
1645
+ };
1646
+
1647
+ // src/cli/auth/api.ts
1648
+ async function fetchOrganizations(token, apiBaseUrl) {
1649
+ const response = await fetch(`${apiBaseUrl}/v2/orgs`, {
1650
+ headers: {
1651
+ Authorization: `Bearer ${token}`,
1652
+ "Content-Type": "application/json"
1653
+ }
1654
+ });
1655
+ if (!response.ok) {
1656
+ throw new AxiomCLIError(
1657
+ `Failed to fetch organizations: ${response.status} ${response.statusText}`
1658
+ );
1659
+ }
1660
+ const data = await response.json();
1661
+ return data;
1662
+ }
1663
+ async function verifyToken(token, orgId, apiBaseUrl) {
1664
+ const response = await fetch(`${apiBaseUrl}/v2/user`, {
1665
+ headers: {
1666
+ Authorization: `Bearer ${token}`,
1667
+ "X-Axiom-Org-Id": orgId,
1668
+ "Content-Type": "application/json"
1669
+ }
1670
+ });
1671
+ return response.ok;
1672
+ }
1673
+
1674
+ // src/cli/auth/callback-server.ts
1675
+ var import_http = __toESM(require("http"), 1);
1676
+ function escapeHtml(text) {
1677
+ const map = {
1678
+ "&": "&amp;",
1679
+ "<": "&lt;",
1680
+ ">": "&gt;",
1681
+ '"': "&quot;",
1682
+ "'": "&#039;",
1683
+ "`": "&#96;"
1684
+ };
1685
+ return text.replace(/[&<>"']/g, (m2) => map[m2] || m2);
1686
+ }
1687
+ var SVG_LOGO = `<svg width="124" height="24" viewBox="0 0 124 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="logo">
1688
+ <path d="M42.9919 16.8116H36.3696L35.5537 19.1572C35.4209 19.539 34.9714 19.8513 34.5548 19.8513H30.9779C30.5614 19.8513 30.3459 19.5449 30.499 19.1703L36.9816 3.31644C37.1346 2.9419 37.6009 2.63546 38.0174 2.63546H41.3718C41.7883 2.63546 42.2541 2.94207 42.4067 3.3168L48.8634 19.17C49.016 19.5447 48.8 19.8513 48.3835 19.8513H44.8067C44.3901 19.8513 43.9406 19.539 43.8078 19.1572L42.9919 16.8116ZM41.8232 13.4223L39.6807 7.18148L37.5383 13.4223H41.8232ZM64.1105 19.8513C63.694 19.8513 63.1767 19.5694 62.9611 19.2247L59.9029 14.3369L56.8447 19.2247C56.6291 19.5694 56.1119 19.8513 55.6953 19.8513H51.3392C50.9227 19.8513 50.7754 19.5801 51.0119 19.2486L56.8978 11.0013L51.5315 3.24414C51.2999 2.90937 51.4513 2.63546 51.8679 2.63546H55.89C56.3066 2.63546 56.8268 2.91563 57.0461 3.25807L59.9029 7.71959L62.7343 3.25955C62.9522 2.9163 63.4713 2.63546 63.8879 2.63546H67.938C68.3545 2.63546 68.5048 2.90868 68.272 3.24261L62.8801 10.9743L68.7935 19.2489C69.0303 19.5802 68.8832 19.8513 68.4666 19.8513H64.1105ZM76.525 19.119C76.525 19.5218 76.1841 19.8513 75.7675 19.8513H72.5522C72.1356 19.8513 71.7947 19.5218 71.7947 19.119V3.36771C71.7947 2.96498 72.1356 2.63546 72.5522 2.63546H75.7675C76.1841 2.63546 76.525 2.96498 76.525 3.36771V19.119ZM79.3736 11.1896C79.3736 6.18625 83.2688 2.15134 89.2511 2.15134C95.2334 2.15134 99.1289 6.18625 99.1289 11.1896C99.1289 16.2199 95.2334 20.2548 89.2511 20.2548C83.2688 20.2548 79.3736 16.2199 79.3736 11.1896ZM94.2873 11.1896C94.2873 8.58038 92.3953 6.21307 89.2511 6.21307C86.1349 6.21307 84.2149 8.58038 84.2149 11.1896C84.2149 13.7989 86.1349 16.2199 89.2511 16.2199C92.3675 16.2199 94.2873 13.7989 94.2873 11.1896ZM119.318 19.8513C118.902 19.8513 118.517 19.5245 118.463 19.1251L117.225 9.89847L113.65 19.1652C113.505 19.5426 113.045 19.8513 112.628 19.8513H111.055C110.638 19.8513 110.179 19.5423 110.034 19.1645L106.457 9.81766L105.218 19.125C105.165 19.5245 104.781 19.8513 104.364 19.8513H101.344C100.927 19.8513 100.633 19.5249 100.69 19.1259L102.932 3.3609C102.988 2.96191 103.376 2.63546 103.792 2.63546H106.59C107.007 2.63546 107.469 2.94351 107.617 3.32002L111.827 14.041L116.064 3.31949C116.213 2.94327 116.676 2.63546 117.092 2.63546H119.89C120.307 2.63546 120.694 2.96191 120.751 3.3609L122.993 19.1259C123.05 19.5249 122.755 19.8513 122.339 19.8513H119.318ZM23.9616 15.6531L18.8054 6.97021C18.5689 6.57115 17.9863 6.24465 17.5106 6.24465H14.2915C13.5433 6.24465 13.2365 5.73171 13.6097 5.1048L15.375 2.13986C15.5151 1.90455 15.5148 1.61487 15.3743 1.37981C15.2337 1.14476 14.9741 1 14.6933 1H10.2025C9.72681 1 9.14291 1.32577 8.90491 1.72395L0.17865 16.3217C-0.0594434 16.7199 -0.0595348 17.3715 0.178285 17.7698L2.42362 21.5297C2.79777 22.1561 3.41129 22.1569 3.78699 21.5313L5.54143 18.6103C5.91722 17.9847 6.53065 17.9854 6.9048 18.6119L8.49538 21.2754C8.7332 21.6737 9.317 21.9995 9.79273 21.9995H20.1698C20.6455 21.9995 21.2293 21.6737 21.4672 21.2754L23.959 17.1028C24.1968 16.7045 24.198 16.0521 23.9616 15.6531ZM16.9981 15.2352C17.3699 15.8629 17.0619 16.3765 16.3136 16.3765H8.24192C7.49372 16.3765 7.1876 15.864 7.56175 15.2375L11.6007 8.47417C11.9748 7.84772 12.5869 7.84774 12.9611 8.47421L16.9981 15.2352Z" fill="#121224"/>
1689
+ </svg>`;
1690
+ function renderCallbackPage(error) {
1691
+ const errorClass = error ? ' class="error"' : "";
1692
+ const errorMessage = error ? escapeHtml(error) : "";
1693
+ return `<!doctype html>
1694
+ <html lang="en">
1695
+ <head>
1696
+ <meta charset="utf-8">
1697
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
1698
+ <title>Axiom</title>
1699
+ <link rel="icon" href="https://app.axiom.co/static/favicon.ico">
1700
+ <meta name="description" content="Axiom CLI">
1701
+ <meta name="viewport" content="width=device-width,initial-scale=1">
1702
+ <style>
1703
+ html,
1704
+ body,
1705
+ .root {
1706
+ width: 100%;
1707
+ height: 100%;
1708
+ text-rendering: optimizeLegibility;
1709
+ -webkit-font-smoothing: antialiased;
1710
+ }
1711
+ body {
1712
+ color: #334155;
1713
+ font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
1714
+ font-size: 14px;
1715
+ font-weight: 500;
1716
+ font-variant: tabular-nums;
1717
+ line-height: 1.5;
1718
+ background-color: #fff;
1719
+ font-feature-settings: "tnum";
1720
+ margin: 0;
1721
+ }
1722
+ h1,
1723
+ h2,
1724
+ h3,
1725
+ h4,
1726
+ h5,
1727
+ h6 {
1728
+ margin-top: 0;
1729
+ margin-bottom: .5em;
1730
+ font-weight: 500;
1731
+ }
1732
+ p {
1733
+ margin-top: 0;
1734
+ margin-bottom: 1em;
1735
+ }
1736
+ h2 {
1737
+ font-size: 16px;
1738
+ font-weight: 600;
1739
+ }
1740
+ .root {
1741
+ display: flex;
1742
+ align-items: center;
1743
+ justify-content: center;
1744
+ }
1745
+ .logo {
1746
+ width: 92px;
1747
+ float: left;
1748
+ position: absolute;
1749
+ top: 16px;
1750
+ left: 16px;
1751
+ }
1752
+ .center p {
1753
+ padding: 8px 0;
1754
+ }
1755
+ .error .center {
1756
+ color: #bf0e08;
1757
+ }
1758
+ </style>
1759
+ </head>
1760
+ <body${errorClass}>
1761
+ <div class="root">
1762
+ <a class="" target="_blank" rel="noopener noreferrer" href="https://axiom.co">
1763
+ ${SVG_LOGO}
1764
+ </svg>
1765
+ </a>
1766
+ <div class="center">
1767
+ ${error ? `<h2 id="msg">Login failed</h2>
1768
+ <p id="details">${errorMessage}</p>` : `<h2 id="msg">Login successful</h2>
1769
+ <p id="details">You can close this page and return to your CLI.</p>`}
1770
+ </div>
1771
+ </div>
1772
+ <script>
1773
+ window.history.replaceState({}, '', \`\${window.location.pathname}\`);
1774
+ </script>
1775
+ </body>
1776
+ </html>`;
1777
+ }
1778
+ async function startCallbackServer() {
1779
+ return new Promise((resolve3) => {
1780
+ const server = import_http.default.createServer();
1781
+ server.listen(0, "127.0.0.1", () => {
1782
+ const address = server.address();
1783
+ resolve3({
1784
+ server,
1785
+ port: address.port,
1786
+ url: `http://127.0.0.1:${address.port}`
1787
+ });
1788
+ });
1789
+ });
1790
+ }
1791
+ async function waitForCallback(server, expectedState) {
1792
+ return new Promise((resolve3, reject) => {
1793
+ const timeout = setTimeout(
1794
+ () => {
1795
+ server.close();
1796
+ reject(new Error("Authentication timeout after 5 minutes"));
1797
+ },
1798
+ 5 * 60 * 1e3
1799
+ );
1800
+ server.on("request", (req, res) => {
1801
+ const url = new URL(req.url || "", `http://${req.headers.host}`);
1802
+ const code = url.searchParams.get("code");
1803
+ const state = url.searchParams.get("state");
1804
+ const error = url.searchParams.get("error");
1805
+ const errorDescription = url.searchParams.get("error_description");
1806
+ if (error) {
1807
+ const errorMsg = errorDescription || error;
1808
+ res.writeHead(400, { "Content-Type": "text/html" });
1809
+ res.end(renderCallbackPage(errorMsg));
1810
+ clearTimeout(timeout);
1811
+ server.close();
1812
+ reject(new Error(`OAuth error: ${errorMsg}`));
1813
+ return;
1814
+ }
1815
+ if (!code || !state) {
1816
+ res.writeHead(400, { "Content-Type": "text/html" });
1817
+ res.end(renderCallbackPage("Missing code or state parameter"));
1818
+ return;
1819
+ }
1820
+ if (state !== expectedState) {
1821
+ res.writeHead(400, { "Content-Type": "text/html" });
1822
+ res.end(renderCallbackPage("Invalid state parameter (CSRF protection)"));
1823
+ clearTimeout(timeout);
1824
+ server.close();
1825
+ reject(new Error("Invalid state parameter"));
1826
+ return;
1827
+ }
1828
+ res.writeHead(200, { "Content-Type": "text/html" });
1829
+ res.end(renderCallbackPage());
1830
+ clearTimeout(timeout);
1831
+ server.close();
1832
+ resolve3({ code });
1833
+ });
1834
+ });
1835
+ }
1836
+
1837
+ // src/cli/auth/global-auth.ts
1838
+ var authContext = null;
1839
+ function getAuthContext() {
1840
+ return authContext;
1841
+ }
1842
+ async function setupGlobalAuth() {
1843
+ const config = await loadGlobalConfig();
1844
+ const profile = getActiveProfile(config);
1845
+ if (profile) {
1846
+ authContext = {
1847
+ token: profile.token,
1848
+ url: profile.url,
1849
+ orgId: profile.org_id
1850
+ };
1851
+ }
1852
+ return authContext;
1853
+ }
1854
+
1872
1855
  // src/config/index.ts
1873
1856
  var DEFAULT_EVAL_INCLUDE = ["**/*.eval.{ts,js,mts,mjs,cts,cjs}"];
1874
1857
  function createPartialDefaults() {
1858
+ let token;
1859
+ let url;
1860
+ let orgId;
1861
+ try {
1862
+ const authContext2 = getAuthContext();
1863
+ if (authContext2) {
1864
+ token = authContext2.token;
1865
+ url = authContext2.url;
1866
+ orgId = authContext2.orgId;
1867
+ }
1868
+ } catch {
1869
+ }
1870
+ token = token || process.env.AXIOM_TOKEN;
1871
+ url = url || process.env.AXIOM_URL;
1872
+ orgId = orgId || process.env.AXIOM_ORG_ID;
1875
1873
  return {
1876
1874
  eval: {
1877
- url: process.env.AXIOM_URL || "https://api.axiom.co",
1878
- token: process.env.AXIOM_TOKEN,
1875
+ url: url || "https://api.axiom.co",
1876
+ orgId,
1877
+ token,
1879
1878
  dataset: process.env.AXIOM_DATASET,
1880
1879
  instrumentation: null,
1881
1880
  include: [...DEFAULT_EVAL_INCLUDE],
@@ -1978,7 +1977,8 @@ async function runInstrumentationHook(hook, options) {
1978
1977
  }
1979
1978
  function setupEvalProvider(connection) {
1980
1979
  const headers = {
1981
- "X-Axiom-Dataset": connection.dataset
1980
+ "X-Axiom-Dataset": connection.dataset,
1981
+ ...connection.orgId ? { "X-AXIOM-ORG-ID": connection.orgId } : {}
1982
1982
  };
1983
1983
  if (connection.token) {
1984
1984
  headers.Authorization = `Bearer ${connection.token}`;
@@ -1998,11 +1998,11 @@ function setupEvalProvider(connection) {
1998
1998
  axiomProvider = new import_sdk_trace_node.NodeTracerProvider({
1999
1999
  resource: (0, import_resources.resourceFromAttributes)({
2000
2000
  ["service.name"]: "axiom",
2001
- ["service.version"]: "0.25.0"
2001
+ ["service.version"]: "0.27.0"
2002
2002
  }),
2003
2003
  spanProcessors: [processor]
2004
2004
  });
2005
- axiomTracer = axiomProvider.getTracer("axiom", "0.25.0");
2005
+ axiomTracer = axiomProvider.getTracer("axiom", "0.27.0");
2006
2006
  }
2007
2007
  async function initInstrumentation(config) {
2008
2008
  if (initialized) {
@@ -2014,7 +2014,7 @@ async function initInstrumentation(config) {
2014
2014
  }
2015
2015
  initializationPromise = (async () => {
2016
2016
  if (!config.enabled) {
2017
- axiomTracer = import_api10.trace.getTracer("axiom", "0.25.0");
2017
+ axiomTracer = import_api10.trace.getTracer("axiom", "0.27.0");
2018
2018
  initialized = true;
2019
2019
  return;
2020
2020
  }
@@ -2026,7 +2026,8 @@ async function initInstrumentation(config) {
2026
2026
  hookResult = await runInstrumentationHook(hook, {
2027
2027
  dataset: connection.dataset,
2028
2028
  token: connection.token,
2029
- url: connection.url
2029
+ url: connection.url,
2030
+ orgId: connection.orgId
2030
2031
  });
2031
2032
  userProvider = hookResult?.provider ?? userProvider;
2032
2033
  }
@@ -2077,6 +2078,9 @@ var flush = async () => {
2077
2078
  };
2078
2079
 
2079
2080
  // src/evals/run-vitest.ts
2081
+ var import_meta2 = {};
2082
+ var __filename = (0, import_node_url.fileURLToPath)(import_meta2.url);
2083
+ var __dirname = (0, import_node_path.dirname)(__filename);
2080
2084
  var printCollectedEvals = (result, rootDir) => {
2081
2085
  if (!result.testModules || result.testModules.length === 0) {
2082
2086
  console.log(u.yellow("\nNo evaluations found\n"));
@@ -2086,7 +2090,7 @@ var printCollectedEvals = (result, rootDir) => {
2086
2090
  let totalEvals = 0;
2087
2091
  let totalCases = 0;
2088
2092
  for (const module2 of result.testModules) {
2089
- const relativePath = import_node_path3.default.relative(rootDir, module2.moduleId);
2093
+ const relativePath = import_node_path2.default.relative(rootDir, module2.moduleId);
2090
2094
  for (const suite of module2.children.suites()) {
2091
2095
  totalEvals++;
2092
2096
  const caseCount = suite.children.size;
@@ -2116,6 +2120,16 @@ var runVitest = async (dir, opts) => {
2116
2120
  if (opts.debug) {
2117
2121
  console.log(u.bgWhite(u.blackBright(" Debug mode enabled ")));
2118
2122
  }
2123
+ const tmpDir = (0, import_node_path.join)((0, import_node_os.tmpdir)(), "axiom-eval", opts.runId);
2124
+ (0, import_node_fs.mkdirSync)(tmpDir, { recursive: true });
2125
+ const nameRegistryFile = (0, import_node_path.join)(tmpDir, "names.jsonl");
2126
+ const abortFile = (0, import_node_path.join)(tmpDir, "abort.txt");
2127
+ (0, import_node_fs.writeFileSync)(nameRegistryFile, "", "utf8");
2128
+ if ((0, import_node_fs.existsSync)(abortFile)) {
2129
+ (0, import_node_fs.unlinkSync)(abortFile);
2130
+ }
2131
+ process.env.AXIOM_NAME_REGISTRY_FILE = nameRegistryFile;
2132
+ process.env.AXIOM_ABORT_FILE = abortFile;
2119
2133
  if (opts.list) {
2120
2134
  console.log(u.bgWhite(u.blackBright(" List mode ")));
2121
2135
  }
@@ -2135,6 +2149,7 @@ var runVitest = async (dir, opts) => {
2135
2149
  disableConsoleIntercept: true,
2136
2150
  testTimeout: opts.config?.eval?.timeoutMs || 6e4,
2137
2151
  globals: true,
2152
+ runner: (0, import_node_path.resolve)(__dirname, "evals", "custom-runner.js"),
2138
2153
  provide: {
2139
2154
  baseline: opts.baseline,
2140
2155
  debug: opts.debug,
@@ -2151,6 +2166,12 @@ var runVitest = async (dir, opts) => {
2151
2166
  process.exit(0);
2152
2167
  }
2153
2168
  await vi.start();
2169
+ if ((0, import_node_fs.existsSync)(abortFile)) {
2170
+ const message = (0, import_node_fs.readFileSync)(abortFile, "utf8");
2171
+ console.error("\n" + message);
2172
+ await vi.close();
2173
+ process.exit(1);
2174
+ }
2154
2175
  const dispose = (0, import_node.registerConsoleShortcuts)(vi, process.stdin, process.stdout);
2155
2176
  if (!vi.shouldKeepServer()) {
2156
2177
  dispose();
@@ -2162,7 +2183,7 @@ var runVitest = async (dir, opts) => {
2162
2183
  };
2163
2184
 
2164
2185
  // src/cli/commands/eval.command.ts
2165
- var import_node_fs = require("fs");
2186
+ var import_node_fs2 = require("fs");
2166
2187
 
2167
2188
  // src/context.ts
2168
2189
  function overrideFlags(partial) {
@@ -2207,14 +2228,35 @@ function isGlob(str) {
2207
2228
 
2208
2229
  // src/cli/commands/eval.command.ts
2209
2230
  var createRunId = (0, import_nanoid.customAlphabet)("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", 10);
2231
+ function getDefaultToken(value) {
2232
+ if (typeof value === "string") {
2233
+ return value;
2234
+ }
2235
+ const authContext2 = getAuthContext();
2236
+ return authContext2?.token || process.env.AXIOM_TOKEN;
2237
+ }
2238
+ function getDefaultUrl(value) {
2239
+ if (typeof value === "string") {
2240
+ return value;
2241
+ }
2242
+ const authContext2 = getAuthContext();
2243
+ return authContext2?.url || process.env.AXIOM_URL || "https://api.axiom.co";
2244
+ }
2245
+ function getDefaultOrgId(value) {
2246
+ if (typeof value === "string") {
2247
+ return value;
2248
+ }
2249
+ const authContext2 = getAuthContext();
2250
+ return authContext2?.orgId ?? process.env.AXIOM_ORG_ID;
2251
+ }
2210
2252
  var loadEvalCommand = (program2, flagOverrides = {}) => {
2211
2253
  return program2.addCommand(
2212
- new import_commander3.Command("eval").description("run evals locally").addArgument(
2213
- new import_commander3.Argument("[target]", "file, directory, glob pattern, or eval name").default(
2254
+ new import_commander.Command("eval").description("run evals locally").addArgument(
2255
+ new import_commander.Argument("[target]", "file, directory, glob pattern, or eval name").default(
2214
2256
  ".",
2215
2257
  "any *.eval.ts file in current directory"
2216
2258
  )
2217
- ).option("-w, --watch true", "keep server running and watch for changes", false).option("-t, --token <TOKEN>", "axiom token", process.env.AXIOM_TOKEN).option("-d, --dataset <DATASET>", "axiom dataset name", process.env.AXIOM_DATASET).option("-u, --url <AXIOM URL>", "axiom url", process.env.AXIOM_URL ?? "https://api.axiom.co").option("-b, --baseline <BASELINE ID>", "id of baseline evaluation to compare against").option("--debug", "run locally without sending to Axiom or loading baselines", false).option("--list", "list evaluations and test cases without running them", false).action(async (target, options) => {
2259
+ ).option("-w, --watch true", "keep server running and watch for changes", false).option("-t, --token <TOKEN>", "axiom token", getDefaultToken).option("-d, --dataset <DATASET>", "axiom dataset name", process.env.AXIOM_DATASET).option("-u, --url <AXIOM URL>", "axiom url", getDefaultUrl).option("-o, --org-id <ORG ID>", "axiom organization id", getDefaultOrgId).option("-b, --baseline <BASELINE ID>", "id of baseline evaluation to compare against").option("--debug", "run locally without sending to Axiom or loading baselines", false).option("--list", "list evaluations and test cases without running them", false).action(async (target, options) => {
2218
2260
  try {
2219
2261
  if (options.debug) {
2220
2262
  process.env.AXIOM_DEBUG = "true";
@@ -2223,12 +2265,22 @@ var loadEvalCommand = (program2, flagOverrides = {}) => {
2223
2265
  let exclude;
2224
2266
  let testNamePattern;
2225
2267
  const isGlobPattern = isGlob(target);
2226
- const { config } = await loadConfig(".");
2268
+ const { config: loadedConfig } = await loadConfig(".");
2269
+ const config = {
2270
+ ...loadedConfig,
2271
+ eval: {
2272
+ ...loadedConfig.eval,
2273
+ ...options.token && { token: options.token },
2274
+ ...options.url && { url: options.url },
2275
+ ...options.dataset && { dataset: options.dataset },
2276
+ ...options.orgId && { orgId: options.orgId }
2277
+ }
2278
+ };
2227
2279
  if (isGlobPattern) {
2228
2280
  include = [target];
2229
2281
  } else {
2230
2282
  try {
2231
- const stat = (0, import_node_fs.lstatSync)(target);
2283
+ const stat = (0, import_node_fs2.lstatSync)(target);
2232
2284
  if (stat.isDirectory()) {
2233
2285
  include = config?.eval?.include || [];
2234
2286
  } else {
@@ -2276,10 +2328,360 @@ var loadEvalCommand = (program2, flagOverrides = {}) => {
2276
2328
  );
2277
2329
  };
2278
2330
 
2331
+ // src/cli/commands/auth-login.command.ts
2332
+ var BASE_HOSTNAME = "axiom.co";
2333
+ var getApiUrl = (hostname) => {
2334
+ return `https://api.${hostname}`;
2335
+ };
2336
+ var getOauthUrl = (hostname) => {
2337
+ return `https://login.${hostname}`;
2338
+ };
2339
+ async function promptSelect(message, choices) {
2340
+ console.log(`
2341
+ ${message}`);
2342
+ choices.forEach((choice, index) => {
2343
+ console.log(` ${index + 1}. ${choice.name}`);
2344
+ });
2345
+ const readline = await import("readline");
2346
+ const rl = readline.createInterface({
2347
+ input: process.stdin,
2348
+ output: process.stdout
2349
+ });
2350
+ return new Promise((resolve3) => {
2351
+ const askQuestion = () => {
2352
+ rl.question(`
2353
+ Select (1-${choices.length}): `, (answer) => {
2354
+ const index = parseInt(answer.trim(), 10) - 1;
2355
+ if (index >= 0 && index < choices.length) {
2356
+ rl.close();
2357
+ resolve3(choices[index].value);
2358
+ } else {
2359
+ console.log("Invalid selection. Please try again.");
2360
+ askQuestion();
2361
+ }
2362
+ });
2363
+ };
2364
+ askQuestion();
2365
+ });
2366
+ }
2367
+ async function promptInput(message, defaultValue) {
2368
+ const readline = await import("readline");
2369
+ const rl = readline.createInterface({
2370
+ input: process.stdin,
2371
+ output: process.stdout
2372
+ });
2373
+ return new Promise((resolve3) => {
2374
+ const prompt = defaultValue ? `${message} (${defaultValue}): ` : `${message}: `;
2375
+ rl.question(prompt, (answer) => {
2376
+ rl.close();
2377
+ resolve3(answer.trim() || defaultValue || "");
2378
+ });
2379
+ });
2380
+ }
2381
+ async function openBrowser(url) {
2382
+ const { default: open } = await import("open");
2383
+ await open(url);
2384
+ }
2385
+ async function loginCommand(hostname) {
2386
+ try {
2387
+ console.log("\u{1F510} Starting authentication flow...\n");
2388
+ const codeVerifier = OAuth.generateCodeVerifier();
2389
+ const codeChallenge = OAuth.generateCodeChallenge(codeVerifier);
2390
+ const state = OAuth.generateState();
2391
+ const oauth = new OAuth(getOauthUrl(hostname));
2392
+ const { server, url: redirectUri } = await startCallbackServer();
2393
+ console.log(`\u2713 Started local callback server on ${redirectUri}
2394
+ `);
2395
+ const authUrl = oauth.buildAuthUrl({
2396
+ redirectUri,
2397
+ state,
2398
+ codeChallenge
2399
+ });
2400
+ console.log("Opening browser for authentication...");
2401
+ console.log(`If the browser doesn't open, visit: ${authUrl}
2402
+ `);
2403
+ try {
2404
+ await openBrowser(authUrl);
2405
+ } catch {
2406
+ console.log("Could not open browser automatically.\n");
2407
+ }
2408
+ console.log("Waiting for authentication...");
2409
+ const { code } = await waitForCallback(server, state);
2410
+ console.log("\u2713 Authentication successful, exchanging code for token...\n");
2411
+ const accessToken = await oauth.exchangeCodeForToken({
2412
+ code,
2413
+ redirectUri,
2414
+ codeVerifier
2415
+ });
2416
+ console.log("\u2713 Token received, fetching organizations...\n");
2417
+ const organizations = await fetchOrganizations(accessToken, getApiUrl(hostname));
2418
+ if (organizations.length === 0) {
2419
+ throw new AxiomCLIError("No organizations found for this account");
2420
+ }
2421
+ let selectedOrgId;
2422
+ if (organizations.length === 1) {
2423
+ selectedOrgId = organizations[0].id;
2424
+ console.log(`\u2713 Using organization: ${organizations[0].name}
2425
+ `);
2426
+ } else {
2427
+ selectedOrgId = await promptSelect(
2428
+ "Select an organization:",
2429
+ organizations.map((org) => ({
2430
+ name: `${org.name} (${org.id})`,
2431
+ value: org.id
2432
+ }))
2433
+ );
2434
+ }
2435
+ const selectedOrg = organizations.find((org) => org.id === selectedOrgId);
2436
+ const defaultAlias = selectedOrg.slug || selectedOrg.name.toLowerCase().replace(/\s+/g, "-");
2437
+ const alias = await promptInput("Enter profile alias", defaultAlias);
2438
+ console.log("\n\u2713 Verifying credentials...\n");
2439
+ const isValid = await verifyToken(accessToken, selectedOrgId, getApiUrl(hostname));
2440
+ if (!isValid) {
2441
+ throw new AxiomCLIError("Token verification failed");
2442
+ }
2443
+ const config = await loadGlobalConfig();
2444
+ config.active_profile = alias;
2445
+ config.profiles[alias] = {
2446
+ url: getApiUrl(hostname),
2447
+ token: accessToken,
2448
+ org_id: selectedOrgId
2449
+ };
2450
+ await saveGlobalConfig(config);
2451
+ console.log(`\u2713 Successfully logged in as ${alias}`);
2452
+ console.log(`\u2713 Configuration saved to ${getGlobalConfigPath()}
2453
+ `);
2454
+ } catch (error) {
2455
+ if (error instanceof AxiomCLIError) {
2456
+ throw error;
2457
+ }
2458
+ throw new AxiomCLIError(`Login failed: ${error.message}`);
2459
+ }
2460
+ }
2461
+ function loadAuthLoginCommand(auth, root) {
2462
+ [auth, root].forEach((program2) => {
2463
+ program2.command("login").description("Authenticate with Axiom").option("--hostname <hostname>", "Axiom hostname (default: axiom.co)").action(async (options) => {
2464
+ try {
2465
+ await loginCommand(options.hostname ?? BASE_HOSTNAME);
2466
+ } catch (error) {
2467
+ if (error instanceof AxiomCLIError) {
2468
+ console.error(`
2469
+ \u274C Error: ${error.message}
2470
+ `);
2471
+ } else {
2472
+ console.error(`
2473
+ \u274C Unexpected error: ${error.message}
2474
+ `);
2475
+ }
2476
+ process.exit(1);
2477
+ }
2478
+ });
2479
+ });
2480
+ }
2481
+
2482
+ // src/cli/commands/auth-logout.command.ts
2483
+ async function logoutCommand(alias) {
2484
+ const config = await loadGlobalConfig();
2485
+ const profileToRemove = alias || config.active_profile;
2486
+ if (!profileToRemove) {
2487
+ throw new AxiomCLIError("No active profile. Use --alias to specify which profile to remove.");
2488
+ }
2489
+ if (!config.profiles[profileToRemove]) {
2490
+ throw new AxiomCLIError(`Profile "${profileToRemove}" not found`);
2491
+ }
2492
+ delete config.profiles[profileToRemove];
2493
+ if (config.active_profile === profileToRemove) {
2494
+ const remainingProfiles = Object.keys(config.profiles);
2495
+ config.active_profile = remainingProfiles.length > 0 ? remainingProfiles[0] : void 0;
2496
+ }
2497
+ await saveGlobalConfig(config);
2498
+ console.log(`\u2713 Logged out from ${profileToRemove}`);
2499
+ if (config.active_profile) {
2500
+ console.log(`\u2713 Active profile is now: ${config.active_profile}`);
2501
+ } else {
2502
+ console.log('No active profiles remaining. Run "axiom auth login" to authenticate.');
2503
+ }
2504
+ }
2505
+ function loadAuthLogoutCommand(auth, root) {
2506
+ [auth, root].forEach((program2) => {
2507
+ program2.command("logout").description("Remove authentication credentials").option("-a, --alias <alias>", "Profile alias to remove").action(async (options) => {
2508
+ try {
2509
+ await logoutCommand(options.alias);
2510
+ } catch (error) {
2511
+ if (error instanceof AxiomCLIError) {
2512
+ console.error(`
2513
+ \u274C Error: ${error.message}
2514
+ `);
2515
+ } else {
2516
+ console.error(`
2517
+ \u274C Unexpected error: ${error.message}
2518
+ `);
2519
+ }
2520
+ process.exit(1);
2521
+ }
2522
+ });
2523
+ });
2524
+ }
2525
+
2526
+ // src/cli/commands/auth-status.command.ts
2527
+ async function statusCommand() {
2528
+ const config = await loadGlobalConfig();
2529
+ if (Object.keys(config.profiles).length === 0) {
2530
+ console.log("No authenticated profiles found.");
2531
+ console.log('Run "axiom auth login" to authenticate.');
2532
+ return;
2533
+ }
2534
+ console.log("\nAuthentication Status:\n");
2535
+ for (const [alias, profile] of Object.entries(config.profiles)) {
2536
+ const isActive = config.active_profile === alias;
2537
+ const marker = isActive ? "\u2192" : " ";
2538
+ try {
2539
+ const isValid = await verifyToken(profile.token, profile.org_id, profile.url);
2540
+ const status = isValid ? "\u2713" : "\u2717";
2541
+ const statusText = isValid ? "Valid" : "Invalid";
2542
+ console.log(`${marker} ${status} ${alias}`);
2543
+ console.log(` URL: ${profile.url}`);
2544
+ console.log(` Org ID: ${profile.org_id}`);
2545
+ console.log(` Status: ${statusText}`);
2546
+ if (isActive) {
2547
+ console.log(` (Active)`);
2548
+ }
2549
+ console.log();
2550
+ } catch (error) {
2551
+ console.log(`${marker} \u2717 ${alias}`);
2552
+ console.log(` URL: ${profile.url}`);
2553
+ console.log(` Org ID: ${profile.org_id}`);
2554
+ console.log(` Status: Error - ${error.message}`);
2555
+ if (isActive) {
2556
+ console.log(` (Active)`);
2557
+ }
2558
+ console.log();
2559
+ }
2560
+ }
2561
+ const activeProfile = getActiveProfile(config);
2562
+ if (process.env.AXIOM_TOKEN) {
2563
+ console.log("Note: Using AXIOM_TOKEN environment variable (overrides config file)\n");
2564
+ } else if (activeProfile && config.active_profile) {
2565
+ console.log(`Active profile: ${config.active_profile}
2566
+ `);
2567
+ }
2568
+ }
2569
+ function loadAuthStatusCommand(program2) {
2570
+ program2.command("status").description("Check authentication status for all profiles").action(async () => {
2571
+ try {
2572
+ await statusCommand();
2573
+ } catch (error) {
2574
+ if (error instanceof AxiomCLIError) {
2575
+ console.error(`
2576
+ \u274C Error: ${error.message}
2577
+ `);
2578
+ } else {
2579
+ console.error(`
2580
+ \u274C Unexpected error: ${error.message}
2581
+ `);
2582
+ }
2583
+ process.exit(1);
2584
+ }
2585
+ });
2586
+ }
2587
+
2588
+ // src/cli/commands/auth-switch.command.ts
2589
+ async function promptSelect2(message, choices) {
2590
+ console.log(`
2591
+ ${message}`);
2592
+ choices.forEach((choice, index) => {
2593
+ console.log(` ${index + 1}. ${choice.name}`);
2594
+ });
2595
+ const readline = await import("readline");
2596
+ const rl = readline.createInterface({
2597
+ input: process.stdin,
2598
+ output: process.stdout
2599
+ });
2600
+ return new Promise((resolve3) => {
2601
+ const askQuestion = () => {
2602
+ rl.question(`
2603
+ Select (1-${choices.length}): `, (answer) => {
2604
+ const index = parseInt(answer.trim(), 10) - 1;
2605
+ if (index >= 0 && index < choices.length) {
2606
+ rl.close();
2607
+ resolve3(choices[index].value);
2608
+ } else {
2609
+ console.log("Invalid selection. Please try again.");
2610
+ askQuestion();
2611
+ }
2612
+ });
2613
+ };
2614
+ askQuestion();
2615
+ });
2616
+ }
2617
+ async function switchCommand(alias) {
2618
+ const config = await loadGlobalConfig();
2619
+ if (Object.keys(config.profiles).length === 0) {
2620
+ throw new AxiomCLIError(
2621
+ 'No authenticated profiles found. Run "axiom auth login" to authenticate.'
2622
+ );
2623
+ }
2624
+ let selectedAlias;
2625
+ if (alias) {
2626
+ if (!config.profiles[alias]) {
2627
+ throw new AxiomCLIError(`Profile "${alias}" not found`);
2628
+ }
2629
+ selectedAlias = alias;
2630
+ } else {
2631
+ const profiles = Object.entries(config.profiles).map(([alias2, profile]) => ({
2632
+ name: `${alias2} (${profile.url})`,
2633
+ value: alias2
2634
+ }));
2635
+ if (profiles.length === 1) {
2636
+ selectedAlias = profiles[0].value;
2637
+ console.log(`\u2713 Using profile: ${selectedAlias}
2638
+ `);
2639
+ } else {
2640
+ selectedAlias = await promptSelect2("Select a profile to switch to:", profiles);
2641
+ }
2642
+ }
2643
+ if (config.active_profile === selectedAlias) {
2644
+ console.log(`\u2713 Profile "${selectedAlias}" is already active
2645
+ `);
2646
+ return;
2647
+ }
2648
+ config.active_profile = selectedAlias;
2649
+ await saveGlobalConfig(config);
2650
+ console.log(`\u2713 Switched to profile: ${selectedAlias}
2651
+ `);
2652
+ }
2653
+ function loadAuthSwitchCommand(program2) {
2654
+ program2.command("switch").description("Switch to a different profile").argument("[alias]", "Profile alias to switch to").action(async (alias) => {
2655
+ try {
2656
+ await switchCommand(alias);
2657
+ } catch (error) {
2658
+ if (error instanceof AxiomCLIError) {
2659
+ console.error(`
2660
+ \u274C Error: ${error.message}
2661
+ `);
2662
+ } else {
2663
+ console.error(`
2664
+ \u274C Unexpected error: ${error.message}
2665
+ `);
2666
+ }
2667
+ process.exit(1);
2668
+ }
2669
+ });
2670
+ }
2671
+
2672
+ // src/cli/commands/auth.command.ts
2673
+ function loadAuthCommand(program2) {
2674
+ const auth = program2.command("auth").description("Manage authentication with Axiom");
2675
+ loadAuthLoginCommand(auth, program2);
2676
+ loadAuthLogoutCommand(auth, program2);
2677
+ loadAuthStatusCommand(auth);
2678
+ loadAuthSwitchCommand(auth);
2679
+ }
2680
+
2279
2681
  // src/cli/utils/parse-flag-overrides.ts
2280
2682
  var import_zod5 = require("zod");
2281
- var import_node_fs2 = require("fs");
2282
- var import_node_path4 = require("path");
2683
+ var import_node_fs3 = require("fs");
2684
+ var import_node_path3 = require("path");
2283
2685
  var FLAG_RE = /^--flag\.([^=]+)(?:=(.*))?$/;
2284
2686
  var CONFIG_RE = /^--flags-config(?:=(.*))?$/;
2285
2687
  function ensureNoSpaceSeparatedSyntax(flagName, value, nextToken, flagType) {
@@ -2308,10 +2710,10 @@ function coerceValue(raw) {
2308
2710
  return raw;
2309
2711
  }
2310
2712
  }
2311
- function loadConfigFile(path4) {
2312
- const abs = (0, import_node_path4.resolve)(process.cwd(), path4);
2713
+ function loadConfigFile(path3) {
2714
+ const abs = (0, import_node_path3.resolve)(process.cwd(), path3);
2313
2715
  try {
2314
- const contents = (0, import_node_fs2.readFileSync)(abs, "utf8");
2716
+ const contents = (0, import_node_fs3.readFileSync)(abs, "utf8");
2315
2717
  const parsed = JSON.parse(contents);
2316
2718
  if (typeof parsed !== "object" || Array.isArray(parsed) || parsed === null) {
2317
2719
  console.error(
@@ -2321,7 +2723,7 @@ function loadConfigFile(path4) {
2321
2723
  }
2322
2724
  return parsed;
2323
2725
  } catch (err) {
2324
- console.error(`\u274C Could not read or parse flags config "${path4}": ${err.message}`);
2726
+ console.error(`\u274C Could not read or parse flags config "${path3}": ${err.message}`);
2325
2727
  process.exit(1);
2326
2728
  }
2327
2729
  }
@@ -2380,11 +2782,11 @@ function extractOverrides(argv) {
2380
2782
  var import_env = __toESM(require("@next/env"), 1);
2381
2783
 
2382
2784
  // src/cli/commands/version.command.ts
2383
- var import_commander4 = require("commander");
2785
+ var import_commander2 = require("commander");
2384
2786
  var loadVersionCommand = (program2) => {
2385
2787
  return program2.addCommand(
2386
- new import_commander4.Command("version").description("cli version").action(() => {
2387
- console.log("0.25.0");
2788
+ new import_commander2.Command("version").description("cli version").action(() => {
2789
+ console.log("0.27.0");
2388
2790
  })
2389
2791
  );
2390
2792
  };
@@ -2393,10 +2795,31 @@ var loadVersionCommand = (program2) => {
2393
2795
  var { loadEnvConfig } = import_env.default;
2394
2796
  loadEnvConfig(process.cwd());
2395
2797
  var { cleanedArgv, overrides } = extractOverrides(process.argv.slice(2));
2396
- var program = new import_commander5.Command();
2397
- program.name("axiom").description("Axiom's CLI to manage your objects and run evals").version("0.25.0");
2398
- loadPushCommand(program);
2399
- loadPullCommand(program);
2798
+ var program = new import_commander3.Command();
2799
+ program.name("axiom").description("Axiom's CLI to manage your objects and run evals").version("0.27.0");
2800
+ program.hook("preAction", async (_, actionCommand) => {
2801
+ const commandName = actionCommand.name();
2802
+ const parentCommand = actionCommand.parent;
2803
+ const parentName = parentCommand?.name();
2804
+ if (commandName === "auth" || parentName === "auth" || commandName === "version") {
2805
+ return;
2806
+ }
2807
+ try {
2808
+ await setupGlobalAuth();
2809
+ } catch (error) {
2810
+ if (error instanceof Error) {
2811
+ console.error(`
2812
+ \u274C ${error.message}
2813
+ `);
2814
+ } else {
2815
+ console.error(`
2816
+ \u274C Unexpected error: ${String(error)}
2817
+ `);
2818
+ }
2819
+ process.exit(1);
2820
+ }
2821
+ });
2822
+ loadAuthCommand(program);
2400
2823
  loadEvalCommand(program, overrides);
2401
2824
  loadVersionCommand(program);
2402
2825
  program.parse(["node", "axiom", ...cleanedArgv]);