Skip to content

add Windsurf support#583

Open
Coooolfan wants to merge 3 commits intosteipete:mainfrom
Coooolfan:feat/windsurf-provider
Open

add Windsurf support#583
Coooolfan wants to merge 3 commits intosteipete:mainfrom
Coooolfan:feat/windsurf-provider

Conversation

@Coooolfan
Copy link

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

for #128

image image

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
Copilot AI review requested due to automatic review settings March 21, 2026 13:48
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 WindsurfStatusProbe to read and decode windsurf.settings.cachedPlanInfo from state.vscdb, converting it into UsageSnapshot.
  • 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.

Comment on lines +1 to +2
import Foundation
import SQLite3
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

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

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

Suggested change
import Foundation
import SQLite3
import Foundation
#if canImport(SQLite3)
import SQLite3
#endif

Copilot uses AI. Check for mistakes.
Comment on lines +94 to +101
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")
}
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +90 to +91
guard sqlite3_step(stmt) == SQLITE_ROW else {
throw WindsurfStatusProbeError.noData
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

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

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

Suggested change
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)

Copilot uses AI. Check for mistakes.
Comment on lines +187 to +193
@Test
func `probe throws dbNotFound for missing file`() {
let probe = WindsurfStatusProbe(dbPath: "/nonexistent/path/state.vscdb")

#expect(throws: WindsurfStatusProbeError.self) {
_ = try probe.fetch()
}
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

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

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

Copilot uses AI. Check for mistakes.
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 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".

Comment on lines +90 to +91
guard sqlite3_step(stmt) == SQLITE_ROW else {
throw WindsurfStatusProbeError.noData

Choose a reason for hiding this comment

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

P2 Badge 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
@Coooolfan
Copy link
Author

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.
@Coooolfan
Copy link
Author

image image

now adds a web-based usage fetching option for the Windsurf provider (via Firebase tokens from browser IndexedDB). It respects the Keychain toggle in Advanced settings.
also, you can manually paste their own token if preferred. Docs for how to grab the token are included as well.

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