Skip to content

feat(providers): add CodeBuddy CN credit usage provider - #269

Merged
Finesssee merged 4 commits into
nesszer:mainfrom
dong5945:pr/add-codebuddy-cn
Aug 9, 2026
Merged

feat(providers): add CodeBuddy CN credit usage provider#269
Finesssee merged 4 commits into
nesszer:mainfrom
dong5945:pr/add-codebuddy-cn

Conversation

@dong5945

@dong5945 dong5945 commented Aug 7, 2026

Copy link
Copy Markdown

Summary

Adds a CodeBuddy CN provider so Win-CodexBar can show subscription credit remaining / used totals for Tencent CodeBuddy (codebuddy.cn).

What it tracks

  • Account credit packages via POST https://www.codebuddy.cn/billing/meter/get-user-resource
  • Summed CapacitySize / CapacityUsed / CapacityRemain across packages
  • Optional earliest package expire time as reset

Auth sources (priority)

  1. Manual Cookie header (Settings / token accounts)
  2. %USERPROFILE%\.codebuddy\cb_cookie.txt (shared with statusline helpers)
  3. Browser cookies for codebuddy.cn
  4. Auto fallback: ~/.codebuddy/cb_credits.json normalized cache

Notes

  • Uses a Chrome UA without Edg/ (EdgeOne WAF rejects Edge UAs on this path)
  • Package codes can be overridden with CB_PACKAGE_CODES when an account returns empty Accounts
  • Default-disabled in the provider catalog

Test plan

  • cargo test -p codexbar --lib providers::codebuddy
  • Enable CodeBuddy in Settings → paste Cookie from plans-usage Network tab → refresh
  • Confirm remaining/total credits match https://www.codebuddy.cn/profile/plans-usage

Scope

CN CodeBuddy only (codebuddy.cn). International codebuddy.ai can be a follow-up.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Add a cookie-backed CodeBuddy CN provider that reads remaining credits from get-user-resource, with fallbacks for ~/.codebuddy/cb_cookie.txt and the local cb_credits.json cache. Wire ProviderId, factory, token accounts, and tray/settings catalog entries.
@Finesssee

Copy link
Copy Markdown
Collaborator

Thanks for the PR, I will review it ASAP.

Shorten tray metric text to 'N / total left', ellipsis long reset lines in the card row, retry transient WAF/proxy errors, and persist a local credits cache after successful web fetches for Auto fallback.

@Finesssee Finesssee left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

REQUEST_CHANGES — the provider itself is clean and well-tested, but three things must move before merge: the repo's own CI gates fail, and two boundary issues in the new module need fixing while the code is fresh.

Blockers

1. Repo gates do not pass

The fork's checked-in CI (pr-check.yml) runs cargo fmt --all --check, cargo clippy --workspace --all-targets -- -D warnings, and cargo test --workspace. At head SHA 9fa487de:

  • cargo fmt --all --check fails (5 spots): rust/src/core/provider_factory.rs:9 (the CodeBuddyProvider import is out of sort order), rust/src/providers/codebuddy/mod.rs:16 (serde_json::{json, Value}{Value, json}), and codebuddy/mod.rs:148,180,369.
  • cargo clippy --workspace --all-targets -- -D warnings fails: exactly 3 net-new clippy::collapsible_if warnings — rust/src/providers/codebuddy/mod.rs:175, :210, :538. Base (main @ 02971a79) is clean for these.

Note: the fork's PR checks did not run these gates — the only check-run on the head commit is [code]smith (skipped). So this is not CI catching it; the bar fails locally. Please run cargo fmt --all, collapse the 3 nested if let (let-chains or a small helper), and confirm all three gates pass before re-requesting review.

2. Cache persistence round-trips through the display label (codebuddy/mod.rs:437-480)

write_credits_cache_from_snapshot recovers remaining/total by re-parsing its own formatted reset_description ("1,234 / 5,678 left") via parse_short_credits_desc, and writes 0/0/0 on any deviation (unwrap_or((0.0, 0.0))). That couples the cache schema to tray-label formatting and can silently persist a poisoned-zero cache that Auto mode later serves as real data. Display text must not be persistence input: have the API parsing produce a typed CreditTotals { total, used, remaining, reset } (the f64s already exist at snapshot_from_api_payload) and let both snapshot formatting and cache writing consume it. parse_short_credits_desc should become cache-read-only.

3. Auto fallback masks auth failure (codebuddy/mod.rs:616-624)

Both Err(err) if matches!(ctx.source_mode, SourceMode::Auto) => self.fetch_local_cache().or(Err(err)) arms catch every web error, including ProviderError::AuthRequired (401/403). An expired cookie then surfaces as a stale "cli" success, hiding the exact re-auth signal the user needs. Please propagate AuthRequired and fall back to the local cache only for explicitly transient/unavailable errors (network, 429/5xx, WAF-HTML).

Non-blocking (follow-up, not required here)

is_retryable_error (codebuddy/mod.rs:498) decides retryability by string-matching its own formatted error messages; long-term that should become a typed transient/permanent classification at the error-construction site.

Strengths worth keeping

Provider layering is clean: everything CodeBuddy-specific stays inside the new module; registry insertions (ProviderId, all(), as_str, display_name, domain, from_str, token_accounts, provider_settings) follow the fork's existing insertion-order convention; reuse of browser_cookie_header, credentialed_http_client_builder, and the core usage types is correct. No file crosses the 1k threshold because of this PR (provider.rs was already over; the +9 here is unavoidable registry cost), and tests are real: all 6 module tests plus the touched core::provider/core::provider_factory registry tests pass at head.

Zero file overlap between nesszer#271 and nesszer#269 changes; merge is a no-op tree-wise.
- typed CreditTotals: cache persists exact numeric totals/expiry
  instead of re-parsing the tray display label (deleted round2 +
  parse_short_credits_desc)
- typed FetchFailure (Transient/Permanent) classified at construction;
  AuthRequired (401/403/payload-auth) is single-attempt and propagates,
  Auto falls back to cache only on transient failures
- account isolation: cache carries SHA-256 cookie fingerprint
  (accountHash); foreign-account cache rejected
- validation: CB_API_URL endpoint (https, loopback-only http),
  CB_PACKAGE_CODES (strings, <=64, <=128 chars, no control chars)
- limits: 8 MiB response cap, 1 MiB cache cap, non-finite/negative
  totals rejected
- fix number_field short-circuit on missing keys
- rustfmt + clippy -D warnings clean; 21 provider tests (incl. mockito
  end-to-end auth/fallback/isolation), workspace tests green
@Finesssee
Finesssee merged commit 4f0f081 into nesszer:main Aug 9, 2026
1 check passed
@Finesssee

Copy link
Copy Markdown
Collaborator

Maintainer repair applied and merged. 🛠️

Repaired on top of the contributor branch dong5945:pr/add-codebuddy-cn (history preserved: fast-forward push 9fa487de → 279fb63d5, no force-push, non-interactive merge of current main ab61e23bf).

Fixes from the REQUEST_CHANGES review:

  1. Repo gates: rustfmt (5 spots incl. import sort) + clippy -D warnings (3 net-new collapsible_if) — now clean.
  2. Cache persistence: typed CreditTotals written/read as exact numeric JSON + RFC 3339 resetsAt — the tray label is derived from totals, never re-parsed. round2/parse_short_credits_desc deleted.
  3. Auth semantics: typed FetchFailure::Transient/Permanent classified at construction (no string matching). AuthRequired (401/403 or auth-flavoured payload) is single-attempt and always propagates; Auto cache fallback only on transient failures.
  4. Bonus hardening: account-isolated cache (accountHash = truncated SHA-256 of cookie; foreign-account cache rejected), validated CB_API_URL/CB_PACKAGE_CODES overrides, 8 MiB response / 1 MiB cache caps, non-finite/negative totals rejected, number_field missing-key short-circuit fixed.

Validation (all green): cargo fmt --all --check ✓ · cargo clippy --workspace --all-targets -- -D warnings ✓ · cargo test --workspace (1206+1+341) ✓ · 21 CodeBuddy tests incl. mockito end-to-end (401 → propagates despite valid cache; 500 → retried once → cache fallback; foreign cache rejected) ✓ · 231 frontend tests + build ✓. Post-merge main tree is byte-identical to the validated tree.

Squash-merged as 4f0f0819. Thanks @dong5945! 🙏

@dong5945

dong5945 commented Aug 9, 2026

Copy link
Copy Markdown
Author

Thanks for the review and for landing the repairs — appreciated!

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