npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@young1lin/dsh-glm-quota

v0.1.4

Published

DSH plugin: Zhipu/GLM Coding Plan quota — host fetch/cache/backoff plus a compact sidebar status and on-demand quota popover

Downloads

334

Readme

glm-quota — DSH 外部插件:智谱 GLM Coding Plan 额度面板

在 DSH Web GUI 左下角(Settings 按钮上方)显示智谱 GLM Coding Plan 的额度: 多窗口用量圆环、五档配色、MCP 调用次数、逐窗口重置倒计时。 侧栏折叠时逐窗口显示纯额度:5h(及存在时的周额度 7d)凹槽圆环,环弧示用量、环内显示重置倒计时,不含 MCP 次数与套餐名(精确百分比在悬停 Tooltip)。不足一分钟时倒计时显示秒数。

界面预览

可交互演示:docs/preview.html —— 用浏览器直接打开即可。该页由 npm run preview 生成,内嵌真实的插件 CSS 与真实组件的 SSR 渲染,并叠加宿主的全局 corner-shape:superellipse(1.5), 覆盖详情浮层、五档状态胶囊、收起栏各档倒计时,右上角可切换明暗主题。页内 DSW 令牌值为近似值, 仅用于让颜色在裸页面上解析;真实配色以宿主主题为准(corner-shape 需 Chrome 139+)。 改动 client.js 后请重新生成,否则演示页会与实现脱节。

以下截图记录改版前布局。新版收起态将倒计时放入圆环中心,展开态的小环也采用统一的凹槽层次;行为约束见 quota-ring-spec.md。

侧栏展开 —— 常驻 42px 状态胶囊(GLM · 套餐名 + 汇总百分比 + 微型圆环),点击弹出详情浮层:

侧栏展开:状态胶囊与详情浮层

改版前侧栏折叠 —— 纯额度竖排:5h 与周额度(存在时)各一枚小圆环,环下各自的重置倒计时:

侧栏折叠:纯额度圆环与重置倒计时

这是一个纯外部插件:不修改 dsh 本体任何代码。 与宿主的全部接触点只有三处—— 本目录(源码)、一个目录 junction、用户配置层里的一行挂载(见安装)。

npm 包名 @young1lin/dsh-glm-quota(scoped 包,公开发布);源码安装走下方方式二, 无需构建步骤。


目录

<插件目录>\            (本仓库解压/克隆到哪都行,例:D:\dev\dsh-glm-quota)
├── host.js           宿主半场:拉取额度 API、节流、缓存、HTTP endpoint
├── client.js         浏览器半场:侧栏面板(factory 格式 bundle,无构建步骤)
├── package.json      包声明 + dsh.client 清单(浏览器半场的发现入口)
├── smoke.mjs         host 半场冒烟测试
├── client-smoke.mjs  client 半场冒烟测试
├── README.md         本文件
└── node_modules/@deepseek-ai/   peer 依赖 junction(schemastery、dsh-credentials)

插件形态:双半场(dual-face)外部包

dsh 的 Web 组合里,一个 UI 插件由两个半场组成,本包用无构建方式同时提供两者:

host 半场(host.js)

  • 挂载机制:~/.dsh/cordis.patch.yml 里一行 - insert: [{ id: dsh-glm-quota, name: '@young1lin/dsh-glm-quota', config: {...} }]。 loader 通过 profile 目录的 node_modules 向上查找解析包名(~/.dsh/profiles/node_modules/@young1lin/dsh-glm-quota junction 到本目录),直接 import 源码 —— 原生 ESM,无需编译。挂载行的 name 同时是浏览器侧 标识:client bundle 的 boot graph id 与 /plugins/<name>/client.js URL 都由它派生, client.js 里的 handoff id 必须与之一致。
  • 职责:持有唯一一份数据与限流状态。监听会话事件、按 provider 门控、节流后请求 智谱额度监控 API,把投影缓存在磁盘,并通过 GET /glm-quota/state 服务给浏览器。
  • 导出:name / inject: ['webServer'] / Config(schemastery 校验)/ apply(ctx, config)。 凭据通过 ctx.get('credentials') 在每次请求时现解析(credential seam,不在配置里写明文)。

client 半场(client.js)

  • 发现机制:package.json 里的 "dsh": { "client": { "platform": "web" } } 清单。 宿主的 dsh-client-modules 服务启动时扫描 loader 全部条目,发现此声明后把 ./client 导出加入浏览器 boot graph(window.__DSH_BOOT__),浏览器按 /plugins/@young1lin/dsh-glm-quota/client.js 拉取。
  • 格式:手写的模块系统 factory bundle —— 整个文件是一次 window.__ModuleLoader__.load({ id, factory }) 调用;React、Tooltip 等平台模块通过 注入的 require 从宿主模块表获取(跨包零值导入,全部走 cordis 服务与插槽)。 因此改面板无需任何构建步骤,保存即生效。
  • 职责:纯展示。注册进 sidebar.footer.action 插槽(ui-sidebar 在 Settings 上方 渲染的 footer action 列表),每 30s 轮询宿主 endpoint(页面不可见时暂停,回到前台立即补拉), 通过注册时 inject 的 hooks: { quota: source } 把快照源绑成组件的 useQuota 选择器。

重载模型(改哪类文件要做什么)

| 改动 | 生效方式 | |---|---| | cordis.patch.yml 的 config 值 | 热重载,立即 | | client.js(面板样式/文案/逻辑) | 宿主 HMR 链自动推送;至多 F5 一次 | | host.js(拉取/节流/映射逻辑) | 需重启 dsh web(生产启动无 --expose-internals,宿主模块不热载) |

重启后验证跑的是新版:响应头 x-glm-quota-rev: relevance-1(每次改 host.js 语义请同步 bump 源码里的 MODULE_REV 常量)。


安装

方式一:npm 安装(推荐)

包已发布到 npm(@young1lin/dsh-glm-quota),用 dsh 自带的插件管理命令一条完成安装与挂载:

# 前置:Node ≥ 22、dsh ≥ 0.1.1-rc.1(含 dsh plugin 命令)、pnpm 在 PATH 上(corepack enable 或 npm i -g pnpm)
dsh plugin --profile web add @young1lin/dsh-glm-quota
dsh web    # 重启宿主(host 半场是模块代码)

浏览器 F5 后,左下角 Settings 上方出现额度面板。dsh plugin 会把包装进 profile (~/.dsh/profiles/web)并把包内自带的挂载行(cordis.patch.yml)加入 profile 层—— 无需手工编辑任何 yml。默认配置即开即用;需要覆盖配置(z.ai 的 baseURL、自定义 watchProviders 等)时,在 ~/.dsh/cordis.patch.yml 写一个同 id 行提供 config 值即可 (键见配置参考):

# ~/.dsh/cordis.patch.yml —— 只覆盖需要改的键
- id: dsh-glm-quota
  name: '@young1lin/dsh-glm-quota'
  config:
    baseURL: 'https://api.z.ai'

升级 dsh plugin --profile web update @young1lin/dsh-glm-quota; 卸载 dsh plugin --profile web remove @young1lin/dsh-glm-quota。

方式二:源码 junction 安装(本仓库开发 / 无 npm 环境)

全新机器(或重装 dsh 后)从源码安装,按顺序执行。整个过程不修改 dsh 本体; 所有命令幂等,重复执行安全。

前置条件

  • Node ≥ 22、dsh 已安装(dsh --version 可用),且至少启动过一次 dsh web (保证 ~/.dsh/profiles/ 目录树存在)
  • 智谱 API key 已配置为 dsh credential(见下节"API key 的来源与配置")
  • 本插件源码目录在位:本仓库所在目录(解压/克隆到哪都行,下面用 $plugin 变量指向它)
# 前置检查(True = key 已配置;只查存在性,不显示值)
Test-Path "$env:USERPROFILE\.dsh\profiles"
Select-String "$env:USERPROFILE\.dsh\.credentials.yaml" -Pattern 'ZAI_CODING_CN_API_KEY' -Quiet

API key 的来源与配置

key 由用户自己提供:dsh 和本插件都不签发 key。 在智谱开放平台控制台 (bigmodel.cn,z.ai 账号同理)的 API Keys 页面创建/查看,就是你自己账号的那把 key。

本插件不单独要 key——它复用你 GLM 对话已经在用的那一把。 你的 ~/.dsh/settings.yaml 里 zai-coding-cn provider 声明了 apiKeyEnv: ZAI_CODING_CN_API_KEY;插件配置里的 keyRef 默认指向同一个引用名, 每次请求时通过 credential seam 现解析。一把 key、一处存储、换 key 一处生效 (换 key 后无需重启:插件监听 credentials/updated,自动清退避立即重拉)。

给 dsh 配这把 key 的三种方式(按凭据层优先级,高的覆盖低的):

| 方式 | 操作 | 适用 | |---|---|---| | GUI(推荐) | Web GUI → Settings → 模型 → 对应 provider 卡片粘贴 key。写入 ~/.dsh/.credentials.yaml,立即生效 | 日常使用 | | 手动编辑文件 | ~/.dsh/.credentials.yaml 加一行 ZAI_CODING_CN_API_KEY: <你的key>(外部编辑热生效) | 脚本化/无 GUI | | 环境变量 | 启动前 $env:ZAI_CODING_CN_API_KEY = '<你的key>'(优先级最高) | CI / 临时换号 |

# ~/.dsh/.credentials.yaml 手动方式的格式(纯 ref → 字符串映射,无其他字段;
# 值就是控制台复制来的完整 key,形如 32位十六进制.16位大小写字母数字)
ZAI_CODING_CN_API_KEY: <控制台复制的完整key>

已经能用 GLM 对话的机器(如本机)这步天然满足——key 早就在 credential 存储里了, 装插件时无需任何 key 操作。

第 1 步:一键安装脚本

整段复制进 PowerShell 执行:创建两个 junction、把挂载行追加进配置层 (任何一项已存在就跳过,不会重复添加)。

$plugin   = 'D:\dev\dsh-glm-quota'   # ← 改成本仓库在你机器上的实际路径
$profiles = "$env:USERPROFILE\.dsh\profiles"

# 1a. peer 依赖 junction:本包的 import 解析到 dsh 自己的包实例(单一 cordis/schemastery)
New-Item -ItemType Directory -Force "$plugin\node_modules\@deepseek-ai" | Out-Null
foreach ($p in 'schemastery', 'dsh-credentials') {
  $link = "$plugin\node_modules\@deepseek-ai\$p"
  if (-not (Test-Path $link)) {
    New-Item -ItemType Junction -Path $link -Value "$profiles\node_modules\@deepseek-ai\$p" | Out-Null
    Write-Host "peer junction created: $p"
  }
}

# 1b. 挂载 junction:loader 从 profile 的 node_modules 解析包名 '@young1lin/dsh-glm-quota'
New-Item -ItemType Directory -Force "$profiles\node_modules\@young1lin" | Out-Null
$mount = "$profiles\node_modules\@young1lin\dsh-glm-quota"
if (-not (Test-Path $mount)) {
  New-Item -ItemType Junction -Path $mount -Value $plugin | Out-Null
  Write-Host 'mount junction created'
}

# 1c. 挂载行:尚未包含 glm-quota 时追加到 ~/.dsh/cordis.patch.yml(UTF-8 无 BOM,原子语义)
$patch = "$env:USERPROFILE\.dsh\cordis.patch.yml"
if (-not (Select-String -Path $patch -Pattern 'id: dsh-glm-quota' -Quiet)) {
  $row = @"

# dsh-glm-quota: Zhipu GLM Coding Plan quota panel (sidebar foot, above Settings).
# All config keys are optional; full reference in the plugin README.
- insert:
    - id: dsh-glm-quota
      name: '@young1lin/dsh-glm-quota'
      config:
        keyRef: ZAI_CODING_CN_API_KEY
        baseURL: 'https://open.bigmodel.cn'
        watchProviders: [zai-coding-cn]
        minFetchIntervalMs: 60000
        rateLimitBackoffMs: 120000
"@
  [System.IO.File]::AppendAllText($patch, $row)
  Write-Host 'patch row appended'
} else {
  Write-Host 'patch row already present'
}

# 1d. 自检:host 半场能被 Node 从 profile 目录解析
Push-Location $profiles
node -e "import('@young1lin/dsh-glm-quota').then(m => console.log('host half OK:', m.name, '| inject:', m.inject.join(','))).catch(e => { console.error('FAIL:', e.message); process.exit(1) })"
Pop-Location

最后一行输出 host half OK: @young1lin/dsh-glm-quota | inject: webServer 即本步成功。 config 键的含义与默认值见下方配置参考,全部可省略。

第 2 步:重启 dsh web 并验证

host 半场是模块代码,首次安装需重启一次宿主;浏览器随后 F5 读入新 boot graph 行。

# 重启宿主(先停掉现有 dsh web 进程,再启动)
dsh web

# 三项验证(都应成立):
(Invoke-WebRequest http://127.0.0.1:3080/glm-quota/state).StatusCode              # 200
(Invoke-WebRequest http://127.0.0.1:3080/glm-quota/state).Headers['x-glm-quota-rev']  # relevance-1
Get-Content "$env:USERPROFILE\.agents\glm-quota\state.json"                    # 投影 + lastAttemptAt

浏览器 F5 后,左下角 Settings 上方出现额度面板即安装完成。

dsh 升级 / 重装后恢复

升级会重建 ~/.dsh/profiles/node_modules(junction 消失);源码目录与配置层不受影响。 恢复 = 重跑第 1 步脚本(幂等)→ 重启 dsh web → F5。同样的手法参考同机的 dsh-vision-router 插件。

分发给他人(接收方注意事项)

已发布 npm:接收方 dsh plugin --profile web add @young1lin/dsh-glm-quota 一条命令即装(方式一)。 离线场景用 zip(git archive 导出)走源码 junction 安装(方式二)——包不含任何 key 与机器特定信息,可直接分发。两种方式都有两个值必须对照自己的 ~/.dsh/settings.yaml 核对——插件的默认值按 zai-coding-cn 这套常见配置写死, 不匹配则额度刷新不触发:

| 核对项 | 在哪看 | 不匹配时改哪 | |---|---|---| | provider id | settings.yaml 的 agent-default-model.provider(或 llm-pi-ai.providers 下的键名) | 挂载行 watchProviders: [<你的provider id>] | | key 引用名 | 同 provider 声明的 apiKeyEnv | 挂载行 keyRef: <你的apiKeyEnv> |

其余差异:z.ai 账号(非 bigmodel.cn)把 baseURL 改为 https://api.z.ai; 周限窗口是否显示由计划决定(pro/lite 有、Max 当前无),见"实现原理 → client 半场核心机制"。

卸载

# 1. 删配置:编辑 ~/.dsh/cordis.patch.yml,删除 dsh-glm-quota 的整个 "- insert:" 块
#    (从 "# dsh-glm-quota:" 注释行到 "rateLimitBackoffMs" 行)。热生效:
#    路由、事件监听、定时器随 fiber 销毁,无需重启。
# 2. 删 junction(源码目录保留,可随时重装):
Remove-Item "$env:USERPROFILE\.dsh\profiles\node_modules\@young1lin\dsh-glm-quota"
Remove-Item "$plugin\node_modules" -Recurse -Force
# 3. 可选:删磁盘缓存与源码目录
Remove-Item "$env:USERPROFILE\.agents\glm-quota" -Recurse -Force
# Remove-Item $plugin -Recurse -Force

实现原理

数据流总览

智谱会话事件(session/event)                     空闲/非智谱会话
      │ 所有事件"想"刷新                              │
      ▼                                              ▼
provider 门控(按 session 跟踪) ──── 非智谱 → 忽略(零请求) ──── 定时器纯内存检查,不发请求
      │ 智谱且 turn 进行中
      ▼
单一节流阀 ── 窗口内? ── 是 → 吞掉(单飞合并)
      │ 窗口外(默认 ≥60s)
      ▼
lastAttemptAt 先落盘(~/.agents/glm-quota/state.json) ──► 请求 {baseURL}/api/monitor/usage/quota/limit
      │                                                (token 裸传,无 Bearer;必须带
      ▼                                                 Accept-Language + Content-Type)
投影写入 state.json + 内存 ──► GET /glm-quota/state ──► client 轮询(30s) ──► 面板渲染

host 半场核心机制

provider 识别(按 session 独立跟踪,多会话并行互不干扰)

  • request/header 事件 → header.config.provider(会话首个请求及每次配置变更记录)
  • assistant/message 事件 → message.source.provider(每个完成的步骤都带)
  • 其余事件(assistant/chunk、tool/call…)复用该 session 已跟踪的路由
  • turn/start/turn/end 维护"该会话是否有回合在跑"(Map,session/disposed 清理)
  • 智谱的 Anthropic 兼容与 OpenAI 兼容两个 baseURL 端点共用 provider id zai-coding-cn,所以 watchProviders 一条即覆盖两者

模型清单在 dsh 侧配置,插件不感知具体模型

glm-5.3、glm-5.3-flash 等模型声明在 dsh 自己的 ~/.dsh/settings.yaml 里 zai-coding-cn provider 的 models 列表下(id / name / contextWindow / maxTokens / reasoningEfforts / compat 等字段)。要上新模型,只需在那里追加一条:

# ~/.dsh/settings.yaml → zai-coding-cn.models 追加(示意,字段可按需精简)
- id: glm-5.3-flash
  name: glm-5.3-flash
  contextWindow: 1000000
  maxTokens: 128000
  compat:
    thinkingFormat: zai
    supportsReasoningEffort: true

本插件只按 provider id(zai-coding-cn)门控刷新:models 列表怎么增删、一回合用 glm-5.3 还是 glm-5.3-flash,面板都无需任何改动——套餐内所有模型共用同一组 5h / 周 / MCP 窗口。

节流算法(所有触发汇入同一阀)

fetchUpstream(force) 的决策(单飞:并发触发共享一次执行):
  1. 重读 state.json —— 文件是跨进程权威(重启/并行 dsh 同 key 都被管住)
  2. retryAt > now ?                是 → 放弃(退避中)
  3. !force && now - lastAttemptAt < minFetchIntervalMs ?  是 → 放弃(窗口内吞掉)
  4. lastAttemptAt = now 并立刻落盘 —— 先写文件再发请求
  5. 发请求;成功 → 更新投影+落盘;失败 → 按类别设 retryAt

触发源三处:会话事件(活跃智谱会话的每个事件)、步进定时器(同 minFetchIntervalMs 间隔,兜底长工具调用间隙;无活跃智谱会话时是纯内存检查零请求)、手动 ?refresh=1(唯一可越过节流的入口,显式人为操作)。

失败退避

  • HTTP 429:max(Retry-After 头, rateLimitBackoffMs);退避期间继续服务上次成功数据
  • 其他失败/超时(4s 超时上限):errorBackoffMs
  • 凭据缺失:不退避(配置问题而非 API 失败,修好后下一触发立即生效)
  • credentials/updated 事件:清空退避与间隔记忆,立即重拉

窗口映射(与参考实现 quota_glm.go 一致)

| API (type, unit, number) | 窗口 | |---|---| | TOKENS_LIMIT, 3, 5 | 5 小时滚动窗口(5h) | | TOKENS_LIMIT, 6, 1 | 周额度(7d)——计划没有就不返回该行,面板自动不渲染 | | TIME_LIMIT | MCP 次数上限;cap 在 usage 字段、已用在 currentValue(沿用 Go 的字段复用映射) | | 其他 TOKENS_LIMIT / 其他 type | 通用扩展窗口(Tok(uX,nY) / 小写 type) |

状态文件(~/.agents/glm-quota/state.json,原子写:tmp + rename)

{
  "planLevel": "Max",
  "windows": [
    { "id": "5h", "label": "5h", "percent": 20, "resetAt": 1787117962367 },
    { "id": "mcp", "label": "MCP", "percent": 1, "used": 15, "limit": 4000, "resetAt": 1789522977998 }
  ],
  "fetchedAt": 1787115844621,
  "retryAt": 0,
  "error": "",
  "lastAttemptAt": 1787115844321
}

GET 时若磁盘文件比内存新则采纳 —— 并行的 headless dsh 刷新过额度时,Web 面板能读到。 无任何 secret 落盘。

HTTP API

  • GET /glm-quota/state → 上述投影 JSON(响应头含 x-glm-quota-rev 版本标记)
  • GET /glm-quota/state?refresh=1 → 强制越过节流刷新一次后返回
  • 路由注册在宿主 webServer(ctx.effect 包裹,fiber 销毁即摘除)

client 半场核心机制

  • 插槽注册:ctx.slots.inject('sidebar.footer.action', () => ctx.slots.register({...}, QuotaPanel)) —— ui-sidebar 声明的 footer action 列表插槽,渲染于 Settings 上方;owner 传 wide (侧栏展开/折叠),inject 提供 refresh 回调与 hooks.quota 快照源
  • 数据通道:hooks compartment 是设计上唯一合法的"注册方私有可变事实"通道, 渲染层把它绑成 useQuota 选择器 hook(uSES),组件零订阅样板
  • 渲染:
    • 侧栏常驻部分固定为 42px 状态胶囊:GLM · 套餐名 + 汇总百分比 + 微型 圆环。汇总只取令牌额度窗口(5h、7d 等)的最大值——语义是"离最近的令牌 天花板还有多远":MCP 是次数帽子不参与(MCP 95% + 5h 1% 时显示 1%),两个 令牌窗口不一致时更接近上限的一方领先(7d 10% + 5h 5% → 10%);仅剩 MCP 数据时回退显示 MCP。它不再把完整额度卡长期塞在工作区列表下方,点击后才在 胶囊上方打开临时详情浮层,点外部或按 Esc 自动关闭
    • 详情中每个窗口是一条紧凑的圆环指标:标签、精确百分比(MCP 为 已用 / 上限)、自身的重置倒计时。绝对重置时间仍在原生 Tooltip 中;倒计时 常态每 30s 更新,任一窗口进入最后一分钟后每秒更新。窗口按 API 返回条件渲染:周额度(7d)只有存在时才出现 —— V1/Max 当前只显示 5 小时与 MCP,V2 及以上返回 7d 时自动多一行,无需改代码
    • 五档配色(沿用 quotaPercentColor):<20% 亮绿、<40% 绿、<60% 青、<80% 黄、≥80% 红; 暗色主题(body[data-ds-dark-theme])自动换色值;主题 token 优先 (--dsw-static-green-400 等),主题没有的档位用固定色
    • 浮层头部保留手动刷新(请求在途时旋转)与陈旧数据提示;浮层使用不透明主题菜单 表面、圆角与系统阴影,不遮透工作区文字
    • 与 DSH 原生拖拽联动:原生会话树是唯一滚动容器,拖拽进入 footer 胶囊时插件会 继续推进该树的 scrollTop,因此拖动会话到侧栏底部不会被额度入口截断自动滚动
    • 收起侧栏为纯额度竖排:每个令牌额度窗口(5h,及存在时的 7d)一枚 46px 凹槽圆环 —— 环弧即已用占比、环色取五档档位,圆环中心显示该窗口的重置倒计时; 低于 1 分钟显示秒数。不渲染百分比、MCP 与套餐名,悬停 Tooltip 给出精确百分比 与绝对重置时间,点击刷新。展开卡片的 28px 指标环和 22px 状态环沿用同一凹槽样式。 周额度同样条件渲染(无 7d 窗口的套餐只显示 5h 一枚);完全没有额度窗口时 回退为旧的单圆环占位
    • 空闲静默是设计行为:所有会话不在跑时面板冻结(数据不旧于最后一次会话内刷新)
    • prefers-reduced-motion 下关闭所有动画

配置参考

配置在 ~/.dsh/cordis.patch.yml 的挂载行 config 下,全部键可省略:

| 键 | 默认 | 说明 | |---|---|---| | keyRef | ZAI_CODING_CN_API_KEY | credential 引用名(值存于 ~/.dsh/.credentials.yaml,每次请求现解析) | | watchProviders | [zai-coding-cn] | 哪些 provider id 的会话驱动刷新;两个协议端点共用一个 id | | baseURL | https://open.bigmodel.cn | 额度监控 API 基址;z.ai 账号填 https://api.z.ai | | minFetchIntervalMs | 60000 | 上游请求最小间隔 —— 限流核心,同时也是步进定时器间隔(下限 500) | | errorBackoffMs | 60000 | 普通失败退避 | | rateLimitBackoffMs | 120000 | 429 退避下限(与 Retry-After 取大) | | stateFile | ~/.agents/glm-quota/state.json | 投影 + 限流权威文件路径 |

行为速查

自动隐藏(relevant 门控):宿主响应携带 relevant 标志——仅当"见过 watched provider 的会话"且"credential 可解析"都成立才为 true。不成立时(这台机器从不用 智谱、或 key 未配置)面板从侧栏完全消失而非显示错误占位。所以插件可装在任意 机器上:不用智谱的机器零感知。

| 场景 | 上游请求 | |---|---| | 智谱会话在跑(任意事件流) | 规整为每 minFetchIntervalMs(默认 1 分钟)最多 1 次 | | 所有会话空闲 / 只有非智谱会话在跑 | 0 次(定时器纯内存检查) | | 宿主启动 | 1 次首拉(若文件记录的最近请求仍在窗口内则被压制) | | 手动 ↻ / ?refresh=1 | 立即 1 次(可越过节流;仍受退避约束) | | 429 / 失败退避中 | 0 次,面板显示上次成功数据 + 陈旧标记 | | 从未见过 watched 会话 / key 未配置 | 面板自动隐藏(relevant=false,零上游请求) |

开发与调试

node smoke.mjs         # host 半场:窗口映射/provider 门控/事件合并/定时器兜底/
                       #   turn-end 吞掉/跨实例文件节流/空闲零请求/429 退避/状态文件
node client-smoke.mjs  # client 半场:handoff 格式/插槽注册/hooks 绑定/轮询/SSR 渲染断言
npm run preview        # 重新生成 docs/preview.html(改完 client.js 跑一次)

preview.mjs 与 docs/preview.html 只用于仓库内演示,不随 npm 包发布—— package.json 的 files 是白名单,两者都不在其中。

改代码:config 值热生效;client.js 保存即推送(至多 F5);host.js 重启 dsh web 并 bump MODULE_REV。

面板不显示排查:① curl /glm-quota/state 是否 200 JSON(否 → patch 行/junction/重启) ② 响应头有无 x-glm-quota-rev(无 → 旧模块仍在跑,重启)③ F5 后浏览器控制台有无 dsh-glm-quota 加载错误 ④ ~/.agents/glm-quota/state.json 的 error 字段(凭据未配置/ API 失败会在面板显示"额度不可用")。

来源致谢

窗口映射((type, unit, number) 分发)、五档配色阈值、API 请求头要求与 429 处理, 移植自开源状态栏项目 claude-token-monitor 的 quota_glm.go(智谱额度监控部分)。