@@ -237,6 +237,10 @@ def create_parser(cls) -> argparse.ArgumentParser:
237237 "--api-key" ,
238238 help = "API key (or set ANTHROPIC_API_KEY / OPENAI_API_KEY)" ,
239239 )
240+ g_auth .add_argument (
241+ "--base-url" ,
242+ help = "Custom API base URL (e.g., OpenAI/Anthropic proxy)" ,
243+ )
240244
241245 # Execution & limits group
242246 g_limits = parser .add_argument_group ("Execution & Limits" )
@@ -363,6 +367,8 @@ def _get_auth_config(
363367 cli_config .setdefault ("runner" , {})["oauth_token" ] = args .oauth_token
364368 if hasattr (args , "api_key" ) and args .api_key :
365369 cli_config .setdefault ("runner" , {})["api_key" ] = args .api_key
370+ if hasattr (args , "base_url" ) and args .base_url :
371+ cli_config .setdefault ("runner" , {})["base_url" ] = args .base_url
366372 # Include plugin selection at top-level for strategy-agnostic wiring
367373 if hasattr (args , "plugin" ) and args .plugin :
368374 cli_config ["plugin_name" ] = args .plugin
@@ -372,10 +378,24 @@ def _get_auth_config(
372378 cli_config , env_config , dotenv_config , config or {}, defaults
373379 )
374380
375- # Extract auth values from merged config
376- oauth_token = full_config .get ("runner" , {}).get ("oauth_token" )
377- api_key = full_config .get ("runner" , {}).get ("api_key" )
378- base_url = full_config .get ("runner" , {}).get ("base_url" )
381+ # Extract auth values from merged config; select by plugin for consistency
382+ runner_cfg = full_config .get ("runner" , {})
383+ plugin_name = full_config .get ("plugin_name" ) or getattr (
384+ args , "plugin" , "claude-code"
385+ )
386+
387+ if str (plugin_name ) == "codex" :
388+ # Prefer OpenAI keys; allow CLI overrides via generic api_key/base_url
389+ api_key = runner_cfg .get ("api_key" ) or runner_cfg .get ("openai_api_key" )
390+ base_url = runner_cfg .get ("base_url" ) or runner_cfg .get ("openai_base_url" )
391+ oauth_token = None
392+ else :
393+ # Claude Code: prefer OAuth, then Anthropic API key
394+ oauth_token = runner_cfg .get ("oauth_token" )
395+ api_key = runner_cfg .get ("api_key" ) or runner_cfg .get ("anthropic_api_key" )
396+ base_url = runner_cfg .get ("base_url" ) or runner_cfg .get (
397+ "anthropic_base_url"
398+ )
379399
380400 # Apply mode selection logic per spec section 6.1
381401 # 1. If --mode api specified, use API key
@@ -412,15 +432,25 @@ def _get_auth_config(
412432 elif api_key :
413433 # API mode when only API key is available
414434 pass
415- # 5. Otherwise, error with clear message
435+ # 5. Otherwise, error with clear message (fail fast for both providers)
416436 else :
417- # Do not hard-fail: non-Anthropic plugins (e.g., Codex) may use OPENAI_API_KEY
418- # or run in OSS mode. Warn and proceed with empty AuthConfig.
419- self .console .print (
420- "[yellow]Warning: No provider credentials found.\n "
421- "- For Anthropic, set CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY.\n "
422- "- For OpenAI, ensure OPENAI_API_KEY is set (and OPENAI_BASE_URL if applicable).[/yellow]"
423- )
437+ if str (plugin_name ) == "codex" :
438+ self .console .print (
439+ "[red]Error: Missing credentials for OpenAI‑compatible provider.[/red]\n "
440+ "Set OPENAI_API_KEY in:\n "
441+ " - .env file (OPENAI_API_KEY=...)\n "
442+ " - Environment variables (export OPENAI_API_KEY=...)\n "
443+ " - Command line: --api-key\n "
444+ "Optionally set --base-url (or OPENAI_BASE_URL) for non-default endpoints."
445+ )
446+ else :
447+ self .console .print (
448+ "[red]Error: Missing credentials for Anthropic.[/red]\n "
449+ "Set CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY in:\n "
450+ " - .env file\n "
451+ " - Environment variables\n "
452+ " - Command line: --oauth-token or --api-key"
453+ )
424454 return AuthConfig (oauth_token = None , api_key = None , base_url = None )
425455
426456 return AuthConfig (oauth_token = oauth_token , api_key = api_key , base_url = base_url )
@@ -1601,6 +1631,8 @@ async def run_orchestrator(self, args: argparse.Namespace) -> int:
16011631 cli_config .setdefault ("runner" , {})["oauth_token" ] = args .oauth_token
16021632 if hasattr (args , "api_key" ) and args .api_key :
16031633 cli_config .setdefault ("runner" , {})["api_key" ] = args .api_key
1634+ if hasattr (args , "base_url" ) and args .base_url :
1635+ cli_config .setdefault ("runner" , {})["base_url" ] = args .base_url
16041636
16051637 # Apply proxy flags to environment early so downstream components observe them
16061638 # Proxy environment mapping removed
@@ -1766,29 +1798,38 @@ def _red(k, v):
17661798
17671799 retry_config = RetryConfig (max_attempts = 3 )
17681800
1769- # Create auth config from merged configuration
1770- oauth_token = full_config .get ("runner" , {}).get ("oauth_token" )
1771- api_key = full_config .get ("runner" , {}).get ("api_key" )
1772- base_url = full_config .get ("runner" , {}).get ("base_url" )
1773-
1774- if not oauth_token and not api_key :
1775- # Allow non-Anthropic plugins to continue (e.g., Codex with OPENAI_API_KEY)
1776- plugin_name = full_config .get ("plugin_name" ) or getattr (
1777- args , "plugin" , "claude-code"
1778- )
1779- if str (plugin_name ) == "claude-code" :
1801+ # Create auth config from merged configuration (plugin-aware)
1802+ plugin_name = full_config .get ("plugin_name" ) or getattr (
1803+ args , "plugin" , "claude-code"
1804+ )
1805+ rcfg = full_config .get ("runner" , {})
1806+ if str (plugin_name ) == "codex" :
1807+ api_key = rcfg .get ("api_key" ) or rcfg .get ("openai_api_key" )
1808+ base_url = rcfg .get ("base_url" ) or rcfg .get ("openai_base_url" )
1809+ oauth_token = None
1810+ if not api_key :
17801811 self .console .print (
1781- "[red]Error: Missing credentials for the selected provider.[/red]\n "
1812+ "[red]Error: Missing credentials for OpenAI‑compatible provider.[/red]\n "
1813+ "Set OPENAI_API_KEY in:\n "
1814+ " - .env file (OPENAI_API_KEY=...)\n "
1815+ " - Environment variables (export OPENAI_API_KEY=...)\n "
1816+ " - Command line: --api-key\n "
1817+ "Optionally set --base-url (or OPENAI_BASE_URL) for non-default endpoints."
1818+ )
1819+ return 1
1820+ else :
1821+ oauth_token = rcfg .get ("oauth_token" )
1822+ api_key = rcfg .get ("api_key" ) or rcfg .get ("anthropic_api_key" )
1823+ base_url = rcfg .get ("base_url" ) or rcfg .get ("anthropic_base_url" )
1824+ if not oauth_token and not api_key :
1825+ self .console .print (
1826+ "[red]Error: Missing credentials for Anthropic.[/red]\n "
17821827 "Set CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY in:\n "
17831828 " - .env file\n "
17841829 " - Environment variables\n "
17851830 " - Command line: --oauth-token or --api-key"
17861831 )
17871832 return 1
1788- else :
1789- self .console .print (
1790- "[yellow]Warning: No provider credentials set. Proceeding with selected plugin.[/yellow]"
1791- )
17921833
17931834 auth_config = AuthConfig (
17941835 oauth_token = oauth_token , api_key = api_key , base_url = base_url
0 commit comments