teamclaude-cloud 1.4.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 +320 -0
- package/package.json +38 -0
- package/src/account-manager.js +1271 -0
- package/src/cloud.js +618 -0
- package/src/config.js +148 -0
- package/src/index.js +1726 -0
- package/src/oauth.js +347 -0
- package/src/server.js +1267 -0
- package/src/tui.js +843 -0
package/README.md
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
# TeamClaude Cloud
|
|
2
|
+
|
|
3
|
+
Multi-account Claude proxy with automatic quota-based rotation for [Claude Code](https://claude.ai/claude-code) — plus a **cloud SaaS layer** that syncs authenticated tokens to the cloud and controls, per auth key, which accounts each machine may use.
|
|
4
|
+
|
|
5
|
+
## Cloud SaaS (teamclaude-cloud)
|
|
6
|
+
|
|
7
|
+
The base proxy runs locally. The cloud layer lets you authenticate every account **once on one PC**, sync those tokens to the cloud, and let other machines pull only the accounts you allow them — via an **auth key**, without re-authenticating each account per PC.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
[Admin PC] teamclaude login ×N → teamclaude cloud push
|
|
11
|
+
│
|
|
12
|
+
▼
|
|
13
|
+
[Cloud] teamclaude.cloud: encrypted tokens (AES-256-GCM) + auth keys (hashed) + key→account mapping
|
|
14
|
+
Cloud API: sync push/pull, auth-key issue/revoke/map
|
|
15
|
+
▲
|
|
16
|
+
│ auth key
|
|
17
|
+
[Consumer PC] teamclaude cloud pull --key tck_… → teamclaude server
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Security model: tokens are encrypted at rest and token storage is server-only (row-level security denies all client roles); auth keys are stored as a SHA-256 hash (for pull-time lookup) **and** an AES-256-GCM ciphertext under the same key as the tokens, so the owner can re-copy the full `teamclaude cloud pull --key …` command from the dashboard at any time (owner-only `GET /auth-keys/:id/reveal`, revoked keys return 409). Keys issued before this change are hash-only and non-revealable — re-issue to get a copyable key. Access control (which key may use which account) is enforced server-side in the cloud API.
|
|
21
|
+
|
|
22
|
+
### Web dashboard
|
|
23
|
+
|
|
24
|
+
A browser dashboard (signup/login + auth-key issuance and control) is served on Vercel:
|
|
25
|
+
|
|
26
|
+
**https://teamclaude-cloud.vercel.app**
|
|
27
|
+
|
|
28
|
+
Sign up there, then use the *same* account with `teamclaude cloud login` on the admin PC. The dashboard shows your synced accounts with their **usage** (5h session, weekly, and the model-scoped "Fable" weekly window — as measured by the proxy at push time, with the push timestamp shown), toggles accounts globally, manages **groups** (create/rename/delete, per-group account enable + priority ordering), and issues/moves/revokes auth keys per group. The dashboard is a static SPA (`public/`) plus a health endpoint (`api/health.js`); `GET /api/health` returns service status JSON.
|
|
29
|
+
|
|
30
|
+
### Cloud commands
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Admin PC: authenticate to the cloud, then push local account tokens
|
|
34
|
+
# (the proxy's quota snapshot rides along so the dashboard shows 5h/weekly/Fable usage)
|
|
35
|
+
teamclaude cloud login --email <you@example.com>
|
|
36
|
+
teamclaude cloud push
|
|
37
|
+
|
|
38
|
+
# Groups: auth keys belong to a group (default "main"); accounts are allowed per GROUP.
|
|
39
|
+
teamclaude cloud group list
|
|
40
|
+
teamclaude cloud group create team-a
|
|
41
|
+
teamclaude cloud group allow team-a --accounts <uuid1>,<uuid2> # order = priority (#0 first)
|
|
42
|
+
teamclaude cloud group rename team-a --to team-alpha
|
|
43
|
+
teamclaude cloud group delete team-a # its keys move to "main"
|
|
44
|
+
|
|
45
|
+
# Auth keys (raw key printed once)
|
|
46
|
+
teamclaude cloud key create --label laptop --group team-a
|
|
47
|
+
teamclaude cloud key list
|
|
48
|
+
teamclaude cloud key move <key-id> --group main
|
|
49
|
+
teamclaude cloud key revoke <key-id>
|
|
50
|
+
|
|
51
|
+
# Global account switch (excluded from EVERY group's pull when disabled)
|
|
52
|
+
teamclaude cloud account disable <accountUuid|localName>
|
|
53
|
+
teamclaude cloud account enable <accountUuid|localName>
|
|
54
|
+
|
|
55
|
+
# Consumer PC: pull the key's group's accounts (tokens + priority) into the local config
|
|
56
|
+
# (key-only — no `cloud login`; the endpoint is built in). The key is saved so the
|
|
57
|
+
# running server keeps syncing on its own.
|
|
58
|
+
teamclaude cloud pull --key tck_…
|
|
59
|
+
teamclaude server
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Group exclusivity:** an account can be *enabled* in at most one group at a time (DB-enforced with a partial unique index; the API answers `409 account_in_use` naming the conflicting group) — so two groups can never pull, and therefore never concurrently use, the same account.
|
|
63
|
+
|
|
64
|
+
**Priority & selection:** the group's account order is delivered as each pulled account's `priority`, which the local proxy's existing selection consumes unchanged — explicit priority first, then the same use-or-lose algorithm (weekly reset soonest, session reset, lowest usage) as always.
|
|
65
|
+
|
|
66
|
+
**Live sync (running server ⇄ cloud).** `cloud pull` stores the endpoint + auth key, so a running `teamclaude server` re-pulls on an interval (`config.cloudSyncIntervalMs`, default 30 s; `0` disables) and applies changes **without a restart**: refreshed tokens are updated in place (freshest-token-wins), accounts added to / removed from the group appear / disappear live, and **revoking the key** drops all of its accounts and the proxy refuses new requests (it self-heals the moment you re-pull with a valid key). The rotation/quota/failover *handling* still runs entirely in the local proxy (unchanged algorithm) — the cloud is only the control plane feeding it fresh config. A **cloud outage is non-blocking**: a transient pull error keeps the last-known accounts and retries next interval; only an explicit `403` (revoked/invalid key) removes accounts.
|
|
67
|
+
|
|
68
|
+
**What revocation does and does not do.** A pull hands the consumer both the **access and refresh** tokens for each allowed account. Server-side controls — **revoking the key** (it can no longer pull) and **disabling/removing the account from the group** (it stops being handed out) — only cut off *future pulls*. They do **not** invalidate a token a consumer already pulled: that consumer holds the refresh token and can keep minting new access tokens directly against Anthropic until Anthropic itself revokes it (this proxy cannot remotely invalidate an OAuth grant). So treat a pulled key/account as "access granted until the underlying OAuth credential is rotated at the source." The client-side prune (dropping local accounts a key no longer allows, scoped per key) — applied automatically by the running server's live sync (above), or via TUI **R** / restart — is **hygiene/convenience**, not a hard revocation boundary. One consumer is expected to use **one key**.
|
|
69
|
+
|
|
70
|
+
Sync is **freshest-token-wins**: a push/pull never overwrites a newer token with an older one (compared by normalized `expiresAt`), so a token refreshed on any machine wins. Pulled `priority` applies even when the token itself is kept-local (control-plane vs token freshness). The cloud layer is additive — if the cloud is unreachable the local proxy still runs on whatever accounts are in the local config.
|
|
71
|
+
|
|
72
|
+
The cloud API lives at `auth.teamclaude.cloud` — the CLI ships no backing-infrastructure hosts or keys; consumers authenticate with their auth key, admins with `cloud login`.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Base proxy
|
|
77
|
+
|
|
78
|
+
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.
|
|
79
|
+
|
|
80
|
+

|
|
81
|
+
|
|
82
|
+
## Features
|
|
83
|
+
|
|
84
|
+
- **Use-or-lose account priority** — measures each account once at startup, then prioritizes the account whose weekly (7d) quota resets soonest (then soonest session reset, then lowest usage), so quota about to renew unused is drained first; re-evaluates every 5 minutes and switches immediately when the active account reaches the quota threshold (default 98%). Pin explicit ranks in the TUI (`o`) or via `teamclaude priority` for the accounts you want first — everything unranked stays on this automatic (`auto`) ordering
|
|
85
|
+
- **Instant failover on 429** — an exhausted account (token quota hit) is throttled for its `retry-after` (clamped to 1s–5m) and skipped; a rate/concurrency 429 (quota left but hit too fast) fails the request over to another account so concurrent overflow spreads instead of erroring. Either way nothing blocks, and a request-global 429 only passes through after every account has been tried — never throttling the fleet
|
|
86
|
+
- **Interactive TUI** — real-time dashboard with color-coded quota bars showing usage %, reset countdowns, an activity log, and keyboard controls (switch, enable/disable, reorder accounts)
|
|
87
|
+
- **Manual account controls** — enable/disable accounts and pin an explicit account order from the TUI or CLI (`teamclaude disable|enable|priority`); a disabled account is excluded from rotation while its in-flight requests drain, and everything unranked stays on automatic use-or-lose ordering
|
|
88
|
+
- **Quota survives restarts** — per-account quota state *and* the warm-up probe template are snapshotted to `<config>.quota.json` (every minute and on exit) and restored at startup, so a restart doesn't blank the dashboard, blind the account ordering, or leave forced re-measure (**R**) dead until traffic flows again
|
|
89
|
+
- **Active warm-up** — after a (re)start the proxy probes still-unmeasured accounts with a minimal request (reusing the shape of the first real request, restored across restarts), so the whole fleet's quota populates within seconds instead of waiting for traffic to reach each account
|
|
90
|
+
- **Server lifecycle** — `teamclaude stop` / `teamclaude restart` cleanly stop or replace the running server from any terminal
|
|
91
|
+
- **OAuth token management** — automatically refreshes tokens nearing expiry and persists them to config; client token refreshes pass through untouched
|
|
92
|
+
- **Hot-reload accounts** — add accounts via `import` or `login` while the server is running, press **R** to pick them up; **R** also force-re-measures the whole fleet's quota, so the dashboard reflects usage spent outside this proxy (other devices/sessions) — and works right after a restart, since the probe template is restored from the snapshot
|
|
93
|
+
- **Account deduplication** — detects duplicate accounts by UUID and keeps the most recent
|
|
94
|
+
- **Request logging** — optional full request/response logging for debugging
|
|
95
|
+
- **Zero dependencies** — uses only Node.js built-in modules
|
|
96
|
+
|
|
97
|
+
## Quick Start
|
|
98
|
+
|
|
99
|
+
Requires Node.js 18+.
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# Install (published to npm — auto-updates via `npm update -g` or `teamclaude update`)
|
|
103
|
+
npm install -g teamclaude-cloud
|
|
104
|
+
|
|
105
|
+
# Add your first account (opens browser for OAuth)
|
|
106
|
+
teamclaude login
|
|
107
|
+
|
|
108
|
+
# Add a second account
|
|
109
|
+
teamclaude login
|
|
110
|
+
|
|
111
|
+
# Start the proxy
|
|
112
|
+
teamclaude server
|
|
113
|
+
|
|
114
|
+
# In another terminal, run Claude Code through the proxy
|
|
115
|
+
teamclaude run
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
You can also import existing Claude Code credentials instead of logging in:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
claude /login # Log into an account in Claude Code
|
|
122
|
+
teamclaude import # Import its credentials
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Adding Accounts
|
|
126
|
+
|
|
127
|
+
### OAuth Login (recommended)
|
|
128
|
+
|
|
129
|
+
The easiest way to add accounts — opens your browser for authentication:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
teamclaude login
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Uses the same OAuth flow as Claude Code. Auto-detects the account email and subscription tier. Logging in with the same account again updates its credentials.
|
|
136
|
+
|
|
137
|
+
You can add accounts while the server is running — press **R** in the TUI to reload.
|
|
138
|
+
|
|
139
|
+
### Import from Claude Code
|
|
140
|
+
|
|
141
|
+
If you already have Claude Code set up, you can import its credentials directly:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
claude /login # Log into an account in Claude Code
|
|
145
|
+
teamclaude import # Import its credentials
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Re-importing the same account updates its credentials. You can also import from a custom path:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
teamclaude import --from /path/to/credentials.json
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### API Key
|
|
155
|
+
|
|
156
|
+
For Anthropic API key accounts (billed via Console):
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
teamclaude login --api
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Usage
|
|
163
|
+
|
|
164
|
+
### Start the proxy server
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
teamclaude server
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
When running from a TTY, shows an interactive TUI with:
|
|
171
|
+
- Account table with **numbered rows** and session/weekly quota progress bars (usage % overlaid, plus a reset countdown when space allows); wide terminals add a third `Fbl` bar with the model-scoped weekly limit (the separate "Fable" weekly limit from Claude's usage UI). Ranked accounts are listed first, then the `auto` accounts in their actual drain order (weekly reset soonest first)
|
|
172
|
+
- Real-time activity log with request tracking
|
|
173
|
+
- Keyboard shortcuts (see below)
|
|
174
|
+
|
|
175
|
+
Falls back to plain log output when not a TTY (e.g. running as a service).
|
|
176
|
+
|
|
177
|
+
If the configured port is already in use — for example another TeamClaude proxy is already running — the server prints a clear message and exits instead of crashing with an unhandled error. Inspect the existing one with `teamclaude status`, or find the listener with `lsof -nP -iTCP:<port> -sTCP:LISTEN`.
|
|
178
|
+
|
|
179
|
+
#### TUI Keyboard Shortcuts
|
|
180
|
+
|
|
181
|
+
| Key | Action |
|
|
182
|
+
|-----|--------|
|
|
183
|
+
| `↑`/`↓` | Move the selection cursor over the accounts |
|
|
184
|
+
| `s` | Switch active account (to the selected one) |
|
|
185
|
+
| `e` | Enable / disable the selected account |
|
|
186
|
+
| `o` | Order the selected account: `↑`/`↓` move its rank, `a` resets the WHOLE order to `auto` (weekly-reset ordering), `c` clears just this account's rank |
|
|
187
|
+
| `a` | Add account (import or API key) |
|
|
188
|
+
| `d` | Delete an account (with confirmation) |
|
|
189
|
+
| `R` | Reload accounts from config **and re-measure every account's quota** — revives lapsed OAuth tokens first, includes the model-scoped Fable window, and reports an honest `M/N` when some accounts fail or are skipped |
|
|
190
|
+
| `q` | Quit |
|
|
191
|
+
|
|
192
|
+
In selection mode, use `j`/`k` or arrow keys to navigate, `Enter` to confirm, `Esc` to cancel.
|
|
193
|
+
|
|
194
|
+
### Stop / restart the server
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
teamclaude stop # SIGTERM the running server (escalates to SIGKILL if needed)
|
|
198
|
+
teamclaude restart # stop the running server (if any) and start a fresh one
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
The running server is discovered via its state file (`<config>.server.json`) with a port-probe fallback, so `stop`/`restart` work from any terminal — even after a config port change. Quota state is restored on restart (see below), so a restart doesn't lose the dashboard.
|
|
202
|
+
|
|
203
|
+
> **Note:** if a Claude Code session is itself routed through the proxy (`teamclaude run`), running `teamclaude stop` *inside that session* severs its own API connection (`Unable to connect to API (ConnectionRefused)`). Stop or restart the proxy from a separate terminal instead — with `restart`, an in-flight session recovers on its own retries.
|
|
204
|
+
|
|
205
|
+
### Account order & manual controls
|
|
206
|
+
|
|
207
|
+
By default every account is on **`auto`** ordering (use-or-lose: weekly reset soonest is drained first). You can layer manual controls on top:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
teamclaude disable <name> # exclude from rotation (in-flight requests drain)
|
|
211
|
+
teamclaude enable <name> # re-enable
|
|
212
|
+
teamclaude priority <name> <n|auto> # pin explicit order (lower = preferred); "auto" clears it
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
In the TUI, `↑`/`↓` select an account, `e` toggles enable/disable, and `o` grabs the selected account into order mode: `↑`/`↓` move its rank, `a` resets the WHOLE order back to `auto`, `c` clears just that account's rank, `Enter`/`Esc` done. Ranked accounts render as `#1 #2 …` and are preferred first; everything unranked stays on the automatic ordering — so you can pin a few accounts and let the rest rotate.
|
|
216
|
+
|
|
217
|
+
CLI changes made while the server is running are picked up with **R** (reload) in the TUI or `teamclaude restart`.
|
|
218
|
+
|
|
219
|
+
### Run Claude Code through the proxy
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
teamclaude run
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Or manually set the environment:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
eval $(teamclaude env)
|
|
229
|
+
claude
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Other commands
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
teamclaude accounts # List accounts with subscription tier and token status
|
|
236
|
+
teamclaude accounts -v # Also show token expiry times
|
|
237
|
+
teamclaude status # Show live proxy status (requires running server)
|
|
238
|
+
teamclaude stop # Stop the running proxy server
|
|
239
|
+
teamclaude restart # Stop the running server and start a fresh one
|
|
240
|
+
teamclaude remove <name> # Remove an account
|
|
241
|
+
teamclaude disable <name> # Disable an account (excluded from rotation)
|
|
242
|
+
teamclaude enable <name> # Re-enable a disabled account
|
|
243
|
+
teamclaude priority <name> <n|auto> # Pin selection order (lower = preferred; "auto" clears)
|
|
244
|
+
teamclaude api <path> # Call an API endpoint with account credentials
|
|
245
|
+
teamclaude help # Show all commands
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Request logging
|
|
249
|
+
|
|
250
|
+
Log full request/response details to a directory (one file per request):
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
teamclaude server --log-to /tmp/requests
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Configuration
|
|
257
|
+
|
|
258
|
+
Config is stored at `~/.config/teamclaude.json` (or `$XDG_CONFIG_HOME/teamclaude.json`). A random proxy API key is generated on first use.
|
|
259
|
+
|
|
260
|
+
Override the config path with `TEAMCLAUDE_CONFIG`:
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
TEAMCLAUDE_CONFIG=./my-config.json teamclaude server
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Config format
|
|
267
|
+
|
|
268
|
+
```json
|
|
269
|
+
{
|
|
270
|
+
"proxy": {
|
|
271
|
+
"port": 3456,
|
|
272
|
+
"apiKey": "tc-auto-generated-key"
|
|
273
|
+
},
|
|
274
|
+
"upstream": "https://api.anthropic.com",
|
|
275
|
+
"switchThreshold": 0.98,
|
|
276
|
+
"accounts": [
|
|
277
|
+
{
|
|
278
|
+
"name": "user@example.com",
|
|
279
|
+
"type": "oauth",
|
|
280
|
+
"accountUuid": "...",
|
|
281
|
+
"accessToken": "sk-ant-oat01-...",
|
|
282
|
+
"refreshToken": "sk-ant-ort01-...",
|
|
283
|
+
"expiresAt": 1774384968427,
|
|
284
|
+
"enabled": true,
|
|
285
|
+
"priority": 0
|
|
286
|
+
}
|
|
287
|
+
]
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
| Field | Description |
|
|
292
|
+
|-------|-------------|
|
|
293
|
+
| `proxy.port` | Local port the proxy listens on |
|
|
294
|
+
| `proxy.apiKey` | API key clients use to authenticate with the proxy |
|
|
295
|
+
| `upstream` | Upstream API base URL |
|
|
296
|
+
| `switchThreshold` | Quota utilization (0–1) at which an account is considered full and skipped |
|
|
297
|
+
| `reevalIntervalMs` | How often (ms) to re-rank accounts by priority while the active one is healthy (optional, default `300000` = 5 min). Set to `0` to disable the timer entirely — the active account then only changes when it becomes unavailable or via per-request 429 failover |
|
|
298
|
+
| `activeWarmup` | Probe unmeasured accounts after a restart to populate quota (optional, default `true`) |
|
|
299
|
+
| `warmupIntervalMs` | How often (ms) the active warm-up re-probes accounts whose quota window reset (optional, default `300000` = 5 min; `0` = startup-only) |
|
|
300
|
+
| `accounts[].enabled` | Set `false` to exclude the account from rotation (optional, default `true`) |
|
|
301
|
+
| `accounts[].priority` | Explicit selection rank (lower = preferred first; optional — unset means automatic use-or-lose ordering) |
|
|
302
|
+
|
|
303
|
+
## How It Works
|
|
304
|
+
|
|
305
|
+
1. Claude Code connects to the local proxy instead of `api.anthropic.com`
|
|
306
|
+
2. The proxy selects the active account and forwards requests with that account's credentials
|
|
307
|
+
3. OAuth tokens expiring within 5 minutes are automatically refreshed and persisted to config
|
|
308
|
+
4. Rate limit headers from the API (`anthropic-ratelimit-unified-*`) track session (5h) and weekly (7d) quota utilization. Model-scoped weekly windows (`7d_oi` — the separate "Fable" weekly limit) are tracked and displayed too, but never affect routing: an account over its Fable weekly limit still serves every other model
|
|
309
|
+
5. **Cold-start warm-up**: quota is only known after a request flows through an account, so at startup the proxy first routes requests to any unmeasured account until every account has been measured once. An **active warm-up** additionally probes unmeasured accounts directly — a minimal 1-token request reusing the shape of the first real request — so the whole fleet is measured within seconds of the first post-restart request instead of waiting for traffic to reach each account (`activeWarmup: false` disables it). Then account selection becomes **use-or-lose**: among accounts still under the threshold, it prefers the one whose weekly (7d) quota resets soonest (tie-breaks: soonest session reset, then lowest usage), so quota about to renew unused is drained first. Explicitly ranked accounts (`priority` / TUI `o`) are preferred before all of that; disabled accounts are excluded entirely. The active account stays sticky to keep its prompt cache warm; priority is re-evaluated every `reevalIntervalMs` (default 5 min; set `0` to disable timer-based switching), and on reaching the threshold it switches immediately to the next-highest-priority account
|
|
310
|
+
6. On a 429 the proxy classifies it (never sleeping holding the client connection):
|
|
311
|
+
- **Account-quota exhaustion** (upstream reports the account is over its limit) → marks that account rate-limited for its `retry-after` (clamped to 1s–5m) and immediately re-dispatches to the next available account. If every account is throttled it returns 429 with a computed `retry-after`. (This also keeps cold-start warm-up fast: an exhausted account is skipped in one round-trip.)
|
|
312
|
+
- **Rate/concurrency or transient 429** (account has token quota left but was hit too fast, or a transient limit) → the request fails over to another available account (per-request, without throttling the account), so concurrent overflow spreads to an idle account instead of erroring. If *every* account has been tried for the request (→ effectively global), the 429 is passed through — still without throttling any account, so the fleet isn't poisoned.
|
|
313
|
+
7. Transient network errors (connection reset, timeout) drop the connection so the client can retry
|
|
314
|
+
8. If all accounts are exhausted, returns 429 with a `retry-after` computed from the soonest account reset — the real unified 5h/7d (or standard) reset of whichever over-threshold window is actually blocking each account, so clients back off until quota genuinely frees instead of retrying against a fixed 60s fallback. A merely concurrency-capped (but quota-healthy) fleet still gets the short fallback, since a freed slot is seconds away
|
|
315
|
+
9. **Quota survives restarts**: the server snapshots per-account quota/throttle state — plus the committed warm-up probe template — to `<config>.quota.json` (every minute and on exit) and restores both at startup, so a restart doesn't blank the dashboard, blind the use-or-lose ordering, or leave warm-up probes and forced re-measure (TUI **R**) without a known-accepted request shape until traffic flows again. A restored template is provisional: the first freshly accepted request shape replaces it (the snapshot's model may have been retired since). Expired windows are swept lazily and re-measured from live traffic
|
|
316
|
+
10. Client token refresh requests (`/v1/oauth/token`) are relayed to upstream untouched — the proxy and client manage their own token lifecycles independently
|
|
317
|
+
|
|
318
|
+
## License
|
|
319
|
+
|
|
320
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "teamclaude-cloud",
|
|
3
|
+
"version": "1.4.1",
|
|
4
|
+
"description": "Multi-account Claude proxy with quota-based rotation + cloud token sync and auth-key control",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"teamclaude": "src/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"src/"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"start": "node src/index.js",
|
|
15
|
+
"test": "node --test",
|
|
16
|
+
"lint": "eslint ."
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"claude",
|
|
20
|
+
"anthropic",
|
|
21
|
+
"proxy",
|
|
22
|
+
"load-balancer",
|
|
23
|
+
"multi-account",
|
|
24
|
+
"cloud-sync"
|
|
25
|
+
],
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/jung-wan-kim/teamclaude-cloud.git"
|
|
29
|
+
},
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/jung-wan-kim/teamclaude-cloud/issues"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://teamclaude-cloud.vercel.app",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18.0.0"
|
|
37
|
+
}
|
|
38
|
+
}
|