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