@axlsdk/studio 0.20.1 → 0.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -1392,7 +1392,10 @@ function createSessionRoutes(connMgr) {
1392
1392
  const body = await c.req.json();
1393
1393
  const session = runtime.session(id);
1394
1394
  const result = await session.send(body.workflow, body.message);
1395
- return c.json({ ok: true, data: { result } });
1395
+ return c.json({
1396
+ ok: true,
1397
+ data: { result: redactValue(result, runtime.isRedactEnabled()) }
1398
+ });
1396
1399
  });
1397
1400
  app5.post("/sessions/:id/stream", async (c) => {
1398
1401
  const runtime = c.get("runtime");
@@ -1401,9 +1404,13 @@ function createSessionRoutes(connMgr) {
1401
1404
  const session = runtime.session(id);
1402
1405
  const stream = await session.stream(body.workflow, body.message);
1403
1406
  const executionId = `session-${id}-${Date.now()}`;
1407
+ const redactOn = runtime.isRedactEnabled();
1404
1408
  (async () => {
1405
1409
  for await (const event of stream) {
1406
- connMgr.broadcastWithWildcard(`execution:${executionId}`, event);
1410
+ connMgr.broadcastWithWildcard(
1411
+ `execution:${executionId}`,
1412
+ redactStreamEvent(event, redactOn)
1413
+ );
1407
1414
  }
1408
1415
  })();
1409
1416
  return c.json({ ok: true, data: { executionId, streaming: true } });
@@ -16109,7 +16116,7 @@ function createServer(options) {
16109
16116
  emptyState: emptyEvalTrendData,
16110
16117
  windows
16111
16118
  });
16112
- if (options.cors !== false) {
16119
+ if (options.cors === true) {
16113
16120
  app5.use("*", (0, import_cors.cors)());
16114
16121
  }
16115
16122
  app5.onError(errorHandler);
@@ -16265,8 +16272,58 @@ var import_axl6 = require("@axlsdk/axl");
16265
16272
 
16266
16273
  // src/cli-utils.ts
16267
16274
  var import_axl7 = require("@axlsdk/axl");
16275
+ var STUDIO_CLI_HOST = "127.0.0.1";
16276
+ function isLoopbackHostname(hostname3) {
16277
+ return hostname3 === "localhost" || hostname3 === "localhost." || hostname3 === "127.0.0.1";
16278
+ }
16279
+ function isAllowedStandaloneHost(host) {
16280
+ if (!host) return false;
16281
+ let url2;
16282
+ try {
16283
+ url2 = new URL(`http://${host}`);
16284
+ } catch {
16285
+ return false;
16286
+ }
16287
+ if (url2.username || url2.password || url2.pathname !== "/" || url2.search || url2.hash) return false;
16288
+ return isLoopbackHostname(url2.hostname);
16289
+ }
16290
+ function isAllowedStandaloneOrigin(origin) {
16291
+ if (origin === void 0) return true;
16292
+ let url2;
16293
+ try {
16294
+ url2 = new URL(origin);
16295
+ } catch {
16296
+ return false;
16297
+ }
16298
+ if (url2.protocol !== "http:" && url2.protocol !== "https:") return false;
16299
+ if (url2.username || url2.password || url2.pathname !== "/" || url2.search || url2.hash) return false;
16300
+ return isLoopbackHostname(url2.hostname);
16301
+ }
16302
+ function isAllowedStandaloneRequest(host, origin) {
16303
+ return isAllowedStandaloneHost(host) && isAllowedStandaloneOrigin(origin);
16304
+ }
16305
+ function withStandaloneRequestGuard(fetch) {
16306
+ return (request) => {
16307
+ const host = request.headers.get("host") ?? void 0;
16308
+ const origin = request.headers.get("origin") ?? void 0;
16309
+ if (!isAllowedStandaloneRequest(host, origin)) {
16310
+ return Response.json(
16311
+ {
16312
+ ok: false,
16313
+ error: {
16314
+ code: "FORBIDDEN_HOST_ORIGIN",
16315
+ message: "Standalone Studio accepts requests only through a local host and origin"
16316
+ }
16317
+ },
16318
+ { status: 403 }
16319
+ );
16320
+ }
16321
+ return fetch(request);
16322
+ };
16323
+ }
16268
16324
  function parseArgs(argv) {
16269
16325
  let port = 4400;
16326
+ let host = STUDIO_CLI_HOST;
16270
16327
  let config2;
16271
16328
  let open = false;
16272
16329
  let help = false;
@@ -16277,6 +16334,9 @@ function parseArgs(argv) {
16277
16334
  if (arg === "--port" && argv[i + 1]) {
16278
16335
  port = parseInt(argv[i + 1], 10);
16279
16336
  i++;
16337
+ } else if (arg === "--dangerously-bind" && argv[i + 1]) {
16338
+ host = argv[i + 1];
16339
+ i++;
16280
16340
  } else if (arg === "--config" && argv[i + 1]) {
16281
16341
  config2 = argv[i + 1];
16282
16342
  i++;
@@ -16291,7 +16351,7 @@ function parseArgs(argv) {
16291
16351
  help = true;
16292
16352
  }
16293
16353
  }
16294
- const result = { port, config: config2, open, help, conditions, readOnly };
16354
+ const result = { port, host, config: config2, open, help, conditions, readOnly };
16295
16355
  if (isNaN(port) || port < 1 || port > 65535) {
16296
16356
  result.portError = `Invalid port: ${port}. Must be between 1 and 65535.`;
16297
16357
  }
@@ -16311,6 +16371,7 @@ Usage:
16311
16371
 
16312
16372
  Options:
16313
16373
  --port <number> Server port (default: 4400)
16374
+ --dangerously-bind <ip> Bind for a loopback-published container/local-forward tunnel
16314
16375
  --config <path> Path to config file (default: auto-detect)
16315
16376
  --conditions <list> Comma-separated Node.js import conditions (e.g., development)
16316
16377
  --read-only Disable all mutating endpoints (runs, imports, rescore, etc)
@@ -16403,24 +16464,41 @@ Tip: Use .mts for configs with top-level await or in projects without "type": "m
16403
16464
  const { app: app5, createWsHandlers: createWsHandlers2 } = createServer({
16404
16465
  runtime,
16405
16466
  staticRoot: hasStaticAssets ? staticRoot : void 0,
16406
- readOnly: args.readOnly
16467
+ readOnly: args.readOnly,
16468
+ cors: false
16407
16469
  });
16408
16470
  if (args.readOnly) {
16409
16471
  console.log("[axl-studio] Read-only mode enabled \u2014 mutating endpoints are disabled.");
16410
16472
  }
16411
16473
  const { injectWebSocket, upgradeWebSocket } = (0, import_node_ws.createNodeWebSocket)({ app: app5 });
16412
16474
  const wsHandlers = createWsHandlers2();
16475
+ app5.use("/ws", async (c, next) => {
16476
+ if (!isAllowedStandaloneRequest(c.req.header("Host"), c.req.header("Origin"))) {
16477
+ return c.json(
16478
+ {
16479
+ ok: false,
16480
+ error: {
16481
+ code: "FORBIDDEN_HOST_ORIGIN",
16482
+ message: "Standalone Studio accepts WebSocket connections only locally"
16483
+ }
16484
+ },
16485
+ 403
16486
+ );
16487
+ }
16488
+ await next();
16489
+ });
16413
16490
  app5.get(
16414
16491
  "/ws",
16415
16492
  upgradeWebSocket(() => wsHandlers)
16416
16493
  );
16417
16494
  const server = (0, import_node_server.serve)(
16418
16495
  {
16419
- fetch: app5.fetch,
16420
- port: args.port
16496
+ fetch: withStandaloneRequestGuard((request) => app5.fetch(request)),
16497
+ port: args.port,
16498
+ hostname: args.host
16421
16499
  },
16422
16500
  (info) => {
16423
- console.log(`[axl-studio] Server running at http://localhost:${info.port}`);
16501
+ console.log(`[axl-studio] Server running at http://${STUDIO_CLI_HOST}:${info.port}`);
16424
16502
  console.log(`[axl-studio] Workflows: ${runtime.getWorkflowNames().join(", ") || "(none)"}`);
16425
16503
  console.log(
16426
16504
  `[axl-studio] Agents: ${runtime.getAgents().map((a) => a._name).join(", ") || "(none)"}`
@@ -16436,8 +16514,13 @@ Tip: Use .mts for configs with top-level await or in projects without "type": "m
16436
16514
  }
16437
16515
  );
16438
16516
  injectWebSocket(server);
16517
+ if (args.host !== STUDIO_CLI_HOST) {
16518
+ console.warn(
16519
+ `[axl-studio] WARNING: binding to ${args.host}. Use this only behind a local-only container port mapping or local-forward tunnel; standalone Studio remains unauthenticated.`
16520
+ );
16521
+ }
16439
16522
  if (args.open) {
16440
- const url2 = `http://localhost:${args.port}`;
16523
+ const url2 = `http://${STUDIO_CLI_HOST}:${args.port}`;
16441
16524
  const { exec } = await import("child_process");
16442
16525
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
16443
16526
  exec(`${cmd} ${url2}`);