@axlsdk/studio 0.20.0 → 0.21.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.
- package/README.md +46 -10
- 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/README.md
CHANGED
|
@@ -68,7 +68,9 @@ Your application imports from `src/runtime.ts` directly. Studio discovers everyt
|
|
|
68
68
|
npx @axlsdk/studio --open
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
-
This loads your config
|
|
71
|
+
This loads your config and starts the server on `http://127.0.0.1:4400`. The
|
|
72
|
+
standalone CLI binds only to IPv4 loopback, emits no CORS headers, and rejects
|
|
73
|
+
non-local HTTP hosts and browser origins across both REST and WebSocket routes.
|
|
72
74
|
|
|
73
75
|
## CLI Options
|
|
74
76
|
|
|
@@ -77,19 +79,33 @@ axl-studio [options]
|
|
|
77
79
|
|
|
78
80
|
Options:
|
|
79
81
|
--port <number> Server port (default: 4400)
|
|
82
|
+
--dangerously-bind <ip> Bind for a loopback-published container/local-forward tunnel
|
|
80
83
|
--config <path> Path to config file (default: auto-detect)
|
|
81
84
|
--conditions <list> Comma-separated Node.js import conditions (e.g., development)
|
|
85
|
+
--read-only Disable mutating endpoints
|
|
82
86
|
--open Auto-open browser
|
|
83
87
|
-h, --help Show help
|
|
84
88
|
```
|
|
85
89
|
|
|
90
|
+
For a container or devcontainer, bind the published host port to loopback and
|
|
91
|
+
make only the container listener broad:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
docker run -p 127.0.0.1:4400:4400 ... axl-studio --dangerously-bind 0.0.0.0
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The Host/Origin guard remains active. Do not publish that port on a public or
|
|
98
|
+
LAN interface; standalone Studio has no user authentication.
|
|
99
|
+
|
|
86
100
|
The `--conditions` flag is useful in monorepos where workspace packages use the `"development"` export condition to point at source instead of built dist files. Pass `--conditions development` to resolve imports through source paths.
|
|
87
101
|
|
|
88
102
|
**Note:** `--conditions` only applies to ESM `import()` resolution. Transitive `require()` calls from packages without `"type": "module"` bypass the hook and use default conditions. If a dependency loads via CJS, it will still resolve from `dist/` regardless of the flag.
|
|
89
103
|
|
|
90
104
|
## Panels
|
|
91
105
|
|
|
92
|
-
Eight panels, all live over WebSocket. The
|
|
106
|
+
Eight panels, all live over WebSocket. The captures below use representative seeded
|
|
107
|
+
runs; see [`docs/assets/CAPTURE.md`](../../docs/assets/CAPTURE.md) for how to regenerate
|
|
108
|
+
them.
|
|
93
109
|
|
|
94
110
|
### Agent Playground
|
|
95
111
|
|
|
@@ -101,7 +117,12 @@ Chat with any registered agent in real time. Streaming tokens, tool calls with e
|
|
|
101
117
|
|
|
102
118
|
### Trace Explorer
|
|
103
119
|
|
|
104
|
-
Waterfall visualization of execution traces. Filter by type, agent, or tool; see token
|
|
120
|
+
Waterfall visualization of execution traces. Filter by type, agent, or tool; see token
|
|
121
|
+
counts, cost per step, and duration. Tool rows distinguish calls that succeeded,
|
|
122
|
+
failed, were denied, were cancelled, or were rejected before they started. Studio also
|
|
123
|
+
keeps older trace history readable and identifies incomplete runs instead of presenting
|
|
124
|
+
missing events as a complete story. The Stats view shows the event-type distribution,
|
|
125
|
+
top tools, and retry stacks.
|
|
105
126
|
|
|
106
127
|
> **Studio vs `ctx.events`.** Studio consumes the same `AxlEvent` firehose via `runtime.on('trace', …)` — every event from every execution. Inside a workflow handler, `ctx.events` is the in-handler counterpart (per-context, scoped to the current workflow). The two coexist: Studio is for cross-execution observability and replay; `ctx.events` is for in-handler streaming UIs. See [`docs/observability.md`](../../docs/observability.md#observation-paths).
|
|
107
128
|
|
|
@@ -115,7 +136,14 @@ Track spending across agents, models, workflows, and embedders with time-window
|
|
|
115
136
|
|
|
116
137
|
### Eval Runner
|
|
117
138
|
|
|
118
|
-
Run evaluations from the UI, watch items stream in, and drill into per-item scores,
|
|
139
|
+
Run evaluations from the UI, watch items stream in, and drill into per-item scores,
|
|
140
|
+
timing, cost, and LLM-judge reasoning. Compare two runs (baseline vs candidate) with
|
|
141
|
+
bootstrap-CI significance, a score-distribution chart, and an item-level diff table.
|
|
142
|
+
The History tab groups multi-run results and tracks mean scores across runs. Toggle
|
|
143
|
+
**Capture traces** to render each item's events inline. For tool-using workflows, those
|
|
144
|
+
events show whether a call failed, was denied, was cancelled, or never started, giving
|
|
145
|
+
you better evidence for debugging and custom scorers. Studio does not turn those states
|
|
146
|
+
into scores automatically. Requires `@axlsdk/eval` as an optional peer dependency.
|
|
119
147
|
|
|
120
148
|
Amber banners flag runs you shouldn't fully trust — a scorer that failed on too many items, a subset run, or annotations dropped by the dataset schema — so a thinned or misconfigured run can't quietly look clean.
|
|
121
149
|
|
|
@@ -165,24 +193,32 @@ const studio = createStudioMiddleware({
|
|
|
165
193
|
runtime,
|
|
166
194
|
basePath: '/studio',
|
|
167
195
|
// WebSocket upgrades bypass Express middleware — always authenticate here.
|
|
168
|
-
verifyUpgrade: (req) =>
|
|
169
|
-
const url = new URL(req.url!, `http://${req.headers.host}`);
|
|
170
|
-
return url.searchParams.get('token') === process.env.MY_SECRET;
|
|
171
|
-
},
|
|
196
|
+
verifyUpgrade: (req) => authenticateStudioRequest(req)?.isAdmin === true,
|
|
172
197
|
});
|
|
173
198
|
|
|
174
199
|
const app = express();
|
|
175
|
-
|
|
200
|
+
const authenticateStudioHttp: express.RequestHandler = (req, res, next) => {
|
|
201
|
+
if (authenticateStudioRequest(req)?.isAdmin !== true) return res.sendStatus(403);
|
|
202
|
+
next();
|
|
203
|
+
};
|
|
204
|
+
app.use('/studio', authenticateStudioHttp, studio.handler);
|
|
176
205
|
const server = app.listen(3000);
|
|
177
206
|
studio.upgradeWebSocket(server); // required for live data
|
|
178
207
|
```
|
|
179
208
|
|
|
180
|
-
Key options: `readOnly` (disable mutating endpoints for production monitoring), `evals` (lazy-load eval files), `filterTraceEvent` (per-tenant broadcast scoping), `bufferCaps` (WS replay-buffer limits). `basePath` must match your framework's mount path.
|
|
209
|
+
Key options: `readOnly` (disable mutating endpoints for production monitoring), `evals` (lazy-load eval files), `filterTraceEvent` (per-tenant broadcast scoping), `bufferCaps` (WS replay-buffer limits). `basePath` must match your framework's mount path. In production, `upgradeWebSocket()` refuses to attach without `verifyUpgrade`; `dangerouslyAllowUnauthenticatedWebSockets` is the explicit escape hatch only when an outer upgrade gate is independently guaranteed.
|
|
181
210
|
|
|
182
211
|
**See [`docs/studio-api.md`](../../docs/studio-api.md) for the full reference:** every REST endpoint, the WebSocket protocol, the complete middleware options/return tables, NestJS/Fastify/Hono examples, host body-limit guidance, lazy eval loading, multi-tenant setup, and the internal architecture.
|
|
183
212
|
|
|
184
213
|
## Security
|
|
185
214
|
|
|
215
|
+
- **Treat Studio as an administrative surface, never as a public application
|
|
216
|
+
API.** Agent routes intentionally expose resolved system prompts, tool
|
|
217
|
+
descriptions, schemas, and runtime configuration. `trace.redact` protects
|
|
218
|
+
user/model observability content; it is not authentication and does not hide
|
|
219
|
+
static agent configuration.
|
|
220
|
+
- Protect the Studio HTTP mount with your framework's authentication and
|
|
221
|
+
authorization middleware.
|
|
186
222
|
- **Always** provide `verifyUpgrade` — WebSocket upgrades bypass Express/Fastify/Koa middleware, so your auth middleware does **not** protect WebSocket connections.
|
|
187
223
|
- Consider `readOnly: true` for production monitoring — view traces, costs, and schemas without execution capability.
|
|
188
224
|
- CORS is not applied in embedded mode — the host framework owns CORS policy.
|
|
@@ -1363,7 +1363,10 @@ function createSessionRoutes(connMgr) {
|
|
|
1363
1363
|
const body = await c.req.json();
|
|
1364
1364
|
const session = runtime.session(id);
|
|
1365
1365
|
const result = await session.send(body.workflow, body.message);
|
|
1366
|
-
return c.json({
|
|
1366
|
+
return c.json({
|
|
1367
|
+
ok: true,
|
|
1368
|
+
data: { result: redactValue(result, runtime.isRedactEnabled()) }
|
|
1369
|
+
});
|
|
1367
1370
|
});
|
|
1368
1371
|
app5.post("/sessions/:id/stream", async (c) => {
|
|
1369
1372
|
const runtime = c.get("runtime");
|
|
@@ -1372,9 +1375,13 @@ function createSessionRoutes(connMgr) {
|
|
|
1372
1375
|
const session = runtime.session(id);
|
|
1373
1376
|
const stream = await session.stream(body.workflow, body.message);
|
|
1374
1377
|
const executionId = `session-${id}-${Date.now()}`;
|
|
1378
|
+
const redactOn = runtime.isRedactEnabled();
|
|
1375
1379
|
(async () => {
|
|
1376
1380
|
for await (const event of stream) {
|
|
1377
|
-
connMgr.broadcastWithWildcard(
|
|
1381
|
+
connMgr.broadcastWithWildcard(
|
|
1382
|
+
`execution:${executionId}`,
|
|
1383
|
+
redactStreamEvent(event, redactOn)
|
|
1384
|
+
);
|
|
1378
1385
|
}
|
|
1379
1386
|
})();
|
|
1380
1387
|
return c.json({ ok: true, data: { executionId, streaming: true } });
|
|
@@ -16080,7 +16087,7 @@ function createServer(options) {
|
|
|
16080
16087
|
emptyState: emptyEvalTrendData,
|
|
16081
16088
|
windows
|
|
16082
16089
|
});
|
|
16083
|
-
if (options.cors
|
|
16090
|
+
if (options.cors === true) {
|
|
16084
16091
|
app5.use("*", cors());
|
|
16085
16092
|
}
|
|
16086
16093
|
app5.onError(errorHandler);
|
|
@@ -16239,4 +16246,4 @@ export {
|
|
|
16239
16246
|
EvalAggregator,
|
|
16240
16247
|
createServer
|
|
16241
16248
|
};
|
|
16242
|
-
//# sourceMappingURL=chunk-
|
|
16249
|
+
//# sourceMappingURL=chunk-YGLWYDQ3.js.map
|