frontmcp 1.0.0-beta.1 → 1.0.0-beta.10

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 (71) hide show
  1. package/README.md +2 -2
  2. package/package.json +5 -5
  3. package/src/commands/build/adapters/cloudflare.js +1 -1
  4. package/src/commands/build/adapters/cloudflare.js.map +1 -1
  5. package/src/commands/build/adapters/lambda.js +1 -1
  6. package/src/commands/build/adapters/lambda.js.map +1 -1
  7. package/src/commands/build/adapters/vercel.js +2 -2
  8. package/src/commands/build/adapters/vercel.js.map +1 -1
  9. package/src/commands/build/browser/index.d.ts +17 -0
  10. package/src/commands/build/browser/index.js +68 -0
  11. package/src/commands/build/browser/index.js.map +1 -0
  12. package/src/commands/build/exec/cli-runtime/daemon-client.js +1 -0
  13. package/src/commands/build/exec/cli-runtime/daemon-client.js.map +1 -1
  14. package/src/commands/build/exec/cli-runtime/generate-cli-entry.js +90 -19
  15. package/src/commands/build/exec/cli-runtime/generate-cli-entry.js.map +1 -1
  16. package/src/commands/build/exec/config.d.ts +3 -0
  17. package/src/commands/build/exec/config.js +1 -1
  18. package/src/commands/build/exec/config.js.map +1 -1
  19. package/src/commands/build/exec/esbuild-bundler.js +1 -1
  20. package/src/commands/build/exec/esbuild-bundler.js.map +1 -1
  21. package/src/commands/build/exec/index.d.ts +4 -1
  22. package/src/commands/build/exec/index.js.map +1 -1
  23. package/src/commands/build/exec/installer-script.js +1 -1
  24. package/src/commands/build/exec/installer-script.js.map +1 -1
  25. package/src/commands/build/exec/runner-script.js +3 -3
  26. package/src/commands/build/exec/runner-script.js.map +1 -1
  27. package/src/commands/build/index.d.ts +8 -13
  28. package/src/commands/build/index.js +48 -27
  29. package/src/commands/build/index.js.map +1 -1
  30. package/src/commands/build/register.js +4 -5
  31. package/src/commands/build/register.js.map +1 -1
  32. package/src/commands/build/sdk/index.d.ts +13 -0
  33. package/src/commands/build/sdk/index.js +76 -0
  34. package/src/commands/build/sdk/index.js.map +1 -0
  35. package/src/commands/dev/dev.js +70 -8
  36. package/src/commands/dev/dev.js.map +1 -1
  37. package/src/commands/package/install.js +2 -2
  38. package/src/commands/package/install.js.map +1 -1
  39. package/src/commands/scaffold/create.d.ts +3 -0
  40. package/src/commands/scaffold/create.js +311 -181
  41. package/src/commands/scaffold/create.js.map +1 -1
  42. package/src/commands/scaffold/register.js +2 -0
  43. package/src/commands/scaffold/register.js.map +1 -1
  44. package/src/commands/skills/catalog.d.ts +42 -0
  45. package/src/commands/skills/catalog.js +276 -0
  46. package/src/commands/skills/catalog.js.map +1 -0
  47. package/src/commands/skills/install.d.ts +8 -0
  48. package/src/commands/skills/install.js +83 -0
  49. package/src/commands/skills/install.js.map +1 -0
  50. package/src/commands/skills/list.d.ts +5 -0
  51. package/src/commands/skills/list.js +44 -0
  52. package/src/commands/skills/list.js.map +1 -0
  53. package/src/commands/skills/register.d.ts +2 -0
  54. package/src/commands/skills/register.js +65 -0
  55. package/src/commands/skills/register.js.map +1 -0
  56. package/src/commands/skills/search.d.ts +5 -0
  57. package/src/commands/skills/search.js +24 -0
  58. package/src/commands/skills/search.js.map +1 -0
  59. package/src/commands/skills/show.d.ts +1 -0
  60. package/src/commands/skills/show.js +46 -0
  61. package/src/commands/skills/show.js.map +1 -0
  62. package/src/core/args.d.ts +5 -4
  63. package/src/core/args.js +19 -11
  64. package/src/core/args.js.map +1 -1
  65. package/src/core/bridge.d.ts +1 -4
  66. package/src/core/bridge.js +9 -14
  67. package/src/core/bridge.js.map +1 -1
  68. package/src/core/help.js +8 -3
  69. package/src/core/help.js.map +1 -1
  70. package/src/core/program.js +2 -0
  71. package/src/core/program.js.map +1 -1
package/README.md CHANGED
@@ -39,14 +39,14 @@ export default class Server {}
39
39
 
40
40
  ## Installation
41
41
 
42
- **Node.js 22+** required (24 recommended).
42
+ **Node.js 24+** required.
43
43
 
44
44
  ```bash
45
45
  # New project (recommended)
46
46
  npx frontmcp create my-app
47
47
 
48
48
  # Existing project
49
- npm i -D frontmcp @types/node@^22
49
+ npm i -D frontmcp @types/node@^24
50
50
  npx frontmcp init
51
51
  ```
52
52
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frontmcp",
3
- "version": "1.0.0-beta.1",
3
+ "version": "1.0.0-beta.10",
4
4
  "description": "FrontMCP command line interface",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "homepage": "https://docs.agentfront.dev",
@@ -26,21 +26,21 @@
26
26
  "frontmcp": "./src/core/cli.js"
27
27
  },
28
28
  "engines": {
29
- "node": ">=22.0.0"
29
+ "node": ">=24.0.0"
30
30
  },
31
31
  "dependencies": {
32
32
  "@clack/prompts": "^0.10.0",
33
- "@frontmcp/utils": "1.0.0-beta.1",
33
+ "@frontmcp/utils": "1.0.0-beta.10",
34
34
  "commander": "^13.0.0",
35
35
  "tslib": "^2.3.0",
36
+ "vectoriadb": "^2.1.3",
36
37
  "@rspack/core": "^1.7.6",
37
38
  "esbuild": "^0.27.3"
38
39
  },
39
40
  "devDependencies": {
40
41
  "typescript": "^5.5.3",
41
42
  "tsx": "^4.20.6",
42
- "@types/node": "^24.0.0",
43
- "@modelcontextprotocol/inspector": "^0.21.1"
43
+ "@types/node": "^24.0.0"
44
44
  },
45
45
  "types": "./index.d.js",
46
46
  "type": "commonjs"
@@ -10,7 +10,7 @@ exports.cloudflareAdapter = void 0;
10
10
  exports.cloudflareAdapter = {
11
11
  moduleFormat: 'commonjs',
12
12
  getEntryTemplate: (mainModulePath) => `// Auto-generated Cloudflare Workers entry point
13
- // Generated by: frontmcp build --adapter cloudflare
13
+ // Generated by: frontmcp build --target cloudflare
14
14
  process.env.FRONTMCP_SERVERLESS = '1';
15
15
 
16
16
  require('${mainModulePath}');
@@ -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;;;;WAIrC,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyDxB;IAEC,SAAS,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC;;;CAG9B;IAEC,cAAc,EAAE,eAAe;CAChC,CAAC","sourcesContent":["import { 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 --adapter cloudflare\nprocess.env.FRONTMCP_SERVERLESS = '1';\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":";;;AAEA;;;;;GAKG;AACU,QAAA,iBAAiB,GAAoB;IAChD,YAAY,EAAE,UAAU;IAExB,gBAAgB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC;;;;WAIrC,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyDxB;IAEC,SAAS,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC;;;CAG9B;IAEC,cAAc,EAAE,eAAe;CAChC,CAAC","sourcesContent":["import { 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';\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"]}
@@ -26,7 +26,7 @@ exports.lambdaAdapter = {
26
26
  process.env.FRONTMCP_SERVERLESS = '1';
27
27
  `,
28
28
  getEntryTemplate: (mainModulePath) => `// Auto-generated AWS Lambda entry point
29
- // Generated by: frontmcp build --adapter lambda
29
+ // Generated by: frontmcp build --target lambda
30
30
  //
31
31
  // IMPORTANT: This adapter requires @codegenie/serverless-express
32
32
  // Install it with: npm install @codegenie/serverless-express
@@ -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;;;;CAIzB;IAEC,gBAAgB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC;;;;;;;UAOtC,cAAc;;;;;;;;;;;;;;;;;CAiBvB;IAEC,qEAAqE;CACtE,CAAC","sourcesContent":["import { 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';\n`,\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated AWS Lambda entry point\n// Generated by: frontmcp build --adapter 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":";;;AAEA;;;;;;;;;;;;;;GAcG;AACU,QAAA,aAAa,GAAoB;IAC5C,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,IAAI;IAClB,YAAY,EAAE,aAAa;IAE3B,gBAAgB,EAAE,GAAG,EAAE,CAAC;;;;CAIzB;IAEC,gBAAgB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC;;;;;;;UAOtC,cAAc;;;;;;;;;;;;;;;;;CAiBvB;IAEC,qEAAqE;CACtE,CAAC","sourcesContent":["import { 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';\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"]}
@@ -57,7 +57,7 @@ exports.vercelAdapter = {
57
57
  process.env.FRONTMCP_SERVERLESS = '1';
58
58
  `,
59
59
  getEntryTemplate: (mainModulePath) => `// Auto-generated Vercel entry point
60
- // Generated by: frontmcp build --adapter vercel
60
+ // Generated by: frontmcp build --target vercel
61
61
  import './serverless-setup.js';
62
62
  import '${mainModulePath}';
63
63
  import { getServerlessHandlerAsync } from '@frontmcp/sdk';
@@ -150,7 +150,7 @@ export default async function handler(req, res) {
150
150
  }
151
151
  // Create function config (.vc-config.json)
152
152
  const vcConfig = {
153
- runtime: 'nodejs22.x',
153
+ runtime: 'nodejs24.x',
154
154
  handler: bundleOutput,
155
155
  launcherType: 'Nodejs',
156
156
  };
@@ -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 { 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';\n`,\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated Vercel entry point\n// Generated by: frontmcp build --adapter 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: 'nodejs22.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":";;;;AAAA,mDAA6B;AAC7B,2BAAgC;AAChC,iDAAyC;AACzC,2CAA0F;AAU1F,MAAM,gBAAgB,GAAiD;IACrE,GAAG,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,eAAe,EAAE;IACrD,IAAI,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,gBAAgB,EAAE;IACxD,IAAI,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,YAAY,EAAE;IACpD,GAAG,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,eAAe,EAAE;CACtD,CAAC;AAEF,MAAM,cAAc,GAAmC;IACrD,WAAW,EAAE,KAAK;IAClB,gBAAgB,EAAE,MAAM;IACxB,WAAW,EAAE,MAAM;IACnB,mBAAmB,EAAE,KAAK;CAC3B,CAAC;AAEF;;;GAGG;AACH,SAAS,oBAAoB,CAAC,GAAW;IACvC,KAAK,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;QAC5D,IAAI,IAAA,eAAU,EAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC;YACzC,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,CAAC,mBAAmB;AACnC,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACU,QAAA,aAAa,GAAoB;IAC5C,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,IAAI;IAClB,YAAY,EAAE,aAAa;IAE3B,gBAAgB,EAAE,GAAG,EAAE,CAAC;;;;CAIzB;IAEC,gBAAgB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC;;;UAGtC,cAAc;;;;;;;;;;;;CAYvB;IAEC,8DAA8D;IAC9D,SAAS,EAAE,CAAC,GAAW,EAAE,EAAE;QACzB,MAAM,EAAE,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,MAAM,GAAG,gBAAgB,CAAC,EAAE,CAAC,CAAC;QACpC,OAAO;YACL,OAAO,EAAE,CAAC;YACV,YAAY,EAAE,MAAM,CAAC,GAAG;YACxB,cAAc,EAAE,MAAM,CAAC,OAAO;SAC/B,CAAC;IACJ,CAAC;IAED,cAAc,EAAE,aAAa;IAE7B;;;;;;;;;;;;OAYG;IACH,UAAU,EAAE,KAAK,EAAE,MAAc,EAAE,GAAW,EAAE,YAAoB,EAAE,EAAE;QACtE,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QACtD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;QAEhE,qBAAqB;QACrB,MAAM,IAAA,aAAK,EAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAE1C,qDAAqD;QACrD,gEAAgE;QAChE,MAAM,SAAS,GAAG,MAAM,IAAA,eAAO,EAAC,MAAM,CAAC,CAAC;QACxC,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YAC1C,MAAM,QAAQ,GAAG,MAAM,IAAA,YAAI,EAAC,OAAO,CAAC,CAAC;YAErC,IAAI,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC;gBAC3B,+BAA+B;gBAC/B,MAAM,IAAA,UAAE,EAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACnD,CAAC;iBAAM,CAAC;gBACN,aAAa;gBACb,MAAM,IAAA,gBAAQ,EAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YACpC,CAAC;QACH,CAAC;QAED,mFAAmF;QACnF,qFAAqF;QACrF,mFAAmF;QACnF,MAAM,WAAW,GAAG,CAAC,YAAY,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;QAE3D,8CAA8C;QAC9C,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QACnD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,IAAA,gBAAQ,EAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;QACjE,MAAM,OAAO,GAAG,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;QAExE,mCAAmC;QACnC,MAAM,aAAa,GAAa,EAAE,CAAC;QACnC,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;YAC9B,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;YAC7B,IAAI,OAAO,EAAE,CAAC;gBACZ,aAAa,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,OAAO,EAAE,CAAC,CAAC;YAC1C,CAAC;QACH,CAAC;QAED,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,4CAA4C;YAC5C,MAAM,WAAW,GAAG,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC;YAC5E,MAAM,IAAA,iBAAS,EAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAE1F,0DAA0D;YAC1D,IAAI,CAAC;gBACH,IAAA,wBAAQ,EAAC,eAAe,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE;oBAC5D,GAAG,EAAE,OAAO;oBACZ,KAAK,EAAE,MAAM;iBACd,CAAC,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACP,mEAAmE;YACrE,CAAC;QACH,CAAC;QAED,2CAA2C;QAC3C,MAAM,QAAQ,GAAG;YACf,OAAO,EAAE,YAAY;YACrB,OAAO,EAAE,YAAY;YACrB,YAAY,EAAE,QAAQ;SACvB,CAAC;QACF,MAAM,IAAA,iBAAS,EACb,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,iBAAiB,CAAC,EACrC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAClC,CAAC;QAEF,kDAAkD;QAClD,MAAM,YAAY,GAAG;YACnB,OAAO,EAAE,CAAC;YACV,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;SAC3C,CAAC;QACF,MAAM,IAAA,iBAAS,EACb,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,EACnC,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,CACtC,CAAC;IACJ,CAAC;CACF,CAAC","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 { 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';\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"]}
@@ -0,0 +1,17 @@
1
+ import { ParsedArgs } from '../../../core/args';
2
+ /**
3
+ * Build a browser-compatible ESM bundle.
4
+ *
5
+ * Uses esbuild with `platform: 'browser'` which resolves conditional imports
6
+ * (`#imports` in package.json) to browser implementations automatically:
7
+ * - Crypto → @noble/hashes + @noble/ciphers (no node:crypto)
8
+ * - AsyncLocalStorage → stack-based polyfill
9
+ * - EventEmitter → Map-based polyfill
10
+ * - SSE/Express/Stdio → stubs that throw on instantiation
11
+ *
12
+ * @example
13
+ * ```bash
14
+ * frontmcp build --target browser
15
+ * ```
16
+ */
17
+ export declare function buildBrowser(opts: ParsedArgs): Promise<void>;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildBrowser = buildBrowser;
4
+ const tslib_1 = require("tslib");
5
+ const path = tslib_1.__importStar(require("path"));
6
+ const colors_1 = require("../../../core/colors");
7
+ const utils_1 = require("@frontmcp/utils");
8
+ const fs_1 = require("../../../shared/fs");
9
+ /**
10
+ * Build a browser-compatible ESM bundle.
11
+ *
12
+ * Uses esbuild with `platform: 'browser'` which resolves conditional imports
13
+ * (`#imports` in package.json) to browser implementations automatically:
14
+ * - Crypto → @noble/hashes + @noble/ciphers (no node:crypto)
15
+ * - AsyncLocalStorage → stack-based polyfill
16
+ * - EventEmitter → Map-based polyfill
17
+ * - SSE/Express/Stdio → stubs that throw on instantiation
18
+ *
19
+ * @example
20
+ * ```bash
21
+ * frontmcp build --target browser
22
+ * ```
23
+ */
24
+ async function buildBrowser(opts) {
25
+ const cwd = process.cwd();
26
+ const entry = await (0, fs_1.resolveEntry)(cwd, opts.entry);
27
+ const outDir = path.resolve(cwd, opts.outDir || 'dist');
28
+ await (0, utils_1.ensureDir)(outDir);
29
+ const pkg = require(path.join(cwd, 'package.json'));
30
+ const appName = pkg.name || path.basename(cwd);
31
+ console.log(`${(0, colors_1.c)('cyan', '[build:browser]')} entry: ${path.relative(cwd, entry)}`);
32
+ console.log(`${(0, colors_1.c)('cyan', '[build:browser]')} outDir: ${path.relative(cwd, outDir)}`);
33
+ const esbuild = await import('esbuild');
34
+ // Build ESM bundle for browsers
35
+ console.log((0, colors_1.c)('cyan', '[build:browser] Bundling ESM for browser...'));
36
+ await esbuild.build({
37
+ entryPoints: [entry],
38
+ bundle: true,
39
+ platform: 'browser',
40
+ format: 'esm',
41
+ target: 'es2022',
42
+ outfile: path.join(outDir, `${appName}.browser.mjs`),
43
+ keepNames: true,
44
+ treeShaking: true,
45
+ sourcemap: true,
46
+ // Browser-safe: keep React and peer deps external (user's bundler handles them)
47
+ external: [
48
+ 'react', 'react-dom', 'react-router', 'react-router-dom',
49
+ // Keep @frontmcp/* external — user installs them
50
+ '@frontmcp/sdk', '@frontmcp/di', '@frontmcp/utils',
51
+ '@frontmcp/auth', '@frontmcp/react',
52
+ 'reflect-metadata',
53
+ // Node.js-only — these should be tree-shaken by platform: 'browser'
54
+ // but list explicitly to avoid accidental bundling
55
+ 'better-sqlite3', 'fsevents', 'ioredis',
56
+ ...Object.keys(pkg.peerDependencies || {}),
57
+ ],
58
+ // Resolve conditional imports to browser variants
59
+ conditions: ['browser', 'import', 'default'],
60
+ define: {
61
+ 'process.env.NODE_ENV': '"production"',
62
+ },
63
+ });
64
+ console.log((0, colors_1.c)('green', `\n Browser build complete:`));
65
+ console.log((0, colors_1.c)('gray', ` ESM: ${appName}.browser.mjs`));
66
+ console.log((0, colors_1.c)('gray', ` Source map: ${appName}.browser.mjs.map`));
67
+ }
68
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/commands/build/browser/index.ts"],"names":[],"mappings":";;AAqBA,oCAgDC;;AArED,mDAA6B;AAE7B,iDAAyC;AACzC,2CAA4C;AAC5C,2CAAkD;AAElD;;;;;;;;;;;;;;GAcG;AACI,KAAK,UAAU,YAAY,CAAC,IAAgB;IACjD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,MAAM,KAAK,GAAG,MAAM,IAAA,iBAAY,EAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,CAAC;IACxD,MAAM,IAAA,iBAAS,EAAC,MAAM,CAAC,CAAC;IAExB,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC;IACpD,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAE/C,OAAO,CAAC,GAAG,CAAC,GAAG,IAAA,UAAC,EAAC,MAAM,EAAE,iBAAiB,CAAC,WAAW,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;IACnF,OAAO,CAAC,GAAG,CAAC,GAAG,IAAA,UAAC,EAAC,MAAM,EAAE,iBAAiB,CAAC,YAAY,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IAErF,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;IAExC,gCAAgC;IAChC,OAAO,CAAC,GAAG,CAAC,IAAA,UAAC,EAAC,MAAM,EAAE,6CAA6C,CAAC,CAAC,CAAC;IACtE,MAAM,OAAO,CAAC,KAAK,CAAC;QAClB,WAAW,EAAE,CAAC,KAAK,CAAC;QACpB,MAAM,EAAE,IAAI;QACZ,QAAQ,EAAE,SAAS;QACnB,MAAM,EAAE,KAAK;QACb,MAAM,EAAE,QAAQ;QAChB,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,OAAO,cAAc,CAAC;QACpD,SAAS,EAAE,IAAI;QACf,WAAW,EAAE,IAAI;QACjB,SAAS,EAAE,IAAI;QACf,gFAAgF;QAChF,QAAQ,EAAE;YACR,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,kBAAkB;YACxD,iDAAiD;YACjD,eAAe,EAAE,cAAc,EAAE,iBAAiB;YAClD,gBAAgB,EAAE,iBAAiB;YACnC,kBAAkB;YAClB,oEAAoE;YACpE,mDAAmD;YACnD,gBAAgB,EAAE,UAAU,EAAE,SAAS;YACvC,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,gBAAgB,IAAI,EAAE,CAAC;SAC3C;QACD,kDAAkD;QAClD,UAAU,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC;QAC5C,MAAM,EAAE;YACN,sBAAsB,EAAE,cAAc;SACvC;KACF,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,CAAC,IAAA,UAAC,EAAC,OAAO,EAAE,6BAA6B,CAAC,CAAC,CAAC;IACvD,OAAO,CAAC,GAAG,CAAC,IAAA,UAAC,EAAC,MAAM,EAAE,UAAU,OAAO,cAAc,CAAC,CAAC,CAAC;IACxD,OAAO,CAAC,GAAG,CAAC,IAAA,UAAC,EAAC,MAAM,EAAE,iBAAiB,OAAO,kBAAkB,CAAC,CAAC,CAAC;AACrE,CAAC","sourcesContent":["import * as path from 'path';\nimport { ParsedArgs } from '../../../core/args';\nimport { c } from '../../../core/colors';\nimport { ensureDir } from '@frontmcp/utils';\nimport { resolveEntry } from '../../../shared/fs';\n\n/**\n * Build a browser-compatible ESM bundle.\n *\n * Uses esbuild with `platform: 'browser'` which resolves conditional imports\n * (`#imports` in package.json) to browser implementations automatically:\n * - Crypto → @noble/hashes + @noble/ciphers (no node:crypto)\n * - AsyncLocalStorage → stack-based polyfill\n * - EventEmitter → Map-based polyfill\n * - SSE/Express/Stdio → stubs that throw on instantiation\n *\n * @example\n * ```bash\n * frontmcp build --target browser\n * ```\n */\nexport async function buildBrowser(opts: ParsedArgs): Promise<void> {\n const cwd = process.cwd();\n const entry = await resolveEntry(cwd, opts.entry);\n const outDir = path.resolve(cwd, opts.outDir || 'dist');\n await ensureDir(outDir);\n\n const pkg = require(path.join(cwd, 'package.json'));\n const appName = pkg.name || path.basename(cwd);\n\n console.log(`${c('cyan', '[build:browser]')} entry: ${path.relative(cwd, entry)}`);\n console.log(`${c('cyan', '[build:browser]')} outDir: ${path.relative(cwd, outDir)}`);\n\n const esbuild = await import('esbuild');\n\n // Build ESM bundle for browsers\n console.log(c('cyan', '[build:browser] Bundling ESM for browser...'));\n await esbuild.build({\n entryPoints: [entry],\n bundle: true,\n platform: 'browser',\n format: 'esm',\n target: 'es2022',\n outfile: path.join(outDir, `${appName}.browser.mjs`),\n keepNames: true,\n treeShaking: true,\n sourcemap: true,\n // Browser-safe: keep React and peer deps external (user's bundler handles them)\n external: [\n 'react', 'react-dom', 'react-router', 'react-router-dom',\n // Keep @frontmcp/* external — user installs them\n '@frontmcp/sdk', '@frontmcp/di', '@frontmcp/utils',\n '@frontmcp/auth', '@frontmcp/react',\n 'reflect-metadata',\n // Node.js-only — these should be tree-shaken by platform: 'browser'\n // but list explicitly to avoid accidental bundling\n 'better-sqlite3', 'fsevents', 'ioredis',\n ...Object.keys(pkg.peerDependencies || {}),\n ],\n // Resolve conditional imports to browser variants\n conditions: ['browser', 'import', 'default'],\n define: {\n 'process.env.NODE_ENV': '\"production\"',\n },\n });\n\n console.log(c('green', `\\n Browser build complete:`));\n console.log(c('gray', ` ESM: ${appName}.browser.mjs`));\n console.log(c('gray', ` Source map: ${appName}.browser.mjs.map`));\n}\n"]}
@@ -92,6 +92,7 @@ function createDaemonClient(socketPath) {
92
92
  }
93
93
 
94
94
  return {
95
+ _isDaemon: true,
95
96
  ping: function() {
96
97
  return call('ping');
97
98
  },
@@ -1 +1 @@
1
- {"version":3,"file":"daemon-client.js","sourceRoot":"","sources":["../../../../../../src/commands/build/exec/cli-runtime/daemon-client.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;AAQH,gEAoJC;AA1JD,0DAA0D;AAE1D;;;GAGG;AACH,SAAgB,0BAA0B;IACxC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkJR,CAAC;AACF,CAAC","sourcesContent":["/**\n * Lightweight daemon client for CLI exec.\n *\n * Sends MCP JSON-RPC requests over a Unix socket using Node.js built-in http module.\n * This avoids the full FrontMCP SDK initialization (~420ms) by talking to an\n * already-running daemon process.\n *\n * NOTE: This file is used as a CJS runtime module — it gets bundled into the CLI\n * output by esbuild alongside the generated CLI entry. It must remain free of\n * TypeScript-only constructs at runtime (the .ts extension is for build-time only).\n */\n\n/* eslint-disable @typescript-eslint/no-require-imports */\n\n/**\n * Generate the daemon-client JavaScript source code (CJS module).\n * This is embedded into the CLI bundle at build time.\n */\nexport function generateDaemonClientSource(): string {\n return `'use strict';\n\nvar http = require('http');\n\n/**\n * Send a JSON-RPC request over a Unix socket.\n * @param {string} socketPath - Path to the Unix socket file.\n * @param {string} method - JSON-RPC method name.\n * @param {object} [params] - Method parameters.\n * @returns {Promise<object>} Parsed JSON-RPC result.\n */\nfunction rpcCall(socketPath, method, params) {\n return new Promise(function(resolve, reject) {\n var body = JSON.stringify({\n jsonrpc: '2.0',\n id: Date.now(),\n method: method,\n params: params || {}\n });\n\n var req = http.request({\n socketPath: socketPath,\n path: '/mcp',\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Content-Length': Buffer.byteLength(body)\n },\n timeout: 10000\n }, function(res) {\n var chunks = [];\n res.on('data', function(chunk) { chunks.push(chunk); });\n res.on('end', function() {\n try {\n var json = JSON.parse(Buffer.concat(chunks).toString());\n if (json.error) {\n var err = new Error(json.error.message || 'RPC error');\n err.code = json.error.code;\n err.data = json.error.data;\n reject(err);\n } else {\n resolve(json.result);\n }\n } catch (e) {\n reject(new Error('Invalid JSON response from daemon'));\n }\n });\n });\n\n req.on('error', function(err) {\n reject(err);\n });\n\n req.on('timeout', function() {\n req.destroy();\n reject(new Error('Daemon request timed out'));\n });\n\n req.write(body);\n req.end();\n });\n}\n\n/**\n * Create a daemon client that implements the same interface as DirectClient.\n * @param {string} socketPath - Path to the Unix socket file.\n * @returns {object} Client object with MCP methods.\n */\nfunction createDaemonClient(socketPath) {\n function call(method, params) {\n return rpcCall(socketPath, method, params);\n }\n\n return {\n ping: function() {\n return call('ping');\n },\n callTool: function(name, args) {\n return call('tools/call', { name: name, arguments: args || {} });\n },\n listTools: function() {\n return call('tools/list');\n },\n listResources: function() {\n return call('resources/list');\n },\n readResource: function(uri) {\n return call('resources/read', { uri: uri });\n },\n listResourceTemplates: function() {\n return call('resources/templates/list');\n },\n listPrompts: function() {\n return call('prompts/list');\n },\n getPrompt: function(name, args) {\n return call('prompts/get', { name: name, arguments: args || {} });\n },\n searchSkills: function(query) {\n return call('skills/search', { query: query || '' });\n },\n loadSkills: function(ids) {\n return call('skills/load', { ids: ids });\n },\n listSkills: function() {\n return call('skills/list');\n },\n listJobs: function() {\n return call('jobs/list');\n },\n executeJob: function(name, input, opts) {\n return call('jobs/execute', { name: name, input: input, options: opts });\n },\n getJobStatus: function(runId) {\n return call('jobs/status', { runId: runId });\n },\n listWorkflows: function() {\n return call('workflows/list');\n },\n executeWorkflow: function(name, input, opts) {\n return call('workflows/execute', { name: name, input: input, options: opts });\n },\n getWorkflowStatus: function(runId) {\n return call('workflows/status', { runId: runId });\n },\n subscribeResource: function(uri) {\n return call('resources/subscribe', { uri: uri });\n },\n unsubscribeResource: function(uri) {\n return call('resources/unsubscribe', { uri: uri });\n },\n onResourceUpdated: function() {\n console.warn('Resource subscriptions are not supported in daemon mode (HTTP-based, no push).');\n return function() {};\n },\n onNotification: function() {\n console.warn('Notifications are not supported in daemon mode (HTTP-based, no push).');\n return function() {};\n },\n close: function() {\n return Promise.resolve();\n }\n };\n}\n\nexports.createDaemonClient = createDaemonClient;\n`;\n}\n"]}
1
+ {"version":3,"file":"daemon-client.js","sourceRoot":"","sources":["../../../../../../src/commands/build/exec/cli-runtime/daemon-client.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;AAQH,gEAqJC;AA3JD,0DAA0D;AAE1D;;;GAGG;AACH,SAAgB,0BAA0B;IACxC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmJR,CAAC;AACF,CAAC","sourcesContent":["/**\n * Lightweight daemon client for CLI exec.\n *\n * Sends MCP JSON-RPC requests over a Unix socket using Node.js built-in http module.\n * This avoids the full FrontMCP SDK initialization (~420ms) by talking to an\n * already-running daemon process.\n *\n * NOTE: This file is used as a CJS runtime module — it gets bundled into the CLI\n * output by esbuild alongside the generated CLI entry. It must remain free of\n * TypeScript-only constructs at runtime (the .ts extension is for build-time only).\n */\n\n/* eslint-disable @typescript-eslint/no-require-imports */\n\n/**\n * Generate the daemon-client JavaScript source code (CJS module).\n * This is embedded into the CLI bundle at build time.\n */\nexport function generateDaemonClientSource(): string {\n return `'use strict';\n\nvar http = require('http');\n\n/**\n * Send a JSON-RPC request over a Unix socket.\n * @param {string} socketPath - Path to the Unix socket file.\n * @param {string} method - JSON-RPC method name.\n * @param {object} [params] - Method parameters.\n * @returns {Promise<object>} Parsed JSON-RPC result.\n */\nfunction rpcCall(socketPath, method, params) {\n return new Promise(function(resolve, reject) {\n var body = JSON.stringify({\n jsonrpc: '2.0',\n id: Date.now(),\n method: method,\n params: params || {}\n });\n\n var req = http.request({\n socketPath: socketPath,\n path: '/mcp',\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Content-Length': Buffer.byteLength(body)\n },\n timeout: 10000\n }, function(res) {\n var chunks = [];\n res.on('data', function(chunk) { chunks.push(chunk); });\n res.on('end', function() {\n try {\n var json = JSON.parse(Buffer.concat(chunks).toString());\n if (json.error) {\n var err = new Error(json.error.message || 'RPC error');\n err.code = json.error.code;\n err.data = json.error.data;\n reject(err);\n } else {\n resolve(json.result);\n }\n } catch (e) {\n reject(new Error('Invalid JSON response from daemon'));\n }\n });\n });\n\n req.on('error', function(err) {\n reject(err);\n });\n\n req.on('timeout', function() {\n req.destroy();\n reject(new Error('Daemon request timed out'));\n });\n\n req.write(body);\n req.end();\n });\n}\n\n/**\n * Create a daemon client that implements the same interface as DirectClient.\n * @param {string} socketPath - Path to the Unix socket file.\n * @returns {object} Client object with MCP methods.\n */\nfunction createDaemonClient(socketPath) {\n function call(method, params) {\n return rpcCall(socketPath, method, params);\n }\n\n return {\n _isDaemon: true,\n ping: function() {\n return call('ping');\n },\n callTool: function(name, args) {\n return call('tools/call', { name: name, arguments: args || {} });\n },\n listTools: function() {\n return call('tools/list');\n },\n listResources: function() {\n return call('resources/list');\n },\n readResource: function(uri) {\n return call('resources/read', { uri: uri });\n },\n listResourceTemplates: function() {\n return call('resources/templates/list');\n },\n listPrompts: function() {\n return call('prompts/list');\n },\n getPrompt: function(name, args) {\n return call('prompts/get', { name: name, arguments: args || {} });\n },\n searchSkills: function(query) {\n return call('skills/search', { query: query || '' });\n },\n loadSkills: function(ids) {\n return call('skills/load', { ids: ids });\n },\n listSkills: function() {\n return call('skills/list');\n },\n listJobs: function() {\n return call('jobs/list');\n },\n executeJob: function(name, input, opts) {\n return call('jobs/execute', { name: name, input: input, options: opts });\n },\n getJobStatus: function(runId) {\n return call('jobs/status', { runId: runId });\n },\n listWorkflows: function() {\n return call('workflows/list');\n },\n executeWorkflow: function(name, input, opts) {\n return call('workflows/execute', { name: name, input: input, options: opts });\n },\n getWorkflowStatus: function(runId) {\n return call('workflows/status', { runId: runId });\n },\n subscribeResource: function(uri) {\n return call('resources/subscribe', { uri: uri });\n },\n unsubscribeResource: function(uri) {\n return call('resources/unsubscribe', { uri: uri });\n },\n onResourceUpdated: function() {\n console.warn('Resource subscriptions are not supported in daemon mode (HTTP-based, no push).');\n return function() {};\n },\n onNotification: function() {\n console.warn('Notifications are not supported in daemon mode (HTTP-based, no push).');\n return function() {};\n },\n close: function() {\n return Promise.resolve();\n }\n };\n}\n\nexports.createDaemonClient = createDaemonClient;\n`;\n}\n"]}
@@ -50,10 +50,10 @@ function generateCliEntry(options) {
50
50
  generateLogoutCommand(appName),
51
51
  generateSessionCommands(),
52
52
  ] : []),
53
- generateServeCommand(serverBundleFilename),
53
+ generateServeCommand(serverBundleFilename, selfContained),
54
54
  generateDoctorCommand(appName, options.nativeDeps),
55
55
  generateInstallCommand(appName, options.nativeDeps, selfContained),
56
- generateDaemonCommands(appName, serverBundleFilename),
56
+ generateDaemonCommands(appName, serverBundleFilename, selfContained),
57
57
  generateFooter(),
58
58
  ];
59
59
  return sections.filter(Boolean).join('\n\n');
@@ -81,7 +81,24 @@ function generateHeader(appName, appVersion, description, serverBundleFilename,
81
81
  groupEntries.push(` 'Subscriptions': []`);
82
82
  groupEntries.push(` 'System': []`);
83
83
  return `'use strict';
84
-
84
+ ${selfContained ? `
85
+ // SEA daemon mode: when spawned by 'daemon start', run the server directly
86
+ // using the inlined (bundled) server code — no external requires needed.
87
+ if (process.env.__FRONTMCP_DAEMON_MODE === '1') {
88
+ require('reflect-metadata');
89
+ var _dMod = require(${JSON.stringify('../' + serverBundleFilename)});
90
+ var _dSdk = require('@frontmcp/sdk');
91
+ var _FMI = _dSdk.FrontMcpInstance || _dSdk.default.FrontMcpInstance;
92
+ var _raw = _dMod.default || _dMod;
93
+ var _cfg = (typeof _raw === 'function' && typeof Reflect !== 'undefined' && Reflect.getMetadata)
94
+ ? (Reflect.getMetadata('__frontmcp:config', _raw) || _raw) : _raw;
95
+ var _sp = process.env.FRONTMCP_DAEMON_SOCKET;
96
+ _FMI.runUnixSocket(Object.assign({}, _cfg, { socketPath: _sp }))
97
+ .then(function() { console.log('Daemon listening on ' + _sp); })
98
+ .catch(function(e) { console.error('Daemon failed:', e); process.exit(1); });
99
+ return;
100
+ }
101
+ ` : ''}
85
102
  var { Command, Option } = require('commander');
86
103
  var path = require('path');
87
104
  var fs = require('fs');
@@ -762,14 +779,33 @@ workflowCmd
762
779
  });`;
763
780
  }
764
781
  function generateSubscribeCommands() {
765
- return `var subscribeCmd = program.command('subscribe').description('Subscribe to updates');
782
+ return `
783
+ // Subscribe commands need push support (onNotification/onResourceUpdated).
784
+ // Daemon HTTP cannot push, so we force in-process when daemon was used.
785
+ async function getSubscribeClient() {
786
+ var client = await getClient();
787
+ // If connected via daemon, the onNotification/onResourceUpdated are no-ops.
788
+ // Reconnect via in-process for push support.
789
+ if (client._isDaemon) {
790
+ _client = null; // clear cached daemon client
791
+ var mod = require(SERVER_BUNDLE);
792
+ var configOrClass = mod.default || mod;
793
+ var sdk = require('@frontmcp/sdk');
794
+ var connect = sdk.connect || sdk.direct.connect;
795
+ _client = await connect(configOrClass, { mode: 'cli' });
796
+ return _client;
797
+ }
798
+ return client;
799
+ }
800
+
801
+ var subscribeCmd = program.command('subscribe').description('Subscribe to updates');
766
802
 
767
803
  subscribeCmd
768
804
  .command('resource <uri>')
769
805
  .description('Stream resource updates (Ctrl+C to stop)')
770
806
  .action(async function(uri) {
771
807
  try {
772
- var client = await getClient();
808
+ var client = await getSubscribeClient();
773
809
  await client.subscribeResource(uri);
774
810
  var mode = program.opts().output || 'text';
775
811
  console.log('Subscribed to resource: ' + uri);
@@ -782,7 +818,9 @@ subscribeCmd
782
818
  try { await client.unsubscribeResource(uri); } catch (_) { /* ok */ }
783
819
  process.exit(0);
784
820
  });
785
- // Keep process alive
821
+ // Keep process alive — setInterval creates an active event loop handle
822
+ // so Node.js won't exit even with InMemoryTransport (no persistent I/O)
823
+ setInterval(function() {}, 2147483647);
786
824
  await new Promise(function() {});
787
825
  } catch (err) {
788
826
  console.error('Error:', err.message || err);
@@ -795,7 +833,7 @@ subscribeCmd
795
833
  .description('Stream notifications (Ctrl+C to stop)')
796
834
  .action(async function(name) {
797
835
  try {
798
- var client = await getClient();
836
+ var client = await getSubscribeClient();
799
837
  var mode = program.opts().output || 'text';
800
838
  console.log('Listening for notification: ' + name);
801
839
  console.log('Waiting for events... (Ctrl+C to stop)\\n');
@@ -808,7 +846,9 @@ subscribeCmd
808
846
  console.log('\\nStopping...');
809
847
  process.exit(0);
810
848
  });
811
- // Keep process alive
849
+ // Keep process alive — setInterval creates an active event loop handle
850
+ // so Node.js won't exit even with InMemoryTransport (no persistent I/O)
851
+ setInterval(function() {}, 2147483647);
812
852
  await new Promise(function() {});
813
853
  } catch (err) {
814
854
  console.error('Error:', err.message || err);
@@ -933,17 +973,30 @@ program
933
973
  console.log('Credentials stored for session: ' + sessionName);
934
974
  });`;
935
975
  }
936
- function generateServeCommand(serverBundleFilename) {
976
+ function generateServeCommand(serverBundleFilename, selfContained) {
977
+ // In self-contained/SEA mode, use a static relative require that esbuild can resolve at bundle time.
978
+ // In normal mode, use dynamic path.join for runtime resolution from disk.
979
+ const requireExpr = selfContained
980
+ ? `require(${JSON.stringify('../' + serverBundleFilename)})`
981
+ : `require(path.join(SCRIPT_DIR, ${JSON.stringify(serverBundleFilename)}))`;
937
982
  return `program
938
983
  .command('serve')
939
984
  .description('Start the HTTP/SSE server')
940
985
  .option('-p, --port <port>', 'Port number', function(v) { return parseInt(v, 10); })
941
986
  .action(async function(opts) {
942
- var mod = require(path.join(SCRIPT_DIR, ${JSON.stringify(serverBundleFilename)}));
987
+ var mod = ${requireExpr};
943
988
  if (opts.port) process.env.PORT = String(opts.port);
944
- // The server bundle should self-start when required
945
- if (typeof mod.start === 'function') await mod.start();
946
- else if (typeof mod.default?.start === 'function') await mod.default.start();
989
+ // If the bundle exports a start() function (@FrontMcp-decorated class auto-bootstraps), use it
990
+ if (typeof mod.start === 'function') { await mod.start(); return; }
991
+ if (typeof mod.default?.start === 'function') { await mod.default.start(); return; }
992
+ // Otherwise, bootstrap the plain config object via FrontMcpInstance
993
+ var raw = mod.default || mod;
994
+ var sdk = require('@frontmcp/sdk');
995
+ var FrontMcpInstance = sdk.FrontMcpInstance || sdk.default.FrontMcpInstance;
996
+ var config = (typeof raw === 'function' && typeof Reflect !== 'undefined' && Reflect.getMetadata)
997
+ ? (Reflect.getMetadata('__frontmcp:config', raw) || raw) : raw;
998
+ if (opts.port) config = Object.assign({}, config, { http: Object.assign({}, config.http || {}, { port: opts.port }) });
999
+ await FrontMcpInstance.bootstrap(config);
947
1000
  });`;
948
1001
  }
949
1002
  function generateDoctorCommand(appName, nativeDeps) {
@@ -1015,6 +1068,10 @@ ${checks.join(',\n')}
1015
1068
  } else {
1016
1069
  console.log(' [!!] App directory not found: ' + appDir);
1017
1070
  ok = false;
1071
+ if (opts.fix) {
1072
+ fs.mkdirSync(appDir, { recursive: true });
1073
+ console.log(' [fixed] Created ' + appDir);
1074
+ }
1018
1075
  }
1019
1076
 
1020
1077
  if (ok) console.log('\\nAll checks passed.');
@@ -1129,7 +1186,7 @@ program
1129
1186
  console.log('Uninstalled ${appName}.');
1130
1187
  });`;
1131
1188
  }
1132
- function generateDaemonCommands(appName, serverBundleFilename) {
1189
+ function generateDaemonCommands(appName, serverBundleFilename, selfContained) {
1133
1190
  return `var daemonCmd = program.command('daemon').description('Daemon management');
1134
1191
 
1135
1192
  daemonCmd
@@ -1169,13 +1226,23 @@ daemonCmd
1169
1226
  var out = fs.openSync(logPath, 'a');
1170
1227
  var err = fs.openSync(logPath, 'a');
1171
1228
 
1172
- // Start the daemon using runUnixSocket via a small wrapper script
1229
+ ${selfContained ? ` // SEA mode: spawn the binary itself in daemon mode all code is inlined
1230
+ env.__FRONTMCP_DAEMON_MODE = '1';
1231
+ var child = spawn(process.execPath, [], {
1232
+ detached: true,
1233
+ stdio: ['ignore', out, err],
1234
+ env: env
1235
+ });` : ` // Start the daemon using runUnixSocket via a small wrapper script
1173
1236
  // Always use absolute path for the server bundle (SCRIPT_DIR resolves to __dirname at runtime)
1174
1237
  var serverBundlePath = pathMod.join(SCRIPT_DIR, ${JSON.stringify(serverBundleFilename)});
1175
- var daemonScript = 'var mod = require(' + JSON.stringify(serverBundlePath) + ');' +
1238
+ var daemonScript = 'require("reflect-metadata");' +
1239
+ 'var mod = require(' + JSON.stringify(serverBundlePath) + ');' +
1176
1240
  'var sdk = require("@frontmcp/sdk");' +
1177
1241
  'var FrontMcpInstance = sdk.FrontMcpInstance || sdk.default.FrontMcpInstance;' +
1178
- 'var config = mod.default || mod;' +
1242
+ 'var raw = mod.default || mod;' +
1243
+ ${'// If the export is a @FrontMcp-decorated class, extract config via Reflect metadata'}
1244
+ 'var config = (typeof raw === "function" && typeof Reflect !== "undefined" && Reflect.getMetadata) ' +
1245
+ ' ? (Reflect.getMetadata("__frontmcp:config", raw) || raw) : raw;' +
1179
1246
  'FrontMcpInstance.runUnixSocket(Object.assign({}, config, { socketPath: ' + JSON.stringify(socketPath) + ' }))' +
1180
1247
  '.then(function() { console.log("Daemon listening on " + ' + JSON.stringify(socketPath) + '); })' +
1181
1248
  '.catch(function(e) { console.error("Daemon failed:", e); process.exit(1); });';
@@ -1184,7 +1251,7 @@ daemonCmd
1184
1251
  detached: true,
1185
1252
  stdio: ['ignore', out, err],
1186
1253
  env: env
1187
- });
1254
+ });`}
1188
1255
 
1189
1256
  fs.writeFileSync(pidPath, JSON.stringify({
1190
1257
  pid: child.pid,
@@ -1264,7 +1331,11 @@ daemonCmd
1264
1331
  });`;
1265
1332
  }
1266
1333
  function generateFooter() {
1267
- return `program.parseAsync(process.argv).catch(function(err) {
1334
+ return `program.on('command:*', function(args) {
1335
+ console.error('Unknown command: ' + args[0]);
1336
+ process.exitCode = 1;
1337
+ });
1338
+ program.parseAsync(process.argv).catch(function(err) {
1268
1339
  console.error('Fatal:', err.message || err);
1269
1340
  process.exit(1);
1270
1341
  });`;