fix: OAuth model cleanup, skill nav, UI polish#551
Conversation
- OAuth disconnect now deletes provider model list and syncs config, so models don't linger in the selector after revoking OAuth - OAuth-expired providers' models are excluded from listModels() - Skill detail "Back to Skills" uses navigate(-1) to return to the previous page instead of always resetting to the initial tab - Skills page persists active tab (Yours/ClawHub) in URL search params so navigating back preserves the selected tab - GitHub import tab input disabled (coming soon) - Added space between "GitHub Issues" link and "反馈" in disclaimer
📝 WalkthroughWalkthroughWires OpenClawAuthService into ModelProviderService, adds OAuth-aware filtering of provider models, runs conditional cleanup and resync on OAuth disconnect, updates tests for disconnect behavior, makes GitHub URL input read-only in the import modal, and drives Skills page top tab from the URL query string. Changes
Sequence DiagramsequenceDiagram
participant User
participant Route as POST /api/v1/providers/{id}/oauth/disconnect
participant AuthSvc as OpenClawAuthService
participant ModelSvc as ModelProviderService
participant SyncSvc as OpenClawSyncService
User->>Route: request disconnect(providerId)
Route->>AuthSvc: getProviderOAuthStatus(providerId)
AuthSvc-->>Route: {connected: true/false}
Route->>AuthSvc: disconnectOAuth(providerId)
AuthSvc-->>Route: {ok: true/false}
alt wasConnected AND ok:true
Route->>ModelSvc: deleteProvider(providerId)
ModelSvc-->>Route: {ok:true}
Route->>ModelSvc: ensureValidDefaultModel()
ModelSvc-->>Route: {ok}
Route->>SyncSvc: syncAll()
SyncSvc-->>Route: {ok}
Route-->>User: {ok: true}
else
Route-->>User: {ok: false} or {ok: true} (no cleanup)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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 |
…red-provider filtering
Deploying nexu-docs with
|
| Latest commit: |
5457a3d
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://428d017f.nexu-docs.pages.dev |
| Branch Preview URL: | https://fix-multi-bugfix-batch.nexu-docs.pages.dev |
|
/cr |
|
✅ CR topic created in Feishu topic group Refly CR. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 102eaeaa03
ℹ️ 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".
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
apps/web/src/pages/skills.tsx (1)
268-273: Consider using functional update to preserve other search params.The current implementation replaces all search params when switching tabs. If other params are added later (e.g., filters, pagination), they would be lost when toggling tabs.
♻️ Suggested safer pattern
const [searchParams, setSearchParams] = useSearchParams(); const topTab: TopTab = searchParams.get("tab") === "explore" ? "explore" : "yours"; const setTopTab = (tab: TopTab) => { - setSearchParams(tab === "yours" ? {} : { tab }, { replace: true }); + setSearchParams((prev) => { + const next = new URLSearchParams(prev); + if (tab === "yours") { + next.delete("tab"); + } else { + next.set("tab", tab); + } + return next; + }, { replace: true }); };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/pages/skills.tsx` around lines 268 - 273, The setTopTab implementation currently calls setSearchParams with a new object and clobbers any existing query parameters; update it to use the functional form of setSearchParams so you can read and mutate the existing URLSearchParams instead of replacing them wholesale: inside setTopTab (and keep topTab and useSearchParams unchanged) call setSearchParams(prev => { const params = new URLSearchParams(prev); if (tab === "yours") params.delete("tab"); else params.set("tab", "explore"); return params; }, { replace: true }) so other params (filters, page, etc.) are preserved when toggling tabs.apps/controller/tests/provider-oauth-routes.test.ts (1)
202-219: Assert the rest of the cleanup path stays untouched onok: false.This failure-path test only guards
deleteProvider(). If the handler regresses and still callsensureValidDefaultModel()orsyncAll(), this case stays green.✅ Tighten the failure-path assertions
await expect(response.json()).resolves.toEqual({ ok: false }); expect( container.modelProviderService.deleteProvider, ).not.toHaveBeenCalled(); + expect( + container.modelProviderService.ensureValidDefaultModel, + ).not.toHaveBeenCalled(); + expect(container.openclawSyncService.syncAll).not.toHaveBeenCalled();🤖 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 202 - 219, Tighten the failure-path test so it also asserts other cleanup routines are not invoked when disconnectOAuth resolves to false: after the existing expects add assertions that container.modelProviderService.ensureValidDefaultModel and the sync method (container.providerSyncService.syncAll) were not called; this ensures the handler doesn't call ensureValidDefaultModel() or syncAll() on an OAuth disconnect failure.
🤖 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/routes/provider-oauth-routes.ts`:
- Around line 140-145: After disconnectOAuth() returns true, make the cleanup
calls (container.modelProviderService.deleteProvider(providerId),
container.modelProviderService.ensureValidDefaultModel(),
container.openclawSyncService.syncAll()) failure-safe so they cannot cause the
handler to return 500; wrap them so errors are caught and logged but not
rethrown (for example use Promise.allSettled([...]) or individual try/catch
blocks and processLogger.error on failure), ensuring the auth state removal
remains final and the HTTP response is not impacted by post-disconnect cleanup
errors.
---
Nitpick comments:
In `@apps/controller/tests/provider-oauth-routes.test.ts`:
- Around line 202-219: Tighten the failure-path test so it also asserts other
cleanup routines are not invoked when disconnectOAuth resolves to false: after
the existing expects add assertions that
container.modelProviderService.ensureValidDefaultModel and the sync method
(container.providerSyncService.syncAll) were not called; this ensures the
handler doesn't call ensureValidDefaultModel() or syncAll() on an OAuth
disconnect failure.
In `@apps/web/src/pages/skills.tsx`:
- Around line 268-273: The setTopTab implementation currently calls
setSearchParams with a new object and clobbers any existing query parameters;
update it to use the functional form of setSearchParams so you can read and
mutate the existing URLSearchParams instead of replacing them wholesale: inside
setTopTab (and keep topTab and useSearchParams unchanged) call
setSearchParams(prev => { const params = new URLSearchParams(prev); if (tab ===
"yours") params.delete("tab"); else params.set("tab", "explore"); return params;
}, { replace: true }) so other params (filters, page, etc.) are preserved when
toggling tabs.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 49bce8b1-5679-4b8f-becb-64700a915c09
📒 Files selected for processing (7)
apps/controller/src/app/container.tsapps/controller/src/routes/provider-oauth-routes.tsapps/controller/src/services/model-provider-service.tsapps/controller/tests/provider-oauth-routes.test.tsapps/web/src/components/skills/import-skill-modal.tsxapps/web/src/pages/skills.tsxtests/controller/provider-oauth-routes.test.ts
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/controller/src/services/model-provider-service.ts (1)
259-260: Avoid a third hardcoded OAuth provider registry.
"openai"is already encoded inapps/controller/src/runtime/openclaw-auth-profiles-store.ts:16-18andapps/controller/src/lib/openclaw-config-compiler.ts:37-39, while this file also has separate MiniMax OAuth handling. Pull this from a shared source—or rename it to the narrower concept it actually represents—so provider capability data does not drift across files.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/controller/src/services/model-provider-service.ts` around lines 259 - 260, Replace the hardcoded OAUTH_PROVIDER_IDS set with the canonical/shared source or narrow its meaning: either import the central OAuth-capability registry used elsewhere and replace the local constant OAUTH_PROVIDER_IDS with that shared symbol, or if this set only reflects MiniMax-specific behavior, rename OAUTH_PROVIDER_IDS to a narrower identifier (e.g., MINIMAX_OAUTH_PROVIDER_IDS) and update usages in the model-provider-service methods to match; ensure you remove the duplicated literal array and reference the single source of truth so provider capability data no longer drifts across files.
🤖 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/services/model-provider-service.ts`:
- Around line 359-377: The current getExpiredOAuthProviderIds marks providers as
expired whenever openclawAuthService.getProviderOAuthStatus returns connected:
false, but that conflates transient read errors with real expirations; modify
getExpiredOAuthProviderIds so it only treats a provider as expired when the auth
status explicitly signals expiration (e.g. status.isExpired === true or a
dedicated status.type === 'expired') and do not add provider.providerId on
transient errors or generic connected: false responses—catch thrown errors from
OpenClawAuthService.getProviderOAuthStatus and treat them as non-expired (or log
and continue), and update callers like ensureValidDefaultModel to rely on this
explicit isExpired signal rather than plain connected: false.
---
Nitpick comments:
In `@apps/controller/src/services/model-provider-service.ts`:
- Around line 259-260: Replace the hardcoded OAUTH_PROVIDER_IDS set with the
canonical/shared source or narrow its meaning: either import the central
OAuth-capability registry used elsewhere and replace the local constant
OAUTH_PROVIDER_IDS with that shared symbol, or if this set only reflects
MiniMax-specific behavior, rename OAUTH_PROVIDER_IDS to a narrower identifier
(e.g., MINIMAX_OAUTH_PROVIDER_IDS) and update usages in the
model-provider-service methods to match; ensure you remove the duplicated
literal array and reference the single source of truth so provider capability
data no longer drifts across files.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 01d915e3-1341-4851-965e-9abbd0f3948c
📒 Files selected for processing (4)
apps/controller/src/app/container.tsapps/controller/src/routes/provider-oauth-routes.tsapps/controller/src/services/model-provider-service.tsapps/controller/tests/provider-oauth-routes.test.ts
✅ Files skipped from review due to trivial changes (1)
- apps/controller/src/app/container.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/controller/src/routes/provider-oauth-routes.ts
Relates to #440
What
Batch of bugfixes from user testing feedback.
Why
Several issues reported during QA testing of OAuth flow, skill browsing, and UI details.
How
OAuth disconnect clears models
deleteProvider()+ensureValidDefaultModel()+syncAll()so models don't linger in the selector after revoking OAuthlistModels()filters out OAuth-only providers whose token has expiredSkill detail back navigation
navigate(-1)to return to previous page instead of always resetting to initial tab?tab=explore)UI polish
readOnly(coming soon)Affected areas
apps/controller/src/routes/provider-oauth-routes.tsapps/controller/src/services/model-provider-service.tsapps/web/src/pages/skills.tsxapps/web/src/pages/community-skill-detail.tsxapps/web/src/components/skills/import-skill-modal.tsxChecklist
pnpm typecheckpassespnpm testpasses for affected filesSummary by CodeRabbit
New Features
Bug Fixes