Skip to content

fix(provider-cache): hydrate synced providers in fresh browsers#204

Merged
ymkiux merged 5 commits into
mainfrom
fix/provider-cache-project-source
Jun 21, 2026
Merged

fix(provider-cache): hydrate synced providers in fresh browsers#204
ymkiux merged 5 commits into
mainfrom
fix/provider-cache-project-source

Conversation

@awsl233777

@awsl233777 awsl233777 commented Jun 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Hydrate Claude provider configs from the server-side synced provider cache during startup, so fresh/incognito browsers can see cached Claude providers without relying on prior localStorage.
  • Keep cached provider secrets server-side: browser state stores providerCacheRef/safe metadata only, while apply-claude-config resolves the real key from ~/.codexmate/claude-providers.json on the backend.
  • Prevent refreshClaudeSelectionFromSettings from importing a real cached-provider secret back into localStorage after applying a cached Claude provider.
  • Preserve provider-cache startup/background hydration behavior and stale request suppression from the earlier PR work.

Verification

  • npm run lint → Lint passed for 224 file(s)
  • npm run test:unit → All 685 tests passed
  • Isolated HOME/browser E2E (/tmp/openclaw/codexmate-pr203-claude-cache-e2e.mjs):
    • normal browser sync wrote 2 providers into 5 server-side cache files
    • fresh/incognito browser hydrated cached providers without original localStorage
    • Claude panel showed cached alpha-sync / beta-sync
    • applying cached alpha-sync wrote the backend secret to isolated ~/.claude/settings.json
    • post-apply incognito localStorage still did not contain the real secret
    • no full secret leak, no absolute HOME path leak, no raw i18n key leak
    • cache files are mode 0600

Summary by CodeRabbit

  • New Features

    • Added background loading of provider-cache records during startup for fresher data.
    • Introduced loadProviderCacheRecords(options) with background and forceRefresh.
    • Enabled provider-cache-backed Claude configurations via providerCacheRef, including a new endpoint to list safe provider-cache metadata.
  • Bug Fixes

    • Prevented stale provider-cache loads from overwriting newer results (including Claude model/config hydration).
  • Tests

    • Added regression coverage for background hydration and forced-refresh ordering, and expanded behavior-parity and Claude-settings sync tests for provider-cache flows.

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6ba24d0f-63ac-4f7f-a169-20f4f5d9e341

📥 Commits

Reviewing files that changed from the base of the PR and between ef83d19 and 8aa7faa.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • package.json
  • tests/unit/claude-settings-sync.test.mjs
✅ Files skipped from review due to trivial changes (1)
  • package.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/unit/claude-settings-sync.test.mjs

📝 Walkthrough

Walkthrough

Provider-cache request sequencing prevents race conditions where stale in-flight responses overwrite fresher forced-refresh results using a monotonic counter guard. The backend exposes cached Claude provider metadata via a new API endpoint with safe fields only (no secrets). Frontend Claude config validation is extended to accept providerCacheRef as an alternative to API keys, and config matching recognizes provider-cache-backed entries. New hydration logic fetches and merges provider-cache configs into the app state with proper localStorage security. Config edit, save, and apply flows are updated to handle and persist providerCacheRef alongside deduplication adjustments. Model loading integrates provider-cache references into startup flows. Backend settings application resolves providerCacheRef references server-side and derives effective configuration by merging cached provider fields.

Changes

Provider cache integration for Claude configuration

Layer / File(s) Summary
Request sequencing and background loading
web-ui/modules/app.methods.provider-cache.mjs, web-ui/app.js, tests/unit/provider-cache-records.test.mjs, tests/unit/web-ui-behavior-parity.test.mjs
loadProviderCacheRecords accepts options with forceRefresh and background flags, uses monotonic providerCacheRequestSeq to prevent stale responses from overwriting fresh results, and skips providerCacheLoading state clearing for background loads. providerCacheRequestSeq: 0 state is added to Vue data(). triggerLoad calls loadProviderCacheRecords({ background: true }) during startup. Regression tests verify background loading and concurrent request race-condition handling.
Backend provider-cache catalog access
cli.js
CLI helpers readClaudeProviderCacheProvider() and readClaudeProviderCacheConfigs() resolve cached Claude providers and enumerate configs with baseUrl, model, targetApi, key status, and providerCacheRef. New /api endpoint get-claude-provider-cache-configs exposes safe metadata (no API keys).
Claude config validation with providerCacheRef
web-ui/modules/app.methods.claude-config.mjs, web-ui/logic.claude.mjs, tests/unit/web-ui-logic.test.mjs
Validation normalizes and includes providerCacheRef, relaxes API-key-required checks when providerCacheRef is present (non-ollama). Config matching adds a branch for provider-cache configs when settings include apiKey but candidate config has blank apiKey plus valid providerCacheRef and hasKey: true. Tests verify matching logic and validation flows.
Provider-cache config hydration and merge
web-ui/modules/app.methods.claude-config.mjs, web-ui/modules/app.methods.startup-claude.mjs, tests/unit/provider-cache-records.test.mjs, tests/unit/claude-settings-sync.test.mjs
New hydrateClaudeConfigsFromProviderCache(options) fetches provider configs via /api, builds cached Claude config objects with source: 'provider-cache' and hasKey, merges into claudeConfigs, updates currentClaudeConfig when no saved config exists, and persists to localStorage. mergeClaudeConfig now validates providerCacheRef, derives source: 'provider-cache', and includes both in returned shape. Tests verify hydration and localStorage security (no secrets).
Config edit/save/apply flows with providerCacheRef
web-ui/modules/app.methods.claude-config.mjs, tests/unit/claude-settings-sync.test.mjs
Edit modal prefills providerCacheRef. Update/save flows conditionally set or remove providerCacheRef based on validation. "Saved without key" and "API key required" checks accept configs when both apiKey and providerCacheRef are absent (non-ollama). Deduplication keys (_claudeKey, _claudeKey2) include providerCacheRef. Tests verify apply of provider-cache-backed configs with empty apiKey.
Config matching and Claude startup integration
web-ui/modules/app.methods.startup-claude.mjs
loadClaudeModels activates local-catalog path when providerCacheRef is present. isLatestRequest includes providerCacheRef in config equality check to prevent stale model-load results.
Backend Claude settings apply with providerCacheRef resolution
cli.js, tests/unit/claude-settings-sync.test.mjs
applyToClaudeSettings accepts config.providerCacheRef, looks up the cached provider, fails early if missing, and builds effectiveConfig merging cached provider fields (apiKey, baseUrl, model, targetApi) with incoming config. Proxy startup derives parameters from effectiveConfig. Tests verify provider-cache reference resolution and metadata catalog behavior.

Sequence Diagram(s)

sequenceDiagram
  participant Startup
  participant loadProviderCacheRecords as loadProviderCacheRecords
  participant seq as providerCacheRequestSeq
  participant API as Backend API
  participant State as App State

  Startup->>loadProviderCacheRecords: background: true
  loadProviderCacheRecords->>seq: increment → seq=1
  loadProviderCacheRecords->>API: fetch records (req=1)

  Startup->>loadProviderCacheRecords: forceRefresh: true
  loadProviderCacheRecords->>seq: increment → seq=2
  loadProviderCacheRecords->>API: fetch records (req=2)

  API-->>loadProviderCacheRecords: respond (req=2)
  loadProviderCacheRecords->>loadProviderCacheRecords: isLatestRequest(2) → true
  loadProviderCacheRecords->>State: apply records, providerCacheLoadedAt

  API-->>loadProviderCacheRecords: respond (req=1, stale)
  loadProviderCacheRecords->>loadProviderCacheRecords: isLatestRequest(1) → false
  Note over loadProviderCacheRecords: skip update, return
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • SakuraByteCore/codexmate#171: Both PRs extend cli.js's applyToClaudeSettings logic to compute effective configuration by merging cached or alternative provider fields with the incoming config.

Poem

🐇 A cache of providers joins the dance so fine,
With refs instead of keys to guard the line.
When stale requests would race and clash,
A sequence counter blocks the thrash.
Now Claude picks cached providers with flair,
While rabbits ensure configs are fair! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the primary change: enabling provider cache hydration in fresh browsers during startup, which directly addresses the PR's main objective and is reflected across multiple file modifications.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 fix/provider-cache-project-source

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.

@awsl233777

Copy link
Copy Markdown
Collaborator Author

Implemented the incognito/provider-cache follow-up.

What changed:

  • UI now hydrates provider cache records from the server-side cache after startup, so a fresh/incognito browser does not rely on previous localStorage state.
  • Manual provider-cache sync now forces a fresh server read after writing cache files.
  • Stale in-flight cache reads are ignored so an older empty read cannot overwrite freshly synced records.

Verified:

  • git diff --check
  • npm run test:unit → All 679 tests passed
  • npm run lint → Lint passed for 224 file(s)
  • E2E with isolated HOME:
    • normal browser before sync: 0 cache files
    • normal browser sync: 2 providers written to 5 cache files
    • fresh --incognito browser readback: 5 cache files / 6 provider summaries without using the original browser localStorage
    • provider detail modal in incognito shows alpha-sync and beta-sync
    • checked no full secret leak, no absolute HOME path leak, no raw i18n key leak
    • cache files are 0600

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== PR Info ==="
gh pr view 204 --repo SakuraByteCore/codexmate --json title,body,files,additions,deletions,changedFiles

echo ""
echo "=== PR Diff ==="
gh pr diff 204 --repo SakuraByteCore/codexmate

Length of output: 9019



@awsl233777 Thanks for the detailed write-up and thorough E2E verification. The sequence-number approach for stale-read suppression is correct, the test is well-constructed (pending + fresh interleave), and all the bookkeeping in finally is sound. A few things worth noting:


background: true is silently ignored (the main concern)

In web-ui/app.js Line ~720 the startup call passes { background: true }:

void this.loadProviderCacheRecords({ background: true });

But loadProviderCacheRecords only reads forceRefresh:

async loadProviderCacheRecords(options = {}) {
    const forceRefresh = options && options.forceRefresh === true;
    // `background` is never read

As a result, the startup hydration sets providerCacheLoading = true just like a user-initiated load would, which may cause the loading spinner to flash on page startup — possibly not the intended UX.

Two options to fix this:

// Option A – implement the flag (suppress spinner for background loads)
async loadProviderCacheRecords(options = {}) {
    const forceRefresh = options?.forceRefresh === true;
    const background  = options?.background  === true;
    if (this.providerCacheLoading && !forceRefresh) return;
    const requestSeq = (Number(this.providerCacheRequestSeq) || 0) + 1;
    this.providerCacheRequestSeq = requestSeq;
    const isLatestRequest = () => requestSeq === Number(this.providerCacheRequestSeq || 0);
    if (!background) this.providerCacheLoading = true;   // skip spinner for background
    ...
    } finally {
        if (isLatestRequest() && !background) {
            this.providerCacheLoading = false;
        }
    }
}
// Option B – remove the unused option (keep the call site honest)
void this.loadProviderCacheRecords();

Test only verifies the call shape, not the background behavior

provider-cache-records.test.mjs (via assert.match) only checks that the literal string loadProviderCacheRecords({ background: true }) exists in app.js. If the background option is meant to suppress the spinner, a unit test similar to the stale-suppression one could verify that providerCacheLoading is never flipped when background: true is passed.


Everything else looks good:

  • Sequence-number stale suppression is correctly implemented — finally only clears providerCacheLoading for isLatestRequest(), so no loading-flag leak when a stale request resolves after a fresh one.
  • Post-sync forceRefresh: true correctly bypasses the providerCacheLoading guard.
  • The parity test correctly registers the new providerCacheRequestSeq data key.
  • The new stale-suppression unit test (interleaved pending/fresh promises) is a solid regression anchor.

@awsl233777

Copy link
Copy Markdown
Collaborator Author

Addressed CodeRabbit's background-load concern.

Follow-up changes:

  • loadProviderCacheRecords({ background: true }) now consumes the background flag.
  • Startup hydration no longer flips providerCacheLoading, avoiding a background spinner flash.
  • Added a regression test that verifies background hydration updates records without setting the loading state.

Re-verified after the follow-up:

  • node tests/unit/run.mjs --grep "provider cache|web-ui behavior parity|i18n" → All 680 tests passed
  • git diff --check
  • npm run test:unit → All 680 tests passed
  • npm run lint → Lint passed for 224 file(s)

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Current PR Diff ==="
gh pr diff 204 --repo SakuraByteCore/codexmate

Length of output: 8176



@awsl233777 The follow-up looks correct. Here's what I verified in the diff:

background flag is properly implemented

  • if (!background) this.providerCacheLoading = true; — spinner is suppressed for background loads.
  • finally guard: if (isLatestRequest() && !background) — correctly skips clearing providerCacheLoading for background loads, so no accidental state mutation.

Early-return in catch + finally interaction
return inside a catch block still executes finally in JavaScript — but the isLatestRequest() guard in finally is false at that point, so nothing leaks. Logic is sound.

syncProviderCacheRecords is deliberately unaffected
The post-sync reload uses { forceRefresh: true } with no background flag, so the user-visible loading spinner correctly fires during a manual sync. That's the right behavior.

New regression test
provider cache background load hydrates records without flipping loading state asserts all three critical invariants:

  • providerCacheLoadedOnce === true (records hydrated)
  • providerCacheLoadedAt === 'background-time' (timestamp applied)
  • providerCacheLoading === false (spinner never set)

Everything is well-addressed. The implementation is clean and the test coverage is solid.

(ノ◕ヮ◕)ノ*:・゚✧

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@awsl233777 awsl233777 changed the title fix(provider-cache): hydrate project cache on startup fix(provider-cache): hydrate synced providers in fresh browsers Jun 21, 2026
@awsl233777

Copy link
Copy Markdown
Collaborator Author

Implemented the Claude/provider-cache follow-up.

What changed:

  • Fresh/incognito browsers now hydrate Claude provider configs from the server-side provider cache instead of only from previous browser localStorage.
  • The browser stores only safe cached-provider metadata (providerCacheRef, base URL, model, hasKey) and never receives the real cached API key from the catalog endpoint.
  • Applying a cached Claude provider resolves the real API key server-side from ~/.codexmate/claude-providers.json.
  • Fixed the nasty post-apply path where refreshClaudeSelectionFromSettings could read the real key from ~/.claude/settings.json and import it back into localStorage.

Verified:

  • npm run lint → Lint passed for 224 file(s)
  • npm run test:unit → All 685 tests passed
  • Isolated HOME/browser E2E:
    • normal browser sync wrote 2 providers into 5 cache files
    • fresh --incognito browser hydrated cached Claude providers without prior localStorage
    • Claude panel showed cached alpha-sync / beta-sync
    • applying cached alpha-sync wrote the backend secret to isolated ~/.claude/settings.json
    • post-apply incognito localStorage still had no real secret
    • checked no full secret leak, no absolute HOME path leak, no raw i18n key leak
    • cache files are 0600

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Current PR Diff ==="
gh pr diff 204 --repo SakuraByteCore/codexmate

echo ""
echo "=== PR Files Changed ==="
gh pr view 204 --repo SakuraByteCore/codexmate --json files --jq '.files[].path'

Length of output: 37409



@awsl233777 This batch of changes is well-designed and the security model is correctly implemented throughout. Here's what I verified in the diff:


Security boundary — API key never leaves the server

readClaudeProviderCacheConfigs deliberately excludes apiKey from the catalog response, only surfacing hasKey: boolean. The test assertion assert.doesNotMatch(fn, /apiKey:/) locks this as a regression anchor. applyToClaudeSettings then resolves the real key server-side via readClaudeProviderCacheProvider(providerCacheRef) — the browser never receives it.

Anti-reimport in matchClaudeConfigFromSettings

The new branch in logic.claude.mjs matches a provider-cache backed config (empty apiKey) against a settings.json environment that has a real key, using URL/model matching + hasKey === true as a proxy. This prevents refreshClaudeSelectionFromSettings from falling through to a key-import path. The unit test confirms the env key sk-secret-from-settings is never written to localStorage.

Startup ordering

In app.js, hydrateClaudeConfigsFromProviderCache is properly await-ed before refreshClaudeSelectionFromSettings fires. This sequencing is necessary: configs must be present in claudeConfigs before the match logic runs, otherwise the cached provider wouldn't be recognized and refreshClaudeSelectionFromSettings might not find a match.

mergeClaudeConfig preservation

providerCacheRef and source are threaded through the merge correctly. The hasKey computation — !!(next.apiKey || externalCredentialType || providerCacheRef || raw.hasKey === true) — correctly marks a provider-cache entry as key-bearing even though its browser-side apiKey is empty.

Stale model load identity check

refreshClaudeModelContext now includes providerCacheRef in the in-flight identity comparison:

&& (typeof latestConfig.providerCacheRef === 'string' ? latestConfig.providerCacheRef.trim() : '') === providerCacheRef

This correctly discards stale model-fetch results when the user switches provider-cache entries mid-flight.

Test coverage

  • hydrateClaudeConfigsFromProviderCache restores Claude providers without storing secrets — verifies apiKey: '' in stored data, providerCacheRef is present, and currentClaudeConfig switches to the cached provider.
  • applyClaudeConfig accepts provider-cache backed Claude providers without browser api key — confirms apply-claude-config receives providerCacheRef and empty apiKey.
  • applyToClaudeSettings resolves Claude provider-cache references server-side — source-text anchor for the server-side resolution path.
  • Claude provider cache catalog route exposes safe provider metadata only — asserts apiKey is absent from the catalog function body.
  • matchClaudeConfigFromSettings matches provider-cache backed config without importing secrets — closes the reimport loop.

One minor observation (non-blocking):

The mergeClaudeConfig changes (new providerCacheRef/source fields, adjusted hasKey formula) don't have a dedicated new unit test. The behavior parity tests exercise the method indirectly, but a targeted test like mergeClaudeConfig preserves providerCacheRef and marks hasKey true would make future refactoring safer. Not a blocker for this PR — just something to consider adding when the test suite is next touched.


Everything else is clean: _claudeKey / _claudeKey2 dedup keys include providerCacheRef, the validation bypass for providerCacheRef (no apiKey required) is correct, and the savedCurrent guard in hydrateClaudeConfigsFromProviderCache properly respects an existing user preference before auto-selecting a cached provider.

Solid work on the security model and the isolation between browser state and server secrets. ✨

[approve]

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
web-ui/modules/app.methods.claude-config.mjs (1)

98-101: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Missing providerCacheRef check in onClaudeModelChange causes false "API key required" errors for provider-cache-backed configs.

The credential validation at line 98 was not updated to include providerCacheRef, unlike the parallel checks in applyClaudeConfig (line 354), saveAndApplyConfig (line 271), and validation (line 44). A provider-cache-backed config has apiKey: '' and no externalCredentialType, so this check incorrectly blocks model changes with an error.

Proposed fix
-            if (!this.claudeConfigs[name].apiKey && !this.claudeConfigs[name].externalCredentialType && this.claudeConfigs[name].targetApi !== 'ollama') {
+            if (!this.claudeConfigs[name].apiKey && !this.claudeConfigs[name].providerCacheRef && !this.claudeConfigs[name].externalCredentialType && this.claudeConfigs[name].targetApi !== 'ollama') {
🤖 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 `@web-ui/modules/app.methods.claude-config.mjs` around lines 98 - 101, The
credential validation check in the `onClaudeModelChange` method is missing the
`providerCacheRef` check, causing it to incorrectly reject provider-cache-backed
configs with an "API key required" error. Update the condition that checks for
missing credentials (which currently checks apiKey, externalCredentialType, and
targetApi) to also include a check for `providerCacheRef`, similar to how it's
already implemented in `applyClaudeConfig`, `saveAndApplyConfig`, and the
validation check. This will allow configs backed by provider cache to pass the
validation since they don't require an explicit apiKey.
🤖 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 `@web-ui/modules/app.methods.startup-claude.mjs`:
- Around line 265-274: The mergeClaudeConfig function's source and hasKey field
assignments can retain stale credential state even after credentials are
cleared. At line 265, the source field logic should ensure that source is only
set to 'provider-cache' if at least one of the actual credential properties
(next.apiKey, externalCredentialType, or providerCacheRef) is present.
Similarly, at line 273, the hasKey field should only be true when there are
valid credentials currently present, rather than falling back to raw.hasKey ===
true which can preserve stale state. Restructure both conditional checks to
reset these fields to falsy values when all credential properties are empty.

---

Outside diff comments:
In `@web-ui/modules/app.methods.claude-config.mjs`:
- Around line 98-101: The credential validation check in the
`onClaudeModelChange` method is missing the `providerCacheRef` check, causing it
to incorrectly reject provider-cache-backed configs with an "API key required"
error. Update the condition that checks for missing credentials (which currently
checks apiKey, externalCredentialType, and targetApi) to also include a check
for `providerCacheRef`, similar to how it's already implemented in
`applyClaudeConfig`, `saveAndApplyConfig`, and the validation check. This will
allow configs backed by provider cache to pass the validation since they don't
require an explicit apiKey.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c3a08fec-60cd-4fd6-974e-6bf0990faf2f

📥 Commits

Reviewing files that changed from the base of the PR and between feb4f29 and ef83d19.

📒 Files selected for processing (8)
  • cli.js
  • tests/unit/claude-settings-sync.test.mjs
  • tests/unit/web-ui-behavior-parity.test.mjs
  • tests/unit/web-ui-logic.test.mjs
  • web-ui/app.js
  • web-ui/logic.claude.mjs
  • web-ui/modules/app.methods.claude-config.mjs
  • web-ui/modules/app.methods.startup-claude.mjs
🚧 Files skipped from review as they are similar to previous changes (1)
  • web-ui/app.js
📜 Review details
🔇 Additional comments (16)
tests/unit/web-ui-behavior-parity.test.mjs (1)

771-771: LGTM!

web-ui/logic.claude.mjs (1)

194-201: LGTM!

web-ui/modules/app.methods.startup-claude.mjs (1)

468-470: LGTM!

Also applies to: 480-480, 532-532

cli.js (3)

2880-2908: LGTM!


10014-10042: LGTM!


12384-12386: LGTM!

tests/unit/claude-settings-sync.test.mjs (6)

15-17: LGTM!


1410-1474: LGTM!


1476-1523: LGTM!


1524-1534: LGTM!


1536-1547: LGTM!


1548-1555: LGTM!

web-ui/modules/app.methods.claude-config.mjs (3)

19-70: LGTM!


117-174: LGTM!


207-220: LGTM!

Also applies to: 222-242, 254-297, 299-324, 348-375

tests/unit/web-ui-logic.test.mjs (1)

191-205: LGTM!

Comment on lines +265 to 274
const source = raw.source === 'provider-cache' ? 'provider-cache' : (existing.source === 'provider-cache' && providerCacheRef ? 'provider-cache' : '');
const externalCredentialType = next.apiKey
? ''
: (next.externalCredentialType || previous.externalCredentialType || '');
return {
const merged = {
apiKey: next.apiKey,
baseUrl: next.baseUrl,
model: next.model || previous.model || 'glm-4.7',
hasKey: !!(next.apiKey || externalCredentialType),
hasKey: !!(next.apiKey || externalCredentialType || providerCacheRef || raw.hasKey === true),
externalCredentialType,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

mergeClaudeConfig can keep stale credential/source state after credentials are cleared

Line 265 and Line 273 can leave source: 'provider-cache' / hasKey: true even when apiKey, externalCredentialType, and providerCacheRef are empty, which creates inconsistent config state for downstream checks.

Suggested fix
-            const source = raw.source === 'provider-cache' ? 'provider-cache' : (existing.source === 'provider-cache' && providerCacheRef ? 'provider-cache' : '');
+            const source = providerCacheRef && (raw.source === 'provider-cache' || existing.source === 'provider-cache')
+                ? 'provider-cache'
+                : '';

             const merged = {
                 apiKey: next.apiKey,
                 baseUrl: next.baseUrl,
                 model: next.model || previous.model || 'glm-4.7',
-                hasKey: !!(next.apiKey || externalCredentialType || providerCacheRef || raw.hasKey === true),
+                hasKey: !!(next.apiKey || externalCredentialType || providerCacheRef),
                 externalCredentialType,
                 targetApi: next.targetApi || previous.targetApi || 'responses'
             };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const source = raw.source === 'provider-cache' ? 'provider-cache' : (existing.source === 'provider-cache' && providerCacheRef ? 'provider-cache' : '');
const externalCredentialType = next.apiKey
? ''
: (next.externalCredentialType || previous.externalCredentialType || '');
return {
const merged = {
apiKey: next.apiKey,
baseUrl: next.baseUrl,
model: next.model || previous.model || 'glm-4.7',
hasKey: !!(next.apiKey || externalCredentialType),
hasKey: !!(next.apiKey || externalCredentialType || providerCacheRef || raw.hasKey === true),
externalCredentialType,
const source = providerCacheRef && (raw.source === 'provider-cache' || existing.source === 'provider-cache')
? 'provider-cache'
: '';
const externalCredentialType = next.apiKey
? ''
: (next.externalCredentialType || previous.externalCredentialType || '');
const merged = {
apiKey: next.apiKey,
baseUrl: next.baseUrl,
model: next.model || previous.model || 'glm-4.7',
hasKey: !!(next.apiKey || externalCredentialType || providerCacheRef),
externalCredentialType,
🤖 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 `@web-ui/modules/app.methods.startup-claude.mjs` around lines 265 - 274, The
mergeClaudeConfig function's source and hasKey field assignments can retain
stale credential state even after credentials are cleared. At line 265, the
source field logic should ensure that source is only set to 'provider-cache' if
at least one of the actual credential properties (next.apiKey,
externalCredentialType, or providerCacheRef) is present. Similarly, at line 273,
the hasKey field should only be true when there are valid credentials currently
present, rather than falling back to raw.hasKey === true which can preserve
stale state. Restructure both conditional checks to reset these fields to falsy
values when all credential properties are empty.

@awsl233777

Copy link
Copy Markdown
Collaborator Author

Addressed the non-blocking CodeRabbit test coverage suggestion.

Follow-up change:

  • Added a targeted regression test for mergeClaudeConfig to verify provider-cache backed Claude configs preserve providerCacheRef / source and are marked hasKey: true even though browser-side apiKey stays empty.

Re-verified after the follow-up:

  • npm run lint → Lint passed for 224 file(s)
  • npm run test:unit → All 686 tests passed

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Current PR Diff ==="
gh pr diff 204 --repo SakuraByteCore/codexmate

Length of output: 39001



@awsl233777 The new test is exactly what was suggested — it directly exercises mergeClaudeConfig with a provider-cache backed input and asserts all three critical invariants in one deepStrictEqual:

  • providerCacheRef: 'cached-provider' ✅ preserved through the merge
  • source: 'provider-cache' ✅ threaded correctly
  • hasKey: true ✅ set even though apiKey is empty

The hasKey formula change (!!(next.apiKey || externalCredentialType || providerCacheRef || raw.hasKey === true)) and the conditional merged.providerCacheRef / merged.source assignments in app.methods.startup-claude.mjs are all validated by this anchor. Future refactors of mergeClaudeConfig will catch regressions immediately. Well done. ✨

(ノ◕ヮ◕)ノ*:・゚✧

@awsl233777

Copy link
Copy Markdown
Collaborator Author

Updated the PR branch for the v0.0.55 version bump.

What changed:

  • Bumped global package version from 0.0.54 to 0.0.55 in package.json and package-lock.json.
  • Added commit 8aa7faa671824d5e49d2b0471535887e7c3258bb (chore(release): bump version to 0.0.55).
  • Created and pushed annotated git tag v0.0.55; the remote tag dereferences to current PR head 8aa7faa671824d5e49d2b0471535887e7c3258bb.

Verified before commit/tag:

  • npm run lint → Lint passed for 224 file(s)
  • npm run test:unit → All 686 tests passed

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Current PR Diff ==="
gh pr diff 204 --repo SakuraByteCore/codexmate

Length of output: 39781



@awsl233777 Confirmed — the diff contains only the version bump in package.json and package-lock.json (0.0.54 → 0.0.55) on top of the already-reviewed and approved feature code. Everything looks clean. ✅

[approve]

@ymkiux ymkiux merged commit 4d21e2e into main Jun 21, 2026
12 checks passed
@ymkiux ymkiux deleted the fix/provider-cache-project-source branch June 21, 2026 01:44
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.

2 participants