dws-quota
deepwerkspace/dws-quota
Tokens remaining widget
설치
dsh plugin --profile web add github:deepwerkspace/dws-quotaREADME
dws-quota
A tiny DSH plugin that shows the Deepwerkspace estimated-remaining-token count
in the web client's composer dock, right next to the context meter (the ring
that shows % of context used).
The Deepwerkspace quota endpoint reports the remaining output token limit.
Because uncached input tokens are charged against output tokens at 1/10th, a
~50/50 input/output split means one output token "buys" about 5.5 total
tokens (1 output + 10 input). So the estimated remaining total token budget
is 5.5 × remaining_tokens. If real traffic skews more input-heavy than 50/50,
the estimate is conservative (low) — the safe direction.
The pill reads e.g. ≈ 11M est. for a 1,995,521-token output quota
(5.5 × 1,995,521 = 10,975,366 ≈ 11M). Hover for the raw output-token count.
The pill also holds an always-visible Topup link (visible even before the
first successful poll, or if the quota is unreadable) that opens the
Deepwerkspace top-up page in a new tab. It points at a stable, generic redirect
(https://deepwerkspace.com/products/get-more-tokens) so the real product URL
can change without a plugin update.
Files
| File | Role |
|---|---|
package.json | Package manifest + dsh.bundle (manager-installable) + dsh.client declaration (browser half opt-in). |
cordis.patch.yml | The bundle's own patch layer — what the plugin manager loads when the bundle is selected. |
index.js | Host half. Reads the key from the credentials store, queries the quota API, serves GET /dws-quota. |
client.js | Browser half. Lazy-CJS bundle; renders a single pill (budget text + always-visible "Topup" link) in conversation.composer.dock. |
.test-harness.js | Mock test for the Host half (run node .test-harness.js). |
.test-client.js | Mock test for the client bundle (run node .test-client.js). |
Design / safety
- The key never reaches the browser. The Host reads it from the credentials
store and does the API call; only the computed JSON (
remaining_tokens,estimated) crosses the wire to the client. - No hard dependencies. The Host half reads every service through
ctx.get()(notinject), so a missingwebServer/connection/credentialsdegrades the plugin to a no-op instead of failing fiber activation. This is the key reason it cannot break boot the way a malformedinjectlist can. - Reversible mounting. It mounts as a bundle: the plugin manager adds the
package to the profile's
dsh.profile.bundlesand loads the package's owncordis.patch.ymllayer. Removing the bundle (and the package) fully reverts it — the bundlecordis.ymlis never edited. - Auth fence. When a
connectionservice is mounted, the route appliesconnection.requestRejection()(Host/Origin + browser-session check) before serving. On a loopback-onlydsh webthis is belt-and-suspenders. - Caching. The Host caches the quota for 30s so a burst of client polls does not hammer the quota API. The client re-polls every 60s.
Installing (in any DSH instance)
Prerequisite — the key in the credentials store
Append the Deepwerkspace key to the refs: block of ~/.dsh/.credentials.yaml
(the local credentials provider reads that file):
version: 1
refs:
# ...your existing refs...
DEEPWERKSPACE_API_KEY: dws-tRVAvvmTtQVlbw1OkKeayyMmAbml5dTT
The ref name must be exactly DEEPWERKSPACE_API_KEY (that is what the plugin
resolves). The value is the bearer token. If you add the key through the
Models settings page instead, name the custom provider's route id
deepwerkspace (lowercase, no separators) — the UI then stores the key under
DEEPWERKSPACE_API_KEY automatically.
The one-command install
The package declares dsh.bundle, so the DSH plugin manager installs it
end-to-end — it runs pnpm add in the profile directory, selects the bundle
(appends it to dsh.profile.bundles), loads this package's cordis.patch.yml
layer, and HMR applies it live on a patchReload: live profile:
dsh plugin --profile web add dws-quota
(Adjust web to whichever profile you actually run. The plugin works in any
profile that mounts a webServer, credentials, and the web client — i.e. the
web profile.)
The same operation is available in the GUI sidebar's Plugins page and the
plugin_manager agent tool (Creator mode). For a package that is not on the
default npm registry, pass a tarball URL or an absolute path instead of a
registry name.
Manual fallback (no plugin manager)
If you must mount it by hand: place this dws-quota/ directory in the
profile's node_modules (cp -r or ln -s), then append to the profile's
cordis.patch.yml:
- insert:
- name: 'dws-quota'
That is the entire registration — no inject (the plugin has no hard deps) and
no config. Then restart dsh web (or let HMR pick it up).
Open the GUI, start a session, and look at the composer dock (bottom of the
input card, next to the context ring) for the ≈ 11M est. pill.
Verify
# The Host route (loopback; needs the browser session cookie in a real browser,
# but on a bare loopback dsh web the connection fence is the only gate):
curl -s http://127.0.0.1:<port>/dws-quota
# -> {"remaining_tokens":1995521,"estimated":10975366}
Error shapes (all 5xx/4xx, never a crash):
{"error":"missing-credential","ref":"DEEPWERKSPACE_API_KEY"}— step 1 not done.{"error":"no-credentials-provider"}— nocredentialsservice in this profile.{"error":"quota-http","status":403}— the API rejected the key.{"error":"quota-failed","detail":"..."}— network/parse failure.
Remove
Plugin manager: dsh plugin --profile web remove dws-quota (or the Plugins
page / plugin_manager tool) — it deselects the bundle, unloads its
contributions, and runs pnpm remove. Manual mount: delete the insert block
from cordis.patch.yml and remove the node_modules/dws-quota entry.
Optionally remove the DEEPWERKSPACE_API_KEY ref. Restart. No trace remains —
the plugin registers nothing in the bundle cordis.yml.
Troubleshooting
- Pill never appears. Check
curl /dws-quotafirst. If it returnsmissing-credential, the ref name/value in step 1 is wrong. If the route 404s, the plugin did not load — confirm step 2 (node_modules) and step 3 (patch entry) and that the profile was restarted. - GUI fails to boot after adding it (the failure mode from a prior attempt):
the most common cause is a malformed client bundle (not the lazy-CJS
window.__ModuleLoader__.load({ id, factory })shape) or adsh.clientdeclaration that references a package that is not in the client graph. This bundle was verified against the shippedui-themebundle shape, and thedsh.client.injectedge (@deepseek-ai/dsh-client-ui-conversation) is a core web-client package. If boot breaks, remove theinsertentry and restart to recover, then diffclient.jsagainst this file. /dws-quotais 401. The connection fence rejected the request (Host/Origin or browser-session mismatch). In a real browser this is handled by the session cookie; a barecurlwithout the cookie will 401, which is expected.