saros-proxy 0.1.0 → 0.2.0

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 (2) hide show
  1. package/README.md +147 -197
  2. package/package.json +4 -1
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  Saros — like the eclipse cycle, this proxy predicts exhaustion and rotates accounts before they run out. Built for the OpenCode-Go API.
9
9
 
10
- Manage multiple OpenCode-Go API keys behind a single endpoint. When one key is rate-limited, revoked, or returns server errors, the proxy automatically fails over to the next healthy key — with zero downtime.
10
+ Manage multiple OpenCode-Go API keys behind a single endpoint. When one key gets rate-limited, revoked, or returns server errors, the proxy automatically fails over to the next healthy key — zero downtime.
11
11
 
12
12
  ---
13
13
 
@@ -16,14 +16,16 @@ Manage multiple OpenCode-Go API keys behind a single endpoint. When one key is r
16
16
  - [Features](#features)
17
17
  - [Architecture](#architecture)
18
18
  - [Installation](#installation)
19
- - [Docker](#docker)
20
- - [Quick Start](#quick-start)
19
+ - [Prompt Install](#prompt-installfor-the-lazy-ones)
20
+ - [Quick Install (npm)](#quick-install-npm)
21
+ - [From Source](#from-source)
22
+ - [Docker](#docker)
23
+ - [Setup Wizard](#setup-wizard)
21
24
  - [Configuration Reference](#configuration-reference)
22
25
  - [Usage Examples](#usage-examples)
23
26
  - [OpenCode Integration](#opencode-integration)
24
27
  - [Troubleshooting](#troubleshooting)
25
28
  - [Development](#development)
26
- - [LLM Quick Reference](#llm-quick-reference)
27
29
  - [Security](#security)
28
30
  - [License](#license)
29
31
 
@@ -31,15 +33,15 @@ Manage multiple OpenCode-Go API keys behind a single endpoint. When one key is r
31
33
 
32
34
  ## Features
33
35
 
34
- - **Multi-key rotation** — Round-robin distribution across API keys.
35
- - **Automatic failover** — Retry failed requests with the next available key.
36
- - **Circuit breaker** — Disable failing keys after a configurable threshold, then lazily re-enable them after a cooldown period.
37
- - **Streaming support** — SSE pass-through with mid-stream error detection.
38
- - **Request timeout** — Configurable timeout with graceful 504 response.
39
- - **Graceful shutdown** — Drains active requests before exiting.
36
+ - **Multi-key rotation** — Round-robin across all your API keys.
37
+ - **Auto failover** — Retry failed requests with the next healthy key.
38
+ - **Circuit breaker** — Disable flaky keys after a configurable threshold, then lazily re-enable them after cooldown.
39
+ - **Streaming support** — SSE passthrough with mid-stream error detection.
40
+ - **Request timeout** — Configurable timeout with a clean 504 response.
41
+ - **Graceful shutdown** — Drains active requests before exiting (no dropped connections).
40
42
  - **Security hardening** — API key masking in logs, SSRF prevention (HTTPS-only upstream), configurable CORS, host binding validation.
41
- - **Config validation** — CLI args, YAML file, and environment variables with sensible defaults.
42
- - **Health endpoint** — `GET /health` returns key status, active requests, and circuit-breaker state.
43
+ - **Config validation** — CLI args, YAML file, or environment variables with sensible defaults.
44
+ - **Health endpoint** — `GET /health` shows key status, active requests, and circuit-breaker state.
43
45
  - **No database** — Stateless proxy, zero external dependencies beyond Node.js.
44
46
 
45
47
  ---
@@ -47,48 +49,49 @@ Manage multiple OpenCode-Go API keys behind a single endpoint. When one key is r
47
49
  ## Architecture
48
50
 
49
51
  ```
50
- ┌──────────────────────────────────────────────────────┐
51
- │ Client / OpenCode │
52
- │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
53
- │ │ Request 1│ │ Request 2│ │ Request 3│ ... │
54
- │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
55
- └───────┼──────────────┼──────────────┼────────────────┘
56
- │ │ │
57
- ▼ ▼ ▼
58
- ┌──────────────────────────────────────────────────────┐
59
- │ Saros (:3000) │
60
- │ │
61
- │ ┌─────────────┐ ┌────────────┐ ┌─────────────┐ │
62
- │ │ Key Selector│──▶│ Circuit │──▶│ Request │ │
63
- │ │ Round-robin │ │ Breaker │ │ Forwarder │ │
64
- │ │ + Failover │ │ Threshold │ │ + Timeout │ │
65
- │ └─────────────┘ │ + Cooldown │ └──────┬──────┘ │
66
- │ └────────────┘ │ │
67
- │ ┌─────────────────────────────────────────┘ │
68
- │ │ ┌─────────────────────┐ │
69
- │ └──▶ Streaming Detector │ SSE passthrough │
70
- │ └─────────────────────┘ │
71
- └──────────────────────┬───────────────────────────────┘
72
- │
73
- ┌────────────┼────────────┐
74
- ▼ ▼ ▼
75
- ┌──────────┐ ┌──────────┐ ┌──────────┐
76
- │ Key A │ │ Key B │ │ Key C │
77
- │ sk-abc… │ │ sk-def… │ │ sk-ghi… │
78
- └────┬─────┘ └────┬─────┘ └────┬─────┘
79
- │ │ │
80
- ▼ ▼ ▼
81
- ┌───────────────────────────────────────┐
82
- │ OpenCode-Go API (upstream) │
83
- │ https://opencode.ai │
84
- └───────────────────────────────────────┘
52
+ ┌─────────────────────────────────────────────────────────────┐
53
+ │ Your App / OpenCode │
54
+ │ │
55
+ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
56
+ │ │ Request 1│ │ Request 2│ │ Request 3│ ... │
57
+ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
58
+ └────────┼──────────────┼──────────────┼───────────────────────┘
59
+ │ │ │
60
+ └──────────────┼──────────────┘
61
+ │
62
+ ▼
63
+ ┌─────────────────────────────────────────────────────────────┐
64
+ │ Saros (:3000) │
65
+ │ │
66
+ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
67
+ │ │ Key Selector │─▶│ Circuit │─▶│ Request │ │
68
+ │ │ Round-Robin │ │ Breaker │ │ Forwarder │ │
69
+ │ │ + Failover │ │ Threshold + │ │ + Timeout + │ │
70
+ │ │ │ │ Cooldown │ │ SSE │ │
71
+ │ └──────────────┘ └──────────────┘ └──────┬───────┘ │
72
+ └─────────────────────────────────────────────┼────────────────┘
73
+ │
74
+ ┌───────────┼───────────┐
75
+ ▼ ▼ ▼
76
+ ┌──────────┐ ┌──────────┐ ┌──────────┐
77
+ │ Key A │ │ Key B │ │ Key C │
78
+ │ sk-abc… │ │ sk-def… │ │ sk-ghi… │
79
+ └────┬─────┘ └────┬─────┘ └────┬─────┘
80
+ │ │ │
81
+ └────────────┼────────────┘
82
+ ▼
83
+ ┌──────────────────────────┐
84
+ │ OpenCode-Go API │
85
+ │ https://opencode.ai │
86
+ └──────────────────────────┘
85
87
  ```
86
88
 
87
89
  ### How Request Distribution Works
88
90
 
89
- The proxy uses **two layers** to decide which API key handles each request:
91
+ Here's how Saros picks which API key to use — it runs on two levels:
92
+
93
+ #### Layer 1: Round-Robin (always on)
90
94
 
91
- #### Layer 1: Round-Robin (always active)
92
95
  Every new request cycles to the next key in order:
93
96
 
94
97
  ```
@@ -98,35 +101,50 @@ Request 3 → Key C
98
101
  Request 4 → Key A (wraps around)
99
102
  ```
100
103
 
101
- This gives you **even distribution by default** — no manual odd/even logic needed. If you have 2 accounts, odd requests go to account 1 and even to account 2 automatically.
104
+ Even distribution by default. Got 2 accounts? Odd requests go to account 1, even to account 2. Simple.
102
105
 
103
106
  #### Layer 2: Usage-Based Gating (optional)
104
- If you enable [dashboard scraping](#usage-based-key-selection), the proxy checks each account's quota usage before selecting a key:
105
107
 
106
- - If an account's usage is **≥ threshold** (default 70%), that key is skipped
107
- - If **all** keys are over threshold, the proxy falls back to the **lowest-usage** key
108
- - If no usage data is available, round-robin continues unchanged
108
+ If you enable dashboard scraping, Saros checks each account's quota before picking a key:
109
+
110
+ - Usage **≥ threshold** (default 70%) → that key gets skipped
111
+ - **All** keys over threshold → falls back to the lowest-usage key
112
+ - No usage data available → pure round-robin, no change
109
113
 
110
114
  | Scenario | Behavior |
111
115
  |---|---|
112
- | Scraping disabled | Pure round-robin across all keys |
113
- | Scraping enabled + data available | Round-robin, skipping over-quota accounts |
114
- | Scraping enabled + no data | Falls back to pure round-robin |
116
+ | Scraping off | Pure round-robin across all keys |
117
+ | Scraping on + data available | Round-robin, skipping over-quota accounts |
118
+ | Scraping on + no data | Falls back to pure round-robin |
115
119
 
116
120
  #### Concurrent Request Safety
117
- The proxy tracks which keys are **currently in use** by active requests. A key already handling a streaming request won't be assigned to another request until it completes. This prevents double-booking the same account under load.
121
+
122
+ Saros tracks which keys are currently in use. A key handling a streaming request won't get assigned another one until it finishes. No double-booking.
118
123
 
119
124
  ---
120
125
 
121
126
  ## Installation
122
127
 
123
- ### From npm (when published)
128
+ ### Prompt Install(for the lazy ones)
129
+
130
+ Don't feel like reading through everything? Copy this prompt and paste it to your AI assistant. It'll read [`LLM_INSTRUCTIONS.md`](LLM_INSTRUCTIONS.md) and guide you through setup — no keys shared in chat.
131
+
132
+ ```
133
+ Install Saros Proxy for my opencode. (https://github.com/vitorvilaca3011/saros-proxy)
134
+ Follow the instructions on LLM_INSTRUCTIONS.md.
135
+ ```
136
+
137
+ Your AI will take it from there. For a more detailed reference your assistant can read: [`LLM_INSTRUCTIONS.md`](LLM_INSTRUCTIONS.md).
138
+
139
+ ---
140
+
141
+ ### Quick Install (npm)
124
142
 
125
143
  ```bash
126
144
  npm install -g saros-proxy
127
145
  ```
128
146
 
129
- ### From source
147
+ ### From Source
130
148
 
131
149
  ```bash
132
150
  git clone https://github.com/vitorvilaca3011/saros-proxy.git
@@ -135,22 +153,22 @@ npm install
135
153
  npm run build
136
154
  ```
137
155
 
138
- > **Requirements**: Node.js >= 22.0.0
156
+ > **Requirements:** Node.js >= 22.0.0
139
157
 
140
158
  ---
141
159
 
142
- ## Docker
160
+ ### Docker
143
161
 
144
- Run the proxy in a clean Linux container without installing Node.js locally.
162
+ Run in a clean Linux container without installing Node.js locally.
145
163
 
146
- ### Prerequisites
164
+ #### Prerequisites
147
165
 
148
- - [Docker](https://docs.docker.com/get-docker/) (with Compose v2)
166
+ - [Docker](https://docs.docker.com/get-docker/) (Compose v2)
149
167
 
150
- ### Quick start
168
+ #### Quick start
151
169
 
152
170
  ```bash
153
- # 1. Create config.yaml from the example (edit with your API keys)
171
+ # 1. Create config.yaml from the example (edit with your keys)
154
172
  cp config.example.yaml config.yaml
155
173
 
156
174
  # 2. Build the image
@@ -160,16 +178,15 @@ npm run docker:build
160
178
  npm run docker:run
161
179
  ```
162
180
 
163
- The proxy is now available at `http://127.0.0.1:3000`.
181
+ The proxy is now at `http://127.0.0.1:3000`.
164
182
 
165
- ### Run tests in Docker
183
+ #### Run tests in Docker
166
184
 
167
185
  ```bash
168
- # Run the full test suite inside a Linux container
169
186
  npm run docker:test
170
187
  ```
171
188
 
172
- ### Manual Docker commands
189
+ #### Manual Docker commands
173
190
 
174
191
  ```bash
175
192
  # Build
@@ -191,44 +208,41 @@ docker compose down
191
208
  docker compose run --rm test
192
209
  ```
193
210
 
194
- ### Configuration with Docker
211
+ #### Configuration with Docker
195
212
 
196
213
  Mount a custom `config.yaml` or use environment variables:
197
214
 
198
215
  ```bash
199
- # Using environment variables (no config.yaml needed)
216
+ # Using env vars (no config.yaml needed)
200
217
  docker compose run --rm -e OPENCODE_GO_KEYS="home:sk-xxx,work:sk-yyy" proxy
201
218
 
202
- # Or edit docker-compose.yml to set env vars directly, then:
219
+ # Or edit docker-compose.yml to set env vars, then:
203
220
  docker compose up proxy
204
221
  ```
205
222
 
206
223
  ---
207
224
 
208
- ## Quick Start
225
+ ### Setup Wizard
209
226
 
210
- ### Option A: Interactive Setup (Recommended)
227
+ The easiest way to get going:
211
228
 
212
229
  ```bash
213
- # Run the setup wizard — it will guide you through everything
214
- npx saros-proxy setup
215
-
216
- # Or if installed globally:
230
+ # If installed globally
217
231
  saros-proxy setup
232
+
233
+ # Or with npx
234
+ npx saros-proxy setup
218
235
  ```
219
236
 
220
237
  The wizard will:
221
238
  1. Ask for your proxy port (default: 3000)
222
- 2. Ask for your API keys (name + key value)
239
+ 2. Ask for your API keys (name + key)
223
240
  3. Generate `config.yaml` automatically
224
241
  4. Run a smoke test to verify everything works
225
242
 
226
- ### Option B: Manual Setup
227
-
228
- #### 1. Create a configuration file
243
+ Want to run manually instead? Skip the wizard and just create a `config.yaml`:
229
244
 
230
245
  ```yaml
231
- # config.yaml
232
246
  port: 3000
233
247
  host: 127.0.0.1
234
248
  upstreamBaseUrl: https://opencode.ai
@@ -242,17 +256,17 @@ circuitBreakerCooldownMs: 60000
242
256
  requestTimeoutMs: 30000
243
257
  ```
244
258
 
245
- #### 2. Run the proxy
259
+ Then run:
246
260
 
247
261
  ```bash
248
- # Using tsx (development)
262
+ # Development (hot reload)
249
263
  npx tsx src/index.ts
250
264
 
251
- # Using built JavaScript
265
+ # Production (built)
252
266
  node dist/index.js
253
267
  ```
254
268
 
255
- #### 3. Test it
269
+ Test that it's alive:
256
270
 
257
271
  ```bash
258
272
  curl http://127.0.0.1:3000/health
@@ -268,23 +282,25 @@ curl http://127.0.0.1:3000/zen/go/v1/chat/completions \
268
282
 
269
283
  ---
270
284
 
285
+ ---
286
+
271
287
  ## Configuration Reference
272
288
 
273
- All configuration options can be set via YAML file (`config.yaml`), environment variables, or CLI flags. Priority order: CLI > YAML > env vars > defaults.
289
+ Every option can be set via YAML (`config.yaml`), environment variables, or CLI flags. Priority: CLI > YAML > env vars > defaults.
274
290
 
275
- ### YAML / Environment Variables
291
+ ### Options
276
292
 
277
- | Option | CLI Flag | Env Variable | Default | Description |
293
+ | Option | CLI Flag | Env Variable | Default | Notes |
278
294
  |---|---|---|---|---|
279
- | `port` | `--port` | `PROXY_PORT` | `3000` | Listening port (1–65535) |
295
+ | `port` | `--port` | `PROXY_PORT` | `3000` | 1–65535 |
280
296
  | `host` | `--host` | `PROXY_HOST` | `127.0.0.1` | Bind address |
281
- | `upstreamBaseUrl` | — | `UPSTREAM_BASE_URL` | `https://opencode.ai` | Upstream API base URL (HTTPS only) |
282
- | `circuitBreakerThreshold` | — | `CIRCUIT_BREAKER_THRESHOLD` | `3` | Consecutive failures before disabling a key (1–10) |
283
- | `circuitBreakerCooldownMs` | — | `CIRCUIT_BREAKER_COOLDOWN_MS` | `60000` | Cooldown in ms before re-enabling a key (1000–3600000) |
284
- | `requestTimeoutMs` | — | `REQUEST_TIMEOUT_MS` | `30000` | Upstream request timeout in ms (1000–300000) |
285
- | `allowedOrigins` | — | — | `["http://localhost:*", "http://127.0.0.1:*"]` | CORS allowed origins (use `[]` to allow all) |
286
- | `keys` | — | `OPENCODE_GO_KEYS` | — | Array of `{label, key}` objects or comma-separated env format |
287
- | `config` | `--config` | — | `config.yaml` | Path to YAML config file |
297
+ | `upstreamBaseUrl` | — | `UPSTREAM_BASE_URL` | `https://opencode.ai` | HTTPS only |
298
+ | `circuitBreakerThreshold` | — | `CIRCUIT_BREAKER_THRESHOLD` | `3` | Consecutive fails before disabling (1–10) |
299
+ | `circuitBreakerCooldownMs` | — | `CIRCUIT_BREAKER_COOLDOWN_MS` | `60000` | Cooldown before re-enabling (1000–3600000) |
300
+ | `requestTimeoutMs` | — | `REQUEST_TIMEOUT_MS` | `30000` | Upstream timeout (1000–300000) |
301
+ | `allowedOrigins` | — | — | `["http://localhost:*", "http://127.0.0.1:*"]` | CORS origins (`[]` = allow all) |
302
+ | `keys` | — | `OPENCODE_GO_KEYS` | — | Array of `{label, key}` |
303
+ | `config` | `--config` | — | `config.yaml` | Path to YAML config |
288
304
 
289
305
  ### API Key Format
290
306
 
@@ -304,12 +320,12 @@ OPENCODE_GO_KEYS="account1:sk-xxx,account2:sk-yyy"
304
320
 
305
321
  ### API Key Encryption
306
322
 
307
- API keys can be encrypted at rest using AES-256-GCM with scrypt key derivation. When encrypted, keys are stored as `enc:scrypt:...` in `config.yaml` and decrypted at startup using the `OPENCODE_GO_ENCRYPTION_KEY` environment variable.
323
+ Keys can be encrypted at rest using AES-256-GCM with scrypt key derivation. Encrypted keys look like `enc:scrypt:...` in `config.yaml` and get decrypted at startup using the `OPENCODE_GO_ENCRYPTION_KEY` environment variable.
308
324
 
309
325
  **Setup with encryption:**
310
326
  ```bash
311
327
  npm run setup
312
- # When prompted, choose to encrypt keys and enter a master key (min 16 characters)
328
+ # Choose to encrypt keys and enter a master key (min 16 chars)
313
329
  ```
314
330
 
315
331
  **Running with encrypted keys:**
@@ -327,10 +343,10 @@ environment:
327
343
 
328
344
  **Notes:**
329
345
  - Encrypted keys use AES-256-GCM with scrypt KDF (N=16384, r=8, p=1)
330
- - Each key is encrypted with a random salt and IV
331
- - Plaintext keys (starting with `sk-`) are still supported for backward compatibility
332
- - The `OPENCODE_GO_KEYS` environment variable always uses plaintext keys (for CI/CD)
333
- - If keys are encrypted but `OPENCODE_GO_ENCRYPTION_KEY` is not set, the proxy will fail to start with a clear error message
346
+ - Each key has its own random salt and IV
347
+ - Plaintext keys (`sk-...`) still work — backward compatible
348
+ - `OPENCODE_GO_KEYS` env var is always plaintext (for CI/CD)
349
+ - If keys are encrypted but no `OPENCODE_GO_ENCRYPTION_KEY` is set, startup fails with a clear error
334
350
 
335
351
  ### CLI Flags
336
352
 
@@ -342,14 +358,14 @@ tsx src/index.ts --config my-config.yaml --port 4000 --host 0.0.0.0
342
358
 
343
359
  ## Usage Examples
344
360
 
345
- ### Basic: Run with YAML config
361
+ ### Basic: YAML config
346
362
 
347
363
  ```bash
348
- # Create config.yaml (see Quick Start), then:
364
+ # Create config.yaml, then:
349
365
  npx tsx src/index.ts
350
366
  ```
351
367
 
352
- ### Advanced: Run with environment variables only
368
+ ### Advanced: Environment variables only
353
369
 
354
370
  ```bash
355
371
  export PROXY_PORT=4000
@@ -370,7 +386,7 @@ npx tsx src/index.ts
370
386
  npx tsx src/index.ts --config /etc/saros/config.yaml
371
387
  ```
372
388
 
373
- ### Run as a background service (Linux)
389
+ ### Run as background service (Linux)
374
390
 
375
391
  ```bash
376
392
  nohup npx tsx src/index.ts > proxy.log 2>&1 &
@@ -380,9 +396,7 @@ nohup npx tsx src/index.ts > proxy.log 2>&1 &
380
396
 
381
397
  ## OpenCode Integration
382
398
 
383
- Configure OpenCode to route API requests through the proxy. Create or edit your OpenCode config file:
384
-
385
- **Location:** `~/.config/opencode/opencode.json` (global) or `opencode.json` in your project root
399
+ Configure OpenCode to route API requests through the proxy. Edit your OpenCode config file at `~/.config/opencode/opencode.json` (global) or `opencode.json` in your project root:
386
400
 
387
401
  ```jsonc
388
402
  {
@@ -407,9 +421,9 @@ Configure OpenCode to route API requests through the proxy. Create or edit your
407
421
 
408
422
  **Key points:**
409
423
  - `baseURL` must end at `/v1` — OpenCode appends route paths internally
410
- - `apiKey` can be any placeholder — the proxy handles real key selection
411
- - Model IDs must match what the upstream API expects (e.g., `glm-5`, not `opencode-go/glm-5`)
412
- - After editing the config, restart OpenCode
424
+ - `apiKey` can be anything — the proxy handles real key selection
425
+ - Model IDs must match what upstream expects (e.g., `glm-5`, not `opencode-go/glm-5`)
426
+ - Restart OpenCode after editing the config
413
427
 
414
428
  **Alternative: Environment variable substitution**
415
429
 
@@ -432,7 +446,7 @@ Then set: `export OPENCODE_PROXY_URL=http://127.0.0.1:3000/zen/go/v1`
432
446
 
433
447
  **Using the proxy in OpenCode:**
434
448
 
435
- Once configured, select the proxy provider in OpenCode's model picker, or set it as default:
449
+ Select the proxy provider in OpenCode's model picker, or set it as default:
436
450
 
437
451
  ```jsonc
438
452
  {
@@ -456,24 +470,23 @@ Returns JSON with key counts, active requests, and circuit-breaker state.
456
470
  ### View logs
457
471
 
458
472
  ```bash
459
- # Development (pretty-printed, colorized)
473
+ # Dev mode (pretty-printed, colorized)
460
474
  NODE_ENV=development npx tsx src/index.ts
461
475
 
462
476
  # Production (structured JSON)
463
477
  NODE_ENV=production npx tsx src/index.ts
464
478
  ```
465
479
 
466
- ### Verify which key served a request
480
+ ### See which key served a request
467
481
 
468
- The proxy returns two debug headers on every successful response:
482
+ Saros adds two debug headers to every response:
469
483
 
470
- | Header | Example | Meaning |
484
+ | Header | Example | What it is |
471
485
  |---|---|---|
472
- | `X-Proxy-Key-Label` | `primary` | Which configured key was used |
473
- | `X-Proxy-Request-Id` | `550e8400-e29b-41d4-a716-446655440000` | Unique request ID for log correlation |
486
+ | `X-Proxy-Key-Label` | `primary` | Which key was used |
487
+ | `X-Proxy-Request-Id` | `550e8400-e29b-41d4-a716-446655440000` | Unique ID for log correlation |
474
488
 
475
489
  ```bash
476
- # Check which key handled your request
477
490
  curl -s -D - http://127.0.0.1:3000/zen/go/v1/models | grep -i x-proxy
478
491
  # X-Proxy-Key-Label: primary
479
492
  # X-Proxy-Request-Id: 550e8400-e29b-41d4-a716-446655440000
@@ -483,11 +496,11 @@ curl -s -D - http://127.0.0.1:3000/zen/go/v1/models | grep -i x-proxy
483
496
 
484
497
  | Symptom | Likely cause | Fix |
485
498
  |---|---|---|
486
- | `No valid API keys configured` | Missing or invalid keys | Check `keys` in config.yaml or `OPENCODE_GO_KEYS` env var |
487
- | `ECONNREFUSED` on startup | Port already in use | Change `port` or kill the existing process |
488
- | `504 Gateway Timeout` | Upstream request exceeded timeout | Increase `requestTimeoutMs` or check upstream latency |
499
+ | `No valid API keys configured` | Missing or bad keys | Check `keys` in config.yaml or `OPENCODE_GO_KEYS` |
500
+ | `ECONNREFUSED` on startup | Port already in use | Change port or kill the existing process |
501
+ | `504 Gateway Timeout` | Upstream too slow | Increase `requestTimeoutMs` |
489
502
  | `All API keys are temporarily unavailable` | All keys disabled by circuit breaker | Wait for cooldown or check upstream status |
490
- | `CORS violation` | Client origin not in `allowedOrigins` | Add the origin to `allowedOrigins` in config |
503
+ | CORS error | Client origin not allowed | Add it to `allowedOrigins` in config |
491
504
 
492
505
  ---
493
506
 
@@ -496,10 +509,10 @@ curl -s -D - http://127.0.0.1:3000/zen/go/v1/models | grep -i x-proxy
496
509
  ### Run tests
497
510
 
498
511
  ```bash
499
- # Unit tests
512
+ # All tests
500
513
  npm test
501
514
 
502
- # E2E tests (requires network)
515
+ # Just E2E (needs network)
503
516
  npx vitest run test/e2e.test.ts
504
517
 
505
518
  # Watch mode
@@ -525,10 +538,10 @@ Output goes to `dist/`.
525
538
  ```
526
539
  src/
527
540
  index.ts — Entry point, server startup, graceful shutdown
528
- proxy.ts — Hono HTTP app, routing, streaming, failover
541
+ proxy.ts — Hono app, routing, streaming, failover
529
542
  proxy-logic.ts — Pure logic: key selection, circuit breaker, error classification
530
543
  proxy-logic.test.ts — Unit tests for proxy logic
531
- config.ts — Configuration loading from YAML, env vars, CLI
544
+ config.ts — Config loading from YAML, env vars, CLI
532
545
  logger.ts — Structured logging with Pino + key masking
533
546
  cli/
534
547
  setup.ts — Interactive setup wizard
@@ -538,77 +551,14 @@ test/
538
551
 
539
552
  ### Contributing
540
553
 
541
- 1. Fork the repository.
554
+ 1. Fork the repo.
542
555
  2. Create a feature branch: `git checkout -b feat/my-feature`
543
- 3. Commit changes: `git commit -am 'Add my feature'`
556
+ 3. Commit: `git commit -am 'Add my feature'`
544
557
  4. Push: `git push origin feat/my-feature`
545
558
  5. Open a pull request.
546
559
 
547
560
  ---
548
561
 
549
- ## LLM Quick Reference
550
-
551
- If you're an AI assistant helping a user install this proxy, use these exact commands:
552
-
553
- **Prerequisites check:**
554
- ```bash
555
- node --version # Must be >= 22.0.0
556
- ```
557
-
558
- **Install from source:**
559
- ```bash
560
- git clone https://github.com/vitorvilaca3011/saros-proxy.git
561
- cd proxy
562
- npm install
563
- npm run build
564
- ```
565
-
566
- **Configure:**
567
- ```bash
568
- npm run setup # Interactive wizard
569
- # OR manually: cp config.example.yaml config.yaml && edit config.yaml
570
- ```
571
-
572
- **Run:**
573
- ```bash
574
- npm run dev # Development
575
- node dist/index.js # Production
576
- ```
577
-
578
- **Verify:**
579
- ```bash
580
- curl http://127.0.0.1:3000/health
581
- ```
582
-
583
- **Docker quick start:**
584
- ```bash
585
- cp config.example.yaml config.yaml # Edit with your keys
586
- npm run docker:build
587
- npm run docker:run
588
- ```
589
-
590
- **Add to OpenCode config:**
591
- ```bash
592
- # Run setup wizard and choose "Update OpenCode config automatically"
593
- npm run setup
594
-
595
- # Or manually edit ~/.config/opencode/opencode.json and add:
596
- {
597
- "provider": {
598
- "saros-proxy": {
599
- "npm": "@ai-sdk/openai-compatible",
600
- "name": "Saros",
601
- "options": {
602
- "baseURL": "http://127.0.0.1:3000/zen/go/v1",
603
- "apiKey": "not-used"
604
- }
605
- }
606
- }
607
- }
608
- ```
609
-
610
- ---
611
-
612
562
  ## Security
613
563
 
614
564
  For vulnerability reporting, see [SECURITY.md](SECURITY.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "saros-proxy",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Saros — predictive multi-account proxy for the OpenCode-Go API. Cycles between accounts before they exhaust.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -25,6 +25,9 @@
25
25
  "lint": "tsc --noEmit",
26
26
  "prepublishOnly": "npm run build",
27
27
  "test": "vitest",
28
+ "test:npm": "vitest run test/npm-package.test.ts",
29
+ "test:source": "vitest run test/source-install.test.ts",
30
+ "test:docker": "vitest run test/docker-install.test.ts",
28
31
  "docker:build": "docker compose build proxy",
29
32
  "docker:run": "docker compose up proxy",
30
33
  "docker:test": "docker compose run --rm test"