@agimon-ai/model-proxy-mcp 0.29.30 → 0.29.31
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 +2 -2
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +2 -2
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +490 -0
- package/dist/index.d.mts +490 -0
- package/dist/index.mjs +1 -1
- package/dist/{stdio-C3pqLpYB.mjs → stdio-BZfhbEP-.mjs} +20 -20
- package/dist/{stdio-CcaHERH8.cjs → stdio-pd8A9DFx.cjs} +18 -18
- package/package.json +12 -7
package/dist/cli.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const e=require(`./stdio-
|
|
3
|
-
`).slice(1).map(e=>e.trim()).filter(Boolean).map(e=>e.split(/\s+/)).map(t=>({pid:Number.parseInt(t[1]||``,10),port:e})).filter(e=>Number.isInteger(e.pid)&&e.pid>0)}catch{return[]}}async findHealthyServicePort(){let e=await this.getRegistration();if(e){let t=await this.healthCheck.check(e.port);if(t.healthy&&t.serviceName===this.serviceName)return e.port}return null}async clearPortListeners(e,t){let n=await this.listListeningProcesses(e);for(let e of n)t&&e.pid===t||await this.killProcess(e.pid)}async registerService(e,t){await this.portRegistry.reservePort({repositoryPath:this.repositoryPath,serviceName:this.serviceName,serviceType:m,preferredPort:e,portRange:{min:e,max:e},pid:t,host:`127.0.0.1`,force:!0})}async findAvailablePort(t){let n=await this.portRegistry.findAvailablePort({repositoryPath:this.repositoryPath,serviceName:this.serviceName,serviceType:m,host:`127.0.0.1`,preferredPort:t,portRange:e.u});if(!n.success||n.port===void 0)throw Error(`No available port found from ${t}`);return n.port}async fileExists(e){try{return await o.default.access(e),!0}catch{return!1}}async resolveCliPath(){let e=n.default.dirname((0,a.fileURLToPath)(require(`url`).pathToFileURL(__filename).href)),t=n.default.resolve(e,`cli.mjs`);if(await this.fileExists(t))return{cliPath:t,runtime:`node`};let r=n.default.resolve(e,`..`,`cli.mjs`);if(await this.fileExists(r))return{cliPath:r,runtime:`node`};let i=n.default.join(this.repositoryPath,`packages`,`mcp`,`model-proxy-mcp`,`dist`,`cli.mjs`);if(await this.fileExists(i))return{cliPath:i,runtime:`node`};for(let t of[n.default.resolve(e,`cli.ts`),n.default.resolve(e,`..`,`cli.ts`)])if(await this.fileExists(t))return{cliPath:t,runtime:`bun`};let o=n.default.join(this.repositoryPath,`packages`,`mcp`,`model-proxy-mcp`,`src`,`cli.ts`);if(await this.fileExists(o))return{cliPath:o,runtime:`bun`};throw Error(`Cannot find model-proxy-mcp CLI`)}async startHttpServer(e){let{cliPath:t,runtime:n}=await this.resolveCliPath(),r=(0,c.spawn)(n===`bun`?process.versions.bun?process.execPath:`bun`:`node`,n===`bun`?[`run`,t,`http-serve`,`--port`,String(e)]:[t,`http-serve`,`--port`,String(e)],{detached:!0,stdio:`ignore`,env:{...process.env,NODE_ENV:process.env.NODE_ENV||`development`}});if(r.unref(),!r.pid)throw Error(`Failed to spawn HTTP server`);return r.pid}async killProcess(e){try{process.kill(e,`SIGTERM`),await new Promise(e=>setTimeout(e,1e3));try{process.kill(e,0),process.kill(e,`SIGKILL`)}catch{return}}catch{return}}async waitForHealthy(e){let t=Date.now()+5e3,n=`Server failed health check after startup`;for(;Date.now()<t;){let t=await this.healthCheck.check(e);if(t.healthy&&t.serviceName===this.serviceName)return{healthy:!0};n=t.error||`Unexpected service on port ${e}`,await new Promise(e=>setTimeout(e,250))}return{healthy:!1,error:n}}async ensureRunning(t=e.c){try{let e=await this.findHealthyServicePort();if(e!==null){let t=(await this.listListeningProcesses(e))[0]?.pid;return await this.registerService(e,t??process.pid),this.createEmptyStatus({running:!0,port:e,pid:t})}let n=await this.getRegistration();if(n){let e=await this.healthCheck.check(n.port);if(e.healthy&&e.serviceName===this.serviceName)return this.createEmptyStatus({running:!0,port:n.port,pid:n.pid});n.pid&&await this.killProcess(n.pid),await this.clearPortListeners(n.port,n.pid),await this.releaseService(n.pid)}await this.clearPortListeners(t);let r=await this.findAvailablePort(t),i=await this.startHttpServer(r),a=await this.waitForHealthy(r);return a.healthy?(await this.registerService(r,i),this.createEmptyStatus({running:!0,port:r,pid:i})):(await this.killProcess(i),await this.clearPortListeners(r,i),await this.releaseService(i),this.createEmptyStatus({error:a.error||`Server failed health check after startup`}))}catch(e){return this.createEmptyStatus({error:e instanceof Error?e.message:String(e)})}}startHealthMonitor(e,t=1e4){let n=!1,r=0,i=2e3,a=6e4,o=async()=>{if(!n)try{if((await this.getStatus()).running)r=0,n||setTimeout(()=>void o(),t);else{r++;let t=Math.min(i*2**(r-1),a);await this.ensureRunning(e),n||setTimeout(()=>void o(),t)}}catch{r++;let e=Math.min(i*2**(r-1),a);n||setTimeout(()=>void o(),e)}};return setTimeout(()=>void o(),t),()=>{n=!0}}async stop(){let e=await this.getRegistration(),t=!1;e?.pid&&(await this.killProcess(e.pid),t=!0),e&&(await this.clearPortListeners(e.port,e.pid),await this.releaseService(e.pid));let n=await this.findHealthyServicePort();if(n!==null){let e=await this.listListeningProcesses(n);for(let n of e)await this.killProcess(n.pid),t=!0;await this.releaseService()}return t}async getStatus(){let e=await this.getRegistration();if(e&&(await this.healthCheck.check(e.port)).healthy)return this.createEmptyStatus({running:!0,port:e.port,pid:e.pid});let t=await this.findHealthyServicePort();if(t!==null){let e=(await this.listListeningProcesses(t))[0]?.pid;return e&&await this.registerService(t,e),this.createEmptyStatus({running:!0,port:t,pid:e})}return this.createEmptyStatus({error:e?`Registered server is unhealthy`:`No HTTP server registered`})}};const v=`claude`,y=`utf8`,b=`[model-proxy-mcp]`,x=`Recovery: verify HOME, proxy port, and that \`${v}\` is on PATH.`;var S=class extends Error{constructor(e,t,n){super(e,n),this.code=t,this.name=`ClaudeCommandError`}},C=class extends S{constructor(e,t){super(e,`CLAUDE_COMMAND_CONFIG_ERROR`,t),this.name=`ClaudeCommandConfigError`}},w=class extends S{constructor(e,t){super(e,`CLAUDE_COMMAND_VALIDATION_ERROR`,t),this.name=`ClaudeCommandValidationError`}},T=class extends S{constructor(e,t){super(e,`CLAUDE_COMMAND_LAUNCH_ERROR`,t),this.name=`ClaudeCommandLaunchError`}};function E(e){return e.trim().replace(/[^a-zA-Z0-9._-]+/g,`-`).replace(/^-+|-+$/g,``)||`default`}function D(){return`scope-${(0,r.randomBytes)(3).toString(`hex`)}`}function O(e,t,n=process.env){return{...n,ANTHROPIC_BASE_URL:`http://127.0.0.1:${e}/scopes/${t}`,ANTHROPIC_DEFAULT_OPUS_MODEL:n.ANTHROPIC_DEFAULT_OPUS_MODEL||`ccproxy-opus`,ANTHROPIC_DEFAULT_SONNET_MODEL:n.ANTHROPIC_DEFAULT_SONNET_MODEL||`ccproxy-sonnet`,ANTHROPIC_DEFAULT_HAIKU_MODEL:n.ANTHROPIC_DEFAULT_HAIKU_MODEL||`ccproxy-haiku`,CLAUDE_CODE_SUBAGENT_MODEL:n.CLAUDE_CODE_SUBAGENT_MODEL||`ccproxy-subagent`,MODEL_PROXY_MCP_SCOPE:n.MODEL_PROXY_MCP_SCOPE||t,MODEL_PROXY_MCP_SLOT:n.MODEL_PROXY_MCP_SLOT||`default`}}async function k(e,t=process.cwd()){let r=e?n.default.resolve(t,e):n.default.join(t,`model-proxy-mcp.yaml`);try{if(!(await o.default.stat(r)).isFile()){if(e)throw new C(`Scope seed config path is not a file: ${r}`);return}return r}catch(t){if(t.code===`ENOENT`){if(e)throw new C(`Scope seed config file not found: ${r}`,{cause:t});return}throw t instanceof C?t:new C(`Failed to read scope seed config file: ${r}`,{cause:t})}}function A(){let e=process.env.HOME||process.env.USERPROFILE;if(!e)throw new C(`HOME or USERPROFILE is not set`);return n.default.join(e,`.claude-sessions`)}async function j(){let e=A();return await o.default.mkdir(e,{recursive:!0}),e}async function M(e){try{return(await o.default.readFile(e,y)).trim()||null}catch(t){if(t.code===`ENOENT`)return null;throw new C(`Failed to read Claude session file: ${e}`,{cause:t})}}async function N(e,t){let i=await j(),a=n.default.join(i,e);if(t)try{await o.default.rm(a,{force:!0})}catch(e){throw new C(`Failed to clear Claude session file: ${a}`,{cause:e})}let s=await M(a);if(s)return{sessionId:s,resume:!0};let c=(0,r.randomUUID)().toLowerCase();try{await o.default.writeFile(a,`${c}\n`,y)}catch(e){throw new C(`Failed to write Claude session file: ${a}`,{cause:e})}return{sessionId:c,resume:!1}}async function P(t,n,r,i,a){let o=await k(a);await new e.a().ensureConfig(t,o);let s=await new _().ensureRunning(n);if(!s.running||!s.port)throw new T(s.error||`Failed to start model proxy HTTP server`);let{sessionId:l,resume:u}=await N(t,r),d=O(s.port,t),f=[`--dangerously-skip-permissions`,...u?[`--resume`,l]:[`--session-id`,l],...i];return console.log(`${b} Scope: ${t}`),console.log(`${b} Proxy: ${d.ANTHROPIC_BASE_URL}`),console.log(`${b} Session: ${l}${u?` (resume)`:` (new)`}`),o&&console.log(`${b} Scope config seed: ${o}`),new Promise((e,t)=>{let n=(0,c.spawn)(v,f,{stdio:`inherit`,env:d});n.on(`error`,e=>{t(new T(`Failed to launch ${v}`,{cause:e}))}),n.on(`exit`,(n,r)=>{if(r){t(new T(`${v} exited with signal ${r}`));return}e(n??0)})})}const F=new s.Command(`claude`).description(`Launch Claude Code through the model proxy`).option(`-s, --scope <scope>`,`Proxy scope to use`).option(`-p, --port <port>`,`Preferred HTTP port for the proxy`,String(e.c)).option(`-c, --config-file <path>`,`Seed a new scope from the specified scope config file`).option(`--config <path>`,`Alias for --config-file`).option(`--clear-session`,`Start with a fresh Claude session for the selected scope`,!1).allowUnknownOption(!0).allowExcessArguments(!0).argument(`[claudeArgs...]`).action(async(e,t)=>{try{let n=E(t.scope||D()),r=Number.parseInt(t.port,10);if(!Number.isInteger(r)||r<=0)throw new w(`Invalid port: ${t.port}`);let i=await P(n,r,t.clearSession,e,t.configFile??t.config);process.exit(i)}catch(e){let t=e instanceof S?e:new T(`Failed to launch Claude`,{cause:e});console.error(`${b} ${t.code}: ${t.message}`),console.error(`${b} ${x}`),t.cause&&console.error(`${b} Cause:`,t.cause),process.exit(1)}}),I=`127.0.0.1`,L=[`pnpm-workspace.yaml`,`nx.json`,`.git`],R=`service`;function z(e=process.cwd()){let t=n.default.resolve(e);for(;;){for(let e of L)if((0,i.existsSync)(n.default.join(t,e)))return t;let e=n.default.dirname(t);if(e===t)return process.cwd();t=e}}const B=new s.Command(`http-serve`).description(`Start the Claude-compatible HTTP model proxy server`).option(`-p, --port <port>`,`Port to listen on`,String(e.c)).action(async n=>{let r;try{let i=Number.parseInt(n.port,10);if(!Number.isInteger(i)||i<=0)throw Error(`Invalid port: ${n.port}`);let a=new u.PortRegistryService(process.env.PORT_REGISTRY_PATH),o=z(),s=await a.reservePort({repositoryPath:o,serviceName:e.l,serviceType:R,preferredPort:i,portRange:e.u,pid:process.pid,host:I,force:!0});if(!s.success||!s.record)throw Error(s.error||`Failed to reserve port ${i}`);let c=s.record.port;r=await(0,d.createProcessLease)({repositoryPath:o,serviceName:e.l,serviceType:R,pid:process.pid,port:c,host:I,command:process.argv[1],args:process.argv.slice(2)});let l=new e.i;await l.ensureConfig();let f=(0,t.serve)({fetch:e.r(l).fetch,port:c,hostname:I});console.log(`model-proxy-mcp listening on http://${I}:${c}`),console.log(`Claude base URL: http://${I}:${c}`);let p=async t=>{console.log(`\n${t} received. Shutting down...`),f.close(),await r?.release(),await a.releasePort({repositoryPath:o,serviceName:e.l,serviceType:R,pid:process.pid}),process.exit(0)};process.once(`SIGINT`,()=>p(`SIGINT`)),process.once(`SIGTERM`,()=>p(`SIGTERM`))}catch(e){await r?.release(),console.error(`Failed to start HTTP server:`,e),process.exit(1)}}),V=new s.Command(`mcp-serve`).description(`Start MCP server with stdio transport`).option(`--cleanup`,`Stop HTTP server on shutdown`,!1).option(`-p, --port <port>`,`Port for HTTP server`,String(e.c)).action(async t=>{try{let n=Number.parseInt(t.port,10);if(!Number.isInteger(n)||n<=0)throw Error(`Invalid port: ${t.port}`);let r=new e.i;await r.ensureConfig(process.env.MODEL_PROXY_MCP_SCOPE||`default`);let i=new _,a=await i.ensureRunning(n);a.running||console.error(`Warning: HTTP server failed to start: ${a.error}`);let o;a.running&&(o=i.startHealthMonitor(n));let s=new e.t(e.n(r)),c=async e=>{console.error(`\nReceived ${e}, shutting down gracefully...`),o?.(),await s.stop(),t.cleanup&&a.running&&await i.stop(),process.exit(0)};process.once(`SIGINT`,()=>void c(`SIGINT`).catch(e=>{console.error(`Failed to shut down after SIGINT:`,e),process.exit(1)})),process.once(`SIGTERM`,()=>void c(`SIGTERM`).catch(e=>{console.error(`Failed to shut down after SIGTERM:`,e),process.exit(1)})),await s.start()}catch(e){console.error(`Failed to start MCP server:`,e),process.exit(1)}}),H=new s.Command(`start`).description(`Start HTTP and/or MCP server for the model proxy`).option(`--mcp-only`,`Start only the MCP server`,!1).option(`--http-only`,`Start only the HTTP server`,!1).option(`-p, --port <port>`,`Port for HTTP server`,String(e.c)).action(async t=>{try{let n=new _,r=Number.parseInt(t.port,10),i=!t.mcpOnly,a=!t.httpOnly;if(i){let e=await n.ensureRunning(r);e.running||(console.error(`HTTP server failed to start: ${e.error}`),process.exit(1)),console.log(`HTTP server running on http://127.0.0.1:${e.port}`)}if(a){let t=new e.t(e.n());await t.start();let n=async e=>{console.error(`\n${e} received. Shutting down...`),await t.stop(),process.exit(0)};process.on(`SIGINT`,()=>n(`SIGINT`)),process.on(`SIGTERM`,()=>n(`SIGTERM`))}else process.exit(0)}catch(e){console.error(`Failed to start services:`,e),process.exit(1)}}),U=`status`,W=`Failed to read status`;var G=class extends Error{code=`STATUS_CHECK_FAILED`;constructor(e){super(W,{cause:e instanceof Error?e:Error(String(e))}),this.name=`StatusCommandError`}};const K=new s.Command(U).description(`Show proxy server and profile status`).option(`-s, --scope <scope>`,`Configuration scope`,`default`).action(async t=>{try{let n=await new _().getStatus(),r=await new e.i().getStatus(t.scope,n.port,n.pid);r.running=n.running,r.error=n.error,e.s.info(JSON.stringify(r,null,2)),process.exit(0)}catch(n){let r=new G(n);e.s.error(W,{command:U,code:r.code,scope:t.scope,cause:r.cause}),process.exit(1)}}),q=new s.Command(`stop`).description(`Stop the background HTTP server`).action(async()=>{try{let e=await new _().stop();console.log(e?`HTTP server stopped`:`No HTTP server running`),process.exit(0)}catch(e){console.error(`Failed to stop HTTP server:`,e),process.exit(1)}}),J=(0,n.dirname)((0,a.fileURLToPath)(require(`url`).pathToFileURL(__filename).href)),Y=JSON.parse((0,i.readFileSync)((0,n.join)(J,`../package.json`),`utf-8`));async function X(){let e=new s.Command;e.name(`model-proxy-mcp`).description(`Claude-compatible model proxy MCP`).version(Y.version),e.addCommand(H),e.addCommand(q),e.addCommand(K),e.addCommand(F),e.addCommand(B),e.addCommand(V),await e.parseAsync(process.argv)}X();
|
|
2
|
+
const e=require(`./stdio-pd8A9DFx.cjs`);let t=require(`@hono/node-server`),n=require(`node:path`);n=e.d(n,1);let r=require(`node:crypto`),i=require(`node:fs`),a=require(`node:url`),o=require(`node:fs/promises`);o=e.d(o,1);let s=require(`commander`),c=require(`node:child_process`),l=require(`node:util`),u=require(`@agimon-ai/foundation-port-registry`),d=require(`@agimon-ai/foundation-process-registry`);var f=class{async check(e){try{let t=await fetch(`http://127.0.0.1:${e}/health`);return t.ok?{healthy:!0,serviceName:(await t.json()).service}:{healthy:!1,error:`Health check failed with status ${t.status}`}}catch(e){return{healthy:!1,error:e instanceof Error?e.message:String(e)}}}};const p=[`pnpm-workspace.yaml`,`nx.json`,`.git`],m=`service`,h=(0,l.promisify)(c.execFile);function g(e=process.cwd()){let t=n.default.resolve(e);for(;;){for(let e of p)if((0,i.existsSync)(n.default.join(t,e)))return t;let e=n.default.dirname(t);if(e===t)return process.cwd();t=e}}var _=class{repositoryPath=g(process.cwd());serviceName=e.l;portRegistry;constructor(e=new f){this.healthCheck=e,this.portRegistry=new u.PortRegistryService(process.env.PORT_REGISTRY_PATH)}createEmptyStatus(e={}){return{running:!1,scope:`default`,activeProfileId:null,auth:{configured:!1,authFilePath:``},profiles:[],slotModels:{},...e}}async getRegistration(){let e=await this.portRegistry.getPort({repositoryPath:this.repositoryPath,serviceName:this.serviceName,serviceType:m});return!e.success||!e.record?null:{port:e.record.port,pid:e.record.pid}}async releaseService(e){await this.portRegistry.releasePort({repositoryPath:this.repositoryPath,serviceName:this.serviceName,serviceType:m,pid:e})}async listListeningProcesses(e){try{let{stdout:t}=await h(`sh`,[`-lc`,`lsof -n -P -sTCP:LISTEN -iTCP:${e} || true`],{encoding:`utf8`});return t.split(`
|
|
3
|
+
`).slice(1).map(e=>e.trim()).filter(Boolean).map(e=>e.split(/\s+/)).map(t=>({pid:Number.parseInt(t[1]||``,10),port:e})).filter(e=>Number.isInteger(e.pid)&&e.pid>0)}catch{return[]}}async findHealthyServicePort(){let e=await this.getRegistration();if(e){let t=await this.healthCheck.check(e.port);if(t.healthy&&t.serviceName===this.serviceName)return e.port}return null}async clearPortListeners(e,t){let n=await this.listListeningProcesses(e);for(let e of n)t&&e.pid===t||await this.killProcess(e.pid)}async registerService(e,t){await this.portRegistry.reservePort({repositoryPath:this.repositoryPath,serviceName:this.serviceName,serviceType:m,preferredPort:e,portRange:{min:e,max:e},pid:t,host:`127.0.0.1`,force:!0})}async findAvailablePort(t){let n=await this.portRegistry.findAvailablePort({repositoryPath:this.repositoryPath,serviceName:this.serviceName,serviceType:m,host:`127.0.0.1`,preferredPort:t,portRange:e.u});if(!n.success||n.port===void 0)throw Error(`No available port found from ${t}`);return n.port}async fileExists(e){try{return await o.default.access(e),!0}catch{return!1}}async resolveCliPath(){let e=n.default.dirname((0,a.fileURLToPath)(require(`url`).pathToFileURL(__filename).href)),t=n.default.resolve(e,`cli.mjs`);if(await this.fileExists(t))return{cliPath:t,runtime:`node`};let r=n.default.resolve(e,`..`,`cli.mjs`);if(await this.fileExists(r))return{cliPath:r,runtime:`node`};let i=n.default.join(this.repositoryPath,`packages`,`mcp`,`model-proxy-mcp`,`dist`,`cli.mjs`);if(await this.fileExists(i))return{cliPath:i,runtime:`node`};for(let t of[n.default.resolve(e,`cli.ts`),n.default.resolve(e,`..`,`cli.ts`)])if(await this.fileExists(t))return{cliPath:t,runtime:`bun`};let o=n.default.join(this.repositoryPath,`packages`,`mcp`,`model-proxy-mcp`,`src`,`cli.ts`);if(await this.fileExists(o))return{cliPath:o,runtime:`bun`};throw Error(`Cannot find model-proxy-mcp CLI`)}async startHttpServer(e){let{cliPath:t,runtime:n}=await this.resolveCliPath(),r=(0,c.spawn)(n===`bun`?process.versions.bun?process.execPath:`bun`:`node`,n===`bun`?[`run`,t,`http-serve`,`--port`,String(e)]:[t,`http-serve`,`--port`,String(e)],{detached:!0,stdio:`ignore`,env:{...process.env,NODE_ENV:process.env.NODE_ENV||`development`}});if(r.unref(),!r.pid)throw Error(`Failed to spawn HTTP server`);return r.pid}async killProcess(e){try{process.kill(e,`SIGTERM`),await new Promise(e=>setTimeout(e,1e3));try{process.kill(e,0),process.kill(e,`SIGKILL`)}catch{return}}catch{return}}async waitForHealthy(e){let t=Date.now()+5e3,n=`Server failed health check after startup`;for(;Date.now()<t;){let t=await this.healthCheck.check(e);if(t.healthy&&t.serviceName===this.serviceName)return{healthy:!0};n=t.error||`Unexpected service on port ${e}`,await new Promise(e=>setTimeout(e,250))}return{healthy:!1,error:n}}async ensureRunning(t=e.c){try{let e=await this.findHealthyServicePort();if(e!==null){let t=(await this.listListeningProcesses(e))[0]?.pid;return await this.registerService(e,t??process.pid),this.createEmptyStatus({running:!0,port:e,pid:t})}let n=await this.getRegistration();if(n){let e=await this.healthCheck.check(n.port);if(e.healthy&&e.serviceName===this.serviceName)return this.createEmptyStatus({running:!0,port:n.port,pid:n.pid});n.pid&&await this.killProcess(n.pid),await this.clearPortListeners(n.port,n.pid),await this.releaseService(n.pid)}await this.clearPortListeners(t);let r=await this.findAvailablePort(t),i=await this.startHttpServer(r),a=await this.waitForHealthy(r);return a.healthy?(await this.registerService(r,i),this.createEmptyStatus({running:!0,port:r,pid:i})):(await this.killProcess(i),await this.clearPortListeners(r,i),await this.releaseService(i),this.createEmptyStatus({error:a.error||`Server failed health check after startup`}))}catch(e){return this.createEmptyStatus({error:e instanceof Error?e.message:String(e)})}}startHealthMonitor(e,t=1e4){let n=!1,r=0,i=2e3,a=6e4,o=async()=>{if(!n)try{if((await this.getStatus()).running)r=0,n||setTimeout(()=>void o(),t);else{r++;let t=Math.min(i*2**(r-1),a);await this.ensureRunning(e),n||setTimeout(()=>void o(),t)}}catch{r++;let e=Math.min(i*2**(r-1),a);n||setTimeout(()=>void o(),e)}};return setTimeout(()=>void o(),t),()=>{n=!0}}async stop(){let e=await this.getRegistration(),t=!1;e?.pid&&(await this.killProcess(e.pid),t=!0),e&&(await this.clearPortListeners(e.port,e.pid),await this.releaseService(e.pid));let n=await this.findHealthyServicePort();if(n!==null){let e=await this.listListeningProcesses(n);for(let n of e)await this.killProcess(n.pid),t=!0;await this.releaseService()}return t}async getStatus(){let e=await this.getRegistration();if(e&&(await this.healthCheck.check(e.port)).healthy)return this.createEmptyStatus({running:!0,port:e.port,pid:e.pid});let t=await this.findHealthyServicePort();if(t!==null){let e=(await this.listListeningProcesses(t))[0]?.pid;return e&&await this.registerService(t,e),this.createEmptyStatus({running:!0,port:t,pid:e})}return this.createEmptyStatus({error:e?`Registered server is unhealthy`:`No HTTP server registered`})}};const v=`claude`,y=`utf8`,b=`[model-proxy-mcp]`,x=`Recovery: verify HOME, proxy port, and that \`${v}\` is on PATH.`;var S=class extends Error{constructor(e,t,n){super(e,n),this.code=t,this.name=`ClaudeCommandError`}},C=class extends S{constructor(e,t){super(e,`CLAUDE_COMMAND_CONFIG_ERROR`,t),this.name=`ClaudeCommandConfigError`}},w=class extends S{constructor(e,t){super(e,`CLAUDE_COMMAND_VALIDATION_ERROR`,t),this.name=`ClaudeCommandValidationError`}},T=class extends S{constructor(e,t){super(e,`CLAUDE_COMMAND_LAUNCH_ERROR`,t),this.name=`ClaudeCommandLaunchError`}};function E(e){return e.trim().replace(/[^a-zA-Z0-9._-]+/g,`-`).replace(/^-+|-+$/g,``)||`default`}function D(){return`scope-${(0,r.randomBytes)(3).toString(`hex`)}`}function O(e,t,n=process.env){return{...n,ANTHROPIC_BASE_URL:`http://127.0.0.1:${e}/scopes/${t}`,ANTHROPIC_DEFAULT_OPUS_MODEL:n.ANTHROPIC_DEFAULT_OPUS_MODEL||`ccproxy-opus`,ANTHROPIC_DEFAULT_SONNET_MODEL:n.ANTHROPIC_DEFAULT_SONNET_MODEL||`ccproxy-sonnet`,ANTHROPIC_DEFAULT_HAIKU_MODEL:n.ANTHROPIC_DEFAULT_HAIKU_MODEL||`ccproxy-haiku`,CLAUDE_CODE_SUBAGENT_MODEL:n.CLAUDE_CODE_SUBAGENT_MODEL||`ccproxy-subagent`,MODEL_PROXY_MCP_SCOPE:n.MODEL_PROXY_MCP_SCOPE||t,MODEL_PROXY_MCP_SLOT:n.MODEL_PROXY_MCP_SLOT||`default`}}async function k(e,t=process.cwd()){let r=e?n.default.resolve(t,e):n.default.join(t,`model-proxy-mcp.yaml`);try{if(!(await o.default.stat(r)).isFile()){if(e)throw new C(`Scope seed config path is not a file: ${r}`);return}return r}catch(t){if(t.code===`ENOENT`){if(e)throw new C(`Scope seed config file not found: ${r}`,{cause:t});return}throw t instanceof C?t:new C(`Failed to read scope seed config file: ${r}`,{cause:t})}}function A(){let e=process.env.HOME||process.env.USERPROFILE;if(!e)throw new C(`HOME or USERPROFILE is not set`);return n.default.join(e,`.claude-sessions`)}async function j(){let e=A();return await o.default.mkdir(e,{recursive:!0}),e}async function M(e){try{return(await o.default.readFile(e,y)).trim()||null}catch(t){if(t.code===`ENOENT`)return null;throw new C(`Failed to read Claude session file: ${e}`,{cause:t})}}async function N(e,t){let i=await j(),a=n.default.join(i,e);if(t)try{await o.default.rm(a,{force:!0})}catch(e){throw new C(`Failed to clear Claude session file: ${a}`,{cause:e})}let s=await M(a);if(s)return{sessionId:s,resume:!0};let c=(0,r.randomUUID)().toLowerCase();try{await o.default.writeFile(a,`${c}\n`,y)}catch(e){throw new C(`Failed to write Claude session file: ${a}`,{cause:e})}return{sessionId:c,resume:!1}}async function P(t,n,r,i,a){let o=await k(a);await new e.a().ensureConfig(t,o);let s=await new _().ensureRunning(n);if(!s.running||!s.port)throw new T(s.error||`Failed to start model proxy HTTP server`);let{sessionId:l,resume:u}=await N(t,r),d=O(s.port,t),f=[`--dangerously-skip-permissions`,...u?[`--resume`,l]:[`--session-id`,l],...i];return console.log(`${b} Scope: ${t}`),console.log(`${b} Proxy: ${d.ANTHROPIC_BASE_URL}`),console.log(`${b} Session: ${l}${u?` (resume)`:` (new)`}`),o&&console.log(`${b} Scope config seed: ${o}`),new Promise((e,t)=>{let n=(0,c.spawn)(v,f,{stdio:`inherit`,env:d});n.on(`error`,e=>{t(new T(`Failed to launch ${v}`,{cause:e}))}),n.on(`exit`,(n,r)=>{if(r){t(new T(`${v} exited with signal ${r}`));return}e(n??0)})})}const F=new s.Command(`claude`).description(`Launch Claude Code through the model proxy`).option(`-s, --scope <scope>`,`Proxy scope to use`).option(`-p, --port <port>`,`Preferred HTTP port for the proxy`,String(e.c)).option(`-c, --config-file <path>`,`Seed a new scope from the specified scope config file`).option(`--config <path>`,`Alias for --config-file`).option(`--clear-session`,`Start with a fresh Claude session for the selected scope`,!1).allowUnknownOption(!0).allowExcessArguments(!0).argument(`[claudeArgs...]`).action(async(e,t)=>{try{let n=E(t.scope||D()),r=Number.parseInt(t.port,10);if(!Number.isInteger(r)||r<=0)throw new w(`Invalid port: ${t.port}`);let i=await P(n,r,t.clearSession,e,t.configFile??t.config);process.exit(i)}catch(e){let t=e instanceof S?e:new T(`Failed to launch Claude`,{cause:e});console.error(`${b} ${t.code}: ${t.message}`),console.error(`${b} ${x}`),t.cause&&console.error(`${b} Cause:`,t.cause),process.exit(1)}}),I=`127.0.0.1`,L=[`pnpm-workspace.yaml`,`nx.json`,`.git`],R=`service`;function z(e=process.cwd()){let t=n.default.resolve(e);for(;;){for(let e of L)if((0,i.existsSync)(n.default.join(t,e)))return t;let e=n.default.dirname(t);if(e===t)return process.cwd();t=e}}const B=new s.Command(`http-serve`).description(`Start the Claude-compatible HTTP model proxy server`).option(`-p, --port <port>`,`Port to listen on`,String(e.c)).action(async n=>{let r;try{let i=Number.parseInt(n.port,10);if(!Number.isInteger(i)||i<=0)throw Error(`Invalid port: ${n.port}`);let a=new u.PortRegistryService(process.env.PORT_REGISTRY_PATH),o=z(),s=await a.reservePort({repositoryPath:o,serviceName:e.l,serviceType:R,preferredPort:i,portRange:e.u,pid:process.pid,host:I,force:!0});if(!s.success||!s.record)throw Error(s.error||`Failed to reserve port ${i}`);let c=s.record.port;r=await(0,d.createProcessLease)({repositoryPath:o,serviceName:e.l,serviceType:R,pid:process.pid,port:c,host:I,command:process.argv[1],args:process.argv.slice(2)});let l=new e.i;await l.ensureConfig();let f=(0,t.serve)({fetch:e.r(l).fetch,port:c,hostname:I});console.log(`model-proxy-mcp listening on http://${I}:${c}`),console.log(`Claude base URL: http://${I}:${c}`);let p=async t=>{console.log(`\n${t} received. Shutting down...`),f.close(),await r?.release(),await a.releasePort({repositoryPath:o,serviceName:e.l,serviceType:R,pid:process.pid}),process.exit(0)};process.once(`SIGINT`,()=>p(`SIGINT`)),process.once(`SIGTERM`,()=>p(`SIGTERM`))}catch(e){await r?.release(),console.error(`Failed to start HTTP server:`,e),process.exit(1)}}),V=new s.Command(`mcp-serve`).description(`Start MCP server with stdio transport`).option(`--cleanup`,`Stop HTTP server on shutdown`,!1).option(`-p, --port <port>`,`Port for HTTP server`,String(e.c)).action(async t=>{try{let n=Number.parseInt(t.port,10);if(!Number.isInteger(n)||n<=0)throw Error(`Invalid port: ${t.port}`);let r=new e.i;await r.ensureConfig(process.env.MODEL_PROXY_MCP_SCOPE||`default`);let i=new _,a=await i.ensureRunning(n);a.running||console.error(`Warning: HTTP server failed to start: ${a.error}`);let o;a.running&&(o=i.startHealthMonitor(n));let s=new e.t(()=>e.n(r)),c=async e=>{console.error(`\nReceived ${e}, shutting down gracefully...`),o?.(),await s.stop(),t.cleanup&&a.running&&await i.stop(),process.exit(0)};process.once(`SIGINT`,()=>void c(`SIGINT`).catch(e=>{console.error(`Failed to shut down after SIGINT:`,e),process.exit(1)})),process.once(`SIGTERM`,()=>void c(`SIGTERM`).catch(e=>{console.error(`Failed to shut down after SIGTERM:`,e),process.exit(1)})),await s.start()}catch(e){console.error(`Failed to start MCP server:`,e),process.exit(1)}}),H=new s.Command(`start`).description(`Start HTTP and/or MCP server for the model proxy`).option(`--mcp-only`,`Start only the MCP server`,!1).option(`--http-only`,`Start only the HTTP server`,!1).option(`-p, --port <port>`,`Port for HTTP server`,String(e.c)).action(async t=>{try{let n=new _,r=Number.parseInt(t.port,10),i=!t.mcpOnly,a=!t.httpOnly;if(i){let e=await n.ensureRunning(r);e.running||(console.error(`HTTP server failed to start: ${e.error}`),process.exit(1)),a?console.error(`HTTP server running on http://127.0.0.1:${e.port}`):console.log(`HTTP server running on http://127.0.0.1:${e.port}`)}if(a){let t=new e.t(()=>e.n());await t.start();let n=async e=>{console.error(`\n${e} received. Shutting down...`),await t.stop(),process.exit(0)};process.on(`SIGINT`,()=>n(`SIGINT`)),process.on(`SIGTERM`,()=>n(`SIGTERM`))}else process.exit(0)}catch(e){console.error(`Failed to start services:`,e),process.exit(1)}}),U=`status`,W=`Failed to read status`;var G=class extends Error{code=`STATUS_CHECK_FAILED`;constructor(e){super(W,{cause:e instanceof Error?e:Error(String(e))}),this.name=`StatusCommandError`}};const K=new s.Command(U).description(`Show proxy server and profile status`).option(`-s, --scope <scope>`,`Configuration scope`,`default`).action(async t=>{try{let n=await new _().getStatus(),r=await new e.i().getStatus(t.scope,n.port,n.pid);r.running=n.running,r.error=n.error,e.s.info(JSON.stringify(r,null,2)),process.exit(0)}catch(n){let r=new G(n);e.s.error(W,{command:U,code:r.code,scope:t.scope,cause:r.cause}),process.exit(1)}}),q=new s.Command(`stop`).description(`Stop the background HTTP server`).action(async()=>{try{let e=await new _().stop();console.log(e?`HTTP server stopped`:`No HTTP server running`),process.exit(0)}catch(e){console.error(`Failed to stop HTTP server:`,e),process.exit(1)}}),J=(0,n.dirname)((0,a.fileURLToPath)(require(`url`).pathToFileURL(__filename).href)),Y=JSON.parse((0,i.readFileSync)((0,n.join)(J,`../package.json`),`utf-8`));async function X(){let e=new s.Command;e.name(`model-proxy-mcp`).description(`Claude-compatible model proxy MCP`).version(Y.version),e.addCommand(H),e.addCommand(q),e.addCommand(K),e.addCommand(F),e.addCommand(B),e.addCommand(V),await e.parseAsync(process.argv)}X();
|
package/dist/cli.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/cli.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/cli.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{a as e,c as t,i as n,l as r,n as i,r as a,s as o,t as s,u as c}from"./stdio-
|
|
3
|
-
`).slice(1).map(e=>e.trim()).filter(Boolean).map(e=>e.split(/\s+/)).map(t=>({pid:Number.parseInt(t[1]||``,10),port:e})).filter(e=>Number.isInteger(e.pid)&&e.pid>0)}catch{return[]}}async findHealthyServicePort(){let e=await this.getRegistration();if(e){let t=await this.healthCheck.check(e.port);if(t.healthy&&t.serviceName===this.serviceName)return e.port}return null}async clearPortListeners(e,t){let n=await this.listListeningProcesses(e);for(let e of n)t&&e.pid===t||await this.killProcess(e.pid)}async registerService(e,t){await this.portRegistry.reservePort({repositoryPath:this.repositoryPath,serviceName:this.serviceName,serviceType:E,preferredPort:e,portRange:{min:e,max:e},pid:t,host:`127.0.0.1`,force:!0})}async findAvailablePort(e){let t=await this.portRegistry.findAvailablePort({repositoryPath:this.repositoryPath,serviceName:this.serviceName,serviceType:E,host:`127.0.0.1`,preferredPort:e,portRange:c});if(!t.success||t.port===void 0)throw Error(`No available port found from ${e}`);return t.port}async fileExists(e){try{return await v.access(e),!0}catch{return!1}}async resolveCliPath(){let e=u.dirname(_(import.meta.url)),t=u.resolve(e,`cli.mjs`);if(await this.fileExists(t))return{cliPath:t,runtime:`node`};let n=u.resolve(e,`..`,`cli.mjs`);if(await this.fileExists(n))return{cliPath:n,runtime:`node`};let r=u.join(this.repositoryPath,`packages`,`mcp`,`model-proxy-mcp`,`dist`,`cli.mjs`);if(await this.fileExists(r))return{cliPath:r,runtime:`node`};for(let t of[u.resolve(e,`cli.ts`),u.resolve(e,`..`,`cli.ts`)])if(await this.fileExists(t))return{cliPath:t,runtime:`bun`};let i=u.join(this.repositoryPath,`packages`,`mcp`,`model-proxy-mcp`,`src`,`cli.ts`);if(await this.fileExists(i))return{cliPath:i,runtime:`bun`};throw Error(`Cannot find model-proxy-mcp CLI`)}async startHttpServer(e){let{cliPath:t,runtime:n}=await this.resolveCliPath(),r=b(n===`bun`?process.versions.bun?process.execPath:`bun`:`node`,n===`bun`?[`run`,t,`http-serve`,`--port`,String(e)]:[t,`http-serve`,`--port`,String(e)],{detached:!0,stdio:`ignore`,env:{...process.env,NODE_ENV:process.env.NODE_ENV||`development`}});if(r.unref(),!r.pid)throw Error(`Failed to spawn HTTP server`);return r.pid}async killProcess(e){try{process.kill(e,`SIGTERM`),await new Promise(e=>setTimeout(e,1e3));try{process.kill(e,0),process.kill(e,`SIGKILL`)}catch{return}}catch{return}}async waitForHealthy(e){let t=Date.now()+5e3,n=`Server failed health check after startup`;for(;Date.now()<t;){let t=await this.healthCheck.check(e);if(t.healthy&&t.serviceName===this.serviceName)return{healthy:!0};n=t.error||`Unexpected service on port ${e}`,await new Promise(e=>setTimeout(e,250))}return{healthy:!1,error:n}}async ensureRunning(e=t){try{let t=await this.findHealthyServicePort();if(t!==null){let e=(await this.listListeningProcesses(t))[0]?.pid;return await this.registerService(t,e??process.pid),this.createEmptyStatus({running:!0,port:t,pid:e})}let n=await this.getRegistration();if(n){let e=await this.healthCheck.check(n.port);if(e.healthy&&e.serviceName===this.serviceName)return this.createEmptyStatus({running:!0,port:n.port,pid:n.pid});n.pid&&await this.killProcess(n.pid),await this.clearPortListeners(n.port,n.pid),await this.releaseService(n.pid)}await this.clearPortListeners(e);let r=await this.findAvailablePort(e),i=await this.startHttpServer(r),a=await this.waitForHealthy(r);return a.healthy?(await this.registerService(r,i),this.createEmptyStatus({running:!0,port:r,pid:i})):(await this.killProcess(i),await this.clearPortListeners(r,i),await this.releaseService(i),this.createEmptyStatus({error:a.error||`Server failed health check after startup`}))}catch(e){return this.createEmptyStatus({error:e instanceof Error?e.message:String(e)})}}startHealthMonitor(e,t=1e4){let n=!1,r=0,i=2e3,a=6e4,o=async()=>{if(!n)try{if((await this.getStatus()).running)r=0,n||setTimeout(()=>void o(),t);else{r++;let t=Math.min(i*2**(r-1),a);await this.ensureRunning(e),n||setTimeout(()=>void o(),t)}}catch{r++;let e=Math.min(i*2**(r-1),a);n||setTimeout(()=>void o(),e)}};return setTimeout(()=>void o(),t),()=>{n=!0}}async stop(){let e=await this.getRegistration(),t=!1;e?.pid&&(await this.killProcess(e.pid),t=!0),e&&(await this.clearPortListeners(e.port,e.pid),await this.releaseService(e.pid));let n=await this.findHealthyServicePort();if(n!==null){let e=await this.listListeningProcesses(n);for(let n of e)await this.killProcess(n.pid),t=!0;await this.releaseService()}return t}async getStatus(){let e=await this.getRegistration();if(e&&(await this.healthCheck.check(e.port)).healthy)return this.createEmptyStatus({running:!0,port:e.port,pid:e.pid});let t=await this.findHealthyServicePort();if(t!==null){let e=(await this.listListeningProcesses(t))[0]?.pid;return e&&await this.registerService(t,e),this.createEmptyStatus({running:!0,port:t,pid:e})}return this.createEmptyStatus({error:e?`Registered server is unhealthy`:`No HTTP server registered`})}};const A=`claude`,j=`utf8`,M=`[model-proxy-mcp]`,te=`Recovery: verify HOME, proxy port, and that \`${A}\` is on PATH.`;var N=class extends Error{constructor(e,t,n){super(e,n),this.code=t,this.name=`ClaudeCommandError`}},P=class extends N{constructor(e,t){super(e,`CLAUDE_COMMAND_CONFIG_ERROR`,t),this.name=`ClaudeCommandConfigError`}},F=class extends N{constructor(e,t){super(e,`CLAUDE_COMMAND_VALIDATION_ERROR`,t),this.name=`ClaudeCommandValidationError`}},I=class extends N{constructor(e,t){super(e,`CLAUDE_COMMAND_LAUNCH_ERROR`,t),this.name=`ClaudeCommandLaunchError`}};function L(e){return e.trim().replace(/[^a-zA-Z0-9._-]+/g,`-`).replace(/^-+|-+$/g,``)||`default`}function R(){return`scope-${p(3).toString(`hex`)}`}function z(e,t,n=process.env){return{...n,ANTHROPIC_BASE_URL:`http://127.0.0.1:${e}/scopes/${t}`,ANTHROPIC_DEFAULT_OPUS_MODEL:n.ANTHROPIC_DEFAULT_OPUS_MODEL||`ccproxy-opus`,ANTHROPIC_DEFAULT_SONNET_MODEL:n.ANTHROPIC_DEFAULT_SONNET_MODEL||`ccproxy-sonnet`,ANTHROPIC_DEFAULT_HAIKU_MODEL:n.ANTHROPIC_DEFAULT_HAIKU_MODEL||`ccproxy-haiku`,CLAUDE_CODE_SUBAGENT_MODEL:n.CLAUDE_CODE_SUBAGENT_MODEL||`ccproxy-subagent`,MODEL_PROXY_MCP_SCOPE:n.MODEL_PROXY_MCP_SCOPE||t,MODEL_PROXY_MCP_SLOT:n.MODEL_PROXY_MCP_SLOT||`default`}}async function B(e,t=process.cwd()){let n=e?u.resolve(t,e):u.join(t,`model-proxy-mcp.yaml`);try{if(!(await v.stat(n)).isFile()){if(e)throw new P(`Scope seed config path is not a file: ${n}`);return}return n}catch(t){if(t.code===`ENOENT`){if(e)throw new P(`Scope seed config file not found: ${n}`,{cause:t});return}throw t instanceof P?t:new P(`Failed to read scope seed config file: ${n}`,{cause:t})}}function V(){let e=process.env.HOME||process.env.USERPROFILE;if(!e)throw new P(`HOME or USERPROFILE is not set`);return u.join(e,`.claude-sessions`)}async function H(){let e=V();return await v.mkdir(e,{recursive:!0}),e}async function U(e){try{return(await v.readFile(e,j)).trim()||null}catch(t){if(t.code===`ENOENT`)return null;throw new P(`Failed to read Claude session file: ${e}`,{cause:t})}}async function W(e,t){let n=await H(),r=u.join(n,e);if(t)try{await v.rm(r,{force:!0})}catch(e){throw new P(`Failed to clear Claude session file: ${r}`,{cause:e})}let i=await U(r);if(i)return{sessionId:i,resume:!0};let a=m().toLowerCase();try{await v.writeFile(r,`${a}\n`,j)}catch(e){throw new P(`Failed to write Claude session file: ${r}`,{cause:e})}return{sessionId:a,resume:!1}}async function G(t,n,r,i,a){let o=await B(a);await new e().ensureConfig(t,o);let s=await new k().ensureRunning(n);if(!s.running||!s.port)throw new I(s.error||`Failed to start model proxy HTTP server`);let{sessionId:c,resume:l}=await W(t,r),u=z(s.port,t),d=[`--dangerously-skip-permissions`,...l?[`--resume`,c]:[`--session-id`,c],...i];return console.log(`${M} Scope: ${t}`),console.log(`${M} Proxy: ${u.ANTHROPIC_BASE_URL}`),console.log(`${M} Session: ${c}${l?` (resume)`:` (new)`}`),o&&console.log(`${M} Scope config seed: ${o}`),new Promise((e,t)=>{let n=b(A,d,{stdio:`inherit`,env:u});n.on(`error`,e=>{t(new I(`Failed to launch ${A}`,{cause:e}))}),n.on(`exit`,(n,r)=>{if(r){t(new I(`${A} exited with signal ${r}`));return}e(n??0)})})}const K=new y(`claude`).description(`Launch Claude Code through the model proxy`).option(`-s, --scope <scope>`,`Proxy scope to use`).option(`-p, --port <port>`,`Preferred HTTP port for the proxy`,String(t)).option(`-c, --config-file <path>`,`Seed a new scope from the specified scope config file`).option(`--config <path>`,`Alias for --config-file`).option(`--clear-session`,`Start with a fresh Claude session for the selected scope`,!1).allowUnknownOption(!0).allowExcessArguments(!0).argument(`[claudeArgs...]`).action(async(e,t)=>{try{let n=L(t.scope||R()),r=Number.parseInt(t.port,10);if(!Number.isInteger(r)||r<=0)throw new F(`Invalid port: ${t.port}`);let i=await G(n,r,t.clearSession,e,t.configFile??t.config);process.exit(i)}catch(e){let t=e instanceof N?e:new I(`Failed to launch Claude`,{cause:e});console.error(`${M} ${t.code}: ${t.message}`),console.error(`${M} ${te}`),t.cause&&console.error(`${M} Cause:`,t.cause),process.exit(1)}}),q=`127.0.0.1`,J=[`pnpm-workspace.yaml`,`nx.json`,`.git`],Y=`service`;function X(e=process.cwd()){let t=u.resolve(e);for(;;){for(let e of J)if(h(u.join(t,e)))return t;let e=u.dirname(t);if(e===t)return process.cwd();t=e}}const Z=new y(`http-serve`).description(`Start the Claude-compatible HTTP model proxy server`).option(`-p, --port <port>`,`Port to listen on`,String(t)).action(async e=>{let t;try{let i=Number.parseInt(e.port,10);if(!Number.isInteger(i)||i<=0)throw Error(`Invalid port: ${e.port}`);let o=new S(process.env.PORT_REGISTRY_PATH),s=X(),u=await o.reservePort({repositoryPath:s,serviceName:r,serviceType:Y,preferredPort:i,portRange:c,pid:process.pid,host:q,force:!0});if(!u.success||!u.record)throw Error(u.error||`Failed to reserve port ${i}`);let d=u.record.port;t=await C({repositoryPath:s,serviceName:r,serviceType:Y,pid:process.pid,port:d,host:q,command:process.argv[1],args:process.argv.slice(2)});let f=new n;await f.ensureConfig();let p=l({fetch:a(f).fetch,port:d,hostname:q});console.log(`model-proxy-mcp listening on http://${q}:${d}`),console.log(`Claude base URL: http://${q}:${d}`);let m=async e=>{console.log(`\n${e} received. Shutting down...`),p.close(),await t?.release(),await o.releasePort({repositoryPath:s,serviceName:r,serviceType:Y,pid:process.pid}),process.exit(0)};process.once(`SIGINT`,()=>m(`SIGINT`)),process.once(`SIGTERM`,()=>m(`SIGTERM`))}catch(e){await t?.release(),console.error(`Failed to start HTTP server:`,e),process.exit(1)}}),ne=new y(`mcp-serve`).description(`Start MCP server with stdio transport`).option(`--cleanup`,`Stop HTTP server on shutdown`,!1).option(`-p, --port <port>`,`Port for HTTP server`,String(t)).action(async e=>{try{let t=Number.parseInt(e.port,10);if(!Number.isInteger(t)||t<=0)throw Error(`Invalid port: ${e.port}`);let r=new n;await r.ensureConfig(process.env.MODEL_PROXY_MCP_SCOPE||`default`);let a=new k,o=await a.ensureRunning(t);o.running||console.error(`Warning: HTTP server failed to start: ${o.error}`);let c;o.running&&(c=a.startHealthMonitor(t));let l=new s(i(r)),u=async t=>{console.error(`\nReceived ${t}, shutting down gracefully...`),c?.(),await l.stop(),e.cleanup&&o.running&&await a.stop(),process.exit(0)};process.once(`SIGINT`,()=>void u(`SIGINT`).catch(e=>{console.error(`Failed to shut down after SIGINT:`,e),process.exit(1)})),process.once(`SIGTERM`,()=>void u(`SIGTERM`).catch(e=>{console.error(`Failed to shut down after SIGTERM:`,e),process.exit(1)})),await l.start()}catch(e){console.error(`Failed to start MCP server:`,e),process.exit(1)}}),re=new y(`start`).description(`Start HTTP and/or MCP server for the model proxy`).option(`--mcp-only`,`Start only the MCP server`,!1).option(`--http-only`,`Start only the HTTP server`,!1).option(`-p, --port <port>`,`Port for HTTP server`,String(t)).action(async e=>{try{let t=new k,n=Number.parseInt(e.port,10),r=!e.mcpOnly,a=!e.httpOnly;if(r){let e=await t.ensureRunning(n);e.running||(console.error(`HTTP server failed to start: ${e.error}`),process.exit(1)),console.log(`HTTP server running on http://127.0.0.1:${e.port}`)}if(a){let e=new s(i());await e.start();let t=async t=>{console.error(`\n${t} received. Shutting down...`),await e.stop(),process.exit(0)};process.on(`SIGINT`,()=>t(`SIGINT`)),process.on(`SIGTERM`,()=>t(`SIGTERM`))}else process.exit(0)}catch(e){console.error(`Failed to start services:`,e),process.exit(1)}}),Q=`status`,$=`Failed to read status`;var ie=class extends Error{code=`STATUS_CHECK_FAILED`;constructor(e){super($,{cause:e instanceof Error?e:Error(String(e))}),this.name=`StatusCommandError`}};const ae=new y(Q).description(`Show proxy server and profile status`).option(`-s, --scope <scope>`,`Configuration scope`,`default`).action(async e=>{try{let t=await new k().getStatus(),r=await new n().getStatus(e.scope,t.port,t.pid);r.running=t.running,r.error=t.error,o.info(JSON.stringify(r,null,2)),process.exit(0)}catch(t){let n=new ie(t);o.error($,{command:Q,code:n.code,scope:e.scope,cause:n.cause}),process.exit(1)}}),oe=new y(`stop`).description(`Stop the background HTTP server`).action(async()=>{try{let e=await new k().stop();console.log(e?`HTTP server stopped`:`No HTTP server running`),process.exit(0)}catch(e){console.error(`Failed to stop HTTP server:`,e),process.exit(1)}}),se=d(_(import.meta.url)),ce=JSON.parse(g(f(se,`../package.json`),`utf-8`));async function le(){let e=new y;e.name(`model-proxy-mcp`).description(`Claude-compatible model proxy MCP`).version(ce.version),e.addCommand(re),e.addCommand(oe),e.addCommand(ae),e.addCommand(K),e.addCommand(Z),e.addCommand(ne),await e.parseAsync(process.argv)}le();export{};
|
|
2
|
+
import{a as e,c as t,i as n,l as r,n as i,r as a,s as o,t as s,u as c}from"./stdio-BZfhbEP-.mjs";import{serve as l}from"@hono/node-server";import u,{dirname as d,join as f}from"node:path";import{randomBytes as p,randomUUID as m}from"node:crypto";import{existsSync as h,readFileSync as g}from"node:fs";import{fileURLToPath as _}from"node:url";import v from"node:fs/promises";import{Command as y}from"commander";import{execFile as ee,spawn as b}from"node:child_process";import{promisify as x}from"node:util";import{PortRegistryService as S}from"@agimon-ai/foundation-port-registry";import{createProcessLease as C}from"@agimon-ai/foundation-process-registry";var w=class{async check(e){try{let t=await fetch(`http://127.0.0.1:${e}/health`);return t.ok?{healthy:!0,serviceName:(await t.json()).service}:{healthy:!1,error:`Health check failed with status ${t.status}`}}catch(e){return{healthy:!1,error:e instanceof Error?e.message:String(e)}}}};const T=[`pnpm-workspace.yaml`,`nx.json`,`.git`],E=`service`,D=x(ee);function O(e=process.cwd()){let t=u.resolve(e);for(;;){for(let e of T)if(h(u.join(t,e)))return t;let e=u.dirname(t);if(e===t)return process.cwd();t=e}}var k=class{repositoryPath=O(process.cwd());serviceName=r;portRegistry;constructor(e=new w){this.healthCheck=e,this.portRegistry=new S(process.env.PORT_REGISTRY_PATH)}createEmptyStatus(e={}){return{running:!1,scope:`default`,activeProfileId:null,auth:{configured:!1,authFilePath:``},profiles:[],slotModels:{},...e}}async getRegistration(){let e=await this.portRegistry.getPort({repositoryPath:this.repositoryPath,serviceName:this.serviceName,serviceType:E});return!e.success||!e.record?null:{port:e.record.port,pid:e.record.pid}}async releaseService(e){await this.portRegistry.releasePort({repositoryPath:this.repositoryPath,serviceName:this.serviceName,serviceType:E,pid:e})}async listListeningProcesses(e){try{let{stdout:t}=await D(`sh`,[`-lc`,`lsof -n -P -sTCP:LISTEN -iTCP:${e} || true`],{encoding:`utf8`});return t.split(`
|
|
3
|
+
`).slice(1).map(e=>e.trim()).filter(Boolean).map(e=>e.split(/\s+/)).map(t=>({pid:Number.parseInt(t[1]||``,10),port:e})).filter(e=>Number.isInteger(e.pid)&&e.pid>0)}catch{return[]}}async findHealthyServicePort(){let e=await this.getRegistration();if(e){let t=await this.healthCheck.check(e.port);if(t.healthy&&t.serviceName===this.serviceName)return e.port}return null}async clearPortListeners(e,t){let n=await this.listListeningProcesses(e);for(let e of n)t&&e.pid===t||await this.killProcess(e.pid)}async registerService(e,t){await this.portRegistry.reservePort({repositoryPath:this.repositoryPath,serviceName:this.serviceName,serviceType:E,preferredPort:e,portRange:{min:e,max:e},pid:t,host:`127.0.0.1`,force:!0})}async findAvailablePort(e){let t=await this.portRegistry.findAvailablePort({repositoryPath:this.repositoryPath,serviceName:this.serviceName,serviceType:E,host:`127.0.0.1`,preferredPort:e,portRange:c});if(!t.success||t.port===void 0)throw Error(`No available port found from ${e}`);return t.port}async fileExists(e){try{return await v.access(e),!0}catch{return!1}}async resolveCliPath(){let e=u.dirname(_(import.meta.url)),t=u.resolve(e,`cli.mjs`);if(await this.fileExists(t))return{cliPath:t,runtime:`node`};let n=u.resolve(e,`..`,`cli.mjs`);if(await this.fileExists(n))return{cliPath:n,runtime:`node`};let r=u.join(this.repositoryPath,`packages`,`mcp`,`model-proxy-mcp`,`dist`,`cli.mjs`);if(await this.fileExists(r))return{cliPath:r,runtime:`node`};for(let t of[u.resolve(e,`cli.ts`),u.resolve(e,`..`,`cli.ts`)])if(await this.fileExists(t))return{cliPath:t,runtime:`bun`};let i=u.join(this.repositoryPath,`packages`,`mcp`,`model-proxy-mcp`,`src`,`cli.ts`);if(await this.fileExists(i))return{cliPath:i,runtime:`bun`};throw Error(`Cannot find model-proxy-mcp CLI`)}async startHttpServer(e){let{cliPath:t,runtime:n}=await this.resolveCliPath(),r=b(n===`bun`?process.versions.bun?process.execPath:`bun`:`node`,n===`bun`?[`run`,t,`http-serve`,`--port`,String(e)]:[t,`http-serve`,`--port`,String(e)],{detached:!0,stdio:`ignore`,env:{...process.env,NODE_ENV:process.env.NODE_ENV||`development`}});if(r.unref(),!r.pid)throw Error(`Failed to spawn HTTP server`);return r.pid}async killProcess(e){try{process.kill(e,`SIGTERM`),await new Promise(e=>setTimeout(e,1e3));try{process.kill(e,0),process.kill(e,`SIGKILL`)}catch{return}}catch{return}}async waitForHealthy(e){let t=Date.now()+5e3,n=`Server failed health check after startup`;for(;Date.now()<t;){let t=await this.healthCheck.check(e);if(t.healthy&&t.serviceName===this.serviceName)return{healthy:!0};n=t.error||`Unexpected service on port ${e}`,await new Promise(e=>setTimeout(e,250))}return{healthy:!1,error:n}}async ensureRunning(e=t){try{let t=await this.findHealthyServicePort();if(t!==null){let e=(await this.listListeningProcesses(t))[0]?.pid;return await this.registerService(t,e??process.pid),this.createEmptyStatus({running:!0,port:t,pid:e})}let n=await this.getRegistration();if(n){let e=await this.healthCheck.check(n.port);if(e.healthy&&e.serviceName===this.serviceName)return this.createEmptyStatus({running:!0,port:n.port,pid:n.pid});n.pid&&await this.killProcess(n.pid),await this.clearPortListeners(n.port,n.pid),await this.releaseService(n.pid)}await this.clearPortListeners(e);let r=await this.findAvailablePort(e),i=await this.startHttpServer(r),a=await this.waitForHealthy(r);return a.healthy?(await this.registerService(r,i),this.createEmptyStatus({running:!0,port:r,pid:i})):(await this.killProcess(i),await this.clearPortListeners(r,i),await this.releaseService(i),this.createEmptyStatus({error:a.error||`Server failed health check after startup`}))}catch(e){return this.createEmptyStatus({error:e instanceof Error?e.message:String(e)})}}startHealthMonitor(e,t=1e4){let n=!1,r=0,i=2e3,a=6e4,o=async()=>{if(!n)try{if((await this.getStatus()).running)r=0,n||setTimeout(()=>void o(),t);else{r++;let t=Math.min(i*2**(r-1),a);await this.ensureRunning(e),n||setTimeout(()=>void o(),t)}}catch{r++;let e=Math.min(i*2**(r-1),a);n||setTimeout(()=>void o(),e)}};return setTimeout(()=>void o(),t),()=>{n=!0}}async stop(){let e=await this.getRegistration(),t=!1;e?.pid&&(await this.killProcess(e.pid),t=!0),e&&(await this.clearPortListeners(e.port,e.pid),await this.releaseService(e.pid));let n=await this.findHealthyServicePort();if(n!==null){let e=await this.listListeningProcesses(n);for(let n of e)await this.killProcess(n.pid),t=!0;await this.releaseService()}return t}async getStatus(){let e=await this.getRegistration();if(e&&(await this.healthCheck.check(e.port)).healthy)return this.createEmptyStatus({running:!0,port:e.port,pid:e.pid});let t=await this.findHealthyServicePort();if(t!==null){let e=(await this.listListeningProcesses(t))[0]?.pid;return e&&await this.registerService(t,e),this.createEmptyStatus({running:!0,port:t,pid:e})}return this.createEmptyStatus({error:e?`Registered server is unhealthy`:`No HTTP server registered`})}};const A=`claude`,j=`utf8`,M=`[model-proxy-mcp]`,te=`Recovery: verify HOME, proxy port, and that \`${A}\` is on PATH.`;var N=class extends Error{constructor(e,t,n){super(e,n),this.code=t,this.name=`ClaudeCommandError`}},P=class extends N{constructor(e,t){super(e,`CLAUDE_COMMAND_CONFIG_ERROR`,t),this.name=`ClaudeCommandConfigError`}},F=class extends N{constructor(e,t){super(e,`CLAUDE_COMMAND_VALIDATION_ERROR`,t),this.name=`ClaudeCommandValidationError`}},I=class extends N{constructor(e,t){super(e,`CLAUDE_COMMAND_LAUNCH_ERROR`,t),this.name=`ClaudeCommandLaunchError`}};function L(e){return e.trim().replace(/[^a-zA-Z0-9._-]+/g,`-`).replace(/^-+|-+$/g,``)||`default`}function R(){return`scope-${p(3).toString(`hex`)}`}function z(e,t,n=process.env){return{...n,ANTHROPIC_BASE_URL:`http://127.0.0.1:${e}/scopes/${t}`,ANTHROPIC_DEFAULT_OPUS_MODEL:n.ANTHROPIC_DEFAULT_OPUS_MODEL||`ccproxy-opus`,ANTHROPIC_DEFAULT_SONNET_MODEL:n.ANTHROPIC_DEFAULT_SONNET_MODEL||`ccproxy-sonnet`,ANTHROPIC_DEFAULT_HAIKU_MODEL:n.ANTHROPIC_DEFAULT_HAIKU_MODEL||`ccproxy-haiku`,CLAUDE_CODE_SUBAGENT_MODEL:n.CLAUDE_CODE_SUBAGENT_MODEL||`ccproxy-subagent`,MODEL_PROXY_MCP_SCOPE:n.MODEL_PROXY_MCP_SCOPE||t,MODEL_PROXY_MCP_SLOT:n.MODEL_PROXY_MCP_SLOT||`default`}}async function B(e,t=process.cwd()){let n=e?u.resolve(t,e):u.join(t,`model-proxy-mcp.yaml`);try{if(!(await v.stat(n)).isFile()){if(e)throw new P(`Scope seed config path is not a file: ${n}`);return}return n}catch(t){if(t.code===`ENOENT`){if(e)throw new P(`Scope seed config file not found: ${n}`,{cause:t});return}throw t instanceof P?t:new P(`Failed to read scope seed config file: ${n}`,{cause:t})}}function V(){let e=process.env.HOME||process.env.USERPROFILE;if(!e)throw new P(`HOME or USERPROFILE is not set`);return u.join(e,`.claude-sessions`)}async function H(){let e=V();return await v.mkdir(e,{recursive:!0}),e}async function U(e){try{return(await v.readFile(e,j)).trim()||null}catch(t){if(t.code===`ENOENT`)return null;throw new P(`Failed to read Claude session file: ${e}`,{cause:t})}}async function W(e,t){let n=await H(),r=u.join(n,e);if(t)try{await v.rm(r,{force:!0})}catch(e){throw new P(`Failed to clear Claude session file: ${r}`,{cause:e})}let i=await U(r);if(i)return{sessionId:i,resume:!0};let a=m().toLowerCase();try{await v.writeFile(r,`${a}\n`,j)}catch(e){throw new P(`Failed to write Claude session file: ${r}`,{cause:e})}return{sessionId:a,resume:!1}}async function G(t,n,r,i,a){let o=await B(a);await new e().ensureConfig(t,o);let s=await new k().ensureRunning(n);if(!s.running||!s.port)throw new I(s.error||`Failed to start model proxy HTTP server`);let{sessionId:c,resume:l}=await W(t,r),u=z(s.port,t),d=[`--dangerously-skip-permissions`,...l?[`--resume`,c]:[`--session-id`,c],...i];return console.log(`${M} Scope: ${t}`),console.log(`${M} Proxy: ${u.ANTHROPIC_BASE_URL}`),console.log(`${M} Session: ${c}${l?` (resume)`:` (new)`}`),o&&console.log(`${M} Scope config seed: ${o}`),new Promise((e,t)=>{let n=b(A,d,{stdio:`inherit`,env:u});n.on(`error`,e=>{t(new I(`Failed to launch ${A}`,{cause:e}))}),n.on(`exit`,(n,r)=>{if(r){t(new I(`${A} exited with signal ${r}`));return}e(n??0)})})}const K=new y(`claude`).description(`Launch Claude Code through the model proxy`).option(`-s, --scope <scope>`,`Proxy scope to use`).option(`-p, --port <port>`,`Preferred HTTP port for the proxy`,String(t)).option(`-c, --config-file <path>`,`Seed a new scope from the specified scope config file`).option(`--config <path>`,`Alias for --config-file`).option(`--clear-session`,`Start with a fresh Claude session for the selected scope`,!1).allowUnknownOption(!0).allowExcessArguments(!0).argument(`[claudeArgs...]`).action(async(e,t)=>{try{let n=L(t.scope||R()),r=Number.parseInt(t.port,10);if(!Number.isInteger(r)||r<=0)throw new F(`Invalid port: ${t.port}`);let i=await G(n,r,t.clearSession,e,t.configFile??t.config);process.exit(i)}catch(e){let t=e instanceof N?e:new I(`Failed to launch Claude`,{cause:e});console.error(`${M} ${t.code}: ${t.message}`),console.error(`${M} ${te}`),t.cause&&console.error(`${M} Cause:`,t.cause),process.exit(1)}}),q=`127.0.0.1`,J=[`pnpm-workspace.yaml`,`nx.json`,`.git`],Y=`service`;function X(e=process.cwd()){let t=u.resolve(e);for(;;){for(let e of J)if(h(u.join(t,e)))return t;let e=u.dirname(t);if(e===t)return process.cwd();t=e}}const Z=new y(`http-serve`).description(`Start the Claude-compatible HTTP model proxy server`).option(`-p, --port <port>`,`Port to listen on`,String(t)).action(async e=>{let t;try{let i=Number.parseInt(e.port,10);if(!Number.isInteger(i)||i<=0)throw Error(`Invalid port: ${e.port}`);let o=new S(process.env.PORT_REGISTRY_PATH),s=X(),u=await o.reservePort({repositoryPath:s,serviceName:r,serviceType:Y,preferredPort:i,portRange:c,pid:process.pid,host:q,force:!0});if(!u.success||!u.record)throw Error(u.error||`Failed to reserve port ${i}`);let d=u.record.port;t=await C({repositoryPath:s,serviceName:r,serviceType:Y,pid:process.pid,port:d,host:q,command:process.argv[1],args:process.argv.slice(2)});let f=new n;await f.ensureConfig();let p=l({fetch:a(f).fetch,port:d,hostname:q});console.log(`model-proxy-mcp listening on http://${q}:${d}`),console.log(`Claude base URL: http://${q}:${d}`);let m=async e=>{console.log(`\n${e} received. Shutting down...`),p.close(),await t?.release(),await o.releasePort({repositoryPath:s,serviceName:r,serviceType:Y,pid:process.pid}),process.exit(0)};process.once(`SIGINT`,()=>m(`SIGINT`)),process.once(`SIGTERM`,()=>m(`SIGTERM`))}catch(e){await t?.release(),console.error(`Failed to start HTTP server:`,e),process.exit(1)}}),ne=new y(`mcp-serve`).description(`Start MCP server with stdio transport`).option(`--cleanup`,`Stop HTTP server on shutdown`,!1).option(`-p, --port <port>`,`Port for HTTP server`,String(t)).action(async e=>{try{let t=Number.parseInt(e.port,10);if(!Number.isInteger(t)||t<=0)throw Error(`Invalid port: ${e.port}`);let r=new n;await r.ensureConfig(process.env.MODEL_PROXY_MCP_SCOPE||`default`);let a=new k,o=await a.ensureRunning(t);o.running||console.error(`Warning: HTTP server failed to start: ${o.error}`);let c;o.running&&(c=a.startHealthMonitor(t));let l=new s(()=>i(r)),u=async t=>{console.error(`\nReceived ${t}, shutting down gracefully...`),c?.(),await l.stop(),e.cleanup&&o.running&&await a.stop(),process.exit(0)};process.once(`SIGINT`,()=>void u(`SIGINT`).catch(e=>{console.error(`Failed to shut down after SIGINT:`,e),process.exit(1)})),process.once(`SIGTERM`,()=>void u(`SIGTERM`).catch(e=>{console.error(`Failed to shut down after SIGTERM:`,e),process.exit(1)})),await l.start()}catch(e){console.error(`Failed to start MCP server:`,e),process.exit(1)}}),re=new y(`start`).description(`Start HTTP and/or MCP server for the model proxy`).option(`--mcp-only`,`Start only the MCP server`,!1).option(`--http-only`,`Start only the HTTP server`,!1).option(`-p, --port <port>`,`Port for HTTP server`,String(t)).action(async e=>{try{let t=new k,n=Number.parseInt(e.port,10),r=!e.mcpOnly,a=!e.httpOnly;if(r){let e=await t.ensureRunning(n);e.running||(console.error(`HTTP server failed to start: ${e.error}`),process.exit(1)),a?console.error(`HTTP server running on http://127.0.0.1:${e.port}`):console.log(`HTTP server running on http://127.0.0.1:${e.port}`)}if(a){let e=new s(()=>i());await e.start();let t=async t=>{console.error(`\n${t} received. Shutting down...`),await e.stop(),process.exit(0)};process.on(`SIGINT`,()=>t(`SIGINT`)),process.on(`SIGTERM`,()=>t(`SIGTERM`))}else process.exit(0)}catch(e){console.error(`Failed to start services:`,e),process.exit(1)}}),Q=`status`,$=`Failed to read status`;var ie=class extends Error{code=`STATUS_CHECK_FAILED`;constructor(e){super($,{cause:e instanceof Error?e:Error(String(e))}),this.name=`StatusCommandError`}};const ae=new y(Q).description(`Show proxy server and profile status`).option(`-s, --scope <scope>`,`Configuration scope`,`default`).action(async e=>{try{let t=await new k().getStatus(),r=await new n().getStatus(e.scope,t.port,t.pid);r.running=t.running,r.error=t.error,o.info(JSON.stringify(r,null,2)),process.exit(0)}catch(t){let n=new ie(t);o.error($,{command:Q,code:n.code,scope:e.scope,cause:n.cause}),process.exit(1)}}),oe=new y(`stop`).description(`Stop the background HTTP server`).action(async()=>{try{let e=await new k().stop();console.log(e?`HTTP server stopped`:`No HTTP server running`),process.exit(0)}catch(e){console.error(`Failed to stop HTTP server:`,e),process.exit(1)}}),se=d(_(import.meta.url)),ce=JSON.parse(g(f(se,`../package.json`),`utf-8`));async function le(){let e=new y;e.name(`model-proxy-mcp`).description(`Claude-compatible model proxy MCP`).version(ce.version),e.addCommand(re),e.addCommand(oe),e.addCommand(ae),e.addCommand(K),e.addCommand(Z),e.addCommand(ne),await e.parseAsync(process.argv)}le();export{};
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./stdio-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./stdio-pd8A9DFx.cjs`);exports.ConversationHistoryService=e.o,exports.GatewayService=e.i,exports.ProfileStore=e.a,exports.StdioTransportHandler=e.t,exports.createHttpServer=e.r,exports.createServer=e.n;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
import * as _$hono_types0 from "hono/types";
|
|
2
|
+
import { Hono } from "hono";
|
|
3
|
+
import { Server } from "@modelcontextprotocol/server";
|
|
4
|
+
|
|
5
|
+
//#region src/types/index.d.ts
|
|
6
|
+
interface LoggerLike {
|
|
7
|
+
info: (msg: string, data?: unknown) => void;
|
|
8
|
+
error: (msg: string, error?: unknown) => void;
|
|
9
|
+
debug: (msg: string, data?: unknown) => void;
|
|
10
|
+
warn: (msg: string, data?: unknown) => void;
|
|
11
|
+
}
|
|
12
|
+
type ReasoningEffort = 'minimal' | 'low' | 'medium' | 'high';
|
|
13
|
+
type ProxyProviderType = 'chatgpt-codex' | 'anthropic-compatible' | 'gemini-direct';
|
|
14
|
+
type GeminiAuthMode = 'auto' | 'api-key' | 'oauth';
|
|
15
|
+
type ModelSlot = 'default' | 'sonnet' | 'opus' | 'haiku' | 'subagent';
|
|
16
|
+
interface ProviderRuntimeConfig {
|
|
17
|
+
type: ProxyProviderType;
|
|
18
|
+
endpoint: string;
|
|
19
|
+
authTokenEnvVar?: string | null;
|
|
20
|
+
apiTimeoutMs?: number | null;
|
|
21
|
+
authMode?: GeminiAuthMode | null;
|
|
22
|
+
apiKeyEnvVar?: string | null;
|
|
23
|
+
project?: string | null;
|
|
24
|
+
location?: string | null;
|
|
25
|
+
apiVersion?: string | null;
|
|
26
|
+
}
|
|
27
|
+
interface ProxyProfile {
|
|
28
|
+
id: string;
|
|
29
|
+
label: string;
|
|
30
|
+
provider: string;
|
|
31
|
+
providerType: ProxyProviderType | null;
|
|
32
|
+
model: string;
|
|
33
|
+
endpoint: string | null;
|
|
34
|
+
reasoningEffort: ReasoningEffort;
|
|
35
|
+
enabled: boolean;
|
|
36
|
+
}
|
|
37
|
+
interface ProxyModelConfig {
|
|
38
|
+
id: string;
|
|
39
|
+
label: string;
|
|
40
|
+
provider: string;
|
|
41
|
+
model: string;
|
|
42
|
+
reasoningEffort: ReasoningEffort;
|
|
43
|
+
enabled: boolean;
|
|
44
|
+
}
|
|
45
|
+
interface ScopeModelSelection {
|
|
46
|
+
provider: string;
|
|
47
|
+
model: string;
|
|
48
|
+
reasoningEffort: ReasoningEffort;
|
|
49
|
+
thinkingDisabled?: boolean;
|
|
50
|
+
}
|
|
51
|
+
interface ScopeSlotConfig {
|
|
52
|
+
main: ScopeModelSelection;
|
|
53
|
+
fallbacks: ScopeModelSelection[];
|
|
54
|
+
}
|
|
55
|
+
interface ScopeSettings {
|
|
56
|
+
models: Partial<Record<ModelSlot, ScopeSlotConfig>>;
|
|
57
|
+
}
|
|
58
|
+
interface ProxySettings {
|
|
59
|
+
providers: Record<string, ProviderRuntimeConfig>;
|
|
60
|
+
models: ProxyModelConfig[];
|
|
61
|
+
scope: ScopeSettings;
|
|
62
|
+
}
|
|
63
|
+
interface ScopeConfig {
|
|
64
|
+
activeProfileId: string | null;
|
|
65
|
+
slotProfileIds: Partial<Record<ModelSlot, string | null>>;
|
|
66
|
+
providers: Record<string, ProviderRuntimeConfig>;
|
|
67
|
+
profiles: ProxyProfile[];
|
|
68
|
+
scope: ScopeSettings;
|
|
69
|
+
}
|
|
70
|
+
type ProxyConfig = ScopeConfig;
|
|
71
|
+
interface ResolvedSlotConfig {
|
|
72
|
+
slot: ModelSlot;
|
|
73
|
+
profileId: string | null;
|
|
74
|
+
label: string | null;
|
|
75
|
+
provider: string | null;
|
|
76
|
+
providerType: ProxyProviderType | null;
|
|
77
|
+
endpoint: string | null;
|
|
78
|
+
model: string | null;
|
|
79
|
+
reasoningEffort: ReasoningEffort;
|
|
80
|
+
thinkingDisabled?: boolean;
|
|
81
|
+
fallbacks: ScopeModelSelection[];
|
|
82
|
+
}
|
|
83
|
+
interface AdminConfig {
|
|
84
|
+
scope: string;
|
|
85
|
+
providerConfigPath: string;
|
|
86
|
+
modelListPath: string;
|
|
87
|
+
scopeConfigPath: string;
|
|
88
|
+
providers: Record<string, ProviderRuntimeConfig>;
|
|
89
|
+
models: ProxyProfile[];
|
|
90
|
+
scopeModels: ScopeSettings['models'];
|
|
91
|
+
slots: Record<ModelSlot, ResolvedSlotConfig>;
|
|
92
|
+
}
|
|
93
|
+
interface GatewayStatus {
|
|
94
|
+
running: boolean;
|
|
95
|
+
port?: number;
|
|
96
|
+
pid?: number;
|
|
97
|
+
scope: string;
|
|
98
|
+
activeProfileId: string | null;
|
|
99
|
+
activeModel?: string;
|
|
100
|
+
activeReasoningEffort?: ReasoningEffort;
|
|
101
|
+
slotModels: Partial<Record<ModelSlot, {
|
|
102
|
+
profileId: string | null;
|
|
103
|
+
provider: string | null;
|
|
104
|
+
model: string | null;
|
|
105
|
+
reasoningEffort: ReasoningEffort;
|
|
106
|
+
thinkingDisabled?: boolean;
|
|
107
|
+
}>>;
|
|
108
|
+
auth: {
|
|
109
|
+
configured: boolean;
|
|
110
|
+
accountId?: string | null;
|
|
111
|
+
authFilePath: string;
|
|
112
|
+
lastRefresh?: string | null;
|
|
113
|
+
};
|
|
114
|
+
profiles: ProxyProfile[];
|
|
115
|
+
error?: string;
|
|
116
|
+
}
|
|
117
|
+
interface AdminConfigUpdate {
|
|
118
|
+
models?: Partial<Record<ModelSlot, ScopeSlotConfig | null>>;
|
|
119
|
+
}
|
|
120
|
+
interface ConversationHistoryEntry {
|
|
121
|
+
id: string;
|
|
122
|
+
scope: string;
|
|
123
|
+
requestId: string;
|
|
124
|
+
direction: 'request' | 'response' | 'error';
|
|
125
|
+
role: string | null;
|
|
126
|
+
messageType: string;
|
|
127
|
+
model: string | null;
|
|
128
|
+
slot: ModelSlot;
|
|
129
|
+
payloadJson: string;
|
|
130
|
+
createdAt: string;
|
|
131
|
+
}
|
|
132
|
+
interface ConversationHistoryPage {
|
|
133
|
+
items: ConversationHistoryEntry[];
|
|
134
|
+
nextCursor: string | null;
|
|
135
|
+
total: number;
|
|
136
|
+
}
|
|
137
|
+
interface ConversationHistoryStats {
|
|
138
|
+
scope: string;
|
|
139
|
+
retentionLimit: number;
|
|
140
|
+
totalMessages: number;
|
|
141
|
+
oldestCreatedAt: string | null;
|
|
142
|
+
newestCreatedAt: string | null;
|
|
143
|
+
}
|
|
144
|
+
interface TransportHandler {
|
|
145
|
+
start(): Promise<void>;
|
|
146
|
+
stop(): Promise<void>;
|
|
147
|
+
}
|
|
148
|
+
//#endregion
|
|
149
|
+
//#region src/adapters/codex/CodexAuth.d.ts
|
|
150
|
+
interface ResolvedCodexAuth {
|
|
151
|
+
accessToken: string | null;
|
|
152
|
+
accountId: string | null;
|
|
153
|
+
}
|
|
154
|
+
declare class CodexAuth {
|
|
155
|
+
private readonly logger;
|
|
156
|
+
private readonly authFilePath;
|
|
157
|
+
private static readonly TOKEN_REFRESH_URL;
|
|
158
|
+
private static readonly CLIENT_ID;
|
|
159
|
+
constructor(logger?: LoggerLike, authFilePath?: string);
|
|
160
|
+
getAuthFilePath(): string;
|
|
161
|
+
getAuthStatus(): Promise<{
|
|
162
|
+
configured: boolean;
|
|
163
|
+
accountId?: string | null;
|
|
164
|
+
authFilePath: string;
|
|
165
|
+
lastRefresh?: string | null;
|
|
166
|
+
}>;
|
|
167
|
+
getAccountId(): Promise<string | null>;
|
|
168
|
+
getAccessToken(): Promise<string | null>;
|
|
169
|
+
resolveAuth(): Promise<ResolvedCodexAuth>;
|
|
170
|
+
private readAuthFile;
|
|
171
|
+
private isTokenExpired;
|
|
172
|
+
private decodeJWT;
|
|
173
|
+
private refreshAccessToken;
|
|
174
|
+
private saveTokens;
|
|
175
|
+
}
|
|
176
|
+
//#endregion
|
|
177
|
+
//#region src/services/ConversationHistoryService.d.ts
|
|
178
|
+
type NewHistoryEntry = Omit<ConversationHistoryEntry, 'id' | 'createdAt'> & {
|
|
179
|
+
id?: string;
|
|
180
|
+
createdAt?: string;
|
|
181
|
+
};
|
|
182
|
+
/**
|
|
183
|
+
* Error raised for conversation history persistence failures.
|
|
184
|
+
*/
|
|
185
|
+
/**
|
|
186
|
+
* Persists scoped conversation history using SQLite and enforces bounded retention.
|
|
187
|
+
*
|
|
188
|
+
* Environment variables:
|
|
189
|
+
* - `MODEL_PROXY_MCP_DB_PATH`
|
|
190
|
+
*/
|
|
191
|
+
declare class ConversationHistoryService {
|
|
192
|
+
private readonly dbPath;
|
|
193
|
+
private readonly retentionLimit;
|
|
194
|
+
private readonly logger;
|
|
195
|
+
private sqlite;
|
|
196
|
+
/**
|
|
197
|
+
* Creates a SQLite-backed history store.
|
|
198
|
+
*
|
|
199
|
+
* `dbPath` defaults to `MODEL_PROXY_MCP_DB_PATH` or `~/.model-proxy/history.sqlite`.
|
|
200
|
+
* `retentionLimit` is enforced per scope after each insert batch.
|
|
201
|
+
* `logger` receives structured operational failures.
|
|
202
|
+
*/
|
|
203
|
+
constructor(dbPath?: string, retentionLimit?: number, logger?: LoggerLike);
|
|
204
|
+
/**
|
|
205
|
+
* Eagerly initializes the SQLite database and schema.
|
|
206
|
+
*/
|
|
207
|
+
ensureInitialized(): Promise<void>;
|
|
208
|
+
/**
|
|
209
|
+
* Inserts normalized history entries and prunes overflow per scope.
|
|
210
|
+
*/
|
|
211
|
+
appendEntries(entries: NewHistoryEntry[]): Promise<void>;
|
|
212
|
+
/**
|
|
213
|
+
* Lists conversation history for a scope ordered from newest to oldest.
|
|
214
|
+
*/
|
|
215
|
+
listHistory(scope: string, limit?: number, cursor?: string): Promise<ConversationHistoryPage>;
|
|
216
|
+
/**
|
|
217
|
+
* Clears all history rows for a single scope and returns the deleted count.
|
|
218
|
+
*/
|
|
219
|
+
clearScope(scope: string): Promise<number>;
|
|
220
|
+
/**
|
|
221
|
+
* Returns retention stats for a scope.
|
|
222
|
+
*/
|
|
223
|
+
getStats(scope: string): Promise<ConversationHistoryStats>;
|
|
224
|
+
/**
|
|
225
|
+
* Lists known scopes discovered from stored conversation history.
|
|
226
|
+
*/
|
|
227
|
+
listScopes(): Promise<string[]>;
|
|
228
|
+
/**
|
|
229
|
+
* Removes the oldest rows for a scope when it exceeds retention.
|
|
230
|
+
*/
|
|
231
|
+
private pruneScope;
|
|
232
|
+
/**
|
|
233
|
+
* Lazily initializes the SQLite connection and schema.
|
|
234
|
+
*/
|
|
235
|
+
private getDb;
|
|
236
|
+
/**
|
|
237
|
+
* Maps a SQLite row into the public history DTO.
|
|
238
|
+
*/
|
|
239
|
+
private toEntry;
|
|
240
|
+
}
|
|
241
|
+
//#endregion
|
|
242
|
+
//#region src/services/ProfileStore.d.ts
|
|
243
|
+
declare class ProfileStore {
|
|
244
|
+
private readonly providerConfigPath;
|
|
245
|
+
private readonly modelListPath;
|
|
246
|
+
private readonly scopeSettingsDir;
|
|
247
|
+
private readonly logger;
|
|
248
|
+
constructor(providerConfigPath?: string, modelListPath?: string, scopeSettingsDir?: string, logger?: LoggerLike);
|
|
249
|
+
getConfigPath(scope?: string): string;
|
|
250
|
+
ensureConfig(scope?: string, seedConfigPath?: string): Promise<ProxyConfig>;
|
|
251
|
+
listScopes(): Promise<string[]>;
|
|
252
|
+
getConfig(scope?: string, seedConfigPath?: string): Promise<ProxyConfig>;
|
|
253
|
+
getAdminConfig(scope?: string): Promise<AdminConfig>;
|
|
254
|
+
listProfiles(scope?: string): Promise<ProxyProfile[]>;
|
|
255
|
+
getActiveProfile(scope?: string, slot?: ModelSlot): Promise<ProxyProfile | null>;
|
|
256
|
+
getResolvedSlotConfig(scope?: string, slot?: ModelSlot): Promise<ResolvedSlotConfig>;
|
|
257
|
+
setActiveProfile(profileId: string, scope?: string, slot?: ModelSlot): Promise<ProxyConfig>;
|
|
258
|
+
upsertProfile(profile: ProxyProfile, scope?: string): Promise<ProxyConfig>;
|
|
259
|
+
updateConfig(update: AdminConfigUpdate, scope?: string): Promise<ProxyConfig>;
|
|
260
|
+
private getScopeConfigPath;
|
|
261
|
+
private sanitizeScope;
|
|
262
|
+
private getSettings;
|
|
263
|
+
private readSettings;
|
|
264
|
+
private isFileNotFoundError;
|
|
265
|
+
private loadScopeSeedSettings;
|
|
266
|
+
private getDefaultScopeSeedPath;
|
|
267
|
+
private normalizeSettings;
|
|
268
|
+
private normalizeProviders;
|
|
269
|
+
private getDefaultSelection;
|
|
270
|
+
private normalizeSlotSelection;
|
|
271
|
+
private normalizeFallbacks;
|
|
272
|
+
private toProxyConfig;
|
|
273
|
+
private resolveSlotConfig;
|
|
274
|
+
private toProfiles;
|
|
275
|
+
private saveSettings;
|
|
276
|
+
}
|
|
277
|
+
//#endregion
|
|
278
|
+
//#region src/services/GatewayService.d.ts
|
|
279
|
+
interface ForwardedResponse {
|
|
280
|
+
status: number;
|
|
281
|
+
body: string;
|
|
282
|
+
headers: Record<string, string>;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Coordinates settings-backed model selection, request forwarding, and scoped history persistence.
|
|
286
|
+
*/
|
|
287
|
+
declare class GatewayService {
|
|
288
|
+
private readonly profileStore;
|
|
289
|
+
private readonly codexAuth;
|
|
290
|
+
private readonly logger;
|
|
291
|
+
private readonly fetchImpl;
|
|
292
|
+
private readonly historyService;
|
|
293
|
+
private readonly codeAssistProjectCache;
|
|
294
|
+
constructor(profileStore?: ProfileStore, codexAuth?: CodexAuth, logger?: LoggerLike, fetchImpl?: typeof fetch, historyService?: ConversationHistoryService);
|
|
295
|
+
/**
|
|
296
|
+
* Lists all configured profiles for the given scope.
|
|
297
|
+
*/
|
|
298
|
+
listProfiles(scope?: string): Promise<ProxyProfile[]>;
|
|
299
|
+
/**
|
|
300
|
+
* Eagerly seeds the scoped settings file so startup always has a backing config.
|
|
301
|
+
*/
|
|
302
|
+
ensureConfig(scope?: string): Promise<Awaited<ReturnType<ProfileStore['ensureConfig']>>>;
|
|
303
|
+
/**
|
|
304
|
+
* Returns the resolved admin config used by the live HTTP server and UI.
|
|
305
|
+
*/
|
|
306
|
+
getAdminConfig(scope?: string): Promise<AdminConfig>;
|
|
307
|
+
/**
|
|
308
|
+
* Updates the resolved admin config.
|
|
309
|
+
*/
|
|
310
|
+
updateAdminConfig(update: AdminConfigUpdate, scope?: string): Promise<AdminConfig>;
|
|
311
|
+
/**
|
|
312
|
+
* Returns the currently selected profile for a scope/slot pair.
|
|
313
|
+
*/
|
|
314
|
+
getActiveProfile(scope?: string, slot?: ModelSlot): Promise<ProxyProfile | null>;
|
|
315
|
+
/**
|
|
316
|
+
* Creates or updates a profile in the scoped settings file.
|
|
317
|
+
*/
|
|
318
|
+
upsertProfile(profile: ProxyProfile, scope?: string): Promise<Awaited<ReturnType<ProfileStore['upsertProfile']>>>;
|
|
319
|
+
/**
|
|
320
|
+
* Selects the active profile by profile id for the requested slot.
|
|
321
|
+
*/
|
|
322
|
+
setActiveProfile(profileId: string, scope?: string, slot?: ModelSlot): Promise<Awaited<ReturnType<ProfileStore['setActiveProfile']>>>;
|
|
323
|
+
/**
|
|
324
|
+
* Returns the active profile currently used for routing requests.
|
|
325
|
+
*/
|
|
326
|
+
getCurrentModel(scope?: string, slot?: ModelSlot): Promise<ProxyProfile | null>;
|
|
327
|
+
/**
|
|
328
|
+
* Switches the selected model by model name.
|
|
329
|
+
*/
|
|
330
|
+
switchModel(model: string, scope?: string, slot?: ModelSlot): Promise<Awaited<ReturnType<ProfileStore['updateConfig']>>>;
|
|
331
|
+
/**
|
|
332
|
+
* Lists persisted history for a scope.
|
|
333
|
+
*/
|
|
334
|
+
listHistory(scope?: string, limit?: number, cursor?: string): Promise<ConversationHistoryPage>;
|
|
335
|
+
/**
|
|
336
|
+
* Clears persisted history for a scope.
|
|
337
|
+
*/
|
|
338
|
+
clearHistory(scope?: string): Promise<{
|
|
339
|
+
deleted: number;
|
|
340
|
+
}>;
|
|
341
|
+
/**
|
|
342
|
+
* Lists known scopes from config files and stored history.
|
|
343
|
+
*/
|
|
344
|
+
listScopes(): Promise<string[]>;
|
|
345
|
+
/**
|
|
346
|
+
* Returns persisted history stats for a scope.
|
|
347
|
+
*/
|
|
348
|
+
getHistoryStats(scope?: string): Promise<ConversationHistoryStats>;
|
|
349
|
+
/**
|
|
350
|
+
* Returns the current gateway status, auth state, and active slot routing.
|
|
351
|
+
*/
|
|
352
|
+
getStatus(scope?: string, port?: number, pid?: number): Promise<GatewayStatus>;
|
|
353
|
+
/**
|
|
354
|
+
* Returns the OpenAI-compatible model listing served by the proxy.
|
|
355
|
+
*/
|
|
356
|
+
getModels(scope?: string): Promise<{
|
|
357
|
+
object: string;
|
|
358
|
+
data: Array<{
|
|
359
|
+
id: string;
|
|
360
|
+
object: string;
|
|
361
|
+
created: number;
|
|
362
|
+
owned_by: string;
|
|
363
|
+
metadata: Record<string, string>;
|
|
364
|
+
}>;
|
|
365
|
+
}>;
|
|
366
|
+
/**
|
|
367
|
+
* Forwards a Claude-compatible request to the configured upstream Codex endpoint.
|
|
368
|
+
*/
|
|
369
|
+
forwardClaudeRequest(requestBody: string, scope?: string, requestHeaders?: Headers): Promise<ForwardedResponse>;
|
|
370
|
+
private buildAttemptTargets;
|
|
371
|
+
private forwardSingleAttempt;
|
|
372
|
+
private emitSuccessMetrics;
|
|
373
|
+
private createMetricsBaseAttributes;
|
|
374
|
+
private tokenAttributes;
|
|
375
|
+
private toolAttributes;
|
|
376
|
+
private emptyProxyUsage;
|
|
377
|
+
private divideUsage;
|
|
378
|
+
private estimateTokens;
|
|
379
|
+
private metricNumber;
|
|
380
|
+
private extractClaudeResponseMetrics;
|
|
381
|
+
private parseSseJsonPayloads;
|
|
382
|
+
private mergeUsage;
|
|
383
|
+
private extractToolOutputImpactSamples;
|
|
384
|
+
private isRecoverableUpstreamError;
|
|
385
|
+
private createCodexSessionId;
|
|
386
|
+
private createGeminiSessionId;
|
|
387
|
+
private createCodeAssistProjectCacheKey;
|
|
388
|
+
private forwardCodexRequest;
|
|
389
|
+
/**
|
|
390
|
+
* Forwards a Claude-compatible request to the Gemini GenerateContent REST API and translates the result back to Claude SSE.
|
|
391
|
+
*/
|
|
392
|
+
private forwardGeminiDirectRequest;
|
|
393
|
+
private forwardGeminiCodeAssistRequest;
|
|
394
|
+
/**
|
|
395
|
+
* Builds response headers shared by successful SSE responses.
|
|
396
|
+
*/
|
|
397
|
+
private createSuccessHeaders;
|
|
398
|
+
/**
|
|
399
|
+
* Builds the target Gemini REST endpoint for buffered or streaming requests.
|
|
400
|
+
*/
|
|
401
|
+
private createGeminiRequestUrl;
|
|
402
|
+
private createCodeAssistUrl;
|
|
403
|
+
private resolveCodeAssistProjectId;
|
|
404
|
+
private toCodeAssistGenerateContentRequest;
|
|
405
|
+
private normalizeCodeAssistBufferedResponse;
|
|
406
|
+
private normalizeCodeAssistStreamingResponse;
|
|
407
|
+
private extractCodeAssistResponse;
|
|
408
|
+
/**
|
|
409
|
+
* Resolves the logical slot from a Claude model name or configured model mapping.
|
|
410
|
+
*/
|
|
411
|
+
private resolveSlot;
|
|
412
|
+
/**
|
|
413
|
+
* Reconciles a request-specified concrete model with the resolved slot configuration.
|
|
414
|
+
*/
|
|
415
|
+
private resolveRequestedModel;
|
|
416
|
+
/**
|
|
417
|
+
* Forwards a Claude-compatible request to an Anthropic-compatible upstream such as Z.ai.
|
|
418
|
+
*/
|
|
419
|
+
private forwardAnthropicCompatibleRequest;
|
|
420
|
+
/**
|
|
421
|
+
* Resolves the environment-provided auth token for Anthropic-compatible upstreams.
|
|
422
|
+
*/
|
|
423
|
+
private getAnthropicCompatibleAuthToken;
|
|
424
|
+
/**
|
|
425
|
+
* Resolves the request timeout for upstream Anthropic-compatible calls.
|
|
426
|
+
*/
|
|
427
|
+
private resolveUpstreamTimeoutMs;
|
|
428
|
+
private createErrorResponse;
|
|
429
|
+
/**
|
|
430
|
+
* Parses and validates an inbound Claude request.
|
|
431
|
+
*/
|
|
432
|
+
private parseClaudeRequest;
|
|
433
|
+
/**
|
|
434
|
+
* Persists normalized inbound request messages for a proxied call.
|
|
435
|
+
*/
|
|
436
|
+
private recordRequest;
|
|
437
|
+
/**
|
|
438
|
+
* Persists normalized upstream response items or a stream fallback.
|
|
439
|
+
*/
|
|
440
|
+
private recordResponse;
|
|
441
|
+
/**
|
|
442
|
+
* Persists a redacted error record for the request lifecycle.
|
|
443
|
+
*/
|
|
444
|
+
private recordError;
|
|
445
|
+
/**
|
|
446
|
+
* Normalizes Claude system payloads into storable entries.
|
|
447
|
+
*/
|
|
448
|
+
private normalizeSystemPayloads;
|
|
449
|
+
/**
|
|
450
|
+
* Removes proxy-private metadata before sending Claude payloads to strict Anthropic-compatible upstreams.
|
|
451
|
+
*/
|
|
452
|
+
private sanitizeClaudePayloadForAnthropic;
|
|
453
|
+
private stripPrivateChatGptMetadata;
|
|
454
|
+
/**
|
|
455
|
+
* Extracts a message role string when present.
|
|
456
|
+
*/
|
|
457
|
+
private extractRole;
|
|
458
|
+
/**
|
|
459
|
+
* Extracts response output items from upstream SSE payloads for storage.
|
|
460
|
+
*/
|
|
461
|
+
private normalizeResponsePayloads;
|
|
462
|
+
/**
|
|
463
|
+
* Extracts the completed response envelope from an upstream SSE event payload.
|
|
464
|
+
*/
|
|
465
|
+
private extractCompletedResponse;
|
|
466
|
+
/**
|
|
467
|
+
* Redacts large request bodies before they are stored in error history.
|
|
468
|
+
*/
|
|
469
|
+
private summarizePayload;
|
|
470
|
+
}
|
|
471
|
+
//#endregion
|
|
472
|
+
//#region src/server/http.d.ts
|
|
473
|
+
declare function createHttpServer(gatewayService?: GatewayService): Hono<_$hono_types0.BlankEnv, _$hono_types0.BlankSchema, "/">;
|
|
474
|
+
//#endregion
|
|
475
|
+
//#region src/server/index.d.ts
|
|
476
|
+
declare function createServer(gatewayService?: GatewayService, environment?: NodeJS.ProcessEnv): Server;
|
|
477
|
+
//#endregion
|
|
478
|
+
//#region src/transports/stdio.d.ts
|
|
479
|
+
/** Serve the modern MCP wire protocol, explicitly rejecting legacy initialization. */
|
|
480
|
+
declare class StdioTransportHandler implements TransportHandler {
|
|
481
|
+
private readonly createServer;
|
|
482
|
+
private readonly canRestart;
|
|
483
|
+
private handle;
|
|
484
|
+
private started;
|
|
485
|
+
constructor(server: Server | (() => Server));
|
|
486
|
+
start(): Promise<void>;
|
|
487
|
+
stop(): Promise<void>;
|
|
488
|
+
}
|
|
489
|
+
//#endregion
|
|
490
|
+
export { AdminConfig, AdminConfigUpdate, ConversationHistoryEntry, ConversationHistoryPage, ConversationHistoryService, ConversationHistoryStats, GatewayService, GatewayStatus, GeminiAuthMode, LoggerLike, ModelSlot, ProfileStore, ProviderRuntimeConfig, ProxyConfig, ProxyModelConfig, ProxyProfile, ProxyProviderType, ProxySettings, ReasoningEffort, ResolvedSlotConfig, ScopeConfig, ScopeModelSelection, ScopeSettings, ScopeSlotConfig, StdioTransportHandler, TransportHandler, createHttpServer, createServer };
|