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