claude-threads 1.6.3 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +1 -0
- package/dist/index.js +598 -312
- package/dist/mcp/permission-server.js +144 -37
- package/docs/CONFIGURATION.md +27 -0
- 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,104 @@ 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
|
+
return false;
|
|
53565
|
+
}
|
|
53566
|
+
if (acc.home && acc.apiKey) {
|
|
53567
|
+
log6.warn(`Claude account ${acc.id} has both home and apiKey set — must choose one; ignoring`);
|
|
53568
|
+
return false;
|
|
53569
|
+
}
|
|
53570
|
+
return true;
|
|
53571
|
+
});
|
|
53572
|
+
this.byId = new Map(this.accounts.map((acc) => [acc.id, acc]));
|
|
53573
|
+
for (const acc of this.accounts) {
|
|
53574
|
+
this.activeCounts.set(acc.id, 0);
|
|
53575
|
+
}
|
|
53576
|
+
}
|
|
53577
|
+
get isEmpty() {
|
|
53578
|
+
return this.accounts.length === 0;
|
|
53579
|
+
}
|
|
53580
|
+
get size() {
|
|
53581
|
+
return this.accounts.length;
|
|
53582
|
+
}
|
|
53583
|
+
acquire(preferredId) {
|
|
53584
|
+
if (this.isEmpty)
|
|
53585
|
+
return null;
|
|
53586
|
+
if (preferredId) {
|
|
53587
|
+
const preferred = this.byId.get(preferredId);
|
|
53588
|
+
if (preferred) {
|
|
53589
|
+
this.incrementActive(preferred.id);
|
|
53590
|
+
return preferred;
|
|
53591
|
+
}
|
|
53592
|
+
log6.warn(`Preferred account "${preferredId}" not in pool — falling back to round-robin`);
|
|
53593
|
+
}
|
|
53594
|
+
const now = Date.now();
|
|
53595
|
+
const n = this.accounts.length;
|
|
53596
|
+
for (let i2 = 0;i2 < n; i2++) {
|
|
53597
|
+
const idx = (this.roundRobinIndex + i2) % n;
|
|
53598
|
+
const candidate = this.accounts[idx];
|
|
53599
|
+
const cooling = this.coolingUntil.get(candidate.id) ?? 0;
|
|
53600
|
+
if (cooling <= now) {
|
|
53601
|
+
this.roundRobinIndex = (idx + 1) % n;
|
|
53602
|
+
this.incrementActive(candidate.id);
|
|
53603
|
+
return candidate;
|
|
53604
|
+
}
|
|
53605
|
+
}
|
|
53606
|
+
log6.warn(`All ${n} accounts are in rate-limit cooldown`);
|
|
53607
|
+
return null;
|
|
53608
|
+
}
|
|
53609
|
+
release(accountId) {
|
|
53610
|
+
const current = this.activeCounts.get(accountId);
|
|
53611
|
+
if (current === undefined)
|
|
53612
|
+
return;
|
|
53613
|
+
this.activeCounts.set(accountId, Math.max(0, current - 1));
|
|
53614
|
+
}
|
|
53615
|
+
markCooling(accountId, untilEpochMs) {
|
|
53616
|
+
if (!this.byId.has(accountId)) {
|
|
53617
|
+
log6.warn(`markCooling called for unknown account "${accountId}"`);
|
|
53618
|
+
return;
|
|
53619
|
+
}
|
|
53620
|
+
const existing = this.coolingUntil.get(accountId) ?? 0;
|
|
53621
|
+
if (untilEpochMs > existing) {
|
|
53622
|
+
this.coolingUntil.set(accountId, untilEpochMs);
|
|
53623
|
+
const minutes = Math.ceil((untilEpochMs - Date.now()) / 60000);
|
|
53624
|
+
log6.info(`Account "${accountId}" cooling for ~${minutes}min`);
|
|
53625
|
+
}
|
|
53626
|
+
}
|
|
53627
|
+
get(accountId) {
|
|
53628
|
+
return this.byId.get(accountId);
|
|
53629
|
+
}
|
|
53630
|
+
status() {
|
|
53631
|
+
const now = Date.now();
|
|
53632
|
+
return this.accounts.map((acc) => {
|
|
53633
|
+
const cooling = this.coolingUntil.get(acc.id) ?? 0;
|
|
53634
|
+
return {
|
|
53635
|
+
id: acc.id,
|
|
53636
|
+
displayName: acc.displayName ?? acc.id,
|
|
53637
|
+
activeSessions: this.activeCounts.get(acc.id) ?? 0,
|
|
53638
|
+
coolingUntil: cooling > now ? cooling : null
|
|
53639
|
+
};
|
|
53640
|
+
});
|
|
53641
|
+
}
|
|
53642
|
+
incrementActive(accountId) {
|
|
53643
|
+
this.activeCounts.set(accountId, (this.activeCounts.get(accountId) ?? 0) + 1);
|
|
53644
|
+
}
|
|
53645
|
+
}
|
|
53646
|
+
|
|
53549
53647
|
// src/session/manager.ts
|
|
53550
53648
|
init_emoji();
|
|
53551
53649
|
|
|
@@ -53560,7 +53658,7 @@ init_logger();
|
|
|
53560
53658
|
import { existsSync as existsSync6, mkdirSync as mkdirSync3, appendFileSync, readdirSync, statSync, unlinkSync, rmdirSync, readFileSync as readFileSync5, chmodSync as chmodSync3 } from "fs";
|
|
53561
53659
|
import { homedir as homedir3 } from "os";
|
|
53562
53660
|
import { join as join4, dirname as dirname4 } from "path";
|
|
53563
|
-
var
|
|
53661
|
+
var log7 = createLogger("thread-log");
|
|
53564
53662
|
var LOGS_BASE_DIR = join4(homedir3(), ".claude-threads", "logs");
|
|
53565
53663
|
|
|
53566
53664
|
class ThreadLoggerImpl {
|
|
@@ -53590,7 +53688,7 @@ class ThreadLoggerImpl {
|
|
|
53590
53688
|
this.flushTimer = setInterval(() => {
|
|
53591
53689
|
this.flushSync();
|
|
53592
53690
|
}, this.flushIntervalMs);
|
|
53593
|
-
|
|
53691
|
+
log7.debug(`Thread logger initialized: ${this.logPath}`);
|
|
53594
53692
|
}
|
|
53595
53693
|
}
|
|
53596
53694
|
isEnabled() {
|
|
@@ -53704,7 +53802,7 @@ class ThreadLoggerImpl {
|
|
|
53704
53802
|
this.flushTimer = null;
|
|
53705
53803
|
}
|
|
53706
53804
|
this.flushSync();
|
|
53707
|
-
|
|
53805
|
+
log7.debug(`Thread logger closed: ${this.logPath}`);
|
|
53708
53806
|
}
|
|
53709
53807
|
addEntry(entry) {
|
|
53710
53808
|
this.buffer.push(entry);
|
|
@@ -53726,7 +53824,7 @@ class ThreadLoggerImpl {
|
|
|
53726
53824
|
}
|
|
53727
53825
|
this.buffer = [];
|
|
53728
53826
|
} catch (err) {
|
|
53729
|
-
|
|
53827
|
+
log7.error(`Failed to flush thread log: ${err}`);
|
|
53730
53828
|
}
|
|
53731
53829
|
}
|
|
53732
53830
|
}
|
|
@@ -53777,25 +53875,25 @@ function cleanupOldLogs(retentionDays = 30) {
|
|
|
53777
53875
|
if (fileStat.mtimeMs < cutoffMs) {
|
|
53778
53876
|
unlinkSync(filePath);
|
|
53779
53877
|
deletedCount++;
|
|
53780
|
-
|
|
53878
|
+
log7.debug(`Deleted old log file: ${filePath}`);
|
|
53781
53879
|
}
|
|
53782
53880
|
} catch (err) {
|
|
53783
|
-
|
|
53881
|
+
log7.warn(`Failed to check/delete log file ${filePath}: ${err}`);
|
|
53784
53882
|
}
|
|
53785
53883
|
}
|
|
53786
53884
|
try {
|
|
53787
53885
|
const remaining = readdirSync(platformDir);
|
|
53788
53886
|
if (remaining.length === 0) {
|
|
53789
53887
|
rmdirSync(platformDir);
|
|
53790
|
-
|
|
53888
|
+
log7.debug(`Removed empty platform log directory: ${platformDir}`);
|
|
53791
53889
|
}
|
|
53792
53890
|
} catch {}
|
|
53793
53891
|
}
|
|
53794
53892
|
if (deletedCount > 0) {
|
|
53795
|
-
|
|
53893
|
+
log7.info(`Cleaned up ${deletedCount} old log file(s)`);
|
|
53796
53894
|
}
|
|
53797
53895
|
} catch (err) {
|
|
53798
|
-
|
|
53896
|
+
log7.error(`Failed to clean up old logs: ${err}`);
|
|
53799
53897
|
}
|
|
53800
53898
|
return deletedCount;
|
|
53801
53899
|
}
|
|
@@ -53804,16 +53902,16 @@ function getLogFilePath(platformId, sessionId) {
|
|
|
53804
53902
|
}
|
|
53805
53903
|
function readRecentLogEntries(platformId, sessionId, maxLines = 50) {
|
|
53806
53904
|
const logPath = getLogFilePath(platformId, sessionId);
|
|
53807
|
-
|
|
53905
|
+
log7.debug(`Reading log entries from: ${logPath}`);
|
|
53808
53906
|
if (!existsSync6(logPath)) {
|
|
53809
|
-
|
|
53907
|
+
log7.debug(`Log file does not exist: ${logPath}`);
|
|
53810
53908
|
return [];
|
|
53811
53909
|
}
|
|
53812
53910
|
try {
|
|
53813
53911
|
const content = readFileSync5(logPath, "utf8");
|
|
53814
53912
|
const lines = content.trim().split(`
|
|
53815
53913
|
`);
|
|
53816
|
-
|
|
53914
|
+
log7.debug(`Log file has ${lines.length} lines`);
|
|
53817
53915
|
const recentLines = lines.slice(-maxLines);
|
|
53818
53916
|
const entries = [];
|
|
53819
53917
|
for (const line of recentLines) {
|
|
@@ -53823,17 +53921,17 @@ function readRecentLogEntries(platformId, sessionId, maxLines = 50) {
|
|
|
53823
53921
|
entries.push(JSON.parse(line));
|
|
53824
53922
|
} catch {}
|
|
53825
53923
|
}
|
|
53826
|
-
|
|
53924
|
+
log7.debug(`Parsed ${entries.length} log entries`);
|
|
53827
53925
|
return entries;
|
|
53828
53926
|
} catch (err) {
|
|
53829
|
-
|
|
53927
|
+
log7.error(`Failed to read log file: ${err}`);
|
|
53830
53928
|
return [];
|
|
53831
53929
|
}
|
|
53832
53930
|
}
|
|
53833
53931
|
|
|
53834
53932
|
// src/cleanup/scheduler.ts
|
|
53835
53933
|
init_worktree();
|
|
53836
|
-
var
|
|
53934
|
+
var log9 = createLogger("cleanup");
|
|
53837
53935
|
var DEFAULT_CLEANUP_INTERVAL_MS = 60 * 60 * 1000;
|
|
53838
53936
|
var MAX_WORKTREE_AGE_MS = 24 * 60 * 60 * 1000;
|
|
53839
53937
|
|
|
@@ -53856,17 +53954,17 @@ class CleanupScheduler {
|
|
|
53856
53954
|
}
|
|
53857
53955
|
start() {
|
|
53858
53956
|
if (this.isRunning) {
|
|
53859
|
-
|
|
53957
|
+
log9.debug("Cleanup scheduler already running");
|
|
53860
53958
|
return;
|
|
53861
53959
|
}
|
|
53862
53960
|
this.isRunning = true;
|
|
53863
|
-
|
|
53961
|
+
log9.info(`Cleanup scheduler started (interval: ${Math.round(this.intervalMs / 60000)}min)`);
|
|
53864
53962
|
this.runCleanup().catch((err) => {
|
|
53865
|
-
|
|
53963
|
+
log9.warn(`Initial cleanup failed: ${err}`);
|
|
53866
53964
|
});
|
|
53867
53965
|
this.timer = setInterval(() => {
|
|
53868
53966
|
this.runCleanup().catch((err) => {
|
|
53869
|
-
|
|
53967
|
+
log9.warn(`Periodic cleanup failed: ${err}`);
|
|
53870
53968
|
});
|
|
53871
53969
|
}, this.intervalMs);
|
|
53872
53970
|
}
|
|
@@ -53876,11 +53974,11 @@ class CleanupScheduler {
|
|
|
53876
53974
|
this.timer = null;
|
|
53877
53975
|
}
|
|
53878
53976
|
this.isRunning = false;
|
|
53879
|
-
|
|
53977
|
+
log9.debug("Cleanup scheduler stopped");
|
|
53880
53978
|
}
|
|
53881
53979
|
async runCleanup() {
|
|
53882
53980
|
const startTime = Date.now();
|
|
53883
|
-
|
|
53981
|
+
log9.debug("Running background cleanup...");
|
|
53884
53982
|
const stats = {
|
|
53885
53983
|
logsDeleted: 0,
|
|
53886
53984
|
worktreesCleaned: 0,
|
|
@@ -53906,9 +54004,9 @@ class CleanupScheduler {
|
|
|
53906
54004
|
const elapsed = Date.now() - startTime;
|
|
53907
54005
|
const totalCleaned = stats.logsDeleted + stats.worktreesCleaned + stats.metadataCleaned;
|
|
53908
54006
|
if (totalCleaned > 0 || stats.errors.length > 0) {
|
|
53909
|
-
|
|
54007
|
+
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
54008
|
} else {
|
|
53911
|
-
|
|
54009
|
+
log9.debug(`Cleanup completed in ${elapsed}ms (nothing to clean)`);
|
|
53912
54010
|
}
|
|
53913
54011
|
return stats;
|
|
53914
54012
|
}
|
|
@@ -53921,7 +54019,7 @@ class CleanupScheduler {
|
|
|
53921
54019
|
const deleted = cleanupOldLogs(this.logRetentionDays);
|
|
53922
54020
|
resolve3(deleted);
|
|
53923
54021
|
} catch (err) {
|
|
53924
|
-
|
|
54022
|
+
log9.warn(`Log cleanup error: ${err}`);
|
|
53925
54023
|
resolve3(0);
|
|
53926
54024
|
}
|
|
53927
54025
|
});
|
|
@@ -53930,7 +54028,7 @@ class CleanupScheduler {
|
|
|
53930
54028
|
const worktreesDir = getWorktreesDir();
|
|
53931
54029
|
const result = { cleaned: 0, metadata: 0 };
|
|
53932
54030
|
if (!existsSync7(worktreesDir)) {
|
|
53933
|
-
|
|
54031
|
+
log9.debug("No worktrees directory exists, nothing to clean");
|
|
53934
54032
|
return result;
|
|
53935
54033
|
}
|
|
53936
54034
|
const persisted = this.sessionStore.load();
|
|
@@ -53948,7 +54046,7 @@ class CleanupScheduler {
|
|
|
53948
54046
|
continue;
|
|
53949
54047
|
const worktreePath = join6(worktreesDir, entry.name);
|
|
53950
54048
|
if (activeWorktrees.has(worktreePath)) {
|
|
53951
|
-
|
|
54049
|
+
log9.debug(`Worktree in use by persisted session, skipping: ${entry.name}`);
|
|
53952
54050
|
continue;
|
|
53953
54051
|
}
|
|
53954
54052
|
const meta = await readWorktreeMetadata(worktreePath);
|
|
@@ -53958,7 +54056,7 @@ class CleanupScheduler {
|
|
|
53958
54056
|
const lastActivity = new Date(meta.lastActivityAt).getTime();
|
|
53959
54057
|
const age = now - lastActivity;
|
|
53960
54058
|
if (meta.sessionId && age < this.maxWorktreeAgeMs) {
|
|
53961
|
-
|
|
54059
|
+
log9.debug(`Worktree has active session (${Math.round(age / 60000)}min old), skipping: ${entry.name}`);
|
|
53962
54060
|
continue;
|
|
53963
54061
|
}
|
|
53964
54062
|
const merged = age >= this.maxWorktreeAgeMs ? await isBranchMerged(meta.repoRoot, meta.branch).catch(() => false) : false;
|
|
@@ -53969,7 +54067,7 @@ class CleanupScheduler {
|
|
|
53969
54067
|
shouldCleanup = true;
|
|
53970
54068
|
cleanupReason = `inactive for ${Math.round(age / 3600000)}h`;
|
|
53971
54069
|
} else {
|
|
53972
|
-
|
|
54070
|
+
log9.debug(`Worktree recent (${Math.round(age / 60000)}min old), skipping: ${entry.name}`);
|
|
53973
54071
|
continue;
|
|
53974
54072
|
}
|
|
53975
54073
|
} else {
|
|
@@ -53978,7 +54076,7 @@ class CleanupScheduler {
|
|
|
53978
54076
|
}
|
|
53979
54077
|
if (!shouldCleanup)
|
|
53980
54078
|
continue;
|
|
53981
|
-
|
|
54079
|
+
log9.info(`Cleaning worktree (${cleanupReason}): ${entry.name}`);
|
|
53982
54080
|
try {
|
|
53983
54081
|
if (meta?.repoRoot) {
|
|
53984
54082
|
await removeWorktree(meta.repoRoot, worktreePath);
|
|
@@ -53989,19 +54087,19 @@ class CleanupScheduler {
|
|
|
53989
54087
|
await removeWorktreeMetadata(worktreePath);
|
|
53990
54088
|
result.metadata++;
|
|
53991
54089
|
} catch (err) {
|
|
53992
|
-
|
|
54090
|
+
log9.warn(`Failed to clean orphaned worktree ${entry.name}: ${err}`);
|
|
53993
54091
|
try {
|
|
53994
54092
|
await rm(worktreePath, { recursive: true, force: true });
|
|
53995
54093
|
result.cleaned++;
|
|
53996
54094
|
await removeWorktreeMetadata(worktreePath);
|
|
53997
54095
|
result.metadata++;
|
|
53998
54096
|
} catch (rmErr) {
|
|
53999
|
-
|
|
54097
|
+
log9.error(`Failed to force remove worktree ${entry.name}: ${rmErr}`);
|
|
54000
54098
|
}
|
|
54001
54099
|
}
|
|
54002
54100
|
}
|
|
54003
54101
|
} catch (err) {
|
|
54004
|
-
|
|
54102
|
+
log9.warn(`Failed to scan worktrees directory: ${err}`);
|
|
54005
54103
|
}
|
|
54006
54104
|
return result;
|
|
54007
54105
|
}
|
|
@@ -54078,7 +54176,70 @@ import { fileURLToPath as fileURLToPath3 } from "url";
|
|
|
54078
54176
|
import { existsSync as existsSync8, readFileSync as readFileSync6, watchFile, unwatchFile, unlinkSync as unlinkSync2, statSync as statSync2, readdirSync as readdirSync2 } from "fs";
|
|
54079
54177
|
import { tmpdir } from "os";
|
|
54080
54178
|
import { join as join7 } from "path";
|
|
54081
|
-
|
|
54179
|
+
|
|
54180
|
+
// src/claude/rate-limit-detector.ts
|
|
54181
|
+
var RATE_LIMIT_PHRASES = [
|
|
54182
|
+
/usage limit reached/i,
|
|
54183
|
+
/rate[_\s-]?limit[_\s-]?error/i,
|
|
54184
|
+
/you have hit the rate limit/i,
|
|
54185
|
+
/quota (has been )?exceeded/i,
|
|
54186
|
+
/\b429\b.*(rate|limit|quota)/i
|
|
54187
|
+
];
|
|
54188
|
+
var DEFAULT_COOLDOWN_MS = 60 * 60 * 1000;
|
|
54189
|
+
function detectRateLimit(text, now = Date.now()) {
|
|
54190
|
+
if (!text)
|
|
54191
|
+
return { detected: false };
|
|
54192
|
+
let matched;
|
|
54193
|
+
for (const phrase of RATE_LIMIT_PHRASES) {
|
|
54194
|
+
const m = text.match(phrase);
|
|
54195
|
+
if (m) {
|
|
54196
|
+
matched = m[0];
|
|
54197
|
+
break;
|
|
54198
|
+
}
|
|
54199
|
+
}
|
|
54200
|
+
if (!matched)
|
|
54201
|
+
return { detected: false };
|
|
54202
|
+
const resetAtEpochMs = extractResetAt(text, now);
|
|
54203
|
+
return { detected: true, matched, resetAtEpochMs };
|
|
54204
|
+
}
|
|
54205
|
+
function cooldownDeadline(hit, now = Date.now()) {
|
|
54206
|
+
if (!hit.detected)
|
|
54207
|
+
return now;
|
|
54208
|
+
return hit.resetAtEpochMs ?? now + DEFAULT_COOLDOWN_MS;
|
|
54209
|
+
}
|
|
54210
|
+
function extractResetAt(text, now) {
|
|
54211
|
+
const relative = text.match(/(?:retry[_\s-]?after|resets?\s+in)\s+(\d+)\s*(second|minute|hour|day)s?/i);
|
|
54212
|
+
if (relative) {
|
|
54213
|
+
const value = parseInt(relative[1], 10);
|
|
54214
|
+
const unit = relative[2].toLowerCase();
|
|
54215
|
+
const unitMs = {
|
|
54216
|
+
second: 1000,
|
|
54217
|
+
minute: 60000,
|
|
54218
|
+
hour: 3600000,
|
|
54219
|
+
day: 86400000
|
|
54220
|
+
};
|
|
54221
|
+
return now + value * unitMs[unit];
|
|
54222
|
+
}
|
|
54223
|
+
const unix = text.match(/\breset(?:_at)?\b\s*["']?\s*[:=]\s*(\d{10,13})/);
|
|
54224
|
+
if (unix) {
|
|
54225
|
+
const raw = parseInt(unix[1], 10);
|
|
54226
|
+
return unix[1].length === 13 ? raw : raw * 1000;
|
|
54227
|
+
}
|
|
54228
|
+
const clock = text.match(/resets?\s+at\s+(\d{1,2}):(\d{2})\s*(utc|gmt)?/i);
|
|
54229
|
+
if (clock) {
|
|
54230
|
+
const hh = parseInt(clock[1], 10);
|
|
54231
|
+
const mm = parseInt(clock[2], 10);
|
|
54232
|
+
if (hh < 24 && mm < 60) {
|
|
54233
|
+
const reference = new Date(now);
|
|
54234
|
+
const target = new Date(Date.UTC(reference.getUTCFullYear(), reference.getUTCMonth(), reference.getUTCDate(), hh, mm)).getTime();
|
|
54235
|
+
return target > now ? target : target + 86400000;
|
|
54236
|
+
}
|
|
54237
|
+
}
|
|
54238
|
+
return;
|
|
54239
|
+
}
|
|
54240
|
+
|
|
54241
|
+
// src/claude/cli.ts
|
|
54242
|
+
var log10 = createLogger("claude");
|
|
54082
54243
|
function cleanupBrowserBridgeSockets() {
|
|
54083
54244
|
try {
|
|
54084
54245
|
const tempDir = tmpdir();
|
|
@@ -54090,15 +54251,23 @@ function cleanupBrowserBridgeSockets() {
|
|
|
54090
54251
|
const stats = statSync2(filePath);
|
|
54091
54252
|
if (stats.isSocket()) {
|
|
54092
54253
|
unlinkSync2(filePath);
|
|
54093
|
-
|
|
54254
|
+
log10.debug(`Removed stale browser bridge socket: ${file}`);
|
|
54094
54255
|
}
|
|
54095
54256
|
} catch {}
|
|
54096
54257
|
}
|
|
54097
54258
|
}
|
|
54098
54259
|
} catch (err) {
|
|
54099
|
-
|
|
54260
|
+
log10.debug(`Browser bridge cleanup failed: ${err}`);
|
|
54100
54261
|
}
|
|
54101
54262
|
}
|
|
54263
|
+
function isErrorResultEvent(event) {
|
|
54264
|
+
const ev = event;
|
|
54265
|
+
if (typeof ev.subtype === "string" && ev.subtype.startsWith("error"))
|
|
54266
|
+
return true;
|
|
54267
|
+
if (ev.is_error === true)
|
|
54268
|
+
return true;
|
|
54269
|
+
return false;
|
|
54270
|
+
}
|
|
54102
54271
|
|
|
54103
54272
|
class ClaudeCli extends EventEmitter2 {
|
|
54104
54273
|
process = null;
|
|
@@ -54108,6 +54277,7 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
54108
54277
|
statusFilePath = null;
|
|
54109
54278
|
lastStatusData = null;
|
|
54110
54279
|
stderrBuffer = "";
|
|
54280
|
+
lastEmittedRateLimitDeadline = 0;
|
|
54111
54281
|
log;
|
|
54112
54282
|
constructor(options2) {
|
|
54113
54283
|
super();
|
|
@@ -54159,6 +54329,7 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
54159
54329
|
if (this.process)
|
|
54160
54330
|
throw new Error("Already running");
|
|
54161
54331
|
this.stderrBuffer = "";
|
|
54332
|
+
this.lastEmittedRateLimitDeadline = 0;
|
|
54162
54333
|
cleanupBrowserBridgeSockets();
|
|
54163
54334
|
const claudePath = getClaudePath();
|
|
54164
54335
|
const args = [
|
|
@@ -54228,9 +54399,13 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
54228
54399
|
args.push("--settings", JSON.stringify(statusLineSettings));
|
|
54229
54400
|
}
|
|
54230
54401
|
this.log.debug(`Starting: ${claudePath} ${args.slice(0, 5).join(" ")}...`);
|
|
54402
|
+
const childEnv = this.buildChildEnv();
|
|
54403
|
+
if (this.options.account) {
|
|
54404
|
+
this.log.debug(`Spawning under Claude account "${this.options.account.id}"`);
|
|
54405
|
+
}
|
|
54231
54406
|
this.process = crossSpawn(claudePath, args, {
|
|
54232
54407
|
cwd: this.options.workingDir,
|
|
54233
|
-
env:
|
|
54408
|
+
env: childEnv,
|
|
54234
54409
|
stdio: ["pipe", "pipe", "pipe"]
|
|
54235
54410
|
});
|
|
54236
54411
|
this.log.debug(`Claude process spawned: pid=${this.process.pid}`);
|
|
@@ -54244,6 +54419,7 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
54244
54419
|
this.stderrBuffer = this.stderrBuffer.slice(-10240);
|
|
54245
54420
|
}
|
|
54246
54421
|
this.log.debug(`stderr: ${text.trim()}`);
|
|
54422
|
+
this.maybeEmitRateLimit(text);
|
|
54247
54423
|
});
|
|
54248
54424
|
this.process.on("error", (err) => {
|
|
54249
54425
|
this.log.error(`Claude error: ${err}`);
|
|
@@ -54298,9 +54474,24 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
54298
54474
|
try {
|
|
54299
54475
|
const event = JSON.parse(trimmed);
|
|
54300
54476
|
this.emit("event", event);
|
|
54477
|
+
if (event.type === "result" && isErrorResultEvent(event)) {
|
|
54478
|
+
this.maybeEmitRateLimit(trimmed);
|
|
54479
|
+
}
|
|
54301
54480
|
} catch {}
|
|
54302
54481
|
}
|
|
54303
54482
|
}
|
|
54483
|
+
maybeEmitRateLimit(text) {
|
|
54484
|
+
const hit = detectRateLimit(text);
|
|
54485
|
+
if (!hit.detected)
|
|
54486
|
+
return;
|
|
54487
|
+
const newDeadline = cooldownDeadline(hit);
|
|
54488
|
+
const MIN_ADVANCE_MS = 60000;
|
|
54489
|
+
if (newDeadline - this.lastEmittedRateLimitDeadline < MIN_ADVANCE_MS)
|
|
54490
|
+
return;
|
|
54491
|
+
this.lastEmittedRateLimitDeadline = newDeadline;
|
|
54492
|
+
this.log.warn(`Rate limit detected: ${hit.matched ?? "(no match text)"}`);
|
|
54493
|
+
this.emit("rate-limit", hit);
|
|
54494
|
+
}
|
|
54304
54495
|
isRunning() {
|
|
54305
54496
|
return this.process !== null;
|
|
54306
54497
|
}
|
|
@@ -54369,6 +54560,22 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
54369
54560
|
this.process.kill("SIGINT");
|
|
54370
54561
|
return true;
|
|
54371
54562
|
}
|
|
54563
|
+
buildChildEnv() {
|
|
54564
|
+
const account = this.options.account;
|
|
54565
|
+
if (!account)
|
|
54566
|
+
return process.env;
|
|
54567
|
+
const env = { ...process.env };
|
|
54568
|
+
if (account.home) {
|
|
54569
|
+
env.HOME = account.home;
|
|
54570
|
+
env.USERPROFILE = account.home;
|
|
54571
|
+
delete env.ANTHROPIC_API_KEY;
|
|
54572
|
+
delete env.CLAUDE_CODE_OAUTH_TOKEN;
|
|
54573
|
+
} else if (account.apiKey) {
|
|
54574
|
+
env.ANTHROPIC_API_KEY = account.apiKey;
|
|
54575
|
+
delete env.CLAUDE_CODE_OAUTH_TOKEN;
|
|
54576
|
+
}
|
|
54577
|
+
return env;
|
|
54578
|
+
}
|
|
54372
54579
|
getMcpServerPath() {
|
|
54373
54580
|
const __filename2 = fileURLToPath3(import.meta.url);
|
|
54374
54581
|
const __dirname4 = dirname6(__filename2);
|
|
@@ -55205,7 +55412,7 @@ import { existsSync as existsSync11 } from "fs";
|
|
|
55205
55412
|
// src/utils/keep-alive.ts
|
|
55206
55413
|
init_logger();
|
|
55207
55414
|
import { spawn as spawn2 } from "child_process";
|
|
55208
|
-
var
|
|
55415
|
+
var log11 = createLogger("keepalive");
|
|
55209
55416
|
|
|
55210
55417
|
class KeepAliveManager {
|
|
55211
55418
|
activeSessionCount = 0;
|
|
@@ -55220,7 +55427,7 @@ class KeepAliveManager {
|
|
|
55220
55427
|
if (!enabled && this.keepAliveProcess) {
|
|
55221
55428
|
this.stopKeepAlive();
|
|
55222
55429
|
}
|
|
55223
|
-
|
|
55430
|
+
log11.debug(`Keep-alive ${enabled ? "enabled" : "disabled"}`);
|
|
55224
55431
|
}
|
|
55225
55432
|
isEnabled() {
|
|
55226
55433
|
return this.enabled;
|
|
@@ -55230,7 +55437,7 @@ class KeepAliveManager {
|
|
|
55230
55437
|
}
|
|
55231
55438
|
sessionStarted() {
|
|
55232
55439
|
this.activeSessionCount++;
|
|
55233
|
-
|
|
55440
|
+
log11.debug(`Session started (${this.activeSessionCount} active)`);
|
|
55234
55441
|
if (this.activeSessionCount === 1) {
|
|
55235
55442
|
this.startKeepAlive();
|
|
55236
55443
|
}
|
|
@@ -55239,7 +55446,7 @@ class KeepAliveManager {
|
|
|
55239
55446
|
if (this.activeSessionCount > 0) {
|
|
55240
55447
|
this.activeSessionCount--;
|
|
55241
55448
|
}
|
|
55242
|
-
|
|
55449
|
+
log11.debug(`Session ended (${this.activeSessionCount} active)`);
|
|
55243
55450
|
if (this.activeSessionCount === 0) {
|
|
55244
55451
|
this.stopKeepAlive();
|
|
55245
55452
|
}
|
|
@@ -55253,11 +55460,11 @@ class KeepAliveManager {
|
|
|
55253
55460
|
}
|
|
55254
55461
|
startKeepAlive() {
|
|
55255
55462
|
if (!this.enabled) {
|
|
55256
|
-
|
|
55463
|
+
log11.debug("Keep-alive disabled, skipping");
|
|
55257
55464
|
return;
|
|
55258
55465
|
}
|
|
55259
55466
|
if (this.keepAliveProcess) {
|
|
55260
|
-
|
|
55467
|
+
log11.debug("Keep-alive already running");
|
|
55261
55468
|
return;
|
|
55262
55469
|
}
|
|
55263
55470
|
switch (this.platform) {
|
|
@@ -55271,12 +55478,12 @@ class KeepAliveManager {
|
|
|
55271
55478
|
this.startWindowsKeepAlive();
|
|
55272
55479
|
break;
|
|
55273
55480
|
default:
|
|
55274
|
-
|
|
55481
|
+
log11.warn(`Keep-alive not supported on ${this.platform}`);
|
|
55275
55482
|
}
|
|
55276
55483
|
}
|
|
55277
55484
|
stopKeepAlive() {
|
|
55278
55485
|
if (this.keepAliveProcess) {
|
|
55279
|
-
|
|
55486
|
+
log11.debug("Stopping keep-alive");
|
|
55280
55487
|
this.keepAliveProcess.kill();
|
|
55281
55488
|
this.keepAliveProcess = null;
|
|
55282
55489
|
}
|
|
@@ -55288,18 +55495,18 @@ class KeepAliveManager {
|
|
|
55288
55495
|
detached: false
|
|
55289
55496
|
});
|
|
55290
55497
|
this.keepAliveProcess.on("error", (err) => {
|
|
55291
|
-
|
|
55498
|
+
log11.error(`Failed to start caffeinate: ${err.message}`);
|
|
55292
55499
|
this.keepAliveProcess = null;
|
|
55293
55500
|
});
|
|
55294
55501
|
this.keepAliveProcess.on("exit", (code) => {
|
|
55295
55502
|
if (code !== null && code !== 0 && this.activeSessionCount > 0) {
|
|
55296
|
-
|
|
55503
|
+
log11.debug(`caffeinate exited with code ${code}`);
|
|
55297
55504
|
}
|
|
55298
55505
|
this.keepAliveProcess = null;
|
|
55299
55506
|
});
|
|
55300
|
-
|
|
55507
|
+
log11.info("Sleep prevention active (caffeinate)");
|
|
55301
55508
|
} catch (err) {
|
|
55302
|
-
|
|
55509
|
+
log11.error(`Failed to start caffeinate: ${err}`);
|
|
55303
55510
|
}
|
|
55304
55511
|
}
|
|
55305
55512
|
startLinuxKeepAlive() {
|
|
@@ -55315,19 +55522,19 @@ class KeepAliveManager {
|
|
|
55315
55522
|
detached: false
|
|
55316
55523
|
});
|
|
55317
55524
|
this.keepAliveProcess.on("error", (err) => {
|
|
55318
|
-
|
|
55525
|
+
log11.debug(`systemd-inhibit not available: ${err.message}`);
|
|
55319
55526
|
this.keepAliveProcess = null;
|
|
55320
55527
|
this.startLinuxKeepAliveFallback();
|
|
55321
55528
|
});
|
|
55322
55529
|
this.keepAliveProcess.on("exit", (code) => {
|
|
55323
55530
|
if (code !== null && code !== 0 && this.activeSessionCount > 0) {
|
|
55324
|
-
|
|
55531
|
+
log11.debug(`systemd-inhibit exited with code ${code}`);
|
|
55325
55532
|
}
|
|
55326
55533
|
this.keepAliveProcess = null;
|
|
55327
55534
|
});
|
|
55328
|
-
|
|
55535
|
+
log11.info("Sleep prevention active (systemd-inhibit)");
|
|
55329
55536
|
} catch (err) {
|
|
55330
|
-
|
|
55537
|
+
log11.debug(`Failed to start systemd-inhibit: ${err}`);
|
|
55331
55538
|
this.startLinuxKeepAliveFallback();
|
|
55332
55539
|
}
|
|
55333
55540
|
}
|
|
@@ -55341,15 +55548,15 @@ class KeepAliveManager {
|
|
|
55341
55548
|
detached: false
|
|
55342
55549
|
});
|
|
55343
55550
|
this.keepAliveProcess.on("error", (err) => {
|
|
55344
|
-
|
|
55551
|
+
log11.warn(`Linux keep-alive fallback not available: ${err.message}`);
|
|
55345
55552
|
this.keepAliveProcess = null;
|
|
55346
55553
|
});
|
|
55347
55554
|
this.keepAliveProcess.on("exit", () => {
|
|
55348
55555
|
this.keepAliveProcess = null;
|
|
55349
55556
|
});
|
|
55350
|
-
|
|
55557
|
+
log11.info("Sleep prevention active (xdg-screensaver)");
|
|
55351
55558
|
} catch (err) {
|
|
55352
|
-
|
|
55559
|
+
log11.warn(`Linux keep-alive not available: ${err}`);
|
|
55353
55560
|
}
|
|
55354
55561
|
}
|
|
55355
55562
|
startWindowsKeepAlive() {
|
|
@@ -55374,18 +55581,18 @@ class KeepAliveManager {
|
|
|
55374
55581
|
windowsHide: true
|
|
55375
55582
|
});
|
|
55376
55583
|
this.keepAliveProcess.on("error", (err) => {
|
|
55377
|
-
|
|
55584
|
+
log11.warn(`Windows keep-alive not available: ${err.message}`);
|
|
55378
55585
|
this.keepAliveProcess = null;
|
|
55379
55586
|
});
|
|
55380
55587
|
this.keepAliveProcess.on("exit", (code) => {
|
|
55381
55588
|
if (code !== null && code !== 0 && this.activeSessionCount > 0) {
|
|
55382
|
-
|
|
55589
|
+
log11.debug(`PowerShell keep-alive exited with code ${code}`);
|
|
55383
55590
|
}
|
|
55384
55591
|
this.keepAliveProcess = null;
|
|
55385
55592
|
});
|
|
55386
|
-
|
|
55593
|
+
log11.info("Sleep prevention active (SetThreadExecutionState)");
|
|
55387
55594
|
} catch (err) {
|
|
55388
|
-
|
|
55595
|
+
log11.warn(`Windows keep-alive not available: ${err}`);
|
|
55389
55596
|
}
|
|
55390
55597
|
}
|
|
55391
55598
|
}
|
|
@@ -55393,7 +55600,7 @@ var keepAlive = new KeepAliveManager;
|
|
|
55393
55600
|
|
|
55394
55601
|
// src/utils/error-handler/index.ts
|
|
55395
55602
|
init_logger();
|
|
55396
|
-
var
|
|
55603
|
+
var log12 = createLogger("error");
|
|
55397
55604
|
|
|
55398
55605
|
class SessionError extends Error {
|
|
55399
55606
|
sessionId;
|
|
@@ -55419,19 +55626,19 @@ async function handleError(error, context, severity = "recoverable") {
|
|
|
55419
55626
|
const sessionPart = sessionId ? ` (${formatShortId(sessionId)})` : "";
|
|
55420
55627
|
const logMessage = `${context.action}${sessionPart}: ${message}`;
|
|
55421
55628
|
if (severity === "recoverable") {
|
|
55422
|
-
|
|
55629
|
+
log12.warn(logMessage);
|
|
55423
55630
|
} else {
|
|
55424
|
-
|
|
55631
|
+
log12.error(logMessage, error instanceof Error ? error : undefined);
|
|
55425
55632
|
}
|
|
55426
55633
|
if (context.details) {
|
|
55427
|
-
|
|
55634
|
+
log12.debugJson("Error details", context.details);
|
|
55428
55635
|
}
|
|
55429
55636
|
if (context.notifyUser && context.session) {
|
|
55430
55637
|
try {
|
|
55431
55638
|
const fmt = context.session.platform.getFormatter();
|
|
55432
55639
|
await context.session.platform.createPost(`⚠️ ${fmt.formatBold("Error")}: ${context.action} failed - ${message}`, context.session.threadId);
|
|
55433
55640
|
} catch (notifyError) {
|
|
55434
|
-
|
|
55641
|
+
log12.warn(`Could not notify user: ${notifyError}`);
|
|
55435
55642
|
}
|
|
55436
55643
|
}
|
|
55437
55644
|
if (severity === "session-fatal" || severity === "system-fatal") {
|
|
@@ -55458,7 +55665,7 @@ async function logAndNotify(error, context) {
|
|
|
55458
55665
|
}
|
|
55459
55666
|
function logSilentError(context, error) {
|
|
55460
55667
|
const message = error instanceof Error ? error.message : String(error);
|
|
55461
|
-
|
|
55668
|
+
log12.debug(`[${context}] Silently caught: ${message}`);
|
|
55462
55669
|
}
|
|
55463
55670
|
|
|
55464
55671
|
// src/session/lifecycle.ts
|
|
@@ -55478,8 +55685,8 @@ function createSessionLog(baseLog) {
|
|
|
55478
55685
|
init_logger();
|
|
55479
55686
|
init_emoji();
|
|
55480
55687
|
init_worktree();
|
|
55481
|
-
var
|
|
55482
|
-
var sessionLog = createSessionLog(
|
|
55688
|
+
var log13 = createLogger("helpers");
|
|
55689
|
+
var sessionLog = createSessionLog(log13);
|
|
55483
55690
|
var POST_TYPES = {
|
|
55484
55691
|
info: "",
|
|
55485
55692
|
success: "✅",
|
|
@@ -55563,7 +55770,7 @@ function updateLastMessage(session, post2) {
|
|
|
55563
55770
|
// src/claude/quick-query.ts
|
|
55564
55771
|
init_spawn();
|
|
55565
55772
|
init_logger();
|
|
55566
|
-
var
|
|
55773
|
+
var log14 = createLogger("query");
|
|
55567
55774
|
async function quickQuery(options2) {
|
|
55568
55775
|
const {
|
|
55569
55776
|
prompt,
|
|
@@ -55579,7 +55786,7 @@ async function quickQuery(options2) {
|
|
|
55579
55786
|
args.push("--system-prompt", systemPrompt);
|
|
55580
55787
|
}
|
|
55581
55788
|
args.push(prompt);
|
|
55582
|
-
|
|
55789
|
+
log14.debug(`Quick query: model=${model}, timeout=${timeout}ms, prompt="${prompt.substring(0, 50)}..."`);
|
|
55583
55790
|
return new Promise((resolve5) => {
|
|
55584
55791
|
let stdout = "";
|
|
55585
55792
|
let stderr = "";
|
|
@@ -55593,7 +55800,7 @@ async function quickQuery(options2) {
|
|
|
55593
55800
|
if (!resolved) {
|
|
55594
55801
|
resolved = true;
|
|
55595
55802
|
proc.kill("SIGTERM");
|
|
55596
|
-
|
|
55803
|
+
log14.debug(`Quick query timed out after ${timeout}ms`);
|
|
55597
55804
|
resolve5({
|
|
55598
55805
|
success: false,
|
|
55599
55806
|
error: "timeout",
|
|
@@ -55611,7 +55818,7 @@ async function quickQuery(options2) {
|
|
|
55611
55818
|
if (!resolved) {
|
|
55612
55819
|
resolved = true;
|
|
55613
55820
|
clearTimeout(timeoutId);
|
|
55614
|
-
|
|
55821
|
+
log14.debug(`Quick query error: ${err.message}`);
|
|
55615
55822
|
resolve5({
|
|
55616
55823
|
success: false,
|
|
55617
55824
|
error: err.message,
|
|
@@ -55625,14 +55832,14 @@ async function quickQuery(options2) {
|
|
|
55625
55832
|
clearTimeout(timeoutId);
|
|
55626
55833
|
const durationMs = Date.now() - startTime;
|
|
55627
55834
|
if (code === 0 && stdout.trim()) {
|
|
55628
|
-
|
|
55835
|
+
log14.debug(`Quick query success: ${durationMs}ms, ${stdout.length} chars`);
|
|
55629
55836
|
resolve5({
|
|
55630
55837
|
success: true,
|
|
55631
55838
|
response: stdout.trim(),
|
|
55632
55839
|
durationMs
|
|
55633
55840
|
});
|
|
55634
55841
|
} else {
|
|
55635
|
-
|
|
55842
|
+
log14.debug(`Quick query failed: code=${code}, stderr=${stderr.substring(0, 100)}`);
|
|
55636
55843
|
resolve5({
|
|
55637
55844
|
success: false,
|
|
55638
55845
|
error: stderr || `exit code ${code}`,
|
|
@@ -55647,7 +55854,7 @@ async function quickQuery(options2) {
|
|
|
55647
55854
|
|
|
55648
55855
|
// src/operations/suggestions/title.ts
|
|
55649
55856
|
init_logger();
|
|
55650
|
-
var
|
|
55857
|
+
var log15 = createLogger("title");
|
|
55651
55858
|
var SUGGESTION_TIMEOUT = 15000;
|
|
55652
55859
|
var MIN_TITLE_LENGTH = 3;
|
|
55653
55860
|
var MAX_TITLE_LENGTH = 50;
|
|
@@ -55711,32 +55918,32 @@ function parseMetadata(response) {
|
|
|
55711
55918
|
const titleMatch = response.match(/TITLE:\s*(.+)/i);
|
|
55712
55919
|
const descMatch = response.match(/DESC:\s*(.+)/i);
|
|
55713
55920
|
if (!titleMatch || !descMatch) {
|
|
55714
|
-
|
|
55921
|
+
log15.debug("Failed to parse title/description from response");
|
|
55715
55922
|
return null;
|
|
55716
55923
|
}
|
|
55717
55924
|
let title = titleMatch[1].trim();
|
|
55718
55925
|
let description = descMatch[1].trim();
|
|
55719
55926
|
if (title.length < MIN_TITLE_LENGTH) {
|
|
55720
|
-
|
|
55927
|
+
log15.debug(`Title too short: ${title.length} chars`);
|
|
55721
55928
|
return null;
|
|
55722
55929
|
}
|
|
55723
55930
|
if (title.length > MAX_TITLE_LENGTH) {
|
|
55724
|
-
|
|
55931
|
+
log15.debug(`Title too long (${title.length} chars), truncating`);
|
|
55725
55932
|
title = truncateAtWord(title, MAX_TITLE_LENGTH);
|
|
55726
55933
|
}
|
|
55727
55934
|
if (description.length < MIN_DESC_LENGTH) {
|
|
55728
|
-
|
|
55935
|
+
log15.debug(`Description too short: ${description.length} chars`);
|
|
55729
55936
|
return null;
|
|
55730
55937
|
}
|
|
55731
55938
|
if (description.length > MAX_DESC_LENGTH) {
|
|
55732
|
-
|
|
55939
|
+
log15.debug(`Description too long (${description.length} chars), truncating`);
|
|
55733
55940
|
description = truncateAtWord(description, MAX_DESC_LENGTH);
|
|
55734
55941
|
}
|
|
55735
55942
|
return { title, description };
|
|
55736
55943
|
}
|
|
55737
55944
|
async function suggestSessionMetadata(context) {
|
|
55738
55945
|
const logContext = typeof context === "string" ? context.substring(0, 50) : context.originalTask.substring(0, 50);
|
|
55739
|
-
|
|
55946
|
+
log15.debug(`Suggesting title for: "${logContext}..."`);
|
|
55740
55947
|
try {
|
|
55741
55948
|
const result = await quickQuery({
|
|
55742
55949
|
prompt: buildTitlePrompt(context),
|
|
@@ -55744,23 +55951,23 @@ async function suggestSessionMetadata(context) {
|
|
|
55744
55951
|
timeout: SUGGESTION_TIMEOUT
|
|
55745
55952
|
});
|
|
55746
55953
|
if (!result.success || !result.response) {
|
|
55747
|
-
|
|
55954
|
+
log15.debug(`Title suggestion failed: ${result.error || "no response"}`);
|
|
55748
55955
|
return null;
|
|
55749
55956
|
}
|
|
55750
55957
|
const metadata = parseMetadata(result.response);
|
|
55751
55958
|
if (metadata) {
|
|
55752
|
-
|
|
55959
|
+
log15.debug(`Got title: "${metadata.title}" (${result.durationMs}ms)`);
|
|
55753
55960
|
}
|
|
55754
55961
|
return metadata;
|
|
55755
55962
|
} catch (err) {
|
|
55756
|
-
|
|
55963
|
+
log15.debug(`Title suggestion error: ${err}`);
|
|
55757
55964
|
return null;
|
|
55758
55965
|
}
|
|
55759
55966
|
}
|
|
55760
55967
|
|
|
55761
55968
|
// src/operations/suggestions/tag.ts
|
|
55762
55969
|
init_logger();
|
|
55763
|
-
var
|
|
55970
|
+
var log16 = createLogger("tags");
|
|
55764
55971
|
var SUGGESTION_TIMEOUT2 = 15000;
|
|
55765
55972
|
var MAX_TAGS = 3;
|
|
55766
55973
|
var VALID_TAGS = [
|
|
@@ -55792,7 +55999,7 @@ function parseTags(response) {
|
|
|
55792
55999
|
return [...new Set(tags)].slice(0, MAX_TAGS);
|
|
55793
56000
|
}
|
|
55794
56001
|
async function suggestSessionTags(userMessage) {
|
|
55795
|
-
|
|
56002
|
+
log16.debug(`Suggesting tags for: "${userMessage.substring(0, 50)}..."`);
|
|
55796
56003
|
try {
|
|
55797
56004
|
const result = await quickQuery({
|
|
55798
56005
|
prompt: buildTagPrompt(userMessage),
|
|
@@ -55800,14 +56007,14 @@ async function suggestSessionTags(userMessage) {
|
|
|
55800
56007
|
timeout: SUGGESTION_TIMEOUT2
|
|
55801
56008
|
});
|
|
55802
56009
|
if (!result.success || !result.response) {
|
|
55803
|
-
|
|
56010
|
+
log16.debug(`Tag suggestion failed: ${result.error || "no response"}`);
|
|
55804
56011
|
return [];
|
|
55805
56012
|
}
|
|
55806
56013
|
const tags = parseTags(result.response);
|
|
55807
|
-
|
|
56014
|
+
log16.debug(`Got tags: ${tags.join(", ")} (${result.durationMs}ms)`);
|
|
55808
56015
|
return tags;
|
|
55809
56016
|
} catch (err) {
|
|
55810
|
-
|
|
56017
|
+
log16.debug(`Tag suggestion error: ${err}`);
|
|
55811
56018
|
return [];
|
|
55812
56019
|
}
|
|
55813
56020
|
}
|
|
@@ -59349,7 +59556,7 @@ class BugReportExecutor extends BaseExecutor {
|
|
|
59349
59556
|
// src/operations/executors/worktree-prompt.ts
|
|
59350
59557
|
init_emoji();
|
|
59351
59558
|
init_logger();
|
|
59352
|
-
var
|
|
59559
|
+
var log17 = createLogger("wt-prompt");
|
|
59353
59560
|
// src/operations/message-manager.ts
|
|
59354
59561
|
init_logger();
|
|
59355
59562
|
|
|
@@ -59386,7 +59593,7 @@ var import_yauzl = __toESM(require_yauzl(), 1);
|
|
|
59386
59593
|
import { createGunzip } from "zlib";
|
|
59387
59594
|
import { pipeline } from "stream/promises";
|
|
59388
59595
|
import { Readable, Writable } from "stream";
|
|
59389
|
-
var
|
|
59596
|
+
var log18 = createLogger("streaming");
|
|
59390
59597
|
var MAX_PDF_SIZE = 32 * 1024 * 1024;
|
|
59391
59598
|
var MAX_TEXT_FILE_SIZE = 1 * 1024 * 1024;
|
|
59392
59599
|
var MAX_DECOMPRESSED_SIZE = 10 * 1024 * 1024;
|
|
@@ -59512,7 +59719,7 @@ async function processImageFile(file, platform, debug = false) {
|
|
|
59512
59719
|
const buffer = await platform.downloadFile(file.id);
|
|
59513
59720
|
const base64 = buffer.toString("base64");
|
|
59514
59721
|
if (debug) {
|
|
59515
|
-
|
|
59722
|
+
log18.debug(`Attached image: ${file.name} (${file.mimeType}, ${Math.round(buffer.length / 1024)}KB)`);
|
|
59516
59723
|
}
|
|
59517
59724
|
return {
|
|
59518
59725
|
block: {
|
|
@@ -59525,7 +59732,7 @@ async function processImageFile(file, platform, debug = false) {
|
|
|
59525
59732
|
}
|
|
59526
59733
|
};
|
|
59527
59734
|
} catch (err) {
|
|
59528
|
-
|
|
59735
|
+
log18.error(`Failed to download image ${file.name}: ${err}`);
|
|
59529
59736
|
return {
|
|
59530
59737
|
skipped: {
|
|
59531
59738
|
name: file.name,
|
|
@@ -59556,7 +59763,7 @@ async function processPdfFile(file, platform, debug = false) {
|
|
|
59556
59763
|
}
|
|
59557
59764
|
const base64 = buffer.toString("base64");
|
|
59558
59765
|
if (debug) {
|
|
59559
|
-
|
|
59766
|
+
log18.debug(`Attached PDF: ${file.name} (${Math.round(buffer.length / 1024)}KB)`);
|
|
59560
59767
|
}
|
|
59561
59768
|
return {
|
|
59562
59769
|
block: {
|
|
@@ -59570,7 +59777,7 @@ async function processPdfFile(file, platform, debug = false) {
|
|
|
59570
59777
|
}
|
|
59571
59778
|
};
|
|
59572
59779
|
} catch (err) {
|
|
59573
|
-
|
|
59780
|
+
log18.error(`Failed to process PDF ${file.name}: ${err}`);
|
|
59574
59781
|
return {
|
|
59575
59782
|
skipped: {
|
|
59576
59783
|
name: file.name,
|
|
@@ -59601,7 +59808,7 @@ async function processTextFile(file, platform, debug = false) {
|
|
|
59601
59808
|
}
|
|
59602
59809
|
const content = buffer.toString("utf-8");
|
|
59603
59810
|
if (debug) {
|
|
59604
|
-
|
|
59811
|
+
log18.debug(`Attached text file: ${file.name} (${Math.round(buffer.length / 1024)}KB)`);
|
|
59605
59812
|
}
|
|
59606
59813
|
const wrappedContent = formatTextFileContent(file.name, content);
|
|
59607
59814
|
return {
|
|
@@ -59611,7 +59818,7 @@ async function processTextFile(file, platform, debug = false) {
|
|
|
59611
59818
|
}
|
|
59612
59819
|
};
|
|
59613
59820
|
} catch (err) {
|
|
59614
|
-
|
|
59821
|
+
log18.error(`Failed to process text file ${file.name}: ${err}`);
|
|
59615
59822
|
return {
|
|
59616
59823
|
skipped: {
|
|
59617
59824
|
name: file.name,
|
|
@@ -59669,7 +59876,7 @@ async function processGzipFile(file, platform, debug = false) {
|
|
|
59669
59876
|
compressedBuffer = await platform.downloadFile(file.id);
|
|
59670
59877
|
} catch (err) {
|
|
59671
59878
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
59672
|
-
|
|
59879
|
+
log18.error(`Failed to download gzip file ${file.name}: ${errorMessage}`);
|
|
59673
59880
|
return {
|
|
59674
59881
|
skipped: {
|
|
59675
59882
|
name: file.name,
|
|
@@ -59679,7 +59886,7 @@ async function processGzipFile(file, platform, debug = false) {
|
|
|
59679
59886
|
};
|
|
59680
59887
|
}
|
|
59681
59888
|
if (file.size && compressedBuffer.length !== file.size) {
|
|
59682
|
-
|
|
59889
|
+
log18.warn(`Downloaded size mismatch for ${file.name}: expected ${file.size}, got ${compressedBuffer.length}`);
|
|
59683
59890
|
}
|
|
59684
59891
|
let decompressedBuffer;
|
|
59685
59892
|
try {
|
|
@@ -59715,7 +59922,7 @@ async function processGzipFile(file, platform, debug = false) {
|
|
|
59715
59922
|
const innerFilename = file.name.toLowerCase().endsWith(".gz") ? file.name.slice(0, -3) : file.name;
|
|
59716
59923
|
const contentType = detectDecompressedContentType(decompressedBuffer, innerFilename);
|
|
59717
59924
|
if (debug) {
|
|
59718
|
-
|
|
59925
|
+
log18.debug(`Decompressed ${file.name}: ${Math.round(decompressedBuffer.length / 1024)}KB, detected type: ${contentType}`);
|
|
59719
59926
|
}
|
|
59720
59927
|
if (contentType === "pdf") {
|
|
59721
59928
|
const base64 = decompressedBuffer.toString("base64");
|
|
@@ -59750,7 +59957,7 @@ async function processGzipFile(file, platform, debug = false) {
|
|
|
59750
59957
|
}
|
|
59751
59958
|
} catch (err) {
|
|
59752
59959
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
59753
|
-
|
|
59960
|
+
log18.error(`Failed to process gzip file ${file.name}: ${errorMessage}`);
|
|
59754
59961
|
return {
|
|
59755
59962
|
skipped: {
|
|
59756
59963
|
name: file.name,
|
|
@@ -59802,7 +60009,7 @@ async function processZipFile(file, platform, debug = false) {
|
|
|
59802
60009
|
}
|
|
59803
60010
|
const zipBuffer = await platform.downloadFile(file.id);
|
|
59804
60011
|
if (debug) {
|
|
59805
|
-
|
|
60012
|
+
log18.debug(`Processing zip file ${file.name}: ${Math.round(zipBuffer.length / 1024)}KB`);
|
|
59806
60013
|
}
|
|
59807
60014
|
const zipfile = await new Promise((resolve5, reject) => {
|
|
59808
60015
|
import_yauzl.default.fromBuffer(zipBuffer, { lazyEntries: true }, (err, zf) => {
|
|
@@ -59876,7 +60083,7 @@ async function processZipFile(file, platform, debug = false) {
|
|
|
59876
60083
|
const buffer = await extractZipEntry(zipfile2, entry);
|
|
59877
60084
|
const contentType = detectDecompressedContentType(buffer, entry.fileName);
|
|
59878
60085
|
if (debug) {
|
|
59879
|
-
|
|
60086
|
+
log18.debug(`Extracted ${entry.fileName}: ${Math.round(buffer.length / 1024)}KB, type: ${contentType}`);
|
|
59880
60087
|
}
|
|
59881
60088
|
if (contentType === "pdf") {
|
|
59882
60089
|
const base64 = buffer.toString("base64");
|
|
@@ -59919,11 +60126,11 @@ async function processZipFile(file, platform, debug = false) {
|
|
|
59919
60126
|
});
|
|
59920
60127
|
zipfile2.close();
|
|
59921
60128
|
if (debug) {
|
|
59922
|
-
|
|
60129
|
+
log18.debug(`Zip ${file.name}: processed ${processedCount} files, skipped ${skipped.length}`);
|
|
59923
60130
|
}
|
|
59924
60131
|
return { blocks, skipped };
|
|
59925
60132
|
} catch (err) {
|
|
59926
|
-
|
|
60133
|
+
log18.error(`Failed to process zip file ${file.name}: ${err}`);
|
|
59927
60134
|
return {
|
|
59928
60135
|
blocks: [],
|
|
59929
60136
|
skipped: [{
|
|
@@ -60020,7 +60227,7 @@ async function processFiles(platform, files, debug = false) {
|
|
|
60020
60227
|
blocks.push(...zipResult.blocks);
|
|
60021
60228
|
for (const s of zipResult.skipped) {
|
|
60022
60229
|
skipped.push(s);
|
|
60023
|
-
|
|
60230
|
+
log18.warn(`Skipped file ${s.name}: ${s.reason}`);
|
|
60024
60231
|
}
|
|
60025
60232
|
continue;
|
|
60026
60233
|
}
|
|
@@ -60054,7 +60261,7 @@ async function processFiles(platform, files, debug = false) {
|
|
|
60054
60261
|
}
|
|
60055
60262
|
if (result.skipped) {
|
|
60056
60263
|
skipped.push(result.skipped);
|
|
60057
|
-
|
|
60264
|
+
log18.warn(`Skipped file ${result.skipped.name}: ${result.skipped.reason}`);
|
|
60058
60265
|
}
|
|
60059
60266
|
}
|
|
60060
60267
|
return { blocks, skipped };
|
|
@@ -60087,7 +60294,7 @@ function stopTyping(session) {
|
|
|
60087
60294
|
}
|
|
60088
60295
|
|
|
60089
60296
|
// src/operations/message-manager.ts
|
|
60090
|
-
var
|
|
60297
|
+
var log19 = createLogger("msg-mgr");
|
|
60091
60298
|
|
|
60092
60299
|
class MessageManager {
|
|
60093
60300
|
platform;
|
|
@@ -60175,7 +60382,7 @@ class MessageManager {
|
|
|
60175
60382
|
});
|
|
60176
60383
|
}
|
|
60177
60384
|
async handleEvent(event) {
|
|
60178
|
-
const logger =
|
|
60385
|
+
const logger = log19.forSession(this.sessionId);
|
|
60179
60386
|
const transformCtx = {
|
|
60180
60387
|
sessionId: this.sessionId,
|
|
60181
60388
|
formatter: this.platform.getFormatter(),
|
|
@@ -60225,7 +60432,7 @@ class MessageManager {
|
|
|
60225
60432
|
}
|
|
60226
60433
|
}
|
|
60227
60434
|
async executeOperation(op) {
|
|
60228
|
-
const logger =
|
|
60435
|
+
const logger = log19.forSession(this.sessionId);
|
|
60229
60436
|
const ctx = this.getExecutorContext();
|
|
60230
60437
|
try {
|
|
60231
60438
|
if (isContentOp(op)) {
|
|
@@ -60293,7 +60500,7 @@ class MessageManager {
|
|
|
60293
60500
|
threadId: this.threadId,
|
|
60294
60501
|
platform: this.platform,
|
|
60295
60502
|
formatter: this.platform.getFormatter(),
|
|
60296
|
-
logger:
|
|
60503
|
+
logger: log19.forSession(this.sessionId),
|
|
60297
60504
|
postTracker: this.postTracker,
|
|
60298
60505
|
contentBreaker: this.contentBreaker,
|
|
60299
60506
|
threadLogger: this.session.threadLogger,
|
|
@@ -60504,13 +60711,13 @@ class MessageManager {
|
|
|
60504
60711
|
return this.systemExecutor.postSuccess(message, this.getExecutorContext());
|
|
60505
60712
|
}
|
|
60506
60713
|
async prepareForUserMessage() {
|
|
60507
|
-
const logger =
|
|
60714
|
+
const logger = log19.forSession(this.sessionId);
|
|
60508
60715
|
logger.debug("Preparing for new user message");
|
|
60509
60716
|
await this.closeCurrentPost();
|
|
60510
60717
|
await this.bumpTaskList();
|
|
60511
60718
|
}
|
|
60512
60719
|
async handleUserMessage(message, files, username, displayName) {
|
|
60513
|
-
const logger =
|
|
60720
|
+
const logger = log19.forSession(this.sessionId);
|
|
60514
60721
|
if (!this.session.claude.isRunning()) {
|
|
60515
60722
|
logger.debug("Claude not running, ignoring user message");
|
|
60516
60723
|
return false;
|
|
@@ -60537,7 +60744,7 @@ class MessageManager {
|
|
|
60537
60744
|
return this.session;
|
|
60538
60745
|
}
|
|
60539
60746
|
async handleReaction(postId, emoji, user, action) {
|
|
60540
|
-
const logger =
|
|
60747
|
+
const logger = log19.forSession(this.sessionId);
|
|
60541
60748
|
const ctx = this.getExecutorContext();
|
|
60542
60749
|
logger.debug(`Routing reaction: postId=${postId}, emoji=${emoji}, user=${user}, action=${action}`);
|
|
60543
60750
|
if (await this.questionApprovalExecutor.handleReaction(postId, emoji, user, action, ctx)) {
|
|
@@ -60733,7 +60940,7 @@ function formatPullRequestLink(url, formatter) {
|
|
|
60733
60940
|
}
|
|
60734
60941
|
|
|
60735
60942
|
// src/operations/sticky-message/handler.ts
|
|
60736
|
-
var
|
|
60943
|
+
var log20 = createLogger("sticky");
|
|
60737
60944
|
var botStartedAt = new Date;
|
|
60738
60945
|
function getPendingPrompts(session) {
|
|
60739
60946
|
const prompts2 = [];
|
|
@@ -60808,21 +61015,21 @@ function initialize(store) {
|
|
|
60808
61015
|
stickyPostIds.set(platformId, postId);
|
|
60809
61016
|
}
|
|
60810
61017
|
if (persistedIds.size > 0) {
|
|
60811
|
-
|
|
61018
|
+
log20.info(`\uD83D\uDCCC Restored ${persistedIds.size} sticky post ID(s) from persistence`);
|
|
60812
61019
|
}
|
|
60813
61020
|
}
|
|
60814
61021
|
function setPlatformPaused(platformId, paused) {
|
|
60815
61022
|
if (paused) {
|
|
60816
61023
|
pausedPlatforms.set(platformId, true);
|
|
60817
|
-
|
|
61024
|
+
log20.debug(`Platform ${platformId} marked as paused`);
|
|
60818
61025
|
} else {
|
|
60819
61026
|
pausedPlatforms.delete(platformId);
|
|
60820
|
-
|
|
61027
|
+
log20.debug(`Platform ${platformId} marked as active`);
|
|
60821
61028
|
}
|
|
60822
61029
|
}
|
|
60823
61030
|
function setShuttingDown(shuttingDown) {
|
|
60824
61031
|
isShuttingDown = shuttingDown;
|
|
60825
|
-
|
|
61032
|
+
log20.debug(`Bot shutdown state: ${shuttingDown}`);
|
|
60826
61033
|
}
|
|
60827
61034
|
function getTaskContent(session) {
|
|
60828
61035
|
const taskState = session.messageManager?.getTaskListState();
|
|
@@ -60919,6 +61126,13 @@ async function buildStatusBar(sessionCount, config, formatter, platformId) {
|
|
|
60919
61126
|
}
|
|
60920
61127
|
items.push(formatter.formatCode(formatVersionString()));
|
|
60921
61128
|
items.push(formatter.formatCode(`${sessionCount}/${config.maxSessions} sessions`));
|
|
61129
|
+
if (config.accountPoolStatus && config.accountPoolStatus.length > 0) {
|
|
61130
|
+
const total = config.accountPoolStatus.length;
|
|
61131
|
+
const cooling = config.accountPoolStatus.filter((a) => a.coolingUntil !== null).length;
|
|
61132
|
+
const available = total - cooling;
|
|
61133
|
+
const label = cooling > 0 ? `\uD83D\uDD11 ${available}/${total} accounts (${cooling} cooling)` : `\uD83D\uDD11 ${total} account${total === 1 ? "" : "s"}`;
|
|
61134
|
+
items.push(formatter.formatCode(label));
|
|
61135
|
+
}
|
|
60922
61136
|
const permMode = config.skipPermissions ? "⚡ Auto" : "\uD83D\uDD10 Interactive";
|
|
60923
61137
|
items.push(formatter.formatCode(permMode));
|
|
60924
61138
|
if (config.worktreeMode === "require") {
|
|
@@ -61127,12 +61341,12 @@ async function validateLastMessageIds(platform, sessions) {
|
|
|
61127
61341
|
try {
|
|
61128
61342
|
const post2 = await platform.getPost(lastMessageId);
|
|
61129
61343
|
if (!post2) {
|
|
61130
|
-
|
|
61344
|
+
log20.debug(`lastMessageId ${lastMessageId.substring(0, 8)} for session ${session.sessionId} was deleted, clearing`);
|
|
61131
61345
|
session.lastMessageId = undefined;
|
|
61132
61346
|
session.lastMessageTs = undefined;
|
|
61133
61347
|
}
|
|
61134
61348
|
} catch (err) {
|
|
61135
|
-
|
|
61349
|
+
log20.debug(`Failed to validate lastMessageId for session ${session.sessionId}, clearing: ${err}`);
|
|
61136
61350
|
session.lastMessageId = undefined;
|
|
61137
61351
|
session.lastMessageTs = undefined;
|
|
61138
61352
|
}
|
|
@@ -61141,63 +61355,63 @@ async function validateLastMessageIds(platform, sessions) {
|
|
|
61141
61355
|
}
|
|
61142
61356
|
async function updateStickyMessageImpl(platform, sessions, config) {
|
|
61143
61357
|
const platformSessions = [...sessions.values()].filter((s) => s.platformId === platform.platformId);
|
|
61144
|
-
|
|
61358
|
+
log20.debug(`updateStickyMessage for ${platform.platformId}, ${platformSessions.length} sessions`);
|
|
61145
61359
|
for (const s of platformSessions) {
|
|
61146
|
-
|
|
61360
|
+
log20.debug(` - ${s.sessionId}: title="${s.sessionTitle}" firstPrompt="${s.firstPrompt?.substring(0, 30)}..."`);
|
|
61147
61361
|
}
|
|
61148
61362
|
await validateLastMessageIds(platform, platformSessions);
|
|
61149
61363
|
const formatter = platform.getFormatter();
|
|
61150
61364
|
const content = await buildStickyMessage(sessions, platform.platformId, config, formatter, (threadId) => platform.getThreadLink(threadId));
|
|
61151
61365
|
const existingPostId = stickyPostIds.get(platform.platformId);
|
|
61152
61366
|
const shouldBump = needsBump.get(platform.platformId) ?? false;
|
|
61153
|
-
|
|
61367
|
+
log20.debug(`existingPostId: ${existingPostId || "(none)"}, needsBump: ${shouldBump}`);
|
|
61154
61368
|
try {
|
|
61155
61369
|
if (existingPostId && !shouldBump) {
|
|
61156
|
-
|
|
61370
|
+
log20.debug(`Updating existing post in place...`);
|
|
61157
61371
|
try {
|
|
61158
61372
|
await platform.updatePost(existingPostId, content);
|
|
61159
61373
|
try {
|
|
61160
61374
|
await platform.pinPost(existingPostId);
|
|
61161
|
-
|
|
61375
|
+
log20.debug(`Re-pinned post`);
|
|
61162
61376
|
} catch (pinErr) {
|
|
61163
|
-
|
|
61377
|
+
log20.debug(`Re-pin failed (might already be pinned): ${pinErr}`);
|
|
61164
61378
|
}
|
|
61165
|
-
|
|
61379
|
+
log20.debug(`Updated successfully`);
|
|
61166
61380
|
return;
|
|
61167
61381
|
} catch (err) {
|
|
61168
|
-
|
|
61382
|
+
log20.debug(`Update failed, will create new: ${err}`);
|
|
61169
61383
|
}
|
|
61170
61384
|
}
|
|
61171
61385
|
needsBump.set(platform.platformId, false);
|
|
61172
61386
|
if (existingPostId) {
|
|
61173
|
-
|
|
61387
|
+
log20.debug(`Unpinning and deleting existing post ${existingPostId.substring(0, 8)}...`);
|
|
61174
61388
|
try {
|
|
61175
61389
|
await platform.unpinPost(existingPostId);
|
|
61176
|
-
|
|
61390
|
+
log20.debug(`Unpinned successfully`);
|
|
61177
61391
|
} catch (err) {
|
|
61178
|
-
|
|
61392
|
+
log20.debug(`Unpin failed (probably already unpinned): ${err}`);
|
|
61179
61393
|
}
|
|
61180
61394
|
try {
|
|
61181
61395
|
await platform.deletePost(existingPostId);
|
|
61182
|
-
|
|
61396
|
+
log20.debug(`Deleted successfully`);
|
|
61183
61397
|
} catch (err) {
|
|
61184
|
-
|
|
61398
|
+
log20.debug(`Delete failed (probably already deleted): ${err}`);
|
|
61185
61399
|
}
|
|
61186
61400
|
stickyPostIds.delete(platform.platformId);
|
|
61187
61401
|
}
|
|
61188
|
-
|
|
61402
|
+
log20.debug(`Creating new post...`);
|
|
61189
61403
|
const post2 = await platform.createPost(content);
|
|
61190
61404
|
stickyPostIds.set(platform.platformId, post2.id);
|
|
61191
61405
|
try {
|
|
61192
61406
|
await platform.pinPost(post2.id);
|
|
61193
|
-
|
|
61407
|
+
log20.debug(`Pinned post successfully`);
|
|
61194
61408
|
} catch (err) {
|
|
61195
|
-
|
|
61409
|
+
log20.debug(`Failed to pin post: ${err}`);
|
|
61196
61410
|
}
|
|
61197
61411
|
if (sessionStore) {
|
|
61198
61412
|
sessionStore.saveStickyPostId(platform.platformId, post2.id);
|
|
61199
61413
|
}
|
|
61200
|
-
|
|
61414
|
+
log20.info(`\uD83D\uDCCC Created sticky message for ${platform.platformId}: ${formatShortId(post2.id)}`);
|
|
61201
61415
|
const excludePostIds = new Set;
|
|
61202
61416
|
if (sessionStore) {
|
|
61203
61417
|
for (const session of sessionStore.load().values()) {
|
|
@@ -61213,10 +61427,10 @@ async function updateStickyMessageImpl(platform, sessions, config) {
|
|
|
61213
61427
|
}
|
|
61214
61428
|
const botUser = await platform.getBotUser();
|
|
61215
61429
|
cleanupOldStickyMessages(platform, botUser.id, false, excludePostIds).catch((err) => {
|
|
61216
|
-
|
|
61430
|
+
log20.debug(`Background cleanup failed: ${err}`);
|
|
61217
61431
|
});
|
|
61218
61432
|
} catch (err) {
|
|
61219
|
-
|
|
61433
|
+
log20.error(`Failed to update sticky message for ${platform.platformId}`, err instanceof Error ? err : undefined);
|
|
61220
61434
|
}
|
|
61221
61435
|
}
|
|
61222
61436
|
async function updateAllStickyMessages(platforms, sessions, config) {
|
|
@@ -61241,7 +61455,7 @@ async function cleanupOldStickyMessages(platform, botUserId, forceRun = false, e
|
|
|
61241
61455
|
if (!forceRun) {
|
|
61242
61456
|
const lastRun = lastCleanupTime.get(platformId) || 0;
|
|
61243
61457
|
if (now - lastRun < CLEANUP_THROTTLE_MS) {
|
|
61244
|
-
|
|
61458
|
+
log20.debug(`Cleanup throttled for ${platformId} (last run ${Math.round((now - lastRun) / 1000)}s ago)`);
|
|
61245
61459
|
return;
|
|
61246
61460
|
}
|
|
61247
61461
|
}
|
|
@@ -61251,31 +61465,31 @@ async function cleanupOldStickyMessages(platform, botUserId, forceRun = false, e
|
|
|
61251
61465
|
const pinnedPostIds = await platform.getPinnedPosts();
|
|
61252
61466
|
const recentPinnedIds = pinnedPostIds.filter((id) => id !== currentStickyId && !excludePostIds?.has(id) && isRecentPost(id));
|
|
61253
61467
|
if (recentPinnedIds.length === 0) {
|
|
61254
|
-
|
|
61468
|
+
log20.debug(`No recent pinned posts to check (${pinnedPostIds.length} total, current: ${currentStickyId?.substring(0, 8) || "(none)"})`);
|
|
61255
61469
|
return;
|
|
61256
61470
|
}
|
|
61257
|
-
|
|
61471
|
+
log20.debug(`Checking ${recentPinnedIds.length} recent pinned posts (of ${pinnedPostIds.length} total)`);
|
|
61258
61472
|
for (const postId of recentPinnedIds) {
|
|
61259
61473
|
try {
|
|
61260
61474
|
const post2 = await platform.getPost(postId);
|
|
61261
61475
|
if (!post2)
|
|
61262
61476
|
continue;
|
|
61263
61477
|
if (post2.userId === botUserId) {
|
|
61264
|
-
|
|
61478
|
+
log20.debug(`Cleaning up old sticky: ${postId.substring(0, 8)}...`);
|
|
61265
61479
|
try {
|
|
61266
61480
|
await platform.unpinPost(postId);
|
|
61267
61481
|
await platform.deletePost(postId);
|
|
61268
|
-
|
|
61482
|
+
log20.info(`\uD83E\uDDF9 Cleaned up old sticky message: ${postId.substring(0, 8)}...`);
|
|
61269
61483
|
} catch (err) {
|
|
61270
|
-
|
|
61484
|
+
log20.debug(`Failed to cleanup ${postId}: ${err}`);
|
|
61271
61485
|
}
|
|
61272
61486
|
}
|
|
61273
61487
|
} catch (err) {
|
|
61274
|
-
|
|
61488
|
+
log20.debug(`Could not check post ${postId}: ${err}`);
|
|
61275
61489
|
}
|
|
61276
61490
|
}
|
|
61277
61491
|
} catch (err) {
|
|
61278
|
-
|
|
61492
|
+
log20.error(`Failed to cleanup old sticky messages`, err instanceof Error ? err : undefined);
|
|
61279
61493
|
}
|
|
61280
61494
|
}
|
|
61281
61495
|
// src/operations/bug-report/handler.ts
|
|
@@ -65338,8 +65552,16 @@ function getUpdateInfo() {
|
|
|
65338
65552
|
init_emoji();
|
|
65339
65553
|
init_logger();
|
|
65340
65554
|
init_worktree();
|
|
65341
|
-
var
|
|
65342
|
-
var sessionLog2 = createSessionLog(
|
|
65555
|
+
var log21 = createLogger("commands");
|
|
65556
|
+
var sessionLog2 = createSessionLog(log21);
|
|
65557
|
+
function sessionAccountOption(session, ctx) {
|
|
65558
|
+
if (!session.claudeAccountId)
|
|
65559
|
+
return;
|
|
65560
|
+
const account = ctx.ops.getClaudeAccount(session.claudeAccountId);
|
|
65561
|
+
if (!account)
|
|
65562
|
+
return;
|
|
65563
|
+
return { id: account.id, home: account.home, apiKey: account.apiKey };
|
|
65564
|
+
}
|
|
65343
65565
|
async function restartClaudeSession(session, cliOptions, ctx, actionName) {
|
|
65344
65566
|
ctx.ops.stopTyping(session);
|
|
65345
65567
|
transitionTo(session, "restarting");
|
|
@@ -65348,6 +65570,7 @@ async function restartClaudeSession(session, cliOptions, ctx, actionName) {
|
|
|
65348
65570
|
session.claude = new ClaudeCli(cliOptions);
|
|
65349
65571
|
session.claude.on("event", (e) => ctx.ops.handleEvent(session.sessionId, e));
|
|
65350
65572
|
session.claude.on("exit", (code) => ctx.ops.handleExit(session.sessionId, code));
|
|
65573
|
+
session.claude.on("rate-limit", (hit) => handleRateLimit(session, hit, ctx));
|
|
65351
65574
|
try {
|
|
65352
65575
|
session.claude.start();
|
|
65353
65576
|
return true;
|
|
@@ -65500,7 +65723,8 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
65500
65723
|
platformConfig: session.platform.getMcpConfig(),
|
|
65501
65724
|
appendSystemPrompt,
|
|
65502
65725
|
logSessionId: session.sessionId,
|
|
65503
|
-
permissionTimeoutMs: ctx.config.permissionTimeoutMs
|
|
65726
|
+
permissionTimeoutMs: ctx.config.permissionTimeoutMs,
|
|
65727
|
+
account: sessionAccountOption(session, ctx)
|
|
65504
65728
|
};
|
|
65505
65729
|
const success = await restartClaudeSession(session, cliOptions, ctx, "Restart Claude for directory change");
|
|
65506
65730
|
if (!success)
|
|
@@ -65595,7 +65819,8 @@ async function enableInteractivePermissions(session, username, ctx) {
|
|
|
65595
65819
|
chrome: ctx.config.chromeEnabled,
|
|
65596
65820
|
platformConfig: session.platform.getMcpConfig(),
|
|
65597
65821
|
logSessionId: session.sessionId,
|
|
65598
|
-
permissionTimeoutMs: ctx.config.permissionTimeoutMs
|
|
65822
|
+
permissionTimeoutMs: ctx.config.permissionTimeoutMs,
|
|
65823
|
+
account: sessionAccountOption(session, ctx)
|
|
65599
65824
|
};
|
|
65600
65825
|
const success = await restartClaudeSession(session, cliOptions, ctx, "Enable interactive permissions");
|
|
65601
65826
|
if (!success)
|
|
@@ -65701,6 +65926,11 @@ async function updateSessionHeader(session, ctx) {
|
|
|
65701
65926
|
if (otherParticipants) {
|
|
65702
65927
|
items.push(["\uD83D\uDC65", "Participants", otherParticipants]);
|
|
65703
65928
|
}
|
|
65929
|
+
if (session.claudeAccountId) {
|
|
65930
|
+
const account = ctx.ops.getClaudeAccount(session.claudeAccountId);
|
|
65931
|
+
const label = account?.displayName ?? session.claudeAccountId;
|
|
65932
|
+
items.push(["\uD83D\uDD11", "Claude account", formatter.formatCode(label)]);
|
|
65933
|
+
}
|
|
65704
65934
|
items.push(["\uD83C\uDD94", "Session ID", formatter.formatCode(session.claudeSessionId.substring(0, 8))]);
|
|
65705
65935
|
const logPath = getLogFilePath(session.platform.platformId, session.claudeSessionId);
|
|
65706
65936
|
const shortLogPath = logPath.replace(process.env.HOME || "", "~");
|
|
@@ -65857,7 +66087,7 @@ init_logger();
|
|
|
65857
66087
|
import { exec as exec3 } from "child_process";
|
|
65858
66088
|
import { promisify as promisify3 } from "util";
|
|
65859
66089
|
var execAsync2 = promisify3(exec3);
|
|
65860
|
-
var
|
|
66090
|
+
var log22 = createLogger("branch");
|
|
65861
66091
|
var SUGGESTION_TIMEOUT3 = 15000;
|
|
65862
66092
|
var MAX_SUGGESTIONS = 3;
|
|
65863
66093
|
async function getCurrentBranch3(workingDir) {
|
|
@@ -65906,7 +66136,7 @@ function parseBranchSuggestions(response) {
|
|
|
65906
66136
|
return lines.slice(0, MAX_SUGGESTIONS);
|
|
65907
66137
|
}
|
|
65908
66138
|
async function suggestBranchNames(workingDir, userMessage) {
|
|
65909
|
-
|
|
66139
|
+
log22.debug(`Suggesting branch names for: "${userMessage.substring(0, 50)}..."`);
|
|
65910
66140
|
try {
|
|
65911
66141
|
const [currentBranch, recentCommits] = await Promise.all([
|
|
65912
66142
|
getCurrentBranch3(workingDir),
|
|
@@ -65920,14 +66150,14 @@ async function suggestBranchNames(workingDir, userMessage) {
|
|
|
65920
66150
|
workingDir
|
|
65921
66151
|
});
|
|
65922
66152
|
if (!result.success || !result.response) {
|
|
65923
|
-
|
|
66153
|
+
log22.debug(`Branch suggestion failed: ${result.error || "no response"}`);
|
|
65924
66154
|
return [];
|
|
65925
66155
|
}
|
|
65926
66156
|
const suggestions = parseBranchSuggestions(result.response);
|
|
65927
|
-
|
|
66157
|
+
log22.debug(`Got ${suggestions.length} branch suggestions: ${suggestions.join(", ")}`);
|
|
65928
66158
|
return suggestions;
|
|
65929
66159
|
} catch (err) {
|
|
65930
|
-
|
|
66160
|
+
log22.debug(`Branch suggestion error: ${err}`);
|
|
65931
66161
|
return [];
|
|
65932
66162
|
}
|
|
65933
66163
|
}
|
|
@@ -65936,8 +66166,8 @@ async function suggestBranchNames(workingDir, userMessage) {
|
|
|
65936
66166
|
init_worktree();
|
|
65937
66167
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
65938
66168
|
init_logger();
|
|
65939
|
-
var
|
|
65940
|
-
var sessionLog3 = createSessionLog(
|
|
66169
|
+
var log23 = createLogger("worktree");
|
|
66170
|
+
var sessionLog3 = createSessionLog(log23);
|
|
65941
66171
|
function parseWorktreeError(error) {
|
|
65942
66172
|
const message = error instanceof Error ? error.message : String(error);
|
|
65943
66173
|
const lowerMessage = message.toLowerCase();
|
|
@@ -66488,8 +66718,8 @@ async function cleanupWorktreeCommand(session, username, hasOtherSessionsUsingWo
|
|
|
66488
66718
|
}
|
|
66489
66719
|
// src/operations/events/handler.ts
|
|
66490
66720
|
init_logger();
|
|
66491
|
-
var
|
|
66492
|
-
var sessionLog4 = createSessionLog(
|
|
66721
|
+
var log24 = createLogger("events");
|
|
66722
|
+
var sessionLog4 = createSessionLog(log24);
|
|
66493
66723
|
function detectAndExecuteClaudeCommands(text, session, ctx) {
|
|
66494
66724
|
const parsed = parseClaudeCommand(text);
|
|
66495
66725
|
if (parsed && isClaudeAllowedCommand(parsed.command)) {
|
|
@@ -66737,8 +66967,8 @@ function createSessionContext(config, state, ops) {
|
|
|
66737
66967
|
// src/operations/context-prompt/handler.ts
|
|
66738
66968
|
init_emoji();
|
|
66739
66969
|
init_logger();
|
|
66740
|
-
var
|
|
66741
|
-
var sessionLog5 = createSessionLog(
|
|
66970
|
+
var log25 = createLogger("context");
|
|
66971
|
+
var sessionLog5 = createSessionLog(log25);
|
|
66742
66972
|
var CONTEXT_PROMPT_TIMEOUT_MS = 30000;
|
|
66743
66973
|
var CONTEXT_OPTIONS = [3, 5, 10];
|
|
66744
66974
|
var contextPromptTimeouts = new Map;
|
|
@@ -66988,11 +67218,16 @@ function formatRelativeTime(date) {
|
|
|
66988
67218
|
}
|
|
66989
67219
|
// src/session/lifecycle.ts
|
|
66990
67220
|
init_worktree();
|
|
66991
|
-
var
|
|
66992
|
-
var sessionLog6 = createSessionLog(
|
|
67221
|
+
var log26 = createLogger("lifecycle");
|
|
67222
|
+
var sessionLog6 = createSessionLog(log26);
|
|
66993
67223
|
function mutableSessions(ctx) {
|
|
66994
67224
|
return ctx.state.sessions;
|
|
66995
67225
|
}
|
|
67226
|
+
var pendingStartsCount = 0;
|
|
67227
|
+
function releasePendingStart() {
|
|
67228
|
+
if (pendingStartsCount > 0)
|
|
67229
|
+
pendingStartsCount--;
|
|
67230
|
+
}
|
|
66996
67231
|
function mutablePostIndex(ctx) {
|
|
66997
67232
|
return ctx.state.postIndex;
|
|
66998
67233
|
}
|
|
@@ -67037,12 +67272,31 @@ async function cleanupSession(session, ctx, options2 = {}) {
|
|
|
67037
67272
|
cleanupPostIndex(ctx, session.threadId);
|
|
67038
67273
|
}
|
|
67039
67274
|
keepAlive.sessionEnded();
|
|
67275
|
+
releaseAccountIfHeld(session, ctx);
|
|
67276
|
+
}
|
|
67277
|
+
function releaseAccountIfHeld(session, ctx) {
|
|
67278
|
+
if (session.claudeAccountId) {
|
|
67279
|
+
ctx.ops.releaseClaudeAccount(session.claudeAccountId);
|
|
67280
|
+
session.claudeAccountId = undefined;
|
|
67281
|
+
}
|
|
67040
67282
|
}
|
|
67041
67283
|
function removeFromRegistry(session, ctx) {
|
|
67042
67284
|
ctx.ops.emitSessionRemove(session.sessionId);
|
|
67043
67285
|
mutableSessions(ctx).delete(session.sessionId);
|
|
67044
67286
|
cleanupPostIndex(ctx, session.threadId);
|
|
67045
67287
|
keepAlive.sessionEnded();
|
|
67288
|
+
releaseAccountIfHeld(session, ctx);
|
|
67289
|
+
}
|
|
67290
|
+
function handleRateLimit(session, hit, ctx) {
|
|
67291
|
+
if (!session.claudeAccountId) {
|
|
67292
|
+
sessionLog6(session).warn(`Rate limit hit in single-account mode — cannot reroute`);
|
|
67293
|
+
return;
|
|
67294
|
+
}
|
|
67295
|
+
const deadline = cooldownDeadline(hit);
|
|
67296
|
+
ctx.ops.markClaudeAccountCooling(session.claudeAccountId, deadline);
|
|
67297
|
+
const minutes = Math.max(1, Math.ceil((deadline - Date.now()) / 60000));
|
|
67298
|
+
sessionLog6(session).warn(`Rate limit on account "${session.claudeAccountId}" — cooling for ~${minutes}min`);
|
|
67299
|
+
post(session, "warning", `⚠️ Claude account \`${session.claudeAccountId}\` hit a rate limit. ` + `New sessions will use another account until it resets (~${minutes}min).`);
|
|
67046
67300
|
}
|
|
67047
67301
|
function findPersistedByThreadId(persisted, threadId) {
|
|
67048
67302
|
for (const session of persisted.values()) {
|
|
@@ -67306,20 +67560,24 @@ async function startSession(options2, username, displayName, replyToPostId, plat
|
|
|
67306
67560
|
if (!platform) {
|
|
67307
67561
|
throw new Error(`Platform '${platformId}' not found. Call addPlatform() first.`);
|
|
67308
67562
|
}
|
|
67309
|
-
|
|
67563
|
+
const activeOrPending = ctx.state.sessions.size + pendingStartsCount;
|
|
67564
|
+
if (activeOrPending >= ctx.config.maxSessions) {
|
|
67310
67565
|
const formatter2 = platform.getFormatter();
|
|
67311
67566
|
const tempSession = {
|
|
67312
67567
|
platform,
|
|
67313
67568
|
threadId: replyToPostId || "",
|
|
67314
67569
|
sessionId: "temp"
|
|
67315
67570
|
};
|
|
67316
|
-
await post(tempSession, "warning", `${formatter2.formatBold("Too busy")} - ${
|
|
67571
|
+
await post(tempSession, "warning", `${formatter2.formatBold("Too busy")} - ${activeOrPending} sessions active. Please try again later.`);
|
|
67317
67572
|
return;
|
|
67318
67573
|
}
|
|
67574
|
+
pendingStartsCount++;
|
|
67319
67575
|
const startFormatter = platform.getFormatter();
|
|
67320
67576
|
const startPost = await withErrorHandling(() => platform.createPost(startFormatter.formatItalic("Claude Threads session starting..."), replyToPostId), { action: "Create session post" });
|
|
67321
|
-
if (!startPost)
|
|
67577
|
+
if (!startPost) {
|
|
67578
|
+
releasePendingStart();
|
|
67322
67579
|
return;
|
|
67580
|
+
}
|
|
67323
67581
|
const actualThreadId = replyToPostId || startPost.id;
|
|
67324
67582
|
const sessionId = ctx.ops.getSessionId(platformId, actualThreadId);
|
|
67325
67583
|
platform.sendTyping(actualThreadId);
|
|
@@ -67334,26 +67592,32 @@ async function startSession(options2, username, displayName, replyToPostId, plat
|
|
|
67334
67592
|
const resolvedDir = resolve6(requestedDir);
|
|
67335
67593
|
if (!existsSync11(resolvedDir)) {
|
|
67336
67594
|
await platform.updatePost(startPost.id, `❌ Directory does not exist: ${formatter.formatCode(initialOptions.workingDir)}`);
|
|
67595
|
+
releasePendingStart();
|
|
67337
67596
|
return;
|
|
67338
67597
|
}
|
|
67339
67598
|
const { statSync: statSync4 } = await import("fs");
|
|
67340
67599
|
if (!statSync4(resolvedDir).isDirectory()) {
|
|
67341
67600
|
await platform.updatePost(startPost.id, `❌ Not a directory: ${formatter.formatCode(initialOptions.workingDir)}`);
|
|
67601
|
+
releasePendingStart();
|
|
67342
67602
|
return;
|
|
67343
67603
|
}
|
|
67344
67604
|
workingDir = resolvedDir;
|
|
67345
|
-
|
|
67605
|
+
log26.info(`Starting session in directory: ${workingDir} (from !cd command)`);
|
|
67346
67606
|
}
|
|
67347
67607
|
if (initialOptions?.forceInteractivePermissions) {
|
|
67348
67608
|
forceInteractivePermissions = true;
|
|
67349
67609
|
skipPermissions = false;
|
|
67350
|
-
|
|
67610
|
+
log26.info(`Starting session with interactive permissions (from !permissions command)`);
|
|
67351
67611
|
}
|
|
67352
67612
|
const sessionContext = buildSessionContext(platform, workingDir);
|
|
67353
67613
|
const systemPrompt = `${sessionContext}
|
|
67354
67614
|
|
|
67355
67615
|
${CHAT_PLATFORM_PROMPT}`;
|
|
67356
67616
|
const platformMcpConfig = platform.getMcpConfig();
|
|
67617
|
+
const claudeAccount = ctx.ops.acquireClaudeAccount();
|
|
67618
|
+
if (claudeAccount) {
|
|
67619
|
+
log26.info(`Session ${sessionId.substring(0, 20)} reserved Claude account "${claudeAccount.id}"`);
|
|
67620
|
+
}
|
|
67357
67621
|
const cliOptions = {
|
|
67358
67622
|
workingDir,
|
|
67359
67623
|
threadId: actualThreadId,
|
|
@@ -67364,7 +67628,8 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
67364
67628
|
platformConfig: platformMcpConfig,
|
|
67365
67629
|
appendSystemPrompt: systemPrompt,
|
|
67366
67630
|
logSessionId: sessionId,
|
|
67367
|
-
permissionTimeoutMs: ctx.config.permissionTimeoutMs
|
|
67631
|
+
permissionTimeoutMs: ctx.config.permissionTimeoutMs,
|
|
67632
|
+
account: claudeAccount ? { id: claudeAccount.id, home: claudeAccount.home, apiKey: claudeAccount.apiKey } : undefined
|
|
67368
67633
|
};
|
|
67369
67634
|
const claude = new ClaudeCli(cliOptions);
|
|
67370
67635
|
const session = {
|
|
@@ -67373,6 +67638,7 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
67373
67638
|
sessionId,
|
|
67374
67639
|
platform,
|
|
67375
67640
|
claudeSessionId,
|
|
67641
|
+
claudeAccountId: claudeAccount?.id,
|
|
67376
67642
|
startedBy: username,
|
|
67377
67643
|
startedByDisplayName: displayName,
|
|
67378
67644
|
startedAt: new Date,
|
|
@@ -67401,6 +67667,7 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
67401
67667
|
workingDir: ctx.config.workingDir
|
|
67402
67668
|
});
|
|
67403
67669
|
mutableSessions(ctx).set(sessionId, session);
|
|
67670
|
+
releasePendingStart();
|
|
67404
67671
|
ctx.ops.registerPost(startPost.id, actualThreadId);
|
|
67405
67672
|
ctx.ops.emitSessionAdd(session);
|
|
67406
67673
|
sessionLog6(session).info(`▶ Session started by @${username}`);
|
|
@@ -67411,6 +67678,7 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
67411
67678
|
ctx.ops.startTyping(session);
|
|
67412
67679
|
claude.on("event", (e) => ctx.ops.handleEvent(sessionId, e));
|
|
67413
67680
|
claude.on("exit", (code) => ctx.ops.handleExit(sessionId, code));
|
|
67681
|
+
claude.on("rate-limit", (hit) => handleRateLimit(session, hit, ctx));
|
|
67414
67682
|
try {
|
|
67415
67683
|
claude.start();
|
|
67416
67684
|
} catch (err) {
|
|
@@ -67418,6 +67686,7 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
67418
67686
|
ctx.ops.stopTyping(session);
|
|
67419
67687
|
ctx.ops.emitSessionRemove(session.sessionId);
|
|
67420
67688
|
mutableSessions(ctx).delete(session.sessionId);
|
|
67689
|
+
releaseAccountIfHeld(session, ctx);
|
|
67421
67690
|
await ctx.ops.updateStickyMessage();
|
|
67422
67691
|
return;
|
|
67423
67692
|
}
|
|
@@ -67453,28 +67722,28 @@ async function resumeSession(state, ctx) {
|
|
|
67453
67722
|
!state.claudeSessionId && "claudeSessionId",
|
|
67454
67723
|
!state.workingDir && "workingDir"
|
|
67455
67724
|
].filter(Boolean).join(", ");
|
|
67456
|
-
|
|
67725
|
+
log26.warn(`Skipping session with missing required fields: ${missing}`);
|
|
67457
67726
|
return;
|
|
67458
67727
|
}
|
|
67459
67728
|
const shortId = state.threadId.substring(0, 8);
|
|
67460
67729
|
const platforms = ctx.state.platforms;
|
|
67461
67730
|
const platform = platforms.get(state.platformId);
|
|
67462
67731
|
if (!platform) {
|
|
67463
|
-
|
|
67732
|
+
log26.warn(`Platform ${state.platformId} not registered, skipping resume for ${shortId}...`);
|
|
67464
67733
|
return;
|
|
67465
67734
|
}
|
|
67466
67735
|
const threadPost = await platform.getPost(state.threadId);
|
|
67467
67736
|
if (!threadPost) {
|
|
67468
|
-
|
|
67737
|
+
log26.warn(`Thread ${shortId}... deleted, skipping resume`);
|
|
67469
67738
|
ctx.state.sessionStore.remove(`${state.platformId}:${state.threadId}`);
|
|
67470
67739
|
return;
|
|
67471
67740
|
}
|
|
67472
67741
|
if (ctx.state.sessions.size >= ctx.config.maxSessions) {
|
|
67473
|
-
|
|
67742
|
+
log26.warn(`Max sessions reached, skipping resume for ${shortId}...`);
|
|
67474
67743
|
return;
|
|
67475
67744
|
}
|
|
67476
67745
|
if (!existsSync11(state.workingDir)) {
|
|
67477
|
-
|
|
67746
|
+
log26.warn(`Working directory ${state.workingDir} no longer exists, skipping resume for ${shortId}...`);
|
|
67478
67747
|
ctx.state.sessionStore.remove(`${state.platformId}:${state.threadId}`);
|
|
67479
67748
|
const resumeFormatter = platform.getFormatter();
|
|
67480
67749
|
const tempSession = {
|
|
@@ -67496,6 +67765,10 @@ Please start a new session.`), { action: "Post resume failure notification" });
|
|
|
67496
67765
|
const appendSystemPrompt = `${sessionContext}
|
|
67497
67766
|
|
|
67498
67767
|
${CHAT_PLATFORM_PROMPT}`;
|
|
67768
|
+
const claudeAccount = ctx.ops.acquireClaudeAccount(state.claudeAccountId);
|
|
67769
|
+
if (state.claudeAccountId && !claudeAccount) {
|
|
67770
|
+
log26.warn(`Persisted session referenced Claude account "${state.claudeAccountId}" ` + `which is no longer configured — resuming under default env`);
|
|
67771
|
+
}
|
|
67499
67772
|
const cliOptions = {
|
|
67500
67773
|
workingDir: state.workingDir,
|
|
67501
67774
|
threadId: state.threadId,
|
|
@@ -67506,7 +67779,8 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
67506
67779
|
platformConfig: platformMcpConfig,
|
|
67507
67780
|
appendSystemPrompt,
|
|
67508
67781
|
logSessionId: sessionId,
|
|
67509
|
-
permissionTimeoutMs: ctx.config.permissionTimeoutMs
|
|
67782
|
+
permissionTimeoutMs: ctx.config.permissionTimeoutMs,
|
|
67783
|
+
account: claudeAccount ? { id: claudeAccount.id, home: claudeAccount.home, apiKey: claudeAccount.apiKey } : undefined
|
|
67510
67784
|
};
|
|
67511
67785
|
const claude = new ClaudeCli(cliOptions);
|
|
67512
67786
|
const session = {
|
|
@@ -67515,6 +67789,7 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
67515
67789
|
sessionId,
|
|
67516
67790
|
platform,
|
|
67517
67791
|
claudeSessionId: state.claudeSessionId,
|
|
67792
|
+
claudeAccountId: claudeAccount?.id,
|
|
67518
67793
|
startedBy: state.startedBy,
|
|
67519
67794
|
startedByDisplayName: state.startedByDisplayName,
|
|
67520
67795
|
startedAt: new Date(state.startedAt),
|
|
@@ -67557,7 +67832,7 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
67557
67832
|
worktreePath: detected.worktreePath,
|
|
67558
67833
|
branch: detected.branch
|
|
67559
67834
|
};
|
|
67560
|
-
|
|
67835
|
+
log26.info(`Auto-detected worktree info for resumed session: branch=${detected.branch}`);
|
|
67561
67836
|
}
|
|
67562
67837
|
}
|
|
67563
67838
|
session.messageManager = createMessageManager(session, ctx);
|
|
@@ -67592,6 +67867,7 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
67592
67867
|
keepAlive.sessionStarted();
|
|
67593
67868
|
claude.on("event", (e) => ctx.ops.handleEvent(sessionId, e));
|
|
67594
67869
|
claude.on("exit", (code) => ctx.ops.handleExit(sessionId, code));
|
|
67870
|
+
claude.on("rate-limit", (hit) => handleRateLimit(session, hit, ctx));
|
|
67595
67871
|
try {
|
|
67596
67872
|
claude.start();
|
|
67597
67873
|
sessionLog6(session).info(`\uD83D\uDD04 Session resumed (@${state.startedBy})`);
|
|
@@ -67612,10 +67888,11 @@ ${sessionFormatter.formatItalic("Reconnected to Claude session. You can continue
|
|
|
67612
67888
|
await ctx.ops.updateStickyMessage();
|
|
67613
67889
|
ctx.ops.persistSession(session);
|
|
67614
67890
|
} catch (err) {
|
|
67615
|
-
|
|
67891
|
+
log26.error(`Failed to resume session ${shortId}`, err instanceof Error ? err : undefined);
|
|
67616
67892
|
ctx.ops.emitSessionRemove(sessionId);
|
|
67617
67893
|
mutableSessions(ctx).delete(sessionId);
|
|
67618
67894
|
ctx.state.sessionStore.remove(sessionId);
|
|
67895
|
+
releaseAccountIfHeld(session, ctx);
|
|
67619
67896
|
const failFormatter = session.platform.getFormatter();
|
|
67620
67897
|
await withErrorHandling(() => post(session, "warning", `${failFormatter.formatBold("Could not resume previous session.")} Starting fresh.
|
|
67621
67898
|
${failFormatter.formatItalic("Your previous conversation context is preserved, but Claude needs to re-read it.")}`), { action: "Post resume failure notification", session });
|
|
@@ -67651,18 +67928,18 @@ async function resumePausedSession(threadId, message, files, ctx) {
|
|
|
67651
67928
|
const persisted = ctx.state.sessionStore.load();
|
|
67652
67929
|
const state = findPersistedByThreadId(persisted, threadId);
|
|
67653
67930
|
if (!state) {
|
|
67654
|
-
|
|
67931
|
+
log26.debug(`No persisted session found for ${threadId.substring(0, 8)}...`);
|
|
67655
67932
|
return;
|
|
67656
67933
|
}
|
|
67657
67934
|
const shortId = threadId.substring(0, 8);
|
|
67658
|
-
|
|
67935
|
+
log26.info(`\uD83D\uDD04 Resuming paused session ${shortId}... for new message`);
|
|
67659
67936
|
await resumeSession(state, ctx);
|
|
67660
67937
|
const session = ctx.ops.findSessionByThreadId(threadId);
|
|
67661
67938
|
if (session && session.claude.isRunning() && session.messageManager) {
|
|
67662
67939
|
session.messageCount++;
|
|
67663
67940
|
await session.messageManager.handleUserMessage(message, files, state.startedBy);
|
|
67664
67941
|
} else {
|
|
67665
|
-
|
|
67942
|
+
log26.warn(`Failed to resume session ${shortId}..., could not send message`);
|
|
67666
67943
|
}
|
|
67667
67944
|
}
|
|
67668
67945
|
async function handleExit(sessionId, code, ctx) {
|
|
@@ -67670,7 +67947,7 @@ async function handleExit(sessionId, code, ctx) {
|
|
|
67670
67947
|
const shortId = sessionId.substring(0, 8);
|
|
67671
67948
|
sessionLog6(session).debug(`handleExit called code=${code} isShuttingDown=${ctx.state.isShuttingDown}`);
|
|
67672
67949
|
if (!session) {
|
|
67673
|
-
|
|
67950
|
+
log26.debug(`Session ${shortId}... not found (already cleaned up)`);
|
|
67674
67951
|
return;
|
|
67675
67952
|
}
|
|
67676
67953
|
if (isSessionRestarting(session)) {
|
|
@@ -67863,7 +68140,7 @@ async function cleanupIdleSessions(timeoutMs, warningMs, ctx) {
|
|
|
67863
68140
|
}
|
|
67864
68141
|
|
|
67865
68142
|
// src/operations/monitor/handler.ts
|
|
67866
|
-
var
|
|
68143
|
+
var log27 = createLogger("monitor");
|
|
67867
68144
|
var DEFAULT_INTERVAL_MS = 60 * 1000;
|
|
67868
68145
|
|
|
67869
68146
|
class SessionMonitor {
|
|
@@ -67885,14 +68162,14 @@ class SessionMonitor {
|
|
|
67885
68162
|
}
|
|
67886
68163
|
start() {
|
|
67887
68164
|
if (this.isRunning) {
|
|
67888
|
-
|
|
68165
|
+
log27.debug("Session monitor already running");
|
|
67889
68166
|
return;
|
|
67890
68167
|
}
|
|
67891
68168
|
this.isRunning = true;
|
|
67892
|
-
|
|
68169
|
+
log27.debug(`Session monitor started (interval: ${this.intervalMs / 1000}s)`);
|
|
67893
68170
|
this.timer = setInterval(() => {
|
|
67894
68171
|
this.runCheck().catch((err) => {
|
|
67895
|
-
|
|
68172
|
+
log27.error(`Error during session monitoring: ${err}`);
|
|
67896
68173
|
});
|
|
67897
68174
|
}, this.intervalMs);
|
|
67898
68175
|
}
|
|
@@ -67902,7 +68179,7 @@ class SessionMonitor {
|
|
|
67902
68179
|
this.timer = null;
|
|
67903
68180
|
}
|
|
67904
68181
|
this.isRunning = false;
|
|
67905
|
-
|
|
68182
|
+
log27.debug("Session monitor stopped");
|
|
67906
68183
|
}
|
|
67907
68184
|
async runCheck() {
|
|
67908
68185
|
await cleanupIdleSessions(this.sessionTimeoutMs, this.sessionWarningMs, this.getContext());
|
|
@@ -67914,8 +68191,8 @@ class SessionMonitor {
|
|
|
67914
68191
|
// src/operations/plugin/handler.ts
|
|
67915
68192
|
init_spawn();
|
|
67916
68193
|
init_logger();
|
|
67917
|
-
var
|
|
67918
|
-
var sessionLog7 = createSessionLog(
|
|
68194
|
+
var log28 = createLogger("plugin");
|
|
68195
|
+
var sessionLog7 = createSessionLog(log28);
|
|
67919
68196
|
async function runPluginCommand(args, cwd, timeout2 = 60000) {
|
|
67920
68197
|
return new Promise((resolve6) => {
|
|
67921
68198
|
const claudePath = process.env.CLAUDE_PATH || "claude";
|
|
@@ -67936,7 +68213,7 @@ async function runPluginCommand(args, cwd, timeout2 = 60000) {
|
|
|
67936
68213
|
});
|
|
67937
68214
|
proc.on("error", (err) => {
|
|
67938
68215
|
resolve6({ stdout, stderr, exitCode: 1 });
|
|
67939
|
-
|
|
68216
|
+
log28.error(`Plugin command error: ${err.message}`);
|
|
67940
68217
|
});
|
|
67941
68218
|
});
|
|
67942
68219
|
}
|
|
@@ -68135,7 +68412,7 @@ class SessionRegistry {
|
|
|
68135
68412
|
|
|
68136
68413
|
// src/session/manager.ts
|
|
68137
68414
|
init_logger();
|
|
68138
|
-
var
|
|
68415
|
+
var log29 = createLogger("manager");
|
|
68139
68416
|
|
|
68140
68417
|
class SessionManager extends EventEmitter4 {
|
|
68141
68418
|
platforms = new Map;
|
|
@@ -68158,7 +68435,8 @@ class SessionManager extends EventEmitter4 {
|
|
|
68158
68435
|
customDescription;
|
|
68159
68436
|
customFooter;
|
|
68160
68437
|
autoUpdateManager = null;
|
|
68161
|
-
|
|
68438
|
+
accountPool;
|
|
68439
|
+
constructor(workingDir, skipPermissions = false, chromeEnabled = false, worktreeMode = "prompt", sessionsPath, threadLogsEnabled = true, threadLogsRetentionDays = 30, limits, claudeAccounts) {
|
|
68162
68440
|
super();
|
|
68163
68441
|
this.workingDir = workingDir;
|
|
68164
68442
|
this.skipPermissions = skipPermissions;
|
|
@@ -68169,6 +68447,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
68169
68447
|
this.limits = resolveLimits(limits);
|
|
68170
68448
|
this.sessionStore = new SessionStore(sessionsPath);
|
|
68171
68449
|
this.registry = new SessionRegistry(this.sessionStore);
|
|
68450
|
+
this.accountPool = new AccountPool(claudeAccounts);
|
|
68172
68451
|
this.sessionMonitor = new SessionMonitor({
|
|
68173
68452
|
sessionTimeoutMs: this.limits.sessionTimeoutMinutes * 60 * 1000,
|
|
68174
68453
|
sessionWarningMs: this.limits.sessionWarningMinutes * 60 * 1000,
|
|
@@ -68202,7 +68481,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
68202
68481
|
markNeedsBump(platformId);
|
|
68203
68482
|
this.updateStickyMessage();
|
|
68204
68483
|
});
|
|
68205
|
-
|
|
68484
|
+
log29.info(`\uD83D\uDCE1 Platform "${platformId}" registered`);
|
|
68206
68485
|
}
|
|
68207
68486
|
removePlatform(platformId) {
|
|
68208
68487
|
this.platforms.delete(platformId);
|
|
@@ -68218,7 +68497,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
68218
68497
|
if (users) {
|
|
68219
68498
|
users.add(sessionId);
|
|
68220
68499
|
}
|
|
68221
|
-
|
|
68500
|
+
log29.debug(`Registered session ${sessionId.substring(0, 20)} as worktree user for ${worktreePath}`);
|
|
68222
68501
|
}
|
|
68223
68502
|
unregisterWorktreeUser(worktreePath, sessionId) {
|
|
68224
68503
|
const users = this.worktreeUsers.get(worktreePath);
|
|
@@ -68284,7 +68563,12 @@ class SessionManager extends EventEmitter4 {
|
|
|
68284
68563
|
offerContextPrompt: (s, q, f, e) => offerContextPrompt(s, q, f, this.getContextPromptHandler(), e),
|
|
68285
68564
|
emitSessionAdd: (s) => this.emitSessionAdd(s),
|
|
68286
68565
|
emitSessionUpdate: (sid, u) => this.emitSessionUpdate(sid, u),
|
|
68287
|
-
emitSessionRemove: (sid) => this.emitSessionRemove(sid)
|
|
68566
|
+
emitSessionRemove: (sid) => this.emitSessionRemove(sid),
|
|
68567
|
+
acquireClaudeAccount: (preferredId) => this.accountPool.acquire(preferredId),
|
|
68568
|
+
getClaudeAccount: (id) => this.accountPool.get(id),
|
|
68569
|
+
releaseClaudeAccount: (id) => this.accountPool.release(id),
|
|
68570
|
+
markClaudeAccountCooling: (id, untilMs) => this.accountPool.markCooling(id, untilMs),
|
|
68571
|
+
getClaudeAccountPoolStatus: () => this.accountPool.status()
|
|
68288
68572
|
};
|
|
68289
68573
|
return createSessionContext(config, state, ops);
|
|
68290
68574
|
}
|
|
@@ -68365,7 +68649,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
68365
68649
|
return false;
|
|
68366
68650
|
}
|
|
68367
68651
|
const shortId = persistedSession.threadId.substring(0, 8);
|
|
68368
|
-
|
|
68652
|
+
log29.info(`\uD83D\uDD04 Resuming session ${shortId}... via emoji reaction by @${username}`);
|
|
68369
68653
|
await resumeSession(persistedSession, this.getContext());
|
|
68370
68654
|
return true;
|
|
68371
68655
|
}
|
|
@@ -68395,7 +68679,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
68395
68679
|
}
|
|
68396
68680
|
if (session.lastError?.postId === postId && isBugReportEmoji(emojiName)) {
|
|
68397
68681
|
if (session.startedBy === username || session.platform.isUserAllowed(username) || session.sessionAllowedUsers.has(username)) {
|
|
68398
|
-
|
|
68682
|
+
log29.info(`\uD83D\uDC1B @${username} triggered bug report from error reaction`);
|
|
68399
68683
|
await reportBug(session, undefined, username, this.getContext(), session.lastError);
|
|
68400
68684
|
return;
|
|
68401
68685
|
}
|
|
@@ -68511,7 +68795,8 @@ class SessionManager extends EventEmitter4 {
|
|
|
68511
68795
|
sessionTags: session.sessionTags,
|
|
68512
68796
|
pullRequestUrl: session.pullRequestUrl,
|
|
68513
68797
|
messageCount: session.messageCount,
|
|
68514
|
-
resumeFailCount: session.lifecycle.resumeFailCount
|
|
68798
|
+
resumeFailCount: session.lifecycle.resumeFailCount,
|
|
68799
|
+
claudeAccountId: session.claudeAccountId
|
|
68515
68800
|
};
|
|
68516
68801
|
this.sessionStore.save(session.sessionId, state);
|
|
68517
68802
|
}
|
|
@@ -68536,7 +68821,8 @@ class SessionManager extends EventEmitter4 {
|
|
|
68536
68821
|
workingDir: this.workingDir,
|
|
68537
68822
|
debug: this.debug,
|
|
68538
68823
|
description: this.customDescription,
|
|
68539
|
-
footer: this.customFooter
|
|
68824
|
+
footer: this.customFooter,
|
|
68825
|
+
accountPoolStatus: this.accountPool.isEmpty ? undefined : this.accountPool.status()
|
|
68540
68826
|
});
|
|
68541
68827
|
}
|
|
68542
68828
|
async updateAllStickyMessages() {
|
|
@@ -68561,11 +68847,11 @@ class SessionManager extends EventEmitter4 {
|
|
|
68561
68847
|
}
|
|
68562
68848
|
}
|
|
68563
68849
|
if (sessionsToKill.length === 0) {
|
|
68564
|
-
|
|
68850
|
+
log29.info(`No active sessions to pause for platform ${platformId}`);
|
|
68565
68851
|
await this.updateStickyMessage();
|
|
68566
68852
|
return;
|
|
68567
68853
|
}
|
|
68568
|
-
|
|
68854
|
+
log29.info(`⏸️ Pausing ${sessionsToKill.length} session(s) for platform ${platformId}`);
|
|
68569
68855
|
for (const session of sessionsToKill) {
|
|
68570
68856
|
try {
|
|
68571
68857
|
const fmt = session.platform.getFormatter();
|
|
@@ -68581,9 +68867,9 @@ class SessionManager extends EventEmitter4 {
|
|
|
68581
68867
|
session.claude.kill();
|
|
68582
68868
|
this.registry.unregister(session.sessionId);
|
|
68583
68869
|
this.emitSessionRemove(session.sessionId);
|
|
68584
|
-
|
|
68870
|
+
log29.info(`⏸️ Paused session ${session.threadId.substring(0, 8)}`);
|
|
68585
68871
|
} catch (err) {
|
|
68586
|
-
|
|
68872
|
+
log29.warn(`Failed to pause session ${session.threadId}: ${err}`);
|
|
68587
68873
|
}
|
|
68588
68874
|
}
|
|
68589
68875
|
for (const session of sessionsToKill) {
|
|
@@ -68604,17 +68890,17 @@ class SessionManager extends EventEmitter4 {
|
|
|
68604
68890
|
sessionsToResume.push(state);
|
|
68605
68891
|
}
|
|
68606
68892
|
if (sessionsToResume.length === 0) {
|
|
68607
|
-
|
|
68893
|
+
log29.info(`No paused sessions to resume for platform ${platformId}`);
|
|
68608
68894
|
await this.updateStickyMessage();
|
|
68609
68895
|
return;
|
|
68610
68896
|
}
|
|
68611
|
-
|
|
68897
|
+
log29.info(`▶️ Resuming ${sessionsToResume.length} paused session(s) for platform ${platformId}`);
|
|
68612
68898
|
for (const state of sessionsToResume) {
|
|
68613
68899
|
try {
|
|
68614
68900
|
await resumeSession(state, this.getContext());
|
|
68615
|
-
|
|
68901
|
+
log29.info(`▶️ Resumed session ${state.threadId.substring(0, 8)}`);
|
|
68616
68902
|
} catch (err) {
|
|
68617
|
-
|
|
68903
|
+
log29.warn(`Failed to resume session ${state.threadId}: ${err}`);
|
|
68618
68904
|
}
|
|
68619
68905
|
}
|
|
68620
68906
|
await this.updateStickyMessage();
|
|
@@ -68626,14 +68912,14 @@ class SessionManager extends EventEmitter4 {
|
|
|
68626
68912
|
const sessionTimeoutMs = this.limits.sessionTimeoutMinutes * 60 * 1000;
|
|
68627
68913
|
const staleIds = this.sessionStore.cleanStale(sessionTimeoutMs * 2);
|
|
68628
68914
|
if (staleIds.length > 0) {
|
|
68629
|
-
|
|
68915
|
+
log29.info(`\uD83E\uDDF9 Soft-deleted ${staleIds.length} stale session(s) (kept for history)`);
|
|
68630
68916
|
}
|
|
68631
68917
|
const removedCount = this.sessionStore.cleanHistory();
|
|
68632
68918
|
if (removedCount > 0) {
|
|
68633
|
-
|
|
68919
|
+
log29.info(`\uD83D\uDDD1️ Permanently removed ${removedCount} old session(s) from history`);
|
|
68634
68920
|
}
|
|
68635
68921
|
const persisted = this.sessionStore.load();
|
|
68636
|
-
|
|
68922
|
+
log29.info(`\uD83D\uDCC2 Loaded ${persisted.size} session(s) from persistence`);
|
|
68637
68923
|
const excludePostIdsByPlatform = new Map;
|
|
68638
68924
|
for (const session of persisted.values()) {
|
|
68639
68925
|
const platformId = session.platformId;
|
|
@@ -68653,10 +68939,10 @@ class SessionManager extends EventEmitter4 {
|
|
|
68653
68939
|
const excludePostIds = excludePostIdsByPlatform.get(platform.platformId);
|
|
68654
68940
|
platform.getBotUser().then((botUser) => {
|
|
68655
68941
|
cleanupOldStickyMessages(platform, botUser.id, true, excludePostIds).catch((err) => {
|
|
68656
|
-
|
|
68942
|
+
log29.warn(`Failed to cleanup old sticky messages for ${platform.platformId}: ${err}`);
|
|
68657
68943
|
});
|
|
68658
68944
|
}).catch((err) => {
|
|
68659
|
-
|
|
68945
|
+
log29.warn(`Failed to get bot user for cleanup on ${platform.platformId}: ${err}`);
|
|
68660
68946
|
});
|
|
68661
68947
|
}
|
|
68662
68948
|
if (persisted.size > 0) {
|
|
@@ -68670,10 +68956,10 @@ class SessionManager extends EventEmitter4 {
|
|
|
68670
68956
|
}
|
|
68671
68957
|
}
|
|
68672
68958
|
if (pausedToSkip.length > 0) {
|
|
68673
|
-
|
|
68959
|
+
log29.info(`⏸️ ${pausedToSkip.length} session(s) remain paused (waiting for user message)`);
|
|
68674
68960
|
}
|
|
68675
68961
|
if (activeToResume.length > 0) {
|
|
68676
|
-
|
|
68962
|
+
log29.info(`\uD83D\uDD04 Attempting to resume ${activeToResume.length} active session(s)...`);
|
|
68677
68963
|
for (const state of activeToResume) {
|
|
68678
68964
|
await resumeSession(state, this.getContext());
|
|
68679
68965
|
}
|
|
@@ -69092,7 +69378,7 @@ Mention me to start a session in this worktree.`, threadId);
|
|
|
69092
69378
|
const message = messageBuilder(formatter);
|
|
69093
69379
|
await post(session, "info", message);
|
|
69094
69380
|
} catch (err) {
|
|
69095
|
-
|
|
69381
|
+
log29.warn(`Failed to broadcast to session ${session.threadId}: ${err}`);
|
|
69096
69382
|
}
|
|
69097
69383
|
}
|
|
69098
69384
|
}
|
|
@@ -69111,7 +69397,7 @@ Mention me to start a session in this worktree.`, threadId);
|
|
|
69111
69397
|
session.messageManager?.setPendingUpdatePrompt({ postId: post2.id });
|
|
69112
69398
|
this.registerPost(post2.id, session.threadId);
|
|
69113
69399
|
} catch (err) {
|
|
69114
|
-
|
|
69400
|
+
log29.warn(`Failed to post ask message to ${threadId}: ${err}`);
|
|
69115
69401
|
}
|
|
69116
69402
|
}
|
|
69117
69403
|
}
|
|
@@ -76706,29 +76992,29 @@ function SessionLog({ logs, maxLines = 20 }) {
|
|
|
76706
76992
|
return /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
|
|
76707
76993
|
flexDirection: "column",
|
|
76708
76994
|
flexShrink: 0,
|
|
76709
|
-
children: displayLogs.map((
|
|
76995
|
+
children: displayLogs.map((log30) => /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
|
|
76710
76996
|
flexShrink: 0,
|
|
76711
76997
|
children: [
|
|
76712
76998
|
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
|
|
76713
|
-
color: getColorForLevel(
|
|
76999
|
+
color: getColorForLevel(log30.level),
|
|
76714
77000
|
dimColor: true,
|
|
76715
77001
|
wrap: "truncate",
|
|
76716
77002
|
children: [
|
|
76717
77003
|
"[",
|
|
76718
|
-
padComponent(
|
|
77004
|
+
padComponent(log30.component),
|
|
76719
77005
|
"]"
|
|
76720
77006
|
]
|
|
76721
77007
|
}, undefined, true, undefined, this),
|
|
76722
77008
|
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
|
|
76723
|
-
color: getColorForLevel(
|
|
77009
|
+
color: getColorForLevel(log30.level),
|
|
76724
77010
|
wrap: "truncate",
|
|
76725
77011
|
children: [
|
|
76726
77012
|
" ",
|
|
76727
|
-
|
|
77013
|
+
log30.message
|
|
76728
77014
|
]
|
|
76729
77015
|
}, undefined, true, undefined, this)
|
|
76730
77016
|
]
|
|
76731
|
-
},
|
|
77017
|
+
}, log30.id, true, undefined, this))
|
|
76732
77018
|
}, undefined, false, undefined, this);
|
|
76733
77019
|
}
|
|
76734
77020
|
// src/ui/components/Footer.tsx
|
|
@@ -77226,7 +77512,7 @@ function LogPanel({ logs, maxLines = 10, focused = false }) {
|
|
|
77226
77512
|
const scrollRef = import_react59.default.useRef(null);
|
|
77227
77513
|
const { stdout } = use_stdout_default();
|
|
77228
77514
|
const isDebug = process.env.DEBUG === "1";
|
|
77229
|
-
const displayLogs = logs.filter((
|
|
77515
|
+
const displayLogs = logs.filter((log30) => isDebug || log30.level !== "debug");
|
|
77230
77516
|
const visibleLogs = displayLogs.slice(-Math.max(maxLines * 3, 100));
|
|
77231
77517
|
import_react59.default.useEffect(() => {
|
|
77232
77518
|
const handleResize = () => scrollRef.current?.remeasure();
|
|
@@ -77266,25 +77552,25 @@ function LogPanel({ logs, maxLines = 10, focused = false }) {
|
|
|
77266
77552
|
overflow: "hidden",
|
|
77267
77553
|
children: /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(ScrollView, {
|
|
77268
77554
|
ref: scrollRef,
|
|
77269
|
-
children: visibleLogs.map((
|
|
77555
|
+
children: visibleLogs.map((log30) => /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
|
|
77270
77556
|
children: [
|
|
77271
77557
|
/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
|
|
77272
77558
|
dimColor: true,
|
|
77273
77559
|
children: [
|
|
77274
77560
|
"[",
|
|
77275
|
-
padComponent2(
|
|
77561
|
+
padComponent2(log30.component),
|
|
77276
77562
|
"]"
|
|
77277
77563
|
]
|
|
77278
77564
|
}, undefined, true, undefined, this),
|
|
77279
77565
|
/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
|
|
77280
|
-
color: getLevelColor(
|
|
77566
|
+
color: getLevelColor(log30.level),
|
|
77281
77567
|
children: [
|
|
77282
77568
|
" ",
|
|
77283
|
-
|
|
77569
|
+
log30.message
|
|
77284
77570
|
]
|
|
77285
77571
|
}, undefined, true, undefined, this)
|
|
77286
77572
|
]
|
|
77287
|
-
},
|
|
77573
|
+
}, log30.id, true, undefined, this))
|
|
77288
77574
|
}, undefined, false, undefined, this)
|
|
77289
77575
|
}, undefined, false, undefined, this);
|
|
77290
77576
|
}
|
|
@@ -77801,10 +78087,10 @@ function useAppState(initialConfig) {
|
|
|
77801
78087
|
});
|
|
77802
78088
|
}, []);
|
|
77803
78089
|
const getLogsForSession = import_react60.useCallback((sessionId) => {
|
|
77804
|
-
return state.logs.filter((
|
|
78090
|
+
return state.logs.filter((log30) => log30.sessionId === sessionId);
|
|
77805
78091
|
}, [state.logs]);
|
|
77806
78092
|
const getGlobalLogs = import_react60.useCallback(() => {
|
|
77807
|
-
return state.logs.filter((
|
|
78093
|
+
return state.logs.filter((log30) => !log30.sessionId);
|
|
77808
78094
|
}, [state.logs]);
|
|
77809
78095
|
const togglePlatformEnabled = import_react60.useCallback((platformId) => {
|
|
77810
78096
|
let newEnabled = false;
|
|
@@ -78798,7 +79084,7 @@ import { EventEmitter as EventEmitter9 } from "events";
|
|
|
78798
79084
|
// src/auto-update/checker.ts
|
|
78799
79085
|
init_logger();
|
|
78800
79086
|
import { EventEmitter as EventEmitter7 } from "events";
|
|
78801
|
-
var
|
|
79087
|
+
var log30 = createLogger("checker");
|
|
78802
79088
|
var PACKAGE_NAME = "claude-threads";
|
|
78803
79089
|
function compareVersions(a, b) {
|
|
78804
79090
|
const partsA = a.replace(/^v/, "").split(".").map(Number);
|
|
@@ -78821,13 +79107,13 @@ async function fetchLatestVersion() {
|
|
|
78821
79107
|
}
|
|
78822
79108
|
});
|
|
78823
79109
|
if (!response.ok) {
|
|
78824
|
-
|
|
79110
|
+
log30.warn(`Failed to fetch latest version: HTTP ${response.status}`);
|
|
78825
79111
|
return null;
|
|
78826
79112
|
}
|
|
78827
79113
|
const data = await response.json();
|
|
78828
79114
|
return data.version ?? null;
|
|
78829
79115
|
} catch (err) {
|
|
78830
|
-
|
|
79116
|
+
log30.warn(`Failed to fetch latest version: ${err}`);
|
|
78831
79117
|
return null;
|
|
78832
79118
|
}
|
|
78833
79119
|
}
|
|
@@ -78844,38 +79130,38 @@ class UpdateChecker extends EventEmitter7 {
|
|
|
78844
79130
|
}
|
|
78845
79131
|
start() {
|
|
78846
79132
|
if (!this.config.enabled) {
|
|
78847
|
-
|
|
79133
|
+
log30.debug("Auto-update disabled, not starting checker");
|
|
78848
79134
|
return;
|
|
78849
79135
|
}
|
|
78850
79136
|
setTimeout(() => {
|
|
78851
79137
|
this.check().catch((err) => {
|
|
78852
|
-
|
|
79138
|
+
log30.warn(`Initial update check failed: ${err}`);
|
|
78853
79139
|
});
|
|
78854
79140
|
}, 5000);
|
|
78855
79141
|
const intervalMs = this.config.checkIntervalMinutes * 60 * 1000;
|
|
78856
79142
|
this.checkInterval = setInterval(() => {
|
|
78857
79143
|
this.check().catch((err) => {
|
|
78858
|
-
|
|
79144
|
+
log30.warn(`Periodic update check failed: ${err}`);
|
|
78859
79145
|
});
|
|
78860
79146
|
}, intervalMs);
|
|
78861
|
-
|
|
79147
|
+
log30.info(`\uD83D\uDD04 Update checker started (every ${this.config.checkIntervalMinutes} minutes)`);
|
|
78862
79148
|
}
|
|
78863
79149
|
stop() {
|
|
78864
79150
|
if (this.checkInterval) {
|
|
78865
79151
|
clearInterval(this.checkInterval);
|
|
78866
79152
|
this.checkInterval = null;
|
|
78867
79153
|
}
|
|
78868
|
-
|
|
79154
|
+
log30.debug("Update checker stopped");
|
|
78869
79155
|
}
|
|
78870
79156
|
async check() {
|
|
78871
79157
|
if (this.isChecking) {
|
|
78872
|
-
|
|
79158
|
+
log30.debug("Check already in progress, skipping");
|
|
78873
79159
|
return this.lastUpdateInfo;
|
|
78874
79160
|
}
|
|
78875
79161
|
this.isChecking = true;
|
|
78876
79162
|
this.emit("check:start");
|
|
78877
79163
|
try {
|
|
78878
|
-
|
|
79164
|
+
log30.debug("Checking for updates...");
|
|
78879
79165
|
const latestVersion2 = await fetchLatestVersion();
|
|
78880
79166
|
if (!latestVersion2) {
|
|
78881
79167
|
this.emit("check:complete", false);
|
|
@@ -78892,18 +79178,18 @@ class UpdateChecker extends EventEmitter7 {
|
|
|
78892
79178
|
detectedAt: new Date
|
|
78893
79179
|
};
|
|
78894
79180
|
if (!this.lastUpdateInfo || this.lastUpdateInfo.latestVersion !== latestVersion2) {
|
|
78895
|
-
|
|
79181
|
+
log30.info(`\uD83C\uDD95 Update available: v${currentVersion} → v${latestVersion2}`);
|
|
78896
79182
|
this.lastUpdateInfo = updateInfo;
|
|
78897
79183
|
this.emit("update", updateInfo);
|
|
78898
79184
|
}
|
|
78899
79185
|
this.emit("check:complete", true);
|
|
78900
79186
|
return updateInfo;
|
|
78901
79187
|
}
|
|
78902
|
-
|
|
79188
|
+
log30.debug(`Up to date (v${currentVersion})`);
|
|
78903
79189
|
this.emit("check:complete", false);
|
|
78904
79190
|
return null;
|
|
78905
79191
|
} catch (err) {
|
|
78906
|
-
|
|
79192
|
+
log30.warn(`Update check failed: ${err}`);
|
|
78907
79193
|
this.emit("check:error", err);
|
|
78908
79194
|
return null;
|
|
78909
79195
|
} finally {
|
|
@@ -78974,7 +79260,7 @@ function isInScheduledWindow(window2) {
|
|
|
78974
79260
|
}
|
|
78975
79261
|
|
|
78976
79262
|
// src/auto-update/scheduler.ts
|
|
78977
|
-
var
|
|
79263
|
+
var log31 = createLogger("scheduler");
|
|
78978
79264
|
|
|
78979
79265
|
class UpdateScheduler extends EventEmitter8 {
|
|
78980
79266
|
config;
|
|
@@ -78998,7 +79284,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
78998
79284
|
scheduleUpdate(updateInfo) {
|
|
78999
79285
|
this.pendingUpdate = updateInfo;
|
|
79000
79286
|
if (this.config.autoRestartMode === "immediate") {
|
|
79001
|
-
|
|
79287
|
+
log31.info("Immediate mode: triggering update now");
|
|
79002
79288
|
this.emit("ready", updateInfo);
|
|
79003
79289
|
return;
|
|
79004
79290
|
}
|
|
@@ -79011,19 +79297,19 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79011
79297
|
this.scheduledRestartAt = null;
|
|
79012
79298
|
this.askApprovals.clear();
|
|
79013
79299
|
this.askStartTime = null;
|
|
79014
|
-
|
|
79300
|
+
log31.debug("Update schedule cancelled");
|
|
79015
79301
|
}
|
|
79016
79302
|
deferUpdate(minutes) {
|
|
79017
79303
|
const deferUntil = new Date(Date.now() + minutes * 60 * 1000);
|
|
79018
79304
|
this.scheduledRestartAt = null;
|
|
79019
79305
|
this.idleStartTime = null;
|
|
79020
79306
|
this.emit("deferred", deferUntil);
|
|
79021
|
-
|
|
79307
|
+
log31.info(`Update deferred until ${deferUntil.toLocaleTimeString()}`);
|
|
79022
79308
|
return deferUntil;
|
|
79023
79309
|
}
|
|
79024
79310
|
recordAskResponse(threadId, approved) {
|
|
79025
79311
|
this.askApprovals.set(threadId, approved);
|
|
79026
|
-
|
|
79312
|
+
log31.debug(`Thread ${threadId.substring(0, 8)} ${approved ? "approved" : "denied"} update`);
|
|
79027
79313
|
this.checkAskCondition();
|
|
79028
79314
|
}
|
|
79029
79315
|
getScheduledRestartAt() {
|
|
@@ -79044,7 +79330,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79044
79330
|
return;
|
|
79045
79331
|
this.checkCondition();
|
|
79046
79332
|
this.checkTimer = setInterval(() => this.checkCondition(), 1e4);
|
|
79047
|
-
|
|
79333
|
+
log31.debug(`Started checking for ${this.config.autoRestartMode} condition`);
|
|
79048
79334
|
}
|
|
79049
79335
|
stopChecking() {
|
|
79050
79336
|
if (this.checkTimer) {
|
|
@@ -79075,17 +79361,17 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79075
79361
|
if (activity.activeSessionCount === 0) {
|
|
79076
79362
|
if (!this.idleStartTime) {
|
|
79077
79363
|
this.idleStartTime = new Date;
|
|
79078
|
-
|
|
79364
|
+
log31.debug("No active sessions, starting idle timer");
|
|
79079
79365
|
}
|
|
79080
79366
|
const idleMs = Date.now() - this.idleStartTime.getTime();
|
|
79081
79367
|
const requiredMs = this.config.idleTimeoutMinutes * 60 * 1000;
|
|
79082
79368
|
if (idleMs >= requiredMs) {
|
|
79083
|
-
|
|
79369
|
+
log31.info(`Idle for ${this.config.idleTimeoutMinutes} minutes, triggering update`);
|
|
79084
79370
|
this.triggerCountdown();
|
|
79085
79371
|
}
|
|
79086
79372
|
} else {
|
|
79087
79373
|
if (this.idleStartTime) {
|
|
79088
|
-
|
|
79374
|
+
log31.debug("Sessions became active, resetting idle timer");
|
|
79089
79375
|
this.idleStartTime = null;
|
|
79090
79376
|
}
|
|
79091
79377
|
}
|
|
@@ -79096,7 +79382,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79096
79382
|
const quietMs = Date.now() - activity.lastActivityAt.getTime();
|
|
79097
79383
|
const requiredMs = this.config.quietTimeoutMinutes * 60 * 1000;
|
|
79098
79384
|
if (quietMs >= requiredMs && !activity.anySessionBusy) {
|
|
79099
|
-
|
|
79385
|
+
log31.info(`Sessions quiet for ${this.config.quietTimeoutMinutes} minutes, triggering update`);
|
|
79100
79386
|
this.triggerCountdown();
|
|
79101
79387
|
}
|
|
79102
79388
|
} else if (activity.activeSessionCount === 0) {
|
|
@@ -79106,7 +79392,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79106
79392
|
const idleMs = Date.now() - this.idleStartTime.getTime();
|
|
79107
79393
|
const requiredMs = this.config.quietTimeoutMinutes * 60 * 1000;
|
|
79108
79394
|
if (idleMs >= requiredMs) {
|
|
79109
|
-
|
|
79395
|
+
log31.info("No sessions and quiet timeout reached, triggering update");
|
|
79110
79396
|
this.triggerCountdown();
|
|
79111
79397
|
}
|
|
79112
79398
|
}
|
|
@@ -79117,13 +79403,13 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79117
79403
|
}
|
|
79118
79404
|
const activity = this.getSessionActivity();
|
|
79119
79405
|
if (activity.activeSessionCount === 0) {
|
|
79120
|
-
|
|
79406
|
+
log31.info("Within scheduled window and no active sessions, triggering update");
|
|
79121
79407
|
this.triggerCountdown();
|
|
79122
79408
|
} else if (activity.lastActivityAt) {
|
|
79123
79409
|
const quietMs = Date.now() - activity.lastActivityAt.getTime();
|
|
79124
79410
|
const requiredMs = this.config.idleTimeoutMinutes * 60 * 1000;
|
|
79125
79411
|
if (quietMs >= requiredMs && !activity.anySessionBusy) {
|
|
79126
|
-
|
|
79412
|
+
log31.info("Within scheduled window and sessions quiet, triggering update");
|
|
79127
79413
|
this.triggerCountdown();
|
|
79128
79414
|
}
|
|
79129
79415
|
}
|
|
@@ -79131,14 +79417,14 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79131
79417
|
checkAskCondition() {
|
|
79132
79418
|
const threadIds = this.getActiveThreadIds();
|
|
79133
79419
|
if (threadIds.length === 0) {
|
|
79134
|
-
|
|
79420
|
+
log31.info("No active threads, proceeding with update");
|
|
79135
79421
|
this.triggerCountdown();
|
|
79136
79422
|
return;
|
|
79137
79423
|
}
|
|
79138
79424
|
if (!this.askStartTime && this.pendingUpdate) {
|
|
79139
79425
|
this.askStartTime = new Date;
|
|
79140
79426
|
this.postAskMessage(threadIds, this.pendingUpdate.latestVersion).catch((err) => {
|
|
79141
|
-
|
|
79427
|
+
log31.warn(`Failed to post ask message: ${err}`);
|
|
79142
79428
|
});
|
|
79143
79429
|
return;
|
|
79144
79430
|
}
|
|
@@ -79151,12 +79437,12 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79151
79437
|
denials++;
|
|
79152
79438
|
}
|
|
79153
79439
|
if (approvals > threadIds.length / 2) {
|
|
79154
|
-
|
|
79440
|
+
log31.info(`Majority approved (${approvals}/${threadIds.length}), triggering update`);
|
|
79155
79441
|
this.triggerCountdown();
|
|
79156
79442
|
return;
|
|
79157
79443
|
}
|
|
79158
79444
|
if (denials > threadIds.length / 2) {
|
|
79159
|
-
|
|
79445
|
+
log31.info(`Majority denied (${denials}/${threadIds.length}), deferring update`);
|
|
79160
79446
|
this.deferUpdate(60);
|
|
79161
79447
|
return;
|
|
79162
79448
|
}
|
|
@@ -79164,7 +79450,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79164
79450
|
const elapsedMs = Date.now() - this.askStartTime.getTime();
|
|
79165
79451
|
const timeoutMs = this.config.askTimeoutMinutes * 60 * 1000;
|
|
79166
79452
|
if (elapsedMs >= timeoutMs) {
|
|
79167
|
-
|
|
79453
|
+
log31.info(`Ask timeout reached (${this.config.askTimeoutMinutes} min), triggering update`);
|
|
79168
79454
|
this.triggerCountdown();
|
|
79169
79455
|
}
|
|
79170
79456
|
}
|
|
@@ -79184,7 +79470,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79184
79470
|
this.emit("ready", this.pendingUpdate);
|
|
79185
79471
|
}
|
|
79186
79472
|
}, 1000);
|
|
79187
|
-
|
|
79473
|
+
log31.info("Update countdown started (60 seconds)");
|
|
79188
79474
|
}
|
|
79189
79475
|
stopCountdown() {
|
|
79190
79476
|
if (this.countdownTimer) {
|
|
@@ -79200,24 +79486,24 @@ import { spawn as spawn4, spawnSync } from "child_process";
|
|
|
79200
79486
|
import { existsSync as existsSync13, readFileSync as readFileSync9, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4 } from "fs";
|
|
79201
79487
|
import { dirname as dirname8, resolve as resolve6 } from "path";
|
|
79202
79488
|
import { homedir as homedir5 } from "os";
|
|
79203
|
-
var
|
|
79489
|
+
var log32 = createLogger("installer");
|
|
79204
79490
|
function detectPackageManager() {
|
|
79205
79491
|
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
79206
79492
|
const originalInstaller = detectOriginalInstaller();
|
|
79207
79493
|
if (originalInstaller) {
|
|
79208
|
-
|
|
79494
|
+
log32.debug(`Detected original installer: ${originalInstaller}`);
|
|
79209
79495
|
if (originalInstaller === "bun") {
|
|
79210
79496
|
const bunCheck2 = spawnSync("bun", ["--version"], { stdio: "ignore" });
|
|
79211
79497
|
if (bunCheck2.status === 0) {
|
|
79212
79498
|
return { cmd: "bun", isBun: true };
|
|
79213
79499
|
}
|
|
79214
|
-
|
|
79500
|
+
log32.warn("Originally installed with bun, but bun not found. Falling back to npm.");
|
|
79215
79501
|
} else {
|
|
79216
79502
|
const npmCheck2 = spawnSync(npmCmd, ["--version"], { stdio: "ignore" });
|
|
79217
79503
|
if (npmCheck2.status === 0) {
|
|
79218
79504
|
return { cmd: npmCmd, isBun: false };
|
|
79219
79505
|
}
|
|
79220
|
-
|
|
79506
|
+
log32.warn("Originally installed with npm, but npm not found. Falling back to bun.");
|
|
79221
79507
|
}
|
|
79222
79508
|
}
|
|
79223
79509
|
const bunCheck = spawnSync("bun", ["--version"], { stdio: "ignore" });
|
|
@@ -79268,7 +79554,7 @@ function loadUpdateState() {
|
|
|
79268
79554
|
return JSON.parse(content);
|
|
79269
79555
|
}
|
|
79270
79556
|
} catch (err) {
|
|
79271
|
-
|
|
79557
|
+
log32.warn(`Failed to load update state: ${err}`);
|
|
79272
79558
|
}
|
|
79273
79559
|
return {};
|
|
79274
79560
|
}
|
|
@@ -79279,9 +79565,9 @@ function saveUpdateState(state) {
|
|
|
79279
79565
|
mkdirSync4(dir, { recursive: true });
|
|
79280
79566
|
}
|
|
79281
79567
|
writeFileSync5(STATE_PATH, JSON.stringify(state, null, 2), "utf-8");
|
|
79282
|
-
|
|
79568
|
+
log32.debug("Update state saved");
|
|
79283
79569
|
} catch (err) {
|
|
79284
|
-
|
|
79570
|
+
log32.warn(`Failed to save update state: ${err}`);
|
|
79285
79571
|
}
|
|
79286
79572
|
}
|
|
79287
79573
|
function clearUpdateState() {
|
|
@@ -79290,7 +79576,7 @@ function clearUpdateState() {
|
|
|
79290
79576
|
writeFileSync5(STATE_PATH, "{}", "utf-8");
|
|
79291
79577
|
}
|
|
79292
79578
|
} catch (err) {
|
|
79293
|
-
|
|
79579
|
+
log32.warn(`Failed to clear update state: ${err}`);
|
|
79294
79580
|
}
|
|
79295
79581
|
}
|
|
79296
79582
|
function checkJustUpdated() {
|
|
@@ -79322,11 +79608,11 @@ function clearRuntimeSettings() {
|
|
|
79322
79608
|
}
|
|
79323
79609
|
}
|
|
79324
79610
|
async function installVersion(version) {
|
|
79325
|
-
|
|
79611
|
+
log32.info(`\uD83D\uDCE6 Installing ${PACKAGE_NAME2}@${version}...`);
|
|
79326
79612
|
const pm = detectPackageManager();
|
|
79327
79613
|
if (!pm) {
|
|
79328
79614
|
const error = "Neither bun nor npm found in PATH. Cannot install update.";
|
|
79329
|
-
|
|
79615
|
+
log32.error(`❌ ${error}`);
|
|
79330
79616
|
return { success: false, error };
|
|
79331
79617
|
}
|
|
79332
79618
|
saveUpdateState({
|
|
@@ -79338,7 +79624,7 @@ async function installVersion(version) {
|
|
|
79338
79624
|
return new Promise((resolve7) => {
|
|
79339
79625
|
const { cmd, isBun: isBun3 } = pm;
|
|
79340
79626
|
const args = ["install", "-g", `${PACKAGE_NAME2}@${version}`];
|
|
79341
|
-
|
|
79627
|
+
log32.debug(`Using ${isBun3 ? "bun" : "npm"} for installation`);
|
|
79342
79628
|
const child = spawn4(cmd, args, {
|
|
79343
79629
|
stdio: ["ignore", "pipe", "pipe"],
|
|
79344
79630
|
env: {
|
|
@@ -79356,7 +79642,7 @@ async function installVersion(version) {
|
|
|
79356
79642
|
});
|
|
79357
79643
|
child.on("close", (code) => {
|
|
79358
79644
|
if (code === 0) {
|
|
79359
|
-
|
|
79645
|
+
log32.info(`✅ Successfully installed ${PACKAGE_NAME2}@${version}`);
|
|
79360
79646
|
saveUpdateState({
|
|
79361
79647
|
previousVersion: VERSION,
|
|
79362
79648
|
targetVersion: version,
|
|
@@ -79366,20 +79652,20 @@ async function installVersion(version) {
|
|
|
79366
79652
|
resolve7({ success: true });
|
|
79367
79653
|
} else {
|
|
79368
79654
|
const errorMsg = stderr || stdout || `Exit code: ${code}`;
|
|
79369
|
-
|
|
79655
|
+
log32.error(`❌ Installation failed: ${errorMsg}`);
|
|
79370
79656
|
clearUpdateState();
|
|
79371
79657
|
resolve7({ success: false, error: errorMsg });
|
|
79372
79658
|
}
|
|
79373
79659
|
});
|
|
79374
79660
|
child.on("error", (err) => {
|
|
79375
|
-
|
|
79661
|
+
log32.error(`❌ Failed to spawn npm: ${err}`);
|
|
79376
79662
|
clearUpdateState();
|
|
79377
79663
|
resolve7({ success: false, error: err.message });
|
|
79378
79664
|
});
|
|
79379
79665
|
setTimeout(() => {
|
|
79380
79666
|
if (child.exitCode === null) {
|
|
79381
79667
|
child.kill();
|
|
79382
|
-
|
|
79668
|
+
log32.error("❌ Installation timed out");
|
|
79383
79669
|
clearUpdateState();
|
|
79384
79670
|
resolve7({ success: false, error: "Installation timed out" });
|
|
79385
79671
|
}
|
|
@@ -79421,7 +79707,7 @@ class UpdateInstaller {
|
|
|
79421
79707
|
}
|
|
79422
79708
|
|
|
79423
79709
|
// src/auto-update/manager.ts
|
|
79424
|
-
var
|
|
79710
|
+
var log33 = createLogger("updater");
|
|
79425
79711
|
|
|
79426
79712
|
class AutoUpdateManager extends EventEmitter9 {
|
|
79427
79713
|
config;
|
|
@@ -79444,23 +79730,23 @@ class AutoUpdateManager extends EventEmitter9 {
|
|
|
79444
79730
|
}
|
|
79445
79731
|
start() {
|
|
79446
79732
|
if (!this.config.enabled) {
|
|
79447
|
-
|
|
79733
|
+
log33.info("Auto-update is disabled");
|
|
79448
79734
|
return;
|
|
79449
79735
|
}
|
|
79450
79736
|
const updateResult = this.installer.checkJustUpdated();
|
|
79451
79737
|
if (updateResult) {
|
|
79452
|
-
|
|
79738
|
+
log33.info(`\uD83C\uDF89 Updated from v${updateResult.previousVersion} to v${updateResult.currentVersion}`);
|
|
79453
79739
|
this.callbacks.broadcastUpdate((fmt) => `\uD83C\uDF89 ${fmt.formatBold("Bot updated")} from v${updateResult.previousVersion} to v${updateResult.currentVersion}`).catch((err) => {
|
|
79454
|
-
|
|
79740
|
+
log33.warn(`Failed to broadcast update notification: ${err}`);
|
|
79455
79741
|
});
|
|
79456
79742
|
}
|
|
79457
79743
|
this.checker.start();
|
|
79458
|
-
|
|
79744
|
+
log33.info(`\uD83D\uDD04 Auto-update manager started (mode: ${this.config.autoRestartMode})`);
|
|
79459
79745
|
}
|
|
79460
79746
|
stop() {
|
|
79461
79747
|
this.checker.stop();
|
|
79462
79748
|
this.scheduler.stop();
|
|
79463
|
-
|
|
79749
|
+
log33.debug("Auto-update manager stopped");
|
|
79464
79750
|
}
|
|
79465
79751
|
getState() {
|
|
79466
79752
|
return { ...this.state };
|
|
@@ -79474,10 +79760,10 @@ class AutoUpdateManager extends EventEmitter9 {
|
|
|
79474
79760
|
async forceUpdate() {
|
|
79475
79761
|
const updateInfo = this.state.updateInfo || await this.checker.check();
|
|
79476
79762
|
if (!updateInfo) {
|
|
79477
|
-
|
|
79763
|
+
log33.info("No update available");
|
|
79478
79764
|
return;
|
|
79479
79765
|
}
|
|
79480
|
-
|
|
79766
|
+
log33.info("Forcing immediate update");
|
|
79481
79767
|
await this.performUpdate(updateInfo);
|
|
79482
79768
|
}
|
|
79483
79769
|
deferUpdate(minutes = 60) {
|
|
@@ -79532,7 +79818,7 @@ class AutoUpdateManager extends EventEmitter9 {
|
|
|
79532
79818
|
await this.callbacks.broadcastUpdate((fmt) => `✅ ${fmt.formatBold("Update installed")} - restarting now. ${fmt.formatItalic("Sessions will resume automatically.")}`).catch(() => {});
|
|
79533
79819
|
await new Promise((resolve7) => setTimeout(resolve7, 1000));
|
|
79534
79820
|
await this.callbacks.prepareForRestart();
|
|
79535
|
-
|
|
79821
|
+
log33.info(`\uD83D\uDD04 Restarting for update to v${updateInfo.latestVersion}`);
|
|
79536
79822
|
process.stdout.write("\x1B[2J\x1B[H");
|
|
79537
79823
|
process.stdout.write("\x1B[?25h");
|
|
79538
79824
|
process.exit(RESTART_EXIT_CODE);
|
|
@@ -79827,7 +80113,7 @@ async function startWithoutDaemon() {
|
|
|
79827
80113
|
keepAlive.setEnabled(keepAliveEnabled);
|
|
79828
80114
|
const threadLogsEnabled = config.threadLogs?.enabled ?? true;
|
|
79829
80115
|
const threadLogsRetentionDays = config.threadLogs?.retentionDays ?? 30;
|
|
79830
|
-
const session = new SessionManager(workingDir, initialSkipPermissions, config.chrome, config.worktreeMode, undefined, threadLogsEnabled, threadLogsRetentionDays, config.limits);
|
|
80116
|
+
const session = new SessionManager(workingDir, initialSkipPermissions, config.chrome, config.worktreeMode, undefined, threadLogsEnabled, threadLogsRetentionDays, config.limits, config.claudeAccounts);
|
|
79831
80117
|
if (config.stickyMessage) {
|
|
79832
80118
|
session.setStickyMessageCustomization(config.stickyMessage.description, config.stickyMessage.footer);
|
|
79833
80119
|
}
|