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.
- package/.github/workflows/publish.yml +31 -0
- package/.husky/pre-commit +2 -0
- package/.husky/pre-push +3 -0
- package/.planning/CLI-ENHANCEMENTS.md +221 -0
- package/.planning/codebase/ARCHITECTURE.md +201 -0
- package/.planning/codebase/CONCERNS.md +219 -0
- package/.planning/codebase/CONVENTIONS.md +335 -0
- package/.planning/codebase/INTEGRATIONS.md +215 -0
- package/.planning/codebase/STACK.md +160 -0
- package/.planning/codebase/STRUCTURE.md +244 -0
- package/.planning/codebase/TESTING.md +456 -0
- package/.prettierignore +3 -0
- package/.prettierrc +10 -0
- package/CONTRIBUTING.md +525 -0
- package/LICENSE +21 -0
- package/README.md +478 -0
- package/agents.md +22 -0
- package/bun.lock +43 -0
- package/cli.mjs +1773 -0
- package/cli.test.mjs +1797 -0
- package/docs/mimese-http-header-system-prompt.md +326 -0
- package/docs/plans/afcw-upstream-watcher.json +495 -0
- package/eslint.config.mjs +69 -0
- package/index.mjs +2209 -0
- package/index.test.mjs +2690 -0
- package/lib/accounts.mjs +670 -0
- package/lib/accounts.test.mjs +782 -0
- package/lib/backoff.mjs +217 -0
- package/lib/backoff.test.mjs +296 -0
- package/lib/config.mjs +366 -0
- package/lib/config.test.mjs +307 -0
- package/lib/oauth.mjs +103 -0
- package/lib/rotation.mjs +287 -0
- package/lib/rotation.test.mjs +346 -0
- package/lib/storage.mjs +265 -0
- package/lib/storage.test.mjs +470 -0
- package/nul +1 -0
- package/package.json +40 -0
- package/script/publish.ts +29 -0
- package/scripts/build.mjs +34 -0
- package/scripts/install.mjs +319 -0
|
@@ -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_
|