@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 +128 -184
- package/dist/cli-a05ws7rb.js +18 -0
- package/dist/cli-m9pfb7h9.js +203 -0
- package/dist/cli-rtab0qa6.js +67 -0
- package/dist/{cli-bjpad5x9.js → cli-s6f9jefk.js} +177 -124
- package/dist/cli.js +56 -3
- package/dist/proxy/adapter.d.ts +1 -1
- package/dist/proxy/errors.d.ts +16 -0
- package/dist/proxy/errors.d.ts.map +1 -1
- package/dist/proxy/fileChanges.d.ts.map +1 -1
- package/dist/proxy/models.d.ts +17 -1
- package/dist/proxy/models.d.ts.map +1 -1
- package/dist/proxy/server.d.ts.map +1 -1
- package/dist/proxy/setup.d.ts +42 -0
- package/dist/proxy/setup.d.ts.map +1 -0
- package/dist/proxy/tokenRefresh.d.ts +51 -0
- package/dist/proxy/tokenRefresh.d.ts.map +1 -0
- package/dist/server.js +4 -1
- package/dist/setup-5x116vbs.js +13 -0
- package/dist/telemetry/dashboard.d.ts +1 -1
- package/dist/telemetry/dashboard.d.ts.map +1 -1
- package/dist/telemetry/routes.d.ts.map +1 -1
- package/dist/tokenRefresh-ywwpe8k2.js +11 -0
- package/package.json +76 -75
- package/plugin/meridian.ts +54 -0
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,
|
|
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
|
-
#
|
|
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
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
|
123
|
+
Crush is automatically detected from its `Charm-Crush/` User-Agent — no plugin needed.
|
|
105
124
|
|
|
106
125
|
### Droid (Factory AI)
|
|
107
126
|
|
|
108
|
-
|
|
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
|
-
|
|
117
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
159
|
+
**3. Run:**
|
|
176
160
|
|
|
177
161
|
```bash
|
|
178
|
-
cline --yolo "refactor the login function"
|
|
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
|
|
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
|
-
|
|
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 |
|
|
209
|
-
|
|
210
|
-
| [OpenCode](https://github.com/anomalyco/opencode) | ✅ Verified |
|
|
211
|
-
| [Droid (Factory AI)](https://factory.ai/product/ide) | ✅ Verified | BYOK config (see
|
|
212
|
-
| [Crush](https://github.com/charmbracelet/crush) | ✅ Verified | Provider config (see
|
|
213
|
-
| [Cline](https://github.com/cline/cline) | ✅ Verified | Config (see
|
|
214
|
-
| [
|
|
215
|
-
| [
|
|
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
|
|
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
|
|
231
|
-
│ ├── droid.ts ← Droid
|
|
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
|
-
├──
|
|
237
|
-
├──
|
|
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
|
-
|
|
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
|
-
###
|
|
236
|
+
### Agent Detection
|
|
261
237
|
|
|
262
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
313
|
-
import { startProxyServer } from "@rynfar/meridian"
|
|
268
|
+
## Endpoints
|
|
314
269
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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
|
-
|
|
323
|
-
// instance.server — underlying http.Server
|
|
282
|
+
Health response example:
|
|
324
283
|
|
|
325
|
-
|
|
326
|
-
|
|
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
|
-
|
|
293
|
+
`plugin.opencode` is `"configured"` when `meridian setup` has been run, `"not-configured"` otherwise.
|
|
330
294
|
|
|
331
|
-
|
|
295
|
+
## CLI Commands
|
|
332
296
|
|
|
333
|
-
|
|
|
334
|
-
|
|
335
|
-
| `
|
|
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
|
-
|
|
303
|
+
## Programmatic API
|
|
338
304
|
|
|
339
|
-
|
|
305
|
+
```typescript
|
|
306
|
+
import { startProxyServer } from "@rynfar/meridian"
|
|
340
307
|
|
|
341
|
-
|
|
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
|
|
395
|
-
npm run build
|
|
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
|
|
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.
|
|
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
|
-
**
|
|
415
|
-
|
|
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
|
-
**
|
|
418
|
-
|
|
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 };
|