English | 中文
dsh-od-quota
Unofficial plugin: not affiliated with Open Design / Powerformer. It only reads your own account's allowance through the public control-plane endpoints.
OpenDesign subscription quota inside DSH. Three surfaces read the same reading:
| Surface | Where | What it does |
|---|
| Quota page | Left sidebar "OpenDesign Quota" icon → full center page | 5-hour / 7-day windows, wallet, sign-in and approval |
| Settings summary | Settings sidebar "OpenDesign Quota" | Status, two mini bars, refresh / open page, auto-refresh and header toggles |
| Header meter | Session title bar, right of the title (left of the "Open in app" arrow) | Plan + two remaining bars; degrades when the row is tight; click to open the page |
The bars are coloured by remaining percentage (green = plenty, amber = watch, red = nearly dry); stale data is dimmed and explained in the tooltip.
What it reads
OpenDesign cloud has two planes; quota lives on the control plane:
| Plane | Host | Key | Use |
|---|
| Control | amr-api.open-design.ai | vela_ctrl_… | plan, wallet, 5h/7d windows, workspace |
| Model | amr-link.open-design.ai/v1 | sk-amr_… | inference |
sk-amr_… cannot read quota (the control plane refuses it), so the plugin uses the control plane. vela_ctrl_… is not printed on any page; it is issued by the CLI device-authorization flow (the same flow as vela login) — the plugin runs that flow for you.
Install
# npm (recommended; also the path the plugin market uses)
dsh plugin --profile web add dsh-od-quota
# or straight from the repository
dsh plugin --profile web add github:WuWei-Peng/dsh-od-quota
# or the prebuilt package from the GitHub Release (no npm; for restricted networks/mirrors)
dsh plugin --profile web add https://github.com/WuWei-Peng/dsh-od-quota/releases/latest/download/dsh-od-quota.tgz
Restart dsh web once after installing. The left sidebar then shows an "OpenDesign Quota" icon, and Settings gets a summary entry.
How it works
- When the quota page or the settings summary opens without a key, the plugin calls
POST /api/v1/cli/device-authorizations and gets an approval link plus a 6-character code; the page shows the code, a countdown, and a copyable link.
- You sign in to OpenDesign in the browser and approve; the plugin polls out the
controlKey and stores it in the DSH credential service (reference name OPENDESIGN_CONTROL_KEY by default; change it with keyRef).
- After that it reads
billing/preflight + billing/summary once every 60 seconds and writes the result into this plugin's quotaJson; the page, the settings summary and the header meter all parse only that document.
- A manual refresh writes
refreshNonce (a timestamp plus a sequence number, so two clicks never collide); the Host notices the change and reads immediately. A refused write is reported with a retry hint instead of a button that appears to do nothing.
- A 15-second watchdog checks the last read against the wall clock: a missed tick, a request that never returns, or a write the settings editor refused is all repaired on the next pass.
Device authorization is the primary path; pasting vela_ctrl_… directly also works. Pasting sk-amr_… is rejected by the UI with an explanation.
UI details
- Quota page: full-height scrolling page, content centred at 960px. Two large cards (5-hour / 7-day) show a big percentage, an 8px bar, remaining / limit money, and a reset countdown; the wallet gets its own card; the footer shows the read time and the
powered by dsh badge. Countdowns tick every 30 seconds.
- Settings summary: a status row, two mini bars, refresh / open page, and two switches for existing config fields:
autoRefresh (read every 60 s) and enabled (reading + header meter).
- Header meter: 28px tall, borderless, hover background — the same size and tokens as the header icon buttons. Its width is chosen from the space the host title row actually leaves: full (plan + two 44px bars) → compact (no plan + 26px bars) → mini (state dot + worst percentage) → hidden. Clicking it opens the quota page.
- Colour and theme: only
--dsw-alias-* / --dsw-radius-* / --dsw-focus-ring-* host tokens; light/dark follow automatically.
- Bilingual: copy follows DSH's language setting (Settings → General → Language); strings live in the
COPY table in client.js; other language packs fall back to English.
When things fail
- A read fails but a previous reading exists: the last numbers stay, marked
status: "stale"; the page shows "Data is stale" plus the reason and the meter is dimmed. A network blip is never reported as "you are signed out".
- No reading has ever succeeded: the page shows "not connected" and the concrete failure, not 0%.
enabled: false: no polling; the header meter does not render; the quota page shows a disabled notice and an Enable button.
- A request hangs (connection black hole, neither returns nor errors): every request has an outside deadline in addition to its
AbortSignal, so one hang costs at most one round; a reading still pending after two timeouts is abandoned and restarted, and later refreshes never join a promise that will never resolve.
- Control plane 401: one automatic re-sign-in, at most once per 5 minutes, so the 60-second poll does not become a device-authorization generator.
- Sign out: the credential is removed, the Host publishes
unconfigured at once, the page returns to the sign-in state, and the header meter disappears — no stale numbers are left behind.
- The Host stops refreshing: after 3 minutes without a new reading, the page and settings summary say "the host may not be polling", and the header tooltip repeats it. The usual cause is an edit to the installed directory/config while the process was running, which unloads the Host half; restart
dsh web to recover. Settings writes themselves have an 8-second deadline, so one wedged write can no longer freeze polling forever.
Layout
| File | Purpose |
|---|
index.js | Host: device authorization, quota reads, polling, writing back config |
client.js | Client: quota page (main), sidebar icon (sidebar.panellist), settings summary (settings.section), header meter (conversation.session.header.utilities) |
cordis.patch.yml | Inserts the od-quota row and its defaults |
locale/*.json | Plugin-manager title and description |
icon.svg | Plugin icon |
Configuration
| Field | Default | Description |
|---|
enabled | true | When off: no polling, no meter, no page |
keyRef | OPENDESIGN_CONTROL_KEY | Credential reference holding the control-plane key |
workspaceId | '' | Empty resolves via /workspaces/current |
autoRefresh | true | Read once every 60 seconds |
refreshNonce | '' | Written by the page's refresh button |
quotaJson / loginJson | '' | Plugin-written cache; the page only reads |
Feedback
Known limitations
- The control-plane API is a private contract. If its fields change, the page and logs show the failure (logs record field names, not values); the plugin never invents numbers.
- The header meter's width measurement relies on the host title row's layout containers (
container-type plus the title cluster / utilities boxes). If the host changes that structure, the meter falls back to the full layout — it will not crash, but it may be less compact than intended.
- Money uses
creditsPerUsd; when the API omits the rate, 10,000 credits = $1; amounts below one cent show < $0.01.
- Copy lives in the
COPY table in client.js and is bilingual following the host language; it is not registered in the Client locale namespace, so third-party language packs fall back to English.
- Single account only; multiple accounts would need
keyRef to become a list.
- The settings footer inlines the official
dsh-badge.png (121×20, data URI) so it renders offline.