@adonis-agora/agent 0.22.2 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/dist/providers/dashboard_provider.d.ts.map +1 -1
- package/dist/providers/dashboard_provider.js +11 -5
- package/dist/providers/dashboard_provider.js.map +1 -1
- package/dist/src/dashboard/define_config.d.ts +22 -0
- package/dist/src/dashboard/define_config.d.ts.map +1 -1
- package/dist/src/dashboard/define_config.js +2 -0
- package/dist/src/dashboard/define_config.js.map +1 -1
- package/dist/src/dashboard/gate.d.ts +8 -2
- package/dist/src/dashboard/gate.d.ts.map +1 -1
- package/dist/src/dashboard/gate.js +9 -1
- package/dist/src/dashboard/gate.js.map +1 -1
- package/dist/src/dashboard/index.d.ts +1 -1
- package/dist/src/dashboard/index.d.ts.map +1 -1
- package/dist/src/dashboard/index.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# @adonis-agora/agent
|
|
2
2
|
|
|
3
|
+
## 0.23.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`3504f37`](https://github.com/DavideCarvalho/adonis-agora-agent/commit/3504f37de4e063b5839388b684c63a0ab2ff5efa) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - The dashboard gate now honors a redirect the host already wrote to the response instead of always overwriting it with the default `401`/`403 { error }` JSON — mirrors `@adonis-agora/durable`'s dashboard guard, and `@adonis-agora/telescope`'s guard gains the same escape hatch in a companion release.
|
|
8
|
+
|
|
9
|
+
- **`authorize` denies (403)**: redirect from inside `authorize` (e.g. `ctx.response.redirect('/acesso-negado')`) and return `false` — the gate detects the `location` header and skips its own JSON write. No API change; this always worked as a predicate, only the response-writing layer changed.
|
|
10
|
+
- **The actor resolver itself rejects the caller (401)** — no resolver configured, or `resolve(ctx)` threw (e.g. `AuthActorResolver` on an anonymous request) — is a case `authorize` never sees, since there's no resolved actor to hand it. New optional `dashboard.onUnauthenticated?: (ctx) => void | Promise<void>` runs there instead, ctx-only (never a fabricated actor, preserving the resolver's "no identity is invented" contract): redirect inside it the same way to replace the default JSON, or leave the response untouched to keep it.
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
// config/agent.ts
|
|
14
|
+
dashboard: {
|
|
15
|
+
authorize: (actor, ctx) => {
|
|
16
|
+
if (isAdmin(actor)) return true
|
|
17
|
+
ctx.response.redirect('/acesso-negado')
|
|
18
|
+
return false
|
|
19
|
+
},
|
|
20
|
+
onUnauthenticated: (ctx) => {
|
|
21
|
+
ctx.response.redirect('/login')
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
3
26
|
## 0.22.2
|
|
4
27
|
|
|
5
28
|
### Patch Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard_provider.d.ts","sourceRoot":"","sources":["../../providers/dashboard_provider.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"dashboard_provider.d.ts","sourceRoot":"","sources":["../../providers/dashboard_provider.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAkB/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,MAAM,CAAC,OAAO,OAAO,iBAAiB;;IACxB,SAAS,CAAC,GAAG,EAAE,kBAAkB;gBAAvB,GAAG,EAAE,kBAAkB;IAcvC,IAAI;CAqHX"}
|
|
@@ -92,14 +92,14 @@ export default class DashboardProvider {
|
|
|
92
92
|
// `<base href="${mount}/">`, so the SPA's relative `./assets/*` URLs resolve against the mount
|
|
93
93
|
// directory regardless of whether the browser's URL carries a trailing slash.
|
|
94
94
|
router.get(mount, async (ctx) => {
|
|
95
|
-
if (!(await this.#gate(ctx, actorResolver, authorize)))
|
|
95
|
+
if (!(await this.#gate(ctx, actorResolver, authorize, dashboardConfig.onUnauthenticated)))
|
|
96
96
|
return;
|
|
97
97
|
await this.#sendIndex(ctx, apiBase, mount);
|
|
98
98
|
});
|
|
99
99
|
// Built assets (JS/CSS/fonts/...), with an index fallback for any unmatched path so the
|
|
100
100
|
// client-rendered console still boots on a deep link.
|
|
101
101
|
router.get(`${mount}/*`, async (ctx) => {
|
|
102
|
-
if (!(await this.#gate(ctx, actorResolver, authorize)))
|
|
102
|
+
if (!(await this.#gate(ctx, actorResolver, authorize, dashboardConfig.onUnauthenticated)))
|
|
103
103
|
return;
|
|
104
104
|
const segments = safeAssetSegments(ctx.params['*']);
|
|
105
105
|
if (segments === null) {
|
|
@@ -112,11 +112,17 @@ export default class DashboardProvider {
|
|
|
112
112
|
* Resolve the actor through the agent config's resolver, mirroring the governance routes, then run
|
|
113
113
|
* the optional `authorize` gate. Returns `true` to proceed; replies `401` on a missing/failed
|
|
114
114
|
* resolver and `403` when `authorize` denies the resolved actor. The decision itself lives in the
|
|
115
|
-
* router-free {@link evaluateDashboardGate} so it can be unit tested; this only writes the response
|
|
115
|
+
* router-free {@link evaluateDashboardGate} so it can be unit tested; this only writes the response —
|
|
116
|
+
* and, mirroring `@adonis-agora/durable`'s dashboard, ONLY when neither `onUnauthenticated` nor
|
|
117
|
+
* `authorize` already answered the request themselves: if either set a `location` header (typically
|
|
118
|
+
* via `ctx.response.redirect(...)`, e.g. to the app's own login page), that redirect stands and this
|
|
119
|
+
* skips writing its own `401`/`403` JSON on top of it.
|
|
116
120
|
*/
|
|
117
|
-
async #gate(ctx, actorResolver, authorize) {
|
|
118
|
-
const verdict = await evaluateDashboardGate(ctx, actorResolver, authorize, !this.app.inProduction);
|
|
121
|
+
async #gate(ctx, actorResolver, authorize, onUnauthenticated) {
|
|
122
|
+
const verdict = await evaluateDashboardGate(ctx, actorResolver, authorize, !this.app.inProduction, onUnauthenticated);
|
|
119
123
|
if (!verdict.ok) {
|
|
124
|
+
if (ctx.response.getHeader('location'))
|
|
125
|
+
return false;
|
|
120
126
|
ctx.response.status(verdict.status).json({ error: verdict.error });
|
|
121
127
|
return false;
|
|
122
128
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard_provider.js","sourceRoot":"","sources":["../../providers/dashboard_provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,OAAO,
|
|
1
|
+
{"version":3,"file":"dashboard_provider.js","sourceRoot":"","sources":["../../providers/dashboard_provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,6 +8,20 @@ import type { Actor } from '../types.js';
|
|
|
8
8
|
* mere authentication, e.g. `(actor) => actor.roles?.includes('ADMIN') ?? false`.
|
|
9
9
|
*/
|
|
10
10
|
export type AgentDashboardAuthorize = (actor: Actor, ctx: HttpContext) => boolean | Promise<boolean>;
|
|
11
|
+
/**
|
|
12
|
+
* Runs when the agent config's `actorResolver` itself rejects the caller — no resolver configured, or
|
|
13
|
+
* `resolve(ctx)` threw (e.g. `AuthActorResolver` over an anonymous `ctx.auth`) — so there is no
|
|
14
|
+
* resolved {@link Actor} to hand to {@link AgentDashboardAuthorize}. Deliberately ctx-only, never a
|
|
15
|
+
* fabricated actor: the resolver's whole contract is "never invent an identity", and this hook must
|
|
16
|
+
* not undermine that.
|
|
17
|
+
*
|
|
18
|
+
* Called BEFORE the gate writes its default `401 { error }` JSON. To show something other than that
|
|
19
|
+
* JSON — a redirect to the app's own login page, most commonly — set a response header inside this
|
|
20
|
+
* hook (typically via `ctx.response.redirect(...)`); the gate detects the `location` header and skips
|
|
21
|
+
* its own write. Anything else (return normally without touching the response) falls through to the
|
|
22
|
+
* default JSON, so this is safe to omit or to use only for side effects (e.g. logging).
|
|
23
|
+
*/
|
|
24
|
+
export type AgentDashboardUnauthenticatedHook = (ctx: HttpContext) => void | Promise<void>;
|
|
11
25
|
/**
|
|
12
26
|
* Optional `config('agent').dashboard` block. The dashboard reuses the agent config's `path` and
|
|
13
27
|
* `actorResolver` (so it sits behind the SAME actor gating as the governance routes); this block
|
|
@@ -34,11 +48,19 @@ export interface AgentDashboardConfig {
|
|
|
34
48
|
* `governanceAuthorize`.
|
|
35
49
|
*/
|
|
36
50
|
authorize?: AgentDashboardAuthorize;
|
|
51
|
+
/**
|
|
52
|
+
* Runs when the actor resolver itself rejects the caller (no resolver, or `resolve(ctx)` threw) —
|
|
53
|
+
* the one denial `authorize` never sees, since there is no resolved actor to hand it. Set a
|
|
54
|
+
* response header inside (e.g. `ctx.response.redirect('/login')`) to replace the default
|
|
55
|
+
* `401 { error }` JSON with a custom page/redirect. See {@link AgentDashboardUnauthenticatedHook}.
|
|
56
|
+
*/
|
|
57
|
+
onUnauthenticated?: AgentDashboardUnauthenticatedHook;
|
|
37
58
|
}
|
|
38
59
|
export interface ResolvedAgentDashboardConfig {
|
|
39
60
|
enabled: boolean;
|
|
40
61
|
path?: string;
|
|
41
62
|
authorize?: AgentDashboardAuthorize;
|
|
63
|
+
onUnauthenticated?: AgentDashboardUnauthenticatedHook;
|
|
42
64
|
}
|
|
43
65
|
/** Fill defaults for the optional dashboard config block. */
|
|
44
66
|
export declare function resolveDashboardConfig(config: AgentDashboardConfig | undefined): ResolvedAgentDashboardConfig;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define_config.d.ts","sourceRoot":"","sources":["../../../src/dashboard/define_config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;;GAKG;AACH,MAAM,MAAM,uBAAuB,GAAG,CACpC,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,WAAW,KACb,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEhC;;;;;;;;;GASG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,SAAS,CAAC,EAAE,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"define_config.d.ts","sourceRoot":"","sources":["../../../src/dashboard/define_config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;;GAKG;AACH,MAAM,MAAM,uBAAuB,GAAG,CACpC,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,WAAW,KACb,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEhC;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,iCAAiC,GAAG,CAAC,GAAG,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE3F;;;;;;;;;GASG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,SAAS,CAAC,EAAE,uBAAuB,CAAC;IACpC;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,iCAAiC,CAAC;CACvD;AAED,MAAM,WAAW,4BAA4B;IAC3C,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,uBAAuB,CAAC;IACpC,iBAAiB,CAAC,EAAE,iCAAiC,CAAC;CACvD;AAED,6DAA6D;AAC7D,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,oBAAoB,GAAG,SAAS,GACvC,4BAA4B,CAO9B;AAED,uGAAuG;AACvG,MAAM,MAAM,sBAAsB,GAC9B;IAAE,KAAK,EAAE,IAAI,CAAA;CAAE,GACf;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,GACpC;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,oBAAoB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC/D;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,uBAAuB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAoBvE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,OAAO,EAChB,mBAAmB,EAAE,wBAAwB,GAAG,SAAS,EACzD,iBAAiB,CAAC,EAAE,OAAO,GAC1B,sBAAsB,CAgBxB"}
|
|
@@ -5,6 +5,8 @@ export function resolveDashboardConfig(config) {
|
|
|
5
5
|
resolved.path = config.path;
|
|
6
6
|
if (config?.authorize !== undefined)
|
|
7
7
|
resolved.authorize = config.authorize;
|
|
8
|
+
if (config?.onUnauthenticated !== undefined)
|
|
9
|
+
resolved.onUnauthenticated = config.onUnauthenticated;
|
|
8
10
|
return resolved;
|
|
9
11
|
}
|
|
10
12
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define_config.js","sourceRoot":"","sources":["../../../src/dashboard/define_config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"define_config.js","sourceRoot":"","sources":["../../../src/dashboard/define_config.ts"],"names":[],"mappings":"AAwEA,6DAA6D;AAC7D,MAAM,UAAU,sBAAsB,CACpC,MAAwC;IAExC,MAAM,QAAQ,GAAiC,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,IAAI,IAAI,EAAE,CAAC;IACpF,IAAI,MAAM,EAAE,IAAI,KAAK,SAAS;QAAE,QAAQ,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;IAC5D,IAAI,MAAM,EAAE,SAAS,KAAK,SAAS;QAAE,QAAQ,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;IAC3E,IAAI,MAAM,EAAE,iBAAiB,KAAK,SAAS;QACzC,QAAQ,CAAC,iBAAiB,GAAG,MAAM,CAAC,iBAAiB,CAAC;IACxD,OAAO,QAAQ,CAAC;AAClB,CAAC;AASD;;;;GAIG;AACH,MAAM,0BAA0B,GAC9B,0sBAA0sB,CAAC;AAE7sB;;;;;;GAMG;AACH,MAAM,6BAA6B,GACjC,ipBAAipB,CAAC;AAEppB;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAAgB,EAChB,mBAAyD,EACzD,iBAA2B;IAE3B,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAC1D,IAAI,mBAAmB,KAAK,SAAS,EAAE,CAAC;QACtC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,OAAO,EAAE,0BAA0B,EAAE,CAAC;IAC7F,CAAC;IACD,mGAAmG;IACnG,iGAAiG;IACjG,0FAA0F;IAC1F,IAAI,iBAAiB,KAAK,KAAK,EAAE,CAAC;QAChC,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,uBAAuB;YAC/B,OAAO,EAAE,6BAA6B;SACvC,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AACzB,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { HttpContext } from '@adonisjs/core/http';
|
|
2
2
|
import type { ActorResolver } from '../spi/actor-resolver.js';
|
|
3
|
-
import type { AgentDashboardAuthorize } from './define_config.js';
|
|
3
|
+
import type { AgentDashboardAuthorize, AgentDashboardUnauthenticatedHook } from './define_config.js';
|
|
4
4
|
/** The outcome of the dashboard access gate: proceed, or deny with an HTTP status + message. */
|
|
5
5
|
export type DashboardGateVerdict = {
|
|
6
6
|
ok: true;
|
|
@@ -39,6 +39,12 @@ export type DashboardGateVerdict = {
|
|
|
39
39
|
* the provider passes `!app.inProduction` so local/dev boots keep the diagnostic detail. The "no
|
|
40
40
|
* actor resolver configured" branch is unaffected — that is a static config error, not a per-request
|
|
41
41
|
* one, identical for every caller, and useful for the operator to see even in production.
|
|
42
|
+
*
|
|
43
|
+
* `onUnauthenticated`, when set, runs on EITHER failure-to-resolve branch (no resolver configured, or
|
|
44
|
+
* `resolve(ctx)` threw) before this returns its verdict — it never sees a fabricated actor, since
|
|
45
|
+
* there isn't one. The provider (not this function) is what actually decides whether to skip its
|
|
46
|
+
* default JSON write in favour of whatever the hook did to `ctx.response` (redirect, custom render);
|
|
47
|
+
* this function only guarantees the hook runs before the verdict is handed back.
|
|
42
48
|
*/
|
|
43
|
-
export declare function evaluateDashboardGate(ctx: HttpContext, actorResolver: ActorResolver | undefined, authorize?: AgentDashboardAuthorize, debug?: boolean): Promise<DashboardGateVerdict>;
|
|
49
|
+
export declare function evaluateDashboardGate(ctx: HttpContext, actorResolver: ActorResolver | undefined, authorize?: AgentDashboardAuthorize, debug?: boolean, onUnauthenticated?: AgentDashboardUnauthenticatedHook): Promise<DashboardGateVerdict>;
|
|
44
50
|
//# sourceMappingURL=gate.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../../src/dashboard/gate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../../src/dashboard/gate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,KAAK,EACV,uBAAuB,EACvB,iCAAiC,EAClC,MAAM,oBAAoB,CAAC;AAE5B,gGAAgG;AAChG,MAAM,MAAM,oBAAoB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,GAAG,GAAG,GAAG,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAElG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,WAAW,EAChB,aAAa,EAAE,aAAa,GAAG,SAAS,EACxC,SAAS,CAAC,EAAE,uBAAuB,EACnC,KAAK,UAAQ,EACb,iBAAiB,CAAC,EAAE,iCAAiC,GACpD,OAAO,CAAC,oBAAoB,CAAC,CA8B/B"}
|
|
@@ -28,9 +28,16 @@
|
|
|
28
28
|
* the provider passes `!app.inProduction` so local/dev boots keep the diagnostic detail. The "no
|
|
29
29
|
* actor resolver configured" branch is unaffected — that is a static config error, not a per-request
|
|
30
30
|
* one, identical for every caller, and useful for the operator to see even in production.
|
|
31
|
+
*
|
|
32
|
+
* `onUnauthenticated`, when set, runs on EITHER failure-to-resolve branch (no resolver configured, or
|
|
33
|
+
* `resolve(ctx)` threw) before this returns its verdict — it never sees a fabricated actor, since
|
|
34
|
+
* there isn't one. The provider (not this function) is what actually decides whether to skip its
|
|
35
|
+
* default JSON write in favour of whatever the hook did to `ctx.response` (redirect, custom render);
|
|
36
|
+
* this function only guarantees the hook runs before the verdict is handed back.
|
|
31
37
|
*/
|
|
32
|
-
export async function evaluateDashboardGate(ctx, actorResolver, authorize, debug = false) {
|
|
38
|
+
export async function evaluateDashboardGate(ctx, actorResolver, authorize, debug = false, onUnauthenticated) {
|
|
33
39
|
if (actorResolver === undefined) {
|
|
40
|
+
await onUnauthenticated?.(ctx);
|
|
34
41
|
return { ok: false, status: 401, error: 'no actor resolver configured' };
|
|
35
42
|
}
|
|
36
43
|
let actor;
|
|
@@ -38,6 +45,7 @@ export async function evaluateDashboardGate(ctx, actorResolver, authorize, debug
|
|
|
38
45
|
actor = await actorResolver.resolve(ctx);
|
|
39
46
|
}
|
|
40
47
|
catch (error) {
|
|
48
|
+
await onUnauthenticated?.(ctx);
|
|
41
49
|
return {
|
|
42
50
|
ok: false,
|
|
43
51
|
status: 401,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../../../src/dashboard/gate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../../../src/dashboard/gate.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,GAAgB,EAChB,aAAwC,EACxC,SAAmC,EACnC,KAAK,GAAG,KAAK,EACb,iBAAqD;IAErD,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;QAChC,MAAM,iBAAiB,EAAE,CAAC,GAAG,CAAC,CAAC;QAC/B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,8BAA8B,EAAE,CAAC;IAC3E,CAAC;IACD,IAAI,KAAoD,CAAC;IACzD,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,iBAAiB,EAAE,CAAC,GAAG,CAAC,CAAC;QAC/B,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,GAAG;YACX,KAAK,EAAE,KAAK,IAAI,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc;SACxE,CAAC;IACJ,CAAC;IACD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,IAAI,CAAC;YACH,IAAI,CAAC,CAAC,MAAM,SAAS,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;gBACnC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;YACxD,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,MAAM,EAAE,GAAG;gBACX,KAAK,EAAE,KAAK,IAAI,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW;aACrE,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AACtB,CAAC"}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* so a package.json edge back from here to there would be circular. See
|
|
15
15
|
* `providers/dashboard_provider.ts`'s module doc for the full story.
|
|
16
16
|
*/
|
|
17
|
-
export { type AgentDashboardAuthorize, type AgentDashboardConfig, type DashboardMountDecision, type ResolvedAgentDashboardConfig, decideDashboardMount, resolveDashboardConfig, } from './define_config.js';
|
|
17
|
+
export { type AgentDashboardAuthorize, type AgentDashboardConfig, type AgentDashboardUnauthenticatedHook, type DashboardMountDecision, type ResolvedAgentDashboardConfig, decideDashboardMount, resolveDashboardConfig, } from './define_config.js';
|
|
18
18
|
export { type DashboardGateVerdict, evaluateDashboardGate } from './gate.js';
|
|
19
19
|
export { apiBaseFor, contentTypeFor, injectApiBase, injectBaseHref, mountPathFor, safeAssetSegments, trimSlashes, } from './paths.js';
|
|
20
20
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/dashboard/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,4BAA4B,EACjC,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,KAAK,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAC7E,OAAO,EACL,UAAU,EACV,cAAc,EACd,aAAa,EACb,cAAc,EACd,YAAY,EACZ,iBAAiB,EACjB,WAAW,GACZ,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/dashboard/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,EACzB,KAAK,iCAAiC,EACtC,KAAK,sBAAsB,EAC3B,KAAK,4BAA4B,EACjC,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,KAAK,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAC7E,OAAO,EACL,UAAU,EACV,cAAc,EACd,aAAa,EACb,cAAc,EACd,YAAY,EACZ,iBAAiB,EACjB,WAAW,GACZ,MAAM,YAAY,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/dashboard/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/dashboard/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAML,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAA6B,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAC7E,OAAO,EACL,UAAU,EACV,cAAc,EACd,aAAa,EACb,cAAc,EACd,YAAY,EACZ,iBAAiB,EACjB,WAAW,GACZ,MAAM,YAAY,CAAC"}
|
package/dist/src/index.d.ts
CHANGED
package/dist/src/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adonis-agora/agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -178,7 +178,7 @@
|
|
|
178
178
|
"devDependencies": {
|
|
179
179
|
"@adonis-agora/authz": "0.10.1",
|
|
180
180
|
"@adonis-agora/diagnostics": "0.2.5",
|
|
181
|
-
"@adonis-agora/durable": "0.
|
|
181
|
+
"@adonis-agora/durable": "0.23.0",
|
|
182
182
|
"@adonis-agora/telescope": "0.7.0",
|
|
183
183
|
"@adonisjs/assembler": "8.4.0",
|
|
184
184
|
"@adonisjs/core": "7.4.0",
|