opencode-anthropic-fix 0.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,215 @@
1
+ # External Integrations
2
+
3
+ **Analysis Date:** 2026-02-07
4
+
5
+ ## APIs & External Services
6
+
7
+ **Anthropic OAuth (Primary Integration):**
8
+
9
+ - **Purpose:** OAuth 2.0 authentication for Claude Pro/Max subscriptions and API key creation
10
+ - **SDK/Client:** Native `fetch()` — no SDK wrapper
11
+ - **Auth:** OAuth 2.0 with PKCE (Authorization Code flow)
12
+ - **Client ID:** `9d1c250a-e61b-44d9-88ed-5944d1962f5e` (hardcoded in `lib/config.mjs:71`)
13
+ - **PKCE Library:** `@openauthjs/openauth/pkce` — `generatePKCE()` function
14
+
15
+ **Anthropic OAuth Endpoints (used in `index.mjs` and `cli.mjs`):**
16
+
17
+ | Endpoint | Method | Purpose | Used In |
18
+ | --------------------------------------------------------------- | ------------- | ------------------------------------- | ---------------------------------------------- |
19
+ | `https://claude.ai/oauth/authorize` | GET (browser) | OAuth authorization (Max plan) | `index.mjs:21` |
20
+ | `https://console.anthropic.com/oauth/authorize` | GET (browser) | OAuth authorization (Console/API key) | `index.mjs:21` |
21
+ | `https://console.anthropic.com/v1/oauth/token` | POST | Token exchange & refresh | `index.mjs:42`, `index.mjs:536`, `cli.mjs:140` |
22
+ | `https://api.anthropic.com/api/oauth/claude_cli/create_api_key` | POST | Create API key from OAuth token | `index.mjs:1028` |
23
+ | `https://api.anthropic.com/api/oauth/usage` | GET | Fetch per-account usage quotas | `cli.mjs:168` |
24
+
25
+ **Anthropic Messages API (Proxied):**
26
+
27
+ - **Purpose:** The plugin intercepts and transforms requests to the Anthropic Messages API
28
+ - **Endpoint:** `/v1/messages` — detected via URL pathname matching
29
+ - **Transforms applied:**
30
+ - Adds `?beta=true` query parameter (`index.mjs:296`)
31
+ - Sets `Authorization: Bearer {access_token}` header
32
+ - Adds `anthropic-beta: oauth-2025-04-20,interleaved-thinking-2025-05-14` header
33
+ - Sets `user-agent: claude-cli/2.1.2 (external, cli)`
34
+ - Removes `x-api-key` header
35
+ - Prefixes tool names with `mcp_` in request body
36
+ - Strips `mcp_` prefix from tool names in response stream
37
+ - Rewrites `OpenCode` → `Claude Code` and `opencode` → `Claude` in system prompts
38
+
39
+ **OAuth Flow Parameters:**
40
+
41
+ - Redirect URI: `https://console.anthropic.com/oauth/code/callback`
42
+ - Scopes: `org:create_api_key user:profile user:inference`
43
+ - PKCE: S256 challenge method
44
+ - Grant types: `authorization_code` (initial), `refresh_token` (renewal)
45
+
46
+ ## Data Storage
47
+
48
+ **Databases:**
49
+
50
+ - None — All storage is local filesystem JSON files
51
+
52
+ **File Storage (Local):**
53
+
54
+ - `~/.config/opencode/anthropic-accounts.json` — Multi-account OAuth token storage
55
+ - Written by: `lib/storage.mjs` (`saveAccounts()`)
56
+ - Read by: `lib/storage.mjs` (`loadAccounts()`)
57
+ - Format: JSON with version, accounts array, activeIndex
58
+ - Security: File mode `0o600` (owner read/write only)
59
+ - Write strategy: Atomic (temp file + rename via `randomBytes(6)` suffix)
60
+ - Auto-deduplication by refresh token on load
61
+ - Auto `.gitignore` maintained in config directory
62
+
63
+ - `~/.config/opencode/anthropic-auth.json` — Plugin configuration
64
+ - Written by: `lib/config.mjs` (`saveConfig()`)
65
+ - Read by: `lib/config.mjs` (`loadConfig()`)
66
+ - Format: JSON with strategy, health scores, token bucket settings
67
+ - Security: File mode `0o600`
68
+ - Write strategy: Atomic (temp file + rename via PID suffix)
69
+
70
+ **Caching:**
71
+
72
+ - In-memory only — Health scores (`HealthScoreTracker`), token buckets (`TokenBucketTracker`), and stats deltas (`Map<string, StatsDelta>`) are held in memory during the process lifetime
73
+ - No external cache service
74
+
75
+ ## Authentication & Identity
76
+
77
+ **Auth Provider:** Anthropic OAuth 2.0 (custom implementation)
78
+
79
+ **Implementation:**
80
+
81
+ - PKCE Authorization Code flow for initial login
82
+ - Refresh token rotation for session maintenance
83
+ - Multi-account support (up to 10 accounts, `MAX_ACCOUNTS` in `lib/accounts.mjs:35`)
84
+ - Single-flight refresh protection (prevents concurrent refresh races in `index.mjs:659`)
85
+ - Automatic account switching on auth failure, rate limit, or quota exhaustion
86
+
87
+ **Token Lifecycle:**
88
+
89
+ 1. User visits authorization URL in browser
90
+ 2. User pastes authorization code back to CLI
91
+ 3. Plugin exchanges code for access + refresh tokens
92
+ 4. Access token used for API requests (short-lived)
93
+ 5. Refresh token used to obtain new access tokens when expired
94
+ 6. Plugin persists tokens to `anthropic-accounts.json`
95
+ 7. Also syncs primary account to OpenCode's `auth.json` for compatibility
96
+
97
+ **Account Selection Strategies (configurable in `lib/rotation.mjs`):**
98
+
99
+ - `sticky` — Stay on current account until it fails (default)
100
+ - `round-robin` — Rotate through accounts on every request
101
+ - `hybrid` — Score-based selection using health scores + token buckets + freshness
102
+
103
+ ## OpenCode Plugin SDK Integration
104
+
105
+ **SDK:** `@opencode-ai/plugin` ^0.4.45 (via `@opencode-ai/sdk` 0.4.19)
106
+
107
+ **Plugin hooks used:**
108
+
109
+ - `auth.loader(getAuth, provider)` — Custom auth loader that intercepts fetch requests
110
+ - `auth.methods[]` — Three auth methods (OAuth Max, OAuth Console/API Key, Manual API Key)
111
+ - `experimental.chat.system.transform` — Prepends Claude Code identity to system prompts
112
+
113
+ **SDK Client API used:**
114
+
115
+ - `client.auth.set()` — Persist OAuth tokens to OpenCode's auth store (`index.mjs:580`)
116
+ - `client.tui?.showToast()` — Display notifications in OpenCode TUI (`index.mjs:638`)
117
+
118
+ ## Monitoring & Observability
119
+
120
+ **Error Tracking:**
121
+
122
+ - None — No external error tracking service
123
+
124
+ **Logs:**
125
+
126
+ - Debug logging to `stderr` via `console.error()` when `config.debug` is enabled
127
+ - Prefix: `[opencode-anthropic-auth]`
128
+ - Controlled by: `OPENCODE_ANTHROPIC_DEBUG=1` env var or `debug: true` in config file
129
+
130
+ **TUI Toasts:**
131
+
132
+ - Account usage notifications (which account is active)
133
+ - Account switch notifications (when switching due to rate limit/failure)
134
+ - Error toasts (disabled accounts, auth failures)
135
+ - Configurable: `toasts.quiet` suppresses non-error toasts, `toasts.debounce_seconds` rate-limits toast frequency
136
+
137
+ ## CI/CD & Deployment
138
+
139
+ **Hosting:**
140
+
141
+ - npm registry (`opencode-anthropic-auth` package, currently v0.0.13)
142
+ - Self-installed to user machine (not a hosted service)
143
+
144
+ **CI Pipeline:**
145
+
146
+ - GitHub Actions: `.github/workflows/publish.yml`
147
+ - Trigger: Manual (`workflow_dispatch`) with optional version bump type
148
+ - Steps: Checkout → Node.js 24 → `npm install` → `npm publish --access public`
149
+ - Auth: `NPM_TOKEN` secret
150
+
151
+ **Publishing Flow:**
152
+
153
+ 1. Run `bun script/publish.ts [patch|minor|major]` locally
154
+ 2. Bumps version in `package.json`, commits, pushes
155
+ 3. Triggers GitHub Actions workflow via `gh workflow run`
156
+ 4. CI publishes to npm
157
+
158
+ ## Environment Configuration
159
+
160
+ **Required env vars:**
161
+
162
+ - None required for runtime (config file is optional, defaults are sane)
163
+
164
+ **Optional env vars (runtime):**
165
+
166
+ - `OPENCODE_ANTHROPIC_STRATEGY` — Override account selection strategy
167
+ - `OPENCODE_ANTHROPIC_DEBUG` — Toggle debug logging
168
+ - `OPENCODE_ANTHROPIC_QUIET` — Suppress non-error toasts
169
+ - `XDG_CONFIG_HOME` — Override config/storage directory base
170
+ - `NO_COLOR` — Disable ANSI colors in CLI output
171
+
172
+ **CI/CD env vars:**
173
+
174
+ - `NPM_TOKEN` — npm authentication for publishing (GitHub Actions secret)
175
+ - `NODE_AUTH_TOKEN` — Mapped from `NPM_TOKEN` in workflow
176
+
177
+ **Secrets location:**
178
+
179
+ - OAuth tokens stored in `~/.config/opencode/anthropic-accounts.json` (file mode 0o600)
180
+ - `.gitignore` auto-maintained in config directory to prevent accidental commits
181
+ - No secrets in repository
182
+
183
+ ## Webhooks & Callbacks
184
+
185
+ **Incoming:**
186
+
187
+ - OAuth callback URL: `https://console.anthropic.com/oauth/code/callback` (handled by Anthropic's servers; user pastes the returned code manually)
188
+
189
+ **Outgoing:**
190
+
191
+ - None
192
+
193
+ ## SSE Stream Processing
194
+
195
+ **The plugin processes Server-Sent Events (SSE) streams from the Anthropic API:**
196
+
197
+ - Strips `mcp_` prefix from tool names in response stream (`index.mjs:489`)
198
+ - Extracts token usage from `message_start` and `message_delta` events (`index.mjs:317-341`)
199
+ - Detects mid-stream account errors for proactive switching (`index.mjs:368-392`)
200
+ - Implemented via `ReadableStream` wrapping of response body (`index.mjs:465-492`)
201
+
202
+ ## Rate Limit & Backoff System
203
+
204
+ **The plugin implements sophisticated rate limit handling across accounts:**
205
+
206
+ - Categorizes errors: `AUTH_FAILED`, `QUOTA_EXHAUSTED`, `RATE_LIMIT_EXCEEDED` (`lib/backoff.mjs`)
207
+ - Respects `Retry-After` headers (both seconds and HTTP-date formats)
208
+ - Escalating backoffs for quota exhaustion: 1m → 5m → 30m → 2h
209
+ - Fixed backoffs: Auth failure 5s, Rate limit 30s
210
+ - Health scoring with passive recovery (configurable in `lib/rotation.mjs`)
211
+ - Token bucket for request gating (`lib/rotation.mjs`)
212
+
213
+ ---
214
+
215
+ _Integration audit: 2026-02-07_
@@ -0,0 +1,160 @@
1
+ # Technology Stack
2
+
3
+ **Analysis Date:** 2026-02-07
4
+
5
+ ## Languages
6
+
7
+ **Primary:**
8
+
9
+ - JavaScript (ESM / `.mjs`) — All application code, no TypeScript compilation step
10
+
11
+ **Secondary:**
12
+
13
+ - TypeScript — Single publish script only (`script/publish.ts`, run via Bun)
14
+ - Python — MITM proxy debugging tool (`.mitm/dump_anthropic.py`, not part of core)
15
+
16
+ **Key Decision:** Pure JavaScript with JSDoc type annotations throughout. No `.ts` source files in the main codebase — types are expressed via `@typedef` and `@param` JSDoc comments. This avoids a TypeScript compilation step while still providing IDE type checking.
17
+
18
+ ## Runtime
19
+
20
+ **Environment:**
21
+
22
+ - Node.js ≥ 20 (build target: `node20` in `scripts/build.mjs`)
23
+ - Development machine: Node.js v25.4.0
24
+
25
+ **Package Manager:**
26
+
27
+ - npm 11.7.0
28
+ - Lockfile: `package-lock.json` (present via `node_modules/`)
29
+
30
+ ## Frameworks
31
+
32
+ **Core:**
33
+
34
+ - `@opencode-ai/plugin` ^0.4.45 (devDependency) — OpenCode plugin SDK; defines the `Plugin` interface the project exports
35
+ - `@openauthjs/openauth` ^0.4.3 (runtime dependency) — PKCE code generation for OAuth flows (`generatePKCE`)
36
+
37
+ **Testing:**
38
+
39
+ - Vitest ^4.0.18 — Test runner and assertion library
40
+ - Config: implicit (no `vitest.config.*` found; uses defaults with `*.test.mjs` pattern)
41
+
42
+ **Build/Dev:**
43
+
44
+ - esbuild ^0.27.3 — Bundles plugin + CLI into self-contained single files in `dist/`
45
+ - Husky ^9.1.7 — Git hooks (pre-commit, pre-push)
46
+ - lint-staged ^16.2.7 — Run lint/format on staged files only
47
+ - ESLint ^10.0.0 — Linting (flat config at `eslint.config.mjs`)
48
+ - Prettier ^3.8.1 — Formatting (config at `.prettierrc`)
49
+
50
+ ## Key Dependencies
51
+
52
+ **Critical (runtime):**
53
+
54
+ - `@openauthjs/openauth` 0.4.3 — Only runtime dependency; provides `generatePKCE` for OAuth PKCE flow. Everything else is Node.js built-ins.
55
+
56
+ **Critical (dev/build):**
57
+
58
+ - `@opencode-ai/plugin` 0.4.45 — Defines the `Plugin` type interface. Transitive dependency: `@opencode-ai/sdk` 0.4.19 (provides `createOpencodeClient`)
59
+ - `esbuild` 0.27.3 — Bundles both entry points into zero-dependency single files
60
+
61
+ **Infrastructure:**
62
+
63
+ - `husky` 9.1.7 — Git hooks
64
+ - `lint-staged` 16.2.7 — Pre-commit quality gates
65
+
66
+ ## Configuration
67
+
68
+ **Environment Variables (runtime):**
69
+
70
+ - `OPENCODE_ANTHROPIC_STRATEGY` — Override account selection strategy (`sticky`, `round-robin`, `hybrid`)
71
+ - `OPENCODE_ANTHROPIC_DEBUG` — Enable debug logging (`1`/`true` or `0`/`false`)
72
+ - `OPENCODE_ANTHROPIC_QUIET` — Suppress non-error toasts (`1`/`true` or `0`/`false`)
73
+ - `XDG_CONFIG_HOME` — Override config directory (default: `~/.config`)
74
+ - `NO_COLOR` — Disable ANSI color output in CLI
75
+
76
+ **Config Files (user-facing, not in repo):**
77
+
78
+ - `~/.config/opencode/anthropic-auth.json` — Plugin configuration (strategy, health scores, token bucket, etc.)
79
+ - `~/.config/opencode/anthropic-accounts.json` — Multi-account storage (OAuth tokens, stats, rate limit state)
80
+
81
+ **Build Config:**
82
+
83
+ - `eslint.config.mjs` — ESLint flat config (ECMAScript 2025, Node.js globals, vitest test globals)
84
+ - `.prettierrc` — Prettier (120 char width, double quotes, trailing commas, 2-space indent)
85
+ - `scripts/build.mjs` — esbuild config (ESM format, node20 target, node:\* external)
86
+
87
+ **Git Hooks:**
88
+
89
+ - `.husky/pre-commit` — Runs `npm test` then `npx lint-staged`
90
+ - `.husky/pre-push` — Runs `npm test`, `npx prettier --check .`, `npx eslint .`
91
+
92
+ ## Build System
93
+
94
+ **Build command:** `npm run build` → `node scripts/build.mjs`
95
+
96
+ **Outputs:**
97
+
98
+ - `dist/opencode-anthropic-auth-plugin.js` — Bundled plugin (60KB, single file, no deps)
99
+ - `dist/opencode-anthropic-auth-cli.mjs` — Bundled CLI (37KB, single file, no deps)
100
+
101
+ **Build characteristics:**
102
+
103
+ - ESM format (`"format": "esm"`)
104
+ - All dependencies bundled except `node:*` builtins
105
+ - Target: Node.js 20+
106
+ - Both outputs are self-contained — no `node_modules` needed at runtime
107
+
108
+ **Install modes:**
109
+
110
+ - `npm run install:link` — Symlinks source files for development (live editing)
111
+ - `npm run install:copy` — Copies built artifacts for stable deployment
112
+ - `npm run uninstall` — Removes both plugin and CLI
113
+
114
+ **Install targets:**
115
+
116
+ - Plugin → `~/.config/opencode/plugin/opencode-anthropic-auth-plugin.js`
117
+ - CLI → `~/.local/bin/opencode-anthropic-auth`
118
+
119
+ ## NPM Scripts
120
+
121
+ | Script | Command | Purpose |
122
+ | -------------- | ------------------------------------------------ | -------------------------------- |
123
+ | `build` | `node scripts/build.mjs` | Bundle plugin + CLI with esbuild |
124
+ | `test` | `vitest run` | Run all tests once |
125
+ | `test:watch` | `vitest` | Run tests in watch mode |
126
+ | `lint` | `eslint .` | Lint all files |
127
+ | `lint:fix` | `eslint --fix .` | Lint and auto-fix |
128
+ | `format` | `prettier --write .` | Format all files |
129
+ | `format:check` | `prettier --check .` | Check formatting |
130
+ | `install:link` | `node scripts/install.mjs link` | Dev install (symlinks) |
131
+ | `install:copy` | `npm run build && node scripts/install.mjs copy` | Production install (copy) |
132
+ | `uninstall` | `node scripts/install.mjs uninstall` | Remove installed files |
133
+ | `prepare` | `husky` | Set up git hooks |
134
+
135
+ ## Platform Requirements
136
+
137
+ **Development:**
138
+
139
+ - Node.js ≥ 20
140
+ - npm (for dependency management)
141
+ - macOS/Linux (install scripts use Unix paths and symlinks)
142
+ - Optional: Bun (for `script/publish.ts` only)
143
+
144
+ **Production/Runtime:**
145
+
146
+ - Node.js ≥ 20
147
+ - OpenCode installed and configured
148
+ - No external dependencies at runtime (everything is bundled)
149
+
150
+ **Publishing:**
151
+
152
+ - GitHub Actions (`.github/workflows/publish.yml`)
153
+ - npm registry (`npm publish --access public`)
154
+ - Node.js 24 in CI
155
+ - Bun for version bump script (`script/publish.ts`)
156
+ - GitHub CLI (`gh`) for triggering workflow
157
+
158
+ ---
159
+
160
+ _Stack analysis: 2026-02-07_
@@ -0,0 +1,244 @@
1
+ # Codebase Structure
2
+
3
+ **Analysis Date:** 2026-02-07
4
+
5
+ ## Directory Layout
6
+
7
+ ```
8
+ opencode-anthropic-auth/
9
+ ├── index.mjs # Plugin entry point (AnthropicAuthPlugin)
10
+ ├── index.test.mjs # Plugin tests
11
+ ├── cli.mjs # CLI entry point (standalone account management)
12
+ ├── cli.test.mjs # CLI tests
13
+ ├── package.json # Package manifest (v0.0.13)
14
+ ├── package-lock.json # Lockfile
15
+ ├── eslint.config.mjs # ESLint configuration
16
+ ├── .gitignore # Git ignore rules
17
+ ├── lib/ # Shared library modules
18
+ │ ├── accounts.mjs # AccountManager class (runtime coordinator)
19
+ │ ├── accounts.test.mjs # AccountManager tests
20
+ │ ├── backoff.mjs # Error classification and backoff calculation
21
+ │ ├── backoff.test.mjs # Backoff tests
22
+ │ ├── config.mjs # Configuration loading, validation, defaults
23
+ │ ├── config.test.mjs # Config tests
24
+ │ ├── rotation.mjs # Selection algorithms (sticky/round-robin/hybrid)
25
+ │ ├── rotation.test.mjs # Rotation tests
26
+ │ ├── storage.mjs # Disk persistence (atomic JSON read/write)
27
+ │ └── storage.test.mjs # Storage tests
28
+ ├── scripts/ # Build and install tooling
29
+ │ ├── build.mjs # esbuild bundler (→ dist/)
30
+ │ └── install.mjs # Plugin/CLI installer (link/copy/uninstall)
31
+ ├── script/ # Release automation
32
+ │ └── publish.ts # Bun script: version bump + trigger GitHub workflow
33
+ ├── dist/ # Build output (gitignored)
34
+ │ ├── opencode-anthropic-auth-plugin.js # Bundled plugin (single file)
35
+ │ └── opencode-anthropic-auth-cli.mjs # Bundled CLI (single file)
36
+ ├── .github/
37
+ │ └── workflows/
38
+ │ └── publish.yml # GitHub Actions: npm publish on manual trigger
39
+ ├── .husky/ # Git hooks
40
+ │ ├── pre-commit # Runs lint-staged
41
+ │ └── pre-push # Runs tests
42
+ ├── .mitm/ # mitmproxy debugging tools (gitignored)
43
+ │ ├── dump_anthropic.py # Proxy script for capturing API traffic
44
+ │ ├── capture.jsonl # Captured request/response data
45
+ │ ├── flows # mitmproxy flow data
46
+ │ └── mitmdump.log # Proxy logs
47
+ └── .planning/ # GSD planning documents
48
+ └── codebase/ # Architecture analysis (this directory)
49
+ ```
50
+
51
+ ## Directory Purposes
52
+
53
+ **Root (`/`):**
54
+
55
+ - Purpose: Entry points and project configuration
56
+ - Contains: Two main entry points (`index.mjs`, `cli.mjs`), their co-located tests, and standard Node.js config files
57
+ - Key files: `index.mjs` (plugin), `cli.mjs` (CLI), `package.json`
58
+
59
+ **`lib/`:**
60
+
61
+ - Purpose: Shared library modules used by both plugin and CLI
62
+ - Contains: Five module pairs (source + test), each with a single clear responsibility
63
+ - Key files: `accounts.mjs` (core runtime), `config.mjs` (configuration), `storage.mjs` (persistence), `rotation.mjs` (algorithms), `backoff.mjs` (error handling)
64
+
65
+ **`scripts/`:**
66
+
67
+ - Purpose: Build and installation tooling
68
+ - Contains: `build.mjs` (esbuild bundler), `install.mjs` (symlink/copy installer)
69
+ - Key files: `build.mjs` produces single-file bundles in `dist/`
70
+
71
+ **`script/`:**
72
+
73
+ - Purpose: Release automation
74
+ - Contains: `publish.ts` — Bun-based version bump and GitHub workflow trigger
75
+ - Note: Uses Bun APIs (not Node.js), separate from `scripts/` directory
76
+
77
+ **`dist/`:**
78
+
79
+ - Purpose: Built artifacts (gitignored)
80
+ - Contains: Two self-contained bundled files (plugin + CLI) with all dependencies except Node.js builtins
81
+ - Generated: `npm run build`
82
+ - Committed: No
83
+
84
+ **`.github/workflows/`:**
85
+
86
+ - Purpose: CI/CD automation
87
+ - Contains: `publish.yml` — manual-trigger npm publish workflow
88
+ - Note: No CI testing workflow; tests run locally via pre-push hook
89
+
90
+ **`.husky/`:**
91
+
92
+ - Purpose: Git hooks via husky
93
+ - Contains: `pre-commit` (lint-staged), `pre-push` (test runner)
94
+
95
+ **`.mitm/`:**
96
+
97
+ - Purpose: Development debugging tools for API traffic inspection
98
+ - Contains: mitmproxy scripts and captured traffic data
99
+ - Committed: No (gitignored)
100
+
101
+ ## Key File Locations
102
+
103
+ **Entry Points:**
104
+
105
+ - `index.mjs`: Plugin entry — exports `AnthropicAuthPlugin` function
106
+ - `cli.mjs`: CLI entry — exports `main()` and individual command functions, self-executes when run directly
107
+
108
+ **Configuration:**
109
+
110
+ - `package.json`: Package manifest, scripts, dependencies
111
+ - `eslint.config.mjs`: ESLint flat config with Node.js globals
112
+ - `lib/config.mjs`: Runtime config loading from `~/.config/opencode/anthropic-auth.json`
113
+
114
+ **Core Logic:**
115
+
116
+ - `lib/accounts.mjs`: `AccountManager` class — central account coordinator
117
+ - `lib/rotation.mjs`: `HealthScoreTracker`, `TokenBucketTracker`, `selectAccount()` — selection algorithms
118
+ - `lib/backoff.mjs`: `isAccountSpecificError()`, `parseRateLimitReason()`, `calculateBackoffMs()` — error classification
119
+ - `lib/storage.mjs`: `loadAccounts()`, `saveAccounts()` — atomic JSON persistence
120
+
121
+ **Testing:**
122
+
123
+ - `index.test.mjs`: Plugin integration tests
124
+ - `cli.test.mjs`: CLI command tests
125
+ - `lib/accounts.test.mjs`: AccountManager unit tests
126
+ - `lib/rotation.test.mjs`: Selection algorithm tests
127
+ - `lib/backoff.test.mjs`: Error classification/backoff tests
128
+ - `lib/config.test.mjs`: Config loading tests
129
+ - `lib/storage.test.mjs`: Storage persistence tests
130
+
131
+ **Build & Deploy:**
132
+
133
+ - `scripts/build.mjs`: esbuild bundler producing `dist/` artifacts
134
+ - `scripts/install.mjs`: Plugin/CLI installer (link for dev, copy for prod)
135
+ - `script/publish.ts`: Release script (version bump + publish trigger)
136
+
137
+ ## Naming Conventions
138
+
139
+ **Files:**
140
+
141
+ - Source modules: `kebab-case.mjs` (e.g., `storage.mjs`, `config.mjs`)
142
+ - Test files: `<module>.test.mjs` co-located with source (e.g., `lib/storage.test.mjs`)
143
+ - Entry points: `index.mjs` (plugin), `cli.mjs` (CLI)
144
+ - Build artifacts: `opencode-anthropic-auth-{plugin,cli}.{js,mjs}`
145
+
146
+ **Directories:**
147
+
148
+ - `lib/`: Shared library modules (lowercase)
149
+ - `scripts/`: Build/install tooling (plural)
150
+ - `script/`: Release automation (singular — legacy Bun convention)
151
+ - `dist/`: Build output (standard)
152
+
153
+ **Exports:**
154
+
155
+ - Classes: PascalCase (`AccountManager`, `HealthScoreTracker`, `TokenBucketTracker`)
156
+ - Functions: camelCase (`loadConfig`, `saveAccounts`, `selectAccount`, `calculateBackoffMs`)
157
+ - Constants: SCREAMING_SNAKE_CASE (`DEFAULT_CONFIG`, `CLIENT_ID`, `VALID_STRATEGIES`, `RATE_LIMIT_KEY`)
158
+ - Types (JSDoc): PascalCase (`ManagedAccount`, `AccountStorage`, `RateLimitReason`)
159
+
160
+ ## Where to Add New Code
161
+
162
+ **New Library Module:**
163
+
164
+ - Implementation: `lib/<module-name>.mjs`
165
+ - Tests: `lib/<module-name>.test.mjs`
166
+ - Import from entry points or other lib modules as needed
167
+
168
+ **New CLI Command:**
169
+
170
+ - Add command function in `cli.mjs` (exported, named `cmd<Name>`)
171
+ - Add routing in the `switch` statement within `main()` function
172
+ - Add to help text in `cmdHelp()`
173
+
174
+ **New Plugin Hook:**
175
+
176
+ - Add in the returned object from `AnthropicAuthPlugin()` in `index.mjs`
177
+ - Follow OpenCode plugin API types from `@opencode-ai/plugin`
178
+
179
+ **New Account Selection Strategy:**
180
+
181
+ - Add strategy name to `VALID_STRATEGIES` in `lib/config.mjs`
182
+ - Add case to `selectAccount()` switch in `lib/rotation.mjs`
183
+ - Update `AccountSelectionStrategy` typedef
184
+
185
+ **Utilities/Helpers:**
186
+
187
+ - If used by multiple modules: create new file in `lib/`
188
+ - If used by single module: add as local function in that module
189
+ - CLI-only helpers: keep in `cli.mjs`
190
+ - Plugin-only helpers: keep in `index.mjs`
191
+
192
+ ## Special Directories
193
+
194
+ **`dist/`:**
195
+
196
+ - Purpose: Self-contained bundled artifacts (plugin + CLI)
197
+ - Generated: Yes (`npm run build` via esbuild)
198
+ - Committed: No (gitignored)
199
+ - Consumers: `scripts/install.mjs copy` deploys these to user's system
200
+
201
+ **`.mitm/`:**
202
+
203
+ - Purpose: mitmproxy debugging for inspecting API traffic between plugin and Anthropic
204
+ - Generated: Manually (development tool)
205
+ - Committed: No (gitignored)
206
+
207
+ **`node_modules/`:**
208
+
209
+ - Purpose: npm dependencies
210
+ - Generated: Yes (`npm install`)
211
+ - Committed: No (gitignored)
212
+
213
+ **`.planning/`:**
214
+
215
+ - Purpose: GSD codebase analysis documents
216
+ - Generated: By analysis tooling
217
+ - Committed: Yes (intended to be tracked)
218
+
219
+ ## Import Dependency Graph
220
+
221
+ ```
222
+ index.mjs (plugin)
223
+ ├── lib/accounts.mjs
224
+ │ ├── lib/storage.mjs
225
+ │ │ └── lib/config.mjs
226
+ │ ├── lib/rotation.mjs
227
+ │ │ └── lib/config.mjs
228
+ │ └── lib/backoff.mjs
229
+ ├── lib/config.mjs
230
+ ├── lib/storage.mjs
231
+ ├── lib/backoff.mjs
232
+ └── @openauthjs/openauth (external)
233
+
234
+ cli.mjs (CLI)
235
+ ├── lib/storage.mjs
236
+ │ └── lib/config.mjs
237
+ └── lib/config.mjs
238
+ ```
239
+
240
+ Note: `cli.mjs` does NOT depend on `lib/accounts.mjs` — it operates directly on the storage layer. The `AccountManager` class is plugin-only (in-memory runtime state). The CLI reads/writes the shared JSON file directly.
241
+
242
+ ---
243
+
244
+ _Structure analysis: 2026-02-07_