CodexBar 接入 StepFun(阶跃星辰)用量统计:Oasis-Token 登录流、双计费模型解析与配置实战
【免费下载链接】CodexBarShow usage stats for OpenAI Codex and Claude Code, without having to login.项目地址: https://gitcode.com/GitHub_Trending/co/CodexBar
导读
本文以 docs/stepfun.md 为主线,系统讲解 CodexBar 如何为 OpenAI Codex / Claude Code 生态之外的 Web 型服务商 StepFun(阶跃星辰)提供用量统计:从三种认证方式(自动登录、手动 Token、环境变量)到 Oasis-Token 的三步获取流程,再到 Step Plan 限流 API 与套餐状态 API 的字段解析,最后深入两套并行计费模型(滚动 5 小时/周窗口与月度 Credit 池)在菜单栏卡片上的呈现逻辑。读完本文,你将掌握 StepFun 在 CodexBar 中的完整配置方法、API 响应结构与核心解析算法,并能读懂对应源码与测试。
一、StepFun Provider 概览
StepFun 是一个 Web 型用量服务商,与 OpenAI、Claude 等直接使用账号体系读取仪表盘的方式不同,CodexBar 通过以下两条数据源获取其用量:
- 认证层:以用户名 + 密码(或直接粘贴 Oasis-Token)换取并缓存会话令牌,令牌存放在 Keychain 背书的
CookieHeaderCache中; - 数据层:调用 Step Plan 限流 API 读取剩余额度百分比,调用套餐状态 API 读取套餐名称。
该 Provider 的标识为.stepfun,CLI 名称为stepfun(别名step-fun、sf),仪表盘地址为https://platform.stepfun.com/plan-usage,这些元数据定义在 Sources/CodexBarCore/Providers/StepFun/StepFunProviderDescriptor.swift 的ProviderMetadata中。
二、三种认证方式与优先级
CodexBar 按固定优先级解析 StepFun 的认证凭据,该逻辑实现在 StepFunProviderDescriptor.swift 的resolveToken(context:allowCached:)中:
| 优先级 | 方式 | 来源 | 说明 |
|---|---|---|---|
| 1 | Manual 模式 | 设置 → Providers → StepFun 中粘贴 Oasis-Token | 直接从设置读取并归一化,不发起网络登录 |
| 2 | 缓存 Token | Keychain 中的CookieHeaderCache | 复用上次登录/刷新得到的令牌 |
| 3 | 设置内账号密码 | 设置界面填写的用户名 + 密码 | 触发完整三步登录流并回写缓存 |
| 4 | 环境变量 Token | STEPFUN_TOKEN | 直接使用环境变量中的令牌 |
| 5 | 环境变量账号密码 | STEPFUN_USERNAME+STEPFUN_PASSWORD | 触发完整登录流并回写缓存 |
其中第 2、3、5 种方式成功后都会把令牌写入CookieHeaderCache(sourceLabel 分别为login),从而让后续刷新直接命中第 2 级缓存。环境变量的读取与清洗(去空白、剥掉首尾成对引号)实现在 Sources/CodexBarCore/Providers/StepFun/StepFunSettingsReader.swift,对应测试见 StepFunUsageFetcherTests.swift 中的StepFunSettingsReaderTests。
三、Oasis-Token 三步登录流
当使用用户名 + 密码登录时,CodexBar 会依次调用三个接口换取令牌,整体流程封装在 StepFunUsageFetcher.swift 的fullLogin(username:password:)中:
- 获取 INGRESSCOOKIE:
GET https://platform.stepfun.com,从响应的Set-Cookie头中提取INGRESSCOOKIE值(源码中同时从HTTPCookieStorage兜底读取)。 - 注册匿名设备:
POST …/passport/proto.api.passport.v1.PassportService/RegisterDevice,请求体为空 JSON{},携带Cookie: INGRESSCOOKIE=…,返回匿名 access/refresh token 对。 - 密码登录:
POST …/passport/proto.api.passport.v1.PassportService/SignInByPassword,请求体为{"username": …, "password": …},携带Cookie: Oasis-Token=<匿名token>; Oasis-Webid=…; INGRESSCOOKIE=…,返回已认证的 Oasis-Token 对。
源码将 access token 与 refresh token 拼接为"<access>...<refresh>"的复合格式(combinedToken),并在后续所有请求中作为Oasis-Token头与Cookie值使用。
除登录外,StepFunUsageFetcher.swift 还实现了refreshOasisToken(token:):通过POST …/PassportService/RefreshToken刷新过期令牌,成功后返回新的 access/refresh 复合令牌。所有网络请求都经由共享的ProviderHTTPClient,超时时间为 15 秒,基础请求头包括content-type: application/json、oasis-appid: 10300、oasis-platform: web以及浏览器 UA。
四、Oasis-Webid 与 device_id 绑定约束
StepFun 服务端要求Oasis-Webid头/Cookie 必须与令牌 JWT 载荷中的device_id声明一致,否则会返回auth failed: oasis-token is embezzled类错误。为此,StepFunUsageFetcher.swift 实现了webID(forToken:):
- 复合令牌按
...拆成两半,优先取 refresh 半段(device_id声明通常位于 refresh token),失败则回退 access 半段; - 对 JWT 的第二段(payload)做 base64url 解码,从中提取
device_id字段; - 在登录/注册阶段(尚无可推导 token)使用内置兜底
defaultWebID,拿到令牌后再用真实device_id覆盖请求中的oasis-webid头。
这也解释了文档中「Manual 模式浏览器导入的 Token,其 device_id 从 refresh-token JWT 载荷推导」的由来:自动登录的 device_id 属于 CodexBar 应用自身,手动粘贴的令牌则属于导入的浏览器会话。
五、限流 API 与响应字段
用量数据来自限流接口POST https://platform.stepfun.com/api/step.openapi.devcenter.Dashboard/QueryStepPlanRateLimit,请求头为Cookie: Oasis-Token=<token>、Content-Type: application/json,请求体为空 JSON。响应结构在 StepFunUsageFetcher.swift 中定义,核心字段如下:
| 字段 | 类型 | 含义 |
|---|---|---|
status | int | 请求状态,1表示成功(isSuccess) |
five_hour_usage_left_rate | number | 5 小时窗口剩余比例(如0.99781543) |
weekly_usage_left_rate | number | 周窗口剩余比例 |
five_hour_usage_reset_time | string/int | 5 小时窗口重置时间戳 |
weekly_usage_reset_time | string/int | 周窗口重置时间戳 |
plan_family | number | 套餐族标识;2表示 Credit 计费套餐(如 Mini、Pro) |
plan_credit_rate_limit | object | Credit 用量对象(plan_family: 2时出现) |
plan_credit_rate_limit内部包含:
subscription_credit_left_rate:订阅额度剩余比例(如0.9641);subscription_credit_reset_time:额度重置/回填时间戳;topup_credit_left_rate:充值额度剩余比例;credit_buckets:数组,元素为{ credit_total, credit_residual, expire_at, next_reset_at }的额度桶。
为兼容该 API 的松散类型,源码定义了StepFunFlexibleNumber(可同时解码 int/float/数字字符串)与StepFunFlexibleTimestamp(可同时解码字符串与整数时间戳)两个弹性类型,实测响应中既有five_hour_usage_left_rate: 1这样的整数,也有"1777528800"这样的字符串时间戳,对应解析测试见 StepFunUsageFetcherTests.swift 中的StepFunUsageFetcherParsingTests。
六、套餐状态 API 与套餐名
套餐名称来自POST https://platform.stepfun.com/api/step.openapi.devcenter.Dashboard/GetStepPlanStatus,同样携带 Oasis-Token 认证头,响应中的subscription.name即套餐名(如 "Plus"、"Mini")。在 StepFunUsageFetcher.swift 中该请求被设计为可选增强:请求失败或解析失败仅记录 debug 日志并返回nil,用量数据仍照常展示(只是缺少套餐名标签)。套餐名经过去空白处理后,最终以loginMethod标签的形式显示在菜单栏卡片上。
七、两套计费模型与解析判定
StepFun 在 2026-06-18 升级后并行运行两套 Step Plan 计费模型(源码注释明确引用了 docs/zh/step-plan/upgrade-notice):
- 滚动窗口模型(Coding Plan 老套餐):计量滚动的 5 小时 / 周窗口;
- 月度 Credit 池模型(Token Plan 新套餐):通过
plan_credit_rate_limit计量月度额度池,其窗口字段返回0、reset_time返回"0"——这是「未配置窗口」而非「额度用尽」。
因此isCreditPlan的判定不单纯信任plan_family,而是按载荷形状分级判断(StepFunUsageFetcher.swift):
- 若存在活跃窗口(任一
*_usage_reset_time > 0)→ 判定为窗口计费(非 Credit); - 否则若存在 Credit 池(
subscription_credit_left_rate、topup_credit_left_rate、非空credit_buckets任一存在)→ 判定为 Credit 计费; - 以上都不满足(如全新套餐既无窗口也无额度)时,才以
plan_family == 2作为最终兜底。
这样设计是为了避免未来 family-id 变化时,把窗口套餐错误地渲染成 Credit 卡片或反之。
7.1 窗口套餐的展示逻辑
toUsageSnapshot()将解析结果映射为 CodexBar 的UsageSnapshot:
- 主窗口(顶部条):5 小时限流,
windowMinutes: 300; - 次窗口(底部条):周限流,
windowMinutes: 10080; usedPercent = (1.0 - left_rate) × 100,并夹取到[0, 100]区间。
7.2 Credit 套餐的展示逻辑
Credit 套餐没有 5 小时/周窗口,其展示规则如下:
- 主窗口展示合并后的 Credit 余额,
usedPercent = (1.0 - credit_left_rate) × 100; - 次窗口不显示(
secondary: nil); - 合并比例的算法在
totalCreditLeftRate中实现:当credit_buckets存在且所有桶都能提供合法的credit_total/credit_residual(均为有限数、total > 0、0 ≤ residual ≤ total)时,按sum(residual) / sum(total)加权合并;缺少桶数据时,不能简单相加两个独立比例,而是取subscription_credit_left_rate,仅在订阅额度不存在时才回退topup_credit_left_rate; - 当 Credit 池存在真实月度重置时间时,主窗口的
windowMinutes被设置为月度窗口哨兵值,从而让月度额度池也能接入套餐利用历史与节奏预测(pace forecast);若没有重置时间则置nil,避免产生无意义的预测。
对应测试覆盖了 bucket 加权、无 bucket 回退、空 bucket 数组等多种形态(见 StepFunUsageFetcherTests.swift 中 Credit 相关用例)。
八、设置项与配置实操
StepFun 的设置字段在 Sources/CodexBar/Providers/StepFun/StepFunProviderImplementation.swift 中声明,底层存储映射在 Sources/CodexBar/Providers/StepFun/StepFunSettingsStore.swift:
| 设置项 | 显示 | 存储位置 |
|---|---|---|
| Auth source | Auto / Manual / Off | cookieSource配置字段 |
| Username | 平台账号(手机号或邮箱),Auto 模式显示 | 复用apiKey字段 |
| Password | 安全输入,Auto 模式显示 | 复用cookieHeader字段(安全存储) |
| Oasis-Token | Manual 模式粘贴,含「Open StepFun Platform」跳转按钮 | 复用region字段(原字段重用途) |
UI 的可见性规则:cookieSource != .manual时显示账号密码字段,cookieSource == .manual时显示 Token 字段。Auth source 设为Off时,StepFunWebFetchStrategy.isAvailable返回false,Provider 完全不发起后台刷新(StepFunProviderDescriptor.swift)。当选择 Manual 模式时,设置界面还会引导用户打开platform.stepfun.com/plan-usage页面复制浏览器会话中的 Oasis-Token。
8.1 手动 Token 的归一化
粘贴的令牌可能是裸 JWT,也可能是一段Oasis-Token=…; …形式的 Cookie 头。StepFunTokenNormalizer.normalize(StepFunProviderDescriptor.swift)会识别并剥离Oasis-Token=前缀,仅保留;之前的令牌本体。手动持久化的 Token 写入配置文件的region字段(persistManualToken)。
8.2 环境变量配置(CLI / 无 UI 场景)
# 方式一:用户名 + 密码(自动完成三步登录流) export STEPFUN_USERNAME="user@example.com" export STEPFUN_PASSWORD="your-password" # 方式二:直接使用已有 Oasis-Token(跳过登录) export STEPFUN_TOKEN="<oasis-token 或 Oasis-Token=… cookie 头>"环境变量支持首尾空格去除、成对双引号/单引号剥离,空值视为未配置(详见StepFunSettingsReader与对应单元测试)。
九、Token 过期、失败恢复与错误处理
当限流 API 返回认证类错误(401/403、unauthorized、unauthenticated、invalid credentials、invalid token、token expired 等,判定函数见 StepFunProviderDescriptor.swift)时,抓取策略会进入recoverFromAuthenticationFailure恢复流程:
- 优先尝试用缓存令牌调用
RefreshToken接口刷新; - 若刷新失败且令牌来自缓存,则清除陈旧缓存(
CookieHeaderCache.clear)后按优先级重新解析(回到环境变量或设置内账号密码); - 若仍不可行,则尝试用设置内或环境变量中的账号密码重新执行完整登录流;
- 全部失败时抛出带可操作提示的错误:Manual 模式提示刷新 Oasis-Token 或改用账号密码自动登录,环境变量 Token 模式提示刷新
STEPFUN_TOKEN或配置账号密码。
恢复成功后,新令牌会按来源写回:缓存类来源存入CookieHeaderCache,Manual / 环境变量 Token 来源则通过 token account 更新器回写。错误类型统一收敛到StepFunUsageError枚举(missingCredentials、missingToken、networkError、apiError、parseFailed、loginFailed、tokenRefreshFailed、deviceRegistrationFailed),便于上层展示与诊断。
十、关键文件与测试索引
- Sources/CodexBarCore/Providers/StepFun/StepFunProviderDescriptor.swift:Provider 描述符、凭据适配、Web 抓取策略与令牌解析/恢复逻辑;
- Sources/CodexBarCore/Providers/StepFun/StepFunUsageFetcher.swift:登录流、HTTP 客户端、JSON 解析与快照映射;
- Sources/CodexBarCore/Providers/StepFun/StepFunSettingsReader.swift:环境变量解析;
- Sources/CodexBarCore/Providers/StepFun/StepFunProviderSettings.swift:设置快照结构;
- Sources/CodexBar/Providers/StepFun/StepFunProviderImplementation.swift:设置字段与激活逻辑;
- Sources/CodexBar/Providers/StepFun/StepFunSettingsStore.swift:SettingsStore 扩展与存储映射;
- Tests/CodexBarTests/StepFunUsageFetcherTests.swift:覆盖环境变量读取、Token 解析、API 响应解析、Credit/窗口套餐判定、错误路径等 26 个测试用例。
从测试集可以看出,该项目对 StepFun 的处理不仅验证「理想响应」,还专门覆盖了字符串时间戳、整数比例、"0"重置时间、空 bucket、缺字段回退等边界形态,确保两套计费模型都能稳定、准确地呈现在菜单栏卡片中。
【免费下载链接】CodexBarShow usage stats for OpenAI Codex and Claude Code, without having to login.项目地址: https://gitcode.com/GitHub_Trending/co/CodexBar
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考