@karpeleslab/teamclaude 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@karpeleslab/teamclaude",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Multi-account Claude proxy with automatic quota-based rotation",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/index.js CHANGED
@@ -266,12 +266,14 @@ async function runCommand() {
266
266
  const claudeArgs = args.slice(1);
267
267
  if (claudeArgs[0] === '--') claudeArgs.shift();
268
268
 
269
+ // Only set ANTHROPIC_BASE_URL — Claude Code keeps its own OAuth token
270
+ // which the proxy accepts from localhost. Not setting ANTHROPIC_API_KEY
271
+ // lets Claude Code stay in subscription mode (full model access).
269
272
  const child = spawn('claude', claudeArgs, {
270
273
  stdio: 'inherit',
271
274
  env: {
272
275
  ...process.env,
273
276
  ANTHROPIC_BASE_URL: `http://localhost:${config.proxy.port}`,
274
- ANTHROPIC_API_KEY: config.proxy.apiKey,
275
277
  },
276
278
  });
277
279
 
@@ -332,6 +334,7 @@ async function statusCommand() {
332
334
 
333
335
  async function accountsCommand() {
334
336
  const config = await loadOrCreateConfig();
337
+ const verbose = args.includes('-v') || args.includes('--verbose');
335
338
 
336
339
  if (config.accounts.length === 0) {
337
340
  console.log('No accounts configured.');
@@ -387,6 +390,17 @@ async function accountsCommand() {
387
390
  console.log(` [${i + 1}] ${a.name} (${status}${src})`);
388
391
  if (p?.email && p.email !== a.name) console.log(` Email: ${p.email}`);
389
392
  if (p?.orgName) console.log(` Org: ${p.orgName}`);
393
+ if (verbose && a.expiresAt) {
394
+ const remaining = a.expiresAt - Date.now();
395
+ if (remaining <= 0) {
396
+ console.log(` Token: expired`);
397
+ } else {
398
+ const mins = Math.floor(remaining / 60000);
399
+ const hrs = Math.floor(mins / 60);
400
+ const expiry = hrs > 0 ? `${hrs}h ${mins % 60}m` : `${mins}m`;
401
+ console.log(` Token: expires in ${expiry}`);
402
+ }
403
+ }
390
404
  }
391
405
  }
392
406
 
package/src/server.js CHANGED
@@ -19,9 +19,11 @@ export function createProxyServer(accountManager, config, hooks = {}) {
19
19
 
20
20
  const server = http.createServer(async (req, res) => {
21
21
  try {
22
- // Auth check
22
+ // Auth check — skip for localhost connections
23
23
  const clientKey = req.headers['x-api-key'];
24
- if (proxyApiKey && clientKey !== proxyApiKey) {
24
+ const remoteAddr = req.socket.remoteAddress;
25
+ const isLocal = remoteAddr === '127.0.0.1' || remoteAddr === '::1' || remoteAddr === '::ffff:127.0.0.1';
26
+ if (proxyApiKey && clientKey !== proxyApiKey && !isLocal) {
25
27
  res.writeHead(401, { 'Content-Type': 'application/json' });
26
28
  res.end(JSON.stringify({
27
29
  type: 'error',
@@ -101,6 +103,7 @@ async function forwardRequest(req, res, body, accountManager, upstream, retryCou
101
103
  const account = accountManager.getActiveAccount();
102
104
  if (!account) {
103
105
  ctx.status = 429;
106
+ ctx.account = '(none available)';
104
107
  const status = accountManager.getStatus();
105
108
  const retryAfter = computeRetryAfter(status.accounts);
106
109
  res.writeHead(429, {
@@ -185,19 +188,6 @@ async function forwardRequest(req, res, body, accountManager, upstream, retryCou
185
188
  logSections.push(`=== RESPONSE ${upstreamRes.status} ===\n${formatHeaders(upstreamRes.headers)}`);
186
189
  }
187
190
 
188
- // Handle 429 — retry with next account
189
- if (upstreamRes.status === 429 && retryCount < maxRetries) {
190
- const retryAfter = parseInt(upstreamRes.headers.get('retry-after') || '60', 10);
191
- accountManager.markRateLimited(account.index, retryAfter);
192
- const drainBuf = await upstreamRes.arrayBuffer();
193
- if (logDir) {
194
- logSections.push(`=== RESPONSE BODY (429) ===\n${Buffer.from(drainBuf).toString()}`);
195
- logSections.push(`=== RETRYING with next account ===`);
196
- writeRequestLog(logDir, reqId, logSections);
197
- }
198
- return forwardRequest(req, res, body, accountManager, upstream, retryCount + 1, hooks, reqId, ctx, logDir);
199
- }
200
-
201
191
  ctx.status = upstreamRes.status;
202
192
 
203
193
  // Build response headers (skip hop-by-hop and encoding headers)