UNPKG

@adonis-agora/agent

Version:

Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.

102 lines • 6.18 kB
/** * The built-in "access denied" page a BROWSER sees when the dashboard guard refuses a page * navigation — the SPA shell, its assets, or (Mode A only) a session-less visit. Before this page * existed the guard answered every denial with the JSON it answers the API with * (`{ "error": "forbidden" }`), which is the right thing for the SPA's `fetch` calls and the wrong * thing for a human staring at a browser tab. * * Deliberately a small, dependency-free, hand-authored HTML page (like `login_page.ts`) — NOT part * of the bundled SPA, so a denial never needs the SPA to load first. It carries NO `<script>` at all, * so a host running a nonce'd `script-src` CSP (the `@adonisjs/shield` default) cannot break it; the * one inline `<style>` takes the request's CSP nonce when the provider hands one over. * * Every string the host can supply ({@link AccessDeniedPageOptions}) is HTML-escaped on the way in. * They are developer-controlled config values, not user input — but escaping them costs nothing and * removes the one class of bug a "just interpolate it" template would invite. * * The visual language (Aviary neutrals, the console's own accent, mono eyebrow over a sans body) * mirrors the agent SPA so the refusal reads as part of the same product, not a server default. * * Ported from `@adonis-agora/payments` (also in `@adonis-agora/durable`, `@adonis-agora/media`, `@adonis-agora/agent` and * `@adonis-agora/telescope` with only {@link CONSOLE} changed, so the Agora consoles share ONE * refusal story rather than each inventing its own. */ /** Why the guard refused. Drives the default title/message and which buttons make sense. */ export type AccessDeniedReason = /** Nobody is signed in (a `401`): offer the login page when one exists. */ 'unauthenticated' /** Somebody is signed in but `authorize` said no (a `403`). */ | 'forbidden' /** `dashboardAuth` runs in Mode A only and there is no session — the host app must mint one. */ | 'session-required'; /** What the provider knows about the refusal — handed to a custom renderer and to the default page. */ export interface AccessDeniedInfo { /** The HTTP status the page is served with. */ status: 401 | 403; reason: AccessDeniedReason; /** The console's mount path (`/agent/dashboard`; `''` when mounted at the root). */ basePath: string; /** The built-in login page, present only when `dashboardAuth.login` is configured. */ loginHref?: string; /** A developer-facing detail (e.g. a thrown hook's message). Providers set it OUTSIDE production only. */ detail?: string; /** The request's CSP nonce, applied to the inline `<style>` so a nonce'd `style-src` keeps the page styled. */ nonce?: string; } /** * Knobs for the built-in page — the `accessDenied` config option in its OBJECT form. Every field is * optional; omit the option entirely for the defaults. */ export interface AccessDeniedPageOptions { /** Console name in the eyebrow and `<title>`. Default: the console's own name (`Agent`). */ brand?: string; /** Heading. Default depends on {@link AccessDeniedInfo.reason}. */ title?: string; /** The sentence under the heading. Default depends on the reason. */ message?: string; /** Where "Back to app" points. Default `/`; `false` hides the button. */ homeHref?: string | false; /** Label of the "Back to app" button. */ homeLabel?: string; /** Where "Sign in" points. Default: the built-in login page when one exists; `false` hides it. */ loginHref?: string | false; /** Label of the "Sign in" button. */ loginLabel?: string; /** Accent colour (any CSS colour). Default: the console's own accent. */ accent?: string; } /** * The `accessDenied` config option in its FUNCTION form: render the page yourself. Return an HTML * string and the provider serves it (with the refusal's status, `text/html`, `no-store`). Return * nothing after answering the request yourself — a redirect to your own login page, most commonly — * and the provider stands down. Return nothing WITHOUT answering and the default page is served, so * the hook is safe to use for side effects (logging) alone. * * `Ctx` is the host framework's HTTP context — the provider passes AdonisJS's `HttpContext` through * untouched; it is generic here so this module stays framework-light and unit-testable. */ export type AccessDeniedRenderer<Ctx = unknown> = (info: AccessDeniedInfo, ctx: Ctx) => string | undefined | null | void | Promise<string | undefined | null | void>; /** The `accessDenied` config option: tweak the built-in page, or replace it. */ export type AccessDeniedOption<Ctx = unknown> = AccessDeniedPageOptions | AccessDeniedRenderer<Ctx>; /** This console's identity — the ONLY thing that differs between the Agora ports of this file. */ export declare const CONSOLE: { readonly brand: 'Agent'; readonly accent: '#a78bfa'; readonly packageName: '@adonis-agora/agent'; }; /** Escape the five characters that matter inside HTML text and double-quoted attributes. */ export declare function escapeHtml(value: string): string; /** * Render the built-in page. Pure: same `info` + `options` in, same HTML out — so it is trivially * snapshot/regex testable and safe to call from anywhere (the provider, a test, a host that wants * to embed the default page inside its own layout). */ export declare function renderAccessDeniedPage(info: AccessDeniedInfo, options?: AccessDeniedPageOptions): string; /** * Resolve WHAT to serve for a refusal, honouring the host's `accessDenied` option in either form. * Returns the HTML to send, or `null` when a custom renderer already answered the request itself * (`answered()` reports whether it did — the provider wires that to "a `location` header or a body * is already set"). Framework-light: knows nothing about the HTTP context beyond passing it through. */ export declare function resolveAccessDeniedPage<Ctx>(info: AccessDeniedInfo, option: AccessDeniedOption<Ctx> | null | undefined, ctx: Ctx, answered: () => boolean): Promise<string | null>; //# sourceMappingURL=access_denied_page.d.ts.map