claude-threads 1.6.3 → 1.7.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.
@@ -53500,6 +53500,64 @@ import { fileURLToPath as fileURLToPath3 } from "url";
53500
53500
  import { existsSync as existsSync4, readFileSync as readFileSync3, watchFile, unwatchFile, unlinkSync, statSync, readdirSync } from "fs";
53501
53501
  import { tmpdir } from "os";
53502
53502
  import { join as join3 } from "path";
53503
+
53504
+ // src/claude/rate-limit-detector.ts
53505
+ var RATE_LIMIT_PHRASES = [
53506
+ /usage limit reached/i,
53507
+ /rate[_\s-]?limit[_\s-]?error/i,
53508
+ /you have hit the rate limit/i,
53509
+ /quota (has been )?exceeded/i,
53510
+ /\b429\b.*(rate|limit|quota)/i
53511
+ ];
53512
+ var DEFAULT_COOLDOWN_MS = 60 * 60 * 1000;
53513
+ function detectRateLimit(text, now = Date.now()) {
53514
+ if (!text)
53515
+ return { detected: false };
53516
+ let matched;
53517
+ for (const phrase of RATE_LIMIT_PHRASES) {
53518
+ const m = text.match(phrase);
53519
+ if (m) {
53520
+ matched = m[0];
53521
+ break;
53522
+ }
53523
+ }
53524
+ if (!matched)
53525
+ return { detected: false };
53526
+ const resetAtEpochMs = extractResetAt(text, now);
53527
+ return { detected: true, matched, resetAtEpochMs };
53528
+ }
53529
+ function extractResetAt(text, now) {
53530
+ const relative = text.match(/(?:retry[_\s-]?after|resets?\s+in)\s+(\d+)\s*(second|minute|hour|day)s?/i);
53531
+ if (relative) {
53532
+ const value = parseInt(relative[1], 10);
53533
+ const unit = relative[2].toLowerCase();
53534
+ const unitMs = {
53535
+ second: 1000,
53536
+ minute: 60000,
53537
+ hour: 3600000,
53538
+ day: 86400000
53539
+ };
53540
+ return now + value * unitMs[unit];
53541
+ }
53542
+ const unix = text.match(/["']?reset(?:_at)?["']?\s*[:=]\s*(\d{10,13})/);
53543
+ if (unix) {
53544
+ const raw = parseInt(unix[1], 10);
53545
+ return unix[1].length === 13 ? raw : raw * 1000;
53546
+ }
53547
+ const clock = text.match(/resets?\s+at\s+(\d{1,2}):(\d{2})\s*(utc|gmt)?/i);
53548
+ if (clock) {
53549
+ const hh = parseInt(clock[1], 10);
53550
+ const mm = parseInt(clock[2], 10);
53551
+ if (hh < 24 && mm < 60) {
53552
+ const reference = new Date(now);
53553
+ const target = new Date(Date.UTC(reference.getUTCFullYear(), reference.getUTCMonth(), reference.getUTCDate(), hh, mm)).getTime();
53554
+ return target > now ? target : target + 86400000;
53555
+ }
53556
+ }
53557
+ return;
53558
+ }
53559
+
53560
+ // src/claude/cli.ts
53503
53561
  var log7 = createLogger("claude");
53504
53562
  function cleanupBrowserBridgeSockets() {
53505
53563
  try {
@@ -53521,6 +53579,14 @@ function cleanupBrowserBridgeSockets() {
53521
53579
  log7.debug(`Browser bridge cleanup failed: ${err}`);
53522
53580
  }
53523
53581
  }
53582
+ function isErrorResultEvent(event) {
53583
+ const ev = event;
53584
+ if (typeof ev.subtype === "string" && ev.subtype.startsWith("error"))
53585
+ return true;
53586
+ if (ev.is_error === true)
53587
+ return true;
53588
+ return false;
53589
+ }
53524
53590
 
53525
53591
  class ClaudeCli extends EventEmitter2 {
53526
53592
  process = null;
@@ -53530,6 +53596,7 @@ class ClaudeCli extends EventEmitter2 {
53530
53596
  statusFilePath = null;
53531
53597
  lastStatusData = null;
53532
53598
  stderrBuffer = "";
53599
+ rateLimitEmitted = false;
53533
53600
  log;
53534
53601
  constructor(options2) {
53535
53602
  super();
@@ -53581,6 +53648,7 @@ class ClaudeCli extends EventEmitter2 {
53581
53648
  if (this.process)
53582
53649
  throw new Error("Already running");
53583
53650
  this.stderrBuffer = "";
53651
+ this.rateLimitEmitted = false;
53584
53652
  cleanupBrowserBridgeSockets();
53585
53653
  const claudePath = getClaudePath();
53586
53654
  const args = [
@@ -53650,9 +53718,13 @@ class ClaudeCli extends EventEmitter2 {
53650
53718
  args.push("--settings", JSON.stringify(statusLineSettings));
53651
53719
  }
53652
53720
  this.log.debug(`Starting: ${claudePath} ${args.slice(0, 5).join(" ")}...`);
53721
+ const childEnv = this.buildChildEnv();
53722
+ if (this.options.account) {
53723
+ this.log.debug(`Spawning under Claude account "${this.options.account.id}"`);
53724
+ }
53653
53725
  this.process = crossSpawn(claudePath, args, {
53654
53726
  cwd: this.options.workingDir,
53655
- env: process.env,
53727
+ env: childEnv,
53656
53728
  stdio: ["pipe", "pipe", "pipe"]
53657
53729
  });
53658
53730
  this.log.debug(`Claude process spawned: pid=${this.process.pid}`);
@@ -53666,6 +53738,7 @@ class ClaudeCli extends EventEmitter2 {
53666
53738
  this.stderrBuffer = this.stderrBuffer.slice(-10240);
53667
53739
  }
53668
53740
  this.log.debug(`stderr: ${text.trim()}`);
53741
+ this.maybeEmitRateLimit(text);
53669
53742
  });
53670
53743
  this.process.on("error", (err) => {
53671
53744
  this.log.error(`Claude error: ${err}`);
@@ -53720,9 +53793,22 @@ class ClaudeCli extends EventEmitter2 {
53720
53793
  try {
53721
53794
  const event = JSON.parse(trimmed);
53722
53795
  this.emit("event", event);
53796
+ if (event.type === "result" && isErrorResultEvent(event)) {
53797
+ this.maybeEmitRateLimit(trimmed);
53798
+ }
53723
53799
  } catch {}
53724
53800
  }
53725
53801
  }
53802
+ maybeEmitRateLimit(text) {
53803
+ const hit = detectRateLimit(text);
53804
+ if (!hit.detected)
53805
+ return;
53806
+ if (this.rateLimitEmitted)
53807
+ return;
53808
+ this.rateLimitEmitted = true;
53809
+ this.log.warn(`Rate limit detected: ${hit.matched ?? "(no match text)"}`);
53810
+ this.emit("rate-limit", hit);
53811
+ }
53726
53812
  isRunning() {
53727
53813
  return this.process !== null;
53728
53814
  }
@@ -53791,6 +53877,22 @@ class ClaudeCli extends EventEmitter2 {
53791
53877
  this.process.kill("SIGINT");
53792
53878
  return true;
53793
53879
  }
53880
+ buildChildEnv() {
53881
+ const account = this.options.account;
53882
+ if (!account)
53883
+ return process.env;
53884
+ const env = { ...process.env };
53885
+ if (account.home) {
53886
+ env.HOME = account.home;
53887
+ env.USERPROFILE = account.home;
53888
+ delete env.ANTHROPIC_API_KEY;
53889
+ delete env.CLAUDE_CODE_OAUTH_TOKEN;
53890
+ } else if (account.apiKey) {
53891
+ env.ANTHROPIC_API_KEY = account.apiKey;
53892
+ delete env.CLAUDE_CODE_OAUTH_TOKEN;
53893
+ }
53894
+ return env;
53895
+ }
53794
53896
  getMcpServerPath() {
53795
53897
  const __filename2 = fileURLToPath3(import.meta.url);
53796
53898
  const __dirname4 = dirname4(__filename2);
@@ -53811,42 +53913,6 @@ class ClaudeCli extends EventEmitter2 {
53811
53913
  }
53812
53914
  }
53813
53915
 
53814
- // src/update-notifier.ts
53815
- var import_semver2 = __toESM(require_semver2(), 1);
53816
-
53817
- // src/operations/commands/handler.ts
53818
- init_emoji();
53819
-
53820
- // src/utils/error-handler/index.ts
53821
- var log8 = createLogger("error");
53822
-
53823
- // src/utils/session-log.ts
53824
- function createSessionLog(baseLog) {
53825
- return (session) => {
53826
- if (session?.sessionId) {
53827
- return baseLog.forSession(session.sessionId);
53828
- }
53829
- return baseLog;
53830
- };
53831
- }
53832
-
53833
- // src/operations/post-helpers/index.ts
53834
- init_emoji();
53835
-
53836
- // src/git/worktree.ts
53837
- import * as path from "path";
53838
- import { homedir as homedir2 } from "os";
53839
- var log9 = createLogger("git-wt");
53840
- var WORKTREES_DIR = path.join(homedir2(), ".claude-threads", "worktrees");
53841
- var METADATA_STORE_PATH = path.join(homedir2(), ".claude-threads", "worktree-metadata.json");
53842
-
53843
- // src/operations/post-helpers/index.ts
53844
- var log10 = createLogger("helpers");
53845
- var sessionLog = createSessionLog(log10);
53846
-
53847
- // src/claude/quick-query.ts
53848
- var log11 = createLogger("query");
53849
-
53850
53916
  // src/commands/registry.ts
53851
53917
  var COMMAND_REGISTRY = [
53852
53918
  {
@@ -54399,6 +54465,36 @@ ${avoidCommands.map((c) => `- \`!${c.command}\` - ${c.reason}`).join(`
54399
54465
  `)}
54400
54466
  `.trim();
54401
54467
  }
54468
+ // src/utils/error-handler/index.ts
54469
+ var log8 = createLogger("error");
54470
+
54471
+ // src/utils/session-log.ts
54472
+ function createSessionLog(baseLog) {
54473
+ return (session) => {
54474
+ if (session?.sessionId) {
54475
+ return baseLog.forSession(session.sessionId);
54476
+ }
54477
+ return baseLog;
54478
+ };
54479
+ }
54480
+
54481
+ // src/operations/post-helpers/index.ts
54482
+ init_emoji();
54483
+
54484
+ // src/git/worktree.ts
54485
+ import * as path from "path";
54486
+ import { homedir as homedir2 } from "os";
54487
+ var log9 = createLogger("git-wt");
54488
+ var WORKTREES_DIR = path.join(homedir2(), ".claude-threads", "worktrees");
54489
+ var METADATA_STORE_PATH = path.join(homedir2(), ".claude-threads", "worktree-metadata.json");
54490
+
54491
+ // src/operations/post-helpers/index.ts
54492
+ var log10 = createLogger("helpers");
54493
+ var sessionLog = createSessionLog(log10);
54494
+
54495
+ // src/claude/quick-query.ts
54496
+ var log11 = createLogger("query");
54497
+
54402
54498
  // src/operations/suggestions/title.ts
54403
54499
  var log12 = createLogger("title");
54404
54500
 
@@ -54416,7 +54512,11 @@ var log15 = createLogger("lifecycle");
54416
54512
  var sessionLog3 = createSessionLog(log15);
54417
54513
  var CHAT_PLATFORM_PROMPT = generateChatPlatformPrompt();
54418
54514
 
54515
+ // src/update-notifier.ts
54516
+ var import_semver2 = __toESM(require_semver2(), 1);
54517
+
54419
54518
  // src/operations/commands/handler.ts
54519
+ init_emoji();
54420
54520
  var log16 = createLogger("commands");
54421
54521
  var sessionLog4 = createSessionLog(log16);
54422
54522
  // src/operations/suggestions/branch.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-threads",
3
- "version": "1.6.3",
3
+ "version": "1.7.0",
4
4
  "description": "Share Claude Code sessions live in a Mattermost channel with interactive features",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",