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 +231 -111
- package/dist/cli.js.map +1 -1
- package/dist/index.js +214 -99
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
426
|
-
|
|
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
|
|
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
|
-
|
|
458
|
-
|
|
459
|
-
|
|
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,
|
|
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
|
|
590
|
+
var import_child_process3;
|
|
478
591
|
var init_shell = __esm({
|
|
479
592
|
"src/shell.ts"() {
|
|
480
593
|
"use strict";
|
|
481
|
-
|
|
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,
|
|
488
|
-
if ((0,
|
|
600
|
+
const versionFile = (0, import_path4.join)(INIT_DIR, ".version");
|
|
601
|
+
if ((0, import_fs5.existsSync)(versionFile)) {
|
|
489
602
|
try {
|
|
490
|
-
if ((0,
|
|
603
|
+
if ((0, import_fs5.readFileSync)(versionFile, "utf-8").trim() === INIT_VERSION) return;
|
|
491
604
|
} catch {
|
|
492
605
|
}
|
|
493
606
|
}
|
|
494
|
-
(0,
|
|
495
|
-
(0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
643
|
+
if ((0, import_fs5.existsSync)(c)) return c;
|
|
531
644
|
} catch {
|
|
532
645
|
}
|
|
533
646
|
}
|
|
534
647
|
}
|
|
535
|
-
return (0,
|
|
648
|
+
return (0, import_os3.homedir)();
|
|
536
649
|
}
|
|
537
|
-
var pty,
|
|
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
|
-
|
|
543
|
-
|
|
544
|
-
|
|
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,
|
|
549
|
-
BASH_INIT = (0,
|
|
550
|
-
ZSH_DIR = (0,
|
|
551
|
-
ZSH_RC = (0,
|
|
552
|
-
ZSH_ENV = (0,
|
|
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,
|
|
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 =
|
|
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,
|
|
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 =
|
|
773
|
-
const freeMem =
|
|
885
|
+
const totalMem = import_os4.default.totalmem();
|
|
886
|
+
const freeMem = import_os4.default.freemem();
|
|
774
887
|
return {
|
|
775
|
-
hostname:
|
|
888
|
+
hostname: import_os4.default.hostname(),
|
|
776
889
|
platform: process.platform,
|
|
777
|
-
arch:
|
|
890
|
+
arch: import_os4.default.arch(),
|
|
778
891
|
cpuUsage: getCpuUsage(),
|
|
779
892
|
memoryUsage: Math.round((totalMem - freeMem) / totalMem * 100),
|
|
780
|
-
uptime: Math.round(
|
|
893
|
+
uptime: Math.round(import_os4.default.uptime()),
|
|
781
894
|
activeSessions: getActiveSessionCount(),
|
|
782
|
-
pathSeparator:
|
|
783
|
-
homeDirectory: 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
|
|
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
|
-
|
|
793
|
-
|
|
794
|
-
|
|
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 =
|
|
803
|
-
return rel === "" || !rel.startsWith(".." +
|
|
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 =
|
|
920
|
+
const resolved = import_path6.default.resolve(dirPath);
|
|
808
921
|
if (isInSecretDir(resolved)) {
|
|
809
922
|
return { entries: [], error: "Access denied" };
|
|
810
923
|
}
|
|
811
|
-
if (!
|
|
924
|
+
if (!import_fs6.default.existsSync(resolved)) {
|
|
812
925
|
return { entries: [], error: "Path does not exist" };
|
|
813
926
|
}
|
|
814
|
-
const stat =
|
|
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 =
|
|
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 =
|
|
823
|
-
const s =
|
|
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 =
|
|
958
|
+
const resolved = import_path6.default.resolve(filePath);
|
|
846
959
|
if (isInSecretDir(resolved)) {
|
|
847
960
|
return { error: "Access denied" };
|
|
848
961
|
}
|
|
849
|
-
if (!
|
|
962
|
+
if (!import_fs6.default.existsSync(resolved)) {
|
|
850
963
|
return { error: "File does not exist" };
|
|
851
964
|
}
|
|
852
|
-
const stat =
|
|
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 =
|
|
860
|
-
const fileStream =
|
|
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
|
|
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
|
-
|
|
894
|
-
|
|
895
|
-
|
|
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 =
|
|
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 (
|
|
922
|
-
return
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
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,
|
|
1343
|
+
execAsync = (0, import_util3.promisify)(import_child_process5.exec);
|
|
1231
1344
|
isWindows = process.platform === "win32";
|
|
1232
|
-
VPN_DIR =
|
|
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 =
|
|
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 (!
|
|
1428
|
+
if (!import_fs8.default.existsSync(CLAUDE_PROJECTS_DIR)) return sessions2;
|
|
1316
1429
|
const isDirSafe = (d) => {
|
|
1317
1430
|
try {
|
|
1318
|
-
return
|
|
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 =
|
|
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 =
|
|
1451
|
+
const dirPath = import_path8.default.join(CLAUDE_PROJECTS_DIR, dirName);
|
|
1339
1452
|
let files;
|
|
1340
1453
|
try {
|
|
1341
|
-
files =
|
|
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(
|
|
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
|
|
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
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
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 =
|
|
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 =
|
|
1383
|
-
if (
|
|
1384
|
-
if (!
|
|
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 =
|
|
1500
|
+
allDirs = import_fs9.default.readdirSync(CLAUDE_PROJECTS_DIR2).filter((d) => {
|
|
1388
1501
|
try {
|
|
1389
|
-
return
|
|
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
|
|
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 (!
|
|
1518
|
+
if (!import_fs9.default.existsSync(CLAUDE_PROJECTS_DIR2)) return null;
|
|
1406
1519
|
let dirs;
|
|
1407
1520
|
try {
|
|
1408
|
-
dirs =
|
|
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 =
|
|
1526
|
+
const candidate = import_path9.default.join(CLAUDE_PROJECTS_DIR2, dir, `${sessionId}.jsonl`);
|
|
1414
1527
|
try {
|
|
1415
|
-
if (
|
|
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 =
|
|
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:
|
|
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:
|
|
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 ?
|
|
1450
|
-
if (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 =
|
|
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
|
|
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
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
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 =
|
|
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:
|
|
1739
|
-
const execAsync2 =
|
|
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 || "" });
|