Skip to content

feat(tui): migrate quota sidebar and commands to TUI V2 - #196

Open
dmmop wants to merge 16 commits into
slkiser:mainfrom
dmmop:main
Open

feat(tui): migrate quota sidebar and commands to TUI V2#196
dmmop wants to merge 16 commits into
slkiser:mainfrom
dmmop:main

Conversation

@dmmop

@dmmop dmmop commented Aug 2, 2026

Copy link
Copy Markdown

Summary

  • Migrate the TUI integration to the OpenCode V2 plugin API.
  • Add a quota sidebar with collapsible provider details and active-provider count.
  • Register local /quota and token-usage slash commands.
  • BREAKING CHANGE: OpenCode's SQLite credential database replaced auth.json as credential source.

Testing

  • pnpm run typecheck
  • pnpm exec vitest run tests/tui-v2-commands.test.ts
  • git diff --check 87e6540...HEAD
  • manually verified /quota in the OpenCode V2 TUI

@slkiser

slkiser commented Aug 2, 2026

Copy link
Copy Markdown
Owner

@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.

@dmmop

dmmop commented Aug 2, 2026

Copy link
Copy Markdown
Author

@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.

@dmmop
dmmop force-pushed the main branch 3 times, most recently from 585fc6f to 4d36e34 Compare August 5, 2026 14:29
@rektide

rektide commented Aug 11, 2026

Copy link
Copy Markdown

this has been great to have, thank you!

@ransagy

ransagy commented Aug 20, 2026

Copy link
Copy Markdown

Tested locally on Windows with opencode2 (beta-17728), at head 2143358, loaded as a TUI plugin via cli.json. The sidebar and the /quota + /tokens_* commands work. A few things I ran into, possibly expected/how it works now but not to my untrained TUI eyes:

  1. To test the built artifact I had to manually compile tui-v2.tsxtui-v2.js first — the build emits tui-v2.jsx (jsx: preserve) while dist/index.js imports ./tui-v2.js, so the packaged output can't resolve it. (Related: tests/tui-v2-commands.test.ts also doesn't run on a clean checkout — vite chokes on the raw JSX — so I ran it against the compiled version.). Maybe something the release/CI usually does, not sure.

  2. Two of the new toast triggers don't fire in the V2 event schema:

    • session.compacted doesn't exist in V2 (compaction events are session.compaction.*).
    • session.tool.success has no tool field on data (neither does session.tool.called); only session.tool.input.started carries data.name, so the data?.tool === "question" check never matches. Probably opencode changing the schema as they go along, there were a few beta releases this week already.

One more thing — right now this won't load on V1 anymore, because index.ts no longer exports the V1 { id, server } module. Probably intentional. but worth checking if you intend to support both.

Let me know if I can help test anything further :)

@dmmop

dmmop commented Aug 21, 2026

Copy link
Copy Markdown
Author

Thanks for the thorough report and for testing it, @ransagy! The packaging, clean-checkout test, and V2 event trigger issues are now fixed.

As for V1 support, I wasn’t planning to support both versions. I adapted the project to my own needs and shared it in case it was useful to @slkiser.

dmmop added 11 commits August 21, 2026 21:15
- 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.
dmmop added 2 commits August 23, 2026 17:03
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.
@gdevenyi

Copy link
Copy Markdown

Confirmed on a newer beta build. Additional diagnostics for the V2 port (cc #229).

Environment

  • OpenCode 2 TUI and server: opencode2 v0.0.0-beta-18314 (beta dist-tag)
  • opencode-quota: 4.8.2 (current latest, installed as @slkiser/opencode-quota@latest)

Observed failure

  • TUI /plugins report: @slkiser/opencode-quota@latest → status failed, runtime tui, error Invalid V2 TUI plugin module: @slkiser/opencode-quota@latest.
  • Server side (~/.local/share/opencode/log/opencode.log, role=server), from the same plugin listed in the V1 plugin array of global opencode.json:
level=WARN message="failed to load plugin" target=@slkiser/opencode-quota@latest cause="Cause([Fail(SchemaError(Missing key
  at [\"default\"][\"effect\"]
Missing key
  at [\"default\"][\"setup\"]))])"

Both entry points reject the module. The server loader expects effect and setup on the default export. The TUI loader expects id plus setup.

Root cause in the 4.8.2 dist

  • dist/index.js default export: { id: "@slkiser/opencode-quota", server: QuotaToastPlugin }. This is the V1 shape.
  • dist/tui.js default export: { id, tui }. This is the V1 TuiPluginModule shape from @opencode-ai/plugin/tui 1.x.
  • TUI loader validation in this beta build, extracted from the opencode2 binary:
// 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}`
  • Build dependency is @opencode-ai/plugin 1.18.11 (peer ^1.4.3). The published dist therefore still compiles against the V1 API. The V2 API is published under the beta dist-tag of @opencode-ai/plugin (0.0.0-beta-18314 today), with Plugin.define available from @opencode-ai/plugin/tui.

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:

  • Main server plugin sets tui: true, so the ./tui export loads automatically.
  • package.json uses "@opencode-ai/plugin": "beta" and peer deps @opentui/core, @opentui/solid, solid-js.

Notes for the install and setup path

  • V2 still reads the V1 plugin array in opencode.json (compatibility path), then fails module validation as shown above.
  • V1 layered tui.json no longer applies. V2 uses one global cli.json with a plugins array. A migrated setup carries the spec into cli.json plugins; this is what triggers the TUI-side error above.

I can test a prerelease build against beta-18314 if that helps.

@christian-taillon

christian-taillon commented Aug 27, 2026

Copy link
Copy Markdown

I've been testing this against opencode2 v0.0.0-beta-18314 and built a follow-on from the current #196 head.

I now have the V2 TUI working on the current { id, setup } loader, including the sidebar and /quota, and added support for multiple native OpenAI connections. I've tested two simultaneous OAuth accounts (Plus + Business); they remain separate and each exposes its Five-hour and Weekly quota windows.

image

I also fixed two issues encountered while testing:

  • tuiSidebarPanel.formatStyle: "allWindows" was being applied after single-window projection, so the sidebar lost the additional quota windows.
  • CLI status could OOM on a large OpenCode database because usage aggregation materialized full message.data JSON payloads. It now queries only the scalar fields required for usage accounting.

Validated on beta-18314:

  • OpenCode sidebar: Plus + Business, Five-hour + Weekly
  • /quota and /quota_status
  • /tokens_all
  • packed CLI show and status
  • typecheck/build
  • full suite: 2,218 tests passing

Follow-on branch: https://github.com/christian-taillon/opencode-quota/tree/v2-native-multi-connection-quota
Commit: 046b4d25a3418c21a0bf6278b32faa196b1cd8d4

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 dmmop/opencode-quota-v2:main if that would be useful.

@dmmop

dmmop commented Aug 27, 2026

Copy link
Copy Markdown
Author

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 dmmop/opencode-quota-v2:main if that would be useful.

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:

  • The sidebar-specific allWindows format needs to be applied before quota projection; otherwise a global singleWindow setting discards the additional windows too early.
  • CLI status can consume excessive memory on large OpenCode databases because it materializes the complete message.data payload instead of selecting only the scalar fields required for usage accounting.

I’d be very happy for you to open a follow-up PR against dmmop/opencode-quota-v2:main. If possible, please rebase it onto the latest main and keep the non-overlapping fixes isolated so they are easier to review. I’ll gladly review and merge it.

I’d also appreciate it if you could review my latest push and share any feedback on the multi-provider implementation.

dmmop added 2 commits August 27, 2026 18:46
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.
@rektide

rektide commented Sep 5, 2026

Copy link
Copy Markdown

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants