@rynfar/meridian 1.24.1 → 1.25.1

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/README.md CHANGED
@@ -11,9 +11,7 @@
11
11
 
12
12
  ---
13
13
 
14
- Meridian turns your Claude Max subscription into a local Anthropic API. Any tool that speaks the Anthropic protocol — OpenCode, Crush, Cline, Continue, Aider — connects to Meridian and gets Claude, powered by your existing subscription through the official Claude Code SDK.
15
-
16
- Harness Claude, your way.
14
+ Meridian turns your Claude Max subscription into a local Anthropic API. Any tool that speaks the Anthropic protocol — OpenCode, Crush, Cline, Aider — connects to Meridian and gets Claude, powered by your existing subscription through the official Claude Code SDK.
17
15
 
18
16
  > [!NOTE]
19
17
  > **Renamed from `opencode-claude-max-proxy`.** If you're upgrading, see [`MIGRATION.md`](MIGRATION.md) for the checklist. Your existing sessions, env vars, and agent configs all continue to work.
@@ -21,17 +19,20 @@ Harness Claude, your way.
21
19
  ## Quick Start
22
20
 
23
21
  ```bash
24
- # Install
22
+ # 1. Install
25
23
  npm install -g @rynfar/meridian
26
24
 
27
- # Authenticate (one time)
25
+ # 2. Authenticate (one time)
28
26
  claude login
29
27
 
30
- # Start
28
+ # 3. Configure OpenCode plugin (one time — OpenCode users only)
29
+ meridian setup
30
+
31
+ # 4. Start
31
32
  meridian
32
33
  ```
33
34
 
34
- Meridian starts on `http://127.0.0.1:3456`. Point any Anthropic-compatible tool at it:
35
+ Meridian runs on `http://127.0.0.1:3456`. Point any Anthropic-compatible tool at it:
35
36
 
36
37
  ```bash
37
38
  ANTHROPIC_API_KEY=x ANTHROPIC_BASE_URL=http://127.0.0.1:3456 opencode
@@ -43,7 +44,7 @@ The API key value doesn't matter — Meridian authenticates through your Claude
43
44
 
44
45
  You're paying for Claude Max. It includes programmatic access through the Claude Code SDK. But your favorite coding tools expect an Anthropic API endpoint and an API key.
45
46
 
46
- Meridian bridges that gap. It runs locally, accepts standard Anthropic API requests, and routes them through the SDK using your Max subscription. Claude does the work — Meridian just lets you pick the tool.
47
+ Meridian bridges that gap. It runs locally, accepts standard Anthropic API requests, and routes them through the SDK using your Max subscription.
47
48
 
48
49
  <p align="center">
49
50
  <img src="assets/how-it-works.svg" alt="How Meridian works" width="920"/>
@@ -51,25 +52,45 @@ Meridian bridges that gap. It runs locally, accepts standard Anthropic API reque
51
52
 
52
53
  ## Features
53
54
 
54
- - **Standard Anthropic API** — drop-in compatible with any tool that supports custom `base_url`
55
+ - **Standard Anthropic API** — drop-in compatible with any tool that supports a custom `base_url`
55
56
  - **Session management** — conversations persist across requests, survive compaction and undo, resume after proxy restarts
56
57
  - **Streaming** — full SSE streaming with MCP tool filtering
57
- - **Concurrent sessions** — run parent + subagent requests in parallel
58
+ - **Concurrent sessions** — run parent and subagent requests in parallel
59
+ - **Subagent model selection** — primary agents get 1M context; subagents get 200k, preserving rate-limit budget
60
+ - **Auto token refresh** — expired OAuth tokens are refreshed automatically; requests continue without interruption
58
61
  - **Passthrough mode** — forward tool calls to the client instead of executing internally
59
62
  - **Multimodal** — images, documents, and file attachments pass through to Claude
60
63
  - **Telemetry dashboard** — real-time performance metrics at `/telemetry`
61
- - **Cross-proxy resume** — sessions persist to disk and survive restarts
62
- - **Agent adapter pattern** — extensible architecture for supporting new agent protocols
63
64
 
64
65
  ## Agent Setup
65
66
 
66
67
  ### OpenCode
67
68
 
69
+ **Step 1: Run `meridian setup` (required, one time)**
70
+
71
+ ```bash
72
+ meridian setup
73
+ ```
74
+
75
+ This adds the Meridian plugin to your OpenCode global config (`~/.config/opencode/opencode.json`). The plugin enables:
76
+
77
+ - **Session tracking** — reliable conversation continuity across requests
78
+ - **Subagent model selection** — primary agents use `sonnet[1m]`; subagents automatically use `sonnet` (200k), preserving your 1M context rate-limit budget
79
+
80
+ If the plugin is missing, Meridian warns at startup and reports `"plugin": "not-configured"` in the health endpoint.
81
+
82
+ **Step 2: Start**
83
+
68
84
  ```bash
69
85
  ANTHROPIC_API_KEY=x ANTHROPIC_BASE_URL=http://127.0.0.1:3456 opencode
70
86
  ```
71
87
 
72
- For automatic session tracking, use a plugin like [opencode-with-claude](https://github.com/ianjwhite99/opencode-with-claude), or see the [reference plugin](examples/opencode-plugin/claude-max-headers.ts) to build your own.
88
+ Or set these in your shell profile so they're always active:
89
+
90
+ ```bash
91
+ export ANTHROPIC_API_KEY=x
92
+ export ANTHROPIC_BASE_URL=http://127.0.0.1:3456
93
+ ```
73
94
 
74
95
  ### Crush
75
96
 
@@ -94,75 +115,38 @@ Add a provider to `~/.config/crush/crush.json`:
94
115
  }
95
116
  ```
96
117
 
97
- Then use Meridian models in Crush:
98
-
99
118
  ```bash
100
119
  crush run --model meridian/claude-sonnet-4-6 "refactor this function"
101
120
  crush --model meridian/claude-opus-4-6 # interactive TUI
102
121
  ```
103
122
 
104
- Crush is automatically detected from its `Charm-Crush/` User-Agent — no extra configuration needed. In `crush run` headless mode all tool operations (read, write, bash) execute automatically without prompting.
123
+ Crush is automatically detected from its `Charm-Crush/` User-Agent — no plugin needed.
105
124
 
106
125
  ### Droid (Factory AI)
107
126
 
108
- Droid connects via its BYOK (Bring Your Own Key) feature. This is a one-time setup.
109
-
110
- **1. Add Meridian as a custom model provider** in `~/.factory/settings.json`:
127
+ Add Meridian as a custom model provider in `~/.factory/settings.json`:
111
128
 
112
129
  ```json
113
130
  {
114
131
  "customModels": [
115
- {
116
- "model": "claude-sonnet-4-6",
117
- "name": "Sonnet 4.6 (1M — Meridian)",
118
- "provider": "anthropic",
119
- "baseUrl": "http://127.0.0.1:3456",
120
- "apiKey": "x"
121
- },
122
- {
123
- "model": "claude-opus-4-6",
124
- "name": "Opus 4.6 (1M — Meridian)",
125
- "provider": "anthropic",
126
- "baseUrl": "http://127.0.0.1:3456",
127
- "apiKey": "x"
128
- },
129
- {
130
- "model": "claude-haiku-4-5-20251001",
131
- "name": "Haiku 4.5 (Meridian)",
132
- "provider": "anthropic",
133
- "baseUrl": "http://127.0.0.1:3456",
134
- "apiKey": "x"
135
- }
132
+ { "model": "claude-sonnet-4-6", "name": "Sonnet 4.6 (Meridian)", "provider": "anthropic", "baseUrl": "http://127.0.0.1:3456", "apiKey": "x" },
133
+ { "model": "claude-opus-4-6", "name": "Opus 4.6 (Meridian)", "provider": "anthropic", "baseUrl": "http://127.0.0.1:3456", "apiKey": "x" },
134
+ { "model": "claude-haiku-4-5-20251001", "name": "Haiku 4.5 (Meridian)", "provider": "anthropic", "baseUrl": "http://127.0.0.1:3456", "apiKey": "x" }
136
135
  ]
137
136
  }
138
137
  ```
139
138
 
140
- The `apiKey` value doesn't matter Meridian authenticates through your Claude Max session.
141
-
142
- **2. In the Droid TUI**, open the model selector (`/model`) and choose any `custom:claude-*` model.
143
-
144
- **How models map to Claude Max tiers:**
145
-
146
- | Model name in config | Claude Max tier |
147
- |---|---|
148
- | `claude-sonnet-4-6` | `sonnet[1m]` — Sonnet 4.6 with 1M context |
149
- | `claude-opus-4-6` | `opus[1m]` — Opus 4.6 with 1M context |
150
- | `claude-haiku-4-5-20251001` | `haiku` — Haiku 4.5 |
151
- | `claude-sonnet-4-5-*` | `sonnet` — Sonnet 4.5, no extended context |
152
-
153
- > **Note:** Droid automatically uses Meridian's internal tool execution mode regardless of the global `CLAUDE_PROXY_PASSTHROUGH` setting. No extra configuration needed.
139
+ Then pick any `custom:claude-*` model in the Droid TUI. No plugin needed Droid is automatically detected.
154
140
 
155
141
  ### Cline
156
142
 
157
- Cline CLI connects by setting `anthropicBaseUrl` in its config. This is a one-time setup.
158
-
159
- **1. Authenticate Cline with the Anthropic provider:**
143
+ **1. Authenticate:**
160
144
 
161
145
  ```bash
162
146
  cline auth --provider anthropic --apikey "dummy" --modelid "claude-sonnet-4-6"
163
147
  ```
164
148
 
165
- **2. Add the proxy base URL** to `~/.cline/data/globalState.json`:
149
+ **2. Set the proxy URL** in `~/.cline/data/globalState.json`:
166
150
 
167
151
  ```json
168
152
  {
@@ -172,81 +156,73 @@ cline auth --provider anthropic --apikey "dummy" --modelid "claude-sonnet-4-6"
172
156
  }
173
157
  ```
174
158
 
175
- **3. Run Cline:**
159
+ **3. Run:**
176
160
 
177
161
  ```bash
178
- cline --yolo "refactor the login function" # interactive
179
- cline --yolo --model claude-opus-4-6 "review this codebase" # opus
180
- cline --yolo --model claude-haiku-4-5-20251001 "quick question" # haiku (fastest)
162
+ cline --yolo "refactor the login function"
181
163
  ```
182
164
 
183
- No adapter or plugin needed — Cline uses the standard Anthropic SDK and falls through to the default adapter. All models (Sonnet 4.6, Opus 4.6, Haiku 4.5) route to their correct Claude Max tiers automatically.
165
+ No plugin needed — Cline uses the standard Anthropic SDK.
184
166
 
185
167
  ### Aider
186
168
 
187
- Aider works out of the box — no plugin or config file needed:
188
-
189
169
  ```bash
190
170
  ANTHROPIC_API_KEY=x ANTHROPIC_BASE_URL=http://127.0.0.1:3456 \
191
171
  aider --model anthropic/claude-sonnet-4-5-20250929
192
172
  ```
193
173
 
194
- All standard aider features work: file editing, repo-map, git integration, multi-file changes.
195
-
196
- > **Note:** Aider's `--no-stream` flag is incompatible due to a litellm parsing issue — use the default streaming mode (no flag needed).
174
+ > **Note:** `--no-stream` is incompatible due to a litellm parsing issue — use the default streaming mode.
197
175
 
198
176
  ### Any Anthropic-compatible tool
199
177
 
200
178
  ```bash
201
179
  export ANTHROPIC_API_KEY=x
202
180
  export ANTHROPIC_BASE_URL=http://127.0.0.1:3456
203
- # Then start your tool normally
204
181
  ```
205
182
 
206
183
  ## Tested Agents
207
184
 
208
- | Agent | Status | Plugin | Notes |
209
- |-------|--------|--------|-------|
210
- | [OpenCode](https://github.com/anomalyco/opencode) | ✅ Verified | [opencode-with-claude](https://github.com/ianjwhite99/opencode-with-claude) | Full tool support, session resume, streaming, subagents |
211
- | [Droid (Factory AI)](https://factory.ai/product/ide) | ✅ Verified | BYOK config (see setup above) | Full tool support, session resume, streaming; one-time BYOK setup |
212
- | [Crush](https://github.com/charmbracelet/crush) | ✅ Verified | Provider config (see setup above) | Full tool support, session resume, streaming, headless `crush run` |
213
- | [Cline](https://github.com/cline/cline) | ✅ Verified | Config (see setup above) | Full tool support, file read/write/edit, bash, session resume, all models |
214
- | [Continue](https://github.com/continuedev/continue) | 🔲 Untested | — | Should work standard Anthropic API |
215
- | [Aider](https://github.com/paul-gauthier/aider) | Verified | Env vars (see setup above) | File editing, streaming; `--no-stream` broken (litellm bug) |
185
+ | Agent | Status | Notes |
186
+ |-------|--------|-------|
187
+ | [OpenCode](https://github.com/anomalyco/opencode) | ✅ Verified | Requires `meridian setup` — full tool support, session resume, streaming, subagents |
188
+ | [Droid (Factory AI)](https://factory.ai/product/ide) | ✅ Verified | BYOK config (see above) full tool support, session resume, streaming |
189
+ | [Crush](https://github.com/charmbracelet/crush) | ✅ Verified | Provider config (see above) full tool support, session resume, headless `crush run` |
190
+ | [Cline](https://github.com/cline/cline) | ✅ Verified | Config (see above) full tool support, file read/write/edit, bash, session resume |
191
+ | [Aider](https://github.com/paul-gauthier/aider) | Verified | Env vars file editing, streaming; `--no-stream` broken (litellm bug) |
192
+ | [Continue](https://github.com/continuedev/continue) | 🔲 Untested | Should work standard Anthropic API |
216
193
 
217
194
  Tested an agent or built a plugin? [Open an issue](https://github.com/rynfar/meridian/issues) and we'll add it.
218
195
 
219
196
  ## Architecture
220
197
 
221
- Meridian is built as a modular proxy with clean separation of concerns:
222
-
223
198
  ```
224
199
  src/proxy/
225
200
  ├── server.ts ← HTTP orchestration (routes, SSE streaming, concurrency)
226
- ├── adapter.ts ← AgentAdapter interface (extensibility point)
201
+ ├── adapter.ts ← AgentAdapter interface
227
202
  ├── adapters/
228
203
  │ ├── detect.ts ← Agent detection from request headers
229
204
  │ ├── opencode.ts ← OpenCode adapter
230
- │ ├── crush.ts ← Crush (Charm) adapter
231
- │ ├── droid.ts ← Droid (Factory AI) adapter
205
+ │ ├── crush.ts ← Crush adapter
206
+ │ ├── droid.ts ← Droid adapter
232
207
  │ └── passthrough.ts ← LiteLLM passthrough adapter
233
208
  ├── query.ts ← SDK query options builder
234
209
  ├── errors.ts ← Error classification
235
- ├── models.ts ← Model mapping (sonnet/opus/haiku)
236
- ├── tools.ts Tool blocking lists
237
- ├── messages.ts Content normalization
210
+ ├── models.ts ← Model mapping (sonnet/opus/haiku, agentMode)
211
+ ├── tokenRefresh.ts Cross-platform OAuth token refresh
212
+ ├── setup.ts OpenCode plugin configuration
238
213
  ├── session/
239
214
  │ ├── lineage.ts ← Per-message hashing, mutation classification (pure)
240
215
  │ ├── fingerprint.ts ← Conversation fingerprinting
241
216
  │ └── cache.ts ← LRU session caches
242
217
  ├── sessionStore.ts ← Cross-proxy file-based session persistence
243
- ├── agentDefs.ts ← Subagent definition extraction
244
218
  └── passthroughTools.ts ← Tool forwarding mode
219
+ plugin/
220
+ └── meridian.ts ← OpenCode plugin (session headers + agent mode)
245
221
  ```
246
222
 
247
223
  ### Session Management
248
224
 
249
- Sessions map agent conversations to Claude SDK sessions. Meridian classifies every incoming request:
225
+ Every incoming request is classified:
250
226
 
251
227
  | Classification | What Happened | Action |
252
228
  |---------------|---------------|--------|
@@ -257,30 +233,9 @@ Sessions map agent conversations to Claude SDK sessions. Meridian classifies eve
257
233
 
258
234
  Sessions are stored in-memory (LRU) and persisted to `~/.cache/meridian/sessions.json` for cross-proxy resume.
259
235
 
260
- ### Adding a New Agent
236
+ ### Agent Detection
261
237
 
262
- Implement the `AgentAdapter` interface in `src/proxy/adapters/`:
263
-
264
- ```typescript
265
- interface AgentAdapter {
266
- // Required
267
- getSessionId(c: Context): string | undefined
268
- extractWorkingDirectory(body: any): string | undefined
269
- normalizeContent(content: any): string
270
- getBlockedBuiltinTools(): readonly string[]
271
- getAgentIncompatibleTools(): readonly string[]
272
- getMcpServerName(): string
273
- getAllowedMcpTools(): readonly string[]
274
-
275
- // Optional
276
- buildSdkAgents?(body: any, mcpToolNames: readonly string[]): Record<string, any>
277
- buildSdkHooks?(body: any, sdkAgents: Record<string, any>): any
278
- buildSystemContextAddendum?(body: any, sdkAgents: Record<string, any>): string
279
- usesPassthrough?(): boolean // overrides CLAUDE_PROXY_PASSTHROUGH per-agent
280
- }
281
- ```
282
-
283
- Agent detection is automatic from the `User-Agent` header:
238
+ Agents are identified from request headers automatically:
284
239
 
285
240
  | User-Agent prefix | Adapter |
286
241
  |---|---|
@@ -288,7 +243,9 @@ Agent detection is automatic from the `User-Agent` header:
288
243
  | `factory-cli/` | Droid |
289
244
  | *(anything else)* | OpenCode (default) |
290
245
 
291
- See [`adapters/detect.ts`](src/proxy/adapters/detect.ts) and [`adapters/opencode.ts`](src/proxy/adapters/opencode.ts) for reference.
246
+ ### Adding a New Agent
247
+
248
+ Implement the `AgentAdapter` interface in `src/proxy/adapters/`. See [`adapters/opencode.ts`](src/proxy/adapters/opencode.ts) for a reference.
292
249
 
293
250
  ## Configuration
294
251
 
@@ -304,77 +261,59 @@ See [`adapters/detect.ts`](src/proxy/adapters/detect.ts) and [`adapters/opencode
304
261
  | `MERIDIAN_IDLE_TIMEOUT_SECONDS` | `CLAUDE_PROXY_IDLE_TIMEOUT_SECONDS` | `120` | HTTP keep-alive timeout |
305
262
  | `MERIDIAN_TELEMETRY_SIZE` | `CLAUDE_PROXY_TELEMETRY_SIZE` | `1000` | Telemetry ring buffer size |
306
263
  | `MERIDIAN_NO_FILE_CHANGES` | `CLAUDE_PROXY_NO_FILE_CHANGES` | unset | Disable "Files changed" summary in responses |
264
+ | `MERIDIAN_SONNET_MODEL` | `CLAUDE_PROXY_SONNET_MODEL` | `sonnet[1m]`* | Force sonnet tier: `sonnet` (200k) or `sonnet[1m]` (1M). Set to `sonnet` if you hit 1M context rate limits |
307
265
 
308
- ## Programmatic API
309
-
310
- Meridian can be used as a library for building agent plugins and integrations.
266
+ *`sonnet[1m]` requires Max subscription with Extra Usage enabled. Falls back to `sonnet` automatically if not available.
311
267
 
312
- ```typescript
313
- import { startProxyServer } from "@rynfar/meridian"
268
+ ## Endpoints
314
269
 
315
- // Start a proxy instance
316
- const instance = await startProxyServer({
317
- port: 3456,
318
- host: "127.0.0.1",
319
- silent: true, // suppress console output
320
- })
270
+ | Endpoint | Description |
271
+ |----------|-------------|
272
+ | `GET /` | Landing page |
273
+ | `POST /v1/messages` | Anthropic Messages API |
274
+ | `POST /messages` | Alias for `/v1/messages` |
275
+ | `GET /health` | Auth status, mode, plugin status |
276
+ | `POST /auth/refresh` | Manually refresh the OAuth token |
277
+ | `GET /telemetry` | Performance dashboard |
278
+ | `GET /telemetry/requests` | Recent request metrics (JSON) |
279
+ | `GET /telemetry/summary` | Aggregate statistics (JSON) |
280
+ | `GET /telemetry/logs` | Diagnostic logs (JSON) |
321
281
 
322
- // instance.config — resolved ProxyConfig
323
- // instance.server — underlying http.Server
282
+ Health response example:
324
283
 
325
- // Shut down cleanly
326
- await instance.close()
284
+ ```json
285
+ {
286
+ "status": "healthy",
287
+ "auth": { "loggedIn": true, "email": "you@example.com", "subscriptionType": "max" },
288
+ "mode": "internal",
289
+ "plugin": { "opencode": "configured" }
290
+ }
327
291
  ```
328
292
 
329
- ### Session Header Contract
293
+ `plugin.opencode` is `"configured"` when `meridian setup` has been run, `"not-configured"` otherwise.
330
294
 
331
- For reliable session tracking, agents should send a session identifier via HTTP header. Without it, the proxy falls back to fingerprint-based matching (hashing the first user message + working directory), which is less reliable.
295
+ ## CLI Commands
332
296
 
333
- | Header | Purpose |
334
- |--------|---------|
335
- | `x-opencode-session` | Maps agent conversations to Claude SDK sessions for resume, undo, and compaction |
297
+ | Command | Description |
298
+ |---------|-------------|
299
+ | `meridian` | Start the proxy server |
300
+ | `meridian setup` | Configure the OpenCode plugin in `~/.config/opencode/opencode.json` |
301
+ | `meridian refresh-token` | Manually refresh the Claude OAuth token (exits 0/1) |
336
302
 
337
- The proxy uses this header to maintain conversation continuity across requests. Plugin authors should inject it on every request to `/v1/messages`.
303
+ ## Programmatic API
338
304
 
339
- ### Plugin Architecture
305
+ ```typescript
306
+ import { startProxyServer } from "@rynfar/meridian"
340
307
 
341
- Meridian is the proxy. Plugins live in the agent's ecosystem.
308
+ const instance = await startProxyServer({
309
+ port: 3456,
310
+ host: "127.0.0.1",
311
+ silent: true,
312
+ })
342
313
 
314
+ // instance.server — underlying http.Server
315
+ await instance.close()
343
316
  ```
344
- ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
345
- │ Agent │ HTTP │ Meridian │ SDK │ Claude Max │
346
- │ (OpenCode, │────────▶│ Proxy │────────▶│ │
347
- │ Crush, etc) │◀────────│ │◀────────│ │
348
- └──────────────┘ └──────────────┘ └──────────────┘
349
-
350
- │ plugin injects headers,
351
- │ manages proxy lifecycle
352
-
353
- ┌──────────────┐
354
- │ Agent Plugin │
355
- │ (optional) │
356
- └──────────────┘
357
- ```
358
-
359
- A plugin's job is to:
360
- 1. Start/stop a Meridian instance (`startProxyServer` / `instance.close()`)
361
- 2. Inject session headers into outgoing requests
362
- 3. Check proxy health (`GET /health`)
363
-
364
- See [`examples/opencode-plugin/`](examples/opencode-plugin/) for a reference implementation.
365
-
366
- ## Endpoints
367
-
368
- | Endpoint | Description |
369
- |----------|-------------|
370
- | `GET /` | Landing page (HTML) or status JSON (`Accept: application/json`) |
371
- | `POST /v1/messages` | Anthropic Messages API |
372
- | `POST /messages` | Alias for `/v1/messages` |
373
- | `GET /health` | Auth status, subscription type, mode |
374
- | `GET /telemetry` | Performance dashboard |
375
- | `GET /telemetry/requests` | Recent request metrics (JSON) |
376
- | `GET /telemetry/summary` | Aggregate statistics (JSON) |
377
- | `GET /telemetry/logs` | Diagnostic logs (JSON) |
378
317
 
379
318
  ## Docker
380
319
 
@@ -382,21 +321,13 @@ See [`examples/opencode-plugin/`](examples/opencode-plugin/) for a reference imp
382
321
  docker run -v ~/.claude:/home/claude/.claude -p 3456:3456 meridian
383
322
  ```
384
323
 
385
- Or with docker-compose:
386
-
387
- ```bash
388
- docker compose up -d
389
- ```
390
-
391
324
  ## Testing
392
325
 
393
326
  ```bash
394
- npm test # 522 unit/integration tests (bun test)
395
- npm run build # Build with bun + tsc
327
+ npm test # unit + integration tests
328
+ npm run build # build with bun + tsc
396
329
  ```
397
330
 
398
- Three test tiers:
399
-
400
331
  | Tier | What | Speed |
401
332
  |------|------|-------|
402
333
  | Unit | Pure functions, no mocks | Fast |
@@ -406,16 +337,29 @@ Three test tiers:
406
337
  ## FAQ
407
338
 
408
339
  **Is this allowed by Anthropic's terms?**
409
- Meridian uses the official Claude Code SDK — the same SDK Anthropic publishes and maintains for programmatic access. It authenticates through your existing Claude Max session using OAuth, not API keys. Nothing is modified, reverse-engineered, or bypassed.
340
+ Meridian uses the official Claude Code SDK — the same SDK Anthropic publishes for programmatic access. It authenticates through your existing Claude Max session using OAuth.
410
341
 
411
342
  **How is this different from using an API key?**
412
- API keys are billed per token. Your Max subscription is a flat monthly fee with higher rate limits. Meridian lets you use that subscription from any compatible tool.
343
+ API keys are billed per token. Claude Max is a flat monthly fee. Meridian lets you use that subscription from any compatible tool.
344
+
345
+ **What happens if my OAuth token expires?**
346
+ Tokens expire roughly every 8 hours. Meridian detects the expiry, refreshes the token automatically, and retries the request — so requests continue transparently. If the refresh fails (e.g. the refresh token has expired after weeks of inactivity), Meridian returns a clear error telling you to run `claude login`.
347
+
348
+ **Can I trigger a token refresh manually?**
349
+
350
+ ```bash
351
+ # CLI — works whether the proxy is running or not
352
+ meridian refresh-token
353
+
354
+ # HTTP — while the proxy is running
355
+ curl -X POST http://127.0.0.1:3456/auth/refresh
356
+ ```
413
357
 
414
- **Does it work with Claude Pro?**
415
- It works with any Claude subscription that supports the Claude Code SDK. Max is recommended for the best rate limits.
358
+ **I'm hitting rate limits on 1M context. What do I do?**
359
+ Set `MERIDIAN_SONNET_MODEL=sonnet` to use the 200k model for all requests. If you're using OpenCode with the Meridian plugin, subagents already use 200k automatically — only the primary agent uses 1M.
416
360
 
417
- **What happens if my session expires?**
418
- The SDK handles token refresh automatically. If it can't refresh, Meridian returns a clear error telling you to run `claude login`.
361
+ **Why does the health endpoint show `"plugin": "not-configured"`?**
362
+ You haven't run `meridian setup`. Without the plugin, OpenCode requests won't have session tracking or subagent model selection. Run `meridian setup` and restart OpenCode.
419
363
 
420
364
  ## Contributing
421
365
 
@@ -0,0 +1,18 @@
1
+ import { createRequire } from "node:module";
2
+ var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, {
10
+ get: all[name],
11
+ enumerable: true,
12
+ configurable: true,
13
+ set: __exportSetter.bind(all, name)
14
+ });
15
+ };
16
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
17
+
18
+ export { __export, __require };