@dudousxd/nestjs-agent-dashboard 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server/index.cjs +795 -102
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +164 -3
- package/dist/server/index.d.ts +164 -3
- package/dist/server/index.js +768 -87
- package/dist/server/index.js.map +1 -1
- package/dist/spa/assets/index-BW1bqmTf.css +1 -0
- package/dist/spa/assets/index-C4U9WEBt.js +1 -0
- package/dist/spa/assets/{preview-CbqJ2CAQ.js → preview-CJ1UfhOA.js} +1 -1
- package/dist/spa/index.html +3 -3
- package/dist/spa/preview.html +3 -3
- package/package.json +1 -1
- package/dist/spa/assets/index-ClnZsvJ1.css +0 -1
- package/dist/spa/assets/index-D3yTBCU0.js +0 -1
- /package/dist/spa/assets/{index-DS11tOEH.js → index-Br3YfiYJ.js} +0 -0
package/dist/server/index.js
CHANGED
|
@@ -83,6 +83,7 @@ var DASHBOARD_BASE_PATH = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent-das
|
|
|
83
83
|
var DASHBOARD_API_PATH = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent-dashboard:api-path");
|
|
84
84
|
var DASHBOARD_APPROVAL_ACTOR_REF = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent-dashboard:approval-actor-ref");
|
|
85
85
|
var DASHBOARD_CONSOLE_OPTIONS = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent-dashboard:console-options");
|
|
86
|
+
var DASHBOARD_DEV_TOOLS = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent-dashboard:dev-tools");
|
|
86
87
|
|
|
87
88
|
// src/server/dashboard.service.ts
|
|
88
89
|
function _ts_decorate(decorators, target, key, desc) {
|
|
@@ -161,8 +162,8 @@ var DashboardService = class {
|
|
|
161
162
|
};
|
|
162
163
|
}
|
|
163
164
|
/** Top threads by cost for a day range (default 10, highest cost first). */
|
|
164
|
-
async topThreads(range,
|
|
165
|
-
const rows = await this.queries.spendByThread(range,
|
|
165
|
+
async topThreads(range, limit2 = 10) {
|
|
166
|
+
const rows = await this.queries.spendByThread(range, limit2);
|
|
166
167
|
return this.withActorLabels(rows);
|
|
167
168
|
}
|
|
168
169
|
/** Run reliability for a day range: metrics, by-agent/by-error breakdowns and the trend, in parallel. */
|
|
@@ -181,12 +182,12 @@ var DashboardService = class {
|
|
|
181
182
|
};
|
|
182
183
|
}
|
|
183
184
|
/** Most recent runs (status/agent/duration/error) for the Reliability recent-runs table. */
|
|
184
|
-
recentRuns(
|
|
185
|
-
return this.queries.recentRuns(
|
|
185
|
+
recentRuns(limit2) {
|
|
186
|
+
return this.queries.recentRuns(limit2);
|
|
186
187
|
}
|
|
187
188
|
/** Most recent tool calls (status/type/thread) for the Runs & tools activity feed. */
|
|
188
|
-
recentToolCalls(
|
|
189
|
-
return this.queries.recentToolCalls(
|
|
189
|
+
recentToolCalls(limit2) {
|
|
190
|
+
return this.queries.recentToolCalls(limit2);
|
|
190
191
|
}
|
|
191
192
|
/** Paged, filterable tool calls for the Runs & tools activity table. */
|
|
192
193
|
toolCallsPage(query) {
|
|
@@ -201,8 +202,8 @@ var DashboardService = class {
|
|
|
201
202
|
* Capped and totalless — kept because the SPA still calls `GET approvals`; new callers want
|
|
202
203
|
* {@link approvalsPage}, which reports how much of the backlog the cap hid.
|
|
203
204
|
*/
|
|
204
|
-
pendingApprovals(
|
|
205
|
-
return this.queries.pendingApprovals(
|
|
205
|
+
pendingApprovals(limit2) {
|
|
206
|
+
return this.queries.pendingApprovals(limit2);
|
|
206
207
|
}
|
|
207
208
|
/** Paged, filterable approvals inbox with a total — the backlog-safe read of the HITL queue. */
|
|
208
209
|
approvalsPage(query) {
|
|
@@ -290,8 +291,8 @@ var DashboardService = class {
|
|
|
290
291
|
});
|
|
291
292
|
}
|
|
292
293
|
/** Most recent threads with rolled-up message/token counts. */
|
|
293
|
-
async recentThreads(
|
|
294
|
-
const rows = await this.queries.recentThreads(
|
|
294
|
+
async recentThreads(limit2) {
|
|
295
|
+
const rows = await this.queries.recentThreads(limit2);
|
|
295
296
|
return this.withActorLabels(rows);
|
|
296
297
|
}
|
|
297
298
|
/**
|
|
@@ -299,10 +300,10 @@ var DashboardService = class {
|
|
|
299
300
|
* {@link recentThreads} is.
|
|
300
301
|
*/
|
|
301
302
|
async threadsPage(query) {
|
|
302
|
-
const
|
|
303
|
-
const rows = await this.withActorLabels(
|
|
303
|
+
const page2 = await this.queries.threadsPage(query);
|
|
304
|
+
const rows = await this.withActorLabels(page2.rows);
|
|
304
305
|
return {
|
|
305
|
-
...
|
|
306
|
+
...page2,
|
|
306
307
|
rows
|
|
307
308
|
};
|
|
308
309
|
}
|
|
@@ -623,27 +624,27 @@ var AgentApiController = class {
|
|
|
623
624
|
return this.dashboard.spend(resolveRange(from, to));
|
|
624
625
|
}
|
|
625
626
|
/** Top threads by cost (default 10, max 200) for a day range (defaults to the last 30 days). */
|
|
626
|
-
topThreads(from, to,
|
|
627
|
-
return this.dashboard.topThreads(resolveRange(from, to), parseLimit(
|
|
627
|
+
topThreads(from, to, limit2) {
|
|
628
|
+
return this.dashboard.topThreads(resolveRange(from, to), parseLimit(limit2, 10));
|
|
628
629
|
}
|
|
629
630
|
/** `{ metrics, byAgent, errors, trend }` for a day range (defaults to the last 30 days). */
|
|
630
631
|
reliability(from, to) {
|
|
631
632
|
return this.dashboard.reliability(resolveRange(from, to));
|
|
632
633
|
}
|
|
633
634
|
/** Most recent runs (default 50, max 200) for the Reliability recent-runs table. */
|
|
634
|
-
runs(
|
|
635
|
-
return this.dashboard.recentRuns(parseLimit(
|
|
635
|
+
runs(limit2) {
|
|
636
|
+
return this.dashboard.recentRuns(parseLimit(limit2, 50));
|
|
636
637
|
}
|
|
637
638
|
/**
|
|
638
639
|
* Paged, filterable runs for the Reliability recent-runs table. `page` (default 1), `limit`
|
|
639
640
|
* (default 25, max 200), and `where[agentName]`/`where[status]`/`where[errorCode]`/
|
|
640
641
|
* `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.
|
|
641
642
|
*/
|
|
642
|
-
runsPage(
|
|
643
|
-
const parsedWhere = parseRunWhere(
|
|
643
|
+
runsPage(page2, limit2, where2) {
|
|
644
|
+
const parsedWhere = parseRunWhere(where2);
|
|
644
645
|
return this.dashboard.runsPage({
|
|
645
|
-
page: parsePageNumber(
|
|
646
|
-
pageSize: parseLimit(
|
|
646
|
+
page: parsePageNumber(page2),
|
|
647
|
+
pageSize: parseLimit(limit2, 25),
|
|
647
648
|
...parsedWhere !== void 0 ? {
|
|
648
649
|
where: parsedWhere
|
|
649
650
|
} : {}
|
|
@@ -660,19 +661,19 @@ var AgentApiController = class {
|
|
|
660
661
|
return this.dashboard.runDetail(runId);
|
|
661
662
|
}
|
|
662
663
|
/** Most recent tool calls (default 50, max 200) for the activity feed. */
|
|
663
|
-
toolCalls(
|
|
664
|
-
return this.dashboard.recentToolCalls(parseLimit(
|
|
664
|
+
toolCalls(limit2) {
|
|
665
|
+
return this.dashboard.recentToolCalls(parseLimit(limit2, 50));
|
|
665
666
|
}
|
|
666
667
|
/**
|
|
667
668
|
* Paged, filterable tool calls for the Runs & tools activity table. `page` (default 1), `limit`
|
|
668
669
|
* (default 25, max 200), and `where[toolName]`/`where[toolType]`/`where[status]`/`where[threadId]`/
|
|
669
670
|
* `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.
|
|
670
671
|
*/
|
|
671
|
-
toolCallsPage(
|
|
672
|
-
const parsedWhere = parseToolCallWhere(
|
|
672
|
+
toolCallsPage(page2, limit2, where2) {
|
|
673
|
+
const parsedWhere = parseToolCallWhere(where2);
|
|
673
674
|
return this.dashboard.toolCallsPage({
|
|
674
|
-
page: parsePageNumber(
|
|
675
|
-
pageSize: parseLimit(
|
|
675
|
+
page: parsePageNumber(page2),
|
|
676
|
+
pageSize: parseLimit(limit2, 25),
|
|
676
677
|
...parsedWhere !== void 0 ? {
|
|
677
678
|
where: parsedWhere
|
|
678
679
|
} : {}
|
|
@@ -684,8 +685,8 @@ var AgentApiController = class {
|
|
|
684
685
|
* Capped with NO total, so a backlog past `limit` is invisible from the response alone. Kept
|
|
685
686
|
* because the SPA still calls it; anything new should call `approvals-page` instead.
|
|
686
687
|
*/
|
|
687
|
-
approvals(
|
|
688
|
-
return this.dashboard.pendingApprovals(parseLimit(
|
|
688
|
+
approvals(limit2) {
|
|
689
|
+
return this.dashboard.pendingApprovals(parseLimit(limit2, 50));
|
|
689
690
|
}
|
|
690
691
|
/**
|
|
691
692
|
* Paged, filterable approvals inbox, oldest first. `page` (default 1), `limit` (default 25, max
|
|
@@ -695,11 +696,11 @@ var AgentApiController = class {
|
|
|
695
696
|
* Unlike `GET approvals` this reports `total`, so the console can say "showing N of M" instead of
|
|
696
697
|
* silently hiding pending decisions behind a row cap.
|
|
697
698
|
*/
|
|
698
|
-
approvalsPage(
|
|
699
|
-
const parsedWhere = parseApprovalWhere(
|
|
699
|
+
approvalsPage(page2, limit2, where2) {
|
|
700
|
+
const parsedWhere = parseApprovalWhere(where2);
|
|
700
701
|
return this.dashboard.approvalsPage({
|
|
701
|
-
page: parsePageNumber(
|
|
702
|
-
pageSize: parseLimit(
|
|
702
|
+
page: parsePageNumber(page2),
|
|
703
|
+
pageSize: parseLimit(limit2, 25),
|
|
703
704
|
...parsedWhere !== void 0 ? {
|
|
704
705
|
where: parsedWhere
|
|
705
706
|
} : {}
|
|
@@ -746,19 +747,19 @@ var AgentApiController = class {
|
|
|
746
747
|
return this.dashboard.genuiStats(resolveRange(from, to));
|
|
747
748
|
}
|
|
748
749
|
/** Most recent threads (default 50, max 200) with rolled-up counts. */
|
|
749
|
-
threads(
|
|
750
|
-
return this.dashboard.recentThreads(parseLimit(
|
|
750
|
+
threads(limit2) {
|
|
751
|
+
return this.dashboard.recentThreads(parseLimit(limit2, 50));
|
|
751
752
|
}
|
|
752
753
|
/**
|
|
753
754
|
* Paged, filterable threads for the Runs & tools threads table. `page` (default 1), `limit`
|
|
754
755
|
* (default 25, max 200), and `where[actorRef]`/`where[title]` (case-insensitive substring)/
|
|
755
756
|
* `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.
|
|
756
757
|
*/
|
|
757
|
-
threadsPage(
|
|
758
|
-
const parsedWhere = parseThreadWhere(
|
|
758
|
+
threadsPage(page2, limit2, where2) {
|
|
759
|
+
const parsedWhere = parseThreadWhere(where2);
|
|
759
760
|
return this.dashboard.threadsPage({
|
|
760
|
-
page: parsePageNumber(
|
|
761
|
-
pageSize: parseLimit(
|
|
761
|
+
page: parsePageNumber(page2),
|
|
762
|
+
pageSize: parseLimit(limit2, 25),
|
|
762
763
|
...parsedWhere !== void 0 ? {
|
|
763
764
|
where: parsedWhere
|
|
764
765
|
} : {}
|
|
@@ -1051,24 +1052,28 @@ import { RouterModule } from "@nestjs/core";
|
|
|
1051
1052
|
import { existsSync, readFileSync } from "fs";
|
|
1052
1053
|
import { basename, extname, join, resolve, sep } from "path";
|
|
1053
1054
|
import { fileURLToPath } from "url";
|
|
1054
|
-
import { CanAnyone as
|
|
1055
|
-
import { Controller as
|
|
1055
|
+
import { CanAnyone as CanAnyone3 } from "@dudousxd/nestjs-agent-core";
|
|
1056
|
+
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";
|
|
1056
1057
|
|
|
1057
1058
|
// src/server/console-authorize.guard.ts
|
|
1058
1059
|
import { Catch, HttpException, Inject as Inject3, Injectable as Injectable2, SetMetadata, UseFilters, UseGuards, applyDecorators } from "@nestjs/common";
|
|
1059
|
-
import { HttpAdapterHost, ModuleRef } from "@nestjs/core";
|
|
1060
|
+
import { HttpAdapterHost, ModuleRef as ModuleRef2 } from "@nestjs/core";
|
|
1060
1061
|
|
|
1061
1062
|
// src/server/console-authorize.ts
|
|
1062
1063
|
import { createHash, timingSafeEqual } from "crypto";
|
|
1063
1064
|
import { Logger } from "@nestjs/common";
|
|
1065
|
+
import { ModuleRef } from "@nestjs/core";
|
|
1064
1066
|
var CONSOLE_GATE = /* @__PURE__ */ Symbol.for("@dudousxd/aviary:console-gate");
|
|
1065
1067
|
var PACKAGE = "@dudousxd/nestjs-agent-dashboard";
|
|
1066
1068
|
var MODULE = "AgentDashboardModule";
|
|
1067
1069
|
var logger = new Logger(MODULE);
|
|
1068
1070
|
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().`;
|
|
1069
|
-
var allow = /* @__PURE__ */ __name(() =>
|
|
1071
|
+
var allow = /* @__PURE__ */ __name((caller) => caller !== void 0 && caller !== "" ? {
|
|
1072
|
+
allowed: true,
|
|
1073
|
+
caller
|
|
1074
|
+
} : {
|
|
1070
1075
|
allowed: true
|
|
1071
|
-
}
|
|
1076
|
+
}, "allow");
|
|
1072
1077
|
var forbidden = /* @__PURE__ */ __name(() => ({
|
|
1073
1078
|
allowed: false,
|
|
1074
1079
|
reason: "forbidden"
|
|
@@ -1092,7 +1097,9 @@ function normalizeConsoleResult(result) {
|
|
|
1092
1097
|
redirectTo: value.redirectTo
|
|
1093
1098
|
};
|
|
1094
1099
|
}
|
|
1095
|
-
if (value.allowed === true)
|
|
1100
|
+
if (value.allowed === true) {
|
|
1101
|
+
return allow(typeof value.caller === "string" ? value.caller : void 0);
|
|
1102
|
+
}
|
|
1096
1103
|
return value.reason === "unauthenticated" ? unauthenticated() : forbidden();
|
|
1097
1104
|
}
|
|
1098
1105
|
__name(normalizeConsoleResult, "normalizeConsoleResult");
|
|
@@ -1168,7 +1175,7 @@ async function resolveConsoleAuthorize(authorize, moduleRef, options = {}) {
|
|
|
1168
1175
|
if (authorize === void 0) {
|
|
1169
1176
|
if (options.warnIfMissing !== false) log.warn(CONSOLE_AUTHORIZE_MISSING_WARNING);
|
|
1170
1177
|
const production = isProduction();
|
|
1171
|
-
return async () => production ? forbidden() : allow();
|
|
1178
|
+
return async () => production ? forbidden() : allow("dev-open");
|
|
1172
1179
|
}
|
|
1173
1180
|
const instanceOf = classInstances(moduleRef);
|
|
1174
1181
|
let decide;
|
|
@@ -1218,12 +1225,18 @@ function authorizeByToken(options) {
|
|
|
1218
1225
|
throw new Error("authorizeByToken: `token` is empty; set it (e.g. from an env var).");
|
|
1219
1226
|
}
|
|
1220
1227
|
const expected = sha256(token);
|
|
1228
|
+
const caller = `token:${expected.toString("hex").slice(0, 12)}`;
|
|
1221
1229
|
return (context) => {
|
|
1222
1230
|
const req = context.switchToHttp().getRequest();
|
|
1223
1231
|
const header = req?.headers?.authorization;
|
|
1224
1232
|
const value = Array.isArray(header) ? header[0] : header;
|
|
1225
1233
|
const match = typeof value === "string" ? /^Bearer\s+(.+)$/i.exec(value.trim()) : null;
|
|
1226
|
-
if (match?.[1] !== void 0 && timingSafeEqual(sha256(match[1]), expected))
|
|
1234
|
+
if (match?.[1] !== void 0 && timingSafeEqual(sha256(match[1]), expected)) {
|
|
1235
|
+
return {
|
|
1236
|
+
allowed: true,
|
|
1237
|
+
caller
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1227
1240
|
return {
|
|
1228
1241
|
allowed: false,
|
|
1229
1242
|
reason: "unauthenticated"
|
|
@@ -1231,6 +1244,48 @@ function authorizeByToken(options) {
|
|
|
1231
1244
|
};
|
|
1232
1245
|
}
|
|
1233
1246
|
__name(authorizeByToken, "authorizeByToken");
|
|
1247
|
+
function anyAuthorize(...forms) {
|
|
1248
|
+
if (forms.length === 0) throw new Error("anyAuthorize: pass at least one authorize form.");
|
|
1249
|
+
let AnyConsoleAuthorizer = class AnyConsoleAuthorizer {
|
|
1250
|
+
static {
|
|
1251
|
+
__name(this, "AnyConsoleAuthorizer");
|
|
1252
|
+
}
|
|
1253
|
+
moduleRef;
|
|
1254
|
+
#decides;
|
|
1255
|
+
constructor(moduleRef) {
|
|
1256
|
+
this.moduleRef = moduleRef;
|
|
1257
|
+
}
|
|
1258
|
+
async authorize(context) {
|
|
1259
|
+
this.#decides ??= Promise.all(forms.map((form) => resolveConsoleAuthorize(form, this.moduleRef, {
|
|
1260
|
+
warnIfMissing: false
|
|
1261
|
+
})));
|
|
1262
|
+
this.#decides.catch(() => {
|
|
1263
|
+
this.#decides = void 0;
|
|
1264
|
+
});
|
|
1265
|
+
let unauthenticated2;
|
|
1266
|
+
for (const decide of await this.#decides) {
|
|
1267
|
+
const decision = await decide(context);
|
|
1268
|
+
if (decision.allowed) return decision;
|
|
1269
|
+
if (decision.reason === "unauthenticated") unauthenticated2 ??= decision;
|
|
1270
|
+
}
|
|
1271
|
+
if (unauthenticated2 === void 0) return {
|
|
1272
|
+
allowed: false,
|
|
1273
|
+
reason: "forbidden"
|
|
1274
|
+
};
|
|
1275
|
+
return "redirectTo" in unauthenticated2 && unauthenticated2.redirectTo !== void 0 ? {
|
|
1276
|
+
redirectTo: unauthenticated2.redirectTo
|
|
1277
|
+
} : {
|
|
1278
|
+
allowed: false,
|
|
1279
|
+
reason: "unauthenticated"
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
1282
|
+
};
|
|
1283
|
+
Reflect.defineMetadata("design:paramtypes", [
|
|
1284
|
+
ModuleRef
|
|
1285
|
+
], AnyConsoleAuthorizer);
|
|
1286
|
+
return AnyConsoleAuthorizer;
|
|
1287
|
+
}
|
|
1288
|
+
__name(anyAuthorize, "anyAuthorize");
|
|
1234
1289
|
|
|
1235
1290
|
// src/server/console-authorize.guard.ts
|
|
1236
1291
|
function _ts_decorate3(decorators, target, key, desc) {
|
|
@@ -1288,7 +1343,7 @@ DashboardConsoleAccess = _ts_decorate3([
|
|
|
1288
1343
|
_ts_metadata3("design:type", Function),
|
|
1289
1344
|
_ts_metadata3("design:paramtypes", [
|
|
1290
1345
|
typeof DashboardConsoleOptions === "undefined" ? Object : DashboardConsoleOptions,
|
|
1291
|
-
typeof
|
|
1346
|
+
typeof ModuleRef2 === "undefined" ? Object : ModuleRef2
|
|
1292
1347
|
])
|
|
1293
1348
|
], DashboardConsoleAccess);
|
|
1294
1349
|
var CONSOLE_PAGE_METADATA = "@dudousxd/nestjs-agent-dashboard:console-page";
|
|
@@ -1299,7 +1354,7 @@ var ConsoleAccessDenied = class extends HttpException {
|
|
|
1299
1354
|
}
|
|
1300
1355
|
decision;
|
|
1301
1356
|
page;
|
|
1302
|
-
constructor(decision,
|
|
1357
|
+
constructor(decision, page2) {
|
|
1303
1358
|
super(decision.reason === "forbidden" ? {
|
|
1304
1359
|
error: "forbidden"
|
|
1305
1360
|
} : {
|
|
@@ -1307,13 +1362,13 @@ var ConsoleAccessDenied = class extends HttpException {
|
|
|
1307
1362
|
...decision.redirectTo !== void 0 ? {
|
|
1308
1363
|
redirectTo: decision.redirectTo
|
|
1309
1364
|
} : {}
|
|
1310
|
-
}, decision.reason === "forbidden" ? 403 : 401), this.decision = decision, this.page =
|
|
1365
|
+
}, decision.reason === "forbidden" ? 403 : 401), this.decision = decision, this.page = page2;
|
|
1311
1366
|
}
|
|
1312
1367
|
};
|
|
1313
1368
|
function isPageNavigation(context) {
|
|
1314
1369
|
if (context.getType() !== "http") return false;
|
|
1315
|
-
const
|
|
1316
|
-
if (!
|
|
1370
|
+
const page2 = Reflect.getMetadata(CONSOLE_PAGE_METADATA, context.getHandler()) === true || Reflect.getMetadata(CONSOLE_PAGE_METADATA, context.getClass()) === true;
|
|
1371
|
+
if (!page2) return false;
|
|
1317
1372
|
const req = context.switchToHttp().getRequest();
|
|
1318
1373
|
if ((req.method ?? "GET").toUpperCase() !== "GET") return false;
|
|
1319
1374
|
const accept = req.headers?.accept;
|
|
@@ -1360,12 +1415,12 @@ var ConsoleAuthorizeFilter = class {
|
|
|
1360
1415
|
catch(exception, host) {
|
|
1361
1416
|
const adapter = this.adapterHost.httpAdapter;
|
|
1362
1417
|
const res = host.switchToHttp().getResponse();
|
|
1363
|
-
const { decision, page } = exception;
|
|
1364
|
-
if (
|
|
1418
|
+
const { decision, page: page2 } = exception;
|
|
1419
|
+
if (page2 && decision.reason === "unauthenticated" && decision.redirectTo !== void 0) {
|
|
1365
1420
|
adapter.redirect(res, 302, decision.redirectTo);
|
|
1366
1421
|
return;
|
|
1367
1422
|
}
|
|
1368
|
-
if (
|
|
1423
|
+
if (page2) {
|
|
1369
1424
|
const forbidden2 = decision.reason === "forbidden";
|
|
1370
1425
|
adapter.setHeader(res, "Content-Type", "text/html; charset=utf-8");
|
|
1371
1426
|
adapter.reply(res, forbidden2 ? FORBIDDEN_PAGE : UNAUTHENTICATED_PAGE, forbidden2 ? 403 : 401);
|
|
@@ -1387,7 +1442,9 @@ function UseConsoleAuthorize() {
|
|
|
1387
1442
|
}
|
|
1388
1443
|
__name(UseConsoleAuthorize, "UseConsoleAuthorize");
|
|
1389
1444
|
|
|
1390
|
-
// src/server/
|
|
1445
|
+
// src/server/console-switcher.ts
|
|
1446
|
+
import { CanAnyone as CanAnyone2 } from "@dudousxd/nestjs-agent-core";
|
|
1447
|
+
import { Controller as Controller2, Get as Get2, Header, Inject as Inject4, Injectable as Injectable3, Logger as Logger2, createParamDecorator } from "@nestjs/common";
|
|
1391
1448
|
function _ts_decorate4(decorators, target, key, desc) {
|
|
1392
1449
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1393
1450
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
@@ -1414,7 +1471,186 @@ function _ts_param4(paramIndex, decorator) {
|
|
|
1414
1471
|
};
|
|
1415
1472
|
}
|
|
1416
1473
|
__name(_ts_param4, "_ts_param");
|
|
1474
|
+
var AVIARY_CONSOLES_SLOT = /* @__PURE__ */ Symbol.for("@dudousxd/aviary:consoles");
|
|
1475
|
+
var ID = /^[a-z][a-z0-9-]*$/;
|
|
1476
|
+
var ICONS = /* @__PURE__ */ new Set([
|
|
1477
|
+
"workflow",
|
|
1478
|
+
"telescope",
|
|
1479
|
+
"media",
|
|
1480
|
+
"payments",
|
|
1481
|
+
"agent",
|
|
1482
|
+
"identity",
|
|
1483
|
+
"generic"
|
|
1484
|
+
]);
|
|
1485
|
+
var CONSOLE_PROBE_TIMEOUT_MS = 300;
|
|
1486
|
+
function isSameOriginPath(path) {
|
|
1487
|
+
return typeof path === "string" && path.startsWith("/") && !path.startsWith("//");
|
|
1488
|
+
}
|
|
1489
|
+
__name(isSameOriginPath, "isSameOriginPath");
|
|
1490
|
+
function readAviaryConsoles() {
|
|
1491
|
+
const value = globalThis[AVIARY_CONSOLES_SLOT];
|
|
1492
|
+
if (value?.version !== 1 || !Array.isArray(value.entries)) return void 0;
|
|
1493
|
+
return value;
|
|
1494
|
+
}
|
|
1495
|
+
__name(readAviaryConsoles, "readAviaryConsoles");
|
|
1496
|
+
function registerAviaryConsole(entry, logger4) {
|
|
1497
|
+
if (!ID.test(entry.id)) throw new Error(`console id ${JSON.stringify(entry.id)} is invalid`);
|
|
1498
|
+
if (!isSameOriginPath(entry.path)) {
|
|
1499
|
+
throw new Error(`console path ${JSON.stringify(entry.path)} must start with a single "/"`);
|
|
1500
|
+
}
|
|
1501
|
+
const holder = globalThis;
|
|
1502
|
+
if (holder[AVIARY_CONSOLES_SLOT] === void 0) {
|
|
1503
|
+
const fresh = {
|
|
1504
|
+
version: 1,
|
|
1505
|
+
entries: []
|
|
1506
|
+
};
|
|
1507
|
+
holder[AVIARY_CONSOLES_SLOT] = fresh;
|
|
1508
|
+
}
|
|
1509
|
+
const registry = readAviaryConsoles();
|
|
1510
|
+
if (registry === void 0) return void 0;
|
|
1511
|
+
if (registry.entries.some((existing) => existing.id === entry.id)) {
|
|
1512
|
+
logger4?.warn(`console id "${entry.id}" is already registered`);
|
|
1513
|
+
return void 0;
|
|
1514
|
+
}
|
|
1515
|
+
registry.entries.push(entry);
|
|
1516
|
+
return () => {
|
|
1517
|
+
const index = registry.entries.indexOf(entry);
|
|
1518
|
+
if (index !== -1) registry.entries.splice(index, 1);
|
|
1519
|
+
};
|
|
1520
|
+
}
|
|
1521
|
+
__name(registerAviaryConsole, "registerAviaryConsole");
|
|
1522
|
+
function consoleSwitcherConfig(selfId, endpoint) {
|
|
1523
|
+
const registry = readAviaryConsoles();
|
|
1524
|
+
if (!registry?.entries.some((entry) => entry.id === selfId)) return void 0;
|
|
1525
|
+
const others = registry.entries.filter((entry) => entry.id !== selfId).length;
|
|
1526
|
+
return others > 0 ? {
|
|
1527
|
+
endpoint,
|
|
1528
|
+
others
|
|
1529
|
+
} : void 0;
|
|
1530
|
+
}
|
|
1531
|
+
__name(consoleSwitcherConfig, "consoleSwitcherConfig");
|
|
1532
|
+
async function allows(entry, context, ms) {
|
|
1533
|
+
let timer;
|
|
1534
|
+
const timeout = new Promise((resolve2) => {
|
|
1535
|
+
timer = setTimeout(() => resolve2(false), ms);
|
|
1536
|
+
});
|
|
1537
|
+
try {
|
|
1538
|
+
return await Promise.race([
|
|
1539
|
+
Promise.resolve().then(() => entry.authorize(context)).then((decision) => decision?.allowed === true, () => false),
|
|
1540
|
+
timeout
|
|
1541
|
+
]);
|
|
1542
|
+
} finally {
|
|
1543
|
+
clearTimeout(timer);
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
__name(allows, "allows");
|
|
1547
|
+
async function listVisibleConsoles(registry, selfId, context, timeoutMs = CONSOLE_PROBE_TIMEOUT_MS) {
|
|
1548
|
+
const others = (registry?.entries ?? []).filter((entry) => entry.id !== selfId && isSameOriginPath(entry.path)).sort((a, b) => a.order - b.order || a.id.localeCompare(b.id));
|
|
1549
|
+
const verdicts = await Promise.all(others.map((entry) => allows(entry, context, timeoutMs)));
|
|
1550
|
+
return {
|
|
1551
|
+
version: 1,
|
|
1552
|
+
current: selfId,
|
|
1553
|
+
consoles: others.filter((_, index) => verdicts[index]).map((entry) => ({
|
|
1554
|
+
id: entry.id,
|
|
1555
|
+
title: entry.title,
|
|
1556
|
+
icon: ICONS.has(entry.icon) ? entry.icon : "generic",
|
|
1557
|
+
href: entry.path
|
|
1558
|
+
}))
|
|
1559
|
+
};
|
|
1560
|
+
}
|
|
1561
|
+
__name(listVisibleConsoles, "listVisibleConsoles");
|
|
1562
|
+
var AGENT_CONSOLE_ID = "agent";
|
|
1563
|
+
var logger2 = new Logger2("AgentDashboardModule");
|
|
1564
|
+
var AgentConsoleRegistration = class {
|
|
1565
|
+
static {
|
|
1566
|
+
__name(this, "AgentConsoleRegistration");
|
|
1567
|
+
}
|
|
1568
|
+
basePath;
|
|
1569
|
+
access;
|
|
1570
|
+
#unregister;
|
|
1571
|
+
constructor(basePath, access) {
|
|
1572
|
+
this.basePath = basePath;
|
|
1573
|
+
this.access = access;
|
|
1574
|
+
}
|
|
1575
|
+
onModuleInit() {
|
|
1576
|
+
this.#unregister = registerAviaryConsole({
|
|
1577
|
+
id: AGENT_CONSOLE_ID,
|
|
1578
|
+
title: "Agents",
|
|
1579
|
+
icon: "agent",
|
|
1580
|
+
order: 30,
|
|
1581
|
+
path: this.basePath === "" ? "/" : this.basePath,
|
|
1582
|
+
authorize: /* @__PURE__ */ __name((context) => this.access.check(context), "authorize")
|
|
1583
|
+
}, logger2);
|
|
1584
|
+
}
|
|
1585
|
+
onModuleDestroy() {
|
|
1586
|
+
this.#unregister?.();
|
|
1587
|
+
this.#unregister = void 0;
|
|
1588
|
+
}
|
|
1589
|
+
};
|
|
1590
|
+
AgentConsoleRegistration = _ts_decorate4([
|
|
1591
|
+
Injectable3(),
|
|
1592
|
+
_ts_param4(0, Inject4(DASHBOARD_BASE_PATH)),
|
|
1593
|
+
_ts_metadata4("design:type", Function),
|
|
1594
|
+
_ts_metadata4("design:paramtypes", [
|
|
1595
|
+
String,
|
|
1596
|
+
typeof DashboardConsoleAccess === "undefined" ? Object : DashboardConsoleAccess
|
|
1597
|
+
])
|
|
1598
|
+
], AgentConsoleRegistration);
|
|
1599
|
+
var CurrentExecutionContext = createParamDecorator((_data, context) => ({
|
|
1600
|
+
context
|
|
1601
|
+
}));
|
|
1602
|
+
var AgentConsolesController = class {
|
|
1603
|
+
static {
|
|
1604
|
+
__name(this, "AgentConsolesController");
|
|
1605
|
+
}
|
|
1606
|
+
consoles(current) {
|
|
1607
|
+
return listVisibleConsoles(readAviaryConsoles(), AGENT_CONSOLE_ID, current.context);
|
|
1608
|
+
}
|
|
1609
|
+
};
|
|
1610
|
+
_ts_decorate4([
|
|
1611
|
+
Get2("consoles"),
|
|
1612
|
+
Header("Cache-Control", "private, no-store"),
|
|
1613
|
+
_ts_param4(0, CurrentExecutionContext()),
|
|
1614
|
+
_ts_metadata4("design:type", Function),
|
|
1615
|
+
_ts_metadata4("design:paramtypes", [
|
|
1616
|
+
typeof CurrentContext === "undefined" ? Object : CurrentContext
|
|
1617
|
+
]),
|
|
1618
|
+
_ts_metadata4("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
1619
|
+
], AgentConsolesController.prototype, "consoles", null);
|
|
1620
|
+
AgentConsolesController = _ts_decorate4([
|
|
1621
|
+
CanAnyone2(),
|
|
1622
|
+
Controller2()
|
|
1623
|
+
], AgentConsolesController);
|
|
1624
|
+
|
|
1625
|
+
// src/server/agent-ui.controller.ts
|
|
1626
|
+
function _ts_decorate5(decorators, target, key, desc) {
|
|
1627
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1628
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
1629
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
1630
|
+
} else {
|
|
1631
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
1632
|
+
if (d = decorators[i]) {
|
|
1633
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1638
|
+
}
|
|
1639
|
+
__name(_ts_decorate5, "_ts_decorate");
|
|
1640
|
+
function _ts_metadata5(metadataKey, metadataValue) {
|
|
1641
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
|
|
1642
|
+
return Reflect.metadata(metadataKey, metadataValue);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
__name(_ts_metadata5, "_ts_metadata");
|
|
1646
|
+
function _ts_param5(paramIndex, decorator) {
|
|
1647
|
+
return function(target, key) {
|
|
1648
|
+
decorator(target, key, paramIndex);
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
__name(_ts_param5, "_ts_param");
|
|
1417
1652
|
var BUILD_BASE = "/ai-gateway";
|
|
1653
|
+
var AGENT_DASHBOARD_CONFIG_ID = "agent-dashboard-config";
|
|
1418
1654
|
function spaDir() {
|
|
1419
1655
|
return fileURLToPath(new URL("../spa", import.meta.url));
|
|
1420
1656
|
}
|
|
@@ -1452,9 +1688,23 @@ var AgentUiController = class {
|
|
|
1452
1688
|
throw new NotFoundException2("Dashboard is not built. Run the package build.");
|
|
1453
1689
|
}
|
|
1454
1690
|
const html = readFileSync(indexPath, "utf8").replaceAll(`="${BUILD_BASE}/`, `="${this.basePath}/`);
|
|
1455
|
-
const inject = `<script>window.__AGENT_BASE__='${this.basePath}';window.__AGENT_API__='${this.apiBasePath}';</script
|
|
1691
|
+
const inject = `<script>window.__AGENT_BASE__='${this.basePath}';window.__AGENT_API__='${this.apiBasePath}';</script>${this.configBlock()}`;
|
|
1456
1692
|
return html.includes("</head>") ? html.replace("</head>", `${inject}</head>`) : inject + html;
|
|
1457
1693
|
}
|
|
1694
|
+
/**
|
|
1695
|
+
* The page's JSON config block (`agent-dashboard-config`, CSP-safe: data, not script). It carries
|
|
1696
|
+
* `switcher` only when another Aviary console is registered.
|
|
1697
|
+
*/
|
|
1698
|
+
configBlock() {
|
|
1699
|
+
const switcher = consoleSwitcherConfig(AGENT_CONSOLE_ID, `${this.apiBasePath}/consoles`);
|
|
1700
|
+
const json = JSON.stringify({
|
|
1701
|
+
apiBase: this.apiBasePath,
|
|
1702
|
+
...switcher !== void 0 ? {
|
|
1703
|
+
switcher
|
|
1704
|
+
} : {}
|
|
1705
|
+
}).replaceAll("<", "\\u003c");
|
|
1706
|
+
return `<script type="application/json" id="${AGENT_DASHBOARD_CONFIG_ID}">${json}</script>`;
|
|
1707
|
+
}
|
|
1458
1708
|
asset(file) {
|
|
1459
1709
|
const safe = basename(file);
|
|
1460
1710
|
if (safe !== file) throw new NotFoundException2();
|
|
@@ -1469,46 +1719,452 @@ var AgentUiController = class {
|
|
|
1469
1719
|
});
|
|
1470
1720
|
}
|
|
1471
1721
|
};
|
|
1472
|
-
|
|
1473
|
-
|
|
1722
|
+
_ts_decorate5([
|
|
1723
|
+
Get3(),
|
|
1474
1724
|
ConsolePage(),
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1725
|
+
Header2("Content-Type", "text/html; charset=utf-8"),
|
|
1726
|
+
Header2("Cache-Control", "no-store, must-revalidate"),
|
|
1727
|
+
_ts_param5(0, Req2()),
|
|
1728
|
+
_ts_param5(1, Res({
|
|
1479
1729
|
passthrough: true
|
|
1480
1730
|
})),
|
|
1481
|
-
|
|
1482
|
-
|
|
1731
|
+
_ts_metadata5("design:type", Function),
|
|
1732
|
+
_ts_metadata5("design:paramtypes", [
|
|
1483
1733
|
typeof UiPageRequest === "undefined" ? Object : UiPageRequest,
|
|
1484
1734
|
typeof UiPageResponse === "undefined" ? Object : UiPageResponse
|
|
1485
1735
|
]),
|
|
1486
|
-
|
|
1736
|
+
_ts_metadata5("design:returntype", String)
|
|
1487
1737
|
], AgentUiController.prototype, "index", null);
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1738
|
+
_ts_decorate5([
|
|
1739
|
+
Get3("assets/:file"),
|
|
1740
|
+
Header2("Cache-Control", "public, max-age=31536000, immutable"),
|
|
1741
|
+
_ts_param5(0, Param2("file")),
|
|
1742
|
+
_ts_metadata5("design:type", Function),
|
|
1743
|
+
_ts_metadata5("design:paramtypes", [
|
|
1494
1744
|
String
|
|
1495
1745
|
]),
|
|
1496
|
-
|
|
1746
|
+
_ts_metadata5("design:returntype", typeof StreamableFile === "undefined" ? Object : StreamableFile)
|
|
1497
1747
|
], AgentUiController.prototype, "asset", null);
|
|
1498
|
-
AgentUiController =
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1748
|
+
AgentUiController = _ts_decorate5([
|
|
1749
|
+
CanAnyone3(),
|
|
1750
|
+
Controller3(),
|
|
1751
|
+
_ts_param5(0, Inject5(DASHBOARD_BASE_PATH)),
|
|
1752
|
+
_ts_param5(1, Inject5(DASHBOARD_API_PATH)),
|
|
1753
|
+
_ts_metadata5("design:type", Function),
|
|
1754
|
+
_ts_metadata5("design:paramtypes", [
|
|
1505
1755
|
String,
|
|
1506
1756
|
String
|
|
1507
1757
|
])
|
|
1508
1758
|
], AgentUiController);
|
|
1509
1759
|
|
|
1760
|
+
// src/server/dev-tools.ts
|
|
1761
|
+
import { Inject as Inject6, Injectable as Injectable4, Logger as Logger3, NotFoundException as NotFoundException3 } from "@nestjs/common";
|
|
1762
|
+
function _ts_decorate6(decorators, target, key, desc) {
|
|
1763
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1764
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
1765
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
1766
|
+
} else {
|
|
1767
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
1768
|
+
if (d = decorators[i]) {
|
|
1769
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1774
|
+
}
|
|
1775
|
+
__name(_ts_decorate6, "_ts_decorate");
|
|
1776
|
+
function _ts_metadata6(metadataKey, metadataValue) {
|
|
1777
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
|
|
1778
|
+
return Reflect.metadata(metadataKey, metadataValue);
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
__name(_ts_metadata6, "_ts_metadata");
|
|
1782
|
+
function _ts_param6(paramIndex, decorator) {
|
|
1783
|
+
return function(target, key) {
|
|
1784
|
+
decorator(target, key, paramIndex);
|
|
1785
|
+
};
|
|
1786
|
+
}
|
|
1787
|
+
__name(_ts_param6, "_ts_param");
|
|
1788
|
+
var DEV_TOOLS_SLOT = /* @__PURE__ */ Symbol.for("@dudousxd/aviary:dev-tools");
|
|
1789
|
+
function devToolsSlot() {
|
|
1790
|
+
const holder = globalThis;
|
|
1791
|
+
const existing = holder[DEV_TOOLS_SLOT];
|
|
1792
|
+
if (existing !== void 0) return existing;
|
|
1793
|
+
const libs = [];
|
|
1794
|
+
const slot = {
|
|
1795
|
+
version: 1,
|
|
1796
|
+
libs,
|
|
1797
|
+
register(lib) {
|
|
1798
|
+
if (libs.some((entry) => entry.lib === lib.lib)) {
|
|
1799
|
+
throw new Error(`dev tools: the lib "${lib.lib}" is already registered`);
|
|
1800
|
+
}
|
|
1801
|
+
libs.push(lib);
|
|
1802
|
+
return () => {
|
|
1803
|
+
const index = libs.indexOf(lib);
|
|
1804
|
+
if (index !== -1) libs.splice(index, 1);
|
|
1805
|
+
};
|
|
1806
|
+
}
|
|
1807
|
+
};
|
|
1808
|
+
holder[DEV_TOOLS_SLOT] = slot;
|
|
1809
|
+
return slot;
|
|
1810
|
+
}
|
|
1811
|
+
__name(devToolsSlot, "devToolsSlot");
|
|
1812
|
+
var DEFAULT_LIMIT = 20;
|
|
1813
|
+
var MAX_LIMIT = 100;
|
|
1814
|
+
var MESSAGE_LIMIT = 50;
|
|
1815
|
+
var MESSAGE_BYTES = 2 * 1024;
|
|
1816
|
+
var DAY_MS2 = 864e5;
|
|
1817
|
+
var ISO_DAY3 = /^\d{4}-\d{2}-\d{2}$/;
|
|
1818
|
+
var day = {
|
|
1819
|
+
type: "string",
|
|
1820
|
+
description: "UTC day, YYYY-MM-DD."
|
|
1821
|
+
};
|
|
1822
|
+
var page = {
|
|
1823
|
+
type: "integer",
|
|
1824
|
+
description: "Page, from 1. Pass the `next` of the previous page.",
|
|
1825
|
+
minimum: 1
|
|
1826
|
+
};
|
|
1827
|
+
var limit = {
|
|
1828
|
+
type: "integer",
|
|
1829
|
+
description: `Rows per page (default ${DEFAULT_LIMIT}, max ${MAX_LIMIT}).`,
|
|
1830
|
+
minimum: 1,
|
|
1831
|
+
maximum: MAX_LIMIT
|
|
1832
|
+
};
|
|
1833
|
+
var str = /* @__PURE__ */ __name((value) => typeof value === "string" && value !== "" ? value : void 0, "str");
|
|
1834
|
+
function dayOf(input, key) {
|
|
1835
|
+
const value = str(input[key]);
|
|
1836
|
+
if (value !== void 0 && !ISO_DAY3.test(value)) throw new Error(`"${key}" must be YYYY-MM-DD`);
|
|
1837
|
+
return value;
|
|
1838
|
+
}
|
|
1839
|
+
__name(dayOf, "dayOf");
|
|
1840
|
+
function rangeOf(input) {
|
|
1841
|
+
const utcDay2 = /* @__PURE__ */ __name((daysAgo) => new Date(Date.now() - daysAgo * DAY_MS2).toISOString().slice(0, 10), "utcDay");
|
|
1842
|
+
return {
|
|
1843
|
+
fromDay: dayOf(input, "from") ?? utcDay2(29),
|
|
1844
|
+
toDay: dayOf(input, "to") ?? utcDay2(0)
|
|
1845
|
+
};
|
|
1846
|
+
}
|
|
1847
|
+
__name(rangeOf, "rangeOf");
|
|
1848
|
+
function pageOf(input) {
|
|
1849
|
+
return {
|
|
1850
|
+
page: typeof input.page === "number" ? input.page : 1,
|
|
1851
|
+
pageSize: typeof input.limit === "number" ? input.limit : DEFAULT_LIMIT
|
|
1852
|
+
};
|
|
1853
|
+
}
|
|
1854
|
+
__name(pageOf, "pageOf");
|
|
1855
|
+
function where(fields) {
|
|
1856
|
+
const set = Object.fromEntries(Object.entries(fields).filter(([, v]) => v !== void 0));
|
|
1857
|
+
return Object.keys(set).length > 0 ? {
|
|
1858
|
+
where: set
|
|
1859
|
+
} : {};
|
|
1860
|
+
}
|
|
1861
|
+
__name(where, "where");
|
|
1862
|
+
function listOf(result) {
|
|
1863
|
+
return {
|
|
1864
|
+
items: result.rows,
|
|
1865
|
+
next: result.page * result.pageSize < result.total ? result.page + 1 : null,
|
|
1866
|
+
total: result.total
|
|
1867
|
+
};
|
|
1868
|
+
}
|
|
1869
|
+
__name(listOf, "listOf");
|
|
1870
|
+
function cut(text, maxBytes) {
|
|
1871
|
+
const size = Buffer.byteLength(text, "utf8");
|
|
1872
|
+
if (size <= maxBytes) return text;
|
|
1873
|
+
const suffix = `\u2026(truncated, ${size} bytes)`;
|
|
1874
|
+
const keep = Math.max(0, maxBytes - Buffer.byteLength(suffix, "utf8"));
|
|
1875
|
+
let head = Buffer.from(text, "utf8").subarray(0, keep).toString("utf8");
|
|
1876
|
+
if (head.endsWith("\uFFFD")) head = head.slice(0, -1);
|
|
1877
|
+
return `${head}${suffix}`;
|
|
1878
|
+
}
|
|
1879
|
+
__name(cut, "cut");
|
|
1880
|
+
function agentDevTools(dashboard) {
|
|
1881
|
+
return [
|
|
1882
|
+
{
|
|
1883
|
+
name: "agent_list_runs",
|
|
1884
|
+
kind: "read",
|
|
1885
|
+
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.",
|
|
1886
|
+
input: {
|
|
1887
|
+
type: "object",
|
|
1888
|
+
properties: {
|
|
1889
|
+
agent: {
|
|
1890
|
+
type: "string",
|
|
1891
|
+
description: "Agent name."
|
|
1892
|
+
},
|
|
1893
|
+
status: {
|
|
1894
|
+
type: "string",
|
|
1895
|
+
enum: [
|
|
1896
|
+
"running",
|
|
1897
|
+
"completed",
|
|
1898
|
+
"failed",
|
|
1899
|
+
"cancelled"
|
|
1900
|
+
]
|
|
1901
|
+
},
|
|
1902
|
+
errorCode: {
|
|
1903
|
+
type: "string"
|
|
1904
|
+
},
|
|
1905
|
+
threadId: {
|
|
1906
|
+
type: "string"
|
|
1907
|
+
},
|
|
1908
|
+
from: day,
|
|
1909
|
+
to: day,
|
|
1910
|
+
page,
|
|
1911
|
+
limit
|
|
1912
|
+
}
|
|
1913
|
+
},
|
|
1914
|
+
async run(input) {
|
|
1915
|
+
return listOf(await dashboard.runsPage({
|
|
1916
|
+
...pageOf(input),
|
|
1917
|
+
...where({
|
|
1918
|
+
agentName: str(input.agent),
|
|
1919
|
+
status: str(input.status),
|
|
1920
|
+
errorCode: str(input.errorCode),
|
|
1921
|
+
threadId: str(input.threadId),
|
|
1922
|
+
fromDay: dayOf(input, "from"),
|
|
1923
|
+
toDay: dayOf(input, "to")
|
|
1924
|
+
})
|
|
1925
|
+
}));
|
|
1926
|
+
}
|
|
1927
|
+
},
|
|
1928
|
+
{
|
|
1929
|
+
name: "agent_get_run",
|
|
1930
|
+
kind: "read",
|
|
1931
|
+
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.",
|
|
1932
|
+
input: {
|
|
1933
|
+
type: "object",
|
|
1934
|
+
properties: {
|
|
1935
|
+
id: {
|
|
1936
|
+
type: "string",
|
|
1937
|
+
description: "Run id."
|
|
1938
|
+
},
|
|
1939
|
+
includeMessages: {
|
|
1940
|
+
type: "boolean",
|
|
1941
|
+
description: "Include messages (default false)."
|
|
1942
|
+
}
|
|
1943
|
+
},
|
|
1944
|
+
required: [
|
|
1945
|
+
"id"
|
|
1946
|
+
]
|
|
1947
|
+
},
|
|
1948
|
+
async run(input) {
|
|
1949
|
+
let detail;
|
|
1950
|
+
try {
|
|
1951
|
+
detail = await dashboard.runDetail(String(input.id));
|
|
1952
|
+
} catch (error) {
|
|
1953
|
+
if (error instanceof NotFoundException3) throw new Error(`run ${String(input.id)} not found`);
|
|
1954
|
+
throw error;
|
|
1955
|
+
}
|
|
1956
|
+
if (input.includeMessages !== true) return detail;
|
|
1957
|
+
const thread = await dashboard.threadDetail({
|
|
1958
|
+
threadId: detail.run.threadId,
|
|
1959
|
+
messageLimit: MESSAGE_LIMIT,
|
|
1960
|
+
runLimit: 1
|
|
1961
|
+
});
|
|
1962
|
+
return {
|
|
1963
|
+
...detail,
|
|
1964
|
+
messages: thread.messages.map((message) => ({
|
|
1965
|
+
...message,
|
|
1966
|
+
content: cut(message.content, MESSAGE_BYTES)
|
|
1967
|
+
}))
|
|
1968
|
+
};
|
|
1969
|
+
}
|
|
1970
|
+
},
|
|
1971
|
+
{
|
|
1972
|
+
name: "agent_get_spend",
|
|
1973
|
+
kind: "read",
|
|
1974
|
+
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.",
|
|
1975
|
+
input: {
|
|
1976
|
+
type: "object",
|
|
1977
|
+
properties: {
|
|
1978
|
+
by: {
|
|
1979
|
+
type: "string",
|
|
1980
|
+
enum: [
|
|
1981
|
+
"model",
|
|
1982
|
+
"actor",
|
|
1983
|
+
"feature"
|
|
1984
|
+
]
|
|
1985
|
+
},
|
|
1986
|
+
from: day,
|
|
1987
|
+
to: day
|
|
1988
|
+
}
|
|
1989
|
+
},
|
|
1990
|
+
async run(input) {
|
|
1991
|
+
const spend = await dashboard.spend(rangeOf(input));
|
|
1992
|
+
switch (input.by) {
|
|
1993
|
+
case "model":
|
|
1994
|
+
return {
|
|
1995
|
+
items: spend.byModel
|
|
1996
|
+
};
|
|
1997
|
+
case "actor":
|
|
1998
|
+
return {
|
|
1999
|
+
items: spend.byActor
|
|
2000
|
+
};
|
|
2001
|
+
case "feature":
|
|
2002
|
+
return {
|
|
2003
|
+
items: spend.byFeature ?? []
|
|
2004
|
+
};
|
|
2005
|
+
default:
|
|
2006
|
+
return spend;
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
},
|
|
2010
|
+
{
|
|
2011
|
+
name: "agent_get_tool_stats",
|
|
2012
|
+
kind: "read",
|
|
2013
|
+
description: "Per tool: calls, failures, rejections and latency, over UTC days from/to (default the last 30 days).",
|
|
2014
|
+
input: {
|
|
2015
|
+
type: "object",
|
|
2016
|
+
properties: {
|
|
2017
|
+
from: day,
|
|
2018
|
+
to: day
|
|
2019
|
+
}
|
|
2020
|
+
},
|
|
2021
|
+
async run(input) {
|
|
2022
|
+
return {
|
|
2023
|
+
items: await dashboard.toolStats(rangeOf(input))
|
|
2024
|
+
};
|
|
2025
|
+
}
|
|
2026
|
+
},
|
|
2027
|
+
{
|
|
2028
|
+
name: "agent_get_reliability",
|
|
2029
|
+
kind: "read",
|
|
2030
|
+
description: "Run metrics, by-agent and by-error breakdowns and the trend, over UTC days from/to (default the last 30 days).",
|
|
2031
|
+
input: {
|
|
2032
|
+
type: "object",
|
|
2033
|
+
properties: {
|
|
2034
|
+
from: day,
|
|
2035
|
+
to: day
|
|
2036
|
+
}
|
|
2037
|
+
},
|
|
2038
|
+
async run(input) {
|
|
2039
|
+
return dashboard.reliability(rangeOf(input));
|
|
2040
|
+
}
|
|
2041
|
+
},
|
|
2042
|
+
{
|
|
2043
|
+
name: "agent_list_pending_approvals",
|
|
2044
|
+
kind: "read",
|
|
2045
|
+
description: "Tool calls waiting for a human approval, oldest first. Read-only: approve them in the app. Returns { items, next, total }.",
|
|
2046
|
+
input: {
|
|
2047
|
+
type: "object",
|
|
2048
|
+
properties: {
|
|
2049
|
+
toolName: {
|
|
2050
|
+
type: "string"
|
|
2051
|
+
},
|
|
2052
|
+
agent: {
|
|
2053
|
+
type: "string",
|
|
2054
|
+
description: "Agent name."
|
|
2055
|
+
},
|
|
2056
|
+
actor: {
|
|
2057
|
+
type: "string",
|
|
2058
|
+
description: "Actor ref of the thread owner."
|
|
2059
|
+
},
|
|
2060
|
+
threadId: {
|
|
2061
|
+
type: "string"
|
|
2062
|
+
},
|
|
2063
|
+
from: day,
|
|
2064
|
+
to: day,
|
|
2065
|
+
page,
|
|
2066
|
+
limit
|
|
2067
|
+
}
|
|
2068
|
+
},
|
|
2069
|
+
async run(input) {
|
|
2070
|
+
return listOf(await dashboard.approvalsPage({
|
|
2071
|
+
...pageOf(input),
|
|
2072
|
+
...where({
|
|
2073
|
+
toolName: str(input.toolName),
|
|
2074
|
+
agentName: str(input.agent),
|
|
2075
|
+
actorRef: str(input.actor),
|
|
2076
|
+
threadId: str(input.threadId),
|
|
2077
|
+
fromDay: dayOf(input, "from"),
|
|
2078
|
+
toDay: dayOf(input, "to")
|
|
2079
|
+
})
|
|
2080
|
+
}));
|
|
2081
|
+
}
|
|
2082
|
+
},
|
|
2083
|
+
{
|
|
2084
|
+
name: "agent_list_recent_tool_calls",
|
|
2085
|
+
kind: "read",
|
|
2086
|
+
description: "Tool calls of every agent, newest first. Filters: toolName, status, threadId, from/to (UTC days). Returns { items, next, total }.",
|
|
2087
|
+
input: {
|
|
2088
|
+
type: "object",
|
|
2089
|
+
properties: {
|
|
2090
|
+
toolName: {
|
|
2091
|
+
type: "string"
|
|
2092
|
+
},
|
|
2093
|
+
status: {
|
|
2094
|
+
type: "string"
|
|
2095
|
+
},
|
|
2096
|
+
threadId: {
|
|
2097
|
+
type: "string"
|
|
2098
|
+
},
|
|
2099
|
+
from: day,
|
|
2100
|
+
to: day,
|
|
2101
|
+
page,
|
|
2102
|
+
limit
|
|
2103
|
+
}
|
|
2104
|
+
},
|
|
2105
|
+
async run(input) {
|
|
2106
|
+
return listOf(await dashboard.toolCallsPage({
|
|
2107
|
+
...pageOf(input),
|
|
2108
|
+
...where({
|
|
2109
|
+
toolName: str(input.toolName),
|
|
2110
|
+
status: str(input.status),
|
|
2111
|
+
threadId: str(input.threadId),
|
|
2112
|
+
fromDay: dayOf(input, "from"),
|
|
2113
|
+
toDay: dayOf(input, "to")
|
|
2114
|
+
})
|
|
2115
|
+
}));
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
];
|
|
2119
|
+
}
|
|
2120
|
+
__name(agentDevTools, "agentDevTools");
|
|
2121
|
+
var logger3 = new Logger3("AgentDashboardModule");
|
|
2122
|
+
var AgentDevToolsRegistration = class {
|
|
2123
|
+
static {
|
|
2124
|
+
__name(this, "AgentDevToolsRegistration");
|
|
2125
|
+
}
|
|
2126
|
+
enabled;
|
|
2127
|
+
dashboard;
|
|
2128
|
+
access;
|
|
2129
|
+
#unregister;
|
|
2130
|
+
constructor(enabled, dashboard, access) {
|
|
2131
|
+
this.enabled = enabled;
|
|
2132
|
+
this.dashboard = dashboard;
|
|
2133
|
+
this.access = access;
|
|
2134
|
+
}
|
|
2135
|
+
onModuleInit() {
|
|
2136
|
+
if (!this.enabled) return;
|
|
2137
|
+
const slot = devToolsSlot();
|
|
2138
|
+
if (slot.version !== 1 || typeof slot.register !== "function") return;
|
|
2139
|
+
try {
|
|
2140
|
+
this.#unregister = slot.register({
|
|
2141
|
+
lib: "agent",
|
|
2142
|
+
title: "Agent governance",
|
|
2143
|
+
authorize: /* @__PURE__ */ __name((context) => this.access.check(context), "authorize"),
|
|
2144
|
+
tools: agentDevTools(this.dashboard)
|
|
2145
|
+
});
|
|
2146
|
+
} catch (error) {
|
|
2147
|
+
logger3.warn(`agent dev tools not registered: ${error instanceof Error ? error.message : String(error)}`);
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
onModuleDestroy() {
|
|
2151
|
+
this.#unregister?.();
|
|
2152
|
+
this.#unregister = void 0;
|
|
2153
|
+
}
|
|
2154
|
+
};
|
|
2155
|
+
AgentDevToolsRegistration = _ts_decorate6([
|
|
2156
|
+
Injectable4(),
|
|
2157
|
+
_ts_param6(0, Inject6(DASHBOARD_DEV_TOOLS)),
|
|
2158
|
+
_ts_metadata6("design:type", Function),
|
|
2159
|
+
_ts_metadata6("design:paramtypes", [
|
|
2160
|
+
Boolean,
|
|
2161
|
+
typeof DashboardService === "undefined" ? Object : DashboardService,
|
|
2162
|
+
typeof DashboardConsoleAccess === "undefined" ? Object : DashboardConsoleAccess
|
|
2163
|
+
])
|
|
2164
|
+
], AgentDevToolsRegistration);
|
|
2165
|
+
|
|
1510
2166
|
// src/server/agent-dashboard.module.ts
|
|
1511
|
-
function
|
|
2167
|
+
function _ts_decorate7(decorators, target, key, desc) {
|
|
1512
2168
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1513
2169
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
1514
2170
|
r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -1521,7 +2177,7 @@ function _ts_decorate5(decorators, target, key, desc) {
|
|
|
1521
2177
|
}
|
|
1522
2178
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1523
2179
|
}
|
|
1524
|
-
__name(
|
|
2180
|
+
__name(_ts_decorate7, "_ts_decorate");
|
|
1525
2181
|
function normalize(path) {
|
|
1526
2182
|
return normalizeDashboardPath(path);
|
|
1527
2183
|
}
|
|
@@ -1550,7 +2206,8 @@ var AgentApiModule = class _AgentApiModule {
|
|
|
1550
2206
|
...options.imports ?? []
|
|
1551
2207
|
],
|
|
1552
2208
|
controllers: options.routes === false ? [] : withControllerDecorators([
|
|
1553
|
-
AgentApiController
|
|
2209
|
+
AgentApiController,
|
|
2210
|
+
AgentConsolesController
|
|
1554
2211
|
], consoleDecorators(options.decorators)),
|
|
1555
2212
|
providers: [
|
|
1556
2213
|
DashboardService,
|
|
@@ -1564,7 +2221,12 @@ var AgentApiModule = class _AgentApiModule {
|
|
|
1564
2221
|
provide: DASHBOARD_CONSOLE_OPTIONS,
|
|
1565
2222
|
useValue: consoleOptions
|
|
1566
2223
|
},
|
|
1567
|
-
DashboardConsoleAccess
|
|
2224
|
+
DashboardConsoleAccess,
|
|
2225
|
+
{
|
|
2226
|
+
provide: DASHBOARD_DEV_TOOLS,
|
|
2227
|
+
useValue: options.devTools ?? options.routes !== false
|
|
2228
|
+
},
|
|
2229
|
+
AgentDevToolsRegistration
|
|
1568
2230
|
],
|
|
1569
2231
|
// What a self-mounted `AgentApiController` subclass injects, too (`routes: false`), and the
|
|
1570
2232
|
// access check `UseConsoleAuthorize()` runs.
|
|
@@ -1576,7 +2238,7 @@ var AgentApiModule = class _AgentApiModule {
|
|
|
1576
2238
|
};
|
|
1577
2239
|
}
|
|
1578
2240
|
};
|
|
1579
|
-
AgentApiModule =
|
|
2241
|
+
AgentApiModule = _ts_decorate7([
|
|
1580
2242
|
Module({})
|
|
1581
2243
|
], AgentApiModule);
|
|
1582
2244
|
var AgentDashboardModule = class _AgentDashboardModule {
|
|
@@ -1606,6 +2268,9 @@ var AgentDashboardModule = class _AgentDashboardModule {
|
|
|
1606
2268
|
} : {},
|
|
1607
2269
|
...options.authorize !== void 0 ? {
|
|
1608
2270
|
authorize: options.authorize
|
|
2271
|
+
} : {},
|
|
2272
|
+
...options.devTools !== void 0 ? {
|
|
2273
|
+
devTools: options.devTools
|
|
1609
2274
|
} : {}
|
|
1610
2275
|
}),
|
|
1611
2276
|
...options.routes === false ? [] : [
|
|
@@ -1632,7 +2297,11 @@ var AgentDashboardModule = class _AgentDashboardModule {
|
|
|
1632
2297
|
{
|
|
1633
2298
|
provide: DASHBOARD_API_PATH,
|
|
1634
2299
|
useValue: apiBasePath
|
|
1635
|
-
}
|
|
2300
|
+
},
|
|
2301
|
+
// The console switcher lists this console only where it serves its own page.
|
|
2302
|
+
...options.routes === false ? [] : [
|
|
2303
|
+
AgentConsoleRegistration
|
|
2304
|
+
]
|
|
1636
2305
|
],
|
|
1637
2306
|
// Re-export the API module so its DashboardService reaches importers (e.g. the host's own
|
|
1638
2307
|
// controllers), and the paths a self-mounted `AgentUiController` subclass injects.
|
|
@@ -1644,22 +2313,29 @@ var AgentDashboardModule = class _AgentDashboardModule {
|
|
|
1644
2313
|
};
|
|
1645
2314
|
}
|
|
1646
2315
|
};
|
|
1647
|
-
AgentDashboardModule =
|
|
2316
|
+
AgentDashboardModule = _ts_decorate7([
|
|
1648
2317
|
Module({})
|
|
1649
2318
|
], AgentDashboardModule);
|
|
1650
2319
|
export {
|
|
1651
2320
|
AGENT_ACTOR_DIRECTORY,
|
|
1652
2321
|
AGENT_ACTOR_RESOLVER,
|
|
1653
2322
|
AGENT_APPROVAL_PORT,
|
|
2323
|
+
AGENT_CONSOLE_ID,
|
|
2324
|
+
AGENT_DASHBOARD_CONFIG_ID,
|
|
1654
2325
|
AGENT_GOVERNANCE_QUERIES,
|
|
1655
2326
|
AGENT_PRICING_STORE,
|
|
2327
|
+
AVIARY_CONSOLES_SLOT,
|
|
1656
2328
|
AgentApiController,
|
|
1657
2329
|
AgentApiModule,
|
|
2330
|
+
AgentConsoleRegistration,
|
|
2331
|
+
AgentConsolesController,
|
|
1658
2332
|
AgentDashboardModule,
|
|
2333
|
+
AgentDevToolsRegistration,
|
|
1659
2334
|
AgentUiController,
|
|
1660
2335
|
CONSOLE_AUTHORIZE_MISSING_WARNING,
|
|
1661
2336
|
CONSOLE_GATE,
|
|
1662
2337
|
CONSOLE_PAGE_METADATA,
|
|
2338
|
+
CONSOLE_PROBE_TIMEOUT_MS,
|
|
1663
2339
|
ConsoleAccessDenied,
|
|
1664
2340
|
ConsoleAuthorizeFilter,
|
|
1665
2341
|
ConsoleAuthorizeGuard,
|
|
@@ -1668,13 +2344,18 @@ export {
|
|
|
1668
2344
|
DASHBOARD_APPROVAL_ACTOR_REF,
|
|
1669
2345
|
DASHBOARD_BASE_PATH,
|
|
1670
2346
|
DASHBOARD_CONSOLE_OPTIONS,
|
|
2347
|
+
DASHBOARD_DEV_TOOLS,
|
|
1671
2348
|
DashboardConsoleAccess,
|
|
1672
2349
|
DashboardService,
|
|
1673
2350
|
UseConsoleAuthorize,
|
|
1674
2351
|
agentDashboardMountPaths,
|
|
2352
|
+
agentDevTools,
|
|
2353
|
+
anyAuthorize,
|
|
1675
2354
|
authorizeByToken,
|
|
2355
|
+
listVisibleConsoles,
|
|
1676
2356
|
normalizeConsoleResult,
|
|
1677
2357
|
parsePriceInput,
|
|
2358
|
+
registerAviaryConsole,
|
|
1678
2359
|
resolveConsoleAuthorize
|
|
1679
2360
|
};
|
|
1680
2361
|
//# sourceMappingURL=index.js.map
|