claude-threads 1.8.3 → 1.9.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 +35 -0
- package/README.md +1 -1
- package/dist/index.js +768 -566
- package/dist/mcp/permission-server.js +2967 -263
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11862,7 +11862,7 @@ import * as fs from "fs/promises";
|
|
|
11862
11862
|
import { homedir as homedir4 } from "os";
|
|
11863
11863
|
async function execGit(args, cwd) {
|
|
11864
11864
|
const cmd = `git ${args.join(" ")}`;
|
|
11865
|
-
|
|
11865
|
+
log7.debug(`Executing: ${cmd}`);
|
|
11866
11866
|
return new Promise((resolve3, reject) => {
|
|
11867
11867
|
const proc = crossSpawn("git", args, { cwd });
|
|
11868
11868
|
let stdout = "";
|
|
@@ -11875,15 +11875,15 @@ async function execGit(args, cwd) {
|
|
|
11875
11875
|
});
|
|
11876
11876
|
proc.on("close", (code) => {
|
|
11877
11877
|
if (code === 0) {
|
|
11878
|
-
|
|
11878
|
+
log7.debug(`${cmd} → success`);
|
|
11879
11879
|
resolve3(stdout.trim());
|
|
11880
11880
|
} else {
|
|
11881
|
-
|
|
11881
|
+
log7.debug(`${cmd} → failed (code=${code}): ${stderr.substring(0, 100) || stdout.substring(0, 100)}`);
|
|
11882
11882
|
reject(new Error(`git ${args.join(" ")} failed: ${stderr || stdout}`));
|
|
11883
11883
|
}
|
|
11884
11884
|
});
|
|
11885
11885
|
proc.on("error", (err) => {
|
|
11886
|
-
|
|
11886
|
+
log7.warn(`${cmd} → error: ${err}`);
|
|
11887
11887
|
reject(err);
|
|
11888
11888
|
});
|
|
11889
11889
|
});
|
|
@@ -11893,7 +11893,7 @@ async function isGitRepository(dir) {
|
|
|
11893
11893
|
await execGit(["rev-parse", "--git-dir"], dir);
|
|
11894
11894
|
return true;
|
|
11895
11895
|
} catch (err) {
|
|
11896
|
-
|
|
11896
|
+
log7.debug(`Not a git repository: ${dir} (${err})`);
|
|
11897
11897
|
return false;
|
|
11898
11898
|
}
|
|
11899
11899
|
}
|
|
@@ -12028,7 +12028,7 @@ async function detectWorktreeInfo(workingDir) {
|
|
|
12028
12028
|
const branchOutput = await execGit(["rev-parse", "--abbrev-ref", "HEAD"], workingDir);
|
|
12029
12029
|
const branch = branchOutput?.trim();
|
|
12030
12030
|
if (!branch) {
|
|
12031
|
-
|
|
12031
|
+
log7.debug(`Could not detect branch for worktree at ${workingDir}`);
|
|
12032
12032
|
return null;
|
|
12033
12033
|
}
|
|
12034
12034
|
const gitDirOutput = await execGit(["rev-parse", "--git-common-dir"], workingDir);
|
|
@@ -12040,45 +12040,45 @@ async function detectWorktreeInfo(workingDir) {
|
|
|
12040
12040
|
repoRoot = repoRoot.slice(0, -4);
|
|
12041
12041
|
}
|
|
12042
12042
|
}
|
|
12043
|
-
|
|
12043
|
+
log7.debug(`Detected worktree: path=${workingDir}, branch=${branch}, repoRoot=${repoRoot}`);
|
|
12044
12044
|
return {
|
|
12045
12045
|
worktreePath: workingDir,
|
|
12046
12046
|
branch,
|
|
12047
12047
|
repoRoot: repoRoot || workingDir
|
|
12048
12048
|
};
|
|
12049
12049
|
} catch (err) {
|
|
12050
|
-
|
|
12050
|
+
log7.debug(`Failed to detect worktree info for ${workingDir}: ${err}`);
|
|
12051
12051
|
return null;
|
|
12052
12052
|
}
|
|
12053
12053
|
}
|
|
12054
12054
|
async function createWorktree(repoRoot, branch, targetDir) {
|
|
12055
|
-
|
|
12055
|
+
log7.info(`Creating worktree for branch '${branch}' at ${targetDir}`);
|
|
12056
12056
|
const parentDir = path.dirname(targetDir);
|
|
12057
|
-
|
|
12057
|
+
log7.debug(`Creating parent directory: ${parentDir}`);
|
|
12058
12058
|
await fs.mkdir(parentDir, { recursive: true });
|
|
12059
12059
|
const exists = await branchExists(repoRoot, branch);
|
|
12060
12060
|
if (exists) {
|
|
12061
|
-
|
|
12061
|
+
log7.debug(`Branch '${branch}' exists, adding worktree`);
|
|
12062
12062
|
await execGit(["worktree", "add", targetDir, branch], repoRoot);
|
|
12063
12063
|
} else {
|
|
12064
|
-
|
|
12064
|
+
log7.debug(`Branch '${branch}' does not exist, creating with worktree`);
|
|
12065
12065
|
await execGit(["worktree", "add", "-b", branch, targetDir], repoRoot);
|
|
12066
12066
|
}
|
|
12067
|
-
|
|
12067
|
+
log7.info(`Worktree created successfully: ${targetDir}`);
|
|
12068
12068
|
return targetDir;
|
|
12069
12069
|
}
|
|
12070
12070
|
async function removeWorktree(repoRoot, worktreePath) {
|
|
12071
|
-
|
|
12071
|
+
log7.info(`Removing worktree: ${worktreePath}`);
|
|
12072
12072
|
try {
|
|
12073
12073
|
await execGit(["worktree", "remove", worktreePath], repoRoot);
|
|
12074
|
-
|
|
12074
|
+
log7.debug("Worktree removed cleanly");
|
|
12075
12075
|
} catch (err) {
|
|
12076
|
-
|
|
12076
|
+
log7.debug(`Clean remove failed (${err}), trying force remove`);
|
|
12077
12077
|
await execGit(["worktree", "remove", "--force", worktreePath], repoRoot);
|
|
12078
12078
|
}
|
|
12079
|
-
|
|
12079
|
+
log7.debug("Pruning stale worktree references");
|
|
12080
12080
|
await execGit(["worktree", "prune"], repoRoot);
|
|
12081
|
-
|
|
12081
|
+
log7.info("Worktree removed and pruned successfully");
|
|
12082
12082
|
}
|
|
12083
12083
|
async function findWorktreeByBranch(repoRoot, branch) {
|
|
12084
12084
|
const worktrees = await listWorktrees(repoRoot);
|
|
@@ -12119,14 +12119,14 @@ async function writeMetadataStore(store) {
|
|
|
12119
12119
|
await fs.writeFile(METADATA_STORE_PATH, JSON.stringify(store, null, 2), { encoding: "utf-8", mode: 384 });
|
|
12120
12120
|
await fs.chmod(METADATA_STORE_PATH, 384);
|
|
12121
12121
|
} catch (err) {
|
|
12122
|
-
|
|
12122
|
+
log7.warn(`Failed to write worktree metadata store: ${err}`);
|
|
12123
12123
|
}
|
|
12124
12124
|
}
|
|
12125
12125
|
async function writeWorktreeMetadata(worktreePath, metadata) {
|
|
12126
12126
|
const store = await readMetadataStore();
|
|
12127
12127
|
store[worktreePath] = metadata;
|
|
12128
12128
|
await writeMetadataStore(store);
|
|
12129
|
-
|
|
12129
|
+
log7.debug(`Wrote worktree metadata for: ${worktreePath}`);
|
|
12130
12130
|
}
|
|
12131
12131
|
async function readWorktreeMetadata(worktreePath) {
|
|
12132
12132
|
const store = await readMetadataStore();
|
|
@@ -12149,14 +12149,14 @@ async function removeWorktreeMetadata(worktreePath) {
|
|
|
12149
12149
|
if (store[worktreePath]) {
|
|
12150
12150
|
delete store[worktreePath];
|
|
12151
12151
|
await writeMetadataStore(store);
|
|
12152
|
-
|
|
12152
|
+
log7.debug(`Removed worktree metadata for: ${worktreePath}`);
|
|
12153
12153
|
}
|
|
12154
12154
|
}
|
|
12155
|
-
var
|
|
12155
|
+
var log7, WORKTREES_DIR, METADATA_STORE_PATH;
|
|
12156
12156
|
var init_worktree = __esm(() => {
|
|
12157
12157
|
init_spawn();
|
|
12158
12158
|
init_logger();
|
|
12159
|
-
|
|
12159
|
+
log7 = createLogger("git-wt");
|
|
12160
12160
|
WORKTREES_DIR = path.join(homedir4(), ".claude-threads", "worktrees");
|
|
12161
12161
|
METADATA_STORE_PATH = path.join(homedir4(), ".claude-threads", "worktree-metadata.json");
|
|
12162
12162
|
});
|
|
@@ -19496,7 +19496,7 @@ var require_react_reconciler_development = __commonJS((exports, module) => {
|
|
|
19496
19496
|
return hook.checkDCE ? true : false;
|
|
19497
19497
|
}
|
|
19498
19498
|
function setIsStrictModeForDevtools(newIsStrictMode) {
|
|
19499
|
-
typeof
|
|
19499
|
+
typeof log30 === "function" && unstable_setDisableYieldValue2(newIsStrictMode);
|
|
19500
19500
|
if (injectedHook && typeof injectedHook.setStrictMode === "function")
|
|
19501
19501
|
try {
|
|
19502
19502
|
injectedHook.setStrictMode(rendererID, newIsStrictMode);
|
|
@@ -27580,7 +27580,7 @@ Check the render method of %s.`, getComponentNameFromFiber(current) || "Unknown"
|
|
|
27580
27580
|
var fiberStack = [];
|
|
27581
27581
|
var index$jscomp$0 = -1, emptyContextObject = {};
|
|
27582
27582
|
Object.freeze(emptyContextObject);
|
|
27583
|
-
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,
|
|
27583
|
+
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, log30 = Scheduler.log, unstable_setDisableYieldValue2 = Scheduler.unstable_setDisableYieldValue, rendererID = null, injectedHook = null, hasLoggedError = false, isDevToolsPresent = typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== "undefined", lastResetTime = 0;
|
|
27584
27584
|
if (typeof performance === "object" && typeof performance.now === "function") {
|
|
27585
27585
|
var localPerformance = performance;
|
|
27586
27586
|
var getCurrentTime = function() {
|
|
@@ -47101,7 +47101,7 @@ var {
|
|
|
47101
47101
|
Help
|
|
47102
47102
|
} = import__.default;
|
|
47103
47103
|
|
|
47104
|
-
// src/config/
|
|
47104
|
+
// src/config/index.ts
|
|
47105
47105
|
import { existsSync, readFileSync, writeFileSync, mkdirSync, chmodSync } from "fs";
|
|
47106
47106
|
import { resolve, dirname } from "path";
|
|
47107
47107
|
import { homedir } from "os";
|
|
@@ -49799,7 +49799,8 @@ var LIMITS_DEFAULTS = {
|
|
|
49799
49799
|
cleanupIntervalMinutes: 60,
|
|
49800
49800
|
maxWorktreeAgeHours: 24,
|
|
49801
49801
|
cleanupWorktrees: true,
|
|
49802
|
-
permissionTimeoutSeconds: 120
|
|
49802
|
+
permissionTimeoutSeconds: 120,
|
|
49803
|
+
flushDelayMs: 500
|
|
49803
49804
|
};
|
|
49804
49805
|
function resolveLimits(limits) {
|
|
49805
49806
|
const envMaxSessions = process.env.MAX_SESSIONS ? parseInt(process.env.MAX_SESSIONS, 10) : undefined;
|
|
@@ -49811,11 +49812,52 @@ function resolveLimits(limits) {
|
|
|
49811
49812
|
cleanupIntervalMinutes: limits?.cleanupIntervalMinutes ?? LIMITS_DEFAULTS.cleanupIntervalMinutes,
|
|
49812
49813
|
maxWorktreeAgeHours: limits?.maxWorktreeAgeHours ?? LIMITS_DEFAULTS.maxWorktreeAgeHours,
|
|
49813
49814
|
cleanupWorktrees: limits?.cleanupWorktrees ?? LIMITS_DEFAULTS.cleanupWorktrees,
|
|
49814
|
-
permissionTimeoutSeconds: limits?.permissionTimeoutSeconds ?? LIMITS_DEFAULTS.permissionTimeoutSeconds
|
|
49815
|
+
permissionTimeoutSeconds: limits?.permissionTimeoutSeconds ?? LIMITS_DEFAULTS.permissionTimeoutSeconds,
|
|
49816
|
+
flushDelayMs: limits?.flushDelayMs ?? LIMITS_DEFAULTS.flushDelayMs
|
|
49815
49817
|
};
|
|
49816
49818
|
}
|
|
49819
|
+
function resolvePermissionMode(opts) {
|
|
49820
|
+
if (opts.permissionMode)
|
|
49821
|
+
return opts.permissionMode;
|
|
49822
|
+
if (opts.skipPermissions === true)
|
|
49823
|
+
return "bypass";
|
|
49824
|
+
if (opts.skipPermissions === false)
|
|
49825
|
+
return "default";
|
|
49826
|
+
return "default";
|
|
49827
|
+
}
|
|
49828
|
+
var MODE_INFO = {
|
|
49829
|
+
default: {
|
|
49830
|
+
icon: "\uD83D\uDD10",
|
|
49831
|
+
label: "Default",
|
|
49832
|
+
description: "Every tool-use prompts for approval."
|
|
49833
|
+
},
|
|
49834
|
+
auto: {
|
|
49835
|
+
icon: "⚡",
|
|
49836
|
+
label: "Auto",
|
|
49837
|
+
description: "Claude classifier auto-approves low-risk tools; high-risk still prompts."
|
|
49838
|
+
},
|
|
49839
|
+
bypass: {
|
|
49840
|
+
icon: "⚠️",
|
|
49841
|
+
label: "Bypass",
|
|
49842
|
+
description: "No prompts — every tool-use is allowed."
|
|
49843
|
+
}
|
|
49844
|
+
};
|
|
49845
|
+
function permissionModeDisplay(mode) {
|
|
49846
|
+
const info = MODE_INFO[mode];
|
|
49847
|
+
return { icon: info.icon, label: info.label, chip: `${info.icon} ${info.label}` };
|
|
49848
|
+
}
|
|
49849
|
+
function permissionModeDescription(mode) {
|
|
49850
|
+
return MODE_INFO[mode].description;
|
|
49851
|
+
}
|
|
49852
|
+
function effectivePermissionMode(input) {
|
|
49853
|
+
if (input.override)
|
|
49854
|
+
return input.override;
|
|
49855
|
+
if (input.sessionHasInteractiveOverride)
|
|
49856
|
+
return "default";
|
|
49857
|
+
return input.botWideMode;
|
|
49858
|
+
}
|
|
49817
49859
|
|
|
49818
|
-
// src/config/
|
|
49860
|
+
// src/config/index.ts
|
|
49819
49861
|
var CONFIG_PATH = resolve(homedir(), ".config", "claude-threads", "config.yaml");
|
|
49820
49862
|
function loadConfigWithMigration() {
|
|
49821
49863
|
if (existsSync(CONFIG_PATH)) {
|
|
@@ -50034,6 +50076,26 @@ function validateClaudeCli() {
|
|
|
50034
50076
|
}
|
|
50035
50077
|
|
|
50036
50078
|
// src/onboarding.ts
|
|
50079
|
+
var PERMISSION_MODE_CHOICES = [
|
|
50080
|
+
{
|
|
50081
|
+
title: "Auto (recommended)",
|
|
50082
|
+
value: "auto",
|
|
50083
|
+
description: "Classifier auto-approves low-risk; high-risk tools still prompt via reactions"
|
|
50084
|
+
},
|
|
50085
|
+
{
|
|
50086
|
+
title: "Default",
|
|
50087
|
+
value: "default",
|
|
50088
|
+
description: "Every tool-use prompts — strictest mode, can be noisy for everyday work"
|
|
50089
|
+
},
|
|
50090
|
+
{
|
|
50091
|
+
title: "Bypass",
|
|
50092
|
+
value: "bypass",
|
|
50093
|
+
description: "No prompts, all tools allowed — use only in trusted environments"
|
|
50094
|
+
}
|
|
50095
|
+
];
|
|
50096
|
+
function permissionModeChoiceIndex(mode) {
|
|
50097
|
+
return PERMISSION_MODE_CHOICES.findIndex((c) => c.value === mode);
|
|
50098
|
+
}
|
|
50037
50099
|
var __dirname2 = dirname2(fileURLToPath(import.meta.url));
|
|
50038
50100
|
var SLACK_MANIFEST_PATH = join2(__dirname2, "..", "docs", "slack-app-manifest.yaml");
|
|
50039
50101
|
var onCancel = () => {
|
|
@@ -50600,14 +50662,14 @@ async function showConfigSummary(config) {
|
|
|
50600
50662
|
console.log(dim(` Bot: @${mm.botName}`));
|
|
50601
50663
|
const allowedUsers = mm.allowedUsers.length > 0 ? mm.allowedUsers.join(", ") : "ANYONE (⚠️ no restrictions)";
|
|
50602
50664
|
console.log(dim(` Allowed Users: ${allowedUsers}`));
|
|
50603
|
-
console.log(dim(`
|
|
50665
|
+
console.log(dim(` Permission Mode: ${permissionModeDisplay(resolvePermissionMode({ permissionMode: mm.permissionMode, skipPermissions: mm.skipPermissions })).chip}`));
|
|
50604
50666
|
} else {
|
|
50605
50667
|
const slack = platform;
|
|
50606
50668
|
console.log(dim(` Channel: ${slack.channelId}`));
|
|
50607
50669
|
console.log(dim(` Bot: @${slack.botName}`));
|
|
50608
50670
|
const allowedUsers = slack.allowedUsers.length > 0 ? slack.allowedUsers.join(", ") : "ANYONE (⚠️ no restrictions)";
|
|
50609
50671
|
console.log(dim(` Allowed Users: ${allowedUsers}`));
|
|
50610
|
-
console.log(dim(`
|
|
50672
|
+
console.log(dim(` Permission Mode: ${permissionModeDisplay(resolvePermissionMode({ permissionMode: slack.permissionMode, skipPermissions: slack.skipPermissions })).chip}`));
|
|
50611
50673
|
}
|
|
50612
50674
|
}
|
|
50613
50675
|
const hasAdvancedSettings = config.limits || config.threadLogs || config.keepAlive === false;
|
|
@@ -50799,7 +50861,10 @@ async function setupMattermostPlatform(id, existing) {
|
|
|
50799
50861
|
let lastChannelId = existingMattermost?.channelId || "";
|
|
50800
50862
|
let lastBotName = existingMattermost?.botName || "claude-code";
|
|
50801
50863
|
let lastAllowedUsers = existingMattermost?.allowedUsers?.join(",") || "";
|
|
50802
|
-
let
|
|
50864
|
+
let lastPermissionMode = existingMattermost ? resolvePermissionMode({
|
|
50865
|
+
permissionMode: existingMattermost.permissionMode,
|
|
50866
|
+
skipPermissions: existingMattermost.skipPermissions
|
|
50867
|
+
}) : "auto";
|
|
50803
50868
|
while (true) {
|
|
50804
50869
|
console.log("");
|
|
50805
50870
|
console.log(dim(" Now enter your Mattermost credentials:"));
|
|
@@ -50897,12 +50962,12 @@ async function setupMattermostPlatform(id, existing) {
|
|
|
50897
50962
|
allowedUsersConfirmed = true;
|
|
50898
50963
|
}
|
|
50899
50964
|
}
|
|
50900
|
-
const {
|
|
50901
|
-
type: "
|
|
50902
|
-
name: "
|
|
50903
|
-
message: "
|
|
50904
|
-
|
|
50905
|
-
|
|
50965
|
+
const { permissionMode } = await import_prompts.default({
|
|
50966
|
+
type: "select",
|
|
50967
|
+
name: "permissionMode",
|
|
50968
|
+
message: "Permission mode for Claude tool-uses?",
|
|
50969
|
+
choices: PERMISSION_MODE_CHOICES,
|
|
50970
|
+
initial: permissionModeChoiceIndex(lastPermissionMode)
|
|
50906
50971
|
}, { onCancel });
|
|
50907
50972
|
lastUrl = basicSettings.url;
|
|
50908
50973
|
lastDisplayName = basicSettings.displayName;
|
|
@@ -50910,7 +50975,7 @@ async function setupMattermostPlatform(id, existing) {
|
|
|
50910
50975
|
lastChannelId = basicSettings.channelId;
|
|
50911
50976
|
lastBotName = basicSettings.botName;
|
|
50912
50977
|
lastAllowedUsers = allowedUsers.join(",");
|
|
50913
|
-
|
|
50978
|
+
lastPermissionMode = permissionMode;
|
|
50914
50979
|
console.log("");
|
|
50915
50980
|
console.log(dim(" Validating credentials..."));
|
|
50916
50981
|
const validationResult = await validateMattermostCredentials(basicSettings.url, finalToken, basicSettings.channelId);
|
|
@@ -50969,7 +51034,7 @@ async function setupMattermostPlatform(id, existing) {
|
|
|
50969
51034
|
channelId: basicSettings.channelId,
|
|
50970
51035
|
botName: basicSettings.botName,
|
|
50971
51036
|
allowedUsers,
|
|
50972
|
-
|
|
51037
|
+
permissionMode: lastPermissionMode
|
|
50973
51038
|
};
|
|
50974
51039
|
}
|
|
50975
51040
|
}
|
|
@@ -51082,7 +51147,10 @@ async function setupSlackPlatform(id, existing) {
|
|
|
51082
51147
|
let lastChannelId = existingSlack?.channelId || "";
|
|
51083
51148
|
let lastBotName = existingSlack?.botName || "claude";
|
|
51084
51149
|
let lastAllowedUsers = existingSlack?.allowedUsers?.join(",") || "";
|
|
51085
|
-
let
|
|
51150
|
+
let lastPermissionMode = existingSlack ? resolvePermissionMode({
|
|
51151
|
+
permissionMode: existingSlack.permissionMode,
|
|
51152
|
+
skipPermissions: existingSlack.skipPermissions
|
|
51153
|
+
}) : "auto";
|
|
51086
51154
|
while (true) {
|
|
51087
51155
|
console.log("");
|
|
51088
51156
|
console.log(dim(" Now enter your Slack credentials:"));
|
|
@@ -51191,12 +51259,12 @@ async function setupSlackPlatform(id, existing) {
|
|
|
51191
51259
|
allowedUsersConfirmed = true;
|
|
51192
51260
|
}
|
|
51193
51261
|
}
|
|
51194
|
-
const {
|
|
51195
|
-
type: "
|
|
51196
|
-
name: "
|
|
51197
|
-
message: "
|
|
51198
|
-
|
|
51199
|
-
|
|
51262
|
+
const { permissionMode } = await import_prompts.default({
|
|
51263
|
+
type: "select",
|
|
51264
|
+
name: "permissionMode",
|
|
51265
|
+
message: "Permission mode for Claude tool-uses?",
|
|
51266
|
+
choices: PERMISSION_MODE_CHOICES,
|
|
51267
|
+
initial: permissionModeChoiceIndex(lastPermissionMode)
|
|
51200
51268
|
}, { onCancel });
|
|
51201
51269
|
lastDisplayName = basicSettings.displayName;
|
|
51202
51270
|
lastBotToken = finalBotToken;
|
|
@@ -51204,7 +51272,7 @@ async function setupSlackPlatform(id, existing) {
|
|
|
51204
51272
|
lastChannelId = basicSettings.channelId;
|
|
51205
51273
|
lastBotName = basicSettings.botName;
|
|
51206
51274
|
lastAllowedUsers = allowedUsers.join(",");
|
|
51207
|
-
|
|
51275
|
+
lastPermissionMode = permissionMode;
|
|
51208
51276
|
console.log("");
|
|
51209
51277
|
console.log(dim(" Validating credentials..."));
|
|
51210
51278
|
const validationResult = await validateSlackCredentials(finalBotToken, finalAppToken, basicSettings.channelId);
|
|
@@ -51269,7 +51337,7 @@ async function setupSlackPlatform(id, existing) {
|
|
|
51269
51337
|
channelId: basicSettings.channelId,
|
|
51270
51338
|
botName: basicSettings.botName,
|
|
51271
51339
|
allowedUsers,
|
|
51272
|
-
|
|
51340
|
+
permissionMode: lastPermissionMode
|
|
51273
51341
|
};
|
|
51274
51342
|
}
|
|
51275
51343
|
}
|
|
@@ -52290,7 +52358,7 @@ class SlackClient extends BasePlatformClient {
|
|
|
52290
52358
|
this.channelId = platformConfig.channelId;
|
|
52291
52359
|
this.botName = platformConfig.botName;
|
|
52292
52360
|
this.allowedUsers = platformConfig.allowedUsers;
|
|
52293
|
-
this.skipPermissions = platformConfig.skipPermissions;
|
|
52361
|
+
this.skipPermissions = platformConfig.skipPermissions ?? false;
|
|
52294
52362
|
this.apiUrl = platformConfig.apiUrl || "https://slack.com/api";
|
|
52295
52363
|
}
|
|
52296
52364
|
normalizePlatformUser(slackUser) {
|
|
@@ -52914,12 +52982,12 @@ class SlackClient extends BasePlatformClient {
|
|
|
52914
52982
|
return this.normalizePlatformFile(response.file);
|
|
52915
52983
|
}
|
|
52916
52984
|
}
|
|
52917
|
-
// src/mattermost/api.ts
|
|
52985
|
+
// src/platform/mattermost/permission-api.ts
|
|
52918
52986
|
init_logger();
|
|
52919
|
-
var
|
|
52987
|
+
var apiLog = createLogger("mm-api");
|
|
52920
52988
|
async function mattermostApi(config, method, path, body) {
|
|
52921
52989
|
const url = `${config.url}/api/v4${path}`;
|
|
52922
|
-
|
|
52990
|
+
apiLog.debug(`API ${method} ${path}`);
|
|
52923
52991
|
const response = await fetch(url, {
|
|
52924
52992
|
method,
|
|
52925
52993
|
headers: {
|
|
@@ -52930,10 +52998,10 @@ async function mattermostApi(config, method, path, body) {
|
|
|
52930
52998
|
});
|
|
52931
52999
|
if (!response.ok) {
|
|
52932
53000
|
const text = await response.text();
|
|
52933
|
-
|
|
53001
|
+
apiLog.warn(`API ${method} ${path} failed: ${response.status} ${text.substring(0, 100)}`);
|
|
52934
53002
|
throw new Error(`Mattermost API error ${response.status}: ${text}`);
|
|
52935
53003
|
}
|
|
52936
|
-
|
|
53004
|
+
apiLog.debug(`API ${method} ${path} → ${response.status}`);
|
|
52937
53005
|
return response.json();
|
|
52938
53006
|
}
|
|
52939
53007
|
async function getMe(config) {
|
|
@@ -52943,7 +53011,7 @@ async function getUser(config, userId) {
|
|
|
52943
53011
|
try {
|
|
52944
53012
|
return await mattermostApi(config, "GET", `/users/${userId}`);
|
|
52945
53013
|
} catch (err) {
|
|
52946
|
-
|
|
53014
|
+
apiLog.debug(`Failed to get user ${userId}: ${err}`);
|
|
52947
53015
|
return null;
|
|
52948
53016
|
}
|
|
52949
53017
|
}
|
|
@@ -52954,7 +53022,7 @@ async function createPost(config, channelId, message, rootId) {
|
|
|
52954
53022
|
root_id: rootId
|
|
52955
53023
|
});
|
|
52956
53024
|
}
|
|
52957
|
-
async function
|
|
53025
|
+
async function updatePostRaw(config, postId, message) {
|
|
52958
53026
|
return mattermostApi(config, "PUT", `/posts/${postId}`, {
|
|
52959
53027
|
id: postId,
|
|
52960
53028
|
message
|
|
@@ -52967,25 +53035,23 @@ async function addReaction(config, postId, userId, emojiName) {
|
|
|
52967
53035
|
emoji_name: emojiName
|
|
52968
53036
|
});
|
|
52969
53037
|
}
|
|
52970
|
-
function
|
|
53038
|
+
function isUserInAllowList(username, allowList) {
|
|
52971
53039
|
if (allowList.length === 0)
|
|
52972
53040
|
return true;
|
|
52973
53041
|
return allowList.includes(username);
|
|
52974
53042
|
}
|
|
52975
|
-
async function
|
|
53043
|
+
async function createInteractivePostInternal(config, channelId, message, reactions, rootId, botUserId) {
|
|
52976
53044
|
const post = await createPost(config, channelId, message, rootId);
|
|
52977
53045
|
for (const emoji of reactions) {
|
|
52978
53046
|
try {
|
|
52979
53047
|
await addReaction(config, post.id, botUserId, emoji);
|
|
52980
53048
|
} catch (err) {
|
|
52981
|
-
|
|
53049
|
+
apiLog.warn(`Failed to add reaction ${emoji}: ${err}`);
|
|
52982
53050
|
}
|
|
52983
53051
|
}
|
|
52984
53052
|
return post;
|
|
52985
53053
|
}
|
|
52986
53054
|
|
|
52987
|
-
// src/platform/mattermost/permission-api.ts
|
|
52988
|
-
init_logger();
|
|
52989
53055
|
class MattermostPermissionApi {
|
|
52990
53056
|
apiConfig;
|
|
52991
53057
|
config;
|
|
@@ -53026,18 +53092,18 @@ class MattermostPermissionApi {
|
|
|
53026
53092
|
}
|
|
53027
53093
|
}
|
|
53028
53094
|
isUserAllowed(username) {
|
|
53029
|
-
return
|
|
53095
|
+
return isUserInAllowList(username, this.config.allowedUsers);
|
|
53030
53096
|
}
|
|
53031
53097
|
async createInteractivePost(message, reactions, threadId) {
|
|
53032
53098
|
mcpLogger.debug(`Creating interactive post with ${reactions.length} reaction options`);
|
|
53033
53099
|
const botUserId = await this.getBotUserId();
|
|
53034
|
-
const post = await
|
|
53100
|
+
const post = await createInteractivePostInternal(this.apiConfig, this.config.channelId, message, reactions, threadId, botUserId);
|
|
53035
53101
|
mcpLogger.debug(`Created post ${formatShortId(post.id)}`);
|
|
53036
53102
|
return { id: post.id };
|
|
53037
53103
|
}
|
|
53038
53104
|
async updatePost(postId, message) {
|
|
53039
53105
|
mcpLogger.debug(`Updating post ${postId.substring(0, 8)}`);
|
|
53040
|
-
await
|
|
53106
|
+
await updatePostRaw(this.apiConfig, postId, message);
|
|
53041
53107
|
}
|
|
53042
53108
|
async waitForReaction(postId, botUserId, timeoutMs) {
|
|
53043
53109
|
return new Promise((resolve3) => {
|
|
@@ -53331,7 +53397,7 @@ init_logger();
|
|
|
53331
53397
|
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2, renameSync, chmodSync as chmodSync2 } from "fs";
|
|
53332
53398
|
import { homedir as homedir2 } from "os";
|
|
53333
53399
|
import { join as join3 } from "path";
|
|
53334
|
-
var
|
|
53400
|
+
var log4 = createLogger("persist");
|
|
53335
53401
|
var STORE_VERSION = 2;
|
|
53336
53402
|
var DEFAULT_CONFIG_DIR = join3(homedir2(), ".config", "claude-threads");
|
|
53337
53403
|
var DEFAULT_SESSIONS_FILE = join3(DEFAULT_CONFIG_DIR, "sessions.json");
|
|
@@ -53356,13 +53422,13 @@ class SessionStore {
|
|
|
53356
53422
|
load() {
|
|
53357
53423
|
const sessions = new Map;
|
|
53358
53424
|
if (!existsSync5(this.sessionsFile)) {
|
|
53359
|
-
|
|
53425
|
+
log4.debug("No sessions file found");
|
|
53360
53426
|
return sessions;
|
|
53361
53427
|
}
|
|
53362
53428
|
try {
|
|
53363
53429
|
const data = this.loadRaw();
|
|
53364
53430
|
if (data.version === 1) {
|
|
53365
|
-
|
|
53431
|
+
log4.info("Migrating sessions from v1 to v2 (adding platformId)");
|
|
53366
53432
|
const newSessions = {};
|
|
53367
53433
|
for (const [_oldKey, session] of Object.entries(data.sessions)) {
|
|
53368
53434
|
const v1Session = session;
|
|
@@ -53376,7 +53442,7 @@ class SessionStore {
|
|
|
53376
53442
|
data.version = 2;
|
|
53377
53443
|
this.writeAtomic(data);
|
|
53378
53444
|
} else if (data.version !== STORE_VERSION) {
|
|
53379
|
-
|
|
53445
|
+
log4.warn(`Sessions file version ${data.version} not supported, starting fresh`);
|
|
53380
53446
|
return sessions;
|
|
53381
53447
|
}
|
|
53382
53448
|
for (const session of Object.values(data.sessions)) {
|
|
@@ -53385,9 +53451,9 @@ class SessionStore {
|
|
|
53385
53451
|
const sessionId = `${session.platformId}:${session.threadId}`;
|
|
53386
53452
|
sessions.set(sessionId, session);
|
|
53387
53453
|
}
|
|
53388
|
-
|
|
53454
|
+
log4.debug(`Loaded ${sessions.size} active session(s)`);
|
|
53389
53455
|
} catch (err) {
|
|
53390
|
-
|
|
53456
|
+
log4.error(`Failed to load sessions: ${err}`);
|
|
53391
53457
|
}
|
|
53392
53458
|
return sessions;
|
|
53393
53459
|
}
|
|
@@ -53396,7 +53462,7 @@ class SessionStore {
|
|
|
53396
53462
|
data.sessions[sessionId] = session;
|
|
53397
53463
|
this.writeAtomic(data);
|
|
53398
53464
|
const shortId = sessionId.substring(0, 20);
|
|
53399
|
-
|
|
53465
|
+
log4.debug(`Saved session ${shortId}...`);
|
|
53400
53466
|
}
|
|
53401
53467
|
remove(sessionId) {
|
|
53402
53468
|
const data = this.loadRaw();
|
|
@@ -53404,7 +53470,7 @@ class SessionStore {
|
|
|
53404
53470
|
delete data.sessions[sessionId];
|
|
53405
53471
|
this.writeAtomic(data);
|
|
53406
53472
|
const shortId = sessionId.substring(0, 20);
|
|
53407
|
-
|
|
53473
|
+
log4.debug(`Removed session ${shortId}...`);
|
|
53408
53474
|
}
|
|
53409
53475
|
}
|
|
53410
53476
|
softDelete(sessionId) {
|
|
@@ -53413,7 +53479,7 @@ class SessionStore {
|
|
|
53413
53479
|
data.sessions[sessionId].cleanedAt = new Date().toISOString();
|
|
53414
53480
|
this.writeAtomic(data);
|
|
53415
53481
|
const shortId = sessionId.substring(0, 20);
|
|
53416
|
-
|
|
53482
|
+
log4.debug(`Soft-deleted session ${shortId}...`);
|
|
53417
53483
|
}
|
|
53418
53484
|
}
|
|
53419
53485
|
cleanStale(maxAgeMs) {
|
|
@@ -53431,7 +53497,7 @@ class SessionStore {
|
|
|
53431
53497
|
}
|
|
53432
53498
|
if (staleIds.length > 0) {
|
|
53433
53499
|
this.writeAtomic(data);
|
|
53434
|
-
|
|
53500
|
+
log4.debug(`Soft-deleted ${staleIds.length} stale session(s)`);
|
|
53435
53501
|
}
|
|
53436
53502
|
return staleIds;
|
|
53437
53503
|
}
|
|
@@ -53450,7 +53516,7 @@ class SessionStore {
|
|
|
53450
53516
|
}
|
|
53451
53517
|
if (removedCount > 0) {
|
|
53452
53518
|
this.writeAtomic(data);
|
|
53453
|
-
|
|
53519
|
+
log4.debug(`Permanently removed ${removedCount} old session(s) from history`);
|
|
53454
53520
|
}
|
|
53455
53521
|
return removedCount;
|
|
53456
53522
|
}
|
|
@@ -53477,7 +53543,7 @@ class SessionStore {
|
|
|
53477
53543
|
clear() {
|
|
53478
53544
|
const data = this.loadRaw();
|
|
53479
53545
|
this.writeAtomic({ version: STORE_VERSION, sessions: {}, stickyPostIds: data.stickyPostIds });
|
|
53480
|
-
|
|
53546
|
+
log4.debug("Cleared all sessions");
|
|
53481
53547
|
}
|
|
53482
53548
|
saveStickyPostId(platformId, postId) {
|
|
53483
53549
|
const data = this.loadRaw();
|
|
@@ -53486,7 +53552,7 @@ class SessionStore {
|
|
|
53486
53552
|
}
|
|
53487
53553
|
data.stickyPostIds[platformId] = postId;
|
|
53488
53554
|
this.writeAtomic(data);
|
|
53489
|
-
|
|
53555
|
+
log4.debug(`Saved sticky post ID for ${platformId}: ${postId.substring(0, 8)}...`);
|
|
53490
53556
|
}
|
|
53491
53557
|
getStickyPostIds() {
|
|
53492
53558
|
const data = this.loadRaw();
|
|
@@ -53497,7 +53563,7 @@ class SessionStore {
|
|
|
53497
53563
|
if (data.stickyPostIds && data.stickyPostIds[platformId]) {
|
|
53498
53564
|
delete data.stickyPostIds[platformId];
|
|
53499
53565
|
this.writeAtomic(data);
|
|
53500
|
-
|
|
53566
|
+
log4.debug(`Removed sticky post ID for ${platformId}`);
|
|
53501
53567
|
}
|
|
53502
53568
|
}
|
|
53503
53569
|
getPlatformEnabledState() {
|
|
@@ -53515,7 +53581,7 @@ class SessionStore {
|
|
|
53515
53581
|
}
|
|
53516
53582
|
data.platformEnabledState[platformId] = enabled;
|
|
53517
53583
|
this.writeAtomic(data);
|
|
53518
|
-
|
|
53584
|
+
log4.debug(`Set platform ${platformId} enabled state to ${enabled}`);
|
|
53519
53585
|
}
|
|
53520
53586
|
findByThread(platformId, threadId) {
|
|
53521
53587
|
const sessionId = `${platformId}:${threadId}`;
|
|
@@ -53566,9 +53632,10 @@ class SessionStore {
|
|
|
53566
53632
|
chmodSync2(this.sessionsFile, 384);
|
|
53567
53633
|
}
|
|
53568
53634
|
}
|
|
53635
|
+
|
|
53569
53636
|
// src/claude/account-pool.ts
|
|
53570
53637
|
init_logger();
|
|
53571
|
-
var
|
|
53638
|
+
var log5 = createLogger("account-pool");
|
|
53572
53639
|
|
|
53573
53640
|
class AccountPool {
|
|
53574
53641
|
accounts;
|
|
@@ -53580,11 +53647,11 @@ class AccountPool {
|
|
|
53580
53647
|
this.accounts = (accounts ?? []).filter((acc) => {
|
|
53581
53648
|
const hasAuth = !!acc.home || !!acc.apiKey;
|
|
53582
53649
|
if (!hasAuth) {
|
|
53583
|
-
|
|
53650
|
+
log5.warn(`Claude account ${acc.id} has neither home nor apiKey — ignoring`);
|
|
53584
53651
|
return false;
|
|
53585
53652
|
}
|
|
53586
53653
|
if (acc.home && acc.apiKey) {
|
|
53587
|
-
|
|
53654
|
+
log5.warn(`Claude account ${acc.id} has both home and apiKey set — must choose one; ignoring`);
|
|
53588
53655
|
return false;
|
|
53589
53656
|
}
|
|
53590
53657
|
return true;
|
|
@@ -53609,7 +53676,7 @@ class AccountPool {
|
|
|
53609
53676
|
this.incrementActive(preferred.id);
|
|
53610
53677
|
return preferred;
|
|
53611
53678
|
}
|
|
53612
|
-
|
|
53679
|
+
log5.warn(`Preferred account "${preferredId}" not in pool — falling back to round-robin`);
|
|
53613
53680
|
}
|
|
53614
53681
|
const now = Date.now();
|
|
53615
53682
|
const n = this.accounts.length;
|
|
@@ -53623,7 +53690,7 @@ class AccountPool {
|
|
|
53623
53690
|
return candidate;
|
|
53624
53691
|
}
|
|
53625
53692
|
}
|
|
53626
|
-
|
|
53693
|
+
log5.warn(`All ${n} accounts are in rate-limit cooldown`);
|
|
53627
53694
|
return null;
|
|
53628
53695
|
}
|
|
53629
53696
|
release(accountId) {
|
|
@@ -53634,14 +53701,14 @@ class AccountPool {
|
|
|
53634
53701
|
}
|
|
53635
53702
|
markCooling(accountId, untilEpochMs) {
|
|
53636
53703
|
if (!this.byId.has(accountId)) {
|
|
53637
|
-
|
|
53704
|
+
log5.warn(`markCooling called for unknown account "${accountId}"`);
|
|
53638
53705
|
return;
|
|
53639
53706
|
}
|
|
53640
53707
|
const existing = this.coolingUntil.get(accountId) ?? 0;
|
|
53641
53708
|
if (untilEpochMs > existing) {
|
|
53642
53709
|
this.coolingUntil.set(accountId, untilEpochMs);
|
|
53643
53710
|
const minutes = Math.ceil((untilEpochMs - Date.now()) / 60000);
|
|
53644
|
-
|
|
53711
|
+
log5.info(`Account "${accountId}" cooling for ~${minutes}min`);
|
|
53645
53712
|
}
|
|
53646
53713
|
}
|
|
53647
53714
|
get(accountId) {
|
|
@@ -53678,7 +53745,7 @@ init_logger();
|
|
|
53678
53745
|
import { existsSync as existsSync6, mkdirSync as mkdirSync3, appendFileSync, readdirSync, statSync, unlinkSync, rmdirSync, readFileSync as readFileSync5, chmodSync as chmodSync3 } from "fs";
|
|
53679
53746
|
import { homedir as homedir3 } from "os";
|
|
53680
53747
|
import { join as join4, dirname as dirname4 } from "path";
|
|
53681
|
-
var
|
|
53748
|
+
var log6 = createLogger("thread-log");
|
|
53682
53749
|
var LOGS_BASE_DIR = join4(homedir3(), ".claude-threads", "logs");
|
|
53683
53750
|
|
|
53684
53751
|
class ThreadLoggerImpl {
|
|
@@ -53708,7 +53775,7 @@ class ThreadLoggerImpl {
|
|
|
53708
53775
|
this.flushTimer = setInterval(() => {
|
|
53709
53776
|
this.flushSync();
|
|
53710
53777
|
}, this.flushIntervalMs);
|
|
53711
|
-
|
|
53778
|
+
log6.debug(`Thread logger initialized: ${this.logPath}`);
|
|
53712
53779
|
}
|
|
53713
53780
|
}
|
|
53714
53781
|
isEnabled() {
|
|
@@ -53822,7 +53889,7 @@ class ThreadLoggerImpl {
|
|
|
53822
53889
|
this.flushTimer = null;
|
|
53823
53890
|
}
|
|
53824
53891
|
this.flushSync();
|
|
53825
|
-
|
|
53892
|
+
log6.debug(`Thread logger closed: ${this.logPath}`);
|
|
53826
53893
|
}
|
|
53827
53894
|
addEntry(entry) {
|
|
53828
53895
|
this.buffer.push(entry);
|
|
@@ -53844,7 +53911,7 @@ class ThreadLoggerImpl {
|
|
|
53844
53911
|
}
|
|
53845
53912
|
this.buffer = [];
|
|
53846
53913
|
} catch (err) {
|
|
53847
|
-
|
|
53914
|
+
log6.error(`Failed to flush thread log: ${err}`);
|
|
53848
53915
|
}
|
|
53849
53916
|
}
|
|
53850
53917
|
}
|
|
@@ -53895,25 +53962,25 @@ function cleanupOldLogs(retentionDays = 30) {
|
|
|
53895
53962
|
if (fileStat.mtimeMs < cutoffMs) {
|
|
53896
53963
|
unlinkSync(filePath);
|
|
53897
53964
|
deletedCount++;
|
|
53898
|
-
|
|
53965
|
+
log6.debug(`Deleted old log file: ${filePath}`);
|
|
53899
53966
|
}
|
|
53900
53967
|
} catch (err) {
|
|
53901
|
-
|
|
53968
|
+
log6.warn(`Failed to check/delete log file ${filePath}: ${err}`);
|
|
53902
53969
|
}
|
|
53903
53970
|
}
|
|
53904
53971
|
try {
|
|
53905
53972
|
const remaining = readdirSync(platformDir);
|
|
53906
53973
|
if (remaining.length === 0) {
|
|
53907
53974
|
rmdirSync(platformDir);
|
|
53908
|
-
|
|
53975
|
+
log6.debug(`Removed empty platform log directory: ${platformDir}`);
|
|
53909
53976
|
}
|
|
53910
53977
|
} catch {}
|
|
53911
53978
|
}
|
|
53912
53979
|
if (deletedCount > 0) {
|
|
53913
|
-
|
|
53980
|
+
log6.info(`Cleaned up ${deletedCount} old log file(s)`);
|
|
53914
53981
|
}
|
|
53915
53982
|
} catch (err) {
|
|
53916
|
-
|
|
53983
|
+
log6.error(`Failed to clean up old logs: ${err}`);
|
|
53917
53984
|
}
|
|
53918
53985
|
return deletedCount;
|
|
53919
53986
|
}
|
|
@@ -53922,16 +53989,16 @@ function getLogFilePath(platformId, sessionId) {
|
|
|
53922
53989
|
}
|
|
53923
53990
|
function readRecentLogEntries(platformId, sessionId, maxLines = 50) {
|
|
53924
53991
|
const logPath = getLogFilePath(platformId, sessionId);
|
|
53925
|
-
|
|
53992
|
+
log6.debug(`Reading log entries from: ${logPath}`);
|
|
53926
53993
|
if (!existsSync6(logPath)) {
|
|
53927
|
-
|
|
53994
|
+
log6.debug(`Log file does not exist: ${logPath}`);
|
|
53928
53995
|
return [];
|
|
53929
53996
|
}
|
|
53930
53997
|
try {
|
|
53931
53998
|
const content = readFileSync5(logPath, "utf8");
|
|
53932
53999
|
const lines = content.trim().split(`
|
|
53933
54000
|
`);
|
|
53934
|
-
|
|
54001
|
+
log6.debug(`Log file has ${lines.length} lines`);
|
|
53935
54002
|
const recentLines = lines.slice(-maxLines);
|
|
53936
54003
|
const entries = [];
|
|
53937
54004
|
for (const line of recentLines) {
|
|
@@ -53941,17 +54008,17 @@ function readRecentLogEntries(platformId, sessionId, maxLines = 50) {
|
|
|
53941
54008
|
entries.push(JSON.parse(line));
|
|
53942
54009
|
} catch {}
|
|
53943
54010
|
}
|
|
53944
|
-
|
|
54011
|
+
log6.debug(`Parsed ${entries.length} log entries`);
|
|
53945
54012
|
return entries;
|
|
53946
54013
|
} catch (err) {
|
|
53947
|
-
|
|
54014
|
+
log6.error(`Failed to read log file: ${err}`);
|
|
53948
54015
|
return [];
|
|
53949
54016
|
}
|
|
53950
54017
|
}
|
|
53951
54018
|
|
|
53952
54019
|
// src/cleanup/scheduler.ts
|
|
53953
54020
|
init_worktree();
|
|
53954
|
-
var
|
|
54021
|
+
var log8 = createLogger("cleanup");
|
|
53955
54022
|
var DEFAULT_CLEANUP_INTERVAL_MS = 60 * 60 * 1000;
|
|
53956
54023
|
var MAX_WORKTREE_AGE_MS = 24 * 60 * 60 * 1000;
|
|
53957
54024
|
|
|
@@ -53974,17 +54041,17 @@ class CleanupScheduler {
|
|
|
53974
54041
|
}
|
|
53975
54042
|
start() {
|
|
53976
54043
|
if (this.isRunning) {
|
|
53977
|
-
|
|
54044
|
+
log8.debug("Cleanup scheduler already running");
|
|
53978
54045
|
return;
|
|
53979
54046
|
}
|
|
53980
54047
|
this.isRunning = true;
|
|
53981
|
-
|
|
54048
|
+
log8.info(`Cleanup scheduler started (interval: ${Math.round(this.intervalMs / 60000)}min)`);
|
|
53982
54049
|
this.runCleanup().catch((err) => {
|
|
53983
|
-
|
|
54050
|
+
log8.warn(`Initial cleanup failed: ${err}`);
|
|
53984
54051
|
});
|
|
53985
54052
|
this.timer = setInterval(() => {
|
|
53986
54053
|
this.runCleanup().catch((err) => {
|
|
53987
|
-
|
|
54054
|
+
log8.warn(`Periodic cleanup failed: ${err}`);
|
|
53988
54055
|
});
|
|
53989
54056
|
}, this.intervalMs);
|
|
53990
54057
|
}
|
|
@@ -53994,11 +54061,11 @@ class CleanupScheduler {
|
|
|
53994
54061
|
this.timer = null;
|
|
53995
54062
|
}
|
|
53996
54063
|
this.isRunning = false;
|
|
53997
|
-
|
|
54064
|
+
log8.debug("Cleanup scheduler stopped");
|
|
53998
54065
|
}
|
|
53999
54066
|
async runCleanup() {
|
|
54000
54067
|
const startTime = Date.now();
|
|
54001
|
-
|
|
54068
|
+
log8.debug("Running background cleanup...");
|
|
54002
54069
|
const stats = {
|
|
54003
54070
|
logsDeleted: 0,
|
|
54004
54071
|
worktreesCleaned: 0,
|
|
@@ -54024,9 +54091,9 @@ class CleanupScheduler {
|
|
|
54024
54091
|
const elapsed = Date.now() - startTime;
|
|
54025
54092
|
const totalCleaned = stats.logsDeleted + stats.worktreesCleaned + stats.metadataCleaned;
|
|
54026
54093
|
if (totalCleaned > 0 || stats.errors.length > 0) {
|
|
54027
|
-
|
|
54094
|
+
log8.info(`Cleanup completed in ${elapsed}ms: ` + `${stats.logsDeleted} logs, ${stats.worktreesCleaned} worktrees, ${stats.metadataCleaned} metadata` + (stats.errors.length > 0 ? ` (${stats.errors.length} errors)` : ""));
|
|
54028
54095
|
} else {
|
|
54029
|
-
|
|
54096
|
+
log8.debug(`Cleanup completed in ${elapsed}ms (nothing to clean)`);
|
|
54030
54097
|
}
|
|
54031
54098
|
return stats;
|
|
54032
54099
|
}
|
|
@@ -54039,7 +54106,7 @@ class CleanupScheduler {
|
|
|
54039
54106
|
const deleted = cleanupOldLogs(this.logRetentionDays);
|
|
54040
54107
|
resolve3(deleted);
|
|
54041
54108
|
} catch (err) {
|
|
54042
|
-
|
|
54109
|
+
log8.warn(`Log cleanup error: ${err}`);
|
|
54043
54110
|
resolve3(0);
|
|
54044
54111
|
}
|
|
54045
54112
|
});
|
|
@@ -54048,7 +54115,7 @@ class CleanupScheduler {
|
|
|
54048
54115
|
const worktreesDir = getWorktreesDir();
|
|
54049
54116
|
const result = { cleaned: 0, metadata: 0 };
|
|
54050
54117
|
if (!existsSync7(worktreesDir)) {
|
|
54051
|
-
|
|
54118
|
+
log8.debug("No worktrees directory exists, nothing to clean");
|
|
54052
54119
|
return result;
|
|
54053
54120
|
}
|
|
54054
54121
|
const persisted = this.sessionStore.load();
|
|
@@ -54066,7 +54133,7 @@ class CleanupScheduler {
|
|
|
54066
54133
|
continue;
|
|
54067
54134
|
const worktreePath = join6(worktreesDir, entry.name);
|
|
54068
54135
|
if (activeWorktrees.has(worktreePath)) {
|
|
54069
|
-
|
|
54136
|
+
log8.debug(`Worktree in use by persisted session, skipping: ${entry.name}`);
|
|
54070
54137
|
continue;
|
|
54071
54138
|
}
|
|
54072
54139
|
const meta = await readWorktreeMetadata(worktreePath);
|
|
@@ -54076,7 +54143,7 @@ class CleanupScheduler {
|
|
|
54076
54143
|
const lastActivity = new Date(meta.lastActivityAt).getTime();
|
|
54077
54144
|
const age = now - lastActivity;
|
|
54078
54145
|
if (meta.sessionId && age < this.maxWorktreeAgeMs) {
|
|
54079
|
-
|
|
54146
|
+
log8.debug(`Worktree has active session (${Math.round(age / 60000)}min old), skipping: ${entry.name}`);
|
|
54080
54147
|
continue;
|
|
54081
54148
|
}
|
|
54082
54149
|
const merged = age >= this.maxWorktreeAgeMs ? await isBranchMerged(meta.repoRoot, meta.branch).catch(() => false) : false;
|
|
@@ -54087,7 +54154,7 @@ class CleanupScheduler {
|
|
|
54087
54154
|
shouldCleanup = true;
|
|
54088
54155
|
cleanupReason = `inactive for ${Math.round(age / 3600000)}h`;
|
|
54089
54156
|
} else {
|
|
54090
|
-
|
|
54157
|
+
log8.debug(`Worktree recent (${Math.round(age / 60000)}min old), skipping: ${entry.name}`);
|
|
54091
54158
|
continue;
|
|
54092
54159
|
}
|
|
54093
54160
|
} else {
|
|
@@ -54096,7 +54163,7 @@ class CleanupScheduler {
|
|
|
54096
54163
|
}
|
|
54097
54164
|
if (!shouldCleanup)
|
|
54098
54165
|
continue;
|
|
54099
|
-
|
|
54166
|
+
log8.info(`Cleaning worktree (${cleanupReason}): ${entry.name}`);
|
|
54100
54167
|
try {
|
|
54101
54168
|
if (meta?.repoRoot) {
|
|
54102
54169
|
await removeWorktree(meta.repoRoot, worktreePath);
|
|
@@ -54107,19 +54174,19 @@ class CleanupScheduler {
|
|
|
54107
54174
|
await removeWorktreeMetadata(worktreePath);
|
|
54108
54175
|
result.metadata++;
|
|
54109
54176
|
} catch (err) {
|
|
54110
|
-
|
|
54177
|
+
log8.warn(`Failed to clean orphaned worktree ${entry.name}: ${err}`);
|
|
54111
54178
|
try {
|
|
54112
54179
|
await rm(worktreePath, { recursive: true, force: true });
|
|
54113
54180
|
result.cleaned++;
|
|
54114
54181
|
await removeWorktreeMetadata(worktreePath);
|
|
54115
54182
|
result.metadata++;
|
|
54116
54183
|
} catch (rmErr) {
|
|
54117
|
-
|
|
54184
|
+
log8.error(`Failed to force remove worktree ${entry.name}: ${rmErr}`);
|
|
54118
54185
|
}
|
|
54119
54186
|
}
|
|
54120
54187
|
}
|
|
54121
54188
|
} catch (err) {
|
|
54122
|
-
|
|
54189
|
+
log8.warn(`Failed to scan worktrees directory: ${err}`);
|
|
54123
54190
|
}
|
|
54124
54191
|
return result;
|
|
54125
54192
|
}
|
|
@@ -54193,7 +54260,7 @@ init_logger();
|
|
|
54193
54260
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
54194
54261
|
import { resolve as resolve3, dirname as dirname6 } from "path";
|
|
54195
54262
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
54196
|
-
import { existsSync as existsSync8, readFileSync as readFileSync6, watchFile, unwatchFile, unlinkSync as unlinkSync2, statSync as statSync2, readdirSync as readdirSync2 } from "fs";
|
|
54263
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, watchFile, unwatchFile, unlinkSync as unlinkSync2, statSync as statSync2, readdirSync as readdirSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
54197
54264
|
import { tmpdir } from "os";
|
|
54198
54265
|
import { join as join7 } from "path";
|
|
54199
54266
|
|
|
@@ -54259,7 +54326,7 @@ function extractResetAt(text, now) {
|
|
|
54259
54326
|
}
|
|
54260
54327
|
|
|
54261
54328
|
// src/claude/cli.ts
|
|
54262
|
-
var
|
|
54329
|
+
var log9 = createLogger("claude");
|
|
54263
54330
|
function cleanupBrowserBridgeSockets() {
|
|
54264
54331
|
try {
|
|
54265
54332
|
const tempDir = tmpdir();
|
|
@@ -54271,13 +54338,13 @@ function cleanupBrowserBridgeSockets() {
|
|
|
54271
54338
|
const stats = statSync2(filePath);
|
|
54272
54339
|
if (stats.isSocket()) {
|
|
54273
54340
|
unlinkSync2(filePath);
|
|
54274
|
-
|
|
54341
|
+
log9.debug(`Removed stale browser bridge socket: ${file}`);
|
|
54275
54342
|
}
|
|
54276
54343
|
} catch {}
|
|
54277
54344
|
}
|
|
54278
54345
|
}
|
|
54279
54346
|
} catch (err) {
|
|
54280
|
-
|
|
54347
|
+
log9.debug(`Browser bridge cleanup failed: ${err}`);
|
|
54281
54348
|
}
|
|
54282
54349
|
}
|
|
54283
54350
|
function buildClaudeChildEnv(parentEnv, account) {
|
|
@@ -54307,6 +54374,64 @@ function isErrorResultEvent(event) {
|
|
|
54307
54374
|
return true;
|
|
54308
54375
|
return false;
|
|
54309
54376
|
}
|
|
54377
|
+
function materializeMcpConfig(config, sessionId, opts = {}) {
|
|
54378
|
+
if (opts.inline ?? process.env.CLAUDE_THREADS_MCP_CONFIG_INLINE === "1") {
|
|
54379
|
+
return { mode: "inline", value: JSON.stringify(config) };
|
|
54380
|
+
}
|
|
54381
|
+
const dir = opts.tmpDirOverride ?? tmpdir();
|
|
54382
|
+
const path2 = join7(dir, `claude-threads-mcp-${sessionId ?? process.pid}-${Date.now()}.json`);
|
|
54383
|
+
writeFileSync3(path2, JSON.stringify(config), { mode: 384 });
|
|
54384
|
+
return { mode: "file", path: path2 };
|
|
54385
|
+
}
|
|
54386
|
+
function buildPermissionArgs(opts) {
|
|
54387
|
+
const args = [];
|
|
54388
|
+
if (opts.permissionMode === "bypass") {
|
|
54389
|
+
args.push("--dangerously-skip-permissions");
|
|
54390
|
+
return { args, tempFile: null };
|
|
54391
|
+
}
|
|
54392
|
+
if (!opts.platformConfig) {
|
|
54393
|
+
throw new Error(`platformConfig is required when permissionMode is '${opts.permissionMode}'`);
|
|
54394
|
+
}
|
|
54395
|
+
const mcpEnv = {
|
|
54396
|
+
PLATFORM_TYPE: opts.platformConfig.type,
|
|
54397
|
+
PLATFORM_URL: opts.platformConfig.url,
|
|
54398
|
+
PLATFORM_TOKEN: opts.platformConfig.token,
|
|
54399
|
+
PLATFORM_CHANNEL_ID: opts.platformConfig.channelId,
|
|
54400
|
+
PLATFORM_THREAD_ID: opts.threadId || "",
|
|
54401
|
+
ALLOWED_USERS: opts.platformConfig.allowedUsers.join(","),
|
|
54402
|
+
DEBUG: opts.debug ? "1" : "",
|
|
54403
|
+
PERMISSION_TIMEOUT_MS: String(opts.permissionTimeoutMs)
|
|
54404
|
+
};
|
|
54405
|
+
if (opts.platformConfig.appToken) {
|
|
54406
|
+
mcpEnv.PLATFORM_APP_TOKEN = opts.platformConfig.appToken;
|
|
54407
|
+
}
|
|
54408
|
+
const mcpConfig = {
|
|
54409
|
+
mcpServers: {
|
|
54410
|
+
"claude-threads-permissions": {
|
|
54411
|
+
type: "stdio",
|
|
54412
|
+
command: "node",
|
|
54413
|
+
args: [opts.mcpServerPath],
|
|
54414
|
+
env: mcpEnv
|
|
54415
|
+
}
|
|
54416
|
+
}
|
|
54417
|
+
};
|
|
54418
|
+
const materialized = materializeMcpConfig(mcpConfig, opts.sessionId, { inline: opts.inline });
|
|
54419
|
+
let tempFile = null;
|
|
54420
|
+
if (materialized.mode === "file") {
|
|
54421
|
+
tempFile = materialized.path;
|
|
54422
|
+
args.push("--mcp-config", materialized.path);
|
|
54423
|
+
} else {
|
|
54424
|
+
args.push("--mcp-config", materialized.value);
|
|
54425
|
+
}
|
|
54426
|
+
args.push("--permission-prompt-tool", "mcp__claude-threads-permissions__permission_prompt");
|
|
54427
|
+
if (opts.permissionMode === "auto") {
|
|
54428
|
+
args.push("--permission-mode", "auto");
|
|
54429
|
+
}
|
|
54430
|
+
return { args, tempFile };
|
|
54431
|
+
}
|
|
54432
|
+
var STDERR_PER_INSTANCE_CAP = 10240;
|
|
54433
|
+
var STDERR_AGGREGATE_SOFT_CAP = 10 * 1024 * 1024;
|
|
54434
|
+
var totalStderrBytes = 0;
|
|
54310
54435
|
|
|
54311
54436
|
class ClaudeCli extends EventEmitter2 {
|
|
54312
54437
|
process = null;
|
|
@@ -54316,6 +54441,7 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
54316
54441
|
statusFilePath = null;
|
|
54317
54442
|
lastStatusData = null;
|
|
54318
54443
|
stderrBuffer = "";
|
|
54444
|
+
mcpConfigTempFile = null;
|
|
54319
54445
|
lastEmittedRateLimitDeadline = 0;
|
|
54320
54446
|
log;
|
|
54321
54447
|
constructor(options2) {
|
|
@@ -54367,6 +54493,7 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
54367
54493
|
start() {
|
|
54368
54494
|
if (this.process)
|
|
54369
54495
|
throw new Error("Already running");
|
|
54496
|
+
totalStderrBytes -= this.stderrBuffer.length;
|
|
54370
54497
|
this.stderrBuffer = "";
|
|
54371
54498
|
this.lastEmittedRateLimitDeadline = 0;
|
|
54372
54499
|
cleanupBrowserBridgeSockets();
|
|
@@ -54385,40 +54512,18 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
54385
54512
|
args.push("--session-id", this.options.sessionId);
|
|
54386
54513
|
}
|
|
54387
54514
|
}
|
|
54388
|
-
|
|
54389
|
-
|
|
54390
|
-
|
|
54391
|
-
|
|
54392
|
-
|
|
54393
|
-
|
|
54394
|
-
|
|
54395
|
-
|
|
54396
|
-
|
|
54397
|
-
|
|
54398
|
-
|
|
54399
|
-
|
|
54400
|
-
PLATFORM_CHANNEL_ID: platformConfig.channelId,
|
|
54401
|
-
PLATFORM_THREAD_ID: this.options.threadId || "",
|
|
54402
|
-
ALLOWED_USERS: platformConfig.allowedUsers.join(","),
|
|
54403
|
-
DEBUG: this.debug ? "1" : "",
|
|
54404
|
-
PERMISSION_TIMEOUT_MS: String(this.options.permissionTimeoutMs ?? 120000)
|
|
54405
|
-
};
|
|
54406
|
-
if (platformConfig.appToken) {
|
|
54407
|
-
mcpEnv.PLATFORM_APP_TOKEN = platformConfig.appToken;
|
|
54408
|
-
}
|
|
54409
|
-
const mcpConfig = {
|
|
54410
|
-
mcpServers: {
|
|
54411
|
-
"claude-threads-permissions": {
|
|
54412
|
-
type: "stdio",
|
|
54413
|
-
command: "node",
|
|
54414
|
-
args: [mcpServerPath],
|
|
54415
|
-
env: mcpEnv
|
|
54416
|
-
}
|
|
54417
|
-
}
|
|
54418
|
-
};
|
|
54419
|
-
args.push("--mcp-config", JSON.stringify(mcpConfig));
|
|
54420
|
-
args.push("--permission-prompt-tool", "mcp__claude-threads-permissions__permission_prompt");
|
|
54421
|
-
}
|
|
54515
|
+
const permissionMode = this.options.permissionMode ?? "default";
|
|
54516
|
+
const permResult = buildPermissionArgs({
|
|
54517
|
+
permissionMode,
|
|
54518
|
+
mcpServerPath: this.getMcpServerPath(),
|
|
54519
|
+
platformConfig: this.options.platformConfig,
|
|
54520
|
+
threadId: this.options.threadId,
|
|
54521
|
+
sessionId: this.options.sessionId,
|
|
54522
|
+
permissionTimeoutMs: this.options.permissionTimeoutMs ?? 120000,
|
|
54523
|
+
debug: this.debug
|
|
54524
|
+
});
|
|
54525
|
+
args.push(...permResult.args);
|
|
54526
|
+
this.mcpConfigTempFile = permResult.tempFile;
|
|
54422
54527
|
if (this.options.chrome) {
|
|
54423
54528
|
args.push("--chrome");
|
|
54424
54529
|
}
|
|
@@ -54453,10 +54558,13 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
54453
54558
|
});
|
|
54454
54559
|
this.process.stderr?.on("data", (chunk) => {
|
|
54455
54560
|
const text = chunk.toString();
|
|
54561
|
+
const before = this.stderrBuffer.length;
|
|
54456
54562
|
this.stderrBuffer += text;
|
|
54457
|
-
|
|
54458
|
-
|
|
54563
|
+
const cap = totalStderrBytes > STDERR_AGGREGATE_SOFT_CAP ? 1024 : STDERR_PER_INSTANCE_CAP;
|
|
54564
|
+
if (this.stderrBuffer.length > cap) {
|
|
54565
|
+
this.stderrBuffer = this.stderrBuffer.slice(-cap);
|
|
54459
54566
|
}
|
|
54567
|
+
totalStderrBytes += this.stderrBuffer.length - before;
|
|
54460
54568
|
this.log.debug(`stderr: ${text.trim()}`);
|
|
54461
54569
|
if (process.env.INTEGRATION_TEST === "1") {
|
|
54462
54570
|
process.stderr.write(text);
|
|
@@ -54471,6 +54579,14 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
54471
54579
|
this.log.debug(`Exited ${code}`);
|
|
54472
54580
|
this.process = null;
|
|
54473
54581
|
this.buffer = "";
|
|
54582
|
+
totalStderrBytes -= this.stderrBuffer.length;
|
|
54583
|
+
if (this.mcpConfigTempFile) {
|
|
54584
|
+
const path2 = this.mcpConfigTempFile;
|
|
54585
|
+
this.mcpConfigTempFile = null;
|
|
54586
|
+
try {
|
|
54587
|
+
unlinkSync2(path2);
|
|
54588
|
+
} catch {}
|
|
54589
|
+
}
|
|
54474
54590
|
this.emit("exit", code);
|
|
54475
54591
|
});
|
|
54476
54592
|
}
|
|
@@ -54716,8 +54832,8 @@ var COMMAND_REGISTRY = [
|
|
|
54716
54832
|
},
|
|
54717
54833
|
{
|
|
54718
54834
|
command: "permissions",
|
|
54719
|
-
description: "
|
|
54720
|
-
args: "
|
|
54835
|
+
description: "Set permission mode (default prompts; auto lets Claude classify; bypass skips all)",
|
|
54836
|
+
args: "default / auto / bypass",
|
|
54721
54837
|
category: "settings",
|
|
54722
54838
|
audience: "user",
|
|
54723
54839
|
claudeNotes: "User decisions, not yours",
|
|
@@ -54832,7 +54948,7 @@ var COMMAND_PATTERNS = [
|
|
|
54832
54948
|
["worktree", /^!worktree\s+(\S+(?:\s+.*)?)$/i],
|
|
54833
54949
|
["invite", /^!invite\s+@?([\w.-]+)\s*$/i],
|
|
54834
54950
|
["kick", /^!kick\s+@?([\w.-]+)\s*$/i],
|
|
54835
|
-
["permissions", /^!permissions?\s+(interactive|
|
|
54951
|
+
["permissions", /^!permissions?\s+(default|auto|bypass|interactive|skip)\s*$/i],
|
|
54836
54952
|
["update", /^!update(?:\s+(now|defer))?\s*$/i],
|
|
54837
54953
|
["context", /^!context\s*$/i],
|
|
54838
54954
|
["cost", /^!cost\s*$/i],
|
|
@@ -54897,7 +55013,7 @@ function removeCommandFromText(text, command) {
|
|
|
54897
55013
|
}
|
|
54898
55014
|
var STACKABLE_PATTERNS = [
|
|
54899
55015
|
["cd", /^!cd\s+(\S+)(?:\s+(.*))?$/i, 1, 2],
|
|
54900
|
-
["permissions", /^!permissions?\s+(interactive)(?:\s+(.*))?$/i, 1, 2],
|
|
55016
|
+
["permissions", /^!permissions?\s+(default|auto|bypass|interactive|skip)(?:\s+(.*))?$/i, 1, 2],
|
|
54901
55017
|
["worktree", /^!worktree\s+(.+)$/i, 1, -1]
|
|
54902
55018
|
];
|
|
54903
55019
|
var IMMEDIATE_PATTERNS = [
|
|
@@ -55175,22 +55291,39 @@ var handleCd = async (ctx, args) => {
|
|
|
55175
55291
|
await ctx.sessionManager.changeDirectory(ctx.threadId, args, ctx.username);
|
|
55176
55292
|
return { handled: true };
|
|
55177
55293
|
};
|
|
55294
|
+
function parsePermissionMode(arg) {
|
|
55295
|
+
switch (arg?.toLowerCase()) {
|
|
55296
|
+
case "default":
|
|
55297
|
+
case "interactive":
|
|
55298
|
+
return "default";
|
|
55299
|
+
case "auto":
|
|
55300
|
+
return "auto";
|
|
55301
|
+
case "bypass":
|
|
55302
|
+
case "skip":
|
|
55303
|
+
return "bypass";
|
|
55304
|
+
default:
|
|
55305
|
+
return null;
|
|
55306
|
+
}
|
|
55307
|
+
}
|
|
55178
55308
|
var handlePermissions = async (ctx, args) => {
|
|
55179
|
-
const mode = args
|
|
55309
|
+
const mode = parsePermissionMode(args);
|
|
55180
55310
|
if (ctx.commandContext === "first-message") {
|
|
55181
|
-
if (mode
|
|
55311
|
+
if (mode) {
|
|
55182
55312
|
return {
|
|
55183
|
-
sessionOptions: {
|
|
55313
|
+
sessionOptions: {
|
|
55314
|
+
permissionMode: mode,
|
|
55315
|
+
forceInteractivePermissions: mode === "default"
|
|
55316
|
+
},
|
|
55184
55317
|
continueProcessing: true
|
|
55185
55318
|
};
|
|
55186
55319
|
}
|
|
55187
55320
|
return { handled: false };
|
|
55188
55321
|
}
|
|
55189
|
-
if (mode
|
|
55190
|
-
await ctx.
|
|
55191
|
-
|
|
55192
|
-
await ctx.client.createPost(`⚠️ Cannot upgrade to auto permissions - can only downgrade to interactive`, ctx.threadId);
|
|
55322
|
+
if (!mode) {
|
|
55323
|
+
await ctx.client.createPost(`⚠️ Unknown permission mode. Usage: \`!permissions default|auto|bypass\` (aliases: \`interactive\`, \`skip\`).`, ctx.threadId);
|
|
55324
|
+
return { handled: true };
|
|
55193
55325
|
}
|
|
55326
|
+
await ctx.sessionManager.setSessionPermissionMode(ctx.threadId, ctx.username, mode);
|
|
55194
55327
|
return { handled: true };
|
|
55195
55328
|
};
|
|
55196
55329
|
var handleWorktree = async (ctx, args) => {
|
|
@@ -55447,7 +55580,7 @@ import { existsSync as existsSync11 } from "fs";
|
|
|
55447
55580
|
// src/utils/keep-alive.ts
|
|
55448
55581
|
init_logger();
|
|
55449
55582
|
import { spawn as spawn2 } from "child_process";
|
|
55450
|
-
var
|
|
55583
|
+
var log10 = createLogger("keepalive");
|
|
55451
55584
|
|
|
55452
55585
|
class KeepAliveManager {
|
|
55453
55586
|
activeSessionCount = 0;
|
|
@@ -55462,7 +55595,7 @@ class KeepAliveManager {
|
|
|
55462
55595
|
if (!enabled && this.keepAliveProcess) {
|
|
55463
55596
|
this.stopKeepAlive();
|
|
55464
55597
|
}
|
|
55465
|
-
|
|
55598
|
+
log10.debug(`Keep-alive ${enabled ? "enabled" : "disabled"}`);
|
|
55466
55599
|
}
|
|
55467
55600
|
isEnabled() {
|
|
55468
55601
|
return this.enabled;
|
|
@@ -55472,7 +55605,7 @@ class KeepAliveManager {
|
|
|
55472
55605
|
}
|
|
55473
55606
|
sessionStarted() {
|
|
55474
55607
|
this.activeSessionCount++;
|
|
55475
|
-
|
|
55608
|
+
log10.debug(`Session started (${this.activeSessionCount} active)`);
|
|
55476
55609
|
if (this.activeSessionCount === 1) {
|
|
55477
55610
|
this.startKeepAlive();
|
|
55478
55611
|
}
|
|
@@ -55481,7 +55614,7 @@ class KeepAliveManager {
|
|
|
55481
55614
|
if (this.activeSessionCount > 0) {
|
|
55482
55615
|
this.activeSessionCount--;
|
|
55483
55616
|
}
|
|
55484
|
-
|
|
55617
|
+
log10.debug(`Session ended (${this.activeSessionCount} active)`);
|
|
55485
55618
|
if (this.activeSessionCount === 0) {
|
|
55486
55619
|
this.stopKeepAlive();
|
|
55487
55620
|
}
|
|
@@ -55495,11 +55628,11 @@ class KeepAliveManager {
|
|
|
55495
55628
|
}
|
|
55496
55629
|
startKeepAlive() {
|
|
55497
55630
|
if (!this.enabled) {
|
|
55498
|
-
|
|
55631
|
+
log10.debug("Keep-alive disabled, skipping");
|
|
55499
55632
|
return;
|
|
55500
55633
|
}
|
|
55501
55634
|
if (this.keepAliveProcess) {
|
|
55502
|
-
|
|
55635
|
+
log10.debug("Keep-alive already running");
|
|
55503
55636
|
return;
|
|
55504
55637
|
}
|
|
55505
55638
|
switch (this.platform) {
|
|
@@ -55513,12 +55646,12 @@ class KeepAliveManager {
|
|
|
55513
55646
|
this.startWindowsKeepAlive();
|
|
55514
55647
|
break;
|
|
55515
55648
|
default:
|
|
55516
|
-
|
|
55649
|
+
log10.warn(`Keep-alive not supported on ${this.platform}`);
|
|
55517
55650
|
}
|
|
55518
55651
|
}
|
|
55519
55652
|
stopKeepAlive() {
|
|
55520
55653
|
if (this.keepAliveProcess) {
|
|
55521
|
-
|
|
55654
|
+
log10.debug("Stopping keep-alive");
|
|
55522
55655
|
this.keepAliveProcess.kill();
|
|
55523
55656
|
this.keepAliveProcess = null;
|
|
55524
55657
|
}
|
|
@@ -55530,18 +55663,18 @@ class KeepAliveManager {
|
|
|
55530
55663
|
detached: false
|
|
55531
55664
|
});
|
|
55532
55665
|
this.keepAliveProcess.on("error", (err) => {
|
|
55533
|
-
|
|
55666
|
+
log10.error(`Failed to start caffeinate: ${err.message}`);
|
|
55534
55667
|
this.keepAliveProcess = null;
|
|
55535
55668
|
});
|
|
55536
55669
|
this.keepAliveProcess.on("exit", (code) => {
|
|
55537
55670
|
if (code !== null && code !== 0 && this.activeSessionCount > 0) {
|
|
55538
|
-
|
|
55671
|
+
log10.debug(`caffeinate exited with code ${code}`);
|
|
55539
55672
|
}
|
|
55540
55673
|
this.keepAliveProcess = null;
|
|
55541
55674
|
});
|
|
55542
|
-
|
|
55675
|
+
log10.info("Sleep prevention active (caffeinate)");
|
|
55543
55676
|
} catch (err) {
|
|
55544
|
-
|
|
55677
|
+
log10.error(`Failed to start caffeinate: ${err}`);
|
|
55545
55678
|
}
|
|
55546
55679
|
}
|
|
55547
55680
|
startLinuxKeepAlive() {
|
|
@@ -55557,19 +55690,19 @@ class KeepAliveManager {
|
|
|
55557
55690
|
detached: false
|
|
55558
55691
|
});
|
|
55559
55692
|
this.keepAliveProcess.on("error", (err) => {
|
|
55560
|
-
|
|
55693
|
+
log10.debug(`systemd-inhibit not available: ${err.message}`);
|
|
55561
55694
|
this.keepAliveProcess = null;
|
|
55562
55695
|
this.startLinuxKeepAliveFallback();
|
|
55563
55696
|
});
|
|
55564
55697
|
this.keepAliveProcess.on("exit", (code) => {
|
|
55565
55698
|
if (code !== null && code !== 0 && this.activeSessionCount > 0) {
|
|
55566
|
-
|
|
55699
|
+
log10.debug(`systemd-inhibit exited with code ${code}`);
|
|
55567
55700
|
}
|
|
55568
55701
|
this.keepAliveProcess = null;
|
|
55569
55702
|
});
|
|
55570
|
-
|
|
55703
|
+
log10.info("Sleep prevention active (systemd-inhibit)");
|
|
55571
55704
|
} catch (err) {
|
|
55572
|
-
|
|
55705
|
+
log10.debug(`Failed to start systemd-inhibit: ${err}`);
|
|
55573
55706
|
this.startLinuxKeepAliveFallback();
|
|
55574
55707
|
}
|
|
55575
55708
|
}
|
|
@@ -55583,15 +55716,15 @@ class KeepAliveManager {
|
|
|
55583
55716
|
detached: false
|
|
55584
55717
|
});
|
|
55585
55718
|
this.keepAliveProcess.on("error", (err) => {
|
|
55586
|
-
|
|
55719
|
+
log10.warn(`Linux keep-alive fallback not available: ${err.message}`);
|
|
55587
55720
|
this.keepAliveProcess = null;
|
|
55588
55721
|
});
|
|
55589
55722
|
this.keepAliveProcess.on("exit", () => {
|
|
55590
55723
|
this.keepAliveProcess = null;
|
|
55591
55724
|
});
|
|
55592
|
-
|
|
55725
|
+
log10.info("Sleep prevention active (xdg-screensaver)");
|
|
55593
55726
|
} catch (err) {
|
|
55594
|
-
|
|
55727
|
+
log10.warn(`Linux keep-alive not available: ${err}`);
|
|
55595
55728
|
}
|
|
55596
55729
|
}
|
|
55597
55730
|
startWindowsKeepAlive() {
|
|
@@ -55616,18 +55749,18 @@ class KeepAliveManager {
|
|
|
55616
55749
|
windowsHide: true
|
|
55617
55750
|
});
|
|
55618
55751
|
this.keepAliveProcess.on("error", (err) => {
|
|
55619
|
-
|
|
55752
|
+
log10.warn(`Windows keep-alive not available: ${err.message}`);
|
|
55620
55753
|
this.keepAliveProcess = null;
|
|
55621
55754
|
});
|
|
55622
55755
|
this.keepAliveProcess.on("exit", (code) => {
|
|
55623
55756
|
if (code !== null && code !== 0 && this.activeSessionCount > 0) {
|
|
55624
|
-
|
|
55757
|
+
log10.debug(`PowerShell keep-alive exited with code ${code}`);
|
|
55625
55758
|
}
|
|
55626
55759
|
this.keepAliveProcess = null;
|
|
55627
55760
|
});
|
|
55628
|
-
|
|
55761
|
+
log10.info("Sleep prevention active (SetThreadExecutionState)");
|
|
55629
55762
|
} catch (err) {
|
|
55630
|
-
|
|
55763
|
+
log10.warn(`Windows keep-alive not available: ${err}`);
|
|
55631
55764
|
}
|
|
55632
55765
|
}
|
|
55633
55766
|
}
|
|
@@ -55635,7 +55768,7 @@ var keepAlive = new KeepAliveManager;
|
|
|
55635
55768
|
|
|
55636
55769
|
// src/utils/error-handler/index.ts
|
|
55637
55770
|
init_logger();
|
|
55638
|
-
var
|
|
55771
|
+
var log11 = createLogger("error");
|
|
55639
55772
|
|
|
55640
55773
|
class SessionError extends Error {
|
|
55641
55774
|
sessionId;
|
|
@@ -55661,19 +55794,19 @@ async function handleError(error, context, severity = "recoverable") {
|
|
|
55661
55794
|
const sessionPart = sessionId ? ` (${formatShortId(sessionId)})` : "";
|
|
55662
55795
|
const logMessage = `${context.action}${sessionPart}: ${message}`;
|
|
55663
55796
|
if (severity === "recoverable") {
|
|
55664
|
-
|
|
55797
|
+
log11.warn(logMessage);
|
|
55665
55798
|
} else {
|
|
55666
|
-
|
|
55799
|
+
log11.error(logMessage, error instanceof Error ? error : undefined);
|
|
55667
55800
|
}
|
|
55668
55801
|
if (context.details) {
|
|
55669
|
-
|
|
55802
|
+
log11.debugJson("Error details", context.details);
|
|
55670
55803
|
}
|
|
55671
55804
|
if (context.notifyUser && context.session) {
|
|
55672
55805
|
try {
|
|
55673
55806
|
const fmt = context.session.platform.getFormatter();
|
|
55674
55807
|
await context.session.platform.createPost(`⚠️ ${fmt.formatBold("Error")}: ${context.action} failed - ${message}`, context.session.threadId);
|
|
55675
55808
|
} catch (notifyError) {
|
|
55676
|
-
|
|
55809
|
+
log11.warn(`Could not notify user: ${notifyError}`);
|
|
55677
55810
|
}
|
|
55678
55811
|
}
|
|
55679
55812
|
if (severity === "session-fatal" || severity === "system-fatal") {
|
|
@@ -55700,7 +55833,7 @@ async function logAndNotify(error, context) {
|
|
|
55700
55833
|
}
|
|
55701
55834
|
function logSilentError(context, error) {
|
|
55702
55835
|
const message = error instanceof Error ? error.message : String(error);
|
|
55703
|
-
|
|
55836
|
+
log11.debug(`[${context}] Silently caught: ${message}`);
|
|
55704
55837
|
}
|
|
55705
55838
|
|
|
55706
55839
|
// src/session/lifecycle.ts
|
|
@@ -55720,8 +55853,8 @@ function createSessionLog(baseLog) {
|
|
|
55720
55853
|
init_logger();
|
|
55721
55854
|
init_emoji();
|
|
55722
55855
|
init_worktree();
|
|
55723
|
-
var
|
|
55724
|
-
var sessionLog = createSessionLog(
|
|
55856
|
+
var log12 = createLogger("helpers");
|
|
55857
|
+
var sessionLog = createSessionLog(log12);
|
|
55725
55858
|
var POST_TYPES = {
|
|
55726
55859
|
info: "",
|
|
55727
55860
|
success: "✅",
|
|
@@ -55771,17 +55904,17 @@ async function postInteractiveAndRegister(session, message, reactions, registerP
|
|
|
55771
55904
|
registerPost(post2.id, session.threadId);
|
|
55772
55905
|
return post2;
|
|
55773
55906
|
}
|
|
55774
|
-
async function
|
|
55907
|
+
async function updatePost(session, postId, message) {
|
|
55775
55908
|
await withErrorHandling(() => session.platform.updatePost(postId, message), { action: "Update post", session });
|
|
55776
55909
|
}
|
|
55777
55910
|
async function updatePostSuccess(session, postId, message) {
|
|
55778
|
-
await
|
|
55911
|
+
await updatePost(session, postId, `✅ ${message}`);
|
|
55779
55912
|
}
|
|
55780
55913
|
async function updatePostError(session, postId, message) {
|
|
55781
|
-
await
|
|
55914
|
+
await updatePost(session, postId, `❌ ${message}`);
|
|
55782
55915
|
}
|
|
55783
55916
|
async function updatePostCancelled(session, postId, message) {
|
|
55784
|
-
await
|
|
55917
|
+
await updatePost(session, postId, `\uD83D\uDEAB ${message}`);
|
|
55785
55918
|
}
|
|
55786
55919
|
async function removeReaction(session, postId, emoji) {
|
|
55787
55920
|
await withErrorHandling(() => session.platform.removeReaction(postId, emoji), { action: `Remove ${emoji} reaction`, session });
|
|
@@ -55805,7 +55938,7 @@ function updateLastMessage(session, post2) {
|
|
|
55805
55938
|
// src/claude/quick-query.ts
|
|
55806
55939
|
init_spawn();
|
|
55807
55940
|
init_logger();
|
|
55808
|
-
var
|
|
55941
|
+
var log13 = createLogger("query");
|
|
55809
55942
|
async function quickQuery(options2) {
|
|
55810
55943
|
const {
|
|
55811
55944
|
prompt,
|
|
@@ -55821,7 +55954,7 @@ async function quickQuery(options2) {
|
|
|
55821
55954
|
args.push("--system-prompt", systemPrompt);
|
|
55822
55955
|
}
|
|
55823
55956
|
args.push(prompt);
|
|
55824
|
-
|
|
55957
|
+
log13.debug(`Quick query: model=${model}, timeout=${timeout}ms, prompt="${prompt.substring(0, 50)}..."`);
|
|
55825
55958
|
return new Promise((resolve5) => {
|
|
55826
55959
|
let stdout = "";
|
|
55827
55960
|
let stderr = "";
|
|
@@ -55835,7 +55968,7 @@ async function quickQuery(options2) {
|
|
|
55835
55968
|
if (!resolved) {
|
|
55836
55969
|
resolved = true;
|
|
55837
55970
|
proc.kill("SIGTERM");
|
|
55838
|
-
|
|
55971
|
+
log13.debug(`Quick query timed out after ${timeout}ms`);
|
|
55839
55972
|
resolve5({
|
|
55840
55973
|
success: false,
|
|
55841
55974
|
error: "timeout",
|
|
@@ -55853,7 +55986,7 @@ async function quickQuery(options2) {
|
|
|
55853
55986
|
if (!resolved) {
|
|
55854
55987
|
resolved = true;
|
|
55855
55988
|
clearTimeout(timeoutId);
|
|
55856
|
-
|
|
55989
|
+
log13.debug(`Quick query error: ${err.message}`);
|
|
55857
55990
|
resolve5({
|
|
55858
55991
|
success: false,
|
|
55859
55992
|
error: err.message,
|
|
@@ -55867,14 +56000,14 @@ async function quickQuery(options2) {
|
|
|
55867
56000
|
clearTimeout(timeoutId);
|
|
55868
56001
|
const durationMs = Date.now() - startTime;
|
|
55869
56002
|
if (code === 0 && stdout.trim()) {
|
|
55870
|
-
|
|
56003
|
+
log13.debug(`Quick query success: ${durationMs}ms, ${stdout.length} chars`);
|
|
55871
56004
|
resolve5({
|
|
55872
56005
|
success: true,
|
|
55873
56006
|
response: stdout.trim(),
|
|
55874
56007
|
durationMs
|
|
55875
56008
|
});
|
|
55876
56009
|
} else {
|
|
55877
|
-
|
|
56010
|
+
log13.debug(`Quick query failed: code=${code}, stderr=${stderr.substring(0, 100)}`);
|
|
55878
56011
|
resolve5({
|
|
55879
56012
|
success: false,
|
|
55880
56013
|
error: stderr || `exit code ${code}`,
|
|
@@ -55889,7 +56022,7 @@ async function quickQuery(options2) {
|
|
|
55889
56022
|
|
|
55890
56023
|
// src/operations/suggestions/title.ts
|
|
55891
56024
|
init_logger();
|
|
55892
|
-
var
|
|
56025
|
+
var log14 = createLogger("title");
|
|
55893
56026
|
var SUGGESTION_TIMEOUT = 15000;
|
|
55894
56027
|
var MIN_TITLE_LENGTH = 3;
|
|
55895
56028
|
var MAX_TITLE_LENGTH = 50;
|
|
@@ -55953,32 +56086,32 @@ function parseMetadata(response) {
|
|
|
55953
56086
|
const titleMatch = response.match(/TITLE:\s*(.+)/i);
|
|
55954
56087
|
const descMatch = response.match(/DESC:\s*(.+)/i);
|
|
55955
56088
|
if (!titleMatch || !descMatch) {
|
|
55956
|
-
|
|
56089
|
+
log14.debug("Failed to parse title/description from response");
|
|
55957
56090
|
return null;
|
|
55958
56091
|
}
|
|
55959
56092
|
let title = titleMatch[1].trim();
|
|
55960
56093
|
let description = descMatch[1].trim();
|
|
55961
56094
|
if (title.length < MIN_TITLE_LENGTH) {
|
|
55962
|
-
|
|
56095
|
+
log14.debug(`Title too short: ${title.length} chars`);
|
|
55963
56096
|
return null;
|
|
55964
56097
|
}
|
|
55965
56098
|
if (title.length > MAX_TITLE_LENGTH) {
|
|
55966
|
-
|
|
56099
|
+
log14.debug(`Title too long (${title.length} chars), truncating`);
|
|
55967
56100
|
title = truncateAtWord(title, MAX_TITLE_LENGTH);
|
|
55968
56101
|
}
|
|
55969
56102
|
if (description.length < MIN_DESC_LENGTH) {
|
|
55970
|
-
|
|
56103
|
+
log14.debug(`Description too short: ${description.length} chars`);
|
|
55971
56104
|
return null;
|
|
55972
56105
|
}
|
|
55973
56106
|
if (description.length > MAX_DESC_LENGTH) {
|
|
55974
|
-
|
|
56107
|
+
log14.debug(`Description too long (${description.length} chars), truncating`);
|
|
55975
56108
|
description = truncateAtWord(description, MAX_DESC_LENGTH);
|
|
55976
56109
|
}
|
|
55977
56110
|
return { title, description };
|
|
55978
56111
|
}
|
|
55979
56112
|
async function suggestSessionMetadata(context) {
|
|
55980
56113
|
const logContext = typeof context === "string" ? context.substring(0, 50) : context.originalTask.substring(0, 50);
|
|
55981
|
-
|
|
56114
|
+
log14.debug(`Suggesting title for: "${logContext}..."`);
|
|
55982
56115
|
try {
|
|
55983
56116
|
const result = await quickQuery({
|
|
55984
56117
|
prompt: buildTitlePrompt(context),
|
|
@@ -55986,23 +56119,23 @@ async function suggestSessionMetadata(context) {
|
|
|
55986
56119
|
timeout: SUGGESTION_TIMEOUT
|
|
55987
56120
|
});
|
|
55988
56121
|
if (!result.success || !result.response) {
|
|
55989
|
-
|
|
56122
|
+
log14.debug(`Title suggestion failed: ${result.error || "no response"}`);
|
|
55990
56123
|
return null;
|
|
55991
56124
|
}
|
|
55992
56125
|
const metadata = parseMetadata(result.response);
|
|
55993
56126
|
if (metadata) {
|
|
55994
|
-
|
|
56127
|
+
log14.debug(`Got title: "${metadata.title}" (${result.durationMs}ms)`);
|
|
55995
56128
|
}
|
|
55996
56129
|
return metadata;
|
|
55997
56130
|
} catch (err) {
|
|
55998
|
-
|
|
56131
|
+
log14.debug(`Title suggestion error: ${err}`);
|
|
55999
56132
|
return null;
|
|
56000
56133
|
}
|
|
56001
56134
|
}
|
|
56002
56135
|
|
|
56003
56136
|
// src/operations/suggestions/tag.ts
|
|
56004
56137
|
init_logger();
|
|
56005
|
-
var
|
|
56138
|
+
var log15 = createLogger("tags");
|
|
56006
56139
|
var SUGGESTION_TIMEOUT2 = 15000;
|
|
56007
56140
|
var MAX_TAGS = 3;
|
|
56008
56141
|
var VALID_TAGS = [
|
|
@@ -56034,7 +56167,7 @@ function parseTags(response) {
|
|
|
56034
56167
|
return [...new Set(tags)].slice(0, MAX_TAGS);
|
|
56035
56168
|
}
|
|
56036
56169
|
async function suggestSessionTags(userMessage) {
|
|
56037
|
-
|
|
56170
|
+
log15.debug(`Suggesting tags for: "${userMessage.substring(0, 50)}..."`);
|
|
56038
56171
|
try {
|
|
56039
56172
|
const result = await quickQuery({
|
|
56040
56173
|
prompt: buildTagPrompt(userMessage),
|
|
@@ -56042,14 +56175,14 @@ async function suggestSessionTags(userMessage) {
|
|
|
56042
56175
|
timeout: SUGGESTION_TIMEOUT2
|
|
56043
56176
|
});
|
|
56044
56177
|
if (!result.success || !result.response) {
|
|
56045
|
-
|
|
56178
|
+
log15.debug(`Tag suggestion failed: ${result.error || "no response"}`);
|
|
56046
56179
|
return [];
|
|
56047
56180
|
}
|
|
56048
56181
|
const tags = parseTags(result.response);
|
|
56049
|
-
|
|
56182
|
+
log15.debug(`Got tags: ${tags.join(", ")} (${result.durationMs}ms)`);
|
|
56050
56183
|
return tags;
|
|
56051
56184
|
} catch (err) {
|
|
56052
|
-
|
|
56185
|
+
log15.debug(`Tag suggestion error: ${err}`);
|
|
56053
56186
|
return [];
|
|
56054
56187
|
}
|
|
56055
56188
|
}
|
|
@@ -57968,6 +58101,18 @@ class ContentExecutor extends BaseExecutor {
|
|
|
57968
58101
|
}
|
|
57969
58102
|
this.state = this.getInitialState();
|
|
57970
58103
|
}
|
|
58104
|
+
async tryUpdatePost(ctx, postId, content, logTag, successDetails, failureDetails, onSuccess, onFailure) {
|
|
58105
|
+
try {
|
|
58106
|
+
await ctx.platform.updatePost(postId, content);
|
|
58107
|
+
onSuccess();
|
|
58108
|
+
ctx.threadLogger?.logExecutor("content", "update", postId, successDetails, logTag);
|
|
58109
|
+
} catch (err) {
|
|
58110
|
+
ctx.logger.debug(`Update failed (${logTag}): ${err}`);
|
|
58111
|
+
const resolvedFailureDetails = typeof failureDetails === "function" ? failureDetails(err) : failureDetails;
|
|
58112
|
+
ctx.threadLogger?.logExecutor("content", "error", postId, resolvedFailureDetails, logTag);
|
|
58113
|
+
onFailure();
|
|
58114
|
+
}
|
|
58115
|
+
}
|
|
57971
58116
|
closeCurrentPost(ctx) {
|
|
57972
58117
|
const oldPostId = this.state.currentPostId;
|
|
57973
58118
|
const contentLength = this.state.currentPostContent.length;
|
|
@@ -58066,23 +58211,13 @@ class ContentExecutor extends BaseExecutor {
|
|
|
58066
58211
|
await this.createNewPost(ctx, content, pendingAtFlushStart);
|
|
58067
58212
|
return;
|
|
58068
58213
|
}
|
|
58069
|
-
|
|
58070
|
-
await ctx.platform.updatePost(postId, combinedContent2);
|
|
58214
|
+
await this.tryUpdatePost(ctx, postId, combinedContent2, "flush", { newContentLength: content.length, combinedLength: combinedContent2.length }, (err) => ({ failedOp: "updatePost", error: String(err) }), () => {
|
|
58071
58215
|
this.state.currentPostContent = combinedContent2;
|
|
58072
58216
|
this.clearFlushedContent(pendingAtFlushStart);
|
|
58073
|
-
|
|
58074
|
-
newContentLength: content.length,
|
|
58075
|
-
combinedLength: combinedContent2.length
|
|
58076
|
-
}, "flush");
|
|
58077
|
-
} catch (err) {
|
|
58078
|
-
ctx.logger.debug(`Update failed, will create new post on next flush: ${err}`);
|
|
58079
|
-
ctx.threadLogger?.logExecutor("content", "error", postId, {
|
|
58080
|
-
failedOp: "updatePost",
|
|
58081
|
-
error: String(err)
|
|
58082
|
-
}, "flush");
|
|
58217
|
+
}, () => {
|
|
58083
58218
|
this.state.currentPostId = null;
|
|
58084
58219
|
this.state.currentPostContent = "";
|
|
58085
|
-
}
|
|
58220
|
+
});
|
|
58086
58221
|
} else {
|
|
58087
58222
|
const chunks = splitContentForHeight(content, ctx.contentBreaker);
|
|
58088
58223
|
ctx.threadLogger?.logExecutor("content", "create_start", "none", {
|
|
@@ -58143,21 +58278,13 @@ class ContentExecutor extends BaseExecutor {
|
|
|
58143
58278
|
breakPoint = bestBreakPoint;
|
|
58144
58279
|
} else {
|
|
58145
58280
|
if (this.state.currentPostId) {
|
|
58146
|
-
|
|
58147
|
-
|
|
58281
|
+
const postId = this.state.currentPostId;
|
|
58282
|
+
await this.tryUpdatePost(ctx, postId, content, "handleSplit", { reason: "soft_break_no_breakpoint", contentLength: content.length }, { reason: "soft_break_no_breakpoint_failed" }, () => {
|
|
58148
58283
|
this.state.currentPostContent = content;
|
|
58149
58284
|
this.clearFlushedContent(pendingAtFlushStart);
|
|
58150
|
-
|
|
58151
|
-
reason: "soft_break_no_breakpoint",
|
|
58152
|
-
contentLength: content.length
|
|
58153
|
-
}, "handleSplit");
|
|
58154
|
-
} catch {
|
|
58155
|
-
ctx.logger.debug("Update failed (no breakpoint), will create new post on next flush");
|
|
58156
|
-
ctx.threadLogger?.logExecutor("content", "error", this.state.currentPostId, {
|
|
58157
|
-
reason: "soft_break_no_breakpoint_failed"
|
|
58158
|
-
}, "handleSplit");
|
|
58285
|
+
}, () => {
|
|
58159
58286
|
this.state.currentPostId = null;
|
|
58160
|
-
}
|
|
58287
|
+
});
|
|
58161
58288
|
}
|
|
58162
58289
|
return;
|
|
58163
58290
|
}
|
|
@@ -58165,22 +58292,14 @@ class ContentExecutor extends BaseExecutor {
|
|
|
58165
58292
|
if (codeBlockOpenPosition !== undefined) {
|
|
58166
58293
|
if (codeBlockOpenPosition === 0) {
|
|
58167
58294
|
if (this.state.currentPostId) {
|
|
58168
|
-
|
|
58169
|
-
|
|
58295
|
+
const postId = this.state.currentPostId;
|
|
58296
|
+
await this.tryUpdatePost(ctx, postId, content, "handleSplit", { reason: "code_block_at_start", contentLength: content.length }, { reason: "code_block_at_start_failed" }, () => {
|
|
58170
58297
|
this.state.currentPostContent = content;
|
|
58171
58298
|
this.clearFlushedContent(pendingAtFlushStart);
|
|
58172
|
-
|
|
58173
|
-
reason: "code_block_at_start",
|
|
58174
|
-
contentLength: content.length
|
|
58175
|
-
}, "handleSplit");
|
|
58176
|
-
} catch {
|
|
58177
|
-
ctx.logger.debug("Update failed (code block at start)");
|
|
58178
|
-
ctx.threadLogger?.logExecutor("content", "error", this.state.currentPostId, {
|
|
58179
|
-
reason: "code_block_at_start_failed"
|
|
58180
|
-
}, "handleSplit");
|
|
58299
|
+
}, () => {
|
|
58181
58300
|
this.state.currentPostId = null;
|
|
58182
58301
|
this.state.currentPostContent = "";
|
|
58183
|
-
}
|
|
58302
|
+
});
|
|
58184
58303
|
}
|
|
58185
58304
|
return;
|
|
58186
58305
|
}
|
|
@@ -58190,22 +58309,14 @@ class ContentExecutor extends BaseExecutor {
|
|
|
58190
58309
|
breakPoint = breakBeforeCodeBlock;
|
|
58191
58310
|
} else {
|
|
58192
58311
|
if (this.state.currentPostId) {
|
|
58193
|
-
|
|
58194
|
-
|
|
58312
|
+
const postId = this.state.currentPostId;
|
|
58313
|
+
await this.tryUpdatePost(ctx, postId, content, "handleSplit", { reason: "no_break_before_code_block", contentLength: content.length }, { reason: "no_break_before_code_block_failed" }, () => {
|
|
58195
58314
|
this.state.currentPostContent = content;
|
|
58196
58315
|
this.clearFlushedContent(pendingAtFlushStart);
|
|
58197
|
-
|
|
58198
|
-
reason: "no_break_before_code_block",
|
|
58199
|
-
contentLength: content.length
|
|
58200
|
-
}, "handleSplit");
|
|
58201
|
-
} catch {
|
|
58202
|
-
ctx.logger.debug("Update failed (no break before code block)");
|
|
58203
|
-
ctx.threadLogger?.logExecutor("content", "error", this.state.currentPostId, {
|
|
58204
|
-
reason: "no_break_before_code_block_failed"
|
|
58205
|
-
}, "handleSplit");
|
|
58316
|
+
}, () => {
|
|
58206
58317
|
this.state.currentPostId = null;
|
|
58207
58318
|
this.state.currentPostContent = "";
|
|
58208
|
-
}
|
|
58319
|
+
});
|
|
58209
58320
|
}
|
|
58210
58321
|
return;
|
|
58211
58322
|
}
|
|
@@ -58213,19 +58324,8 @@ class ContentExecutor extends BaseExecutor {
|
|
|
58213
58324
|
const firstPart = content.substring(0, breakPoint).trim();
|
|
58214
58325
|
const remainder = content.substring(breakPoint).trim();
|
|
58215
58326
|
if (this.state.currentPostId) {
|
|
58216
|
-
|
|
58217
|
-
|
|
58218
|
-
ctx.threadLogger?.logExecutor("content", "update", this.state.currentPostId, {
|
|
58219
|
-
reason: "split_first_part",
|
|
58220
|
-
firstPartLength: firstPart.length,
|
|
58221
|
-
remainderLength: remainder.length
|
|
58222
|
-
}, "handleSplit");
|
|
58223
|
-
} catch {
|
|
58224
|
-
ctx.logger.debug("Update failed during split, continuing with new post");
|
|
58225
|
-
ctx.threadLogger?.logExecutor("content", "error", this.state.currentPostId, {
|
|
58226
|
-
reason: "split_first_part_failed"
|
|
58227
|
-
}, "handleSplit");
|
|
58228
|
-
}
|
|
58327
|
+
const postId = this.state.currentPostId;
|
|
58328
|
+
await this.tryUpdatePost(ctx, postId, firstPart, "handleSplit", { reason: "split_first_part", firstPartLength: firstPart.length, remainderLength: remainder.length }, { reason: "split_first_part_failed" }, () => {}, () => {});
|
|
58229
58329
|
}
|
|
58230
58330
|
this.state.currentPostId = null;
|
|
58231
58331
|
this.state.currentPostContent = "";
|
|
@@ -59591,7 +59691,7 @@ class BugReportExecutor extends BaseExecutor {
|
|
|
59591
59691
|
// src/operations/executors/worktree-prompt.ts
|
|
59592
59692
|
init_emoji();
|
|
59593
59693
|
init_logger();
|
|
59594
|
-
var
|
|
59694
|
+
var log16 = createLogger("wt-prompt");
|
|
59595
59695
|
// src/operations/message-manager.ts
|
|
59596
59696
|
init_logger();
|
|
59597
59697
|
|
|
@@ -59628,7 +59728,7 @@ var import_yauzl = __toESM(require_yauzl(), 1);
|
|
|
59628
59728
|
import { createGunzip } from "zlib";
|
|
59629
59729
|
import { pipeline } from "stream/promises";
|
|
59630
59730
|
import { Readable, Writable } from "stream";
|
|
59631
|
-
var
|
|
59731
|
+
var log17 = createLogger("streaming");
|
|
59632
59732
|
var MAX_PDF_SIZE = 32 * 1024 * 1024;
|
|
59633
59733
|
var MAX_TEXT_FILE_SIZE = 1 * 1024 * 1024;
|
|
59634
59734
|
var MAX_DECOMPRESSED_SIZE = 10 * 1024 * 1024;
|
|
@@ -59754,7 +59854,7 @@ async function processImageFile(file, platform, debug = false) {
|
|
|
59754
59854
|
const buffer = await platform.downloadFile(file.id);
|
|
59755
59855
|
const base64 = buffer.toString("base64");
|
|
59756
59856
|
if (debug) {
|
|
59757
|
-
|
|
59857
|
+
log17.debug(`Attached image: ${file.name} (${file.mimeType}, ${Math.round(buffer.length / 1024)}KB)`);
|
|
59758
59858
|
}
|
|
59759
59859
|
return {
|
|
59760
59860
|
block: {
|
|
@@ -59767,7 +59867,7 @@ async function processImageFile(file, platform, debug = false) {
|
|
|
59767
59867
|
}
|
|
59768
59868
|
};
|
|
59769
59869
|
} catch (err) {
|
|
59770
|
-
|
|
59870
|
+
log17.error(`Failed to download image ${file.name}: ${err}`);
|
|
59771
59871
|
return {
|
|
59772
59872
|
skipped: {
|
|
59773
59873
|
name: file.name,
|
|
@@ -59798,7 +59898,7 @@ async function processPdfFile(file, platform, debug = false) {
|
|
|
59798
59898
|
}
|
|
59799
59899
|
const base64 = buffer.toString("base64");
|
|
59800
59900
|
if (debug) {
|
|
59801
|
-
|
|
59901
|
+
log17.debug(`Attached PDF: ${file.name} (${Math.round(buffer.length / 1024)}KB)`);
|
|
59802
59902
|
}
|
|
59803
59903
|
return {
|
|
59804
59904
|
block: {
|
|
@@ -59812,7 +59912,7 @@ async function processPdfFile(file, platform, debug = false) {
|
|
|
59812
59912
|
}
|
|
59813
59913
|
};
|
|
59814
59914
|
} catch (err) {
|
|
59815
|
-
|
|
59915
|
+
log17.error(`Failed to process PDF ${file.name}: ${err}`);
|
|
59816
59916
|
return {
|
|
59817
59917
|
skipped: {
|
|
59818
59918
|
name: file.name,
|
|
@@ -59843,7 +59943,7 @@ async function processTextFile(file, platform, debug = false) {
|
|
|
59843
59943
|
}
|
|
59844
59944
|
const content = buffer.toString("utf-8");
|
|
59845
59945
|
if (debug) {
|
|
59846
|
-
|
|
59946
|
+
log17.debug(`Attached text file: ${file.name} (${Math.round(buffer.length / 1024)}KB)`);
|
|
59847
59947
|
}
|
|
59848
59948
|
const wrappedContent = formatTextFileContent(file.name, content);
|
|
59849
59949
|
return {
|
|
@@ -59853,7 +59953,7 @@ async function processTextFile(file, platform, debug = false) {
|
|
|
59853
59953
|
}
|
|
59854
59954
|
};
|
|
59855
59955
|
} catch (err) {
|
|
59856
|
-
|
|
59956
|
+
log17.error(`Failed to process text file ${file.name}: ${err}`);
|
|
59857
59957
|
return {
|
|
59858
59958
|
skipped: {
|
|
59859
59959
|
name: file.name,
|
|
@@ -59911,7 +60011,7 @@ async function processGzipFile(file, platform, debug = false) {
|
|
|
59911
60011
|
compressedBuffer = await platform.downloadFile(file.id);
|
|
59912
60012
|
} catch (err) {
|
|
59913
60013
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
59914
|
-
|
|
60014
|
+
log17.error(`Failed to download gzip file ${file.name}: ${errorMessage}`);
|
|
59915
60015
|
return {
|
|
59916
60016
|
skipped: {
|
|
59917
60017
|
name: file.name,
|
|
@@ -59921,7 +60021,7 @@ async function processGzipFile(file, platform, debug = false) {
|
|
|
59921
60021
|
};
|
|
59922
60022
|
}
|
|
59923
60023
|
if (file.size && compressedBuffer.length !== file.size) {
|
|
59924
|
-
|
|
60024
|
+
log17.warn(`Downloaded size mismatch for ${file.name}: expected ${file.size}, got ${compressedBuffer.length}`);
|
|
59925
60025
|
}
|
|
59926
60026
|
let decompressedBuffer;
|
|
59927
60027
|
try {
|
|
@@ -59957,7 +60057,7 @@ async function processGzipFile(file, platform, debug = false) {
|
|
|
59957
60057
|
const innerFilename = file.name.toLowerCase().endsWith(".gz") ? file.name.slice(0, -3) : file.name;
|
|
59958
60058
|
const contentType = detectDecompressedContentType(decompressedBuffer, innerFilename);
|
|
59959
60059
|
if (debug) {
|
|
59960
|
-
|
|
60060
|
+
log17.debug(`Decompressed ${file.name}: ${Math.round(decompressedBuffer.length / 1024)}KB, detected type: ${contentType}`);
|
|
59961
60061
|
}
|
|
59962
60062
|
if (contentType === "pdf") {
|
|
59963
60063
|
const base64 = decompressedBuffer.toString("base64");
|
|
@@ -59992,7 +60092,7 @@ async function processGzipFile(file, platform, debug = false) {
|
|
|
59992
60092
|
}
|
|
59993
60093
|
} catch (err) {
|
|
59994
60094
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
59995
|
-
|
|
60095
|
+
log17.error(`Failed to process gzip file ${file.name}: ${errorMessage}`);
|
|
59996
60096
|
return {
|
|
59997
60097
|
skipped: {
|
|
59998
60098
|
name: file.name,
|
|
@@ -60044,7 +60144,7 @@ async function processZipFile(file, platform, debug = false) {
|
|
|
60044
60144
|
}
|
|
60045
60145
|
const zipBuffer = await platform.downloadFile(file.id);
|
|
60046
60146
|
if (debug) {
|
|
60047
|
-
|
|
60147
|
+
log17.debug(`Processing zip file ${file.name}: ${Math.round(zipBuffer.length / 1024)}KB`);
|
|
60048
60148
|
}
|
|
60049
60149
|
const zipfile = await new Promise((resolve5, reject) => {
|
|
60050
60150
|
import_yauzl.default.fromBuffer(zipBuffer, { lazyEntries: true }, (err, zf) => {
|
|
@@ -60118,7 +60218,7 @@ async function processZipFile(file, platform, debug = false) {
|
|
|
60118
60218
|
const buffer = await extractZipEntry(zipfile2, entry);
|
|
60119
60219
|
const contentType = detectDecompressedContentType(buffer, entry.fileName);
|
|
60120
60220
|
if (debug) {
|
|
60121
|
-
|
|
60221
|
+
log17.debug(`Extracted ${entry.fileName}: ${Math.round(buffer.length / 1024)}KB, type: ${contentType}`);
|
|
60122
60222
|
}
|
|
60123
60223
|
if (contentType === "pdf") {
|
|
60124
60224
|
const base64 = buffer.toString("base64");
|
|
@@ -60161,11 +60261,11 @@ async function processZipFile(file, platform, debug = false) {
|
|
|
60161
60261
|
});
|
|
60162
60262
|
zipfile2.close();
|
|
60163
60263
|
if (debug) {
|
|
60164
|
-
|
|
60264
|
+
log17.debug(`Zip ${file.name}: processed ${processedCount} files, skipped ${skipped.length}`);
|
|
60165
60265
|
}
|
|
60166
60266
|
return { blocks, skipped };
|
|
60167
60267
|
} catch (err) {
|
|
60168
|
-
|
|
60268
|
+
log17.error(`Failed to process zip file ${file.name}: ${err}`);
|
|
60169
60269
|
return {
|
|
60170
60270
|
blocks: [],
|
|
60171
60271
|
skipped: [{
|
|
@@ -60262,7 +60362,7 @@ async function processFiles(platform, files, debug = false) {
|
|
|
60262
60362
|
blocks.push(...zipResult.blocks);
|
|
60263
60363
|
for (const s of zipResult.skipped) {
|
|
60264
60364
|
skipped.push(s);
|
|
60265
|
-
|
|
60365
|
+
log17.warn(`Skipped file ${s.name}: ${s.reason}`);
|
|
60266
60366
|
}
|
|
60267
60367
|
continue;
|
|
60268
60368
|
}
|
|
@@ -60296,7 +60396,7 @@ async function processFiles(platform, files, debug = false) {
|
|
|
60296
60396
|
}
|
|
60297
60397
|
if (result.skipped) {
|
|
60298
60398
|
skipped.push(result.skipped);
|
|
60299
|
-
|
|
60399
|
+
log17.warn(`Skipped file ${result.skipped.name}: ${result.skipped.reason}`);
|
|
60300
60400
|
}
|
|
60301
60401
|
}
|
|
60302
60402
|
return { blocks, skipped };
|
|
@@ -60329,7 +60429,7 @@ function stopTyping(session) {
|
|
|
60329
60429
|
}
|
|
60330
60430
|
|
|
60331
60431
|
// src/operations/message-manager.ts
|
|
60332
|
-
var
|
|
60432
|
+
var log18 = createLogger("msg-mgr");
|
|
60333
60433
|
|
|
60334
60434
|
class MessageManager {
|
|
60335
60435
|
platform;
|
|
@@ -60355,7 +60455,8 @@ class MessageManager {
|
|
|
60355
60455
|
emitSessionUpdateCallback;
|
|
60356
60456
|
toolStartTimes = new Map;
|
|
60357
60457
|
flushTimer = null;
|
|
60358
|
-
static
|
|
60458
|
+
static DEFAULT_FLUSH_DELAY_MS = 500;
|
|
60459
|
+
flushDelayMs;
|
|
60359
60460
|
events;
|
|
60360
60461
|
constructor(options2) {
|
|
60361
60462
|
this.session = options2.session;
|
|
@@ -60370,6 +60471,7 @@ class MessageManager {
|
|
|
60370
60471
|
this.buildMessageContentCallback = options2.buildMessageContent;
|
|
60371
60472
|
this.startTypingCallback = options2.startTyping;
|
|
60372
60473
|
this.emitSessionUpdateCallback = options2.emitSessionUpdate;
|
|
60474
|
+
this.flushDelayMs = options2.flushDelayMs ?? MessageManager.DEFAULT_FLUSH_DELAY_MS;
|
|
60373
60475
|
this.events = createMessageManagerEvents();
|
|
60374
60476
|
this.contentBreaker = new DefaultContentBreaker;
|
|
60375
60477
|
this.contentExecutor = new ContentExecutor({
|
|
@@ -60417,7 +60519,7 @@ class MessageManager {
|
|
|
60417
60519
|
});
|
|
60418
60520
|
}
|
|
60419
60521
|
async handleEvent(event) {
|
|
60420
|
-
const logger =
|
|
60522
|
+
const logger = log18.forSession(this.sessionId);
|
|
60421
60523
|
const transformCtx = {
|
|
60422
60524
|
sessionId: this.sessionId,
|
|
60423
60525
|
formatter: this.platform.getFormatter(),
|
|
@@ -60467,7 +60569,7 @@ class MessageManager {
|
|
|
60467
60569
|
}
|
|
60468
60570
|
}
|
|
60469
60571
|
async executeOperation(op) {
|
|
60470
|
-
const logger =
|
|
60572
|
+
const logger = log18.forSession(this.sessionId);
|
|
60471
60573
|
const ctx = this.getExecutorContext();
|
|
60472
60574
|
try {
|
|
60473
60575
|
if (isContentOp(op)) {
|
|
@@ -60516,7 +60618,7 @@ class MessageManager {
|
|
|
60516
60618
|
this.flushTimer = null;
|
|
60517
60619
|
const flushOp = createFlushOp(this.sessionId, "soft_threshold");
|
|
60518
60620
|
await this.contentExecutor.executeFlush(flushOp, ctx);
|
|
60519
|
-
},
|
|
60621
|
+
}, this.flushDelayMs);
|
|
60520
60622
|
}
|
|
60521
60623
|
cancelScheduledFlush() {
|
|
60522
60624
|
if (this.flushTimer) {
|
|
@@ -60535,7 +60637,7 @@ class MessageManager {
|
|
|
60535
60637
|
threadId: this.threadId,
|
|
60536
60638
|
platform: this.platform,
|
|
60537
60639
|
formatter: this.platform.getFormatter(),
|
|
60538
|
-
logger:
|
|
60640
|
+
logger: log18.forSession(this.sessionId),
|
|
60539
60641
|
postTracker: this.postTracker,
|
|
60540
60642
|
contentBreaker: this.contentBreaker,
|
|
60541
60643
|
threadLogger: this.session.threadLogger,
|
|
@@ -60746,13 +60848,13 @@ class MessageManager {
|
|
|
60746
60848
|
return this.systemExecutor.postSuccess(message, this.getExecutorContext());
|
|
60747
60849
|
}
|
|
60748
60850
|
async prepareForUserMessage() {
|
|
60749
|
-
const logger =
|
|
60851
|
+
const logger = log18.forSession(this.sessionId);
|
|
60750
60852
|
logger.debug("Preparing for new user message");
|
|
60751
60853
|
await this.closeCurrentPost();
|
|
60752
60854
|
await this.bumpTaskList();
|
|
60753
60855
|
}
|
|
60754
60856
|
async handleUserMessage(message, files, username, displayName) {
|
|
60755
|
-
const logger =
|
|
60857
|
+
const logger = log18.forSession(this.sessionId);
|
|
60756
60858
|
if (!this.session.claude.isRunning()) {
|
|
60757
60859
|
logger.debug("Claude not running, ignoring user message");
|
|
60758
60860
|
return false;
|
|
@@ -60779,7 +60881,7 @@ class MessageManager {
|
|
|
60779
60881
|
return this.session;
|
|
60780
60882
|
}
|
|
60781
60883
|
async handleReaction(postId, emoji, user, action) {
|
|
60782
|
-
const logger =
|
|
60884
|
+
const logger = log18.forSession(this.sessionId);
|
|
60783
60885
|
const ctx = this.getExecutorContext();
|
|
60784
60886
|
logger.debug(`Routing reaction: postId=${postId}, emoji=${emoji}, user=${user}, action=${action}`);
|
|
60785
60887
|
if (await this.questionApprovalExecutor.handleReaction(postId, emoji, user, action, ctx)) {
|
|
@@ -60975,7 +61077,7 @@ function formatPullRequestLink(url, formatter) {
|
|
|
60975
61077
|
}
|
|
60976
61078
|
|
|
60977
61079
|
// src/operations/sticky-message/handler.ts
|
|
60978
|
-
var
|
|
61080
|
+
var log19 = createLogger("sticky");
|
|
60979
61081
|
var botStartedAt = new Date;
|
|
60980
61082
|
function getPendingPrompts(session) {
|
|
60981
61083
|
const prompts2 = [];
|
|
@@ -61050,21 +61152,21 @@ function initialize(store) {
|
|
|
61050
61152
|
stickyPostIds.set(platformId, postId);
|
|
61051
61153
|
}
|
|
61052
61154
|
if (persistedIds.size > 0) {
|
|
61053
|
-
|
|
61155
|
+
log19.info(`\uD83D\uDCCC Restored ${persistedIds.size} sticky post ID(s) from persistence`);
|
|
61054
61156
|
}
|
|
61055
61157
|
}
|
|
61056
61158
|
function setPlatformPaused(platformId, paused) {
|
|
61057
61159
|
if (paused) {
|
|
61058
61160
|
pausedPlatforms.set(platformId, true);
|
|
61059
|
-
|
|
61161
|
+
log19.debug(`Platform ${platformId} marked as paused`);
|
|
61060
61162
|
} else {
|
|
61061
61163
|
pausedPlatforms.delete(platformId);
|
|
61062
|
-
|
|
61164
|
+
log19.debug(`Platform ${platformId} marked as active`);
|
|
61063
61165
|
}
|
|
61064
61166
|
}
|
|
61065
61167
|
function setShuttingDown(shuttingDown) {
|
|
61066
61168
|
isShuttingDown = shuttingDown;
|
|
61067
|
-
|
|
61169
|
+
log19.debug(`Bot shutdown state: ${shuttingDown}`);
|
|
61068
61170
|
}
|
|
61069
61171
|
function getTaskContent(session) {
|
|
61070
61172
|
const taskState = session.messageManager?.getTaskListState();
|
|
@@ -61168,8 +61270,7 @@ async function buildStatusBar(sessionCount, config, formatter, platformId) {
|
|
|
61168
61270
|
const label = cooling > 0 ? `\uD83D\uDD11 ${available}/${total} accounts (${cooling} cooling)` : `\uD83D\uDD11 ${total} account${total === 1 ? "" : "s"}`;
|
|
61169
61271
|
items.push(formatter.formatCode(label));
|
|
61170
61272
|
}
|
|
61171
|
-
|
|
61172
|
-
items.push(formatter.formatCode(permMode));
|
|
61273
|
+
items.push(formatter.formatCode(permissionModeDisplay(config.permissionMode).chip));
|
|
61173
61274
|
if (config.worktreeMode === "require") {
|
|
61174
61275
|
items.push(formatter.formatCode("\uD83C\uDF3F Worktree: require"));
|
|
61175
61276
|
} else if (config.worktreeMode === "off") {
|
|
@@ -61376,12 +61477,12 @@ async function validateLastMessageIds(platform, sessions) {
|
|
|
61376
61477
|
try {
|
|
61377
61478
|
const post2 = await platform.getPost(lastMessageId);
|
|
61378
61479
|
if (!post2) {
|
|
61379
|
-
|
|
61480
|
+
log19.debug(`lastMessageId ${lastMessageId.substring(0, 8)} for session ${session.sessionId} was deleted, clearing`);
|
|
61380
61481
|
session.lastMessageId = undefined;
|
|
61381
61482
|
session.lastMessageTs = undefined;
|
|
61382
61483
|
}
|
|
61383
61484
|
} catch (err) {
|
|
61384
|
-
|
|
61485
|
+
log19.debug(`Failed to validate lastMessageId for session ${session.sessionId}, clearing: ${err}`);
|
|
61385
61486
|
session.lastMessageId = undefined;
|
|
61386
61487
|
session.lastMessageTs = undefined;
|
|
61387
61488
|
}
|
|
@@ -61390,63 +61491,63 @@ async function validateLastMessageIds(platform, sessions) {
|
|
|
61390
61491
|
}
|
|
61391
61492
|
async function updateStickyMessageImpl(platform, sessions, config) {
|
|
61392
61493
|
const platformSessions = [...sessions.values()].filter((s) => s.platformId === platform.platformId);
|
|
61393
|
-
|
|
61494
|
+
log19.debug(`updateStickyMessage for ${platform.platformId}, ${platformSessions.length} sessions`);
|
|
61394
61495
|
for (const s of platformSessions) {
|
|
61395
|
-
|
|
61496
|
+
log19.debug(` - ${s.sessionId}: title="${s.sessionTitle}" firstPrompt="${s.firstPrompt?.substring(0, 30)}..."`);
|
|
61396
61497
|
}
|
|
61397
61498
|
await validateLastMessageIds(platform, platformSessions);
|
|
61398
61499
|
const formatter = platform.getFormatter();
|
|
61399
61500
|
const content = await buildStickyMessage(sessions, platform.platformId, config, formatter, (threadId) => platform.getThreadLink(threadId));
|
|
61400
61501
|
const existingPostId = stickyPostIds.get(platform.platformId);
|
|
61401
61502
|
const shouldBump = needsBump.get(platform.platformId) ?? false;
|
|
61402
|
-
|
|
61503
|
+
log19.debug(`existingPostId: ${existingPostId || "(none)"}, needsBump: ${shouldBump}`);
|
|
61403
61504
|
try {
|
|
61404
61505
|
if (existingPostId && !shouldBump) {
|
|
61405
|
-
|
|
61506
|
+
log19.debug(`Updating existing post in place...`);
|
|
61406
61507
|
try {
|
|
61407
61508
|
await platform.updatePost(existingPostId, content);
|
|
61408
61509
|
try {
|
|
61409
61510
|
await platform.pinPost(existingPostId);
|
|
61410
|
-
|
|
61511
|
+
log19.debug(`Re-pinned post`);
|
|
61411
61512
|
} catch (pinErr) {
|
|
61412
|
-
|
|
61513
|
+
log19.debug(`Re-pin failed (might already be pinned): ${pinErr}`);
|
|
61413
61514
|
}
|
|
61414
|
-
|
|
61515
|
+
log19.debug(`Updated successfully`);
|
|
61415
61516
|
return;
|
|
61416
61517
|
} catch (err) {
|
|
61417
|
-
|
|
61518
|
+
log19.debug(`Update failed, will create new: ${err}`);
|
|
61418
61519
|
}
|
|
61419
61520
|
}
|
|
61420
61521
|
needsBump.set(platform.platformId, false);
|
|
61421
61522
|
if (existingPostId) {
|
|
61422
|
-
|
|
61523
|
+
log19.debug(`Unpinning and deleting existing post ${existingPostId.substring(0, 8)}...`);
|
|
61423
61524
|
try {
|
|
61424
61525
|
await platform.unpinPost(existingPostId);
|
|
61425
|
-
|
|
61526
|
+
log19.debug(`Unpinned successfully`);
|
|
61426
61527
|
} catch (err) {
|
|
61427
|
-
|
|
61528
|
+
log19.debug(`Unpin failed (probably already unpinned): ${err}`);
|
|
61428
61529
|
}
|
|
61429
61530
|
try {
|
|
61430
61531
|
await platform.deletePost(existingPostId);
|
|
61431
|
-
|
|
61532
|
+
log19.debug(`Deleted successfully`);
|
|
61432
61533
|
} catch (err) {
|
|
61433
|
-
|
|
61534
|
+
log19.debug(`Delete failed (probably already deleted): ${err}`);
|
|
61434
61535
|
}
|
|
61435
61536
|
stickyPostIds.delete(platform.platformId);
|
|
61436
61537
|
}
|
|
61437
|
-
|
|
61538
|
+
log19.debug(`Creating new post...`);
|
|
61438
61539
|
const post2 = await platform.createPost(content);
|
|
61439
61540
|
stickyPostIds.set(platform.platformId, post2.id);
|
|
61440
61541
|
try {
|
|
61441
61542
|
await platform.pinPost(post2.id);
|
|
61442
|
-
|
|
61543
|
+
log19.debug(`Pinned post successfully`);
|
|
61443
61544
|
} catch (err) {
|
|
61444
|
-
|
|
61545
|
+
log19.debug(`Failed to pin post: ${err}`);
|
|
61445
61546
|
}
|
|
61446
61547
|
if (sessionStore) {
|
|
61447
61548
|
sessionStore.saveStickyPostId(platform.platformId, post2.id);
|
|
61448
61549
|
}
|
|
61449
|
-
|
|
61550
|
+
log19.info(`\uD83D\uDCCC Created sticky message for ${platform.platformId}: ${formatShortId(post2.id)}`);
|
|
61450
61551
|
const excludePostIds = new Set;
|
|
61451
61552
|
if (sessionStore) {
|
|
61452
61553
|
for (const session of sessionStore.load().values()) {
|
|
@@ -61462,10 +61563,10 @@ async function updateStickyMessageImpl(platform, sessions, config) {
|
|
|
61462
61563
|
}
|
|
61463
61564
|
const botUser = await platform.getBotUser();
|
|
61464
61565
|
cleanupOldStickyMessages(platform, botUser.id, false, excludePostIds).catch((err) => {
|
|
61465
|
-
|
|
61566
|
+
log19.debug(`Background cleanup failed: ${err}`);
|
|
61466
61567
|
});
|
|
61467
61568
|
} catch (err) {
|
|
61468
|
-
|
|
61569
|
+
log19.error(`Failed to update sticky message for ${platform.platformId}`, err instanceof Error ? err : undefined);
|
|
61469
61570
|
}
|
|
61470
61571
|
}
|
|
61471
61572
|
async function updateAllStickyMessages(platforms, sessions, config) {
|
|
@@ -61490,7 +61591,7 @@ async function cleanupOldStickyMessages(platform, botUserId, forceRun = false, e
|
|
|
61490
61591
|
if (!forceRun) {
|
|
61491
61592
|
const lastRun = lastCleanupTime.get(platformId) || 0;
|
|
61492
61593
|
if (now - lastRun < CLEANUP_THROTTLE_MS) {
|
|
61493
|
-
|
|
61594
|
+
log19.debug(`Cleanup throttled for ${platformId} (last run ${Math.round((now - lastRun) / 1000)}s ago)`);
|
|
61494
61595
|
return;
|
|
61495
61596
|
}
|
|
61496
61597
|
}
|
|
@@ -61500,36 +61601,36 @@ async function cleanupOldStickyMessages(platform, botUserId, forceRun = false, e
|
|
|
61500
61601
|
const pinnedPostIds = await platform.getPinnedPosts();
|
|
61501
61602
|
const recentPinnedIds = pinnedPostIds.filter((id) => id !== currentStickyId && !excludePostIds?.has(id) && isRecentPost(id));
|
|
61502
61603
|
if (recentPinnedIds.length === 0) {
|
|
61503
|
-
|
|
61604
|
+
log19.debug(`No recent pinned posts to check (${pinnedPostIds.length} total, current: ${currentStickyId?.substring(0, 8) || "(none)"})`);
|
|
61504
61605
|
return;
|
|
61505
61606
|
}
|
|
61506
|
-
|
|
61607
|
+
log19.debug(`Checking ${recentPinnedIds.length} recent pinned posts (of ${pinnedPostIds.length} total)`);
|
|
61507
61608
|
for (const postId of recentPinnedIds) {
|
|
61508
61609
|
try {
|
|
61509
61610
|
const post2 = await platform.getPost(postId);
|
|
61510
61611
|
if (!post2)
|
|
61511
61612
|
continue;
|
|
61512
61613
|
if (post2.userId === botUserId) {
|
|
61513
|
-
|
|
61614
|
+
log19.debug(`Cleaning up old sticky: ${postId.substring(0, 8)}...`);
|
|
61514
61615
|
try {
|
|
61515
61616
|
await platform.unpinPost(postId);
|
|
61516
61617
|
await platform.deletePost(postId);
|
|
61517
|
-
|
|
61618
|
+
log19.info(`\uD83E\uDDF9 Cleaned up old sticky message: ${postId.substring(0, 8)}...`);
|
|
61518
61619
|
} catch (err) {
|
|
61519
|
-
|
|
61620
|
+
log19.debug(`Failed to cleanup ${postId}: ${err}`);
|
|
61520
61621
|
}
|
|
61521
61622
|
}
|
|
61522
61623
|
} catch (err) {
|
|
61523
|
-
|
|
61624
|
+
log19.debug(`Could not check post ${postId}: ${err}`);
|
|
61524
61625
|
}
|
|
61525
61626
|
}
|
|
61526
61627
|
} catch (err) {
|
|
61527
|
-
|
|
61628
|
+
log19.error(`Failed to cleanup old sticky messages`, err instanceof Error ? err : undefined);
|
|
61528
61629
|
}
|
|
61529
61630
|
}
|
|
61530
61631
|
// src/operations/bug-report/handler.ts
|
|
61531
61632
|
import { execSync as execSync2 } from "child_process";
|
|
61532
|
-
import { writeFileSync as
|
|
61633
|
+
import { writeFileSync as writeFileSync4, unlinkSync as unlinkSync3 } from "fs";
|
|
61533
61634
|
import { tmpdir as tmpdir2 } from "os";
|
|
61534
61635
|
import { join as join8 } from "path";
|
|
61535
61636
|
|
|
@@ -62113,7 +62214,7 @@ async function createGitHubIssue(title, body, workingDir) {
|
|
|
62113
62214
|
}
|
|
62114
62215
|
const bodyFile = join8(tmpdir2(), `bug-body-${Date.now()}.md`);
|
|
62115
62216
|
try {
|
|
62116
|
-
|
|
62217
|
+
writeFileSync4(bodyFile, body, "utf-8");
|
|
62117
62218
|
const cmd = `gh issue create --repo "${GITHUB_REPO}" --title "${escapeShell(title)}" --body-file "${bodyFile}"`;
|
|
62118
62219
|
const result = execSync2(cmd, {
|
|
62119
62220
|
cwd: workingDir,
|
|
@@ -62518,9 +62619,9 @@ node_default(Temp.purgeSyncAll);
|
|
|
62518
62619
|
var temp_default = Temp;
|
|
62519
62620
|
|
|
62520
62621
|
// node_modules/atomically/dist/index.js
|
|
62521
|
-
function
|
|
62622
|
+
function writeFileSync5(filePath, data, options2 = DEFAULT_WRITE_OPTIONS) {
|
|
62522
62623
|
if (isString(options2))
|
|
62523
|
-
return
|
|
62624
|
+
return writeFileSync5(filePath, data, { encoding: options2 });
|
|
62524
62625
|
const timeout = options2.timeout ?? DEFAULT_TIMEOUT_SYNC;
|
|
62525
62626
|
const retryOptions = { timeout };
|
|
62526
62627
|
let tempDisposer = null;
|
|
@@ -62848,7 +62949,7 @@ class Configstore {
|
|
|
62848
62949
|
}
|
|
62849
62950
|
if (error.name === "SyntaxError") {
|
|
62850
62951
|
if (this._clearInvalidConfig) {
|
|
62851
|
-
|
|
62952
|
+
writeFileSync5(this._path, "", writeFileOptions);
|
|
62852
62953
|
return {};
|
|
62853
62954
|
}
|
|
62854
62955
|
throw error;
|
|
@@ -62860,7 +62961,7 @@ class Configstore {
|
|
|
62860
62961
|
set all(value) {
|
|
62861
62962
|
try {
|
|
62862
62963
|
import_graceful_fs.default.mkdirSync(path5.dirname(this._path), mkdirOptions);
|
|
62863
|
-
|
|
62964
|
+
writeFileSync5(this._path, JSON.stringify(value, undefined, "\t"), writeFileOptions);
|
|
62864
62965
|
} catch (error) {
|
|
62865
62966
|
handlePermissionError(error);
|
|
62866
62967
|
}
|
|
@@ -65587,8 +65688,8 @@ function getUpdateInfo() {
|
|
|
65587
65688
|
init_emoji();
|
|
65588
65689
|
init_logger();
|
|
65589
65690
|
init_worktree();
|
|
65590
|
-
var
|
|
65591
|
-
var sessionLog2 = createSessionLog(
|
|
65691
|
+
var log20 = createLogger("commands");
|
|
65692
|
+
var sessionLog2 = createSessionLog(log20);
|
|
65592
65693
|
function sessionAccountOption(session, ctx) {
|
|
65593
65694
|
if (!session.claudeAccountId)
|
|
65594
65695
|
return;
|
|
@@ -65751,7 +65852,11 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
65751
65852
|
const cliOptions = {
|
|
65752
65853
|
workingDir: absoluteDir,
|
|
65753
65854
|
threadId: session.threadId,
|
|
65754
|
-
|
|
65855
|
+
permissionMode: effectivePermissionMode({
|
|
65856
|
+
override: session.permissionModeOverride,
|
|
65857
|
+
sessionHasInteractiveOverride: session.forceInteractivePermissions,
|
|
65858
|
+
botWideMode: ctx.config.permissionMode
|
|
65859
|
+
}),
|
|
65755
65860
|
sessionId: newSessionId,
|
|
65756
65861
|
resume: false,
|
|
65757
65862
|
chrome: ctx.config.chromeEnabled,
|
|
@@ -65828,45 +65933,37 @@ async function kickUser(session, kickedUser, kickedBy, ctx) {
|
|
|
65828
65933
|
sessionLog2(session).warn(`\uD83D\uDEAB @${kickedUser} was not in session`);
|
|
65829
65934
|
}
|
|
65830
65935
|
}
|
|
65831
|
-
async function
|
|
65936
|
+
async function setSessionPermissionMode(session, username, mode, ctx) {
|
|
65832
65937
|
if (!await requireSessionOwner(session, username, "change permissions")) {
|
|
65833
65938
|
return;
|
|
65834
65939
|
}
|
|
65835
|
-
|
|
65836
|
-
|
|
65837
|
-
|
|
65838
|
-
|
|
65839
|
-
|
|
65840
|
-
if (session.forceInteractivePermissions) {
|
|
65841
|
-
await post(session, "info", `Interactive permissions already enabled for this session`);
|
|
65842
|
-
sessionLog2(session).debug(`\uD83D\uDD10 Permissions already interactive (session override)`);
|
|
65843
|
-
return;
|
|
65844
|
-
}
|
|
65845
|
-
session.forceInteractivePermissions = true;
|
|
65846
|
-
sessionLog2(session).info(`\uD83D\uDD10 Enabling interactive permissions`);
|
|
65847
|
-
session.threadLogger?.logCommand("permissions", "interactive", username);
|
|
65940
|
+
session.permissionModeOverride = mode;
|
|
65941
|
+
session.forceInteractivePermissions = mode === "default";
|
|
65942
|
+
sessionLog2(session).info(`\uD83D\uDD10 Setting permission mode to "${mode}"`);
|
|
65943
|
+
session.threadLogger?.logCommand("permissions", mode, username);
|
|
65944
|
+
const canResume = session.lifecycle.hasClaudeResponded;
|
|
65848
65945
|
const cliOptions = {
|
|
65849
65946
|
workingDir: session.workingDir,
|
|
65850
65947
|
threadId: session.threadId,
|
|
65851
|
-
|
|
65948
|
+
permissionMode: mode,
|
|
65852
65949
|
sessionId: session.claudeSessionId,
|
|
65853
|
-
resume:
|
|
65950
|
+
resume: canResume,
|
|
65854
65951
|
chrome: ctx.config.chromeEnabled,
|
|
65855
65952
|
platformConfig: session.platform.getMcpConfig(),
|
|
65856
65953
|
logSessionId: session.sessionId,
|
|
65857
65954
|
permissionTimeoutMs: ctx.config.permissionTimeoutMs,
|
|
65858
65955
|
account: sessionAccountOption(session, ctx)
|
|
65859
65956
|
};
|
|
65860
|
-
const success = await restartClaudeSession(session, cliOptions, ctx,
|
|
65957
|
+
const success = await restartClaudeSession(session, cliOptions, ctx, `Set permission mode to ${mode}`);
|
|
65861
65958
|
if (!success)
|
|
65862
65959
|
return;
|
|
65863
65960
|
await updateSessionHeader(session, ctx);
|
|
65864
65961
|
const formatter = session.platform.getFormatter();
|
|
65865
|
-
|
|
65866
|
-
${formatter.
|
|
65867
|
-
|
|
65868
|
-
|
|
65869
|
-
|
|
65962
|
+
const display = permissionModeDisplay(mode);
|
|
65963
|
+
await post(session, "secure", `${display.icon} ${formatter.formatBold(`Permission mode: ${display.label}`)} set for this session by ${formatter.formatUserMention(username)}
|
|
65964
|
+
` + `${formatter.formatItalic(permissionModeDescription(mode))}
|
|
65965
|
+
` + `${formatter.formatItalic("Claude Code restarted.")}`);
|
|
65966
|
+
sessionLog2(session).info(`\uD83D\uDD10 Permission mode set to "${mode}" by @${username}`);
|
|
65870
65967
|
}
|
|
65871
65968
|
async function requestMessageApproval(session, username, message, ctx) {
|
|
65872
65969
|
if (session.messageManager?.getPendingMessageApproval()) {
|
|
@@ -65892,8 +65989,12 @@ async function updateSessionHeader(session, ctx) {
|
|
|
65892
65989
|
const formatter = session.platform.getFormatter();
|
|
65893
65990
|
const worktreeContext = session.worktreeInfo ? { path: session.worktreeInfo.worktreePath, branch: session.worktreeInfo.branch } : undefined;
|
|
65894
65991
|
const shortDir = shortenPath(session.workingDir, undefined, worktreeContext);
|
|
65895
|
-
const
|
|
65896
|
-
|
|
65992
|
+
const effectiveMode = effectivePermissionMode({
|
|
65993
|
+
override: session.permissionModeOverride,
|
|
65994
|
+
sessionHasInteractiveOverride: session.forceInteractivePermissions,
|
|
65995
|
+
botWideMode: ctx.config.permissionMode
|
|
65996
|
+
});
|
|
65997
|
+
const permMode = permissionModeDisplay(effectiveMode).chip;
|
|
65897
65998
|
const otherParticipants = [...session.sessionAllowedUsers].filter((u) => u !== session.startedBy).map((u) => formatter.formatUserMention(u)).join(", ");
|
|
65898
65999
|
const statusItems = [];
|
|
65899
66000
|
const versionStr = formatVersionString();
|
|
@@ -65982,7 +66083,7 @@ async function updateSessionHeader(session, ctx) {
|
|
|
65982
66083
|
].filter((item) => item !== null && item !== undefined).join(`
|
|
65983
66084
|
`);
|
|
65984
66085
|
const postId = session.sessionStartPostId;
|
|
65985
|
-
await
|
|
66086
|
+
await updatePost(session, postId, msg);
|
|
65986
66087
|
}
|
|
65987
66088
|
async function showUpdateStatus(session, updateManager, ctx) {
|
|
65988
66089
|
const formatter = session.platform.getFormatter();
|
|
@@ -66122,7 +66223,7 @@ init_logger();
|
|
|
66122
66223
|
import { exec as exec3 } from "child_process";
|
|
66123
66224
|
import { promisify as promisify3 } from "util";
|
|
66124
66225
|
var execAsync2 = promisify3(exec3);
|
|
66125
|
-
var
|
|
66226
|
+
var log21 = createLogger("branch");
|
|
66126
66227
|
var SUGGESTION_TIMEOUT3 = 15000;
|
|
66127
66228
|
var MAX_SUGGESTIONS = 3;
|
|
66128
66229
|
async function getCurrentBranch3(workingDir) {
|
|
@@ -66171,7 +66272,7 @@ function parseBranchSuggestions(response) {
|
|
|
66171
66272
|
return lines.slice(0, MAX_SUGGESTIONS);
|
|
66172
66273
|
}
|
|
66173
66274
|
async function suggestBranchNames(workingDir, userMessage) {
|
|
66174
|
-
|
|
66275
|
+
log21.debug(`Suggesting branch names for: "${userMessage.substring(0, 50)}..."`);
|
|
66175
66276
|
try {
|
|
66176
66277
|
const [currentBranch, recentCommits] = await Promise.all([
|
|
66177
66278
|
getCurrentBranch3(workingDir),
|
|
@@ -66185,14 +66286,14 @@ async function suggestBranchNames(workingDir, userMessage) {
|
|
|
66185
66286
|
workingDir
|
|
66186
66287
|
});
|
|
66187
66288
|
if (!result.success || !result.response) {
|
|
66188
|
-
|
|
66289
|
+
log21.debug(`Branch suggestion failed: ${result.error || "no response"}`);
|
|
66189
66290
|
return [];
|
|
66190
66291
|
}
|
|
66191
66292
|
const suggestions = parseBranchSuggestions(result.response);
|
|
66192
|
-
|
|
66293
|
+
log21.debug(`Got ${suggestions.length} branch suggestions: ${suggestions.join(", ")}`);
|
|
66193
66294
|
return suggestions;
|
|
66194
66295
|
} catch (err) {
|
|
66195
|
-
|
|
66296
|
+
log21.debug(`Branch suggestion error: ${err}`);
|
|
66196
66297
|
return [];
|
|
66197
66298
|
}
|
|
66198
66299
|
}
|
|
@@ -66201,8 +66302,8 @@ async function suggestBranchNames(workingDir, userMessage) {
|
|
|
66201
66302
|
init_worktree();
|
|
66202
66303
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
66203
66304
|
init_logger();
|
|
66204
|
-
var
|
|
66205
|
-
var sessionLog3 = createSessionLog(
|
|
66305
|
+
var log22 = createLogger("worktree");
|
|
66306
|
+
var sessionLog3 = createSessionLog(log22);
|
|
66206
66307
|
function parseWorktreeError(error) {
|
|
66207
66308
|
const message = error instanceof Error ? error.message : String(error);
|
|
66208
66309
|
const lowerMessage = message.toLowerCase();
|
|
@@ -66441,7 +66542,11 @@ async function createAndSwitchToWorktree(session, branch, username, options2) {
|
|
|
66441
66542
|
const cliOptions = {
|
|
66442
66543
|
workingDir: existing.path,
|
|
66443
66544
|
threadId: session.threadId,
|
|
66444
|
-
|
|
66545
|
+
permissionMode: effectivePermissionMode({
|
|
66546
|
+
override: session.permissionModeOverride,
|
|
66547
|
+
sessionHasInteractiveOverride: session.forceInteractivePermissions,
|
|
66548
|
+
botWideMode: options2.permissionMode
|
|
66549
|
+
}),
|
|
66445
66550
|
sessionId: newSessionId,
|
|
66446
66551
|
resume: false,
|
|
66447
66552
|
chrome: options2.chromeEnabled,
|
|
@@ -66530,7 +66635,11 @@ ${fmt.formatItalic("Claude Code restarted in the worktree")}`);
|
|
|
66530
66635
|
const cliOptions = {
|
|
66531
66636
|
workingDir: worktreePath,
|
|
66532
66637
|
threadId: session.threadId,
|
|
66533
|
-
|
|
66638
|
+
permissionMode: effectivePermissionMode({
|
|
66639
|
+
override: session.permissionModeOverride,
|
|
66640
|
+
sessionHasInteractiveOverride: session.forceInteractivePermissions,
|
|
66641
|
+
botWideMode: options2.permissionMode
|
|
66642
|
+
}),
|
|
66534
66643
|
sessionId: newSessionId,
|
|
66535
66644
|
resume: false,
|
|
66536
66645
|
chrome: options2.chromeEnabled,
|
|
@@ -66576,7 +66685,7 @@ ${fmt.formatItalic("Claude Code restarted in the new worktree")}`);
|
|
|
66576
66685
|
const { summary, suggestion } = parseWorktreeError(err);
|
|
66577
66686
|
const worktreePromptId = session.worktreePromptPostId;
|
|
66578
66687
|
if (worktreePromptId) {
|
|
66579
|
-
await
|
|
66688
|
+
await updatePost(session, worktreePromptId, `❌ ${fmt.formatBold(summary)}: ${fmt.formatCode(branch)}`);
|
|
66580
66689
|
await removeReaction(session, worktreePromptId, "x");
|
|
66581
66690
|
}
|
|
66582
66691
|
if (options2.worktreeMode === "require") {
|
|
@@ -66753,8 +66862,8 @@ async function cleanupWorktreeCommand(session, username, hasOtherSessionsUsingWo
|
|
|
66753
66862
|
}
|
|
66754
66863
|
// src/operations/events/handler.ts
|
|
66755
66864
|
init_logger();
|
|
66756
|
-
var
|
|
66757
|
-
var sessionLog4 = createSessionLog(
|
|
66865
|
+
var log23 = createLogger("events");
|
|
66866
|
+
var sessionLog4 = createSessionLog(log23);
|
|
66758
66867
|
function detectAndExecuteClaudeCommands(text, session, ctx) {
|
|
66759
66868
|
const parsed = parseClaudeCommand(text);
|
|
66760
66869
|
if (parsed && isClaudeAllowedCommand(parsed.command)) {
|
|
@@ -66887,7 +66996,7 @@ async function handleCompactionComplete(session, compactMetadata, _ctx) {
|
|
|
66887
66996
|
const formatter = session.platform.getFormatter();
|
|
66888
66997
|
const completionMessage = `✅ ${formatter.formatBold("Context compacted")} ${formatter.formatItalic(`(${info})`)}`;
|
|
66889
66998
|
if (session.compactionPostId) {
|
|
66890
|
-
await
|
|
66999
|
+
await updatePost(session, session.compactionPostId, completionMessage);
|
|
66891
67000
|
session.compactionPostId = undefined;
|
|
66892
67001
|
} else {
|
|
66893
67002
|
await withErrorHandling(() => post(session, "info", completionMessage), { action: "Post compaction complete", session });
|
|
@@ -67002,8 +67111,8 @@ function createSessionContext(config, state, ops) {
|
|
|
67002
67111
|
// src/operations/context-prompt/handler.ts
|
|
67003
67112
|
init_emoji();
|
|
67004
67113
|
init_logger();
|
|
67005
|
-
var
|
|
67006
|
-
var sessionLog5 = createSessionLog(
|
|
67114
|
+
var log24 = createLogger("context");
|
|
67115
|
+
var sessionLog5 = createSessionLog(log24);
|
|
67007
67116
|
var CONTEXT_PROMPT_TIMEOUT_MS = 30000;
|
|
67008
67117
|
var CONTEXT_OPTIONS = [3, 5, 10];
|
|
67009
67118
|
var contextPromptTimeouts = new Map;
|
|
@@ -67253,8 +67362,8 @@ function formatRelativeTime(date) {
|
|
|
67253
67362
|
}
|
|
67254
67363
|
// src/session/lifecycle.ts
|
|
67255
67364
|
init_worktree();
|
|
67256
|
-
var
|
|
67257
|
-
var sessionLog6 = createSessionLog(
|
|
67365
|
+
var log25 = createLogger("lifecycle");
|
|
67366
|
+
var sessionLog6 = createSessionLog(log25);
|
|
67258
67367
|
function mutableSessions(ctx) {
|
|
67259
67368
|
return ctx.state.sessions;
|
|
67260
67369
|
}
|
|
@@ -67366,7 +67475,8 @@ function createMessageManager(session, ctx) {
|
|
|
67366
67475
|
},
|
|
67367
67476
|
emitSessionUpdate: (updates) => {
|
|
67368
67477
|
ctx.ops.emitSessionUpdate(session.sessionId, updates);
|
|
67369
|
-
}
|
|
67478
|
+
},
|
|
67479
|
+
flushDelayMs: ctx.config.flushDelayMs
|
|
67370
67480
|
});
|
|
67371
67481
|
messageManager.events.on("question:complete", ({ toolUseId: _toolUseId, answers }) => {
|
|
67372
67482
|
const answerJson = JSON.stringify(answers);
|
|
@@ -67618,8 +67728,9 @@ async function startSession(options2, username, displayName, replyToPostId, plat
|
|
|
67618
67728
|
platform.sendTyping(actualThreadId);
|
|
67619
67729
|
const claudeSessionId = randomUUID4();
|
|
67620
67730
|
let workingDir = ctx.config.workingDir;
|
|
67621
|
-
let
|
|
67731
|
+
let permissionMode = ctx.config.permissionMode;
|
|
67622
67732
|
let forceInteractivePermissions = false;
|
|
67733
|
+
let sessionPermissionModeOverride;
|
|
67623
67734
|
const formatter = platform.getFormatter();
|
|
67624
67735
|
if (initialOptions?.workingDir) {
|
|
67625
67736
|
const { resolve: resolve6 } = await import("path");
|
|
@@ -67637,12 +67748,17 @@ async function startSession(options2, username, displayName, replyToPostId, plat
|
|
|
67637
67748
|
return;
|
|
67638
67749
|
}
|
|
67639
67750
|
workingDir = resolvedDir;
|
|
67640
|
-
|
|
67641
|
-
}
|
|
67642
|
-
if (initialOptions?.
|
|
67751
|
+
log25.info(`Starting session in directory: ${workingDir} (from !cd command)`);
|
|
67752
|
+
}
|
|
67753
|
+
if (initialOptions?.permissionMode) {
|
|
67754
|
+
permissionMode = initialOptions.permissionMode;
|
|
67755
|
+
forceInteractivePermissions = permissionMode === "default";
|
|
67756
|
+
sessionPermissionModeOverride = permissionMode;
|
|
67757
|
+
log25.info(`Starting session with permission mode "${permissionMode}" (from !permissions command)`);
|
|
67758
|
+
} else if (initialOptions?.forceInteractivePermissions) {
|
|
67643
67759
|
forceInteractivePermissions = true;
|
|
67644
|
-
|
|
67645
|
-
|
|
67760
|
+
permissionMode = "default";
|
|
67761
|
+
log25.info(`Starting session with interactive permissions (from !permissions command)`);
|
|
67646
67762
|
}
|
|
67647
67763
|
const sessionContext = buildSessionContext(platform, workingDir);
|
|
67648
67764
|
const systemPrompt = `${sessionContext}
|
|
@@ -67651,12 +67767,12 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
67651
67767
|
const platformMcpConfig = platform.getMcpConfig();
|
|
67652
67768
|
const claudeAccount = ctx.ops.acquireClaudeAccount();
|
|
67653
67769
|
if (claudeAccount) {
|
|
67654
|
-
|
|
67770
|
+
log25.info(`Session ${sessionId.substring(0, 20)} reserved Claude account "${claudeAccount.id}"`);
|
|
67655
67771
|
}
|
|
67656
67772
|
const cliOptions = {
|
|
67657
67773
|
workingDir,
|
|
67658
67774
|
threadId: actualThreadId,
|
|
67659
|
-
|
|
67775
|
+
permissionMode,
|
|
67660
67776
|
sessionId: claudeSessionId,
|
|
67661
67777
|
resume: false,
|
|
67662
67778
|
chrome: ctx.config.chromeEnabled,
|
|
@@ -67684,6 +67800,7 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
67684
67800
|
planApproved: false,
|
|
67685
67801
|
sessionAllowedUsers: new Set([username]),
|
|
67686
67802
|
forceInteractivePermissions,
|
|
67803
|
+
permissionModeOverride: sessionPermissionModeOverride,
|
|
67687
67804
|
sessionStartPostId: startPost.id,
|
|
67688
67805
|
timers: createSessionTimers(),
|
|
67689
67806
|
lifecycle: createSessionLifecycle(),
|
|
@@ -67755,28 +67872,28 @@ async function resumeSession(state, ctx) {
|
|
|
67755
67872
|
!state.claudeSessionId && "claudeSessionId",
|
|
67756
67873
|
!state.workingDir && "workingDir"
|
|
67757
67874
|
].filter(Boolean).join(", ");
|
|
67758
|
-
|
|
67875
|
+
log25.warn(`Skipping session with missing required fields: ${missing}`);
|
|
67759
67876
|
return;
|
|
67760
67877
|
}
|
|
67761
67878
|
const shortId = state.threadId.substring(0, 8);
|
|
67762
67879
|
const platforms = ctx.state.platforms;
|
|
67763
67880
|
const platform = platforms.get(state.platformId);
|
|
67764
67881
|
if (!platform) {
|
|
67765
|
-
|
|
67882
|
+
log25.warn(`Platform ${state.platformId} not registered, skipping resume for ${shortId}...`);
|
|
67766
67883
|
return;
|
|
67767
67884
|
}
|
|
67768
67885
|
const threadPost = await platform.getPost(state.threadId);
|
|
67769
67886
|
if (!threadPost) {
|
|
67770
|
-
|
|
67887
|
+
log25.warn(`Thread ${shortId}... deleted, skipping resume`);
|
|
67771
67888
|
ctx.state.sessionStore.remove(`${state.platformId}:${state.threadId}`);
|
|
67772
67889
|
return;
|
|
67773
67890
|
}
|
|
67774
67891
|
if (ctx.state.sessions.size >= ctx.config.maxSessions) {
|
|
67775
|
-
|
|
67892
|
+
log25.warn(`Max sessions reached, skipping resume for ${shortId}...`);
|
|
67776
67893
|
return;
|
|
67777
67894
|
}
|
|
67778
67895
|
if (!existsSync11(state.workingDir)) {
|
|
67779
|
-
|
|
67896
|
+
log25.warn(`Working directory ${state.workingDir} no longer exists, skipping resume for ${shortId}...`);
|
|
67780
67897
|
ctx.state.sessionStore.remove(`${state.platformId}:${state.threadId}`);
|
|
67781
67898
|
const resumeFormatter = platform.getFormatter();
|
|
67782
67899
|
const tempSession = {
|
|
@@ -67792,7 +67909,7 @@ Please start a new session.`), { action: "Post resume failure notification" });
|
|
|
67792
67909
|
}
|
|
67793
67910
|
const platformId = state.platformId;
|
|
67794
67911
|
const sessionId = ctx.ops.getSessionId(platformId, state.threadId);
|
|
67795
|
-
const
|
|
67912
|
+
const resumePermissionMode = state.forceInteractivePermissions ? "default" : ctx.config.permissionMode;
|
|
67796
67913
|
const platformMcpConfig = platform.getMcpConfig();
|
|
67797
67914
|
const sessionContext = buildSessionContext(platform, state.workingDir);
|
|
67798
67915
|
const appendSystemPrompt = `${sessionContext}
|
|
@@ -67800,12 +67917,12 @@ Please start a new session.`), { action: "Post resume failure notification" });
|
|
|
67800
67917
|
${CHAT_PLATFORM_PROMPT}`;
|
|
67801
67918
|
const claudeAccount = ctx.ops.acquireClaudeAccount(state.claudeAccountId);
|
|
67802
67919
|
if (state.claudeAccountId && !claudeAccount) {
|
|
67803
|
-
|
|
67920
|
+
log25.warn(`Persisted session referenced Claude account "${state.claudeAccountId}" ` + `which is no longer configured — resuming under default env`);
|
|
67804
67921
|
}
|
|
67805
67922
|
const cliOptions = {
|
|
67806
67923
|
workingDir: state.workingDir,
|
|
67807
67924
|
threadId: state.threadId,
|
|
67808
|
-
|
|
67925
|
+
permissionMode: resumePermissionMode,
|
|
67809
67926
|
sessionId: state.claudeSessionId,
|
|
67810
67927
|
resume: true,
|
|
67811
67928
|
chrome: ctx.config.chromeEnabled,
|
|
@@ -67865,7 +67982,7 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
67865
67982
|
worktreePath: detected.worktreePath,
|
|
67866
67983
|
branch: detected.branch
|
|
67867
67984
|
};
|
|
67868
|
-
|
|
67985
|
+
log25.info(`Auto-detected worktree info for resumed session: branch=${detected.branch}`);
|
|
67869
67986
|
}
|
|
67870
67987
|
}
|
|
67871
67988
|
session.messageManager = createMessageManager(session, ctx);
|
|
@@ -67921,7 +68038,7 @@ ${sessionFormatter.formatItalic("Reconnected to Claude session. You can continue
|
|
|
67921
68038
|
await ctx.ops.updateStickyMessage();
|
|
67922
68039
|
ctx.ops.persistSession(session);
|
|
67923
68040
|
} catch (err) {
|
|
67924
|
-
|
|
68041
|
+
log25.error(`Failed to resume session ${shortId}`, err instanceof Error ? err : undefined);
|
|
67925
68042
|
ctx.ops.emitSessionRemove(sessionId);
|
|
67926
68043
|
mutableSessions(ctx).delete(sessionId);
|
|
67927
68044
|
ctx.state.sessionStore.remove(sessionId);
|
|
@@ -67961,18 +68078,18 @@ async function resumePausedSession(threadId, message, files, ctx) {
|
|
|
67961
68078
|
const persisted = ctx.state.sessionStore.load();
|
|
67962
68079
|
const state = findPersistedByThreadId(persisted, threadId);
|
|
67963
68080
|
if (!state) {
|
|
67964
|
-
|
|
68081
|
+
log25.debug(`No persisted session found for ${threadId.substring(0, 8)}...`);
|
|
67965
68082
|
return;
|
|
67966
68083
|
}
|
|
67967
68084
|
const shortId = threadId.substring(0, 8);
|
|
67968
|
-
|
|
68085
|
+
log25.info(`\uD83D\uDD04 Resuming paused session ${shortId}... for new message`);
|
|
67969
68086
|
await resumeSession(state, ctx);
|
|
67970
68087
|
const session = ctx.ops.findSessionByThreadId(threadId);
|
|
67971
68088
|
if (session && session.claude.isRunning() && session.messageManager) {
|
|
67972
68089
|
session.messageCount++;
|
|
67973
68090
|
await session.messageManager.handleUserMessage(message, files, state.startedBy);
|
|
67974
68091
|
} else {
|
|
67975
|
-
|
|
68092
|
+
log25.warn(`Failed to resume session ${shortId}..., could not send message`);
|
|
67976
68093
|
}
|
|
67977
68094
|
}
|
|
67978
68095
|
async function handleExit(sessionId, code, ctx) {
|
|
@@ -67980,7 +68097,7 @@ async function handleExit(sessionId, code, ctx) {
|
|
|
67980
68097
|
const shortId = sessionId.substring(0, 8);
|
|
67981
68098
|
sessionLog6(session).debug(`handleExit called code=${code} isShuttingDown=${ctx.state.isShuttingDown}`);
|
|
67982
68099
|
if (!session) {
|
|
67983
|
-
|
|
68100
|
+
log25.debug(`Session ${shortId}... not found (already cleaned up)`);
|
|
67984
68101
|
return;
|
|
67985
68102
|
}
|
|
67986
68103
|
if (isSessionRestarting(session)) {
|
|
@@ -68173,7 +68290,7 @@ async function cleanupIdleSessions(timeoutMs, warningMs, ctx) {
|
|
|
68173
68290
|
}
|
|
68174
68291
|
|
|
68175
68292
|
// src/operations/monitor/handler.ts
|
|
68176
|
-
var
|
|
68293
|
+
var log26 = createLogger("monitor");
|
|
68177
68294
|
var DEFAULT_INTERVAL_MS = 60 * 1000;
|
|
68178
68295
|
|
|
68179
68296
|
class SessionMonitor {
|
|
@@ -68195,14 +68312,14 @@ class SessionMonitor {
|
|
|
68195
68312
|
}
|
|
68196
68313
|
start() {
|
|
68197
68314
|
if (this.isRunning) {
|
|
68198
|
-
|
|
68315
|
+
log26.debug("Session monitor already running");
|
|
68199
68316
|
return;
|
|
68200
68317
|
}
|
|
68201
68318
|
this.isRunning = true;
|
|
68202
|
-
|
|
68319
|
+
log26.debug(`Session monitor started (interval: ${this.intervalMs / 1000}s)`);
|
|
68203
68320
|
this.timer = setInterval(() => {
|
|
68204
68321
|
this.runCheck().catch((err) => {
|
|
68205
|
-
|
|
68322
|
+
log26.error(`Error during session monitoring: ${err}`);
|
|
68206
68323
|
});
|
|
68207
68324
|
}, this.intervalMs);
|
|
68208
68325
|
}
|
|
@@ -68212,7 +68329,7 @@ class SessionMonitor {
|
|
|
68212
68329
|
this.timer = null;
|
|
68213
68330
|
}
|
|
68214
68331
|
this.isRunning = false;
|
|
68215
|
-
|
|
68332
|
+
log26.debug("Session monitor stopped");
|
|
68216
68333
|
}
|
|
68217
68334
|
async runCheck() {
|
|
68218
68335
|
await cleanupIdleSessions(this.sessionTimeoutMs, this.sessionWarningMs, this.getContext());
|
|
@@ -68224,8 +68341,8 @@ class SessionMonitor {
|
|
|
68224
68341
|
// src/operations/plugin/handler.ts
|
|
68225
68342
|
init_spawn();
|
|
68226
68343
|
init_logger();
|
|
68227
|
-
var
|
|
68228
|
-
var sessionLog7 = createSessionLog(
|
|
68344
|
+
var log27 = createLogger("plugin");
|
|
68345
|
+
var sessionLog7 = createSessionLog(log27);
|
|
68229
68346
|
async function runPluginCommand(args, cwd, timeout2 = 60000) {
|
|
68230
68347
|
return new Promise((resolve6) => {
|
|
68231
68348
|
const claudePath = process.env.CLAUDE_PATH || "claude";
|
|
@@ -68246,7 +68363,7 @@ async function runPluginCommand(args, cwd, timeout2 = 60000) {
|
|
|
68246
68363
|
});
|
|
68247
68364
|
proc.on("error", (err) => {
|
|
68248
68365
|
resolve6({ stdout, stderr, exitCode: 1 });
|
|
68249
|
-
|
|
68366
|
+
log27.error(`Plugin command error: ${err.message}`);
|
|
68250
68367
|
});
|
|
68251
68368
|
});
|
|
68252
68369
|
}
|
|
@@ -68282,9 +68399,13 @@ ${formatter.formatCodeBlock(errorMsg, "text")}`);
|
|
|
68282
68399
|
const cliOptions = {
|
|
68283
68400
|
workingDir: session.workingDir,
|
|
68284
68401
|
threadId: session.threadId,
|
|
68285
|
-
|
|
68402
|
+
permissionMode: effectivePermissionMode({
|
|
68403
|
+
override: session.permissionModeOverride,
|
|
68404
|
+
sessionHasInteractiveOverride: session.forceInteractivePermissions,
|
|
68405
|
+
botWideMode: ctx.config.permissionMode
|
|
68406
|
+
}),
|
|
68286
68407
|
sessionId: session.claudeSessionId,
|
|
68287
|
-
resume:
|
|
68408
|
+
resume: session.lifecycle.hasClaudeResponded,
|
|
68288
68409
|
chrome: ctx.config.chromeEnabled,
|
|
68289
68410
|
platformConfig: session.platform.getMcpConfig(),
|
|
68290
68411
|
logSessionId: session.sessionId,
|
|
@@ -68315,9 +68436,13 @@ ${formatter.formatCodeBlock(errorMsg, "text")}`);
|
|
|
68315
68436
|
const cliOptions = {
|
|
68316
68437
|
workingDir: session.workingDir,
|
|
68317
68438
|
threadId: session.threadId,
|
|
68318
|
-
|
|
68439
|
+
permissionMode: effectivePermissionMode({
|
|
68440
|
+
override: session.permissionModeOverride,
|
|
68441
|
+
sessionHasInteractiveOverride: session.forceInteractivePermissions,
|
|
68442
|
+
botWideMode: ctx.config.permissionMode
|
|
68443
|
+
}),
|
|
68319
68444
|
sessionId: session.claudeSessionId,
|
|
68320
|
-
resume:
|
|
68445
|
+
resume: session.lifecycle.hasClaudeResponded,
|
|
68321
68446
|
chrome: ctx.config.chromeEnabled,
|
|
68322
68447
|
platformConfig: session.platform.getMcpConfig(),
|
|
68323
68448
|
logSessionId: session.sessionId,
|
|
@@ -68439,12 +68564,12 @@ class SessionRegistry {
|
|
|
68439
68564
|
|
|
68440
68565
|
// src/session/manager.ts
|
|
68441
68566
|
init_logger();
|
|
68442
|
-
var
|
|
68567
|
+
var log28 = createLogger("manager");
|
|
68443
68568
|
|
|
68444
68569
|
class SessionManager extends EventEmitter4 {
|
|
68445
68570
|
platforms = new Map;
|
|
68446
68571
|
workingDir;
|
|
68447
|
-
|
|
68572
|
+
permissionMode;
|
|
68448
68573
|
chromeEnabled;
|
|
68449
68574
|
worktreeMode;
|
|
68450
68575
|
threadLogsEnabled;
|
|
@@ -68463,10 +68588,10 @@ class SessionManager extends EventEmitter4 {
|
|
|
68463
68588
|
customFooter;
|
|
68464
68589
|
autoUpdateManager = null;
|
|
68465
68590
|
accountPool;
|
|
68466
|
-
constructor(workingDir,
|
|
68591
|
+
constructor(workingDir, permissionModeOrSkipFlag = "default", chromeEnabled = false, worktreeMode = "prompt", sessionsPath, threadLogsEnabled = true, threadLogsRetentionDays = 30, limits, claudeAccounts) {
|
|
68467
68592
|
super();
|
|
68468
68593
|
this.workingDir = workingDir;
|
|
68469
|
-
this.
|
|
68594
|
+
this.permissionMode = typeof permissionModeOrSkipFlag === "boolean" ? permissionModeOrSkipFlag ? "bypass" : "default" : permissionModeOrSkipFlag;
|
|
68470
68595
|
this.chromeEnabled = chromeEnabled;
|
|
68471
68596
|
this.worktreeMode = worktreeMode;
|
|
68472
68597
|
this.threadLogsEnabled = threadLogsEnabled;
|
|
@@ -68508,7 +68633,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
68508
68633
|
markNeedsBump(platformId);
|
|
68509
68634
|
this.updateStickyMessage();
|
|
68510
68635
|
});
|
|
68511
|
-
|
|
68636
|
+
log28.info(`\uD83D\uDCE1 Platform "${platformId}" registered`);
|
|
68512
68637
|
}
|
|
68513
68638
|
removePlatform(platformId) {
|
|
68514
68639
|
this.platforms.delete(platformId);
|
|
@@ -68524,7 +68649,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
68524
68649
|
if (users) {
|
|
68525
68650
|
users.add(sessionId);
|
|
68526
68651
|
}
|
|
68527
|
-
|
|
68652
|
+
log28.debug(`Registered session ${sessionId.substring(0, 20)} as worktree user for ${worktreePath}`);
|
|
68528
68653
|
}
|
|
68529
68654
|
unregisterWorktreeUser(worktreePath, sessionId) {
|
|
68530
68655
|
const users = this.worktreeUsers.get(worktreePath);
|
|
@@ -68544,13 +68669,14 @@ class SessionManager extends EventEmitter4 {
|
|
|
68544
68669
|
getContext() {
|
|
68545
68670
|
const config = {
|
|
68546
68671
|
workingDir: this.workingDir,
|
|
68547
|
-
|
|
68672
|
+
permissionMode: this.permissionMode,
|
|
68548
68673
|
chromeEnabled: this.chromeEnabled,
|
|
68549
68674
|
debug: this.debug,
|
|
68550
68675
|
maxSessions: this.limits.maxSessions,
|
|
68551
68676
|
threadLogsEnabled: this.threadLogsEnabled,
|
|
68552
68677
|
threadLogsRetentionDays: this.threadLogsRetentionDays,
|
|
68553
|
-
permissionTimeoutMs: this.limits.permissionTimeoutSeconds * 1000
|
|
68678
|
+
permissionTimeoutMs: this.limits.permissionTimeoutSeconds * 1000,
|
|
68679
|
+
flushDelayMs: this.limits.flushDelayMs
|
|
68554
68680
|
};
|
|
68555
68681
|
const state = {
|
|
68556
68682
|
sessions: this.registry.getSessions(),
|
|
@@ -68649,6 +68775,15 @@ class SessionManager extends EventEmitter4 {
|
|
|
68649
68775
|
if (session.platformId !== platformId)
|
|
68650
68776
|
return;
|
|
68651
68777
|
if (!session.sessionAllowedUsers.has(username) && !session.platform.isUserAllowed(username)) {
|
|
68778
|
+
log28.info(`\uD83D\uDEAB rejected reaction from unauthorized user`, {
|
|
68779
|
+
event: "reaction.rejected",
|
|
68780
|
+
platformId,
|
|
68781
|
+
sessionId: session.sessionId,
|
|
68782
|
+
postId,
|
|
68783
|
+
emoji: normalizedEmoji,
|
|
68784
|
+
action,
|
|
68785
|
+
user: username
|
|
68786
|
+
});
|
|
68652
68787
|
return;
|
|
68653
68788
|
}
|
|
68654
68789
|
await this.handleSessionReaction(session, postId, normalizedEmoji, username, action);
|
|
@@ -68676,7 +68811,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
68676
68811
|
return false;
|
|
68677
68812
|
}
|
|
68678
68813
|
const shortId = persistedSession.threadId.substring(0, 8);
|
|
68679
|
-
|
|
68814
|
+
log28.info(`\uD83D\uDD04 Resuming session ${shortId}... via emoji reaction by @${username}`);
|
|
68680
68815
|
await resumeSession(persistedSession, this.getContext());
|
|
68681
68816
|
return true;
|
|
68682
68817
|
}
|
|
@@ -68706,7 +68841,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
68706
68841
|
}
|
|
68707
68842
|
if (session.lastError?.postId === postId && isBugReportEmoji(emojiName)) {
|
|
68708
68843
|
if (session.startedBy === username || session.platform.isUserAllowed(username) || session.sessionAllowedUsers.has(username)) {
|
|
68709
|
-
|
|
68844
|
+
log28.info(`\uD83D\uDC1B @${username} triggered bug report from error reaction`);
|
|
68710
68845
|
await reportBug(session, undefined, username, this.getContext(), session.lastError);
|
|
68711
68846
|
return;
|
|
68712
68847
|
}
|
|
@@ -68843,7 +68978,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
68843
68978
|
await updateAllStickyMessages(this.platforms, this.registry.getSessions(), {
|
|
68844
68979
|
maxSessions: this.limits.maxSessions,
|
|
68845
68980
|
chromeEnabled: this.chromeEnabled,
|
|
68846
|
-
|
|
68981
|
+
permissionMode: this.permissionMode,
|
|
68847
68982
|
worktreeMode: this.worktreeMode,
|
|
68848
68983
|
workingDir: this.workingDir,
|
|
68849
68984
|
debug: this.debug,
|
|
@@ -68859,8 +68994,14 @@ class SessionManager extends EventEmitter4 {
|
|
|
68859
68994
|
this.customDescription = description;
|
|
68860
68995
|
this.customFooter = footer;
|
|
68861
68996
|
}
|
|
68997
|
+
setPermissionMode(mode) {
|
|
68998
|
+
this.permissionMode = mode;
|
|
68999
|
+
}
|
|
68862
69000
|
setSkipPermissions(value) {
|
|
68863
|
-
this.
|
|
69001
|
+
this.permissionMode = value ? "bypass" : "default";
|
|
69002
|
+
}
|
|
69003
|
+
getPermissionMode() {
|
|
69004
|
+
return this.permissionMode;
|
|
68864
69005
|
}
|
|
68865
69006
|
setChromeEnabled(value) {
|
|
68866
69007
|
this.chromeEnabled = value;
|
|
@@ -68874,11 +69015,11 @@ class SessionManager extends EventEmitter4 {
|
|
|
68874
69015
|
}
|
|
68875
69016
|
}
|
|
68876
69017
|
if (sessionsToKill.length === 0) {
|
|
68877
|
-
|
|
69018
|
+
log28.info(`No active sessions to pause for platform ${platformId}`);
|
|
68878
69019
|
await this.updateStickyMessage();
|
|
68879
69020
|
return;
|
|
68880
69021
|
}
|
|
68881
|
-
|
|
69022
|
+
log28.info(`⏸️ Pausing ${sessionsToKill.length} session(s) for platform ${platformId}`);
|
|
68882
69023
|
for (const session of sessionsToKill) {
|
|
68883
69024
|
try {
|
|
68884
69025
|
const fmt = session.platform.getFormatter();
|
|
@@ -68894,9 +69035,9 @@ class SessionManager extends EventEmitter4 {
|
|
|
68894
69035
|
session.claude.kill();
|
|
68895
69036
|
this.registry.unregister(session.sessionId);
|
|
68896
69037
|
this.emitSessionRemove(session.sessionId);
|
|
68897
|
-
|
|
69038
|
+
log28.info(`⏸️ Paused session ${session.threadId.substring(0, 8)}`);
|
|
68898
69039
|
} catch (err) {
|
|
68899
|
-
|
|
69040
|
+
log28.warn(`Failed to pause session ${session.threadId}: ${err}`);
|
|
68900
69041
|
}
|
|
68901
69042
|
}
|
|
68902
69043
|
for (const session of sessionsToKill) {
|
|
@@ -68917,17 +69058,17 @@ class SessionManager extends EventEmitter4 {
|
|
|
68917
69058
|
sessionsToResume.push(state);
|
|
68918
69059
|
}
|
|
68919
69060
|
if (sessionsToResume.length === 0) {
|
|
68920
|
-
|
|
69061
|
+
log28.info(`No paused sessions to resume for platform ${platformId}`);
|
|
68921
69062
|
await this.updateStickyMessage();
|
|
68922
69063
|
return;
|
|
68923
69064
|
}
|
|
68924
|
-
|
|
69065
|
+
log28.info(`▶️ Resuming ${sessionsToResume.length} paused session(s) for platform ${platformId}`);
|
|
68925
69066
|
for (const state of sessionsToResume) {
|
|
68926
69067
|
try {
|
|
68927
69068
|
await resumeSession(state, this.getContext());
|
|
68928
|
-
|
|
69069
|
+
log28.info(`▶️ Resumed session ${state.threadId.substring(0, 8)}`);
|
|
68929
69070
|
} catch (err) {
|
|
68930
|
-
|
|
69071
|
+
log28.warn(`Failed to resume session ${state.threadId}: ${err}`);
|
|
68931
69072
|
}
|
|
68932
69073
|
}
|
|
68933
69074
|
await this.updateStickyMessage();
|
|
@@ -68939,14 +69080,14 @@ class SessionManager extends EventEmitter4 {
|
|
|
68939
69080
|
const sessionTimeoutMs = this.limits.sessionTimeoutMinutes * 60 * 1000;
|
|
68940
69081
|
const staleIds = this.sessionStore.cleanStale(sessionTimeoutMs * 2);
|
|
68941
69082
|
if (staleIds.length > 0) {
|
|
68942
|
-
|
|
69083
|
+
log28.info(`\uD83E\uDDF9 Soft-deleted ${staleIds.length} stale session(s) (kept for history)`);
|
|
68943
69084
|
}
|
|
68944
69085
|
const removedCount = this.sessionStore.cleanHistory();
|
|
68945
69086
|
if (removedCount > 0) {
|
|
68946
|
-
|
|
69087
|
+
log28.info(`\uD83D\uDDD1️ Permanently removed ${removedCount} old session(s) from history`);
|
|
68947
69088
|
}
|
|
68948
69089
|
const persisted = this.sessionStore.load();
|
|
68949
|
-
|
|
69090
|
+
log28.info(`\uD83D\uDCC2 Loaded ${persisted.size} session(s) from persistence`);
|
|
68950
69091
|
const excludePostIdsByPlatform = new Map;
|
|
68951
69092
|
for (const session of persisted.values()) {
|
|
68952
69093
|
const platformId = session.platformId;
|
|
@@ -68966,10 +69107,10 @@ class SessionManager extends EventEmitter4 {
|
|
|
68966
69107
|
const excludePostIds = excludePostIdsByPlatform.get(platform.platformId);
|
|
68967
69108
|
platform.getBotUser().then((botUser) => {
|
|
68968
69109
|
cleanupOldStickyMessages(platform, botUser.id, true, excludePostIds).catch((err) => {
|
|
68969
|
-
|
|
69110
|
+
log28.warn(`Failed to cleanup old sticky messages for ${platform.platformId}: ${err}`);
|
|
68970
69111
|
});
|
|
68971
69112
|
}).catch((err) => {
|
|
68972
|
-
|
|
69113
|
+
log28.warn(`Failed to get bot user for cleanup on ${platform.platformId}: ${err}`);
|
|
68973
69114
|
});
|
|
68974
69115
|
}
|
|
68975
69116
|
if (persisted.size > 0) {
|
|
@@ -68983,10 +69124,10 @@ class SessionManager extends EventEmitter4 {
|
|
|
68983
69124
|
}
|
|
68984
69125
|
}
|
|
68985
69126
|
if (pausedToSkip.length > 0) {
|
|
68986
|
-
|
|
69127
|
+
log28.info(`⏸️ ${pausedToSkip.length} session(s) remain paused (waiting for user message)`);
|
|
68987
69128
|
}
|
|
68988
69129
|
if (activeToResume.length > 0) {
|
|
68989
|
-
|
|
69130
|
+
log28.info(`\uD83D\uDD04 Attempting to resume ${activeToResume.length} active session(s)...`);
|
|
68990
69131
|
for (const state of activeToResume) {
|
|
68991
69132
|
await resumeSession(state, this.getContext());
|
|
68992
69133
|
}
|
|
@@ -69081,11 +69222,14 @@ class SessionManager extends EventEmitter4 {
|
|
|
69081
69222
|
return;
|
|
69082
69223
|
await kickUser(session, kickedUser, kickedBy, this.getContext());
|
|
69083
69224
|
}
|
|
69084
|
-
async
|
|
69225
|
+
async setSessionPermissionMode(threadId, username, mode) {
|
|
69085
69226
|
const session = this.findSessionByThreadId(threadId);
|
|
69086
69227
|
if (!session)
|
|
69087
69228
|
return;
|
|
69088
|
-
await
|
|
69229
|
+
await setSessionPermissionMode(session, username, mode, this.getContext());
|
|
69230
|
+
}
|
|
69231
|
+
async enableInteractivePermissions(threadId, username) {
|
|
69232
|
+
await this.setSessionPermissionMode(threadId, username, "default");
|
|
69089
69233
|
}
|
|
69090
69234
|
async reportBug(threadId, description, username, files) {
|
|
69091
69235
|
const session = this.findSessionByThreadId(threadId);
|
|
@@ -69168,10 +69312,13 @@ class SessionManager extends EventEmitter4 {
|
|
|
69168
69312
|
isSessionInteractive(threadId) {
|
|
69169
69313
|
const session = this.findSessionByThreadId(threadId);
|
|
69170
69314
|
if (!session)
|
|
69171
|
-
return
|
|
69172
|
-
|
|
69173
|
-
|
|
69174
|
-
|
|
69315
|
+
return this.permissionMode !== "bypass";
|
|
69316
|
+
const effective = effectivePermissionMode({
|
|
69317
|
+
override: session.permissionModeOverride,
|
|
69318
|
+
sessionHasInteractiveOverride: session.forceInteractivePermissions,
|
|
69319
|
+
botWideMode: this.permissionMode
|
|
69320
|
+
});
|
|
69321
|
+
return effective !== "bypass";
|
|
69175
69322
|
}
|
|
69176
69323
|
async requestMessageApproval(threadId, username, message) {
|
|
69177
69324
|
const session = this.findSessionByThreadId(threadId);
|
|
@@ -69196,7 +69343,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
69196
69343
|
if (!session)
|
|
69197
69344
|
return;
|
|
69198
69345
|
await createAndSwitchToWorktree(session, branch, username, {
|
|
69199
|
-
|
|
69346
|
+
permissionMode: this.permissionMode,
|
|
69200
69347
|
chromeEnabled: this.chromeEnabled,
|
|
69201
69348
|
worktreeMode: this.worktreeMode,
|
|
69202
69349
|
permissionTimeoutMs: this.limits.permissionTimeoutSeconds * 1000,
|
|
@@ -69405,7 +69552,7 @@ Mention me to start a session in this worktree.`, threadId);
|
|
|
69405
69552
|
const message = messageBuilder(formatter);
|
|
69406
69553
|
await post(session, "info", message);
|
|
69407
69554
|
} catch (err) {
|
|
69408
|
-
|
|
69555
|
+
log28.warn(`Failed to broadcast to session ${session.threadId}: ${err}`);
|
|
69409
69556
|
}
|
|
69410
69557
|
}
|
|
69411
69558
|
}
|
|
@@ -69424,7 +69571,7 @@ Mention me to start a session in this worktree.`, threadId);
|
|
|
69424
69571
|
session.messageManager?.setPendingUpdatePrompt({ postId: post2.id });
|
|
69425
69572
|
this.registerPost(post2.id, session.threadId);
|
|
69426
69573
|
} catch (err) {
|
|
69427
|
-
|
|
69574
|
+
log28.warn(`Failed to post ask message to ${threadId}: ${err}`);
|
|
69428
69575
|
}
|
|
69429
69576
|
}
|
|
69430
69577
|
}
|
|
@@ -77020,29 +77167,29 @@ function SessionLog({ logs, maxLines = 20 }) {
|
|
|
77020
77167
|
return /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
|
|
77021
77168
|
flexDirection: "column",
|
|
77022
77169
|
flexShrink: 0,
|
|
77023
|
-
children: displayLogs.map((
|
|
77170
|
+
children: displayLogs.map((log29) => /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
|
|
77024
77171
|
flexShrink: 0,
|
|
77025
77172
|
children: [
|
|
77026
77173
|
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
|
|
77027
|
-
color: getColorForLevel(
|
|
77174
|
+
color: getColorForLevel(log29.level),
|
|
77028
77175
|
dimColor: true,
|
|
77029
77176
|
wrap: "truncate",
|
|
77030
77177
|
children: [
|
|
77031
77178
|
"[",
|
|
77032
|
-
padComponent(
|
|
77179
|
+
padComponent(log29.component),
|
|
77033
77180
|
"]"
|
|
77034
77181
|
]
|
|
77035
77182
|
}, undefined, true, undefined, this),
|
|
77036
77183
|
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
|
|
77037
|
-
color: getColorForLevel(
|
|
77184
|
+
color: getColorForLevel(log29.level),
|
|
77038
77185
|
wrap: "truncate",
|
|
77039
77186
|
children: [
|
|
77040
77187
|
" ",
|
|
77041
|
-
|
|
77188
|
+
log29.message
|
|
77042
77189
|
]
|
|
77043
77190
|
}, undefined, true, undefined, this)
|
|
77044
77191
|
]
|
|
77045
|
-
},
|
|
77192
|
+
}, log29.id, true, undefined, this))
|
|
77046
77193
|
}, undefined, false, undefined, this);
|
|
77047
77194
|
}
|
|
77048
77195
|
// src/ui/components/Footer.tsx
|
|
@@ -77072,6 +77219,34 @@ function ToggleKey({ keyChar, label, enabled, color }) {
|
|
|
77072
77219
|
]
|
|
77073
77220
|
}, undefined, true, undefined, this);
|
|
77074
77221
|
}
|
|
77222
|
+
function PermissionModeKey({ mode }) {
|
|
77223
|
+
const color = mode === "default" ? "green" : mode === "auto" ? "yellow" : "red";
|
|
77224
|
+
return /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
|
|
77225
|
+
gap: 0,
|
|
77226
|
+
children: [
|
|
77227
|
+
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
77228
|
+
dimColor: true,
|
|
77229
|
+
children: "["
|
|
77230
|
+
}, undefined, false, undefined, this),
|
|
77231
|
+
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
77232
|
+
color,
|
|
77233
|
+
bold: true,
|
|
77234
|
+
children: "p"
|
|
77235
|
+
}, undefined, false, undefined, this),
|
|
77236
|
+
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
77237
|
+
dimColor: true,
|
|
77238
|
+
children: "]"
|
|
77239
|
+
}, undefined, false, undefined, this),
|
|
77240
|
+
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
77241
|
+
color,
|
|
77242
|
+
children: [
|
|
77243
|
+
"erms:",
|
|
77244
|
+
permissionModeDisplay(mode).label.toLowerCase()
|
|
77245
|
+
]
|
|
77246
|
+
}, undefined, true, undefined, this)
|
|
77247
|
+
]
|
|
77248
|
+
}, undefined, true, undefined, this);
|
|
77249
|
+
}
|
|
77075
77250
|
function PlatformToggle({ index, platform: platform2 }) {
|
|
77076
77251
|
let color;
|
|
77077
77252
|
if (!platform2.enabled) {
|
|
@@ -77172,10 +77347,8 @@ function Footer({
|
|
|
77172
77347
|
label: "ebug",
|
|
77173
77348
|
enabled: toggles.debugMode
|
|
77174
77349
|
}, undefined, false, undefined, this),
|
|
77175
|
-
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(
|
|
77176
|
-
|
|
77177
|
-
label: "erms",
|
|
77178
|
-
enabled: !toggles.skipPermissions
|
|
77350
|
+
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(PermissionModeKey, {
|
|
77351
|
+
mode: toggles.permissionMode
|
|
77179
77352
|
}, undefined, false, undefined, this),
|
|
77180
77353
|
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(ToggleKey, {
|
|
77181
77354
|
keyChar: "c",
|
|
@@ -77540,7 +77713,7 @@ function LogPanel({ logs, maxLines = 10, focused = false }) {
|
|
|
77540
77713
|
const scrollRef = import_react59.default.useRef(null);
|
|
77541
77714
|
const { stdout } = use_stdout_default();
|
|
77542
77715
|
const isDebug = process.env.DEBUG === "1";
|
|
77543
|
-
const displayLogs = logs.filter((
|
|
77716
|
+
const displayLogs = logs.filter((log29) => isDebug || log29.level !== "debug");
|
|
77544
77717
|
const visibleLogs = displayLogs.slice(-Math.max(maxLines * 3, 100));
|
|
77545
77718
|
import_react59.default.useEffect(() => {
|
|
77546
77719
|
const handleResize = () => scrollRef.current?.remeasure();
|
|
@@ -77580,25 +77753,25 @@ function LogPanel({ logs, maxLines = 10, focused = false }) {
|
|
|
77580
77753
|
overflow: "hidden",
|
|
77581
77754
|
children: /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(ScrollView, {
|
|
77582
77755
|
ref: scrollRef,
|
|
77583
|
-
children: visibleLogs.map((
|
|
77756
|
+
children: visibleLogs.map((log29) => /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
|
|
77584
77757
|
children: [
|
|
77585
77758
|
/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
|
|
77586
77759
|
dimColor: true,
|
|
77587
77760
|
children: [
|
|
77588
77761
|
"[",
|
|
77589
|
-
padComponent2(
|
|
77762
|
+
padComponent2(log29.component),
|
|
77590
77763
|
"]"
|
|
77591
77764
|
]
|
|
77592
77765
|
}, undefined, true, undefined, this),
|
|
77593
77766
|
/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
|
|
77594
|
-
color: getLevelColor(
|
|
77767
|
+
color: getLevelColor(log29.level),
|
|
77595
77768
|
children: [
|
|
77596
77769
|
" ",
|
|
77597
|
-
|
|
77770
|
+
log29.message
|
|
77598
77771
|
]
|
|
77599
77772
|
}, undefined, true, undefined, this)
|
|
77600
77773
|
]
|
|
77601
|
-
},
|
|
77774
|
+
}, log29.id, true, undefined, this))
|
|
77602
77775
|
}, undefined, false, undefined, this)
|
|
77603
77776
|
}, undefined, false, undefined, this);
|
|
77604
77777
|
}
|
|
@@ -78115,10 +78288,10 @@ function useAppState(initialConfig) {
|
|
|
78115
78288
|
});
|
|
78116
78289
|
}, []);
|
|
78117
78290
|
const getLogsForSession = import_react60.useCallback((sessionId) => {
|
|
78118
|
-
return state.logs.filter((
|
|
78291
|
+
return state.logs.filter((log29) => log29.sessionId === sessionId);
|
|
78119
78292
|
}, [state.logs]);
|
|
78120
78293
|
const getGlobalLogs = import_react60.useCallback(() => {
|
|
78121
|
-
return state.logs.filter((
|
|
78294
|
+
return state.logs.filter((log29) => !log29.sessionId);
|
|
78122
78295
|
}, [state.logs]);
|
|
78123
78296
|
const togglePlatformEnabled = import_react60.useCallback((platformId) => {
|
|
78124
78297
|
let newEnabled = false;
|
|
@@ -78239,6 +78412,19 @@ function useKeyboard({
|
|
|
78239
78412
|
});
|
|
78240
78413
|
}
|
|
78241
78414
|
|
|
78415
|
+
// src/config/permission-mode-cycle.ts
|
|
78416
|
+
var PERMISSION_MODE_CYCLE = [
|
|
78417
|
+
"default",
|
|
78418
|
+
"auto",
|
|
78419
|
+
"bypass"
|
|
78420
|
+
];
|
|
78421
|
+
function nextPermissionMode(current) {
|
|
78422
|
+
const idx = PERMISSION_MODE_CYCLE.indexOf(current);
|
|
78423
|
+
if (idx === -1)
|
|
78424
|
+
return PERMISSION_MODE_CYCLE[0];
|
|
78425
|
+
return PERMISSION_MODE_CYCLE[(idx + 1) % PERMISSION_MODE_CYCLE.length];
|
|
78426
|
+
}
|
|
78427
|
+
|
|
78242
78428
|
// src/ui/App.tsx
|
|
78243
78429
|
var jsx_dev_runtime13 = __toESM(require_jsx_dev_runtime(), 1);
|
|
78244
78430
|
function App2({ config, onStateReady, onResizeReady, onQuit, toggleCallbacks }) {
|
|
@@ -78261,7 +78447,7 @@ function App2({ config, onStateReady, onResizeReady, onQuit, toggleCallbacks })
|
|
|
78261
78447
|
const [resizeCount, setResizeCount] = import_react61.default.useState(0);
|
|
78262
78448
|
const [toggles, setToggles] = import_react61.default.useState({
|
|
78263
78449
|
debugMode: process.env.DEBUG === "1",
|
|
78264
|
-
|
|
78450
|
+
permissionMode: config.permissionMode,
|
|
78265
78451
|
chromeEnabled: config.chromeEnabled,
|
|
78266
78452
|
keepAliveEnabled: config.keepAliveEnabled,
|
|
78267
78453
|
updateModalVisible: false,
|
|
@@ -78282,9 +78468,9 @@ function App2({ config, onStateReady, onResizeReady, onQuit, toggleCallbacks })
|
|
|
78282
78468
|
}, [toggleCallbacks]);
|
|
78283
78469
|
const handlePermissionsToggle = import_react61.default.useCallback(() => {
|
|
78284
78470
|
setToggles((prev) => {
|
|
78285
|
-
const
|
|
78286
|
-
toggleCallbacks?.onPermissionsToggle?.(
|
|
78287
|
-
return { ...prev,
|
|
78471
|
+
const newMode = nextPermissionMode(prev.permissionMode);
|
|
78472
|
+
toggleCallbacks?.onPermissionsToggle?.(newMode);
|
|
78473
|
+
return { ...prev, permissionMode: newMode };
|
|
78288
78474
|
});
|
|
78289
78475
|
}, [toggleCallbacks]);
|
|
78290
78476
|
const handleChromeToggle = import_react61.default.useCallback(() => {
|
|
@@ -78749,7 +78935,7 @@ class HeadlessProvider {
|
|
|
78749
78935
|
this.options = options2;
|
|
78750
78936
|
this.toggles = {
|
|
78751
78937
|
debugMode: process.env.DEBUG === "1",
|
|
78752
|
-
|
|
78938
|
+
permissionMode: options2.config.permissionMode,
|
|
78753
78939
|
chromeEnabled: options2.config.chromeEnabled,
|
|
78754
78940
|
keepAliveEnabled: options2.config.keepAliveEnabled,
|
|
78755
78941
|
updateModalVisible: false,
|
|
@@ -79112,7 +79298,7 @@ import { EventEmitter as EventEmitter9 } from "events";
|
|
|
79112
79298
|
// src/auto-update/checker.ts
|
|
79113
79299
|
init_logger();
|
|
79114
79300
|
import { EventEmitter as EventEmitter7 } from "events";
|
|
79115
|
-
var
|
|
79301
|
+
var log29 = createLogger("checker");
|
|
79116
79302
|
var PACKAGE_NAME = "claude-threads";
|
|
79117
79303
|
function compareVersions(a, b) {
|
|
79118
79304
|
const partsA = a.replace(/^v/, "").split(".").map(Number);
|
|
@@ -79135,13 +79321,13 @@ async function fetchLatestVersion() {
|
|
|
79135
79321
|
}
|
|
79136
79322
|
});
|
|
79137
79323
|
if (!response.ok) {
|
|
79138
|
-
|
|
79324
|
+
log29.warn(`Failed to fetch latest version: HTTP ${response.status}`);
|
|
79139
79325
|
return null;
|
|
79140
79326
|
}
|
|
79141
79327
|
const data = await response.json();
|
|
79142
79328
|
return data.version ?? null;
|
|
79143
79329
|
} catch (err) {
|
|
79144
|
-
|
|
79330
|
+
log29.warn(`Failed to fetch latest version: ${err}`);
|
|
79145
79331
|
return null;
|
|
79146
79332
|
}
|
|
79147
79333
|
}
|
|
@@ -79158,38 +79344,38 @@ class UpdateChecker extends EventEmitter7 {
|
|
|
79158
79344
|
}
|
|
79159
79345
|
start() {
|
|
79160
79346
|
if (!this.config.enabled) {
|
|
79161
|
-
|
|
79347
|
+
log29.debug("Auto-update disabled, not starting checker");
|
|
79162
79348
|
return;
|
|
79163
79349
|
}
|
|
79164
79350
|
setTimeout(() => {
|
|
79165
79351
|
this.check().catch((err) => {
|
|
79166
|
-
|
|
79352
|
+
log29.warn(`Initial update check failed: ${err}`);
|
|
79167
79353
|
});
|
|
79168
79354
|
}, 5000);
|
|
79169
79355
|
const intervalMs = this.config.checkIntervalMinutes * 60 * 1000;
|
|
79170
79356
|
this.checkInterval = setInterval(() => {
|
|
79171
79357
|
this.check().catch((err) => {
|
|
79172
|
-
|
|
79358
|
+
log29.warn(`Periodic update check failed: ${err}`);
|
|
79173
79359
|
});
|
|
79174
79360
|
}, intervalMs);
|
|
79175
|
-
|
|
79361
|
+
log29.info(`\uD83D\uDD04 Update checker started (every ${this.config.checkIntervalMinutes} minutes)`);
|
|
79176
79362
|
}
|
|
79177
79363
|
stop() {
|
|
79178
79364
|
if (this.checkInterval) {
|
|
79179
79365
|
clearInterval(this.checkInterval);
|
|
79180
79366
|
this.checkInterval = null;
|
|
79181
79367
|
}
|
|
79182
|
-
|
|
79368
|
+
log29.debug("Update checker stopped");
|
|
79183
79369
|
}
|
|
79184
79370
|
async check() {
|
|
79185
79371
|
if (this.isChecking) {
|
|
79186
|
-
|
|
79372
|
+
log29.debug("Check already in progress, skipping");
|
|
79187
79373
|
return this.lastUpdateInfo;
|
|
79188
79374
|
}
|
|
79189
79375
|
this.isChecking = true;
|
|
79190
79376
|
this.emit("check:start");
|
|
79191
79377
|
try {
|
|
79192
|
-
|
|
79378
|
+
log29.debug("Checking for updates...");
|
|
79193
79379
|
const latestVersion2 = await fetchLatestVersion();
|
|
79194
79380
|
if (!latestVersion2) {
|
|
79195
79381
|
this.emit("check:complete", false);
|
|
@@ -79206,18 +79392,18 @@ class UpdateChecker extends EventEmitter7 {
|
|
|
79206
79392
|
detectedAt: new Date
|
|
79207
79393
|
};
|
|
79208
79394
|
if (!this.lastUpdateInfo || this.lastUpdateInfo.latestVersion !== latestVersion2) {
|
|
79209
|
-
|
|
79395
|
+
log29.info(`\uD83C\uDD95 Update available: v${currentVersion} → v${latestVersion2}`);
|
|
79210
79396
|
this.lastUpdateInfo = updateInfo;
|
|
79211
79397
|
this.emit("update", updateInfo);
|
|
79212
79398
|
}
|
|
79213
79399
|
this.emit("check:complete", true);
|
|
79214
79400
|
return updateInfo;
|
|
79215
79401
|
}
|
|
79216
|
-
|
|
79402
|
+
log29.debug(`Up to date (v${currentVersion})`);
|
|
79217
79403
|
this.emit("check:complete", false);
|
|
79218
79404
|
return null;
|
|
79219
79405
|
} catch (err) {
|
|
79220
|
-
|
|
79406
|
+
log29.warn(`Update check failed: ${err}`);
|
|
79221
79407
|
this.emit("check:error", err);
|
|
79222
79408
|
return null;
|
|
79223
79409
|
} finally {
|
|
@@ -79288,7 +79474,7 @@ function isInScheduledWindow(window2) {
|
|
|
79288
79474
|
}
|
|
79289
79475
|
|
|
79290
79476
|
// src/auto-update/scheduler.ts
|
|
79291
|
-
var
|
|
79477
|
+
var log30 = createLogger("scheduler");
|
|
79292
79478
|
|
|
79293
79479
|
class UpdateScheduler extends EventEmitter8 {
|
|
79294
79480
|
config;
|
|
@@ -79312,7 +79498,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79312
79498
|
scheduleUpdate(updateInfo) {
|
|
79313
79499
|
this.pendingUpdate = updateInfo;
|
|
79314
79500
|
if (this.config.autoRestartMode === "immediate") {
|
|
79315
|
-
|
|
79501
|
+
log30.info("Immediate mode: triggering update now");
|
|
79316
79502
|
this.emit("ready", updateInfo);
|
|
79317
79503
|
return;
|
|
79318
79504
|
}
|
|
@@ -79325,19 +79511,19 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79325
79511
|
this.scheduledRestartAt = null;
|
|
79326
79512
|
this.askApprovals.clear();
|
|
79327
79513
|
this.askStartTime = null;
|
|
79328
|
-
|
|
79514
|
+
log30.debug("Update schedule cancelled");
|
|
79329
79515
|
}
|
|
79330
79516
|
deferUpdate(minutes) {
|
|
79331
79517
|
const deferUntil = new Date(Date.now() + minutes * 60 * 1000);
|
|
79332
79518
|
this.scheduledRestartAt = null;
|
|
79333
79519
|
this.idleStartTime = null;
|
|
79334
79520
|
this.emit("deferred", deferUntil);
|
|
79335
|
-
|
|
79521
|
+
log30.info(`Update deferred until ${deferUntil.toLocaleTimeString()}`);
|
|
79336
79522
|
return deferUntil;
|
|
79337
79523
|
}
|
|
79338
79524
|
recordAskResponse(threadId, approved) {
|
|
79339
79525
|
this.askApprovals.set(threadId, approved);
|
|
79340
|
-
|
|
79526
|
+
log30.debug(`Thread ${threadId.substring(0, 8)} ${approved ? "approved" : "denied"} update`);
|
|
79341
79527
|
this.checkAskCondition();
|
|
79342
79528
|
}
|
|
79343
79529
|
getScheduledRestartAt() {
|
|
@@ -79358,7 +79544,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79358
79544
|
return;
|
|
79359
79545
|
this.checkCondition();
|
|
79360
79546
|
this.checkTimer = setInterval(() => this.checkCondition(), 1e4);
|
|
79361
|
-
|
|
79547
|
+
log30.debug(`Started checking for ${this.config.autoRestartMode} condition`);
|
|
79362
79548
|
}
|
|
79363
79549
|
stopChecking() {
|
|
79364
79550
|
if (this.checkTimer) {
|
|
@@ -79389,17 +79575,17 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79389
79575
|
if (activity.activeSessionCount === 0) {
|
|
79390
79576
|
if (!this.idleStartTime) {
|
|
79391
79577
|
this.idleStartTime = new Date;
|
|
79392
|
-
|
|
79578
|
+
log30.debug("No active sessions, starting idle timer");
|
|
79393
79579
|
}
|
|
79394
79580
|
const idleMs = Date.now() - this.idleStartTime.getTime();
|
|
79395
79581
|
const requiredMs = this.config.idleTimeoutMinutes * 60 * 1000;
|
|
79396
79582
|
if (idleMs >= requiredMs) {
|
|
79397
|
-
|
|
79583
|
+
log30.info(`Idle for ${this.config.idleTimeoutMinutes} minutes, triggering update`);
|
|
79398
79584
|
this.triggerCountdown();
|
|
79399
79585
|
}
|
|
79400
79586
|
} else {
|
|
79401
79587
|
if (this.idleStartTime) {
|
|
79402
|
-
|
|
79588
|
+
log30.debug("Sessions became active, resetting idle timer");
|
|
79403
79589
|
this.idleStartTime = null;
|
|
79404
79590
|
}
|
|
79405
79591
|
}
|
|
@@ -79410,7 +79596,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79410
79596
|
const quietMs = Date.now() - activity.lastActivityAt.getTime();
|
|
79411
79597
|
const requiredMs = this.config.quietTimeoutMinutes * 60 * 1000;
|
|
79412
79598
|
if (quietMs >= requiredMs && !activity.anySessionBusy) {
|
|
79413
|
-
|
|
79599
|
+
log30.info(`Sessions quiet for ${this.config.quietTimeoutMinutes} minutes, triggering update`);
|
|
79414
79600
|
this.triggerCountdown();
|
|
79415
79601
|
}
|
|
79416
79602
|
} else if (activity.activeSessionCount === 0) {
|
|
@@ -79420,7 +79606,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79420
79606
|
const idleMs = Date.now() - this.idleStartTime.getTime();
|
|
79421
79607
|
const requiredMs = this.config.quietTimeoutMinutes * 60 * 1000;
|
|
79422
79608
|
if (idleMs >= requiredMs) {
|
|
79423
|
-
|
|
79609
|
+
log30.info("No sessions and quiet timeout reached, triggering update");
|
|
79424
79610
|
this.triggerCountdown();
|
|
79425
79611
|
}
|
|
79426
79612
|
}
|
|
@@ -79431,13 +79617,13 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79431
79617
|
}
|
|
79432
79618
|
const activity = this.getSessionActivity();
|
|
79433
79619
|
if (activity.activeSessionCount === 0) {
|
|
79434
|
-
|
|
79620
|
+
log30.info("Within scheduled window and no active sessions, triggering update");
|
|
79435
79621
|
this.triggerCountdown();
|
|
79436
79622
|
} else if (activity.lastActivityAt) {
|
|
79437
79623
|
const quietMs = Date.now() - activity.lastActivityAt.getTime();
|
|
79438
79624
|
const requiredMs = this.config.idleTimeoutMinutes * 60 * 1000;
|
|
79439
79625
|
if (quietMs >= requiredMs && !activity.anySessionBusy) {
|
|
79440
|
-
|
|
79626
|
+
log30.info("Within scheduled window and sessions quiet, triggering update");
|
|
79441
79627
|
this.triggerCountdown();
|
|
79442
79628
|
}
|
|
79443
79629
|
}
|
|
@@ -79445,14 +79631,14 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79445
79631
|
checkAskCondition() {
|
|
79446
79632
|
const threadIds = this.getActiveThreadIds();
|
|
79447
79633
|
if (threadIds.length === 0) {
|
|
79448
|
-
|
|
79634
|
+
log30.info("No active threads, proceeding with update");
|
|
79449
79635
|
this.triggerCountdown();
|
|
79450
79636
|
return;
|
|
79451
79637
|
}
|
|
79452
79638
|
if (!this.askStartTime && this.pendingUpdate) {
|
|
79453
79639
|
this.askStartTime = new Date;
|
|
79454
79640
|
this.postAskMessage(threadIds, this.pendingUpdate.latestVersion).catch((err) => {
|
|
79455
|
-
|
|
79641
|
+
log30.warn(`Failed to post ask message: ${err}`);
|
|
79456
79642
|
});
|
|
79457
79643
|
return;
|
|
79458
79644
|
}
|
|
@@ -79465,12 +79651,12 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79465
79651
|
denials++;
|
|
79466
79652
|
}
|
|
79467
79653
|
if (approvals > threadIds.length / 2) {
|
|
79468
|
-
|
|
79654
|
+
log30.info(`Majority approved (${approvals}/${threadIds.length}), triggering update`);
|
|
79469
79655
|
this.triggerCountdown();
|
|
79470
79656
|
return;
|
|
79471
79657
|
}
|
|
79472
79658
|
if (denials > threadIds.length / 2) {
|
|
79473
|
-
|
|
79659
|
+
log30.info(`Majority denied (${denials}/${threadIds.length}), deferring update`);
|
|
79474
79660
|
this.deferUpdate(60);
|
|
79475
79661
|
return;
|
|
79476
79662
|
}
|
|
@@ -79478,7 +79664,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79478
79664
|
const elapsedMs = Date.now() - this.askStartTime.getTime();
|
|
79479
79665
|
const timeoutMs = this.config.askTimeoutMinutes * 60 * 1000;
|
|
79480
79666
|
if (elapsedMs >= timeoutMs) {
|
|
79481
|
-
|
|
79667
|
+
log30.info(`Ask timeout reached (${this.config.askTimeoutMinutes} min), triggering update`);
|
|
79482
79668
|
this.triggerCountdown();
|
|
79483
79669
|
}
|
|
79484
79670
|
}
|
|
@@ -79498,7 +79684,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79498
79684
|
this.emit("ready", this.pendingUpdate);
|
|
79499
79685
|
}
|
|
79500
79686
|
}, 1000);
|
|
79501
|
-
|
|
79687
|
+
log30.info("Update countdown started (60 seconds)");
|
|
79502
79688
|
}
|
|
79503
79689
|
stopCountdown() {
|
|
79504
79690
|
if (this.countdownTimer) {
|
|
@@ -79511,27 +79697,27 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79511
79697
|
// src/auto-update/installer.ts
|
|
79512
79698
|
init_logger();
|
|
79513
79699
|
import { spawn as spawn4, spawnSync } from "child_process";
|
|
79514
|
-
import { existsSync as existsSync13, readFileSync as readFileSync9, writeFileSync as
|
|
79700
|
+
import { existsSync as existsSync13, readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync4 } from "fs";
|
|
79515
79701
|
import { dirname as dirname8, resolve as resolve6 } from "path";
|
|
79516
79702
|
import { homedir as homedir5 } from "os";
|
|
79517
|
-
var
|
|
79703
|
+
var log31 = createLogger("installer");
|
|
79518
79704
|
function detectPackageManager() {
|
|
79519
79705
|
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
79520
79706
|
const originalInstaller = detectOriginalInstaller();
|
|
79521
79707
|
if (originalInstaller) {
|
|
79522
|
-
|
|
79708
|
+
log31.debug(`Detected original installer: ${originalInstaller}`);
|
|
79523
79709
|
if (originalInstaller === "bun") {
|
|
79524
79710
|
const bunCheck2 = spawnSync("bun", ["--version"], { stdio: "ignore" });
|
|
79525
79711
|
if (bunCheck2.status === 0) {
|
|
79526
79712
|
return { cmd: "bun", isBun: true };
|
|
79527
79713
|
}
|
|
79528
|
-
|
|
79714
|
+
log31.warn("Originally installed with bun, but bun not found. Falling back to npm.");
|
|
79529
79715
|
} else {
|
|
79530
79716
|
const npmCheck2 = spawnSync(npmCmd, ["--version"], { stdio: "ignore" });
|
|
79531
79717
|
if (npmCheck2.status === 0) {
|
|
79532
79718
|
return { cmd: npmCmd, isBun: false };
|
|
79533
79719
|
}
|
|
79534
|
-
|
|
79720
|
+
log31.warn("Originally installed with npm, but npm not found. Falling back to bun.");
|
|
79535
79721
|
}
|
|
79536
79722
|
}
|
|
79537
79723
|
const bunCheck = spawnSync("bun", ["--version"], { stdio: "ignore" });
|
|
@@ -79582,7 +79768,7 @@ function loadUpdateState() {
|
|
|
79582
79768
|
return JSON.parse(content);
|
|
79583
79769
|
}
|
|
79584
79770
|
} catch (err) {
|
|
79585
|
-
|
|
79771
|
+
log31.warn(`Failed to load update state: ${err}`);
|
|
79586
79772
|
}
|
|
79587
79773
|
return {};
|
|
79588
79774
|
}
|
|
@@ -79592,19 +79778,19 @@ function saveUpdateState(state) {
|
|
|
79592
79778
|
if (!existsSync13(dir)) {
|
|
79593
79779
|
mkdirSync4(dir, { recursive: true });
|
|
79594
79780
|
}
|
|
79595
|
-
|
|
79596
|
-
|
|
79781
|
+
writeFileSync6(STATE_PATH, JSON.stringify(state, null, 2), "utf-8");
|
|
79782
|
+
log31.debug("Update state saved");
|
|
79597
79783
|
} catch (err) {
|
|
79598
|
-
|
|
79784
|
+
log31.warn(`Failed to save update state: ${err}`);
|
|
79599
79785
|
}
|
|
79600
79786
|
}
|
|
79601
79787
|
function clearUpdateState() {
|
|
79602
79788
|
try {
|
|
79603
79789
|
if (existsSync13(STATE_PATH)) {
|
|
79604
|
-
|
|
79790
|
+
writeFileSync6(STATE_PATH, "{}", "utf-8");
|
|
79605
79791
|
}
|
|
79606
79792
|
} catch (err) {
|
|
79607
|
-
|
|
79793
|
+
log31.warn(`Failed to clear update state: ${err}`);
|
|
79608
79794
|
}
|
|
79609
79795
|
}
|
|
79610
79796
|
function checkJustUpdated() {
|
|
@@ -79636,11 +79822,11 @@ function clearRuntimeSettings() {
|
|
|
79636
79822
|
}
|
|
79637
79823
|
}
|
|
79638
79824
|
async function installVersion(version) {
|
|
79639
|
-
|
|
79825
|
+
log31.info(`\uD83D\uDCE6 Installing ${PACKAGE_NAME2}@${version}...`);
|
|
79640
79826
|
const pm = detectPackageManager();
|
|
79641
79827
|
if (!pm) {
|
|
79642
79828
|
const error = "Neither bun nor npm found in PATH. Cannot install update.";
|
|
79643
|
-
|
|
79829
|
+
log31.error(`❌ ${error}`);
|
|
79644
79830
|
return { success: false, error };
|
|
79645
79831
|
}
|
|
79646
79832
|
saveUpdateState({
|
|
@@ -79652,7 +79838,7 @@ async function installVersion(version) {
|
|
|
79652
79838
|
return new Promise((resolve7) => {
|
|
79653
79839
|
const { cmd, isBun: isBun3 } = pm;
|
|
79654
79840
|
const args = ["install", "-g", `${PACKAGE_NAME2}@${version}`];
|
|
79655
|
-
|
|
79841
|
+
log31.debug(`Using ${isBun3 ? "bun" : "npm"} for installation`);
|
|
79656
79842
|
const child = spawn4(cmd, args, {
|
|
79657
79843
|
stdio: ["ignore", "pipe", "pipe"],
|
|
79658
79844
|
env: {
|
|
@@ -79670,7 +79856,7 @@ async function installVersion(version) {
|
|
|
79670
79856
|
});
|
|
79671
79857
|
child.on("close", (code) => {
|
|
79672
79858
|
if (code === 0) {
|
|
79673
|
-
|
|
79859
|
+
log31.info(`✅ Successfully installed ${PACKAGE_NAME2}@${version}`);
|
|
79674
79860
|
saveUpdateState({
|
|
79675
79861
|
previousVersion: VERSION,
|
|
79676
79862
|
targetVersion: version,
|
|
@@ -79680,20 +79866,20 @@ async function installVersion(version) {
|
|
|
79680
79866
|
resolve7({ success: true });
|
|
79681
79867
|
} else {
|
|
79682
79868
|
const errorMsg = stderr || stdout || `Exit code: ${code}`;
|
|
79683
|
-
|
|
79869
|
+
log31.error(`❌ Installation failed: ${errorMsg}`);
|
|
79684
79870
|
clearUpdateState();
|
|
79685
79871
|
resolve7({ success: false, error: errorMsg });
|
|
79686
79872
|
}
|
|
79687
79873
|
});
|
|
79688
79874
|
child.on("error", (err) => {
|
|
79689
|
-
|
|
79875
|
+
log31.error(`❌ Failed to spawn npm: ${err}`);
|
|
79690
79876
|
clearUpdateState();
|
|
79691
79877
|
resolve7({ success: false, error: err.message });
|
|
79692
79878
|
});
|
|
79693
79879
|
setTimeout(() => {
|
|
79694
79880
|
if (child.exitCode === null) {
|
|
79695
79881
|
child.kill();
|
|
79696
|
-
|
|
79882
|
+
log31.error("❌ Installation timed out");
|
|
79697
79883
|
clearUpdateState();
|
|
79698
79884
|
resolve7({ success: false, error: "Installation timed out" });
|
|
79699
79885
|
}
|
|
@@ -79735,7 +79921,7 @@ class UpdateInstaller {
|
|
|
79735
79921
|
}
|
|
79736
79922
|
|
|
79737
79923
|
// src/auto-update/manager.ts
|
|
79738
|
-
var
|
|
79924
|
+
var log32 = createLogger("updater");
|
|
79739
79925
|
|
|
79740
79926
|
class AutoUpdateManager extends EventEmitter9 {
|
|
79741
79927
|
config;
|
|
@@ -79758,23 +79944,23 @@ class AutoUpdateManager extends EventEmitter9 {
|
|
|
79758
79944
|
}
|
|
79759
79945
|
start() {
|
|
79760
79946
|
if (!this.config.enabled) {
|
|
79761
|
-
|
|
79947
|
+
log32.info("Auto-update is disabled");
|
|
79762
79948
|
return;
|
|
79763
79949
|
}
|
|
79764
79950
|
const updateResult = this.installer.checkJustUpdated();
|
|
79765
79951
|
if (updateResult) {
|
|
79766
|
-
|
|
79952
|
+
log32.info(`\uD83C\uDF89 Updated from v${updateResult.previousVersion} to v${updateResult.currentVersion}`);
|
|
79767
79953
|
this.callbacks.broadcastUpdate((fmt) => `\uD83C\uDF89 ${fmt.formatBold("Bot updated")} from v${updateResult.previousVersion} to v${updateResult.currentVersion}`).catch((err) => {
|
|
79768
|
-
|
|
79954
|
+
log32.warn(`Failed to broadcast update notification: ${err}`);
|
|
79769
79955
|
});
|
|
79770
79956
|
}
|
|
79771
79957
|
this.checker.start();
|
|
79772
|
-
|
|
79958
|
+
log32.info(`\uD83D\uDD04 Auto-update manager started (mode: ${this.config.autoRestartMode})`);
|
|
79773
79959
|
}
|
|
79774
79960
|
stop() {
|
|
79775
79961
|
this.checker.stop();
|
|
79776
79962
|
this.scheduler.stop();
|
|
79777
|
-
|
|
79963
|
+
log32.debug("Auto-update manager stopped");
|
|
79778
79964
|
}
|
|
79779
79965
|
getState() {
|
|
79780
79966
|
return { ...this.state };
|
|
@@ -79788,10 +79974,10 @@ class AutoUpdateManager extends EventEmitter9 {
|
|
|
79788
79974
|
async forceUpdate() {
|
|
79789
79975
|
const updateInfo = this.state.updateInfo || await this.checker.check();
|
|
79790
79976
|
if (!updateInfo) {
|
|
79791
|
-
|
|
79977
|
+
log32.info("No update available");
|
|
79792
79978
|
return;
|
|
79793
79979
|
}
|
|
79794
|
-
|
|
79980
|
+
log32.info("Forcing immediate update");
|
|
79795
79981
|
await this.performUpdate(updateInfo);
|
|
79796
79982
|
}
|
|
79797
79983
|
deferUpdate(minutes = 60) {
|
|
@@ -79846,7 +80032,7 @@ class AutoUpdateManager extends EventEmitter9 {
|
|
|
79846
80032
|
await this.callbacks.broadcastUpdate((fmt) => `✅ ${fmt.formatBold("Update installed")} - restarting now. ${fmt.formatItalic("Sessions will resume automatically.")}`).catch(() => {});
|
|
79847
80033
|
await new Promise((resolve7) => setTimeout(resolve7, 1000));
|
|
79848
80034
|
await this.callbacks.prepareForRestart();
|
|
79849
|
-
|
|
80035
|
+
log32.info(`\uD83D\uDD04 Restarting for update to v${updateInfo.latestVersion}`);
|
|
79850
80036
|
process.stdout.write("\x1B[2J\x1B[H");
|
|
79851
80037
|
process.stdout.write("\x1B[?25h");
|
|
79852
80038
|
process.exit(RESTART_EXIT_CODE);
|
|
@@ -79906,7 +80092,7 @@ function wirePlatformEvents(platformId, client, session, ui) {
|
|
|
79906
80092
|
ui.addLog({ level: "error", component: platformId, message: String(e) });
|
|
79907
80093
|
});
|
|
79908
80094
|
}
|
|
79909
|
-
program.name("claude-threads").version(VERSION).description("Share Claude Code sessions in Mattermost").option("--url <url>", "Mattermost server URL").option("--token <token>", "Mattermost bot token").option("--channel <id>", "Mattermost channel ID").option("--bot-name <name>", "Bot mention name (default: claude-code)").option("--allowed-users <users>", "Comma-separated allowed usernames").option("--skip-permissions", "
|
|
80095
|
+
program.name("claude-threads").version(VERSION).description("Share Claude Code sessions in Mattermost").option("--url <url>", "Mattermost server URL").option("--token <token>", "Mattermost bot token").option("--channel <id>", "Mattermost channel ID").option("--bot-name <name>", "Bot mention name (default: claude-code)").option("--allowed-users <users>", "Comma-separated allowed usernames").option("--permission-mode <mode>", "Permission mode: default | auto | bypass (default: from config)").option("--skip-permissions", "[deprecated] Alias for --permission-mode bypass").option("--no-skip-permissions", "[deprecated] Alias for --permission-mode default").option("--chrome", "Enable Claude in Chrome integration").option("--no-chrome", "Disable Claude in Chrome integration").option("--worktree-mode <mode>", "Git worktree mode: off, prompt, require (default: prompt)").option("--keep-alive", "Enable system sleep prevention (default: enabled)").option("--no-keep-alive", "Disable system sleep prevention").option("--setup", "Run interactive setup wizard (reconfigure existing settings)").option("--debug", "Enable debug logging").option("--skip-version-check", "Skip Claude CLI version compatibility check").option("--auto-restart", "Enable auto-restart on updates (default when autoUpdate enabled)").option("--no-auto-restart", "Disable auto-restart on updates").option("--headless", "Run without interactive UI (logs to stdout)").parse();
|
|
79910
80096
|
var opts = program.opts();
|
|
79911
80097
|
var forcedInteractive = !!process.env.CLAUDE_THREADS_INTERACTIVE;
|
|
79912
80098
|
var isHeadless = opts.headless || !forcedInteractive && (!process.stdout.isTTY || !process.stdin.isTTY);
|
|
@@ -79990,6 +80176,10 @@ async function startWithoutDaemon() {
|
|
|
79990
80176
|
if (opts.debug) {
|
|
79991
80177
|
process.env.DEBUG = "1";
|
|
79992
80178
|
}
|
|
80179
|
+
if (opts.permissionMode !== undefined && !["default", "auto", "bypass"].includes(opts.permissionMode)) {
|
|
80180
|
+
console.error(red(` ❌ Invalid --permission-mode: "${opts.permissionMode}". Must be one of: default, auto, bypass.`));
|
|
80181
|
+
process.exit(1);
|
|
80182
|
+
}
|
|
79993
80183
|
const cliArgs = {
|
|
79994
80184
|
url: opts.url,
|
|
79995
80185
|
token: opts.token,
|
|
@@ -79997,6 +80187,7 @@ async function startWithoutDaemon() {
|
|
|
79997
80187
|
botName: opts.botName,
|
|
79998
80188
|
allowedUsers: opts.allowedUsers,
|
|
79999
80189
|
skipPermissions: opts.skipPermissions,
|
|
80190
|
+
permissionMode: opts.permissionMode,
|
|
80000
80191
|
chrome: opts.chrome,
|
|
80001
80192
|
worktreeMode: opts.worktreeMode,
|
|
80002
80193
|
keepAlive: opts.keepAlive
|
|
@@ -80026,7 +80217,10 @@ async function startWithoutDaemon() {
|
|
|
80026
80217
|
}
|
|
80027
80218
|
const config = newConfig;
|
|
80028
80219
|
const firstPlatformConfig = config.platforms[0];
|
|
80029
|
-
const
|
|
80220
|
+
const initialPermissionMode = resolvePermissionMode({
|
|
80221
|
+
permissionMode: cliArgs.permissionMode ?? firstPlatformConfig.permissionMode,
|
|
80222
|
+
skipPermissions: cliArgs.skipPermissions ?? firstPlatformConfig.skipPermissions
|
|
80223
|
+
});
|
|
80030
80224
|
const claudeValidation = validateClaudeCli();
|
|
80031
80225
|
if (!claudeValidation.compatible && !opts.skipVersionCheck) {
|
|
80032
80226
|
console.error(red(` ❌ ${claudeValidation.message}`));
|
|
@@ -80053,8 +80247,10 @@ async function startWithoutDaemon() {
|
|
|
80053
80247
|
process.env.DEBUG = "1";
|
|
80054
80248
|
}
|
|
80055
80249
|
}
|
|
80250
|
+
const restoredPermissionMode = restoredSettings?.permissionMode ?? (restoredSettings?.skipPermissions === true ? "bypass" : restoredSettings?.skipPermissions === false ? "default" : initialPermissionMode);
|
|
80056
80251
|
const runtimeConfig = {
|
|
80057
|
-
|
|
80252
|
+
permissionMode: restoredPermissionMode,
|
|
80253
|
+
skipPermissions: restoredPermissionMode === "bypass",
|
|
80058
80254
|
chromeEnabled: restoredSettings?.chromeEnabled ?? (config.chrome ?? false),
|
|
80059
80255
|
keepAliveEnabled: restoredSettings?.keepAliveEnabled ?? keepAliveEnabled
|
|
80060
80256
|
};
|
|
@@ -80067,7 +80263,7 @@ async function startWithoutDaemon() {
|
|
|
80067
80263
|
workingDir,
|
|
80068
80264
|
claudeVersion: claudeValidation.version || "unknown",
|
|
80069
80265
|
claudeCompatible: claudeValidation.compatible,
|
|
80070
|
-
|
|
80266
|
+
permissionMode: runtimeConfig.permissionMode,
|
|
80071
80267
|
chromeEnabled: runtimeConfig.chromeEnabled,
|
|
80072
80268
|
keepAliveEnabled: runtimeConfig.keepAliveEnabled
|
|
80073
80269
|
},
|
|
@@ -80082,14 +80278,20 @@ async function startWithoutDaemon() {
|
|
|
80082
80278
|
ui.addLog({ level: "info", component: "toggle", message: `Debug mode ${enabled ? "enabled" : "disabled"}` });
|
|
80083
80279
|
sessionManager?.updateAllStickyMessages();
|
|
80084
80280
|
},
|
|
80085
|
-
onPermissionsToggle: (
|
|
80086
|
-
runtimeConfig.
|
|
80087
|
-
saveRuntimeSettings({
|
|
80281
|
+
onPermissionsToggle: (mode) => {
|
|
80282
|
+
runtimeConfig.permissionMode = mode;
|
|
80283
|
+
saveRuntimeSettings({
|
|
80284
|
+
...getRuntimeSettings(),
|
|
80285
|
+
permissionMode: mode,
|
|
80286
|
+
skipPermissions: mode === "bypass"
|
|
80287
|
+
});
|
|
80088
80288
|
for (const platformConfig of config.platforms) {
|
|
80089
|
-
|
|
80289
|
+
const pc = platformConfig;
|
|
80290
|
+
pc.permissionMode = mode;
|
|
80291
|
+
pc.skipPermissions = mode === "bypass";
|
|
80090
80292
|
}
|
|
80091
|
-
sessionManager?.
|
|
80092
|
-
ui.addLog({ level: "info", component: "toggle", message: `
|
|
80293
|
+
sessionManager?.setPermissionMode(mode);
|
|
80294
|
+
ui.addLog({ level: "info", component: "toggle", message: `Permission mode: ${mode}` });
|
|
80093
80295
|
sessionManager?.updateAllStickyMessages();
|
|
80094
80296
|
},
|
|
80095
80297
|
onChromeToggle: (enabled) => {
|
|
@@ -80152,7 +80354,7 @@ async function startWithoutDaemon() {
|
|
|
80152
80354
|
keepAlive.setEnabled(keepAliveEnabled);
|
|
80153
80355
|
const threadLogsEnabled = config.threadLogs?.enabled ?? true;
|
|
80154
80356
|
const threadLogsRetentionDays = config.threadLogs?.retentionDays ?? 30;
|
|
80155
|
-
const session = new SessionManager(workingDir,
|
|
80357
|
+
const session = new SessionManager(workingDir, initialPermissionMode, config.chrome, config.worktreeMode, undefined, threadLogsEnabled, threadLogsRetentionDays, config.limits, config.claudeAccounts);
|
|
80156
80358
|
if (config.stickyMessage) {
|
|
80157
80359
|
session.setStickyMessageCustomization(config.stickyMessage.description, config.stickyMessage.footer);
|
|
80158
80360
|
}
|