@dudousxd/nestjs-agent-dashboard 0.1.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 (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +84 -0
  3. package/dist/client/agent-client.d.ts +78 -0
  4. package/dist/client/agent-client.d.ts.map +1 -0
  5. package/dist/client/agent-client.js +47 -0
  6. package/dist/client/agent-client.js.map +1 -0
  7. package/dist/client/budget-usage.d.ts +23 -0
  8. package/dist/client/budget-usage.d.ts.map +1 -0
  9. package/dist/client/budget-usage.js +24 -0
  10. package/dist/client/budget-usage.js.map +1 -0
  11. package/dist/client/default-range.d.ts +13 -0
  12. package/dist/client/default-range.d.ts.map +1 -0
  13. package/dist/client/default-range.js +26 -0
  14. package/dist/client/default-range.js.map +1 -0
  15. package/dist/client/format-usd.d.ts +8 -0
  16. package/dist/client/format-usd.d.ts.map +1 -0
  17. package/dist/client/format-usd.js +34 -0
  18. package/dist/client/format-usd.js.map +1 -0
  19. package/dist/client/merge-live-events.d.ts +17 -0
  20. package/dist/client/merge-live-events.d.ts.map +1 -0
  21. package/dist/client/merge-live-events.js +19 -0
  22. package/dist/client/merge-live-events.js.map +1 -0
  23. package/dist/client/spend-summary.d.ts +37 -0
  24. package/dist/client/spend-summary.d.ts.map +1 -0
  25. package/dist/client/spend-summary.js +57 -0
  26. package/dist/client/spend-summary.js.map +1 -0
  27. package/dist/client/trend-path.d.ts +27 -0
  28. package/dist/client/trend-path.d.ts.map +1 -0
  29. package/dist/client/trend-path.js +25 -0
  30. package/dist/client/trend-path.js.map +1 -0
  31. package/dist/server/index.cjs +449 -0
  32. package/dist/server/index.cjs.map +1 -0
  33. package/dist/server/index.d.cts +129 -0
  34. package/dist/server/index.d.ts +129 -0
  35. package/dist/server/index.js +416 -0
  36. package/dist/server/index.js.map +1 -0
  37. package/dist/spa/assets/index-DAwWrHBV.css +1 -0
  38. package/dist/spa/assets/index-DUf1WOrt.js +49 -0
  39. package/dist/spa/assets/index-DkSjPfKA.js +1 -0
  40. package/dist/spa/assets/preview-DlXVLsfh.js +1 -0
  41. package/dist/spa/index.html +20 -0
  42. package/dist/spa/preview.html +20 -0
  43. package/package.json +65 -0
@@ -0,0 +1,449 @@
1
+ const __importMetaUrl = require('url').pathToFileURL(__filename).href;
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+
22
+ // src/server/index.ts
23
+ var index_exports = {};
24
+ __export(index_exports, {
25
+ AGENT_GOVERNANCE_QUERIES: () => AGENT_GOVERNANCE_QUERIES,
26
+ AgentApiController: () => AgentApiController,
27
+ AgentApiModule: () => AgentApiModule,
28
+ AgentDashboardModule: () => AgentDashboardModule,
29
+ AgentUiController: () => AgentUiController,
30
+ DASHBOARD_API_PATH: () => DASHBOARD_API_PATH,
31
+ DASHBOARD_BASE_PATH: () => DASHBOARD_BASE_PATH,
32
+ DashboardService: () => DashboardService
33
+ });
34
+ module.exports = __toCommonJS(index_exports);
35
+
36
+ // src/server/agent-api.controller.ts
37
+ var import_common2 = require("@nestjs/common");
38
+
39
+ // src/server/dashboard.service.ts
40
+ var import_node_diagnostics_channel = require("diagnostics_channel");
41
+ var import_nestjs_diagnostics = require("@dudousxd/nestjs-diagnostics");
42
+ var import_common = require("@nestjs/common");
43
+ var import_rxjs = require("rxjs");
44
+
45
+ // src/server/tokens.ts
46
+ var AGENT_GOVERNANCE_QUERIES = Symbol.for("@dudousxd/nestjs-agent:governance-queries");
47
+ var DASHBOARD_BASE_PATH = Symbol.for("@dudousxd/nestjs-agent-dashboard:base-path");
48
+ var DASHBOARD_API_PATH = Symbol.for("@dudousxd/nestjs-agent-dashboard:api-path");
49
+
50
+ // src/server/dashboard.service.ts
51
+ function _ts_decorate(decorators, target, key, desc) {
52
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
53
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
54
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
55
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
56
+ }
57
+ __name(_ts_decorate, "_ts_decorate");
58
+ function _ts_metadata(k, v) {
59
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
60
+ }
61
+ __name(_ts_metadata, "_ts_metadata");
62
+ function _ts_param(paramIndex, decorator) {
63
+ return function(target, key) {
64
+ decorator(target, key, paramIndex);
65
+ };
66
+ }
67
+ __name(_ts_param, "_ts_param");
68
+ var AGENT_EVENTS = [
69
+ "run.started",
70
+ "message",
71
+ "tool-call",
72
+ "quota.exceeded",
73
+ "run.finished",
74
+ "delegated"
75
+ ];
76
+ function isAgentEnvelope(message) {
77
+ return typeof message === "object" && message !== null && "event" in message && typeof message.event === "string";
78
+ }
79
+ __name(isAgentEnvelope, "isAgentEnvelope");
80
+ var DashboardService = class {
81
+ static {
82
+ __name(this, "DashboardService");
83
+ }
84
+ queries;
85
+ constructor(queries) {
86
+ this.queries = queries;
87
+ }
88
+ /** Spend/usage overview for a day range: by-model + by-actor spend and the daily trend, in parallel. */
89
+ async spend(range) {
90
+ const [byModel, byActor, trend] = await Promise.all([
91
+ this.queries.spendByModel(range),
92
+ this.queries.spendByActor(range),
93
+ this.queries.usageTrend(range)
94
+ ]);
95
+ return {
96
+ byModel,
97
+ byActor,
98
+ trend
99
+ };
100
+ }
101
+ /** Most recent tool calls (status/type/thread) for the Runs & tools activity feed. */
102
+ recentToolCalls(limit) {
103
+ return this.queries.recentToolCalls(limit);
104
+ }
105
+ /** Most recent threads with rolled-up message/token counts. */
106
+ recentThreads(limit) {
107
+ return this.queries.recentThreads(limit);
108
+ }
109
+ /**
110
+ * Live SSE stream of `aviary:agent:*` diagnostics events. One subscription per SSE client:
111
+ * subscribing wires a handler onto each agent channel; the returned teardown removes them all when
112
+ * the client disconnects (or the observable is otherwise unsubscribed).
113
+ */
114
+ streamEvents() {
115
+ return new import_rxjs.Observable((subscriber) => {
116
+ const bindings = AGENT_EVENTS.map((event) => {
117
+ const name = (0, import_nestjs_diagnostics.channelName)("agent", event);
118
+ const handler = /* @__PURE__ */ __name((message) => {
119
+ if (!isAgentEnvelope(message)) return;
120
+ subscriber.next({
121
+ data: {
122
+ event: message.event,
123
+ ts: message.ts ?? Date.now(),
124
+ payload: message.payload ?? {}
125
+ }
126
+ });
127
+ }, "handler");
128
+ (0, import_node_diagnostics_channel.subscribe)(name, handler);
129
+ return {
130
+ name,
131
+ handler
132
+ };
133
+ });
134
+ return () => {
135
+ for (const binding of bindings) (0, import_node_diagnostics_channel.unsubscribe)(binding.name, binding.handler);
136
+ };
137
+ });
138
+ }
139
+ };
140
+ DashboardService = _ts_decorate([
141
+ (0, import_common.Injectable)(),
142
+ _ts_param(0, (0, import_common.Inject)(AGENT_GOVERNANCE_QUERIES)),
143
+ _ts_metadata("design:type", Function),
144
+ _ts_metadata("design:paramtypes", [
145
+ typeof AgentGovernanceQueries === "undefined" ? Object : AgentGovernanceQueries
146
+ ])
147
+ ], DashboardService);
148
+
149
+ // src/server/agent-api.controller.ts
150
+ function _ts_decorate2(decorators, target, key, desc) {
151
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
152
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
153
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
154
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
155
+ }
156
+ __name(_ts_decorate2, "_ts_decorate");
157
+ function _ts_metadata2(k, v) {
158
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
159
+ }
160
+ __name(_ts_metadata2, "_ts_metadata");
161
+ function _ts_param2(paramIndex, decorator) {
162
+ return function(target, key) {
163
+ decorator(target, key, paramIndex);
164
+ };
165
+ }
166
+ __name(_ts_param2, "_ts_param");
167
+ var DAY_MS = 864e5;
168
+ var ISO_DAY = /^\d{4}-\d{2}-\d{2}$/;
169
+ function utcDay(daysAgo) {
170
+ return new Date(Date.now() - daysAgo * DAY_MS).toISOString().slice(0, 10);
171
+ }
172
+ __name(utcDay, "utcDay");
173
+ function dayOr(value, fallback) {
174
+ return value !== void 0 && ISO_DAY.test(value) ? value : fallback;
175
+ }
176
+ __name(dayOr, "dayOr");
177
+ function resolveRange(from, to) {
178
+ return {
179
+ fromDay: dayOr(from, utcDay(29)),
180
+ toDay: dayOr(to, utcDay(0))
181
+ };
182
+ }
183
+ __name(resolveRange, "resolveRange");
184
+ function parseLimit(value, fallback) {
185
+ const parsed = value === void 0 ? Number.NaN : Number.parseInt(value, 10);
186
+ if (!Number.isFinite(parsed)) return fallback;
187
+ return Math.max(1, Math.min(200, parsed));
188
+ }
189
+ __name(parseLimit, "parseLimit");
190
+ var AgentApiController = class {
191
+ static {
192
+ __name(this, "AgentApiController");
193
+ }
194
+ dashboard;
195
+ constructor(dashboard) {
196
+ this.dashboard = dashboard;
197
+ }
198
+ /** `{ byModel, byActor, trend }` for a day range (defaults to the last 30 days). */
199
+ spend(from, to) {
200
+ return this.dashboard.spend(resolveRange(from, to));
201
+ }
202
+ /** Most recent tool calls (default 50, max 200) for the activity feed. */
203
+ toolCalls(limit) {
204
+ return this.dashboard.recentToolCalls(parseLimit(limit, 50));
205
+ }
206
+ /** Most recent threads (default 50, max 200) with rolled-up counts. */
207
+ threads(limit) {
208
+ return this.dashboard.recentThreads(parseLimit(limit, 50));
209
+ }
210
+ /** Server-Sent Events stream of live `aviary:agent:*` events — the Live feed tails it. */
211
+ stream() {
212
+ return this.dashboard.streamEvents();
213
+ }
214
+ };
215
+ _ts_decorate2([
216
+ (0, import_common2.Get)("spend"),
217
+ _ts_param2(0, (0, import_common2.Query)("from")),
218
+ _ts_param2(1, (0, import_common2.Query)("to")),
219
+ _ts_metadata2("design:type", Function),
220
+ _ts_metadata2("design:paramtypes", [
221
+ String,
222
+ String
223
+ ]),
224
+ _ts_metadata2("design:returntype", typeof Promise === "undefined" ? Object : Promise)
225
+ ], AgentApiController.prototype, "spend", null);
226
+ _ts_decorate2([
227
+ (0, import_common2.Get)("tool-calls"),
228
+ _ts_param2(0, (0, import_common2.Query)("limit")),
229
+ _ts_metadata2("design:type", Function),
230
+ _ts_metadata2("design:paramtypes", [
231
+ String
232
+ ]),
233
+ _ts_metadata2("design:returntype", typeof Promise === "undefined" ? Object : Promise)
234
+ ], AgentApiController.prototype, "toolCalls", null);
235
+ _ts_decorate2([
236
+ (0, import_common2.Get)("threads"),
237
+ _ts_param2(0, (0, import_common2.Query)("limit")),
238
+ _ts_metadata2("design:type", Function),
239
+ _ts_metadata2("design:paramtypes", [
240
+ String
241
+ ]),
242
+ _ts_metadata2("design:returntype", typeof Promise === "undefined" ? Object : Promise)
243
+ ], AgentApiController.prototype, "threads", null);
244
+ _ts_decorate2([
245
+ (0, import_common2.Sse)("stream"),
246
+ _ts_metadata2("design:type", Function),
247
+ _ts_metadata2("design:paramtypes", []),
248
+ _ts_metadata2("design:returntype", typeof Observable === "undefined" ? Object : Observable)
249
+ ], AgentApiController.prototype, "stream", null);
250
+ AgentApiController = _ts_decorate2([
251
+ (0, import_common2.Controller)(),
252
+ _ts_metadata2("design:type", Function),
253
+ _ts_metadata2("design:paramtypes", [
254
+ typeof DashboardService === "undefined" ? Object : DashboardService
255
+ ])
256
+ ], AgentApiController);
257
+
258
+ // src/server/agent-dashboard.module.ts
259
+ var import_common4 = require("@nestjs/common");
260
+ var import_core = require("@nestjs/core");
261
+
262
+ // src/server/agent-ui.controller.ts
263
+ var import_node_fs = require("fs");
264
+ var import_node_path = require("path");
265
+ var import_node_url = require("url");
266
+ var import_common3 = require("@nestjs/common");
267
+ function _ts_decorate3(decorators, target, key, desc) {
268
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
269
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
270
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
271
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
272
+ }
273
+ __name(_ts_decorate3, "_ts_decorate");
274
+ function _ts_metadata3(k, v) {
275
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
276
+ }
277
+ __name(_ts_metadata3, "_ts_metadata");
278
+ function _ts_param3(paramIndex, decorator) {
279
+ return function(target, key) {
280
+ decorator(target, key, paramIndex);
281
+ };
282
+ }
283
+ __name(_ts_param3, "_ts_param");
284
+ var BUILD_BASE = "/ai-gateway";
285
+ function spaDir() {
286
+ return (0, import_node_url.fileURLToPath)(new URL("../spa", __importMetaUrl));
287
+ }
288
+ __name(spaDir, "spaDir");
289
+ var CONTENT_TYPES = {
290
+ ".js": "text/javascript; charset=utf-8",
291
+ ".css": "text/css; charset=utf-8",
292
+ ".map": "application/json; charset=utf-8",
293
+ ".svg": "image/svg+xml",
294
+ ".json": "application/json; charset=utf-8",
295
+ ".woff2": "font/woff2",
296
+ ".ico": "image/x-icon"
297
+ };
298
+ var AgentUiController = class {
299
+ static {
300
+ __name(this, "AgentUiController");
301
+ }
302
+ basePath;
303
+ apiBasePath;
304
+ dir = spaDir();
305
+ constructor(basePath, apiBasePath) {
306
+ this.basePath = basePath;
307
+ this.apiBasePath = apiBasePath;
308
+ }
309
+ // index.html references hash-named bundles, so it MUST NOT be cached (stale bundle = the classic
310
+ // "stuck loading after a deploy"). The hashed assets below are immutable.
311
+ index() {
312
+ const indexPath = (0, import_node_path.join)(this.dir, "index.html");
313
+ if (!(0, import_node_fs.existsSync)(indexPath)) {
314
+ throw new import_common3.NotFoundException("Dashboard is not built. Run the package build.");
315
+ }
316
+ const html = (0, import_node_fs.readFileSync)(indexPath, "utf8").replaceAll(`="${BUILD_BASE}/`, `="${this.basePath}/`);
317
+ const inject = `<script>window.__AGENT_BASE__='${this.basePath}';window.__AGENT_API__='${this.apiBasePath}';</script>`;
318
+ return html.includes("</head>") ? html.replace("</head>", `${inject}</head>`) : inject + html;
319
+ }
320
+ asset(file) {
321
+ const safe = (0, import_node_path.basename)(file);
322
+ if (safe !== file) throw new import_common3.NotFoundException();
323
+ const root = (0, import_node_path.resolve)(this.dir, "assets");
324
+ const assetPath = (0, import_node_path.resolve)(root, safe);
325
+ if (!assetPath.startsWith(root + import_node_path.sep) || !(0, import_node_fs.existsSync)(assetPath)) {
326
+ throw new import_common3.NotFoundException();
327
+ }
328
+ const type = CONTENT_TYPES[(0, import_node_path.extname)(safe)] ?? "application/octet-stream";
329
+ return new import_common3.StreamableFile((0, import_node_fs.readFileSync)(assetPath), {
330
+ type
331
+ });
332
+ }
333
+ };
334
+ _ts_decorate3([
335
+ (0, import_common3.Get)(),
336
+ (0, import_common3.Header)("Content-Type", "text/html; charset=utf-8"),
337
+ (0, import_common3.Header)("Cache-Control", "no-store, must-revalidate"),
338
+ _ts_metadata3("design:type", Function),
339
+ _ts_metadata3("design:paramtypes", []),
340
+ _ts_metadata3("design:returntype", String)
341
+ ], AgentUiController.prototype, "index", null);
342
+ _ts_decorate3([
343
+ (0, import_common3.Get)("assets/:file"),
344
+ (0, import_common3.Header)("Cache-Control", "public, max-age=31536000, immutable"),
345
+ _ts_param3(0, (0, import_common3.Param)("file")),
346
+ _ts_metadata3("design:type", Function),
347
+ _ts_metadata3("design:paramtypes", [
348
+ String
349
+ ]),
350
+ _ts_metadata3("design:returntype", typeof import_common3.StreamableFile === "undefined" ? Object : import_common3.StreamableFile)
351
+ ], AgentUiController.prototype, "asset", null);
352
+ AgentUiController = _ts_decorate3([
353
+ (0, import_common3.Controller)(),
354
+ _ts_param3(0, (0, import_common3.Inject)(DASHBOARD_BASE_PATH)),
355
+ _ts_param3(1, (0, import_common3.Inject)(DASHBOARD_API_PATH)),
356
+ _ts_metadata3("design:type", Function),
357
+ _ts_metadata3("design:paramtypes", [
358
+ String,
359
+ String
360
+ ])
361
+ ], AgentUiController);
362
+
363
+ // src/server/agent-dashboard.module.ts
364
+ function _ts_decorate4(decorators, target, key, desc) {
365
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
366
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
367
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
368
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
369
+ }
370
+ __name(_ts_decorate4, "_ts_decorate");
371
+ function normalize(path) {
372
+ return `/${path.replace(/^\/+|\/+$/g, "")}`;
373
+ }
374
+ __name(normalize, "normalize");
375
+ var AgentApiModule = class {
376
+ static {
377
+ __name(this, "AgentApiModule");
378
+ }
379
+ };
380
+ AgentApiModule = _ts_decorate4([
381
+ (0, import_common4.Module)({
382
+ controllers: [
383
+ AgentApiController
384
+ ],
385
+ providers: [
386
+ DashboardService
387
+ ],
388
+ exports: [
389
+ DashboardService
390
+ ]
391
+ })
392
+ ], AgentApiModule);
393
+ var AgentDashboardModule = class _AgentDashboardModule {
394
+ static {
395
+ __name(this, "AgentDashboardModule");
396
+ }
397
+ static forRoot(options = {}) {
398
+ const basePath = normalize(options.basePath ?? "/ai-gateway");
399
+ const apiBasePath = normalize(options.apiBasePath ?? `${basePath}/api`);
400
+ return {
401
+ module: _AgentDashboardModule,
402
+ imports: [
403
+ AgentApiModule,
404
+ import_core.RouterModule.register([
405
+ {
406
+ path: basePath,
407
+ module: _AgentDashboardModule
408
+ },
409
+ {
410
+ path: apiBasePath,
411
+ module: AgentApiModule
412
+ }
413
+ ])
414
+ ],
415
+ controllers: [
416
+ AgentUiController
417
+ ],
418
+ providers: [
419
+ {
420
+ provide: DASHBOARD_BASE_PATH,
421
+ useValue: basePath
422
+ },
423
+ {
424
+ provide: DASHBOARD_API_PATH,
425
+ useValue: apiBasePath
426
+ }
427
+ ],
428
+ // Re-export the API module so its DashboardService reaches importers (e.g. the host's own controllers).
429
+ exports: [
430
+ AgentApiModule
431
+ ]
432
+ };
433
+ }
434
+ };
435
+ AgentDashboardModule = _ts_decorate4([
436
+ (0, import_common4.Module)({})
437
+ ], AgentDashboardModule);
438
+ // Annotate the CommonJS export names for ESM import in node:
439
+ 0 && (module.exports = {
440
+ AGENT_GOVERNANCE_QUERIES,
441
+ AgentApiController,
442
+ AgentApiModule,
443
+ AgentDashboardModule,
444
+ AgentUiController,
445
+ DASHBOARD_API_PATH,
446
+ DASHBOARD_BASE_PATH,
447
+ DashboardService
448
+ });
449
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/server/index.ts","../../src/server/agent-api.controller.ts","../../src/server/dashboard.service.ts","../../src/server/tokens.ts","../../src/server/agent-dashboard.module.ts","../../src/server/agent-ui.controller.ts"],"sourcesContent":["export * from './agent-api.controller.js';\nexport * from './agent-dashboard.module.js';\nexport * from './agent-ui.controller.js';\nexport * from './dashboard.service.js';\nexport * from './tokens.js';\n","import type { ThreadActivityRow, ToolCallActivityRow } from '@dudousxd/nestjs-agent-core';\nimport { Controller, Get, Query, Sse } from '@nestjs/common';\nimport type { Observable } from 'rxjs';\nimport { DashboardService, type LiveAgentEvent, type SpendOverview } from './dashboard.service.js';\n\nconst DAY_MS = 86_400_000;\nconst ISO_DAY = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/** A `YYYY-MM-DD` UTC day string `daysAgo` days before now (0 = today). */\nfunction utcDay(daysAgo: number): string {\n return new Date(Date.now() - daysAgo * DAY_MS).toISOString().slice(0, 10);\n}\n\n/** Accept a client-supplied `YYYY-MM-DD`, or fall back to `fallback`; guards against junk input. */\nfunction dayOr(value: string | undefined, fallback: string): string {\n return value !== undefined && ISO_DAY.test(value) ? value : fallback;\n}\n\n/** Resolve the `from`/`to` query params into a validated range, defaulting to the last 30 days. */\nfunction resolveRange(\n from: string | undefined,\n to: string | undefined,\n): {\n fromDay: string;\n toDay: string;\n} {\n return { fromDay: dayOr(from, utcDay(29)), toDay: dayOr(to, utcDay(0)) };\n}\n\n/** Parse a `limit` query param, clamped to a sane window; falls back to `fallback` when absent/junk. */\nfunction parseLimit(value: string | undefined, fallback: number): number {\n const parsed = value === undefined ? Number.NaN : Number.parseInt(value, 10);\n if (!Number.isFinite(parsed)) return fallback;\n return Math.max(1, Math.min(200, parsed));\n}\n\n/**\n * JSON + SSE API consumed by the AI-gateway console SPA. Mounted at `apiBasePath` (set by\n * `RouterModule` in {@link AgentDashboardModule.forRoot}), so the controller routes are relative.\n */\n@Controller()\nexport class AgentApiController {\n constructor(private readonly dashboard: DashboardService) {}\n\n /** `{ byModel, byActor, trend }` for a day range (defaults to the last 30 days). */\n @Get('spend')\n spend(@Query('from') from?: string, @Query('to') to?: string): Promise<SpendOverview> {\n return this.dashboard.spend(resolveRange(from, to));\n }\n\n /** Most recent tool calls (default 50, max 200) for the activity feed. */\n @Get('tool-calls')\n toolCalls(@Query('limit') limit?: string): Promise<ToolCallActivityRow[]> {\n return this.dashboard.recentToolCalls(parseLimit(limit, 50));\n }\n\n /** Most recent threads (default 50, max 200) with rolled-up counts. */\n @Get('threads')\n threads(@Query('limit') limit?: string): Promise<ThreadActivityRow[]> {\n return this.dashboard.recentThreads(parseLimit(limit, 50));\n }\n\n /** Server-Sent Events stream of live `aviary:agent:*` events — the Live feed tails it. */\n @Sse('stream')\n stream(): Observable<{ data: LiveAgentEvent }> {\n return this.dashboard.streamEvents();\n }\n}\n","import { subscribe, unsubscribe } from 'node:diagnostics_channel';\nimport type {\n ActorSpendRow,\n AgentGovernanceQueries,\n GovernanceRange,\n ModelSpendRow,\n ThreadActivityRow,\n ToolCallActivityRow,\n UsageTrendPoint,\n} from '@dudousxd/nestjs-agent-core';\nimport { channelName } from '@dudousxd/nestjs-diagnostics';\nimport { Inject, Injectable } from '@nestjs/common';\nimport { Observable } from 'rxjs';\nimport { AGENT_GOVERNANCE_QUERIES } from './tokens.js';\n\n/** The spend/usage overview the SPA renders on its headline section (`GET <api>/spend`). */\nexport interface SpendOverview {\n byModel: ModelSpendRow[];\n byActor: ActorSpendRow[];\n trend: UsageTrendPoint[];\n}\n\n/** One live agent event forwarded over SSE, flattened from the `aviary:agent:*` diagnostics envelope. */\nexport interface LiveAgentEvent {\n /** The event name, e.g. `run.started` / `tool-call` / `quota.exceeded`. */\n event: string;\n /** Epoch millis the event was emitted. */\n ts: number;\n /** The library-defined payload (see the `Agent*Event` shapes in core's diagnostics). */\n payload: Record<string, unknown>;\n}\n\n/** The `aviary:agent:*` events the Live feed tails. Mirrors the telescope watcher's subscription. */\nconst AGENT_EVENTS = [\n 'run.started',\n 'message',\n 'tool-call',\n 'quota.exceeded',\n 'run.finished',\n 'delegated',\n] as const;\n\n/** The `node:diagnostics_channel` envelope `emit()` publishes (see `@dudousxd/nestjs-diagnostics`). */\ninterface AgentDiagnosticEnvelope {\n event: string;\n ts?: number;\n payload?: Record<string, unknown>;\n}\n\n/** Narrow the untyped diagnostics-channel message to the envelope we forward. */\nfunction isAgentEnvelope(message: unknown): message is AgentDiagnosticEnvelope {\n return (\n typeof message === 'object' &&\n message !== null &&\n 'event' in message &&\n typeof (message as { event: unknown }).event === 'string'\n );\n}\n\n/**\n * Read-model + live bridge backing the AI-gateway console.\n *\n * - Historical, restart-surviving spend/usage/threads come from the injected\n * {@link AGENT_GOVERNANCE_QUERIES} read-model (backed by a store adapter). The host must provide\n * that token — bind it via your `@dudousxd/nestjs-agent` module (global) alongside this dashboard.\n * - Live activity comes off the `aviary:agent:*` diagnostics channel, subscribed per SSE client and\n * unsubscribed when the client disconnects.\n */\n@Injectable()\nexport class DashboardService {\n constructor(@Inject(AGENT_GOVERNANCE_QUERIES) private readonly queries: AgentGovernanceQueries) {}\n\n /** Spend/usage overview for a day range: by-model + by-actor spend and the daily trend, in parallel. */\n async spend(range: GovernanceRange): Promise<SpendOverview> {\n const [byModel, byActor, trend] = await Promise.all([\n this.queries.spendByModel(range),\n this.queries.spendByActor(range),\n this.queries.usageTrend(range),\n ]);\n return { byModel, byActor, trend };\n }\n\n /** Most recent tool calls (status/type/thread) for the Runs & tools activity feed. */\n recentToolCalls(limit: number): Promise<ToolCallActivityRow[]> {\n return this.queries.recentToolCalls(limit);\n }\n\n /** Most recent threads with rolled-up message/token counts. */\n recentThreads(limit: number): Promise<ThreadActivityRow[]> {\n return this.queries.recentThreads(limit);\n }\n\n /**\n * Live SSE stream of `aviary:agent:*` diagnostics events. One subscription per SSE client:\n * subscribing wires a handler onto each agent channel; the returned teardown removes them all when\n * the client disconnects (or the observable is otherwise unsubscribed).\n */\n streamEvents(): Observable<{ data: LiveAgentEvent }> {\n return new Observable<{ data: LiveAgentEvent }>((subscriber) => {\n const bindings = AGENT_EVENTS.map((event) => {\n const name = channelName('agent', event);\n const handler = (message: unknown): void => {\n if (!isAgentEnvelope(message)) return;\n subscriber.next({\n data: {\n event: message.event,\n ts: message.ts ?? Date.now(),\n payload: message.payload ?? {},\n },\n });\n };\n subscribe(name, handler);\n return { name, handler };\n });\n return () => {\n for (const binding of bindings) unsubscribe(binding.name, binding.handler);\n };\n });\n }\n}\n","/**\n * DI tokens for the standalone AI-gateway dashboard.\n *\n * All use `Symbol.for(...)` (the global symbol registry) on purpose: pnpm peer multiplexing + dual\n * ESM/CJS can load a package more than once, and a plain `Symbol()` would mint a distinct token per\n * copy and break DI across the ESM/CJS split. A registered symbol collapses every copy onto the same\n * token.\n */\n\n/**\n * The governance read-model, owned by `@dudousxd/nestjs-agent-core`. We re-declare it here BY VALUE\n * (not by import) so DI does not depend on a runtime value-import of core resolving — `Symbol.for`\n * with the identical key resolves to the SAME symbol instance as core's own\n * `packages/core/src/tokens.ts` export. The key MUST stay byte-identical with that export.\n */\nexport const AGENT_GOVERNANCE_QUERIES = Symbol.for('@dudousxd/nestjs-agent:governance-queries');\n\n/** DI token carrying the UI mount base (e.g. `/ai-gateway`). */\nexport const DASHBOARD_BASE_PATH = Symbol.for('@dudousxd/nestjs-agent-dashboard:base-path');\n\n/** DI token carrying the JSON API base the SPA fetches from (e.g. `/ai-gateway/api`). */\nexport const DASHBOARD_API_PATH = Symbol.for('@dudousxd/nestjs-agent-dashboard:api-path');\n","import { type DynamicModule, Module } from '@nestjs/common';\nimport { RouterModule } from '@nestjs/core';\nimport { AgentApiController } from './agent-api.controller.js';\nimport { AgentUiController } from './agent-ui.controller.js';\nimport { DashboardService } from './dashboard.service.js';\nimport { DASHBOARD_API_PATH, DASHBOARD_BASE_PATH } from './tokens.js';\n\nexport interface AgentDashboardOptions {\n /**\n * Where the SPA (UI) is served. Default `/ai-gateway`. This is a page route — keep it out of an\n * `/api` prefix so it reads as a UI, not an endpoint.\n */\n basePath?: string;\n /**\n * Where the JSON API is mounted (what the SPA fetches). Default `<basePath>/api`. Set it under\n * your app's `/api` prefix — e.g. `/api/ai-gateway` — so the API inherits the app's auth/proxy\n * rules while the UI stays at `basePath`.\n */\n apiBasePath?: string;\n}\n\n/** Leading slash, no trailing slash. */\nfunction normalize(path: string): string {\n return `/${path.replace(/^\\/+|\\/+$/g, '')}`;\n}\n\n/** Holds the JSON API + SSE controller and its read service, mounted on its own path by `forRoot`. */\n@Module({\n controllers: [AgentApiController],\n providers: [DashboardService],\n exports: [DashboardService],\n})\nexport class AgentApiModule {}\n\n/**\n * Mounts the AI-gateway governance console: the bundled React SPA at `basePath` and its JSON + SSE\n * API at `apiBasePath` (default `<basePath>/api`).\n *\n * Import via `AgentDashboardModule.forRoot(...)` alongside your `@dudousxd/nestjs-agent` module\n * (global), which must provide `AGENT_GOVERNANCE_QUERIES` (bound by a store adapter). Front the\n * routes with your own guard — the controllers are path-relative and guard-frontable.\n */\n@Module({})\nexport class AgentDashboardModule {\n static forRoot(options: AgentDashboardOptions = {}): DynamicModule {\n const basePath = normalize(options.basePath ?? '/ai-gateway');\n const apiBasePath = normalize(options.apiBasePath ?? `${basePath}/api`);\n return {\n module: AgentDashboardModule,\n imports: [\n AgentApiModule,\n RouterModule.register([\n { path: basePath, module: AgentDashboardModule }, // the UI controller below\n { path: apiBasePath, module: AgentApiModule },\n ]),\n ],\n controllers: [AgentUiController],\n providers: [\n { provide: DASHBOARD_BASE_PATH, useValue: basePath },\n { provide: DASHBOARD_API_PATH, useValue: apiBasePath },\n ],\n // Re-export the API module so its DashboardService reaches importers (e.g. the host's own controllers).\n exports: [AgentApiModule],\n };\n }\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { basename, extname, join, resolve, sep } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n Controller,\n Get,\n Header,\n Inject,\n NotFoundException,\n Param,\n StreamableFile,\n} from '@nestjs/common';\nimport { DASHBOARD_API_PATH, DASHBOARD_BASE_PATH } from './tokens.js';\n\n/** The base the SPA bundle was built with (Vite `base`); rewritten to the configured base at serve time. */\nconst BUILD_BASE = '/ai-gateway';\n\n/** dist/server/agent-ui.controller.js -> ../spa (the Vite build output). */\nfunction spaDir(): string {\n return fileURLToPath(new URL('../spa', import.meta.url));\n}\n\nconst CONTENT_TYPES: Record<string, string> = {\n '.js': 'text/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.map': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.json': 'application/json; charset=utf-8',\n '.woff2': 'font/woff2',\n '.ico': 'image/x-icon',\n};\n\n/**\n * Serves the bundled AI-gateway console SPA at the configured base (+ hashed assets at\n * `<base>/assets`). The path comes from `RouterModule` (set by\n * {@link AgentDashboardModule.forRoot}({ basePath })), so the controller routes are relative.\n */\n@Controller()\nexport class AgentUiController {\n private readonly dir = spaDir();\n\n constructor(\n @Inject(DASHBOARD_BASE_PATH) private readonly basePath: string,\n @Inject(DASHBOARD_API_PATH) private readonly apiBasePath: string,\n ) {}\n\n // index.html references hash-named bundles, so it MUST NOT be cached (stale bundle = the classic\n // \"stuck loading after a deploy\"). The hashed assets below are immutable.\n @Get()\n @Header('Content-Type', 'text/html; charset=utf-8')\n @Header('Cache-Control', 'no-store, must-revalidate')\n index(): string {\n const indexPath = join(this.dir, 'index.html');\n if (!existsSync(indexPath)) {\n throw new NotFoundException('Dashboard is not built. Run the package build.');\n }\n // The bundle was built with Vite base `/ai-gateway/`; rewrite asset URLs to the configured base\n // so the SPA loads from `<base>/assets` wherever it's mounted, and tell the client its API base.\n const html = readFileSync(indexPath, 'utf8').replaceAll(\n `=\"${BUILD_BASE}/`,\n `=\"${this.basePath}/`,\n );\n // __AGENT_BASE__ = where assets load; __AGENT_API__ = where the SPA fetches the JSON API.\n const inject = `<script>window.__AGENT_BASE__='${this.basePath}';window.__AGENT_API__='${this.apiBasePath}';</script>`;\n return html.includes('</head>') ? html.replace('</head>', `${inject}</head>`) : inject + html;\n }\n\n @Get('assets/:file')\n @Header('Cache-Control', 'public, max-age=31536000, immutable')\n asset(@Param('file') file: string): StreamableFile {\n const safe = basename(file);\n if (safe !== file) throw new NotFoundException();\n const root = resolve(this.dir, 'assets');\n const assetPath = resolve(root, safe);\n if (!assetPath.startsWith(root + sep) || !existsSync(assetPath)) {\n throw new NotFoundException();\n }\n const type = CONTENT_TYPES[extname(safe)] ?? 'application/octet-stream';\n return new StreamableFile(readFileSync(assetPath), { type });\n }\n}\n"],"mappings":"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","names":["import_common","AGENT_GOVERNANCE_QUERIES","Symbol","for","DASHBOARD_BASE_PATH","DASHBOARD_API_PATH","AGENT_EVENTS","isAgentEnvelope","message","event","DashboardService","queries","spend","range","byModel","byActor","trend","Promise","all","spendByModel","spendByActor","usageTrend","recentToolCalls","limit","recentThreads","streamEvents","Observable","subscriber","bindings","map","name","channelName","handler","next","data","ts","Date","now","payload","subscribe","binding","unsubscribe","DAY_MS","ISO_DAY","utcDay","daysAgo","Date","now","toISOString","slice","dayOr","value","fallback","undefined","test","resolveRange","from","to","fromDay","toDay","parseLimit","parsed","Number","NaN","parseInt","isFinite","Math","max","min","AgentApiController","dashboard","spend","toolCalls","limit","recentToolCalls","threads","recentThreads","stream","streamEvents","import_common","import_common","BUILD_BASE","spaDir","fileURLToPath","URL","CONTENT_TYPES","AgentUiController","dir","basePath","apiBasePath","index","indexPath","join","existsSync","NotFoundException","html","readFileSync","replaceAll","inject","includes","replace","asset","file","safe","basename","root","resolve","assetPath","startsWith","sep","type","extname","StreamableFile","normalize","path","replace","AgentApiModule","controllers","AgentApiController","providers","DashboardService","exports","AgentDashboardModule","forRoot","options","basePath","apiBasePath","module","imports","RouterModule","register","AgentUiController","provide","DASHBOARD_BASE_PATH","useValue","DASHBOARD_API_PATH"]}
@@ -0,0 +1,129 @@
1
+ import { AgentGovernanceQueries, GovernanceRange, ModelSpendRow, ActorSpendRow, UsageTrendPoint, ToolCallActivityRow, ThreadActivityRow } from '@dudousxd/nestjs-agent-core';
2
+ import { Observable } from 'rxjs';
3
+ import { DynamicModule, StreamableFile } from '@nestjs/common';
4
+
5
+ /** The spend/usage overview the SPA renders on its headline section (`GET <api>/spend`). */
6
+ interface SpendOverview {
7
+ byModel: ModelSpendRow[];
8
+ byActor: ActorSpendRow[];
9
+ trend: UsageTrendPoint[];
10
+ }
11
+ /** One live agent event forwarded over SSE, flattened from the `aviary:agent:*` diagnostics envelope. */
12
+ interface LiveAgentEvent {
13
+ /** The event name, e.g. `run.started` / `tool-call` / `quota.exceeded`. */
14
+ event: string;
15
+ /** Epoch millis the event was emitted. */
16
+ ts: number;
17
+ /** The library-defined payload (see the `Agent*Event` shapes in core's diagnostics). */
18
+ payload: Record<string, unknown>;
19
+ }
20
+ /**
21
+ * Read-model + live bridge backing the AI-gateway console.
22
+ *
23
+ * - Historical, restart-surviving spend/usage/threads come from the injected
24
+ * {@link AGENT_GOVERNANCE_QUERIES} read-model (backed by a store adapter). The host must provide
25
+ * that token — bind it via your `@dudousxd/nestjs-agent` module (global) alongside this dashboard.
26
+ * - Live activity comes off the `aviary:agent:*` diagnostics channel, subscribed per SSE client and
27
+ * unsubscribed when the client disconnects.
28
+ */
29
+ declare class DashboardService {
30
+ private readonly queries;
31
+ constructor(queries: AgentGovernanceQueries);
32
+ /** Spend/usage overview for a day range: by-model + by-actor spend and the daily trend, in parallel. */
33
+ spend(range: GovernanceRange): Promise<SpendOverview>;
34
+ /** Most recent tool calls (status/type/thread) for the Runs & tools activity feed. */
35
+ recentToolCalls(limit: number): Promise<ToolCallActivityRow[]>;
36
+ /** Most recent threads with rolled-up message/token counts. */
37
+ recentThreads(limit: number): Promise<ThreadActivityRow[]>;
38
+ /**
39
+ * Live SSE stream of `aviary:agent:*` diagnostics events. One subscription per SSE client:
40
+ * subscribing wires a handler onto each agent channel; the returned teardown removes them all when
41
+ * the client disconnects (or the observable is otherwise unsubscribed).
42
+ */
43
+ streamEvents(): Observable<{
44
+ data: LiveAgentEvent;
45
+ }>;
46
+ }
47
+
48
+ /**
49
+ * JSON + SSE API consumed by the AI-gateway console SPA. Mounted at `apiBasePath` (set by
50
+ * `RouterModule` in {@link AgentDashboardModule.forRoot}), so the controller routes are relative.
51
+ */
52
+ declare class AgentApiController {
53
+ private readonly dashboard;
54
+ constructor(dashboard: DashboardService);
55
+ /** `{ byModel, byActor, trend }` for a day range (defaults to the last 30 days). */
56
+ spend(from?: string, to?: string): Promise<SpendOverview>;
57
+ /** Most recent tool calls (default 50, max 200) for the activity feed. */
58
+ toolCalls(limit?: string): Promise<ToolCallActivityRow[]>;
59
+ /** Most recent threads (default 50, max 200) with rolled-up counts. */
60
+ threads(limit?: string): Promise<ThreadActivityRow[]>;
61
+ /** Server-Sent Events stream of live `aviary:agent:*` events — the Live feed tails it. */
62
+ stream(): Observable<{
63
+ data: LiveAgentEvent;
64
+ }>;
65
+ }
66
+
67
+ interface AgentDashboardOptions {
68
+ /**
69
+ * Where the SPA (UI) is served. Default `/ai-gateway`. This is a page route — keep it out of an
70
+ * `/api` prefix so it reads as a UI, not an endpoint.
71
+ */
72
+ basePath?: string;
73
+ /**
74
+ * Where the JSON API is mounted (what the SPA fetches). Default `<basePath>/api`. Set it under
75
+ * your app's `/api` prefix — e.g. `/api/ai-gateway` — so the API inherits the app's auth/proxy
76
+ * rules while the UI stays at `basePath`.
77
+ */
78
+ apiBasePath?: string;
79
+ }
80
+ /** Holds the JSON API + SSE controller and its read service, mounted on its own path by `forRoot`. */
81
+ declare class AgentApiModule {
82
+ }
83
+ /**
84
+ * Mounts the AI-gateway governance console: the bundled React SPA at `basePath` and its JSON + SSE
85
+ * API at `apiBasePath` (default `<basePath>/api`).
86
+ *
87
+ * Import via `AgentDashboardModule.forRoot(...)` alongside your `@dudousxd/nestjs-agent` module
88
+ * (global), which must provide `AGENT_GOVERNANCE_QUERIES` (bound by a store adapter). Front the
89
+ * routes with your own guard — the controllers are path-relative and guard-frontable.
90
+ */
91
+ declare class AgentDashboardModule {
92
+ static forRoot(options?: AgentDashboardOptions): DynamicModule;
93
+ }
94
+
95
+ /**
96
+ * Serves the bundled AI-gateway console SPA at the configured base (+ hashed assets at
97
+ * `<base>/assets`). The path comes from `RouterModule` (set by
98
+ * {@link AgentDashboardModule.forRoot}({ basePath })), so the controller routes are relative.
99
+ */
100
+ declare class AgentUiController {
101
+ private readonly basePath;
102
+ private readonly apiBasePath;
103
+ private readonly dir;
104
+ constructor(basePath: string, apiBasePath: string);
105
+ index(): string;
106
+ asset(file: string): StreamableFile;
107
+ }
108
+
109
+ /**
110
+ * DI tokens for the standalone AI-gateway dashboard.
111
+ *
112
+ * All use `Symbol.for(...)` (the global symbol registry) on purpose: pnpm peer multiplexing + dual
113
+ * ESM/CJS can load a package more than once, and a plain `Symbol()` would mint a distinct token per
114
+ * copy and break DI across the ESM/CJS split. A registered symbol collapses every copy onto the same
115
+ * token.
116
+ */
117
+ /**
118
+ * The governance read-model, owned by `@dudousxd/nestjs-agent-core`. We re-declare it here BY VALUE
119
+ * (not by import) so DI does not depend on a runtime value-import of core resolving — `Symbol.for`
120
+ * with the identical key resolves to the SAME symbol instance as core's own
121
+ * `packages/core/src/tokens.ts` export. The key MUST stay byte-identical with that export.
122
+ */
123
+ declare const AGENT_GOVERNANCE_QUERIES: unique symbol;
124
+ /** DI token carrying the UI mount base (e.g. `/ai-gateway`). */
125
+ declare const DASHBOARD_BASE_PATH: unique symbol;
126
+ /** DI token carrying the JSON API base the SPA fetches from (e.g. `/ai-gateway/api`). */
127
+ declare const DASHBOARD_API_PATH: unique symbol;
128
+
129
+ export { AGENT_GOVERNANCE_QUERIES, AgentApiController, AgentApiModule, AgentDashboardModule, type AgentDashboardOptions, AgentUiController, DASHBOARD_API_PATH, DASHBOARD_BASE_PATH, DashboardService, type LiveAgentEvent, type SpendOverview };