dsh-plugin-ecustcic-quota
Verifieddsh-plugin-ecustcic-quota · v0.2.3 · MIT · Web UI
在 DSH 侧边栏显示 ecustcic(ai.ecustcic.com)的周期配额:每个受限维度一行进度条,数字与重置倒计时放在悬停提示里;密钥自动复用已指向该站点的模型供应商,无需额外配置。
Install
dsh plugin add dsh-plugin-ecustcic-quota Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Published to npm without a public repository. Inspect the package contents before installing.
Tags
Creators
Readme
description: 在 DSH 侧边栏显示 ecustcic(ai.ecustcic.com)的周期配额剩余量 kind: plugin
dsh-plugin-ecustcic-quota
在 DSH 侧边栏里常驻一张小卡片,显示 ecustcic(ModelGate)账户的周期配额剩余量:每个受限维度一行(「每小时 · 请求」+ 进度条),数字与倒计时放在悬停提示里。
不需要配置 API Key——直接复用 DSH 里已指向 ai.ecustcic.com 的模型供应商的密钥。
安装
本插件同时发布到 npm 官方源和 CNB 制品库,两个源内容一致。装进任意 DSH 应用有两种方式。
方式一:插件市场(推荐)
打开 DSH,在侧边栏的面板列表里点「插件」(和「会话」「文件」并列的那一行)。
点「添加插件」→「安装第三方插件」。
「包名或地址」填:
dsh-plugin-ecustcic-quota「安装源」:
默认安装源即可(包已发布到 npm 官方源);
国内网络慢或连不上时,改选「自定义地址」,填:
https://npm.cnb.cool/ecustcic/dsh-ecustcic-quota/-/packages/
点「安装」,装完按提示重启 DSH。
两点说明:
- 两个源都公开只读,不需要登录、不需要令牌。
- 安装界面只填包名和源地址;不要在这里填
sk-gw-...之类的密钥——密钥由插件自动复用(见下方「配置凭据」)。
装不上时先核对包名和源地址是否和上面完全一致。若你的 DSH 版本面板列表里没有 「插件」,用下面的手动方式。
方式二:手动装进 profile
插件必须挂在 DSH profile 的 bundle 列表里。以 desktop profile 为例,编辑
~/.dsh/profiles/desktop/package.json:
{
"dsh": {
"profile": {
"bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "dsh-plugin-ecustcic-quota"]
}
},
"dependencies": {
"dsh-plugin-ecustcic-quota": "^0.2.3"
}
}
用 npm 官方源不需要额外配置;想走 CNB 源,在 profile 目录加一行 .npmrc:
registry=https://npm.cnb.cool/ecustcic/dsh-ecustcic-quota/-/packages/
然后在该目录执行 pnpm install,并重启 DSH(宿主半是 Node 侧代码,改完要重启,光刷新页面不够)。
本地开发时依赖写成 "file:D:\\path\\to\\dshplugin-ecustcic-quota" 或 link: 更顺手;改客户端代码只需重新 pnpm run build 再刷新页面,改宿主代码仍要重启。
如果重启后 DSH 起不来:把
dsh-plugin-ecustcic-quota从dsh.profile.bundles里删掉即可恢复。DSH 对启动期的 unhandled rejection 是 fail-loud 的(直接process.exit(1)),所以插件有启动缺陷会表现为「应用崩溃」 而不是「插件不显示」。本仓库的pnpm test里包含boot-check,会用真实的 cordis Context 和真实补丁完整走一遍启动路径,正是为了防止这种情况。
功能
- 一维一行:卡面按上游返回的周期档逐条展示,只列出真的设了上限的维度;不限的维度不占位置。
- 悬停看数字:每行只有标签和进度条,已用/上限、剩余量、重置倒计时、百分比都在 tooltip 里。
- 最紧张维度高亮:卡片标题和加粗的那一行都指向「最接近耗尽」的那个维度(同比例时取更短的窗口),而不是上游的计费档。
- 失败不白屏:一次刷新失败会保留上一次的数据并标注「(旧数据)」,错误卡片会直接告诉你该改什么(哪台主机、实际发现了哪些 provider)。
- API Key 不进浏览器:凭据只在宿主侧读取,浏览器访问的是同源路由。
配置凭据
插件不保存 API Key,也不需要你额外配置:它直接复用 DSH 里已经指向 baseUrl 的那个模型供应商的密钥。
DSH 的 profile 里本来就有一条通往 ModelGate 的配置:
- id: llm-pi-ai
config:
providers:
cic:
apiKeyEnv: CIC_API_KEY # 凭据名字,不是密钥本身
baseURL: https://ai.ecustcic.com/api/v1
宿主侧启动时向 cordis loader 读合并后的 provider 表(bundle 层 + 你写的 patch 层都已合并),按主机名 ai.ecustcic.com 找到 cic 这一条,取它的 apiKeyEnv(CIC_API_KEY),再从 DSH 凭据库现取密钥。
所以只要 DSH 里能正常用 cic 模型,卡片就能读配额,不需要任何额外步骤。轮换 Key 同样不需要改配置、不需要重启。
密钥在 ~/.dsh/.credentials.yaml 的 refs 段里:
refs:
CIC_API_KEY: sk-gw-your-key-here
匹配只按主机名,不比对路径:provider 指向 …/api/v1(模型接口),而配额接口在 …/api/user/quota,两者路径不同是设计如此。
找不到主机匹配的 provider 时,卡片会明说找不到,并列出它实际看到的 provider,例如「bestzyq (newapi.bestzyq.cn), cic (ai.ecustcic.com)」,而不是丢一句「未配置」。
配置项
写在 cordis.patch.yml(或任意 patch 层)里,也可以被 Settings 表单覆盖:
| 配置项 | 默认值 | 说明 |
|---|---|---|
baseUrl |
https://ai.ecustcic.com |
上游地址,路径会被忽略(只取 origin) |
apiKeyEnv |
空(自动发现) | 覆盖用。留空即复用指向 baseUrl 的 provider 的凭据;填了就只用它 |
refreshIntervalMs |
60000 |
刷新间隔,范围 5000–3600000 |
requestTimeoutMs |
15000 |
单次上游超时,范围 1000–120000 |
showInSidebar |
true |
是否显示在侧边栏 |
startCollapsed |
false |
是否默认收起卡面(只显示标题行) |
apiKeyEnv 留空或填合法的标识符(/^[A-Za-z_][A-Za-z0-9_]*$/)——直接粘 sk-gw-... 进来会被 schema 拒绝,因为那是密钥而不是名字。
工作原理
侧边栏卡片 ──GET /api/ecustcic-quota──▶ 宿主插件 ──GET /api/user/quota──▶ ai.ecustcic.com
(浏览器) (Node 侧) Authorization: Bearer sk-gw-…
- 宿主侧(
lib/index.js)按间隔拉/api/user/quota,把上游的 snake_case 字段整理成浏览器直接能画的形状(periods[].requests.ratio之类),并把结果缓存起来。 - 同源路由
/api/ecustcic-quota返回这份快照。它受connection.requestRejection把守,只有受信任的、已登录的浏览器会话能读,所以它不会变成一个谁都能用的转发口。 - 浏览器侧(
lib/client.js)只认这个路由,拿不到也存不了 Key。
并发轮询会合并成一次上游请求;POST(或 ?refresh=1)可以强制绕过缓存立即刷新。
上游几处反直觉的地方都做了处理:
quota_*为null表示不限量,不是 0。这类维度整行不渲染,不会被误读成「剩余 0」。rate里的-1同样表示不限;三项全不限时速率块整个不出现。- 上游没给
period_label时按period_seconds补一个(3600 → 每小时,86400 → 每日,604800 → 每周,2592000 → 每月)。 total常常只有used_*没有quota_*,没有任何上限可算比例,因此不做展示。
数据从哪来
主数据源是 GET /api/user/quota,它一次返回账户级总量(total)、各周期档(periods)和速率限制(rate)。卡片用其中的 periods(每个受限维度一行)和 rate(有上限时才显示);total 只有已用量、没有上限可算比例,因此不展示。
/api/user/model-quotas(按模型拆分)和 /api/user/redeem(兑换码)没有被使用——周期配额卡片用不上它们。
开发
pnpm run build # 编译宿主半 + 打包浏览器半
pnpm test # 单元测试 + 渲染冒烟测试 + 加载器契约校验
pnpm run smoke # 只跑渲染冒烟测试
pnpm run verify # 只跑加载器契约校验
pnpm run preview # 把卡片渲染成 preview.html,改样式时先看这里
打包与转译全部零依赖,不需要 esbuild / babel / typescript:
scripts/build-host.mjs:宿主半是普通 ESM,直接复制并把相对导入补成显式.js。scripts/build.mjs:浏览器半打成window.__ModuleLoader__.load({id, factory})单文件,外部依赖只允许用 shell 的共享模块表(react、@deepseek-ai/dsh-client-*)。写盘前会先解析一遍产物,所以模板字符串写坏之类的问题当场构建失败,而不是等重启才发现。scripts/jsx.mjs:一个就够用的 JSX →jsx()转译器,认字符串、模板串、注释和嵌套标签。模板字符串里的 JSX 不支持,需要的话先赋给变量。scripts/smoke.mjs:假造一个 shell 装载 bundle,用 React 的 server renderer 把每种状态都画一遍,断言关键文案在、且没有undefined/NaN。
测试全部离线跑,不依赖线上 ModelGate。
发布新版本(两个源都发,保持版本一致):
npm publish --registry=https://registry.npmjs.org/ # npm 官方源
npm publish --registry=https://npm.cnb.cool/ecustcic/dsh-ecustcic-quota/-/packages/ # CNB 制品库
publishConfig 里没有锁 registry,所以发哪个源由命令行的 --registry 决定。
已知限制
- 只读,不提供额度充值或兑换。
- 不做按模型的细分配额。
- 倒计时精度是 30 秒一跳,够看但不做秒级。
License
MIT