@@ -17,11 +17,11 @@ import * as http from 'node:http';
1717import * as url from 'node:url' ;
1818import { RelayPlane } from './relay.js' ;
1919import { inferTaskType , getInferenceConfidence } from './routing/inference.js' ;
20- import { loadConfig , watchConfig , getStrategy , type Config } from './config.js' ;
20+ import { loadConfig , watchConfig , getStrategy , getAnthropicAuth , type Config } from './config.js' ;
2121import type { Provider , TaskType } from './types.js' ;
2222
2323/** Package version */
24- const VERSION = '0.1.8 ' ;
24+ const VERSION = '0.1.9 ' ;
2525
2626/** Recent runs buffer for /runs endpoint */
2727interface RecentRun {
@@ -164,23 +164,37 @@ function extractPromptText(messages: ChatRequest['messages']): string {
164164 . join ( '\n' ) ;
165165}
166166
167+ /**
168+ * Auth info for Anthropic requests
169+ */
170+ interface AnthropicAuth {
171+ type : 'apiKey' | 'max' ;
172+ value : string ;
173+ }
174+
167175/**
168176 * Forward non-streaming request to Anthropic API
169177 */
170178async function forwardToAnthropic (
171179 request : ChatRequest ,
172180 targetModel : string ,
173- apiKey : string ,
181+ auth : AnthropicAuth ,
174182 betaHeaders ?: string
175183) : Promise < Response > {
176184 const anthropicBody = buildAnthropicBody ( request , targetModel , false ) ;
177185
178186 const headers : Record < string , string > = {
179187 'Content-Type' : 'application/json' ,
180- 'x-api-key' : apiKey ,
181188 'anthropic-version' : '2023-06-01' ,
182189 } ;
183190
191+ // Use appropriate auth header based on type
192+ if ( auth . type === 'max' ) {
193+ headers [ 'Authorization' ] = `Bearer ${ auth . value } ` ;
194+ } else {
195+ headers [ 'x-api-key' ] = auth . value ;
196+ }
197+
184198 // Pass through beta headers (prompt caching, extended thinking, etc.)
185199 if ( betaHeaders ) {
186200 headers [ 'anthropic-beta' ] = betaHeaders ;
@@ -201,17 +215,23 @@ async function forwardToAnthropic(
201215async function forwardToAnthropicStream (
202216 request : ChatRequest ,
203217 targetModel : string ,
204- apiKey : string ,
218+ auth : AnthropicAuth ,
205219 betaHeaders ?: string
206220) : Promise < Response > {
207221 const anthropicBody = buildAnthropicBody ( request , targetModel , true ) ;
208222
209223 const headers : Record < string , string > = {
210224 'Content-Type' : 'application/json' ,
211- 'x-api-key' : apiKey ,
212225 'anthropic-version' : '2023-06-01' ,
213226 } ;
214227
228+ // Use appropriate auth header based on type
229+ if ( auth . type === 'max' ) {
230+ headers [ 'Authorization' ] = `Bearer ${ auth . value } ` ;
231+ } else {
232+ headers [ 'x-api-key' ] = auth . value ;
233+ }
234+
215235 if ( betaHeaders ) {
216236 headers [ 'anthropic-beta' ] = betaHeaders ;
217237 }
@@ -1419,14 +1439,29 @@ export async function startProxy(config: ProxyConfig = {}): Promise<http.Server>
14191439
14201440 log ( `Routing to: ${ targetProvider } /${ targetModel } ` ) ;
14211441
1422- // Get API key for target provider
1423- const apiKeyEnv = DEFAULT_ENDPOINTS [ targetProvider ] ?. apiKeyEnv ?? ` ${ targetProvider . toUpperCase ( ) } _API_KEY` ;
1424- const apiKey = process . env [ apiKeyEnv ] ;
1442+ // Get auth for target provider
1443+ let apiKey : string | undefined ;
1444+ let anthropicAuth : { type : ' apiKey' | 'max' ; value : string } | null = null ;
14251445
1426- if ( ! apiKey ) {
1427- res . writeHead ( 500 , { 'Content-Type' : 'application/json' } ) ;
1428- res . end ( JSON . stringify ( { error : `Missing ${ apiKeyEnv } environment variable` } ) ) ;
1429- return ;
1446+ if ( targetProvider === 'anthropic' ) {
1447+ // Use hybrid auth system for Anthropic (supports MAX + API key)
1448+ anthropicAuth = getAnthropicAuth ( currentConfig , targetModel ) ;
1449+ if ( ! anthropicAuth ) {
1450+ res . writeHead ( 500 , { 'Content-Type' : 'application/json' } ) ;
1451+ res . end ( JSON . stringify ( { error : 'No Anthropic auth configured (set ANTHROPIC_API_KEY or config.auth.anthropicMaxToken)' } ) ) ;
1452+ return ;
1453+ }
1454+ log ( `Using ${ anthropicAuth . type === 'max' ? 'MAX token' : 'API key' } auth for ${ targetModel } ` ) ;
1455+ } else {
1456+ // Standard API key auth for other providers
1457+ const apiKeyEnv = DEFAULT_ENDPOINTS [ targetProvider ] ?. apiKeyEnv ?? `${ targetProvider . toUpperCase ( ) } _API_KEY` ;
1458+ apiKey = process . env [ apiKeyEnv ] ;
1459+
1460+ if ( ! apiKey ) {
1461+ res . writeHead ( 500 , { 'Content-Type' : 'application/json' } ) ;
1462+ res . end ( JSON . stringify ( { error : `Missing ${ apiKeyEnv } environment variable` } ) ) ;
1463+ return ;
1464+ }
14301465 }
14311466
14321467 const startTime = Date . now ( ) ;
@@ -1442,6 +1477,7 @@ export async function startProxy(config: ProxyConfig = {}): Promise<http.Server>
14421477 targetProvider ,
14431478 targetModel ,
14441479 apiKey ,
1480+ anthropicAuth ,
14451481 relay ,
14461482 promptText ,
14471483 taskType ,
@@ -1458,6 +1494,7 @@ export async function startProxy(config: ProxyConfig = {}): Promise<http.Server>
14581494 targetProvider ,
14591495 targetModel ,
14601496 apiKey ,
1497+ anthropicAuth ,
14611498 relay ,
14621499 promptText ,
14631500 taskType ,
@@ -1499,7 +1536,8 @@ async function handleStreamingRequest(
14991536 request : ChatRequest ,
15001537 targetProvider : Provider ,
15011538 targetModel : string ,
1502- apiKey : string ,
1539+ apiKey : string | undefined ,
1540+ anthropicAuth : { type : 'apiKey' | 'max' ; value : string } | null ,
15031541 relay : RelayPlane ,
15041542 promptText : string ,
15051543 taskType : TaskType ,
@@ -1514,19 +1552,20 @@ async function handleStreamingRequest(
15141552 try {
15151553 switch ( targetProvider ) {
15161554 case 'anthropic' :
1517- providerResponse = await forwardToAnthropicStream ( request , targetModel , apiKey , betaHeaders ) ;
1555+ if ( ! anthropicAuth ) throw new Error ( 'No Anthropic auth' ) ;
1556+ providerResponse = await forwardToAnthropicStream ( request , targetModel , anthropicAuth , betaHeaders ) ;
15181557 break ;
15191558 case 'google' :
1520- providerResponse = await forwardToGeminiStream ( request , targetModel , apiKey ) ;
1559+ providerResponse = await forwardToGeminiStream ( request , targetModel , apiKey ! ) ;
15211560 break ;
15221561 case 'xai' :
1523- providerResponse = await forwardToXAIStream ( request , targetModel , apiKey ) ;
1562+ providerResponse = await forwardToXAIStream ( request , targetModel , apiKey ! ) ;
15241563 break ;
15251564 case 'moonshot' :
1526- providerResponse = await forwardToMoonshotStream ( request , targetModel , apiKey ) ;
1565+ providerResponse = await forwardToMoonshotStream ( request , targetModel , apiKey ! ) ;
15271566 break ;
15281567 default :
1529- providerResponse = await forwardToOpenAIStream ( request , targetModel , apiKey ) ;
1568+ providerResponse = await forwardToOpenAIStream ( request , targetModel , apiKey ! ) ;
15301569 }
15311570
15321571 if ( ! providerResponse . ok ) {
@@ -1619,7 +1658,8 @@ async function handleNonStreamingRequest(
16191658 request : ChatRequest ,
16201659 targetProvider : Provider ,
16211660 targetModel : string ,
1622- apiKey : string ,
1661+ apiKey : string | undefined ,
1662+ anthropicAuth : { type : 'apiKey' | 'max' ; value : string } | null ,
16231663 relay : RelayPlane ,
16241664 promptText : string ,
16251665 taskType : TaskType ,
@@ -1635,7 +1675,8 @@ async function handleNonStreamingRequest(
16351675 try {
16361676 switch ( targetProvider ) {
16371677 case 'anthropic' : {
1638- providerResponse = await forwardToAnthropic ( request , targetModel , apiKey , betaHeaders ) ;
1678+ if ( ! anthropicAuth ) throw new Error ( 'No Anthropic auth' ) ;
1679+ providerResponse = await forwardToAnthropic ( request , targetModel , anthropicAuth , betaHeaders ) ;
16391680 const rawData = ( await providerResponse . json ( ) ) as AnthropicResponse ;
16401681
16411682 if ( ! providerResponse . ok ) {
@@ -1649,7 +1690,7 @@ async function handleNonStreamingRequest(
16491690 break ;
16501691 }
16511692 case 'google' : {
1652- providerResponse = await forwardToGemini ( request , targetModel , apiKey ) ;
1693+ providerResponse = await forwardToGemini ( request , targetModel , apiKey ! ) ;
16531694 const rawData = ( await providerResponse . json ( ) ) as GeminiResponse ;
16541695
16551696 if ( ! providerResponse . ok ) {
@@ -1663,7 +1704,7 @@ async function handleNonStreamingRequest(
16631704 break ;
16641705 }
16651706 case 'xai' : {
1666- providerResponse = await forwardToXAI ( request , targetModel , apiKey ) ;
1707+ providerResponse = await forwardToXAI ( request , targetModel , apiKey ! ) ;
16671708 responseData = ( await providerResponse . json ( ) ) as Record < string , unknown > ;
16681709
16691710 if ( ! providerResponse . ok ) {
@@ -1674,7 +1715,7 @@ async function handleNonStreamingRequest(
16741715 break ;
16751716 }
16761717 case 'moonshot' : {
1677- providerResponse = await forwardToMoonshot ( request , targetModel , apiKey ) ;
1718+ providerResponse = await forwardToMoonshot ( request , targetModel , apiKey ! ) ;
16781719 responseData = ( await providerResponse . json ( ) ) as Record < string , unknown > ;
16791720
16801721 if ( ! providerResponse . ok ) {
@@ -1685,7 +1726,7 @@ async function handleNonStreamingRequest(
16851726 break ;
16861727 }
16871728 default : {
1688- providerResponse = await forwardToOpenAI ( request , targetModel , apiKey ) ;
1729+ providerResponse = await forwardToOpenAI ( request , targetModel , apiKey ! ) ;
16891730 responseData = ( await providerResponse . json ( ) ) as Record < string , unknown > ;
16901731
16911732 if ( ! providerResponse . ok ) {
0 commit comments