claude-pool 0.0.0-stage โ†’ 0.1.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/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,191 @@
1
- # Temporary Holding Version
1
+ # ๐Ÿ”„ Claude Pool (`claude-pool`)
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
4
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
5
+ [![Claude Code](https://img.shields.io/badge/Claude%20Code-Compatible-orange.svg)](https://claude.ai/code)
6
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](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 switch [account]` | Manually switch active account with zero session loss |
129
+ | `claude-pool limits` / `remaining` | View exact % session and weekly quota remaining + reset countdowns |
130
+ | `claude-pool dashboard` | Unified pool dashboard with today's consumption & gauges |
131
+ | `claude-pool usage [account]` | Deep token breakdown (Input, Output, Cache Read/Create) per account |
132
+ | `claude-pool priority` | View priority order of accounts |
133
+ | `claude-pool set-priority <name> <p>` | Change account priority (e.g. `set-priority primary 1`) |
134
+ | `claude-pool priority --order a,b,c` | Reorder all accounts in one command |
135
+ | `claude-pool status` / `accounts` | List all accounts, directories, and health badges |
136
+ | `claude-pool check-auth` | Verify live authentication status across all accounts |
137
+ | `claude-pool login <account>` | Re-authenticate an account whose OAuth session expired |
138
+ | `claude-pool add [name]` | Add a new account to the pool interactively |
139
+
140
+ ---
141
+
142
+ ## โšก In-Session Slash Commands
143
+
144
+ You don't need to exit an active Claude session to check limits or switch accounts! `claude-pool setup` automatically installs custom slash commands into all configured accounts:
145
+
146
+ - **`/limits`** (or `/pool-limits`): Prints real-time remaining quota percentages and reset timers across all accounts right inside your chat transcript.
147
+ - **`/pool`**: Displays the aggregated token usage dashboard.
148
+ - **`/switch <account>`** (or `/pool-switch <account>`): Transfers your active session transcript and working directory to the target account. Type `/exit` to seamlessly relaunch on the newly switched account with 100% memory preserved!
149
+
150
+ ---
151
+
152
+ ## ๐Ÿง  How It Works Under The Hood
153
+
154
+ Claude Code stores project sessions as local JSONL transcripts:
155
+ ```
156
+ ~/<config_dir>/projects/<CWD-encoded>/<session-id>.jsonl
157
+ ```
158
+
159
+ When an account exhausts its tokens:
160
+ 1. `claude-pool` detects the limit exit code or message.
161
+ 2. Identifies the active `<session-id>` and working directory.
162
+ 3. Copies `<session-id>.jsonl` and sidecar tool results into the next standby account's configuration directory.
163
+ 4. Sets `CLAUDE_CONFIG_DIR = ~/.claude-account2`.
164
+ 5. Relaunches `claude --resume <session-id>` with zero loss of memory.
165
+
166
+ ---
167
+
168
+ ## ๐Ÿ› ๏ธ Troubleshooting & FAQ
169
+
170
+ ### What happens if an account's login expires?
171
+ OAuth tokens occasionally expire. Run:
172
+ ```bash
173
+ claude-pool check-auth
174
+ ```
175
+ If an account is flagged as `EXPIRED`, simply re-authenticate it with:
176
+ ```bash
177
+ claude-pool login <account_name>
178
+ ```
179
+
180
+ ### Can I pass custom Claude arguments?
181
+ Yes! Any arguments passed to `claude-pool` pass directly through to Claude Code:
182
+ ```bash
183
+ claude-pool --model opus --verbose
184
+ claude-pool -p "Fix the failing tests in auth.py"
185
+ ```
186
+
187
+ ---
188
+
189
+ ## ๐Ÿ“„ License
190
+
191
+ Distributed under the [MIT License](LICENSE).
package/bin/cli.js ADDED
@@ -0,0 +1,392 @@
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
+ 'switch',
14
+ 'use',
15
+ 'limits',
16
+ 'remaining',
17
+ 'quota',
18
+ 'dashboard',
19
+ 'usage',
20
+ 'tokens',
21
+ 'priority',
22
+ 'set-priority',
23
+ 'status',
24
+ 'accounts',
25
+ 'check-auth',
26
+ 'login',
27
+ 'add',
28
+ '--help',
29
+ '-h',
30
+ ]);
31
+
32
+ const args = process.argv.slice(2);
33
+ const firstArg = args[0];
34
+
35
+ const cfg = new ConfigManager();
36
+
37
+ async function handleCommand() {
38
+ if (!firstArg || (!firstArg.startsWith('-') && !KNOWN_COMMANDS.has(firstArg))) {
39
+ // Default: run interactive session with failover
40
+ const runner = new ProcessRunner(cfg);
41
+ runner.runSession(args);
42
+ return;
43
+ }
44
+
45
+ switch (firstArg) {
46
+ case 'start': {
47
+ const runner = new ProcessRunner(cfg);
48
+ runner.runSession(args.slice(1));
49
+ break;
50
+ }
51
+
52
+ case 'setup': {
53
+ await runSetupWizard();
54
+ break;
55
+ }
56
+
57
+ case 'switch':
58
+ case 'use': {
59
+ let targetName = args[1];
60
+ const accounts = cfg.loadAccounts();
61
+
62
+ if (!targetName) {
63
+ console.log(`\n${colors.indigo}${colors.bold}Switch Active Claude Account:${colors.reset}\n`);
64
+ accounts.forEach((a, idx) => {
65
+ const activeTag = idx === 0 ? `${colors.emerald} [ACTIVE]${colors.reset}` : '';
66
+ const emailTag = a.email ? ` (${a.email})` : '';
67
+ console.log(` ${colors.bold}${idx + 1})${colors.reset} ${a.name}${emailTag}${activeTag}`);
68
+ });
69
+ console.log();
70
+ const { default: readline } = await import('readline/promises');
71
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
72
+ const choice = await rl.question(`Select account (1-${accounts.length}): `);
73
+ rl.close();
74
+ const chosenIdx = parseInt(choice.trim(), 10) - 1;
75
+ if (chosenIdx >= 0 && chosenIdx < accounts.length) {
76
+ targetName = accounts[chosenIdx].name;
77
+ } else {
78
+ console.log(`${colors.red}Invalid selection.${colors.reset}`);
79
+ return;
80
+ }
81
+ }
82
+
83
+ const prevActive = accounts[0];
84
+ const target = accounts.find(a => a.name.toLowerCase() === targetName.toLowerCase());
85
+ if (!target) {
86
+ console.log(`${colors.red}Error: Account '${targetName}' not found in pool.${colors.reset}`);
87
+ return;
88
+ }
89
+
90
+ target.rateLimitedUntil = null;
91
+ cfg.updateAccountPriority(target.name, 1);
92
+
93
+ if (prevActive && prevActive.name.toLowerCase() !== target.name.toLowerCase()) {
94
+ const { SessionSyncEngine } = await import('../src/sessionSync.js');
95
+ const syncedId = SessionSyncEngine.transferSession(prevActive, target);
96
+ if (syncedId) {
97
+ console.log(`${colors.emerald}โœ“ Synchronized active session (${syncedId.slice(0, 12)}...) to ${target.name}.${colors.reset}`);
98
+ }
99
+ }
100
+
101
+ console.log(`\n${colors.emerald}โœ“ Active account is now: ${colors.bold}${target.name} (Priority 1)${colors.reset}`);
102
+ console.log(`${colors.dim}Run ${colors.reset}${colors.indigo}claude-pool${colors.reset}${colors.dim} to start working on ${target.name}.${colors.reset}\n`);
103
+ break;
104
+ }
105
+
106
+ case 'limits':
107
+ case 'remaining':
108
+ case 'quota': {
109
+ console.log(`\n${colors.indigo}Querying live Claude usage quotas & reset countdowns...${colors.reset}`);
110
+ const accounts = cfg.loadAccounts();
111
+ const force = args.includes('-r') || args.includes('--refresh');
112
+ QuotaEngine.fetchAllQuotas(accounts, force);
113
+
114
+ const headers = [
115
+ { label: 'Rank', align: 'center' },
116
+ { label: 'Priority', align: 'center' },
117
+ { label: 'Account' },
118
+ { label: 'Status', align: 'center' },
119
+ { label: 'Session Remaining' },
120
+ { label: 'Session Resets At' },
121
+ { label: 'Weekly Remaining' },
122
+ { label: 'Weekly Resets At' },
123
+ ];
124
+
125
+ const rows = accounts.map((a, i) => [
126
+ i === 0 ? '#1' : `#${i + 1}`,
127
+ `P${a.priority}`,
128
+ a.name,
129
+ makePill(i === 0 ? 'active' : 'standby'),
130
+ a.quota ? makeGauge(a.quota.sessionRemainingPct) : `${colors.dim}โ€”${colors.reset}`,
131
+ a.quota?.sessionReset || 'โ€”',
132
+ a.quota ? makeGauge(a.quota.weekRemainingPct) : `${colors.dim}โ€”${colors.reset}`,
133
+ a.quota?.weekReset || 'โ€”',
134
+ ]);
135
+
136
+ console.log(`\n${colors.indigo}${colors.bold}Pool Quota & Remaining Usage${colors.reset}`);
137
+ printTable(headers, rows);
138
+ console.log();
139
+ break;
140
+ }
141
+
142
+ case 'dashboard': {
143
+ const accounts = cfg.loadAccounts();
144
+ const stats = MetricsEngine.getPoolStats(accounts);
145
+ QuotaEngine.fetchAllQuotas(accounts, false);
146
+
147
+ console.log();
148
+ console.log(colors.border + 'โ”Œโ”€ ' + colors.indigo + colors.bold + 'Token Pool Dashboard' + colors.border + ' โ”€'.repeat(45) + 'โ”' + colors.reset);
149
+ 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}`);
150
+ console.log(colors.border + 'โ””' + 'โ”€'.repeat(68) + 'โ”˜' + colors.reset);
151
+ console.log();
152
+
153
+ const headers = [
154
+ { label: 'Priority', align: 'center' },
155
+ { label: 'Account' },
156
+ { label: 'Status', align: 'center' },
157
+ { label: 'Remaining Quota' },
158
+ { label: "Today's Tokens", align: 'right' },
159
+ { label: 'Model Breakdown (Today)' },
160
+ { label: 'Sessions', align: 'center' },
161
+ ];
162
+
163
+ const rows = stats.accounts.map((s, i) => {
164
+ const acct = accounts.find(a => a.name === s.accountName) || { priority: 99 };
165
+ const bdown = Object.entries(s.todayTokensByModel)
166
+ .map(([m, t]) => `${m.replace('claude-', '')}: ${formatTokenCount(t)}`)
167
+ .join(' ยท ') || 'โ€”';
168
+
169
+ return [
170
+ `P${acct.priority}`,
171
+ s.accountName,
172
+ makePill(i === 0 ? 'active' : 'standby'),
173
+ acct.quota ? makeGauge(acct.quota.sessionRemainingPct, 8) : `${colors.dim}โ€”${colors.reset}`,
174
+ `${colors.bold}${formatTokenCount(s.todayTotalTokens)}${colors.reset}`,
175
+ `${colors.slate}${bdown}${colors.reset}`,
176
+ String(s.totalSessions),
177
+ ];
178
+ });
179
+
180
+ printTable(headers, rows);
181
+ 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`);
182
+ break;
183
+ }
184
+
185
+ case 'usage':
186
+ case 'tokens': {
187
+ const targetName = args[1];
188
+ const accounts = cfg.loadAccounts();
189
+
190
+ const targets = targetName
191
+ ? accounts.filter(a => a.name.toLowerCase() === targetName.toLowerCase())
192
+ : accounts;
193
+
194
+ if (targets.length === 0) {
195
+ console.log(`${colors.red}Account '${targetName}' not found in pool.${colors.reset}`);
196
+ return;
197
+ }
198
+
199
+ for (const acct of targets) {
200
+ const stat = MetricsEngine.readAccountStats(acct);
201
+ console.log();
202
+ console.log(colors.border + 'โ”Œโ”€ ' + colors.indigo + colors.bold + `Token Breakdown: ${acct.name}` + colors.border + ' โ”€'.repeat(45) + 'โ”' + colors.reset);
203
+ console.log(colors.border + 'โ”‚' + colors.reset + ` Account: ${colors.bold}${acct.name}${colors.reset} [Priority ${acct.priority}] ${colors.slate}ยท ${acct.email || acct.configDir}${colors.reset}`);
204
+ console.log(colors.border + 'โ””' + 'โ”€'.repeat(71) + 'โ”˜' + colors.reset);
205
+
206
+ const headers = [
207
+ { label: 'Model' },
208
+ { label: 'Input Tokens', align: 'right' },
209
+ { label: 'Output Tokens', align: 'right' },
210
+ { label: 'Cache Read', align: 'right' },
211
+ { label: 'Cache Create', align: 'right' },
212
+ { label: 'Total Tokens', align: 'right' },
213
+ ];
214
+
215
+ const rows = Object.entries(stat.todayModelsDetail).map(([model, u]) => [
216
+ model,
217
+ u.input.toLocaleString(),
218
+ u.output.toLocaleString(),
219
+ u.cacheRead.toLocaleString(),
220
+ u.cacheCreate.toLocaleString(),
221
+ `${colors.bold}${u.total.toLocaleString()} (${formatTokenCount(u.total)})${colors.reset}`,
222
+ ]);
223
+
224
+ if (rows.length === 0) {
225
+ rows.push(['No activity today', '0', '0', '0', '0', '0']);
226
+ }
227
+
228
+ console.log(`\n${colors.emerald}${colors.bold}Today's Usage (Total: ${formatTokenCount(stat.todayTotalTokens)})${colors.reset}`);
229
+ printTable(headers, rows);
230
+ console.log();
231
+ }
232
+ break;
233
+ }
234
+
235
+ case 'priority': {
236
+ const accounts = cfg.loadAccounts();
237
+ const headers = [
238
+ { label: 'Rank', align: 'center' },
239
+ { label: 'Priority', align: 'center' },
240
+ { label: 'Account' },
241
+ { label: 'Status', align: 'center' },
242
+ { label: 'Email' },
243
+ { label: 'Config Directory' },
244
+ ];
245
+ const rows = accounts.map((a, i) => [
246
+ i === 0 ? '#1' : `#${i + 1}`,
247
+ `P${a.priority}`,
248
+ a.name,
249
+ makePill(i === 0 ? 'active' : 'standby'),
250
+ a.email || 'โ€”',
251
+ a.configDir,
252
+ ]);
253
+ console.log(`\n${colors.indigo}${colors.bold}Account Priority Ranking${colors.reset}`);
254
+ printTable(headers, rows);
255
+ console.log();
256
+ break;
257
+ }
258
+
259
+ case 'set-priority': {
260
+ const name = args[1];
261
+ const prio = parseInt(args[2], 10);
262
+ if (!name || isNaN(prio)) {
263
+ console.log(`${colors.red}Usage: claude-pool set-priority <account_name> <priority_number>${colors.reset}`);
264
+ return;
265
+ }
266
+ if (cfg.updateAccountPriority(name, prio)) {
267
+ console.log(`${colors.emerald}โœ“ Priority for '${name}' set to P${prio}.${colors.reset}`);
268
+ } else {
269
+ console.log(`${colors.red}Error: Account '${name}' not found.${colors.reset}`);
270
+ }
271
+ break;
272
+ }
273
+
274
+ case 'status':
275
+ case 'accounts': {
276
+ const accounts = cfg.loadAccounts();
277
+ const headers = [
278
+ { label: 'Rank', align: 'center' },
279
+ { label: 'Priority', align: 'center' },
280
+ { label: 'Account' },
281
+ { label: 'Status', align: 'center' },
282
+ { label: 'Email' },
283
+ { label: 'Plan' },
284
+ { label: 'Config Directory' },
285
+ ];
286
+ const rows = accounts.map((a, i) => [
287
+ i === 0 ? '#1' : `#${i + 1}`,
288
+ `P${a.priority}`,
289
+ a.name,
290
+ makePill(i === 0 ? 'active' : 'standby'),
291
+ a.email || 'โ€”',
292
+ a.subscriptionType || 'โ€”',
293
+ a.configDir,
294
+ ]);
295
+ printTable(headers, rows);
296
+ console.log();
297
+ break;
298
+ }
299
+
300
+ case 'check-auth': {
301
+ console.log(`\n${colors.indigo}Checking live authentication status across all accounts...${colors.reset}`);
302
+ const accounts = cfg.loadAccounts();
303
+ for (const a of accounts) {
304
+ const st = checkAuthStatus(a.configDir);
305
+ a.email = st.email;
306
+ a.subscriptionType = st.plan;
307
+ }
308
+ cfg.saveAccounts(accounts);
309
+ console.log(`${colors.emerald}โœ“ Authentication check complete.${colors.reset}\n`);
310
+
311
+ const headers = [
312
+ { label: 'Rank', align: 'center' },
313
+ { label: 'Priority', align: 'center' },
314
+ { label: 'Account' },
315
+ { label: 'Status', align: 'center' },
316
+ { label: 'Email' },
317
+ { label: 'Plan' },
318
+ ];
319
+ const rows = accounts.map((a, i) => [
320
+ i === 0 ? '#1' : `#${i + 1}`,
321
+ `P${a.priority}`,
322
+ a.name,
323
+ makePill(a.email ? (i === 0 ? 'active' : 'standby') : 'auth_expired'),
324
+ a.email || 'โ€”',
325
+ a.subscriptionType || 'โ€”',
326
+ ]);
327
+ printTable(headers, rows);
328
+ console.log();
329
+ break;
330
+ }
331
+
332
+ case 'login': {
333
+ const name = args[1];
334
+ if (!name) {
335
+ console.log(`${colors.red}Usage: claude-pool login <account_name>${colors.reset}`);
336
+ return;
337
+ }
338
+ const accounts = cfg.loadAccounts();
339
+ const target = accounts.find(a => a.name.toLowerCase() === name.toLowerCase());
340
+ if (!target) {
341
+ console.log(`${colors.red}Account '${name}' not found.${colors.reset}`);
342
+ return;
343
+ }
344
+ runAccountLogin(target.configDir);
345
+ const st = checkAuthStatus(target.configDir);
346
+ if (st.ok) {
347
+ target.email = st.email;
348
+ target.subscriptionType = st.plan;
349
+ cfg.saveAccounts(accounts);
350
+ console.log(`${colors.emerald}โœ“ Account '${target.name}' authenticated as ${st.email}.${colors.reset}`);
351
+ }
352
+ break;
353
+ }
354
+
355
+ case '--help':
356
+ case '-h': {
357
+ console.log(`
358
+ ${colors.indigo}${colors.bold}Claude Pool${colors.reset} โ€” Multi-account token pooling and seamless failover manager
359
+
360
+ ${colors.bold}USAGE:${colors.reset}
361
+ npx claude-pool [COMMAND] [ARGS...]
362
+ claude-pool [COMMAND] [ARGS...]
363
+
364
+ ${colors.bold}COMMANDS:${colors.reset}
365
+ ${colors.indigo}claude-pool${colors.reset} Launch Claude session with pooled accounts & auto-failover
366
+ ${colors.indigo}claude-pool switch [acct]${colors.reset} Manually switch active account (or interactive picker)
367
+ ${colors.indigo}claude-pool setup${colors.reset} Guided setup wizard to connect accounts from scratch
368
+ ${colors.indigo}claude-pool limits${colors.reset} Inspect remaining session/weekly % quota & reset timers
369
+ ${colors.indigo}claude-pool dashboard${colors.reset} Real-time aggregated token metrics & model stats
370
+ ${colors.indigo}claude-pool usage [acct]${colors.reset} Detailed token breakdown (input, output, cache) per account
371
+ ${colors.indigo}claude-pool priority${colors.reset} View or reorder account priority order
372
+ ${colors.indigo}claude-pool set-priority <name> <p>${colors.reset} Set priority rank for an account
373
+ ${colors.indigo}claude-pool status${colors.reset} List configured accounts & health badges
374
+ ${colors.indigo}claude-pool check-auth${colors.reset} Verify live authentication across all accounts
375
+ ${colors.indigo}claude-pool login <acct>${colors.reset} Re-authenticate an expired account in browser
376
+ `);
377
+ break;
378
+ }
379
+
380
+ default: {
381
+ // Pass through any other arguments directly to Claude Code
382
+ const runner = new ProcessRunner(cfg);
383
+ runner.runSession(args);
384
+ break;
385
+ }
386
+ }
387
+ }
388
+
389
+ handleCommand().catch(err => {
390
+ console.error(`${colors.red}Error:${colors.reset}`, err.message);
391
+ process.exit(1);
392
+ });
package/package.json CHANGED
@@ -1,6 +1,38 @@
1
1
  {
2
2
  "name": "claude-pool",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
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
+ }