@forestadmin/agent 1.99.2 → 1.100.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/dist/agent.d.ts +34 -1
- package/dist/agent.js +97 -14
- package/dist/bff-routes.d.ts +25 -0
- package/dist/bff-routes.js +49 -0
- package/dist/embedded-bff.d.ts +61 -0
- package/dist/embedded-bff.js +220 -0
- package/dist/framework-mounter.d.ts +8 -4
- package/dist/framework-mounter.js +21 -14
- package/dist/index.d.ts +1 -1
- package/dist/root-middleware.d.ts +33 -0
- package/dist/root-middleware.js +70 -0
- package/dist/types.d.ts +47 -1
- package/dist/types.js +1 -1
- package/package.json +10 -3
- package/dist/mcp-middleware.d.ts +0 -27
- package/dist/mcp-middleware.js +0 -67
package/dist/agent.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ForestAdminHttpDriverServices } from './services';
|
|
2
|
-
import type { AgentOptions, AgentOptionsWithDefaults, WorkflowExecutorEmbedOptions } from './types';
|
|
2
|
+
import type { AgentOptions, AgentOptionsWithDefaults, BffEmbedOptions, WorkflowExecutorEmbedOptions } from './types';
|
|
3
3
|
import type { CollectionCustomizer, DataSourceChartDefinition, DataSourceOptions, Plugin, TCollectionName, TSchema } from '@forestadmin/datasource-customizer';
|
|
4
4
|
import type { DataSource, DataSourceFactory } from '@forestadmin/datasource-toolkit';
|
|
5
5
|
import type { FileUploadsOptions, TokenTtlOptions, ToolName } from '@forestadmin/mcp-server';
|
|
@@ -32,7 +32,15 @@ export default class Agent<S extends TSchema = TSchema> extends FrameworkMounter
|
|
|
32
32
|
private mcpFileUploads?;
|
|
33
33
|
/** In-process workflow executor, created only when addWorkflowExecutor() is called. */
|
|
34
34
|
private embeddedExecutor;
|
|
35
|
+
/** In-process BFF, created only when addBff() is called. */
|
|
36
|
+
private embeddedBff;
|
|
35
37
|
private isRestarting;
|
|
38
|
+
/**
|
|
39
|
+
* Set as soon as start() begins, not once it finishes: mount() drains the `onFirstStart` hooks
|
|
40
|
+
* partway through, so a builder call landing mid-startup is already too late. Cleared again when
|
|
41
|
+
* startup fails, which leaves nothing mounted and makes the agent configurable once more.
|
|
42
|
+
*/
|
|
43
|
+
private startupBegun;
|
|
36
44
|
/**
|
|
37
45
|
* Create a new Agent Builder.
|
|
38
46
|
* If any options are missing, the default will be applied:
|
|
@@ -179,6 +187,31 @@ export default class Agent<S extends TSchema = TSchema> extends FrameworkMounter
|
|
|
179
187
|
* .start();
|
|
180
188
|
*/
|
|
181
189
|
addWorkflowExecutor(options?: WorkflowExecutorEmbedOptions): this;
|
|
190
|
+
/**
|
|
191
|
+
* Serve a BFF in-process, alongside the agent, at `/bff` — no second deployment, no second port.
|
|
192
|
+
* The agent builds it on start(), stops it on stop(), and hands it a dispatcher that reaches its
|
|
193
|
+
* own stack without a socket, so this works the same on every mount target.
|
|
194
|
+
*
|
|
195
|
+
* Requires the `@forestadmin/agent-bff` package to be installed:
|
|
196
|
+
* ```bash
|
|
197
|
+
* npm install @forestadmin/agent-bff
|
|
198
|
+
* ```
|
|
199
|
+
*
|
|
200
|
+
* The secrets, the Forest urls and the logger are inherited from the agent. Everything left is
|
|
201
|
+
* a feature the BFF switches on when configured: `tokenEncryptionKey` enables OAuth (and with it
|
|
202
|
+
* the AI relay), `allowedOrigins` enables browser access, `openapiEnabled` serves the docs.
|
|
203
|
+
*
|
|
204
|
+
* @param options embedded BFF options
|
|
205
|
+
* @returns the agent instance for chaining
|
|
206
|
+
* @throws Error if called more than once, or if the MCP server already claims `/bff`
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* createAgent(options)
|
|
210
|
+
* .addDataSource(...)
|
|
211
|
+
* .addBff({ allowedOrigins: ['https://my-app.com'] })
|
|
212
|
+
* .start();
|
|
213
|
+
*/
|
|
214
|
+
addBff(options?: BffEmbedOptions): this;
|
|
182
215
|
protected getRoutes(dataSource: DataSource, services: ForestAdminHttpDriverServices): import("./routes/base-route").default[];
|
|
183
216
|
/**
|
|
184
217
|
* Create an http handler which can respond to all queries which are expected from an agent.
|
package/dist/agent.js
CHANGED
|
@@ -43,6 +43,8 @@ const router_1 = __importDefault(require("@koa/router"));
|
|
|
43
43
|
const promises_1 = require("fs/promises");
|
|
44
44
|
const json_stringify_pretty_compact_1 = __importDefault(require("json-stringify-pretty-compact"));
|
|
45
45
|
const audit_trail_1 = require("./audit-trail");
|
|
46
|
+
const bff_routes_1 = require("./bff-routes");
|
|
47
|
+
const embedded_bff_1 = __importDefault(require("./embedded-bff"));
|
|
46
48
|
const embedded_workflow_executor_1 = __importDefault(require("./embedded-workflow-executor"));
|
|
47
49
|
const framework_mounter_1 = __importDefault(require("./framework-mounter"));
|
|
48
50
|
const routes_1 = __importDefault(require("./routes"));
|
|
@@ -51,6 +53,12 @@ const customization_1 = __importDefault(require("./services/model-customizations
|
|
|
51
53
|
const correlation_id_1 = require("./utils/correlation-id");
|
|
52
54
|
const generator_1 = __importDefault(require("./utils/forest-schema/generator"));
|
|
53
55
|
const options_validator_1 = __importDefault(require("./utils/options-validator"));
|
|
56
|
+
// Whichever is registered second raises it. `addBff()` registers at builder time and the MCP
|
|
57
|
+
// server only at start(), and the root middleware answers with the first handler whose matcher
|
|
58
|
+
// claims the url — so the BFF wins and the whole MCP surface would go silently dark.
|
|
59
|
+
const bffMcpCollision = (mcpBasePath) => `Cannot use addBff together with mountAiMcpServer({ basePath: '${mcpBasePath}' }): the MCP ` +
|
|
60
|
+
`server would claim ${bff_routes_1.BFF_PREFIX} paths the embedded BFF answers on (${bff_routes_1.BFF_PREFIX}/oauth, ` +
|
|
61
|
+
`${bff_routes_1.BFF_PREFIX}/mcp). Mount the MCP server elsewhere.`;
|
|
54
62
|
/**
|
|
55
63
|
* Allow to create a new Forest Admin agent from scratch.
|
|
56
64
|
* Builds the application by composing and configuring all the collection decorators.
|
|
@@ -85,7 +93,15 @@ class Agent extends framework_mounter_1.default {
|
|
|
85
93
|
this.mcpEnabled = false;
|
|
86
94
|
/** In-process workflow executor, created only when addWorkflowExecutor() is called. */
|
|
87
95
|
this.embeddedExecutor = null;
|
|
96
|
+
/** In-process BFF, created only when addBff() is called. */
|
|
97
|
+
this.embeddedBff = null;
|
|
88
98
|
this.isRestarting = false;
|
|
99
|
+
/**
|
|
100
|
+
* Set as soon as start() begins, not once it finishes: mount() drains the `onFirstStart` hooks
|
|
101
|
+
* partway through, so a builder call landing mid-startup is already too late. Cleared again when
|
|
102
|
+
* startup fails, which leaves nothing mounted and makes the agent configurable once more.
|
|
103
|
+
*/
|
|
104
|
+
this.startupBegun = false;
|
|
89
105
|
this.options = allOptions;
|
|
90
106
|
this.customizer = new datasource_customizer_1.DataSourceCustomizer({
|
|
91
107
|
ignoreMissingSchemaElementErrors: options.ignoreMissingSchemaElementErrors || false,
|
|
@@ -98,18 +114,31 @@ class Agent extends framework_mounter_1.default {
|
|
|
98
114
|
*/
|
|
99
115
|
async start() {
|
|
100
116
|
let mounted = false;
|
|
117
|
+
this.startupBegun = true;
|
|
101
118
|
try {
|
|
102
|
-
|
|
119
|
+
// First, before anything is mounted or subscribed: everything it validates is what the caller
|
|
120
|
+
// handed to addBff(), so a mistyped key must fail on that line rather than leave the host
|
|
121
|
+
// serving /forest with a permanently bricked /bff.
|
|
122
|
+
await this.embeddedBff?.prepare();
|
|
123
|
+
const { router, mcp } = await this.buildRouterAndSendSchema();
|
|
103
124
|
await this.options.forestAdminClient.subscribeToServerEvents();
|
|
104
125
|
this.options.forestAdminClient.onRefreshCustomizations(this.restart.bind(this));
|
|
105
|
-
this.setMcpCallback(
|
|
126
|
+
this.setMcpCallback(mcp ?? null);
|
|
106
127
|
await this.mount(router);
|
|
107
128
|
mounted = true;
|
|
108
129
|
// Boot after mount(): the embedded executor reaches the agent over HTTP, and the
|
|
109
130
|
// standalone server's host/port (used to derive that URL) are only known once mounted.
|
|
110
131
|
await this.embeddedExecutor?.start(this.standaloneServerHost, this.standaloneServerPort);
|
|
132
|
+
// Same reason, without the socket: the dispatcher injects into the stack mount() just built.
|
|
133
|
+
await this.embeddedBff?.start(this.getInProcessDispatcher());
|
|
111
134
|
}
|
|
112
135
|
catch (error) {
|
|
136
|
+
// Only when nothing was mounted. Past mount() the host framework is already serving this
|
|
137
|
+
// agent, so it is not configurable again: clearing the flag would let a later addBff() past
|
|
138
|
+
// its guard and register a BFF whose start() nothing calls — /bff would answer 503 for the
|
|
139
|
+
// rest of the process, and restart() only invalidates, it never starts it.
|
|
140
|
+
if (!mounted)
|
|
141
|
+
this.startupBegun = false;
|
|
113
142
|
const { message } = error;
|
|
114
143
|
this.options.logger('Error', `Forest Admin agent startup failure: ${message}`);
|
|
115
144
|
// buildRouterAndSendSchema() may already have opened the audit-trail connection even
|
|
@@ -133,7 +162,10 @@ class Agent extends framework_mounter_1.default {
|
|
|
133
162
|
* Stop the agent.
|
|
134
163
|
*/
|
|
135
164
|
async stop() {
|
|
136
|
-
//
|
|
165
|
+
// Stop answering before the stack it dispatches into goes away: the host application keeps
|
|
166
|
+
// whatever middleware it registered, so a stopped agent would otherwise still serve BFF data.
|
|
167
|
+
this.embeddedBff?.stop();
|
|
168
|
+
// Drain the embedded executor next, while the agent it depends on is still serving.
|
|
137
169
|
await this.embeddedExecutor?.stop();
|
|
138
170
|
// Close anything related to ForestAdmin client
|
|
139
171
|
this.options.forestAdminClient.close();
|
|
@@ -163,9 +195,11 @@ class Agent extends framework_mounter_1.default {
|
|
|
163
195
|
this.isRestarting = true;
|
|
164
196
|
try {
|
|
165
197
|
// We force sending schema when restarting
|
|
166
|
-
const { router,
|
|
167
|
-
this.setMcpCallback(
|
|
198
|
+
const { router, mcp } = await this.buildRouterAndSendSchema();
|
|
199
|
+
this.setMcpCallback(mcp ?? null);
|
|
168
200
|
await this.remount(router);
|
|
201
|
+
// A restart means the customizations changed, so the schema the BFF read is stale.
|
|
202
|
+
this.embeddedBff?.invalidate();
|
|
169
203
|
}
|
|
170
204
|
finally {
|
|
171
205
|
this.isRestarting = false;
|
|
@@ -277,6 +311,9 @@ class Agent extends framework_mounter_1.default {
|
|
|
277
311
|
* agent.mountAiMcpServer({ fileUploads: false });
|
|
278
312
|
*/
|
|
279
313
|
mountAiMcpServer(options) {
|
|
314
|
+
if (this.embeddedBff && (0, bff_routes_1.collidesWithBff)(options?.basePath)) {
|
|
315
|
+
throw new Error(bffMcpCollision(options?.basePath));
|
|
316
|
+
}
|
|
280
317
|
this.mcpEnabled = true;
|
|
281
318
|
this.mcpEnabledTools = options?.enabledTools;
|
|
282
319
|
this.mcpBasePath = options?.basePath;
|
|
@@ -320,6 +357,54 @@ class Agent extends framework_mounter_1.default {
|
|
|
320
357
|
this.embeddedExecutor = executor;
|
|
321
358
|
return this;
|
|
322
359
|
}
|
|
360
|
+
/**
|
|
361
|
+
* Serve a BFF in-process, alongside the agent, at `/bff` — no second deployment, no second port.
|
|
362
|
+
* The agent builds it on start(), stops it on stop(), and hands it a dispatcher that reaches its
|
|
363
|
+
* own stack without a socket, so this works the same on every mount target.
|
|
364
|
+
*
|
|
365
|
+
* Requires the `@forestadmin/agent-bff` package to be installed:
|
|
366
|
+
* ```bash
|
|
367
|
+
* npm install @forestadmin/agent-bff
|
|
368
|
+
* ```
|
|
369
|
+
*
|
|
370
|
+
* The secrets, the Forest urls and the logger are inherited from the agent. Everything left is
|
|
371
|
+
* a feature the BFF switches on when configured: `tokenEncryptionKey` enables OAuth (and with it
|
|
372
|
+
* the AI relay), `allowedOrigins` enables browser access, `openapiEnabled` serves the docs.
|
|
373
|
+
*
|
|
374
|
+
* @param options embedded BFF options
|
|
375
|
+
* @returns the agent instance for chaining
|
|
376
|
+
* @throws Error if called more than once, or if the MCP server already claims `/bff`
|
|
377
|
+
*
|
|
378
|
+
* @example
|
|
379
|
+
* createAgent(options)
|
|
380
|
+
* .addDataSource(...)
|
|
381
|
+
* .addBff({ allowedOrigins: ['https://my-app.com'] })
|
|
382
|
+
* .start();
|
|
383
|
+
*/
|
|
384
|
+
addBff(options = {}) {
|
|
385
|
+
if (this.embeddedBff) {
|
|
386
|
+
throw new Error('addBff can only be called once.');
|
|
387
|
+
}
|
|
388
|
+
// Refused rather than accepted and left dark: once startup has begun the dispatcher hook has no
|
|
389
|
+
// mount left to attach to and nothing calls the BFF's own start(), so `/bff` would answer 503
|
|
390
|
+
// for the rest of the process while every other route works. A throw on the line the developer
|
|
391
|
+
// wrote instead. Refused from the first line of start(), not from its last: `start()` is async,
|
|
392
|
+
// so a call made while it is still in flight is just as late.
|
|
393
|
+
if (this.startupBegun) {
|
|
394
|
+
throw new Error('addBff must be called before start(): the agent is already starting.');
|
|
395
|
+
}
|
|
396
|
+
if ((0, bff_routes_1.collidesWithBff)(this.mcpBasePath)) {
|
|
397
|
+
throw new Error(bffMcpCollision(this.mcpBasePath));
|
|
398
|
+
}
|
|
399
|
+
const bff = new embedded_bff_1.default(this.options, options);
|
|
400
|
+
this.embeddedBff = bff;
|
|
401
|
+
// Registered now rather than at start(): getInProcessDispatcher() pushes its hook the first
|
|
402
|
+
// time it is called, and mount() only runs the hooks registered before it — asked for later,
|
|
403
|
+
// the dispatcher would have no handler until the first restart.
|
|
404
|
+
this.getInProcessDispatcher();
|
|
405
|
+
this.setBffCallback(bff.handle);
|
|
406
|
+
return this;
|
|
407
|
+
}
|
|
323
408
|
getRoutes(dataSource, services) {
|
|
324
409
|
return (0, routes_1.default)(dataSource, this.options, services);
|
|
325
410
|
}
|
|
@@ -333,11 +418,9 @@ class Agent extends framework_mounter_1.default {
|
|
|
333
418
|
const routes = this.getRoutes(dataSource, services);
|
|
334
419
|
await Promise.all(routes.map(route => route.bootstrap()));
|
|
335
420
|
// Initialize MCP server if enabled via mountAiMcpServer()
|
|
336
|
-
let
|
|
337
|
-
let mcpIsMcpRoute;
|
|
421
|
+
let mcp;
|
|
338
422
|
if (this.mcpEnabled) {
|
|
339
|
-
|
|
340
|
-
await this.initializeMcpServer());
|
|
423
|
+
mcp = await this.initializeMcpServer();
|
|
341
424
|
}
|
|
342
425
|
// Build main router
|
|
343
426
|
const router = new router_1.default();
|
|
@@ -355,7 +438,7 @@ class Agent extends framework_mounter_1.default {
|
|
|
355
438
|
}));
|
|
356
439
|
router.use(correlation_id_1.correlationIdMiddleware);
|
|
357
440
|
routes.forEach(route => route.setupRoutes(router));
|
|
358
|
-
return { router,
|
|
441
|
+
return { router, mcp };
|
|
359
442
|
}
|
|
360
443
|
/**
|
|
361
444
|
* Initialize the MCP server.
|
|
@@ -382,11 +465,11 @@ class Agent extends framework_mounter_1.default {
|
|
|
382
465
|
fileUploads: this.mcpFileUploads,
|
|
383
466
|
agentDispatcher: this.getInProcessDispatcher(),
|
|
384
467
|
});
|
|
385
|
-
const
|
|
386
|
-
const
|
|
468
|
+
const callback = await mcpServer.getHttpCallback();
|
|
469
|
+
const matches = makeIsMcpRoute(this.mcpBasePath);
|
|
387
470
|
mcpLogger('Info', 'Server initialized successfully');
|
|
388
471
|
mcpLogger('Info', 'Tool calls dispatch in-process and skip any middleware mounted in front of the agent');
|
|
389
|
-
return {
|
|
472
|
+
return { callback, matches };
|
|
390
473
|
}
|
|
391
474
|
catch (error) {
|
|
392
475
|
const { message } = error;
|
|
@@ -507,4 +590,4 @@ class Agent extends framework_mounter_1.default {
|
|
|
507
590
|
}
|
|
508
591
|
}
|
|
509
592
|
exports.default = Agent;
|
|
510
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdlbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvYWdlbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFxQkEsOEVBQTBFO0FBQzFFLGlFQUF5QztBQUN6QyxxREFBNkI7QUFDN0IseURBQWlDO0FBQ2pDLDBDQUFrRDtBQUNsRCxrR0FBc0Q7QUFFdEQsK0NBQXVEO0FBQ3ZELDhGQUFvRTtBQUNwRSw0RUFBbUQ7QUFDbkQsc0RBQWtDO0FBQ2xDLDBEQUFzQztBQUN0QyxrR0FBaUY7QUFDakYsMkRBQXdGO0FBQ3hGLGdGQUE4RDtBQUM5RCxrRkFBeUQ7QUFFekQ7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBcUIsS0FBbUMsU0FBUSwyQkFBZ0I7SUFvQjlFOzs7Ozs7Ozs7Ozs7Ozs7T0FlRztJQUNILFlBQVksT0FBcUI7UUFDL0IsTUFBTSxVQUFVLEdBQUcsMkJBQWdCLENBQUMsUUFBUSxDQUFDLDJCQUFnQixDQUFDLFlBQVksQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO1FBQ3JGLEtBQUssQ0FBQyxVQUFVLENBQUMsTUFBTSxFQUFFLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQS9COUMsMkNBQTJDO1FBQ25DLGVBQVUsR0FBRyxLQUFLLENBQUM7UUFPM0IsdUZBQXVGO1FBQy9FLHFCQUFnQixHQUFvQyxJQUFJLENBQUM7UUFFekQsaUJBQVksR0FBRyxLQUFLLENBQUM7UUFzQjNCLElBQUksQ0FBQyxPQUFPLEdBQUcsVUFBVSxDQUFDO1FBQzFCLElBQUksQ0FBQyxVQUFVLEdBQUcsSUFBSSw0Q0FBb0IsQ0FBSTtZQUM1QyxnQ0FBZ0MsRUFBRSxPQUFPLENBQUMsZ0NBQWdDLElBQUksS0FBSztTQUNwRixDQUFDLENBQUM7UUFDSCxJQUFJLENBQUMsb0JBQW9CLEdBQUcsSUFBSSx1QkFBb0IsQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUNqRSxJQUFJLENBQUMsZUFBZSxH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
|
|
593
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdlbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvYWdlbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFxQkEsOEVBQTBFO0FBQzFFLGlFQUF5QztBQUN6QyxxREFBNkI7QUFDN0IseURBQWlDO0FBQ2pDLDBDQUFrRDtBQUNsRCxrR0FBc0Q7QUFFdEQsK0NBQXVEO0FBQ3ZELDZDQUEyRDtBQUMzRCxrRUFBeUM7QUFDekMsOEZBQW9FO0FBQ3BFLDRFQUFtRDtBQUNuRCxzREFBa0M7QUFDbEMsMERBQXNDO0FBQ3RDLGtHQUFpRjtBQUNqRiwyREFBd0Y7QUFDeEYsZ0ZBQThEO0FBQzlELGtGQUF5RDtBQUV6RCw2RkFBNkY7QUFDN0YsK0ZBQStGO0FBQy9GLHFGQUFxRjtBQUNyRixNQUFNLGVBQWUsR0FBRyxDQUFDLFdBQW1CLEVBQUUsRUFBRSxDQUM5QyxpRUFBaUUsV0FBVyxnQkFBZ0I7SUFDNUYsc0JBQXNCLHVCQUFVLHVDQUF1Qyx1QkFBVSxVQUFVO0lBQzNGLEdBQUcsdUJBQVUsd0NBQXdDLENBQUM7QUFFeEQ7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBcUIsS0FBbUMsU0FBUSwyQkFBZ0I7SUE4QjlFOzs7Ozs7Ozs7Ozs7Ozs7T0FlRztJQUNILFlBQVksT0FBcUI7UUFDL0IsTUFBTSxVQUFVLEdBQUcsMkJBQWdCLENBQUMsUUFBUSxDQUFDLDJCQUFnQixDQUFDLFlBQVksQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO1FBQ3JGLEtBQUssQ0FBQyxVQUFVLENBQUMsTUFBTSxFQUFFLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQXpDOUMsMkNBQTJDO1FBQ25DLGVBQVUsR0FBRyxLQUFLLENBQUM7UUFPM0IsdUZBQXVGO1FBQy9FLHFCQUFnQixHQUFvQyxJQUFJLENBQUM7UUFFakUsNERBQTREO1FBQ3BELGdCQUFXLEdBQXVCLElBQUksQ0FBQztRQUV2QyxpQkFB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
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where an embedded BFF answers, at the root of the host application. Fixed: the BFF serves its own
|
|
3
|
+
* `/oauth/*` and `/docs`, which would otherwise collide with the MCP server's root paths and with
|
|
4
|
+
* whatever the host already serves.
|
|
5
|
+
*/
|
|
6
|
+
export declare const BFF_PREFIX = "/bff";
|
|
7
|
+
/**
|
|
8
|
+
* Matches on the pathname and on a segment boundary, so `/bff?x=1` is claimed and `/bffalo` is not.
|
|
9
|
+
*/
|
|
10
|
+
export declare function isBffRoute(url: string): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* The url the BFF itself expects: it knows nothing of the prefix the host serves it under, and its
|
|
13
|
+
* routes are absolute (`/agent/v1/…`, `/health`). Never yields an empty string — Koa would read that
|
|
14
|
+
* as a malformed request rather than as the root.
|
|
15
|
+
*/
|
|
16
|
+
export declare function stripBffPrefix(url: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Whether an MCP mount path would land inside `/bff`. The MCP server normalizes its `basePath`
|
|
19
|
+
* (trim, leading slash, collapsed and stripped trailing slashes) before deriving
|
|
20
|
+
* `<basePath>/oauth/` and `<basePath>/mcp`, so comparing the raw option to `/bff` would let `bff`,
|
|
21
|
+
* `/bff/` and `/bff/ai` through — each of which claims paths the BFF answers on. The normalization
|
|
22
|
+
* is mirrored rather than imported: mcp-server keeps it internal.
|
|
23
|
+
*/
|
|
24
|
+
export declare function collidesWithBff(mcpBasePath?: string): boolean;
|
|
25
|
+
//# sourceMappingURL=bff-routes.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BFF_PREFIX = void 0;
|
|
4
|
+
exports.isBffRoute = isBffRoute;
|
|
5
|
+
exports.stripBffPrefix = stripBffPrefix;
|
|
6
|
+
exports.collidesWithBff = collidesWithBff;
|
|
7
|
+
/**
|
|
8
|
+
* Where an embedded BFF answers, at the root of the host application. Fixed: the BFF serves its own
|
|
9
|
+
* `/oauth/*` and `/docs`, which would otherwise collide with the MCP server's root paths and with
|
|
10
|
+
* whatever the host already serves.
|
|
11
|
+
*/
|
|
12
|
+
exports.BFF_PREFIX = '/bff';
|
|
13
|
+
/**
|
|
14
|
+
* Matches on the pathname and on a segment boundary, so `/bff?x=1` is claimed and `/bffalo` is not.
|
|
15
|
+
*/
|
|
16
|
+
function isBffRoute(url) {
|
|
17
|
+
const [pathname] = url.split(/[?#]/, 1);
|
|
18
|
+
return pathname === exports.BFF_PREFIX || pathname.startsWith(`${exports.BFF_PREFIX}/`);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The url the BFF itself expects: it knows nothing of the prefix the host serves it under, and its
|
|
22
|
+
* routes are absolute (`/agent/v1/…`, `/health`). Never yields an empty string — Koa would read that
|
|
23
|
+
* as a malformed request rather than as the root.
|
|
24
|
+
*/
|
|
25
|
+
function stripBffPrefix(url) {
|
|
26
|
+
const remainder = url.slice(exports.BFF_PREFIX.length);
|
|
27
|
+
if (remainder === '')
|
|
28
|
+
return '/';
|
|
29
|
+
return remainder.startsWith('/') ? remainder : `/${remainder}`;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Whether an MCP mount path would land inside `/bff`. The MCP server normalizes its `basePath`
|
|
33
|
+
* (trim, leading slash, collapsed and stripped trailing slashes) before deriving
|
|
34
|
+
* `<basePath>/oauth/` and `<basePath>/mcp`, so comparing the raw option to `/bff` would let `bff`,
|
|
35
|
+
* `/bff/` and `/bff/ai` through — each of which claims paths the BFF answers on. The normalization
|
|
36
|
+
* is mirrored rather than imported: mcp-server keeps it internal.
|
|
37
|
+
*/
|
|
38
|
+
function collidesWithBff(mcpBasePath) {
|
|
39
|
+
if (!mcpBasePath)
|
|
40
|
+
return false;
|
|
41
|
+
const trimmed = mcpBasePath.trim();
|
|
42
|
+
if (trimmed === '' || trimmed === '/')
|
|
43
|
+
return false;
|
|
44
|
+
const normalized = (trimmed.startsWith('/') ? trimmed : `/${trimmed}`)
|
|
45
|
+
.replace(/\/+/g, '/')
|
|
46
|
+
.replace(/\/+$/, '');
|
|
47
|
+
return isBffRoute(`${normalized}/mcp`);
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmZmLXJvdXRlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9iZmYtcm91dGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQVVBLGdDQUlDO0FBT0Qsd0NBTUM7QUFTRCwwQ0FZQztBQWhERDs7OztHQUlHO0FBQ1UsUUFBQSxVQUFVLEdBQUcsTUFBTSxDQUFDO0FBRWpDOztHQUVHO0FBQ0gsU0FBZ0IsVUFBVSxDQUFDLEdBQVc7SUFDcEMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBRXhDLE9BQU8sUUFBUSxLQUFLLGtCQUFVLElBQUksUUFBUSxDQUFDLFVBQVUsQ0FBQyxHQUFHLGtCQUFVLEdBQUcsQ0FBQyxDQUFDO0FBQzFFLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsU0FBZ0IsY0FBYyxDQUFDLEdBQVc7SUFDeEMsTUFBTSxTQUFTLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxrQkFBVSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBRS9DLElBQUksU0FBUyxLQUFLLEVBQUU7UUFBRSxPQUFPLEdBQUcsQ0FBQztJQUVqQyxPQUFPLFNBQVMsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsSUFBSSxTQUFTLEVBQUUsQ0FBQztBQUNqRSxDQUFDO0FBRUQ7Ozs7OztHQU1HO0FBQ0gsU0FBZ0IsZUFBZSxDQUFDLFdBQW9CO0lBQ2xELElBQUksQ0FBQyxXQUFXO1FBQUUsT0FBTyxLQUFLLENBQUM7SUFFL0IsTUFBTSxPQUFPLEdBQUcsV0FBVyxDQUFDLElBQUksRUFBRSxDQUFDO0lBRW5DLElBQUksT0FBTyxLQUFLLEVBQUUsSUFBSSxPQUFPLEtBQUssR0FBRztRQUFFLE9BQU8sS0FBSyxDQUFDO0lBRXBELE1BQU0sVUFBVSxHQUFHLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxJQUFJLE9BQU8sRUFBRSxDQUFDO1NBQ25FLE9BQU8sQ0FBQyxNQUFNLEVBQUUsR0FBRyxDQUFDO1NBQ3BCLE9BQU8sQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFFdkIsT0FBTyxVQUFVLENBQUMsR0FBRyxVQUFVLE1BQU0sQ0FBQyxDQUFDO0FBQ3pDLENBQUMifQ==
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { AgentOptionsWithDefaults, BffEmbedOptions, HttpCallback } from './types';
|
|
2
|
+
import type { AgentDispatcher } from '@forestadmin/agent-bff';
|
|
3
|
+
/**
|
|
4
|
+
* Owns the lifecycle of a BFF embedded in the agent process: configuration, dynamic loading of the
|
|
5
|
+
* optional package, build, and the request handler the agent mounts at `/bff`. The agent only wires
|
|
6
|
+
* the callback and delegates start/stop.
|
|
7
|
+
*/
|
|
8
|
+
export default class EmbeddedBff {
|
|
9
|
+
private readonly options;
|
|
10
|
+
private readonly embedOptions;
|
|
11
|
+
private config;
|
|
12
|
+
private bff;
|
|
13
|
+
private stopped;
|
|
14
|
+
constructor(options: AgentOptionsWithDefaults, embedOptions: BffEmbedOptions);
|
|
15
|
+
/**
|
|
16
|
+
* Load the package and validate the options the caller handed to `addBff()`. Called before the
|
|
17
|
+
* agent mounts anything: everything checked here is caller input, and `parseConfig` throws on a
|
|
18
|
+
* mistyped `tokenEncryptionKey`, timezone, timeout or url. Failing here leaves nothing serving,
|
|
19
|
+
* where failing after mount() would leave the host with a live `/forest` and a bricked `/bff`.
|
|
20
|
+
*/
|
|
21
|
+
prepare(): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Build the BFF. Called from agent.start() after mount(), because the dispatcher only reaches the
|
|
24
|
+
* agent's own stack once that stack is mounted.
|
|
25
|
+
*/
|
|
26
|
+
start(dispatcher: AgentDispatcher): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* The whitelist exempts a caller arriving over a loopback socket with no proxy hop, which is
|
|
29
|
+
* exactly what the in-process transport looks like — so every BFF request escapes it. That is the
|
|
30
|
+
* decision, not an oversight: propagating the caller's ip would make the embedded mode stricter
|
|
31
|
+
* than the standalone one, where the whitelist only ever sees the BFF's own host, and would refuse
|
|
32
|
+
* the browsers a third-party UI is made of. What it must not be is silent — an operator who turned
|
|
33
|
+
* the whitelist on to close a door has no other way to learn this door is not part of it.
|
|
34
|
+
*
|
|
35
|
+
* Reads the configuration itself rather than borrowing the one `IpWhitelist` already fetched: that
|
|
36
|
+
* route keeps it private, and reaching into it would put BFF concerns in an unrelated part of the
|
|
37
|
+
* agent. Costs one round-trip at boot. Never fails the boot: a warning is not worth refusing to
|
|
38
|
+
* serve over, and this is the same posture as the rest of the BFF's startup.
|
|
39
|
+
*/
|
|
40
|
+
private warnIfExemptFromIpWhitelist;
|
|
41
|
+
/** Drop what the BFF read from the SaaS. Called on a restart, which means the schema moved. */
|
|
42
|
+
invalidate(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Stop answering. The host application keeps whatever middleware it registered, so without this
|
|
45
|
+
* a stopped agent would go on serving BFF data through a dispatcher pointing at a dead stack.
|
|
46
|
+
*/
|
|
47
|
+
stop(): void;
|
|
48
|
+
/**
|
|
49
|
+
* The callback the agent registers at `/bff`. Answers 503 rather than falling through while the
|
|
50
|
+
* BFF is not serving: a 404 from the host would read as "wrong url" instead of "not available".
|
|
51
|
+
* Boot and shutdown are told apart, because a probe should wait on the first and drain on the
|
|
52
|
+
* second.
|
|
53
|
+
*/
|
|
54
|
+
readonly handle: HttpCallback;
|
|
55
|
+
/**
|
|
56
|
+
* Dynamically load the optional @forestadmin/agent-bff package. Deferred so agents that embed no
|
|
57
|
+
* BFF never load its code at startup.
|
|
58
|
+
*/
|
|
59
|
+
private importPackage;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=embedded-bff.d.ts.map
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const bff_routes_1 = require("./bff-routes");
|
|
37
|
+
/**
|
|
38
|
+
* Serialize the BFF's structured log context onto the message: the agent's logger only accepts an
|
|
39
|
+
* Error as its third argument, so the context would be dropped otherwise. Errors are unfolded by
|
|
40
|
+
* hand — their `message` and `stack` are not enumerable, so `JSON.stringify` alone turns the one
|
|
41
|
+
* value worth logging into `{}`. Never throws — logging must not break a request.
|
|
42
|
+
*/
|
|
43
|
+
function formatLog(message, context) {
|
|
44
|
+
if (!context || Object.keys(context).length === 0)
|
|
45
|
+
return `[BFF] ${message}`;
|
|
46
|
+
try {
|
|
47
|
+
const serialized = JSON.stringify(context, (_key, value) => value instanceof Error
|
|
48
|
+
? { name: value.name, message: value.message, stack: value.stack }
|
|
49
|
+
: value);
|
|
50
|
+
return `[BFF] ${message} ${serialized}`;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return `[BFF] ${message} [unserializable context]`;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Owns the lifecycle of a BFF embedded in the agent process: configuration, dynamic loading of the
|
|
58
|
+
* optional package, build, and the request handler the agent mounts at `/bff`. The agent only wires
|
|
59
|
+
* the callback and delegates start/stop.
|
|
60
|
+
*/
|
|
61
|
+
class EmbeddedBff {
|
|
62
|
+
constructor(options, embedOptions) {
|
|
63
|
+
this.options = options;
|
|
64
|
+
this.embedOptions = embedOptions;
|
|
65
|
+
this.config = null;
|
|
66
|
+
this.bff = null;
|
|
67
|
+
this.stopped = false;
|
|
68
|
+
/**
|
|
69
|
+
* The callback the agent registers at `/bff`. Answers 503 rather than falling through while the
|
|
70
|
+
* BFF is not serving: a 404 from the host would read as "wrong url" instead of "not available".
|
|
71
|
+
* Boot and shutdown are told apart, because a probe should wait on the first and drain on the
|
|
72
|
+
* second.
|
|
73
|
+
*/
|
|
74
|
+
this.handle = (req, res) => {
|
|
75
|
+
if (!this.bff) {
|
|
76
|
+
const type = this.stopped ? 'bff_stopped' : 'bff_not_started';
|
|
77
|
+
const message = this.stopped
|
|
78
|
+
? 'The embedded BFF was stopped with the agent.'
|
|
79
|
+
: 'The embedded BFF is not started yet.';
|
|
80
|
+
res.statusCode = 503;
|
|
81
|
+
res.setHeader('Content-Type', 'application/json');
|
|
82
|
+
res.end(JSON.stringify({ error: { type, status: 503, message } }));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// The BFF knows nothing of the prefix it is served under. `originalUrl` is claimed before the
|
|
86
|
+
// rewrite so a host logger reading it still reports the url the client asked for.
|
|
87
|
+
const bffReq = req;
|
|
88
|
+
bffReq.originalUrl ?? (bffReq.originalUrl = bffReq.url);
|
|
89
|
+
req.url = (0, bff_routes_1.stripBffPrefix)(req.url ?? bff_routes_1.BFF_PREFIX);
|
|
90
|
+
this.bff.callback(req, res);
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Load the package and validate the options the caller handed to `addBff()`. Called before the
|
|
95
|
+
* agent mounts anything: everything checked here is caller input, and `parseConfig` throws on a
|
|
96
|
+
* mistyped `tokenEncryptionKey`, timezone, timeout or url. Failing here leaves nothing serving,
|
|
97
|
+
* where failing after mount() would leave the host with a live `/forest` and a bricked `/bff`.
|
|
98
|
+
*/
|
|
99
|
+
async prepare() {
|
|
100
|
+
const { parseConfig, IN_PROCESS_AGENT_URL } = await this.importPackage();
|
|
101
|
+
const { embedOptions } = this;
|
|
102
|
+
this.config = parseConfig({
|
|
103
|
+
FOREST_AUTH_SECRET: this.options.authSecret,
|
|
104
|
+
FOREST_ENV_SECRET: this.options.envSecret,
|
|
105
|
+
FOREST_SERVER_URL: this.options.forestServerUrl,
|
|
106
|
+
FOREST_APP_URL: this.options.forestAppUrl,
|
|
107
|
+
// Names where the agent answers, never how it is reached: the dispatcher is the transport.
|
|
108
|
+
AGENT_URL: IN_PROCESS_AGENT_URL,
|
|
109
|
+
BFF_TOKEN_ENCRYPTION_KEY: embedOptions.tokenEncryptionKey,
|
|
110
|
+
BFF_ALLOWED_ORIGINS: embedOptions.allowedOrigins?.join(','),
|
|
111
|
+
BFF_DEFAULT_TIMEZONE: embedOptions.defaultTimezone,
|
|
112
|
+
BFF_AGENT_TIMEOUT_MS: embedOptions.agentTimeoutMs?.toString(),
|
|
113
|
+
BFF_AI_TIMEOUT_MS: embedOptions.aiTimeoutMs?.toString(),
|
|
114
|
+
// Off unless asked for: the document is not filtered per caller, so mounting a BFF must not
|
|
115
|
+
// publish the name of every exposed collection and field on an already-open port.
|
|
116
|
+
BFF_OPENAPI_ENABLED: String(embedOptions.openapiEnabled ?? false),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Build the BFF. Called from agent.start() after mount(), because the dispatcher only reaches the
|
|
121
|
+
* agent's own stack once that stack is mounted.
|
|
122
|
+
*/
|
|
123
|
+
async start(dispatcher) {
|
|
124
|
+
if (!this.config)
|
|
125
|
+
await this.prepare();
|
|
126
|
+
// Cleared before the await, not after it: a start() following a stop() has to drop the previous
|
|
127
|
+
// shutdown's flag, while a stop() landing DURING the await must still be seen by the guard below.
|
|
128
|
+
this.stopped = false;
|
|
129
|
+
const { buildBff } = await this.importPackage();
|
|
130
|
+
const bff = await buildBff({
|
|
131
|
+
config: this.config,
|
|
132
|
+
dispatcher,
|
|
133
|
+
basePath: bff_routes_1.BFF_PREFIX,
|
|
134
|
+
// Counters are the schema cache's and the action-endpoint resolver's only channel — they take
|
|
135
|
+
// no logger — and every one of them reports a failure, so they go to the host's logs. Gauges
|
|
136
|
+
// do not: they are periodic cache sizes, and the default console sink reports them at Info,
|
|
137
|
+
// which would flood a host that only asked for a BFF.
|
|
138
|
+
metrics: {
|
|
139
|
+
// Tags carried through: `action_endpoint_error` and `action_endpoint_miss` name the
|
|
140
|
+
// rendering, collection and action that failed, which is the whole of what makes the line
|
|
141
|
+
// actionable — the metric name alone says only that something, somewhere, did not resolve.
|
|
142
|
+
increment: (name, tags) => this.options.logger('Warn', formatLog(`metric ${name}`, tags)),
|
|
143
|
+
gauge: () => undefined,
|
|
144
|
+
},
|
|
145
|
+
logger: (level, message, context) => this.options.logger(level, formatLog(message, context)),
|
|
146
|
+
});
|
|
147
|
+
// stop() may have landed while buildBff() was in flight. Assigning anyway would resurrect a
|
|
148
|
+
// stopped agent's /bff — dispatching into the stack it just tore down instead of answering 503.
|
|
149
|
+
if (this.stopped)
|
|
150
|
+
return;
|
|
151
|
+
this.bff = bff;
|
|
152
|
+
this.options.logger('Info', formatLog(`Embedded BFF mounted on ${bff_routes_1.BFF_PREFIX}`));
|
|
153
|
+
await this.warnIfExemptFromIpWhitelist();
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* The whitelist exempts a caller arriving over a loopback socket with no proxy hop, which is
|
|
157
|
+
* exactly what the in-process transport looks like — so every BFF request escapes it. That is the
|
|
158
|
+
* decision, not an oversight: propagating the caller's ip would make the embedded mode stricter
|
|
159
|
+
* than the standalone one, where the whitelist only ever sees the BFF's own host, and would refuse
|
|
160
|
+
* the browsers a third-party UI is made of. What it must not be is silent — an operator who turned
|
|
161
|
+
* the whitelist on to close a door has no other way to learn this door is not part of it.
|
|
162
|
+
*
|
|
163
|
+
* Reads the configuration itself rather than borrowing the one `IpWhitelist` already fetched: that
|
|
164
|
+
* route keeps it private, and reaching into it would put BFF concerns in an unrelated part of the
|
|
165
|
+
* agent. Costs one round-trip at boot. Never fails the boot: a warning is not worth refusing to
|
|
166
|
+
* serve over, and this is the same posture as the rest of the BFF's startup.
|
|
167
|
+
*/
|
|
168
|
+
async warnIfExemptFromIpWhitelist() {
|
|
169
|
+
try {
|
|
170
|
+
const { isFeatureEnabled } = await this.options.forestAdminClient.getIpWhitelistConfiguration();
|
|
171
|
+
if (!isFeatureEnabled)
|
|
172
|
+
return;
|
|
173
|
+
this.options.logger('Warn', formatLog(`The IP whitelist is enabled for this environment, but requests served under ` +
|
|
174
|
+
`${bff_routes_1.BFF_PREFIX} are not subject to it: they reach the agent in-process, which the ` +
|
|
175
|
+
`whitelist exempts as a trusted loopback caller. A resolved API key or a valid OAuth ` +
|
|
176
|
+
`session is still required.`));
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
this.options.logger('Debug', formatLog('Could not read the IP whitelist configuration', {
|
|
180
|
+
cause: error instanceof Error ? error.message : String(error),
|
|
181
|
+
}));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/** Drop what the BFF read from the SaaS. Called on a restart, which means the schema moved. */
|
|
185
|
+
invalidate() {
|
|
186
|
+
this.bff?.invalidate();
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Stop answering. The host application keeps whatever middleware it registered, so without this
|
|
190
|
+
* a stopped agent would go on serving BFF data through a dispatcher pointing at a dead stack.
|
|
191
|
+
*/
|
|
192
|
+
stop() {
|
|
193
|
+
this.bff = null;
|
|
194
|
+
this.stopped = true;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Dynamically load the optional @forestadmin/agent-bff package. Deferred so agents that embed no
|
|
198
|
+
* BFF never load its code at startup.
|
|
199
|
+
*/
|
|
200
|
+
async importPackage() {
|
|
201
|
+
try {
|
|
202
|
+
return await Promise.resolve().then(() => __importStar(require('@forestadmin/agent-bff')));
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
// The original reason is kept: the package resolves from the host's own node_modules, so this
|
|
206
|
+
// also fires on a broken transitive dependency, a SyntaxError from a partial install, or a
|
|
207
|
+
// throw during the package's own evaluation — none of which "install it" would fix.
|
|
208
|
+
const { message } = error;
|
|
209
|
+
const wrapped = new Error(`The embedded BFF requires the \`@forestadmin/agent-bff\` package, which failed to ` +
|
|
210
|
+
`load: ${message}. Install it with \`npm install @forestadmin/agent-bff\`.`);
|
|
211
|
+
// Assigned rather than passed to the constructor: the repo targets ES2020, whose lib types no
|
|
212
|
+
// options bag on Error. A plain assignment carries the cause on every runtime, and the reason
|
|
213
|
+
// is in the message regardless of what reads it.
|
|
214
|
+
wrapped.cause = error;
|
|
215
|
+
throw wrapped;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
exports.default = EmbeddedBff;
|
|
220
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW1iZWRkZWQtYmZmLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2VtYmVkZGVkLWJmZi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUdBLDZDQUEwRDtBQUUxRDs7Ozs7R0FLRztBQUNILFNBQVMsU0FBUyxDQUFDLE9BQWUsRUFBRSxPQUFpQztJQUNuRSxJQUFJLENBQUMsT0FBTyxJQUFJLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsTUFBTSxLQUFLLENBQUM7UUFBRSxPQUFPLFNBQVMsT0FBTyxFQUFFLENBQUM7SUFFN0UsSUFBSSxDQUFDO1FBQ0gsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxJQUFJLEVBQUUsS0FBSyxFQUFFLEVBQUUsQ0FDekQsS0FBSyxZQUFZLEtBQUs7WUFDcEIsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLEtBQUssQ0FBQyxJQUFJLEVBQUUsT0FBTyxFQUFFLEtBQUssQ0FBQyxPQUFPLEVBQUUsS0FBSyxFQUFFLEtBQUssQ0FBQyxLQUFLLEVBQUU7WUFDbEUsQ0FBQyxDQUFDLEtBQUssQ0FDVixDQUFDO1FBRUYsT0FBTyxTQUFTLE9BQU8sSUFBSSxVQUFVLEVBQUUsQ0FBQztJQUMxQyxDQUFDO0lBQUMsTUFBTSxDQUFDO1FBQ1AsT0FBTyxTQUFTLE9BQU8sMkJBQTJCLENBQUM7SUFDckQsQ0FBQztBQUNILENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBcUIsV0FBVztJQUs5QixZQUNtQixPQUFpQyxFQUNqQyxZQUE2QjtRQUQ3QixZQUFPLEdBQVAsT0FBTyxDQUEwQjtRQUNqQyxpQkFBWSxHQUFaLFlBQVksQ0FBaUI7UUFOeEMsV0FBTSxHQUFxQixJQUFJLENBQUM7UUFDaEMsUUFBRyxHQUFlLElBQUksQ0FBQztRQUN2QixZQUFPLEdBQUcsS0FBSyxDQUFDO1FBa0l4Qjs7Ozs7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
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { HttpCallback,
|
|
1
|
+
import type { HttpCallback, RootHandler } from './types';
|
|
2
2
|
import type { Logger } from '@forestadmin/datasource-toolkit';
|
|
3
3
|
import Router from '@koa/router';
|
|
4
4
|
import InProcessDispatcher from './mcp-in-process-dispatcher';
|
|
@@ -11,16 +11,20 @@ export default class FrameworkMounter {
|
|
|
11
11
|
protected readonly prefix: string;
|
|
12
12
|
private readonly logger;
|
|
13
13
|
private readonly fastifyAdapter;
|
|
14
|
-
private readonly
|
|
14
|
+
private readonly rootMiddleware;
|
|
15
15
|
private readonly inProcessDispatcher;
|
|
16
16
|
private inProcessHookRegistered;
|
|
17
17
|
/** Compute the prefix that the main router should be mounted at in the client's application */
|
|
18
18
|
private get completeMountPrefix();
|
|
19
19
|
constructor(prefix: string, logger: Logger);
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
21
|
+
* Register the MCP server at the root of the host application, or `null` to unregister it. The
|
|
22
|
+
* matcher comes with the callback: there is no default pattern set, so nothing can claim host
|
|
23
|
+
* urls by accident. Call this before mount() or remount().
|
|
22
24
|
*/
|
|
23
|
-
protected setMcpCallback(
|
|
25
|
+
protected setMcpCallback(handler: RootHandler | null): void;
|
|
26
|
+
/** Serve an embedded BFF at `/bff`. Pass null to stop answering there. */
|
|
27
|
+
protected setBffCallback(callback: HttpCallback | null): void;
|
|
24
28
|
/**
|
|
25
29
|
* Dispatcher that runs agent-client requests against the agent's own `/forest` stack in-memory.
|
|
26
30
|
* The handler is rebuilt on every (re)mount so a captured reference never goes stale.
|
|
@@ -7,9 +7,10 @@ const router_1 = __importDefault(require("@koa/router"));
|
|
|
7
7
|
const http_1 = require("http");
|
|
8
8
|
const koa_1 = __importDefault(require("koa"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const bff_routes_1 = require("./bff-routes");
|
|
10
11
|
const fastify_adapter_1 = __importDefault(require("./fastify-adapter"));
|
|
11
12
|
const mcp_in_process_dispatcher_1 = __importDefault(require("./mcp-in-process-dispatcher"));
|
|
12
|
-
const
|
|
13
|
+
const root_middleware_1 = __importDefault(require("./root-middleware"));
|
|
13
14
|
class FrameworkMounter {
|
|
14
15
|
/** Compute the prefix that the main router should be mounted at in the client's application */
|
|
15
16
|
get completeMountPrefix() {
|
|
@@ -23,14 +24,20 @@ class FrameworkMounter {
|
|
|
23
24
|
this.prefix = prefix;
|
|
24
25
|
this.logger = logger;
|
|
25
26
|
this.fastifyAdapter = new fastify_adapter_1.default(logger);
|
|
26
|
-
this.
|
|
27
|
+
this.rootMiddleware = new root_middleware_1.default(logger);
|
|
27
28
|
this.inProcessDispatcher = new mcp_in_process_dispatcher_1.default(logger);
|
|
28
29
|
}
|
|
29
30
|
/**
|
|
30
|
-
*
|
|
31
|
+
* Register the MCP server at the root of the host application, or `null` to unregister it. The
|
|
32
|
+
* matcher comes with the callback: there is no default pattern set, so nothing can claim host
|
|
33
|
+
* urls by accident. Call this before mount() or remount().
|
|
31
34
|
*/
|
|
32
|
-
setMcpCallback(
|
|
33
|
-
this.
|
|
35
|
+
setMcpCallback(handler) {
|
|
36
|
+
this.rootMiddleware.set('mcp', handler);
|
|
37
|
+
}
|
|
38
|
+
/** Serve an embedded BFF at `/bff`. Pass null to stop answering there. */
|
|
39
|
+
setBffCallback(callback) {
|
|
40
|
+
this.rootMiddleware.set('bff', callback && { callback, matches: bff_routes_1.isBffRoute });
|
|
34
41
|
}
|
|
35
42
|
/**
|
|
36
43
|
* Dispatcher that runs agent-client requests against the agent's own `/forest` stack in-memory.
|
|
@@ -101,7 +108,7 @@ class FrameworkMounter {
|
|
|
101
108
|
*/
|
|
102
109
|
mountOnExpress(express) {
|
|
103
110
|
// MCP middleware - the callback handles its own path filtering and calls next() for non-MCP routes
|
|
104
|
-
express.use(this.
|
|
111
|
+
express.use(this.rootMiddleware.getExpressMiddleware());
|
|
105
112
|
// Mount main forest routes at /{prefix}/forest
|
|
106
113
|
express.use(this.completeMountPrefix, this.getConnectCallback(false));
|
|
107
114
|
this.logger('Info', `Successfully mounted on Express.js`);
|
|
@@ -113,7 +120,7 @@ class FrameworkMounter {
|
|
|
113
120
|
*/
|
|
114
121
|
mountOnFastify(fastify) {
|
|
115
122
|
// MCP middleware at root - the callback handles its own path filtering
|
|
116
|
-
this.fastifyAdapter.useCallback(fastify, this.
|
|
123
|
+
this.fastifyAdapter.useCallback(fastify, this.rootMiddleware.getExpressMiddleware(), '/');
|
|
117
124
|
// Mount main forest routes
|
|
118
125
|
const callback = this.getConnectCallback(false);
|
|
119
126
|
this.fastifyAdapter.useCallback(fastify, callback, this.completeMountPrefix);
|
|
@@ -138,7 +145,7 @@ class FrameworkMounter {
|
|
|
138
145
|
parentRouter.use(driverRouter.routes());
|
|
139
146
|
});
|
|
140
147
|
// MCP middleware - intercepts MCP routes before they reach Koa's body parser
|
|
141
|
-
koa.use(this.
|
|
148
|
+
koa.use(this.rootMiddleware.getKoaMiddleware());
|
|
142
149
|
koa.use(parentRouter.routes());
|
|
143
150
|
this.logger('Info', `Successfully mounted on Koa`);
|
|
144
151
|
return this;
|
|
@@ -152,13 +159,13 @@ class FrameworkMounter {
|
|
|
152
159
|
const callback = this.getConnectCallback(false);
|
|
153
160
|
if (adapter.constructor.name === 'ExpressAdapter') {
|
|
154
161
|
// MCP middleware at root - the callback handles its own path filtering
|
|
155
|
-
nestJs.use(this.
|
|
162
|
+
nestJs.use(this.rootMiddleware.getExpressMiddleware());
|
|
156
163
|
// Mount main forest routes
|
|
157
164
|
nestJs.use(this.completeMountPrefix, callback);
|
|
158
165
|
}
|
|
159
166
|
else {
|
|
160
167
|
// Fastify adapter - MCP middleware at root
|
|
161
|
-
this.fastifyAdapter.useCallback(nestJs, this.
|
|
168
|
+
this.fastifyAdapter.useCallback(nestJs, this.rootMiddleware.getExpressMiddleware(), '/');
|
|
162
169
|
this.fastifyAdapter.useCallback(nestJs, callback, this.completeMountPrefix);
|
|
163
170
|
}
|
|
164
171
|
this.logger('Info', `Successfully mounted on NestJS`);
|
|
@@ -176,9 +183,9 @@ class FrameworkMounter {
|
|
|
176
183
|
return (req, res) => {
|
|
177
184
|
// For standalone server (nested), check MCP callback first
|
|
178
185
|
// The MCP callback handles its own path filtering
|
|
179
|
-
const
|
|
180
|
-
if (nested &&
|
|
181
|
-
|
|
186
|
+
const rootCallback = this.rootMiddleware.getCallback();
|
|
187
|
+
if (nested && rootCallback) {
|
|
188
|
+
rootCallback(req, res, () => {
|
|
182
189
|
// next() called means not an MCP route - forward to main handler
|
|
183
190
|
if (handler) {
|
|
184
191
|
handler(req, res);
|
|
@@ -201,4 +208,4 @@ class FrameworkMounter {
|
|
|
201
208
|
}
|
|
202
209
|
}
|
|
203
210
|
exports.default = FrameworkMounter;
|
|
204
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
211
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZnJhbWV3b3JrLW1vdW50ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvZnJhbWV3b3JrLW1vdW50ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFLQSx5REFBaUM7QUFDakMsK0JBQW9DO0FBQ3BDLDhDQUFzQjtBQUN0QixnREFBd0I7QUFFeEIsNkNBQTBDO0FBQzFDLHdFQUErQztBQUMvQyw0RkFBOEQ7QUFDOUQsd0VBQStDO0FBRS9DLE1BQXFCLGdCQUFnQjtJQWdCbkMsK0ZBQStGO0lBQy9GLElBQVksbUJBQW1CO1FBQzdCLE9BQU8sY0FBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxNQUFNLEVBQUUsUUFBUSxDQUFDLENBQUM7SUFDckQsQ0FBQztJQUVELFlBQVksTUFBYyxFQUFFLE1BQWM7UUFqQnpCLGlCQUFZLEdBQTRCLEVBQUUsQ0FBQztRQUMzQyxnQkFBVyxHQUEwQyxFQUFFLENBQUM7UUFFeEQsV0FBTSxHQUE0QixFQUFFLENBQUM7UUFPOUMsNEJBQXVCLEdBQUcsS0FBSyxDQUFDO1FBUXRDLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO1FBQ3JCLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO1FBQ3JCLElBQUksQ0FBQyxjQUFjLEdBQUcsSUFBSSx5QkFBYyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ2pELElBQUksQ0FBQyxjQUFjLEdBQUcsSUFBSSx5QkFBYyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ2pELElBQUksQ0FBQyxtQkFBbUIsR0FBRyxJQUFJLG1DQUFtQixDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQzdELENBQUM7SUFFRDs7OztPQUlHO0lBQ08sY0FBYyxDQUFDLE9BQTJCO1FBQ2xELElBQUksQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxPQUFPLENBQUMsQ0FBQztJQUMxQyxDQUFDO0lBRUQsMEVBQTBFO0lBQ2hFLGNB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
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { TSchema } from '@forestadmin/datasource-customizer';
|
|
|
3
3
|
import Agent from './agent';
|
|
4
4
|
export declare function createAgent<S extends TSchema = TSchema>(options: AgentOptions): Agent<S>;
|
|
5
5
|
export { Agent };
|
|
6
|
-
export { AgentOptions, WorkflowExecutorEmbedOptions } from './types';
|
|
6
|
+
export { AgentOptions, BffEmbedOptions, WorkflowExecutorEmbedOptions } from './types';
|
|
7
7
|
export * from '@forestadmin/datasource-customizer';
|
|
8
8
|
export { default as SchemaGenerator } from './utils/forest-schema/generator';
|
|
9
9
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { HttpCallback, RootHandler } from './types';
|
|
2
|
+
import type { Logger } from '@forestadmin/datasource-toolkit';
|
|
3
|
+
import type Koa from 'koa';
|
|
4
|
+
/**
|
|
5
|
+
* Handlers the agent serves at the root of the host application rather than under its own router
|
|
6
|
+
* prefix — the MCP server, the embedded BFF. Each one claims a set of paths through its matcher;
|
|
7
|
+
* anything else falls through to the host, untouched.
|
|
8
|
+
*
|
|
9
|
+
* Registered by name so a handler can be replaced or removed on a restart without the mount points
|
|
10
|
+
* having to know what is registered.
|
|
11
|
+
*/
|
|
12
|
+
export default class RootMiddleware {
|
|
13
|
+
private readonly handlers;
|
|
14
|
+
private readonly logger;
|
|
15
|
+
constructor(logger: Logger);
|
|
16
|
+
set(name: string, handler: RootHandler | null): void;
|
|
17
|
+
private entryFor;
|
|
18
|
+
/**
|
|
19
|
+
* First registered matcher wins. Overlapping matchers cannot be caught at registration — the
|
|
20
|
+
* predicates are opaque — so the winner is named here instead: a request answered by the wrong
|
|
21
|
+
* handler is otherwise indistinguishable from one whose route was never registered.
|
|
22
|
+
*/
|
|
23
|
+
private handlerFor;
|
|
24
|
+
/**
|
|
25
|
+
* Connect-style middleware. A handler that decides not to answer calls `next()`, which passes the
|
|
26
|
+
* request on to the host exactly as if nothing had been registered.
|
|
27
|
+
*/
|
|
28
|
+
getExpressMiddleware(): HttpCallback;
|
|
29
|
+
getKoaMiddleware(): Koa.Middleware;
|
|
30
|
+
/** The combined callback, or null when nothing is registered and the host needs no detour. */
|
|
31
|
+
getCallback(): HttpCallback | null;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=root-middleware.d.ts.map
|
|
@@ -0,0 +1,70 @@
|
|
|
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
|
+
const express_to_koa_1 = __importDefault(require("./utils/express-to-koa"));
|
|
7
|
+
/**
|
|
8
|
+
* Handlers the agent serves at the root of the host application rather than under its own router
|
|
9
|
+
* prefix — the MCP server, the embedded BFF. Each one claims a set of paths through its matcher;
|
|
10
|
+
* anything else falls through to the host, untouched.
|
|
11
|
+
*
|
|
12
|
+
* Registered by name so a handler can be replaced or removed on a restart without the mount points
|
|
13
|
+
* having to know what is registered.
|
|
14
|
+
*/
|
|
15
|
+
class RootMiddleware {
|
|
16
|
+
constructor(logger) {
|
|
17
|
+
this.handlers = new Map();
|
|
18
|
+
this.logger = logger;
|
|
19
|
+
}
|
|
20
|
+
set(name, handler) {
|
|
21
|
+
if (handler)
|
|
22
|
+
this.handlers.set(name, handler);
|
|
23
|
+
else
|
|
24
|
+
this.handlers.delete(name);
|
|
25
|
+
}
|
|
26
|
+
entryFor(url) {
|
|
27
|
+
for (const entry of this.handlers) {
|
|
28
|
+
if (entry[1].matches(url))
|
|
29
|
+
return entry;
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* First registered matcher wins. Overlapping matchers cannot be caught at registration — the
|
|
35
|
+
* predicates are opaque — so the winner is named here instead: a request answered by the wrong
|
|
36
|
+
* handler is otherwise indistinguishable from one whose route was never registered.
|
|
37
|
+
*/
|
|
38
|
+
handlerFor(url) {
|
|
39
|
+
const entry = this.entryFor(url);
|
|
40
|
+
if (!entry)
|
|
41
|
+
return null;
|
|
42
|
+
this.logger('Debug', `Root middleware: '${entry[0]}' claims ${url}`);
|
|
43
|
+
return entry[1].callback;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Connect-style middleware. A handler that decides not to answer calls `next()`, which passes the
|
|
47
|
+
* request on to the host exactly as if nothing had been registered.
|
|
48
|
+
*/
|
|
49
|
+
getExpressMiddleware() {
|
|
50
|
+
return (req, res, next) => {
|
|
51
|
+
const handler = this.handlerFor(req.url ?? '/');
|
|
52
|
+
if (handler) {
|
|
53
|
+
handler(req, res, next);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
next?.();
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
getKoaMiddleware() {
|
|
60
|
+
return (0, express_to_koa_1.default)(this.getExpressMiddleware(), url => this.entryFor(url) !== null);
|
|
61
|
+
}
|
|
62
|
+
/** The combined callback, or null when nothing is registered and the host needs no detour. */
|
|
63
|
+
getCallback() {
|
|
64
|
+
if (this.handlers.size === 0)
|
|
65
|
+
return null;
|
|
66
|
+
return this.getExpressMiddleware();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
exports.default = RootMiddleware;
|
|
70
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm9vdC1taWRkbGV3YXJlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3Jvb3QtbWlkZGxld2FyZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUlBLDRFQUFrRDtBQUVsRDs7Ozs7OztHQU9HO0FBQ0gsTUFBcUIsY0FBYztJQUlqQyxZQUFZLE1BQWM7UUFIVCxhQUFRLEdBQUcsSUFBSSxHQUFHLEVBQXVCLENBQUM7UUFJekQsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7SUFDdkIsQ0FBQztJQUVELEdBQUcsQ0FBQyxJQUFZLEVBQUUsT0FBMkI7UUFDM0MsSUFBSSxPQUFPO1lBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUFDOztZQUN6QyxJQUFJLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUNsQyxDQUFDO0lBRU8sUUFBUSxDQUFDLEdBQVc7UUFDMUIsS0FBSyxNQUFNLEtBQUssSUFBSSxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDbEMsSUFBSSxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQztnQkFBRSxPQUFPLEtBQUssQ0FBQztRQUMxQyxDQUFDO1FBRUQsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNLLFVBQVUsQ0FBQyxHQUFXO1FBQzVCLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUM7UUFFakMsSUFBSSxDQUFDLEtBQUs7WUFBRSxPQUFPLElBQUksQ0FBQztRQUV4QixJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxxQkFBcUIsS0FBSyxDQUFDLENBQUMsQ0FBQyxZQUFZLEdBQUcsRUFBRSxDQUFDLENBQUM7UUFFckUsT0FBTyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDO0lBQzNCLENBQUM7SUFFRDs7O09BR0c7SUFDSCxvQkFBb0I7UUFDbEIsT0FBTyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsSUFBSSxFQUFFLEVBQUU7WUFDeEIsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsR0FBRyxJQUFJLEdBQUcsQ0FBQyxDQUFDO1lBRWhELElBQUksT0FBTyxFQUFFLENBQUM7Z0JBQ1osT0FBTyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsSUFBSSxDQUFDLENBQUM7Z0JBRXhCLE9BQU87WUFDVCxDQUFDO1lBRUQsSUFBSSxFQUFFLEVBQUUsQ0FBQztRQUNYLENBQUMsQ0FBQztJQUNKLENBQUM7SUFFRCxnQkFBZ0I7UUFDZCxPQUFPLElBQUEsd0JBQVksRUFBQyxJQUFJLENBQUMsb0JBQW9CLEVBQUUsRUFBRSxHQUFHLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLEtBQUssSUFBSSxDQUFDLENBQUM7SUFDdkYsQ0FBQztJQUVELDhGQUE4RjtJQUM5RixXQUFXO1FBQ1QsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksS0FBSyxDQUFDO1lBQUUsT0FBTyxJQUFJLENBQUM7UUFFMUMsT0FBTyxJQUFJLENBQUMsb0JBQW9CLEVBQUUsQ0FBQztJQUNyQyxDQUFDO0NBQ0Y7QUFoRUQsaUNBZ0VDIn0=
|
package/dist/types.d.ts
CHANGED
|
@@ -151,6 +151,47 @@ export type WorkflowExecutorEmbedOptions = Omit<WorkflowExecutorTuningOptions, '
|
|
|
151
151
|
*/
|
|
152
152
|
encryptionKey?: string;
|
|
153
153
|
};
|
|
154
|
+
/**
|
|
155
|
+
* Options of an embedded BFF. Everything the BFF shares with the agent — the secrets, the Forest
|
|
156
|
+
* urls, the logger — is inherited rather than repeated: a divergent `authSecret` would make the
|
|
157
|
+
* agent reject the very tokens the BFF mints, as an opaque 401.
|
|
158
|
+
*/
|
|
159
|
+
export type BffEmbedOptions = {
|
|
160
|
+
/**
|
|
161
|
+
* Base64-encoded 32-byte AES-256 key encrypting stored refresh tokens. Gates the OAuth
|
|
162
|
+
* login/refresh flow (mode 1) and, with it, the AI relay.
|
|
163
|
+
*
|
|
164
|
+
* It is not an access control. The data routes accept a `bff_access` bearer whether or not this
|
|
165
|
+
* is set, because that token is verified against the agent's `authSecret` alone. Omitting it
|
|
166
|
+
* closes the login flow, not the session-bearer path — what protects that path is `authSecret`.
|
|
167
|
+
*/
|
|
168
|
+
tokenEncryptionKey?: string;
|
|
169
|
+
/**
|
|
170
|
+
* Exact origins (scheme + host + port) allowed to call the BFF from a browser. No wildcard.
|
|
171
|
+
* Empty means no cross-origin browser access at all, which for a backend-for-frontend is
|
|
172
|
+
* almost always a mistake — the BFF warns about it at startup.
|
|
173
|
+
*/
|
|
174
|
+
allowedOrigins?: string[];
|
|
175
|
+
/** Fallback IANA timezone for a request that carries none. */
|
|
176
|
+
defaultTimezone?: string;
|
|
177
|
+
/**
|
|
178
|
+
* How long a call to the agent may take. Defaults to 10s.
|
|
179
|
+
*
|
|
180
|
+
* It bounds how long the BFF waits, and cancels nothing: an action or a write cut off at the
|
|
181
|
+
* deadline keeps running inside the agent and still commits, so a client retrying on the timeout
|
|
182
|
+
* can double the mutation. Raise it rather than relying on a retry for operations that are not
|
|
183
|
+
* idempotent.
|
|
184
|
+
*/
|
|
185
|
+
agentTimeoutMs?: number;
|
|
186
|
+
/** How long the AI relay waits for the Forest server. Defaults to 120s. */
|
|
187
|
+
aiTimeoutMs?: number;
|
|
188
|
+
/**
|
|
189
|
+
* Serve `/bff/docs` and `/bff/agent/openapi.json`. Defaults to `false` when embedded: the
|
|
190
|
+
* document is not filtered per caller, so any authenticated caller reads the name of every
|
|
191
|
+
* exposed collection, relation and field.
|
|
192
|
+
*/
|
|
193
|
+
openapiEnabled?: boolean;
|
|
194
|
+
};
|
|
154
195
|
export type AuditTrailRuntime = AuditTrailConfig & {
|
|
155
196
|
store: AuditStore;
|
|
156
197
|
close: () => Promise<void>;
|
|
@@ -159,7 +200,12 @@ export type AgentOptionsWithDefaults = Readonly<Required<Omit<AgentOptions, 'aud
|
|
|
159
200
|
auditTrail: AuditTrailRuntime | null;
|
|
160
201
|
}>;
|
|
161
202
|
export type HttpCallback = (req: IncomingMessage, res: ServerResponse, next?: () => void) => void;
|
|
162
|
-
export type
|
|
203
|
+
export type RouteMatcher = (url: string) => boolean;
|
|
204
|
+
/** A handler served at the root of the host application, with the urls it claims. */
|
|
205
|
+
export type RootHandler = {
|
|
206
|
+
callback: HttpCallback;
|
|
207
|
+
matches: RouteMatcher;
|
|
208
|
+
};
|
|
163
209
|
export declare enum HttpCode {
|
|
164
210
|
BadRequest = 400,
|
|
165
211
|
Unauthorized = 401,
|
package/dist/types.js
CHANGED
|
@@ -22,4 +22,4 @@ var RouteType;
|
|
|
22
22
|
RouteType[RouteType["Authentication"] = 3] = "Authentication";
|
|
23
23
|
RouteType[RouteType["PrivateRoute"] = 4] = "PrivateRoute";
|
|
24
24
|
})(RouteType || (exports.RouteType = RouteType = {}));
|
|
25
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
25
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBbU5BLElBQVksUUFVWDtBQVZELFdBQVksUUFBUTtJQUNsQixxREFBZ0IsQ0FBQTtJQUNoQix5REFBa0IsQ0FBQTtJQUNsQixtREFBZSxDQUFBO0lBQ2YsaURBQWMsQ0FBQTtJQUNkLDJEQUFtQixDQUFBO0lBQ25CLCtEQUFxQixDQUFBO0lBQ3JCLHVFQUF5QixDQUFBO0lBQ3pCLG1EQUFlLENBQUE7SUFDZixxQ0FBUSxDQUFBO0FBQ1YsQ0FBQyxFQVZXLFFBQVEsd0JBQVIsUUFBUSxRQVVuQjtBQUVELElBQVksU0FPWDtBQVBELFdBQVksU0FBUztJQUNuQixpR0FBaUc7SUFDakcsMkRBQWlCLENBQUE7SUFDakIseURBQWdCLENBQUE7SUFDaEIsdURBQWUsQ0FBQTtJQUNmLDZEQUFrQixDQUFBO0lBQ2xCLHlEQUFnQixDQUFBO0FBQ2xCLENBQUMsRUFQVyxTQUFTLHlCQUFULFNBQVMsUUFPcEIifQ==
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forestadmin/agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.100.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"license": "GPL-3.0",
|
|
6
6
|
"publishConfig": {
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@fastify/express": "^1.1.0",
|
|
52
|
+
"@forestadmin/agent-bff": "1.29.0",
|
|
52
53
|
"@forestadmin/datasource-sql": "1.17.14",
|
|
53
54
|
"@forestadmin/workflow-executor": "1.28.0",
|
|
54
55
|
"@nestjs/common": "^11.1.24",
|
|
@@ -56,28 +57,34 @@
|
|
|
56
57
|
"@nestjs/platform-express": "^11.1.24",
|
|
57
58
|
"@nestjs/platform-fastify": "^11.1.24",
|
|
58
59
|
"@shopify/jest-koa-mocks": "^3.1.0",
|
|
59
|
-
"sqlite3": "^5.1.7",
|
|
60
60
|
"@types/json-api-serializer": "^2.6.3",
|
|
61
61
|
"@types/jsonwebtoken": "^9.0.1",
|
|
62
62
|
"@types/koa": "^2.13.5",
|
|
63
63
|
"@types/koa__cors": "^3.3.0",
|
|
64
64
|
"@types/superagent": "^8.1.9",
|
|
65
|
+
"@types/supertest": "^6.0.2",
|
|
65
66
|
"express": "^4.18.2",
|
|
66
67
|
"fastify": "^3.29.0",
|
|
67
68
|
"fastify2": "npm:fastify@^2.15.3",
|
|
68
69
|
"fastify4": "npm:fastify@^4.28.0",
|
|
69
70
|
"openid-client": "^5.7.1",
|
|
70
71
|
"reflect-metadata": "^0.1.13",
|
|
71
|
-
"rxjs": "^7.8.0"
|
|
72
|
+
"rxjs": "^7.8.0",
|
|
73
|
+
"sqlite3": "^5.1.7",
|
|
74
|
+
"supertest": "^7.1.3"
|
|
72
75
|
},
|
|
73
76
|
"peerDependencies": {
|
|
74
77
|
"@fastify/express": "^1.1.0 || ^2.0.0 || ^3.0.0 || ^4.0.0",
|
|
78
|
+
"@forestadmin/agent-bff": "1.29.0",
|
|
75
79
|
"@forestadmin/workflow-executor": "1.28.0"
|
|
76
80
|
},
|
|
77
81
|
"peerDependenciesMeta": {
|
|
78
82
|
"@fastify/express": {
|
|
79
83
|
"optional": true
|
|
80
84
|
},
|
|
85
|
+
"@forestadmin/agent-bff": {
|
|
86
|
+
"optional": true
|
|
87
|
+
},
|
|
81
88
|
"@forestadmin/workflow-executor": {
|
|
82
89
|
"optional": true
|
|
83
90
|
}
|
package/dist/mcp-middleware.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { HttpCallback, McpRouteMatcher } from './types';
|
|
2
|
-
import type Koa from 'koa';
|
|
3
|
-
/**
|
|
4
|
-
* Factory functions for creating MCP middleware for different frameworks.
|
|
5
|
-
* MCP middleware intercepts MCP-specific routes (/.well-known/*, /oauth/*, /mcp)
|
|
6
|
-
* and forwards them to the MCP callback, while passing through other routes.
|
|
7
|
-
*/
|
|
8
|
-
export default class McpMiddleware {
|
|
9
|
-
private mcpHttpCallback;
|
|
10
|
-
private routeMatcher;
|
|
11
|
-
setCallback(callback: HttpCallback | null, routeMatcher?: McpRouteMatcher): void;
|
|
12
|
-
/**
|
|
13
|
-
* Get the current MCP HTTP callback.
|
|
14
|
-
*/
|
|
15
|
-
getCallback(): HttpCallback | null;
|
|
16
|
-
/**
|
|
17
|
-
* Get an Express/Connect-style middleware that forwards requests to the MCP callback.
|
|
18
|
-
* The MCP callback handles path filtering and calls next() for non-MCP routes.
|
|
19
|
-
*/
|
|
20
|
-
getExpressMiddleware(): HttpCallback;
|
|
21
|
-
/**
|
|
22
|
-
* Get a Koa middleware that forwards requests to the MCP callback.
|
|
23
|
-
* Uses expressToKoa wrapper for proper Koa integration.
|
|
24
|
-
*/
|
|
25
|
-
getKoaMiddleware(): Koa.Middleware;
|
|
26
|
-
}
|
|
27
|
-
//# sourceMappingURL=mcp-middleware.d.ts.map
|
package/dist/mcp-middleware.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
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
|
-
const express_to_koa_1 = __importDefault(require("./utils/express-to-koa"));
|
|
7
|
-
/**
|
|
8
|
-
* MCP route patterns that should be intercepted by the MCP middleware.
|
|
9
|
-
*/
|
|
10
|
-
const MCP_ROUTE_PATTERNS = ['/.well-known/', '/oauth/', '/mcp'];
|
|
11
|
-
/**
|
|
12
|
-
* Check if a URL matches any MCP route pattern.
|
|
13
|
-
*/
|
|
14
|
-
function isMcpRoute(url) {
|
|
15
|
-
return MCP_ROUTE_PATTERNS.some(pattern => url.startsWith(pattern));
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Factory functions for creating MCP middleware for different frameworks.
|
|
19
|
-
* MCP middleware intercepts MCP-specific routes (/.well-known/*, /oauth/*, /mcp)
|
|
20
|
-
* and forwards them to the MCP callback, while passing through other routes.
|
|
21
|
-
*/
|
|
22
|
-
class McpMiddleware {
|
|
23
|
-
constructor() {
|
|
24
|
-
this.mcpHttpCallback = null;
|
|
25
|
-
this.routeMatcher = isMcpRoute;
|
|
26
|
-
}
|
|
27
|
-
setCallback(callback, routeMatcher) {
|
|
28
|
-
this.mcpHttpCallback = callback;
|
|
29
|
-
this.routeMatcher = routeMatcher ?? isMcpRoute;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Get the current MCP HTTP callback.
|
|
33
|
-
*/
|
|
34
|
-
getCallback() {
|
|
35
|
-
return this.mcpHttpCallback;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Get an Express/Connect-style middleware that forwards requests to the MCP callback.
|
|
39
|
-
* The MCP callback handles path filtering and calls next() for non-MCP routes.
|
|
40
|
-
*/
|
|
41
|
-
getExpressMiddleware() {
|
|
42
|
-
return (req, res, next) => {
|
|
43
|
-
if (this.mcpHttpCallback) {
|
|
44
|
-
this.mcpHttpCallback(req, res, next);
|
|
45
|
-
}
|
|
46
|
-
else if (next) {
|
|
47
|
-
next();
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Get a Koa middleware that forwards requests to the MCP callback.
|
|
53
|
-
* Uses expressToKoa wrapper for proper Koa integration.
|
|
54
|
-
*/
|
|
55
|
-
getKoaMiddleware() {
|
|
56
|
-
return (0, express_to_koa_1.default)((req, res, next) => {
|
|
57
|
-
if (this.mcpHttpCallback) {
|
|
58
|
-
this.mcpHttpCallback(req, res, next);
|
|
59
|
-
}
|
|
60
|
-
else if (next) {
|
|
61
|
-
next();
|
|
62
|
-
}
|
|
63
|
-
}, url => this.routeMatcher(url));
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
exports.default = McpMiddleware;
|
|
67
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWNwLW1pZGRsZXdhcmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvbWNwLW1pZGRsZXdhcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFHQSw0RUFBa0Q7QUFFbEQ7O0dBRUc7QUFDSCxNQUFNLGtCQUFrQixHQUFHLENBQUMsZUFBZSxFQUFFLFNBQVMsRUFBRSxNQUFNLENBQUMsQ0FBQztBQUVoRTs7R0FFRztBQUNILFNBQVMsVUFBVSxDQUFDLEdBQVc7SUFDN0IsT0FBTyxrQkFBa0IsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7QUFDckUsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxNQUFxQixhQUFhO0lBQWxDO1FBQ1Usb0JBQWUsR0FBd0IsSUFBSSxDQUFDO1FBQzVDLGlCQUFZLEdBQW9CLFVBQVUsQ0FBQztJQTRDckQsQ0FBQztJQTFDQyxXQUFXLENBQUMsUUFBNkIsRUFBRSxZQUE4QjtRQUN2RSxJQUFJLENBQUMsZUFBZSxHQUFHLFFBQVEsQ0FBQztRQUNoQyxJQUFJLENBQUMsWUFBWSxHQUFHLFlBQVksSUFBSSxVQUFVLENBQUM7SUFDakQsQ0FBQztJQUVEOztPQUVHO0lBQ0gsV0FBVztRQUNULE9BQU8sSUFBSSxDQUFDLGVBQWUsQ0FBQztJQUM5QixDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsb0JBQW9CO1FBQ2xCLE9BQU8sQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxFQUFFO1lBQ3hCLElBQUksSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO2dCQUN6QixJQUFJLENBQUMsZUFBZSxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsSUFBSSxDQUFDLENBQUM7WUFDdkMsQ0FBQztpQkFBTSxJQUFJLElBQUksRUFBRSxDQUFDO2dCQUNoQixJQUFJLEVBQUUsQ0FBQztZQUNULENBQUM7UUFDSCxDQUFDLENBQUM7SUFDSixDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsZ0JBQWdCO1FBQ2QsT0FBTyxJQUFBLHdCQUFZLEVBQ2pCLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsRUFBRTtZQUNqQixJQUFJLElBQUksQ0FBQyxlQUFlLEVBQUUsQ0FBQztnQkFDekIsSUFBSSxDQUFDLGVBQWUsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLElBQUksQ0FBQyxDQUFDO1lBQ3ZDLENBQUM7aUJBQU0sSUFBSSxJQUFJLEVBQUUsQ0FBQztnQkFDaEIsSUFBSSxFQUFFLENBQUM7WUFDVCxDQUFDO1FBQ0gsQ0FBQyxFQUNELEdBQUcsQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsQ0FDOUIsQ0FBQztJQUNKLENBQUM7Q0FDRjtBQTlDRCxnQ0E4Q0MifQ==
|