Summary
OpenCode V2 (@opencode/cli 2.x) does not load V1 plugins, and its built-in berget integration only accepts an API key. Berget Code seat users on V2 therefore have no way to sign in, and a seat token expires with no refresh path.
I hit this myself, so I went ahead and built a port to make it work for me: one new file that registers the two existing Berget Code seat logins as OAuth methods on V2's built-in berget integration, reusing the current flows and refresh code unchanged.
The branch is public and ready — would you like me to open a PR? klovaaxel/opencode-berget-auth @ feat/opencode-v2
I would also genuinely like to know whether a plugin is the right layer for this at all (see the first question below) — if you'd rather handle V2 in the integration itself, the issue is still worth having and the work is disposable.
The problem
- V2 ignores V1 plugins, so this package's
server() entrypoint is never called.
- V2's built-in Berget integration offers API-key auth only.
- Seat tokens are short-lived. Without the plugin registering a refresh path, a seat user has to re-authenticate by hand.
What I built
One new file, src/v2.ts, that:
- registers both seat methods (
berget-code-browser, berget-code-device) on V2's built-in berget integration via context.integration.transform
- hands V2 a
refresh function, so V2 owns credential storage and refresh
- reuses
createPkceAuthorizeMethod and createDeviceAuthorizeMethod unchanged
index.ts gets a single default export that serves both runtimes: V2 reads id + setup, V1 reads server().
@opencode/plugin 2.0.16 is added as a devDependency, types only, so V1 installs pull in nothing new.
Nothing about the existing V1 flow, token handling or model logic changes.
Open questions
-
Is a plugin-registered OAuth method the intended extension point here? If Berget would rather ship this inside the V2 integration itself, a PR from me is unnecessary — this issue is still worth tracking.
-
baseURL handling. The port sets the provider baseURL to the Berget inference URL on every load, matching what the V1 plugin already does. In V2 that overwrites a user's own baseURL override. I left it as-is to keep this to a single concern, but it may deserve its own change.
-
Lockfile growth. @opencode/plugin adds roughly 3,600 lines to package-lock.json. It is a dev-only types package, but flagging it in case that matters to you.
-
Worth knowing if you do this in the integration: the V2 plugin API is thinly documented, and dev does not match 2.0.16. I worked from the @opencode/plugin 2.0.16 type definitions. If the plugin API is still moving, that may argue for waiting rather than for a plugin.
Testing
Full check suite run in a clean LF clone of the branch:
| Check |
Result |
npm ci |
pass |
npx tsc --noEmit |
pass |
npm test |
75 passed, 7 files |
npm run lint |
pass |
npm run format:check |
pass |
Nine of those are new tests in src/v2.test.ts, covering credential mapping, token refresh, V1-flow-to-V2-method adaptation, setup() registration, and the dual-runtime default export.
Manually on OpenCode 2.0.16 (Windows, vendored plugin):
- Berget Code seat sign-in completes through the V2 plugin path. The issued token is a seat token (
azp: berget-code, realm role berget_code_seat).
berget/zai-org/GLM-5.3-Flash answers prompts end to end, both against the background service and --standalone.
- V2 stores the credential under this plugin's
methodID (berget-code-browser) and persists the refreshed token, so a seat session survives the 15-minute access token without re-authenticating.
POST /v1/auth/refresh returns a new access token with expires_in: 900 and a rotated refresh token; the old refresh token still works on reuse, so the rotation is non-destructive. The plugin already carries the rotated token forward when the server sends one (src/plugin/token.ts).
Not tested by hand: the device/QR flow, which needs a second device. It shares the code path with the browser flow and is covered by the same unit tests, but I have not driven the QR flow.
Summary
OpenCode V2 (
@opencode/cli2.x) does not load V1 plugins, and its built-inbergetintegration only accepts an API key. Berget Code seat users on V2 therefore have no way to sign in, and a seat token expires with no refresh path.I hit this myself, so I went ahead and built a port to make it work for me: one new file that registers the two existing Berget Code seat logins as OAuth methods on V2's built-in
bergetintegration, reusing the current flows and refresh code unchanged.The branch is public and ready — would you like me to open a PR?
klovaaxel/opencode-berget-auth@feat/opencode-v2I would also genuinely like to know whether a plugin is the right layer for this at all (see the first question below) — if you'd rather handle V2 in the integration itself, the issue is still worth having and the work is disposable.
The problem
server()entrypoint is never called.What I built
One new file,
src/v2.ts, that:berget-code-browser,berget-code-device) on V2's built-inbergetintegration viacontext.integration.transformrefreshfunction, so V2 owns credential storage and refreshcreatePkceAuthorizeMethodandcreateDeviceAuthorizeMethodunchangedindex.tsgets a single default export that serves both runtimes: V2 readsid+setup, V1 readsserver().@opencode/plugin2.0.16 is added as a devDependency, types only, so V1 installs pull in nothing new.Nothing about the existing V1 flow, token handling or model logic changes.
Open questions
Is a plugin-registered OAuth method the intended extension point here? If Berget would rather ship this inside the V2 integration itself, a PR from me is unnecessary — this issue is still worth tracking.
baseURLhandling. The port sets the providerbaseURLto the Berget inference URL on every load, matching what the V1 plugin already does. In V2 that overwrites a user's ownbaseURLoverride. I left it as-is to keep this to a single concern, but it may deserve its own change.Lockfile growth.
@opencode/pluginadds roughly 3,600 lines topackage-lock.json. It is a dev-only types package, but flagging it in case that matters to you.Worth knowing if you do this in the integration: the V2 plugin API is thinly documented, and
devdoes not match 2.0.16. I worked from the@opencode/plugin2.0.16 type definitions. If the plugin API is still moving, that may argue for waiting rather than for a plugin.Testing
Full check suite run in a clean LF clone of the branch:
npm cinpx tsc --noEmitnpm testnpm run lintnpm run format:checkNine of those are new tests in
src/v2.test.ts, covering credential mapping, token refresh, V1-flow-to-V2-method adaptation,setup()registration, and the dual-runtime default export.Manually on OpenCode 2.0.16 (Windows, vendored plugin):
azp: berget-code, realm roleberget_code_seat).berget/zai-org/GLM-5.3-Flashanswers prompts end to end, both against the background service and--standalone.methodID(berget-code-browser) and persists the refreshed token, so a seat session survives the 15-minute access token without re-authenticating.POST /v1/auth/refreshreturns a new access token withexpires_in: 900and a rotated refresh token; the old refresh token still works on reuse, so the rotation is non-destructive. The plugin already carries the rotated token forward when the server sends one (src/plugin/token.ts).Not tested by hand: the device/QR flow, which needs a second device. It shares the code path with the browser flow and is covered by the same unit tests, but I have not driven the QR flow.