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.
- package/CHANGELOG.md +7 -0
- package/dist/index.js +576 -309
- package/dist/mcp/permission-server.js +137 -37
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11880,7 +11880,7 @@ import * as fs from "fs/promises";
|
|
|
11880
11880
|
import { homedir as homedir4 } from "os";
|
|
11881
11881
|
async function execGit(args, cwd) {
|
|
11882
11882
|
const cmd = `git ${args.join(" ")}`;
|
|
11883
|
-
|
|
11883
|
+
log8.debug(`Executing: ${cmd}`);
|
|
11884
11884
|
return new Promise((resolve3, reject) => {
|
|
11885
11885
|
const proc = crossSpawn("git", args, { cwd });
|
|
11886
11886
|
let stdout = "";
|
|
@@ -11893,15 +11893,15 @@ async function execGit(args, cwd) {
|
|
|
11893
11893
|
});
|
|
11894
11894
|
proc.on("close", (code) => {
|
|
11895
11895
|
if (code === 0) {
|
|
11896
|
-
|
|
11896
|
+
log8.debug(`${cmd} → success`);
|
|
11897
11897
|
resolve3(stdout.trim());
|
|
11898
11898
|
} else {
|
|
11899
|
-
|
|
11899
|
+
log8.debug(`${cmd} → failed (code=${code}): ${stderr.substring(0, 100) || stdout.substring(0, 100)}`);
|
|
11900
11900
|
reject(new Error(`git ${args.join(" ")} failed: ${stderr || stdout}`));
|
|
11901
11901
|
}
|
|
11902
11902
|
});
|
|
11903
11903
|
proc.on("error", (err) => {
|
|
11904
|
-
|
|
11904
|
+
log8.warn(`${cmd} → error: ${err}`);
|
|
11905
11905
|
reject(err);
|
|
11906
11906
|
});
|
|
11907
11907
|
});
|
|
@@ -11911,7 +11911,7 @@ async function isGitRepository(dir) {
|
|
|
11911
11911
|
await execGit(["rev-parse", "--git-dir"], dir);
|
|
11912
11912
|
return true;
|
|
11913
11913
|
} catch (err) {
|
|
11914
|
-
|
|
11914
|
+
log8.debug(`Not a git repository: ${dir} (${err})`);
|
|
11915
11915
|
return false;
|
|
11916
11916
|
}
|
|
11917
11917
|
}
|
|
@@ -12046,7 +12046,7 @@ async function detectWorktreeInfo(workingDir) {
|
|
|
12046
12046
|
const branchOutput = await execGit(["rev-parse", "--abbrev-ref", "HEAD"], workingDir);
|
|
12047
12047
|
const branch = branchOutput?.trim();
|
|
12048
12048
|
if (!branch) {
|
|
12049
|
-
|
|
12049
|
+
log8.debug(`Could not detect branch for worktree at ${workingDir}`);
|
|
12050
12050
|
return null;
|
|
12051
12051
|
}
|
|
12052
12052
|
const gitDirOutput = await execGit(["rev-parse", "--git-common-dir"], workingDir);
|
|
@@ -12058,45 +12058,45 @@ async function detectWorktreeInfo(workingDir) {
|
|
|
12058
12058
|
repoRoot = repoRoot.slice(0, -4);
|
|
12059
12059
|
}
|
|
12060
12060
|
}
|
|
12061
|
-
|
|
12061
|
+
log8.debug(`Detected worktree: path=${workingDir}, branch=${branch}, repoRoot=${repoRoot}`);
|
|
12062
12062
|
return {
|
|
12063
12063
|
worktreePath: workingDir,
|
|
12064
12064
|
branch,
|
|
12065
12065
|
repoRoot: repoRoot || workingDir
|
|
12066
12066
|
};
|
|
12067
12067
|
} catch (err) {
|
|
12068
|
-
|
|
12068
|
+
log8.debug(`Failed to detect worktree info for ${workingDir}: ${err}`);
|
|
12069
12069
|
return null;
|
|
12070
12070
|
}
|
|
12071
12071
|
}
|
|
12072
12072
|
async function createWorktree(repoRoot, branch, targetDir) {
|
|
12073
|
-
|
|
12073
|
+
log8.info(`Creating worktree for branch '${branch}' at ${targetDir}`);
|
|
12074
12074
|
const parentDir = path.dirname(targetDir);
|
|
12075
|
-
|
|
12075
|
+
log8.debug(`Creating parent directory: ${parentDir}`);
|
|
12076
12076
|
await fs.mkdir(parentDir, { recursive: true });
|
|
12077
12077
|
const exists = await branchExists(repoRoot, branch);
|
|
12078
12078
|
if (exists) {
|
|
12079
|
-
|
|
12079
|
+
log8.debug(`Branch '${branch}' exists, adding worktree`);
|
|
12080
12080
|
await execGit(["worktree", "add", targetDir, branch], repoRoot);
|
|
12081
12081
|
} else {
|
|
12082
|
-
|
|
12082
|
+
log8.debug(`Branch '${branch}' does not exist, creating with worktree`);
|
|
12083
12083
|
await execGit(["worktree", "add", "-b", branch, targetDir], repoRoot);
|
|
12084
12084
|
}
|
|
12085
|
-
|
|
12085
|
+
log8.info(`Worktree created successfully: ${targetDir}`);
|
|
12086
12086
|
return targetDir;
|
|
12087
12087
|
}
|
|
12088
12088
|
async function removeWorktree(repoRoot, worktreePath) {
|
|
12089
|
-
|
|
12089
|
+
log8.info(`Removing worktree: ${worktreePath}`);
|
|
12090
12090
|
try {
|
|
12091
12091
|
await execGit(["worktree", "remove", worktreePath], repoRoot);
|
|
12092
|
-
|
|
12092
|
+
log8.debug("Worktree removed cleanly");
|
|
12093
12093
|
} catch (err) {
|
|
12094
|
-
|
|
12094
|
+
log8.debug(`Clean remove failed (${err}), trying force remove`);
|
|
12095
12095
|
await execGit(["worktree", "remove", "--force", worktreePath], repoRoot);
|
|
12096
12096
|
}
|
|
12097
|
-
|
|
12097
|
+
log8.debug("Pruning stale worktree references");
|
|
12098
12098
|
await execGit(["worktree", "prune"], repoRoot);
|
|
12099
|
-
|
|
12099
|
+
log8.info("Worktree removed and pruned successfully");
|
|
12100
12100
|
}
|
|
12101
12101
|
async function findWorktreeByBranch(repoRoot, branch) {
|
|
12102
12102
|
const worktrees = await listWorktrees(repoRoot);
|
|
@@ -12137,14 +12137,14 @@ async function writeMetadataStore(store) {
|
|
|
12137
12137
|
await fs.writeFile(METADATA_STORE_PATH, JSON.stringify(store, null, 2), { encoding: "utf-8", mode: 384 });
|
|
12138
12138
|
await fs.chmod(METADATA_STORE_PATH, 384);
|
|
12139
12139
|
} catch (err) {
|
|
12140
|
-
|
|
12140
|
+
log8.warn(`Failed to write worktree metadata store: ${err}`);
|
|
12141
12141
|
}
|
|
12142
12142
|
}
|
|
12143
12143
|
async function writeWorktreeMetadata(worktreePath, metadata) {
|
|
12144
12144
|
const store = await readMetadataStore();
|
|
12145
12145
|
store[worktreePath] = metadata;
|
|
12146
12146
|
await writeMetadataStore(store);
|
|
12147
|
-
|
|
12147
|
+
log8.debug(`Wrote worktree metadata for: ${worktreePath}`);
|
|
12148
12148
|
}
|
|
12149
12149
|
async function readWorktreeMetadata(worktreePath) {
|
|
12150
12150
|
const store = await readMetadataStore();
|
|
@@ -12167,14 +12167,14 @@ async function removeWorktreeMetadata(worktreePath) {
|
|
|
12167
12167
|
if (store[worktreePath]) {
|
|
12168
12168
|
delete store[worktreePath];
|
|
12169
12169
|
await writeMetadataStore(store);
|
|
12170
|
-
|
|
12170
|
+
log8.debug(`Removed worktree metadata for: ${worktreePath}`);
|
|
12171
12171
|
}
|
|
12172
12172
|
}
|
|
12173
|
-
var
|
|
12173
|
+
var log8, WORKTREES_DIR, METADATA_STORE_PATH;
|
|
12174
12174
|
var init_worktree = __esm(() => {
|
|
12175
12175
|
init_spawn();
|
|
12176
12176
|
init_logger();
|
|
12177
|
-
|
|
12177
|
+
log8 = createLogger("git-wt");
|
|
12178
12178
|
WORKTREES_DIR = path.join(homedir4(), ".claude-threads", "worktrees");
|
|
12179
12179
|
METADATA_STORE_PATH = path.join(homedir4(), ".claude-threads", "worktree-metadata.json");
|
|
12180
12180
|
});
|
|
@@ -19514,7 +19514,7 @@ var require_react_reconciler_development = __commonJS((exports, module) => {
|
|
|
19514
19514
|
return hook.checkDCE ? true : false;
|
|
19515
19515
|
}
|
|
19516
19516
|
function setIsStrictModeForDevtools(newIsStrictMode) {
|
|
19517
|
-
typeof
|
|
19517
|
+
typeof log31 === "function" && unstable_setDisableYieldValue2(newIsStrictMode);
|
|
19518
19518
|
if (injectedHook && typeof injectedHook.setStrictMode === "function")
|
|
19519
19519
|
try {
|
|
19520
19520
|
injectedHook.setStrictMode(rendererID, newIsStrictMode);
|
|
@@ -27598,7 +27598,7 @@ Check the render method of %s.`, getComponentNameFromFiber(current) || "Unknown"
|
|
|
27598
27598
|
var fiberStack = [];
|
|
27599
27599
|
var index$jscomp$0 = -1, emptyContextObject = {};
|
|
27600
27600
|
Object.freeze(emptyContextObject);
|
|
27601
|
-
var clz32 = Math.clz32 ? Math.clz32 : clz32Fallback, log$1 = Math.log, LN2 = Math.LN2, nextTransitionUpdateLane = 256, nextTransitionDeferredLane = 262144, nextRetryLane = 4194304, scheduleCallback$3 = Scheduler.unstable_scheduleCallback, cancelCallback$1 = Scheduler.unstable_cancelCallback, shouldYield = Scheduler.unstable_shouldYield, requestPaint = Scheduler.unstable_requestPaint, now$1 = Scheduler.unstable_now, ImmediatePriority = Scheduler.unstable_ImmediatePriority, UserBlockingPriority = Scheduler.unstable_UserBlockingPriority, NormalPriority$1 = Scheduler.unstable_NormalPriority, IdlePriority = Scheduler.unstable_IdlePriority,
|
|
27601
|
+
var clz32 = Math.clz32 ? Math.clz32 : clz32Fallback, log$1 = Math.log, LN2 = Math.LN2, nextTransitionUpdateLane = 256, nextTransitionDeferredLane = 262144, nextRetryLane = 4194304, scheduleCallback$3 = Scheduler.unstable_scheduleCallback, cancelCallback$1 = Scheduler.unstable_cancelCallback, shouldYield = Scheduler.unstable_shouldYield, requestPaint = Scheduler.unstable_requestPaint, now$1 = Scheduler.unstable_now, ImmediatePriority = Scheduler.unstable_ImmediatePriority, UserBlockingPriority = Scheduler.unstable_UserBlockingPriority, NormalPriority$1 = Scheduler.unstable_NormalPriority, IdlePriority = Scheduler.unstable_IdlePriority, log31 = Scheduler.log, unstable_setDisableYieldValue2 = Scheduler.unstable_setDisableYieldValue, rendererID = null, injectedHook = null, hasLoggedError = false, isDevToolsPresent = typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== "undefined", lastResetTime = 0;
|
|
27602
27602
|
if (typeof performance === "object" && typeof performance.now === "function") {
|
|
27603
27603
|
var localPerformance = performance;
|
|
27604
27604
|
var getCurrentTime = function() {
|
|
@@ -53546,6 +53546,99 @@ class SessionStore {
|
|
|
53546
53546
|
chmodSync2(this.sessionsFile, 384);
|
|
53547
53547
|
}
|
|
53548
53548
|
}
|
|
53549
|
+
// src/claude/account-pool.ts
|
|
53550
|
+
init_logger();
|
|
53551
|
+
var log6 = createLogger("account-pool");
|
|
53552
|
+
|
|
53553
|
+
class AccountPool {
|
|
53554
|
+
accounts;
|
|
53555
|
+
byId;
|
|
53556
|
+
activeCounts = new Map;
|
|
53557
|
+
coolingUntil = new Map;
|
|
53558
|
+
roundRobinIndex = 0;
|
|
53559
|
+
constructor(accounts) {
|
|
53560
|
+
this.accounts = (accounts ?? []).filter((acc) => {
|
|
53561
|
+
const hasAuth = !!acc.home || !!acc.apiKey;
|
|
53562
|
+
if (!hasAuth) {
|
|
53563
|
+
log6.warn(`Claude account ${acc.id} has neither home nor apiKey — ignoring`);
|
|
53564
|
+
}
|
|
53565
|
+
return hasAuth;
|
|
53566
|
+
});
|
|
53567
|
+
this.byId = new Map(this.accounts.map((acc) => [acc.id, acc]));
|
|
53568
|
+
for (const acc of this.accounts) {
|
|
53569
|
+
this.activeCounts.set(acc.id, 0);
|
|
53570
|
+
}
|
|
53571
|
+
}
|
|
53572
|
+
get isEmpty() {
|
|
53573
|
+
return this.accounts.length === 0;
|
|
53574
|
+
}
|
|
53575
|
+
get size() {
|
|
53576
|
+
return this.accounts.length;
|
|
53577
|
+
}
|
|
53578
|
+
acquire(preferredId) {
|
|
53579
|
+
if (this.isEmpty)
|
|
53580
|
+
return null;
|
|
53581
|
+
if (preferredId) {
|
|
53582
|
+
const preferred = this.byId.get(preferredId);
|
|
53583
|
+
if (preferred) {
|
|
53584
|
+
this.incrementActive(preferred.id);
|
|
53585
|
+
return preferred;
|
|
53586
|
+
}
|
|
53587
|
+
log6.warn(`Preferred account "${preferredId}" not in pool — falling back to round-robin`);
|
|
53588
|
+
}
|
|
53589
|
+
const now = Date.now();
|
|
53590
|
+
const n = this.accounts.length;
|
|
53591
|
+
for (let i2 = 0;i2 < n; i2++) {
|
|
53592
|
+
const idx = (this.roundRobinIndex + i2) % n;
|
|
53593
|
+
const candidate = this.accounts[idx];
|
|
53594
|
+
const cooling = this.coolingUntil.get(candidate.id) ?? 0;
|
|
53595
|
+
if (cooling <= now) {
|
|
53596
|
+
this.roundRobinIndex = (idx + 1) % n;
|
|
53597
|
+
this.incrementActive(candidate.id);
|
|
53598
|
+
return candidate;
|
|
53599
|
+
}
|
|
53600
|
+
}
|
|
53601
|
+
log6.warn(`All ${n} accounts are in rate-limit cooldown`);
|
|
53602
|
+
return null;
|
|
53603
|
+
}
|
|
53604
|
+
release(accountId) {
|
|
53605
|
+
const current = this.activeCounts.get(accountId);
|
|
53606
|
+
if (current === undefined)
|
|
53607
|
+
return;
|
|
53608
|
+
this.activeCounts.set(accountId, Math.max(0, current - 1));
|
|
53609
|
+
}
|
|
53610
|
+
markCooling(accountId, untilEpochMs) {
|
|
53611
|
+
if (!this.byId.has(accountId)) {
|
|
53612
|
+
log6.warn(`markCooling called for unknown account "${accountId}"`);
|
|
53613
|
+
return;
|
|
53614
|
+
}
|
|
53615
|
+
const existing = this.coolingUntil.get(accountId) ?? 0;
|
|
53616
|
+
if (untilEpochMs > existing) {
|
|
53617
|
+
this.coolingUntil.set(accountId, untilEpochMs);
|
|
53618
|
+
const minutes = Math.ceil((untilEpochMs - Date.now()) / 60000);
|
|
53619
|
+
log6.info(`Account "${accountId}" cooling for ~${minutes}min`);
|
|
53620
|
+
}
|
|
53621
|
+
}
|
|
53622
|
+
get(accountId) {
|
|
53623
|
+
return this.byId.get(accountId);
|
|
53624
|
+
}
|
|
53625
|
+
status() {
|
|
53626
|
+
const now = Date.now();
|
|
53627
|
+
return this.accounts.map((acc) => {
|
|
53628
|
+
const cooling = this.coolingUntil.get(acc.id) ?? 0;
|
|
53629
|
+
return {
|
|
53630
|
+
id: acc.id,
|
|
53631
|
+
displayName: acc.displayName ?? acc.id,
|
|
53632
|
+
activeSessions: this.activeCounts.get(acc.id) ?? 0,
|
|
53633
|
+
coolingUntil: cooling > now ? cooling : null
|
|
53634
|
+
};
|
|
53635
|
+
});
|
|
53636
|
+
}
|
|
53637
|
+
incrementActive(accountId) {
|
|
53638
|
+
this.activeCounts.set(accountId, (this.activeCounts.get(accountId) ?? 0) + 1);
|
|
53639
|
+
}
|
|
53640
|
+
}
|
|
53641
|
+
|
|
53549
53642
|
// src/session/manager.ts
|
|
53550
53643
|
init_emoji();
|
|
53551
53644
|
|
|
@@ -53560,7 +53653,7 @@ init_logger();
|
|
|
53560
53653
|
import { existsSync as existsSync6, mkdirSync as mkdirSync3, appendFileSync, readdirSync, statSync, unlinkSync, rmdirSync, readFileSync as readFileSync5, chmodSync as chmodSync3 } from "fs";
|
|
53561
53654
|
import { homedir as homedir3 } from "os";
|
|
53562
53655
|
import { join as join4, dirname as dirname4 } from "path";
|
|
53563
|
-
var
|
|
53656
|
+
var log7 = createLogger("thread-log");
|
|
53564
53657
|
var LOGS_BASE_DIR = join4(homedir3(), ".claude-threads", "logs");
|
|
53565
53658
|
|
|
53566
53659
|
class ThreadLoggerImpl {
|
|
@@ -53590,7 +53683,7 @@ class ThreadLoggerImpl {
|
|
|
53590
53683
|
this.flushTimer = setInterval(() => {
|
|
53591
53684
|
this.flushSync();
|
|
53592
53685
|
}, this.flushIntervalMs);
|
|
53593
|
-
|
|
53686
|
+
log7.debug(`Thread logger initialized: ${this.logPath}`);
|
|
53594
53687
|
}
|
|
53595
53688
|
}
|
|
53596
53689
|
isEnabled() {
|
|
@@ -53704,7 +53797,7 @@ class ThreadLoggerImpl {
|
|
|
53704
53797
|
this.flushTimer = null;
|
|
53705
53798
|
}
|
|
53706
53799
|
this.flushSync();
|
|
53707
|
-
|
|
53800
|
+
log7.debug(`Thread logger closed: ${this.logPath}`);
|
|
53708
53801
|
}
|
|
53709
53802
|
addEntry(entry) {
|
|
53710
53803
|
this.buffer.push(entry);
|
|
@@ -53726,7 +53819,7 @@ class ThreadLoggerImpl {
|
|
|
53726
53819
|
}
|
|
53727
53820
|
this.buffer = [];
|
|
53728
53821
|
} catch (err) {
|
|
53729
|
-
|
|
53822
|
+
log7.error(`Failed to flush thread log: ${err}`);
|
|
53730
53823
|
}
|
|
53731
53824
|
}
|
|
53732
53825
|
}
|
|
@@ -53777,25 +53870,25 @@ function cleanupOldLogs(retentionDays = 30) {
|
|
|
53777
53870
|
if (fileStat.mtimeMs < cutoffMs) {
|
|
53778
53871
|
unlinkSync(filePath);
|
|
53779
53872
|
deletedCount++;
|
|
53780
|
-
|
|
53873
|
+
log7.debug(`Deleted old log file: ${filePath}`);
|
|
53781
53874
|
}
|
|
53782
53875
|
} catch (err) {
|
|
53783
|
-
|
|
53876
|
+
log7.warn(`Failed to check/delete log file ${filePath}: ${err}`);
|
|
53784
53877
|
}
|
|
53785
53878
|
}
|
|
53786
53879
|
try {
|
|
53787
53880
|
const remaining = readdirSync(platformDir);
|
|
53788
53881
|
if (remaining.length === 0) {
|
|
53789
53882
|
rmdirSync(platformDir);
|
|
53790
|
-
|
|
53883
|
+
log7.debug(`Removed empty platform log directory: ${platformDir}`);
|
|
53791
53884
|
}
|
|
53792
53885
|
} catch {}
|
|
53793
53886
|
}
|
|
53794
53887
|
if (deletedCount > 0) {
|
|
53795
|
-
|
|
53888
|
+
log7.info(`Cleaned up ${deletedCount} old log file(s)`);
|
|
53796
53889
|
}
|
|
53797
53890
|
} catch (err) {
|
|
53798
|
-
|
|
53891
|
+
log7.error(`Failed to clean up old logs: ${err}`);
|
|
53799
53892
|
}
|
|
53800
53893
|
return deletedCount;
|
|
53801
53894
|
}
|
|
@@ -53804,16 +53897,16 @@ function getLogFilePath(platformId, sessionId) {
|
|
|
53804
53897
|
}
|
|
53805
53898
|
function readRecentLogEntries(platformId, sessionId, maxLines = 50) {
|
|
53806
53899
|
const logPath = getLogFilePath(platformId, sessionId);
|
|
53807
|
-
|
|
53900
|
+
log7.debug(`Reading log entries from: ${logPath}`);
|
|
53808
53901
|
if (!existsSync6(logPath)) {
|
|
53809
|
-
|
|
53902
|
+
log7.debug(`Log file does not exist: ${logPath}`);
|
|
53810
53903
|
return [];
|
|
53811
53904
|
}
|
|
53812
53905
|
try {
|
|
53813
53906
|
const content = readFileSync5(logPath, "utf8");
|
|
53814
53907
|
const lines = content.trim().split(`
|
|
53815
53908
|
`);
|
|
53816
|
-
|
|
53909
|
+
log7.debug(`Log file has ${lines.length} lines`);
|
|
53817
53910
|
const recentLines = lines.slice(-maxLines);
|
|
53818
53911
|
const entries = [];
|
|
53819
53912
|
for (const line of recentLines) {
|
|
@@ -53823,17 +53916,17 @@ function readRecentLogEntries(platformId, sessionId, maxLines = 50) {
|
|
|
53823
53916
|
entries.push(JSON.parse(line));
|
|
53824
53917
|
} catch {}
|
|
53825
53918
|
}
|
|
53826
|
-
|
|
53919
|
+
log7.debug(`Parsed ${entries.length} log entries`);
|
|
53827
53920
|
return entries;
|
|
53828
53921
|
} catch (err) {
|
|
53829
|
-
|
|
53922
|
+
log7.error(`Failed to read log file: ${err}`);
|
|
53830
53923
|
return [];
|
|
53831
53924
|
}
|
|
53832
53925
|
}
|
|
53833
53926
|
|
|
53834
53927
|
// src/cleanup/scheduler.ts
|
|
53835
53928
|
init_worktree();
|
|
53836
|
-
var
|
|
53929
|
+
var log9 = createLogger("cleanup");
|
|
53837
53930
|
var DEFAULT_CLEANUP_INTERVAL_MS = 60 * 60 * 1000;
|
|
53838
53931
|
var MAX_WORKTREE_AGE_MS = 24 * 60 * 60 * 1000;
|
|
53839
53932
|
|
|
@@ -53856,17 +53949,17 @@ class CleanupScheduler {
|
|
|
53856
53949
|
}
|
|
53857
53950
|
start() {
|
|
53858
53951
|
if (this.isRunning) {
|
|
53859
|
-
|
|
53952
|
+
log9.debug("Cleanup scheduler already running");
|
|
53860
53953
|
return;
|
|
53861
53954
|
}
|
|
53862
53955
|
this.isRunning = true;
|
|
53863
|
-
|
|
53956
|
+
log9.info(`Cleanup scheduler started (interval: ${Math.round(this.intervalMs / 60000)}min)`);
|
|
53864
53957
|
this.runCleanup().catch((err) => {
|
|
53865
|
-
|
|
53958
|
+
log9.warn(`Initial cleanup failed: ${err}`);
|
|
53866
53959
|
});
|
|
53867
53960
|
this.timer = setInterval(() => {
|
|
53868
53961
|
this.runCleanup().catch((err) => {
|
|
53869
|
-
|
|
53962
|
+
log9.warn(`Periodic cleanup failed: ${err}`);
|
|
53870
53963
|
});
|
|
53871
53964
|
}, this.intervalMs);
|
|
53872
53965
|
}
|
|
@@ -53876,11 +53969,11 @@ class CleanupScheduler {
|
|
|
53876
53969
|
this.timer = null;
|
|
53877
53970
|
}
|
|
53878
53971
|
this.isRunning = false;
|
|
53879
|
-
|
|
53972
|
+
log9.debug("Cleanup scheduler stopped");
|
|
53880
53973
|
}
|
|
53881
53974
|
async runCleanup() {
|
|
53882
53975
|
const startTime = Date.now();
|
|
53883
|
-
|
|
53976
|
+
log9.debug("Running background cleanup...");
|
|
53884
53977
|
const stats = {
|
|
53885
53978
|
logsDeleted: 0,
|
|
53886
53979
|
worktreesCleaned: 0,
|
|
@@ -53906,9 +53999,9 @@ class CleanupScheduler {
|
|
|
53906
53999
|
const elapsed = Date.now() - startTime;
|
|
53907
54000
|
const totalCleaned = stats.logsDeleted + stats.worktreesCleaned + stats.metadataCleaned;
|
|
53908
54001
|
if (totalCleaned > 0 || stats.errors.length > 0) {
|
|
53909
|
-
|
|
54002
|
+
log9.info(`Cleanup completed in ${elapsed}ms: ` + `${stats.logsDeleted} logs, ${stats.worktreesCleaned} worktrees, ${stats.metadataCleaned} metadata` + (stats.errors.length > 0 ? ` (${stats.errors.length} errors)` : ""));
|
|
53910
54003
|
} else {
|
|
53911
|
-
|
|
54004
|
+
log9.debug(`Cleanup completed in ${elapsed}ms (nothing to clean)`);
|
|
53912
54005
|
}
|
|
53913
54006
|
return stats;
|
|
53914
54007
|
}
|
|
@@ -53921,7 +54014,7 @@ class CleanupScheduler {
|
|
|
53921
54014
|
const deleted = cleanupOldLogs(this.logRetentionDays);
|
|
53922
54015
|
resolve3(deleted);
|
|
53923
54016
|
} catch (err) {
|
|
53924
|
-
|
|
54017
|
+
log9.warn(`Log cleanup error: ${err}`);
|
|
53925
54018
|
resolve3(0);
|
|
53926
54019
|
}
|
|
53927
54020
|
});
|
|
@@ -53930,7 +54023,7 @@ class CleanupScheduler {
|
|
|
53930
54023
|
const worktreesDir = getWorktreesDir();
|
|
53931
54024
|
const result = { cleaned: 0, metadata: 0 };
|
|
53932
54025
|
if (!existsSync7(worktreesDir)) {
|
|
53933
|
-
|
|
54026
|
+
log9.debug("No worktrees directory exists, nothing to clean");
|
|
53934
54027
|
return result;
|
|
53935
54028
|
}
|
|
53936
54029
|
const persisted = this.sessionStore.load();
|
|
@@ -53948,7 +54041,7 @@ class CleanupScheduler {
|
|
|
53948
54041
|
continue;
|
|
53949
54042
|
const worktreePath = join6(worktreesDir, entry.name);
|
|
53950
54043
|
if (activeWorktrees.has(worktreePath)) {
|
|
53951
|
-
|
|
54044
|
+
log9.debug(`Worktree in use by persisted session, skipping: ${entry.name}`);
|
|
53952
54045
|
continue;
|
|
53953
54046
|
}
|
|
53954
54047
|
const meta = await readWorktreeMetadata(worktreePath);
|
|
@@ -53958,7 +54051,7 @@ class CleanupScheduler {
|
|
|
53958
54051
|
const lastActivity = new Date(meta.lastActivityAt).getTime();
|
|
53959
54052
|
const age = now - lastActivity;
|
|
53960
54053
|
if (meta.sessionId && age < this.maxWorktreeAgeMs) {
|
|
53961
|
-
|
|
54054
|
+
log9.debug(`Worktree has active session (${Math.round(age / 60000)}min old), skipping: ${entry.name}`);
|
|
53962
54055
|
continue;
|
|
53963
54056
|
}
|
|
53964
54057
|
const merged = age >= this.maxWorktreeAgeMs ? await isBranchMerged(meta.repoRoot, meta.branch).catch(() => false) : false;
|
|
@@ -53969,7 +54062,7 @@ class CleanupScheduler {
|
|
|
53969
54062
|
shouldCleanup = true;
|
|
53970
54063
|
cleanupReason = `inactive for ${Math.round(age / 3600000)}h`;
|
|
53971
54064
|
} else {
|
|
53972
|
-
|
|
54065
|
+
log9.debug(`Worktree recent (${Math.round(age / 60000)}min old), skipping: ${entry.name}`);
|
|
53973
54066
|
continue;
|
|
53974
54067
|
}
|
|
53975
54068
|
} else {
|
|
@@ -53978,7 +54071,7 @@ class CleanupScheduler {
|
|
|
53978
54071
|
}
|
|
53979
54072
|
if (!shouldCleanup)
|
|
53980
54073
|
continue;
|
|
53981
|
-
|
|
54074
|
+
log9.info(`Cleaning worktree (${cleanupReason}): ${entry.name}`);
|
|
53982
54075
|
try {
|
|
53983
54076
|
if (meta?.repoRoot) {
|
|
53984
54077
|
await removeWorktree(meta.repoRoot, worktreePath);
|
|
@@ -53989,19 +54082,19 @@ class CleanupScheduler {
|
|
|
53989
54082
|
await removeWorktreeMetadata(worktreePath);
|
|
53990
54083
|
result.metadata++;
|
|
53991
54084
|
} catch (err) {
|
|
53992
|
-
|
|
54085
|
+
log9.warn(`Failed to clean orphaned worktree ${entry.name}: ${err}`);
|
|
53993
54086
|
try {
|
|
53994
54087
|
await rm(worktreePath, { recursive: true, force: true });
|
|
53995
54088
|
result.cleaned++;
|
|
53996
54089
|
await removeWorktreeMetadata(worktreePath);
|
|
53997
54090
|
result.metadata++;
|
|
53998
54091
|
} catch (rmErr) {
|
|
53999
|
-
|
|
54092
|
+
log9.error(`Failed to force remove worktree ${entry.name}: ${rmErr}`);
|
|
54000
54093
|
}
|
|
54001
54094
|
}
|
|
54002
54095
|
}
|
|
54003
54096
|
} catch (err) {
|
|
54004
|
-
|
|
54097
|
+
log9.warn(`Failed to scan worktrees directory: ${err}`);
|
|
54005
54098
|
}
|
|
54006
54099
|
return result;
|
|
54007
54100
|
}
|
|
@@ -54078,7 +54171,70 @@ import { fileURLToPath as fileURLToPath3 } from "url";
|
|
|
54078
54171
|
import { existsSync as existsSync8, readFileSync as readFileSync6, watchFile, unwatchFile, unlinkSync as unlinkSync2, statSync as statSync2, readdirSync as readdirSync2 } from "fs";
|
|
54079
54172
|
import { tmpdir } from "os";
|
|
54080
54173
|
import { join as join7 } from "path";
|
|
54081
|
-
|
|
54174
|
+
|
|
54175
|
+
// src/claude/rate-limit-detector.ts
|
|
54176
|
+
var RATE_LIMIT_PHRASES = [
|
|
54177
|
+
/usage limit reached/i,
|
|
54178
|
+
/rate[_\s-]?limit[_\s-]?error/i,
|
|
54179
|
+
/you have hit the rate limit/i,
|
|
54180
|
+
/quota (has been )?exceeded/i,
|
|
54181
|
+
/\b429\b.*(rate|limit|quota)/i
|
|
54182
|
+
];
|
|
54183
|
+
var DEFAULT_COOLDOWN_MS = 60 * 60 * 1000;
|
|
54184
|
+
function detectRateLimit(text, now = Date.now()) {
|
|
54185
|
+
if (!text)
|
|
54186
|
+
return { detected: false };
|
|
54187
|
+
let matched;
|
|
54188
|
+
for (const phrase of RATE_LIMIT_PHRASES) {
|
|
54189
|
+
const m = text.match(phrase);
|
|
54190
|
+
if (m) {
|
|
54191
|
+
matched = m[0];
|
|
54192
|
+
break;
|
|
54193
|
+
}
|
|
54194
|
+
}
|
|
54195
|
+
if (!matched)
|
|
54196
|
+
return { detected: false };
|
|
54197
|
+
const resetAtEpochMs = extractResetAt(text, now);
|
|
54198
|
+
return { detected: true, matched, resetAtEpochMs };
|
|
54199
|
+
}
|
|
54200
|
+
function cooldownDeadline(hit, now = Date.now()) {
|
|
54201
|
+
if (!hit.detected)
|
|
54202
|
+
return now;
|
|
54203
|
+
return hit.resetAtEpochMs ?? now + DEFAULT_COOLDOWN_MS;
|
|
54204
|
+
}
|
|
54205
|
+
function extractResetAt(text, now) {
|
|
54206
|
+
const relative = text.match(/(?:retry[_\s-]?after|resets?\s+in)\s+(\d+)\s*(second|minute|hour|day)s?/i);
|
|
54207
|
+
if (relative) {
|
|
54208
|
+
const value = parseInt(relative[1], 10);
|
|
54209
|
+
const unit = relative[2].toLowerCase();
|
|
54210
|
+
const unitMs = {
|
|
54211
|
+
second: 1000,
|
|
54212
|
+
minute: 60000,
|
|
54213
|
+
hour: 3600000,
|
|
54214
|
+
day: 86400000
|
|
54215
|
+
};
|
|
54216
|
+
return now + value * unitMs[unit];
|
|
54217
|
+
}
|
|
54218
|
+
const unix = text.match(/["']?reset(?:_at)?["']?\s*[:=]\s*(\d{10,13})/);
|
|
54219
|
+
if (unix) {
|
|
54220
|
+
const raw = parseInt(unix[1], 10);
|
|
54221
|
+
return unix[1].length === 13 ? raw : raw * 1000;
|
|
54222
|
+
}
|
|
54223
|
+
const clock = text.match(/resets?\s+at\s+(\d{1,2}):(\d{2})\s*(utc|gmt)?/i);
|
|
54224
|
+
if (clock) {
|
|
54225
|
+
const hh = parseInt(clock[1], 10);
|
|
54226
|
+
const mm = parseInt(clock[2], 10);
|
|
54227
|
+
if (hh < 24 && mm < 60) {
|
|
54228
|
+
const reference = new Date(now);
|
|
54229
|
+
const target = new Date(Date.UTC(reference.getUTCFullYear(), reference.getUTCMonth(), reference.getUTCDate(), hh, mm)).getTime();
|
|
54230
|
+
return target > now ? target : target + 86400000;
|
|
54231
|
+
}
|
|
54232
|
+
}
|
|
54233
|
+
return;
|
|
54234
|
+
}
|
|
54235
|
+
|
|
54236
|
+
// src/claude/cli.ts
|
|
54237
|
+
var log10 = createLogger("claude");
|
|
54082
54238
|
function cleanupBrowserBridgeSockets() {
|
|
54083
54239
|
try {
|
|
54084
54240
|
const tempDir = tmpdir();
|
|
@@ -54090,15 +54246,23 @@ function cleanupBrowserBridgeSockets() {
|
|
|
54090
54246
|
const stats = statSync2(filePath);
|
|
54091
54247
|
if (stats.isSocket()) {
|
|
54092
54248
|
unlinkSync2(filePath);
|
|
54093
|
-
|
|
54249
|
+
log10.debug(`Removed stale browser bridge socket: ${file}`);
|
|
54094
54250
|
}
|
|
54095
54251
|
} catch {}
|
|
54096
54252
|
}
|
|
54097
54253
|
}
|
|
54098
54254
|
} catch (err) {
|
|
54099
|
-
|
|
54255
|
+
log10.debug(`Browser bridge cleanup failed: ${err}`);
|
|
54100
54256
|
}
|
|
54101
54257
|
}
|
|
54258
|
+
function isErrorResultEvent(event) {
|
|
54259
|
+
const ev = event;
|
|
54260
|
+
if (typeof ev.subtype === "string" && ev.subtype.startsWith("error"))
|
|
54261
|
+
return true;
|
|
54262
|
+
if (ev.is_error === true)
|
|
54263
|
+
return true;
|
|
54264
|
+
return false;
|
|
54265
|
+
}
|
|
54102
54266
|
|
|
54103
54267
|
class ClaudeCli extends EventEmitter2 {
|
|
54104
54268
|
process = null;
|
|
@@ -54108,6 +54272,7 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
54108
54272
|
statusFilePath = null;
|
|
54109
54273
|
lastStatusData = null;
|
|
54110
54274
|
stderrBuffer = "";
|
|
54275
|
+
rateLimitEmitted = false;
|
|
54111
54276
|
log;
|
|
54112
54277
|
constructor(options2) {
|
|
54113
54278
|
super();
|
|
@@ -54159,6 +54324,7 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
54159
54324
|
if (this.process)
|
|
54160
54325
|
throw new Error("Already running");
|
|
54161
54326
|
this.stderrBuffer = "";
|
|
54327
|
+
this.rateLimitEmitted = false;
|
|
54162
54328
|
cleanupBrowserBridgeSockets();
|
|
54163
54329
|
const claudePath = getClaudePath();
|
|
54164
54330
|
const args = [
|
|
@@ -54228,9 +54394,13 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
54228
54394
|
args.push("--settings", JSON.stringify(statusLineSettings));
|
|
54229
54395
|
}
|
|
54230
54396
|
this.log.debug(`Starting: ${claudePath} ${args.slice(0, 5).join(" ")}...`);
|
|
54397
|
+
const childEnv = this.buildChildEnv();
|
|
54398
|
+
if (this.options.account) {
|
|
54399
|
+
this.log.debug(`Spawning under Claude account "${this.options.account.id}"`);
|
|
54400
|
+
}
|
|
54231
54401
|
this.process = crossSpawn(claudePath, args, {
|
|
54232
54402
|
cwd: this.options.workingDir,
|
|
54233
|
-
env:
|
|
54403
|
+
env: childEnv,
|
|
54234
54404
|
stdio: ["pipe", "pipe", "pipe"]
|
|
54235
54405
|
});
|
|
54236
54406
|
this.log.debug(`Claude process spawned: pid=${this.process.pid}`);
|
|
@@ -54244,6 +54414,7 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
54244
54414
|
this.stderrBuffer = this.stderrBuffer.slice(-10240);
|
|
54245
54415
|
}
|
|
54246
54416
|
this.log.debug(`stderr: ${text.trim()}`);
|
|
54417
|
+
this.maybeEmitRateLimit(text);
|
|
54247
54418
|
});
|
|
54248
54419
|
this.process.on("error", (err) => {
|
|
54249
54420
|
this.log.error(`Claude error: ${err}`);
|
|
@@ -54298,9 +54469,22 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
54298
54469
|
try {
|
|
54299
54470
|
const event = JSON.parse(trimmed);
|
|
54300
54471
|
this.emit("event", event);
|
|
54472
|
+
if (event.type === "result" && isErrorResultEvent(event)) {
|
|
54473
|
+
this.maybeEmitRateLimit(trimmed);
|
|
54474
|
+
}
|
|
54301
54475
|
} catch {}
|
|
54302
54476
|
}
|
|
54303
54477
|
}
|
|
54478
|
+
maybeEmitRateLimit(text) {
|
|
54479
|
+
const hit = detectRateLimit(text);
|
|
54480
|
+
if (!hit.detected)
|
|
54481
|
+
return;
|
|
54482
|
+
if (this.rateLimitEmitted)
|
|
54483
|
+
return;
|
|
54484
|
+
this.rateLimitEmitted = true;
|
|
54485
|
+
this.log.warn(`Rate limit detected: ${hit.matched ?? "(no match text)"}`);
|
|
54486
|
+
this.emit("rate-limit", hit);
|
|
54487
|
+
}
|
|
54304
54488
|
isRunning() {
|
|
54305
54489
|
return this.process !== null;
|
|
54306
54490
|
}
|
|
@@ -54369,6 +54553,22 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
54369
54553
|
this.process.kill("SIGINT");
|
|
54370
54554
|
return true;
|
|
54371
54555
|
}
|
|
54556
|
+
buildChildEnv() {
|
|
54557
|
+
const account = this.options.account;
|
|
54558
|
+
if (!account)
|
|
54559
|
+
return process.env;
|
|
54560
|
+
const env = { ...process.env };
|
|
54561
|
+
if (account.home) {
|
|
54562
|
+
env.HOME = account.home;
|
|
54563
|
+
env.USERPROFILE = account.home;
|
|
54564
|
+
delete env.ANTHROPIC_API_KEY;
|
|
54565
|
+
delete env.CLAUDE_CODE_OAUTH_TOKEN;
|
|
54566
|
+
} else if (account.apiKey) {
|
|
54567
|
+
env.ANTHROPIC_API_KEY = account.apiKey;
|
|
54568
|
+
delete env.CLAUDE_CODE_OAUTH_TOKEN;
|
|
54569
|
+
}
|
|
54570
|
+
return env;
|
|
54571
|
+
}
|
|
54372
54572
|
getMcpServerPath() {
|
|
54373
54573
|
const __filename2 = fileURLToPath3(import.meta.url);
|
|
54374
54574
|
const __dirname4 = dirname6(__filename2);
|
|
@@ -55205,7 +55405,7 @@ import { existsSync as existsSync11 } from "fs";
|
|
|
55205
55405
|
// src/utils/keep-alive.ts
|
|
55206
55406
|
init_logger();
|
|
55207
55407
|
import { spawn as spawn2 } from "child_process";
|
|
55208
|
-
var
|
|
55408
|
+
var log11 = createLogger("keepalive");
|
|
55209
55409
|
|
|
55210
55410
|
class KeepAliveManager {
|
|
55211
55411
|
activeSessionCount = 0;
|
|
@@ -55220,7 +55420,7 @@ class KeepAliveManager {
|
|
|
55220
55420
|
if (!enabled && this.keepAliveProcess) {
|
|
55221
55421
|
this.stopKeepAlive();
|
|
55222
55422
|
}
|
|
55223
|
-
|
|
55423
|
+
log11.debug(`Keep-alive ${enabled ? "enabled" : "disabled"}`);
|
|
55224
55424
|
}
|
|
55225
55425
|
isEnabled() {
|
|
55226
55426
|
return this.enabled;
|
|
@@ -55230,7 +55430,7 @@ class KeepAliveManager {
|
|
|
55230
55430
|
}
|
|
55231
55431
|
sessionStarted() {
|
|
55232
55432
|
this.activeSessionCount++;
|
|
55233
|
-
|
|
55433
|
+
log11.debug(`Session started (${this.activeSessionCount} active)`);
|
|
55234
55434
|
if (this.activeSessionCount === 1) {
|
|
55235
55435
|
this.startKeepAlive();
|
|
55236
55436
|
}
|
|
@@ -55239,7 +55439,7 @@ class KeepAliveManager {
|
|
|
55239
55439
|
if (this.activeSessionCount > 0) {
|
|
55240
55440
|
this.activeSessionCount--;
|
|
55241
55441
|
}
|
|
55242
|
-
|
|
55442
|
+
log11.debug(`Session ended (${this.activeSessionCount} active)`);
|
|
55243
55443
|
if (this.activeSessionCount === 0) {
|
|
55244
55444
|
this.stopKeepAlive();
|
|
55245
55445
|
}
|
|
@@ -55253,11 +55453,11 @@ class KeepAliveManager {
|
|
|
55253
55453
|
}
|
|
55254
55454
|
startKeepAlive() {
|
|
55255
55455
|
if (!this.enabled) {
|
|
55256
|
-
|
|
55456
|
+
log11.debug("Keep-alive disabled, skipping");
|
|
55257
55457
|
return;
|
|
55258
55458
|
}
|
|
55259
55459
|
if (this.keepAliveProcess) {
|
|
55260
|
-
|
|
55460
|
+
log11.debug("Keep-alive already running");
|
|
55261
55461
|
return;
|
|
55262
55462
|
}
|
|
55263
55463
|
switch (this.platform) {
|
|
@@ -55271,12 +55471,12 @@ class KeepAliveManager {
|
|
|
55271
55471
|
this.startWindowsKeepAlive();
|
|
55272
55472
|
break;
|
|
55273
55473
|
default:
|
|
55274
|
-
|
|
55474
|
+
log11.warn(`Keep-alive not supported on ${this.platform}`);
|
|
55275
55475
|
}
|
|
55276
55476
|
}
|
|
55277
55477
|
stopKeepAlive() {
|
|
55278
55478
|
if (this.keepAliveProcess) {
|
|
55279
|
-
|
|
55479
|
+
log11.debug("Stopping keep-alive");
|
|
55280
55480
|
this.keepAliveProcess.kill();
|
|
55281
55481
|
this.keepAliveProcess = null;
|
|
55282
55482
|
}
|
|
@@ -55288,18 +55488,18 @@ class KeepAliveManager {
|
|
|
55288
55488
|
detached: false
|
|
55289
55489
|
});
|
|
55290
55490
|
this.keepAliveProcess.on("error", (err) => {
|
|
55291
|
-
|
|
55491
|
+
log11.error(`Failed to start caffeinate: ${err.message}`);
|
|
55292
55492
|
this.keepAliveProcess = null;
|
|
55293
55493
|
});
|
|
55294
55494
|
this.keepAliveProcess.on("exit", (code) => {
|
|
55295
55495
|
if (code !== null && code !== 0 && this.activeSessionCount > 0) {
|
|
55296
|
-
|
|
55496
|
+
log11.debug(`caffeinate exited with code ${code}`);
|
|
55297
55497
|
}
|
|
55298
55498
|
this.keepAliveProcess = null;
|
|
55299
55499
|
});
|
|
55300
|
-
|
|
55500
|
+
log11.info("Sleep prevention active (caffeinate)");
|
|
55301
55501
|
} catch (err) {
|
|
55302
|
-
|
|
55502
|
+
log11.error(`Failed to start caffeinate: ${err}`);
|
|
55303
55503
|
}
|
|
55304
55504
|
}
|
|
55305
55505
|
startLinuxKeepAlive() {
|
|
@@ -55315,19 +55515,19 @@ class KeepAliveManager {
|
|
|
55315
55515
|
detached: false
|
|
55316
55516
|
});
|
|
55317
55517
|
this.keepAliveProcess.on("error", (err) => {
|
|
55318
|
-
|
|
55518
|
+
log11.debug(`systemd-inhibit not available: ${err.message}`);
|
|
55319
55519
|
this.keepAliveProcess = null;
|
|
55320
55520
|
this.startLinuxKeepAliveFallback();
|
|
55321
55521
|
});
|
|
55322
55522
|
this.keepAliveProcess.on("exit", (code) => {
|
|
55323
55523
|
if (code !== null && code !== 0 && this.activeSessionCount > 0) {
|
|
55324
|
-
|
|
55524
|
+
log11.debug(`systemd-inhibit exited with code ${code}`);
|
|
55325
55525
|
}
|
|
55326
55526
|
this.keepAliveProcess = null;
|
|
55327
55527
|
});
|
|
55328
|
-
|
|
55528
|
+
log11.info("Sleep prevention active (systemd-inhibit)");
|
|
55329
55529
|
} catch (err) {
|
|
55330
|
-
|
|
55530
|
+
log11.debug(`Failed to start systemd-inhibit: ${err}`);
|
|
55331
55531
|
this.startLinuxKeepAliveFallback();
|
|
55332
55532
|
}
|
|
55333
55533
|
}
|
|
@@ -55341,15 +55541,15 @@ class KeepAliveManager {
|
|
|
55341
55541
|
detached: false
|
|
55342
55542
|
});
|
|
55343
55543
|
this.keepAliveProcess.on("error", (err) => {
|
|
55344
|
-
|
|
55544
|
+
log11.warn(`Linux keep-alive fallback not available: ${err.message}`);
|
|
55345
55545
|
this.keepAliveProcess = null;
|
|
55346
55546
|
});
|
|
55347
55547
|
this.keepAliveProcess.on("exit", () => {
|
|
55348
55548
|
this.keepAliveProcess = null;
|
|
55349
55549
|
});
|
|
55350
|
-
|
|
55550
|
+
log11.info("Sleep prevention active (xdg-screensaver)");
|
|
55351
55551
|
} catch (err) {
|
|
55352
|
-
|
|
55552
|
+
log11.warn(`Linux keep-alive not available: ${err}`);
|
|
55353
55553
|
}
|
|
55354
55554
|
}
|
|
55355
55555
|
startWindowsKeepAlive() {
|
|
@@ -55374,18 +55574,18 @@ class KeepAliveManager {
|
|
|
55374
55574
|
windowsHide: true
|
|
55375
55575
|
});
|
|
55376
55576
|
this.keepAliveProcess.on("error", (err) => {
|
|
55377
|
-
|
|
55577
|
+
log11.warn(`Windows keep-alive not available: ${err.message}`);
|
|
55378
55578
|
this.keepAliveProcess = null;
|
|
55379
55579
|
});
|
|
55380
55580
|
this.keepAliveProcess.on("exit", (code) => {
|
|
55381
55581
|
if (code !== null && code !== 0 && this.activeSessionCount > 0) {
|
|
55382
|
-
|
|
55582
|
+
log11.debug(`PowerShell keep-alive exited with code ${code}`);
|
|
55383
55583
|
}
|
|
55384
55584
|
this.keepAliveProcess = null;
|
|
55385
55585
|
});
|
|
55386
|
-
|
|
55586
|
+
log11.info("Sleep prevention active (SetThreadExecutionState)");
|
|
55387
55587
|
} catch (err) {
|
|
55388
|
-
|
|
55588
|
+
log11.warn(`Windows keep-alive not available: ${err}`);
|
|
55389
55589
|
}
|
|
55390
55590
|
}
|
|
55391
55591
|
}
|
|
@@ -55393,7 +55593,7 @@ var keepAlive = new KeepAliveManager;
|
|
|
55393
55593
|
|
|
55394
55594
|
// src/utils/error-handler/index.ts
|
|
55395
55595
|
init_logger();
|
|
55396
|
-
var
|
|
55596
|
+
var log12 = createLogger("error");
|
|
55397
55597
|
|
|
55398
55598
|
class SessionError extends Error {
|
|
55399
55599
|
sessionId;
|
|
@@ -55419,19 +55619,19 @@ async function handleError(error, context, severity = "recoverable") {
|
|
|
55419
55619
|
const sessionPart = sessionId ? ` (${formatShortId(sessionId)})` : "";
|
|
55420
55620
|
const logMessage = `${context.action}${sessionPart}: ${message}`;
|
|
55421
55621
|
if (severity === "recoverable") {
|
|
55422
|
-
|
|
55622
|
+
log12.warn(logMessage);
|
|
55423
55623
|
} else {
|
|
55424
|
-
|
|
55624
|
+
log12.error(logMessage, error instanceof Error ? error : undefined);
|
|
55425
55625
|
}
|
|
55426
55626
|
if (context.details) {
|
|
55427
|
-
|
|
55627
|
+
log12.debugJson("Error details", context.details);
|
|
55428
55628
|
}
|
|
55429
55629
|
if (context.notifyUser && context.session) {
|
|
55430
55630
|
try {
|
|
55431
55631
|
const fmt = context.session.platform.getFormatter();
|
|
55432
55632
|
await context.session.platform.createPost(`⚠️ ${fmt.formatBold("Error")}: ${context.action} failed - ${message}`, context.session.threadId);
|
|
55433
55633
|
} catch (notifyError) {
|
|
55434
|
-
|
|
55634
|
+
log12.warn(`Could not notify user: ${notifyError}`);
|
|
55435
55635
|
}
|
|
55436
55636
|
}
|
|
55437
55637
|
if (severity === "session-fatal" || severity === "system-fatal") {
|
|
@@ -55458,7 +55658,7 @@ async function logAndNotify(error, context) {
|
|
|
55458
55658
|
}
|
|
55459
55659
|
function logSilentError(context, error) {
|
|
55460
55660
|
const message = error instanceof Error ? error.message : String(error);
|
|
55461
|
-
|
|
55661
|
+
log12.debug(`[${context}] Silently caught: ${message}`);
|
|
55462
55662
|
}
|
|
55463
55663
|
|
|
55464
55664
|
// src/session/lifecycle.ts
|
|
@@ -55478,8 +55678,8 @@ function createSessionLog(baseLog) {
|
|
|
55478
55678
|
init_logger();
|
|
55479
55679
|
init_emoji();
|
|
55480
55680
|
init_worktree();
|
|
55481
|
-
var
|
|
55482
|
-
var sessionLog = createSessionLog(
|
|
55681
|
+
var log13 = createLogger("helpers");
|
|
55682
|
+
var sessionLog = createSessionLog(log13);
|
|
55483
55683
|
var POST_TYPES = {
|
|
55484
55684
|
info: "",
|
|
55485
55685
|
success: "✅",
|
|
@@ -55563,7 +55763,7 @@ function updateLastMessage(session, post2) {
|
|
|
55563
55763
|
// src/claude/quick-query.ts
|
|
55564
55764
|
init_spawn();
|
|
55565
55765
|
init_logger();
|
|
55566
|
-
var
|
|
55766
|
+
var log14 = createLogger("query");
|
|
55567
55767
|
async function quickQuery(options2) {
|
|
55568
55768
|
const {
|
|
55569
55769
|
prompt,
|
|
@@ -55579,7 +55779,7 @@ async function quickQuery(options2) {
|
|
|
55579
55779
|
args.push("--system-prompt", systemPrompt);
|
|
55580
55780
|
}
|
|
55581
55781
|
args.push(prompt);
|
|
55582
|
-
|
|
55782
|
+
log14.debug(`Quick query: model=${model}, timeout=${timeout}ms, prompt="${prompt.substring(0, 50)}..."`);
|
|
55583
55783
|
return new Promise((resolve5) => {
|
|
55584
55784
|
let stdout = "";
|
|
55585
55785
|
let stderr = "";
|
|
@@ -55593,7 +55793,7 @@ async function quickQuery(options2) {
|
|
|
55593
55793
|
if (!resolved) {
|
|
55594
55794
|
resolved = true;
|
|
55595
55795
|
proc.kill("SIGTERM");
|
|
55596
|
-
|
|
55796
|
+
log14.debug(`Quick query timed out after ${timeout}ms`);
|
|
55597
55797
|
resolve5({
|
|
55598
55798
|
success: false,
|
|
55599
55799
|
error: "timeout",
|
|
@@ -55611,7 +55811,7 @@ async function quickQuery(options2) {
|
|
|
55611
55811
|
if (!resolved) {
|
|
55612
55812
|
resolved = true;
|
|
55613
55813
|
clearTimeout(timeoutId);
|
|
55614
|
-
|
|
55814
|
+
log14.debug(`Quick query error: ${err.message}`);
|
|
55615
55815
|
resolve5({
|
|
55616
55816
|
success: false,
|
|
55617
55817
|
error: err.message,
|
|
@@ -55625,14 +55825,14 @@ async function quickQuery(options2) {
|
|
|
55625
55825
|
clearTimeout(timeoutId);
|
|
55626
55826
|
const durationMs = Date.now() - startTime;
|
|
55627
55827
|
if (code === 0 && stdout.trim()) {
|
|
55628
|
-
|
|
55828
|
+
log14.debug(`Quick query success: ${durationMs}ms, ${stdout.length} chars`);
|
|
55629
55829
|
resolve5({
|
|
55630
55830
|
success: true,
|
|
55631
55831
|
response: stdout.trim(),
|
|
55632
55832
|
durationMs
|
|
55633
55833
|
});
|
|
55634
55834
|
} else {
|
|
55635
|
-
|
|
55835
|
+
log14.debug(`Quick query failed: code=${code}, stderr=${stderr.substring(0, 100)}`);
|
|
55636
55836
|
resolve5({
|
|
55637
55837
|
success: false,
|
|
55638
55838
|
error: stderr || `exit code ${code}`,
|
|
@@ -55647,7 +55847,7 @@ async function quickQuery(options2) {
|
|
|
55647
55847
|
|
|
55648
55848
|
// src/operations/suggestions/title.ts
|
|
55649
55849
|
init_logger();
|
|
55650
|
-
var
|
|
55850
|
+
var log15 = createLogger("title");
|
|
55651
55851
|
var SUGGESTION_TIMEOUT = 15000;
|
|
55652
55852
|
var MIN_TITLE_LENGTH = 3;
|
|
55653
55853
|
var MAX_TITLE_LENGTH = 50;
|
|
@@ -55711,32 +55911,32 @@ function parseMetadata(response) {
|
|
|
55711
55911
|
const titleMatch = response.match(/TITLE:\s*(.+)/i);
|
|
55712
55912
|
const descMatch = response.match(/DESC:\s*(.+)/i);
|
|
55713
55913
|
if (!titleMatch || !descMatch) {
|
|
55714
|
-
|
|
55914
|
+
log15.debug("Failed to parse title/description from response");
|
|
55715
55915
|
return null;
|
|
55716
55916
|
}
|
|
55717
55917
|
let title = titleMatch[1].trim();
|
|
55718
55918
|
let description = descMatch[1].trim();
|
|
55719
55919
|
if (title.length < MIN_TITLE_LENGTH) {
|
|
55720
|
-
|
|
55920
|
+
log15.debug(`Title too short: ${title.length} chars`);
|
|
55721
55921
|
return null;
|
|
55722
55922
|
}
|
|
55723
55923
|
if (title.length > MAX_TITLE_LENGTH) {
|
|
55724
|
-
|
|
55924
|
+
log15.debug(`Title too long (${title.length} chars), truncating`);
|
|
55725
55925
|
title = truncateAtWord(title, MAX_TITLE_LENGTH);
|
|
55726
55926
|
}
|
|
55727
55927
|
if (description.length < MIN_DESC_LENGTH) {
|
|
55728
|
-
|
|
55928
|
+
log15.debug(`Description too short: ${description.length} chars`);
|
|
55729
55929
|
return null;
|
|
55730
55930
|
}
|
|
55731
55931
|
if (description.length > MAX_DESC_LENGTH) {
|
|
55732
|
-
|
|
55932
|
+
log15.debug(`Description too long (${description.length} chars), truncating`);
|
|
55733
55933
|
description = truncateAtWord(description, MAX_DESC_LENGTH);
|
|
55734
55934
|
}
|
|
55735
55935
|
return { title, description };
|
|
55736
55936
|
}
|
|
55737
55937
|
async function suggestSessionMetadata(context) {
|
|
55738
55938
|
const logContext = typeof context === "string" ? context.substring(0, 50) : context.originalTask.substring(0, 50);
|
|
55739
|
-
|
|
55939
|
+
log15.debug(`Suggesting title for: "${logContext}..."`);
|
|
55740
55940
|
try {
|
|
55741
55941
|
const result = await quickQuery({
|
|
55742
55942
|
prompt: buildTitlePrompt(context),
|
|
@@ -55744,23 +55944,23 @@ async function suggestSessionMetadata(context) {
|
|
|
55744
55944
|
timeout: SUGGESTION_TIMEOUT
|
|
55745
55945
|
});
|
|
55746
55946
|
if (!result.success || !result.response) {
|
|
55747
|
-
|
|
55947
|
+
log15.debug(`Title suggestion failed: ${result.error || "no response"}`);
|
|
55748
55948
|
return null;
|
|
55749
55949
|
}
|
|
55750
55950
|
const metadata = parseMetadata(result.response);
|
|
55751
55951
|
if (metadata) {
|
|
55752
|
-
|
|
55952
|
+
log15.debug(`Got title: "${metadata.title}" (${result.durationMs}ms)`);
|
|
55753
55953
|
}
|
|
55754
55954
|
return metadata;
|
|
55755
55955
|
} catch (err) {
|
|
55756
|
-
|
|
55956
|
+
log15.debug(`Title suggestion error: ${err}`);
|
|
55757
55957
|
return null;
|
|
55758
55958
|
}
|
|
55759
55959
|
}
|
|
55760
55960
|
|
|
55761
55961
|
// src/operations/suggestions/tag.ts
|
|
55762
55962
|
init_logger();
|
|
55763
|
-
var
|
|
55963
|
+
var log16 = createLogger("tags");
|
|
55764
55964
|
var SUGGESTION_TIMEOUT2 = 15000;
|
|
55765
55965
|
var MAX_TAGS = 3;
|
|
55766
55966
|
var VALID_TAGS = [
|
|
@@ -55792,7 +55992,7 @@ function parseTags(response) {
|
|
|
55792
55992
|
return [...new Set(tags)].slice(0, MAX_TAGS);
|
|
55793
55993
|
}
|
|
55794
55994
|
async function suggestSessionTags(userMessage) {
|
|
55795
|
-
|
|
55995
|
+
log16.debug(`Suggesting tags for: "${userMessage.substring(0, 50)}..."`);
|
|
55796
55996
|
try {
|
|
55797
55997
|
const result = await quickQuery({
|
|
55798
55998
|
prompt: buildTagPrompt(userMessage),
|
|
@@ -55800,14 +56000,14 @@ async function suggestSessionTags(userMessage) {
|
|
|
55800
56000
|
timeout: SUGGESTION_TIMEOUT2
|
|
55801
56001
|
});
|
|
55802
56002
|
if (!result.success || !result.response) {
|
|
55803
|
-
|
|
56003
|
+
log16.debug(`Tag suggestion failed: ${result.error || "no response"}`);
|
|
55804
56004
|
return [];
|
|
55805
56005
|
}
|
|
55806
56006
|
const tags = parseTags(result.response);
|
|
55807
|
-
|
|
56007
|
+
log16.debug(`Got tags: ${tags.join(", ")} (${result.durationMs}ms)`);
|
|
55808
56008
|
return tags;
|
|
55809
56009
|
} catch (err) {
|
|
55810
|
-
|
|
56010
|
+
log16.debug(`Tag suggestion error: ${err}`);
|
|
55811
56011
|
return [];
|
|
55812
56012
|
}
|
|
55813
56013
|
}
|
|
@@ -59349,7 +59549,7 @@ class BugReportExecutor extends BaseExecutor {
|
|
|
59349
59549
|
// src/operations/executors/worktree-prompt.ts
|
|
59350
59550
|
init_emoji();
|
|
59351
59551
|
init_logger();
|
|
59352
|
-
var
|
|
59552
|
+
var log17 = createLogger("wt-prompt");
|
|
59353
59553
|
// src/operations/message-manager.ts
|
|
59354
59554
|
init_logger();
|
|
59355
59555
|
|
|
@@ -59386,7 +59586,7 @@ var import_yauzl = __toESM(require_yauzl(), 1);
|
|
|
59386
59586
|
import { createGunzip } from "zlib";
|
|
59387
59587
|
import { pipeline } from "stream/promises";
|
|
59388
59588
|
import { Readable, Writable } from "stream";
|
|
59389
|
-
var
|
|
59589
|
+
var log18 = createLogger("streaming");
|
|
59390
59590
|
var MAX_PDF_SIZE = 32 * 1024 * 1024;
|
|
59391
59591
|
var MAX_TEXT_FILE_SIZE = 1 * 1024 * 1024;
|
|
59392
59592
|
var MAX_DECOMPRESSED_SIZE = 10 * 1024 * 1024;
|
|
@@ -59512,7 +59712,7 @@ async function processImageFile(file, platform, debug = false) {
|
|
|
59512
59712
|
const buffer = await platform.downloadFile(file.id);
|
|
59513
59713
|
const base64 = buffer.toString("base64");
|
|
59514
59714
|
if (debug) {
|
|
59515
|
-
|
|
59715
|
+
log18.debug(`Attached image: ${file.name} (${file.mimeType}, ${Math.round(buffer.length / 1024)}KB)`);
|
|
59516
59716
|
}
|
|
59517
59717
|
return {
|
|
59518
59718
|
block: {
|
|
@@ -59525,7 +59725,7 @@ async function processImageFile(file, platform, debug = false) {
|
|
|
59525
59725
|
}
|
|
59526
59726
|
};
|
|
59527
59727
|
} catch (err) {
|
|
59528
|
-
|
|
59728
|
+
log18.error(`Failed to download image ${file.name}: ${err}`);
|
|
59529
59729
|
return {
|
|
59530
59730
|
skipped: {
|
|
59531
59731
|
name: file.name,
|
|
@@ -59556,7 +59756,7 @@ async function processPdfFile(file, platform, debug = false) {
|
|
|
59556
59756
|
}
|
|
59557
59757
|
const base64 = buffer.toString("base64");
|
|
59558
59758
|
if (debug) {
|
|
59559
|
-
|
|
59759
|
+
log18.debug(`Attached PDF: ${file.name} (${Math.round(buffer.length / 1024)}KB)`);
|
|
59560
59760
|
}
|
|
59561
59761
|
return {
|
|
59562
59762
|
block: {
|
|
@@ -59570,7 +59770,7 @@ async function processPdfFile(file, platform, debug = false) {
|
|
|
59570
59770
|
}
|
|
59571
59771
|
};
|
|
59572
59772
|
} catch (err) {
|
|
59573
|
-
|
|
59773
|
+
log18.error(`Failed to process PDF ${file.name}: ${err}`);
|
|
59574
59774
|
return {
|
|
59575
59775
|
skipped: {
|
|
59576
59776
|
name: file.name,
|
|
@@ -59601,7 +59801,7 @@ async function processTextFile(file, platform, debug = false) {
|
|
|
59601
59801
|
}
|
|
59602
59802
|
const content = buffer.toString("utf-8");
|
|
59603
59803
|
if (debug) {
|
|
59604
|
-
|
|
59804
|
+
log18.debug(`Attached text file: ${file.name} (${Math.round(buffer.length / 1024)}KB)`);
|
|
59605
59805
|
}
|
|
59606
59806
|
const wrappedContent = formatTextFileContent(file.name, content);
|
|
59607
59807
|
return {
|
|
@@ -59611,7 +59811,7 @@ async function processTextFile(file, platform, debug = false) {
|
|
|
59611
59811
|
}
|
|
59612
59812
|
};
|
|
59613
59813
|
} catch (err) {
|
|
59614
|
-
|
|
59814
|
+
log18.error(`Failed to process text file ${file.name}: ${err}`);
|
|
59615
59815
|
return {
|
|
59616
59816
|
skipped: {
|
|
59617
59817
|
name: file.name,
|
|
@@ -59669,7 +59869,7 @@ async function processGzipFile(file, platform, debug = false) {
|
|
|
59669
59869
|
compressedBuffer = await platform.downloadFile(file.id);
|
|
59670
59870
|
} catch (err) {
|
|
59671
59871
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
59672
|
-
|
|
59872
|
+
log18.error(`Failed to download gzip file ${file.name}: ${errorMessage}`);
|
|
59673
59873
|
return {
|
|
59674
59874
|
skipped: {
|
|
59675
59875
|
name: file.name,
|
|
@@ -59679,7 +59879,7 @@ async function processGzipFile(file, platform, debug = false) {
|
|
|
59679
59879
|
};
|
|
59680
59880
|
}
|
|
59681
59881
|
if (file.size && compressedBuffer.length !== file.size) {
|
|
59682
|
-
|
|
59882
|
+
log18.warn(`Downloaded size mismatch for ${file.name}: expected ${file.size}, got ${compressedBuffer.length}`);
|
|
59683
59883
|
}
|
|
59684
59884
|
let decompressedBuffer;
|
|
59685
59885
|
try {
|
|
@@ -59715,7 +59915,7 @@ async function processGzipFile(file, platform, debug = false) {
|
|
|
59715
59915
|
const innerFilename = file.name.toLowerCase().endsWith(".gz") ? file.name.slice(0, -3) : file.name;
|
|
59716
59916
|
const contentType = detectDecompressedContentType(decompressedBuffer, innerFilename);
|
|
59717
59917
|
if (debug) {
|
|
59718
|
-
|
|
59918
|
+
log18.debug(`Decompressed ${file.name}: ${Math.round(decompressedBuffer.length / 1024)}KB, detected type: ${contentType}`);
|
|
59719
59919
|
}
|
|
59720
59920
|
if (contentType === "pdf") {
|
|
59721
59921
|
const base64 = decompressedBuffer.toString("base64");
|
|
@@ -59750,7 +59950,7 @@ async function processGzipFile(file, platform, debug = false) {
|
|
|
59750
59950
|
}
|
|
59751
59951
|
} catch (err) {
|
|
59752
59952
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
59753
|
-
|
|
59953
|
+
log18.error(`Failed to process gzip file ${file.name}: ${errorMessage}`);
|
|
59754
59954
|
return {
|
|
59755
59955
|
skipped: {
|
|
59756
59956
|
name: file.name,
|
|
@@ -59802,7 +60002,7 @@ async function processZipFile(file, platform, debug = false) {
|
|
|
59802
60002
|
}
|
|
59803
60003
|
const zipBuffer = await platform.downloadFile(file.id);
|
|
59804
60004
|
if (debug) {
|
|
59805
|
-
|
|
60005
|
+
log18.debug(`Processing zip file ${file.name}: ${Math.round(zipBuffer.length / 1024)}KB`);
|
|
59806
60006
|
}
|
|
59807
60007
|
const zipfile = await new Promise((resolve5, reject) => {
|
|
59808
60008
|
import_yauzl.default.fromBuffer(zipBuffer, { lazyEntries: true }, (err, zf) => {
|
|
@@ -59876,7 +60076,7 @@ async function processZipFile(file, platform, debug = false) {
|
|
|
59876
60076
|
const buffer = await extractZipEntry(zipfile2, entry);
|
|
59877
60077
|
const contentType = detectDecompressedContentType(buffer, entry.fileName);
|
|
59878
60078
|
if (debug) {
|
|
59879
|
-
|
|
60079
|
+
log18.debug(`Extracted ${entry.fileName}: ${Math.round(buffer.length / 1024)}KB, type: ${contentType}`);
|
|
59880
60080
|
}
|
|
59881
60081
|
if (contentType === "pdf") {
|
|
59882
60082
|
const base64 = buffer.toString("base64");
|
|
@@ -59919,11 +60119,11 @@ async function processZipFile(file, platform, debug = false) {
|
|
|
59919
60119
|
});
|
|
59920
60120
|
zipfile2.close();
|
|
59921
60121
|
if (debug) {
|
|
59922
|
-
|
|
60122
|
+
log18.debug(`Zip ${file.name}: processed ${processedCount} files, skipped ${skipped.length}`);
|
|
59923
60123
|
}
|
|
59924
60124
|
return { blocks, skipped };
|
|
59925
60125
|
} catch (err) {
|
|
59926
|
-
|
|
60126
|
+
log18.error(`Failed to process zip file ${file.name}: ${err}`);
|
|
59927
60127
|
return {
|
|
59928
60128
|
blocks: [],
|
|
59929
60129
|
skipped: [{
|
|
@@ -60020,7 +60220,7 @@ async function processFiles(platform, files, debug = false) {
|
|
|
60020
60220
|
blocks.push(...zipResult.blocks);
|
|
60021
60221
|
for (const s of zipResult.skipped) {
|
|
60022
60222
|
skipped.push(s);
|
|
60023
|
-
|
|
60223
|
+
log18.warn(`Skipped file ${s.name}: ${s.reason}`);
|
|
60024
60224
|
}
|
|
60025
60225
|
continue;
|
|
60026
60226
|
}
|
|
@@ -60054,7 +60254,7 @@ async function processFiles(platform, files, debug = false) {
|
|
|
60054
60254
|
}
|
|
60055
60255
|
if (result.skipped) {
|
|
60056
60256
|
skipped.push(result.skipped);
|
|
60057
|
-
|
|
60257
|
+
log18.warn(`Skipped file ${result.skipped.name}: ${result.skipped.reason}`);
|
|
60058
60258
|
}
|
|
60059
60259
|
}
|
|
60060
60260
|
return { blocks, skipped };
|
|
@@ -60087,7 +60287,7 @@ function stopTyping(session) {
|
|
|
60087
60287
|
}
|
|
60088
60288
|
|
|
60089
60289
|
// src/operations/message-manager.ts
|
|
60090
|
-
var
|
|
60290
|
+
var log19 = createLogger("msg-mgr");
|
|
60091
60291
|
|
|
60092
60292
|
class MessageManager {
|
|
60093
60293
|
platform;
|
|
@@ -60175,7 +60375,7 @@ class MessageManager {
|
|
|
60175
60375
|
});
|
|
60176
60376
|
}
|
|
60177
60377
|
async handleEvent(event) {
|
|
60178
|
-
const logger =
|
|
60378
|
+
const logger = log19.forSession(this.sessionId);
|
|
60179
60379
|
const transformCtx = {
|
|
60180
60380
|
sessionId: this.sessionId,
|
|
60181
60381
|
formatter: this.platform.getFormatter(),
|
|
@@ -60225,7 +60425,7 @@ class MessageManager {
|
|
|
60225
60425
|
}
|
|
60226
60426
|
}
|
|
60227
60427
|
async executeOperation(op) {
|
|
60228
|
-
const logger =
|
|
60428
|
+
const logger = log19.forSession(this.sessionId);
|
|
60229
60429
|
const ctx = this.getExecutorContext();
|
|
60230
60430
|
try {
|
|
60231
60431
|
if (isContentOp(op)) {
|
|
@@ -60293,7 +60493,7 @@ class MessageManager {
|
|
|
60293
60493
|
threadId: this.threadId,
|
|
60294
60494
|
platform: this.platform,
|
|
60295
60495
|
formatter: this.platform.getFormatter(),
|
|
60296
|
-
logger:
|
|
60496
|
+
logger: log19.forSession(this.sessionId),
|
|
60297
60497
|
postTracker: this.postTracker,
|
|
60298
60498
|
contentBreaker: this.contentBreaker,
|
|
60299
60499
|
threadLogger: this.session.threadLogger,
|
|
@@ -60504,13 +60704,13 @@ class MessageManager {
|
|
|
60504
60704
|
return this.systemExecutor.postSuccess(message, this.getExecutorContext());
|
|
60505
60705
|
}
|
|
60506
60706
|
async prepareForUserMessage() {
|
|
60507
|
-
const logger =
|
|
60707
|
+
const logger = log19.forSession(this.sessionId);
|
|
60508
60708
|
logger.debug("Preparing for new user message");
|
|
60509
60709
|
await this.closeCurrentPost();
|
|
60510
60710
|
await this.bumpTaskList();
|
|
60511
60711
|
}
|
|
60512
60712
|
async handleUserMessage(message, files, username, displayName) {
|
|
60513
|
-
const logger =
|
|
60713
|
+
const logger = log19.forSession(this.sessionId);
|
|
60514
60714
|
if (!this.session.claude.isRunning()) {
|
|
60515
60715
|
logger.debug("Claude not running, ignoring user message");
|
|
60516
60716
|
return false;
|
|
@@ -60537,7 +60737,7 @@ class MessageManager {
|
|
|
60537
60737
|
return this.session;
|
|
60538
60738
|
}
|
|
60539
60739
|
async handleReaction(postId, emoji, user, action) {
|
|
60540
|
-
const logger =
|
|
60740
|
+
const logger = log19.forSession(this.sessionId);
|
|
60541
60741
|
const ctx = this.getExecutorContext();
|
|
60542
60742
|
logger.debug(`Routing reaction: postId=${postId}, emoji=${emoji}, user=${user}, action=${action}`);
|
|
60543
60743
|
if (await this.questionApprovalExecutor.handleReaction(postId, emoji, user, action, ctx)) {
|
|
@@ -60733,7 +60933,7 @@ function formatPullRequestLink(url, formatter) {
|
|
|
60733
60933
|
}
|
|
60734
60934
|
|
|
60735
60935
|
// src/operations/sticky-message/handler.ts
|
|
60736
|
-
var
|
|
60936
|
+
var log20 = createLogger("sticky");
|
|
60737
60937
|
var botStartedAt = new Date;
|
|
60738
60938
|
function getPendingPrompts(session) {
|
|
60739
60939
|
const prompts2 = [];
|
|
@@ -60808,21 +61008,21 @@ function initialize(store) {
|
|
|
60808
61008
|
stickyPostIds.set(platformId, postId);
|
|
60809
61009
|
}
|
|
60810
61010
|
if (persistedIds.size > 0) {
|
|
60811
|
-
|
|
61011
|
+
log20.info(`\uD83D\uDCCC Restored ${persistedIds.size} sticky post ID(s) from persistence`);
|
|
60812
61012
|
}
|
|
60813
61013
|
}
|
|
60814
61014
|
function setPlatformPaused(platformId, paused) {
|
|
60815
61015
|
if (paused) {
|
|
60816
61016
|
pausedPlatforms.set(platformId, true);
|
|
60817
|
-
|
|
61017
|
+
log20.debug(`Platform ${platformId} marked as paused`);
|
|
60818
61018
|
} else {
|
|
60819
61019
|
pausedPlatforms.delete(platformId);
|
|
60820
|
-
|
|
61020
|
+
log20.debug(`Platform ${platformId} marked as active`);
|
|
60821
61021
|
}
|
|
60822
61022
|
}
|
|
60823
61023
|
function setShuttingDown(shuttingDown) {
|
|
60824
61024
|
isShuttingDown = shuttingDown;
|
|
60825
|
-
|
|
61025
|
+
log20.debug(`Bot shutdown state: ${shuttingDown}`);
|
|
60826
61026
|
}
|
|
60827
61027
|
function getTaskContent(session) {
|
|
60828
61028
|
const taskState = session.messageManager?.getTaskListState();
|
|
@@ -60919,6 +61119,13 @@ async function buildStatusBar(sessionCount, config, formatter, platformId) {
|
|
|
60919
61119
|
}
|
|
60920
61120
|
items.push(formatter.formatCode(formatVersionString()));
|
|
60921
61121
|
items.push(formatter.formatCode(`${sessionCount}/${config.maxSessions} sessions`));
|
|
61122
|
+
if (config.accountPoolStatus && config.accountPoolStatus.length > 0) {
|
|
61123
|
+
const total = config.accountPoolStatus.length;
|
|
61124
|
+
const cooling = config.accountPoolStatus.filter((a) => a.coolingUntil !== null).length;
|
|
61125
|
+
const available = total - cooling;
|
|
61126
|
+
const label = cooling > 0 ? `\uD83D\uDD11 ${available}/${total} accounts (${cooling} cooling)` : `\uD83D\uDD11 ${total} account${total === 1 ? "" : "s"}`;
|
|
61127
|
+
items.push(formatter.formatCode(label));
|
|
61128
|
+
}
|
|
60922
61129
|
const permMode = config.skipPermissions ? "⚡ Auto" : "\uD83D\uDD10 Interactive";
|
|
60923
61130
|
items.push(formatter.formatCode(permMode));
|
|
60924
61131
|
if (config.worktreeMode === "require") {
|
|
@@ -61127,12 +61334,12 @@ async function validateLastMessageIds(platform, sessions) {
|
|
|
61127
61334
|
try {
|
|
61128
61335
|
const post2 = await platform.getPost(lastMessageId);
|
|
61129
61336
|
if (!post2) {
|
|
61130
|
-
|
|
61337
|
+
log20.debug(`lastMessageId ${lastMessageId.substring(0, 8)} for session ${session.sessionId} was deleted, clearing`);
|
|
61131
61338
|
session.lastMessageId = undefined;
|
|
61132
61339
|
session.lastMessageTs = undefined;
|
|
61133
61340
|
}
|
|
61134
61341
|
} catch (err) {
|
|
61135
|
-
|
|
61342
|
+
log20.debug(`Failed to validate lastMessageId for session ${session.sessionId}, clearing: ${err}`);
|
|
61136
61343
|
session.lastMessageId = undefined;
|
|
61137
61344
|
session.lastMessageTs = undefined;
|
|
61138
61345
|
}
|
|
@@ -61141,63 +61348,63 @@ async function validateLastMessageIds(platform, sessions) {
|
|
|
61141
61348
|
}
|
|
61142
61349
|
async function updateStickyMessageImpl(platform, sessions, config) {
|
|
61143
61350
|
const platformSessions = [...sessions.values()].filter((s) => s.platformId === platform.platformId);
|
|
61144
|
-
|
|
61351
|
+
log20.debug(`updateStickyMessage for ${platform.platformId}, ${platformSessions.length} sessions`);
|
|
61145
61352
|
for (const s of platformSessions) {
|
|
61146
|
-
|
|
61353
|
+
log20.debug(` - ${s.sessionId}: title="${s.sessionTitle}" firstPrompt="${s.firstPrompt?.substring(0, 30)}..."`);
|
|
61147
61354
|
}
|
|
61148
61355
|
await validateLastMessageIds(platform, platformSessions);
|
|
61149
61356
|
const formatter = platform.getFormatter();
|
|
61150
61357
|
const content = await buildStickyMessage(sessions, platform.platformId, config, formatter, (threadId) => platform.getThreadLink(threadId));
|
|
61151
61358
|
const existingPostId = stickyPostIds.get(platform.platformId);
|
|
61152
61359
|
const shouldBump = needsBump.get(platform.platformId) ?? false;
|
|
61153
|
-
|
|
61360
|
+
log20.debug(`existingPostId: ${existingPostId || "(none)"}, needsBump: ${shouldBump}`);
|
|
61154
61361
|
try {
|
|
61155
61362
|
if (existingPostId && !shouldBump) {
|
|
61156
|
-
|
|
61363
|
+
log20.debug(`Updating existing post in place...`);
|
|
61157
61364
|
try {
|
|
61158
61365
|
await platform.updatePost(existingPostId, content);
|
|
61159
61366
|
try {
|
|
61160
61367
|
await platform.pinPost(existingPostId);
|
|
61161
|
-
|
|
61368
|
+
log20.debug(`Re-pinned post`);
|
|
61162
61369
|
} catch (pinErr) {
|
|
61163
|
-
|
|
61370
|
+
log20.debug(`Re-pin failed (might already be pinned): ${pinErr}`);
|
|
61164
61371
|
}
|
|
61165
|
-
|
|
61372
|
+
log20.debug(`Updated successfully`);
|
|
61166
61373
|
return;
|
|
61167
61374
|
} catch (err) {
|
|
61168
|
-
|
|
61375
|
+
log20.debug(`Update failed, will create new: ${err}`);
|
|
61169
61376
|
}
|
|
61170
61377
|
}
|
|
61171
61378
|
needsBump.set(platform.platformId, false);
|
|
61172
61379
|
if (existingPostId) {
|
|
61173
|
-
|
|
61380
|
+
log20.debug(`Unpinning and deleting existing post ${existingPostId.substring(0, 8)}...`);
|
|
61174
61381
|
try {
|
|
61175
61382
|
await platform.unpinPost(existingPostId);
|
|
61176
|
-
|
|
61383
|
+
log20.debug(`Unpinned successfully`);
|
|
61177
61384
|
} catch (err) {
|
|
61178
|
-
|
|
61385
|
+
log20.debug(`Unpin failed (probably already unpinned): ${err}`);
|
|
61179
61386
|
}
|
|
61180
61387
|
try {
|
|
61181
61388
|
await platform.deletePost(existingPostId);
|
|
61182
|
-
|
|
61389
|
+
log20.debug(`Deleted successfully`);
|
|
61183
61390
|
} catch (err) {
|
|
61184
|
-
|
|
61391
|
+
log20.debug(`Delete failed (probably already deleted): ${err}`);
|
|
61185
61392
|
}
|
|
61186
61393
|
stickyPostIds.delete(platform.platformId);
|
|
61187
61394
|
}
|
|
61188
|
-
|
|
61395
|
+
log20.debug(`Creating new post...`);
|
|
61189
61396
|
const post2 = await platform.createPost(content);
|
|
61190
61397
|
stickyPostIds.set(platform.platformId, post2.id);
|
|
61191
61398
|
try {
|
|
61192
61399
|
await platform.pinPost(post2.id);
|
|
61193
|
-
|
|
61400
|
+
log20.debug(`Pinned post successfully`);
|
|
61194
61401
|
} catch (err) {
|
|
61195
|
-
|
|
61402
|
+
log20.debug(`Failed to pin post: ${err}`);
|
|
61196
61403
|
}
|
|
61197
61404
|
if (sessionStore) {
|
|
61198
61405
|
sessionStore.saveStickyPostId(platform.platformId, post2.id);
|
|
61199
61406
|
}
|
|
61200
|
-
|
|
61407
|
+
log20.info(`\uD83D\uDCCC Created sticky message for ${platform.platformId}: ${formatShortId(post2.id)}`);
|
|
61201
61408
|
const excludePostIds = new Set;
|
|
61202
61409
|
if (sessionStore) {
|
|
61203
61410
|
for (const session of sessionStore.load().values()) {
|
|
@@ -61213,10 +61420,10 @@ async function updateStickyMessageImpl(platform, sessions, config) {
|
|
|
61213
61420
|
}
|
|
61214
61421
|
const botUser = await platform.getBotUser();
|
|
61215
61422
|
cleanupOldStickyMessages(platform, botUser.id, false, excludePostIds).catch((err) => {
|
|
61216
|
-
|
|
61423
|
+
log20.debug(`Background cleanup failed: ${err}`);
|
|
61217
61424
|
});
|
|
61218
61425
|
} catch (err) {
|
|
61219
|
-
|
|
61426
|
+
log20.error(`Failed to update sticky message for ${platform.platformId}`, err instanceof Error ? err : undefined);
|
|
61220
61427
|
}
|
|
61221
61428
|
}
|
|
61222
61429
|
async function updateAllStickyMessages(platforms, sessions, config) {
|
|
@@ -61241,7 +61448,7 @@ async function cleanupOldStickyMessages(platform, botUserId, forceRun = false, e
|
|
|
61241
61448
|
if (!forceRun) {
|
|
61242
61449
|
const lastRun = lastCleanupTime.get(platformId) || 0;
|
|
61243
61450
|
if (now - lastRun < CLEANUP_THROTTLE_MS) {
|
|
61244
|
-
|
|
61451
|
+
log20.debug(`Cleanup throttled for ${platformId} (last run ${Math.round((now - lastRun) / 1000)}s ago)`);
|
|
61245
61452
|
return;
|
|
61246
61453
|
}
|
|
61247
61454
|
}
|
|
@@ -61251,31 +61458,31 @@ async function cleanupOldStickyMessages(platform, botUserId, forceRun = false, e
|
|
|
61251
61458
|
const pinnedPostIds = await platform.getPinnedPosts();
|
|
61252
61459
|
const recentPinnedIds = pinnedPostIds.filter((id) => id !== currentStickyId && !excludePostIds?.has(id) && isRecentPost(id));
|
|
61253
61460
|
if (recentPinnedIds.length === 0) {
|
|
61254
|
-
|
|
61461
|
+
log20.debug(`No recent pinned posts to check (${pinnedPostIds.length} total, current: ${currentStickyId?.substring(0, 8) || "(none)"})`);
|
|
61255
61462
|
return;
|
|
61256
61463
|
}
|
|
61257
|
-
|
|
61464
|
+
log20.debug(`Checking ${recentPinnedIds.length} recent pinned posts (of ${pinnedPostIds.length} total)`);
|
|
61258
61465
|
for (const postId of recentPinnedIds) {
|
|
61259
61466
|
try {
|
|
61260
61467
|
const post2 = await platform.getPost(postId);
|
|
61261
61468
|
if (!post2)
|
|
61262
61469
|
continue;
|
|
61263
61470
|
if (post2.userId === botUserId) {
|
|
61264
|
-
|
|
61471
|
+
log20.debug(`Cleaning up old sticky: ${postId.substring(0, 8)}...`);
|
|
61265
61472
|
try {
|
|
61266
61473
|
await platform.unpinPost(postId);
|
|
61267
61474
|
await platform.deletePost(postId);
|
|
61268
|
-
|
|
61475
|
+
log20.info(`\uD83E\uDDF9 Cleaned up old sticky message: ${postId.substring(0, 8)}...`);
|
|
61269
61476
|
} catch (err) {
|
|
61270
|
-
|
|
61477
|
+
log20.debug(`Failed to cleanup ${postId}: ${err}`);
|
|
61271
61478
|
}
|
|
61272
61479
|
}
|
|
61273
61480
|
} catch (err) {
|
|
61274
|
-
|
|
61481
|
+
log20.debug(`Could not check post ${postId}: ${err}`);
|
|
61275
61482
|
}
|
|
61276
61483
|
}
|
|
61277
61484
|
} catch (err) {
|
|
61278
|
-
|
|
61485
|
+
log20.error(`Failed to cleanup old sticky messages`, err instanceof Error ? err : undefined);
|
|
61279
61486
|
}
|
|
61280
61487
|
}
|
|
61281
61488
|
// src/operations/bug-report/handler.ts
|
|
@@ -65338,8 +65545,16 @@ function getUpdateInfo() {
|
|
|
65338
65545
|
init_emoji();
|
|
65339
65546
|
init_logger();
|
|
65340
65547
|
init_worktree();
|
|
65341
|
-
var
|
|
65342
|
-
var sessionLog2 = createSessionLog(
|
|
65548
|
+
var log21 = createLogger("commands");
|
|
65549
|
+
var sessionLog2 = createSessionLog(log21);
|
|
65550
|
+
function sessionAccountOption(session, ctx) {
|
|
65551
|
+
if (!session.claudeAccountId)
|
|
65552
|
+
return;
|
|
65553
|
+
const account = ctx.ops.getClaudeAccount(session.claudeAccountId);
|
|
65554
|
+
if (!account)
|
|
65555
|
+
return;
|
|
65556
|
+
return { id: account.id, home: account.home, apiKey: account.apiKey };
|
|
65557
|
+
}
|
|
65343
65558
|
async function restartClaudeSession(session, cliOptions, ctx, actionName) {
|
|
65344
65559
|
ctx.ops.stopTyping(session);
|
|
65345
65560
|
transitionTo(session, "restarting");
|
|
@@ -65348,6 +65563,7 @@ async function restartClaudeSession(session, cliOptions, ctx, actionName) {
|
|
|
65348
65563
|
session.claude = new ClaudeCli(cliOptions);
|
|
65349
65564
|
session.claude.on("event", (e) => ctx.ops.handleEvent(session.sessionId, e));
|
|
65350
65565
|
session.claude.on("exit", (code) => ctx.ops.handleExit(session.sessionId, code));
|
|
65566
|
+
session.claude.on("rate-limit", (hit) => handleRateLimit(session, hit, ctx));
|
|
65351
65567
|
try {
|
|
65352
65568
|
session.claude.start();
|
|
65353
65569
|
return true;
|
|
@@ -65500,7 +65716,8 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
65500
65716
|
platformConfig: session.platform.getMcpConfig(),
|
|
65501
65717
|
appendSystemPrompt,
|
|
65502
65718
|
logSessionId: session.sessionId,
|
|
65503
|
-
permissionTimeoutMs: ctx.config.permissionTimeoutMs
|
|
65719
|
+
permissionTimeoutMs: ctx.config.permissionTimeoutMs,
|
|
65720
|
+
account: sessionAccountOption(session, ctx)
|
|
65504
65721
|
};
|
|
65505
65722
|
const success = await restartClaudeSession(session, cliOptions, ctx, "Restart Claude for directory change");
|
|
65506
65723
|
if (!success)
|
|
@@ -65595,7 +65812,8 @@ async function enableInteractivePermissions(session, username, ctx) {
|
|
|
65595
65812
|
chrome: ctx.config.chromeEnabled,
|
|
65596
65813
|
platformConfig: session.platform.getMcpConfig(),
|
|
65597
65814
|
logSessionId: session.sessionId,
|
|
65598
|
-
permissionTimeoutMs: ctx.config.permissionTimeoutMs
|
|
65815
|
+
permissionTimeoutMs: ctx.config.permissionTimeoutMs,
|
|
65816
|
+
account: sessionAccountOption(session, ctx)
|
|
65599
65817
|
};
|
|
65600
65818
|
const success = await restartClaudeSession(session, cliOptions, ctx, "Enable interactive permissions");
|
|
65601
65819
|
if (!success)
|
|
@@ -65701,6 +65919,11 @@ async function updateSessionHeader(session, ctx) {
|
|
|
65701
65919
|
if (otherParticipants) {
|
|
65702
65920
|
items.push(["\uD83D\uDC65", "Participants", otherParticipants]);
|
|
65703
65921
|
}
|
|
65922
|
+
if (session.claudeAccountId) {
|
|
65923
|
+
const account = ctx.ops.getClaudeAccount(session.claudeAccountId);
|
|
65924
|
+
const label = account?.displayName ?? session.claudeAccountId;
|
|
65925
|
+
items.push(["\uD83D\uDD11", "Claude account", formatter.formatCode(label)]);
|
|
65926
|
+
}
|
|
65704
65927
|
items.push(["\uD83C\uDD94", "Session ID", formatter.formatCode(session.claudeSessionId.substring(0, 8))]);
|
|
65705
65928
|
const logPath = getLogFilePath(session.platform.platformId, session.claudeSessionId);
|
|
65706
65929
|
const shortLogPath = logPath.replace(process.env.HOME || "", "~");
|
|
@@ -65857,7 +66080,7 @@ init_logger();
|
|
|
65857
66080
|
import { exec as exec3 } from "child_process";
|
|
65858
66081
|
import { promisify as promisify3 } from "util";
|
|
65859
66082
|
var execAsync2 = promisify3(exec3);
|
|
65860
|
-
var
|
|
66083
|
+
var log22 = createLogger("branch");
|
|
65861
66084
|
var SUGGESTION_TIMEOUT3 = 15000;
|
|
65862
66085
|
var MAX_SUGGESTIONS = 3;
|
|
65863
66086
|
async function getCurrentBranch3(workingDir) {
|
|
@@ -65906,7 +66129,7 @@ function parseBranchSuggestions(response) {
|
|
|
65906
66129
|
return lines.slice(0, MAX_SUGGESTIONS);
|
|
65907
66130
|
}
|
|
65908
66131
|
async function suggestBranchNames(workingDir, userMessage) {
|
|
65909
|
-
|
|
66132
|
+
log22.debug(`Suggesting branch names for: "${userMessage.substring(0, 50)}..."`);
|
|
65910
66133
|
try {
|
|
65911
66134
|
const [currentBranch, recentCommits] = await Promise.all([
|
|
65912
66135
|
getCurrentBranch3(workingDir),
|
|
@@ -65920,14 +66143,14 @@ async function suggestBranchNames(workingDir, userMessage) {
|
|
|
65920
66143
|
workingDir
|
|
65921
66144
|
});
|
|
65922
66145
|
if (!result.success || !result.response) {
|
|
65923
|
-
|
|
66146
|
+
log22.debug(`Branch suggestion failed: ${result.error || "no response"}`);
|
|
65924
66147
|
return [];
|
|
65925
66148
|
}
|
|
65926
66149
|
const suggestions = parseBranchSuggestions(result.response);
|
|
65927
|
-
|
|
66150
|
+
log22.debug(`Got ${suggestions.length} branch suggestions: ${suggestions.join(", ")}`);
|
|
65928
66151
|
return suggestions;
|
|
65929
66152
|
} catch (err) {
|
|
65930
|
-
|
|
66153
|
+
log22.debug(`Branch suggestion error: ${err}`);
|
|
65931
66154
|
return [];
|
|
65932
66155
|
}
|
|
65933
66156
|
}
|
|
@@ -65936,8 +66159,8 @@ async function suggestBranchNames(workingDir, userMessage) {
|
|
|
65936
66159
|
init_worktree();
|
|
65937
66160
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
65938
66161
|
init_logger();
|
|
65939
|
-
var
|
|
65940
|
-
var sessionLog3 = createSessionLog(
|
|
66162
|
+
var log23 = createLogger("worktree");
|
|
66163
|
+
var sessionLog3 = createSessionLog(log23);
|
|
65941
66164
|
function parseWorktreeError(error) {
|
|
65942
66165
|
const message = error instanceof Error ? error.message : String(error);
|
|
65943
66166
|
const lowerMessage = message.toLowerCase();
|
|
@@ -66488,8 +66711,8 @@ async function cleanupWorktreeCommand(session, username, hasOtherSessionsUsingWo
|
|
|
66488
66711
|
}
|
|
66489
66712
|
// src/operations/events/handler.ts
|
|
66490
66713
|
init_logger();
|
|
66491
|
-
var
|
|
66492
|
-
var sessionLog4 = createSessionLog(
|
|
66714
|
+
var log24 = createLogger("events");
|
|
66715
|
+
var sessionLog4 = createSessionLog(log24);
|
|
66493
66716
|
function detectAndExecuteClaudeCommands(text, session, ctx) {
|
|
66494
66717
|
const parsed = parseClaudeCommand(text);
|
|
66495
66718
|
if (parsed && isClaudeAllowedCommand(parsed.command)) {
|
|
@@ -66737,8 +66960,8 @@ function createSessionContext(config, state, ops) {
|
|
|
66737
66960
|
// src/operations/context-prompt/handler.ts
|
|
66738
66961
|
init_emoji();
|
|
66739
66962
|
init_logger();
|
|
66740
|
-
var
|
|
66741
|
-
var sessionLog5 = createSessionLog(
|
|
66963
|
+
var log25 = createLogger("context");
|
|
66964
|
+
var sessionLog5 = createSessionLog(log25);
|
|
66742
66965
|
var CONTEXT_PROMPT_TIMEOUT_MS = 30000;
|
|
66743
66966
|
var CONTEXT_OPTIONS = [3, 5, 10];
|
|
66744
66967
|
var contextPromptTimeouts = new Map;
|
|
@@ -66988,8 +67211,8 @@ function formatRelativeTime(date) {
|
|
|
66988
67211
|
}
|
|
66989
67212
|
// src/session/lifecycle.ts
|
|
66990
67213
|
init_worktree();
|
|
66991
|
-
var
|
|
66992
|
-
var sessionLog6 = createSessionLog(
|
|
67214
|
+
var log26 = createLogger("lifecycle");
|
|
67215
|
+
var sessionLog6 = createSessionLog(log26);
|
|
66993
67216
|
function mutableSessions(ctx) {
|
|
66994
67217
|
return ctx.state.sessions;
|
|
66995
67218
|
}
|
|
@@ -67037,12 +67260,31 @@ async function cleanupSession(session, ctx, options2 = {}) {
|
|
|
67037
67260
|
cleanupPostIndex(ctx, session.threadId);
|
|
67038
67261
|
}
|
|
67039
67262
|
keepAlive.sessionEnded();
|
|
67263
|
+
releaseAccountIfHeld(session, ctx);
|
|
67264
|
+
}
|
|
67265
|
+
function releaseAccountIfHeld(session, ctx) {
|
|
67266
|
+
if (session.claudeAccountId) {
|
|
67267
|
+
ctx.ops.releaseClaudeAccount(session.claudeAccountId);
|
|
67268
|
+
session.claudeAccountId = undefined;
|
|
67269
|
+
}
|
|
67040
67270
|
}
|
|
67041
67271
|
function removeFromRegistry(session, ctx) {
|
|
67042
67272
|
ctx.ops.emitSessionRemove(session.sessionId);
|
|
67043
67273
|
mutableSessions(ctx).delete(session.sessionId);
|
|
67044
67274
|
cleanupPostIndex(ctx, session.threadId);
|
|
67045
67275
|
keepAlive.sessionEnded();
|
|
67276
|
+
releaseAccountIfHeld(session, ctx);
|
|
67277
|
+
}
|
|
67278
|
+
function handleRateLimit(session, hit, ctx) {
|
|
67279
|
+
if (!session.claudeAccountId) {
|
|
67280
|
+
sessionLog6(session).warn(`Rate limit hit in single-account mode — cannot reroute`);
|
|
67281
|
+
return;
|
|
67282
|
+
}
|
|
67283
|
+
const deadline = cooldownDeadline(hit);
|
|
67284
|
+
ctx.ops.markClaudeAccountCooling(session.claudeAccountId, deadline);
|
|
67285
|
+
const minutes = Math.max(1, Math.ceil((deadline - Date.now()) / 60000));
|
|
67286
|
+
sessionLog6(session).warn(`Rate limit on account "${session.claudeAccountId}" — cooling for ~${minutes}min`);
|
|
67287
|
+
post(session, "warning", `⚠️ Claude account \`${session.claudeAccountId}\` hit a rate limit. ` + `New sessions will use another account until it resets (~${minutes}min).`);
|
|
67046
67288
|
}
|
|
67047
67289
|
function findPersistedByThreadId(persisted, threadId) {
|
|
67048
67290
|
for (const session of persisted.values()) {
|
|
@@ -67342,18 +67584,22 @@ async function startSession(options2, username, displayName, replyToPostId, plat
|
|
|
67342
67584
|
return;
|
|
67343
67585
|
}
|
|
67344
67586
|
workingDir = resolvedDir;
|
|
67345
|
-
|
|
67587
|
+
log26.info(`Starting session in directory: ${workingDir} (from !cd command)`);
|
|
67346
67588
|
}
|
|
67347
67589
|
if (initialOptions?.forceInteractivePermissions) {
|
|
67348
67590
|
forceInteractivePermissions = true;
|
|
67349
67591
|
skipPermissions = false;
|
|
67350
|
-
|
|
67592
|
+
log26.info(`Starting session with interactive permissions (from !permissions command)`);
|
|
67351
67593
|
}
|
|
67352
67594
|
const sessionContext = buildSessionContext(platform, workingDir);
|
|
67353
67595
|
const systemPrompt = `${sessionContext}
|
|
67354
67596
|
|
|
67355
67597
|
${CHAT_PLATFORM_PROMPT}`;
|
|
67356
67598
|
const platformMcpConfig = platform.getMcpConfig();
|
|
67599
|
+
const claudeAccount = ctx.ops.acquireClaudeAccount();
|
|
67600
|
+
if (claudeAccount) {
|
|
67601
|
+
log26.info(`Session ${sessionId.substring(0, 20)} reserved Claude account "${claudeAccount.id}"`);
|
|
67602
|
+
}
|
|
67357
67603
|
const cliOptions = {
|
|
67358
67604
|
workingDir,
|
|
67359
67605
|
threadId: actualThreadId,
|
|
@@ -67364,7 +67610,8 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
67364
67610
|
platformConfig: platformMcpConfig,
|
|
67365
67611
|
appendSystemPrompt: systemPrompt,
|
|
67366
67612
|
logSessionId: sessionId,
|
|
67367
|
-
permissionTimeoutMs: ctx.config.permissionTimeoutMs
|
|
67613
|
+
permissionTimeoutMs: ctx.config.permissionTimeoutMs,
|
|
67614
|
+
account: claudeAccount ? { id: claudeAccount.id, home: claudeAccount.home, apiKey: claudeAccount.apiKey } : undefined
|
|
67368
67615
|
};
|
|
67369
67616
|
const claude = new ClaudeCli(cliOptions);
|
|
67370
67617
|
const session = {
|
|
@@ -67373,6 +67620,7 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
67373
67620
|
sessionId,
|
|
67374
67621
|
platform,
|
|
67375
67622
|
claudeSessionId,
|
|
67623
|
+
claudeAccountId: claudeAccount?.id,
|
|
67376
67624
|
startedBy: username,
|
|
67377
67625
|
startedByDisplayName: displayName,
|
|
67378
67626
|
startedAt: new Date,
|
|
@@ -67411,6 +67659,7 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
67411
67659
|
ctx.ops.startTyping(session);
|
|
67412
67660
|
claude.on("event", (e) => ctx.ops.handleEvent(sessionId, e));
|
|
67413
67661
|
claude.on("exit", (code) => ctx.ops.handleExit(sessionId, code));
|
|
67662
|
+
claude.on("rate-limit", (hit) => handleRateLimit(session, hit, ctx));
|
|
67414
67663
|
try {
|
|
67415
67664
|
claude.start();
|
|
67416
67665
|
} catch (err) {
|
|
@@ -67418,6 +67667,7 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
67418
67667
|
ctx.ops.stopTyping(session);
|
|
67419
67668
|
ctx.ops.emitSessionRemove(session.sessionId);
|
|
67420
67669
|
mutableSessions(ctx).delete(session.sessionId);
|
|
67670
|
+
releaseAccountIfHeld(session, ctx);
|
|
67421
67671
|
await ctx.ops.updateStickyMessage();
|
|
67422
67672
|
return;
|
|
67423
67673
|
}
|
|
@@ -67453,28 +67703,28 @@ async function resumeSession(state, ctx) {
|
|
|
67453
67703
|
!state.claudeSessionId && "claudeSessionId",
|
|
67454
67704
|
!state.workingDir && "workingDir"
|
|
67455
67705
|
].filter(Boolean).join(", ");
|
|
67456
|
-
|
|
67706
|
+
log26.warn(`Skipping session with missing required fields: ${missing}`);
|
|
67457
67707
|
return;
|
|
67458
67708
|
}
|
|
67459
67709
|
const shortId = state.threadId.substring(0, 8);
|
|
67460
67710
|
const platforms = ctx.state.platforms;
|
|
67461
67711
|
const platform = platforms.get(state.platformId);
|
|
67462
67712
|
if (!platform) {
|
|
67463
|
-
|
|
67713
|
+
log26.warn(`Platform ${state.platformId} not registered, skipping resume for ${shortId}...`);
|
|
67464
67714
|
return;
|
|
67465
67715
|
}
|
|
67466
67716
|
const threadPost = await platform.getPost(state.threadId);
|
|
67467
67717
|
if (!threadPost) {
|
|
67468
|
-
|
|
67718
|
+
log26.warn(`Thread ${shortId}... deleted, skipping resume`);
|
|
67469
67719
|
ctx.state.sessionStore.remove(`${state.platformId}:${state.threadId}`);
|
|
67470
67720
|
return;
|
|
67471
67721
|
}
|
|
67472
67722
|
if (ctx.state.sessions.size >= ctx.config.maxSessions) {
|
|
67473
|
-
|
|
67723
|
+
log26.warn(`Max sessions reached, skipping resume for ${shortId}...`);
|
|
67474
67724
|
return;
|
|
67475
67725
|
}
|
|
67476
67726
|
if (!existsSync11(state.workingDir)) {
|
|
67477
|
-
|
|
67727
|
+
log26.warn(`Working directory ${state.workingDir} no longer exists, skipping resume for ${shortId}...`);
|
|
67478
67728
|
ctx.state.sessionStore.remove(`${state.platformId}:${state.threadId}`);
|
|
67479
67729
|
const resumeFormatter = platform.getFormatter();
|
|
67480
67730
|
const tempSession = {
|
|
@@ -67496,6 +67746,10 @@ Please start a new session.`), { action: "Post resume failure notification" });
|
|
|
67496
67746
|
const appendSystemPrompt = `${sessionContext}
|
|
67497
67747
|
|
|
67498
67748
|
${CHAT_PLATFORM_PROMPT}`;
|
|
67749
|
+
const claudeAccount = ctx.ops.acquireClaudeAccount(state.claudeAccountId);
|
|
67750
|
+
if (state.claudeAccountId && !claudeAccount) {
|
|
67751
|
+
log26.warn(`Persisted session referenced Claude account "${state.claudeAccountId}" ` + `which is no longer configured — resuming under default env`);
|
|
67752
|
+
}
|
|
67499
67753
|
const cliOptions = {
|
|
67500
67754
|
workingDir: state.workingDir,
|
|
67501
67755
|
threadId: state.threadId,
|
|
@@ -67506,7 +67760,8 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
67506
67760
|
platformConfig: platformMcpConfig,
|
|
67507
67761
|
appendSystemPrompt,
|
|
67508
67762
|
logSessionId: sessionId,
|
|
67509
|
-
permissionTimeoutMs: ctx.config.permissionTimeoutMs
|
|
67763
|
+
permissionTimeoutMs: ctx.config.permissionTimeoutMs,
|
|
67764
|
+
account: claudeAccount ? { id: claudeAccount.id, home: claudeAccount.home, apiKey: claudeAccount.apiKey } : undefined
|
|
67510
67765
|
};
|
|
67511
67766
|
const claude = new ClaudeCli(cliOptions);
|
|
67512
67767
|
const session = {
|
|
@@ -67515,6 +67770,7 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
67515
67770
|
sessionId,
|
|
67516
67771
|
platform,
|
|
67517
67772
|
claudeSessionId: state.claudeSessionId,
|
|
67773
|
+
claudeAccountId: claudeAccount?.id,
|
|
67518
67774
|
startedBy: state.startedBy,
|
|
67519
67775
|
startedByDisplayName: state.startedByDisplayName,
|
|
67520
67776
|
startedAt: new Date(state.startedAt),
|
|
@@ -67557,7 +67813,7 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
67557
67813
|
worktreePath: detected.worktreePath,
|
|
67558
67814
|
branch: detected.branch
|
|
67559
67815
|
};
|
|
67560
|
-
|
|
67816
|
+
log26.info(`Auto-detected worktree info for resumed session: branch=${detected.branch}`);
|
|
67561
67817
|
}
|
|
67562
67818
|
}
|
|
67563
67819
|
session.messageManager = createMessageManager(session, ctx);
|
|
@@ -67592,6 +67848,7 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
67592
67848
|
keepAlive.sessionStarted();
|
|
67593
67849
|
claude.on("event", (e) => ctx.ops.handleEvent(sessionId, e));
|
|
67594
67850
|
claude.on("exit", (code) => ctx.ops.handleExit(sessionId, code));
|
|
67851
|
+
claude.on("rate-limit", (hit) => handleRateLimit(session, hit, ctx));
|
|
67595
67852
|
try {
|
|
67596
67853
|
claude.start();
|
|
67597
67854
|
sessionLog6(session).info(`\uD83D\uDD04 Session resumed (@${state.startedBy})`);
|
|
@@ -67612,10 +67869,11 @@ ${sessionFormatter.formatItalic("Reconnected to Claude session. You can continue
|
|
|
67612
67869
|
await ctx.ops.updateStickyMessage();
|
|
67613
67870
|
ctx.ops.persistSession(session);
|
|
67614
67871
|
} catch (err) {
|
|
67615
|
-
|
|
67872
|
+
log26.error(`Failed to resume session ${shortId}`, err instanceof Error ? err : undefined);
|
|
67616
67873
|
ctx.ops.emitSessionRemove(sessionId);
|
|
67617
67874
|
mutableSessions(ctx).delete(sessionId);
|
|
67618
67875
|
ctx.state.sessionStore.remove(sessionId);
|
|
67876
|
+
releaseAccountIfHeld(session, ctx);
|
|
67619
67877
|
const failFormatter = session.platform.getFormatter();
|
|
67620
67878
|
await withErrorHandling(() => post(session, "warning", `${failFormatter.formatBold("Could not resume previous session.")} Starting fresh.
|
|
67621
67879
|
${failFormatter.formatItalic("Your previous conversation context is preserved, but Claude needs to re-read it.")}`), { action: "Post resume failure notification", session });
|
|
@@ -67651,18 +67909,18 @@ async function resumePausedSession(threadId, message, files, ctx) {
|
|
|
67651
67909
|
const persisted = ctx.state.sessionStore.load();
|
|
67652
67910
|
const state = findPersistedByThreadId(persisted, threadId);
|
|
67653
67911
|
if (!state) {
|
|
67654
|
-
|
|
67912
|
+
log26.debug(`No persisted session found for ${threadId.substring(0, 8)}...`);
|
|
67655
67913
|
return;
|
|
67656
67914
|
}
|
|
67657
67915
|
const shortId = threadId.substring(0, 8);
|
|
67658
|
-
|
|
67916
|
+
log26.info(`\uD83D\uDD04 Resuming paused session ${shortId}... for new message`);
|
|
67659
67917
|
await resumeSession(state, ctx);
|
|
67660
67918
|
const session = ctx.ops.findSessionByThreadId(threadId);
|
|
67661
67919
|
if (session && session.claude.isRunning() && session.messageManager) {
|
|
67662
67920
|
session.messageCount++;
|
|
67663
67921
|
await session.messageManager.handleUserMessage(message, files, state.startedBy);
|
|
67664
67922
|
} else {
|
|
67665
|
-
|
|
67923
|
+
log26.warn(`Failed to resume session ${shortId}..., could not send message`);
|
|
67666
67924
|
}
|
|
67667
67925
|
}
|
|
67668
67926
|
async function handleExit(sessionId, code, ctx) {
|
|
@@ -67670,7 +67928,7 @@ async function handleExit(sessionId, code, ctx) {
|
|
|
67670
67928
|
const shortId = sessionId.substring(0, 8);
|
|
67671
67929
|
sessionLog6(session).debug(`handleExit called code=${code} isShuttingDown=${ctx.state.isShuttingDown}`);
|
|
67672
67930
|
if (!session) {
|
|
67673
|
-
|
|
67931
|
+
log26.debug(`Session ${shortId}... not found (already cleaned up)`);
|
|
67674
67932
|
return;
|
|
67675
67933
|
}
|
|
67676
67934
|
if (isSessionRestarting(session)) {
|
|
@@ -67863,7 +68121,7 @@ async function cleanupIdleSessions(timeoutMs, warningMs, ctx) {
|
|
|
67863
68121
|
}
|
|
67864
68122
|
|
|
67865
68123
|
// src/operations/monitor/handler.ts
|
|
67866
|
-
var
|
|
68124
|
+
var log27 = createLogger("monitor");
|
|
67867
68125
|
var DEFAULT_INTERVAL_MS = 60 * 1000;
|
|
67868
68126
|
|
|
67869
68127
|
class SessionMonitor {
|
|
@@ -67885,14 +68143,14 @@ class SessionMonitor {
|
|
|
67885
68143
|
}
|
|
67886
68144
|
start() {
|
|
67887
68145
|
if (this.isRunning) {
|
|
67888
|
-
|
|
68146
|
+
log27.debug("Session monitor already running");
|
|
67889
68147
|
return;
|
|
67890
68148
|
}
|
|
67891
68149
|
this.isRunning = true;
|
|
67892
|
-
|
|
68150
|
+
log27.debug(`Session monitor started (interval: ${this.intervalMs / 1000}s)`);
|
|
67893
68151
|
this.timer = setInterval(() => {
|
|
67894
68152
|
this.runCheck().catch((err) => {
|
|
67895
|
-
|
|
68153
|
+
log27.error(`Error during session monitoring: ${err}`);
|
|
67896
68154
|
});
|
|
67897
68155
|
}, this.intervalMs);
|
|
67898
68156
|
}
|
|
@@ -67902,7 +68160,7 @@ class SessionMonitor {
|
|
|
67902
68160
|
this.timer = null;
|
|
67903
68161
|
}
|
|
67904
68162
|
this.isRunning = false;
|
|
67905
|
-
|
|
68163
|
+
log27.debug("Session monitor stopped");
|
|
67906
68164
|
}
|
|
67907
68165
|
async runCheck() {
|
|
67908
68166
|
await cleanupIdleSessions(this.sessionTimeoutMs, this.sessionWarningMs, this.getContext());
|
|
@@ -67914,8 +68172,8 @@ class SessionMonitor {
|
|
|
67914
68172
|
// src/operations/plugin/handler.ts
|
|
67915
68173
|
init_spawn();
|
|
67916
68174
|
init_logger();
|
|
67917
|
-
var
|
|
67918
|
-
var sessionLog7 = createSessionLog(
|
|
68175
|
+
var log28 = createLogger("plugin");
|
|
68176
|
+
var sessionLog7 = createSessionLog(log28);
|
|
67919
68177
|
async function runPluginCommand(args, cwd, timeout2 = 60000) {
|
|
67920
68178
|
return new Promise((resolve6) => {
|
|
67921
68179
|
const claudePath = process.env.CLAUDE_PATH || "claude";
|
|
@@ -67936,7 +68194,7 @@ async function runPluginCommand(args, cwd, timeout2 = 60000) {
|
|
|
67936
68194
|
});
|
|
67937
68195
|
proc.on("error", (err) => {
|
|
67938
68196
|
resolve6({ stdout, stderr, exitCode: 1 });
|
|
67939
|
-
|
|
68197
|
+
log28.error(`Plugin command error: ${err.message}`);
|
|
67940
68198
|
});
|
|
67941
68199
|
});
|
|
67942
68200
|
}
|
|
@@ -68135,7 +68393,7 @@ class SessionRegistry {
|
|
|
68135
68393
|
|
|
68136
68394
|
// src/session/manager.ts
|
|
68137
68395
|
init_logger();
|
|
68138
|
-
var
|
|
68396
|
+
var log29 = createLogger("manager");
|
|
68139
68397
|
|
|
68140
68398
|
class SessionManager extends EventEmitter4 {
|
|
68141
68399
|
platforms = new Map;
|
|
@@ -68158,7 +68416,8 @@ class SessionManager extends EventEmitter4 {
|
|
|
68158
68416
|
customDescription;
|
|
68159
68417
|
customFooter;
|
|
68160
68418
|
autoUpdateManager = null;
|
|
68161
|
-
|
|
68419
|
+
accountPool;
|
|
68420
|
+
constructor(workingDir, skipPermissions = false, chromeEnabled = false, worktreeMode = "prompt", sessionsPath, threadLogsEnabled = true, threadLogsRetentionDays = 30, limits, claudeAccounts) {
|
|
68162
68421
|
super();
|
|
68163
68422
|
this.workingDir = workingDir;
|
|
68164
68423
|
this.skipPermissions = skipPermissions;
|
|
@@ -68169,6 +68428,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
68169
68428
|
this.limits = resolveLimits(limits);
|
|
68170
68429
|
this.sessionStore = new SessionStore(sessionsPath);
|
|
68171
68430
|
this.registry = new SessionRegistry(this.sessionStore);
|
|
68431
|
+
this.accountPool = new AccountPool(claudeAccounts);
|
|
68172
68432
|
this.sessionMonitor = new SessionMonitor({
|
|
68173
68433
|
sessionTimeoutMs: this.limits.sessionTimeoutMinutes * 60 * 1000,
|
|
68174
68434
|
sessionWarningMs: this.limits.sessionWarningMinutes * 60 * 1000,
|
|
@@ -68202,7 +68462,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
68202
68462
|
markNeedsBump(platformId);
|
|
68203
68463
|
this.updateStickyMessage();
|
|
68204
68464
|
});
|
|
68205
|
-
|
|
68465
|
+
log29.info(`\uD83D\uDCE1 Platform "${platformId}" registered`);
|
|
68206
68466
|
}
|
|
68207
68467
|
removePlatform(platformId) {
|
|
68208
68468
|
this.platforms.delete(platformId);
|
|
@@ -68218,7 +68478,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
68218
68478
|
if (users) {
|
|
68219
68479
|
users.add(sessionId);
|
|
68220
68480
|
}
|
|
68221
|
-
|
|
68481
|
+
log29.debug(`Registered session ${sessionId.substring(0, 20)} as worktree user for ${worktreePath}`);
|
|
68222
68482
|
}
|
|
68223
68483
|
unregisterWorktreeUser(worktreePath, sessionId) {
|
|
68224
68484
|
const users = this.worktreeUsers.get(worktreePath);
|
|
@@ -68284,7 +68544,12 @@ class SessionManager extends EventEmitter4 {
|
|
|
68284
68544
|
offerContextPrompt: (s, q, f, e) => offerContextPrompt(s, q, f, this.getContextPromptHandler(), e),
|
|
68285
68545
|
emitSessionAdd: (s) => this.emitSessionAdd(s),
|
|
68286
68546
|
emitSessionUpdate: (sid, u) => this.emitSessionUpdate(sid, u),
|
|
68287
|
-
emitSessionRemove: (sid) => this.emitSessionRemove(sid)
|
|
68547
|
+
emitSessionRemove: (sid) => this.emitSessionRemove(sid),
|
|
68548
|
+
acquireClaudeAccount: (preferredId) => this.accountPool.acquire(preferredId),
|
|
68549
|
+
getClaudeAccount: (id) => this.accountPool.get(id),
|
|
68550
|
+
releaseClaudeAccount: (id) => this.accountPool.release(id),
|
|
68551
|
+
markClaudeAccountCooling: (id, untilMs) => this.accountPool.markCooling(id, untilMs),
|
|
68552
|
+
getClaudeAccountPoolStatus: () => this.accountPool.status()
|
|
68288
68553
|
};
|
|
68289
68554
|
return createSessionContext(config, state, ops);
|
|
68290
68555
|
}
|
|
@@ -68365,7 +68630,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
68365
68630
|
return false;
|
|
68366
68631
|
}
|
|
68367
68632
|
const shortId = persistedSession.threadId.substring(0, 8);
|
|
68368
|
-
|
|
68633
|
+
log29.info(`\uD83D\uDD04 Resuming session ${shortId}... via emoji reaction by @${username}`);
|
|
68369
68634
|
await resumeSession(persistedSession, this.getContext());
|
|
68370
68635
|
return true;
|
|
68371
68636
|
}
|
|
@@ -68395,7 +68660,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
68395
68660
|
}
|
|
68396
68661
|
if (session.lastError?.postId === postId && isBugReportEmoji(emojiName)) {
|
|
68397
68662
|
if (session.startedBy === username || session.platform.isUserAllowed(username) || session.sessionAllowedUsers.has(username)) {
|
|
68398
|
-
|
|
68663
|
+
log29.info(`\uD83D\uDC1B @${username} triggered bug report from error reaction`);
|
|
68399
68664
|
await reportBug(session, undefined, username, this.getContext(), session.lastError);
|
|
68400
68665
|
return;
|
|
68401
68666
|
}
|
|
@@ -68511,7 +68776,8 @@ class SessionManager extends EventEmitter4 {
|
|
|
68511
68776
|
sessionTags: session.sessionTags,
|
|
68512
68777
|
pullRequestUrl: session.pullRequestUrl,
|
|
68513
68778
|
messageCount: session.messageCount,
|
|
68514
|
-
resumeFailCount: session.lifecycle.resumeFailCount
|
|
68779
|
+
resumeFailCount: session.lifecycle.resumeFailCount,
|
|
68780
|
+
claudeAccountId: session.claudeAccountId
|
|
68515
68781
|
};
|
|
68516
68782
|
this.sessionStore.save(session.sessionId, state);
|
|
68517
68783
|
}
|
|
@@ -68536,7 +68802,8 @@ class SessionManager extends EventEmitter4 {
|
|
|
68536
68802
|
workingDir: this.workingDir,
|
|
68537
68803
|
debug: this.debug,
|
|
68538
68804
|
description: this.customDescription,
|
|
68539
|
-
footer: this.customFooter
|
|
68805
|
+
footer: this.customFooter,
|
|
68806
|
+
accountPoolStatus: this.accountPool.isEmpty ? undefined : this.accountPool.status()
|
|
68540
68807
|
});
|
|
68541
68808
|
}
|
|
68542
68809
|
async updateAllStickyMessages() {
|
|
@@ -68561,11 +68828,11 @@ class SessionManager extends EventEmitter4 {
|
|
|
68561
68828
|
}
|
|
68562
68829
|
}
|
|
68563
68830
|
if (sessionsToKill.length === 0) {
|
|
68564
|
-
|
|
68831
|
+
log29.info(`No active sessions to pause for platform ${platformId}`);
|
|
68565
68832
|
await this.updateStickyMessage();
|
|
68566
68833
|
return;
|
|
68567
68834
|
}
|
|
68568
|
-
|
|
68835
|
+
log29.info(`⏸️ Pausing ${sessionsToKill.length} session(s) for platform ${platformId}`);
|
|
68569
68836
|
for (const session of sessionsToKill) {
|
|
68570
68837
|
try {
|
|
68571
68838
|
const fmt = session.platform.getFormatter();
|
|
@@ -68581,9 +68848,9 @@ class SessionManager extends EventEmitter4 {
|
|
|
68581
68848
|
session.claude.kill();
|
|
68582
68849
|
this.registry.unregister(session.sessionId);
|
|
68583
68850
|
this.emitSessionRemove(session.sessionId);
|
|
68584
|
-
|
|
68851
|
+
log29.info(`⏸️ Paused session ${session.threadId.substring(0, 8)}`);
|
|
68585
68852
|
} catch (err) {
|
|
68586
|
-
|
|
68853
|
+
log29.warn(`Failed to pause session ${session.threadId}: ${err}`);
|
|
68587
68854
|
}
|
|
68588
68855
|
}
|
|
68589
68856
|
for (const session of sessionsToKill) {
|
|
@@ -68604,17 +68871,17 @@ class SessionManager extends EventEmitter4 {
|
|
|
68604
68871
|
sessionsToResume.push(state);
|
|
68605
68872
|
}
|
|
68606
68873
|
if (sessionsToResume.length === 0) {
|
|
68607
|
-
|
|
68874
|
+
log29.info(`No paused sessions to resume for platform ${platformId}`);
|
|
68608
68875
|
await this.updateStickyMessage();
|
|
68609
68876
|
return;
|
|
68610
68877
|
}
|
|
68611
|
-
|
|
68878
|
+
log29.info(`▶️ Resuming ${sessionsToResume.length} paused session(s) for platform ${platformId}`);
|
|
68612
68879
|
for (const state of sessionsToResume) {
|
|
68613
68880
|
try {
|
|
68614
68881
|
await resumeSession(state, this.getContext());
|
|
68615
|
-
|
|
68882
|
+
log29.info(`▶️ Resumed session ${state.threadId.substring(0, 8)}`);
|
|
68616
68883
|
} catch (err) {
|
|
68617
|
-
|
|
68884
|
+
log29.warn(`Failed to resume session ${state.threadId}: ${err}`);
|
|
68618
68885
|
}
|
|
68619
68886
|
}
|
|
68620
68887
|
await this.updateStickyMessage();
|
|
@@ -68626,14 +68893,14 @@ class SessionManager extends EventEmitter4 {
|
|
|
68626
68893
|
const sessionTimeoutMs = this.limits.sessionTimeoutMinutes * 60 * 1000;
|
|
68627
68894
|
const staleIds = this.sessionStore.cleanStale(sessionTimeoutMs * 2);
|
|
68628
68895
|
if (staleIds.length > 0) {
|
|
68629
|
-
|
|
68896
|
+
log29.info(`\uD83E\uDDF9 Soft-deleted ${staleIds.length} stale session(s) (kept for history)`);
|
|
68630
68897
|
}
|
|
68631
68898
|
const removedCount = this.sessionStore.cleanHistory();
|
|
68632
68899
|
if (removedCount > 0) {
|
|
68633
|
-
|
|
68900
|
+
log29.info(`\uD83D\uDDD1️ Permanently removed ${removedCount} old session(s) from history`);
|
|
68634
68901
|
}
|
|
68635
68902
|
const persisted = this.sessionStore.load();
|
|
68636
|
-
|
|
68903
|
+
log29.info(`\uD83D\uDCC2 Loaded ${persisted.size} session(s) from persistence`);
|
|
68637
68904
|
const excludePostIdsByPlatform = new Map;
|
|
68638
68905
|
for (const session of persisted.values()) {
|
|
68639
68906
|
const platformId = session.platformId;
|
|
@@ -68653,10 +68920,10 @@ class SessionManager extends EventEmitter4 {
|
|
|
68653
68920
|
const excludePostIds = excludePostIdsByPlatform.get(platform.platformId);
|
|
68654
68921
|
platform.getBotUser().then((botUser) => {
|
|
68655
68922
|
cleanupOldStickyMessages(platform, botUser.id, true, excludePostIds).catch((err) => {
|
|
68656
|
-
|
|
68923
|
+
log29.warn(`Failed to cleanup old sticky messages for ${platform.platformId}: ${err}`);
|
|
68657
68924
|
});
|
|
68658
68925
|
}).catch((err) => {
|
|
68659
|
-
|
|
68926
|
+
log29.warn(`Failed to get bot user for cleanup on ${platform.platformId}: ${err}`);
|
|
68660
68927
|
});
|
|
68661
68928
|
}
|
|
68662
68929
|
if (persisted.size > 0) {
|
|
@@ -68670,10 +68937,10 @@ class SessionManager extends EventEmitter4 {
|
|
|
68670
68937
|
}
|
|
68671
68938
|
}
|
|
68672
68939
|
if (pausedToSkip.length > 0) {
|
|
68673
|
-
|
|
68940
|
+
log29.info(`⏸️ ${pausedToSkip.length} session(s) remain paused (waiting for user message)`);
|
|
68674
68941
|
}
|
|
68675
68942
|
if (activeToResume.length > 0) {
|
|
68676
|
-
|
|
68943
|
+
log29.info(`\uD83D\uDD04 Attempting to resume ${activeToResume.length} active session(s)...`);
|
|
68677
68944
|
for (const state of activeToResume) {
|
|
68678
68945
|
await resumeSession(state, this.getContext());
|
|
68679
68946
|
}
|
|
@@ -69092,7 +69359,7 @@ Mention me to start a session in this worktree.`, threadId);
|
|
|
69092
69359
|
const message = messageBuilder(formatter);
|
|
69093
69360
|
await post(session, "info", message);
|
|
69094
69361
|
} catch (err) {
|
|
69095
|
-
|
|
69362
|
+
log29.warn(`Failed to broadcast to session ${session.threadId}: ${err}`);
|
|
69096
69363
|
}
|
|
69097
69364
|
}
|
|
69098
69365
|
}
|
|
@@ -69111,7 +69378,7 @@ Mention me to start a session in this worktree.`, threadId);
|
|
|
69111
69378
|
session.messageManager?.setPendingUpdatePrompt({ postId: post2.id });
|
|
69112
69379
|
this.registerPost(post2.id, session.threadId);
|
|
69113
69380
|
} catch (err) {
|
|
69114
|
-
|
|
69381
|
+
log29.warn(`Failed to post ask message to ${threadId}: ${err}`);
|
|
69115
69382
|
}
|
|
69116
69383
|
}
|
|
69117
69384
|
}
|
|
@@ -76706,29 +76973,29 @@ function SessionLog({ logs, maxLines = 20 }) {
|
|
|
76706
76973
|
return /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
|
|
76707
76974
|
flexDirection: "column",
|
|
76708
76975
|
flexShrink: 0,
|
|
76709
|
-
children: displayLogs.map((
|
|
76976
|
+
children: displayLogs.map((log30) => /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
|
|
76710
76977
|
flexShrink: 0,
|
|
76711
76978
|
children: [
|
|
76712
76979
|
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
|
|
76713
|
-
color: getColorForLevel(
|
|
76980
|
+
color: getColorForLevel(log30.level),
|
|
76714
76981
|
dimColor: true,
|
|
76715
76982
|
wrap: "truncate",
|
|
76716
76983
|
children: [
|
|
76717
76984
|
"[",
|
|
76718
|
-
padComponent(
|
|
76985
|
+
padComponent(log30.component),
|
|
76719
76986
|
"]"
|
|
76720
76987
|
]
|
|
76721
76988
|
}, undefined, true, undefined, this),
|
|
76722
76989
|
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
|
|
76723
|
-
color: getColorForLevel(
|
|
76990
|
+
color: getColorForLevel(log30.level),
|
|
76724
76991
|
wrap: "truncate",
|
|
76725
76992
|
children: [
|
|
76726
76993
|
" ",
|
|
76727
|
-
|
|
76994
|
+
log30.message
|
|
76728
76995
|
]
|
|
76729
76996
|
}, undefined, true, undefined, this)
|
|
76730
76997
|
]
|
|
76731
|
-
},
|
|
76998
|
+
}, log30.id, true, undefined, this))
|
|
76732
76999
|
}, undefined, false, undefined, this);
|
|
76733
77000
|
}
|
|
76734
77001
|
// src/ui/components/Footer.tsx
|
|
@@ -77226,7 +77493,7 @@ function LogPanel({ logs, maxLines = 10, focused = false }) {
|
|
|
77226
77493
|
const scrollRef = import_react59.default.useRef(null);
|
|
77227
77494
|
const { stdout } = use_stdout_default();
|
|
77228
77495
|
const isDebug = process.env.DEBUG === "1";
|
|
77229
|
-
const displayLogs = logs.filter((
|
|
77496
|
+
const displayLogs = logs.filter((log30) => isDebug || log30.level !== "debug");
|
|
77230
77497
|
const visibleLogs = displayLogs.slice(-Math.max(maxLines * 3, 100));
|
|
77231
77498
|
import_react59.default.useEffect(() => {
|
|
77232
77499
|
const handleResize = () => scrollRef.current?.remeasure();
|
|
@@ -77266,25 +77533,25 @@ function LogPanel({ logs, maxLines = 10, focused = false }) {
|
|
|
77266
77533
|
overflow: "hidden",
|
|
77267
77534
|
children: /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(ScrollView, {
|
|
77268
77535
|
ref: scrollRef,
|
|
77269
|
-
children: visibleLogs.map((
|
|
77536
|
+
children: visibleLogs.map((log30) => /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
|
|
77270
77537
|
children: [
|
|
77271
77538
|
/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
|
|
77272
77539
|
dimColor: true,
|
|
77273
77540
|
children: [
|
|
77274
77541
|
"[",
|
|
77275
|
-
padComponent2(
|
|
77542
|
+
padComponent2(log30.component),
|
|
77276
77543
|
"]"
|
|
77277
77544
|
]
|
|
77278
77545
|
}, undefined, true, undefined, this),
|
|
77279
77546
|
/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
|
|
77280
|
-
color: getLevelColor(
|
|
77547
|
+
color: getLevelColor(log30.level),
|
|
77281
77548
|
children: [
|
|
77282
77549
|
" ",
|
|
77283
|
-
|
|
77550
|
+
log30.message
|
|
77284
77551
|
]
|
|
77285
77552
|
}, undefined, true, undefined, this)
|
|
77286
77553
|
]
|
|
77287
|
-
},
|
|
77554
|
+
}, log30.id, true, undefined, this))
|
|
77288
77555
|
}, undefined, false, undefined, this)
|
|
77289
77556
|
}, undefined, false, undefined, this);
|
|
77290
77557
|
}
|
|
@@ -77801,10 +78068,10 @@ function useAppState(initialConfig) {
|
|
|
77801
78068
|
});
|
|
77802
78069
|
}, []);
|
|
77803
78070
|
const getLogsForSession = import_react60.useCallback((sessionId) => {
|
|
77804
|
-
return state.logs.filter((
|
|
78071
|
+
return state.logs.filter((log30) => log30.sessionId === sessionId);
|
|
77805
78072
|
}, [state.logs]);
|
|
77806
78073
|
const getGlobalLogs = import_react60.useCallback(() => {
|
|
77807
|
-
return state.logs.filter((
|
|
78074
|
+
return state.logs.filter((log30) => !log30.sessionId);
|
|
77808
78075
|
}, [state.logs]);
|
|
77809
78076
|
const togglePlatformEnabled = import_react60.useCallback((platformId) => {
|
|
77810
78077
|
let newEnabled = false;
|
|
@@ -78798,7 +79065,7 @@ import { EventEmitter as EventEmitter9 } from "events";
|
|
|
78798
79065
|
// src/auto-update/checker.ts
|
|
78799
79066
|
init_logger();
|
|
78800
79067
|
import { EventEmitter as EventEmitter7 } from "events";
|
|
78801
|
-
var
|
|
79068
|
+
var log30 = createLogger("checker");
|
|
78802
79069
|
var PACKAGE_NAME = "claude-threads";
|
|
78803
79070
|
function compareVersions(a, b) {
|
|
78804
79071
|
const partsA = a.replace(/^v/, "").split(".").map(Number);
|
|
@@ -78821,13 +79088,13 @@ async function fetchLatestVersion() {
|
|
|
78821
79088
|
}
|
|
78822
79089
|
});
|
|
78823
79090
|
if (!response.ok) {
|
|
78824
|
-
|
|
79091
|
+
log30.warn(`Failed to fetch latest version: HTTP ${response.status}`);
|
|
78825
79092
|
return null;
|
|
78826
79093
|
}
|
|
78827
79094
|
const data = await response.json();
|
|
78828
79095
|
return data.version ?? null;
|
|
78829
79096
|
} catch (err) {
|
|
78830
|
-
|
|
79097
|
+
log30.warn(`Failed to fetch latest version: ${err}`);
|
|
78831
79098
|
return null;
|
|
78832
79099
|
}
|
|
78833
79100
|
}
|
|
@@ -78844,38 +79111,38 @@ class UpdateChecker extends EventEmitter7 {
|
|
|
78844
79111
|
}
|
|
78845
79112
|
start() {
|
|
78846
79113
|
if (!this.config.enabled) {
|
|
78847
|
-
|
|
79114
|
+
log30.debug("Auto-update disabled, not starting checker");
|
|
78848
79115
|
return;
|
|
78849
79116
|
}
|
|
78850
79117
|
setTimeout(() => {
|
|
78851
79118
|
this.check().catch((err) => {
|
|
78852
|
-
|
|
79119
|
+
log30.warn(`Initial update check failed: ${err}`);
|
|
78853
79120
|
});
|
|
78854
79121
|
}, 5000);
|
|
78855
79122
|
const intervalMs = this.config.checkIntervalMinutes * 60 * 1000;
|
|
78856
79123
|
this.checkInterval = setInterval(() => {
|
|
78857
79124
|
this.check().catch((err) => {
|
|
78858
|
-
|
|
79125
|
+
log30.warn(`Periodic update check failed: ${err}`);
|
|
78859
79126
|
});
|
|
78860
79127
|
}, intervalMs);
|
|
78861
|
-
|
|
79128
|
+
log30.info(`\uD83D\uDD04 Update checker started (every ${this.config.checkIntervalMinutes} minutes)`);
|
|
78862
79129
|
}
|
|
78863
79130
|
stop() {
|
|
78864
79131
|
if (this.checkInterval) {
|
|
78865
79132
|
clearInterval(this.checkInterval);
|
|
78866
79133
|
this.checkInterval = null;
|
|
78867
79134
|
}
|
|
78868
|
-
|
|
79135
|
+
log30.debug("Update checker stopped");
|
|
78869
79136
|
}
|
|
78870
79137
|
async check() {
|
|
78871
79138
|
if (this.isChecking) {
|
|
78872
|
-
|
|
79139
|
+
log30.debug("Check already in progress, skipping");
|
|
78873
79140
|
return this.lastUpdateInfo;
|
|
78874
79141
|
}
|
|
78875
79142
|
this.isChecking = true;
|
|
78876
79143
|
this.emit("check:start");
|
|
78877
79144
|
try {
|
|
78878
|
-
|
|
79145
|
+
log30.debug("Checking for updates...");
|
|
78879
79146
|
const latestVersion2 = await fetchLatestVersion();
|
|
78880
79147
|
if (!latestVersion2) {
|
|
78881
79148
|
this.emit("check:complete", false);
|
|
@@ -78892,18 +79159,18 @@ class UpdateChecker extends EventEmitter7 {
|
|
|
78892
79159
|
detectedAt: new Date
|
|
78893
79160
|
};
|
|
78894
79161
|
if (!this.lastUpdateInfo || this.lastUpdateInfo.latestVersion !== latestVersion2) {
|
|
78895
|
-
|
|
79162
|
+
log30.info(`\uD83C\uDD95 Update available: v${currentVersion} → v${latestVersion2}`);
|
|
78896
79163
|
this.lastUpdateInfo = updateInfo;
|
|
78897
79164
|
this.emit("update", updateInfo);
|
|
78898
79165
|
}
|
|
78899
79166
|
this.emit("check:complete", true);
|
|
78900
79167
|
return updateInfo;
|
|
78901
79168
|
}
|
|
78902
|
-
|
|
79169
|
+
log30.debug(`Up to date (v${currentVersion})`);
|
|
78903
79170
|
this.emit("check:complete", false);
|
|
78904
79171
|
return null;
|
|
78905
79172
|
} catch (err) {
|
|
78906
|
-
|
|
79173
|
+
log30.warn(`Update check failed: ${err}`);
|
|
78907
79174
|
this.emit("check:error", err);
|
|
78908
79175
|
return null;
|
|
78909
79176
|
} finally {
|
|
@@ -78974,7 +79241,7 @@ function isInScheduledWindow(window2) {
|
|
|
78974
79241
|
}
|
|
78975
79242
|
|
|
78976
79243
|
// src/auto-update/scheduler.ts
|
|
78977
|
-
var
|
|
79244
|
+
var log31 = createLogger("scheduler");
|
|
78978
79245
|
|
|
78979
79246
|
class UpdateScheduler extends EventEmitter8 {
|
|
78980
79247
|
config;
|
|
@@ -78998,7 +79265,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
78998
79265
|
scheduleUpdate(updateInfo) {
|
|
78999
79266
|
this.pendingUpdate = updateInfo;
|
|
79000
79267
|
if (this.config.autoRestartMode === "immediate") {
|
|
79001
|
-
|
|
79268
|
+
log31.info("Immediate mode: triggering update now");
|
|
79002
79269
|
this.emit("ready", updateInfo);
|
|
79003
79270
|
return;
|
|
79004
79271
|
}
|
|
@@ -79011,19 +79278,19 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79011
79278
|
this.scheduledRestartAt = null;
|
|
79012
79279
|
this.askApprovals.clear();
|
|
79013
79280
|
this.askStartTime = null;
|
|
79014
|
-
|
|
79281
|
+
log31.debug("Update schedule cancelled");
|
|
79015
79282
|
}
|
|
79016
79283
|
deferUpdate(minutes) {
|
|
79017
79284
|
const deferUntil = new Date(Date.now() + minutes * 60 * 1000);
|
|
79018
79285
|
this.scheduledRestartAt = null;
|
|
79019
79286
|
this.idleStartTime = null;
|
|
79020
79287
|
this.emit("deferred", deferUntil);
|
|
79021
|
-
|
|
79288
|
+
log31.info(`Update deferred until ${deferUntil.toLocaleTimeString()}`);
|
|
79022
79289
|
return deferUntil;
|
|
79023
79290
|
}
|
|
79024
79291
|
recordAskResponse(threadId, approved) {
|
|
79025
79292
|
this.askApprovals.set(threadId, approved);
|
|
79026
|
-
|
|
79293
|
+
log31.debug(`Thread ${threadId.substring(0, 8)} ${approved ? "approved" : "denied"} update`);
|
|
79027
79294
|
this.checkAskCondition();
|
|
79028
79295
|
}
|
|
79029
79296
|
getScheduledRestartAt() {
|
|
@@ -79044,7 +79311,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79044
79311
|
return;
|
|
79045
79312
|
this.checkCondition();
|
|
79046
79313
|
this.checkTimer = setInterval(() => this.checkCondition(), 1e4);
|
|
79047
|
-
|
|
79314
|
+
log31.debug(`Started checking for ${this.config.autoRestartMode} condition`);
|
|
79048
79315
|
}
|
|
79049
79316
|
stopChecking() {
|
|
79050
79317
|
if (this.checkTimer) {
|
|
@@ -79075,17 +79342,17 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79075
79342
|
if (activity.activeSessionCount === 0) {
|
|
79076
79343
|
if (!this.idleStartTime) {
|
|
79077
79344
|
this.idleStartTime = new Date;
|
|
79078
|
-
|
|
79345
|
+
log31.debug("No active sessions, starting idle timer");
|
|
79079
79346
|
}
|
|
79080
79347
|
const idleMs = Date.now() - this.idleStartTime.getTime();
|
|
79081
79348
|
const requiredMs = this.config.idleTimeoutMinutes * 60 * 1000;
|
|
79082
79349
|
if (idleMs >= requiredMs) {
|
|
79083
|
-
|
|
79350
|
+
log31.info(`Idle for ${this.config.idleTimeoutMinutes} minutes, triggering update`);
|
|
79084
79351
|
this.triggerCountdown();
|
|
79085
79352
|
}
|
|
79086
79353
|
} else {
|
|
79087
79354
|
if (this.idleStartTime) {
|
|
79088
|
-
|
|
79355
|
+
log31.debug("Sessions became active, resetting idle timer");
|
|
79089
79356
|
this.idleStartTime = null;
|
|
79090
79357
|
}
|
|
79091
79358
|
}
|
|
@@ -79096,7 +79363,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79096
79363
|
const quietMs = Date.now() - activity.lastActivityAt.getTime();
|
|
79097
79364
|
const requiredMs = this.config.quietTimeoutMinutes * 60 * 1000;
|
|
79098
79365
|
if (quietMs >= requiredMs && !activity.anySessionBusy) {
|
|
79099
|
-
|
|
79366
|
+
log31.info(`Sessions quiet for ${this.config.quietTimeoutMinutes} minutes, triggering update`);
|
|
79100
79367
|
this.triggerCountdown();
|
|
79101
79368
|
}
|
|
79102
79369
|
} else if (activity.activeSessionCount === 0) {
|
|
@@ -79106,7 +79373,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79106
79373
|
const idleMs = Date.now() - this.idleStartTime.getTime();
|
|
79107
79374
|
const requiredMs = this.config.quietTimeoutMinutes * 60 * 1000;
|
|
79108
79375
|
if (idleMs >= requiredMs) {
|
|
79109
|
-
|
|
79376
|
+
log31.info("No sessions and quiet timeout reached, triggering update");
|
|
79110
79377
|
this.triggerCountdown();
|
|
79111
79378
|
}
|
|
79112
79379
|
}
|
|
@@ -79117,13 +79384,13 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79117
79384
|
}
|
|
79118
79385
|
const activity = this.getSessionActivity();
|
|
79119
79386
|
if (activity.activeSessionCount === 0) {
|
|
79120
|
-
|
|
79387
|
+
log31.info("Within scheduled window and no active sessions, triggering update");
|
|
79121
79388
|
this.triggerCountdown();
|
|
79122
79389
|
} else if (activity.lastActivityAt) {
|
|
79123
79390
|
const quietMs = Date.now() - activity.lastActivityAt.getTime();
|
|
79124
79391
|
const requiredMs = this.config.idleTimeoutMinutes * 60 * 1000;
|
|
79125
79392
|
if (quietMs >= requiredMs && !activity.anySessionBusy) {
|
|
79126
|
-
|
|
79393
|
+
log31.info("Within scheduled window and sessions quiet, triggering update");
|
|
79127
79394
|
this.triggerCountdown();
|
|
79128
79395
|
}
|
|
79129
79396
|
}
|
|
@@ -79131,14 +79398,14 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79131
79398
|
checkAskCondition() {
|
|
79132
79399
|
const threadIds = this.getActiveThreadIds();
|
|
79133
79400
|
if (threadIds.length === 0) {
|
|
79134
|
-
|
|
79401
|
+
log31.info("No active threads, proceeding with update");
|
|
79135
79402
|
this.triggerCountdown();
|
|
79136
79403
|
return;
|
|
79137
79404
|
}
|
|
79138
79405
|
if (!this.askStartTime && this.pendingUpdate) {
|
|
79139
79406
|
this.askStartTime = new Date;
|
|
79140
79407
|
this.postAskMessage(threadIds, this.pendingUpdate.latestVersion).catch((err) => {
|
|
79141
|
-
|
|
79408
|
+
log31.warn(`Failed to post ask message: ${err}`);
|
|
79142
79409
|
});
|
|
79143
79410
|
return;
|
|
79144
79411
|
}
|
|
@@ -79151,12 +79418,12 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79151
79418
|
denials++;
|
|
79152
79419
|
}
|
|
79153
79420
|
if (approvals > threadIds.length / 2) {
|
|
79154
|
-
|
|
79421
|
+
log31.info(`Majority approved (${approvals}/${threadIds.length}), triggering update`);
|
|
79155
79422
|
this.triggerCountdown();
|
|
79156
79423
|
return;
|
|
79157
79424
|
}
|
|
79158
79425
|
if (denials > threadIds.length / 2) {
|
|
79159
|
-
|
|
79426
|
+
log31.info(`Majority denied (${denials}/${threadIds.length}), deferring update`);
|
|
79160
79427
|
this.deferUpdate(60);
|
|
79161
79428
|
return;
|
|
79162
79429
|
}
|
|
@@ -79164,7 +79431,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79164
79431
|
const elapsedMs = Date.now() - this.askStartTime.getTime();
|
|
79165
79432
|
const timeoutMs = this.config.askTimeoutMinutes * 60 * 1000;
|
|
79166
79433
|
if (elapsedMs >= timeoutMs) {
|
|
79167
|
-
|
|
79434
|
+
log31.info(`Ask timeout reached (${this.config.askTimeoutMinutes} min), triggering update`);
|
|
79168
79435
|
this.triggerCountdown();
|
|
79169
79436
|
}
|
|
79170
79437
|
}
|
|
@@ -79184,7 +79451,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79184
79451
|
this.emit("ready", this.pendingUpdate);
|
|
79185
79452
|
}
|
|
79186
79453
|
}, 1000);
|
|
79187
|
-
|
|
79454
|
+
log31.info("Update countdown started (60 seconds)");
|
|
79188
79455
|
}
|
|
79189
79456
|
stopCountdown() {
|
|
79190
79457
|
if (this.countdownTimer) {
|
|
@@ -79200,24 +79467,24 @@ import { spawn as spawn4, spawnSync } from "child_process";
|
|
|
79200
79467
|
import { existsSync as existsSync13, readFileSync as readFileSync9, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4 } from "fs";
|
|
79201
79468
|
import { dirname as dirname8, resolve as resolve6 } from "path";
|
|
79202
79469
|
import { homedir as homedir5 } from "os";
|
|
79203
|
-
var
|
|
79470
|
+
var log32 = createLogger("installer");
|
|
79204
79471
|
function detectPackageManager() {
|
|
79205
79472
|
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
79206
79473
|
const originalInstaller = detectOriginalInstaller();
|
|
79207
79474
|
if (originalInstaller) {
|
|
79208
|
-
|
|
79475
|
+
log32.debug(`Detected original installer: ${originalInstaller}`);
|
|
79209
79476
|
if (originalInstaller === "bun") {
|
|
79210
79477
|
const bunCheck2 = spawnSync("bun", ["--version"], { stdio: "ignore" });
|
|
79211
79478
|
if (bunCheck2.status === 0) {
|
|
79212
79479
|
return { cmd: "bun", isBun: true };
|
|
79213
79480
|
}
|
|
79214
|
-
|
|
79481
|
+
log32.warn("Originally installed with bun, but bun not found. Falling back to npm.");
|
|
79215
79482
|
} else {
|
|
79216
79483
|
const npmCheck2 = spawnSync(npmCmd, ["--version"], { stdio: "ignore" });
|
|
79217
79484
|
if (npmCheck2.status === 0) {
|
|
79218
79485
|
return { cmd: npmCmd, isBun: false };
|
|
79219
79486
|
}
|
|
79220
|
-
|
|
79487
|
+
log32.warn("Originally installed with npm, but npm not found. Falling back to bun.");
|
|
79221
79488
|
}
|
|
79222
79489
|
}
|
|
79223
79490
|
const bunCheck = spawnSync("bun", ["--version"], { stdio: "ignore" });
|
|
@@ -79268,7 +79535,7 @@ function loadUpdateState() {
|
|
|
79268
79535
|
return JSON.parse(content);
|
|
79269
79536
|
}
|
|
79270
79537
|
} catch (err) {
|
|
79271
|
-
|
|
79538
|
+
log32.warn(`Failed to load update state: ${err}`);
|
|
79272
79539
|
}
|
|
79273
79540
|
return {};
|
|
79274
79541
|
}
|
|
@@ -79279,9 +79546,9 @@ function saveUpdateState(state) {
|
|
|
79279
79546
|
mkdirSync4(dir, { recursive: true });
|
|
79280
79547
|
}
|
|
79281
79548
|
writeFileSync5(STATE_PATH, JSON.stringify(state, null, 2), "utf-8");
|
|
79282
|
-
|
|
79549
|
+
log32.debug("Update state saved");
|
|
79283
79550
|
} catch (err) {
|
|
79284
|
-
|
|
79551
|
+
log32.warn(`Failed to save update state: ${err}`);
|
|
79285
79552
|
}
|
|
79286
79553
|
}
|
|
79287
79554
|
function clearUpdateState() {
|
|
@@ -79290,7 +79557,7 @@ function clearUpdateState() {
|
|
|
79290
79557
|
writeFileSync5(STATE_PATH, "{}", "utf-8");
|
|
79291
79558
|
}
|
|
79292
79559
|
} catch (err) {
|
|
79293
|
-
|
|
79560
|
+
log32.warn(`Failed to clear update state: ${err}`);
|
|
79294
79561
|
}
|
|
79295
79562
|
}
|
|
79296
79563
|
function checkJustUpdated() {
|
|
@@ -79322,11 +79589,11 @@ function clearRuntimeSettings() {
|
|
|
79322
79589
|
}
|
|
79323
79590
|
}
|
|
79324
79591
|
async function installVersion(version) {
|
|
79325
|
-
|
|
79592
|
+
log32.info(`\uD83D\uDCE6 Installing ${PACKAGE_NAME2}@${version}...`);
|
|
79326
79593
|
const pm = detectPackageManager();
|
|
79327
79594
|
if (!pm) {
|
|
79328
79595
|
const error = "Neither bun nor npm found in PATH. Cannot install update.";
|
|
79329
|
-
|
|
79596
|
+
log32.error(`❌ ${error}`);
|
|
79330
79597
|
return { success: false, error };
|
|
79331
79598
|
}
|
|
79332
79599
|
saveUpdateState({
|
|
@@ -79338,7 +79605,7 @@ async function installVersion(version) {
|
|
|
79338
79605
|
return new Promise((resolve7) => {
|
|
79339
79606
|
const { cmd, isBun: isBun3 } = pm;
|
|
79340
79607
|
const args = ["install", "-g", `${PACKAGE_NAME2}@${version}`];
|
|
79341
|
-
|
|
79608
|
+
log32.debug(`Using ${isBun3 ? "bun" : "npm"} for installation`);
|
|
79342
79609
|
const child = spawn4(cmd, args, {
|
|
79343
79610
|
stdio: ["ignore", "pipe", "pipe"],
|
|
79344
79611
|
env: {
|
|
@@ -79356,7 +79623,7 @@ async function installVersion(version) {
|
|
|
79356
79623
|
});
|
|
79357
79624
|
child.on("close", (code) => {
|
|
79358
79625
|
if (code === 0) {
|
|
79359
|
-
|
|
79626
|
+
log32.info(`✅ Successfully installed ${PACKAGE_NAME2}@${version}`);
|
|
79360
79627
|
saveUpdateState({
|
|
79361
79628
|
previousVersion: VERSION,
|
|
79362
79629
|
targetVersion: version,
|
|
@@ -79366,20 +79633,20 @@ async function installVersion(version) {
|
|
|
79366
79633
|
resolve7({ success: true });
|
|
79367
79634
|
} else {
|
|
79368
79635
|
const errorMsg = stderr || stdout || `Exit code: ${code}`;
|
|
79369
|
-
|
|
79636
|
+
log32.error(`❌ Installation failed: ${errorMsg}`);
|
|
79370
79637
|
clearUpdateState();
|
|
79371
79638
|
resolve7({ success: false, error: errorMsg });
|
|
79372
79639
|
}
|
|
79373
79640
|
});
|
|
79374
79641
|
child.on("error", (err) => {
|
|
79375
|
-
|
|
79642
|
+
log32.error(`❌ Failed to spawn npm: ${err}`);
|
|
79376
79643
|
clearUpdateState();
|
|
79377
79644
|
resolve7({ success: false, error: err.message });
|
|
79378
79645
|
});
|
|
79379
79646
|
setTimeout(() => {
|
|
79380
79647
|
if (child.exitCode === null) {
|
|
79381
79648
|
child.kill();
|
|
79382
|
-
|
|
79649
|
+
log32.error("❌ Installation timed out");
|
|
79383
79650
|
clearUpdateState();
|
|
79384
79651
|
resolve7({ success: false, error: "Installation timed out" });
|
|
79385
79652
|
}
|
|
@@ -79421,7 +79688,7 @@ class UpdateInstaller {
|
|
|
79421
79688
|
}
|
|
79422
79689
|
|
|
79423
79690
|
// src/auto-update/manager.ts
|
|
79424
|
-
var
|
|
79691
|
+
var log33 = createLogger("updater");
|
|
79425
79692
|
|
|
79426
79693
|
class AutoUpdateManager extends EventEmitter9 {
|
|
79427
79694
|
config;
|
|
@@ -79444,23 +79711,23 @@ class AutoUpdateManager extends EventEmitter9 {
|
|
|
79444
79711
|
}
|
|
79445
79712
|
start() {
|
|
79446
79713
|
if (!this.config.enabled) {
|
|
79447
|
-
|
|
79714
|
+
log33.info("Auto-update is disabled");
|
|
79448
79715
|
return;
|
|
79449
79716
|
}
|
|
79450
79717
|
const updateResult = this.installer.checkJustUpdated();
|
|
79451
79718
|
if (updateResult) {
|
|
79452
|
-
|
|
79719
|
+
log33.info(`\uD83C\uDF89 Updated from v${updateResult.previousVersion} to v${updateResult.currentVersion}`);
|
|
79453
79720
|
this.callbacks.broadcastUpdate((fmt) => `\uD83C\uDF89 ${fmt.formatBold("Bot updated")} from v${updateResult.previousVersion} to v${updateResult.currentVersion}`).catch((err) => {
|
|
79454
|
-
|
|
79721
|
+
log33.warn(`Failed to broadcast update notification: ${err}`);
|
|
79455
79722
|
});
|
|
79456
79723
|
}
|
|
79457
79724
|
this.checker.start();
|
|
79458
|
-
|
|
79725
|
+
log33.info(`\uD83D\uDD04 Auto-update manager started (mode: ${this.config.autoRestartMode})`);
|
|
79459
79726
|
}
|
|
79460
79727
|
stop() {
|
|
79461
79728
|
this.checker.stop();
|
|
79462
79729
|
this.scheduler.stop();
|
|
79463
|
-
|
|
79730
|
+
log33.debug("Auto-update manager stopped");
|
|
79464
79731
|
}
|
|
79465
79732
|
getState() {
|
|
79466
79733
|
return { ...this.state };
|
|
@@ -79474,10 +79741,10 @@ class AutoUpdateManager extends EventEmitter9 {
|
|
|
79474
79741
|
async forceUpdate() {
|
|
79475
79742
|
const updateInfo = this.state.updateInfo || await this.checker.check();
|
|
79476
79743
|
if (!updateInfo) {
|
|
79477
|
-
|
|
79744
|
+
log33.info("No update available");
|
|
79478
79745
|
return;
|
|
79479
79746
|
}
|
|
79480
|
-
|
|
79747
|
+
log33.info("Forcing immediate update");
|
|
79481
79748
|
await this.performUpdate(updateInfo);
|
|
79482
79749
|
}
|
|
79483
79750
|
deferUpdate(minutes = 60) {
|
|
@@ -79532,7 +79799,7 @@ class AutoUpdateManager extends EventEmitter9 {
|
|
|
79532
79799
|
await this.callbacks.broadcastUpdate((fmt) => `✅ ${fmt.formatBold("Update installed")} - restarting now. ${fmt.formatItalic("Sessions will resume automatically.")}`).catch(() => {});
|
|
79533
79800
|
await new Promise((resolve7) => setTimeout(resolve7, 1000));
|
|
79534
79801
|
await this.callbacks.prepareForRestart();
|
|
79535
|
-
|
|
79802
|
+
log33.info(`\uD83D\uDD04 Restarting for update to v${updateInfo.latestVersion}`);
|
|
79536
79803
|
process.stdout.write("\x1B[2J\x1B[H");
|
|
79537
79804
|
process.stdout.write("\x1B[?25h");
|
|
79538
79805
|
process.exit(RESTART_EXIT_CODE);
|
|
@@ -79827,7 +80094,7 @@ async function startWithoutDaemon() {
|
|
|
79827
80094
|
keepAlive.setEnabled(keepAliveEnabled);
|
|
79828
80095
|
const threadLogsEnabled = config.threadLogs?.enabled ?? true;
|
|
79829
80096
|
const threadLogsRetentionDays = config.threadLogs?.retentionDays ?? 30;
|
|
79830
|
-
const session = new SessionManager(workingDir, initialSkipPermissions, config.chrome, config.worktreeMode, undefined, threadLogsEnabled, threadLogsRetentionDays, config.limits);
|
|
80097
|
+
const session = new SessionManager(workingDir, initialSkipPermissions, config.chrome, config.worktreeMode, undefined, threadLogsEnabled, threadLogsRetentionDays, config.limits, config.claudeAccounts);
|
|
79831
80098
|
if (config.stickyMessage) {
|
|
79832
80099
|
session.setStickyMessageCustomization(config.stickyMessage.description, config.stickyMessage.footer);
|
|
79833
80100
|
}
|