@adonis-agora/agent-dashboard 0.2.2 → 0.3.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 +17 -0
- package/dist/providers/agent_dashboard_provider.d.ts +4 -2
- package/dist/providers/agent_dashboard_provider.d.ts.map +1 -1
- package/dist/providers/agent_dashboard_provider.js +14 -18
- package/dist/providers/agent_dashboard_provider.js.map +1 -1
- package/dist/src/server/define_config.d.ts +17 -2
- package/dist/src/server/define_config.d.ts.map +1 -1
- package/dist/src/server/define_config.js +6 -2
- package/dist/src/server/define_config.js.map +1 -1
- package/dist/src/server/gate.d.ts +21 -0
- package/dist/src/server/gate.d.ts.map +1 -0
- package/dist/src/server/gate.js +40 -0
- package/dist/src/server/gate.js.map +1 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @adonis-agora/agent-dashboard
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#10](https://github.com/DavideCarvalho/adonis-agent/pull/10) [`c2ddde3`](https://github.com/DavideCarvalho/adonis-agent/commit/c2ddde3d4fdb5f87f3c49984c5cbbe145fbd1038) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Add an optional `authorize` gate to the dashboard config
|
|
8
|
+
|
|
9
|
+
The console serves the governance read-model, which spans EVERY actor's spend and
|
|
10
|
+
usage — so authenticating the caller (the default gate, shared with
|
|
11
|
+
`/agent/governance/*`) is often not enough; you want to restrict it to admins.
|
|
12
|
+
|
|
13
|
+
`config('agent').dashboard.authorize` is an optional `(actor, ctx) => boolean |
|
|
14
|
+
Promise<boolean>` run after the actor resolves. Return `false` (or throw) to deny
|
|
15
|
+
— the request gets `403`. Omit it to keep the previous behavior (any resolved
|
|
16
|
+
actor allowed). Typical use: `authorize: (actor) => actor.roles?.includes('ADMIN')
|
|
17
|
+
?? false`. The gate decision lives in a router-free `evaluateDashboardGate` helper
|
|
18
|
+
so it is unit tested directly.
|
|
19
|
+
|
|
3
20
|
## 0.2.2
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
|
@@ -21,8 +21,10 @@ export default class AgentDashboardProvider {
|
|
|
21
21
|
private spaDirUrl;
|
|
22
22
|
boot(): Promise<void>;
|
|
23
23
|
/**
|
|
24
|
-
* Resolve the actor through the agent config's resolver, mirroring the governance routes
|
|
25
|
-
* `true` to proceed;
|
|
24
|
+
* Resolve the actor through the agent config's resolver, mirroring the governance routes, then run
|
|
25
|
+
* the optional `authorize` gate. Returns `true` to proceed; replies `401` on a missing/failed
|
|
26
|
+
* resolver and `403` when `authorize` denies the resolved actor. The decision itself lives in the
|
|
27
|
+
* router-free {@link evaluateDashboardGate} so it can be unit tested; this only writes the response.
|
|
26
28
|
*/
|
|
27
29
|
private gate;
|
|
28
30
|
/** Read `dist/spa/index.html`, inject the API base, and send it. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent_dashboard_provider.d.ts","sourceRoot":"","sources":["../../providers/agent_dashboard_provider.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"agent_dashboard_provider.d.ts","sourceRoot":"","sources":["../../providers/agent_dashboard_provider.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAe/D;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,OAAO,OAAO,sBAAsB;IAC7B,SAAS,CAAC,GAAG,EAAE,kBAAkB;gBAAvB,GAAG,EAAE,kBAAkB;IAE7C,+FAA+F;IAC/F,OAAO,CAAC,SAAS,CAAuC;IAElD,IAAI;IAoCV;;;;;OAKG;YACW,IAAI;IAalB,oEAAoE;YACtD,SAAS;IAOvB,4FAA4F;YAC9E,SAAS;CAcxB"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import router from '@adonisjs/core/services/router';
|
|
3
|
-
import { resolveDashboardConfig } from '../src/server/define_config.js';
|
|
3
|
+
import { resolveDashboardConfig, } from '../src/server/define_config.js';
|
|
4
|
+
import { evaluateDashboardGate } from '../src/server/gate.js';
|
|
4
5
|
import { apiBaseFor, contentTypeFor, injectApiBase, mountPathFor, safeAssetSegments, } from '../src/server/paths.js';
|
|
5
6
|
/**
|
|
6
7
|
* Serves the `@adonis-agora/agent-dashboard` governance SPA (a Vite build in `dist/spa`) as static
|
|
@@ -36,16 +37,17 @@ export default class AgentDashboardProvider {
|
|
|
36
37
|
// Bare mount → canonical trailing slash so the SPA's relative `./assets/*` URLs resolve against
|
|
37
38
|
// the mount directory rather than its parent.
|
|
38
39
|
router.get(mount, (ctx) => ctx.response.redirect().toPath(`${mount}/`));
|
|
40
|
+
const authorize = dashboardConfig.authorize;
|
|
39
41
|
// The SPA shell.
|
|
40
42
|
router.get(`${mount}/`, async (ctx) => {
|
|
41
|
-
if (!(await this.gate(ctx, actorResolver)))
|
|
43
|
+
if (!(await this.gate(ctx, actorResolver, authorize)))
|
|
42
44
|
return;
|
|
43
45
|
await this.sendIndex(ctx, apiBase);
|
|
44
46
|
});
|
|
45
47
|
// Built assets (JS/CSS/fonts/...), with an index fallback for any unmatched path so the
|
|
46
48
|
// client-rendered console still boots on a deep link.
|
|
47
49
|
router.get(`${mount}/*`, async (ctx) => {
|
|
48
|
-
if (!(await this.gate(ctx, actorResolver)))
|
|
50
|
+
if (!(await this.gate(ctx, actorResolver, authorize)))
|
|
49
51
|
return;
|
|
50
52
|
const segments = safeAssetSegments(ctx.params['*']);
|
|
51
53
|
if (segments === null) {
|
|
@@ -55,24 +57,18 @@ export default class AgentDashboardProvider {
|
|
|
55
57
|
});
|
|
56
58
|
}
|
|
57
59
|
/**
|
|
58
|
-
* Resolve the actor through the agent config's resolver, mirroring the governance routes
|
|
59
|
-
* `true` to proceed;
|
|
60
|
+
* Resolve the actor through the agent config's resolver, mirroring the governance routes, then run
|
|
61
|
+
* the optional `authorize` gate. Returns `true` to proceed; replies `401` on a missing/failed
|
|
62
|
+
* resolver and `403` when `authorize` denies the resolved actor. The decision itself lives in the
|
|
63
|
+
* router-free {@link evaluateDashboardGate} so it can be unit tested; this only writes the response.
|
|
60
64
|
*/
|
|
61
|
-
async gate(ctx, actorResolver) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
try {
|
|
67
|
-
await actorResolver.resolve(ctx);
|
|
68
|
-
return true;
|
|
69
|
-
}
|
|
70
|
-
catch (error) {
|
|
71
|
-
ctx.response
|
|
72
|
-
.status(401)
|
|
73
|
-
.json({ error: error instanceof Error ? error.message : 'unauthorized' });
|
|
65
|
+
async gate(ctx, actorResolver, authorize) {
|
|
66
|
+
const verdict = await evaluateDashboardGate(ctx, actorResolver, authorize);
|
|
67
|
+
if (!verdict.ok) {
|
|
68
|
+
ctx.response.status(verdict.status).json({ error: verdict.error });
|
|
74
69
|
return false;
|
|
75
70
|
}
|
|
71
|
+
return true;
|
|
76
72
|
}
|
|
77
73
|
/** Read `dist/spa/index.html`, inject the API base, and send it. */
|
|
78
74
|
async sendIndex(ctx, apiBase) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent_dashboard_provider.js","sourceRoot":"","sources":["../../providers/agent_dashboard_provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,OAAO,MAAM,MAAM,gCAAgC,CAAC;AAEpD,OAAO,
|
|
1
|
+
{"version":3,"file":"agent_dashboard_provider.js","sourceRoot":"","sources":["../../providers/agent_dashboard_provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,17 +1,32 @@
|
|
|
1
|
+
import type { Actor } from '@adonis-agora/agent';
|
|
2
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
3
|
+
/**
|
|
4
|
+
* An extra authorization gate for the dashboard, run AFTER the agent config's `actorResolver` has
|
|
5
|
+
* resolved the caller. Return `false` (or throw) to deny — the request gets `403`. The governance
|
|
6
|
+
* read-model the SPA serves spans EVERY actor's spend/usage, so most apps restrict the console beyond
|
|
7
|
+
* mere authentication, e.g. `(actor) => actor.roles?.includes('ADMIN') ?? false`.
|
|
8
|
+
*/
|
|
9
|
+
export type AgentDashboardAuthorize = (actor: Actor, ctx: HttpContext) => boolean | Promise<boolean>;
|
|
1
10
|
/**
|
|
2
11
|
* Optional `config('agent').dashboard` block. The dashboard reuses the agent config's `path` and
|
|
3
|
-
* `actorResolver` (so it sits behind the SAME actor gating as the governance routes); this block
|
|
4
|
-
* toggles it on/off
|
|
12
|
+
* `actorResolver` (so it sits behind the SAME actor gating as the governance routes); this block
|
|
13
|
+
* toggles it on/off, optionally overrides the mount path, and optionally adds an `authorize` gate.
|
|
5
14
|
*/
|
|
6
15
|
export interface AgentDashboardConfig {
|
|
7
16
|
/** Mount the SPA. Default `true` — set `false` to keep the routes off entirely. */
|
|
8
17
|
enabled?: boolean;
|
|
9
18
|
/** Override the mount path. Default `<agentPath>/dashboard`. */
|
|
10
19
|
path?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Extra authorization run after the actor resolves. Return `false` to deny (`403`). Omit to allow
|
|
22
|
+
* any resolved actor (the default), matching the `/agent/governance/*` routes' gating.
|
|
23
|
+
*/
|
|
24
|
+
authorize?: AgentDashboardAuthorize;
|
|
11
25
|
}
|
|
12
26
|
export interface ResolvedAgentDashboardConfig {
|
|
13
27
|
enabled: boolean;
|
|
14
28
|
path?: string;
|
|
29
|
+
authorize?: AgentDashboardAuthorize;
|
|
15
30
|
}
|
|
16
31
|
/** Fill defaults for the optional dashboard config block. */
|
|
17
32
|
export declare function resolveDashboardConfig(config: AgentDashboardConfig | undefined): ResolvedAgentDashboardConfig;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define_config.d.ts","sourceRoot":"","sources":["../../../src/server/define_config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,mFAAmF;IACnF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"define_config.d.ts","sourceRoot":"","sources":["../../../src/server/define_config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,MAAM,uBAAuB,GAAG,CACpC,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,WAAW,KACb,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEhC;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,mFAAmF;IACnF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,SAAS,CAAC,EAAE,uBAAuB,CAAC;CACrC;AAED,MAAM,WAAW,4BAA4B;IAC3C,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,uBAAuB,CAAC;CACrC;AAED,6DAA6D;AAC7D,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,oBAAoB,GAAG,SAAS,GACvC,4BAA4B,CAK9B"}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
/** Fill defaults for the optional dashboard config block. */
|
|
2
2
|
export function resolveDashboardConfig(config) {
|
|
3
|
-
const
|
|
4
|
-
|
|
3
|
+
const resolved = { enabled: config?.enabled ?? true };
|
|
4
|
+
if (config?.path !== undefined)
|
|
5
|
+
resolved.path = config.path;
|
|
6
|
+
if (config?.authorize !== undefined)
|
|
7
|
+
resolved.authorize = config.authorize;
|
|
8
|
+
return resolved;
|
|
5
9
|
}
|
|
6
10
|
//# sourceMappingURL=define_config.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define_config.js","sourceRoot":"","sources":["../../../src/server/define_config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"define_config.js","sourceRoot":"","sources":["../../../src/server/define_config.ts"],"names":[],"mappings":"AAqCA,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,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ActorResolver } from '@adonis-agora/agent';
|
|
2
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
3
|
+
import type { AgentDashboardAuthorize } from './define_config.js';
|
|
4
|
+
/** The outcome of the dashboard access gate: proceed, or deny with an HTTP status + message. */
|
|
5
|
+
export type DashboardGateVerdict = {
|
|
6
|
+
ok: true;
|
|
7
|
+
} | {
|
|
8
|
+
ok: false;
|
|
9
|
+
status: 401 | 403;
|
|
10
|
+
error: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Decide whether a request may reach the dashboard, WITHOUT touching the response — so it is unit
|
|
14
|
+
* testable free of the AdonisJS router/app. Mirrors the `/agent/governance/*` gating: resolve the
|
|
15
|
+
* actor through the agent config's resolver (missing/failed → `401`), then run the optional
|
|
16
|
+
* `authorize` gate over the resolved actor (denied/threw → `403`). No resolver and no `authorize`
|
|
17
|
+
* means the SPA is exposed to any request — the caller (the provider) is expected to short-circuit on
|
|
18
|
+
* a missing resolver via the `401` here.
|
|
19
|
+
*/
|
|
20
|
+
export declare function evaluateDashboardGate(ctx: HttpContext, actorResolver: ActorResolver | undefined, authorize?: AgentDashboardAuthorize): Promise<DashboardGateVerdict>;
|
|
21
|
+
//# sourceMappingURL=gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../../src/server/gate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAElE,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;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,WAAW,EAChB,aAAa,EAAE,aAAa,GAAG,SAAS,EACxC,SAAS,CAAC,EAAE,uBAAuB,GAClC,OAAO,CAAC,oBAAoB,CAAC,CA4B/B"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decide whether a request may reach the dashboard, WITHOUT touching the response — so it is unit
|
|
3
|
+
* testable free of the AdonisJS router/app. Mirrors the `/agent/governance/*` gating: resolve the
|
|
4
|
+
* actor through the agent config's resolver (missing/failed → `401`), then run the optional
|
|
5
|
+
* `authorize` gate over the resolved actor (denied/threw → `403`). No resolver and no `authorize`
|
|
6
|
+
* means the SPA is exposed to any request — the caller (the provider) is expected to short-circuit on
|
|
7
|
+
* a missing resolver via the `401` here.
|
|
8
|
+
*/
|
|
9
|
+
export async function evaluateDashboardGate(ctx, actorResolver, authorize) {
|
|
10
|
+
if (actorResolver === undefined) {
|
|
11
|
+
return { ok: false, status: 401, error: 'no actor resolver configured' };
|
|
12
|
+
}
|
|
13
|
+
let actor;
|
|
14
|
+
try {
|
|
15
|
+
actor = await actorResolver.resolve(ctx);
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
return {
|
|
19
|
+
ok: false,
|
|
20
|
+
status: 401,
|
|
21
|
+
error: error instanceof Error ? error.message : 'unauthorized',
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
if (authorize !== undefined) {
|
|
25
|
+
try {
|
|
26
|
+
if (!(await authorize(actor, ctx))) {
|
|
27
|
+
return { ok: false, status: 403, error: 'forbidden' };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
return {
|
|
32
|
+
ok: false,
|
|
33
|
+
status: 403,
|
|
34
|
+
error: error instanceof Error ? error.message : 'forbidden',
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return { ok: true };
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../../../src/server/gate.ts"],"names":[],"mappings":"AAOA;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,GAAgB,EAChB,aAAwC,EACxC,SAAmC;IAEnC,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;QAChC,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,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,GAAG;YACX,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc;SAC/D,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,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW;aAC5D,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AACtB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adonis-agora/agent-dashboard",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Read-only governance console (spend by model/actor, usage trend, recent threads & tool calls, quota) for @adonis-agora/agent — a dependency-light React SPA served by a thin AdonisJS provider. Part of the Agora ecosystem.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"typescript": "^5.4.0",
|
|
58
58
|
"vite": "^7.0.0",
|
|
59
59
|
"vitest": "^3.0.0",
|
|
60
|
-
"@adonis-agora/agent": "0.4.
|
|
60
|
+
"@adonis-agora/agent": "0.4.1"
|
|
61
61
|
},
|
|
62
62
|
"engines": {
|
|
63
63
|
"node": ">=22.0.0"
|