Add DeepSeek platform session usage dashboard and status submenu#1910
Add DeepSeek platform session usage dashboard and status submenu#1910Yuxin-Qiao wants to merge 16 commits into
Conversation
Co-authored-by: Cursor <cursoragent@cursor.com>
|
Codex review: needs changes before merge. Reviewed July 5, 2026, 12:33 PM ET / 16:33 UTC. Summary Reproducibility: yes. for the review finding: source inspection shows the user-initiated availability path catches importer errors, but the fetch candidate path still swallows the same importer error with Review metrics: 2 noteworthy metrics.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Copy recommended automerge instructionNext step before merge
Maintainer decision needed
Security Review findings
Review detailsBest possible solution: Land the feature only after user-initiated browser-import errors propagate with actionable setup guidance and maintainers accept the DeepSeek web-session credential scope. Do we have a high-confidence way to reproduce the issue? Yes for the review finding: source inspection shows the user-initiated availability path catches importer errors, but the fetch candidate path still swallows the same importer error with Is this the best way to solve the issue? No: the feature direction is useful, but the current patch is not the best mergeable solution until browser-import setup errors propagate from the fetch path instead of becoming generic invalid credentials. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 8e9a844dbd36. Label changesLabel changes:
Label justifications:
Evidence reviewedAcceptance criteria:
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
Review history (9 earlier review cycles; latest 8 shown)
|
Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
💡 Codex Review
When a user has only a DeepSeek platform session (manual DEEPSEEK_COOKIE, cached browser session, or auto-imported cookies) and no API key/token account, this availability check returns false. UsageStore.enabledProviders()/enabledProvidersForBackgroundWork() filters unavailable providers before fetching, so the new DeepSeekWebOnlyFetchStrategy is never reached and the advertised no-API-key web path stays hidden/unrefreshed.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| throw DeepSeekUsageError.invalidCredentials | ||
| } | ||
| // Both are optional/best-effort: a failure in one must not drop the other. | ||
| let summary = try? await self.fetchAccountSummary(session: session) |
There was a problem hiding this comment.
Preserve non-auth DeepSeek account errors
When get_user_summary fails for a transient platform error, parse drift, or cancellation, this try? converts the failure into nil; DeepSeekWebOnlyFetchStrategy.fetch then treats the missing summary as invalidCredentials and clears cached candidates. That can evict a valid cached session and report it as expired even though the session was not rejected, so non-auth summary failures should propagate while only optional identity/usage enrichment is downgraded.
Useful? React with 👍 / 👎.
| let cookieHeader = DeepSeekCookieHeader.header(from: cookies) | ||
| ?? DeepSeekCookieHeader.supplementalHeader(from: cookies) |
There was a problem hiding this comment.
Require bearer auth before using supplemental cookies
This fallback turns any DeepSeek-domain browser cookie into a session, including WAF/tracking cookies from a logged-out profile, even though supplementalHeader is documented as only useful when a bearer token is present. In that logged-out/expired-browser scenario hasSession() reports DeepSeek as available and the web fetch then sends unauthenticated cookies and fails as invalid credentials; only use the supplemental header after a localStorage/live-tab bearer token has been found.
Useful? React with 👍 / 👎.
| if let cached = CookieHeaderCache.load(provider: .deepseek), | ||
| let session = DeepSeekCookieHeader.session(from: cached.cookieHeader) |
There was a problem hiding this comment.
Keep manual DeepSeek mode from falling back to cache
When the user selects Manual mode, this cached-session branch still runs after the explicit manual candidate. If the pasted header is empty/expired but a cached or browser session exists, refresh can silently show usage for that cached browser account instead of failing the manually selected credentials, which breaks account selection/isolation for users switching DeepSeek accounts.
Useful? React with 👍 / 👎.
| if wallets.contains(where: { $0.currency == "USD" && (Double($0.balance ?? "") ?? 0) > 0 }) { | ||
| return "USD" | ||
| } | ||
| return normal.first?.currency ?? bonus.first?.currency ?? "CNY" |
There was a problem hiding this comment.
Prefer a funded wallet over the first empty currency
For platform account summaries with multiple wallets, this falls back to the first wallet's currency whenever USD is not funded. If DeepSeek returns an empty USD wallet before a positive CNY wallet, the parser selects USD, walletBalance returns 0, and the menu shows “add credits” even though the account has CNY balance; mirror the API balance parser's positive-balance preference before falling back to the first row.
Useful? React with 👍 / 👎.
| func shouldFallback(on _: Error, context _: ProviderFetchContext) -> Bool { | ||
| false |
There was a problem hiding this comment.
Fall back to web when the API key is rejected
In Auto mode with both a configured API key and a valid platform web session, any stale or revoked API key stops the pipeline here before the queued deepseek.web strategy can run. That leaves users with working browser credentials seeing an API-key error instead of the new web-only balance/usage path; fallback should be allowed for missing/unauthorized API-key failures.
Useful? React with 👍 / 👎.
| let bearer = liveBearer | ||
| ?? bearerByLabel[session.sourceLabel] | ||
| ?? bearerByLabel.first(where: { self.normalizeStorageLabel($0.key) == normalizedLabel })?.value |
There was a problem hiding this comment.
Avoid applying one profile bearer to all sessions
With multiple Chrome profiles/accounts, this chooses a live-tab bearer token before any label-matched localStorage token, so every imported cookie session is paired with the active tab's account token. The first sorted profile can then fetch/cache usage for a different DeepSeek account than its cookies/source label, so prefer same-label tokens and only use a global live bearer when there is no profile-specific session to pair.
Useful? React with 👍 / 👎.
| currency: account.summary?.currency ?? enriched.currency, | ||
| totalBalance: account.summary?.totalBalance ?? enriched.totalBalance, | ||
| grantedBalance: account.summary?.grantedBalance ?? enriched.grantedBalance, | ||
| toppedUpBalance: account.summary?.paidBalance ?? enriched.toppedUpBalance, |
There was a problem hiding this comment.
Don't overwrite API balance with an unmatched web account
When an API key and browser session belong to different DeepSeek accounts, enrichment replaces the API-key balance/currency with the web session's wallet values. That can make the menu show the browser account's balance while the fetch source/API key is for another account; without an identity match between the two sources, web data should not override the API balance.
Useful? React with 👍 / 👎.
Move session bearer sanitization to cross-platform code, treat web-only sessions as available, isolate manual/auto/off credential sources, and add focused regression tests for browser-import gating. Co-authored-by: Cursor <cursoragent@cursor.com>
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 977e2cdb18
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return true | ||
| } | ||
| #endif | ||
| return false |
There was a problem hiding this comment.
Let auto mode run before a cookie is cached
In Auto mode with no API key, no pasted/env session, and no previously validated cache, this marks DeepSeek unavailable, so UsageStore.enabledProvidersForBackgroundWork() filters it out before a user-initiated refresh can reach DeepSeekCookieImporter.importSessions. That makes the documented first-time browser import path unreachable for users who are only logged in via Chrome; it only starts working after some other path has already created the cache.
Useful? React with 👍 / 👎.
| }), | ||
| noDataMessage: { "DeepSeek usage summaries need a platform.deepseek.com web session." }), | ||
| fetchPlan: ProviderFetchPlan( | ||
| sourceModes: [.auto, .web, .api], |
There was a problem hiding this comment.
Honor DeepSeek source selections in the app
Advertising .web and .api here makes DeepSeek configs with source = "web" or source = "api" pass validation, but the app still gets its fetch mode from ProviderCatalog.implementation(...).sourceMode(...); DeepSeekProviderImplementation does not override the default .auto or read providerConfig.source like the other source-selectable providers do. In the app, a user trying to force web-only or API-only DeepSeek refreshes is silently run through Auto mode instead, so the advertised source selection cannot be enforced.
Useful? React with 👍 / 👎.
| windsurf: WindsurfProviderSettings? = nil, | ||
| perplexity: PerplexityProviderSettings? = nil, | ||
| mimo: MiMoProviderSettings? = nil, | ||
| deepseek: DeepSeekProviderSettings? = nil, |
There was a problem hiding this comment.
Populate DeepSeek settings in CLI snapshots
This adds DeepSeek settings to the shared snapshot, but the CLI snapshot factory was not updated: TokenAccountCLI.makeCookieBackedSnapshot and its makeSnapshot wrapper still have no DeepSeek case/parameter. In CLI runs, context.settings?.deepseek therefore stays nil, so codexbar usage deepseek --source web ignores a DeepSeek cookie_header/manual session from config and also ignores cookie_source = "off" instead of honoring the user's configured web-session source.
Useful? React with 👍 / 👎.
Treat macOS auto mode as available before cache exists, wire provider config source into app fetch mode, and populate DeepSeek settings in CLI snapshots. Co-authored-by: Cursor <cursoragent@cursor.com>
Extract simple provider cookie snapshots into a helper so adding DeepSeek does not exceed the cyclomatic complexity limit. Co-authored-by: Cursor <cursoragent@cursor.com>
Resolve Settings at each makeSnapshot call site so cross-platform builds can compile the extracted provider cookie snapshot helper. Co-authored-by: Cursor <cursoragent@cursor.com>
Remove redundant return keywords so macOS CI swiftformat --lint passes. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8df16e3bd8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| CookieHeaderCache.store( | ||
| provider: .deepseek, | ||
| cookieHeader: payload, |
There was a problem hiding this comment.
Preserve bearer headers when caching web sessions
For imported sessions that include both a cookie and a localStorage/live-tab bearer, candidate.session.storagePayload is two lines (Authorization then Cookie), but CookieHeaderCache.store runs CookieHeaderNormalizer.normalize, whose cookie regex extracts only the Cookie line. After the first successful user-initiated import, the cached candidate therefore loses the bearer and background refreshes fail for sessions that require it, including supplemental-cookie bearer sessions.
Useful? React with 👍 / 👎.
| public var last7DaysTokens: Int { | ||
| self.daily.suffix(7).reduce(0) { $0 + $1.totalTokens } |
There was a problem hiding this comment.
Filter 7-day DeepSeek metrics by calendar date
The DeepSeek daily array is sparse because the parser only emits dates returned by the API, so suffix(7) means the last seven non-empty rows rather than the last seven calendar days. An account with no usage this week but a few rows from 10+ days ago will still show those tokens/cost in the “7d” KPI and chart via trendDays(last:), overstating recent usage; filter by date relative to updatedAt or fill zero days.
Useful? React with 👍 / 👎.
| }), | ||
| noDataMessage: { "DeepSeek usage summaries need a platform.deepseek.com web session." }), | ||
| fetchPlan: ProviderFetchPlan( | ||
| sourceModes: [.auto, .web, .api], |
There was a problem hiding this comment.
Let manual DeepSeek web sessions run on Linux CLI
With .web advertised here, non-macOS CLI preflight treats DeepSeek like a browser-import provider: sourceModeRequiresWebSupport returns true for .web/.auto providers unless they have a manual-cookie exception, and DeepSeek has none. So codexbar usage deepseek --source web with cookie_header or DEEPSEEK_PLATFORM_SESSION exits as unsupported before the new settings.deepseek snapshot reaches DeepSeekWebOnlyFetchStrategy.
Useful? React with 👍 / 👎.
| } else { | ||
| BarMark( | ||
| x: .value(L("Day"), point.date, unit: .day), | ||
| y: .value(L("tokens"), point.totalTokens)) |
There was a problem hiding this comment.
Avoid mixing token bars into cost trends
When any day has cost data, prefersCostTrend makes this chart a cost trend, but days whose point.cost is nil fall into this token branch. A partially populated cost response then plots CNY/USD amounts and token counts on the same hidden y-axis, so a single missing-cost day can dwarf the spend bars and make the DeepSeek usage submenu misleading; render zero/empty cost bars or switch the whole series to tokens consistently.
Useful? React with 👍 / 👎.
| let object = try? JSONSerialization.jsonObject(with: data) as? [String: Any], | ||
| let code = object["code"] as? Int | ||
| else { |
There was a problem hiding this comment.
Treat nested DeepSeek auth codes as invalid credentials
When DeepSeek wraps an auth failure in the already-modeled data.biz_code field with top-level code == 0, this guard returns false and the account/usage parsers later throw apiError instead of invalidCredentials. In that expired-session response shape, cached web sessions are not cleared and the user sees a generic API error rather than the session-expired path; check nested biz_code 40002/40003 before returning false.
Useful? React with 👍 / 👎.
Co-authored-by: Cursor <cursoragent@cursor.com>
…e body. Keeps type_body_length lint green after main adds Claude swap support. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a5d3b4e93f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| priorAmountData = try await self.fetchAmount(session: session, month: prior.month, year: prior.year) | ||
| priorCostData = try await self.fetchCost(session: session, month: prior.month, year: prior.year) |
There was a problem hiding this comment.
Keep prior-month DeepSeek fetch best-effort
During the first 29 days of a month, a transient/server failure on either prior-month request throws out of fetchUsageSummary after the current-month amount and cost calls have already succeeded. Because the prior data is only supplemental for rolling 30-day windows, this makes all DeepSeek usage KPIs/chart data disappear for early-month refreshes instead of showing the available current-month data; treat these prior fetches as optional and parse with nil prior data on failure.
Useful? React with 👍 / 👎.
| } | ||
| #if os(macOS) | ||
| if DeepSeekWebEnrichmentResolver.allowsBrowserCookieImport(context: context), | ||
| DeepSeekCookieImporter.hasSession(browserDetection: context.browserDetection) |
There was a problem hiding this comment.
Surface DeepSeek browser-import access errors
When Chrome cookie import hits a permission/access error on a first-time browser setup, hasSession wraps importSessions in try?, so this availability probe returns false and the web strategy is skipped before DeepSeekSettingsError.missingCookie(details:) can surface the Full Disk Access/browser hint. The user then sees a generic no-strategy/credentials failure even though the actionable error was already produced; avoid using the lossy probe here or propagate the import error when no explicit/cache candidate exists.
Useful? React with 👍 / 👎.
| accountOrganization: nil, | ||
| loginMethod: nil) | ||
| let balanceWindow = RateWindow( | ||
| usedPercent: 100, |
There was a problem hiding this comment.
Avoid treating low-balance alerts as quota depletion
When a DeepSeek account has a nonzero balance below its configured alert bound, setting the primary window to usedPercent: 100 makes UsageStore.sessionQuotaWindow treat this balance row as a depleted session quota because nil-duration primary windows are considered session windows. With session quota notifications enabled, users can get a false “depleted” notification even though they still have credit; keep the alert text/styling separate from the depleted percentage or exclude this balance alert from session quota tracking.
Useful? React with 👍 / 👎.
Preserve bearer+cookie cache payloads, filter rolling usage by calendar day, allow Linux CLI web mode with manual sessions, keep cost charts on one axis, and treat nested DeepSeek auth biz_code failures as invalid credentials. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Require verified email match before replacing API balances, keep prior-month usage enrichment best-effort, surface browser-import failures during fetch, avoid false quota depletion from balance alerts, and isolate the live Chrome probe config write to a temp file. Co-authored-by: Yuxin Qiao <Yuxin-Qiao@users.noreply.github.com>
|
Closing for now — need to revisit the approach; current changes need more work before merge. |
Summary
Screenshots
Menu card (inline usage dashboard)
Token usage details submenu
Status Page submenu
Settings panel
Test plan
swift test --filter DeepSeekmake check./Scripts/compile_and_run.sh— menu card, token usage submenu, and status submenu verified locally