Repository navigation
feat: OpenCode V2 support (dual V1/V2 package, 2.0.0) - #45
Merged
Merged
Conversation
…ke findings - MIGRATE_V2.md: researched dual-support strategy for OpenCode V2 - IMPLEMENTATION_PLAN_V2.md: phased execution plan, amended with phase 0 outcomes - PHASE0_FINDINGS.md: spike validation results (credential pipeline, SDK-free entrypoint, broken fresh-db V1 import in @opencode/cli 2.0.22 + proven fallback)
Default export becomes the officially recommended dual shape: V2 CLIs read id + setup() (V2 implementation loads lazily via dynamic import, so no V2 SDK code executes under V1); V1 CLIs >= 1.3.4 call server(), which delegates to the unchanged BergetAuthPlugin. All named exports preserved. - add @opencode/[email protected] as exact devDependency (type-only usage, enforced by test; user installs stay qrcode-weight) - promote @opencode-ai/[email protected] to explicit dependency (typecheck hygiene; imports are all type-only) - src/v2/setup.ts stub establishes the lazy import target - index.test.ts: entrypoint shape, lazy setup(), server() delegation, public API surface, and a type-only SDK import guard for src/v2/ - tsconfig: include index.test.ts Verified: npm test (66 pass), npm run lint, npm run typecheck, npm run format:check
src/v2/integration.ts registers the berget integration via ctx.integration.transform as an upsert onto the CLI's native (models.dev) integration: PKCE (id 'oauth', mode auto), device flow (id 'device', mode auto, device-code expiry passed through as expiresAt), and an API key method. Flow logic is shared with V1 (createPkceAuthorizeMethod / createDeviceAuthorizeMethod unchanged except an additive optional expiresAt field); toV2Authorization adapts V1 results to the V2 authorization shape, mapping failed results to rejections. src/v2/credential.ts adapts V1 token fields to the V2 Credential.OAuth shape (integer epoch-ms expires) and implements the shared refresh callback on top of refreshAccessTokenDirect. Method ids are shared constants: PKCE must register as 'oauth' to match V2's legacy-import mapping; refresh echoes the method's own id. src/v2/migrate.ts performs a one-time, idempotent import of the V1 auth.json berget OAuth credential via the client credential API. It is feature-detected (the plugin context does not expose the credential domain on @opencode/[email protected]) and never blocks startup. Also: vitest now runs index.test.ts (root-level tests were previously not executed by the runner — typecheck was the only gate covering it). Verified: npm test (88 pass), lint, typecheck, format:check.
src/v2/provider.ts composes the provider transform with the CLI's native models.dev definition instead of replacing it: updates in place to bind integrationID 'berget', keep V1's always-on activation, and honor BERGET_INFERENCE_URL as a baseURL override; fills in models the live /v1/models/chat catalog lists that models.dev does not yet know (native metadata and costs preserved). A missing provider is added from scratch. Models are fetched once in setup and captured by the synchronous, repeatable transform; fetch failures degrade to the native catalog. VISION_MODELS is exported from src/plugin/models.ts (additive) for reuse in the V2 mapping. setup() now fetches models and registers the provider. Verified end to end with the actual package loaded by @opencode/cli: integration shows the three methods, provider shows activation enabled + integrationID binding, and the model catalog stays native (no fill-ins needed today). Verified: npm test (96 pass), lint, typecheck, format:check.
E2E matrix (installed package prepopulated into the CLI plugin cache so the loader exercises the real exports map; config spec pinned to @2.0.0 which short-circuits the installer on all three CLIs): - V1 1.3.4 (floor leg): PASS — object-form default export loads via exports["./server"], provider + live-fetched models register, auth reaches Berget's API (upstream 401 'Invalid token format' is the real API body for the seeded fake key) - V1 1.18.34 (latest): PASS — same results - V2 @opencode/cli 2.0.22: PASS — 3 methods registered on the integration, provider enabled + integration-bound, 6 native models, key connect + agent loop hits the real API - PKCE/device client login, TUI QR rendering, and full CLI versions of the expiry/transition legs remain user-gated; the credential pipeline (refresh near expiry, re-persistence), device/PKCE authorize starts, and the SDK-free install were verified headlessly in phase 0 (PHASE0_FINDINGS.md) Changes: - README: V1/V2 compatibility table (foot note: < 1.3.4 must pin @1), V2 'plugins' config example, V1->V2 credential-transition summary - docs/release-2.0.0.md: release notes draft - version: 2.0.0 Verified: npm test (96 pass), lint, typecheck, format:check.
MIGRATE_V2.md, IMPLEMENTATION_PLAN_V2.md and PHASE0_FINDINGS.md are working documents for this change, not package documentation; they stay local. Also drop the dangling PHASE0_FINDINGS.md reference from docs/auth.md.
- Move @opencode-ai/sdk to devDependencies (type-only usage; it already reaches installs transitively via @opencode-ai/plugin) - Guard device-flow expiresAt with Number.isFinite so a malformed expires_in can never hand NaN to the V2 framework - Exclude catalog models without a numeric contextWindow from the V2 fill-in instead of registering them with limit.context: 0 - Read V1's pre-XDG ~/.opencode/auth.json as a fallback in the V1 credential import, matching V1's own read order
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds OpenCode V2 support to
@bergetai/opencode-authwhile keeping V1 working from the same package. Ships as 2.0.0.How it works
id+setup(); V1 (>= 1.3.4) callsserver(). V2 code loads lazily via dynamic import, so no V2 SDK code ever executes under V1.@opencode/[email protected]is a devDependency; allsrc/v2/imports areimport type(CI-enforced). User installs stayqrcode-weight.bergetintegration: PKCE (id: "oauth", matches V2's legacy-import mapping), device flow (id: "device"), API key. That is the plugin's core value on V2: V2 natively supports only key/env auth for Berget.integrationID, keeps V1's always-on activation, honorsBERGET_INFERENCE_URL, and fills in models the live/v1/models/chatcatalog knows that models.dev doesn't (never clobbers native metadata).src/v2/migrate.ts): one-time, idempotent import of the V1auth.jsonberget login — feature-detected (see findings below), silent no-op when unavailable, never blocks startup.server()delegates to the existingBergetAuthPlugin); only additive change is an optionalexpiresAton the device flow result.Notable findings during validation
@opencode/[email protected]: fresh-db bootstrap journals all 48 migrations without executing their up-functions), and the plugin context does not expose the clientcredentialAPI yet. The plugin-side import covers both automatically once either lands upstream.@opencode/cli(npm), notopencode-ai@beta.Test plan
oauth/deviceregistration-vs-credential regression guard, refresh mapping/rejection, all import outcomes, provider upsert/fill-in/env-override). Lint, typecheck, format clean.exportsresolution):@1. No behavior change for V1 >= 1.3.4 users.Docs: README (V1/V2 compatibility table, config keys, transition summary),
docs/auth.md(V2 credential storage + provider registration sections),docs/release-2.0.0.md(release notes draft).After merge: release via
publish.ymlusingdocs/release-2.0.0.mdas the release body.