Repository navigation
Releases
v0.4.13
[0.4.13] - 2026-11-02
Fixed
-
ROADMAP §6 那 13 条 low 与 1 条增强全部清掉(第五轮审查的尾巴,逐条附反证或行为验证):
·link-targets:HTML 属性抽取现在限行、限长、且要求属性名前是空白或<(未配对的引号不再吞掉整段
文档,data-src/xsrc=不再被当成目标);两个入口共用一次剥壳,HTML 注释替换成空格而不是
空串(![x]<!-- c -->(a.png)曾被拼成一条不存在的引用);远端链接改为解码后比较、owner/repo 忽略大小写、
并要求剩余段整段对上[blob|raw|tree]/<ref>/<wanted>(未解码时中文图名永远比不中=假红,endsWith连
更深一层的同尾路径都算=假绿);stripTarget的结果改名undecorated。新增 7 条行为用例,四条关键改动
逐个改回去验过会红。
·check-docs:package.json不再读两次;第 4 / 4b / 10 项共用一次抽取与分类(过去三处各跑一遍、
classifyTarget分支各写一份);编码护栏读不出的文件改为记违规而不是静默跳过;第 10 项块内缩进归位、
路径口径统一;正文里按名字写的 guards 数字纳入核对。
·diagrams/make-diagram-png.mjs:--viewport 0x0在解析阶段就拒(原先设备度量整个被跳过,
报错却打印'视口=0x0');覆盖守卫先判isFile再只读前 8 字节(目录/FIFO 不再抛 EISDIR 顶掉
安全提示);页面求值改成空安全(SVG 没了不再表现为'页面求值异常');cdp.close()显式释放
WebSocket 句柄(原先靠杀进程间接断开,而那次 kill 是静默 try/catch);失败出口改为同步写 fd 2 并带上调用栈,
非Error值不再打印'失败:undefined'。成功路径实跑验证(61 s,裁剪区 1410x704 → 产物 2820x1408,
尺寸断言按取整后的裁剪区精确对上)。
·NotLogined诊断(§6 第 14 条增强):期望票据不在、但名字里确实有一张*ticket*时,改为点名'看到的
是哪张、本控制台要的是哪张'——那只另一台主机的指纹正是用户最需要的线索。断言随之收紧并验证可证伪。 -
Windows 绝对路径从越界检查里隐形(第五轮审查,
link-targets):/^[a-z][a-z0-9+.-]*:/i会把C:当成协议,
于是[x](C:\Windows\win.ini)被判成skip、调用方直接continue。这道门禁的立身之说就是"把越界面彻底关掉",
而它在本机平台上恰好漏掉。现在盘符先于协议判定,归入escape。 -
markdown title 只剥双引号那一种(同轮):
[x](docs/a.md 't')留下尾巴被当成相对路径去existsSync,
制造指向错契约的假红;HTML 那边(extractHtmlTargets)是刻意支持三种引号的,两边口径现在一致(双/单/括号)。 -
引用式定义正则两处过宽(同轮):
\s*跨行,[1]:这种空定义会把下一行第一个词吞成目标;
没有行尾锚,正文里[TODO]: finish this section会造出假目标finish并报"链接目标不存在"。
改为要求目的地与可选 title 同行、行尾收住。 -
文档门禁在测试套件失败时拿不到结论(同轮,high):
execFileSync在套件非零退出时直接抛,
本脚本带着未捕获堆栈终止,于是"有 N 项失败"那条分支从来执行不到(死代码),后面的 6b/6/10 项与汇总
全不跑,报错文案还把「文档门禁」与「测试失败」混成一坨。现在捕获输出、给出结构化结论(反向验证:
故意让一条用例红 →test/guards.mjs 实跑失败… 有 1 项失败,不再是堆栈),并补maxBuffer。 -
"当前版本必须有 CHANGELOG 章节"被自己的前置条件短路(同轮,high):正常发布流程就是先有 tag,
而这条要求tags.includes(v) === false才检查——缺章节照样绿灯,宣称的覆盖面≠实际覆盖面。
现在无条件要求,并补反方向:CHANGELOG 最新的已发布章节必须等于package.json#version,
且该版本必须有[X]:链接定义行(半次发布也抓住)。 -
兼容区间有两份手写副本却没有等值门禁(同轮):
dsh.engines.dsh与dshhub.compatibility.dsh
(以及两处node区间)本轮就被同时改了两遍——只改一处时宿主装得上而目录站拒收,而npm run check
全绿。check-manifest现在断言两组等式。 -
编码护栏读不出文件时静默跳过(同轮):扫描面自检按枚举数统计,所以读失败不会被它抓住;
Windows 上占用/权限/路径异常很常见,那是"零违规通过"的假绿。现在记一条违规。 -
发布手册教的路径与脚本相反(同轮,两次审查各报一次):
RELEASE.md的流水线图与「一句话版」
仍写着git push origin main后再推 tag(正是被本轮消灭的竞态),还留着本机npm login && npm publish
——而 §3.1 的标题就是"npm 的身份不在你电脑上"。两处已同步成git push --atomic origin HEAD:main,
并在test/guards.mjs给文档加了与脚本同口径的反向断言(漂回旧写法立刻红)。 -
NotLogined诊断的票据名有两处字面量(预设 +??兜底):预设改名而兜底忘改,诊断就会点一张不存在
的票据;??也挡不住被覆盖成空串(文案退化成悬空半句)。现在DEFAULT_LOGIN_TICKET_COOKIE是唯一来源
(预设显式引用),兜底用||,并补两条用例证明字段真的驱动文案、空串退回具名默认。 -
walk-skip.mjs导出的是活的Set:任何消费者 require 时add/delete一笔,改的就是另一道门禁的
扫描面——而该模块的前提正是「谁也不持有私有副本」。改为导出shouldSkipDir与冻结快照,
.gitignore的目录条目与跳过集合之间加了双向核对(只改 .gitignore 的人现在会立刻撞红)。 -
diagrams/make-diagram-png.mjs:尺寸断言原本拿未取整的rect反推,而裁剪用的是rounded
(--scale 3/4时差到 2px 就会误拒一张正常的图);reloadAndSettle内部只等 load 事件、没有任何超时,
离线/CDN 被劫持时脚本既不落盘也不非零退出(与文件头"一律非零退出并说清原因"的自证相违)。
现在断言以真正传给 shot 的那组数为准,等待加了有界超时。 -
正文里
guards N 项这类按名字写的数字此前没人核(6b 只按位置核 host/client 两个数)——现在按
套件名单独锚定,中英两份都核。 -
报错的余额卡正面显示"没有数据"而不是"查询失败"(第四轮审查,lib/client.js 的
cardValueText):
0.4.12 那条「报错且无数字 → 正面说失败」排在metric:"money"与实测卡的早退之后,
所以对余额卡永远走不到——而凭据未配置时宿主给的正是这样一张卡(lib/index.js的 Bearer 门不分
metric)。实机症状就是接口坏了却看着像"只是没数据",而这恰是那条分支要消灭的观感。现在把判断
提到函数最前面(hasNumbers已经保证没有真读数,不会因此少显示任何数字)。 -
徽标降级时,没有数字的窗口卡抢在报错卡之前(同轮第 2 条,
pickOne的⑦⑧两档):走到这两档的
必然没有数字(有数的被上面几档收走),于是画面落在"无官方额度数据"上,而那张报错卡带着
「去配 Cookie」的指引。两档对调,可行动的那张优先。
两组正反回归都验证过可证伪:把任一改动回退,对应那条立刻变红(239/238 passed, 1-2 failed)。 -
NotLogined诊断里的提示会被另一台主机的票据顶掉(第二轮审查第 1 条,已用回归测试证伪旧写法):
判断用的是泛匹配/ticket/i,而抄错来源时 Cookie 里本来就有login_aliyunid_ticket(阿里云控制台的票据),
于是"没有看到登录票据"这半句被吞掉——那正是本条诊断要认的错事。现在按预设给出的票据名精确匹配
(loginTicketCookie,默认login_qianwenai_ticket),文案里的名字也用变量,不把这个通用助手与
某一家控制台焊死。正反两组回归:另一家的票据不能顶掉提示;票据在场时不许再报缺。 -
data-src这类同名后缀属性会被当成src收进引用检查(第二轮审查第 6 条,已复现):HTML 属性抽取
用了/b一类的词边界写法,而它b在连字符后面也成立。于是<img data-src="docs/images/a.png">
(懒加载标记,合法 HTML)凭空造出一个待检查目标,data-src="../../outside/x.png"制造假红,而一张
根本不渲染的占位图就能把"两侧引同一张图"这条断言喂绿。现在要求属性名前是空白或<(xsrc=一并
排除),正反两组用例进行为矩阵。 -
结构图的引用口径自相矛盾(第一轮审查第 2 条,已复现):合并时把
architecture.png塞进了截图套的
存在性清单,而那份清单同时喂给逐语言的引用集合——集合只认 markdown 行内图片(对<img src=x.png>,
extractImageTargets返回空数组)。于是谁把结构图改写成已被声明为无害的<img src>形态,报错会来自
截图契约、指向错的契约。现在拆成两份清单:SCREENSHOTS进引用集合,DIAGRAM只做存在性检查与
mentionsTarget点名断言;该常量挪到模块作用域,不再两处各写一遍。 -
src=形态只有半个检查面看得见(第一轮第 1/3/5 条):mentionsTarget自己补了一条只认双引号小写的
正则,而存在性、越界、随包发布三条检查走extractLinkTargets(HTML 分支只有href)。后果是
<img src="../../outside/x.png">对越界检查完全隐形、却能让点名断言变绿;而<img src='x.png'>、
<img src = "x.png">、<IMG SRC=…>一律被判成没引用(004 的 Lens 1 收了前三类,这是第四类)。
现在 HTML 属性形态抽成extractHtmlTargets,href 与 src 共用一条实现,双/单引号与裸值三种形态、
等号空白与大小写都认。 -
远端同尾路径能让"双语同源"断言假绿(第一轮第 4 条):
pathname.endsWith('/' + wanted)对任意 host、
任意仓库都放行——别家仓库的 blob 链接、CDN 上的过期副本都算已引用,本地那张可以一次都没被引到。
现在绝对链接必须落在本仓库owner/repo前缀下才算(由调用方从package.json#repository.url取),
不给就不认远端形态。 -
发布脚本的失败提示把刚修掉的竞态又教了一遍(第一轮第 7 条):回退提示仍写着两步 push,照它做等于
手动重建ci看不到 tag 的窗口;而且它写的是main,在 detached HEAD 的 runner 上会把旧 tip 再推一遍。
改成一次推两个 ref,并补--atomic:多 refspec 的 push 默认不保证原子,main 被拒时 tag 仍可能落地,
正是这道发布门要拦的"有 tag、main 却没有对应提交"。 -
"门禁的扫描面不含 IDE 缓存"当时只做了一半(第一轮第 1 条的前置,已核):对齐发布线时只给
check-refs
加了.qoder/.dsh,而check-docs的编码护栏跑的是它自己那份SKIP_DIRS,仍会把 57 份生成文档读进来
判编码。今天没红只是因为那些文件恰好干净——红了却修不了(文件不在仓库里)。现在抽出
scripts/walk-skip.mjs作为唯一来源,两份门禁共用,test/guards.mjs钉住"谁再各写一份字面量就红"。 -
发布流水线分两次 push,把每次发版自己的 main 门禁推红(0.4.12 实发):
git push origin HEAD:main与
git push origin v0.4.12之间那几秒,main 的 push 事件已经拉起ci,它的 checkout 还看不到尚未落地
的 tag ——check-docs的"CHANGELOG 引用的 tag 必须存在"判红(ci#51),而同一时刻从 tag ref 起跑的
publish#16是绿的、npm 也确实上架了。红的是 main 不是包;最坏后果是下次发版前没人再信绿灯。
Changed
scripts/walk-skip.mjs的跳过集合补上.scratch(与.qoder/.dsh同类:都在.gitignore里、都是本地
草稿或可重生成物。实机数过:旧集合会把 27 份草稿读进扫描面,其中 1 份是 markdown——活文档门禁会为了
一个不在仓库里的文件判红),并去掉装饰性的 shebang(同目录的纯库模块都不带;带 shebang 的应当是可跑的
门禁)。
npm: https://www.npmjs.com/package/dsh-token-plan-quota/v0.4.13
安装:dsh plugin add dsh-token-plan-quota
v0.4.12
[0.4.12] - 2026-11-02
Fixed
-
额度徽标在 DSH 0.2 上不跟随模型、降级时只剩「无官方额度数据」(实机反馈"插件又没有正常显示了")。
两层叠加:① 0.2 起客户端的sessions.list快照是{ ids, byId, phase },没有current,
而插件只读.current→ 当前会话恒为null→ 模型供应商恒为null→ 徽标永远走"路由解析不到"
的降级分支(宿主取"主区域那一个会话"的口子是retainedBy.mainView > 0,现已两代形状都认);
② 降级分支把"有数字"和"报错"混在同一档、且候选只取官方卡 —— 控制台 Cookie 失效后,
报错卡按数组顺序先命中,正面落在noData上,而同一时刻实测窗口卡有数千万 tokens 可以显示。
现在降级挑选与跟随路径同一优先级(真在服务请求的那条路由上、有数字的卡优先,报错卡垫底、
候选池含实测卡),报错且无数字的卡正面改说「查询失败」(错误码与修复提示仍在 tooltip 与面板)。
client 侧从 207 项涨到 217 项,新增三组回归:新外壳形状能认出现行会话、实机症状原样复现、
只剩报错卡时的正面文案。 -
"余量百分比只留一份算法"守卫不再数调用点个数:它原先断言
remainingPercentOf(出现 3 次,
每加一处用途就得改这个魔数(这次就是它先红,还把check-docs一起带红)。改成认结构:
换算只有一处、徽标与次级计量都走它、实测卡一律不出条。 -
「控制台 Cookie 失效」的提示把人指去了插件从不调用的域名(2026-10-06 实机):提示写着
"在 Network 筛选器输入bailian-cs,复制bailian-cs.console.aliyun.com/data/api.json那条的 Cookie",
而预设打的是POST https://cs-data.qianwenai.com/data/api.json?action=BroadScopeAspnGateway
(DECLARED_HOSTS里也只有后者)。结果是用户在筛选框里打bailian-cs一条请求都筛不出来,
永远抄不对 Cookie。现在提示与中英 README 都点名cs-data.qianwenai.com那条请求,并说清
"Cookie 按域隔离、要抄插件实际调用的那台主机";host 侧新增守卫:该提示必须含预设 URL 的主机名、
且不得出现console.aliyun.com。 -
网关回
NotLogined时不再只说"没登录"(同一天的第二次实机):光说结论没法行动 —— 用户抄来的
是另一台主机的反爬 Cookie 集(cna/cnaui/aui/sca/atpsida/tfstk/isg/partitioned_cookie_flaglogin_aliyunid_csrf),里面没有千问控制台的登录票据。现在错误里会写清插件打的是哪台主机、
收到的 Cookie 有哪些名字(只列名字,值绝不进提示),并在缺login_qianwenai_ticket时点名。
中英 README 的抄 Cookie 步骤也补上两条硬要求:同一台主机 + 必须含登录票据。host 侧从 381 项涨到 388 项。
Changed
dsh.engines.dsh与dshhub.compatibility.dsh补上^0.2.0-0:插件一直跑得动 0.2
(工具与/summary正常、host 379 项全绿),只是声明还停在 0.1.x;客户端侧本轮已显式支持
0.2 的会话列表形状,声明与事实对齐。- 补文档同步:
allowUndeclaredHosts进中英 README 配置表,键数 18、配置表 19 行、
client 测试 217 项一并改齐;check-docs的英文正文断言冠词放宽为an?(19 行时说 "a 19-row")。
npm: https://www.npmjs.com/package/dsh-token-plan-quota/v0.4.12
安装:dsh plugin add dsh-token-plan-quota
v0.4.11
[0.4.11] - 2026-09-29
Security
- 上游错误体里的凭据不再随错误信息出网(CWE-209):
SourceError的raw早已按值脱敏,但
message 没有——而 message 同样会随卡片(card.error)与/probe(error: error.message)
出网。真实的注入拦截 / WAF 常把收到的Authorization原样写进错误体(Invalid Authentication: Bearer sk-…),于是报错信息里就躺着真 Key。现在SourceError新增可选的secrets参数,把
message 与 raw 一起按值脱敏,四条出站路径都把手头的凭据(Cookie / Bearer / 请求体 / AK-SK)传进去;
"不传就不脱敏"这条契约也用一组对照断言钉住(边界不是魔法,调用点必须传)。回归用例是端到端的:
起一个把Authorization原样回显进 401 体的回环服务,断言卡片error与raw都不含该密钥。
(CodeRabbit 在 PR #3 上报出;采纳前我核了代码与四条出站路径。)
Added
- 出站主机白名单:
dshhub.permissions.network从"文档承诺"变成运行时断言——请求发出之前
核主机在声明集合里,未声明一律HostNotDeclared拒发(含非绝对 URL 的BadUrl)。自定义源指向
别处要显式打开新配置allowUndeclaredHosts,打开后快照里多一条 notice,不静默降级。
起因:endpoint(用户可配)与自定义源的url原本能把含 AK/SK 签名的请求打到任意主机而没有任何
检查,"声明集合 == 实际出站集合"(宪法 V)于是无法成立。代码侧真值DECLARED_HOSTS与
package.json的声明由门禁双向核对;docs/adding-a-provider.md同步要求两处一起改。
Fixed
-
门禁宣称的覆盖面大于实际覆盖面(比漏检更坏的假绿):
- 出站主机检查里扫 README 的那行是死代码(两份 README 里根本没有那个 URL,实测出现 0 次),
而只做单向比对;DEFAULTS.endpoint(阿里云 BSS 的裸主机名)与lib/client.js的字体 CDN
这两个真实出站主机压根不在被检集合里。现在推导逻辑抽成scripts/outbound-hosts.mjs
单一实现(预设 + endpoint + FONT_CSS),双向比对,并带扫描面下限自检。 - 编码护栏只扫 "11 个顶层文件 + 6 个目录一层",漏掉
.githooks/pre-commit(不变量 VI 点名它依赖
shebang,BOM 会让 shebang 失效)与scripts/shots/*.mjs;改为递归全仓文本文件并带下限自检。 - 截图契约只数引用条数(同一张图引 7 次即过),而"13 张图存在"由另一份硬编码清单保证,两者从不
交汇;改为按集合核对:名单里每张图都要被对应 README 引到。 check-refs只扫 ROADMAP +workflow/+specs/(19 个活文档),漏掉scripts/shots/README.md
里那 5 处行号引用;改为全仓.md(豁免 CHANGELOG 与reviews/),现在明确报 34 个活文档。test/guards.mjs的rejects()捕任何异常都算通过——夹具自己崩了(TypeError)也是绿;现在要求
消息匹配预期,或至少驳回"不像守卫在拒绝"的运行时错误。新增出站主机正反例 10 条。
- 出站主机检查里扫 README 的那行是死代码(两份 README 里根本没有那个 URL,实测出现 0 次),
-
发布流水线三处静默失手:
- 用
GITHUB_TOKEN推的 tag 不会触发 publish.yml(GitHub 防递归的设计,例外只有
workflow_dispatch/repository_dispatch),而一键发版推 tag 用的正是它——tag 看着推成功、
两处注释都宣称 publish 接手,npm 上什么都没有。现在release.yml推完显式 dispatch
(因此补上actions: write);publish.yml两条入口改为等价(原先 dispatch 路径不建 GitHub Release)。 - CI 下"只在 main 发版"闸门被整条跳过,而
workflow_dispatch的分支是人在 UI 上选的、推的却是
HEAD:main;改为 CI 下校验GITHUB_REF === refs/heads/main。 - 发布提交撞自家门禁:先 commit 后 tag,而 CONTRIBUTING 推荐的 pre-commit 钩子会跑 check-docs
(它要求 CHANGELOG 引用的 tag 已存在)→ 提交被拒、留下"已改已 add、无 commit"的半途状态;
改为git commit --no-verify(脚本本就在打完 tag 之后跑完整七步自检)。 make_latest: 'true'与旁边注释相反(不传该字段才是 API 默认=新建即标 latest),补发旧版照样
抢 Latest;改为显式比较版本:严格更高才标,API 查不通时保守不标并告警。
- 用
修复(评审轮:CodeRabbit 在 PR #3 上的 7 条发现)
- 手动发布要落在 tag 的那个 commit 上:dispatch 入口原先只从
package.json取版本号,却不核对
"这次 checkout 是不是该 tag 指向的 commit"——tag 推完之后 main 再前进一格,一次手动重跑就会把
后来的代码以同一个版本号发上 npm,而 GitHub Release 还指着 tag 的 commit(包内容与 tag 不一致)。
现在真要发布(dry_run=false)时 tag 必须存在且指向本次 checkout;dry run 只告警——它的用途正是
在分支上验机制,那里本来就没有对应 tag。 - 候选版本等于现有 latest 时不再摘掉 latest:比较从
>改成>=。同一个 release 重跑时,
旧写法会发false,等于让 action 取消那条已经标好的 latest。 - 出站主机比对走归一化
origin:https://API.DeepSeek.com:443/与声明里的
https://api.deepseek.com是同一台主机;拿原文比会误报"未声明"(假红)。 - 结构化
raw与 RPC 的单条凭据也进脱敏:parseEnvelope把上游失败信封塞进raw时没把
secrets 传给configlessTrim(按键名过滤挡得住"键名叫 sec_token",挡不住"值里回显了凭据、
键名由上游自取");postForm原先只把整个请求体当候选值,而上游可能只回显一个 AccessKeyId。 owner/repo抽取保留点号(owner/plugin.v2.git曾被[^/.]+抓成owner/plugin,
算出的投稿条目文件名跟着错),并抽成scripts/repo-path.mjs单一实现——原先
release.mjs与check-submission.mjs各写一份,加package.json里的repository.url一共三份。workflow/ENVIRONMENT.md去掉会漂的门禁项数(它写着 379/207/32,实跑是 420/224/91),
并把耗时改成实测的 7 秒——该文件开头自己写着"能查到的东西不写在这里,写下来就是一份会过期的缓存"。
Fixed
- 徽标只剩余额钱数、条状额度不见了(0.4.10 引入的偏差)。当前路由解析不出供应商时
(中转/自定义路由,模型目录不认它),0.4.10 用模型名当挑卡线索 —— 而deepseek-v4.1-flash
是模型家族,实际计费路由是千问 Token Plan,于是绑deepseek的余额卡把额度卡顶掉了,
实机反馈正是"只剩余额的钱数"。现在线索换成吞吐行里最近真在服务请求的那条路由(那才是
计费路由),再退一步优先"额度/窗口"语义的卡,最后才按官方卡顺序;候选集也补上了"有数字"
之外的那些官方卡(否则它们会被余额卡直接顶掉)。回归测试补了一个带官方额度卡的 fixture
模式 —— 默认快照里没有那张卡,所以 0.4.10 那版断言测不出这个 bug。 - 复核口径一并更正:此前几轮的"真宿主真 DOM"证据打的是 3080 的 checkout 实例,而实际使用的是
打包版 GUI(19387)。本轮起对实际实例量:胶囊数 1、Token Plan 余量 51%、条在。
npm: https://www.npmjs.com/package/dsh-token-plan-quota/v0.4.11
安装:dsh plugin add dsh-token-plan-quota
v0.4.10
[0.4.10] - 2026-09-25
Changed
- 判定口径:宪法原则 I 升到 v2.0.0(MAJOR —— 按 Governance 自己的档位,改的是 NON-NEGOTIABLE
原则的边界)。原来那句「没有官方分母就永不出现百分比」写成绝对式,是因为写下它的时候百分比
只可能从remaining/total派生;现在千问控制台直接返回usage.per1MonthPercentage,
绝对式就自相矛盾:一个可追溯到官方字段名的真值,因为同一次查询里另一路没取到而被丢掉,反而违背
原则 I 第一句(每个数字必须可追溯到官方真值)。新口径:永不自行派生百分比(used/total
与任何换算式),上游直给的比例属官方真值,可显示、也可照它画余量条(100 − 已用%是同一个
官方数的反向写法);缺分母时不报绝对剩余量、必须留诊断。同步改:.specify/memory/constitution.md
(含 Sync Impact Report)、AGENTS.md同一句、两份 README「不做什么」、docs/upstream-contracts.md、
finalizeMeter与buildConsoleCard的注释。修订过程中初稿还写了"缺分母时不画余量条",实机
半小时就被否掉(用户:"换回条状显示")——条画的是同一个官方比例,禁它只是视觉上惩罚用户,
不增加任何真值保护;v2.0.0 尚未随任何已发布版本出厂(0.4.9 打的是 1.0.1 口径),所以在它自身的
措辞里改掉,不另计版本号。
Fixed
- 上游慢的时候,千问额度卡整张退回"无数据" —— 丢掉的正是已经拿到的官方真值。
实测:连打 5 次?fresh=1,1 次没有分母,那一次全程 19.8 s(单次timeoutMs=15000,四次调用之间
还有minIntervalMs=1200串行节流),quota-config撞超时抛错,被
catch { /* 拿不到档位额度时仍报已用比例 */ }静默吞掉 —— 注释承诺的行为代码没做:没分母 →
顶层算不出usedPercent→ 前端hasNumbers()判定"这张卡没数字" → 卡片收起,而且这张退化卡还会被
TTL 缓存 10 分钟。现在:- 宿主半边把上游直给的比例作为顶层
usedPercent发出(不折算绝对剩余量),并给
extra.denominatorFailed;emptyReason只留给真的一无所有的情况; - 前端
hasNumbers()认这个数(不再当空卡);remainingPercentOf()认两个合法来源
(官方分母派生的remainingPercent,或上游直给的usedPercent),所以徽标照旧是
「余量% + 条」;绝对值仍然不编; summarizeText(模型工具摘要)同口径报比例而不是"无数据";"官方有数就收起实测兜底行"的
判据不动(仍要求官方卡有绝对数字)——把usedPercent也算进去会让摘要收起而面板仍显示,
两半边不一致比多印一行更糟(本轮实测发现并回退)。
- 宿主半边把上游直给的比例作为顶层
- 当前路由是包装层时,徽标并排冒出两个胶囊(实机:
Token Plan 94%+DeepSeek ¥33.80)。
模型切到modlens-qwen-token-plan-cn/deepseek-v4.1-flash之后,这个 provider id 既不在余量卡的
bindProviders(qwen-token-plan-cn)里也不在余额卡里,于是四处精确相等的比较全部落空,
走"匹配不到就全量显示"的兜底 → 两家并排。现在统一用一层按-边界的容忍匹配
(sameProvider:任一方是另一方的完整尾段),四处比较共用:徽标选卡、panelScope: current
的面板过滤、吞吐行归属、以及兜底卡的收起判据。边界是必须的 —— 否则qwen这类短名会把
qwen-token-plan-cn一起吞掉;真正无关的路由仍照旧退回全量(不是藏成 0×0)。 - 按本轮决定仍不做重试、不动缓存时长:退化态下一次缓存周期自然恢复,最坏 10 分钟。
npm: https://www.npmjs.com/package/dsh-token-plan-quota/v0.4.10
安装:dsh plugin add dsh-token-plan-quota
v0.4.9
[0.4.9] - 2026-09-23
Changed
- 发布流水线的
verify it landed窗口从 60 秒拉到 6 分钟,失败时还会把 registry 当下的
dist-tags/versions打出来:v0.4.8 那次npm publish其实已经成功(OIDC trusted
publishing + provenance,退出码 0,回执 24 files / 2.3 MB / shasum 齐全),但 runner 在
60 秒里连续 6 次Cache-Control: no-cache查询都拿到旧文档 —— npm 自己还在传播 —— 于是
verify 判红,连带把后面的 GitHub Release 跳过了。同一步骤在 0.4.6 那次也被骗过一回,
那次根因是 runner 的本地 npm cache 旧快照:症状一样、根因不一样,而
"绕开客户端缓存"并不等于"服务端已经是新的"。所以这次既拉长窗口(24×15s)又在失败分支里
直接打印真相,不再靠事后翻 workflow 注解考古。
Fixed
- 千问 Token Plan 余量整张卡没有数字(上游把账期改成按月了)。症状:徽标只剩"实测"卡,面板里
那张官方卡既不出百分比也不报余量,emptyReason写"网关 usage 里没有 per1Week/per5Hour 比例"。
用probe+ debug 核对真实回包后确认:同一个源的usage只剩per1MonthPercentage/
per1MonthResetTime,quota-config的档位额度字段也跟着从weekly改名成monthly(档位表
lite / standard / pro / essential 各回monthly+five_hour,另有addon_quota.extrabundle),
而buildConsoleCard只认周度与 5 小时两个名字 —— 一条计量都拼不出来。它不报错,因为网关
那次调用是成功的:这正是"认不出的键不报错、只是没作用"的形态。现在月度与周度两条都读,谁回
了读数谁当主计量,老套餐继续走weekly;主计量固定排在meters[0](前端只渲染
meters.slice(1),顺序错了就是徽标报一个窗口、明细报另一个)。addon_quota.extrabundle不并进
分母:官方没说明它与档位额度的关系,折进去就是估算(原则 I)。 probe对自动检测出来的源永远回unknown-source。零配置安装里源不在配置文件里,而是
computeStatus跑applyAutoDetect时现挂到那份 config 上的;路由每次从getConfig()拿一份
新配置,于是唯一一个"能看上游实际字段集"的入口,对最需要它的源(token-plan-console)恰好不可用
—— 这次诊断就是被它逼着去临时改配置开 debug 的。现在查不到会补跑一次检测再查(检测只做本地解析
与凭据探测,不打额度端点,同名去重、可重入),404 里的known列表也跟着变准。
npm: https://www.npmjs.com/package/dsh-token-plan-quota/v0.4.9
安装:dsh plugin add dsh-token-plan-quota
v0.4.6
[0.4.6] - 2026-09-08
Added
- 发布自动化:
.github/workflows/publish.yml接v*tag,在 CI 里核"tag ↔package.json↔
CHANGELOG 三处一致" → 跑全量npm run check→ 扫 tarball(有没有夹带截图中间产物、有没有漏掉
README 引用的图)→npm publish --access public→ 回读 registry 确认版本真的上了。任一步失败
都发不出包。鉴权两种都支持:配了 npmjs 的 Trusted Publishing 就走 OIDC(带 provenance、零 secret),
配了NPM_TOKEN就自动改走 token。 scripts/release.mjs(npm run release -- --patch):切版本、把[Unreleased]搬进
## [X] - 日期并补 compare 链接、提交、打 tag、跑全量自检,全绿才推main+ tag。
前置会拦四件事:工作区不干净、不在main、[Unreleased]是空的、版本号或 tag 已被占用。
先加--dry-run可以看计划而不写入任何东西。- 浏览器一键发版:
.github/workflows/release.yml接workflow_dispatch,Actions 页面点
Run workflow → 选patch/minor/major→ 跑。跑的还是上面那段release.mjs,逻辑不复制;
差别只在 runner 上 checkout 是 detached HEAD 且没配过 git 身份 —— 脚本看到GITHUB_ACTIONS=true
会补user.name/email并把推的分支写成HEAD:main。commit 里带[bot]署名,不冒名。 - tag 一落地自动开 GitHub Release:
publish.yml在npm publish+ 回读 registry 之后
追加两步 —— 用scripts/extract-changelog-section.mjs从 CHANGELOG 抠出这一版的正文(逐行
扫,不用\s*\n那种会连吞下一节的正则;同一套代码在release.mjs里也踩过的坑),落成
release-notes.md交给softprops/action-gh-release@v2的body_path。抽错节等于把公告
挂到别的版本下面,8 条回归锁进test/host.mjs。publish 失败(verify it landed红灯)时
公告也不会建,registry 与 GitHub 上不会一个说发了一个说没发。
Fixed
- 每点一次标题栏,明细窗口就永久大一点(实机反馈)。根因不在点击,在量到的和套回去的
不是同一个盒:拖拽/缩放存进localStorage的是getBoundingClientRect()的 border box,
而.tpq-panel没写box-sizing,style.width/height默认按 content box 解释 —— 于是每轮
加回 1px×2 边框。而标题栏的onPointerDown会先 detach 并存一次尺寸,所以"只是点了一下
额度明细标题"也会触发。实测连点六次382→384→386→388→390→392→394;.tpq-panel加
box-sizing:border-box后六次稳定在380×434。
双击标题栏复位保留不动(先试过删它,但那治不了这个 bug —— 单击同样会存尺寸;
真正的问题在盒模型,修好后功能完好,实测双击仍能清掉悬浮矩形回到锚定态)。
三条回归锁进test/client.mjs:.tpq-panel必须是 border-box、onDoubleClick: resetFloat
与resetFloat本体都必须在(均已反向验证会红)。 - OIDC 发布被 setup-node 的
registry-url挡在门外(首次真跑 publish.yml 时踩到)。
actions/setup-node@v4只要给了registry-url: https://registry.npmjs.org,就会往
~/.npmrc写一行_authToken=${NODE_AUTH_TOKEN};npm 客户端不展开${...},于是 OIDC
那条本应"没 token、走 runner 注入"的路被这把字面量假钥匙挡住,返回 "Access token expired
or revoked"(真实原因完全无关)。修法是 setup-node 只留node-version,token 路径下由
发布步骤自己printf ... >> ~/.npmrc,OIDC 路径保持.npmrc里没有_authToken键。
test/host.mjs三条新断言:publish.yml/release.yml不许再出现registry-url:、
token 分支必须显式写_authToken。 - publish 流水线不幂等,重跑永远发不出去(v0.4.6 首跑撞上:
npm publish真把包传上去了,
verify it landed却因为npm view读 runner 本地~/.npm/_cacache拿到旧快照,误报
60 秒查不到 → 整条 run 判失败 → 后面的 GitHub Release 步骤被跳过;下一次 tag 强推重跑,
npm publish又被 registry "You cannot publish over the previously published versions"
挡下来,公告就永远建不出来)。两处一起改:publish 把这条 E403 当"其实已经发过了"继续往下走;
verify 换成curl -fsSL -H 'Cache-Control: no-cache' registry.npmjs.org/<pkg>打 HTTP,
绕开 npm 本地缓存。两条各一条断言锁进test/host.mjs(371 → 373 项)。
npm: https://www.npmjs.com/package/dsh-token-plan-quota/v0.4.6
安装:dsh plugin add dsh-token-plan-quota