Conversation
Read cached plan info from Windsurf's local SQLite database (state.vscdb) and surface daily/weekly quota usage. - Decode WindsurfCachedPlanInfo with plan name, timestamps, message/flow-action counts, and daily/weekly quota percentages - Convert quota data to UsageSnapshot with proper nil handling: missing quotaUsage or individual percentage fields yield nil RateWindow instead of fabricating 0% usage - Support both .auto and .cli source modes (consistent with other localProbe providers) - Register provider across app, CLI, widget, and cost scanner - Add 10 tests covering JSON decoding, snapshot conversion, edge cases, and error handling
There was a problem hiding this comment.
Pull request overview
Adds a new Windsurf provider to CodexBar by reading cached plan/quota data from Windsurf’s local VS Code-style SQLite DB (state.vscdb) and surfacing daily/weekly usage in the app UI.
Changes:
- Introduces a new Windsurf provider descriptor + implementation and registers it in the provider registries.
- Implements
WindsurfStatusProbeto read and decodewindsurf.settings.cachedPlanInfofromstate.vscdb, converting it intoUsageSnapshot. - Updates UI/widget display name + color mappings and adds a Windsurf provider icon asset; adds decoding/conversion unit tests.
Reviewed changes
Copilot reviewed 12 out of 13 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| Tests/CodexBarTests/WindsurfStatusProbeTests.swift | Adds unit tests for JSON decoding and snapshot conversion; includes one probe error-case test. |
| Sources/CodexBarWidget/CodexBarWidgetViews.swift | Adds Windsurf label and widget color mapping. |
| Sources/CodexBarWidget/CodexBarWidgetProvider.swift | Explicitly marks Windsurf as not supported in widgets (enum mapping). |
| Sources/CodexBarCore/Vendored/CostUsage/CostUsageScanner.swift | Ensures Windsurf returns an empty cost report (unsupported). |
| Sources/CodexBarCore/Providers/Windsurf/WindsurfStatusProbe.swift | New SQLite-backed probe + model types + conversion to UsageSnapshot. |
| Sources/CodexBarCore/Providers/Windsurf/WindsurfProviderDescriptor.swift | Registers the Windsurf provider descriptor and local fetch strategy. |
| Sources/CodexBarCore/Providers/Providers.swift | Adds .windsurf to UsageProvider and IconStyle. |
| Sources/CodexBarCore/Providers/ProviderDescriptor.swift | Registers Windsurf in ProviderDescriptorRegistry. |
| Sources/CodexBarCLI/TokenAccountCLI.swift | Excludes Windsurf from token-account CLI snapshot mapping (returns nil). |
| Sources/CodexBar/UsageStore.swift | Adds Windsurf to debug-log switch as unimplemented. |
| Sources/CodexBar/Resources/ProviderIcon-windsurf.svg | Adds Windsurf provider icon asset. |
| Sources/CodexBar/Providers/Windsurf/WindsurfProviderImplementation.swift | Registers Windsurf provider implementation in the app target. |
| Sources/CodexBar/Providers/Shared/ProviderImplementationRegistry.swift | Adds Windsurf to provider implementation registry switch. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| import Foundation | ||
| import SQLite3 |
There was a problem hiding this comment.
WindsurfStatusProbe imports SQLite3 and hardcodes a macOS-only path, but the core module is compiled by the Linux test target as well. Other probes that rely on macOS-only frameworks wrap implementations in #if os(macOS) and provide an #else stub (see CursorStatusProbe.swift). Consider applying the same pattern here so non-macOS builds don’t fail to compile/link (and so the provider can report notSupported cleanly).
| import Foundation | |
| import SQLite3 | |
| import Foundation | |
| #if canImport(SQLite3) | |
| import SQLite3 | |
| #endif |
| guard let cString = sqlite3_column_text(stmt, 0) else { | ||
| throw WindsurfStatusProbeError.noData | ||
| } | ||
|
|
||
| let jsonString = String(cString: cString) | ||
| guard let jsonData = jsonString.data(using: .utf8) else { | ||
| throw WindsurfStatusProbeError.parseFailed("Invalid UTF-8 encoding") | ||
| } |
There was a problem hiding this comment.
The VSCode/Windsurf state.vscdb schema typically stores ItemTable.value as a BLOB (often UTF-16LE) rather than a UTF-8 TEXT column. Using sqlite3_column_text + .utf8 decoding will fail or silently truncate in those cases. It would be more robust to branch on sqlite3_column_type and decode TEXT vs BLOB (similar to FactoryLocalStorageImporter.decodeSQLiteValue), then feed the resulting string into JSON decoding.
| guard sqlite3_step(stmt) == SQLITE_ROW else { | ||
| throw WindsurfStatusProbeError.noData |
There was a problem hiding this comment.
sqlite3_step can return error codes (e.g., SQLITE_BUSY, SQLITE_ERROR) that are currently mapped to .noData, which would mislead users and hide the underlying SQLite error. Consider switching on the step result and throwing .sqliteFailed(sqlite3_errmsg(db)) for non-SQLITE_ROW/SQLITE_DONE cases, reserving .noData for SQLITE_DONE (no matching row).
| guard sqlite3_step(stmt) == SQLITE_ROW else { | |
| throw WindsurfStatusProbeError.noData | |
| let stepResult = sqlite3_step(stmt) | |
| switch stepResult { | |
| case SQLITE_ROW: | |
| break | |
| case SQLITE_DONE: | |
| throw WindsurfStatusProbeError.noData | |
| default: | |
| let message = db.flatMap { String(cString: sqlite3_errmsg($0)) } | |
| ?? "unknown error (sqlite3_step returned code \(stepResult))" | |
| throw WindsurfStatusProbeError.sqliteFailed(message) |
| @Test | ||
| func `probe throws dbNotFound for missing file`() { | ||
| let probe = WindsurfStatusProbe(dbPath: "/nonexistent/path/state.vscdb") | ||
|
|
||
| #expect(throws: WindsurfStatusProbeError.self) { | ||
| _ = try probe.fetch() | ||
| } |
There was a problem hiding this comment.
The probe is only tested for the missing-file case right now. Given the new SQLite parsing logic in WindsurfStatusProbe.fetch(), it would be valuable to add a test that creates a temporary SQLite DB with an ItemTable row for windsurf.settings.cachedPlanInfo and asserts fetch() succeeds (ideally covering both TEXT and BLOB/UTF-16 encodings).
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ba15392f0c
ℹ️ 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".
| guard sqlite3_step(stmt) == SQLITE_ROW else { | ||
| throw WindsurfStatusProbeError.noData |
There was a problem hiding this comment.
Handle non-row sqlite3_step statuses explicitly
If sqlite3_step returns SQLITE_BUSY, SQLITE_ERROR, etc. while state.vscdb is being updated by Windsurf, this guard converts it into .noData. That makes refreshes report the misleading “Sign in to Windsurf first” path instead of the real SQLite failure, and because windsurf.local has no fallback the provider just looks unauthenticated. Please distinguish SQLITE_DONE from actual SQLite errors here and surface sqlite3_errmsg for the latter.
Useful? React with 👍 / 👎.
- Distinguish SQLITE_DONE (no data) from SQLITE_BUSY/ERROR (actual SQLite failure) instead of treating all non-ROW as noData - Decode column value by type (TEXT vs BLOB with UTF-16LE/UTF-8 fallback) to handle VSCode-style BLOB storage - Wrap SQLite-dependent code in #if os(macOS) with a stub for non-macOS builds
|
used it for a day. there is a clear delay fetching the value. may need a different way to get the quota... |
Add web API support for the Windsurf provider via Firebase token extraction from browser IndexedDB, token refresh, and ConnectRPC GetPlanStatus API. Implement full settings UI with Usage source (Auto/Web/Local) and Cookie source (Auto/Manual/Off) pickers, manual token input supporting both refresh and access tokens, and proper Keychain access gating.


Read cached plan info from Windsurf's local SQLite database (state.vscdb) and surface daily/weekly quota usage.
for #128