Add Codex OAuth provider support#2748
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds Codex CLI ChatGPT auth import and account_id-aware OpenAI OAuth handling: import from CODEX_HOME/~/.codex/auth.json, persist OAuth profile with optional account_id, expose RPC/controller/schema for import, route OAuth-backed OpenAI requests via a Codex backend with extra headers/query params, and add frontend UI, API facades, and tests to trigger/import Codex OAuth. ChangesOpenAI Codex CLI OAuth & Account ID Routing
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (2)
src/openhuman/inference/provider/factory.rs (1)
832-851: ⚡ Quick winLog the Codex routing branch explicitly.
The info log above still reports
entry.endpoint, but this branch can silently send traffic tohttps://chatgpt.com/backend-api/codexand addChatGPT-Account-ID. Moving the log after endpoint selection, or adding a dedicated branch log here, will make OAuth routing failures much easier to diagnose. As per coding guidelines "Log entry/exit, branches, external calls, retries/timeouts, state transitions, and errors with verbose diagnostics using stable grep-friendly prefixes and correlation fields".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/openhuman/inference/provider/factory.rs` around lines 832 - 851, Move or add an info log immediately after the endpoint selection (the `endpoint = if using_openai_oauth { ... } else { &entry.endpoint }` block) that explicitly records which branch was taken and the effective endpoint value, and also log whether a ChatGPT-Account-ID header was added (i.e., whether `using_openai_oauth` is true and `openai_oauth_credentials` provided an `account_id`), so when constructing the `OpenAiCompatibleProvider` (via `OpenAiCompatibleProvider::new`) you emit a stable, grep-friendly message containing the chosen `endpoint` and a flag or the actual account id that will be passed to `provider.with_extra_header("ChatGPT-Account-ID", ...)`.src/openhuman/inference/provider/ops.rs (1)
61-75: ⚡ Quick winCentralize the OpenAI OAuth routing/header decision.
This duplicates the Codex base-URL and
ChatGPT-Account-IDselection logic fromfactory.rs, and the two paths have already drifted: the provider path drops blankaccount_idvalues viawith_extra_header(), while this probe path would still send an empty header. Please extract one shared helper that returns the effective base URL plus optional extra headers, and reuse it here and in the factory.Also applies to: 100-107
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/openhuman/inference/provider/ops.rs` around lines 61 - 75, The probe path duplicates the OpenAI OAuth routing and ChatGPT-Account-ID header logic from factory.rs (the using_openai_oauth/base/header selection), causing drift and sending blank headers; extract a single helper (e.g., resolve_openai_base_and_headers or get_openai_routing_and_headers) that returns (base_url: String, extra_headers: Option<HeaderMap>) and centralizes: 1) detecting OAuth via lookup_openai_oauth_credentials, 2) choosing the codex/chatgpt base URL, and 3) only adding ChatGPT-Account-ID when account_id is non-empty (matching with_extra_header behavior). Replace the inline using_openai_oauth/base/header logic in ops.rs (and in factory.rs) to call that helper so both paths reuse the same routing and header decision.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/src/components/settings/panels/AIPanel.tsx`:
- Around line 2690-2705: The Codex auth error state (codexAuthError) isn't
cleared when the user successfully connects OpenAI via a non-Codex path; update
the success paths to explicitly reset this state: after the await
connectProvider(...) call in connectOpenAiViaCodexAuth, call
setCodexAuthError(null) so a prior error is removed on success, and apply the
same change to the other OpenAI connect handler that also calls connectProvider
(the API-key/normal connect path) so any successful connect clears
codexAuthError as well.
- Around line 2629-2632: The code currently calls
clearCloudProviderKey('openai') immediately when isCodexOAuth && slug ===
'openai', which can remove the user’s existing OpenAI key before
flushCloudProviders(...) and persist(...) succeed; change the flow so you do NOT
clear the existing key here and instead call clearCloudProviderKey('openai')
only after flushCloudProviders(...) and persist(...) have both completed
successfully (and handle errors so the clear is skipped on failure). Locate
references to clearCloudProviderKey, setCloudProviderKey, flushCloudProviders,
persist, isCodexOAuth and slug in AIPanel.tsx and move the clearCloudProviderKey
invocation to the success path after the flush/persist operations, ensuring
proper error handling so the old key is preserved if either write fails.
In `@app/src/services/api/aiSettingsApi.ts`:
- Around line 342-345: Replace the hard-coded English Error throws around the
authUrl check (the throw after checking const authUrl =
res?.result?.authUrl?.trim()) and the similar throw later in this module with a
stable machine-readable error code instead of prose (for example throw new
Error('OPENAI_CODEX_OAUTH_MISSING') or throw { code:
'OPENAI_CODEX_OAUTH_MISSING' }); keep the check logic the same but return/throw
the code so callers can map it to localized copy via useT(); update both
occurrences in aiSettingsApi.ts that validate res?.result?.authUrl and the
similar block at the other validation point, and add a corresponding English
localization entry for that new error key in the i18n English resources so
callers can t('...') it.
In `@src/openhuman/inference/openai_oauth/store.rs`:
- Around line 100-157: The import_codex_cli_auth_from_path function lacks
structured entry/exit/branch/error diagnostics; add grep-friendly logs (e.g.,
prefix OPENAI_OAUTH_IMPORT) at function entry and exit, before/after each
failure branch (file read, JSON parse, missing tokens, empty access token), and
around the external persistence call persist_openai_oauth_token_set, including
correlation fields path (path.display()), provider ("codex"), and
profile_id/account_id (when available) but never logging token values or
secrets; use the project's existing logging facility (tracing or the local
logger) and include references to import_codex_cli_auth_from_path,
persist_openai_oauth_token_set, extract_account_id_from_access_token, and
extract_expires_at_from_access_token so logs show context for debugging.
- Around line 94-97: The code currently only reads std::env::var_os("HOME") to
build the auth path, which fails on Windows; update the path-resolution logic
(the block producing home and building
Ok(home.join(".codex").join("auth.json"))) to fall back to Windows env vars: if
"HOME" is unset, try std::env::var_os("USERPROFILE"), then try combining
"HOMEDRIVE" + "HOMEPATH" if needed, and use the first non-empty PathBuf; then
join ".codex/auth.json" as before so the function (the home resolution that
produces the PathBuf) works on Windows as well.
In `@src/openhuman/inference/provider/factory.rs`:
- Around line 788-797: Replace the unconditional fatal error on
lookup_openai_oauth_credentials with a tolerant lookup: call
lookup_openai_oauth_credentials(config) and on Err convert to None but record
that the lookup failed; compute using_openai_oauth from
openai_oauth_credentials.as_ref().is_some_and(|c| c.access_token == key); if the
lookup previously failed AND using_openai_oauth is true then propagate the
error, otherwise treat openai_oauth_credentials as None and continue so normal
API-key routing still works. Ensure you modify the block around
lookup_openai_oauth_credentials, openai_oauth_credentials and using_openai_oauth
to implement this logic.
---
Nitpick comments:
In `@src/openhuman/inference/provider/factory.rs`:
- Around line 832-851: Move or add an info log immediately after the endpoint
selection (the `endpoint = if using_openai_oauth { ... } else { &entry.endpoint
}` block) that explicitly records which branch was taken and the effective
endpoint value, and also log whether a ChatGPT-Account-ID header was added
(i.e., whether `using_openai_oauth` is true and `openai_oauth_credentials`
provided an `account_id`), so when constructing the `OpenAiCompatibleProvider`
(via `OpenAiCompatibleProvider::new`) you emit a stable, grep-friendly message
containing the chosen `endpoint` and a flag or the actual account id that will
be passed to `provider.with_extra_header("ChatGPT-Account-ID", ...)`.
In `@src/openhuman/inference/provider/ops.rs`:
- Around line 61-75: The probe path duplicates the OpenAI OAuth routing and
ChatGPT-Account-ID header logic from factory.rs (the
using_openai_oauth/base/header selection), causing drift and sending blank
headers; extract a single helper (e.g., resolve_openai_base_and_headers or
get_openai_routing_and_headers) that returns (base_url: String, extra_headers:
Option<HeaderMap>) and centralizes: 1) detecting OAuth via
lookup_openai_oauth_credentials, 2) choosing the codex/chatgpt base URL, and 3)
only adding ChatGPT-Account-ID when account_id is non-empty (matching
with_extra_header behavior). Replace the inline using_openai_oauth/base/header
logic in ops.rs (and in factory.rs) to call that helper so both paths reuse the
same routing and header decision.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 01fb2d23-8b9e-4701-86d7-ad1580ef354d
📒 Files selected for processing (14)
app/src/components/settings/panels/AIPanel.tsxapp/src/components/settings/panels/__tests__/AIPanel.test.tsxapp/src/services/api/aiSettingsApi.tssrc/openhuman/inference/openai_oauth/flow.rssrc/openhuman/inference/openai_oauth/flow_tests.rssrc/openhuman/inference/openai_oauth/mod.rssrc/openhuman/inference/openai_oauth/store.rssrc/openhuman/inference/ops.rssrc/openhuman/inference/provider/compatible.rssrc/openhuman/inference/provider/compatible_tests.rssrc/openhuman/inference/provider/factory.rssrc/openhuman/inference/provider/ops.rssrc/openhuman/inference/schemas.rssrc/openhuman/inference/schemas_tests.rs
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/openhuman/inference/openai_oauth/store.rs (1)
249-260:⚠️ Potential issue | 🟠 Major | ⚡ Quick winFix JWT base64url decoding in decode_access_token_payload
decode_access_token_payloadpads the JWT payload with=and then decodes it usinggeneral_purpose::URL_SAFE_NO_PAD, which rejects padded (=) input. The fallback togeneral_purpose::STANDARDis also incorrect for JWT base64url alphabet:STANDARDexpects+/and rejects-_, soaccount_id/expextraction can fail for tokens with URL-safe characters.🐛 Proposed fix
fn decode_access_token_payload(access_token: &str) -> Option<serde_json::Value> { let payload = access_token.split('.').nth(1)?; - let padded = match payload.len() % 4 { - 0 => payload.to_string(), - n => format!("{}{}", payload, "=".repeat(4 - n)), - }; - let bytes = base64::engine::general_purpose::URL_SAFE_NO_PAD - .decode(padded.as_bytes()) - .or_else(|_| base64::engine::general_purpose::STANDARD.decode(padded.as_bytes())) - .ok()?; + // JWT payloads are base64url-encoded without padding + let bytes = base64::engine::general_purpose::URL_SAFE_NO_PAD + .decode(payload) + .or_else(|_| { + // Some tokens may have padding; try with padding added + let padded = match payload.len() % 4 { + 0 => payload.to_string(), + n => format!("{}{}", payload, "=".repeat(4 - n)), + }; + base64::engine::general_purpose::URL_SAFE.decode(&padded) + }) + .ok()?; serde_json::from_slice(&bytes).ok() }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/openhuman/inference/openai_oauth/store.rs` around lines 249 - 260, The JWT payload decoding in decode_access_token_payload incorrectly pads then uses URL_SAFE_NO_PAD and falls back to STANDARD (which uses the wrong alphabet); update decode_access_token_payload to decode using the URL-safe base64 alphabet that accepts padding: first create the correctly padded payload string (as you already do) and call base64::engine::general_purpose::URL_SAFE.decode(...) (not URL_SAFE_NO_PAD), and if that fails fall back to URL_SAFE_NO_PAD.decode(...) — remove the use of STANDARD entirely — then parse the resulting bytes with serde_json::from_slice as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/openhuman/inference/openai_oauth/store.rs`:
- Around line 249-260: The JWT payload decoding in decode_access_token_payload
incorrectly pads then uses URL_SAFE_NO_PAD and falls back to STANDARD (which
uses the wrong alphabet); update decode_access_token_payload to decode using the
URL-safe base64 alphabet that accepts padding: first create the correctly padded
payload string (as you already do) and call
base64::engine::general_purpose::URL_SAFE.decode(...) (not URL_SAFE_NO_PAD), and
if that fails fall back to URL_SAFE_NO_PAD.decode(...) — remove the use of
STANDARD entirely — then parse the resulting bytes with serde_json::from_slice
as before.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 709a89b3-4feb-475e-935d-6689549915bf
📒 Files selected for processing (31)
app/src/components/settings/panels/AIPanel.tsxapp/src/components/settings/panels/__tests__/AIPanel.test.tsxapp/src/lib/i18n/chunks/ar-1.tsapp/src/lib/i18n/chunks/ar-4.tsapp/src/lib/i18n/chunks/bn-1.tsapp/src/lib/i18n/chunks/bn-4.tsapp/src/lib/i18n/chunks/de-1.tsapp/src/lib/i18n/chunks/de-4.tsapp/src/lib/i18n/chunks/en-1.tsapp/src/lib/i18n/chunks/en-4.tsapp/src/lib/i18n/chunks/es-1.tsapp/src/lib/i18n/chunks/es-4.tsapp/src/lib/i18n/chunks/fr-1.tsapp/src/lib/i18n/chunks/fr-4.tsapp/src/lib/i18n/chunks/hi-1.tsapp/src/lib/i18n/chunks/hi-4.tsapp/src/lib/i18n/chunks/id-1.tsapp/src/lib/i18n/chunks/id-4.tsapp/src/lib/i18n/chunks/it-1.tsapp/src/lib/i18n/chunks/it-4.tsapp/src/lib/i18n/chunks/pt-1.tsapp/src/lib/i18n/chunks/pt-4.tsapp/src/lib/i18n/chunks/ru-1.tsapp/src/lib/i18n/chunks/ru-4.tsapp/src/lib/i18n/chunks/zh-CN-1.tsapp/src/lib/i18n/chunks/zh-CN-4.tsapp/src/lib/i18n/en.tsapp/src/services/api/__tests__/aiSettingsApi.test.tsapp/src/services/api/aiSettingsApi.tssrc/openhuman/inference/openai_oauth/store.rssrc/openhuman/inference/provider/factory.rs
✅ Files skipped from review due to trivial changes (13)
- app/src/lib/i18n/chunks/it-1.ts
- app/src/lib/i18n/chunks/en-4.ts
- app/src/lib/i18n/chunks/hi-4.ts
- app/src/lib/i18n/chunks/hi-1.ts
- app/src/lib/i18n/chunks/zh-CN-4.ts
- app/src/lib/i18n/chunks/fr-4.ts
- app/src/lib/i18n/chunks/en-1.ts
- app/src/lib/i18n/chunks/de-4.ts
- app/src/lib/i18n/chunks/bn-1.ts
- app/src/lib/i18n/chunks/id-1.ts
- app/src/lib/i18n/chunks/ar-1.ts
- app/src/lib/i18n/chunks/id-4.ts
- app/src/lib/i18n/chunks/ru-1.ts
🚧 Files skipped from review as they are similar to previous changes (4)
- app/src/services/api/aiSettingsApi.ts
- app/src/components/settings/panels/tests/AIPanel.test.tsx
- src/openhuman/inference/provider/factory.rs
- app/src/components/settings/panels/AIPanel.tsx
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/src/lib/i18n/chunks/ko-1.ts`:
- Around line 617-619: The two translation keys
'settings.ai.codexOauthMissingAuthUrl' and
'settings.ai.codexOauthMissingCallbackUrl' are duplicated across chunks; remove
one set so each key appears only once. Locate the duplicate key entries (the
strings 'settings.ai.codexOauthMissingAuthUrl' and
'settings.ai.codexOauthMissingCallbackUrl') and delete them from either ko-1.ts
or ko-4.ts, keeping the copy that best fits the feature grouping/organization;
ensure the remaining entry's text is correct and run the merge/build to verify
no duplicate-key warnings.
In `@app/src/lib/i18n/chunks/ko-4.ts`:
- Around line 279-281: Duplicate translation keys
'settings.ai.codexOauthMissingAuthUrl' and
'settings.ai.codexOauthMissingCallbackUrl' appear in multiple Korean chunks;
remove the duplicate definitions so each key exists exactly once—either delete
these two entries from the ko-4 chunk or remove the ones in ko-1 and keep them
where they best fit in your grouping convention (pick one canonical chunk for
AI/settings strings), then verify the merged locale contains only the retained
entries.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c95cc9a7-8b39-48c8-bf78-bfff1f162a11
📒 Files selected for processing (2)
app/src/lib/i18n/chunks/ko-1.tsapp/src/lib/i18n/chunks/ko-4.ts
There was a problem hiding this comment.
@junjunjunbong hey! The code looks solid to me — all of CodeRabbit's findings are properly addressed, the feature is well-tested, and the implementation is clean. However, CI is still running (coverage and E2E tests pending). Once those all pass, I'll circle back and approve this. Let me know if you hit any snags!
AI Summary
What it does: Adds Codex OAuth provider support to OpenAI — users can now authenticate via ChatGPT Codex by importing existing Codex CLI credentials (~/.codex/auth.json) instead of using API keys. The implementation adds new OAuth RPC operations, model catalog normalization (supports both OpenAI data and Codex models response formats), and structured error handling with localized messages.
Breaking risk: Low. New credential mode codex_oauth is opt-in. Model catalog parsing is backward-compatible (accepts both response shapes). No public API changes, no DB schema changes.
Security risk: Low. File I/O reads from local ~/.codex/auth.json, validates on read, no secrets logged, tokens stored via existing encrypted auth profiles. Windows home-dir fallback is correctly implemented. OAuth metadata is tolerant of broken profiles when API-key fallback exists.
Bottom line: Safe to merge once CI passes.
Verification Checklist
- Codex key clear moved after
persist()succeeds (safe transaction order) codexAuthErrorstate properly cleared on successful OpenAI connect- OAuth validation errors translated via error codes, not English strings
- Windows home-directory fallback (
HOME,USERPROFILE,HOMEDRIVE+HOMEPATH) - Structured diagnostics for Codex CLI import (grep-friendly logs)
- OAuth metadata lookup is tolerant when API-key routing is available
- Korean i18n duplicates removed from ko-1.ts, canonical entries in ko-4.ts
- Tests cover happy + failure paths, error localization verified
- No lint warnings, TypeScript checks pass
|
CI is now green on the latest head (2de8ab3). The earlier Rust Core Coverage failure was a GitHub runner disk issue (), so I pushed an empty rerun commit and the rerun passed, including diff-cover, Rust coverage, Tauri coverage, Linux/Windows/macOS E2E, build, and smoke checks.\n\nCould you please re-review/approve when you get a chance? |
|
CI is now green on the latest head (2de8ab3). The earlier Rust Core Coverage failure was a GitHub runner disk issue: No space left on device. I pushed an empty rerun commit and the rerun passed, including diff-cover, Rust coverage, Tauri coverage, Linux/Windows/macOS E2E, build, and smoke checks. Could you please re-review/approve when you get a chance? |
|
@junjunjunbong unresolved review feedback — please address before we review. |
graycyrus
left a comment
There was a problem hiding this comment.
Continuation review — my prior COMMENT deferred approval while CI was running. CI is now fully green across all jobs.
The new commit routes Codex OAuth inference through the Responses API. Changes are well-structured and tested.
What changed since last review:
responses_api_primaryflag routes all three chat paths (with history, without, streaming) directly to the Responses API for Codex OAuth, instead of first attempting chat completions. Clean separation — the flag is opt-in, so all other providers are unaffected.ResponsesContentPartproperly types message content. Theinput_text/output_textdistinction correctly maps user/tool messages as input and assistant messages as output, which is what the Responses API expects. Tool-role messages normalizing toassistantin the response format while gettinginput_textkind is correct.- Account ID extraction now checks the namespaced JWT claim
https://api.openai.com/auth.chatgpt_account_idbefore falling back tosub. This matches how Codex CLI JWTs are structured. merge_openai_codex_model_hintsinjects well-known Codex model IDs when the catalog doesn't return them. Deduplication is case-insensitive — correct.originatorheader and user agent added to model listing requests when using OAuth, matching what the Codex backend expects.
One thing worth a follow-up PR: the streaming branch for responses_api_primary calls chat_via_responses and emits the entire response as a single TextDelta. The Responses API supports SSE, so users on Codex OAuth will see a pause until the full response arrives rather than incremental output. Functional and safe, but not a great UX on long responses. Worth wiring up SSE for this path later.
Tests cover the new JWT claim shape, the responses_api_primary mock-server routing, model hint deduplication, and the updated Responses prompt content structure. Coverage gate passed.
Code is clean. Approving.
|
Unresolved review feedback from coderabbitai[bot] — please address before we review. |
Summary
~/.codex/auth.jsonand persists it as the OpenAI OAuth profile.{ data: [...] }and Codex-style{ models: [...] }model catalog responses.Problem
OpenAI provider setup could use API keys, but not the user's existing Codex/ChatGPT OAuth session. After adding the OAuth route, the Codex backend model catalog also rejected requests missing
client_version, then returned a{ models: [...] }payload that the OpenAI-compatible catalog parser did not accept.Solution
client_version.data[].id, Codexmodels[].slug, and string model entries.Submission Checklist
## Related— N/A: no coverage-matrix feature ID changed.docs/RELEASE-MANUAL-SMOKE.md) — N/A: targeted provider setup fix only.Closes #NNNin the## Relatedsection — N/A: user-reported local regression, no GitHub issue.Impact
Desktop app users can connect OpenAI through Codex OAuth and list/use Codex backend models without the
client_versionor missingdataarray failures. No migration is required; existing API-key OpenAI provider behavior is preserved.Related
AI Authored PR Metadata (required for Codex/Linear PRs)
Linear Issue
Commit & Branch
codex/add-codex-oauth-provider0db55dc6990c53469e3c6ee965f02ebd4621e922Validation Run
pnpm --filter openhuman-app exec vitest run --config test/vitest.config.ts src/components/settings/panels/__tests__/AIPanel.test.tsx src/services/api/__tests__/aiSettingsApi.test.tspnpm debug rust openai_oauthpnpm debug rust lookup_key_for_slug_routes_openai_oauth_lookup_pathpnpm debug rust model_catalog_accepts;pnpm debug rust openai_codex_models_url_includes_client_version_query;pnpm debug rust extra_query_params_are_applied_to_codex_urlspnpm i18n:checkpnpm lint(passes with existing warnings)pnpm typecheckpnpm format:checkwith Rust toolchain PATH configuredGGML_NATIVE=OFF cargo check --manifest-path Cargo.tomlformat:check,lint,compile,rust:checkValidation Blocked
command:pnpm i18n:bundle:checkerror:verify-i18n-bundle: dist directory does not exist or is not a directory: /Users/junwon/Projects/openhuman/app/distimpact:Bundle verification requires a builtapp/dist; i18n source coverage passed viapnpm i18n:check.Behavior Changes
client_versionor Codexmodelsresponse shape.Parity Contract
datamodel catalogs.dataormodelsarrays.Duplicate / Superseded PR Handling
junjunjunbong:codex/add-codex-oauth-provider.