frontmcp 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/package.json +5 -5
  2. package/src/commands/build/adapters/cloudflare.js +29 -1
  3. package/src/commands/build/adapters/cloudflare.js.map +1 -1
  4. package/src/commands/build/adapters/distributed.js +4 -1
  5. package/src/commands/build/adapters/distributed.js.map +1 -1
  6. package/src/commands/build/exec/cli-runtime/extract-public-message.snippet.d.ts +18 -0
  7. package/src/commands/build/exec/cli-runtime/extract-public-message.snippet.js +58 -0
  8. package/src/commands/build/exec/cli-runtime/extract-public-message.snippet.js.map +1 -0
  9. package/src/commands/build/exec/cli-runtime/generate-cli-entry.d.ts +2 -2
  10. package/src/commands/build/exec/cli-runtime/generate-cli-entry.js +165 -47
  11. package/src/commands/build/exec/cli-runtime/generate-cli-entry.js.map +1 -1
  12. package/src/commands/build/exec/cli-runtime/schema-extractor.d.ts +7 -0
  13. package/src/commands/build/exec/cli-runtime/schema-extractor.js +17 -1
  14. package/src/commands/build/exec/cli-runtime/schema-extractor.js.map +1 -1
  15. package/src/commands/build/exec/index.d.ts +11 -0
  16. package/src/commands/build/exec/index.js +68 -20
  17. package/src/commands/build/exec/index.js.map +1 -1
  18. package/src/commands/build/exec/installer-script.d.ts +8 -2
  19. package/src/commands/build/exec/installer-script.js +34 -15
  20. package/src/commands/build/exec/installer-script.js.map +1 -1
  21. package/src/commands/build/exec/manifest.d.ts +16 -3
  22. package/src/commands/build/exec/manifest.js +17 -5
  23. package/src/commands/build/exec/manifest.js.map +1 -1
  24. package/src/commands/build/exec/runner-script.d.ts +9 -1
  25. package/src/commands/build/exec/runner-script.js +60 -2
  26. package/src/commands/build/exec/runner-script.js.map +1 -1
  27. package/src/commands/build/index.js +80 -18
  28. package/src/commands/build/index.js.map +1 -1
  29. package/src/commands/build/load-entry-config.d.ts +5 -0
  30. package/src/commands/build/load-entry-config.js +96 -0
  31. package/src/commands/build/load-entry-config.js.map +1 -0
  32. package/src/commands/build/mcpb/manifest.d.ts +14 -0
  33. package/src/commands/build/mcpb/manifest.js +29 -0
  34. package/src/commands/build/mcpb/manifest.js.map +1 -1
  35. package/src/commands/build/types.d.ts +19 -0
  36. package/src/commands/build/types.js.map +1 -1
  37. package/src/commands/dev/doctor.js +7 -3
  38. package/src/commands/dev/doctor.js.map +1 -1
  39. package/src/commands/package/install.d.ts +1 -1
  40. package/src/commands/package/install.js +10 -8
  41. package/src/commands/package/install.js.map +1 -1
  42. package/src/commands/package/types.d.ts +2 -1
  43. package/src/commands/package/types.js.map +1 -1
  44. package/src/config/frontmcp-config.loader.d.ts +20 -0
  45. package/src/config/frontmcp-config.loader.js +124 -5
  46. package/src/config/frontmcp-config.loader.js.map +1 -1
  47. package/src/config/index.d.ts +1 -1
  48. package/src/config/index.js +2 -1
  49. package/src/config/index.js.map +1 -1
@@ -32,8 +32,24 @@ async function buildExec(opts) {
32
32
  const cwd = process.cwd();
33
33
  const outDir = path.resolve(cwd, opts.outDir || 'dist');
34
34
  console.log(`${(0, colors_1.c)('cyan', '[build:exec]')} Building executable bundle...`);
35
- // 1. Load config
35
+ // 1. Load config (and merge in overrides forwarded from frontmcp.config —
36
+ // `build.storage`, `deployments[].cli.outputDefault`, etc.)
36
37
  const rawConfig = await (0, config_1.loadExecConfig)(cwd);
38
+ if (opts.execOverrides) {
39
+ if (opts.execOverrides.storage && !rawConfig.storage) {
40
+ rawConfig.storage = opts.execOverrides.storage;
41
+ }
42
+ if (opts.execOverrides.cli) {
43
+ const existing = rawConfig.cli;
44
+ // CliConfig requires `enabled: boolean`; preserve any existing value or
45
+ // default to true (we only get here when CLI mode is being configured).
46
+ rawConfig.cli = {
47
+ enabled: existing?.enabled ?? true,
48
+ ...existing,
49
+ ...opts.execOverrides.cli,
50
+ };
51
+ }
52
+ }
37
53
  const config = (0, config_1.normalizeConfig)(rawConfig);
38
54
  const cliEnabled = opts.cli || config.cli?.enabled;
39
55
  const seaEnabled = opts.sea || config.sea?.enabled;
@@ -90,14 +106,35 @@ async function buildExec(opts) {
90
106
  // Always build non-self-contained first (schema extraction needs host SDK)
91
107
  const bundleResult = await (0, esbuild_bundler_1.bundleWithEsbuild)(compiledEntry, outDir, config);
92
108
  console.log(`${(0, colors_1.c)('green', '[build:exec]')} bundle created: ${path.relative(cwd, bundleResult.bundlePath)} (${(0, esbuild_bundler_1.formatSize)(bundleResult.bundleSize)})`);
93
- // 6. Generate manifest
109
+ // 6. Extract schema once and reuse for both the manifest port resolution
110
+ // (#371) and the CLI command generation (when cliEnabled). Schema
111
+ // extraction loads the user's bundle and boots an in-memory client
112
+ // (~200ms); running it twice on every --target cli build added up.
94
113
  const bundleFilename = `${config.name}.bundle.js`;
95
- const manifest = (0, manifest_1.generateManifest)(config, bundleFilename);
114
+ const { extractSchemas } = await import('./cli-runtime/schema-extractor.js');
115
+ let extractedSchema;
116
+ try {
117
+ extractedSchema = await extractSchemas(bundleResult.bundlePath);
118
+ }
119
+ catch (err) {
120
+ if (cliEnabled) {
121
+ // CLI builds genuinely need the schema — fail loud.
122
+ throw err;
123
+ }
124
+ // For --target node we only need httpPort from the schema; falling back
125
+ // to the manifest's default precedence chain is acceptable.
126
+ }
127
+ // 7. Generate manifest with extracted decorator port + per-deployment cli config
128
+ const manifest = (0, manifest_1.generateManifest)(config, bundleFilename, {
129
+ target: cliEnabled ? 'cli' : 'node',
130
+ decoratorHttpPort: extractedSchema?.httpPort,
131
+ outputDefault: config.cli?.outputDefault,
132
+ });
96
133
  // 7. CLI build step (if enabled)
97
134
  let cliBundlePath;
98
135
  if (cliEnabled) {
99
136
  console.log(`${(0, colors_1.c)('cyan', '[build:exec]')} extracting schemas for CLI...`);
100
- const { extractSchemas, SYSTEM_TOOL_NAMES } = await import('./cli-runtime/schema-extractor.js');
137
+ const { SYSTEM_TOOL_NAMES } = await import('./cli-runtime/schema-extractor.js');
101
138
  const { generateCliEntry, resolveToolCommandName } = await import('./cli-runtime/generate-cli-entry.js');
102
139
  const { generateOutputFormatterSource } = await import('./cli-runtime/output-formatter.js');
103
140
  const { generateSessionManagerSource } = await import('./cli-runtime/session-manager.js');
@@ -105,8 +142,10 @@ async function buildExec(opts) {
105
142
  const { generateOAuthHelperSource } = await import('./cli-runtime/oauth-helper.js');
106
143
  const { generateDaemonClientSource } = await import('./cli-runtime/daemon-client.js');
107
144
  const { bundleCliWithEsbuild } = await import('./cli-runtime/cli-bundler.js');
108
- // Extract schemas from server bundle
109
- const schema = await extractSchemas(bundleResult.bundlePath);
145
+ // Reuse the schema extracted in step 6 (single extraction per build).
146
+ // The non-null assertion is safe — we threw above if cliEnabled and
147
+ // extraction failed, so by here `extractedSchema` is always defined.
148
+ const schema = extractedSchema;
110
149
  const capabilities = schema.capabilities;
111
150
  const userToolCount = schema.tools.filter((t) => !SYSTEM_TOOL_NAMES.has(t.name)).length;
112
151
  console.log(`${(0, colors_1.c)('cyan', '[build:exec]')} extracted: ${schema.tools.length} tools (${userToolCount} user), ${schema.resources.length} resources, ${schema.resourceTemplates.length} templates, ${schema.prompts.length} prompts`);
@@ -188,23 +227,29 @@ async function buildExec(opts) {
188
227
  };
189
228
  }
190
229
  // 8. Build SEA binaries if enabled
230
+ //
231
+ // For --target cli (cliEnabled === true), the runner script `exec`s only
232
+ // the CLI binary — the standalone server SEA is dead weight (~114 MB, see
233
+ // issue #373). Skip the server-SEA pass entirely in that mode. For
234
+ // --target node (cliEnabled === false), build only the server SEA.
191
235
  let seaServerResult;
192
236
  let seaCliResult;
193
237
  if (seaEnabled) {
194
238
  const { buildSea } = await import('./sea-builder.js');
195
- // Rebuild server bundle as self-contained for SEA (inlines all deps)
196
- // Use a temp filename so the original non-self-contained bundle is preserved
197
- const seaTempName = `${config.name}.sea-temp`;
198
- console.log(`${(0, colors_1.c)('cyan', '[build:sea]')} rebuilding server bundle (self-contained)...`);
199
- const seaBundle = await (0, esbuild_bundler_1.bundleWithEsbuild)(compiledEntry, outDir, config, {
200
- selfContained: true,
201
- outputName: seaTempName,
202
- });
203
- console.log(`${(0, colors_1.c)('cyan', '[build:sea]')} building server SEA binary...`);
204
- seaServerResult = await buildSea(seaBundle.bundlePath, outDir, config.name);
205
- // Clean up temp self-contained bundle
206
- fs.unlinkSync(seaBundle.bundlePath);
207
- console.log(`${(0, colors_1.c)('green', '[build:sea]')} server binary: ${path.relative(cwd, seaServerResult.executablePath)} (${(0, esbuild_bundler_1.formatSize)(seaServerResult.executableSize)})`);
239
+ if (!cliEnabled) {
240
+ // Server-only SEA path (--target node): rebuild bundle as self-contained
241
+ // (inlines all deps) and produce ${name}-bin.
242
+ const seaTempName = `${config.name}.sea-temp`;
243
+ console.log(`${(0, colors_1.c)('cyan', '[build:sea]')} rebuilding server bundle (self-contained)...`);
244
+ const seaBundle = await (0, esbuild_bundler_1.bundleWithEsbuild)(compiledEntry, outDir, config, {
245
+ selfContained: true,
246
+ outputName: seaTempName,
247
+ });
248
+ console.log(`${(0, colors_1.c)('cyan', '[build:sea]')} building server SEA binary...`);
249
+ seaServerResult = await buildSea(seaBundle.bundlePath, outDir, config.name);
250
+ fs.unlinkSync(seaBundle.bundlePath);
251
+ console.log(`${(0, colors_1.c)('green', '[build:sea]')} server binary: ${path.relative(cwd, seaServerResult.executablePath)} (${(0, esbuild_bundler_1.formatSize)(seaServerResult.executableSize)})`);
252
+ }
208
253
  if (cliBundlePath) {
209
254
  console.log(`${(0, colors_1.c)('cyan', '[build:sea]')} building CLI SEA binary...`);
210
255
  seaCliResult = await buildSea(cliBundlePath, outDir, `${config.name}-cli`);
@@ -221,7 +266,10 @@ async function buildExec(opts) {
221
266
  fs.writeFileSync(runnerPath, runnerContent, { mode: 0o755 });
222
267
  console.log(`${(0, colors_1.c)('green', '[build:exec]')} runner: ${path.relative(cwd, runnerPath)}`);
223
268
  // 10. Generate installer script
224
- const installerContent = (0, installer_script_1.generateInstallerScript)(config);
269
+ const installerContent = (0, installer_script_1.generateInstallerScript)(config, {
270
+ target: cliEnabled ? 'cli' : 'node',
271
+ seaEnabled: !!seaEnabled,
272
+ });
225
273
  const installerPath = path.join(outDir, `install-${config.name}.sh`);
226
274
  fs.writeFileSync(installerPath, installerContent, { mode: 0o755 });
227
275
  console.log(`${(0, colors_1.c)('green', '[build:exec]')} installer: ${path.relative(cwd, installerPath)}`);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/commands/build/exec/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * buildExec() orchestrator — produces a distributable bundle from a FrontMCP app.\n *\n * Output (server-only mode):\n * dist/{name}.bundle.js — esbuild single-file bundle\n * dist/{name}.manifest.json — app metadata (runtime reqs, setup questions)\n * dist/{name} — bash runner script\n * dist/install-{name}.sh — bash installer script\n *\n * Output (CLI mode, --cli flag):\n * All of the above, plus:\n * dist/{name}-cli.bundle.js — CLI executable bundle (commander.js)\n * dist/{name} — bash runner dispatches to CLI bundle\n */\n\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { type ParsedArgs } from '../../../core/args';\nimport { c } from '../../../core/colors';\nimport { resolveEntry } from '../../../shared/fs';\nimport { loadExecConfig, normalizeConfig } from './config';\nimport { bundleWithEsbuild, formatSize } from './esbuild-bundler';\nimport { generateManifest } from './manifest';\nimport { generateRunnerScript } from './runner-script';\nimport { generateInstallerScript } from './installer-script';\nimport { validateStepGraph } from './setup';\nimport { ensureDir, fileExists, runCmd } from '@frontmcp/utils';\nimport { REQUIRED_DECORATOR_FIELDS } from '../../../core/tsconfig';\n\nexport async function buildExec(opts: ParsedArgs & { cli?: boolean; sea?: boolean }): Promise<void> {\n const cwd = process.cwd();\n const outDir = path.resolve(cwd, opts.outDir || 'dist');\n\n console.log(`${c('cyan', '[build:exec]')} Building executable bundle...`);\n\n // 1. Load config\n const rawConfig = await loadExecConfig(cwd);\n const config = normalizeConfig(rawConfig);\n const cliEnabled = opts.cli || config.cli?.enabled;\n const seaEnabled = opts.sea || config.sea?.enabled;\n\n console.log(`${c('cyan', '[build:exec]')} name: ${config.name}`);\n console.log(`${c('cyan', '[build:exec]')} version: ${config.version}`);\n if (cliEnabled) {\n console.log(`${c('cyan', '[build:exec]')} CLI mode: enabled`);\n }\n if (seaEnabled) {\n console.log(`${c('cyan', '[build:exec]')} SEA mode: enabled (single executable)`);\n }\n\n // 2. Resolve entry\n const entry = await resolveEntry(cwd, config.entry || opts.entry);\n console.log(`${c('cyan', '[build:exec]')} entry: ${path.relative(cwd, entry)}`);\n\n // 3. Validate setup graph if present\n if (config.setup?.steps) {\n console.log(\n `${c('cyan', '[build:exec]')} validating setup questionnaire (${config.setup.steps.length} steps)...`,\n );\n const errors = validateStepGraph(config.setup.steps);\n const realErrors = errors.filter((e) => !e.startsWith('Warning:'));\n const warnings = errors.filter((e) => e.startsWith('Warning:'));\n\n for (const w of warnings) {\n console.log(`${c('yellow', '[build:exec]')} ${w}`);\n }\n if (realErrors.length > 0) {\n for (const e of realErrors) {\n console.error(`${c('red', '[build:exec]')} ${e}`);\n }\n throw new Error('Setup questionnaire has validation errors. Fix them before building.');\n }\n }\n\n // 4. Compile TypeScript\n console.log(`${c('cyan', '[build:exec]')} compiling TypeScript...`);\n await ensureDir(outDir);\n\n const tsconfigPath = path.join(cwd, 'tsconfig.json');\n const hasTsconfig = await fileExists(tsconfigPath);\n const tscArgs: string[] = ['-y', 'tsc'];\n\n if (hasTsconfig) {\n tscArgs.push('--project', tsconfigPath);\n } else {\n tscArgs.push(entry);\n tscArgs.push('--rootDir', path.dirname(entry));\n tscArgs.push('--experimentalDecorators', '--emitDecoratorMetadata');\n tscArgs.push('--target', REQUIRED_DECORATOR_FIELDS.target);\n }\n\n tscArgs.push('--module', 'commonjs');\n tscArgs.push('--outDir', outDir);\n tscArgs.push('--skipLibCheck');\n\n await runCmd('npx', tscArgs);\n console.log(`${c('green', '[build:exec]')} TypeScript compiled.`);\n\n // 5. Bundle with esbuild\n console.log(`${c('cyan', '[build:exec]')} bundling with esbuild...`);\n const compiledEntry = path.join(\n outDir,\n path.basename(entry).replace(/\\.tsx?$/, '.js'),\n );\n\n // Always build non-self-contained first (schema extraction needs host SDK)\n const bundleResult = await bundleWithEsbuild(compiledEntry, outDir, config);\n console.log(\n `${c('green', '[build:exec]')} bundle created: ${path.relative(cwd, bundleResult.bundlePath)} (${formatSize(bundleResult.bundleSize)})`,\n );\n\n // 6. Generate manifest\n const bundleFilename = `${config.name}.bundle.js`;\n const manifest = generateManifest(config, bundleFilename);\n\n // 7. CLI build step (if enabled)\n let cliBundlePath: string | undefined;\n if (cliEnabled) {\n console.log(`${c('cyan', '[build:exec]')} extracting schemas for CLI...`);\n\n const { extractSchemas, SYSTEM_TOOL_NAMES } = await import('./cli-runtime/schema-extractor.js');\n const { generateCliEntry, resolveToolCommandName } = await import('./cli-runtime/generate-cli-entry.js');\n const { generateOutputFormatterSource } = await import('./cli-runtime/output-formatter.js');\n const { generateSessionManagerSource } = await import('./cli-runtime/session-manager.js');\n const { generateCredentialStoreSource } = await import('./cli-runtime/credential-store.js');\n const { generateOAuthHelperSource } = await import('./cli-runtime/oauth-helper.js');\n const { generateDaemonClientSource } = await import('./cli-runtime/daemon-client.js');\n const { bundleCliWithEsbuild } = await import('./cli-runtime/cli-bundler.js');\n\n // Extract schemas from server bundle\n const schema = await extractSchemas(bundleResult.bundlePath);\n\n const capabilities = schema.capabilities;\n const userToolCount = schema.tools.filter(\n (t) => !SYSTEM_TOOL_NAMES.has(t.name),\n ).length;\n\n console.log(\n `${c('cyan', '[build:exec]')} extracted: ${schema.tools.length} tools (${userToolCount} user), ${schema.resources.length} resources, ${schema.resourceTemplates.length} templates, ${schema.prompts.length} prompts`,\n );\n if (capabilities.skills) console.log(`${c('cyan', '[build:exec]')} capability: skills`);\n if (capabilities.jobs) console.log(`${c('cyan', '[build:exec]')} capability: jobs`);\n if (capabilities.workflows) console.log(`${c('cyan', '[build:exec]')} capability: workflows`);\n\n // Copy skill content files via shared helper (flat _skills/ layout).\n const { copySkillAssets } = await import('./skill-assets.js');\n const { copiedCount } = copySkillAssets(outDir, schema.skillAssets);\n if (copiedCount > 0) {\n console.log(`${c('green', '[build:exec]')} copied ${copiedCount} skill content file(s) to _skills/`);\n }\n\n // Log tool name conflicts\n const cliConfig = config.cli || { enabled: true };\n const excludeTools = cliConfig.excludeTools || [];\n schema.tools\n .filter((t) => !excludeTools.includes(t.name))\n .forEach((t) => {\n const { wasRenamed, cmdName } = resolveToolCommandName(t.name);\n if (wasRenamed) {\n console.log(\n `${c('yellow', '[build:exec]')} Tool \"${t.name}\" conflicts with built-in command, mapped to \"${cmdName}\"`,\n );\n }\n });\n\n // Generate runtime modules\n const outputDefault = cliConfig.outputDefault || 'text';\n const authRequired = cliConfig.authRequired ?? false;\n const nativeDeps = cliConfig.nativeDeps || {};\n const oauthConfig = cliConfig.oauth;\n\n // Write runtime modules to temp files for bundling\n const tempDir = path.join(outDir, '__cli_temp');\n fs.mkdirSync(tempDir, { recursive: true });\n\n fs.writeFileSync(\n path.join(tempDir, 'output-formatter.js'),\n generateOutputFormatterSource(),\n );\n fs.writeFileSync(\n path.join(tempDir, 'session-manager.js'),\n generateSessionManagerSource(config.name),\n );\n fs.writeFileSync(\n path.join(tempDir, 'credential-store.js'),\n generateCredentialStoreSource(config.name),\n );\n fs.writeFileSync(\n path.join(tempDir, 'oauth-helper.js'),\n generateOAuthHelperSource(config.name),\n );\n fs.writeFileSync(\n path.join(tempDir, 'daemon-client.js'),\n generateDaemonClientSource(),\n );\n\n // Generate CLI entry\n const cliEntrySource = generateCliEntry({\n appName: config.name,\n appVersion: config.version || '1.0.0',\n description: cliConfig.description || `${config.name} CLI`,\n serverBundleFilename: bundleFilename,\n outputDefault,\n authRequired,\n excludeTools,\n nativeDeps,\n schema,\n oauthConfig,\n selfContained: !!seaEnabled,\n });\n\n const cliEntryPath = path.join(tempDir, 'cli-entry.js');\n fs.writeFileSync(cliEntryPath, cliEntrySource);\n\n // Bundle CLI\n console.log(`${c('cyan', '[build:exec]')} bundling CLI...`);\n const cliResult = await bundleCliWithEsbuild(cliEntryPath, outDir, config, {\n selfContained: !!seaEnabled,\n });\n cliBundlePath = cliResult.bundlePath;\n console.log(\n `${c('green', '[build:exec]')} CLI bundle: ${path.relative(cwd, cliResult.bundlePath)} (${formatSize(cliResult.bundleSize)})`,\n );\n\n // Clean temp\n fs.rmSync(tempDir, { recursive: true, force: true });\n\n // Extend manifest with CLI metadata\n manifest.cli = {\n enabled: true,\n cliBundle: `${config.name}-cli.bundle.js`,\n outputDefault,\n authRequired,\n toolCount: userToolCount,\n resourceCount: schema.resources.length,\n templateCount: schema.resourceTemplates.length,\n promptCount: schema.prompts.length,\n oauthEnabled: !!oauthConfig,\n skillsEnabled: capabilities.skills || undefined,\n jobsEnabled: capabilities.jobs || undefined,\n workflowsEnabled: capabilities.workflows || undefined,\n };\n }\n\n // 8. Build SEA binaries if enabled\n let seaServerResult: { executablePath: string; executableSize: number } | undefined;\n let seaCliResult: { executablePath: string; executableSize: number } | undefined;\n if (seaEnabled) {\n const { buildSea } = await import('./sea-builder.js');\n\n // Rebuild server bundle as self-contained for SEA (inlines all deps)\n // Use a temp filename so the original non-self-contained bundle is preserved\n const seaTempName = `${config.name}.sea-temp`;\n console.log(`${c('cyan', '[build:sea]')} rebuilding server bundle (self-contained)...`);\n const seaBundle = await bundleWithEsbuild(compiledEntry, outDir, config, {\n selfContained: true,\n outputName: seaTempName,\n });\n\n console.log(`${c('cyan', '[build:sea]')} building server SEA binary...`);\n seaServerResult = await buildSea(seaBundle.bundlePath, outDir, config.name);\n // Clean up temp self-contained bundle\n fs.unlinkSync(seaBundle.bundlePath);\n console.log(\n `${c('green', '[build:sea]')} server binary: ${path.relative(cwd, seaServerResult.executablePath)} (${formatSize(seaServerResult.executableSize)})`,\n );\n\n if (cliBundlePath) {\n console.log(`${c('cyan', '[build:sea]')} building CLI SEA binary...`);\n seaCliResult = await buildSea(cliBundlePath, outDir, `${config.name}-cli`);\n console.log(\n `${c('green', '[build:sea]')} CLI binary: ${path.relative(cwd, seaCliResult.executablePath)} (${formatSize(seaCliResult.executableSize)})`,\n );\n }\n }\n\n // 9. Write manifest\n const manifestPath = path.join(outDir, `${config.name}.manifest.json`);\n fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), 'utf-8');\n console.log(\n `${c('green', '[build:exec]')} manifest: ${path.relative(cwd, manifestPath)}`,\n );\n\n // 10. Generate runner script (dispatches to CLI bundle or SEA binary)\n const runnerContent = generateRunnerScript(config, !!cliEnabled, !!seaEnabled);\n const runnerPath = path.join(outDir, config.name);\n fs.writeFileSync(runnerPath, runnerContent, { mode: 0o755 });\n console.log(\n `${c('green', '[build:exec]')} runner: ${path.relative(cwd, runnerPath)}`,\n );\n\n // 10. Generate installer script\n const installerContent = generateInstallerScript(config);\n const installerPath = path.join(outDir, `install-${config.name}.sh`);\n fs.writeFileSync(installerPath, installerContent, { mode: 0o755 });\n console.log(\n `${c('green', '[build:exec]')} installer: ${path.relative(cwd, installerPath)}`,\n );\n\n // 12. Clean up intermediate compiled files\n const keepFiles = new Set([\n `${config.name}.bundle.js`,\n `${config.name}-cli.bundle.js`,\n `${config.name}.manifest.json`,\n config.name,\n `install-${config.name}.sh`,\n `${config.name}-bin`,\n `${config.name}-cli-bin`,\n '_skills',\n ]);\n\n const allFiles = fs.readdirSync(outDir);\n let cleaned = 0;\n for (const file of allFiles) {\n if (!keepFiles.has(file)) {\n const filePath = path.join(outDir, file);\n const stat = fs.statSync(filePath);\n // Only clean intermediate compiled files; preserve .md and _skills/ directory\n if (stat.isFile() && !file.endsWith('.md')) {\n fs.unlinkSync(filePath);\n cleaned++;\n }\n }\n }\n if (cleaned > 0) {\n console.log(\n `${c('gray', '[build:exec]')} cleaned ${cleaned} intermediate file(s)`,\n );\n }\n\n // 13. Print summary\n console.log(`\\n${c('green', 'Executable build completed.')}`);\n console.log(`\\n${c('bold', 'Output:')}`);\n console.log(` ${path.relative(cwd, bundleResult.bundlePath)} ${c('gray', `(${formatSize(bundleResult.bundleSize)})`)}`);\n if (cliBundlePath) {\n const cliStat = fs.statSync(cliBundlePath);\n console.log(` ${path.relative(cwd, cliBundlePath)} ${c('gray', `(${formatSize(cliStat.size)})`)}`);\n }\n if (seaServerResult) {\n console.log(` ${path.relative(cwd, seaServerResult.executablePath)} ${c('gray', `(${formatSize(seaServerResult.executableSize)})`)}`);\n }\n if (seaCliResult) {\n console.log(` ${path.relative(cwd, seaCliResult.executablePath)} ${c('gray', `(${formatSize(seaCliResult.executableSize)})`)}`);\n }\n console.log(` ${path.relative(cwd, manifestPath)}`);\n console.log(` ${path.relative(cwd, runnerPath)}`);\n console.log(` ${path.relative(cwd, installerPath)}`);\n\n if (cliEnabled) {\n console.log(`\\n${c('gray', 'Run the CLI:')} ./${path.relative(cwd, runnerPath)} --help`);\n console.log(`${c('gray', 'Start server:')} ./${path.relative(cwd, runnerPath)} serve`);\n } else {\n console.log(`\\n${c('gray', 'Run the server:')} ./${path.relative(cwd, runnerPath)}`);\n }\n if (seaServerResult || seaCliResult) {\n console.log(`\\n${c('yellow', 'Note:')} SEA binaries are native executables. Run directly (not via bash).`);\n }\n console.log(`${c('gray', 'Install to system:')} bash ./${path.relative(cwd, installerPath)}`);\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/commands/build/exec/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * buildExec() orchestrator — produces a distributable bundle from a FrontMCP app.\n *\n * Output (server-only mode):\n * dist/{name}.bundle.js — esbuild single-file bundle\n * dist/{name}.manifest.json — app metadata (runtime reqs, setup questions)\n * dist/{name} — bash runner script\n * dist/install-{name}.sh — bash installer script\n *\n * Output (CLI mode, --cli flag):\n * All of the above, plus:\n * dist/{name}-cli.bundle.js — CLI executable bundle (commander.js)\n * dist/{name} — bash runner dispatches to CLI bundle\n */\n\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { type ParsedArgs } from '../../../core/args';\nimport { c } from '../../../core/colors';\nimport { resolveEntry } from '../../../shared/fs';\nimport { loadExecConfig, normalizeConfig } from './config';\nimport { bundleWithEsbuild, formatSize } from './esbuild-bundler';\nimport { generateManifest } from './manifest';\nimport { generateRunnerScript } from './runner-script';\nimport { generateInstallerScript } from './installer-script';\nimport { validateStepGraph } from './setup';\nimport { ensureDir, fileExists, runCmd } from '@frontmcp/utils';\nimport { REQUIRED_DECORATOR_FIELDS } from '../../../core/tsconfig';\n\nexport async function buildExec(\n opts: ParsedArgs & {\n cli?: boolean;\n sea?: boolean;\n execOverrides?: {\n storage?: { type: 'sqlite' | 'redis' | 'none'; required?: boolean };\n cli?: { outputDefault?: 'text' | 'json'; description?: string; authRequired?: boolean };\n };\n },\n): Promise<void> {\n const cwd = process.cwd();\n const outDir = path.resolve(cwd, opts.outDir || 'dist');\n\n console.log(`${c('cyan', '[build:exec]')} Building executable bundle...`);\n\n // 1. Load config (and merge in overrides forwarded from frontmcp.config —\n // `build.storage`, `deployments[].cli.outputDefault`, etc.)\n const rawConfig = await loadExecConfig(cwd);\n if (opts.execOverrides) {\n if (opts.execOverrides.storage && !rawConfig.storage) {\n rawConfig.storage = opts.execOverrides.storage;\n }\n if (opts.execOverrides.cli) {\n const existing = rawConfig.cli;\n // CliConfig requires `enabled: boolean`; preserve any existing value or\n // default to true (we only get here when CLI mode is being configured).\n rawConfig.cli = {\n enabled: existing?.enabled ?? true,\n ...existing,\n ...opts.execOverrides.cli,\n };\n }\n }\n const config = normalizeConfig(rawConfig);\n const cliEnabled = opts.cli || config.cli?.enabled;\n const seaEnabled = opts.sea || config.sea?.enabled;\n\n console.log(`${c('cyan', '[build:exec]')} name: ${config.name}`);\n console.log(`${c('cyan', '[build:exec]')} version: ${config.version}`);\n if (cliEnabled) {\n console.log(`${c('cyan', '[build:exec]')} CLI mode: enabled`);\n }\n if (seaEnabled) {\n console.log(`${c('cyan', '[build:exec]')} SEA mode: enabled (single executable)`);\n }\n\n // 2. Resolve entry\n const entry = await resolveEntry(cwd, config.entry || opts.entry);\n console.log(`${c('cyan', '[build:exec]')} entry: ${path.relative(cwd, entry)}`);\n\n // 3. Validate setup graph if present\n if (config.setup?.steps) {\n console.log(\n `${c('cyan', '[build:exec]')} validating setup questionnaire (${config.setup.steps.length} steps)...`,\n );\n const errors = validateStepGraph(config.setup.steps);\n const realErrors = errors.filter((e) => !e.startsWith('Warning:'));\n const warnings = errors.filter((e) => e.startsWith('Warning:'));\n\n for (const w of warnings) {\n console.log(`${c('yellow', '[build:exec]')} ${w}`);\n }\n if (realErrors.length > 0) {\n for (const e of realErrors) {\n console.error(`${c('red', '[build:exec]')} ${e}`);\n }\n throw new Error('Setup questionnaire has validation errors. Fix them before building.');\n }\n }\n\n // 4. Compile TypeScript\n console.log(`${c('cyan', '[build:exec]')} compiling TypeScript...`);\n await ensureDir(outDir);\n\n const tsconfigPath = path.join(cwd, 'tsconfig.json');\n const hasTsconfig = await fileExists(tsconfigPath);\n const tscArgs: string[] = ['-y', 'tsc'];\n\n if (hasTsconfig) {\n tscArgs.push('--project', tsconfigPath);\n } else {\n tscArgs.push(entry);\n tscArgs.push('--rootDir', path.dirname(entry));\n tscArgs.push('--experimentalDecorators', '--emitDecoratorMetadata');\n tscArgs.push('--target', REQUIRED_DECORATOR_FIELDS.target);\n }\n\n tscArgs.push('--module', 'commonjs');\n tscArgs.push('--outDir', outDir);\n tscArgs.push('--skipLibCheck');\n\n await runCmd('npx', tscArgs);\n console.log(`${c('green', '[build:exec]')} TypeScript compiled.`);\n\n // 5. Bundle with esbuild\n console.log(`${c('cyan', '[build:exec]')} bundling with esbuild...`);\n const compiledEntry = path.join(\n outDir,\n path.basename(entry).replace(/\\.tsx?$/, '.js'),\n );\n\n // Always build non-self-contained first (schema extraction needs host SDK)\n const bundleResult = await bundleWithEsbuild(compiledEntry, outDir, config);\n console.log(\n `${c('green', '[build:exec]')} bundle created: ${path.relative(cwd, bundleResult.bundlePath)} (${formatSize(bundleResult.bundleSize)})`,\n );\n\n // 6. Extract schema once and reuse for both the manifest port resolution\n // (#371) and the CLI command generation (when cliEnabled). Schema\n // extraction loads the user's bundle and boots an in-memory client\n // (~200ms); running it twice on every --target cli build added up.\n const bundleFilename = `${config.name}.bundle.js`;\n const { extractSchemas } = await import('./cli-runtime/schema-extractor.js');\n let extractedSchema: Awaited<ReturnType<typeof extractSchemas>> | undefined;\n try {\n extractedSchema = await extractSchemas(bundleResult.bundlePath);\n } catch (err) {\n if (cliEnabled) {\n // CLI builds genuinely need the schema — fail loud.\n throw err;\n }\n // For --target node we only need httpPort from the schema; falling back\n // to the manifest's default precedence chain is acceptable.\n }\n\n // 7. Generate manifest with extracted decorator port + per-deployment cli config\n const manifest = generateManifest(config, bundleFilename, {\n target: cliEnabled ? 'cli' : 'node',\n decoratorHttpPort: extractedSchema?.httpPort,\n outputDefault: config.cli?.outputDefault as 'text' | 'json' | undefined,\n });\n\n // 7. CLI build step (if enabled)\n let cliBundlePath: string | undefined;\n if (cliEnabled) {\n console.log(`${c('cyan', '[build:exec]')} extracting schemas for CLI...`);\n\n const { SYSTEM_TOOL_NAMES } = await import('./cli-runtime/schema-extractor.js');\n const { generateCliEntry, resolveToolCommandName } = await import('./cli-runtime/generate-cli-entry.js');\n const { generateOutputFormatterSource } = await import('./cli-runtime/output-formatter.js');\n const { generateSessionManagerSource } = await import('./cli-runtime/session-manager.js');\n const { generateCredentialStoreSource } = await import('./cli-runtime/credential-store.js');\n const { generateOAuthHelperSource } = await import('./cli-runtime/oauth-helper.js');\n const { generateDaemonClientSource } = await import('./cli-runtime/daemon-client.js');\n const { bundleCliWithEsbuild } = await import('./cli-runtime/cli-bundler.js');\n\n // Reuse the schema extracted in step 6 (single extraction per build).\n // The non-null assertion is safe — we threw above if cliEnabled and\n // extraction failed, so by here `extractedSchema` is always defined.\n const schema = extractedSchema!;\n\n const capabilities = schema.capabilities;\n const userToolCount = schema.tools.filter(\n (t) => !SYSTEM_TOOL_NAMES.has(t.name),\n ).length;\n\n console.log(\n `${c('cyan', '[build:exec]')} extracted: ${schema.tools.length} tools (${userToolCount} user), ${schema.resources.length} resources, ${schema.resourceTemplates.length} templates, ${schema.prompts.length} prompts`,\n );\n if (capabilities.skills) console.log(`${c('cyan', '[build:exec]')} capability: skills`);\n if (capabilities.jobs) console.log(`${c('cyan', '[build:exec]')} capability: jobs`);\n if (capabilities.workflows) console.log(`${c('cyan', '[build:exec]')} capability: workflows`);\n\n // Copy skill content files via shared helper (flat _skills/ layout).\n const { copySkillAssets } = await import('./skill-assets.js');\n const { copiedCount } = copySkillAssets(outDir, schema.skillAssets);\n if (copiedCount > 0) {\n console.log(`${c('green', '[build:exec]')} copied ${copiedCount} skill content file(s) to _skills/`);\n }\n\n // Log tool name conflicts\n const cliConfig = config.cli || { enabled: true };\n const excludeTools = cliConfig.excludeTools || [];\n schema.tools\n .filter((t) => !excludeTools.includes(t.name))\n .forEach((t) => {\n const { wasRenamed, cmdName } = resolveToolCommandName(t.name);\n if (wasRenamed) {\n console.log(\n `${c('yellow', '[build:exec]')} Tool \"${t.name}\" conflicts with built-in command, mapped to \"${cmdName}\"`,\n );\n }\n });\n\n // Generate runtime modules\n const outputDefault = cliConfig.outputDefault || 'text';\n const authRequired = cliConfig.authRequired ?? false;\n const nativeDeps = cliConfig.nativeDeps || {};\n const oauthConfig = cliConfig.oauth;\n\n // Write runtime modules to temp files for bundling\n const tempDir = path.join(outDir, '__cli_temp');\n fs.mkdirSync(tempDir, { recursive: true });\n\n fs.writeFileSync(\n path.join(tempDir, 'output-formatter.js'),\n generateOutputFormatterSource(),\n );\n fs.writeFileSync(\n path.join(tempDir, 'session-manager.js'),\n generateSessionManagerSource(config.name),\n );\n fs.writeFileSync(\n path.join(tempDir, 'credential-store.js'),\n generateCredentialStoreSource(config.name),\n );\n fs.writeFileSync(\n path.join(tempDir, 'oauth-helper.js'),\n generateOAuthHelperSource(config.name),\n );\n fs.writeFileSync(\n path.join(tempDir, 'daemon-client.js'),\n generateDaemonClientSource(),\n );\n\n // Generate CLI entry\n const cliEntrySource = generateCliEntry({\n appName: config.name,\n appVersion: config.version || '1.0.0',\n description: cliConfig.description || `${config.name} CLI`,\n serverBundleFilename: bundleFilename,\n outputDefault,\n authRequired,\n excludeTools,\n nativeDeps,\n schema,\n oauthConfig,\n selfContained: !!seaEnabled,\n });\n\n const cliEntryPath = path.join(tempDir, 'cli-entry.js');\n fs.writeFileSync(cliEntryPath, cliEntrySource);\n\n // Bundle CLI\n console.log(`${c('cyan', '[build:exec]')} bundling CLI...`);\n const cliResult = await bundleCliWithEsbuild(cliEntryPath, outDir, config, {\n selfContained: !!seaEnabled,\n });\n cliBundlePath = cliResult.bundlePath;\n console.log(\n `${c('green', '[build:exec]')} CLI bundle: ${path.relative(cwd, cliResult.bundlePath)} (${formatSize(cliResult.bundleSize)})`,\n );\n\n // Clean temp\n fs.rmSync(tempDir, { recursive: true, force: true });\n\n // Extend manifest with CLI metadata\n manifest.cli = {\n enabled: true,\n cliBundle: `${config.name}-cli.bundle.js`,\n outputDefault,\n authRequired,\n toolCount: userToolCount,\n resourceCount: schema.resources.length,\n templateCount: schema.resourceTemplates.length,\n promptCount: schema.prompts.length,\n oauthEnabled: !!oauthConfig,\n skillsEnabled: capabilities.skills || undefined,\n jobsEnabled: capabilities.jobs || undefined,\n workflowsEnabled: capabilities.workflows || undefined,\n };\n }\n\n // 8. Build SEA binaries if enabled\n //\n // For --target cli (cliEnabled === true), the runner script `exec`s only\n // the CLI binary — the standalone server SEA is dead weight (~114 MB, see\n // issue #373). Skip the server-SEA pass entirely in that mode. For\n // --target node (cliEnabled === false), build only the server SEA.\n let seaServerResult: { executablePath: string; executableSize: number } | undefined;\n let seaCliResult: { executablePath: string; executableSize: number } | undefined;\n if (seaEnabled) {\n const { buildSea } = await import('./sea-builder.js');\n\n if (!cliEnabled) {\n // Server-only SEA path (--target node): rebuild bundle as self-contained\n // (inlines all deps) and produce ${name}-bin.\n const seaTempName = `${config.name}.sea-temp`;\n console.log(`${c('cyan', '[build:sea]')} rebuilding server bundle (self-contained)...`);\n const seaBundle = await bundleWithEsbuild(compiledEntry, outDir, config, {\n selfContained: true,\n outputName: seaTempName,\n });\n\n console.log(`${c('cyan', '[build:sea]')} building server SEA binary...`);\n seaServerResult = await buildSea(seaBundle.bundlePath, outDir, config.name);\n fs.unlinkSync(seaBundle.bundlePath);\n console.log(\n `${c('green', '[build:sea]')} server binary: ${path.relative(cwd, seaServerResult.executablePath)} (${formatSize(seaServerResult.executableSize)})`,\n );\n }\n\n if (cliBundlePath) {\n console.log(`${c('cyan', '[build:sea]')} building CLI SEA binary...`);\n seaCliResult = await buildSea(cliBundlePath, outDir, `${config.name}-cli`);\n console.log(\n `${c('green', '[build:sea]')} CLI binary: ${path.relative(cwd, seaCliResult.executablePath)} (${formatSize(seaCliResult.executableSize)})`,\n );\n }\n }\n\n // 9. Write manifest\n const manifestPath = path.join(outDir, `${config.name}.manifest.json`);\n fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), 'utf-8');\n console.log(\n `${c('green', '[build:exec]')} manifest: ${path.relative(cwd, manifestPath)}`,\n );\n\n // 10. Generate runner script (dispatches to CLI bundle or SEA binary)\n const runnerContent = generateRunnerScript(config, !!cliEnabled, !!seaEnabled);\n const runnerPath = path.join(outDir, config.name);\n fs.writeFileSync(runnerPath, runnerContent, { mode: 0o755 });\n console.log(\n `${c('green', '[build:exec]')} runner: ${path.relative(cwd, runnerPath)}`,\n );\n\n // 10. Generate installer script\n const installerContent = generateInstallerScript(config, {\n target: cliEnabled ? 'cli' : 'node',\n seaEnabled: !!seaEnabled,\n });\n const installerPath = path.join(outDir, `install-${config.name}.sh`);\n fs.writeFileSync(installerPath, installerContent, { mode: 0o755 });\n console.log(\n `${c('green', '[build:exec]')} installer: ${path.relative(cwd, installerPath)}`,\n );\n\n // 12. Clean up intermediate compiled files\n const keepFiles = new Set([\n `${config.name}.bundle.js`,\n `${config.name}-cli.bundle.js`,\n `${config.name}.manifest.json`,\n config.name,\n `install-${config.name}.sh`,\n `${config.name}-bin`,\n `${config.name}-cli-bin`,\n '_skills',\n ]);\n\n const allFiles = fs.readdirSync(outDir);\n let cleaned = 0;\n for (const file of allFiles) {\n if (!keepFiles.has(file)) {\n const filePath = path.join(outDir, file);\n const stat = fs.statSync(filePath);\n // Only clean intermediate compiled files; preserve .md and _skills/ directory\n if (stat.isFile() && !file.endsWith('.md')) {\n fs.unlinkSync(filePath);\n cleaned++;\n }\n }\n }\n if (cleaned > 0) {\n console.log(\n `${c('gray', '[build:exec]')} cleaned ${cleaned} intermediate file(s)`,\n );\n }\n\n // 13. Print summary\n console.log(`\\n${c('green', 'Executable build completed.')}`);\n console.log(`\\n${c('bold', 'Output:')}`);\n console.log(` ${path.relative(cwd, bundleResult.bundlePath)} ${c('gray', `(${formatSize(bundleResult.bundleSize)})`)}`);\n if (cliBundlePath) {\n const cliStat = fs.statSync(cliBundlePath);\n console.log(` ${path.relative(cwd, cliBundlePath)} ${c('gray', `(${formatSize(cliStat.size)})`)}`);\n }\n if (seaServerResult) {\n console.log(` ${path.relative(cwd, seaServerResult.executablePath)} ${c('gray', `(${formatSize(seaServerResult.executableSize)})`)}`);\n }\n if (seaCliResult) {\n console.log(` ${path.relative(cwd, seaCliResult.executablePath)} ${c('gray', `(${formatSize(seaCliResult.executableSize)})`)}`);\n }\n console.log(` ${path.relative(cwd, manifestPath)}`);\n console.log(` ${path.relative(cwd, runnerPath)}`);\n console.log(` ${path.relative(cwd, installerPath)}`);\n\n if (cliEnabled) {\n console.log(`\\n${c('gray', 'Run the CLI:')} ./${path.relative(cwd, runnerPath)} --help`);\n console.log(`${c('gray', 'Start server:')} ./${path.relative(cwd, runnerPath)} serve`);\n } else {\n console.log(`\\n${c('gray', 'Run the server:')} ./${path.relative(cwd, runnerPath)}`);\n }\n if (seaServerResult || seaCliResult) {\n console.log(`\\n${c('yellow', 'Note:')} SEA binaries are native executables. Run directly (not via bash).`);\n }\n console.log(`${c('gray', 'Install to system:')} bash ./${path.relative(cwd, installerPath)}`);\n}\n"]}
@@ -2,5 +2,11 @@
2
2
  * Bash installer script generation.
3
3
  * Supports interactive and --yes (silent) modes.
4
4
  */
5
- import { FrontmcpExecConfig } from './config';
6
- export declare function generateInstallerScript(config: FrontmcpExecConfig): string;
5
+ import { type FrontmcpExecConfig } from './config';
6
+ export interface InstallerScriptContext {
7
+ /** Build target this installer was generated for. Drives binary copying and Node version checks. */
8
+ target: 'node' | 'cli';
9
+ /** True when SEA binaries were emitted (CLI mode). Skips the Node version check at install time. */
10
+ seaEnabled: boolean;
11
+ }
12
+ export declare function generateInstallerScript(config: FrontmcpExecConfig, context?: InstallerScriptContext): string;
@@ -5,17 +5,45 @@
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.generateInstallerScript = generateInstallerScript;
8
- function generateInstallerScript(config) {
8
+ const runner_script_1 = require("./runner-script");
9
+ function generateInstallerScript(config, context = { target: 'node', seaEnabled: false }) {
9
10
  const name = config.name;
10
11
  const nativeAddons = config.dependencies?.nativeAddons || [];
11
12
  const storageType = config.storage?.type || 'none';
12
- const nodeVersion = config.nodeVersion || '>=22.0.0';
13
+ // Sanitize before interpolating into bash echo / error messages — the
14
+ // user-controlled `nodeVersion` ends up in command lines verbatim.
15
+ const nodeVersion = (0, runner_script_1.sanitizeShellLiteral)(config.nodeVersion || '>=22.0.0');
13
16
  const minNodeMajor = extractMinMajor(nodeVersion);
17
+ // CLI/SEA installs ship a self-contained binary — no Node runtime required
18
+ // at install or run time. Skip the Node check entirely; the binary is the
19
+ // whole point of `--target cli`.
20
+ const seaCli = context.target === 'cli' && context.seaEnabled;
21
+ const nodeCheck = seaCli
22
+ ? '# CLI/SEA install: Node.js not required at runtime — skipping version check.'
23
+ : `# Check Node.js
24
+ if ! command -v node &> /dev/null; then
25
+ echo "Error: Node.js is required."
26
+ echo "Install Node.js ${nodeVersion}: https://nodejs.org"
27
+ exit 1
28
+ fi
29
+
30
+ NODE_MAJOR=$(node -e "console.log(process.versions.node.split('.')[0])")
31
+ if [ "\${NODE_MAJOR}" -lt "${minNodeMajor}" ]; then
32
+ echo "Error: Node.js ${nodeVersion} required, found v$(node -v)"
33
+ exit 1
34
+ fi`;
35
+ // For SEA-CLI installs, also copy the CLI binary the runner exec's.
36
+ // Without this the installer leaves a runner pointing at a missing file.
37
+ const seaBinaryCopy = seaCli
38
+ ? `# Copy SEA binary that the runner exec's
39
+ cp "\${SCRIPT_DIR}/${name}-cli-bin" "\${INSTALL_DIR}/"
40
+ chmod +x "\${INSTALL_DIR}/${name}-cli-bin"`
41
+ : '# No SEA binary to copy (JS bundle mode)';
14
42
  return `#!/usr/bin/env bash
15
43
  set -euo pipefail
16
44
 
17
45
  # install-${name}.sh — FrontMCP App Installer
18
- # Generated by frontmcp build --target node
46
+ # Generated by frontmcp build --target ${context.target}
19
47
 
20
48
  SCRIPT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
21
49
  APP_NAME="${name}"
@@ -32,18 +60,7 @@ done
32
60
  echo "Installing \${APP_NAME}..."
33
61
  echo ""
34
62
 
35
- # Check Node.js
36
- if ! command -v node &> /dev/null; then
37
- echo "Error: Node.js is required."
38
- echo "Install Node.js ${nodeVersion}: https://nodejs.org"
39
- exit 1
40
- fi
41
-
42
- NODE_MAJOR=$(node -e "console.log(process.versions.node.split('.')[0])")
43
- if [ "\${NODE_MAJOR}" -lt "${minNodeMajor}" ]; then
44
- echo "Error: Node.js ${nodeVersion} required, found v$(node -v)"
45
- exit 1
46
- fi
63
+ ${nodeCheck}
47
64
 
48
65
  # Create install directory
49
66
  mkdir -p "\${INSTALL_DIR}"
@@ -54,6 +71,8 @@ cp "\${SCRIPT_DIR}/${name}.manifest.json" "\${INSTALL_DIR}/"
54
71
  cp "\${SCRIPT_DIR}/${name}" "\${INSTALL_DIR}/"
55
72
  chmod +x "\${INSTALL_DIR}/${name}"
56
73
 
74
+ ${seaBinaryCopy}
75
+
57
76
  ${nativeAddons.length > 0 ? generateNativeAddonInstall(nativeAddons) : '# No native addons required'}
58
77
 
59
78
  ${storageType === 'sqlite' ? generateSqliteSetup(name) : '# No storage setup required'}
@@ -1 +1 @@
1
- {"version":3,"file":"installer-script.js","sourceRoot":"","sources":["../../../../../src/commands/build/exec/installer-script.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAIH,0DAsEC;AAtED,SAAgB,uBAAuB,CAAC,MAA0B;IAChE,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;IACzB,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,EAAE,YAAY,IAAI,EAAE,CAAC;IAC7D,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,EAAE,IAAI,IAAI,MAAM,CAAC;IACnD,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,UAAU,CAAC;IACrD,MAAM,YAAY,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;IAElD,OAAO;;;YAGG,IAAI;;;;YAIJ,IAAI;;;;;;;;;;;;;;;;;0BAiBU,WAAW;;;;;6BAKR,YAAY;yBAChB,WAAW;;;;;;;;qBAQf,IAAI;qBACJ,IAAI;qBACJ,IAAI;4BACG,IAAI;;EAE9B,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,0BAA0B,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,6BAA6B;;EAElG,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,6BAA6B;;;;iDAIrC,IAAI;;;;;;;;;;;;;CAapD,CAAC;AACF,CAAC;AAED,SAAS,0BAA0B,CAAC,MAAgB;IAClD,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxD,OAAO;;2EAEkE,SAAS;CACnF,CAAC;AACF,CAAC;AAED,SAAS,mBAAmB,CAAC,IAAY;IACvC,OAAO;oCAC2B,IAAI;;0CAEE,IAAI;CAC7C,CAAC;AACF,CAAC;AAED,SAAS,eAAe,CAAC,OAAe;IACtC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACrC,OAAO,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC7C,CAAC","sourcesContent":["/**\n * Bash installer script generation.\n * Supports interactive and --yes (silent) modes.\n */\n\nimport { FrontmcpExecConfig } from './config';\n\nexport function generateInstallerScript(config: FrontmcpExecConfig): string {\n const name = config.name;\n const nativeAddons = config.dependencies?.nativeAddons || [];\n const storageType = config.storage?.type || 'none';\n const nodeVersion = config.nodeVersion || '>=22.0.0';\n const minNodeMajor = extractMinMajor(nodeVersion);\n\n return `#!/usr/bin/env bash\nset -euo pipefail\n\n# install-${name}.sh FrontMCP App Installer\n# Generated by frontmcp build --target node\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"\\${BASH_SOURCE[0]}\")\" && pwd)\"\nAPP_NAME=\"${name}\"\nINSTALL_DIR=\"\\${HOME}/.frontmcp/apps/\\${APP_NAME}\"\nSILENT=false\n\n# Parse args\nfor arg in \"$@\"; do\n case \"\\${arg}\" in\n --yes|-y) SILENT=true ;;\n esac\ndone\n\necho \"Installing \\${APP_NAME}...\"\necho \"\"\n\n# Check Node.js\nif ! command -v node &> /dev/null; then\n echo \"Error: Node.js is required.\"\n echo \"Install Node.js ${nodeVersion}: https://nodejs.org\"\n exit 1\nfi\n\nNODE_MAJOR=$(node -e \"console.log(process.versions.node.split('.')[0])\")\nif [ \"\\${NODE_MAJOR}\" -lt \"${minNodeMajor}\" ]; then\n echo \"Error: Node.js ${nodeVersion} required, found v$(node -v)\"\n exit 1\nfi\n\n# Create install directory\nmkdir -p \"\\${INSTALL_DIR}\"\n\n# Copy files\ncp \"\\${SCRIPT_DIR}/${name}.bundle.js\" \"\\${INSTALL_DIR}/\"\ncp \"\\${SCRIPT_DIR}/${name}.manifest.json\" \"\\${INSTALL_DIR}/\"\ncp \"\\${SCRIPT_DIR}/${name}\" \"\\${INSTALL_DIR}/\"\nchmod +x \"\\${INSTALL_DIR}/${name}\"\n\n${nativeAddons.length > 0 ? generateNativeAddonInstall(nativeAddons) : '# No native addons required'}\n\n${storageType === 'sqlite' ? generateSqliteSetup(name) : '# No storage setup required'}\n\n# Run questionnaire if manifest has setup and not silent\nif [ \"\\${SILENT}\" = false ] && command -v frontmcp &> /dev/null; then\n if node -e \"const m=require('\\${INSTALL_DIR}/${name}.manifest.json'); process.exit(m.setup ? 0 : 1)\" 2>/dev/null; then\n echo \"\"\n echo \"Running setup questionnaire...\"\n frontmcp configure \"\\${APP_NAME}\"\n fi\nfi\n\necho \"\"\necho \"Installed to \\${INSTALL_DIR}\"\necho \"\"\necho \"Start with:\"\necho \" frontmcp start \\${APP_NAME}\"\necho \"\"\n`;\n}\n\nfunction generateNativeAddonInstall(addons: string[]): string {\n const addonList = addons.map((a) => `\"${a}\"`).join(' ');\n return `# Install native addons\necho \"Installing native dependencies...\"\n(cd \"\\${INSTALL_DIR}\" && npm init -y --silent 2>/dev/null && npm install ${addonList} --save --silent)\n`;\n}\n\nfunction generateSqliteSetup(name: string): string {\n return `# Create SQLite data directory\nDATA_DIR=\"\\${HOME}/.frontmcp/data/${name}\"\nmkdir -p \"\\${DATA_DIR}\"\necho \"FRONTMCP_SQLITE_PATH=\\${DATA_DIR}/${name}.sqlite\" >> \"\\${INSTALL_DIR}/.env\"\n`;\n}\n\nfunction extractMinMajor(version: string): number {\n const match = version.match(/(\\d+)/);\n return match ? parseInt(match[1], 10) : 22;\n}\n"]}
1
+ {"version":3,"file":"installer-script.js","sourceRoot":"","sources":["../../../../../src/commands/build/exec/installer-script.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAYH,0DA6FC;AAtGD,mDAAuD;AASvD,SAAgB,uBAAuB,CACrC,MAA0B,EAC1B,UAAkC,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE;IAEvE,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;IACzB,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,EAAE,YAAY,IAAI,EAAE,CAAC;IAC7D,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,EAAE,IAAI,IAAI,MAAM,CAAC;IACnD,sEAAsE;IACtE,mEAAmE;IACnE,MAAM,WAAW,GAAG,IAAA,oCAAoB,EAAC,MAAM,CAAC,WAAW,IAAI,UAAU,CAAC,CAAC;IAC3E,MAAM,YAAY,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;IAElD,2EAA2E;IAC3E,0EAA0E;IAC1E,iCAAiC;IACjC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,KAAK,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC9D,MAAM,SAAS,GAAG,MAAM;QACtB,CAAC,CAAC,8EAA8E;QAChF,CAAC,CAAC;;;0BAGoB,WAAW;;;;;6BAKR,YAAY;yBAChB,WAAW;;GAEjC,CAAC;IAEF,oEAAoE;IACpE,yEAAyE;IACzE,MAAM,aAAa,GAAG,MAAM;QAC1B,CAAC,CAAC;qBACe,IAAI;4BACG,IAAI,WAAW;QACvC,CAAC,CAAC,0CAA0C,CAAC;IAE/C,OAAO;;;YAGG,IAAI;yCACyB,OAAO,CAAC,MAAM;;;YAG3C,IAAI;;;;;;;;;;;;;;EAcd,SAAS;;;;;;qBAMU,IAAI;qBACJ,IAAI;qBACJ,IAAI;4BACG,IAAI;;EAE9B,aAAa;;EAEb,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,0BAA0B,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,6BAA6B;;EAElG,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,6BAA6B;;;;iDAIrC,IAAI;;;;;;;;;;;;;CAapD,CAAC;AACF,CAAC;AAED,SAAS,0BAA0B,CAAC,MAAgB;IAClD,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxD,OAAO;;2EAEkE,SAAS;CACnF,CAAC;AACF,CAAC;AAED,SAAS,mBAAmB,CAAC,IAAY;IACvC,OAAO;oCAC2B,IAAI;;0CAEE,IAAI;CAC7C,CAAC;AACF,CAAC;AAED,SAAS,eAAe,CAAC,OAAe;IACtC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACrC,OAAO,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC7C,CAAC","sourcesContent":["/**\n * Bash installer script generation.\n * Supports interactive and --yes (silent) modes.\n */\n\nimport { type FrontmcpExecConfig } from './config';\nimport { sanitizeShellLiteral } from './runner-script';\n\nexport interface InstallerScriptContext {\n /** Build target this installer was generated for. Drives binary copying and Node version checks. */\n target: 'node' | 'cli';\n /** True when SEA binaries were emitted (CLI mode). Skips the Node version check at install time. */\n seaEnabled: boolean;\n}\n\nexport function generateInstallerScript(\n config: FrontmcpExecConfig,\n context: InstallerScriptContext = { target: 'node', seaEnabled: false },\n): string {\n const name = config.name;\n const nativeAddons = config.dependencies?.nativeAddons || [];\n const storageType = config.storage?.type || 'none';\n // Sanitize before interpolating into bash echo / error messages — the\n // user-controlled `nodeVersion` ends up in command lines verbatim.\n const nodeVersion = sanitizeShellLiteral(config.nodeVersion || '>=22.0.0');\n const minNodeMajor = extractMinMajor(nodeVersion);\n\n // CLI/SEA installs ship a self-contained binary — no Node runtime required\n // at install or run time. Skip the Node check entirely; the binary is the\n // whole point of `--target cli`.\n const seaCli = context.target === 'cli' && context.seaEnabled;\n const nodeCheck = seaCli\n ? '# CLI/SEA install: Node.js not required at runtime — skipping version check.'\n : `# Check Node.js\nif ! command -v node &> /dev/null; then\n echo \"Error: Node.js is required.\"\n echo \"Install Node.js ${nodeVersion}: https://nodejs.org\"\n exit 1\nfi\n\nNODE_MAJOR=$(node -e \"console.log(process.versions.node.split('.')[0])\")\nif [ \"\\${NODE_MAJOR}\" -lt \"${minNodeMajor}\" ]; then\n echo \"Error: Node.js ${nodeVersion} required, found v$(node -v)\"\n exit 1\nfi`;\n\n // For SEA-CLI installs, also copy the CLI binary the runner exec's.\n // Without this the installer leaves a runner pointing at a missing file.\n const seaBinaryCopy = seaCli\n ? `# Copy SEA binary that the runner exec's\ncp \"\\${SCRIPT_DIR}/${name}-cli-bin\" \"\\${INSTALL_DIR}/\"\nchmod +x \"\\${INSTALL_DIR}/${name}-cli-bin\"`\n : '# No SEA binary to copy (JS bundle mode)';\n\n return `#!/usr/bin/env bash\nset -euo pipefail\n\n# install-${name}.sh — FrontMCP App Installer\n# Generated by frontmcp build --target ${context.target}\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"\\${BASH_SOURCE[0]}\")\" && pwd)\"\nAPP_NAME=\"${name}\"\nINSTALL_DIR=\"\\${HOME}/.frontmcp/apps/\\${APP_NAME}\"\nSILENT=false\n\n# Parse args\nfor arg in \"$@\"; do\n case \"\\${arg}\" in\n --yes|-y) SILENT=true ;;\n esac\ndone\n\necho \"Installing \\${APP_NAME}...\"\necho \"\"\n\n${nodeCheck}\n\n# Create install directory\nmkdir -p \"\\${INSTALL_DIR}\"\n\n# Copy files\ncp \"\\${SCRIPT_DIR}/${name}.bundle.js\" \"\\${INSTALL_DIR}/\"\ncp \"\\${SCRIPT_DIR}/${name}.manifest.json\" \"\\${INSTALL_DIR}/\"\ncp \"\\${SCRIPT_DIR}/${name}\" \"\\${INSTALL_DIR}/\"\nchmod +x \"\\${INSTALL_DIR}/${name}\"\n\n${seaBinaryCopy}\n\n${nativeAddons.length > 0 ? generateNativeAddonInstall(nativeAddons) : '# No native addons required'}\n\n${storageType === 'sqlite' ? generateSqliteSetup(name) : '# No storage setup required'}\n\n# Run questionnaire if manifest has setup and not silent\nif [ \"\\${SILENT}\" = false ] && command -v frontmcp &> /dev/null; then\n if node -e \"const m=require('\\${INSTALL_DIR}/${name}.manifest.json'); process.exit(m.setup ? 0 : 1)\" 2>/dev/null; then\n echo \"\"\n echo \"Running setup questionnaire...\"\n frontmcp configure \"\\${APP_NAME}\"\n fi\nfi\n\necho \"\"\necho \"Installed to \\${INSTALL_DIR}\"\necho \"\"\necho \"Start with:\"\necho \" frontmcp start \\${APP_NAME}\"\necho \"\"\n`;\n}\n\nfunction generateNativeAddonInstall(addons: string[]): string {\n const addonList = addons.map((a) => `\"${a}\"`).join(' ');\n return `# Install native addons\necho \"Installing native dependencies...\"\n(cd \"\\${INSTALL_DIR}\" && npm init -y --silent 2>/dev/null && npm install ${addonList} --save --silent)\n`;\n}\n\nfunction generateSqliteSetup(name: string): string {\n return `# Create SQLite data directory\nDATA_DIR=\"\\${HOME}/.frontmcp/data/${name}\"\nmkdir -p \"\\${DATA_DIR}\"\necho \"FRONTMCP_SQLITE_PATH=\\${DATA_DIR}/${name}.sqlite\" >> \"\\${INSTALL_DIR}/.env\"\n`;\n}\n\nfunction extractMinMajor(version: string): number {\n const match = version.match(/(\\d+)/);\n return match ? parseInt(match[1], 10) : 22;\n}\n"]}
@@ -3,7 +3,7 @@
3
3
  * The manifest is a JSON file that describes the bundled app
4
4
  * for installation and runtime.
5
5
  */
6
- import { FrontmcpExecConfig } from './config';
6
+ import { type FrontmcpExecConfig } from './config';
7
7
  export interface ExecManifest {
8
8
  name: string;
9
9
  version: string;
@@ -12,7 +12,12 @@ export interface ExecManifest {
12
12
  type: 'sqlite' | 'redis' | 'none';
13
13
  required: boolean;
14
14
  };
15
- network: {
15
+ /**
16
+ * Server bind config. Omitted entirely for `--target cli` (CLI builds don't
17
+ * bind a port — the manifest is consumed by `frontmcp install`, container
18
+ * `EXPOSE`, and proxy generators).
19
+ */
20
+ network?: {
16
21
  defaultPort: number;
17
22
  supportsSocket: boolean;
18
23
  };
@@ -50,4 +55,12 @@ export interface ManifestSetupStep {
50
55
  next?: string | Record<string, string>;
51
56
  showWhen?: Record<string, string | string[]>;
52
57
  }
53
- export declare function generateManifest(config: FrontmcpExecConfig, bundleFilename: string): ExecManifest;
58
+ export interface GenerateManifestContext {
59
+ /** Build target the manifest is being written for. */
60
+ target?: 'node' | 'cli';
61
+ /** Port declared via `@FrontMcp({ http: { port } })` on the user's entry. */
62
+ decoratorHttpPort?: number;
63
+ /** `cli.outputDefault` from frontmcp.config.deployments[].cli (per-deployment). */
64
+ outputDefault?: 'text' | 'json';
65
+ }
66
+ export declare function generateManifest(config: FrontmcpExecConfig, bundleFilename: string, context?: GenerateManifestContext): ExecManifest;
@@ -7,7 +7,14 @@
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.generateManifest = generateManifest;
9
9
  const setup_1 = require("./setup");
10
- function generateManifest(config, bundleFilename) {
10
+ function generateManifest(config, bundleFilename, context = {}) {
11
+ // Resolve port precedence (highest → lowest):
12
+ // 1. `frontmcp.config.network.defaultPort` (build-config override)
13
+ // 2. `@FrontMcp({ http: { port } })` (decorator metadata)
14
+ // 3. 3000 (Express default)
15
+ // The legacy `3001` was a phantom default that didn't match what the
16
+ // server actually bound (#371).
17
+ const resolvedPort = config.network?.defaultPort ?? context.decoratorHttpPort ?? 3000;
11
18
  const manifest = {
12
19
  name: config.name,
13
20
  version: config.version || '1.0.0',
@@ -16,16 +23,21 @@ function generateManifest(config, bundleFilename) {
16
23
  type: config.storage?.type || 'none',
17
24
  required: config.storage?.required ?? false,
18
25
  },
19
- network: {
20
- defaultPort: config.network?.defaultPort || 3001,
21
- supportsSocket: config.network?.supportsSocket ?? true,
22
- },
23
26
  dependencies: {
24
27
  system: config.dependencies?.system || [],
25
28
  nativeAddons: config.dependencies?.nativeAddons || [],
26
29
  },
27
30
  bundle: bundleFilename,
28
31
  };
32
+ // Network section is server-build-specific. CLI builds don't bind a port,
33
+ // so emitting `network.defaultPort` confuses tooling that drives container
34
+ // `EXPOSE` / reverse proxies (#371 note).
35
+ if (context.target !== 'cli') {
36
+ manifest.network = {
37
+ defaultPort: resolvedPort,
38
+ supportsSocket: config.network?.supportsSocket ?? true,
39
+ };
40
+ }
29
41
  // Convert setup steps — serialize Zod schemas to JSON Schema
30
42
  if (config.setup?.steps && config.setup.steps.length > 0) {
31
43
  manifest.setup = {
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../../../src/commands/build/exec/manifest.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;AAqDH,4CA+BC;AAjFD,mCAAwE;AAkDxE,SAAgB,gBAAgB,CAC9B,MAA0B,EAC1B,cAAsB;IAEtB,MAAM,QAAQ,GAAiB;QAC7B,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,OAAO;QAClC,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,UAAU;QAC7C,OAAO,EAAE;YACP,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,IAAI,MAAM;YACpC,QAAQ,EAAE,MAAM,CAAC,OAAO,EAAE,QAAQ,IAAI,KAAK;SAC5C;QACD,OAAO,EAAE;YACP,WAAW,EAAE,MAAM,CAAC,OAAO,EAAE,WAAW,IAAI,IAAI;YAChD,cAAc,EAAE,MAAM,CAAC,OAAO,EAAE,cAAc,IAAI,IAAI;SACvD;QACD,YAAY,EAAE;YACZ,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,IAAI,EAAE;YACzC,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,YAAY,IAAI,EAAE;SACtD;QACD,MAAM,EAAE,cAAc;KACvB,CAAC;IAEF,6DAA6D;IAC7D,IAAI,MAAM,CAAC,KAAK,EAAE,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzD,QAAQ,CAAC,KAAK,GAAG;YACf,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;SAC7D,CAAC;IACJ,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,aAAa,CAAC,IAAe;IACpC,IAAI,UAAU,GAA4B,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAE7D,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;QACpB,8CAA8C;QAC9C,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;IAC/B,CAAC;SAAM,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QACvB,oCAAoC;QACpC,UAAU,GAAG,IAAA,6BAAqB,EAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IAED,OAAO;QACL,EAAE,EAAE,IAAI,CAAC,EAAE;QACX,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,UAAU;QACV,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,IAAA,mBAAW,EAAC,IAAI,CAAC,EAAE,CAAC;QACrC,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;KACxB,CAAC;AACJ,CAAC","sourcesContent":["/**\n * ExecManifest type + generation.\n * The manifest is a JSON file that describes the bundled app\n * for installation and runtime.\n */\n\nimport { FrontmcpExecConfig } from './config';\nimport { SetupStep, zodSchemaToJsonSchema, idToEnvName } from './setup';\n\nexport interface ExecManifest {\n name: string;\n version: string;\n nodeVersion: string;\n storage: {\n type: 'sqlite' | 'redis' | 'none';\n required: boolean;\n };\n network: {\n defaultPort: number;\n supportsSocket: boolean;\n };\n dependencies: {\n system: string[];\n nativeAddons: string[];\n };\n bundle: string; // bundle filename\n setup?: {\n steps: ManifestSetupStep[];\n };\n cli?: {\n enabled: boolean;\n cliBundle: string;\n outputDefault: 'text' | 'json';\n authRequired: boolean;\n toolCount: number;\n resourceCount: number;\n templateCount: number;\n promptCount: number;\n oauthEnabled: boolean;\n skillsEnabled?: boolean;\n jobsEnabled?: boolean;\n workflowsEnabled?: boolean;\n };\n}\n\nexport interface ManifestSetupStep {\n id: string;\n prompt: string;\n description?: string;\n jsonSchema: Record<string, unknown>;\n env: string;\n sensitive?: boolean;\n group?: string;\n next?: string | Record<string, string>;\n showWhen?: Record<string, string | string[]>;\n}\n\nexport function generateManifest(\n config: FrontmcpExecConfig,\n bundleFilename: string,\n): ExecManifest {\n const manifest: ExecManifest = {\n name: config.name,\n version: config.version || '1.0.0',\n nodeVersion: config.nodeVersion || '>=22.0.0',\n storage: {\n type: config.storage?.type || 'none',\n required: config.storage?.required ?? false,\n },\n network: {\n defaultPort: config.network?.defaultPort || 3001,\n supportsSocket: config.network?.supportsSocket ?? true,\n },\n dependencies: {\n system: config.dependencies?.system || [],\n nativeAddons: config.dependencies?.nativeAddons || [],\n },\n bundle: bundleFilename,\n };\n\n // Convert setup steps — serialize Zod schemas to JSON Schema\n if (config.setup?.steps && config.setup.steps.length > 0) {\n manifest.setup = {\n steps: config.setup.steps.map((step) => serializeStep(step)),\n };\n }\n\n return manifest;\n}\n\nfunction serializeStep(step: SetupStep): ManifestSetupStep {\n let jsonSchema: Record<string, unknown> = { type: 'string' };\n\n if (step.jsonSchema) {\n // Already serialized (e.g., from JSON config)\n jsonSchema = step.jsonSchema;\n } else if (step.schema) {\n // Convert Zod schema to JSON Schema\n jsonSchema = zodSchemaToJsonSchema(step.schema);\n }\n\n return {\n id: step.id,\n prompt: step.prompt,\n description: step.description,\n jsonSchema,\n env: step.env || idToEnvName(step.id),\n sensitive: step.sensitive,\n group: step.group,\n next: step.next,\n showWhen: step.showWhen,\n };\n}\n"]}
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../../../src/commands/build/exec/manifest.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;AAmEH,4CA8CC;AA9GD,mCAA6E;AAgE7E,SAAgB,gBAAgB,CAC9B,MAA0B,EAC1B,cAAsB,EACtB,UAAmC,EAAE;IAErC,8CAA8C;IAC9C,6EAA6E;IAC7E,0EAA0E;IAC1E,uEAAuE;IACvE,qEAAqE;IACrE,gCAAgC;IAChC,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,EAAE,WAAW,IAAI,OAAO,CAAC,iBAAiB,IAAI,IAAI,CAAC;IAEtF,MAAM,QAAQ,GAAiB;QAC7B,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,OAAO;QAClC,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,UAAU;QAC7C,OAAO,EAAE;YACP,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,IAAI,MAAM;YACpC,QAAQ,EAAE,MAAM,CAAC,OAAO,EAAE,QAAQ,IAAI,KAAK;SAC5C;QACD,YAAY,EAAE;YACZ,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,IAAI,EAAE;YACzC,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,YAAY,IAAI,EAAE;SACtD;QACD,MAAM,EAAE,cAAc;KACvB,CAAC;IAEF,0EAA0E;IAC1E,2EAA2E;IAC3E,0CAA0C;IAC1C,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;QAC7B,QAAQ,CAAC,OAAO,GAAG;YACjB,WAAW,EAAE,YAAY;YACzB,cAAc,EAAE,MAAM,CAAC,OAAO,EAAE,cAAc,IAAI,IAAI;SACvD,CAAC;IACJ,CAAC;IAED,6DAA6D;IAC7D,IAAI,MAAM,CAAC,KAAK,EAAE,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzD,QAAQ,CAAC,KAAK,GAAG;YACf,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;SAC7D,CAAC;IACJ,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,aAAa,CAAC,IAAe;IACpC,IAAI,UAAU,GAA4B,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAE7D,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;QACpB,8CAA8C;QAC9C,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;IAC/B,CAAC;SAAM,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QACvB,oCAAoC;QACpC,UAAU,GAAG,IAAA,6BAAqB,EAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IAED,OAAO;QACL,EAAE,EAAE,IAAI,CAAC,EAAE;QACX,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,UAAU;QACV,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,IAAA,mBAAW,EAAC,IAAI,CAAC,EAAE,CAAC;QACrC,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;KACxB,CAAC;AACJ,CAAC","sourcesContent":["/**\n * ExecManifest type + generation.\n * The manifest is a JSON file that describes the bundled app\n * for installation and runtime.\n */\n\nimport { type FrontmcpExecConfig } from './config';\nimport { type SetupStep, zodSchemaToJsonSchema, idToEnvName } from './setup';\n\nexport interface ExecManifest {\n name: string;\n version: string;\n nodeVersion: string;\n storage: {\n type: 'sqlite' | 'redis' | 'none';\n required: boolean;\n };\n /**\n * Server bind config. Omitted entirely for `--target cli` (CLI builds don't\n * bind a port — the manifest is consumed by `frontmcp install`, container\n * `EXPOSE`, and proxy generators).\n */\n network?: {\n defaultPort: number;\n supportsSocket: boolean;\n };\n dependencies: {\n system: string[];\n nativeAddons: string[];\n };\n bundle: string; // bundle filename\n setup?: {\n steps: ManifestSetupStep[];\n };\n cli?: {\n enabled: boolean;\n cliBundle: string;\n outputDefault: 'text' | 'json';\n authRequired: boolean;\n toolCount: number;\n resourceCount: number;\n templateCount: number;\n promptCount: number;\n oauthEnabled: boolean;\n skillsEnabled?: boolean;\n jobsEnabled?: boolean;\n workflowsEnabled?: boolean;\n };\n}\n\nexport interface ManifestSetupStep {\n id: string;\n prompt: string;\n description?: string;\n jsonSchema: Record<string, unknown>;\n env: string;\n sensitive?: boolean;\n group?: string;\n next?: string | Record<string, string>;\n showWhen?: Record<string, string | string[]>;\n}\n\nexport interface GenerateManifestContext {\n /** Build target the manifest is being written for. */\n target?: 'node' | 'cli';\n /** Port declared via `@FrontMcp({ http: { port } })` on the user's entry. */\n decoratorHttpPort?: number;\n /** `cli.outputDefault` from frontmcp.config.deployments[].cli (per-deployment). */\n outputDefault?: 'text' | 'json';\n}\n\nexport function generateManifest(\n config: FrontmcpExecConfig,\n bundleFilename: string,\n context: GenerateManifestContext = {},\n): ExecManifest {\n // Resolve port precedence (highest → lowest):\n // 1. `frontmcp.config.network.defaultPort` (build-config override)\n // 2. `@FrontMcp({ http: { port } })` (decorator metadata)\n // 3. 3000 (Express default)\n // The legacy `3001` was a phantom default that didn't match what the\n // server actually bound (#371).\n const resolvedPort = config.network?.defaultPort ?? context.decoratorHttpPort ?? 3000;\n\n const manifest: ExecManifest = {\n name: config.name,\n version: config.version || '1.0.0',\n nodeVersion: config.nodeVersion || '>=22.0.0',\n storage: {\n type: config.storage?.type || 'none',\n required: config.storage?.required ?? false,\n },\n dependencies: {\n system: config.dependencies?.system || [],\n nativeAddons: config.dependencies?.nativeAddons || [],\n },\n bundle: bundleFilename,\n };\n\n // Network section is server-build-specific. CLI builds don't bind a port,\n // so emitting `network.defaultPort` confuses tooling that drives container\n // `EXPOSE` / reverse proxies (#371 note).\n if (context.target !== 'cli') {\n manifest.network = {\n defaultPort: resolvedPort,\n supportsSocket: config.network?.supportsSocket ?? true,\n };\n }\n\n // Convert setup steps — serialize Zod schemas to JSON Schema\n if (config.setup?.steps && config.setup.steps.length > 0) {\n manifest.setup = {\n steps: config.setup.steps.map((step) => serializeStep(step)),\n };\n }\n\n return manifest;\n}\n\nfunction serializeStep(step: SetupStep): ManifestSetupStep {\n let jsonSchema: Record<string, unknown> = { type: 'string' };\n\n if (step.jsonSchema) {\n // Already serialized (e.g., from JSON config)\n jsonSchema = step.jsonSchema;\n } else if (step.schema) {\n // Convert Zod schema to JSON Schema\n jsonSchema = zodSchemaToJsonSchema(step.schema);\n }\n\n return {\n id: step.id,\n prompt: step.prompt,\n description: step.description,\n jsonSchema,\n env: step.env || idToEnvName(step.id),\n sensitive: step.sensitive,\n group: step.group,\n next: step.next,\n showWhen: step.showWhen,\n };\n}\n"]}
@@ -2,5 +2,13 @@
2
2
  * Bash runner script generation.
3
3
  * The runner checks Node.js, loads .env, and runs the bundle.
4
4
  */
5
- import { FrontmcpExecConfig } from './config';
5
+ import { type FrontmcpExecConfig } from './config';
6
+ /**
7
+ * Defense-in-depth: scrub anything outside `[a-zA-Z0-9._+-]` from values that
8
+ * the runner / installer scripts interpolate into bash. The user owns
9
+ * `frontmcp.config`, so a malicious value would be self-inflicted, but the
10
+ * generated scripts are committed into repos and downloaded by end-users —
11
+ * so we keep them safe to copy/paste regardless of upstream config hygiene.
12
+ */
13
+ export declare function sanitizeShellLiteral(value: string): string;
6
14
  export declare function generateRunnerScript(config: FrontmcpExecConfig, cliMode?: boolean, seaMode?: boolean): string;
@@ -4,9 +4,67 @@
4
4
  * The runner checks Node.js, loads .env, and runs the bundle.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.sanitizeShellLiteral = sanitizeShellLiteral;
7
8
  exports.generateRunnerScript = generateRunnerScript;
9
+ /**
10
+ * Defense-in-depth: scrub anything outside `[a-zA-Z0-9._+-]` from values that
11
+ * the runner / installer scripts interpolate into bash. The user owns
12
+ * `frontmcp.config`, so a malicious value would be self-inflicted, but the
13
+ * generated scripts are committed into repos and downloaded by end-users —
14
+ * so we keep them safe to copy/paste regardless of upstream config hygiene.
15
+ */
16
+ function sanitizeShellLiteral(value) {
17
+ // `-` placed last in the character class is literal, so no escape needed.
18
+ return value.replace(/[^A-Za-z0-9._+-]/g, '_');
19
+ }
8
20
  function generateRunnerScript(config, cliMode, seaMode) {
9
21
  const name = config.name;
22
+ const version = sanitizeShellLiteral(config.version || '0.0.0');
23
+ // #377 — `--target node` runner used to silently exec the bundle for any
24
+ // flag, so `./frontegg-bin --help` quietly booted the HTTP server. Intercept
25
+ // help/version here so server-mode runners behave like a normal CLI for
26
+ // those flags. CLI-mode runners pass everything through to the bundle's
27
+ // own commander parser.
28
+ const helpInterceptor = !cliMode
29
+ ? `
30
+ # Intercept standard CLI flags before booting the long-running server.
31
+ case "\${1:-}" in
32
+ -h|--help)
33
+ cat <<EOF
34
+ ${name} v${version} — FrontMCP server
35
+
36
+ This binary starts a long-running MCP HTTP server.
37
+
38
+ Usage:
39
+ ${name} Start the server
40
+ ${name} --help Show this help
41
+ ${name} --version Show version
42
+ ${name} --print-manifest Print the deployment manifest as JSON
43
+
44
+ Configure via environment variables, .env, or frontmcp.config.
45
+
46
+ For a CLI-style binary that exposes tools/resources/prompts as subcommands,
47
+ build with: frontmcp build --target cli
48
+ EOF
49
+ exit 0
50
+ ;;
51
+ --version)
52
+ echo "${name} ${version}"
53
+ exit 0
54
+ ;;
55
+ --print-manifest)
56
+ cat "\${SCRIPT_DIR}/${name}.manifest.json"
57
+ exit 0
58
+ ;;
59
+ --*)
60
+ echo "Error: unsupported flag '\${1}' on the server runner."
61
+ echo "This binary is a long-running HTTP server; flag-style invocation is reserved." >&2
62
+ echo "Run with no args to start, or build with --target cli for a CLI binary." >&2
63
+ exit 2
64
+ ;;
65
+ esac
66
+ `
67
+ : '';
10
68
  // SEA mode: binary is self-contained, no Node.js required
11
69
  if (seaMode) {
12
70
  const binary = cliMode ? `${name}-cli-bin` : `${name}-bin`;
@@ -26,7 +84,7 @@ if [ ! -f "\${BINARY}" ]; then
26
84
  echo "Error: Binary not found at \${BINARY}"
27
85
  exit 1
28
86
  fi
29
-
87
+ ${helpInterceptor}
30
88
  # Load .env if present
31
89
  ENV_FILE="\${SCRIPT_DIR}/.env"
32
90
  if [ -f "\${ENV_FILE}" ]; then
@@ -55,7 +113,7 @@ ${comment}
55
113
 
56
114
  SCRIPT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
57
115
  BUNDLE="${bundle}"
58
-
116
+ ${helpInterceptor}
59
117
  # Check Node.js
60
118
  if ! command -v node &> /dev/null; then
61
119
  echo "Error: Node.js is required but not installed."