cli-remote-agent 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +148 -103
- package/dist/cli.js.map +1 -1
- package/dist/index.js +139 -101
- package/dist/index.js.map +1 -1
- package/dist/setup.js +11 -0
- package/dist/setup.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -119,7 +119,18 @@ function decodeToken(token) {
|
|
|
119
119
|
function ask(rl, question) {
|
|
120
120
|
return new Promise((resolve) => rl.question(question, (answer) => resolve(answer)));
|
|
121
121
|
}
|
|
122
|
+
function normalizeServerUrl(url) {
|
|
123
|
+
const fixed = url.trim().replace(/^(wss?|https?)\/\//i, (_m, proto) => `${proto.toLowerCase()}://`);
|
|
124
|
+
try {
|
|
125
|
+
const u = new URL(fixed);
|
|
126
|
+
if (!/^(wss?:|https?:)$/.test(u.protocol)) throw new Error("unsupported protocol");
|
|
127
|
+
} catch {
|
|
128
|
+
throw new Error(`Invalid server URL: "${url}" (expected e.g. wss://crc.example.com)`);
|
|
129
|
+
}
|
|
130
|
+
return fixed;
|
|
131
|
+
}
|
|
122
132
|
function writeConfig(cfg) {
|
|
133
|
+
cfg.serverUrl = normalizeServerUrl(cfg.serverUrl);
|
|
123
134
|
import_fs2.default.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
124
135
|
import_fs2.default.writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2));
|
|
125
136
|
console.log(`Saved. Start the agent with: crc-agent`);
|
|
@@ -485,8 +496,27 @@ var init_claude_live = __esm({
|
|
|
485
496
|
});
|
|
486
497
|
|
|
487
498
|
// src/tmux.ts
|
|
499
|
+
function resolveTmuxPath() {
|
|
500
|
+
if (cachedTmuxPath !== void 0) return cachedTmuxPath;
|
|
501
|
+
const dirs = (process.env.PATH || "").split(import_path4.default.delimiter).filter(Boolean);
|
|
502
|
+
for (const extra of ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin", "/opt/local/bin"]) {
|
|
503
|
+
if (!dirs.includes(extra)) dirs.push(extra);
|
|
504
|
+
}
|
|
505
|
+
for (const dir of dirs) {
|
|
506
|
+
const candidate = import_path4.default.join(dir, "tmux");
|
|
507
|
+
try {
|
|
508
|
+
import_fs5.default.accessSync(candidate, import_fs5.default.constants.X_OK);
|
|
509
|
+
cachedTmuxPath = candidate;
|
|
510
|
+
return candidate;
|
|
511
|
+
} catch {
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
cachedTmuxPath = null;
|
|
515
|
+
return null;
|
|
516
|
+
}
|
|
488
517
|
function tmuxCmd(args) {
|
|
489
|
-
|
|
518
|
+
if (IS_WIN) return { file: "wsl.exe", args: ["tmux", ...args] };
|
|
519
|
+
return { file: resolveTmuxPath() || "tmux", args };
|
|
490
520
|
}
|
|
491
521
|
async function listTmuxSessions() {
|
|
492
522
|
const fmt = "#{session_name} #{session_windows} #{session_attached} #{session_activity}";
|
|
@@ -525,8 +555,8 @@ async function enrichWithPanesAndClaude(sessions2) {
|
|
|
525
555
|
const { file, args } = tmuxCmd(["list-panes", "-a", "-F", paneFmt]);
|
|
526
556
|
const { stdout } = await execFileAsync2(file, args, { timeout: 6e3 });
|
|
527
557
|
const panes = stdout.split("\n").map((line) => line.replace(/\r$/, "").trim()).filter(Boolean).map((line) => {
|
|
528
|
-
const [session, pid,
|
|
529
|
-
return { session, pid: parseInt(pid, 10) || 0, path:
|
|
558
|
+
const [session, pid, path9, activeFlags] = line.split(" ");
|
|
559
|
+
return { session, pid: parseInt(pid, 10) || 0, path: path9 || "", active: activeFlags === "11" };
|
|
530
560
|
});
|
|
531
561
|
const byName = new Map(sessions2.map((s) => [s.name, s]));
|
|
532
562
|
for (const pane of panes) {
|
|
@@ -548,26 +578,31 @@ async function enrichWithPanesAndClaude(sessions2) {
|
|
|
548
578
|
} catch {
|
|
549
579
|
}
|
|
550
580
|
}
|
|
551
|
-
function
|
|
552
|
-
return `'${v.replace(/'/g, `'\\''`)}'`;
|
|
553
|
-
}
|
|
554
|
-
function buildTmuxLaunch(name, launch, shell) {
|
|
581
|
+
function buildTmuxLaunch(name, launch) {
|
|
555
582
|
const trimmed = launch && launch.trim() ? launch.trim() : void 0;
|
|
556
583
|
if (IS_WIN) {
|
|
557
|
-
const
|
|
558
|
-
if (trimmed)
|
|
559
|
-
return { file: "wsl.exe", args };
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
if (
|
|
563
|
-
|
|
584
|
+
const args2 = ["tmux", "new-session", "-A", "-s", name];
|
|
585
|
+
if (trimmed) args2.push(trimmed);
|
|
586
|
+
return { file: "wsl.exe", args: args2 };
|
|
587
|
+
}
|
|
588
|
+
const tmuxBin = resolveTmuxPath();
|
|
589
|
+
if (!tmuxBin) return null;
|
|
590
|
+
execFileAsync2(tmuxBin, ["set-option", "-g", "aggressive-resize", "on"], { timeout: 4e3 }).catch(
|
|
591
|
+
() => {
|
|
592
|
+
}
|
|
593
|
+
);
|
|
594
|
+
const args = ["new-session", "-A", "-s", name];
|
|
595
|
+
if (trimmed) args.push(trimmed);
|
|
596
|
+
return { file: tmuxBin, args };
|
|
564
597
|
}
|
|
565
|
-
var import_child_process2, import_util2, execFileAsync2, IS_WIN;
|
|
598
|
+
var import_child_process2, import_util2, import_fs5, import_path4, execFileAsync2, IS_WIN, cachedTmuxPath;
|
|
566
599
|
var init_tmux = __esm({
|
|
567
600
|
"src/tmux.ts"() {
|
|
568
601
|
"use strict";
|
|
569
602
|
import_child_process2 = require("child_process");
|
|
570
603
|
import_util2 = require("util");
|
|
604
|
+
import_fs5 = __toESM(require("fs"));
|
|
605
|
+
import_path4 = __toESM(require("path"));
|
|
571
606
|
init_claude_live();
|
|
572
607
|
execFileAsync2 = (0, import_util2.promisify)(import_child_process2.execFile);
|
|
573
608
|
IS_WIN = process.platform === "win32";
|
|
@@ -597,15 +632,15 @@ var init_shell = __esm({
|
|
|
597
632
|
|
|
598
633
|
// src/pty-session.ts
|
|
599
634
|
function ensureInitFiles() {
|
|
600
|
-
const versionFile = (0,
|
|
601
|
-
if ((0,
|
|
635
|
+
const versionFile = (0, import_path5.join)(INIT_DIR, ".version");
|
|
636
|
+
if ((0, import_fs6.existsSync)(versionFile)) {
|
|
602
637
|
try {
|
|
603
|
-
if ((0,
|
|
638
|
+
if ((0, import_fs6.readFileSync)(versionFile, "utf-8").trim() === INIT_VERSION) return;
|
|
604
639
|
} catch {
|
|
605
640
|
}
|
|
606
641
|
}
|
|
607
|
-
(0,
|
|
608
|
-
(0,
|
|
642
|
+
(0, import_fs6.mkdirSync)(ZSH_DIR, { recursive: true });
|
|
643
|
+
(0, import_fs6.writeFileSync)(BASH_INIT, [
|
|
609
644
|
"[ -f ~/.bash_profile ] && source ~/.bash_profile",
|
|
610
645
|
"[ -f ~/.bashrc ] && source ~/.bashrc",
|
|
611
646
|
"__crc_s=$SECONDS",
|
|
@@ -616,11 +651,11 @@ function ensureInitFiles() {
|
|
|
616
651
|
`PROMPT_COMMAND='[ "$__crc_armed" = 1 ] && [ \${__crc_elapsed:-0} -ge ${THRESHOLD} ] && printf "\\a"; __crc_armed=1; eval "$__crc_orig_pc"'`,
|
|
617
652
|
""
|
|
618
653
|
].join("\n"), "utf-8");
|
|
619
|
-
(0,
|
|
654
|
+
(0, import_fs6.writeFileSync)(ZSH_ENV, [
|
|
620
655
|
'[ -f "${ZDOTDIR_ORIG:-$HOME}/.zshenv" ] && source "${ZDOTDIR_ORIG:-$HOME}/.zshenv"',
|
|
621
656
|
""
|
|
622
657
|
].join("\n"), "utf-8");
|
|
623
|
-
(0,
|
|
658
|
+
(0, import_fs6.writeFileSync)(ZSH_RC, [
|
|
624
659
|
'[ -f "${ZDOTDIR_ORIG:-$HOME}/.zshrc" ] && source "${ZDOTDIR_ORIG:-$HOME}/.zshrc"',
|
|
625
660
|
"__crc_s=$SECONDS",
|
|
626
661
|
"__crc_armed=0",
|
|
@@ -633,36 +668,36 @@ function ensureInitFiles() {
|
|
|
633
668
|
"fi",
|
|
634
669
|
""
|
|
635
670
|
].join("\n"), "utf-8");
|
|
636
|
-
(0,
|
|
671
|
+
(0, import_fs6.writeFileSync)(versionFile, INIT_VERSION, "utf-8");
|
|
637
672
|
}
|
|
638
673
|
function resolveCwd(cwd) {
|
|
639
674
|
const candidates = process.platform === "win32" ? [cwd, (0, import_os3.homedir)(), process.env.USERPROFILE, process.env.HOME] : [cwd, (0, import_os3.homedir)(), process.env.HOME, process.env.USERPROFILE];
|
|
640
675
|
for (const c of candidates) {
|
|
641
676
|
if (c) {
|
|
642
677
|
try {
|
|
643
|
-
if ((0,
|
|
678
|
+
if ((0, import_fs6.existsSync)(c)) return c;
|
|
644
679
|
} catch {
|
|
645
680
|
}
|
|
646
681
|
}
|
|
647
682
|
}
|
|
648
683
|
return (0, import_os3.homedir)();
|
|
649
684
|
}
|
|
650
|
-
var pty,
|
|
685
|
+
var pty, import_fs6, import_path5, import_os3, THRESHOLD, INIT_DIR, BASH_INIT, ZSH_DIR, ZSH_RC, ZSH_ENV, INIT_VERSION, PtySession;
|
|
651
686
|
var init_pty_session = __esm({
|
|
652
687
|
"src/pty-session.ts"() {
|
|
653
688
|
"use strict";
|
|
654
689
|
pty = __toESM(require("node-pty"));
|
|
655
|
-
|
|
656
|
-
|
|
690
|
+
import_fs6 = require("fs");
|
|
691
|
+
import_path5 = require("path");
|
|
657
692
|
import_os3 = require("os");
|
|
658
693
|
init_src();
|
|
659
694
|
init_shell();
|
|
660
695
|
THRESHOLD = 3;
|
|
661
|
-
INIT_DIR = (0,
|
|
662
|
-
BASH_INIT = (0,
|
|
663
|
-
ZSH_DIR = (0,
|
|
664
|
-
ZSH_RC = (0,
|
|
665
|
-
ZSH_ENV = (0,
|
|
696
|
+
INIT_DIR = (0, import_path5.join)((0, import_os3.tmpdir)(), "crc-shell-init");
|
|
697
|
+
BASH_INIT = (0, import_path5.join)(INIT_DIR, "bashrc");
|
|
698
|
+
ZSH_DIR = (0, import_path5.join)(INIT_DIR, "zsh");
|
|
699
|
+
ZSH_RC = (0, import_path5.join)(ZSH_DIR, ".zshrc");
|
|
700
|
+
ZSH_ENV = (0, import_path5.join)(ZSH_DIR, ".zshenv");
|
|
666
701
|
INIT_VERSION = "2";
|
|
667
702
|
PtySession = class {
|
|
668
703
|
id;
|
|
@@ -892,18 +927,18 @@ function buildHeartbeat(homeDir) {
|
|
|
892
927
|
memoryUsage: Math.round((totalMem - freeMem) / totalMem * 100),
|
|
893
928
|
uptime: Math.round(import_os4.default.uptime()),
|
|
894
929
|
activeSessions: getActiveSessionCount(),
|
|
895
|
-
pathSeparator:
|
|
930
|
+
pathSeparator: import_path6.default.sep,
|
|
896
931
|
homeDirectory: homeDir || import_os4.default.homedir(),
|
|
897
932
|
rootPaths: getRootPaths(),
|
|
898
933
|
capabilities: { terminal: true, fileTransfer: false }
|
|
899
934
|
};
|
|
900
935
|
}
|
|
901
|
-
var import_os4,
|
|
936
|
+
var import_os4, import_path6, import_child_process4, prevCpu;
|
|
902
937
|
var init_heartbeat = __esm({
|
|
903
938
|
"src/heartbeat.ts"() {
|
|
904
939
|
"use strict";
|
|
905
940
|
import_os4 = __toESM(require("os"));
|
|
906
|
-
|
|
941
|
+
import_path6 = __toESM(require("path"));
|
|
907
942
|
import_child_process4 = require("child_process");
|
|
908
943
|
init_terminal_manager();
|
|
909
944
|
prevCpu = null;
|
|
@@ -912,28 +947,28 @@ var init_heartbeat = __esm({
|
|
|
912
947
|
|
|
913
948
|
// src/file-explorer.ts
|
|
914
949
|
function isInSecretDir(resolved) {
|
|
915
|
-
const rel =
|
|
916
|
-
return rel === "" || !rel.startsWith(".." +
|
|
950
|
+
const rel = import_path7.default.relative(SECRET_DIR, resolved);
|
|
951
|
+
return rel === "" || !rel.startsWith(".." + import_path7.default.sep) && rel !== ".." && !import_path7.default.isAbsolute(rel);
|
|
917
952
|
}
|
|
918
953
|
function listDirectory(dirPath) {
|
|
919
954
|
try {
|
|
920
|
-
const resolved =
|
|
955
|
+
const resolved = import_path7.default.resolve(dirPath);
|
|
921
956
|
if (isInSecretDir(resolved)) {
|
|
922
957
|
return { entries: [], error: "Access denied" };
|
|
923
958
|
}
|
|
924
|
-
if (!
|
|
959
|
+
if (!import_fs7.default.existsSync(resolved)) {
|
|
925
960
|
return { entries: [], error: "Path does not exist" };
|
|
926
961
|
}
|
|
927
|
-
const stat =
|
|
962
|
+
const stat = import_fs7.default.statSync(resolved);
|
|
928
963
|
if (!stat.isDirectory()) {
|
|
929
964
|
return { entries: [], error: "Not a directory" };
|
|
930
965
|
}
|
|
931
|
-
const dirents =
|
|
966
|
+
const dirents = import_fs7.default.readdirSync(resolved, { withFileTypes: true });
|
|
932
967
|
const entries = [];
|
|
933
968
|
for (const dirent of dirents) {
|
|
934
969
|
try {
|
|
935
|
-
const fullPath =
|
|
936
|
-
const s =
|
|
970
|
+
const fullPath = import_path7.default.join(resolved, dirent.name);
|
|
971
|
+
const s = import_fs7.default.statSync(fullPath);
|
|
937
972
|
entries.push({
|
|
938
973
|
name: dirent.name,
|
|
939
974
|
isDirectory: dirent.isDirectory(),
|
|
@@ -955,22 +990,22 @@ function listDirectory(dirPath) {
|
|
|
955
990
|
}
|
|
956
991
|
async function downloadFile(filePath, serverUrl, secret, agentId) {
|
|
957
992
|
try {
|
|
958
|
-
const resolved =
|
|
993
|
+
const resolved = import_path7.default.resolve(filePath);
|
|
959
994
|
if (isInSecretDir(resolved)) {
|
|
960
995
|
return { error: "Access denied" };
|
|
961
996
|
}
|
|
962
|
-
if (!
|
|
997
|
+
if (!import_fs7.default.existsSync(resolved)) {
|
|
963
998
|
return { error: "File does not exist" };
|
|
964
999
|
}
|
|
965
|
-
const stat =
|
|
1000
|
+
const stat = import_fs7.default.statSync(resolved);
|
|
966
1001
|
if (stat.isDirectory()) {
|
|
967
1002
|
return { error: "Cannot download a directory" };
|
|
968
1003
|
}
|
|
969
1004
|
if (stat.size > FILE_MAX_SIZE) {
|
|
970
1005
|
return { error: `File too large (max ${FILE_MAX_SIZE} bytes)` };
|
|
971
1006
|
}
|
|
972
|
-
const fileName =
|
|
973
|
-
const fileStream =
|
|
1007
|
+
const fileName = import_path7.default.basename(resolved);
|
|
1008
|
+
const fileStream = import_fs7.default.createReadStream(resolved);
|
|
974
1009
|
const baseUrl = serverUrl.replace("wss://", "https://").replace("ws://", "http://");
|
|
975
1010
|
const url = `${baseUrl}/api/files/receive`;
|
|
976
1011
|
const res = await fetch(url, {
|
|
@@ -999,21 +1034,21 @@ async function downloadFile(filePath, serverUrl, secret, agentId) {
|
|
|
999
1034
|
return { error: err.message };
|
|
1000
1035
|
}
|
|
1001
1036
|
}
|
|
1002
|
-
var
|
|
1037
|
+
var import_fs7, import_path7, import_os5, SECRET_DIR;
|
|
1003
1038
|
var init_file_explorer = __esm({
|
|
1004
1039
|
"src/file-explorer.ts"() {
|
|
1005
1040
|
"use strict";
|
|
1006
|
-
|
|
1007
|
-
|
|
1041
|
+
import_fs7 = __toESM(require("fs"));
|
|
1042
|
+
import_path7 = __toESM(require("path"));
|
|
1008
1043
|
import_os5 = __toESM(require("os"));
|
|
1009
1044
|
init_logger();
|
|
1010
1045
|
init_src();
|
|
1011
|
-
SECRET_DIR =
|
|
1046
|
+
SECRET_DIR = import_path7.default.join(import_os5.default.homedir(), ".crc-agent");
|
|
1012
1047
|
}
|
|
1013
1048
|
});
|
|
1014
1049
|
|
|
1015
1050
|
// src/vpn-manager.ts
|
|
1016
|
-
function
|
|
1051
|
+
function shq(value) {
|
|
1017
1052
|
return value.replace(/'/g, "'\\''");
|
|
1018
1053
|
}
|
|
1019
1054
|
function psq(value) {
|
|
@@ -1031,11 +1066,11 @@ async function runCmd(cmd2, shell) {
|
|
|
1031
1066
|
}
|
|
1032
1067
|
function getConfigPath(profile) {
|
|
1033
1068
|
const file = profile.configFile || `${profile.id}.conf`;
|
|
1034
|
-
if (
|
|
1035
|
-
return
|
|
1069
|
+
if (import_path8.default.isAbsolute(file)) return file;
|
|
1070
|
+
return import_path8.default.join(VPN_DIR, file);
|
|
1036
1071
|
}
|
|
1037
1072
|
async function getScutilStatus(serviceName) {
|
|
1038
|
-
const { stdout } = await runCmd(`scutil --nc status '${
|
|
1073
|
+
const { stdout } = await runCmd(`scutil --nc status '${shq(serviceName)}'`);
|
|
1039
1074
|
const firstLine = stdout.trim().split("\n")[0];
|
|
1040
1075
|
if (firstLine === "Connected") return "connected";
|
|
1041
1076
|
if (firstLine === "Connecting") return "connecting";
|
|
@@ -1043,11 +1078,11 @@ async function getScutilStatus(serviceName) {
|
|
|
1043
1078
|
return "disconnected";
|
|
1044
1079
|
}
|
|
1045
1080
|
async function scutilStart(serviceName) {
|
|
1046
|
-
const { stderr } = await runCmd(`scutil --nc start '${
|
|
1081
|
+
const { stderr } = await runCmd(`scutil --nc start '${shq(serviceName)}'`);
|
|
1047
1082
|
return stderr || null;
|
|
1048
1083
|
}
|
|
1049
1084
|
async function scutilStop(serviceName) {
|
|
1050
|
-
const { stderr } = await runCmd(`scutil --nc stop '${
|
|
1085
|
+
const { stderr } = await runCmd(`scutil --nc stop '${shq(serviceName)}'`);
|
|
1051
1086
|
return stderr || null;
|
|
1052
1087
|
}
|
|
1053
1088
|
async function runOsascript(script) {
|
|
@@ -1096,7 +1131,7 @@ async function getWireGuardStatus(profile) {
|
|
|
1096
1131
|
return getScutilStatus(profile.serviceName);
|
|
1097
1132
|
}
|
|
1098
1133
|
const tunnelName = profile.tunnelName || profile.id;
|
|
1099
|
-
const { stdout } = await runCmd(`wg show '${
|
|
1134
|
+
const { stdout } = await runCmd(`wg show '${shq(tunnelName)}' 2>/dev/null`);
|
|
1100
1135
|
return stdout.trim() ? "connected" : "disconnected";
|
|
1101
1136
|
}
|
|
1102
1137
|
async function getOpenVpnStatus(profile) {
|
|
@@ -1148,7 +1183,7 @@ async function connectWireGuard(profile) {
|
|
|
1148
1183
|
const tunnelName = profile.tunnelName || profile.id;
|
|
1149
1184
|
if (isWindows) {
|
|
1150
1185
|
const confPath2 = getConfigPath(profile);
|
|
1151
|
-
if (!
|
|
1186
|
+
if (!import_fs8.default.existsSync(confPath2)) {
|
|
1152
1187
|
return `Config file not found: ${confPath2}`;
|
|
1153
1188
|
}
|
|
1154
1189
|
const script = `
|
|
@@ -1168,13 +1203,13 @@ async function connectWireGuard(profile) {
|
|
|
1168
1203
|
return scutilStart(profile.serviceName);
|
|
1169
1204
|
}
|
|
1170
1205
|
const confPath = getConfigPath(profile);
|
|
1171
|
-
const { stderr } = await runCmd(`sudo wg-quick up '${
|
|
1206
|
+
const { stderr } = await runCmd(`sudo wg-quick up '${shq(confPath)}'`);
|
|
1172
1207
|
return stderr || null;
|
|
1173
1208
|
}
|
|
1174
1209
|
async function connectOpenVpn(profile) {
|
|
1175
1210
|
if (isWindows) {
|
|
1176
1211
|
const confPath2 = getConfigPath(profile);
|
|
1177
|
-
if (!
|
|
1212
|
+
if (!import_fs8.default.existsSync(confPath2)) {
|
|
1178
1213
|
return `Config file not found: ${confPath2}`;
|
|
1179
1214
|
}
|
|
1180
1215
|
const connector = "C:\\Program Files\\OpenVPN Connect\\ovpnconnector.exe";
|
|
@@ -1201,16 +1236,16 @@ async function connectOpenVpn(profile) {
|
|
|
1201
1236
|
return scutilStart(profile.serviceName);
|
|
1202
1237
|
}
|
|
1203
1238
|
const confPath = getConfigPath(profile);
|
|
1204
|
-
if (!
|
|
1239
|
+
if (!import_fs8.default.existsSync(confPath)) {
|
|
1205
1240
|
return `Config file not found: ${confPath}`;
|
|
1206
1241
|
}
|
|
1207
|
-
const { stderr } = await runCmd(`sudo openvpn --config '${
|
|
1242
|
+
const { stderr } = await runCmd(`sudo openvpn --config '${shq(confPath)}' --daemon`);
|
|
1208
1243
|
return stderr || null;
|
|
1209
1244
|
}
|
|
1210
1245
|
async function connectAzure(profile) {
|
|
1211
1246
|
if (isWindows) {
|
|
1212
1247
|
const confPath = getConfigPath(profile);
|
|
1213
|
-
if (!
|
|
1248
|
+
if (!import_fs8.default.existsSync(confPath)) {
|
|
1214
1249
|
return `Config file not found: ${confPath}`;
|
|
1215
1250
|
}
|
|
1216
1251
|
await runCmd(`& 'AzureVpn.exe' -i '${psq(confPath)}' 2>&1`);
|
|
@@ -1254,7 +1289,7 @@ async function disconnectWireGuard(profile) {
|
|
|
1254
1289
|
return scutilStop(profile.serviceName);
|
|
1255
1290
|
}
|
|
1256
1291
|
const confPath = getConfigPath(profile);
|
|
1257
|
-
const { stderr } = await runCmd(`sudo wg-quick down '${
|
|
1292
|
+
const { stderr } = await runCmd(`sudo wg-quick down '${shq(confPath)}'`);
|
|
1258
1293
|
return stderr || null;
|
|
1259
1294
|
}
|
|
1260
1295
|
async function disconnectOpenVpn(profile) {
|
|
@@ -1330,19 +1365,19 @@ async function disconnectVpn(configs, profileId) {
|
|
|
1330
1365
|
}
|
|
1331
1366
|
return profiles;
|
|
1332
1367
|
}
|
|
1333
|
-
var import_child_process5, import_util3,
|
|
1368
|
+
var import_child_process5, import_util3, import_fs8, import_path8, import_os6, execAsync, isWindows, VPN_DIR;
|
|
1334
1369
|
var init_vpn_manager = __esm({
|
|
1335
1370
|
"src/vpn-manager.ts"() {
|
|
1336
1371
|
"use strict";
|
|
1337
1372
|
import_child_process5 = require("child_process");
|
|
1338
1373
|
import_util3 = require("util");
|
|
1339
|
-
|
|
1340
|
-
|
|
1374
|
+
import_fs8 = __toESM(require("fs"));
|
|
1375
|
+
import_path8 = __toESM(require("path"));
|
|
1341
1376
|
import_os6 = __toESM(require("os"));
|
|
1342
1377
|
init_logger();
|
|
1343
1378
|
execAsync = (0, import_util3.promisify)(import_child_process5.exec);
|
|
1344
1379
|
isWindows = process.platform === "win32";
|
|
1345
|
-
VPN_DIR =
|
|
1380
|
+
VPN_DIR = import_path8.default.join(import_os6.default.homedir(), ".crc-agent", "vpn");
|
|
1346
1381
|
}
|
|
1347
1382
|
});
|
|
1348
1383
|
|
|
@@ -1377,7 +1412,7 @@ var init_claude_path = __esm({
|
|
|
1377
1412
|
// src/claude-sessions.ts
|
|
1378
1413
|
async function parseSessionFile(filePath) {
|
|
1379
1414
|
try {
|
|
1380
|
-
const stream =
|
|
1415
|
+
const stream = import_fs9.default.createReadStream(filePath, { encoding: "utf-8" });
|
|
1381
1416
|
const rl = import_readline2.default.createInterface({ input: stream, crlfDelay: Infinity });
|
|
1382
1417
|
let firstMessage = "";
|
|
1383
1418
|
let lastTimestamp = "";
|
|
@@ -1425,17 +1460,17 @@ async function parseSessionFile(filePath) {
|
|
|
1425
1460
|
}
|
|
1426
1461
|
async function listClaudeSessions(filterProjectPath) {
|
|
1427
1462
|
const sessions2 = [];
|
|
1428
|
-
if (!
|
|
1463
|
+
if (!import_fs9.default.existsSync(CLAUDE_PROJECTS_DIR)) return sessions2;
|
|
1429
1464
|
const isDirSafe = (d) => {
|
|
1430
1465
|
try {
|
|
1431
|
-
return
|
|
1466
|
+
return import_fs9.default.statSync(import_path9.default.join(CLAUDE_PROJECTS_DIR, d)).isDirectory();
|
|
1432
1467
|
} catch {
|
|
1433
1468
|
return false;
|
|
1434
1469
|
}
|
|
1435
1470
|
};
|
|
1436
1471
|
let allEntries;
|
|
1437
1472
|
try {
|
|
1438
|
-
allEntries =
|
|
1473
|
+
allEntries = import_fs9.default.readdirSync(CLAUDE_PROJECTS_DIR);
|
|
1439
1474
|
} catch {
|
|
1440
1475
|
return sessions2;
|
|
1441
1476
|
}
|
|
@@ -1448,16 +1483,16 @@ async function listClaudeSessions(filterProjectPath) {
|
|
|
1448
1483
|
projectDirs = allEntries.filter((d) => isDirSafe(d) && !d.startsWith("-private-"));
|
|
1449
1484
|
}
|
|
1450
1485
|
for (const dirName of projectDirs) {
|
|
1451
|
-
const dirPath =
|
|
1486
|
+
const dirPath = import_path9.default.join(CLAUDE_PROJECTS_DIR, dirName);
|
|
1452
1487
|
let files;
|
|
1453
1488
|
try {
|
|
1454
|
-
files =
|
|
1489
|
+
files = import_fs9.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
|
|
1455
1490
|
} catch {
|
|
1456
1491
|
continue;
|
|
1457
1492
|
}
|
|
1458
1493
|
for (const file of files) {
|
|
1459
1494
|
const sessionId = file.replace(".jsonl", "");
|
|
1460
|
-
const parsed = await parseSessionFile(
|
|
1495
|
+
const parsed = await parseSessionFile(import_path9.default.join(dirPath, file));
|
|
1461
1496
|
if (!parsed) continue;
|
|
1462
1497
|
const projectPath = parsed.cwd || dirName.replace(/^-/, "/").replace(/-/g, "/");
|
|
1463
1498
|
sessions2.push({
|
|
@@ -1475,31 +1510,31 @@ async function listClaudeSessions(filterProjectPath) {
|
|
|
1475
1510
|
sessions2.sort((a, b) => b.lastTimestamp > a.lastTimestamp ? 1 : -1);
|
|
1476
1511
|
return sessions2;
|
|
1477
1512
|
}
|
|
1478
|
-
var
|
|
1513
|
+
var import_fs9, import_path9, import_os7, import_readline2, CLAUDE_PROJECTS_DIR;
|
|
1479
1514
|
var init_claude_sessions = __esm({
|
|
1480
1515
|
"src/claude-sessions.ts"() {
|
|
1481
1516
|
"use strict";
|
|
1482
|
-
|
|
1483
|
-
|
|
1517
|
+
import_fs9 = __toESM(require("fs"));
|
|
1518
|
+
import_path9 = __toESM(require("path"));
|
|
1484
1519
|
import_os7 = __toESM(require("os"));
|
|
1485
1520
|
import_readline2 = __toESM(require("readline"));
|
|
1486
1521
|
init_logger();
|
|
1487
1522
|
init_claude_path();
|
|
1488
|
-
CLAUDE_PROJECTS_DIR =
|
|
1523
|
+
CLAUDE_PROJECTS_DIR = import_path9.default.join(import_os7.default.homedir(), ".claude", "projects");
|
|
1489
1524
|
}
|
|
1490
1525
|
});
|
|
1491
1526
|
|
|
1492
1527
|
// src/claude-conversation.ts
|
|
1493
1528
|
function findProjectDir(projectPath) {
|
|
1494
1529
|
const encoded = encodeProjectPath(projectPath);
|
|
1495
|
-
const dirPath =
|
|
1496
|
-
if (
|
|
1497
|
-
if (!
|
|
1530
|
+
const dirPath = import_path10.default.join(CLAUDE_PROJECTS_DIR2, encoded);
|
|
1531
|
+
if (import_fs10.default.existsSync(dirPath)) return dirPath;
|
|
1532
|
+
if (!import_fs10.default.existsSync(CLAUDE_PROJECTS_DIR2)) return null;
|
|
1498
1533
|
let allDirs;
|
|
1499
1534
|
try {
|
|
1500
|
-
allDirs =
|
|
1535
|
+
allDirs = import_fs10.default.readdirSync(CLAUDE_PROJECTS_DIR2).filter((d) => {
|
|
1501
1536
|
try {
|
|
1502
|
-
return
|
|
1537
|
+
return import_fs10.default.statSync(import_path10.default.join(CLAUDE_PROJECTS_DIR2, d)).isDirectory();
|
|
1503
1538
|
} catch {
|
|
1504
1539
|
return false;
|
|
1505
1540
|
}
|
|
@@ -1509,23 +1544,23 @@ function findProjectDir(projectPath) {
|
|
|
1509
1544
|
}
|
|
1510
1545
|
const matches = findProjectDirs(allDirs, projectPath);
|
|
1511
1546
|
if (matches.length > 0) {
|
|
1512
|
-
return
|
|
1547
|
+
return import_path10.default.join(CLAUDE_PROJECTS_DIR2, matches[0]);
|
|
1513
1548
|
}
|
|
1514
1549
|
return null;
|
|
1515
1550
|
}
|
|
1516
1551
|
function findSessionFileById(sessionId) {
|
|
1517
1552
|
if (!/^[A-Za-z0-9._-]+$/.test(sessionId)) return null;
|
|
1518
|
-
if (!
|
|
1553
|
+
if (!import_fs10.default.existsSync(CLAUDE_PROJECTS_DIR2)) return null;
|
|
1519
1554
|
let dirs;
|
|
1520
1555
|
try {
|
|
1521
|
-
dirs =
|
|
1556
|
+
dirs = import_fs10.default.readdirSync(CLAUDE_PROJECTS_DIR2);
|
|
1522
1557
|
} catch {
|
|
1523
1558
|
return null;
|
|
1524
1559
|
}
|
|
1525
1560
|
for (const dir of dirs) {
|
|
1526
|
-
const candidate =
|
|
1561
|
+
const candidate = import_path10.default.join(CLAUDE_PROJECTS_DIR2, dir, `${sessionId}.jsonl`);
|
|
1527
1562
|
try {
|
|
1528
|
-
if (
|
|
1563
|
+
if (import_fs10.default.statSync(candidate).isFile()) return candidate;
|
|
1529
1564
|
} catch {
|
|
1530
1565
|
}
|
|
1531
1566
|
}
|
|
@@ -1536,20 +1571,20 @@ function findLatestSessionFile(projectPath) {
|
|
|
1536
1571
|
if (!dirPath) return null;
|
|
1537
1572
|
let entries;
|
|
1538
1573
|
try {
|
|
1539
|
-
entries =
|
|
1574
|
+
entries = import_fs10.default.readdirSync(dirPath);
|
|
1540
1575
|
} catch {
|
|
1541
1576
|
return null;
|
|
1542
1577
|
}
|
|
1543
1578
|
const files = entries.filter((f) => f.endsWith(".jsonl") && !f.includes("/")).map((f) => {
|
|
1544
1579
|
try {
|
|
1545
|
-
return { name: f, mtime:
|
|
1580
|
+
return { name: f, mtime: import_fs10.default.statSync(import_path10.default.join(dirPath, f)).mtimeMs };
|
|
1546
1581
|
} catch {
|
|
1547
1582
|
return null;
|
|
1548
1583
|
}
|
|
1549
1584
|
}).filter((f) => f !== null).sort((a, b) => b.mtime - a.mtime);
|
|
1550
1585
|
if (files.length === 0) return null;
|
|
1551
1586
|
return {
|
|
1552
|
-
filePath:
|
|
1587
|
+
filePath: import_path10.default.join(dirPath, files[0].name),
|
|
1553
1588
|
sessionId: files[0].name.replace(".jsonl", "")
|
|
1554
1589
|
};
|
|
1555
1590
|
}
|
|
@@ -1559,8 +1594,8 @@ async function readConversation(projectPath, afterLine = 0, specificSessionId) {
|
|
|
1559
1594
|
if (specificSessionId) {
|
|
1560
1595
|
sessionId = specificSessionId;
|
|
1561
1596
|
const dirPath = findProjectDir(projectPath);
|
|
1562
|
-
const direct = dirPath ?
|
|
1563
|
-
if (direct &&
|
|
1597
|
+
const direct = dirPath ? import_path10.default.join(dirPath, `${specificSessionId}.jsonl`) : null;
|
|
1598
|
+
if (direct && import_fs10.default.existsSync(direct)) {
|
|
1564
1599
|
filePath = direct;
|
|
1565
1600
|
} else {
|
|
1566
1601
|
const byId = findSessionFileById(specificSessionId);
|
|
@@ -1574,7 +1609,7 @@ async function readConversation(projectPath, afterLine = 0, specificSessionId) {
|
|
|
1574
1609
|
sessionId = found.sessionId;
|
|
1575
1610
|
}
|
|
1576
1611
|
try {
|
|
1577
|
-
const stream =
|
|
1612
|
+
const stream = import_fs10.default.createReadStream(filePath, { encoding: "utf-8" });
|
|
1578
1613
|
const rl = import_readline3.default.createInterface({ input: stream, crlfDelay: Infinity });
|
|
1579
1614
|
const messages = [];
|
|
1580
1615
|
let lineNum = 0;
|
|
@@ -1655,17 +1690,17 @@ async function readConversation(projectPath, afterLine = 0, specificSessionId) {
|
|
|
1655
1690
|
return null;
|
|
1656
1691
|
}
|
|
1657
1692
|
}
|
|
1658
|
-
var
|
|
1693
|
+
var import_fs10, import_path10, import_os8, import_readline3, CLAUDE_PROJECTS_DIR2;
|
|
1659
1694
|
var init_claude_conversation = __esm({
|
|
1660
1695
|
"src/claude-conversation.ts"() {
|
|
1661
1696
|
"use strict";
|
|
1662
|
-
|
|
1663
|
-
|
|
1697
|
+
import_fs10 = __toESM(require("fs"));
|
|
1698
|
+
import_path10 = __toESM(require("path"));
|
|
1664
1699
|
import_os8 = __toESM(require("os"));
|
|
1665
1700
|
import_readline3 = __toESM(require("readline"));
|
|
1666
1701
|
init_logger();
|
|
1667
1702
|
init_claude_path();
|
|
1668
|
-
CLAUDE_PROJECTS_DIR2 =
|
|
1703
|
+
CLAUDE_PROJECTS_DIR2 = import_path10.default.join(import_os8.default.homedir(), ".claude", "projects");
|
|
1669
1704
|
}
|
|
1670
1705
|
});
|
|
1671
1706
|
|
|
@@ -1682,7 +1717,6 @@ var init_index = __esm({
|
|
|
1682
1717
|
init_claude_plugin_installer();
|
|
1683
1718
|
init_local_control();
|
|
1684
1719
|
init_tmux();
|
|
1685
|
-
init_shell();
|
|
1686
1720
|
init_heartbeat();
|
|
1687
1721
|
init_file_explorer();
|
|
1688
1722
|
init_vpn_manager();
|
|
@@ -1754,7 +1788,18 @@ var init_index = __esm({
|
|
|
1754
1788
|
socket.on(TERMINAL_OPEN, (payload) => {
|
|
1755
1789
|
const { sessionId, cols, rows, tmux, launch } = payload;
|
|
1756
1790
|
if (!sessionId) return;
|
|
1757
|
-
|
|
1791
|
+
let launchSpec;
|
|
1792
|
+
if (tmux) {
|
|
1793
|
+
const spec = buildTmuxLaunch(tmux, launch);
|
|
1794
|
+
if (!spec) {
|
|
1795
|
+
const msg = "\r\n\x1B[31mtmux is not installed on this machine.\x1B[0m\r\nInstall it to use tmux mirroring:\r\n macOS: brew install tmux\r\n Linux: sudo apt install tmux (or your package manager)\r\n\r\n";
|
|
1796
|
+
socket.emit(TERMINAL_OUTPUT, { sessionId, data: msg });
|
|
1797
|
+
socket.emit(TERMINAL_EXIT, { sessionId, exitCode: 1 });
|
|
1798
|
+
logger.warn({ sessionId, tmux }, "tmux launch requested but tmux not found");
|
|
1799
|
+
return;
|
|
1800
|
+
}
|
|
1801
|
+
launchSpec = spec;
|
|
1802
|
+
}
|
|
1758
1803
|
createTerminalSession(
|
|
1759
1804
|
sessionId,
|
|
1760
1805
|
cols,
|