cli-remote-agent 1.0.7 → 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 +166 -104
- package/dist/cli.js.map +1 -1
- package/dist/index.js +147 -94
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -472,6 +472,60 @@ var init_pty_helper_fix = __esm({
|
|
|
472
472
|
}
|
|
473
473
|
});
|
|
474
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
|
+
|
|
475
529
|
// src/claude-live.ts
|
|
476
530
|
function isAlive(pid) {
|
|
477
531
|
try {
|
|
@@ -484,14 +538,14 @@ function isAlive(pid) {
|
|
|
484
538
|
function getLiveClaudeSessions() {
|
|
485
539
|
let files;
|
|
486
540
|
try {
|
|
487
|
-
files =
|
|
541
|
+
files = import_fs6.default.readdirSync(LIVE_SESSIONS_DIR).filter((f) => f.endsWith(".json"));
|
|
488
542
|
} catch {
|
|
489
543
|
return [];
|
|
490
544
|
}
|
|
491
545
|
const out = [];
|
|
492
546
|
for (const f of files) {
|
|
493
547
|
try {
|
|
494
|
-
const obj = JSON.parse(
|
|
548
|
+
const obj = JSON.parse(import_fs6.default.readFileSync(import_path5.default.join(LIVE_SESSIONS_DIR, f), "utf-8"));
|
|
495
549
|
if (typeof obj?.pid !== "number" || !isAlive(obj.pid)) continue;
|
|
496
550
|
out.push({
|
|
497
551
|
pid: obj.pid,
|
|
@@ -526,31 +580,31 @@ function isDescendantOf(pid, ancestor, parents) {
|
|
|
526
580
|
}
|
|
527
581
|
return false;
|
|
528
582
|
}
|
|
529
|
-
var
|
|
583
|
+
var import_fs6, import_path5, import_os2, import_child_process2, import_util, execFileAsync, LIVE_SESSIONS_DIR;
|
|
530
584
|
var init_claude_live = __esm({
|
|
531
585
|
"src/claude-live.ts"() {
|
|
532
586
|
"use strict";
|
|
533
|
-
|
|
534
|
-
|
|
587
|
+
import_fs6 = __toESM(require("fs"));
|
|
588
|
+
import_path5 = __toESM(require("path"));
|
|
535
589
|
import_os2 = __toESM(require("os"));
|
|
536
|
-
|
|
590
|
+
import_child_process2 = require("child_process");
|
|
537
591
|
import_util = require("util");
|
|
538
|
-
execFileAsync = (0, import_util.promisify)(
|
|
539
|
-
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");
|
|
540
594
|
}
|
|
541
595
|
});
|
|
542
596
|
|
|
543
597
|
// src/tmux.ts
|
|
544
598
|
function resolveTmuxPath() {
|
|
545
599
|
if (cachedTmuxPath !== void 0) return cachedTmuxPath;
|
|
546
|
-
const dirs = (process.env.PATH || "").split(
|
|
600
|
+
const dirs = (process.env.PATH || "").split(import_path6.default.delimiter).filter(Boolean);
|
|
547
601
|
for (const extra of ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin", "/opt/local/bin"]) {
|
|
548
602
|
if (!dirs.includes(extra)) dirs.push(extra);
|
|
549
603
|
}
|
|
550
604
|
for (const dir of dirs) {
|
|
551
|
-
const candidate =
|
|
605
|
+
const candidate = import_path6.default.join(dir, "tmux");
|
|
552
606
|
try {
|
|
553
|
-
|
|
607
|
+
import_fs7.default.accessSync(candidate, import_fs7.default.constants.X_OK);
|
|
554
608
|
cachedTmuxPath = candidate;
|
|
555
609
|
return candidate;
|
|
556
610
|
} catch {
|
|
@@ -600,8 +654,8 @@ async function enrichWithPanesAndClaude(sessions2) {
|
|
|
600
654
|
const { file, args } = tmuxCmd(["list-panes", "-a", "-F", paneFmt]);
|
|
601
655
|
const { stdout } = await execFileAsync2(file, args, { timeout: 6e3 });
|
|
602
656
|
const panes = stdout.split("\n").map((line) => line.replace(/\r$/, "").trim()).filter(Boolean).map((line) => {
|
|
603
|
-
const [session, pid,
|
|
604
|
-
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" };
|
|
605
659
|
});
|
|
606
660
|
const byName = new Map(sessions2.map((s) => [s.name, s]));
|
|
607
661
|
for (const pane of panes) {
|
|
@@ -640,16 +694,16 @@ function buildTmuxLaunch(name, launch) {
|
|
|
640
694
|
if (trimmed) args.push(trimmed);
|
|
641
695
|
return { file: tmuxBin, args };
|
|
642
696
|
}
|
|
643
|
-
var
|
|
697
|
+
var import_child_process3, import_util2, import_fs7, import_path6, execFileAsync2, IS_WIN, cachedTmuxPath;
|
|
644
698
|
var init_tmux = __esm({
|
|
645
699
|
"src/tmux.ts"() {
|
|
646
700
|
"use strict";
|
|
647
|
-
|
|
701
|
+
import_child_process3 = require("child_process");
|
|
648
702
|
import_util2 = require("util");
|
|
649
|
-
|
|
650
|
-
|
|
703
|
+
import_fs7 = __toESM(require("fs"));
|
|
704
|
+
import_path6 = __toESM(require("path"));
|
|
651
705
|
init_claude_live();
|
|
652
|
-
execFileAsync2 = (0, import_util2.promisify)(
|
|
706
|
+
execFileAsync2 = (0, import_util2.promisify)(import_child_process3.execFile);
|
|
653
707
|
IS_WIN = process.platform === "win32";
|
|
654
708
|
}
|
|
655
709
|
});
|
|
@@ -659,7 +713,7 @@ function detectShell(preference) {
|
|
|
659
713
|
if (preference !== "auto") return preference;
|
|
660
714
|
if (process.platform === "win32") {
|
|
661
715
|
try {
|
|
662
|
-
(0,
|
|
716
|
+
(0, import_child_process4.execSync)("where pwsh", { stdio: "ignore" });
|
|
663
717
|
return "pwsh.exe";
|
|
664
718
|
} catch {
|
|
665
719
|
return "powershell.exe";
|
|
@@ -667,25 +721,25 @@ function detectShell(preference) {
|
|
|
667
721
|
}
|
|
668
722
|
return process.env.SHELL || "/bin/bash";
|
|
669
723
|
}
|
|
670
|
-
var
|
|
724
|
+
var import_child_process4;
|
|
671
725
|
var init_shell = __esm({
|
|
672
726
|
"src/shell.ts"() {
|
|
673
727
|
"use strict";
|
|
674
|
-
|
|
728
|
+
import_child_process4 = require("child_process");
|
|
675
729
|
}
|
|
676
730
|
});
|
|
677
731
|
|
|
678
732
|
// src/pty-session.ts
|
|
679
733
|
function ensureInitFiles() {
|
|
680
|
-
const versionFile = (0,
|
|
681
|
-
if ((0,
|
|
734
|
+
const versionFile = (0, import_path7.join)(INIT_DIR, ".version");
|
|
735
|
+
if ((0, import_fs8.existsSync)(versionFile)) {
|
|
682
736
|
try {
|
|
683
|
-
if ((0,
|
|
737
|
+
if ((0, import_fs8.readFileSync)(versionFile, "utf-8").trim() === INIT_VERSION) return;
|
|
684
738
|
} catch {
|
|
685
739
|
}
|
|
686
740
|
}
|
|
687
|
-
(0,
|
|
688
|
-
(0,
|
|
741
|
+
(0, import_fs8.mkdirSync)(ZSH_DIR, { recursive: true });
|
|
742
|
+
(0, import_fs8.writeFileSync)(BASH_INIT, [
|
|
689
743
|
"[ -f ~/.bash_profile ] && source ~/.bash_profile",
|
|
690
744
|
"[ -f ~/.bashrc ] && source ~/.bashrc",
|
|
691
745
|
"__crc_s=$SECONDS",
|
|
@@ -696,11 +750,11 @@ function ensureInitFiles() {
|
|
|
696
750
|
`PROMPT_COMMAND='[ "$__crc_armed" = 1 ] && [ \${__crc_elapsed:-0} -ge ${THRESHOLD} ] && printf "\\a"; __crc_armed=1; eval "$__crc_orig_pc"'`,
|
|
697
751
|
""
|
|
698
752
|
].join("\n"), "utf-8");
|
|
699
|
-
(0,
|
|
753
|
+
(0, import_fs8.writeFileSync)(ZSH_ENV, [
|
|
700
754
|
'[ -f "${ZDOTDIR_ORIG:-$HOME}/.zshenv" ] && source "${ZDOTDIR_ORIG:-$HOME}/.zshenv"',
|
|
701
755
|
""
|
|
702
756
|
].join("\n"), "utf-8");
|
|
703
|
-
(0,
|
|
757
|
+
(0, import_fs8.writeFileSync)(ZSH_RC, [
|
|
704
758
|
'[ -f "${ZDOTDIR_ORIG:-$HOME}/.zshrc" ] && source "${ZDOTDIR_ORIG:-$HOME}/.zshrc"',
|
|
705
759
|
"__crc_s=$SECONDS",
|
|
706
760
|
"__crc_armed=0",
|
|
@@ -713,36 +767,36 @@ function ensureInitFiles() {
|
|
|
713
767
|
"fi",
|
|
714
768
|
""
|
|
715
769
|
].join("\n"), "utf-8");
|
|
716
|
-
(0,
|
|
770
|
+
(0, import_fs8.writeFileSync)(versionFile, INIT_VERSION, "utf-8");
|
|
717
771
|
}
|
|
718
772
|
function resolveCwd(cwd) {
|
|
719
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];
|
|
720
774
|
for (const c of candidates) {
|
|
721
775
|
if (c) {
|
|
722
776
|
try {
|
|
723
|
-
if ((0,
|
|
777
|
+
if ((0, import_fs8.existsSync)(c)) return c;
|
|
724
778
|
} catch {
|
|
725
779
|
}
|
|
726
780
|
}
|
|
727
781
|
}
|
|
728
782
|
return (0, import_os3.homedir)();
|
|
729
783
|
}
|
|
730
|
-
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;
|
|
731
785
|
var init_pty_session = __esm({
|
|
732
786
|
"src/pty-session.ts"() {
|
|
733
787
|
"use strict";
|
|
734
788
|
pty = __toESM(require("node-pty"));
|
|
735
|
-
|
|
736
|
-
|
|
789
|
+
import_fs8 = require("fs");
|
|
790
|
+
import_path7 = require("path");
|
|
737
791
|
import_os3 = require("os");
|
|
738
792
|
init_src();
|
|
739
793
|
init_shell();
|
|
740
794
|
THRESHOLD = 3;
|
|
741
|
-
INIT_DIR = (0,
|
|
742
|
-
BASH_INIT = (0,
|
|
743
|
-
ZSH_DIR = (0,
|
|
744
|
-
ZSH_RC = (0,
|
|
745
|
-
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");
|
|
746
800
|
INIT_VERSION = "2";
|
|
747
801
|
PtySession = class {
|
|
748
802
|
id;
|
|
@@ -953,7 +1007,7 @@ function getCpuUsage() {
|
|
|
953
1007
|
function getRootPaths() {
|
|
954
1008
|
if (process.platform === "win32") {
|
|
955
1009
|
try {
|
|
956
|
-
const output = (0,
|
|
1010
|
+
const output = (0, import_child_process5.execSync)("wmic logicaldisk get name", { encoding: "utf-8" });
|
|
957
1011
|
return output.split("\n").map((l) => l.trim()).filter((l) => /^[A-Z]:$/.test(l)).map((l) => l + "\\");
|
|
958
1012
|
} catch {
|
|
959
1013
|
return ["C:\\"];
|
|
@@ -972,19 +1026,19 @@ function buildHeartbeat(homeDir) {
|
|
|
972
1026
|
memoryUsage: Math.round((totalMem - freeMem) / totalMem * 100),
|
|
973
1027
|
uptime: Math.round(import_os4.default.uptime()),
|
|
974
1028
|
activeSessions: getActiveSessionCount(),
|
|
975
|
-
pathSeparator:
|
|
1029
|
+
pathSeparator: import_path8.default.sep,
|
|
976
1030
|
homeDirectory: homeDir || import_os4.default.homedir(),
|
|
977
1031
|
rootPaths: getRootPaths(),
|
|
978
1032
|
capabilities: { terminal: true, fileTransfer: false }
|
|
979
1033
|
};
|
|
980
1034
|
}
|
|
981
|
-
var import_os4,
|
|
1035
|
+
var import_os4, import_path8, import_child_process5, prevCpu;
|
|
982
1036
|
var init_heartbeat = __esm({
|
|
983
1037
|
"src/heartbeat.ts"() {
|
|
984
1038
|
"use strict";
|
|
985
1039
|
import_os4 = __toESM(require("os"));
|
|
986
|
-
|
|
987
|
-
|
|
1040
|
+
import_path8 = __toESM(require("path"));
|
|
1041
|
+
import_child_process5 = require("child_process");
|
|
988
1042
|
init_terminal_manager();
|
|
989
1043
|
prevCpu = null;
|
|
990
1044
|
}
|
|
@@ -992,28 +1046,28 @@ var init_heartbeat = __esm({
|
|
|
992
1046
|
|
|
993
1047
|
// src/file-explorer.ts
|
|
994
1048
|
function isInSecretDir(resolved) {
|
|
995
|
-
const rel =
|
|
996
|
-
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);
|
|
997
1051
|
}
|
|
998
1052
|
function listDirectory(dirPath) {
|
|
999
1053
|
try {
|
|
1000
|
-
const resolved =
|
|
1054
|
+
const resolved = import_path9.default.resolve(dirPath);
|
|
1001
1055
|
if (isInSecretDir(resolved)) {
|
|
1002
1056
|
return { entries: [], error: "Access denied" };
|
|
1003
1057
|
}
|
|
1004
|
-
if (!
|
|
1058
|
+
if (!import_fs9.default.existsSync(resolved)) {
|
|
1005
1059
|
return { entries: [], error: "Path does not exist" };
|
|
1006
1060
|
}
|
|
1007
|
-
const stat =
|
|
1061
|
+
const stat = import_fs9.default.statSync(resolved);
|
|
1008
1062
|
if (!stat.isDirectory()) {
|
|
1009
1063
|
return { entries: [], error: "Not a directory" };
|
|
1010
1064
|
}
|
|
1011
|
-
const dirents =
|
|
1065
|
+
const dirents = import_fs9.default.readdirSync(resolved, { withFileTypes: true });
|
|
1012
1066
|
const entries = [];
|
|
1013
1067
|
for (const dirent of dirents) {
|
|
1014
1068
|
try {
|
|
1015
|
-
const fullPath =
|
|
1016
|
-
const s =
|
|
1069
|
+
const fullPath = import_path9.default.join(resolved, dirent.name);
|
|
1070
|
+
const s = import_fs9.default.statSync(fullPath);
|
|
1017
1071
|
entries.push({
|
|
1018
1072
|
name: dirent.name,
|
|
1019
1073
|
isDirectory: dirent.isDirectory(),
|
|
@@ -1035,22 +1089,22 @@ function listDirectory(dirPath) {
|
|
|
1035
1089
|
}
|
|
1036
1090
|
async function downloadFile(filePath, serverUrl, secret, agentId) {
|
|
1037
1091
|
try {
|
|
1038
|
-
const resolved =
|
|
1092
|
+
const resolved = import_path9.default.resolve(filePath);
|
|
1039
1093
|
if (isInSecretDir(resolved)) {
|
|
1040
1094
|
return { error: "Access denied" };
|
|
1041
1095
|
}
|
|
1042
|
-
if (!
|
|
1096
|
+
if (!import_fs9.default.existsSync(resolved)) {
|
|
1043
1097
|
return { error: "File does not exist" };
|
|
1044
1098
|
}
|
|
1045
|
-
const stat =
|
|
1099
|
+
const stat = import_fs9.default.statSync(resolved);
|
|
1046
1100
|
if (stat.isDirectory()) {
|
|
1047
1101
|
return { error: "Cannot download a directory" };
|
|
1048
1102
|
}
|
|
1049
1103
|
if (stat.size > FILE_MAX_SIZE) {
|
|
1050
1104
|
return { error: `File too large (max ${FILE_MAX_SIZE} bytes)` };
|
|
1051
1105
|
}
|
|
1052
|
-
const fileName =
|
|
1053
|
-
const fileStream =
|
|
1106
|
+
const fileName = import_path9.default.basename(resolved);
|
|
1107
|
+
const fileStream = import_fs9.default.createReadStream(resolved);
|
|
1054
1108
|
const baseUrl = serverUrl.replace("wss://", "https://").replace("ws://", "http://");
|
|
1055
1109
|
const url = `${baseUrl}/api/files/receive`;
|
|
1056
1110
|
const res = await fetch(url, {
|
|
@@ -1079,16 +1133,16 @@ async function downloadFile(filePath, serverUrl, secret, agentId) {
|
|
|
1079
1133
|
return { error: err.message };
|
|
1080
1134
|
}
|
|
1081
1135
|
}
|
|
1082
|
-
var
|
|
1136
|
+
var import_fs9, import_path9, import_os5, SECRET_DIR;
|
|
1083
1137
|
var init_file_explorer = __esm({
|
|
1084
1138
|
"src/file-explorer.ts"() {
|
|
1085
1139
|
"use strict";
|
|
1086
|
-
|
|
1087
|
-
|
|
1140
|
+
import_fs9 = __toESM(require("fs"));
|
|
1141
|
+
import_path9 = __toESM(require("path"));
|
|
1088
1142
|
import_os5 = __toESM(require("os"));
|
|
1089
1143
|
init_logger();
|
|
1090
1144
|
init_src();
|
|
1091
|
-
SECRET_DIR =
|
|
1145
|
+
SECRET_DIR = import_path9.default.join(import_os5.default.homedir(), ".crc-agent");
|
|
1092
1146
|
}
|
|
1093
1147
|
});
|
|
1094
1148
|
|
|
@@ -1111,8 +1165,8 @@ async function runCmd(cmd2, shell) {
|
|
|
1111
1165
|
}
|
|
1112
1166
|
function getConfigPath(profile) {
|
|
1113
1167
|
const file = profile.configFile || `${profile.id}.conf`;
|
|
1114
|
-
if (
|
|
1115
|
-
return
|
|
1168
|
+
if (import_path10.default.isAbsolute(file)) return file;
|
|
1169
|
+
return import_path10.default.join(VPN_DIR, file);
|
|
1116
1170
|
}
|
|
1117
1171
|
async function getScutilStatus(serviceName) {
|
|
1118
1172
|
const { stdout } = await runCmd(`scutil --nc status '${shq(serviceName)}'`);
|
|
@@ -1228,7 +1282,7 @@ async function connectWireGuard(profile) {
|
|
|
1228
1282
|
const tunnelName = profile.tunnelName || profile.id;
|
|
1229
1283
|
if (isWindows) {
|
|
1230
1284
|
const confPath2 = getConfigPath(profile);
|
|
1231
|
-
if (!
|
|
1285
|
+
if (!import_fs10.default.existsSync(confPath2)) {
|
|
1232
1286
|
return `Config file not found: ${confPath2}`;
|
|
1233
1287
|
}
|
|
1234
1288
|
const script = `
|
|
@@ -1254,7 +1308,7 @@ async function connectWireGuard(profile) {
|
|
|
1254
1308
|
async function connectOpenVpn(profile) {
|
|
1255
1309
|
if (isWindows) {
|
|
1256
1310
|
const confPath2 = getConfigPath(profile);
|
|
1257
|
-
if (!
|
|
1311
|
+
if (!import_fs10.default.existsSync(confPath2)) {
|
|
1258
1312
|
return `Config file not found: ${confPath2}`;
|
|
1259
1313
|
}
|
|
1260
1314
|
const connector = "C:\\Program Files\\OpenVPN Connect\\ovpnconnector.exe";
|
|
@@ -1281,7 +1335,7 @@ async function connectOpenVpn(profile) {
|
|
|
1281
1335
|
return scutilStart(profile.serviceName);
|
|
1282
1336
|
}
|
|
1283
1337
|
const confPath = getConfigPath(profile);
|
|
1284
|
-
if (!
|
|
1338
|
+
if (!import_fs10.default.existsSync(confPath)) {
|
|
1285
1339
|
return `Config file not found: ${confPath}`;
|
|
1286
1340
|
}
|
|
1287
1341
|
const { stderr } = await runCmd(`sudo openvpn --config '${shq(confPath)}' --daemon`);
|
|
@@ -1290,7 +1344,7 @@ async function connectOpenVpn(profile) {
|
|
|
1290
1344
|
async function connectAzure(profile) {
|
|
1291
1345
|
if (isWindows) {
|
|
1292
1346
|
const confPath = getConfigPath(profile);
|
|
1293
|
-
if (!
|
|
1347
|
+
if (!import_fs10.default.existsSync(confPath)) {
|
|
1294
1348
|
return `Config file not found: ${confPath}`;
|
|
1295
1349
|
}
|
|
1296
1350
|
await runCmd(`& 'AzureVpn.exe' -i '${psq(confPath)}' 2>&1`);
|
|
@@ -1410,19 +1464,19 @@ async function disconnectVpn(configs, profileId) {
|
|
|
1410
1464
|
}
|
|
1411
1465
|
return profiles;
|
|
1412
1466
|
}
|
|
1413
|
-
var
|
|
1467
|
+
var import_child_process6, import_util3, import_fs10, import_path10, import_os6, execAsync, isWindows, VPN_DIR;
|
|
1414
1468
|
var init_vpn_manager = __esm({
|
|
1415
1469
|
"src/vpn-manager.ts"() {
|
|
1416
1470
|
"use strict";
|
|
1417
|
-
|
|
1471
|
+
import_child_process6 = require("child_process");
|
|
1418
1472
|
import_util3 = require("util");
|
|
1419
|
-
|
|
1420
|
-
|
|
1473
|
+
import_fs10 = __toESM(require("fs"));
|
|
1474
|
+
import_path10 = __toESM(require("path"));
|
|
1421
1475
|
import_os6 = __toESM(require("os"));
|
|
1422
1476
|
init_logger();
|
|
1423
|
-
execAsync = (0, import_util3.promisify)(
|
|
1477
|
+
execAsync = (0, import_util3.promisify)(import_child_process6.exec);
|
|
1424
1478
|
isWindows = process.platform === "win32";
|
|
1425
|
-
VPN_DIR =
|
|
1479
|
+
VPN_DIR = import_path10.default.join(import_os6.default.homedir(), ".crc-agent", "vpn");
|
|
1426
1480
|
}
|
|
1427
1481
|
});
|
|
1428
1482
|
|
|
@@ -1457,7 +1511,7 @@ var init_claude_path = __esm({
|
|
|
1457
1511
|
// src/claude-sessions.ts
|
|
1458
1512
|
async function parseSessionFile(filePath) {
|
|
1459
1513
|
try {
|
|
1460
|
-
const stream =
|
|
1514
|
+
const stream = import_fs11.default.createReadStream(filePath, { encoding: "utf-8" });
|
|
1461
1515
|
const rl = import_readline2.default.createInterface({ input: stream, crlfDelay: Infinity });
|
|
1462
1516
|
let firstMessage = "";
|
|
1463
1517
|
let lastTimestamp = "";
|
|
@@ -1505,17 +1559,17 @@ async function parseSessionFile(filePath) {
|
|
|
1505
1559
|
}
|
|
1506
1560
|
async function listClaudeSessions(filterProjectPath) {
|
|
1507
1561
|
const sessions2 = [];
|
|
1508
|
-
if (!
|
|
1562
|
+
if (!import_fs11.default.existsSync(CLAUDE_PROJECTS_DIR)) return sessions2;
|
|
1509
1563
|
const isDirSafe = (d) => {
|
|
1510
1564
|
try {
|
|
1511
|
-
return
|
|
1565
|
+
return import_fs11.default.statSync(import_path11.default.join(CLAUDE_PROJECTS_DIR, d)).isDirectory();
|
|
1512
1566
|
} catch {
|
|
1513
1567
|
return false;
|
|
1514
1568
|
}
|
|
1515
1569
|
};
|
|
1516
1570
|
let allEntries;
|
|
1517
1571
|
try {
|
|
1518
|
-
allEntries =
|
|
1572
|
+
allEntries = import_fs11.default.readdirSync(CLAUDE_PROJECTS_DIR);
|
|
1519
1573
|
} catch {
|
|
1520
1574
|
return sessions2;
|
|
1521
1575
|
}
|
|
@@ -1528,16 +1582,16 @@ async function listClaudeSessions(filterProjectPath) {
|
|
|
1528
1582
|
projectDirs = allEntries.filter((d) => isDirSafe(d) && !d.startsWith("-private-"));
|
|
1529
1583
|
}
|
|
1530
1584
|
for (const dirName of projectDirs) {
|
|
1531
|
-
const dirPath =
|
|
1585
|
+
const dirPath = import_path11.default.join(CLAUDE_PROJECTS_DIR, dirName);
|
|
1532
1586
|
let files;
|
|
1533
1587
|
try {
|
|
1534
|
-
files =
|
|
1588
|
+
files = import_fs11.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
|
|
1535
1589
|
} catch {
|
|
1536
1590
|
continue;
|
|
1537
1591
|
}
|
|
1538
1592
|
for (const file of files) {
|
|
1539
1593
|
const sessionId = file.replace(".jsonl", "");
|
|
1540
|
-
const parsed = await parseSessionFile(
|
|
1594
|
+
const parsed = await parseSessionFile(import_path11.default.join(dirPath, file));
|
|
1541
1595
|
if (!parsed) continue;
|
|
1542
1596
|
const projectPath = parsed.cwd || dirName.replace(/^-/, "/").replace(/-/g, "/");
|
|
1543
1597
|
sessions2.push({
|
|
@@ -1555,31 +1609,31 @@ async function listClaudeSessions(filterProjectPath) {
|
|
|
1555
1609
|
sessions2.sort((a, b) => b.lastTimestamp > a.lastTimestamp ? 1 : -1);
|
|
1556
1610
|
return sessions2;
|
|
1557
1611
|
}
|
|
1558
|
-
var
|
|
1612
|
+
var import_fs11, import_path11, import_os7, import_readline2, CLAUDE_PROJECTS_DIR;
|
|
1559
1613
|
var init_claude_sessions = __esm({
|
|
1560
1614
|
"src/claude-sessions.ts"() {
|
|
1561
1615
|
"use strict";
|
|
1562
|
-
|
|
1563
|
-
|
|
1616
|
+
import_fs11 = __toESM(require("fs"));
|
|
1617
|
+
import_path11 = __toESM(require("path"));
|
|
1564
1618
|
import_os7 = __toESM(require("os"));
|
|
1565
1619
|
import_readline2 = __toESM(require("readline"));
|
|
1566
1620
|
init_logger();
|
|
1567
1621
|
init_claude_path();
|
|
1568
|
-
CLAUDE_PROJECTS_DIR =
|
|
1622
|
+
CLAUDE_PROJECTS_DIR = import_path11.default.join(import_os7.default.homedir(), ".claude", "projects");
|
|
1569
1623
|
}
|
|
1570
1624
|
});
|
|
1571
1625
|
|
|
1572
1626
|
// src/claude-conversation.ts
|
|
1573
1627
|
function findProjectDir(projectPath) {
|
|
1574
1628
|
const encoded = encodeProjectPath(projectPath);
|
|
1575
|
-
const dirPath =
|
|
1576
|
-
if (
|
|
1577
|
-
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;
|
|
1578
1632
|
let allDirs;
|
|
1579
1633
|
try {
|
|
1580
|
-
allDirs =
|
|
1634
|
+
allDirs = import_fs12.default.readdirSync(CLAUDE_PROJECTS_DIR2).filter((d) => {
|
|
1581
1635
|
try {
|
|
1582
|
-
return
|
|
1636
|
+
return import_fs12.default.statSync(import_path12.default.join(CLAUDE_PROJECTS_DIR2, d)).isDirectory();
|
|
1583
1637
|
} catch {
|
|
1584
1638
|
return false;
|
|
1585
1639
|
}
|
|
@@ -1589,23 +1643,23 @@ function findProjectDir(projectPath) {
|
|
|
1589
1643
|
}
|
|
1590
1644
|
const matches = findProjectDirs(allDirs, projectPath);
|
|
1591
1645
|
if (matches.length > 0) {
|
|
1592
|
-
return
|
|
1646
|
+
return import_path12.default.join(CLAUDE_PROJECTS_DIR2, matches[0]);
|
|
1593
1647
|
}
|
|
1594
1648
|
return null;
|
|
1595
1649
|
}
|
|
1596
1650
|
function findSessionFileById(sessionId) {
|
|
1597
1651
|
if (!/^[A-Za-z0-9._-]+$/.test(sessionId)) return null;
|
|
1598
|
-
if (!
|
|
1652
|
+
if (!import_fs12.default.existsSync(CLAUDE_PROJECTS_DIR2)) return null;
|
|
1599
1653
|
let dirs;
|
|
1600
1654
|
try {
|
|
1601
|
-
dirs =
|
|
1655
|
+
dirs = import_fs12.default.readdirSync(CLAUDE_PROJECTS_DIR2);
|
|
1602
1656
|
} catch {
|
|
1603
1657
|
return null;
|
|
1604
1658
|
}
|
|
1605
1659
|
for (const dir of dirs) {
|
|
1606
|
-
const candidate =
|
|
1660
|
+
const candidate = import_path12.default.join(CLAUDE_PROJECTS_DIR2, dir, `${sessionId}.jsonl`);
|
|
1607
1661
|
try {
|
|
1608
|
-
if (
|
|
1662
|
+
if (import_fs12.default.statSync(candidate).isFile()) return candidate;
|
|
1609
1663
|
} catch {
|
|
1610
1664
|
}
|
|
1611
1665
|
}
|
|
@@ -1616,20 +1670,20 @@ function findLatestSessionFile(projectPath) {
|
|
|
1616
1670
|
if (!dirPath) return null;
|
|
1617
1671
|
let entries;
|
|
1618
1672
|
try {
|
|
1619
|
-
entries =
|
|
1673
|
+
entries = import_fs12.default.readdirSync(dirPath);
|
|
1620
1674
|
} catch {
|
|
1621
1675
|
return null;
|
|
1622
1676
|
}
|
|
1623
1677
|
const files = entries.filter((f) => f.endsWith(".jsonl") && !f.includes("/")).map((f) => {
|
|
1624
1678
|
try {
|
|
1625
|
-
return { name: f, mtime:
|
|
1679
|
+
return { name: f, mtime: import_fs12.default.statSync(import_path12.default.join(dirPath, f)).mtimeMs };
|
|
1626
1680
|
} catch {
|
|
1627
1681
|
return null;
|
|
1628
1682
|
}
|
|
1629
1683
|
}).filter((f) => f !== null).sort((a, b) => b.mtime - a.mtime);
|
|
1630
1684
|
if (files.length === 0) return null;
|
|
1631
1685
|
return {
|
|
1632
|
-
filePath:
|
|
1686
|
+
filePath: import_path12.default.join(dirPath, files[0].name),
|
|
1633
1687
|
sessionId: files[0].name.replace(".jsonl", "")
|
|
1634
1688
|
};
|
|
1635
1689
|
}
|
|
@@ -1639,8 +1693,8 @@ async function readConversation(projectPath, afterLine = 0, specificSessionId) {
|
|
|
1639
1693
|
if (specificSessionId) {
|
|
1640
1694
|
sessionId = specificSessionId;
|
|
1641
1695
|
const dirPath = findProjectDir(projectPath);
|
|
1642
|
-
const direct = dirPath ?
|
|
1643
|
-
if (direct &&
|
|
1696
|
+
const direct = dirPath ? import_path12.default.join(dirPath, `${specificSessionId}.jsonl`) : null;
|
|
1697
|
+
if (direct && import_fs12.default.existsSync(direct)) {
|
|
1644
1698
|
filePath = direct;
|
|
1645
1699
|
} else {
|
|
1646
1700
|
const byId = findSessionFileById(specificSessionId);
|
|
@@ -1654,7 +1708,7 @@ async function readConversation(projectPath, afterLine = 0, specificSessionId) {
|
|
|
1654
1708
|
sessionId = found.sessionId;
|
|
1655
1709
|
}
|
|
1656
1710
|
try {
|
|
1657
|
-
const stream =
|
|
1711
|
+
const stream = import_fs12.default.createReadStream(filePath, { encoding: "utf-8" });
|
|
1658
1712
|
const rl = import_readline3.default.createInterface({ input: stream, crlfDelay: Infinity });
|
|
1659
1713
|
const messages = [];
|
|
1660
1714
|
let lineNum = 0;
|
|
@@ -1735,23 +1789,23 @@ async function readConversation(projectPath, afterLine = 0, specificSessionId) {
|
|
|
1735
1789
|
return null;
|
|
1736
1790
|
}
|
|
1737
1791
|
}
|
|
1738
|
-
var
|
|
1792
|
+
var import_fs12, import_path12, import_os8, import_readline3, CLAUDE_PROJECTS_DIR2;
|
|
1739
1793
|
var init_claude_conversation = __esm({
|
|
1740
1794
|
"src/claude-conversation.ts"() {
|
|
1741
1795
|
"use strict";
|
|
1742
|
-
|
|
1743
|
-
|
|
1796
|
+
import_fs12 = __toESM(require("fs"));
|
|
1797
|
+
import_path12 = __toESM(require("path"));
|
|
1744
1798
|
import_os8 = __toESM(require("os"));
|
|
1745
1799
|
import_readline3 = __toESM(require("readline"));
|
|
1746
1800
|
init_logger();
|
|
1747
1801
|
init_claude_path();
|
|
1748
|
-
CLAUDE_PROJECTS_DIR2 =
|
|
1802
|
+
CLAUDE_PROJECTS_DIR2 = import_path12.default.join(import_os8.default.homedir(), ".claude", "projects");
|
|
1749
1803
|
}
|
|
1750
1804
|
});
|
|
1751
1805
|
|
|
1752
1806
|
// src/index.ts
|
|
1753
1807
|
var index_exports = {};
|
|
1754
|
-
var import_socket, config, socket, SESSION_MAX_IDLE_MS, reaperInterval, vpnProfiles;
|
|
1808
|
+
var import_socket, config, lock, socket, SESSION_MAX_IDLE_MS, reaperInterval, vpnProfiles;
|
|
1755
1809
|
var init_index = __esm({
|
|
1756
1810
|
"src/index.ts"() {
|
|
1757
1811
|
"use strict";
|
|
@@ -1762,6 +1816,7 @@ var init_index = __esm({
|
|
|
1762
1816
|
init_claude_plugin_installer();
|
|
1763
1817
|
init_local_control();
|
|
1764
1818
|
init_pty_helper_fix();
|
|
1819
|
+
init_single_instance();
|
|
1765
1820
|
init_tmux();
|
|
1766
1821
|
init_heartbeat();
|
|
1767
1822
|
init_file_explorer();
|
|
@@ -1775,6 +1830,13 @@ var init_index = __esm({
|
|
|
1775
1830
|
}
|
|
1776
1831
|
config = loadConfig();
|
|
1777
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
|
+
}
|
|
1778
1840
|
ensurePtyHelperExecutable();
|
|
1779
1841
|
process.on("unhandledRejection", (reason) => {
|
|
1780
1842
|
logger.error({ reason }, "Unhandled promise rejection (agent kept alive)");
|