@@ -288,6 +288,33 @@ if (!proxyAgent) {
288288 logRequest ( 'warn' , 'startup' , { message : 'No HTTPS_PROXY configured, requests will go direct' } ) ;
289289}
290290
291+ /**
292+ * Resolves the OpenCode routing configuration based on available credentials.
293+ * Priority: OPENAI_API_KEY > ANTHROPIC_API_KEY > copilotToken (COPILOT_GITHUB_TOKEN / COPILOT_API_KEY)
294+ *
295+ * @param {string|undefined } openaiKey
296+ * @param {string|undefined } anthropicKey
297+ * @param {string|undefined } copilotToken
298+ * @param {string } openaiTarget
299+ * @param {string } anthropicTarget
300+ * @param {string } copilotTarget
301+ * @param {string } [openaiBasePath]
302+ * @param {string } [anthropicBasePath]
303+ * @returns {{ target: string, headers: Record<string,string>, basePath: string|undefined } | null }
304+ */
305+ function resolveOpenCodeRoute ( openaiKey , anthropicKey , copilotToken , openaiTarget , anthropicTarget , copilotTarget , openaiBasePath , anthropicBasePath ) {
306+ if ( openaiKey ) {
307+ return { target : openaiTarget , headers : { 'Authorization' : `Bearer ${ openaiKey } ` } , basePath : openaiBasePath } ;
308+ }
309+ if ( anthropicKey ) {
310+ return { target : anthropicTarget , headers : { 'x-api-key' : anthropicKey } , basePath : anthropicBasePath } ;
311+ }
312+ if ( copilotToken ) {
313+ return { target : copilotTarget , headers : { 'Authorization' : `Bearer ${ copilotToken } ` } , basePath : undefined } ;
314+ }
315+ return null ;
316+ }
317+
291318/**
292319 * Check rate limit and send 429 if exceeded.
293320 * Returns true if request was rate-limited (caller should return early).
@@ -1052,52 +1079,44 @@ if (require.main === module) {
10521079 url : logUrl ,
10531080 } ) ;
10541081
1055- if ( OPENAI_API_KEY ) {
1056- logRequest ( 'info' , 'opencode_proxy_header_injection' , {
1057- message : '[OpenCode Proxy] Routing to OpenAI/Copilot via OPENAI_API_KEY' ,
1058- target : OPENAI_API_TARGET ,
1059- } ) ;
1060- proxyRequest ( req , res , OPENAI_API_TARGET , {
1061- 'Authorization' : `Bearer ${ OPENAI_API_KEY } ` ,
1062- } , 'opencode' , OPENAI_API_BASE_PATH ) ;
1063- } else if ( ANTHROPIC_API_KEY ) {
1064- logRequest ( 'info' , 'opencode_proxy_header_injection' , {
1065- message : '[OpenCode Proxy] Routing to Anthropic via ANTHROPIC_API_KEY' ,
1066- target : ANTHROPIC_API_TARGET ,
1067- } ) ;
1068- const anthropicHeaders = { 'x-api-key' : ANTHROPIC_API_KEY } ;
1069- if ( ! req . headers [ 'anthropic-version' ] ) {
1070- anthropicHeaders [ 'anthropic-version' ] = '2023-06-01' ;
1071- }
1072- proxyRequest ( req , res , ANTHROPIC_API_TARGET , anthropicHeaders , 'opencode' , ANTHROPIC_API_BASE_PATH ) ;
1073- } else {
1074- // COPILOT_AUTH_TOKEN only — route to Copilot API target
1075- logRequest ( 'info' , 'opencode_proxy_header_injection' , {
1076- message : '[OpenCode Proxy] Routing to Copilot via COPILOT_AUTH_TOKEN' ,
1077- target : COPILOT_API_TARGET ,
1078- } ) ;
1079- proxyRequest ( req , res , COPILOT_API_TARGET , {
1080- 'Authorization' : `Bearer ${ COPILOT_AUTH_TOKEN } ` ,
1081- } , 'opencode' ) ;
1082+ const parsedContentLength = Number ( req . headers [ 'content-length' ] ) ;
1083+ const contentLength = Number . isFinite ( parsedContentLength ) && parsedContentLength > 0 ? parsedContentLength : 0 ;
1084+ if ( checkRateLimit ( req , res , 'opencode' , contentLength ) ) {
1085+ return ;
10821086 }
1087+
1088+ const route = resolveOpenCodeRoute (
1089+ OPENAI_API_KEY , ANTHROPIC_API_KEY , COPILOT_AUTH_TOKEN ,
1090+ OPENAI_API_TARGET , ANTHROPIC_API_TARGET , COPILOT_API_TARGET ,
1091+ OPENAI_API_BASE_PATH , ANTHROPIC_API_BASE_PATH
1092+ ) ;
1093+ if ( ! route ) return ;
1094+
1095+ logRequest ( 'info' , 'opencode_proxy_header_injection' , {
1096+ message : `[OpenCode Proxy] Routing to ${ route . target } ` ,
1097+ target : route . target ,
1098+ } ) ;
1099+
1100+ const headers = Object . assign ( { } , route . headers ) ;
1101+ if ( ANTHROPIC_API_KEY && ! OPENAI_API_KEY && ! req . headers [ 'anthropic-version' ] ) {
1102+ headers [ 'anthropic-version' ] = '2023-06-01' ;
1103+ }
1104+ proxyRequest ( req , res , route . target , headers , 'opencode' , route . basePath ) ;
10831105 } ) ;
10841106
10851107 opencodeServer . on ( 'upgrade' , ( req , socket , head ) => {
1086- if ( OPENAI_API_KEY ) {
1087- proxyWebSocket ( req , socket , head , OPENAI_API_TARGET , {
1088- 'Authorization' : `Bearer ${ OPENAI_API_KEY } ` ,
1089- } , 'opencode' , OPENAI_API_BASE_PATH ) ;
1090- } else if ( ANTHROPIC_API_KEY ) {
1091- const anthropicHeaders = { 'x-api-key' : ANTHROPIC_API_KEY } ;
1092- if ( ! req . headers [ 'anthropic-version' ] ) {
1093- anthropicHeaders [ 'anthropic-version' ] = '2023-06-01' ;
1094- }
1095- proxyWebSocket ( req , socket , head , ANTHROPIC_API_TARGET , anthropicHeaders , 'opencode' , ANTHROPIC_API_BASE_PATH ) ;
1096- } else {
1097- proxyWebSocket ( req , socket , head , COPILOT_API_TARGET , {
1098- 'Authorization' : `Bearer ${ COPILOT_AUTH_TOKEN } ` ,
1099- } , 'opencode' ) ;
1108+ const route = resolveOpenCodeRoute (
1109+ OPENAI_API_KEY , ANTHROPIC_API_KEY , COPILOT_AUTH_TOKEN ,
1110+ OPENAI_API_TARGET , ANTHROPIC_API_TARGET , COPILOT_API_TARGET ,
1111+ OPENAI_API_BASE_PATH , ANTHROPIC_API_BASE_PATH
1112+ ) ;
1113+ if ( ! route ) return ;
1114+
1115+ const headers = Object . assign ( { } , route . headers ) ;
1116+ if ( ANTHROPIC_API_KEY && ! OPENAI_API_KEY && ! req . headers [ 'anthropic-version' ] ) {
1117+ headers [ 'anthropic-version' ] = '2023-06-01' ;
11001118 }
1119+ proxyWebSocket ( req , socket , head , route . target , headers , 'opencode' , route . basePath ) ;
11011120 } ) ;
11021121
11031122 opencodeServer . listen ( 10004 , '0.0.0.0' , ( ) => {
@@ -1125,4 +1144,4 @@ if (require.main === module) {
11251144}
11261145
11271146// Export for testing
1128- module . exports = { normalizeApiTarget, deriveCopilotApiTarget, deriveGitHubApiTarget, deriveGitHubApiBasePath, normalizeBasePath, buildUpstreamPath, proxyWebSocket, resolveCopilotAuthToken } ;
1147+ module . exports = { normalizeApiTarget, deriveCopilotApiTarget, deriveGitHubApiTarget, deriveGitHubApiBasePath, normalizeBasePath, buildUpstreamPath, proxyWebSocket, resolveCopilotAuthToken, resolveOpenCodeRoute } ;
0 commit comments