@axlsdk/studio 0.9.1 → 0.10.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.
@@ -0,0 +1,346 @@
1
+ import {
2
+ createServer,
3
+ handleWsMessage
4
+ } from "./chunk-GKIPF45K.js";
5
+
6
+ // src/middleware.ts
7
+ import { resolve as resolve2, dirname as dirname2 } from "path";
8
+ import { existsSync } from "fs";
9
+ import { fileURLToPath } from "url";
10
+ import { getRequestListener } from "@hono/node-server";
11
+ import { WebSocketServer } from "ws";
12
+
13
+ // src/eval-loader.ts
14
+ import { resolve, relative, dirname, basename } from "path";
15
+ import { readdirSync, statSync } from "fs";
16
+ import { pathToFileURL } from "url";
17
+ function createEvalLoader(config, runtime, cwd) {
18
+ let loadPromise;
19
+ const { patterns, conditions } = normalizeConfig(config);
20
+ const baseCwd = cwd ?? process.cwd();
21
+ return () => {
22
+ if (!loadPromise) {
23
+ loadPromise = loadEvalFiles(patterns, conditions, baseCwd, runtime).catch((err) => {
24
+ loadPromise = void 0;
25
+ throw err;
26
+ });
27
+ }
28
+ return loadPromise;
29
+ };
30
+ }
31
+ async function loadEvalFiles(patterns, conditions, cwd, runtime) {
32
+ if (conditions.length > 0) {
33
+ await registerConditions(conditions);
34
+ }
35
+ const files = resolvePatterns(patterns, cwd);
36
+ if (files.length === 0) {
37
+ console.warn(`[axl-studio] No eval files found matching: ${patterns.join(", ")}`);
38
+ return;
39
+ }
40
+ if (files.some((f) => /\.[mc]?tsx?$/.test(f))) {
41
+ await ensureTsxLoader();
42
+ }
43
+ for (const file of files) {
44
+ try {
45
+ const mod = await import(pathToFileURL(file).href);
46
+ const evalConfig = mod.default ?? mod.config ?? mod;
47
+ if (!evalConfig.workflow || !evalConfig.dataset || !evalConfig.scorers) {
48
+ console.warn(
49
+ `[axl-studio] Skipping ${file}: not a valid eval config (missing workflow, dataset, or scorers)`
50
+ );
51
+ continue;
52
+ }
53
+ const name = deriveEvalName(file, cwd);
54
+ if (runtime.getRegisteredEval(name)) {
55
+ console.warn(
56
+ `[axl-studio] Eval name "${name}" from ${file} collides with an already-registered eval \u2014 overwriting`
57
+ );
58
+ }
59
+ runtime.registerEval(name, evalConfig, mod.executeWorkflow);
60
+ } catch (err) {
61
+ const msg = err instanceof Error ? err.message : String(err);
62
+ console.warn(`[axl-studio] Failed to load eval ${file}: ${msg}`);
63
+ }
64
+ }
65
+ }
66
+ function normalizeConfig(config) {
67
+ if (typeof config === "string") {
68
+ return { patterns: [config], conditions: [] };
69
+ }
70
+ if (Array.isArray(config)) {
71
+ return { patterns: config, conditions: [] };
72
+ }
73
+ const files = typeof config.files === "string" ? [config.files] : config.files;
74
+ return { patterns: files, conditions: config.conditions ?? [] };
75
+ }
76
+ function deriveEvalName(filePath, cwd) {
77
+ const rel = relative(cwd, filePath);
78
+ const normalized = rel.replace(/\\/g, "/");
79
+ if (normalized.startsWith("../")) {
80
+ const base = basename(filePath);
81
+ const stripped = base.replace(/\.eval\.[mc]?[jt]sx?$/, "");
82
+ return stripped !== base ? stripped : base.replace(/\.[mc]?[jt]sx?$/, "") || base;
83
+ }
84
+ const withoutEval = normalized.replace(/\.eval\.[mc]?[jt]sx?$/, "");
85
+ if (withoutEval !== normalized) return withoutEval;
86
+ const withoutExt = normalized.replace(/\.[mc]?[jt]sx?$/, "");
87
+ return withoutExt || normalized;
88
+ }
89
+ function resolvePatterns(patterns, cwd) {
90
+ const files = [];
91
+ const seen = /* @__PURE__ */ new Set();
92
+ for (const pattern of patterns) {
93
+ const resolved = pattern.includes("*") ? expandGlob(pattern, cwd) : [resolve(cwd, pattern)];
94
+ for (const file of resolved) {
95
+ if (!seen.has(file)) {
96
+ seen.add(file);
97
+ files.push(file);
98
+ }
99
+ }
100
+ }
101
+ return files;
102
+ }
103
+ function expandGlob(pattern, cwd) {
104
+ if (pattern.includes("**/")) {
105
+ const sepIdx = pattern.indexOf("**/");
106
+ const baseDir = resolve(cwd, pattern.slice(0, sepIdx) || ".");
107
+ const fileGlob2 = pattern.slice(sepIdx + 3) || "*";
108
+ return findFiles(baseDir, fileGlob2, true);
109
+ }
110
+ const dir = resolve(cwd, dirname(pattern));
111
+ const fileGlob = basename(pattern);
112
+ return findFiles(dir, fileGlob, false);
113
+ }
114
+ var MAX_DEPTH = 20;
115
+ function findFiles(dir, fileGlob, recursive, depth = 0) {
116
+ if (depth > MAX_DEPTH) return [];
117
+ const matcher = globToRegex(fileGlob);
118
+ const results = [];
119
+ try {
120
+ const entries = readdirSync(dir);
121
+ for (const entry of entries) {
122
+ const full = resolve(dir, entry);
123
+ try {
124
+ const stat = statSync(full);
125
+ if (stat.isFile() && matcher.test(entry)) {
126
+ results.push(full);
127
+ } else if (stat.isDirectory() && recursive) {
128
+ results.push(...findFiles(full, fileGlob, true, depth + 1));
129
+ }
130
+ } catch {
131
+ }
132
+ }
133
+ } catch {
134
+ }
135
+ return results;
136
+ }
137
+ function globToRegex(glob) {
138
+ const escaped = glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
139
+ return new RegExp(`^${escaped}$`);
140
+ }
141
+ var tsxRegistered = false;
142
+ async function ensureTsxLoader() {
143
+ if (tsxRegistered) return;
144
+ tsxRegistered = true;
145
+ let loaded = false;
146
+ try {
147
+ const tsxEsm = await import("tsx/esm/api");
148
+ tsxEsm.register();
149
+ loaded = true;
150
+ } catch {
151
+ }
152
+ try {
153
+ const tsxCjs = await import("tsx/cjs/api");
154
+ tsxCjs.register();
155
+ loaded = true;
156
+ } catch {
157
+ }
158
+ if (!loaded) {
159
+ console.warn(
160
+ "[axl-studio] Warning: tsx is not installed. TypeScript eval files require tsx.\n Install it with: npm install -D tsx"
161
+ );
162
+ }
163
+ }
164
+ async function registerConditions(conditions) {
165
+ try {
166
+ const nodeModule = await import("module");
167
+ const hookCode = [
168
+ `const extra = ${JSON.stringify(conditions)};`,
169
+ `export async function resolve(specifier, context, nextResolve) {`,
170
+ ` return nextResolve(specifier, {`,
171
+ ` ...context,`,
172
+ ` conditions: [...new Set([...context.conditions, ...extra])],`,
173
+ ` });`,
174
+ `}`
175
+ ].join("\n");
176
+ nodeModule.register(`data:text/javascript,${encodeURIComponent(hookCode)}`);
177
+ } catch {
178
+ console.warn("[axl-studio] Warning: import conditions require Node.js 20.6+");
179
+ }
180
+ }
181
+
182
+ // src/middleware.ts
183
+ function createStudioMiddleware(options) {
184
+ const { runtime, serveClient = true, verifyUpgrade, readOnly = false } = options;
185
+ const basePath = normalizeBasePath(options.basePath);
186
+ const staticRoot = serveClient ? resolveClientDist() : void 0;
187
+ if (serveClient && !staticRoot) {
188
+ const dir = import.meta.dirname ?? dirname2(fileURLToPath(import.meta.url));
189
+ console.warn(
190
+ `[axl-studio] serveClient is true but no pre-built client found at ${resolve2(dir, "client")}. Studio UI will not be available. Set serveClient: false to suppress this warning.`
191
+ );
192
+ }
193
+ const evalLoader = options.evals ? createEvalLoader(options.evals, runtime) : void 0;
194
+ const { app, connMgr, traceListener } = createServer({
195
+ runtime,
196
+ staticRoot,
197
+ basePath,
198
+ readOnly,
199
+ cors: false,
200
+ // Host framework owns CORS policy
201
+ evalLoader
202
+ });
203
+ if (process.env.NODE_ENV === "production" && !verifyUpgrade) {
204
+ console.warn(
205
+ "[axl-studio] WARNING: Studio middleware mounted in production without verifyUpgrade. WebSocket connections are not authenticated. All registered workflows, tools, and agents are accessible. See https://axlsdk.com/docs/studio/security"
206
+ );
207
+ }
208
+ const listener = getRequestListener(app.fetch, {
209
+ overrideGlobalObjects: false
210
+ });
211
+ let closed = false;
212
+ function handler(req, res) {
213
+ if (closed) {
214
+ res.writeHead(503, { "Content-Type": "application/json" });
215
+ res.end(
216
+ JSON.stringify({
217
+ ok: false,
218
+ error: { code: "CLOSED", message: "Studio middleware has been shut down" }
219
+ })
220
+ );
221
+ return;
222
+ }
223
+ listener(req, res).catch((err) => {
224
+ console.error("[axl-studio] Unhandled error in request handler:", err);
225
+ if (!res.headersSent) {
226
+ res.writeHead(500, { "Content-Type": "application/json" });
227
+ res.end(
228
+ JSON.stringify({
229
+ ok: false,
230
+ error: { code: "INTERNAL_ERROR", message: "Internal server error" }
231
+ })
232
+ );
233
+ }
234
+ });
235
+ }
236
+ function handleWebSocket(ws) {
237
+ if (closed) {
238
+ ws.close();
239
+ return;
240
+ }
241
+ const socket = {
242
+ send: (data) => ws.send(data),
243
+ close: () => ws.close()
244
+ };
245
+ connMgr.add(socket);
246
+ ws.on("message", (raw) => {
247
+ const reply = handleWsMessage(String(raw), socket, connMgr);
248
+ if (reply) ws.send(reply);
249
+ });
250
+ ws.on("close", () => connMgr.remove(socket));
251
+ ws.on("error", () => connMgr.remove(socket));
252
+ }
253
+ let wss;
254
+ let upgradeHandler;
255
+ let serverRef;
256
+ function upgradeWebSocket(server, path) {
257
+ if (wss) {
258
+ throw new Error(
259
+ "[axl-studio] upgradeWebSocket() has already been called. Call close() first if you need to re-attach."
260
+ );
261
+ }
262
+ const wsPath = path ?? (basePath ? `${basePath}/ws` : "/ws");
263
+ wss = new WebSocketServer({ noServer: true });
264
+ serverRef = server;
265
+ upgradeHandler = async (req, socket, head) => {
266
+ const pathname = new URL(req.url, `http://${req.headers.host}`).pathname;
267
+ if (pathname !== wsPath) return;
268
+ if (verifyUpgrade) {
269
+ try {
270
+ const allowed = await verifyUpgrade(req);
271
+ if (!allowed) {
272
+ socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
273
+ socket.destroy();
274
+ return;
275
+ }
276
+ } catch {
277
+ socket.write("HTTP/1.1 403 Forbidden\r\n\r\n");
278
+ socket.destroy();
279
+ return;
280
+ }
281
+ }
282
+ if (!wss) {
283
+ socket.destroy();
284
+ return;
285
+ }
286
+ wss.handleUpgrade(req, socket, head, (ws) => {
287
+ handleWebSocket(ws);
288
+ });
289
+ };
290
+ server.on("upgrade", upgradeHandler);
291
+ }
292
+ function close() {
293
+ closed = true;
294
+ connMgr.closeAll();
295
+ if (upgradeHandler && serverRef) {
296
+ serverRef.removeListener("upgrade", upgradeHandler);
297
+ upgradeHandler = void 0;
298
+ serverRef = void 0;
299
+ }
300
+ if (wss) {
301
+ wss.close();
302
+ wss = void 0;
303
+ }
304
+ if (traceListener) {
305
+ runtime.removeListener("trace", traceListener);
306
+ }
307
+ }
308
+ return {
309
+ handler,
310
+ handleWebSocket,
311
+ upgradeWebSocket,
312
+ app,
313
+ connectionManager: connMgr,
314
+ close
315
+ };
316
+ }
317
+ function normalizeBasePath(raw) {
318
+ if (!raw) return "";
319
+ const normalized = raw.replace(/\/+$/, "");
320
+ if (!normalized) return "";
321
+ if (!normalized.startsWith("/")) {
322
+ throw new Error(`basePath must start with '/' (got '${raw}'). Example: '/studio'`);
323
+ }
324
+ if (normalized.includes("..")) {
325
+ throw new Error(`basePath must not contain '..' segments (got '${raw}')`);
326
+ }
327
+ if (normalized.includes("//")) {
328
+ throw new Error(`basePath must not contain consecutive slashes (got '${raw}')`);
329
+ }
330
+ if (!/^\/[a-zA-Z0-9/_-]*$/.test(normalized)) {
331
+ throw new Error(
332
+ `basePath contains invalid characters (got '${raw}'). Only alphanumeric characters, /, _, and - are allowed.`
333
+ );
334
+ }
335
+ return normalized;
336
+ }
337
+ function resolveClientDist() {
338
+ const dir = import.meta.dirname ?? dirname2(fileURLToPath(import.meta.url));
339
+ const candidate = resolve2(dir, "client");
340
+ return existsSync(resolve2(candidate, "index.html")) ? candidate : void 0;
341
+ }
342
+ export {
343
+ createStudioMiddleware,
344
+ handleWsMessage
345
+ };
346
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/middleware.ts","../src/eval-loader.ts"],"sourcesContent":["import { resolve, dirname } from 'node:path';\nimport { existsSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { getRequestListener } from '@hono/node-server';\nimport { WebSocketServer } from 'ws';\nimport { createServer } from './server/index.js';\nimport { handleWsMessage } from './server/ws/protocol.js';\nimport { createEvalLoader } from './eval-loader.js';\nimport type { EvalLoaderConfig } from './eval-loader.js';\nimport type { AxlRuntime } from '@axlsdk/axl';\nimport type { IncomingMessage, ServerResponse, Server } from 'node:http';\n\nexport type { EvalLoaderConfig } from './eval-loader.js';\n\nexport type StudioMiddlewareOptions = {\n /** The AxlRuntime instance to observe and control. */\n runtime: AxlRuntime;\n\n /**\n * URL path prefix where Studio is mounted.\n * Must match the mount path in your framework.\n *\n * Must start with '/' when non-empty. Trailing slashes are stripped.\n * Only URL-safe characters allowed: [a-zA-Z0-9/_-]\n *\n * @example '/studio'\n * @example '/admin/studio'\n * @example '' — mounted at root (default)\n */\n basePath?: string;\n\n /**\n * Serve the pre-built Studio SPA.\n * Set to false if serving the client from a CDN or separate build.\n * @default true\n */\n serveClient?: boolean;\n\n /**\n * Verify a WebSocket upgrade request before completing the handshake.\n * Return true to allow, false to reject. Throw to reject with an error.\n *\n * IMPORTANT: WebSocket upgrades bypass Express/Fastify/Koa middleware.\n * If your HTTP routes are behind auth middleware, WS connections are NOT\n * automatically protected. Use this callback to enforce authentication\n * on WebSocket connections.\n */\n verifyUpgrade?: (req: IncomingMessage) => boolean | Promise<boolean>;\n\n /**\n * Disable all mutating endpoints (execute, test, send, delete, resolve).\n * When true, Studio is observation-only.\n * @default false\n */\n readOnly?: boolean;\n\n /**\n * Lazy-load eval files for the Eval Runner panel.\n *\n * Eval files are dynamically imported on first access to eval endpoints,\n * not at middleware construction time. This means:\n * - Zero cost during normal API operation\n * - Eval files can import from any module without creating circular deps\n * in the static module graph (they're loaded as standalone entry points)\n * - `@axlsdk/eval` can remain a devDependency — eval files never enter\n * the production bundle since bundlers can't see dynamic imports\n *\n * Accepts glob patterns, explicit file paths, or an object with\n * `conditions` for monorepo source export resolution.\n *\n * Eval files are loaded once and cached for the middleware's lifetime.\n * Changes to eval files require a server restart.\n *\n * @example\n * // Single glob pattern\n * evals: 'evals/*.eval.ts'\n *\n * @example\n * // Multiple patterns\n * evals: ['libs/api/evals/*.eval.ts', 'libs/ai/evals/*.eval.ts']\n *\n * @example\n * // With import conditions for monorepo source exports\n * evals: {\n * files: 'libs/api/evals/*.eval.ts',\n * conditions: ['development'],\n * }\n */\n evals?: EvalLoaderConfig;\n};\n\n/**\n * Minimal contract a WebSocket connection must satisfy.\n * Matches the `ws` library API (de facto standard in Node.js).\n */\nexport interface StudioWebSocket {\n send(data: string): void;\n close(): void;\n on(event: 'message', fn: (data: string | Buffer) => void): void;\n on(event: 'close', fn: () => void): void;\n on(event: 'error', fn: (err: Error) => void): void;\n}\n\n// Re-export for Hono-in-Hono consumers\nexport { handleWsMessage } from './server/ws/protocol.js';\n\nexport type StudioMiddleware = ReturnType<typeof createStudioMiddleware>;\n\nexport function createStudioMiddleware(options: StudioMiddlewareOptions) {\n const { runtime, serveClient = true, verifyUpgrade, readOnly = false } = options;\n\n // Normalize basePath: strip trailing slashes, validate format\n const basePath = normalizeBasePath(options.basePath);\n\n // Resolve pre-built SPA assets from this package's dist/\n const staticRoot = serveClient ? resolveClientDist() : undefined;\n\n if (serveClient && !staticRoot) {\n const dir = import.meta.dirname ?? dirname(fileURLToPath(import.meta.url));\n console.warn(\n '[axl-studio] serveClient is true but no pre-built client found at ' +\n `${resolve(dir, 'client')}. Studio UI will not be available. ` +\n 'Set serveClient: false to suppress this warning.',\n );\n }\n\n // Create lazy eval loader if eval files are configured\n const evalLoader = options.evals ? createEvalLoader(options.evals, runtime) : undefined;\n\n const { app, connMgr, traceListener } = createServer({\n runtime,\n staticRoot,\n basePath,\n readOnly,\n cors: false, // Host framework owns CORS policy\n evalLoader,\n });\n\n // Log production safety warning\n if (process.env.NODE_ENV === 'production' && !verifyUpgrade) {\n console.warn(\n '[axl-studio] WARNING: Studio middleware mounted in production without verifyUpgrade. ' +\n 'WebSocket connections are not authenticated. All registered workflows, tools, and ' +\n 'agents are accessible. See https://axlsdk.com/docs/studio/security',\n );\n }\n\n // Convert Hono app → Node.js (req, res) handler.\n // overrideGlobalObjects: false prevents replacing global.Request and\n // global.Response, which could break the host application.\n const listener = getRequestListener(app.fetch, {\n overrideGlobalObjects: false,\n });\n\n let closed = false;\n\n function handler(req: IncomingMessage, res: ServerResponse) {\n if (closed) {\n res.writeHead(503, { 'Content-Type': 'application/json' });\n res.end(\n JSON.stringify({\n ok: false,\n error: { code: 'CLOSED', message: 'Studio middleware has been shut down' },\n }),\n );\n return;\n }\n // listener returns Promise<void>. Catch async errors to prevent\n // unhandled rejections from crashing the host process.\n listener(req, res).catch((err) => {\n console.error('[axl-studio] Unhandled error in request handler:', err);\n if (!res.headersSent) {\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(\n JSON.stringify({\n ok: false,\n error: { code: 'INTERNAL_ERROR', message: 'Internal server error' },\n }),\n );\n }\n });\n }\n\n // Handle an individual WebSocket using the Studio protocol.\n // Adapts any StudioWebSocket to ConnectionManager's internal BroadcastTarget.\n function handleWebSocket(ws: StudioWebSocket) {\n if (closed) {\n ws.close();\n return;\n }\n const socket = {\n send: (data: string) => ws.send(data),\n close: () => ws.close(),\n };\n connMgr.add(socket);\n\n ws.on('message', (raw) => {\n const reply = handleWsMessage(String(raw), socket, connMgr);\n if (reply) ws.send(reply);\n });\n\n ws.on('close', () => connMgr.remove(socket));\n ws.on('error', () => connMgr.remove(socket));\n }\n\n // Internal WebSocketServer — created lazily by upgradeWebSocket()\n let wss: InstanceType<typeof WebSocketServer> | undefined;\n // References for cleanup: the upgrade handler and server it's attached to\n let upgradeHandler: ((...args: any[]) => void) | undefined;\n let serverRef: Server | undefined;\n\n // Convenience: attach WS handling to an http.Server.\n function upgradeWebSocket(server: Server, path?: string) {\n if (wss) {\n throw new Error(\n '[axl-studio] upgradeWebSocket() has already been called. ' +\n 'Call close() first if you need to re-attach.',\n );\n }\n\n const wsPath = path ?? (basePath ? `${basePath}/ws` : '/ws');\n\n wss = new WebSocketServer({ noServer: true });\n serverRef = server;\n\n upgradeHandler = async (req: IncomingMessage, socket: any, head: Buffer) => {\n // Match path, ignoring query string\n const pathname = new URL(req.url!, `http://${req.headers.host}`).pathname;\n if (pathname !== wsPath) return; // Let other upgrade handlers run\n\n // Apply auth verification\n if (verifyUpgrade) {\n try {\n const allowed = await verifyUpgrade(req);\n if (!allowed) {\n socket.write('HTTP/1.1 401 Unauthorized\\r\\n\\r\\n');\n socket.destroy();\n return;\n }\n } catch {\n socket.write('HTTP/1.1 403 Forbidden\\r\\n\\r\\n');\n socket.destroy();\n return;\n }\n }\n\n // Guard against close() being called during async verifyUpgrade\n if (!wss) {\n socket.destroy();\n return;\n }\n\n wss.handleUpgrade(req, socket, head, (ws) => {\n handleWebSocket(ws);\n });\n };\n\n server.on('upgrade', upgradeHandler);\n }\n\n // Cleanup function for lifecycle management.\n function close() {\n closed = true;\n\n // Close all WebSocket connections\n connMgr.closeAll();\n\n // Remove the upgrade listener from the server before closing WSS\n if (upgradeHandler && serverRef) {\n serverRef.removeListener('upgrade', upgradeHandler);\n upgradeHandler = undefined;\n serverRef = undefined;\n }\n\n // Shut down the internal WebSocketServer if one was created\n if (wss) {\n wss.close();\n wss = undefined;\n }\n\n // Remove only our trace event listener from the runtime\n if (traceListener) {\n runtime.removeListener('trace', traceListener);\n }\n }\n\n return {\n handler,\n handleWebSocket,\n upgradeWebSocket,\n app,\n connectionManager: connMgr,\n close,\n };\n}\n\n/**\n * Normalize and validate basePath.\n * - Empty string and undefined → ''\n * - Strip trailing slashes\n * - Validate leading slash when non-empty\n * - Reject unsafe characters\n */\nfunction normalizeBasePath(raw?: string): string {\n if (!raw) return '';\n\n // Strip trailing slashes\n const normalized = raw.replace(/\\/+$/, '');\n if (!normalized) return '';\n\n // Must start with /\n if (!normalized.startsWith('/')) {\n throw new Error(`basePath must start with '/' (got '${raw}'). Example: '/studio'`);\n }\n\n // Reject path traversal, consecutive slashes, and unsafe characters\n if (normalized.includes('..')) {\n throw new Error(`basePath must not contain '..' segments (got '${raw}')`);\n }\n if (normalized.includes('//')) {\n throw new Error(`basePath must not contain consecutive slashes (got '${raw}')`);\n }\n if (!/^\\/[a-zA-Z0-9/_-]*$/.test(normalized)) {\n throw new Error(\n `basePath contains invalid characters (got '${raw}'). ` +\n 'Only alphanumeric characters, /, _, and - are allowed.',\n );\n }\n\n return normalized;\n}\n\nfunction resolveClientDist(): string | undefined {\n // Resolve the directory of this file (dist/ in published package).\n const dir = import.meta.dirname ?? dirname(fileURLToPath(import.meta.url));\n const candidate = resolve(dir, 'client');\n return existsSync(resolve(candidate, 'index.html')) ? candidate : undefined;\n}\n","import { resolve, relative, dirname, basename } from 'node:path';\nimport { readdirSync, statSync } from 'node:fs';\nimport { pathToFileURL } from 'node:url';\nimport type { AxlRuntime } from '@axlsdk/axl';\n\n/**\n * Configuration for lazy eval file discovery.\n *\n * - `string` — a glob pattern or explicit file path\n * - `string[]` — multiple patterns/paths\n * - `object` — patterns with optional import conditions\n */\nexport type EvalLoaderConfig =\n | string\n | string[]\n | {\n files: string | string[];\n\n /**\n * Custom Node.js import conditions (e.g., `['development']`).\n *\n * In monorepos, package.json `exports` often use the `development` condition\n * to point at source (`.ts`) instead of built dist. Without this, eval files\n * that import workspace packages resolve to dist files, which may not exist.\n *\n * **WARNING**: Conditions are registered process-wide via `module.register()`.\n * They affect all subsequent imports in the process, not just eval files.\n */\n conditions?: string[];\n };\n\n/**\n * Create a lazy eval loader that resolves file patterns and dynamically imports\n * eval files on first call, registering them with the runtime.\n *\n * The loader is idempotent — subsequent calls return the same promise.\n * Concurrent callers all await the same loading work.\n *\n * Eval files should export a default config with `{ workflow, dataset, scorers }`\n * (the result of `defineEval()` from `@axlsdk/eval`). An optional named export\n * `executeWorkflow` overrides the default `runtime.execute()` behavior.\n *\n * Eval names are the file's path relative to `cwd` (project root), minus the\n * `.eval.*` suffix. This makes names completely stable — a file's name never\n * changes regardless of what other files or patterns exist.\n *\n * @param config Glob patterns, file paths, or object with conditions\n * @param runtime The AxlRuntime to register discovered evals on\n * @param cwd Base directory for resolving patterns and deriving names (default: `process.cwd()`)\n */\nexport function createEvalLoader(\n config: EvalLoaderConfig,\n runtime: AxlRuntime,\n cwd?: string,\n): () => Promise<void> {\n let loadPromise: Promise<void> | undefined;\n const { patterns, conditions } = normalizeConfig(config);\n const baseCwd = cwd ?? process.cwd();\n\n return () => {\n if (!loadPromise) {\n loadPromise = loadEvalFiles(patterns, conditions, baseCwd, runtime).catch((err) => {\n loadPromise = undefined; // Allow retry on next request\n throw err;\n });\n }\n return loadPromise;\n };\n}\n\n// ── Core loading logic ─────────────────────────────────────────────\n\nasync function loadEvalFiles(\n patterns: string[],\n conditions: string[],\n cwd: string,\n runtime: AxlRuntime,\n): Promise<void> {\n if (conditions.length > 0) {\n await registerConditions(conditions);\n }\n\n const files = resolvePatterns(patterns, cwd);\n\n if (files.length === 0) {\n console.warn(`[axl-studio] No eval files found matching: ${patterns.join(', ')}`);\n return;\n }\n\n // Register tsx loader if any files are TypeScript\n if (files.some((f) => /\\.[mc]?tsx?$/.test(f))) {\n await ensureTsxLoader();\n }\n\n for (const file of files) {\n try {\n const mod = await import(pathToFileURL(file).href);\n const evalConfig = mod.default ?? mod.config ?? mod;\n\n if (!evalConfig.workflow || !evalConfig.dataset || !evalConfig.scorers) {\n console.warn(\n `[axl-studio] Skipping ${file}: not a valid eval config ` +\n `(missing workflow, dataset, or scorers)`,\n );\n continue;\n }\n\n const name = deriveEvalName(file, cwd);\n\n if (runtime.getRegisteredEval(name)) {\n console.warn(\n `[axl-studio] Eval name \"${name}\" from ${file} collides with an ` +\n `already-registered eval — overwriting`,\n );\n }\n\n runtime.registerEval(name, evalConfig, mod.executeWorkflow);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(`[axl-studio] Failed to load eval ${file}: ${msg}`);\n }\n }\n}\n\n// ── Internal helpers ───────────────────────────────────────────────\n\nfunction normalizeConfig(config: EvalLoaderConfig): {\n patterns: string[];\n conditions: string[];\n} {\n if (typeof config === 'string') {\n return { patterns: [config], conditions: [] };\n }\n if (Array.isArray(config)) {\n return { patterns: config, conditions: [] };\n }\n const files = typeof config.files === 'string' ? [config.files] : config.files;\n return { patterns: files, conditions: config.conditions ?? [] };\n}\n\n/**\n * Derive eval name from file path relative to cwd.\n *\n * Examples (cwd = `/project`):\n * - `/project/evals/suggestions.eval.ts` → `\"evals/suggestions\"`\n * - `/project/evals/api/accuracy.eval.ts` → `\"evals/api/accuracy\"`\n */\nfunction deriveEvalName(filePath: string, cwd: string): string {\n const rel = relative(cwd, filePath);\n // Normalize to forward slashes for cross-platform consistency\n const normalized = rel.replace(/\\\\/g, '/');\n // Guard: file outside cwd (symlink, absolute path) — fall back to basename\n if (normalized.startsWith('../')) {\n const base = basename(filePath);\n const stripped = base.replace(/\\.eval\\.[mc]?[jt]sx?$/, '');\n return stripped !== base ? stripped : base.replace(/\\.[mc]?[jt]sx?$/, '') || base;\n }\n // Strip .eval.ts, .eval.mjs, .eval.js, etc.\n const withoutEval = normalized.replace(/\\.eval\\.[mc]?[jt]sx?$/, '');\n if (withoutEval !== normalized) return withoutEval;\n // Fallback: strip extension\n const withoutExt = normalized.replace(/\\.[mc]?[jt]sx?$/, '');\n return withoutExt || normalized;\n}\n\n/**\n * Resolve patterns to absolute file paths.\n *\n * Supports:\n * - Explicit file paths (no wildcards)\n * - Single-directory globs: `dir/*.eval.ts`\n * - Recursive globs: `dir/**\\/*.eval.ts` or `**\\/*.eval.ts`\n *\n * Multi-segment `**` (e.g., `a/**\\/b/**\\/*.ts`) is not supported.\n */\nfunction resolvePatterns(patterns: string[], cwd: string): string[] {\n const files: string[] = [];\n const seen = new Set<string>();\n for (const pattern of patterns) {\n const resolved = pattern.includes('*') ? expandGlob(pattern, cwd) : [resolve(cwd, pattern)];\n for (const file of resolved) {\n if (!seen.has(file)) {\n seen.add(file);\n files.push(file);\n }\n }\n }\n return files;\n}\n\n/**\n * Expand a glob pattern to matching file paths.\n *\n * Supported forms:\n * - `dir/*.eval.ts` — match files in dir/\n * - `dir/**\\/*.eval.ts` — recursively match under dir/\n * - `**\\/*.eval.ts` — recursively match under cwd\n */\nfunction expandGlob(pattern: string, cwd: string): string[] {\n if (pattern.includes('**/')) {\n const sepIdx = pattern.indexOf('**/');\n const baseDir = resolve(cwd, pattern.slice(0, sepIdx) || '.');\n const fileGlob = pattern.slice(sepIdx + 3) || '*';\n return findFiles(baseDir, fileGlob, true);\n }\n\n const dir = resolve(cwd, dirname(pattern));\n const fileGlob = basename(pattern);\n return findFiles(dir, fileGlob, false);\n}\n\nconst MAX_DEPTH = 20;\n\nfunction findFiles(dir: string, fileGlob: string, recursive: boolean, depth = 0): string[] {\n if (depth > MAX_DEPTH) return [];\n const matcher = globToRegex(fileGlob);\n const results: string[] = [];\n\n try {\n const entries = readdirSync(dir);\n for (const entry of entries) {\n const full = resolve(dir, entry);\n try {\n const stat = statSync(full);\n if (stat.isFile() && matcher.test(entry)) {\n results.push(full);\n } else if (stat.isDirectory() && recursive) {\n results.push(...findFiles(full, fileGlob, true, depth + 1));\n }\n } catch {\n // Skip unreadable entries\n }\n }\n } catch {\n // Directory doesn't exist or unreadable\n }\n\n return results;\n}\n\n/** Convert a simple glob pattern (e.g., `*.eval.ts`) to a RegExp. */\nfunction globToRegex(glob: string): RegExp {\n const escaped = glob.replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&').replace(/\\*/g, '.*');\n return new RegExp(`^${escaped}$`);\n}\n\nlet tsxRegistered = false;\n\nasync function ensureTsxLoader(): Promise<void> {\n if (tsxRegistered) return;\n tsxRegistered = true;\n\n let loaded = false;\n try {\n const tsxEsm = await import('tsx/esm/api');\n tsxEsm.register();\n loaded = true;\n } catch {\n // ESM hook not available\n }\n try {\n const tsxCjs = await import('tsx/cjs/api');\n tsxCjs.register();\n loaded = true;\n } catch {\n // CJS hook not available\n }\n if (!loaded) {\n console.warn(\n '[axl-studio] Warning: tsx is not installed. TypeScript eval files require tsx.\\n' +\n ' Install it with: npm install -D tsx',\n );\n }\n}\n\nasync function registerConditions(conditions: string[]): Promise<void> {\n try {\n const nodeModule = await import('node:module');\n const hookCode = [\n `const extra = ${JSON.stringify(conditions)};`,\n `export async function resolve(specifier, context, nextResolve) {`,\n ` return nextResolve(specifier, {`,\n ` ...context,`,\n ` conditions: [...new Set([...context.conditions, ...extra])],`,\n ` });`,\n `}`,\n ].join('\\n');\n nodeModule.register(`data:text/javascript,${encodeURIComponent(hookCode)}`);\n } catch {\n console.warn('[axl-studio] Warning: import conditions require Node.js 20.6+');\n }\n}\n"],"mappings":"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","names":["resolve","dirname","fileGlob","dirname","resolve"]}