cli-remote-agent 1.0.5 → 1.0.6

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/dist/cli.js CHANGED
@@ -496,8 +496,27 @@ var init_claude_live = __esm({
496
496
  });
497
497
 
498
498
  // src/tmux.ts
499
+ function resolveTmuxPath() {
500
+ if (cachedTmuxPath !== void 0) return cachedTmuxPath;
501
+ const dirs = (process.env.PATH || "").split(import_path4.default.delimiter).filter(Boolean);
502
+ for (const extra of ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin", "/opt/local/bin"]) {
503
+ if (!dirs.includes(extra)) dirs.push(extra);
504
+ }
505
+ for (const dir of dirs) {
506
+ const candidate = import_path4.default.join(dir, "tmux");
507
+ try {
508
+ import_fs5.default.accessSync(candidate, import_fs5.default.constants.X_OK);
509
+ cachedTmuxPath = candidate;
510
+ return candidate;
511
+ } catch {
512
+ }
513
+ }
514
+ cachedTmuxPath = null;
515
+ return null;
516
+ }
499
517
  function tmuxCmd(args) {
500
- return IS_WIN ? { file: "wsl.exe", args: ["tmux", ...args] } : { file: "tmux", args };
518
+ if (IS_WIN) return { file: "wsl.exe", args: ["tmux", ...args] };
519
+ return { file: resolveTmuxPath() || "tmux", args };
501
520
  }
502
521
  async function listTmuxSessions() {
503
522
  const fmt = "#{session_name} #{session_windows} #{session_attached} #{session_activity}";
@@ -536,8 +555,8 @@ async function enrichWithPanesAndClaude(sessions2) {
536
555
  const { file, args } = tmuxCmd(["list-panes", "-a", "-F", paneFmt]);
537
556
  const { stdout } = await execFileAsync2(file, args, { timeout: 6e3 });
538
557
  const panes = stdout.split("\n").map((line) => line.replace(/\r$/, "").trim()).filter(Boolean).map((line) => {
539
- const [session, pid, path8, activeFlags] = line.split(" ");
540
- return { session, pid: parseInt(pid, 10) || 0, path: path8 || "", active: activeFlags === "11" };
558
+ const [session, pid, path9, activeFlags] = line.split(" ");
559
+ return { session, pid: parseInt(pid, 10) || 0, path: path9 || "", active: activeFlags === "11" };
541
560
  });
542
561
  const byName = new Map(sessions2.map((s) => [s.name, s]));
543
562
  for (const pane of panes) {
@@ -559,26 +578,31 @@ async function enrichWithPanesAndClaude(sessions2) {
559
578
  } catch {
560
579
  }
561
580
  }
562
- function shq(v) {
563
- return `'${v.replace(/'/g, `'\\''`)}'`;
564
- }
565
- function buildTmuxLaunch(name, launch, shell) {
581
+ function buildTmuxLaunch(name, launch) {
566
582
  const trimmed = launch && launch.trim() ? launch.trim() : void 0;
567
583
  if (IS_WIN) {
568
- const args = ["tmux", "new-session", "-A", "-s", name];
569
- if (trimmed) args.push(trimmed);
570
- return { file: "wsl.exe", args };
571
- }
572
- let cmd2 = `tmux set-option -g aggressive-resize on 2>/dev/null; exec tmux new-session -A -s ${shq(name)}`;
573
- if (trimmed) cmd2 += ` ${shq(trimmed)}`;
574
- return { file: shell, args: ["-l", "-c", cmd2] };
584
+ const args2 = ["tmux", "new-session", "-A", "-s", name];
585
+ if (trimmed) args2.push(trimmed);
586
+ return { file: "wsl.exe", args: args2 };
587
+ }
588
+ const tmuxBin = resolveTmuxPath();
589
+ if (!tmuxBin) return null;
590
+ execFileAsync2(tmuxBin, ["set-option", "-g", "aggressive-resize", "on"], { timeout: 4e3 }).catch(
591
+ () => {
592
+ }
593
+ );
594
+ const args = ["new-session", "-A", "-s", name];
595
+ if (trimmed) args.push(trimmed);
596
+ return { file: tmuxBin, args };
575
597
  }
576
- var import_child_process2, import_util2, execFileAsync2, IS_WIN;
598
+ var import_child_process2, import_util2, import_fs5, import_path4, execFileAsync2, IS_WIN, cachedTmuxPath;
577
599
  var init_tmux = __esm({
578
600
  "src/tmux.ts"() {
579
601
  "use strict";
580
602
  import_child_process2 = require("child_process");
581
603
  import_util2 = require("util");
604
+ import_fs5 = __toESM(require("fs"));
605
+ import_path4 = __toESM(require("path"));
582
606
  init_claude_live();
583
607
  execFileAsync2 = (0, import_util2.promisify)(import_child_process2.execFile);
584
608
  IS_WIN = process.platform === "win32";
@@ -608,15 +632,15 @@ var init_shell = __esm({
608
632
 
609
633
  // src/pty-session.ts
610
634
  function ensureInitFiles() {
611
- const versionFile = (0, import_path4.join)(INIT_DIR, ".version");
612
- if ((0, import_fs5.existsSync)(versionFile)) {
635
+ const versionFile = (0, import_path5.join)(INIT_DIR, ".version");
636
+ if ((0, import_fs6.existsSync)(versionFile)) {
613
637
  try {
614
- if ((0, import_fs5.readFileSync)(versionFile, "utf-8").trim() === INIT_VERSION) return;
638
+ if ((0, import_fs6.readFileSync)(versionFile, "utf-8").trim() === INIT_VERSION) return;
615
639
  } catch {
616
640
  }
617
641
  }
618
- (0, import_fs5.mkdirSync)(ZSH_DIR, { recursive: true });
619
- (0, import_fs5.writeFileSync)(BASH_INIT, [
642
+ (0, import_fs6.mkdirSync)(ZSH_DIR, { recursive: true });
643
+ (0, import_fs6.writeFileSync)(BASH_INIT, [
620
644
  "[ -f ~/.bash_profile ] && source ~/.bash_profile",
621
645
  "[ -f ~/.bashrc ] && source ~/.bashrc",
622
646
  "__crc_s=$SECONDS",
@@ -627,11 +651,11 @@ function ensureInitFiles() {
627
651
  `PROMPT_COMMAND='[ "$__crc_armed" = 1 ] && [ \${__crc_elapsed:-0} -ge ${THRESHOLD} ] && printf "\\a"; __crc_armed=1; eval "$__crc_orig_pc"'`,
628
652
  ""
629
653
  ].join("\n"), "utf-8");
630
- (0, import_fs5.writeFileSync)(ZSH_ENV, [
654
+ (0, import_fs6.writeFileSync)(ZSH_ENV, [
631
655
  '[ -f "${ZDOTDIR_ORIG:-$HOME}/.zshenv" ] && source "${ZDOTDIR_ORIG:-$HOME}/.zshenv"',
632
656
  ""
633
657
  ].join("\n"), "utf-8");
634
- (0, import_fs5.writeFileSync)(ZSH_RC, [
658
+ (0, import_fs6.writeFileSync)(ZSH_RC, [
635
659
  '[ -f "${ZDOTDIR_ORIG:-$HOME}/.zshrc" ] && source "${ZDOTDIR_ORIG:-$HOME}/.zshrc"',
636
660
  "__crc_s=$SECONDS",
637
661
  "__crc_armed=0",
@@ -644,36 +668,36 @@ function ensureInitFiles() {
644
668
  "fi",
645
669
  ""
646
670
  ].join("\n"), "utf-8");
647
- (0, import_fs5.writeFileSync)(versionFile, INIT_VERSION, "utf-8");
671
+ (0, import_fs6.writeFileSync)(versionFile, INIT_VERSION, "utf-8");
648
672
  }
649
673
  function resolveCwd(cwd) {
650
674
  const candidates = process.platform === "win32" ? [cwd, (0, import_os3.homedir)(), process.env.USERPROFILE, process.env.HOME] : [cwd, (0, import_os3.homedir)(), process.env.HOME, process.env.USERPROFILE];
651
675
  for (const c of candidates) {
652
676
  if (c) {
653
677
  try {
654
- if ((0, import_fs5.existsSync)(c)) return c;
678
+ if ((0, import_fs6.existsSync)(c)) return c;
655
679
  } catch {
656
680
  }
657
681
  }
658
682
  }
659
683
  return (0, import_os3.homedir)();
660
684
  }
661
- var pty, import_fs5, import_path4, import_os3, THRESHOLD, INIT_DIR, BASH_INIT, ZSH_DIR, ZSH_RC, ZSH_ENV, INIT_VERSION, PtySession;
685
+ var pty, import_fs6, import_path5, import_os3, THRESHOLD, INIT_DIR, BASH_INIT, ZSH_DIR, ZSH_RC, ZSH_ENV, INIT_VERSION, PtySession;
662
686
  var init_pty_session = __esm({
663
687
  "src/pty-session.ts"() {
664
688
  "use strict";
665
689
  pty = __toESM(require("node-pty"));
666
- import_fs5 = require("fs");
667
- import_path4 = require("path");
690
+ import_fs6 = require("fs");
691
+ import_path5 = require("path");
668
692
  import_os3 = require("os");
669
693
  init_src();
670
694
  init_shell();
671
695
  THRESHOLD = 3;
672
- INIT_DIR = (0, import_path4.join)((0, import_os3.tmpdir)(), "crc-shell-init");
673
- BASH_INIT = (0, import_path4.join)(INIT_DIR, "bashrc");
674
- ZSH_DIR = (0, import_path4.join)(INIT_DIR, "zsh");
675
- ZSH_RC = (0, import_path4.join)(ZSH_DIR, ".zshrc");
676
- ZSH_ENV = (0, import_path4.join)(ZSH_DIR, ".zshenv");
696
+ INIT_DIR = (0, import_path5.join)((0, import_os3.tmpdir)(), "crc-shell-init");
697
+ BASH_INIT = (0, import_path5.join)(INIT_DIR, "bashrc");
698
+ ZSH_DIR = (0, import_path5.join)(INIT_DIR, "zsh");
699
+ ZSH_RC = (0, import_path5.join)(ZSH_DIR, ".zshrc");
700
+ ZSH_ENV = (0, import_path5.join)(ZSH_DIR, ".zshenv");
677
701
  INIT_VERSION = "2";
678
702
  PtySession = class {
679
703
  id;
@@ -903,18 +927,18 @@ function buildHeartbeat(homeDir) {
903
927
  memoryUsage: Math.round((totalMem - freeMem) / totalMem * 100),
904
928
  uptime: Math.round(import_os4.default.uptime()),
905
929
  activeSessions: getActiveSessionCount(),
906
- pathSeparator: import_path5.default.sep,
930
+ pathSeparator: import_path6.default.sep,
907
931
  homeDirectory: homeDir || import_os4.default.homedir(),
908
932
  rootPaths: getRootPaths(),
909
933
  capabilities: { terminal: true, fileTransfer: false }
910
934
  };
911
935
  }
912
- var import_os4, import_path5, import_child_process4, prevCpu;
936
+ var import_os4, import_path6, import_child_process4, prevCpu;
913
937
  var init_heartbeat = __esm({
914
938
  "src/heartbeat.ts"() {
915
939
  "use strict";
916
940
  import_os4 = __toESM(require("os"));
917
- import_path5 = __toESM(require("path"));
941
+ import_path6 = __toESM(require("path"));
918
942
  import_child_process4 = require("child_process");
919
943
  init_terminal_manager();
920
944
  prevCpu = null;
@@ -923,28 +947,28 @@ var init_heartbeat = __esm({
923
947
 
924
948
  // src/file-explorer.ts
925
949
  function isInSecretDir(resolved) {
926
- const rel = import_path6.default.relative(SECRET_DIR, resolved);
927
- return rel === "" || !rel.startsWith(".." + import_path6.default.sep) && rel !== ".." && !import_path6.default.isAbsolute(rel);
950
+ const rel = import_path7.default.relative(SECRET_DIR, resolved);
951
+ return rel === "" || !rel.startsWith(".." + import_path7.default.sep) && rel !== ".." && !import_path7.default.isAbsolute(rel);
928
952
  }
929
953
  function listDirectory(dirPath) {
930
954
  try {
931
- const resolved = import_path6.default.resolve(dirPath);
955
+ const resolved = import_path7.default.resolve(dirPath);
932
956
  if (isInSecretDir(resolved)) {
933
957
  return { entries: [], error: "Access denied" };
934
958
  }
935
- if (!import_fs6.default.existsSync(resolved)) {
959
+ if (!import_fs7.default.existsSync(resolved)) {
936
960
  return { entries: [], error: "Path does not exist" };
937
961
  }
938
- const stat = import_fs6.default.statSync(resolved);
962
+ const stat = import_fs7.default.statSync(resolved);
939
963
  if (!stat.isDirectory()) {
940
964
  return { entries: [], error: "Not a directory" };
941
965
  }
942
- const dirents = import_fs6.default.readdirSync(resolved, { withFileTypes: true });
966
+ const dirents = import_fs7.default.readdirSync(resolved, { withFileTypes: true });
943
967
  const entries = [];
944
968
  for (const dirent of dirents) {
945
969
  try {
946
- const fullPath = import_path6.default.join(resolved, dirent.name);
947
- const s = import_fs6.default.statSync(fullPath);
970
+ const fullPath = import_path7.default.join(resolved, dirent.name);
971
+ const s = import_fs7.default.statSync(fullPath);
948
972
  entries.push({
949
973
  name: dirent.name,
950
974
  isDirectory: dirent.isDirectory(),
@@ -966,22 +990,22 @@ function listDirectory(dirPath) {
966
990
  }
967
991
  async function downloadFile(filePath, serverUrl, secret, agentId) {
968
992
  try {
969
- const resolved = import_path6.default.resolve(filePath);
993
+ const resolved = import_path7.default.resolve(filePath);
970
994
  if (isInSecretDir(resolved)) {
971
995
  return { error: "Access denied" };
972
996
  }
973
- if (!import_fs6.default.existsSync(resolved)) {
997
+ if (!import_fs7.default.existsSync(resolved)) {
974
998
  return { error: "File does not exist" };
975
999
  }
976
- const stat = import_fs6.default.statSync(resolved);
1000
+ const stat = import_fs7.default.statSync(resolved);
977
1001
  if (stat.isDirectory()) {
978
1002
  return { error: "Cannot download a directory" };
979
1003
  }
980
1004
  if (stat.size > FILE_MAX_SIZE) {
981
1005
  return { error: `File too large (max ${FILE_MAX_SIZE} bytes)` };
982
1006
  }
983
- const fileName = import_path6.default.basename(resolved);
984
- const fileStream = import_fs6.default.createReadStream(resolved);
1007
+ const fileName = import_path7.default.basename(resolved);
1008
+ const fileStream = import_fs7.default.createReadStream(resolved);
985
1009
  const baseUrl = serverUrl.replace("wss://", "https://").replace("ws://", "http://");
986
1010
  const url = `${baseUrl}/api/files/receive`;
987
1011
  const res = await fetch(url, {
@@ -1010,21 +1034,21 @@ async function downloadFile(filePath, serverUrl, secret, agentId) {
1010
1034
  return { error: err.message };
1011
1035
  }
1012
1036
  }
1013
- var import_fs6, import_path6, import_os5, SECRET_DIR;
1037
+ var import_fs7, import_path7, import_os5, SECRET_DIR;
1014
1038
  var init_file_explorer = __esm({
1015
1039
  "src/file-explorer.ts"() {
1016
1040
  "use strict";
1017
- import_fs6 = __toESM(require("fs"));
1018
- import_path6 = __toESM(require("path"));
1041
+ import_fs7 = __toESM(require("fs"));
1042
+ import_path7 = __toESM(require("path"));
1019
1043
  import_os5 = __toESM(require("os"));
1020
1044
  init_logger();
1021
1045
  init_src();
1022
- SECRET_DIR = import_path6.default.join(import_os5.default.homedir(), ".crc-agent");
1046
+ SECRET_DIR = import_path7.default.join(import_os5.default.homedir(), ".crc-agent");
1023
1047
  }
1024
1048
  });
1025
1049
 
1026
1050
  // src/vpn-manager.ts
1027
- function shq2(value) {
1051
+ function shq(value) {
1028
1052
  return value.replace(/'/g, "'\\''");
1029
1053
  }
1030
1054
  function psq(value) {
@@ -1042,11 +1066,11 @@ async function runCmd(cmd2, shell) {
1042
1066
  }
1043
1067
  function getConfigPath(profile) {
1044
1068
  const file = profile.configFile || `${profile.id}.conf`;
1045
- if (import_path7.default.isAbsolute(file)) return file;
1046
- return import_path7.default.join(VPN_DIR, file);
1069
+ if (import_path8.default.isAbsolute(file)) return file;
1070
+ return import_path8.default.join(VPN_DIR, file);
1047
1071
  }
1048
1072
  async function getScutilStatus(serviceName) {
1049
- const { stdout } = await runCmd(`scutil --nc status '${shq2(serviceName)}'`);
1073
+ const { stdout } = await runCmd(`scutil --nc status '${shq(serviceName)}'`);
1050
1074
  const firstLine = stdout.trim().split("\n")[0];
1051
1075
  if (firstLine === "Connected") return "connected";
1052
1076
  if (firstLine === "Connecting") return "connecting";
@@ -1054,11 +1078,11 @@ async function getScutilStatus(serviceName) {
1054
1078
  return "disconnected";
1055
1079
  }
1056
1080
  async function scutilStart(serviceName) {
1057
- const { stderr } = await runCmd(`scutil --nc start '${shq2(serviceName)}'`);
1081
+ const { stderr } = await runCmd(`scutil --nc start '${shq(serviceName)}'`);
1058
1082
  return stderr || null;
1059
1083
  }
1060
1084
  async function scutilStop(serviceName) {
1061
- const { stderr } = await runCmd(`scutil --nc stop '${shq2(serviceName)}'`);
1085
+ const { stderr } = await runCmd(`scutil --nc stop '${shq(serviceName)}'`);
1062
1086
  return stderr || null;
1063
1087
  }
1064
1088
  async function runOsascript(script) {
@@ -1107,7 +1131,7 @@ async function getWireGuardStatus(profile) {
1107
1131
  return getScutilStatus(profile.serviceName);
1108
1132
  }
1109
1133
  const tunnelName = profile.tunnelName || profile.id;
1110
- const { stdout } = await runCmd(`wg show '${shq2(tunnelName)}' 2>/dev/null`);
1134
+ const { stdout } = await runCmd(`wg show '${shq(tunnelName)}' 2>/dev/null`);
1111
1135
  return stdout.trim() ? "connected" : "disconnected";
1112
1136
  }
1113
1137
  async function getOpenVpnStatus(profile) {
@@ -1159,7 +1183,7 @@ async function connectWireGuard(profile) {
1159
1183
  const tunnelName = profile.tunnelName || profile.id;
1160
1184
  if (isWindows) {
1161
1185
  const confPath2 = getConfigPath(profile);
1162
- if (!import_fs7.default.existsSync(confPath2)) {
1186
+ if (!import_fs8.default.existsSync(confPath2)) {
1163
1187
  return `Config file not found: ${confPath2}`;
1164
1188
  }
1165
1189
  const script = `
@@ -1179,13 +1203,13 @@ async function connectWireGuard(profile) {
1179
1203
  return scutilStart(profile.serviceName);
1180
1204
  }
1181
1205
  const confPath = getConfigPath(profile);
1182
- const { stderr } = await runCmd(`sudo wg-quick up '${shq2(confPath)}'`);
1206
+ const { stderr } = await runCmd(`sudo wg-quick up '${shq(confPath)}'`);
1183
1207
  return stderr || null;
1184
1208
  }
1185
1209
  async function connectOpenVpn(profile) {
1186
1210
  if (isWindows) {
1187
1211
  const confPath2 = getConfigPath(profile);
1188
- if (!import_fs7.default.existsSync(confPath2)) {
1212
+ if (!import_fs8.default.existsSync(confPath2)) {
1189
1213
  return `Config file not found: ${confPath2}`;
1190
1214
  }
1191
1215
  const connector = "C:\\Program Files\\OpenVPN Connect\\ovpnconnector.exe";
@@ -1212,16 +1236,16 @@ async function connectOpenVpn(profile) {
1212
1236
  return scutilStart(profile.serviceName);
1213
1237
  }
1214
1238
  const confPath = getConfigPath(profile);
1215
- if (!import_fs7.default.existsSync(confPath)) {
1239
+ if (!import_fs8.default.existsSync(confPath)) {
1216
1240
  return `Config file not found: ${confPath}`;
1217
1241
  }
1218
- const { stderr } = await runCmd(`sudo openvpn --config '${shq2(confPath)}' --daemon`);
1242
+ const { stderr } = await runCmd(`sudo openvpn --config '${shq(confPath)}' --daemon`);
1219
1243
  return stderr || null;
1220
1244
  }
1221
1245
  async function connectAzure(profile) {
1222
1246
  if (isWindows) {
1223
1247
  const confPath = getConfigPath(profile);
1224
- if (!import_fs7.default.existsSync(confPath)) {
1248
+ if (!import_fs8.default.existsSync(confPath)) {
1225
1249
  return `Config file not found: ${confPath}`;
1226
1250
  }
1227
1251
  await runCmd(`& 'AzureVpn.exe' -i '${psq(confPath)}' 2>&1`);
@@ -1265,7 +1289,7 @@ async function disconnectWireGuard(profile) {
1265
1289
  return scutilStop(profile.serviceName);
1266
1290
  }
1267
1291
  const confPath = getConfigPath(profile);
1268
- const { stderr } = await runCmd(`sudo wg-quick down '${shq2(confPath)}'`);
1292
+ const { stderr } = await runCmd(`sudo wg-quick down '${shq(confPath)}'`);
1269
1293
  return stderr || null;
1270
1294
  }
1271
1295
  async function disconnectOpenVpn(profile) {
@@ -1341,19 +1365,19 @@ async function disconnectVpn(configs, profileId) {
1341
1365
  }
1342
1366
  return profiles;
1343
1367
  }
1344
- var import_child_process5, import_util3, import_fs7, import_path7, import_os6, execAsync, isWindows, VPN_DIR;
1368
+ var import_child_process5, import_util3, import_fs8, import_path8, import_os6, execAsync, isWindows, VPN_DIR;
1345
1369
  var init_vpn_manager = __esm({
1346
1370
  "src/vpn-manager.ts"() {
1347
1371
  "use strict";
1348
1372
  import_child_process5 = require("child_process");
1349
1373
  import_util3 = require("util");
1350
- import_fs7 = __toESM(require("fs"));
1351
- import_path7 = __toESM(require("path"));
1374
+ import_fs8 = __toESM(require("fs"));
1375
+ import_path8 = __toESM(require("path"));
1352
1376
  import_os6 = __toESM(require("os"));
1353
1377
  init_logger();
1354
1378
  execAsync = (0, import_util3.promisify)(import_child_process5.exec);
1355
1379
  isWindows = process.platform === "win32";
1356
- VPN_DIR = import_path7.default.join(import_os6.default.homedir(), ".crc-agent", "vpn");
1380
+ VPN_DIR = import_path8.default.join(import_os6.default.homedir(), ".crc-agent", "vpn");
1357
1381
  }
1358
1382
  });
1359
1383
 
@@ -1388,7 +1412,7 @@ var init_claude_path = __esm({
1388
1412
  // src/claude-sessions.ts
1389
1413
  async function parseSessionFile(filePath) {
1390
1414
  try {
1391
- const stream = import_fs8.default.createReadStream(filePath, { encoding: "utf-8" });
1415
+ const stream = import_fs9.default.createReadStream(filePath, { encoding: "utf-8" });
1392
1416
  const rl = import_readline2.default.createInterface({ input: stream, crlfDelay: Infinity });
1393
1417
  let firstMessage = "";
1394
1418
  let lastTimestamp = "";
@@ -1436,17 +1460,17 @@ async function parseSessionFile(filePath) {
1436
1460
  }
1437
1461
  async function listClaudeSessions(filterProjectPath) {
1438
1462
  const sessions2 = [];
1439
- if (!import_fs8.default.existsSync(CLAUDE_PROJECTS_DIR)) return sessions2;
1463
+ if (!import_fs9.default.existsSync(CLAUDE_PROJECTS_DIR)) return sessions2;
1440
1464
  const isDirSafe = (d) => {
1441
1465
  try {
1442
- return import_fs8.default.statSync(import_path8.default.join(CLAUDE_PROJECTS_DIR, d)).isDirectory();
1466
+ return import_fs9.default.statSync(import_path9.default.join(CLAUDE_PROJECTS_DIR, d)).isDirectory();
1443
1467
  } catch {
1444
1468
  return false;
1445
1469
  }
1446
1470
  };
1447
1471
  let allEntries;
1448
1472
  try {
1449
- allEntries = import_fs8.default.readdirSync(CLAUDE_PROJECTS_DIR);
1473
+ allEntries = import_fs9.default.readdirSync(CLAUDE_PROJECTS_DIR);
1450
1474
  } catch {
1451
1475
  return sessions2;
1452
1476
  }
@@ -1459,16 +1483,16 @@ async function listClaudeSessions(filterProjectPath) {
1459
1483
  projectDirs = allEntries.filter((d) => isDirSafe(d) && !d.startsWith("-private-"));
1460
1484
  }
1461
1485
  for (const dirName of projectDirs) {
1462
- const dirPath = import_path8.default.join(CLAUDE_PROJECTS_DIR, dirName);
1486
+ const dirPath = import_path9.default.join(CLAUDE_PROJECTS_DIR, dirName);
1463
1487
  let files;
1464
1488
  try {
1465
- files = import_fs8.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
1489
+ files = import_fs9.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
1466
1490
  } catch {
1467
1491
  continue;
1468
1492
  }
1469
1493
  for (const file of files) {
1470
1494
  const sessionId = file.replace(".jsonl", "");
1471
- const parsed = await parseSessionFile(import_path8.default.join(dirPath, file));
1495
+ const parsed = await parseSessionFile(import_path9.default.join(dirPath, file));
1472
1496
  if (!parsed) continue;
1473
1497
  const projectPath = parsed.cwd || dirName.replace(/^-/, "/").replace(/-/g, "/");
1474
1498
  sessions2.push({
@@ -1486,31 +1510,31 @@ async function listClaudeSessions(filterProjectPath) {
1486
1510
  sessions2.sort((a, b) => b.lastTimestamp > a.lastTimestamp ? 1 : -1);
1487
1511
  return sessions2;
1488
1512
  }
1489
- var import_fs8, import_path8, import_os7, import_readline2, CLAUDE_PROJECTS_DIR;
1513
+ var import_fs9, import_path9, import_os7, import_readline2, CLAUDE_PROJECTS_DIR;
1490
1514
  var init_claude_sessions = __esm({
1491
1515
  "src/claude-sessions.ts"() {
1492
1516
  "use strict";
1493
- import_fs8 = __toESM(require("fs"));
1494
- import_path8 = __toESM(require("path"));
1517
+ import_fs9 = __toESM(require("fs"));
1518
+ import_path9 = __toESM(require("path"));
1495
1519
  import_os7 = __toESM(require("os"));
1496
1520
  import_readline2 = __toESM(require("readline"));
1497
1521
  init_logger();
1498
1522
  init_claude_path();
1499
- CLAUDE_PROJECTS_DIR = import_path8.default.join(import_os7.default.homedir(), ".claude", "projects");
1523
+ CLAUDE_PROJECTS_DIR = import_path9.default.join(import_os7.default.homedir(), ".claude", "projects");
1500
1524
  }
1501
1525
  });
1502
1526
 
1503
1527
  // src/claude-conversation.ts
1504
1528
  function findProjectDir(projectPath) {
1505
1529
  const encoded = encodeProjectPath(projectPath);
1506
- const dirPath = import_path9.default.join(CLAUDE_PROJECTS_DIR2, encoded);
1507
- if (import_fs9.default.existsSync(dirPath)) return dirPath;
1508
- if (!import_fs9.default.existsSync(CLAUDE_PROJECTS_DIR2)) return null;
1530
+ const dirPath = import_path10.default.join(CLAUDE_PROJECTS_DIR2, encoded);
1531
+ if (import_fs10.default.existsSync(dirPath)) return dirPath;
1532
+ if (!import_fs10.default.existsSync(CLAUDE_PROJECTS_DIR2)) return null;
1509
1533
  let allDirs;
1510
1534
  try {
1511
- allDirs = import_fs9.default.readdirSync(CLAUDE_PROJECTS_DIR2).filter((d) => {
1535
+ allDirs = import_fs10.default.readdirSync(CLAUDE_PROJECTS_DIR2).filter((d) => {
1512
1536
  try {
1513
- return import_fs9.default.statSync(import_path9.default.join(CLAUDE_PROJECTS_DIR2, d)).isDirectory();
1537
+ return import_fs10.default.statSync(import_path10.default.join(CLAUDE_PROJECTS_DIR2, d)).isDirectory();
1514
1538
  } catch {
1515
1539
  return false;
1516
1540
  }
@@ -1520,23 +1544,23 @@ function findProjectDir(projectPath) {
1520
1544
  }
1521
1545
  const matches = findProjectDirs(allDirs, projectPath);
1522
1546
  if (matches.length > 0) {
1523
- return import_path9.default.join(CLAUDE_PROJECTS_DIR2, matches[0]);
1547
+ return import_path10.default.join(CLAUDE_PROJECTS_DIR2, matches[0]);
1524
1548
  }
1525
1549
  return null;
1526
1550
  }
1527
1551
  function findSessionFileById(sessionId) {
1528
1552
  if (!/^[A-Za-z0-9._-]+$/.test(sessionId)) return null;
1529
- if (!import_fs9.default.existsSync(CLAUDE_PROJECTS_DIR2)) return null;
1553
+ if (!import_fs10.default.existsSync(CLAUDE_PROJECTS_DIR2)) return null;
1530
1554
  let dirs;
1531
1555
  try {
1532
- dirs = import_fs9.default.readdirSync(CLAUDE_PROJECTS_DIR2);
1556
+ dirs = import_fs10.default.readdirSync(CLAUDE_PROJECTS_DIR2);
1533
1557
  } catch {
1534
1558
  return null;
1535
1559
  }
1536
1560
  for (const dir of dirs) {
1537
- const candidate = import_path9.default.join(CLAUDE_PROJECTS_DIR2, dir, `${sessionId}.jsonl`);
1561
+ const candidate = import_path10.default.join(CLAUDE_PROJECTS_DIR2, dir, `${sessionId}.jsonl`);
1538
1562
  try {
1539
- if (import_fs9.default.statSync(candidate).isFile()) return candidate;
1563
+ if (import_fs10.default.statSync(candidate).isFile()) return candidate;
1540
1564
  } catch {
1541
1565
  }
1542
1566
  }
@@ -1547,20 +1571,20 @@ function findLatestSessionFile(projectPath) {
1547
1571
  if (!dirPath) return null;
1548
1572
  let entries;
1549
1573
  try {
1550
- entries = import_fs9.default.readdirSync(dirPath);
1574
+ entries = import_fs10.default.readdirSync(dirPath);
1551
1575
  } catch {
1552
1576
  return null;
1553
1577
  }
1554
1578
  const files = entries.filter((f) => f.endsWith(".jsonl") && !f.includes("/")).map((f) => {
1555
1579
  try {
1556
- return { name: f, mtime: import_fs9.default.statSync(import_path9.default.join(dirPath, f)).mtimeMs };
1580
+ return { name: f, mtime: import_fs10.default.statSync(import_path10.default.join(dirPath, f)).mtimeMs };
1557
1581
  } catch {
1558
1582
  return null;
1559
1583
  }
1560
1584
  }).filter((f) => f !== null).sort((a, b) => b.mtime - a.mtime);
1561
1585
  if (files.length === 0) return null;
1562
1586
  return {
1563
- filePath: import_path9.default.join(dirPath, files[0].name),
1587
+ filePath: import_path10.default.join(dirPath, files[0].name),
1564
1588
  sessionId: files[0].name.replace(".jsonl", "")
1565
1589
  };
1566
1590
  }
@@ -1570,8 +1594,8 @@ async function readConversation(projectPath, afterLine = 0, specificSessionId) {
1570
1594
  if (specificSessionId) {
1571
1595
  sessionId = specificSessionId;
1572
1596
  const dirPath = findProjectDir(projectPath);
1573
- const direct = dirPath ? import_path9.default.join(dirPath, `${specificSessionId}.jsonl`) : null;
1574
- if (direct && import_fs9.default.existsSync(direct)) {
1597
+ const direct = dirPath ? import_path10.default.join(dirPath, `${specificSessionId}.jsonl`) : null;
1598
+ if (direct && import_fs10.default.existsSync(direct)) {
1575
1599
  filePath = direct;
1576
1600
  } else {
1577
1601
  const byId = findSessionFileById(specificSessionId);
@@ -1585,7 +1609,7 @@ async function readConversation(projectPath, afterLine = 0, specificSessionId) {
1585
1609
  sessionId = found.sessionId;
1586
1610
  }
1587
1611
  try {
1588
- const stream = import_fs9.default.createReadStream(filePath, { encoding: "utf-8" });
1612
+ const stream = import_fs10.default.createReadStream(filePath, { encoding: "utf-8" });
1589
1613
  const rl = import_readline3.default.createInterface({ input: stream, crlfDelay: Infinity });
1590
1614
  const messages = [];
1591
1615
  let lineNum = 0;
@@ -1666,17 +1690,17 @@ async function readConversation(projectPath, afterLine = 0, specificSessionId) {
1666
1690
  return null;
1667
1691
  }
1668
1692
  }
1669
- var import_fs9, import_path9, import_os8, import_readline3, CLAUDE_PROJECTS_DIR2;
1693
+ var import_fs10, import_path10, import_os8, import_readline3, CLAUDE_PROJECTS_DIR2;
1670
1694
  var init_claude_conversation = __esm({
1671
1695
  "src/claude-conversation.ts"() {
1672
1696
  "use strict";
1673
- import_fs9 = __toESM(require("fs"));
1674
- import_path9 = __toESM(require("path"));
1697
+ import_fs10 = __toESM(require("fs"));
1698
+ import_path10 = __toESM(require("path"));
1675
1699
  import_os8 = __toESM(require("os"));
1676
1700
  import_readline3 = __toESM(require("readline"));
1677
1701
  init_logger();
1678
1702
  init_claude_path();
1679
- CLAUDE_PROJECTS_DIR2 = import_path9.default.join(import_os8.default.homedir(), ".claude", "projects");
1703
+ CLAUDE_PROJECTS_DIR2 = import_path10.default.join(import_os8.default.homedir(), ".claude", "projects");
1680
1704
  }
1681
1705
  });
1682
1706
 
@@ -1693,7 +1717,6 @@ var init_index = __esm({
1693
1717
  init_claude_plugin_installer();
1694
1718
  init_local_control();
1695
1719
  init_tmux();
1696
- init_shell();
1697
1720
  init_heartbeat();
1698
1721
  init_file_explorer();
1699
1722
  init_vpn_manager();
@@ -1765,7 +1788,18 @@ var init_index = __esm({
1765
1788
  socket.on(TERMINAL_OPEN, (payload) => {
1766
1789
  const { sessionId, cols, rows, tmux, launch } = payload;
1767
1790
  if (!sessionId) return;
1768
- const launchSpec = tmux ? buildTmuxLaunch(tmux, launch, detectShell(config.shell)) : void 0;
1791
+ let launchSpec;
1792
+ if (tmux) {
1793
+ const spec = buildTmuxLaunch(tmux, launch);
1794
+ if (!spec) {
1795
+ const msg = "\r\n\x1B[31mtmux is not installed on this machine.\x1B[0m\r\nInstall it to use tmux mirroring:\r\n macOS: brew install tmux\r\n Linux: sudo apt install tmux (or your package manager)\r\n\r\n";
1796
+ socket.emit(TERMINAL_OUTPUT, { sessionId, data: msg });
1797
+ socket.emit(TERMINAL_EXIT, { sessionId, exitCode: 1 });
1798
+ logger.warn({ sessionId, tmux }, "tmux launch requested but tmux not found");
1799
+ return;
1800
+ }
1801
+ launchSpec = spec;
1802
+ }
1769
1803
  createTerminalSession(
1770
1804
  sessionId,
1771
1805
  cols,