frontmcp 1.1.2-beta.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/package.json +4 -4
  2. package/src/commands/build/adapters/lambda.js +13 -8
  3. package/src/commands/build/adapters/lambda.js.map +1 -1
  4. package/src/commands/build/adapters/vercel.js +24 -10
  5. package/src/commands/build/adapters/vercel.js.map +1 -1
  6. package/src/commands/build/bundler.js +10 -0
  7. package/src/commands/build/bundler.js.map +1 -1
  8. package/src/commands/build/exec/cli-runtime/schema-extractor.js +2 -1
  9. package/src/commands/build/exec/cli-runtime/schema-extractor.js.map +1 -1
  10. package/src/commands/build/exec/config.d.ts +1 -1
  11. package/src/commands/build/exec/config.js +30 -1
  12. package/src/commands/build/exec/config.js.map +1 -1
  13. package/src/commands/build/exec/index.d.ts +1 -0
  14. package/src/commands/build/exec/index.js +6 -0
  15. package/src/commands/build/exec/index.js.map +1 -1
  16. package/src/commands/build/index.js +1 -0
  17. package/src/commands/build/index.js.map +1 -1
  18. package/src/commands/skills/export.d.ts +9 -0
  19. package/src/commands/skills/export.js +116 -0
  20. package/src/commands/skills/export.js.map +1 -0
  21. package/src/commands/skills/exporters/copilot.d.ts +3 -0
  22. package/src/commands/skills/exporters/copilot.js +28 -0
  23. package/src/commands/skills/exporters/copilot.js.map +1 -0
  24. package/src/commands/skills/exporters/cursor.d.ts +19 -0
  25. package/src/commands/skills/exporters/cursor.js +46 -0
  26. package/src/commands/skills/exporters/cursor.js.map +1 -0
  27. package/src/commands/skills/exporters/index.d.ts +4 -0
  28. package/src/commands/skills/exporters/index.js +11 -0
  29. package/src/commands/skills/exporters/index.js.map +1 -0
  30. package/src/commands/skills/exporters/sanitize.d.ts +1 -0
  31. package/src/commands/skills/exporters/sanitize.js +0 -0
  32. package/src/commands/skills/exporters/sanitize.js.map +1 -0
  33. package/src/commands/skills/exporters/windsurf.d.ts +7 -0
  34. package/src/commands/skills/exporters/windsurf.js +31 -0
  35. package/src/commands/skills/exporters/windsurf.js.map +1 -0
  36. package/src/commands/skills/publish.d.ts +11 -0
  37. package/src/commands/skills/publish.js +91 -0
  38. package/src/commands/skills/publish.js.map +1 -0
  39. package/src/commands/skills/register.d.ts +1 -1
  40. package/src/commands/skills/register.js +46 -0
  41. package/src/commands/skills/register.js.map +1 -1
  42. package/src/commands/skills/targets/glama.d.ts +18 -0
  43. package/src/commands/skills/targets/glama.js +28 -0
  44. package/src/commands/skills/targets/glama.js.map +1 -0
  45. package/src/commands/skills/targets/index.d.ts +3 -0
  46. package/src/commands/skills/targets/index.js +11 -0
  47. package/src/commands/skills/targets/index.js.map +1 -0
  48. package/src/commands/skills/targets/smithery.d.ts +32 -0
  49. package/src/commands/skills/targets/smithery.js +29 -0
  50. package/src/commands/skills/targets/smithery.js.map +1 -0
  51. package/src/config/frontmcp-config.loader.js +31 -18
  52. package/src/config/frontmcp-config.loader.js.map +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frontmcp",
3
- "version": "1.1.2-beta.1",
3
+ "version": "1.2.0",
4
4
  "description": "FrontMCP command line interface",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "homepage": "https://docs.agentfront.dev",
@@ -31,9 +31,9 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@clack/prompts": "^0.10.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",
34
+ "@frontmcp/lazy-zod": "1.2.0",
35
+ "@frontmcp/utils": "1.2.0",
36
+ "@frontmcp/skills": "1.2.0",
37
37
  "commander": "^13.0.0",
38
38
  "tslib": "^2.3.0",
39
39
  "vectoriadb": "^2.2.0",
@@ -20,12 +20,16 @@ const path = tslib_1.__importStar(require("path"));
20
20
  * @see https://github.com/codegenie/serverless-express
21
21
  */
22
22
  exports.lambdaAdapter = {
23
- 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',
24
29
  shouldBundle: true,
25
30
  bundleOutput: 'handler.cjs',
26
- getSetupTemplate: () => `// Serverless environment setup - MUST be imported first
31
+ getSetupTemplate: () => `// Serverless environment setup - MUST be required first
27
32
  // This sets FRONTMCP_SERVERLESS before any decorators run
28
- // Required because ESM hoists imports before other statements
29
33
  process.env.FRONTMCP_SERVERLESS = '1';
30
34
  process.env.FRONTMCP_DEPLOYMENT_MODE = 'serverless';
31
35
  `,
@@ -35,10 +39,11 @@ process.env.FRONTMCP_DEPLOYMENT_MODE = 'serverless';
35
39
  // IMPORTANT: This adapter requires @codegenie/serverless-express
36
40
  // Install it with: npm install @codegenie/serverless-express
37
41
  //
38
- import './serverless-setup.js';
39
- import '${mainModulePath}';
40
- import { getServerlessHandlerAsync } from '@frontmcp/sdk';
41
- 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;
42
47
 
43
48
  let serverlessExpressInstance = null;
44
49
 
@@ -47,7 +52,7 @@ async function setup() {
47
52
  serverlessExpressInstance = serverlessExpress({ app });
48
53
  }
49
54
 
50
- export const handler = async (event, context) => {
55
+ exports.handler = async (event, context) => {
51
56
  if (!serverlessExpressInstance) {
52
57
  await setup();
53
58
  }
@@ -1 +1 @@
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"]}
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"]}
@@ -41,6 +41,16 @@ async function bundleForServerless(entryPath, outDir, outputFilename) {
41
41
  // validate hook surfaces a clear "npm install @codegenie/serverless-express"
42
42
  // error when it's actually missing from node_modules at build time.
43
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',
44
54
  },
45
55
  resolve: {
46
56
  extensions: ['.js', '.mjs', '.cjs', '.json'],
@@ -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 // #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"]}
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"]}
@@ -97,7 +97,8 @@ async function extractSchemas(bundlePath) {
97
97
  arguments: p.arguments,
98
98
  }));
99
99
  const toolNameSet = new Set(tools.map((t) => t.name));
100
- const hasSkillsResources = resourceTemplates.some((rt) => rt.uriTemplate.startsWith('skills://'));
100
+ // SEP-2640: skills are exposed under the singular `skill://` URI scheme.
101
+ const hasSkillsResources = resourceTemplates.some((rt) => rt.uriTemplate.startsWith('skill://'));
101
102
  const capabilities = {
102
103
  skills: hasSkillsResources,
103
104
  jobs: toolNameSet.has('execute-job') || toolNameSet.has('get-job-status'),
@@ -1 +1 @@
1
- {"version":3,"file":"schema-extractor.js","sourceRoot":"","sources":["../../../../../../src/commands/build/exec/cli-runtime/schema-extractor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Build-time schema extraction.\n * After the server bundle is produced, boots a DirectClient via connect(),\n * extracts tool/resource/prompt schemas, and serializes them for CLI code generation.\n */\n\nexport interface ExtractedTool {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n}\n\nexport interface ExtractedResource {\n uri: string;\n name: string;\n description?: string;\n mimeType?: string;\n}\n\nexport interface ExtractedResourceTemplate {\n uriTemplate: string;\n name: string;\n description?: string;\n}\n\nexport interface ExtractedPrompt {\n name: string;\n description?: string;\n arguments?: Array<{\n name: string;\n description?: string;\n required?: boolean;\n }>;\n}\n\nexport interface ExtractedJob {\n name: string;\n description?: string;\n inputSchema?: Record<string, unknown>;\n tags?: string[];\n}\n\nexport interface ExtractedCapabilities {\n skills: boolean;\n jobs: boolean;\n workflows: boolean;\n}\n\nexport interface ExtractedSkillAsset {\n skillName: string;\n baseDir?: string;\n instructionFile?: string;\n resourceDirs?: {\n references?: string;\n examples?: string;\n scripts?: string;\n assets?: string;\n };\n}\n\nexport interface ExtractedSchema {\n tools: ExtractedTool[];\n resources: ExtractedResource[];\n resourceTemplates: ExtractedResourceTemplate[];\n prompts: ExtractedPrompt[];\n jobs: ExtractedJob[];\n capabilities: ExtractedCapabilities;\n skillAssets: ExtractedSkillAsset[];\n /**\n * Server bind config extracted from `@FrontMcp({ http })` (or its\n * decorator-attached `__frontmcp:config` metadata). Used by the manifest\n * writer so the published manifest reflects the port the server will\n * actually listen on, not a hard-coded SDK default.\n */\n httpPort?: number;\n}\n\n/** Known system tool names injected by SDK features (jobs, workflows). */\nexport const SYSTEM_TOOL_NAMES = new Set([\n 'list-jobs',\n 'execute-job',\n 'get-job-status',\n 'register-job',\n 'remove-job',\n 'list-workflows',\n 'execute-workflow',\n 'get-workflow-status',\n 'register-workflow',\n 'remove-workflow',\n]);\n\n/**\n * Extract schemas from a compiled server bundle.\n * Requires the bundle to export a FrontMcp-decorated class as default export\n * or a config object usable by connect().\n */\nexport async function extractSchemas(bundlePath: string): Promise<ExtractedSchema> {\n // Suppress @FrontMcp() decorator bootstrap — we only need metadata, not a running server\n const prev = process.env['FRONTMCP_SCHEMA_EXTRACT'];\n process.env['FRONTMCP_SCHEMA_EXTRACT'] = '1';\n\n let mod: Record<string, unknown>;\n try {\n mod = require(bundlePath);\n } finally {\n if (prev === undefined) {\n delete process.env['FRONTMCP_SCHEMA_EXTRACT'];\n } else {\n process.env['FRONTMCP_SCHEMA_EXTRACT'] = prev;\n }\n }\n\n const configOrClass = mod.default || mod;\n\n // Use @frontmcp/sdk connect() to boot in-memory client\n let connect: (config: unknown, options?: { mode?: string }) => Promise<unknown>;\n try {\n const sdk = require('@frontmcp/sdk');\n connect = sdk.connect || sdk.direct?.connect;\n if (!connect) {\n throw new Error('connect() not found in @frontmcp/sdk');\n }\n } catch {\n throw new Error(\n '@frontmcp/sdk is required for CLI schema extraction. Ensure it is installed.',\n );\n }\n\n const client = await connect(configOrClass, { mode: 'cli' }) as {\n listTools(): Promise<unknown>;\n listResources(): Promise<{ resources: Array<{ uri: string; name?: string; description?: string; mimeType?: string }> }>;\n listResourceTemplates?(): Promise<{ resourceTemplates: Array<{ uriTemplate: string; name?: string; description?: string }> }>;\n listPrompts(): Promise<{ prompts: Array<{ name: string; description?: string; arguments?: unknown[] }> }>;\n listJobs?(): Promise<{ jobs: Array<{ name: string; description?: string; inputSchema?: Record<string, unknown>; tags?: string[] }>; count: number }>;\n collectSkillAssets?(): Promise<{ entries: Array<{ skillName: string; baseDir?: string; instructionFile?: string; resources?: Record<string, string | undefined> }> }>;\n close(): Promise<void>;\n };\n\n try {\n const [toolsRaw, resourcesResult, promptsResult] = await Promise.all([\n client.listTools().catch(() => []),\n client.listResources().catch(() => ({ resources: [] })),\n client.listPrompts().catch(() => ({ prompts: [] })),\n ]);\n\n let resourceTemplates: ExtractedResourceTemplate[] = [];\n if (client.listResourceTemplates) {\n try {\n const templatesResult = await client.listResourceTemplates();\n resourceTemplates = (templatesResult.resourceTemplates || []).map((t) => ({\n uriTemplate: t.uriTemplate,\n name: t.name || t.uriTemplate,\n description: t.description,\n }));\n } catch {\n // Resource templates not supported\n }\n }\n\n // DirectClient.listTools() returns FormattedTools (array) directly,\n // not { tools: [...] } like the raw MCP client\n const toolsList = Array.isArray(toolsRaw)\n ? toolsRaw as Array<{ name: string; description?: string; inputSchema?: unknown }>\n : ((toolsRaw as { tools?: unknown[] })?.tools || []) as Array<{ name: string; description?: string; inputSchema?: unknown }>;\n\n const tools: ExtractedTool[] = toolsList.map((t) => ({\n name: t.name,\n description: t.description || '',\n inputSchema: (t.inputSchema as Record<string, unknown>) || { type: 'object', properties: {} },\n }));\n\n const resources: ExtractedResource[] = (resourcesResult.resources || []).map((r) => ({\n uri: r.uri,\n name: r.name || r.uri,\n description: r.description,\n mimeType: r.mimeType,\n }));\n\n const prompts: ExtractedPrompt[] = (promptsResult.prompts || []).map((p) => ({\n name: p.name,\n description: p.description,\n arguments: p.arguments as ExtractedPrompt['arguments'],\n }));\n\n const toolNameSet = new Set(tools.map((t) => t.name));\n const hasSkillsResources = resourceTemplates.some(\n (rt) => rt.uriTemplate.startsWith('skills://'),\n );\n const capabilities: ExtractedCapabilities = {\n skills: hasSkillsResources,\n jobs: toolNameSet.has('execute-job') || toolNameSet.has('get-job-status'),\n workflows: toolNameSet.has('execute-workflow') || toolNameSet.has('get-workflow-status'),\n };\n\n // Extract job schemas if jobs capability is available\n let jobs: ExtractedJob[] = [];\n if (capabilities.jobs && client.listJobs) {\n try {\n const jobsResult = await client.listJobs();\n jobs = (jobsResult.jobs || []).map((j) => ({\n name: j.name,\n description: j.description,\n inputSchema: j.inputSchema,\n tags: j.tags,\n }));\n } catch {\n // Jobs listing not available at build time\n }\n }\n\n // Collect skill file assets for copying to dist\n let skillAssets: ExtractedSkillAsset[] = [];\n if (capabilities.skills && client.collectSkillAssets) {\n try {\n const manifest = await client.collectSkillAssets();\n skillAssets = manifest.entries.map((e) => ({\n skillName: e.skillName,\n baseDir: e.baseDir,\n instructionFile: e.instructionFile,\n resourceDirs: e.resources as ExtractedSkillAsset['resourceDirs'],\n }));\n } catch {\n // Skill asset collection not available\n }\n }\n\n // Extract http.port from the @FrontMcp() decorator metadata. Returns\n // undefined when the entry is a plain config object (no decorator) or\n // when no port was declared — caller falls back to its own default.\n let httpPort: number | undefined;\n try {\n const sdk = require('@frontmcp/sdk') as { getDecoratorConfig?: (t: unknown) => Record<string, unknown> | undefined };\n const cfg = typeof sdk.getDecoratorConfig === 'function'\n ? sdk.getDecoratorConfig(configOrClass)\n : (configOrClass as Record<string, unknown> | undefined);\n const http = cfg?.['http'] as { port?: number } | undefined;\n if (http && typeof http.port === 'number') httpPort = http.port;\n } catch {\n // best-effort — manifest writer will fall back to its own default\n }\n\n return { tools, resources, resourceTemplates, prompts, jobs, capabilities, skillAssets, httpPort };\n } finally {\n await client.close().catch(() => {});\n }\n}\n"]}
1
+ {"version":3,"file":"schema-extractor.js","sourceRoot":"","sources":["../../../../../../src/commands/build/exec/cli-runtime/schema-extractor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Build-time schema extraction.\n * After the server bundle is produced, boots a DirectClient via connect(),\n * extracts tool/resource/prompt schemas, and serializes them for CLI code generation.\n */\n\nexport interface ExtractedTool {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n}\n\nexport interface ExtractedResource {\n uri: string;\n name: string;\n description?: string;\n mimeType?: string;\n}\n\nexport interface ExtractedResourceTemplate {\n uriTemplate: string;\n name: string;\n description?: string;\n}\n\nexport interface ExtractedPrompt {\n name: string;\n description?: string;\n arguments?: Array<{\n name: string;\n description?: string;\n required?: boolean;\n }>;\n}\n\nexport interface ExtractedJob {\n name: string;\n description?: string;\n inputSchema?: Record<string, unknown>;\n tags?: string[];\n}\n\nexport interface ExtractedCapabilities {\n skills: boolean;\n jobs: boolean;\n workflows: boolean;\n}\n\nexport interface ExtractedSkillAsset {\n skillName: string;\n baseDir?: string;\n instructionFile?: string;\n resourceDirs?: {\n references?: string;\n examples?: string;\n scripts?: string;\n assets?: string;\n };\n}\n\nexport interface ExtractedSchema {\n tools: ExtractedTool[];\n resources: ExtractedResource[];\n resourceTemplates: ExtractedResourceTemplate[];\n prompts: ExtractedPrompt[];\n jobs: ExtractedJob[];\n capabilities: ExtractedCapabilities;\n skillAssets: ExtractedSkillAsset[];\n /**\n * Server bind config extracted from `@FrontMcp({ http })` (or its\n * decorator-attached `__frontmcp:config` metadata). Used by the manifest\n * writer so the published manifest reflects the port the server will\n * actually listen on, not a hard-coded SDK default.\n */\n httpPort?: number;\n}\n\n/** Known system tool names injected by SDK features (jobs, workflows). */\nexport const SYSTEM_TOOL_NAMES = new Set([\n 'list-jobs',\n 'execute-job',\n 'get-job-status',\n 'register-job',\n 'remove-job',\n 'list-workflows',\n 'execute-workflow',\n 'get-workflow-status',\n 'register-workflow',\n 'remove-workflow',\n]);\n\n/**\n * Extract schemas from a compiled server bundle.\n * Requires the bundle to export a FrontMcp-decorated class as default export\n * or a config object usable by connect().\n */\nexport async function extractSchemas(bundlePath: string): Promise<ExtractedSchema> {\n // Suppress @FrontMcp() decorator bootstrap — we only need metadata, not a running server\n const prev = process.env['FRONTMCP_SCHEMA_EXTRACT'];\n process.env['FRONTMCP_SCHEMA_EXTRACT'] = '1';\n\n let mod: Record<string, unknown>;\n try {\n mod = require(bundlePath);\n } finally {\n if (prev === undefined) {\n delete process.env['FRONTMCP_SCHEMA_EXTRACT'];\n } else {\n process.env['FRONTMCP_SCHEMA_EXTRACT'] = prev;\n }\n }\n\n const configOrClass = mod.default || mod;\n\n // Use @frontmcp/sdk connect() to boot in-memory client\n let connect: (config: unknown, options?: { mode?: string }) => Promise<unknown>;\n try {\n const sdk = require('@frontmcp/sdk');\n connect = sdk.connect || sdk.direct?.connect;\n if (!connect) {\n throw new Error('connect() not found in @frontmcp/sdk');\n }\n } catch {\n throw new Error(\n '@frontmcp/sdk is required for CLI schema extraction. Ensure it is installed.',\n );\n }\n\n const client = await connect(configOrClass, { mode: 'cli' }) as {\n listTools(): Promise<unknown>;\n listResources(): Promise<{ resources: Array<{ uri: string; name?: string; description?: string; mimeType?: string }> }>;\n listResourceTemplates?(): Promise<{ resourceTemplates: Array<{ uriTemplate: string; name?: string; description?: string }> }>;\n listPrompts(): Promise<{ prompts: Array<{ name: string; description?: string; arguments?: unknown[] }> }>;\n listJobs?(): Promise<{ jobs: Array<{ name: string; description?: string; inputSchema?: Record<string, unknown>; tags?: string[] }>; count: number }>;\n collectSkillAssets?(): Promise<{ entries: Array<{ skillName: string; baseDir?: string; instructionFile?: string; resources?: Record<string, string | undefined> }> }>;\n close(): Promise<void>;\n };\n\n try {\n const [toolsRaw, resourcesResult, promptsResult] = await Promise.all([\n client.listTools().catch(() => []),\n client.listResources().catch(() => ({ resources: [] })),\n client.listPrompts().catch(() => ({ prompts: [] })),\n ]);\n\n let resourceTemplates: ExtractedResourceTemplate[] = [];\n if (client.listResourceTemplates) {\n try {\n const templatesResult = await client.listResourceTemplates();\n resourceTemplates = (templatesResult.resourceTemplates || []).map((t) => ({\n uriTemplate: t.uriTemplate,\n name: t.name || t.uriTemplate,\n description: t.description,\n }));\n } catch {\n // Resource templates not supported\n }\n }\n\n // DirectClient.listTools() returns FormattedTools (array) directly,\n // not { tools: [...] } like the raw MCP client\n const toolsList = Array.isArray(toolsRaw)\n ? toolsRaw as Array<{ name: string; description?: string; inputSchema?: unknown }>\n : ((toolsRaw as { tools?: unknown[] })?.tools || []) as Array<{ name: string; description?: string; inputSchema?: unknown }>;\n\n const tools: ExtractedTool[] = toolsList.map((t) => ({\n name: t.name,\n description: t.description || '',\n inputSchema: (t.inputSchema as Record<string, unknown>) || { type: 'object', properties: {} },\n }));\n\n const resources: ExtractedResource[] = (resourcesResult.resources || []).map((r) => ({\n uri: r.uri,\n name: r.name || r.uri,\n description: r.description,\n mimeType: r.mimeType,\n }));\n\n const prompts: ExtractedPrompt[] = (promptsResult.prompts || []).map((p) => ({\n name: p.name,\n description: p.description,\n arguments: p.arguments as ExtractedPrompt['arguments'],\n }));\n\n const toolNameSet = new Set(tools.map((t) => t.name));\n // SEP-2640: skills are exposed under the singular `skill://` URI scheme.\n const hasSkillsResources = resourceTemplates.some(\n (rt) => rt.uriTemplate.startsWith('skill://'),\n );\n const capabilities: ExtractedCapabilities = {\n skills: hasSkillsResources,\n jobs: toolNameSet.has('execute-job') || toolNameSet.has('get-job-status'),\n workflows: toolNameSet.has('execute-workflow') || toolNameSet.has('get-workflow-status'),\n };\n\n // Extract job schemas if jobs capability is available\n let jobs: ExtractedJob[] = [];\n if (capabilities.jobs && client.listJobs) {\n try {\n const jobsResult = await client.listJobs();\n jobs = (jobsResult.jobs || []).map((j) => ({\n name: j.name,\n description: j.description,\n inputSchema: j.inputSchema,\n tags: j.tags,\n }));\n } catch {\n // Jobs listing not available at build time\n }\n }\n\n // Collect skill file assets for copying to dist\n let skillAssets: ExtractedSkillAsset[] = [];\n if (capabilities.skills && client.collectSkillAssets) {\n try {\n const manifest = await client.collectSkillAssets();\n skillAssets = manifest.entries.map((e) => ({\n skillName: e.skillName,\n baseDir: e.baseDir,\n instructionFile: e.instructionFile,\n resourceDirs: e.resources as ExtractedSkillAsset['resourceDirs'],\n }));\n } catch {\n // Skill asset collection not available\n }\n }\n\n // Extract http.port from the @FrontMcp() decorator metadata. Returns\n // undefined when the entry is a plain config object (no decorator) or\n // when no port was declared — caller falls back to its own default.\n let httpPort: number | undefined;\n try {\n const sdk = require('@frontmcp/sdk') as { getDecoratorConfig?: (t: unknown) => Record<string, unknown> | undefined };\n const cfg = typeof sdk.getDecoratorConfig === 'function'\n ? sdk.getDecoratorConfig(configOrClass)\n : (configOrClass as Record<string, unknown> | undefined);\n const http = cfg?.['http'] as { port?: number } | undefined;\n if (http && typeof http.port === 'number') httpPort = http.port;\n } catch {\n // best-effort — manifest writer will fall back to its own default\n }\n\n return { tools, resources, resourceTemplates, prompts, jobs, capabilities, skillAssets, httpPort };\n } finally {\n await client.close().catch(() => {});\n }\n}\n"]}
@@ -56,7 +56,7 @@ export interface FrontmcpExecConfig {
56
56
  };
57
57
  }
58
58
  /**
59
- * Load frontmcp.config.js/json from the given directory.
59
+ * Load frontmcp.config.{ts,js,json,mjs,cjs} from the given directory.
60
60
  * Falls back to deriving minimal config from package.json.
61
61
  */
62
62
  export declare function loadExecConfig(cwd: string): Promise<FrontmcpExecConfig>;
@@ -9,13 +9,19 @@ const tslib_1 = require("tslib");
9
9
  const path = tslib_1.__importStar(require("path"));
10
10
  const fs = tslib_1.__importStar(require("fs"));
11
11
  const CONFIG_FILENAMES = [
12
+ // #365 round-3 — `.ts` is checked first so the user's typed config takes
13
+ // priority over a stale `.js` left in the project. `.ts` loading routes
14
+ // through the same esbuild path as the new-shape loader so it works under
15
+ // `"type": "commonjs"` (Node's `require(.ts)` and `await import(.ts)` are
16
+ // both unreliable here and were the root cause of #365 1.1.0–1.1.2-beta.1).
17
+ 'frontmcp.config.ts',
12
18
  'frontmcp.config.js',
13
19
  'frontmcp.config.json',
14
20
  'frontmcp.config.mjs',
15
21
  'frontmcp.config.cjs',
16
22
  ];
17
23
  /**
18
- * Load frontmcp.config.js/json from the given directory.
24
+ * Load frontmcp.config.{ts,js,json,mjs,cjs} from the given directory.
19
25
  * Falls back to deriving minimal config from package.json.
20
26
  */
21
27
  async function loadExecConfig(cwd) {
@@ -26,6 +32,29 @@ async function loadExecConfig(cwd) {
26
32
  const content = fs.readFileSync(configPath, 'utf-8');
27
33
  return JSON.parse(content);
28
34
  }
35
+ if (filename.endsWith('.ts')) {
36
+ // Delegate to the new loader's esbuild transpile so `.ts` configs
37
+ // work under `"type": "commonjs"` projects. Hard-fails on parse error
38
+ // (no silent default) — matches frontmcp-config.loader semantics.
39
+ // tsc uses `--moduleResolution nodenext`, so the dynamic import
40
+ // needs an explicit `.js` suffix that resolves to the compiled
41
+ // output. The barrel (`../../../config/index.js`) re-exports
42
+ // `loadFrontMcpConfig` from the loader module.
43
+ const { loadFrontMcpConfig } = await import('../../../config/index.js');
44
+ try {
45
+ // The new-shape loader returns a parsed config — we only consume the
46
+ // legacy-shape fields here. Fields that exist in both shapes
47
+ // (name, version, entry, nodeVersion) carry through; new-shape-only
48
+ // fields like `deployments` are ignored by this consumer.
49
+ const newShape = await loadFrontMcpConfig(cwd);
50
+ return newShape;
51
+ }
52
+ catch (err) {
53
+ throw new Error(`Failed to load ${filename}: ${err.message}\n` +
54
+ `If your config doesn't match the new schema (deployments[] etc.), ` +
55
+ `rename it to .js or use the legacy module.exports shape.`);
56
+ }
57
+ }
29
58
  // JS/MJS/CJS config — require it
30
59
  const mod = require(configPath);
31
60
  return (mod.default || mod);
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../../../src/commands/build/exec/config.ts"],"names":[],"mappings":";AAAA;;GAEG;;AA0EH,wCA6BC;AAKD,0CAgBC;;AA1HD,mDAA6B;AAC7B,+CAAyB;AA4DzB,MAAM,gBAAgB,GAAG;IACvB,oBAAoB;IACpB,sBAAsB;IACtB,qBAAqB;IACrB,qBAAqB;CACtB,CAAC;AAEF;;;GAGG;AACI,KAAK,UAAU,cAAc,CAAC,GAAW;IAC9C,KAAK,MAAM,QAAQ,IAAI,gBAAgB,EAAE,CAAC;QACxC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC5C,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC9B,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC/B,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;gBACrD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAuB,CAAC;YACnD,CAAC;YACD,iCAAiC;YAEjC,MAAM,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;YAChC,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAuB,CAAC;QACpD,CAAC;IACH,CAAC;IAED,qCAAqC;IACrC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IAC/C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CACb,sGAAsG,CACvG,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1D,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;QAC9D,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,OAAO;QAC/B,KAAK,EAAE,GAAG,CAAC,IAAI;KAChB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAgB,eAAe,CAAC,MAA0B;IAIxD,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CACb,sBAAsB,MAAM,CAAC,IAAI,wCAAwC,CAC1E,CAAC;IACJ,CAAC;IAED,OAAO;QACL,GAAG,MAAM;QACT,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,OAAO;QAClC,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,UAAU;KAC9C,CAAC;AACJ,CAAC","sourcesContent":["/**\n * frontmcp.config.js/json schema and loader.\n */\n\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport type { SetupDefinition } from './setup';\n\nexport interface OAuthConfig {\n serverUrl?: string;\n clientId?: string;\n defaultScope?: string;\n portRange?: [number, number];\n defaultPort?: number;\n timeout?: number;\n}\n\nexport interface CliConfig {\n enabled: boolean;\n outputDefault?: 'text' | 'json';\n authRequired?: boolean;\n description?: string;\n excludeTools?: string[];\n nativeDeps?: {\n brew?: string[];\n apt?: string[];\n npm?: string[];\n };\n oauth?: OAuthConfig;\n}\n\nexport type ConfigBuildTarget = 'cli' | 'node' | 'sdk' | 'browser' | 'cloudflare' | 'vercel' | 'lambda' | 'distributed';\n\nexport interface FrontmcpExecConfig {\n name: string;\n version?: string;\n entry?: string;\n /** Build target. When set, takes precedence over cli.enabled / sea.enabled. */\n target?: ConfigBuildTarget;\n storage?: {\n type: 'sqlite' | 'redis' | 'none';\n required?: boolean;\n };\n network?: {\n defaultPort?: number;\n supportsSocket?: boolean;\n };\n dependencies?: {\n system?: string[];\n nativeAddons?: string[];\n };\n nodeVersion?: string;\n esbuild?: {\n external?: string[];\n define?: Record<string, string>;\n target?: string;\n minify?: boolean;\n };\n setup?: SetupDefinition;\n cli?: CliConfig;\n sea?: {\n enabled?: boolean;\n };\n}\n\nconst CONFIG_FILENAMES = [\n 'frontmcp.config.js',\n 'frontmcp.config.json',\n 'frontmcp.config.mjs',\n 'frontmcp.config.cjs',\n];\n\n/**\n * Load frontmcp.config.js/json from the given directory.\n * Falls back to deriving minimal config from package.json.\n */\nexport async function loadExecConfig(cwd: string): Promise<FrontmcpExecConfig> {\n for (const filename of CONFIG_FILENAMES) {\n const configPath = path.join(cwd, filename);\n if (fs.existsSync(configPath)) {\n if (filename.endsWith('.json')) {\n const content = fs.readFileSync(configPath, 'utf-8');\n return JSON.parse(content) as FrontmcpExecConfig;\n }\n // JS/MJS/CJS config — require it\n \n const mod = require(configPath);\n return (mod.default || mod) as FrontmcpExecConfig;\n }\n }\n\n // Fallback: derive from package.json\n const pkgPath = path.join(cwd, 'package.json');\n if (!fs.existsSync(pkgPath)) {\n throw new Error(\n 'No frontmcp.config.js/json found and no package.json. Create a frontmcp.config.js for build targets.',\n );\n }\n\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));\n return {\n name: pkg.name?.replace(/^@[^/]+\\//, '') || path.basename(cwd),\n version: pkg.version || '1.0.0',\n entry: pkg.main,\n };\n}\n\n/**\n * Validate config and return normalized version.\n */\nexport function normalizeConfig(config: FrontmcpExecConfig): Required<\n Pick<FrontmcpExecConfig, 'name' | 'version' | 'nodeVersion'>\n> &\n FrontmcpExecConfig {\n if (!config.name || !/^[a-zA-Z0-9._-]+$/.test(config.name)) {\n throw new Error(\n `Invalid app name: \"${config.name}\". Must be alphanumeric with .-_ only.`,\n );\n }\n\n return {\n ...config,\n name: config.name,\n version: config.version || '1.0.0',\n nodeVersion: config.nodeVersion || '>=22.0.0',\n };\n}\n"]}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../../../src/commands/build/exec/config.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * frontmcp.config.js/json schema and loader.\n */\n\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport type { SetupDefinition } from './setup';\n\nexport interface OAuthConfig {\n serverUrl?: string;\n clientId?: string;\n defaultScope?: string;\n portRange?: [number, number];\n defaultPort?: number;\n timeout?: number;\n}\n\nexport interface CliConfig {\n enabled: boolean;\n outputDefault?: 'text' | 'json';\n authRequired?: boolean;\n description?: string;\n excludeTools?: string[];\n nativeDeps?: {\n brew?: string[];\n apt?: string[];\n npm?: string[];\n };\n oauth?: OAuthConfig;\n}\n\nexport type ConfigBuildTarget = 'cli' | 'node' | 'sdk' | 'browser' | 'cloudflare' | 'vercel' | 'lambda' | 'distributed';\n\nexport interface FrontmcpExecConfig {\n name: string;\n version?: string;\n entry?: string;\n /** Build target. When set, takes precedence over cli.enabled / sea.enabled. */\n target?: ConfigBuildTarget;\n storage?: {\n type: 'sqlite' | 'redis' | 'none';\n required?: boolean;\n };\n network?: {\n defaultPort?: number;\n supportsSocket?: boolean;\n };\n dependencies?: {\n system?: string[];\n nativeAddons?: string[];\n };\n nodeVersion?: string;\n esbuild?: {\n external?: string[];\n define?: Record<string, string>;\n target?: string;\n minify?: boolean;\n };\n setup?: SetupDefinition;\n cli?: CliConfig;\n sea?: {\n enabled?: boolean;\n };\n}\n\nconst CONFIG_FILENAMES = [\n // #365 round-3 — `.ts` is checked first so the user's typed config takes\n // priority over a stale `.js` left in the project. `.ts` loading routes\n // through the same esbuild path as the new-shape loader so it works under\n // `\"type\": \"commonjs\"` (Node's `require(.ts)` and `await import(.ts)` are\n // both unreliable here and were the root cause of #365 1.1.0–1.1.2-beta.1).\n 'frontmcp.config.ts',\n 'frontmcp.config.js',\n 'frontmcp.config.json',\n 'frontmcp.config.mjs',\n 'frontmcp.config.cjs',\n];\n\n/**\n * Load frontmcp.config.{ts,js,json,mjs,cjs} from the given directory.\n * Falls back to deriving minimal config from package.json.\n */\nexport async function loadExecConfig(cwd: string): Promise<FrontmcpExecConfig> {\n for (const filename of CONFIG_FILENAMES) {\n const configPath = path.join(cwd, filename);\n if (fs.existsSync(configPath)) {\n if (filename.endsWith('.json')) {\n const content = fs.readFileSync(configPath, 'utf-8');\n return JSON.parse(content) as FrontmcpExecConfig;\n }\n if (filename.endsWith('.ts')) {\n // Delegate to the new loader's esbuild transpile so `.ts` configs\n // work under `\"type\": \"commonjs\"` projects. Hard-fails on parse error\n // (no silent default) — matches frontmcp-config.loader semantics.\n // tsc uses `--moduleResolution nodenext`, so the dynamic import\n // needs an explicit `.js` suffix that resolves to the compiled\n // output. The barrel (`../../../config/index.js`) re-exports\n // `loadFrontMcpConfig` from the loader module.\n const { loadFrontMcpConfig } = await import('../../../config/index.js');\n try {\n // The new-shape loader returns a parsed config — we only consume the\n // legacy-shape fields here. Fields that exist in both shapes\n // (name, version, entry, nodeVersion) carry through; new-shape-only\n // fields like `deployments` are ignored by this consumer.\n const newShape = await loadFrontMcpConfig(cwd);\n return newShape as unknown as FrontmcpExecConfig;\n } catch (err) {\n throw new Error(\n `Failed to load ${filename}: ${(err as Error).message}\\n` +\n `If your config doesn't match the new schema (deployments[] etc.), ` +\n `rename it to .js or use the legacy module.exports shape.`,\n );\n }\n }\n // JS/MJS/CJS config — require it\n\n const mod = require(configPath);\n return (mod.default || mod) as FrontmcpExecConfig;\n }\n }\n\n // Fallback: derive from package.json\n const pkgPath = path.join(cwd, 'package.json');\n if (!fs.existsSync(pkgPath)) {\n throw new Error(\n 'No frontmcp.config.js/json found and no package.json. Create a frontmcp.config.js for build targets.',\n );\n }\n\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));\n return {\n name: pkg.name?.replace(/^@[^/]+\\//, '') || path.basename(cwd),\n version: pkg.version || '1.0.0',\n entry: pkg.main,\n };\n}\n\n/**\n * Validate config and return normalized version.\n */\nexport function normalizeConfig(config: FrontmcpExecConfig): Required<\n Pick<FrontmcpExecConfig, 'name' | 'version' | 'nodeVersion'>\n> &\n FrontmcpExecConfig {\n if (!config.name || !/^[a-zA-Z0-9._-]+$/.test(config.name)) {\n throw new Error(\n `Invalid app name: \"${config.name}\". Must be alphanumeric with .-_ only.`,\n );\n }\n\n return {\n ...config,\n name: config.name,\n version: config.version || '1.0.0',\n nodeVersion: config.nodeVersion || '>=22.0.0',\n };\n}\n"]}
@@ -26,5 +26,6 @@ export declare function buildExec(opts: ParsedArgs & {
26
26
  description?: string;
27
27
  authRequired?: boolean;
28
28
  };
29
+ nodeVersion?: string;
29
30
  };
30
31
  }): Promise<void>;
@@ -49,6 +49,12 @@ async function buildExec(opts) {
49
49
  ...opts.execOverrides.cli,
50
50
  };
51
51
  }
52
+ // #365 round-3 — apply only when the legacy loader didn't already pick up
53
+ // a value (the legacy loader can read it from .js/.json/.cjs/.mjs configs;
54
+ // the new-shape forward fills the .ts gap).
55
+ if (opts.execOverrides.nodeVersion && !rawConfig.nodeVersion) {
56
+ rawConfig.nodeVersion = opts.execOverrides.nodeVersion;
57
+ }
52
58
  }
53
59
  const config = (0, config_1.normalizeConfig)(rawConfig);
54
60
  const cliEnabled = opts.cli || config.cli?.enabled;