@dudousxd/nestjs-agent-dashboard 0.19.1 → 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.
@@ -1,8 +1,10 @@
1
1
  const __importMetaUrl = require('url').pathToFileURL(__filename).href;
2
2
  "use strict";
3
+ var __create = Object.create;
3
4
  var __defProp = Object.defineProperty;
4
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
6
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
9
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
8
10
  var __export = (target, all) => {
@@ -17,6 +19,14 @@ var __copyProps = (to, from, except, desc) => {
17
19
  }
18
20
  return to;
19
21
  };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
23
+ // If the importer is in node compatibility mode or this is not an ESM
24
+ // file that has been converted to a CommonJS file using a Babel-
25
+ // compatible transform (i.e. "__esModule" has not been set), then set
26
+ // "default" to the CommonJS "module.exports" for node compatibility.
27
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
28
+ mod
29
+ ));
20
30
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
31
 
22
32
  // src/server/index.ts
@@ -30,13 +40,30 @@ __export(index_exports, {
30
40
  AgentApiController: () => AgentApiController,
31
41
  AgentApiModule: () => AgentApiModule,
32
42
  AgentDashboardModule: () => AgentDashboardModule,
43
+ AgentDevToolsRegistration: () => AgentDevToolsRegistration,
33
44
  AgentUiController: () => AgentUiController,
45
+ CONSOLE_AUTHORIZE_MISSING_WARNING: () => CONSOLE_AUTHORIZE_MISSING_WARNING,
46
+ CONSOLE_GATE: () => CONSOLE_GATE,
47
+ CONSOLE_PAGE_METADATA: () => CONSOLE_PAGE_METADATA,
48
+ ConsoleAccessDenied: () => ConsoleAccessDenied,
49
+ ConsoleAuthorizeFilter: () => ConsoleAuthorizeFilter,
50
+ ConsoleAuthorizeGuard: () => ConsoleAuthorizeGuard,
51
+ ConsolePage: () => ConsolePage,
34
52
  DASHBOARD_API_PATH: () => DASHBOARD_API_PATH,
35
53
  DASHBOARD_APPROVAL_ACTOR_REF: () => DASHBOARD_APPROVAL_ACTOR_REF,
36
54
  DASHBOARD_BASE_PATH: () => DASHBOARD_BASE_PATH,
55
+ DASHBOARD_CONSOLE_OPTIONS: () => DASHBOARD_CONSOLE_OPTIONS,
56
+ DASHBOARD_DEV_TOOLS: () => DASHBOARD_DEV_TOOLS,
57
+ DashboardConsoleAccess: () => DashboardConsoleAccess,
37
58
  DashboardService: () => DashboardService,
59
+ UseConsoleAuthorize: () => UseConsoleAuthorize,
38
60
  agentDashboardMountPaths: () => agentDashboardMountPaths,
39
- parsePriceInput: () => parsePriceInput
61
+ agentDevTools: () => agentDevTools,
62
+ anyAuthorize: () => anyAuthorize,
63
+ authorizeByToken: () => authorizeByToken,
64
+ normalizeConsoleResult: () => normalizeConsoleResult,
65
+ parsePriceInput: () => parsePriceInput,
66
+ resolveConsoleAuthorize: () => resolveConsoleAuthorize
40
67
  });
41
68
  module.exports = __toCommonJS(index_exports);
42
69
 
@@ -121,6 +148,8 @@ var AGENT_ACTOR_RESOLVER = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:ac
121
148
  var DASHBOARD_BASE_PATH = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent-dashboard:base-path");
122
149
  var DASHBOARD_API_PATH = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent-dashboard:api-path");
123
150
  var DASHBOARD_APPROVAL_ACTOR_REF = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent-dashboard:approval-actor-ref");
151
+ var DASHBOARD_CONSOLE_OPTIONS = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent-dashboard:console-options");
152
+ var DASHBOARD_DEV_TOOLS = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent-dashboard:dev-tools");
124
153
 
125
154
  // src/server/dashboard.service.ts
126
155
  function _ts_decorate(decorators, target, key, desc) {
@@ -199,8 +228,8 @@ var DashboardService = class {
199
228
  };
200
229
  }
201
230
  /** Top threads by cost for a day range (default 10, highest cost first). */
202
- async topThreads(range, limit = 10) {
203
- const rows = await this.queries.spendByThread(range, limit);
231
+ async topThreads(range, limit2 = 10) {
232
+ const rows = await this.queries.spendByThread(range, limit2);
204
233
  return this.withActorLabels(rows);
205
234
  }
206
235
  /** Run reliability for a day range: metrics, by-agent/by-error breakdowns and the trend, in parallel. */
@@ -219,12 +248,12 @@ var DashboardService = class {
219
248
  };
220
249
  }
221
250
  /** Most recent runs (status/agent/duration/error) for the Reliability recent-runs table. */
222
- recentRuns(limit) {
223
- return this.queries.recentRuns(limit);
251
+ recentRuns(limit2) {
252
+ return this.queries.recentRuns(limit2);
224
253
  }
225
254
  /** Most recent tool calls (status/type/thread) for the Runs & tools activity feed. */
226
- recentToolCalls(limit) {
227
- return this.queries.recentToolCalls(limit);
255
+ recentToolCalls(limit2) {
256
+ return this.queries.recentToolCalls(limit2);
228
257
  }
229
258
  /** Paged, filterable tool calls for the Runs & tools activity table. */
230
259
  toolCallsPage(query) {
@@ -239,8 +268,8 @@ var DashboardService = class {
239
268
  * Capped and totalless — kept because the SPA still calls `GET approvals`; new callers want
240
269
  * {@link approvalsPage}, which reports how much of the backlog the cap hid.
241
270
  */
242
- pendingApprovals(limit) {
243
- return this.queries.pendingApprovals(limit);
271
+ pendingApprovals(limit2) {
272
+ return this.queries.pendingApprovals(limit2);
244
273
  }
245
274
  /** Paged, filterable approvals inbox with a total — the backlog-safe read of the HITL queue. */
246
275
  approvalsPage(query) {
@@ -328,8 +357,8 @@ var DashboardService = class {
328
357
  });
329
358
  }
330
359
  /** Most recent threads with rolled-up message/token counts. */
331
- async recentThreads(limit) {
332
- const rows = await this.queries.recentThreads(limit);
360
+ async recentThreads(limit2) {
361
+ const rows = await this.queries.recentThreads(limit2);
333
362
  return this.withActorLabels(rows);
334
363
  }
335
364
  /**
@@ -337,10 +366,10 @@ var DashboardService = class {
337
366
  * {@link recentThreads} is.
338
367
  */
339
368
  async threadsPage(query) {
340
- const page = await this.queries.threadsPage(query);
341
- const rows = await this.withActorLabels(page.rows);
369
+ const page2 = await this.queries.threadsPage(query);
370
+ const rows = await this.withActorLabels(page2.rows);
342
371
  return {
343
- ...page,
372
+ ...page2,
344
373
  rows
345
374
  };
346
375
  }
@@ -661,27 +690,27 @@ var AgentApiController = class {
661
690
  return this.dashboard.spend(resolveRange(from, to));
662
691
  }
663
692
  /** Top threads by cost (default 10, max 200) for a day range (defaults to the last 30 days). */
664
- topThreads(from, to, limit) {
665
- return this.dashboard.topThreads(resolveRange(from, to), parseLimit(limit, 10));
693
+ topThreads(from, to, limit2) {
694
+ return this.dashboard.topThreads(resolveRange(from, to), parseLimit(limit2, 10));
666
695
  }
667
696
  /** `{ metrics, byAgent, errors, trend }` for a day range (defaults to the last 30 days). */
668
697
  reliability(from, to) {
669
698
  return this.dashboard.reliability(resolveRange(from, to));
670
699
  }
671
700
  /** Most recent runs (default 50, max 200) for the Reliability recent-runs table. */
672
- runs(limit) {
673
- return this.dashboard.recentRuns(parseLimit(limit, 50));
701
+ runs(limit2) {
702
+ return this.dashboard.recentRuns(parseLimit(limit2, 50));
674
703
  }
675
704
  /**
676
705
  * Paged, filterable runs for the Reliability recent-runs table. `page` (default 1), `limit`
677
706
  * (default 25, max 200), and `where[agentName]`/`where[status]`/`where[errorCode]`/
678
707
  * `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.
679
708
  */
680
- runsPage(page, limit, where) {
681
- const parsedWhere = parseRunWhere(where);
709
+ runsPage(page2, limit2, where2) {
710
+ const parsedWhere = parseRunWhere(where2);
682
711
  return this.dashboard.runsPage({
683
- page: parsePageNumber(page),
684
- pageSize: parseLimit(limit, 25),
712
+ page: parsePageNumber(page2),
713
+ pageSize: parseLimit(limit2, 25),
685
714
  ...parsedWhere !== void 0 ? {
686
715
  where: parsedWhere
687
716
  } : {}
@@ -698,19 +727,19 @@ var AgentApiController = class {
698
727
  return this.dashboard.runDetail(runId);
699
728
  }
700
729
  /** Most recent tool calls (default 50, max 200) for the activity feed. */
701
- toolCalls(limit) {
702
- return this.dashboard.recentToolCalls(parseLimit(limit, 50));
730
+ toolCalls(limit2) {
731
+ return this.dashboard.recentToolCalls(parseLimit(limit2, 50));
703
732
  }
704
733
  /**
705
734
  * Paged, filterable tool calls for the Runs & tools activity table. `page` (default 1), `limit`
706
735
  * (default 25, max 200), and `where[toolName]`/`where[toolType]`/`where[status]`/`where[threadId]`/
707
736
  * `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.
708
737
  */
709
- toolCallsPage(page, limit, where) {
710
- const parsedWhere = parseToolCallWhere(where);
738
+ toolCallsPage(page2, limit2, where2) {
739
+ const parsedWhere = parseToolCallWhere(where2);
711
740
  return this.dashboard.toolCallsPage({
712
- page: parsePageNumber(page),
713
- pageSize: parseLimit(limit, 25),
741
+ page: parsePageNumber(page2),
742
+ pageSize: parseLimit(limit2, 25),
714
743
  ...parsedWhere !== void 0 ? {
715
744
  where: parsedWhere
716
745
  } : {}
@@ -722,8 +751,8 @@ var AgentApiController = class {
722
751
  * Capped with NO total, so a backlog past `limit` is invisible from the response alone. Kept
723
752
  * because the SPA still calls it; anything new should call `approvals-page` instead.
724
753
  */
725
- approvals(limit) {
726
- return this.dashboard.pendingApprovals(parseLimit(limit, 50));
754
+ approvals(limit2) {
755
+ return this.dashboard.pendingApprovals(parseLimit(limit2, 50));
727
756
  }
728
757
  /**
729
758
  * Paged, filterable approvals inbox, oldest first. `page` (default 1), `limit` (default 25, max
@@ -733,11 +762,11 @@ var AgentApiController = class {
733
762
  * Unlike `GET approvals` this reports `total`, so the console can say "showing N of M" instead of
734
763
  * silently hiding pending decisions behind a row cap.
735
764
  */
736
- approvalsPage(page, limit, where) {
737
- const parsedWhere = parseApprovalWhere(where);
765
+ approvalsPage(page2, limit2, where2) {
766
+ const parsedWhere = parseApprovalWhere(where2);
738
767
  return this.dashboard.approvalsPage({
739
- page: parsePageNumber(page),
740
- pageSize: parseLimit(limit, 25),
768
+ page: parsePageNumber(page2),
769
+ pageSize: parseLimit(limit2, 25),
741
770
  ...parsedWhere !== void 0 ? {
742
771
  where: parsedWhere
743
772
  } : {}
@@ -784,19 +813,19 @@ var AgentApiController = class {
784
813
  return this.dashboard.genuiStats(resolveRange(from, to));
785
814
  }
786
815
  /** Most recent threads (default 50, max 200) with rolled-up counts. */
787
- threads(limit) {
788
- return this.dashboard.recentThreads(parseLimit(limit, 50));
816
+ threads(limit2) {
817
+ return this.dashboard.recentThreads(parseLimit(limit2, 50));
789
818
  }
790
819
  /**
791
820
  * Paged, filterable threads for the Runs & tools threads table. `page` (default 1), `limit`
792
821
  * (default 25, max 200), and `where[actorRef]`/`where[title]` (case-insensitive substring)/
793
822
  * `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.
794
823
  */
795
- threadsPage(page, limit, where) {
796
- const parsedWhere = parseThreadWhere(where);
824
+ threadsPage(page2, limit2, where2) {
825
+ const parsedWhere = parseThreadWhere(where2);
797
826
  return this.dashboard.threadsPage({
798
- page: parsePageNumber(page),
799
- pageSize: parseLimit(limit, 25),
827
+ page: parsePageNumber(page2),
828
+ pageSize: parseLimit(limit2, 25),
800
829
  ...parsedWhere !== void 0 ? {
801
830
  where: parsedWhere
802
831
  } : {}
@@ -1082,15 +1111,249 @@ __name(agentDashboardMountPaths, "agentDashboardMountPaths");
1082
1111
  // src/server/agent-dashboard.module.ts
1083
1112
  var import_nestjs_agent_core3 = require("@dudousxd/nestjs-agent-core");
1084
1113
  var import_reflect_metadata = require("reflect-metadata");
1085
- var import_common7 = require("@nestjs/common");
1086
- var import_core = require("@nestjs/core");
1114
+ var import_common10 = require("@nestjs/common");
1115
+ var import_core3 = require("@nestjs/core");
1087
1116
 
1088
1117
  // src/server/agent-ui.controller.ts
1089
1118
  var import_node_fs = require("fs");
1090
1119
  var import_node_path = require("path");
1091
1120
  var import_node_url = require("url");
1092
1121
  var import_nestjs_agent_core2 = require("@dudousxd/nestjs-agent-core");
1122
+ var import_common8 = require("@nestjs/common");
1123
+
1124
+ // src/server/console-authorize.guard.ts
1125
+ var import_common7 = require("@nestjs/common");
1126
+ var import_core2 = require("@nestjs/core");
1127
+
1128
+ // src/server/console-authorize.ts
1129
+ var import_node_crypto = require("crypto");
1093
1130
  var import_common6 = require("@nestjs/common");
1131
+ var import_core = require("@nestjs/core");
1132
+ var CONSOLE_GATE = /* @__PURE__ */ Symbol.for("@dudousxd/aviary:console-gate");
1133
+ var PACKAGE = "@dudousxd/nestjs-agent-dashboard";
1134
+ var MODULE = "AgentDashboardModule";
1135
+ var logger = new import_common6.Logger(MODULE);
1136
+ var CONSOLE_AUTHORIZE_MISSING_WARNING = `[${PACKAGE}] console has no \`authorize\` configured: it is open outside production and denies every request in production. Set \`authorize\` in ${MODULE}.forRoot().`;
1137
+ var allow = /* @__PURE__ */ __name((caller) => caller !== void 0 && caller !== "" ? {
1138
+ allowed: true,
1139
+ caller
1140
+ } : {
1141
+ allowed: true
1142
+ }, "allow");
1143
+ var forbidden = /* @__PURE__ */ __name(() => ({
1144
+ allowed: false,
1145
+ reason: "forbidden"
1146
+ }), "forbidden");
1147
+ var unauthenticated = /* @__PURE__ */ __name(() => ({
1148
+ allowed: false,
1149
+ reason: "unauthenticated"
1150
+ }), "unauthenticated");
1151
+ function isProduction() {
1152
+ return (process.env.NODE_ENV ?? "").trim().toLowerCase() === "production";
1153
+ }
1154
+ __name(isProduction, "isProduction");
1155
+ function normalizeConsoleResult(result) {
1156
+ if (result === true) return allow();
1157
+ if (result === null || typeof result !== "object") return forbidden();
1158
+ const value = result;
1159
+ if (typeof value.redirectTo === "string" && value.redirectTo !== "" && value.allowed !== true) {
1160
+ return {
1161
+ allowed: false,
1162
+ reason: "unauthenticated",
1163
+ redirectTo: value.redirectTo
1164
+ };
1165
+ }
1166
+ if (value.allowed === true) {
1167
+ return allow(typeof value.caller === "string" ? value.caller : void 0);
1168
+ }
1169
+ return value.reason === "unauthenticated" ? unauthenticated() : forbidden();
1170
+ }
1171
+ __name(normalizeConsoleResult, "normalizeConsoleResult");
1172
+ function isAuthorizerClass(value) {
1173
+ if (typeof value !== "function") return false;
1174
+ if (typeof value.prototype?.authorize === "function") {
1175
+ return true;
1176
+ }
1177
+ return /^class[\s{]/.test(Function.prototype.toString.call(value));
1178
+ }
1179
+ __name(isAuthorizerClass, "isAuthorizerClass");
1180
+ function isPolicyCheck(value) {
1181
+ return typeof value === "object" && value !== null && typeof value.policy === "function" && typeof value.ability === "string";
1182
+ }
1183
+ __name(isPolicyCheck, "isPolicyCheck");
1184
+ function lazyModuleClass(value) {
1185
+ if (typeof value !== "object" || value === null) return void 0;
1186
+ const candidate = value.default;
1187
+ return isAuthorizerClass(candidate) ? candidate : void 0;
1188
+ }
1189
+ __name(lazyModuleClass, "lazyModuleClass");
1190
+ function tryGet(moduleRef, token) {
1191
+ try {
1192
+ return moduleRef.get(token, {
1193
+ strict: false
1194
+ }) ?? void 0;
1195
+ } catch {
1196
+ return void 0;
1197
+ }
1198
+ }
1199
+ __name(tryGet, "tryGet");
1200
+ function classInstances(moduleRef) {
1201
+ const instances = /* @__PURE__ */ new Map();
1202
+ return (Class) => {
1203
+ let instance = instances.get(Class);
1204
+ if (instance === void 0) {
1205
+ const registered = tryGet(moduleRef, Class);
1206
+ instance = registered !== void 0 ? Promise.resolve(registered) : moduleRef.create(Class);
1207
+ instance.catch(() => instances.delete(Class));
1208
+ instances.set(Class, instance);
1209
+ }
1210
+ return instance;
1211
+ };
1212
+ }
1213
+ __name(classInstances, "classInstances");
1214
+ function hasUser(user) {
1215
+ return user !== null && user !== void 0 && user !== false;
1216
+ }
1217
+ __name(hasUser, "hasUser");
1218
+ async function policyDecider(check, moduleRef) {
1219
+ let mod;
1220
+ try {
1221
+ mod = await import("@nestjs/authorization");
1222
+ } catch {
1223
+ throw new Error(`${MODULE}: \`authorize: { policy, ability }\` needs @nestjs/authorization installed and AuthorizationModule imported; or pass a function, a class, or a gate name.`);
1224
+ }
1225
+ const service = tryGet(moduleRef, mod.AuthorizationService);
1226
+ if (service === void 0) {
1227
+ throw new Error(`${MODULE}: \`authorize: { policy, ability }\` needs AuthorizationModule.forRoot() from @nestjs/authorization imported in the app.`);
1228
+ }
1229
+ const options = tryGet(moduleRef, mod.AUTHORIZATION_MODULE_OPTIONS);
1230
+ const getUser = options?.getUser ?? mod.defaultGetUser;
1231
+ return async (context) => {
1232
+ const user = await getUser(context);
1233
+ const signedIn = hasUser(user);
1234
+ if (await service.can(check.policy, check.ability, signedIn ? user : null)) return allow();
1235
+ return signedIn ? forbidden() : unauthenticated();
1236
+ };
1237
+ }
1238
+ __name(policyDecider, "policyDecider");
1239
+ async function resolveConsoleAuthorize(authorize, moduleRef, options = {}) {
1240
+ const log = options.logger ?? logger;
1241
+ if (authorize === void 0) {
1242
+ if (options.warnIfMissing !== false) log.warn(CONSOLE_AUTHORIZE_MISSING_WARNING);
1243
+ const production = isProduction();
1244
+ return async () => production ? forbidden() : allow("dev-open");
1245
+ }
1246
+ const instanceOf = classInstances(moduleRef);
1247
+ let decide;
1248
+ if (typeof authorize === "string") {
1249
+ const name = authorize;
1250
+ const gate = tryGet(moduleRef, CONSOLE_GATE);
1251
+ if (gate === void 0) {
1252
+ throw new Error(`${MODULE}: \`authorize: '${name}'\` needs @dudousxd/nestjs-authz's AuthzModule (it resolves gate names); or pass a function, a class, or { policy, ability }.`);
1253
+ }
1254
+ decide = /* @__PURE__ */ __name((context) => gate.allows(name, context), "decide");
1255
+ } else if (isPolicyCheck(authorize)) {
1256
+ decide = await policyDecider(authorize, moduleRef);
1257
+ } else if (isAuthorizerClass(authorize)) {
1258
+ const Class = authorize;
1259
+ decide = /* @__PURE__ */ __name(async (context) => (await instanceOf(Class)).authorize(context), "decide");
1260
+ } else if (typeof authorize === "function") {
1261
+ const fn = authorize;
1262
+ let lazyClass;
1263
+ decide = /* @__PURE__ */ __name(async (context) => {
1264
+ if (lazyClass !== void 0) return (await instanceOf(lazyClass)).authorize(context);
1265
+ const result = await fn(context);
1266
+ const Class = lazyModuleClass(result);
1267
+ if (Class === void 0) return result;
1268
+ lazyClass = Class;
1269
+ return (await instanceOf(Class)).authorize(context);
1270
+ }, "decide");
1271
+ } else {
1272
+ throw new Error(`${MODULE}: \`authorize\` must be a function, a class with authorize(), a lazy import, { policy, ability } or a gate name.`);
1273
+ }
1274
+ return async (context) => {
1275
+ try {
1276
+ return normalizeConsoleResult(await decide(context));
1277
+ } catch (error) {
1278
+ log.warn(`console \`authorize\` threw, denying the request: ${error instanceof Error ? error.message : String(error)}`);
1279
+ return forbidden();
1280
+ }
1281
+ };
1282
+ }
1283
+ __name(resolveConsoleAuthorize, "resolveConsoleAuthorize");
1284
+ function sha256(value) {
1285
+ return (0, import_node_crypto.createHash)("sha256").update(value).digest();
1286
+ }
1287
+ __name(sha256, "sha256");
1288
+ function authorizeByToken(options) {
1289
+ const token = options.token;
1290
+ if (typeof token !== "string" || token === "") {
1291
+ throw new Error("authorizeByToken: `token` is empty; set it (e.g. from an env var).");
1292
+ }
1293
+ const expected = sha256(token);
1294
+ const caller = `token:${expected.toString("hex").slice(0, 12)}`;
1295
+ return (context) => {
1296
+ const req = context.switchToHttp().getRequest();
1297
+ const header = req?.headers?.authorization;
1298
+ const value = Array.isArray(header) ? header[0] : header;
1299
+ const match = typeof value === "string" ? /^Bearer\s+(.+)$/i.exec(value.trim()) : null;
1300
+ if (match?.[1] !== void 0 && (0, import_node_crypto.timingSafeEqual)(sha256(match[1]), expected)) {
1301
+ return {
1302
+ allowed: true,
1303
+ caller
1304
+ };
1305
+ }
1306
+ return {
1307
+ allowed: false,
1308
+ reason: "unauthenticated"
1309
+ };
1310
+ };
1311
+ }
1312
+ __name(authorizeByToken, "authorizeByToken");
1313
+ function anyAuthorize(...forms) {
1314
+ if (forms.length === 0) throw new Error("anyAuthorize: pass at least one authorize form.");
1315
+ let AnyConsoleAuthorizer = class AnyConsoleAuthorizer {
1316
+ static {
1317
+ __name(this, "AnyConsoleAuthorizer");
1318
+ }
1319
+ moduleRef;
1320
+ #decides;
1321
+ constructor(moduleRef) {
1322
+ this.moduleRef = moduleRef;
1323
+ }
1324
+ async authorize(context) {
1325
+ this.#decides ??= Promise.all(forms.map((form) => resolveConsoleAuthorize(form, this.moduleRef, {
1326
+ warnIfMissing: false
1327
+ })));
1328
+ this.#decides.catch(() => {
1329
+ this.#decides = void 0;
1330
+ });
1331
+ let unauthenticated2;
1332
+ for (const decide of await this.#decides) {
1333
+ const decision = await decide(context);
1334
+ if (decision.allowed) return decision;
1335
+ if (decision.reason === "unauthenticated") unauthenticated2 ??= decision;
1336
+ }
1337
+ if (unauthenticated2 === void 0) return {
1338
+ allowed: false,
1339
+ reason: "forbidden"
1340
+ };
1341
+ return "redirectTo" in unauthenticated2 && unauthenticated2.redirectTo !== void 0 ? {
1342
+ redirectTo: unauthenticated2.redirectTo
1343
+ } : {
1344
+ allowed: false,
1345
+ reason: "unauthenticated"
1346
+ };
1347
+ }
1348
+ };
1349
+ Reflect.defineMetadata("design:paramtypes", [
1350
+ import_core.ModuleRef
1351
+ ], AnyConsoleAuthorizer);
1352
+ return AnyConsoleAuthorizer;
1353
+ }
1354
+ __name(anyAuthorize, "anyAuthorize");
1355
+
1356
+ // src/server/console-authorize.guard.ts
1094
1357
  function _ts_decorate3(decorators, target, key, desc) {
1095
1358
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1096
1359
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
@@ -1117,6 +1380,161 @@ function _ts_param3(paramIndex, decorator) {
1117
1380
  };
1118
1381
  }
1119
1382
  __name(_ts_param3, "_ts_param");
1383
+ var DashboardConsoleAccess = class {
1384
+ static {
1385
+ __name(this, "DashboardConsoleAccess");
1386
+ }
1387
+ options;
1388
+ moduleRef;
1389
+ #decide;
1390
+ constructor(options, moduleRef) {
1391
+ this.options = options;
1392
+ this.moduleRef = moduleRef;
1393
+ }
1394
+ #resolving;
1395
+ /** Resolves `authorize` at boot: a configuration mistake fails the boot, and the warning logs once. */
1396
+ async onModuleInit() {
1397
+ this.#resolving ??= resolveConsoleAuthorize(this.options.authorize, this.moduleRef);
1398
+ this.#decide = await this.#resolving;
1399
+ }
1400
+ /** The decision for one request. */
1401
+ async check(context) {
1402
+ if (this.#decide === void 0) await this.onModuleInit();
1403
+ return this.#decide(context);
1404
+ }
1405
+ };
1406
+ DashboardConsoleAccess = _ts_decorate3([
1407
+ (0, import_common7.Injectable)(),
1408
+ _ts_param3(0, (0, import_common7.Inject)(DASHBOARD_CONSOLE_OPTIONS)),
1409
+ _ts_metadata3("design:type", Function),
1410
+ _ts_metadata3("design:paramtypes", [
1411
+ typeof DashboardConsoleOptions === "undefined" ? Object : DashboardConsoleOptions,
1412
+ typeof import_core2.ModuleRef === "undefined" ? Object : import_core2.ModuleRef
1413
+ ])
1414
+ ], DashboardConsoleAccess);
1415
+ var CONSOLE_PAGE_METADATA = "@dudousxd/nestjs-agent-dashboard:console-page";
1416
+ var ConsolePage = /* @__PURE__ */ __name(() => (0, import_common7.SetMetadata)(CONSOLE_PAGE_METADATA, true), "ConsolePage");
1417
+ var ConsoleAccessDenied = class extends import_common7.HttpException {
1418
+ static {
1419
+ __name(this, "ConsoleAccessDenied");
1420
+ }
1421
+ decision;
1422
+ page;
1423
+ constructor(decision, page2) {
1424
+ super(decision.reason === "forbidden" ? {
1425
+ error: "forbidden"
1426
+ } : {
1427
+ error: "unauthenticated",
1428
+ ...decision.redirectTo !== void 0 ? {
1429
+ redirectTo: decision.redirectTo
1430
+ } : {}
1431
+ }, decision.reason === "forbidden" ? 403 : 401), this.decision = decision, this.page = page2;
1432
+ }
1433
+ };
1434
+ function isPageNavigation(context) {
1435
+ if (context.getType() !== "http") return false;
1436
+ const page2 = Reflect.getMetadata(CONSOLE_PAGE_METADATA, context.getHandler()) === true || Reflect.getMetadata(CONSOLE_PAGE_METADATA, context.getClass()) === true;
1437
+ if (!page2) return false;
1438
+ const req = context.switchToHttp().getRequest();
1439
+ if ((req.method ?? "GET").toUpperCase() !== "GET") return false;
1440
+ const accept = req.headers?.accept;
1441
+ const value = Array.isArray(accept) ? accept.join(",") : accept ?? "";
1442
+ return value.includes("text/html");
1443
+ }
1444
+ __name(isPageNavigation, "isPageNavigation");
1445
+ var ConsoleAuthorizeGuard = class {
1446
+ static {
1447
+ __name(this, "ConsoleAuthorizeGuard");
1448
+ }
1449
+ access;
1450
+ constructor(access) {
1451
+ this.access = access;
1452
+ }
1453
+ async canActivate(context) {
1454
+ const decision = await this.access.check(context);
1455
+ if (decision.allowed) return true;
1456
+ throw new ConsoleAccessDenied(decision, isPageNavigation(context));
1457
+ }
1458
+ };
1459
+ ConsoleAuthorizeGuard = _ts_decorate3([
1460
+ (0, import_common7.Injectable)(),
1461
+ _ts_metadata3("design:type", Function),
1462
+ _ts_metadata3("design:paramtypes", [
1463
+ typeof DashboardConsoleAccess === "undefined" ? Object : DashboardConsoleAccess
1464
+ ])
1465
+ ], ConsoleAuthorizeGuard);
1466
+ function deniedPage(title, message) {
1467
+ const body = "font-family:system-ui,sans-serif;display:grid;place-items:center;min-height:100vh;margin:0";
1468
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${title}</title></head><body style="${body}"><main style="text-align:center"><h1>${title}</h1><p>${message}</p></main></body></html>`;
1469
+ }
1470
+ __name(deniedPage, "deniedPage");
1471
+ var FORBIDDEN_PAGE = deniedPage("Access denied", "You do not have access to this console.");
1472
+ var UNAUTHENTICATED_PAGE = deniedPage("Sign in required", "Sign in to open this console.");
1473
+ var ConsoleAuthorizeFilter = class {
1474
+ static {
1475
+ __name(this, "ConsoleAuthorizeFilter");
1476
+ }
1477
+ adapterHost;
1478
+ constructor(adapterHost) {
1479
+ this.adapterHost = adapterHost;
1480
+ }
1481
+ catch(exception, host) {
1482
+ const adapter = this.adapterHost.httpAdapter;
1483
+ const res = host.switchToHttp().getResponse();
1484
+ const { decision, page: page2 } = exception;
1485
+ if (page2 && decision.reason === "unauthenticated" && decision.redirectTo !== void 0) {
1486
+ adapter.redirect(res, 302, decision.redirectTo);
1487
+ return;
1488
+ }
1489
+ if (page2) {
1490
+ const forbidden2 = decision.reason === "forbidden";
1491
+ adapter.setHeader(res, "Content-Type", "text/html; charset=utf-8");
1492
+ adapter.reply(res, forbidden2 ? FORBIDDEN_PAGE : UNAUTHENTICATED_PAGE, forbidden2 ? 403 : 401);
1493
+ return;
1494
+ }
1495
+ adapter.reply(res, exception.getResponse(), exception.getStatus());
1496
+ }
1497
+ };
1498
+ ConsoleAuthorizeFilter = _ts_decorate3([
1499
+ (0, import_common7.Catch)(ConsoleAccessDenied),
1500
+ (0, import_common7.Injectable)(),
1501
+ _ts_metadata3("design:type", Function),
1502
+ _ts_metadata3("design:paramtypes", [
1503
+ typeof import_core2.HttpAdapterHost === "undefined" ? Object : import_core2.HttpAdapterHost
1504
+ ])
1505
+ ], ConsoleAuthorizeFilter);
1506
+ function UseConsoleAuthorize() {
1507
+ return (0, import_common7.applyDecorators)((0, import_common7.UseGuards)(ConsoleAuthorizeGuard), (0, import_common7.UseFilters)(ConsoleAuthorizeFilter));
1508
+ }
1509
+ __name(UseConsoleAuthorize, "UseConsoleAuthorize");
1510
+
1511
+ // src/server/agent-ui.controller.ts
1512
+ function _ts_decorate4(decorators, target, key, desc) {
1513
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1514
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
1515
+ r = Reflect.decorate(decorators, target, key, desc);
1516
+ } else {
1517
+ for (var i = decorators.length - 1; i >= 0; i--) {
1518
+ if (d = decorators[i]) {
1519
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1520
+ }
1521
+ }
1522
+ }
1523
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1524
+ }
1525
+ __name(_ts_decorate4, "_ts_decorate");
1526
+ function _ts_metadata4(metadataKey, metadataValue) {
1527
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
1528
+ return Reflect.metadata(metadataKey, metadataValue);
1529
+ }
1530
+ }
1531
+ __name(_ts_metadata4, "_ts_metadata");
1532
+ function _ts_param4(paramIndex, decorator) {
1533
+ return function(target, key) {
1534
+ decorator(target, key, paramIndex);
1535
+ };
1536
+ }
1537
+ __name(_ts_param4, "_ts_param");
1120
1538
  var BUILD_BASE = "/ai-gateway";
1121
1539
  function spaDir() {
1122
1540
  return (0, import_node_url.fileURLToPath)(new URL("../spa", __importMetaUrl));
@@ -1152,7 +1570,7 @@ var AgentUiController = class {
1152
1570
  }
1153
1571
  const indexPath = (0, import_node_path.join)(this.dir, "index.html");
1154
1572
  if (!(0, import_node_fs.existsSync)(indexPath)) {
1155
- throw new import_common6.NotFoundException("Dashboard is not built. Run the package build.");
1573
+ throw new import_common8.NotFoundException("Dashboard is not built. Run the package build.");
1156
1574
  }
1157
1575
  const html = (0, import_node_fs.readFileSync)(indexPath, "utf8").replaceAll(`="${BUILD_BASE}/`, `="${this.basePath}/`);
1158
1576
  const inject = `<script>window.__AGENT_BASE__='${this.basePath}';window.__AGENT_API__='${this.apiBasePath}';</script>`;
@@ -1160,57 +1578,464 @@ var AgentUiController = class {
1160
1578
  }
1161
1579
  asset(file) {
1162
1580
  const safe = (0, import_node_path.basename)(file);
1163
- if (safe !== file) throw new import_common6.NotFoundException();
1581
+ if (safe !== file) throw new import_common8.NotFoundException();
1164
1582
  const root = (0, import_node_path.resolve)(this.dir, "assets");
1165
1583
  const assetPath = (0, import_node_path.resolve)(root, safe);
1166
1584
  if (!assetPath.startsWith(root + import_node_path.sep) || !(0, import_node_fs.existsSync)(assetPath)) {
1167
- throw new import_common6.NotFoundException();
1585
+ throw new import_common8.NotFoundException();
1168
1586
  }
1169
1587
  const type = CONTENT_TYPES[(0, import_node_path.extname)(safe)] ?? "application/octet-stream";
1170
- return new import_common6.StreamableFile((0, import_node_fs.readFileSync)(assetPath), {
1588
+ return new import_common8.StreamableFile((0, import_node_fs.readFileSync)(assetPath), {
1171
1589
  type
1172
1590
  });
1173
1591
  }
1174
1592
  };
1175
- _ts_decorate3([
1176
- (0, import_common6.Get)(),
1177
- (0, import_common6.Header)("Content-Type", "text/html; charset=utf-8"),
1178
- (0, import_common6.Header)("Cache-Control", "no-store, must-revalidate"),
1179
- _ts_param3(0, (0, import_common6.Req)()),
1180
- _ts_param3(1, (0, import_common6.Res)({
1593
+ _ts_decorate4([
1594
+ (0, import_common8.Get)(),
1595
+ ConsolePage(),
1596
+ (0, import_common8.Header)("Content-Type", "text/html; charset=utf-8"),
1597
+ (0, import_common8.Header)("Cache-Control", "no-store, must-revalidate"),
1598
+ _ts_param4(0, (0, import_common8.Req)()),
1599
+ _ts_param4(1, (0, import_common8.Res)({
1181
1600
  passthrough: true
1182
1601
  })),
1183
- _ts_metadata3("design:type", Function),
1184
- _ts_metadata3("design:paramtypes", [
1602
+ _ts_metadata4("design:type", Function),
1603
+ _ts_metadata4("design:paramtypes", [
1185
1604
  typeof UiPageRequest === "undefined" ? Object : UiPageRequest,
1186
1605
  typeof UiPageResponse === "undefined" ? Object : UiPageResponse
1187
1606
  ]),
1188
- _ts_metadata3("design:returntype", String)
1607
+ _ts_metadata4("design:returntype", String)
1189
1608
  ], AgentUiController.prototype, "index", null);
1190
- _ts_decorate3([
1191
- (0, import_common6.Get)("assets/:file"),
1192
- (0, import_common6.Header)("Cache-Control", "public, max-age=31536000, immutable"),
1193
- _ts_param3(0, (0, import_common6.Param)("file")),
1194
- _ts_metadata3("design:type", Function),
1195
- _ts_metadata3("design:paramtypes", [
1609
+ _ts_decorate4([
1610
+ (0, import_common8.Get)("assets/:file"),
1611
+ (0, import_common8.Header)("Cache-Control", "public, max-age=31536000, immutable"),
1612
+ _ts_param4(0, (0, import_common8.Param)("file")),
1613
+ _ts_metadata4("design:type", Function),
1614
+ _ts_metadata4("design:paramtypes", [
1196
1615
  String
1197
1616
  ]),
1198
- _ts_metadata3("design:returntype", typeof import_common6.StreamableFile === "undefined" ? Object : import_common6.StreamableFile)
1617
+ _ts_metadata4("design:returntype", typeof import_common8.StreamableFile === "undefined" ? Object : import_common8.StreamableFile)
1199
1618
  ], AgentUiController.prototype, "asset", null);
1200
- AgentUiController = _ts_decorate3([
1619
+ AgentUiController = _ts_decorate4([
1201
1620
  (0, import_nestjs_agent_core2.CanAnyone)(),
1202
- (0, import_common6.Controller)(),
1203
- _ts_param3(0, (0, import_common6.Inject)(DASHBOARD_BASE_PATH)),
1204
- _ts_param3(1, (0, import_common6.Inject)(DASHBOARD_API_PATH)),
1205
- _ts_metadata3("design:type", Function),
1206
- _ts_metadata3("design:paramtypes", [
1621
+ (0, import_common8.Controller)(),
1622
+ _ts_param4(0, (0, import_common8.Inject)(DASHBOARD_BASE_PATH)),
1623
+ _ts_param4(1, (0, import_common8.Inject)(DASHBOARD_API_PATH)),
1624
+ _ts_metadata4("design:type", Function),
1625
+ _ts_metadata4("design:paramtypes", [
1207
1626
  String,
1208
1627
  String
1209
1628
  ])
1210
1629
  ], AgentUiController);
1211
1630
 
1631
+ // src/server/dev-tools.ts
1632
+ var import_common9 = require("@nestjs/common");
1633
+ function _ts_decorate5(decorators, target, key, desc) {
1634
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1635
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
1636
+ r = Reflect.decorate(decorators, target, key, desc);
1637
+ } else {
1638
+ for (var i = decorators.length - 1; i >= 0; i--) {
1639
+ if (d = decorators[i]) {
1640
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1641
+ }
1642
+ }
1643
+ }
1644
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1645
+ }
1646
+ __name(_ts_decorate5, "_ts_decorate");
1647
+ function _ts_metadata5(metadataKey, metadataValue) {
1648
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
1649
+ return Reflect.metadata(metadataKey, metadataValue);
1650
+ }
1651
+ }
1652
+ __name(_ts_metadata5, "_ts_metadata");
1653
+ function _ts_param5(paramIndex, decorator) {
1654
+ return function(target, key) {
1655
+ decorator(target, key, paramIndex);
1656
+ };
1657
+ }
1658
+ __name(_ts_param5, "_ts_param");
1659
+ var DEV_TOOLS_SLOT = /* @__PURE__ */ Symbol.for("@dudousxd/aviary:dev-tools");
1660
+ function devToolsSlot() {
1661
+ const holder = globalThis;
1662
+ const existing = holder[DEV_TOOLS_SLOT];
1663
+ if (existing !== void 0) return existing;
1664
+ const libs = [];
1665
+ const slot = {
1666
+ version: 1,
1667
+ libs,
1668
+ register(lib) {
1669
+ if (libs.some((entry) => entry.lib === lib.lib)) {
1670
+ throw new Error(`dev tools: the lib "${lib.lib}" is already registered`);
1671
+ }
1672
+ libs.push(lib);
1673
+ return () => {
1674
+ const index = libs.indexOf(lib);
1675
+ if (index !== -1) libs.splice(index, 1);
1676
+ };
1677
+ }
1678
+ };
1679
+ holder[DEV_TOOLS_SLOT] = slot;
1680
+ return slot;
1681
+ }
1682
+ __name(devToolsSlot, "devToolsSlot");
1683
+ var DEFAULT_LIMIT = 20;
1684
+ var MAX_LIMIT = 100;
1685
+ var MESSAGE_LIMIT = 50;
1686
+ var MESSAGE_BYTES = 2 * 1024;
1687
+ var DAY_MS2 = 864e5;
1688
+ var ISO_DAY3 = /^\d{4}-\d{2}-\d{2}$/;
1689
+ var day = {
1690
+ type: "string",
1691
+ description: "UTC day, YYYY-MM-DD."
1692
+ };
1693
+ var page = {
1694
+ type: "integer",
1695
+ description: "Page, from 1. Pass the `next` of the previous page.",
1696
+ minimum: 1
1697
+ };
1698
+ var limit = {
1699
+ type: "integer",
1700
+ description: `Rows per page (default ${DEFAULT_LIMIT}, max ${MAX_LIMIT}).`,
1701
+ minimum: 1,
1702
+ maximum: MAX_LIMIT
1703
+ };
1704
+ var str = /* @__PURE__ */ __name((value) => typeof value === "string" && value !== "" ? value : void 0, "str");
1705
+ function dayOf(input, key) {
1706
+ const value = str(input[key]);
1707
+ if (value !== void 0 && !ISO_DAY3.test(value)) throw new Error(`"${key}" must be YYYY-MM-DD`);
1708
+ return value;
1709
+ }
1710
+ __name(dayOf, "dayOf");
1711
+ function rangeOf(input) {
1712
+ const utcDay2 = /* @__PURE__ */ __name((daysAgo) => new Date(Date.now() - daysAgo * DAY_MS2).toISOString().slice(0, 10), "utcDay");
1713
+ return {
1714
+ fromDay: dayOf(input, "from") ?? utcDay2(29),
1715
+ toDay: dayOf(input, "to") ?? utcDay2(0)
1716
+ };
1717
+ }
1718
+ __name(rangeOf, "rangeOf");
1719
+ function pageOf(input) {
1720
+ return {
1721
+ page: typeof input.page === "number" ? input.page : 1,
1722
+ pageSize: typeof input.limit === "number" ? input.limit : DEFAULT_LIMIT
1723
+ };
1724
+ }
1725
+ __name(pageOf, "pageOf");
1726
+ function where(fields) {
1727
+ const set = Object.fromEntries(Object.entries(fields).filter(([, v]) => v !== void 0));
1728
+ return Object.keys(set).length > 0 ? {
1729
+ where: set
1730
+ } : {};
1731
+ }
1732
+ __name(where, "where");
1733
+ function listOf(result) {
1734
+ return {
1735
+ items: result.rows,
1736
+ next: result.page * result.pageSize < result.total ? result.page + 1 : null,
1737
+ total: result.total
1738
+ };
1739
+ }
1740
+ __name(listOf, "listOf");
1741
+ function cut(text, maxBytes) {
1742
+ const size = Buffer.byteLength(text, "utf8");
1743
+ if (size <= maxBytes) return text;
1744
+ const suffix = `\u2026(truncated, ${size} bytes)`;
1745
+ const keep = Math.max(0, maxBytes - Buffer.byteLength(suffix, "utf8"));
1746
+ let head = Buffer.from(text, "utf8").subarray(0, keep).toString("utf8");
1747
+ if (head.endsWith("\uFFFD")) head = head.slice(0, -1);
1748
+ return `${head}${suffix}`;
1749
+ }
1750
+ __name(cut, "cut");
1751
+ function agentDevTools(dashboard) {
1752
+ return [
1753
+ {
1754
+ name: "agent_list_runs",
1755
+ kind: "read",
1756
+ description: "Agent runs, newest first: agent, actor, status, duration, cost, error. Filters: agent, status, errorCode, threadId, from/to (UTC days). Returns { items, next, total }; pass next as page.",
1757
+ input: {
1758
+ type: "object",
1759
+ properties: {
1760
+ agent: {
1761
+ type: "string",
1762
+ description: "Agent name."
1763
+ },
1764
+ status: {
1765
+ type: "string",
1766
+ enum: [
1767
+ "running",
1768
+ "completed",
1769
+ "failed",
1770
+ "cancelled"
1771
+ ]
1772
+ },
1773
+ errorCode: {
1774
+ type: "string"
1775
+ },
1776
+ threadId: {
1777
+ type: "string"
1778
+ },
1779
+ from: day,
1780
+ to: day,
1781
+ page,
1782
+ limit
1783
+ }
1784
+ },
1785
+ async run(input) {
1786
+ return listOf(await dashboard.runsPage({
1787
+ ...pageOf(input),
1788
+ ...where({
1789
+ agentName: str(input.agent),
1790
+ status: str(input.status),
1791
+ errorCode: str(input.errorCode),
1792
+ threadId: str(input.threadId),
1793
+ fromDay: dayOf(input, "from"),
1794
+ toDay: dayOf(input, "to")
1795
+ })
1796
+ }));
1797
+ }
1798
+ },
1799
+ {
1800
+ name: "agent_get_run",
1801
+ kind: "read",
1802
+ description: "One run: the run, its thread, its tool calls and timings. With includeMessages: true, the thread's latest 50 messages, each cut to 2 KB. An error when no run has that id.",
1803
+ input: {
1804
+ type: "object",
1805
+ properties: {
1806
+ id: {
1807
+ type: "string",
1808
+ description: "Run id."
1809
+ },
1810
+ includeMessages: {
1811
+ type: "boolean",
1812
+ description: "Include messages (default false)."
1813
+ }
1814
+ },
1815
+ required: [
1816
+ "id"
1817
+ ]
1818
+ },
1819
+ async run(input) {
1820
+ let detail;
1821
+ try {
1822
+ detail = await dashboard.runDetail(String(input.id));
1823
+ } catch (error) {
1824
+ if (error instanceof import_common9.NotFoundException) throw new Error(`run ${String(input.id)} not found`);
1825
+ throw error;
1826
+ }
1827
+ if (input.includeMessages !== true) return detail;
1828
+ const thread = await dashboard.threadDetail({
1829
+ threadId: detail.run.threadId,
1830
+ messageLimit: MESSAGE_LIMIT,
1831
+ runLimit: 1
1832
+ });
1833
+ return {
1834
+ ...detail,
1835
+ messages: thread.messages.map((message) => ({
1836
+ ...message,
1837
+ content: cut(message.content, MESSAGE_BYTES)
1838
+ }))
1839
+ };
1840
+ }
1841
+ },
1842
+ {
1843
+ name: "agent_get_spend",
1844
+ kind: "read",
1845
+ description: "Tokens and cost over UTC days from/to (default the last 30 days): by model, by actor, by feature and the daily trend. `by` returns one of them.",
1846
+ input: {
1847
+ type: "object",
1848
+ properties: {
1849
+ by: {
1850
+ type: "string",
1851
+ enum: [
1852
+ "model",
1853
+ "actor",
1854
+ "feature"
1855
+ ]
1856
+ },
1857
+ from: day,
1858
+ to: day
1859
+ }
1860
+ },
1861
+ async run(input) {
1862
+ const spend = await dashboard.spend(rangeOf(input));
1863
+ switch (input.by) {
1864
+ case "model":
1865
+ return {
1866
+ items: spend.byModel
1867
+ };
1868
+ case "actor":
1869
+ return {
1870
+ items: spend.byActor
1871
+ };
1872
+ case "feature":
1873
+ return {
1874
+ items: spend.byFeature ?? []
1875
+ };
1876
+ default:
1877
+ return spend;
1878
+ }
1879
+ }
1880
+ },
1881
+ {
1882
+ name: "agent_get_tool_stats",
1883
+ kind: "read",
1884
+ description: "Per tool: calls, failures, rejections and latency, over UTC days from/to (default the last 30 days).",
1885
+ input: {
1886
+ type: "object",
1887
+ properties: {
1888
+ from: day,
1889
+ to: day
1890
+ }
1891
+ },
1892
+ async run(input) {
1893
+ return {
1894
+ items: await dashboard.toolStats(rangeOf(input))
1895
+ };
1896
+ }
1897
+ },
1898
+ {
1899
+ name: "agent_get_reliability",
1900
+ kind: "read",
1901
+ description: "Run metrics, by-agent and by-error breakdowns and the trend, over UTC days from/to (default the last 30 days).",
1902
+ input: {
1903
+ type: "object",
1904
+ properties: {
1905
+ from: day,
1906
+ to: day
1907
+ }
1908
+ },
1909
+ async run(input) {
1910
+ return dashboard.reliability(rangeOf(input));
1911
+ }
1912
+ },
1913
+ {
1914
+ name: "agent_list_pending_approvals",
1915
+ kind: "read",
1916
+ description: "Tool calls waiting for a human approval, oldest first. Read-only: approve them in the app. Returns { items, next, total }.",
1917
+ input: {
1918
+ type: "object",
1919
+ properties: {
1920
+ toolName: {
1921
+ type: "string"
1922
+ },
1923
+ agent: {
1924
+ type: "string",
1925
+ description: "Agent name."
1926
+ },
1927
+ actor: {
1928
+ type: "string",
1929
+ description: "Actor ref of the thread owner."
1930
+ },
1931
+ threadId: {
1932
+ type: "string"
1933
+ },
1934
+ from: day,
1935
+ to: day,
1936
+ page,
1937
+ limit
1938
+ }
1939
+ },
1940
+ async run(input) {
1941
+ return listOf(await dashboard.approvalsPage({
1942
+ ...pageOf(input),
1943
+ ...where({
1944
+ toolName: str(input.toolName),
1945
+ agentName: str(input.agent),
1946
+ actorRef: str(input.actor),
1947
+ threadId: str(input.threadId),
1948
+ fromDay: dayOf(input, "from"),
1949
+ toDay: dayOf(input, "to")
1950
+ })
1951
+ }));
1952
+ }
1953
+ },
1954
+ {
1955
+ name: "agent_list_recent_tool_calls",
1956
+ kind: "read",
1957
+ description: "Tool calls of every agent, newest first. Filters: toolName, status, threadId, from/to (UTC days). Returns { items, next, total }.",
1958
+ input: {
1959
+ type: "object",
1960
+ properties: {
1961
+ toolName: {
1962
+ type: "string"
1963
+ },
1964
+ status: {
1965
+ type: "string"
1966
+ },
1967
+ threadId: {
1968
+ type: "string"
1969
+ },
1970
+ from: day,
1971
+ to: day,
1972
+ page,
1973
+ limit
1974
+ }
1975
+ },
1976
+ async run(input) {
1977
+ return listOf(await dashboard.toolCallsPage({
1978
+ ...pageOf(input),
1979
+ ...where({
1980
+ toolName: str(input.toolName),
1981
+ status: str(input.status),
1982
+ threadId: str(input.threadId),
1983
+ fromDay: dayOf(input, "from"),
1984
+ toDay: dayOf(input, "to")
1985
+ })
1986
+ }));
1987
+ }
1988
+ }
1989
+ ];
1990
+ }
1991
+ __name(agentDevTools, "agentDevTools");
1992
+ var logger2 = new import_common9.Logger("AgentDashboardModule");
1993
+ var AgentDevToolsRegistration = class {
1994
+ static {
1995
+ __name(this, "AgentDevToolsRegistration");
1996
+ }
1997
+ enabled;
1998
+ dashboard;
1999
+ access;
2000
+ #unregister;
2001
+ constructor(enabled, dashboard, access) {
2002
+ this.enabled = enabled;
2003
+ this.dashboard = dashboard;
2004
+ this.access = access;
2005
+ }
2006
+ onModuleInit() {
2007
+ if (!this.enabled) return;
2008
+ const slot = devToolsSlot();
2009
+ if (slot.version !== 1 || typeof slot.register !== "function") return;
2010
+ try {
2011
+ this.#unregister = slot.register({
2012
+ lib: "agent",
2013
+ title: "Agent governance",
2014
+ authorize: /* @__PURE__ */ __name((context) => this.access.check(context), "authorize"),
2015
+ tools: agentDevTools(this.dashboard)
2016
+ });
2017
+ } catch (error) {
2018
+ logger2.warn(`agent dev tools not registered: ${error instanceof Error ? error.message : String(error)}`);
2019
+ }
2020
+ }
2021
+ onModuleDestroy() {
2022
+ this.#unregister?.();
2023
+ this.#unregister = void 0;
2024
+ }
2025
+ };
2026
+ AgentDevToolsRegistration = _ts_decorate5([
2027
+ (0, import_common9.Injectable)(),
2028
+ _ts_param5(0, (0, import_common9.Inject)(DASHBOARD_DEV_TOOLS)),
2029
+ _ts_metadata5("design:type", Function),
2030
+ _ts_metadata5("design:paramtypes", [
2031
+ Boolean,
2032
+ typeof DashboardService === "undefined" ? Object : DashboardService,
2033
+ typeof DashboardConsoleAccess === "undefined" ? Object : DashboardConsoleAccess
2034
+ ])
2035
+ ], AgentDevToolsRegistration);
2036
+
1212
2037
  // src/server/agent-dashboard.module.ts
1213
- function _ts_decorate4(decorators, target, key, desc) {
2038
+ function _ts_decorate6(decorators, target, key, desc) {
1214
2039
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1215
2040
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
1216
2041
  r = Reflect.decorate(decorators, target, key, desc);
@@ -1223,23 +2048,29 @@ function _ts_decorate4(decorators, target, key, desc) {
1223
2048
  }
1224
2049
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1225
2050
  }
1226
- __name(_ts_decorate4, "_ts_decorate");
2051
+ __name(_ts_decorate6, "_ts_decorate");
1227
2052
  function normalize(path) {
1228
2053
  return normalizeDashboardPath(path);
1229
2054
  }
1230
2055
  __name(normalize, "normalize");
1231
- function assertProtected(options) {
1232
- if (process.env.NODE_ENV !== "production") return;
1233
- if (options.routes === false) return;
1234
- if ((options.decorators ?? []).length > 0) return;
1235
- throw new Error("AgentDashboardModule: the AI-gateway console would be served open in production. Say who reaches it, with `decorators: [UseGuards(AuthenticationGuard), Can(ConsolePolicy, 'view')]`, or mount its routes yourself with `routes: false` and extend `AgentApiController` / `AgentUiController` under your own guards.");
2056
+ function consoleDecorators(decorators) {
2057
+ return [
2058
+ ...decorators ?? [],
2059
+ (0, import_common10.UseGuards)(ConsoleAuthorizeGuard),
2060
+ (0, import_common10.UseFilters)(ConsoleAuthorizeFilter)
2061
+ ];
1236
2062
  }
1237
- __name(assertProtected, "assertProtected");
2063
+ __name(consoleDecorators, "consoleDecorators");
1238
2064
  var AgentApiModule = class _AgentApiModule {
1239
2065
  static {
1240
2066
  __name(this, "AgentApiModule");
1241
2067
  }
1242
2068
  static register(options) {
2069
+ const consoleOptions = {
2070
+ ...options.authorize !== void 0 ? {
2071
+ authorize: options.authorize
2072
+ } : {}
2073
+ };
1243
2074
  return {
1244
2075
  module: _AgentApiModule,
1245
2076
  imports: [
@@ -1247,7 +2078,7 @@ var AgentApiModule = class _AgentApiModule {
1247
2078
  ],
1248
2079
  controllers: options.routes === false ? [] : (0, import_nestjs_agent_core3.withControllerDecorators)([
1249
2080
  AgentApiController
1250
- ], options.decorators),
2081
+ ], consoleDecorators(options.decorators)),
1251
2082
  providers: [
1252
2083
  DashboardService,
1253
2084
  // `useValue` even when `options.approvalActorRef` is `undefined` — AgentApiController
@@ -1255,25 +2086,36 @@ var AgentApiModule = class _AgentApiModule {
1255
2086
  {
1256
2087
  provide: DASHBOARD_APPROVAL_ACTOR_REF,
1257
2088
  useValue: options.approvalActorRef
1258
- }
2089
+ },
2090
+ {
2091
+ provide: DASHBOARD_CONSOLE_OPTIONS,
2092
+ useValue: consoleOptions
2093
+ },
2094
+ DashboardConsoleAccess,
2095
+ {
2096
+ provide: DASHBOARD_DEV_TOOLS,
2097
+ useValue: options.devTools ?? options.routes !== false
2098
+ },
2099
+ AgentDevToolsRegistration
1259
2100
  ],
1260
- // What a self-mounted `AgentApiController` subclass injects, too (`routes: false`).
2101
+ // What a self-mounted `AgentApiController` subclass injects, too (`routes: false`), and the
2102
+ // access check `UseConsoleAuthorize()` runs.
1261
2103
  exports: [
1262
2104
  DashboardService,
1263
- DASHBOARD_APPROVAL_ACTOR_REF
2105
+ DASHBOARD_APPROVAL_ACTOR_REF,
2106
+ DashboardConsoleAccess
1264
2107
  ]
1265
2108
  };
1266
2109
  }
1267
2110
  };
1268
- AgentApiModule = _ts_decorate4([
1269
- (0, import_common7.Module)({})
2111
+ AgentApiModule = _ts_decorate6([
2112
+ (0, import_common10.Module)({})
1270
2113
  ], AgentApiModule);
1271
2114
  var AgentDashboardModule = class _AgentDashboardModule {
1272
2115
  static {
1273
2116
  __name(this, "AgentDashboardModule");
1274
2117
  }
1275
2118
  static forRoot(options = {}) {
1276
- assertProtected(options);
1277
2119
  const basePath = normalize(options.basePath ?? "/ai-gateway");
1278
2120
  const apiBasePath = normalize(options.apiBasePath ?? `${basePath}/api`);
1279
2121
  return {
@@ -1293,10 +2135,16 @@ var AgentDashboardModule = class _AgentDashboardModule {
1293
2135
  } : {},
1294
2136
  ...options.approvalActorRef ? {
1295
2137
  approvalActorRef: options.approvalActorRef
2138
+ } : {},
2139
+ ...options.authorize !== void 0 ? {
2140
+ authorize: options.authorize
2141
+ } : {},
2142
+ ...options.devTools !== void 0 ? {
2143
+ devTools: options.devTools
1296
2144
  } : {}
1297
2145
  }),
1298
2146
  ...options.routes === false ? [] : [
1299
- import_core.RouterModule.register([
2147
+ import_core3.RouterModule.register([
1300
2148
  {
1301
2149
  path: basePath,
1302
2150
  module: _AgentDashboardModule
@@ -1310,7 +2158,7 @@ var AgentDashboardModule = class _AgentDashboardModule {
1310
2158
  ],
1311
2159
  controllers: options.routes === false ? [] : (0, import_nestjs_agent_core3.withControllerDecorators)([
1312
2160
  AgentUiController
1313
- ], options.decorators),
2161
+ ], consoleDecorators(options.decorators)),
1314
2162
  providers: [
1315
2163
  {
1316
2164
  provide: DASHBOARD_BASE_PATH,
@@ -1331,8 +2179,8 @@ var AgentDashboardModule = class _AgentDashboardModule {
1331
2179
  };
1332
2180
  }
1333
2181
  };
1334
- AgentDashboardModule = _ts_decorate4([
1335
- (0, import_common7.Module)({})
2182
+ AgentDashboardModule = _ts_decorate6([
2183
+ (0, import_common10.Module)({})
1336
2184
  ], AgentDashboardModule);
1337
2185
  // Annotate the CommonJS export names for ESM import in node:
1338
2186
  0 && (module.exports = {
@@ -1344,12 +2192,29 @@ AgentDashboardModule = _ts_decorate4([
1344
2192
  AgentApiController,
1345
2193
  AgentApiModule,
1346
2194
  AgentDashboardModule,
2195
+ AgentDevToolsRegistration,
1347
2196
  AgentUiController,
2197
+ CONSOLE_AUTHORIZE_MISSING_WARNING,
2198
+ CONSOLE_GATE,
2199
+ CONSOLE_PAGE_METADATA,
2200
+ ConsoleAccessDenied,
2201
+ ConsoleAuthorizeFilter,
2202
+ ConsoleAuthorizeGuard,
2203
+ ConsolePage,
1348
2204
  DASHBOARD_API_PATH,
1349
2205
  DASHBOARD_APPROVAL_ACTOR_REF,
1350
2206
  DASHBOARD_BASE_PATH,
2207
+ DASHBOARD_CONSOLE_OPTIONS,
2208
+ DASHBOARD_DEV_TOOLS,
2209
+ DashboardConsoleAccess,
1351
2210
  DashboardService,
2211
+ UseConsoleAuthorize,
1352
2212
  agentDashboardMountPaths,
1353
- parsePriceInput
2213
+ agentDevTools,
2214
+ anyAuthorize,
2215
+ authorizeByToken,
2216
+ normalizeConsoleResult,
2217
+ parsePriceInput,
2218
+ resolveConsoleAuthorize
1354
2219
  });
1355
2220
  //# sourceMappingURL=index.cjs.map