ghc-tunnel 1.0.6 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/index.js +99 -84
- package/package.json +1 -1
- package/public/dashboard.html +1 -1
package/README.md
CHANGED
|
@@ -57,8 +57,8 @@ Run `ghc-tunnel --setup --claudecode` or manually configure `~/.claude/settings.
|
|
|
57
57
|
"env": {
|
|
58
58
|
"ANTHROPIC_BASE_URL": "http://127.0.0.1:8314/",
|
|
59
59
|
"ANTHROPIC_AUTH_TOKEN": "dummy",
|
|
60
|
-
"ANTHROPIC_MODEL": "claude-opus-4-
|
|
61
|
-
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-opus-4-
|
|
60
|
+
"ANTHROPIC_MODEL": "claude-opus-4-8[1m]",
|
|
61
|
+
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-opus-4-8[1m]"
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
```
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var Et=Object.create;var Ke=Object.defineProperty;var Dt=Object.getOwnPropertyDescriptor;var Pt=Object.getOwnPropertyNames;var qt=Object.getPrototypeOf,
|
|
2
|
+
"use strict";var Et=Object.create;var Ke=Object.defineProperty;var Dt=Object.getOwnPropertyDescriptor;var Pt=Object.getOwnPropertyNames;var qt=Object.getPrototypeOf,Nt=Object.prototype.hasOwnProperty;var Lt=(t,e,o,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of Pt(e))!Nt.call(t,s)&&s!==o&&Ke(t,s,{get:()=>e[s],enumerable:!(n=Dt(e,s))||n.enumerable});return t};var v=(t,e,o)=>(o=t!=null?Et(qt(t)):{},Lt(e||!t||!t.__esModule?Ke(o,"default",{value:t,enumerable:!0}):o,t));var Re=v(require("fs")),Pe=v(require("path")),De=v(require("express"));var le=v(require("fs")),ke=v(require("path")),qe=v(require("os")),Ye=v(require("js-yaml")),Qe=parseInt(process.env.PORT||"8314",10),ee="127.0.0.1",Ze=process.env.HOST||ee,et="https://api.github.com",pe="0.26.7",de="2025-04-01",ge="1.93.0",m="claude-opus-4.8-1m",X="claude-haiku-4.5",V="claude-opus-4-8[1m]",Le={exact:{opus:m,sonnet:m,"opus4-8":m,"opus4-7":m,"4-8[1m]":m,"4-7[1m]":m,haiku:X},prefix:{"claude-sonnet-4-":m,"claude-opus-4.5-":m,"claude-opus-4.6-":m,"claude-opus-4.7-":m,"claude-opus-4.8-":m,"claude-opus-4-5-":m,"claude-opus-4-6-":m,"claude-opus-4-7-":m,"claude-opus-4-8-":m,"claude-opus-4.5":m,"claude-opus-4.6":m,"claude-opus-4.7":m,"claude-opus-4.8":m,"claude-opus-4-6":m,"claude-opus-4-7":m,"claude-opus-4-8":m,"claude-opus-4-6[1m]":m,"claude-opus-4-7[1m]":m,"claude-opus-4-8[1m]":m,"claude-sonnet-4-7":m,"claude-sonnet-4-6":m,"claude-sonnet-4-5":m,"claude-haiku-4.5-":X,"claude-haiku-4-5-":X}},Ie="01ab8ac9400c4e429b23",Ne=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??{}}},G=new Ne;function D(){return process.platform==="win32"?ke.default.join(process.env.APPDATA||qe.default.homedir(),"ghc-tunnel"):ke.default.join(qe.default.homedir(),".ghc-tunnel")}function tt(t){let e=le.default.readFileSync(t,"utf-8");return Ye.default.load(e)||{}}function Ue(){let t=D();le.default.mkdirSync(t,{recursive:!0});let e=ke.default.join(t,"config.yaml");if(le.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
|
|
@@ -20,29 +20,36 @@ copilot_version: "${pe}"
|
|
|
20
20
|
# Two types: exact (full name match) and prefix (starts-with match)
|
|
21
21
|
model_mappings:
|
|
22
22
|
exact:
|
|
23
|
-
opus: ${
|
|
24
|
-
sonnet: ${
|
|
25
|
-
opus4-
|
|
26
|
-
|
|
23
|
+
opus: ${m}
|
|
24
|
+
sonnet: ${m}
|
|
25
|
+
opus4-8: ${m}
|
|
26
|
+
opus4-7: ${m}
|
|
27
|
+
"4-8[1m]": ${m}
|
|
28
|
+
"4-7[1m]": ${m}
|
|
27
29
|
haiku: ${X}
|
|
28
30
|
prefix:
|
|
29
|
-
claude-sonnet-4-: ${
|
|
30
|
-
claude-opus-4.5-: ${
|
|
31
|
-
claude-opus-4.6-: ${
|
|
32
|
-
claude-opus-4.7-: ${
|
|
33
|
-
claude-opus-4
|
|
34
|
-
claude-opus-4-
|
|
35
|
-
claude-opus-4-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"claude-opus-4.
|
|
39
|
-
"claude-opus-4
|
|
40
|
-
"claude-opus-4
|
|
41
|
-
"claude-opus-4
|
|
42
|
-
"claude-opus-4-
|
|
43
|
-
claude-
|
|
44
|
-
claude-
|
|
45
|
-
claude-
|
|
31
|
+
claude-sonnet-4-: ${m}
|
|
32
|
+
claude-opus-4.5-: ${m}
|
|
33
|
+
claude-opus-4.6-: ${m}
|
|
34
|
+
claude-opus-4.7-: ${m}
|
|
35
|
+
claude-opus-4.8-: ${m}
|
|
36
|
+
claude-opus-4-5-: ${m}
|
|
37
|
+
claude-opus-4-6-: ${m}
|
|
38
|
+
claude-opus-4-7-: ${m}
|
|
39
|
+
claude-opus-4-8-: ${m}
|
|
40
|
+
"claude-opus-4.5": ${m}
|
|
41
|
+
"claude-opus-4.6": ${m}
|
|
42
|
+
"claude-opus-4.7": ${m}
|
|
43
|
+
"claude-opus-4.8": ${m}
|
|
44
|
+
"claude-opus-4-6": ${m}
|
|
45
|
+
"claude-opus-4-7": ${m}
|
|
46
|
+
"claude-opus-4-8": ${m}
|
|
47
|
+
"claude-opus-4-6[1m]": ${m}
|
|
48
|
+
"claude-opus-4-7[1m]": ${m}
|
|
49
|
+
"claude-opus-4-8[1m]": ${m}
|
|
50
|
+
claude-sonnet-4-7: ${m}
|
|
51
|
+
claude-sonnet-4-6: ${m}
|
|
52
|
+
claude-sonnet-4-5: ${m}
|
|
46
53
|
claude-haiku-4.5-: ${X}
|
|
47
54
|
claude-haiku-4-5-: ${X}
|
|
48
55
|
|
|
@@ -57,17 +64,17 @@ tool_result_suffix_remove: []
|
|
|
57
64
|
# Retry Settings
|
|
58
65
|
# Max retries for upstream connection errors (0 = no retries)
|
|
59
66
|
max_connection_retries: 3
|
|
60
|
-
`;
|
|
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:
|
|
67
|
+
`;le.default.writeFileSync(e,o,"utf-8"),console.log(`Configuration file generated at: ${e}`)}var je=class{githubToken="";copilotToken=null;models=null;accountType="individual";tokenExpiresAt=0;vscodeVersion=ge;copilotVersion=pe;apiVersion=de;systemPromptRemove=[];systemPromptAdd=[];toolResultSuffixRemove=[];redirectAnthropic=!1;maxConnectionRetries=3;get editorPluginVersion(){return`copilot-chat/${this.copilotVersion}`}get userAgent(){return`GitHubCopilotChat/${this.copilotVersion}`}},_=new je;var fe=v(require("fs")),nt=require("child_process"),ot=v(require("path"));function st(){let t=D();return fe.default.mkdirSync(t,{recursive:!0}),ot.default.join(t,"github_token.txt")}function It(){let t=st();if(!fe.default.existsSync(t))return null;try{let e=fe.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 Ut(t){try{let e=st();fe.default.writeFileSync(e,t,"utf-8"),console.log(`Saved GitHub token to ${e}`)}catch(e){console.log(`Failed to save token file: ${e}`)}}function jt(t){let e=process.platform==="darwin"?"open":process.platform==="win32"?"start":"xdg-open";(0,nt.exec)(`${e} ${t}`,()=>{})}function Ft(t){return new Promise(e=>setTimeout(e,t))}async function Mt(){console.log(`
|
|
68
|
+
`+"=".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:Ie,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
69
|
Please visit: ${s}`),console.log(`And enter the code: ${n}`),console.log(`
|
|
63
|
-
Waiting for authorization (expires in ${r} seconds)...`),jt(s),console.log("(Browser opened automatically)");let c=Date.now()+r*1e3;for(;Date.now()<c;){await Ft(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:
|
|
64
|
-
Authorization expired. Please try again.`),null;if(
|
|
65
|
-
Authorization denied by user.`),null;if(
|
|
66
|
-
Error: ${
|
|
67
|
-
|
|
68
|
-
Authorization successful!`),
|
|
69
|
-
Authorization timed out. Please try again.`),null}async function $e(){let t=(process.env.GITHUB_TOKEN||"").trim();if(t)return console.log("Using GitHub token from GITHUB_TOKEN environment variable"),t;let e=
|
|
70
|
-
No GitHub token found. Starting GitHub Device Flow authentication...`);let o=await Mt();return o?(Ut(o),o):null}function
|
|
70
|
+
Waiting for authorization (expires in ${r} seconds)...`),jt(s),console.log("(Browser opened automatically)");let c=Date.now()+r*1e3;for(;Date.now()<c;){await Ft(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:Ie,device_code:o,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(`
|
|
71
|
+
Authorization expired. Please try again.`),null;if(u.error==="access_denied")return console.log(`
|
|
72
|
+
Authorization denied by user.`),null;if(u.error)return console.log(`
|
|
73
|
+
Error: ${u.error_description||u.error}`),null;if(u.access_token)return console.log(`
|
|
74
|
+
|
|
75
|
+
Authorization successful!`),u.access_token}return console.log(`
|
|
76
|
+
Authorization timed out. Please try again.`),null}async function $e(){let t=(process.env.GITHUB_TOKEN||"").trim();if(t)return console.log("Using GitHub token from GITHUB_TOKEN environment variable"),t;let e=It();if(e)return e;console.log(`
|
|
77
|
+
No GitHub token found. Starting GitHub Device Flow authentication...`);let o=await Mt();return o?(Ut(o),o):null}function I(){return _.accountType==="individual"?"https://api.githubcopilot.com":`https://api.${_.accountType}.githubcopilot.com`}function Ht(){return{"Content-Type":"application/json",Accept:"application/json",Authorization:`token ${_.githubToken}`,"Editor-Version":`vscode/${_.vscodeVersion}`,"Editor-Plugin-Version":_.editorPluginVersion,"User-Agent":_.userAgent,"X-GitHub-Api-Version":_.apiVersion,"X-VSCode-User-Agent-Library-Version":"electron-fetch"}}function te(t=!1){let e={Authorization:`Bearer ${_.copilotToken}`,"Content-Type":"application/json","Copilot-Integration-Id":"vscode-chat","Editor-Version":`vscode/${_.vscodeVersion}`,"Editor-Plugin-Version":_.editorPluginVersion,"User-Agent":_.userAgent,"OpenAI-Intent":"conversation-panel","X-GitHub-Api-Version":_.apiVersion,"X-Request-Id":crypto.randomUUID(),"X-VSCode-User-Agent-Library-Version":"electron-fetch"};return t&&(e["Copilot-Vision-Request"]="true"),e}async function me(){if(_.copilotToken&&Date.now()/1e3<_.tokenExpiresAt-60)return;console.log("Refreshing Copilot token...");let t=await fetch(`${et}/copilot_internal/v2/token`,{headers:Ht()});if(!t.ok)throw new Error(`Failed to get Copilot token: ${t.status} ${await t.text()}`);let e=await t.json();_.copilotToken=e.token,_.tokenExpiresAt=Date.now()/1e3+(e.refresh_in??1800),console.log("Copilot token refreshed successfully")}async function F(){(!_.copilotToken||Date.now()/1e3>=_.tokenExpiresAt-60)&&await me()}async function se(){await F();let t=await fetch(`${I()}/models`,{headers:te()});t.ok?(_.models=await t.json(),console.log(`Loaded ${_.models.data?.length??0} models`)):console.log(`Failed to fetch models: ${t.status}`)}function rt(t){return _.redirectAnthropic?!1:_.models?.data?.find(o=>o.id===t)?.supported_endpoints?.includes("/v1/messages")??!1}function it(t){return _.models?.data?.find(o=>o.id===t)?.supported_endpoints?.includes("/responses")??!1}function M(t){return Math.ceil(t.length/4)}var ne=v(require("fs")),Se=v(require("path"));function oe(t,e,o,n){let s=D();ne.default.mkdirSync(s,{recursive:!0});let r=Se.default.join(s,"error.log"),i={timestamp:new Date().toISOString(),endpoint:t,status_code:n,request:e,response:o};ne.default.appendFileSync(r,JSON.stringify(i)+`
|
|
71
78
|
`,"utf-8")}function be(t,e,o,n,s){try{let r=D();ne.default.mkdirSync(r,{recursive:!0});let i=Se.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};ne.default.appendFileSync(i,JSON.stringify(c)+`
|
|
72
79
|
`,"utf-8")}catch{}}function xe(t){try{let e=D();ne.default.mkdirSync(e,{recursive:!0});let o=Se.default.join(e,"tool_result_cleanup.jl");t.timestamp=new Date().toISOString(),ne.default.appendFileSync(o,JSON.stringify(t)+`
|
|
73
80
|
`,"utf-8")}catch{}}function Fe(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 Me(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 He(t,e){return t===400&&e.includes("tool_use_id")&&e.includes("tool_result")}function Be(){console.log(`
|
|
@@ -76,60 +83,60 @@ Exact Mappings:`);for(let[o,n]of t)console.log(` ${o} -> ${n}`)}else console.lo
|
|
|
76
83
|
Exact Mappings: (none)`);let e=Object.entries(G.prefixMappings);if(e.length){console.log(`
|
|
77
84
|
Prefix Mappings:`);for(let[o,n]of e)console.log(` ${o}* -> ${n}`)}else console.log(`
|
|
78
85
|
Prefix Mappings: (none)`);console.log("=".repeat(60)+`
|
|
79
|
-
`)}function ze(){if(!
|
|
80
|
-
`+"=".repeat(60)),console.log("Available Models"),console.log("=".repeat(60));for(let t of
|
|
86
|
+
`)}function ze(){if(!_.models?.data?.length){console.log("No models available yet.");return}console.log(`
|
|
87
|
+
`+"=".repeat(60)),console.log("Available Models"),console.log("=".repeat(60));for(let t of _.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(`
|
|
81
88
|
`+"=".repeat(60)+`
|
|
82
|
-
`)}var
|
|
89
|
+
`)}var ut=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 re(t){return t.clientDisconnected&&!t.streamCompleted?499:t.upstreamStatusCode}function ie(t){return G.translate(t)}function we(t){for(let e of _.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 Ve(t){for(let e of _.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 ct(t){let e=[],o=t.system;if(o){if(typeof o=="string")e.push({role:"system",content:we(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:we(r.join(`
|
|
83
90
|
|
|
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"),
|
|
91
|
+
`))})}}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=Ve(l)),e.push({role:"tool",tool_call_id:p.tool_use_id,content:l})}if(u.length){let p=Bt(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(`
|
|
85
92
|
|
|
86
|
-
`);a.length?e.push({role:"assistant",content:p||null,tool_calls:a.map(
|
|
93
|
+
`);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 n={model:ie(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 Bt(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
94
|
|
|
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 zt={stop:"end_turn",length:"max_tokens",tool_calls:"tool_use",content_filter:"refusal"};function Ge(t){return t?zt[t]??null:null}function We(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
|
|
89
|
-
`);C=
|
|
95
|
+
`):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 zt={stop:"end_turn",length:"max_tokens",tool_calls:"tool_use",content_filter:"refusal"};function Ge(t){return t?zt[t]??null:null}function We(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 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&&(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:Ge(o),stop_sequence:null,usage:{input_tokens:r,output_tokens:n.completion_tokens??0,...s?{cache_read_input_tokens:s}:{}}}}var ve=class{messageStartSent=!1;contentBlockIndex=0;contentBlockOpen=!1;toolCalls={}};function at(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:Ge(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 Ce(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 u of a.choices??[]){let p=u.delta;if(p?.content&&o.push(p.content),p?.tool_calls)for(let l of p.tool_calls){let d=l.index??0;n[d]||(n[d]={id:"",type:"function",function:{name:"",arguments:""}}),l.id&&(n[d].id=l.id),l.function?.name&&(n[d].function.name=l.function.name),l.function?.arguments&&(n[d].function.arguments+=l.function.arguments)}u.finish_reason&&(s=u.finish_reason)}}let i={role:"assistant",content:o.length?o.join(""):null},c=Object.keys(n).map(Number).sort((a,u)=>a-u);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 ce=(0,ut.Router)();ce.get(["/v1/models","/models"],async(t,e)=>{try{await F(),_.models||await se();let o=(_.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)})}});ce.get(["/v1/models/full/","/models/full/"],(t,e)=>{e.json(_.models)});ce.post(["/v1/chat/completions","/chat/completions"],async(t,e)=>{try{let o=Date.now();await F();let n=t.body,s=crypto.randomUUID(),r=n.model??"unknown",i=ie(r);i!==r&&(n={...n,model:i}),console.log(`[Chat Completions API] Using OpenAI Chat Completions path for: ${i}`);let c=(n.messages??[]).some(f=>Array.isArray(f.content)&&f.content.some(S=>S.type==="image_url")),a=(n.messages??[]).some(f=>f.role==="assistant"||f.role==="tool"),u=te(c);u["X-Initiator"]=a?"agent":"user";let p=JSON.stringify(n).length;if(n.stream)return Jt(e,n,u,s,p,o,r,i);let l=await lt(`${I()}/chat/completions`,{method:"POST",headers:u,body:JSON.stringify(n)},s,"/v1/chat/completions"),d=Math.round((Date.now()-o)/1e3*100)/100;if(!l)return e.status(504).json({error:`Upstream connection error after ${_.maxConnectionRetries+1} attempts`});let h=await l.text(),k=h.length;if(l.ok){let f=JSON.parse(h),S=f.usage??{};$.addRequest(s,{request_body:n,response_body:f,model:r,translated_model:i!==r?i:null,endpoint:"/v1/chat/completions",status_code:l.status,request_size:p,response_size:k,input_tokens:S.prompt_tokens??0,output_tokens:S.completion_tokens??0,duration:d}),e.json(f)}else oe("/v1/chat/completions",n,h,l.status),e.status(l.status).type("json").send(h)}catch(o){e.status(500).json({error:String(o)})}});async function Jt(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=[],u=0,p=0,l=200,d=!1,h=!1;t.on("close",()=>{d=!0});try{$.updateRequestState(n,"sending");let y=await fetch(`${I()}/chat/completions`,{method:"POST",headers:o,body:JSON.stringify(e),signal:AbortSignal.timeout(12e5)});l=y.status;let R=y.body.getReader(),b=new TextDecoder,C="";for($.updateRequestState(n,"receiving");;){let{done:A,value:j}=await R.read();if(A)break;if(d){R.cancel();break}C+=b.decode(j,{stream:!0});let N=C.split(`
|
|
96
|
+
`);C=N.pop();for(let O of N)if(O.trim()&&O.startsWith("data: ")){let z=O.slice(6);if(z==="[DONE]"){h=!0,d||t.write(`data: [DONE]
|
|
90
97
|
|
|
91
|
-
`);continue}try{let
|
|
98
|
+
`);continue}try{let L=JSON.parse(z);a.push(L),L.usage&&(u=L.usage.prompt_tokens??0,p=L.usage.completion_tokens??0),d||t.write(`data: ${z}
|
|
92
99
|
|
|
93
|
-
`)}catch{}}}}catch(
|
|
94
|
-
`);A=O.pop();for(let z of O){let
|
|
100
|
+
`)}catch{}}}}catch(y){l=504,console.log(`[Stream] Error for ${n}: ${y}`)}let k=re({clientDisconnected:d,streamCompleted:h,upstreamStatusCode:l});k===499?$.updateRequestState(n,"error",{status_code:499}):d||t.end();let f=Math.round((Date.now()-r)/1e3*100)/100,S=Ce(a);$.completeRequest(n,{request_body:e,response_body:S,model:i,translated_model:c!==i?c:null,endpoint:"/v1/chat/completions",status_code:k,request_size:s,response_size:a.reduce((y,R)=>y+JSON.stringify(R).length,0),input_tokens:u,output_tokens:p,duration:f})}ce.post(["/v1/responses","/responses"],async(t,e)=>{try{let o=Date.now();await F();let n=t.body,s=crypto.randomUUID(),r=n.model??"unknown",i=ie(r);if(i!==r&&(n={...n,model:i}),!it(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(h=>h.type==="custom"&&h.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}:h)}),Array.isArray(n.tools)){let h=new Set(["image_generation"]);n={...n,tools:n.tools.filter(k=>!(typeof k.type=="string"&&h.has(k.type)))}}if(Array.isArray(n.input)){let h=Wt(n.input);h!==n.input&&(n={...n,input:h})}n={...n,service_tier:null};let c=Xt(n.input),a=te(c);a["X-Initiator"]=Gt(n.input)?"agent":"user";let u=JSON.stringify(n).length;if(n.stream)return Vt(e,n,a,s,u,o,r,i);let p=await lt(`${I()}/responses`,{method:"POST",headers:a,body:JSON.stringify(n)},s,"/v1/responses"),l=Math.round((Date.now()-o)/1e3*100)/100;if(!p)return e.status(504).json({error:`Upstream connection error after ${_.maxConnectionRetries+1} attempts`});let d=await p.text();if(p.ok){let h=JSON.parse(d),k=h.usage??{};$.addRequest(s,{request_body:n,response_body:h,model:r,translated_model:i!==r?i:null,endpoint:"/v1/responses",status_code:p.status,request_size:u,response_size:d.length,input_tokens:k.input_tokens??0,output_tokens:k.output_tokens??0,duration:l}),e.json(h)}else oe("/v1/responses",n,d,p.status),e.status(p.status).type("json").send(d)}catch(o){e.status(500).json({error:String(o)})}});async function Vt(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,u=0,p=200,l={},d=[],h=!1,k=!1;t.on("close",()=>{h=!0});try{$.updateRequestState(n,"sending");let R=await fetch(`${I()}/responses`,{method:"POST",headers:o,body:JSON.stringify(e),signal:AbortSignal.timeout(12e5)});if(p=R.status,R.ok){let b=R.body.getReader(),C=new TextDecoder,A="";for($.updateRequestState(n,"receiving");;){let{done:j,value:N}=await b.read();if(j)break;if(h){b.cancel();break}h||t.write(N),A+=C.decode(N,{stream:!0});let O=A.split(`
|
|
101
|
+
`);A=O.pop();for(let z of O){let L=z.replace(/\r$/,"");if(!L.startsWith("data: "))continue;let W=L.slice(6);if(W!=="[DONE]")try{let T=JSON.parse(W);if(T.type==="response.completed"){k=!0;let J=T.response??{};l=J;let E=J.usage??{};a=E.input_tokens??0,u=E.output_tokens??0}else T.type==="response.output_text.delta"&&d.push(T.delta??"")}catch{}}}}else{let b=await R.text();oe("/v1/responses",e,b,p),h||t.write(`event: error
|
|
95
102
|
data: ${JSON.stringify({status:p,body:b})}
|
|
96
103
|
|
|
97
|
-
`)}}catch(
|
|
104
|
+
`)}}catch(R){p=504,console.log(`[Stream Responses] Error for ${n}: ${R}`)}let f=re({clientDisconnected:h,streamCompleted:k,upstreamStatusCode:p});f===499?$.updateRequestState(n,"error",{status_code:499}):h||t.end();let S=Math.round((Date.now()-r)/1e3*100)/100,y=Object.keys(l).length?l:null;!y&&d.length&&(y={output:[{type:"message",content:[{type:"output_text",text:d.join("")}]}]}),$.completeRequest(n,{request_body:e,response_body:y??{error:"Stream interrupted"},model:i,translated_model:c!==i?c:null,endpoint:"/v1/responses",status_code:f,request_size:s,response_size:JSON.stringify(y??{}).length,input_tokens:a,output_tokens:u,duration:S})}function Gt(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 Wt(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 Xt(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 lt(t,e,o,n){let s=_.maxConnectionRetries;for(let r=0;r<=s;r++)try{return await fetch(t,{...e,signal:AbortSignal.timeout(12e5)})}catch(i){let c=i;if(be(o,n,r,s,c),await F(),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(u=>setTimeout(u,a))}else console.log(`[${n}] Connection error (final attempt): ${c.message}`)}return null}var pt=require("express");var Ae=(0,pt.Router)(),Kt=new Set(["model","messages","max_tokens","system","metadata","stop_sequences","stream","temperature","top_p","top_k","tools","tool_choice","thinking","service_tier"]);function Yt(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 Qt(t){let e={};for(let[o,n]of Object.entries(t))Kt.has(o)&&(e[o]=n);return Yt(e),e}function Zt(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 en(t){let e=t.system;if(!e)return _.systemPromptAdd.length?{...t,system:_.systemPromptAdd.map(o=>({type:"text",text:o}))}:t;if(typeof e=="string"){let o=we(e);for(let n of _.systemPromptAdd)o.includes(n)||(o+=`
|
|
98
105
|
|
|
99
106
|
`+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
|
|
101
|
-
`);
|
|
107
|
+
`),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 u of _.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 _.systemPromptAdd)n.includes(i)||(r.push({type:"text",text:i}),s=!0);return s?{...t,system:r}:t}return t}function tn(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=Ve(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}Ae.post("/v1/messages/count_tokens",async(t,e)=>{try{await F();let o=t.body,n=o.model??"",s=_.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+=M(i);else if(Array.isArray(i))for(let c of i)c.type==="text"&&(r+=M(c.text??""));for(let c of o.messages??[]){let a=c.content;if(typeof a=="string")r+=M(a);else if(Array.isArray(a))for(let u of a)u.type==="text"?r+=M(u.text??""):u.type==="tool_result"&&typeof u.content=="string"?r+=M(u.content):u.type==="tool_use"&&(r+=M(JSON.stringify(u.input??{})))}if(o.tools?.length){r+=n.startsWith("claude")?346:n.startsWith("grok")?480:346;for(let c of o.tools)r+=M(c.name??""),r+=M(c.description??""),r+=M(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})}});Ae.post("/v1/messages",async(t,e)=>{let o=Date.now();await F();let n=t.body,s=crypto.randomUUID(),r=n,i=n.model??"unknown",c=ie(i);return c!==i&&(console.log(`[Anthropic API] Model name translated: ${i} -> ${c}`),n={...n,model:c}),n=en(n),n=tn(n),rt(c)?(console.log(`[Anthropic API] Using direct Anthropic API path for: ${c}`),nn(e,n,s,o,i,c,r)):(console.log(`[Anthropic API] Using OpenAI translation path for: ${c}`),sn(e,n,s,o,i,c,r))});async function nn(t,e,o,n,s,r,i){let c=JSON.stringify(e).length,a=dt(e),u=(e.messages??[]).some(h=>h.role==="assistant"),p=te(a);p["anthropic-version"]="2023-06-01",p["X-Initiator"]=u?"agent":"user";let l=e,d=null;for(let h=0;h<=3;h++){let k=Qt(l);if(k=Zt(k),k.stream)return on(t,k,p,o,l,c,n,s,r,i);let f=await gt(`${I()}/v1/messages`,{method:"POST",headers:p,body:JSON.stringify(k)},o,"/v1/messages");if(!f)return void t.status(504).json({type:"error",error:{type:"api_error",message:"Upstream connection error"}});let S=Math.round((Date.now()-n)/1e3*100)/100;if(f.ok){let R=await f.json(),b=R.usage;return $.addRequest(o,{original_request_body:i,request_body:l,response_body:R,model:s,translated_model:r!==s?r:null,endpoint:"/v1/messages",status_code:f.status,request_size:c,response_size:JSON.stringify(R).length,input_tokens:b?.input_tokens??0,output_tokens:b?.output_tokens??0,duration:S}),d&&xe({...d,modified_request:l,final_status_code:f.status,final_response:R}),void t.json(R)}let y=await f.text();if(oe("/v1/messages",l,y,f.status),He(f.status,y)){let R=Fe(y);if(R.length){console.log(`[Direct Anthropic] Attempt ${h+1}: orphaned IDs: ${R}`),d?d.orphaned_ids.push(...R):d={request_id:o,original_request:e,error_response:y,error_status_code:f.status,orphaned_ids:R},l={...l,messages:Me(l.messages,R)};continue}}return d&&xe({...d,modified_request:l,final_status_code:f.status,final_response:y}),void t.status(f.status).type("json").send(y)}}async function on(t,e,o,n,s,r,i,c,a,u){$.startRequest(n,{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,d=200,h=c,k=[],f=!1,S=!1;t.on("close",()=>{f=!0});try{$.updateRequestState(n,"sending");let C=await fetch(`${I()}/v1/messages`,{method:"POST",headers:o,body:JSON.stringify(e),signal:AbortSignal.timeout(12e5)});d=C.status,$.updateRequestState(n,"receiving");let A=C.body.getReader(),j=new TextDecoder,N="",O="";for(;;){let{done:z,value:L}=await A.read();if(z)break;if(f){A.cancel();break}N+=j.decode(L,{stream:!0});let W=N.split(`
|
|
108
|
+
`);N=W.pop();for(let T of W){if(!T.trim())continue;if(T.startsWith("event: ")){O=T.slice(7);continue}if(!T.startsWith("data: "))continue;let J=T.slice(6);if(J==="[DONE]"){S=!0;break}try{let E=JSON.parse(J),Z=O||E.type||"";if(O="",Z==="message_start"){let ue=E.message??{};h=ue.model??c,p=ue.usage?.input_tokens??0}else Z==="message_delta"?l=E.usage?.output_tokens??0:Z==="message_stop"?S=!0:Z==="content_block_delta"&&E.delta?.type==="text_delta"&&k.push(E.delta.text??"");f||t.write(`event: ${Z}
|
|
102
109
|
data: ${J}
|
|
103
110
|
|
|
104
|
-
`)}catch{}}}}catch(C){d=504,console.log(`[Stream Direct Anthropic] Error for ${n}: ${C}`)}let
|
|
105
|
-
`);O=W.pop();for(let T of W){if(!T.trim()||!T.startsWith("data: "))continue;let J=T.slice(6);if(J==="[DONE]"){S=!0;break}try{let E=JSON.parse(J);
|
|
106
|
-
data: ${JSON.stringify(
|
|
111
|
+
`)}catch{}}}}catch(C){d=504,console.log(`[Stream Direct Anthropic] Error for ${n}: ${C}`)}let y=re({clientDisconnected:f,streamCompleted:S,upstreamStatusCode:d});y===499?$.updateRequestState(n,"error",{status_code:499}):f||t.end();let R=Math.round((Date.now()-i)/1e3*100)/100,b=k.length?{id:n,type:"message",role:"assistant",content:[{type:"text",text:k.join("")}],model:h,usage:{input_tokens:p,output_tokens:l}}:{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:y,request_size:r,response_size:JSON.stringify(b).length,input_tokens:p,output_tokens:l,duration:R})}async function sn(t,e,o,n,s,r,i){let c=dt(e),a=JSON.stringify(e).length,u=e,p=null;for(let l=0;l<=3;l++){let d=ct(u),h=(d.messages??[]).some(R=>R.role==="assistant"||R.role==="tool"),k=te(c);if(k["X-Initiator"]=h?"agent":"user",e.stream)return rn(t,d,k,o,u,a,n,s,r,i);let f=await gt(`${I()}/chat/completions`,{method:"POST",headers:k,body:JSON.stringify(d)},o,"/v1/messages (translated)");if(!f)return void t.status(504).json({type:"error",error:{type:"api_error",message:"Upstream connection error"}});let S=Math.round((Date.now()-n)/1e3*100)/100;if(f.ok){let R=await f.json(),b=We(R),C=R.usage;return $.addRequest(o,{original_request_body:i,request_body:u,response_body:b,model:s,translated_model:r!==s?r:null,endpoint:"/v1/messages",status_code:f.status,request_size:a,response_size:JSON.stringify(b).length,input_tokens:C?.prompt_tokens??0,output_tokens:C?.completion_tokens??0,duration:S}),void t.json(b)}let y=await f.text();if(oe("/v1/messages",u,y,f.status),He(f.status,y)){let R=Fe(y);if(R.length){p?p.orphaned_ids.push(...R):p={request_id:o,original_request:e,error_response:y,error_status_code:f.status,orphaned_ids:R},u={...u,messages:Me(u.messages,R)};continue}}return p&&xe({...p,modified_request:u,final_status_code:f.status,final_response:y}),void t.status(f.status).type("json").send(y)}}async function rn(t,e,o,n,s,r,i,c,a,u){$.startRequest(n,{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 ve,l=[],d=0,h=0,k=200,f=!1,S=!1;t.on("close",()=>{f=!0});try{$.updateRequestState(n,"sending");let A=await fetch(`${I()}/chat/completions`,{method:"POST",headers:o,body:JSON.stringify(e),signal:AbortSignal.timeout(12e5)});k=A.status,$.updateRequestState(n,"receiving");let j=A.body.getReader(),N=new TextDecoder,O="";for(;;){let{done:z,value:L}=await j.read();if(z)break;if(f){j.cancel();break}O+=N.decode(L,{stream:!0});let W=O.split(`
|
|
112
|
+
`);O=W.pop();for(let T of W){if(!T.trim()||!T.startsWith("data: "))continue;let J=T.slice(6);if(J==="[DONE]"){S=!0;break}try{let E=JSON.parse(J);l.push(E),E.usage&&(d=E.usage.prompt_tokens??0,h=E.usage.completion_tokens??0);let Z=at(E,p);for(let ue of Z)f||t.write(`event: ${ue.type}
|
|
113
|
+
data: ${JSON.stringify(ue)}
|
|
107
114
|
|
|
108
|
-
`)}catch{}}}}catch(A){k=504,console.log(`[Stream Anthropic] Error for ${n}: ${A}`)}let
|
|
115
|
+
`)}catch{}}}}catch(A){k=504,console.log(`[Stream Anthropic] Error for ${n}: ${A}`)}let y=re({clientDisconnected:f,streamCompleted:S,upstreamStatusCode:k});y===499?$.updateRequestState(n,"error",{status_code:499}):f||t.end();let R=Math.round((Date.now()-i)/1e3*100)/100,b=Ce(l),C=Object.keys(b).length?We(b):{error:{type:"api_error",message:"Stream interrupted"}};$.completeRequest(n,{request_body:s,response_body:C,model:c,translated_model:a!==c?a:null,endpoint:"/v1/messages",status_code:y,request_size:r,response_size:l.reduce((A,j)=>A+JSON.stringify(j).length,0),input_tokens:d,output_tokens:h,duration:R})}function dt(t){return(t.messages??[]).some(e=>Array.isArray(e.content)&&e.content.some(o=>o.type==="image"))}async function gt(t,e,o,n){let s=_.maxConnectionRetries;for(let r=0;r<=s;r++)try{return await fetch(t,{...e,signal:AbortSignal.timeout(12e5)})}catch(i){let c=i;be(o,n,r,s,c),await F(),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 ft=require("express"),_e=v(require("path")),mt=v(require("fs"));var P=(0,ft.Router)();function _t(){let t=_e.default.join(__dirname,"..","public");return mt.default.existsSync(t)?t:_e.default.join(__dirname,"..","..","public")}P.get("/",(t,e)=>{e.sendFile(_e.default.join(_t(),"dashboard.html"))});P.get("/requests",(t,e)=>{e.sendFile(_e.default.join(_t(),"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(u=>({...u,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(u=>({...u,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
116
|
`);e.end()});P.post("/api/requests/import",(t,e)=>{let n=(typeof t.body=="string"?t.body:JSON.stringify(t.body)).split(`
|
|
110
117
|
`).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 w=v(require("fs")),B=v(require("path")),yt=v(require("readline"));var U="\x1B[1m",x="\x1B[2m",g="\x1B[0m",Q="\x1B[36m",q="\x1B[32m",ae="\x1B[33m",K="\x1B[35m",Oe=ee;function cn(){console.log(`
|
|
111
118
|
${K}\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
119
|
${K}\u2551${g} ${U}ghc-tunnel Setup Wizard${g} ${K}\u2551${g}
|
|
113
120
|
${K}\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
121
|
`)}function Xe(){return yt.default.createInterface({input:process.stdin,output:process.stdout})}function ye(t,e){return new Promise(o=>t.question(e,o))}async function Rt(t={}){cn(),console.log(`${U}Step 1: GitHub Authentication${g}`),console.log(`${x}Checking for GitHub token...${g}
|
|
115
|
-
`);let e=await $e();e||(console.error("Failed to get GitHub token. Cannot continue setup."),process.exit(1)),
|
|
122
|
+
`);let e=await $e();e||(console.error("Failed to get GitHub token. Cannot continue setup."),process.exit(1)),_.githubToken=e,await me(),await se();let o=_.models?.data??[];o.length||(console.error("No models available. Check your Copilot subscription."),process.exit(1)),console.log(`
|
|
116
123
|
${U}Step 2: Configure Models${g}
|
|
117
124
|
`),console.log(`${x}Available Copilot models:${g}
|
|
118
|
-
`);for(let f=0;f<o.length;f++){let S=o[f],
|
|
125
|
+
`);for(let f=0;f<o.length;f++){let S=o[f],y=[];S.capabilities?.supports?.vision&&y.push("Vision"),S.capabilities?.supports?.tool_calls&&y.push("Tools"),S.supported_endpoints?.includes("/v1/messages")&&y.push("Anthropic"),S.preview&&y.push("Preview");let R=y.length?` ${x}(${y.join(", ")})${g}`:"";console.log(` ${Q}${String(f+1).padStart(2)}.${g} ${S.id}${R}`)}let n=t.useDefaults?void 0:Xe();console.log(`
|
|
119
126
|
${ae}Pick models by number. Press Enter to accept the default.${g}
|
|
120
|
-
`);let s=an(o,[
|
|
121
|
-
${q}Primary: ${i.id}${g}`),console.log(`${q}Small: ${
|
|
127
|
+
`);let s=an(o,[m,"claude-opus-4.8","claude-opus-4.7-1m","claude-opus-4.7","claude-opus-4.6-1m","claude-opus-4.6"])??0,r=t.useDefaults?s:await ht(n,o,'Primary model (mapped to "opus", "sonnet" aliases)',s),i=o[r],c=kt(o,X)??0,a=t.useDefaults?c:await ht(n,o,'Small/fast model (mapped to "haiku" alias)',c),u=o[a];console.log(`
|
|
128
|
+
${q}Primary: ${i.id}${g}`),console.log(`${q}Small: ${u.id}${g}
|
|
122
129
|
`),console.log(`${U}Step 3: Server Settings${g}
|
|
123
|
-
`);let p=t.useDefaults?"":await ye(n,` Port ${x}[8314]${g}: `),
|
|
130
|
+
`);let p=t.useDefaults?"":await ye(n,` Port ${x}[8314]${g}: `),l=p.trim()?parseInt(p.trim(),10):8314,d=Oe;console.log(`${q}Host: ${d}${g}
|
|
124
131
|
`),console.log(`
|
|
125
132
|
${U}Step 4: Claude Code Model Settings${g}`),console.log(`${x}Using the same Claude Code model identifier for both primary and fallback model settings.${g}
|
|
126
133
|
`),console.log(`${q}ANTHROPIC_MODEL: ${V}${g}`),console.log(`${q}ANTHROPIC_DEFAULT_HAIKU_MODEL: ${V}${g}
|
|
127
134
|
`),console.log(`${U}Step 4b: Codex Model${g}
|
|
128
|
-
`);let{model:
|
|
135
|
+
`);let{model:h,reasoning:k}=t.useDefaults?{model:Te,reasoning:Ee}:await wt(n,{model:Te,reasoning:Ee});return console.log(`${q}Codex model: ${h} (reasoning: ${k})${g}
|
|
129
136
|
`),n?.close(),console.log(`${U}Step 5: Saving Configuration${g}
|
|
130
|
-
`),
|
|
137
|
+
`),un(i.id,u.id,l,d),$t(l,d),dn(l,d,h,k),console.log(`
|
|
131
138
|
${q}${U}Setup complete!${g}
|
|
132
|
-
`),{port:
|
|
139
|
+
`),{port:l,host:d}}async function ht(t,e,o,n){let s=e[n]?.id??"?";for(;;){let r=await ye(t,` ${o} ${x}[${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(` ${ae}Invalid choice. Enter a number 1-${e.length} or a model name.${g}`)}}function kt(t,e){let o=t.findIndex(n=>n.id===e);return o>=0?o:void 0}function an(t,e){for(let o of e){let n=kt(t,o);if(n!==void 0)return n}}function un(t,e,o,n){let s=D();w.default.mkdirSync(s,{recursive:!0});let r=B.default.join(s,"config.yaml"),i=`# ghc-tunnel Configuration (generated by --setup)
|
|
133
140
|
# ================================================
|
|
134
141
|
|
|
135
142
|
address: ${n}
|
|
@@ -145,27 +152,35 @@ copilot_version: "${pe}"
|
|
|
145
152
|
model_mappings:
|
|
146
153
|
exact:
|
|
147
154
|
opus: ${t}
|
|
148
|
-
sonnet:
|
|
155
|
+
sonnet: ${t}
|
|
156
|
+
opus4-8: ${t}
|
|
149
157
|
opus4-7: ${t}
|
|
158
|
+
"4-8[1m]": ${t}
|
|
150
159
|
"4-7[1m]": ${t}
|
|
151
160
|
haiku: ${e}
|
|
152
161
|
prefix:
|
|
153
|
-
claude-
|
|
154
|
-
claude-opus-4.
|
|
155
|
-
claude-opus-4.
|
|
156
|
-
claude-opus-4
|
|
157
|
-
claude-opus-4
|
|
158
|
-
claude-opus-4-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
"claude-opus-4
|
|
163
|
-
"claude-opus-4
|
|
164
|
-
"claude-opus-4
|
|
165
|
-
"claude-opus-4
|
|
166
|
-
claude-
|
|
167
|
-
claude-
|
|
168
|
-
claude-
|
|
162
|
+
claude-sonnet-4-: ${t}
|
|
163
|
+
claude-opus-4.5-: ${t}
|
|
164
|
+
claude-opus-4.6-: ${t}
|
|
165
|
+
claude-opus-4.7-: ${t}
|
|
166
|
+
claude-opus-4.8-: ${t}
|
|
167
|
+
claude-opus-4-5-: ${t}
|
|
168
|
+
claude-opus-4-6-: ${t}
|
|
169
|
+
claude-opus-4-7-: ${t}
|
|
170
|
+
claude-opus-4-8-: ${t}
|
|
171
|
+
"claude-opus-4.5": ${t}
|
|
172
|
+
"claude-opus-4.6": ${t}
|
|
173
|
+
"claude-opus-4.7": ${t}
|
|
174
|
+
"claude-opus-4.8": ${t}
|
|
175
|
+
"claude-opus-4-6": ${t}
|
|
176
|
+
"claude-opus-4-7": ${t}
|
|
177
|
+
"claude-opus-4-8": ${t}
|
|
178
|
+
"claude-opus-4-6[1m]": ${t}
|
|
179
|
+
"claude-opus-4-7[1m]": ${t}
|
|
180
|
+
"claude-opus-4-8[1m]": ${t}
|
|
181
|
+
claude-sonnet-4-7: ${t}
|
|
182
|
+
claude-sonnet-4-6: ${t}
|
|
183
|
+
claude-sonnet-4-5: ${t}
|
|
169
184
|
claude-haiku-4.5-: ${e}
|
|
170
185
|
claude-haiku-4-5-: ${e}
|
|
171
186
|
|
|
@@ -180,35 +195,35 @@ max_connection_retries: 3
|
|
|
180
195
|
`;return`${s}${a}${i}
|
|
181
196
|
${r}`}function xt(t,e,o,n){let s=e.replace(/[.[\]]/g,a=>`\\${a}`),r=new RegExp(`(^|\\n)\\[${s}\\][^\\[]*?(?=\\n\\[|$)`),i=`[${e}]
|
|
182
197
|
${o.trim()}
|
|
183
|
-
`;if(r.test(t))return t.replace(r,(a,
|
|
198
|
+
`;if(r.test(t))return t.replace(r,(a,u)=>`${u}${i}`);if(n){let a=n.replace(/\./g,"\\."),u=new RegExp(`\\[${a}[^\\]]*\\][^\\[]*`,"g"),p=-1,l;for(;(l=u.exec(t))!==null;)p=l.index+l[0].length;if(p!==-1){let d=t.slice(0,p),h=t.slice(p),k=d.endsWith(`
|
|
184
199
|
|
|
185
200
|
`)?"":d.endsWith(`
|
|
186
201
|
`)?`
|
|
187
202
|
`:`
|
|
188
203
|
|
|
189
|
-
`,f=
|
|
204
|
+
`,f=h.startsWith(`
|
|
190
205
|
`)?"":`
|
|
191
|
-
`;return`${d}${k}${i}${f}${
|
|
206
|
+
`;return`${d}${k}${i}${f}${h}`}}let c=t.length===0||t.endsWith(`
|
|
192
207
|
|
|
193
208
|
`)?"":t.endsWith(`
|
|
194
209
|
`)?`
|
|
195
210
|
`:`
|
|
196
211
|
|
|
197
|
-
`;return`${t}${c}${i}`}function he(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
|
|
198
|
-
`);
|
|
199
|
-
${ae}Codex config is incomplete (${d.join(", ")}). Filling in:${g}`);let
|
|
200
|
-
`);
|
|
212
|
+
`;return`${t}${c}${i}`}function he(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 ln(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 pn(t){let e=ln(t,`model_providers.${H}`);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 wt(t,e){let o=(await ye(t,` Codex model ${x}[${e.model}]${g}: `)).trim(),n=(await ye(t,` Reasoning effort ${x}[${e.reasoning}] (low | medium | high | xhigh)${g}: `)).trim();return{model:o||e.model,reasoning:n||e.reasoning}}function dn(t,e,o=Te,n=Ee){let s=B.default.join(process.env.HOME||"~",".codex"),r=B.default.join(s,"config.toml"),i=`http://${e}:${t}/v1`,c="";if(w.default.existsSync(r))try{c=w.default.readFileSync(r,"utf-8")}catch{console.log(` ${ae}\u26A0${g} Could not read existing config.toml, creating new one`)}let a=he(c,"model_provider"),u=c;u=Y(u,"model",JSON.stringify(o)),u=Y(u,"model_reasoning_effort",JSON.stringify(n)),u=Y(u,"personality",JSON.stringify(bt)),u=Y(u,"model_provider",JSON.stringify(H));let p=[`name = ${JSON.stringify(St)}`,`base_url = ${JSON.stringify(i)}`,'wire_api = "responses"'].join(`
|
|
213
|
+
`);u=xt(u,`model_providers.${H}`,p,"model_providers."),w.default.mkdirSync(s,{recursive:!0}),w.default.writeFileSync(r,u,"utf-8");let l=a&&a!==H?` ${x}(was '${a}')${g}`:"";console.log(` ${q}\u2713${g} Codex config updated at ${Q}${r}${g}${l}`),console.log(` ${x}model = ${o}, reasoning = ${n}${g}`),console.log(` ${x}model_provider = ${H} (base_url = ${i})${g}`)}async function vt(t,e,o={}){let n=B.default.join(process.env.HOME||"~",".codex"),s=B.default.join(n,"config.toml"),r=`http://${e}:${t}/v1`,i="";if(w.default.existsSync(s))try{i=w.default.readFileSync(s,"utf-8")}catch{return}let c=he(i,"model"),a=he(i,"model_reasoning_effort"),u=he(i,"personality"),p=he(i,"model_provider"),l=pn(i),d=[];if(c||d.push("model"),a||d.push("model_reasoning_effort"),u||d.push("personality"),p!==H&&d.push("model_provider"),l||d.push(`[model_providers.${H}]`),d.length===0)return;console.log(`
|
|
214
|
+
${ae}Codex config is incomplete (${d.join(", ")}). Filling in:${g}`);let h=c||Te,k=a||Ee,f=!c,S=!a;if((f||S)&&process.stdin.isTTY&&!o.useDefaults){let R=Xe();try{let b=await wt(R,{model:h,reasoning:k});f&&(h=b.model),S&&(k=b.reasoning)}finally{R.close()}}let y=i;if(c||(y=Y(y,"model",JSON.stringify(h))),a||(y=Y(y,"model_reasoning_effort",JSON.stringify(k))),u||(y=Y(y,"personality",JSON.stringify(bt))),p!==H&&(y=Y(y,"model_provider",JSON.stringify(H))),!l){let R=[`name = ${JSON.stringify(St)}`,`base_url = ${JSON.stringify(r)}`,'wire_api = "responses"'].join(`
|
|
215
|
+
`);y=xt(y,`model_providers.${H}`,R,"model_providers.")}try{w.default.mkdirSync(n,{recursive:!0}),w.default.writeFileSync(s,y,"utf-8"),console.log(` ${q}\u2713${g} Codex config patched at ${Q}${s}${g}`)}catch(R){console.log(` ${ae}\u26A0${g} Could not write Codex config: ${R}`)}}async function Ct(t={}){console.log(`
|
|
201
216
|
${K}\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
217
|
${K}\u2551${g} ${U}Claude Code Settings${g} ${K}\u2551${g}
|
|
203
218
|
${K}\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
219
|
`);let e=B.default.join(process.env.HOME||"~",".claude"),o=B.default.join(e,"settings.json"),n={},s={};if(w.default.existsSync(o))try{n=JSON.parse(w.default.readFileSync(o,"utf-8")),s=n.env??{}}catch{}(s.ANTHROPIC_BASE_URL||s.ANTHROPIC_MODEL||s.ANTHROPIC_DEFAULT_HAIKU_MODEL)&&(console.log(`${U}Current configuration:${g}`),s.ANTHROPIC_BASE_URL&&console.log(` ANTHROPIC_BASE_URL: ${Q}${s.ANTHROPIC_BASE_URL}${g}`),s.ANTHROPIC_MODEL&&console.log(` ANTHROPIC_MODEL: ${Q}${s.ANTHROPIC_MODEL}${g}`),s.ANTHROPIC_DEFAULT_HAIKU_MODEL&&console.log(` ANTHROPIC_DEFAULT_HAIKU_MODEL: ${Q}${s.ANTHROPIC_DEFAULT_HAIKU_MODEL}${g}`),console.log(`
|
|
205
220
|
${x}Press Enter to keep the current port. Host and model identifiers will be reset to ${Oe} and ${V}.${g}
|
|
206
|
-
`));let i=8314,c=i;if(s.ANTHROPIC_BASE_URL)try{let
|
|
221
|
+
`));let i=8314,c=i;if(s.ANTHROPIC_BASE_URL)try{let l=new URL(s.ANTHROPIC_BASE_URL);c=parseInt(l.port,10)||i}catch{}let a=t.useDefaults?void 0:Xe(),u=t.useDefaults?"":await ye(a,` Port ${x}[${c}]${g}: `),p=u.trim()?parseInt(u.trim(),10):c;a?.close(),console.log(`
|
|
207
222
|
${x}Writing ANTHROPIC_BASE_URL as http://${Oe}:${p}/ and both model identifiers as ${V}.${g}
|
|
208
223
|
`),$t(p,Oe),console.log(`
|
|
209
224
|
${q}${U}Claude Code settings updated!${g}
|
|
210
225
|
`)}var At="1.0.6";function gn(){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==="-d"||n==="--default"?e.defaults=!0:n==="--claudecode"?e.claudecode=!0:n==="-v"||n==="--version"?e.version=!0:(n==="-h"||n==="--help")&&(e.help=!0)}return e}function Ot(t,e){let o=(0,De.default)();o.use(De.default.json({limit:"50mb"})),o.use(De.default.text({limit:"50mb"})),o.use(Ae),o.use(ce),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 Tt(t,e){let o=D(),n=Pe.default.join(o,"config.yaml");Re.default.existsSync(n)||(console.log(`No config file found at ${n}, generating one.`),Ue());try{let s=tt(n);t=s.address??t,e=s.port??e,s.account_type&&(
|
|
226
|
+
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 Tt(t,e){let o=D(),n=Pe.default.join(o,"config.yaml");Re.default.existsSync(n)||(console.log(`No config file found at ${n}, generating one.`),Ue());try{let s=tt(n);t=s.address??t,e=s.port??e,s.account_type&&(_.accountType=s.account_type),s.vscode_version&&(_.vscodeVersion=s.vscode_version),s.api_version&&(_.apiVersion=s.api_version),s.copilot_version&&(_.copilotVersion=s.copilot_version),s.system_prompt_remove&&(_.systemPromptRemove=s.system_prompt_remove),s.system_prompt_add&&(_.systemPromptAdd=s.system_prompt_add),s.tool_result_suffix_remove&&(_.toolResultSuffixRemove=s.tool_result_suffix_remove),s.max_connection_retries!=null&&(_.maxConnectionRetries=s.max_connection_retries),s.model_mappings?G.loadFromConfig(s):G.loadFromConfig({model_mappings:Le}),console.log(`Loaded configuration from: ${n}`)}catch(s){console.log(`Error loading config: ${s}`),G.loadFromConfig({model_mappings:Le})}return{host:t,port:e}}function fn(){let t=D(),e=Pe.default.join(t,"config.yaml");if(!Re.default.existsSync(e))return!0;let o=Pe.default.join(process.env.HOME||"~",".claude","settings.json");if(!Re.default.existsSync(o))return!0;try{let s=JSON.parse(Re.default.readFileSync(o,"utf-8"))?.env;if(!s?.ANTHROPIC_BASE_URL||!mn(s.ANTHROPIC_BASE_URL))return!0}catch{return!0}return!1}function mn(t){try{let e=new URL(t);return e.hostname==="localhost"||e.hostname===ee}catch{return!1}}async function _n(){let t=gn();if(t.help&&(console.log(`ghc-tunnel v${At} \u2013 GitHub Copilot API Proxy
|
|
212
227
|
|
|
213
228
|
Usage: ghc-tunnel [options]
|
|
214
229
|
|
|
@@ -222,4 +237,4 @@ Options:
|
|
|
222
237
|
-v, --version Show version
|
|
223
238
|
-h, --help Show this help`),process.exit(0)),t.version&&(console.log(`ghc-tunnel ${At}`),process.exit(0)),t.config&&(Ue(),process.exit(0)),t.setup&&t.claudecode&&(await Ct({useDefaults:t.defaults}),process.exit(0)),t.setup||fn()){t.setup||console.log(`First run detected \u2014 launching setup wizard.
|
|
224
239
|
`);let{port:r,host:i}=await Rt({useDefaults:t.defaults}),{host:c,port:a}=Tt(i,r);ze(),Be(),Ot(c,a);return}let{host:o,port:n}=Tt(Ze,Qe);t.address!=null&&(o=t.address),t.port!=null&&(n=t.port);let s=await $e();s||(console.error(`
|
|
225
|
-
`+"=".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 "+D()),console.error(" 3. Run again for interactive Device Flow auth"),console.error("=".repeat(60)),process.exit(1)),
|
|
240
|
+
`+"=".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 "+D()),console.error(" 3. Run again for interactive Device Flow auth"),console.error("=".repeat(60)),process.exit(1)),_.githubToken=s,await me(),await se(),await vt(n,o,{useDefaults:t.defaults}),ze(),Be(),Ot(o,n)}_n().catch(t=>{console.error("Fatal error:",t),process.exit(1)});
|
package/package.json
CHANGED
package/public/dashboard.html
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>GHC
|
|
6
|
+
<title>GHC Tunnel - Dashboard</title>
|
|
7
7
|
<style>
|
|
8
8
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
9
9
|
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f5f5f5; color: #333; line-height: 1.6; }
|