- 人工智能
- AI Agent
- 自主智能体
- 桌面应用
- MCP Clients
【免费下载链接】Kun
Local-first AI agent workspace for coding, writing, design, research, and automation — one runtime for desktop GUI and TUI.
导读
本文以 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 existing
GET /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的字段如下:
| 字段 | 类型 | 说明 |
|---|---|---|
providerId | string (1–128) | 提供商稳定标识 |
providerName | string (1–120) | 展示名称 |
presetId | string,可选 | 预置身份(如claude-subscription) |
status | enum | available/unsupported/missing_credentials/error |
source | string,可选 | 数据来源描述(如 "DeepSeek balance API") |
dashboardUrl | url,可选(≤2048) | 跳转到官方控制台的 URL |
summary | string,可选 | 计划名/摘要(如 Z.ai 的 planName) |
metrics | 数组(≤500) | 归一化指标 |
localCost | 对象,可选 | 本地 API 参考价估算 |
updatedAt | datetime,可选 | 本条刷新时间 |
message | string,可选(≤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中,全部为固定只读端点:
| 提供商 | 主机名判定 | 固定端点 | 解析器 |
|---|---|---|---|
| DeepSeek | api.deepseek.com | https://api.deepseek.com/user/balance | parseDeepSeekQuota |
| OpenRouter | openrouter.ai | https://openrouter.ai/api/v1/credits+/api/v1/key | parseOpenRouterQuota |
| Moonshot 全球 | api.moonshot.ai | https://api.moonshot.ai/v1/users/me/balance | parseMoonshotQuota |
| Moonshot 中国 | api.moonshot.cn | https://api.moonshot.cn/v1/users/me/balance | parseMoonshotQuota |
| Z.ai | api.z.ai | https://api.z.ai/api/monitor/usage/quota/limit | parseZaiQuota |
| BigModel | open.bigmodel.cn | https://open.bigmodel.cn/api/monitor/usage/quota/limit | parseZaiQuota |
| MiniMax 全球 | api.minimax.io | /v1/token_plan/remains、/v1/api/openplatform/coding_plan/remains(双端点多退避) | parseMiniMaxQuota |
| MiniMax 中国 | api.minimaxi.com | 同上 | parseMiniMaxQuota |
| OpenAI(exact) | api.openai.com | https://api.openai.com/v1/dashboard/billing/credit_grants | parseOpenAiQuota |
| Kimi Code | api.kimi.com(且 stableId 为kimi-code) | https://api.kimi.com/coding/v1/usages | parseKimiCodeQuota |
| 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 进程内。落地在四个层面:
- DTO 白名单:Zod
.strict()schema 只允许归一化展示字段,requestJson返回的原始 JSON 从不进入响应体; - 错误信息清洗:
quotaErrorMessage只保留固定文案(如 "The quota request timed out."、"The provider did not authorize quota access for this credential."),原始响应体或凭据值绝不外泄;订阅探测中凭据解析失败统一抛出ProviderQuotaMissingCredentialError引导用户登录; - TUI 端二次清洗:渲染前所有不可信字符串经过 secret-redaction.ts 的
redactSecretText与 TUI 布局层sanitizeTerminalText(终端控制字符清洗),见 provider-quota.ts; - 传输层代理感知:探测通过
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.")。
- 标题行:语义 glyph + 加粗提供商名,右侧右对齐显示状态标签(
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.
相关推荐
Kun 终端配额面板落地实录:从运行时契约到 TUI `/quota` 路由的完整实现解析
Kun 终端配额面板落地实录:从运行时契约到 TUI /quota 路由的完整实现解析 Kun 的桌面 GUI 已经能够展示已配置模型的账户余额与限流窗口,但独
人工智能AI Agent自主智能体桌面应用MCP ClientsKun TUI 提供商配额查询(`/quota`)深度解析:运行时合约、固定端点探测与终端渲染
Kun TUI 提供商配额查询( /quota )深度解析:运行时合约、固定端点探测与终端渲染 本文围绕 Kun 开源仓库中 add provider quot
人工智能AI Agent自主智能体桌面应用MCP ClientsKun Provider Quota TUI:在终端内安全查看各模型厂商余额与用量配额的设计与实践
Kun Provider Quota TUI:在终端内安全查看各模型厂商余额与用量配额的设计与实践 Kun 桌面 GUI 已能在 Electron 主进程中展示
人工智能AI Agent自主智能体桌面应用MCP Clients
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考