Skip to content

feat: Add Codex provider config bridge#18

Open
hanxuanliang wants to merge 1 commit intopaoloanzn:mainfrom
hanxuanliang:feat/codex-provider-bridge
Open

feat: Add Codex provider config bridge#18
hanxuanliang wants to merge 1 commit intopaoloanzn:mainfrom
hanxuanliang:feat/codex-provider-bridge

Conversation

@hanxuanliang
Copy link
Copy Markdown

@hanxuanliang hanxuanliang commented Apr 1, 2026

Summary

This PR adds a Codex-style provider/auth/config bridge to free-code while keeping the existing Anthropic -> Responses conversion flow unchanged.

  • Read provider selection from ~/.codex/config.toml
  • Read auth from ~/.codex/auth.json, OPENAI_API_KEY, and CODEX_API_KEY
  • Support OpenAI-compatible base_url + /responses
  • Preserve the existing ChatGPT Codex OAuth path as fallback
  • Route the OpenAI provider through either generic Responses transport or the existing ChatGPT transport

Features

  • Codex-like provider resolution via model_provider and model_providers
  • API key, base_url, headers, env-backed headers, and query param support
  • Compatibility with auth.json.OPENAI_API_KEY
  • ChatGPT token fallback when no API key provider is configured
  • Subscriber detection updated to recognize the new bridge path

Testing

  • Added targeted bridge tests in src/services/api/codex-provider-bridge.test.ts
  • Verified custom provider resolution, auth.json API key loading, ChatGPT token fallback, and non-Responses rejection

Related

Closes the gap around reusing local Codex provider/auth/config in free-code.

Summary by CodeRabbit

  • Chores

    • Added bun test npm script for running tests.
    • Refactored internal API provider resolution and client configuration architecture.
  • Tests

    • Added test suite for provider bridge configuration validation across multiple provider scenarios.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 1, 2026

📝 Walkthrough

Walkthrough

This PR introduces a provider bridge abstraction layer for API client initialization, replacing direct token-based provider selection with a configuration-driven approach. A new module resolves provider bridges from environment variables and config files, API clients leverage this bridge for authentication, and auth logic is updated to validate subscription through provider resolution rather than direct token checks.

Changes

Cohort / File(s) Summary
Configuration
package.json
Added test npm script (bun test) and trailing comma to dev script.
Provider Bridge Layer
src/services/api/codex-provider-bridge.ts, src/services/api/codex-provider-bridge.test.ts
New module that resolves provider bridge configuration from environment variables, TOML config files, and JSON auth files, supporting custom API providers and fallback to ChatGPT. Comprehensive test suite validates resolution logic across multiple provider scenarios.
API Client & Fetch Adapters
src/services/api/client.ts, src/services/api/codex-fetch-adapter.ts
Refactored client initialization to use provider bridge pattern instead of isCodexSubscriber checks. Extracted fetch adapter logic into createResponsesBridgeFetch factory; added new createResponsesFetch export for custom provider support.
Authentication
src/utils/auth.ts
Updated isCodexSubscriber() to validate subscription by resolving provider bridge instead of directly checking token presence.

Sequence Diagram(s)

sequenceDiagram
    participant Client Code
    participant Provider Bridge Resolver
    participant Config/Env Reader
    participant API Client Factory
    participant Fetch Adapter
    participant SDK Client

    Client Code->>Provider Bridge Resolver: resolveCodexProviderBridge(options)
    Provider Bridge Resolver->>Config/Env Reader: Read codexHomeDir, config.toml, auth.json
    Config/Env Reader-->>Provider Bridge Resolver: Config, API keys, tokens
    Provider Bridge Resolver->>Provider Bridge Resolver: Validate provider & wire_api<br/>Resolve authentication
    alt API Key Available
        Provider Bridge Resolver-->>Client Code: kind='responses' bridge<br/>(endpoint, apiKey, headers)
    else No API Key, OpenAI Default
        Provider Bridge Resolver-->>Client Code: kind='chatgpt' bridge<br/>(accessToken)
    end
    
    Client Code->>API Client Factory: getAnthropicClient(bridge)
    alt bridge.kind === 'chatgpt'
        API Client Factory->>Fetch Adapter: createCodexFetch(bridge.accessToken)
    else bridge.kind === 'responses'
        API Client Factory->>Fetch Adapter: createResponsesFetch(bridge)
    end
    Fetch Adapter-->>API Client Factory: fetch implementation
    API Client Factory->>SDK Client: Initialize with fetch
    SDK Client-->>Client Code: Ready SDK client
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 A bridge of providers, now configured with care,
No more scattered tokens hiding elsewhere,
From config files and env vars so bright,
API adapters choose their path right,
The rabbit hops through resolution's dance! 🎩✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.88% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Add Codex provider config bridge' clearly and specifically summarizes the main change: introducing a provider configuration bridge system for Codex. It directly corresponds to the primary objective of reading provider/auth configuration and routing through multiple provider types.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/services/api/client.ts (1)

313-332: ⚠️ Potential issue | 🟡 Minor

Use tryResolveCodexProviderBridge to gracefully handle bridge resolution errors.

resolveCodexProviderBridge throws errors when the provider is not found (line 196) or when wire_api is not "responses" (line 201). At line 315 in client.ts, this function is called without error handling, so misconfiguration in ~/.codex/config.toml will cause an uncaught exception rather than falling back to the default behavior.

The codebase already provides tryResolveCodexProviderBridge (a try-catch wrapper that returns null on error) for this exact scenario. Use it instead to match the fallback pattern established by the null-check on line 319.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/services/api/client.ts` around lines 313 - 332, The code calls
resolveCodexProviderBridge directly (inside the getAPIProvider() === 'openai'
branch) which can throw; replace that call with tryResolveCodexProviderBridge to
safely return null on errors, then keep the existing null-check and bridge
handling intact (use the same variables: codexTokens, bridgeFetch creation using
createCodexFetch/createResponsesFetch, clientConfig and new Anthropic(...)) so
misconfigured ~/.codex/config.toml won’t raise an uncaught exception.
🧹 Nitpick comments (3)
src/services/api/codex-provider-bridge.ts (2)

239-247: Error swallowing may hide configuration issues.

tryResolveCodexProviderBridge catches all errors and returns null, which is appropriate for isCodexSubscriber() checks but could hide misconfiguration issues (e.g., malformed TOML, missing required fields).

Consider logging errors at debug level before returning null to aid troubleshooting:

🔧 Suggested improvement
 export function tryResolveCodexProviderBridge(
   options: ResolveCodexProviderBridgeOptions = {},
 ): CodexProviderBridgeConfig | null {
   try {
     return resolveCodexProviderBridge(options)
-  } catch {
+  } catch (error) {
+    // Log at debug level to aid troubleshooting without disrupting normal flow
+    if (process.env.DEBUG) {
+      console.error('[codex-provider-bridge] Resolution failed:', error)
+    }
     return null
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/services/api/codex-provider-bridge.ts` around lines 239 - 247, The
tryResolveCodexProviderBridge function currently swallows all exceptions from
resolveCodexProviderBridge; update it to catch the error, log the error (at
debug/verbose level) including the error object and a brief context message that
resolving the Codex provider bridge failed, then return null; reference
tryResolveCodexProviderBridge and resolveCodexProviderBridge so you add the
logging inside the catch before the null return.

68-78: Synchronous file I/O may block the event loop.

readFileSync is used here to read config.toml. While this is acceptable during initialization, if resolveCodexProviderBridge is called frequently during request handling, it could cause latency issues.

Consider caching the parsed config or using async reads if this function is called on hot paths. Based on the usage in client.ts, this appears to be called per-client creation, which should be infrequent enough that sync I/O is acceptable.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/services/api/codex-provider-bridge.ts` around lines 68 - 78,
readCodexConfig currently uses synchronous readFileSync which can block the
event loop; change it to avoid hot-path sync I/O by either (A) caching the
parsed config per codexHomeDir inside this module (e.g., a Map keyed by
codexHomeDir) and returning the cached CodexConfigToml if present before doing a
single sync read on first load, or (B) convert readCodexConfig to an async
function that uses Bun.file(...).text() or fs.promises.readFile and update
callers such as resolveCodexProviderBridge and client.ts to await the async
call; prefer option A if callers are not ready for async to minimize call-site
changes. Ensure the ENOENT handling remains and the cache invalidation strategy
(none or a manual reset) is documented.
src/services/api/codex-fetch-adapter.ts (1)

742-763: Internal type naming could be clearer.

The accessToken field in ResponsesBridgeFetchOptions is used for both OAuth access tokens (in createCodexFetch) and API keys (in createResponsesFetch). Consider renaming to something more generic like authToken or bearerToken to clarify its dual purpose, or add a comment explaining the usage.

This is a minor readability concern since the type is internal.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/services/api/codex-fetch-adapter.ts` around lines 742 - 763, Rename the
ambiguous field accessToken in the ResponsesBridgeFetchOptions type to a more
generic name (e.g., authToken or bearerToken) and update all references in
createResponsesBridgeFetch to use the new name (including the parameter
destructuring and resolveAccessToken assignment); also update any callers or
related helpers such as createCodexFetch and createResponsesFetch (or add a
short inline comment on ResponsesBridgeFetchOptions) to clarify whether the
value is an OAuth token or API key so the dual-purpose intent is explicit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/services/api/client.ts`:
- Around line 313-332: The code calls resolveCodexProviderBridge directly
(inside the getAPIProvider() === 'openai' branch) which can throw; replace that
call with tryResolveCodexProviderBridge to safely return null on errors, then
keep the existing null-check and bridge handling intact (use the same variables:
codexTokens, bridgeFetch creation using createCodexFetch/createResponsesFetch,
clientConfig and new Anthropic(...)) so misconfigured ~/.codex/config.toml won’t
raise an uncaught exception.

---

Nitpick comments:
In `@src/services/api/codex-fetch-adapter.ts`:
- Around line 742-763: Rename the ambiguous field accessToken in the
ResponsesBridgeFetchOptions type to a more generic name (e.g., authToken or
bearerToken) and update all references in createResponsesBridgeFetch to use the
new name (including the parameter destructuring and resolveAccessToken
assignment); also update any callers or related helpers such as createCodexFetch
and createResponsesFetch (or add a short inline comment on
ResponsesBridgeFetchOptions) to clarify whether the value is an OAuth token or
API key so the dual-purpose intent is explicit.

In `@src/services/api/codex-provider-bridge.ts`:
- Around line 239-247: The tryResolveCodexProviderBridge function currently
swallows all exceptions from resolveCodexProviderBridge; update it to catch the
error, log the error (at debug/verbose level) including the error object and a
brief context message that resolving the Codex provider bridge failed, then
return null; reference tryResolveCodexProviderBridge and
resolveCodexProviderBridge so you add the logging inside the catch before the
null return.
- Around line 68-78: readCodexConfig currently uses synchronous readFileSync
which can block the event loop; change it to avoid hot-path sync I/O by either
(A) caching the parsed config per codexHomeDir inside this module (e.g., a Map
keyed by codexHomeDir) and returning the cached CodexConfigToml if present
before doing a single sync read on first load, or (B) convert readCodexConfig to
an async function that uses Bun.file(...).text() or fs.promises.readFile and
update callers such as resolveCodexProviderBridge and client.ts to await the
async call; prefer option A if callers are not ready for async to minimize
call-site changes. Ensure the ENOENT handling remains and the cache invalidation
strategy (none or a manual reset) is documented.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e8617db1-8881-450e-b8fc-454c70239146

📥 Commits

Reviewing files that changed from the base of the PR and between 7dc15d6 and f0f5e84.

📒 Files selected for processing (6)
  • package.json
  • src/services/api/client.ts
  • src/services/api/codex-fetch-adapter.ts
  • src/services/api/codex-provider-bridge.test.ts
  • src/services/api/codex-provider-bridge.ts
  • src/utils/auth.ts

Copy link
Copy Markdown

@brrock brrock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simply lovely.

@jason-c-huang
Copy link
Copy Markdown

@paoloanzn Can you please open github issues for community engagement.

@brrock
Copy link
Copy Markdown

brrock commented Apr 2, 2026

From what I have seen in other popular cc forks, issues become spammed, and hard to manage

@paoloanzn
Copy link
Copy Markdown
Owner

@paoloanzn Can you please open github issues for community engagement.

done.

sorry for the delay but this project came unexpected for obvious reasons i did not had the time to organize properly.

there is a specific direction i have in mind to bring this forward, but i'm still planning.

any good maintainers that wants to help is welcome. feel free to email me at paolo@gladium.ai linking your github profile.

@company8
Copy link
Copy Markdown

company8 commented Apr 2, 2026

@paoloanzn Would've opened a issue but I couldn't.

[*] Cloning repository...
Cloning into '/home/host/free-code'...
remote: Repository 'paoloanzn/free-code' is disabled.
remote: Please ask the owner to check their account.
fatal: unable to access 'https://github.com/paoloanzn/free-code.git/': The requested URL returned error: 403

@brrock
Copy link
Copy Markdown

brrock commented Apr 2, 2026 via email

@brrock
Copy link
Copy Markdown

brrock commented Apr 2, 2026

@paoloanzn Can you please open github issues for community engagement.

done.

sorry for the delay but this project came unexpected for obvious reasons i did not had the time to organize properly.

there is a specific direction i have in mind to bring this forward, but i'm still planning.

any good maintainers that wants to help is welcome. feel free to email me at paolo@gladium.ai linking your github profile.

Hey I sent you an email! Thanks for a nice project!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants