Skip to content

Releases

v0.4.13

Choose a tag to compare

@github-actions github-actions released this 02 Nov 09:59

[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

Choose a tag to compare

@github-actions github-actions released this 02 Nov 03:04

[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_flag

    • login_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

Choose a tag to compare

@github-actions github-actions released this 29 Sep 17:34

[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 条。
  • 发布流水线三处静默失手:

    • 用 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

Choose a tag to compare

@github-actions github-actions released this 25 Sep 08:08

[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

Choose a tag to compare

@github-actions github-actions released this 23 Sep 01:42

[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

Choose a tag to compare

@github-actions github-actions released this 08 Sep 07:40

[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