@dudousxd/nestjs-agent-dashboard 0.8.0 → 0.9.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,7 +1,7 @@
1
1
  import { ActorSpendRow, AgentGovernanceQueries, ActorDirectory, AgentPricingStore, AgentApprovalPort, GovernanceRange, ModelSpendRow, UsageTrendPoint, ThreadSpendRow, RunMetrics, RunAgentBreakdownRow, RunErrorBreakdownRow, RunTrendPoint, RecentRunRow, ToolCallActivityRow, GovernancePageQuery, ToolCallWhere, GovernancePage, RunWhere, PendingApprovalRow, ToolStatRow, ThreadActivityRow, ThreadWhere, CurrentModelPrice, ActorResolver, ModelPriceInput } from '@dudousxd/nestjs-agent-core';
2
2
  export { ActorDirectory } from '@dudousxd/nestjs-agent-core';
3
3
  import { Observable } from 'rxjs';
4
- import { DynamicModule, Type, CanActivate, StreamableFile } from '@nestjs/common';
4
+ import { DynamicModule, Type, CanActivate, Provider, InjectionToken, OptionalFactoryDependency, StreamableFile } from '@nestjs/common';
5
5
 
6
6
  /** An actor-scoped row decorated with its resolved display label — `null` when unbound or unresolved. */
7
7
  interface WithActorLabel {
@@ -112,6 +112,10 @@ declare class DashboardService {
112
112
  /**
113
113
  * JSON + SSE API consumed by the AI-gateway console SPA. Mounted at `apiBasePath` (set by
114
114
  * `RouterModule` in {@link AgentDashboardModule.forRoot}), so the controller routes are relative.
115
+ *
116
+ * `@UseGuards(DashboardAuthGuard)` is a NO-OP unless the host configured `dashboardAuth` — see
117
+ * that guard's own doc. Host `guards` (see {@link AgentDashboardOptions.guards}) are APPENDED onto
118
+ * this baseline by `stampGuards` (`./guards.js`), never replacing it.
115
119
  */
116
120
  declare class AgentApiController {
117
121
  private readonly dashboard;
@@ -183,6 +187,96 @@ declare class AgentApiController {
183
187
  }>;
184
188
  }
185
189
 
190
+ /** The session user the host's `login` hook returns to mint a cookie. */
191
+ interface DashboardSessionUser {
192
+ id: string;
193
+ name?: string;
194
+ roles?: string[];
195
+ }
196
+
197
+ /** Host hook: validates submitted credentials for the built-in login screen. */
198
+ type LoginHook = (username: string, password: string) => Promise<DashboardSessionUser | null> | DashboardSessionUser | null;
199
+ /**
200
+ * Author-facing `dashboardAuth` option (see `AgentDashboardOptions.dashboardAuth`). Gates the
201
+ * console (SPA + API) behind a built-in cookie-session login screen — no host frontend changes,
202
+ * no SSO required. Mirrors `@dudousxd/nestjs-telescope`'s `dashboardAuth` Mode B (`login`).
203
+ */
204
+ interface DashboardAuthOptions {
205
+ /** REQUIRED HMAC-SHA256 signing key (32+ bytes recommended). Missing/empty => boot error (fail closed). */
206
+ secret: string;
207
+ /** Cookie TTL (duration string `'<n><ms|s|m|h|d>'`, e.g. `'30m'`, `'7d'`). Default `'8h'`. */
208
+ ttl?: string;
209
+ /**
210
+ * Validates submitted username/password. Return the session user to grant access, or `null` to
211
+ * deny — the login endpoint responds identically (generic failure) for an unknown user and a
212
+ * wrong password, so it never reveals which one was wrong.
213
+ */
214
+ login: LoginHook;
215
+ }
216
+ /** Resolved, validated `dashboardAuth` config used by the guards/controller. */
217
+ interface ResolvedDashboardAuth {
218
+ secret: string;
219
+ ttlMs: number;
220
+ login: LoginHook;
221
+ }
222
+ /**
223
+ * Validate + resolve `dashboardAuth`. Returns `null` when unconfigured (behavior unchanged — the
224
+ * console stays open, front it with your own `guards`). Throws at boot (fail closed) when
225
+ * configured but missing a secret or a `login` hook — the host learns immediately rather than
226
+ * shipping an open or un-mintable console.
227
+ */
228
+ declare function resolveDashboardAuth(options: DashboardAuthOptions | undefined): ResolvedDashboardAuth | null;
229
+
230
+ /** The slice of the platform request this controller reads — structural, no Express/Fastify dependency. */
231
+ interface AuthPageRequest {
232
+ headers: Record<string, string | string[] | undefined>;
233
+ }
234
+ /** The slice of the platform response this controller writes (passthrough mode — Nest still sends). */
235
+ interface AuthPageResponse {
236
+ status(code: number): unknown;
237
+ setHeader(name: string, value: string): unknown;
238
+ }
239
+ interface LoginFormBody {
240
+ username?: unknown;
241
+ password?: unknown;
242
+ returnTo?: unknown;
243
+ }
244
+ /**
245
+ * Mounted directly on `AgentDashboardModule` at `<basePath>/auth/*` — a SEPARATE controller from
246
+ * `AgentUiController`/`AgentApiController` so it carries neither `DashboardAuthPageGuard`,
247
+ * `DashboardAuthGuard`, nor any host `guards`: these routes MINT (and clear) the session cookie the
248
+ * built-in gates check for, so they can never be made to require the very auth they grant. Mirrors
249
+ * `@dudousxd/nestjs-telescope`'s own auth controller staying outside its `stampGuards` call.
250
+ *
251
+ * A dependency-free, server-rendered login form (GET/POST) rather than a JSON API + client-side
252
+ * screen — the bundled React SPA in this package is a built Vite artifact with no auth-aware UI of
253
+ * its own, so gating the actual page navigation needs a self-contained flow that doesn't touch it.
254
+ *
255
+ * 404s every route when `dashboardAuth` is unconfigured (`DASHBOARD_AUTH` resolves to `null`) — no
256
+ * dangling login page when the feature is off.
257
+ */
258
+ declare class AgentDashboardAuthController {
259
+ private readonly auth;
260
+ private readonly basePath;
261
+ private readonly logger;
262
+ /** Warn once (not per request) so a broken hook can't be used to flood the logs. */
263
+ private warnedLoginHookThrow;
264
+ constructor(auth: ResolvedDashboardAuth | null, basePath: string);
265
+ loginPage(req: AuthPageRequest, res: AuthPageResponse, returnToQuery?: string, error?: string): string;
266
+ login(body: LoginFormBody, req: unknown, res: AuthPageResponse): Promise<string>;
267
+ logout(req: unknown, res: AuthPageResponse): string;
268
+ /** Uniform failure: same redirect (generic error flag) whether the user is unknown or the password is wrong. */
269
+ private redirectToLoginError;
270
+ private currentSession;
271
+ /**
272
+ * Run the host's `login` hook defensively: a throw (sync or async) is treated as a denial
273
+ * (`null`) and warn-logged ONCE, so a buggy/unreachable hook never 500s the endpoint into a
274
+ * stack-trace leak nor floods the logs.
275
+ */
276
+ private runLoginHook;
277
+ private requireAuth;
278
+ }
279
+
186
280
  /** Same shape {@link AgentDashboardModule.forRoot} accepts — kept local so this stays a pure, DI-free helper. */
187
281
  interface AgentDashboardMountPathsOptions {
188
282
  basePath?: string;
@@ -234,10 +328,9 @@ interface AgentDashboardOptions<TReq = unknown> {
234
328
  apiBasePath?: string;
235
329
  /**
236
330
  * Guard classes fronting BOTH dashboard controllers (the SPA at `basePath` and its JSON API at
237
- * `apiBasePath`). Stamped onto each controller via `@nestjs/common`'s own `@UseGuards` metadata key
238
- * — REPLACE semantics, so a second `forRoot(...)` call overwrites (not appends to) whatever a prior
239
- * call stamped, same as re-applying `@UseGuards` by hand. Omit to leave the routes unguarded (the
240
- * host fronts them another way, e.g. a global guard or reverse-proxy auth).
331
+ * `apiBasePath`) — APPENDED onto the built-in `dashboardAuth` gate (a no-op unless configured;
332
+ * see {@link dashboardAuth}), so a request must pass BOTH. Omit to leave the routes gated by
333
+ * `dashboardAuth` alone (or fully open when that's unset too).
241
334
  *
242
335
  * A guard's own DEPENDENCIES resolve from this module's `imports` (see {@link imports}) — the
243
336
  * dashboard module has no application context of its own to pull them from otherwise.
@@ -261,6 +354,36 @@ interface AgentDashboardOptions<TReq = unknown> {
261
354
  * override wins outright, with no resolver fallback.
262
355
  */
263
356
  approvalActorRef?: (req: TReq) => string | undefined;
357
+ /**
358
+ * Gate the console (SPA + API) behind a BUILT-IN cookie-session login screen — the simplest way
359
+ * to protect `basePath` when the host has no ready-made guard (e.g. header-only auth that a
360
+ * browser navigation can't attach). Omit to leave the console open, or fronted only by
361
+ * {@link guards}. When set, an unauthenticated page navigation is redirected (302) to
362
+ * `<basePath>/auth/login`; an unauthenticated API call gets a `401`. Composes with `guards` —
363
+ * BOTH gates must pass. See `DashboardAuthOptions` for the `secret`/`ttl`/`login` shape.
364
+ */
365
+ dashboardAuth?: DashboardAuthOptions;
366
+ }
367
+ /**
368
+ * Async variant of {@link AgentDashboardOptions}, for a `login` hook that needs injected services
369
+ * (e.g. an EntityManager to check credentials against a DB). Everything else stays static/sync —
370
+ * `basePath`/`apiBasePath`/`guards` are needed at module-build time (the same constraint
371
+ * `@dudousxd/nestjs-telescope`'s and `@dudousxd/nestjs-media`'s own `forRootAsync`s document), so
372
+ * only the auth piece itself is resolved through DI.
373
+ */
374
+ interface AgentDashboardAsyncOptions<TReq = unknown> {
375
+ basePath?: string;
376
+ apiBasePath?: string;
377
+ guards?: Type<CanActivate>[];
378
+ imports?: DynamicModule['imports'];
379
+ approvalActorRef?: (req: TReq) => string | undefined;
380
+ /** Providers injected into `useDashboardAuth`, in order. Shared with a guard class's own deps via `imports`. */
381
+ inject?: Array<InjectionToken | OptionalFactoryDependency>;
382
+ /**
383
+ * Build the `dashboardAuth` config from injected deps, or return `undefined` to leave the
384
+ * console open (or fronted only by `guards`). See {@link AgentDashboardOptions.dashboardAuth}.
385
+ */
386
+ useDashboardAuth: (...deps: any[]) => DashboardAuthOptions | undefined | Promise<DashboardAuthOptions | undefined>;
264
387
  }
265
388
  /**
266
389
  * Holds the JSON API + SSE controller and its read service, mounted on its own path by `forRoot`.
@@ -275,16 +398,19 @@ declare class AgentApiModule {
275
398
  imports?: DynamicModule['imports'];
276
399
  guards?: Type<CanActivate>[];
277
400
  approvalActorRef?: (req: TReq) => string | undefined;
401
+ /** Provider for `DASHBOARD_AUTH` — a `useValue` (`forRoot`) or a `useFactory` (`forRootAsync`). */
402
+ dashboardAuth: Provider;
278
403
  }): DynamicModule;
279
404
  }
280
405
  /**
281
406
  * Mounts the AI-gateway governance console: the bundled React SPA at `basePath` and its JSON + SSE
282
- * API at `apiBasePath` (default `<basePath>/api`).
407
+ * API at `apiBasePath` (default `<basePath>/api`), plus the built-in login screen's routes at
408
+ * `<basePath>/auth/*` when {@link AgentDashboardOptions.dashboardAuth} is set.
283
409
  *
284
410
  * Import via `AgentDashboardModule.forRoot(...)` alongside your `@dudousxd/nestjs-agent` module
285
411
  * (global), which must provide `AGENT_GOVERNANCE_QUERIES` (bound by a store adapter). Front the
286
412
  * routes with the first-class `guards` option (plus `imports` for the guards' own dependencies) —
287
- * see {@link AgentDashboardOptions.guards}.
413
+ * see {@link AgentDashboardOptions.guards} — and/or the built-in `dashboardAuth` cookie login.
288
414
  *
289
415
  * Inertia hosts: the console is a full-page app, not an Inertia page. An in-app `<Link>` visit to
290
416
  * `basePath` (an XHR carrying `X-Inertia`) is bounced with the protocol's own external-redirect
@@ -294,6 +420,7 @@ declare class AgentApiModule {
294
420
  */
295
421
  declare class AgentDashboardModule {
296
422
  static forRoot<TReq = unknown>(options?: AgentDashboardOptions<TReq>): DynamicModule;
423
+ static forRootAsync<TReq = unknown>(options: AgentDashboardAsyncOptions<TReq>): DynamicModule;
297
424
  }
298
425
 
299
426
  /**
@@ -314,6 +441,12 @@ interface UiPageResponse {
314
441
  * Serves the bundled AI-gateway console SPA at the configured base (+ hashed assets at
315
442
  * `<base>/assets`). The path comes from `RouterModule` (set by
316
443
  * {@link AgentDashboardModule.forRoot}({ basePath })), so the controller routes are relative.
444
+ *
445
+ * `@UseGuards(DashboardAuthPageGuard)` is a NO-OP unless the host configured `dashboardAuth` — see
446
+ * that guard's own doc. Host `guards` (see {@link AgentDashboardOptions.guards}) are APPENDED onto
447
+ * this baseline by `stampGuards` (`./guards.js`), never replacing it. `@UseFilters` turns the
448
+ * guard's redirect-on-deny into an actual 302 response; it is a no-op when the guard never throws
449
+ * (i.e. `dashboardAuth` unconfigured).
317
450
  */
318
451
  declare class AgentUiController {
319
452
  private readonly basePath;
@@ -388,5 +521,13 @@ declare const DASHBOARD_API_PATH: unique symbol;
388
521
  * `@dudousxd/nestjs-agent`).
389
522
  */
390
523
  declare const DASHBOARD_APPROVAL_ACTOR_REF: unique symbol;
524
+ /**
525
+ * DI token carrying the resolved `dashboardAuth` config (`ResolvedDashboardAuth | null`) — `null`
526
+ * when the host didn't configure it (the built-in guards are then a no-op, today's behavior
527
+ * byte-for-byte). Bound once by `AgentApiModule.register` and re-exported so `AgentUiController`
528
+ * and `AgentDashboardAuthController` (hosted directly by `AgentDashboardModule`, which imports
529
+ * `AgentApiModule`) resolve the SAME instance rather than re-running a host's async factory twice.
530
+ */
531
+ declare const DASHBOARD_AUTH: unique symbol;
391
532
 
392
- export { AGENT_ACTOR_DIRECTORY, AGENT_ACTOR_RESOLVER, AGENT_APPROVAL_PORT, AGENT_GOVERNANCE_QUERIES, AGENT_PRICING_STORE, type ActorSpendRowWithLabel, AgentApiController, AgentApiModule, AgentDashboardModule, type AgentDashboardMountPathsOptions, type AgentDashboardOptions, AgentUiController, DASHBOARD_API_PATH, DASHBOARD_APPROVAL_ACTOR_REF, DASHBOARD_BASE_PATH, DashboardService, type LiveAgentEvent, type ReliabilityOverview, type SpendOverview, type ThreadActivityRowWithLabel, type ThreadSpendRowWithLabel, type WithActorLabel, agentDashboardMountPaths, parsePriceInput };
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 };
@@ -1,7 +1,7 @@
1
1
  import { ActorSpendRow, AgentGovernanceQueries, ActorDirectory, AgentPricingStore, AgentApprovalPort, GovernanceRange, ModelSpendRow, UsageTrendPoint, ThreadSpendRow, RunMetrics, RunAgentBreakdownRow, RunErrorBreakdownRow, RunTrendPoint, RecentRunRow, ToolCallActivityRow, GovernancePageQuery, ToolCallWhere, GovernancePage, RunWhere, PendingApprovalRow, ToolStatRow, ThreadActivityRow, ThreadWhere, CurrentModelPrice, ActorResolver, ModelPriceInput } from '@dudousxd/nestjs-agent-core';
2
2
  export { ActorDirectory } from '@dudousxd/nestjs-agent-core';
3
3
  import { Observable } from 'rxjs';
4
- import { DynamicModule, Type, CanActivate, StreamableFile } from '@nestjs/common';
4
+ import { DynamicModule, Type, CanActivate, Provider, InjectionToken, OptionalFactoryDependency, StreamableFile } from '@nestjs/common';
5
5
 
6
6
  /** An actor-scoped row decorated with its resolved display label — `null` when unbound or unresolved. */
7
7
  interface WithActorLabel {
@@ -112,6 +112,10 @@ declare class DashboardService {
112
112
  /**
113
113
  * JSON + SSE API consumed by the AI-gateway console SPA. Mounted at `apiBasePath` (set by
114
114
  * `RouterModule` in {@link AgentDashboardModule.forRoot}), so the controller routes are relative.
115
+ *
116
+ * `@UseGuards(DashboardAuthGuard)` is a NO-OP unless the host configured `dashboardAuth` — see
117
+ * that guard's own doc. Host `guards` (see {@link AgentDashboardOptions.guards}) are APPENDED onto
118
+ * this baseline by `stampGuards` (`./guards.js`), never replacing it.
115
119
  */
116
120
  declare class AgentApiController {
117
121
  private readonly dashboard;
@@ -183,6 +187,96 @@ declare class AgentApiController {
183
187
  }>;
184
188
  }
185
189
 
190
+ /** The session user the host's `login` hook returns to mint a cookie. */
191
+ interface DashboardSessionUser {
192
+ id: string;
193
+ name?: string;
194
+ roles?: string[];
195
+ }
196
+
197
+ /** Host hook: validates submitted credentials for the built-in login screen. */
198
+ type LoginHook = (username: string, password: string) => Promise<DashboardSessionUser | null> | DashboardSessionUser | null;
199
+ /**
200
+ * Author-facing `dashboardAuth` option (see `AgentDashboardOptions.dashboardAuth`). Gates the
201
+ * console (SPA + API) behind a built-in cookie-session login screen — no host frontend changes,
202
+ * no SSO required. Mirrors `@dudousxd/nestjs-telescope`'s `dashboardAuth` Mode B (`login`).
203
+ */
204
+ interface DashboardAuthOptions {
205
+ /** REQUIRED HMAC-SHA256 signing key (32+ bytes recommended). Missing/empty => boot error (fail closed). */
206
+ secret: string;
207
+ /** Cookie TTL (duration string `'<n><ms|s|m|h|d>'`, e.g. `'30m'`, `'7d'`). Default `'8h'`. */
208
+ ttl?: string;
209
+ /**
210
+ * Validates submitted username/password. Return the session user to grant access, or `null` to
211
+ * deny — the login endpoint responds identically (generic failure) for an unknown user and a
212
+ * wrong password, so it never reveals which one was wrong.
213
+ */
214
+ login: LoginHook;
215
+ }
216
+ /** Resolved, validated `dashboardAuth` config used by the guards/controller. */
217
+ interface ResolvedDashboardAuth {
218
+ secret: string;
219
+ ttlMs: number;
220
+ login: LoginHook;
221
+ }
222
+ /**
223
+ * Validate + resolve `dashboardAuth`. Returns `null` when unconfigured (behavior unchanged — the
224
+ * console stays open, front it with your own `guards`). Throws at boot (fail closed) when
225
+ * configured but missing a secret or a `login` hook — the host learns immediately rather than
226
+ * shipping an open or un-mintable console.
227
+ */
228
+ declare function resolveDashboardAuth(options: DashboardAuthOptions | undefined): ResolvedDashboardAuth | null;
229
+
230
+ /** The slice of the platform request this controller reads — structural, no Express/Fastify dependency. */
231
+ interface AuthPageRequest {
232
+ headers: Record<string, string | string[] | undefined>;
233
+ }
234
+ /** The slice of the platform response this controller writes (passthrough mode — Nest still sends). */
235
+ interface AuthPageResponse {
236
+ status(code: number): unknown;
237
+ setHeader(name: string, value: string): unknown;
238
+ }
239
+ interface LoginFormBody {
240
+ username?: unknown;
241
+ password?: unknown;
242
+ returnTo?: unknown;
243
+ }
244
+ /**
245
+ * Mounted directly on `AgentDashboardModule` at `<basePath>/auth/*` — a SEPARATE controller from
246
+ * `AgentUiController`/`AgentApiController` so it carries neither `DashboardAuthPageGuard`,
247
+ * `DashboardAuthGuard`, nor any host `guards`: these routes MINT (and clear) the session cookie the
248
+ * built-in gates check for, so they can never be made to require the very auth they grant. Mirrors
249
+ * `@dudousxd/nestjs-telescope`'s own auth controller staying outside its `stampGuards` call.
250
+ *
251
+ * A dependency-free, server-rendered login form (GET/POST) rather than a JSON API + client-side
252
+ * screen — the bundled React SPA in this package is a built Vite artifact with no auth-aware UI of
253
+ * its own, so gating the actual page navigation needs a self-contained flow that doesn't touch it.
254
+ *
255
+ * 404s every route when `dashboardAuth` is unconfigured (`DASHBOARD_AUTH` resolves to `null`) — no
256
+ * dangling login page when the feature is off.
257
+ */
258
+ declare class AgentDashboardAuthController {
259
+ private readonly auth;
260
+ private readonly basePath;
261
+ private readonly logger;
262
+ /** Warn once (not per request) so a broken hook can't be used to flood the logs. */
263
+ private warnedLoginHookThrow;
264
+ constructor(auth: ResolvedDashboardAuth | null, basePath: string);
265
+ loginPage(req: AuthPageRequest, res: AuthPageResponse, returnToQuery?: string, error?: string): string;
266
+ login(body: LoginFormBody, req: unknown, res: AuthPageResponse): Promise<string>;
267
+ logout(req: unknown, res: AuthPageResponse): string;
268
+ /** Uniform failure: same redirect (generic error flag) whether the user is unknown or the password is wrong. */
269
+ private redirectToLoginError;
270
+ private currentSession;
271
+ /**
272
+ * Run the host's `login` hook defensively: a throw (sync or async) is treated as a denial
273
+ * (`null`) and warn-logged ONCE, so a buggy/unreachable hook never 500s the endpoint into a
274
+ * stack-trace leak nor floods the logs.
275
+ */
276
+ private runLoginHook;
277
+ private requireAuth;
278
+ }
279
+
186
280
  /** Same shape {@link AgentDashboardModule.forRoot} accepts — kept local so this stays a pure, DI-free helper. */
187
281
  interface AgentDashboardMountPathsOptions {
188
282
  basePath?: string;
@@ -234,10 +328,9 @@ interface AgentDashboardOptions<TReq = unknown> {
234
328
  apiBasePath?: string;
235
329
  /**
236
330
  * Guard classes fronting BOTH dashboard controllers (the SPA at `basePath` and its JSON API at
237
- * `apiBasePath`). Stamped onto each controller via `@nestjs/common`'s own `@UseGuards` metadata key
238
- * — REPLACE semantics, so a second `forRoot(...)` call overwrites (not appends to) whatever a prior
239
- * call stamped, same as re-applying `@UseGuards` by hand. Omit to leave the routes unguarded (the
240
- * host fronts them another way, e.g. a global guard or reverse-proxy auth).
331
+ * `apiBasePath`) — APPENDED onto the built-in `dashboardAuth` gate (a no-op unless configured;
332
+ * see {@link dashboardAuth}), so a request must pass BOTH. Omit to leave the routes gated by
333
+ * `dashboardAuth` alone (or fully open when that's unset too).
241
334
  *
242
335
  * A guard's own DEPENDENCIES resolve from this module's `imports` (see {@link imports}) — the
243
336
  * dashboard module has no application context of its own to pull them from otherwise.
@@ -261,6 +354,36 @@ interface AgentDashboardOptions<TReq = unknown> {
261
354
  * override wins outright, with no resolver fallback.
262
355
  */
263
356
  approvalActorRef?: (req: TReq) => string | undefined;
357
+ /**
358
+ * Gate the console (SPA + API) behind a BUILT-IN cookie-session login screen — the simplest way
359
+ * to protect `basePath` when the host has no ready-made guard (e.g. header-only auth that a
360
+ * browser navigation can't attach). Omit to leave the console open, or fronted only by
361
+ * {@link guards}. When set, an unauthenticated page navigation is redirected (302) to
362
+ * `<basePath>/auth/login`; an unauthenticated API call gets a `401`. Composes with `guards` —
363
+ * BOTH gates must pass. See `DashboardAuthOptions` for the `secret`/`ttl`/`login` shape.
364
+ */
365
+ dashboardAuth?: DashboardAuthOptions;
366
+ }
367
+ /**
368
+ * Async variant of {@link AgentDashboardOptions}, for a `login` hook that needs injected services
369
+ * (e.g. an EntityManager to check credentials against a DB). Everything else stays static/sync —
370
+ * `basePath`/`apiBasePath`/`guards` are needed at module-build time (the same constraint
371
+ * `@dudousxd/nestjs-telescope`'s and `@dudousxd/nestjs-media`'s own `forRootAsync`s document), so
372
+ * only the auth piece itself is resolved through DI.
373
+ */
374
+ interface AgentDashboardAsyncOptions<TReq = unknown> {
375
+ basePath?: string;
376
+ apiBasePath?: string;
377
+ guards?: Type<CanActivate>[];
378
+ imports?: DynamicModule['imports'];
379
+ approvalActorRef?: (req: TReq) => string | undefined;
380
+ /** Providers injected into `useDashboardAuth`, in order. Shared with a guard class's own deps via `imports`. */
381
+ inject?: Array<InjectionToken | OptionalFactoryDependency>;
382
+ /**
383
+ * Build the `dashboardAuth` config from injected deps, or return `undefined` to leave the
384
+ * console open (or fronted only by `guards`). See {@link AgentDashboardOptions.dashboardAuth}.
385
+ */
386
+ useDashboardAuth: (...deps: any[]) => DashboardAuthOptions | undefined | Promise<DashboardAuthOptions | undefined>;
264
387
  }
265
388
  /**
266
389
  * Holds the JSON API + SSE controller and its read service, mounted on its own path by `forRoot`.
@@ -275,16 +398,19 @@ declare class AgentApiModule {
275
398
  imports?: DynamicModule['imports'];
276
399
  guards?: Type<CanActivate>[];
277
400
  approvalActorRef?: (req: TReq) => string | undefined;
401
+ /** Provider for `DASHBOARD_AUTH` — a `useValue` (`forRoot`) or a `useFactory` (`forRootAsync`). */
402
+ dashboardAuth: Provider;
278
403
  }): DynamicModule;
279
404
  }
280
405
  /**
281
406
  * Mounts the AI-gateway governance console: the bundled React SPA at `basePath` and its JSON + SSE
282
- * API at `apiBasePath` (default `<basePath>/api`).
407
+ * API at `apiBasePath` (default `<basePath>/api`), plus the built-in login screen's routes at
408
+ * `<basePath>/auth/*` when {@link AgentDashboardOptions.dashboardAuth} is set.
283
409
  *
284
410
  * Import via `AgentDashboardModule.forRoot(...)` alongside your `@dudousxd/nestjs-agent` module
285
411
  * (global), which must provide `AGENT_GOVERNANCE_QUERIES` (bound by a store adapter). Front the
286
412
  * routes with the first-class `guards` option (plus `imports` for the guards' own dependencies) —
287
- * see {@link AgentDashboardOptions.guards}.
413
+ * see {@link AgentDashboardOptions.guards} — and/or the built-in `dashboardAuth` cookie login.
288
414
  *
289
415
  * Inertia hosts: the console is a full-page app, not an Inertia page. An in-app `<Link>` visit to
290
416
  * `basePath` (an XHR carrying `X-Inertia`) is bounced with the protocol's own external-redirect
@@ -294,6 +420,7 @@ declare class AgentApiModule {
294
420
  */
295
421
  declare class AgentDashboardModule {
296
422
  static forRoot<TReq = unknown>(options?: AgentDashboardOptions<TReq>): DynamicModule;
423
+ static forRootAsync<TReq = unknown>(options: AgentDashboardAsyncOptions<TReq>): DynamicModule;
297
424
  }
298
425
 
299
426
  /**
@@ -314,6 +441,12 @@ interface UiPageResponse {
314
441
  * Serves the bundled AI-gateway console SPA at the configured base (+ hashed assets at
315
442
  * `<base>/assets`). The path comes from `RouterModule` (set by
316
443
  * {@link AgentDashboardModule.forRoot}({ basePath })), so the controller routes are relative.
444
+ *
445
+ * `@UseGuards(DashboardAuthPageGuard)` is a NO-OP unless the host configured `dashboardAuth` — see
446
+ * that guard's own doc. Host `guards` (see {@link AgentDashboardOptions.guards}) are APPENDED onto
447
+ * this baseline by `stampGuards` (`./guards.js`), never replacing it. `@UseFilters` turns the
448
+ * guard's redirect-on-deny into an actual 302 response; it is a no-op when the guard never throws
449
+ * (i.e. `dashboardAuth` unconfigured).
317
450
  */
318
451
  declare class AgentUiController {
319
452
  private readonly basePath;
@@ -388,5 +521,13 @@ declare const DASHBOARD_API_PATH: unique symbol;
388
521
  * `@dudousxd/nestjs-agent`).
389
522
  */
390
523
  declare const DASHBOARD_APPROVAL_ACTOR_REF: unique symbol;
524
+ /**
525
+ * DI token carrying the resolved `dashboardAuth` config (`ResolvedDashboardAuth | null`) — `null`
526
+ * when the host didn't configure it (the built-in guards are then a no-op, today's behavior
527
+ * byte-for-byte). Bound once by `AgentApiModule.register` and re-exported so `AgentUiController`
528
+ * and `AgentDashboardAuthController` (hosted directly by `AgentDashboardModule`, which imports
529
+ * `AgentApiModule`) resolve the SAME instance rather than re-running a host's async factory twice.
530
+ */
531
+ declare const DASHBOARD_AUTH: unique symbol;
391
532
 
392
- export { AGENT_ACTOR_DIRECTORY, AGENT_ACTOR_RESOLVER, AGENT_APPROVAL_PORT, AGENT_GOVERNANCE_QUERIES, AGENT_PRICING_STORE, type ActorSpendRowWithLabel, AgentApiController, AgentApiModule, AgentDashboardModule, type AgentDashboardMountPathsOptions, type AgentDashboardOptions, AgentUiController, DASHBOARD_API_PATH, DASHBOARD_APPROVAL_ACTOR_REF, DASHBOARD_BASE_PATH, DashboardService, type LiveAgentEvent, type ReliabilityOverview, type SpendOverview, type ThreadActivityRowWithLabel, type ThreadSpendRowWithLabel, type WithActorLabel, agentDashboardMountPaths, parsePriceInput };
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 };