Skip to content

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

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

feat(tui): migrate quota sidebar and commands to TUI V2#196
dmmop wants to merge 12 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.
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.
@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.

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