@dudousxd/nestjs-agent-dashboard 0.9.1 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -5
- package/dist/server/index.cjs +193 -78
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +96 -37
- package/dist/server/index.d.ts +96 -37
- package/dist/server/index.js +194 -79
- package/dist/server/index.js.map +1 -1
- package/package.json +2 -2
package/dist/server/index.d.cts
CHANGED
|
@@ -194,12 +194,36 @@ interface DashboardSessionUser {
|
|
|
194
194
|
roles?: string[];
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
-
/**
|
|
197
|
+
/**
|
|
198
|
+
* Host hook for Mode A: validates the host's own auth on the raw request POSTed to
|
|
199
|
+
* `<basePath>/auth/session`. Return the session user to grant access, or `null` to deny.
|
|
200
|
+
*/
|
|
201
|
+
type SessionHook = (request: unknown) => Promise<DashboardSessionUser | null> | DashboardSessionUser | null;
|
|
202
|
+
/** Host hook for Mode B: validates submitted credentials for the built-in login screen. */
|
|
198
203
|
type LoginHook = (username: string, password: string) => Promise<DashboardSessionUser | null> | DashboardSessionUser | null;
|
|
204
|
+
/**
|
|
205
|
+
* Re-checks a LIVE session when the cookie is slid forward. Runs at most once per `ttl/2` PER
|
|
206
|
+
* COOKIE GENERATION — the gate is on the inbound cookie's `iat`, so every request still carrying a
|
|
207
|
+
* not-yet-renewed cookie would, on its own, invoke this hook again; `maybeRenewSession` de-dupes
|
|
208
|
+
* concurrent calls for the same session (`sub` + `iat`) into a single in-flight call, so a page
|
|
209
|
+
* load that fires N parallel API calls still costs exactly ONE host round-trip, not N. Still not a
|
|
210
|
+
* per-session-lifetime cap — this can run again every `ttl/2` as the session keeps sliding, so a
|
|
211
|
+
* slow or expensive check should still debounce/cache across renewal windows on its own. Return
|
|
212
|
+
* `false` to revoke (the cookie is cleared and the request denied). Distinct from `session`: that
|
|
213
|
+
* hook reads the host's auth off a fresh request, which a console XHR does not carry — this one
|
|
214
|
+
* receives the already-minted session.
|
|
215
|
+
*/
|
|
216
|
+
type RevalidateHook = (session: DashboardSessionUser) => Promise<boolean> | boolean;
|
|
217
|
+
/** Which `dashboardAuth` hook(s) a resolved config was given — see `resolveDashboardAuth`. */
|
|
218
|
+
type AuthMode = 'session' | 'login';
|
|
199
219
|
/**
|
|
200
220
|
* Author-facing `dashboardAuth` option (see `AgentDashboardOptions.dashboardAuth`). Gates the
|
|
201
|
-
* console (SPA + API) behind a
|
|
202
|
-
*
|
|
221
|
+
* console (SPA + API) behind a signed session cookie. Two ways to mint that cookie: Mode A
|
|
222
|
+
* (`session`) — the host frontend, already carrying its own auth, POSTs to
|
|
223
|
+
* `<basePath>/auth/session` and the host hook decides — or Mode B (`login`) — the built-in,
|
|
224
|
+
* dependency-free server-rendered login screen. Mirrors `@dudousxd/nestjs-telescope`'s
|
|
225
|
+
* `dashboardAuth` two-mode shape. At least one of the two is required so an un-mintable gate is a
|
|
226
|
+
* boot error, not a silently-open (or silently-stuck) console.
|
|
203
227
|
*/
|
|
204
228
|
interface DashboardAuthOptions {
|
|
205
229
|
/** REQUIRED HMAC-SHA256 signing key (32+ bytes recommended). Missing/empty => boot error (fail closed). */
|
|
@@ -207,23 +231,36 @@ interface DashboardAuthOptions {
|
|
|
207
231
|
/** Cookie TTL (duration string `'<n><ms|s|m|h|d>'`, e.g. `'30m'`, `'7d'`). Default `'8h'`. */
|
|
208
232
|
ttl?: string;
|
|
209
233
|
/**
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
234
|
+
* Mode A: validates the host's own auth on the raw request POSTed to `<basePath>/auth/session`.
|
|
235
|
+
* Return the session user to grant access, or `null` to deny. No credential reaches this
|
|
236
|
+
* library — the host decides from whatever it already has (a header, an SSO cookie, ...).
|
|
213
237
|
*/
|
|
214
|
-
|
|
238
|
+
session?: SessionHook;
|
|
239
|
+
/**
|
|
240
|
+
* Mode B: validates submitted username/password. Return the session user to grant access, or
|
|
241
|
+
* `null` to deny — the login endpoint responds identically (generic failure) for an unknown user
|
|
242
|
+
* and a wrong password, so it never reveals which one was wrong.
|
|
243
|
+
*/
|
|
244
|
+
login?: LoginHook;
|
|
245
|
+
/** Re-checks a live session on sliding renewal; see `RevalidateHook`. Not a mode — it cannot
|
|
246
|
+
* mint a session, only revoke one already minted by `session`/`login`. */
|
|
247
|
+
revalidate?: RevalidateHook;
|
|
215
248
|
}
|
|
216
249
|
/** Resolved, validated `dashboardAuth` config used by the guards/controller. */
|
|
217
250
|
interface ResolvedDashboardAuth {
|
|
218
251
|
secret: string;
|
|
219
252
|
ttlMs: number;
|
|
220
|
-
|
|
253
|
+
modes: AuthMode[];
|
|
254
|
+
session?: SessionHook;
|
|
255
|
+
login?: LoginHook;
|
|
256
|
+
revalidate?: RevalidateHook;
|
|
221
257
|
}
|
|
222
258
|
/**
|
|
223
259
|
* Validate + resolve `dashboardAuth`. Returns `null` when unconfigured (behavior unchanged — the
|
|
224
260
|
* console stays open, front it with your own `guards`). Throws at boot (fail closed) when
|
|
225
|
-
* configured but missing a secret
|
|
226
|
-
* shipping an open or
|
|
261
|
+
* configured but missing a secret, when neither `session` nor `login` is given, or when a given
|
|
262
|
+
* hook isn't a function — the host learns immediately rather than shipping an open, stuck, or
|
|
263
|
+
* un-mintable console.
|
|
227
264
|
*/
|
|
228
265
|
declare function resolveDashboardAuth(options: DashboardAuthOptions | undefined): ResolvedDashboardAuth | null;
|
|
229
266
|
|
|
@@ -248,32 +285,37 @@ interface LoginFormBody {
|
|
|
248
285
|
* built-in gates check for, so they can never be made to require the very auth they grant. Mirrors
|
|
249
286
|
* `@dudousxd/nestjs-telescope`'s own auth controller staying outside its `stampGuards` call.
|
|
250
287
|
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
288
|
+
* Two ways to mint that cookie, matching `resolveDashboardAuth`'s modes: Mode A (`session`) — the
|
|
289
|
+
* host frontend, already carrying its own auth, POSTs to `<basePath>/auth/session` and the host
|
|
290
|
+
* hook decides — or Mode B (`login`), a dependency-free, server-rendered login form (GET/POST) —
|
|
291
|
+
* the bundled React SPA in this package is a built Vite artifact with no auth-aware UI of its own,
|
|
292
|
+
* so gating the actual page navigation needs a self-contained flow that doesn't touch it. Each
|
|
293
|
+
* mode's routes 404 when that mode isn't listed in `auth.modes` — the single source of truth for
|
|
294
|
+
* which mode(s) are configured, not hook-presence truthiness — and every route 404s outright when
|
|
295
|
+
* `dashboardAuth` is unconfigured (`DASHBOARD_AUTH` resolves to `null`) — no dangling login page
|
|
296
|
+
* when the feature is off.
|
|
257
297
|
*/
|
|
258
298
|
declare class AgentDashboardAuthController {
|
|
259
299
|
private readonly auth;
|
|
260
300
|
private readonly basePath;
|
|
261
301
|
private readonly logger;
|
|
262
|
-
/** Warn once (not per request) so a broken hook can't be used to flood the logs. */
|
|
263
|
-
private
|
|
302
|
+
/** Warn once PER HOOK KIND (not per request) so a broken hook can't be used to flood the logs. */
|
|
303
|
+
private readonly warnedHooks;
|
|
264
304
|
constructor(auth: ResolvedDashboardAuth | null, basePath: string);
|
|
265
305
|
loginPage(req: AuthPageRequest, res: AuthPageResponse, returnToQuery?: string, error?: string): string;
|
|
266
306
|
login(body: LoginFormBody, req: unknown, res: AuthPageResponse): Promise<string>;
|
|
267
307
|
logout(req: unknown, res: AuthPageResponse): string;
|
|
308
|
+
session(request: unknown, response: unknown): Promise<void>;
|
|
309
|
+
sessionRequiredPage(): string;
|
|
268
310
|
/** Uniform failure: same redirect (generic error flag) whether the user is unknown or the password is wrong. */
|
|
269
311
|
private redirectToLoginError;
|
|
270
312
|
private currentSession;
|
|
271
313
|
/**
|
|
272
|
-
* Run
|
|
273
|
-
* (`null`) and warn-logged ONCE, so a buggy/unreachable hook never 500s the
|
|
274
|
-
* stack-trace leak nor floods the logs.
|
|
314
|
+
* Run a host hook (`login` or `session`) defensively: a throw (sync or async) is treated as a
|
|
315
|
+
* denial (`null`) and warn-logged ONCE PER KIND, so a buggy/unreachable hook never 500s the
|
|
316
|
+
* endpoint into a stack-trace leak nor floods the logs on repeated bad attempts.
|
|
275
317
|
*/
|
|
276
|
-
private
|
|
318
|
+
private runHook;
|
|
277
319
|
private requireAuth;
|
|
278
320
|
}
|
|
279
321
|
|
|
@@ -355,21 +397,36 @@ interface AgentDashboardOptions<TReq = unknown> {
|
|
|
355
397
|
*/
|
|
356
398
|
approvalActorRef?: (req: TReq) => string | undefined;
|
|
357
399
|
/**
|
|
358
|
-
* Gate the console (SPA + API) behind a
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
*
|
|
400
|
+
* Gate the console (SPA + API) behind a built-in, signed session cookie. Two ways to mint it:
|
|
401
|
+
*
|
|
402
|
+
* - **Mode A (`session`, recommended)**: your host app already has its own auth (SSO/OIDC/
|
|
403
|
+
* whatever) — the host frontend, carrying that auth, POSTs to `<basePath>/auth/session`, your
|
|
404
|
+
* `session` hook validates the raw request and returns the session user, and this library
|
|
405
|
+
* mints its cookie from that. No credential this library understands ever exists; you keep
|
|
406
|
+
* your own identity provider as the source of truth. A page-level request with no valid
|
|
407
|
+
* session (and only Mode A configured) gets a small server-rendered instruction page telling
|
|
408
|
+
* the visitor to sign in through your host app, since there is no login page to redirect to.
|
|
409
|
+
* - **Mode B (`login`, standalone fallback)**: no host frontend/IdP to lean on — the console
|
|
410
|
+
* serves its own small, dependency-free, server-rendered login page (`GET
|
|
411
|
+
* <basePath>/auth/login`) and your `login` hook validates submitted username/password. A
|
|
412
|
+
* missing/invalid/expired session redirects a page-level request to that login page with
|
|
413
|
+
* `?returnTo=<original url>`.
|
|
414
|
+
*
|
|
415
|
+
* Either mode can be used alone, or both together (e.g. Mode A for normal use, Mode B as a
|
|
416
|
+
* break-glass fallback) — at least one is required, or `forRoot`/`forRootAsync` throws at boot
|
|
417
|
+
* (an un-mintable gate is a boot error, not a silently-open or silently-stuck console). An
|
|
418
|
+
* unauthenticated API call always gets a plain `401`, regardless of mode — the caller reads the
|
|
419
|
+
* status code, not HTML. Composes with `guards` — BOTH gates must pass. See
|
|
420
|
+
* `DashboardAuthOptions` for the `secret`/`ttl`/`session`/`login` shape.
|
|
364
421
|
*/
|
|
365
422
|
dashboardAuth?: DashboardAuthOptions;
|
|
366
423
|
}
|
|
367
424
|
/**
|
|
368
|
-
* Async variant of {@link AgentDashboardOptions}, for a `login` hook that needs injected
|
|
369
|
-
* (e.g. an EntityManager to check credentials against a DB). Everything else stays
|
|
370
|
-
* `basePath`/`apiBasePath`/`guards` are needed at module-build time (the same
|
|
371
|
-
* `@dudousxd/nestjs-telescope`'s and `@dudousxd/nestjs-media`'s own `forRootAsync`s
|
|
372
|
-
* only the auth piece itself is resolved through DI.
|
|
425
|
+
* Async variant of {@link AgentDashboardOptions}, for a `session`/`login` hook that needs injected
|
|
426
|
+
* services (e.g. an EntityManager to check credentials against a DB). Everything else stays
|
|
427
|
+
* static/sync — `basePath`/`apiBasePath`/`guards` are needed at module-build time (the same
|
|
428
|
+
* constraint `@dudousxd/nestjs-telescope`'s and `@dudousxd/nestjs-media`'s own `forRootAsync`s
|
|
429
|
+
* document), so only the auth piece itself is resolved through DI.
|
|
373
430
|
*/
|
|
374
431
|
interface AgentDashboardAsyncOptions<TReq = unknown> {
|
|
375
432
|
basePath?: string;
|
|
@@ -404,13 +461,15 @@ declare class AgentApiModule {
|
|
|
404
461
|
}
|
|
405
462
|
/**
|
|
406
463
|
* Mounts the AI-gateway governance console: the bundled React SPA at `basePath` and its JSON + SSE
|
|
407
|
-
* API at `apiBasePath` (default `<basePath>/api`), plus the built-in
|
|
408
|
-
* `<basePath>/auth/*` when {@link AgentDashboardOptions.dashboardAuth} is set
|
|
464
|
+
* API at `apiBasePath` (default `<basePath>/api`), plus the built-in `dashboardAuth` routes at
|
|
465
|
+
* `<basePath>/auth/*` when {@link AgentDashboardOptions.dashboardAuth} is set — `POST
|
|
466
|
+
* <basePath>/auth/session` for Mode A (the host mints the session from its own auth) and/or `GET`/
|
|
467
|
+
* `POST <basePath>/auth/login` for Mode B (the built-in login screen).
|
|
409
468
|
*
|
|
410
469
|
* Import via `AgentDashboardModule.forRoot(...)` alongside your `@dudousxd/nestjs-agent` module
|
|
411
470
|
* (global), which must provide `AGENT_GOVERNANCE_QUERIES` (bound by a store adapter). Front the
|
|
412
471
|
* routes with the first-class `guards` option (plus `imports` for the guards' own dependencies) —
|
|
413
|
-
* see {@link AgentDashboardOptions.guards} — and/or the built-in `dashboardAuth` cookie
|
|
472
|
+
* see {@link AgentDashboardOptions.guards} — and/or the built-in `dashboardAuth` session cookie.
|
|
414
473
|
*
|
|
415
474
|
* Inertia hosts: the console is a full-page app, not an Inertia page. An in-app `<Link>` visit to
|
|
416
475
|
* `basePath` (an XHR carrying `X-Inertia`) is bounced with the protocol's own external-redirect
|
|
@@ -530,4 +589,4 @@ declare const DASHBOARD_APPROVAL_ACTOR_REF: unique symbol;
|
|
|
530
589
|
*/
|
|
531
590
|
declare const DASHBOARD_AUTH: unique symbol;
|
|
532
591
|
|
|
533
|
-
export { AGENT_ACTOR_DIRECTORY, AGENT_ACTOR_RESOLVER, AGENT_APPROVAL_PORT, AGENT_GOVERNANCE_QUERIES, AGENT_PRICING_STORE, type ActorSpendRowWithLabel, AgentApiController, AgentApiModule, type AgentDashboardAsyncOptions, AgentDashboardAuthController, AgentDashboardModule, type AgentDashboardMountPathsOptions, type AgentDashboardOptions, AgentUiController, DASHBOARD_API_PATH, DASHBOARD_APPROVAL_ACTOR_REF, DASHBOARD_AUTH, DASHBOARD_BASE_PATH, type DashboardAuthOptions, DashboardService, type LiveAgentEvent, type LoginHook, type ReliabilityOverview, type ResolvedDashboardAuth, type SpendOverview, type ThreadActivityRowWithLabel, type ThreadSpendRowWithLabel, type WithActorLabel, agentDashboardMountPaths, parsePriceInput, resolveDashboardAuth };
|
|
592
|
+
export { AGENT_ACTOR_DIRECTORY, AGENT_ACTOR_RESOLVER, AGENT_APPROVAL_PORT, AGENT_GOVERNANCE_QUERIES, AGENT_PRICING_STORE, type ActorSpendRowWithLabel, AgentApiController, AgentApiModule, type AgentDashboardAsyncOptions, AgentDashboardAuthController, AgentDashboardModule, type AgentDashboardMountPathsOptions, type AgentDashboardOptions, AgentUiController, type AuthMode, DASHBOARD_API_PATH, DASHBOARD_APPROVAL_ACTOR_REF, DASHBOARD_AUTH, DASHBOARD_BASE_PATH, type DashboardAuthOptions, DashboardService, type LiveAgentEvent, type LoginHook, type ReliabilityOverview, type ResolvedDashboardAuth, type RevalidateHook, type SessionHook, type SpendOverview, type ThreadActivityRowWithLabel, type ThreadSpendRowWithLabel, type WithActorLabel, agentDashboardMountPaths, parsePriceInput, resolveDashboardAuth };
|
package/dist/server/index.d.ts
CHANGED
|
@@ -194,12 +194,36 @@ interface DashboardSessionUser {
|
|
|
194
194
|
roles?: string[];
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
-
/**
|
|
197
|
+
/**
|
|
198
|
+
* Host hook for Mode A: validates the host's own auth on the raw request POSTed to
|
|
199
|
+
* `<basePath>/auth/session`. Return the session user to grant access, or `null` to deny.
|
|
200
|
+
*/
|
|
201
|
+
type SessionHook = (request: unknown) => Promise<DashboardSessionUser | null> | DashboardSessionUser | null;
|
|
202
|
+
/** Host hook for Mode B: validates submitted credentials for the built-in login screen. */
|
|
198
203
|
type LoginHook = (username: string, password: string) => Promise<DashboardSessionUser | null> | DashboardSessionUser | null;
|
|
204
|
+
/**
|
|
205
|
+
* Re-checks a LIVE session when the cookie is slid forward. Runs at most once per `ttl/2` PER
|
|
206
|
+
* COOKIE GENERATION — the gate is on the inbound cookie's `iat`, so every request still carrying a
|
|
207
|
+
* not-yet-renewed cookie would, on its own, invoke this hook again; `maybeRenewSession` de-dupes
|
|
208
|
+
* concurrent calls for the same session (`sub` + `iat`) into a single in-flight call, so a page
|
|
209
|
+
* load that fires N parallel API calls still costs exactly ONE host round-trip, not N. Still not a
|
|
210
|
+
* per-session-lifetime cap — this can run again every `ttl/2` as the session keeps sliding, so a
|
|
211
|
+
* slow or expensive check should still debounce/cache across renewal windows on its own. Return
|
|
212
|
+
* `false` to revoke (the cookie is cleared and the request denied). Distinct from `session`: that
|
|
213
|
+
* hook reads the host's auth off a fresh request, which a console XHR does not carry — this one
|
|
214
|
+
* receives the already-minted session.
|
|
215
|
+
*/
|
|
216
|
+
type RevalidateHook = (session: DashboardSessionUser) => Promise<boolean> | boolean;
|
|
217
|
+
/** Which `dashboardAuth` hook(s) a resolved config was given — see `resolveDashboardAuth`. */
|
|
218
|
+
type AuthMode = 'session' | 'login';
|
|
199
219
|
/**
|
|
200
220
|
* Author-facing `dashboardAuth` option (see `AgentDashboardOptions.dashboardAuth`). Gates the
|
|
201
|
-
* console (SPA + API) behind a
|
|
202
|
-
*
|
|
221
|
+
* console (SPA + API) behind a signed session cookie. Two ways to mint that cookie: Mode A
|
|
222
|
+
* (`session`) — the host frontend, already carrying its own auth, POSTs to
|
|
223
|
+
* `<basePath>/auth/session` and the host hook decides — or Mode B (`login`) — the built-in,
|
|
224
|
+
* dependency-free server-rendered login screen. Mirrors `@dudousxd/nestjs-telescope`'s
|
|
225
|
+
* `dashboardAuth` two-mode shape. At least one of the two is required so an un-mintable gate is a
|
|
226
|
+
* boot error, not a silently-open (or silently-stuck) console.
|
|
203
227
|
*/
|
|
204
228
|
interface DashboardAuthOptions {
|
|
205
229
|
/** REQUIRED HMAC-SHA256 signing key (32+ bytes recommended). Missing/empty => boot error (fail closed). */
|
|
@@ -207,23 +231,36 @@ interface DashboardAuthOptions {
|
|
|
207
231
|
/** Cookie TTL (duration string `'<n><ms|s|m|h|d>'`, e.g. `'30m'`, `'7d'`). Default `'8h'`. */
|
|
208
232
|
ttl?: string;
|
|
209
233
|
/**
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
234
|
+
* Mode A: validates the host's own auth on the raw request POSTed to `<basePath>/auth/session`.
|
|
235
|
+
* Return the session user to grant access, or `null` to deny. No credential reaches this
|
|
236
|
+
* library — the host decides from whatever it already has (a header, an SSO cookie, ...).
|
|
213
237
|
*/
|
|
214
|
-
|
|
238
|
+
session?: SessionHook;
|
|
239
|
+
/**
|
|
240
|
+
* Mode B: validates submitted username/password. Return the session user to grant access, or
|
|
241
|
+
* `null` to deny — the login endpoint responds identically (generic failure) for an unknown user
|
|
242
|
+
* and a wrong password, so it never reveals which one was wrong.
|
|
243
|
+
*/
|
|
244
|
+
login?: LoginHook;
|
|
245
|
+
/** Re-checks a live session on sliding renewal; see `RevalidateHook`. Not a mode — it cannot
|
|
246
|
+
* mint a session, only revoke one already minted by `session`/`login`. */
|
|
247
|
+
revalidate?: RevalidateHook;
|
|
215
248
|
}
|
|
216
249
|
/** Resolved, validated `dashboardAuth` config used by the guards/controller. */
|
|
217
250
|
interface ResolvedDashboardAuth {
|
|
218
251
|
secret: string;
|
|
219
252
|
ttlMs: number;
|
|
220
|
-
|
|
253
|
+
modes: AuthMode[];
|
|
254
|
+
session?: SessionHook;
|
|
255
|
+
login?: LoginHook;
|
|
256
|
+
revalidate?: RevalidateHook;
|
|
221
257
|
}
|
|
222
258
|
/**
|
|
223
259
|
* Validate + resolve `dashboardAuth`. Returns `null` when unconfigured (behavior unchanged — the
|
|
224
260
|
* console stays open, front it with your own `guards`). Throws at boot (fail closed) when
|
|
225
|
-
* configured but missing a secret
|
|
226
|
-
* shipping an open or
|
|
261
|
+
* configured but missing a secret, when neither `session` nor `login` is given, or when a given
|
|
262
|
+
* hook isn't a function — the host learns immediately rather than shipping an open, stuck, or
|
|
263
|
+
* un-mintable console.
|
|
227
264
|
*/
|
|
228
265
|
declare function resolveDashboardAuth(options: DashboardAuthOptions | undefined): ResolvedDashboardAuth | null;
|
|
229
266
|
|
|
@@ -248,32 +285,37 @@ interface LoginFormBody {
|
|
|
248
285
|
* built-in gates check for, so they can never be made to require the very auth they grant. Mirrors
|
|
249
286
|
* `@dudousxd/nestjs-telescope`'s own auth controller staying outside its `stampGuards` call.
|
|
250
287
|
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
288
|
+
* Two ways to mint that cookie, matching `resolveDashboardAuth`'s modes: Mode A (`session`) — the
|
|
289
|
+
* host frontend, already carrying its own auth, POSTs to `<basePath>/auth/session` and the host
|
|
290
|
+
* hook decides — or Mode B (`login`), a dependency-free, server-rendered login form (GET/POST) —
|
|
291
|
+
* the bundled React SPA in this package is a built Vite artifact with no auth-aware UI of its own,
|
|
292
|
+
* so gating the actual page navigation needs a self-contained flow that doesn't touch it. Each
|
|
293
|
+
* mode's routes 404 when that mode isn't listed in `auth.modes` — the single source of truth for
|
|
294
|
+
* which mode(s) are configured, not hook-presence truthiness — and every route 404s outright when
|
|
295
|
+
* `dashboardAuth` is unconfigured (`DASHBOARD_AUTH` resolves to `null`) — no dangling login page
|
|
296
|
+
* when the feature is off.
|
|
257
297
|
*/
|
|
258
298
|
declare class AgentDashboardAuthController {
|
|
259
299
|
private readonly auth;
|
|
260
300
|
private readonly basePath;
|
|
261
301
|
private readonly logger;
|
|
262
|
-
/** Warn once (not per request) so a broken hook can't be used to flood the logs. */
|
|
263
|
-
private
|
|
302
|
+
/** Warn once PER HOOK KIND (not per request) so a broken hook can't be used to flood the logs. */
|
|
303
|
+
private readonly warnedHooks;
|
|
264
304
|
constructor(auth: ResolvedDashboardAuth | null, basePath: string);
|
|
265
305
|
loginPage(req: AuthPageRequest, res: AuthPageResponse, returnToQuery?: string, error?: string): string;
|
|
266
306
|
login(body: LoginFormBody, req: unknown, res: AuthPageResponse): Promise<string>;
|
|
267
307
|
logout(req: unknown, res: AuthPageResponse): string;
|
|
308
|
+
session(request: unknown, response: unknown): Promise<void>;
|
|
309
|
+
sessionRequiredPage(): string;
|
|
268
310
|
/** Uniform failure: same redirect (generic error flag) whether the user is unknown or the password is wrong. */
|
|
269
311
|
private redirectToLoginError;
|
|
270
312
|
private currentSession;
|
|
271
313
|
/**
|
|
272
|
-
* Run
|
|
273
|
-
* (`null`) and warn-logged ONCE, so a buggy/unreachable hook never 500s the
|
|
274
|
-
* stack-trace leak nor floods the logs.
|
|
314
|
+
* Run a host hook (`login` or `session`) defensively: a throw (sync or async) is treated as a
|
|
315
|
+
* denial (`null`) and warn-logged ONCE PER KIND, so a buggy/unreachable hook never 500s the
|
|
316
|
+
* endpoint into a stack-trace leak nor floods the logs on repeated bad attempts.
|
|
275
317
|
*/
|
|
276
|
-
private
|
|
318
|
+
private runHook;
|
|
277
319
|
private requireAuth;
|
|
278
320
|
}
|
|
279
321
|
|
|
@@ -355,21 +397,36 @@ interface AgentDashboardOptions<TReq = unknown> {
|
|
|
355
397
|
*/
|
|
356
398
|
approvalActorRef?: (req: TReq) => string | undefined;
|
|
357
399
|
/**
|
|
358
|
-
* Gate the console (SPA + API) behind a
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
*
|
|
400
|
+
* Gate the console (SPA + API) behind a built-in, signed session cookie. Two ways to mint it:
|
|
401
|
+
*
|
|
402
|
+
* - **Mode A (`session`, recommended)**: your host app already has its own auth (SSO/OIDC/
|
|
403
|
+
* whatever) — the host frontend, carrying that auth, POSTs to `<basePath>/auth/session`, your
|
|
404
|
+
* `session` hook validates the raw request and returns the session user, and this library
|
|
405
|
+
* mints its cookie from that. No credential this library understands ever exists; you keep
|
|
406
|
+
* your own identity provider as the source of truth. A page-level request with no valid
|
|
407
|
+
* session (and only Mode A configured) gets a small server-rendered instruction page telling
|
|
408
|
+
* the visitor to sign in through your host app, since there is no login page to redirect to.
|
|
409
|
+
* - **Mode B (`login`, standalone fallback)**: no host frontend/IdP to lean on — the console
|
|
410
|
+
* serves its own small, dependency-free, server-rendered login page (`GET
|
|
411
|
+
* <basePath>/auth/login`) and your `login` hook validates submitted username/password. A
|
|
412
|
+
* missing/invalid/expired session redirects a page-level request to that login page with
|
|
413
|
+
* `?returnTo=<original url>`.
|
|
414
|
+
*
|
|
415
|
+
* Either mode can be used alone, or both together (e.g. Mode A for normal use, Mode B as a
|
|
416
|
+
* break-glass fallback) — at least one is required, or `forRoot`/`forRootAsync` throws at boot
|
|
417
|
+
* (an un-mintable gate is a boot error, not a silently-open or silently-stuck console). An
|
|
418
|
+
* unauthenticated API call always gets a plain `401`, regardless of mode — the caller reads the
|
|
419
|
+
* status code, not HTML. Composes with `guards` — BOTH gates must pass. See
|
|
420
|
+
* `DashboardAuthOptions` for the `secret`/`ttl`/`session`/`login` shape.
|
|
364
421
|
*/
|
|
365
422
|
dashboardAuth?: DashboardAuthOptions;
|
|
366
423
|
}
|
|
367
424
|
/**
|
|
368
|
-
* Async variant of {@link AgentDashboardOptions}, for a `login` hook that needs injected
|
|
369
|
-
* (e.g. an EntityManager to check credentials against a DB). Everything else stays
|
|
370
|
-
* `basePath`/`apiBasePath`/`guards` are needed at module-build time (the same
|
|
371
|
-
* `@dudousxd/nestjs-telescope`'s and `@dudousxd/nestjs-media`'s own `forRootAsync`s
|
|
372
|
-
* only the auth piece itself is resolved through DI.
|
|
425
|
+
* Async variant of {@link AgentDashboardOptions}, for a `session`/`login` hook that needs injected
|
|
426
|
+
* services (e.g. an EntityManager to check credentials against a DB). Everything else stays
|
|
427
|
+
* static/sync — `basePath`/`apiBasePath`/`guards` are needed at module-build time (the same
|
|
428
|
+
* constraint `@dudousxd/nestjs-telescope`'s and `@dudousxd/nestjs-media`'s own `forRootAsync`s
|
|
429
|
+
* document), so only the auth piece itself is resolved through DI.
|
|
373
430
|
*/
|
|
374
431
|
interface AgentDashboardAsyncOptions<TReq = unknown> {
|
|
375
432
|
basePath?: string;
|
|
@@ -404,13 +461,15 @@ declare class AgentApiModule {
|
|
|
404
461
|
}
|
|
405
462
|
/**
|
|
406
463
|
* Mounts the AI-gateway governance console: the bundled React SPA at `basePath` and its JSON + SSE
|
|
407
|
-
* API at `apiBasePath` (default `<basePath>/api`), plus the built-in
|
|
408
|
-
* `<basePath>/auth/*` when {@link AgentDashboardOptions.dashboardAuth} is set
|
|
464
|
+
* API at `apiBasePath` (default `<basePath>/api`), plus the built-in `dashboardAuth` routes at
|
|
465
|
+
* `<basePath>/auth/*` when {@link AgentDashboardOptions.dashboardAuth} is set — `POST
|
|
466
|
+
* <basePath>/auth/session` for Mode A (the host mints the session from its own auth) and/or `GET`/
|
|
467
|
+
* `POST <basePath>/auth/login` for Mode B (the built-in login screen).
|
|
409
468
|
*
|
|
410
469
|
* Import via `AgentDashboardModule.forRoot(...)` alongside your `@dudousxd/nestjs-agent` module
|
|
411
470
|
* (global), which must provide `AGENT_GOVERNANCE_QUERIES` (bound by a store adapter). Front the
|
|
412
471
|
* routes with the first-class `guards` option (plus `imports` for the guards' own dependencies) —
|
|
413
|
-
* see {@link AgentDashboardOptions.guards} — and/or the built-in `dashboardAuth` cookie
|
|
472
|
+
* see {@link AgentDashboardOptions.guards} — and/or the built-in `dashboardAuth` session cookie.
|
|
414
473
|
*
|
|
415
474
|
* Inertia hosts: the console is a full-page app, not an Inertia page. An in-app `<Link>` visit to
|
|
416
475
|
* `basePath` (an XHR carrying `X-Inertia`) is bounced with the protocol's own external-redirect
|
|
@@ -530,4 +589,4 @@ declare const DASHBOARD_APPROVAL_ACTOR_REF: unique symbol;
|
|
|
530
589
|
*/
|
|
531
590
|
declare const DASHBOARD_AUTH: unique symbol;
|
|
532
591
|
|
|
533
|
-
export { AGENT_ACTOR_DIRECTORY, AGENT_ACTOR_RESOLVER, AGENT_APPROVAL_PORT, AGENT_GOVERNANCE_QUERIES, AGENT_PRICING_STORE, type ActorSpendRowWithLabel, AgentApiController, AgentApiModule, type AgentDashboardAsyncOptions, AgentDashboardAuthController, AgentDashboardModule, type AgentDashboardMountPathsOptions, type AgentDashboardOptions, AgentUiController, DASHBOARD_API_PATH, DASHBOARD_APPROVAL_ACTOR_REF, DASHBOARD_AUTH, DASHBOARD_BASE_PATH, type DashboardAuthOptions, DashboardService, type LiveAgentEvent, type LoginHook, type ReliabilityOverview, type ResolvedDashboardAuth, type SpendOverview, type ThreadActivityRowWithLabel, type ThreadSpendRowWithLabel, type WithActorLabel, agentDashboardMountPaths, parsePriceInput, resolveDashboardAuth };
|
|
592
|
+
export { AGENT_ACTOR_DIRECTORY, AGENT_ACTOR_RESOLVER, AGENT_APPROVAL_PORT, AGENT_GOVERNANCE_QUERIES, AGENT_PRICING_STORE, type ActorSpendRowWithLabel, AgentApiController, AgentApiModule, type AgentDashboardAsyncOptions, AgentDashboardAuthController, AgentDashboardModule, type AgentDashboardMountPathsOptions, type AgentDashboardOptions, AgentUiController, type AuthMode, DASHBOARD_API_PATH, DASHBOARD_APPROVAL_ACTOR_REF, DASHBOARD_AUTH, DASHBOARD_BASE_PATH, type DashboardAuthOptions, DashboardService, type LiveAgentEvent, type LoginHook, type ReliabilityOverview, type ResolvedDashboardAuth, type RevalidateHook, type SessionHook, type SpendOverview, type ThreadActivityRowWithLabel, type ThreadSpendRowWithLabel, type WithActorLabel, agentDashboardMountPaths, parsePriceInput, resolveDashboardAuth };
|