frontmcp 1.0.0-beta.9 → 1.0.0

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 (39) hide show
  1. package/README.md +2 -2
  2. package/package.json +3 -3
  3. package/src/commands/build/adapters/vercel.js +1 -1
  4. package/src/commands/build/adapters/vercel.js.map +1 -1
  5. package/src/commands/build/exec/cli-runtime/daemon-client.js +0 -1
  6. package/src/commands/build/exec/cli-runtime/daemon-client.js.map +1 -1
  7. package/src/commands/build/exec/cli-runtime/schema-extractor.d.ts +1 -1
  8. package/src/commands/build/exec/cli-runtime/schema-extractor.js +3 -4
  9. package/src/commands/build/exec/cli-runtime/schema-extractor.js.map +1 -1
  10. package/src/commands/dev/dev.js +18 -3
  11. package/src/commands/dev/dev.js.map +1 -1
  12. package/src/commands/pm/health.d.ts +3 -0
  13. package/src/commands/pm/health.js +25 -6
  14. package/src/commands/pm/health.js.map +1 -1
  15. package/src/commands/scaffold/create.js +142 -166
  16. package/src/commands/scaffold/create.js.map +1 -1
  17. package/src/commands/skills/catalog.d.ts +2 -0
  18. package/src/commands/skills/catalog.js +14 -2
  19. package/src/commands/skills/catalog.js.map +1 -1
  20. package/src/commands/skills/install.d.ts +22 -2
  21. package/src/commands/skills/install.js +171 -18
  22. package/src/commands/skills/install.js.map +1 -1
  23. package/src/commands/skills/read.d.ts +6 -0
  24. package/src/commands/skills/read.js +186 -0
  25. package/src/commands/skills/read.js.map +1 -0
  26. package/src/commands/skills/register.js +22 -8
  27. package/src/commands/skills/register.js.map +1 -1
  28. package/src/commands/skills/search.js +1 -1
  29. package/src/commands/skills/search.js.map +1 -1
  30. package/src/core/colors.js +9 -1
  31. package/src/core/colors.js.map +1 -1
  32. package/src/core/help.d.ts +2 -2
  33. package/src/core/help.js +82 -36
  34. package/src/core/help.js.map +1 -1
  35. package/src/core/program.js +4 -1
  36. package/src/core/program.js.map +1 -1
  37. package/src/commands/skills/show.d.ts +0 -1
  38. package/src/commands/skills/show.js +0 -46
  39. package/src/commands/skills/show.js.map +0 -1
package/README.md CHANGED
@@ -39,14 +39,14 @@ export default class Server {}
39
39
 
40
40
  ## Installation
41
41
 
42
- **Node.js 22+** required (24 recommended).
42
+ **Node.js 24+** required.
43
43
 
44
44
  ```bash
45
45
  # New project (recommended)
46
46
  npx frontmcp create my-app
47
47
 
48
48
  # Existing project
49
- npm i -D frontmcp @types/node@^22
49
+ npm i -D frontmcp @types/node@^24
50
50
  npx frontmcp init
51
51
  ```
52
52
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frontmcp",
3
- "version": "1.0.0-beta.9",
3
+ "version": "1.0.0",
4
4
  "description": "FrontMCP command line interface",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "homepage": "https://docs.agentfront.dev",
@@ -26,11 +26,11 @@
26
26
  "frontmcp": "./src/core/cli.js"
27
27
  },
28
28
  "engines": {
29
- "node": ">=22.0.0"
29
+ "node": ">=24.0.0"
30
30
  },
31
31
  "dependencies": {
32
32
  "@clack/prompts": "^0.10.0",
33
- "@frontmcp/utils": "1.0.0-beta.9",
33
+ "@frontmcp/utils": "1.0.0",
34
34
  "commander": "^13.0.0",
35
35
  "tslib": "^2.3.0",
36
36
  "vectoriadb": "^2.1.3",
@@ -150,7 +150,7 @@ export default async function handler(req, res) {
150
150
  }
151
151
  // Create function config (.vc-config.json)
152
152
  const vcConfig = {
153
- runtime: 'nodejs22.x',
153
+ runtime: 'nodejs24.x',
154
154
  handler: bundleOutput,
155
155
  launcherType: 'Nodejs',
156
156
  };
@@ -1 +1 @@
1
- {"version":3,"file":"vercel.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/vercel.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path';\nimport { existsSync } from 'fs';\nimport { execSync } from 'child_process';\nimport { mkdir, readdir, stat, cp, copyFile, readFile, writeFile } from '@frontmcp/utils';\nimport { AdapterTemplate } from '../types';\n\ntype PackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun';\n\ninterface PackageManagerConfig {\n install: string;\n run: string;\n}\n\nconst PACKAGE_MANAGERS: Record<PackageManager, PackageManagerConfig> = {\n bun: { install: 'bun install', run: 'bun run build' },\n pnpm: { install: 'pnpm install', run: 'pnpm run build' },\n yarn: { install: 'yarn install', run: 'yarn build' },\n npm: { install: 'npm install', run: 'npm run build' },\n};\n\nconst LOCKFILE_TO_PM: Record<string, PackageManager> = {\n 'bun.lockb': 'bun',\n 'pnpm-lock.yaml': 'pnpm',\n 'yarn.lock': 'yarn',\n 'package-lock.json': 'npm',\n};\n\n/**\n * Detect package manager based on lockfile presence.\n * Priority: bun > pnpm > yarn > npm (fastest to slowest install times)\n */\nfunction detectPackageManager(cwd: string): PackageManager {\n for (const [lockfile, pm] of Object.entries(LOCKFILE_TO_PM)) {\n if (existsSync(path.join(cwd, lockfile))) {\n return pm;\n }\n }\n return 'npm'; // Default fallback\n}\n\n/**\n * Vercel adapter - serverless deployment on Vercel.\n * Compiles to ESM, bundles with rspack to CJS for maximum compatibility.\n *\n * Uses Vercel Build Output API for deployment:\n * - Creates .vercel/output/config.json with routing\n * - Creates .vercel/output/functions/index.func/ with handler\n *\n * The build process:\n * 1. TypeScript compiles to ESM in dist/\n * 2. serverless-setup.js is generated (sets FRONTMCP_SERVERLESS=1)\n * 3. index.js imports setup first, then main module\n * 4. rspack bundles everything into handler.cjs\n * 5. Build Output API structure is created in .vercel/output/\n *\n * @see https://vercel.com/docs/build-output-api/v3\n */\nexport const vercelAdapter: AdapterTemplate = {\n moduleFormat: 'esnext',\n shouldBundle: true,\n bundleOutput: 'handler.cjs',\n\n getSetupTemplate: () => `// Serverless environment setup - MUST be imported first\n// This sets FRONTMCP_SERVERLESS before any decorators run\n// Required because ESM hoists imports before other statements\nprocess.env.FRONTMCP_SERVERLESS = '1';\n`,\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated Vercel entry point\n// Generated by: frontmcp build --target vercel\nimport './serverless-setup.js';\nimport '${mainModulePath}';\nimport { getServerlessHandlerAsync } from '@frontmcp/sdk';\n\nlet handlerPromise = null;\n\nexport default async function handler(req, res) {\n if (!handlerPromise) {\n handlerPromise = getServerlessHandlerAsync();\n }\n const app = await handlerPromise;\n return app(req, res);\n}\n`,\n\n // Detect package manager and generate appropriate vercel.json\n getConfig: (cwd: string) => {\n const pm = detectPackageManager(cwd);\n const config = PACKAGE_MANAGERS[pm];\n return {\n version: 2,\n buildCommand: config.run,\n installCommand: config.install,\n };\n },\n\n configFileName: 'vercel.json',\n\n /**\n * Create Vercel Build Output API structure after bundling.\n * This allows Vercel to deploy the function without needing an /api folder.\n *\n * Structure created:\n * .vercel/output/\n * ├── config.json (routes all requests to index function)\n * └── functions/\n * └── index.func/\n * ├── .vc-config.json (Node.js 22 runtime config)\n * ├── handler.cjs (bundled handler + chunks)\n * └── node_modules/ (runtime dependencies that can't be bundled)\n */\n postBundle: async (outDir: string, cwd: string, bundleOutput: string) => {\n const outputDir = path.join(cwd, '.vercel', 'output');\n const funcDir = path.join(outputDir, 'functions', 'index.func');\n\n // Create directories\n await mkdir(funcDir, { recursive: true });\n\n // Copy all files from dist to the function directory\n // This includes handler.cjs and any chunk files (*.handler.cjs)\n const distFiles = await readdir(outDir);\n for (const file of distFiles) {\n const srcPath = path.join(outDir, file);\n const destPath = path.join(funcDir, file);\n const fileStat = await stat(srcPath);\n\n if (fileStat.isDirectory()) {\n // Recursively copy directories\n await cp(srcPath, destPath, { recursive: true });\n } else {\n // Copy files\n await copyFile(srcPath, destPath);\n }\n }\n\n // Install runtime dependencies that can't be statically bundled (dynamic requires)\n // These are packages loaded via require() inside functions that rspack can't analyze\n // We install them fresh to ensure correct platform binaries (linux-x64 for Vercel)\n const runtimeDeps = ['@vercel/kv', 'esbuild', '@swc/core'];\n\n // Read package.json to get the exact versions\n const pkgJsonPath = path.join(cwd, 'package.json');\n const pkgJson = JSON.parse(await readFile(pkgJsonPath, 'utf-8'));\n const allDeps = { ...pkgJson.dependencies, ...pkgJson.devDependencies };\n\n // Build list of deps with versions\n const depsToInstall: string[] = [];\n for (const dep of runtimeDeps) {\n const version = allDeps[dep];\n if (version) {\n depsToInstall.push(`${dep}@${version}`);\n }\n }\n\n if (depsToInstall.length > 0) {\n // Create package.json in function directory\n const funcPkgJson = { name: 'index.func', private: true, dependencies: {} };\n await writeFile(path.join(funcDir, 'package.json'), JSON.stringify(funcPkgJson, null, 2));\n\n // Install dependencies using npm (works on all platforms)\n try {\n execSync(`npm install ${depsToInstall.join(' ')} --omit=dev`, {\n cwd: funcDir,\n stdio: 'pipe',\n });\n } catch {\n // Silently continue if install fails - the dep might not be needed\n }\n }\n\n // Create function config (.vc-config.json)\n const vcConfig = {\n runtime: 'nodejs22.x',\n handler: bundleOutput,\n launcherType: 'Nodejs',\n };\n await writeFile(\n path.join(funcDir, '.vc-config.json'),\n JSON.stringify(vcConfig, null, 2),\n );\n\n // Create output config (config.json) with routing\n const outputConfig = {\n version: 3,\n routes: [{ src: '/(.*)', dest: '/index' }],\n };\n await writeFile(\n path.join(outputDir, 'config.json'),\n JSON.stringify(outputConfig, null, 2),\n );\n },\n};\n"]}
1
+ {"version":3,"file":"vercel.js","sourceRoot":"","sources":["../../../../../src/commands/build/adapters/vercel.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path';\nimport { existsSync } from 'fs';\nimport { execSync } from 'child_process';\nimport { mkdir, readdir, stat, cp, copyFile, readFile, writeFile } from '@frontmcp/utils';\nimport { AdapterTemplate } from '../types';\n\ntype PackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun';\n\ninterface PackageManagerConfig {\n install: string;\n run: string;\n}\n\nconst PACKAGE_MANAGERS: Record<PackageManager, PackageManagerConfig> = {\n bun: { install: 'bun install', run: 'bun run build' },\n pnpm: { install: 'pnpm install', run: 'pnpm run build' },\n yarn: { install: 'yarn install', run: 'yarn build' },\n npm: { install: 'npm install', run: 'npm run build' },\n};\n\nconst LOCKFILE_TO_PM: Record<string, PackageManager> = {\n 'bun.lockb': 'bun',\n 'pnpm-lock.yaml': 'pnpm',\n 'yarn.lock': 'yarn',\n 'package-lock.json': 'npm',\n};\n\n/**\n * Detect package manager based on lockfile presence.\n * Priority: bun > pnpm > yarn > npm (fastest to slowest install times)\n */\nfunction detectPackageManager(cwd: string): PackageManager {\n for (const [lockfile, pm] of Object.entries(LOCKFILE_TO_PM)) {\n if (existsSync(path.join(cwd, lockfile))) {\n return pm;\n }\n }\n return 'npm'; // Default fallback\n}\n\n/**\n * Vercel adapter - serverless deployment on Vercel.\n * Compiles to ESM, bundles with rspack to CJS for maximum compatibility.\n *\n * Uses Vercel Build Output API for deployment:\n * - Creates .vercel/output/config.json with routing\n * - Creates .vercel/output/functions/index.func/ with handler\n *\n * The build process:\n * 1. TypeScript compiles to ESM in dist/\n * 2. serverless-setup.js is generated (sets FRONTMCP_SERVERLESS=1)\n * 3. index.js imports setup first, then main module\n * 4. rspack bundles everything into handler.cjs\n * 5. Build Output API structure is created in .vercel/output/\n *\n * @see https://vercel.com/docs/build-output-api/v3\n */\nexport const vercelAdapter: AdapterTemplate = {\n moduleFormat: 'esnext',\n shouldBundle: true,\n bundleOutput: 'handler.cjs',\n\n getSetupTemplate: () => `// Serverless environment setup - MUST be imported first\n// This sets FRONTMCP_SERVERLESS before any decorators run\n// Required because ESM hoists imports before other statements\nprocess.env.FRONTMCP_SERVERLESS = '1';\n`,\n\n getEntryTemplate: (mainModulePath: string) => `// Auto-generated Vercel entry point\n// Generated by: frontmcp build --target vercel\nimport './serverless-setup.js';\nimport '${mainModulePath}';\nimport { getServerlessHandlerAsync } from '@frontmcp/sdk';\n\nlet handlerPromise = null;\n\nexport default async function handler(req, res) {\n if (!handlerPromise) {\n handlerPromise = getServerlessHandlerAsync();\n }\n const app = await handlerPromise;\n return app(req, res);\n}\n`,\n\n // Detect package manager and generate appropriate vercel.json\n getConfig: (cwd: string) => {\n const pm = detectPackageManager(cwd);\n const config = PACKAGE_MANAGERS[pm];\n return {\n version: 2,\n buildCommand: config.run,\n installCommand: config.install,\n };\n },\n\n configFileName: 'vercel.json',\n\n /**\n * Create Vercel Build Output API structure after bundling.\n * This allows Vercel to deploy the function without needing an /api folder.\n *\n * Structure created:\n * .vercel/output/\n * ├── config.json (routes all requests to index function)\n * └── functions/\n * └── index.func/\n * ├── .vc-config.json (Node.js 22 runtime config)\n * ├── handler.cjs (bundled handler + chunks)\n * └── node_modules/ (runtime dependencies that can't be bundled)\n */\n postBundle: async (outDir: string, cwd: string, bundleOutput: string) => {\n const outputDir = path.join(cwd, '.vercel', 'output');\n const funcDir = path.join(outputDir, 'functions', 'index.func');\n\n // Create directories\n await mkdir(funcDir, { recursive: true });\n\n // Copy all files from dist to the function directory\n // This includes handler.cjs and any chunk files (*.handler.cjs)\n const distFiles = await readdir(outDir);\n for (const file of distFiles) {\n const srcPath = path.join(outDir, file);\n const destPath = path.join(funcDir, file);\n const fileStat = await stat(srcPath);\n\n if (fileStat.isDirectory()) {\n // Recursively copy directories\n await cp(srcPath, destPath, { recursive: true });\n } else {\n // Copy files\n await copyFile(srcPath, destPath);\n }\n }\n\n // Install runtime dependencies that can't be statically bundled (dynamic requires)\n // These are packages loaded via require() inside functions that rspack can't analyze\n // We install them fresh to ensure correct platform binaries (linux-x64 for Vercel)\n const runtimeDeps = ['@vercel/kv', 'esbuild', '@swc/core'];\n\n // Read package.json to get the exact versions\n const pkgJsonPath = path.join(cwd, 'package.json');\n const pkgJson = JSON.parse(await readFile(pkgJsonPath, 'utf-8'));\n const allDeps = { ...pkgJson.dependencies, ...pkgJson.devDependencies };\n\n // Build list of deps with versions\n const depsToInstall: string[] = [];\n for (const dep of runtimeDeps) {\n const version = allDeps[dep];\n if (version) {\n depsToInstall.push(`${dep}@${version}`);\n }\n }\n\n if (depsToInstall.length > 0) {\n // Create package.json in function directory\n const funcPkgJson = { name: 'index.func', private: true, dependencies: {} };\n await writeFile(path.join(funcDir, 'package.json'), JSON.stringify(funcPkgJson, null, 2));\n\n // Install dependencies using npm (works on all platforms)\n try {\n execSync(`npm install ${depsToInstall.join(' ')} --omit=dev`, {\n cwd: funcDir,\n stdio: 'pipe',\n });\n } catch {\n // Silently continue if install fails - the dep might not be needed\n }\n }\n\n // Create function config (.vc-config.json)\n const vcConfig = {\n runtime: 'nodejs24.x',\n handler: bundleOutput,\n launcherType: 'Nodejs',\n };\n await writeFile(\n path.join(funcDir, '.vc-config.json'),\n JSON.stringify(vcConfig, null, 2),\n );\n\n // Create output config (config.json) with routing\n const outputConfig = {\n version: 3,\n routes: [{ src: '/(.*)', dest: '/index' }],\n };\n await writeFile(\n path.join(outputDir, 'config.json'),\n JSON.stringify(outputConfig, null, 2),\n );\n },\n};\n"]}
@@ -12,7 +12,6 @@
12
12
  */
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
14
  exports.generateDaemonClientSource = generateDaemonClientSource;
15
- /* eslint-disable @typescript-eslint/no-require-imports */
16
15
  /**
17
16
  * Generate the daemon-client JavaScript source code (CJS module).
18
17
  * This is embedded into the CLI bundle at build time.
@@ -1 +1 @@
1
- {"version":3,"file":"daemon-client.js","sourceRoot":"","sources":["../../../../../../src/commands/build/exec/cli-runtime/daemon-client.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;AAQH,gEAqJC;AA3JD,0DAA0D;AAE1D;;;GAGG;AACH,SAAgB,0BAA0B;IACxC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmJR,CAAC;AACF,CAAC","sourcesContent":["/**\n * Lightweight daemon client for CLI exec.\n *\n * Sends MCP JSON-RPC requests over a Unix socket using Node.js built-in http module.\n * This avoids the full FrontMCP SDK initialization (~420ms) by talking to an\n * already-running daemon process.\n *\n * NOTE: This file is used as a CJS runtime module — it gets bundled into the CLI\n * output by esbuild alongside the generated CLI entry. It must remain free of\n * TypeScript-only constructs at runtime (the .ts extension is for build-time only).\n */\n\n/* eslint-disable @typescript-eslint/no-require-imports */\n\n/**\n * Generate the daemon-client JavaScript source code (CJS module).\n * This is embedded into the CLI bundle at build time.\n */\nexport function generateDaemonClientSource(): string {\n return `'use strict';\n\nvar http = require('http');\n\n/**\n * Send a JSON-RPC request over a Unix socket.\n * @param {string} socketPath - Path to the Unix socket file.\n * @param {string} method - JSON-RPC method name.\n * @param {object} [params] - Method parameters.\n * @returns {Promise<object>} Parsed JSON-RPC result.\n */\nfunction rpcCall(socketPath, method, params) {\n return new Promise(function(resolve, reject) {\n var body = JSON.stringify({\n jsonrpc: '2.0',\n id: Date.now(),\n method: method,\n params: params || {}\n });\n\n var req = http.request({\n socketPath: socketPath,\n path: '/mcp',\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Content-Length': Buffer.byteLength(body)\n },\n timeout: 10000\n }, function(res) {\n var chunks = [];\n res.on('data', function(chunk) { chunks.push(chunk); });\n res.on('end', function() {\n try {\n var json = JSON.parse(Buffer.concat(chunks).toString());\n if (json.error) {\n var err = new Error(json.error.message || 'RPC error');\n err.code = json.error.code;\n err.data = json.error.data;\n reject(err);\n } else {\n resolve(json.result);\n }\n } catch (e) {\n reject(new Error('Invalid JSON response from daemon'));\n }\n });\n });\n\n req.on('error', function(err) {\n reject(err);\n });\n\n req.on('timeout', function() {\n req.destroy();\n reject(new Error('Daemon request timed out'));\n });\n\n req.write(body);\n req.end();\n });\n}\n\n/**\n * Create a daemon client that implements the same interface as DirectClient.\n * @param {string} socketPath - Path to the Unix socket file.\n * @returns {object} Client object with MCP methods.\n */\nfunction createDaemonClient(socketPath) {\n function call(method, params) {\n return rpcCall(socketPath, method, params);\n }\n\n return {\n _isDaemon: true,\n ping: function() {\n return call('ping');\n },\n callTool: function(name, args) {\n return call('tools/call', { name: name, arguments: args || {} });\n },\n listTools: function() {\n return call('tools/list');\n },\n listResources: function() {\n return call('resources/list');\n },\n readResource: function(uri) {\n return call('resources/read', { uri: uri });\n },\n listResourceTemplates: function() {\n return call('resources/templates/list');\n },\n listPrompts: function() {\n return call('prompts/list');\n },\n getPrompt: function(name, args) {\n return call('prompts/get', { name: name, arguments: args || {} });\n },\n searchSkills: function(query) {\n return call('skills/search', { query: query || '' });\n },\n loadSkills: function(ids) {\n return call('skills/load', { ids: ids });\n },\n listSkills: function() {\n return call('skills/list');\n },\n listJobs: function() {\n return call('jobs/list');\n },\n executeJob: function(name, input, opts) {\n return call('jobs/execute', { name: name, input: input, options: opts });\n },\n getJobStatus: function(runId) {\n return call('jobs/status', { runId: runId });\n },\n listWorkflows: function() {\n return call('workflows/list');\n },\n executeWorkflow: function(name, input, opts) {\n return call('workflows/execute', { name: name, input: input, options: opts });\n },\n getWorkflowStatus: function(runId) {\n return call('workflows/status', { runId: runId });\n },\n subscribeResource: function(uri) {\n return call('resources/subscribe', { uri: uri });\n },\n unsubscribeResource: function(uri) {\n return call('resources/unsubscribe', { uri: uri });\n },\n onResourceUpdated: function() {\n console.warn('Resource subscriptions are not supported in daemon mode (HTTP-based, no push).');\n return function() {};\n },\n onNotification: function() {\n console.warn('Notifications are not supported in daemon mode (HTTP-based, no push).');\n return function() {};\n },\n close: function() {\n return Promise.resolve();\n }\n };\n}\n\nexports.createDaemonClient = createDaemonClient;\n`;\n}\n"]}
1
+ {"version":3,"file":"daemon-client.js","sourceRoot":"","sources":["../../../../../../src/commands/build/exec/cli-runtime/daemon-client.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;AAQH,gEAqJC;AAzJD;;;GAGG;AACH,SAAgB,0BAA0B;IACxC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmJR,CAAC;AACF,CAAC","sourcesContent":["/**\n * Lightweight daemon client for CLI exec.\n *\n * Sends MCP JSON-RPC requests over a Unix socket using Node.js built-in http module.\n * This avoids the full FrontMCP SDK initialization (~420ms) by talking to an\n * already-running daemon process.\n *\n * NOTE: This file is used as a CJS runtime module — it gets bundled into the CLI\n * output by esbuild alongside the generated CLI entry. It must remain free of\n * TypeScript-only constructs at runtime (the .ts extension is for build-time only).\n */\n\n \n\n/**\n * Generate the daemon-client JavaScript source code (CJS module).\n * This is embedded into the CLI bundle at build time.\n */\nexport function generateDaemonClientSource(): string {\n return `'use strict';\n\nvar http = require('http');\n\n/**\n * Send a JSON-RPC request over a Unix socket.\n * @param {string} socketPath - Path to the Unix socket file.\n * @param {string} method - JSON-RPC method name.\n * @param {object} [params] - Method parameters.\n * @returns {Promise<object>} Parsed JSON-RPC result.\n */\nfunction rpcCall(socketPath, method, params) {\n return new Promise(function(resolve, reject) {\n var body = JSON.stringify({\n jsonrpc: '2.0',\n id: Date.now(),\n method: method,\n params: params || {}\n });\n\n var req = http.request({\n socketPath: socketPath,\n path: '/mcp',\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Content-Length': Buffer.byteLength(body)\n },\n timeout: 10000\n }, function(res) {\n var chunks = [];\n res.on('data', function(chunk) { chunks.push(chunk); });\n res.on('end', function() {\n try {\n var json = JSON.parse(Buffer.concat(chunks).toString());\n if (json.error) {\n var err = new Error(json.error.message || 'RPC error');\n err.code = json.error.code;\n err.data = json.error.data;\n reject(err);\n } else {\n resolve(json.result);\n }\n } catch (e) {\n reject(new Error('Invalid JSON response from daemon'));\n }\n });\n });\n\n req.on('error', function(err) {\n reject(err);\n });\n\n req.on('timeout', function() {\n req.destroy();\n reject(new Error('Daemon request timed out'));\n });\n\n req.write(body);\n req.end();\n });\n}\n\n/**\n * Create a daemon client that implements the same interface as DirectClient.\n * @param {string} socketPath - Path to the Unix socket file.\n * @returns {object} Client object with MCP methods.\n */\nfunction createDaemonClient(socketPath) {\n function call(method, params) {\n return rpcCall(socketPath, method, params);\n }\n\n return {\n _isDaemon: true,\n ping: function() {\n return call('ping');\n },\n callTool: function(name, args) {\n return call('tools/call', { name: name, arguments: args || {} });\n },\n listTools: function() {\n return call('tools/list');\n },\n listResources: function() {\n return call('resources/list');\n },\n readResource: function(uri) {\n return call('resources/read', { uri: uri });\n },\n listResourceTemplates: function() {\n return call('resources/templates/list');\n },\n listPrompts: function() {\n return call('prompts/list');\n },\n getPrompt: function(name, args) {\n return call('prompts/get', { name: name, arguments: args || {} });\n },\n searchSkills: function(query) {\n return call('skills/search', { query: query || '' });\n },\n loadSkills: function(ids) {\n return call('skills/load', { ids: ids });\n },\n listSkills: function() {\n return call('skills/list');\n },\n listJobs: function() {\n return call('jobs/list');\n },\n executeJob: function(name, input, opts) {\n return call('jobs/execute', { name: name, input: input, options: opts });\n },\n getJobStatus: function(runId) {\n return call('jobs/status', { runId: runId });\n },\n listWorkflows: function() {\n return call('workflows/list');\n },\n executeWorkflow: function(name, input, opts) {\n return call('workflows/execute', { name: name, input: input, options: opts });\n },\n getWorkflowStatus: function(runId) {\n return call('workflows/status', { runId: runId });\n },\n subscribeResource: function(uri) {\n return call('resources/subscribe', { uri: uri });\n },\n unsubscribeResource: function(uri) {\n return call('resources/unsubscribe', { uri: uri });\n },\n onResourceUpdated: function() {\n console.warn('Resource subscriptions are not supported in daemon mode (HTTP-based, no push).');\n return function() {};\n },\n onNotification: function() {\n console.warn('Notifications are not supported in daemon mode (HTTP-based, no push).');\n return function() {};\n },\n close: function() {\n return Promise.resolve();\n }\n };\n}\n\nexports.createDaemonClient = createDaemonClient;\n`;\n}\n"]}
@@ -47,7 +47,7 @@ export interface ExtractedSchema {
47
47
  jobs: ExtractedJob[];
48
48
  capabilities: ExtractedCapabilities;
49
49
  }
50
- /** Known system tool names injected by SDK features (skills, jobs, workflows). */
50
+ /** Known system tool names injected by SDK features (jobs, workflows). */
51
51
  export declare const SYSTEM_TOOL_NAMES: Set<string>;
52
52
  /**
53
53
  * Extract schemas from a compiled server bundle.
@@ -7,10 +7,8 @@
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.SYSTEM_TOOL_NAMES = void 0;
9
9
  exports.extractSchemas = extractSchemas;
10
- /** Known system tool names injected by SDK features (skills, jobs, workflows). */
10
+ /** Known system tool names injected by SDK features (jobs, workflows). */
11
11
  exports.SYSTEM_TOOL_NAMES = new Set([
12
- 'searchSkills',
13
- 'loadSkills',
14
12
  'list-jobs',
15
13
  'execute-job',
16
14
  'get-job-status',
@@ -99,8 +97,9 @@ async function extractSchemas(bundlePath) {
99
97
  arguments: p.arguments,
100
98
  }));
101
99
  const toolNameSet = new Set(tools.map((t) => t.name));
100
+ const hasSkillsResources = resourceTemplates.some((rt) => rt.uriTemplate.startsWith('skills://'));
102
101
  const capabilities = {
103
- skills: toolNameSet.has('searchSkills') || toolNameSet.has('loadSkills'),
102
+ skills: hasSkillsResources,
104
103
  jobs: toolNameSet.has('execute-job') || toolNameSet.has('get-job-status'),
105
104
  workflows: toolNameSet.has('execute-workflow') || toolNameSet.has('get-workflow-status'),
106
105
  };
@@ -1 +1 @@
1
- {"version":3,"file":"schema-extractor.js","sourceRoot":"","sources":["../../../../../../src/commands/build/exec/cli-runtime/schema-extractor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Build-time schema extraction.\n * After the server bundle is produced, boots a DirectClient via connect(),\n * extracts tool/resource/prompt schemas, and serializes them for CLI code generation.\n */\n\nexport interface ExtractedTool {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n}\n\nexport interface ExtractedResource {\n uri: string;\n name: string;\n description?: string;\n mimeType?: string;\n}\n\nexport interface ExtractedResourceTemplate {\n uriTemplate: string;\n name: string;\n description?: string;\n}\n\nexport interface ExtractedPrompt {\n name: string;\n description?: string;\n arguments?: Array<{\n name: string;\n description?: string;\n required?: boolean;\n }>;\n}\n\nexport interface ExtractedJob {\n name: string;\n description?: string;\n inputSchema?: Record<string, unknown>;\n tags?: string[];\n}\n\nexport interface ExtractedCapabilities {\n skills: boolean;\n jobs: boolean;\n workflows: boolean;\n}\n\nexport interface ExtractedSchema {\n tools: ExtractedTool[];\n resources: ExtractedResource[];\n resourceTemplates: ExtractedResourceTemplate[];\n prompts: ExtractedPrompt[];\n jobs: ExtractedJob[];\n capabilities: ExtractedCapabilities;\n}\n\n/** Known system tool names injected by SDK features (skills, jobs, workflows). */\nexport const SYSTEM_TOOL_NAMES = new Set([\n 'searchSkills',\n 'loadSkills',\n 'list-jobs',\n 'execute-job',\n 'get-job-status',\n 'register-job',\n 'remove-job',\n 'list-workflows',\n 'execute-workflow',\n 'get-workflow-status',\n 'register-workflow',\n 'remove-workflow',\n]);\n\n/**\n * Extract schemas from a compiled server bundle.\n * Requires the bundle to export a FrontMcp-decorated class as default export\n * or a config object usable by connect().\n */\nexport async function extractSchemas(bundlePath: string): Promise<ExtractedSchema> {\n // Suppress @FrontMcp() decorator bootstrap — we only need metadata, not a running server\n const prev = process.env['FRONTMCP_SCHEMA_EXTRACT'];\n process.env['FRONTMCP_SCHEMA_EXTRACT'] = '1';\n\n let mod: Record<string, unknown>;\n try {\n mod = require(bundlePath);\n } finally {\n if (prev === undefined) {\n delete process.env['FRONTMCP_SCHEMA_EXTRACT'];\n } else {\n process.env['FRONTMCP_SCHEMA_EXTRACT'] = prev;\n }\n }\n\n const configOrClass = mod.default || mod;\n\n // Use @frontmcp/sdk connect() to boot in-memory client\n let connect: (config: unknown, options?: { mode?: string }) => Promise<unknown>;\n try {\n const sdk = require('@frontmcp/sdk');\n connect = sdk.connect || sdk.direct?.connect;\n if (!connect) {\n throw new Error('connect() not found in @frontmcp/sdk');\n }\n } catch {\n throw new Error(\n '@frontmcp/sdk is required for CLI schema extraction. Ensure it is installed.',\n );\n }\n\n const client = await connect(configOrClass, { mode: 'cli' }) as {\n listTools(): Promise<unknown>;\n listResources(): Promise<{ resources: Array<{ uri: string; name?: string; description?: string; mimeType?: string }> }>;\n listResourceTemplates?(): Promise<{ resourceTemplates: Array<{ uriTemplate: string; name?: string; description?: string }> }>;\n listPrompts(): Promise<{ prompts: Array<{ name: string; description?: string; arguments?: unknown[] }> }>;\n listJobs?(): Promise<{ jobs: Array<{ name: string; description?: string; inputSchema?: Record<string, unknown>; tags?: string[] }>; count: number }>;\n close(): Promise<void>;\n };\n\n try {\n const [toolsRaw, resourcesResult, promptsResult] = await Promise.all([\n client.listTools().catch(() => []),\n client.listResources().catch(() => ({ resources: [] })),\n client.listPrompts().catch(() => ({ prompts: [] })),\n ]);\n\n let resourceTemplates: ExtractedResourceTemplate[] = [];\n if (client.listResourceTemplates) {\n try {\n const templatesResult = await client.listResourceTemplates();\n resourceTemplates = (templatesResult.resourceTemplates || []).map((t) => ({\n uriTemplate: t.uriTemplate,\n name: t.name || t.uriTemplate,\n description: t.description,\n }));\n } catch {\n // Resource templates not supported\n }\n }\n\n // DirectClient.listTools() returns FormattedTools (array) directly,\n // not { tools: [...] } like the raw MCP client\n const toolsList = Array.isArray(toolsRaw)\n ? toolsRaw as Array<{ name: string; description?: string; inputSchema?: unknown }>\n : ((toolsRaw as { tools?: unknown[] })?.tools || []) as Array<{ name: string; description?: string; inputSchema?: unknown }>;\n\n const tools: ExtractedTool[] = toolsList.map((t) => ({\n name: t.name,\n description: t.description || '',\n inputSchema: (t.inputSchema as Record<string, unknown>) || { type: 'object', properties: {} },\n }));\n\n const resources: ExtractedResource[] = (resourcesResult.resources || []).map((r) => ({\n uri: r.uri,\n name: r.name || r.uri,\n description: r.description,\n mimeType: r.mimeType,\n }));\n\n const prompts: ExtractedPrompt[] = (promptsResult.prompts || []).map((p) => ({\n name: p.name,\n description: p.description,\n arguments: p.arguments as ExtractedPrompt['arguments'],\n }));\n\n const toolNameSet = new Set(tools.map((t) => t.name));\n const capabilities: ExtractedCapabilities = {\n skills: toolNameSet.has('searchSkills') || toolNameSet.has('loadSkills'),\n jobs: toolNameSet.has('execute-job') || toolNameSet.has('get-job-status'),\n workflows: toolNameSet.has('execute-workflow') || toolNameSet.has('get-workflow-status'),\n };\n\n // Extract job schemas if jobs capability is available\n let jobs: ExtractedJob[] = [];\n if (capabilities.jobs && client.listJobs) {\n try {\n const jobsResult = await client.listJobs();\n jobs = (jobsResult.jobs || []).map((j) => ({\n name: j.name,\n description: j.description,\n inputSchema: j.inputSchema,\n tags: j.tags,\n }));\n } catch {\n // Jobs listing not available at build time\n }\n }\n\n return { tools, resources, resourceTemplates, prompts, jobs, capabilities };\n } finally {\n await client.close().catch(() => {});\n }\n}\n"]}
1
+ {"version":3,"file":"schema-extractor.js","sourceRoot":"","sources":["../../../../../../src/commands/build/exec/cli-runtime/schema-extractor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Build-time schema extraction.\n * After the server bundle is produced, boots a DirectClient via connect(),\n * extracts tool/resource/prompt schemas, and serializes them for CLI code generation.\n */\n\nexport interface ExtractedTool {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n}\n\nexport interface ExtractedResource {\n uri: string;\n name: string;\n description?: string;\n mimeType?: string;\n}\n\nexport interface ExtractedResourceTemplate {\n uriTemplate: string;\n name: string;\n description?: string;\n}\n\nexport interface ExtractedPrompt {\n name: string;\n description?: string;\n arguments?: Array<{\n name: string;\n description?: string;\n required?: boolean;\n }>;\n}\n\nexport interface ExtractedJob {\n name: string;\n description?: string;\n inputSchema?: Record<string, unknown>;\n tags?: string[];\n}\n\nexport interface ExtractedCapabilities {\n skills: boolean;\n jobs: boolean;\n workflows: boolean;\n}\n\nexport interface ExtractedSchema {\n tools: ExtractedTool[];\n resources: ExtractedResource[];\n resourceTemplates: ExtractedResourceTemplate[];\n prompts: ExtractedPrompt[];\n jobs: ExtractedJob[];\n capabilities: ExtractedCapabilities;\n}\n\n/** Known system tool names injected by SDK features (jobs, workflows). */\nexport const SYSTEM_TOOL_NAMES = new Set([\n 'list-jobs',\n 'execute-job',\n 'get-job-status',\n 'register-job',\n 'remove-job',\n 'list-workflows',\n 'execute-workflow',\n 'get-workflow-status',\n 'register-workflow',\n 'remove-workflow',\n]);\n\n/**\n * Extract schemas from a compiled server bundle.\n * Requires the bundle to export a FrontMcp-decorated class as default export\n * or a config object usable by connect().\n */\nexport async function extractSchemas(bundlePath: string): Promise<ExtractedSchema> {\n // Suppress @FrontMcp() decorator bootstrap — we only need metadata, not a running server\n const prev = process.env['FRONTMCP_SCHEMA_EXTRACT'];\n process.env['FRONTMCP_SCHEMA_EXTRACT'] = '1';\n\n let mod: Record<string, unknown>;\n try {\n mod = require(bundlePath);\n } finally {\n if (prev === undefined) {\n delete process.env['FRONTMCP_SCHEMA_EXTRACT'];\n } else {\n process.env['FRONTMCP_SCHEMA_EXTRACT'] = prev;\n }\n }\n\n const configOrClass = mod.default || mod;\n\n // Use @frontmcp/sdk connect() to boot in-memory client\n let connect: (config: unknown, options?: { mode?: string }) => Promise<unknown>;\n try {\n const sdk = require('@frontmcp/sdk');\n connect = sdk.connect || sdk.direct?.connect;\n if (!connect) {\n throw new Error('connect() not found in @frontmcp/sdk');\n }\n } catch {\n throw new Error(\n '@frontmcp/sdk is required for CLI schema extraction. Ensure it is installed.',\n );\n }\n\n const client = await connect(configOrClass, { mode: 'cli' }) as {\n listTools(): Promise<unknown>;\n listResources(): Promise<{ resources: Array<{ uri: string; name?: string; description?: string; mimeType?: string }> }>;\n listResourceTemplates?(): Promise<{ resourceTemplates: Array<{ uriTemplate: string; name?: string; description?: string }> }>;\n listPrompts(): Promise<{ prompts: Array<{ name: string; description?: string; arguments?: unknown[] }> }>;\n listJobs?(): Promise<{ jobs: Array<{ name: string; description?: string; inputSchema?: Record<string, unknown>; tags?: string[] }>; count: number }>;\n close(): Promise<void>;\n };\n\n try {\n const [toolsRaw, resourcesResult, promptsResult] = await Promise.all([\n client.listTools().catch(() => []),\n client.listResources().catch(() => ({ resources: [] })),\n client.listPrompts().catch(() => ({ prompts: [] })),\n ]);\n\n let resourceTemplates: ExtractedResourceTemplate[] = [];\n if (client.listResourceTemplates) {\n try {\n const templatesResult = await client.listResourceTemplates();\n resourceTemplates = (templatesResult.resourceTemplates || []).map((t) => ({\n uriTemplate: t.uriTemplate,\n name: t.name || t.uriTemplate,\n description: t.description,\n }));\n } catch {\n // Resource templates not supported\n }\n }\n\n // DirectClient.listTools() returns FormattedTools (array) directly,\n // not { tools: [...] } like the raw MCP client\n const toolsList = Array.isArray(toolsRaw)\n ? toolsRaw as Array<{ name: string; description?: string; inputSchema?: unknown }>\n : ((toolsRaw as { tools?: unknown[] })?.tools || []) as Array<{ name: string; description?: string; inputSchema?: unknown }>;\n\n const tools: ExtractedTool[] = toolsList.map((t) => ({\n name: t.name,\n description: t.description || '',\n inputSchema: (t.inputSchema as Record<string, unknown>) || { type: 'object', properties: {} },\n }));\n\n const resources: ExtractedResource[] = (resourcesResult.resources || []).map((r) => ({\n uri: r.uri,\n name: r.name || r.uri,\n description: r.description,\n mimeType: r.mimeType,\n }));\n\n const prompts: ExtractedPrompt[] = (promptsResult.prompts || []).map((p) => ({\n name: p.name,\n description: p.description,\n arguments: p.arguments as ExtractedPrompt['arguments'],\n }));\n\n const toolNameSet = new Set(tools.map((t) => t.name));\n const hasSkillsResources = resourceTemplates.some(\n (rt) => rt.uriTemplate.startsWith('skills://'),\n );\n const capabilities: ExtractedCapabilities = {\n skills: hasSkillsResources,\n jobs: toolNameSet.has('execute-job') || toolNameSet.has('get-job-status'),\n workflows: toolNameSet.has('execute-workflow') || toolNameSet.has('get-workflow-status'),\n };\n\n // Extract job schemas if jobs capability is available\n let jobs: ExtractedJob[] = [];\n if (capabilities.jobs && client.listJobs) {\n try {\n const jobsResult = await client.listJobs();\n jobs = (jobsResult.jobs || []).map((j) => ({\n name: j.name,\n description: j.description,\n inputSchema: j.inputSchema,\n tags: j.tags,\n }));\n } catch {\n // Jobs listing not available at build time\n }\n }\n\n return { tools, resources, resourceTemplates, prompts, jobs, capabilities };\n } finally {\n await client.close().catch(() => {});\n }\n}\n"]}
@@ -65,18 +65,33 @@ async function runDev(opts) {
65
65
  clearForceKillTimer();
66
66
  }
67
67
  };
68
- process.on('SIGINT', () => {
68
+ process.once('SIGINT', () => {
69
69
  cleanup(false);
70
70
  // Force-kill after 2s if children haven't exited
71
71
  clearForceKillTimer();
72
72
  forceKillTimer = setTimeout(() => {
73
73
  killQuiet(checker, 'SIGKILL');
74
74
  killQuiet(app, 'SIGKILL');
75
- process.exit(1);
75
+ process.exit(0);
76
76
  }, 2000);
77
77
  forceKillTimer.unref();
78
+ // Exit cleanly once both children have closed
79
+ const tryExit = () => {
80
+ if (appClosed && checkerClosed) {
81
+ clearForceKillTimer();
82
+ process.exit(0);
83
+ }
84
+ };
85
+ app.once('close', () => {
86
+ markClosed('app');
87
+ tryExit();
88
+ });
89
+ checker.once('close', () => {
90
+ markClosed('checker');
91
+ tryExit();
92
+ });
78
93
  });
79
- process.on('SIGTERM', () => {
94
+ process.once('SIGTERM', () => {
80
95
  cleanup();
81
96
  process.exit(0);
82
97
  });
@@ -1 +1 @@
1
- {"version":3,"file":"dev.js","sourceRoot":"","sources":["../../../../src/commands/dev/dev.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path';\nimport { spawn, ChildProcess } from 'child_process';\nimport { ParsedArgs } from '../../core/args';\nimport { c } from '../../core/colors';\nimport { resolveEntry } from '../../shared/fs';\nimport { loadDevEnv } from '../../shared/env';\n\nfunction killQuiet(proc?: ChildProcess, signal: NodeJS.Signals = 'SIGINT') {\n try {\n if (proc && proc.exitCode === null && proc.signalCode === null) {\n proc.kill(signal);\n }\n } catch {\n // ignore\n }\n}\n\nexport async function runDev(opts: ParsedArgs): Promise<void> {\n const cwd = process.cwd();\n const entry = await resolveEntry(cwd, opts.entry);\n\n // Load .env and .env.local files before starting the server\n loadDevEnv(cwd);\n\n console.log(`${c('cyan', '[dev]')} using entry: ${path.relative(cwd, entry)}`);\n console.log(\n `${c('gray', '[dev]')} starting ${c('bold', 'tsx --watch')} and ${c(\n 'bold',\n 'tsc --noEmit --watch',\n )} (async type-checker)`,\n );\n console.log(`${c('gray', 'hint:')} press Ctrl+C to stop`);\n\n // Use --conditions node to ensure proper Node.js module resolution\n // This helps with dynamic require() calls in packages like ioredis\n // Only use shell on Windows where npx.cmd requires it; on Unix, direct spawn\n // allows proper SIGINT propagation without intermediate shell processes\n const useShell = process.platform === 'win32';\n const app = spawn('npx', ['-y', 'tsx', '--conditions', 'node', '--watch', entry], {\n stdio: 'inherit',\n shell: useShell,\n });\n const checker = spawn('npx', ['-y', 'tsc', '--noEmit', '--pretty', '--watch'], {\n stdio: 'inherit',\n shell: useShell,\n });\n\n const cleanup = (clearTimer = true) => {\n if (clearTimer) {\n clearForceKillTimer();\n }\n killQuiet(checker);\n killQuiet(app);\n };\n\n let forceKillTimer: NodeJS.Timeout | undefined;\n let appClosed = false;\n let checkerClosed = false;\n\n const clearForceKillTimer = () => {\n if (forceKillTimer) {\n clearTimeout(forceKillTimer);\n forceKillTimer = undefined;\n }\n };\n\n const markClosed = (child: 'app' | 'checker') => {\n if (child === 'app') {\n appClosed = true;\n } else {\n checkerClosed = true;\n }\n if (appClosed && checkerClosed) {\n clearForceKillTimer();\n }\n };\n\n process.on('SIGINT', () => {\n cleanup(false);\n // Force-kill after 2s if children haven't exited\n clearForceKillTimer();\n forceKillTimer = setTimeout(() => {\n killQuiet(checker, 'SIGKILL');\n killQuiet(app, 'SIGKILL');\n process.exit(1);\n }, 2000);\n forceKillTimer.unref();\n });\n\n process.on('SIGTERM', () => {\n cleanup();\n process.exit(0);\n });\n\n await new Promise<void>((resolve, reject) => {\n app.on('close', () => {\n markClosed('app');\n cleanup(false);\n resolve();\n });\n app.on('error', (err) => {\n clearForceKillTimer();\n cleanup();\n reject(err);\n });\n checker.on('close', () => {\n markClosed('checker');\n });\n checker.on('error', (err) => {\n clearForceKillTimer();\n cleanup();\n reject(err);\n });\n });\n}\n"]}
1
+ {"version":3,"file":"dev.js","sourceRoot":"","sources":["../../../../src/commands/dev/dev.ts"],"names":[],"mappings":";;AAiBA,wBAgHC;;AAjID,mDAA6B;AAC7B,iDAAoD;AAEpD,8CAAsC;AACtC,wCAA+C;AAC/C,0CAA8C;AAE9C,SAAS,SAAS,CAAC,IAAmB,EAAE,SAAyB,QAAQ;IACvE,IAAI,CAAC;QACH,IAAI,IAAI,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;YAC/D,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;AACH,CAAC;AAEM,KAAK,UAAU,MAAM,CAAC,IAAgB;IAC3C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,MAAM,KAAK,GAAG,MAAM,IAAA,iBAAY,EAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IAElD,4DAA4D;IAC5D,IAAA,gBAAU,EAAC,GAAG,CAAC,CAAC;IAEhB,OAAO,CAAC,GAAG,CAAC,GAAG,IAAA,UAAC,EAAC,MAAM,EAAE,OAAO,CAAC,iBAAiB,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;IAC/E,OAAO,CAAC,GAAG,CACT,GAAG,IAAA,UAAC,EAAC,MAAM,EAAE,OAAO,CAAC,aAAa,IAAA,UAAC,EAAC,MAAM,EAAE,aAAa,CAAC,QAAQ,IAAA,UAAC,EACjE,MAAM,EACN,sBAAsB,CACvB,uBAAuB,CACzB,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,GAAG,IAAA,UAAC,EAAC,MAAM,EAAE,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAE1D,mEAAmE;IACnE,mEAAmE;IACnE,6EAA6E;IAC7E,wEAAwE;IACxE,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC;IAC9C,MAAM,GAAG,GAAG,IAAA,qBAAK,EAAC,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC,EAAE;QAChF,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,QAAQ;KAChB,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,IAAA,qBAAK,EAAC,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE;QAC7E,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,QAAQ;KAChB,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,CAAC,UAAU,GAAG,IAAI,EAAE,EAAE;QACpC,IAAI,UAAU,EAAE,CAAC;YACf,mBAAmB,EAAE,CAAC;QACxB,CAAC;QACD,SAAS,CAAC,OAAO,CAAC,CAAC;QACnB,SAAS,CAAC,GAAG,CAAC,CAAC;IACjB,CAAC,CAAC;IAEF,IAAI,cAA0C,CAAC;IAC/C,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,aAAa,GAAG,KAAK,CAAC;IAE1B,MAAM,mBAAmB,GAAG,GAAG,EAAE;QAC/B,IAAI,cAAc,EAAE,CAAC;YACnB,YAAY,CAAC,cAAc,CAAC,CAAC;YAC7B,cAAc,GAAG,SAAS,CAAC;QAC7B,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,UAAU,GAAG,CAAC,KAAwB,EAAE,EAAE;QAC9C,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;YACpB,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC;aAAM,CAAC;YACN,aAAa,GAAG,IAAI,CAAC;QACvB,CAAC;QACD,IAAI,SAAS,IAAI,aAAa,EAAE,CAAC;YAC/B,mBAAmB,EAAE,CAAC;QACxB,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE;QAC1B,OAAO,CAAC,KAAK,CAAC,CAAC;QACf,iDAAiD;QACjD,mBAAmB,EAAE,CAAC;QACtB,cAAc,GAAG,UAAU,CAAC,GAAG,EAAE;YAC/B,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YAC9B,SAAS,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;YAC1B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,EAAE,IAAI,CAAC,CAAC;QACT,cAAc,CAAC,KAAK,EAAE,CAAC;QACvB,8CAA8C;QAC9C,MAAM,OAAO,GAAG,GAAG,EAAE;YACnB,IAAI,SAAS,IAAI,aAAa,EAAE,CAAC;gBAC/B,mBAAmB,EAAE,CAAC;gBACtB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;QACH,CAAC,CAAC;QACF,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;YACrB,UAAU,CAAC,KAAK,CAAC,CAAC;YAClB,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;YACzB,UAAU,CAAC,SAAS,CAAC,CAAC;YACtB,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE;QAC3B,OAAO,EAAE,CAAC;QACV,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC1C,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACnB,UAAU,CAAC,KAAK,CAAC,CAAC;YAClB,OAAO,CAAC,KAAK,CAAC,CAAC;YACf,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACtB,mBAAmB,EAAE,CAAC;YACtB,OAAO,EAAE,CAAC;YACV,MAAM,CAAC,GAAG,CAAC,CAAC;QACd,CAAC,CAAC,CAAC;QACH,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACvB,UAAU,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC,CAAC,CAAC;QACH,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YAC1B,mBAAmB,EAAE,CAAC;YACtB,OAAO,EAAE,CAAC;YACV,MAAM,CAAC,GAAG,CAAC,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import * as path from 'path';\nimport { spawn, ChildProcess } from 'child_process';\nimport { ParsedArgs } from '../../core/args';\nimport { c } from '../../core/colors';\nimport { resolveEntry } from '../../shared/fs';\nimport { loadDevEnv } from '../../shared/env';\n\nfunction killQuiet(proc?: ChildProcess, signal: NodeJS.Signals = 'SIGINT') {\n try {\n if (proc && proc.exitCode === null && proc.signalCode === null) {\n proc.kill(signal);\n }\n } catch {\n // ignore\n }\n}\n\nexport async function runDev(opts: ParsedArgs): Promise<void> {\n const cwd = process.cwd();\n const entry = await resolveEntry(cwd, opts.entry);\n\n // Load .env and .env.local files before starting the server\n loadDevEnv(cwd);\n\n console.log(`${c('cyan', '[dev]')} using entry: ${path.relative(cwd, entry)}`);\n console.log(\n `${c('gray', '[dev]')} starting ${c('bold', 'tsx --watch')} and ${c(\n 'bold',\n 'tsc --noEmit --watch',\n )} (async type-checker)`,\n );\n console.log(`${c('gray', 'hint:')} press Ctrl+C to stop`);\n\n // Use --conditions node to ensure proper Node.js module resolution\n // This helps with dynamic require() calls in packages like ioredis\n // Only use shell on Windows where npx.cmd requires it; on Unix, direct spawn\n // allows proper SIGINT propagation without intermediate shell processes\n const useShell = process.platform === 'win32';\n const app = spawn('npx', ['-y', 'tsx', '--conditions', 'node', '--watch', entry], {\n stdio: 'inherit',\n shell: useShell,\n });\n const checker = spawn('npx', ['-y', 'tsc', '--noEmit', '--pretty', '--watch'], {\n stdio: 'inherit',\n shell: useShell,\n });\n\n const cleanup = (clearTimer = true) => {\n if (clearTimer) {\n clearForceKillTimer();\n }\n killQuiet(checker);\n killQuiet(app);\n };\n\n let forceKillTimer: NodeJS.Timeout | undefined;\n let appClosed = false;\n let checkerClosed = false;\n\n const clearForceKillTimer = () => {\n if (forceKillTimer) {\n clearTimeout(forceKillTimer);\n forceKillTimer = undefined;\n }\n };\n\n const markClosed = (child: 'app' | 'checker') => {\n if (child === 'app') {\n appClosed = true;\n } else {\n checkerClosed = true;\n }\n if (appClosed && checkerClosed) {\n clearForceKillTimer();\n }\n };\n\n process.once('SIGINT', () => {\n cleanup(false);\n // Force-kill after 2s if children haven't exited\n clearForceKillTimer();\n forceKillTimer = setTimeout(() => {\n killQuiet(checker, 'SIGKILL');\n killQuiet(app, 'SIGKILL');\n process.exit(0);\n }, 2000);\n forceKillTimer.unref();\n // Exit cleanly once both children have closed\n const tryExit = () => {\n if (appClosed && checkerClosed) {\n clearForceKillTimer();\n process.exit(0);\n }\n };\n app.once('close', () => {\n markClosed('app');\n tryExit();\n });\n checker.once('close', () => {\n markClosed('checker');\n tryExit();\n });\n });\n\n process.once('SIGTERM', () => {\n cleanup();\n process.exit(0);\n });\n\n await new Promise<void>((resolve, reject) => {\n app.on('close', () => {\n markClosed('app');\n cleanup(false);\n resolve();\n });\n app.on('error', (err) => {\n clearForceKillTimer();\n cleanup();\n reject(err);\n });\n checker.on('close', () => {\n markClosed('checker');\n });\n checker.on('error', (err) => {\n clearForceKillTimer();\n cleanup();\n reject(err);\n });\n });\n}\n"]}
@@ -7,9 +7,12 @@ export interface HealthCheckResult {
7
7
  statusCode?: number;
8
8
  responseTime: number;
9
9
  error?: string;
10
+ body?: Record<string, unknown>;
10
11
  }
11
12
  export declare function checkHealth(opts: {
12
13
  port?: number;
13
14
  socketPath?: string;
14
15
  timeout?: number;
16
+ /** Endpoint to check: '/health', '/healthz', or '/readyz'. Defaults to '/health'. */
17
+ endpoint?: string;
15
18
  }): Promise<HealthCheckResult>;
@@ -13,7 +13,7 @@ function checkHealth(opts) {
13
13
  return new Promise((resolve) => {
14
14
  const requestOpts = {
15
15
  method: 'GET',
16
- path: '/health',
16
+ path: opts.endpoint ?? '/health',
17
17
  timeout,
18
18
  };
19
19
  if (opts.socketPath) {
@@ -33,12 +33,31 @@ function checkHealth(opts) {
33
33
  }
34
34
  const req = http.request(requestOpts, (res) => {
35
35
  const responseTime = Date.now() - start;
36
- resolve({
37
- healthy: res.statusCode === 200,
38
- statusCode: res.statusCode,
39
- responseTime,
36
+ const chunks = [];
37
+ res.on('data', (chunk) => chunks.push(chunk));
38
+ res.on('end', () => {
39
+ let body;
40
+ try {
41
+ body = JSON.parse(Buffer.concat(chunks).toString('utf-8'));
42
+ }
43
+ catch {
44
+ // Ignore parse errors — body is optional
45
+ }
46
+ resolve({
47
+ healthy: res.statusCode === 200,
48
+ statusCode: res.statusCode,
49
+ responseTime,
50
+ body,
51
+ });
52
+ });
53
+ res.on('error', (err) => {
54
+ resolve({
55
+ healthy: false,
56
+ statusCode: res.statusCode,
57
+ responseTime,
58
+ error: err.message,
59
+ });
40
60
  });
41
- res.resume(); // drain the response
42
61
  });
43
62
  req.on('error', (err) => {
44
63
  resolve({
@@ -1 +1 @@
1
- {"version":3,"file":"health.js","sourceRoot":"","sources":["../../../../src/commands/pm/health.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAWH,kCA0DC;;AAnED,mDAA6B;AAS7B,SAAgB,WAAW,CAAC,IAI3B;IACC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC;IACrC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAEzB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,WAAW,GAAwB;YACvC,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,SAAS;YACf,OAAO;SACR,CAAC;QAEF,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,WAAW,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;QAC3C,CAAC;aAAM,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACrB,WAAW,CAAC,QAAQ,GAAG,WAAW,CAAC;YACnC,WAAW,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QAC/B,CAAC;aAAM,CAAC;YACN,OAAO,CAAC;gBACN,OAAO,EAAE,KAAK;gBACd,YAAY,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;gBAChC,KAAK,EAAE,iCAAiC;aACzC,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,EAAE;YAC5C,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;YACxC,OAAO,CAAC;gBACN,OAAO,EAAE,GAAG,CAAC,UAAU,KAAK,GAAG;gBAC/B,UAAU,EAAE,GAAG,CAAC,UAAU;gBAC1B,YAAY;aACb,CAAC,CAAC;YACH,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,qBAAqB;QACrC,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACtB,OAAO,CAAC;gBACN,OAAO,EAAE,KAAK;gBACd,YAAY,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;gBAChC,KAAK,EAAE,GAAG,CAAC,OAAO;aACnB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;YACrB,GAAG,CAAC,OAAO,EAAE,CAAC;YACd,OAAO,CAAC;gBACN,OAAO,EAAE,KAAK;gBACd,YAAY,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;gBAChC,KAAK,EAAE,wBAAwB;aAChC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,GAAG,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["/**\n * HTTP health checks against running MCP servers.\n * Supports both TCP (port) and Unix socket connections.\n */\n\nimport * as http from 'http';\n\nexport interface HealthCheckResult {\n healthy: boolean;\n statusCode?: number;\n responseTime: number;\n error?: string;\n}\n\nexport function checkHealth(opts: {\n port?: number;\n socketPath?: string;\n timeout?: number;\n}): Promise<HealthCheckResult> {\n const timeout = opts.timeout ?? 5000;\n const start = Date.now();\n\n return new Promise((resolve) => {\n const requestOpts: http.RequestOptions = {\n method: 'GET',\n path: '/health',\n timeout,\n };\n\n if (opts.socketPath) {\n requestOpts.socketPath = opts.socketPath;\n } else if (opts.port) {\n requestOpts.hostname = '127.0.0.1';\n requestOpts.port = opts.port;\n } else {\n resolve({\n healthy: false,\n responseTime: Date.now() - start,\n error: 'No port or socket path provided',\n });\n return;\n }\n\n const req = http.request(requestOpts, (res) => {\n const responseTime = Date.now() - start;\n resolve({\n healthy: res.statusCode === 200,\n statusCode: res.statusCode,\n responseTime,\n });\n res.resume(); // drain the response\n });\n\n req.on('error', (err) => {\n resolve({\n healthy: false,\n responseTime: Date.now() - start,\n error: err.message,\n });\n });\n\n req.on('timeout', () => {\n req.destroy();\n resolve({\n healthy: false,\n responseTime: Date.now() - start,\n error: 'Health check timed out',\n });\n });\n\n req.end();\n });\n}\n"]}
1
+ {"version":3,"file":"health.js","sourceRoot":"","sources":["../../../../src/commands/pm/health.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * HTTP health checks against running MCP servers.\n * Supports both TCP (port) and Unix socket connections.\n */\n\nimport * as http from 'http';\n\nexport interface HealthCheckResult {\n healthy: boolean;\n statusCode?: number;\n responseTime: number;\n error?: string;\n body?: Record<string, unknown>;\n}\n\nexport function checkHealth(opts: {\n port?: number;\n socketPath?: string;\n timeout?: number;\n /** Endpoint to check: '/health', '/healthz', or '/readyz'. Defaults to '/health'. */\n endpoint?: string;\n}): Promise<HealthCheckResult> {\n const timeout = opts.timeout ?? 5000;\n const start = Date.now();\n\n return new Promise((resolve) => {\n const requestOpts: http.RequestOptions = {\n method: 'GET',\n path: opts.endpoint ?? '/health',\n timeout,\n };\n\n if (opts.socketPath) {\n requestOpts.socketPath = opts.socketPath;\n } else if (opts.port) {\n requestOpts.hostname = '127.0.0.1';\n requestOpts.port = opts.port;\n } else {\n resolve({\n healthy: false,\n responseTime: Date.now() - start,\n error: 'No port or socket path provided',\n });\n return;\n }\n\n const req = http.request(requestOpts, (res) => {\n const responseTime = Date.now() - start;\n const chunks: Buffer[] = [];\n res.on('data', (chunk: Buffer) => chunks.push(chunk));\n res.on('end', () => {\n let body: Record<string, unknown> | undefined;\n try {\n body = JSON.parse(Buffer.concat(chunks).toString('utf-8'));\n } catch {\n // Ignore parse errors — body is optional\n }\n resolve({\n healthy: res.statusCode === 200,\n statusCode: res.statusCode,\n responseTime,\n body,\n });\n });\n res.on('error', (err) => {\n resolve({\n healthy: false,\n statusCode: res.statusCode,\n responseTime,\n error: err.message,\n });\n });\n });\n\n req.on('error', (err) => {\n resolve({\n healthy: false,\n responseTime: Date.now() - start,\n error: err.message,\n });\n });\n\n req.on('timeout', () => {\n req.destroy();\n resolve({\n healthy: false,\n responseTime: Date.now() - start,\n error: 'Health check timed out',\n });\n });\n\n req.end();\n });\n}\n"]}