Skip to content
dsh.fish
Bundle

dsh-usage-estimator

Sidebar usage monitor for opencode.go and commandcode credits — usage bars plus per-model estimated request counts from the official pricing docs

Source
lbwfff
License
MIT
Updated
Updated 2 days ago

Readme

<div align="center">

# 📊 dsh-usage-estimator

**Sidebar usage monitor for [opencode.go](https://opencode.ai/docs/zh-cn/go/) & [commandcode](https://commandcode.ai/docs/plans/goat)** — a dual-face plugin for **DeepSeek Harness Web**

Live usage bars · Per-model request budget estimates · 中文 / English UI

[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![dsh-plugin](https://img.shields.io/badge/dsh-plugin-purple.svg)](https://github.com/topics/dsh-plugin)

**🌐 [Read this in 中文](./README.zh.md)**

---

</div>

The **host half** serves a `/quota` route on DSH's own HTTP server — no separate
process, no port, no CORS. The **browser half** polls that route and renders
usage bars into the sidebar footer (`sidebar.footer.action` slot).

The UI follows your DSH language setting (Simplified Chinese or English).
Product vocabulary stays untranslated on purpose: the time-window labels
`5-Hour` / `Weekly` / `Monthly` and the provider names `OpenCode Go` /
`Command Code`.

## 🖼️ Screenshots

<div align="center">

**Sidebar usage bars**

<img src="assets/sidebar-usage.png" width="420" alt="Sidebar usage bars">

**Estimated request counts modal** — the feature most other quota plugins lack

<img src="assets/estimated-request-counts.png" width="480" alt="Estimated request counts">

</div>

## ✨ Features

- 📈 **Live usage bars** — rolling / weekly / monthly quota for both providers,
  with time-until-reset.
- 🧮 **Estimated request counts** — one merged table comparing OpenCode Go vs
  Command Code GOAT per-model request budgets (per 5h / week / month). Models
  present in only one plan show a slash (`/`) in the other plan's columns.
- 🌐 **Fully localized** — follows your DSH language setting (中文 / English).
- 🔒 **Privacy-conscious** — cookie reading is optional and clearly documented.
- 📦 **Zero-setup request tables** — scraped from the official pricing docs,
  no auth needed.

### Why these two plans?

OpenCode Go and Command Code GOAT are the two **most affordable coding plans** —
the ones most people actually run their daily coding on. That's why the plugin
tracks their live usage and request budgets out of the box.

On a different plan or provider? The quota collector and the request-count
tables are plain readable scripts, and the plugin's config lets you point it at
your own workspace id and credit caps — adapt it to your own setup with DSH.

Data sources (cached ~6 h):

| Source | URL |
|---|---|
| 🌐 OpenCode Go | <https://opencode.ai/docs/zh-cn/go/> |
| 🌐 Command Code GOAT | <https://commandcode.ai/docs/plans/goat> |

## 📦 Install

**Prerequisite:** `dsh plugin` forwards to pnpm, so you need it on your PATH:

```bash
npm i -g pnpm   # or: corepack enable
```

Then install the plugin:

```bash
dsh plugin --profile web add github:lbwfff/dsh-usage-estimator
```

> [!NOTE]
> The first git install may ask you to allow pnpm's build scripts — add the
> exact key pnpm prints under `allowBuilds` in
> `~/.dsh/profiles/web/pnpm-workspace.yaml`, then re-run.

Then **restart dsh web**. The plugin registers itself into the profile's bundle
layers automatically (`dsh.bundle` + `cordis.patch.yml`).

## ⚙️ Configuration

The plugin reads its settings from the profile patch
(`~/.dsh/profiles/web/cordis.patch.yml`) under the entry's `config`:

```yaml
- insert:
    - id: dsh-usage-estimator
      name: dsh-usage-estimator
      config:
        # your opencode.go workspace id (required for the OpenCode Go bars)
        opencodeWorkspaceId: wrk_xxxxxxxxxxxxxxxxxxxxxxxx
        # your commandcode monthly credit cap (used to compute the monthly %)
        commandcodeMonthlyCap: 70
        # read browser cookies to authenticate against the official APIs
        cookieEnabled: true
        # path to a commandcode.ai HAR you exported yourself (fallback source)
        # harPath: /path/to/commandcode.ai.har
```

| Field | Default | Meaning |
|---|---|---|
| `opencodeWorkspaceId` | `""` | opencode.go workspace id (`wrk_...`). Empty disables the OpenCode Go bars. |
| `commandcodeMonthlyCap` | `0` | monthly credit cap for commandcode. Without it the monthly % falls back to the API-reported cap or is shown as unavailable. |
| `cookieEnabled` | `true` | read browser cookies to authenticate against the official APIs. Set `false` to disable cookie reading entirely (request-count tables keep working). |
| `harPath` | `~/.dsh-usage-estimator/commandcode.ai.har` | user-exported HAR used when the live API is unreachable. |

### 🔑 Finding your OpenCode Go workspace id

`opencodeWorkspaceId` is the one field you **must** set yourself — it's a
per-account value we can't ship in the plugin. To find yours:

1. Log in to [opencode.ai](https://opencode.ai) in your browser.
2. Open your workspace's usage page — the URL looks like
   `https://opencode.ai/workspace/wrk_xxxxxxxxxxxxxxxxxxxxxxxx/go`.
3. Copy the `wrk_...` segment and paste it into `opencodeWorkspaceId`.

Everything else is optional: with just the workspace id set (and
`cookieEnabled` left on), the plugin shows both providers' live usage bars and
the full request-count table. If you leave it empty, the OpenCode Go bars show
a clear "not configured" message and the rest keeps working.

## 🔒 Privacy

Read this before enabling cookie mode.

- ✅ **`cookieEnabled: true` (default)** — the collector reads your browser
  cookies (Edge, then Chrome/Firefox/Safari as fallbacks) for `opencode.ai`
  and `commandcode.ai`, and sends them **only** to those official domains to
  fetch your quota. Cookies never leave your machine to anywhere else, and
  nothing is logged or transmitted to any third party.
- 🚫 **`cookieEnabled: false`** — no cookie is read at all. You lose the live
  usage bars but keep the request-count tables (which need no auth).
- 💾 **HAR fallback** — if you export a `commandcode.ai` HAR yourself (DevTools
  → Network → save), the collector can read usage from it when the live API
  is down. The HAR stays on your machine; never commit it to a repo.
- 🗂️ The collector stores only a local cache of the public request-count
  tables (`~/.cache/dsh-usage-estimator/`), refreshed every 6 hours.

## 🏗️ Architecture

```
scripts/quota.py ──────────────(execFile)──> host half (lib/index.js)
   ├─ usage bars (opencode.go + commandcode credits)         │
   └─ requestCounts (2 pricing-doc tables merged, 6h cache)  ▼
                                                  registers exact route "/quota" on ctx.webServer
                                             DSH web server (same origin)
                                                  │
                                                  ▼  fetch("/quota") every 10 min
                                             browser half (lib/client.js)
                                                  │
                                                  ▼  ctx.slots.inject("sidebar.footer.action")
                                             sidebar footer widget (+ Requests modal)
```

- **Host half** (`lib/index.js`): a Cordis plugin that registers a `/quota`
  exact route on the web profile's existing `webServer` service. Each request
  shells out to `scripts/quota.py` via `execFile`, caches the snapshot for
  120s, and returns it as JSON. Your cordis `config` is forwarded to the
  script as `QM_*` environment variables. Registers on startup, unregisters on
  shutdown — no standalone process.
- **Browser half** (`lib/client.js`): a `window.__ModuleLoader__.load()` bundle
  mounting a React component into `sidebar.footer.action`, polling `/quota`
  every 10 minutes (manual refresh forces a re-fetch via `?force=1`). The
  Requests modal uses the platform `Modal` primitive
  (`@deepseek-ai/dsh-client-ui-primitives`) and the DSH locale plugin for its
  Chinese/English UI.

## 🧩 Files

| File | Role |
|---|---|
| `lib/index.js` | Host entry — `apply(ctx, config)` registers the `/quota` route on `webServer` |
| `lib/client.js` | Browser bundle — React component + slot injection + polling + Requests modal |
| `scripts/quota.py` | Data collector — reads `QM_*` env vars, outputs the JSON snapshot |
| `scripts/requests_count_lib.py` | Shared request-table scraper + name normalization + merged table |
| `requirements.txt` | Python deps (only `browser_cookie3`, for the live usage bars) |
| `lib/types/*.d.ts` | Type declarations |
| `assets/` | README screenshots |
| `test-host.js` | Smoke test: boots real Cordis + WebServer, hits `/quota`, prints JSON |
| `package.json` | `dsh.bundle` + `dsh.client` declarations + `exports["./client"]` |

## ✅ Requirements

- Python 3 with `browser_cookie3` (only needed when `cookieEnabled: true`):
  ```bash
  pip install -r requirements.txt
  ```
- The request-count tables need no extra Python deps (standard library only).
- **Cookie reading supports Edge, Chrome, Firefox and Safari** (in that order
  of preference). The collector reads the browser cookies for
  `opencode.ai` / `commandcode.ai` from whichever of these browsers has you
  logged in — so if you sign in with Chrome instead of Edge, it works too.
  Only the login state of these browsers is used; nothing else is read.

## 🧪 Test

```bash
node test-host.js
# [test] webServer listening on 64573
# [test] status: 200
# [test] go.ok: ... | cc.ok: ... | cc.source: ...
# [test] requestCounts.opencode.ok: true | rows: 22
# [test] requestCounts.cc_goat.ok: true | rows: 30
# [test] requestCounts.merged rows: 34
# [test] privacy scan: OK
# [test] PASS
```

## 📝 Notes

- **Data refresh**: host caches quota.py output for 120s; browser polls every
  10 min; manual refresh sends `?force=1` to bypass the cache.
- **Request tables**: quota.py refreshes them from the docs every 6 h
  (`~/.cache/dsh-usage-estimator/requests-count-cache.json`).
- **Collapsed sidebar**: the widget shrinks to a status dot (the Requests modal
  is still reachable after expanding).
- **No personal data shipped**: the workspace id, credit caps, cookie opt-in
  and HAR path all come from your cordis config. Export your own HAR if you
  want the offline fallback — don't commit it.

Install

dsh plugin --profile web add github:lbwfff/dsh-usage-estimator#5aca37480cd2942707f4ecf77a9044c7af39349d

Profile: web

Source