claude-pool 0.0.0-stage → 0.1.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.
- package/LICENSE +21 -0
- package/README.md +179 -2
- package/bin/cli.js +340 -0
- package/package.json +36 -4
- package/src/config.js +150 -0
- package/src/metrics.js +160 -0
- package/src/quota.js +104 -0
- package/src/runner.js +132 -0
- package/src/sessionSync.js +79 -0
- package/src/setup.js +187 -0
- package/src/ui.js +128 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kiran & Claude-Pool Contributors
|
|
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,3 +1,180 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 🔄 Claude Pool (`claude-pool`)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
[](https://claude.ai/code)
|
|
6
|
+
[](https://github.com/psf/black)
|
|
7
|
+
|
|
8
|
+
> **Pool multiple Claude subscriptions into one uninterrupted coding session with automated failover, live quota gauges, and cross-account memory sync.**
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## ⚡ The Problem
|
|
13
|
+
|
|
14
|
+
If you have multiple Claude subscriptions (personal, work, backup), hitting Anthropic's **5-hour rolling limit or weekly cap** mid-flow is painful:
|
|
15
|
+
1. You have to stop work.
|
|
16
|
+
2. Manually log out and log in with your other account.
|
|
17
|
+
3. Lose conversation context and file history.
|
|
18
|
+
4. Guess how many tokens or remaining quota you have left across accounts.
|
|
19
|
+
|
|
20
|
+
**`claude-pool` solves this completely.** It pools your accounts with customizable priority, monitors your limits in real-time, and **automatically transfers your active session transcript to your standby account** when one hits a limit. You never lose your flow.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## ✨ Features
|
|
25
|
+
|
|
26
|
+
- 🔄 **Seamless Session Continuity**: Automatically syncs active project transcripts (`<session-id>.jsonl`) to the standby account and resumes via `claude --resume`. No lost memory.
|
|
27
|
+
- 🎯 **Priority-Based Scheduling**: Strictly prioritize accounts (e.g. `Primary` → `Account 2` → `Account 3`).
|
|
28
|
+
- 📊 **Real-time Token Metrics**: Live token aggregation directly from active sessions (Input, Output, Cache Read/Create, per model).
|
|
29
|
+
- ⏳ **Quota & Countdown Tracking**: Real-time gauge bars showing exact `% session quota remaining` and reset timers (e.g. `resets at 7:10pm`).
|
|
30
|
+
- 🎨 **Minimalist Aesthetic UI**: Linear & Raycast-inspired terminal UI with clean pill badges and subtle borders.
|
|
31
|
+
- 🧙 **Zero-Friction Setup Wizard**: Run `claude-pool setup` to connect and authenticate all your accounts in 60 seconds.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 🚀 Quick Start (Zero-Setup with `npx`)
|
|
36
|
+
|
|
37
|
+
Since every Claude Code user already has Node.js and npm installed, you can run `claude-pool` instantly **with zero installation**:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Run setup wizard directly via npx:
|
|
41
|
+
npx claude-pool setup
|
|
42
|
+
|
|
43
|
+
# Or start pooled session:
|
|
44
|
+
npx claude-pool
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Install Globally (Recommended, exactly like `codeburn`)
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm install -g claude-pool
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Now `claude-pool` is available anywhere in your terminal:
|
|
54
|
+
```bash
|
|
55
|
+
claude-pool
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
*(Python users can also install via `pip install claude-pool`)*
|
|
59
|
+
|
|
60
|
+
The interactive wizard will:
|
|
61
|
+
- Detect your primary account (`~/.claude`).
|
|
62
|
+
- Guide you to add your secondary/standby Claude accounts.
|
|
63
|
+
- Open your browser to authenticate each account safely into isolated directories (`~/.claude-account2`, etc.).
|
|
64
|
+
- Set your priority rankings.
|
|
65
|
+
|
|
66
|
+
### 3. Start Coding!
|
|
67
|
+
|
|
68
|
+
Instead of typing `claude`, simply type:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
claude-pool
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
You can pass any standard Claude flags:
|
|
75
|
+
```bash
|
|
76
|
+
claude-pool --model opus
|
|
77
|
+
claude-pool -p "Summarize git diff"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 🖥️ Terminal Experience
|
|
83
|
+
|
|
84
|
+
### 1. Launch HUD Header
|
|
85
|
+
Anchored at the top of every session:
|
|
86
|
+
```
|
|
87
|
+
┌─ claude-pool ─────────────────────────────────────────────────────────────┐
|
|
88
|
+
│ Active Account primary [Priority 1] · 71% remaining (resets 7:10pm) │
|
|
89
|
+
│ Pool Today 121.4M tokens across 3 accounts │
|
|
90
|
+
│ Standby Queue account2, account3 │
|
|
91
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 2. Live Failover Transition Card
|
|
95
|
+
When an account reaches its 5-hour limit, `claude-pool` catches it and migrates the session:
|
|
96
|
+
```
|
|
97
|
+
╭─ Auto-Failover Transition ──────────────────────────────────────────────────╮
|
|
98
|
+
│ Rate limit reached on primary │
|
|
99
|
+
│ │
|
|
100
|
+
│ + Preserved active session: 6f1ff60d-9bb... │
|
|
101
|
+
│ + Synchronized project state to account2 │
|
|
102
|
+
│ -> Continuing on account2 (Priority 2) │
|
|
103
|
+
╰─────────────────────────────────────────────────────────────────────────────╯
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 3. Unified Pool Dashboard (`claude-pool dashboard`)
|
|
107
|
+
```
|
|
108
|
+
┌─ Token Pool Dashboard ────────────────────────────────────────────────────┐
|
|
109
|
+
│ Today's Pool Total: 121.4M │ Output: 941.2K │ Cache: 120.4M │
|
|
110
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
111
|
+
┌────┬──────────┬─────────┬──────────────┬───────────┬──────────────────┬─────┐
|
|
112
|
+
│ │ Account │ Status │ Rem. Quota │ Today's │ Model Breakdown │ Ses │
|
|
113
|
+
├────┼──────────┼─────────┼──────────────┼───────────┼──────────────────┼─────┤
|
|
114
|
+
│ P1 │ primary │ ACTIVE │ ██████░░ 71% │ 118.0M │ opus-5-5: 118.0M │ 50 │
|
|
115
|
+
│ P2 │ account2 │ STANDBY │ ███████░ 93% │ 3.4M │ fable-5-1: 3.4M │ 46 │
|
|
116
|
+
│ P3 │ account3 │ STANDBY │ ████████ 99% │ 0 │ - │ 12 │
|
|
117
|
+
└────┴──────────┴─────────┴──────────────┴───────────┴──────────────────┴─────┘
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## ⌨️ CLI Command Reference
|
|
123
|
+
|
|
124
|
+
| Command | Description |
|
|
125
|
+
|---|---|
|
|
126
|
+
| `claude-pool` | Launch Claude session with active account & auto-failover |
|
|
127
|
+
| `claude-pool setup` | Interactive wizard to connect all accounts from scratch |
|
|
128
|
+
| `claude-pool limits` / `remaining` | View exact % session and weekly quota remaining + reset countdowns |
|
|
129
|
+
| `claude-pool dashboard` | Unified pool dashboard with today's consumption & gauges |
|
|
130
|
+
| `claude-pool usage [account]` | Deep token breakdown (Input, Output, Cache Read/Create) per account |
|
|
131
|
+
| `claude-pool priority` | View priority order of accounts |
|
|
132
|
+
| `claude-pool set-priority <name> <p>` | Change account priority (e.g. `set-priority primary 1`) |
|
|
133
|
+
| `claude-pool priority --order a,b,c` | Reorder all accounts in one command |
|
|
134
|
+
| `claude-pool status` / `accounts` | List all accounts, directories, and health badges |
|
|
135
|
+
| `claude-pool check-auth` | Verify live authentication status across all accounts |
|
|
136
|
+
| `claude-pool login <account>` | Re-authenticate an account whose OAuth session expired |
|
|
137
|
+
| `claude-pool add [name]` | Add a new account to the pool interactively |
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## 🧠 How It Works Under The Hood
|
|
142
|
+
|
|
143
|
+
Claude Code stores project sessions as local JSONL transcripts:
|
|
144
|
+
```
|
|
145
|
+
~/<config_dir>/projects/<CWD-encoded>/<session-id>.jsonl
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
When an account exhausts its tokens:
|
|
149
|
+
1. `claude-pool` detects the limit exit code or message.
|
|
150
|
+
2. Identifies the active `<session-id>` and working directory.
|
|
151
|
+
3. Copies `<session-id>.jsonl` and sidecar tool results into the next standby account's configuration directory.
|
|
152
|
+
4. Sets `CLAUDE_CONFIG_DIR = ~/.claude-account2`.
|
|
153
|
+
5. Relaunches `claude --resume <session-id>` with zero loss of memory.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## 🛠️ Troubleshooting & FAQ
|
|
158
|
+
|
|
159
|
+
### What happens if an account's login expires?
|
|
160
|
+
OAuth tokens occasionally expire. Run:
|
|
161
|
+
```bash
|
|
162
|
+
claude-pool check-auth
|
|
163
|
+
```
|
|
164
|
+
If an account is flagged as `EXPIRED`, simply re-authenticate it with:
|
|
165
|
+
```bash
|
|
166
|
+
claude-pool login <account_name>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Can I pass custom Claude arguments?
|
|
170
|
+
Yes! Any arguments passed to `claude-pool` pass directly through to Claude Code:
|
|
171
|
+
```bash
|
|
172
|
+
claude-pool --model opus --verbose
|
|
173
|
+
claude-pool -p "Fix the failing tests in auth.py"
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## 📄 License
|
|
179
|
+
|
|
180
|
+
Distributed under the [MIT License](LICENSE).
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { ConfigManager } from '../src/config.js';
|
|
4
|
+
import { MetricsEngine } from '../src/metrics.js';
|
|
5
|
+
import { QuotaEngine } from '../src/quota.js';
|
|
6
|
+
import { ProcessRunner } from '../src/runner.js';
|
|
7
|
+
import { runSetupWizard, checkAuthStatus, runAccountLogin } from '../src/setup.js';
|
|
8
|
+
import { colors, formatTokenCount, makePill, makeGauge, printTable } from '../src/ui.js';
|
|
9
|
+
|
|
10
|
+
const KNOWN_COMMANDS = new Set([
|
|
11
|
+
'start',
|
|
12
|
+
'setup',
|
|
13
|
+
'limits',
|
|
14
|
+
'remaining',
|
|
15
|
+
'quota',
|
|
16
|
+
'dashboard',
|
|
17
|
+
'usage',
|
|
18
|
+
'tokens',
|
|
19
|
+
'priority',
|
|
20
|
+
'set-priority',
|
|
21
|
+
'status',
|
|
22
|
+
'accounts',
|
|
23
|
+
'check-auth',
|
|
24
|
+
'login',
|
|
25
|
+
'add',
|
|
26
|
+
'--help',
|
|
27
|
+
'-h',
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
const args = process.argv.slice(2);
|
|
31
|
+
const firstArg = args[0];
|
|
32
|
+
|
|
33
|
+
const cfg = new ConfigManager();
|
|
34
|
+
|
|
35
|
+
async function handleCommand() {
|
|
36
|
+
if (!firstArg || (!firstArg.startsWith('-') && !KNOWN_COMMANDS.has(firstArg))) {
|
|
37
|
+
// Default: run interactive session with failover
|
|
38
|
+
const runner = new ProcessRunner(cfg);
|
|
39
|
+
runner.runSession(args);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
switch (firstArg) {
|
|
44
|
+
case 'start': {
|
|
45
|
+
const runner = new ProcessRunner(cfg);
|
|
46
|
+
runner.runSession(args.slice(1));
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
case 'setup': {
|
|
51
|
+
await runSetupWizard();
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
case 'limits':
|
|
56
|
+
case 'remaining':
|
|
57
|
+
case 'quota': {
|
|
58
|
+
console.log(`\n${colors.indigo}Querying live Claude usage quotas & reset countdowns...${colors.reset}`);
|
|
59
|
+
const accounts = cfg.loadAccounts();
|
|
60
|
+
const force = args.includes('-r') || args.includes('--refresh');
|
|
61
|
+
QuotaEngine.fetchAllQuotas(accounts, force);
|
|
62
|
+
|
|
63
|
+
const headers = [
|
|
64
|
+
{ label: 'Rank', align: 'center' },
|
|
65
|
+
{ label: 'Priority', align: 'center' },
|
|
66
|
+
{ label: 'Account' },
|
|
67
|
+
{ label: 'Status', align: 'center' },
|
|
68
|
+
{ label: 'Session Remaining' },
|
|
69
|
+
{ label: 'Session Resets At' },
|
|
70
|
+
{ label: 'Weekly Remaining' },
|
|
71
|
+
{ label: 'Weekly Resets At' },
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
const rows = accounts.map((a, i) => [
|
|
75
|
+
i === 0 ? '#1' : `#${i + 1}`,
|
|
76
|
+
`P${a.priority}`,
|
|
77
|
+
a.name,
|
|
78
|
+
makePill(i === 0 ? 'active' : 'standby'),
|
|
79
|
+
a.quota ? makeGauge(a.quota.sessionRemainingPct) : `${colors.dim}—${colors.reset}`,
|
|
80
|
+
a.quota?.sessionReset || '—',
|
|
81
|
+
a.quota ? makeGauge(a.quota.weekRemainingPct) : `${colors.dim}—${colors.reset}`,
|
|
82
|
+
a.quota?.weekReset || '—',
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
console.log(`\n${colors.indigo}${colors.bold}Pool Quota & Remaining Usage${colors.reset}`);
|
|
86
|
+
printTable(headers, rows);
|
|
87
|
+
console.log();
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
case 'dashboard': {
|
|
92
|
+
const accounts = cfg.loadAccounts();
|
|
93
|
+
const stats = MetricsEngine.getPoolStats(accounts);
|
|
94
|
+
QuotaEngine.fetchAllQuotas(accounts, false);
|
|
95
|
+
|
|
96
|
+
console.log();
|
|
97
|
+
console.log(colors.border + '┌─ ' + colors.indigo + colors.bold + 'Token Pool Dashboard' + colors.border + ' ─'.repeat(45) + '┐' + colors.reset);
|
|
98
|
+
console.log(colors.border + '│' + colors.reset + ` Today's Pool: ${colors.emerald}${colors.bold}${formatTokenCount(stats.todayPoolTokens)}${colors.reset} │ Output: ${colors.terracotta}${formatTokenCount(stats.todayPoolOutput)}${colors.reset} │ Cache: ${colors.indigo}${formatTokenCount(stats.todayPoolCache)}${colors.reset} │ Lifetime: ${colors.bold}${formatTokenCount(stats.lifetimePoolCache + stats.lifetimePoolOutput)}${colors.reset}`);
|
|
99
|
+
console.log(colors.border + '└' + '─'.repeat(68) + '┘' + colors.reset);
|
|
100
|
+
console.log();
|
|
101
|
+
|
|
102
|
+
const headers = [
|
|
103
|
+
{ label: 'Priority', align: 'center' },
|
|
104
|
+
{ label: 'Account' },
|
|
105
|
+
{ label: 'Status', align: 'center' },
|
|
106
|
+
{ label: 'Remaining Quota' },
|
|
107
|
+
{ label: "Today's Tokens", align: 'right' },
|
|
108
|
+
{ label: 'Model Breakdown (Today)' },
|
|
109
|
+
{ label: 'Sessions', align: 'center' },
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
const rows = stats.accounts.map((s, i) => {
|
|
113
|
+
const acct = accounts.find(a => a.name === s.accountName) || { priority: 99 };
|
|
114
|
+
const bdown = Object.entries(s.todayTokensByModel)
|
|
115
|
+
.map(([m, t]) => `${m.replace('claude-', '')}: ${formatTokenCount(t)}`)
|
|
116
|
+
.join(' · ') || '—';
|
|
117
|
+
|
|
118
|
+
return [
|
|
119
|
+
`P${acct.priority}`,
|
|
120
|
+
s.accountName,
|
|
121
|
+
makePill(i === 0 ? 'active' : 'standby'),
|
|
122
|
+
acct.quota ? makeGauge(acct.quota.sessionRemainingPct, 8) : `${colors.dim}—${colors.reset}`,
|
|
123
|
+
`${colors.bold}${formatTokenCount(s.todayTotalTokens)}${colors.reset}`,
|
|
124
|
+
`${colors.slate}${bdown}${colors.reset}`,
|
|
125
|
+
String(s.totalSessions),
|
|
126
|
+
];
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
printTable(headers, rows);
|
|
130
|
+
console.log(`${colors.dim}Tip: Run ${colors.reset}${colors.indigo}claude-pool limits${colors.reset}${colors.dim} to inspect reset countdowns & weekly quotas.${colors.reset}\n`);
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
case 'usage':
|
|
135
|
+
case 'tokens': {
|
|
136
|
+
const targetName = args[1];
|
|
137
|
+
const accounts = cfg.loadAccounts();
|
|
138
|
+
|
|
139
|
+
const targets = targetName
|
|
140
|
+
? accounts.filter(a => a.name.toLowerCase() === targetName.toLowerCase())
|
|
141
|
+
: accounts;
|
|
142
|
+
|
|
143
|
+
if (targets.length === 0) {
|
|
144
|
+
console.log(`${colors.red}Account '${targetName}' not found in pool.${colors.reset}`);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
for (const acct of targets) {
|
|
149
|
+
const stat = MetricsEngine.readAccountStats(acct);
|
|
150
|
+
console.log();
|
|
151
|
+
console.log(colors.border + '┌─ ' + colors.indigo + colors.bold + `Token Breakdown: ${acct.name}` + colors.border + ' ─'.repeat(45) + '┐' + colors.reset);
|
|
152
|
+
console.log(colors.border + '│' + colors.reset + ` Account: ${colors.bold}${acct.name}${colors.reset} [Priority ${acct.priority}] ${colors.slate}· ${acct.email || acct.configDir}${colors.reset}`);
|
|
153
|
+
console.log(colors.border + '└' + '─'.repeat(71) + '┘' + colors.reset);
|
|
154
|
+
|
|
155
|
+
const headers = [
|
|
156
|
+
{ label: 'Model' },
|
|
157
|
+
{ label: 'Input Tokens', align: 'right' },
|
|
158
|
+
{ label: 'Output Tokens', align: 'right' },
|
|
159
|
+
{ label: 'Cache Read', align: 'right' },
|
|
160
|
+
{ label: 'Cache Create', align: 'right' },
|
|
161
|
+
{ label: 'Total Tokens', align: 'right' },
|
|
162
|
+
];
|
|
163
|
+
|
|
164
|
+
const rows = Object.entries(stat.todayModelsDetail).map(([model, u]) => [
|
|
165
|
+
model,
|
|
166
|
+
u.input.toLocaleString(),
|
|
167
|
+
u.output.toLocaleString(),
|
|
168
|
+
u.cacheRead.toLocaleString(),
|
|
169
|
+
u.cacheCreate.toLocaleString(),
|
|
170
|
+
`${colors.bold}${u.total.toLocaleString()} (${formatTokenCount(u.total)})${colors.reset}`,
|
|
171
|
+
]);
|
|
172
|
+
|
|
173
|
+
if (rows.length === 0) {
|
|
174
|
+
rows.push(['No activity today', '0', '0', '0', '0', '0']);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
console.log(`\n${colors.emerald}${colors.bold}Today's Usage (Total: ${formatTokenCount(stat.todayTotalTokens)})${colors.reset}`);
|
|
178
|
+
printTable(headers, rows);
|
|
179
|
+
console.log();
|
|
180
|
+
}
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
case 'priority': {
|
|
185
|
+
const accounts = cfg.loadAccounts();
|
|
186
|
+
const headers = [
|
|
187
|
+
{ label: 'Rank', align: 'center' },
|
|
188
|
+
{ label: 'Priority', align: 'center' },
|
|
189
|
+
{ label: 'Account' },
|
|
190
|
+
{ label: 'Status', align: 'center' },
|
|
191
|
+
{ label: 'Email' },
|
|
192
|
+
{ label: 'Config Directory' },
|
|
193
|
+
];
|
|
194
|
+
const rows = accounts.map((a, i) => [
|
|
195
|
+
i === 0 ? '#1' : `#${i + 1}`,
|
|
196
|
+
`P${a.priority}`,
|
|
197
|
+
a.name,
|
|
198
|
+
makePill(i === 0 ? 'active' : 'standby'),
|
|
199
|
+
a.email || '—',
|
|
200
|
+
a.configDir,
|
|
201
|
+
]);
|
|
202
|
+
console.log(`\n${colors.indigo}${colors.bold}Account Priority Ranking${colors.reset}`);
|
|
203
|
+
printTable(headers, rows);
|
|
204
|
+
console.log();
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
case 'set-priority': {
|
|
209
|
+
const name = args[1];
|
|
210
|
+
const prio = parseInt(args[2], 10);
|
|
211
|
+
if (!name || isNaN(prio)) {
|
|
212
|
+
console.log(`${colors.red}Usage: claude-pool set-priority <account_name> <priority_number>${colors.reset}`);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (cfg.updateAccountPriority(name, prio)) {
|
|
216
|
+
console.log(`${colors.emerald}✓ Priority for '${name}' set to P${prio}.${colors.reset}`);
|
|
217
|
+
} else {
|
|
218
|
+
console.log(`${colors.red}Error: Account '${name}' not found.${colors.reset}`);
|
|
219
|
+
}
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
case 'status':
|
|
224
|
+
case 'accounts': {
|
|
225
|
+
const accounts = cfg.loadAccounts();
|
|
226
|
+
const headers = [
|
|
227
|
+
{ label: 'Rank', align: 'center' },
|
|
228
|
+
{ label: 'Priority', align: 'center' },
|
|
229
|
+
{ label: 'Account' },
|
|
230
|
+
{ label: 'Status', align: 'center' },
|
|
231
|
+
{ label: 'Email' },
|
|
232
|
+
{ label: 'Plan' },
|
|
233
|
+
{ label: 'Config Directory' },
|
|
234
|
+
];
|
|
235
|
+
const rows = accounts.map((a, i) => [
|
|
236
|
+
i === 0 ? '#1' : `#${i + 1}`,
|
|
237
|
+
`P${a.priority}`,
|
|
238
|
+
a.name,
|
|
239
|
+
makePill(i === 0 ? 'active' : 'standby'),
|
|
240
|
+
a.email || '—',
|
|
241
|
+
a.subscriptionType || '—',
|
|
242
|
+
a.configDir,
|
|
243
|
+
]);
|
|
244
|
+
printTable(headers, rows);
|
|
245
|
+
console.log();
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
case 'check-auth': {
|
|
250
|
+
console.log(`\n${colors.indigo}Checking live authentication status across all accounts...${colors.reset}`);
|
|
251
|
+
const accounts = cfg.loadAccounts();
|
|
252
|
+
for (const a of accounts) {
|
|
253
|
+
const st = checkAuthStatus(a.configDir);
|
|
254
|
+
a.email = st.email;
|
|
255
|
+
a.subscriptionType = st.plan;
|
|
256
|
+
}
|
|
257
|
+
cfg.saveAccounts(accounts);
|
|
258
|
+
console.log(`${colors.emerald}✓ Authentication check complete.${colors.reset}\n`);
|
|
259
|
+
|
|
260
|
+
const headers = [
|
|
261
|
+
{ label: 'Rank', align: 'center' },
|
|
262
|
+
{ label: 'Priority', align: 'center' },
|
|
263
|
+
{ label: 'Account' },
|
|
264
|
+
{ label: 'Status', align: 'center' },
|
|
265
|
+
{ label: 'Email' },
|
|
266
|
+
{ label: 'Plan' },
|
|
267
|
+
];
|
|
268
|
+
const rows = accounts.map((a, i) => [
|
|
269
|
+
i === 0 ? '#1' : `#${i + 1}`,
|
|
270
|
+
`P${a.priority}`,
|
|
271
|
+
a.name,
|
|
272
|
+
makePill(a.email ? (i === 0 ? 'active' : 'standby') : 'auth_expired'),
|
|
273
|
+
a.email || '—',
|
|
274
|
+
a.subscriptionType || '—',
|
|
275
|
+
]);
|
|
276
|
+
printTable(headers, rows);
|
|
277
|
+
console.log();
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
case 'login': {
|
|
282
|
+
const name = args[1];
|
|
283
|
+
if (!name) {
|
|
284
|
+
console.log(`${colors.red}Usage: claude-pool login <account_name>${colors.reset}`);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
const accounts = cfg.loadAccounts();
|
|
288
|
+
const target = accounts.find(a => a.name.toLowerCase() === name.toLowerCase());
|
|
289
|
+
if (!target) {
|
|
290
|
+
console.log(`${colors.red}Account '${name}' not found.${colors.reset}`);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
runAccountLogin(target.configDir);
|
|
294
|
+
const st = checkAuthStatus(target.configDir);
|
|
295
|
+
if (st.ok) {
|
|
296
|
+
target.email = st.email;
|
|
297
|
+
target.subscriptionType = st.plan;
|
|
298
|
+
cfg.saveAccounts(accounts);
|
|
299
|
+
console.log(`${colors.emerald}✓ Account '${target.name}' authenticated as ${st.email}.${colors.reset}`);
|
|
300
|
+
}
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
case '--help':
|
|
305
|
+
case '-h': {
|
|
306
|
+
console.log(`
|
|
307
|
+
${colors.indigo}${colors.bold}Claude Pool${colors.reset} — Multi-account token pooling and seamless failover manager
|
|
308
|
+
|
|
309
|
+
${colors.bold}USAGE:${colors.reset}
|
|
310
|
+
npx claude-pool [COMMAND] [ARGS...]
|
|
311
|
+
claude-pool [COMMAND] [ARGS...]
|
|
312
|
+
|
|
313
|
+
${colors.bold}COMMANDS:${colors.reset}
|
|
314
|
+
${colors.indigo}claude-pool${colors.reset} Launch Claude session with pooled accounts & auto-failover
|
|
315
|
+
${colors.indigo}claude-pool setup${colors.reset} Guided setup wizard to connect accounts from scratch
|
|
316
|
+
${colors.indigo}claude-pool limits${colors.reset} Inspect remaining session/weekly % quota & reset timers
|
|
317
|
+
${colors.indigo}claude-pool dashboard${colors.reset} Real-time aggregated token metrics & model stats
|
|
318
|
+
${colors.indigo}claude-pool usage [acct]${colors.reset} Detailed token breakdown (input, output, cache) per account
|
|
319
|
+
${colors.indigo}claude-pool priority${colors.reset} View or reorder account priority order
|
|
320
|
+
${colors.indigo}claude-pool set-priority <name> <p>${colors.reset} Set priority rank for an account
|
|
321
|
+
${colors.indigo}claude-pool status${colors.reset} List configured accounts & health badges
|
|
322
|
+
${colors.indigo}claude-pool check-auth${colors.reset} Verify live authentication across all accounts
|
|
323
|
+
${colors.indigo}claude-pool login <acct>${colors.reset} Re-authenticate an expired account in browser
|
|
324
|
+
`);
|
|
325
|
+
break;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
default: {
|
|
329
|
+
// Pass through any other arguments directly to Claude Code
|
|
330
|
+
const runner = new ProcessRunner(cfg);
|
|
331
|
+
runner.runSession(args);
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
handleCommand().catch(err => {
|
|
338
|
+
console.error(`${colors.red}Error:${colors.reset}`, err.message);
|
|
339
|
+
process.exit(1);
|
|
340
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-pool",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Multi-account token pooling and seamless failover manager for Claude Code CLI",
|
|
5
|
+
"main": "bin/cli.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"claude-pool": "bin/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node ./bin/cli.js",
|
|
12
|
+
"test": "node --test tests/*.test.js"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"claude",
|
|
16
|
+
"claude-code",
|
|
17
|
+
"anthropic",
|
|
18
|
+
"token-pool",
|
|
19
|
+
"multi-account",
|
|
20
|
+
"failover",
|
|
21
|
+
"developer-tools",
|
|
22
|
+
"cli"
|
|
23
|
+
],
|
|
24
|
+
"author": "Kiran & Claude-Pool Contributors",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=18.0.0"
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/Kbastwad19/claude-pool.git"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/Kbastwad19/claude-pool/issues"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/Kbastwad19/claude-pool#readme",
|
|
37
|
+
"preferGlobal": true
|
|
38
|
+
}
|