@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 +40 -3
- package/dist/ai/ai-proxy-client.d.ts +26 -0
- package/dist/ai/ai-proxy-client.js +74 -0
- package/dist/ai/ai-proxy-timeout-error.d.ts +5 -0
- package/dist/ai/ai-proxy-timeout-error.js +11 -0
- package/dist/ai/ai-routes-middleware.d.ts +15 -0
- package/dist/ai/ai-routes-middleware.js +96 -0
- package/dist/api-key/agent-token.js +4 -7
- package/dist/auth/auth-mode.d.ts +2 -0
- package/dist/auth/auth-mode.js +9 -1
- package/dist/cli-core.d.ts +9 -0
- package/dist/cli-core.js +55 -7
- package/dist/cli-dispatch.js +15 -3
- package/dist/config/env-config.d.ts +3 -1
- package/dist/config/env-config.js +10 -8
- package/dist/docs/docs-samples.js +24 -16
- package/dist/http/bff-local-errors.d.ts +4 -0
- package/dist/http/bff-local-errors.js +19 -1
- package/dist/http/body-limit.d.ts +1 -0
- package/dist/http/body-limit.js +3 -1
- package/dist/oauth/oauth-error.d.ts +3 -2
- package/dist/oauth/oauth-error.js +5 -4
- package/dist/oauth/session-lifecycle.js +2 -2
- package/dist/openapi/openapi-document.d.ts +5 -1
- package/dist/openapi/openapi-document.js +133 -17
- package/dist/openapi/openapi-routes.d.ts +2 -1
- package/dist/openapi/openapi-routes.js +9 -4
- package/dist/openapi/schemas.d.ts +1 -0
- package/dist/openapi/schemas.js +10 -2
- package/dist/openapi/unfolded-document.d.ts +5 -1
- package/dist/openapi/unfolded-document.js +6 -3
- package/package.json +1 -1
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
|
-
|
|
146
|
-
header, (2) body `timezone` field, (3) `BFF_DEFAULT_TIMEZONE`. None →
|
|
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,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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWktcm91dGVzLW1pZGRsZXdhcmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYWkvYWktcm91dGVzLW1pZGRsZXdhcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBc0dBLDJDQTZDQztBQTNJRCx1REFBd0Q7QUFDeEQsaURBQXVEO0FBQ3ZELDJEQUFzRDtBQUN0RCwrREFLa0M7QUFDbEMsc0RBQXlEO0FBQ3pELG1GQUFpRTtBQUVwRCxRQUFBLGNBQWMsR0FBRyxvQkFBb0IsQ0FBQztBQUVuRCxNQUFNLGlCQUFpQixHQUFHLGtCQUFrQixDQUFDO0FBQzdDLE1BQU0sZUFBZSxHQUFHLENBQUMsQ0FBQztBQUMxQixNQUFNLGlCQUFpQixHQUFHLEdBQUcsQ0FBQztBQVU5QixTQUFTLHVCQUF1QixDQUFDLEdBQVk7SUFDM0MsSUFBSSxHQUFHLENBQUMsS0FBSyxDQUFDLFFBQVEsS0FBSyxTQUFTO1FBQUUsTUFBTSxJQUFBLGdDQUFhLEdBQUUsQ0FBQztJQUU1RCxNQUFNLFNBQVMsR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLFNBQThDLENBQUM7SUFDM0UsSUFBSSxDQUFDLFNBQVM7UUFBRSxNQUFNLElBQUEsNkJBQVksRUFBQyw2QkFBNkIsQ0FBQyxDQUFDO0lBRWxFLE9BQU8sU0FBUyxDQUFDO0FBQ25CLENBQUM7QUFFRCxTQUFTLEtBQUssQ0FBQyxPQUFlO0lBQzVCLE9BQU8sT0FBTyxDQUFDLE1BQU0sR0FBRyxpQkFBaUIsQ0FBQyxDQUFDLENBQUMsR0FBRyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQztBQUNsRyxDQUFDO0FBRUQsU0FBUyxlQUFlLENBQUMsS0FBYyxFQUFFLFNBQVMsR0FBRyxlQUFlO0lBQ2xFLElBQUksQ0FBQyxDQUFDLEtBQUssWUFBWSxLQUFLLENBQUM7UUFBRSxPQUFPLEtBQUssQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztJQUUzRCxNQUFNLEVBQUUsS0FBSyxFQUFFLEdBQUcsS0FBNEIsQ0FBQztJQUMvQyxNQUFNLFFBQVEsR0FBRyxTQUFTLEdBQUcsQ0FBQyxJQUFJLEtBQUssS0FBSyxTQUFTLENBQUM7SUFDdEQsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLENBQUMsQ0FBQyxZQUFZLGVBQWUsQ0FBQyxLQUFLLEVBQUUsU0FBUyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztJQUVsRixPQUFPLEdBQUcsS0FBSyxDQUFDLElBQUksS0FBSyxLQUFLLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxHQUFHLElBQUksRUFBRSxDQUFDO0FBQ3pELENBQUM7QUFFRCxLQUFLLFVBQVUseUJBQXlCLENBQ3RDLFNBQWdDLEVBQ2hDLEtBQW1CLEVBQ25CLFlBQWdDLEVBQ2hDLE1BQWM7SUFFZCxJQUFJLENBQUM7UUFDSCxPQUFPLE1BQU0sSUFBQSwyQkFBdUIsRUFBQyxFQUFFLEdBQUcsRUFBRSxTQUFTLENBQUMsR0FBRyxFQUFFLEtBQUssRUFBRSxZQUFZLEVBQUUsQ0FBQyxDQUFDO0lBQ3BGLENBQUM7SUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1FBQ2YsSUFBSSxLQUFLLFlBQVksK0JBQWlCLElBQUksS0FBSyxDQUFDLE1BQU0sSUFBSSxHQUFHLEVBQUUsQ0FBQztZQUM5RCxNQUFNLENBQUMsTUFBTSxFQUFFLG1FQUFtRSxFQUFFO2dCQUNsRixLQUFLLEVBQUUsZUFBZSxDQUFDLEtBQUssQ0FBQzthQUM5QixDQUFDLENBQUM7WUFFSCxNQUFNLElBQUEsc0NBQW1CLEdBQUUsQ0FBQztRQUM5QixDQUFDO1FBRUQsTUFBTSxLQUFLLENBQUM7SUFDZCxDQUFDO0FBQ0gsQ0FBQztBQUVELFNBQVMsMkJBQTJCLENBQ2xDLEdBQVksRUFDWixRQUF5QixFQUN6QixNQUFjO0lBRWQsTUFBTSwyQkFBMkIsR0FBRyxRQUFRLENBQUMsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUM7SUFFL0UsSUFBSSwyQkFBMkIsRUFBRSxDQUFDO1FBQ2hDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsNERBQTRELEVBQUU7WUFDM0UsTUFBTSxFQUFFLFFBQVEsQ0FBQyxNQUFNO1lBQ3ZCLE1BQU0sRUFBRSxRQUFRLENBQUMsTUFBTTtZQUN2QixHQUFHLENBQUMsUUFBUSxDQUFDLG9CQUFvQixLQUFLLFNBQVM7Z0JBQzdDLENBQUMsQ0FBQyxFQUFFLEtBQUssRUFBRSxlQUFlLENBQUMsUUFBUSxDQUFDLG9CQUFvQixDQUFDLEVBQUU7Z0JBQzNELENBQUMsQ0FBQyxFQUFFLENBQUM7U0FDUixDQUFDLENBQUM7UUFFSCxNQUFNLElBQUEsZ0NBQWEsRUFBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDdkMsQ0FBQztJQUVELEdBQUcsQ0FBQyxNQUFNLEdBQUcsUUFBUSxDQUFDLE1BQU0sQ0FBQztJQUM3QixHQUFHLENBQUMsSUFBSSxHQUFHLGlCQUFpQixDQUFDO0lBQzdCLEdBQUcsQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUM7QUFDM0MsQ0FBQztBQUVELFNBQXdCLHdCQUF3QixDQUFDLEVBQy9DLE1BQU0sRUFDTixZQUFZLEVBQ1osWUFBWSxFQUNaLGFBQWEsRUFDYixNQUFNLEdBQ29CO0lBQzFCLE9BQU8sS0FBSyxVQUFVLGtCQUFrQixDQUFDLEdBQUcsRUFBRSxJQUFJO1FBQ2hELElBQUksR0FBRyxDQUFDLElBQUksS0FBSyxzQkFBYyxJQUFJLEdBQUcsQ0FBQyxNQUFNLEtBQUssTUFBTSxFQUFFLENBQUM7WUFDekQsTUFBTSxJQUFJLEVBQUUsQ0FBQztZQUViLE9BQU87UUFDVCxDQUFDO1FBRUQsTUFBTSxTQUFTLEdBQUcsdUJBQXVCLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDL0MsTUFBTSxXQUFXLEdBQUcsSUFBQSw4QkFBa0IsRUFBQyxTQUFTLENBQUMsQ0FBQztRQUVsRCxNQUFNLGVBQWUsR0FBRyxNQUFNLHlCQUF5QixDQUNyRCxTQUFTLEVBQ1QsWUFBWSxFQUNaLFlBQVksRUFDWixNQUFNLENBQ1AsQ0FBQztRQUVGLElBQUksUUFBeUIsQ0FBQztRQUU5QixJQUFJLENBQUM7WUFDSCxRQUFRLEdBQUcsTUFBTSxNQUFNLENBQUMsS0FBSyxDQUFDO2dCQUM1QixlQUFlO2dCQUNmLGFBQWE7Z0JBQ2IsV0FBVztnQkFDWCxJQUFJLEVBQUUsR0FBRyxDQUFDLE9BQU8sQ0FBQyxJQUFJO2FBQ3ZCLENBQUMsQ0FBQztRQUNMLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsTUFBTSxDQUFDLE1BQU0sRUFBRSwyQ0FBMkMsRUFBRTtnQkFDMUQsS0FBSyxFQUFFLGVBQWUsQ0FBQyxLQUFLLENBQUM7YUFDOUIsQ0FBQyxDQUFDO1lBRUgsSUFBSSxLQUFLLFlBQVkscUNBQW1CO2dCQUFFLE1BQU0sSUFBQSxrQ0FBZSxHQUFFLENBQUM7WUFFbEUsTUFBTSxJQUFBLHNDQUFtQixHQUFFLENBQUM7UUFDOUIsQ0FBQztRQUVELDJCQUEyQixDQUFDLEdBQUcsRUFBRSxRQUFRLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDckQsQ0FBQyxDQUFDO0FBQ0osQ0FBQyJ9
|
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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,
|
|
55
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdlbnQtdG9rZW4uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYXBpLWtleS9hZ2VudC10b2tlbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUF5REEsMENBZ0JDO0FBRUQsb0VBb0JDO0FBNUZELGdFQUF3QztBQUV4QyxpREFBdUQ7QUFFMUMsUUFBQSxzQkFBc0IsR0FBRyxJQUFJLENBQUM7QUFZM0MsU0FBUyxZQUFZLENBQUMsSUFBc0M7SUFDMUQsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsR0FBRyxFQUFFLEtBQUssRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDLEVBQUUsR0FBRyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsRUFBRSxLQUFLLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBQ2hGLENBQUM7QUFjRCxTQUFTLDhDQUE4QyxDQUNyRCxNQUF5QixFQUN6QixVQUFrQjtJQUVsQixNQUFNLFNBQVMsR0FBRyxNQUFNLENBQUMsU0FBUyxJQUFJLEVBQUUsQ0FBQztJQUN6QyxNQUFNLFFBQVEsR0FBRyxNQUFNLENBQUMsUUFBUSxJQUFJLEVBQUUsQ0FBQztJQUV2QyxPQUFPLHNCQUFZLENBQUMsSUFBSSxDQUN0QjtRQUNFLEdBQUcsTUFBTTtRQUNULFNBQVM7UUFDVCxRQUFRO1FBQ1IsVUFBVSxFQUFFLFNBQVM7UUFDckIsU0FBUyxFQUFFLFFBQVE7UUFDbkIsWUFBWSxFQUFFLE1BQU0sQ0FBQyxXQUFXO1FBQ2hDLGdCQUFnQixFQUFFLE1BQU0sQ0FBQyxlQUFlO0tBQ3pDLEVBQ0QsVUFBVSxFQUNWLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsOEJBQXNCLEVBQUUsQ0FDMUQsQ0FBQztBQUNKLENBQUM7QUFFRCxTQUFnQixlQUFlLENBQUMsRUFBRSxRQUFRLEVBQUUsVUFBVSxFQUF5QjtJQUM3RSxNQUFNLEVBQUUsSUFBSSxFQUFFLFdBQVcsRUFBRSxHQUFHLFFBQVEsQ0FBQztJQUV2QyxPQUFPLDhDQUE4QyxDQUNuRDtRQUNFLEVBQUUsRUFBRSxJQUFJLENBQUMsRUFBRTtRQUNYLEtBQUssRUFBRSxJQUFJLENBQUMsS0FBSztRQUNqQixTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVM7UUFDekIsUUFBUSxFQUFFLElBQUksQ0FBQyxRQUFRO1FBQ3ZCLElBQUksRUFBRSxJQUFJLENBQUMsSUFBSTtRQUNmLFdBQVc7UUFDWCxJQUFJLEVBQUUsWUFBWSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUM7UUFDN0IsZUFBZSxFQUFFLElBQUksQ0FBQyxlQUFlO0tBQ3RDLEVBQ0QsVUFBVSxDQUNYLENBQUM7QUFDSixDQUFDO0FBRUQsU0FBZ0IsNEJBQTRCLENBQUMsRUFDM0MsU0FBUyxFQUNULFVBQVUsR0FDeUI7SUFDbkMsTUFBTSxXQUFXLEdBQUcsSUFBQSw4QkFBa0IsRUFBQyxTQUFTLENBQUMsQ0FBQztJQUVsRCxPQUFPLDhDQUE4QyxDQUNuRDtRQUNFLEVBQUUsRUFBRSxTQUFTLENBQUMsRUFBRTtRQUNoQixLQUFLLEVBQUUsU0FBUyxDQUFDLEtBQUs7UUFDdEIsU0FBUyxFQUFFLFNBQVMsQ0FBQyxVQUFVO1FBQy9CLFFBQVEsRUFBRSxTQUFTLENBQUMsU0FBUztRQUM3QixJQUFJLEVBQUUsU0FBUyxDQUFDLElBQUk7UUFDcEIsV0FBVztRQUNYLElBQUksRUFBRSxTQUFTLENBQUMsSUFBSSxJQUFJLEVBQUU7UUFDMUIsZUFBZSxFQUFFLFNBQVMsQ0FBQyxnQkFBZ0I7UUFDM0MsSUFBSSxFQUFFLFNBQVMsQ0FBQyxJQUFJO0tBQ3JCLEVBQ0QsVUFBVSxDQUNYLENBQUM7QUFDSixDQUFDIn0=
|
package/dist/auth/auth-mode.d.ts
CHANGED
|
@@ -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;
|
package/dist/auth/auth-mode.js
CHANGED
|
@@ -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,
|
|
33
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aC1tb2RlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2F1dGgvYXV0aC1tb2RlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBU0EsZ0RBTUM7QUFFRCxnREFTQztBQUVELDBDQVlDO0FBdENELDJEQUE0RTtBQUk1RSxNQUFNLGNBQWMsR0FBRyxxQkFBcUIsQ0FBQztBQUM3QyxNQUFNLGdCQUFnQixHQUFHLFlBQVksQ0FBQztBQUV0QyxTQUFnQixrQkFBa0IsQ0FBQyxTQUFnQztJQUNqRSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsWUFBWSxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQzNELE1BQU0sSUFBQSw2QkFBWSxFQUFDLHlDQUF5QyxDQUFDLENBQUM7SUFDaEUsQ0FBQztJQUVELE9BQU8sTUFBTSxDQUFDLFNBQVMsQ0FBQyxZQUFZLENBQUMsQ0FBQztBQUN4QyxDQUFDO0FBRUQsU0FBZ0Isa0JBQWtCLENBQUMsYUFBaUM7SUFDbEUsSUFBSSxDQUFDLGFBQWE7UUFBRSxPQUFPLFNBQVMsQ0FBQztJQUVyQyxNQUFNLEtBQUssR0FBRyxjQUFjLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ3hELElBQUksQ0FBQyxLQUFLO1FBQUUsT0FBTyxTQUFTLENBQUM7SUFFN0IsTUFBTSxLQUFLLEdBQUcsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO0lBRTlCLE9BQU8sS0FBSyxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUM7QUFDMUMsQ0FBQztBQUVELFNBQWdCLGVBQWUsQ0FBQyxFQUM5QixTQUFTLEVBQ1QsU0FBUyxHQUlWO0lBQ0MsSUFBSSxTQUFTLElBQUksU0FBUztRQUFFLE1BQU0sSUFBQSxxQ0FBb0IsR0FBRSxDQUFDO0lBQ3pELElBQUksU0FBUztRQUFFLE9BQU8sU0FBUyxDQUFDO0lBQ2hDLElBQUksU0FBUztRQUFFLE9BQU8sT0FBTyxDQUFDO0lBRTlCLE1BQU0sSUFBQSw2QkFBWSxHQUFFLENBQUM7QUFDdkIsQ0FBQyJ9
|
package/dist/cli-core.d.ts
CHANGED
|
@@ -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 =
|
|
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 {
|
|
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
|
|
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)({
|
|
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
|
|
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
|
-
(
|
|
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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
|