
CodexBar 中的 Windsurf 用量读取浏览器 localStorage 会话导入、本地 SQLite 缓存与 GetPlanStatus Protobuf API 全解析【免费下载链接】CodexBarShow usage stats for OpenAI Codex and Claude Code, without having to login.项目地址: https://gitcode.com/GitHub_Trending/co/CodexBarCodexBar 的 Windsurf Provider 通过三类数据源获取 WindsurfDevin订阅配额基于浏览器会话的 Web API、Windsurf 本地 SQLite 缓存以及两者自动回退的组合链路。本文以 docs/windsurf.md 为骨架结合 WindsurfWebFetcher.swift、WindsurfStatusProbe.swift、WindsurfDevinSessionImporter.swift 等源码逐层拆解读完你将掌握手动会话包的获取与粘贴方法、浏览器会话提取的完整规则、GetPlanStatus 请求/响应的 Protobuf 字段级细节以及每一类典型报错的排查路径。一、架构总览三类数据源与回退链Windsurf 支持两类用量数据源文档中表述为 web API backed by the current website session 与 local SQLite cacheWeb API复用你在浏览器中已登录的 Windsurf/Devin 网站会话调用GetPlanStatus接口获取实时配额日/周剩余百分比与重置时间。本地 SQLite 缓存直接读取 Windsurf 桌面端写入的state.vscdb无需登录凭证但数据只在你启动 Windsurf 时更新。数据源选择器Usage source设置入口Preferences → Providers → Windsurf →Usage source。源码 WindsurfUsageDataSource.swift 定义了三个选项及其 UI 显示名选项源码 caseUI 显示名行为Auto默认.autoAuto先 Web API失败回退本地 SQLiteWeb API.webWeb API (IndexedDB)仅 Web API失败不回退本地Local.cliLocal (SQLite cache)仅读取本地 SQLite 缓存注意文档中写作 Auto / Web API / Local而当前仓库的显示名为 Web API (IndexedDB) 与 Local (SQLite cache)两者语义一致以仓库源码为准。Auto 模式下界面右侧还会实时显示实际生效的来源标签见 WindsurfProviderImplementation.swift 中windsurf-usage-source选择器的trailingText逻辑。Auto 模式回退链的实现在 WindsurfProviderDescriptor.swift 中取数管线固定为两个策略的序列fetchPlan: ProviderFetchPlan( sourceModes: [.auto, .web, .cli], pipeline: ProviderFetchPipeline(resolveStrategies: { _ in [WindsurfWebFetchStrategy(), WindsurfLocalFetchStrategy()] })),策略级的可用性判断与回退开关可推断即为 Auto 模式先 Web 后 Local的底层机制WindsurfWebFetchStrategy.isAvailable仅当sourceMode.usesWeb且 Cookie source 不为Off时可用shouldFallback仅在sourceMode .auto时返回true——即显式 Web 模式失败时不会回退本地缓存。WindsurfLocalFetchStrategy.isAvailable仅当sourceMode ! .web时可用——即显式 Local 模式不会尝试 Web API其shouldFallback恒为false。这一点也被测试 WindsurfProviderTests.swift 覆盖其中两条用例分别验证local probe is unavailable in explicit web mode显式 Web 模式下本地探针不可用与 web mode with cookies off does not fall back to local probeWeb 模式且 Cookie 关闭时不回退本地。二、Cookie source 设置Automatic / Manual / Off设置入口Preferences → Providers → Windsurf →Cookie source三档含义Automatic默认从 Chromium 系浏览器的 localStorage 中导入当前生效的 Windsurf 会话包devin_*四键。Manual粘贴包含devin_session_token、devin_auth1_token、devin_account_id、devin_primary_org_id四个键的 JSON 会话包。Off完全禁用 Web API 访问仅使用本地 SQLite 缓存。在 UI 实现 WindsurfProviderImplementation.swift 中Cookie source 选择器通过ProviderCookieSourceUI.options(allowsOff: true, keychainDisabled: ...)生成注意支持Off并带有动态副标题AutomaticAutomatic imports Windsurf session data from Chromium browser localStorage.ManualPaste the Windsurf session JSON bundle from localStorage.OffWindsurf web API access is disabled.选择 Manual 后会显示一个安全输入框kind: .secure占位符 Windsurf session JSON bundle其值通过 WindsurfSettingsStore.swift 中的windsurfCookieHeader属性持久化该属性的 setter 会写入providerConfig(for: .windsurf)并调用logSecretUpdate——源码表明会话包按机密级别对待仅脱敏记录。如何获取手动会话包Manual在 Chrome 或 Edge 中打开windsurf.com/profile并登录会话迁移后也可在app.devin.ai上操作见下节来源优先级。打开开发者工具F12或CmdOptionI。进入Console标签页。粘贴并回车执行以下 JavaScript(() { const keys [ devin_session_token, devin_auth1_token, devin_account_id, devin_primary_org_id, ]; const read (key) { const value localStorage.getItem(key); if (!value) return null; try { return JSON.parse(value); } catch { return value; } }; const payload Object.fromEntries(keys.map((key) [key, read(key)])); const missing keys.filter((key) !payload[key]); if (missing.length 0) { console.log(Missing Windsurf session keys:, missing.join(, )); return; } const json JSON.stringify(payload, null, 2); console.log(json); if (typeof copy function) { copy(json); console.log(Copied Windsurf session bundle to clipboard.); } })();复制输出的 JSON。在 CodexBar 中进入 Providers → Windsurf → Cookie source → Manual粘贴该 JSON 会话包。Web API 的取数顺序Manual 模式下优先使用手动会话包Automatic 模式下则走浏览器导入先首选浏览器 Chrome再回退其余 Chromium 系浏览器。源码 WindsurfWebFetcher.swift 的fetchUsage还实现了两层恢复逻辑跨浏览器回退Chrome 会话请求失败且错误为可恢复的HTTP 400/401/403时见isRecoverableImportedSessionError自动改用回退浏览器集合中的会话重试。同浏览器内多会话回退fetchUsage(sessionInfos:)依次尝试每个导入会话仅当错误是 400/401/403 这类会话失效错误时才继续下一个会话其他错误直接抛出。手动输入本身也做了兼容处理parseManualSessionInput先按 JSON 解析失败则尝试宽松的keyvalue/key: value文本解析并且字段名接受驼峰别名如devinSessionToken、sessionToken、auth1Token等只要最终能凑齐四个必需键即可。三、Authentication flowAutomatic 模式文档给出的调用链Browser localStorage (leveldb on disk) ↓ extract devin_session_token / devin_auth1_token / devin_account_id / devin_primary_org_id POST https://windsurf.com/_backend/.../GetPlanStatus ↓ headers: x-auth-token x-devin-* ↓ protobuf body: { auth_token, include_top_up_status: true } UsageSnapshot (daily/weekly quota %)对应的WindsurfDevinSessionAuth结构持有四个字符串字段最终封装为UsageSnapshotprimary/secondary 两个RateWindow 身份快照。四、浏览器会话提取Browser localStorage的实现细节扫描的浏览器文档概括为 Chrome, Edge, Brave, Arc, Vivaldi, Chromium, and compatible Chromium forks。源码 WindsurfDevinSessionImporter.swift 给出了精确清单首选preferred仅 Chrome。回退fallbackChrome Beta/Canary、Edge Beta/Canary、Brave Beta/Nightly、Vivaldi、Arc Beta/Canary、Dia、ChatGPT Atlas、Chromium、Helium 等在fallbackBrowsers常量中枚举。即Chrome 有会话时不会再去碰其他浏览器只有 Chrome 无会话或 Chrome 会话全部失效时才会扫描其余浏览器。扫描路径与来源Origin规则Local storage 路径~/Library/Application Support/Browser/Profile/Local Storage/leveldb/。源码中进一步过滤 Profile 目录仅识别Default、Profile前缀与user-前缀的目录并确认其下存在Local Storage/leveldb。Originshttps://app.devin.ai与 legacyhttps://windsurf.com两个来源。来源隔离关键正确性保证localStorageSnapshots(from:)按来源分别聚合键值且只有某个来源同时具备全部 4 个目标键时才生成一个会话快照storage.count Self.targetKeys.count。文档强调 Values from different structured origins are never combined. A partial app-origin bundle cannot contaminate a complete legacy-origin fallback源码实现正是通过逐 origin 完整度校验来保证的。leveldb 文本兜底除了按 origin 前缀读取的结构化条目代码还会扫描 leveldb 中的纯文本条目当文本条目恰好凑齐全部 4 个键时会额外追加一个不带 origin 后缀的快照。必需键与去重必需键targetKeysdevin_session_tokendevin_auth1_tokendevin_account_iddevin_primary_org_id值解码decodedStorageValue会先尝试把值当作 JSON 字符串字面量解开localStorage 常以带引号的 JSON 形式存储失败则去掉首尾引号。同一sessionToken的多个候选会话通过deduplicateSessions去重保证同一账号只取一次。以上行为由 WindsurfDevinSessionImporterTests.swift 覆盖包括来源隔离与去重场景。五、GetPlanStatus APIConnectRPC over Protobuf端点与请求头POST https://windsurf.com/_backend/exa.seat_management_pb.SeatManagementService/GetPlanStatus该 URL 常量直接定义在WindsurfWebFetcher中请求头applyWindsurfHeaders 协议头Content-Type: application/protoConnect-Protocol-Version: 1Origin: https://windsurf.comReferer: https://windsurf.com/profilex-auth-token: devin_session_tokenx-devin-session-token: devin_session_tokenx-devin-auth1-token: devin_auth1_tokenx-devin-account-id: devin_account_idx-devin-primary-org-id: devin_primary_org_idProtobuf 请求体请求只有两个字段WindsurfPlanStatusProtoCodec.encodeRequest按 wire format 手工编码1 auth_token: stringlength-delimited2 include_top_up_status: boolvarintCodexBar 固定传true响应解析字段WindsurfPlanStatusProtoCodec.decodeResponse按字段号解析响应。源码中可确认的 PlanStatus 字段号字段号类型语义1messageplan_status顶层唯一解析字段1.plan_info (子消息)messageplan_info其中1 teams_tier(varint)、2 plan_name(string)2timestampplan_start1 seconds/2 nanos3timestampplan_end10messagetop_up_status1 top_up_transaction_status12varintgrace_period_status14varintdaily_quota_remaining_percent15varintweekly_quota_remaining_percent17varintdaily_quota_reset_at_unix18varintweekly_quota_reset_at_unixCodexBar 实际消费的响应字段即文档所列plan_status.plan_info.plan_name、plan_status.plan_end、日/周剩余百分比、日/周重置 Unix 时间戳。源码注释还说明这些字段号来自 Windsurf 应用捆绑的 protobuf 元数据extension.js并在 2026-04-17 与真实浏览器流量复核过——意味着该接口属于未公开协议未来存在字段变更风险解析器对未知字段采用skipFieldBody跳过以保证前向兼容。解析后的 Swift 模型为WindsurfGetPlanStatusResponse含TopUpStatus、gracePeriodStatus等可选字段非 200 响应会抛出形如Windsurf API call failed: HTTP code: body 前 200 字符的错误——这正是排查 HTTP 401 类报错时看到的完整消息来源。六、本地 SQLite 缓存WindsurfStatusProbe文件与键文件~/Library/Application Support/Windsurf/User/globalStorage/state.vscdb路径由WindsurfStatusProbe.defaultDBPath以NSHomeDirectory()拼接init(dbPath:)允许测试注入自定义路径。键ItemTable表中的windsurf.settings.cachedPlanInfo查询语句即SELECT value FROM ItemTable WHERE key windsurf.settings.cachedPlanInfo LIMIT 1;读取实现要点数据库以SQLITE_OPEN_READONLY只读方式打开并设置 250mssqlite3_busy_timeout避免与正在写库的 Windsurf 进程冲突Windsurf 基于 VSCode 内核state.vscdb即其 globalStorage 数据库。错误映射WindsurfStatusProbeErrordbNotFoundWindsurf database not found at. Ensure Windsurf is installed and has been launched at least once.sqliteFailedSQLite error reading Windsurf data: noDataNo plan data found in Windsurf database. Sign in to Windsurf first.parseFailedCould not parse Windsurf plan data: BLOB 值的双编码解码VSCode/Windsurf 的 schema 将value声明为 BLOB源码decodeJSONBlob会依次尝试 UTF-8 与 UTF-16LE 解码并且只有解码结果能被 JSON 解析接受以此规避 UTF-16 乱码代码注释明确说明了该取舍。缓存 JSON 的结构WindsurfCachedPlanInfoWindsurfCachedPlanInfo的全部字段均为可选{ planName: Pro, startTimestamp: 1771610750000, // 毫秒 endTimestamp: 1774029950000, // 毫秒换算时除以 1000 usage: { messages: 50000, usedMessages: 35650, remainingMessages: 14350, flowActions: 150000, usedFlowActions: 0, remainingFlowActions: 150000, flexCredits: null, usedFlexCredits: null, remainingFlexCredits: null }, quotaUsage: { dailyRemainingPercent: 9, weeklyRemainingPercent: 54, dailyResetAtUnix: 1774080000, // 秒 weeklyResetAtUnix: 1774166400 // 秒 } }以上结构可对照测试 WindsurfStatusProbeTests.swift 中的decodes full plan info与decodes minimal plan info{planName: Free}这类极简缓存也能解码。限制该缓存只在你启动 Windsurf 时更新可能显著滞后——这正是文档建议在需要实时数据时切到 Auto 或 Web API 的原因。另外本地探测与浏览器会话提取整段实现都包在#if os(macOS)中非 macOS 平台的WindsurfStatusProbe.fetch()直接抛出 Windsurf is only supported on macOS.即Windsurf Provider 目前是 macOS 专属能力。七、Snapshot mapping从原始数据到界面用量Web 与 Local 两个来源最终都产出UsageSnapshotprimary/secondary 双窗口 身份快照映射规则与文档一致目标Web APIWindsurfGetPlanStatusResponse.toUsageSnapshot本地缓存WindsurfCachedPlanInfo.toUsageSnapshotPrimaryDaily100 - daily_quota_remaining_percent配daily_quota_reset_at_unix作为重置时间存在quotaUsage.dailyRemainingPercent时用百分比否则由usedMessages/messages推导描述为used / total messagesSecondaryWeekly100 - weekly_quota_remaining_percent配周重置时间存在周百分比则用否则由usedFlowActions/flowActions推导描述为used / total flow actionsReset日/周重置时间戳Unix 秒同左quotaUsage内的 Unix 秒Planplan_status.plan_info.plan_name作为身份快照的 loginMethodplanName同位置Expiryplan_status.plan_end→ Expires endTimestamp毫秒 → 秒→ Expires 源码层面的几个细节值得注意百分比钳制max(0, min(100, 100 - remaining))剩余百分比为异常值时不会把界面撑爆。used 的推断usedMessages缺失时用total - remainingMessages推断并钳制到[0, total]。重置描述formatResetDescription统一输出Resets in 2d 3h/Resets in 5h 12m/Resets in 40m过期则显示Expired——两个来源各自实现了同一段格式化逻辑。flexCredits 不参与映射缓存 JSON 虽有 flex credits 三键但toUsageSnapshot()只消费 messages 与 flowActions 两组计数器。Provider 元数据WindsurfProviderDescriptor.metadata还定义了菜单展示会话标签 Daily、周标签 Weekly、defaultEnabled: false默认不开启、supportsTokenCost: false不支持 token 成本统计UI 提示 Windsurf cost summary is not supported.、仪表盘 URL 指向windsurf.com/subscription/usage并提供了 free/pro/team/enterprise/ultimate 等计划名到本地化标签的映射。八、Troubleshooting按报错信息定位以下四条排查路径与文档一一对应并标注了每条错误消息在源码中的真实位置便于在日志中精确匹配。No Windsurf web session found in Chromium localStorage报错来源WindsurfWebFetcherError.noSessionDataSign in to app.devin.ai or windsurf.com in Chrome first.。处理在 Chrome、Edge 或其他 Chromium 浏览器中登录app.devin.ai或windsurf.com为 CodexBar 授予 Full Disk AccessSystem Settings → Privacy Security → Full Disk Access否则无法读取浏览器 leveldb改用 Manual 模式直接粘贴 JSON 会话包见第二节。Invalid Windsurf session payload报错来源WindsurfWebFetcherError.invalidManualSession即手动输入为空或缺少四个必需键之一devin_session_token、devin_auth1_token、devin_account_id、devin_primary_org_id。处理在已登录的windsurf.com页面上重新执行控制台的 JS 片段重新粘贴完整会话包。Windsurf API call failed: HTTP 401报错来源WindsurfWebFetcherError.apiCallFailedHTTP 非 200 时抛出消息包含状态码与响应体前 200 字符。含义导入的浏览器会话已过期或失效。处理在浏览器中刷新 Windsurf 页面重新登录再触发刷新Manual 模式则粘贴一份新的会话包。另注意400/401/403 在导入会话场景下会被视为可恢复错误CodexBar 会先静默尝试下一个候选会话只有全部失败才会把最后一条错误抛给用户。Local 模式数据陈旧本地 SQLite 缓存只在 Windsurf 启动/更新时写入切到 Auto 或 Web API 模式获取实时数据或启动一次 Windsurf 再刷新。九、关键文件与验证测试核心实现相对仓库根目录WindsurfStatusProbe.swift本地 SQLite 探针、缓存 JSON 模型与快照映射WindsurfDevinSessionImporter.swiftChromium localStorage 会话提取、来源隔离与去重WindsurfWebFetcher.swift会话解析、GetPlanStatus请求构造、手工 Protobuf 编解码器WindsurfProviderDescriptor.swiftWeb/Local 两条取数策略与回退规则、Provider 元数据WindsurfUsageDataSource.swift / WindsurfProviderSettings.swift数据源枚举与设置结构WindsurfProviderImplementation.swift设置界面选择器Usage source / Cookie source / 安全输入框WindsurfSettingsStore.swift设置持久化与快照测试WindsurfProviderTests.swift显式 Web/Local 模式下的策略可用性断言WindsurfStatusProbeTests.swift缓存 JSON 解码完整/极简两种形态WindsurfWebFetcherTests.swift手动会话解析、会话回退与 protobuf 编解码WindsurfDevinSessionImporterTests.swift来源隔离、键值完整度与去重小结CodexBar 对 Windsurf 的支持本质上是一套凭证零配置 未公开协议解析的取数管线——Automatic 模式下从 Chrome必要时扩展到其他 Chromium 系浏览器的 leveldb 中取出完整四键会话按 ConnectRPC/protobuf 调用GetPlanStatus拿实时配额任何一环失败时Auto 模式无缝降级到 Windsurf 桌面端的state.vscdb缓存而当浏览器凭证整体不可用时Manual 模式提供了一个基于 DevTools 控制台的标准化会话导出脚本作为兜底。理解上述回退链与字段映射后绝大多数 Windsurf 用量显示问题都可以按第八节的报错信息快速定位。【免费下载链接】CodexBarShow usage stats for OpenAI Codex and Claude Code, without having to login.项目地址: https://gitcode.com/GitHub_Trending/co/CodexBar创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考