@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/README.md +29 -7
- package/dist/{chunk-KQ3HBRTC.js → chunk-YGLWYDQ3.js} +11 -4
- package/dist/chunk-YGLWYDQ3.js.map +1 -0
- package/dist/cli.cjs +92 -9
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +83 -7
- package/dist/cli.js.map +1 -1
- package/dist/middleware.cjs +18 -5
- package/dist/middleware.cjs.map +1 -1
- package/dist/middleware.d.cts +8 -0
- package/dist/middleware.d.ts +8 -0
- package/dist/middleware.js +9 -3
- package/dist/middleware.js.map +1 -1
- package/dist/server/index.cjs +10 -3
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/package.json +4 -4
- package/dist/chunk-KQ3HBRTC.js.map +0 -1
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({
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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}`);
|