proxitor 0.9.0-beta.0 → 0.9.0-beta.2

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
@@ -127,15 +127,17 @@ This changes the header to `Authorization: OAuth sk-...`.
127
127
 
128
128
  When using a custom `openrouterBaseUrl` that points to a third-party service, that service may not support OpenRouter-specific endpoints like `/providers` or `/models/{author}/{slug}/endpoints`. Proxitor handles this automatically:
129
129
 
130
- - **Automatic fallback** — if the custom API returns an error (4xx/5xx) or an unexpected response format for data endpoints, proxitor falls back to `https://openrouter.ai/api/v1` (no API key needed — these endpoints are public)
130
+ - **Automatic fallback** — if the custom API returns an error (4xx/5xx) or an unexpected response format for data endpoints, proxitor falls back to `https://openrouter.ai/api` (no API key needed — these endpoints are public)
131
131
  - **`openrouterDataUrl`** — set this explicitly to control the primary URL for data fetching, independent of `openrouterBaseUrl` (which is used for proxying requests)
132
132
 
133
133
  ```yaml
134
134
  # Proxy requests go to custom service, data fetching falls back to OpenRouter
135
- openrouterBaseUrl: 'https://custom-service.example.com/v1'
135
+ # NOTE: do NOT include /v1 in the base URL — request paths like /v1/chat/completions
136
+ # are forwarded as-is, so /v1 would be duplicated if included here
137
+ openrouterBaseUrl: 'https://custom-service.example.com/api'
136
138
 
137
139
  # Explicitly set the primary data URL (optional, defaults to openrouterBaseUrl)
138
- # openrouterDataUrl: 'https://openrouter.ai/api/v1'
140
+ # openrouterDataUrl: 'https://openrouter.ai/api'
139
141
  ```
140
142
 
141
143
  When a fallback occurs, proxitor logs a warning: `Custom API did not return providers, using OpenRouter data as fallback`.
@@ -256,32 +258,48 @@ By default, OpenRouter doesn't enable prompt caching — every request pays full
256
258
 
257
259
  **`cacheControl`** — injects `cache_control: { "type": "ephemeral" }` into the request body. OpenRouter uses this to set cache breakpoints and advance them as conversations grow.
258
260
 
261
+ **`cacheControlTtl`** — controls the cache time-to-live. Anthropic's default TTL is 5 minutes (300s). Set to `1h` for a 1-hour cache at higher write cost (2× vs 1.25×). Only applies to Anthropic models — other providers don't support TTL.
262
+
259
263
  **`sessionId`** — injects `session_id` for provider sticky routing. Without it, OpenRouter only pins to a provider after detecting a cache hit. With it, routing sticks from the **first request** — critical for OpenAI models where delayed caching means 0 cached tokens on the first 1-2 requests.
260
264
 
261
- Both support `auto` / `always` / `never` modes:
265
+ Both `cacheControl` and `sessionId` support `auto` / `always` / `never` modes:
262
266
 
263
267
  | Mode | `cacheControl` | `sessionId` |
264
- |---|---|---|
268
+ | --- | --- | --- |
265
269
  | `auto` (default) | Anthropic models on `/v1/chat/completions`; all models on `/v1/messages` and `/v1/responses` | Use `X-Claude-Code-Session-Id` header if present; otherwise generate proxy UUID |
266
270
  | `always` | All models, all endpoints | Generate a proxy UUID for sticky routing |
267
271
  | `never` | Disabled | Disabled |
268
272
 
273
+ `cacheControlTtl` values:
274
+
275
+ | Value | TTL | Write cost | Use when |
276
+ | --- | --- | --- | --- |
277
+ | _(not set)_ | 5 min (Anthropic default) | 1.25× | High-frequency requests (>1 per 5 min) |
278
+ | `5m` | 5 minutes | 1.25× | Explicit short cache |
279
+ | `1h` | 1 hour | 2.0× | Low-frequency or long-running sessions |
280
+
269
281
  ```yaml
270
282
  cacheControl: auto # safe default — Anthropic and safe endpoints only
271
283
  sessionId: auto # always ensures sticky routing (client header or proxy UUID)
272
284
 
285
+ # Use 1-hour cache for all Anthropic models (higher write cost, longer TTL)
286
+ cacheControlTtl: 1h
287
+
273
288
  # Force caching for all models (may cause 400 on non-Anthropic /v1/chat/completions)
274
289
  # cacheControl: always
275
290
 
276
- # Per-model overrides
291
+ # Per-model overrides — TTL supports '5m', '1h', or 'default' (cancel global TTL)
277
292
  modelOverrides:
278
293
  "gpt-*":
279
- cacheControl: never # OpenAI caches automatically, no injection needed
280
- sessionId: always # but sticky routing still helps
294
+ cacheControl: never # OpenAI caches automatically, no injection needed
295
+ sessionId: always # but sticky routing still helps
296
+ "claude-opus-*":
297
+ cacheControlTtl: default # cancel global 1h TTL for Opus — use Anthropic's 5 min default
281
298
  ```
282
299
 
283
- **Why both matter:**
284
- - **Anthropic models** — `cache_control` activates caching, `session_id` prevents provider flip-flopping that would invalidate it
300
+ **Why all three matter:**
301
+
302
+ - **Anthropic models** — `cache_control` activates caching, `cacheControlTtl` extends it beyond 5 min, `session_id` prevents provider flip-flopping that would invalidate it
285
303
  - **OpenAI models** — caching is automatic (no `cache_control` needed), but `session_id` ensures sticky routing from request #1 instead of waiting for a cache hit
286
304
  - **All models** — `session_id` prevents the provider switch that silently resets cache
287
305
 
@@ -327,13 +345,14 @@ The wizard asks for:
327
345
 
328
346
  - **OpenRouter API key** — stored in config or set as `OPENROUTER_API_KEY` env var
329
347
  - **Port** — default `8828` (avoids conflicts with common dev servers on 8080)
330
- - **API base URL** — default `https://openrouter.ai/api/v1`; change for self-hosted or custom endpoints
331
- - **Data URL** — separate URL for provider/model data fetching; falls back to OpenRouter automatically if the custom API doesn't support these endpoints
348
+ - **Listen address** — all interfaces (`0.0.0.0`), localhost only (`127.0.0.1`), or a custom address (IP, hostname, or `unix:/path`)
349
+ - **API base URL** — default `https://openrouter.ai/api`; change for self-hosted or custom endpoints
332
350
  - **Authentication type** — `bearer` (default) or `oauth`; use `oauth` for custom proxy providers that pass tokens in the `Authorization: OAuth ...` header
333
- - **Host** — all interfaces (`0.0.0.0`) or localhost only (`127.0.0.1`)
334
351
  - **Save location** — project directory, `~/.config/proxitor/`, or `$XDG_CONFIG_HOME/proxitor/`
335
352
 
336
- If a config already exists, the wizard shows its location and asks whether to reconfigure. Existing `modelOverrides`, `provider`, and other fields are preserved only the wizard fields are updated.
353
+ After collecting the key, base URL, and auth type, the wizard performs a **best-effort upstream probe** (3 s timeout) to verify connectivity. If the upstream is unreachable or the key is rejected, a warning is shown but the config is still saved — this is informational only.
354
+
355
+ If a config already exists, the wizard shows its location and asks whether to reconfigure. All fields are **pre-filled** with current values — press Enter to keep, or type a new value. Existing `modelOverrides`, `provider`, and other fields are preserved — only the wizard fields are updated.
337
356
 
338
357
  ```sh
339
358
  proxitor config menu # interactive menu
@@ -341,9 +360,16 @@ proxitor config add # add a model override
341
360
  proxitor config edit # edit existing override
342
361
  proxitor config remove # remove override(s)
343
362
  proxitor config list # show current overrides
363
+ proxitor config list --json # overrides as JSON
364
+ proxitor config show # print the resolved config (merged)
365
+ proxitor config show --json # same, machine-readable
344
366
  proxitor config browse # explore models with pricing info
345
367
  proxitor config wizard # interactive setup wizard
346
- proxitor config validate # validate config file
368
+ proxitor config validate # validate config file (exit 0 ok, 1 invalid)
369
+ proxitor config validate --json # structured JSON result
370
+ proxitor doctor # diagnose environment + network + port + version
371
+ proxitor doctor --json # machine-readable diagnostic report
372
+ proxitor doctor --offline # skip network checks
347
373
  ```
348
374
 
349
375
  ### Add override walkthrough
@@ -408,19 +434,92 @@ The interface uses live data from the OpenRouter API — model search with type-
408
434
 
409
435
  ---
410
436
 
437
+ ## Diagnostics
438
+
439
+ When something doesn't work, `proxitor doctor` runs a battery of checks and prints a report. Sections cover:
440
+
441
+ - **Environment** — Node version, platform, TTY
442
+ - **Config** — discovery path, validity, override count
443
+ - **API key** — resolution (env vs. file; never prints the key)
444
+ - **Network** — upstream reachability (with configurable timeout)
445
+ - **Port** — availability of the configured port
446
+ - **Version** — installed version
447
+
448
+ Statuses: `✓ ok` / `⚠ warn` / `✗ fail` / `ⓘ skip`. Exit code is `0` when no `fail`, `1` otherwise — scriptable from CI.
449
+
450
+ ```sh
451
+ $ proxitor doctor
452
+
453
+ ▲ Proxitor Doctor
454
+
455
+ ◇ Environment
456
+ │ ✓ node-version — v22.4.1
457
+ │ ✓ platform — darwin arm64
458
+ │ ✓ tty — true
459
+
460
+ ◇ Config
461
+ │ ✓ config-found — /Users/u/proj/proxitor.config.yaml
462
+ │ ✓ config-valid — 12 keys, 3 override(s)
463
+
464
+ ◇ API key
465
+ │ ✓ api-key — set (env: set, file: set)
466
+
467
+ ◇ Network
468
+ │ ✓ upstream — https://openrouter.ai/api — 200, 342 models
469
+
470
+ ◇ Port
471
+ │ ✓ port-8828 — 127.0.0.1:8828
472
+
473
+ ◇ Version
474
+ │ ✓ version — 0.9.0-beta.1
475
+
476
+ └ Done. All checks passed.
477
+ ```
478
+
479
+ Useful flags:
480
+
481
+ ```sh
482
+ proxitor doctor --json # structured JSON for CI / scripts
483
+ proxitor doctor --offline # skip network checks (no upstream, no npm)
484
+ proxitor doctor --timeout 5000 # custom per-check network timeout (ms)
485
+ ```
486
+
487
+ ---
488
+
411
489
  ## CLI Options
412
490
 
491
+ ```sh
492
+ proxitor # start the proxy (default command)
493
+ proxitor start # same as above
494
+ proxitor up # alias for start
495
+ proxitor run # alias for start
496
+ proxitor --port 9000 # override port
497
+ proxitor ./team.yaml # use an explicit config
498
+ proxitor config show # print the resolved config
499
+ proxitor config show --json # machine-readable config
500
+ proxitor config list --json # overrides as JSON
501
+ proxitor config wizard # interactive setup
502
+ proxitor config validate # check the current config (exit 0/1)
503
+ proxitor config validate --json # structured JSON result
504
+ proxitor doctor # diagnose environment, network, port, version
505
+ proxitor doctor --offline # skip network checks
506
+ proxitor --help # full help
507
+ proxitor --version # print version
508
+ ```
509
+
413
510
  | Flag | Default | Description |
414
511
  |---|---|---|
415
- | `-p, --port <port>` | `8828` | Server port |
512
+ | `-p, --port <port>` | `8828` | Server port (validated: 1-65535) |
416
513
  | `-h, --host <host>` | `0.0.0.0` | Server host |
417
- | `-c, --config <path>` | auto-discovered | Path to config file |
418
- | `--openrouter-key <key>` | `$OPENROUTER_API_KEY` | OpenRouter API key |
514
+ | `-c, --config <path>` | auto-discovered | Path to config file (positional `[config-path]` also accepted) |
515
+ | `--openrouter-key <key>` / `-k <key>` | `$OPENROUTER_API_KEY` | OpenRouter API key |
419
516
  | `--verbose` | `false` | Enable verbose logging |
420
517
  | `--no-config` | | Skip config file discovery |
421
518
  | `-v, --version` | | Print version |
422
519
  | `--help` | | Print help |
423
520
 
521
+ Subcommands live under `proxitor config <subcommand>`. Run `proxitor config --help` for the full list, or see [Interactive Config Manager](#interactive-config-manager) for the walkthroughs.
522
+
424
523
  ---
425
524
 
426
525
  ## Development