Skip to content

feat: add OpenAI Codex OAuth login and Z.AI Coding Plan quick-setup#537

Merged
alchemistklk merged 4 commits intomainfrom
feat/openai-codex-oauth
Mar 25, 2026
Merged

feat: add OpenAI Codex OAuth login and Z.AI Coding Plan quick-setup#537
alchemistklk merged 4 commits intomainfrom
feat/openai-codex-oauth

Conversation

@alchemistklk
Copy link
Copy Markdown
Contributor

@alchemistklk alchemistklk commented Mar 25, 2026

Closes #412, closes #438

Summary

  • Add "Login with ChatGPT" OAuth button on the OpenAI provider settings page — users with ChatGPT Plus/Pro subscriptions can use GPT models without buying separate API keys
  • Add Z.AI Coding Plan quick-setup on the GLM provider panel with Global/CN region toggle
  • Fix auth profiles writer to preserve OAuth tokens during config sync

What changed

OpenAI Codex OAuth

  • New OpenClawAuthService — handles PKCE OAuth flow with auth.openai.com
  • Writes OAuth tokens directly to OpenClaw's auth-profiles.json for auto token refresh
  • Config compiler maps openai/*openai-codex/* when provider is OAuth-connected
  • Runtime model resolver trusts openai-codex/ model refs from auth-profiles
  • 4 new API routes: /oauth/start, /oauth/status, /oauth/provider-status, /oauth/disconnect

Z.AI Coding Plan

  • Quick-setup section with API key input and Global/CN region toggle
  • Global: api.z.ai/api/coding/paas/v4 / CN: open.bigmodel.cn/api/coding/paas/v4
  • Known free models: glm-5, glm-4.7, glm-4.7-flash, glm-4.7-flashx

Auth profiles writer

  • Changed from overwrite to read-merge-write to preserve OAuth profiles during sync

Test plan

  • pnpm typecheck — all 4 projects pass
  • pnpm lint — no errors in changed files
  • pnpm test — 135/135 tests pass (15 new)
  • Manual: Login with ChatGPT → select gpt-5.4 → verify model switches correctly
  • Manual: GLM Coding Plan → enter key with Global/CN toggle → verify models saved
  • Playwright E2E specs at apps/web/e2e/openai-oauth.spec.ts (mocked)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • ChatGPT (OpenAI) OAuth: start, poll status, provider connection-status, and disconnect; automatic provisioning of known models.
    • Z.AI Coding Plan quick-setup for GLM providers; updated default model lists (includes gpt-5.4).
  • UX

    • Connected state hides API-key input, shows connected banner; polling, success and error flows surfaced.
  • Validation

    • Stricter slug pattern for SkillHub inputs/paths.
    • Provider API key can be explicitly cleared (null).
  • Tests

    • New unit and E2E tests covering OAuth flows.
  • Documentation

    • Added OAuth model persistence and verification plans.

Add alternative authentication methods on the provider settings page:

**OpenAI Codex OAuth:**
- "Login with ChatGPT" button on the OpenAI provider panel
- PKCE OAuth flow via auth.openai.com (fixed port 1455)
- Writes OAuth tokens to OpenClaw's auth-profiles.json
- OpenClaw auto-refreshes tokens — no Nexu refresh timer needed
- Config compiler maps openai/* → openai-codex/* for OAuth users
- Runtime model resolver trusts openai-codex/ refs from auth-profiles

**Z.AI Coding Plan:**
- Quick-setup section on GLM provider panel with region toggle (Global/CN)
- Global: api.z.ai/api/coding/paas/v4
- CN: open.bigmodel.cn/api/coding/paas/v4
- Pre-fills base URL and known free models (glm-5, glm-4.7, etc.)

**Auth profiles writer fix:**
- Read-merge-write instead of overwrite to preserve OAuth profiles
- Prevents sync loop from destroying OpenClaw-managed tokens

Includes unit tests, Playwright E2E specs, and test plan document.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 25, 2026

📝 Walkthrough

Walkthrough

Adds OpenAI Codex OAuth: new controller routes, an OpenClawAuthService implementing PKCE + local callback, persistence/merge of OAuth profiles into per-agent auth stores, OAuth-aware model resolution and sync, frontend SDK/types/UI updates, tests, and schema/validation tweaks (nullable provider apiKey, stricter SkillHub slug pattern).

Changes

Cohort / File(s) Summary
API spec & validation
apps/controller/openapi.json, packages/shared/src/schemas/provider.ts
Added provider OAuth endpoints and response schemas; made provider apiKey nullable; tightened SkillHub slug validation to regex.
Controller wiring
apps/controller/src/app/container.ts, apps/controller/src/app/create-app.ts
Added OpenClawAuthService to container and lifecycle; registered provider OAuth routes during app creation.
Provider OAuth routes
apps/controller/src/routes/provider-oauth-routes.ts
New routes: POST /.../oauth/start, GET /.../oauth/status, GET /.../oauth/provider-status, POST /.../oauth/disconnect with flow consumption, provider upsert, and sync trigger logic.
Auth service & persistence
apps/controller/src/services/openclaw-auth-service.ts, apps/controller/src/runtime/openclaw-auth-profiles-store.ts, apps/controller/src/runtime/openclaw-auth-profiles-writer.ts
New OpenClawAuthService (PKCE, local callback server, flow lifecycle, profile merge/consume/disconnect) and a store + writer for reading/updating per-agent auth-profiles.json.
Model resolution & sync
apps/controller/src/lib/openclaw-config-compiler.ts, apps/controller/src/services/openclaw-sync-service.ts
Made model resolution OAuth-aware (new OAuth state threading and OAuth-scoped model id mapping); adjusted runtime model selection to respect OAuth prefixes.
Config store behavior
apps/controller/src/store/nexu-config-store.ts, apps/controller/tests/nexu-config-store.test.ts
upsertProvider now distinguishes undefined vs explicit null for apiKey; added test verifying clearing apiKey with null.
Frontend: SDK/types/UI/i18n/tests
apps/web/lib/api/sdk.gen.ts, apps/web/lib/api/types.gen.ts, apps/web/src/pages/models.tsx, apps/web/src/i18n/locales/en.ts, apps/web/src/i18n/locales/zh-CN.ts, apps/web/e2e/openai-oauth.spec.ts
Added SDK calls and types for new endpoints; integrated OAuth start/status/disconnect into models UI; added i18n strings and Playwright E2E coverage.
Tests: controller & integration
tests/controller/openclaw-auth-service.test.ts, apps/controller/tests/* (openclaw-auth-*, provider-oauth-routes.test.ts, openclaw-auth-profiles-store.test.ts, openclaw-config-compiler.test.ts, openclaw-sync.test.ts, route-compat.test.ts)
Added/updated unit and integration tests for OpenClawAuthService, profiles store/writer, provider-oauth routes, and OAuth-aware config/compiler behavior.
Docs / plans
docs/plans/2026-03-24-oauth-model-persistence.md, docs/plans/2026-03-24-openai-oauth-test-plan.md
Added implementation and verification plans documenting OAuth model persistence and test coverage.

Sequence Diagram(s)

sequenceDiagram
participant WebUI
participant Controller
participant OpenClawAuthService as AuthSvc
participant OpenAI
participant FS as "Agent FS"
participant ModelSvc
participant SyncSvc

WebUI->>Controller: POST /api/v1/providers/{id}/oauth/start
Controller->>AuthSvc: startOAuthFlow(providerId)
AuthSvc-->>Controller: { browserUrl }
Controller-->>WebUI: { browserUrl }

note over WebUI,OpenAI: User authenticates in browser

OpenAI->>AuthSvc: GET /auth/callback?code=...&state=...
AuthSvc->>AuthSvc: validate state, exchange code (token endpoint)
AuthSvc->>FS: merge/write OAuth profile into agent auth-profiles.json
AuthSvc-->>Controller: mark flow completed

loop polling
    WebUI->>Controller: GET /api/v1/providers/{id}/oauth/status
    Controller->>AuthSvc: getFlowStatus()
    AuthSvc-->>Controller: { status:"completed", models:[...] }
    Controller->>ModelSvc: upsertProvider(... apiKey: null, modelsJson ...)
    Controller->>SyncSvc: syncAll()
    Controller-->>WebUI: { status:"completed", models:[...] }
end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • mrcfps
  • nettee
  • Siri-Ray

Poem

"I hopped with keys and tiny grace,
Spawned PKCE, a callback place.
Profiles merged in cozy files,
Models mapped across the miles.
Hooray — OAuth stitched with rabbit smiles 🐰"

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.16% 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
Title check ✅ Passed The title 'feat: add OpenAI Codex OAuth login and Z.AI Coding Plan quick-setup' accurately summarizes the two main features introduced in this changeset.
Description check ✅ Passed The description covers all required sections: What (one-liner and summary), Why (problem solved), How (implementation details), Affected areas (Controller, Web, Shared), Checklist status, and Test plan with specific manual verification steps.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/openai-codex-oauth

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 722c46a4ff

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/controller/src/routes/provider-oauth-routes.ts
Comment thread apps/controller/src/services/openclaw-auth-service.ts Outdated
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.

Actionable comments posted: 10

🧹 Nitpick comments (2)
apps/controller/src/services/openclaw-sync-service.ts (1)

59-61: Keep the OAuth provider mapping in one source of truth.

OAUTH_PROVIDER_PREFIXES here now has to stay aligned with OAUTH_PROVIDER_MAP in apps/controller/src/lib/openclaw-config-compiler.ts:265-267. If one side changes without the other, compile-time and runtime model resolution will drift silently. Please extract the mapping into a shared helper/constant.

Also applies to: 71-75

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

In `@apps/controller/src/services/openclaw-sync-service.ts` around lines 59 - 61,
OAUTH_PROVIDER_PREFIXES is duplicated and must be the single source of truth;
extract the mapping into a shared constant (e.g., export const
OAUTH_PROVIDER_PREFIXES or OAUTH_PROVIDER_MAP in a new shared module like
openclaw-oauth-constants) and replace the local definitions in both
openclaw-sync-service.ts (const OAUTH_PROVIDER_PREFIXES) and
openclaw-config-compiler.ts (OAUTH_PROVIDER_MAP) to import and use that shared
export so runtime and compile-time model resolution stay in sync; ensure the
exported name is descriptive and update all references in both files to use the
imported symbol.
docs/plans/2026-03-24-oauth-model-persistence.md (1)

11-11: Prefer a repo-relative worktree reference instead of an absolute local path.

Line 11 hardcodes a machine-specific path, which is not reusable by other contributors.

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

In `@docs/plans/2026-03-24-oauth-model-persistence.md` at line 11, Replace the
machine-specific absolute worktree path on line 11 with a repo-relative
reference; change the hardcoded
"/Users/alche/Documents/digit-sutando/nexu/.worktrees/openai-oauth" to a
relative path such as ".worktrees/openai-oauth" or "./.worktrees/openai-oauth"
and keep the branch name `feat/openai-codex-oauth` intact so contributors can
use the same worktree reference across machines.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/controller/src/lib/openclaw-config-compiler.ts`:
- Around line 263-275: The code currently treats provider.apiKey === null as
“OAuth connected” and rewrites openai → openai-codex; change this to check a
persisted OAuth connection flag instead. In the block that uses
OAUTH_PROVIDER_MAP, oauthTarget and provider (from config.providers.find by
providerId), replace the apiKey === null gate with a check for a persisted
connection indicator on the provider (for example provider.oauthConnected ===
true or provider.connection?.profileId != null or provider.profile?.id != null
depending on your model for persisted OAuth state). Ensure you only return
`${oauthTarget}/${modelSuffix}` when provider.enabled is true AND the provider
has that persisted OAuth connection flag set (not merely apiKey === null).

In `@apps/controller/src/routes/provider-oauth-routes.ts`:
- Around line 67-81: Don't call container.openclawAuthService.consumeCompleted()
before persisting the provider and syncing; consumeCompleted() clears the
in-memory completion state too early. Change the flow in the completed branch so
you first compute models (using completed.models or OPENAI_CODEX_KNOWN_MODELS),
then call container.modelProviderService.upsertProvider(providerId, ...) and
await container.openclawSyncService.syncAll(), and only after both succeed call
container.openclawAuthService.consumeCompleted() and return c.json({
...flowStatus, models }, 200); alternatively implement a transactional
consumeCompleted() variant that only clears state after upsertProvider and
syncAll complete successfully.

In `@apps/controller/src/runtime/openclaw-auth-profiles-writer.ts`:
- Around line 19-45: readExistingProfiles currently swallows all read/parse
errors which can turn transient partial reads into full clobbers; change
readExistingProfiles to only return null for ENOENT and to rethrow other errors
(including JSON.parse failures) so callers can decide how to proceed. Add a
shared per-file lock/single-writer helper (e.g., withFileLock or a Mutex keyed
by "auth-profiles.json") and use it from both writeForAgents and
OpenClawAuthService.mergeOAuthProfile so the read-modify-write cycle for
auth-profiles.json is serialized; move the read->modify->write logic inside the
locked callback and ensure mergeOAuthProfile and writeForAgents call that
helper.

In `@apps/controller/src/services/openclaw-auth-service.ts`:
- Around line 473-526: findAgentAuthProfilesPath currently returns the
auth-profiles.json for the "first directory wins" agent which makes
readAuthProfiles and writeAuthProfiles nondeterministic in multi-agent setups;
change the logic so the target agent is resolved explicitly (preferred) or
updates are fanned out to every agent directory: update
findAgentAuthProfilesPath to accept an agent identifier (or provide a new
findAllAgentAuthProfilesPaths helper) and have readAuthProfiles/readAuthProfiles
use that explicit path(s); alternatively implement a loop in writeAuthProfiles
that writes the same JSON to every path returned by a new
findAllAgentAuthProfilesPaths, and make readAuthProfiles deterministic by
reading a specified agent path or merging/validating all agent files
consistently.
- Around line 528-543: mergeOAuthProfile is doing an unlocked read-modify-write
(readAuthProfiles -> writeAuthProfiles) which races with the new sync writer;
serialize updates by acquiring the same global/shared writer lock or delegating
all mutations to the single-writer helper in openclaw-auth-profiles-writer
(instead of calling writeAuthProfiles directly). Wrap the entire
read/merge/write sequence in the shared lock API provided by
apps/controller/src/runtime/openclaw-auth-profiles-writer.ts (or call its single
writer method) so mergeOAuthProfile (function name) uses that lock/entrypoint
and prevents concurrent clobbering of auth-profiles.json.
- Around line 95-101: Validate the incoming state before processing the error
branch in the OAuth callback: in the callback handler, check that the request's
state matches the stored/expected state and reject or ignore the request (return
400) if it does not, before reading error_description; additionally,
sanitize/escape any user-provided text passed into errorHtml by HTML-escaping
characters (use a small utility that replaces &, <, >, ", ' with entities) and
pass the escaped string into errorHtml instead of raw error_description; apply
the same state-first validation and escaping to the other error-handling branch
in this file as well.
- Around line 242-251: When removing the profileKey ("openai-codex:default")
from existing.profiles in disconnect logic, also clear any stale lastGood
pointer that references that profile: check existing.lastGood and if it equals
profileKey (or if lastGood points to a profile key that no longer exists in
remainingProfiles) remove or set updated.lastGood to undefined/null so
AuthProfilesData stays consistent; update the same updated object created after
computing remainingProfiles (references: profileKey, existing,
remainingProfiles, updated, lastGood).

In `@apps/web/src/pages/models.tsx`:
- Around line 1285-1356: The queries and mutations currently coerce missing
backend responses into harmless defaults, which hides failures and can leave
oauthPending spinning; update the oauthProviderStatus and oauthFlowStatus
queryFn functions to treat missing or error responses as failures by throwing an
error when res.data is undefined or indicates an error (instead of returning
{connected:false} or {status:"idle"}), and update startOAuthMutation.mutationFn
and disconnectOAuthMutation.mutationFn to validate res.data and throw when the
response is missing or contains an error so React Query routes to onError rather
than onSuccess; keep the existing enabled/refetchInterval logic and ensure the
useEffect still reads oauthFlowStatus.data after failures are surfaced via
react-query errors.

In `@docs/plans/2026-03-24-oauth-model-persistence.md`:
- Line 15: Several task headings (for example "Task 1: Add `models` field to
OAuth status response schema" and the other task headings noted in the review)
are using level-3 headings (###) directly under the document's top-level #
header, causing a heading-level jump; change those task headings from ### to
level-2 (##) so the structure is # then ## for tasks, and scan the file for the
other task headings mentioned (the ones at the other commented locations) and
update them similarly to maintain consistent hierarchical markdown structure.

In `@docs/plans/2026-03-24-openai-oauth-test-plan.md`:
- Around line 35-36: Update the test plan so callback behavior no longer assumes
model-fetch during the OAuth callback: change table rows for cases 11 and 12 to
remove "Mock `fetch` for token + models endpoints" and instead assert that on a
valid callback the status becomes "completed" and the user's profile is
populated while models are NOT populated at callback (models are populated later
via the known-model persistence flow), and for the state-mismatch case assert
status "failed" with a CSRF error and no model-fetch attempt; apply the same
adjustments to the corresponding section covering lines 59-67 so all
expectations reflect the no-fetch callback flow.

---

Nitpick comments:
In `@apps/controller/src/services/openclaw-sync-service.ts`:
- Around line 59-61: OAUTH_PROVIDER_PREFIXES is duplicated and must be the
single source of truth; extract the mapping into a shared constant (e.g., export
const OAUTH_PROVIDER_PREFIXES or OAUTH_PROVIDER_MAP in a new shared module like
openclaw-oauth-constants) and replace the local definitions in both
openclaw-sync-service.ts (const OAUTH_PROVIDER_PREFIXES) and
openclaw-config-compiler.ts (OAUTH_PROVIDER_MAP) to import and use that shared
export so runtime and compile-time model resolution stay in sync; ensure the
exported name is descriptive and update all references in both files to use the
imported symbol.

In `@docs/plans/2026-03-24-oauth-model-persistence.md`:
- Line 11: Replace the machine-specific absolute worktree path on line 11 with a
repo-relative reference; change the hardcoded
"/Users/alche/Documents/digit-sutando/nexu/.worktrees/openai-oauth" to a
relative path such as ".worktrees/openai-oauth" or "./.worktrees/openai-oauth"
and keep the branch name `feat/openai-codex-oauth` intact so contributors can
use the same worktree reference across machines.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9a8cb3f9-1a3c-4160-8f3f-297e5dff4c56

📥 Commits

Reviewing files that changed from the base of the PR and between 6a41469 and 722c46a.

📒 Files selected for processing (18)
  • apps/controller/openapi.json
  • apps/controller/src/app/container.ts
  • apps/controller/src/app/create-app.ts
  • apps/controller/src/lib/openclaw-config-compiler.ts
  • apps/controller/src/routes/provider-oauth-routes.ts
  • apps/controller/src/runtime/openclaw-auth-profiles-writer.ts
  • apps/controller/src/services/openclaw-auth-service.ts
  • apps/controller/src/services/openclaw-sync-service.ts
  • apps/web/e2e/openai-oauth.spec.ts
  • apps/web/lib/api/sdk.gen.ts
  • apps/web/lib/api/types.gen.ts
  • apps/web/src/i18n/locales/en.ts
  • apps/web/src/i18n/locales/zh-CN.ts
  • apps/web/src/pages/models.tsx
  • docs/plans/2026-03-24-oauth-model-persistence.md
  • docs/plans/2026-03-24-openai-oauth-test-plan.md
  • packages/shared/src/schemas/provider.ts
  • tests/controller/openclaw-auth-service.test.ts

Comment thread apps/controller/src/lib/openclaw-config-compiler.ts Outdated
Comment thread apps/controller/src/routes/provider-oauth-routes.ts
Comment thread apps/controller/src/runtime/openclaw-auth-profiles-writer.ts Outdated
Comment thread apps/controller/src/services/openclaw-auth-service.ts
Comment thread apps/controller/src/services/openclaw-auth-service.ts Outdated
Comment thread apps/controller/src/services/openclaw-auth-service.ts Outdated
Comment thread apps/controller/src/services/openclaw-auth-service.ts Outdated
Comment thread apps/web/src/pages/models.tsx
Comment thread docs/plans/2026-03-24-oauth-model-persistence.md
Comment thread docs/plans/2026-03-24-openai-oauth-test-plan.md
Address Codex review findings:

- Explicitly pass apiKey: null when OAuth completes so any previously
  saved API key is cleared. Updated config store merge to treat null
  as an explicit clear instead of "keep existing".

- Auth service now reads/writes/disconnects OAuth profiles across all
  agent workspace directories, not just the first one found. Prevents
  bot-specific auth failures in multi-bot setups.

- Added regression tests for both fixes.
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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/controller/tests/provider-oauth-routes.test.ts (1)

8-55: Consider extracting shared test utilities.

The createEnv helper is duplicated between this file and openclaw-auth-service.test.ts. Consider extracting it to a shared test fixture file (e.g., apps/controller/tests/fixtures/test-env.ts) to reduce maintenance burden and ensure consistency.

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

In `@apps/controller/tests/provider-oauth-routes.test.ts` around lines 8 - 55, The
createEnv helper is duplicated across tests; extract it into a single shared
test fixture (e.g., export function createEnv(rootDir: string): ControllerEnv
from a new tests/fixtures/test-env module), export the ControllerEnv type if
needed, and replace the duplicate implementations in provider-oauth-routes.test
and openclaw-auth-service.test with an import of the shared createEnv function;
ensure both tests update their imports to reference the new fixture and that the
function signature and returned keys remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/controller/openapi.json`:
- Around line 4848-4850: The OpenAPI 3.1 schema for the apiKey property still
uses the removed "nullable": true; update the apiKey schema (the "apiKey"
property in the OpenAPI document) to express nullability using a union type by
replacing "type": "string" and "nullable": true with a JSON Schema
3.1-compatible null union such as "type": ["string","null"] so SDKs will honor
sending null to clear the saved key.

---

Nitpick comments:
In `@apps/controller/tests/provider-oauth-routes.test.ts`:
- Around line 8-55: The createEnv helper is duplicated across tests; extract it
into a single shared test fixture (e.g., export function createEnv(rootDir:
string): ControllerEnv from a new tests/fixtures/test-env module), export the
ControllerEnv type if needed, and replace the duplicate implementations in
provider-oauth-routes.test and openclaw-auth-service.test with an import of the
shared createEnv function; ensure both tests update their imports to reference
the new fixture and that the function signature and returned keys remain
unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4eec8ea3-18af-4919-9516-4eee20a00407

📥 Commits

Reviewing files that changed from the base of the PR and between 722c46a and 01e8b1e.

📒 Files selected for processing (8)
  • apps/controller/openapi.json
  • apps/controller/src/routes/provider-oauth-routes.ts
  • apps/controller/src/services/openclaw-auth-service.ts
  • apps/controller/src/store/nexu-config-store.ts
  • apps/controller/tests/nexu-config-store.test.ts
  • apps/controller/tests/openclaw-auth-service.test.ts
  • apps/controller/tests/provider-oauth-routes.test.ts
  • packages/shared/src/schemas/provider.ts
✅ Files skipped from review due to trivial changes (1)
  • apps/controller/tests/nexu-config-store.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/controller/src/routes/provider-oauth-routes.ts
  • packages/shared/src/schemas/provider.ts
  • apps/controller/src/services/openclaw-auth-service.ts

Comment thread apps/controller/openapi.json
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Mar 25, 2026

Deploying nexu-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: c482fef
Status: ✅  Deploy successful!
Preview URL: https://4079c61a.nexu-docs.pages.dev
Branch Preview URL: https://feat-openai-codex-oauth.nexu-docs.pages.dev

View logs

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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
apps/web/src/pages/models.tsx (1)

1328-1337: Consider adding providerId to the effect's dependency array.

The effect relies on meta.defaultProxyUrl changing to trigger reset when the provider changes. While this works because each provider has a unique defaultProxyUrl, explicitly including providerId would make the intent clearer and avoid potential issues if two providers ever share the same URL.

Suggested fix
   useEffect(() => {
     setApiKey("");
     setBaseUrl(dbProvider?.baseUrl ?? meta.defaultProxyUrl ?? "");
     setIsEditingApiKey(!dbProvider?.hasApiKey);
     setVerifiedModels(null);
     setOauthPending(false);
     setCodingPlanKey("");
     setCodingPlanRegion("global");
-  }, [dbProvider, meta.defaultProxyUrl]);
+  }, [providerId, dbProvider, meta.defaultProxyUrl]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/pages/models.tsx` around lines 1328 - 1337, The reset useEffect
that calls setApiKey, setBaseUrl, setIsEditingApiKey, setVerifiedModels,
setOauthPending, setCodingPlanKey, and setCodingPlanRegion currently depends on
[dbProvider, meta.defaultProxyUrl]; update the dependency array to also include
providerId so the effect explicitly re-runs when the selected provider changes
(keeps the same reset logic in the useEffect tied to dbProvider,
meta.defaultProxyUrl, and providerId).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/pages/models.tsx`:
- Around line 1273-1279: The onSuccess callback currently calls
window.open(data.browserUrl, "_blank") in the OAuth flow; update that call to
pass the third feature string "noopener,noreferrer" (i.e.,
window.open(data.browserUrl, "_blank", "noopener,noreferrer")) to prevent the
opened page from accessing window.opener, leaving the rest of the logic
(setOauthPending(true) and toast.error(data.error)) unchanged; locate this
change in the onSuccess handler where window.open, setOauthPending, and
toast.error are referenced.
- Around line 1307-1326: The saveCodingPlanMutation currently has no error
handler so failures from saveProvider leave the UI silent; add an onError
handler on saveCodingPlanMutation that captures the error from saveProvider and
surfaces user feedback (e.g., set an error state or call a toast/notification)
and ensures UI state cleanup (e.g., do not clear codingPlanKey or call
markSetupComplete/onAutoSelectModel on failure). Locate saveCodingPlanMutation
and implement onError to accept the error, log/report it, update any local error
state or show a notification, and avoid running the success-side effects
(setCodingPlanKey, markSetupComplete, onAutoSelectModel) when an error occurs.

---

Nitpick comments:
In `@apps/web/src/pages/models.tsx`:
- Around line 1328-1337: The reset useEffect that calls setApiKey, setBaseUrl,
setIsEditingApiKey, setVerifiedModels, setOauthPending, setCodingPlanKey, and
setCodingPlanRegion currently depends on [dbProvider, meta.defaultProxyUrl];
update the dependency array to also include providerId so the effect explicitly
re-runs when the selected provider changes (keeps the same reset logic in the
useEffect tied to dbProvider, meta.defaultProxyUrl, and providerId).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3cd105d2-f79d-4a92-9d6f-db0bad236bc5

📥 Commits

Reviewing files that changed from the base of the PR and between 01e8b1e and c482fef.

📒 Files selected for processing (6)
  • apps/controller/src/app/create-app.ts
  • apps/controller/src/lib/openclaw-config-compiler.ts
  • apps/controller/src/store/nexu-config-store.ts
  • apps/web/src/i18n/locales/en.ts
  • apps/web/src/i18n/locales/zh-CN.ts
  • apps/web/src/pages/models.tsx
✅ Files skipped from review due to trivial changes (2)
  • apps/web/src/i18n/locales/en.ts
  • apps/web/src/i18n/locales/zh-CN.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/controller/src/app/create-app.ts
  • apps/controller/src/store/nexu-config-store.ts
  • apps/controller/src/lib/openclaw-config-compiler.ts

Comment thread apps/web/src/pages/models.tsx
Comment thread apps/web/src/pages/models.tsx
Replace implicit apiKey === null detection with explicit
OAuthConnectionState derived from auth-profiles.json files.

- New OpenClawAuthProfilesStore: shared serialized store for
  reading/writing auth profiles across all agent workspaces
- OAuthConnectionState passed into config compiler and sync service
- Model ID remap (openai → openai-codex) now gated by actual
  connected OAuth profiles, not inferred from missing API key
- Per-file serial queue prevents race conditions on concurrent writes
- Malformed auth-profiles.json treated as hard errors instead of
  silently rebuilding from empty
- Regression tests for store, compiler, and route behavior
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.

Actionable comments posted: 1

🧹 Nitpick comments (2)
apps/controller/tests/openclaw-config-compiler.test.ts (1)

223-248: Consider extracting a shared OpenAI config fixture.

Both tests construct nearly identical config payloads. A small helper would reduce duplication and future drift.

Also applies to: 265-288

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

In `@apps/controller/tests/openclaw-config-compiler.test.ts` around lines 223 -
248, Tests around compileOpenClawConfig and createConfig duplicate nearly
identical OpenAI config payloads (bots, runtime.defaultModelId,
providers.models, etc.); extract a shared fixture/helper (e.g.,
createOpenAIConfigFixture or openaiConfigPayload) that builds the common config
and accept small overrides for bot/model/provider differences, then replace the
duplicated inline objects in the tests with calls to that helper to reduce
duplication and future drift.
apps/controller/src/services/openclaw-sync-service.ts (1)

62-78: Consider extracting OAUTH_PROVIDER_PREFIXES from the canonical OAUTH_PROVIDER_MAP.

The OAUTH_PROVIDER_PREFIXES array duplicates knowledge of OAuth provider output prefixes that's already defined in OAUTH_PROVIDER_MAP within openclaw-config-compiler.ts. If additional OAuth providers are added to the map (e.g., google: "google-oauth"), this array must be updated manually.

♻️ Proposed approach

Export the map values from the compiler and derive prefixes:

// In openclaw-config-compiler.ts, export:
export const OAUTH_PROVIDER_MAP: Record<string, string> = {
  openai: "openai-codex",
};

// In openclaw-sync-service.ts:
import { OAUTH_PROVIDER_MAP } from "../lib/openclaw-config-compiler.js";

const OAUTH_PROVIDER_PREFIXES = Object.values(OAUTH_PROVIDER_MAP).map(
  (p) => `${p}/`,
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/controller/src/services/openclaw-sync-service.ts` around lines 62 - 78,
Replace the hard-coded OAUTH_PROVIDER_PREFIXES in openclaw-sync-service.ts with
a derived list from the canonical OAUTH_PROVIDER_MAP exported from
openclaw-config-compiler.ts: export OAUTH_PROVIDER_MAP (if not already exported)
from openclaw-config-compiler.ts, import it into openclaw-sync-service.ts, and
compute OAUTH_PROVIDER_PREFIXES as Object.values(OAUTH_PROVIDER_MAP).map(p =>
`${p}/`) so resolveAvailableRuntimeModel uses the authoritative prefixes instead
of a duplicated array.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/controller/src/runtime/openclaw-auth-profiles-store.ts`:
- Around line 99-112: readAuthProfiles currently only swallows missing-file
errors but lets parse errors from parseAuthProfilesData propagate; update
readAuthProfiles to catch JSON/parse errors and, when options?.missingOk is
true, return null instead of rethrowing. Specifically, wrap the readFile +
parseAuthProfilesData call in a try/catch, keep the existing isMissingFileError
check, and add a check for parse failures (e.g., any non-missing error from
parseAuthProfilesData) to return null when options?.missingOk is true; otherwise
rethrow. This targets the readAuthProfiles function and prevents uncaught parse
errors for callers (e.g., openclaw-auth-service.ts) that rely on missingOk.

---

Nitpick comments:
In `@apps/controller/src/services/openclaw-sync-service.ts`:
- Around line 62-78: Replace the hard-coded OAUTH_PROVIDER_PREFIXES in
openclaw-sync-service.ts with a derived list from the canonical
OAUTH_PROVIDER_MAP exported from openclaw-config-compiler.ts: export
OAUTH_PROVIDER_MAP (if not already exported) from openclaw-config-compiler.ts,
import it into openclaw-sync-service.ts, and compute OAUTH_PROVIDER_PREFIXES as
Object.values(OAUTH_PROVIDER_MAP).map(p => `${p}/`) so
resolveAvailableRuntimeModel uses the authoritative prefixes instead of a
duplicated array.

In `@apps/controller/tests/openclaw-config-compiler.test.ts`:
- Around line 223-248: Tests around compileOpenClawConfig and createConfig
duplicate nearly identical OpenAI config payloads (bots, runtime.defaultModelId,
providers.models, etc.); extract a shared fixture/helper (e.g.,
createOpenAIConfigFixture or openaiConfigPayload) that builds the common config
and accept small overrides for bot/model/provider differences, then replace the
duplicated inline objects in the tests with calls to that helper to reduce
duplication and future drift.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6393f065-13aa-46cc-aed1-d35926fe8232

📥 Commits

Reviewing files that changed from the base of the PR and between c482fef and ca06c36.

📒 Files selected for processing (10)
  • apps/controller/src/app/container.ts
  • apps/controller/src/lib/openclaw-config-compiler.ts
  • apps/controller/src/runtime/openclaw-auth-profiles-store.ts
  • apps/controller/src/runtime/openclaw-auth-profiles-writer.ts
  • apps/controller/src/services/openclaw-auth-service.ts
  • apps/controller/src/services/openclaw-sync-service.ts
  • apps/controller/tests/openclaw-auth-profiles-store.test.ts
  • apps/controller/tests/openclaw-config-compiler.test.ts
  • apps/controller/tests/openclaw-sync.test.ts
  • apps/controller/tests/route-compat.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/controller/src/app/container.ts
  • apps/controller/src/lib/openclaw-config-compiler.ts
  • apps/controller/src/services/openclaw-auth-service.ts

Comment thread apps/controller/src/runtime/openclaw-auth-profiles-store.ts
@alchemistklk
Copy link
Copy Markdown
Contributor Author

/cr

@slack-code-review-channel
Copy link
Copy Markdown

✅ CR topic created in Feishu topic group Refly CR.

@alchemistklk alchemistklk merged commit fdb0442 into main Mar 25, 2026
7 checks passed
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.

Support Coding Plan Please OAuth authorization support for Codex

2 participants