ghc-tunnel 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +113 -3
  2. package/dist/index.js +161 -124
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -23,8 +23,9 @@ On first run the proxy initiates GitHub Device Flow authentication if no `GITHUB
23
23
 
24
24
  ## Features
25
25
 
26
- - **OpenAI-compatible** `/v1/chat/completions` and `/v1/responses` endpoints
26
+ - **OpenAI-compatible** `/v1/chat/completions` and `/v1/responses` endpoints (with Codex adapters: `apply_patch` tool, `X-Initiator`, context compaction)
27
27
  - **Anthropic-compatible** `/v1/messages` endpoint (direct or translated)
28
+ - **Codex config auto-repair** — fills missing keys in `~/.codex/config.toml` on startup
28
29
  - Automatic **model name translation** via configurable mappings
29
30
  - **Streaming** support (SSE) for all endpoints
30
31
  - **Request cache** with analytics dashboard
@@ -55,18 +56,127 @@ Run `ghc-tunnel --setup --claudecode` or manually configure `~/.claude/settings.
55
56
  "env": {
56
57
  "ANTHROPIC_BASE_URL": "http://localhost:8314/",
57
58
  "ANTHROPIC_AUTH_TOKEN": "dummy",
58
- "ANTHROPIC_MODEL": "claude-opus-4-6[1m]",
59
- "ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-sonnet-4-6"
59
+ "ANTHROPIC_MODEL": "claude-opus-4-7[1m]",
60
+ "ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-opus-4-7[1m]"
60
61
  }
61
62
  }
62
63
  ```
63
64
 
65
+ ## Codex Integration
66
+
67
+ `ghc-tunnel` exposes the OpenAI Responses API (`/v1/responses`) with Codex-specific adapters so the Codex CLI can drive Copilot models directly.
68
+
69
+ On startup ghc-tunnel automatically repairs `~/.codex/config.toml` — if any of `model`, `model_reasoning_effort`, `personality`, `model_provider`, or the `[model_providers.ghc-tunnel]` block is missing (or the block is partial), the missing pieces are filled in. When `model` or `model_reasoning_effort` is absent it prompts interactively (defaults: `gpt-5.4` / `high`, `xhigh` also valid).
70
+
71
+ `ghc-tunnel --setup` forces a full rewrite of those keys (still prompting for model + reasoning).
72
+
73
+ Resulting config:
74
+
75
+ ```toml
76
+ model = "gpt-5.4"
77
+ model_reasoning_effort = "high"
78
+ personality = "pragmatic"
79
+ model_provider = "ghc-tunnel"
80
+
81
+ [model_providers.ghc-tunnel]
82
+ name = "GHC TUNNEL"
83
+ base_url = "http://localhost:8314/v1"
84
+ wire_api = "responses"
85
+ ```
86
+
87
+ Codex-specific behavior on `/v1/responses`:
88
+
89
+ - Custom `apply_patch` tool is rewritten as an OpenAI function tool with a JSON schema (Copilot rejects `type: "custom"`).
90
+ - `X-Initiator` header is derived from the last input item's role so resumed tool turns aren't billed as fresh user interactions.
91
+ - `type: "compaction"` markers in the input array cause everything before the latest one to be trimmed.
92
+ - `service_tier` is nulled (Copilot rejects it).
93
+ - Unsupported tool types (`web_search`, `image_generation`) are stripped to avoid 400 errors.
94
+
64
95
  ## Configuration
65
96
 
66
97
  Config file: `~/.ghc-tunnel/config.yaml` (generated on first run or with `--config`).
67
98
 
68
99
  See [docs/configuration.md](docs/configuration.md) for full reference.
69
100
 
101
+ ### Config Sync and OneDrive
102
+
103
+ `ghc-api` can manage and sync these files:
104
+
105
+ - Claude Code: `~/.claude/settings.json`
106
+ - Codex: `~/.codex/config.toml`
107
+ - ghc-api: `~/.ghc-api/config.yaml` (or `%APPDATA%/ghc-api/config.yaml` on Windows)
108
+
109
+ OneDrive detection priority:
110
+
111
+ 1. `~/OneDrive - *`
112
+ 2. `~/OneDrive`
113
+ 3. In WSL: `/mnt/c/Users/<username>/OneDrive - *` then `/mnt/c/Users/<username>/OneDrive`
114
+
115
+ To disable all OneDrive-dependent operations, set `disable_onedrive_access: true` in `config.yaml`.
116
+ When enabled, ghc-api skips OneDrive detection, config sync actions, and shared OneDrive hash reads.
117
+
118
+ Sync target folder:
119
+
120
+ - `.ghc-api/configSync` under detected OneDrive root
121
+
122
+ Machine folder:
123
+
124
+ - `.ghc-api/agents/{hostname}_{os}` where `os` is `Win`, `Linux`, or `WSL`
125
+
126
+ Hash files:
127
+
128
+ - `.ghc-api/configSync/config.sha1`
129
+ - `.ghc-api/agents/{hostname}_{os}/ghc-api/config.sha1`
130
+
131
+ Hashes are recalculated when local config file timestamp is newer than the hash file.
132
+ On startup, ghc-api checks synced files and prints config differences to stdout (and UI indicator if different).
133
+
134
+ ### Token Usage Logging
135
+
136
+ Every 5 minutes, ghc-api writes token usage delta (if non-zero) to:
137
+
138
+ - OneDrive mode: `.ghc-api/agents/{hostname}_{os}/token_usage.jl`
139
+ - Fallback when OneDrive is unavailable: `~/.ghc-api/token_usage.jl`
140
+
141
+ Also flushes pending usage on shutdown (`Ctrl+C`/termination/normal exit).
142
+
143
+ Each JSONL line includes:
144
+
145
+ - `timestamp` (unix seconds)
146
+ - `models` list with:
147
+ - `model`
148
+ - `request_count`
149
+ - `input_tokens`
150
+ - `output_tokens`
151
+ - `total_tokens`
152
+
153
+ ### Request File Logging
154
+
155
+ When `save_request_to_file: true`, ghc-api appends each completed request to:
156
+
157
+ - `<ghc-api config dir>/requests/YYYY-MM-DD.jl`
158
+
159
+ The saved `.jl` line format is the same as dashboard export (`/api/requests/export`) and can be imported by dashboard import (`/api/requests/import`).
160
+
161
+ ### Code Agent Interaction
162
+
163
+ The Code Agent page (`/agent`) provides a web interface for interacting with AI coding agents via the [Agent Client Protocol (ACP)](https://agentclientprotocol.com/). Supported agents:
164
+
165
+ | Agent | Package | Install |
166
+ |-------|---------|---------|
167
+ | Claude Code | `@agentclientprotocol/claude-agent-acp` | `npm install -g @agentclientprotocol/claude-agent-acp` |
168
+ | Codex | `codex-acp` | Download from [GitHub releases](https://github.com/zed-industries/codex-acp/releases) |
169
+ | Copilot CLI | `@github/copilot` | `npm install -g @github/copilot` |
170
+
171
+ Agent binaries are resolved in order: environment variable override (`CLAUDE_ACP_BINARY`, `CODEX_ACP_BINARY`, `COPILOT_CLI_BINARY`), then PATH lookup, then npm global packages.
172
+
173
+ Session data is stored in:
174
+
175
+ - OneDrive mode: `.ghc-api/agents/{hostname}_{os}/sessions/`
176
+ - Fallback: `~/.ghc-api/sessions/` (or `%APPDATA%/ghc-api/sessions/` on Windows)
177
+
178
+ Recent working directories are persisted to `workdirs.json` in the same location. Sessions from other machines are browsable via the machine selector dropdown when OneDrive is enabled.
179
+
70
180
  ## API Endpoints
71
181
 
72
182
  | Endpoint | Description |
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- "use strict";var gt=Object.create;var je=Object.defineProperty;var mt=Object.getOwnPropertyDescriptor;var ft=Object.getOwnPropertyNames;var _t=Object.getPrototypeOf,ht=Object.prototype.hasOwnProperty;var yt=(t,e,n,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of ft(e))!ht.call(t,s)&&s!==n&&je(t,s,{get:()=>e[s],enumerable:!(o=mt(e,s))||o.enumerable});return t};var A=(t,e,n)=>(n=t!=null?gt(_t(t)):{},yt(e||!t||!t.__esModule?je(n,"default",{value:t,enumerable:!0}):n,t));var ae=A(require("fs")),ke=A(require("path")),ye=A(require("express"));var te=A(require("fs")),ue=A(require("path")),Re=A(require("os")),Le=A(require("js-yaml")),He=parseInt(process.env.PORT||"8314",10),Me=process.env.HOST||"localhost",Ue="https://api.github.com",ne="0.26.7",oe="2025-04-01",se="1.93.0",we={exact:{opus:"claude-opus-4.6-1m",sonnet:"claude-opus-4.6-1m",haiku:"claude-haiku-4.5"},prefix:{"claude-sonnet-4-":"claude-opus-4.6-1m","claude-opus-4.5-":"claude-opus-4.6-1m","claude-opus-4.6-":"claude-opus-4.6-1m","claude-opus-4-5-":"claude-opus-4.6-1m","claude-opus-4-6-":"claude-opus-4.6-1m","claude-opus-4.5":"claude-opus-4.6-1m","claude-opus-4.6":"claude-opus-4.6-1m","claude-opus-4-6":"claude-opus-4.6-1m","claude-opus-4-6[1m]":"claude-opus-4.6-1m","claude-sonnet-4-6":"claude-opus-4.6-1m","claude-sonnet-4-5":"claude-opus-4.6-1m","claude-haiku-4.5-":"claude-haiku-4.5","claude-haiku-4-5-":"claude-haiku-4.5"}},Se="01ab8ac9400c4e429b23",be=class{exactMappings={};prefixMappings={};translate(e){if(e in this.exactMappings)return this.exactMappings[e];for(let[n,o]of Object.entries(this.prefixMappings))if(e.startsWith(n))return o;return e}loadFromConfig(e){let n=e.model_mappings;this.exactMappings=n?.exact??{},this.prefixMappings=n?.prefix??{}}},M=new be;function T(){return process.platform==="win32"?ue.default.join(process.env.APPDATA||Re.default.homedir(),"ghc-tunnel"):ue.default.join(Re.default.homedir(),".ghc-tunnel")}function Fe(t){let e=te.default.readFileSync(t,"utf-8");return Le.default.load(e)||{}}function ve(){let t=T();te.default.mkdirSync(t,{recursive:!0});let e=ue.default.join(t,"config.yaml");if(te.default.existsSync(e)){console.log(`Configuration file already exists at: ${e}`);return}let n=`# GitHub Copilot API Proxy Configuration
2
+ "use strict";var Ct=Object.create;var Ve=Object.defineProperty;var At=Object.getOwnPropertyDescriptor;var Ot=Object.getOwnPropertyNames;var Tt=Object.getPrototypeOf,Et=Object.prototype.hasOwnProperty;var Pt=(t,e,o,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of Ot(e))!Et.call(t,s)&&s!==o&&Ve(t,s,{get:()=>e[s],enumerable:!(n=At(e,s))||n.enumerable});return t};var v=(t,e,o)=>(o=t!=null?Ct(Tt(t)):{},Pt(e||!t||!t.__esModule?Ve(o,"default",{value:t,enumerable:!0}):o,t));var me=v(require("fs")),Ce=v(require("path")),ve=v(require("express"));var ie=v(require("fs")),_e=v(require("path")),Ae=v(require("os")),Ge=v(require("js-yaml")),We=parseInt(process.env.PORT||"8314",10),Xe=process.env.HOST||"localhost",Ke="https://api.github.com",ce="0.26.7",ae="2025-04-01",le="1.93.0",h="claude-opus-4.7-1m",G="claude-haiku-4.5",z="claude-opus-4-7[1m]",Te={exact:{opus:h,sonnet:h,"opus4-7":h,"4-7[1m]":h,haiku:G},prefix:{"claude-sonnet-4-":h,"claude-opus-4.5-":h,"claude-opus-4.6-":h,"claude-opus-4.7-":h,"claude-opus-4-5-":h,"claude-opus-4-6-":h,"claude-opus-4-7-":h,"claude-opus-4.5":h,"claude-opus-4.6":h,"claude-opus-4.7":h,"claude-opus-4-6":h,"claude-opus-4-7":h,"claude-opus-4-6[1m]":h,"claude-opus-4-7[1m]":h,"claude-sonnet-4-7":h,"claude-sonnet-4-6":h,"claude-sonnet-4-5":h,"claude-haiku-4.5-":G,"claude-haiku-4-5-":G}},Ee="01ab8ac9400c4e429b23",Oe=class{exactMappings={};prefixMappings={};translate(e){if(e in this.exactMappings)return this.exactMappings[e];for(let[o,n]of Object.entries(this.prefixMappings))if(e.startsWith(o))return n;return e}loadFromConfig(e){let o=e.model_mappings;this.exactMappings=o?.exact??{},this.prefixMappings=o?.prefix??{}}},J=new Oe;function E(){return process.platform==="win32"?_e.default.join(process.env.APPDATA||Ae.default.homedir(),"ghc-tunnel"):_e.default.join(Ae.default.homedir(),".ghc-tunnel")}function Ye(t){let e=ie.default.readFileSync(t,"utf-8");return Ge.default.load(e)||{}}function Pe(){let t=E();ie.default.mkdirSync(t,{recursive:!0});let e=_e.default.join(t,"config.yaml");if(ie.default.existsSync(e)){console.log(`Configuration file already exists at: ${e}`);return}let o=`# GitHub Copilot API Proxy Configuration
3
3
  # ========================================
4
4
 
5
5
  # Server Settings
@@ -12,31 +12,39 @@ debug: false
12
12
  account_type: individual
13
13
 
14
14
  # Header version strings (only affect request headers to Copilot API)
15
- vscode_version: "${se}"
16
- api_version: "${oe}"
17
- copilot_version: "${ne}"
15
+ vscode_version: "${le}"
16
+ api_version: "${ae}"
17
+ copilot_version: "${ce}"
18
18
 
19
19
  # Model Name Mappings
20
20
  # Two types: exact (full name match) and prefix (starts-with match)
21
21
  model_mappings:
22
22
  exact:
23
- opus: claude-opus-4.6-1m
24
- sonnet: claude-opus-4.6-1m
25
- haiku: claude-haiku-4.5
23
+ opus: ${h}
24
+ sonnet: ${h}
25
+ opus4-7: ${h}
26
+ "4-7[1m]": ${h}
27
+ haiku: ${G}
26
28
  prefix:
27
- claude-sonnet-4-: claude-opus-4.6-1m
28
- claude-opus-4.5-: claude-opus-4.6-1m
29
- claude-opus-4.6-: claude-opus-4.6-1m
30
- claude-opus-4-5-: claude-opus-4.6-1m
31
- claude-opus-4-6-: claude-opus-4.6-1m
32
- "claude-opus-4.5": claude-opus-4.6-1m
33
- "claude-opus-4.6": claude-opus-4.6-1m
34
- "claude-opus-4-6": claude-opus-4.6-1m
35
- "claude-opus-4-6[1m]": claude-opus-4.6-1m
36
- claude-sonnet-4-6: claude-opus-4.6-1m
37
- claude-sonnet-4-5: claude-opus-4.6-1m
38
- claude-haiku-4.5-: claude-haiku-4.5
39
- claude-haiku-4-5-: claude-haiku-4.5
29
+ claude-sonnet-4-: ${h}
30
+ claude-opus-4.5-: ${h}
31
+ claude-opus-4.6-: ${h}
32
+ claude-opus-4.7-: ${h}
33
+ claude-opus-4-5-: ${h}
34
+ claude-opus-4-6-: ${h}
35
+ claude-opus-4-7-: ${h}
36
+ "claude-opus-4.5": ${h}
37
+ "claude-opus-4.6": ${h}
38
+ "claude-opus-4.7": ${h}
39
+ "claude-opus-4-6": ${h}
40
+ "claude-opus-4-7": ${h}
41
+ "claude-opus-4-6[1m]": ${h}
42
+ "claude-opus-4-7[1m]": ${h}
43
+ claude-sonnet-4-7: ${h}
44
+ claude-sonnet-4-6: ${h}
45
+ claude-sonnet-4-5: ${h}
46
+ claude-haiku-4.5-: ${G}
47
+ claude-haiku-4-5-: ${G}
40
48
 
41
49
  # Content Filtering
42
50
  # system_prompt_remove: strings to strip from system prompts
@@ -49,110 +57,115 @@ tool_result_suffix_remove: []
49
57
  # Retry Settings
50
58
  # Max retries for upstream connection errors (0 = no retries)
51
59
  max_connection_retries: 3
52
- `;te.default.writeFileSync(e,n,"utf-8"),console.log(`Configuration file generated at: ${e}`)}var xe=class{githubToken="";copilotToken=null;models=null;accountType="individual";tokenExpiresAt=0;vscodeVersion=se;copilotVersion=ne;apiVersion=oe;systemPromptRemove=[];systemPromptAdd=[];toolResultSuffixRemove=[];redirectAnthropic=!1;maxConnectionRetries=3;get editorPluginVersion(){return`copilot-chat/${this.copilotVersion}`}get userAgent(){return`GitHubCopilotChat/${this.copilotVersion}`}},g=new xe;var re=A(require("fs")),Be=require("child_process"),ze=A(require("path"));function Ve(){let t=T();return re.default.mkdirSync(t,{recursive:!0}),ze.default.join(t,"github_token.txt")}function kt(){let t=Ve();if(!re.default.existsSync(t))return null;try{let e=re.default.readFileSync(t,"utf-8").trim();if(e)return console.log(`Loaded GitHub token from ${t}`),e}catch(e){console.log(`Failed to read token file: ${e}`)}return null}function Rt(t){try{let e=Ve();re.default.writeFileSync(e,t,"utf-8"),console.log(`Saved GitHub token to ${e}`)}catch(e){console.log(`Failed to save token file: ${e}`)}}function bt(t){let e=process.platform==="darwin"?"open":process.platform==="win32"?"start":"xdg-open";(0,Be.exec)(`${e} ${t}`,()=>{})}function wt(t){return new Promise(e=>setTimeout(e,t))}async function St(){console.log(`
53
- `+"=".repeat(60)),console.log("GitHub Device Flow Authentication"),console.log("=".repeat(60));let t=await fetch("https://github.com/login/device/code",{method:"POST",headers:{Accept:"application/json","Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:Se,scope:"read:user copilot"})});if(!t.ok)return console.log(`Failed to get device code: ${t.status} ${await t.text()}`),null;let e=await t.json(),{device_code:n,user_code:o,verification_uri:s,expires_in:r=900}=e,i=e.interval??5;console.log(`
54
- Please visit: ${s}`),console.log(`And enter the code: ${o}`),console.log(`
55
- Waiting for authorization (expires in ${r} seconds)...`),bt(s),console.log("(Browser opened automatically)");let c=Date.now()+r*1e3;for(;Date.now()<c;){await wt(i*1e3);let a=await fetch("https://github.com/login/oauth/access_token",{method:"POST",headers:{Accept:"application/json","Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:Se,device_code:n,grant_type:"urn:ietf:params:oauth:grant-type:device_code"})});if(!a.ok)continue;let u=await a.json();if(u.error==="authorization_pending"){process.stdout.write(".");continue}if(u.error==="slow_down"){i+=5;continue}if(u.error==="expired_token")return console.log(`
56
- Authorization expired. Please try again.`),null;if(u.error==="access_denied")return console.log(`
57
- Authorization denied by user.`),null;if(u.error)return console.log(`
58
- Error: ${u.error_description||u.error}`),null;if(u.access_token)return console.log(`
59
-
60
- Authorization successful!`),u.access_token}return console.log(`
61
- Authorization timed out. Please try again.`),null}async function le(){let t=(process.env.GITHUB_TOKEN||"").trim();if(t)return console.log("Using GitHub token from GITHUB_TOKEN environment variable"),t;let e=kt();if(e)return e;console.log(`
62
- No GitHub token found. Starting GitHub Device Flow authentication...`);let n=await St();return n?(Rt(n),n):null}function D(){return g.accountType==="individual"?"https://api.githubcopilot.com":`https://api.${g.accountType}.githubcopilot.com`}function vt(){return{"Content-Type":"application/json",Accept:"application/json",Authorization:`token ${g.githubToken}`,"Editor-Version":`vscode/${g.vscodeVersion}`,"Editor-Plugin-Version":g.editorPluginVersion,"User-Agent":g.userAgent,"X-GitHub-Api-Version":g.apiVersion,"X-VSCode-User-Agent-Library-Version":"electron-fetch"}}function V(t=!1){let e={Authorization:`Bearer ${g.copilotToken}`,"Content-Type":"application/json","Copilot-Integration-Id":"vscode-chat","Editor-Version":`vscode/${g.vscodeVersion}`,"Editor-Plugin-Version":g.editorPluginVersion,"User-Agent":g.userAgent,"OpenAI-Intent":"conversation-panel","X-GitHub-Api-Version":g.apiVersion,"X-Request-Id":crypto.randomUUID(),"X-VSCode-User-Agent-Library-Version":"electron-fetch"};return t&&(e["Copilot-Vision-Request"]="true"),e}async function ie(){if(g.copilotToken&&Date.now()/1e3<g.tokenExpiresAt-60)return;console.log("Refreshing Copilot token...");let t=await fetch(`${Ue}/copilot_internal/v2/token`,{headers:vt()});if(!t.ok)throw new Error(`Failed to get Copilot token: ${t.status} ${await t.text()}`);let e=await t.json();g.copilotToken=e.token,g.tokenExpiresAt=Date.now()/1e3+(e.refresh_in??1800),console.log("Copilot token refreshed successfully")}async function N(){(!g.copilotToken||Date.now()/1e3>=g.tokenExpiresAt-60)&&await ie()}async function W(){await N();let t=await fetch(`${D()}/models`,{headers:V()});t.ok?(g.models=await t.json(),console.log(`Loaded ${g.models.data?.length??0} models`)):console.log(`Failed to fetch models: ${t.status}`)}function Je(t){return g.redirectAnthropic?!1:g.models?.data?.find(n=>n.id===t)?.supported_endpoints?.includes("/v1/messages")??!1}function Ge(t){return g.models?.data?.find(n=>n.id===t)?.supported_endpoints?.includes("/responses")??!1}function j(t){return Math.ceil(t.length/4)}var J=A(require("fs")),pe=A(require("path"));function K(t,e,n,o){let s=T();J.default.mkdirSync(s,{recursive:!0});let r=pe.default.join(s,"error.log"),i={timestamp:new Date().toISOString(),endpoint:t,status_code:o,request:e,response:n};J.default.appendFileSync(r,JSON.stringify(i)+`
63
- `,"utf-8")}function de(t,e,n,o,s){try{let r=T();J.default.mkdirSync(r,{recursive:!0});let i=pe.default.join(r,"connection_retry.jl"),c={timestamp:new Date().toISOString(),request_id:t,endpoint:e,attempt:n+1,max_attempts:o+1,error_type:s.constructor.name,error_message:s.message};J.default.appendFileSync(i,JSON.stringify(c)+`
64
- `,"utf-8")}catch{}}function ge(t){try{let e=T();J.default.mkdirSync(e,{recursive:!0});let n=pe.default.join(e,"tool_result_cleanup.jl");t.timestamp=new Date().toISOString(),J.default.appendFileSync(n,JSON.stringify(t)+`
65
- `,"utf-8")}catch{}}function $e(t){let e=[],n="unexpected `tool_use_id` found in `tool_result` blocks: ",o=t.indexOf(n);if(o!==-1){let s=o+n.length,r=s;for(;r<t.length&&!/[. "'\\\n]/.test(t[r]);)r++;let i=t.slice(s,r).trim();i&&e.push(i)}if(e.length===0){let s=t;for(;s.includes("toolu_");){let r=s.indexOf("toolu_"),i=r+6;for(;i<s.length&&/[\w-]/.test(s[i]);)i++;let c=s.slice(r,i);c&&!e.includes(c)&&e.push(c),s=s.slice(i)}}return e}function Ae(t,e){if(!e.length)return t;let n=new Set(e),o=[];for(let s of t){if(s.role!=="user"){o.push(s);continue}let r=s.content;if(!Array.isArray(r)){o.push(s);continue}let i=r.filter(c=>c.type==="tool_result"&&n.has(c.tool_use_id)?(console.log(`[Tool Result Cleanup] Removing orphaned tool_result: ${c.tool_use_id}`),!1):!0);i.length>0&&o.push({...s,content:i})}return o}function Ce(t,e){return t===400&&e.includes("tool_use_id")&&e.includes("tool_result")}function Oe(){console.log(`
66
- `+"=".repeat(60)),console.log("Model Name Mappings"),console.log("=".repeat(60));let t=Object.entries(M.exactMappings);if(t.length){console.log(`
67
- Exact Mappings:`);for(let[n,o]of t)console.log(` ${n} -> ${o}`)}else console.log(`
68
- Exact Mappings: (none)`);let e=Object.entries(M.prefixMappings);if(e.length){console.log(`
69
- Prefix Mappings:`);for(let[n,o]of e)console.log(` ${n}* -> ${o}`)}else console.log(`
60
+ `;ie.default.writeFileSync(e,o,"utf-8"),console.log(`Configuration file generated at: ${e}`)}var De=class{githubToken="";copilotToken=null;models=null;accountType="individual";tokenExpiresAt=0;vscodeVersion=le;copilotVersion=ce;apiVersion=ae;systemPromptRemove=[];systemPromptAdd=[];toolResultSuffixRemove=[];redirectAnthropic=!1;maxConnectionRetries=3;get editorPluginVersion(){return`copilot-chat/${this.copilotVersion}`}get userAgent(){return`GitHubCopilotChat/${this.copilotVersion}`}},f=new De;var ue=v(require("fs")),Qe=require("child_process"),Ze=v(require("path"));function et(){let t=E();return ue.default.mkdirSync(t,{recursive:!0}),Ze.default.join(t,"github_token.txt")}function Dt(){let t=et();if(!ue.default.existsSync(t))return null;try{let e=ue.default.readFileSync(t,"utf-8").trim();if(e)return console.log(`Loaded GitHub token from ${t}`),e}catch(e){console.log(`Failed to read token file: ${e}`)}return null}function qt(t){try{let e=et();ue.default.writeFileSync(e,t,"utf-8"),console.log(`Saved GitHub token to ${e}`)}catch(e){console.log(`Failed to save token file: ${e}`)}}function It(t){let e=process.platform==="darwin"?"open":process.platform==="win32"?"start":"xdg-open";(0,Qe.exec)(`${e} ${t}`,()=>{})}function Nt(t){return new Promise(e=>setTimeout(e,t))}async function Lt(){console.log(`
61
+ `+"=".repeat(60)),console.log("GitHub Device Flow Authentication"),console.log("=".repeat(60));let t=await fetch("https://github.com/login/device/code",{method:"POST",headers:{Accept:"application/json","Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:Ee,scope:"read:user copilot"})});if(!t.ok)return console.log(`Failed to get device code: ${t.status} ${await t.text()}`),null;let e=await t.json(),{device_code:o,user_code:n,verification_uri:s,expires_in:r=900}=e,i=e.interval??5;console.log(`
62
+ Please visit: ${s}`),console.log(`And enter the code: ${n}`),console.log(`
63
+ Waiting for authorization (expires in ${r} seconds)...`),It(s),console.log("(Browser opened automatically)");let c=Date.now()+r*1e3;for(;Date.now()<c;){await Nt(i*1e3);let a=await fetch("https://github.com/login/oauth/access_token",{method:"POST",headers:{Accept:"application/json","Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:Ee,device_code:o,grant_type:"urn:ietf:params:oauth:grant-type:device_code"})});if(!a.ok)continue;let l=await a.json();if(l.error==="authorization_pending"){process.stdout.write(".");continue}if(l.error==="slow_down"){i+=5;continue}if(l.error==="expired_token")return console.log(`
64
+ Authorization expired. Please try again.`),null;if(l.error==="access_denied")return console.log(`
65
+ Authorization denied by user.`),null;if(l.error)return console.log(`
66
+ Error: ${l.error_description||l.error}`),null;if(l.access_token)return console.log(`
67
+
68
+ Authorization successful!`),l.access_token}return console.log(`
69
+ Authorization timed out. Please try again.`),null}async function he(){let t=(process.env.GITHUB_TOKEN||"").trim();if(t)return console.log("Using GitHub token from GITHUB_TOKEN environment variable"),t;let e=Dt();if(e)return e;console.log(`
70
+ No GitHub token found. Starting GitHub Device Flow authentication...`);let o=await Lt();return o?(qt(o),o):null}function N(){return f.accountType==="individual"?"https://api.githubcopilot.com":`https://api.${f.accountType}.githubcopilot.com`}function Ut(){return{"Content-Type":"application/json",Accept:"application/json",Authorization:`token ${f.githubToken}`,"Editor-Version":`vscode/${f.vscodeVersion}`,"Editor-Plugin-Version":f.editorPluginVersion,"User-Agent":f.userAgent,"X-GitHub-Api-Version":f.apiVersion,"X-VSCode-User-Agent-Library-Version":"electron-fetch"}}function Y(t=!1){let e={Authorization:`Bearer ${f.copilotToken}`,"Content-Type":"application/json","Copilot-Integration-Id":"vscode-chat","Editor-Version":`vscode/${f.vscodeVersion}`,"Editor-Plugin-Version":f.editorPluginVersion,"User-Agent":f.userAgent,"OpenAI-Intent":"conversation-panel","X-GitHub-Api-Version":f.apiVersion,"X-Request-Id":crypto.randomUUID(),"X-VSCode-User-Agent-Library-Version":"electron-fetch"};return t&&(e["Copilot-Vision-Request"]="true"),e}async function pe(){if(f.copilotToken&&Date.now()/1e3<f.tokenExpiresAt-60)return;console.log("Refreshing Copilot token...");let t=await fetch(`${Ke}/copilot_internal/v2/token`,{headers:Ut()});if(!t.ok)throw new Error(`Failed to get Copilot token: ${t.status} ${await t.text()}`);let e=await t.json();f.copilotToken=e.token,f.tokenExpiresAt=Date.now()/1e3+(e.refresh_in??1800),console.log("Copilot token refreshed successfully")}async function U(){(!f.copilotToken||Date.now()/1e3>=f.tokenExpiresAt-60)&&await pe()}async function te(){await U();let t=await fetch(`${N()}/models`,{headers:Y()});t.ok?(f.models=await t.json(),console.log(`Loaded ${f.models.data?.length??0} models`)):console.log(`Failed to fetch models: ${t.status}`)}function tt(t){return f.redirectAnthropic?!1:f.models?.data?.find(o=>o.id===t)?.supported_endpoints?.includes("/v1/messages")??!1}function nt(t){return f.models?.data?.find(o=>o.id===t)?.supported_endpoints?.includes("/responses")??!1}function j(t){return Math.ceil(t.length/4)}var Q=v(require("fs")),ye=v(require("path"));function Z(t,e,o,n){let s=E();Q.default.mkdirSync(s,{recursive:!0});let r=ye.default.join(s,"error.log"),i={timestamp:new Date().toISOString(),endpoint:t,status_code:n,request:e,response:o};Q.default.appendFileSync(r,JSON.stringify(i)+`
71
+ `,"utf-8")}function Re(t,e,o,n,s){try{let r=E();Q.default.mkdirSync(r,{recursive:!0});let i=ye.default.join(r,"connection_retry.jl"),c={timestamp:new Date().toISOString(),request_id:t,endpoint:e,attempt:o+1,max_attempts:n+1,error_type:s.constructor.name,error_message:s.message};Q.default.appendFileSync(i,JSON.stringify(c)+`
72
+ `,"utf-8")}catch{}}function ke(t){try{let e=E();Q.default.mkdirSync(e,{recursive:!0});let o=ye.default.join(e,"tool_result_cleanup.jl");t.timestamp=new Date().toISOString(),Q.default.appendFileSync(o,JSON.stringify(t)+`
73
+ `,"utf-8")}catch{}}function qe(t){let e=[],o="unexpected `tool_use_id` found in `tool_result` blocks: ",n=t.indexOf(o);if(n!==-1){let s=n+o.length,r=s;for(;r<t.length&&!/[. "'\\\n]/.test(t[r]);)r++;let i=t.slice(s,r).trim();i&&e.push(i)}if(e.length===0){let s=t;for(;s.includes("toolu_");){let r=s.indexOf("toolu_"),i=r+6;for(;i<s.length&&/[\w-]/.test(s[i]);)i++;let c=s.slice(r,i);c&&!e.includes(c)&&e.push(c),s=s.slice(i)}}return e}function Ie(t,e){if(!e.length)return t;let o=new Set(e),n=[];for(let s of t){if(s.role!=="user"){n.push(s);continue}let r=s.content;if(!Array.isArray(r)){n.push(s);continue}let i=r.filter(c=>c.type==="tool_result"&&o.has(c.tool_use_id)?(console.log(`[Tool Result Cleanup] Removing orphaned tool_result: ${c.tool_use_id}`),!1):!0);i.length>0&&n.push({...s,content:i})}return n}function Ne(t,e){return t===400&&e.includes("tool_use_id")&&e.includes("tool_result")}function Le(){console.log(`
74
+ `+"=".repeat(60)),console.log("Model Name Mappings"),console.log("=".repeat(60));let t=Object.entries(J.exactMappings);if(t.length){console.log(`
75
+ Exact Mappings:`);for(let[o,n]of t)console.log(` ${o} -> ${n}`)}else console.log(`
76
+ Exact Mappings: (none)`);let e=Object.entries(J.prefixMappings);if(e.length){console.log(`
77
+ Prefix Mappings:`);for(let[o,n]of e)console.log(` ${o}* -> ${n}`)}else console.log(`
70
78
  Prefix Mappings: (none)`);console.log("=".repeat(60)+`
71
- `)}function Te(){if(!g.models?.data?.length){console.log("No models available yet.");return}console.log(`
72
- `+"=".repeat(60)),console.log("Available Models"),console.log("=".repeat(60));for(let t of g.models.data){let e=t.capabilities,n=c=>c&&c>=1e3?`${Math.floor(c/1e3)}K`:String(c??"N/A"),o=n(e?.limits?.max_context_window_tokens),s=n(e?.limits?.max_prompt_tokens),r=n(e?.limits?.max_output_tokens),i=[];e?.supports?.vision&&i.push("Vision"),e?.supports?.tool_calls&&i.push("Tool"),t.supported_endpoints?.includes("/v1/messages")&&i.push("Anthropic"),t.preview&&i.push("Preview"),console.log(`${t.id.padEnd(30)} ctx: ${o} in: ${s} out: ${r} [${t.vendor??"unknown"}] (${i.join(",")})`)}console.log(`
79
+ `)}function Ue(){if(!f.models?.data?.length){console.log("No models available yet.");return}console.log(`
80
+ `+"=".repeat(60)),console.log("Available Models"),console.log("=".repeat(60));for(let t of f.models.data){let e=t.capabilities,o=c=>c&&c>=1e3?`${Math.floor(c/1e3)}K`:String(c??"N/A"),n=o(e?.limits?.max_context_window_tokens),s=o(e?.limits?.max_prompt_tokens),r=o(e?.limits?.max_output_tokens),i=[];e?.supports?.vision&&i.push("Vision"),e?.supports?.tool_calls&&i.push("Tool"),t.supported_endpoints?.includes("/v1/messages")&&i.push("Anthropic"),t.preview&&i.push("Preview"),console.log(`${t.id.padEnd(30)} ctx: ${n} in: ${s} out: ${r} [${t.vendor??"unknown"}] (${i.join(",")})`)}console.log(`
73
81
  `+"=".repeat(60)+`
74
- `)}var Xe=require("express");var Ee=class{cache=new Map;maxEntries;requestCount=0;bytesSent=0;bytesReceived=0;modelStats={};endpointStats={};constructor(e=1e3){this.maxEntries=e}startRequest(e,n){this.evictIfFull(),this.cache.set(e,{id:e,timestamp:new Date().toISOString(),original_request_body:n.original_request_body??null,request_body:n.request_body??null,response_body:null,model:n.model??"unknown",translated_model:n.translated_model??null,endpoint:n.endpoint??"unknown",status_code:null,request_size:n.request_size??0,response_size:0,input_tokens:0,output_tokens:0,duration:0,state:"pending"})}updateRequestState(e,n,o){let s=this.cache.get(e);s&&(s.state=n,o&&Object.assign(s,o))}completeRequest(e,n){let o=this.cache.get(e);o?Object.assign(o,{response_body:n.response_body,status_code:n.status_code??200,response_size:n.response_size??0,input_tokens:n.input_tokens??0,output_tokens:n.output_tokens??0,duration:n.duration??0,state:(n.status_code??200)<400?"completed":"error"}):(this.evictIfFull(),o={id:e,timestamp:new Date().toISOString(),original_request_body:n.original_request_body??null,request_body:n.request_body??null,response_body:n.response_body??null,model:n.model??"unknown",translated_model:n.translated_model??null,endpoint:n.endpoint??"unknown",status_code:n.status_code??200,request_size:n.request_size??0,response_size:n.response_size??0,input_tokens:n.input_tokens??0,output_tokens:n.output_tokens??0,duration:n.duration??0,state:(n.status_code??200)<400?"completed":"error"},this.cache.set(e,o)),this.requestCount++,this.bytesSent+=n.request_size??0,this.bytesReceived+=n.response_size??0,this.updateModelStats(n),this.updateEndpointStats(n)}addRequest(e,n){this.cache.has(e)?this.completeRequest(e,n):(this.startRequest(e,n),this.completeRequest(e,n))}getRequest(e){return this.cache.get(e)}getRecentRequests(e=50,n=0){return[...this.cache.values()].reverse().slice(n,n+e)}getTotalCount(){return this.cache.size}getStats(){return{total_requests:this.requestCount,cached_requests:this.cache.size,bytes_sent:this.bytesSent,bytes_received:this.bytesReceived,model_stats:{...this.modelStats},endpoint_stats:{...this.endpointStats}}}searchRequests(e,n=50,o=0){let s=e.toLowerCase(),r=[];for(let i of[...this.cache.values()].reverse())(i.model.toLowerCase().includes(s)||i.endpoint.toLowerCase().includes(s)||JSON.stringify(i.request_body).toLowerCase().includes(s))&&r.push(i);return r.slice(o,o+n)}fulltextSearch(e,n=50,o=0){let s=e.toLowerCase(),r=[];for(let i of[...this.cache.values()].reverse()){let c=JSON.stringify(i.request_body).toLowerCase(),a=JSON.stringify(i.response_body).toLowerCase();(c.includes(s)||a.includes(s))&&r.push(i)}return[r.slice(o,o+n),r.length]}getAllRequests(){return[...this.cache.values()]}importRequest(e){let n=e.id||crypto.randomUUID();this.evictIfFull();let o={id:n,timestamp:e.timestamp||new Date().toISOString(),original_request_body:e.original_request_body??null,request_body:e.request_body??null,response_body:e.response_body??null,model:e.model||"unknown",translated_model:e.translated_model||null,endpoint:e.endpoint||"unknown",status_code:e.status_code??200,request_size:e.request_size??0,response_size:e.response_size??0,input_tokens:e.input_tokens??0,output_tokens:e.output_tokens??0,duration:e.duration??0,state:e.state||"completed"};this.cache.set(n,o),this.requestCount++,this.bytesSent+=o.request_size,this.bytesReceived+=o.response_size,this.updateModelStats(o),this.updateEndpointStats(o)}evictIfFull(){if(this.cache.size>=this.maxEntries){let e=this.cache.keys().next().value;this.cache.delete(e)}}updateModelStats(e){let n=e.model??"unknown";this.modelStats[n]||(this.modelStats[n]={request_count:0,input_tokens:0,output_tokens:0,bytes_sent:0,bytes_received:0});let o=this.modelStats[n];o.request_count++,o.input_tokens+=e.input_tokens??0,o.output_tokens+=e.output_tokens??0,o.bytes_sent+=e.request_size??0,o.bytes_received+=e.response_size??0}updateEndpointStats(e){let n=e.endpoint??"unknown";this.endpointStats[n]||(this.endpointStats[n]={request_count:0,bytes_sent:0,bytes_received:0});let o=this.endpointStats[n];o.request_count++,o.bytes_sent+=e.request_size??0,o.bytes_received+=e.response_size??0}},y=new Ee;function X(t){return M.translate(t)}function me(t){for(let e of g.systemPromptRemove)t.includes(e)&&(t=t.replaceAll(e,""),console.log(`[Content Filter] Removed from system prompt: ${e.slice(0,50)}${e.length>50?"...":""}`));return t}function Pe(t){for(let e of g.toolResultSuffixRemove)t.endsWith(e)&&(t=t.slice(0,-e.length),console.log(`[Content Filter] Removed suffix from tool result: ${e.slice(0,50)}${e.length>50?"...":""}`));return t}function We(t){let e=[],n=t.system;if(n){if(typeof n=="string")e.push({role:"system",content:me(n)});else if(Array.isArray(n)){let r=n.filter(i=>i.type==="text"&&!(i.text||"").includes("x-anthropic-billing-header")).map(i=>i.text);r.length&&e.push({role:"system",content:me(r.join(`
75
-
76
- `))})}}for(let r of t.messages||[]){let i=r.role,c=r.content;if(i==="user")if(Array.isArray(c)){let a=c.filter(p=>p.type==="tool_result"),u=c.filter(p=>p.type!=="tool_result");for(let p of a){let l=p.content??"";typeof l=="string"&&(l=Pe(l)),e.push({role:"tool",tool_call_id:p.tool_use_id,content:l})}if(u.length){let p=xt(u);p!=null&&e.push({role:"user",content:p})}}else e.push({role:"user",content:c});else if(i==="assistant")if(Array.isArray(c)){let a=c.filter(l=>l.type==="tool_use"),p=c.filter(l=>l.type==="text"||l.type==="thinking").map(l=>(l.type==="text"?l.text:l.thinking)??"").join(`
77
-
78
- `);a.length?e.push({role:"assistant",content:p||null,tool_calls:a.map(l=>({id:l.id,type:"function",function:{name:l.name,arguments:JSON.stringify(l.input??{})}}))}):e.push({role:"assistant",content:p})}else e.push({role:"assistant",content:c})}let o={model:X(t.model??""),messages:e,max_tokens:t.max_tokens,stream:t.stream??!1};t.temperature!=null&&(o.temperature=t.temperature),t.top_p!=null&&(o.top_p=t.top_p),t.stop_sequences&&(o.stop=t.stop_sequences),Array.isArray(t.tools)&&(o.tools=t.tools.map(r=>({type:"function",function:{name:r.name,description:r.description,parameters:r.input_schema??{}}})));let s=t.tool_choice;if(s){let r=s.type;r==="auto"?o.tool_choice="auto":r==="any"?o.tool_choice="required":r==="none"?o.tool_choice="none":r==="tool"&&s.name&&(o.tool_choice={type:"function",function:{name:s.name}})}return o}function xt(t){if(!t.some(o=>o.type==="image")){let o=[];for(let s of t)s.type==="text"?o.push(s.text??""):s.type==="thinking"&&o.push(s.thinking??"");return o.length?o.join(`
79
-
80
- `):null}let n=[];for(let o of t)if(o.type==="text")n.push({type:"text",text:o.text});else if(o.type==="thinking")n.push({type:"text",text:o.thinking});else if(o.type==="image"){let s=o.source;n.push({type:"image_url",image_url:{url:`data:${s?.media_type};base64,${s?.data}`}})}return n.length?n:null}var $t={stop:"end_turn",length:"max_tokens",tool_calls:"tool_use",content_filter:"refusal"};function qe(t){return t?$t[t]??null:null}function De(t){let e=[],n=null;for(let i of t.choices||[]){let c=i.message;if(c){if(c.content&&e.push({type:"text",text:c.content}),Array.isArray(c.tool_calls))for(let a of c.tool_calls){let u=a.function,p;try{p=JSON.parse(u.arguments||"{}")}catch{p={_raw_arguments:u.arguments}}e.push({type:"tool_use",id:a.id,name:u.name,input:p})}i.finish_reason&&(n=i.finish_reason)}}let o=t.usage??{},s=o.prompt_tokens_details?.cached_tokens??0,r=(o.prompt_tokens??0)-s;return{id:t.id??crypto.randomUUID(),type:"message",role:"assistant",content:e,model:t.model??"",stop_reason:qe(n),stop_sequence:null,usage:{input_tokens:r,output_tokens:o.completion_tokens??0,...s?{cache_read_input_tokens:s}:{}}}}var fe=class{messageStartSent=!1;contentBlockIndex=0;contentBlockOpen=!1;toolCalls={}};function Ke(t,e){let n=[];if(!t.choices?.length)return n;let o=t.choices[0],s=o.delta??{};if(!e.messageStartSent){let r=t.usage??{},i=r.prompt_tokens_details?.cached_tokens??0;n.push({type:"message_start",message:{id:t.id??crypto.randomUUID(),type:"message",role:"assistant",content:[],model:t.model??"",stop_reason:null,stop_sequence:null,usage:{input_tokens:(r.prompt_tokens??0)-i,output_tokens:0,...i?{cache_read_input_tokens:i}:{}}}}),e.messageStartSent=!0}if(s.content&&(e.contentBlockOpen&&Object.values(e.toolCalls).some(r=>r.anthropicBlockIndex===e.contentBlockIndex)&&(n.push({type:"content_block_stop",index:e.contentBlockIndex}),e.contentBlockIndex++,e.contentBlockOpen=!1),e.contentBlockOpen||(n.push({type:"content_block_start",index:e.contentBlockIndex,content_block:{type:"text",text:""}}),e.contentBlockOpen=!0),n.push({type:"content_block_delta",index:e.contentBlockIndex,delta:{type:"text_delta",text:s.content}})),s.tool_calls)for(let r of s.tool_calls){let i=r.index??0;if(r.id&&r.function?.name){e.contentBlockOpen&&(n.push({type:"content_block_stop",index:e.contentBlockIndex}),e.contentBlockIndex++,e.contentBlockOpen=!1);let c=e.contentBlockIndex;e.toolCalls[i]={id:r.id,name:r.function.name,anthropicBlockIndex:c},n.push({type:"content_block_start",index:c,content_block:{type:"tool_use",id:r.id,name:r.function.name,input:{}}}),e.contentBlockOpen=!0}if(r.function?.arguments){let c=e.toolCalls[i];c&&n.push({type:"content_block_delta",index:c.anthropicBlockIndex,delta:{type:"input_json_delta",partial_json:r.function.arguments}})}}if(o.finish_reason){e.contentBlockOpen&&(n.push({type:"content_block_stop",index:e.contentBlockIndex}),e.contentBlockOpen=!1);let r=t.usage??{},i=r.prompt_tokens_details?.cached_tokens??0;n.push({type:"message_delta",delta:{stop_reason:qe(o.finish_reason),stop_sequence:null},usage:{input_tokens:(r.prompt_tokens??0)-i,output_tokens:r.completion_tokens??0,...i?{cache_read_input_tokens:i}:{}}}),n.push({type:"message_stop"})}return n}function _e(t){if(!t.length)return{};let e=t[0],n=[],o={},s=null,r={};for(let a of t){a.usage&&(r=a.usage);for(let u of a.choices??[]){let p=u.delta;if(p?.content&&n.push(p.content),p?.tool_calls)for(let l of p.tool_calls){let f=l.index??0;o[f]||(o[f]={id:"",type:"function",function:{name:"",arguments:""}}),l.id&&(o[f].id=l.id),l.function?.name&&(o[f].function.name=l.function.name),l.function?.arguments&&(o[f].function.arguments+=l.function.arguments)}u.finish_reason&&(s=u.finish_reason)}}let i={role:"assistant",content:n.length?n.join(""):null},c=Object.keys(o).map(Number).sort((a,u)=>a-u);return c.length&&(i.tool_calls=c.map(a=>o[a])),{id:e.id??"",object:"chat.completion",created:e.created??0,model:e.model??"",choices:[{index:0,message:i,finish_reason:s}],usage:r}}var Y=(0,Xe.Router)();Y.get(["/v1/models","/models"],async(t,e)=>{try{await N(),g.models||await W();let n=(g.models?.data??[]).map(o=>({id:o.id,object:"model",type:"model",created:0,created_at:new Date(0).toISOString(),owned_by:o.vendor??"unknown",display_name:o.name??o.id}));e.json({object:"list",data:n,has_more:!1})}catch(n){e.status(500).json({error:String(n)})}});Y.get(["/v1/models/full/","/models/full/"],(t,e)=>{e.json(g.models)});Y.post(["/v1/chat/completions","/chat/completions"],async(t,e)=>{try{let n=Date.now();await N();let o=t.body,s=crypto.randomUUID(),r=o.model??"unknown",i=X(r);i!==r&&(o={...o,model:i});let c=(o.messages??[]).some(d=>Array.isArray(d.content)&&d.content.some(w=>w.type==="image_url")),a=(o.messages??[]).some(d=>d.role==="assistant"||d.role==="tool"),u=V(c);u["X-Initiator"]=a?"agent":"user";let p=JSON.stringify(o).length;if(o.stream)return At(e,o,u,s,p,n,r,i);let l=await Ye(`${D()}/chat/completions`,{method:"POST",headers:u,body:JSON.stringify(o)},s,"/v1/chat/completions"),f=Math.round((Date.now()-n)/1e3*100)/100;if(!l)return e.status(504).json({error:`Upstream connection error after ${g.maxConnectionRetries+1} attempts`});let _=await l.text(),k=_.length;if(l.ok){let d=JSON.parse(_),w=d.usage??{};y.addRequest(s,{request_body:o,response_body:d,model:r,translated_model:i!==r?i:null,endpoint:"/v1/chat/completions",status_code:l.status,request_size:p,response_size:k,input_tokens:w.prompt_tokens??0,output_tokens:w.completion_tokens??0,duration:f}),e.json(d)}else K("/v1/chat/completions",o,_,l.status),e.status(l.status).type("json").send(_)}catch(n){e.status(500).json({error:String(n)})}});async function At(t,e,n,o,s,r,i,c){y.startRequest(o,{request_body:e,model:i,translated_model:c!==i?c:null,endpoint:"/v1/chat/completions",request_size:s}),t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","X-Accel-Buffering":"no"});let a=[],u=0,p=0,l=200,f=!1;t.on("close",()=>{f=!0});try{y.updateRequestState(o,"sending");let d=await fetch(`${D()}/chat/completions`,{method:"POST",headers:n,body:JSON.stringify(e),signal:AbortSignal.timeout(12e5)});l=d.status;let w=d.body.getReader(),b=new TextDecoder,h="";for(y.updateRequestState(o,"receiving");;){let{done:R,value:S}=await w.read();if(R)break;if(f){w.cancel();break}h+=b.decode(S,{stream:!0});let x=h.split(`
81
- `);h=x.pop();for(let $ of x)if($.trim()&&$.startsWith("data: ")){let C=$.slice(6);if(C==="[DONE]"){f||t.write(`data: [DONE]
82
-
83
- `);continue}try{let O=JSON.parse(C);a.push(O),O.usage&&(u=O.usage.prompt_tokens??0,p=O.usage.completion_tokens??0),f||t.write(`data: ${C}
84
-
85
- `)}catch{}}}}catch(d){l=504,console.log(`[Stream] Error for ${o}: ${d}`)}f?y.updateRequestState(o,"error",{status_code:499}):t.end();let _=Math.round((Date.now()-r)/1e3*100)/100,k=_e(a);y.completeRequest(o,{request_body:e,response_body:k,model:i,translated_model:c!==i?c:null,endpoint:"/v1/chat/completions",status_code:f?499:l,request_size:s,response_size:a.reduce((d,w)=>d+JSON.stringify(w).length,0),input_tokens:u,output_tokens:p,duration:_})}Y.post(["/v1/responses","/responses"],async(t,e)=>{try{let n=Date.now();await N();let o=t.body,s=crypto.randomUUID(),r=o.model??"unknown",i=X(r);if(i!==r&&(o={...o,model:i}),!Ge(i))return e.status(400).json({error:{message:`Model '${r}' does not support the /v1/responses endpoint.`,type:"invalid_request_error",code:"unsupported_model"}});Array.isArray(o.tools)&&(o={...o,tools:o.tools.filter(_=>_.type==="web_search"?(console.log(`Removed unsupported tool 'web_search' from request ${s}`),!1):!0)});let c=Ot(o.input),a=V(c),u=JSON.stringify(o).length;if(o.stream)return Ct(e,o,a,s,u,n,r,i);let p=await Ye(`${D()}/v1/responses`,{method:"POST",headers:a,body:JSON.stringify(o)},s,"/v1/responses"),l=Math.round((Date.now()-n)/1e3*100)/100;if(!p)return e.status(504).json({error:`Upstream connection error after ${g.maxConnectionRetries+1} attempts`});let f=await p.text();if(p.ok){let _=JSON.parse(f),k=_.usage??{};y.addRequest(s,{request_body:o,response_body:_,model:r,translated_model:i!==r?i:null,endpoint:"/v1/responses",status_code:p.status,request_size:u,response_size:f.length,input_tokens:k.input_tokens??0,output_tokens:k.output_tokens??0,duration:l}),e.json(_)}else K("/v1/responses",o,f,p.status),e.status(p.status).type("json").send(f)}catch(n){e.status(500).json({error:String(n)})}});async function Ct(t,e,n,o,s,r,i,c){y.startRequest(o,{request_body:e,model:i,translated_model:c!==i?c:null,endpoint:"/v1/responses",request_size:s}),t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","X-Accel-Buffering":"no"});let a=0,u=0,p=200,l={},f=[],_=!1;t.on("close",()=>{_=!0});try{y.updateRequestState(o,"sending");let w=await fetch(`${D()}/v1/responses`,{method:"POST",headers:n,body:JSON.stringify(e),signal:AbortSignal.timeout(12e5)});p=w.status;let b=w.body.getReader(),h=new TextDecoder,R="";for(y.updateRequestState(o,"receiving");;){let{done:S,value:x}=await b.read();if(S)break;if(_){b.cancel();break}R+=h.decode(x,{stream:!0});let $=R.split(`
86
- `);R=$.pop();for(let C of $)if(C.trim()){if(C.startsWith("event: ")){_||t.write(`${C}
87
- `);continue}if(C.startsWith("data: ")){let O=C.slice(6);if(O==="[DONE]"){_||t.write(`data: [DONE]
88
-
89
- `);continue}try{let I=JSON.parse(O);if(I.type==="response.completed"){let E=I.response??{};l=E;let H=E.usage??{};a=H.input_tokens??0,u=H.output_tokens??0}else I.type==="response.output_text.delta"&&f.push(I.delta??"");_||t.write(`data: ${O}
90
-
91
- `)}catch{_||t.write(`data: ${O}
92
-
93
- `)}}}}}catch(w){p=504,console.log(`[Stream Responses] Error for ${o}: ${w}`)}_?y.updateRequestState(o,"error",{status_code:499}):t.end();let k=Math.round((Date.now()-r)/1e3*100)/100,d=Object.keys(l).length?l:null;!d&&f.length&&(d={output:[{type:"message",content:[{type:"output_text",text:f.join("")}]}]}),y.completeRequest(o,{request_body:e,response_body:d??{error:"Stream interrupted"},model:i,translated_model:c!==i?c:null,endpoint:"/v1/responses",status_code:_?499:p,request_size:s,response_size:JSON.stringify(d??{}).length,input_tokens:a,output_tokens:u,duration:k})}function Ot(t){if(!Array.isArray(t))return!1;for(let e of t){if(typeof e!="object"||!e)continue;let n=e.content;if(Array.isArray(n)&&n.some(o=>o.type==="input_image")||e.type==="input_image")return!0}return!1}async function Ye(t,e,n,o){let s=g.maxConnectionRetries;for(let r=0;r<=s;r++)try{return await fetch(t,{...e,signal:AbortSignal.timeout(12e5)})}catch(i){let c=i;if(de(n,o,r,s,c),await N(),r<s){let a=Math.min(2**r,8)*1e3;console.log(`[${o}] Connection error (attempt ${r+1}/${s+1}): ${c.message}`),await new Promise(u=>setTimeout(u,a))}else console.log(`[${o}] Connection error (final attempt): ${c.message}`)}return null}var Qe=require("express");var he=(0,Qe.Router)(),Tt=new Set(["model","messages","max_tokens","system","metadata","stop_sequences","stream","temperature","top_p","top_k","tools","tool_choice","thinking","service_tier"]);function Et(t){let e=n=>{let o=n?.cache_control;o?.type==="ephemeral"&&"scope"in o&&delete o.scope};for(let n of t.tools??[])e(n);if(Array.isArray(t.system))for(let n of t.system)e(n);for(let n of t.messages??[])if(Array.isArray(n.content))for(let o of n.content)e(o)}function Pt(t){let e={};for(let[n,o]of Object.entries(t))Tt.has(n)&&(e[n]=o);return Et(e),e}function qt(t){let e=t.thinking;if(!e?.budget_tokens)return t;let n=e.budget_tokens,o=t.max_tokens??0;if(o<=n){let s=n+Math.min(16384,n);return console.log(`[DirectAnthropic] Adjusted max_tokens: ${o} \u2192 ${s} (thinking.budget_tokens=${n})`),{...t,max_tokens:s}}return t}function Dt(t){let e=t.system;if(!e)return g.systemPromptAdd.length?{...t,system:g.systemPromptAdd.map(n=>({type:"text",text:n}))}:t;if(typeof e=="string"){let n=me(e);for(let o of g.systemPromptAdd)n.includes(o)||(n+=`
94
-
95
- `+o);return{...t,system:n}}if(Array.isArray(e)){let n=e,o=n.filter(i=>i.type==="text").map(i=>i.text).join(`
96
- `),s=!1,r=[];for(let i of n)if(i.type==="text"){let c=i.text;if(c.startsWith("x-anthropic-billing-header:")){s=!0;continue}let a=c;for(let u of g.systemPromptRemove)a.includes(u)&&(a=a.replaceAll(u,""),s=!0);r.push(a!==c?{...i,text:a}:i)}else r.push(i);for(let i of g.systemPromptAdd)o.includes(i)||(r.push({type:"text",text:i}),s=!0);return s?{...t,system:r}:t}return t}function It(t){let e=t.messages;if(!e?.length)return t;let n=!1,o=e.map(s=>{if(!Array.isArray(s.content))return s;let r=!1,i=s.content.map(c=>{if(c.type!=="tool_result"||typeof c.content!="string")return c;let a=Pe(c.content);return a!==c.content?(r=!0,{...c,content:a}):c});return r?(n=!0,{...s,content:i}):s});return n?{...t,messages:o}:t}he.post("/v1/messages/count_tokens",async(t,e)=>{try{await N();let n=t.body,o=n.model??"",s=g.models?.data?.find(c=>c.id===o);if(!s)return e.json({input_tokens:1});let r=0,i=n.system;if(typeof i=="string")r+=j(i);else if(Array.isArray(i))for(let c of i)c.type==="text"&&(r+=j(c.text??""));for(let c of n.messages??[]){let a=c.content;if(typeof a=="string")r+=j(a);else if(Array.isArray(a))for(let u of a)u.type==="text"?r+=j(u.text??""):u.type==="tool_result"&&typeof u.content=="string"?r+=j(u.content):u.type==="tool_use"&&(r+=j(JSON.stringify(u.input??{})))}if(n.tools?.length){r+=o.startsWith("claude")?346:o.startsWith("grok")?480:346;for(let c of n.tools)r+=j(c.name??""),r+=j(c.description??""),r+=j(JSON.stringify(c.input_schema??{}))}s.vendor!=="Anthropic"&&(r=Math.ceil(r*(o.startsWith("grok")?1.03:1.05))),e.json({input_tokens:r})}catch(n){console.log(`[count_tokens] Error: ${n}`),e.json({input_tokens:1})}});he.post("/v1/messages",async(t,e)=>{let n=Date.now();await N();let o=t.body,s=crypto.randomUUID(),r=o,i=o.model??"unknown",c=X(i);return c!==i&&(console.log(`[Anthropic API] Model name translated: ${i} -> ${c}`),o={...o,model:c}),o=Dt(o),o=It(o),Je(c)?(console.log(`[Anthropic API] Using direct Anthropic API path for: ${c}`),Nt(e,o,s,n,i,c,r)):(console.log(`[Anthropic API] Using OpenAI translation path for: ${c}`),Lt(e,o,s,n,i,c,r))});async function Nt(t,e,n,o,s,r,i){let c=JSON.stringify(e).length,a=Ze(e),u=(e.messages??[]).some(_=>_.role==="assistant"),p=V(a);p["anthropic-version"]="2023-06-01",p["X-Initiator"]=u?"agent":"user";let l=e,f=null;for(let _=0;_<=3;_++){let k=Pt(l);if(k=qt(k),k.stream)return jt(t,k,p,n,l,c,o,s,r,i);let d=await et(`${D()}/v1/messages`,{method:"POST",headers:p,body:JSON.stringify(k)},n,"/v1/messages");if(!d)return void t.status(504).json({type:"error",error:{type:"api_error",message:"Upstream connection error"}});let w=Math.round((Date.now()-o)/1e3*100)/100;if(d.ok){let h=await d.json(),R=h.usage;return y.addRequest(n,{original_request_body:i,request_body:l,response_body:h,model:s,translated_model:r!==s?r:null,endpoint:"/v1/messages",status_code:d.status,request_size:c,response_size:JSON.stringify(h).length,input_tokens:R?.input_tokens??0,output_tokens:R?.output_tokens??0,duration:w}),f&&ge({...f,modified_request:l,final_status_code:d.status,final_response:h}),void t.json(h)}let b=await d.text();if(K("/v1/messages",l,b,d.status),Ce(d.status,b)){let h=$e(b);if(h.length){console.log(`[Direct Anthropic] Attempt ${_+1}: orphaned IDs: ${h}`),f?f.orphaned_ids.push(...h):f={request_id:n,original_request:e,error_response:b,error_status_code:d.status,orphaned_ids:h},l={...l,messages:Ae(l.messages,h)};continue}}return f&&ge({...f,modified_request:l,final_status_code:d.status,final_response:b}),void t.status(d.status).type("json").send(b)}}async function jt(t,e,n,o,s,r,i,c,a,u){y.startRequest(o,{original_request_body:u,request_body:s,model:c,translated_model:a!==c?a:null,endpoint:"/v1/messages",request_size:r}),t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","X-Accel-Buffering":"no"});let p=0,l=0,f=200,_=c,k=[],d=!1;t.on("close",()=>{d=!0});try{y.updateRequestState(o,"sending");let h=await fetch(`${D()}/v1/messages`,{method:"POST",headers:n,body:JSON.stringify(e),signal:AbortSignal.timeout(12e5)});f=h.status,y.updateRequestState(o,"receiving");let R=h.body.getReader(),S=new TextDecoder,x="",$="";for(;;){let{done:C,value:O}=await R.read();if(C)break;if(d){R.cancel();break}x+=S.decode(O,{stream:!0});let I=x.split(`
97
- `);x=I.pop();for(let E of I){if(!E.trim())continue;if(E.startsWith("event: ")){$=E.slice(7);continue}if(!E.startsWith("data: "))continue;let H=E.slice(6);if(H==="[DONE]")break;try{let q=JSON.parse(H),G=$||q.type||"";if($="",G==="message_start"){let ee=q.message??{};_=ee.model??c,p=ee.usage?.input_tokens??0}else G==="message_delta"?l=q.usage?.output_tokens??0:G==="content_block_delta"&&q.delta?.type==="text_delta"&&k.push(q.delta.text??"");d||t.write(`event: ${G}
98
- data: ${H}
99
-
100
- `)}catch{}}}}catch(h){f=504,console.log(`[Stream Direct Anthropic] Error for ${o}: ${h}`)}d?y.updateRequestState(o,"error",{status_code:499}):t.end();let w=Math.round((Date.now()-i)/1e3*100)/100,b=k.length?{id:o,type:"message",role:"assistant",content:[{type:"text",text:k.join("")}],model:_,usage:{input_tokens:p,output_tokens:l}}:{error:{type:"api_error",message:"Stream interrupted"}};y.completeRequest(o,{request_body:s,response_body:b,model:c,translated_model:a!==c?a:null,endpoint:"/v1/messages",status_code:d?499:f,request_size:r,response_size:JSON.stringify(b).length,input_tokens:p,output_tokens:l,duration:w})}async function Lt(t,e,n,o,s,r,i){let c=Ze(e),a=JSON.stringify(e).length,u=e,p=null;for(let l=0;l<=3;l++){let f=We(u),_=(f.messages??[]).some(h=>h.role==="assistant"||h.role==="tool"),k=V(c);if(k["X-Initiator"]=_?"agent":"user",e.stream)return Ht(t,f,k,n,u,a,o,s,r,i);let d=await et(`${D()}/chat/completions`,{method:"POST",headers:k,body:JSON.stringify(f)},n,"/v1/messages (translated)");if(!d)return void t.status(504).json({type:"error",error:{type:"api_error",message:"Upstream connection error"}});let w=Math.round((Date.now()-o)/1e3*100)/100;if(d.ok){let h=await d.json(),R=De(h),S=h.usage;return y.addRequest(n,{original_request_body:i,request_body:u,response_body:R,model:s,translated_model:r!==s?r:null,endpoint:"/v1/messages",status_code:d.status,request_size:a,response_size:JSON.stringify(R).length,input_tokens:S?.prompt_tokens??0,output_tokens:S?.completion_tokens??0,duration:w}),void t.json(R)}let b=await d.text();if(K("/v1/messages",u,b,d.status),Ce(d.status,b)){let h=$e(b);if(h.length){p?p.orphaned_ids.push(...h):p={request_id:n,original_request:e,error_response:b,error_status_code:d.status,orphaned_ids:h},u={...u,messages:Ae(u.messages,h)};continue}}return p&&ge({...p,modified_request:u,final_status_code:d.status,final_response:b}),void t.status(d.status).type("json").send(b)}}async function Ht(t,e,n,o,s,r,i,c,a,u){y.startRequest(o,{original_request_body:u,request_body:s,model:c,translated_model:a!==c?a:null,endpoint:"/v1/messages",request_size:r}),t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","X-Accel-Buffering":"no"});let p=new fe,l=[],f=0,_=0,k=200,d=!1;t.on("close",()=>{d=!0});try{y.updateRequestState(o,"sending");let R=await fetch(`${D()}/chat/completions`,{method:"POST",headers:n,body:JSON.stringify(e),signal:AbortSignal.timeout(12e5)});k=R.status,y.updateRequestState(o,"receiving");let S=R.body.getReader(),x=new TextDecoder,$="";for(;;){let{done:C,value:O}=await S.read();if(C)break;if(d){S.cancel();break}$+=x.decode(O,{stream:!0});let I=$.split(`
101
- `);$=I.pop();for(let E of I){if(!E.trim()||!E.startsWith("data: "))continue;let H=E.slice(6);if(H==="[DONE]")break;try{let q=JSON.parse(H);l.push(q),q.usage&&(f=q.usage.prompt_tokens??0,_=q.usage.completion_tokens??0);let G=Ke(q,p);for(let ee of G)d||t.write(`event: ${ee.type}
102
- data: ${JSON.stringify(ee)}
103
-
104
- `)}catch{}}}}catch(R){k=504,console.log(`[Stream Anthropic] Error for ${o}: ${R}`)}d?y.updateRequestState(o,"error",{status_code:499}):t.end();let w=Math.round((Date.now()-i)/1e3*100)/100,b=_e(l),h=Object.keys(b).length?De(b):{error:{type:"api_error",message:"Stream interrupted"}};y.completeRequest(o,{request_body:s,response_body:h,model:c,translated_model:a!==c?a:null,endpoint:"/v1/messages",status_code:d?499:k,request_size:r,response_size:l.reduce((R,S)=>R+JSON.stringify(S).length,0),input_tokens:f,output_tokens:_,duration:w})}function Ze(t){return(t.messages??[]).some(e=>Array.isArray(e.content)&&e.content.some(n=>n.type==="image"))}async function et(t,e,n,o){let s=g.maxConnectionRetries;for(let r=0;r<=s;r++)try{return await fetch(t,{...e,signal:AbortSignal.timeout(12e5)})}catch(i){let c=i;de(n,o,r,s,c),await N(),r<s&&(console.log(`[${o}] Connection error (attempt ${r+1}/${s+1}): ${c.message}`),await new Promise(a=>setTimeout(a,Math.min(2**r,8)*1e3)))}return null}var tt=require("express"),ce=A(require("path")),nt=A(require("fs"));var P=(0,tt.Router)();function ot(){let t=ce.default.join(__dirname,"..","public");return nt.default.existsSync(t)?t:ce.default.join(__dirname,"..","..","public")}P.get("/",(t,e)=>{e.sendFile(ce.default.join(ot(),"dashboard.html"))});P.get("/requests",(t,e)=>{e.sendFile(ce.default.join(ot(),"requests.html"))});P.get("/api/stats",(t,e)=>{e.json(y.getStats())});P.get("/api/requests",(t,e)=>{let n=parseInt(t.query.page)||1,o=parseInt(t.query.per_page)||50,s=t.query.search||"",r=(n-1)*o,i,c;s?(i=y.searchRequests(s,o,r),c=y.searchRequests(s,1e4,0).length):(i=y.getRecentRequests(o,r),c=y.getTotalCount());let a=i.map(u=>({...u,request_body:null,response_body:null}));e.json({items:a,total:c,page:n,per_page:o,total_pages:Math.ceil(c/o)})});P.get("/api/request/:id",(t,e)=>{let n=y.getRequest(t.params.id);if(!n)return e.status(404).json({error:"Request not found"});e.json(n)});P.get("/api/request/:id/request-body",(t,e)=>{let n=y.getRequest(t.params.id);if(!n)return e.status(404).json({error:"Request not found"});e.json(n.request_body)});P.get("/api/request/:id/response-body",(t,e)=>{let n=y.getRequest(t.params.id);if(!n)return e.status(404).json({error:"Request not found"});e.json(n.response_body)});P.get("/api/requests/search",(t,e)=>{let n=parseInt(t.query.page)||1,o=parseInt(t.query.per_page)||50,s=t.query.q||"",r=(n-1)*o;if(!s)return e.json({items:[],total:0,page:n,per_page:o,total_pages:0});let[i,c]=y.fulltextSearch(s,o,r),a=i.map(u=>({...u,request_body:null,response_body:null}));e.json({items:a,total:c,page:n,per_page:o,total_pages:c>0?Math.ceil(c/o):0})});P.get("/api/requests/export",(t,e)=>{let n=new Date().toISOString().replace(/[:.]/g,"").slice(0,15);e.setHeader("Content-Type","application/x-jsonlines"),e.setHeader("Content-Disposition",`attachment; filename=requests_${n}.jl`);for(let o of y.getAllRequests())e.write(JSON.stringify(o)+`
105
- `);e.end()});P.post("/api/requests/import",(t,e)=>{let o=(typeof t.body=="string"?t.body:JSON.stringify(t.body)).split(`
106
- `).filter(i=>i.trim()),s=0,r=[];for(let i=0;i<o.length;i++)try{let c=JSON.parse(o[i]);y.importRequest(c),s++}catch(c){r.push(`Line ${i+1}: ${c}`)}e.json({imported:s,errors:r.slice(0,10),total_errors:r.length})});var U=A(require("fs")),Z=A(require("path")),rt=A(require("readline"));var L="\x1B[1m",v="\x1B[2m",m="\x1B[0m",Q="\x1B[36m",B="\x1B[32m",Ne="\x1B[33m",z="\x1B[35m";function Mt(){console.log(`
107
- ${z}\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557${m}
108
- ${z}\u2551${m} ${L}ghc-tunnel Setup Wizard${m} ${z}\u2551${m}
109
- ${z}\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D${m}
110
- `)}function it(){return rt.default.createInterface({input:process.stdin,output:process.stdout})}function F(t,e){return new Promise(n=>t.question(e,n))}async function ct(){Mt(),console.log(`${L}Step 1: GitHub Authentication${m}`),console.log(`${v}Checking for GitHub token...${m}
111
- `);let t=await le();t||(console.error("Failed to get GitHub token. Cannot continue setup."),process.exit(1)),g.githubToken=t,await ie(),await W();let e=g.models?.data??[];e.length||(console.error("No models available. Check your Copilot subscription."),process.exit(1)),console.log(`
112
- ${L}Step 2: Configure Models${m}
113
- `),console.log(`${v}Available Copilot models:${m}
114
- `);for(let R=0;R<e.length;R++){let S=e[R],x=[];S.capabilities?.supports?.vision&&x.push("Vision"),S.capabilities?.supports?.tool_calls&&x.push("Tools"),S.supported_endpoints?.includes("/v1/messages")&&x.push("Anthropic"),S.preview&&x.push("Preview");let $=x.length?` ${v}(${x.join(", ")})${m}`:"";console.log(` ${Q}${String(R+1).padStart(2)}.${m} ${S.id}${$}`)}let n=it();console.log(`
115
- ${Ne}Pick models by number. Press Enter to accept the default.${m}
116
- `);let o=Ie(e,"claude-opus-4.6-1m")??Ie(e,"claude-opus-4.6")??0,s=await st(n,e,'Primary model (mapped to "opus", "sonnet" aliases)',o),r=e[s],i=Ie(e,"claude-haiku-4.5")??0,c=await st(n,e,'Small/fast model (mapped to "haiku" alias)',i),a=e[c];console.log(`
117
- ${B}Primary: ${r.id}${m}`),console.log(`${B}Small: ${a.id}${m}
118
- `),console.log(`${L}Step 3: Server Settings${m}
119
- `);let u=await F(n,` Port ${v}[8314]${m}: `),p=u.trim()?parseInt(u.trim(),10):8314,f=(await F(n,` Host ${v}[localhost]${m}: `)).trim()||"localhost";console.log(`
120
- ${L}Step 4: Claude Code Model Settings${m}`),console.log(`${v}These are the model identifiers written to ~/.claude/settings.json.${m}`),console.log(`${v}The proxy's model_mappings translate them to actual Copilot models.${m}
121
- `);let _="claude-opus-4-6[1m]",k="claude-sonnet-4-6",w=(await F(n,` ANTHROPIC_MODEL ${v}[${_}]${m}: `)).trim()||_,h=(await F(n,` ANTHROPIC_DEFAULT_HAIKU_MODEL ${v}[${k}]${m}: `)).trim()||k;return console.log(`
122
- ${B}ANTHROPIC_MODEL: ${w}${m}`),console.log(`${B}ANTHROPIC_DEFAULT_HAIKU_MODEL: ${h}${m}
123
- `),n.close(),console.log(`${L}Step 5: Saving Configuration${m}
124
- `),Ut(r.id,a.id,p,f),at(p,f,w,h),console.log(`
125
- ${B}${L}Setup complete!${m}
126
- `),{port:p,host:f}}async function st(t,e,n,o){let s=e[o]?.id??"?";for(;;){let r=await F(t,` ${n} ${v}[${o+1}: ${s}]${m}: `);if(!r.trim())return o;let i=parseInt(r.trim(),10);if(i>=1&&i<=e.length)return i-1;let c=e.findIndex(a=>a.id===r.trim());if(c!==-1)return c;console.log(` ${Ne}Invalid choice. Enter a number 1-${e.length} or a model name.${m}`)}}function Ie(t,e){let n=t.findIndex(o=>o.id===e);return n>=0?n:void 0}function Ut(t,e,n,o){let s=T();U.default.mkdirSync(s,{recursive:!0});let r=Z.default.join(s,"config.yaml"),i=`# ghc-tunnel Configuration (generated by --setup)
82
+ `)}var rt=require("express");var je=class{cache=new Map;maxEntries;requestCount=0;bytesSent=0;bytesReceived=0;modelStats={};endpointStats={};constructor(e=1e3){this.maxEntries=e}startRequest(e,o){this.evictIfFull(),this.cache.set(e,{id:e,timestamp:new Date().toISOString(),original_request_body:o.original_request_body??null,request_body:o.request_body??null,response_body:null,model:o.model??"unknown",translated_model:o.translated_model??null,endpoint:o.endpoint??"unknown",status_code:null,request_size:o.request_size??0,response_size:0,input_tokens:0,output_tokens:0,duration:0,state:"pending"})}updateRequestState(e,o,n){let s=this.cache.get(e);s&&(s.state=o,n&&Object.assign(s,n))}completeRequest(e,o){let n=this.cache.get(e);n?Object.assign(n,{response_body:o.response_body,status_code:o.status_code??200,response_size:o.response_size??0,input_tokens:o.input_tokens??0,output_tokens:o.output_tokens??0,duration:o.duration??0,state:(o.status_code??200)<400?"completed":"error"}):(this.evictIfFull(),n={id:e,timestamp:new Date().toISOString(),original_request_body:o.original_request_body??null,request_body:o.request_body??null,response_body:o.response_body??null,model:o.model??"unknown",translated_model:o.translated_model??null,endpoint:o.endpoint??"unknown",status_code:o.status_code??200,request_size:o.request_size??0,response_size:o.response_size??0,input_tokens:o.input_tokens??0,output_tokens:o.output_tokens??0,duration:o.duration??0,state:(o.status_code??200)<400?"completed":"error"},this.cache.set(e,n)),this.requestCount++,this.bytesSent+=o.request_size??0,this.bytesReceived+=o.response_size??0,this.updateModelStats(o),this.updateEndpointStats(o)}addRequest(e,o){this.cache.has(e)?this.completeRequest(e,o):(this.startRequest(e,o),this.completeRequest(e,o))}getRequest(e){return this.cache.get(e)}getRecentRequests(e=50,o=0){return[...this.cache.values()].reverse().slice(o,o+e)}getTotalCount(){return this.cache.size}getStats(){return{total_requests:this.requestCount,cached_requests:this.cache.size,bytes_sent:this.bytesSent,bytes_received:this.bytesReceived,model_stats:{...this.modelStats},endpoint_stats:{...this.endpointStats}}}searchRequests(e,o=50,n=0){let s=e.toLowerCase(),r=[];for(let i of[...this.cache.values()].reverse())(i.model.toLowerCase().includes(s)||i.endpoint.toLowerCase().includes(s)||JSON.stringify(i.request_body).toLowerCase().includes(s))&&r.push(i);return r.slice(n,n+o)}fulltextSearch(e,o=50,n=0){let s=e.toLowerCase(),r=[];for(let i of[...this.cache.values()].reverse()){let c=JSON.stringify(i.request_body).toLowerCase(),a=JSON.stringify(i.response_body).toLowerCase();(c.includes(s)||a.includes(s))&&r.push(i)}return[r.slice(n,n+o),r.length]}getAllRequests(){return[...this.cache.values()]}importRequest(e){let o=e.id||crypto.randomUUID();this.evictIfFull();let n={id:o,timestamp:e.timestamp||new Date().toISOString(),original_request_body:e.original_request_body??null,request_body:e.request_body??null,response_body:e.response_body??null,model:e.model||"unknown",translated_model:e.translated_model||null,endpoint:e.endpoint||"unknown",status_code:e.status_code??200,request_size:e.request_size??0,response_size:e.response_size??0,input_tokens:e.input_tokens??0,output_tokens:e.output_tokens??0,duration:e.duration??0,state:e.state||"completed"};this.cache.set(o,n),this.requestCount++,this.bytesSent+=n.request_size,this.bytesReceived+=n.response_size,this.updateModelStats(n),this.updateEndpointStats(n)}evictIfFull(){if(this.cache.size>=this.maxEntries){let e=this.cache.keys().next().value;this.cache.delete(e)}}updateModelStats(e){let o=e.model??"unknown";this.modelStats[o]||(this.modelStats[o]={request_count:0,input_tokens:0,output_tokens:0,bytes_sent:0,bytes_received:0});let n=this.modelStats[o];n.request_count++,n.input_tokens+=e.input_tokens??0,n.output_tokens+=e.output_tokens??0,n.bytes_sent+=e.request_size??0,n.bytes_received+=e.response_size??0}updateEndpointStats(e){let o=e.endpoint??"unknown";this.endpointStats[o]||(this.endpointStats[o]={request_count:0,bytes_sent:0,bytes_received:0});let n=this.endpointStats[o];n.request_count++,n.bytes_sent+=e.request_size??0,n.bytes_received+=e.response_size??0}},$=new je;function ne(t){return J.translate(t)}function $e(t){for(let e of f.systemPromptRemove)t.includes(e)&&(t=t.replaceAll(e,""),console.log(`[Content Filter] Removed from system prompt: ${e.slice(0,50)}${e.length>50?"...":""}`));return t}function Fe(t){for(let e of f.toolResultSuffixRemove)t.endsWith(e)&&(t=t.slice(0,-e.length),console.log(`[Content Filter] Removed suffix from tool result: ${e.slice(0,50)}${e.length>50?"...":""}`));return t}function ot(t){let e=[],o=t.system;if(o){if(typeof o=="string")e.push({role:"system",content:$e(o)});else if(Array.isArray(o)){let r=o.filter(i=>i.type==="text"&&!(i.text||"").includes("x-anthropic-billing-header")).map(i=>i.text);r.length&&e.push({role:"system",content:$e(r.join(`
83
+
84
+ `))})}}for(let r of t.messages||[]){let i=r.role,c=r.content;if(i==="user")if(Array.isArray(c)){let a=c.filter(p=>p.type==="tool_result"),l=c.filter(p=>p.type!=="tool_result");for(let p of a){let u=p.content??"";typeof u=="string"&&(u=Fe(u)),e.push({role:"tool",tool_call_id:p.tool_use_id,content:u})}if(l.length){let p=jt(l);p!=null&&e.push({role:"user",content:p})}}else e.push({role:"user",content:c});else if(i==="assistant")if(Array.isArray(c)){let a=c.filter(u=>u.type==="tool_use"),p=c.filter(u=>u.type==="text"||u.type==="thinking").map(u=>(u.type==="text"?u.text:u.thinking)??"").join(`
85
+
86
+ `);a.length?e.push({role:"assistant",content:p||null,tool_calls:a.map(u=>({id:u.id,type:"function",function:{name:u.name,arguments:JSON.stringify(u.input??{})}}))}):e.push({role:"assistant",content:p})}else e.push({role:"assistant",content:c})}let n={model:ne(t.model??""),messages:e,max_tokens:t.max_tokens,stream:t.stream??!1};t.temperature!=null&&(n.temperature=t.temperature),t.top_p!=null&&(n.top_p=t.top_p),t.stop_sequences&&(n.stop=t.stop_sequences),Array.isArray(t.tools)&&(n.tools=t.tools.map(r=>({type:"function",function:{name:r.name,description:r.description,parameters:r.input_schema??{}}})));let s=t.tool_choice;if(s){let r=s.type;r==="auto"?n.tool_choice="auto":r==="any"?n.tool_choice="required":r==="none"?n.tool_choice="none":r==="tool"&&s.name&&(n.tool_choice={type:"function",function:{name:s.name}})}return n}function jt(t){if(!t.some(n=>n.type==="image")){let n=[];for(let s of t)s.type==="text"?n.push(s.text??""):s.type==="thinking"&&n.push(s.thinking??"");return n.length?n.join(`
87
+
88
+ `):null}let o=[];for(let n of t)if(n.type==="text")o.push({type:"text",text:n.text});else if(n.type==="thinking")o.push({type:"text",text:n.thinking});else if(n.type==="image"){let s=n.source;o.push({type:"image_url",image_url:{url:`data:${s?.media_type};base64,${s?.data}`}})}return o.length?o:null}var Ft={stop:"end_turn",length:"max_tokens",tool_calls:"tool_use",content_filter:"refusal"};function Me(t){return t?Ft[t]??null:null}function He(t){let e=[],o=null;for(let i of t.choices||[]){let c=i.message;if(c){if(c.content&&e.push({type:"text",text:c.content}),Array.isArray(c.tool_calls))for(let a of c.tool_calls){let l=a.function,p;try{p=JSON.parse(l.arguments||"{}")}catch{p={_raw_arguments:l.arguments}}e.push({type:"tool_use",id:a.id,name:l.name,input:p})}i.finish_reason&&(o=i.finish_reason)}}let n=t.usage??{},s=n.prompt_tokens_details?.cached_tokens??0,r=(n.prompt_tokens??0)-s;return{id:t.id??crypto.randomUUID(),type:"message",role:"assistant",content:e,model:t.model??"",stop_reason:Me(o),stop_sequence:null,usage:{input_tokens:r,output_tokens:n.completion_tokens??0,...s?{cache_read_input_tokens:s}:{}}}}var be=class{messageStartSent=!1;contentBlockIndex=0;contentBlockOpen=!1;toolCalls={}};function st(t,e){let o=[];if(!t.choices?.length)return o;let n=t.choices[0],s=n.delta??{};if(!e.messageStartSent){let r=t.usage??{},i=r.prompt_tokens_details?.cached_tokens??0;o.push({type:"message_start",message:{id:t.id??crypto.randomUUID(),type:"message",role:"assistant",content:[],model:t.model??"",stop_reason:null,stop_sequence:null,usage:{input_tokens:(r.prompt_tokens??0)-i,output_tokens:0,...i?{cache_read_input_tokens:i}:{}}}}),e.messageStartSent=!0}if(s.content&&(e.contentBlockOpen&&Object.values(e.toolCalls).some(r=>r.anthropicBlockIndex===e.contentBlockIndex)&&(o.push({type:"content_block_stop",index:e.contentBlockIndex}),e.contentBlockIndex++,e.contentBlockOpen=!1),e.contentBlockOpen||(o.push({type:"content_block_start",index:e.contentBlockIndex,content_block:{type:"text",text:""}}),e.contentBlockOpen=!0),o.push({type:"content_block_delta",index:e.contentBlockIndex,delta:{type:"text_delta",text:s.content}})),s.tool_calls)for(let r of s.tool_calls){let i=r.index??0;if(r.id&&r.function?.name){e.contentBlockOpen&&(o.push({type:"content_block_stop",index:e.contentBlockIndex}),e.contentBlockIndex++,e.contentBlockOpen=!1);let c=e.contentBlockIndex;e.toolCalls[i]={id:r.id,name:r.function.name,anthropicBlockIndex:c},o.push({type:"content_block_start",index:c,content_block:{type:"tool_use",id:r.id,name:r.function.name,input:{}}}),e.contentBlockOpen=!0}if(r.function?.arguments){let c=e.toolCalls[i];c&&o.push({type:"content_block_delta",index:c.anthropicBlockIndex,delta:{type:"input_json_delta",partial_json:r.function.arguments}})}}if(n.finish_reason){e.contentBlockOpen&&(o.push({type:"content_block_stop",index:e.contentBlockIndex}),e.contentBlockOpen=!1);let r=t.usage??{},i=r.prompt_tokens_details?.cached_tokens??0;o.push({type:"message_delta",delta:{stop_reason:Me(n.finish_reason),stop_sequence:null},usage:{input_tokens:(r.prompt_tokens??0)-i,output_tokens:r.completion_tokens??0,...i?{cache_read_input_tokens:i}:{}}}),o.push({type:"message_stop"})}return o}function xe(t){if(!t.length)return{};let e=t[0],o=[],n={},s=null,r={};for(let a of t){a.usage&&(r=a.usage);for(let l of a.choices??[]){let p=l.delta;if(p?.content&&o.push(p.content),p?.tool_calls)for(let u of p.tool_calls){let m=u.index??0;n[m]||(n[m]={id:"",type:"function",function:{name:"",arguments:""}}),u.id&&(n[m].id=u.id),u.function?.name&&(n[m].function.name=u.function.name),u.function?.arguments&&(n[m].function.arguments+=u.function.arguments)}l.finish_reason&&(s=l.finish_reason)}}let i={role:"assistant",content:o.length?o.join(""):null},c=Object.keys(n).map(Number).sort((a,l)=>a-l);return c.length&&(i.tool_calls=c.map(a=>n[a])),{id:e.id??"",object:"chat.completion",created:e.created??0,model:e.model??"",choices:[{index:0,message:i,finish_reason:s}],usage:r}}var oe=(0,rt.Router)();oe.get(["/v1/models","/models"],async(t,e)=>{try{await U(),f.models||await te();let o=(f.models?.data??[]).map(n=>({id:n.id,object:"model",type:"model",created:0,created_at:new Date(0).toISOString(),owned_by:n.vendor??"unknown",display_name:n.name??n.id}));e.json({object:"list",data:o,has_more:!1})}catch(o){e.status(500).json({error:String(o)})}});oe.get(["/v1/models/full/","/models/full/"],(t,e)=>{e.json(f.models)});oe.post(["/v1/chat/completions","/chat/completions"],async(t,e)=>{try{let o=Date.now();await U();let n=t.body,s=crypto.randomUUID(),r=n.model??"unknown",i=ne(r);i!==r&&(n={...n,model:i}),console.log(`[Chat Completions API] Using OpenAI Chat Completions path for: ${i}`);let c=(n.messages??[]).some(d=>Array.isArray(d.content)&&d.content.some(y=>y.type==="image_url")),a=(n.messages??[]).some(d=>d.role==="assistant"||d.role==="tool"),l=Y(c);l["X-Initiator"]=a?"agent":"user";let p=JSON.stringify(n).length;if(n.stream)return Mt(e,n,l,s,p,o,r,i);let u=await it(`${N()}/chat/completions`,{method:"POST",headers:l,body:JSON.stringify(n)},s,"/v1/chat/completions"),m=Math.round((Date.now()-o)/1e3*100)/100;if(!u)return e.status(504).json({error:`Upstream connection error after ${f.maxConnectionRetries+1} attempts`});let _=await u.text(),R=_.length;if(u.ok){let d=JSON.parse(_),y=d.usage??{};$.addRequest(s,{request_body:n,response_body:d,model:r,translated_model:i!==r?i:null,endpoint:"/v1/chat/completions",status_code:u.status,request_size:p,response_size:R,input_tokens:y.prompt_tokens??0,output_tokens:y.completion_tokens??0,duration:m}),e.json(d)}else Z("/v1/chat/completions",n,_,u.status),e.status(u.status).type("json").send(_)}catch(o){e.status(500).json({error:String(o)})}});async function Mt(t,e,o,n,s,r,i,c){$.startRequest(n,{request_body:e,model:i,translated_model:c!==i?c:null,endpoint:"/v1/chat/completions",request_size:s}),t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","X-Accel-Buffering":"no"});let a=[],l=0,p=0,u=200,m=!1;t.on("close",()=>{m=!0});try{$.updateRequestState(n,"sending");let d=await fetch(`${N()}/chat/completions`,{method:"POST",headers:o,body:JSON.stringify(e),signal:AbortSignal.timeout(12e5)});u=d.status;let y=d.body.getReader(),b=new TextDecoder,k="";for($.updateRequestState(n,"receiving");;){let{done:x,value:T}=await y.read();if(x)break;if(m){y.cancel();break}k+=b.decode(T,{stream:!0});let q=k.split(`
89
+ `);k=q.pop();for(let C of q)if(C.trim()&&C.startsWith("data: ")){let H=C.slice(6);if(H==="[DONE]"){m||t.write(`data: [DONE]
90
+
91
+ `);continue}try{let I=JSON.parse(H);a.push(I),I.usage&&(l=I.usage.prompt_tokens??0,p=I.usage.completion_tokens??0),m||t.write(`data: ${H}
92
+
93
+ `)}catch{}}}}catch(d){u=504,console.log(`[Stream] Error for ${n}: ${d}`)}m?$.updateRequestState(n,"error",{status_code:499}):t.end();let _=Math.round((Date.now()-r)/1e3*100)/100,R=xe(a);$.completeRequest(n,{request_body:e,response_body:R,model:i,translated_model:c!==i?c:null,endpoint:"/v1/chat/completions",status_code:m?499:u,request_size:s,response_size:a.reduce((d,y)=>d+JSON.stringify(y).length,0),input_tokens:l,output_tokens:p,duration:_})}oe.post(["/v1/responses","/responses"],async(t,e)=>{try{let o=Date.now();await U();let n=t.body,s=crypto.randomUUID(),r=n.model??"unknown",i=ne(r);if(i!==r&&(n={...n,model:i}),!nt(i))return e.status(400).json({error:{message:`Model '${r}' does not support the /v1/responses endpoint.`,type:"invalid_request_error",code:"unsupported_model"}});if(console.log(`[Responses API] Using OpenAI Responses API path for: ${i}`),Array.isArray(n.tools)&&(n={...n,tools:n.tools.map(_=>_.type==="custom"&&_.name==="apply_patch"?{type:"function",name:"apply_patch",description:"Use the `apply_patch` tool to edit files",parameters:{type:"object",properties:{input:{type:"string",description:"The entire contents of the apply_patch command"}},required:["input"]},strict:!1}:_)}),Array.isArray(n.tools)){let _=new Set(["image_generation"]);n={...n,tools:n.tools.filter(R=>!(typeof R.type=="string"&&_.has(R.type)))}}if(Array.isArray(n.input)){let _=zt(n.input);_!==n.input&&(n={...n,input:_})}n={...n,service_tier:null};let c=Jt(n.input),a=Y(c);a["X-Initiator"]=Bt(n.input)?"agent":"user";let l=JSON.stringify(n).length;if(n.stream)return Ht(e,n,a,s,l,o,r,i);let p=await it(`${N()}/responses`,{method:"POST",headers:a,body:JSON.stringify(n)},s,"/v1/responses"),u=Math.round((Date.now()-o)/1e3*100)/100;if(!p)return e.status(504).json({error:`Upstream connection error after ${f.maxConnectionRetries+1} attempts`});let m=await p.text();if(p.ok){let _=JSON.parse(m),R=_.usage??{};$.addRequest(s,{request_body:n,response_body:_,model:r,translated_model:i!==r?i:null,endpoint:"/v1/responses",status_code:p.status,request_size:l,response_size:m.length,input_tokens:R.input_tokens??0,output_tokens:R.output_tokens??0,duration:u}),e.json(_)}else Z("/v1/responses",n,m,p.status),e.status(p.status).type("json").send(m)}catch(o){e.status(500).json({error:String(o)})}});async function Ht(t,e,o,n,s,r,i,c){$.startRequest(n,{request_body:e,model:i,translated_model:c!==i?c:null,endpoint:"/v1/responses",request_size:s}),t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","X-Accel-Buffering":"no"});let a=0,l=0,p=200,u={},m=[],_=!1;t.on("close",()=>{_=!0});try{$.updateRequestState(n,"sending");let y=await fetch(`${N()}/responses`,{method:"POST",headers:o,body:JSON.stringify(e),signal:AbortSignal.timeout(12e5)});if(p=y.status,y.ok){let b=y.body.getReader(),k=new TextDecoder,x="";for($.updateRequestState(n,"receiving");;){let{done:T,value:q}=await b.read();if(T)break;if(_){b.cancel();break}_||t.write(q),x+=k.decode(q,{stream:!0});let C=x.split(`
94
+ `);x=C.pop();for(let H of C){let I=H.replace(/\r$/,"");if(!I.startsWith("data: "))continue;let V=I.slice(6);if(V!=="[DONE]")try{let A=JSON.parse(V);if(A.type==="response.completed"){let B=A.response??{};u=B;let O=B.usage??{};a=O.input_tokens??0,l=O.output_tokens??0}else A.type==="response.output_text.delta"&&m.push(A.delta??"")}catch{}}}}else{let b=await y.text();Z("/v1/responses",e,b,p),_||t.write(`event: error
95
+ data: ${JSON.stringify({status:p,body:b})}
96
+
97
+ `)}}catch(y){p=504,console.log(`[Stream Responses] Error for ${n}: ${y}`)}_?$.updateRequestState(n,"error",{status_code:499}):t.end();let R=Math.round((Date.now()-r)/1e3*100)/100,d=Object.keys(u).length?u:null;!d&&m.length&&(d={output:[{type:"message",content:[{type:"output_text",text:m.join("")}]}]}),$.completeRequest(n,{request_body:e,response_body:d??{error:"Stream interrupted"},model:i,translated_model:c!==i?c:null,endpoint:"/v1/responses",status_code:_?499:p,request_size:s,response_size:JSON.stringify(d??{}).length,input_tokens:a,output_tokens:l,duration:R})}function Bt(t){if(!Array.isArray(t)||t.length===0)return!1;let e=t[t.length-1];if(typeof e!="object"||!e)return!1;let o=e.role;return o?typeof o=="string"&&o.toLowerCase()==="assistant":!0}function zt(t){for(let e=t.length-1;e>=0;e--){let o=t[e];if(o&&typeof o=="object"&&o.type==="compaction")return e===0?t:t.slice(e)}return t}function Jt(t){if(!Array.isArray(t))return!1;for(let e of t){if(typeof e!="object"||!e)continue;let o=e.content;if(Array.isArray(o)&&o.some(n=>n.type==="input_image")||e.type==="input_image")return!0}return!1}async function it(t,e,o,n){let s=f.maxConnectionRetries;for(let r=0;r<=s;r++)try{return await fetch(t,{...e,signal:AbortSignal.timeout(12e5)})}catch(i){let c=i;if(Re(o,n,r,s,c),await U(),r<s){let a=Math.min(2**r,8)*1e3;console.log(`[${n}] Connection error (attempt ${r+1}/${s+1}): ${c.message}`),await new Promise(l=>setTimeout(l,a))}else console.log(`[${n}] Connection error (final attempt): ${c.message}`)}return null}var ct=require("express");var we=(0,ct.Router)(),Vt=new Set(["model","messages","max_tokens","system","metadata","stop_sequences","stream","temperature","top_p","top_k","tools","tool_choice","thinking","service_tier"]);function Gt(t){let e=o=>{let n=o?.cache_control;n?.type==="ephemeral"&&"scope"in n&&delete n.scope};for(let o of t.tools??[])e(o);if(Array.isArray(t.system))for(let o of t.system)e(o);for(let o of t.messages??[])if(Array.isArray(o.content))for(let n of o.content)e(n)}function Wt(t){let e={};for(let[o,n]of Object.entries(t))Vt.has(o)&&(e[o]=n);return Gt(e),e}function Xt(t){let e=t.thinking;if(!e?.budget_tokens)return t;let o=e.budget_tokens,n=t.max_tokens??0;if(n<=o){let s=o+Math.min(16384,o);return console.log(`[DirectAnthropic] Adjusted max_tokens: ${n} \u2192 ${s} (thinking.budget_tokens=${o})`),{...t,max_tokens:s}}return t}function Kt(t){let e=t.system;if(!e)return f.systemPromptAdd.length?{...t,system:f.systemPromptAdd.map(o=>({type:"text",text:o}))}:t;if(typeof e=="string"){let o=$e(e);for(let n of f.systemPromptAdd)o.includes(n)||(o+=`
98
+
99
+ `+n);return{...t,system:o}}if(Array.isArray(e)){let o=e,n=o.filter(i=>i.type==="text").map(i=>i.text).join(`
100
+ `),s=!1,r=[];for(let i of o)if(i.type==="text"){let c=i.text;if(c.startsWith("x-anthropic-billing-header:")){s=!0;continue}let a=c;for(let l of f.systemPromptRemove)a.includes(l)&&(a=a.replaceAll(l,""),s=!0);r.push(a!==c?{...i,text:a}:i)}else r.push(i);for(let i of f.systemPromptAdd)n.includes(i)||(r.push({type:"text",text:i}),s=!0);return s?{...t,system:r}:t}return t}function Yt(t){let e=t.messages;if(!e?.length)return t;let o=!1,n=e.map(s=>{if(!Array.isArray(s.content))return s;let r=!1,i=s.content.map(c=>{if(c.type!=="tool_result"||typeof c.content!="string")return c;let a=Fe(c.content);return a!==c.content?(r=!0,{...c,content:a}):c});return r?(o=!0,{...s,content:i}):s});return o?{...t,messages:n}:t}we.post("/v1/messages/count_tokens",async(t,e)=>{try{await U();let o=t.body,n=o.model??"",s=f.models?.data?.find(c=>c.id===n);if(!s)return e.json({input_tokens:1});let r=0,i=o.system;if(typeof i=="string")r+=j(i);else if(Array.isArray(i))for(let c of i)c.type==="text"&&(r+=j(c.text??""));for(let c of o.messages??[]){let a=c.content;if(typeof a=="string")r+=j(a);else if(Array.isArray(a))for(let l of a)l.type==="text"?r+=j(l.text??""):l.type==="tool_result"&&typeof l.content=="string"?r+=j(l.content):l.type==="tool_use"&&(r+=j(JSON.stringify(l.input??{})))}if(o.tools?.length){r+=n.startsWith("claude")?346:n.startsWith("grok")?480:346;for(let c of o.tools)r+=j(c.name??""),r+=j(c.description??""),r+=j(JSON.stringify(c.input_schema??{}))}s.vendor!=="Anthropic"&&(r=Math.ceil(r*(n.startsWith("grok")?1.03:1.05))),e.json({input_tokens:r})}catch(o){console.log(`[count_tokens] Error: ${o}`),e.json({input_tokens:1})}});we.post("/v1/messages",async(t,e)=>{let o=Date.now();await U();let n=t.body,s=crypto.randomUUID(),r=n,i=n.model??"unknown",c=ne(i);return c!==i&&(console.log(`[Anthropic API] Model name translated: ${i} -> ${c}`),n={...n,model:c}),n=Kt(n),n=Yt(n),tt(c)?(console.log(`[Anthropic API] Using direct Anthropic API path for: ${c}`),Qt(e,n,s,o,i,c,r)):(console.log(`[Anthropic API] Using OpenAI translation path for: ${c}`),en(e,n,s,o,i,c,r))});async function Qt(t,e,o,n,s,r,i){let c=JSON.stringify(e).length,a=at(e),l=(e.messages??[]).some(_=>_.role==="assistant"),p=Y(a);p["anthropic-version"]="2023-06-01",p["X-Initiator"]=l?"agent":"user";let u=e,m=null;for(let _=0;_<=3;_++){let R=Wt(u);if(R=Xt(R),R.stream)return Zt(t,R,p,o,u,c,n,s,r,i);let d=await lt(`${N()}/v1/messages`,{method:"POST",headers:p,body:JSON.stringify(R)},o,"/v1/messages");if(!d)return void t.status(504).json({type:"error",error:{type:"api_error",message:"Upstream connection error"}});let y=Math.round((Date.now()-n)/1e3*100)/100;if(d.ok){let k=await d.json(),x=k.usage;return $.addRequest(o,{original_request_body:i,request_body:u,response_body:k,model:s,translated_model:r!==s?r:null,endpoint:"/v1/messages",status_code:d.status,request_size:c,response_size:JSON.stringify(k).length,input_tokens:x?.input_tokens??0,output_tokens:x?.output_tokens??0,duration:y}),m&&ke({...m,modified_request:u,final_status_code:d.status,final_response:k}),void t.json(k)}let b=await d.text();if(Z("/v1/messages",u,b,d.status),Ne(d.status,b)){let k=qe(b);if(k.length){console.log(`[Direct Anthropic] Attempt ${_+1}: orphaned IDs: ${k}`),m?m.orphaned_ids.push(...k):m={request_id:o,original_request:e,error_response:b,error_status_code:d.status,orphaned_ids:k},u={...u,messages:Ie(u.messages,k)};continue}}return m&&ke({...m,modified_request:u,final_status_code:d.status,final_response:b}),void t.status(d.status).type("json").send(b)}}async function Zt(t,e,o,n,s,r,i,c,a,l){$.startRequest(n,{original_request_body:l,request_body:s,model:c,translated_model:a!==c?a:null,endpoint:"/v1/messages",request_size:r}),t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","X-Accel-Buffering":"no"});let p=0,u=0,m=200,_=c,R=[],d=!1;t.on("close",()=>{d=!0});try{$.updateRequestState(n,"sending");let k=await fetch(`${N()}/v1/messages`,{method:"POST",headers:o,body:JSON.stringify(e),signal:AbortSignal.timeout(12e5)});m=k.status,$.updateRequestState(n,"receiving");let x=k.body.getReader(),T=new TextDecoder,q="",C="";for(;;){let{done:H,value:I}=await x.read();if(H)break;if(d){x.cancel();break}q+=T.decode(I,{stream:!0});let V=q.split(`
101
+ `);q=V.pop();for(let A of V){if(!A.trim())continue;if(A.startsWith("event: ")){C=A.slice(7);continue}if(!A.startsWith("data: "))continue;let B=A.slice(6);if(B==="[DONE]")break;try{let O=JSON.parse(B),ee=C||O.type||"";if(C="",ee==="message_start"){let re=O.message??{};_=re.model??c,p=re.usage?.input_tokens??0}else ee==="message_delta"?u=O.usage?.output_tokens??0:ee==="content_block_delta"&&O.delta?.type==="text_delta"&&R.push(O.delta.text??"");d||t.write(`event: ${ee}
102
+ data: ${B}
103
+
104
+ `)}catch{}}}}catch(k){m=504,console.log(`[Stream Direct Anthropic] Error for ${n}: ${k}`)}d?$.updateRequestState(n,"error",{status_code:499}):t.end();let y=Math.round((Date.now()-i)/1e3*100)/100,b=R.length?{id:n,type:"message",role:"assistant",content:[{type:"text",text:R.join("")}],model:_,usage:{input_tokens:p,output_tokens:u}}:{error:{type:"api_error",message:"Stream interrupted"}};$.completeRequest(n,{request_body:s,response_body:b,model:c,translated_model:a!==c?a:null,endpoint:"/v1/messages",status_code:d?499:m,request_size:r,response_size:JSON.stringify(b).length,input_tokens:p,output_tokens:u,duration:y})}async function en(t,e,o,n,s,r,i){let c=at(e),a=JSON.stringify(e).length,l=e,p=null;for(let u=0;u<=3;u++){let m=ot(l),_=(m.messages??[]).some(k=>k.role==="assistant"||k.role==="tool"),R=Y(c);if(R["X-Initiator"]=_?"agent":"user",e.stream)return tn(t,m,R,o,l,a,n,s,r,i);let d=await lt(`${N()}/chat/completions`,{method:"POST",headers:R,body:JSON.stringify(m)},o,"/v1/messages (translated)");if(!d)return void t.status(504).json({type:"error",error:{type:"api_error",message:"Upstream connection error"}});let y=Math.round((Date.now()-n)/1e3*100)/100;if(d.ok){let k=await d.json(),x=He(k),T=k.usage;return $.addRequest(o,{original_request_body:i,request_body:l,response_body:x,model:s,translated_model:r!==s?r:null,endpoint:"/v1/messages",status_code:d.status,request_size:a,response_size:JSON.stringify(x).length,input_tokens:T?.prompt_tokens??0,output_tokens:T?.completion_tokens??0,duration:y}),void t.json(x)}let b=await d.text();if(Z("/v1/messages",l,b,d.status),Ne(d.status,b)){let k=qe(b);if(k.length){p?p.orphaned_ids.push(...k):p={request_id:o,original_request:e,error_response:b,error_status_code:d.status,orphaned_ids:k},l={...l,messages:Ie(l.messages,k)};continue}}return p&&ke({...p,modified_request:l,final_status_code:d.status,final_response:b}),void t.status(d.status).type("json").send(b)}}async function tn(t,e,o,n,s,r,i,c,a,l){$.startRequest(n,{original_request_body:l,request_body:s,model:c,translated_model:a!==c?a:null,endpoint:"/v1/messages",request_size:r}),t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","X-Accel-Buffering":"no"});let p=new be,u=[],m=0,_=0,R=200,d=!1;t.on("close",()=>{d=!0});try{$.updateRequestState(n,"sending");let x=await fetch(`${N()}/chat/completions`,{method:"POST",headers:o,body:JSON.stringify(e),signal:AbortSignal.timeout(12e5)});R=x.status,$.updateRequestState(n,"receiving");let T=x.body.getReader(),q=new TextDecoder,C="";for(;;){let{done:H,value:I}=await T.read();if(H)break;if(d){T.cancel();break}C+=q.decode(I,{stream:!0});let V=C.split(`
105
+ `);C=V.pop();for(let A of V){if(!A.trim()||!A.startsWith("data: "))continue;let B=A.slice(6);if(B==="[DONE]")break;try{let O=JSON.parse(B);u.push(O),O.usage&&(m=O.usage.prompt_tokens??0,_=O.usage.completion_tokens??0);let ee=st(O,p);for(let re of ee)d||t.write(`event: ${re.type}
106
+ data: ${JSON.stringify(re)}
107
+
108
+ `)}catch{}}}}catch(x){R=504,console.log(`[Stream Anthropic] Error for ${n}: ${x}`)}d?$.updateRequestState(n,"error",{status_code:499}):t.end();let y=Math.round((Date.now()-i)/1e3*100)/100,b=xe(u),k=Object.keys(b).length?He(b):{error:{type:"api_error",message:"Stream interrupted"}};$.completeRequest(n,{request_body:s,response_body:k,model:c,translated_model:a!==c?a:null,endpoint:"/v1/messages",status_code:d?499:R,request_size:r,response_size:u.reduce((x,T)=>x+JSON.stringify(T).length,0),input_tokens:m,output_tokens:_,duration:y})}function at(t){return(t.messages??[]).some(e=>Array.isArray(e.content)&&e.content.some(o=>o.type==="image"))}async function lt(t,e,o,n){let s=f.maxConnectionRetries;for(let r=0;r<=s;r++)try{return await fetch(t,{...e,signal:AbortSignal.timeout(12e5)})}catch(i){let c=i;Re(o,n,r,s,c),await U(),r<s&&(console.log(`[${n}] Connection error (attempt ${r+1}/${s+1}): ${c.message}`),await new Promise(a=>setTimeout(a,Math.min(2**r,8)*1e3)))}return null}var ut=require("express"),de=v(require("path")),pt=v(require("fs"));var P=(0,ut.Router)();function dt(){let t=de.default.join(__dirname,"..","public");return pt.default.existsSync(t)?t:de.default.join(__dirname,"..","..","public")}P.get("/",(t,e)=>{e.sendFile(de.default.join(dt(),"dashboard.html"))});P.get("/requests",(t,e)=>{e.sendFile(de.default.join(dt(),"requests.html"))});P.get("/api/stats",(t,e)=>{e.json($.getStats())});P.get("/api/requests",(t,e)=>{let o=parseInt(t.query.page)||1,n=parseInt(t.query.per_page)||50,s=t.query.search||"",r=(o-1)*n,i,c;s?(i=$.searchRequests(s,n,r),c=$.searchRequests(s,1e4,0).length):(i=$.getRecentRequests(n,r),c=$.getTotalCount());let a=i.map(l=>({...l,request_body:null,response_body:null}));e.json({items:a,total:c,page:o,per_page:n,total_pages:Math.ceil(c/n)})});P.get("/api/request/:id",(t,e)=>{let o=$.getRequest(t.params.id);if(!o)return e.status(404).json({error:"Request not found"});e.json(o)});P.get("/api/request/:id/request-body",(t,e)=>{let o=$.getRequest(t.params.id);if(!o)return e.status(404).json({error:"Request not found"});e.json(o.request_body)});P.get("/api/request/:id/response-body",(t,e)=>{let o=$.getRequest(t.params.id);if(!o)return e.status(404).json({error:"Request not found"});e.json(o.response_body)});P.get("/api/requests/search",(t,e)=>{let o=parseInt(t.query.page)||1,n=parseInt(t.query.per_page)||50,s=t.query.q||"",r=(o-1)*n;if(!s)return e.json({items:[],total:0,page:o,per_page:n,total_pages:0});let[i,c]=$.fulltextSearch(s,n,r),a=i.map(l=>({...l,request_body:null,response_body:null}));e.json({items:a,total:c,page:o,per_page:n,total_pages:c>0?Math.ceil(c/n):0})});P.get("/api/requests/export",(t,e)=>{let o=new Date().toISOString().replace(/[:.]/g,"").slice(0,15);e.setHeader("Content-Type","application/x-jsonlines"),e.setHeader("Content-Disposition",`attachment; filename=requests_${o}.jl`);for(let n of $.getAllRequests())e.write(JSON.stringify(n)+`
109
+ `);e.end()});P.post("/api/requests/import",(t,e)=>{let n=(typeof t.body=="string"?t.body:JSON.stringify(t.body)).split(`
110
+ `).filter(i=>i.trim()),s=0,r=[];for(let i=0;i<n.length;i++)try{let c=JSON.parse(n[i]);$.importRequest(c),s++}catch(c){r.push(`Line ${i+1}: ${c}`)}e.json({imported:s,errors:r.slice(0,10),total_errors:r.length})});var S=v(require("fs")),M=v(require("path")),ft=v(require("readline"));var L="\x1B[1m",w="\x1B[2m",g="\x1B[0m",K="\x1B[36m",D="\x1B[32m",se="\x1B[33m",W="\x1B[35m",Se="localhost";function nn(){console.log(`
111
+ ${W}\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557${g}
112
+ ${W}\u2551${g} ${L}ghc-tunnel Setup Wizard${g} ${W}\u2551${g}
113
+ ${W}\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D${g}
114
+ `)}function Be(){return ft.default.createInterface({input:process.stdin,output:process.stdout})}function fe(t,e){return new Promise(o=>t.question(e,o))}async function mt(){nn(),console.log(`${L}Step 1: GitHub Authentication${g}`),console.log(`${w}Checking for GitHub token...${g}
115
+ `);let t=await he();t||(console.error("Failed to get GitHub token. Cannot continue setup."),process.exit(1)),f.githubToken=t,await pe(),await te();let e=f.models?.data??[];e.length||(console.error("No models available. Check your Copilot subscription."),process.exit(1)),console.log(`
116
+ ${L}Step 2: Configure Models${g}
117
+ `),console.log(`${w}Available Copilot models:${g}
118
+ `);for(let R=0;R<e.length;R++){let d=e[R],y=[];d.capabilities?.supports?.vision&&y.push("Vision"),d.capabilities?.supports?.tool_calls&&y.push("Tools"),d.supported_endpoints?.includes("/v1/messages")&&y.push("Anthropic"),d.preview&&y.push("Preview");let b=y.length?` ${w}(${y.join(", ")})${g}`:"";console.log(` ${K}${String(R+1).padStart(2)}.${g} ${d.id}${b}`)}let o=Be();console.log(`
119
+ ${se}Pick models by number. Press Enter to accept the default.${g}
120
+ `);let n=on(e,[h,"claude-opus-4.7","claude-opus-4.6-1m","claude-opus-4.6"])??0,s=await gt(o,e,'Primary model (mapped to "opus", "sonnet" aliases)',n),r=e[s],i=_t(e,G)??0,c=await gt(o,e,'Small/fast model (mapped to "haiku" alias)',i),a=e[c];console.log(`
121
+ ${D}Primary: ${r.id}${g}`),console.log(`${D}Small: ${a.id}${g}
122
+ `),console.log(`${L}Step 3: Server Settings${g}
123
+ `);let l=await fe(o,` Port ${w}[8314]${g}: `),p=l.trim()?parseInt(l.trim(),10):8314,u=Se;console.log(`${D}Host: ${u}${g}
124
+ `),console.log(`
125
+ ${L}Step 4: Claude Code Model Settings${g}`),console.log(`${w}Using the same Claude Code model identifier for both primary and fallback model settings.${g}
126
+ `),console.log(`${D}ANTHROPIC_MODEL: ${z}${g}`),console.log(`${D}ANTHROPIC_DEFAULT_HAIKU_MODEL: ${z}${g}
127
+ `),console.log(`${L}Step 4b: Codex Model${g}
128
+ `);let{model:m,reasoning:_}=await $t(o,{model:ze,reasoning:Je});return console.log(`${D}Codex model: ${m} (reasoning: ${_})${g}
129
+ `),o.close(),console.log(`${L}Step 5: Saving Configuration${g}
130
+ `),sn(r.id,a.id,p,u),ht(p,u),an(p,u,m,_),console.log(`
131
+ ${D}${L}Setup complete!${g}
132
+ `),{port:p,host:u}}async function gt(t,e,o,n){let s=e[n]?.id??"?";for(;;){let r=await fe(t,` ${o} ${w}[${n+1}: ${s}]${g}: `);if(!r.trim())return n;let i=parseInt(r.trim(),10);if(i>=1&&i<=e.length)return i-1;let c=e.findIndex(a=>a.id===r.trim());if(c!==-1)return c;console.log(` ${se}Invalid choice. Enter a number 1-${e.length} or a model name.${g}`)}}function _t(t,e){let o=t.findIndex(n=>n.id===e);return o>=0?o:void 0}function on(t,e){for(let o of e){let n=_t(t,o);if(n!==void 0)return n}}function sn(t,e,o,n){let s=E();S.default.mkdirSync(s,{recursive:!0});let r=M.default.join(s,"config.yaml"),i=`# ghc-tunnel Configuration (generated by --setup)
127
133
  # ================================================
128
134
 
129
- address: ${o}
130
- port: ${n}
135
+ address: ${n}
136
+ port: ${o}
131
137
  debug: false
132
138
 
133
139
  account_type: individual
134
140
 
135
- vscode_version: "${se}"
136
- api_version: "${oe}"
137
- copilot_version: "${ne}"
141
+ vscode_version: "${le}"
142
+ api_version: "${ae}"
143
+ copilot_version: "${ce}"
138
144
 
139
145
  model_mappings:
140
146
  exact:
141
147
  opus: ${t}
142
- sonnet: ${t}
148
+ sonnet: claude-sonnet-4.6
149
+ opus4-7: ${t}
150
+ "4-7[1m]": ${t}
143
151
  haiku: ${e}
144
152
  prefix:
145
- claude-sonnet-4-: ${t}
146
- claude-opus-4.5-: ${t}
147
- claude-opus-4.6-: ${t}
148
- claude-opus-4-5-: ${t}
149
- claude-opus-4-6-: ${t}
150
- "claude-opus-4.5": ${t}
151
- "claude-opus-4.6": ${t}
152
- "claude-opus-4-6": ${t}
153
- "claude-opus-4-6[1m]": ${t}
154
- claude-sonnet-4-6: ${t}
155
- claude-sonnet-4-5: ${t}
153
+ claude-opus-4.5-: claude-opus-4.6
154
+ claude-opus-4.6-: claude-opus-4.6
155
+ claude-opus-4.7-: claude-opus-4.7
156
+ claude-opus-4-5-: claude-opus-4.6
157
+ claude-opus-4-6-: claude-opus-4.6
158
+ claude-opus-4-7-: claude-opus-4.7
159
+ "claude-opus-4.5": claude-opus-4.6
160
+ "claude-opus-4.6": claude-opus-4.6
161
+ "claude-opus-4.7": claude-opus-4.7
162
+ "claude-opus-4-6": claude-opus-4.6
163
+ "claude-opus-4-7": claude-opus-4.7
164
+ "claude-opus-4-6[1m]": claude-opus-4.6
165
+ "claude-opus-4-7[1m]": claude-opus-4.7
166
+ claude-sonnet-4-7: claude-sonnet-4.7
167
+ claude-sonnet-4-6: claude-sonnet-4.6
168
+ claude-sonnet-4-5: claude-sonnet-4.6
156
169
  claude-haiku-4.5-: ${e}
157
170
  claude-haiku-4-5-: ${e}
158
171
 
@@ -161,17 +174,41 @@ system_prompt_add: []
161
174
  tool_result_suffix_remove: []
162
175
 
163
176
  max_connection_retries: 3
164
- `;U.default.writeFileSync(r,i,"utf-8"),console.log(` ${B}\u2713${m} Config saved to ${Q}${r}${m}`)}function at(t,e,n,o){let s=Z.default.join(process.env.HOME||"~",".claude"),r=Z.default.join(s,"settings.json"),i={};if(U.default.existsSync(r))try{i=JSON.parse(U.default.readFileSync(r,"utf-8"))}catch{console.log(` ${Ne}\u26A0${m} Could not parse existing settings.json, creating new one`)}let c=`http://${e}:${t}/`,a=i.env??{};a.ANTHROPIC_BASE_URL=c,a.ANTHROPIC_AUTH_TOKEN="dummy",a.ANTHROPIC_MODEL=n,a.ANTHROPIC_DEFAULT_HAIKU_MODEL=o,a.DISABLE_NON_ESSENTIAL_MODEL_CALLS="1",a.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC="1",delete a.ANTHROPIC_SMALL_FAST_MODEL,i.env=a,U.default.mkdirSync(s,{recursive:!0}),U.default.writeFileSync(r,JSON.stringify(i,null,2)+`
165
- `,"utf-8"),console.log(` ${B}\u2713${m} Claude Code settings updated at ${Q}${r}${m}`),console.log(` ${v}ANTHROPIC_BASE_URL = ${c}${m}`),console.log(` ${v}ANTHROPIC_MODEL = ${n}${m}`),console.log(` ${v}ANTHROPIC_DEFAULT_HAIKU_MODEL = ${o}${m}`)}async function ut(){console.log(`
166
- ${z}\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557${m}
167
- ${z}\u2551${m} ${L}Claude Code Settings${m} ${z}\u2551${m}
168
- ${z}\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D${m}
169
- `);let t=Z.default.join(process.env.HOME||"~",".claude"),e=Z.default.join(t,"settings.json"),n={},o={};if(U.default.existsSync(e))try{n=JSON.parse(U.default.readFileSync(e,"utf-8")),o=n.env??{}}catch{}(o.ANTHROPIC_BASE_URL||o.ANTHROPIC_MODEL||o.ANTHROPIC_DEFAULT_HAIKU_MODEL)&&(console.log(`${L}Current configuration:${m}`),o.ANTHROPIC_BASE_URL&&console.log(` ANTHROPIC_BASE_URL: ${Q}${o.ANTHROPIC_BASE_URL}${m}`),o.ANTHROPIC_MODEL&&console.log(` ANTHROPIC_MODEL: ${Q}${o.ANTHROPIC_MODEL}${m}`),o.ANTHROPIC_DEFAULT_HAIKU_MODEL&&console.log(` ANTHROPIC_DEFAULT_HAIKU_MODEL: ${Q}${o.ANTHROPIC_DEFAULT_HAIKU_MODEL}${m}`),console.log(`
170
- ${v}Press Enter to keep current values.${m}
171
- `));let r=8314,i="localhost",c=o.ANTHROPIC_MODEL||"claude-opus-4-6[1m]",a=o.ANTHROPIC_DEFAULT_HAIKU_MODEL||"claude-sonnet-4-6",u=r,p=i;if(o.ANTHROPIC_BASE_URL)try{let S=new URL(o.ANTHROPIC_BASE_URL);p=S.hostname,u=parseInt(S.port,10)||r}catch{}let l=it(),f=await F(l,` Port ${v}[${u}]${m}: `),_=f.trim()?parseInt(f.trim(),10):u,d=(await F(l,` Host ${v}[${p}]${m}: `)).trim()||p,b=(await F(l,` ANTHROPIC_MODEL ${v}[${c}]${m}: `)).trim()||c,R=(await F(l,` ANTHROPIC_DEFAULT_HAIKU_MODEL ${v}[${a}]${m}: `)).trim()||a;l.close(),at(_,d,b,R),console.log(`
172
- ${B}${L}Claude Code settings updated!${m}
173
- `)}var lt="1.0.2";function Ft(){let t=process.argv.slice(2),e={};for(let n=0;n<t.length;n++){let o=t[n];o==="-p"||o==="--port"?e.port=parseInt(t[++n],10):o==="-a"||o==="--address"?e.address=t[++n]:o==="-c"||o==="--config"?e.config=!0:o==="-s"||o==="--setup"?e.setup=!0:o==="--claudecode"?e.claudecode=!0:o==="-v"||o==="--version"?e.version=!0:(o==="-h"||o==="--help")&&(e.help=!0)}return e}function pt(t,e){let n=(0,ye.default)();n.use(ye.default.json({limit:"50mb"})),n.use(ye.default.text({limit:"50mb"})),n.use(he),n.use(Y),n.use(P),n.use((o,s)=>s.status(404).json({error:"Not found"})),n.listen(e,t,()=>{console.log(`
174
- Starting GitHub Copilot API Proxy on ${t}:${e}`),console.log(`Dashboard: http://${t}:${e}/`),console.log(`OpenAI API: http://${t}:${e}/v1/chat/completions`),console.log(`Responses API: http://${t}:${e}/v1/responses`),console.log(`Anthropic API: http://${t}:${e}/v1/messages`)})}function dt(t,e){let n=T(),o=ke.default.join(n,"config.yaml");ae.default.existsSync(o)||(console.log(`No config file found at ${o}, generating one.`),ve());try{let s=Fe(o);t=s.address??t,e=s.port??e,s.account_type&&(g.accountType=s.account_type),s.vscode_version&&(g.vscodeVersion=s.vscode_version),s.api_version&&(g.apiVersion=s.api_version),s.copilot_version&&(g.copilotVersion=s.copilot_version),s.system_prompt_remove&&(g.systemPromptRemove=s.system_prompt_remove),s.system_prompt_add&&(g.systemPromptAdd=s.system_prompt_add),s.tool_result_suffix_remove&&(g.toolResultSuffixRemove=s.tool_result_suffix_remove),s.max_connection_retries!=null&&(g.maxConnectionRetries=s.max_connection_retries),s.model_mappings?M.loadFromConfig(s):M.loadFromConfig({model_mappings:we}),console.log(`Loaded configuration from: ${o}`)}catch(s){console.log(`Error loading config: ${s}`),M.loadFromConfig({model_mappings:we})}return{host:t,port:e}}function Bt(){let t=T(),e=ke.default.join(t,"config.yaml");if(!ae.default.existsSync(e))return!0;let n=ke.default.join(process.env.HOME||"~",".claude","settings.json");if(!ae.default.existsSync(n))return!0;try{let s=JSON.parse(ae.default.readFileSync(n,"utf-8"))?.env;if(!s?.ANTHROPIC_BASE_URL||!s.ANTHROPIC_BASE_URL.includes("localhost"))return!0}catch{return!0}return!1}async function zt(){let t=Ft();if(t.help&&(console.log(`ghc-tunnel v${lt} \u2013 GitHub Copilot API Proxy
177
+ `;S.default.writeFileSync(r,i,"utf-8"),console.log(` ${D}\u2713${g} Config saved to ${K}${r}${g}`)}function ht(t,e){let o=M.default.join(process.env.HOME||"~",".claude"),n=M.default.join(o,"settings.json"),s={};if(S.default.existsSync(n))try{s=JSON.parse(S.default.readFileSync(n,"utf-8"))}catch{console.log(` ${se}\u26A0${g} Could not parse existing settings.json, creating new one`)}let r=`http://${e}:${t}/`,i=s.env??{};i.ANTHROPIC_BASE_URL=r,i.ANTHROPIC_AUTH_TOKEN="dummy",i.ANTHROPIC_MODEL=z,i.ANTHROPIC_DEFAULT_HAIKU_MODEL=z,i.DISABLE_NON_ESSENTIAL_MODEL_CALLS="1",i.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC="1",delete i.ANTHROPIC_SMALL_FAST_MODEL,s.env=i,S.default.mkdirSync(o,{recursive:!0}),S.default.writeFileSync(n,JSON.stringify(s,null,2)+`
178
+ `,"utf-8"),console.log(` ${D}\u2713${g} Claude Code settings updated at ${K}${n}${g}`),console.log(` ${w}ANTHROPIC_BASE_URL = ${r}${g}`),console.log(` ${w}ANTHROPIC_MODEL = ${z}${g}`),console.log(` ${w}ANTHROPIC_DEFAULT_HAIKU_MODEL = ${z}${g}`)}var ze="gpt-5.4",F="ghc-tunnel",yt="GHC TUNNEL",Je="high",Rt="pragmatic";function X(t,e,o){let n=t.search(/(^|\n)\[/),s=n===-1?t:t.slice(0,n),r=n===-1?"":t.slice(n),i=`${e} = ${o}`,c=new RegExp(`^${e}\\s*=.*$`,"m");if(c.test(s))return s.replace(c,i)+r;let a=s.length===0||s.endsWith(`
179
+ `)?"":`
180
+ `;return`${s}${a}${i}
181
+ ${r}`}function kt(t,e,o,n){let s=e.replace(/[.[\]]/g,a=>`\\${a}`),r=new RegExp(`(^|\\n)\\[${s}\\][^\\[]*?(?=\\n\\[|$)`),i=`[${e}]
182
+ ${o.trim()}
183
+ `;if(r.test(t))return t.replace(r,(a,l)=>`${l}${i}`);if(n){let a=n.replace(/\./g,"\\."),l=new RegExp(`\\[${a}[^\\]]*\\][^\\[]*`,"g"),p=-1,u;for(;(u=l.exec(t))!==null;)p=u.index+u[0].length;if(p!==-1){let m=t.slice(0,p),_=t.slice(p),R=m.endsWith(`
184
+
185
+ `)?"":m.endsWith(`
186
+ `)?`
187
+ `:`
188
+
189
+ `,d=_.startsWith(`
190
+ `)?"":`
191
+ `;return`${m}${R}${i}${d}${_}`}}let c=t.length===0||t.endsWith(`
192
+
193
+ `)?"":t.endsWith(`
194
+ `)?`
195
+ `:`
196
+
197
+ `;return`${t}${c}${i}`}function ge(t,e){let o=t.search(/(^|\n)\[/),n=o===-1?t:t.slice(0,o),s=new RegExp(`^${e}\\s*=\\s*"([^"]*)"`,"m"),r=n.match(s);return r?r[1]:void 0}function rn(t,e){let o=e.replace(/[.[\]]/g,r=>`\\${r}`),n=new RegExp(`(?:^|\\n)\\[${o}\\]\\n([\\s\\S]*?)(?=\\n\\[|$)`),s=t.match(n);return s?s[1]:void 0}function cn(t){let e=rn(t,`model_providers.${F}`);return e===void 0?!1:/^\s*name\s*=/m.test(e)&&/^\s*base_url\s*=/m.test(e)&&/^\s*wire_api\s*=/m.test(e)}async function $t(t,e){let o=(await fe(t,` Codex model ${w}[${e.model}]${g}: `)).trim(),n=(await fe(t,` Reasoning effort ${w}[${e.reasoning}] (low | medium | high | xhigh)${g}: `)).trim();return{model:o||e.model,reasoning:n||e.reasoning}}function an(t,e,o=ze,n=Je){let s=M.default.join(process.env.HOME||"~",".codex"),r=M.default.join(s,"config.toml"),i=`http://${e}:${t}/v1`,c="";if(S.default.existsSync(r))try{c=S.default.readFileSync(r,"utf-8")}catch{console.log(` ${se}\u26A0${g} Could not read existing config.toml, creating new one`)}let a=ge(c,"model_provider"),l=c;l=X(l,"model",JSON.stringify(o)),l=X(l,"model_reasoning_effort",JSON.stringify(n)),l=X(l,"personality",JSON.stringify(Rt)),l=X(l,"model_provider",JSON.stringify(F));let p=[`name = ${JSON.stringify(yt)}`,`base_url = ${JSON.stringify(i)}`,'wire_api = "responses"'].join(`
198
+ `);l=kt(l,`model_providers.${F}`,p,"model_providers."),S.default.mkdirSync(s,{recursive:!0}),S.default.writeFileSync(r,l,"utf-8");let u=a&&a!==F?` ${w}(was '${a}')${g}`:"";console.log(` ${D}\u2713${g} Codex config updated at ${K}${r}${g}${u}`),console.log(` ${w}model = ${o}, reasoning = ${n}${g}`),console.log(` ${w}model_provider = ${F} (base_url = ${i})${g}`)}async function bt(t,e){let o=M.default.join(process.env.HOME||"~",".codex"),n=M.default.join(o,"config.toml"),s=`http://${e}:${t}/v1`,r="";if(S.default.existsSync(n))try{r=S.default.readFileSync(n,"utf-8")}catch{return}let i=ge(r,"model"),c=ge(r,"model_reasoning_effort"),a=ge(r,"personality"),l=ge(r,"model_provider"),p=cn(r),u=[];if(i||u.push("model"),c||u.push("model_reasoning_effort"),a||u.push("personality"),l!==F&&u.push("model_provider"),p||u.push(`[model_providers.${F}]`),u.length===0)return;console.log(`
199
+ ${se}Codex config is incomplete (${u.join(", ")}). Filling in:${g}`);let m=i||ze,_=c||Je,R=!i,d=!c;if((R||d)&&process.stdin.isTTY){let b=Be();try{let k=await $t(b,{model:m,reasoning:_});R&&(m=k.model),d&&(_=k.reasoning)}finally{b.close()}}let y=r;if(i||(y=X(y,"model",JSON.stringify(m))),c||(y=X(y,"model_reasoning_effort",JSON.stringify(_))),a||(y=X(y,"personality",JSON.stringify(Rt))),l!==F&&(y=X(y,"model_provider",JSON.stringify(F))),!p){let b=[`name = ${JSON.stringify(yt)}`,`base_url = ${JSON.stringify(s)}`,'wire_api = "responses"'].join(`
200
+ `);y=kt(y,`model_providers.${F}`,b,"model_providers.")}try{S.default.mkdirSync(o,{recursive:!0}),S.default.writeFileSync(n,y,"utf-8"),console.log(` ${D}\u2713${g} Codex config patched at ${K}${n}${g}`)}catch(b){console.log(` ${se}\u26A0${g} Could not write Codex config: ${b}`)}}async function xt(){console.log(`
201
+ ${W}\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557${g}
202
+ ${W}\u2551${g} ${L}Claude Code Settings${g} ${W}\u2551${g}
203
+ ${W}\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D${g}
204
+ `);let t=M.default.join(process.env.HOME||"~",".claude"),e=M.default.join(t,"settings.json"),o={},n={};if(S.default.existsSync(e))try{o=JSON.parse(S.default.readFileSync(e,"utf-8")),n=o.env??{}}catch{}(n.ANTHROPIC_BASE_URL||n.ANTHROPIC_MODEL||n.ANTHROPIC_DEFAULT_HAIKU_MODEL)&&(console.log(`${L}Current configuration:${g}`),n.ANTHROPIC_BASE_URL&&console.log(` ANTHROPIC_BASE_URL: ${K}${n.ANTHROPIC_BASE_URL}${g}`),n.ANTHROPIC_MODEL&&console.log(` ANTHROPIC_MODEL: ${K}${n.ANTHROPIC_MODEL}${g}`),n.ANTHROPIC_DEFAULT_HAIKU_MODEL&&console.log(` ANTHROPIC_DEFAULT_HAIKU_MODEL: ${K}${n.ANTHROPIC_DEFAULT_HAIKU_MODEL}${g}`),console.log(`
205
+ ${w}Press Enter to keep the current port. Host and model identifiers will be reset to ${Se} and ${z}.${g}
206
+ `));let r=8314,i=r;if(n.ANTHROPIC_BASE_URL)try{let p=new URL(n.ANTHROPIC_BASE_URL);i=parseInt(p.port,10)||r}catch{}let c=Be(),a=await fe(c,` Port ${w}[${i}]${g}: `),l=a.trim()?parseInt(a.trim(),10):i;c.close(),console.log(`
207
+ ${w}Writing ANTHROPIC_BASE_URL as http://${Se}:${l}/ and both model identifiers as ${z}.${g}
208
+ `),ht(l,Se),console.log(`
209
+ ${D}${L}Claude Code settings updated!${g}
210
+ `)}var wt="1.0.2";function ln(){let t=process.argv.slice(2),e={};for(let o=0;o<t.length;o++){let n=t[o];n==="-p"||n==="--port"?e.port=parseInt(t[++o],10):n==="-a"||n==="--address"?e.address=t[++o]:n==="-c"||n==="--config"?e.config=!0:n==="-s"||n==="--setup"?e.setup=!0:n==="--claudecode"?e.claudecode=!0:n==="-v"||n==="--version"?e.version=!0:(n==="-h"||n==="--help")&&(e.help=!0)}return e}function St(t,e){let o=(0,ve.default)();o.use(ve.default.json({limit:"50mb"})),o.use(ve.default.text({limit:"50mb"})),o.use(we),o.use(oe),o.use(P),o.use((n,s)=>s.status(404).json({error:"Not found"})),o.listen(e,t,()=>{console.log(`
211
+ Starting GitHub Copilot API Proxy on ${t}:${e}`),console.log(`Dashboard: http://${t}:${e}/`),console.log(`OpenAI API: http://${t}:${e}/v1/chat/completions`),console.log(`Responses API: http://${t}:${e}/v1/responses`),console.log(`Anthropic API: http://${t}:${e}/v1/messages`)})}function vt(t,e){let o=E(),n=Ce.default.join(o,"config.yaml");me.default.existsSync(n)||(console.log(`No config file found at ${n}, generating one.`),Pe());try{let s=Ye(n);t=s.address??t,e=s.port??e,s.account_type&&(f.accountType=s.account_type),s.vscode_version&&(f.vscodeVersion=s.vscode_version),s.api_version&&(f.apiVersion=s.api_version),s.copilot_version&&(f.copilotVersion=s.copilot_version),s.system_prompt_remove&&(f.systemPromptRemove=s.system_prompt_remove),s.system_prompt_add&&(f.systemPromptAdd=s.system_prompt_add),s.tool_result_suffix_remove&&(f.toolResultSuffixRemove=s.tool_result_suffix_remove),s.max_connection_retries!=null&&(f.maxConnectionRetries=s.max_connection_retries),s.model_mappings?J.loadFromConfig(s):J.loadFromConfig({model_mappings:Te}),console.log(`Loaded configuration from: ${n}`)}catch(s){console.log(`Error loading config: ${s}`),J.loadFromConfig({model_mappings:Te})}return{host:t,port:e}}function un(){let t=E(),e=Ce.default.join(t,"config.yaml");if(!me.default.existsSync(e))return!0;let o=Ce.default.join(process.env.HOME||"~",".claude","settings.json");if(!me.default.existsSync(o))return!0;try{let s=JSON.parse(me.default.readFileSync(o,"utf-8"))?.env;if(!s?.ANTHROPIC_BASE_URL||!s.ANTHROPIC_BASE_URL.includes("localhost"))return!0}catch{return!0}return!1}async function pn(){let t=ln();if(t.help&&(console.log(`ghc-tunnel v${wt} \u2013 GitHub Copilot API Proxy
175
212
 
176
213
  Usage: ghc-tunnel [options]
177
214
 
@@ -182,6 +219,6 @@ Options:
182
219
  -a, --address <addr> Address to listen on (default: localhost)
183
220
  -c, --config Generate default config file
184
221
  -v, --version Show version
185
- -h, --help Show this help`),process.exit(0)),t.version&&(console.log(`ghc-tunnel ${lt}`),process.exit(0)),t.config&&(ve(),process.exit(0)),t.setup&&t.claudecode&&(await ut(),process.exit(0)),t.setup||Bt()){t.setup||console.log(`First run detected \u2014 launching setup wizard.
186
- `);let{port:r,host:i}=await ct(),{host:c,port:a}=dt(i,r);Te(),Oe(),pt(c,a);return}let{host:n,port:o}=dt(Me,He);t.address!=null&&(n=t.address),t.port!=null&&(o=t.port);let s=await le();s||(console.error(`
187
- `+"=".repeat(60)),console.error("ERROR: No GitHub token available!"),console.error("Options:"),console.error(" 1. Set GITHUB_TOKEN environment variable"),console.error(" 2. Create github_token.txt in "+T()),console.error(" 3. Run again for interactive Device Flow auth"),console.error("=".repeat(60)),process.exit(1)),g.githubToken=s,await ie(),await W(),Te(),Oe(),pt(n,o)}zt().catch(t=>{console.error("Fatal error:",t),process.exit(1)});
222
+ -h, --help Show this help`),process.exit(0)),t.version&&(console.log(`ghc-tunnel ${wt}`),process.exit(0)),t.config&&(Pe(),process.exit(0)),t.setup&&t.claudecode&&(await xt(),process.exit(0)),t.setup||un()){t.setup||console.log(`First run detected \u2014 launching setup wizard.
223
+ `);let{port:r,host:i}=await mt(),{host:c,port:a}=vt(i,r);Ue(),Le(),St(c,a);return}let{host:o,port:n}=vt(Xe,We);t.address!=null&&(o=t.address),t.port!=null&&(n=t.port);let s=await he();s||(console.error(`
224
+ `+"=".repeat(60)),console.error("ERROR: No GitHub token available!"),console.error("Options:"),console.error(" 1. Set GITHUB_TOKEN environment variable"),console.error(" 2. Create github_token.txt in "+E()),console.error(" 3. Run again for interactive Device Flow auth"),console.error("=".repeat(60)),process.exit(1)),f.githubToken=s,await pe(),await te(),await bt(n,o),Ue(),Le(),St(o,n)}pn().catch(t=>{console.error("Fatal error:",t),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ghc-tunnel",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "GitHub Copilot API Proxy - Provides OpenAI and Anthropic compatible endpoints via GitHub Copilot",
5
5
  "main": "dist/index.js",
6
6
  "bin": {