dsh-glm-quota
View GLM Coding Plan quota (5-hour window / weekly window / monthly MCP calls) inside DeepSeek Harness — as a glm_quota tool, as a strip at the sidebar foot, as a left-sidebar panel, as a settings page, and as a card on the plugin's own page.
Reference implementation follows the structure of dsh-codex-subscription (manifest + cordis patch insert + host plugin exporting apply/Config + a hand-written client bundle), with zero framework imports:
- the
glm_quotatool is built as a plain literal object (same shapedefineToolproduces), - the API key is resolved through DSH's
credentialsservice (config literal → credential ref → env vars), - HTTP uses global
fetch, - the UI is a hand-written
window.__ModuleLoader__bundle registeringsidebar.footer.action,sidebar.panellist,main,settings.section, andplugins.bundle.config, - data reaches the UI over a host route (
connection.fetch.register) atPOST /api/glm-quota/query. @deepseek-ai/schemastery+@deepseek-ai/cosmokitare ordinarydependencies, installed from the public registry with the package — nothing is vendored into the repository.
Sidebar entry
A sidebar icon only exists if the bundle registers the pair itself:
| Slot | Key | What it draws |
|---|---|---|
sidebar.footer.action |
id = package name |
the quota strip beside the sidebar's Settings button (order 2 here) |
sidebar.panellist |
id = package name |
one row in the panel list (order 20 here) |
main |
key = package name |
the central column for that panel id |
The row glyph is an inline SVG gauge using currentColor, sized by the size prop the sidebar passes. The page reuses the card component with variant: 'page', so the four UI entry points cannot drift apart.
The footer slot hands each action only { wide }. Wide shows a title row and one bar per window, each with its remaining percentage and a countdown to the window's reset (1h 32m后重置); the 56px rail shows a single GLM 74% label for whichever window is closest to running out. Because the footer is mounted all the time, the strip polls every 5 minutes rather than refreshing only when opened, and clicking it calls ctx.layout.selectPanel('dsh-glm-quota').
The strip's reset label is a countdown, not a timestamp: the sidebar is too narrow for a locale-formatted date, and "how long until it recovers" is the figure worth watching. See formatResetIn in lib/client.js.
Credentials are asynchronous
Credentials.resolve/describe return promises of record objects, never bare strings:
const record = await credentials.resolve('GLM_API_KEY') // => { value, source } | undefined
Reading it without await silently yields undefined, which is what made "API key saved." appear next to "No API key configured." in 0.2.0. The card also now confirms the write by re-reading status before reporting success, so a failed read-back surfaces as an error instead of a false positive.
Install
DSH installs plugins from a market index, an npm package, a git repository, or a local directory/tarball.
From this repository:
# DSH: plugin_manager install_bundle with the absolute package directory,
# or from a packed tarball produced by `npm pack`.
The bundle declares dsh.bundle.patch (which inserts the glm-quota entry) and
engines.dsh (>=0.2.2-rc.1 <0.3.0-0), so the harness can check compatibility
before activating it.
After installing, the plugin appears in four places:
| Where | What |
|---|---|
| Sidebar foot | the quota strip (wide) or the GLM n% label (56px rail) |
| Sidebar panel list | the gauge icon, opening the full quota page |
| Settings | a GLM Quota section |
| Plugins → GLM Quota | the plugin page card |
Upstream
GET {base}/api/monitor/usage/quota/limit, header Authorization: <raw token>:
cn→https://open.bigmodel.cnglobal→https://api.z.ai
Response: { code, msg, success, data: { level, limits: [...] } }.
limits carries the two usage windows in one of two shapes, and both are handled:
| Shape | 5-hour window | Weekly window |
|---|---|---|
TOKENS_LIMIT × 2 |
first after sorting by nextResetTime |
second |
CREDIT_LIMIT × 2 |
unit: 3 |
unit: 6 |
lite plans answer with CREDIT_LIMIT, which is why a parser that only knew TOKENS_LIMIT reported an empty quota. A TIME_LIMIT row is the monthly MCP allowance (usage total, currentValue used, remaining) and applies under either shape. nextResetTime is epoch milliseconds upstream and ISO in the plugin's output.
Remaining, not consumed
Upstream's percentage is the share already used — 100 means the window is exhausted. Every surface here reports the opposite: remainingPercentage, where 100 is a full allowance and 0 is nothing left. The conversion happens once, in normalizeQuota/makeWindow (lib/index.js), so no renderer has to remember the direction. Bar tones follow the same sense: a nearly empty bar is the warning, not a nearly full one.
Layout
lib/index.js host plugin (tool + config + /api/glm-quota/query route)
lib/client.js client bundle (sidebar strip + panel + settings page + plugins-page card)
cordis.patch.yml bundle patch (insert id: glm-quota)
locale/*.json plugin meta (en/zh)
docs/DETAIL*.md plugin detail page text
icon-glm-quota.svg plugin icon
License
MIT
No comments yet. Be the first to write one.