☰
Kun 终端配额面板深度解析:ProviderQuotaService 与 TUI `/quota` 路由的实现与使用指南
2026/10/12 3:28:59 网站建设 项目流程
  • 人工智能
  • AI Agent
  • 自主智能体
  • 桌面应用
  • MCP Clients

【免费下载链接】Kun

Local-first AI agent workspace for coding, writing, design, research, and automation — one runtime for desktop GUI and TUI.

项目地址:https://gitcode.com/gh_mirrors/de/Kun
点击查看免费下载

导读

本文以 Kun 仓库中 Provider quota TUI 变更规格 为主体,结合 设计文档、提案 与源码实现,完整讲解 Kun 如何在独立 TUI(Terminal UI)中展示各模型提供商账户的配额快照(余额、用量窗口、重置时间)。读完本文你将掌握:配额快照如何由共享 Kun runtime 通过GET /v1/provider-quotas只读接口下发、凭据如何被严格限制在服务端、TUI 中/quota与/provider usage命令的路由与键盘操作,以及 wide / compact / narrow 三种终端宽度下的自适应渲染机制。

适用前提:本文描述的行为以当前仓库代码为准。Kun 的 TUI 既可以在桌面应用之外独立运行(standalone),也可以由桌面启动,两者都通过共享的kun serve进程访问配额接口,因此本功能不依赖 Electron IPC。


一、背景:为什么 TUI 需要独立的配额接口

Kun 的桌面 GUI 此前已经具备"提供商配额面板",但它运行在 Electron 主进程中——因为受保护的凭据(API Key、OAuth token、cookie)绝不能进入渲染进程。而独立 TUI 是kun serve进程的另一个客户端,无法调用 Electron IPC,因此需要一个由 runtime 自己拥有的只读配额表面(quota surface)。

与此同时,Kun 已有的GET /v1/usage端点报告的是线程累计 token 消耗与费用,这是"会话级"概念;而提供商账户额度(provider account allowance)是另一个独立概念。设计文档明确写道:

The existingGET /v1/usageendpoint reports accumulated thread tokens and cost. Provider account allowance is a separate concept and must have a separate contract and/quotaTUI route.

并且仓库架构明确禁止恢复一个基于/usage的 runtime 控制斜杠面板。因此:

  • /v1/usage继续报告线程 token 用量;
  • /context继续保留为"线程 token 用量"页面;
  • 新增GET /v1/provider-quotas+/quota路由专门呈现提供商账户额度。

相关实现证据:TUI 的 usage 报告页在页脚明确提示"Provider allowance is shown in /quota"(见 usage-report.ts)。


二、运行时契约:严格 Zod Schema 与四种状态

2.1 契约文件与结构

配额快照的 DTO 全部定义在 contracts/provider-quota.ts,使用 Zod 的.strict()模式,杜绝多余字段混入。

核心类型ProviderQuotaEntrySchema的字段如下:

字段类型说明
providerIdstring (1–128)提供商稳定标识
providerNamestring (1–120)展示名称
presetIdstring,可选预置身份(如claude-subscription)
statusenumavailable/unsupported/missing_credentials/error
sourcestring,可选数据来源描述(如 "DeepSeek balance API")
dashboardUrlurl,可选(≤2048)跳转到官方控制台的 URL
summarystring,可选计划名/摘要(如 Z.ai 的 planName)
metrics数组(≤500)归一化指标
localCost对象,可选本地 API 参考价估算
updatedAtdatetime,可选本条刷新时间
messagestring,可选(≤4096)经清洗的错误/引导信息

单个指标ProviderQuotaMetricSchema包含id、label、unit、used、limit、remaining、usedPercent(0–100)、resetsAt(ISO datetime)。列表响应ProviderQuotaListResponseSchema为{ entries: Entry[](≤500), refreshedAt: datetime }。

2.2 四种状态语义

规格(spec.md)要求每个配置的模型连接都产生一条相互隔离的条目:混合结果场景下,某几个提供商探测失败,不影响其他成功条目展示。ProviderQuotaService.refreshProfile的实现与之对应(provider-quota-service-core.ts):

  • available:探测成功,返回归一化指标与摘要;
  • unsupported:无法识别该提供商/主机名,消息为 "This provider does not expose a supported quota API in this version.";
  • missing_credentials:未配置 API Key 或官方订阅未登录,消息为 "Connect a provider credential before refreshing quota.";
  • error:请求被拒绝、超时、响应过大或解析失败,消息经过quotaErrorMessage截断清洗(≤4096 字符)。

三、认证 HTTP 路由:GET /v1/provider-quotas

3.1 路由挂载与鉴权

路由注册在 register-thread-routes.ts,处理函数位于 provider-quotas.ts。该路由走 Kun 常规的 bearer token 鉴权管道;规格明确要求"未授权请求在不启动任何提供商探测的情况下被拒绝"。

3.2?refresh=1手动刷新语义

路由处理函数支持一个查询参数:

// `?refresh=1` is the manual GUI refresh path: it bypasses the shared TTL // cache; plain polls reuse cached entries. const forceRefresh = new URL(request.url).searchParams.get('refresh') === '1' return jsonResponse(await service.list(forceRefresh ? { forceRefresh: true } : undefined))
  • 普通请求:命中 5 分钟 TTL 缓存,适合轮询;
  • ?refresh=1:绕过缓存强制探测,是手动刷新路径(TUI 的r键与 GUI 刷新共用此语义)。

3.3 客户端方法

TUI 客户端在 client-thread-api.ts 中新增了带响应校验的调用:

providerQuotas() { return this.request('/v1/provider-quotas', ProviderQuotaListResponseSchema) }

返回体直接通过ProviderQuotaListResponseSchema校验,保证类型安全。


四、探测机制:canonical 身份 + 精确主机名

规格要求:配额探测必须使用稳定的 provider/preset 身份、期望的传输类型(transport kind)以及精确识别的 API 主机名,并且只调用固定的只读端点;绝不允许从自定义 Base URL 推导配额 URL。

4.1 分类函数classifyProviderQuotaProbe

核心分类逻辑在 provider-quota-service-core.ts:

  • 订阅类提供商按stableId+kind匹配:claude-subscription(agent-sdk)、codex(http)、grok-subscription(http)、cursor-subscription(cursor-sdk)、antigravity-cli、gemini-cli-api;
  • API Key 类提供商按exactHostname(baseUrl)精确匹配(exactHostname 用new URL(...).hostname.toLowerCase()解析);
  • 无法识别的主机名返回null,即unsupported,不会拼接任何配额 URL。

4.2 API Key 类提供商探测端点

探测执行在 provider-quota-service-probe.ts 的runProbe/requestJson中,全部为固定只读端点:

提供商主机名判定固定端点解析器
DeepSeekapi.deepseek.comhttps://api.deepseek.com/user/balanceparseDeepSeekQuota
OpenRouteropenrouter.aihttps://openrouter.ai/api/v1/credits+/api/v1/keyparseOpenRouterQuota
Moonshot 全球api.moonshot.aihttps://api.moonshot.ai/v1/users/me/balanceparseMoonshotQuota
Moonshot 中国api.moonshot.cnhttps://api.moonshot.cn/v1/users/me/balanceparseMoonshotQuota
Z.aiapi.z.aihttps://api.z.ai/api/monitor/usage/quota/limitparseZaiQuota
BigModelopen.bigmodel.cnhttps://open.bigmodel.cn/api/monitor/usage/quota/limitparseZaiQuota
MiniMax 全球api.minimax.io/v1/token_plan/remains、/v1/api/openplatform/coding_plan/remains(双端点多退避)parseMiniMaxQuota
MiniMax 中国api.minimaxi.com同上parseMiniMaxQuota
OpenAI(exact)api.openai.comhttps://api.openai.com/v1/dashboard/billing/credit_grantsparseOpenAiQuota
Kimi Codeapi.kimi.com(且 stableId 为kimi-code)https://api.kimi.com/coding/v1/usagesparseKimiCodeQuota
OpenCode Go(本地估算)opencode.ai(且 stableId 为opencode-go)本地命令/Web 配额读取opencode-go-local-quota.ts/opencode-go-web-quota.ts

其中probeMiniMax实现了"主机 × 路径"的多重退避(provider-quota-service-probe.ts):先尝试/v1/token_plan/remains,失败再尝试/v1/api/openplatform/coding_plan/remains。

4.3 订阅类提供商探测

订阅类探测走 provider-subscription-quota-service.ts,覆盖规格中列举的 Claude、ChatGPT/Codex、Grok、Cursor、Antigravity、Gemini CLI 六类官方订阅,外加 OpenCode Go:

  • Claude:解析 Claude Code OAuth token,调用https://api.anthropic.com/api/oauth/usage;
  • Codex:解析 Codex CLI 存储的 OAuth 凭据,走 gRPC-web 传输;遇到 401/403 会内存内刷新 token 后重试一次;
  • Grok:解析 Grok 登录态,走 gRPC-web billing 接口;
  • Cursor:读取 Cursor.app 会话 cookie,调用 usage summary API;
  • Antigravity / Gemini CLI:复用 Google CLI OAuth 登录态,只读调用配额接口。

这些来源全部只读:官方客户端凭据(Claude Code、Codex CLI、Cursor.app、Antigravity、Gemini CLI)不会被配额服务复制或写回(design.md 明确:"Source credentials are never copied or written back by the quota service.")。token 刷新仅发生在内存中,满足官方 OAuth 契约需要。

4.4 解析器与归一化

各提供商原始响应由 provider-quota-service-provider-parsers.ts 中的parse*Quota系列解析为统一指标,例如:

  • parseDeepSeekQuota提取total_balance(总余额)、topped_up_balance(充值余额)、granted_balance(赠送余额);
  • parseOpenRouterQuota计算 Credits(total_usage/total_credits),若 key 接口可用则追加 API key 预算;
  • parseZaiQuota依据TOKENS_LIMIT/CREDIT_LIMIT/TIME_LIMIT类型生成 token / credits / requests 指标,并用quotaWindowLabel把number+unit转成 "1-day"、"5-hour" 等窗口标签;
  • parseMiniMaxQuota从model_remains生成每模型的 interval/weekly 窗口指标,并对已耗尽的 100% 状态做去噪处理;
  • parseKimiCodeQuota生成 weekly 请求配额与 5-hour rate limit 指标;
  • parseOpenAiQuota提取 credit grants,并取最早未过期 grant 的到期时间作为resetsAt。

ProviderQuotaService.list()最终用ProviderQuotaListResponseSchema.parse整体校验后返回,任何不合规字段都会被拒绝。


五、安全边界:凭据永不离开进程

规格第三条要求:API Key、OAuth token、cookie、官方客户端凭据、原始上游响应体全部留在 Kun 进程内。落地在四个层面:

  1. DTO 白名单:Zod.strict()schema 只允许归一化展示字段,requestJson返回的原始 JSON 从不进入响应体;
  2. 错误信息清洗:quotaErrorMessage只保留固定文案(如 "The quota request timed out."、"The provider did not authorize quota access for this credential."),原始响应体或凭据值绝不外泄;订阅探测中凭据解析失败统一抛出ProviderQuotaMissingCredentialError引导用户登录;
  3. TUI 端二次清洗:渲染前所有不可信字符串经过 secret-redaction.ts 的redactSecretText与 TUI 布局层sanitizeTerminalText(终端控制字符清洗),见 provider-quota.ts;
  4. 传输层代理感知:探测通过createProxyFetch(proxyUrl) ?? fetch构造的proxyAwareFetch发起,沿用 Kun 的代理配置。

5.1 边界约束常量

设计文档的风险条目在代码中固化为四个常量(provider-quota-service-core.ts):

export const QUOTA_TIMEOUT_MS = 12_000 // 单请求超时 12 秒 export const MAX_RESPONSE_BYTES = 256 * 1024 // 响应体上限 256 KiB export const QUOTA_CONCURRENCY = 4 // 最多 4 个探测并发 export const QUOTA_CACHE_TTL_MS = 5 * 60_000 // 每提供商缓存 5 分钟

readBoundedResponseText会同时检查content-length头声明与实际流式读取字节数,超限即报 "The provider quota response was too large.",防止大响应拖垮 TUI 路由。

5.2 缓存、并发与失败保留

cachedRefreshProfile(provider-quota-service-core.ts)实现了三重保护:

  • TTL 缓存:5 分钟内重复请求直接返回缓存快照;
  • inflight 去重:同一提供商的并发调用共享同一个在途 Promise;
  • 失败保留旧快照:刷新返回error时保留上一次成功快照,路由/UI 不会被瞬时探测错误卡住。

mapWithConcurrency以固定 4 个 worker 并行处理全部 profile(provider-quota-service-probe.ts)。


六、TUI 路由与命令:/quota、/provider usage

6.1 命令解析

命令解析在 commands.ts:

case 'provider': return rest === 'usage' || rest === 'quota' ? { kind: 'quota' } : { kind: 'connect' } case 'usage': return { kind: 'usage-report' } case 'quota': return { kind: 'quota' }

即:

用户输入结果
/quota打开 Provider quota 路由
/provider usage打开 Provider quota(兼容快捷方式)
/provider quota同样打开 Provider quota
裸/provider仍打开模型连接编辑路由(connect),行为不变
/usage仍是线程 token 用量报告,不被覆盖
/context仍是线程 token 用量页面

命令测试在 commands.test.ts 中固化了上述断言。命令面板(command palette)注册了 "Show provider quota" 条目(slash: 'quota'),并提供/pro自动补全提示。

6.2 路由生命周期

showQuota()在 application-routes.ts 中实现:先关闭连接路由、模型路由、usage 路由与子代理路由,再创建ProviderQuotaDialog并作为独占 primary route显示,随后立即触发component.refresh()加载快照。关闭时closeQuotaRoute隐藏 primary route 并把焦点交还根组件。

/context页面(线程 token 用量)与/quota页面(提供商额度)互不干扰,这正是规格中"preserving/contextfor thread token usage"的落地。


七、TUI 渲染:ProviderQuotaDialog详解

7.1 页面骨架

渲染逻辑位于 tui/provider-quota.ts 的ProviderQuotaDialog,完全遵循 Kun 的终端视觉系统(pageFrame、sectionLabel、statusGlyph、visualDensity):

  • 面包屑:KUN / Provider quota;
  • 右上角:refreshed HH:MM:SS刷新时钟,加载中显示refreshing;可滚动时追加起始-结束/总数范围指示;
  • 描述行:Account balances and rate limits from configured providers.;
  • 页脚:滚动可用时显示↑/↓ PgUp/PgDn navigate、r refresh、Esc back。

7.2 各状态渲染

  • 加载中:statusGlyph('running')动画 + "Loading provider quota…";
  • 刷新失败但已有快照:黄色警示行 "Refresh failed" + 清洗后的错误文案,保留旧结果,路由保持可用(对应规格 Scenario: Refresh fails);
  • 空结果:No model providers are configured.;
  • 每个提供商:分隔线(sectionLabel)后依次是——
    • 标题行:语义 glyph + 加粗提供商名,右侧右对齐显示状态标签(available/计划名、sign in required、unsupported、error),颜色分别为绿/黄/暗/红;
    • available:逐条渲染指标行;
    • 其他状态:缩进换行展示清洗后的引导信息(如 "Connect this provider before refreshing quota.")。

7.3 指标行与进度条

metricLines依据visualDensity(width)分三档渲染:

  • wide:label(≤28 字符截断)+ 20 格进度条 + 百分比 + 数值 + 重置时间排在同一行;
  • compact:缩短进度条,把数值/重置时间移到缩进第二行;
  • narrow:第一行只保留label+ 百分比(或数值),进度条与数值/重置时间逐行缩进,绝不横向溢出(对应规格 Scenario: Narrow terminal)。

进度条为终端原生方块:[■■■···](已用部分visual.focus青色,剩余visual.muted暗色)。数值格式化formatAmount对小数保留 4 位、整数按千分位;百分比整数直接显示、非整数保留 1 位。重置时间由resetLabel实时换算为 "resets in 5m / 2h 30m / 3d",已到点显示 "reset due"。

7.4 本地 API 参考成本(localCost)

ProviderQuotaEntry.localCost承载reference_api_estimate(USD)估算:today与last30Days两个窗口,各自包含requests、totalTokens、amount与coverage(complete/partial/unavailable)。TUI 中渲染为 "Local API reference value" 区块,并明确标注"API reference estimate, not an actual subscription charge."——这只是一个估算值,绝不冒充真实扣费。路由刷新(includeLocalCosts: false)可跳过该聚合以降低开销。


八、键盘交互与导航

handleInput(tui/provider-quota.ts)支持:

按键行为
r/Ctrl+R手动刷新(加载中忽略重复触发)
Esc/Ctrl+C关闭路由返回
↑/k上滚一行
↓/j下滚一行
PageUp/PageDown上/下翻一页
Home/End跳到首/末行

render中pageSize = terminalRows - 7,maxOffset与offset被钳制在合法范围,超出终端行数时页脚显示当前可视范围1-20/45这类指示(对应规格 Scenario: Long provider list)。组件完全自持垂直偏移量,与鼠标滚轮无关,符合设计文档"mouse-wheel-independent terminal scroll controls are bounded"的约定。


九、测试与验证体系

本变更的测试覆盖相当完整,任务清单(tasks.md)全部完成,关键测试文件包括:

  • 契约与路由:provider-quotas.test.ts(鉴权、?refresh=1、响应校验);
  • 服务层:provider-quota-service.test.ts、provider-quota-service-cache.test.ts(缓存 TTL 与失败保留)、provider-quota-timeout-isolation.test.ts(超时隔离)、provider-quota-local-cost.test.ts;
  • 订阅探测:provider-subscription-quota.test.ts、opencode-go-web-quota.test.ts、opencode-go-local-quota.test.ts;
  • TUI:provider-quota.test.ts(响应式渲染、刷新、导航、空/错误状态)、commands.test.ts(命令解析)。

任务清单 5.1/5.2 还专门补充了 Grok gRPC-web 与 Kimi Code 的分类/解析覆盖,保持 runtime 与 GUI 的配额分类一致。


十、非目标、风险与迁移

10.1 非目标(design.md 明示)

本变更不做:替换/v1/usage、/context或 GUI 现有 Electron IPC 路径;后台轮询、通知、购买入口、持久化配额快照;新增交互式 OAuth 流程或导入任意浏览器 cookie;向 TUI 客户端发送凭据、cookie、原始响应体或用户自定义配额 URL。

10.2 风险应对

  • 上游私有端点/官方客户端存储格式可能变化 → 每个解析器/凭据解析器相互隔离、校验期望形状、逐提供商清洗失败,并用固定 URL 与 payload 覆盖测试;
  • 配额探测可能拖慢 TUI 路由 → 4 并发、12 秒超时、256 KiB 上限、立即显示 loading、刷新保持手动;
  • 系统钥匙串凭据提示可能打扰用户 → 优先配置文件/已配置凭据、限制平台命令、绝不触发登录 UI;
  • GUI 与 runtime 探测实现可能漂移 → 先在测试中对齐 DTO 与行为,后续可在两套界面稳定后提取共享包。

10.3 迁移与回滚

该变更是纯增量的:已有连接注册表与设置无需任何迁移;回滚只需移除路由、客户端方法与命令,提供商配置与凭据完全不受影响。


总结

/quota是 Kun TUI 中一个典型的"runtime 所有权 + 终端原生"功能:由 ProviderQuotaService 在进程内完成凭据解析与固定端点探测,通过严格的 Zod 契约 和GET /v1/provider-quotas路由下发归一化、净化后的快照;ProviderQuotaDialog 再以 Kun 既有的终端视觉系统渲染四种状态、进度条、重置时间与本地参考成本,并支持三档宽度自适应与全套键盘滚动。开发者只需记住:/quota看提供商额度,/usage看线程 token 用量,r刷新,Esc返回——配额探测的复杂性全部被隔离在服务端。

  • 人工智能
  • AI Agent
  • 自主智能体
  • 桌面应用
  • MCP Clients

【免费下载链接】Kun

Local-first AI agent workspace for coding, writing, design, research, and automation — one runtime for desktop GUI and TUI.

项目地址:https://gitcode.com/gh_mirrors/de/Kun
点击查看免费下载

相关推荐

上一篇:微信聊天记录永久保存与智能分析:WeChatMsg使用完全指南
下一篇:终极指南:如何使用Arduino-ESP32构建智能物联网设备

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询