cli-remote-agent 1.0.2 → 1.0.4

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
@@ -204,7 +204,7 @@ var init_types = __esm({
204
204
  });
205
205
 
206
206
  // ../shared/src/protocol.ts
207
- var AGENT_HEARTBEAT, TERMINAL_OUTPUT, TERMINAL_EXIT, TERMINAL_OPEN, TERMINAL_INPUT, TERMINAL_RESIZE, TERMINAL_CLOSE, SESSION_ATTACH, SESSION_DETACH, SESSION_BUFFER, SESSION_SYNC, SESSION_SYNC_RESULT, VPN_LIST, VPN_CONNECT, VPN_DISCONNECT, VPN_UPDATE, FILES_LIST, FILES_DOWNLOAD, FILES_LIST_RESULT, FILES_DOWNLOAD_READY, FILES_DOWNLOAD_ERROR, AGENT_EXEC, AGENT_EXEC_RESULT, CLAUDE_CONV_READ, CLAUDE_CONV_DATA, CLAUDE_SESSIONS_LIST, CLAUDE_SESSIONS_RESULT, CLAUDE_HOOK, TMUX_LIST, TMUX_LIST_RESULT;
207
+ var AGENT_HEARTBEAT, TERMINAL_OUTPUT, TERMINAL_EXIT, TERMINAL_OPEN, TERMINAL_INPUT, TERMINAL_RESIZE, TERMINAL_CLOSE, SESSION_ATTACH, SESSION_DETACH, SESSION_BUFFER, SESSION_SYNC, SESSION_SYNC_RESULT, VPN_LIST, VPN_CONNECT, VPN_DISCONNECT, VPN_UPDATE, FILES_LIST, FILES_DOWNLOAD, FILES_LIST_RESULT, FILES_DOWNLOAD_READY, FILES_DOWNLOAD_ERROR, AGENT_EXEC, AGENT_EXEC_RESULT, CLAUDE_CONV_READ, CLAUDE_CONV_DATA, CLAUDE_SESSIONS_LIST, CLAUDE_SESSIONS_RESULT, CLAUDE_HOOK, TMUX_LIST, TMUX_LIST_RESULT, TMUX_KILL, TMUX_KILL_RESULT;
208
208
  var init_protocol = __esm({
209
209
  "../shared/src/protocol.ts"() {
210
210
  "use strict";
@@ -238,6 +238,8 @@ var init_protocol = __esm({
238
238
  CLAUDE_HOOK = "claude:hook";
239
239
  TMUX_LIST = "tmux:list";
240
240
  TMUX_LIST_RESULT = "tmux:list:result";
241
+ TMUX_KILL = "tmux:kill";
242
+ TMUX_KILL_RESULT = "tmux:kill:result";
241
243
  }
242
244
  });
243
245
 
@@ -414,6 +416,74 @@ var init_local_control = __esm({
414
416
  }
415
417
  });
416
418
 
419
+ // src/claude-live.ts
420
+ function isAlive(pid) {
421
+ try {
422
+ process.kill(pid, 0);
423
+ return true;
424
+ } catch {
425
+ return false;
426
+ }
427
+ }
428
+ function getLiveClaudeSessions() {
429
+ let files;
430
+ try {
431
+ files = import_fs4.default.readdirSync(LIVE_SESSIONS_DIR).filter((f) => f.endsWith(".json"));
432
+ } catch {
433
+ return [];
434
+ }
435
+ const out = [];
436
+ for (const f of files) {
437
+ try {
438
+ const obj = JSON.parse(import_fs4.default.readFileSync(import_path3.default.join(LIVE_SESSIONS_DIR, f), "utf-8"));
439
+ if (typeof obj?.pid !== "number" || !isAlive(obj.pid)) continue;
440
+ out.push({
441
+ pid: obj.pid,
442
+ cwd: typeof obj.cwd === "string" ? obj.cwd : "",
443
+ name: typeof obj.name === "string" ? obj.name : void 0,
444
+ status: typeof obj.status === "string" ? obj.status : void 0,
445
+ updatedAt: typeof obj.updatedAt === "number" ? obj.updatedAt : void 0
446
+ });
447
+ } catch {
448
+ }
449
+ }
450
+ out.sort((a, b) => (a.updatedAt || 0) - (b.updatedAt || 0));
451
+ return out;
452
+ }
453
+ async function getPidParents() {
454
+ const map = /* @__PURE__ */ new Map();
455
+ try {
456
+ const { stdout } = await execFileAsync("ps", ["-axo", "pid=,ppid="], { timeout: 5e3 });
457
+ for (const line of stdout.split("\n")) {
458
+ const m = line.trim().match(/^(\d+)\s+(\d+)$/);
459
+ if (m) map.set(parseInt(m[1], 10), parseInt(m[2], 10));
460
+ }
461
+ } catch {
462
+ }
463
+ return map;
464
+ }
465
+ function isDescendantOf(pid, ancestor, parents) {
466
+ let cur = pid;
467
+ for (let i = 0; i < 25 && cur !== void 0 && cur > 1; i++) {
468
+ if (cur === ancestor) return true;
469
+ cur = parents.get(cur);
470
+ }
471
+ return false;
472
+ }
473
+ var import_fs4, import_path3, import_os2, import_child_process, import_util, execFileAsync, LIVE_SESSIONS_DIR;
474
+ var init_claude_live = __esm({
475
+ "src/claude-live.ts"() {
476
+ "use strict";
477
+ import_fs4 = __toESM(require("fs"));
478
+ import_path3 = __toESM(require("path"));
479
+ import_os2 = __toESM(require("os"));
480
+ import_child_process = require("child_process");
481
+ import_util = require("util");
482
+ execFileAsync = (0, import_util.promisify)(import_child_process.execFile);
483
+ LIVE_SESSIONS_DIR = import_path3.default.join(import_os2.default.homedir(), ".claude", "sessions");
484
+ }
485
+ });
486
+
417
487
  // src/tmux.ts
418
488
  function tmuxCmd(args) {
419
489
  return IS_WIN ? { file: "wsl.exe", args: ["tmux", ...args] } : { file: "tmux", args };
@@ -422,8 +492,8 @@ async function listTmuxSessions() {
422
492
  const fmt = "#{session_name} #{session_windows} #{session_attached} #{session_activity}";
423
493
  const { file, args } = tmuxCmd(["list-sessions", "-F", fmt]);
424
494
  try {
425
- const { stdout } = await execFileAsync(file, args, { timeout: 6e3 });
426
- return stdout.split("\n").map((line) => line.replace(/\r$/, "").trim()).filter(Boolean).map((line) => {
495
+ const { stdout } = await execFileAsync2(file, args, { timeout: 6e3 });
496
+ const sessions2 = stdout.split("\n").map((line) => line.replace(/\r$/, "").trim()).filter(Boolean).map((line) => {
427
497
  const [name, windows, attached, activity] = line.split(" ");
428
498
  return {
429
499
  name,
@@ -432,10 +502,52 @@ async function listTmuxSessions() {
432
502
  activity: parseInt(activity, 10) || void 0
433
503
  };
434
504
  });
505
+ await enrichWithPanesAndClaude(sessions2);
506
+ return sessions2;
435
507
  } catch {
436
508
  return [];
437
509
  }
438
510
  }
511
+ async function killTmuxSession(name) {
512
+ const { file, args } = tmuxCmd(["kill-session", "-t", `=${name}`]);
513
+ try {
514
+ await execFileAsync2(file, args, { timeout: 6e3 });
515
+ return { ok: true };
516
+ } catch (err) {
517
+ const msg = (err?.stderr || err?.message || "tmux kill failed").toString().trim();
518
+ return { ok: false, error: msg };
519
+ }
520
+ }
521
+ async function enrichWithPanesAndClaude(sessions2) {
522
+ if (IS_WIN || sessions2.length === 0) return;
523
+ try {
524
+ const paneFmt = "#{session_name} #{pane_pid} #{pane_current_path} #{window_active}#{pane_active}";
525
+ const { file, args } = tmuxCmd(["list-panes", "-a", "-F", paneFmt]);
526
+ const { stdout } = await execFileAsync2(file, args, { timeout: 6e3 });
527
+ const panes = stdout.split("\n").map((line) => line.replace(/\r$/, "").trim()).filter(Boolean).map((line) => {
528
+ const [session, pid, path8, activeFlags] = line.split(" ");
529
+ return { session, pid: parseInt(pid, 10) || 0, path: path8 || "", active: activeFlags === "11" };
530
+ });
531
+ const byName = new Map(sessions2.map((s) => [s.name, s]));
532
+ for (const pane of panes) {
533
+ const s = byName.get(pane.session);
534
+ if (s && (!s.path || pane.active)) s.path = pane.path || s.path;
535
+ }
536
+ const live = getLiveClaudeSessions();
537
+ if (live.length === 0) return;
538
+ const parents = await getPidParents();
539
+ for (const cs of live) {
540
+ const target = parents.size > 0 ? panes.find((p) => p.pid > 0 && isDescendantOf(cs.pid, p.pid, parents)) : panes.find((p) => cs.cwd && p.path === cs.cwd);
541
+ if (!target) continue;
542
+ const s = byName.get(target.session);
543
+ if (!s) continue;
544
+ s.claudeTitle = cs.name;
545
+ s.claudeStatus = cs.status;
546
+ if (cs.cwd) s.path = cs.cwd;
547
+ }
548
+ } catch {
549
+ }
550
+ }
439
551
  function shq(v) {
440
552
  return `'${v.replace(/'/g, `'\\''`)}'`;
441
553
  }
@@ -450,13 +562,14 @@ function buildTmuxLaunch(name, launch, shell) {
450
562
  if (trimmed) cmd2 += ` ${shq(trimmed)}`;
451
563
  return { file: shell, args: ["-l", "-c", cmd2] };
452
564
  }
453
- var import_child_process, import_util, execFileAsync, IS_WIN;
565
+ var import_child_process2, import_util2, execFileAsync2, IS_WIN;
454
566
  var init_tmux = __esm({
455
567
  "src/tmux.ts"() {
456
568
  "use strict";
457
- import_child_process = require("child_process");
458
- import_util = require("util");
459
- execFileAsync = (0, import_util.promisify)(import_child_process.execFile);
569
+ import_child_process2 = require("child_process");
570
+ import_util2 = require("util");
571
+ init_claude_live();
572
+ execFileAsync2 = (0, import_util2.promisify)(import_child_process2.execFile);
460
573
  IS_WIN = process.platform === "win32";
461
574
  }
462
575
  });
@@ -466,7 +579,7 @@ function detectShell(preference) {
466
579
  if (preference !== "auto") return preference;
467
580
  if (process.platform === "win32") {
468
581
  try {
469
- (0, import_child_process2.execSync)("where pwsh", { stdio: "ignore" });
582
+ (0, import_child_process3.execSync)("where pwsh", { stdio: "ignore" });
470
583
  return "pwsh.exe";
471
584
  } catch {
472
585
  return "powershell.exe";
@@ -474,25 +587,25 @@ function detectShell(preference) {
474
587
  }
475
588
  return process.env.SHELL || "/bin/bash";
476
589
  }
477
- var import_child_process2;
590
+ var import_child_process3;
478
591
  var init_shell = __esm({
479
592
  "src/shell.ts"() {
480
593
  "use strict";
481
- import_child_process2 = require("child_process");
594
+ import_child_process3 = require("child_process");
482
595
  }
483
596
  });
484
597
 
485
598
  // src/pty-session.ts
486
599
  function ensureInitFiles() {
487
- const versionFile = (0, import_path3.join)(INIT_DIR, ".version");
488
- if ((0, import_fs4.existsSync)(versionFile)) {
600
+ const versionFile = (0, import_path4.join)(INIT_DIR, ".version");
601
+ if ((0, import_fs5.existsSync)(versionFile)) {
489
602
  try {
490
- if ((0, import_fs4.readFileSync)(versionFile, "utf-8").trim() === INIT_VERSION) return;
603
+ if ((0, import_fs5.readFileSync)(versionFile, "utf-8").trim() === INIT_VERSION) return;
491
604
  } catch {
492
605
  }
493
606
  }
494
- (0, import_fs4.mkdirSync)(ZSH_DIR, { recursive: true });
495
- (0, import_fs4.writeFileSync)(BASH_INIT, [
607
+ (0, import_fs5.mkdirSync)(ZSH_DIR, { recursive: true });
608
+ (0, import_fs5.writeFileSync)(BASH_INIT, [
496
609
  "[ -f ~/.bash_profile ] && source ~/.bash_profile",
497
610
  "[ -f ~/.bashrc ] && source ~/.bashrc",
498
611
  "__crc_s=$SECONDS",
@@ -503,11 +616,11 @@ function ensureInitFiles() {
503
616
  `PROMPT_COMMAND='[ "$__crc_armed" = 1 ] && [ \${__crc_elapsed:-0} -ge ${THRESHOLD} ] && printf "\\a"; __crc_armed=1; eval "$__crc_orig_pc"'`,
504
617
  ""
505
618
  ].join("\n"), "utf-8");
506
- (0, import_fs4.writeFileSync)(ZSH_ENV, [
619
+ (0, import_fs5.writeFileSync)(ZSH_ENV, [
507
620
  '[ -f "${ZDOTDIR_ORIG:-$HOME}/.zshenv" ] && source "${ZDOTDIR_ORIG:-$HOME}/.zshenv"',
508
621
  ""
509
622
  ].join("\n"), "utf-8");
510
- (0, import_fs4.writeFileSync)(ZSH_RC, [
623
+ (0, import_fs5.writeFileSync)(ZSH_RC, [
511
624
  '[ -f "${ZDOTDIR_ORIG:-$HOME}/.zshrc" ] && source "${ZDOTDIR_ORIG:-$HOME}/.zshrc"',
512
625
  "__crc_s=$SECONDS",
513
626
  "__crc_armed=0",
@@ -520,36 +633,36 @@ function ensureInitFiles() {
520
633
  "fi",
521
634
  ""
522
635
  ].join("\n"), "utf-8");
523
- (0, import_fs4.writeFileSync)(versionFile, INIT_VERSION, "utf-8");
636
+ (0, import_fs5.writeFileSync)(versionFile, INIT_VERSION, "utf-8");
524
637
  }
525
638
  function resolveCwd(cwd) {
526
- const candidates = process.platform === "win32" ? [cwd, (0, import_os2.homedir)(), process.env.USERPROFILE, process.env.HOME] : [cwd, (0, import_os2.homedir)(), process.env.HOME, process.env.USERPROFILE];
639
+ 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];
527
640
  for (const c of candidates) {
528
641
  if (c) {
529
642
  try {
530
- if ((0, import_fs4.existsSync)(c)) return c;
643
+ if ((0, import_fs5.existsSync)(c)) return c;
531
644
  } catch {
532
645
  }
533
646
  }
534
647
  }
535
- return (0, import_os2.homedir)();
648
+ return (0, import_os3.homedir)();
536
649
  }
537
- var pty, import_fs4, import_path3, import_os2, THRESHOLD, INIT_DIR, BASH_INIT, ZSH_DIR, ZSH_RC, ZSH_ENV, INIT_VERSION, PtySession;
650
+ var pty, import_fs5, import_path4, import_os3, THRESHOLD, INIT_DIR, BASH_INIT, ZSH_DIR, ZSH_RC, ZSH_ENV, INIT_VERSION, PtySession;
538
651
  var init_pty_session = __esm({
539
652
  "src/pty-session.ts"() {
540
653
  "use strict";
541
654
  pty = __toESM(require("node-pty"));
542
- import_fs4 = require("fs");
543
- import_path3 = require("path");
544
- import_os2 = require("os");
655
+ import_fs5 = require("fs");
656
+ import_path4 = require("path");
657
+ import_os3 = require("os");
545
658
  init_src();
546
659
  init_shell();
547
660
  THRESHOLD = 3;
548
- INIT_DIR = (0, import_path3.join)((0, import_os2.tmpdir)(), "crc-shell-init");
549
- BASH_INIT = (0, import_path3.join)(INIT_DIR, "bashrc");
550
- ZSH_DIR = (0, import_path3.join)(INIT_DIR, "zsh");
551
- ZSH_RC = (0, import_path3.join)(ZSH_DIR, ".zshrc");
552
- ZSH_ENV = (0, import_path3.join)(ZSH_DIR, ".zshenv");
661
+ INIT_DIR = (0, import_path4.join)((0, import_os3.tmpdir)(), "crc-shell-init");
662
+ BASH_INIT = (0, import_path4.join)(INIT_DIR, "bashrc");
663
+ ZSH_DIR = (0, import_path4.join)(INIT_DIR, "zsh");
664
+ ZSH_RC = (0, import_path4.join)(ZSH_DIR, ".zshrc");
665
+ ZSH_ENV = (0, import_path4.join)(ZSH_DIR, ".zshenv");
553
666
  INIT_VERSION = "2";
554
667
  PtySession = class {
555
668
  id;
@@ -593,7 +706,7 @@ var init_pty_session = __esm({
593
706
  name: "xterm-256color",
594
707
  cols,
595
708
  rows,
596
- cwd: (0, import_os2.homedir)(),
709
+ cwd: (0, import_os3.homedir)(),
597
710
  env
598
711
  });
599
712
  }
@@ -740,7 +853,7 @@ var init_terminal_manager = __esm({
740
853
 
741
854
  // src/heartbeat.ts
742
855
  function getCpuUsage() {
743
- const cpus = import_os3.default.cpus();
856
+ const cpus = import_os4.default.cpus();
744
857
  let idle = 0;
745
858
  let total = 0;
746
859
  for (const cpu of cpus) {
@@ -760,7 +873,7 @@ function getCpuUsage() {
760
873
  function getRootPaths() {
761
874
  if (process.platform === "win32") {
762
875
  try {
763
- const output = (0, import_child_process3.execSync)("wmic logicaldisk get name", { encoding: "utf-8" });
876
+ const output = (0, import_child_process4.execSync)("wmic logicaldisk get name", { encoding: "utf-8" });
764
877
  return output.split("\n").map((l) => l.trim()).filter((l) => /^[A-Z]:$/.test(l)).map((l) => l + "\\");
765
878
  } catch {
766
879
  return ["C:\\"];
@@ -769,29 +882,29 @@ function getRootPaths() {
769
882
  return ["/"];
770
883
  }
771
884
  function buildHeartbeat(homeDir) {
772
- const totalMem = import_os3.default.totalmem();
773
- const freeMem = import_os3.default.freemem();
885
+ const totalMem = import_os4.default.totalmem();
886
+ const freeMem = import_os4.default.freemem();
774
887
  return {
775
- hostname: import_os3.default.hostname(),
888
+ hostname: import_os4.default.hostname(),
776
889
  platform: process.platform,
777
- arch: import_os3.default.arch(),
890
+ arch: import_os4.default.arch(),
778
891
  cpuUsage: getCpuUsage(),
779
892
  memoryUsage: Math.round((totalMem - freeMem) / totalMem * 100),
780
- uptime: Math.round(import_os3.default.uptime()),
893
+ uptime: Math.round(import_os4.default.uptime()),
781
894
  activeSessions: getActiveSessionCount(),
782
- pathSeparator: import_path4.default.sep,
783
- homeDirectory: homeDir || import_os3.default.homedir(),
895
+ pathSeparator: import_path5.default.sep,
896
+ homeDirectory: homeDir || import_os4.default.homedir(),
784
897
  rootPaths: getRootPaths(),
785
898
  capabilities: { terminal: true, fileTransfer: false }
786
899
  };
787
900
  }
788
- var import_os3, import_path4, import_child_process3, prevCpu;
901
+ var import_os4, import_path5, import_child_process4, prevCpu;
789
902
  var init_heartbeat = __esm({
790
903
  "src/heartbeat.ts"() {
791
904
  "use strict";
792
- import_os3 = __toESM(require("os"));
793
- import_path4 = __toESM(require("path"));
794
- import_child_process3 = require("child_process");
905
+ import_os4 = __toESM(require("os"));
906
+ import_path5 = __toESM(require("path"));
907
+ import_child_process4 = require("child_process");
795
908
  init_terminal_manager();
796
909
  prevCpu = null;
797
910
  }
@@ -799,28 +912,28 @@ var init_heartbeat = __esm({
799
912
 
800
913
  // src/file-explorer.ts
801
914
  function isInSecretDir(resolved) {
802
- const rel = import_path5.default.relative(SECRET_DIR, resolved);
803
- return rel === "" || !rel.startsWith(".." + import_path5.default.sep) && rel !== ".." && !import_path5.default.isAbsolute(rel);
915
+ const rel = import_path6.default.relative(SECRET_DIR, resolved);
916
+ return rel === "" || !rel.startsWith(".." + import_path6.default.sep) && rel !== ".." && !import_path6.default.isAbsolute(rel);
804
917
  }
805
918
  function listDirectory(dirPath) {
806
919
  try {
807
- const resolved = import_path5.default.resolve(dirPath);
920
+ const resolved = import_path6.default.resolve(dirPath);
808
921
  if (isInSecretDir(resolved)) {
809
922
  return { entries: [], error: "Access denied" };
810
923
  }
811
- if (!import_fs5.default.existsSync(resolved)) {
924
+ if (!import_fs6.default.existsSync(resolved)) {
812
925
  return { entries: [], error: "Path does not exist" };
813
926
  }
814
- const stat = import_fs5.default.statSync(resolved);
927
+ const stat = import_fs6.default.statSync(resolved);
815
928
  if (!stat.isDirectory()) {
816
929
  return { entries: [], error: "Not a directory" };
817
930
  }
818
- const dirents = import_fs5.default.readdirSync(resolved, { withFileTypes: true });
931
+ const dirents = import_fs6.default.readdirSync(resolved, { withFileTypes: true });
819
932
  const entries = [];
820
933
  for (const dirent of dirents) {
821
934
  try {
822
- const fullPath = import_path5.default.join(resolved, dirent.name);
823
- const s = import_fs5.default.statSync(fullPath);
935
+ const fullPath = import_path6.default.join(resolved, dirent.name);
936
+ const s = import_fs6.default.statSync(fullPath);
824
937
  entries.push({
825
938
  name: dirent.name,
826
939
  isDirectory: dirent.isDirectory(),
@@ -842,22 +955,22 @@ function listDirectory(dirPath) {
842
955
  }
843
956
  async function downloadFile(filePath, serverUrl, secret, agentId) {
844
957
  try {
845
- const resolved = import_path5.default.resolve(filePath);
958
+ const resolved = import_path6.default.resolve(filePath);
846
959
  if (isInSecretDir(resolved)) {
847
960
  return { error: "Access denied" };
848
961
  }
849
- if (!import_fs5.default.existsSync(resolved)) {
962
+ if (!import_fs6.default.existsSync(resolved)) {
850
963
  return { error: "File does not exist" };
851
964
  }
852
- const stat = import_fs5.default.statSync(resolved);
965
+ const stat = import_fs6.default.statSync(resolved);
853
966
  if (stat.isDirectory()) {
854
967
  return { error: "Cannot download a directory" };
855
968
  }
856
969
  if (stat.size > FILE_MAX_SIZE) {
857
970
  return { error: `File too large (max ${FILE_MAX_SIZE} bytes)` };
858
971
  }
859
- const fileName = import_path5.default.basename(resolved);
860
- const fileStream = import_fs5.default.createReadStream(resolved);
972
+ const fileName = import_path6.default.basename(resolved);
973
+ const fileStream = import_fs6.default.createReadStream(resolved);
861
974
  const baseUrl = serverUrl.replace("wss://", "https://").replace("ws://", "http://");
862
975
  const url = `${baseUrl}/api/files/receive`;
863
976
  const res = await fetch(url, {
@@ -886,16 +999,16 @@ async function downloadFile(filePath, serverUrl, secret, agentId) {
886
999
  return { error: err.message };
887
1000
  }
888
1001
  }
889
- var import_fs5, import_path5, import_os4, SECRET_DIR;
1002
+ var import_fs6, import_path6, import_os5, SECRET_DIR;
890
1003
  var init_file_explorer = __esm({
891
1004
  "src/file-explorer.ts"() {
892
1005
  "use strict";
893
- import_fs5 = __toESM(require("fs"));
894
- import_path5 = __toESM(require("path"));
895
- import_os4 = __toESM(require("os"));
1006
+ import_fs6 = __toESM(require("fs"));
1007
+ import_path6 = __toESM(require("path"));
1008
+ import_os5 = __toESM(require("os"));
896
1009
  init_logger();
897
1010
  init_src();
898
- SECRET_DIR = import_path5.default.join(import_os4.default.homedir(), ".crc-agent");
1011
+ SECRET_DIR = import_path6.default.join(import_os5.default.homedir(), ".crc-agent");
899
1012
  }
900
1013
  });
901
1014
 
@@ -918,8 +1031,8 @@ async function runCmd(cmd2, shell) {
918
1031
  }
919
1032
  function getConfigPath(profile) {
920
1033
  const file = profile.configFile || `${profile.id}.conf`;
921
- if (import_path6.default.isAbsolute(file)) return file;
922
- return import_path6.default.join(VPN_DIR, file);
1034
+ if (import_path7.default.isAbsolute(file)) return file;
1035
+ return import_path7.default.join(VPN_DIR, file);
923
1036
  }
924
1037
  async function getScutilStatus(serviceName) {
925
1038
  const { stdout } = await runCmd(`scutil --nc status '${shq2(serviceName)}'`);
@@ -1035,7 +1148,7 @@ async function connectWireGuard(profile) {
1035
1148
  const tunnelName = profile.tunnelName || profile.id;
1036
1149
  if (isWindows) {
1037
1150
  const confPath2 = getConfigPath(profile);
1038
- if (!import_fs6.default.existsSync(confPath2)) {
1151
+ if (!import_fs7.default.existsSync(confPath2)) {
1039
1152
  return `Config file not found: ${confPath2}`;
1040
1153
  }
1041
1154
  const script = `
@@ -1061,7 +1174,7 @@ async function connectWireGuard(profile) {
1061
1174
  async function connectOpenVpn(profile) {
1062
1175
  if (isWindows) {
1063
1176
  const confPath2 = getConfigPath(profile);
1064
- if (!import_fs6.default.existsSync(confPath2)) {
1177
+ if (!import_fs7.default.existsSync(confPath2)) {
1065
1178
  return `Config file not found: ${confPath2}`;
1066
1179
  }
1067
1180
  const connector = "C:\\Program Files\\OpenVPN Connect\\ovpnconnector.exe";
@@ -1088,7 +1201,7 @@ async function connectOpenVpn(profile) {
1088
1201
  return scutilStart(profile.serviceName);
1089
1202
  }
1090
1203
  const confPath = getConfigPath(profile);
1091
- if (!import_fs6.default.existsSync(confPath)) {
1204
+ if (!import_fs7.default.existsSync(confPath)) {
1092
1205
  return `Config file not found: ${confPath}`;
1093
1206
  }
1094
1207
  const { stderr } = await runCmd(`sudo openvpn --config '${shq2(confPath)}' --daemon`);
@@ -1097,7 +1210,7 @@ async function connectOpenVpn(profile) {
1097
1210
  async function connectAzure(profile) {
1098
1211
  if (isWindows) {
1099
1212
  const confPath = getConfigPath(profile);
1100
- if (!import_fs6.default.existsSync(confPath)) {
1213
+ if (!import_fs7.default.existsSync(confPath)) {
1101
1214
  return `Config file not found: ${confPath}`;
1102
1215
  }
1103
1216
  await runCmd(`& 'AzureVpn.exe' -i '${psq(confPath)}' 2>&1`);
@@ -1217,19 +1330,19 @@ async function disconnectVpn(configs, profileId) {
1217
1330
  }
1218
1331
  return profiles;
1219
1332
  }
1220
- var import_child_process4, import_util2, import_fs6, import_path6, import_os5, execAsync, isWindows, VPN_DIR;
1333
+ var import_child_process5, import_util3, import_fs7, import_path7, import_os6, execAsync, isWindows, VPN_DIR;
1221
1334
  var init_vpn_manager = __esm({
1222
1335
  "src/vpn-manager.ts"() {
1223
1336
  "use strict";
1224
- import_child_process4 = require("child_process");
1225
- import_util2 = require("util");
1226
- import_fs6 = __toESM(require("fs"));
1227
- import_path6 = __toESM(require("path"));
1228
- import_os5 = __toESM(require("os"));
1337
+ import_child_process5 = require("child_process");
1338
+ import_util3 = require("util");
1339
+ import_fs7 = __toESM(require("fs"));
1340
+ import_path7 = __toESM(require("path"));
1341
+ import_os6 = __toESM(require("os"));
1229
1342
  init_logger();
1230
- execAsync = (0, import_util2.promisify)(import_child_process4.exec);
1343
+ execAsync = (0, import_util3.promisify)(import_child_process5.exec);
1231
1344
  isWindows = process.platform === "win32";
1232
- VPN_DIR = import_path6.default.join(import_os5.default.homedir(), ".crc-agent", "vpn");
1345
+ VPN_DIR = import_path7.default.join(import_os6.default.homedir(), ".crc-agent", "vpn");
1233
1346
  }
1234
1347
  });
1235
1348
 
@@ -1264,7 +1377,7 @@ var init_claude_path = __esm({
1264
1377
  // src/claude-sessions.ts
1265
1378
  async function parseSessionFile(filePath) {
1266
1379
  try {
1267
- const stream = import_fs7.default.createReadStream(filePath, { encoding: "utf-8" });
1380
+ const stream = import_fs8.default.createReadStream(filePath, { encoding: "utf-8" });
1268
1381
  const rl = import_readline2.default.createInterface({ input: stream, crlfDelay: Infinity });
1269
1382
  let firstMessage = "";
1270
1383
  let lastTimestamp = "";
@@ -1312,17 +1425,17 @@ async function parseSessionFile(filePath) {
1312
1425
  }
1313
1426
  async function listClaudeSessions(filterProjectPath) {
1314
1427
  const sessions2 = [];
1315
- if (!import_fs7.default.existsSync(CLAUDE_PROJECTS_DIR)) return sessions2;
1428
+ if (!import_fs8.default.existsSync(CLAUDE_PROJECTS_DIR)) return sessions2;
1316
1429
  const isDirSafe = (d) => {
1317
1430
  try {
1318
- return import_fs7.default.statSync(import_path7.default.join(CLAUDE_PROJECTS_DIR, d)).isDirectory();
1431
+ return import_fs8.default.statSync(import_path8.default.join(CLAUDE_PROJECTS_DIR, d)).isDirectory();
1319
1432
  } catch {
1320
1433
  return false;
1321
1434
  }
1322
1435
  };
1323
1436
  let allEntries;
1324
1437
  try {
1325
- allEntries = import_fs7.default.readdirSync(CLAUDE_PROJECTS_DIR);
1438
+ allEntries = import_fs8.default.readdirSync(CLAUDE_PROJECTS_DIR);
1326
1439
  } catch {
1327
1440
  return sessions2;
1328
1441
  }
@@ -1335,16 +1448,16 @@ async function listClaudeSessions(filterProjectPath) {
1335
1448
  projectDirs = allEntries.filter((d) => isDirSafe(d) && !d.startsWith("-private-"));
1336
1449
  }
1337
1450
  for (const dirName of projectDirs) {
1338
- const dirPath = import_path7.default.join(CLAUDE_PROJECTS_DIR, dirName);
1451
+ const dirPath = import_path8.default.join(CLAUDE_PROJECTS_DIR, dirName);
1339
1452
  let files;
1340
1453
  try {
1341
- files = import_fs7.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
1454
+ files = import_fs8.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
1342
1455
  } catch {
1343
1456
  continue;
1344
1457
  }
1345
1458
  for (const file of files) {
1346
1459
  const sessionId = file.replace(".jsonl", "");
1347
- const parsed = await parseSessionFile(import_path7.default.join(dirPath, file));
1460
+ const parsed = await parseSessionFile(import_path8.default.join(dirPath, file));
1348
1461
  if (!parsed) continue;
1349
1462
  const projectPath = parsed.cwd || dirName.replace(/^-/, "/").replace(/-/g, "/");
1350
1463
  sessions2.push({
@@ -1362,31 +1475,31 @@ async function listClaudeSessions(filterProjectPath) {
1362
1475
  sessions2.sort((a, b) => b.lastTimestamp > a.lastTimestamp ? 1 : -1);
1363
1476
  return sessions2;
1364
1477
  }
1365
- var import_fs7, import_path7, import_os6, import_readline2, CLAUDE_PROJECTS_DIR;
1478
+ var import_fs8, import_path8, import_os7, import_readline2, CLAUDE_PROJECTS_DIR;
1366
1479
  var init_claude_sessions = __esm({
1367
1480
  "src/claude-sessions.ts"() {
1368
1481
  "use strict";
1369
- import_fs7 = __toESM(require("fs"));
1370
- import_path7 = __toESM(require("path"));
1371
- import_os6 = __toESM(require("os"));
1482
+ import_fs8 = __toESM(require("fs"));
1483
+ import_path8 = __toESM(require("path"));
1484
+ import_os7 = __toESM(require("os"));
1372
1485
  import_readline2 = __toESM(require("readline"));
1373
1486
  init_logger();
1374
1487
  init_claude_path();
1375
- CLAUDE_PROJECTS_DIR = import_path7.default.join(import_os6.default.homedir(), ".claude", "projects");
1488
+ CLAUDE_PROJECTS_DIR = import_path8.default.join(import_os7.default.homedir(), ".claude", "projects");
1376
1489
  }
1377
1490
  });
1378
1491
 
1379
1492
  // src/claude-conversation.ts
1380
1493
  function findProjectDir(projectPath) {
1381
1494
  const encoded = encodeProjectPath(projectPath);
1382
- const dirPath = import_path8.default.join(CLAUDE_PROJECTS_DIR2, encoded);
1383
- if (import_fs8.default.existsSync(dirPath)) return dirPath;
1384
- if (!import_fs8.default.existsSync(CLAUDE_PROJECTS_DIR2)) return null;
1495
+ const dirPath = import_path9.default.join(CLAUDE_PROJECTS_DIR2, encoded);
1496
+ if (import_fs9.default.existsSync(dirPath)) return dirPath;
1497
+ if (!import_fs9.default.existsSync(CLAUDE_PROJECTS_DIR2)) return null;
1385
1498
  let allDirs;
1386
1499
  try {
1387
- allDirs = import_fs8.default.readdirSync(CLAUDE_PROJECTS_DIR2).filter((d) => {
1500
+ allDirs = import_fs9.default.readdirSync(CLAUDE_PROJECTS_DIR2).filter((d) => {
1388
1501
  try {
1389
- return import_fs8.default.statSync(import_path8.default.join(CLAUDE_PROJECTS_DIR2, d)).isDirectory();
1502
+ return import_fs9.default.statSync(import_path9.default.join(CLAUDE_PROJECTS_DIR2, d)).isDirectory();
1390
1503
  } catch {
1391
1504
  return false;
1392
1505
  }
@@ -1396,23 +1509,23 @@ function findProjectDir(projectPath) {
1396
1509
  }
1397
1510
  const matches = findProjectDirs(allDirs, projectPath);
1398
1511
  if (matches.length > 0) {
1399
- return import_path8.default.join(CLAUDE_PROJECTS_DIR2, matches[0]);
1512
+ return import_path9.default.join(CLAUDE_PROJECTS_DIR2, matches[0]);
1400
1513
  }
1401
1514
  return null;
1402
1515
  }
1403
1516
  function findSessionFileById(sessionId) {
1404
1517
  if (!/^[A-Za-z0-9._-]+$/.test(sessionId)) return null;
1405
- if (!import_fs8.default.existsSync(CLAUDE_PROJECTS_DIR2)) return null;
1518
+ if (!import_fs9.default.existsSync(CLAUDE_PROJECTS_DIR2)) return null;
1406
1519
  let dirs;
1407
1520
  try {
1408
- dirs = import_fs8.default.readdirSync(CLAUDE_PROJECTS_DIR2);
1521
+ dirs = import_fs9.default.readdirSync(CLAUDE_PROJECTS_DIR2);
1409
1522
  } catch {
1410
1523
  return null;
1411
1524
  }
1412
1525
  for (const dir of dirs) {
1413
- const candidate = import_path8.default.join(CLAUDE_PROJECTS_DIR2, dir, `${sessionId}.jsonl`);
1526
+ const candidate = import_path9.default.join(CLAUDE_PROJECTS_DIR2, dir, `${sessionId}.jsonl`);
1414
1527
  try {
1415
- if (import_fs8.default.statSync(candidate).isFile()) return candidate;
1528
+ if (import_fs9.default.statSync(candidate).isFile()) return candidate;
1416
1529
  } catch {
1417
1530
  }
1418
1531
  }
@@ -1423,20 +1536,20 @@ function findLatestSessionFile(projectPath) {
1423
1536
  if (!dirPath) return null;
1424
1537
  let entries;
1425
1538
  try {
1426
- entries = import_fs8.default.readdirSync(dirPath);
1539
+ entries = import_fs9.default.readdirSync(dirPath);
1427
1540
  } catch {
1428
1541
  return null;
1429
1542
  }
1430
1543
  const files = entries.filter((f) => f.endsWith(".jsonl") && !f.includes("/")).map((f) => {
1431
1544
  try {
1432
- return { name: f, mtime: import_fs8.default.statSync(import_path8.default.join(dirPath, f)).mtimeMs };
1545
+ return { name: f, mtime: import_fs9.default.statSync(import_path9.default.join(dirPath, f)).mtimeMs };
1433
1546
  } catch {
1434
1547
  return null;
1435
1548
  }
1436
1549
  }).filter((f) => f !== null).sort((a, b) => b.mtime - a.mtime);
1437
1550
  if (files.length === 0) return null;
1438
1551
  return {
1439
- filePath: import_path8.default.join(dirPath, files[0].name),
1552
+ filePath: import_path9.default.join(dirPath, files[0].name),
1440
1553
  sessionId: files[0].name.replace(".jsonl", "")
1441
1554
  };
1442
1555
  }
@@ -1446,8 +1559,8 @@ async function readConversation(projectPath, afterLine = 0, specificSessionId) {
1446
1559
  if (specificSessionId) {
1447
1560
  sessionId = specificSessionId;
1448
1561
  const dirPath = findProjectDir(projectPath);
1449
- const direct = dirPath ? import_path8.default.join(dirPath, `${specificSessionId}.jsonl`) : null;
1450
- if (direct && import_fs8.default.existsSync(direct)) {
1562
+ const direct = dirPath ? import_path9.default.join(dirPath, `${specificSessionId}.jsonl`) : null;
1563
+ if (direct && import_fs9.default.existsSync(direct)) {
1451
1564
  filePath = direct;
1452
1565
  } else {
1453
1566
  const byId = findSessionFileById(specificSessionId);
@@ -1461,7 +1574,7 @@ async function readConversation(projectPath, afterLine = 0, specificSessionId) {
1461
1574
  sessionId = found.sessionId;
1462
1575
  }
1463
1576
  try {
1464
- const stream = import_fs8.default.createReadStream(filePath, { encoding: "utf-8" });
1577
+ const stream = import_fs9.default.createReadStream(filePath, { encoding: "utf-8" });
1465
1578
  const rl = import_readline3.default.createInterface({ input: stream, crlfDelay: Infinity });
1466
1579
  const messages = [];
1467
1580
  let lineNum = 0;
@@ -1542,17 +1655,17 @@ async function readConversation(projectPath, afterLine = 0, specificSessionId) {
1542
1655
  return null;
1543
1656
  }
1544
1657
  }
1545
- var import_fs8, import_path8, import_os7, import_readline3, CLAUDE_PROJECTS_DIR2;
1658
+ var import_fs9, import_path9, import_os8, import_readline3, CLAUDE_PROJECTS_DIR2;
1546
1659
  var init_claude_conversation = __esm({
1547
1660
  "src/claude-conversation.ts"() {
1548
1661
  "use strict";
1549
- import_fs8 = __toESM(require("fs"));
1550
- import_path8 = __toESM(require("path"));
1551
- import_os7 = __toESM(require("os"));
1662
+ import_fs9 = __toESM(require("fs"));
1663
+ import_path9 = __toESM(require("path"));
1664
+ import_os8 = __toESM(require("os"));
1552
1665
  import_readline3 = __toESM(require("readline"));
1553
1666
  init_logger();
1554
1667
  init_claude_path();
1555
- CLAUDE_PROJECTS_DIR2 = import_path8.default.join(import_os7.default.homedir(), ".claude", "projects");
1668
+ CLAUDE_PROJECTS_DIR2 = import_path9.default.join(import_os8.default.homedir(), ".claude", "projects");
1556
1669
  }
1557
1670
  });
1558
1671
 
@@ -1667,6 +1780,13 @@ var init_index = __esm({
1667
1780
  socket.emit(TMUX_LIST_RESULT, { requestId, sessions: [], error: err?.message || "tmux list failed" });
1668
1781
  }
1669
1782
  });
1783
+ socket.on(TMUX_KILL, async (payload) => {
1784
+ const { requestId, name } = payload;
1785
+ if (!requestId || !name) return;
1786
+ const result = await killTmuxSession(name);
1787
+ logger.info({ name, ok: result.ok, error: result.error }, "tmux kill requested");
1788
+ socket.emit(TMUX_KILL_RESULT, { requestId, name, ok: result.ok, error: result.error });
1789
+ });
1670
1790
  socket.on(TERMINAL_INPUT, (payload) => {
1671
1791
  writeToSession(payload.sessionId, payload.data);
1672
1792
  });
@@ -1735,8 +1855,8 @@ var init_index = __esm({
1735
1855
  const { requestId, command, cwd } = payload;
1736
1856
  if (!requestId) return;
1737
1857
  const { exec: exec2 } = await import("child_process");
1738
- const { promisify: promisify3 } = await import("util");
1739
- const execAsync2 = promisify3(exec2);
1858
+ const { promisify: promisify4 } = await import("util");
1859
+ const execAsync2 = promisify4(exec2);
1740
1860
  try {
1741
1861
  const { stdout, stderr } = await execAsync2(command, { cwd, timeout: 3e4 });
1742
1862
  socket.emit(AGENT_EXEC_RESULT, { requestId, stdout: stdout || "", stderr: stderr || "" });