frontmcp 1.1.0 → 1.1.2-beta.1

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.
Files changed (53) hide show
  1. package/package.json +5 -5
  2. package/src/commands/build/adapters/cloudflare.js +56 -4
  3. package/src/commands/build/adapters/cloudflare.js.map +1 -1
  4. package/src/commands/build/adapters/distributed.js +4 -1
  5. package/src/commands/build/adapters/distributed.js.map +1 -1
  6. package/src/commands/build/adapters/lambda.js +32 -0
  7. package/src/commands/build/adapters/lambda.js.map +1 -1
  8. package/src/commands/build/bundler.js +23 -1
  9. package/src/commands/build/bundler.js.map +1 -1
  10. package/src/commands/build/exec/cli-runtime/extract-public-message.snippet.d.ts +18 -0
  11. package/src/commands/build/exec/cli-runtime/extract-public-message.snippet.js +58 -0
  12. package/src/commands/build/exec/cli-runtime/extract-public-message.snippet.js.map +1 -0
  13. package/src/commands/build/exec/cli-runtime/generate-cli-entry.d.ts +2 -2
  14. package/src/commands/build/exec/cli-runtime/generate-cli-entry.js +195 -47
  15. package/src/commands/build/exec/cli-runtime/generate-cli-entry.js.map +1 -1
  16. package/src/commands/build/exec/cli-runtime/schema-extractor.d.ts +7 -0
  17. package/src/commands/build/exec/cli-runtime/schema-extractor.js +17 -1
  18. package/src/commands/build/exec/cli-runtime/schema-extractor.js.map +1 -1
  19. package/src/commands/build/exec/index.d.ts +11 -0
  20. package/src/commands/build/exec/index.js +68 -20
  21. package/src/commands/build/exec/index.js.map +1 -1
  22. package/src/commands/build/exec/installer-script.d.ts +8 -2
  23. package/src/commands/build/exec/installer-script.js +34 -15
  24. package/src/commands/build/exec/installer-script.js.map +1 -1
  25. package/src/commands/build/exec/manifest.d.ts +16 -3
  26. package/src/commands/build/exec/manifest.js +17 -5
  27. package/src/commands/build/exec/manifest.js.map +1 -1
  28. package/src/commands/build/exec/runner-script.d.ts +9 -1
  29. package/src/commands/build/exec/runner-script.js +60 -2
  30. package/src/commands/build/exec/runner-script.js.map +1 -1
  31. package/src/commands/build/index.js +86 -22
  32. package/src/commands/build/index.js.map +1 -1
  33. package/src/commands/build/load-entry-config.d.ts +33 -0
  34. package/src/commands/build/load-entry-config.js +206 -0
  35. package/src/commands/build/load-entry-config.js.map +1 -0
  36. package/src/commands/build/mcpb/manifest.d.ts +14 -0
  37. package/src/commands/build/mcpb/manifest.js +29 -0
  38. package/src/commands/build/mcpb/manifest.js.map +1 -1
  39. package/src/commands/build/types.d.ts +36 -1
  40. package/src/commands/build/types.js.map +1 -1
  41. package/src/commands/dev/doctor.js +7 -3
  42. package/src/commands/dev/doctor.js.map +1 -1
  43. package/src/commands/package/install.d.ts +1 -1
  44. package/src/commands/package/install.js +10 -8
  45. package/src/commands/package/install.js.map +1 -1
  46. package/src/commands/package/types.d.ts +2 -1
  47. package/src/commands/package/types.js.map +1 -1
  48. package/src/config/frontmcp-config.loader.d.ts +20 -0
  49. package/src/config/frontmcp-config.loader.js +152 -5
  50. package/src/config/frontmcp-config.loader.js.map +1 -1
  51. package/src/config/index.d.ts +1 -1
  52. package/src/config/index.js +2 -1
  53. package/src/config/index.js.map +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frontmcp",
3
- "version": "1.1.0",
3
+ "version": "1.1.2-beta.1",
4
4
  "description": "FrontMCP command line interface",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "homepage": "https://docs.agentfront.dev",
@@ -22,6 +22,7 @@
22
22
  "url": "https://github.com/agentfront/frontmcp/issues"
23
23
  },
24
24
  "main": "./src/index.js",
25
+ "types": "./src/index.d.ts",
25
26
  "bin": {
26
27
  "frontmcp": "./src/core/cli.js"
27
28
  },
@@ -30,9 +31,9 @@
30
31
  },
31
32
  "dependencies": {
32
33
  "@clack/prompts": "^0.10.0",
33
- "@frontmcp/lazy-zod": "1.1.0",
34
- "@frontmcp/utils": "1.1.0",
35
- "@frontmcp/skills": "1.1.0",
34
+ "@frontmcp/lazy-zod": "1.1.2-beta.1",
35
+ "@frontmcp/utils": "1.1.2-beta.1",
36
+ "@frontmcp/skills": "1.1.2-beta.1",
36
37
  "commander": "^13.0.0",
37
38
  "tslib": "^2.3.0",
38
39
  "vectoriadb": "^2.2.0",
@@ -48,6 +49,5 @@
48
49
  "@types/yauzl": "^2.10.3",
49
50
  "@types/yazl": "^2.4.5"
50
51
  },
51
- "types": "./index.d.js",
52
52
  "type": "commonjs"
53
53
  }
@@ -72,10 +72,62 @@ module.exports = {
72
72
  }
73
73
  };
74
74
  `,
75
- getConfig: (_cwd) => `name = "frontmcp-worker"
76
- main = "dist/index.js"
77
- compatibility_date = "2024-01-01"
78
- `,
75
+ // #375 fail the build when the user's @FrontMcp config references
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) => {
88
+ const errors = [];
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) {
98
+ errors.push('sqlite storage is not supported on --target cloudflare (no fs / native modules on Workers). ' +
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.');
102
+ }
103
+ if (redisSeen) {
104
+ errors.push('ioredis-style `redis` storage is not supported on --target cloudflare (no Node net). ' +
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.');
108
+ }
109
+ if (errors.length) {
110
+ throw new Error(`[--target cloudflare] config incompatible with Cloudflare Workers:\n - ${errors.join('\n - ')}`);
111
+ }
112
+ },
113
+ // #374 — always write wrangler.toml from the build output. Skipping when
114
+ // the file already exists left users with a wrangler.toml that pointed at
115
+ // dist/index.js while the build emitted dist/cloudflare/index.js, and
116
+ // wrangler deploy silently failed.
117
+ alwaysWriteConfig: true,
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}"
127
+ main = "dist/cloudflare/index.js"
128
+ compatibility_date = "${compatibilityDate}"
129
+ `;
130
+ },
79
131
  configFileName: 'wrangler.toml',
80
132
  };
81
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,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 getConfig: (_cwd: string) => `name = \"frontmcp-worker\"\nmain = \"dist/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"]}
@@ -23,7 +23,10 @@ process.env.FRONTMCP_DEPLOYMENT_MODE = 'distributed';
23
23
  `,
24
24
  getEntryTemplate: (mainModulePath) => `// Auto-generated distributed entry point
25
25
  // Generated by: frontmcp build --target distributed
26
- require('./ha-setup.js');
26
+ // The build emits the HA bootstrap as serverless-setup.js (shared name across
27
+ // all serverless/distributed adapters); requiring ha-setup.js used to crash at
28
+ // runtime because that filename was never written.
29
+ require('./serverless-setup.js');
27
30
  require('${mainModulePath}');
28
31
  `,
29
32
  };
@@ -1 +1 @@
1
- {"version":3,"file":"distributed.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/distributed.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;GAYG;AACU,QAAA,kBAAkB,GAAoB;IACjD,YAAY,EAAE,UAAU;IAExB,gBAAgB,EAAE,GAAG,EAAE,CAAC;;;;CAIzB;IAEC,gBAAgB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC;;;WAGrC,cAAc;CACxB;CACA,CAAC","sourcesContent":["import type { AdapterTemplate } from '../types';\n\n/**\n * Distributed adapter - multi-pod Node.js deployment with HA.\n * Compiles to CommonJS with an HA setup file that sets FRONTMCP_DEPLOYMENT_MODE.\n *\n * The setup file runs before any imports to ensure the deployment mode\n * is detected before @FrontMcp decorators execute. This enables:\n * - Session heartbeat and takeover\n * - Cross-pod notification relay via Redis pub/sub\n * - LB affinity via __frontmcp_node cookie\n * - Machine ID from K8s HOSTNAME or os.hostname()\n *\n * Requires Redis configuration in @FrontMcp() for session persistence.\n */\nexport const distributedAdapter: AdapterTemplate = {\n moduleFormat: 'commonjs',\n\n getSetupTemplate: () => `// Distributed HA setup - MUST be imported first\n// Sets FRONTMCP_DEPLOYMENT_MODE before any decorators run\n// Enables heartbeat, session takeover, and notification relay\nprocess.env.FRONTMCP_DEPLOYMENT_MODE = 'distributed';\n`,\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated distributed entry point\n// Generated by: frontmcp build --target distributed\nrequire('./ha-setup.js');\nrequire('${mainModulePath}');\n`,\n};\n"]}
1
+ {"version":3,"file":"distributed.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/distributed.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;GAYG;AACU,QAAA,kBAAkB,GAAoB;IACjD,YAAY,EAAE,UAAU;IAExB,gBAAgB,EAAE,GAAG,EAAE,CAAC;;;;CAIzB;IAEC,gBAAgB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC;;;;;;WAMrC,cAAc;CACxB;CACA,CAAC","sourcesContent":["import type { AdapterTemplate } from '../types';\n\n/**\n * Distributed adapter - multi-pod Node.js deployment with HA.\n * Compiles to CommonJS with an HA setup file that sets FRONTMCP_DEPLOYMENT_MODE.\n *\n * The setup file runs before any imports to ensure the deployment mode\n * is detected before @FrontMcp decorators execute. This enables:\n * - Session heartbeat and takeover\n * - Cross-pod notification relay via Redis pub/sub\n * - LB affinity via __frontmcp_node cookie\n * - Machine ID from K8s HOSTNAME or os.hostname()\n *\n * Requires Redis configuration in @FrontMcp() for session persistence.\n */\nexport const distributedAdapter: AdapterTemplate = {\n moduleFormat: 'commonjs',\n\n getSetupTemplate: () => `// Distributed HA setup - MUST be imported first\n// Sets FRONTMCP_DEPLOYMENT_MODE before any decorators run\n// Enables heartbeat, session takeover, and notification relay\nprocess.env.FRONTMCP_DEPLOYMENT_MODE = 'distributed';\n`,\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated distributed entry point\n// Generated by: frontmcp build --target distributed\n// The build emits the HA bootstrap as serverless-setup.js (shared name across\n// all serverless/distributed adapters); requiring ha-setup.js used to crash at\n// runtime because that filename was never written.\nrequire('./serverless-setup.js');\nrequire('${mainModulePath}');\n`,\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.
@@ -51,6 +54,35 @@ export const handler = async (event, context) => {
51
54
  return serverlessExpressInstance(event, context);
52
55
  };
53
56
  `,
57
+ // #368 round-2 — `@codegenie/serverless-express` is a peer dep on this
58
+ // adapter (the lambda runtime ships it via Layer or the user installs it).
59
+ // The bundler externalizes it so rspack doesn't try to bundle it; this
60
+ // validate hook surfaces a clear error at build time when the package
61
+ // isn't installed locally either, instead of letting the user discover
62
+ // a runtime "Cannot find module" error in CloudWatch logs.
63
+ validate: () => {
64
+ try {
65
+ // Walk node_modules from cwd upward so monorepos / hoisted layouts work.
66
+ let dir = process.cwd();
67
+ const root = path.parse(dir).root;
68
+ while (dir !== root) {
69
+ const pkg = path.join(dir, 'node_modules', '@codegenie', 'serverless-express', 'package.json');
70
+ if (fs.existsSync(pkg))
71
+ return;
72
+ const next = path.dirname(dir);
73
+ if (next === dir)
74
+ break;
75
+ dir = next;
76
+ }
77
+ }
78
+ catch {
79
+ // Fall through to throw — accessibility errors mean we can't verify presence.
80
+ }
81
+ throw new Error(`[--target lambda] missing required peer dependency @codegenie/serverless-express.\n` +
82
+ ` Install it in your project:\n` +
83
+ ` npm install @codegenie/serverless-express\n` +
84
+ ` (or use a Lambda Layer that provides it).`);
85
+ },
54
86
  // No config file - user manages serverless.yml, SAM template, or CDK
55
87
  };
56
88
  //# 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,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,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 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 // #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"]}
@@ -34,11 +34,33 @@ 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',
37
44
  },
38
45
  resolve: {
39
46
  extensions: ['.js', '.mjs', '.cjs', '.json'],
40
- // Allow imports without file extensions (TypeScript compiles without .js but ESM requires them)
47
+ // Allow imports without file extensions (TypeScript compiles without .js
48
+ // but strict ESM requires them).
49
+ //
50
+ // #368 round-2 — top-level `fullySpecified: false` alone wasn't enough.
51
+ // When the entry's sibling `package.json` declares `{"type":"module"}`
52
+ // (vercel/lambda adapters do this so Node treats `index.js` as ESM),
53
+ // rspack classifies the relative import edges as `esm` dependencies
54
+ // and applies its strict-ESM resolver, which ignores the top-level
55
+ // setting. `byDependency` overrides per dependency type so
56
+ // `import { CalcApp } from './calc.app'` resolves whether the import
57
+ // is parsed as CJS, ESM, or commonjs-require.
41
58
  fullySpecified: false,
59
+ byDependency: {
60
+ esm: { fullySpecified: false },
61
+ commonjs: { fullySpecified: false },
62
+ 'commonjs-require': { fullySpecified: false },
63
+ },
42
64
  },
43
65
  module: {
44
66
  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 },\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"]}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * CLI-bundle source for `_extractPublicMessage` / `_exitWithError`.
3
+ *
4
+ * The SEA CLI bundle has to be self-contained — it can't reach into
5
+ * `@frontmcp/sdk` at runtime — so we duplicate the SDK's
6
+ * `extractPublicMessage` walker into the generated source. To prevent the
7
+ * two implementations from drifting, the duplicated source lives in this
8
+ * file as a string constant and is shared by:
9
+ *
10
+ * - `generateCliEntry.ts` (embeds it in the generated CLI source)
11
+ * - `extract-public-message-parity.spec.ts` (parity-tests it against
12
+ * the SDK's TS implementation using shared fixtures)
13
+ *
14
+ * Keep the function bodies free of TS / ESM-only syntax — the resulting
15
+ * string is concatenated into a CommonJS bundle and `Function('return …')`
16
+ * compiles it as plain ES2020.
17
+ */
18
+ export declare const EXTRACT_PUBLIC_MESSAGE_SNIPPET: string;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EXTRACT_PUBLIC_MESSAGE_SNIPPET = void 0;
4
+ /**
5
+ * CLI-bundle source for `_extractPublicMessage` / `_exitWithError`.
6
+ *
7
+ * The SEA CLI bundle has to be self-contained — it can't reach into
8
+ * `@frontmcp/sdk` at runtime — so we duplicate the SDK's
9
+ * `extractPublicMessage` walker into the generated source. To prevent the
10
+ * two implementations from drifting, the duplicated source lives in this
11
+ * file as a string constant and is shared by:
12
+ *
13
+ * - `generateCliEntry.ts` (embeds it in the generated CLI source)
14
+ * - `extract-public-message-parity.spec.ts` (parity-tests it against
15
+ * the SDK's TS implementation using shared fixtures)
16
+ *
17
+ * Keep the function bodies free of TS / ESM-only syntax — the resulting
18
+ * string is concatenated into a CommonJS bundle and `Function('return …')`
19
+ * compiles it as plain ES2020.
20
+ */
21
+ exports.EXTRACT_PUBLIC_MESSAGE_SNIPPET = `
22
+ // Walk an error chain (cause / originalError) and return the most user-friendly
23
+ // message — prefers PublicMcpError.getPublicMessage() over wrapped wrappers like
24
+ // "Tool 'X' execution failed: Unknown error". Mirrors @frontmcp/sdk's
25
+ // extractPublicMessage so the SEA bundle stays self-contained. Cycles in the
26
+ // chain are short-circuited via a WeakSet of already-visited error objects.
27
+ function _extractPublicMessage(err) {
28
+ return _extractPublicMessageImpl(err, new WeakSet());
29
+ }
30
+ function _extractPublicMessageImpl(err, visited) {
31
+ if (err && typeof err === 'object') {
32
+ if (visited.has(err)) return 'Unknown error';
33
+ visited.add(err);
34
+ }
35
+ if (err == null) return 'Unknown error';
36
+ if (typeof err === 'string') return err;
37
+ // Direct PublicMcpError (has isPublic === true and a non-default message)
38
+ if (err && err.isPublic === true && err.message) return err.message;
39
+ // Wrapped: try originalError, then cause.
40
+ var inner = err && (err.originalError || err.cause);
41
+ if (inner) {
42
+ var innerMsg = _extractPublicMessageImpl(inner, visited);
43
+ if (innerMsg && innerMsg !== 'Unknown error') return innerMsg;
44
+ }
45
+ // Fallback: own .message, but skip generic wrappers when we have nothing better.
46
+ if (err.message) return err.message;
47
+ return String(err);
48
+ }
49
+ // Print an error with the best available public message and set the appropriate
50
+ // exit code (1 = runtime error, 2 = usage error). Centralized so every action
51
+ // handler reports the same shape.
52
+ function _exitWithError(err, code) {
53
+ var msg = _extractPublicMessage(err);
54
+ console.error('Error: ' + msg);
55
+ process.exitCode = code || 1;
56
+ }
57
+ `.trim();
58
+ //# sourceMappingURL=extract-public-message.snippet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-public-message.snippet.js","sourceRoot":"","sources":["../../../../../../src/commands/build/exec/cli-runtime/extract-public-message.snippet.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;;GAgBG;AACU,QAAA,8BAA8B,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoC7C,CAAC,IAAI,EAAE,CAAC","sourcesContent":["/**\n * CLI-bundle source for `_extractPublicMessage` / `_exitWithError`.\n *\n * The SEA CLI bundle has to be self-contained — it can't reach into\n * `@frontmcp/sdk` at runtime — so we duplicate the SDK's\n * `extractPublicMessage` walker into the generated source. To prevent the\n * two implementations from drifting, the duplicated source lives in this\n * file as a string constant and is shared by:\n *\n * - `generateCliEntry.ts` (embeds it in the generated CLI source)\n * - `extract-public-message-parity.spec.ts` (parity-tests it against\n * the SDK's TS implementation using shared fixtures)\n *\n * Keep the function bodies free of TS / ESM-only syntax — the resulting\n * string is concatenated into a CommonJS bundle and `Function('return …')`\n * compiles it as plain ES2020.\n */\nexport const EXTRACT_PUBLIC_MESSAGE_SNIPPET = `\n// Walk an error chain (cause / originalError) and return the most user-friendly\n// message — prefers PublicMcpError.getPublicMessage() over wrapped wrappers like\n// \"Tool 'X' execution failed: Unknown error\". Mirrors @frontmcp/sdk's\n// extractPublicMessage so the SEA bundle stays self-contained. Cycles in the\n// chain are short-circuited via a WeakSet of already-visited error objects.\nfunction _extractPublicMessage(err) {\n return _extractPublicMessageImpl(err, new WeakSet());\n}\nfunction _extractPublicMessageImpl(err, visited) {\n if (err && typeof err === 'object') {\n if (visited.has(err)) return 'Unknown error';\n visited.add(err);\n }\n if (err == null) return 'Unknown error';\n if (typeof err === 'string') return err;\n // Direct PublicMcpError (has isPublic === true and a non-default message)\n if (err && err.isPublic === true && err.message) return err.message;\n // Wrapped: try originalError, then cause.\n var inner = err && (err.originalError || err.cause);\n if (inner) {\n var innerMsg = _extractPublicMessageImpl(inner, visited);\n if (innerMsg && innerMsg !== 'Unknown error') return innerMsg;\n }\n // Fallback: own .message, but skip generic wrappers when we have nothing better.\n if (err.message) return err.message;\n return String(err);\n}\n// Print an error with the best available public message and set the appropriate\n// exit code (1 = runtime error, 2 = usage error). Centralized so every action\n// handler reports the same shape.\nfunction _exitWithError(err, code) {\n var msg = _extractPublicMessage(err);\n console.error('Error: ' + msg);\n process.exitCode = code || 1;\n}\n`.trim();\n"]}
@@ -2,8 +2,8 @@
2
2
  * Generates the CLI entry point TypeScript/JavaScript source code.
3
3
  * This creates a commander.js-based CLI where each MCP tool is a subcommand.
4
4
  */
5
- import { CliConfig, OAuthConfig } from '../config';
6
- import { ExtractedSchema } from './schema-extractor';
5
+ import { type CliConfig, type OAuthConfig } from '../config';
6
+ import { type ExtractedSchema } from './schema-extractor';
7
7
  export declare const RESERVED_COMMANDS: Set<string>;
8
8
  export interface CliEntryOptions {
9
9
  appName: string;