cli-remote-agent 1.0.6 → 1.0.8
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 +213 -104
- package/dist/cli.js.map +1 -1
- package/dist/index.js +186 -94
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -427,6 +427,105 @@ var init_local_control = __esm({
|
|
|
427
427
|
}
|
|
428
428
|
});
|
|
429
429
|
|
|
430
|
+
// src/pty-helper-fix.ts
|
|
431
|
+
function ensurePtyHelperExecutable() {
|
|
432
|
+
if (process.platform === "win32") return;
|
|
433
|
+
try {
|
|
434
|
+
const nodeRequire = (0, import_node_module.createRequire)(__filename);
|
|
435
|
+
const ptyMain = nodeRequire.resolve("node-pty");
|
|
436
|
+
const ptyDir = import_path3.default.dirname(import_path3.default.dirname(ptyMain));
|
|
437
|
+
const candidates = [import_path3.default.join(ptyDir, "build", "Release", "spawn-helper")];
|
|
438
|
+
const prebuilds = import_path3.default.join(ptyDir, "prebuilds");
|
|
439
|
+
try {
|
|
440
|
+
for (const d of import_fs4.default.readdirSync(prebuilds)) {
|
|
441
|
+
candidates.push(import_path3.default.join(prebuilds, d, "spawn-helper"));
|
|
442
|
+
}
|
|
443
|
+
} catch {
|
|
444
|
+
}
|
|
445
|
+
for (const file of candidates) {
|
|
446
|
+
try {
|
|
447
|
+
const mode = import_fs4.default.statSync(file).mode;
|
|
448
|
+
if (!(mode & 73)) {
|
|
449
|
+
import_fs4.default.chmodSync(file, mode | 493);
|
|
450
|
+
logger.info({ file }, "Restored execute bit on node-pty spawn-helper");
|
|
451
|
+
}
|
|
452
|
+
} catch {
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
if (process.platform === "darwin") {
|
|
456
|
+
(0, import_node_child_process.execFile)("xattr", ["-dr", "com.apple.quarantine", ptyDir], () => {
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
} catch (err) {
|
|
460
|
+
logger.warn({ error: err?.message }, "Could not verify node-pty spawn-helper permissions");
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
var import_node_module, import_node_child_process, import_fs4, import_path3;
|
|
464
|
+
var init_pty_helper_fix = __esm({
|
|
465
|
+
"src/pty-helper-fix.ts"() {
|
|
466
|
+
"use strict";
|
|
467
|
+
import_node_module = require("module");
|
|
468
|
+
import_node_child_process = require("child_process");
|
|
469
|
+
import_fs4 = __toESM(require("fs"));
|
|
470
|
+
import_path3 = __toESM(require("path"));
|
|
471
|
+
init_logger();
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
// src/single-instance.ts
|
|
476
|
+
function isRunningAgent(pid) {
|
|
477
|
+
try {
|
|
478
|
+
process.kill(pid, 0);
|
|
479
|
+
} catch {
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
if (process.platform === "win32") return true;
|
|
483
|
+
try {
|
|
484
|
+
const out = (0, import_child_process.execFileSync)("ps", ["-p", String(pid), "-o", "command="], { encoding: "utf8" });
|
|
485
|
+
return /crc-agent|cli-remote-agent|agent[\/\\](dist|src)[\/\\](cli|index)/.test(out);
|
|
486
|
+
} catch {
|
|
487
|
+
return true;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
function acquireSingleInstanceLock() {
|
|
491
|
+
if (process.env.CRC_ALLOW_MULTIPLE === "1") return { ok: true };
|
|
492
|
+
try {
|
|
493
|
+
if (import_fs5.default.existsSync(LOCK_FILE)) {
|
|
494
|
+
const prev = parseInt(import_fs5.default.readFileSync(LOCK_FILE, "utf8").trim(), 10);
|
|
495
|
+
if (prev && prev !== process.pid && isRunningAgent(prev)) {
|
|
496
|
+
return { ok: false, pid: prev };
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
import_fs5.default.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
500
|
+
import_fs5.default.writeFileSync(LOCK_FILE, String(process.pid));
|
|
501
|
+
const release = () => {
|
|
502
|
+
try {
|
|
503
|
+
if (parseInt(import_fs5.default.readFileSync(LOCK_FILE, "utf8").trim(), 10) === process.pid) {
|
|
504
|
+
import_fs5.default.unlinkSync(LOCK_FILE);
|
|
505
|
+
}
|
|
506
|
+
} catch {
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
process.on("exit", release);
|
|
510
|
+
return { ok: true };
|
|
511
|
+
} catch (err) {
|
|
512
|
+
logger.warn({ error: err?.message }, "Could not manage single-instance lock");
|
|
513
|
+
return { ok: true };
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
var import_fs5, import_path4, import_child_process, LOCK_FILE;
|
|
517
|
+
var init_single_instance = __esm({
|
|
518
|
+
"src/single-instance.ts"() {
|
|
519
|
+
"use strict";
|
|
520
|
+
import_fs5 = __toESM(require("fs"));
|
|
521
|
+
import_path4 = __toESM(require("path"));
|
|
522
|
+
import_child_process = require("child_process");
|
|
523
|
+
init_config();
|
|
524
|
+
init_logger();
|
|
525
|
+
LOCK_FILE = import_path4.default.join(CONFIG_DIR, "agent.lock");
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
|
|
430
529
|
// src/claude-live.ts
|
|
431
530
|
function isAlive(pid) {
|
|
432
531
|
try {
|
|
@@ -439,14 +538,14 @@ function isAlive(pid) {
|
|
|
439
538
|
function getLiveClaudeSessions() {
|
|
440
539
|
let files;
|
|
441
540
|
try {
|
|
442
|
-
files =
|
|
541
|
+
files = import_fs6.default.readdirSync(LIVE_SESSIONS_DIR).filter((f) => f.endsWith(".json"));
|
|
443
542
|
} catch {
|
|
444
543
|
return [];
|
|
445
544
|
}
|
|
446
545
|
const out = [];
|
|
447
546
|
for (const f of files) {
|
|
448
547
|
try {
|
|
449
|
-
const obj = JSON.parse(
|
|
548
|
+
const obj = JSON.parse(import_fs6.default.readFileSync(import_path5.default.join(LIVE_SESSIONS_DIR, f), "utf-8"));
|
|
450
549
|
if (typeof obj?.pid !== "number" || !isAlive(obj.pid)) continue;
|
|
451
550
|
out.push({
|
|
452
551
|
pid: obj.pid,
|
|
@@ -481,31 +580,31 @@ function isDescendantOf(pid, ancestor, parents) {
|
|
|
481
580
|
}
|
|
482
581
|
return false;
|
|
483
582
|
}
|
|
484
|
-
var
|
|
583
|
+
var import_fs6, import_path5, import_os2, import_child_process2, import_util, execFileAsync, LIVE_SESSIONS_DIR;
|
|
485
584
|
var init_claude_live = __esm({
|
|
486
585
|
"src/claude-live.ts"() {
|
|
487
586
|
"use strict";
|
|
488
|
-
|
|
489
|
-
|
|
587
|
+
import_fs6 = __toESM(require("fs"));
|
|
588
|
+
import_path5 = __toESM(require("path"));
|
|
490
589
|
import_os2 = __toESM(require("os"));
|
|
491
|
-
|
|
590
|
+
import_child_process2 = require("child_process");
|
|
492
591
|
import_util = require("util");
|
|
493
|
-
execFileAsync = (0, import_util.promisify)(
|
|
494
|
-
LIVE_SESSIONS_DIR =
|
|
592
|
+
execFileAsync = (0, import_util.promisify)(import_child_process2.execFile);
|
|
593
|
+
LIVE_SESSIONS_DIR = import_path5.default.join(import_os2.default.homedir(), ".claude", "sessions");
|
|
495
594
|
}
|
|
496
595
|
});
|
|
497
596
|
|
|
498
597
|
// src/tmux.ts
|
|
499
598
|
function resolveTmuxPath() {
|
|
500
599
|
if (cachedTmuxPath !== void 0) return cachedTmuxPath;
|
|
501
|
-
const dirs = (process.env.PATH || "").split(
|
|
600
|
+
const dirs = (process.env.PATH || "").split(import_path6.default.delimiter).filter(Boolean);
|
|
502
601
|
for (const extra of ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin", "/opt/local/bin"]) {
|
|
503
602
|
if (!dirs.includes(extra)) dirs.push(extra);
|
|
504
603
|
}
|
|
505
604
|
for (const dir of dirs) {
|
|
506
|
-
const candidate =
|
|
605
|
+
const candidate = import_path6.default.join(dir, "tmux");
|
|
507
606
|
try {
|
|
508
|
-
|
|
607
|
+
import_fs7.default.accessSync(candidate, import_fs7.default.constants.X_OK);
|
|
509
608
|
cachedTmuxPath = candidate;
|
|
510
609
|
return candidate;
|
|
511
610
|
} catch {
|
|
@@ -555,8 +654,8 @@ async function enrichWithPanesAndClaude(sessions2) {
|
|
|
555
654
|
const { file, args } = tmuxCmd(["list-panes", "-a", "-F", paneFmt]);
|
|
556
655
|
const { stdout } = await execFileAsync2(file, args, { timeout: 6e3 });
|
|
557
656
|
const panes = stdout.split("\n").map((line) => line.replace(/\r$/, "").trim()).filter(Boolean).map((line) => {
|
|
558
|
-
const [session, pid,
|
|
559
|
-
return { session, pid: parseInt(pid, 10) || 0, path:
|
|
657
|
+
const [session, pid, path11, activeFlags] = line.split(" ");
|
|
658
|
+
return { session, pid: parseInt(pid, 10) || 0, path: path11 || "", active: activeFlags === "11" };
|
|
560
659
|
});
|
|
561
660
|
const byName = new Map(sessions2.map((s) => [s.name, s]));
|
|
562
661
|
for (const pane of panes) {
|
|
@@ -595,16 +694,16 @@ function buildTmuxLaunch(name, launch) {
|
|
|
595
694
|
if (trimmed) args.push(trimmed);
|
|
596
695
|
return { file: tmuxBin, args };
|
|
597
696
|
}
|
|
598
|
-
var
|
|
697
|
+
var import_child_process3, import_util2, import_fs7, import_path6, execFileAsync2, IS_WIN, cachedTmuxPath;
|
|
599
698
|
var init_tmux = __esm({
|
|
600
699
|
"src/tmux.ts"() {
|
|
601
700
|
"use strict";
|
|
602
|
-
|
|
701
|
+
import_child_process3 = require("child_process");
|
|
603
702
|
import_util2 = require("util");
|
|
604
|
-
|
|
605
|
-
|
|
703
|
+
import_fs7 = __toESM(require("fs"));
|
|
704
|
+
import_path6 = __toESM(require("path"));
|
|
606
705
|
init_claude_live();
|
|
607
|
-
execFileAsync2 = (0, import_util2.promisify)(
|
|
706
|
+
execFileAsync2 = (0, import_util2.promisify)(import_child_process3.execFile);
|
|
608
707
|
IS_WIN = process.platform === "win32";
|
|
609
708
|
}
|
|
610
709
|
});
|
|
@@ -614,7 +713,7 @@ function detectShell(preference) {
|
|
|
614
713
|
if (preference !== "auto") return preference;
|
|
615
714
|
if (process.platform === "win32") {
|
|
616
715
|
try {
|
|
617
|
-
(0,
|
|
716
|
+
(0, import_child_process4.execSync)("where pwsh", { stdio: "ignore" });
|
|
618
717
|
return "pwsh.exe";
|
|
619
718
|
} catch {
|
|
620
719
|
return "powershell.exe";
|
|
@@ -622,25 +721,25 @@ function detectShell(preference) {
|
|
|
622
721
|
}
|
|
623
722
|
return process.env.SHELL || "/bin/bash";
|
|
624
723
|
}
|
|
625
|
-
var
|
|
724
|
+
var import_child_process4;
|
|
626
725
|
var init_shell = __esm({
|
|
627
726
|
"src/shell.ts"() {
|
|
628
727
|
"use strict";
|
|
629
|
-
|
|
728
|
+
import_child_process4 = require("child_process");
|
|
630
729
|
}
|
|
631
730
|
});
|
|
632
731
|
|
|
633
732
|
// src/pty-session.ts
|
|
634
733
|
function ensureInitFiles() {
|
|
635
|
-
const versionFile = (0,
|
|
636
|
-
if ((0,
|
|
734
|
+
const versionFile = (0, import_path7.join)(INIT_DIR, ".version");
|
|
735
|
+
if ((0, import_fs8.existsSync)(versionFile)) {
|
|
637
736
|
try {
|
|
638
|
-
if ((0,
|
|
737
|
+
if ((0, import_fs8.readFileSync)(versionFile, "utf-8").trim() === INIT_VERSION) return;
|
|
639
738
|
} catch {
|
|
640
739
|
}
|
|
641
740
|
}
|
|
642
|
-
(0,
|
|
643
|
-
(0,
|
|
741
|
+
(0, import_fs8.mkdirSync)(ZSH_DIR, { recursive: true });
|
|
742
|
+
(0, import_fs8.writeFileSync)(BASH_INIT, [
|
|
644
743
|
"[ -f ~/.bash_profile ] && source ~/.bash_profile",
|
|
645
744
|
"[ -f ~/.bashrc ] && source ~/.bashrc",
|
|
646
745
|
"__crc_s=$SECONDS",
|
|
@@ -651,11 +750,11 @@ function ensureInitFiles() {
|
|
|
651
750
|
`PROMPT_COMMAND='[ "$__crc_armed" = 1 ] && [ \${__crc_elapsed:-0} -ge ${THRESHOLD} ] && printf "\\a"; __crc_armed=1; eval "$__crc_orig_pc"'`,
|
|
652
751
|
""
|
|
653
752
|
].join("\n"), "utf-8");
|
|
654
|
-
(0,
|
|
753
|
+
(0, import_fs8.writeFileSync)(ZSH_ENV, [
|
|
655
754
|
'[ -f "${ZDOTDIR_ORIG:-$HOME}/.zshenv" ] && source "${ZDOTDIR_ORIG:-$HOME}/.zshenv"',
|
|
656
755
|
""
|
|
657
756
|
].join("\n"), "utf-8");
|
|
658
|
-
(0,
|
|
757
|
+
(0, import_fs8.writeFileSync)(ZSH_RC, [
|
|
659
758
|
'[ -f "${ZDOTDIR_ORIG:-$HOME}/.zshrc" ] && source "${ZDOTDIR_ORIG:-$HOME}/.zshrc"',
|
|
660
759
|
"__crc_s=$SECONDS",
|
|
661
760
|
"__crc_armed=0",
|
|
@@ -668,36 +767,36 @@ function ensureInitFiles() {
|
|
|
668
767
|
"fi",
|
|
669
768
|
""
|
|
670
769
|
].join("\n"), "utf-8");
|
|
671
|
-
(0,
|
|
770
|
+
(0, import_fs8.writeFileSync)(versionFile, INIT_VERSION, "utf-8");
|
|
672
771
|
}
|
|
673
772
|
function resolveCwd(cwd) {
|
|
674
773
|
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];
|
|
675
774
|
for (const c of candidates) {
|
|
676
775
|
if (c) {
|
|
677
776
|
try {
|
|
678
|
-
if ((0,
|
|
777
|
+
if ((0, import_fs8.existsSync)(c)) return c;
|
|
679
778
|
} catch {
|
|
680
779
|
}
|
|
681
780
|
}
|
|
682
781
|
}
|
|
683
782
|
return (0, import_os3.homedir)();
|
|
684
783
|
}
|
|
685
|
-
var pty,
|
|
784
|
+
var pty, import_fs8, import_path7, import_os3, THRESHOLD, INIT_DIR, BASH_INIT, ZSH_DIR, ZSH_RC, ZSH_ENV, INIT_VERSION, PtySession;
|
|
686
785
|
var init_pty_session = __esm({
|
|
687
786
|
"src/pty-session.ts"() {
|
|
688
787
|
"use strict";
|
|
689
788
|
pty = __toESM(require("node-pty"));
|
|
690
|
-
|
|
691
|
-
|
|
789
|
+
import_fs8 = require("fs");
|
|
790
|
+
import_path7 = require("path");
|
|
692
791
|
import_os3 = require("os");
|
|
693
792
|
init_src();
|
|
694
793
|
init_shell();
|
|
695
794
|
THRESHOLD = 3;
|
|
696
|
-
INIT_DIR = (0,
|
|
697
|
-
BASH_INIT = (0,
|
|
698
|
-
ZSH_DIR = (0,
|
|
699
|
-
ZSH_RC = (0,
|
|
700
|
-
ZSH_ENV = (0,
|
|
795
|
+
INIT_DIR = (0, import_path7.join)((0, import_os3.tmpdir)(), "crc-shell-init");
|
|
796
|
+
BASH_INIT = (0, import_path7.join)(INIT_DIR, "bashrc");
|
|
797
|
+
ZSH_DIR = (0, import_path7.join)(INIT_DIR, "zsh");
|
|
798
|
+
ZSH_RC = (0, import_path7.join)(ZSH_DIR, ".zshrc");
|
|
799
|
+
ZSH_ENV = (0, import_path7.join)(ZSH_DIR, ".zshenv");
|
|
701
800
|
INIT_VERSION = "2";
|
|
702
801
|
PtySession = class {
|
|
703
802
|
id;
|
|
@@ -908,7 +1007,7 @@ function getCpuUsage() {
|
|
|
908
1007
|
function getRootPaths() {
|
|
909
1008
|
if (process.platform === "win32") {
|
|
910
1009
|
try {
|
|
911
|
-
const output = (0,
|
|
1010
|
+
const output = (0, import_child_process5.execSync)("wmic logicaldisk get name", { encoding: "utf-8" });
|
|
912
1011
|
return output.split("\n").map((l) => l.trim()).filter((l) => /^[A-Z]:$/.test(l)).map((l) => l + "\\");
|
|
913
1012
|
} catch {
|
|
914
1013
|
return ["C:\\"];
|
|
@@ -927,19 +1026,19 @@ function buildHeartbeat(homeDir) {
|
|
|
927
1026
|
memoryUsage: Math.round((totalMem - freeMem) / totalMem * 100),
|
|
928
1027
|
uptime: Math.round(import_os4.default.uptime()),
|
|
929
1028
|
activeSessions: getActiveSessionCount(),
|
|
930
|
-
pathSeparator:
|
|
1029
|
+
pathSeparator: import_path8.default.sep,
|
|
931
1030
|
homeDirectory: homeDir || import_os4.default.homedir(),
|
|
932
1031
|
rootPaths: getRootPaths(),
|
|
933
1032
|
capabilities: { terminal: true, fileTransfer: false }
|
|
934
1033
|
};
|
|
935
1034
|
}
|
|
936
|
-
var import_os4,
|
|
1035
|
+
var import_os4, import_path8, import_child_process5, prevCpu;
|
|
937
1036
|
var init_heartbeat = __esm({
|
|
938
1037
|
"src/heartbeat.ts"() {
|
|
939
1038
|
"use strict";
|
|
940
1039
|
import_os4 = __toESM(require("os"));
|
|
941
|
-
|
|
942
|
-
|
|
1040
|
+
import_path8 = __toESM(require("path"));
|
|
1041
|
+
import_child_process5 = require("child_process");
|
|
943
1042
|
init_terminal_manager();
|
|
944
1043
|
prevCpu = null;
|
|
945
1044
|
}
|
|
@@ -947,28 +1046,28 @@ var init_heartbeat = __esm({
|
|
|
947
1046
|
|
|
948
1047
|
// src/file-explorer.ts
|
|
949
1048
|
function isInSecretDir(resolved) {
|
|
950
|
-
const rel =
|
|
951
|
-
return rel === "" || !rel.startsWith(".." +
|
|
1049
|
+
const rel = import_path9.default.relative(SECRET_DIR, resolved);
|
|
1050
|
+
return rel === "" || !rel.startsWith(".." + import_path9.default.sep) && rel !== ".." && !import_path9.default.isAbsolute(rel);
|
|
952
1051
|
}
|
|
953
1052
|
function listDirectory(dirPath) {
|
|
954
1053
|
try {
|
|
955
|
-
const resolved =
|
|
1054
|
+
const resolved = import_path9.default.resolve(dirPath);
|
|
956
1055
|
if (isInSecretDir(resolved)) {
|
|
957
1056
|
return { entries: [], error: "Access denied" };
|
|
958
1057
|
}
|
|
959
|
-
if (!
|
|
1058
|
+
if (!import_fs9.default.existsSync(resolved)) {
|
|
960
1059
|
return { entries: [], error: "Path does not exist" };
|
|
961
1060
|
}
|
|
962
|
-
const stat =
|
|
1061
|
+
const stat = import_fs9.default.statSync(resolved);
|
|
963
1062
|
if (!stat.isDirectory()) {
|
|
964
1063
|
return { entries: [], error: "Not a directory" };
|
|
965
1064
|
}
|
|
966
|
-
const dirents =
|
|
1065
|
+
const dirents = import_fs9.default.readdirSync(resolved, { withFileTypes: true });
|
|
967
1066
|
const entries = [];
|
|
968
1067
|
for (const dirent of dirents) {
|
|
969
1068
|
try {
|
|
970
|
-
const fullPath =
|
|
971
|
-
const s =
|
|
1069
|
+
const fullPath = import_path9.default.join(resolved, dirent.name);
|
|
1070
|
+
const s = import_fs9.default.statSync(fullPath);
|
|
972
1071
|
entries.push({
|
|
973
1072
|
name: dirent.name,
|
|
974
1073
|
isDirectory: dirent.isDirectory(),
|
|
@@ -990,22 +1089,22 @@ function listDirectory(dirPath) {
|
|
|
990
1089
|
}
|
|
991
1090
|
async function downloadFile(filePath, serverUrl, secret, agentId) {
|
|
992
1091
|
try {
|
|
993
|
-
const resolved =
|
|
1092
|
+
const resolved = import_path9.default.resolve(filePath);
|
|
994
1093
|
if (isInSecretDir(resolved)) {
|
|
995
1094
|
return { error: "Access denied" };
|
|
996
1095
|
}
|
|
997
|
-
if (!
|
|
1096
|
+
if (!import_fs9.default.existsSync(resolved)) {
|
|
998
1097
|
return { error: "File does not exist" };
|
|
999
1098
|
}
|
|
1000
|
-
const stat =
|
|
1099
|
+
const stat = import_fs9.default.statSync(resolved);
|
|
1001
1100
|
if (stat.isDirectory()) {
|
|
1002
1101
|
return { error: "Cannot download a directory" };
|
|
1003
1102
|
}
|
|
1004
1103
|
if (stat.size > FILE_MAX_SIZE) {
|
|
1005
1104
|
return { error: `File too large (max ${FILE_MAX_SIZE} bytes)` };
|
|
1006
1105
|
}
|
|
1007
|
-
const fileName =
|
|
1008
|
-
const fileStream =
|
|
1106
|
+
const fileName = import_path9.default.basename(resolved);
|
|
1107
|
+
const fileStream = import_fs9.default.createReadStream(resolved);
|
|
1009
1108
|
const baseUrl = serverUrl.replace("wss://", "https://").replace("ws://", "http://");
|
|
1010
1109
|
const url = `${baseUrl}/api/files/receive`;
|
|
1011
1110
|
const res = await fetch(url, {
|
|
@@ -1034,16 +1133,16 @@ async function downloadFile(filePath, serverUrl, secret, agentId) {
|
|
|
1034
1133
|
return { error: err.message };
|
|
1035
1134
|
}
|
|
1036
1135
|
}
|
|
1037
|
-
var
|
|
1136
|
+
var import_fs9, import_path9, import_os5, SECRET_DIR;
|
|
1038
1137
|
var init_file_explorer = __esm({
|
|
1039
1138
|
"src/file-explorer.ts"() {
|
|
1040
1139
|
"use strict";
|
|
1041
|
-
|
|
1042
|
-
|
|
1140
|
+
import_fs9 = __toESM(require("fs"));
|
|
1141
|
+
import_path9 = __toESM(require("path"));
|
|
1043
1142
|
import_os5 = __toESM(require("os"));
|
|
1044
1143
|
init_logger();
|
|
1045
1144
|
init_src();
|
|
1046
|
-
SECRET_DIR =
|
|
1145
|
+
SECRET_DIR = import_path9.default.join(import_os5.default.homedir(), ".crc-agent");
|
|
1047
1146
|
}
|
|
1048
1147
|
});
|
|
1049
1148
|
|
|
@@ -1066,8 +1165,8 @@ async function runCmd(cmd2, shell) {
|
|
|
1066
1165
|
}
|
|
1067
1166
|
function getConfigPath(profile) {
|
|
1068
1167
|
const file = profile.configFile || `${profile.id}.conf`;
|
|
1069
|
-
if (
|
|
1070
|
-
return
|
|
1168
|
+
if (import_path10.default.isAbsolute(file)) return file;
|
|
1169
|
+
return import_path10.default.join(VPN_DIR, file);
|
|
1071
1170
|
}
|
|
1072
1171
|
async function getScutilStatus(serviceName) {
|
|
1073
1172
|
const { stdout } = await runCmd(`scutil --nc status '${shq(serviceName)}'`);
|
|
@@ -1183,7 +1282,7 @@ async function connectWireGuard(profile) {
|
|
|
1183
1282
|
const tunnelName = profile.tunnelName || profile.id;
|
|
1184
1283
|
if (isWindows) {
|
|
1185
1284
|
const confPath2 = getConfigPath(profile);
|
|
1186
|
-
if (!
|
|
1285
|
+
if (!import_fs10.default.existsSync(confPath2)) {
|
|
1187
1286
|
return `Config file not found: ${confPath2}`;
|
|
1188
1287
|
}
|
|
1189
1288
|
const script = `
|
|
@@ -1209,7 +1308,7 @@ async function connectWireGuard(profile) {
|
|
|
1209
1308
|
async function connectOpenVpn(profile) {
|
|
1210
1309
|
if (isWindows) {
|
|
1211
1310
|
const confPath2 = getConfigPath(profile);
|
|
1212
|
-
if (!
|
|
1311
|
+
if (!import_fs10.default.existsSync(confPath2)) {
|
|
1213
1312
|
return `Config file not found: ${confPath2}`;
|
|
1214
1313
|
}
|
|
1215
1314
|
const connector = "C:\\Program Files\\OpenVPN Connect\\ovpnconnector.exe";
|
|
@@ -1236,7 +1335,7 @@ async function connectOpenVpn(profile) {
|
|
|
1236
1335
|
return scutilStart(profile.serviceName);
|
|
1237
1336
|
}
|
|
1238
1337
|
const confPath = getConfigPath(profile);
|
|
1239
|
-
if (!
|
|
1338
|
+
if (!import_fs10.default.existsSync(confPath)) {
|
|
1240
1339
|
return `Config file not found: ${confPath}`;
|
|
1241
1340
|
}
|
|
1242
1341
|
const { stderr } = await runCmd(`sudo openvpn --config '${shq(confPath)}' --daemon`);
|
|
@@ -1245,7 +1344,7 @@ async function connectOpenVpn(profile) {
|
|
|
1245
1344
|
async function connectAzure(profile) {
|
|
1246
1345
|
if (isWindows) {
|
|
1247
1346
|
const confPath = getConfigPath(profile);
|
|
1248
|
-
if (!
|
|
1347
|
+
if (!import_fs10.default.existsSync(confPath)) {
|
|
1249
1348
|
return `Config file not found: ${confPath}`;
|
|
1250
1349
|
}
|
|
1251
1350
|
await runCmd(`& 'AzureVpn.exe' -i '${psq(confPath)}' 2>&1`);
|
|
@@ -1365,19 +1464,19 @@ async function disconnectVpn(configs, profileId) {
|
|
|
1365
1464
|
}
|
|
1366
1465
|
return profiles;
|
|
1367
1466
|
}
|
|
1368
|
-
var
|
|
1467
|
+
var import_child_process6, import_util3, import_fs10, import_path10, import_os6, execAsync, isWindows, VPN_DIR;
|
|
1369
1468
|
var init_vpn_manager = __esm({
|
|
1370
1469
|
"src/vpn-manager.ts"() {
|
|
1371
1470
|
"use strict";
|
|
1372
|
-
|
|
1471
|
+
import_child_process6 = require("child_process");
|
|
1373
1472
|
import_util3 = require("util");
|
|
1374
|
-
|
|
1375
|
-
|
|
1473
|
+
import_fs10 = __toESM(require("fs"));
|
|
1474
|
+
import_path10 = __toESM(require("path"));
|
|
1376
1475
|
import_os6 = __toESM(require("os"));
|
|
1377
1476
|
init_logger();
|
|
1378
|
-
execAsync = (0, import_util3.promisify)(
|
|
1477
|
+
execAsync = (0, import_util3.promisify)(import_child_process6.exec);
|
|
1379
1478
|
isWindows = process.platform === "win32";
|
|
1380
|
-
VPN_DIR =
|
|
1479
|
+
VPN_DIR = import_path10.default.join(import_os6.default.homedir(), ".crc-agent", "vpn");
|
|
1381
1480
|
}
|
|
1382
1481
|
});
|
|
1383
1482
|
|
|
@@ -1412,7 +1511,7 @@ var init_claude_path = __esm({
|
|
|
1412
1511
|
// src/claude-sessions.ts
|
|
1413
1512
|
async function parseSessionFile(filePath) {
|
|
1414
1513
|
try {
|
|
1415
|
-
const stream =
|
|
1514
|
+
const stream = import_fs11.default.createReadStream(filePath, { encoding: "utf-8" });
|
|
1416
1515
|
const rl = import_readline2.default.createInterface({ input: stream, crlfDelay: Infinity });
|
|
1417
1516
|
let firstMessage = "";
|
|
1418
1517
|
let lastTimestamp = "";
|
|
@@ -1460,17 +1559,17 @@ async function parseSessionFile(filePath) {
|
|
|
1460
1559
|
}
|
|
1461
1560
|
async function listClaudeSessions(filterProjectPath) {
|
|
1462
1561
|
const sessions2 = [];
|
|
1463
|
-
if (!
|
|
1562
|
+
if (!import_fs11.default.existsSync(CLAUDE_PROJECTS_DIR)) return sessions2;
|
|
1464
1563
|
const isDirSafe = (d) => {
|
|
1465
1564
|
try {
|
|
1466
|
-
return
|
|
1565
|
+
return import_fs11.default.statSync(import_path11.default.join(CLAUDE_PROJECTS_DIR, d)).isDirectory();
|
|
1467
1566
|
} catch {
|
|
1468
1567
|
return false;
|
|
1469
1568
|
}
|
|
1470
1569
|
};
|
|
1471
1570
|
let allEntries;
|
|
1472
1571
|
try {
|
|
1473
|
-
allEntries =
|
|
1572
|
+
allEntries = import_fs11.default.readdirSync(CLAUDE_PROJECTS_DIR);
|
|
1474
1573
|
} catch {
|
|
1475
1574
|
return sessions2;
|
|
1476
1575
|
}
|
|
@@ -1483,16 +1582,16 @@ async function listClaudeSessions(filterProjectPath) {
|
|
|
1483
1582
|
projectDirs = allEntries.filter((d) => isDirSafe(d) && !d.startsWith("-private-"));
|
|
1484
1583
|
}
|
|
1485
1584
|
for (const dirName of projectDirs) {
|
|
1486
|
-
const dirPath =
|
|
1585
|
+
const dirPath = import_path11.default.join(CLAUDE_PROJECTS_DIR, dirName);
|
|
1487
1586
|
let files;
|
|
1488
1587
|
try {
|
|
1489
|
-
files =
|
|
1588
|
+
files = import_fs11.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
|
|
1490
1589
|
} catch {
|
|
1491
1590
|
continue;
|
|
1492
1591
|
}
|
|
1493
1592
|
for (const file of files) {
|
|
1494
1593
|
const sessionId = file.replace(".jsonl", "");
|
|
1495
|
-
const parsed = await parseSessionFile(
|
|
1594
|
+
const parsed = await parseSessionFile(import_path11.default.join(dirPath, file));
|
|
1496
1595
|
if (!parsed) continue;
|
|
1497
1596
|
const projectPath = parsed.cwd || dirName.replace(/^-/, "/").replace(/-/g, "/");
|
|
1498
1597
|
sessions2.push({
|
|
@@ -1510,31 +1609,31 @@ async function listClaudeSessions(filterProjectPath) {
|
|
|
1510
1609
|
sessions2.sort((a, b) => b.lastTimestamp > a.lastTimestamp ? 1 : -1);
|
|
1511
1610
|
return sessions2;
|
|
1512
1611
|
}
|
|
1513
|
-
var
|
|
1612
|
+
var import_fs11, import_path11, import_os7, import_readline2, CLAUDE_PROJECTS_DIR;
|
|
1514
1613
|
var init_claude_sessions = __esm({
|
|
1515
1614
|
"src/claude-sessions.ts"() {
|
|
1516
1615
|
"use strict";
|
|
1517
|
-
|
|
1518
|
-
|
|
1616
|
+
import_fs11 = __toESM(require("fs"));
|
|
1617
|
+
import_path11 = __toESM(require("path"));
|
|
1519
1618
|
import_os7 = __toESM(require("os"));
|
|
1520
1619
|
import_readline2 = __toESM(require("readline"));
|
|
1521
1620
|
init_logger();
|
|
1522
1621
|
init_claude_path();
|
|
1523
|
-
CLAUDE_PROJECTS_DIR =
|
|
1622
|
+
CLAUDE_PROJECTS_DIR = import_path11.default.join(import_os7.default.homedir(), ".claude", "projects");
|
|
1524
1623
|
}
|
|
1525
1624
|
});
|
|
1526
1625
|
|
|
1527
1626
|
// src/claude-conversation.ts
|
|
1528
1627
|
function findProjectDir(projectPath) {
|
|
1529
1628
|
const encoded = encodeProjectPath(projectPath);
|
|
1530
|
-
const dirPath =
|
|
1531
|
-
if (
|
|
1532
|
-
if (!
|
|
1629
|
+
const dirPath = import_path12.default.join(CLAUDE_PROJECTS_DIR2, encoded);
|
|
1630
|
+
if (import_fs12.default.existsSync(dirPath)) return dirPath;
|
|
1631
|
+
if (!import_fs12.default.existsSync(CLAUDE_PROJECTS_DIR2)) return null;
|
|
1533
1632
|
let allDirs;
|
|
1534
1633
|
try {
|
|
1535
|
-
allDirs =
|
|
1634
|
+
allDirs = import_fs12.default.readdirSync(CLAUDE_PROJECTS_DIR2).filter((d) => {
|
|
1536
1635
|
try {
|
|
1537
|
-
return
|
|
1636
|
+
return import_fs12.default.statSync(import_path12.default.join(CLAUDE_PROJECTS_DIR2, d)).isDirectory();
|
|
1538
1637
|
} catch {
|
|
1539
1638
|
return false;
|
|
1540
1639
|
}
|
|
@@ -1544,23 +1643,23 @@ function findProjectDir(projectPath) {
|
|
|
1544
1643
|
}
|
|
1545
1644
|
const matches = findProjectDirs(allDirs, projectPath);
|
|
1546
1645
|
if (matches.length > 0) {
|
|
1547
|
-
return
|
|
1646
|
+
return import_path12.default.join(CLAUDE_PROJECTS_DIR2, matches[0]);
|
|
1548
1647
|
}
|
|
1549
1648
|
return null;
|
|
1550
1649
|
}
|
|
1551
1650
|
function findSessionFileById(sessionId) {
|
|
1552
1651
|
if (!/^[A-Za-z0-9._-]+$/.test(sessionId)) return null;
|
|
1553
|
-
if (!
|
|
1652
|
+
if (!import_fs12.default.existsSync(CLAUDE_PROJECTS_DIR2)) return null;
|
|
1554
1653
|
let dirs;
|
|
1555
1654
|
try {
|
|
1556
|
-
dirs =
|
|
1655
|
+
dirs = import_fs12.default.readdirSync(CLAUDE_PROJECTS_DIR2);
|
|
1557
1656
|
} catch {
|
|
1558
1657
|
return null;
|
|
1559
1658
|
}
|
|
1560
1659
|
for (const dir of dirs) {
|
|
1561
|
-
const candidate =
|
|
1660
|
+
const candidate = import_path12.default.join(CLAUDE_PROJECTS_DIR2, dir, `${sessionId}.jsonl`);
|
|
1562
1661
|
try {
|
|
1563
|
-
if (
|
|
1662
|
+
if (import_fs12.default.statSync(candidate).isFile()) return candidate;
|
|
1564
1663
|
} catch {
|
|
1565
1664
|
}
|
|
1566
1665
|
}
|
|
@@ -1571,20 +1670,20 @@ function findLatestSessionFile(projectPath) {
|
|
|
1571
1670
|
if (!dirPath) return null;
|
|
1572
1671
|
let entries;
|
|
1573
1672
|
try {
|
|
1574
|
-
entries =
|
|
1673
|
+
entries = import_fs12.default.readdirSync(dirPath);
|
|
1575
1674
|
} catch {
|
|
1576
1675
|
return null;
|
|
1577
1676
|
}
|
|
1578
1677
|
const files = entries.filter((f) => f.endsWith(".jsonl") && !f.includes("/")).map((f) => {
|
|
1579
1678
|
try {
|
|
1580
|
-
return { name: f, mtime:
|
|
1679
|
+
return { name: f, mtime: import_fs12.default.statSync(import_path12.default.join(dirPath, f)).mtimeMs };
|
|
1581
1680
|
} catch {
|
|
1582
1681
|
return null;
|
|
1583
1682
|
}
|
|
1584
1683
|
}).filter((f) => f !== null).sort((a, b) => b.mtime - a.mtime);
|
|
1585
1684
|
if (files.length === 0) return null;
|
|
1586
1685
|
return {
|
|
1587
|
-
filePath:
|
|
1686
|
+
filePath: import_path12.default.join(dirPath, files[0].name),
|
|
1588
1687
|
sessionId: files[0].name.replace(".jsonl", "")
|
|
1589
1688
|
};
|
|
1590
1689
|
}
|
|
@@ -1594,8 +1693,8 @@ async function readConversation(projectPath, afterLine = 0, specificSessionId) {
|
|
|
1594
1693
|
if (specificSessionId) {
|
|
1595
1694
|
sessionId = specificSessionId;
|
|
1596
1695
|
const dirPath = findProjectDir(projectPath);
|
|
1597
|
-
const direct = dirPath ?
|
|
1598
|
-
if (direct &&
|
|
1696
|
+
const direct = dirPath ? import_path12.default.join(dirPath, `${specificSessionId}.jsonl`) : null;
|
|
1697
|
+
if (direct && import_fs12.default.existsSync(direct)) {
|
|
1599
1698
|
filePath = direct;
|
|
1600
1699
|
} else {
|
|
1601
1700
|
const byId = findSessionFileById(specificSessionId);
|
|
@@ -1609,7 +1708,7 @@ async function readConversation(projectPath, afterLine = 0, specificSessionId) {
|
|
|
1609
1708
|
sessionId = found.sessionId;
|
|
1610
1709
|
}
|
|
1611
1710
|
try {
|
|
1612
|
-
const stream =
|
|
1711
|
+
const stream = import_fs12.default.createReadStream(filePath, { encoding: "utf-8" });
|
|
1613
1712
|
const rl = import_readline3.default.createInterface({ input: stream, crlfDelay: Infinity });
|
|
1614
1713
|
const messages = [];
|
|
1615
1714
|
let lineNum = 0;
|
|
@@ -1690,23 +1789,23 @@ async function readConversation(projectPath, afterLine = 0, specificSessionId) {
|
|
|
1690
1789
|
return null;
|
|
1691
1790
|
}
|
|
1692
1791
|
}
|
|
1693
|
-
var
|
|
1792
|
+
var import_fs12, import_path12, import_os8, import_readline3, CLAUDE_PROJECTS_DIR2;
|
|
1694
1793
|
var init_claude_conversation = __esm({
|
|
1695
1794
|
"src/claude-conversation.ts"() {
|
|
1696
1795
|
"use strict";
|
|
1697
|
-
|
|
1698
|
-
|
|
1796
|
+
import_fs12 = __toESM(require("fs"));
|
|
1797
|
+
import_path12 = __toESM(require("path"));
|
|
1699
1798
|
import_os8 = __toESM(require("os"));
|
|
1700
1799
|
import_readline3 = __toESM(require("readline"));
|
|
1701
1800
|
init_logger();
|
|
1702
1801
|
init_claude_path();
|
|
1703
|
-
CLAUDE_PROJECTS_DIR2 =
|
|
1802
|
+
CLAUDE_PROJECTS_DIR2 = import_path12.default.join(import_os8.default.homedir(), ".claude", "projects");
|
|
1704
1803
|
}
|
|
1705
1804
|
});
|
|
1706
1805
|
|
|
1707
1806
|
// src/index.ts
|
|
1708
1807
|
var index_exports = {};
|
|
1709
|
-
var import_socket, config, socket, SESSION_MAX_IDLE_MS, reaperInterval, vpnProfiles;
|
|
1808
|
+
var import_socket, config, lock, socket, SESSION_MAX_IDLE_MS, reaperInterval, vpnProfiles;
|
|
1710
1809
|
var init_index = __esm({
|
|
1711
1810
|
"src/index.ts"() {
|
|
1712
1811
|
"use strict";
|
|
@@ -1716,6 +1815,8 @@ var init_index = __esm({
|
|
|
1716
1815
|
init_logger();
|
|
1717
1816
|
init_claude_plugin_installer();
|
|
1718
1817
|
init_local_control();
|
|
1818
|
+
init_pty_helper_fix();
|
|
1819
|
+
init_single_instance();
|
|
1719
1820
|
init_tmux();
|
|
1720
1821
|
init_heartbeat();
|
|
1721
1822
|
init_file_explorer();
|
|
@@ -1729,6 +1830,14 @@ var init_index = __esm({
|
|
|
1729
1830
|
}
|
|
1730
1831
|
config = loadConfig();
|
|
1731
1832
|
logger.info({ agentId: config.agentId, serverUrl: config.serverUrl }, "Starting agent");
|
|
1833
|
+
lock = acquireSingleInstanceLock();
|
|
1834
|
+
if (!lock.ok) {
|
|
1835
|
+
console.error(
|
|
1836
|
+
`Another crc-agent is already running on this machine (PID ${lock.pid}). Stop it first, or set CRC_ALLOW_MULTIPLE=1 to override.`
|
|
1837
|
+
);
|
|
1838
|
+
process.exit(1);
|
|
1839
|
+
}
|
|
1840
|
+
ensurePtyHelperExecutable();
|
|
1732
1841
|
process.on("unhandledRejection", (reason) => {
|
|
1733
1842
|
logger.error({ reason }, "Unhandled promise rejection (agent kept alive)");
|
|
1734
1843
|
});
|