frontmcp 1.1.1 → 1.1.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frontmcp",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "FrontMCP command line interface",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "homepage": "https://docs.agentfront.dev",
@@ -22,7 +22,7 @@
22
22
  "url": "https://github.com/agentfront/frontmcp/issues"
23
23
  },
24
24
  "main": "./src/index.js",
25
- "types": "./index.d.js",
25
+ "types": "./src/index.d.ts",
26
26
  "bin": {
27
27
  "frontmcp": "./src/core/cli.js"
28
28
  },
@@ -31,9 +31,9 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@clack/prompts": "^0.10.0",
34
- "@frontmcp/lazy-zod": "1.1.1",
35
- "@frontmcp/utils": "1.1.1",
36
- "@frontmcp/skills": "1.1.1",
34
+ "@frontmcp/lazy-zod": "1.1.2",
35
+ "@frontmcp/utils": "1.1.2",
36
+ "@frontmcp/skills": "1.1.2",
37
37
  "commander": "^13.0.0",
38
38
  "tslib": "^2.3.0",
39
39
  "vectoriadb": "^2.2.0",
@@ -73,23 +73,38 @@ module.exports = {
73
73
  };
74
74
  `,
75
75
  // #375 — fail the build when the user's @FrontMcp config references
76
- // Node-only storage providers that can't run on Workers. Conditional
77
- // (env-gated) usage still passes; only unconditional declarations throw.
78
- validate: (decoratorConfig) => {
79
- if (!decoratorConfig)
80
- return;
81
- const sqlite = decoratorConfig['sqlite'];
82
- const redis = decoratorConfig['redis'];
76
+ // Node-only storage providers that can't run on Workers.
77
+ //
78
+ // Round 1 only inspected the runtime-evaluated config object. The reporter's
79
+ // failure case was `sqlite: process.env.REDIS_HOST ? {…} : { sqlite: {…} }` —
80
+ // a ternary that may evaluate to `undefined` at decorator-load time (so the
81
+ // runtime check sees nothing) but still ships the Node-only branch in the
82
+ // bundled worker. Round 2 also inspects `info.keysSeenInSource`, which is
83
+ // collected by walking the @FrontMcp({...}) source expression — that captures
84
+ // the property name regardless of whether its value is a literal, a ternary,
85
+ // or a function call. If `sqlite`/`redis` appear at all, fail loud and tell
86
+ // the user how to gate them at the source level.
87
+ validate: (decoratorConfig, info) => {
83
88
  const errors = [];
84
- if (sqlite && typeof sqlite === 'object') {
89
+ const sqliteIsLiteral = decoratorConfig?.['sqlite'] !== undefined &&
90
+ typeof decoratorConfig['sqlite'] === 'object' &&
91
+ decoratorConfig['sqlite'] !== null;
92
+ const redisIsLiteral = decoratorConfig?.['redis'] !== undefined &&
93
+ typeof decoratorConfig['redis'] === 'object' &&
94
+ decoratorConfig['redis'] !== null;
95
+ const sqliteSeen = sqliteIsLiteral || !!info?.keysSeenInSource?.includes('sqlite');
96
+ const redisSeen = redisIsLiteral || !!info?.keysSeenInSource?.includes('redis');
97
+ if (sqliteSeen) {
85
98
  errors.push('sqlite storage is not supported on --target cloudflare (no fs / native modules on Workers). ' +
86
- 'Use Cloudflare KV / Durable Objects, or gate the sqlite branch behind a build-time `define` ' +
87
- 'so the bundler can dead-code-eliminate it.');
99
+ 'Even an env-gated `sqlite: process.env.X ? {...} : undefined` still ships the Node-only ' +
100
+ 'branch in the worker bundle. Use Cloudflare KV / Durable Objects, or move the sqlite ' +
101
+ 'config behind a build-time `define` so the bundler can dead-code-eliminate it.');
88
102
  }
89
- if (redis && typeof redis === 'object') {
103
+ if (redisSeen) {
90
104
  errors.push('ioredis-style `redis` storage is not supported on --target cloudflare (no Node net). ' +
91
- 'Use Vercel KV / Upstash Redis (HTTP), or move the redis config to a runtime branch ' +
92
- 'gated on `globalThis.process?.env`.');
105
+ 'Even an env-gated `redis: process.env.X ? {...} : undefined` still ships the Node-only ' +
106
+ 'branch in the worker bundle. Use Vercel KV / Upstash Redis (HTTP), or move the redis ' +
107
+ 'config behind a build-time `define` so the bundler can dead-code-eliminate it.');
93
108
  }
94
109
  if (errors.length) {
95
110
  throw new Error(`[--target cloudflare] config incompatible with Cloudflare Workers:\n - ${errors.join('\n - ')}`);
@@ -100,10 +115,19 @@ module.exports = {
100
115
  // dist/index.js while the build emitted dist/cloudflare/index.js, and
101
116
  // wrangler deploy silently failed.
102
117
  alwaysWriteConfig: true,
103
- getConfig: (_cwd) => `name = "frontmcp-worker"
118
+ // #374 round-2 merge `frontmcp.config.deployments[].wrangler.{name,
119
+ // compatibilityDate}` into the rendered TOML so values declared in the
120
+ // user's config actually reach `wrangler deploy`. Defaults preserved when
121
+ // the field is absent or no deployment was matched.
122
+ getConfig: (_cwd, deployment) => {
123
+ const wrangler = deployment?.wrangler ?? {};
124
+ const name = wrangler.name ?? 'frontmcp-worker';
125
+ const compatibilityDate = wrangler.compatibilityDate ?? '2024-01-01';
126
+ return `name = "${name}"
104
127
  main = "dist/cloudflare/index.js"
105
- compatibility_date = "2024-01-01"
106
- `,
128
+ compatibility_date = "${compatibilityDate}"
129
+ `;
130
+ },
107
131
  configFileName: 'wrangler.toml',
108
132
  };
109
133
  //# sourceMappingURL=cloudflare.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/cloudflare.ts"],"names":[],"mappings":";;;AAEA;;;;;GAKG;AACU,QAAA,iBAAiB,GAAoB;IAChD,YAAY,EAAE,UAAU;IAExB,gBAAgB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC;;;;;WAKrC,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyDxB;IAEC,oEAAoE;IACpE,qEAAqE;IACrE,yEAAyE;IACzE,QAAQ,EAAE,CAAC,eAAe,EAAE,EAAE;QAC5B,IAAI,CAAC,eAAe;YAAE,OAAO;QAC7B,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;QACzC,MAAM,KAAK,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;QACvC,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YACzC,MAAM,CAAC,IAAI,CACT,8FAA8F;gBAC5F,8FAA8F;gBAC9F,4CAA4C,CAC/C,CAAC;QACJ,CAAC;QACD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YACvC,MAAM,CAAC,IAAI,CACT,uFAAuF;gBACrF,qFAAqF;gBACrF,qCAAqC,CACxC,CAAC;QACJ,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CACb,2EAA2E,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CACnG,CAAC;QACJ,CAAC;IACH,CAAC;IAED,yEAAyE;IACzE,0EAA0E;IAC1E,sEAAsE;IACtE,mCAAmC;IACnC,iBAAiB,EAAE,IAAI;IAEvB,SAAS,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC;;;CAG9B;IAEC,cAAc,EAAE,eAAe;CAChC,CAAC","sourcesContent":["import type { AdapterTemplate } from '../types';\n\n/**\n * Cloudflare Workers adapter - edge deployment on Cloudflare.\n * Compiles to CommonJS and adapts the Express app to Cloudflare's fetch API.\n *\n * @see https://developers.cloudflare.com/workers/\n */\nexport const cloudflareAdapter: AdapterTemplate = {\n moduleFormat: 'commonjs',\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated Cloudflare Workers entry point\n// Generated by: frontmcp build --target cloudflare\nprocess.env.FRONTMCP_SERVERLESS = '1';\nprocess.env.FRONTMCP_DEPLOYMENT_MODE = 'serverless';\n\nrequire('${mainModulePath}');\nconst { getServerlessHandlerAsync } = require('@frontmcp/sdk');\n\nlet app = null;\n\nasync function handleRequest(request) {\n if (!app) {\n app = await getServerlessHandlerAsync();\n }\n\n // Convert Cloudflare Request to Node.js format\n const url = new URL(request.url);\n const req = {\n method: request.method,\n url: url.pathname + url.search,\n headers: Object.fromEntries(request.headers),\n body: request.body,\n };\n\n return new Promise((resolve) => {\n const res = {\n statusCode: 200,\n headers: {},\n body: '',\n status(code) { this.statusCode = code; return this; },\n setHeader(key, value) { this.headers[key] = value; },\n json(data) {\n this.headers['Content-Type'] = 'application/json';\n this.body = JSON.stringify(data);\n resolve(new Response(this.body, {\n status: this.statusCode,\n headers: this.headers,\n }));\n },\n send(data) {\n this.body = data;\n resolve(new Response(this.body, {\n status: this.statusCode,\n headers: this.headers,\n }));\n },\n end() {\n resolve(new Response(this.body, {\n status: this.statusCode,\n headers: this.headers,\n }));\n },\n };\n app(req, res);\n });\n}\n\nmodule.exports = {\n async fetch(request, env, ctx) {\n return handleRequest(request);\n }\n};\n`,\n\n // #375 — fail the build when the user's @FrontMcp config references\n // Node-only storage providers that can't run on Workers. Conditional\n // (env-gated) usage still passes; only unconditional declarations throw.\n validate: (decoratorConfig) => {\n if (!decoratorConfig) return;\n const sqlite = decoratorConfig['sqlite'];\n const redis = decoratorConfig['redis'];\n const errors: string[] = [];\n if (sqlite && typeof sqlite === 'object') {\n errors.push(\n 'sqlite storage is not supported on --target cloudflare (no fs / native modules on Workers). ' +\n 'Use Cloudflare KV / Durable Objects, or gate the sqlite branch behind a build-time `define` ' +\n 'so the bundler can dead-code-eliminate it.',\n );\n }\n if (redis && typeof redis === 'object') {\n errors.push(\n 'ioredis-style `redis` storage is not supported on --target cloudflare (no Node net). ' +\n 'Use Vercel KV / Upstash Redis (HTTP), or move the redis config to a runtime branch ' +\n 'gated on `globalThis.process?.env`.',\n );\n }\n if (errors.length) {\n throw new Error(\n `[--target cloudflare] config incompatible with Cloudflare Workers:\\n - ${errors.join('\\n - ')}`,\n );\n }\n },\n\n // #374 — always write wrangler.toml from the build output. Skipping when\n // the file already exists left users with a wrangler.toml that pointed at\n // dist/index.js while the build emitted dist/cloudflare/index.js, and\n // wrangler deploy silently failed.\n alwaysWriteConfig: true,\n\n getConfig: (_cwd: string) => `name = \"frontmcp-worker\"\nmain = \"dist/cloudflare/index.js\"\ncompatibility_date = \"2024-01-01\"\n`,\n\n configFileName: 'wrangler.toml',\n};\n"]}
1
+ {"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/cloudflare.ts"],"names":[],"mappings":";;;AAGA;;;;;GAKG;AACU,QAAA,iBAAiB,GAAoB;IAChD,YAAY,EAAE,UAAU;IAExB,gBAAgB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC;;;;;WAKrC,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyDxB;IAEC,oEAAoE;IACpE,yDAAyD;IACzD,EAAE;IACF,6EAA6E;IAC7E,8EAA8E;IAC9E,4EAA4E;IAC5E,0EAA0E;IAC1E,0EAA0E;IAC1E,8EAA8E;IAC9E,6EAA6E;IAC7E,4EAA4E;IAC5E,iDAAiD;IACjD,QAAQ,EAAE,CAAC,eAAe,EAAE,IAAI,EAAE,EAAE;QAClC,MAAM,MAAM,GAAa,EAAE,CAAC;QAE5B,MAAM,eAAe,GACnB,eAAe,EAAE,CAAC,QAAQ,CAAC,KAAK,SAAS;YACzC,OAAO,eAAe,CAAC,QAAQ,CAAC,KAAK,QAAQ;YAC7C,eAAe,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;QACrC,MAAM,cAAc,GAClB,eAAe,EAAE,CAAC,OAAO,CAAC,KAAK,SAAS;YACxC,OAAO,eAAe,CAAC,OAAO,CAAC,KAAK,QAAQ;YAC5C,eAAe,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;QACpC,MAAM,UAAU,GAAG,eAAe,IAAI,CAAC,CAAC,IAAI,EAAE,gBAAgB,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACnF,MAAM,SAAS,GAAG,cAAc,IAAI,CAAC,CAAC,IAAI,EAAE,gBAAgB,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;QAEhF,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CACT,8FAA8F;gBAC5F,0FAA0F;gBAC1F,uFAAuF;gBACvF,gFAAgF,CACnF,CAAC;QACJ,CAAC;QACD,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,CAAC,IAAI,CACT,uFAAuF;gBACrF,yFAAyF;gBACzF,uFAAuF;gBACvF,gFAAgF,CACnF,CAAC;QACJ,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CACb,2EAA2E,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CACnG,CAAC;QACJ,CAAC;IACH,CAAC;IAED,yEAAyE;IACzE,0EAA0E;IAC1E,sEAAsE;IACtE,mCAAmC;IACnC,iBAAiB,EAAE,IAAI;IAEvB,sEAAsE;IACtE,uEAAuE;IACvE,0EAA0E;IAC1E,oDAAoD;IACpD,SAAS,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE;QAC9B,MAAM,QAAQ,GAAI,UAA+C,EAAE,QAAQ,IAAI,EAAE,CAAC;QAClF,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,IAAI,iBAAiB,CAAC;QAChD,MAAM,iBAAiB,GAAG,QAAQ,CAAC,iBAAiB,IAAI,YAAY,CAAC;QACrE,OAAO,WAAW,IAAI;;wBAEF,iBAAiB;CACxC,CAAC;IACA,CAAC;IAED,cAAc,EAAE,eAAe;CAChC,CAAC","sourcesContent":["import type { CloudflareDeployment } from '../../../config/frontmcp-config.types';\nimport type { AdapterTemplate } from '../types';\n\n/**\n * Cloudflare Workers adapter - edge deployment on Cloudflare.\n * Compiles to CommonJS and adapts the Express app to Cloudflare's fetch API.\n *\n * @see https://developers.cloudflare.com/workers/\n */\nexport const cloudflareAdapter: AdapterTemplate = {\n moduleFormat: 'commonjs',\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated Cloudflare Workers entry point\n// Generated by: frontmcp build --target cloudflare\nprocess.env.FRONTMCP_SERVERLESS = '1';\nprocess.env.FRONTMCP_DEPLOYMENT_MODE = 'serverless';\n\nrequire('${mainModulePath}');\nconst { getServerlessHandlerAsync } = require('@frontmcp/sdk');\n\nlet app = null;\n\nasync function handleRequest(request) {\n if (!app) {\n app = await getServerlessHandlerAsync();\n }\n\n // Convert Cloudflare Request to Node.js format\n const url = new URL(request.url);\n const req = {\n method: request.method,\n url: url.pathname + url.search,\n headers: Object.fromEntries(request.headers),\n body: request.body,\n };\n\n return new Promise((resolve) => {\n const res = {\n statusCode: 200,\n headers: {},\n body: '',\n status(code) { this.statusCode = code; return this; },\n setHeader(key, value) { this.headers[key] = value; },\n json(data) {\n this.headers['Content-Type'] = 'application/json';\n this.body = JSON.stringify(data);\n resolve(new Response(this.body, {\n status: this.statusCode,\n headers: this.headers,\n }));\n },\n send(data) {\n this.body = data;\n resolve(new Response(this.body, {\n status: this.statusCode,\n headers: this.headers,\n }));\n },\n end() {\n resolve(new Response(this.body, {\n status: this.statusCode,\n headers: this.headers,\n }));\n },\n };\n app(req, res);\n });\n}\n\nmodule.exports = {\n async fetch(request, env, ctx) {\n return handleRequest(request);\n }\n};\n`,\n\n // #375 — fail the build when the user's @FrontMcp config references\n // Node-only storage providers that can't run on Workers.\n //\n // Round 1 only inspected the runtime-evaluated config object. The reporter's\n // failure case was `sqlite: process.env.REDIS_HOST ? {…} : { sqlite: {…} }` —\n // a ternary that may evaluate to `undefined` at decorator-load time (so the\n // runtime check sees nothing) but still ships the Node-only branch in the\n // bundled worker. Round 2 also inspects `info.keysSeenInSource`, which is\n // collected by walking the @FrontMcp({...}) source expression — that captures\n // the property name regardless of whether its value is a literal, a ternary,\n // or a function call. If `sqlite`/`redis` appear at all, fail loud and tell\n // the user how to gate them at the source level.\n validate: (decoratorConfig, info) => {\n const errors: string[] = [];\n\n const sqliteIsLiteral =\n decoratorConfig?.['sqlite'] !== undefined &&\n typeof decoratorConfig['sqlite'] === 'object' &&\n decoratorConfig['sqlite'] !== null;\n const redisIsLiteral =\n decoratorConfig?.['redis'] !== undefined &&\n typeof decoratorConfig['redis'] === 'object' &&\n decoratorConfig['redis'] !== null;\n const sqliteSeen = sqliteIsLiteral || !!info?.keysSeenInSource?.includes('sqlite');\n const redisSeen = redisIsLiteral || !!info?.keysSeenInSource?.includes('redis');\n\n if (sqliteSeen) {\n errors.push(\n 'sqlite storage is not supported on --target cloudflare (no fs / native modules on Workers). ' +\n 'Even an env-gated `sqlite: process.env.X ? {...} : undefined` still ships the Node-only ' +\n 'branch in the worker bundle. Use Cloudflare KV / Durable Objects, or move the sqlite ' +\n 'config behind a build-time `define` so the bundler can dead-code-eliminate it.',\n );\n }\n if (redisSeen) {\n errors.push(\n 'ioredis-style `redis` storage is not supported on --target cloudflare (no Node net). ' +\n 'Even an env-gated `redis: process.env.X ? {...} : undefined` still ships the Node-only ' +\n 'branch in the worker bundle. Use Vercel KV / Upstash Redis (HTTP), or move the redis ' +\n 'config behind a build-time `define` so the bundler can dead-code-eliminate it.',\n );\n }\n if (errors.length) {\n throw new Error(\n `[--target cloudflare] config incompatible with Cloudflare Workers:\\n - ${errors.join('\\n - ')}`,\n );\n }\n },\n\n // #374 — always write wrangler.toml from the build output. Skipping when\n // the file already exists left users with a wrangler.toml that pointed at\n // dist/index.js while the build emitted dist/cloudflare/index.js, and\n // wrangler deploy silently failed.\n alwaysWriteConfig: true,\n\n // #374 round-2 — merge `frontmcp.config.deployments[].wrangler.{name,\n // compatibilityDate}` into the rendered TOML so values declared in the\n // user's config actually reach `wrangler deploy`. Defaults preserved when\n // the field is absent or no deployment was matched.\n getConfig: (_cwd, deployment) => {\n const wrangler = (deployment as CloudflareDeployment | undefined)?.wrangler ?? {};\n const name = wrangler.name ?? 'frontmcp-worker';\n const compatibilityDate = wrangler.compatibilityDate ?? '2024-01-01';\n return `name = \"${name}\"\nmain = \"dist/cloudflare/index.js\"\ncompatibility_date = \"${compatibilityDate}\"\n`;\n },\n\n configFileName: 'wrangler.toml',\n};\n"]}
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.lambdaAdapter = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const fs = tslib_1.__importStar(require("fs"));
6
+ const path = tslib_1.__importStar(require("path"));
4
7
  /**
5
8
  * AWS Lambda adapter - serverless deployment on AWS Lambda.
6
9
  * Compiles to ESM, bundles with rspack to CJS for maximum compatibility.
@@ -17,12 +20,16 @@ exports.lambdaAdapter = void 0;
17
20
  * @see https://github.com/codegenie/serverless-express
18
21
  */
19
22
  exports.lambdaAdapter = {
20
- moduleFormat: 'esnext',
23
+ // #368 round-3 — switched from 'esnext' to 'commonjs' for the same reason
24
+ // as the vercel adapter: strict-ESM rspack rejects TS-emitted extensionless
25
+ // relative imports, and the `byDependency.fullySpecified: false` workaround
26
+ // didn't suppress it reliably. Lambda's Node runtime accepts CJS handlers,
27
+ // and the bundled output is a CJS `handler.cjs` either way.
28
+ moduleFormat: 'commonjs',
21
29
  shouldBundle: true,
22
30
  bundleOutput: 'handler.cjs',
23
- getSetupTemplate: () => `// Serverless environment setup - MUST be imported first
31
+ getSetupTemplate: () => `// Serverless environment setup - MUST be required first
24
32
  // This sets FRONTMCP_SERVERLESS before any decorators run
25
- // Required because ESM hoists imports before other statements
26
33
  process.env.FRONTMCP_SERVERLESS = '1';
27
34
  process.env.FRONTMCP_DEPLOYMENT_MODE = 'serverless';
28
35
  `,
@@ -32,10 +39,11 @@ process.env.FRONTMCP_DEPLOYMENT_MODE = 'serverless';
32
39
  // IMPORTANT: This adapter requires @codegenie/serverless-express
33
40
  // Install it with: npm install @codegenie/serverless-express
34
41
  //
35
- import './serverless-setup.js';
36
- import '${mainModulePath}';
37
- import { getServerlessHandlerAsync } from '@frontmcp/sdk';
38
- import serverlessExpress from '@codegenie/serverless-express';
42
+ require('./serverless-setup.js');
43
+ require('${mainModulePath}');
44
+ const { getServerlessHandlerAsync } = require('@frontmcp/sdk');
45
+ const serverlessExpressMod = require('@codegenie/serverless-express');
46
+ const serverlessExpress = serverlessExpressMod.default || serverlessExpressMod;
39
47
 
40
48
  let serverlessExpressInstance = null;
41
49
 
@@ -44,13 +52,42 @@ async function setup() {
44
52
  serverlessExpressInstance = serverlessExpress({ app });
45
53
  }
46
54
 
47
- export const handler = async (event, context) => {
55
+ exports.handler = async (event, context) => {
48
56
  if (!serverlessExpressInstance) {
49
57
  await setup();
50
58
  }
51
59
  return serverlessExpressInstance(event, context);
52
60
  };
53
61
  `,
62
+ // #368 round-2 — `@codegenie/serverless-express` is a peer dep on this
63
+ // adapter (the lambda runtime ships it via Layer or the user installs it).
64
+ // The bundler externalizes it so rspack doesn't try to bundle it; this
65
+ // validate hook surfaces a clear error at build time when the package
66
+ // isn't installed locally either, instead of letting the user discover
67
+ // a runtime "Cannot find module" error in CloudWatch logs.
68
+ validate: () => {
69
+ try {
70
+ // Walk node_modules from cwd upward so monorepos / hoisted layouts work.
71
+ let dir = process.cwd();
72
+ const root = path.parse(dir).root;
73
+ while (dir !== root) {
74
+ const pkg = path.join(dir, 'node_modules', '@codegenie', 'serverless-express', 'package.json');
75
+ if (fs.existsSync(pkg))
76
+ return;
77
+ const next = path.dirname(dir);
78
+ if (next === dir)
79
+ break;
80
+ dir = next;
81
+ }
82
+ }
83
+ catch {
84
+ // Fall through to throw — accessibility errors mean we can't verify presence.
85
+ }
86
+ throw new Error(`[--target lambda] missing required peer dependency @codegenie/serverless-express.\n` +
87
+ ` Install it in your project:\n` +
88
+ ` npm install @codegenie/serverless-express\n` +
89
+ ` (or use a Lambda Layer that provides it).`);
90
+ },
54
91
  // No config file - user manages serverless.yml, SAM template, or CDK
55
92
  };
56
93
  //# sourceMappingURL=lambda.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"lambda.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/lambda.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;GAcG;AACU,QAAA,aAAa,GAAoB;IAC5C,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,IAAI;IAClB,YAAY,EAAE,aAAa;IAE3B,gBAAgB,EAAE,GAAG,EAAE,CAAC;;;;;CAKzB;IAEC,gBAAgB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC;;;;;;;UAOtC,cAAc;;;;;;;;;;;;;;;;;CAiBvB;IAEC,qEAAqE;CACtE,CAAC","sourcesContent":["import type { AdapterTemplate } from '../types';\n\n/**\n * AWS Lambda adapter - serverless deployment on AWS Lambda.\n * Compiles to ESM, bundles with rspack to CJS for maximum compatibility.\n *\n * Prerequisites:\n * npm install @codegenie/serverless-express\n *\n * The build process:\n * 1. TypeScript compiles to ESM in dist/\n * 2. serverless-setup.js is generated (sets FRONTMCP_SERVERLESS=1)\n * 3. index.js imports setup first, then main module\n * 4. rspack bundles everything into handler.cjs\n *\n * @see https://github.com/codegenie/serverless-express\n */\nexport const lambdaAdapter: AdapterTemplate = {\n moduleFormat: 'esnext',\n shouldBundle: true,\n bundleOutput: 'handler.cjs',\n\n getSetupTemplate: () => `// Serverless environment setup - MUST be imported first\n// This sets FRONTMCP_SERVERLESS before any decorators run\n// Required because ESM hoists imports before other statements\nprocess.env.FRONTMCP_SERVERLESS = '1';\nprocess.env.FRONTMCP_DEPLOYMENT_MODE = 'serverless';\n`,\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated AWS Lambda entry point\n// Generated by: frontmcp build --target lambda\n//\n// IMPORTANT: This adapter requires @codegenie/serverless-express\n// Install it with: npm install @codegenie/serverless-express\n//\nimport './serverless-setup.js';\nimport '${mainModulePath}';\nimport { getServerlessHandlerAsync } from '@frontmcp/sdk';\nimport serverlessExpress from '@codegenie/serverless-express';\n\nlet serverlessExpressInstance = null;\n\nasync function setup() {\n const app = await getServerlessHandlerAsync();\n serverlessExpressInstance = serverlessExpress({ app });\n}\n\nexport const handler = async (event, context) => {\n if (!serverlessExpressInstance) {\n await setup();\n }\n return serverlessExpressInstance(event, context);\n};\n`,\n\n // No config file - user manages serverless.yml, SAM template, or CDK\n};\n"]}
1
+ {"version":3,"file":"lambda.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/lambda.ts"],"names":[],"mappings":";;;;AAAA,+CAAyB;AACzB,mDAA6B;AAG7B;;;;;;;;;;;;;;GAcG;AACU,QAAA,aAAa,GAAoB;IAC5C,0EAA0E;IAC1E,4EAA4E;IAC5E,4EAA4E;IAC5E,2EAA2E;IAC3E,4DAA4D;IAC5D,YAAY,EAAE,UAAU;IACxB,YAAY,EAAE,IAAI;IAClB,YAAY,EAAE,aAAa;IAE3B,gBAAgB,EAAE,GAAG,EAAE,CAAC;;;;CAIzB;IAEC,gBAAgB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC;;;;;;;WAOrC,cAAc;;;;;;;;;;;;;;;;;;CAkBxB;IAEC,uEAAuE;IACvE,2EAA2E;IAC3E,uEAAuE;IACvE,sEAAsE;IACtE,uEAAuE;IACvE,2DAA2D;IAC3D,QAAQ,EAAE,GAAG,EAAE;QACb,IAAI,CAAC;YACH,yEAAyE;YACzE,IAAI,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;YACxB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YAClC,OAAO,GAAG,KAAK,IAAI,EAAE,CAAC;gBACpB,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,EAAE,YAAY,EAAE,oBAAoB,EAAE,cAAc,CAAC,CAAC;gBAC/F,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;oBAAE,OAAO;gBAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBAC/B,IAAI,IAAI,KAAK,GAAG;oBAAE,MAAM;gBACxB,GAAG,GAAG,IAAI,CAAC;YACb,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,8EAA8E;QAChF,CAAC;QACD,MAAM,IAAI,KAAK,CACb,qFAAqF;YACnF,iCAAiC;YACjC,iDAAiD;YACjD,6CAA6C,CAChD,CAAC;IACJ,CAAC;IAED,qEAAqE;CACtE,CAAC","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport type { AdapterTemplate } from '../types';\n\n/**\n * AWS Lambda adapter - serverless deployment on AWS Lambda.\n * Compiles to ESM, bundles with rspack to CJS for maximum compatibility.\n *\n * Prerequisites:\n * npm install @codegenie/serverless-express\n *\n * The build process:\n * 1. TypeScript compiles to ESM in dist/\n * 2. serverless-setup.js is generated (sets FRONTMCP_SERVERLESS=1)\n * 3. index.js imports setup first, then main module\n * 4. rspack bundles everything into handler.cjs\n *\n * @see https://github.com/codegenie/serverless-express\n */\nexport const lambdaAdapter: AdapterTemplate = {\n // #368 round-3 — switched from 'esnext' to 'commonjs' for the same reason\n // as the vercel adapter: strict-ESM rspack rejects TS-emitted extensionless\n // relative imports, and the `byDependency.fullySpecified: false` workaround\n // didn't suppress it reliably. Lambda's Node runtime accepts CJS handlers,\n // and the bundled output is a CJS `handler.cjs` either way.\n moduleFormat: 'commonjs',\n shouldBundle: true,\n bundleOutput: 'handler.cjs',\n\n getSetupTemplate: () => `// Serverless environment setup - MUST be required first\n// This sets FRONTMCP_SERVERLESS before any decorators run\nprocess.env.FRONTMCP_SERVERLESS = '1';\nprocess.env.FRONTMCP_DEPLOYMENT_MODE = 'serverless';\n`,\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated AWS Lambda entry point\n// Generated by: frontmcp build --target lambda\n//\n// IMPORTANT: This adapter requires @codegenie/serverless-express\n// Install it with: npm install @codegenie/serverless-express\n//\nrequire('./serverless-setup.js');\nrequire('${mainModulePath}');\nconst { getServerlessHandlerAsync } = require('@frontmcp/sdk');\nconst serverlessExpressMod = require('@codegenie/serverless-express');\nconst serverlessExpress = serverlessExpressMod.default || serverlessExpressMod;\n\nlet serverlessExpressInstance = null;\n\nasync function setup() {\n const app = await getServerlessHandlerAsync();\n serverlessExpressInstance = serverlessExpress({ app });\n}\n\nexports.handler = async (event, context) => {\n if (!serverlessExpressInstance) {\n await setup();\n }\n return serverlessExpressInstance(event, context);\n};\n`,\n\n // #368 round-2 — `@codegenie/serverless-express` is a peer dep on this\n // adapter (the lambda runtime ships it via Layer or the user installs it).\n // The bundler externalizes it so rspack doesn't try to bundle it; this\n // validate hook surfaces a clear error at build time when the package\n // isn't installed locally either, instead of letting the user discover\n // a runtime \"Cannot find module\" error in CloudWatch logs.\n validate: () => {\n try {\n // Walk node_modules from cwd upward so monorepos / hoisted layouts work.\n let dir = process.cwd();\n const root = path.parse(dir).root;\n while (dir !== root) {\n const pkg = path.join(dir, 'node_modules', '@codegenie', 'serverless-express', 'package.json');\n if (fs.existsSync(pkg)) return;\n const next = path.dirname(dir);\n if (next === dir) break;\n dir = next;\n }\n } catch {\n // Fall through to throw — accessibility errors mean we can't verify presence.\n }\n throw new Error(\n `[--target lambda] missing required peer dependency @codegenie/serverless-express.\\n` +\n ` Install it in your project:\\n` +\n ` npm install @codegenie/serverless-express\\n` +\n ` (or use a Lambda Layer that provides it).`,\n );\n },\n\n // No config file - user manages serverless.yml, SAM template, or CDK\n};\n"]}
@@ -48,30 +48,37 @@ function detectPackageManager(cwd) {
48
48
  * @see https://vercel.com/docs/build-output-api/v3
49
49
  */
50
50
  exports.vercelAdapter = {
51
- moduleFormat: 'esnext',
51
+ // #368 round-3 — was 'esnext'. The ESM entry forced rspack to treat the
52
+ // bundle as strict ESM, which requires fully-specified `./foo.js` imports
53
+ // in TS-emitted code. The `byDependency.fullySpecified: false` workaround
54
+ // didn't reliably suppress this. Switching to CJS mirrors the cloudflare
55
+ // adapter (which has been stable) and eliminates the entire class of
56
+ // extensionless-import errors. The bundled CJS handler still works on
57
+ // Vercel's Node runtime since rspack outputs CommonJS.
58
+ moduleFormat: 'commonjs',
52
59
  shouldBundle: true,
53
60
  bundleOutput: 'handler.cjs',
54
- getSetupTemplate: () => `// Serverless environment setup - MUST be imported first
61
+ getSetupTemplate: () => `// Serverless environment setup - MUST be required first
55
62
  // This sets FRONTMCP_SERVERLESS before any decorators run
56
- // Required because ESM hoists imports before other statements
57
63
  process.env.FRONTMCP_SERVERLESS = '1';
58
64
  process.env.FRONTMCP_DEPLOYMENT_MODE = 'serverless';
59
65
  `,
60
66
  getEntryTemplate: (mainModulePath) => `// Auto-generated Vercel entry point
61
67
  // Generated by: frontmcp build --target vercel
62
- import './serverless-setup.js';
63
- import '${mainModulePath}';
64
- import { getServerlessHandlerAsync } from '@frontmcp/sdk';
68
+ require('./serverless-setup.js');
69
+ require('${mainModulePath}');
70
+ const { getServerlessHandlerAsync } = require('@frontmcp/sdk');
65
71
 
66
72
  let handlerPromise = null;
67
73
 
68
- export default async function handler(req, res) {
74
+ module.exports = async function handler(req, res) {
69
75
  if (!handlerPromise) {
70
76
  handlerPromise = getServerlessHandlerAsync();
71
77
  }
72
78
  const app = await handlerPromise;
73
79
  return app(req, res);
74
- }
80
+ };
81
+ module.exports.default = module.exports;
75
82
  `,
76
83
  // Detect package manager and generate appropriate vercel.json
77
84
  getConfig: (cwd) => {
@@ -120,8 +127,15 @@ export default async function handler(req, res) {
120
127
  }
121
128
  // Install runtime dependencies that can't be statically bundled (dynamic requires)
122
129
  // These are packages loaded via require() inside functions that rspack can't analyze
123
- // We install them fresh to ensure correct platform binaries (linux-x64 for Vercel)
124
- const runtimeDeps = ['@vercel/kv', 'esbuild', '@swc/core'];
130
+ // We install them fresh to ensure correct platform binaries (linux-x64 for Vercel).
131
+ //
132
+ // #368 round-3 — `openai` and `@anthropic-ai/sdk` are peer deps consumed
133
+ // by `@Agent({ adapter: 'openai' | 'anthropic' })` via lazy
134
+ // `await import()` inside the SDK. They're externalized at bundle time
135
+ // so rspack doesn't fail when they're not installed; only ship them on
136
+ // Vercel when the user actually has them in their package.json (i.e.,
137
+ // they wired up an agent). If absent, the lazy require never fires.
138
+ const runtimeDeps = ['@vercel/kv', 'esbuild', '@swc/core', 'openai', '@anthropic-ai/sdk'];
125
139
  // Read package.json to get the exact versions
126
140
  const pkgJsonPath = path.join(cwd, 'package.json');
127
141
  const pkgJson = JSON.parse(await (0, utils_1.readFile)(pkgJsonPath, 'utf-8'));
@@ -1 +1 @@
1
- {"version":3,"file":"vercel.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/vercel.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path';\nimport { existsSync } from 'fs';\nimport { execSync } from 'child_process';\nimport { mkdir, readdir, stat, cp, copyFile, readFile, writeFile } from '@frontmcp/utils';\nimport type { AdapterTemplate } from '../types';\n\ntype PackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun';\n\ninterface PackageManagerConfig {\n install: string;\n run: string;\n}\n\nconst PACKAGE_MANAGERS: Record<PackageManager, PackageManagerConfig> = {\n bun: { install: 'bun install', run: 'bun run build' },\n pnpm: { install: 'pnpm install', run: 'pnpm run build' },\n yarn: { install: 'yarn install', run: 'yarn build' },\n npm: { install: 'npm install', run: 'npm run build' },\n};\n\nconst LOCKFILE_TO_PM: Record<string, PackageManager> = {\n 'bun.lockb': 'bun',\n 'pnpm-lock.yaml': 'pnpm',\n 'yarn.lock': 'yarn',\n 'package-lock.json': 'npm',\n};\n\n/**\n * Detect package manager based on lockfile presence.\n * Priority: bun > pnpm > yarn > npm (fastest to slowest install times)\n */\nfunction detectPackageManager(cwd: string): PackageManager {\n for (const [lockfile, pm] of Object.entries(LOCKFILE_TO_PM)) {\n if (existsSync(path.join(cwd, lockfile))) {\n return pm;\n }\n }\n return 'npm'; // Default fallback\n}\n\n/**\n * Vercel adapter - serverless deployment on Vercel.\n * Compiles to ESM, bundles with rspack to CJS for maximum compatibility.\n *\n * Uses Vercel Build Output API for deployment:\n * - Creates .vercel/output/config.json with routing\n * - Creates .vercel/output/functions/index.func/ with handler\n *\n * The build process:\n * 1. TypeScript compiles to ESM in dist/\n * 2. serverless-setup.js is generated (sets FRONTMCP_SERVERLESS=1)\n * 3. index.js imports setup first, then main module\n * 4. rspack bundles everything into handler.cjs\n * 5. Build Output API structure is created in .vercel/output/\n *\n * @see https://vercel.com/docs/build-output-api/v3\n */\nexport const vercelAdapter: AdapterTemplate = {\n moduleFormat: 'esnext',\n shouldBundle: true,\n bundleOutput: 'handler.cjs',\n\n getSetupTemplate: () => `// Serverless environment setup - MUST be imported first\n// This sets FRONTMCP_SERVERLESS before any decorators run\n// Required because ESM hoists imports before other statements\nprocess.env.FRONTMCP_SERVERLESS = '1';\nprocess.env.FRONTMCP_DEPLOYMENT_MODE = 'serverless';\n`,\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated Vercel entry point\n// Generated by: frontmcp build --target vercel\nimport './serverless-setup.js';\nimport '${mainModulePath}';\nimport { getServerlessHandlerAsync } from '@frontmcp/sdk';\n\nlet handlerPromise = null;\n\nexport default async function handler(req, res) {\n if (!handlerPromise) {\n handlerPromise = getServerlessHandlerAsync();\n }\n const app = await handlerPromise;\n return app(req, res);\n}\n`,\n\n // Detect package manager and generate appropriate vercel.json\n getConfig: (cwd: string) => {\n const pm = detectPackageManager(cwd);\n const config = PACKAGE_MANAGERS[pm];\n return {\n version: 2,\n buildCommand: config.run,\n installCommand: config.install,\n };\n },\n\n configFileName: 'vercel.json',\n\n /**\n * Create Vercel Build Output API structure after bundling.\n * This allows Vercel to deploy the function without needing an /api folder.\n *\n * Structure created:\n * .vercel/output/\n * ├── config.json (routes all requests to index function)\n * └── functions/\n * └── index.func/\n * ├── .vc-config.json (Node.js 22 runtime config)\n * ├── handler.cjs (bundled handler + chunks)\n * └── node_modules/ (runtime dependencies that can't be bundled)\n */\n postBundle: async (outDir: string, cwd: string, bundleOutput: string) => {\n const outputDir = path.join(cwd, '.vercel', 'output');\n const funcDir = path.join(outputDir, 'functions', 'index.func');\n\n // Create directories\n await mkdir(funcDir, { recursive: true });\n\n // Copy all files from dist to the function directory\n // This includes handler.cjs and any chunk files (*.handler.cjs)\n const distFiles = await readdir(outDir);\n for (const file of distFiles) {\n const srcPath = path.join(outDir, file);\n const destPath = path.join(funcDir, file);\n const fileStat = await stat(srcPath);\n\n if (fileStat.isDirectory()) {\n // Recursively copy directories\n await cp(srcPath, destPath, { recursive: true });\n } else {\n // Copy files\n await copyFile(srcPath, destPath);\n }\n }\n\n // Install runtime dependencies that can't be statically bundled (dynamic requires)\n // These are packages loaded via require() inside functions that rspack can't analyze\n // We install them fresh to ensure correct platform binaries (linux-x64 for Vercel)\n const runtimeDeps = ['@vercel/kv', 'esbuild', '@swc/core'];\n\n // Read package.json to get the exact versions\n const pkgJsonPath = path.join(cwd, 'package.json');\n const pkgJson = JSON.parse(await readFile(pkgJsonPath, 'utf-8'));\n const allDeps = { ...pkgJson.dependencies, ...pkgJson.devDependencies };\n\n // Build list of deps with versions\n const depsToInstall: string[] = [];\n for (const dep of runtimeDeps) {\n const version = allDeps[dep];\n if (version) {\n depsToInstall.push(`${dep}@${version}`);\n }\n }\n\n if (depsToInstall.length > 0) {\n // Create package.json in function directory\n const funcPkgJson = { name: 'index.func', private: true, dependencies: {} };\n await writeFile(path.join(funcDir, 'package.json'), JSON.stringify(funcPkgJson, null, 2));\n\n // Install dependencies using npm (works on all platforms)\n try {\n execSync(`npm install ${depsToInstall.join(' ')} --omit=dev`, {\n cwd: funcDir,\n stdio: 'pipe',\n });\n } catch {\n // Silently continue if install fails - the dep might not be needed\n }\n }\n\n // Create function config (.vc-config.json)\n const vcConfig = {\n runtime: 'nodejs24.x',\n handler: bundleOutput,\n launcherType: 'Nodejs',\n };\n await writeFile(\n path.join(funcDir, '.vc-config.json'),\n JSON.stringify(vcConfig, null, 2),\n );\n\n // Create output config (config.json) with routing\n const outputConfig = {\n version: 3,\n routes: [{ src: '/(.*)', dest: '/index' }],\n };\n await writeFile(\n path.join(outputDir, 'config.json'),\n JSON.stringify(outputConfig, null, 2),\n );\n },\n};\n"]}
1
+ {"version":3,"file":"vercel.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/vercel.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path';\nimport { existsSync } from 'fs';\nimport { execSync } from 'child_process';\nimport { mkdir, readdir, stat, cp, copyFile, readFile, writeFile } from '@frontmcp/utils';\nimport type { AdapterTemplate } from '../types';\n\ntype PackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun';\n\ninterface PackageManagerConfig {\n install: string;\n run: string;\n}\n\nconst PACKAGE_MANAGERS: Record<PackageManager, PackageManagerConfig> = {\n bun: { install: 'bun install', run: 'bun run build' },\n pnpm: { install: 'pnpm install', run: 'pnpm run build' },\n yarn: { install: 'yarn install', run: 'yarn build' },\n npm: { install: 'npm install', run: 'npm run build' },\n};\n\nconst LOCKFILE_TO_PM: Record<string, PackageManager> = {\n 'bun.lockb': 'bun',\n 'pnpm-lock.yaml': 'pnpm',\n 'yarn.lock': 'yarn',\n 'package-lock.json': 'npm',\n};\n\n/**\n * Detect package manager based on lockfile presence.\n * Priority: bun > pnpm > yarn > npm (fastest to slowest install times)\n */\nfunction detectPackageManager(cwd: string): PackageManager {\n for (const [lockfile, pm] of Object.entries(LOCKFILE_TO_PM)) {\n if (existsSync(path.join(cwd, lockfile))) {\n return pm;\n }\n }\n return 'npm'; // Default fallback\n}\n\n/**\n * Vercel adapter - serverless deployment on Vercel.\n * Compiles to ESM, bundles with rspack to CJS for maximum compatibility.\n *\n * Uses Vercel Build Output API for deployment:\n * - Creates .vercel/output/config.json with routing\n * - Creates .vercel/output/functions/index.func/ with handler\n *\n * The build process:\n * 1. TypeScript compiles to ESM in dist/\n * 2. serverless-setup.js is generated (sets FRONTMCP_SERVERLESS=1)\n * 3. index.js imports setup first, then main module\n * 4. rspack bundles everything into handler.cjs\n * 5. Build Output API structure is created in .vercel/output/\n *\n * @see https://vercel.com/docs/build-output-api/v3\n */\nexport const vercelAdapter: AdapterTemplate = {\n // #368 round-3 — was 'esnext'. The ESM entry forced rspack to treat the\n // bundle as strict ESM, which requires fully-specified `./foo.js` imports\n // in TS-emitted code. The `byDependency.fullySpecified: false` workaround\n // didn't reliably suppress this. Switching to CJS mirrors the cloudflare\n // adapter (which has been stable) and eliminates the entire class of\n // extensionless-import errors. The bundled CJS handler still works on\n // Vercel's Node runtime since rspack outputs CommonJS.\n moduleFormat: 'commonjs',\n shouldBundle: true,\n bundleOutput: 'handler.cjs',\n\n getSetupTemplate: () => `// Serverless environment setup - MUST be required first\n// This sets FRONTMCP_SERVERLESS before any decorators run\nprocess.env.FRONTMCP_SERVERLESS = '1';\nprocess.env.FRONTMCP_DEPLOYMENT_MODE = 'serverless';\n`,\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated Vercel entry point\n// Generated by: frontmcp build --target vercel\nrequire('./serverless-setup.js');\nrequire('${mainModulePath}');\nconst { getServerlessHandlerAsync } = require('@frontmcp/sdk');\n\nlet handlerPromise = null;\n\nmodule.exports = async function handler(req, res) {\n if (!handlerPromise) {\n handlerPromise = getServerlessHandlerAsync();\n }\n const app = await handlerPromise;\n return app(req, res);\n};\nmodule.exports.default = module.exports;\n`,\n\n // Detect package manager and generate appropriate vercel.json\n getConfig: (cwd: string) => {\n const pm = detectPackageManager(cwd);\n const config = PACKAGE_MANAGERS[pm];\n return {\n version: 2,\n buildCommand: config.run,\n installCommand: config.install,\n };\n },\n\n configFileName: 'vercel.json',\n\n /**\n * Create Vercel Build Output API structure after bundling.\n * This allows Vercel to deploy the function without needing an /api folder.\n *\n * Structure created:\n * .vercel/output/\n * ├── config.json (routes all requests to index function)\n * └── functions/\n * └── index.func/\n * ├── .vc-config.json (Node.js 22 runtime config)\n * ├── handler.cjs (bundled handler + chunks)\n * └── node_modules/ (runtime dependencies that can't be bundled)\n */\n postBundle: async (outDir: string, cwd: string, bundleOutput: string) => {\n const outputDir = path.join(cwd, '.vercel', 'output');\n const funcDir = path.join(outputDir, 'functions', 'index.func');\n\n // Create directories\n await mkdir(funcDir, { recursive: true });\n\n // Copy all files from dist to the function directory\n // This includes handler.cjs and any chunk files (*.handler.cjs)\n const distFiles = await readdir(outDir);\n for (const file of distFiles) {\n const srcPath = path.join(outDir, file);\n const destPath = path.join(funcDir, file);\n const fileStat = await stat(srcPath);\n\n if (fileStat.isDirectory()) {\n // Recursively copy directories\n await cp(srcPath, destPath, { recursive: true });\n } else {\n // Copy files\n await copyFile(srcPath, destPath);\n }\n }\n\n // Install runtime dependencies that can't be statically bundled (dynamic requires)\n // These are packages loaded via require() inside functions that rspack can't analyze\n // We install them fresh to ensure correct platform binaries (linux-x64 for Vercel).\n //\n // #368 round-3 — `openai` and `@anthropic-ai/sdk` are peer deps consumed\n // by `@Agent({ adapter: 'openai' | 'anthropic' })` via lazy\n // `await import()` inside the SDK. They're externalized at bundle time\n // so rspack doesn't fail when they're not installed; only ship them on\n // Vercel when the user actually has them in their package.json (i.e.,\n // they wired up an agent). If absent, the lazy require never fires.\n const runtimeDeps = ['@vercel/kv', 'esbuild', '@swc/core', 'openai', '@anthropic-ai/sdk'];\n\n // Read package.json to get the exact versions\n const pkgJsonPath = path.join(cwd, 'package.json');\n const pkgJson = JSON.parse(await readFile(pkgJsonPath, 'utf-8'));\n const allDeps = { ...pkgJson.dependencies, ...pkgJson.devDependencies };\n\n // Build list of deps with versions\n const depsToInstall: string[] = [];\n for (const dep of runtimeDeps) {\n const version = allDeps[dep];\n if (version) {\n depsToInstall.push(`${dep}@${version}`);\n }\n }\n\n if (depsToInstall.length > 0) {\n // Create package.json in function directory\n const funcPkgJson = { name: 'index.func', private: true, dependencies: {} };\n await writeFile(path.join(funcDir, 'package.json'), JSON.stringify(funcPkgJson, null, 2));\n\n // Install dependencies using npm (works on all platforms)\n try {\n execSync(`npm install ${depsToInstall.join(' ')} --omit=dev`, {\n cwd: funcDir,\n stdio: 'pipe',\n });\n } catch {\n // Silently continue if install fails - the dep might not be needed\n }\n }\n\n // Create function config (.vc-config.json)\n const vcConfig = {\n runtime: 'nodejs24.x',\n handler: bundleOutput,\n launcherType: 'Nodejs',\n };\n await writeFile(\n path.join(funcDir, '.vc-config.json'),\n JSON.stringify(vcConfig, null, 2),\n );\n\n // Create output config (config.json) with routing\n const outputConfig = {\n version: 3,\n routes: [{ src: '/(.*)', dest: '/index' }],\n };\n await writeFile(\n path.join(outputDir, 'config.json'),\n JSON.stringify(outputConfig, null, 2),\n );\n },\n};\n"]}
@@ -34,11 +34,43 @@ async function bundleForServerless(entryPath, outDir, outputFilename) {
34
34
  'react-dom': 'react-dom',
35
35
  'react-dom/server': 'react-dom/server',
36
36
  'react/jsx-runtime': 'react/jsx-runtime',
37
+ // #368 round-2 — Lambda's entry imports `@codegenie/serverless-express`
38
+ // which is intentionally a peer dep (the user installs the version
39
+ // they want). Mark it external so rspack doesn't fail with
40
+ // "Module not found" trying to bundle it. The lambda adapter's own
41
+ // validate hook surfaces a clear "npm install @codegenie/serverless-express"
42
+ // error when it's actually missing from node_modules at build time.
43
+ '@codegenie/serverless-express': '@codegenie/serverless-express',
44
+ // #368 round-3 — `@frontmcp/sdk/esm` contains lazy `await import('openai')`
45
+ // and `await import('@anthropic-ai/sdk')` calls inside agent adapters.
46
+ // These are intentionally optional peers (only resolved when the user
47
+ // actually instantiates an OpenAI/Anthropic agent), but rspack treats
48
+ // them as hard imports during static analysis and the vercel/lambda
49
+ // build fails because neither is installed. Externalizing tells rspack
50
+ // to leave the `require()` in place — the dynamic-import branch only
51
+ // executes if the user wires up the corresponding agent.
52
+ openai: 'openai',
53
+ '@anthropic-ai/sdk': '@anthropic-ai/sdk',
37
54
  },
38
55
  resolve: {
39
56
  extensions: ['.js', '.mjs', '.cjs', '.json'],
40
- // Allow imports without file extensions (TypeScript compiles without .js but ESM requires them)
57
+ // Allow imports without file extensions (TypeScript compiles without .js
58
+ // but strict ESM requires them).
59
+ //
60
+ // #368 round-2 — top-level `fullySpecified: false` alone wasn't enough.
61
+ // When the entry's sibling `package.json` declares `{"type":"module"}`
62
+ // (vercel/lambda adapters do this so Node treats `index.js` as ESM),
63
+ // rspack classifies the relative import edges as `esm` dependencies
64
+ // and applies its strict-ESM resolver, which ignores the top-level
65
+ // setting. `byDependency` overrides per dependency type so
66
+ // `import { CalcApp } from './calc.app'` resolves whether the import
67
+ // is parsed as CJS, ESM, or commonjs-require.
41
68
  fullySpecified: false,
69
+ byDependency: {
70
+ esm: { fullySpecified: false },
71
+ commonjs: { fullySpecified: false },
72
+ 'commonjs-require': { fullySpecified: false },
73
+ },
42
74
  },
43
75
  module: {
44
76
  rules: [],
@@ -1 +1 @@
1
- {"version":3,"file":"bundler.js","sourceRoot":"","sources":["../../../../src/commands/build/bundler.ts"],"names":[],"mappings":"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","sourcesContent":["import { rspack } from '@rspack/core';\nimport { c } from '../../core/colors';\n\n/**\n * Bundle the serverless entry point into a single CJS file using rspack.\n * This resolves ESM/CJS compatibility issues and dynamic import problems.\n *\n * @param entryPath - Absolute path to the entry file (e.g., dist/index.js)\n * @param outDir - Output directory for the bundled file\n * @param outputFilename - Name of the output bundle (e.g., 'handler.cjs')\n */\nexport async function bundleForServerless(\n entryPath: string,\n outDir: string,\n outputFilename: string,\n): Promise<void> {\n const compiler = rspack({\n mode: 'production',\n target: 'node',\n entry: entryPath,\n output: {\n path: outDir,\n filename: outputFilename,\n library: { type: 'commonjs2' },\n clean: false,\n },\n // Use node externals preset for built-in modules\n externalsPresets: { node: true },\n // Exclude problematic optional dependencies (native binaries that can't be bundled)\n externals: {\n '@swc/core': '@swc/core',\n fsevents: 'fsevents',\n esbuild: 'esbuild',\n // React is optional - only needed for MDX/JSX rendering\n react: 'react',\n 'react-dom': 'react-dom',\n 'react-dom/server': 'react-dom/server',\n 'react/jsx-runtime': 'react/jsx-runtime',\n },\n resolve: {\n extensions: ['.js', '.mjs', '.cjs', '.json'],\n // Allow imports without file extensions (TypeScript compiles without .js but ESM requires them)\n fullySpecified: false,\n },\n module: {\n rules: [],\n parser: {\n javascript: {\n // Handle dynamic requires like require('@vercel/kv') inside functions\n // by wrapping them instead of externalizing them\n dynamicImportMode: 'eager',\n exprContextCritical: false,\n unknownContextCritical: false,\n },\n },\n },\n // Don't minimize to preserve readability for debugging\n optimization: {\n minimize: false,\n },\n // Suppress known third-party library warnings that don't affect runtime\n ignoreWarnings: [\n // Express view engine dynamic require - expected behavior, harmless at runtime\n /Critical dependency: the request of a dependency is an expression/,\n // Handlebars require.extensions - deprecated Node.js API but works at runtime\n /require\\.extensions is not supported by Rspack/,\n ],\n // Suppress verbose output\n stats: 'errors-warnings',\n });\n\n return new Promise((resolve, reject) => {\n compiler.run((err, stats) => {\n if (err) {\n return reject(err);\n }\n if (stats?.hasErrors()) {\n const info = stats.toJson();\n const errorMessages = info.errors?.map((e) => e.message).join('\\n') || 'Unknown error';\n return reject(new Error(`Bundle failed:\\n${errorMessages}`));\n }\n if (stats?.hasWarnings()) {\n const info = stats.toJson();\n info.warnings?.forEach((w) => {\n console.log(c('yellow', ` Warning: ${w.message}`));\n });\n }\n compiler.close((closeErr) => {\n if (closeErr) {\n console.log(c('yellow', ` Warning closing compiler: ${closeErr.message}`));\n }\n resolve();\n });\n });\n });\n}\n"]}
1
+ {"version":3,"file":"bundler.js","sourceRoot":"","sources":["../../../../src/commands/build/bundler.ts"],"names":[],"mappings":"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","sourcesContent":["import { rspack } from '@rspack/core';\nimport { c } from '../../core/colors';\n\n/**\n * Bundle the serverless entry point into a single CJS file using rspack.\n * This resolves ESM/CJS compatibility issues and dynamic import problems.\n *\n * @param entryPath - Absolute path to the entry file (e.g., dist/index.js)\n * @param outDir - Output directory for the bundled file\n * @param outputFilename - Name of the output bundle (e.g., 'handler.cjs')\n */\nexport async function bundleForServerless(\n entryPath: string,\n outDir: string,\n outputFilename: string,\n): Promise<void> {\n const compiler = rspack({\n mode: 'production',\n target: 'node',\n entry: entryPath,\n output: {\n path: outDir,\n filename: outputFilename,\n library: { type: 'commonjs2' },\n clean: false,\n },\n // Use node externals preset for built-in modules\n externalsPresets: { node: true },\n // Exclude problematic optional dependencies (native binaries that can't be bundled)\n externals: {\n '@swc/core': '@swc/core',\n fsevents: 'fsevents',\n esbuild: 'esbuild',\n // React is optional - only needed for MDX/JSX rendering\n react: 'react',\n 'react-dom': 'react-dom',\n 'react-dom/server': 'react-dom/server',\n 'react/jsx-runtime': 'react/jsx-runtime',\n // #368 round-2 — Lambda's entry imports `@codegenie/serverless-express`\n // which is intentionally a peer dep (the user installs the version\n // they want). Mark it external so rspack doesn't fail with\n // \"Module not found\" trying to bundle it. The lambda adapter's own\n // validate hook surfaces a clear \"npm install @codegenie/serverless-express\"\n // error when it's actually missing from node_modules at build time.\n '@codegenie/serverless-express': '@codegenie/serverless-express',\n // #368 round-3 — `@frontmcp/sdk/esm` contains lazy `await import('openai')`\n // and `await import('@anthropic-ai/sdk')` calls inside agent adapters.\n // These are intentionally optional peers (only resolved when the user\n // actually instantiates an OpenAI/Anthropic agent), but rspack treats\n // them as hard imports during static analysis and the vercel/lambda\n // build fails because neither is installed. Externalizing tells rspack\n // to leave the `require()` in place — the dynamic-import branch only\n // executes if the user wires up the corresponding agent.\n openai: 'openai',\n '@anthropic-ai/sdk': '@anthropic-ai/sdk',\n },\n resolve: {\n extensions: ['.js', '.mjs', '.cjs', '.json'],\n // Allow imports without file extensions (TypeScript compiles without .js\n // but strict ESM requires them).\n //\n // #368 round-2 — top-level `fullySpecified: false` alone wasn't enough.\n // When the entry's sibling `package.json` declares `{\"type\":\"module\"}`\n // (vercel/lambda adapters do this so Node treats `index.js` as ESM),\n // rspack classifies the relative import edges as `esm` dependencies\n // and applies its strict-ESM resolver, which ignores the top-level\n // setting. `byDependency` overrides per dependency type so\n // `import { CalcApp } from './calc.app'` resolves whether the import\n // is parsed as CJS, ESM, or commonjs-require.\n fullySpecified: false,\n byDependency: {\n esm: { fullySpecified: false },\n commonjs: { fullySpecified: false },\n 'commonjs-require': { fullySpecified: false },\n },\n },\n module: {\n rules: [],\n parser: {\n javascript: {\n // Handle dynamic requires like require('@vercel/kv') inside functions\n // by wrapping them instead of externalizing them\n dynamicImportMode: 'eager',\n exprContextCritical: false,\n unknownContextCritical: false,\n },\n },\n },\n // Don't minimize to preserve readability for debugging\n optimization: {\n minimize: false,\n },\n // Suppress known third-party library warnings that don't affect runtime\n ignoreWarnings: [\n // Express view engine dynamic require - expected behavior, harmless at runtime\n /Critical dependency: the request of a dependency is an expression/,\n // Handlebars require.extensions - deprecated Node.js API but works at runtime\n /require\\.extensions is not supported by Rspack/,\n ],\n // Suppress verbose output\n stats: 'errors-warnings',\n });\n\n return new Promise((resolve, reject) => {\n compiler.run((err, stats) => {\n if (err) {\n return reject(err);\n }\n if (stats?.hasErrors()) {\n const info = stats.toJson();\n const errorMessages = info.errors?.map((e) => e.message).join('\\n') || 'Unknown error';\n return reject(new Error(`Bundle failed:\\n${errorMessages}`));\n }\n if (stats?.hasWarnings()) {\n const info = stats.toJson();\n info.warnings?.forEach((w) => {\n console.log(c('yellow', ` Warning: ${w.message}`));\n });\n }\n compiler.close((closeErr) => {\n if (closeErr) {\n console.log(c('yellow', ` Warning closing compiler: ${closeErr.message}`));\n }\n resolve();\n });\n });\n });\n}\n"]}
@@ -493,6 +493,25 @@ function generatePromptCommands(prompts) {
493
493
  }));
494
494
  return `${JSON.stringify(p.name)}: ${JSON.stringify(args)}`;
495
495
  }).join(',\n ');
496
+ // #382 round-2 — collect the union of all known prompt-arg kebab names so we
497
+ // can register them as `.option(--<name> <value>)` on `prompt get`. Without
498
+ // these registrations, Commander treats the value tokens (e.g. `add` after
499
+ // `--op`) as extra positional args and rejects them with
500
+ // `too many arguments for 'get'. Expected 1 argument but got 7.` —
501
+ // breaking every prompt that takes arguments. Per-prompt validation still
502
+ // happens inside the action handler against `promptArgs[name]`, so a user
503
+ // that types `--op` for a prompt that doesn't declare it gets a clear
504
+ // "unknown option(s) for prompt" error rather than silent acceptance.
505
+ const allKebabNames = new Set();
506
+ for (const p of prompts) {
507
+ for (const a of p.arguments || []) {
508
+ allKebabNames.add((0, schema_to_commander_1.camelToKebab)(a.name));
509
+ }
510
+ }
511
+ const promptGetOptionLines = Array.from(allKebabNames)
512
+ .sort()
513
+ .map((kebab) => ` .option(${JSON.stringify(`--${kebab} <value>`)}, '')`)
514
+ .join('\n');
496
515
  const subcommands = prompts.map((prompt) => {
497
516
  const cmdName = (0, schema_to_commander_1.camelToKebab)(prompt.name).replace(/_/g, '-');
498
517
  const argOptions = (prompt.arguments || [])
@@ -565,6 +584,7 @@ var promptArgs = {
565
584
  var _getCmd = promptCmd
566
585
  .command('get <name>')
567
586
  .description('Render a prompt by name')
587
+ ${promptGetOptionLines}
568
588
  .allowUnknownOption(true)
569
589
  .action(async function(name) {
570
590
  try {
@@ -574,41 +594,51 @@ var _getCmd = promptCmd
574
594
  process.exitCode = 1;
575
595
  return;
576
596
  }
597
+ // Commander parsed registered options into rawOpts (camelCased). For
598
+ // unregistered prompts (a flag a different prompt declares but this one
599
+ // doesn't), fall back to scanning the raw token stream — that way an
600
+ // out-of-spec --foo for "this" prompt still surfaces as an explicit
601
+ // "unknown option(s)" error rather than being silently dropped.
602
+ var rawOpts = this.opts();
577
603
  var rawTokens = this.args.slice(1);
578
604
  var args = {};
579
605
  var unknown = [];
580
606
  var byKebab = {};
581
- for (var s = 0; s < spec.length; s++) byKebab[spec[s].kebab] = spec[s];
607
+ var byCamel = {};
608
+ for (var s = 0; s < spec.length; s++) {
609
+ byKebab[spec[s].kebab] = spec[s];
610
+ byCamel[spec[s].camel] = spec[s];
611
+ }
612
+ // 1. Pull values from Commander-parsed options (registered names).
613
+ for (var camelKey in rawOpts) {
614
+ if (Object.prototype.hasOwnProperty.call(rawOpts, camelKey)) {
615
+ var matchByCamel = byCamel[camelKey];
616
+ if (matchByCamel) {
617
+ args[matchByCamel.name] = rawOpts[camelKey];
618
+ }
619
+ }
620
+ }
621
+ // 2. Scan rawTokens for any --<flag> not in this prompt's spec — those
622
+ // are real "unknown for this prompt" errors. (Commander already
623
+ // consumed the registered ones; only out-of-spec flags survive here
624
+ // via .allowUnknownOption(true).)
582
625
  for (var i = 0; i < rawTokens.length; i++) {
583
626
  var tok = rawTokens[i];
584
627
  if (tok === '--') break;
585
628
  if (typeof tok !== 'string' || tok.indexOf('--') !== 0) continue;
586
629
  var keyAndVal = tok.slice(2);
587
- var key, val;
588
630
  var eq = keyAndVal.indexOf('=');
589
- if (eq >= 0) {
590
- key = keyAndVal.slice(0, eq);
591
- val = keyAndVal.slice(eq + 1);
592
- } else {
593
- key = keyAndVal;
594
- var next = (i + 1 < rawTokens.length) ? rawTokens[i + 1] : undefined;
595
- if (typeof next === 'string' && next.indexOf('--') !== 0) {
596
- val = next;
597
- i++;
598
- } else {
599
- val = 'true';
600
- }
631
+ var key = eq >= 0 ? keyAndVal.slice(0, eq) : keyAndVal;
632
+ if (!byKebab[key]) {
633
+ unknown.push('--' + key);
601
634
  }
602
- var match = byKebab[key];
603
- if (!match) { unknown.push('--' + key); continue; }
604
- args[match.name] = val;
605
635
  }
606
636
  if (unknown.length > 0) {
607
637
  console.error('Error: unknown option(s) for prompt "' + name + '": ' + unknown.join(', '));
608
638
  process.exitCode = 2;
609
639
  return;
610
640
  }
611
- // Validate required args
641
+ // 3. Validate required args.
612
642
  for (var r = 0; r < spec.length; r++) {
613
643
  if (spec[r].required && args[spec[r].name] === undefined) {
614
644
  console.error('Error: missing required option --' + spec[r].kebab);