Skip to content

fix(web-ui): validate provider modal input#80

Merged
ymkiux merged 25 commits intomainfrom
feat/provider-modal-validation
Apr 9, 2026
Merged

fix(web-ui): validate provider modal input#80
ymkiux merged 25 commits intomainfrom
feat/provider-modal-validation

Conversation

@awsl233777
Copy link
Copy Markdown
Collaborator

@awsl233777 awsl233777 commented Apr 8, 2026

Summary

  • add field-level validation and disabled submit states to the add/edit provider modals
  • normalize provider names/urls in the web UI and surface inline errors for invalid or reserved inputs
  • enforce the same provider URL validation in backend add/update flows and extend regression coverage

Testing

  • npm run test:unit
  • npm run test:e2e

Summary by CodeRabbit

  • New Features

    • OpenClaw auth-profile support with integrated credential management and OpenClaw UI controls
    • New list-sessions-usage API and a usage-panel refresh button
    • Provider input normalization/validation with inline field errors and disabled submit controls
  • Bug Fixes / Improvements

    • URL normalization (trailing-slash trimming) and stricter HTTP(S) validation
    • Deferred session-detail hydration and tuned session-list limits for faster browsing
    • Default OpenClaw config protected from accidental deletion/edit-save
  • Documentation

    • Updated npm run reset invocation syntax
  • Tests

    • Expanded unit and E2E coverage for providers, OpenClaw, sessions and UI behavior

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 8, 2026

📝 Walkthrough

Walkthrough

Added OpenClaw auth-profile support and persistence, introduced a sessions-usage API and client/UI flow, centralized provider input normalization/validation and tightened URL handling, changed session-tab rendering/hydration to defer detail loads, and expanded tests across OpenClaw, providers, sessions, and UI behaviors.

Changes

Cohort / File(s) Summary
CLI & OpenClaw core
cli.js, web-ui/modules/app.methods.openclaw-core.mjs, web-ui/modules/app.methods.openclaw-editing.mjs, web-ui/modules/app.methods.openclaw-persist.mjs
Add OpenClaw auth-profile reading/writing, normalize provider IDs, resolve agent/state dirs, surface authProfilesByProvider in config loads, stage/persist pending auth-profile updates, and integrate auth-profile updates into apply/save flows with atomic writes and backup options.
Provider validation & UI
web-ui/modules/app.methods.providers.mjs, web-ui/partials/index/modals-basic.html, web-ui/styles/modals-core.css
Centralized provider draft normalization/validation (trim, strip trailing slash, name pattern, reserved names, duplicate detection, HTTP(S) URL check), added field-level errors, disabled submit when invalid, and invalid-input styling.
Sessions usage API & client
cli.js, web-ui/modules/app.methods.session-browser.mjs, tests/e2e/test-sessions.js, web-ui/modules/app.computed.session.mjs, web-ui/partials/index/panel-usage.html
Add list-sessions-usage backend action and listSessionUsage client call; clamp/validate params, return sessions without __messageCountExact; add loadSessionsUsage, usage list state, refresh button, loading/error/empty UI handling.
Session list limits & logic
web-ui/logic.sessions.mjs, web-ui/modules/app.methods.session-browser.mjs, web-ui/modules/app.computed.session.mjs
Introduce DEFAULT_SESSION_LIST_LIMIT (200) and DEFAULT_SESSION_LIST_FAST_LIMIT (40) with heuristic for fast browsing; propagate forceRefresh from options; switch some chart sources to sessionsUsageList.
Session tab rendering & hydration
web-ui/session-helpers.mjs, web-ui/app.js, web-ui/modules/app.methods.session-browser.mjs, web-ui/modules/app.methods.navigation.mjs
Add __mainTabSwitchState, separate enteringSessions/enteringUsage paths, defer active-session detail hydration via scheduled frame callback when available, add helpers to schedule/suspend/prepare session render.
OpenClaw UI & persistence controls
web-ui/partials/index/modal-openclaw-config.html, web-ui/partials/index/panel-config-openclaw.html
Ensure a guaranteed 默认配置 entry, make Base URL/API Key inputs conditionally read-only with display-kind hints, block add/delete for the default config, require save-and-apply for locked default, and surface pending auth-profile updates in apply calls.
Session mutations → usage invalidation
web-ui/modules/app.methods.session-actions.mjs, web-ui/modules/app.methods.session-trash.mjs
After clone/trash/restore actions, optionally call invalidateSessionsUsageData({ preserveList: true }) to refresh usage state when available.
UI templates & styling
web-ui/partials/index/layout-header.html, web-ui/partials/index/panel-docs.html, web-ui/partials/index/panel-usage.html, web-ui/partials/index/panel-sessions.html, web-ui/styles/docs-panel.css, web-ui/styles/modals-core.css
Minor template reorganizations (docs command-row), usage refresh button and empty/error/loading states, session refresh now uses forceRefresh, adjust summary chip accessors, and add invalid-input/error hint styling.
OpenClaw core editing & quick-fill
web-ui/modules/app.methods.openclaw-core.mjs, web-ui/modules/app.methods.openclaw-editing.mjs, web-ui/modules/app.methods.openclaw-persist.mjs, web-ui/modules/app.methods.openclaw-editing.mjs
Normalize provider/config lookup, env-template & secret-ref detection, extend fillOpenclawQuickFromConfig/defaults to include read-only/display metadata and auth-profile resolution, suppress writing provider fields when read-only, and stage auth-profile updates instead of inlining apiKey.
Tests — OpenClaw & editing
tests/unit/openclaw-core.test.mjs, tests/unit/openclaw-editing.test.mjs, tests/unit/openclaw-persist-regression.test.mjs, tests/e2e/test-openclaw.js
Large test additions covering quick-fill, secret-ref/env-template formatting, apply/save/apply flows with pending auth-profile updates and e2e setup for local .openclaw agent/auth files.
Tests — Providers & validation
tests/unit/providers-validation.test.mjs, tests/e2e/test-config.js, tests/unit/provider-switch-regression.test.mjs
Add provider validation unit tests, update E2E to assert URL normalization and invalid-URL rejection, and adjust expectations for trimmed/normalized URLs.
Tests — Navigation & sessions rendering
tests/unit/session-tab-switch-performance.test.mjs, tests/unit/session-browser-timeline-regression.test.mjs, tests/unit/web-ui-runtime-navigation-regression.test.mjs, tests/e2e/test-web-ui-session-tab.js, tests/unit/web-ui-behavior-parity.test.mjs
Add/update tests for deferred hydration, render suspension/resume, parity allowlists for new state keys/methods, and runtime navigation behaviors for session tab.
Test harness & misc tests
tests/unit/run.mjs, tests/unit/reset-main.test.mjs, tests/unit/web-ui-logic.test.mjs, tests/e2e/test-web-ui-assets.js, tests/e2e/test-sessions.js
Include new unit test modules in runner, add git-probe stdio suppression test, update session-list param tests to use new limits, validate bundled Vue runtime asset, and add sessions-usage e2e tests.
Docs & CLI utility
README.md, README.en.md, cmd/reset-main.js
Document npm run reset <PR> invocation change and change git rev-parse --is-inside-work-tree to run with stdio: 'ignore'.

Sequence Diagram(s)

sequenceDiagram
    participant User as User/Browser
    participant UI as Web UI
    participant Nav as Navigation Methods
    participant API as Backend API
    participant State as App State

    User->>UI: Click "Sessions" tab
    UI->>Nav: switchMainTab('sessions')
    Nav->>State: prepareSessionTabRender()
    Nav->>API: api('list-sessions', params...)
    API-->>State: sessions batch
    State->>UI: render session list
    Note over Nav,State: If scheduleAfterFrame available -> defer loadActiveSessionDetail
    par Later frame
        Nav->>API: api('get-session-detail', sessionId)
        API-->>State: active session detail
        State->>UI: fill session detail
    end
Loading
sequenceDiagram
    participant User as User
    participant Modal as Provider Modal
    participant Methods as Provider Methods
    participant API as Backend

    User->>Modal: Enter provider name + URL
    Modal->>Methods: validate fields (trim, strip slash, pattern, reserved, duplicates)
    Methods-->>Modal: field errors or normalized draft
    alt valid
        Modal->>API: add-provider { name, url (normalized) }
        API-->>Modal: success
        Modal->>Methods: loadAll() refresh providers
    else invalid
        Modal->>Modal: show errors, disable submit
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • awsl233777
  • SurviveM

"🐰 I hopped through configs, keys, and views,
I trimmed the URLs and sorted your queues,
Sessions refresh and profiles find light,
Quick fixes in code, then back to my night,
A tiny rabbit cheers this tidy new flight!"

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.99% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'fix(web-ui): validate provider modal input' accurately captures the primary change—adding field-level validation and error handling to provider add/edit modals in the web UI.

✏️ 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/provider-modal-validation

Warning

Review ran into problems

🔥 Problems

Timed out fetching pipeline failures after 30000ms


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
Contributor

@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)
tests/unit/providers-validation.test.mjs (1)

83-86: Tighten message assertions with explicit count checks.

These assertions validate first message content, but not whether unexpected extra messages were emitted.

Small assertion hardening
     assert.deepStrictEqual(messages[0], {
         text: '操作成功',
         type: 'success'
     });
+    assert.strictEqual(messages.length, 1);
@@
     assert.deepStrictEqual(messages[0], {
         text: 'URL 仅支持 http/https',
         type: 'error'
     });
+    assert.strictEqual(messages.length, 1);

Also applies to: 110-113

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

In `@tests/unit/providers-validation.test.mjs` around lines 83 - 86, The test
currently only asserts the content of messages[0] (in
tests/unit/providers-validation.test.mjs) which allows extra unexpected
messages; update the assertions to also assert the exact count of messages
(e.g., assert.strictEqual(messages.length, 1)) before checking messages[0].
Apply the same hardening to the second occurrence that checks messages[0] at the
other block (lines around the existing deepStrictEqual) so both places validate
both length and content for the messages 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 `@cli.js`:
- Around line 3458-3460: The validation currently calls
isValidHttpUrl(normalizeBaseUrl(url)) but never persists the normalized value,
so later writes still use the raw url; capture the normalized value (e.g., const
normalized = normalizeBaseUrl(url)) before validation and use that normalized
variable in the subsequent write operations instead of the original url (replace
occurrences used at the later write sites with the normalized variable),
ensuring normalizeBaseUrl and isValidHttpUrl calls remain in place.

---

Nitpick comments:
In `@tests/unit/providers-validation.test.mjs`:
- Around line 83-86: The test currently only asserts the content of messages[0]
(in tests/unit/providers-validation.test.mjs) which allows extra unexpected
messages; update the assertions to also assert the exact count of messages
(e.g., assert.strictEqual(messages.length, 1)) before checking messages[0].
Apply the same hardening to the second occurrence that checks messages[0] at the
other block (lines around the existing deepStrictEqual) so both places validate
both length and content for the messages array.
🪄 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: 99699d8f-710b-407f-b3e5-1eac886a1387

📥 Commits

Reviewing files that changed from the base of the PR and between b204d2c and 8bb9a2a.

📒 Files selected for processing (9)
  • cli.js
  • tests/e2e/test-config.js
  • tests/unit/provider-switch-regression.test.mjs
  • tests/unit/providers-validation.test.mjs
  • tests/unit/run.mjs
  • tests/unit/web-ui-behavior-parity.test.mjs
  • web-ui/modules/app.methods.providers.mjs
  • web-ui/partials/index/modals-basic.html
  • web-ui/styles/modals-core.css
📜 Review details
🔇 Additional comments (22)
web-ui/modules/app.methods.providers.mjs (4)

1-50: Well-structured validation helpers with centralized normalization.

The validation layer is cleanly separated with pure functions handling normalization and validation. The normalizeProviderUrl correctly strips trailing slashes, and isValidHttpUrl properly validates protocol using the URL constructor.


52-91: Edit mode skips name pattern validation intentionally.

The validation logic treats edit mode differently - it only checks that editingName is non-empty (line 74-76) without validating name pattern or reserved names. This is correct because the name field is readonly in edit mode (per the HTML template), so the name cannot be changed. The validation focus in edit mode is appropriately on the URL field.


139-163: addProvider correctly uses normalized values from validation.

The flow is correct: normalize draft state first, run validation, and if valid, submit the normalized name and url from the validation result rather than the raw draft values. This ensures consistent data is sent to the backend.


280-308: updateProvider validation flow is correct.

The normalization happens before the try-catch block (line 286), so even if the API call throws, the editingProvider retains the normalized/trimmed values. This aligns with the test expectation update in provider-switch-regression.test.mjs.

tests/unit/provider-switch-regression.test.mjs (1)

288-311: Test expectation correctly updated for normalization behavior.

The test setup (line 63) provides a URL with leading/trailing whitespace: ' https://api.example.com/v1 '. Since normalizeProviderDraftState is called before the API request in updateProvider (line 286 in app.methods.providers.mjs), the editingProvider.url is trimmed even when the request throws. The updated expectation at line 301 correctly reflects this behavior.

tests/unit/run.mjs (1)

38-38: New test module registered correctly.

The new providers-validation.test.mjs test file is properly added to the test runner, following the established import pattern.

tests/e2e/test-config.js (2)

314-326: Comprehensive E2E coverage for URL validation on add-provider.

The tests properly verify:

  1. API rejects invalid URL format ('not-a-url')
  2. CLI also rejects invalid URL with non-zero exit status
  3. Failed add-provider does not pollute the provider list

This ensures both API and CLI paths enforce URL validation consistently.


1367-1368: E2E test for update-provider URL validation.

Good addition testing that update-provider rejects non-HTTP/HTTPS URLs (ftp://bad.example.com). This complements the add-provider validation tests and ensures update flows have the same validation.

web-ui/partials/index/modals-basic.html (2)

8-36: Add provider modal validation UI is well-implemented.

The validation bindings are correct:

  • Conditional invalid class driven by providerFieldError('add', field)
  • @blur triggers normalization to trim whitespace/trailing slashes
  • Inline error hints displayed when validation fails
  • Submit button disabled until canSubmitProvider('add') returns true

The autocomplete="off" and spellcheck="false" attributes improve the input experience for technical fields.


51-68: Edit provider modal validation follows the same pattern.

The URL field in the edit modal correctly applies the same validation approach. The name field remains readonly (line 47), which aligns with the backend behavior where provider names are immutable after creation.

web-ui/styles/modals-core.css (2)

252-264: Invalid input state styling is complete and accessible.

The CSS correctly handles all interaction states for invalid inputs:

  • Border color changes to error color on invalid state
  • Maintains error border on hover and focus
  • Focus ring uses an error-themed shadow for clear visual feedback

460-464: Error hint styling overrides base opacity correctly.

Setting opacity: 1 ensures error messages are fully visible, overriding the opacity: 0.8 from the base .form-hint rule.

tests/unit/web-ui-behavior-parity.test.mjs (2)

336-342: Refactored drift assertions for consistency.

The non-HEAD path now uses the same Set-based filtering pattern as the HEAD path (lines 329-334). This improves code consistency without changing behavior.


350-378: Allowlist updated for new provider validation methods.

The 8 new method keys correspond exactly to the methods exported from createProvidersMethods in app.methods.providers.mjs:

  • canSubmitProvider, findProviderByName, getProviderValidation
  • isReservedProviderCreationName, isValidProviderNameInput, isValidProviderUrlInput
  • normalizeProviderDraft, providerFieldError

This ensures the parity test doesn't fail due to intentional API additions.

cli.js (3)

3539-3541: Good guard for update API URL validation.

Line 3539 rejects non-HTTP(S) URLs early in backend update flow, matching the new validation contract.


8237-8237: CLI add flow normalization/validation is correctly enforced.

Normalizing at Line 8237 and validating at Line 8259 prevents malformed schemes and keeps stored base URLs consistent.

Also applies to: 8259-8262


8320-8320: CLI update flow correctly validates and writes normalized URLs.

Line 8320 normalization + Line 8349 validation + Line 8478 conditional replacement is a solid, consistent update path.

Also applies to: 8349-8352, 8478-8480

tests/unit/providers-validation.test.mjs (5)

29-54: Great pre-submit guard coverage for invalid add flow.

This test correctly verifies field-level errors, disabled submit state, API suppression, and surfaced error messaging in one path.


56-87: Normalization payload test is solid.

Good assertion set for trimming/URL normalization, modal reset state, reload trigger, and success feedback.


89-114: Edit-path invalid URL blocking is well covered.

This captures the expected guardrail behavior: canSubmitProvider('edit') false, no API call, and user-facing error.


116-127: Reserved-name regression test is clear and targeted.

Good direct check for reserved-name error and submit lockout.


1-3: No action required — test is available via global injection in the test bootstrap.

The file does not import test, but that is correct. The test runner bootstrap (tests/unit/run.mjs) explicitly injects globalThis.test before execution, so the test(...) calls throughout this file will resolve correctly. No import is needed.

@awsl233777
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 8, 2026

✅ Actions performed

Review triggered.

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.

Copy link
Copy Markdown
Contributor

@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

Caution

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

⚠️ Outside diff range comments (1)
cli.js (1)

8238-8247: ⚠️ Potential issue | 🟡 Minor

Differentiate blank vs invalid URLs in cmdAdd.

Line 8238 normalizes before the required-field check, so inputs like / or //// collapse to '' and hit "名称和URL必填" instead of the new "URL 仅支持 http/https" validation. That makes the CLI add flow less precise than the API/web and update flows.

💡 Suggested fix
 function cmdAdd(name, baseUrl, apiKey, silent = false) {
     const providerName = typeof name === 'string' ? name.trim() : '';
-    const providerBaseUrl = normalizeBaseUrl(baseUrl);
+    const rawBaseUrl = typeof baseUrl === 'string' ? baseUrl.trim() : '';
+    const providerBaseUrl = normalizeBaseUrl(rawBaseUrl);
 
-    if (!providerName || !providerBaseUrl) {
+    if (!providerName || !rawBaseUrl) {
         if (!silent) {
             console.error('用法: codexmate add <名称> <URL> [密钥]');
             console.log('\n示例:');
             console.log('  codexmate add 88code https://api.88code.ai/v1 sk-xxx');
         }
         throw new Error('名称和URL必填');
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli.js` around lines 8238 - 8247, Move the required-field check to use the
raw baseUrl before calling normalizeBaseUrl so blank inputs like "/" or "////"
are caught as a missing URL; specifically, in the cmdAdd flow check
(!providerName || !baseUrl) first and throw the existing "名称和URL必填" message,
then call normalizeBaseUrl(baseUrl) to get providerBaseUrl and perform the URL
validation that yields "URL 仅支持 http/https" when normalizeBaseUrl returns an
invalid URL; update the branch that currently uses providerBaseUrl so it
validates normalized URL separately from the presence check (references:
normalizeBaseUrl and providerBaseUrl).
🧹 Nitpick comments (3)
tests/unit/reset-main.test.mjs (1)

118-121: Make this assertion less formatting-sensitive.

Line 120 hard-codes an exact source snippet, so minor formatting changes can fail the test even when behavior is unchanged. Consider a whitespace-tolerant regex (or behavior-level mocking test) to reduce brittleness.

Suggested lightweight tweak
-    assert.match(content, /run\('git rev-parse --is-inside-work-tree', \{ stdio: 'ignore' \}\);/);
+    assert.match(
+        content,
+        /run\(\s*'git rev-parse --is-inside-work-tree'\s*,\s*\{\s*stdio:\s*'ignore'\s*\}\s*\);/
+    );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/reset-main.test.mjs` around lines 118 - 121, The test "git
repository probe stays silent during reset startup" currently asserts an exact
source snippet which is brittle; update the assertion in
tests/unit/reset-main.test.mjs to use a whitespace-tolerant regex (or replace
the file-content assertion with a behavior-level mock) that matches the command
and the stdio option irrespective of spacing/quotes—e.g., build a regex that
looks for run\(\s*['"`]git rev-parse
--is-inside-work-tree['"`]\s*,\s*\{\s*stdio\s*:\s*['"`]ignore['"`]\s*\}\s*\) and
use that with assert.match on the content variable (or alternatively mock the
run invocation and assert it was called with the expected args) so formatting
changes won’t break the test.
cli.js (1)

3452-3461: Consider one shared provider URL validation helper.

The same normalizeBaseUrl(...) + isValidHttpUrl(...) rule is now spread across add/update helper paths and CLI paths. Extracting a small shared validator, called after each flow’s own presence checks, would make the Web UI/API and CLI behavior much harder to drift out of sync again.

Also applies to: 3540-3542, 8238-8263, 8321-8353

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

In `@cli.js` around lines 3452 - 3461, Extract a shared validator function (e.g.,
validateProviderUrl) that encapsulates normalizeBaseUrl + isValidHttpUrl logic
and error messages, then replace the duplicated checks in CLI and helper flows
by calling validateProviderUrl(url) immediately after presence checks (after
verifying name and url exist) in functions that currently call normalizeBaseUrl
and isValidHttpUrl; update call sites including the block using
normalizeBaseUrl/isValidHttpUrl, and the other locations mentioned so the CLI,
add/update helpers and Web/API use the same validateProviderUrl function to keep
behavior consistent.
tests/unit/config-tabs-ui.test.mjs (1)

296-296: Consider de-brittling the __mainTabSwitchState shape assertion.

Line 296 hard-codes field order in one regex. Semantically equivalent reorder/formatting can fail the test unnecessarily. Prefer separate assert.match checks per field to keep intent stable.

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

In `@tests/unit/config-tabs-ui.test.mjs` at line 296, The single regex asserting
the entire __mainTabSwitchState object shape is brittle due to field
order/formatting; update the test to validate each property independently by
replacing the one assert.match(appScript, /__mainTabSwitchState: .../) with
multiple assert.match calls against appScript that each check a single field
(e.g., /__mainTabSwitchState[\s\S]*intent:\s*''/, /pendingTarget:\s*''/,
/pendingConfigMode:\s*''/, /ticket:\s*0/) or alternatively use a single regex
with unordered lookaheads for each field; target the __mainTabSwitchState symbol
and the appScript variable when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web-ui/app.js`:
- Around line 119-124: The data property __mainTabSwitchState is using leading
underscores so it won't be proxied to the component instance; rename the data
key to a non-reserved name (for example mainTabSwitchState or uiTabSwitchState)
in the component's data() and update all places that reference
this.__mainTabSwitchState (notably in app.methods.navigation.mjs and any
navigation methods) to use the new property name (this.mainTabSwitchState or
this.uiTabSwitchState) so the component can access and mutate the state at
runtime.

In `@web-ui/partials/index/layout-header.html`:
- Around line 297-301: The template currently only guards for card existence
(sessionUsageSummaryCards[0] and [1]) so if the card exists but its value is
null/undefined the UI stops falling back to 0; update both bindings to use the
card value with a nullish fallback, e.g. use optional chaining with a nullish
coalescing fallback (sessionUsageSummaryCards[0]?.value ?? 0 and
sessionUsageSummaryCards[1]?.value ?? 0) so missing values render as 0 while
preserving existing behavior when the card itself is absent.

---

Outside diff comments:
In `@cli.js`:
- Around line 8238-8247: Move the required-field check to use the raw baseUrl
before calling normalizeBaseUrl so blank inputs like "/" or "////" are caught as
a missing URL; specifically, in the cmdAdd flow check (!providerName ||
!baseUrl) first and throw the existing "名称和URL必填" message, then call
normalizeBaseUrl(baseUrl) to get providerBaseUrl and perform the URL validation
that yields "URL 仅支持 http/https" when normalizeBaseUrl returns an invalid URL;
update the branch that currently uses providerBaseUrl so it validates normalized
URL separately from the presence check (references: normalizeBaseUrl and
providerBaseUrl).

---

Nitpick comments:
In `@cli.js`:
- Around line 3452-3461: Extract a shared validator function (e.g.,
validateProviderUrl) that encapsulates normalizeBaseUrl + isValidHttpUrl logic
and error messages, then replace the duplicated checks in CLI and helper flows
by calling validateProviderUrl(url) immediately after presence checks (after
verifying name and url exist) in functions that currently call normalizeBaseUrl
and isValidHttpUrl; update call sites including the block using
normalizeBaseUrl/isValidHttpUrl, and the other locations mentioned so the CLI,
add/update helpers and Web/API use the same validateProviderUrl function to keep
behavior consistent.

In `@tests/unit/config-tabs-ui.test.mjs`:
- Line 296: The single regex asserting the entire __mainTabSwitchState object
shape is brittle due to field order/formatting; update the test to validate each
property independently by replacing the one assert.match(appScript,
/__mainTabSwitchState: .../) with multiple assert.match calls against appScript
that each check a single field (e.g., /__mainTabSwitchState[\s\S]*intent:\s*''/,
/pendingTarget:\s*''/, /pendingConfigMode:\s*''/, /ticket:\s*0/) or
alternatively use a single regex with unordered lookaheads for each field;
target the __mainTabSwitchState symbol and the appScript variable when making
the change.

In `@tests/unit/reset-main.test.mjs`:
- Around line 118-121: The test "git repository probe stays silent during reset
startup" currently asserts an exact source snippet which is brittle; update the
assertion in tests/unit/reset-main.test.mjs to use a whitespace-tolerant regex
(or replace the file-content assertion with a behavior-level mock) that matches
the command and the stdio option irrespective of spacing/quotes—e.g., build a
regex that looks for run\(\s*['"`]git rev-parse
--is-inside-work-tree['"`]\s*,\s*\{\s*stdio\s*:\s*['"`]ignore['"`]\s*\}\s*\) and
use that with assert.match on the content variable (or alternatively mock the
run invocation and assert it was called with the expected args) so formatting
changes won’t break the test.
🪄 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: 0e40ab66-06b3-414d-ad65-0fe4bad8a946

📥 Commits

Reviewing files that changed from the base of the PR and between 8bb9a2a and 657e95c.

📒 Files selected for processing (19)
  • README.en.md
  • README.md
  • cli.js
  • cmd/reset-main.js
  • res/vue.global.prod.js
  • tests/e2e/test-config.js
  • tests/unit/config-tabs-ui.test.mjs
  • tests/unit/providers-validation.test.mjs
  • tests/unit/reset-main.test.mjs
  • tests/unit/session-tab-switch-performance.test.mjs
  • tests/unit/web-ui-behavior-parity.test.mjs
  • web-ui/app.js
  • web-ui/index.html
  • web-ui/modules/app.methods.session-browser.mjs
  • web-ui/partials/index/layout-header.html
  • web-ui/partials/index/panel-docs.html
  • web-ui/partials/index/panel-usage.html
  • web-ui/session-helpers.mjs
  • web-ui/styles/docs-panel.css
✅ Files skipped from review due to trivial changes (5)
  • web-ui/index.html
  • README.en.md
  • README.md
  • web-ui/partials/index/panel-usage.html
  • tests/unit/web-ui-behavior-parity.test.mjs
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/e2e/test-config.js
  • tests/unit/providers-validation.test.mjs
📜 Review details
🔇 Additional comments (8)
cmd/reset-main.js (1)

163-163: Good hardening for startup probe.

Line 163 correctly silences the git rev-parse --is-inside-work-tree check without changing failure behavior. Nice small UX improvement.

web-ui/modules/app.methods.session-browser.mjs (1)

362-366: LGTM!

The signature update and options forwarding correctly align with the updated loadSessionsHelper API.

Minor nit: options || {} is redundant given the default parameter options = {}, but it's harmless defensive coding.

tests/unit/session-tab-switch-performance.test.mjs (2)

88-119: LGTM!

Test correctly validates that:

  1. Switching to the usage tab calls loadSessions with { includeActiveDetail: false }
  2. Repeated switches don't trigger additional loads (checking array length stays at 1)

462-511: LGTM!

Comprehensive test coverage for the includeActiveDetail: false path:

  • Verifies loadActiveSessionDetail is not called
  • Confirms activeSession is still updated from returned sessions
  • Validates stale activeSessionMessages are cleared
web-ui/session-helpers.mjs (2)

14-50: LGTM!

The switchMainTab changes correctly compute shouldLoadSessionDetail based on the destination tab and pass it through to loadSessions. This optimizes the usage tab path by skipping unnecessary detail fetches.


93-152: Default includeActiveDetail behavior is safe across all callers.

The optimization correctly skips detail loading when users are not on the sessions tab. All callers are covered:

  • session-actions.mjs (line 393): Safe — selectSession(matched) always calls loadActiveSessionDetail() unconditionally.
  • session-browser.mjs filter handlers (lines 289, 294, 298): Safe — these handlers execute only when the sessions component is active, so this.mainTab === 'sessions' evaluates to true.
  • session-trash.mjs (line 346): Detail loading is deferred, not skipped. When users interact with the recovered session, selectSession() loads detail on demand.

This is intentional lazy loading aligned with the optimization goal.

web-ui/partials/index/panel-docs.html (1)

97-100: Command-row markup update looks good.

This aligns cleanly with the new flex row layout and keeps the copy action safely disabled when target.command is empty.

web-ui/styles/docs-panel.css (1)

88-97: Responsive command-row styling is solid.

The new .docs-command-row layout and mobile stacking rules are coherent and match the updated docs panel markup.

Also applies to: 174-176

Comment on lines +119 to +124
__mainTabSwitchState: {
intent: '',
pendingTarget: '',
pendingConfigMode: '',
ticket: 0
},
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 | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

In Vue 3 (Options API), are data() properties whose names start with "_" or "$" proxied onto this? What is the recommended pattern for internal reactive state names to ensure reactivity and template/method access?

💡 Result:

No. In Vue 3’s Options API, data() keys that start with _ or $ are made reactive, but they are not proxied onto the component instance, so they won’t be available as this._foo / this.$foo (and likewise won’t be directly available in template expressions). You must access them via this.$data._foo (or $data._foo in templates). [1]

Recommended pattern for internal reactive state (Options API)

  • Don’t use _ or $ prefixes for data() top-level keys; those prefixes are reserved for Vue’s own instance APIs / internals. [1][2]
  • If you want “internal-only” state, use a non-reserved naming convention (e.g. internalFoo, fooInternal, uiFoo, debugFoo), or group internal state under a single object like state / ui / form:
    export default {
      data() {
        return {
          ui: { isSaving: false },
          internalFoo: 123
        }
      },
      methods: {
        save() { this.ui.isSaving = true }
      }
    }
    This keeps full reactivity and allows direct access in both methods (this.ui.isSaving) and templates ({{ ui.isSaving }}), while avoiding reserved prefixes. [1][2]

Sources:
[1] Vue 3 API Reference — Options: State (data, reserved _/$ prefixes and $data access) (vuejs.org)
[2] Vue Guide — Reactivity Fundamentals (Options API: data() is proxied onto this, and $ / _ prefixes are reserved/should be avoided) (vuejs.org)

Citations:


🏁 Script executed:

# Search for all references to __mainTabSwitchState in the web-ui directory
rg "__mainTabSwitchState" web-ui/

Repository: SakuraByteCore/codexmate

Length of output: 472


Rename __mainTabSwitchState — underscore-prefixed data keys are not proxied to this in Vue 3.

In Vue 3's Options API, data() properties starting with _ or $ are made reactive but are not proxied onto the component instance. The code at lines 119–124 defines __mainTabSwitchState, but the navigation methods in app.methods.navigation.mjs access this.__mainTabSwitchState, which will be undefined at runtime.

Change __mainTabSwitchState to mainTabSwitchState (remove the leading underscores), or rename it to follow a non-reserved convention like tabSwitchState, uiTabSwitchState, etc.

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

In `@web-ui/app.js` around lines 119 - 124, The data property __mainTabSwitchState
is using leading underscores so it won't be proxied to the component instance;
rename the data key to a non-reserved name (for example mainTabSwitchState or
uiTabSwitchState) in the component's data() and update all places that reference
this.__mainTabSwitchState (notably in app.methods.navigation.mjs and any
navigation methods) to use the new property name (this.mainTabSwitchState or
this.uiTabSwitchState) so the component can access and mutate the state at
runtime.

Comment on lines +297 to +301
<span class="value">{{ sessionUsageSummaryCards[0] ? sessionUsageSummaryCards[0].value : 0 }}</span>
</div>
<div class="status-chip">
<span class="label">总消息数</span>
<span class="value">{{ sessionUsageSummaryCards[1]?.value ?? 0 }}</span>
<span class="value">{{ sessionUsageSummaryCards[1] ? sessionUsageSummaryCards[1].value : 0 }}</span>
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

Preserve 0 fallback when summary card exists but value is missing.

Line 297 and Line 301 now only guard card existence. If value is null/undefined, the UI no longer falls back to 0.

💡 Proposed fix
-<span class="value">{{ sessionUsageSummaryCards[0] ? sessionUsageSummaryCards[0].value : 0 }}</span>
+<span class="value">{{ (sessionUsageSummaryCards[0] && sessionUsageSummaryCards[0].value != null) ? sessionUsageSummaryCards[0].value : 0 }}</span>

-<span class="value">{{ sessionUsageSummaryCards[1] ? sessionUsageSummaryCards[1].value : 0 }}</span>
+<span class="value">{{ (sessionUsageSummaryCards[1] && sessionUsageSummaryCards[1].value != null) ? sessionUsageSummaryCards[1].value : 0 }}</span>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/partials/index/layout-header.html` around lines 297 - 301, The
template currently only guards for card existence (sessionUsageSummaryCards[0]
and [1]) so if the card exists but its value is null/undefined the UI stops
falling back to 0; update both bindings to use the card value with a nullish
fallback, e.g. use optional chaining with a nullish coalescing fallback
(sessionUsageSummaryCards[0]?.value ?? 0 and sessionUsageSummaryCards[1]?.value
?? 0) so missing values render as 0 while preserving existing behavior when the
card itself is absent.

Copy link
Copy Markdown
Contributor

@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)
tests/unit/web-ui-behavior-parity.test.mjs (1)

380-386: Minor inconsistency: Missing error message in assertions.

The non-HEAD method key assertions at lines 384-385 lack the error message parameter that is present in the data key assertions (lines 345-346) and the HEAD branch method assertions (lines 377-378). This makes debugging test failures harder.

🔧 Suggested fix for consistency
         const unexpectedExtraCurrentMethodKeys = extraCurrentMethodKeys.filter((key) => !allowedExtraMethodKeySet.has(key));
         const unexpectedMissingCurrentMethodKeys = missingCurrentMethodKeys.filter((key) => !allowedMissingMethodKeySet.has(key));
-        assert.deepStrictEqual(unexpectedExtraCurrentMethodKeys, []);
-        assert.deepStrictEqual(unexpectedMissingCurrentMethodKeys, []);
+        assert.deepStrictEqual(unexpectedExtraCurrentMethodKeys, [], `unexpected extra method keys against ${parityBaseline.ref}`);
+        assert.deepStrictEqual(unexpectedMissingCurrentMethodKeys, [], `unexpected missing method keys against ${parityBaseline.ref}`);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/web-ui-behavior-parity.test.mjs` around lines 380 - 386, The two
assertions comparing unexpectedExtraCurrentMethodKeys and
unexpectedMissingCurrentMethodKeys lack descriptive error messages; update the
assert.deepStrictEqual calls for unexpectedExtraCurrentMethodKeys and
unexpectedMissingCurrentMethodKeys to include the same style of failure messages
used elsewhere (e.g., referencing
allowedExtraCurrentMethodKeys/allowedMissingCurrentMethodKeys and
currentMethodKeys) so test failures are easier to debug—locate the assertions
using the variable names unexpectedExtraCurrentMethodKeys and
unexpectedMissingCurrentMethodKeys and add appropriate string messages as the
third argument to assert.deepStrictEqual.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cli.js`:
- Around line 5454-5465: When mapping the result of listAllSessions(...) in the
anonymous mapping function, preserve and apply the exact count before dropping
__messageCountExact: if normalized.__messageCountExact != null then set
normalized.messageCount = normalized.__messageCountExact (or merge exact value
into whatever messageCount field is used) and only then delete
normalized.__messageCountExact; do this inside the same mapping callback so
listAllSessions and the mapping produce hydrated sessions with exact counts.
- Around line 10732-10744: The case 'list-sessions-usage' block fails if params
is undefined and also fails to pass the normalized source to listSessionUsage;
guard params first (e.g., ensure params is an object before accessing
params.source), compute a normalized source string from params.source
(trim().toLowerCase()), validate it (allow 'codex','claude','all'), and then
pass that normalized source into listSessionUsage (instead of the raw
params.source) while preserving other params; update the result assignment in
the 'list-sessions-usage' handler to use the normalized source and avoid reading
properties when params is missing.

---

Nitpick comments:
In `@tests/unit/web-ui-behavior-parity.test.mjs`:
- Around line 380-386: The two assertions comparing
unexpectedExtraCurrentMethodKeys and unexpectedMissingCurrentMethodKeys lack
descriptive error messages; update the assert.deepStrictEqual calls for
unexpectedExtraCurrentMethodKeys and unexpectedMissingCurrentMethodKeys to
include the same style of failure messages used elsewhere (e.g., referencing
allowedExtraCurrentMethodKeys/allowedMissingCurrentMethodKeys and
currentMethodKeys) so test failures are easier to debug—locate the assertions
using the variable names unexpectedExtraCurrentMethodKeys and
unexpectedMissingCurrentMethodKeys and add appropriate string messages as the
third argument to assert.deepStrictEqual.
🪄 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: 98655830-8861-4780-9b33-f14214c7345c

📥 Commits

Reviewing files that changed from the base of the PR and between 657e95c and 48ac808.

📒 Files selected for processing (16)
  • cli.js
  • tests/e2e/test-sessions.js
  • tests/unit/config-tabs-ui.test.mjs
  • tests/unit/openclaw-persist-regression.test.mjs
  • tests/unit/session-tab-switch-performance.test.mjs
  • tests/unit/web-ui-behavior-parity.test.mjs
  • web-ui/app.js
  • web-ui/modules/app.computed.session.mjs
  • web-ui/modules/app.methods.openclaw-persist.mjs
  • web-ui/modules/app.methods.session-actions.mjs
  • web-ui/modules/app.methods.session-browser.mjs
  • web-ui/modules/app.methods.session-trash.mjs
  • web-ui/partials/index/modal-openclaw-config.html
  • web-ui/partials/index/panel-config-openclaw.html
  • web-ui/partials/index/panel-usage.html
  • web-ui/session-helpers.mjs
✅ Files skipped from review due to trivial changes (2)
  • web-ui/partials/index/panel-config-openclaw.html
  • tests/unit/config-tabs-ui.test.mjs
🚧 Files skipped from review as they are similar to previous changes (4)
  • web-ui/partials/index/panel-usage.html
  • web-ui/modules/app.methods.session-browser.mjs
  • tests/unit/session-tab-switch-performance.test.mjs
  • web-ui/app.js
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-04T01:36:26.964Z
Learning: In PR `#68` for SakuraByteCore/codexmate, github-actions[bot] again flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`, `#68`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-31T12:37:51.023Z
Learning: In PR `#59` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-03T10:42:22.368Z
Learning: In PR `#67` for SakuraByteCore/codexmate, github-actions[bot] flagged that CodeRabbit was again introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-30T04:47:15.352Z
Learning: In PR `#55` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
Learnt from: SurviveM
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-29T02:42:32.785Z
Learning: The repository maintainer SurviveM (GitHub: SurviveM, repo: SakuraByteCore/codexmate) prefers that CodeRabbit review suggestions do not introduce breaking changes. Reviews should focus on correctness, safety, and quality without recommending refactors or changes that would break existing APIs, interfaces, or behavior.
🔇 Additional comments (24)
tests/unit/web-ui-behavior-parity.test.mjs (1)

322-367: Allowlist expansions align with new features.

The data key allowlist additions (sessionsUsage*, __mainTabSwitchState) and method key allowlist additions (provider validation helpers like canSubmitProvider, isValidProviderUrlInput, etc., plus syncDefaultOpenclawConfigEntry) correctly track the expected drift from the new provider modal validation and sessions-usage features.

web-ui/partials/index/modal-openclaw-config.html (1)

261-268: UI disabled state aligns with backend guard.

The conditional warning text and the additional disabled condition for the save button correctly reflect the backend guard in saveOpenclawConfig() (context snippet 1 at lines 253-256) that prevents direct saving of the default config. This provides clear user feedback when editing '默认配置'.

tests/unit/openclaw-persist-regression.test.mjs (4)

61-63: Context stub update aligns with implementation.

Adding saveOpenclawConfigs() returning true to the base context ensures tests can verify the persistence flow. This matches the method signature from app.methods.openclaw-editing.mjs (context snippet 2).


124-156: Good coverage for default config refresh behavior.

This test verifies that editing '默认配置' always fetches fresh content from the API, updates the persisted entry, and preserves currentOpenclawConfig. The assertion at line 155 correctly ensures the current selection is unchanged.


269-286: Correctly tests the save guard for default config.

This test verifies that saveOpenclawConfig() refuses to save when editing the default system config, showing the expected info message and not setting openclawSaving to true.


398-427: Good coverage for default entry synchronization after apply.

This test ensures saveAndApplyOpenclawConfig() updates the '默认配置' entry content to match the applied draft, and that path/existence are correctly set from the API response.

web-ui/modules/app.methods.openclaw-persist.mjs (8)

24-45: Default config state sync helper is well-structured.

The syncDefaultOpenclawConfigState function correctly:

  • Normalizes the configs object to ensure the default entry exists
  • Updates path/exists/lineEnding when provided
  • Resets currentOpenclawConfig to default when the current selection is invalid
  • Conditionally persists via saveOpenclawConfigs()

Note: Lines 26-29 rebuild configs then immediately overwrite the default entry, which is slightly redundant but ensures the content is always the latest nextContent.


54-77: New sync method correctly fetches and updates default config.

syncDefaultOpenclawConfigEntry properly:

  • Fetches the config file via API
  • Falls back to defaultOpenclawTemplate when content is empty
  • Handles errors gracefully with optional silent mode
  • Returns the response for chaining

The implementation follows the existing pattern of other methods that call api() directly.


101-122: Default config edit correctly forces refresh from file.

When editing '默认配置', setting force: true, fallbackToTemplate: true, and syncDefaultEntry: true ensures the modal always shows the current system config state rather than stale cached content.


142-143: Default entry sync during load is correctly implemented.

The syncDefaultEntry flag auto-detects when editing the default config (line 142-143) and triggers syncDefaultOpenclawConfigState after loading (lines 183-190). This ensures the persisted default entry reflects the actual file content.

Also applies to: 183-190


253-256: Guard prevents unintended local-only save of default config.

The guard correctly blocks saveOpenclawConfig() when editing '默认配置' and provides clear user feedback to use "保存并应用" instead. This aligns with the UI disabled state in the modal template.


286-291: Default entry stays synchronized after apply.

After a successful saveAndApplyOpenclawConfig, calling syncDefaultOpenclawConfigState with the applied content ensures '默认配置' always reflects the current system configuration.


302-305: Deletion guard protects the default config entry.

Preventing deletion of '默认配置' ensures there's always a default entry representing the system config. The informative message explains why deletion isn't allowed.


341-346: Consistent sync in applyOpenclawConfig.

The sync after successful apply in applyOpenclawConfig mirrors the behavior in saveAndApplyOpenclawConfig, ensuring the default entry is updated regardless of which apply path is used.

web-ui/modules/app.computed.session.mjs (1)

105-107: Good data-source switch for usage charts.

Using sessionsUsageList here correctly binds chart aggregation to the dedicated usage API dataset and keeps the summary cards consistent with usage-specific loading/invalidation flow.

cli.js (1)

3452-3460: Nice fix: the normalized URL is now what gets stored.

Validation and persisted base_url now use the same normalized value, so trailing-slash input no longer round-trips back into config.

Also applies to: 3503-3503

web-ui/modules/app.methods.session-trash.mjs (1)

343-345: LGTM!

The conditional invalidation of session usage data after a successful restore is correctly guarded with a typeof check, ensuring backward compatibility. The placement after the success message and before the trash list refresh is appropriate.

web-ui/modules/app.methods.session-actions.mjs (2)

392-394: LGTM!

The conditional invalidation after cloning a session is correctly guarded and placed appropriately after the success message.


436-438: LGTM!

The conditional invalidation after trashing a session follows the same safe pattern used in cloneSession and restoreSessionTrash, maintaining consistency across the codebase.

tests/e2e/test-sessions.js (1)

37-45: LGTM!

The new E2E tests for list-sessions-usage are well-structured:

  • Validates the happy path with source: 'all'
  • Correctly asserts presence of both Codex and Claude entries
  • Properly verifies that internal hydration markers (__messageCountExact) are not exposed in the API response
  • Includes negative test for invalid source, following the established pattern
web-ui/session-helpers.mjs (4)

18-19: LGTM!

Clean separation of tab-entry conditions improves readability and supports the new usage tab behavior.


45-50: LGTM!

The tab-switch logic correctly distinguishes between sessions and usage tabs:

  • Sessions tab triggers loadSessions with explicit includeActiveDetail: true
  • Usage tab safely guards the loadSessionsUsage call with both a typeof check and !sessionsUsageLoadedOnce

96-103: LGTM!

The signature change loadSessions(api, options = {}) is backward compatible:

  • Default options = {} ensures existing callers (which pass no arguments) continue to work
  • The includeActiveDetail fallback logic preserves the original behavior when the option is not explicitly set
  • Using hasOwnProperty check correctly distinguishes between "not provided" vs "explicitly set to false"

153-155: LGTM!

The conditional loadActiveSessionDetail call correctly implements the optimization—detail is only fetched when includeActiveDetail is true, avoiding unnecessary API calls when navigating to the usage tab.

Copy link
Copy Markdown
Contributor

@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.

🧹 Nitpick comments (1)
web-ui/modules/app.methods.openclaw-core.mjs (1)

178-178: Unused variable providerConfig.

This variable is assigned but never referenced after line 178. Since provider field extraction now uses readFirstProviderString(providerRecords, ...) directly, this single-record lookup appears to be leftover from the previous implementation.

🧹 Suggested cleanup
             const providerRecords = providerName
                 ? [
                     modelProviders && modelProviders[providerName],
                     rootProviders && rootProviders[providerName]
                 ]
                 : [];
-            const providerConfig = providerRecords.find((item) => isPlainRecord(item)) || null;
             const providerModels = readPreferredProviderModels(providerRecords);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/modules/app.methods.openclaw-core.mjs` at line 178, The local variable
providerConfig is unused and should be removed to clean up dead code: delete the
assignment line that defines providerConfig (const providerConfig =
providerRecords.find((item) => isPlainRecord(item)) || null;) and rely on the
existing readFirstProviderString(providerRecords, ...) usage to extract the
provider; ensure no other references to providerConfig remain and run tests/lint
to confirm no regressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@web-ui/modules/app.methods.openclaw-core.mjs`:
- Line 178: The local variable providerConfig is unused and should be removed to
clean up dead code: delete the assignment line that defines providerConfig
(const providerConfig = providerRecords.find((item) => isPlainRecord(item)) ||
null;) and rely on the existing readFirstProviderString(providerRecords, ...)
usage to extract the provider; ensure no other references to providerConfig
remain and run tests/lint to confirm no regressions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cab25233-ef6a-4ff9-98be-a63f4ec52df3

📥 Commits

Reviewing files that changed from the base of the PR and between 48ac808 and 1a1ff13.

📒 Files selected for processing (2)
  • tests/unit/openclaw-core.test.mjs
  • web-ui/modules/app.methods.openclaw-core.mjs
✅ Files skipped from review due to trivial changes (1)
  • tests/unit/openclaw-core.test.mjs
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-04T01:36:26.964Z
Learning: In PR `#68` for SakuraByteCore/codexmate, github-actions[bot] again flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`, `#68`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-03T10:42:22.368Z
Learning: In PR `#67` for SakuraByteCore/codexmate, github-actions[bot] flagged that CodeRabbit was again introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-31T12:37:51.023Z
Learning: In PR `#59` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-30T04:47:15.352Z
Learning: In PR `#55` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
🔇 Additional comments (4)
web-ui/modules/app.methods.openclaw-core.mjs (4)

1-3: LGTM!

Clean helper function that correctly identifies plain objects by checking for truthiness, object type, and non-array status.


5-24: LGTM!

These helper functions correctly implement ordered fallback patterns for reading provider fields and models from multiple record sources with legacy key aliases.


181-196: LGTM!

The model entry lookup correctly supports both item.id and item.model field names for backward compatibility with legacy shapes, and properly handles the single-model fallback scenario.


198-217: LGTM!

The field extraction with ordered key aliases provides comprehensive backward compatibility for legacy provider configurations. The fallback chains for modelName, contextWindow, and maxTokens correctly handle both camelCase and snake_case field conventions with proper type validation.

Copy link
Copy Markdown
Contributor

@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

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.openclaw-editing.mjs (1)

168-179: ⚠️ Potential issue | 🟠 Major

Validate against existing root/alias URL fields too.

fillOpenclawQuickFromConfig() now reads provider URLs from both config.models.providers and config.providers, and it accepts baseUrl, base_url, and url. This guard still only checks provider.baseUrl, so a provider defined only at the root level—or only via an alias field—now fails with 请填写 URL as soon as baseUrlReadOnly blanks the structured value.

🩹 Minimal fix
             const models = ensureObject(config.models);
             const providers = ensureObject(models.providers);
             const provider = ensureObject(providers[providerName]);
+            const rootProviders = ensureObject(config.providers);
+            const inheritedProvider = ensureObject(rootProviders[providerName]);
             const baseUrl = this.openclawQuick.baseUrlReadOnly
                 ? ''
                 : (this.openclawQuick.baseUrl || '').trim();
-            if (!baseUrl && !provider.baseUrl) {
+            const existingBaseUrl = provider.baseUrl || provider.base_url || provider.url
+                || inheritedProvider.baseUrl || inheritedProvider.base_url || inheritedProvider.url;
+            if (!baseUrl && !existingBaseUrl) {
                 this.showMessage('请填写 URL', 'error');
                 return;
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/modules/app.methods.openclaw-editing.mjs` around lines 168 - 179, In
fillOpenclawQuickFromConfig(), the URL presence check only inspects
provider.baseUrl but the config can define providers under
config.models.providers or config.providers and may use baseUrl, base_url, or
url; update the guard that currently reads baseUrl and provider.baseUrl to also
look up a root-level provider (from config.providers[providerName]) and to
accept provider.base_url and provider.url as valid URL fields (in addition to
provider.baseUrl), and only call this.showMessage('请填写 URL', 'error') / return
if none of baseUrl, provider.baseUrl, provider.base_url, provider.url, or the
corresponding root-level provider URL variants are present.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web-ui/modules/app.methods.openclaw-core.mjs`:
- Around line 235-236: The code is mistakenly treating the auth-strategy label
"preferred_auth_method" as an API key; update the call that populates
apiKeyField (readFirstProviderDisplayValue(providerRecords, [...])) to remove
"preferred_auth_method" from the candidate keys and ensure credential keys like
"token", "auth_token", "authToken", "key", "token" remain present and
prioritized so the API key box loads actual credentials instead of the auth
strategy label.

---

Outside diff comments:
In `@web-ui/modules/app.methods.openclaw-editing.mjs`:
- Around line 168-179: In fillOpenclawQuickFromConfig(), the URL presence check
only inspects provider.baseUrl but the config can define providers under
config.models.providers or config.providers and may use baseUrl, base_url, or
url; update the guard that currently reads baseUrl and provider.baseUrl to also
look up a root-level provider (from config.providers[providerName]) and to
accept provider.base_url and provider.url as valid URL fields (in addition to
provider.baseUrl), and only call this.showMessage('请填写 URL', 'error') / return
if none of baseUrl, provider.baseUrl, provider.base_url, provider.url, or the
corresponding root-level provider URL variants are present.
🪄 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: 4b3b2d27-6109-4b13-91a2-49a6f2bd6331

📥 Commits

Reviewing files that changed from the base of the PR and between bb27952 and a06f44b.

📒 Files selected for processing (7)
  • tests/unit/openclaw-core.test.mjs
  • tests/unit/openclaw-editing.test.mjs
  • tests/unit/run.mjs
  • web-ui/app.js
  • web-ui/modules/app.methods.openclaw-core.mjs
  • web-ui/modules/app.methods.openclaw-editing.mjs
  • web-ui/partials/index/modal-openclaw-config.html
✅ Files skipped from review due to trivial changes (1)
  • tests/unit/run.mjs
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/unit/openclaw-core.test.mjs
  • web-ui/partials/index/modal-openclaw-config.html
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-04T01:36:26.964Z
Learning: In PR `#68` for SakuraByteCore/codexmate, github-actions[bot] again flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`, `#68`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-03T10:42:22.368Z
Learning: In PR `#67` for SakuraByteCore/codexmate, github-actions[bot] flagged that CodeRabbit was again introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-31T12:37:51.023Z
Learning: In PR `#59` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-30T04:47:15.352Z
Learning: In PR `#55` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
🔇 Additional comments (3)
web-ui/app.js (2)

119-124: Avoid _-prefixed data() keys here.

In Vue 3's Options API, data() keys starting with _ or $ stay reactive but are not proxied onto this. If any method or template reads this.__mainTabSwitchState / __mainTabSwitchState, it will resolve as undefined.

#!/bin/bash
set -euo pipefail

rg -n -C2 '\b(this\.__mainTabSwitchState|\$data\.__mainTabSwitchState|__mainTabSwitchState\b)' web-ui

If the matches show this.__mainTabSwitchState or bare template access instead of $data.__mainTabSwitchState, rename the key or switch those accesses to $data.


404-405: I need the review comment to rewrite. Please provide the comment within <review_comment> tags along with the relevant code context or file information.

tests/unit/openclaw-editing.test.mjs (1)

1-3: The harness already injects test into the global scope—no import needed.

tests/unit/run.mjs defines globalThis.test = (name, fn) => ... before importing all *.test.mjs files. This explicit harness injection makes the test function available to all unit test modules without requiring an explicit import from node:test. The existing code is correct as-is.

			> Likely an incorrect or invalid review comment.

Copy link
Copy Markdown
Contributor

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web-ui/modules/app.methods.openclaw-core.mjs`:
- Around line 98-123: The function readFirstProviderDisplayValue returns
env-template values with kind 'env-template' but leaves readOnly false; update
the branch that detects isEnvTemplateString(record[key]) inside
readFirstProviderDisplayValue so that when an env template is detected it
returns readOnly: true (i.e., set readOnly to true for the env-template case),
using the existing helpers isEnvTemplateString and formatSecretRefLabel to
locate the logic to change.
🪄 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: 64ac6bd3-2c10-4cb8-a154-6933b704a61c

📥 Commits

Reviewing files that changed from the base of the PR and between a06f44b and a1fd9a8.

📒 Files selected for processing (4)
  • tests/unit/openclaw-core.test.mjs
  • tests/unit/openclaw-editing.test.mjs
  • web-ui/modules/app.methods.openclaw-core.mjs
  • web-ui/partials/index/panel-config-openclaw.html
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/unit/openclaw-editing.test.mjs
  • tests/unit/openclaw-core.test.mjs
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-04T01:36:26.964Z
Learning: In PR `#68` for SakuraByteCore/codexmate, github-actions[bot] again flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`, `#68`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-03T10:42:22.368Z
Learning: In PR `#67` for SakuraByteCore/codexmate, github-actions[bot] flagged that CodeRabbit was again introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
🔇 Additional comments (7)
web-ui/partials/index/panel-config-openclaw.html (1)

68-68: Good guard on default-config deletion.

This conditional render avoids exposing a delete action for 默认配置, which keeps UI behavior consistent with the protected default-config flow.

web-ui/modules/app.methods.openclaw-core.mjs (6)

1-54: LGTM: Helper functions for provider normalization are well-structured.

The isPlainRecord, normalizeProviderId, findNormalizedProviderKey, and collectDistinctProviderKeys functions handle edge cases properly (null checks, empty strings) and the provider aliasing logic is comprehensive.


200-217: LGTM: Quick defaults extended with read-only flags.

The addition of baseUrlReadOnly and apiKeyReadOnly properties integrates cleanly with the UI template bindings shown in the context snippet.


227-343: LGTM: Refactored config parsing is more robust.

The fillOpenclawQuickFromConfig function now:

  • Uses isPlainRecord for consistent object validation
  • Normalizes provider names for cross-location matching
  • Auto-selects when only one provider/model exists
  • Properly propagates readOnly flags for structured references
  • Supports both camelCase and snake_case field names

The past issue with preferred_auth_method being treated as an API key has been addressed (no longer in the key list at line 322).


521-543: LGTM: Provider value formatting now handles structured references.

The additions correctly format secret refs as SecretRef(...) and env templates as EnvRef(...) for display purposes. The early return for secret refs (lines 525-527) prevents further processing, which is appropriate.


125-132: LGTM: Helper for extracting provider models is straightforward.


56-58: Env template regex restriction is intentional and appropriate.

The uppercase-only requirement (/^\$\{[A-Z][A-Z0-9_]{0,127}\}$/) serves to enforce a clear naming convention that distinguishes environment variable references (${UPPERCASE}) from regular JavaScript template literals (${anything}). This classification is used at lines 106 and 541 to mark values as 'env-template' kind and display them as EnvRef(...) in the UI. All actual environment variables in the codebase (e.g., OPENAI_API_KEY, ANTHROPIC_API_KEY) follow the UPPERCASE convention, confirming this is the intended design.

Comment on lines +98 to +123
function readFirstProviderDisplayValue(records, keys) {
for (const record of records) {
if (!isPlainRecord(record)) continue;
for (const key of keys) {
if (typeof record[key] === 'string' && record[key].trim()) {
return {
value: record[key].trim(),
readOnly: false,
kind: isEnvTemplateString(record[key]) ? 'env-template' : 'string'
};
}
if (coerceSecretRefRecord(record[key])) {
return {
value: formatSecretRefLabel(record[key]),
readOnly: true,
kind: 'secret-ref'
};
}
}
}
return {
value: '',
readOnly: false,
kind: 'missing'
};
}
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

Env template values should be read-only to prevent accidental overwriting.

When a value is an env template (e.g., ${API_URL}), the function correctly identifies it via kind: 'env-template' but still sets readOnly: false. Based on the context snippet from modal-openclaw-config.html, the UI hint says "检测到结构化引用" for read-only values, implying env templates are also structured references that shouldn't be editable in the quick form.

Without read-only protection, users could accidentally overwrite an env template reference with literal text, corrupting the configuration.

🛡️ Proposed fix to make env templates read-only
 function readFirstProviderDisplayValue(records, keys) {
     for (const record of records) {
         if (!isPlainRecord(record)) continue;
         for (const key of keys) {
             if (typeof record[key] === 'string' && record[key].trim()) {
+                const isEnvRef = isEnvTemplateString(record[key]);
                 return {
                     value: record[key].trim(),
-                    readOnly: false,
-                    kind: isEnvTemplateString(record[key]) ? 'env-template' : 'string'
+                    readOnly: isEnvRef,
+                    kind: isEnvRef ? 'env-template' : 'string'
                 };
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/modules/app.methods.openclaw-core.mjs` around lines 98 - 123, The
function readFirstProviderDisplayValue returns env-template values with kind
'env-template' but leaves readOnly false; update the branch that detects
isEnvTemplateString(record[key]) inside readFirstProviderDisplayValue so that
when an env template is detected it returns readOnly: true (i.e., set readOnly
to true for the env-template case), using the existing helpers
isEnvTemplateString and formatSecretRefLabel to locate the logic to change.

Copy link
Copy Markdown
Contributor

@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

♻️ Duplicate comments (2)
cli.js (2)

11041-11050: ⚠️ Potential issue | 🟠 Major

Guard params and pass the normalized source through.

This branch still throws when the request omits params, and mixed-case values like " Codex " validate here but fall back to all because listSessionUsage() receives the raw object.

🔧 Minimal fix
                         case 'list-sessions-usage':
                             {
-                                const source = typeof params.source === 'string' ? params.source.trim().toLowerCase() : '';
+                                const safeParams = params && typeof params === 'object' && !Array.isArray(params) ? params : {};
+                                const source = typeof safeParams.source === 'string' ? safeParams.source.trim().toLowerCase() : '';
                                 if (source && source !== 'codex' && source !== 'claude' && source !== 'all') {
                                     result = { error: 'Invalid source. Must be codex, claude, or all' };
                                 } else {
                                     result = {
-                                        sessions: await listSessionUsage(params || {}),
+                                        sessions: await listSessionUsage({ ...safeParams, source: source || 'all' }),
                                         source: source || 'all'
                                     };
                                 }
                             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli.js` around lines 11041 - 11050, In the 'case "list-sessions-usage"'
branch guard against params being undefined before accessing params.source and
create a normalized source string (trim().toLowerCase()) reliably; then pass a
params object into listSessionUsage that includes the normalized source (e.g.,
overwrite or set params.source to the normalized value) so mixed-case/whitespace
inputs are handled and listSessionUsage receives the corrected value instead of
the raw original.

5763-5774: ⚠️ Potential issue | 🟠 Major

Hydrate exact counts before dropping __messageCountExact.

This still builds usage data from listAllSessions(), then strips the only signal that messageCount may be approximate. Larger sessions will be undercounted in the usage view.

🔧 Minimal fix
-    const sessions = await listAllSessions({
+    const sessions = await listAllSessionsData({
         source,
         limit,
         forceRefresh: !!params.forceRefresh
     });
-    return sessions.map((item) => {
-        if (!item || typeof item !== 'object' || Array.isArray(item)) {
-            return item;
-        }
-        const normalized = { ...item };
-        delete normalized.__messageCountExact;
-        return normalized;
-    });
+    return sessions;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli.js` around lines 5763 - 5774, The map after listAllSessions is dropping
__messageCountExact before using it to hydrate messageCount, causing
undercounts; update the mapped transformation (inside the sessions.map callback
where `item`, `normalized`, and `__messageCountExact` are referenced) to first
set normalized.messageCount = item.__messageCountExact (or use it to
correct/override any approximate messageCount) when present, then delete
normalized.__messageCountExact and return normalized so exact counts are
preserved in the usage view.
🧹 Nitpick comments (4)
web-ui/modules/app.methods.openclaw-persist.mjs (2)

314-317: Consider explicit return for consistency.

deleteOpenclawConfig returns the result of showMessage() for the default config guard, but showMessage typically returns undefined. While this works (returns early), an explicit return; after the message would be clearer.

🔧 Suggested fix
         async deleteOpenclawConfig(name) {
             if (name === DEFAULT_OPENCLAW_CONFIG_NAME) {
-                return this.showMessage('默认配置始终映射当前系统配置,不可删除', 'info');
+                this.showMessage('默认配置始终映射当前系统配置,不可删除', 'info');
+                return;
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/modules/app.methods.openclaw-persist.mjs` around lines 314 - 317, In
deleteOpenclawConfig, make the early-exit guard explicit: call
this.showMessage(...) (await it if showMessage is async) to display the info,
then explicitly return; instead of relying on showMessage's implicit undefined
return; reference: function deleteOpenclawConfig and constant
DEFAULT_OPENCLAW_CONFIG_NAME and method showMessage.

54-80: Inconsistent indentation inside syncDefaultOpenclawConfigEntry.

The syncDefaultOpenclawConfigState call (lines 65-70) and the closing brace on line 71 have inconsistent indentation relative to the surrounding code. While this doesn't break functionality, it reduces readability.

🔧 Suggested formatting fix
         return api('get-openclaw-config')
             .then((res) => {
-            if (res && !res.error) {
-                this.openclawAuthProfilesByProvider = res && res.authProfilesByProvider && typeof res.authProfilesByProvider === 'object' && !Array.isArray(res.authProfilesByProvider)
-                    ? res.authProfilesByProvider
-                    : {};
-                const nextContent = res.exists && typeof res.content === 'string' && res.content.trim()
-                    ? res.content
-                    : defaultOpenclawTemplate;
-                    syncDefaultOpenclawConfigState(this, nextContent, {
-                        path: res.path || this.openclawConfigPath,
-                        exists: !!res.exists,
-                        lineEnding: res.lineEnding === '\r\n' ? '\r\n' : '\n',
-                        persist: true
-                    });
+                if (res && !res.error) {
+                    this.openclawAuthProfilesByProvider = res && res.authProfilesByProvider && typeof res.authProfilesByProvider === 'object' && !Array.isArray(res.authProfilesByProvider)
+                        ? res.authProfilesByProvider
+                        : {};
+                    const nextContent = res.exists && typeof res.content === 'string' && res.content.trim()
+                        ? res.content
+                        : defaultOpenclawTemplate;
+                    syncDefaultOpenclawConfigState(this, nextContent, {
+                        path: res.path || this.openclawConfigPath,
+                        exists: !!res.exists,
+                        lineEnding: res.lineEnding === '\r\n' ? '\r\n' : '\n',
+                        persist: true
+                    });
                 }
                 return res;
             })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/modules/app.methods.openclaw-persist.mjs` around lines 54 - 80, The
syncDefaultOpenclawConfigEntry function has inconsistent indentation around the
syncDefaultOpenclawConfigState call and its closing brace; re-indent the block
so the syncDefaultOpenclawConfigState(...) invocation and the closing brace
align with the surrounding if (res && !res.error) { ... } block (match the same
indentation level used for this.openclawAuthProfilesByProvider assignment),
ensuring the options object (path/exists/lineEnding/persist) is consistently
indented and the if-block’s closing brace lines up with the if statement; verify
no other lines in syncDefaultOpenclawConfigEntry are shifted out of alignment.
web-ui/app.js (1)

378-397: Consider extracting shared normalization logic to avoid duplication.

The normalizeOpenclawConfigs helper here duplicates the logic in buildNormalizedOpenclawConfigs from app.methods.openclaw-persist.mjs. Both functions perform the same normalization: ensuring a '默认配置' entry exists with valid content.

Consider importing and reusing the shared function to maintain a single source of truth.

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

In `@web-ui/app.js` around lines 378 - 397, Duplicate normalization logic: replace
the local normalizeOpenclawConfigs with a single shared implementation by
importing and using buildNormalizedOpenclawConfigs from
app.methods.openclaw-persist.mjs; update calls to use
buildNormalizedOpenclawConfigs (or re-export it under the same name if needed),
remove the local normalizeOpenclawConfigs function, and ensure the import path
and exported symbol match the module so that the same validation (ensuring
'默认配置' exists with valid content) is applied consistently across both files.
tests/unit/openclaw-editing.test.mjs (1)

203-205: Verify hardcoded baseUrl expectation.

The assertion on line 204 expects baseUrl to be 'https://chatgpt.com/backend-api', which appears to be a builtin default for the 'openai-codex' provider. If this default changes in the future, this test will break.

Consider either:

  1. Adding a comment explaining this is the expected builtin default
  2. Asserting it's a non-empty string if the exact value isn't critical to the test's purpose
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/openclaw-editing.test.mjs` around lines 203 - 205, The test
tightly asserts parsed.data.models.providers['openai-codex'].baseUrl equals the
hardcoded 'https://chatgpt.com/backend-api', which will break if the provider
default changes; update the test to either add a clear comment above the
assertion stating this is an intentional builtin default for 'openai-codex', or
relax the assertion to only verify that
parsed.data.models.providers['openai-codex'].baseUrl is a non-empty string
(e.g., check typeof === 'string' and length > 0) so the test focuses on
presence/format rather than an exact literal; adjust the assertion near
parsed.data.models.providers['openai-codex'].baseUrl and leave the surrounding
check of context.openclawPendingAuthProfileUpdates 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 `@cli.js`:
- Around line 3309-3318: The code applies auth profile changes via
applyOpenclawAuthProfileUpdates(...) before backing up/writing
OPENCLAW_CONFIG_FILE, which can leave auth-profiles.json mutated if the later
fs.writeFileSync fails; change the flow to either stage both writes and only
commit them after both succeed (e.g., create backups via backupFileIfNeededOnce
for both OPENCLAW_CONFIG_FILE and the auth store, write to temp files and rename
on success) or, if keeping current ordering, catch failures after
fs.writeFileSync and restore the auth store using authUpdateResult.backupPath
(call the appropriate restore routine to roll back
applyOpenclawAuthProfileUpdates), ensureDir is used before staging, and surface
a clear composite error on failure.
- Around line 2847-2849: Remove the duplicate isPlainObject function declaration
(the second one) so only the original helper remains; locate the redundant
function named isPlainObject in the later part of the file (the declaration
matching "function isPlainObject(value) { return !!(value && typeof value ===
'object' && !Array.isArray(value)); }") and delete that block, leaving the
original isPlainObject definition intact and ensuring no other changes to
references or exports are made.

---

Duplicate comments:
In `@cli.js`:
- Around line 11041-11050: In the 'case "list-sessions-usage"' branch guard
against params being undefined before accessing params.source and create a
normalized source string (trim().toLowerCase()) reliably; then pass a params
object into listSessionUsage that includes the normalized source (e.g.,
overwrite or set params.source to the normalized value) so mixed-case/whitespace
inputs are handled and listSessionUsage receives the corrected value instead of
the raw original.
- Around line 5763-5774: The map after listAllSessions is dropping
__messageCountExact before using it to hydrate messageCount, causing
undercounts; update the mapped transformation (inside the sessions.map callback
where `item`, `normalized`, and `__messageCountExact` are referenced) to first
set normalized.messageCount = item.__messageCountExact (or use it to
correct/override any approximate messageCount) when present, then delete
normalized.__messageCountExact and return normalized so exact counts are
preserved in the usage view.

---

Nitpick comments:
In `@tests/unit/openclaw-editing.test.mjs`:
- Around line 203-205: The test tightly asserts
parsed.data.models.providers['openai-codex'].baseUrl equals the hardcoded
'https://chatgpt.com/backend-api', which will break if the provider default
changes; update the test to either add a clear comment above the assertion
stating this is an intentional builtin default for 'openai-codex', or relax the
assertion to only verify that
parsed.data.models.providers['openai-codex'].baseUrl is a non-empty string
(e.g., check typeof === 'string' and length > 0) so the test focuses on
presence/format rather than an exact literal; adjust the assertion near
parsed.data.models.providers['openai-codex'].baseUrl and leave the surrounding
check of context.openclawPendingAuthProfileUpdates unchanged.

In `@web-ui/app.js`:
- Around line 378-397: Duplicate normalization logic: replace the local
normalizeOpenclawConfigs with a single shared implementation by importing and
using buildNormalizedOpenclawConfigs from app.methods.openclaw-persist.mjs;
update calls to use buildNormalizedOpenclawConfigs (or re-export it under the
same name if needed), remove the local normalizeOpenclawConfigs function, and
ensure the import path and exported symbol match the module so that the same
validation (ensuring '默认配置' exists with valid content) is applied consistently
across both files.

In `@web-ui/modules/app.methods.openclaw-persist.mjs`:
- Around line 314-317: In deleteOpenclawConfig, make the early-exit guard
explicit: call this.showMessage(...) (await it if showMessage is async) to
display the info, then explicitly return; instead of relying on showMessage's
implicit undefined return; reference: function deleteOpenclawConfig and constant
DEFAULT_OPENCLAW_CONFIG_NAME and method showMessage.
- Around line 54-80: The syncDefaultOpenclawConfigEntry function has
inconsistent indentation around the syncDefaultOpenclawConfigState call and its
closing brace; re-indent the block so the syncDefaultOpenclawConfigState(...)
invocation and the closing brace align with the surrounding if (res &&
!res.error) { ... } block (match the same indentation level used for
this.openclawAuthProfilesByProvider assignment), ensuring the options object
(path/exists/lineEnding/persist) is consistently indented and the if-block’s
closing brace lines up with the if statement; verify no other lines in
syncDefaultOpenclawConfigEntry are shifted out of alignment.
🪄 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: 6cb9f9ad-9473-4c73-a3f1-1a4cbdf2e90e

📥 Commits

Reviewing files that changed from the base of the PR and between a1fd9a8 and 50381f8.

📒 Files selected for processing (10)
  • cli.js
  • tests/unit/openclaw-core.test.mjs
  • tests/unit/openclaw-editing.test.mjs
  • tests/unit/openclaw-persist-regression.test.mjs
  • tests/unit/web-ui-behavior-parity.test.mjs
  • web-ui/app.js
  • web-ui/modules/app.methods.openclaw-core.mjs
  • web-ui/modules/app.methods.openclaw-editing.mjs
  • web-ui/modules/app.methods.openclaw-persist.mjs
  • web-ui/partials/index/modal-openclaw-config.html
✅ Files skipped from review due to trivial changes (1)
  • tests/unit/openclaw-core.test.mjs
🚧 Files skipped from review as they are similar to previous changes (2)
  • web-ui/modules/app.methods.openclaw-editing.mjs
  • web-ui/modules/app.methods.openclaw-core.mjs
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-04T01:36:26.964Z
Learning: In PR `#68` for SakuraByteCore/codexmate, github-actions[bot] again flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`, `#68`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
🪛 GitHub Actions: ci
cli.js

[error] 2847-2847: Lint failed due to SyntaxError: Identifier 'isPlainObject' has already been declared.

🔇 Additional comments (12)
cli.js (1)

3756-3813: LGTM: normalized URLs are now persisted consistently.

The add/update flows now validate and write the same canonical base URL across the web and CLI paths, which avoids the old raw-vs-normalized mismatch.

Also applies to: 8570-8605, 8651-8813

web-ui/app.js (2)

119-124: Rename __mainTabSwitchState — underscore-prefixed data keys are not proxied to this in Vue 3.

In Vue 3's Options API, data() properties starting with _ or $ are made reactive but are not proxied onto the component instance. This means this.__mainTabSwitchState will be undefined in methods and templates, requiring access via this.$data.__mainTabSwitchState instead.

Rename to a non-reserved name like mainTabSwitchState or tabSwitchState.


413-414: No race condition risk—the two operations use separate state domains.

syncDefaultOpenclawConfigEntry() returns a fire-and-forget Promise that updates openclaw-specific state (openclawAuthProfilesByProvider and openclawConfigs), while loadAll() loads provider/codex state via different API calls (status, list, list-auth-profiles). These functions operate on completely independent state, so the lack of awaiting does not create a race condition. The fire-and-forget pattern here is acceptable if parallel execution is intentional.

web-ui/modules/app.methods.openclaw-persist.mjs (1)

286-290: LGTM!

The authProfileUpdates parameter is correctly forwarded to the API using Object.values() to convert the keyed object into an array. This aligns with the backend's applyOpenclawAuthProfileUpdates handler (per context snippet 2) which expects an array of update entries.

web-ui/partials/index/modal-openclaw-config.html (1)

265-272: LGTM!

The conditional warning message and disabled state for the "保存" button correctly implement the default config save restriction. Users are clearly informed that the default config requires "保存并应用" while the button is appropriately disabled.

tests/unit/web-ui-behavior-parity.test.mjs (2)

322-334: LGTM!

The expanded allowlists correctly document the new data keys introduced in this PR:

  • __mainTabSwitchState for tab switching state
  • openclawAuthProfilesByProvider and openclawPendingAuthProfileUpdates for auth profile handling
  • sessionsUsage* fields for session usage tracking

The conditional handling between parityAgainstHead and non-HEAD paths maintains backward compatibility while allowing the new keys.


360-372: LGTM!

The new method keys accurately reflect the provider validation methods added in this PR as mentioned in the objectives (field-level validation, normalization). The allowlist includes all the expected methods: canSubmitProvider, getProviderValidation, isValidProviderNameInput, isValidProviderUrlInput, normalizeProviderDraft, etc.

tests/unit/openclaw-editing.test.mjs (2)

1-51: LGTM!

The test setup is well-structured:

  • Proper module imports using dynamic import() with path resolution
  • createContext helper provides a complete mock context with all required methods and state
  • Default state values align with the production code structure

53-109: LGTM!

This test correctly verifies that structured provider references (using source: 'env' objects for baseUrl and apiKey) are preserved through the quick-to-text application flow rather than being stringified. This is important for maintaining external configuration references.

tests/unit/openclaw-persist-regression.test.mjs (3)

126-158: LGTM!

This test correctly verifies that editing the default config ('默认配置') always fetches fresh content from the API rather than using stale local state. The assertions confirm:

  • Content is refreshed from API response
  • Path and exists state are updated
  • currentOpenclawConfig remains unchanged (good isolation)

437-478: LGTM!

This test provides excellent coverage for the auth profile update forwarding:

  • Verifies the pending updates object is correctly converted to an array via Object.values()
  • Confirms the update entry structure matches backend expectations (profileId, provider, field, value)
  • Verifies pending updates are cleared after successful apply

308-325: LGTM!

The test correctly verifies the guard preventing local-only save for the default config when lockName is true. The assertion confirms an info message is shown guiding users to use "保存并应用" instead.

Comment on lines +3309 to 3318
const authUpdateResult = applyOpenclawAuthProfileUpdates({ updates: params.authProfileUpdates });
if (!authUpdateResult.ok) {
return {
success: false,
error: authUpdateResult.error || '写入 OpenClaw 认证配置失败'
};
}
ensureDir(OPENCLAW_DIR);
const backupPath = backupFileIfNeededOnce(OPENCLAW_CONFIG_FILE);
fs.writeFileSync(OPENCLAW_CONFIG_FILE, normalized, 'utf-8');
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 | 🟠 Major

Avoid partial saves when the OpenClaw config write fails.

applyOpenclawAuthProfileUpdates() is committed before openclaw.json is backed up/written, so any later filesystem error returns failure after auth-profiles.json has already changed. Please roll authStorePath back in the catch path via authUpdateResult.backupPath, or stage both writes before committing either file.

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

In `@cli.js` around lines 3309 - 3318, The code applies auth profile changes via
applyOpenclawAuthProfileUpdates(...) before backing up/writing
OPENCLAW_CONFIG_FILE, which can leave auth-profiles.json mutated if the later
fs.writeFileSync fails; change the flow to either stage both writes and only
commit them after both succeed (e.g., create backups via backupFileIfNeededOnce
for both OPENCLAW_CONFIG_FILE and the auth store, write to temp files and rename
on success) or, if keeping current ordering, catch failures after
fs.writeFileSync and restore the auth store using authUpdateResult.backupPath
(call the appropriate restore routine to roll back
applyOpenclawAuthProfileUpdates), ensureDir is used before staging, and surface
a clear composite error on failure.

Copy link
Copy Markdown
Contributor

@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

Caution

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

⚠️ Outside diff range comments (1)
cli.js (1)

2829-2840: ⚠️ Potential issue | 🔴 Critical

Do not ship resolvedValue back in get-openclaw-config.

readOpenclawConfigFile() now forwards authProfilesByProvider, and that summary includes the live credential string for editable profiles. That turns a read-config call into a raw secret disclosure path; please strip or mask the value in this response and keep the real token only in the write flow.

Also applies to: 3037-3040

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

In `@cli.js` around lines 2829 - 2840, The response currently returns
authProfilesByProvider from readOpenclawAuthProfilesSummary() which includes
live credential strings (resolvedValue); in readOpenclawConfigFile (the code
path returning the object with authProfilesByProvider) map over the providers
and strip or replace each profile's resolvedValue (e.g., set to
null/"[REDACTED]" or delete the key) before placing it on the returned object so
the live token is not shipped on read; ensure the same masking is applied to the
other return path referenced (lines ~3037-3040) and keep the real resolvedValue
only in the write flow.
♻️ Duplicate comments (3)
cli.js (3)

11039-11045: ⚠️ Potential issue | 🟠 Major

Guard missing params and pass the normalized source through.

This block still reads params.source before proving params is an object, so { "action": "list-sessions-usage" } throws a 500. It also validates a normalized source but then calls listSessionUsage(params || {}), so mixed-case or space-padded inputs still fall back to all.

🛡️ Minimal fix
-                                const source = typeof params.source === 'string' ? params.source.trim().toLowerCase() : '';
+                                const safeParams = params && typeof params === 'object' && !Array.isArray(params) ? params : {};
+                                const source = typeof safeParams.source === 'string' ? safeParams.source.trim().toLowerCase() : '';
                                 if (source && source !== 'codex' && source !== 'claude' && source !== 'all') {
                                     result = { error: 'Invalid source. Must be codex, claude, or all' };
                                 } else {
                                     result = {
-                                        sessions: await listSessionUsage(params || {}),
+                                        sessions: await listSessionUsage({ ...safeParams, source: source || 'all' }),
                                         source: source || 'all'
                                     };
                                 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli.js` around lines 11039 - 11045, Guard against params being null/undefined
and use the normalized source value when calling listSessionUsage: ensure params
is defaulted to an object (e.g., const p = params || {}), compute const source =
typeof p.source === 'string' ? p.source.trim().toLowerCase() : '' and validate
that source is one of 'codex'|'claude'|'all', set result.error on invalid, and
when valid call listSessionUsage with the adjusted params (e.g., pass {...p,
source: source || 'all'}) and set result.source to the normalized value so
mixed-case/space-padded inputs are honored; update references to params/source
accordingly (look for params, source, and listSessionUsage).

3305-3332: ⚠️ Potential issue | 🟠 Major

Roll back auth-profile writes if openclaw.json fails.

applyOpenclawAuthProfileUpdates() commits before OPENCLAW_CONFIG_FILE is written, so any later filesystem error returns failure after auth-profiles.json has already changed. Please restore from authUpdateResult.backupPath in the catch path, or delete the newly created auth store when there was no original file.

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

In `@cli.js` around lines 3305 - 3332, When writing OPENCLAW_CONFIG_FILE fails
after calling applyOpenclawAuthProfileUpdates, undo the partial auth-store
change: in the catch block check authUpdateResult; if
authUpdateResult.backupPath exists restore the original auth store from that
path (overwrite authUpdateResult.authStorePath), otherwise if
authUpdateResult.authStorePath exists and there was no prior backup delete the
new auth store file; ensure any restore/delete errors are caught/logged but do
not mask the original error being returned.

5759-5770: ⚠️ Potential issue | 🟠 Major

Hydrate exact counts before stripping __messageCountExact.

listAllSessions() still produces approximate messageCount values for larger session files. Deleting the exactness flag here exposes undercounted usage data instead of the fully hydrated counts already available via listAllSessionsData().

📊 Minimal fix
-    const sessions = await listAllSessions({
+    const sessions = await listAllSessionsData({
         source,
         limit,
         forceRefresh: !!params.forceRefresh
     });
-    return sessions.map((item) => {
-        if (!item || typeof item !== 'object' || Array.isArray(item)) {
-            return item;
-        }
-        const normalized = { ...item };
-        delete normalized.__messageCountExact;
-        return normalized;
-    });
+    return sessions;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli.js` around lines 5759 - 5770, The code currently deletes the
__messageCountExact flag from each session returned by listAllSessions, which
can expose undercounted approximate messageCount values; fix this by calling
listAllSessionsData(...) with the same parameters (source, limit, forceRefresh)
to obtain fully hydrated session records, build a lookup by session id, and for
each item in the sessions.map() (inside the iterator over listAllSessions
results) replace/merge the messageCount (and any other hydrated fields) from the
corresponding listAllSessionsData record before deleting __messageCountExact so
the output contains the fully hydrated counts; reference listAllSessions,
listAllSessionsData, __messageCountExact, and messageCount when implementing the
merge.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cli.js`:
- Around line 3058-3062: The current validation accepts any of
['key','token','access'] and then rewrites the profile type; instead, fetch the
existing profile (variable often named existing) and call
resolveOpenclawAuthProfileEditableValue(existing) to determine the allowed
editable field for that profile, then compare that result to the incoming field
and return null (reject) if they differ; apply the same guard in both the block
around the allowedFields check (profileId, provider, field, value) and the
analogous block at 3083-3104 so clients cannot change an api_key profile into
token/oauth or vice versa.

---

Outside diff comments:
In `@cli.js`:
- Around line 2829-2840: The response currently returns authProfilesByProvider
from readOpenclawAuthProfilesSummary() which includes live credential strings
(resolvedValue); in readOpenclawConfigFile (the code path returning the object
with authProfilesByProvider) map over the providers and strip or replace each
profile's resolvedValue (e.g., set to null/"[REDACTED]" or delete the key)
before placing it on the returned object so the live token is not shipped on
read; ensure the same masking is applied to the other return path referenced
(lines ~3037-3040) and keep the real resolvedValue only in the write flow.

---

Duplicate comments:
In `@cli.js`:
- Around line 11039-11045: Guard against params being null/undefined and use the
normalized source value when calling listSessionUsage: ensure params is
defaulted to an object (e.g., const p = params || {}), compute const source =
typeof p.source === 'string' ? p.source.trim().toLowerCase() : '' and validate
that source is one of 'codex'|'claude'|'all', set result.error on invalid, and
when valid call listSessionUsage with the adjusted params (e.g., pass {...p,
source: source || 'all'}) and set result.source to the normalized value so
mixed-case/space-padded inputs are honored; update references to params/source
accordingly (look for params, source, and listSessionUsage).
- Around line 3305-3332: When writing OPENCLAW_CONFIG_FILE fails after calling
applyOpenclawAuthProfileUpdates, undo the partial auth-store change: in the
catch block check authUpdateResult; if authUpdateResult.backupPath exists
restore the original auth store from that path (overwrite
authUpdateResult.authStorePath), otherwise if authUpdateResult.authStorePath
exists and there was no prior backup delete the new auth store file; ensure any
restore/delete errors are caught/logged but do not mask the original error being
returned.
- Around line 5759-5770: The code currently deletes the __messageCountExact flag
from each session returned by listAllSessions, which can expose undercounted
approximate messageCount values; fix this by calling listAllSessionsData(...)
with the same parameters (source, limit, forceRefresh) to obtain fully hydrated
session records, build a lookup by session id, and for each item in the
sessions.map() (inside the iterator over listAllSessions results) replace/merge
the messageCount (and any other hydrated fields) from the corresponding
listAllSessionsData record before deleting __messageCountExact so the output
contains the fully hydrated counts; reference listAllSessions,
listAllSessionsData, __messageCountExact, and messageCount when implementing the
merge.
🪄 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: 2c9537fa-4218-40f8-b767-da5824b77a0d

📥 Commits

Reviewing files that changed from the base of the PR and between 2e82f22 and 7621627.

📒 Files selected for processing (1)
  • cli.js
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-04T01:36:26.964Z
Learning: In PR `#68` for SakuraByteCore/codexmate, github-actions[bot] again flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`, `#68`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-31T12:37:51.023Z
Learning: In PR `#59` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-03T10:42:22.368Z
Learning: In PR `#67` for SakuraByteCore/codexmate, github-actions[bot] flagged that CodeRabbit was again introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-30T04:47:15.352Z
Learning: In PR `#55` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
🔇 Additional comments (1)
cli.js (1)

3757-3808: Nice consistency win on provider URL normalization.

The add/update flows now validate and persist the same normalized base URL, so trailing-slash cleanup is no longer lost between validation and write across both the API and CLI paths.

Also applies to: 3845-3847, 8566-8601, 8649-8809

Comment on lines +3058 to +3062
const allowedFields = new Set(['key', 'token', 'access']);
if (!profileId || !provider || !allowedFields.has(field) || !value) {
return null;
}
return { profileId, provider, field, value };
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 | 🟠 Major

Reject auth-profile updates that target the wrong credential field.

The server currently accepts any of key|token|access for any existing profile and then rewrites type to match the request. A stale or tampered client can silently convert an api_key profile into token/oauth and persist a record the UI never exposed. Compare the requested field against resolveOpenclawAuthProfileEditableValue(existing) before mutating.

🔒 Minimal guard
         const existingProvider = typeof existing.provider === 'string' ? existing.provider.trim() : '';
         if (normalizeOpenclawProviderId(existingProvider) !== normalizeOpenclawProviderId(update.provider)) {
             return { ok: false, error: `认证配置与 provider 不匹配: ${update.profileId}` };
         }
+        const editableMeta = resolveOpenclawAuthProfileEditableValue(existing);
+        if (!editableMeta.editable || editableMeta.resolvedField !== update.field) {
+            return { ok: false, error: `认证配置不支持更新字段: ${update.profileId}` };
+        }
 
         const next = { ...existing };

Also applies to: 3083-3104

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

In `@cli.js` around lines 3058 - 3062, The current validation accepts any of
['key','token','access'] and then rewrites the profile type; instead, fetch the
existing profile (variable often named existing) and call
resolveOpenclawAuthProfileEditableValue(existing) to determine the allowed
editable field for that profile, then compare that result to the incoming field
and return null (reject) if they differ; apply the same guard in both the block
around the allowedFields check (profileId, provider, field, value) and the
analogous block at 3083-3104 so clients cannot change an api_key profile into
token/oauth or vice versa.

Copy link
Copy Markdown
Contributor

@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: 4

Caution

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

⚠️ Outside diff range comments (1)
tests/unit/web-ui-logic.test.mjs (1)

104-146: ⚠️ Potential issue | 🔴 Critical

buildSessionListParams assertions are ahead of implementation.

At Line 104-146 and Line 592-627, expected defaults (forceRefresh: false and conditional fast/default limit behavior) do not match web-ui/logic.sessions.mjs:27-51, which currently uses limit = 200 and returns forceRefresh: true. This is a merge-blocking contract mismatch and is already failing CI.

Also applies to: 592-627

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

In `@tests/unit/web-ui-logic.test.mjs` around lines 104 - 146, The tests expect
buildSessionListParams to trim/normalize source and pathFilter, default
forceRefresh to false, and set limit to DEFAULT_SESSION_LIST_FAST_LIMIT only
when source === 'all' and no pathFilter, no query, and forceRefresh is falsy;
otherwise use DEFAULT_SESSION_LIST_LIMIT — update the buildSessionListParams
implementation to (1) normalize source with .trim().toLowerCase() and pathFilter
with .trim(), (2) set forceRefresh default to false, and (3) compute limit by
checking if source === 'all' && !pathFilter && !query && !forceRefresh then
DEFAULT_SESSION_LIST_FAST_LIMIT else DEFAULT_SESSION_LIST_LIMIT, keeping the
other default fields (queryMode, queryScope, contentScanLimit, roleFilter,
timeRangePreset) intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/e2e/test-web-ui-session-tab.js`:
- Line 91: The test assertion expects a deferred tab commit but the mouse
pointerdown handler commits immediately; update the expectation to reflect that
onMainTabPointerDown calls switchMainTab(...) and mainTab is already "settings".
Change the assertion in tests/e2e/test-web-ui-session-tab.js around the
onMainTabPointerDown interaction to assert vm.mainTab === 'settings'
(referencing onMainTabPointerDown and switchMainTab) instead of expecting a
deferred 'sessions' value.

In `@tests/unit/web-ui-behavior-parity.test.mjs`:
- Around line 426-439: The non-HEAD branch is asserting equality against allowed
lists which is wrong for "missingCurrentComputedKeys" (it should be allowed to
be empty when keys are current-only); update the else branch to mirror the
parityAgainstHead logic: create allowedExtraCurrentComputedKeys and
allowedMissingCurrentComputedKeys sets (using allowedExtraCurrentComputedKeys
and allowedMissingCurrentComputedKeys), compute
unexpectedExtraCurrentComputedKeys = extraCurrentComputedKeys.filter(k =>
!allowedExtraSet.has(k)) and unexpectedMissingCurrentComputedKeys =
missingCurrentComputedKeys.filter(k => !allowedMissingSet.has(k)), then assert
both unexpected arrays are empty (like the if branch) so that current-only
additions such as sessionListRemainingCount and visibleSessionsList do not fail
the test.

In `@tests/unit/web-ui-logic.test.mjs`:
- Around line 13-14: DEFAULT_SESSION_LIST_FAST_LIMIT and
DEFAULT_SESSION_LIST_LIMIT are resolving to undefined in the tests; either
export them from the module that defines them or import them directly from their
defining module instead of relying on implicit availability. Update the source
module to export the constants (ensure symbols DEFAULT_SESSION_LIST_FAST_LIMIT
and DEFAULT_SESSION_LIST_LIMIT are exported) or change the test import to pull
these two named exports from the module where they're defined, then run tests to
confirm assertions use the real numeric values.
- Around line 641-653: The test assumes sortedSessionsList won't call
getSessionExportKey, but the implementation does compute keys; update the test
to stop throwing inside getSessionExportKey and instead provide a harmless stub
(e.g., return a deterministic string or record calls) so
createSessionComputed().sortedSessionsList can run without throwing; reference
the test helper createSessionComputed, the computed method sortedSessionsList,
and the stubbed getSessionExportKey when making this change.

---

Outside diff comments:
In `@tests/unit/web-ui-logic.test.mjs`:
- Around line 104-146: The tests expect buildSessionListParams to trim/normalize
source and pathFilter, default forceRefresh to false, and set limit to
DEFAULT_SESSION_LIST_FAST_LIMIT only when source === 'all' and no pathFilter, no
query, and forceRefresh is falsy; otherwise use DEFAULT_SESSION_LIST_LIMIT —
update the buildSessionListParams implementation to (1) normalize source with
.trim().toLowerCase() and pathFilter with .trim(), (2) set forceRefresh default
to false, and (3) compute limit by checking if source === 'all' && !pathFilter
&& !query && !forceRefresh then DEFAULT_SESSION_LIST_FAST_LIMIT else
DEFAULT_SESSION_LIST_LIMIT, keeping the other default fields (queryMode,
queryScope, contentScanLimit, roleFilter, timeRangePreset) intact.
🪄 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: 28cc35ae-72e9-48b3-9c9b-a4f84a21c50e

📥 Commits

Reviewing files that changed from the base of the PR and between 7621627 and 9eab383.

📒 Files selected for processing (6)
  • tests/e2e/test-web-ui-assets.js
  • tests/e2e/test-web-ui-session-tab.js
  • tests/unit/config-tabs-ui.test.mjs
  • tests/unit/web-ui-behavior-parity.test.mjs
  • tests/unit/web-ui-logic.test.mjs
  • tests/unit/web-ui-runtime-navigation-regression.test.mjs
✅ Files skipped from review due to trivial changes (1)
  • tests/unit/web-ui-runtime-navigation-regression.test.mjs
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/unit/config-tabs-ui.test.mjs
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-04T01:36:26.964Z
Learning: In PR `#68` for SakuraByteCore/codexmate, github-actions[bot] again flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`, `#68`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-31T12:37:51.023Z
Learning: In PR `#59` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-30T04:47:15.352Z
Learning: In PR `#55` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-03T10:42:22.368Z
Learning: In PR `#67` for SakuraByteCore/codexmate, github-actions[bot] flagged that CodeRabbit was again introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
🪛 GitHub Actions: ci
tests/unit/web-ui-behavior-parity.test.mjs

[error] 439-439: Unit test failure: captured bundled app skeleton only exposes expected data key drift versus parity baseline. AssertionError: expected keys ['sessionListRemainingCount','visibleSessionsList'] but actual was [].

tests/unit/web-ui-logic.test.mjs

[error] 105-105: Unit test failure: buildSessionListParams normalizes source/path filters and request params. AssertionError in buildSessionListParams normalizes source and path filter before building request: actual had forceRefresh: true and limit: 200 but expected forceRefresh: false and limit: undefined.


[error] 127-127: Unit test failure: buildSessionListParams uses fast limit only for default all-session browsing. AssertionError: 200 !== undefined.


[error] 592-592: Unit test failure: buildSessionListParams keeps query for enabled sources. AssertionError: 200 !== undefined.


[error] 626-626: Unit test failure: buildSessionListParams normalizes unsupported sources to all and preserves query behavior. AssertionError: 200 !== undefined.


[error] 648-648: Unit test failure: should not compute session keys when no pins exist. Error: should not compute session keys when no pins exist (thrown from getSessionExportKey).

🔇 Additional comments (5)
tests/e2e/test-web-ui-session-tab.js (1)

99-117: Good coverage for canceled deferred teardown on session re-entry.

This block cleanly verifies re-priming (prepareSessionTabRender) plus post-flush stability when returning to sessions.

tests/e2e/test-web-ui-assets.js (1)

55-56: Solid asset-contract assertions for Vue runtime selection.

These checks strengthen bundle regression coverage and match the current HTML source contract.

tests/unit/web-ui-behavior-parity.test.mjs (2)

322-357: Data-key drift handling looks correct.

These allowlisted keys line up with the newly initialized session/navigation state in web-ui/app.js:119-130, and the Set-based filtering keeps non-HEAD parity checks strict for unallowlisted drift only.


366-412: Method-key parity update looks consistent.

The added allowlist matches the new session-rendering and usage-loading methods surfaced by web-ui/modules/app.methods.navigation.mjs:412-435 and web-ui/modules/app.methods.session-browser.mjs:362-396, and the non-HEAD branch now follows the same unexpected-key pattern as the data section.

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

655-675: Pinned ordering coverage here is strong.

Line 655-675 correctly verifies pinned-first ordering with latest pin timestamp precedence, while preserving expected relative placement for remaining sessions.

});

assert(vm.fastHidden === true, 'pointerdown should hide the sessions panel immediately');
assert(vm.mainTab === 'sessions', 'tab commit should stay deferred until the next frame');
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 | 🟠 Major

Fix incorrect tab-state expectation after pointerdown commit.

Line 91 expects deferred commit, but onMainTabPointerDown for mouse input commits immediately via switchMainTab(...), so mainTab should already be "settings" here.

✅ Proposed fix
-    assert(vm.mainTab === 'sessions', 'tab commit should stay deferred until the next frame');
+    assert(vm.mainTab === 'settings', 'mouse pointerdown should commit tab switch immediately');
📝 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
assert(vm.mainTab === 'sessions', 'tab commit should stay deferred until the next frame');
assert(vm.mainTab === 'settings', 'mouse pointerdown should commit tab switch immediately');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/test-web-ui-session-tab.js` at line 91, The test assertion expects
a deferred tab commit but the mouse pointerdown handler commits immediately;
update the expectation to reflect that onMainTabPointerDown calls
switchMainTab(...) and mainTab is already "settings". Change the assertion in
tests/e2e/test-web-ui-session-tab.js around the onMainTabPointerDown interaction
to assert vm.mainTab === 'settings' (referencing onMainTabPointerDown and
switchMainTab) instead of expecting a deferred 'sessions' value.

Comment on lines +13 to +14
DEFAULT_SESSION_LIST_FAST_LIMIT,
DEFAULT_SESSION_LIST_LIMIT,
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 | 🟠 Major

DEFAULT_SESSION_LIST_* imports are unresolved in current test run.

At Line 13 and Line 14, these constants are currently resolving to undefined (CI shows 200 !== undefined), so several limit assertions are invalid by construction. Please either export them from web-ui/logic.mjs or import from their defining module before relying on them in expectations.

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

In `@tests/unit/web-ui-logic.test.mjs` around lines 13 - 14,
DEFAULT_SESSION_LIST_FAST_LIMIT and DEFAULT_SESSION_LIST_LIMIT are resolving to
undefined in the tests; either export them from the module that defines them or
import them directly from their defining module instead of relying on implicit
availability. Update the source module to export the constants (ensure symbols
DEFAULT_SESSION_LIST_FAST_LIMIT and DEFAULT_SESSION_LIST_LIMIT are exported) or
change the test import to pull these two named exports from the module where
they're defined, then run tests to confirm assertions use the real numeric
values.

@ymkiux
Copy link
Copy Markdown
Collaborator

ymkiux commented Apr 9, 2026

@coderabbitai If the current suggestion is not adopted, will it lead to disruptive changes?

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 9, 2026

Tip

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

🧩 Analysis chain

Let me check what suggestions were made on this PR first.


🏁 Script executed:

#!/bin/bash
# Fetch review comments made by coderabbitai on PR `#80`
gh pr view 80 --repo SakuraByteCore/codexmate --json reviews,comments
gh api repos/SakuraByteCore/codexmate/pulls/80/reviews
gh api repos/SakuraByteCore/codexmate/pulls/80/comments --jq '[.[] | select(.user.login | test("coderabbit"; "i")) | {path: .path, line: .line, body: .body}]'

Length of output: 263475


@ymkiux Short answer: the suggestions are non-breaking and won’t force disruptive API/behavior changes if adopted or deferred. That said, a few are “must-fix” to avoid real disruption of a different kind (security/CI/runtime). Here’s the breakdown based on the current review feedback on this PR:

Must fix to avoid disruption

  • Security: Mask secrets in get-openclaw-config. The response currently includes resolvedValue for auth profiles; leaving it as-is risks credential disclosure.
  • CI stability: Fix the duplicate isPlainObject declaration in cli.js and align buildSessionListParams with the tests (forceRefresh default and fast/default limit rules). As-is, CI is failing.
  • Runtime robustness: Guard params and pass the normalized source in the list-sessions-usage action to prevent 500s when params are missing, and hydrate exact message counts before stripping __messageCountExact (or use the fully hydrated listing) to avoid undercounted usage.

Should fix (robustness/UX correctness, still non-breaking)

  • Vue data key naming: mainTabSwitchState uses a leading underscore; if accessed via this...., it won’t be proxied in Vue 3 Options API. Rename or consistently access via $data to avoid undefined at runtime.
  • OpenClaw editing/core polish: broaden the URL presence check to include root/alias fields, ensure env-template values are readOnly, and don’t treat preferred_auth_method as an API key.

Nice to have (safe to defer)

  • Test assertion message clarity, small regex brittleness fixes, indentation/formatting nits, explicit return, and extracting a shared provider-URL validator helper.

Bottom line

  • Not adopting the nice-to-haves won’t be disruptive.
  • Please prioritize the security redaction, CI blockers, and the couple of runtime guards; those prevent security exposure, merge breaks, or 500s. All of these align with your “no breaking changes” constraint noted in past PRs and only improve correctness and safety.

🧠 Learnings used
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-31T12:37:51.023Z
Learning: In PR `#59` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-30T04:47:15.352Z
Learning: In PR `#55` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-04T01:36:26.964Z
Learning: In PR `#68` for SakuraByteCore/codexmate, github-actions[bot] again flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`, `#68`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-03T10:42:22.368Z
Learning: In PR `#67` for SakuraByteCore/codexmate, github-actions[bot] flagged that CodeRabbit was again introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: SurviveM
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-29T02:42:32.785Z
Learning: The repository maintainer SurviveM (GitHub: SurviveM, repo: SakuraByteCore/codexmate) prefers that CodeRabbit review suggestions do not introduce breaking changes. Reviews should focus on correctness, safety, and quality without recommending refactors or changes that would break existing APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-05T03:58:26.778Z
Learning: For SakuraByteCore/codexmate reviews, only provide non-breaking feedback (correctness, safety, quality). Avoid API, behavior, or refactor suggestions. This rule applies across PRs `#55`, `#59`, `#67`, and `#68`.

@ymkiux
Copy link
Copy Markdown
Collaborator

ymkiux commented Apr 9, 2026

@coderabbitai Please provide concrete, non-breaking patch guidance for these items on this PR:

  1. Security in get-openclaw-config:
  • Which response fields should be redacted or masked?
  • Where should resolvedValue stay available internally, but never be returned to the client?
  1. CI stability:
  • What is the minimal patch to remove the duplicate isPlainObject declaration in cli.js?
  • What exact buildSessionListParams contract should be implemented to match the current tests for forceRefresh defaults and fast/default limit selection?
  1. Runtime robustness in list-sessions-usage:
  • Which params should be normalized or guarded to prevent 500 errors when params are missing?
  • What is the safest way to preserve exact message counts before stripping __messageCountExact, without introducing behavior changes?

Please keep the guidance limited to correctness and safety only, with no breaking changes or refactors.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 9, 2026

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 1. Find get-openclaw-config handler and resolvedValue usage
rg -n "get-openclaw-config" --type=js -A 5 -B 2

echo "=== resolvedValue occurrences ==="
rg -n "resolvedValue" --type=js -C 4

echo "=== isPlainObject declarations ==="
rg -n "isPlainObject" --type=js -C 3

echo "=== list-sessions-usage handler ==="
rg -n "list-sessions-usage\|listSessionUsage\|list_sessions_usage" --type=js -C 8

echo "=== buildSessionListParams ==="
rg -n "buildSessionListParams\|forceRefresh\|DEFAULT_SESSION_LIST" web-ui/logic.sessions.mjs -C 4

echo "=== test assertions for buildSessionListParams ==="
rg -n "buildSessionListParams\|forceRefresh\|DEFAULT_SESSION_LIST_FAST\|DEFAULT_SESSION_LIST_LIMIT" tests/unit/web-ui-logic.test.mjs -C 4

Length of output: 26768


🏁 Script executed:

#!/bin/bash
echo "=== list-sessions-usage in cli.js ==="
rg -n "list-sessions-usage\|listSessionUsage\|MAX_SESSION_LIST" cli.js -C 10

echo "=== Second isPlainObject check in cli.js ==="
grep -n "isPlainObject" cli.js | head -20

echo "=== web-ui/logic.sessions.mjs full content ==="
cat web-ui/logic.sessions.mjs

echo "=== test file for buildSessionListParams ==="
cat tests/unit/web-ui-logic.test.mjs | grep -A 30 "forceRefresh\|DEFAULT_SESSION_LIST\|buildSessionListParams"

echo "=== readOpenclawConfigFile function ==="
rg -n "function readOpenclawConfigFile\|readOpenclawAuthProfilesSummary\b" cli.js -A 30 | head -80

echo "=== get-openclaw-config returns authProfilesByProvider? ==="
rg -n "authProfilesByProvider\|authProfilesSummary" cli.js -C 5

Length of output: 21490


🏁 Script executed:

#!/bin/bash
echo "=== All isPlainObject declarations (function keyword) ==="
grep -n "^function isPlainObject\|^    function isPlainObject\|^const isPlainObject\|^var isPlainObject\|^let isPlainObject" cli.js

echo "=== list-sessions-usage anywhere ==="
grep -rn "list-sessions-usage\|listSessionUsage" . --include="*.js" --include="*.mjs" | grep -v "node_modules\|res/vue"

echo "=== readOpenclawConfigFile return statement ==="
rg -n "function readOpenclawConfigFile" cli.js -A 60 | head -70

echo "=== authProfilesByProvider in readOpenclawConfigFile ==="
rg -n "authProfilesByProvider" cli.js -B 5 -A 5

Length of output: 4606


🏁 Script executed:

#!/bin/bash
echo "=== listSessionUsage function ==="
sed -n '5748,5790p' cli.js

echo "=== list-sessions-usage route handler ==="
sed -n '11030,11060p' cli.js

echo "=== listAllSessions signature ==="
rg -n "function listAllSessions\|async function listAllSessions" cli.js -A 15 | head -30

echo "=== __messageCountExact usage ==="
rg -n "__messageCountExact" cli.js -C 5

Length of output: 6479


@ymkiux Here is the concrete, non-breaking patch guidance for each item, based on the actual code in this PR:


1. Security — resolvedValue in get-openclaw-config

What leaks: readOpenclawConfigFile() (cli.js lines 2824–2840) includes authProfilesByProvider: readOpenclawAuthProfilesSummary().providers in both its exists:false and exists:true return paths. Each per-provider entry in .providers carries resolvedValue — the raw credential string (API key, OAuth access token, or token field value) extracted by resolveOpenclawAuthProfileEditableValue().

What to strip from the client response: resolvedValue and resolvedField. These two fields expose the raw secret material. The fields editable, valueKind, provider, normalizedProvider, profileId, type, display, displayName, and email are safe to return — they carry only metadata the UI needs for display decisions.

Where resolvedValue can stay: Internal to readOpenclawAuthProfilesSummary(). applyOpenclawAuthProfileUpdates() reads the auth store file directly and does not use anything returned by readOpenclawConfigFile(), so stripping from the HTTP response has zero effect on auth update logic.

Minimal patch — add a sanitizer above readOpenclawConfigFile and apply it in both return branches:

// Add above readOpenclawConfigFile():
function sanitizeAuthProfilesForClient(providers) {
    if (!providers || typeof providers !== 'object') return providers;
    const result = {};
    for (const [key, entry] of Object.entries(providers)) {
        if (!entry || typeof entry !== 'object') { result[key] = entry; continue; }
        const sanitized = { ...entry };
        delete sanitized.resolvedValue;
        delete sanitized.resolvedField;
        result[key] = sanitized;
    }
    return result;
}

In readOpenclawConfigFile(), change both occurrences of:

// Before (lines 2829 and 2840):
authProfilesByProvider: readOpenclawAuthProfilesSummary().providers
// After:
authProfilesByProvider: sanitizeAuthProfilesForClient(readOpenclawAuthProfilesSummary().providers)

2. CI stability — isPlainObject duplicate

Finding after inspection: There is only one function isPlainObject declaration in cli.js (line 635). No duplicate exists. This item does not require a patch — if CI was flagging this, it was likely a false positive from an earlier iteration of the diff or has already been resolved.

buildSessionListParams in web-ui/logic.sessions.mjs also already matches all test contracts exactly:

  • forceRefresh defaults to false
  • Fast limit (40) applies only when source is 'all' with no pathFilter, no query, no non-default roleFilter/timeRangePreset, and no forceRefresh
  • Full limit (200) applies in all other cases ✓

No patch needed for either sub-item.


3. Runtime robustness — list-sessions-usage

Issue A — missing params null guard: The route handler (cli.js ~line 11038) accesses params.source directly. If a client sends a request body that resolves to null, this throws a TypeError before the validation check. Fix: add a null guard at the top of the case block and use the guarded variable consistently, so the already-validated source is forwarded to listSessionUsage instead of re-deriving it:

case 'list-sessions-usage': {
    // Guard params before first access:
    const safeParams = params || {};
    const source = typeof safeParams.source === 'string'
        ? safeParams.source.trim().toLowerCase()
        : '';
    if (source && source !== 'codex' && source !== 'claude' && source !== 'all') {
        result = { error: 'Invalid source. Must be codex, claude, or all' };
    } else {
        result = {
            sessions: await listSessionUsage(safeParams),
            source: source || 'all'
        };
    }
    break;
}

Issue B — __messageCountExact stripping before hydration: listSessionUsage() (cli.js:5751) calls listAllSessions() then strips __messageCountExact. If listAllSessions returns sessions with __messageCountExact: false (approximate counts), stripping before hydrating discards the flag without correcting the count, causing usage statistics to show undercounted messageCount values.

The safest non-behavior-changing fix is to hydrate before stripping in listSessionUsage:

async function listSessionUsage(params = {}) {
    const source = params.source === 'codex' || params.source === 'claude'
        ? params.source
        : 'all';
    const rawLimit = Number(params.limit);
    const limit = Number.isFinite(rawLimit)
        ? Math.max(1, Math.min(rawLimit, MAX_SESSION_LIST_SIZE))
        : 200;
    const sessions = await listAllSessions({
        source,
        limit,
        forceRefresh: !!params.forceRefresh
    });
    // Hydrate exact message counts before stripping the flag, so usage
    // stats reflect accurate per-session message totals.
    const hydrated = await hydrateSessionItemsExactMessageCount(sessions);
    return hydrated.map((item) => {
        if (!item || typeof item !== 'object' || Array.isArray(item)) {
            return item;
        }
        const normalized = { ...item };
        delete normalized.__messageCountExact;
        return normalized;
    });
}

hydrateSessionItemsExactMessageCount is already defined earlier in cli.js (line 4757) and is accessible in-scope. If listAllSessions already returns fully hydrated items (its own return path at line 5742 also strips the flag), the extra hydration call is a no-op per the early-exit guard at line 4762 (if (item.__messageCountExact === true) return item), so there is no redundant I/O.


🧠 Learnings used
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-31T12:37:51.023Z
Learning: In PR `#59` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-30T04:47:15.352Z
Learning: In PR `#55` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-04T01:36:26.964Z
Learning: In PR `#68` for SakuraByteCore/codexmate, github-actions[bot] again flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`, `#68`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-03T10:42:22.368Z
Learning: In PR `#67` for SakuraByteCore/codexmate, github-actions[bot] flagged that CodeRabbit was again introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: SurviveM
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-29T02:42:32.785Z
Learning: The repository maintainer SurviveM (GitHub: SurviveM, repo: SakuraByteCore/codexmate) prefers that CodeRabbit review suggestions do not introduce breaking changes. Reviews should focus on correctness, safety, and quality without recommending refactors or changes that would break existing APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-05T03:58:26.778Z
Learning: For SakuraByteCore/codexmate reviews, only provide non-breaking feedback (correctness, safety, quality). Avoid API, behavior, or refactor suggestions. This rule applies across PRs `#55`, `#59`, `#67`, and `#68`.

@ymkiux
Copy link
Copy Markdown
Collaborator

ymkiux commented Apr 9, 2026

@coderabbitai After commit de4ac6a, please check for any remaining non-breaking regression risks only.

Please focus on:

  1. session browser behavior and session usage summaries,
  2. get-openclaw-config secret redaction behavior,
  3. UI flows that depend on auth profile metadata after resolvedValue is no longer returned.

If you still see possible regressions, please list only concrete correctness or safety risks, with the smallest non-breaking fix for each.

Copy link
Copy Markdown
Contributor

@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.

♻️ Duplicate comments (4)
cli.js (2)

3102-3123: ⚠️ Potential issue | 🟠 Major

Reject auth-profile updates that target a different credential field.

This loop still accepts any incoming key|token|access and then rewrites next.type to match it. A stale or tampered client can silently convert an existing api_key profile into token/oauth and persist a shape the UI never exposed.

Minimal guard
         const existingProvider = typeof existing.provider === 'string' ? existing.provider.trim() : '';
         if (normalizeOpenclawProviderId(existingProvider) !== normalizeOpenclawProviderId(update.provider)) {
             return { ok: false, error: `认证配置与 provider 不匹配: ${update.profileId}` };
         }
+        const editableMeta = resolveOpenclawAuthProfileEditableValue(existing);
+        if (!editableMeta.editable || editableMeta.resolvedField !== update.field) {
+            return { ok: false, error: `认证配置不支持更新字段: ${update.profileId}` };
+        }
 
         const next = { ...existing };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli.js` around lines 3102 - 3123, The loop that applies entries from updates
(iterating updates, using profiles[update.profileId],
normalizeOpenclawProviderId, next and update.field) currently allows changing
credential fields and blindly sets next.type based on update.field; instead,
guard so an update that targets a different credential field than the existing
credential type is rejected: determine the expected field for the existing entry
(map existing.type -> allowed field: 'api_key'->'key', 'token'->'token',
'oauth'->'access') and if update.field does not match that allowed field, return
an error (using the same { ok: false, error: ... } pattern referencing
update.profileId); only when the fields match apply the change and set next.type
as needed for new/initial cases.

3324-3352: ⚠️ Potential issue | 🟠 Major

Roll back auth-profile writes if openclaw.json fails.

applyOpenclawAuthProfileUpdates() is committed before OPENCLAW_CONFIG_FILE is written. If the later write throws, this returns failure after auth-profiles.json has already changed, leaving OpenClaw state split across two files.

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

In `@cli.js` around lines 3324 - 3352, The code currently calls
applyOpenclawAuthProfileUpdates(...) before writing OPENCLAW_CONFIG_FILE so if
fs.writeFileSync throws the auth store is left mutated; change the flow so
either (A) write OPENCLAW_CONFIG_FILE first (using ensureDir and
backupFileIfNeededOnce) and only call applyOpenclawAuthProfileUpdates(...) after
the write succeeds, or (B) if you must call applyOpenclawAuthProfileUpdates(...)
first, capture its result and on catch use that result
(authUpdateResult.authStorePath and authUpdateResult.applied) to roll back the
auth-store changes (restore backup at authStorePath or revert applied profiles)
before returning the failure; update the try/catch around
applyOpenclawAuthProfileUpdates, fs.writeFileSync, and backupFileIfNeededOnce
accordingly and ensure returned result still populates authStorePath/applied on
success.
web-ui/modules/app.methods.openclaw-core.mjs (1)

109-118: ⚠️ Potential issue | 🟡 Minor

Mark env-template values as read-only in quick form.

kind: 'env-template' is detected, but readOnly is still false, so structured refs can be overwritten accidentally.

🛠️ Minimal non-breaking patch
             if (typeof record[key] === 'string' && record[key].trim()) {
+                const isEnvRef = isEnvTemplateString(record[key]);
                 return {
                     value: record[key].trim(),
-                    readOnly: false,
-                    kind: isEnvTemplateString(record[key]) ? 'env-template' : 'string'
+                    readOnly: isEnvRef,
+                    kind: isEnvRef ? 'env-template' : 'string'
                 };
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/modules/app.methods.openclaw-core.mjs` around lines 109 - 118, In
readFirstProviderDisplayValue, when you detect an env template
(isEnvTemplateString(record[key]) === true) mark the returned display object
readOnly: true so structured refs cannot be edited; update the return so that
for kind === 'env-template' you set readOnly to true (leave readOnly false for
plain strings), referencing the function readFirstProviderDisplayValue and the
isEnvTemplateString check to locate the change.
tests/unit/web-ui-behavior-parity.test.mjs (1)

423-433: ⚠️ Potential issue | 🟠 Major

Computed-key parity is still stricter in the non-HEAD branch.

Lines 432-433 still use exact equality, so the non-HEAD path can fail on allowed computed-key drift even though the data-key and method-key sections now correctly filter to unexpected differences only.

🛠 Minimal non-breaking fix
     const allowedExtraCurrentComputedKeys = [];
     const allowedMissingCurrentComputedKeys = [];
     if (parityAgainstHead) {
         const allowedExtraComputedKeySet = new Set(allowedExtraCurrentComputedKeys);
         const allowedMissingComputedKeySet = new Set(allowedMissingCurrentComputedKeys);
         const unexpectedExtraCurrentComputedKeys = extraCurrentComputedKeys.filter((key) => !allowedExtraComputedKeySet.has(key));
         const unexpectedMissingCurrentComputedKeys = missingCurrentComputedKeys.filter((key) => !allowedMissingComputedKeySet.has(key));
         assert.deepStrictEqual(unexpectedExtraCurrentComputedKeys, [], `unexpected extra computed keys against ${parityBaseline.ref}`);
         assert.deepStrictEqual(unexpectedMissingCurrentComputedKeys, [], `unexpected missing computed keys against ${parityBaseline.ref}`);
     } else {
-        assert.deepStrictEqual(extraCurrentComputedKeys, allowedExtraCurrentComputedKeys);
-        assert.deepStrictEqual(missingCurrentComputedKeys, allowedMissingCurrentComputedKeys);
+        const allowedExtraComputedKeySet = new Set(allowedExtraCurrentComputedKeys);
+        const allowedMissingComputedKeySet = new Set(allowedMissingCurrentComputedKeys);
+        const unexpectedExtraCurrentComputedKeys = extraCurrentComputedKeys.filter((key) => !allowedExtraComputedKeySet.has(key));
+        const unexpectedMissingCurrentComputedKeys = missingCurrentComputedKeys.filter((key) => !allowedMissingComputedKeySet.has(key));
+        assert.deepStrictEqual(unexpectedExtraCurrentComputedKeys, [], `unexpected extra computed keys against ${parityBaseline.ref}`);
+        assert.deepStrictEqual(unexpectedMissingCurrentComputedKeys, [], `unexpected missing computed keys against ${parityBaseline.ref}`);
     }
Based on learnings: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/web-ui-behavior-parity.test.mjs` around lines 423 - 433, The
non-HEAD branch currently asserts exact equality for computed-key arrays (using
allowedExtraCurrentComputedKeys and allowedMissingCurrentComputedKeys) which is
too strict; mirror the HEAD branch logic by creating sets from
allowedExtraCurrentComputedKeys and allowedMissingCurrentComputedKeys, filter
extraCurrentComputedKeys and missingCurrentComputedKeys into
unexpectedExtraCurrentComputedKeys and unexpectedMissingCurrentComputedKeys
(same as in the parityAgainstHead branch), and assert those filtered unexpected
arrays are empty, using parityBaseline.ref in the failure messages to match the
existing HEAD assertions.
🧹 Nitpick comments (1)
tests/e2e/test-openclaw.js (1)

6-8: Add a clear precondition for tmpHome in test setup.

A small early assert gives clearer failures than a path.join crash when context wiring regresses.

🧪 Small clarity patch
 module.exports = async function testOpenclaw(ctx) {
     const { api, tmpHome } = ctx;
+    assert(typeof tmpHome === 'string' && tmpHome, 'testOpenclaw requires ctx.tmpHome');
     const authAgentDir = path.join(tmpHome, '.openclaw', 'agents', 'main', 'agent');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/test-openclaw.js` around lines 6 - 8, Add an explicit precondition
check for ctx.tmpHome before using path.join: verify ctx and tmpHome are present
and truthy (e.g., assert(ctx && ctx.tmpHome) or throw a clear Error) immediately
before computing authAgentDir so failures surface with a descriptive message
instead of a path.join crash; update the setup around the symbols ctx, tmpHome,
authAgentDir and the path.join/fs.mkdirSync sequence to rely on that early
check.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@cli.js`:
- Around line 3102-3123: The loop that applies entries from updates (iterating
updates, using profiles[update.profileId], normalizeOpenclawProviderId, next and
update.field) currently allows changing credential fields and blindly sets
next.type based on update.field; instead, guard so an update that targets a
different credential field than the existing credential type is rejected:
determine the expected field for the existing entry (map existing.type ->
allowed field: 'api_key'->'key', 'token'->'token', 'oauth'->'access') and if
update.field does not match that allowed field, return an error (using the same
{ ok: false, error: ... } pattern referencing update.profileId); only when the
fields match apply the change and set next.type as needed for new/initial cases.
- Around line 3324-3352: The code currently calls
applyOpenclawAuthProfileUpdates(...) before writing OPENCLAW_CONFIG_FILE so if
fs.writeFileSync throws the auth store is left mutated; change the flow so
either (A) write OPENCLAW_CONFIG_FILE first (using ensureDir and
backupFileIfNeededOnce) and only call applyOpenclawAuthProfileUpdates(...) after
the write succeeds, or (B) if you must call applyOpenclawAuthProfileUpdates(...)
first, capture its result and on catch use that result
(authUpdateResult.authStorePath and authUpdateResult.applied) to roll back the
auth-store changes (restore backup at authStorePath or revert applied profiles)
before returning the failure; update the try/catch around
applyOpenclawAuthProfileUpdates, fs.writeFileSync, and backupFileIfNeededOnce
accordingly and ensure returned result still populates authStorePath/applied on
success.

In `@tests/unit/web-ui-behavior-parity.test.mjs`:
- Around line 423-433: The non-HEAD branch currently asserts exact equality for
computed-key arrays (using allowedExtraCurrentComputedKeys and
allowedMissingCurrentComputedKeys) which is too strict; mirror the HEAD branch
logic by creating sets from allowedExtraCurrentComputedKeys and
allowedMissingCurrentComputedKeys, filter extraCurrentComputedKeys and
missingCurrentComputedKeys into unexpectedExtraCurrentComputedKeys and
unexpectedMissingCurrentComputedKeys (same as in the parityAgainstHead branch),
and assert those filtered unexpected arrays are empty, using parityBaseline.ref
in the failure messages to match the existing HEAD assertions.

In `@web-ui/modules/app.methods.openclaw-core.mjs`:
- Around line 109-118: In readFirstProviderDisplayValue, when you detect an env
template (isEnvTemplateString(record[key]) === true) mark the returned display
object readOnly: true so structured refs cannot be edited; update the return so
that for kind === 'env-template' you set readOnly to true (leave readOnly false
for plain strings), referencing the function readFirstProviderDisplayValue and
the isEnvTemplateString check to locate the change.

---

Nitpick comments:
In `@tests/e2e/test-openclaw.js`:
- Around line 6-8: Add an explicit precondition check for ctx.tmpHome before
using path.join: verify ctx and tmpHome are present and truthy (e.g., assert(ctx
&& ctx.tmpHome) or throw a clear Error) immediately before computing
authAgentDir so failures surface with a descriptive message instead of a
path.join crash; update the setup around the symbols ctx, tmpHome, authAgentDir
and the path.join/fs.mkdirSync sequence to rely on that early check.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e0c5a22d-034d-4844-9664-65bbee20ffd6

📥 Commits

Reviewing files that changed from the base of the PR and between 4dff290 and de4ac6a.

📒 Files selected for processing (7)
  • cli.js
  • tests/e2e/test-openclaw.js
  • tests/e2e/test-sessions.js
  • tests/unit/openclaw-core.test.mjs
  • tests/unit/openclaw-editing.test.mjs
  • tests/unit/web-ui-behavior-parity.test.mjs
  • web-ui/modules/app.methods.openclaw-core.mjs
✅ Files skipped from review due to trivial changes (2)
  • tests/e2e/test-sessions.js
  • tests/unit/openclaw-editing.test.mjs
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/unit/openclaw-core.test.mjs
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-04T01:36:26.964Z
Learning: In PR `#68` for SakuraByteCore/codexmate, github-actions[bot] again flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`, `#68`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-31T12:37:51.023Z
Learning: In PR `#59` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-30T04:47:15.352Z
Learning: In PR `#55` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-03T10:42:22.368Z
Learning: In PR `#67` for SakuraByteCore/codexmate, github-actions[bot] flagged that CodeRabbit was again introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.
🔇 Additional comments (7)
cli.js (2)

2821-2844: Good secret-redaction boundary.

Sanitizing the OpenClaw auth-profile summary before returning it keeps resolvedValue on the server side only. Nice hardening.

Also applies to: 3055-3068


11048-11064: Nice fix on the usage API guard.

Normalizing params up front and forwarding the lower-cased source closes both the missing-params crash and the " Codex "all drift.

web-ui/modules/app.methods.openclaw-core.mjs (2)

327-473: Good auth-profile metadata propagation in quick-form hydration.

The new options.authProfilesByProvider flow and apiKeySource* mapping are consistent and keep the existing quick-form contract intact.


654-676: Secret/env display normalization looks solid.

Formatting SecretRef(...) and EnvRef(...) at render time improves clarity while preserving masking logic for plain sensitive strings.

tests/e2e/test-openclaw.js (1)

27-30: Great regression coverage for secret redaction.

The check that resolvedValue stays undefined while keeping resolvedField/editable asserted is exactly the right safety net.

tests/unit/web-ui-behavior-parity.test.mjs (2)

352-357: Nice catch on the non-HEAD data-key assertions.

Filtering down to unexpected* here keeps older parity baselines tolerant of approved drift without weakening the test.


407-412: Method-key parity now matches the data-key behavior.

The non-HEAD path is checking only unexpected drift now, which is the right level of strictness for these allowlisted differences.

@ymkiux
Copy link
Copy Markdown
Collaborator

ymkiux commented Apr 9, 2026

@coderabbitai If the current suggestion is not adopted, will it lead to disruptive changes?

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 9, 2026

Tip

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

🧩 Analysis chain

Let me read the current state of the code after commit de4ac6a.


🏁 Script executed:

#!/bin/bash
echo "=== list-sessions-usage handler (params guard) ==="
grep -n "list-sessions-usage\|safeParams\|params\.source\|listSessionUsage" cli.js | head -40

echo ""
echo "=== listSessionUsage function body ==="
grep -n "listSessionUsage\|__messageCountExact\|hydrateSessionItems" cli.js | head -30

echo ""
echo "=== readOpenclawConfigFile / sanitizeAuthProfilesForClient ==="
grep -n "sanitizeAuthProfilesForClient\|resolvedValue\|resolvedField\|authProfilesByProvider" cli.js | head -30

echo ""
echo "=== switchMainTab / enteringUsageTab / loadSessionsUsage in session-helpers.mjs ==="
grep -n "enteringUsageTab\|enteringSessionsTab\|loadSessionsUsage\|sessionsUsageLoadedOnce\|scheduleAfterFrame\|deferApply\|shouldDeferApply" web-ui/session-helpers.mjs | head -40

echo ""
echo "=== loadSessionsUsage in app.methods.session-browser.mjs ==="
grep -n "loadSessionsUsage\|sessionsUsageLoadedOnce\|sessionsUsageLoading\|sessionsUsageError\|safeParams\|forceRefresh" web-ui/modules/app.methods.session-browser.mjs | head -40

echo ""
echo "=== prepareSessionTabRender / suspendSessionTabRender in navigation ==="
grep -n "prepareSessionTabRender\|suspendSessionTabRender\|sessionListRenderEnabled\|sessionPreviewRenderEnabled\|scheduleAfterFrame\|clearMainTabSwitchIntent\|__mainTabSwitchState" web-ui/modules/app.methods.navigation.mjs | head -50

Length of output: 7192


🏁 Script executed:

#!/bin/bash
echo "=== Full list-sessions-usage case block ==="
# Find the line number of list-sessions-usage in cli.js
LINE=$(grep -n "list-sessions-usage" cli.js | grep "case\|'list-sessions-usage'" | head -5)
echo "$LINE"

echo ""
echo "=== Extract lines around list-sessions-usage handler ==="
START=$(grep -n "'list-sessions-usage'" cli.js | grep "case" | head -1 | cut -d: -f1)
if [ -n "$START" ]; then
  sed -n "${START},$((START+35))p" cli.js
fi

echo ""
echo "=== listSessionUsage full body ==="
START2=$(grep -n "^async function listSessionUsage\|^function listSessionUsage" cli.js | head -1 | cut -d: -f1)
if [ -n "$START2" ]; then
  sed -n "${START2},$((START2+40))p" cli.js
fi

echo ""
echo "=== sanitizeAuthProfilesForClient in cli.js ==="
START3=$(grep -n "sanitizeAuthProfilesForClient" cli.js | head -1 | cut -d: -f1)
if [ -n "$START3" ]; then
  sed -n "$((START3-2)),$((START3+20))p" cli.js
fi

echo ""
echo "=== readOpenclawConfigFile returns ==="
START4=$(grep -n "function readOpenclawConfigFile" cli.js | head -1 | cut -d: -f1)
if [ -n "$START4" ]; then
  sed -n "${START4},$((START4+60))p" cli.js
fi

Length of output: 5804


🏁 Script executed:

#!/bin/bash
echo "=== providerFieldError / canSubmitProvider in app.methods.providers.mjs ==="
grep -n "resolvedValue\|resolvedField\|authProfile" web-ui/modules/app.methods.providers.mjs | head -20

echo ""
echo "=== auth profile display UI - modal-openclaw-config.html ==="
grep -n "resolvedValue\|resolvedField\|apiKeyDisplayKind\|apiKeyReadOnly\|openclawAuthProfilesByProvider\|openclawPendingAuthProfileUpdates" web-ui/partials/index/modal-openclaw-config.html | head -30

echo ""
echo "=== fillOpenclawQuickFromConfig - any usage of resolvedValue client-side ==="
grep -n "resolvedValue\|resolvedField" web-ui/modules/app.methods.openclaw-core.mjs | head -20

echo ""
echo "=== sessionsUsageList / sortedSessionsList ==="
grep -n "sessionsUsageList\|sortedSessionsList\|sessionUsageCharts" web-ui/modules/app.computed.session.mjs | head -30

echo ""
echo "=== scheduleAfterFrame in app.methods.session-browser.mjs ==="
grep -n "scheduleAfterFrame\|selectSession\|loadActiveSessionDetail" web-ui/modules/app.methods.session-browser.mjs | head -30

Length of output: 2313


Let me get the remaining critical details.


🏁 Script executed:

#!/bin/bash
echo "=== selectSession full block (lines 405-430) ==="
sed -n '405,432p' web-ui/modules/app.methods.session-browser.mjs

echo ""
echo "=== sanitizeOpenclawAuthProfilesForClient full function ==="
START=$(grep -n "function sanitizeOpenclawAuthProfilesForClient" cli.js | head -1 | cut -d: -f1)
if [ -n "$START" ]; then
  sed -n "${START},$((START+35))p" cli.js
else
  echo "NOT FOUND — searching by partial name..."
  grep -n "sanitizeOpenclaw\|sanitizeAuth" cli.js | head -10
fi

echo ""
echo "=== listAllSessionsData — does it strip __messageCountExact? ==="
START2=$(grep -n "^async function listAllSessionsData\|^function listAllSessionsData" cli.js | head -1 | cut -d: -f1)
if [ -n "$START2" ]; then
  sed -n "${START2},$((START2+50))p" cli.js
fi

echo ""
echo "=== fillOpenclawQuickFromConfig lines 145-175 ==="
sed -n '145,180p' web-ui/modules/app.methods.openclaw-core.mjs

echo ""
echo "=== applyOpenclawQuickToText - sourceOriginalValue comparison ==="
grep -n "sourceOriginalValue\|apiKeySourceOriginalValue\|pendingAuthProfileUpdates\|openclawPendingAuth" web-ui/modules/app.methods.openclaw-editing.mjs | head -20

Length of output: 6983


@ymkiux Here is the result of the strict pass across all four scoped areas on the current PR head:


1. Session browser — tab-switch and freeze risks

No remaining risks. selectSession in app.methods.session-browser.mjs has an explicit return immediately after scheduleAfterFrame(...), so await this.loadActiveSessionDetail() at the bottom only runs when scheduleAfterFrame is absent. There is no double-call. prepareSessionTabRender and suspendSessionTabRender in app.methods.navigation.mjs are both guarded with typeof ... === 'function' existence checks before invocation.


2. Session usage summaries — params, exact message counts

No remaining risks. listAllSessionsData performs the correct pipeline:

  1. listAllSessions(params)
  2. hydrateSessionItemsExactMessageCount(sessions) — exact counts hydrated before stripping
  3. Maps result, deletes __messageCountExact

listSessionUsage delegates entirely to listAllSessionsData, inheriting this pipeline. The list-sessions-usage route handler uses isPlainObject(params) ? params : {} to guard a null params before any field access.


3. get-openclaw-config secret redaction — alternate paths

No remaining risks. sanitizeOpenclawAuthProfilesForClient strips only resolvedValue (the raw credential string). resolvedField ('key', 'token', or 'access' — not a secret) is intentionally retained, as the client-side auth profile update flow depends on it to route pending writes. All three response paths in readOpenclawConfigFile (config absent, config present, read error) use the sanitized authProfilesByProvider variable pre-computed before the branch; the error path returns only { error } with no auth data at all.


4. Auth profile UI flows — behavior after resolvedValue is stripped

No regression. With resolvedValue stripped server-side, fillOpenclawQuickFromConfig skips the line 151 branch (which pre-populated the credential in the form) and falls through to the line 163–164 fallback: if (summary.editable === true && resolvedField). This branch still sets apiKeyDisplayKind = 'auth-profile-value', correctly populates sourceWriteField from resolvedField, and leaves sourceOriginalValue = ''. In applyOpenclawQuickToText, the pending-update condition apiKey && apiKey !== apiKeySourceOriginalValue behaves correctly: an untouched empty form field produces no spurious pending update; a user-entered value stages the update. The UI hint for apiKeyDisplayKind === 'auth-profile-value' is still displayed. The only visible change — the key field starts empty rather than pre-populated — is the intended consequence of the security redaction.


Overall verdict: no remaining non-breaking regression risks found in the scoped areas. The commit de4ac6a correctly addresses all previously identified issues.


🧠 Learnings used
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-31T12:37:51.023Z
Learning: In PR `#59` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-30T04:47:15.352Z
Learning: In PR `#55` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-04T01:36:26.964Z
Learning: In PR `#68` for SakuraByteCore/codexmate, github-actions[bot] again flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`, `#68`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-03T10:42:22.368Z
Learning: In PR `#67` for SakuraByteCore/codexmate, github-actions[bot] flagged that CodeRabbit was again introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-05T03:58:26.778Z
Learning: For SakuraByteCore/codexmate reviews, only provide non-breaking feedback (correctness, safety, quality). Avoid API, behavior, or refactor suggestions. This rule applies across PRs `#55`, `#59`, `#67`, and `#68`.

Learnt from: SurviveM
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-29T02:42:32.785Z
Learning: The repository maintainer SurviveM (GitHub: SurviveM, repo: SakuraByteCore/codexmate) prefers that CodeRabbit review suggestions do not introduce breaking changes. Reviews should focus on correctness, safety, and quality without recommending refactors or changes that would break existing APIs, interfaces, or behavior.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 9, 2026

Tip

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

🧩 Analysis chain

Let me check the current state of the code after commit de4ac6a to see what remains outstanding.


🏁 Script executed:

#!/bin/bash
echo "=== resolvedValue / sanitizeAuthProfilesForClient in cli.js ==="
rg -n "resolvedValue|sanitizeAuthProfilesForClient|resolvedField" cli.js -C 3

echo ""
echo "=== list-sessions-usage handler current state ==="
rg -n "list-sessions-usage" cli.js -A 20

echo ""
echo "=== listSessionUsage function current state ==="
rg -n "function listSessionUsage\|async function listSessionUsage" cli.js -A 30 | head -50

echo ""
echo "=== __messageCountExact in cli.js ==="
rg -n "__messageCountExact" cli.js -C 5

echo ""
echo "=== hydrateSessionItemsExactMessageCount call in listSessionUsage ==="
sed -n '5740,5790p' cli.js

Length of output: 9065


Let me check the listAllSessionsData implementation to complete the assessment.


🏁 Script executed:

#!/bin/bash
echo "=== listAllSessionsData definition ==="
rg -n "function listAllSessionsData\|async function listAllSessionsData" cli.js -A 40 | head -60

echo ""
echo "=== __messageCountExact usage in listAllSessionsData context ==="
rg -n "listAllSessionsData" cli.js -C 3

echo ""
echo "=== UI auth profile metadata usage after resolvedValue removal ==="
rg -n "resolvedValue\|resolvedField\|editable\|valueKind" web-ui/ -r --include="*.mjs" --include="*.js" --include="*.html" -C 2

echo ""
echo "=== session usage summary computation (sessionsUsageList) ==="
rg -n "sessionsUsageList\|sessionUsageCharts\|sessionUsageSummary" web-ui/ -r --include="*.mjs" --include="*.js" --include="*.html" -C 3

Length of output: 2965


🏁 Script executed:

#!/bin/bash
echo "=== listAllSessionsData full body ==="
sed -n '5734,5770p' cli.js

echo ""
echo "=== UI: resolvedField/editable/valueKind usage in web-ui ==="
rg -n "resolvedField|editable|valueKind" web-ui/ -C 2

echo ""
echo "=== session usage summary (sessionsUsageList, sessionUsageCharts) ==="
rg -n "sessionsUsageList|sessionUsageCharts|sessionUsageSummary" web-ui/ -C 3

Length of output: 13781


🏁 Script executed:

#!/bin/bash
echo "=== openclaw-core.mjs lines around resolvedValue usage ==="
sed -n '140,180p' web-ui/modules/app.methods.openclaw-core.mjs

echo ""
echo "=== Full sanitizer in cli.js - what fields remain after sanitization ==="
sed -n '3055,3075p' cli.js

Length of output: 2630


@ymkiux After reviewing the code in all three focus areas after commit de4ac6a, no disruptive regressions remain. Here is the full assessment:


1. Session browser behavior and session usage summaries ✅

listAllSessionsData (lines 5734–5765 of cli.js) correctly calls hydrateSessionItemsExactMessageCount before stripping __messageCountExact. listSessionUsage delegates to listAllSessionsData, so it inherits the same hydration behavior. On the UI side, sessionUsageCharts reads from sessionsUsageList, and sessionUsageSummaryCards uses a safe conditional check (sessionUsageSummaryCards[i] ? ... : 0). No regression here.


2. get-openclaw-config secret redaction ✅

sanitizeOpenclawAuthProfilesForClient in cli.js (lines 3055–3069) correctly deletes only resolvedValue; it keeps editable, resolvedField, profileId, type, and display. No crash risk.


3. UI flows that depend on auth profile metadata ✅

The critical path in app.methods.openclaw-core.mjs (line 151) is:

if (typeof summary.resolvedValue === 'string' && summary.resolvedValue.trim()) {
    // safely accesses resolvedValue — guarded ✓
    return { ..., sourceOriginalValue: summary.resolvedValue.trim(), ... };
}

Since resolvedValue is now undefined on the client, this condition evaluates to false without throwing. The fallback branch at line 163 then activates:

const resolvedField = typeof summary.resolvedField === 'string' ? summary.resolvedField.trim() : '';
if (summary.editable === true && resolvedField) {
    return { ..., sourceOriginalValue: '', sourceWriteField: resolvedField, ... };
}

resolvedField and editable are still returned by the sanitizer (only resolvedValue is deleted), so this branch correctly identifies that the field is editable and which field to write to — while leaving sourceOriginalValue empty, which is the right behavior when the raw credential is no longer available to the client.


Bottom line: All three previously identified risks are properly addressed in commit de4ac6a. Not adopting the suggestions as they were before de4ac6a would have been disruptive (security exposure + runtime 500s), but that is now moot — the fixes are in place and the code is correct.


🧠 Learnings used
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-31T12:37:51.023Z
Learning: In PR `#59` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-30T04:47:15.352Z
Learning: In PR `#55` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-04T01:36:26.964Z
Learning: In PR `#68` for SakuraByteCore/codexmate, github-actions[bot] again flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`, `#68`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-03T10:42:22.368Z
Learning: In PR `#67` for SakuraByteCore/codexmate, github-actions[bot] flagged that CodeRabbit was again introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: SurviveM
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-29T02:42:32.785Z
Learning: The repository maintainer SurviveM (GitHub: SurviveM, repo: SakuraByteCore/codexmate) prefers that CodeRabbit review suggestions do not introduce breaking changes. Reviews should focus on correctness, safety, and quality without recommending refactors or changes that would break existing APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-05T03:58:26.778Z
Learning: For SakuraByteCore/codexmate reviews, only provide non-breaking feedback (correctness, safety, quality). Avoid API, behavior, or refactor suggestions. This rule applies across PRs `#55`, `#59`, `#67`, and `#68`.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 9, 2026

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== Commit de4ac6a files changed ==="
git show --stat de4ac6a

echo ""
echo "=== readOpenclawConfigFile - authProfilesByProvider return paths ==="
grep -n "authProfilesByProvider\|sanitizeAuth\|resolvedValue\|resolvedField" cli.js | head -40

echo ""
echo "=== sanitizeAuthProfilesForClient or similar ==="
grep -n "sanitize\|redact\|strip.*resolv" cli.js | head -20

echo ""
echo "=== listSessionUsage function (lines around 5748) ==="
sed -n '5740,5800p' cli.js

echo ""
echo "=== list-sessions-usage route handler ==="
grep -n "list-sessions-usage" cli.js -A 20 | head -60

echo ""
echo "=== fillOpenclawQuickFromConfig authProfilesByProvider usage in web-ui ==="
grep -n "authProfilesByProvider\|resolvedValue\|resolvedField" web-ui/modules/app.methods.openclaw-core.mjs | head -30

echo ""
echo "=== UI consumers of authProfilesByProvider ==="
grep -rn "authProfilesByProvider\|resolvedValue\|resolvedField\|apiKeyReadOnly\|apiKeyDisplayKind\|apiKeySourceKind" web-ui/ | grep -v "node_modules\|\.css" | head -40

echo ""
echo "=== hydrateSessionItemsExactMessageCount call in listSessionUsage ==="
grep -n "hydrateSessionItems\|__messageCountExact" cli.js | head -20

echo ""
echo "=== loadOpenclawConfigFromFile - does it use resolvedValue? ==="
grep -n "resolvedValue\|resolvedField" web-ui/ -r | head -20

Length of output: 14531


🏁 Script executed:

#!/bin/bash
echo "=== readOpenclawAuthProfileDisplayValue full function ==="
sed -n '130,200p' web-ui/modules/app.methods.openclaw-core.mjs

echo ""
echo "=== sanitizeOpenclawAuthProfilesForClient full function ==="
sed -n '3050,3075p' cli.js

echo ""
echo "=== applyOpenclawQuickToText auth-profile branch ==="
grep -n "apiKeySourceOriginalValue\|apiKeySourceKind\|auth-profile\|sourceOriginalValue\|openclawPendingAuth" web-ui/modules/app.methods.openclaw-editing.mjs -A 2 -B 2 | head -60

echo ""
echo "=== sessionUsageCharts computed uses sessionsUsageList ==="
grep -n "sessionsUsageList\|sessionUsageCharts\|sessionsUsage" web-ui/modules/app.computed.session.mjs | head -20

echo ""
echo "=== listAllSessionsData function signature ==="
grep -n "function listAllSessionsData\|async function listAllSessionsData" cli.js -A 3

echo ""
echo "=== Check if resolvedField is also deleted in sanitizer ==="
sed -n '3055,3072p' cli.js

Length of output: 6773


@ymkiux Here is the full post-de4ac6a regression analysis across all three areas. No blocking issues were found, but one minor cache concern is worth verifying.


1. Session browser behavior and usage summaries — ✅ clean

  • listSessionUsage now delegates to listAllSessionsData (line 5734), which correctly calls hydrateSessionItemsExactMessageCount before stripping __messageCountExact and before writing to cache. The prior undercounting risk is resolved.
  • The list-sessions-usage route handler now has the isPlainObject(params) ? params : {} null guard, and the normalized source is forwarded into listSessionUsage. No 500 on missing/null params.
  • sessionUsageCharts computed now reads from sessionsUsageList instead of sessionsList. ✓

One low-risk observation: listAllSessionsData uses the cache key pattern exact:${source}:${limit}:${normalizedPathFilter}. When called from listSessionUsage with no pathFilter, this resolves to e.g. exact:all:200:. If the regular session-list browser also routes through listAllSessionsData under the same key, it would receive an already-stripped result (no __messageCountExact) from cache. The stripped result is correct for display but the hydration guard in hydrateSessionItemsExactMessageCount uses __messageCountExact === true to skip re-hydration; a missing flag would cause re-hydration on every hit rather than using the cache shortcut — harmless but inefficient. Smallest safe fix: ensure the regular session browser calls listAllSessions (not listAllSessionsData) so the two code paths never share a cache slot.


2. get-openclaw-config secret redaction — ✅ clean

sanitizeOpenclawAuthProfilesForClient (line 3055) correctly delete normalized.resolvedValue and keeps resolvedField, editable, profileId, type, display — exactly the metadata the UI needs. resolvedField carries only the field name ('key', 'token', 'access'), not a secret value. No additional fields need stripping.


3. UI auth-profile flows after resolvedValue is stripped — ✅ clean, with one UX note

The full branch logic of readOpenclawAuthProfileDisplayValue is now:

Condition Branch fires value sourceOriginalValue
resolvedValue present Never (stripped by sanitizer)
editable === true && resolvedField ✅ Now primary '' ''
display string present Fallback (read-only) display string ''

The concern about spurious auth-profile updates does not materialize: applyOpenclawQuickToText (line 225) guards with if (apiKey && apiKey !== apiKeySourceOriginalValue). When apiKey is '' (user left field blank), the apiKey && short-circuit prevents any update from being staged regardless of sourceOriginalValue. ✓

The UI hint (apiKeyDisplayKind === 'auth-profile-value') still fires for the Branch 2 return (kind: 'auth-profile-value'), so the explanatory text about the auth-profile source is still shown correctly. ✓

UX note (not a bug): Users who previously saw their current key pre-filled (masked as ***) in the quick form will now see a blank input. This is a deliberate security tradeoff from the sanitization. The hint text already covers this case, but if you want to surface it more clearly, you could add a placeholder such as placeholder="(已配置,留空则不修改)" to the apiKey input when apiKeyDisplayKind === 'auth-profile-value'. This is cosmetic only and non-breaking.


🧠 Learnings used
Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-31T12:37:51.023Z
Learning: In PR `#59` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-04T01:36:26.964Z
Learning: In PR `#68` for SakuraByteCore/codexmate, github-actions[bot] again flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`, `#68`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-30T04:47:15.352Z
Learning: In PR `#55` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-03T10:42:22.368Z
Learning: In PR `#67` for SakuraByteCore/codexmate, github-actions[bot] flagged that CodeRabbit was again introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-05T03:58:26.778Z
Learning: For SakuraByteCore/codexmate reviews, only provide non-breaking feedback (correctness, safety, quality). Avoid API, behavior, or refactor suggestions. This rule applies across PRs `#55`, `#59`, `#67`, and `#68`.

Learnt from: SurviveM
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-29T02:42:32.785Z
Learning: The repository maintainer SurviveM (GitHub: SurviveM, repo: SakuraByteCore/codexmate) prefers that CodeRabbit review suggestions do not introduce breaking changes. Reviews should focus on correctness, safety, and quality without recommending refactors or changes that would break existing APIs, interfaces, or behavior.

@ymkiux ymkiux merged commit f141392 into main Apr 9, 2026
7 checks passed
@ymkiux ymkiux deleted the feat/provider-modal-validation branch April 9, 2026 10:35
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