maxpool 1.0.0 → 1.0.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/oauth.js +53 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "maxpool",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Multi-account Claude Code proxy with adaptive, rate-aware load balancing across Claude accounts",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/oauth.js CHANGED
@@ -1,19 +1,65 @@
1
1
  import { readFile } from 'node:fs/promises';
2
- import { homedir } from 'node:os';
2
+ import { homedir, userInfo } from 'node:os';
3
3
  import { randomBytes, createHash } from 'node:crypto';
4
- import { exec } from 'node:child_process';
4
+ import { exec, execFile } from 'node:child_process';
5
+ import { promisify } from 'node:util';
5
6
  import { createInterface } from 'node:readline';
6
7
  import http from 'node:http';
7
8
 
9
+ const execFileAsync = promisify(execFile);
10
+
11
+ const KEYCHAIN_SERVICE = 'Claude Code-credentials';
12
+
8
13
  /**
9
- * Import OAuth credentials from a Claude Code credentials file.
14
+ * Read Claude Code credentials from the macOS Keychain.
15
+ * Claude Code (recent versions, macOS) stores OAuth creds in the login
16
+ * Keychain under service "Claude Code-credentials", account = the OS
17
+ * username — NOT in ~/.claude/.credentials.json. Returns the parsed
18
+ * credential object (unwrapped from "claudeAiOauth"), or null if absent.
10
19
  */
11
- export async function importCredentials(filePath) {
20
+ async function readMacKeychainCredentials() {
21
+ if (process.platform !== 'darwin') return null;
22
+ const account = userInfo().username;
23
+ try {
24
+ const { stdout } = await execFileAsync('security', [
25
+ 'find-generic-password', '-s', KEYCHAIN_SERVICE, '-a', account, '-w',
26
+ ]);
27
+ const raw = JSON.parse(stdout.trim());
28
+ return raw.claudeAiOauth || raw;
29
+ } catch {
30
+ return null;
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Import OAuth credentials from a Claude Code credentials file, falling back
36
+ * to the macOS Keychain when the file is absent (the default on macOS).
37
+ */
38
+ export async function importCredentials(filePath = '~/.claude/.credentials.json') {
12
39
  const resolvedPath = filePath.replace(/^~/, homedir());
13
- const raw = JSON.parse(await readFile(resolvedPath, 'utf-8'));
14
40
 
15
- // Claude Code stores credentials nested under "claudeAiOauth"
16
- const data = raw.claudeAiOauth || raw;
41
+ let data;
42
+ try {
43
+ const raw = JSON.parse(await readFile(resolvedPath, 'utf-8'));
44
+ // Claude Code stores credentials nested under "claudeAiOauth"
45
+ data = raw.claudeAiOauth || raw;
46
+ } catch (fileErr) {
47
+ // No file → try the macOS Keychain (where Claude Code now stores creds).
48
+ data = await readMacKeychainCredentials();
49
+ if (!data) {
50
+ throw new Error(
51
+ process.platform === 'darwin'
52
+ ? `No credentials at ${resolvedPath} and none in the macOS Keychain ` +
53
+ `("${KEYCHAIN_SERVICE}"). Is Claude Code logged in on this machine? ` +
54
+ `Run 'claude' once to log in, or paste a token with 'maxpool import --json ...'.`
55
+ : `Could not read credentials from ${resolvedPath}: ${fileErr.message}`,
56
+ );
57
+ }
58
+ }
59
+
60
+ if (!data.accessToken) {
61
+ throw new Error('Imported credentials have no accessToken');
62
+ }
17
63
  return {
18
64
  accessToken: data.accessToken,
19
65
  refreshToken: data.refreshToken,