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
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/commands/build/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Configuration for a deployment adapter.\n * Each adapter defines how to compile and package the FrontMCP server\n * for a specific deployment target.\n */\nexport type AdapterTemplate = {\n /** Module format for TypeScript compilation */\n moduleFormat: 'commonjs' | 'esnext';\n\n /**\n * Generate the entry point file content.\n * @param mainModulePath - Relative path to the compiled main module (e.g., './main.js')\n * @returns The content for index.js, or empty string if no wrapper needed\n */\n getEntryTemplate: (mainModulePath: string) => string;\n\n /**\n * Generate the serverless setup file content.\n * This file is imported first to set environment variables before decorators run.\n * @returns The content for serverless-setup.js, or undefined if not needed\n */\n getSetupTemplate?: () => string;\n\n /**\n * Whether to bundle the output with rspack.\n * Recommended for serverless deployments to avoid ESM/CJS issues.\n */\n shouldBundle?: boolean;\n\n /**\n * Output filename for the bundled file (e.g., 'handler.cjs').\n * Only used when shouldBundle is true.\n */\n bundleOutput?: string;\n\n /**\n * Generate the deployment platform config file content.\n * @param cwd - Current working directory (for detecting package manager, etc.)\n * @returns Object (for JSON) or string (for TOML/YAML)\n */\n getConfig?: (cwd: string) => object | string;\n\n /** Name of the config file (e.g., 'vercel.json', 'wrangler.toml') */\n configFileName?: string;\n\n /**\n * Post-bundle hook for creating deployment-specific output structure.\n * Called after bundling is complete.\n * @param outDir - The output directory (e.g., 'dist')\n * @param cwd - Current working directory\n * @param bundleOutput - Name of the bundled file (e.g., 'handler.cjs')\n */\n postBundle?: (outDir: string, cwd: string, bundleOutput: string) => Promise<void>;\n};\n\nexport type AdapterName = 'node' | 'vercel' | 'lambda' | 'cloudflare' | 'distributed';\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/commands/build/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Configuration for a deployment adapter.\n * Each adapter defines how to compile and package the FrontMCP server\n * for a specific deployment target.\n */\nexport type AdapterTemplate = {\n /** Module format for TypeScript compilation */\n moduleFormat: 'commonjs' | 'esnext';\n\n /**\n * Generate the entry point file content.\n * @param mainModulePath - Relative path to the compiled main module (e.g., './main.js')\n * @returns The content for index.js, or empty string if no wrapper needed\n */\n getEntryTemplate: (mainModulePath: string) => string;\n\n /**\n * Generate the serverless setup file content.\n * This file is imported first to set environment variables before decorators run.\n * @returns The content for serverless-setup.js, or undefined if not needed\n */\n getSetupTemplate?: () => string;\n\n /**\n * Whether to bundle the output with rspack.\n * Recommended for serverless deployments to avoid ESM/CJS issues.\n */\n shouldBundle?: boolean;\n\n /**\n * Output filename for the bundled file (e.g., 'handler.cjs').\n * Only used when shouldBundle is true.\n */\n bundleOutput?: string;\n\n /**\n * Generate the deployment platform config file content.\n * @param cwd - Current working directory (for detecting package manager, etc.)\n * @returns Object (for JSON) or string (for TOML/YAML)\n */\n getConfig?: (cwd: string) => object | string;\n\n /** Name of the config file (e.g., 'vercel.json', 'wrangler.toml') */\n configFileName?: string;\n\n /**\n * Post-bundle hook for creating deployment-specific output structure.\n * Called after bundling is complete.\n * @param outDir - The output directory (e.g., 'dist')\n * @param cwd - Current working directory\n * @param bundleOutput - Name of the bundled file (e.g., 'handler.cjs')\n */\n postBundle?: (outDir: string, cwd: string, bundleOutput: string) => Promise<void>;\n\n /**\n * Pre-build validation hook. Runs after schema/decorator extraction but\n * before TypeScript compilation. Allows the adapter to fail loudly when\n * the user's config references runtime features that won't work on the\n * target platform (e.g., sqlite on Cloudflare Workers).\n *\n * @param decoratorConfig - Best-effort `__frontmcp:config` metadata\n * extracted from the entry's @FrontMcp() decorator. May be undefined\n * when the entry exports a plain config object.\n * @throws to abort the build with a user-facing message.\n */\n validate?: (decoratorConfig: Record<string, unknown> | undefined) => void;\n\n /**\n * Whether `getConfig()` output should overwrite an existing config file\n * (e.g., wrangler.toml) on every build. When false, an existing file is\n * left untouched but its contents are diffed against the build output and\n * the build fails on mismatch (#374). Default: false (preserve existing).\n */\n alwaysWriteConfig?: boolean;\n};\n\nexport type AdapterName = 'node' | 'vercel' | 'lambda' | 'cloudflare' | 'distributed';\n"]}
@@ -2,10 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runDoctor = runDoctor;
4
4
  const tslib_1 = require("tslib");
5
- const path = tslib_1.__importStar(require("path"));
6
5
  const child_process_1 = require("child_process");
7
- const colors_1 = require("../../core/colors");
6
+ const path = tslib_1.__importStar(require("path"));
8
7
  const utils_1 = require("@frontmcp/utils");
8
+ const colors_1 = require("../../core/colors");
9
9
  const tsconfig_1 = require("../../core/tsconfig");
10
10
  const fs_1 = require("../../shared/fs");
11
11
  function cmpSemver(a, b) {
@@ -35,7 +35,11 @@ async function runDoctor() {
35
35
  let npmVer = 'unknown';
36
36
  try {
37
37
  npmVer = await new Promise((resolve, reject) => {
38
- const child = (0, child_process_1.spawn)('npm', ['-v'], { shell: true });
38
+ // shell:true triggers Node DEP0190 (deprecated unescaped-arg passing).
39
+ // Use explicit binary names: npm.cmd on win32, npm elsewhere — args
40
+ // already in array form, no shell needed.
41
+ const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
42
+ const child = (0, child_process_1.spawn)(npmCmd, ['-v']);
39
43
  let out = '';
40
44
  child.stdout?.on('data', (d) => (out += String(d)));
41
45
  child.on('close', () => resolve(out.trim()));
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../../../src/commands/dev/doctor.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path';\nimport { spawn } from 'child_process';\nimport { c } from '../../core/colors';\nimport { fileExists, readJSON } from '@frontmcp/utils';\nimport { checkRequiredTsOptions } from '../../core/tsconfig';\nimport { resolveEntry } from '../../shared/fs';\n\nfunction cmpSemver(a: string, b: string): number {\n const pa = a.split('.').map((n) => parseInt(n, 10) || 0);\n const pb = b.split('.').map((n) => parseInt(n, 10) || 0);\n for (let i = 0; i < 3; i++) {\n if ((pa[i] || 0) > (pb[i] || 0)) return 1;\n if ((pa[i] || 0) < (pb[i] || 0)) return -1;\n }\n return 0;\n}\n\nexport async function runDoctor(): Promise<void> {\n const MIN_NODE = '22.0.0';\n const MIN_NPM = '10.0.0';\n const cwd = process.cwd();\n\n let ok = true;\n\n const nodeVer = process.versions.node;\n if (cmpSemver(nodeVer, MIN_NODE) >= 0) {\n console.log(`✅ Node ${nodeVer} (min ${MIN_NODE})`);\n } else {\n ok = false;\n console.log(`❌ Node ${nodeVer} — please upgrade to >= ${MIN_NODE}`);\n }\n\n let npmVer = 'unknown';\n try {\n npmVer = await new Promise<string>((resolve, reject) => {\n const child = spawn('npm', ['-v'], { shell: true });\n let out = '';\n child.stdout?.on('data', (d) => (out += String(d)));\n child.on('close', () => resolve(out.trim()));\n child.on('error', reject);\n });\n if (cmpSemver(npmVer, MIN_NPM) >= 0) {\n console.log(`✅ npm ${npmVer} (min ${MIN_NPM})`);\n } else {\n ok = false;\n console.log(`❌ npm ${npmVer} — please upgrade to >= ${MIN_NPM}`);\n }\n } catch {\n ok = false;\n console.log('❌ npm not found in PATH');\n }\n\n const tsconfigPath = path.join(cwd, 'tsconfig.json');\n if (await fileExists(tsconfigPath)) {\n console.log(`✅ tsconfig.json found`);\n const tsconfig = await readJSON<Record<string, any>>(tsconfigPath);\n const { ok: oks, issues } = checkRequiredTsOptions(tsconfig?.compilerOptions);\n for (const line of oks) console.log(c('green', ` ✓ ${line}`));\n if (issues.length) {\n ok = false;\n for (const line of issues) console.log(c('yellow', ` • ${line}`));\n console.log(c('cyan', ` -> Run \"frontmcp init\" to apply the required settings.`));\n }\n } else {\n ok = false;\n console.log(`❌ tsconfig.json not found — run ${c('cyan', 'frontmcp init')}`);\n }\n\n try {\n const entry = await resolveEntry(cwd);\n console.log(`✅ entry detected: ${path.relative(cwd, entry)}`);\n } catch (e: unknown) {\n ok = false;\n const firstLine = e instanceof Error ? (e.message.split('\\n')[0] ?? 'entry not found') : 'entry not found';\n console.log(`❌ entry not detected — ${firstLine}`);\n }\n\n if (ok) console.log(c('green', '\\nAll checks passed. You are ready to go!'));\n else console.log(c('yellow', '\\nSome checks failed. See above for fixes.'));\n}\n"]}
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../../../src/commands/dev/doctor.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from 'child_process';\nimport * as path from 'path';\n\nimport { fileExists, readJSON } from '@frontmcp/utils';\n\nimport { c } from '../../core/colors';\nimport { checkRequiredTsOptions } from '../../core/tsconfig';\nimport { resolveEntry } from '../../shared/fs';\n\nfunction cmpSemver(a: string, b: string): number {\n const pa = a.split('.').map((n) => parseInt(n, 10) || 0);\n const pb = b.split('.').map((n) => parseInt(n, 10) || 0);\n for (let i = 0; i < 3; i++) {\n if ((pa[i] || 0) > (pb[i] || 0)) return 1;\n if ((pa[i] || 0) < (pb[i] || 0)) return -1;\n }\n return 0;\n}\n\nexport async function runDoctor(): Promise<void> {\n const MIN_NODE = '22.0.0';\n const MIN_NPM = '10.0.0';\n const cwd = process.cwd();\n\n let ok = true;\n\n const nodeVer = process.versions.node;\n if (cmpSemver(nodeVer, MIN_NODE) >= 0) {\n console.log(`✅ Node ${nodeVer} (min ${MIN_NODE})`);\n } else {\n ok = false;\n console.log(`❌ Node ${nodeVer} — please upgrade to >= ${MIN_NODE}`);\n }\n\n let npmVer = 'unknown';\n try {\n npmVer = await new Promise<string>((resolve, reject) => {\n // shell:true triggers Node DEP0190 (deprecated unescaped-arg passing).\n // Use explicit binary names: npm.cmd on win32, npm elsewhere — args\n // already in array form, no shell needed.\n const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';\n const child = spawn(npmCmd, ['-v']);\n let out = '';\n child.stdout?.on('data', (d) => (out += String(d)));\n child.on('close', () => resolve(out.trim()));\n child.on('error', reject);\n });\n if (cmpSemver(npmVer, MIN_NPM) >= 0) {\n console.log(`✅ npm ${npmVer} (min ${MIN_NPM})`);\n } else {\n ok = false;\n console.log(`❌ npm ${npmVer} — please upgrade to >= ${MIN_NPM}`);\n }\n } catch {\n ok = false;\n console.log('❌ npm not found in PATH');\n }\n\n const tsconfigPath = path.join(cwd, 'tsconfig.json');\n if (await fileExists(tsconfigPath)) {\n console.log(`✅ tsconfig.json found`);\n const tsconfig = await readJSON<Record<string, any>>(tsconfigPath);\n const { ok: oks, issues } = checkRequiredTsOptions(tsconfig?.compilerOptions);\n for (const line of oks) console.log(c('green', ` ✓ ${line}`));\n if (issues.length) {\n ok = false;\n for (const line of issues) console.log(c('yellow', ` • ${line}`));\n console.log(c('cyan', ` -> Run \"frontmcp init\" to apply the required settings.`));\n }\n } else {\n ok = false;\n console.log(`❌ tsconfig.json not found — run ${c('cyan', 'frontmcp init')}`);\n }\n\n try {\n const entry = await resolveEntry(cwd);\n console.log(`✅ entry detected: ${path.relative(cwd, entry)}`);\n } catch (e: unknown) {\n ok = false;\n const firstLine = e instanceof Error ? (e.message.split('\\n')[0] ?? 'entry not found') : 'entry not found';\n console.log(`❌ entry not detected — ${firstLine}`);\n }\n\n if (ok) console.log(c('green', '\\nAll checks passed. You are ready to go!'));\n else console.log(c('yellow', '\\nSome checks failed. See above for fixes.'));\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * runInstall() orchestrator — installs an MCP app from npm, local, or git source.
3
3
  */
4
- import { ParsedArgs } from '../../core/args';
4
+ import { type ParsedArgs } from '../../core/args';
5
5
  export declare function runInstall(opts: ParsedArgs): Promise<void>;
@@ -5,18 +5,18 @@
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.runInstall = runInstall;
7
7
  const tslib_1 = require("tslib");
8
- const path = tslib_1.__importStar(require("path"));
9
8
  const fs = tslib_1.__importStar(require("fs"));
10
9
  const os = tslib_1.__importStar(require("os"));
10
+ const path = tslib_1.__importStar(require("path"));
11
+ const utils_1 = require("@frontmcp/utils");
11
12
  const colors_1 = require("../../core/colors");
12
13
  const paths_1 = require("../pm/paths");
13
- const types_1 = require("./types");
14
- const registry_1 = require("./registry");
15
14
  const questionnaire_1 = require("./questionnaire");
16
- const npm_1 = require("./sources/npm");
17
- const local_1 = require("./sources/local");
15
+ const registry_1 = require("./registry");
18
16
  const git_1 = require("./sources/git");
19
- const utils_1 = require("@frontmcp/utils");
17
+ const local_1 = require("./sources/local");
18
+ const npm_1 = require("./sources/npm");
19
+ const types_1 = require("./types");
20
20
  async function runInstall(opts) {
21
21
  const sourceStr = opts._[1];
22
22
  if (!sourceStr) {
@@ -99,8 +99,10 @@ async function runInstall(opts) {
99
99
  (0, questionnaire_1.writeEnvFile)(installDir, result.envContent);
100
100
  console.log(`${(0, colors_1.c)('green', '[install]')} configuration saved to .env`);
101
101
  }
102
- // 8. Register in registry
103
- const port = opts.port || manifestData.network.defaultPort;
102
+ // 8. Register in registry. CLI builds omit the network section (they don't
103
+ // bind a port), so fall back to undefined when --port wasn't passed.
104
+ // Use `??` (not `||`) so an explicit `--port 0` overrides the manifest.
105
+ const port = opts.port ?? manifestData.network?.defaultPort;
104
106
  (0, registry_1.registerApp)(manifestData.name, {
105
107
  version: manifestData.version,
106
108
  installDir,
@@ -1 +1 @@
1
- {"version":3,"file":"install.js","sourceRoot":"","sources":["../../../../src/commands/package/install.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * runInstall() orchestrator — installs an MCP app from npm, local, or git source.\n */\n\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport * as os from 'os';\nimport { ParsedArgs } from '../../core/args';\nimport { c } from '../../core/colors';\nimport { appDir, ensurePmDirs } from '../pm/paths';\nimport { parseInstallSource } from './types';\nimport { registerApp } from './registry';\nimport { runQuestionnaire, writeEnvFile } from './questionnaire';\nimport { fetchFromNpm } from './sources/npm';\nimport { fetchFromLocal } from './sources/local';\nimport { fetchFromGit } from './sources/git';\nimport { ExecManifest } from '../build/exec/manifest';\nimport { runCmd } from '@frontmcp/utils';\n\nexport async function runInstall(opts: ParsedArgs): Promise<void> {\n const sourceStr = opts._[1];\n if (!sourceStr) {\n throw new Error('Missing install source. Usage: frontmcp install <npm-package|./local-path|github:user/repo>');\n }\n\n const source = parseInstallSource(sourceStr);\n console.log(`${c('cyan', '[install]')} source: ${source.type} → ${source.ref}`);\n\n // Create temp directory\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'frontmcp-install-'));\n\n try {\n // 1. Fetch to temp directory\n console.log(`${c('cyan', '[install]')} fetching...`);\n let packageDir: string;\n\n switch (source.type) {\n case 'npm':\n packageDir = await fetchFromNpm(source.ref, tmpDir, opts.registry);\n break;\n case 'local':\n packageDir = await fetchFromLocal(source.ref, tmpDir);\n break;\n case 'git':\n packageDir = await fetchFromGit(source.ref, tmpDir);\n break;\n case 'esm':\n throw new Error(\n 'ESM sources cannot be installed via \"frontmcp install\". ' +\n 'Use loadFrom() in your FrontMcp config to load ESM packages at runtime.',\n );\n }\n\n // 2. Look for manifest\n let manifest = findManifest(packageDir);\n\n // 3. If no manifest, check for frontmcp.config.js and build\n if (!manifest) {\n const configPath = path.join(packageDir, 'frontmcp.config.js');\n const configJsonPath = path.join(packageDir, 'frontmcp.config.json');\n\n if (fs.existsSync(configPath) || fs.existsSync(configJsonPath)) {\n console.log(`${c('cyan', '[install]')} no manifest found, building from config...`);\n await runCmd('npx', ['frontmcp', 'build', '--target', 'node'], {\n cwd: packageDir,\n });\n manifest = findManifest(path.join(packageDir, 'dist')) || findManifest(packageDir);\n }\n }\n\n if (!manifest) {\n throw new Error(\n 'Could not find or generate a manifest. Ensure the package has a ' +\n 'frontmcp.config.js (or frontmcp.config.json) or was built with \"frontmcp build --target node\".',\n );\n }\n\n const { data: manifestData, dir: manifestDir } = manifest;\n\n // 4. Install to ~/.frontmcp/apps/{name}/\n const installDir = appDir(manifestData.name);\n ensurePmDirs();\n fs.mkdirSync(installDir, { recursive: true });\n\n console.log(`${c('cyan', '[install]')} installing \"${manifestData.name}\" to ${installDir}`);\n\n // Copy bundle + manifest + runner\n copyIfExists(manifestDir, installDir, manifestData.bundle);\n copyIfExists(manifestDir, installDir, `${manifestData.name}.manifest.json`);\n copyIfExists(manifestDir, installDir, manifestData.name);\n\n // Make runner executable\n const runnerPath = path.join(installDir, manifestData.name);\n if (fs.existsSync(runnerPath)) {\n fs.chmodSync(runnerPath, 0o755);\n }\n\n // 5. Install native addons\n if (manifestData.dependencies.nativeAddons.length > 0) {\n console.log(`${c('cyan', '[install]')} installing native dependencies...`);\n await runCmd('npm', ['init', '-y', '--silent'], { cwd: installDir });\n await runCmd('npm', ['install', ...manifestData.dependencies.nativeAddons, '--save', '--silent'], {\n cwd: installDir,\n });\n }\n\n // 6. Set up SQLite data dir if needed\n if (manifestData.storage.type === 'sqlite') {\n const dataDir = path.join(os.homedir(), '.frontmcp', 'data', manifestData.name);\n fs.mkdirSync(dataDir, { recursive: true });\n }\n\n // 7. Run setup questionnaire\n if (manifestData.setup?.steps && manifestData.setup.steps.length > 0) {\n console.log(`\\n${c('bold', 'Setup Configuration')}`);\n const result = await runQuestionnaire(manifestData.setup.steps, {\n silent: opts.yes,\n });\n writeEnvFile(installDir, result.envContent);\n console.log(`${c('green', '[install]')} configuration saved to .env`);\n }\n\n // 8. Register in registry\n const port = opts.port || manifestData.network.defaultPort;\n registerApp(manifestData.name, {\n version: manifestData.version,\n installDir,\n installedAt: new Date().toISOString(),\n runner: path.join(installDir, manifestData.name),\n bundle: path.join(installDir, manifestData.bundle),\n storage: manifestData.storage.type,\n port,\n source: { type: source.type, ref: source.ref },\n });\n\n console.log(`\\n${c('green', `Installed \"${manifestData.name}\" successfully.`)}`);\n console.log(`\\n${c('bold', 'Start with:')}`);\n console.log(` frontmcp start ${manifestData.name}`);\n console.log(`\\n${c('bold', 'Reconfigure:')}`);\n console.log(` frontmcp configure ${manifestData.name}`);\n } finally {\n // Clean up temp directory\n try {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n } catch {\n // ignore cleanup errors\n }\n }\n}\n\nfunction findManifest(dir: string): { data: ExecManifest; dir: string } | null {\n if (!fs.existsSync(dir)) return null;\n\n const files = fs.readdirSync(dir);\n const manifestFile = files.find((f: string) => f.endsWith('.manifest.json'));\n\n if (manifestFile) {\n const manifestPath = path.join(dir, manifestFile);\n const data = JSON.parse(fs.readFileSync(manifestPath, 'utf-8')) as ExecManifest;\n return { data, dir };\n }\n\n // Check dist/ subdirectory\n const distDir = path.join(dir, 'dist');\n if (fs.existsSync(distDir)) {\n return findManifest(distDir);\n }\n\n return null;\n}\n\nfunction copyIfExists(fromDir: string, toDir: string, filename: string): void {\n const src = path.join(fromDir, filename);\n if (fs.existsSync(src)) {\n fs.copyFileSync(src, path.join(toDir, filename));\n }\n}\n"]}
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../../../src/commands/package/install.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * runInstall() orchestrator — installs an MCP app from npm, local, or git source.\n */\n\nimport * as fs from 'fs';\nimport * as os from 'os';\nimport * as path from 'path';\n\nimport { runCmd } from '@frontmcp/utils';\n\nimport { type ParsedArgs } from '../../core/args';\nimport { c } from '../../core/colors';\nimport { type ExecManifest } from '../build/exec/manifest';\nimport { appDir, ensurePmDirs } from '../pm/paths';\nimport { runQuestionnaire, writeEnvFile } from './questionnaire';\nimport { registerApp } from './registry';\nimport { fetchFromGit } from './sources/git';\nimport { fetchFromLocal } from './sources/local';\nimport { fetchFromNpm } from './sources/npm';\nimport { parseInstallSource } from './types';\n\nexport async function runInstall(opts: ParsedArgs): Promise<void> {\n const sourceStr = opts._[1];\n if (!sourceStr) {\n throw new Error('Missing install source. Usage: frontmcp install <npm-package|./local-path|github:user/repo>');\n }\n\n const source = parseInstallSource(sourceStr);\n console.log(`${c('cyan', '[install]')} source: ${source.type} → ${source.ref}`);\n\n // Create temp directory\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'frontmcp-install-'));\n\n try {\n // 1. Fetch to temp directory\n console.log(`${c('cyan', '[install]')} fetching...`);\n let packageDir: string;\n\n switch (source.type) {\n case 'npm':\n packageDir = await fetchFromNpm(source.ref, tmpDir, opts.registry);\n break;\n case 'local':\n packageDir = await fetchFromLocal(source.ref, tmpDir);\n break;\n case 'git':\n packageDir = await fetchFromGit(source.ref, tmpDir);\n break;\n case 'esm':\n throw new Error(\n 'ESM sources cannot be installed via \"frontmcp install\". ' +\n 'Use loadFrom() in your FrontMcp config to load ESM packages at runtime.',\n );\n }\n\n // 2. Look for manifest\n let manifest = findManifest(packageDir);\n\n // 3. If no manifest, check for frontmcp.config.js and build\n if (!manifest) {\n const configPath = path.join(packageDir, 'frontmcp.config.js');\n const configJsonPath = path.join(packageDir, 'frontmcp.config.json');\n\n if (fs.existsSync(configPath) || fs.existsSync(configJsonPath)) {\n console.log(`${c('cyan', '[install]')} no manifest found, building from config...`);\n await runCmd('npx', ['frontmcp', 'build', '--target', 'node'], {\n cwd: packageDir,\n });\n manifest = findManifest(path.join(packageDir, 'dist')) || findManifest(packageDir);\n }\n }\n\n if (!manifest) {\n throw new Error(\n 'Could not find or generate a manifest. Ensure the package has a ' +\n 'frontmcp.config.js (or frontmcp.config.json) or was built with \"frontmcp build --target node\".',\n );\n }\n\n const { data: manifestData, dir: manifestDir } = manifest;\n\n // 4. Install to ~/.frontmcp/apps/{name}/\n const installDir = appDir(manifestData.name);\n ensurePmDirs();\n fs.mkdirSync(installDir, { recursive: true });\n\n console.log(`${c('cyan', '[install]')} installing \"${manifestData.name}\" to ${installDir}`);\n\n // Copy bundle + manifest + runner\n copyIfExists(manifestDir, installDir, manifestData.bundle);\n copyIfExists(manifestDir, installDir, `${manifestData.name}.manifest.json`);\n copyIfExists(manifestDir, installDir, manifestData.name);\n\n // Make runner executable\n const runnerPath = path.join(installDir, manifestData.name);\n if (fs.existsSync(runnerPath)) {\n fs.chmodSync(runnerPath, 0o755);\n }\n\n // 5. Install native addons\n if (manifestData.dependencies.nativeAddons.length > 0) {\n console.log(`${c('cyan', '[install]')} installing native dependencies...`);\n await runCmd('npm', ['init', '-y', '--silent'], { cwd: installDir });\n await runCmd('npm', ['install', ...manifestData.dependencies.nativeAddons, '--save', '--silent'], {\n cwd: installDir,\n });\n }\n\n // 6. Set up SQLite data dir if needed\n if (manifestData.storage.type === 'sqlite') {\n const dataDir = path.join(os.homedir(), '.frontmcp', 'data', manifestData.name);\n fs.mkdirSync(dataDir, { recursive: true });\n }\n\n // 7. Run setup questionnaire\n if (manifestData.setup?.steps && manifestData.setup.steps.length > 0) {\n console.log(`\\n${c('bold', 'Setup Configuration')}`);\n const result = await runQuestionnaire(manifestData.setup.steps, {\n silent: opts.yes,\n });\n writeEnvFile(installDir, result.envContent);\n console.log(`${c('green', '[install]')} configuration saved to .env`);\n }\n\n // 8. Register in registry. CLI builds omit the network section (they don't\n // bind a port), so fall back to undefined when --port wasn't passed.\n // Use `??` (not `||`) so an explicit `--port 0` overrides the manifest.\n const port = opts.port ?? manifestData.network?.defaultPort;\n registerApp(manifestData.name, {\n version: manifestData.version,\n installDir,\n installedAt: new Date().toISOString(),\n runner: path.join(installDir, manifestData.name),\n bundle: path.join(installDir, manifestData.bundle),\n storage: manifestData.storage.type,\n port,\n source: { type: source.type, ref: source.ref },\n });\n\n console.log(`\\n${c('green', `Installed \"${manifestData.name}\" successfully.`)}`);\n console.log(`\\n${c('bold', 'Start with:')}`);\n console.log(` frontmcp start ${manifestData.name}`);\n console.log(`\\n${c('bold', 'Reconfigure:')}`);\n console.log(` frontmcp configure ${manifestData.name}`);\n } finally {\n // Clean up temp directory\n try {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n } catch {\n // ignore cleanup errors\n }\n }\n}\n\nfunction findManifest(dir: string): { data: ExecManifest; dir: string } | null {\n if (!fs.existsSync(dir)) return null;\n\n const files = fs.readdirSync(dir);\n const manifestFile = files.find((f: string) => f.endsWith('.manifest.json'));\n\n if (manifestFile) {\n const manifestPath = path.join(dir, manifestFile);\n const data = JSON.parse(fs.readFileSync(manifestPath, 'utf-8')) as ExecManifest;\n return { data, dir };\n }\n\n // Check dist/ subdirectory\n const distDir = path.join(dir, 'dist');\n if (fs.existsSync(distDir)) {\n return findManifest(distDir);\n }\n\n return null;\n}\n\nfunction copyIfExists(fromDir: string, toDir: string, filename: string): void {\n const src = path.join(fromDir, filename);\n if (fs.existsSync(src)) {\n fs.copyFileSync(src, path.join(toDir, filename));\n }\n}\n"]}
@@ -15,7 +15,8 @@ export interface FrontmcpRegistry {
15
15
  runner: string;
16
16
  bundle: string;
17
17
  storage: 'sqlite' | 'redis' | 'none';
18
- port: number;
18
+ /** Server port. Optional because CLI builds (no network) don't bind a port. */
19
+ port?: number;
19
20
  source?: {
20
21
  type: InstallSourceType;
21
22
  ref: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/commands/package/types.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAuCH,gDAeC;AAtBD;;;;;;GAMG;AACH,SAAgB,kBAAkB,CAAC,MAAc;IAC/C,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAClF,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;IACxC,CAAC;IAED,iHAAiH;IACjH,IAAI,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACtE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC;IAC3D,CAAC;IAED,IAAI,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACzF,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;IACtC,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;AACtC,CAAC","sourcesContent":["/**\n * Install source parsing and types.\n */\n\nexport type InstallSourceType = 'npm' | 'local' | 'git' | 'esm';\n\nexport interface InstallSource {\n type: InstallSourceType;\n ref: string;\n}\n\nexport interface FrontmcpRegistry {\n version: 1;\n apps: Record<\n string,\n {\n version: string;\n installDir: string;\n installedAt: string;\n runner: string;\n bundle: string;\n storage: 'sqlite' | 'redis' | 'none';\n port: number;\n source?: { type: InstallSourceType; ref: string };\n /** Resolved ESM version (for ESM-loaded apps) */\n esmVersion?: string;\n /** Path to cached ESM bundle */\n esmCachePath?: string;\n /** ISO timestamp of last update check */\n lastUpdateCheck?: string;\n }\n >;\n}\n\n/**\n * Parse an install source string into a typed source object.\n *\n * - Starts with `./ | ../ | /` → local\n * - Starts with `github:` or `git+` or ends with `.git` → git\n * - Everything else → npm\n */\nexport function parseInstallSource(source: string): InstallSource {\n if (source.startsWith('./') || source.startsWith('../') || source.startsWith('/')) {\n return { type: 'local', ref: source };\n }\n\n // ESM sources: explicit esm.sh URL or esm: prefix (checked before git to avoid esm:...pkg.git misclassification)\n if (source.startsWith('https://esm.sh/') || source.startsWith('esm:')) {\n return { type: 'esm', ref: source.replace(/^esm:/, '') };\n }\n\n if (source.startsWith('github:') || source.startsWith('git+') || source.endsWith('.git')) {\n return { type: 'git', ref: source };\n }\n\n return { type: 'npm', ref: source };\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/commands/package/types.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAwCH,gDAeC;AAtBD;;;;;;GAMG;AACH,SAAgB,kBAAkB,CAAC,MAAc;IAC/C,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAClF,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;IACxC,CAAC;IAED,iHAAiH;IACjH,IAAI,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACtE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC;IAC3D,CAAC;IAED,IAAI,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACzF,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;IACtC,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;AACtC,CAAC","sourcesContent":["/**\n * Install source parsing and types.\n */\n\nexport type InstallSourceType = 'npm' | 'local' | 'git' | 'esm';\n\nexport interface InstallSource {\n type: InstallSourceType;\n ref: string;\n}\n\nexport interface FrontmcpRegistry {\n version: 1;\n apps: Record<\n string,\n {\n version: string;\n installDir: string;\n installedAt: string;\n runner: string;\n bundle: string;\n storage: 'sqlite' | 'redis' | 'none';\n /** Server port. Optional because CLI builds (no network) don't bind a port. */\n port?: number;\n source?: { type: InstallSourceType; ref: string };\n /** Resolved ESM version (for ESM-loaded apps) */\n esmVersion?: string;\n /** Path to cached ESM bundle */\n esmCachePath?: string;\n /** ISO timestamp of last update check */\n lastUpdateCheck?: string;\n }\n >;\n}\n\n/**\n * Parse an install source string into a typed source object.\n *\n * - Starts with `./ | ../ | /` → local\n * - Starts with `github:` or `git+` or ends with `.git` → git\n * - Everything else → npm\n */\nexport function parseInstallSource(source: string): InstallSource {\n if (source.startsWith('./') || source.startsWith('../') || source.startsWith('/')) {\n return { type: 'local', ref: source };\n }\n\n // ESM sources: explicit esm.sh URL or esm: prefix (checked before git to avoid esm:...pkg.git misclassification)\n if (source.startsWith('https://esm.sh/') || source.startsWith('esm:')) {\n return { type: 'esm', ref: source.replace(/^esm:/, '') };\n }\n\n if (source.startsWith('github:') || source.startsWith('git+') || source.endsWith('.git')) {\n return { type: 'git', ref: source };\n }\n\n return { type: 'npm', ref: source };\n}\n"]}
@@ -18,6 +18,26 @@ import type { DeploymentTarget } from './frontmcp-config.types';
18
18
  * 6. Derive from package.json (minimal config with 'node' target)
19
19
  */
20
20
  export declare function loadFrontMcpConfig(cwd: string): Promise<FrontMcpConfigParsed>;
21
+ /**
22
+ * Variant that load-errors propagate (parse failures in `frontmcp.config.ts`,
23
+ * missing dependencies, etc.) but schema-validation errors return `undefined`.
24
+ *
25
+ * Used by `runBuild` to support both shapes: the new top-level
26
+ * `frontmcpConfigSchema` (with `deployments`) and the older exec-only shape
27
+ * (top-level `cli`, `sea`, `esbuild`) that `loadExecConfig` consumes
28
+ * directly. A user with the old shape should still get a successful build
29
+ * — the exec-loader picks the file up from disk by itself.
30
+ *
31
+ * Returns `undefined` when:
32
+ * - no config file is present (caller falls back to CLI flags), or
33
+ * - the file loads but doesn't match the new schema (legacy shape).
34
+ *
35
+ * Throws when:
36
+ * - the file exists but can't be parsed (TS syntax error, ESM/CJS mismatch
37
+ * that even esbuild can't recover from, etc.) — i.e., #365 silent-default
38
+ * regressions.
39
+ */
40
+ export declare function tryLoadFrontMcpConfig(cwd: string): Promise<FrontMcpConfigParsed | undefined>;
21
41
  /**
22
42
  * Validate raw config against the Zod schema.
23
43
  */
@@ -7,12 +7,14 @@
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.loadFrontMcpConfig = loadFrontMcpConfig;
10
+ exports.tryLoadFrontMcpConfig = tryLoadFrontMcpConfig;
10
11
  exports.validateConfig = validateConfig;
11
12
  exports.findDeployment = findDeployment;
12
13
  exports.getDeploymentTargets = getDeploymentTargets;
13
14
  const tslib_1 = require("tslib");
14
15
  const fs = tslib_1.__importStar(require("fs"));
15
16
  const path = tslib_1.__importStar(require("path"));
17
+ const utils_1 = require("@frontmcp/utils");
16
18
  const frontmcp_config_schema_1 = require("./frontmcp-config.schema");
17
19
  const CONFIG_FILENAMES = [
18
20
  'frontmcp.config.ts',
@@ -36,6 +38,57 @@ async function loadFrontMcpConfig(cwd) {
36
38
  const raw = await loadRawConfig(cwd);
37
39
  return validateConfig(raw);
38
40
  }
41
+ /**
42
+ * Variant that load-errors propagate (parse failures in `frontmcp.config.ts`,
43
+ * missing dependencies, etc.) but schema-validation errors return `undefined`.
44
+ *
45
+ * Used by `runBuild` to support both shapes: the new top-level
46
+ * `frontmcpConfigSchema` (with `deployments`) and the older exec-only shape
47
+ * (top-level `cli`, `sea`, `esbuild`) that `loadExecConfig` consumes
48
+ * directly. A user with the old shape should still get a successful build
49
+ * — the exec-loader picks the file up from disk by itself.
50
+ *
51
+ * Returns `undefined` when:
52
+ * - no config file is present (caller falls back to CLI flags), or
53
+ * - the file loads but doesn't match the new schema (legacy shape).
54
+ *
55
+ * Throws when:
56
+ * - the file exists but can't be parsed (TS syntax error, ESM/CJS mismatch
57
+ * that even esbuild can't recover from, etc.) — i.e., #365 silent-default
58
+ * regressions.
59
+ */
60
+ async function tryLoadFrontMcpConfig(cwd) {
61
+ let raw;
62
+ try {
63
+ raw = await loadRawConfig(cwd);
64
+ }
65
+ catch (err) {
66
+ // Distinguish "no config and no package.json" from real load failures.
67
+ // `deriveFromPackageJson` throws this exact message — treat it as "no config".
68
+ if (err.message?.startsWith('No frontmcp.config found')) {
69
+ return undefined;
70
+ }
71
+ throw err;
72
+ }
73
+ const result = frontmcp_config_schema_1.frontmcpConfigSchema.safeParse(raw);
74
+ if (!result.success) {
75
+ // Distinguish two failure shapes:
76
+ // 1. Legacy exec-only config — top-level `cli` / `sea` / `esbuild`,
77
+ // no `deployments` key. Return undefined so `loadExecConfig` picks
78
+ // it up directly. Pre-v1.1 fixtures live in this branch.
79
+ // 2. Anything else — looks like the user attempted a v1.1 config and
80
+ // got it wrong (typo'd `deployments`, invalid `target`, etc.).
81
+ // Throw so we don't silently fall back to defaults — that was the
82
+ // silent-corruption mode #365 was trying to eliminate.
83
+ const obj = typeof raw === 'object' && raw !== null ? raw : undefined;
84
+ const isLegacyExecOnlyShape = !!obj && !('deployments' in obj) && ('cli' in obj || 'sea' in obj || 'esbuild' in obj);
85
+ if (isLegacyExecOnlyShape)
86
+ return undefined;
87
+ const issues = result.error.issues.map((i) => ` - ${i.path.join('.')}: ${i.message}`).join('\n');
88
+ throw new Error(`Invalid frontmcp.config:\n${issues}`);
89
+ }
90
+ return result.data;
91
+ }
39
92
  /**
40
93
  * Load raw config without validation.
41
94
  */
@@ -49,15 +102,34 @@ async function loadRawConfig(cwd) {
49
102
  return JSON.parse(content);
50
103
  }
51
104
  if (filename.endsWith('.ts')) {
52
- // TypeScript config try tsx or ts-node for runtime loading
105
+ // #365When the project is `"type": "commonjs"` (the default),
106
+ // `require()` can't load .ts (no transpiler) and `await import()`
107
+ // fails with "Make sure to set 'type': 'module'". The previous loader
108
+ // silently fell through to defaults. Now we transpile the .ts file
109
+ // with esbuild (already a dependency) and eval the resulting CJS.
110
+ // Any failure throws — no silent fallback.
53
111
  try {
54
112
  const mod = require(configPath);
55
113
  return mod.default ?? mod;
56
114
  }
57
- catch {
58
- // If require fails (no ts-node), try dynamic import
59
- const mod = await import(configPath);
60
- return mod.default ?? mod;
115
+ catch (requireErr) {
116
+ try {
117
+ const mod = await import(configPath);
118
+ return mod.default ?? mod;
119
+ }
120
+ catch {
121
+ // Both runtime loaders rejected — fall through to esbuild transpile.
122
+ }
123
+ try {
124
+ return await loadTsConfigViaEsbuild(configPath);
125
+ }
126
+ catch (esbuildErr) {
127
+ throw new Error(`Failed to load ${filename}.\n` +
128
+ ` require() error: ${requireErr.message}\n` +
129
+ ` esbuild error: ${esbuildErr.message}\n` +
130
+ `Hint: ensure the file exports a default config (e.g., ` +
131
+ `\`export default defineConfig({...})\`) and that all imports resolve.`);
132
+ }
61
133
  }
62
134
  }
63
135
  // JS/MJS/CJS
@@ -71,6 +143,53 @@ async function loadRawConfig(cwd) {
71
143
  // Fallback: derive from package.json
72
144
  return deriveFromPackageJson(cwd);
73
145
  }
146
+ /**
147
+ * Transpile a TypeScript config file with esbuild (CJS target) and eval the
148
+ * result via Module-via-vm. Used as a last-resort path when neither `require()`
149
+ * (no ts-node hook) nor `await import()` (project is `"type": "commonjs"`)
150
+ * can load the file directly.
151
+ *
152
+ * Uses `esbuild.build({ bundle: true, packages: 'external' })` rather than
153
+ * `transformSync` so a config that imports a sibling helper TS file
154
+ * (`import { foo } from './helpers'`) gets the helper inlined into the
155
+ * compiled output. Without bundling, the resulting CJS would emit
156
+ * `require('./helpers')` which Node can't resolve under `"type": "commonjs"`.
157
+ *
158
+ * `packages: 'external'` keeps node_modules dependencies as runtime
159
+ * `require()` calls so `import { defineConfig } from 'frontmcp'` still
160
+ * resolves against the project's installed copy of the SDK.
161
+ */
162
+ async function loadTsConfigViaEsbuild(configPath) {
163
+ const esbuild = require('esbuild');
164
+ const built = await esbuild.build({
165
+ entryPoints: [configPath],
166
+ bundle: true,
167
+ write: false,
168
+ platform: 'node',
169
+ format: 'cjs',
170
+ target: 'es2022',
171
+ packages: 'external',
172
+ sourcemap: 'inline',
173
+ logLevel: 'silent',
174
+ });
175
+ if (!built.outputFiles || built.outputFiles.length === 0) {
176
+ throw new Error('esbuild produced no output for ' + configPath);
177
+ }
178
+ const code = built.outputFiles[0].text;
179
+ const Module = require('module');
180
+ const m = new Module(configPath, module);
181
+ // Make the loaded module's `require` resolve relative to the config dir
182
+ // so user `import { defineConfig } from 'frontmcp'` keeps working.
183
+ m.filename = configPath;
184
+ m.paths = Module._nodeModulePaths(path.dirname(configPath));
185
+ m._compile(code, configPath);
186
+ const exported = m.exports;
187
+ return exported?.default ?? exported;
188
+ }
189
+ // Surface the @frontmcp/utils import even though esbuild reads the entry
190
+ // itself — keeps the file's filesystem boundary going through @frontmcp/utils
191
+ // for any future expansion.
192
+ void utils_1.readFile;
74
193
  /**
75
194
  * Derive minimal config from package.json.
76
195
  */
@@ -1 +1 @@
1
- {"version":3,"file":"frontmcp-config.loader.js","sourceRoot":"","sources":["../../../src/config/frontmcp-config.loader.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * FrontMCP Config Loader\n *\n * Loads `frontmcp.config.(json|js|ts|mjs|cjs)` from a directory.\n * Falls back to deriving minimal config from package.json.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nimport { frontmcpConfigSchema, type FrontMcpConfigParsed } from './frontmcp-config.schema';\nimport type { DeploymentTarget, FrontMcpConfig } from './frontmcp-config.types';\n\nconst CONFIG_FILENAMES = [\n 'frontmcp.config.ts',\n 'frontmcp.config.js',\n 'frontmcp.config.json',\n 'frontmcp.config.mjs',\n 'frontmcp.config.cjs',\n];\n\n/**\n * Load and validate a frontmcp.config file from the given directory.\n *\n * Resolution order:\n * 1. frontmcp.config.ts\n * 2. frontmcp.config.js\n * 3. frontmcp.config.json\n * 4. frontmcp.config.mjs\n * 5. frontmcp.config.cjs\n * 6. Derive from package.json (minimal config with 'node' target)\n */\nexport async function loadFrontMcpConfig(cwd: string): Promise<FrontMcpConfigParsed> {\n const raw = await loadRawConfig(cwd);\n return validateConfig(raw);\n}\n\n/**\n * Load raw config without validation.\n */\nasync function loadRawConfig(cwd: string): Promise<unknown> {\n for (const filename of CONFIG_FILENAMES) {\n const configPath = path.join(cwd, filename);\n if (!fs.existsSync(configPath)) continue;\n\n if (filename.endsWith('.json')) {\n const content = fs.readFileSync(configPath, 'utf-8');\n return JSON.parse(content);\n }\n\n if (filename.endsWith('.ts')) {\n // TypeScript config — try tsx or ts-node for runtime loading\n try {\n const mod = require(configPath);\n return mod.default ?? mod;\n } catch {\n // If require fails (no ts-node), try dynamic import\n const mod = await import(configPath);\n return mod.default ?? mod;\n }\n }\n\n // JS/MJS/CJS\n if (filename.endsWith('.mjs')) {\n const mod = await import(configPath);\n return mod.default ?? mod;\n }\n\n const mod = require(configPath);\n return mod.default ?? mod;\n }\n\n // Fallback: derive from package.json\n return deriveFromPackageJson(cwd);\n}\n\n/**\n * Derive minimal config from package.json.\n */\nfunction deriveFromPackageJson(cwd: string): FrontMcpConfig {\n const pkgPath = path.join(cwd, 'package.json');\n if (!fs.existsSync(pkgPath)) {\n throw new Error(\n 'No frontmcp.config found and no package.json. Create a frontmcp.config.ts to configure build targets.',\n );\n }\n\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));\n return {\n name: pkg.name?.replace(/^@[^/]+\\//, '') || path.basename(cwd),\n version: pkg.version || '1.0.0',\n entry: pkg.main,\n deployments: [{ target: 'node' }],\n };\n}\n\n/**\n * Validate raw config against the Zod schema.\n */\nexport function validateConfig(raw: unknown): FrontMcpConfigParsed {\n const result = frontmcpConfigSchema.safeParse(raw);\n if (!result.success) {\n const issues = result.error.issues.map((i) => ` - ${i.path.join('.')}: ${i.message}`).join('\\n');\n throw new Error(`Invalid frontmcp.config:\\n${issues}`);\n }\n return result.data;\n}\n\n/**\n * Find a deployment target by type.\n */\nexport function findDeployment(config: FrontMcpConfigParsed, target: string): DeploymentTarget | undefined {\n return config.deployments.find((d) => d.target === target);\n}\n\n/**\n * Get all deployment target types from the config.\n */\nexport function getDeploymentTargets(config: FrontMcpConfigParsed): string[] {\n return config.deployments.map((d) => d.target);\n}\n"]}
1
+ {"version":3,"file":"frontmcp-config.loader.js","sourceRoot":"","sources":["../../../src/config/frontmcp-config.loader.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * FrontMCP Config Loader\n *\n * Loads `frontmcp.config.(json|js|ts|mjs|cjs)` from a directory.\n * Falls back to deriving minimal config from package.json.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nimport { readFile } from '@frontmcp/utils';\n\nimport { frontmcpConfigSchema, type FrontMcpConfigParsed } from './frontmcp-config.schema';\nimport type { DeploymentTarget, FrontMcpConfig } from './frontmcp-config.types';\n\nconst CONFIG_FILENAMES = [\n 'frontmcp.config.ts',\n 'frontmcp.config.js',\n 'frontmcp.config.json',\n 'frontmcp.config.mjs',\n 'frontmcp.config.cjs',\n];\n\n/**\n * Load and validate a frontmcp.config file from the given directory.\n *\n * Resolution order:\n * 1. frontmcp.config.ts\n * 2. frontmcp.config.js\n * 3. frontmcp.config.json\n * 4. frontmcp.config.mjs\n * 5. frontmcp.config.cjs\n * 6. Derive from package.json (minimal config with 'node' target)\n */\nexport async function loadFrontMcpConfig(cwd: string): Promise<FrontMcpConfigParsed> {\n const raw = await loadRawConfig(cwd);\n return validateConfig(raw);\n}\n\n/**\n * Variant that load-errors propagate (parse failures in `frontmcp.config.ts`,\n * missing dependencies, etc.) but schema-validation errors return `undefined`.\n *\n * Used by `runBuild` to support both shapes: the new top-level\n * `frontmcpConfigSchema` (with `deployments`) and the older exec-only shape\n * (top-level `cli`, `sea`, `esbuild`) that `loadExecConfig` consumes\n * directly. A user with the old shape should still get a successful build\n * — the exec-loader picks the file up from disk by itself.\n *\n * Returns `undefined` when:\n * - no config file is present (caller falls back to CLI flags), or\n * - the file loads but doesn't match the new schema (legacy shape).\n *\n * Throws when:\n * - the file exists but can't be parsed (TS syntax error, ESM/CJS mismatch\n * that even esbuild can't recover from, etc.) — i.e., #365 silent-default\n * regressions.\n */\nexport async function tryLoadFrontMcpConfig(cwd: string): Promise<FrontMcpConfigParsed | undefined> {\n let raw: unknown;\n try {\n raw = await loadRawConfig(cwd);\n } catch (err) {\n // Distinguish \"no config and no package.json\" from real load failures.\n // `deriveFromPackageJson` throws this exact message — treat it as \"no config\".\n if ((err as Error).message?.startsWith('No frontmcp.config found')) {\n return undefined;\n }\n throw err;\n }\n const result = frontmcpConfigSchema.safeParse(raw);\n if (!result.success) {\n // Distinguish two failure shapes:\n // 1. Legacy exec-only config — top-level `cli` / `sea` / `esbuild`,\n // no `deployments` key. Return undefined so `loadExecConfig` picks\n // it up directly. Pre-v1.1 fixtures live in this branch.\n // 2. Anything else — looks like the user attempted a v1.1 config and\n // got it wrong (typo'd `deployments`, invalid `target`, etc.).\n // Throw so we don't silently fall back to defaults — that was the\n // silent-corruption mode #365 was trying to eliminate.\n const obj = typeof raw === 'object' && raw !== null ? (raw as Record<string, unknown>) : undefined;\n const isLegacyExecOnlyShape =\n !!obj && !('deployments' in obj) && ('cli' in obj || 'sea' in obj || 'esbuild' in obj);\n if (isLegacyExecOnlyShape) return undefined;\n const issues = result.error.issues.map((i) => ` - ${i.path.join('.')}: ${i.message}`).join('\\n');\n throw new Error(`Invalid frontmcp.config:\\n${issues}`);\n }\n return result.data;\n}\n\n/**\n * Load raw config without validation.\n */\nasync function loadRawConfig(cwd: string): Promise<unknown> {\n for (const filename of CONFIG_FILENAMES) {\n const configPath = path.join(cwd, filename);\n if (!fs.existsSync(configPath)) continue;\n\n if (filename.endsWith('.json')) {\n const content = fs.readFileSync(configPath, 'utf-8');\n return JSON.parse(content);\n }\n\n if (filename.endsWith('.ts')) {\n // #365 — When the project is `\"type\": \"commonjs\"` (the default),\n // `require()` can't load .ts (no transpiler) and `await import()`\n // fails with \"Make sure to set 'type': 'module'\". The previous loader\n // silently fell through to defaults. Now we transpile the .ts file\n // with esbuild (already a dependency) and eval the resulting CJS.\n // Any failure throws — no silent fallback.\n try {\n const mod = require(configPath);\n return mod.default ?? mod;\n } catch (requireErr) {\n try {\n const mod = await import(configPath);\n return mod.default ?? mod;\n } catch {\n // Both runtime loaders rejected — fall through to esbuild transpile.\n }\n try {\n return await loadTsConfigViaEsbuild(configPath);\n } catch (esbuildErr) {\n throw new Error(\n `Failed to load ${filename}.\\n` +\n ` require() error: ${(requireErr as Error).message}\\n` +\n ` esbuild error: ${(esbuildErr as Error).message}\\n` +\n `Hint: ensure the file exports a default config (e.g., ` +\n `\\`export default defineConfig({...})\\`) and that all imports resolve.`,\n );\n }\n }\n }\n\n // JS/MJS/CJS\n if (filename.endsWith('.mjs')) {\n const mod = await import(configPath);\n return mod.default ?? mod;\n }\n\n const mod = require(configPath);\n return mod.default ?? mod;\n }\n\n // Fallback: derive from package.json\n return deriveFromPackageJson(cwd);\n}\n\n/**\n * Transpile a TypeScript config file with esbuild (CJS target) and eval the\n * result via Module-via-vm. Used as a last-resort path when neither `require()`\n * (no ts-node hook) nor `await import()` (project is `\"type\": \"commonjs\"`)\n * can load the file directly.\n *\n * Uses `esbuild.build({ bundle: true, packages: 'external' })` rather than\n * `transformSync` so a config that imports a sibling helper TS file\n * (`import { foo } from './helpers'`) gets the helper inlined into the\n * compiled output. Without bundling, the resulting CJS would emit\n * `require('./helpers')` which Node can't resolve under `\"type\": \"commonjs\"`.\n *\n * `packages: 'external'` keeps node_modules dependencies as runtime\n * `require()` calls so `import { defineConfig } from 'frontmcp'` still\n * resolves against the project's installed copy of the SDK.\n */\nasync function loadTsConfigViaEsbuild(configPath: string): Promise<unknown> {\n const esbuild = require('esbuild') as typeof import('esbuild');\n const built = await esbuild.build({\n entryPoints: [configPath],\n bundle: true,\n write: false,\n platform: 'node',\n format: 'cjs',\n target: 'es2022',\n packages: 'external',\n sourcemap: 'inline',\n logLevel: 'silent',\n });\n if (!built.outputFiles || built.outputFiles.length === 0) {\n throw new Error('esbuild produced no output for ' + configPath);\n }\n const code = built.outputFiles[0].text;\n\n const Module = require('module') as typeof import('module');\n const m = new Module(configPath, module);\n // Make the loaded module's `require` resolve relative to the config dir\n // so user `import { defineConfig } from 'frontmcp'` keeps working.\n m.filename = configPath;\n m.paths = (Module as unknown as { _nodeModulePaths(p: string): string[] })._nodeModulePaths(path.dirname(configPath));\n\n (m as any)._compile(code, configPath);\n\n const exported = (m as any).exports as { default?: unknown };\n return exported?.default ?? exported;\n}\n\n// Surface the @frontmcp/utils import even though esbuild reads the entry\n// itself — keeps the file's filesystem boundary going through @frontmcp/utils\n// for any future expansion.\nvoid readFile;\n\n/**\n * Derive minimal config from package.json.\n */\nfunction deriveFromPackageJson(cwd: string): FrontMcpConfig {\n const pkgPath = path.join(cwd, 'package.json');\n if (!fs.existsSync(pkgPath)) {\n throw new Error(\n 'No frontmcp.config found and no package.json. Create a frontmcp.config.ts to configure build targets.',\n );\n }\n\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));\n return {\n name: pkg.name?.replace(/^@[^/]+\\//, '') || path.basename(cwd),\n version: pkg.version || '1.0.0',\n entry: pkg.main,\n deployments: [{ target: 'node' }],\n };\n}\n\n/**\n * Validate raw config against the Zod schema.\n */\nexport function validateConfig(raw: unknown): FrontMcpConfigParsed {\n const result = frontmcpConfigSchema.safeParse(raw);\n if (!result.success) {\n const issues = result.error.issues.map((i) => ` - ${i.path.join('.')}: ${i.message}`).join('\\n');\n throw new Error(`Invalid frontmcp.config:\\n${issues}`);\n }\n return result.data;\n}\n\n/**\n * Find a deployment target by type.\n */\nexport function findDeployment(config: FrontMcpConfigParsed, target: string): DeploymentTarget | undefined {\n return config.deployments.find((d) => d.target === target);\n}\n\n/**\n * Get all deployment target types from the config.\n */\nexport function getDeploymentTargets(config: FrontMcpConfigParsed): string[] {\n return config.deployments.map((d) => d.target);\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  export { defineConfig } from './define-config';
2
- export { loadFrontMcpConfig, validateConfig, findDeployment, getDeploymentTargets } from './frontmcp-config.loader';
2
+ export { loadFrontMcpConfig, tryLoadFrontMcpConfig, validateConfig, findDeployment, getDeploymentTargets, } from './frontmcp-config.loader';
3
3
  export { frontmcpConfigSchema } from './frontmcp-config.schema';
4
4
  export type { FrontMcpConfigParsed } from './frontmcp-config.schema';
5
5
  export type { FrontMcpConfig, ServerDefaults, DeploymentTarget, DeploymentTargetType } from './frontmcp-config.types';
@@ -1,10 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.frontmcpConfigSchema = exports.getDeploymentTargets = exports.findDeployment = exports.validateConfig = exports.loadFrontMcpConfig = exports.defineConfig = void 0;
3
+ exports.frontmcpConfigSchema = exports.getDeploymentTargets = exports.findDeployment = exports.validateConfig = exports.tryLoadFrontMcpConfig = exports.loadFrontMcpConfig = exports.defineConfig = void 0;
4
4
  var define_config_1 = require("./define-config");
5
5
  Object.defineProperty(exports, "defineConfig", { enumerable: true, get: function () { return define_config_1.defineConfig; } });
6
6
  var frontmcp_config_loader_1 = require("./frontmcp-config.loader");
7
7
  Object.defineProperty(exports, "loadFrontMcpConfig", { enumerable: true, get: function () { return frontmcp_config_loader_1.loadFrontMcpConfig; } });
8
+ Object.defineProperty(exports, "tryLoadFrontMcpConfig", { enumerable: true, get: function () { return frontmcp_config_loader_1.tryLoadFrontMcpConfig; } });
8
9
  Object.defineProperty(exports, "validateConfig", { enumerable: true, get: function () { return frontmcp_config_loader_1.validateConfig; } });
9
10
  Object.defineProperty(exports, "findDeployment", { enumerable: true, get: function () { return frontmcp_config_loader_1.findDeployment; } });
10
11
  Object.defineProperty(exports, "getDeploymentTargets", { enumerable: true, get: function () { return frontmcp_config_loader_1.getDeploymentTargets; } });
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/config/index.ts"],"names":[],"mappings":";;;AAAA,iDAA+C;AAAtC,6GAAA,YAAY,OAAA;AACrB,mEAAoH;AAA3G,4HAAA,kBAAkB,OAAA;AAAE,wHAAA,cAAc,OAAA;AAAE,wHAAA,cAAc,OAAA;AAAE,8HAAA,oBAAoB,OAAA;AACjF,mEAAgE;AAAvD,8HAAA,oBAAoB,OAAA","sourcesContent":["export { defineConfig } from './define-config';\nexport { loadFrontMcpConfig, validateConfig, findDeployment, getDeploymentTargets } from './frontmcp-config.loader';\nexport { frontmcpConfigSchema } from './frontmcp-config.schema';\nexport type { FrontMcpConfigParsed } from './frontmcp-config.schema';\nexport type { FrontMcpConfig, ServerDefaults, DeploymentTarget, DeploymentTargetType } from './frontmcp-config.types';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/config/index.ts"],"names":[],"mappings":";;;AAAA,iDAA+C;AAAtC,6GAAA,YAAY,OAAA;AACrB,mEAMkC;AALhC,4HAAA,kBAAkB,OAAA;AAClB,+HAAA,qBAAqB,OAAA;AACrB,wHAAA,cAAc,OAAA;AACd,wHAAA,cAAc,OAAA;AACd,8HAAA,oBAAoB,OAAA;AAEtB,mEAAgE;AAAvD,8HAAA,oBAAoB,OAAA","sourcesContent":["export { defineConfig } from './define-config';\nexport {\n loadFrontMcpConfig,\n tryLoadFrontMcpConfig,\n validateConfig,\n findDeployment,\n getDeploymentTargets,\n} from './frontmcp-config.loader';\nexport { frontmcpConfigSchema } from './frontmcp-config.schema';\nexport type { FrontMcpConfigParsed } from './frontmcp-config.schema';\nexport type { FrontMcpConfig, ServerDefaults, DeploymentTarget, DeploymentTargetType } from './frontmcp-config.types';\n"]}