@agimon-ai/imagine-mcp 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- const e=require(`./stdio-D8OPo478.cjs`);let t=require(`commander`);const n={version:`0.1.0`},r=[{name:`mcp-serve`,description:`Start MCP server with specified transport`,loader:()=>Promise.resolve().then(()=>require(`./commands-D59ANVbf.cjs`)).then(e=>e.mcpServeCommand)}];var i=class extends Error{code;recovery;constructor(e,t=`CLI_EXECUTION_FAILED`,n){super(e,n),this.name=`CLIExecutionError`,this.code=t,this.recovery=n?.recovery}},a=class extends Error{code=`COMMAND_LOAD_ERROR`;recovery;commandName;constructor(e,t,n){super(`Failed to load command '${e}': ${t}`,n),this.name=`CommandLoadError`,this.commandName=e,this.recovery=`Check that the command module exists and exports the expected command. Run with --help to see available commands.`}};function o(e){let n=new t.Command(e.name).description(e.description).allowUnknownOption(!0).allowExcessArguments(!0);return n.action(async()=>{let n;try{n=await e.loader()}catch(t){throw t instanceof a?t:new a(e.name,t instanceof Error?t.message:String(t),{cause:t})}let r=process.argv.slice(2);await new t.Command().addCommand(n).parseAsync([`node`,`imagine-mcp`,...r])}),n}async function s(){let e=process.argv[2]??`unknown`;try{let e=new t.Command;e.name(`imagine`).description(`MCP server for AI image generation and manipulation`).version(n.version);for(let t of r)e.addCommand(o(t));await e.parseAsync(process.argv)}catch(t){if(t instanceof i)process.stderr.write(`Error [${t.code}] ${JSON.stringify({command:e,recovery:t.recovery,message:t.message,cause:t.cause instanceof Error?t.cause.message:t.cause})}\n`),t.recovery&&process.stderr.write(`Recovery: ${t.recovery}\n`),t.cause&&process.stderr.write(`Caused by: ${t.cause instanceof Error?t.cause.message:String(t.cause)}\n`);else if(t instanceof a)process.stderr.write(`Error [${t.code}] ${JSON.stringify({command:e,recovery:t.recovery,message:t.message})}\n`),t.recovery&&process.stderr.write(`Recovery: ${t.recovery}\n`),t.cause&&process.stderr.write(`Caused by: ${t.cause instanceof Error?t.cause.message:String(t.cause)}\n`);else{let n=t.code??`CLI_EXECUTION_FAILED`;process.stderr.write(`Error [${n}] ${JSON.stringify({command:e,message:t instanceof Error?t.message:String(t)})}\n`),process.stderr.write(`Recovery: Run with --help for usage information.
2
+ const e=require(`./stdio-D8OPo478.cjs`);let t=require(`commander`);const n={version:`0.1.0`},r=[{name:`mcp-serve`,description:`Start MCP server with specified transport`,loader:()=>Promise.resolve().then(()=>require(`./commands-D84TELoS.cjs`)).then(e=>e.mcpServeCommand)}];var i=class extends Error{code;recovery;constructor(e,t=`CLI_EXECUTION_FAILED`,n){super(e,n),this.name=`CLIExecutionError`,this.code=t,this.recovery=n?.recovery}},a=class extends Error{code=`COMMAND_LOAD_ERROR`;recovery;commandName;constructor(e,t,n){super(`Failed to load command '${e}': ${t}`,n),this.name=`CommandLoadError`,this.commandName=e,this.recovery=`Check that the command module exists and exports the expected command. Run with --help to see available commands.`}};function o(e){let n=new t.Command(e.name).description(e.description).allowUnknownOption(!0).allowExcessArguments(!0);return n.action(async()=>{let n;try{n=await e.loader()}catch(t){throw t instanceof a?t:new a(e.name,t instanceof Error?t.message:String(t),{cause:t})}let r=process.argv.slice(2);await new t.Command().addCommand(n).parseAsync([`node`,`imagine-mcp`,...r])}),n}async function s(){let e=process.argv[2]??`unknown`;try{let e=new t.Command;e.name(`imagine`).description(`MCP server for AI image generation and manipulation`).version(n.version);for(let t of r)e.addCommand(o(t));await e.parseAsync(process.argv)}catch(t){if(t instanceof i)process.stderr.write(`Error [${t.code}] ${JSON.stringify({command:e,recovery:t.recovery,message:t.message,cause:t.cause instanceof Error?t.cause.message:t.cause})}\n`),t.recovery&&process.stderr.write(`Recovery: ${t.recovery}\n`),t.cause&&process.stderr.write(`Caused by: ${t.cause instanceof Error?t.cause.message:String(t.cause)}\n`);else if(t instanceof a)process.stderr.write(`Error [${t.code}] ${JSON.stringify({command:e,recovery:t.recovery,message:t.message})}\n`),t.recovery&&process.stderr.write(`Recovery: ${t.recovery}\n`),t.cause&&process.stderr.write(`Caused by: ${t.cause instanceof Error?t.cause.message:String(t.cause)}\n`);else{let n=t.code??`CLI_EXECUTION_FAILED`;process.stderr.write(`Error [${n}] ${JSON.stringify({command:e,message:t instanceof Error?t.message:String(t)})}\n`),process.stderr.write(`Recovery: Run with --help for usage information.
3
3
  `),t instanceof Error&&t.cause&&process.stderr.write(`Caused by: ${t.cause instanceof Error?t.cause.message:String(t.cause)}\n`)}process.exit(1)}}s(),exports.CLIExecutionError=i,exports.CommandLoadError=a,exports.commandDefinitions=r,exports.createLazyCommand=o;
4
4
  //# sourceMappingURL=cli.cjs.map
package/dist/cli.mjs CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import{Command as e}from"commander";const t={version:`0.1.0`},n=[{name:`mcp-serve`,description:`Start MCP server with specified transport`,loader:()=>import(`./commands-Dw2IITFV.mjs`).then(e=>e.mcpServeCommand)}];var r=class extends Error{code;recovery;constructor(e,t=`CLI_EXECUTION_FAILED`,n){super(e,n),this.name=`CLIExecutionError`,this.code=t,this.recovery=n?.recovery}},i=class extends Error{code=`COMMAND_LOAD_ERROR`;recovery;commandName;constructor(e,t,n){super(`Failed to load command '${e}': ${t}`,n),this.name=`CommandLoadError`,this.commandName=e,this.recovery=`Check that the command module exists and exports the expected command. Run with --help to see available commands.`}};function a(t){let n=new e(t.name).description(t.description).allowUnknownOption(!0).allowExcessArguments(!0);return n.action(async()=>{let n;try{n=await t.loader()}catch(e){throw e instanceof i?e:new i(t.name,e instanceof Error?e.message:String(e),{cause:e})}let r=process.argv.slice(2);await new e().addCommand(n).parseAsync([`node`,`imagine-mcp`,...r])}),n}async function o(){let o=process.argv[2]??`unknown`;try{let r=new e;r.name(`imagine`).description(`MCP server for AI image generation and manipulation`).version(t.version);for(let e of n)r.addCommand(a(e));await r.parseAsync(process.argv)}catch(e){if(e instanceof r)process.stderr.write(`Error [${e.code}] ${JSON.stringify({command:o,recovery:e.recovery,message:e.message,cause:e.cause instanceof Error?e.cause.message:e.cause})}\n`),e.recovery&&process.stderr.write(`Recovery: ${e.recovery}\n`),e.cause&&process.stderr.write(`Caused by: ${e.cause instanceof Error?e.cause.message:String(e.cause)}\n`);else if(e instanceof i)process.stderr.write(`Error [${e.code}] ${JSON.stringify({command:o,recovery:e.recovery,message:e.message})}\n`),e.recovery&&process.stderr.write(`Recovery: ${e.recovery}\n`),e.cause&&process.stderr.write(`Caused by: ${e.cause instanceof Error?e.cause.message:String(e.cause)}\n`);else{let t=e.code??`CLI_EXECUTION_FAILED`;process.stderr.write(`Error [${t}] ${JSON.stringify({command:o,message:e instanceof Error?e.message:String(e)})}\n`),process.stderr.write(`Recovery: Run with --help for usage information.
2
+ import{Command as e}from"commander";const t={version:`0.1.0`},n=[{name:`mcp-serve`,description:`Start MCP server with specified transport`,loader:()=>import(`./commands-D0-npj2G.mjs`).then(e=>e.mcpServeCommand)}];var r=class extends Error{code;recovery;constructor(e,t=`CLI_EXECUTION_FAILED`,n){super(e,n),this.name=`CLIExecutionError`,this.code=t,this.recovery=n?.recovery}},i=class extends Error{code=`COMMAND_LOAD_ERROR`;recovery;commandName;constructor(e,t,n){super(`Failed to load command '${e}': ${t}`,n),this.name=`CommandLoadError`,this.commandName=e,this.recovery=`Check that the command module exists and exports the expected command. Run with --help to see available commands.`}};function a(t){let n=new e(t.name).description(t.description).allowUnknownOption(!0).allowExcessArguments(!0);return n.action(async()=>{let n;try{n=await t.loader()}catch(e){throw e instanceof i?e:new i(t.name,e instanceof Error?e.message:String(e),{cause:e})}let r=process.argv.slice(2);await new e().addCommand(n).parseAsync([`node`,`imagine-mcp`,...r])}),n}async function o(){let o=process.argv[2]??`unknown`;try{let r=new e;r.name(`imagine`).description(`MCP server for AI image generation and manipulation`).version(t.version);for(let e of n)r.addCommand(a(e));await r.parseAsync(process.argv)}catch(e){if(e instanceof r)process.stderr.write(`Error [${e.code}] ${JSON.stringify({command:o,recovery:e.recovery,message:e.message,cause:e.cause instanceof Error?e.cause.message:e.cause})}\n`),e.recovery&&process.stderr.write(`Recovery: ${e.recovery}\n`),e.cause&&process.stderr.write(`Caused by: ${e.cause instanceof Error?e.cause.message:String(e.cause)}\n`);else if(e instanceof i)process.stderr.write(`Error [${e.code}] ${JSON.stringify({command:o,recovery:e.recovery,message:e.message})}\n`),e.recovery&&process.stderr.write(`Recovery: ${e.recovery}\n`),e.cause&&process.stderr.write(`Caused by: ${e.cause instanceof Error?e.cause.message:String(e.cause)}\n`);else{let t=e.code??`CLI_EXECUTION_FAILED`;process.stderr.write(`Error [${t}] ${JSON.stringify({command:o,message:e instanceof Error?e.message:String(e)})}\n`),process.stderr.write(`Recovery: Run with --help for usage information.
3
3
  `),e instanceof Error&&e.cause&&process.stderr.write(`Caused by: ${e.cause instanceof Error?e.cause.message:String(e.cause)}\n`)}process.exit(1)}}o();export{r as CLIExecutionError,i as CommandLoadError,n as commandDefinitions,a as createLazyCommand};
4
4
  //# sourceMappingURL=cli.mjs.map
@@ -0,0 +1,2 @@
1
+ import{i as e,n as t,r as n,t as r}from"./stdio-DYg9ZLDI.mjs";import{Command as i}from"commander";import{existsSync as a}from"node:fs";import o from"node:path";import{DEFAULT_PORT_RANGE as s,PortRegistryService as c}from"@agimon-ai/foundation-port-registry";import{createProcessLease as l}from"@agimon-ai/foundation-process-registry";let u=function(e){return e.STDIO=`stdio`,e.HTTP=`http`,e.SSE=`sse`,e}({});const d=[`pnpm-workspace.yaml`,`nx.json`,`.git`],f=`tool`,p=process.env.NODE_ENV||`development`,m=process.env.MCP_HOST||`localhost`;function h(e=process.cwd()){let t=o.resolve(e);for(;;){for(let e of d)if(a(o.join(t,e)))return t;let e=o.dirname(t);if(e===t)return process.cwd();t=e}}function g(){return new c(process.env.PORT_REGISTRY_PATH)}function _(e){if(typeof e==`number`)return Number.isInteger(e)&&e>0&&e<=65535?e:void 0;if(typeof e==`string`&&e.trim().length>0){let t=Number.parseInt(e,10);return Number.isInteger(t)&&t>0&&t<=65535?t:void 0}}async function v(e,t,n,r){let i=g(),a=r??s.min,o=r?{min:r,max:Math.max(s.max,r)}:s,c=await i.reservePort({repositoryPath:e,serviceName:t,serviceType:f,environment:p,preferredPort:a,portRange:o,pid:process.pid,host:m,force:!0,metadata:{transport:n}});if(!c.success||!c.record)throw Error(c.error||`Failed to reserve port for ${t}`);let l=!1;return{port:c.record.port,release:async()=>{if(l)return;l=!0;let n=await i.releasePort({repositoryPath:e,serviceName:t,serviceType:f,environment:p,pid:process.pid});if(!n.success&&!n.error?.includes(`No matching registry entry`))throw Error(n.error||`Failed to release port for ${t}`)}}}async function y(e){for(let t of e)t&&await t.release()}async function b(e,t){await e.start();let n=async n=>{process.stderr.write(`\nReceived ${n}, shutting down gracefully...\n`);let r;try{await e.stop()}catch(e){r=e}try{await t?.()}catch(e){r??=e}r&&(process.stderr.write(`Error during shutdown: ${r instanceof Error?r.message:String(r)}\n`),process.exit(1)),process.exit(0)};process.on(`SIGINT`,()=>n(`SIGINT`)),process.on(`SIGTERM`,()=>n(`SIGTERM`))}const x=new i(`mcp-serve`).description(`Start MCP server with specified transport`).option(`-t, --type <type>`,`Transport type: stdio, http, or sse`,`stdio`).option(`-p, --port <port>`,`Port to listen on (http/sse only)`,e=>Number.parseInt(e,10)).option(`--host <host>`,`Host to bind to (http/sse only)`,`localhost`).action(async i=>{let a,o;try{let s=i.type.toLowerCase(),c=h(process.cwd());if(s===`stdio`){let t=new r(e());o=await l({repositoryPath:c,serviceName:`imagine-mcp-stdio`,pid:process.pid,metadata:{transport:`stdio`}}),await b(t,async()=>{await o?.release()})}else if(s===`http`){let t=_(i.port)??_(process.env.MCP_PORT);a=await v(c,`imagine-mcp-http`,u.HTTP,t),o=await l({repositoryPath:c,serviceName:`imagine-mcp-http`,pid:process.pid,metadata:{transport:`http`},port:a.port,host:i.host||m,command:process.argv[1],args:process.argv.slice(2)}),await b(new n(()=>e(),{mode:u.HTTP,port:a.port,host:i.host||m}),async()=>{await y([o,a])})}else if(s===`sse`){let n=_(i.port)??_(process.env.MCP_PORT);a=await v(c,`imagine-mcp-sse`,u.SSE,n),o=await l({repositoryPath:c,serviceName:`imagine-mcp-sse`,pid:process.pid,metadata:{transport:`sse`},port:a.port,host:i.host||m,command:process.argv[1],args:process.argv.slice(2)}),await b(new t(()=>e(),{mode:u.SSE,port:a.port,host:i.host||m}),async()=>{await y([o,a])})}else process.stderr.write(`Unknown transport type: ${s}. Use: stdio, http, or sse\n`),process.exit(1)}catch(e){await y([o,a]).catch(()=>void 0),process.stderr.write(`Failed to start MCP server: ${e instanceof Error?e.message:String(e)}\n`),process.exit(1)}});export{x as mcpServeCommand};
2
+ //# sourceMappingURL=commands-D0-npj2G.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands-D0-npj2G.mjs","names":["shutdownError: unknown","portLease: PortLease | undefined","processLease: ProcessLease | undefined"],"sources":["../src/types/index.ts","../src/commands/mcp-serve.ts"],"sourcesContent":["/**\n * Shared TypeScript Types\n *\n * DESIGN PATTERNS:\n * - Type-first development\n * - Interface segregation\n *\n * CODING STANDARDS:\n * - Export all shared types from this file\n * - Use descriptive names for types and interfaces\n */\n\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\n\n/**\n * Tool definition for MCP\n */\nexport interface ToolDefinition {\n name: string;\n description: string;\n inputSchema: {\n type: string;\n properties: Record<string, unknown>;\n required?: string[];\n additionalProperties?: boolean;\n };\n}\n\n/**\n * Base tool interface following MCP SDK patterns\n */\nexport interface Tool<TInput = unknown> {\n getDefinition(): ToolDefinition;\n execute(input: TInput): Promise<CallToolResult>;\n}\n\n/**\n * Transport mode types\n */\nexport enum TransportMode {\n STDIO = 'stdio',\n HTTP = 'http',\n SSE = 'sse',\n}\n\n/**\n * Transport configuration options\n */\nexport interface TransportConfig {\n mode: TransportMode;\n port?: number;\n host?: string;\n}\n\n/**\n * Base interface for all transport handlers\n */\nexport interface TransportHandler {\n start(): Promise<void>;\n stop(): Promise<void>;\n}\n\n/**\n * HTTP transport specific types\n */\nexport interface HttpTransportHandler extends TransportHandler {\n getPort(): number;\n getHost(): string;\n}\n","/**\n * MCP Serve Command\n *\n * DESIGN PATTERNS:\n * - Command pattern with Commander for CLI argument parsing\n * - Transport abstraction pattern for flexible deployment (stdio, HTTP, SSE)\n * - Factory pattern for creating transport handlers\n * - Graceful shutdown pattern with signal handling\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Implement proper error handling with try-catch blocks\n * - Handle process signals for graceful shutdown\n * - Provide clear CLI options and help messages\n *\n * AVOID:\n * - Hardcoded configuration values (use CLI options or environment variables)\n * - Missing error handling for transport startup\n * - Not cleaning up resources on shutdown\n */\n\nimport { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport { DEFAULT_PORT_RANGE, PortRegistryService } from '@agimon-ai/foundation-port-registry';\nimport { type ProcessLease, createProcessLease } from '@agimon-ai/foundation-process-registry';\nimport { Command } from 'commander';\nimport { createServer } from '../server';\nimport { HttpTransportHandler } from '../transports/http';\nimport { SseTransportHandler } from '../transports/sse';\nimport { StdioTransportHandler } from '../transports/stdio';\nimport { type TransportConfig, TransportMode } from '../types';\n\ninterface LifecycleHandler {\n start(): Promise<void>;\n stop(): Promise<void>;\n}\n\ninterface PortLease {\n port: number;\n release(): Promise<void>;\n}\n\nconst WORKSPACE_MARKERS = ['pnpm-workspace.yaml', 'nx.json', '.git'];\nconst DEFAULT_SERVICE_TYPE = 'tool' as const;\nconst DEFAULT_ENVIRONMENT = process.env.NODE_ENV || 'development';\nconst DEFAULT_HOST = process.env.MCP_HOST || 'localhost';\n\nfunction resolveWorkspaceRoot(startPath = process.cwd()): string {\n let current = path.resolve(startPath);\n\n while (true) {\n for (const marker of WORKSPACE_MARKERS) {\n if (existsSync(path.join(current, marker))) {\n return current;\n }\n }\n\n const parent = path.dirname(current);\n if (parent === current) {\n return process.cwd();\n }\n\n current = parent;\n }\n}\n\nfunction createPortRegistryService(): PortRegistryService {\n return new PortRegistryService(process.env.PORT_REGISTRY_PATH);\n}\n\nfunction parseRequestedPort(value: unknown): number | undefined {\n if (typeof value === 'number') {\n return Number.isInteger(value) && value > 0 && value <= 65535 ? value : undefined;\n }\n\n if (typeof value === 'string' && value.trim().length > 0) {\n const parsed = Number.parseInt(value, 10);\n return Number.isInteger(parsed) && parsed > 0 && parsed <= 65535 ? parsed : undefined;\n }\n\n return undefined;\n}\n\nasync function reservePortLease(\n repositoryPath: string,\n serviceName: string,\n transport: TransportMode.HTTP | TransportMode.SSE,\n requestedPort?: number,\n): Promise<PortLease> {\n const portRegistry = createPortRegistryService();\n const preferredPort = requestedPort ?? DEFAULT_PORT_RANGE.min;\n const portRange = requestedPort\n ? { min: requestedPort, max: Math.max(DEFAULT_PORT_RANGE.max, requestedPort) }\n : DEFAULT_PORT_RANGE;\n const response = await portRegistry.reservePort({\n repositoryPath,\n serviceName,\n serviceType: DEFAULT_SERVICE_TYPE,\n environment: DEFAULT_ENVIRONMENT,\n preferredPort,\n portRange,\n pid: process.pid,\n host: DEFAULT_HOST,\n force: true,\n metadata: { transport },\n });\n\n if (!response.success || !response.record) {\n throw new Error(response.error || `Failed to reserve port for ${serviceName}`);\n }\n\n let released = false;\n return {\n port: response.record.port,\n release: async () => {\n if (released) {\n return;\n }\n released = true;\n\n const releaseResponse = await portRegistry.releasePort({\n repositoryPath,\n serviceName,\n serviceType: DEFAULT_SERVICE_TYPE,\n environment: DEFAULT_ENVIRONMENT,\n pid: process.pid,\n });\n\n if (!releaseResponse.success && !releaseResponse.error?.includes('No matching registry entry')) {\n throw new Error(releaseResponse.error || `Failed to release port for ${serviceName}`);\n }\n },\n };\n}\n\nasync function cleanupLeases(leases: Array<PortLease | ProcessLease | undefined>): Promise<void> {\n for (const lease of leases) {\n if (!lease) {\n continue;\n }\n await lease.release();\n }\n}\n\n/**\n * Start MCP server with given transport handler\n */\nasync function startServer(handler: LifecycleHandler, onShutdown?: () => Promise<void>) {\n await handler.start();\n\n // Handle graceful shutdown\n const shutdown = async (signal: string) => {\n process.stderr.write(`\\nReceived ${signal}, shutting down gracefully...\\n`);\n let shutdownError: unknown;\n\n try {\n await handler.stop();\n } catch (error) {\n shutdownError = error;\n }\n\n try {\n await onShutdown?.();\n } catch (error) {\n shutdownError = shutdownError ?? error;\n }\n\n if (shutdownError) {\n process.stderr.write(\n `Error during shutdown: ${shutdownError instanceof Error ? shutdownError.message : String(shutdownError)}\\n`,\n );\n process.exit(1);\n }\n\n process.exit(0);\n };\n\n process.on('SIGINT', () => shutdown('SIGINT'));\n process.on('SIGTERM', () => shutdown('SIGTERM'));\n}\n\n/**\n * MCP Serve command\n */\nexport const mcpServeCommand = new Command('mcp-serve')\n .description('Start MCP server with specified transport')\n .option('-t, --type <type>', 'Transport type: stdio, http, or sse', 'stdio')\n .option('-p, --port <port>', 'Port to listen on (http/sse only)', (val) => Number.parseInt(val, 10))\n .option('--host <host>', 'Host to bind to (http/sse only)', 'localhost')\n .action(async (options) => {\n let portLease: PortLease | undefined;\n let processLease: ProcessLease | undefined;\n try {\n const transportType = options.type.toLowerCase();\n const repositoryPath = resolveWorkspaceRoot(process.cwd());\n\n if (transportType === 'stdio') {\n const server = createServer();\n const handler = new StdioTransportHandler(server);\n processLease = await createProcessLease({\n repositoryPath,\n serviceName: 'imagine-mcp-stdio',\n pid: process.pid,\n metadata: { transport: 'stdio' },\n });\n await startServer(handler, async () => {\n await processLease?.release();\n });\n } else if (transportType === 'http') {\n const requestedPort = parseRequestedPort(options.port) ?? parseRequestedPort(process.env.MCP_PORT);\n portLease = await reservePortLease(repositoryPath, 'imagine-mcp-http', TransportMode.HTTP, requestedPort);\n processLease = await createProcessLease({\n repositoryPath,\n serviceName: 'imagine-mcp-http',\n pid: process.pid,\n metadata: { transport: 'http' },\n port: portLease.port,\n host: options.host || DEFAULT_HOST,\n command: process.argv[1],\n args: process.argv.slice(2),\n });\n // For HTTP, pass a factory function to create new server instances per session\n const config: TransportConfig = {\n mode: TransportMode.HTTP,\n port: portLease.port,\n host: options.host || DEFAULT_HOST,\n };\n const handler = new HttpTransportHandler(() => createServer(), config);\n await startServer(handler, async () => {\n await cleanupLeases([processLease, portLease]);\n });\n } else if (transportType === 'sse') {\n const requestedPort = parseRequestedPort(options.port) ?? parseRequestedPort(process.env.MCP_PORT);\n portLease = await reservePortLease(repositoryPath, 'imagine-mcp-sse', TransportMode.SSE, requestedPort);\n processLease = await createProcessLease({\n repositoryPath,\n serviceName: 'imagine-mcp-sse',\n pid: process.pid,\n metadata: { transport: 'sse' },\n port: portLease.port,\n host: options.host || DEFAULT_HOST,\n command: process.argv[1],\n args: process.argv.slice(2),\n });\n // For SSE, pass a factory function to create new server instances per connection\n const config: TransportConfig = {\n mode: TransportMode.SSE,\n port: portLease.port,\n host: options.host || DEFAULT_HOST,\n };\n const handler = new SseTransportHandler(() => createServer(), config);\n await startServer(handler, async () => {\n await cleanupLeases([processLease, portLease]);\n });\n } else {\n process.stderr.write(`Unknown transport type: ${transportType}. Use: stdio, http, or sse\\n`);\n process.exit(1);\n }\n } catch (error) {\n await cleanupLeases([processLease, portLease]).catch(() => undefined);\n process.stderr.write(`Failed to start MCP server: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(1);\n }\n });\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ const e=require(`./stdio-D8OPo478.cjs`);let t=require(`commander`),n=require(`node:fs`),r=require(`node:path`);r=e.s(r);let i=require(`@agimon-ai/foundation-port-registry`),a=require(`@agimon-ai/foundation-process-registry`),o=function(e){return e.STDIO=`stdio`,e.HTTP=`http`,e.SSE=`sse`,e}({});const s=[`pnpm-workspace.yaml`,`nx.json`,`.git`],c=`tool`,l=process.env.NODE_ENV||`development`,u=process.env.MCP_HOST||`localhost`;function d(e=process.cwd()){let t=r.default.resolve(e);for(;;){for(let e of s)if((0,n.existsSync)(r.default.join(t,e)))return t;let e=r.default.dirname(t);if(e===t)return process.cwd();t=e}}function f(){return new i.PortRegistryService(process.env.PORT_REGISTRY_PATH)}function p(e){if(typeof e==`number`)return Number.isInteger(e)&&e>0&&e<=65535?e:void 0;if(typeof e==`string`&&e.trim().length>0){let t=Number.parseInt(e,10);return Number.isInteger(t)&&t>0&&t<=65535?t:void 0}}async function m(e,t,n,r){let a=f(),o=r??i.DEFAULT_PORT_RANGE.min,s=r?{min:r,max:Math.max(i.DEFAULT_PORT_RANGE.max,r)}:i.DEFAULT_PORT_RANGE,d=await a.reservePort({repositoryPath:e,serviceName:t,serviceType:c,environment:l,preferredPort:o,portRange:s,pid:process.pid,host:u,force:!0,metadata:{transport:n}});if(!d.success||!d.record)throw Error(d.error||`Failed to reserve port for ${t}`);let p=!1;return{port:d.record.port,release:async()=>{if(p)return;p=!0;let n=await a.releasePort({repositoryPath:e,serviceName:t,serviceType:c,environment:l,pid:process.pid});if(!n.success&&!n.error?.includes(`No matching registry entry`))throw Error(n.error||`Failed to release port for ${t}`)}}}async function h(e){for(let t of e)t&&await t.release()}async function g(e,t){await e.start();let n=async n=>{process.stderr.write(`\nReceived ${n}, shutting down gracefully...\n`);let r;try{await e.stop()}catch(e){r=e}try{await t?.()}catch(e){r??=e}r&&(process.stderr.write(`Error during shutdown: ${r instanceof Error?r.message:String(r)}\n`),process.exit(1)),process.exit(0)};process.on(`SIGINT`,()=>n(`SIGINT`)),process.on(`SIGTERM`,()=>n(`SIGTERM`))}const _=new t.Command(`mcp-serve`).description(`Start MCP server with specified transport`).option(`-t, --type <type>`,`Transport type: stdio, http, or sse`,`stdio`).option(`-p, --port <port>`,`Port to listen on (http/sse only)`,e=>Number.parseInt(e,10)).option(`--host <host>`,`Host to bind to (http/sse only)`,`localhost`).action(async t=>{let n,r;try{let i=t.type.toLowerCase(),s=d(process.cwd());if(i===`stdio`){let t=new e.t(e.i());r=await(0,a.createProcessLease)({repositoryPath:s,serviceName:`imagine-mcp-stdio`,pid:process.pid,metadata:{transport:`stdio`}}),await g(t,async()=>{await r?.release()})}else if(i===`http`){let i=p(t.port)??p(process.env.MCP_PORT);n=await m(s,`imagine-mcp-http`,o.HTTP,i),r=await(0,a.createProcessLease)({repositoryPath:s,serviceName:`imagine-mcp-http`,pid:process.pid,metadata:{transport:`http`},port:n.port,host:t.host||u,command:process.argv[1],args:process.argv.slice(2)}),await g(new e.r(()=>e.i(),{mode:o.HTTP,port:n.port,host:t.host||u}),async()=>{await h([r,n])})}else if(i===`sse`){let i=p(t.port)??p(process.env.MCP_PORT);n=await m(s,`imagine-mcp-sse`,o.SSE,i),r=await(0,a.createProcessLease)({repositoryPath:s,serviceName:`imagine-mcp-sse`,pid:process.pid,metadata:{transport:`sse`},port:n.port,host:t.host||u,command:process.argv[1],args:process.argv.slice(2)}),await g(new e.n(()=>e.i(),{mode:o.SSE,port:n.port,host:t.host||u}),async()=>{await h([r,n])})}else process.stderr.write(`Unknown transport type: ${i}. Use: stdio, http, or sse\n`),process.exit(1)}catch(e){await h([r,n]).catch(()=>void 0),process.stderr.write(`Failed to start MCP server: ${e instanceof Error?e.message:String(e)}\n`),process.exit(1)}});exports.mcpServeCommand=_;
2
+ //# sourceMappingURL=commands-D84TELoS.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands-D84TELoS.cjs","names":["path","PortRegistryService","DEFAULT_PORT_RANGE","shutdownError: unknown","Command","portLease: PortLease | undefined","processLease: ProcessLease | undefined","StdioTransportHandler","createServer","HttpTransportHandler","SseTransportHandler"],"sources":["../src/types/index.ts","../src/commands/mcp-serve.ts"],"sourcesContent":["/**\n * Shared TypeScript Types\n *\n * DESIGN PATTERNS:\n * - Type-first development\n * - Interface segregation\n *\n * CODING STANDARDS:\n * - Export all shared types from this file\n * - Use descriptive names for types and interfaces\n */\n\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\n\n/**\n * Tool definition for MCP\n */\nexport interface ToolDefinition {\n name: string;\n description: string;\n inputSchema: {\n type: string;\n properties: Record<string, unknown>;\n required?: string[];\n additionalProperties?: boolean;\n };\n}\n\n/**\n * Base tool interface following MCP SDK patterns\n */\nexport interface Tool<TInput = unknown> {\n getDefinition(): ToolDefinition;\n execute(input: TInput): Promise<CallToolResult>;\n}\n\n/**\n * Transport mode types\n */\nexport enum TransportMode {\n STDIO = 'stdio',\n HTTP = 'http',\n SSE = 'sse',\n}\n\n/**\n * Transport configuration options\n */\nexport interface TransportConfig {\n mode: TransportMode;\n port?: number;\n host?: string;\n}\n\n/**\n * Base interface for all transport handlers\n */\nexport interface TransportHandler {\n start(): Promise<void>;\n stop(): Promise<void>;\n}\n\n/**\n * HTTP transport specific types\n */\nexport interface HttpTransportHandler extends TransportHandler {\n getPort(): number;\n getHost(): string;\n}\n","/**\n * MCP Serve Command\n *\n * DESIGN PATTERNS:\n * - Command pattern with Commander for CLI argument parsing\n * - Transport abstraction pattern for flexible deployment (stdio, HTTP, SSE)\n * - Factory pattern for creating transport handlers\n * - Graceful shutdown pattern with signal handling\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Implement proper error handling with try-catch blocks\n * - Handle process signals for graceful shutdown\n * - Provide clear CLI options and help messages\n *\n * AVOID:\n * - Hardcoded configuration values (use CLI options or environment variables)\n * - Missing error handling for transport startup\n * - Not cleaning up resources on shutdown\n */\n\nimport { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport { DEFAULT_PORT_RANGE, PortRegistryService } from '@agimon-ai/foundation-port-registry';\nimport { type ProcessLease, createProcessLease } from '@agimon-ai/foundation-process-registry';\nimport { Command } from 'commander';\nimport { createServer } from '../server';\nimport { HttpTransportHandler } from '../transports/http';\nimport { SseTransportHandler } from '../transports/sse';\nimport { StdioTransportHandler } from '../transports/stdio';\nimport { type TransportConfig, TransportMode } from '../types';\n\ninterface LifecycleHandler {\n start(): Promise<void>;\n stop(): Promise<void>;\n}\n\ninterface PortLease {\n port: number;\n release(): Promise<void>;\n}\n\nconst WORKSPACE_MARKERS = ['pnpm-workspace.yaml', 'nx.json', '.git'];\nconst DEFAULT_SERVICE_TYPE = 'tool' as const;\nconst DEFAULT_ENVIRONMENT = process.env.NODE_ENV || 'development';\nconst DEFAULT_HOST = process.env.MCP_HOST || 'localhost';\n\nfunction resolveWorkspaceRoot(startPath = process.cwd()): string {\n let current = path.resolve(startPath);\n\n while (true) {\n for (const marker of WORKSPACE_MARKERS) {\n if (existsSync(path.join(current, marker))) {\n return current;\n }\n }\n\n const parent = path.dirname(current);\n if (parent === current) {\n return process.cwd();\n }\n\n current = parent;\n }\n}\n\nfunction createPortRegistryService(): PortRegistryService {\n return new PortRegistryService(process.env.PORT_REGISTRY_PATH);\n}\n\nfunction parseRequestedPort(value: unknown): number | undefined {\n if (typeof value === 'number') {\n return Number.isInteger(value) && value > 0 && value <= 65535 ? value : undefined;\n }\n\n if (typeof value === 'string' && value.trim().length > 0) {\n const parsed = Number.parseInt(value, 10);\n return Number.isInteger(parsed) && parsed > 0 && parsed <= 65535 ? parsed : undefined;\n }\n\n return undefined;\n}\n\nasync function reservePortLease(\n repositoryPath: string,\n serviceName: string,\n transport: TransportMode.HTTP | TransportMode.SSE,\n requestedPort?: number,\n): Promise<PortLease> {\n const portRegistry = createPortRegistryService();\n const preferredPort = requestedPort ?? DEFAULT_PORT_RANGE.min;\n const portRange = requestedPort\n ? { min: requestedPort, max: Math.max(DEFAULT_PORT_RANGE.max, requestedPort) }\n : DEFAULT_PORT_RANGE;\n const response = await portRegistry.reservePort({\n repositoryPath,\n serviceName,\n serviceType: DEFAULT_SERVICE_TYPE,\n environment: DEFAULT_ENVIRONMENT,\n preferredPort,\n portRange,\n pid: process.pid,\n host: DEFAULT_HOST,\n force: true,\n metadata: { transport },\n });\n\n if (!response.success || !response.record) {\n throw new Error(response.error || `Failed to reserve port for ${serviceName}`);\n }\n\n let released = false;\n return {\n port: response.record.port,\n release: async () => {\n if (released) {\n return;\n }\n released = true;\n\n const releaseResponse = await portRegistry.releasePort({\n repositoryPath,\n serviceName,\n serviceType: DEFAULT_SERVICE_TYPE,\n environment: DEFAULT_ENVIRONMENT,\n pid: process.pid,\n });\n\n if (!releaseResponse.success && !releaseResponse.error?.includes('No matching registry entry')) {\n throw new Error(releaseResponse.error || `Failed to release port for ${serviceName}`);\n }\n },\n };\n}\n\nasync function cleanupLeases(leases: Array<PortLease | ProcessLease | undefined>): Promise<void> {\n for (const lease of leases) {\n if (!lease) {\n continue;\n }\n await lease.release();\n }\n}\n\n/**\n * Start MCP server with given transport handler\n */\nasync function startServer(handler: LifecycleHandler, onShutdown?: () => Promise<void>) {\n await handler.start();\n\n // Handle graceful shutdown\n const shutdown = async (signal: string) => {\n process.stderr.write(`\\nReceived ${signal}, shutting down gracefully...\\n`);\n let shutdownError: unknown;\n\n try {\n await handler.stop();\n } catch (error) {\n shutdownError = error;\n }\n\n try {\n await onShutdown?.();\n } catch (error) {\n shutdownError = shutdownError ?? error;\n }\n\n if (shutdownError) {\n process.stderr.write(\n `Error during shutdown: ${shutdownError instanceof Error ? shutdownError.message : String(shutdownError)}\\n`,\n );\n process.exit(1);\n }\n\n process.exit(0);\n };\n\n process.on('SIGINT', () => shutdown('SIGINT'));\n process.on('SIGTERM', () => shutdown('SIGTERM'));\n}\n\n/**\n * MCP Serve command\n */\nexport const mcpServeCommand = new Command('mcp-serve')\n .description('Start MCP server with specified transport')\n .option('-t, --type <type>', 'Transport type: stdio, http, or sse', 'stdio')\n .option('-p, --port <port>', 'Port to listen on (http/sse only)', (val) => Number.parseInt(val, 10))\n .option('--host <host>', 'Host to bind to (http/sse only)', 'localhost')\n .action(async (options) => {\n let portLease: PortLease | undefined;\n let processLease: ProcessLease | undefined;\n try {\n const transportType = options.type.toLowerCase();\n const repositoryPath = resolveWorkspaceRoot(process.cwd());\n\n if (transportType === 'stdio') {\n const server = createServer();\n const handler = new StdioTransportHandler(server);\n processLease = await createProcessLease({\n repositoryPath,\n serviceName: 'imagine-mcp-stdio',\n pid: process.pid,\n metadata: { transport: 'stdio' },\n });\n await startServer(handler, async () => {\n await processLease?.release();\n });\n } else if (transportType === 'http') {\n const requestedPort = parseRequestedPort(options.port) ?? parseRequestedPort(process.env.MCP_PORT);\n portLease = await reservePortLease(repositoryPath, 'imagine-mcp-http', TransportMode.HTTP, requestedPort);\n processLease = await createProcessLease({\n repositoryPath,\n serviceName: 'imagine-mcp-http',\n pid: process.pid,\n metadata: { transport: 'http' },\n port: portLease.port,\n host: options.host || DEFAULT_HOST,\n command: process.argv[1],\n args: process.argv.slice(2),\n });\n // For HTTP, pass a factory function to create new server instances per session\n const config: TransportConfig = {\n mode: TransportMode.HTTP,\n port: portLease.port,\n host: options.host || DEFAULT_HOST,\n };\n const handler = new HttpTransportHandler(() => createServer(), config);\n await startServer(handler, async () => {\n await cleanupLeases([processLease, portLease]);\n });\n } else if (transportType === 'sse') {\n const requestedPort = parseRequestedPort(options.port) ?? parseRequestedPort(process.env.MCP_PORT);\n portLease = await reservePortLease(repositoryPath, 'imagine-mcp-sse', TransportMode.SSE, requestedPort);\n processLease = await createProcessLease({\n repositoryPath,\n serviceName: 'imagine-mcp-sse',\n pid: process.pid,\n metadata: { transport: 'sse' },\n port: portLease.port,\n host: options.host || DEFAULT_HOST,\n command: process.argv[1],\n args: process.argv.slice(2),\n });\n // For SSE, pass a factory function to create new server instances per connection\n const config: TransportConfig = {\n mode: TransportMode.SSE,\n port: portLease.port,\n host: options.host || DEFAULT_HOST,\n };\n const handler = new SseTransportHandler(() => createServer(), config);\n await startServer(handler, async () => {\n await cleanupLeases([processLease, portLease]);\n });\n } else {\n process.stderr.write(`Unknown transport type: ${transportType}. Use: stdio, http, or sse\\n`);\n process.exit(1);\n }\n } catch (error) {\n await cleanupLeases([processLease, portLease]).catch(() => undefined);\n process.stderr.write(`Failed to start MCP server: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(1);\n }\n });\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agimon-ai/imagine-mcp",
3
3
  "description": "MCP server for AI image generation and manipulation",
4
- "version": "0.5.1",
4
+ "version": "0.5.2",
5
5
  "license": "AGPL-3.0",
6
6
  "keywords": [
7
7
  "mcp",
@@ -30,8 +30,8 @@
30
30
  "sharp": "0.34.1",
31
31
  "unsplash-js": "^7.0.20",
32
32
  "zod": "4.3.6",
33
- "@agimon-ai/foundation-port-registry": "0.5.1",
34
- "@agimon-ai/foundation-process-registry": "0.5.1"
33
+ "@agimon-ai/foundation-port-registry": "0.5.2",
34
+ "@agimon-ai/foundation-process-registry": "0.5.2"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/express": "^5.0.0",
@@ -1,2 +0,0 @@
1
- const e=require(`./stdio-D8OPo478.cjs`);let t=require(`commander`),n=require(`node:fs`),r=require(`node:path`);r=e.s(r);let i=require(`@agimon-ai/foundation-process-registry`),a=require(`@agimon-ai/foundation-port-registry`),o=function(e){return e.STDIO=`stdio`,e.HTTP=`http`,e.SSE=`sse`,e}({});const s=[`pnpm-workspace.yaml`,`nx.json`,`.git`],c=`tool`,l=process.env.NODE_ENV||`development`,u=process.env.MCP_HOST||`localhost`;function d(e=process.cwd()){let t=r.default.resolve(e);for(;;){for(let e of s)if((0,n.existsSync)(r.default.join(t,e)))return t;let e=r.default.dirname(t);if(e===t)return process.cwd();t=e}}function f(){return new a.PortRegistryService(process.env.PORT_REGISTRY_PATH)}function p(e){if(typeof e==`number`)return Number.isInteger(e)&&e>0&&e<=65535?e:void 0;if(typeof e==`string`&&e.trim().length>0){let t=Number.parseInt(e,10);return Number.isInteger(t)&&t>0&&t<=65535?t:void 0}}async function m(e,t,n,r){let i=f(),o=r??a.DEFAULT_PORT_RANGE.min,s=r?{min:r,max:r}:a.DEFAULT_PORT_RANGE,d=await i.reservePort({repositoryPath:e,serviceName:t,serviceType:c,environment:l,preferredPort:o,portRange:s,pid:process.pid,host:u,force:!0,metadata:{transport:n}});if(!d.success||!d.record)throw Error(d.error||`Failed to reserve port for ${t}`);let p=!1;return{port:d.record.port,release:async()=>{if(p)return;p=!0;let n=await i.releasePort({repositoryPath:e,serviceName:t,serviceType:c,environment:l,pid:process.pid});if(!n.success&&!n.error?.includes(`No matching registry entry`))throw Error(n.error||`Failed to release port for ${t}`)}}}async function h(e){for(let t of e)t&&await t.release()}async function g(e,t){await e.start();let n=async n=>{process.stderr.write(`\nReceived ${n}, shutting down gracefully...\n`);let r;try{await e.stop()}catch(e){r=e}try{await t?.()}catch(e){r??=e}r&&(process.stderr.write(`Error during shutdown: ${r instanceof Error?r.message:String(r)}\n`),process.exit(1)),process.exit(0)};process.on(`SIGINT`,()=>n(`SIGINT`)),process.on(`SIGTERM`,()=>n(`SIGTERM`))}const _=new t.Command(`mcp-serve`).description(`Start MCP server with specified transport`).option(`-t, --type <type>`,`Transport type: stdio, http, or sse`,`stdio`).option(`-p, --port <port>`,`Port to listen on (http/sse only)`,e=>parseInt(e,10)).option(`--host <host>`,`Host to bind to (http/sse only)`,`localhost`).action(async t=>{let n,r;try{let a=t.type.toLowerCase(),s=d(process.cwd());if(a===`stdio`){let t=new e.t(e.i());r=await(0,i.createProcessLease)({repositoryPath:s,serviceName:`imagine-mcp-stdio`,pid:process.pid,metadata:{transport:`stdio`}}),await g(t,async()=>{await r?.release()})}else if(a===`http`){let a=p(t.port)??p(process.env.MCP_PORT);n=await m(s,`imagine-mcp-http`,o.HTTP,a),r=await(0,i.createProcessLease)({repositoryPath:s,serviceName:`imagine-mcp-http`,pid:process.pid,metadata:{transport:`http`},port:n.port,host:t.host||u,command:process.argv[1],args:process.argv.slice(2)}),await g(new e.r(()=>e.i(),{mode:o.HTTP,port:n.port,host:t.host||u}),async()=>{await h([r,n])})}else if(a===`sse`){let a=p(t.port)??p(process.env.MCP_PORT);n=await m(s,`imagine-mcp-sse`,o.SSE,a),r=await(0,i.createProcessLease)({repositoryPath:s,serviceName:`imagine-mcp-sse`,pid:process.pid,metadata:{transport:`sse`},port:n.port,host:t.host||u,command:process.argv[1],args:process.argv.slice(2)}),await g(new e.n(()=>e.i(),{mode:o.SSE,port:n.port,host:t.host||u}),async()=>{await h([r,n])})}else process.stderr.write(`Unknown transport type: ${a}. Use: stdio, http, or sse\n`),process.exit(1)}catch(e){await h([r,n]).catch(()=>void 0),process.stderr.write(`Failed to start MCP server: ${e instanceof Error?e.message:String(e)}\n`),process.exit(1)}});exports.mcpServeCommand=_;
2
- //# sourceMappingURL=commands-D59ANVbf.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"commands-D59ANVbf.cjs","names":["path","PortRegistryService","DEFAULT_PORT_RANGE","shutdownError: unknown","Command","portLease: PortLease | undefined","processLease: ProcessLease | undefined","StdioTransportHandler","createServer","HttpTransportHandler","SseTransportHandler"],"sources":["../src/types/index.ts","../src/commands/mcp-serve.ts"],"sourcesContent":["/**\n * Shared TypeScript Types\n *\n * DESIGN PATTERNS:\n * - Type-first development\n * - Interface segregation\n *\n * CODING STANDARDS:\n * - Export all shared types from this file\n * - Use descriptive names for types and interfaces\n */\n\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\n\n/**\n * Tool definition for MCP\n */\nexport interface ToolDefinition {\n name: string;\n description: string;\n inputSchema: {\n type: string;\n properties: Record<string, unknown>;\n required?: string[];\n additionalProperties?: boolean;\n };\n}\n\n/**\n * Base tool interface following MCP SDK patterns\n */\nexport interface Tool<TInput = unknown> {\n getDefinition(): ToolDefinition;\n execute(input: TInput): Promise<CallToolResult>;\n}\n\n/**\n * Transport mode types\n */\nexport enum TransportMode {\n STDIO = 'stdio',\n HTTP = 'http',\n SSE = 'sse',\n}\n\n/**\n * Transport configuration options\n */\nexport interface TransportConfig {\n mode: TransportMode;\n port?: number;\n host?: string;\n}\n\n/**\n * Base interface for all transport handlers\n */\nexport interface TransportHandler {\n start(): Promise<void>;\n stop(): Promise<void>;\n}\n\n/**\n * HTTP transport specific types\n */\nexport interface HttpTransportHandler extends TransportHandler {\n getPort(): number;\n getHost(): string;\n}\n","/**\n * MCP Serve Command\n *\n * DESIGN PATTERNS:\n * - Command pattern with Commander for CLI argument parsing\n * - Transport abstraction pattern for flexible deployment (stdio, HTTP, SSE)\n * - Factory pattern for creating transport handlers\n * - Graceful shutdown pattern with signal handling\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Implement proper error handling with try-catch blocks\n * - Handle process signals for graceful shutdown\n * - Provide clear CLI options and help messages\n *\n * AVOID:\n * - Hardcoded configuration values (use CLI options or environment variables)\n * - Missing error handling for transport startup\n * - Not cleaning up resources on shutdown\n */\n\nimport { Command } from 'commander';\nimport { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport { createProcessLease, type ProcessLease } from '@agimon-ai/foundation-process-registry';\nimport { DEFAULT_PORT_RANGE, PortRegistryService } from '@agimon-ai/foundation-port-registry';\nimport { createServer } from '../server';\nimport { HttpTransportHandler } from '../transports/http';\nimport { SseTransportHandler } from '../transports/sse';\nimport { StdioTransportHandler } from '../transports/stdio';\nimport { type TransportConfig, TransportMode } from '../types';\n\ninterface LifecycleHandler {\n start(): Promise<void>;\n stop(): Promise<void>;\n}\n\ninterface PortLease {\n port: number;\n release(): Promise<void>;\n}\n\nconst WORKSPACE_MARKERS = ['pnpm-workspace.yaml', 'nx.json', '.git'];\nconst DEFAULT_SERVICE_TYPE = 'tool' as const;\nconst DEFAULT_ENVIRONMENT = process.env.NODE_ENV || 'development';\nconst DEFAULT_HOST = process.env.MCP_HOST || 'localhost';\n\nfunction resolveWorkspaceRoot(startPath = process.cwd()): string {\n let current = path.resolve(startPath);\n\n while (true) {\n for (const marker of WORKSPACE_MARKERS) {\n if (existsSync(path.join(current, marker))) {\n return current;\n }\n }\n\n const parent = path.dirname(current);\n if (parent === current) {\n return process.cwd();\n }\n\n current = parent;\n }\n}\n\nfunction createPortRegistryService(): PortRegistryService {\n return new PortRegistryService(process.env.PORT_REGISTRY_PATH);\n}\n\nfunction parseRequestedPort(value: unknown): number | undefined {\n if (typeof value === 'number') {\n return Number.isInteger(value) && value > 0 && value <= 65535 ? value : undefined;\n }\n\n if (typeof value === 'string' && value.trim().length > 0) {\n const parsed = Number.parseInt(value, 10);\n return Number.isInteger(parsed) && parsed > 0 && parsed <= 65535 ? parsed : undefined;\n }\n\n return undefined;\n}\n\nasync function reservePortLease(\n repositoryPath: string,\n serviceName: string,\n transport: TransportMode.HTTP | TransportMode.SSE,\n requestedPort?: number,\n): Promise<PortLease> {\n const portRegistry = createPortRegistryService();\n const preferredPort = requestedPort ?? DEFAULT_PORT_RANGE.min;\n const portRange = requestedPort ? { min: requestedPort, max: requestedPort } : DEFAULT_PORT_RANGE;\n const response = await portRegistry.reservePort({\n repositoryPath,\n serviceName,\n serviceType: DEFAULT_SERVICE_TYPE,\n environment: DEFAULT_ENVIRONMENT,\n preferredPort,\n portRange,\n pid: process.pid,\n host: DEFAULT_HOST,\n force: true,\n metadata: { transport },\n });\n\n if (!response.success || !response.record) {\n throw new Error(response.error || `Failed to reserve port for ${serviceName}`);\n }\n\n let released = false;\n return {\n port: response.record.port,\n release: async () => {\n if (released) {\n return;\n }\n released = true;\n\n const releaseResponse = await portRegistry.releasePort({\n repositoryPath,\n serviceName,\n serviceType: DEFAULT_SERVICE_TYPE,\n environment: DEFAULT_ENVIRONMENT,\n pid: process.pid,\n });\n\n if (!releaseResponse.success && !releaseResponse.error?.includes('No matching registry entry')) {\n throw new Error(releaseResponse.error || `Failed to release port for ${serviceName}`);\n }\n },\n };\n}\n\nasync function cleanupLeases(leases: Array<PortLease | ProcessLease | undefined>): Promise<void> {\n for (const lease of leases) {\n if (!lease) {\n continue;\n }\n await lease.release();\n }\n}\n\n/**\n * Start MCP server with given transport handler\n */\nasync function startServer(handler: LifecycleHandler, onShutdown?: () => Promise<void>) {\n await handler.start();\n\n // Handle graceful shutdown\n const shutdown = async (signal: string) => {\n process.stderr.write(`\\nReceived ${signal}, shutting down gracefully...\\n`);\n let shutdownError: unknown;\n\n try {\n await handler.stop();\n } catch (error) {\n shutdownError = error;\n }\n\n try {\n await onShutdown?.();\n } catch (error) {\n shutdownError = shutdownError ?? error;\n }\n\n if (shutdownError) {\n process.stderr.write(\n `Error during shutdown: ${shutdownError instanceof Error ? shutdownError.message : String(shutdownError)}\\n`,\n );\n process.exit(1);\n }\n\n process.exit(0);\n };\n\n process.on('SIGINT', () => shutdown('SIGINT'));\n process.on('SIGTERM', () => shutdown('SIGTERM'));\n}\n\n/**\n * MCP Serve command\n */\nexport const mcpServeCommand = new Command('mcp-serve')\n .description('Start MCP server with specified transport')\n .option('-t, --type <type>', 'Transport type: stdio, http, or sse', 'stdio')\n .option('-p, --port <port>', 'Port to listen on (http/sse only)', (val) => parseInt(val, 10))\n .option('--host <host>', 'Host to bind to (http/sse only)', 'localhost')\n .action(async (options) => {\n let portLease: PortLease | undefined;\n let processLease: ProcessLease | undefined;\n try {\n const transportType = options.type.toLowerCase();\n const repositoryPath = resolveWorkspaceRoot(process.cwd());\n\n if (transportType === 'stdio') {\n const server = createServer();\n const handler = new StdioTransportHandler(server);\n processLease = await createProcessLease({\n repositoryPath,\n serviceName: 'imagine-mcp-stdio',\n pid: process.pid,\n metadata: { transport: 'stdio' },\n });\n await startServer(handler, async () => {\n await processLease?.release();\n });\n } else if (transportType === 'http') {\n const requestedPort = parseRequestedPort(options.port) ?? parseRequestedPort(process.env.MCP_PORT);\n portLease = await reservePortLease(repositoryPath, 'imagine-mcp-http', TransportMode.HTTP, requestedPort);\n processLease = await createProcessLease({\n repositoryPath,\n serviceName: 'imagine-mcp-http',\n pid: process.pid,\n metadata: { transport: 'http' },\n port: portLease.port,\n host: options.host || DEFAULT_HOST,\n command: process.argv[1],\n args: process.argv.slice(2),\n });\n // For HTTP, pass a factory function to create new server instances per session\n const config: TransportConfig = {\n mode: TransportMode.HTTP,\n port: portLease.port,\n host: options.host || DEFAULT_HOST,\n };\n const handler = new HttpTransportHandler(() => createServer(), config);\n await startServer(handler, async () => {\n await cleanupLeases([processLease, portLease]);\n });\n } else if (transportType === 'sse') {\n const requestedPort = parseRequestedPort(options.port) ?? parseRequestedPort(process.env.MCP_PORT);\n portLease = await reservePortLease(repositoryPath, 'imagine-mcp-sse', TransportMode.SSE, requestedPort);\n processLease = await createProcessLease({\n repositoryPath,\n serviceName: 'imagine-mcp-sse',\n pid: process.pid,\n metadata: { transport: 'sse' },\n port: portLease.port,\n host: options.host || DEFAULT_HOST,\n command: process.argv[1],\n args: process.argv.slice(2),\n });\n // For SSE, pass a factory function to create new server instances per connection\n const config: TransportConfig = {\n mode: TransportMode.SSE,\n port: portLease.port,\n host: options.host || DEFAULT_HOST,\n };\n const handler = new SseTransportHandler(() => createServer(), config);\n await startServer(handler, async () => {\n await cleanupLeases([processLease, portLease]);\n });\n } else {\n process.stderr.write(`Unknown transport type: ${transportType}. Use: stdio, http, or sse\\n`);\n process.exit(1);\n }\n } catch (error) {\n await cleanupLeases([processLease, portLease]).catch(() => undefined);\n process.stderr.write(`Failed to start MCP server: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(1);\n }\n });\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- import{i as e,n as t,r as n,t as r}from"./stdio-DYg9ZLDI.mjs";import{Command as i}from"commander";import{existsSync as a}from"node:fs";import o from"node:path";import{createProcessLease as s}from"@agimon-ai/foundation-process-registry";import{DEFAULT_PORT_RANGE as c,PortRegistryService as l}from"@agimon-ai/foundation-port-registry";let u=function(e){return e.STDIO=`stdio`,e.HTTP=`http`,e.SSE=`sse`,e}({});const d=[`pnpm-workspace.yaml`,`nx.json`,`.git`],f=`tool`,p=process.env.NODE_ENV||`development`,m=process.env.MCP_HOST||`localhost`;function h(e=process.cwd()){let t=o.resolve(e);for(;;){for(let e of d)if(a(o.join(t,e)))return t;let e=o.dirname(t);if(e===t)return process.cwd();t=e}}function g(){return new l(process.env.PORT_REGISTRY_PATH)}function _(e){if(typeof e==`number`)return Number.isInteger(e)&&e>0&&e<=65535?e:void 0;if(typeof e==`string`&&e.trim().length>0){let t=Number.parseInt(e,10);return Number.isInteger(t)&&t>0&&t<=65535?t:void 0}}async function v(e,t,n,r){let i=g(),a=r??c.min,o=r?{min:r,max:r}:c,s=await i.reservePort({repositoryPath:e,serviceName:t,serviceType:f,environment:p,preferredPort:a,portRange:o,pid:process.pid,host:m,force:!0,metadata:{transport:n}});if(!s.success||!s.record)throw Error(s.error||`Failed to reserve port for ${t}`);let l=!1;return{port:s.record.port,release:async()=>{if(l)return;l=!0;let n=await i.releasePort({repositoryPath:e,serviceName:t,serviceType:f,environment:p,pid:process.pid});if(!n.success&&!n.error?.includes(`No matching registry entry`))throw Error(n.error||`Failed to release port for ${t}`)}}}async function y(e){for(let t of e)t&&await t.release()}async function b(e,t){await e.start();let n=async n=>{process.stderr.write(`\nReceived ${n}, shutting down gracefully...\n`);let r;try{await e.stop()}catch(e){r=e}try{await t?.()}catch(e){r??=e}r&&(process.stderr.write(`Error during shutdown: ${r instanceof Error?r.message:String(r)}\n`),process.exit(1)),process.exit(0)};process.on(`SIGINT`,()=>n(`SIGINT`)),process.on(`SIGTERM`,()=>n(`SIGTERM`))}const x=new i(`mcp-serve`).description(`Start MCP server with specified transport`).option(`-t, --type <type>`,`Transport type: stdio, http, or sse`,`stdio`).option(`-p, --port <port>`,`Port to listen on (http/sse only)`,e=>parseInt(e,10)).option(`--host <host>`,`Host to bind to (http/sse only)`,`localhost`).action(async i=>{let a,o;try{let c=i.type.toLowerCase(),l=h(process.cwd());if(c===`stdio`){let t=new r(e());o=await s({repositoryPath:l,serviceName:`imagine-mcp-stdio`,pid:process.pid,metadata:{transport:`stdio`}}),await b(t,async()=>{await o?.release()})}else if(c===`http`){let t=_(i.port)??_(process.env.MCP_PORT);a=await v(l,`imagine-mcp-http`,u.HTTP,t),o=await s({repositoryPath:l,serviceName:`imagine-mcp-http`,pid:process.pid,metadata:{transport:`http`},port:a.port,host:i.host||m,command:process.argv[1],args:process.argv.slice(2)}),await b(new n(()=>e(),{mode:u.HTTP,port:a.port,host:i.host||m}),async()=>{await y([o,a])})}else if(c===`sse`){let n=_(i.port)??_(process.env.MCP_PORT);a=await v(l,`imagine-mcp-sse`,u.SSE,n),o=await s({repositoryPath:l,serviceName:`imagine-mcp-sse`,pid:process.pid,metadata:{transport:`sse`},port:a.port,host:i.host||m,command:process.argv[1],args:process.argv.slice(2)}),await b(new t(()=>e(),{mode:u.SSE,port:a.port,host:i.host||m}),async()=>{await y([o,a])})}else process.stderr.write(`Unknown transport type: ${c}. Use: stdio, http, or sse\n`),process.exit(1)}catch(e){await y([o,a]).catch(()=>void 0),process.stderr.write(`Failed to start MCP server: ${e instanceof Error?e.message:String(e)}\n`),process.exit(1)}});export{x as mcpServeCommand};
2
- //# sourceMappingURL=commands-Dw2IITFV.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"commands-Dw2IITFV.mjs","names":["shutdownError: unknown","portLease: PortLease | undefined","processLease: ProcessLease | undefined"],"sources":["../src/types/index.ts","../src/commands/mcp-serve.ts"],"sourcesContent":["/**\n * Shared TypeScript Types\n *\n * DESIGN PATTERNS:\n * - Type-first development\n * - Interface segregation\n *\n * CODING STANDARDS:\n * - Export all shared types from this file\n * - Use descriptive names for types and interfaces\n */\n\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\n\n/**\n * Tool definition for MCP\n */\nexport interface ToolDefinition {\n name: string;\n description: string;\n inputSchema: {\n type: string;\n properties: Record<string, unknown>;\n required?: string[];\n additionalProperties?: boolean;\n };\n}\n\n/**\n * Base tool interface following MCP SDK patterns\n */\nexport interface Tool<TInput = unknown> {\n getDefinition(): ToolDefinition;\n execute(input: TInput): Promise<CallToolResult>;\n}\n\n/**\n * Transport mode types\n */\nexport enum TransportMode {\n STDIO = 'stdio',\n HTTP = 'http',\n SSE = 'sse',\n}\n\n/**\n * Transport configuration options\n */\nexport interface TransportConfig {\n mode: TransportMode;\n port?: number;\n host?: string;\n}\n\n/**\n * Base interface for all transport handlers\n */\nexport interface TransportHandler {\n start(): Promise<void>;\n stop(): Promise<void>;\n}\n\n/**\n * HTTP transport specific types\n */\nexport interface HttpTransportHandler extends TransportHandler {\n getPort(): number;\n getHost(): string;\n}\n","/**\n * MCP Serve Command\n *\n * DESIGN PATTERNS:\n * - Command pattern with Commander for CLI argument parsing\n * - Transport abstraction pattern for flexible deployment (stdio, HTTP, SSE)\n * - Factory pattern for creating transport handlers\n * - Graceful shutdown pattern with signal handling\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Implement proper error handling with try-catch blocks\n * - Handle process signals for graceful shutdown\n * - Provide clear CLI options and help messages\n *\n * AVOID:\n * - Hardcoded configuration values (use CLI options or environment variables)\n * - Missing error handling for transport startup\n * - Not cleaning up resources on shutdown\n */\n\nimport { Command } from 'commander';\nimport { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport { createProcessLease, type ProcessLease } from '@agimon-ai/foundation-process-registry';\nimport { DEFAULT_PORT_RANGE, PortRegistryService } from '@agimon-ai/foundation-port-registry';\nimport { createServer } from '../server';\nimport { HttpTransportHandler } from '../transports/http';\nimport { SseTransportHandler } from '../transports/sse';\nimport { StdioTransportHandler } from '../transports/stdio';\nimport { type TransportConfig, TransportMode } from '../types';\n\ninterface LifecycleHandler {\n start(): Promise<void>;\n stop(): Promise<void>;\n}\n\ninterface PortLease {\n port: number;\n release(): Promise<void>;\n}\n\nconst WORKSPACE_MARKERS = ['pnpm-workspace.yaml', 'nx.json', '.git'];\nconst DEFAULT_SERVICE_TYPE = 'tool' as const;\nconst DEFAULT_ENVIRONMENT = process.env.NODE_ENV || 'development';\nconst DEFAULT_HOST = process.env.MCP_HOST || 'localhost';\n\nfunction resolveWorkspaceRoot(startPath = process.cwd()): string {\n let current = path.resolve(startPath);\n\n while (true) {\n for (const marker of WORKSPACE_MARKERS) {\n if (existsSync(path.join(current, marker))) {\n return current;\n }\n }\n\n const parent = path.dirname(current);\n if (parent === current) {\n return process.cwd();\n }\n\n current = parent;\n }\n}\n\nfunction createPortRegistryService(): PortRegistryService {\n return new PortRegistryService(process.env.PORT_REGISTRY_PATH);\n}\n\nfunction parseRequestedPort(value: unknown): number | undefined {\n if (typeof value === 'number') {\n return Number.isInteger(value) && value > 0 && value <= 65535 ? value : undefined;\n }\n\n if (typeof value === 'string' && value.trim().length > 0) {\n const parsed = Number.parseInt(value, 10);\n return Number.isInteger(parsed) && parsed > 0 && parsed <= 65535 ? parsed : undefined;\n }\n\n return undefined;\n}\n\nasync function reservePortLease(\n repositoryPath: string,\n serviceName: string,\n transport: TransportMode.HTTP | TransportMode.SSE,\n requestedPort?: number,\n): Promise<PortLease> {\n const portRegistry = createPortRegistryService();\n const preferredPort = requestedPort ?? DEFAULT_PORT_RANGE.min;\n const portRange = requestedPort ? { min: requestedPort, max: requestedPort } : DEFAULT_PORT_RANGE;\n const response = await portRegistry.reservePort({\n repositoryPath,\n serviceName,\n serviceType: DEFAULT_SERVICE_TYPE,\n environment: DEFAULT_ENVIRONMENT,\n preferredPort,\n portRange,\n pid: process.pid,\n host: DEFAULT_HOST,\n force: true,\n metadata: { transport },\n });\n\n if (!response.success || !response.record) {\n throw new Error(response.error || `Failed to reserve port for ${serviceName}`);\n }\n\n let released = false;\n return {\n port: response.record.port,\n release: async () => {\n if (released) {\n return;\n }\n released = true;\n\n const releaseResponse = await portRegistry.releasePort({\n repositoryPath,\n serviceName,\n serviceType: DEFAULT_SERVICE_TYPE,\n environment: DEFAULT_ENVIRONMENT,\n pid: process.pid,\n });\n\n if (!releaseResponse.success && !releaseResponse.error?.includes('No matching registry entry')) {\n throw new Error(releaseResponse.error || `Failed to release port for ${serviceName}`);\n }\n },\n };\n}\n\nasync function cleanupLeases(leases: Array<PortLease | ProcessLease | undefined>): Promise<void> {\n for (const lease of leases) {\n if (!lease) {\n continue;\n }\n await lease.release();\n }\n}\n\n/**\n * Start MCP server with given transport handler\n */\nasync function startServer(handler: LifecycleHandler, onShutdown?: () => Promise<void>) {\n await handler.start();\n\n // Handle graceful shutdown\n const shutdown = async (signal: string) => {\n process.stderr.write(`\\nReceived ${signal}, shutting down gracefully...\\n`);\n let shutdownError: unknown;\n\n try {\n await handler.stop();\n } catch (error) {\n shutdownError = error;\n }\n\n try {\n await onShutdown?.();\n } catch (error) {\n shutdownError = shutdownError ?? error;\n }\n\n if (shutdownError) {\n process.stderr.write(\n `Error during shutdown: ${shutdownError instanceof Error ? shutdownError.message : String(shutdownError)}\\n`,\n );\n process.exit(1);\n }\n\n process.exit(0);\n };\n\n process.on('SIGINT', () => shutdown('SIGINT'));\n process.on('SIGTERM', () => shutdown('SIGTERM'));\n}\n\n/**\n * MCP Serve command\n */\nexport const mcpServeCommand = new Command('mcp-serve')\n .description('Start MCP server with specified transport')\n .option('-t, --type <type>', 'Transport type: stdio, http, or sse', 'stdio')\n .option('-p, --port <port>', 'Port to listen on (http/sse only)', (val) => parseInt(val, 10))\n .option('--host <host>', 'Host to bind to (http/sse only)', 'localhost')\n .action(async (options) => {\n let portLease: PortLease | undefined;\n let processLease: ProcessLease | undefined;\n try {\n const transportType = options.type.toLowerCase();\n const repositoryPath = resolveWorkspaceRoot(process.cwd());\n\n if (transportType === 'stdio') {\n const server = createServer();\n const handler = new StdioTransportHandler(server);\n processLease = await createProcessLease({\n repositoryPath,\n serviceName: 'imagine-mcp-stdio',\n pid: process.pid,\n metadata: { transport: 'stdio' },\n });\n await startServer(handler, async () => {\n await processLease?.release();\n });\n } else if (transportType === 'http') {\n const requestedPort = parseRequestedPort(options.port) ?? parseRequestedPort(process.env.MCP_PORT);\n portLease = await reservePortLease(repositoryPath, 'imagine-mcp-http', TransportMode.HTTP, requestedPort);\n processLease = await createProcessLease({\n repositoryPath,\n serviceName: 'imagine-mcp-http',\n pid: process.pid,\n metadata: { transport: 'http' },\n port: portLease.port,\n host: options.host || DEFAULT_HOST,\n command: process.argv[1],\n args: process.argv.slice(2),\n });\n // For HTTP, pass a factory function to create new server instances per session\n const config: TransportConfig = {\n mode: TransportMode.HTTP,\n port: portLease.port,\n host: options.host || DEFAULT_HOST,\n };\n const handler = new HttpTransportHandler(() => createServer(), config);\n await startServer(handler, async () => {\n await cleanupLeases([processLease, portLease]);\n });\n } else if (transportType === 'sse') {\n const requestedPort = parseRequestedPort(options.port) ?? parseRequestedPort(process.env.MCP_PORT);\n portLease = await reservePortLease(repositoryPath, 'imagine-mcp-sse', TransportMode.SSE, requestedPort);\n processLease = await createProcessLease({\n repositoryPath,\n serviceName: 'imagine-mcp-sse',\n pid: process.pid,\n metadata: { transport: 'sse' },\n port: portLease.port,\n host: options.host || DEFAULT_HOST,\n command: process.argv[1],\n args: process.argv.slice(2),\n });\n // For SSE, pass a factory function to create new server instances per connection\n const config: TransportConfig = {\n mode: TransportMode.SSE,\n port: portLease.port,\n host: options.host || DEFAULT_HOST,\n };\n const handler = new SseTransportHandler(() => createServer(), config);\n await startServer(handler, async () => {\n await cleanupLeases([processLease, portLease]);\n });\n } else {\n process.stderr.write(`Unknown transport type: ${transportType}. Use: stdio, http, or sse\\n`);\n process.exit(1);\n }\n } catch (error) {\n await cleanupLeases([processLease, portLease]).catch(() => undefined);\n process.stderr.write(`Failed to start MCP server: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exit(1);\n }\n });\n"],"mappings":"8UAuCA,IAAY,EAAA,SAAA,EAAL,OACL,GAAA,MAAA,QACA,EAAA,KAAA,OACA,EAAA,IAAA,aCAF,MAAM,EAAoB,CAAC,sBAAuB,UAAW,OAAO,CAC9D,EAAuB,OACvB,EAAsB,QAAQ,IAAI,UAAY,cAC9C,EAAe,QAAQ,IAAI,UAAY,YAE7C,SAAS,EAAqB,EAAY,QAAQ,KAAK,CAAU,CAC/D,IAAI,EAAU,EAAK,QAAQ,EAAU,CAErC,OAAa,CACX,IAAK,IAAM,KAAU,EACnB,GAAI,EAAW,EAAK,KAAK,EAAS,EAAO,CAAC,CACxC,OAAO,EAIX,IAAM,EAAS,EAAK,QAAQ,EAAQ,CACpC,GAAI,IAAW,EACb,OAAO,QAAQ,KAAK,CAGtB,EAAU,GAId,SAAS,GAAiD,CACxD,OAAO,IAAI,EAAoB,QAAQ,IAAI,mBAAmB,CAGhE,SAAS,EAAmB,EAAoC,CAC9D,GAAI,OAAO,GAAU,SACnB,OAAO,OAAO,UAAU,EAAM,EAAI,EAAQ,GAAK,GAAS,MAAQ,EAAQ,IAAA,GAG1E,GAAI,OAAO,GAAU,UAAY,EAAM,MAAM,CAAC,OAAS,EAAG,CACxD,IAAM,EAAS,OAAO,SAAS,EAAO,GAAG,CACzC,OAAO,OAAO,UAAU,EAAO,EAAI,EAAS,GAAK,GAAU,MAAQ,EAAS,IAAA,IAMhF,eAAe,EACb,EACA,EACA,EACA,EACoB,CACpB,IAAM,EAAe,GAA2B,CAC1C,EAAgB,GAAiB,EAAmB,IACpD,EAAY,EAAgB,CAAE,IAAK,EAAe,IAAK,EAAe,CAAG,EACzE,EAAW,MAAM,EAAa,YAAY,CAC9C,iBACA,cACA,YAAa,EACb,YAAa,EACb,gBACA,YACA,IAAK,QAAQ,IACb,KAAM,EACN,MAAO,GACP,SAAU,CAAE,YAAW,CACxB,CAAC,CAEF,GAAI,CAAC,EAAS,SAAW,CAAC,EAAS,OACjC,MAAU,MAAM,EAAS,OAAS,8BAA8B,IAAc,CAGhF,IAAI,EAAW,GACf,MAAO,CACL,KAAM,EAAS,OAAO,KACtB,QAAS,SAAY,CACnB,GAAI,EACF,OAEF,EAAW,GAEX,IAAM,EAAkB,MAAM,EAAa,YAAY,CACrD,iBACA,cACA,YAAa,EACb,YAAa,EACb,IAAK,QAAQ,IACd,CAAC,CAEF,GAAI,CAAC,EAAgB,SAAW,CAAC,EAAgB,OAAO,SAAS,6BAA6B,CAC5F,MAAU,MAAM,EAAgB,OAAS,8BAA8B,IAAc,EAG1F,CAGH,eAAe,EAAc,EAAoE,CAC/F,IAAK,IAAM,KAAS,EACb,GAGL,MAAM,EAAM,SAAS,CAOzB,eAAe,EAAY,EAA2B,EAAkC,CACtF,MAAM,EAAQ,OAAO,CAGrB,IAAM,EAAW,KAAO,IAAmB,CACzC,QAAQ,OAAO,MAAM,cAAc,EAAO,iCAAiC,CAC3E,IAAIA,EAEJ,GAAI,CACF,MAAM,EAAQ,MAAM,OACb,EAAO,CACd,EAAgB,EAGlB,GAAI,CACF,MAAM,KAAc,OACb,EAAO,CACd,IAAiC,EAG/B,IACF,QAAQ,OAAO,MACb,0BAA0B,aAAyB,MAAQ,EAAc,QAAU,OAAO,EAAc,CAAC,IAC1G,CACD,QAAQ,KAAK,EAAE,EAGjB,QAAQ,KAAK,EAAE,EAGjB,QAAQ,GAAG,aAAgB,EAAS,SAAS,CAAC,CAC9C,QAAQ,GAAG,cAAiB,EAAS,UAAU,CAAC,CAMlD,MAAa,EAAkB,IAAI,EAAQ,YAAY,CACpD,YAAY,4CAA4C,CACxD,OAAO,oBAAqB,sCAAuC,QAAQ,CAC3E,OAAO,oBAAqB,oCAAsC,GAAQ,SAAS,EAAK,GAAG,CAAC,CAC5F,OAAO,gBAAiB,kCAAmC,YAAY,CACvE,OAAO,KAAO,IAAY,CACzB,IAAIC,EACAC,EACJ,GAAI,CACF,IAAM,EAAgB,EAAQ,KAAK,aAAa,CAC1C,EAAiB,EAAqB,QAAQ,KAAK,CAAC,CAE1D,GAAI,IAAkB,QAAS,CAE7B,IAAM,EAAU,IAAI,EADL,GAAc,CACoB,CACjD,EAAe,MAAM,EAAmB,CACtC,iBACA,YAAa,oBACb,IAAK,QAAQ,IACb,SAAU,CAAE,UAAW,QAAS,CACjC,CAAC,CACF,MAAM,EAAY,EAAS,SAAY,CACrC,MAAM,GAAc,SAAS,EAC7B,SACO,IAAkB,OAAQ,CACnC,IAAM,EAAgB,EAAmB,EAAQ,KAAK,EAAI,EAAmB,QAAQ,IAAI,SAAS,CAClG,EAAY,MAAM,EAAiB,EAAgB,mBAAoB,EAAc,KAAM,EAAc,CACzG,EAAe,MAAM,EAAmB,CACtC,iBACA,YAAa,mBACb,IAAK,QAAQ,IACb,SAAU,CAAE,UAAW,OAAQ,CAC/B,KAAM,EAAU,KAChB,KAAM,EAAQ,MAAQ,EACtB,QAAS,QAAQ,KAAK,GACtB,KAAM,QAAQ,KAAK,MAAM,EAAE,CAC5B,CAAC,CAQF,MAAM,EADU,IAAI,MAA2B,GAAc,CAL7B,CAC9B,KAAM,EAAc,KACpB,KAAM,EAAU,KAChB,KAAM,EAAQ,MAAQ,EACvB,CACqE,CAC3C,SAAY,CACrC,MAAM,EAAc,CAAC,EAAc,EAAU,CAAC,EAC9C,SACO,IAAkB,MAAO,CAClC,IAAM,EAAgB,EAAmB,EAAQ,KAAK,EAAI,EAAmB,QAAQ,IAAI,SAAS,CAClG,EAAY,MAAM,EAAiB,EAAgB,kBAAmB,EAAc,IAAK,EAAc,CACvG,EAAe,MAAM,EAAmB,CACtC,iBACA,YAAa,kBACb,IAAK,QAAQ,IACb,SAAU,CAAE,UAAW,MAAO,CAC9B,KAAM,EAAU,KAChB,KAAM,EAAQ,MAAQ,EACtB,QAAS,QAAQ,KAAK,GACtB,KAAM,QAAQ,KAAK,MAAM,EAAE,CAC5B,CAAC,CAQF,MAAM,EADU,IAAI,MAA0B,GAAc,CAL5B,CAC9B,KAAM,EAAc,IACpB,KAAM,EAAU,KAChB,KAAM,EAAQ,MAAQ,EACvB,CACoE,CAC1C,SAAY,CACrC,MAAM,EAAc,CAAC,EAAc,EAAU,CAAC,EAC9C,MAEF,QAAQ,OAAO,MAAM,2BAA2B,EAAc,8BAA8B,CAC5F,QAAQ,KAAK,EAAE,OAEV,EAAO,CACd,MAAM,EAAc,CAAC,EAAc,EAAU,CAAC,CAAC,UAAY,IAAA,GAAU,CACrE,QAAQ,OAAO,MAAM,+BAA+B,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,CAAC,IAAI,CAC/G,QAAQ,KAAK,EAAE,GAEjB"}