frontmcp 1.0.3 → 1.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +10 -4
- package/src/commands/build/adapters/cloudflare.d.ts +1 -1
- package/src/commands/build/adapters/cloudflare.js +1 -0
- package/src/commands/build/adapters/cloudflare.js.map +1 -1
- package/src/commands/build/adapters/distributed.d.ts +15 -0
- package/src/commands/build/adapters/distributed.js +30 -0
- package/src/commands/build/adapters/distributed.js.map +1 -0
- package/src/commands/build/adapters/index.d.ts +3 -2
- package/src/commands/build/adapters/index.js +4 -1
- package/src/commands/build/adapters/index.js.map +1 -1
- package/src/commands/build/adapters/lambda.d.ts +1 -1
- package/src/commands/build/adapters/lambda.js +1 -0
- package/src/commands/build/adapters/lambda.js.map +1 -1
- package/src/commands/build/adapters/vercel.d.ts +1 -1
- package/src/commands/build/adapters/vercel.js +1 -0
- package/src/commands/build/adapters/vercel.js.map +1 -1
- package/src/commands/build/exec/cli-runtime/generate-cli-entry.js +122 -34
- package/src/commands/build/exec/cli-runtime/generate-cli-entry.js.map +1 -1
- package/src/commands/build/exec/config.d.ts +2 -2
- package/src/commands/build/exec/config.js.map +1 -1
- package/src/commands/build/exec/esbuild-bundler.d.ts +1 -1
- package/src/commands/build/exec/esbuild-bundler.js +1 -1
- package/src/commands/build/exec/esbuild-bundler.js.map +1 -1
- package/src/commands/build/exec/index.d.ts +1 -1
- package/src/commands/build/exec/index.js +5 -38
- package/src/commands/build/exec/index.js.map +1 -1
- package/src/commands/build/exec/skill-assets.d.ts +27 -0
- package/src/commands/build/exec/skill-assets.js +60 -0
- package/src/commands/build/exec/skill-assets.js.map +1 -0
- package/src/commands/build/index.d.ts +1 -1
- package/src/commands/build/index.js +44 -5
- package/src/commands/build/index.js.map +1 -1
- package/src/commands/build/mcpb/binary.d.ts +37 -0
- package/src/commands/build/mcpb/binary.js +72 -0
- package/src/commands/build/mcpb/binary.js.map +1 -0
- package/src/commands/build/mcpb/constants.d.ts +21 -0
- package/src/commands/build/mcpb/constants.js +31 -0
- package/src/commands/build/mcpb/constants.js.map +1 -0
- package/src/commands/build/mcpb/index.d.ts +20 -0
- package/src/commands/build/mcpb/index.js +241 -0
- package/src/commands/build/mcpb/index.js.map +1 -0
- package/src/commands/build/mcpb/manifest.d.ts +183 -0
- package/src/commands/build/mcpb/manifest.js +252 -0
- package/src/commands/build/mcpb/manifest.js.map +1 -0
- package/src/commands/build/mcpb/stage.d.ts +50 -0
- package/src/commands/build/mcpb/stage.js +94 -0
- package/src/commands/build/mcpb/stage.js.map +1 -0
- package/src/commands/build/mcpb/user-config.d.ts +26 -0
- package/src/commands/build/mcpb/user-config.js +147 -0
- package/src/commands/build/mcpb/user-config.js.map +1 -0
- package/src/commands/build/mcpb/validate.d.ts +27 -0
- package/src/commands/build/mcpb/validate.js +218 -0
- package/src/commands/build/mcpb/validate.js.map +1 -0
- package/src/commands/build/mcpb/zip.d.ts +37 -0
- package/src/commands/build/mcpb/zip.js +85 -0
- package/src/commands/build/mcpb/zip.js.map +1 -0
- package/src/commands/build/register.d.ts +1 -1
- package/src/commands/build/register.js +7 -1
- package/src/commands/build/register.js.map +1 -1
- package/src/commands/build/sdk/index.d.ts +1 -1
- package/src/commands/build/sdk/index.js +1 -1
- package/src/commands/build/sdk/index.js.map +1 -1
- package/src/commands/build/types.d.ts +1 -1
- package/src/commands/build/types.js.map +1 -1
- package/src/commands/mcpb/register.d.ts +2 -0
- package/src/commands/mcpb/register.js +14 -0
- package/src/commands/mcpb/register.js.map +1 -0
- package/src/commands/mcpb/validate.d.ts +1 -0
- package/src/commands/mcpb/validate.js +28 -0
- package/src/commands/mcpb/validate.js.map +1 -0
- package/src/commands/scaffold/create.js +5 -7
- package/src/commands/scaffold/create.js.map +1 -1
- package/src/config/define-config.d.ts +26 -0
- package/src/config/define-config.js +31 -0
- package/src/config/define-config.js.map +1 -0
- package/src/config/frontmcp-config.loader.d.ts +32 -0
- package/src/config/frontmcp-config.loader.js +113 -0
- package/src/config/frontmcp-config.loader.js.map +1 -0
- package/src/config/frontmcp-config.schema.d.ts +1062 -0
- package/src/config/frontmcp-config.schema.js +313 -0
- package/src/config/frontmcp-config.schema.js.map +1 -0
- package/src/config/frontmcp-config.types.d.ts +287 -0
- package/src/config/frontmcp-config.types.js +14 -0
- package/src/config/frontmcp-config.types.js.map +1 -0
- package/src/config/index.d.ts +5 -0
- package/src/config/index.js +13 -0
- package/src/config/index.js.map +1 -0
- package/src/core/args.d.ts +7 -2
- package/src/core/args.js +12 -2
- package/src/core/args.js.map +1 -1
- package/src/core/bridge.d.ts +1 -1
- package/src/core/bridge.js +10 -0
- package/src/core/bridge.js.map +1 -1
- package/src/core/cli.js +3 -1
- package/src/core/cli.js.map +1 -1
- package/src/core/program.js +15 -13
- package/src/core/program.js.map +1 -1
- package/src/index.d.ts +2 -0
- package/src/index.js +7 -0
- package/src/index.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "frontmcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.1.0-beta.1",
|
|
4
4
|
"description": "FrontMCP command line interface",
|
|
5
5
|
"author": "AgentFront <info@agentfront.dev>",
|
|
6
6
|
"homepage": "https://docs.agentfront.dev",
|
|
@@ -30,17 +30,23 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@clack/prompts": "^0.10.0",
|
|
33
|
-
"@frontmcp/
|
|
33
|
+
"@frontmcp/lazy-zod": "1.1.0-beta.1",
|
|
34
|
+
"@frontmcp/utils": "1.1.0-beta.1",
|
|
35
|
+
"@frontmcp/skills": "1.1.0-beta.1",
|
|
34
36
|
"commander": "^13.0.0",
|
|
35
37
|
"tslib": "^2.3.0",
|
|
36
38
|
"vectoriadb": "^2.2.0",
|
|
37
39
|
"@rspack/core": "^1.7.6",
|
|
38
|
-
"esbuild": "^0.27.3"
|
|
40
|
+
"esbuild": "^0.27.3",
|
|
41
|
+
"yauzl": "^3.2.0",
|
|
42
|
+
"yazl": "^3.3.1"
|
|
39
43
|
},
|
|
40
44
|
"devDependencies": {
|
|
41
45
|
"typescript": "^5.5.3",
|
|
42
46
|
"tsx": "^4.20.6",
|
|
43
|
-
"@types/node": "^24.0.0"
|
|
47
|
+
"@types/node": "^24.0.0",
|
|
48
|
+
"@types/yauzl": "^2.10.3",
|
|
49
|
+
"@types/yazl": "^2.4.5"
|
|
44
50
|
},
|
|
45
51
|
"types": "./index.d.js",
|
|
46
52
|
"type": "commonjs"
|
|
@@ -12,6 +12,7 @@ exports.cloudflareAdapter = {
|
|
|
12
12
|
getEntryTemplate: (mainModulePath) => `// Auto-generated Cloudflare Workers entry point
|
|
13
13
|
// Generated by: frontmcp build --target cloudflare
|
|
14
14
|
process.env.FRONTMCP_SERVERLESS = '1';
|
|
15
|
+
process.env.FRONTMCP_DEPLOYMENT_MODE = 'serverless';
|
|
15
16
|
|
|
16
17
|
require('${mainModulePath}');
|
|
17
18
|
const { getServerlessHandlerAsync } = require('@frontmcp/sdk');
|
|
@@ -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
|
|
1
|
+
{"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/cloudflare.ts"],"names":[],"mappings":";;;AAEA;;;;;GAKG;AACU,QAAA,iBAAiB,GAAoB;IAChD,YAAY,EAAE,UAAU;IAExB,gBAAgB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC;;;;;WAKrC,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyDxB;IAEC,SAAS,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC;;;CAG9B;IAEC,cAAc,EAAE,eAAe;CAChC,CAAC","sourcesContent":["import type { AdapterTemplate } from '../types';\n\n/**\n * Cloudflare Workers adapter - edge deployment on Cloudflare.\n * Compiles to CommonJS and adapts the Express app to Cloudflare's fetch API.\n *\n * @see https://developers.cloudflare.com/workers/\n */\nexport const cloudflareAdapter: AdapterTemplate = {\n moduleFormat: 'commonjs',\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated Cloudflare Workers entry point\n// Generated by: frontmcp build --target cloudflare\nprocess.env.FRONTMCP_SERVERLESS = '1';\nprocess.env.FRONTMCP_DEPLOYMENT_MODE = 'serverless';\n\nrequire('${mainModulePath}');\nconst { getServerlessHandlerAsync } = require('@frontmcp/sdk');\n\nlet app = null;\n\nasync function handleRequest(request) {\n if (!app) {\n app = await getServerlessHandlerAsync();\n }\n\n // Convert Cloudflare Request to Node.js format\n const url = new URL(request.url);\n const req = {\n method: request.method,\n url: url.pathname + url.search,\n headers: Object.fromEntries(request.headers),\n body: request.body,\n };\n\n return new Promise((resolve) => {\n const res = {\n statusCode: 200,\n headers: {},\n body: '',\n status(code) { this.statusCode = code; return this; },\n setHeader(key, value) { this.headers[key] = value; },\n json(data) {\n this.headers['Content-Type'] = 'application/json';\n this.body = JSON.stringify(data);\n resolve(new Response(this.body, {\n status: this.statusCode,\n headers: this.headers,\n }));\n },\n send(data) {\n this.body = data;\n resolve(new Response(this.body, {\n status: this.statusCode,\n headers: this.headers,\n }));\n },\n end() {\n resolve(new Response(this.body, {\n status: this.statusCode,\n headers: this.headers,\n }));\n },\n };\n app(req, res);\n });\n}\n\nmodule.exports = {\n async fetch(request, env, ctx) {\n return handleRequest(request);\n }\n};\n`,\n\n getConfig: (_cwd: string) => `name = \"frontmcp-worker\"\nmain = \"dist/index.js\"\ncompatibility_date = \"2024-01-01\"\n`,\n\n configFileName: 'wrangler.toml',\n};\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AdapterTemplate } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Distributed adapter - multi-pod Node.js deployment with HA.
|
|
4
|
+
* Compiles to CommonJS with an HA setup file that sets FRONTMCP_DEPLOYMENT_MODE.
|
|
5
|
+
*
|
|
6
|
+
* The setup file runs before any imports to ensure the deployment mode
|
|
7
|
+
* is detected before @FrontMcp decorators execute. This enables:
|
|
8
|
+
* - Session heartbeat and takeover
|
|
9
|
+
* - Cross-pod notification relay via Redis pub/sub
|
|
10
|
+
* - LB affinity via __frontmcp_node cookie
|
|
11
|
+
* - Machine ID from K8s HOSTNAME or os.hostname()
|
|
12
|
+
*
|
|
13
|
+
* Requires Redis configuration in @FrontMcp() for session persistence.
|
|
14
|
+
*/
|
|
15
|
+
export declare const distributedAdapter: AdapterTemplate;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.distributedAdapter = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Distributed adapter - multi-pod Node.js deployment with HA.
|
|
6
|
+
* Compiles to CommonJS with an HA setup file that sets FRONTMCP_DEPLOYMENT_MODE.
|
|
7
|
+
*
|
|
8
|
+
* The setup file runs before any imports to ensure the deployment mode
|
|
9
|
+
* is detected before @FrontMcp decorators execute. This enables:
|
|
10
|
+
* - Session heartbeat and takeover
|
|
11
|
+
* - Cross-pod notification relay via Redis pub/sub
|
|
12
|
+
* - LB affinity via __frontmcp_node cookie
|
|
13
|
+
* - Machine ID from K8s HOSTNAME or os.hostname()
|
|
14
|
+
*
|
|
15
|
+
* Requires Redis configuration in @FrontMcp() for session persistence.
|
|
16
|
+
*/
|
|
17
|
+
exports.distributedAdapter = {
|
|
18
|
+
moduleFormat: 'commonjs',
|
|
19
|
+
getSetupTemplate: () => `// Distributed HA setup - MUST be imported first
|
|
20
|
+
// Sets FRONTMCP_DEPLOYMENT_MODE before any decorators run
|
|
21
|
+
// Enables heartbeat, session takeover, and notification relay
|
|
22
|
+
process.env.FRONTMCP_DEPLOYMENT_MODE = 'distributed';
|
|
23
|
+
`,
|
|
24
|
+
getEntryTemplate: (mainModulePath) => `// Auto-generated distributed entry point
|
|
25
|
+
// Generated by: frontmcp build --target distributed
|
|
26
|
+
require('./ha-setup.js');
|
|
27
|
+
require('${mainModulePath}');
|
|
28
|
+
`,
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=distributed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"distributed.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/distributed.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;GAYG;AACU,QAAA,kBAAkB,GAAoB;IACjD,YAAY,EAAE,UAAU;IAExB,gBAAgB,EAAE,GAAG,EAAE,CAAC;;;;CAIzB;IAEC,gBAAgB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC;;;WAGrC,cAAc;CACxB;CACA,CAAC","sourcesContent":["import type { AdapterTemplate } from '../types';\n\n/**\n * Distributed adapter - multi-pod Node.js deployment with HA.\n * Compiles to CommonJS with an HA setup file that sets FRONTMCP_DEPLOYMENT_MODE.\n *\n * The setup file runs before any imports to ensure the deployment mode\n * is detected before @FrontMcp decorators execute. This enables:\n * - Session heartbeat and takeover\n * - Cross-pod notification relay via Redis pub/sub\n * - LB affinity via __frontmcp_node cookie\n * - Machine ID from K8s HOSTNAME or os.hostname()\n *\n * Requires Redis configuration in @FrontMcp() for session persistence.\n */\nexport const distributedAdapter: AdapterTemplate = {\n moduleFormat: 'commonjs',\n\n getSetupTemplate: () => `// Distributed HA setup - MUST be imported first\n// Sets FRONTMCP_DEPLOYMENT_MODE before any decorators run\n// Enables heartbeat, session takeover, and notification relay\nprocess.env.FRONTMCP_DEPLOYMENT_MODE = 'distributed';\n`,\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated distributed entry point\n// Generated by: frontmcp build --target distributed\nrequire('./ha-setup.js');\nrequire('${mainModulePath}');\n`,\n};\n"]}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { AdapterTemplate, AdapterName } from '../types';
|
|
1
|
+
import type { AdapterTemplate, AdapterName } from '../types';
|
|
2
2
|
import { nodeAdapter } from './node';
|
|
3
3
|
import { vercelAdapter } from './vercel';
|
|
4
4
|
import { lambdaAdapter } from './lambda';
|
|
5
5
|
import { cloudflareAdapter } from './cloudflare';
|
|
6
|
+
import { distributedAdapter } from './distributed';
|
|
6
7
|
/**
|
|
7
8
|
* Registry of all available deployment adapters.
|
|
8
9
|
* Each adapter configures how the FrontMCP server is compiled and packaged
|
|
9
10
|
* for a specific deployment target.
|
|
10
11
|
*/
|
|
11
12
|
export declare const ADAPTERS: Record<AdapterName, AdapterTemplate>;
|
|
12
|
-
export { nodeAdapter, vercelAdapter, lambdaAdapter, cloudflareAdapter };
|
|
13
|
+
export { nodeAdapter, vercelAdapter, lambdaAdapter, cloudflareAdapter, distributedAdapter };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.cloudflareAdapter = exports.lambdaAdapter = exports.vercelAdapter = exports.nodeAdapter = exports.ADAPTERS = void 0;
|
|
3
|
+
exports.distributedAdapter = exports.cloudflareAdapter = exports.lambdaAdapter = exports.vercelAdapter = exports.nodeAdapter = exports.ADAPTERS = void 0;
|
|
4
4
|
const node_1 = require("./node");
|
|
5
5
|
Object.defineProperty(exports, "nodeAdapter", { enumerable: true, get: function () { return node_1.nodeAdapter; } });
|
|
6
6
|
const vercel_1 = require("./vercel");
|
|
@@ -9,6 +9,8 @@ const lambda_1 = require("./lambda");
|
|
|
9
9
|
Object.defineProperty(exports, "lambdaAdapter", { enumerable: true, get: function () { return lambda_1.lambdaAdapter; } });
|
|
10
10
|
const cloudflare_1 = require("./cloudflare");
|
|
11
11
|
Object.defineProperty(exports, "cloudflareAdapter", { enumerable: true, get: function () { return cloudflare_1.cloudflareAdapter; } });
|
|
12
|
+
const distributed_1 = require("./distributed");
|
|
13
|
+
Object.defineProperty(exports, "distributedAdapter", { enumerable: true, get: function () { return distributed_1.distributedAdapter; } });
|
|
12
14
|
/**
|
|
13
15
|
* Registry of all available deployment adapters.
|
|
14
16
|
* Each adapter configures how the FrontMCP server is compiled and packaged
|
|
@@ -19,5 +21,6 @@ exports.ADAPTERS = {
|
|
|
19
21
|
vercel: vercel_1.vercelAdapter,
|
|
20
22
|
lambda: lambda_1.lambdaAdapter,
|
|
21
23
|
cloudflare: cloudflare_1.cloudflareAdapter,
|
|
24
|
+
distributed: distributed_1.distributedAdapter,
|
|
22
25
|
};
|
|
23
26
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/index.ts"],"names":[],"mappings":";;;AACA,iCAAqC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/index.ts"],"names":[],"mappings":";;;AACA,iCAAqC;AAmB5B,4FAnBA,kBAAW,OAmBA;AAlBpB,qCAAyC;AAkBnB,8FAlBb,sBAAa,OAkBa;AAjBnC,qCAAyC;AAiBJ,8FAjB5B,sBAAa,OAiB4B;AAhBlD,6CAAiD;AAgBG,kGAhB3C,8BAAiB,OAgB2C;AAfrE,+CAAmD;AAeoB,mGAf9D,gCAAkB,OAe8D;AAbzF;;;;GAIG;AACU,QAAA,QAAQ,GAAyC;IAC5D,IAAI,EAAE,kBAAW;IACjB,MAAM,EAAE,sBAAa;IACrB,MAAM,EAAE,sBAAa;IACrB,UAAU,EAAE,8BAAiB;IAC7B,WAAW,EAAE,gCAAkB;CAChC,CAAC","sourcesContent":["import type { AdapterTemplate, AdapterName } from '../types';\nimport { nodeAdapter } from './node';\nimport { vercelAdapter } from './vercel';\nimport { lambdaAdapter } from './lambda';\nimport { cloudflareAdapter } from './cloudflare';\nimport { distributedAdapter } from './distributed';\n\n/**\n * Registry of all available deployment adapters.\n * Each adapter configures how the FrontMCP server is compiled and packaged\n * for a specific deployment target.\n */\nexport const ADAPTERS: Record<AdapterName, AdapterTemplate> = {\n node: nodeAdapter,\n vercel: vercelAdapter,\n lambda: lambdaAdapter,\n cloudflare: cloudflareAdapter,\n distributed: distributedAdapter,\n};\n\nexport { nodeAdapter, vercelAdapter, lambdaAdapter, cloudflareAdapter, distributedAdapter };\n"]}
|
|
@@ -24,6 +24,7 @@ exports.lambdaAdapter = {
|
|
|
24
24
|
// This sets FRONTMCP_SERVERLESS before any decorators run
|
|
25
25
|
// Required because ESM hoists imports before other statements
|
|
26
26
|
process.env.FRONTMCP_SERVERLESS = '1';
|
|
27
|
+
process.env.FRONTMCP_DEPLOYMENT_MODE = 'serverless';
|
|
27
28
|
`,
|
|
28
29
|
getEntryTemplate: (mainModulePath) => `// Auto-generated AWS Lambda entry point
|
|
29
30
|
// Generated by: frontmcp build --target lambda
|
|
@@ -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
|
|
1
|
+
{"version":3,"file":"lambda.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/lambda.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;GAcG;AACU,QAAA,aAAa,GAAoB;IAC5C,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,IAAI;IAClB,YAAY,EAAE,aAAa;IAE3B,gBAAgB,EAAE,GAAG,EAAE,CAAC;;;;;CAKzB;IAEC,gBAAgB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC;;;;;;;UAOtC,cAAc;;;;;;;;;;;;;;;;;CAiBvB;IAEC,qEAAqE;CACtE,CAAC","sourcesContent":["import type { AdapterTemplate } from '../types';\n\n/**\n * AWS Lambda adapter - serverless deployment on AWS Lambda.\n * Compiles to ESM, bundles with rspack to CJS for maximum compatibility.\n *\n * Prerequisites:\n * npm install @codegenie/serverless-express\n *\n * The build process:\n * 1. TypeScript compiles to ESM in dist/\n * 2. serverless-setup.js is generated (sets FRONTMCP_SERVERLESS=1)\n * 3. index.js imports setup first, then main module\n * 4. rspack bundles everything into handler.cjs\n *\n * @see https://github.com/codegenie/serverless-express\n */\nexport const lambdaAdapter: AdapterTemplate = {\n moduleFormat: 'esnext',\n shouldBundle: true,\n bundleOutput: 'handler.cjs',\n\n getSetupTemplate: () => `// Serverless environment setup - MUST be imported first\n// This sets FRONTMCP_SERVERLESS before any decorators run\n// Required because ESM hoists imports before other statements\nprocess.env.FRONTMCP_SERVERLESS = '1';\nprocess.env.FRONTMCP_DEPLOYMENT_MODE = 'serverless';\n`,\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated AWS Lambda entry point\n// Generated by: frontmcp build --target lambda\n//\n// IMPORTANT: This adapter requires @codegenie/serverless-express\n// Install it with: npm install @codegenie/serverless-express\n//\nimport './serverless-setup.js';\nimport '${mainModulePath}';\nimport { getServerlessHandlerAsync } from '@frontmcp/sdk';\nimport serverlessExpress from '@codegenie/serverless-express';\n\nlet serverlessExpressInstance = null;\n\nasync function setup() {\n const app = await getServerlessHandlerAsync();\n serverlessExpressInstance = serverlessExpress({ app });\n}\n\nexport const handler = async (event, context) => {\n if (!serverlessExpressInstance) {\n await setup();\n }\n return serverlessExpressInstance(event, context);\n};\n`,\n\n // No config file - user manages serverless.yml, SAM template, or CDK\n};\n"]}
|
|
@@ -55,6 +55,7 @@ exports.vercelAdapter = {
|
|
|
55
55
|
// This sets FRONTMCP_SERVERLESS before any decorators run
|
|
56
56
|
// Required because ESM hoists imports before other statements
|
|
57
57
|
process.env.FRONTMCP_SERVERLESS = '1';
|
|
58
|
+
process.env.FRONTMCP_DEPLOYMENT_MODE = 'serverless';
|
|
58
59
|
`,
|
|
59
60
|
getEntryTemplate: (mainModulePath) => `// Auto-generated Vercel entry point
|
|
60
61
|
// Generated by: frontmcp build --target vercel
|
|
@@ -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 --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":";;;;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;;;;;CAKzB;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 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"]}
|
|
@@ -86,19 +86,52 @@ ${selfContained ? `
|
|
|
86
86
|
// using the inlined (bundled) server code — no external requires needed.
|
|
87
87
|
if (process.env.__FRONTMCP_DAEMON_MODE === '1') {
|
|
88
88
|
require('reflect-metadata');
|
|
89
|
+
// Suppress @FrontMcp decorator auto-bootstrap — daemon handles bootstrap explicitly below.
|
|
90
|
+
process.env.FRONTMCP_SCHEMA_EXTRACT = '1';
|
|
89
91
|
var _dMod = require(${JSON.stringify('../' + serverBundleFilename)});
|
|
92
|
+
delete process.env.FRONTMCP_SCHEMA_EXTRACT;
|
|
90
93
|
var _dSdk = require('@frontmcp/sdk');
|
|
91
94
|
var _FMI = _dSdk.FrontMcpInstance || _dSdk.default.FrontMcpInstance;
|
|
92
95
|
var _raw = _dMod.default || _dMod;
|
|
93
96
|
var _cfg = (typeof _raw === 'function' && typeof Reflect !== 'undefined' && Reflect.getMetadata)
|
|
94
97
|
? (Reflect.getMetadata('__frontmcp:config', _raw) || _raw) : _raw;
|
|
95
|
-
var
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
.
|
|
98
|
+
var _dp = process.env.FRONTMCP_DAEMON_PORT;
|
|
99
|
+
if (_dp) {
|
|
100
|
+
var _port = parseInt(_dp, 10);
|
|
101
|
+
_cfg = Object.assign({}, _cfg, { http: Object.assign({}, _cfg.http || {}, { port: _port }) });
|
|
102
|
+
process.env.PORT = _dp;
|
|
103
|
+
_FMI.bootstrap(_cfg)
|
|
104
|
+
.then(function() { console.log('Daemon listening on port ' + _port); })
|
|
105
|
+
.catch(function(e) { console.error('Daemon failed:', e); process.exit(1); });
|
|
106
|
+
} else {
|
|
107
|
+
var _sp = process.env.FRONTMCP_DAEMON_SOCKET;
|
|
108
|
+
_FMI.runUnixSocket(Object.assign({}, _cfg, { socketPath: _sp }))
|
|
109
|
+
.then(function() { console.log('Daemon listening on ' + _sp); })
|
|
110
|
+
.catch(function(e) { console.error('Daemon failed:', e); process.exit(1); });
|
|
111
|
+
}
|
|
99
112
|
return;
|
|
100
113
|
}
|
|
101
114
|
` : ''}
|
|
115
|
+
// Stdio mode: when --stdio is passed, run as an MCP stdio server (stdin/stdout JSON-RPC).
|
|
116
|
+
// Detected early before commander to avoid overhead and ensure stdout stays clean for MCP protocol.
|
|
117
|
+
// Logs go to ~/.frontmcp/logs/{appName}-*.log — stdout is reserved for MCP JSON-RPC only.
|
|
118
|
+
if (process.argv.includes('--stdio')) {
|
|
119
|
+
// Set app name for file logging before any initialization
|
|
120
|
+
process.env.FRONTMCP_APP_NAME = process.env.FRONTMCP_APP_NAME || ${JSON.stringify(appName)};
|
|
121
|
+
require('reflect-metadata');
|
|
122
|
+
process.env.FRONTMCP_SCHEMA_EXTRACT = '1';
|
|
123
|
+
var _sMod = require(${selfContained ? `${JSON.stringify('../' + serverBundleFilename)}` : `require('path').join(__dirname, ${JSON.stringify(serverBundleFilename)})`});
|
|
124
|
+
delete process.env.FRONTMCP_SCHEMA_EXTRACT;
|
|
125
|
+
var _sSdk = require('@frontmcp/sdk');
|
|
126
|
+
var _sFMI = _sSdk.FrontMcpInstance || _sSdk.default.FrontMcpInstance;
|
|
127
|
+
var _sRaw = _sMod.default || _sMod;
|
|
128
|
+
var _sCfg = (typeof _sRaw === 'function' && typeof Reflect !== 'undefined' && Reflect.getMetadata)
|
|
129
|
+
? (Reflect.getMetadata('__frontmcp:config', _sRaw) || _sRaw) : _sRaw;
|
|
130
|
+
_sFMI.runStdio(_sCfg)
|
|
131
|
+
.catch(function(e) { console.error('Stdio server failed:', e); process.exit(1); });
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
102
135
|
var { Command, Option } = require('commander');
|
|
103
136
|
var path = require('path');
|
|
104
137
|
var fs = require('fs');
|
|
@@ -1059,11 +1092,31 @@ function generateServeCommand(serverBundleFilename, selfContained) {
|
|
|
1059
1092
|
: `require(path.join(SCRIPT_DIR, ${JSON.stringify(serverBundleFilename)}))`;
|
|
1060
1093
|
return `program
|
|
1061
1094
|
.command('serve')
|
|
1062
|
-
.description('Start the
|
|
1095
|
+
.description('Start the MCP server')
|
|
1063
1096
|
.option('-p, --port <port>', 'Port number', function(v) { return parseInt(v, 10); })
|
|
1097
|
+
.option('--stdio', 'Run as stdio transport (stdin/stdout JSON-RPC) for use in .mcp.json')
|
|
1064
1098
|
.action(async function(opts) {
|
|
1099
|
+
if (opts.stdio) {
|
|
1100
|
+
// --stdio on serve is handled by the early argv check at the top of the entry;
|
|
1101
|
+
// this branch is a fallback in case commander parsed it first.
|
|
1102
|
+
_isLongRunning = true;
|
|
1103
|
+
process.env.FRONTMCP_SCHEMA_EXTRACT = '1';
|
|
1104
|
+
var sMod = ${requireExpr};
|
|
1105
|
+
delete process.env.FRONTMCP_SCHEMA_EXTRACT;
|
|
1106
|
+
var sRaw = sMod.default || sMod;
|
|
1107
|
+
var sSdk = require('@frontmcp/sdk');
|
|
1108
|
+
var sFMI = sSdk.FrontMcpInstance || sSdk.default.FrontMcpInstance;
|
|
1109
|
+
var sCfg = (typeof sRaw === 'function' && typeof Reflect !== 'undefined' && Reflect.getMetadata)
|
|
1110
|
+
? (Reflect.getMetadata('__frontmcp:config', sRaw) || sRaw) : sRaw;
|
|
1111
|
+
await sFMI.runStdio(sCfg);
|
|
1112
|
+
return;
|
|
1113
|
+
}
|
|
1065
1114
|
_isLongRunning = true;
|
|
1115
|
+
// Suppress @FrontMcp decorator auto-bootstrap during require() — the CLI
|
|
1116
|
+
// serve command handles bootstrap explicitly below with port/config overrides.
|
|
1117
|
+
process.env.FRONTMCP_SCHEMA_EXTRACT = '1';
|
|
1066
1118
|
var mod = ${requireExpr};
|
|
1119
|
+
delete process.env.FRONTMCP_SCHEMA_EXTRACT;
|
|
1067
1120
|
if (opts.port) process.env.PORT = String(opts.port);
|
|
1068
1121
|
// If the bundle exports a start() function (@FrontMcp-decorated class auto-bootstraps), use it
|
|
1069
1122
|
if (typeof mod.start === 'function') { await mod.start(); return; }
|
|
@@ -1277,7 +1330,8 @@ function generateDaemonCommands(appName, serverBundleFilename, selfContained) {
|
|
|
1277
1330
|
|
|
1278
1331
|
daemonCmd
|
|
1279
1332
|
.command('start')
|
|
1280
|
-
.description('Start as a background daemon
|
|
1333
|
+
.description('Start as a background daemon')
|
|
1334
|
+
.option('-p, --port <port>', 'Listen on a TCP port instead of a Unix socket', function(v) { return parseInt(v, 10); })
|
|
1281
1335
|
.option('--idle-timeout <ms>', 'Auto-stop after idle period (ms, 0 to disable)', function(v) { return parseInt(v, 10); }, 300000)
|
|
1282
1336
|
.action(async function(opts) {
|
|
1283
1337
|
var { spawn } = require('child_process');
|
|
@@ -1289,10 +1343,11 @@ daemonCmd
|
|
|
1289
1343
|
fs.mkdirSync(logDir, { recursive: true });
|
|
1290
1344
|
fs.mkdirSync(socketDir, { recursive: true });
|
|
1291
1345
|
|
|
1346
|
+
var usePort = !!opts.port;
|
|
1292
1347
|
var socketPath = pathMod.join(socketDir, ${JSON.stringify(appName)} + '.sock');
|
|
1293
1348
|
|
|
1294
|
-
// Clean up stale socket file
|
|
1295
|
-
try { fs.unlinkSync(socketPath); } catch (_) { /* ok */ }
|
|
1349
|
+
// Clean up stale socket file (only relevant in socket mode)
|
|
1350
|
+
if (!usePort) { try { fs.unlinkSync(socketPath); } catch (_) { /* ok */ } }
|
|
1296
1351
|
|
|
1297
1352
|
// Check if already running
|
|
1298
1353
|
var pidPath = pathMod.join(pidDir, ${JSON.stringify(appName)} + '.pid');
|
|
@@ -1304,9 +1359,13 @@ daemonCmd
|
|
|
1304
1359
|
} catch (_) { /* not running, proceed */ }
|
|
1305
1360
|
|
|
1306
1361
|
var env = Object.assign({}, process.env, {
|
|
1307
|
-
FRONTMCP_DAEMON_SOCKET: socketPath,
|
|
1308
1362
|
FRONTMCP_DAEMON_IDLE_TIMEOUT: String(opts.idleTimeout)
|
|
1309
1363
|
});
|
|
1364
|
+
if (usePort) {
|
|
1365
|
+
env.FRONTMCP_DAEMON_PORT = String(opts.port);
|
|
1366
|
+
} else {
|
|
1367
|
+
env.FRONTMCP_DAEMON_SOCKET = socketPath;
|
|
1368
|
+
}
|
|
1310
1369
|
|
|
1311
1370
|
var logPath = pathMod.join(logDir, ${JSON.stringify(appName)} + '.log');
|
|
1312
1371
|
var out = fs.openSync(logPath, 'a');
|
|
@@ -1318,20 +1377,33 @@ ${selfContained ? ` // SEA mode: spawn the binary itself in daemon mode — a
|
|
|
1318
1377
|
detached: true,
|
|
1319
1378
|
stdio: ['ignore', out, err],
|
|
1320
1379
|
env: env
|
|
1321
|
-
});` : ` // Start the daemon
|
|
1380
|
+
});` : ` // Start the daemon via a small wrapper script
|
|
1322
1381
|
// Always use absolute path for the server bundle (SCRIPT_DIR resolves to __dirname at runtime)
|
|
1323
1382
|
var serverBundlePath = pathMod.join(SCRIPT_DIR, ${JSON.stringify(serverBundleFilename)});
|
|
1324
1383
|
var daemonScript = 'require("reflect-metadata");' +
|
|
1384
|
+
'process.env.FRONTMCP_SCHEMA_EXTRACT="1";' +
|
|
1325
1385
|
'var mod = require(' + JSON.stringify(serverBundlePath) + ');' +
|
|
1386
|
+
'delete process.env.FRONTMCP_SCHEMA_EXTRACT;' +
|
|
1326
1387
|
'var sdk = require("@frontmcp/sdk");' +
|
|
1327
1388
|
'var FrontMcpInstance = sdk.FrontMcpInstance || sdk.default.FrontMcpInstance;' +
|
|
1328
1389
|
'var raw = mod.default || mod;' +
|
|
1329
1390
|
${'// If the export is a @FrontMcp-decorated class, extract config via Reflect metadata'}
|
|
1330
1391
|
'var config = (typeof raw === "function" && typeof Reflect !== "undefined" && Reflect.getMetadata) ' +
|
|
1331
|
-
' ? (Reflect.getMetadata("__frontmcp:config", raw) || raw) : raw;'
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1392
|
+
' ? (Reflect.getMetadata("__frontmcp:config", raw) || raw) : raw;';
|
|
1393
|
+
|
|
1394
|
+
if (usePort) {
|
|
1395
|
+
daemonScript +=
|
|
1396
|
+
'config = Object.assign({}, config, { http: Object.assign({}, config.http || {}, { port: ' + opts.port + ' }) });' +
|
|
1397
|
+
'process.env.PORT = ' + JSON.stringify(String(opts.port)) + ';' +
|
|
1398
|
+
'FrontMcpInstance.bootstrap(config)' +
|
|
1399
|
+
'.then(function() { console.log("Daemon listening on port ' + opts.port + '"); })' +
|
|
1400
|
+
'.catch(function(e) { console.error("Daemon failed:", e); process.exit(1); });';
|
|
1401
|
+
} else {
|
|
1402
|
+
daemonScript +=
|
|
1403
|
+
'FrontMcpInstance.runUnixSocket(Object.assign({}, config, { socketPath: ' + JSON.stringify(socketPath) + ' }))' +
|
|
1404
|
+
'.then(function() { console.log("Daemon listening on " + ' + JSON.stringify(socketPath) + '); })' +
|
|
1405
|
+
'.catch(function(e) { console.error("Daemon failed:", e); process.exit(1); });';
|
|
1406
|
+
}
|
|
1335
1407
|
|
|
1336
1408
|
var child = spawn('node', ['-e', daemonScript], {
|
|
1337
1409
|
detached: true,
|
|
@@ -1343,26 +1415,40 @@ ${selfContained ? ` // SEA mode: spawn the binary itself in daemon mode — a
|
|
|
1343
1415
|
fs.closeSync(out);
|
|
1344
1416
|
fs.closeSync(err);
|
|
1345
1417
|
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
child.unref();
|
|
1352
|
-
|
|
1353
|
-
// Wait for socket file to appear (max 5s)
|
|
1354
|
-
var waited = 0;
|
|
1355
|
-
while (!fs.existsSync(socketPath) && waited < 5000) {
|
|
1356
|
-
await new Promise(function(r) { setTimeout(r, 100); });
|
|
1357
|
-
waited += 100;
|
|
1418
|
+
var pidData = { pid: child.pid, startedAt: new Date().toISOString() };
|
|
1419
|
+
if (usePort) {
|
|
1420
|
+
pidData.port = opts.port;
|
|
1421
|
+
} else {
|
|
1422
|
+
pidData.socketPath = socketPath;
|
|
1358
1423
|
}
|
|
1424
|
+
fs.writeFileSync(pidPath, JSON.stringify(pidData));
|
|
1425
|
+
child.unref();
|
|
1359
1426
|
|
|
1360
|
-
if (
|
|
1361
|
-
|
|
1362
|
-
|
|
1427
|
+
if (usePort) {
|
|
1428
|
+
// For port mode, wait briefly then check if process is still alive
|
|
1429
|
+
await new Promise(function(r) { setTimeout(r, 1000); });
|
|
1430
|
+
try {
|
|
1431
|
+
process.kill(child.pid, 0);
|
|
1432
|
+
console.log('Daemon started (PID: ' + child.pid + '). Port: ' + opts.port);
|
|
1433
|
+
console.log('Logs: ' + logPath);
|
|
1434
|
+
} catch (_) {
|
|
1435
|
+
console.log('Daemon failed to start. Check logs: ' + logPath);
|
|
1436
|
+
}
|
|
1363
1437
|
} else {
|
|
1364
|
-
|
|
1365
|
-
|
|
1438
|
+
// Wait for socket file to appear (max 5s)
|
|
1439
|
+
var waited = 0;
|
|
1440
|
+
while (!fs.existsSync(socketPath) && waited < 5000) {
|
|
1441
|
+
await new Promise(function(r) { setTimeout(r, 100); });
|
|
1442
|
+
waited += 100;
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
if (fs.existsSync(socketPath)) {
|
|
1446
|
+
console.log('Daemon started (PID: ' + child.pid + '). Socket: ' + socketPath);
|
|
1447
|
+
console.log('Logs: ' + logPath);
|
|
1448
|
+
} else {
|
|
1449
|
+
console.log('Daemon started (PID: ' + child.pid + ') but socket not yet available.');
|
|
1450
|
+
console.log('Check logs: ' + logPath);
|
|
1451
|
+
}
|
|
1366
1452
|
}
|
|
1367
1453
|
});
|
|
1368
1454
|
|
|
@@ -1396,8 +1482,8 @@ daemonCmd
|
|
|
1396
1482
|
var data = JSON.parse(fs.readFileSync(pidPath, 'utf8'));
|
|
1397
1483
|
try {
|
|
1398
1484
|
process.kill(data.pid, 0);
|
|
1399
|
-
var
|
|
1400
|
-
console.log('Running (PID: ' + data.pid + ', started: ' + data.startedAt +
|
|
1485
|
+
var listenInfo = data.port ? ', port: ' + data.port : (data.socketPath && fs.existsSync(data.socketPath) ? ', socket: active' : '');
|
|
1486
|
+
console.log('Running (PID: ' + data.pid + ', started: ' + data.startedAt + listenInfo + ')');
|
|
1401
1487
|
} catch (_) {
|
|
1402
1488
|
console.log('Not running (stale PID file).');
|
|
1403
1489
|
fs.unlinkSync(pidPath);
|
|
@@ -1431,7 +1517,9 @@ program.parseAsync(process.argv).then(async function() {
|
|
|
1431
1517
|
// on unclosed handles (file loggers, in-memory transport, etc.).
|
|
1432
1518
|
if (_isLongRunning) return;
|
|
1433
1519
|
await closeClient();
|
|
1434
|
-
process.exit(
|
|
1520
|
+
// Defer process.exit() by one event-loop tick so native addon destructors
|
|
1521
|
+
// (ONNX runtime, etc.) can release mutexes before V8 tears down.
|
|
1522
|
+
setImmediate(function() { process.exit(process.exitCode || 0); });
|
|
1435
1523
|
}).catch(async function(err) {
|
|
1436
1524
|
console.error('Fatal:', err.message || err);
|
|
1437
1525
|
await closeClient();
|