@agimon-ai/model-proxy-mcp 0.2.5 → 0.2.7

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.
@@ -0,0 +1,1133 @@
1
+ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`node:fs`);c=s(c);let l=require(`node:path`);l=s(l);let u=require(`node:url`),d=require(`node:crypto`),f=require(`node:fs/promises`);f=s(f);let p=require(`node:os`);p=s(p);let m=require(`yaml`),h=require(`zod`),ee=require(`@hono/node-server`),te=require(`hono`),g=require(`ulidx`),_=require(`better-sqlite3`);_=s(_);let ne=require(`@modelcontextprotocol/sdk/server/index.js`),re=require(`@modelcontextprotocol/sdk/types.js`),ie=require(`@modelcontextprotocol/sdk/server/stdio.js`);const v=l.default.join(p.default.homedir(),`.model-proxy`),ae=l.default.join(v,`model-provider.yaml`),oe=l.default.join(v,`model-list.yaml`),se=l.default.join(v,`scopes`),ce=l.default.join(v,`history.sqlite`),le=l.default.join(p.default.homedir(),`.codex`,`auth.json`),ue=43191,y=`model-proxy-mcp-http`,de=1e3,fe={"chatgpt-codex":{type:`chatgpt-codex`,endpoint:`https://chatgpt.com/backend-api/codex/responses`,authTokenEnvVar:null,apiTimeoutMs:null},"zai-anthropic-compat":{type:`anthropic-compatible`,endpoint:`https://api.z.ai/api/anthropic/v1/messages`,authTokenEnvVar:`ZAI_ANTHROPIC_AUTH_TOKEN`,apiTimeoutMs:3e6},"google-gemini-direct":{type:`gemini-direct`,endpoint:`https://generativelanguage.googleapis.com`,authTokenEnvVar:`GEMINI_API_KEY`,apiTimeoutMs:3e5,authMode:`auto`,apiKeyEnvVar:`GEMINI_API_KEY`,project:null,location:`global`,apiVersion:`v1beta`}},pe=[{id:`chatgpt-codex-gpt-5.3-codex`,label:`ChatGPT Codex GPT-5.3 Codex`,provider:`chatgpt-codex`,model:`gpt-5.3-codex`,reasoningEffort:`medium`,enabled:!0},{id:`chatgpt-codex-gpt-5.3-codex-low`,label:`ChatGPT Codex GPT-5.3 Codex Low`,provider:`chatgpt-codex`,model:`gpt-5.3-codex`,reasoningEffort:`low`,enabled:!0},{id:`chatgpt-codex-gpt-5.4`,label:`ChatGPT Codex GPT-5.4`,provider:`chatgpt-codex`,model:`gpt-5.4`,reasoningEffort:`medium`,enabled:!0},{id:`chatgpt-codex-gpt-5.2`,label:`ChatGPT Codex GPT-5.2`,provider:`chatgpt-codex`,model:`gpt-5.2`,reasoningEffort:`medium`,enabled:!0},{id:`zai-anthropic-compat-glm-4.7`,label:`Z.ai GLM-4.7`,provider:`zai-anthropic-compat`,model:`GLM-4.7`,reasoningEffort:`high`,enabled:!0},{id:`zai-anthropic-compat-glm-5`,label:`Z.ai GLM-5`,provider:`zai-anthropic-compat`,model:`glm-5`,reasoningEffort:`high`,enabled:!0},{id:`zai-anthropic-compat-glm-4.5-air`,label:`Z.ai GLM-4.5-Air`,provider:`zai-anthropic-compat`,model:`GLM-4.5-Air`,reasoningEffort:`medium`,enabled:!0},{id:`google-gemini-direct-gemini-2.5-flash`,label:`Google Gemini 2.5 Flash`,provider:`google-gemini-direct`,model:`gemini-2.5-flash`,reasoningEffort:`medium`,enabled:!0},{id:`google-gemini-direct-gemini-3-flash-preview`,label:`Google Gemini 3 Flash Preview`,provider:`google-gemini-direct`,model:`gemini-3-flash-preview`,reasoningEffort:`medium`,enabled:!0},{id:`google-gemini-direct-gemini-3.1-pro-preview`,label:`Google Gemini 3.1 Pro Preview`,provider:`google-gemini-direct`,model:`gemini-3.1-pro-preview`,reasoningEffort:`high`,enabled:!0}],me={models:{default:{main:{provider:`chatgpt-codex`,model:`gpt-5.4`,reasoningEffort:`medium`},fallbacks:[{provider:`chatgpt-codex`,model:`gpt-5.4`,reasoningEffort:`low`}]},sonnet:{main:{provider:`chatgpt-codex`,model:`gpt-5.3-codex`,reasoningEffort:`medium`},fallbacks:[{provider:`chatgpt-codex`,model:`gpt-5.4`,reasoningEffort:`medium`}]},opus:{main:{provider:`chatgpt-codex`,model:`gpt-5.4`,reasoningEffort:`medium`},fallbacks:[{provider:`chatgpt-codex`,model:`gpt-5.4`,reasoningEffort:`low`}]},haiku:{main:{provider:`chatgpt-codex`,model:`gpt-5.3-codex`,reasoningEffort:`low`},fallbacks:[{provider:`chatgpt-codex`,model:`gpt-5.4`,reasoningEffort:`medium`}]},subagent:{main:{provider:`chatgpt-codex`,model:`gpt-5.3-codex`,reasoningEffort:`low`},fallbacks:[{provider:`chatgpt-codex`,model:`gpt-5.4`,reasoningEffort:`medium`}]}}},b={info:(e,t)=>console.error(e,t??``),error:(e,t)=>console.error(e,t??``),debug:()=>void 0,warn:(e,t)=>console.error(e,t??``)},x=`default`,he=`zai-anthropic-compat`,ge=`ANTHROPIC_AUTH_TOKEN`,_e=`ZAI_ANTHROPIC_AUTH_TOKEN`,ve=`.yaml`,S=`medium`,C=[`default`,`sonnet`,`opus`,`haiku`,`subagent`],w=`[model-proxy-mcp]`,ye=`ENOENT`,be=2,T=`utf8`,xe=[`chatgpt-codex`,`anthropic-compatible`,`gemini-direct`],E={profileNotFound:`PROFILE_NOT_FOUND`,settingsReadFailed:`SETTINGS_READ_FAILED`,settingsWriteFailed:`SETTINGS_WRITE_FAILED`},Se=h.z.enum([`minimal`,`low`,`medium`,`high`]),Ce=h.z.object({type:h.z.enum(xe),endpoint:h.z.url(),authTokenEnvVar:h.z.string().min(1).nullable().optional(),apiTimeoutMs:h.z.number().int().positive().nullable().optional(),authMode:h.z.enum([`auto`,`api-key`,`oauth`]).nullable().optional(),apiKeyEnvVar:h.z.string().min(1).nullable().optional(),project:h.z.string().min(1).nullable().optional(),location:h.z.string().min(1).nullable().optional(),apiVersion:h.z.string().min(1).nullable().optional()}),we=h.z.object({providers:h.z.record(h.z.string().min(1),Ce)}),D=h.z.object({id:h.z.string().min(1),label:h.z.string().min(1),provider:h.z.string().min(1),model:h.z.string().min(1),reasoningEffort:Se.default(S),enabled:h.z.boolean().default(!0)}),Te=h.z.object({provider:h.z.string().min(1),model:h.z.string().min(1),reasoningEffort:Se.default(S),thinkingDisabled:h.z.boolean().optional()}),O=h.z.object({main:Te,fallbacks:h.z.array(Te).default([])}),Ee=h.z.object({models:h.z.object({default:O.optional(),sonnet:O.optional(),opus:O.optional(),haiku:O.optional(),subagent:O.optional()}).default({})}),De=h.z.array(D),Oe=h.z.object({models:h.z.object({default:O.nullable().optional(),sonnet:O.nullable().optional(),opus:O.nullable().optional(),haiku:O.nullable().optional(),subagent:O.nullable().optional()}).optional()});var k=class extends Error{constructor(e,t,n){super(e,n),this.code=t,this.name=`ProfileStoreError`}},ke=class{constructor(e=process.env.MODEL_PROXY_MCP_PROVIDER_PATH||ae,t=process.env.MODEL_PROXY_MCP_MODEL_LIST_PATH||l.default.join(l.default.dirname(process.env.MODEL_PROXY_MCP_PROVIDER_PATH||ae),l.default.basename(oe)),n=process.env.MODEL_PROXY_MCP_SCOPE_DIR||l.default.join(l.default.dirname(process.env.MODEL_PROXY_MCP_PROVIDER_PATH||ae),l.default.basename(se)),r=b){this.providerConfigPath=e,this.modelListPath=t,this.scopeSettingsDir=n,this.logger=r}getConfigPath(e=x){return this.getScopeConfigPath(e)}async ensureConfig(e=x,t){return this.getConfig(e,t)}async listScopes(){try{let e=await f.default.readdir(this.scopeSettingsDir,{withFileTypes:!0}),t=new Set([x]);for(let n of e){if(!n.isFile()||!n.name.endsWith(ve))continue;let e=n.name.slice(0,-5);e&&t.add(e)}return Array.from(t).sort()}catch(e){if(this.isFileNotFoundError(e))return[x];throw this.logger.error(`${w} Failed to list scopes`,{scopeSettingsDir:this.scopeSettingsDir,cause:e}),new k(`Failed to list scopes in ${this.scopeSettingsDir}`,E.settingsReadFailed,{cause:e})}}async getConfig(e=x,t){return this.toProxyConfig(await this.getSettings(e,t))}async getAdminConfig(e=x){let t=await this.getSettings(e),n=this.toProfiles(t),r=Object.fromEntries(C.map(e=>[e,this.resolveSlotConfig(t,n,e)]));return{scope:e,providerConfigPath:this.providerConfigPath,modelListPath:this.modelListPath,scopeConfigPath:this.getScopeConfigPath(e),providers:t.providers,models:n,scopeModels:t.scope.models,slots:r}}async listProfiles(e=x){return(await this.getAdminConfig(e)).models}async getActiveProfile(e=x,t=x){let n=await this.getAdminConfig(e),r=n.slots[t];return n.models.find(e=>e.id===r.profileId&&e.enabled)??null}async getResolvedSlotConfig(e=x,t=x){return(await this.getAdminConfig(e)).slots[t]}async setActiveProfile(e,t=x,n=x){let r=await this.getSettings(t),i=this.toProfiles(r).find(t=>t.id===e&&t.enabled);if(!i)throw new k(`Profile not found or disabled: ${e}`,E.profileNotFound,{cause:{profileId:e,scope:t,slot:n}});return this.updateConfig({models:{[n]:{main:{provider:i.provider,model:i.model,reasoningEffort:i.reasoningEffort,thinkingDisabled:r.scope.models[n]?.main.thinkingDisabled??!1},fallbacks:r.scope.models[n]?.fallbacks??[]}}},t)}async upsertProfile(e,t=x){let n=D.parse({id:e.id,label:e.label,provider:e.provider,model:e.model,reasoningEffort:e.reasoningEffort,enabled:e.enabled}),r=await this.getSettings(t),i=r.models.filter(e=>e.id!==n.id);i.push(n);let a=this.normalizeSettings({providers:r.providers,models:i,scope:r.scope});return await this.saveSettings(a,t),this.toProxyConfig(a)}async updateConfig(e,t=x){let n=Oe.parse(e),r=await this.getSettings(t),i={...r.scope.models};for(let e of C){let t=n.models?.[e];if(t!==void 0){if(t===null){delete i[e];continue}i[e]=t}}let a=this.normalizeSettings({providers:r.providers,models:r.models,scope:{models:i}});return await this.saveSettings(a,t),this.toProxyConfig(a)}getScopeConfigPath(e){return l.default.join(this.scopeSettingsDir,`${this.sanitizeScope(e)}${ve}`)}sanitizeScope(e){return e.replace(/[^a-zA-Z0-9._-]/g,`-`).replace(/--+/g,`-`).replace(/^-|-$/g,``)||x}async getSettings(e,t){let n=await this.readSettings(e,t),r=this.normalizeSettings(n);return JSON.stringify(r)!==JSON.stringify(n)&&await this.saveSettings(r,e),r}async readSettings(e,t){let n=this.getScopeConfigPath(e),[r,i,a]=await Promise.allSettled([f.default.readFile(this.providerConfigPath,T),f.default.readFile(this.modelListPath,T),f.default.readFile(n,T)]);try{let n=r.status===`fulfilled`?r.value:null,o=i.status===`fulfilled`?i.value:null,s=a.status===`fulfilled`?a.value:null,c=r.status===`rejected`&&this.isFileNotFoundError(r.reason),l=i.status===`rejected`&&this.isFileNotFoundError(i.reason),u=a.status===`rejected`&&this.isFileNotFoundError(a.reason);if(!n&&r.status===`rejected`&&!c)throw r.reason;if(!o&&i.status===`rejected`&&!l)throw i.reason;if(!s&&a.status===`rejected`&&!u)throw a.reason;let d=s?Ee.parse((0,m.parse)(s)):await this.loadScopeSeedSettings(e,t),f=this.normalizeSettings({providers:n?we.parse((0,m.parse)(n)).providers:structuredClone(fe),models:o?De.parse((0,m.parse)(o)):structuredClone(pe),scope:d});return(c||l||u)&&await this.saveSettings(f,e),f}catch(t){throw this.logger.error(`${w} Failed to read settings`,{scope:e,providerConfigPath:this.providerConfigPath,modelListPath:this.modelListPath,scopeConfigPath:n,code:E.settingsReadFailed,cause:t}),new k(`Failed to read settings from ${this.providerConfigPath}, ${this.modelListPath}, and ${n}`,E.settingsReadFailed,{cause:t})}}isFileNotFoundError(e){return e?.code===`ENOENT`}async loadScopeSeedSettings(e,t){let n=[t,this.getDefaultScopeSeedPath(e)].filter(e=>!!e);for(let t of n)try{let e=await f.default.readFile(t,T);return Ee.parse((0,m.parse)(e))}catch(n){if(this.isFileNotFoundError(n))continue;throw this.logger.error(`${w} Failed to read scope seed settings`,{scope:e,seedConfigPath:t,cause:n}),n}return structuredClone(me)}getDefaultScopeSeedPath(e){let t=this.getScopeConfigPath(x);return e===x?null:t}normalizeSettings(e){let t=this.normalizeProviders(Object.keys(e.providers).length>0?e.providers:structuredClone(fe)),n=e.models.map(e=>D.parse(e)),r=this.normalizeSlotSelection(e.scope.models.default?.main??null,n,t,this.getDefaultSelection(n,t)),i={default:r?{main:r,fallbacks:this.normalizeFallbacks(e.scope.models.default?.fallbacks??[],r,n,t)}:void 0};for(let a of C){if(a===x)continue;let o=r??this.getDefaultSelection(n,t),s=this.normalizeSlotSelection(e.scope.models[a]?.main??null,n,t,o);i[a]=s?{main:s,fallbacks:this.normalizeFallbacks(e.scope.models[a]?.fallbacks??[],s,n,t)}:void 0}return{providers:t,models:n,scope:{models:i}}}normalizeProviders(e){let t={...e},n=t[he];return n?.type===`anthropic-compatible`&&n.authTokenEnvVar===`ANTHROPIC_AUTH_TOKEN`&&(t[he]={...n,authTokenEnvVar:`ZAI_ANTHROPIC_AUTH_TOKEN`}),t}getDefaultSelection(e,t){let n=e.find(e=>e.enabled&&t[e.provider]);return n?{provider:n.provider,model:n.model,reasoningEffort:n.reasoningEffort,thinkingDisabled:!1}:null}normalizeSlotSelection(e,t,n,r){if(!e)return r?{...r}:null;let i=n[e.provider]?e.provider:r?.provider??null;if(!i)return null;if(i!==e.provider)return r&&n[r.provider]?{...r}:null;let a=t.find(t=>t.enabled&&t.provider===i&&t.model===e.model);return{provider:i,model:a?.model??e.model,reasoningEffort:e.reasoningEffort??a?.reasoningEffort??S,thinkingDisabled:e.thinkingDisabled??!1}}normalizeFallbacks(e,t,n,r){let i=[],a=new Set([`${t.provider}:${t.model}:${t.reasoningEffort}:${t.thinkingDisabled?`off`:`on`}`]);for(let t of e){let e=this.normalizeSlotSelection(t,n,r,null);if(!e)continue;let o=`${e.provider}:${e.model}:${e.reasoningEffort}:${e.thinkingDisabled?`off`:`on`}`;a.has(o)||(a.add(o),i.push(e))}return i}toProxyConfig(e){let t=this.toProfiles(e),n=Object.fromEntries(C.map(n=>[n,this.resolveSlotConfig(e,t,n).profileId]));return{activeProfileId:n.default??null,slotProfileIds:n,providers:e.providers,profiles:t,scope:e.scope}}resolveSlotConfig(e,t,n){let r=e.scope.models[n]??e.scope.models.default,i=r?.main??null,a=i?t.find(e=>e.provider===i.provider&&e.model===i.model&&e.enabled):void 0,o=i?.provider?e.providers[i.provider]:void 0;return{slot:n,profileId:a?.id??null,label:a?.label??null,provider:i?.provider??null,providerType:o?.type??null,endpoint:o?.endpoint??null,model:i?.model??null,reasoningEffort:i?.reasoningEffort??a?.reasoningEffort??S,thinkingDisabled:i?.thinkingDisabled??!1,fallbacks:r?.fallbacks??[]}}toProfiles(e){return e.models.map(t=>({id:t.id,label:t.label,provider:t.provider,providerType:e.providers[t.provider]?.type??null,model:t.model,endpoint:e.providers[t.provider]?.endpoint??null,reasoningEffort:t.reasoningEffort,enabled:t.enabled}))}async saveSettings(e,t=x){let n=this.getScopeConfigPath(t);try{let t=new Set([l.default.dirname(this.providerConfigPath),l.default.dirname(this.modelListPath),l.default.dirname(n)]);await Promise.all(Array.from(t,e=>f.default.mkdir(e,{recursive:!0}))),await Promise.all([f.default.writeFile(this.providerConfigPath,(0,m.stringify)(we.parse({providers:e.providers}),{indent:2}),T),f.default.writeFile(this.modelListPath,(0,m.stringify)(De.parse(e.models),{indent:2}),T),f.default.writeFile(n,(0,m.stringify)(Ee.parse(e.scope),{indent:2}),T)])}catch(e){throw this.logger.error(`${w} Failed to write settings`,{scope:t,providerConfigPath:this.providerConfigPath,modelListPath:this.modelListPath,scopeConfigPath:n,code:E.settingsWriteFailed,cause:e}),new k(`Failed to write settings to ${this.providerConfigPath}, ${this.modelListPath}, and ${n}`,E.settingsWriteFailed,{cause:e})}}};const Ae=(0,u.fileURLToPath)(require(`url`).pathToFileURL(__filename).href),je=l.default.dirname(Ae),Me=l.default.join(je,`codex.md`);let A=null;var Ne=class{config;codexInstructions;constructor(e){this.config=e,this.codexInstructions=this.loadCodexInstructions()}loadCodexInstructions(){if(A!==null)return A;try{return A=c.default.readFileSync(Me,`utf-8`),A}catch{return console.warn(`Warning: Could not load codex.md from ${Me}`),A=``,``}}async transform(e,t){try{let e=JSON.parse(t);this.config.logger?.debug(`[ClaudeToOpenAI] ===== ORIGINAL CLAUDE REQUEST =====`),this.config.logger?.debug(`[ClaudeToOpenAI] Original body`,{body:JSON.stringify(e,null,2)});let n=this.config.sessionId||(0,d.randomUUID)(),r=n,i=this.config.sessionReasoningEffort,a=e.model&&e.model.toLowerCase().includes(`haiku`),o=i||(a?`minimal`:`medium`);this.config.logger?.debug(`[ClaudeToOpenAI] Model detection and reasoning effort`,{originalModel:e.model,isHaikuModel:a,sessionReasoningEffort:i||`none`,finalReasoningEffort:o,source:i?`session override`:`model-based`});let s={model:this.config.toModel||`gpt-5`,stream:!0,store:!1,tool_choice:`auto`,parallel_tool_calls:!1,prompt_cache_key:n};this.config.thinkingDisabled||(s.reasoning={effort:o,summary:`auto`},s.include=[`reasoning.encrypted_content`]),this.config.logger?.debug(`[ClaudeToOpenAI] Thinking mode`,{thinkingDisabled:this.config.thinkingDisabled??!1}),s.instructions=this.adaptInstructionsForChatGPT(this.codexInstructions);let c=[],l=``;if(e.system){let t=this.extractSystemMessages(e.system);t&&Array.isArray(t)&&t.length>0&&(l=t.map(e=>e.content).join(`
2
+
3
+ `))}if(l=this.removeClaudeCodeInstructions(l),l&&c.push({type:`message`,role:`user`,content:[{type:`input_text`,text:l}]}),e.messages&&Array.isArray(e.messages))for(let t of e.messages){let e=this.convertMessageToInput(t);Array.isArray(e)?c.push(...e):e&&c.push(e)}if(s.input=c,e.tools&&Array.isArray(e.tools)){this.config.logger?.debug(`[ClaudeToOpenAI] Original Claude tools`,{tools:JSON.stringify(e.tools,null,2)});let t=this.convertTools(e.tools);this.config.logger?.debug(`[ClaudeToOpenAI] Converted tools`,{tools:JSON.stringify(t,null,2)}),t.length>0?(s.tools=t,this.config.logger?.debug(`[ClaudeToOpenAI] Added tools to responsesRequest`,{toolCount:t.length}),this.config.logger?.debug(`[ClaudeToOpenAI] Verify responsesRequest.tools exists`,{hasTools:!!s.tools,toolsLength:s.tools?.length,keys:Object.keys(s)})):this.config.logger?.warn(`[ClaudeToOpenAI] No valid tools after conversion, omitting tools field`)}else this.config.logger?.debug(`[ClaudeToOpenAI] No tools in Claude request`,{hasTools:!!e.tools,isArray:Array.isArray(e.tools)});let u=this.config.toEndpoint||`https://chatgpt.com/backend-api/codex/responses`,f={version:`0.46.0`,"openai-beta":`responses=experimental`,conversation_id:r,session_id:n,accept:`text/event-stream`,"content-type":`application/json`,"user-agent":`codex_cli_rs/0.46.0 (Mac OS 15.6.0; arm64) iTerm.app/3.6.2`,originator:`codex_cli_rs`},p=this.config.resolvedAuth;return p?.accessToken?(this.config.logger?.debug(`[ClaudeToOpenAI] Raw access token`,{token:`${p.accessToken.substring(0,30)}...`}),f.authorization=p.accessToken.startsWith(`Bearer `)?p.accessToken:`Bearer ${p.accessToken}`,p.accountId&&(f[`chatgpt-account-id`]=p.accountId)):this.config.toApiKey&&(f.authorization=`Bearer ${this.config.toApiKey}`),f.authorization?.startsWith(`Bearer `)&&this.config.logger?.debug(`[ClaudeToOpenAI] Added Bearer auth header`),this.config.logger?.debug(`[ClaudeToOpenAI] ===== REQUEST DETAILS =====`),this.config.logger?.debug(`[ClaudeToOpenAI] Target URL`,{targetUrl:u}),this.config.logger?.debug(`[ClaudeToOpenAI] Headers`,{headers:{...f,authorization:f.authorization?`${f.authorization.substring(0,30)}...`:void 0}}),this.config.logger?.debug(`[ClaudeToOpenAI] Body`,{body:JSON.stringify(s,null,2)}),this.config.logger?.debug(`[ClaudeToOpenAI] ===============================`),this.config.logger?.debug(`[ClaudeToOpenAI] ===== FINAL TRANSFORMED REQUEST =====`),this.config.logger?.debug(`[ClaudeToOpenAI] Pre-final check - responsesRequest.tools`,{hasTools:!!s.tools,toolsLength:s.tools?.length,keys:Object.keys(s)}),this.config.logger?.debug(`[ClaudeToOpenAI] Final body`,{body:JSON.stringify(s,null,2)}),this.config.logger?.debug(`[ClaudeToOpenAI] ===== END FINAL TRANSFORMED REQUEST =====`),{url:u,body:JSON.stringify(s),headers:f}}catch(e){throw Error(`Failed to transform Claude request to OpenAI: ${e}`)}}adaptInstructionsForChatGPT(e){let t=e;return t=t.replace(/You are powered by the model named Sonnet 4\.5\. The exact model ID is claude-sonnet-4-5-\d+\./g,`You are powered by ChatGPT (GPT-5 reasoning model).`),t=t.replace(/Assistant knowledge cutoff is January 2025/g,`Assistant knowledge cutoff is October 2023`),t=t.replace(/\bClaude\b/g,`ChatGPT`),t=t.replace(/\bAnthropic\b/g,`OpenAI`),t}removeClaudeCodeInstructions(e){let t=[/You are Claude Code, Anthropic's official CLI for Claude\.[\s\S]*?claude_code_docs_map\.md/,/You are Claude Code[\s\S]*?using Claude Code\n/],n=e;for(let e of t)n=n.replace(e,``);return n=n.replace(/\n{3,}/g,`
4
+
5
+ `).trim(),n}extractSystemMessages(e){let t=[];if(typeof e==`string`)t.push({role:`system`,content:e});else if(Array.isArray(e))for(let n of e)typeof n==`string`?t.push({role:`system`,content:n}):n.type===`text`&&n.text&&t.push({role:`system`,content:n.text});else e.type===`text`&&e.text&&t.push({role:`system`,content:e.text});return t}convertMessageToInput(e){if(!e.role||!e.content)return null;let t=e.role===`assistant`?`output_text`:`input_text`;if(Array.isArray(e.content)){let n=[],r=[],i=()=>{r.length!==0&&(n.push({type:`message`,role:e.role,content:r}),r=[])};for(let a of e.content)if(a.type===`tool_result`){i();let e=typeof a.content==`string`?a.content:JSON.stringify(a.content);n.push({type:`function_call_output`,call_id:a.tool_use_id,output:e})}else if(a.type===`text`)r.push({type:t,text:a.text||``});else if(a.type===`image`)if(a.source&&a.source.type===`base64`&&a.source.data){let e=a.source.media_type||`image/jpeg`,t=`data:${e};base64,${a.source.data}`;r.push({type:`input_image`,image_url:t}),this.config.logger?.debug(`[ClaudeToOpenAI] Converted image block`,{mediaType:e,dataLength:a.source.data.length})}else this.config.logger?.warn(`[ClaudeToOpenAI] Unsupported image format`,{source:a.source});else a.type===`tool_use`&&(i(),n.push({type:`function_call`,call_id:a.id,name:a.name,arguments:JSON.stringify(a.input||{})}));return i(),n.length>1?n:n.length===1?n[0]:null}return typeof e.content==`string`?{type:`message`,role:e.role,content:[{type:t,text:e.content}]}:null}convertTools(e){return!e||!Array.isArray(e)?[]:e.filter(e=>!(!e||typeof e!=`object`||!e.name)).map(e=>({type:`function`,name:e.name,description:e.description||``,parameters:e.input_schema||e.parameters||{}}))}},Pe=class e{static TOKEN_REFRESH_URL=`https://auth.openai.com/oauth/token`;static CLIENT_ID=`app_EMoamEEZ73f0CkXaXp7hrann`;constructor(e=b,t=l.default.join(process.env.HOME||``,`.codex`,`auth.json`)){this.logger=e,this.authFilePath=t}getAuthFilePath(){return this.authFilePath}async getAuthStatus(){let e=this.readAuthFile();return{configured:!!e?.tokens?.refresh_token,accountId:e?.tokens?.account_id??null,authFilePath:this.authFilePath,lastRefresh:e?.last_refresh??null}}async getAccountId(){return(await this.resolveAuth()).accountId}async getAccessToken(){return(await this.resolveAuth()).accessToken}async resolveAuth(){let e=this.readAuthFile();if(!e?.tokens)return this.logger.warn(`[CodexAuth] No tokens found in auth file`),{accessToken:null,accountId:null};let t=e.tokens.account_id||null,n=e.tokens.access_token;if(n.startsWith(`Bearer `)&&(n=n.slice(7)),!this.isTokenExpired(n))return{accessToken:n,accountId:t};let r=await this.refreshAccessToken(e.tokens.refresh_token);return r?(this.saveTokens(r),{accessToken:r.access_token,accountId:r.account_id||t}):{accessToken:null,accountId:t}}readAuthFile(){try{return c.default.existsSync(this.authFilePath)?JSON.parse(c.default.readFileSync(this.authFilePath,`utf8`)):null}catch(e){return this.logger.error(`[CodexAuth] Failed to read auth file`,e),null}}isTokenExpired(e){let t=this.decodeJWT(e);return t?.exp?t.exp*1e3-Date.now()<300*1e3:!0}decodeJWT(e){try{let[,t]=e.split(`.`);return t?JSON.parse(Buffer.from(t,`base64url`).toString(`utf8`)):null}catch(e){return this.logger.error(`[CodexAuth] Failed to decode JWT`,e),null}}async refreshAccessToken(t){try{let n=await fetch(e.TOKEN_REFRESH_URL,{method:`POST`,headers:{"content-type":`application/x-www-form-urlencoded`},body:new URLSearchParams({grant_type:`refresh_token`,refresh_token:t,client_id:e.CLIENT_ID})});if(!n.ok)return this.logger.error(`[CodexAuth] Token refresh failed`,await n.text()),null;let r=await n.json();return{id_token:r.id_token??``,access_token:(r.access_token??``).replace(/^Bearer\s+/i,``),refresh_token:r.refresh_token||t,account_id:r.account_id??``}}catch(e){return this.logger.error(`[CodexAuth] Failed to refresh token`,e),null}}saveTokens(e){try{let t=this.readAuthFile();if(!t)return;t.tokens=e,t.last_refresh=new Date().toISOString(),c.default.writeFileSync(this.authFilePath,JSON.stringify(t,null,2),`utf8`)}catch(e){this.logger.error(`[CodexAuth] Failed to save tokens`,e)}}},Fe=class{logger;thinkingDisabled;constructor(e,t=!1){this.logger=e,this.thinkingDisabled=t}formatSseEvent(e,t){return`event: ${e}\ndata: ${JSON.stringify(t)}\n\n`}transform(e){try{return!e||e.trim()===``?this.createEmptyClaudeResponse():e.includes(`data:`)?this.convertStreamingResponse(e):this.convertNonStreamingResponse(e)}catch(e){return this.logger?.error(`[OpenAIToClaude] ERROR in transform`,e),this.createEmptyClaudeResponse()}}transformStream(e){let t={encoder:new TextEncoder,messageId:`msg_${(0,g.ulid)()}`,model:`gpt-5`,inputTokens:0,outputTokens:0,cachedTokens:0,reasoningTokens:0,reasoningEffort:void 0,stopReason:`end_turn`,messageStarted:!1,messageStopped:!1,thinkingBlockStarted:!1,textBlockStarted:!1,emittedThinking:!1,emittedText:!1,lastThinkingChunk:null,lastTextChunk:null,streamedReasoningKeys:new Set,streamedTextKeys:new Set,emittedToolCallIndexes:new Set,toolCalls:new Map},n=new TextDecoder,r=``;return new ReadableStream({start:async i=>{let a=e.getReader();try{for(;;){let{done:e,value:o}=await a.read();if(e)break;r+=n.decode(o,{stream:!0}),r=this.processBufferedEvents(r,t,i)}r+=n.decode(),r=this.processBufferedEvents(r,t,i,!0),t.messageStopped||(t.messageStarted?this.finishStreamingResponse(i,t):i.enqueue(t.encoder.encode(this.createEmptyClaudeResponse())))}catch(e){this.logger?.error(`[OpenAIToClaude] ERROR in transformStream`,e),t.messageStopped||(t.messageStarted?this.emitStreamingError(i,t,`Unexpected API error`):i.enqueue(t.encoder.encode(this.createClaudeErrorStream(`Unexpected API error`))))}finally{i.close(),a.releaseLock()}}})}createEmptyClaudeResponse(){let e=`msg_${(0,g.ulid)()}`,t=[];return t.push(`event: message_start`),t.push(`data: ${JSON.stringify({type:`message_start`,message:{id:e,type:`message`,role:`assistant`,content:[],model:`gpt-5`,stop_reason:null,stop_sequence:null,usage:{input_tokens:0,output_tokens:0}}})}`),t.push(``),t.push(`event: message_stop`),t.push(`data: {"type":"message_stop"}`),t.push(``),t.join(`
6
+ `)}convertStreamingResponse(e){let t=this.parseOpenAIStream(e),n=this.createClaudeStreamFromParsed(t),r=t.errorMessage||`Empty streaming response from provider`;return this.ensureValidClaudeStream(n,r)}parseOpenAIStream(e){let t={textSegments:[],thinkingSegments:[],toolCalls:new Map,streamedReasoningKeys:new Set,streamedTextKeys:new Set,model:`gpt-5`,inputTokens:0,outputTokens:0,cachedTokens:0,reasoningTokens:0,reasoningEffort:void 0,stopReason:void 0,errorMessage:void 0},n=e.split(`
7
+ `),r=``,i=/event:\s*response\./i.test(e)||/"type"\s*:\s*"response\./i.test(e)||/"response"\s*:\s*\{/i.test(e);for(let e of n){let n=e.trim();if(!n)continue;if(n.startsWith(`event:`)){r=n.slice(6).trim();continue}if(!n.startsWith(`data:`))continue;let a=n.slice(5).trim();if(!a||a===`[DONE]`)continue;let o;try{o=JSON.parse(a)}catch{continue}if(o?.error){t.errorMessage=o.error?.message||o.error?.error||(typeof o.error==`string`?o.error:`Unexpected API error`);break}i||r.startsWith(`response.`)?this.handleResponsesEvent(r,o,t):this.handleChatCompletionChunk(o,t)}return t}handleResponsesEvent(e,t,n){let r=typeof t?.type==`string`?t.type:e;switch(t?.model&&typeof t.model==`string`&&(n.model=t.model),t?.usage&&(n.inputTokens=t.usage.input_tokens??t.usage.prompt_tokens??n.inputTokens,n.outputTokens=t.usage.output_tokens??t.usage.completion_tokens??n.outputTokens),r){case`response.created`:t?.response?.model&&(n.model=t.response.model);break;case`response.reasoning.delta`:case`response.reasoning_summary_text.delta`:case`response.function_call_arguments.delta`:case`response.function_call_arguments.done`:case`response.in_progress`:case`response.output_item.added`:case`response.output_item.done`:case`response.content_part.added`:case`response.content_part.done`:case`response.reasoning_summary_part.added`:case`response.reasoning_summary_part.done`:case`response.reasoning_summary_text.done`:case`response.output_text.delta`:case`response.output_text.done`:case`response.delta`:break;case`response.completed`:case`response.done`:this.collectCompletedResponse(t,n);break;case`response.error`:n.errorMessage=t?.error?.message||t?.message||`Unexpected API error`;break;default:t?.delta&&(this.collectTextFromNode(t.delta,n.textSegments),t.delta.tool_calls&&this.collectToolCalls(t.delta.tool_calls,n.toolCalls));break}}handleChatCompletionChunk(e,t){if(e&&(e.model&&typeof e.model==`string`&&(t.model=e.model),e.usage&&(t.inputTokens=e.usage.prompt_tokens??t.inputTokens,t.outputTokens=e.usage.completion_tokens??t.outputTokens),Array.isArray(e.choices)))for(let n of e.choices)n?.delta&&(this.collectTextFromNode(n.delta,t.textSegments),n.delta.tool_calls&&this.collectToolCalls(n.delta.tool_calls,t.toolCalls)),n?.message?.content&&this.collectTextFromNode(n.message.content,t.textSegments),n?.finish_reason&&(t.stopReason=this.mapFinishReason(n.finish_reason))}collectCompletedResponse(e,t){let n=!1;if(e?.response?.model&&(t.model=e.response.model),e?.response?.usage&&(t.inputTokens=e.response.usage.input_tokens??e.response.usage.prompt_tokens??t.inputTokens,t.outputTokens=e.response.usage.output_tokens??e.response.usage.completion_tokens??t.outputTokens,e.response.usage.input_tokens_details?.cached_tokens&&(t.cachedTokens=e.response.usage.input_tokens_details.cached_tokens),e.response.usage.output_tokens_details?.reasoning_tokens&&(t.reasoningTokens=e.response.usage.output_tokens_details.reasoning_tokens)),e?.response?.reasoning?.effort&&(t.reasoningEffort=e.response.reasoning.effort),Array.isArray(e?.response?.tool_calls)&&this.collectToolCalls(e.response.tool_calls,t.toolCalls),Array.isArray(e?.response?.output)){for(let[r,i]of e.response.output.entries())if(i?.type===`reasoning`){let e={output_index:r,item:i};if(!this.shouldEmitTerminalContent(e,t.streamedReasoningKeys,`reasoning`,t.thinkingSegments.length>0))continue;if(Array.isArray(i?.summary))for(let e of i.summary)e?.type===`summary_text`&&e?.text&&t.thinkingSegments.push(e.text)}else if(i?.type===`message`){let e={output_index:r,item:i};if(!this.shouldEmitTerminalContent(e,t.streamedTextKeys,`text`,t.textSegments.length>0))continue;if(Array.isArray(i?.content))for(let e of i.content)(e?.type===`output_text`||e?.type===`text`)&&e?.text&&(t.textSegments.push(e.text),n=!0)}else if(i?.type===`function_call`){let e={index:t.toolCalls.size,id:i.id||`tool_${(0,g.ulid)()}`,function:{name:i.name,arguments:i.arguments}};this.collectToolCalls([e],t.toolCalls)}}e?.response?.output_text&&!n&&this.shouldEmitTerminalContent({output_index:0},t.streamedTextKeys,`text`,t.textSegments.length>0)&&this.collectTextFromNode(e.response.output_text,t.textSegments),t.stopReason=this.mapResponseStatusToStopReason(e?.response?.status)}collectTextFromNode(e,t){if(e!=null){if(typeof e==`string`){e.length>0&&t.push(e);return}if(Array.isArray(e)){for(let n of e)this.collectTextFromNode(n,t);return}if(typeof e==`object`){typeof e.text==`string`&&t.push(e.text),typeof e.output_text==`string`&&t.push(e.output_text),typeof e.value==`string`&&t.push(e.value),typeof e.delta==`string`?t.push(e.delta):e.delta&&this.collectTextFromNode(e.delta,t),e.token&&typeof e.token.text==`string`&&t.push(e.token.text);for(let n of[`content`,`output`,`output_text`,`message`,`choices`,`segments`])if(e[n]!==void 0)if(n===`choices`&&Array.isArray(e[n]))for(let r of e[n])r?.message?.content&&this.collectTextFromNode(r.message.content,t),r?.delta&&this.collectTextFromNode(r.delta,t);else this.collectTextFromNode(e[n],t)}}}collectToolCalls(e,t){if(!e)return;let n=Array.isArray(e)?e:[e];for(let e of n){if(!e)continue;let n=typeof e.index==`number`?e.index:t.size,r=t.get(n)||{id:``,name:``,argumentChunks:[]};typeof e.id==`string`&&!r.id&&(r.id=e.id);let i=e.function?.name??e.name;typeof i==`string`&&i.length>0&&(r.name=i);let a=e.function?.arguments??e.arguments;typeof a==`string`&&a.length>0&&r.argumentChunks.push(a),t.set(n,r)}}collectResponsesToolCallDelta(e,t){let n=typeof e?.output_index==`number`?e.output_index:t.size,r=t.get(n)||{id:``,name:``,argumentChunks:[]},i=e?.item_id??e?.id;typeof i==`string`&&!r.id&&(r.id=i);let a=e?.name??e?.item?.name??e?.item?.call_id;typeof a==`string`&&a.length>0&&(r.name=a);let o=e?.delta??e?.arguments??e?.item?.arguments;typeof o==`string`&&o.length>0&&r.argumentChunks.push(o),t.set(n,r)}collectResponsesOutputItem(e,t,n=!1){let r=e?.item;if(r?.type===`message`){if(n&&!this.shouldEmitTerminalContent(e,t.streamedTextKeys,`text`,t.textSegments.length>0))return;this.collectTextFromNode(r.content,t.textSegments);return}if(r?.type===`reasoning`){if(n&&!this.shouldEmitTerminalContent(e,t.streamedReasoningKeys,`reasoning`,t.thinkingSegments.length>0))return;this.collectTextFromNode(r.summary??r.content,t.thinkingSegments);return}this.collectResponsesToolCallDelta(e,t.toolCalls)}parseToolCallInput(e){if(!e)return{};try{let t=JSON.parse(e);return t&&typeof t==`object`&&!Array.isArray(t)?t:{}}catch{return{}}}createClaudeStreamFromParsed(e){if(e.errorMessage)return this.createClaudeErrorStream(e.errorMessage);let t=this.thinkingDisabled?[]:this.mergeAndChunkSegments(this.dedupeSegments(e.thinkingSegments)),n=this.mergeAndChunkSegments(this.dedupeSegments(e.textSegments)),r=new Set,i=Array.from(e.toolCalls.entries()).sort((e,t)=>e[0]-t[0]).map(([e,t])=>({index:e,id:t.id,name:t.name,arguments:this.normalizeToolCallArguments(t.argumentChunks)})).filter(e=>{if(!e.name)return!1;let t=`${e.id}\u0000${e.name}\u0000${e.arguments}`;return r.has(t)?!1:(r.add(t),!0)});if(t.length===0&&n.length===0&&i.length===0)return this.createClaudeErrorStream(`Empty streaming response from provider`);let a=`msg_${(0,g.ulid)()}`,o=e.model||`gpt-5`,s=[];s.push(`event: message_start`),s.push(`data: ${JSON.stringify({type:`message_start`,message:{id:a,type:`message`,role:`assistant`,content:[],model:o,stop_reason:null,stop_sequence:null,usage:{input_tokens:e.inputTokens??0,output_tokens:0}}})}`),s.push(``);let c=0;if(t.length>0){s.push(`event: content_block_start`),s.push(`data: ${JSON.stringify({type:`content_block_start`,index:c,content_block:{type:`thinking`,thinking:``}})}`),s.push(``);for(let e of t)e&&(s.push(`event: content_block_delta`),s.push(`data: ${JSON.stringify({type:`content_block_delta`,index:c,delta:{type:`thinking_delta`,thinking:e}})}`),s.push(``));s.push(`event: content_block_stop`),s.push(`data: ${JSON.stringify({type:`content_block_stop`,index:c})}`),s.push(``),c+=1}if(n.length>0){s.push(`event: content_block_start`),s.push(`data: ${JSON.stringify({type:`content_block_start`,index:c,content_block:{type:`text`,text:``}})}`),s.push(``);for(let e of n)e&&(s.push(`event: content_block_delta`),s.push(`data: ${JSON.stringify({type:`content_block_delta`,index:c,delta:{type:`text_delta`,text:e}})}`),s.push(``));s.push(`event: content_block_stop`),s.push(`data: ${JSON.stringify({type:`content_block_stop`,index:c})}`),s.push(``),c+=1}for(let e of i){let t=c+e.index,n=e.id||`tool_${(0,g.ulid)()}`;s.push(`event: content_block_start`),s.push(`data: ${JSON.stringify({type:`content_block_start`,index:t,content_block:{type:`tool_use`,id:n,name:e.name,input:{}}})}`),s.push(``),e.arguments&&(s.push(`event: content_block_delta`),s.push(`data: ${JSON.stringify({type:`content_block_delta`,index:t,delta:{type:`input_json_delta`,partial_json:e.arguments}})}`),s.push(``)),s.push(`event: content_block_stop`),s.push(`data: ${JSON.stringify({type:`content_block_stop`,index:t})}`),s.push(``)}let l=e.stopReason||`end_turn`,u={output_tokens:e.outputTokens??0};return(e.cachedTokens||e.reasoningTokens||e.reasoningEffort)&&(u.metadata={},e.cachedTokens&&(u.metadata.cached_tokens=e.cachedTokens),e.reasoningTokens&&(u.metadata.reasoning_tokens=e.reasoningTokens),e.reasoningEffort&&(u.metadata.reasoning_effort=e.reasoningEffort)),s.push(`event: message_delta`),s.push(`data: ${JSON.stringify({type:`message_delta`,delta:{stop_reason:l,stop_sequence:null},usage:u})}`),s.push(``),s.push(`event: message_stop`),s.push(`data: {"type":"message_stop"}`),s.push(``),s.join(`
8
+ `)}mergeAndChunkSegments(e,t=2e3){if(!e||e.length===0)return[];let n=this.collapseRepeatedWholeText(e.join(``));if(!n)return[];let r=[];for(let e=0;e<n.length;e+=t)r.push(n.slice(e,e+t));return r}dedupeSegments(e){if(e.length<2)return e;let t=[];for(let n of e)n&&t.at(-1)!==n&&t.push(n);return t}normalizeToolCallArguments(e){return e.length===0?``:this.sanitizeToolCallArgumentsJson(this.collapseRepeatedWholeText(this.dedupeSegments(e).join(``)))}sanitizeToolCallArgumentsJson(e){if(!e)return``;try{let t=JSON.parse(e),n=this.stripEmptyStringValues(t);return JSON.stringify(n)}catch{return e}}stripEmptyStringValues(e){return Array.isArray(e)?e.map(e=>this.stripEmptyStringValues(e)):!e||typeof e!=`object`?e:Object.fromEntries(Object.entries(e).filter(([,e])=>e!==``).map(([e,t])=>[e,this.stripEmptyStringValues(t)]))}collapseRepeatedWholeText(e){if(!e||e.length<2)return e;for(let t=4;t>=2;--t){if(e.length%t!==0)continue;let n=e.slice(0,e.length/t);if(n&&n.repeat(t)===e)return n}return e}markStreamedContentKey(e,t,n){let r=this.getResponseContentKey(e,n);r&&t.add(r)}shouldEmitTerminalContent(e,t,n,r){let i=this.getResponseContentKey(e,n);return i?!t.has(i):!r}getResponseContentKey(e,t){let n=e?.item_id??e?.item?.id??e?.id,r=typeof e?.output_index==`number`?e.output_index:null,i=typeof e?.content_index==`number`?e.content_index:null;return typeof n==`string`&&n.length>0?`${t}:item:${n}:${i??`na`}`:r===null?null:`${t}:output:${r}:${i??`na`}`}mapResponseStatusToStopReason(e){return e&&{completed:`end_turn`,completed_with_error:`error`,completed_with_streaming_error:`error`,cancelled:`error`,errored:`error`}[e]||`end_turn`}convertNonStreamingResponse(e){try{let t=JSON.parse(e);if(t?.error){let e=t.error?.message||t.error?.error||(typeof t.error==`string`?t.error:`Unexpected API error`);return this.createClaudeErrorResponse(e)}let n=t?.response&&typeof t.response==`object`?t.response:t;if(typeof n?.type==`string`?n.type.startsWith(`response`):Array.isArray(n?.output)||n?.output_text!==void 0){let e={textSegments:[],thinkingSegments:[],toolCalls:new Map,streamedReasoningKeys:new Set,streamedTextKeys:new Set,model:`gpt-5`,inputTokens:0,outputTokens:0,cachedTokens:0,reasoningTokens:0,reasoningEffort:void 0,stopReason:void 0,errorMessage:void 0};this.collectCompletedResponse({response:n},e);let t=this.createClaudeStreamFromParsed(e);return this.ensureValidClaudeStream(t,`Empty response from provider`)}let r=t.choices?.[0];if(!r)return this.createClaudeErrorStream(`Empty response from provider`);let i={textSegments:[],thinkingSegments:[],toolCalls:new Map,streamedReasoningKeys:new Set,streamedTextKeys:new Set,model:t.model||`gpt-4-turbo`,inputTokens:t.usage?.prompt_tokens||0,outputTokens:t.usage?.completion_tokens||0,cachedTokens:0,reasoningTokens:0,reasoningEffort:void 0,stopReason:this.mapFinishReason(r.finish_reason),errorMessage:void 0};this.collectTextFromNode(r.message?.content,i.textSegments);let a=this.createClaudeStreamFromParsed(i);return this.ensureValidClaudeStream(a,`Empty response from provider`)}catch(t){this.logger?.error(`Failed to transform OpenAI response to Claude format`,t);let n=typeof e==`string`&&e.trim()?e.trim():`Unexpected API error`;return this.createClaudeErrorStream(n)}}mapFinishReason(e){return e?{stop:`end_turn`,length:`max_tokens`,function_call:`tool_use`,tool_calls:`tool_use`,content_filter:`stop_sequence`}[e]||`end_turn`:null}createClaudeErrorResponse(e){let t=`msg_${(0,g.ulid)()}`,n=e||`Unexpected API error`;return JSON.stringify({id:t,type:`message`,role:`assistant`,content:[{type:`text`,text:n}],model:`gpt-5`,stop_reason:`error`,stop_sequence:null,usage:{input_tokens:0,output_tokens:0}})}createClaudeErrorStream(e){let t=`msg_${(0,g.ulid)()}`,n=e||`Unexpected API error`,r=[];return r.push(`event: message_start`),r.push(`data: ${JSON.stringify({type:`message_start`,message:{id:t,type:`message`,role:`assistant`,content:[],model:`gpt-5`,stop_reason:null,stop_sequence:null,usage:{input_tokens:0,output_tokens:0}}})}`),r.push(``),r.push(`event: content_block_start`),r.push(`data: ${JSON.stringify({type:`content_block_start`,index:0,content_block:{type:`text`,text:``}})}`),r.push(``),r.push(`event: content_block_delta`),r.push(`data: ${JSON.stringify({type:`content_block_delta`,index:0,delta:{type:`text_delta`,text:n}})}`),r.push(``),r.push(`event: content_block_stop`),r.push(`data: ${JSON.stringify({type:`content_block_stop`,index:0})}`),r.push(``),r.push(`event: message_delta`),r.push(`data: ${JSON.stringify({type:`message_delta`,delta:{stop_reason:`error`,stop_sequence:null},usage:{output_tokens:0}})}`),r.push(``),r.push(`event: message_stop`),r.push(`data: {"type":"message_stop"}`),r.push(``),r.join(`
9
+ `)}ensureValidClaudeStream(e,t){return!e||!e.includes(`event: message_start`)?this.createClaudeErrorStream(t):e}processBufferedEvents(e,t,n,r=!1){let i=e.replace(/\r\n/g,`
10
+ `).split(`
11
+
12
+ `),a=r?``:i.pop()??``;for(let e of i)this.processSseChunk(e,t,n);return r&&a.trim()&&this.processSseChunk(a,t,n),a}processSseChunk(e,t,n){let r=e.split(`
13
+ `),i=``,a=[];for(let e of r){let t=e.replace(/\r$/,``);t.startsWith(`event:`)?i=t.slice(6).trim():t.startsWith(`data:`)&&a.push(t.slice(5).trim())}let o=a.join(`
14
+ `).trim();if(!(!o||o===`[DONE]`))try{let e=JSON.parse(o),r=typeof e?.type==`string`?e.type:i,a=r.startsWith(`response.`)||i.startsWith(`response.`);if(e?.error||r===`response.error`){let r=e?.error?.message||e?.error?.error||e?.message||(typeof e?.error==`string`?e.error:`Unexpected API error`);this.emitStreamingError(n,t,r);return}if(a){this.processResponsesStreamingEvent(r,e,t,n);return}this.processChatCompletionStreamingChunk(e,t,n)}catch{}}processResponsesStreamingEvent(e,t,n,r){switch(t?.model&&typeof t.model==`string`&&(n.model=t.model),e){case`response.created`:typeof t?.response?.model==`string`&&(n.model=t.response.model);break;case`response.reasoning.delta`:case`response.reasoning_summary_text.delta`:{let e=[];this.markStreamedContentKey(t,n.streamedReasoningKeys,`reasoning`),this.collectTextFromNode(t?.delta??t?.text??t?.summary_text,e);for(let t of e)this.emitThinkingDelta(r,n,t);break}case`response.output_text.delta`:case`response.delta`:{let e=[];this.markStreamedContentKey(t,n.streamedTextKeys,`text`),this.collectTextFromNode(t?.delta??t?.output_text,e);for(let t of e)this.emitTextDelta(r,n,t);t?.delta?.tool_calls&&this.collectToolCalls(t.delta.tool_calls,n.toolCalls);break}case`response.function_call_arguments.delta`:case`response.function_call_arguments.done`:case`response.output_item.added`:case`response.output_item.done`:if(t?.item?.type===`message`){if(this.shouldEmitTerminalContent(t,n.streamedTextKeys,`text`,n.emittedText)){let e=[];this.collectTextFromNode(t.item.content,e);let i=e.join(``);if(i&&i===n.lastTextChunk)break;for(let t of e)this.emitTextDelta(r,n,t)}}else if(t?.item?.type===`reasoning`){if(this.shouldEmitTerminalContent(t,n.streamedReasoningKeys,`reasoning`,n.emittedThinking)){let e=[];this.collectTextFromNode(t.item.summary??t.item.content,e);let i=e.join(``);if(i&&i===n.lastThinkingChunk)break;for(let t of e)this.emitThinkingDelta(r,n,t)}}else this.collectResponsesToolCallDelta(t,n.toolCalls);break;case`response.output_text.done`:case`response.content_part.done`:if(n.textBlockStarted)break;if(this.shouldEmitTerminalContent(t,n.streamedTextKeys,`text`,n.emittedText)){let e=[];this.collectTextFromNode(t?.delta??t?.output_text??t?.text??t?.part,e);let i=e.join(``);if(i&&i===n.lastTextChunk)break;for(let t of e)this.emitTextDelta(r,n,t)}break;case`response.completed`:case`response.done`:{let e={textSegments:[],thinkingSegments:[],toolCalls:new Map(n.toolCalls),streamedReasoningKeys:new Set(n.streamedReasoningKeys),streamedTextKeys:new Set(n.streamedTextKeys),model:n.model,inputTokens:n.inputTokens,outputTokens:n.outputTokens,cachedTokens:n.cachedTokens,reasoningTokens:n.reasoningTokens,reasoningEffort:n.reasoningEffort,stopReason:n.stopReason};if(this.collectCompletedResponse(t,e),n.model=e.model,n.inputTokens=e.inputTokens,n.outputTokens=e.outputTokens,n.cachedTokens=e.cachedTokens,n.reasoningTokens=e.reasoningTokens,n.reasoningEffort=e.reasoningEffort,n.stopReason=e.stopReason,n.toolCalls=e.toolCalls,!n.emittedThinking)for(let t of e.thinkingSegments)this.emitThinkingDelta(r,n,t);if(!n.emittedText)for(let t of e.textSegments)this.emitTextDelta(r,n,t);this.emitPendingToolCalls(r,n),this.finishStreamingResponse(r,n);break}default:break}}processChatCompletionStreamingChunk(e,t,n){if(e&&(typeof e.model==`string`&&(t.model=e.model),e.usage&&(t.inputTokens=e.usage.prompt_tokens??t.inputTokens,t.outputTokens=e.usage.completion_tokens??t.outputTokens),Array.isArray(e.choices)))for(let r of e.choices){let e=[];this.collectTextFromNode(r?.delta??r?.message?.content,e);for(let r of e)this.emitTextDelta(n,t,r);r?.delta?.tool_calls&&this.collectToolCalls(r.delta.tool_calls,t.toolCalls),r?.finish_reason&&(t.stopReason=this.mapFinishReason(r.finish_reason))}}emitPendingToolCalls(e,t){let n=Array.from(t.toolCalls.entries()).sort((e,t)=>e[0]-t[0]).map(([e,t])=>({index:e,id:t.id||`tool_${(0,g.ulid)()}`,name:t.name,arguments:t.argumentChunks.join(``)})).filter(e=>e.name&&!t.emittedToolCallIndexes.has(e.index));for(let r of n){this.stopOpenContentBlocks(e,t);let n=this.getNextToolBlockIndex(r.index);e.enqueue(t.encoder.encode(this.formatSseEvent(`content_block_start`,{type:`content_block_start`,index:n,content_block:{type:`tool_use`,id:r.id,name:r.name,input:{}}})+(r.arguments?this.formatSseEvent(`content_block_delta`,{type:`content_block_delta`,index:n,delta:{type:`input_json_delta`,partial_json:r.arguments}}):``)+this.formatSseEvent(`content_block_stop`,{type:`content_block_stop`,index:n}))),t.emittedToolCallIndexes.add(r.index)}}emitThinkingDelta(e,t,n){this.thinkingDisabled||!n||(this.ensureMessageStarted(e,t),t.textBlockStarted&&this.stopTextBlock(e,t),t.thinkingBlockStarted||=(e.enqueue(t.encoder.encode(this.formatSseEvent(`content_block_start`,{type:`content_block_start`,index:0,content_block:{type:`thinking`,thinking:``}}))),!0),e.enqueue(t.encoder.encode(this.formatSseEvent(`content_block_delta`,{type:`content_block_delta`,index:0,delta:{type:`thinking_delta`,thinking:n}}))),t.emittedThinking=!0,t.lastThinkingChunk=n)}emitTextDelta(e,t,n){n&&(this.ensureMessageStarted(e,t),t.textBlockStarted||=(t.thinkingBlockStarted&&this.stopThinkingBlock(e,t),e.enqueue(t.encoder.encode(this.formatSseEvent(`content_block_start`,{type:`content_block_start`,index:this.getTextBlockIndex(),content_block:{type:`text`,text:``}}))),!0),e.enqueue(t.encoder.encode(this.formatSseEvent(`content_block_delta`,{type:`content_block_delta`,index:this.getTextBlockIndex(),delta:{type:`text_delta`,text:n}}))),t.emittedText=!0,t.lastTextChunk=n)}emitStreamingError(e,t,n){if(!t.messageStarted){e.enqueue(t.encoder.encode(this.createClaudeErrorStream(n))),t.messageStarted=!0,t.messageStopped=!0;return}!t.textBlockStarted&&!t.thinkingBlockStarted&&this.emitTextDelta(e,t,n),t.stopReason=`error`,this.finishStreamingResponse(e,t)}finishStreamingResponse(e,t){if(t.messageStopped)return;this.stopOpenContentBlocks(e,t);let n={output_tokens:t.outputTokens??0};if(t.cachedTokens||t.reasoningTokens||t.reasoningEffort){n.metadata={};let e=n.metadata;t.cachedTokens&&(e.cached_tokens=t.cachedTokens),t.reasoningTokens&&(e.reasoning_tokens=t.reasoningTokens),t.reasoningEffort&&(e.reasoning_effort=t.reasoningEffort)}e.enqueue(t.encoder.encode(this.formatSseEvent(`message_delta`,{type:`message_delta`,delta:{stop_reason:t.stopReason||`end_turn`,stop_sequence:null},usage:n})+this.formatSseEvent(`message_stop`,{type:`message_stop`}))),t.messageStopped=!0}ensureMessageStarted(e,t){t.messageStarted||=(e.enqueue(t.encoder.encode(this.formatSseEvent(`message_start`,{type:`message_start`,message:{id:t.messageId,type:`message`,role:`assistant`,content:[],model:t.model,stop_reason:null,stop_sequence:null,usage:{input_tokens:t.inputTokens??0,output_tokens:0}}}))),!0)}stopOpenContentBlocks(e,t){this.stopTextBlock(e,t),this.stopThinkingBlock(e,t)}stopThinkingBlock(e,t){t.thinkingBlockStarted&&=(e.enqueue(t.encoder.encode(this.formatSseEvent(`content_block_stop`,{type:`content_block_stop`,index:0}))),!1)}stopTextBlock(e,t){t.textBlockStarted&&=(e.enqueue(t.encoder.encode(this.formatSseEvent(`content_block_stop`,{type:`content_block_stop`,index:this.getTextBlockIndex()}))),!1)}getTextBlockIndex(){return this.thinkingDisabled?0:1}getNextToolBlockIndex(e){return(this.thinkingDisabled?1:2)+e}};const Ie=`GEMINI_API_KEY`,Le=`GEMINI_CLI_HOME`,Re=`v1beta`,ze=`auto`,Be=`oauth-personal`,Ve=`https://cloudcode-pa.googleapis.com`,He=`v1internal`,Ue=`settings.json`,We=`oauth_creds.json`,Ge=`user`,Ke=`model`,qe=`models/`,Je=4096,Ye=2048,Xe=`AUTO`,Ze=`NONE`,Qe=new Set([`type`,`format`,`description`,`nullable`,`enum`,`items`,`maxItems`,`minItems`,`properties`,`required`,`propertyOrdering`,`maxProperties`,`minProperties`,`minimum`,`maximum`,`minLength`,`maxLength`,`pattern`,`example`,`anyOf`,`title`]);var $e=class{transform(e,t,n,r=!1){let i=this.toGeminiContents(e.messages),a=this.toSystemInstruction(e.system),o=this.toGeminiTools(e.tools);return{modelPath:this.toGeminiModelPath(t),body:{contents:i,system_instruction:a,tools:o,tool_config:{function_calling_config:{mode:o.length>0?`AUTO`:`NONE`}},generationConfig:{maxOutputTokens:this.resolveMaxOutputTokens(e.max_tokens,n,r)}}}}toGeminiModelPath(e){return e.startsWith(qe)?e:`${qe}${e}`}toSystemInstruction(e){let t=this.extractTextParts(e);if(t.length!==0)return{parts:t.map(e=>({text:e}))}}toGeminiContents(e){let t=[];for(let n of e){let e=this.toGeminiParts(n.content);e.length!==0&&t.push({role:n.role===`assistant`?`model`:`user`,parts:e})}return t}toGeminiParts(e){if(typeof e==`string`)return e.trim()?[{text:e}]:[];if(!Array.isArray(e))return[];let t=[];for(let n of e)n.type===`text`&&n.text&&t.push({text:n.text}),n.type===`tool_use`&&t.push({functionCall:{name:n.name,args:n.input}}),n.type===`tool_result`&&t.push({functionResponse:{name:n.tool_use_id,response:{content:typeof n.content==`string`?n.content:JSON.stringify(n.content??{})}}});return t}toGeminiTools(e){return!e||e.length===0?[]:[{function_declarations:e.map(e=>({name:e.name,description:e.description,parameters:this.toGeminiParameters(e.input_schema)}))}]}toGeminiParameters(e){if(e)return this.sanitizeGeminiSchema(e)}sanitizeGeminiSchema(e){if(Array.isArray(e))return e.map(e=>this.sanitizeGeminiSchema(e));if(!e||typeof e!=`object`)return e;let t=Object.entries(e).flatMap(([e,t])=>{if(!Qe.has(e))return[];if(e===`properties`&&t&&typeof t==`object`&&!Array.isArray(t)){let n=Object.entries(t).map(([e,t])=>[e,this.sanitizeGeminiSchema(t)]);return[[e,Object.fromEntries(n)]]}return[[e,this.sanitizeGeminiSchema(t)]]});return Object.fromEntries(t)}extractTextParts(e){return typeof e==`string`?e.trim()?[e]:[]:Array.isArray(e)?e.map(e=>typeof e==`string`?e:e&&typeof e==`object`&&`text`in e&&typeof e.text==`string`?e.text:null).filter(e=>!!e?.trim()):[]}resolveMaxOutputTokens(e,t,n){return typeof e==`number`&&e>0?e:n||t===`minimal`?2048:4096}};const et=`.gemini`,tt=`Authorization`,nt=`x-goog-api-key`,rt=`Bearer `,j=`GEMINI_AUTH_CONFIG_ERROR`,it=`https://oauth2.googleapis.com/token`,at=`refresh_token`,ot=`681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com`,st=`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`,ct=6e4;var M=class extends Error{constructor(e,t){super(e),this.code=t,this.name=`GeminiAuthError`}},lt=class{constructor(e=b,t=process.env){this.logger=e,this.env=t}async resolveHeaders(e){let t=e.authMode??`auto`;return t===`api-key`?this.resolveApiKeyHeaders(e):t===`oauth`?this.resolveOAuthHeaders():await this.tryResolveApiKeyHeaders(e)??this.resolveOAuthHeaders()}getGeminiDirectory(){let e=this.env.GEMINI_CLI_HOME||p.default.homedir();return l.default.join(e,`.gemini`)}getSettingsPath(){return l.default.join(this.getGeminiDirectory(),`settings.json`)}getOAuthPath(){return l.default.join(this.getGeminiDirectory(),`oauth_creds.json`)}async tryResolveApiKeyHeaders(e){try{return await this.resolveApiKeyHeaders(e)}catch{return null}}async resolveApiKeyHeaders(e){let t=e.apiKeyEnvVar??e.authTokenEnvVar??Ie,n=this.env[t]?.trim();if(!n)throw new M(`Missing Gemini API key. Set ${t}.`,j);return{headers:{"x-goog-api-key":n},authMode:`api-key`,authSource:`env`}}async resolveOAuthHeaders(){let e=await this.readSettings(),t=await this.readOAuthCredentials(),n=await this.refreshOAuthCredentialsIfNeeded(t),r=e?.security?.auth?.selectedType??null,i=n.access_token?.trim();if(!i)throw new M(`Missing Gemini OAuth credentials. Expected ${this.getOAuthPath()} or ${Ie}.`,j);return{headers:{Authorization:`Bearer ${i}`},authMode:`oauth`,authSource:`gemini-home`,authType:r}}async readOAuthCredentials(){try{let e=await f.default.readFile(this.getOAuthPath(),`utf8`);return JSON.parse(e)}catch(e){throw this.logger.warn(`[GeminiAuth] Failed to read oauth credentials`,{oauthPath:this.getOAuthPath(),cause:e}),new M(`Unable to read Gemini OAuth credentials from ${this.getOAuthPath()}.`,j)}}async readSettings(){try{let e=await f.default.readFile(this.getSettingsPath(),`utf8`);return JSON.parse(e)}catch(e){return e?.code===`ENOENT`||this.logger.warn(`[GeminiAuth] Failed to read settings`,{settingsPath:this.getSettingsPath(),cause:e}),null}}async refreshOAuthCredentialsIfNeeded(e){if(!this.shouldRefreshCredentials(e))return e;let t=e.refresh_token?.trim();if(!t)return e;let n=new URLSearchParams({client_id:`681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com`,client_secret:`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`,grant_type:`refresh_token`,refresh_token:t}),r=await fetch(`https://oauth2.googleapis.com/token`,{method:`POST`,headers:{"content-type":`application/x-www-form-urlencoded`},body:n});if(!r.ok)return this.logger.warn(`[GeminiAuth] Failed to refresh oauth credentials`,{status:r.status,oauthPath:this.getOAuthPath()}),e;let i=await r.json(),a={...e,access_token:i.access_token??e.access_token,token_type:i.token_type??e.token_type,scope:i.scope??e.scope,expiry_date:typeof i.expires_in==`number`?Date.now()+i.expires_in*1e3:e.expiry_date};return await f.default.writeFile(this.getOAuthPath(),`${JSON.stringify(a,null,2)}\n`,`utf8`),a}shouldRefreshCredentials(e){let t=e.access_token?.trim(),n=e.expiry_date;return t?typeof n==`number`?n<=Date.now()+6e4:!1:!0}};const N=`event: `,P=`data: `,ut=`message_start`,dt=`content_block_start`,ft=`content_block_delta`,pt=`content_block_stop`,mt=`message_delta`,ht=`message_stop`,gt=`text`,_t=`text_delta`,vt=`end_turn`,yt=`tool_use`,bt=`[DONE]`;var xt=class{transformBuffered(e,t){let n=JSON.parse(e);return this.toClaudeStream([n],t)}transformStreaming(e,t){let n=[];for(let t of e.split(`
15
+
16
+ `)){let e=t.split(`
17
+ `).find(e=>e.startsWith(P))?.slice(6).trim();!e||e===`[DONE]`||n.push(JSON.parse(e))}return this.toClaudeStream(n,t)}toClaudeStream(e,t){let n=e.flatMap(e=>e.candidates??[]).flatMap(e=>e.content?.parts??[]).map(e=>`text`in e&&typeof e.text==`string`?e.text:``).join(``),r=e.map(e=>e.candidates?.[0]?.finishReason).find(e=>typeof e==`string`),i=e.find(e=>e.usageMetadata)?.usageMetadata,a=`msg_${(0,g.ulid)()}`,o=[];return o.push(`${N}${ut}`),o.push(`${P}${JSON.stringify({type:ut,message:{id:a,type:`message`,role:`assistant`,content:[],model:t,stop_reason:null,stop_sequence:null,usage:{input_tokens:i?.promptTokenCount??0,output_tokens:0}}})}`),o.push(``),o.push(`${N}${dt}`),o.push(`${P}${JSON.stringify({type:dt,index:0,content_block:{type:`text`,text:``}})}`),o.push(``),n&&(o.push(`${N}${ft}`),o.push(`${P}${JSON.stringify({type:ft,index:0,delta:{type:`text_delta`,text:n}})}`),o.push(``)),o.push(`${N}${pt}`),o.push(`${P}${JSON.stringify({type:pt,index:0})}`),o.push(``),o.push(`${N}${mt}`),o.push(`${P}${JSON.stringify({type:mt,delta:{stop_reason:this.mapStopReason(r),stop_sequence:null},usage:{output_tokens:i?.candidatesTokenCount??0}})}`),o.push(``),o.push(`${N}${ht}`),o.push(`${P}${JSON.stringify({type:ht})}`),o.push(``),o.join(`
18
+ `)}mapStopReason(e){return e?e===`STOP`?vt:`tool_use`:vt}};const St=50,Ct=1,wt=200,F=`conversation_history`,Tt=`conversation_history_scope_sequence_idx`,Et=`conversation_history_scope_request_idx`,I={appendFailed:`HISTORY_APPEND_FAILED`,listFailed:`HISTORY_LIST_FAILED`,clearFailed:`HISTORY_CLEAR_FAILED`,statsFailed:`HISTORY_STATS_FAILED`,initFailed:`HISTORY_INIT_FAILED`},Dt=h.z.string().transform(e=>Number(e)).refine(e=>Number.isInteger(e)&&e>0,`Cursor must be a positive integer`);var L=class extends Error{constructor(e,t,n){super(e,n),this.code=t,this.name=`ConversationHistoryServiceError`}},Ot=class{sqlite=null;constructor(e=process.env.MODEL_PROXY_MCP_DB_PATH||ce,t=1e3,n=b){this.dbPath=e,this.retentionLimit=t,this.logger=n}async ensureInitialized(){await this.getDb()}async appendEntries(e){if(e.length!==0)try{let t=await this.getDb(),n=t.prepare(`
19
+ INSERT INTO conversation_history (
20
+ id, scope, request_id, direction, role, message_type, model, slot, payload_json, created_at
21
+ ) VALUES (
22
+ @id, @scope, @request_id, @direction, @role, @message_type, @model, @slot, @payload_json, @created_at
23
+ )
24
+ `),r=t.transaction(e=>{for(let t of e)n.run({id:t.id??(0,g.ulid)(),scope:t.scope,request_id:t.requestId,direction:t.direction,role:t.role,message_type:t.messageType,model:t.model,slot:t.slot,payload_json:t.payloadJson,created_at:t.createdAt??new Date().toISOString()})}),i=new Map;for(let t of e){let e=i.get(t.scope)??[];e.push(t),i.set(t.scope,e)}for(let e of i.values())r(e);for(let e of i.keys())await this.pruneScope(e)}catch(e){throw this.logger.error(`[model-proxy-mcp] Failed to append history`,{code:I.appendFailed,dbPath:this.dbPath,cause:e}),new L(`Failed to append conversation history`,I.appendFailed,{cause:e})}}async listHistory(e,t=50,n){try{let r=await this.getDb(),i=n?Dt.parse(n):void 0,a=Math.max(1,Math.min(t,200)),o=i?r.prepare(`
25
+ SELECT sequence, id, scope, request_id, direction, role, message_type, model, slot, payload_json, created_at
26
+ FROM ${F}
27
+ WHERE scope = ? AND sequence < ?
28
+ ORDER BY sequence DESC
29
+ LIMIT ?
30
+ `).all(e,i,a+1):r.prepare(`
31
+ SELECT sequence, id, scope, request_id, direction, role, message_type, model, slot, payload_json, created_at
32
+ FROM ${F}
33
+ WHERE scope = ?
34
+ ORDER BY sequence DESC
35
+ LIMIT ?
36
+ `).all(e,a+1),s=Number(r.prepare(`SELECT COUNT(*) as count FROM ${F} WHERE scope = ?`).get(e).count),c=o.length>a?o.pop():void 0;return{items:o.map(e=>this.toEntry(e)),nextCursor:c?String(c.sequence):null,total:s}}catch(t){throw this.logger.error(`[model-proxy-mcp] Failed to list history`,{code:I.listFailed,scope:e,cause:t}),new L(`Failed to list conversation history`,I.listFailed,{cause:t})}}async clearScope(e){try{return(await this.getDb()).prepare(`DELETE FROM ${F} WHERE scope = ?`).run(e).changes}catch(t){throw this.logger.error(`[model-proxy-mcp] Failed to clear history`,{code:I.clearFailed,scope:e,cause:t}),new L(`Failed to clear conversation history`,I.clearFailed,{cause:t})}}async getStats(e){try{let t=(await this.getDb()).prepare(`
37
+ SELECT COUNT(*) as count, MIN(created_at) as oldest, MAX(created_at) as newest
38
+ FROM ${F}
39
+ WHERE scope = ?
40
+ `).get(e);return{scope:e,retentionLimit:this.retentionLimit,totalMessages:Number(t.count),oldestCreatedAt:t.oldest,newestCreatedAt:t.newest}}catch(t){throw this.logger.error(`[model-proxy-mcp] Failed to read history stats`,{code:I.statsFailed,scope:e,cause:t}),new L(`Failed to read history stats`,I.statsFailed,{cause:t})}}async listScopes(){try{return(await this.getDb()).prepare(`SELECT DISTINCT scope FROM ${F} ORDER BY scope ASC`).all().map(e=>e.scope)}catch(e){return this.logger.error(`[model-proxy-mcp] Failed to list history scopes`,{code:I.listFailed,cause:e}),[]}}async pruneScope(e){let t=await this.getDb(),n=t.prepare(`SELECT COUNT(*) as count FROM ${F} WHERE scope = ?`).get(e),r=Number(n.count)-this.retentionLimit;r<=0||t.prepare(`
41
+ DELETE FROM ${F}
42
+ WHERE sequence IN (
43
+ SELECT sequence
44
+ FROM ${F}
45
+ WHERE scope = ?
46
+ ORDER BY sequence ASC
47
+ LIMIT ?
48
+ )
49
+ `).run(e,r)}async getDb(){if(this.sqlite)return this.sqlite;try{return await f.default.mkdir(l.default.dirname(this.dbPath),{recursive:!0}),this.sqlite=new _.default(this.dbPath),this.sqlite.pragma(`journal_mode = WAL`),this.sqlite.exec(`
50
+ CREATE TABLE IF NOT EXISTS ${F} (
51
+ sequence INTEGER PRIMARY KEY AUTOINCREMENT,
52
+ id TEXT NOT NULL UNIQUE,
53
+ scope TEXT NOT NULL,
54
+ request_id TEXT NOT NULL,
55
+ direction TEXT NOT NULL,
56
+ role TEXT,
57
+ message_type TEXT NOT NULL,
58
+ model TEXT,
59
+ slot TEXT NOT NULL,
60
+ payload_json TEXT NOT NULL,
61
+ created_at TEXT NOT NULL
62
+ )
63
+ `),this.sqlite.exec(`CREATE INDEX IF NOT EXISTS conversation_history_scope_sequence_idx ON ${F} (scope, sequence DESC)`),this.sqlite.exec(`CREATE INDEX IF NOT EXISTS conversation_history_scope_request_idx ON ${F} (scope, request_id)`),this.sqlite}catch(e){throw this.logger.error(`[model-proxy-mcp] Failed to initialize history database`,{code:I.initFailed,dbPath:this.dbPath,cause:e}),new L(`Failed to initialize history database`,I.initFailed,{cause:e})}}toEntry(e){return{id:e.id,scope:e.scope,requestId:e.request_id,direction:e.direction,role:e.role,messageType:e.message_type,model:e.model,slot:e.slot,payloadJson:e.payload_json,createdAt:e.created_at}}};const kt={"ccproxy-default":`default`,"ccproxy-sonnet":`sonnet`,"ccproxy-opus":`opus`,"ccproxy-haiku":`haiku`,"ccproxy-subagent":`subagent`},R=`default`,z=`default`,At=50,jt=[`default`,`sonnet`,`opus`,`haiku`,`subagent`],Mt=`https://api.anthropic.com/v1/messages`,Nt=`2023-06-01`,Pt=`anthropic-compatible`,Ft=`chatgpt-codex`,It=`gemini-direct`,Lt=`ANTHROPIC_AUTH_TOKEN`,Rt=`MODEL_PROXY_MCP_UPSTREAM_AUTH_TOKEN`,zt=`MODEL_PROXY_MCP_UPSTREAM_TIMEOUT_MS`,Bt=`MODEL_PROXY_MCP_UPSTREAM_AUTH_ENV`,Vt=`API_TIMEOUT_MS`,Ht=`application/json; charset=utf-8`,B=`application/json`,V=`text/event-stream; charset=utf-8`,H=`content-type`,U=`cache-control`,Ut=`connection`,Wt=`authorization`,Gt=`x-api-key`,W=`accept`,Kt=`anthropic-version`,qt=`anthropic-beta`,Jt=`no-cache`,Yt=`keep-alive`,Xt=`api_error`,Zt=`raw-request`,Qt=`response-stream`,$t=`error`,en=`request`,tn=`response`,nn=`error`,rn=`system`,an=`assistant`,on=`message`,sn=`response-item`,G=`MODEL_NOT_FOUND`,K=`REQUEST_FORWARD_FAILED`,cn=`REQUEST_VALIDATION_FAILED`,ln=240,un=200,dn=`generateContent`,fn=`loadCodeAssist`,pn=`IDE_UNSPECIFIED`,mn=`PLATFORM_UNSPECIFIED`,hn=`GEMINI`,gn=h.z.object({model:h.z.string().optional(),system:h.z.unknown().optional(),messages:h.z.array(h.z.unknown()).default([]),tools:h.z.array(h.z.unknown()).optional(),max_tokens:h.z.number().int().positive().optional(),stream:h.z.boolean().optional()});var q=class extends Error{constructor(e,t,n,r){super(e,r),this.code=t,this.status=n,this.name=new.target.name}},_n=class extends q{},J=class extends q{},Y=class extends q{},X=class extends q{constructor(e,t,n,r,i){super(e,K,t,i),this.upstreamBody=n,this.attemptLabel=r}};const vn=500,yn=new Set([408,429,500,502,503,504]);var bn=class{codeAssistProjectCache=new Map;constructor(e=new ke,t=new Pe(b,le),n=b,r=fetch,i=new Ot){this.profileStore=e,this.codexAuth=t,this.logger=n,this.fetchImpl=r,this.historyService=i}async listProfiles(e=R){return this.profileStore.listProfiles(e)}async ensureConfig(e=R){return await this.historyService.ensureInitialized(),this.profileStore.ensureConfig(e)}async getAdminConfig(e=R){return this.profileStore.getAdminConfig(e)}async updateAdminConfig(e,t=R){return await this.profileStore.updateConfig(e,t),this.profileStore.getAdminConfig(t)}async getActiveProfile(e=R,t=z){return this.profileStore.getActiveProfile(e,t)}async upsertProfile(e,t=R){return this.profileStore.upsertProfile(e,t)}async setActiveProfile(e,t=R,n=z){return this.profileStore.setActiveProfile(e,t,n)}async getCurrentModel(e=R,t=z){return this.profileStore.getActiveProfile(e,t)}async switchModel(e,t=R,n=z){let r=await this.profileStore.listProfiles(t),i=await this.profileStore.getResolvedSlotConfig(t,n),a=r.find(t=>t.model===e&&t.provider===i.provider&&t.enabled)??r.find(t=>t.model===e&&t.enabled);if(!a)throw new J(`Model not found or disabled: ${e}`,G,400,{cause:{model:e,scope:t,slot:n}});return this.profileStore.updateConfig({models:{[n]:{main:{provider:a.provider,model:a.model,reasoningEffort:a.reasoningEffort},fallbacks:(await this.profileStore.getAdminConfig(t)).scopeModels[n]?.fallbacks??[]}}},t)}async listHistory(e=R,t=50,n){return this.historyService.listHistory(e,t,n)}async clearHistory(e=R){return{deleted:await this.historyService.clearScope(e)}}async listScopes(){let[e,t]=await Promise.all([this.profileStore.listScopes(),this.historyService.listScopes()]);return Array.from(new Set([...e,...t,R])).sort()}async getHistoryStats(e=R){return this.historyService.getStats(e)}async getStatus(e=R,t,n){let r=await this.profileStore.getConfig(e),i=await this.profileStore.getResolvedSlotConfig(e,z),a=await this.codexAuth.getAuthStatus(),o=Object.fromEntries(await Promise.all(jt.map(async t=>{let n=await this.profileStore.getResolvedSlotConfig(e,t);return[t,{profileId:n.profileId,provider:n.provider,model:n.model,reasoningEffort:n.reasoningEffort,thinkingDisabled:n.thinkingDisabled??!1}]})));return{running:!0,port:t,pid:n,scope:e,activeProfileId:r.activeProfileId,activeModel:i.model??void 0,activeReasoningEffort:i.reasoningEffort,slotModels:o,auth:a,profiles:r.profiles}}async getModels(e=R){return{object:`list`,data:(await this.profileStore.listProfiles(e)).map(e=>({id:e.model,object:`model`,created:0,owned_by:`${e.provider}:${e.id}`,metadata:{profileId:e.id,reasoningEffort:e.reasoningEffort}}))}}async forwardClaudeRequest(e,t=R,n=new Headers){let r=(0,g.ulid)(),i=z,a=null;try{let o=this.parseClaudeRequest(e),s=await this.profileStore.getAdminConfig(t);if(i=this.resolveSlot(o.model,s),a=this.resolveRequestedModel(o.model,s,i),!a.model){let n=`No active model configured for scope '${t}' and slot '${i}'`;return await this.recordError(t,i,a,r,n,e),this.createErrorResponse(400,n)}let c=this.buildAttemptTargets(s,a,i);if(c.length===0){let n=`No provider configured for scope '${t}' and slot '${i}'`;return await this.recordError(t,i,a,r,n,e),this.createErrorResponse(400,n)}await this.recordRequest(t,i,c[0].resolved,r,o,e);let l=null;for(let a of c){let s=await this.forwardSingleAttempt(a,o,e,t,i,n);if(s.ok)return await this.recordResponse(t,i,a.resolved,r,s.success.history.upstreamText,s.success.history.claudeBody),s.success.response;let{error:c,payloadForHistory:u}=s.failure;if(!this.isRecoverableUpstreamError(c))return this.logger.error(`[model-proxy-mcp] Non-recoverable model attempt failed`,{scope:t,slot:i,model:a.resolved.model,code:c.code,status:c.status,message:c.message}),await this.recordError(t,i,a.resolved,r,c.message,u),this.createErrorResponse(c.status,c.message);l=s.failure,this.logger.warn(`[model-proxy-mcp] Recoverable model attempt failed; trying fallback`,{scope:t,slot:i,model:a.resolved.model,code:c.code,status:c.status,message:c.message}),await this.recordError(t,i,a.resolved,r,`Attempt failed for ${a.label}: ${c.message}`,u)}if(l)return await this.recordError(t,i,a,r,l.error.message,l.payloadForHistory),this.createErrorResponse(l.error.status,l.error.message);let u=`No model attempt could be executed`;return await this.recordError(t,i,a,r,u,e),this.createErrorResponse(500,u)}catch(n){let o=n instanceof q?n:new q(`Failed to process Claude proxy request`,K,500,{cause:n});return this.logger.error(`[model-proxy-mcp] Request forwarding failed`,{scope:t,slot:i,code:o.code,message:o.message,cause:o.cause}),await this.recordError(t,i,a,r,o.message,e),this.createErrorResponse(o.status,o.message)}}buildAttemptTargets(e,t,n){let r=[],i=new Set,a=[t,...t.fallbacks.map(e=>({...t,...e,slot:n}))];for(let t of a){let a=t.provider,o=t.model;if(!a||!o)continue;let s=e.providers[a];if(!s)continue;let c=`${a}:${o}:${t.reasoningEffort}:${t.thinkingDisabled?`off`:`on`}`;i.has(c)||(i.add(c),r.push({resolved:{...t,slot:n,provider:a,model:o,providerType:s.type,endpoint:s.endpoint,reasoningEffort:t.reasoningEffort,thinkingDisabled:t.thinkingDisabled??!1,fallbacks:[]},provider:s,label:`${a}/${o}`}))}return r}async forwardSingleAttempt(e,t,n,r,i,a){try{return e.resolved.providerType===Pt?{ok:!0,success:await this.forwardAnthropicCompatibleRequest(n,r,i,e.resolved,e.provider,a)}:e.resolved.providerType===`gemini-direct`?{ok:!0,success:await this.forwardGeminiDirectRequest(t,r,i,e.resolved,e.provider,a)}:{ok:!0,success:await this.forwardCodexRequest(n,r,i,e.resolved,e.provider)}}catch(e){return{ok:!1,failure:{error:e instanceof q?e:new q(`Failed to process Claude proxy request`,K,500,{cause:e}),payloadForHistory:e instanceof X?e.upstreamBody:n}}}}isRecoverableUpstreamError(e){return e instanceof X&&yn.has(e.status)}createCodexSessionId(e,t,n){let r=[y,`codex`,e,t,n.provider??`unknown-provider`,n.model??`unknown-model`].join(`:`);return(0,d.createHash)(`sha256`).update(r).digest(`hex`)}createGeminiSessionId(e,t,n){let r=[y,`gemini-code-assist`,e,t,n.provider??`unknown-provider`,n.model??`unknown-model`].join(`:`);return(0,d.createHash)(`sha256`).update(r).digest(`hex`)}createCodeAssistProjectCacheKey(e){let t=process.env.GOOGLE_CLOUD_PROJECT||process.env.GOOGLE_CLOUD_PROJECT_ID||``,n=Object.entries(e.headers).sort(([e],[t])=>e.localeCompare(t));return JSON.stringify({authMode:e.authMode,authSource:e.authSource,authType:e.authType??null,configuredProject:t,headers:n})}async forwardCodexRequest(e,t,n,r,i){let a=r.model;if(!a)throw new J(`No active model configured for slot '${n}'`,G,400);let o=await this.codexAuth.resolveAuth(),s=i.apiKeyEnvVar?process.env[i.apiKeyEnvVar]:void 0;if(!o.accessToken&&!s)throw new Y(`Missing Codex auth. Sign in with the official Codex CLI first.`,`AUTH_MISSING`,401);let c=new Ne({toProvider:`chatgpt-codex`,toModel:a,toEndpoint:r.endpoint??Mt,toApiKey:s,sessionId:this.createCodexSessionId(t,n,r),sessionReasoningEffort:r.reasoningEffort,thinkingDisabled:r.thinkingDisabled??!1,logger:this.logger,resolvedAuth:o}),l=new Fe(this.logger,r.thinkingDisabled??!1),u=await c.transform(Mt,e),d=await this.fetchImpl(u.url,{method:`POST`,headers:u.headers,body:u.body});if(!d.ok){let e=await d.text();throw this.logger.error(`[model-proxy-mcp] Upstream request failed`,{scope:t,slot:n,profileId:r.profileId,model:r.model,status:d.status,body:e}),new X(e||`Codex upstream request failed`,d.status,e,`${r.provider}/${r.model}`)}let f=await d.text(),p=l.transform(f);return{response:{status:200,body:p,headers:this.createSuccessHeaders(t,n)},history:{upstreamText:f,claudeBody:p}}}async forwardGeminiDirectRequest(e,t,n,r,i,a){try{let a=r.model;if(!a)throw new J(`No active Gemini model configured for scope '${t}' and slot '${n}'`,G,400);let o=await new lt(this.logger).resolveHeaders(i),s=new $e,c=new xt,l={model:e.model,system:e.system,messages:e.messages,tools:e.tools,max_tokens:e.max_tokens},u=s.transform(l,a,r.reasoningEffort,r.thinkingDisabled??!1);if(o.authType===`oauth-personal`)return await this.forwardGeminiCodeAssistRequest(u.body,t,n,r,o,c);let d=this.createGeminiRequestUrl(i,u.modelPath,dn),f=new Headers(o.headers);f.set(H,B),f.set(W,B);let p=i.apiTimeoutMs??this.resolveUpstreamTimeoutMs(),m=await this.fetchImpl(d,{method:`POST`,headers:f,body:JSON.stringify(u.body),signal:p?AbortSignal.timeout(p):void 0}),h=await m.text();if(!m.ok)throw this.logger.error(`[model-proxy-mcp] Gemini upstream request failed`,{scope:t,slot:n,profileId:r.profileId,model:r.model,status:m.status,body:h,authMode:o.authMode,authSource:o.authSource}),new X(h||`Gemini upstream request failed`,m.status,h,`${r.provider}/${r.model}`);let ee=c.transformBuffered(h,a);return{response:{status:200,body:ee,headers:this.createSuccessHeaders(t,n)},history:{upstreamText:h,claudeBody:ee}}}catch(e){throw e instanceof M?new Y(e.message,e.code,401,{cause:e}):e}}async forwardGeminiCodeAssistRequest(e,t,n,r,i,a){let o=await this.resolveCodeAssistProjectId(i),s=this.createCodeAssistUrl(dn),c=new Headers(i.headers);c.set(H,B),c.set(W,B);let l={model:r.model,project:o,user_prompt_id:(0,g.ulid)(),request:this.toCodeAssistGenerateContentRequest(e,this.createGeminiSessionId(t,n,r))},u=await this.fetchImpl(s,{method:`POST`,headers:c,body:JSON.stringify(l)}),d=await u.text();if(!u.ok)throw this.logger.error(`[model-proxy-mcp] Gemini Code Assist request failed`,{scope:t,slot:n,profileId:r.profileId,model:r.model,status:u.status,body:d,authMode:i.authMode,authSource:i.authSource,authType:i.authType}),new X(d||`Gemini Code Assist request failed`,u.status,d,`${r.provider}/${r.model}`);let f=this.normalizeCodeAssistBufferedResponse(d),p=a.transformBuffered(f,r.model??`gemini`);return{response:{status:200,body:p,headers:this.createSuccessHeaders(t,n)},history:{upstreamText:d,claudeBody:p}}}createSuccessHeaders(e,t){return{[H]:V,[U]:Jt,[Ut]:Yt,"x-model-proxy-service":y,"x-model-proxy-scope":e,"x-model-proxy-slot":t}}createGeminiRequestUrl(e,t,n){let r=e.endpoint.replace(/\/$/,``),i=e.apiVersion??`v1beta`;return new URL(`${r}/${i}/${t}:${n}`).toString()}createCodeAssistUrl(e){return new URL(`https://cloudcode-pa.googleapis.com/v1internal:${e}`).toString()}async resolveCodeAssistProjectId(e){let t=this.createCodeAssistProjectCacheKey(e),n=this.codeAssistProjectCache.get(t);if(n)return n;let r=new Headers(e.headers);r.set(H,B);let i=process.env.GOOGLE_CLOUD_PROJECT||process.env.GOOGLE_CLOUD_PROJECT_ID||void 0,a=await this.fetchImpl(this.createCodeAssistUrl(`loadCodeAssist`),{method:`POST`,headers:r,body:JSON.stringify({cloudaicompanionProject:i,metadata:{ideType:`IDE_UNSPECIFIED`,platform:`PLATFORM_UNSPECIFIED`,pluginType:`GEMINI`,duetProject:i}})}),o=await a.text();if(!a.ok)throw new X(o||`Failed to load Gemini Code Assist project context`,a.status,o,`google-gemini-direct/loadCodeAssist`);let s=JSON.parse(o).cloudaicompanionProject||i;if(!s)throw new Y(`Gemini Code Assist did not return a usable project ID.`,`GEMINI_PROJECT_MISSING`,401);return this.codeAssistProjectCache.set(t,s),s}toCodeAssistGenerateContentRequest(e,t){return{contents:e.contents,systemInstruction:e.system_instruction,tools:e.tools,toolConfig:e.tool_config,generationConfig:e.generationConfig,session_id:t}}normalizeCodeAssistBufferedResponse(e){let t=JSON.parse(e);return JSON.stringify(this.extractCodeAssistResponse(t))}normalizeCodeAssistStreamingResponse(e){let t=[];for(let n of e.split(`
64
+
65
+ `)){let e=n.split(`
66
+ `).find(e=>e.startsWith(`data:`))?.slice(5).trim();if(!e||e===`[DONE]`)continue;let r=JSON.parse(e);t.push(`data: ${JSON.stringify(this.extractCodeAssistResponse(r))}`),t.push(``)}return t.join(`
67
+ `)}extractCodeAssistResponse(e){return{candidates:e.response?.candidates??[],usageMetadata:e.response?.usageMetadata,modelVersion:e.response?.modelVersion}}resolveSlot(e,t){if(!e)return z;let n=kt[e];if(n)return n;for(let n of jt)if(t.slots[n].model===e)return n;return z}resolveRequestedModel(e,t,n){let r=t.slots[n];if(!e||kt[e])return r;let i=t.models.find(t=>t.model===e&&t.provider===r.provider&&t.enabled)??t.models.find(t=>t.model===e&&t.enabled);if(!i&&r.model!==e)throw new J(`Model not found or disabled: ${e}`,G,400,{cause:{model:e,slot:n,scope:t.scope}});return{...r,profileId:i?.id??null,label:i?.label??null,provider:i?.provider??r.provider,providerType:i?.providerType??r.providerType,endpoint:i?.endpoint??r.endpoint,model:i?.model??e,reasoningEffort:i?.reasoningEffort??r.reasoningEffort,thinkingDisabled:r.thinkingDisabled??!1}}async forwardAnthropicCompatibleRequest(e,t,n,r,i,a){let o=this.getAnthropicCompatibleAuthToken(i.authTokenEnvVar);if(!o)throw new Y(`Missing upstream auth token for ${Pt}. Set ${i.authTokenEnvVar||Lt}.`,`UPSTREAM_AUTH_MISSING`,401);let s=this.sanitizeClaudePayloadForAnthropic({...gn.parse(JSON.parse(e)),model:r.model}),c=new Headers;c.set(W,a.get(W)??V),c.set(H,B),c.set(Kt,a.get(Kt)??`2023-06-01`),c.set(`x-api-key`,o),c.set(`authorization`,`Bearer ${o}`);let l=a.get(qt);l&&c.set(qt,l);let u=i.apiTimeoutMs??this.resolveUpstreamTimeoutMs(),d=await this.fetchImpl(i.endpoint,{method:`POST`,headers:c,body:JSON.stringify(s),signal:u?AbortSignal.timeout(u):void 0}),f=await d.text();if(!d.ok)throw this.logger.error(`[model-proxy-mcp] Anthropic-compatible upstream request failed`,{scope:t,slot:n,profileId:r.profileId,model:r.model,status:d.status,body:f}),new X(f||`Anthropic-compatible upstream request failed`,d.status,f,`${r.provider}/${r.model}`);return{response:{status:d.status,body:f,headers:{[H]:d.headers.get(H)??V,[U]:d.headers.get(U)??Jt,[Ut]:d.headers.get(Ut)??Yt,"x-model-proxy-service":y,"x-model-proxy-scope":t,"x-model-proxy-slot":n}},history:{upstreamText:f,claudeBody:f}}}getAnthropicCompatibleAuthToken(e){let t=e||process.env.MODEL_PROXY_MCP_UPSTREAM_AUTH_ENV||Lt;return process.env[t]||process.env.MODEL_PROXY_MCP_UPSTREAM_AUTH_TOKEN||null}resolveUpstreamTimeoutMs(){let e=process.env.API_TIMEOUT_MS||process.env.MODEL_PROXY_MCP_UPSTREAM_TIMEOUT_MS;if(!e)return null;let t=Number.parseInt(e,10);return Number.isInteger(t)&&t>0?t:null}createErrorResponse(e,t){return{status:e,body:JSON.stringify({type:`error`,error:{type:`api_error`,message:t}}),headers:{"content-type":`application/json; charset=utf-8`}}}parseClaudeRequest(e){try{return gn.parse(JSON.parse(e))}catch(e){throw e instanceof SyntaxError||e instanceof h.ZodError?new _n(`Invalid Claude request payload`,`REQUEST_VALIDATION_FAILED`,400,{cause:e}):e}}async recordRequest(e,t,n,r,i,a){let o=[],s=this.normalizeSystemPayloads(i.system);for(let i of s)o.push({scope:e,requestId:r,direction:en,role:rn,messageType:rn,model:n.model,slot:t,payloadJson:JSON.stringify(i)});let c=Array.isArray(i.messages)?i.messages:[];for(let i of c){let a=this.extractRole(i);o.push({scope:e,requestId:r,direction:en,role:a,messageType:`message`,model:n.model,slot:t,payloadJson:JSON.stringify(i)})}o.length===0&&o.push({scope:e,requestId:r,direction:en,role:null,messageType:`raw-request`,model:n.model,slot:t,payloadJson:a}),await this.historyService.appendEntries(o)}async recordResponse(e,t,n,r,i,a){let o=this.normalizeResponsePayloads(e,r,t,n.model,i);if(o.length>0){await this.historyService.appendEntries(o);return}await this.historyService.appendEntries([{scope:e,requestId:r,direction:tn,role:an,messageType:`response-stream`,model:n.model,slot:t,payloadJson:JSON.stringify({upstreamText:i,claudeBody:a})}])}async recordError(e,t,n,r,i,a){await this.historyService.appendEntries([{scope:e,requestId:r,direction:`error`,role:null,messageType:`error`,model:n?.model??null,slot:t,payloadJson:JSON.stringify({message:i,payloadPreview:this.summarizePayload(a)})}])}normalizeSystemPayloads(e){return e?typeof e==`string`?[{type:`text`,text:e}]:Array.isArray(e)?e:[e]:[]}sanitizeClaudePayloadForAnthropic(e){return this.stripPrivateChatGptMetadata(e)}stripPrivateChatGptMetadata(e){return Array.isArray(e)?e.map(e=>this.stripPrivateChatGptMetadata(e)):!e||typeof e!=`object`?e:Object.fromEntries(Object.entries(e).filter(([e])=>!e.startsWith(`_chatgpt_`)).map(([e,t])=>[e,this.stripPrivateChatGptMetadata(t)]))}extractRole(e){if(!e||typeof e!=`object`)return null;let t=e.role;return typeof t==`string`?t:null}normalizeResponsePayloads(e,t,n,r,i){let a=[];for(let o of i.split(`
68
+
69
+ `)){let i=o.split(`
70
+ `).find(e=>e.startsWith(`data:`))?.slice(5).trim();if(!i||i===`[DONE]`)continue;let s;try{s=JSON.parse(i)}catch{continue}let c=this.extractCompletedResponse(s);if(!(!c?.output||!Array.isArray(c.output)))for(let i of c.output)a.push({scope:e,requestId:t,direction:tn,role:an,messageType:typeof i.type==`string`?i.type:`response-item`,model:typeof c.model==`string`?c.model:r,slot:n,payloadJson:JSON.stringify(i)})}return a}extractCompletedResponse(e){if(!e||typeof e!=`object`)return null;let t=e;return t.type===`response.completed`&&t.response&&typeof t.response==`object`||t.response&&typeof t.response==`object`?t.response:{model:typeof t.model==`string`?t.model:void 0,output:Array.isArray(t.output)?t.output:void 0}}summarizePayload(e){let t=e.replace(/\s+/g,` `).trim();return t.length<=240?t:`${t.slice(0,240)}...`}};const xn=`<!DOCTYPE html>
71
+ <html lang="en">
72
+ <head>
73
+ <meta charset="utf-8" />
74
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
75
+ <title>Model Proxy Admin</title>
76
+ <style>
77
+ :root {
78
+ color-scheme: light;
79
+ --bg: #f3efe6;
80
+ --panel: rgba(255, 252, 246, 0.92);
81
+ --panel-border: #d7cdb7;
82
+ --text: #1f1b16;
83
+ --muted: #6b6255;
84
+ --accent: #b24a2f;
85
+ --accent-2: #1b6b73;
86
+ --danger: #8f2d21;
87
+ }
88
+ * { box-sizing: border-box; }
89
+ body {
90
+ margin: 0;
91
+ font-family: "Iowan Old Style", "Palatino Linotype", serif;
92
+ color: var(--text);
93
+ background:
94
+ radial-gradient(circle at top left, rgba(178, 74, 47, 0.14), transparent 28%),
95
+ radial-gradient(circle at top right, rgba(27, 107, 115, 0.12), transparent 24%),
96
+ linear-gradient(180deg, #f8f4eb 0%, var(--bg) 100%);
97
+ }
98
+ main {
99
+ max-width: 1280px;
100
+ margin: 0 auto;
101
+ padding: 32px 20px 48px;
102
+ }
103
+ h1, h2 {
104
+ margin: 0 0 12px;
105
+ font-weight: 700;
106
+ }
107
+ p { color: var(--muted); margin-top: 0; }
108
+ .hero {
109
+ display: grid;
110
+ gap: 16px;
111
+ margin-bottom: 24px;
112
+ }
113
+ .panel {
114
+ background: var(--panel);
115
+ border: 1px solid var(--panel-border);
116
+ border-radius: 18px;
117
+ padding: 18px;
118
+ box-shadow: 0 14px 40px rgba(80, 58, 24, 0.08);
119
+ }
120
+ .row {
121
+ display: grid;
122
+ gap: 12px;
123
+ }
124
+ .row.cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
125
+ .row.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
126
+ .toolbar {
127
+ display: flex;
128
+ flex-wrap: wrap;
129
+ gap: 10px;
130
+ align-items: end;
131
+ }
132
+ label {
133
+ display: grid;
134
+ gap: 6px;
135
+ font-size: 13px;
136
+ color: var(--muted);
137
+ }
138
+ input, select, button, textarea {
139
+ font: inherit;
140
+ border-radius: 10px;
141
+ border: 1px solid #c9bea7;
142
+ padding: 10px 12px;
143
+ background: #fffdf8;
144
+ color: var(--text);
145
+ }
146
+ button {
147
+ background: var(--text);
148
+ color: #fff7ea;
149
+ border-color: var(--text);
150
+ cursor: pointer;
151
+ }
152
+ button.secondary {
153
+ background: #fff7ea;
154
+ color: var(--text);
155
+ }
156
+ button.danger {
157
+ background: var(--danger);
158
+ border-color: var(--danger);
159
+ }
160
+ table {
161
+ width: 100%;
162
+ border-collapse: collapse;
163
+ }
164
+ th, td {
165
+ padding: 10px 8px;
166
+ text-align: left;
167
+ border-bottom: 1px solid rgba(107, 98, 85, 0.16);
168
+ vertical-align: top;
169
+ }
170
+ th {
171
+ font-size: 12px;
172
+ text-transform: uppercase;
173
+ letter-spacing: 0.08em;
174
+ color: var(--muted);
175
+ }
176
+ code, pre {
177
+ font-family: "SFMono-Regular", "Menlo", monospace;
178
+ font-size: 12px;
179
+ }
180
+ pre {
181
+ margin: 0;
182
+ white-space: pre-wrap;
183
+ word-break: break-word;
184
+ max-height: 180px;
185
+ overflow: auto;
186
+ }
187
+ .status {
188
+ min-height: 20px;
189
+ color: var(--accent-2);
190
+ }
191
+ .muted { color: var(--muted); }
192
+ .slot-grid {
193
+ display: grid;
194
+ gap: 12px;
195
+ }
196
+ .slot-card {
197
+ padding: 14px;
198
+ border-radius: 14px;
199
+ border: 1px solid rgba(107, 98, 85, 0.16);
200
+ background: rgba(255, 255, 255, 0.7);
201
+ }
202
+ .slot-card h3 {
203
+ margin: 0 0 10px;
204
+ font-size: 18px;
205
+ }
206
+ .pill {
207
+ display: inline-flex;
208
+ padding: 4px 8px;
209
+ border-radius: 999px;
210
+ background: rgba(27, 107, 115, 0.1);
211
+ color: var(--accent-2);
212
+ font-size: 12px;
213
+ }
214
+ .paths {
215
+ display: grid;
216
+ gap: 8px;
217
+ }
218
+ @media (max-width: 900px) {
219
+ .row.cols-2, .row.cols-3 { grid-template-columns: 1fr; }
220
+ }
221
+ </style>
222
+ </head>
223
+ <body>
224
+ <main>
225
+ <section class="hero">
226
+ <div>
227
+ <h1>Model Proxy Admin</h1>
228
+ <p>Scoped slot routing, provider-aware model selection, and conversation history.</p>
229
+ </div>
230
+ <div class="panel toolbar">
231
+ <label>
232
+ Scope
233
+ <select id="scope-input"></select>
234
+ </label>
235
+ <button id="load-button" class="secondary">Load scope</button>
236
+ <span id="status" class="status"></span>
237
+ </div>
238
+ </section>
239
+
240
+ <section class="panel">
241
+ <h2>Runtime Config</h2>
242
+ <div class="paths muted">
243
+ <div>Providers: <code id="provider-config-path"></code></div>
244
+ <div>Model catalog: <code id="model-list-path"></code></div>
245
+ <div>Scope config: <code id="scope-config-path"></code></div>
246
+ </div>
247
+ <div id="slot-grid" class="slot-grid" style="margin-top:16px;"></div>
248
+ <div class="toolbar" style="margin-top:16px;">
249
+ <button id="save-config">Save config</button>
250
+ </div>
251
+ </section>
252
+
253
+ <section class="panel" style="margin-top:20px;">
254
+ <h2>Model Catalog</h2>
255
+ <div id="catalog" class="row"></div>
256
+ </section>
257
+
258
+ <section class="panel" style="margin-top:20px;">
259
+ <div class="toolbar" style="justify-content:space-between;">
260
+ <div>
261
+ <h2 style="margin-bottom:4px;">Conversation History</h2>
262
+ <p id="history-stats"></p>
263
+ </div>
264
+ <div class="toolbar">
265
+ <label>
266
+ Limit
267
+ <select id="history-limit">
268
+ <option>25</option>
269
+ <option selected>50</option>
270
+ <option>100</option>
271
+ </select>
272
+ </label>
273
+ <button id="refresh-history" class="secondary">Refresh history</button>
274
+ <button id="clear-history" class="danger">Clear history</button>
275
+ </div>
276
+ </div>
277
+ <div style="overflow:auto;">
278
+ <table>
279
+ <thead>
280
+ <tr>
281
+ <th>Created</th>
282
+ <th>Direction</th>
283
+ <th>Role</th>
284
+ <th>Slot</th>
285
+ <th>Model</th>
286
+ <th>Payload</th>
287
+ </tr>
288
+ </thead>
289
+ <tbody id="history-body"></tbody>
290
+ </table>
291
+ </div>
292
+ <div class="toolbar" style="margin-top:16px;">
293
+ <button id="more-history" class="secondary">Load more</button>
294
+ </div>
295
+ </section>
296
+ </main>
297
+
298
+ <script>
299
+ const reasoningOptions = ['minimal', 'low', 'medium', 'high'];
300
+ const slots = ['default', 'sonnet', 'opus', 'haiku', 'subagent'];
301
+ let currentConfig = null;
302
+ let nextCursor = null;
303
+
304
+ const scopeInput = document.getElementById('scope-input');
305
+ const statusEl = document.getElementById('status');
306
+ const slotGridEl = document.getElementById('slot-grid');
307
+ const catalogEl = document.getElementById('catalog');
308
+ const providerConfigPathEl = document.getElementById('provider-config-path');
309
+ const modelListPathEl = document.getElementById('model-list-path');
310
+ const scopeConfigPathEl = document.getElementById('scope-config-path');
311
+ const historyBodyEl = document.getElementById('history-body');
312
+ const historyStatsEl = document.getElementById('history-stats');
313
+ const historyLimitEl = document.getElementById('history-limit');
314
+
315
+ function setStatus(message, isError = false) {
316
+ statusEl.textContent = message;
317
+ statusEl.style.color = isError ? 'var(--danger)' : 'var(--accent-2)';
318
+ }
319
+
320
+ function renderScopeOptions(scopes) {
321
+ const currentScope = scopeInput.value || 'default';
322
+ const scopeList = Array.from(new Set(['default'].concat(scopes || []))).sort();
323
+ scopeInput.innerHTML = scopeList
324
+ .map((scope) => '<option value="' + scope + '">' + scope + '</option>')
325
+ .join('');
326
+ scopeInput.value = scopeList.includes(currentScope) ? currentScope : 'default';
327
+ }
328
+
329
+ function providerOptions(providers, selected) {
330
+ return Object.entries(providers)
331
+ .sort((left, right) => left[0].localeCompare(right[0]))
332
+ .map(([id, config]) => {
333
+ const isSelected = id === selected ? ' selected' : '';
334
+ return '<option value="' + id + '"' + isSelected + '>' + id + ' (' + config.type + ')</option>';
335
+ })
336
+ .join('');
337
+ }
338
+
339
+ function modelOptions(models, provider, selected) {
340
+ return models
341
+ .filter((model) => model.enabled && model.provider === provider)
342
+ .map((model) => {
343
+ const isSelected = model.model === selected ? ' selected' : '';
344
+ return '<option value="' + model.model + '"' + isSelected + '>' + model.label + ' (' + model.model + ')</option>';
345
+ })
346
+ .join('');
347
+ }
348
+
349
+ function reasoningSelect(value, slot) {
350
+ return (
351
+ '<select data-slot-reasoning="' +
352
+ slot +
353
+ '">' +
354
+ reasoningOptions
355
+ .map((option) => {
356
+ const selected = option === value ? ' selected' : '';
357
+ return '<option value="' + option + '"' + selected + '>' + option + '</option>';
358
+ })
359
+ .join('') +
360
+ '</select>'
361
+ );
362
+ }
363
+
364
+ function refreshSlotModelOptions(slot) {
365
+ if (!currentConfig) {
366
+ return;
367
+ }
368
+
369
+ const providerSelect = document.querySelector('[data-slot-provider="' + slot + '"]');
370
+ const modelSelect = document.querySelector('[data-slot-model="' + slot + '"]');
371
+ if (!providerSelect || !modelSelect) {
372
+ return;
373
+ }
374
+
375
+ const resolved = currentConfig.slots[slot];
376
+ const provider = providerSelect.value;
377
+ modelSelect.innerHTML = modelOptions(currentConfig.models, provider, resolved.model);
378
+
379
+ if (!modelSelect.value) {
380
+ const fallbackModel = currentConfig.models.find((model) => model.enabled && model.provider === provider);
381
+ if (fallbackModel) {
382
+ modelSelect.value = fallbackModel.model;
383
+ }
384
+ }
385
+
386
+ const endpointEl = document.querySelector('[data-slot-endpoint="' + slot + '"]');
387
+ if (endpointEl) {
388
+ endpointEl.textContent = currentConfig.providers[provider]?.endpoint || 'No provider config';
389
+ }
390
+ }
391
+
392
+ function renderConfig(config) {
393
+ currentConfig = config;
394
+ providerConfigPathEl.textContent = config.providerConfigPath;
395
+ modelListPathEl.textContent = config.modelListPath;
396
+ scopeConfigPathEl.textContent = config.scopeConfigPath;
397
+
398
+ slotGridEl.innerHTML = slots.map((slot) => {
399
+ const resolved = config.slots[slot];
400
+ return (
401
+ '<div class="slot-card">' +
402
+ '<div class="toolbar" style="justify-content:space-between;">' +
403
+ '<h3>' + slot + '</h3>' +
404
+ '<span class="pill">' +
405
+ (resolved.providerType ?? 'unconfigured') +
406
+ (resolved.fallbacks.length > 0 ? ' · +' + resolved.fallbacks.length + ' fallback' + (resolved.fallbacks.length > 1 ? 's' : '') : '') +
407
+ '</span>' +
408
+ '</div>' +
409
+ '<div class="row cols-3">' +
410
+ '<label>Provider<select data-slot-provider="' + slot + '">' +
411
+ providerOptions(config.providers, resolved.provider || '') +
412
+ '</select></label>' +
413
+ '<label>Model<select data-slot-model="' + slot + '"></select></label>' +
414
+ '<label>Thinking' + reasoningSelect(resolved.reasoningEffort, slot) + '</label>' +
415
+ '</div>' +
416
+ '<div class="muted" style="margin-top:10px;">Endpoint: <code data-slot-endpoint="' + slot + '"></code></div>' +
417
+ '</div>'
418
+ );
419
+ }).join('');
420
+
421
+ for (const slot of slots) {
422
+ refreshSlotModelOptions(slot);
423
+ const providerSelect = document.querySelector('[data-slot-provider="' + slot + '"]');
424
+ if (providerSelect) {
425
+ providerSelect.addEventListener('change', () => refreshSlotModelOptions(slot));
426
+ }
427
+ }
428
+
429
+ catalogEl.innerHTML = config.models
430
+ .map(
431
+ (model) =>
432
+ '<div class="panel" style="padding:12px;">' +
433
+ '<strong>' + model.label + '</strong>' +
434
+ '<div class="muted"><code>' + model.provider + '</code> · <code>' + model.model + '</code> · default thinking: ' + model.reasoningEffort + '</div>' +
435
+ '</div>',
436
+ )
437
+ .join('');
438
+ }
439
+
440
+ function payloadPreview(payloadJson) {
441
+ try {
442
+ return JSON.stringify(JSON.parse(payloadJson), null, 2);
443
+ } catch {
444
+ return payloadJson;
445
+ }
446
+ }
447
+
448
+ async function loadConfig() {
449
+ const scope = encodeURIComponent(scopeInput.value || 'default');
450
+ const response = await fetch('/admin/config?scope=' + scope);
451
+ if (!response.ok) {
452
+ throw new Error(await response.text());
453
+ }
454
+ renderConfig(await response.json());
455
+ }
456
+
457
+ async function loadScopes() {
458
+ const response = await fetch('/admin/scopes');
459
+ if (!response.ok) {
460
+ throw new Error(await response.text());
461
+ }
462
+ const payload = await response.json();
463
+ renderScopeOptions(payload.scopes || []);
464
+ }
465
+
466
+ async function saveConfig() {
467
+ const models = {};
468
+ for (const slot of slots) {
469
+ const currentSlot = currentConfig.scopeModels[slot];
470
+ models[slot] = {
471
+ main: {
472
+ provider: document.querySelector('[data-slot-provider="' + slot + '"]')?.value,
473
+ model: document.querySelector('[data-slot-model="' + slot + '"]')?.value,
474
+ reasoningEffort: document.querySelector('[data-slot-reasoning="' + slot + '"]')?.value,
475
+ thinkingDisabled:
476
+ currentSlot?.main?.thinkingDisabled ?? currentConfig.slots[slot]?.thinkingDisabled ?? false,
477
+ },
478
+ fallbacks: currentSlot?.fallbacks ?? [],
479
+ };
480
+ }
481
+
482
+ const response = await fetch('/admin/config?scope=' + encodeURIComponent(scopeInput.value || 'default'), {
483
+ method: 'PUT',
484
+ headers: { 'content-type': 'application/json' },
485
+ body: JSON.stringify({ models }),
486
+ });
487
+ if (!response.ok) {
488
+ throw new Error(await response.text());
489
+ }
490
+ renderConfig(await response.json());
491
+ }
492
+
493
+ async function loadHistory(append = false) {
494
+ const scope = encodeURIComponent(scopeInput.value || 'default');
495
+ const cursorQuery = append && nextCursor ? '&cursor=' + encodeURIComponent(nextCursor) : '';
496
+ const limit = encodeURIComponent(historyLimitEl.value);
497
+ const response = await fetch('/admin/history?scope=' + scope + '&limit=' + limit + cursorQuery);
498
+ if (!response.ok) {
499
+ throw new Error(await response.text());
500
+ }
501
+ const payload = await response.json();
502
+ nextCursor = payload.nextCursor;
503
+ const rows = payload.items
504
+ .map(
505
+ (item) =>
506
+ '<tr>' +
507
+ '<td>' + item.createdAt + '</td>' +
508
+ '<td>' + item.direction + '</td>' +
509
+ '<td>' + (item.role ?? '') + '</td>' +
510
+ '<td>' + item.slot + '</td>' +
511
+ '<td>' + (item.model ?? '') + '</td>' +
512
+ '<td><pre>' + payloadPreview(item.payloadJson) + '</pre></td>' +
513
+ '</tr>',
514
+ )
515
+ .join('');
516
+ historyBodyEl.innerHTML = append ? historyBodyEl.innerHTML + rows : rows;
517
+ document.getElementById('more-history').disabled = !nextCursor;
518
+
519
+ const statsResponse = await fetch('/admin/history/stats?scope=' + scope);
520
+ const stats = await statsResponse.json();
521
+ historyStatsEl.textContent = stats.totalMessages + ' stored messages. Retention limit ' + stats.retentionLimit + '.';
522
+ }
523
+
524
+ async function clearHistory() {
525
+ const scope = encodeURIComponent(scopeInput.value || 'default');
526
+ const response = await fetch('/admin/history?scope=' + scope, { method: 'DELETE' });
527
+ if (!response.ok) {
528
+ throw new Error(await response.text());
529
+ }
530
+ nextCursor = null;
531
+ historyBodyEl.innerHTML = '';
532
+ await loadHistory();
533
+ }
534
+
535
+ document.getElementById('load-button').addEventListener('click', async () => {
536
+ try {
537
+ setStatus('Loading...');
538
+ nextCursor = null;
539
+ await loadConfig();
540
+ await loadHistory();
541
+ setStatus('Scope loaded.');
542
+ } catch (error) {
543
+ setStatus(error.message, true);
544
+ }
545
+ });
546
+
547
+ document.getElementById('save-config').addEventListener('click', async () => {
548
+ try {
549
+ setStatus('Saving config...');
550
+ await saveConfig();
551
+ setStatus('Config saved.');
552
+ } catch (error) {
553
+ setStatus(error.message, true);
554
+ }
555
+ });
556
+
557
+ document.getElementById('refresh-history').addEventListener('click', async () => {
558
+ try {
559
+ setStatus('Refreshing history...');
560
+ nextCursor = null;
561
+ await loadHistory();
562
+ setStatus('History refreshed.');
563
+ } catch (error) {
564
+ setStatus(error.message, true);
565
+ }
566
+ });
567
+
568
+ document.getElementById('clear-history').addEventListener('click', async () => {
569
+ try {
570
+ setStatus('Clearing history...');
571
+ await clearHistory();
572
+ setStatus('History cleared.');
573
+ } catch (error) {
574
+ setStatus(error.message, true);
575
+ }
576
+ });
577
+
578
+ document.getElementById('more-history').addEventListener('click', async () => {
579
+ try {
580
+ setStatus('Loading more history...');
581
+ await loadHistory(true);
582
+ setStatus('More history loaded.');
583
+ } catch (error) {
584
+ setStatus(error.message, true);
585
+ }
586
+ });
587
+
588
+ document.addEventListener('DOMContentLoaded', async () => {
589
+ try {
590
+ await loadScopes();
591
+ await loadConfig();
592
+ await loadHistory();
593
+ setStatus('Ready.');
594
+ } catch (error) {
595
+ setStatus(error.message, true);
596
+ }
597
+ });
598
+ <\/script>
599
+ </body>
600
+ </html>
601
+ `,Sn=h.z.enum([`minimal`,`low`,`medium`,`high`]),Cn=h.z.enum([`default`,`sonnet`,`opus`,`haiku`,`subagent`]),wn=h.z.object({provider:h.z.string().min(1),model:h.z.string().min(1),reasoningEffort:Sn,thinkingDisabled:h.z.boolean().optional()}),Z=h.z.object({main:wn,fallbacks:h.z.array(wn).default([])}),Tn=h.z.object({models:h.z.object({default:Z.nullable().optional(),sonnet:Z.nullable().optional(),opus:Z.nullable().optional(),haiku:Z.nullable().optional(),subagent:Z.nullable().optional()}).optional()}),En=h.z.object({id:h.z.string().min(1),label:h.z.string().min(1),provider:h.z.string().min(1),model:h.z.string().min(1),endpoint:h.z.url().nullable(),reasoningEffort:Sn.default(`medium`),enabled:h.z.boolean().default(!0),providerType:h.z.enum([`chatgpt-codex`,`anthropic-compatible`,`gemini-direct`]).nullable().optional()});function Dn(e=new bn){let t=new te.Hono,n=e=>e||`default`;return t.get(`/health`,e=>e.json({status:`healthy`,service:y})),t.get(`/status`,async t=>{let n=await e.getStatus();return t.json(n)}),t.get(`/v1/models`,async t=>t.json(await e.getModels())),t.get(`/scopes/:scope/status`,async t=>t.json(await e.getStatus(n(t.req.param(`scope`))))),t.get(`/scopes/:scope/v1/models`,async t=>t.json(await e.getModels(n(t.req.param(`scope`))))),t.post(`/v1/messages`,async t=>{let n=await t.req.text(),r=await e.forwardClaudeRequest(n,`default`,t.req.raw.headers);return new Response(r.body,{status:r.status,headers:r.headers})}),t.post(`/scopes/:scope/v1/messages`,async t=>{let r=await t.req.text(),i=await e.forwardClaudeRequest(r,n(t.req.param(`scope`)),t.req.raw.headers);return new Response(i.body,{status:i.status,headers:i.headers})}),t.get(`/admin`,()=>new Response(`<!DOCTYPE html>
602
+ <html lang="en">
603
+ <head>
604
+ <meta charset="utf-8" />
605
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
606
+ <title>Model Proxy Admin</title>
607
+ <style>
608
+ :root {
609
+ color-scheme: light;
610
+ --bg: #f3efe6;
611
+ --panel: rgba(255, 252, 246, 0.92);
612
+ --panel-border: #d7cdb7;
613
+ --text: #1f1b16;
614
+ --muted: #6b6255;
615
+ --accent: #b24a2f;
616
+ --accent-2: #1b6b73;
617
+ --danger: #8f2d21;
618
+ }
619
+ * { box-sizing: border-box; }
620
+ body {
621
+ margin: 0;
622
+ font-family: "Iowan Old Style", "Palatino Linotype", serif;
623
+ color: var(--text);
624
+ background:
625
+ radial-gradient(circle at top left, rgba(178, 74, 47, 0.14), transparent 28%),
626
+ radial-gradient(circle at top right, rgba(27, 107, 115, 0.12), transparent 24%),
627
+ linear-gradient(180deg, #f8f4eb 0%, var(--bg) 100%);
628
+ }
629
+ main {
630
+ max-width: 1280px;
631
+ margin: 0 auto;
632
+ padding: 32px 20px 48px;
633
+ }
634
+ h1, h2 {
635
+ margin: 0 0 12px;
636
+ font-weight: 700;
637
+ }
638
+ p { color: var(--muted); margin-top: 0; }
639
+ .hero {
640
+ display: grid;
641
+ gap: 16px;
642
+ margin-bottom: 24px;
643
+ }
644
+ .panel {
645
+ background: var(--panel);
646
+ border: 1px solid var(--panel-border);
647
+ border-radius: 18px;
648
+ padding: 18px;
649
+ box-shadow: 0 14px 40px rgba(80, 58, 24, 0.08);
650
+ }
651
+ .row {
652
+ display: grid;
653
+ gap: 12px;
654
+ }
655
+ .row.cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
656
+ .row.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
657
+ .toolbar {
658
+ display: flex;
659
+ flex-wrap: wrap;
660
+ gap: 10px;
661
+ align-items: end;
662
+ }
663
+ label {
664
+ display: grid;
665
+ gap: 6px;
666
+ font-size: 13px;
667
+ color: var(--muted);
668
+ }
669
+ input, select, button, textarea {
670
+ font: inherit;
671
+ border-radius: 10px;
672
+ border: 1px solid #c9bea7;
673
+ padding: 10px 12px;
674
+ background: #fffdf8;
675
+ color: var(--text);
676
+ }
677
+ button {
678
+ background: var(--text);
679
+ color: #fff7ea;
680
+ border-color: var(--text);
681
+ cursor: pointer;
682
+ }
683
+ button.secondary {
684
+ background: #fff7ea;
685
+ color: var(--text);
686
+ }
687
+ button.danger {
688
+ background: var(--danger);
689
+ border-color: var(--danger);
690
+ }
691
+ table {
692
+ width: 100%;
693
+ border-collapse: collapse;
694
+ }
695
+ th, td {
696
+ padding: 10px 8px;
697
+ text-align: left;
698
+ border-bottom: 1px solid rgba(107, 98, 85, 0.16);
699
+ vertical-align: top;
700
+ }
701
+ th {
702
+ font-size: 12px;
703
+ text-transform: uppercase;
704
+ letter-spacing: 0.08em;
705
+ color: var(--muted);
706
+ }
707
+ code, pre {
708
+ font-family: "SFMono-Regular", "Menlo", monospace;
709
+ font-size: 12px;
710
+ }
711
+ pre {
712
+ margin: 0;
713
+ white-space: pre-wrap;
714
+ word-break: break-word;
715
+ max-height: 180px;
716
+ overflow: auto;
717
+ }
718
+ .status {
719
+ min-height: 20px;
720
+ color: var(--accent-2);
721
+ }
722
+ .muted { color: var(--muted); }
723
+ .slot-grid {
724
+ display: grid;
725
+ gap: 12px;
726
+ }
727
+ .slot-card {
728
+ padding: 14px;
729
+ border-radius: 14px;
730
+ border: 1px solid rgba(107, 98, 85, 0.16);
731
+ background: rgba(255, 255, 255, 0.7);
732
+ }
733
+ .slot-card h3 {
734
+ margin: 0 0 10px;
735
+ font-size: 18px;
736
+ }
737
+ .pill {
738
+ display: inline-flex;
739
+ padding: 4px 8px;
740
+ border-radius: 999px;
741
+ background: rgba(27, 107, 115, 0.1);
742
+ color: var(--accent-2);
743
+ font-size: 12px;
744
+ }
745
+ .paths {
746
+ display: grid;
747
+ gap: 8px;
748
+ }
749
+ @media (max-width: 900px) {
750
+ .row.cols-2, .row.cols-3 { grid-template-columns: 1fr; }
751
+ }
752
+ </style>
753
+ </head>
754
+ <body>
755
+ <main>
756
+ <section class="hero">
757
+ <div>
758
+ <h1>Model Proxy Admin</h1>
759
+ <p>Scoped slot routing, provider-aware model selection, and conversation history.</p>
760
+ </div>
761
+ <div class="panel toolbar">
762
+ <label>
763
+ Scope
764
+ <select id="scope-input"></select>
765
+ </label>
766
+ <button id="load-button" class="secondary">Load scope</button>
767
+ <span id="status" class="status"></span>
768
+ </div>
769
+ </section>
770
+
771
+ <section class="panel">
772
+ <h2>Runtime Config</h2>
773
+ <div class="paths muted">
774
+ <div>Providers: <code id="provider-config-path"></code></div>
775
+ <div>Model catalog: <code id="model-list-path"></code></div>
776
+ <div>Scope config: <code id="scope-config-path"></code></div>
777
+ </div>
778
+ <div id="slot-grid" class="slot-grid" style="margin-top:16px;"></div>
779
+ <div class="toolbar" style="margin-top:16px;">
780
+ <button id="save-config">Save config</button>
781
+ </div>
782
+ </section>
783
+
784
+ <section class="panel" style="margin-top:20px;">
785
+ <h2>Model Catalog</h2>
786
+ <div id="catalog" class="row"></div>
787
+ </section>
788
+
789
+ <section class="panel" style="margin-top:20px;">
790
+ <div class="toolbar" style="justify-content:space-between;">
791
+ <div>
792
+ <h2 style="margin-bottom:4px;">Conversation History</h2>
793
+ <p id="history-stats"></p>
794
+ </div>
795
+ <div class="toolbar">
796
+ <label>
797
+ Limit
798
+ <select id="history-limit">
799
+ <option>25</option>
800
+ <option selected>50</option>
801
+ <option>100</option>
802
+ </select>
803
+ </label>
804
+ <button id="refresh-history" class="secondary">Refresh history</button>
805
+ <button id="clear-history" class="danger">Clear history</button>
806
+ </div>
807
+ </div>
808
+ <div style="overflow:auto;">
809
+ <table>
810
+ <thead>
811
+ <tr>
812
+ <th>Created</th>
813
+ <th>Direction</th>
814
+ <th>Role</th>
815
+ <th>Slot</th>
816
+ <th>Model</th>
817
+ <th>Payload</th>
818
+ </tr>
819
+ </thead>
820
+ <tbody id="history-body"></tbody>
821
+ </table>
822
+ </div>
823
+ <div class="toolbar" style="margin-top:16px;">
824
+ <button id="more-history" class="secondary">Load more</button>
825
+ </div>
826
+ </section>
827
+ </main>
828
+
829
+ <script>
830
+ const reasoningOptions = ['minimal', 'low', 'medium', 'high'];
831
+ const slots = ['default', 'sonnet', 'opus', 'haiku', 'subagent'];
832
+ let currentConfig = null;
833
+ let nextCursor = null;
834
+
835
+ const scopeInput = document.getElementById('scope-input');
836
+ const statusEl = document.getElementById('status');
837
+ const slotGridEl = document.getElementById('slot-grid');
838
+ const catalogEl = document.getElementById('catalog');
839
+ const providerConfigPathEl = document.getElementById('provider-config-path');
840
+ const modelListPathEl = document.getElementById('model-list-path');
841
+ const scopeConfigPathEl = document.getElementById('scope-config-path');
842
+ const historyBodyEl = document.getElementById('history-body');
843
+ const historyStatsEl = document.getElementById('history-stats');
844
+ const historyLimitEl = document.getElementById('history-limit');
845
+
846
+ function setStatus(message, isError = false) {
847
+ statusEl.textContent = message;
848
+ statusEl.style.color = isError ? 'var(--danger)' : 'var(--accent-2)';
849
+ }
850
+
851
+ function renderScopeOptions(scopes) {
852
+ const currentScope = scopeInput.value || 'default';
853
+ const scopeList = Array.from(new Set(['default'].concat(scopes || []))).sort();
854
+ scopeInput.innerHTML = scopeList
855
+ .map((scope) => '<option value="' + scope + '">' + scope + '</option>')
856
+ .join('');
857
+ scopeInput.value = scopeList.includes(currentScope) ? currentScope : 'default';
858
+ }
859
+
860
+ function providerOptions(providers, selected) {
861
+ return Object.entries(providers)
862
+ .sort((left, right) => left[0].localeCompare(right[0]))
863
+ .map(([id, config]) => {
864
+ const isSelected = id === selected ? ' selected' : '';
865
+ return '<option value="' + id + '"' + isSelected + '>' + id + ' (' + config.type + ')</option>';
866
+ })
867
+ .join('');
868
+ }
869
+
870
+ function modelOptions(models, provider, selected) {
871
+ return models
872
+ .filter((model) => model.enabled && model.provider === provider)
873
+ .map((model) => {
874
+ const isSelected = model.model === selected ? ' selected' : '';
875
+ return '<option value="' + model.model + '"' + isSelected + '>' + model.label + ' (' + model.model + ')</option>';
876
+ })
877
+ .join('');
878
+ }
879
+
880
+ function reasoningSelect(value, slot) {
881
+ return (
882
+ '<select data-slot-reasoning="' +
883
+ slot +
884
+ '">' +
885
+ reasoningOptions
886
+ .map((option) => {
887
+ const selected = option === value ? ' selected' : '';
888
+ return '<option value="' + option + '"' + selected + '>' + option + '</option>';
889
+ })
890
+ .join('') +
891
+ '</select>'
892
+ );
893
+ }
894
+
895
+ function refreshSlotModelOptions(slot) {
896
+ if (!currentConfig) {
897
+ return;
898
+ }
899
+
900
+ const providerSelect = document.querySelector('[data-slot-provider="' + slot + '"]');
901
+ const modelSelect = document.querySelector('[data-slot-model="' + slot + '"]');
902
+ if (!providerSelect || !modelSelect) {
903
+ return;
904
+ }
905
+
906
+ const resolved = currentConfig.slots[slot];
907
+ const provider = providerSelect.value;
908
+ modelSelect.innerHTML = modelOptions(currentConfig.models, provider, resolved.model);
909
+
910
+ if (!modelSelect.value) {
911
+ const fallbackModel = currentConfig.models.find((model) => model.enabled && model.provider === provider);
912
+ if (fallbackModel) {
913
+ modelSelect.value = fallbackModel.model;
914
+ }
915
+ }
916
+
917
+ const endpointEl = document.querySelector('[data-slot-endpoint="' + slot + '"]');
918
+ if (endpointEl) {
919
+ endpointEl.textContent = currentConfig.providers[provider]?.endpoint || 'No provider config';
920
+ }
921
+ }
922
+
923
+ function renderConfig(config) {
924
+ currentConfig = config;
925
+ providerConfigPathEl.textContent = config.providerConfigPath;
926
+ modelListPathEl.textContent = config.modelListPath;
927
+ scopeConfigPathEl.textContent = config.scopeConfigPath;
928
+
929
+ slotGridEl.innerHTML = slots.map((slot) => {
930
+ const resolved = config.slots[slot];
931
+ return (
932
+ '<div class="slot-card">' +
933
+ '<div class="toolbar" style="justify-content:space-between;">' +
934
+ '<h3>' + slot + '</h3>' +
935
+ '<span class="pill">' +
936
+ (resolved.providerType ?? 'unconfigured') +
937
+ (resolved.fallbacks.length > 0 ? ' · +' + resolved.fallbacks.length + ' fallback' + (resolved.fallbacks.length > 1 ? 's' : '') : '') +
938
+ '</span>' +
939
+ '</div>' +
940
+ '<div class="row cols-3">' +
941
+ '<label>Provider<select data-slot-provider="' + slot + '">' +
942
+ providerOptions(config.providers, resolved.provider || '') +
943
+ '</select></label>' +
944
+ '<label>Model<select data-slot-model="' + slot + '"></select></label>' +
945
+ '<label>Thinking' + reasoningSelect(resolved.reasoningEffort, slot) + '</label>' +
946
+ '</div>' +
947
+ '<div class="muted" style="margin-top:10px;">Endpoint: <code data-slot-endpoint="' + slot + '"></code></div>' +
948
+ '</div>'
949
+ );
950
+ }).join('');
951
+
952
+ for (const slot of slots) {
953
+ refreshSlotModelOptions(slot);
954
+ const providerSelect = document.querySelector('[data-slot-provider="' + slot + '"]');
955
+ if (providerSelect) {
956
+ providerSelect.addEventListener('change', () => refreshSlotModelOptions(slot));
957
+ }
958
+ }
959
+
960
+ catalogEl.innerHTML = config.models
961
+ .map(
962
+ (model) =>
963
+ '<div class="panel" style="padding:12px;">' +
964
+ '<strong>' + model.label + '</strong>' +
965
+ '<div class="muted"><code>' + model.provider + '</code> · <code>' + model.model + '</code> · default thinking: ' + model.reasoningEffort + '</div>' +
966
+ '</div>',
967
+ )
968
+ .join('');
969
+ }
970
+
971
+ function payloadPreview(payloadJson) {
972
+ try {
973
+ return JSON.stringify(JSON.parse(payloadJson), null, 2);
974
+ } catch {
975
+ return payloadJson;
976
+ }
977
+ }
978
+
979
+ async function loadConfig() {
980
+ const scope = encodeURIComponent(scopeInput.value || 'default');
981
+ const response = await fetch('/admin/config?scope=' + scope);
982
+ if (!response.ok) {
983
+ throw new Error(await response.text());
984
+ }
985
+ renderConfig(await response.json());
986
+ }
987
+
988
+ async function loadScopes() {
989
+ const response = await fetch('/admin/scopes');
990
+ if (!response.ok) {
991
+ throw new Error(await response.text());
992
+ }
993
+ const payload = await response.json();
994
+ renderScopeOptions(payload.scopes || []);
995
+ }
996
+
997
+ async function saveConfig() {
998
+ const models = {};
999
+ for (const slot of slots) {
1000
+ const currentSlot = currentConfig.scopeModels[slot];
1001
+ models[slot] = {
1002
+ main: {
1003
+ provider: document.querySelector('[data-slot-provider="' + slot + '"]')?.value,
1004
+ model: document.querySelector('[data-slot-model="' + slot + '"]')?.value,
1005
+ reasoningEffort: document.querySelector('[data-slot-reasoning="' + slot + '"]')?.value,
1006
+ thinkingDisabled:
1007
+ currentSlot?.main?.thinkingDisabled ?? currentConfig.slots[slot]?.thinkingDisabled ?? false,
1008
+ },
1009
+ fallbacks: currentSlot?.fallbacks ?? [],
1010
+ };
1011
+ }
1012
+
1013
+ const response = await fetch('/admin/config?scope=' + encodeURIComponent(scopeInput.value || 'default'), {
1014
+ method: 'PUT',
1015
+ headers: { 'content-type': 'application/json' },
1016
+ body: JSON.stringify({ models }),
1017
+ });
1018
+ if (!response.ok) {
1019
+ throw new Error(await response.text());
1020
+ }
1021
+ renderConfig(await response.json());
1022
+ }
1023
+
1024
+ async function loadHistory(append = false) {
1025
+ const scope = encodeURIComponent(scopeInput.value || 'default');
1026
+ const cursorQuery = append && nextCursor ? '&cursor=' + encodeURIComponent(nextCursor) : '';
1027
+ const limit = encodeURIComponent(historyLimitEl.value);
1028
+ const response = await fetch('/admin/history?scope=' + scope + '&limit=' + limit + cursorQuery);
1029
+ if (!response.ok) {
1030
+ throw new Error(await response.text());
1031
+ }
1032
+ const payload = await response.json();
1033
+ nextCursor = payload.nextCursor;
1034
+ const rows = payload.items
1035
+ .map(
1036
+ (item) =>
1037
+ '<tr>' +
1038
+ '<td>' + item.createdAt + '</td>' +
1039
+ '<td>' + item.direction + '</td>' +
1040
+ '<td>' + (item.role ?? '') + '</td>' +
1041
+ '<td>' + item.slot + '</td>' +
1042
+ '<td>' + (item.model ?? '') + '</td>' +
1043
+ '<td><pre>' + payloadPreview(item.payloadJson) + '</pre></td>' +
1044
+ '</tr>',
1045
+ )
1046
+ .join('');
1047
+ historyBodyEl.innerHTML = append ? historyBodyEl.innerHTML + rows : rows;
1048
+ document.getElementById('more-history').disabled = !nextCursor;
1049
+
1050
+ const statsResponse = await fetch('/admin/history/stats?scope=' + scope);
1051
+ const stats = await statsResponse.json();
1052
+ historyStatsEl.textContent = stats.totalMessages + ' stored messages. Retention limit ' + stats.retentionLimit + '.';
1053
+ }
1054
+
1055
+ async function clearHistory() {
1056
+ const scope = encodeURIComponent(scopeInput.value || 'default');
1057
+ const response = await fetch('/admin/history?scope=' + scope, { method: 'DELETE' });
1058
+ if (!response.ok) {
1059
+ throw new Error(await response.text());
1060
+ }
1061
+ nextCursor = null;
1062
+ historyBodyEl.innerHTML = '';
1063
+ await loadHistory();
1064
+ }
1065
+
1066
+ document.getElementById('load-button').addEventListener('click', async () => {
1067
+ try {
1068
+ setStatus('Loading...');
1069
+ nextCursor = null;
1070
+ await loadConfig();
1071
+ await loadHistory();
1072
+ setStatus('Scope loaded.');
1073
+ } catch (error) {
1074
+ setStatus(error.message, true);
1075
+ }
1076
+ });
1077
+
1078
+ document.getElementById('save-config').addEventListener('click', async () => {
1079
+ try {
1080
+ setStatus('Saving config...');
1081
+ await saveConfig();
1082
+ setStatus('Config saved.');
1083
+ } catch (error) {
1084
+ setStatus(error.message, true);
1085
+ }
1086
+ });
1087
+
1088
+ document.getElementById('refresh-history').addEventListener('click', async () => {
1089
+ try {
1090
+ setStatus('Refreshing history...');
1091
+ nextCursor = null;
1092
+ await loadHistory();
1093
+ setStatus('History refreshed.');
1094
+ } catch (error) {
1095
+ setStatus(error.message, true);
1096
+ }
1097
+ });
1098
+
1099
+ document.getElementById('clear-history').addEventListener('click', async () => {
1100
+ try {
1101
+ setStatus('Clearing history...');
1102
+ await clearHistory();
1103
+ setStatus('History cleared.');
1104
+ } catch (error) {
1105
+ setStatus(error.message, true);
1106
+ }
1107
+ });
1108
+
1109
+ document.getElementById('more-history').addEventListener('click', async () => {
1110
+ try {
1111
+ setStatus('Loading more history...');
1112
+ await loadHistory(true);
1113
+ setStatus('More history loaded.');
1114
+ } catch (error) {
1115
+ setStatus(error.message, true);
1116
+ }
1117
+ });
1118
+
1119
+ document.addEventListener('DOMContentLoaded', async () => {
1120
+ try {
1121
+ await loadScopes();
1122
+ await loadConfig();
1123
+ await loadHistory();
1124
+ setStatus('Ready.');
1125
+ } catch (error) {
1126
+ setStatus(error.message, true);
1127
+ }
1128
+ });
1129
+ <\/script>
1130
+ </body>
1131
+ </html>
1132
+ `,{headers:{"content-type":`text/html; charset=utf-8`}})),t.get(`/admin/scopes`,async t=>t.json({scopes:await e.listScopes()})),t.get(`/admin/config`,async t=>{let r=n(t.req.query(`scope`));return t.json(await e.getAdminConfig(r))}),t.get(`/admin/profiles`,async t=>{let r=n(t.req.query(`scope`));return t.json({profiles:await e.listProfiles(r)})}),t.get(`/admin/current-model`,async t=>{let r=n(t.req.query(`scope`)),i=t.req.query(`slot`)||`default`;return t.json({profile:await e.getCurrentModel(r,i)})}),t.get(`/admin/history`,async t=>{let r=n(t.req.query(`scope`)),i=h.z.coerce.number().int().positive().max(200).default(50).parse(t.req.query(`limit`)??50),a=t.req.query(`cursor`)||void 0;return t.json(await e.listHistory(r,i,a))}),t.get(`/admin/history/stats`,async t=>{let r=n(t.req.query(`scope`));return t.json(await e.getHistoryStats(r))}),t.put(`/admin/profiles/:id`,async t=>{let r=En.parse(await t.req.json()),i=n(t.req.query(`scope`)),a=await e.upsertProfile({...r,id:t.req.param(`id`),providerType:r.providerType??null},i);return t.json(a)}),t.put(`/admin/active-profile`,async t=>{let r=n(t.req.query(`scope`)),i=h.z.object({profileId:h.z.string().min(1),slot:Cn.default(`default`)}).parse(await t.req.json()),a=await e.setActiveProfile(i.profileId,r,i.slot);return t.json(a)}),t.put(`/admin/current-model`,async t=>{let r=n(t.req.query(`scope`)),i=h.z.object({model:h.z.string().min(1),slot:Cn.default(`default`)}).parse(await t.req.json()),a=await e.switchModel(i.model,r,i.slot);return t.json(a)}),t.put(`/admin/config`,async t=>{let r=n(t.req.query(`scope`)),i=Tn.parse(await t.req.json());return t.json(await e.updateAdminConfig(i,r))}),t.delete(`/admin/history`,async t=>{let r=n(t.req.query(`scope`));return t.json(await e.clearHistory(r))}),t}const On=`default`,kn=`default`,An=`MODEL_PROXY_MCP_SCOPE`,jn=`MODEL_PROXY_MCP_SLOT`,Q=h.z.enum([`default`,`sonnet`,`opus`,`haiku`,`subagent`]);function Mn(e=new bn,t=process.env){let n=e=>{let n=e?.scope;return typeof n==`string`&&n.trim()?n:t.MODEL_PROXY_MCP_SCOPE||`default`},r=e=>{let n=typeof e?.slot==`string`?e.slot:t.MODEL_PROXY_MCP_SLOT||`default`;return Q.parse(n)},i=new ne.Server({name:`model-proxy-mcp`,version:`0.1.0`},{capabilities:{tools:{}}});return i.setRequestHandler(re.ListToolsRequestSchema,async()=>({tools:[{name:`get_proxy_status`,description:`Get the HTTP proxy status, auth status, and active profile.`,inputSchema:{type:`object`,properties:{scope:{type:`string`}},additionalProperties:!1}},{name:`list_profiles`,description:`List all configured model proxy profiles.`,inputSchema:{type:`object`,properties:{scope:{type:`string`}},additionalProperties:!1}},{name:`get_active_profile`,description:`Get the active model proxy profile.`,inputSchema:{type:`object`,properties:{scope:{type:`string`},slot:{type:`string`,enum:Q.options}},additionalProperties:!1}},{name:`get_current_model`,description:`Get the currently active model and profile settings.`,inputSchema:{type:`object`,properties:{scope:{type:`string`},slot:{type:`string`,enum:Q.options}},additionalProperties:!1}},{name:`set_model_target`,description:`Set the scoped slot main target directly by provider, model, and reasoning effort.`,inputSchema:{type:`object`,properties:{provider:{type:`string`},model:{type:`string`},reasoningEffort:{type:`string`,enum:[`minimal`,`low`,`medium`,`high`]},scope:{type:`string`},slot:{type:`string`,enum:Q.options},thinkingDisabled:{type:`boolean`}},required:[`provider`,`model`,`reasoningEffort`],additionalProperties:!1}},{name:`upsert_profile`,description:`Create or update a Codex model profile.`,inputSchema:{type:`object`,properties:{id:{type:`string`},label:{type:`string`},provider:{type:`string`},model:{type:`string`},endpoint:{type:`string`},reasoningEffort:{type:`string`,enum:[`minimal`,`low`,`medium`,`high`]},enabled:{type:`boolean`},scope:{type:`string`}},required:[`id`,`label`,`model`,`endpoint`,`reasoningEffort`,`enabled`],additionalProperties:!1}}]})),i.setRequestHandler(re.CallToolRequestSchema,async t=>{let{name:i,arguments:a}=t.params;try{switch(i){case`get_proxy_status`:return $(JSON.stringify(await e.getStatus(n(a)),null,2));case`list_profiles`:return $(JSON.stringify(await e.listProfiles(n(a)),null,2));case`get_active_profile`:return $(JSON.stringify(await e.getActiveProfile(n(a),r(a)),null,2));case`get_current_model`:return $(JSON.stringify(await e.getCurrentModel(n(a),r(a)),null,2));case`set_model_target`:{let t=n(a),i=r(a),o=await e.getAdminConfig(t);return $(JSON.stringify(await e.updateAdminConfig({models:{[i]:{main:{provider:String(a?.provider),model:String(a?.model),reasoningEffort:h.z.enum([`minimal`,`low`,`medium`,`high`]).parse(String(a?.reasoningEffort)),thinkingDisabled:a?.thinkingDisabled===void 0?!1:!!a.thinkingDisabled},fallbacks:o.scopeModels[i]?.fallbacks??[]}}},t),null,2))}case`upsert_profile`:return $(JSON.stringify(await e.upsertProfile({id:String(a?.id),label:String(a?.label),provider:String(a?.provider||`chatgpt-codex`),providerType:null,model:String(a?.model),endpoint:String(a?.endpoint),reasoningEffort:String(a?.reasoningEffort),enabled:!!a?.enabled},n(a)),null,2));default:throw Error(`Unknown tool: ${i}`)}}catch(e){return{content:[{type:`text`,text:`Error executing tool ${i}: ${e instanceof Error?e.message:String(e)}`}],isError:!0}}}),i}function $(e){return{content:[{type:`text`,text:e}]}}var Nn=class{transport=null;constructor(e){this.server=e}async start(){this.transport=new ie.StdioServerTransport,await this.server.connect(this.transport)}async stop(){this.transport&&=(await this.transport.close(),null)}};Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return Ot}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return 43191}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return bn}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return Mn}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return ke}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return Dn}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return b}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return Nn}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return s}});
1133
+ //# sourceMappingURL=stdio-B-atmzW4.cjs.map