xinghe-1018/dsh-token-plan-quota ↗★ 0
dsh-token-plan-quota
DeepSeek Harness Web 插件:输入框工具行的额度徽标,跟随当前模型供应商切换,零配置按在用路由自动开源。官方真值来源:DeepSeek /user/balance、Moonshot 开放平台、OpenRouter credits、阿里云 BssOpenApi(AK/SK)、千问 Token Plan 控制台网关(Cookie);官方无额度接口的供应商显示明确标注「实测」的本实例窗口。含模型工具 token_plan_quota。
安装
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:xinghe-1018/dsh-token-plan-quota说明文档
阅读完整 README ↗零配置自动检测
宿主在用的供应商路由就是唯一事实来源,所以你不用写 sources,也不用手填 providers——历史上正是这两个
字段各写各的,导致"卡查得到却永远看不见"。识别按可信度排序,命中即停:
| 依据 | 说明 |
|---|---|
baseURL 的 host | 最可信:路由实际打到哪。api.moonshot.cn → Moonshot 大陆区,openrouter.ai → OpenRouter,Token Plan 网关 → 千问 |
| 路由 id / 名称关键词 | 只在拿不到 host 时降级使用(出厂的 deepseek-official 就靠这条) |
| Key 前缀 | 最后的线索(sk-or- → OpenRouter) |
| 都没命中 | 给每条认不出的路由各挂一张 `window: |
| ` 实测源:徽标不空,只报本地 token/次数 |
本文里 provider、路由 id、providers 数组里的字符串是同一个东西:宿主 llm.listProviders() 返回的
id(比较时忽略大小写,. _ / 一律当 -)。所以 window:minimax-cn 的实测卡只在路由 id 为
minimax-cn 的模型下出现,检测自动挂的兜底卡也叫这个名字(window:minimax-cn)——两个写法指同一张卡。
两条不变量:
- 你写过的永远赢,但它是一层叠加,不是替换。
autoDetect: true(默认)时,你写的sources先落地, 检测再补齐你没管的部分——不会因为多写了一条就把它不认识的卡全清掉。"已覆盖"只有三种精确含义: ① 你某条源的providers里含这个路由 id → 该路由不再追加任何源;② 你有一条同名id的源 → 检测不再开 同名源(skipped记id-taken-by-configured);③ 你写了{"id":"","enabled":false}→ 这个名字 永不再自动开(skipped记disabled-by-config)。此外还有第四种可能:凭据解析不到的源整个不开 (skipped记no-credential,并列出试过哪些引用名;不挂错误卡占地方)。autoDetect: false才是"以你写的为唯一事实"。 - 可解释。每张自动开出的卡带
detected: {by, rule, host, fallback},标题 tooltip 写 「按 api.moonshot.ai 自动识别 · 区 international」;快照的detection块交代在用路由、开了什么、 谁因何被跳过、谁没被认出——排查"这家怎么不显示"只看这一处(它只在GET /token-plan-quota/summary的返回里,明细面板不显示这一坨)。老宿主没有llm服务时静默退回配置语义。
配置
主配置 ~/.dsh/token-plan-quota.json——宿主每次查询前重读,改完不用重启(也可写在插件行 config,
JSON 优先级更高):
{
"autoDetect": true,
"refreshMinutes": 10,
"pollSeconds": 10,
"panelScope": "current",
"debug": false
}
下表 18 行:17 行是代码里 DEFAULTS 的键,另 1 行 moonshotRegion 不在 DEFAULTS 中——它是 Moonshot 源
通过 regionConfigKey 读取的区选择器。一个键一行,单位都写在键名里(Minutes/Seconds/Ms)。
| 键 | 默认 | 含义 |
|---|---|---|
autoDetect | true | 按宿主在用路由自动补齐数据源。写 sources 不会把它关掉:你写的是一条叠加层,检测照样补齐你没管的路由,所以 "sources": [] 只表示"我没额外要求"(与不写等价)。设 false 才是"以你写的为唯一事实":此时什么都不写 → 回落到内置两条 deepseek-balance + token-plan-window;写 "sources": [] → 一张卡都没有 |
sources | 无(交给检测) | 数据源清单,按顺序显示。内置源名(8 个):deepseek-balance(DeepSeek 余额)、token-plan-console(千问控制台余量,Cookie)、token-plan-window(千问本地实测账本,非自建窗口)、moonshot-balance(Moonshot/Kimi 余额)、openrouter-credits(OpenRouter 余额)、account-balance(阿里云账户余额)、fr-instances(阿里云资源包实例列表)、resource-package(阿里云资源包额度列表);或简写 `window: |
| `(给任意供应商挂实测窗口);或完全自定义对象(见自定义源) | ||
moonshotRegion | china-mainland | Moonshot 区:china-mainland(api.moonshot.cn,CNY)/ international(api.moonshot.ai,USD)。两区 Key 不互通,选错会 401(卡片会直接提示切区);host 与币种成对切换,不做自动探测 |
refreshMinutes | 10 | 官方源的快照缓存分钟数:TTL = 分钟 × 60 秒,下限 15 秒,所以 "refreshMinutes": 3 就是每 3 分钟回源一次(想比 1 分钟更勤没有意义,秒级刷新请看 pollSeconds)。点面板「更新于」或带 ?fresh=1 可强制回源 |
pollSeconds | 10 | 前端轮询秒数——只管界面多久取一次快照,实测与吞吐每次实时重算;调小让徽标速度更跟手,不会多打上游 |
panelScope | current | 明细面板范围:current 只列 providers 里含当前路由 id 的卡 + 本实例实测;all 列全部源。所以 current 下看不到阿里云那三条——账户余额与资源包没有供应商归属,永远不进按路由过滤的视图,要看它们就设 "all" |
showInstanceWindow | true | 明细里是否带「本实例实测用量 + 限流重试观测」这一块。它不是某个数据源的开关,也不影响 `window: |
| ` 卡 | ||
exposeTool | true | 是否注册模型可调用工具 token_plan_quota |
debug | false | 明细里常驻回显上游响应的字段骨架(值打码、跳过凭据字段名)。与 GET /token-plan-quota/probe?source= 输出同一份东西,区别是 debug 常驻、probe 按需单次且不用改配置 |
endpoint | business.aliyuncs.com | 仅阿里云费用中心三个源使用:OpenAPI 接入点(国际站要换)。会自动剥掉 https:// 与末尾斜杠 |
regionId | 无 | 仅阿里云使用:OpenAPI 的 RegionId |
accessKeyIdRef | ALIBABA_CLOUD_ACCESS_KEY_ID | 仅阿里云使用:AccessKeyId 的凭据引用名 |
accessKeySecretRef | ALIBABA_CLOUD_ACCESS_KEY_SECRET | 仅阿里云使用:AccessKeySecret 的凭据引用名 |
securityTokenRef | 无 | 仅阿里云使用:STS 临时凭据的引用名(用 AK/SK 长期凭据时留空) |
configPath | $DSH_HOME/token-plan-quota.json | 外部 JSON 配置位置(~/ 落 OS 家目录,$DSH_HOME/ 落 harness 家目录) |
usagePath | $DSH_HOME/token-plan-quota.usage.json | 本实例账本落盘位置 |
minIntervalMs | 1200 | 出站最小间隔(毫秒),全局节流 |
timeoutMs | 15000 | 单次上游请求超时(毫秒,下限 1000) |
只想关掉某一张卡
sources 的一项既可以写成内置源名(字符串),也可以写成对象——选项只有对象形式带得上去,所以想关掉某一张卡,
要把那一项改成对象。这是一条排除项,不是整张清单的替换:autoDetect 保持 true,其余卡照常补齐。
{
"autoDetect": true,
"sources": [
{ "id": "deepseek-balance", "enabled": false },
{ "id": "fr-instances", "enabled": false }
]
}
- 对自动检测开出来的源同样有效:写了
{"id":"moonshot-balance","enabled":false},检测就不会再把这张卡补 回来,诊断块的skipped里留一条disabled-by-config——「没认出来」和「你关掉了」这两种情况得分得开。 - 关的是这一条源,不是这个供应商——但"这家还剩什么"取决于它在不在规则表里,这一条必须说白:
- 认得出的厂家(DeepSeek / Moonshot / OpenRouter)各自只有一条官方源,关掉它就没有这张卡: 实测兜底窗口只挂在认不出的路由上,不会因此补上来。
- 千问是唯一的例外:它同时有
token-plan-console(官方)与token-plan-window(实测)两条预设, 关掉前者,后者照旧在。 - 认不出的厂家(MiniMax 这类)本来就只有一张
window:卡,要关它就点它的名:{"id":"window:minimax-cn","enabled":false}(就是路由 id,见上节)。
- 不想逐张关,就把
autoDetect设false之后自己列全清单。两种做法都行,你写过的永远赢。
凭据
按顺序解析:DSH 凭据服务 → 环境变量 → ~/.dsh/.credentials.yaml → ~/.dsh/.env,每次查询重新解析,
换 Key 不用重启。
| 源 | 引用名 |
|---|---|
| DeepSeek | DEEPSEEK_API_KEY |
| Moonshot | MOONSHOT_API_KEY(须与区配对;自动检测会优先沿用路由 profile 点名的引用名,如 MOONSHOT_CN_API_KEY) |
| OpenRouter | OPENROUTER_API_KEY(sk-or-v1-…) |
| 千问 Token Plan 余量 | BAILIAN_CONSOLE_COOKIE(+ 可选 BAILIAN_CONSOLE_SECTOKEN 兜底) |
| 阿里云 | ALIBABA_CLOUD_ACCESS_KEY_ID / ALIBABA_CLOUD_ACCESS_KEY_SECRET |
Key 引用名可在 sources 条目里用 bearerRef / cookieRef 覆盖。
想看到千问的真实余量(订阅页那种「剩余量 65.1% / 总额度 10,000」):登录打开
订阅页 → F12 Network → 复制任一请求的
整行 Cookie: → 存成一行 BAILIAN_CONSOLE_COOKIE: → 点面板「更新于」刷新。Cookie 通常能用几周,
过期后该源报错并自动退回实测卡,重贴即恢复。
自定义源
上游给了新端点、或你想把某个控制台接口接进来,不用等本插件更新——kind: "single" + fields/derive
就能覆盖"余额是差值"这类形态:
{
"sources": [{
"id": "my-balance", "label": "我的余额", "kind": "single", "metric": "money",
"url": "https://example.com/api/account", "method": "GET", "bearerRef": "MY_API_KEY",
"unit": "USD",
"fields": { "credits": ["data.total_credits", "total_credits"], "usage": ["data.total_usage", "total_usage"] },
"derive": { "total": "credits", "used": "usage", "remaining": "credits - usage" },
"providers": ["my-provider"]
}]
}
derive 只认「一个 +/-、两侧是 fields 里的引用名或数字」;任何一个操作数取不到就整条空,绝不猜数补上。
fields 的槽位名随你起(credits/usage 只是示例),它同时也是 derive 表达式里能引用的变量名;
每个槽位给多条候选路径是有意为之(同一家 API 版本间 data 信封加不加都见过)。
手写条目的全部可用键
认不出的键不会报错,只是没作用(条目对象是原样合并进去的),所以键名打错字的表现是"卡片空着"而不是启动失败。 这张表是权威清单:
| 键 | 用在哪 | 说明 |
|---|---|---|
id | 必填 | 源标识,同时是缓存键和 probe?source= 的参数。两个条目用同一个 id 会共用缓存并叠成两张卡(日志会 warn),第二个请改名 |
kind | 建议写 | single(一次读数,不写即此值)/ list(多条资源列表)/ window(本实例实测窗口,不打网络) |
label / labelEn | 标题 | label 是卡片标题,不写时回落成该条目的 id。labelEn 会随快照下发但当前界面不消费它(面板标题只读 label),留着是为宿主英文化 |
url | single / list | 端点。写了 url 即 HTTP 源;不写 url、也不是内置源名,则按阿里云 OpenAPI RPC 处理(要 action + version) |
method | HTTP | 默认 GET |
headers | HTTP | 追加请求头,与 accept: application/json 合并 |
jsonBody / formBody | HTTP(POST 用) | JSON 请求体 / application/x-www-form-urlencoded 请求体 |
bearerRef / cookieRef | HTTP | 凭据引用名。Bearer 型只认 bearerRef,Cookie 会话型只认 cookieRef——不会拿套餐 Key 去顶替 Cookie(反之亦然),因为那只会得到一张误导性的错误卡 |
action / version / params | RPC | OpenAPI 的 Action、版本号、查询参数(params 会被扁平化)。RPC 型缺 action 或 version 会直接出错误卡 |
paginate | RPC | { "mode": "page", "request": "PageNum", "pageSize": "PageSize", "total": ["TotalCount"] },或 { "mode": "token", "request": "NextToken", "response": ["NextToken"] };page 模式最多翻 10 页 |
fields | single | 变量名 → 候选路径数组,取到数的才进 derive 作用域 |
extract | single | 上游直接给值时的路径:remaining、total、unit。它们也能被 derive 引用 |
derive | single | 只有 remaining / total / used 三个键会被消费;used 不写时,remaining 与 total 都在就自动算差值 |
extra | single | 副信息,键名随你起;toppedUp/granted/cash/credit/quotaLimit 有既定文案 |
list / item | list | list 是数组候选路径;item 内可给 name/id/remaining/total/used/unit/status/expiresAt/startsAt/cycleType/capacityType/haystack |
metric | 显示口径 | money / credits / count。默认值按构建器不同:手写 single 源是 money,list 源是 credits,window 是 count——要按 token/次数显示就显式写 |
unit | 显示 | 上游不给单位时的兜底(如 USD);metric:"money" 且上游不给时兜到 CNY |
providers | 面板归属 | 这条属于哪些路由 id(见上文「provider 就是路由 id」)。panelScope:"current" 下,列表里没有当前路由就不显示这张卡;写了却一条都对不上在用路由,宿主日志会提醒(免得对着空白面板瞎猜);`window: |
| ` 简写会自动填 | ||
windowDays | window | 实测窗口天数,默认 7,最小 1 |
regions / region | 多区供应商 | regions 是「区名 → 该区的字段覆盖(host 与币种成对换)」,region 选哪一区;写错的区会 warn 并退回第一个 |
enabled | 任意 | false 停用这一条(临时关源不必删整段) |
表外的键是内置预设专用,手写别照抄:builder、apiPrefix、consoleSite、gatewayAction、gatewayProduct、
infoUrl、secTokenRef、dashboardURL、regionConfigKey、errorHints、keywords 走的是各家特定的签名/CSRF/
信封流程,只对相应预设成立。特别地:面板里的多行窗口(「5 小时」+「每周」两行)目前只有
token-plan-console 会产生,手写条目一条只有一个读数;想显示两个窗口,就写两条源。
实测窗口用 {"kind":"window","providers":["my-provider"],"label":"我的窗口","windowDays":30},
或简写 window: 。接入新厂家的完整核对流程见 docs/adding-a-provider.md。
卡片空着、又没报错,按这个顺序查
GET /token-plan-quota/probe?source=(或临时开"debug": true)——先看上游实际回了哪些字段名;- 对照上面那张权威键表逐字核对键名:认不出的键不报错、只是没作用,表现就是卡片空着而不是启动失败;
fields的路径是否真命中了这份响应(信封加不加data见过两种),derive引用的名字是否都取到了数 ——任一操作数缺失就整条空;- 如果这张卡根本没出现,那通常不是空卡问题:凭据解析不到的源整个不开(不挂错误卡占地方),
或者被
panelScope: current挡在视野外。看快照的detection块,它会写明谁被跳过、为什么。