feat(tui): migrate quota sidebar and commands to TUI V2 - #196
Conversation
|
@dmmop Thanks for the PR, code looks good. I believe OpenCode v2 is still in beta, so I'd hold off on releasing this on main for now. If there is support for v2 or when v2 is out of beta I'll definitely return to the PR. |
|
@slkiser Thanks you for this plugin! Yes, v2 is still in beta, thats why "opencode-next.db" is hardcoded in the code. I adapted the plugin to suit my needs in v2, i'll keep this PR update if they introduce any breaking change. |
585fc6f to
4d36e34
Compare
|
this has been great to have, thank you! |
|
Tested locally on Windows with opencode2 (beta-17728), at head 2143358, loaded as a TUI plugin via
One more thing — right now this won't load on V1 anymore, because Let me know if I can help test anything further :) |
- Extend `src/lib/opencode-auth.ts` with a credential database reader - Add `CredentialDatabase` and `CredentialDatabaseConstructor` types - Add `runtimeRequire` to conditionally load `better-sqlite3` - Implement `getCredentialDbPaths`, `readCredentialDatabase`, `openCredentialDatabase`, and `parseCredentialValue` - Modify `readAuthFile` and add `readAuthFiles` + `readCredentialDatabases` to merge credentials from auth.json and OpenCode's database - Preserve precedence: entries in auth.json override database credentials; database serves as a fallback for OAuth credentials - Add tests covering database fallback and auth.json precedence in `tests/lib.opencode-auth.test.ts`
- src/lib/opencode-auth.ts: update `getCredentialDbPaths` to compute credential database paths from a single base path and return them in a consistent format. - tests/lib.opencode-auth.test.ts: update the `createCredentialDatabase` helper to construct expected paths using the same base-path logic. Why: path construction was duplicated across callers, which made it easy for the test helper to drift from production behavior. Centralizing the path calculation keeps runtime and tests aligned on where credential databases are located.
- package.json: update @opentui/core and @opentui/solid from ^0.4.3 to ^0.5.3 - pnpm-lock.yaml: refresh lockfile for updated dependency resolution - src/tui-v2.tsx: update `TuiContext` and `plugin::setup` to align with the @OpenTui 0.5 API; `trigger` helper has a cosmetic-only change - tests/tui-v2-commands.test.ts: update "registers every quota command as a local slash command" test to cover the updated setup behavior
Compile the V2 TUI entry to the filename imported by the package and verify the clean distribution can be loaded. Use the current compaction and tool lifecycle events so automatic quota notifications run on OpenCode V2.
Merge upstream changes through 5158aa3 while preserving the fork's opencode.db credential source and adopting the new OpenCode Go config precedence and responsible update workflow.
Read every native credential row and report quota independently for each connection. Preserve provider plans, aliases, active markers, partial errors, and per-connection accounting across supported providers.
|
Confirmed on a newer beta build. Additional diagnostics for the V2 port (cc #229). Environment
Observed failure
Both entry points reject the module. The server loader expects Root cause in the 4.8.2 dist
// Module is valid only when the default export has id: string (non-empty) and setup: function
(mod) =>
typeof mod === "object" && mod !== null &&
"id" in mod && typeof mod.id === "string" && mod.id.length > 0 &&
"setup" in mod && typeof mod.setup === "function"
// On failure it throws: `Invalid V2 TUI plugin module: ${spec}`
Target shape (per current V2 plugin docs)import { Plugin } from "@opencode-ai/plugin/tui"
export default Plugin.define({
id: "@slkiser/opencode-quota",
async setup(context) {
// context.ui.slot(...), context.keymap.layer(...), context.data.*, context.ui.toast.show(...)
return () => {
/* cleanup */
}
},
})Also required:
Notes for the install and setup path
I can test a prerelease build against |
|
I've been testing this against I now have the V2 TUI working on the current
I also fixed two issues encountered while testing:
Validated on beta-18314:
Follow-on branch: https://github.com/christian-taillon/opencode-quota/tree/v2-native-multi-connection-quota This is based on #196 rather than upstream main, so I don't want to create a competing V2 PR. Happy to open a follow-up PR against |
Thanks, that would be very useful! I’ve just pushed my own implementation of native multi-connection quota support. It covers the credential-backed providers rather than only OpenAI, and I’m currently testing it. After reviewing your changes, I noticed that your branch also fixes two separate issues that my implementation does not currently cover:
I’d be very happy for you to open a follow-up PR against I’d also appreciate it if you could review my latest push and share any feedback on the multi-provider implementation. |
Use the plan returned by GitHub for user-scoped Copilot quota presentation instead of inferring a personal license from the billing mode. Preserve the user billing scope in diagnostics.
Add the conventional local TUI entrypoint and align OpenTUI with the beta-19151 runtime without changing server or TUI behavior.
|
i've been integrating both dmmop-v2 and upstream for a bit now into my own copy, to run in v2. i humbly submit my own fork that i intend to keep juggling for the foreseeable. it's definitely not as well maintained, caveat emptor, but also, i am excited to share and expect to keep updating for the foreseeable. https://github.com/rektide/opencode-quota-plus/ i also have some features like pro rata #252 that are very helpful to me, and multiple different view formats, which i haven't really been using heavily tbh. i'm making stronger use of the opencode v2 view slots that opencode has been evolving. i used to juggle v1+v2 support but dropped that (v2 is so much better, switch switch switch!). this plugin is the #1 must have opencode addition to me, and i'm so thankful for all the work folks have put in, and look forward to ongoingly contributing upstream here too. 🙇 |
Absorb 13 upstream commits (credential-scoped quota cache, cache security review, Windows portability) while preserving fork changes: multi-connection fan-out, opencode.db credential source and root tui.ts entrypoint. Adopt upstream removal of account_email/account_id from OpenAI status details. Adapt the new cache-isolation test mock to the opencode.db field contract.
The toast formatter call dropped resetTimeSpaced (and percentLabelStyle and accountingDetail), so toasts showed dense countdowns like 3d0h35m while the sidebar, footers, /quota and the CLI showed 3d 0h 35m. Pass the same settings the 4.x toast passed. A cross-surface test checks toast, sidebar, Home footer and prompt bar for both spaced and dense settings.
An OpenCode Console org without a monthly budget returns null from budgets/org (issue slkiser#216), which showed the error row "Could not parse OpenCode Console budgets/org response". A null body, an empty body, or {} now means no org budget, so Zen falls back to billing/account plus usage/cost-by-day without an error. Other malformed bodies stay errors. Zen error rows now read "OpenCode Zen:" instead of "OpenCode:"; Go errors already read "OpenCode Go:".
The sidebar renders each line with wrapMode="none" so bars stay intact,
which cut long error rows at the panel edge ("OpenCode: Could not parse
OpenCode Co"). The formatter already fits every bar and value row to the
36-column sidebar, so buildSidebarQuotaPanelLines now word-wraps only the
lines longer than that (errors and notices). Bars and aligned rows are
unchanged. Toasts already word-wrap in OpenCode, and the compact footers
are single-line by design.
show and status read OpenCode's database through node:sqlite, and Node 22+ printed "ExperimentalWarning: SQLite is an experimental feature and might change at any time" on every run. The CLI entry now filters process .emitWarning for that one ExperimentalWarning before any command loads node:sqlite. Every other warning still prints.
Add a flat-square badge to the top badge row that links to anomalyco/opencode#38281, and remove the "Like this plugin?" tip it replaces. The in-app opencode-ecosystem-listing-thumbs-up announcement stays.
… session In "dialog" mode (the default), Enter on a prompt that holds exactly a quota slash command (for example `/quota` or `/tokens_between 2026-09-01 2026-09-25`) now clears the prompt and runs the command over the quota RPC, before OpenCode submits it. The report opens only in the popup: it no longer shows in the chat for a moment, and on Home no new session is created. A keymap layer binds Enter at priority 1, above OpenCode's prompt, with no mode, so it is off while the "/" list or a dialog is open. Other text, and every command in "inline" mode, goes on to OpenCode's submit as before. tuiCommandDisplay is read at start and again after each typed quota command. The "/" list still shows only the server commands, and Web and Desktop are unchanged. Closes slkiser#296
OpenCode 2 mounts the TUI "app" slot again when it reconnects to its server. That happens when the server restarts, and it can also happen right after a cold start. Leaving a mount drops the keymap layers and event listeners registered in it, but the plugin skipped registering them again on the next mount. After such a remount, a typed /quota was no longer caught on Enter, and the listener that cancels a posted report was gone too. So OpenCode created a new session and the full report stayed in the chat, with no popup. The palette entries and quota toasts were also lost. Each mount of the app slot now disposes the previous listeners and registers the commands, the Enter binding, and the listeners again. While tuiCommandDisplay is still being read, a typed quota command now follows the default, "dialog", and opens the popup. Users with "inline" may see one popup if they type a quota command in the first moments after OpenCode starts. Reports for a session created from Home already work: OpenCode opens the new session before the server runs the command, so the report arrives for the session on screen. A test now covers that case.
Brings in the 3 main commits v5 lacked: - 4394e14 fix(opencode-zen): empty Console org budget means no budget - 1b6429f docs(readme): OpenCode listing badge - c5a7ae7 chore(release): sync package version to 4.10.6 Resolutions: - package.json: kept v5 version 5.0.0 (main had 4.10.6). - src/providers/opencode-zen.ts: kept v5. v5's 5c604b6 already has the same empty-budget handling and "OpenCode Zen:" error label. - tests/providers.opencode-zen.test.ts, tests/quota-render-data.test.ts: kept v5. - tests/lib.opencode-zen.test.ts: kept v5. The clean auto-merge would have added a second copy of the empty org budget test v5 already has. - src/lib/opencode-zen.ts: auto-merged to v5's version unchanged. - README.md: auto-merged to v5's version unchanged; exactly one listing badge. - tests/lib.quota-status.test.ts: took main's one-line fixture relabel ("OpenCode" -> "OpenCode Zen") so the Zen live-probe fixture matches the label v5 already emits.
|
|
Any idea when it officially will be released? Thanks for this by the way! |
Probably today! I've been using it extensively yesterday and trying to see any bugs. |
Every quota popup showed its title twice: the dialog title, then the report's own title line, e.g. "Tokens used (Last 7 Days)" and then "Tokens used (Last 7 Days) (/tokens_weekly) 16:00 29/09/2026". A report heading now has two fields: `line`, the first line of the text report, and `subtitle`, what the dialog shows in one muted line under its title instead: the facts in the line that the dialog title lacks, and the time. For example "opencode-quota v5.0.0 · 16:00 29/09/2026" for /quota_status, the date range for /tokens_between, "Percent used" for /quota with bare percent labels, or just the time. The dialog no longer draws the heading line, so the command echo is gone from the popup too. /quota and the announcements list now keep their first line in the heading instead of in a section. Chat, Web, and CLI text is unchanged: it still starts with `line`.
5.0.0 ships as the stable release, so the plain install command gets 5.x and the @next instructions no longer apply.
|
|
|
Tghanks @slkiser , I do have one (related?) question: I see this: "quota details unavailable". I'm out of tokens, but I would expect to see that right?
|
@rogierlommers Don't know your setup, but it could be a regression. Please open up a bug report and dump /quota_status |
|
After increasing quota, everything works now. I guess the plugin needs to (cache?) data and to get that data it needs tokens :) . |


Summary
/quotaand token-usage slash commands.auth.jsonas credential source.Testing
pnpm run typecheckpnpm exec vitest run tests/tui-v2-commands.test.tsgit diff --check 87e6540...HEAD/quotain the OpenCode V2 TUI