@dudousxd/nestjs-durable-dashboard 0.56.0 → 0.58.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,4 +1,4 @@
1
- import { HttpException, ExecutionContext, Type, ExceptionFilter, ArgumentsHost, CanActivate, Logger, DynamicModule, StreamableFile } from '@nestjs/common';
1
+ import { HttpException, ExecutionContext, Type, ExceptionFilter, ArgumentsHost, CanActivate, Logger, DynamicModule, StreamableFile, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
2
2
  import { HttpAdapterHost, ModuleRef } from '@nestjs/core';
3
3
  import * as _dudousxd_nestjs_durable_core from '@dudousxd/nestjs-durable-core';
4
4
  import { RunGateway, StateStore, WorkflowEngine, ScheduleListQuery, ScheduleDescription, RunResult, RunQuery, RunListItem, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, DurableTopology, GroupHealth, RunDetailOptions, RunDetail, FanPageQuery, FanPage, RepublishLifecycleResult, WorkflowLifecycleLinks, UpdateResult, EngineEvent, AttributeFilter } from '@dudousxd/nestjs-durable-core';
@@ -8,8 +8,14 @@ import { Observable } from 'rxjs';
8
8
  import * as _dudousxd_nestjs_filter from '@dudousxd/nestjs-filter';
9
9
  import { FilterRunner } from '@dudousxd/nestjs-filter';
10
10
 
11
+ /**
12
+ * What an `authorize` check answers. An allow may name who it let in (`caller`, for example
13
+ * `'user:42'`): the dev MCP endpoint logs it as the caller of a developer tool. Never put a secret
14
+ * in it.
15
+ */
11
16
  type ConsoleAuthorizeResult = boolean | {
12
17
  allowed: true;
18
+ caller?: string;
13
19
  } | {
14
20
  allowed: false;
15
21
  reason?: 'unauthenticated' | 'forbidden';
@@ -31,6 +37,7 @@ type ConsoleAuthorize = ((context: ExecutionContext) => ConsoleAuthorizeResult |
31
37
  }>) | ConsolePolicyCheck | string;
32
38
  type ConsoleDecision = {
33
39
  allowed: true;
40
+ caller?: string;
34
41
  } | {
35
42
  allowed: false;
36
43
  reason: 'unauthenticated' | 'forbidden';
@@ -65,11 +72,23 @@ declare function resolveConsoleAuthorize(authorize: ConsoleAuthorize | undefined
65
72
  /**
66
73
  * Allows a request carrying `Authorization: Bearer <token>` (compared timing-safe); anything else
67
74
  * is `unauthenticated`. Header only: no query string, no custom header. Throws when `token` is
68
- * empty, so a missing env var fails at config time instead of opening the console.
75
+ * empty, so a missing env var fails at config time instead of opening the console. The allow names
76
+ * its caller `token:<first 12 hex of sha256(token)>`, never the token.
69
77
  */
70
78
  declare function authorizeByToken(options: {
71
79
  token: string | undefined;
72
80
  }): ConsoleAuthorize;
81
+ /**
82
+ * One `authorize` from several: allowed when any form allows (checked in order, stopping at the
83
+ * first that does, whose `caller` it keeps); otherwise `unauthenticated` when any form said so
84
+ * (keeping the first `redirectTo`), else `forbidden`. Each form is any `authorize` value: a
85
+ * function, a class, a lazy import, `{ policy, ability }` or a gate name.
86
+ *
87
+ * ```ts
88
+ * authorize: anyAuthorize(authorizeByToken({ token: process.env.CONSOLE_TOKEN }), AdminSessionAuthorizer)
89
+ * ```
90
+ */
91
+ declare function anyAuthorize(...forms: ConsoleAuthorize[]): ConsoleAuthorizerClass;
73
92
  /** DI token of the resolved {@link ConsoleDecide} the guard runs. */
74
93
  declare const CONSOLE_AUTHORIZE_DECIDE: unique symbol;
75
94
  /** Marks a controller as serving pages: a denied `GET` that accepts HTML gets a page, not JSON. */
@@ -88,6 +107,12 @@ declare class ConsoleAccessDenied extends HttpException {
88
107
  allowed: false;
89
108
  }>, page: boolean);
90
109
  }
110
+ /**
111
+ * Marks a console route that is not part of the console: `authorize` does not apply to it. Used on
112
+ * the `ctx.webhook()` callback, which third parties call with the token as the secret.
113
+ */
114
+ declare const CONSOLE_PUBLIC_METADATA = "@dudousxd/nestjs-durable-dashboard:console-public";
115
+ declare const ConsolePublic: () => MethodDecorator;
91
116
  declare class ConsoleAuthorizeGuard implements CanActivate {
92
117
  private readonly decide;
93
118
  constructor(decide: ConsoleDecide);
@@ -235,6 +260,22 @@ declare class DashboardService {
235
260
  * rows behind or signal non-webhook tokens. Control-plane-only.
236
261
  */
237
262
  deliverWebhook(token: string, body: unknown): Promise<RunResult | null>;
263
+ /**
264
+ * What this deployment can serve: `controlPlane` (signals, run events, updates, metrics) and
265
+ * `schedules` (persisted schedules). Both are false on a store-less tenant.
266
+ */
267
+ capabilities(): {
268
+ controlPlane: boolean;
269
+ schedules: boolean;
270
+ };
271
+ /** Whether `runId` names a run the caller may see (tenancy applies). */
272
+ runExists(runId: string): Promise<boolean>;
273
+ /**
274
+ * Deliver `payload` to the run waiting on `token` (`engine.signal`). Null when no run the caller
275
+ * may see waits on it yet: the engine then buffers the payload for the run that waits next.
276
+ * Control-plane-only.
277
+ */
278
+ signal(token: string, payload: unknown): Promise<RunResult | null>;
238
279
  /** Side-effect-free read of a value a run published via `ctx.setEvent` (a live query). Control-plane-only. */
239
280
  getEvent(runId: string, key: string): Promise<unknown>;
240
281
  /** Deliver a validated `ctx.onUpdate` to a run; the validator may reject it (see UpdateResult). Control-plane-only. */
@@ -422,9 +463,9 @@ declare class DurableApiControllerBase {
422
463
  }>;
423
464
  /**
424
465
  * Public callback endpoint for `ctx.webhook()`: a third party POSTs here (the url handed to it),
425
- * and the body resumes the waiting run. NOTE: this is reachable by external systems — protect it
426
- * by treating the token as a secret (it embeds runId:seq) and/or fronting it with signature
427
- * verification in your own middleware.
466
+ * and the body resumes the waiting run. Not a console route: the console's `authorize` does not
467
+ * apply, the token is the secret. The app's `decorators` still do. Treat the token as a secret
468
+ * and/or front it with signature verification in your own middleware.
428
469
  */
429
470
  webhook(token: string, body: unknown): Promise<_dudousxd_nestjs_durable_core.RunResult>;
430
471
  /** Live query: the latest value a run published for `key` via `ctx.setEvent` (no side effect). */
@@ -443,7 +484,9 @@ declare class DurableApiController extends DurableApiControllerBase {
443
484
 
444
485
  interface DurableDashboardOptions {
445
486
  /**
446
- * Who may use the console (the SPA, its JSON API, the run stream and the webhook route). One of:
487
+ * Who may use the console (the SPA, its JSON API and the run stream). Not the `ctx.webhook()`
488
+ * callback (`POST <apiBasePath>/webhooks/:token`), which third parties call with the token as the
489
+ * secret; {@link decorators} still apply to it. One of:
447
490
  *
448
491
  * - a function `(context) => boolean | { allowed } | { redirectTo }` (may be async);
449
492
  * - an `@Injectable()` class implementing {@link ConsoleAuthorizer} (constructor injection works;
@@ -525,6 +568,12 @@ interface DurableDashboardOptions {
525
568
  * strings that keep their meaning (where the console lives) whether or not this module mounts it.
526
569
  */
527
570
  routes?: boolean;
571
+ /**
572
+ * Register the console's reads and run/schedule actions as developer tools on the agent MCP
573
+ * server's dev endpoint (`/mcp/aviary`), gated by this console's `authorize`. Default: on when
574
+ * the console's routes are mounted, off with `routes: false`.
575
+ */
576
+ devTools?: boolean;
528
577
  }
529
578
  /**
530
579
  * Holds the JSON API controller + its read service, mounted on its own path by `forRoot`. Its own
@@ -540,6 +589,8 @@ declare class DurableApiModule {
540
589
  decorators?: ClassDecorator[];
541
590
  /** See {@link DurableDashboardOptions.authorize}. */
542
591
  authorize?: ConsoleAuthorize;
592
+ /** See {@link DurableDashboardOptions.devTools}. Default: on when the routes are mounted. */
593
+ devTools?: boolean;
543
594
  }): DynamicModule;
544
595
  }
545
596
  /**
@@ -557,9 +608,12 @@ declare class DurableDashboardModule {
557
608
  }
558
609
 
559
610
  /** DI token carrying the UI mount base (e.g. `/durable`). */
560
- declare const DASHBOARD_BASE_PATH: unique symbol;
611
+ declare const DASHBOARD_BASE_PATH: symbol;
561
612
  /** DI token carrying the JSON API base the SPA fetches from (e.g. `/api/durable`). */
562
- declare const DASHBOARD_API_PATH: unique symbol;
613
+ declare const DASHBOARD_API_PATH: symbol;
614
+
615
+ /** The id of the page's JSON config block. */
616
+ declare const DURABLE_DASHBOARD_CONFIG_ID = "durable-dashboard-config";
563
617
  /**
564
618
  * Serves the bundled control-plane SPA at the configured base (+ hashed assets at `<base>/assets`).
565
619
  * The controller routes are relative: the mounted {@link DurableUiController} gets its path from
@@ -577,6 +631,11 @@ declare class DurableUiControllerBase {
577
631
  private readonly dir;
578
632
  constructor(basePath: string, apiBasePath: string);
579
633
  index(): string;
634
+ /**
635
+ * The page's JSON config block (`durable-dashboard-config`, CSP-safe: data, not script). It
636
+ * carries `switcher` only when another Aviary console is registered.
637
+ */
638
+ private configBlock;
580
639
  asset(file: string): StreamableFile;
581
640
  }
582
641
  /**
@@ -588,4 +647,165 @@ declare class DurableUiControllerBase {
588
647
  declare class DurableUiController extends DurableUiControllerBase {
589
648
  }
590
649
 
591
- export { CONSOLE_AUTHORIZE_DECIDE, CONSOLE_AUTHORIZE_MISSING_WARNING, CONSOLE_GATE, CONSOLE_PAGE_METADATA, ConsoleAccessDenied, type ConsoleAuthorize, ConsoleAuthorizeFilter, ConsoleAuthorizeGuard, type ConsoleAuthorizeResult, type ConsoleAuthorizer, type ConsoleAuthorizerClass, type ConsoleDecide, type ConsoleDecision, type ConsoleGate, ConsolePage, type ConsolePolicyCheck, DASHBOARD_API_PATH, DASHBOARD_BASE_PATH, DashboardService, DurableApiController, DurableApiControllerBase, DurableApiModule, DurableDashboardModule, type DurableDashboardOptions, DurableUiController, DurableUiControllerBase, UseConsoleAuthorize, type WorkflowLifecyclePage, authorizeByToken, normalizeConsoleResult, resolveConsoleAuthorize };
650
+ /** The process-wide slot `@dudousxd/nestjs-agent-mcp-server` serves on its dev MCP endpoint. */
651
+ declare const DEV_TOOLS_SLOT: symbol;
652
+ /** One property of a {@link DevToolInputSchema}. */
653
+ interface DevToolInputProperty {
654
+ type: 'string' | 'number' | 'integer' | 'boolean' | 'array';
655
+ description?: string;
656
+ enum?: readonly string[];
657
+ /** Arrays hold strings only. */
658
+ items?: {
659
+ type: 'string';
660
+ };
661
+ minimum?: number;
662
+ maximum?: number;
663
+ }
664
+ /** An object of scalar / enum / array-of-string properties. */
665
+ interface DevToolInputSchema {
666
+ type: 'object';
667
+ properties: Record<string, DevToolInputProperty>;
668
+ required?: string[];
669
+ }
670
+ /** What the slot's `authorize` answers: the console's resolved decision. */
671
+ type DevToolsDecision = {
672
+ allowed: true;
673
+ caller?: string;
674
+ } | {
675
+ allowed: false;
676
+ reason: 'unauthenticated' | 'forbidden';
677
+ redirectTo?: string;
678
+ };
679
+ /** What a tool's `run` receives besides its input. */
680
+ interface DevToolCall {
681
+ context: ExecutionContext;
682
+ caller: {
683
+ id: string;
684
+ via: 'token' | 'session' | 'open';
685
+ };
686
+ limits: {
687
+ maxRows: number;
688
+ maxBytes: number;
689
+ };
690
+ }
691
+ /** One developer tool, as the dev-tools slot takes it. */
692
+ interface DurableDevTool {
693
+ /** `durable_<verb>_<noun>`. */
694
+ name: string;
695
+ /** `read` tools are listed by default; `action` tools only when the app opts in. */
696
+ kind: 'read' | 'action';
697
+ description: string;
698
+ input: DevToolInputSchema;
699
+ /** Set on actions that cancel. */
700
+ destructive?: boolean;
701
+ /** Capability, not access: hides a tool this deployment cannot run. */
702
+ available?(): boolean | Promise<boolean>;
703
+ run(input: Record<string, unknown>, call?: DevToolCall): Promise<unknown>;
704
+ }
705
+ /** Runs `durable_retry_run` / `durable_cancel_run` act on at most per call. */
706
+ declare const MAX_RUN_IDS = 100;
707
+ /**
708
+ * The durable developer tools (lib `durable`): the console's reads and its run and schedule
709
+ * actions, through the same {@link DashboardService} methods its API serves, so tenancy and the
710
+ * app's redaction apply. Signals, run events and schedules are available only on the control plane.
711
+ */
712
+ declare function durableDevTools(service: DashboardService): DurableDevTool[];
713
+ /**
714
+ * Registers the durable tools into the dev-tools slot, gated by the console's own resolved
715
+ * `authorize`, when the console is mounted (or `devTools: true`). Removed again on shutdown.
716
+ */
717
+ declare class DurableDevToolsRegistration implements OnModuleInit, OnModuleDestroy {
718
+ #private;
719
+ private readonly enabled;
720
+ private readonly service;
721
+ private readonly decide;
722
+ constructor(enabled: boolean, service: DashboardService, decide: ConsoleDecide);
723
+ onModuleInit(): void;
724
+ onModuleDestroy(): void;
725
+ }
726
+
727
+ /** The process-wide slot every Aviary console registers into (created by whichever boots first). */
728
+ declare const AVIARY_CONSOLES_SLOT: symbol;
729
+ /** The icon a console shows in the switcher. An unknown value renders as `generic`. */
730
+ type AviaryConsoleIcon = 'workflow' | 'telescope' | 'media' | 'payments' | 'agent' | 'identity' | 'generic';
731
+ /** What a console's `authorize` answers; only `allowed: true` lists it. */
732
+ interface AviaryConsoleDecision {
733
+ allowed: boolean;
734
+ reason?: string;
735
+ redirectTo?: string;
736
+ }
737
+ /** One console the switcher can list. */
738
+ interface AviaryConsoleEntry {
739
+ /** `/^[a-z][a-z0-9-]*$/`: `'durable'`, `'telescope'`, `'agent'`, `'media'`, … */
740
+ id: string;
741
+ /** Product title: `'Workflows'`, `'Telescope'`, `'Agents'`, `'Media'`. */
742
+ title: string;
743
+ icon: AviaryConsoleIcon;
744
+ /** Same-origin absolute path of the console's page (`/durable`). */
745
+ path: string;
746
+ /** Sort key: durable 10, telescope 20, agent 30, media 50. */
747
+ order: number;
748
+ /** The console's resolved gate (what its own routes run). Never throws. */
749
+ authorize(context: ExecutionContext): Promise<AviaryConsoleDecision>;
750
+ }
751
+ /** The value at {@link AVIARY_CONSOLES_SLOT}. Data only, so every console's copy is identical. */
752
+ interface AviaryConsoleRegistry {
753
+ readonly version: 1;
754
+ readonly entries: AviaryConsoleEntry[];
755
+ }
756
+ /** One console as `GET <api>/consoles` returns it. */
757
+ interface VisibleConsole {
758
+ id: string;
759
+ title: string;
760
+ icon: AviaryConsoleIcon;
761
+ href: string;
762
+ }
763
+ /** The answer of `GET <api>/consoles`. */
764
+ interface VisibleConsoles {
765
+ version: 1;
766
+ current: string;
767
+ consoles: VisibleConsole[];
768
+ }
769
+ /** How long another console's `authorize` may take before it is left out. */
770
+ declare const CONSOLE_PROBE_TIMEOUT_MS: number;
771
+ /**
772
+ * Adds a console to the process's switcher registry, creating it when absent. Returns a function
773
+ * that removes the entry again, or `undefined` when it was not added (another registry version, or
774
+ * the id is taken: logged as a warning). A bad `id` or `path` throws.
775
+ */
776
+ declare function registerAviaryConsole(entry: AviaryConsoleEntry, logger?: {
777
+ warn(message: string): void;
778
+ }): (() => void) | undefined;
779
+ /**
780
+ * The consoles other than `selfId` the viewer may open, by `order` then `id`. Every other entry's
781
+ * `authorize` runs in parallel, each given `timeoutMs`; a timeout, a rejection or any denial hides
782
+ * it. No reason is ever returned.
783
+ */
784
+ declare function listVisibleConsoles(registry: AviaryConsoleRegistry | undefined, selfId: string, context: ExecutionContext, timeoutMs?: number): Promise<VisibleConsoles>;
785
+ /** The durable console's id in the registry. */
786
+ declare const DURABLE_CONSOLE_ID: string;
787
+ /**
788
+ * Registers the durable console into the switcher registry (the console's page path, gated by its
789
+ * resolved `authorize`). Provided only when the console's routes are mounted; removed on shutdown.
790
+ */
791
+ declare class DurableConsoleRegistration implements OnModuleInit, OnModuleDestroy {
792
+ #private;
793
+ private readonly basePath;
794
+ private readonly decide;
795
+ constructor(basePath: string, decide: ConsoleDecide);
796
+ onModuleInit(): void;
797
+ onModuleDestroy(): void;
798
+ }
799
+ /** What the route hands on to the other consoles' `authorize`: this request's `ExecutionContext`. */
800
+ interface CurrentContext {
801
+ context: ExecutionContext;
802
+ }
803
+ /**
804
+ * `GET <api>/consoles`: the other Aviary consoles this viewer may open. Behind the console's own
805
+ * access check, like every API route, so only someone who may open this console learns the list.
806
+ */
807
+ declare class DurableConsolesController {
808
+ consoles(current: CurrentContext): Promise<VisibleConsoles>;
809
+ }
810
+
811
+ export { AVIARY_CONSOLES_SLOT, type AviaryConsoleDecision, type AviaryConsoleEntry, type AviaryConsoleIcon, type AviaryConsoleRegistry, CONSOLE_AUTHORIZE_DECIDE, CONSOLE_AUTHORIZE_MISSING_WARNING, CONSOLE_GATE, CONSOLE_PAGE_METADATA, CONSOLE_PROBE_TIMEOUT_MS, CONSOLE_PUBLIC_METADATA, ConsoleAccessDenied, type ConsoleAuthorize, ConsoleAuthorizeFilter, ConsoleAuthorizeGuard, type ConsoleAuthorizeResult, type ConsoleAuthorizer, type ConsoleAuthorizerClass, type ConsoleDecide, type ConsoleDecision, type ConsoleGate, ConsolePage, type ConsolePolicyCheck, ConsolePublic, DASHBOARD_API_PATH, DASHBOARD_BASE_PATH, DEV_TOOLS_SLOT, DURABLE_CONSOLE_ID, DURABLE_DASHBOARD_CONFIG_ID, DashboardService, type DevToolCall, type DevToolInputProperty, type DevToolInputSchema, type DevToolsDecision, DurableApiController, DurableApiControllerBase, DurableApiModule, DurableConsoleRegistration, DurableConsolesController, DurableDashboardModule, type DurableDashboardOptions, type DurableDevTool, DurableDevToolsRegistration, DurableUiController, DurableUiControllerBase, MAX_RUN_IDS, UseConsoleAuthorize, type VisibleConsole, type VisibleConsoles, type WorkflowLifecyclePage, anyAuthorize, authorizeByToken, durableDevTools, listVisibleConsoles, normalizeConsoleResult, registerAviaryConsole, resolveConsoleAuthorize };
@@ -1,4 +1,4 @@
1
- import { HttpException, ExecutionContext, Type, ExceptionFilter, ArgumentsHost, CanActivate, Logger, DynamicModule, StreamableFile } from '@nestjs/common';
1
+ import { HttpException, ExecutionContext, Type, ExceptionFilter, ArgumentsHost, CanActivate, Logger, DynamicModule, StreamableFile, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
2
2
  import { HttpAdapterHost, ModuleRef } from '@nestjs/core';
3
3
  import * as _dudousxd_nestjs_durable_core from '@dudousxd/nestjs-durable-core';
4
4
  import { RunGateway, StateStore, WorkflowEngine, ScheduleListQuery, ScheduleDescription, RunResult, RunQuery, RunListItem, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, DurableTopology, GroupHealth, RunDetailOptions, RunDetail, FanPageQuery, FanPage, RepublishLifecycleResult, WorkflowLifecycleLinks, UpdateResult, EngineEvent, AttributeFilter } from '@dudousxd/nestjs-durable-core';
@@ -8,8 +8,14 @@ import { Observable } from 'rxjs';
8
8
  import * as _dudousxd_nestjs_filter from '@dudousxd/nestjs-filter';
9
9
  import { FilterRunner } from '@dudousxd/nestjs-filter';
10
10
 
11
+ /**
12
+ * What an `authorize` check answers. An allow may name who it let in (`caller`, for example
13
+ * `'user:42'`): the dev MCP endpoint logs it as the caller of a developer tool. Never put a secret
14
+ * in it.
15
+ */
11
16
  type ConsoleAuthorizeResult = boolean | {
12
17
  allowed: true;
18
+ caller?: string;
13
19
  } | {
14
20
  allowed: false;
15
21
  reason?: 'unauthenticated' | 'forbidden';
@@ -31,6 +37,7 @@ type ConsoleAuthorize = ((context: ExecutionContext) => ConsoleAuthorizeResult |
31
37
  }>) | ConsolePolicyCheck | string;
32
38
  type ConsoleDecision = {
33
39
  allowed: true;
40
+ caller?: string;
34
41
  } | {
35
42
  allowed: false;
36
43
  reason: 'unauthenticated' | 'forbidden';
@@ -65,11 +72,23 @@ declare function resolveConsoleAuthorize(authorize: ConsoleAuthorize | undefined
65
72
  /**
66
73
  * Allows a request carrying `Authorization: Bearer <token>` (compared timing-safe); anything else
67
74
  * is `unauthenticated`. Header only: no query string, no custom header. Throws when `token` is
68
- * empty, so a missing env var fails at config time instead of opening the console.
75
+ * empty, so a missing env var fails at config time instead of opening the console. The allow names
76
+ * its caller `token:<first 12 hex of sha256(token)>`, never the token.
69
77
  */
70
78
  declare function authorizeByToken(options: {
71
79
  token: string | undefined;
72
80
  }): ConsoleAuthorize;
81
+ /**
82
+ * One `authorize` from several: allowed when any form allows (checked in order, stopping at the
83
+ * first that does, whose `caller` it keeps); otherwise `unauthenticated` when any form said so
84
+ * (keeping the first `redirectTo`), else `forbidden`. Each form is any `authorize` value: a
85
+ * function, a class, a lazy import, `{ policy, ability }` or a gate name.
86
+ *
87
+ * ```ts
88
+ * authorize: anyAuthorize(authorizeByToken({ token: process.env.CONSOLE_TOKEN }), AdminSessionAuthorizer)
89
+ * ```
90
+ */
91
+ declare function anyAuthorize(...forms: ConsoleAuthorize[]): ConsoleAuthorizerClass;
73
92
  /** DI token of the resolved {@link ConsoleDecide} the guard runs. */
74
93
  declare const CONSOLE_AUTHORIZE_DECIDE: unique symbol;
75
94
  /** Marks a controller as serving pages: a denied `GET` that accepts HTML gets a page, not JSON. */
@@ -88,6 +107,12 @@ declare class ConsoleAccessDenied extends HttpException {
88
107
  allowed: false;
89
108
  }>, page: boolean);
90
109
  }
110
+ /**
111
+ * Marks a console route that is not part of the console: `authorize` does not apply to it. Used on
112
+ * the `ctx.webhook()` callback, which third parties call with the token as the secret.
113
+ */
114
+ declare const CONSOLE_PUBLIC_METADATA = "@dudousxd/nestjs-durable-dashboard:console-public";
115
+ declare const ConsolePublic: () => MethodDecorator;
91
116
  declare class ConsoleAuthorizeGuard implements CanActivate {
92
117
  private readonly decide;
93
118
  constructor(decide: ConsoleDecide);
@@ -235,6 +260,22 @@ declare class DashboardService {
235
260
  * rows behind or signal non-webhook tokens. Control-plane-only.
236
261
  */
237
262
  deliverWebhook(token: string, body: unknown): Promise<RunResult | null>;
263
+ /**
264
+ * What this deployment can serve: `controlPlane` (signals, run events, updates, metrics) and
265
+ * `schedules` (persisted schedules). Both are false on a store-less tenant.
266
+ */
267
+ capabilities(): {
268
+ controlPlane: boolean;
269
+ schedules: boolean;
270
+ };
271
+ /** Whether `runId` names a run the caller may see (tenancy applies). */
272
+ runExists(runId: string): Promise<boolean>;
273
+ /**
274
+ * Deliver `payload` to the run waiting on `token` (`engine.signal`). Null when no run the caller
275
+ * may see waits on it yet: the engine then buffers the payload for the run that waits next.
276
+ * Control-plane-only.
277
+ */
278
+ signal(token: string, payload: unknown): Promise<RunResult | null>;
238
279
  /** Side-effect-free read of a value a run published via `ctx.setEvent` (a live query). Control-plane-only. */
239
280
  getEvent(runId: string, key: string): Promise<unknown>;
240
281
  /** Deliver a validated `ctx.onUpdate` to a run; the validator may reject it (see UpdateResult). Control-plane-only. */
@@ -422,9 +463,9 @@ declare class DurableApiControllerBase {
422
463
  }>;
423
464
  /**
424
465
  * Public callback endpoint for `ctx.webhook()`: a third party POSTs here (the url handed to it),
425
- * and the body resumes the waiting run. NOTE: this is reachable by external systems — protect it
426
- * by treating the token as a secret (it embeds runId:seq) and/or fronting it with signature
427
- * verification in your own middleware.
466
+ * and the body resumes the waiting run. Not a console route: the console's `authorize` does not
467
+ * apply, the token is the secret. The app's `decorators` still do. Treat the token as a secret
468
+ * and/or front it with signature verification in your own middleware.
428
469
  */
429
470
  webhook(token: string, body: unknown): Promise<_dudousxd_nestjs_durable_core.RunResult>;
430
471
  /** Live query: the latest value a run published for `key` via `ctx.setEvent` (no side effect). */
@@ -443,7 +484,9 @@ declare class DurableApiController extends DurableApiControllerBase {
443
484
 
444
485
  interface DurableDashboardOptions {
445
486
  /**
446
- * Who may use the console (the SPA, its JSON API, the run stream and the webhook route). One of:
487
+ * Who may use the console (the SPA, its JSON API and the run stream). Not the `ctx.webhook()`
488
+ * callback (`POST <apiBasePath>/webhooks/:token`), which third parties call with the token as the
489
+ * secret; {@link decorators} still apply to it. One of:
447
490
  *
448
491
  * - a function `(context) => boolean | { allowed } | { redirectTo }` (may be async);
449
492
  * - an `@Injectable()` class implementing {@link ConsoleAuthorizer} (constructor injection works;
@@ -525,6 +568,12 @@ interface DurableDashboardOptions {
525
568
  * strings that keep their meaning (where the console lives) whether or not this module mounts it.
526
569
  */
527
570
  routes?: boolean;
571
+ /**
572
+ * Register the console's reads and run/schedule actions as developer tools on the agent MCP
573
+ * server's dev endpoint (`/mcp/aviary`), gated by this console's `authorize`. Default: on when
574
+ * the console's routes are mounted, off with `routes: false`.
575
+ */
576
+ devTools?: boolean;
528
577
  }
529
578
  /**
530
579
  * Holds the JSON API controller + its read service, mounted on its own path by `forRoot`. Its own
@@ -540,6 +589,8 @@ declare class DurableApiModule {
540
589
  decorators?: ClassDecorator[];
541
590
  /** See {@link DurableDashboardOptions.authorize}. */
542
591
  authorize?: ConsoleAuthorize;
592
+ /** See {@link DurableDashboardOptions.devTools}. Default: on when the routes are mounted. */
593
+ devTools?: boolean;
543
594
  }): DynamicModule;
544
595
  }
545
596
  /**
@@ -557,9 +608,12 @@ declare class DurableDashboardModule {
557
608
  }
558
609
 
559
610
  /** DI token carrying the UI mount base (e.g. `/durable`). */
560
- declare const DASHBOARD_BASE_PATH: unique symbol;
611
+ declare const DASHBOARD_BASE_PATH: symbol;
561
612
  /** DI token carrying the JSON API base the SPA fetches from (e.g. `/api/durable`). */
562
- declare const DASHBOARD_API_PATH: unique symbol;
613
+ declare const DASHBOARD_API_PATH: symbol;
614
+
615
+ /** The id of the page's JSON config block. */
616
+ declare const DURABLE_DASHBOARD_CONFIG_ID = "durable-dashboard-config";
563
617
  /**
564
618
  * Serves the bundled control-plane SPA at the configured base (+ hashed assets at `<base>/assets`).
565
619
  * The controller routes are relative: the mounted {@link DurableUiController} gets its path from
@@ -577,6 +631,11 @@ declare class DurableUiControllerBase {
577
631
  private readonly dir;
578
632
  constructor(basePath: string, apiBasePath: string);
579
633
  index(): string;
634
+ /**
635
+ * The page's JSON config block (`durable-dashboard-config`, CSP-safe: data, not script). It
636
+ * carries `switcher` only when another Aviary console is registered.
637
+ */
638
+ private configBlock;
580
639
  asset(file: string): StreamableFile;
581
640
  }
582
641
  /**
@@ -588,4 +647,165 @@ declare class DurableUiControllerBase {
588
647
  declare class DurableUiController extends DurableUiControllerBase {
589
648
  }
590
649
 
591
- export { CONSOLE_AUTHORIZE_DECIDE, CONSOLE_AUTHORIZE_MISSING_WARNING, CONSOLE_GATE, CONSOLE_PAGE_METADATA, ConsoleAccessDenied, type ConsoleAuthorize, ConsoleAuthorizeFilter, ConsoleAuthorizeGuard, type ConsoleAuthorizeResult, type ConsoleAuthorizer, type ConsoleAuthorizerClass, type ConsoleDecide, type ConsoleDecision, type ConsoleGate, ConsolePage, type ConsolePolicyCheck, DASHBOARD_API_PATH, DASHBOARD_BASE_PATH, DashboardService, DurableApiController, DurableApiControllerBase, DurableApiModule, DurableDashboardModule, type DurableDashboardOptions, DurableUiController, DurableUiControllerBase, UseConsoleAuthorize, type WorkflowLifecyclePage, authorizeByToken, normalizeConsoleResult, resolveConsoleAuthorize };
650
+ /** The process-wide slot `@dudousxd/nestjs-agent-mcp-server` serves on its dev MCP endpoint. */
651
+ declare const DEV_TOOLS_SLOT: symbol;
652
+ /** One property of a {@link DevToolInputSchema}. */
653
+ interface DevToolInputProperty {
654
+ type: 'string' | 'number' | 'integer' | 'boolean' | 'array';
655
+ description?: string;
656
+ enum?: readonly string[];
657
+ /** Arrays hold strings only. */
658
+ items?: {
659
+ type: 'string';
660
+ };
661
+ minimum?: number;
662
+ maximum?: number;
663
+ }
664
+ /** An object of scalar / enum / array-of-string properties. */
665
+ interface DevToolInputSchema {
666
+ type: 'object';
667
+ properties: Record<string, DevToolInputProperty>;
668
+ required?: string[];
669
+ }
670
+ /** What the slot's `authorize` answers: the console's resolved decision. */
671
+ type DevToolsDecision = {
672
+ allowed: true;
673
+ caller?: string;
674
+ } | {
675
+ allowed: false;
676
+ reason: 'unauthenticated' | 'forbidden';
677
+ redirectTo?: string;
678
+ };
679
+ /** What a tool's `run` receives besides its input. */
680
+ interface DevToolCall {
681
+ context: ExecutionContext;
682
+ caller: {
683
+ id: string;
684
+ via: 'token' | 'session' | 'open';
685
+ };
686
+ limits: {
687
+ maxRows: number;
688
+ maxBytes: number;
689
+ };
690
+ }
691
+ /** One developer tool, as the dev-tools slot takes it. */
692
+ interface DurableDevTool {
693
+ /** `durable_<verb>_<noun>`. */
694
+ name: string;
695
+ /** `read` tools are listed by default; `action` tools only when the app opts in. */
696
+ kind: 'read' | 'action';
697
+ description: string;
698
+ input: DevToolInputSchema;
699
+ /** Set on actions that cancel. */
700
+ destructive?: boolean;
701
+ /** Capability, not access: hides a tool this deployment cannot run. */
702
+ available?(): boolean | Promise<boolean>;
703
+ run(input: Record<string, unknown>, call?: DevToolCall): Promise<unknown>;
704
+ }
705
+ /** Runs `durable_retry_run` / `durable_cancel_run` act on at most per call. */
706
+ declare const MAX_RUN_IDS = 100;
707
+ /**
708
+ * The durable developer tools (lib `durable`): the console's reads and its run and schedule
709
+ * actions, through the same {@link DashboardService} methods its API serves, so tenancy and the
710
+ * app's redaction apply. Signals, run events and schedules are available only on the control plane.
711
+ */
712
+ declare function durableDevTools(service: DashboardService): DurableDevTool[];
713
+ /**
714
+ * Registers the durable tools into the dev-tools slot, gated by the console's own resolved
715
+ * `authorize`, when the console is mounted (or `devTools: true`). Removed again on shutdown.
716
+ */
717
+ declare class DurableDevToolsRegistration implements OnModuleInit, OnModuleDestroy {
718
+ #private;
719
+ private readonly enabled;
720
+ private readonly service;
721
+ private readonly decide;
722
+ constructor(enabled: boolean, service: DashboardService, decide: ConsoleDecide);
723
+ onModuleInit(): void;
724
+ onModuleDestroy(): void;
725
+ }
726
+
727
+ /** The process-wide slot every Aviary console registers into (created by whichever boots first). */
728
+ declare const AVIARY_CONSOLES_SLOT: symbol;
729
+ /** The icon a console shows in the switcher. An unknown value renders as `generic`. */
730
+ type AviaryConsoleIcon = 'workflow' | 'telescope' | 'media' | 'payments' | 'agent' | 'identity' | 'generic';
731
+ /** What a console's `authorize` answers; only `allowed: true` lists it. */
732
+ interface AviaryConsoleDecision {
733
+ allowed: boolean;
734
+ reason?: string;
735
+ redirectTo?: string;
736
+ }
737
+ /** One console the switcher can list. */
738
+ interface AviaryConsoleEntry {
739
+ /** `/^[a-z][a-z0-9-]*$/`: `'durable'`, `'telescope'`, `'agent'`, `'media'`, … */
740
+ id: string;
741
+ /** Product title: `'Workflows'`, `'Telescope'`, `'Agents'`, `'Media'`. */
742
+ title: string;
743
+ icon: AviaryConsoleIcon;
744
+ /** Same-origin absolute path of the console's page (`/durable`). */
745
+ path: string;
746
+ /** Sort key: durable 10, telescope 20, agent 30, media 50. */
747
+ order: number;
748
+ /** The console's resolved gate (what its own routes run). Never throws. */
749
+ authorize(context: ExecutionContext): Promise<AviaryConsoleDecision>;
750
+ }
751
+ /** The value at {@link AVIARY_CONSOLES_SLOT}. Data only, so every console's copy is identical. */
752
+ interface AviaryConsoleRegistry {
753
+ readonly version: 1;
754
+ readonly entries: AviaryConsoleEntry[];
755
+ }
756
+ /** One console as `GET <api>/consoles` returns it. */
757
+ interface VisibleConsole {
758
+ id: string;
759
+ title: string;
760
+ icon: AviaryConsoleIcon;
761
+ href: string;
762
+ }
763
+ /** The answer of `GET <api>/consoles`. */
764
+ interface VisibleConsoles {
765
+ version: 1;
766
+ current: string;
767
+ consoles: VisibleConsole[];
768
+ }
769
+ /** How long another console's `authorize` may take before it is left out. */
770
+ declare const CONSOLE_PROBE_TIMEOUT_MS: number;
771
+ /**
772
+ * Adds a console to the process's switcher registry, creating it when absent. Returns a function
773
+ * that removes the entry again, or `undefined` when it was not added (another registry version, or
774
+ * the id is taken: logged as a warning). A bad `id` or `path` throws.
775
+ */
776
+ declare function registerAviaryConsole(entry: AviaryConsoleEntry, logger?: {
777
+ warn(message: string): void;
778
+ }): (() => void) | undefined;
779
+ /**
780
+ * The consoles other than `selfId` the viewer may open, by `order` then `id`. Every other entry's
781
+ * `authorize` runs in parallel, each given `timeoutMs`; a timeout, a rejection or any denial hides
782
+ * it. No reason is ever returned.
783
+ */
784
+ declare function listVisibleConsoles(registry: AviaryConsoleRegistry | undefined, selfId: string, context: ExecutionContext, timeoutMs?: number): Promise<VisibleConsoles>;
785
+ /** The durable console's id in the registry. */
786
+ declare const DURABLE_CONSOLE_ID: string;
787
+ /**
788
+ * Registers the durable console into the switcher registry (the console's page path, gated by its
789
+ * resolved `authorize`). Provided only when the console's routes are mounted; removed on shutdown.
790
+ */
791
+ declare class DurableConsoleRegistration implements OnModuleInit, OnModuleDestroy {
792
+ #private;
793
+ private readonly basePath;
794
+ private readonly decide;
795
+ constructor(basePath: string, decide: ConsoleDecide);
796
+ onModuleInit(): void;
797
+ onModuleDestroy(): void;
798
+ }
799
+ /** What the route hands on to the other consoles' `authorize`: this request's `ExecutionContext`. */
800
+ interface CurrentContext {
801
+ context: ExecutionContext;
802
+ }
803
+ /**
804
+ * `GET <api>/consoles`: the other Aviary consoles this viewer may open. Behind the console's own
805
+ * access check, like every API route, so only someone who may open this console learns the list.
806
+ */
807
+ declare class DurableConsolesController {
808
+ consoles(current: CurrentContext): Promise<VisibleConsoles>;
809
+ }
810
+
811
+ export { AVIARY_CONSOLES_SLOT, type AviaryConsoleDecision, type AviaryConsoleEntry, type AviaryConsoleIcon, type AviaryConsoleRegistry, CONSOLE_AUTHORIZE_DECIDE, CONSOLE_AUTHORIZE_MISSING_WARNING, CONSOLE_GATE, CONSOLE_PAGE_METADATA, CONSOLE_PROBE_TIMEOUT_MS, CONSOLE_PUBLIC_METADATA, ConsoleAccessDenied, type ConsoleAuthorize, ConsoleAuthorizeFilter, ConsoleAuthorizeGuard, type ConsoleAuthorizeResult, type ConsoleAuthorizer, type ConsoleAuthorizerClass, type ConsoleDecide, type ConsoleDecision, type ConsoleGate, ConsolePage, type ConsolePolicyCheck, ConsolePublic, DASHBOARD_API_PATH, DASHBOARD_BASE_PATH, DEV_TOOLS_SLOT, DURABLE_CONSOLE_ID, DURABLE_DASHBOARD_CONFIG_ID, DashboardService, type DevToolCall, type DevToolInputProperty, type DevToolInputSchema, type DevToolsDecision, DurableApiController, DurableApiControllerBase, DurableApiModule, DurableConsoleRegistration, DurableConsolesController, DurableDashboardModule, type DurableDashboardOptions, type DurableDevTool, DurableDevToolsRegistration, DurableUiController, DurableUiControllerBase, MAX_RUN_IDS, UseConsoleAuthorize, type VisibleConsole, type VisibleConsoles, type WorkflowLifecyclePage, anyAuthorize, authorizeByToken, durableDevTools, listVisibleConsoles, normalizeConsoleResult, registerAviaryConsole, resolveConsoleAuthorize };