@karpeleslab/teamclaude 1.0.6 → 1.0.8

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 KarpelesLab
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,18 +1,31 @@
1
1
  # TeamClaude
2
2
 
3
+ [![CI](https://github.com/KarpelesLab/teamclaude/actions/workflows/ci.yml/badge.svg)](https://github.com/KarpelesLab/teamclaude/actions/workflows/ci.yml)
4
+ [![npm version](https://img.shields.io/npm/v/@karpeleslab/teamclaude.svg)](https://www.npmjs.com/package/@karpeleslab/teamclaude)
5
+ [![node](https://img.shields.io/node/v/@karpeleslab/teamclaude.svg)](https://nodejs.org)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
+
3
8
  Multi-account Claude proxy with automatic quota-based rotation for [Claude Code](https://claude.ai/claude-code).
4
9
 
5
- Sits transparently between Claude Code and the Anthropic API, managing multiple Claude Max accounts and automatically switching when one approaches its session or weekly quota limit.
10
+ Sits transparently between Claude Code and the Anthropic API, managing multiple Claude Max (or API key) accounts and automatically switching when one approaches its session or weekly quota limit.
6
11
 
7
12
  ![TeamClaude TUI](screenshots/teamclaude.png)
8
13
 
9
14
  ## Features
10
15
 
11
16
  - **Automatic account rotation** — switches to the next account when session (5h) or weekly (7d) quota reaches the configured threshold (default 98%)
12
- - **Auto-retry on 429** — if an account is rate-limited, transparently retries with the next one
13
- - **Interactive TUI** — real-time dashboard with color-coded quota bars showing reset countdowns, activity log, and keyboard controls
14
- - **OAuth token refresh** — proactively refreshes expiring tokens, intercepts client token renewals, and persists them to config
15
- - **Hot-reload accounts** — add accounts via `import` or `login` while the server is running, press **R** to pick them up
17
+ - **Auto-retry on 429** — waits the `retry-after` duration and retries the same account; switches to the next on persistent errors
18
+ - **Interactive TUI** — real-time dashboard with color-coded quota bars, reset countdowns, activity log, and keyboard controls; a settings screen (`g`) edits the rotation threshold, quota-probe interval, and sx.org proxy live
19
+ - **OAuth token management** — automatically refreshes tokens nearing expiry and persists them to config; client token refreshes pass through untouched
20
+ - **Hot-reload accounts** — add or change accounts while the server is running; press **R** in the TUI, or run headless and CLI changes auto-reload via a local control endpoint
21
+ - **Headless mode** — run the proxy without the TUI (`--headless`) for backgrounding/services
22
+ - **Org-aware accounts** — one email can hold multiple accounts across different organizations (e.g. corp + personal); dedup is keyed on account + org, and names disambiguate as `email (Org)`
23
+ - **Rotation priority** — pin a preferred account order with `teamclaude priority`
24
+ - **Enable/disable accounts** — temporarily pause an account without removing it (`teamclaude disable`/`enable`, or `d` in the TUI); re-enabling also clears a stuck error state
25
+ - **Quota persistence** — observed quota survives restarts (saved to a sibling state file), so rotation state isn't lost on restart; stale windows are discarded automatically
26
+ - **Optional quota probe** — off by default; when enabled, periodically refreshes idle accounts' quota from the usage endpoint (no message spend), and surfaces the Sonnet weekly bucket
27
+ - **Optional MITM proxy mode** — `teamclaude run --mitm` routes claude via an HTTPS forward proxy with a local CA so even hardcoded `api.anthropic.com` endpoints (e.g. the Claude Design MCP) get the real token injected
28
+ - **Optional sx.org proxy mode** — off by default; set an [sx.org](https://sx.org) API key in the TUI settings screen (`g`) and TeamClaude auto-provisions a residential proxy to change the egress IP and work around IP-based `429`s. Three modes (`m` to cycle): **always** (route all upstream traffic), **on 429 only** (stay direct, fail over to the proxy after a 429), or **off** (keep the key but don't use it). TLS stays end-to-end with Anthropic (the proxy only relays ciphertext)
16
29
  - **Request logging** — optional full request/response logging for debugging
17
30
  - **Zero dependencies** — uses only Node.js built-in modules
18
31
 
@@ -67,7 +80,11 @@ claude /login # Log into an account in Claude Code
67
80
  teamclaude import # Import its credentials
68
81
  ```
69
82
 
70
- Re-importing the same account updates its credentials.
83
+ Re-importing the same account updates its credentials. You can also import from a custom path:
84
+
85
+ ```bash
86
+ teamclaude import --from /path/to/credentials.json
87
+ ```
71
88
 
72
89
  ### API Key
73
90
 
@@ -86,11 +103,32 @@ teamclaude server
86
103
  ```
87
104
 
88
105
  When running from a TTY, shows an interactive TUI with:
89
- - Account table with session/weekly quota progress bars
106
+ - Account table with session/weekly quota progress bars and reset countdowns
90
107
  - Real-time activity log with request tracking
91
- - Keyboard shortcuts: **s**witch, **a**dd, **r**emove, **R**eload, **q**uit
108
+ - Keyboard shortcuts (see below)
92
109
 
93
- Falls back to plain log output when not a TTY (e.g. running as a service).
110
+ Falls back to plain log output when not a TTY (e.g. running as a service). Pass `--headless` (or `--no-tui`) to force the plain-log mode even from a terminal — useful for backgrounding the proxy.
111
+
112
+ When running headless, you can re-sync accounts from the config without a restart by POSTing to the local control endpoint (the equivalent of pressing **R** in the TUI):
113
+
114
+ ```bash
115
+ curl -X POST http://localhost:3456/teamclaude/reload
116
+ ```
117
+
118
+ You usually don't need to call it directly: `teamclaude login`, `import`, `enable`, `disable`, and `priority` automatically notify a running server to reload. (New accounts and credential/priority/enable-disable changes are picked up live; account *removals* still require a restart.)
119
+
120
+ #### TUI Keyboard Shortcuts
121
+
122
+ | Key | Action |
123
+ |-----|--------|
124
+ | `s` | Switch active account |
125
+ | `a` | Add account (import or API key) |
126
+ | `r` | Remove an account |
127
+ | `d` | Enable/disable an account |
128
+ | `R` | Reload accounts from config |
129
+ | `q` | Quit |
130
+
131
+ In selection mode, use `j`/`k` or arrow keys to navigate, `Enter` to confirm, `Esc` to cancel.
94
132
 
95
133
  ### Run Claude Code through the proxy
96
134
 
@@ -98,6 +136,8 @@ Falls back to plain log output when not a TTY (e.g. running as a service).
98
136
  teamclaude run
99
137
  ```
100
138
 
139
+ `run` probes the proxy first: if it's up, Claude Code is routed through it; if it's **not** running, `claude` is launched directly so nothing breaks.
140
+
101
141
  Or manually set the environment:
102
142
 
103
143
  ```bash
@@ -105,16 +145,39 @@ eval $(teamclaude env)
105
145
  claude
106
146
  ```
107
147
 
148
+ ### Routing plain `claude` automatically (alias)
149
+
150
+ So you don't have to type `teamclaude run` every time, add a shell alias that makes plain `claude` go through the proxy (and fall back to direct when it's down):
151
+
152
+ ```bash
153
+ teamclaude alias # print the alias for your shell
154
+ teamclaude alias --install # or write it to your shell rc (--uninstall to remove)
155
+ ```
156
+
157
+ This is an interactive-shell alias — it affects `claude` typed at a prompt, not `claude` spawned by editors or scripts. It's a thin passthrough to `teamclaude run`, which holds the proxy-up/down logic.
158
+
108
159
  ### Other commands
109
160
 
110
161
  ```bash
111
- teamclaude accounts # List accounts with live profile info
162
+ teamclaude accounts # List accounts with subscription tier and token status
163
+ teamclaude accounts -v # Also show token expiry times
112
164
  teamclaude status # Show live proxy status (requires running server)
113
- teamclaude remove <name> # Remove an account
165
+ teamclaude remove <name> # Remove an account (by name or email)
166
+ teamclaude disable <name> # Temporarily exclude an account from rotation
167
+ teamclaude enable <name> # Re-enable it (also clears a stuck error state)
168
+ teamclaude priority <name> 1 # Set rotation priority (lower = preferred)
169
+ teamclaude probe 300 # Enable background quota refresh (off by default)
170
+ teamclaude alias # Print/install a `claude` alias that routes via the proxy
114
171
  teamclaude api <path> # Call an API endpoint with account credentials
115
172
  teamclaude help # Show all commands
116
173
  ```
117
174
 
175
+ When the same email belongs to multiple organizations, accounts are named
176
+ `email (Org)` to keep them distinct. Pass `--org <name|uuid>` to disambiguate a
177
+ bare email, e.g. `teamclaude remove user@example.com --org Acme`. Use
178
+ `teamclaude priority <name> --first` / `--last` to move an account to the front
179
+ or back of the rotation order.
180
+
118
181
  ### Request logging
119
182
 
120
183
  Log full request/response details to a directory (one file per request):
@@ -127,6 +190,8 @@ teamclaude server --log-to /tmp/requests
127
190
 
128
191
  Config is stored at `~/.config/teamclaude.json` (or `$XDG_CONFIG_HOME/teamclaude.json`). A random proxy API key is generated on first use.
129
192
 
193
+ Volatile runtime state (observed quota) is written separately to `teamclaude.state.json` alongside the config, so the config file stays clean and hand-editable. The state file is safe to delete — quota is simply re-learned from traffic.
194
+
130
195
  Override the config path with `TEAMCLAUDE_CONFIG`:
131
196
 
132
197
  ```bash
@@ -143,11 +208,15 @@ TEAMCLAUDE_CONFIG=./my-config.json teamclaude server
143
208
  },
144
209
  "upstream": "https://api.anthropic.com",
145
210
  "switchThreshold": 0.98,
211
+ "sx": { "apiKey": "your-sx-org-api-key", "mode": "always" },
146
212
  "accounts": [
147
213
  {
148
- "name": "user@example.com",
214
+ "name": "user@example.com (Acme)",
149
215
  "type": "oauth",
150
216
  "accountUuid": "...",
217
+ "orgUuid": "...",
218
+ "orgName": "Acme",
219
+ "priority": 0,
151
220
  "accessToken": "sk-ant-oat01-...",
152
221
  "refreshToken": "sk-ant-ort01-...",
153
222
  "expiresAt": 1774384968427
@@ -156,14 +225,105 @@ TEAMCLAUDE_CONFIG=./my-config.json teamclaude server
156
225
  }
157
226
  ```
158
227
 
228
+ | Field | Description |
229
+ |-------|-------------|
230
+ | `proxy.port` | Local port the proxy listens on |
231
+ | `proxy.apiKey` | API key clients use to authenticate with the proxy |
232
+ | `upstream` | Upstream API base URL |
233
+ | `switchThreshold` | Quota utilization (0–1) at which to switch accounts (TUI: `g` → `t`) |
234
+ | `quotaProbeSeconds` | Background quota-probe interval in seconds (`0` = off, the default; CLI `probe` or TUI `g` → `p`) |
235
+ | `sx.apiKey` | [sx.org](https://sx.org) API key. When set, TeamClaude auto-provisions a residential proxy (egress-IP 429 workaround). Absent/empty = off |
236
+ | `sx.mode` | `always` (route all upstream traffic), `429` (direct, fail over to the proxy after a 429), or `off` (keep the key but don't use it). Defaults to `always` when a key is set |
237
+ | `accounts[].accountUuid` | Anthropic account (person) id; set automatically from the OAuth profile |
238
+ | `accounts[].orgUuid` / `orgName` | Organization the account is scoped to — lets one email hold multiple org accounts |
239
+ | `accounts[].priority` | Rotation preference, lower = preferred (default 0) |
240
+ | `accounts[].disabled` | If `true`, the account is excluded from rotation until re-enabled |
241
+
242
+ ### Quota probe (optional, off by default)
243
+
244
+ By default TeamClaude is **passive** — it learns each account's quota only from the responses that flow through it, so an account that hasn't been used yet shows unknown quota until it's first rotated to.
245
+
246
+ If you'd rather keep idle accounts' quota fresh, enable the background probe:
247
+
248
+ ```bash
249
+ teamclaude probe 300 # refresh every 300s
250
+ teamclaude probe off # back to passive (default)
251
+ teamclaude probe # show current setting
252
+ ```
253
+
254
+ You can also set the interval live from the TUI settings screen (`g` → `p`), alongside the rotation threshold (`t`).
255
+
256
+ It reads each OAuth account's utilization from Anthropic's usage endpoint (`/api/oauth/usage`), which reports quota **without consuming any message quota**. Minimum interval is 30s. Changing it takes effect on a running server immediately (no restart). When enabled, it also surfaces the **Sonnet 7-day** bucket as an extra bar in the TUI / `status` (when your plan exposes it).
257
+
258
+ ### MITM proxy mode (optional, off by default)
259
+
260
+ The normal reverse-proxy only intercepts what `ANTHROPIC_BASE_URL` covers. Some Claude Code features (e.g. the **Claude Design MCP**) use a **hardcoded** `https://api.anthropic.com` URL that ignores that variable, so they bypass the proxy. MITM proxy mode captures those too.
261
+
262
+ Run claude with the `--mitm` flag:
263
+
264
+ ```bash
265
+ teamclaude run --mitm -- <claude args...>
266
+ ```
267
+
268
+ That launches claude pointed at teamclaude as an **HTTPS forward proxy** (`HTTPS_PROXY`) and trusts a locally-generated CA (`NODE_EXTRA_CA_CERTS`). For an intercepted host, teamclaude **dials the real upstream first, mirrors its negotiated ALPN** (HTTP/2 or HTTP/1.1), then terminates TLS toward claude with the same protocol and relays the traffic **as transparently as possible** — rewriting only what it must:
269
+
270
+ - the **`authorization`** header → the active account's real token (dropping any client `x-api-key`);
271
+ - the **`account_uuid`** inside `metadata.user_id` → the active account's UUID (so the body agrees with the injected token);
272
+ - and it reads `anthropic-ratelimit-*` from responses for quota.
273
+
274
+ Everything else is copied byte-for-byte (HTTP/2 is handled with a built-in HPACK codec so the only header changed is the auth one). Any host other than the upstream is blind-tunnelled. The server accepts *both* base-URL and proxy clients at once, so instances launched with and without `--mitm` can share one server.
275
+
276
+ Trust model:
277
+ - The CA is generated locally, stored in the config dir, and trusted **only** by the claude process you launch via `teamclaude run` (through `NODE_EXTRA_CA_CERTS`) — it is **never** added to your system trust store. The leaf private key is `0600`; the CA private key is never written to disk.
278
+ - teamclaude still verifies the **real** Anthropic certificate on the upstream leg.
279
+
280
+ Verify the proxy + CA without any credentials — the proxy always answers a built-in test host:
281
+
282
+ ```bash
283
+ # (with the server running and certs generated, e.g. after one `teamclaude run`)
284
+ curl --proxy http://localhost:3456 --cacert ~/.config/teamclaude-ca.pem https://www.example.org/
285
+ # → {"teamclaude":"mitm-proxy-ok","host":"www.example.org",...}
286
+ ```
287
+
288
+ ### sx.org proxy mode (optional, off by default)
289
+
290
+ Some transient `429`s key on the proxy's **outbound IP**, not the account — so rotating accounts doesn't help. To work around them, TeamClaude can route upstream requests through a residential proxy from [sx.org](https://sx.org), giving a different egress IP.
291
+
292
+ Open the TUI and press **`g`** for the settings screen, then **`k`** to paste your sx.org API key (stored in `config.sx.apiKey`). TeamClaude reuses an existing active proxy port on your sx.org account, or auto-creates a residential US one, and dials the upstream through it via HTTP `CONNECT` on **both** the reverse-proxy and `--mitm` paths.
293
+
294
+ Press **`m`** to cycle the **mode**:
295
+
296
+ | Mode | Behavior |
297
+ |------|----------|
298
+ | **always** | Tunnel **every** upstream request through sx.org. |
299
+ | **on 429 only** | Connect directly; on a `429` (which is IP-based), immediately retry that request through sx.org's fresh egress IP — no wait. On the `--mitm` path, a recent `429` routes new tunnels through sx.org for a short window. |
300
+ | **off** | Never use sx.org, but **keep the API key** so you can re-enable it instantly. |
301
+
302
+ TLS is established **end-to-end with `api.anthropic.com` over the tunnel**, so the sx.org proxy only ever relays ciphertext and the real Anthropic certificate is still verified. Mode and key changes apply live (no restart). Press **`x`** to forget the key entirely.
303
+
304
+ > **Cost:** in **always** mode *all* Claude traffic flows through the residential proxy, which sx.org meters by bandwidth — expect real per-GB cost. **on 429 only** uses the proxy just when you're actually being throttled, so it's the cheaper way to ride out rate limits.
305
+
159
306
  ## How It Works
160
307
 
161
308
  1. Claude Code connects to the local proxy instead of `api.anthropic.com`
162
309
  2. The proxy selects the active account and forwards requests with that account's credentials
163
- 3. Rate limit headers from the API (`anthropic-ratelimit-unified-*`) track session and weekly quota
164
- 4. When usage reaches the threshold, the proxy switches to the next available account
165
- 5. If all accounts are exhausted, returns 429 with the soonest reset time
310
+ 3. OAuth tokens expiring within 5 minutes are automatically refreshed and persisted to config
311
+ 4. Rate limit headers from the API (`anthropic-ratelimit-unified-*`) track session (5h) and weekly (7d) quota utilization
312
+ 5. When usage reaches the threshold, the proxy switches to the next available account via round-robin
313
+ 6. On 429 responses, the proxy waits the `retry-after` duration and retries; on persistent errors, it switches accounts
314
+ 7. Transient network errors (connection reset, timeout) drop the connection so the client can retry
315
+ 8. If all accounts are exhausted, returns 429 with the soonest reset time
316
+ 9. Client token refresh requests (`/v1/oauth/token`) are relayed to upstream untouched — the proxy and client manage their own token lifecycles independently
317
+
318
+ ## Security
319
+
320
+ The only canonical sources for TeamClaude are this repository
321
+ (https://github.com/KarpelesLab/teamclaude) and the
322
+ [`@karpeleslab/teamclaude`](https://www.npmjs.com/package/@karpeleslab/teamclaude)
323
+ npm package. TeamClaude is **never** distributed as a downloadable binary
324
+ archive — be wary of soft-forks that bundle a `.zip` and tell you to extract and
325
+ run it. See [SECURITY.md](SECURITY.md) for details and how to report issues.
166
326
 
167
327
  ## License
168
328
 
169
- MIT
329
+ MIT — see [LICENSE](LICENSE).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@karpeleslab/teamclaude",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "Multi-account Claude proxy with automatic quota-based rotation",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -11,7 +11,9 @@
11
11
  "src/"
12
12
  ],
13
13
  "scripts": {
14
- "start": "node src/index.js"
14
+ "start": "node src/index.js",
15
+ "test": "node --test",
16
+ "lint": "eslint ."
15
17
  },
16
18
  "keywords": [
17
19
  "claude",
@@ -23,5 +25,8 @@
23
25
  "license": "MIT",
24
26
  "engines": {
25
27
  "node": ">=18.0.0"
28
+ },
29
+ "devDependencies": {
30
+ "eslint": "^9.0.0"
26
31
  }
27
32
  }