@rynfar/meridian 1.21.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.
Files changed (67) hide show
  1. package/README.md +410 -0
  2. package/assets/banner.svg +46 -0
  3. package/assets/how-it-works.svg +87 -0
  4. package/assets/icon.svg +16 -0
  5. package/assets/logo.svg +15 -0
  6. package/dist/cli-zyn4cqp2.js +24392 -0
  7. package/dist/cli.js +46 -0
  8. package/dist/logger.d.ts +5 -0
  9. package/dist/logger.d.ts.map +1 -0
  10. package/dist/mcpTools.d.ts +15 -0
  11. package/dist/mcpTools.d.ts.map +1 -0
  12. package/dist/proxy/adapter.d.ts +78 -0
  13. package/dist/proxy/adapter.d.ts.map +1 -0
  14. package/dist/proxy/adapters/crush.d.ts +22 -0
  15. package/dist/proxy/adapters/crush.d.ts.map +1 -0
  16. package/dist/proxy/adapters/detect.d.ts +18 -0
  17. package/dist/proxy/adapters/detect.d.ts.map +1 -0
  18. package/dist/proxy/adapters/droid.d.ts +19 -0
  19. package/dist/proxy/adapters/droid.d.ts.map +1 -0
  20. package/dist/proxy/adapters/opencode.d.ts +9 -0
  21. package/dist/proxy/adapters/opencode.d.ts.map +1 -0
  22. package/dist/proxy/agentDefs.d.ts +51 -0
  23. package/dist/proxy/agentDefs.d.ts.map +1 -0
  24. package/dist/proxy/agentMatch.d.ts +18 -0
  25. package/dist/proxy/agentMatch.d.ts.map +1 -0
  26. package/dist/proxy/errors.d.ts +25 -0
  27. package/dist/proxy/errors.d.ts.map +1 -0
  28. package/dist/proxy/messages.d.ts +24 -0
  29. package/dist/proxy/messages.d.ts.map +1 -0
  30. package/dist/proxy/models.d.ts +46 -0
  31. package/dist/proxy/models.d.ts.map +1 -0
  32. package/dist/proxy/passthroughTools.d.ts +30 -0
  33. package/dist/proxy/passthroughTools.d.ts.map +1 -0
  34. package/dist/proxy/query.d.ts +105 -0
  35. package/dist/proxy/query.d.ts.map +1 -0
  36. package/dist/proxy/server.d.ts +10 -0
  37. package/dist/proxy/server.d.ts.map +1 -0
  38. package/dist/proxy/session/cache.d.ts +32 -0
  39. package/dist/proxy/session/cache.d.ts.map +1 -0
  40. package/dist/proxy/session/fingerprint.d.ts +31 -0
  41. package/dist/proxy/session/fingerprint.d.ts.map +1 -0
  42. package/dist/proxy/session/lineage.d.ts +103 -0
  43. package/dist/proxy/session/lineage.d.ts.map +1 -0
  44. package/dist/proxy/sessionStore.d.ts +36 -0
  45. package/dist/proxy/sessionStore.d.ts.map +1 -0
  46. package/dist/proxy/tools.d.ts +26 -0
  47. package/dist/proxy/tools.d.ts.map +1 -0
  48. package/dist/proxy/types.d.ts +27 -0
  49. package/dist/proxy/types.d.ts.map +1 -0
  50. package/dist/server.js +18 -0
  51. package/dist/telemetry/dashboard.d.ts +6 -0
  52. package/dist/telemetry/dashboard.d.ts.map +1 -0
  53. package/dist/telemetry/index.d.ts +7 -0
  54. package/dist/telemetry/index.d.ts.map +1 -0
  55. package/dist/telemetry/landing.d.ts +7 -0
  56. package/dist/telemetry/landing.d.ts.map +1 -0
  57. package/dist/telemetry/logStore.d.ts +50 -0
  58. package/dist/telemetry/logStore.d.ts.map +1 -0
  59. package/dist/telemetry/routes.d.ts +11 -0
  60. package/dist/telemetry/routes.d.ts.map +1 -0
  61. package/dist/telemetry/store.d.ts +39 -0
  62. package/dist/telemetry/store.d.ts.map +1 -0
  63. package/dist/telemetry/types.d.ts +93 -0
  64. package/dist/telemetry/types.d.ts.map +1 -0
  65. package/dist/utils/lruMap.d.ts +28 -0
  66. package/dist/utils/lruMap.d.ts.map +1 -0
  67. package/package.json +77 -0
package/README.md ADDED
@@ -0,0 +1,410 @@
1
+ <p align="center">
2
+ <img src="assets/banner.svg" alt="Meridian" width="800"/>
3
+ </p>
4
+
5
+ <p align="center">
6
+ <a href="https://github.com/rynfar/meridian/releases"><img src="https://img.shields.io/github/v/release/rynfar/@rynfar/meridian?style=flat-square&color=6366f1&label=release" alt="Release"></a>
7
+ <a href="https://www.npmjs.com/package/@rynfar/meridian"><img src="https://img.shields.io/npm/v/@rynfar/meridian?style=flat-square&color=8b5cf6&label=npm" alt="npm"></a>
8
+ <a href="#"><img src="https://img.shields.io/badge/platform-macOS%20%7C%20Linux%20%7C%20Windows-a78bfa?style=flat-square" alt="Platform"></a>
9
+ <a href="#"><img src="https://img.shields.io/badge/license-MIT-c4b5fd?style=flat-square" alt="License"></a>
10
+ </p>
11
+
12
+ ---
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.
17
+
18
+ > [!NOTE]
19
+ > **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.
20
+
21
+ ## Quick Start
22
+
23
+ ```bash
24
+ # Install
25
+ npm install -g @rynfar/meridian
26
+
27
+ # Authenticate (one time)
28
+ claude login
29
+
30
+ # Start
31
+ meridian
32
+ ```
33
+
34
+ Meridian starts on `http://127.0.0.1:3456`. Point any Anthropic-compatible tool at it:
35
+
36
+ ```bash
37
+ ANTHROPIC_API_KEY=x ANTHROPIC_BASE_URL=http://127.0.0.1:3456 opencode
38
+ ```
39
+
40
+ The API key value doesn't matter — Meridian authenticates through your Claude Max session, not API keys.
41
+
42
+ ## Why Meridian?
43
+
44
+ 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
+ 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
+
48
+ <p align="center">
49
+ <img src="assets/how-it-works.svg" alt="How Meridian works" width="920"/>
50
+ </p>
51
+
52
+ ## Features
53
+
54
+ - **Standard Anthropic API** — drop-in compatible with any tool that supports custom `base_url`
55
+ - **Session management** — conversations persist across requests, survive compaction and undo, resume after proxy restarts
56
+ - **Streaming** — full SSE streaming with MCP tool filtering
57
+ - **Concurrent sessions** — run parent + subagent requests in parallel
58
+ - **Passthrough mode** — forward tool calls to the client instead of executing internally
59
+ - **Multimodal** — images, documents, and file attachments pass through to Claude
60
+ - **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
+ ## Agent Setup
65
+
66
+ ### OpenCode
67
+
68
+ ```bash
69
+ ANTHROPIC_API_KEY=x ANTHROPIC_BASE_URL=http://127.0.0.1:3456 opencode
70
+ ```
71
+
72
+ For automatic session tracking, use a plugin like [opencode-meridian](https://github.com/ianjwhite99/opencode-meridian), or see the [reference plugin](examples/opencode-plugin/claude-max-headers.ts) to build your own.
73
+
74
+ ### Crush
75
+
76
+ Add a provider to `~/.config/crush/crush.json`:
77
+
78
+ ```json
79
+ {
80
+ "providers": {
81
+ "claude-max": {
82
+ "id": "claude-max",
83
+ "name": "Claude Max (Meridian)",
84
+ "type": "anthropic",
85
+ "base_url": "http://127.0.0.1:3456",
86
+ "api_key": "dummy",
87
+ "models": [
88
+ { "id": "claude-sonnet-4-6", "name": "Claude Sonnet 4.6 (1M)", "context_window": 1000000, "default_max_tokens": 64000, "can_reason": true, "supports_attachments": true },
89
+ { "id": "claude-opus-4-6", "name": "Claude Opus 4.6 (1M)", "context_window": 1000000, "default_max_tokens": 32768, "can_reason": true, "supports_attachments": true },
90
+ { "id": "claude-haiku-4-5-20251001", "name": "Claude Haiku 4.5", "context_window": 200000, "default_max_tokens": 16384, "can_reason": true, "supports_attachments": true }
91
+ ]
92
+ }
93
+ }
94
+ }
95
+ ```
96
+
97
+ Then use Meridian models in Crush:
98
+
99
+ ```bash
100
+ crush run --model claude-max/claude-sonnet-4-6 "refactor this function"
101
+ crush --model claude-max/claude-opus-4-6 # interactive TUI
102
+ ```
103
+
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.
105
+
106
+ ### Droid (Factory AI)
107
+
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`:
111
+
112
+ ```json
113
+ {
114
+ "customModels": [
115
+ {
116
+ "model": "claude-sonnet-4-6",
117
+ "name": "Sonnet 4.6 (1M — Claude Max)",
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 — Claude Max)",
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 (Claude Max)",
132
+ "provider": "anthropic",
133
+ "baseUrl": "http://127.0.0.1:3456",
134
+ "apiKey": "x"
135
+ }
136
+ ]
137
+ }
138
+ ```
139
+
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.
154
+
155
+ ### Cline
156
+
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:**
160
+
161
+ ```bash
162
+ cline auth --provider anthropic --apikey "dummy" --modelid "claude-sonnet-4-6"
163
+ ```
164
+
165
+ **2. Add the proxy base URL** to `~/.cline/data/globalState.json`:
166
+
167
+ ```json
168
+ {
169
+ "anthropicBaseUrl": "http://127.0.0.1:3456",
170
+ "actModeApiProvider": "anthropic",
171
+ "actModeApiModelId": "claude-sonnet-4-6"
172
+ }
173
+ ```
174
+
175
+ **3. Run Cline:**
176
+
177
+ ```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)
181
+ ```
182
+
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.
184
+
185
+ ### Any Anthropic-compatible tool
186
+
187
+ ```bash
188
+ export ANTHROPIC_API_KEY=x
189
+ export ANTHROPIC_BASE_URL=http://127.0.0.1:3456
190
+ # Then start your tool normally
191
+ ```
192
+
193
+ ## Tested Agents
194
+
195
+ | Agent | Status | Plugin | Notes |
196
+ |-------|--------|--------|-------|
197
+ | [OpenCode](https://github.com/anomalyco/opencode) | ✅ Verified | [opencode-meridian](https://github.com/ianjwhite99/opencode-meridian) | Full tool support, session resume, streaming, subagents |
198
+ | [Droid (Factory AI)](https://factory.ai/product/ide) | ✅ Verified | BYOK config (see setup above) | Full tool support, session resume, streaming; one-time BYOK setup |
199
+ | [Crush](https://github.com/charmbracelet/crush) | ✅ Verified | Provider config (see setup above) | Full tool support, session resume, streaming, headless `crush run` |
200
+ | [Cline](https://github.com/cline/cline) | ✅ Verified | Config (see setup above) | Full tool support, file read/write/edit, bash, session resume, all models |
201
+ | [Continue](https://github.com/continuedev/continue) | 🔲 Untested | — | Should work — standard Anthropic API |
202
+ | [Aider](https://github.com/paul-gauthier/aider) | 🔲 Untested | — | Should work — standard Anthropic API |
203
+
204
+ Tested an agent or built a plugin? [Open an issue](https://github.com/rynfar/meridian/issues) and we'll add it.
205
+
206
+ ## Architecture
207
+
208
+ Meridian is built as a modular proxy with clean separation of concerns:
209
+
210
+ ```
211
+ src/proxy/
212
+ ├── server.ts ← HTTP orchestration (routes, SSE streaming, concurrency)
213
+ ├── adapter.ts ← AgentAdapter interface (extensibility point)
214
+ ├── adapters/
215
+ │ ├── detect.ts ← Agent detection from request headers
216
+ │ ├── opencode.ts ← OpenCode adapter
217
+ │ └── droid.ts ← Droid (Factory AI) adapter
218
+ ├── query.ts ← SDK query options builder
219
+ ├── errors.ts ← Error classification
220
+ ├── models.ts ← Model mapping (sonnet/opus/haiku)
221
+ ├── tools.ts ← Tool blocking lists
222
+ ├── messages.ts ← Content normalization
223
+ ├── session/
224
+ │ ├── lineage.ts ← Per-message hashing, mutation classification (pure)
225
+ │ ├── fingerprint.ts ← Conversation fingerprinting
226
+ │ └── cache.ts ← LRU session caches
227
+ ├── sessionStore.ts ← Cross-proxy file-based session persistence
228
+ ├── agentDefs.ts ← Subagent definition extraction
229
+ └── passthroughTools.ts ← Tool forwarding mode
230
+ ```
231
+
232
+ ### Session Management
233
+
234
+ Sessions map agent conversations to Claude SDK sessions. Meridian classifies every incoming request:
235
+
236
+ | Classification | What Happened | Action |
237
+ |---------------|---------------|--------|
238
+ | **Continuation** | New messages appended | Resume SDK session |
239
+ | **Compaction** | Agent summarized old messages | Resume (suffix preserved) |
240
+ | **Undo** | User rolled back messages | Fork at rollback point |
241
+ | **Diverged** | Completely different conversation | Start fresh |
242
+
243
+ Sessions are stored in-memory (LRU) and persisted to `~/.cache/@rynfar/meridian/sessions.json` for cross-proxy resume.
244
+
245
+ ### Adding a New Agent
246
+
247
+ Implement the `AgentAdapter` interface in `src/proxy/adapters/`:
248
+
249
+ ```typescript
250
+ interface AgentAdapter {
251
+ // Required
252
+ getSessionId(c: Context): string | undefined
253
+ extractWorkingDirectory(body: any): string | undefined
254
+ normalizeContent(content: any): string
255
+ getBlockedBuiltinTools(): readonly string[]
256
+ getAgentIncompatibleTools(): readonly string[]
257
+ getMcpServerName(): string
258
+ getAllowedMcpTools(): readonly string[]
259
+
260
+ // Optional
261
+ buildSdkAgents?(body: any, mcpToolNames: readonly string[]): Record<string, any>
262
+ buildSdkHooks?(body: any, sdkAgents: Record<string, any>): any
263
+ buildSystemContextAddendum?(body: any, sdkAgents: Record<string, any>): string
264
+ usesPassthrough?(): boolean // overrides CLAUDE_PROXY_PASSTHROUGH per-agent
265
+ }
266
+ ```
267
+
268
+ Agent detection is automatic from the `User-Agent` header:
269
+
270
+ | User-Agent prefix | Adapter |
271
+ |---|---|
272
+ | `Charm-Crush/` | Crush |
273
+ | `factory-cli/` | Droid |
274
+ | *(anything else)* | OpenCode (default) |
275
+
276
+ See [`adapters/detect.ts`](src/proxy/adapters/detect.ts) and [`adapters/opencode.ts`](src/proxy/adapters/opencode.ts) for reference.
277
+
278
+ ## Configuration
279
+
280
+ | Variable | Default | Description |
281
+ |----------|---------|-------------|
282
+ | `CLAUDE_PROXY_PORT` | `3456` | Port to listen on |
283
+ | `CLAUDE_PROXY_HOST` | `127.0.0.1` | Host to bind to |
284
+ | `CLAUDE_PROXY_PASSTHROUGH` | unset | Forward tool calls to client instead of executing |
285
+ | `CLAUDE_PROXY_MAX_CONCURRENT` | `10` | Maximum concurrent SDK sessions |
286
+ | `CLAUDE_PROXY_MAX_SESSIONS` | `1000` | In-memory LRU session cache size |
287
+ | `CLAUDE_PROXY_MAX_STORED_SESSIONS` | `10000` | File-based session store capacity |
288
+ | `CLAUDE_PROXY_WORKDIR` | `cwd()` | Default working directory for SDK |
289
+ | `CLAUDE_PROXY_IDLE_TIMEOUT_SECONDS` | `120` | HTTP keep-alive timeout |
290
+ | `CLAUDE_PROXY_TELEMETRY_SIZE` | `1000` | Telemetry ring buffer size |
291
+
292
+ ## Programmatic API
293
+
294
+ Meridian can be used as a library for building agent plugins and integrations.
295
+
296
+ ```typescript
297
+ import { startProxyServer } from "@rynfar/meridian"
298
+
299
+ // Start a proxy instance
300
+ const instance = await startProxyServer({
301
+ port: 3456,
302
+ host: "127.0.0.1",
303
+ silent: true, // suppress console output
304
+ })
305
+
306
+ // instance.config — resolved ProxyConfig
307
+ // instance.server — underlying http.Server
308
+
309
+ // Shut down cleanly
310
+ await instance.close()
311
+ ```
312
+
313
+ ### Session Header Contract
314
+
315
+ 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.
316
+
317
+ | Header | Purpose |
318
+ |--------|---------|
319
+ | `x-opencode-session` | Maps agent conversations to Claude SDK sessions for resume, undo, and compaction |
320
+
321
+ The proxy uses this header to maintain conversation continuity across requests. Plugin authors should inject it on every request to `/v1/messages`.
322
+
323
+ ### Plugin Architecture
324
+
325
+ Meridian is the proxy. Plugins live in the agent's ecosystem.
326
+
327
+ ```
328
+ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
329
+ │ Agent │ HTTP │ Meridian │ SDK │ Claude Max │
330
+ │ (OpenCode, │────────▶│ Proxy │────────▶│ │
331
+ │ Crush, etc) │◀────────│ │◀────────│ │
332
+ └──────────────┘ └──────────────┘ └──────────────┘
333
+
334
+ │ plugin injects headers,
335
+ │ manages proxy lifecycle
336
+
337
+ ┌──────────────┐
338
+ │ Agent Plugin │
339
+ │ (optional) │
340
+ └──────────────┘
341
+ ```
342
+
343
+ A plugin's job is to:
344
+ 1. Start/stop a Meridian instance (`startProxyServer` / `instance.close()`)
345
+ 2. Inject session headers into outgoing requests
346
+ 3. Check proxy health (`GET /health`)
347
+
348
+ See [`examples/opencode-plugin/`](examples/opencode-plugin/) for a reference implementation.
349
+
350
+ ## Endpoints
351
+
352
+ | Endpoint | Description |
353
+ |----------|-------------|
354
+ | `GET /` | Landing page (HTML) or status JSON (`Accept: application/json`) |
355
+ | `POST /v1/messages` | Anthropic Messages API |
356
+ | `POST /messages` | Alias for `/v1/messages` |
357
+ | `GET /health` | Auth status, subscription type, mode |
358
+ | `GET /telemetry` | Performance dashboard |
359
+ | `GET /telemetry/requests` | Recent request metrics (JSON) |
360
+ | `GET /telemetry/summary` | Aggregate statistics (JSON) |
361
+ | `GET /telemetry/logs` | Diagnostic logs (JSON) |
362
+
363
+ ## Docker
364
+
365
+ ```bash
366
+ docker run -v ~/.claude:/home/claude/.claude -p 3456:3456 meridian
367
+ ```
368
+
369
+ Or with docker-compose:
370
+
371
+ ```bash
372
+ docker compose up -d
373
+ ```
374
+
375
+ ## Testing
376
+
377
+ ```bash
378
+ npm test # 339 unit/integration tests (bun test)
379
+ npm run build # Build with bun + tsc
380
+ ```
381
+
382
+ Three test tiers:
383
+
384
+ | Tier | What | Speed |
385
+ |------|------|-------|
386
+ | Unit | Pure functions, no mocks | Fast |
387
+ | Integration | HTTP layer with mocked SDK | Fast |
388
+ | E2E | Real proxy + real Claude Max ([`E2E.md`](E2E.md)) | Manual |
389
+
390
+ ## FAQ
391
+
392
+ **Is this allowed by Anthropic's terms?**
393
+ 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.
394
+
395
+ **How is this different from using an API key?**
396
+ 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.
397
+
398
+ **Does it work with Claude Pro?**
399
+ It works with any Claude subscription that supports the Claude Code SDK. Max is recommended for the best rate limits.
400
+
401
+ **What happens if my session expires?**
402
+ The SDK handles token refresh automatically. If it can't refresh, Meridian returns a clear error telling you to run `claude login`.
403
+
404
+ ## Contributing
405
+
406
+ Issues and PRs welcome. See [`ARCHITECTURE.md`](ARCHITECTURE.md) for module structure and dependency rules, [`CLAUDE.md`](CLAUDE.md) for coding guidelines, and [`E2E.md`](E2E.md) for end-to-end test procedures.
407
+
408
+ ## License
409
+
410
+ MIT
@@ -0,0 +1,46 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 400" fill="none">
2
+ <defs>
3
+ <linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#0f0b1a"/>
5
+ <stop offset="100%" stop-color="#1a1030"/>
6
+ </linearGradient>
7
+ </defs>
8
+
9
+ <rect width="1200" height="400" rx="16" fill="url(#bg)"/>
10
+
11
+ <!-- Subtle dot grid -->
12
+ <g opacity="0.05" fill="#a78bfa">
13
+ <circle cx="100" cy="100" r="1.5"/><circle cx="200" cy="100" r="1.5"/><circle cx="300" cy="100" r="1.5"/>
14
+ <circle cx="100" cy="200" r="1.5"/><circle cx="200" cy="200" r="1.5"/><circle cx="300" cy="200" r="1.5"/>
15
+ <circle cx="100" cy="300" r="1.5"/><circle cx="200" cy="300" r="1.5"/><circle cx="300" cy="300" r="1.5"/>
16
+ <circle cx="900" cy="100" r="1.5"/><circle cx="1000" cy="100" r="1.5"/><circle cx="1100" cy="100" r="1.5"/>
17
+ <circle cx="900" cy="200" r="1.5"/><circle cx="1000" cy="200" r="1.5"/><circle cx="1100" cy="200" r="1.5"/>
18
+ <circle cx="900" cy="300" r="1.5"/><circle cx="1000" cy="300" r="1.5"/><circle cx="1100" cy="300" r="1.5"/>
19
+ </g>
20
+
21
+ <!-- Logo mark — V2 full globe -->
22
+ <g transform="translate(380, 90)">
23
+ <!-- Meridian axis -->
24
+ <line x1="80" y1="8" x2="80" y2="192" stroke="#8B7CF6" stroke-width="5" stroke-linecap="round"/>
25
+ <!-- Latitude arcs — outer -->
26
+ <path d="M28 50 A60 60 0 0 1 132 50" fill="none" stroke="#C4B5FD" stroke-width="2.5" opacity="0.4"/>
27
+ <path d="M28 150 A60 60 0 0 0 132 150" fill="none" stroke="#C4B5FD" stroke-width="2.5" opacity="0.4"/>
28
+ <!-- Latitude arcs — inner -->
29
+ <path d="M38 78 A46 46 0 0 1 122 78" fill="none" stroke="#C4B5FD" stroke-width="1.5" opacity="0.2"/>
30
+ <path d="M38 122 A46 46 0 0 0 122 122" fill="none" stroke="#C4B5FD" stroke-width="1.5" opacity="0.2"/>
31
+ <!-- Poles -->
32
+ <circle cx="80" cy="8" r="8" fill="#C4B5FD"/>
33
+ <circle cx="80" cy="192" r="8" fill="#C4B5FD"/>
34
+ <!-- Center node -->
35
+ <circle cx="80" cy="100" r="8" fill="#8B7CF6"/>
36
+ </g>
37
+
38
+ <!-- Wordmark -->
39
+ <text x="540" y="195" font-family="Inter, -apple-system, 'SF Pro Display', 'Segoe UI', Helvetica, Arial, sans-serif" font-size="72" font-weight="700" fill="#e0e7ff" letter-spacing="10">MERIDIAN</text>
40
+
41
+ <!-- Tagline -->
42
+ <text x="543" y="240" font-family="Inter, -apple-system, 'SF Pro Display', 'Segoe UI', Helvetica, Arial, sans-serif" font-size="17" font-weight="600" fill="#8B7CF6" letter-spacing="3">HARNESS CLAUDE, YOUR WAY.</text>
43
+
44
+ <!-- Subtitle -->
45
+ <text x="543" y="278" font-family="Inter, -apple-system, 'SF Pro Display', 'Segoe UI', Helvetica, Arial, sans-serif" font-size="14" font-weight="300" fill="rgba(255,255,255,0.35)">Local Anthropic API powered by the Claude Code SDK</text>
46
+ </svg>
@@ -0,0 +1,87 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 920 310" fill="none">
2
+ <style>
3
+ .title { font-family: 'Inter', -apple-system, sans-serif; font-size: 20px; font-weight: 700; fill: #C4B5FD; }
4
+ .box-label { font-family: 'Inter', -apple-system, sans-serif; font-size: 13px; font-weight: 600; fill: #F5F5F7; }
5
+ .box-sub { font-family: 'JetBrains Mono', 'SF Mono', monospace; font-size: 10px; fill: #8B7CF6; }
6
+ .box-detail { font-family: 'Inter', -apple-system, sans-serif; font-size: 10px; fill: #9898B4; }
7
+ .tag { font-family: 'Inter', -apple-system, sans-serif; font-size: 9px; font-weight: 600; fill: #5E5B72; letter-spacing: 1px; }
8
+ .step-num { font-family: 'Inter', -apple-system, sans-serif; font-size: 10px; font-weight: 700; }
9
+ .footer { font-family: 'Inter', -apple-system, sans-serif; font-size: 11px; fill: #5E5B72; }
10
+ .arrow-label { font-family: 'Inter', -apple-system, sans-serif; font-size: 9px; fill: #5E5B72; text-anchor: middle; }
11
+ </style>
12
+
13
+ <rect width="920" height="310" rx="16" fill="#13111C"/>
14
+ <rect x="0.5" y="0.5" width="919" height="309" rx="15.5" stroke="#2A2740" stroke-width="1" fill="none"/>
15
+
16
+ <text class="title" x="460" y="38" text-anchor="middle">How it works</text>
17
+
18
+ <!-- ═══ STEP 1: YOUR TOOLS ═══ -->
19
+ <rect x="32" y="64" width="156" height="140" rx="12" fill="#1C1930" stroke="#2A2740" stroke-width="1"/>
20
+ <rect x="44" y="54" width="18" height="18" rx="9" fill="#8B7CF6"/>
21
+ <text class="step-num" x="53" y="67" text-anchor="middle" fill="#13111C">1</text>
22
+ <text class="box-label" x="110" y="100" text-anchor="middle">Your tools</text>
23
+ <text class="box-detail" x="110" y="122" text-anchor="middle">OpenCode, Cline</text>
24
+ <text class="box-detail" x="110" y="136" text-anchor="middle">Continue, Crush, aider</text>
25
+ <text class="box-detail" x="110" y="150" text-anchor="middle">or any Anthropic client</text>
26
+ <text class="tag" x="110" y="192" text-anchor="middle">ANTHROPIC API FORMAT</text>
27
+
28
+ <!-- ═══ ARROWS 1 ↔ 2 ═══ -->
29
+ <line x1="200" y1="122" x2="250" y2="122" stroke="#8B7CF6" stroke-width="1.5"/>
30
+ <polygon points="248,118 256,122 248,126" fill="#8B7CF6"/>
31
+ <text class="arrow-label" x="226" y="114">request</text>
32
+ <line x1="250" y1="142" x2="200" y2="142" stroke="#3D3858" stroke-width="1"/>
33
+ <polygon points="202,138 194,142 202,146" fill="#3D3858"/>
34
+ <text class="arrow-label" x="226" y="162">response</text>
35
+
36
+ <!-- ═══ STEP 2: MERIDIAN ═══ -->
37
+ <rect x="264" y="64" width="156" height="140" rx="12" fill="#1C1930" stroke="#8B7CF6" stroke-width="1.5"/>
38
+ <rect x="276" y="54" width="18" height="18" rx="9" fill="#8B7CF6"/>
39
+ <text class="step-num" x="285" y="67" text-anchor="middle" fill="#13111C">2</text>
40
+ <text class="box-label" x="342" y="100" text-anchor="middle">Meridian</text>
41
+ <text class="box-sub" x="342" y="118" text-anchor="middle">localhost:3456</text>
42
+ <text class="box-detail" x="342" y="140" text-anchor="middle">Local proxy server</text>
43
+ <text class="box-detail" x="342" y="154" text-anchor="middle">Translates API calls</text>
44
+ <text class="box-detail" x="342" y="168" text-anchor="middle">to Claude Code SDK</text>
45
+ <text class="tag" x="342" y="192" text-anchor="middle">BRIDGES THE GAP</text>
46
+
47
+ <!-- ═══ ARROWS 2 ↔ 3 ═══ -->
48
+ <line x1="432" y1="122" x2="482" y2="122" stroke="#8B7CF6" stroke-width="1.5"/>
49
+ <polygon points="480,118 488,122 480,126" fill="#8B7CF6"/>
50
+ <text class="arrow-label" x="458" y="114">SDK call</text>
51
+ <line x1="482" y1="142" x2="432" y2="142" stroke="#3D3858" stroke-width="1"/>
52
+ <polygon points="434,138 426,142 434,146" fill="#3D3858"/>
53
+ <text class="arrow-label" x="458" y="162">result</text>
54
+
55
+ <!-- ═══ STEP 3: CLAUDE CODE SDK ═══ -->
56
+ <rect x="496" y="64" width="172" height="140" rx="12" fill="#1C1930" stroke="#2A2740" stroke-width="1"/>
57
+ <rect x="508" y="54" width="18" height="18" rx="9" fill="#8B7CF6"/>
58
+ <text class="step-num" x="517" y="67" text-anchor="middle" fill="#13111C">3</text>
59
+ <text class="box-label" x="582" y="100" text-anchor="middle">Claude Code SDK</text>
60
+ <text class="box-sub" x="582" y="118" text-anchor="middle">@anthropic-ai/claude-code</text>
61
+ <text class="box-detail" x="582" y="140" text-anchor="middle">Official Anthropic SDK</text>
62
+ <text class="box-detail" x="582" y="154" text-anchor="middle">Handles auth + sessions</text>
63
+ <text class="tag" x="582" y="192" text-anchor="middle">ANTHROPIC'S OWN SDK</text>
64
+
65
+ <!-- ═══ ARROWS 3 ↔ 4 ═══ -->
66
+ <line x1="680" y1="122" x2="726" y2="122" stroke="#8B7CF6" stroke-width="1.5"/>
67
+ <polygon points="724,118 732,122 724,126" fill="#8B7CF6"/>
68
+ <text class="arrow-label" x="704" y="114">API call</text>
69
+ <line x1="726" y1="142" x2="680" y2="142" stroke="#3D3858" stroke-width="1"/>
70
+ <polygon points="682,138 674,142 682,146" fill="#3D3858"/>
71
+ <text class="arrow-label" x="704" y="162">stream</text>
72
+
73
+ <!-- ═══ STEP 4: CLAUDE ═══ -->
74
+ <rect x="740" y="64" width="140" height="140" rx="12" fill="#1C1930" stroke="#2A2740" stroke-width="1"/>
75
+ <rect x="752" y="54" width="18" height="18" rx="9" fill="#8B7CF6"/>
76
+ <text class="step-num" x="761" y="67" text-anchor="middle" fill="#13111C">4</text>
77
+ <text class="box-label" x="810" y="100" text-anchor="middle">Claude</text>
78
+ <text class="box-sub" x="810" y="118" text-anchor="middle">api.anthropic.com</text>
79
+ <text class="box-detail" x="810" y="140" text-anchor="middle">Opus, Sonnet, Haiku</text>
80
+ <text class="box-detail" x="810" y="154" text-anchor="middle">Extended thinking</text>
81
+ <text class="tag" x="810" y="192" text-anchor="middle">YOUR SUBSCRIPTION</text>
82
+
83
+ <!-- ═══ BOTTOM ═══ -->
84
+ <line x1="60" y1="246" x2="860" y2="246" stroke="#2A2740" stroke-width="1"/>
85
+ <text class="footer" x="460" y="274" text-anchor="middle">No API keys needed — authenticates through your existing Claude Code / Max subscription</text>
86
+
87
+ </svg>
@@ -0,0 +1,16 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" fill="none">
2
+ <rect width="64" height="64" rx="14" fill="#1C1830"/>
3
+ <!-- Meridian axis -->
4
+ <line x1="32" y1="10" x2="32" y2="54" stroke="#8B7CF6" stroke-width="2.5" stroke-linecap="round"/>
5
+ <!-- Latitude arcs — outer -->
6
+ <path d="M16 20 A18 18 0 0 1 48 20" fill="none" stroke="#C4B5FD" stroke-width="1.2" opacity="0.4"/>
7
+ <path d="M16 44 A18 18 0 0 0 48 44" fill="none" stroke="#C4B5FD" stroke-width="1.2" opacity="0.4"/>
8
+ <!-- Latitude arcs — inner -->
9
+ <path d="M20 30 A14 14 0 0 1 44 30" fill="none" stroke="#C4B5FD" stroke-width="0.8" opacity="0.2"/>
10
+ <path d="M20 34 A14 14 0 0 0 44 34" fill="none" stroke="#C4B5FD" stroke-width="0.8" opacity="0.2"/>
11
+ <!-- Poles -->
12
+ <circle cx="32" cy="10" r="3.5" fill="#C4B5FD"/>
13
+ <circle cx="32" cy="54" r="3.5" fill="#C4B5FD"/>
14
+ <!-- Center node -->
15
+ <circle cx="32" cy="32" r="3" fill="#8B7CF6"/>
16
+ </svg>
@@ -0,0 +1,15 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="none">
2
+ <!-- Meridian axis -->
3
+ <line x1="256" y1="40" x2="256" y2="472" stroke="#8B7CF6" stroke-width="12" stroke-linecap="round"/>
4
+ <!-- Latitude arcs — outer pair -->
5
+ <path d="M110 130 A166 166 0 0 1 402 130" fill="none" stroke="#C4B5FD" stroke-width="7" opacity="0.4"/>
6
+ <path d="M110 382 A166 166 0 0 0 402 382" fill="none" stroke="#C4B5FD" stroke-width="7" opacity="0.4"/>
7
+ <!-- Latitude arcs — inner pair -->
8
+ <path d="M132 210 A136 136 0 0 1 380 210" fill="none" stroke="#C4B5FD" stroke-width="5" opacity="0.25"/>
9
+ <path d="M132 302 A136 136 0 0 0 380 302" fill="none" stroke="#C4B5FD" stroke-width="5" opacity="0.25"/>
10
+ <!-- Poles -->
11
+ <circle cx="256" cy="40" r="22" fill="#C4B5FD"/>
12
+ <circle cx="256" cy="472" r="22" fill="#C4B5FD"/>
13
+ <!-- Center node -->
14
+ <circle cx="256" cy="256" r="20" fill="#8B7CF6"/>
15
+ </svg>