@dudousxd/nestjs-agent-dashboard 0.21.0 → 0.23.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.
@@ -73,12 +73,37 @@ function isFiniteNonNegative(value) {
73
73
  }
74
74
  __name(isFiniteNonNegative, "isFiniteNonNegative");
75
75
 
76
+ // src/server/tenancy.ts
77
+ var SLOT = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-diagnostics:accessor");
78
+ function consoleTenantFilter(option = "inherit", accessor) {
79
+ const slot = globalThis[SLOT];
80
+ const a = accessor ?? slot?.current;
81
+ if (option === false || typeof a?.tenancy !== "function" || !a.tenantFilter) return void 0;
82
+ const own = typeof option === "object" ? option : {};
83
+ const inherit = own.enabled === void 0 || own.enabled === "inherit";
84
+ if (!(inherit ? a.tenancy().enabled : own.enabled)) return void 0;
85
+ const filter = a.tenantFilter(own.kinds ? {
86
+ kinds: own.kinds
87
+ } : void 0);
88
+ if (filter.mode === "system" || filter.mode === "all") return void 0;
89
+ if (filter.mode === "tenantless") return {
90
+ nulls: true
91
+ };
92
+ return {
93
+ tenant: filter.tenant.id,
94
+ nulls: own.nullRows === "shared"
95
+ };
96
+ }
97
+ __name(consoleTenantFilter, "consoleTenantFilter");
98
+
76
99
  // src/server/tokens.ts
77
100
  var AGENT_GOVERNANCE_QUERIES = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:governance-queries");
78
101
  var AGENT_ACTOR_DIRECTORY = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:actor-directory");
79
102
  var AGENT_PRICING_STORE = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:pricing-store");
80
103
  var AGENT_APPROVAL_PORT = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:approval-port");
81
104
  var AGENT_ACTOR_RESOLVER = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:actor-resolver");
105
+ var AGENT_OPTIONS = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:options");
106
+ var CONTEXT_ACCESSOR = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-context:accessor");
82
107
  var DASHBOARD_BASE_PATH = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent-dashboard:base-path");
83
108
  var DASHBOARD_API_PATH = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent-dashboard:api-path");
84
109
  var DASHBOARD_APPROVAL_ACTOR_REF = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent-dashboard:approval-actor-ref");
@@ -114,6 +139,7 @@ function _ts_param(paramIndex, decorator) {
114
139
  __name(_ts_param, "_ts_param");
115
140
  var PRICING_STORE_UNBOUND_MESSAGE = "Pricing CRUD is unavailable: no AGENT_PRICING_STORE is bound. Bind a pricing store (e.g. MikroOrmPricingStore from @dudousxd/nestjs-agent-store-mikro-orm) to enable it.";
116
141
  var APPROVAL_PORT_UNBOUND_MESSAGE = "Approve/reject is unavailable: no AGENT_APPROVAL_PORT is bound. Import AgentModule from @dudousxd/nestjs-agent alongside this dashboard to enable it \u2014 the approvals inbox stays read-only until then.";
142
+ var TENANT_SCOPE_UNSUPPORTED_MESSAGE = "The agent console is tenant-scoped (AgentModule tenancy is on), and the bound AGENT_GOVERNANCE_QUERIES store does not implement forTenant. Use a store adapter that does (Drizzle, MikroORM, in-memory) or set AgentModule `tenancy: false`.";
117
143
  var GENUI_STATS_UNSUPPORTED_MESSAGE = "Generative UI telemetry is unavailable: the bound AGENT_GOVERNANCE_QUERIES store does not implement genuiStats. Use a store adapter that records generative-UI calls (recordGenuiCalls) to enable it.";
118
144
  var AGENT_EVENTS = [
119
145
  "run.started",
@@ -137,19 +163,37 @@ var DashboardService = class {
137
163
  actorDirectory;
138
164
  pricingStore;
139
165
  approvalPort;
140
- constructor(queries, actorDirectory, pricingStore, approvalPort) {
166
+ agentOptions;
167
+ contextAccessor;
168
+ constructor(queries, actorDirectory, pricingStore, approvalPort, agentOptions, contextAccessor) {
141
169
  this.queries = queries;
142
170
  this.actorDirectory = actorDirectory;
143
171
  this.pricingStore = pricingStore;
144
172
  this.approvalPort = approvalPort;
173
+ this.agentOptions = agentOptions;
174
+ this.contextAccessor = contextAccessor;
175
+ }
176
+ /**
177
+ * The read-model for this request: with the agent's tenancy on, narrowed to the viewer's context
178
+ * tenant (another tenant's thread or run is not found). A store without `forTenant` cannot be
179
+ * narrowed, so the console refuses (`501`) rather than show every tenant's rows.
180
+ */
181
+ scoped() {
182
+ const filter = consoleTenantFilter(this.agentOptions?.tenancy ?? "inherit", this.contextAccessor);
183
+ if (filter === void 0) return this.queries;
184
+ if (typeof this.queries.forTenant !== "function") {
185
+ throw new NotImplementedException(TENANT_SCOPE_UNSUPPORTED_MESSAGE);
186
+ }
187
+ return this.queries.forTenant(filter);
145
188
  }
146
189
  /** Spend/usage overview for a day range: by-model + by-actor spend and the daily trend, in parallel. */
147
190
  async spend(range) {
191
+ const queries = this.scoped();
148
192
  const [byModel, byActorRaw, trend, byFeature] = await Promise.all([
149
- this.queries.spendByModel(range),
150
- this.queries.spendByActor(range),
151
- this.queries.usageTrend(range),
152
- this.queries.spendByFeature?.(range)
193
+ queries.spendByModel(range),
194
+ queries.spendByActor(range),
195
+ queries.usageTrend(range),
196
+ queries.spendByFeature?.(range)
153
197
  ]);
154
198
  const byActor = await this.withActorLabels(byActorRaw);
155
199
  return {
@@ -163,16 +207,17 @@ var DashboardService = class {
163
207
  }
164
208
  /** Top threads by cost for a day range (default 10, highest cost first). */
165
209
  async topThreads(range, limit2 = 10) {
166
- const rows = await this.queries.spendByThread(range, limit2);
210
+ const rows = await this.scoped().spendByThread(range, limit2);
167
211
  return this.withActorLabels(rows);
168
212
  }
169
213
  /** Run reliability for a day range: metrics, by-agent/by-error breakdowns and the trend, in parallel. */
170
214
  async reliability(range) {
215
+ const queries = this.scoped();
171
216
  const [metrics, byAgent, errors, trend] = await Promise.all([
172
- this.queries.runMetrics(range),
173
- this.queries.runsByAgent(range),
174
- this.queries.runErrors(range),
175
- this.queries.runTrend(range)
217
+ queries.runMetrics(range),
218
+ queries.runsByAgent(range),
219
+ queries.runErrors(range),
220
+ queries.runTrend(range)
176
221
  ]);
177
222
  return {
178
223
  metrics,
@@ -183,19 +228,19 @@ var DashboardService = class {
183
228
  }
184
229
  /** Most recent runs (status/agent/duration/error) for the Reliability recent-runs table. */
185
230
  recentRuns(limit2) {
186
- return this.queries.recentRuns(limit2);
231
+ return this.scoped().recentRuns(limit2);
187
232
  }
188
233
  /** Most recent tool calls (status/type/thread) for the Runs & tools activity feed. */
189
234
  recentToolCalls(limit2) {
190
- return this.queries.recentToolCalls(limit2);
235
+ return this.scoped().recentToolCalls(limit2);
191
236
  }
192
237
  /** Paged, filterable tool calls for the Runs & tools activity table. */
193
238
  toolCallsPage(query) {
194
- return this.queries.toolCallsPage(query);
239
+ return this.scoped().toolCallsPage(query);
195
240
  }
196
241
  /** Paged, filterable runs for the Reliability recent-runs table. */
197
242
  runsPage(query) {
198
- return this.queries.runsPage(query);
243
+ return this.scoped().runsPage(query);
199
244
  }
200
245
  /**
201
246
  * Tool calls sitting `pending_approval`, oldest first, for the cross-thread approvals inbox.
@@ -203,11 +248,11 @@ var DashboardService = class {
203
248
  * {@link approvalsPage}, which reports how much of the backlog the cap hid.
204
249
  */
205
250
  pendingApprovals(limit2) {
206
- return this.queries.pendingApprovals(limit2);
251
+ return this.scoped().pendingApprovals(limit2);
207
252
  }
208
253
  /** Paged, filterable approvals inbox with a total — the backlog-safe read of the HITL queue. */
209
254
  approvalsPage(query) {
210
- return this.queries.approvalsPage(query);
255
+ return this.scoped().approvalsPage(query);
211
256
  }
212
257
  /**
213
258
  * One run with its thread and its tool calls. 404s on an unknown id rather than returning an empty
@@ -215,7 +260,7 @@ var DashboardService = class {
215
260
  * that conflates them sends an operator looking for a bug that isn't there.
216
261
  */
217
262
  async runDetail(runId) {
218
- const detail = await this.queries.runDetail(runId);
263
+ const detail = await this.scoped().runDetail(runId);
219
264
  if (detail === null) {
220
265
  throw new NotFoundException(`No run "${runId}".`);
221
266
  }
@@ -232,7 +277,7 @@ var DashboardService = class {
232
277
  }
233
278
  /** One thread with its usage rollup, runs and messages. 404s on an unknown id, like {@link runDetail}. */
234
279
  async threadDetail(query) {
235
- const detail = await this.queries.threadDetail(query);
280
+ const detail = await this.scoped().threadDetail(query);
236
281
  if (detail === null) {
237
282
  throw new NotFoundException(`No thread "${query.threadId}".`);
238
283
  }
@@ -249,7 +294,7 @@ var DashboardService = class {
249
294
  }
250
295
  /** Per-tool call/failure/rejection/latency rollup for a day range, for the Tools section. */
251
296
  toolStats(range) {
252
- return this.queries.toolStats(range);
297
+ return this.scoped().toolStats(range);
253
298
  }
254
299
  /**
255
300
  * Generative-UI telemetry for a day range — first-try success per tool/model, common errors, UI
@@ -258,10 +303,11 @@ var DashboardService = class {
258
303
  * same posture as an unbound pricing store — the console renders that as its own state.
259
304
  */
260
305
  async genuiStats(range) {
261
- if (this.queries.genuiStats === void 0) {
306
+ const queries = this.scoped();
307
+ if (queries.genuiStats === void 0) {
262
308
  throw new NotImplementedException(GENUI_STATS_UNSUPPORTED_MESSAGE);
263
309
  }
264
- return this.queries.genuiStats(range);
310
+ return queries.genuiStats(range);
265
311
  }
266
312
  /**
267
313
  * Decide a pending HITL tool call from the console. Routes through the OPTIONAL
@@ -292,7 +338,7 @@ var DashboardService = class {
292
338
  }
293
339
  /** Most recent threads with rolled-up message/token counts. */
294
340
  async recentThreads(limit2) {
295
- const rows = await this.queries.recentThreads(limit2);
341
+ const rows = await this.scoped().recentThreads(limit2);
296
342
  return this.withActorLabels(rows);
297
343
  }
298
344
  /**
@@ -300,7 +346,7 @@ var DashboardService = class {
300
346
  * {@link recentThreads} is.
301
347
  */
302
348
  async threadsPage(query) {
303
- const page2 = await this.queries.threadsPage(query);
349
+ const page2 = await this.scoped().threadsPage(query);
304
350
  const rows = await this.withActorLabels(page2.rows);
305
351
  return {
306
352
  ...page2,
@@ -389,12 +435,18 @@ DashboardService = _ts_decorate([
389
435
  _ts_param(2, Inject(AGENT_PRICING_STORE)),
390
436
  _ts_param(3, Optional()),
391
437
  _ts_param(3, Inject(AGENT_APPROVAL_PORT)),
438
+ _ts_param(4, Optional()),
439
+ _ts_param(4, Inject(AGENT_OPTIONS)),
440
+ _ts_param(5, Optional()),
441
+ _ts_param(5, Inject(CONTEXT_ACCESSOR)),
392
442
  _ts_metadata("design:type", Function),
393
443
  _ts_metadata("design:paramtypes", [
394
444
  typeof AgentGovernanceQueries === "undefined" ? Object : AgentGovernanceQueries,
395
445
  typeof ActorDirectory === "undefined" ? Object : ActorDirectory,
396
446
  typeof AgentPricingStore === "undefined" ? Object : AgentPricingStore,
397
- typeof AgentApprovalPort === "undefined" ? Object : AgentApprovalPort
447
+ typeof AgentApprovalPort === "undefined" ? Object : AgentApprovalPort,
448
+ Object,
449
+ Object
398
450
  ])
399
451
  ], DashboardService);
400
452
 
@@ -1052,8 +1104,8 @@ import { RouterModule } from "@nestjs/core";
1052
1104
  import { existsSync, readFileSync } from "fs";
1053
1105
  import { basename, extname, join, resolve, sep } from "path";
1054
1106
  import { fileURLToPath } from "url";
1055
- import { CanAnyone as CanAnyone2 } from "@dudousxd/nestjs-agent-core";
1056
- import { Controller as Controller2, Get as Get2, Header, Inject as Inject4, NotFoundException as NotFoundException2, Param as Param2, Req as Req2, Res, StreamableFile } from "@nestjs/common";
1107
+ import { CanAnyone as CanAnyone3 } from "@dudousxd/nestjs-agent-core";
1108
+ import { Controller as Controller3, Get as Get3, Header as Header2, Inject as Inject5, NotFoundException as NotFoundException2, Param as Param2, Req as Req2, Res, StreamableFile } from "@nestjs/common";
1057
1109
 
1058
1110
  // src/server/console-authorize.guard.ts
1059
1111
  import { Catch, HttpException, Inject as Inject3, Injectable as Injectable2, SetMetadata, UseFilters, UseGuards, applyDecorators } from "@nestjs/common";
@@ -1442,7 +1494,9 @@ function UseConsoleAuthorize() {
1442
1494
  }
1443
1495
  __name(UseConsoleAuthorize, "UseConsoleAuthorize");
1444
1496
 
1445
- // src/server/agent-ui.controller.ts
1497
+ // src/server/console-switcher.ts
1498
+ import { CanAnyone as CanAnyone2 } from "@dudousxd/nestjs-agent-core";
1499
+ import { Controller as Controller2, Get as Get2, Header, Inject as Inject4, Injectable as Injectable3, Logger as Logger2, createParamDecorator } from "@nestjs/common";
1446
1500
  function _ts_decorate4(decorators, target, key, desc) {
1447
1501
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1448
1502
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
@@ -1469,7 +1523,186 @@ function _ts_param4(paramIndex, decorator) {
1469
1523
  };
1470
1524
  }
1471
1525
  __name(_ts_param4, "_ts_param");
1526
+ var AVIARY_CONSOLES_SLOT = /* @__PURE__ */ Symbol.for("@dudousxd/aviary:consoles");
1527
+ var ID = /^[a-z][a-z0-9-]*$/;
1528
+ var ICONS = /* @__PURE__ */ new Set([
1529
+ "workflow",
1530
+ "telescope",
1531
+ "media",
1532
+ "payments",
1533
+ "agent",
1534
+ "identity",
1535
+ "generic"
1536
+ ]);
1537
+ var CONSOLE_PROBE_TIMEOUT_MS = 300;
1538
+ function isSameOriginPath(path) {
1539
+ return typeof path === "string" && path.startsWith("/") && !path.startsWith("//");
1540
+ }
1541
+ __name(isSameOriginPath, "isSameOriginPath");
1542
+ function readAviaryConsoles() {
1543
+ const value = globalThis[AVIARY_CONSOLES_SLOT];
1544
+ if (value?.version !== 1 || !Array.isArray(value.entries)) return void 0;
1545
+ return value;
1546
+ }
1547
+ __name(readAviaryConsoles, "readAviaryConsoles");
1548
+ function registerAviaryConsole(entry, logger4) {
1549
+ if (!ID.test(entry.id)) throw new Error(`console id ${JSON.stringify(entry.id)} is invalid`);
1550
+ if (!isSameOriginPath(entry.path)) {
1551
+ throw new Error(`console path ${JSON.stringify(entry.path)} must start with a single "/"`);
1552
+ }
1553
+ const holder = globalThis;
1554
+ if (holder[AVIARY_CONSOLES_SLOT] === void 0) {
1555
+ const fresh = {
1556
+ version: 1,
1557
+ entries: []
1558
+ };
1559
+ holder[AVIARY_CONSOLES_SLOT] = fresh;
1560
+ }
1561
+ const registry = readAviaryConsoles();
1562
+ if (registry === void 0) return void 0;
1563
+ if (registry.entries.some((existing) => existing.id === entry.id)) {
1564
+ logger4?.warn(`console id "${entry.id}" is already registered`);
1565
+ return void 0;
1566
+ }
1567
+ registry.entries.push(entry);
1568
+ return () => {
1569
+ const index = registry.entries.indexOf(entry);
1570
+ if (index !== -1) registry.entries.splice(index, 1);
1571
+ };
1572
+ }
1573
+ __name(registerAviaryConsole, "registerAviaryConsole");
1574
+ function consoleSwitcherConfig(selfId, endpoint) {
1575
+ const registry = readAviaryConsoles();
1576
+ if (!registry?.entries.some((entry) => entry.id === selfId)) return void 0;
1577
+ const others = registry.entries.filter((entry) => entry.id !== selfId).length;
1578
+ return others > 0 ? {
1579
+ endpoint,
1580
+ others
1581
+ } : void 0;
1582
+ }
1583
+ __name(consoleSwitcherConfig, "consoleSwitcherConfig");
1584
+ async function allows(entry, context, ms) {
1585
+ let timer;
1586
+ const timeout = new Promise((resolve2) => {
1587
+ timer = setTimeout(() => resolve2(false), ms);
1588
+ });
1589
+ try {
1590
+ return await Promise.race([
1591
+ Promise.resolve().then(() => entry.authorize(context)).then((decision) => decision?.allowed === true, () => false),
1592
+ timeout
1593
+ ]);
1594
+ } finally {
1595
+ clearTimeout(timer);
1596
+ }
1597
+ }
1598
+ __name(allows, "allows");
1599
+ async function listVisibleConsoles(registry, selfId, context, timeoutMs = CONSOLE_PROBE_TIMEOUT_MS) {
1600
+ const others = (registry?.entries ?? []).filter((entry) => entry.id !== selfId && isSameOriginPath(entry.path)).sort((a, b) => a.order - b.order || a.id.localeCompare(b.id));
1601
+ const verdicts = await Promise.all(others.map((entry) => allows(entry, context, timeoutMs)));
1602
+ return {
1603
+ version: 1,
1604
+ current: selfId,
1605
+ consoles: others.filter((_, index) => verdicts[index]).map((entry) => ({
1606
+ id: entry.id,
1607
+ title: entry.title,
1608
+ icon: ICONS.has(entry.icon) ? entry.icon : "generic",
1609
+ href: entry.path
1610
+ }))
1611
+ };
1612
+ }
1613
+ __name(listVisibleConsoles, "listVisibleConsoles");
1614
+ var AGENT_CONSOLE_ID = "agent";
1615
+ var logger2 = new Logger2("AgentDashboardModule");
1616
+ var AgentConsoleRegistration = class {
1617
+ static {
1618
+ __name(this, "AgentConsoleRegistration");
1619
+ }
1620
+ basePath;
1621
+ access;
1622
+ #unregister;
1623
+ constructor(basePath, access) {
1624
+ this.basePath = basePath;
1625
+ this.access = access;
1626
+ }
1627
+ onModuleInit() {
1628
+ this.#unregister = registerAviaryConsole({
1629
+ id: AGENT_CONSOLE_ID,
1630
+ title: "Agents",
1631
+ icon: "agent",
1632
+ order: 30,
1633
+ path: this.basePath === "" ? "/" : this.basePath,
1634
+ authorize: /* @__PURE__ */ __name((context) => this.access.check(context), "authorize")
1635
+ }, logger2);
1636
+ }
1637
+ onModuleDestroy() {
1638
+ this.#unregister?.();
1639
+ this.#unregister = void 0;
1640
+ }
1641
+ };
1642
+ AgentConsoleRegistration = _ts_decorate4([
1643
+ Injectable3(),
1644
+ _ts_param4(0, Inject4(DASHBOARD_BASE_PATH)),
1645
+ _ts_metadata4("design:type", Function),
1646
+ _ts_metadata4("design:paramtypes", [
1647
+ String,
1648
+ typeof DashboardConsoleAccess === "undefined" ? Object : DashboardConsoleAccess
1649
+ ])
1650
+ ], AgentConsoleRegistration);
1651
+ var CurrentExecutionContext = createParamDecorator((_data, context) => ({
1652
+ context
1653
+ }));
1654
+ var AgentConsolesController = class {
1655
+ static {
1656
+ __name(this, "AgentConsolesController");
1657
+ }
1658
+ consoles(current) {
1659
+ return listVisibleConsoles(readAviaryConsoles(), AGENT_CONSOLE_ID, current.context);
1660
+ }
1661
+ };
1662
+ _ts_decorate4([
1663
+ Get2("consoles"),
1664
+ Header("Cache-Control", "private, no-store"),
1665
+ _ts_param4(0, CurrentExecutionContext()),
1666
+ _ts_metadata4("design:type", Function),
1667
+ _ts_metadata4("design:paramtypes", [
1668
+ typeof CurrentContext === "undefined" ? Object : CurrentContext
1669
+ ]),
1670
+ _ts_metadata4("design:returntype", typeof Promise === "undefined" ? Object : Promise)
1671
+ ], AgentConsolesController.prototype, "consoles", null);
1672
+ AgentConsolesController = _ts_decorate4([
1673
+ CanAnyone2(),
1674
+ Controller2()
1675
+ ], AgentConsolesController);
1676
+
1677
+ // src/server/agent-ui.controller.ts
1678
+ function _ts_decorate5(decorators, target, key, desc) {
1679
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1680
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
1681
+ r = Reflect.decorate(decorators, target, key, desc);
1682
+ } else {
1683
+ for (var i = decorators.length - 1; i >= 0; i--) {
1684
+ if (d = decorators[i]) {
1685
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1686
+ }
1687
+ }
1688
+ }
1689
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1690
+ }
1691
+ __name(_ts_decorate5, "_ts_decorate");
1692
+ function _ts_metadata5(metadataKey, metadataValue) {
1693
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
1694
+ return Reflect.metadata(metadataKey, metadataValue);
1695
+ }
1696
+ }
1697
+ __name(_ts_metadata5, "_ts_metadata");
1698
+ function _ts_param5(paramIndex, decorator) {
1699
+ return function(target, key) {
1700
+ decorator(target, key, paramIndex);
1701
+ };
1702
+ }
1703
+ __name(_ts_param5, "_ts_param");
1472
1704
  var BUILD_BASE = "/ai-gateway";
1705
+ var AGENT_DASHBOARD_CONFIG_ID = "agent-dashboard-config";
1473
1706
  function spaDir() {
1474
1707
  return fileURLToPath(new URL("../spa", import.meta.url));
1475
1708
  }
@@ -1507,9 +1740,23 @@ var AgentUiController = class {
1507
1740
  throw new NotFoundException2("Dashboard is not built. Run the package build.");
1508
1741
  }
1509
1742
  const html = readFileSync(indexPath, "utf8").replaceAll(`="${BUILD_BASE}/`, `="${this.basePath}/`);
1510
- const inject = `<script>window.__AGENT_BASE__='${this.basePath}';window.__AGENT_API__='${this.apiBasePath}';</script>`;
1743
+ const inject = `<script>window.__AGENT_BASE__='${this.basePath}';window.__AGENT_API__='${this.apiBasePath}';</script>${this.configBlock()}`;
1511
1744
  return html.includes("</head>") ? html.replace("</head>", `${inject}</head>`) : inject + html;
1512
1745
  }
1746
+ /**
1747
+ * The page's JSON config block (`agent-dashboard-config`, CSP-safe: data, not script). It carries
1748
+ * `switcher` only when another Aviary console is registered.
1749
+ */
1750
+ configBlock() {
1751
+ const switcher = consoleSwitcherConfig(AGENT_CONSOLE_ID, `${this.apiBasePath}/consoles`);
1752
+ const json = JSON.stringify({
1753
+ apiBase: this.apiBasePath,
1754
+ ...switcher !== void 0 ? {
1755
+ switcher
1756
+ } : {}
1757
+ }).replaceAll("<", "\\u003c");
1758
+ return `<script type="application/json" id="${AGENT_DASHBOARD_CONFIG_ID}">${json}</script>`;
1759
+ }
1513
1760
  asset(file) {
1514
1761
  const safe = basename(file);
1515
1762
  if (safe !== file) throw new NotFoundException2();
@@ -1524,47 +1771,47 @@ var AgentUiController = class {
1524
1771
  });
1525
1772
  }
1526
1773
  };
1527
- _ts_decorate4([
1528
- Get2(),
1774
+ _ts_decorate5([
1775
+ Get3(),
1529
1776
  ConsolePage(),
1530
- Header("Content-Type", "text/html; charset=utf-8"),
1531
- Header("Cache-Control", "no-store, must-revalidate"),
1532
- _ts_param4(0, Req2()),
1533
- _ts_param4(1, Res({
1777
+ Header2("Content-Type", "text/html; charset=utf-8"),
1778
+ Header2("Cache-Control", "no-store, must-revalidate"),
1779
+ _ts_param5(0, Req2()),
1780
+ _ts_param5(1, Res({
1534
1781
  passthrough: true
1535
1782
  })),
1536
- _ts_metadata4("design:type", Function),
1537
- _ts_metadata4("design:paramtypes", [
1783
+ _ts_metadata5("design:type", Function),
1784
+ _ts_metadata5("design:paramtypes", [
1538
1785
  typeof UiPageRequest === "undefined" ? Object : UiPageRequest,
1539
1786
  typeof UiPageResponse === "undefined" ? Object : UiPageResponse
1540
1787
  ]),
1541
- _ts_metadata4("design:returntype", String)
1788
+ _ts_metadata5("design:returntype", String)
1542
1789
  ], AgentUiController.prototype, "index", null);
1543
- _ts_decorate4([
1544
- Get2("assets/:file"),
1545
- Header("Cache-Control", "public, max-age=31536000, immutable"),
1546
- _ts_param4(0, Param2("file")),
1547
- _ts_metadata4("design:type", Function),
1548
- _ts_metadata4("design:paramtypes", [
1790
+ _ts_decorate5([
1791
+ Get3("assets/:file"),
1792
+ Header2("Cache-Control", "public, max-age=31536000, immutable"),
1793
+ _ts_param5(0, Param2("file")),
1794
+ _ts_metadata5("design:type", Function),
1795
+ _ts_metadata5("design:paramtypes", [
1549
1796
  String
1550
1797
  ]),
1551
- _ts_metadata4("design:returntype", typeof StreamableFile === "undefined" ? Object : StreamableFile)
1798
+ _ts_metadata5("design:returntype", typeof StreamableFile === "undefined" ? Object : StreamableFile)
1552
1799
  ], AgentUiController.prototype, "asset", null);
1553
- AgentUiController = _ts_decorate4([
1554
- CanAnyone2(),
1555
- Controller2(),
1556
- _ts_param4(0, Inject4(DASHBOARD_BASE_PATH)),
1557
- _ts_param4(1, Inject4(DASHBOARD_API_PATH)),
1558
- _ts_metadata4("design:type", Function),
1559
- _ts_metadata4("design:paramtypes", [
1800
+ AgentUiController = _ts_decorate5([
1801
+ CanAnyone3(),
1802
+ Controller3(),
1803
+ _ts_param5(0, Inject5(DASHBOARD_BASE_PATH)),
1804
+ _ts_param5(1, Inject5(DASHBOARD_API_PATH)),
1805
+ _ts_metadata5("design:type", Function),
1806
+ _ts_metadata5("design:paramtypes", [
1560
1807
  String,
1561
1808
  String
1562
1809
  ])
1563
1810
  ], AgentUiController);
1564
1811
 
1565
1812
  // src/server/dev-tools.ts
1566
- import { Inject as Inject5, Injectable as Injectable3, Logger as Logger2, NotFoundException as NotFoundException3 } from "@nestjs/common";
1567
- function _ts_decorate5(decorators, target, key, desc) {
1813
+ import { Inject as Inject6, Injectable as Injectable4, Logger as Logger3, NotFoundException as NotFoundException3 } from "@nestjs/common";
1814
+ function _ts_decorate6(decorators, target, key, desc) {
1568
1815
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1569
1816
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
1570
1817
  r = Reflect.decorate(decorators, target, key, desc);
@@ -1577,19 +1824,19 @@ function _ts_decorate5(decorators, target, key, desc) {
1577
1824
  }
1578
1825
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1579
1826
  }
1580
- __name(_ts_decorate5, "_ts_decorate");
1581
- function _ts_metadata5(metadataKey, metadataValue) {
1827
+ __name(_ts_decorate6, "_ts_decorate");
1828
+ function _ts_metadata6(metadataKey, metadataValue) {
1582
1829
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
1583
1830
  return Reflect.metadata(metadataKey, metadataValue);
1584
1831
  }
1585
1832
  }
1586
- __name(_ts_metadata5, "_ts_metadata");
1587
- function _ts_param5(paramIndex, decorator) {
1833
+ __name(_ts_metadata6, "_ts_metadata");
1834
+ function _ts_param6(paramIndex, decorator) {
1588
1835
  return function(target, key) {
1589
1836
  decorator(target, key, paramIndex);
1590
1837
  };
1591
1838
  }
1592
- __name(_ts_param5, "_ts_param");
1839
+ __name(_ts_param6, "_ts_param");
1593
1840
  var DEV_TOOLS_SLOT = /* @__PURE__ */ Symbol.for("@dudousxd/aviary:dev-tools");
1594
1841
  function devToolsSlot() {
1595
1842
  const holder = globalThis;
@@ -1923,7 +2170,7 @@ function agentDevTools(dashboard) {
1923
2170
  ];
1924
2171
  }
1925
2172
  __name(agentDevTools, "agentDevTools");
1926
- var logger2 = new Logger2("AgentDashboardModule");
2173
+ var logger3 = new Logger3("AgentDashboardModule");
1927
2174
  var AgentDevToolsRegistration = class {
1928
2175
  static {
1929
2176
  __name(this, "AgentDevToolsRegistration");
@@ -1949,7 +2196,7 @@ var AgentDevToolsRegistration = class {
1949
2196
  tools: agentDevTools(this.dashboard)
1950
2197
  });
1951
2198
  } catch (error) {
1952
- logger2.warn(`agent dev tools not registered: ${error instanceof Error ? error.message : String(error)}`);
2199
+ logger3.warn(`agent dev tools not registered: ${error instanceof Error ? error.message : String(error)}`);
1953
2200
  }
1954
2201
  }
1955
2202
  onModuleDestroy() {
@@ -1957,11 +2204,11 @@ var AgentDevToolsRegistration = class {
1957
2204
  this.#unregister = void 0;
1958
2205
  }
1959
2206
  };
1960
- AgentDevToolsRegistration = _ts_decorate5([
1961
- Injectable3(),
1962
- _ts_param5(0, Inject5(DASHBOARD_DEV_TOOLS)),
1963
- _ts_metadata5("design:type", Function),
1964
- _ts_metadata5("design:paramtypes", [
2207
+ AgentDevToolsRegistration = _ts_decorate6([
2208
+ Injectable4(),
2209
+ _ts_param6(0, Inject6(DASHBOARD_DEV_TOOLS)),
2210
+ _ts_metadata6("design:type", Function),
2211
+ _ts_metadata6("design:paramtypes", [
1965
2212
  Boolean,
1966
2213
  typeof DashboardService === "undefined" ? Object : DashboardService,
1967
2214
  typeof DashboardConsoleAccess === "undefined" ? Object : DashboardConsoleAccess
@@ -1969,7 +2216,7 @@ AgentDevToolsRegistration = _ts_decorate5([
1969
2216
  ], AgentDevToolsRegistration);
1970
2217
 
1971
2218
  // src/server/agent-dashboard.module.ts
1972
- function _ts_decorate6(decorators, target, key, desc) {
2219
+ function _ts_decorate7(decorators, target, key, desc) {
1973
2220
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1974
2221
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
1975
2222
  r = Reflect.decorate(decorators, target, key, desc);
@@ -1982,7 +2229,7 @@ function _ts_decorate6(decorators, target, key, desc) {
1982
2229
  }
1983
2230
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1984
2231
  }
1985
- __name(_ts_decorate6, "_ts_decorate");
2232
+ __name(_ts_decorate7, "_ts_decorate");
1986
2233
  function normalize(path) {
1987
2234
  return normalizeDashboardPath(path);
1988
2235
  }
@@ -2011,7 +2258,8 @@ var AgentApiModule = class _AgentApiModule {
2011
2258
  ...options.imports ?? []
2012
2259
  ],
2013
2260
  controllers: options.routes === false ? [] : withControllerDecorators([
2014
- AgentApiController
2261
+ AgentApiController,
2262
+ AgentConsolesController
2015
2263
  ], consoleDecorators(options.decorators)),
2016
2264
  providers: [
2017
2265
  DashboardService,
@@ -2042,7 +2290,7 @@ var AgentApiModule = class _AgentApiModule {
2042
2290
  };
2043
2291
  }
2044
2292
  };
2045
- AgentApiModule = _ts_decorate6([
2293
+ AgentApiModule = _ts_decorate7([
2046
2294
  Module({})
2047
2295
  ], AgentApiModule);
2048
2296
  var AgentDashboardModule = class _AgentDashboardModule {
@@ -2101,7 +2349,11 @@ var AgentDashboardModule = class _AgentDashboardModule {
2101
2349
  {
2102
2350
  provide: DASHBOARD_API_PATH,
2103
2351
  useValue: apiBasePath
2104
- }
2352
+ },
2353
+ // The console switcher lists this console only where it serves its own page.
2354
+ ...options.routes === false ? [] : [
2355
+ AgentConsoleRegistration
2356
+ ]
2105
2357
  ],
2106
2358
  // Re-export the API module so its DashboardService reaches importers (e.g. the host's own
2107
2359
  // controllers), and the paths a self-mounted `AgentUiController` subclass injects.
@@ -2113,23 +2365,31 @@ var AgentDashboardModule = class _AgentDashboardModule {
2113
2365
  };
2114
2366
  }
2115
2367
  };
2116
- AgentDashboardModule = _ts_decorate6([
2368
+ AgentDashboardModule = _ts_decorate7([
2117
2369
  Module({})
2118
2370
  ], AgentDashboardModule);
2119
2371
  export {
2120
2372
  AGENT_ACTOR_DIRECTORY,
2121
2373
  AGENT_ACTOR_RESOLVER,
2122
2374
  AGENT_APPROVAL_PORT,
2375
+ AGENT_CONSOLE_ID,
2376
+ AGENT_DASHBOARD_CONFIG_ID,
2123
2377
  AGENT_GOVERNANCE_QUERIES,
2378
+ AGENT_OPTIONS,
2124
2379
  AGENT_PRICING_STORE,
2380
+ AVIARY_CONSOLES_SLOT,
2125
2381
  AgentApiController,
2126
2382
  AgentApiModule,
2383
+ AgentConsoleRegistration,
2384
+ AgentConsolesController,
2127
2385
  AgentDashboardModule,
2128
2386
  AgentDevToolsRegistration,
2129
2387
  AgentUiController,
2130
2388
  CONSOLE_AUTHORIZE_MISSING_WARNING,
2131
2389
  CONSOLE_GATE,
2132
2390
  CONSOLE_PAGE_METADATA,
2391
+ CONSOLE_PROBE_TIMEOUT_MS,
2392
+ CONTEXT_ACCESSOR,
2133
2393
  ConsoleAccessDenied,
2134
2394
  ConsoleAuthorizeFilter,
2135
2395
  ConsoleAuthorizeGuard,
@@ -2146,8 +2406,10 @@ export {
2146
2406
  agentDevTools,
2147
2407
  anyAuthorize,
2148
2408
  authorizeByToken,
2409
+ listVisibleConsoles,
2149
2410
  normalizeConsoleResult,
2150
2411
  parsePriceInput,
2412
+ registerAviaryConsole,
2151
2413
  resolveConsoleAuthorize
2152
2414
  };
2153
2415
  //# sourceMappingURL=index.js.map