@forestadmin/agent-bff 1.21.0 → 1.22.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/README.md CHANGED
@@ -93,6 +93,7 @@ yarn start:dev # node --env-file=.env dist/cli.js
93
93
  | `HTTP_PORT` | no | Server port, integer 0–65535. Defaults to `3450`. `0` binds an OS-assigned ephemeral port. |
94
94
  | `BFF_ALLOWED_ORIGINS`| no | Comma-separated CORS allow-list of exact origins (scheme + host + port). No wildcard. Empty ⇒ no cross-origin browser access. |
95
95
  | `BFF_DEFAULT_TIMEZONE`| no | Fallback IANA timezone used when a request carries neither an `X-Forest-Timezone` header nor a body `timezone`. |
96
+ | `BFF_AI_TIMEOUT_MS` | no | How long `POST /agent/v1/ai/query` waits for the Forest server on the relay itself, in milliseconds. Defaults to `120000` — an AI generation is slow, and neither the app nor the Forest server bounds it. Past it the route answers `504`. It is not the route's end-to-end cap: an expired session is refreshed first, under a separate hard-coded 60 s ceiling that answers `502`, so a request can exceed this value. A malformed value (non-integer, `0`, or above 2147483647) fails the boot, unlike an absent one which takes the default. |
96
97
  | `BFF_OPENAPI_ENABLED` | no | Serve `GET/HEAD /agent/openapi.json` (auth-gated) when `true`. Defaults to `true`. Set to `false` for customers who do not want the HTTP surface exposed: an authenticated `GET`/`HEAD` then gets `404 openapi_disabled`, other methods fall through to the agent routes exactly as they do when enabled, and `forest-bff openapi` keeps working either way. Accepted values: `true`/`false`. **The served document is unfolded and is not filtered per caller**: any authenticated caller, whatever their role, reads the name of every exposed collection, relation and field. Set this to `false` if that surface must not be reachable over HTTP. |
97
98
 
98
99
  ### Config validation
@@ -111,6 +112,13 @@ Every agent call flows through a request edge that enforces three cross-cutting
111
112
  call is proxied to the agent. Errors use a structured, type-first contract — `{ error: { type, status,
112
113
  message, details? } }` — so consumers branch on `error.type`, never on message text.
113
114
 
115
+ Two paths sit before the timezone layer and so skip it: `/agent/v1/context`, and the AI relay
116
+ described below. Neither reaches an agent, so neither has a timezone to resolve. This holds only
117
+ where they are actually mounted — `/agent/v1/context` needs the read-model bundle and the AI relay
118
+ needs an OAuth session. In a deployment missing either, the request falls through to the timezone
119
+ layer instead, so a caller sending no timezone against a BFF with no `BFF_DEFAULT_TIMEZONE` gets
120
+ `400 missing_timezone` rather than the `404` it would expect from an unmounted route.
121
+
114
122
  ### Auth-mode precedence
115
123
 
116
124
  | Presented credentials | Result |
@@ -142,9 +150,38 @@ normalized away, no trailing slash, no wildcard, no subdomain matching):
142
150
 
143
151
  ### Timezone
144
152
 
145
- The BFF always forwards an explicit `timezone` to the agent, resolved in order: (1) `X-Forest-Timezone`
146
- header, (2) body `timezone` field, (3) `BFF_DEFAULT_TIMEZONE`. None → `400 missing_timezone`; a
147
- non-IANA value → `400 invalid_timezone`.
153
+ On every route that reaches an agent, the BFF forwards an explicit `timezone`, resolved in order:
154
+ (1) `X-Forest-Timezone` header, (2) body `timezone` field, (3) `BFF_DEFAULT_TIMEZONE`. None →
155
+ `400 missing_timezone`; a non-IANA value → `400 invalid_timezone`. The two paths listed above are
156
+ mounted ahead of this layer and answer without it.
157
+
158
+ ### AI query relay
159
+
160
+ `POST /agent/v1/ai/query` relays its body to the Forest AI proxy under the OAuth session the BFF
161
+ holds, and relays the answer back:
162
+
163
+ - **Session only.** An API key is refused with `403 oauth_required`: only the OAuth flow yields the
164
+ Forest access token this route forwards. The environment and rendering are derived server-side, so
165
+ sending `forest-*` headers has no effect.
166
+ - **Body.** `1mb` instead of the `16kb` of every other route, and `application/json` only — any other
167
+ content type arrives as an empty body, is relayed upstream as `{}`, and the Forest server rejects
168
+ the query. Over the limit → `413`. The parser is selected on the request path alone and runs ahead
169
+ of authentication, so an unauthenticated caller can make the BFF buffer and JSON-parse 1 MB on this
170
+ path — 64x the pre-auth surface of every other route. Bounding that is a deployment concern (rate
171
+ limiting or a body cap at the reverse proxy), not a BFF one.
172
+ - **Timeout.** `BFF_AI_TIMEOUT_MS` (default `120000`) caps the relay itself; past it → `504`. It is
173
+ not the only cap on the request: when the session's access token has expired the route first
174
+ refreshes it against the Forest server, and that hop has its own hard-coded 60 s ceiling which
175
+ answers `502 network_error`, not `504`.
176
+ - **Error shape.** Below 500 the upstream JSON body is relayed unchanged, so its shape is the Forest
177
+ AI proxy's contract and not the BFF's; when the upstream answer carried no JSON body the BFF
178
+ substitutes its own envelope, keeping the status only when it was 4xx and reporting `502` for a
179
+ 2xx or 3xx that carried nothing to relay. At 500 and above the body is always the BFF
180
+ envelope, so no upstream infrastructure detail reaches the caller. **This is the one route where a
181
+ sub-500 body is not guaranteed to be `{ error: { type, … } }`**, so a consumer cannot branch on
182
+ `error.type` here without checking that the field exists.
183
+ - **Mounted only with OAuth.** Without a complete OAuth configuration the route is absent (`404`) and
184
+ `forest-bff openapi` omits it from the document.
148
185
 
149
186
  ## Sessions & token rotation
150
187
 
@@ -0,0 +1,26 @@
1
+ export { default as AiProxyTimeoutError } from './ai-proxy-timeout-error';
2
+ export interface AiProxyClientOptions {
3
+ forestServerUrl: string;
4
+ timeoutMs: number;
5
+ }
6
+ export interface AiQueryParams {
7
+ saasAccessToken: string;
8
+ environmentId?: number;
9
+ renderingId: number;
10
+ body: unknown;
11
+ }
12
+ export interface AiProxyResponse {
13
+ status: number;
14
+ body: unknown;
15
+ isJson: boolean;
16
+ unparseableBodyError?: unknown;
17
+ }
18
+ export default class AiProxyClient {
19
+ private readonly forestServerUrl;
20
+ private readonly timeoutMs;
21
+ constructor({ forestServerUrl, timeoutMs }: AiProxyClientOptions);
22
+ private url;
23
+ private static headers;
24
+ query(params: AiQueryParams): Promise<AiProxyResponse>;
25
+ }
26
+ //# sourceMappingURL=ai-proxy-client.d.ts.map
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.AiProxyTimeoutError = void 0;
7
+ const ai_proxy_timeout_error_1 = __importDefault(require("./ai-proxy-timeout-error"));
8
+ var ai_proxy_timeout_error_2 = require("./ai-proxy-timeout-error");
9
+ Object.defineProperty(exports, "AiProxyTimeoutError", { enumerable: true, get: function () { return __importDefault(ai_proxy_timeout_error_2).default; } });
10
+ const AI_NAME = 'zendesk';
11
+ const AI_QUERY_PATH = '/api/ai-proxy/ai-query';
12
+ const JSON_CONTENT_TYPE = 'application/json';
13
+ const TIMEOUT_ERROR_NAME = 'TimeoutError';
14
+ function isTimeout(error) {
15
+ if (typeof error !== 'object' || error === null)
16
+ return false;
17
+ return error.name === TIMEOUT_ERROR_NAME;
18
+ }
19
+ class AiProxyClient {
20
+ constructor({ forestServerUrl, timeoutMs }) {
21
+ this.forestServerUrl = forestServerUrl;
22
+ this.timeoutMs = timeoutMs;
23
+ }
24
+ url() {
25
+ const url = new URL(AI_QUERY_PATH, this.forestServerUrl);
26
+ url.searchParams.set('ai-name', AI_NAME);
27
+ return url.toString();
28
+ }
29
+ static headers({ saasAccessToken, environmentId, renderingId }) {
30
+ const headers = {
31
+ Authorization: `Bearer ${saasAccessToken}`,
32
+ 'Content-Type': JSON_CONTENT_TYPE,
33
+ 'forest-rendering-id': String(renderingId),
34
+ };
35
+ if (environmentId !== undefined)
36
+ headers['forest-environment-id'] = String(environmentId);
37
+ return headers;
38
+ }
39
+ async query(params) {
40
+ let response;
41
+ try {
42
+ response = await fetch(this.url(), {
43
+ method: 'POST',
44
+ headers: AiProxyClient.headers(params),
45
+ body: JSON.stringify(params.body),
46
+ signal: AbortSignal.timeout(this.timeoutMs),
47
+ });
48
+ }
49
+ catch (error) {
50
+ if (isTimeout(error))
51
+ throw new ai_proxy_timeout_error_1.default(error);
52
+ throw error;
53
+ }
54
+ const contentType = response.headers.get('content-type')?.toLowerCase() ?? '';
55
+ const declaresJson = contentType.includes(JSON_CONTENT_TYPE);
56
+ if (!declaresJson)
57
+ return { status: response.status, body: undefined, isJson: false };
58
+ try {
59
+ return { status: response.status, body: await response.json(), isJson: true };
60
+ }
61
+ catch (error) {
62
+ if (isTimeout(error))
63
+ throw new ai_proxy_timeout_error_1.default(error);
64
+ return {
65
+ status: response.status,
66
+ body: undefined,
67
+ isJson: false,
68
+ unparseableBodyError: error,
69
+ };
70
+ }
71
+ }
72
+ }
73
+ exports.default = AiProxyClient;
74
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWktcHJveHktY2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FpL2FpLXByb3h5LWNsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSxzRkFBMkQ7QUFFM0QsbUVBQTBFO0FBQWpFLDhJQUFBLE9BQU8sT0FBdUI7QUFFdkMsTUFBTSxPQUFPLEdBQUcsU0FBUyxDQUFDO0FBQzFCLE1BQU0sYUFBYSxHQUFHLHdCQUF3QixDQUFDO0FBQy9DLE1BQU0saUJBQWlCLEdBQUcsa0JBQWtCLENBQUM7QUFDN0MsTUFBTSxrQkFBa0IsR0FBRyxjQUFjLENBQUM7QUFFMUMsU0FBUyxTQUFTLENBQUMsS0FBYztJQUMvQixJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxLQUFLLEtBQUssSUFBSTtRQUFFLE9BQU8sS0FBSyxDQUFDO0lBRTlELE9BQVEsS0FBNEIsQ0FBQyxJQUFJLEtBQUssa0JBQWtCLENBQUM7QUFDbkUsQ0FBQztBQXFCRCxNQUFxQixhQUFhO0lBSWhDLFlBQVksRUFBRSxlQUFlLEVBQUUsU0FBUyxFQUF3QjtRQUM5RCxJQUFJLENBQUMsZUFBZSxHQUFHLGVBQWUsQ0FBQztRQUN2QyxJQUFJLENBQUMsU0FBUyxHQUFHLFNBQVMsQ0FBQztJQUM3QixDQUFDO0lBRU8sR0FBRztRQUNULE1BQU0sR0FBRyxHQUFHLElBQUksR0FBRyxDQUFDLGFBQWEsRUFBRSxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUM7UUFDekQsR0FBRyxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsU0FBUyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBRXpDLE9BQU8sR0FBRyxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQ3hCLENBQUM7SUFFTyxNQUFNLENBQUMsT0FBTyxDQUFDLEVBQUUsZUFBZSxFQUFFLGFBQWEsRUFBRSxXQUFXLEVBQWlCO1FBQ25GLE1BQU0sT0FBTyxHQUEyQjtZQUN0QyxhQUFhLEVBQUUsVUFBVSxlQUFlLEVBQUU7WUFDMUMsY0FBYyxFQUFFLGlCQUFpQjtZQUNqQyxxQkFBcUIsRUFBRSxNQUFNLENBQUMsV0FBVyxDQUFDO1NBQzNDLENBQUM7UUFFRixJQUFJLGFBQWEsS0FBSyxTQUFTO1lBQUUsT0FBTyxDQUFDLHVCQUF1QixDQUFDLEdBQUcsTUFBTSxDQUFDLGFBQWEsQ0FBQyxDQUFDO1FBRTFGLE9BQU8sT0FBTyxDQUFDO0lBQ2pCLENBQUM7SUFFRCxLQUFLLENBQUMsS0FBSyxDQUFDLE1BQXFCO1FBQy9CLElBQUksUUFBa0IsQ0FBQztRQUV2QixJQUFJLENBQUM7WUFDSCxRQUFRLEdBQUcsTUFBTSxLQUFLLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxFQUFFO2dCQUNqQyxNQUFNLEVBQUUsTUFBTTtnQkFDZCxPQUFPLEVBQUUsYUFBYSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUM7Z0JBQ3RDLElBQUksRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUM7Z0JBQ2pDLE1BQU0sRUFBRSxXQUFXLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUM7YUFDNUMsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixJQUFJLFNBQVMsQ0FBQyxLQUFLLENBQUM7Z0JBQUUsTUFBTSxJQUFJLGdDQUFtQixDQUFDLEtBQUssQ0FBQyxDQUFDO1lBQzNELE1BQU0sS0FBSyxDQUFDO1FBQ2QsQ0FBQztRQUVELE1BQU0sV0FBVyxHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLGNBQWMsQ0FBQyxFQUFFLFdBQVcsRUFBRSxJQUFJLEVBQUUsQ0FBQztRQUM5RSxNQUFNLFlBQVksR0FBRyxXQUFXLENBQUMsUUFBUSxDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFFN0QsSUFBSSxDQUFDLFlBQVk7WUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLFFBQVEsQ0FBQyxNQUFNLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLENBQUM7UUFFdEYsSUFBSSxDQUFDO1lBQ0gsT0FBTyxFQUFFLE1BQU0sRUFBRSxRQUFRLENBQUMsTUFBTSxFQUFFLElBQUksRUFBRSxNQUFNLFFBQVEsQ0FBQyxJQUFJLEVBQUUsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLENBQUM7UUFDaEYsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixJQUFJLFNBQVMsQ0FBQyxLQUFLLENBQUM7Z0JBQUUsTUFBTSxJQUFJLGdDQUFtQixDQUFDLEtBQUssQ0FBQyxDQUFDO1lBRTNELE9BQU87Z0JBQ0wsTUFBTSxFQUFFLFFBQVEsQ0FBQyxNQUFNO2dCQUN2QixJQUFJLEVBQUUsU0FBUztnQkFDZixNQUFNLEVBQUUsS0FBSztnQkFDYixvQkFBb0IsRUFBRSxLQUFLO2FBQzVCLENBQUM7UUFDSixDQUFDO0lBQ0gsQ0FBQztDQUNGO0FBN0RELGdDQTZEQyJ9
@@ -0,0 +1,5 @@
1
+ export default class AiProxyTimeoutError extends Error {
2
+ readonly cause?: unknown;
3
+ constructor(cause?: unknown);
4
+ }
5
+ //# sourceMappingURL=ai-proxy-timeout-error.d.ts.map
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ class AiProxyTimeoutError extends Error {
4
+ constructor(cause) {
5
+ super('The Forest server did not respond in time');
6
+ this.name = 'AiProxyTimeoutError';
7
+ this.cause = cause;
8
+ }
9
+ }
10
+ exports.default = AiProxyTimeoutError;
11
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWktcHJveHktdGltZW91dC1lcnJvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9haS9haS1wcm94eS10aW1lb3V0LWVycm9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsTUFBcUIsbUJBQW9CLFNBQVEsS0FBSztJQUdwRCxZQUFZLEtBQWU7UUFDekIsS0FBSyxDQUFDLDJDQUEyQyxDQUFDLENBQUM7UUFDbkQsSUFBSSxDQUFDLElBQUksR0FBRyxxQkFBcUIsQ0FBQztRQUNsQyxJQUFJLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQztJQUNyQixDQUFDO0NBQ0Y7QUFSRCxzQ0FRQyJ9
@@ -0,0 +1,15 @@
1
+ import type AiProxyClient from './ai-proxy-client';
2
+ import type ForestServerClient from '../oauth/forest-server-client';
3
+ import type { SessionStore } from '../oauth/session-store';
4
+ import type { Logger } from '../ports/logger-port';
5
+ import type { Middleware } from 'koa';
6
+ export declare const AI_QUERY_ROUTE = "/agent/v1/ai/query";
7
+ export interface AiRoutesMiddlewareOptions {
8
+ client: AiProxyClient;
9
+ sessionStore: SessionStore;
10
+ serverClient: ForestServerClient;
11
+ environmentId?: number;
12
+ logger: Logger;
13
+ }
14
+ export default function createAiRoutesMiddleware({ client, sessionStore, serverClient, environmentId, logger, }: AiRoutesMiddlewareOptions): Middleware;
15
+ //# sourceMappingURL=ai-routes-middleware.d.ts.map
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.AI_QUERY_ROUTE = void 0;
7
+ exports.default = createAiRoutesMiddleware;
8
+ const ai_proxy_client_1 = require("./ai-proxy-client");
9
+ const auth_mode_1 = require("../auth/auth-mode");
10
+ const bff_http_error_1 = require("../http/bff-http-error");
11
+ const bff_local_errors_1 = require("../http/bff-local-errors");
12
+ const oauth_error_1 = require("../oauth/oauth-error");
13
+ const session_lifecycle_1 = __importDefault(require("../oauth/session-lifecycle"));
14
+ exports.AI_QUERY_ROUTE = '/agent/v1/ai/query';
15
+ const JSON_CONTENT_TYPE = 'application/json';
16
+ const MAX_CAUSE_DEPTH = 3;
17
+ const MAX_CAUSE_MESSAGE = 512;
18
+ function requireSessionPrincipal(ctx) {
19
+ if (ctx.state.authMode === 'api-key')
20
+ throw (0, bff_local_errors_1.oauthRequired)();
21
+ const principal = ctx.state.principal;
22
+ if (!principal)
23
+ throw (0, bff_http_error_1.unauthorized)('No session for this request');
24
+ return principal;
25
+ }
26
+ function clamp(message) {
27
+ return message.length > MAX_CAUSE_MESSAGE ? `${message.slice(0, MAX_CAUSE_MESSAGE)}…` : message;
28
+ }
29
+ function describeFailure(error, depthLeft = MAX_CAUSE_DEPTH) {
30
+ if (!(error instanceof Error))
31
+ return clamp(String(error));
32
+ const { cause } = error;
33
+ const walkable = depthLeft > 0 && cause !== undefined;
34
+ const tail = walkable ? ` (cause: ${describeFailure(cause, depthLeft - 1)})` : '';
35
+ return `${error.name}: ${clamp(error.message)}${tail}`;
36
+ }
37
+ async function resolveSessionAccessToken(principal, store, serverClient, logger) {
38
+ try {
39
+ return await (0, session_lifecycle_1.default)({ sid: principal.sid, store, serverClient });
40
+ }
41
+ catch (error) {
42
+ if (error instanceof oauth_error_1.OAuthRequestError && error.status >= 500) {
43
+ logger('Warn', 'AI query refused: the Forest server could not refresh the session', {
44
+ cause: describeFailure(error),
45
+ });
46
+ throw (0, bff_local_errors_1.upstreamUnreachable)();
47
+ }
48
+ throw error;
49
+ }
50
+ }
51
+ function relayJsonBodyOrGenericError(ctx, response, logger) {
52
+ const bodyWouldLeakInfrastructure = response.status >= 500 || !response.isJson;
53
+ if (bodyWouldLeakInfrastructure) {
54
+ logger('Warn', 'AI proxy upstream body withheld; client message is generic', {
55
+ status: response.status,
56
+ isJson: response.isJson,
57
+ ...(response.unparseableBodyError !== undefined
58
+ ? { cause: describeFailure(response.unparseableBodyError) }
59
+ : {}),
60
+ });
61
+ throw (0, bff_local_errors_1.upstreamError)(response.status);
62
+ }
63
+ ctx.status = response.status;
64
+ ctx.type = JSON_CONTENT_TYPE;
65
+ ctx.body = JSON.stringify(response.body);
66
+ }
67
+ function createAiRoutesMiddleware({ client, sessionStore, serverClient, environmentId, logger, }) {
68
+ return async function aiRoutesMiddleware(ctx, next) {
69
+ if (ctx.path !== exports.AI_QUERY_ROUTE || ctx.method !== 'POST') {
70
+ await next();
71
+ return;
72
+ }
73
+ const principal = requireSessionPrincipal(ctx);
74
+ const renderingId = (0, auth_mode_1.requireRenderingId)(principal);
75
+ const saasAccessToken = await resolveSessionAccessToken(principal, sessionStore, serverClient, logger);
76
+ let response;
77
+ try {
78
+ response = await client.query({
79
+ saasAccessToken,
80
+ environmentId,
81
+ renderingId,
82
+ body: ctx.request.body,
83
+ });
84
+ }
85
+ catch (error) {
86
+ logger('Warn', 'AI query failed against the Forest server', {
87
+ cause: describeFailure(error),
88
+ });
89
+ if (error instanceof ai_proxy_client_1.AiProxyTimeoutError)
90
+ throw (0, bff_local_errors_1.upstreamTimeout)();
91
+ throw (0, bff_local_errors_1.upstreamUnreachable)();
92
+ }
93
+ relayJsonBodyOrGenericError(ctx, response, logger);
94
+ };
95
+ }
96
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWktcm91dGVzLW1pZGRsZXdhcmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYWkvYWktcm91dGVzLW1pZGRsZXdhcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBc0dBLDJDQTZDQztBQTNJRCx1REFBd0Q7QUFDeEQsaURBQXVEO0FBQ3ZELDJEQUFzRDtBQUN0RCwrREFLa0M7QUFDbEMsc0RBQXlEO0FBQ3pELG1GQUFpRTtBQUVwRCxRQUFBLGNBQWMsR0FBRyxvQkFBb0IsQ0FBQztBQUVuRCxNQUFNLGlCQUFpQixHQUFHLGtCQUFrQixDQUFDO0FBQzdDLE1BQU0sZUFBZSxHQUFHLENBQUMsQ0FBQztBQUMxQixNQUFNLGlCQUFpQixHQUFHLEdBQUcsQ0FBQztBQVU5QixTQUFTLHVCQUF1QixDQUFDLEdBQVk7SUFDM0MsSUFBSSxHQUFHLENBQUMsS0FBSyxDQUFDLFFBQVEsS0FBSyxTQUFTO1FBQUUsTUFBTSxJQUFBLGdDQUFhLEdBQUUsQ0FBQztJQUU1RCxNQUFNLFNBQVMsR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLFNBQThDLENBQUM7SUFDM0UsSUFBSSxDQUFDLFNBQVM7UUFBRSxNQUFNLElBQUEsNkJBQVksRUFBQyw2QkFBNkIsQ0FBQyxDQUFDO0lBRWxFLE9BQU8sU0FBUyxDQUFDO0FBQ25CLENBQUM7QUFFRCxTQUFTLEtBQUssQ0FBQyxPQUFlO0lBQzVCLE9BQU8sT0FBTyxDQUFDLE1BQU0sR0FBRyxpQkFBaUIsQ0FBQyxDQUFDLENBQUMsR0FBRyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQztBQUNsRyxDQUFDO0FBRUQsU0FBUyxlQUFlLENBQUMsS0FBYyxFQUFFLFNBQVMsR0FBRyxlQUFlO0lBQ2xFLElBQUksQ0FBQyxDQUFDLEtBQUssWUFBWSxLQUFLLENBQUM7UUFBRSxPQUFPLEtBQUssQ0FBQyxNQUFNLENBQUMs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
@@ -7,9 +7,8 @@ exports.AGENT_TOKEN_EXPIRES_IN = void 0;
7
7
  exports.issueAgentToken = issueAgentToken;
8
8
  exports.issueAgentTokenFromPrincipal = issueAgentTokenFromPrincipal;
9
9
  const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
10
- const bff_http_error_1 = require("../http/bff-http-error");
10
+ const auth_mode_1 = require("../auth/auth-mode");
11
11
  exports.AGENT_TOKEN_EXPIRES_IN = '5m';
12
- const POSITIVE_INTEGER = /^[1-9]\d*$/;
13
12
  function tagsToRecord(tags) {
14
13
  return tags.reduce((memo, { key, value }) => ({ ...memo, [key]: value }), {});
15
14
  }
@@ -40,19 +39,17 @@ function issueAgentToken({ identity, authSecret }) {
40
39
  }, authSecret);
41
40
  }
42
41
  function issueAgentTokenFromPrincipal({ principal, authSecret, }) {
43
- if (!POSITIVE_INTEGER.test(String(principal.rendering_id))) {
44
- throw (0, bff_http_error_1.unauthorized)('The session carries no usable rendering');
45
- }
42
+ const renderingId = (0, auth_mode_1.requireRenderingId)(principal);
46
43
  return signWithSnakeCaseAliasesForRubyAndPythonAgents({
47
44
  id: principal.id,
48
45
  email: principal.email,
49
46
  firstName: principal.first_name,
50
47
  lastName: principal.last_name,
51
48
  team: principal.team,
52
- renderingId: Number(principal.rendering_id),
49
+ renderingId,
53
50
  tags: principal.tags ?? {},
54
51
  permissionLevel: principal.permission_level,
55
52
  role: principal.role,
56
53
  }, authSecret);
57
54
  }
58
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdlbnQtdG9rZW4uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYXBpLWtleS9hZ2VudC10b2tlbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUEyREEsMENBZ0JDO0FBRUQsb0VBc0JDO0FBaEdELGdFQUF3QztBQUV4QywyREFBc0Q7QUFFekMsUUFBQSxzQkFBc0IsR0FBRyxJQUFJLENBQUM7QUFZM0MsTUFBTSxnQkFBZ0IsR0FBRyxZQUFZLENBQUM7QUFFdEMsU0FBUyxZQUFZLENBQUMsSUFBc0M7SUFDMUQsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsR0FBRyxFQUFFLEtBQUssRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDLEVBQUUsR0FBRyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsRUFBRSxLQUFLLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBQ2hGLENBQUM7QUFjRCxTQUFTLDhDQUE4QyxDQUNyRCxNQUF5QixFQUN6QixVQUFrQjtJQUVsQixNQUFNLFNBQVMsR0FBRyxNQUFNLENBQUMsU0FBUyxJQUFJLEVBQUUsQ0FBQztJQUN6QyxNQUFNLFFBQVEsR0FBRyxNQUFNLENBQUMsUUFBUSxJQUFJLEVBQUUsQ0FBQztJQUV2QyxPQUFPLHNCQUFZLENBQUMsSUFBSSxDQUN0QjtRQUNFLEdBQUcsTUFBTTtRQUNULFNBQVM7UUFDVCxRQUFRO1FBQ1IsVUFBVSxFQUFFLFNBQVM7UUFDckIsU0FBUyxFQUFFLFFBQVE7UUFDbkIsWUFBWSxFQUFFLE1BQU0sQ0FBQyxXQUFXO1FBQ2hDLGdCQUFnQixFQUFFLE1BQU0sQ0FBQyxlQUFlO0tBQ3pDLEVBQ0QsVUFBVSxFQUNWLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsOEJBQXNCLEVBQUUsQ0FDMUQsQ0FBQztBQUNKLENBQUM7QUFFRCxTQUFnQixlQUFlLENBQUMsRUFBRSxRQUFRLEVBQUUsVUFBVSxFQUF5QjtJQUM3RSxNQUFNLEVBQUUsSUFBSSxFQUFFLFdBQVcsRUFBRSxHQUFHLFFBQVEsQ0FBQztJQUV2QyxPQUFPLDhDQUE4QyxDQUNuRDtRQUNFLEVBQUUsRUFBRSxJQUFJLENBQUMsRUFBRTtRQUNYLEtBQUssRUFBRSxJQUFJLENBQUMsS0FBSztRQUNqQixTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVM7UUFDekIsUUFBUSxFQUFFLElBQUksQ0FBQyxRQUFRO1FBQ3ZCLElBQUksRUFBRSxJQUFJLENBQUMsSUFBSTtRQUNmLFdBQVc7UUFDWCxJQUFJLEVBQUUsWUFBWSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUM7UUFDN0IsZUFBZSxFQUFFLElBQUksQ0FBQyxlQUFlO0tBQ3RDLEVBQ0QsVUFBVSxDQUNYLENBQUM7QUFDSixDQUFDO0FBRUQsU0FBZ0IsNEJBQTRCLENBQUMsRUFDM0MsU0FBUyxFQUNULFVBQVUsR0FDeUI7SUFDbkMsSUFBSSxDQUFDLGdCQUFnQixDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLFlBQVksQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUMzRCxNQUFNLElBQUEsNkJBQVksRUFBQyx5Q0FBeUMsQ0FBQyxDQUFDO0lBQ2hFLENBQUM7SUFFRCxPQUFPLDhDQUE4QyxDQUNuRDtRQUNFLEVBQUUsRUFBRSxTQUFTLENBQUMsRUFBRTtRQUNoQixLQUFLLEVBQUUsU0FBUyxDQUFDLEtBQUs7UUFDdEIsU0FBUyxFQUFFLFNBQVMsQ0FBQyxVQUFVO1FBQy9CLFFBQVEsRUFBRSxTQUFTLENBQUMsU0FBUztRQUM3QixJQUFJLEVBQUUsU0FBUyxDQUFDLElBQUk7UUFDcEIsV0FBVyxFQUFFLE1BQU0sQ0FBQyxTQUFTLENBQUMsWUFBWSxDQUFDO1FBQzNDLElBQUksRUFBRSxTQUFTLENBQUMsSUFBSSxJQUFJLEVBQUU7UUFDMUIsZUFBZSxFQUFFLFNBQVMsQ0FBQyxnQkFBZ0I7UUFDM0MsSUFBSSxFQUFFLFNBQVMsQ0FBQyxJQUFJO0tBQ3JCLEVBQ0QsVUFBVSxDQUNYLENBQUM7QUFDSixDQUFDIn0=
55
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdlbnQtdG9rZW4uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYXBpLWtleS9hZ2VudC10b2tlbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUF5REEsMENBZ0JDO0FBRUQsb0VBb0JDO0FBNUZELGdFQUF3QztBQUV4QyxpREFBdUQ7QUFFMUMsUUFBQSxzQkFBc0IsR0FBRyxJQUFJLENBQUM7QUFZM0MsU0FBUyxZQUFZLENBQUMsSUFBc0M7SUFDMUQsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsR0FBRyxFQUFFLEtBQUssRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDLEVBQUUsR0FBRyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsRUFBRSxLQUFLLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBQ2hGLENBQUM7QUFjRCxTQUFTLDhDQUE4QyxDQUNyRCxNQUF5QixFQUN6QixVQUFrQjtJQUVsQixNQUFNLFNBQVMsR0FBRyxNQUFNLENBQUMsU0FBUyxJQUFJLEVBQUUsQ0FBQztJQUN6QyxNQUFNLFFBQVEsR0FBRyxNQUFNLENBQUMsUUFBUSxJQUFJLEVBQUUsQ0FBQztJQUV2QyxPQUFPLHNCQUFZLENBQUMsSUFBSSxDQUN0QjtRQUNFLEdBQUcsTUFBTTtRQUNULFNBQVM7UUFDVCxRQUFRO1FBQ1IsVUFBVSxFQUFFLFNBQVM7UUFDckIsU0FBUyxFQUFFLFFBQVE7UUFDbkIsWUFBWSxFQUFFLE1BQU0sQ0FBQyxXQUFXO1FBQ2hDLGdCQUFnQixFQUFFLE1BQU0sQ0FBQyxlQUFlO0tBQ3pDLEVBQ0QsVUFBVSxFQUNWLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsOEJBQXNCLEVBQUUsQ0FDMUQsQ0FBQztBQUNKLENBQUM7QUFFRCxTQUFnQixlQUFlLENBQUMsRUFBRSxRQUFRLEVBQUUsVUFBVSxFQUF5QjtJQUM3RSxNQUFNLEVBQUUsSUFBSSxFQUFFLFdBQVcsRUFBRSxHQUFHLFFBQVEsQ0FBQztJQUV2QyxPQUFPLDhDQUE4QyxDQUNuRDtRQUNFLEVBQUUsRUFBRSxJQUFJLENBQUMsRUFBRTtRQUNYLEtBQUssRUFBRSxJQUFJLENBQUMsS0FBSztRQUNqQixTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVM7UUFDekIsUUFBUSxFQUFFLElBQUksQ0FBQyxRQUFRO1FBQ3ZCLElBQUksRUFBRSxJQUFJLENBQUMsSUFBSTtRQUNmLFdBQVc7UUFDWCxJQUFJLEVBQUUsWUFBWSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUM7UUFDN0IsZUFBZSxFQUFFLElBQUksQ0FBQyxlQUFlO0tBQ3RDLEVBQ0QsVUFBVSxDQUNYLENBQUM7QUFDSixDQUFDO0FBRUQsU0FBZ0IsNEJBQTRCLENBQUMsRUFDM0MsU0FBUyxFQUNULFVBQVUsR0FDeUI7SUFDbkMsTUFBTSxXQUFXLEdBQUcsSUFBQSw4QkFBa0IsRUFBQyxTQUFTLENBQUMsQ0FBQztJQUVsRCxPQUFPLDhDQUE4QyxDQUNuRDtRQUNFLEVBQUUsRUFBRSxTQUFTLENBQUMsRUFBRTtRQUNoQixLQUFLLEVBQUUsU0FBUyxDQUFDLEtBQUs7UUFDdEIsU0FBUyxFQUFFLFNBQVMsQ0FBQyxVQUFVO1FBQy9CLFFBQVEsRUFBRSxTQUFTLENBQUMsU0FBUztRQUM3QixJQUFJLEVBQUUsU0FBUyxDQUFDLElBQUk7UUFDcEIsV0FBVztRQUNYLElBQUksRUFBRSxTQUFTLENBQUMsSUFBSSxJQUFJLEVBQUU7UUFDMUIsZUFBZSxFQUFFLFNBQVMsQ0FBQyxnQkFBZ0I7UUFDM0MsSUFBSSxFQUFFLFNBQVMsQ0FBQyxJQUFJO0tBQ3JCLEVBQ0QsVUFBVSxDQUNYLENBQUM7QUFDSixDQUFDIn0=
@@ -1,4 +1,6 @@
1
+ import type { BffAccessTokenPayload } from '../oauth/bff-token';
1
2
  export type AuthMode = 'oauth' | 'api-key';
3
+ export declare function requireRenderingId(principal: BffAccessTokenPayload): number;
2
4
  export declare function extractBearerToken(authorization: string | undefined): string | undefined;
3
5
  export declare function resolveAuthMode({ hasBearer, hasApiKey, }: {
4
6
  hasBearer: boolean;
@@ -1,9 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.requireRenderingId = requireRenderingId;
3
4
  exports.extractBearerToken = extractBearerToken;
4
5
  exports.resolveAuthMode = resolveAuthMode;
5
6
  const bff_http_error_1 = require("../http/bff-http-error");
6
7
  const BEARER_PATTERN = /^Bearer[ \t]+(.+)$/i;
8
+ const POSITIVE_INTEGER = /^[1-9]\d*$/;
9
+ function requireRenderingId(principal) {
10
+ if (!POSITIVE_INTEGER.test(String(principal.rendering_id))) {
11
+ throw (0, bff_http_error_1.unauthorized)('The session carries no usable rendering');
12
+ }
13
+ return Number(principal.rendering_id);
14
+ }
7
15
  function extractBearerToken(authorization) {
8
16
  if (!authorization)
9
17
  return undefined;
@@ -22,4 +30,4 @@ function resolveAuthMode({ hasBearer, hasApiKey, }) {
22
30
  return 'oauth';
23
31
  throw (0, bff_http_error_1.unauthorized)();
24
32
  }
25
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aC1tb2RlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2F1dGgvYXV0aC1tb2RlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBTUEsZ0RBU0M7QUFFRCwwQ0FZQztBQTdCRCwyREFBNEU7QUFJNUUsTUFBTSxjQUFjLEdBQUcscUJBQXFCLENBQUM7QUFFN0MsU0FBZ0Isa0JBQWtCLENBQUMsYUFBaUM7SUFDbEUsSUFBSSxDQUFDLGFBQWE7UUFBRSxPQUFPLFNBQVMsQ0FBQztJQUVyQyxNQUFNLEtBQUssR0FBRyxjQUFjLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ3hELElBQUksQ0FBQyxLQUFLO1FBQUUsT0FBTyxTQUFTLENBQUM7SUFFN0IsTUFBTSxLQUFLLEdBQUcsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO0lBRTlCLE9BQU8sS0FBSyxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUM7QUFDMUMsQ0FBQztBQUVELFNBQWdCLGVBQWUsQ0FBQyxFQUM5QixTQUFTLEVBQ1QsU0FBUyxHQUlWO0lBQ0MsSUFBSSxTQUFTLElBQUksU0FBUztRQUFFLE1BQU0sSUFBQSxxQ0FBb0IsR0FBRSxDQUFDO0lBQ3pELElBQUksU0FBUztRQUFFLE9BQU8sU0FBUyxDQUFDO0lBQ2hDLElBQUksU0FBUztRQUFFLE9BQU8sT0FBTyxDQUFDO0lBRTlCLE1BQU0sSUFBQSw2QkFBWSxHQUFFLENBQUM7QUFDdkIsQ0FBQyJ9
33
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aC1tb2RlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2F1dGgvYXV0aC1tb2RlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBU0EsZ0RBTUM7QUFFRCxnREFTQztBQUVELDBDQVlDO0FBdENELDJEQUE0RTtBQUk1RSxNQUFNLGNBQWMsR0FBRyxxQkFBcUIsQ0FBQztBQUM3QyxNQUFNLGdCQUFnQixHQUFHLFlBQVksQ0FBQztBQUV0QyxTQUFnQixrQkFBa0IsQ0FBQyxTQUFnQztJQUNqRSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsWUFBWSxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQzNELE1BQU0sSUFBQSw2QkFBWSxFQUFDLHlDQUF5QyxDQUFDLENBQUM7SUFDaEUsQ0FBQztJQUVELE9BQU8sTUFBTSxDQUFDLFNBQVMsQ0FBQyxZQUFZLENBQUMsQ0FBQztBQUN4QyxDQUFDO0FBRUQsU0FBZ0Isa0JBQWtCLENBQUMsYUFBaUM7SUFDbEUsSUFBSSxDQUFDLGFBQWE7UUFBRSxPQUFPLFNBQVMsQ0FBQztJQUVyQyxNQUFNLEtBQUssR0FBRyxjQUFjLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ3hELElBQUksQ0FBQyxLQUFLO1FBQUUsT0FBTyxTQUFTLENBQUM7SUFFN0IsTUFBTSxLQUFLLEdBQUcsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO0lBRTlCLE9BQU8sS0FBSyxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUM7QUFDMUMsQ0FBQztBQUVELFNBQWdCLGVBQWUsQ0FBQyxFQUM5QixTQUFTLEVBQ1QsU0FBUyxHQUlWO0lBQ0MsSUFBSSxTQUFTLElBQUksU0FBUztRQUFFLE1BQU0sSUFBQSxxQ0FBb0IsR0FBRSxDQUFDO0lBQ3pELElBQUksU0FBUztRQUFFLE9BQU8sU0FBUyxDQUFDO0lBQ2hDLElBQUksU0FBUztRQUFFLE9BQU8sT0FBTyxDQUFDO0lBRTlCLE1BQU0sSUFBQSw2QkFBWSxHQUFFLENBQUM7QUFDdkIsQ0FBQyJ9
@@ -2,7 +2,16 @@ import type { BFFConfig } from './config/env-config';
2
2
  import type { UnfoldSource } from './openapi/unfolded-document';
3
3
  import type { Logger } from './ports/logger-port';
4
4
  import BFFHttpServer from './http/bff-http-server';
5
+ interface ResolvedOAuthConfig {
6
+ forestServerUrl: string;
7
+ forestEnvSecret: string;
8
+ forestAppUrl: string;
9
+ forestAuthSecret: string;
10
+ tokenEncryptionKey: string;
11
+ }
12
+ export declare function resolveOAuthConfig(config: BFFConfig): ResolvedOAuthConfig | undefined;
5
13
  export declare function resolveUnfoldSource(config: BFFConfig, logger: Logger): UnfoldSource | undefined;
6
14
  export default function runCli(env: NodeJS.ProcessEnv, logger?: Logger): Promise<BFFHttpServer>;
7
15
  export declare function reportFatalError(err: unknown): void;
16
+ export {};
8
17
  //# sourceMappingURL=cli-core.d.ts.map
package/dist/cli-core.js CHANGED
@@ -36,6 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.resolveOAuthConfig = resolveOAuthConfig;
39
40
  exports.resolveUnfoldSource = resolveUnfoldSource;
40
41
  exports.default = runCli;
41
42
  exports.reportFatalError = reportFatalError;
@@ -43,6 +44,8 @@ const bodyparser_1 = require("@koa/bodyparser");
43
44
  const action_routes_middleware_1 = __importDefault(require("./action/action-routes-middleware"));
44
45
  const console_logger_1 = __importDefault(require("./adapters/console-logger"));
45
46
  const agent_stub_1 = __importDefault(require("./agent/agent-stub"));
47
+ const ai_proxy_client_1 = __importDefault(require("./ai/ai-proxy-client"));
48
+ const ai_routes_middleware_1 = __importStar(require("./ai/ai-routes-middleware"));
46
49
  const api_key_authenticator_1 = __importDefault(require("./api-key/api-key-authenticator"));
47
50
  const api_key_client_1 = __importDefault(require("./api-key/api-key-client"));
48
51
  const api_key_middleware_1 = __importDefault(require("./api-key/api-key-middleware"));
@@ -57,7 +60,7 @@ const docs_routes_1 = __importDefault(require("./docs/docs-routes"));
57
60
  const errors_1 = require("./errors");
58
61
  const bff_http_error_1 = require("./http/bff-http-error");
59
62
  const bff_http_server_1 = __importDefault(require("./http/bff-http-server"));
60
- const body_limit_1 = __importDefault(require("./http/body-limit"));
63
+ const body_limit_1 = __importStar(require("./http/body-limit"));
61
64
  const error_middleware_1 = __importDefault(require("./http/error-middleware"));
62
65
  const forest_server_client_1 = __importDefault(require("./oauth/forest-server-client"));
63
66
  const oauth_routes_1 = __importDefault(require("./oauth/oauth-routes"));
@@ -83,6 +86,19 @@ function agentScoped(middleware) {
83
86
  await middleware(ctx, next);
84
87
  };
85
88
  }
89
+ function createBodyParser(hasAiQueryRoute) {
90
+ const parseBody = (0, bodyparser_1.bodyParser)({ jsonLimit: body_limit_1.default });
91
+ if (!hasAiQueryRoute)
92
+ return parseBody;
93
+ const parseAiBody = (0, bodyparser_1.bodyParser)({ jsonLimit: body_limit_1.AI_BODY_LIMIT, enableTypes: ['json'] });
94
+ return async function selectedBodyParser(ctx, next) {
95
+ if (ctx.path === ai_routes_middleware_1.AI_QUERY_ROUTE) {
96
+ await parseAiBody(ctx, next);
97
+ return;
98
+ }
99
+ await parseBody(ctx, next);
100
+ };
101
+ }
86
102
  function createApiKeyUnavailableGuard(logger) {
87
103
  return async function apiKeyUnavailableGuard(ctx, next) {
88
104
  if (ctx.state.authMode === 'api-key') {
@@ -125,7 +141,11 @@ async function buildOAuthMiddlewares(config, logger) {
125
141
  environmentId,
126
142
  logger,
127
143
  });
128
- return { middlewares: [oauthRoutes], environmentId };
144
+ return {
145
+ middlewares: [oauthRoutes],
146
+ environmentId,
147
+ session: { store: sessionStore, serverClient, forestServerUrl },
148
+ };
129
149
  }
130
150
  function resolveApiKeyConfig(config) {
131
151
  const { forestServerUrl, forestEnvSecret, forestAuthSecret } = config;
@@ -222,8 +242,29 @@ function buildAgentRouteMiddlewares(bundle, config, logger) {
222
242
  (0, action_routes_middleware_1.default)({ store, agentUrl, timeoutMs, logger }),
223
243
  ];
224
244
  }
225
- function buildAgentMiddlewares(config, logger, environmentId) {
245
+ function buildAiMiddlewares(config, oauth, logger) {
246
+ const { session, environmentId } = oauth;
247
+ if (!session) {
248
+ logger('Warn', 'AI query route disabled: the deployment carries no OAuth session');
249
+ return [];
250
+ }
251
+ const client = new ai_proxy_client_1.default({
252
+ forestServerUrl: session.forestServerUrl,
253
+ timeoutMs: config.aiTimeoutMs,
254
+ });
255
+ return [
256
+ (0, ai_routes_middleware_1.default)({
257
+ client,
258
+ sessionStore: session.store,
259
+ serverClient: session.serverClient,
260
+ environmentId,
261
+ logger,
262
+ }),
263
+ ];
264
+ }
265
+ function buildAgentMiddlewares(config, logger, oauth, aiMiddlewares) {
226
266
  const { forestAuthSecret, defaultTimezone } = config;
267
+ const { environmentId } = oauth;
227
268
  if (!forestAuthSecret) {
228
269
  logger('Warn', 'Agent edge disabled: FOREST_AUTH_SECRET is missing');
229
270
  return [];
@@ -237,8 +278,14 @@ function buildAgentMiddlewares(config, logger, environmentId) {
237
278
  (0, auth_mode_middleware_1.default)({ authSecret: forestAuthSecret }),
238
279
  apiKeyStep,
239
280
  (0, per_key_origin_1.default)(),
240
- (0, openapi_routes_1.default)({ version: version_1.default, enabled: config.openapiEnabled, source }),
281
+ (0, openapi_routes_1.default)({
282
+ version: version_1.default,
283
+ enabled: config.openapiEnabled,
284
+ source,
285
+ hasAiQueryRoute: aiMiddlewares.length > 0,
286
+ }),
241
287
  ...(bundle ? [(0, context_routes_middleware_1.default)({ store: bundle.store, environmentId })] : []),
288
+ ...aiMiddlewares,
242
289
  (0, timezone_middleware_1.default)({ defaultTimezone }),
243
290
  ...buildAgentRouteMiddlewares(bundle, config, logger),
244
291
  ];
@@ -252,12 +299,13 @@ async function runCli(env, logger = (0, console_logger_1.default)()) {
252
299
  });
253
300
  }
254
301
  const oauth = await buildOAuthMiddlewares(config, logger);
255
- const agentMiddlewares = buildAgentMiddlewares(config, logger, oauth.environmentId);
302
+ const aiMiddlewares = buildAiMiddlewares(config, oauth, logger);
303
+ const agentMiddlewares = buildAgentMiddlewares(config, logger, oauth, aiMiddlewares);
256
304
  const agentErrorMiddleware = agentMiddlewares.length > 0 ? [agentScoped((0, error_middleware_1.default)({ logger }))] : [];
257
305
  const middlewares = [
258
306
  (0, cors_middleware_1.default)({ allowedOrigins: config.allowedOrigins }),
259
307
  ...agentErrorMiddleware,
260
- (0, bodyparser_1.bodyParser)({ jsonLimit: body_limit_1.default }),
308
+ createBodyParser(aiMiddlewares.length > 0),
261
309
  ...oauth.middlewares,
262
310
  // Outside the agent-scoped chain on purpose: the viewer is a public page, the document it fetches
263
311
  // is not. Gated on the edge being mounted too, like the error middleware above: with no agent
@@ -283,4 +331,4 @@ function reportFatalError(err) {
283
331
  process.stderr.write(`Error: ${(0, errors_1.extractErrorMessage)(err)}\n`);
284
332
  process.exitCode = 1;
285
333
  }
286
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLWNvcmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvY2xpLWNvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFxT0Esa0RBRUM7QUEyRUQseUJBMENDO0FBRUQsNENBR0M7QUExVkQsZ0RBQTZDO0FBRTdDLGlHQUE2RTtBQUM3RSwrRUFBNEQ7QUFDNUQsb0VBQTJEO0FBQzNELDRGQUF3RTtBQUN4RSw4RUFBb0Q7QUFDcEQsc0ZBQWtFO0FBQ2xFLDRFQUF5RDtBQUN6RCx1RkFBbUU7QUFDbkUsb0RBQWtEO0FBQ2xELG9HQUFnRjtBQUNoRiw2RUFBMEQ7QUFDMUQsMkVBQWlFO0FBQ2pFLDJGQUF1RTtBQUN2RSxxRUFBa0Q7QUFDbEQscUNBQStDO0FBQy9DLDBEQUFxRDtBQUNyRCw2RUFBbUQ7QUFDbkQsbUVBQTJDO0FBQzNDLCtFQUE0RDtBQUM1RCx3RkFBOEQ7QUFDOUQsd0VBQXFEO0FBQ3JELDBFQUErRDtBQUMvRCx3RUFBcUQ7QUFDckQsMkVBQTZFO0FBQzdFLHdGQUErRDtBQUMvRCwwRkFBaUU7QUFDakUsZ0hBQTRGO0FBQzVGLHVGQUE2RDtBQUM3RCx5RkFBc0U7QUFDdEUsd0RBQWdDO0FBRWhDLE1BQU0sbUJBQW1CLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLENBQUM7QUFFekMsU0FBUyxXQUFXLENBQUMsSUFBWTtJQUMvQixPQUFPLElBQUksS0FBSyxRQUFRLElBQUksSUFBSSxDQUFDLFVBQVUsQ0FBQyxTQUFTLENBQUMsQ0FBQztBQUN6RCxDQUFDO0FBRUQsU0FBUyxXQUFXLENBQUMsVUFBc0I7SUFDekMsT0FBTyxLQUFLLFVBQVUsTUFBTSxDQUFDLEdBQUcsRUFBRSxJQUFJO1FBQ3BDLElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7WUFDM0IsTUFBTSxJQUFJLEVBQUUsQ0FBQztZQUViLE9B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
334
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLWNvcmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvY2xpLWNvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFrR0EsZ0RBZUM7QUFvSkQsa0RBRUM7QUE0R0QseUJBMkNDO0FBRUQsNENBR0M7QUEzWkQsZ0RBQTZDO0FBRTdDLGlHQUE2RTtBQUM3RSwrRUFBNEQ7QUFDNUQsb0VBQTJEO0FBQzNELDJFQUFpRDtBQUNqRCxrRkFBcUY7QUFDckYsNEZBQXdFO0FBQ3hFLDhFQUFvRDtBQUNwRCxzRkFBa0U7QUFDbEUsNEVBQXlEO0FBQ3pELHVGQUFtRTtBQUNuRSxvREFBa0Q7QUFDbEQsb0dBQWdGO0FBQ2hGLDZFQUEwRDtBQUMxRCwyRUFBaUU7QUFDakUsMkZBQXVFO0FBQ3ZFLHFFQUFrRDtBQUNsRCxxQ0FBK0M7QUFDL0MsMERBQXFEO0FBQ3JELDZFQUFtRDtBQUNuRCxnRUFBOEQ7QUFDOUQsK0VBQTREO0FBQzVELHdGQUE4RDtBQUM5RCx3RUFBcUQ7QUFDckQsMEVBQStEO0FBQy9ELHdFQUFxRDtBQUNyRCwyRUFBNkU7QUFDN0Usd0ZBQStEO0FBQy9ELDBGQUFpRTtBQUNqRSxnSEFBNEY7QUFDNUYsdUZBQTZEO0FBQzdELHlGQUFzRTtBQUN0RSx3REFBZ0M7QUFFaEMsTUFBTSxtQkFBbUIsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsQ0FBQztBQUV6QyxTQUFTLFdBQVcsQ0FBQyxJQUFZO0lBQy9CLE9BQU8sSUFBSSxLQUFLLFFBQVEsSUFBSSxJQUFJLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxDQUFDO0FBQ3pELENBQUM7QUFFRCxTQUFTLFdBQVcsQ0FBQyxVQUFzQjtJQUN6QyxPQUFPLEtBQUssVUFBVSxNQUFNLENBQUMsR0FBRyxFQUFFLElBQUk7UUFDcEMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLEVBQUUs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