cli-remote-agent 1.0.5 → 1.0.7
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 +190 -109
- package/dist/cli.js.map +1 -1
- package/dist/index.js +183 -106
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -251,18 +251,58 @@ function startLocalControl(port, onHook) {
|
|
|
251
251
|
return server;
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
// src/pty-helper-fix.ts
|
|
255
|
+
var import_node_module = require("module");
|
|
256
|
+
var import_node_child_process = require("child_process");
|
|
257
|
+
var import_fs3 = __toESM(require("fs"));
|
|
258
|
+
var import_path3 = __toESM(require("path"));
|
|
259
|
+
function ensurePtyHelperExecutable() {
|
|
260
|
+
if (process.platform === "win32") return;
|
|
261
|
+
try {
|
|
262
|
+
const nodeRequire = (0, import_node_module.createRequire)(__filename);
|
|
263
|
+
const ptyMain = nodeRequire.resolve("node-pty");
|
|
264
|
+
const ptyDir = import_path3.default.dirname(import_path3.default.dirname(ptyMain));
|
|
265
|
+
const candidates = [import_path3.default.join(ptyDir, "build", "Release", "spawn-helper")];
|
|
266
|
+
const prebuilds = import_path3.default.join(ptyDir, "prebuilds");
|
|
267
|
+
try {
|
|
268
|
+
for (const d of import_fs3.default.readdirSync(prebuilds)) {
|
|
269
|
+
candidates.push(import_path3.default.join(prebuilds, d, "spawn-helper"));
|
|
270
|
+
}
|
|
271
|
+
} catch {
|
|
272
|
+
}
|
|
273
|
+
for (const file of candidates) {
|
|
274
|
+
try {
|
|
275
|
+
const mode = import_fs3.default.statSync(file).mode;
|
|
276
|
+
if (!(mode & 73)) {
|
|
277
|
+
import_fs3.default.chmodSync(file, mode | 493);
|
|
278
|
+
logger.info({ file }, "Restored execute bit on node-pty spawn-helper");
|
|
279
|
+
}
|
|
280
|
+
} catch {
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
if (process.platform === "darwin") {
|
|
284
|
+
(0, import_node_child_process.execFile)("xattr", ["-dr", "com.apple.quarantine", ptyDir], () => {
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
} catch (err) {
|
|
288
|
+
logger.warn({ error: err?.message }, "Could not verify node-pty spawn-helper permissions");
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
254
292
|
// src/tmux.ts
|
|
255
293
|
var import_child_process2 = require("child_process");
|
|
256
294
|
var import_util2 = require("util");
|
|
295
|
+
var import_fs5 = __toESM(require("fs"));
|
|
296
|
+
var import_path5 = __toESM(require("path"));
|
|
257
297
|
|
|
258
298
|
// src/claude-live.ts
|
|
259
|
-
var
|
|
260
|
-
var
|
|
299
|
+
var import_fs4 = __toESM(require("fs"));
|
|
300
|
+
var import_path4 = __toESM(require("path"));
|
|
261
301
|
var import_os2 = __toESM(require("os"));
|
|
262
302
|
var import_child_process = require("child_process");
|
|
263
303
|
var import_util = require("util");
|
|
264
304
|
var execFileAsync = (0, import_util.promisify)(import_child_process.execFile);
|
|
265
|
-
var LIVE_SESSIONS_DIR =
|
|
305
|
+
var LIVE_SESSIONS_DIR = import_path4.default.join(import_os2.default.homedir(), ".claude", "sessions");
|
|
266
306
|
function isAlive(pid) {
|
|
267
307
|
try {
|
|
268
308
|
process.kill(pid, 0);
|
|
@@ -274,14 +314,14 @@ function isAlive(pid) {
|
|
|
274
314
|
function getLiveClaudeSessions() {
|
|
275
315
|
let files;
|
|
276
316
|
try {
|
|
277
|
-
files =
|
|
317
|
+
files = import_fs4.default.readdirSync(LIVE_SESSIONS_DIR).filter((f) => f.endsWith(".json"));
|
|
278
318
|
} catch {
|
|
279
319
|
return [];
|
|
280
320
|
}
|
|
281
321
|
const out = [];
|
|
282
322
|
for (const f of files) {
|
|
283
323
|
try {
|
|
284
|
-
const obj = JSON.parse(
|
|
324
|
+
const obj = JSON.parse(import_fs4.default.readFileSync(import_path4.default.join(LIVE_SESSIONS_DIR, f), "utf-8"));
|
|
285
325
|
if (typeof obj?.pid !== "number" || !isAlive(obj.pid)) continue;
|
|
286
326
|
out.push({
|
|
287
327
|
pid: obj.pid,
|
|
@@ -320,8 +360,28 @@ function isDescendantOf(pid, ancestor, parents) {
|
|
|
320
360
|
// src/tmux.ts
|
|
321
361
|
var execFileAsync2 = (0, import_util2.promisify)(import_child_process2.execFile);
|
|
322
362
|
var IS_WIN = process.platform === "win32";
|
|
363
|
+
var cachedTmuxPath;
|
|
364
|
+
function resolveTmuxPath() {
|
|
365
|
+
if (cachedTmuxPath !== void 0) return cachedTmuxPath;
|
|
366
|
+
const dirs = (process.env.PATH || "").split(import_path5.default.delimiter).filter(Boolean);
|
|
367
|
+
for (const extra of ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin", "/opt/local/bin"]) {
|
|
368
|
+
if (!dirs.includes(extra)) dirs.push(extra);
|
|
369
|
+
}
|
|
370
|
+
for (const dir of dirs) {
|
|
371
|
+
const candidate = import_path5.default.join(dir, "tmux");
|
|
372
|
+
try {
|
|
373
|
+
import_fs5.default.accessSync(candidate, import_fs5.default.constants.X_OK);
|
|
374
|
+
cachedTmuxPath = candidate;
|
|
375
|
+
return candidate;
|
|
376
|
+
} catch {
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
cachedTmuxPath = null;
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
323
382
|
function tmuxCmd(args) {
|
|
324
|
-
|
|
383
|
+
if (IS_WIN) return { file: "wsl.exe", args: ["tmux", ...args] };
|
|
384
|
+
return { file: resolveTmuxPath() || "tmux", args };
|
|
325
385
|
}
|
|
326
386
|
async function listTmuxSessions() {
|
|
327
387
|
const fmt = "#{session_name} #{session_windows} #{session_attached} #{session_activity}";
|
|
@@ -360,8 +420,8 @@ async function enrichWithPanesAndClaude(sessions2) {
|
|
|
360
420
|
const { file, args } = tmuxCmd(["list-panes", "-a", "-F", paneFmt]);
|
|
361
421
|
const { stdout } = await execFileAsync2(file, args, { timeout: 6e3 });
|
|
362
422
|
const panes = stdout.split("\n").map((line) => line.replace(/\r$/, "").trim()).filter(Boolean).map((line) => {
|
|
363
|
-
const [session, pid,
|
|
364
|
-
return { session, pid: parseInt(pid, 10) || 0, path:
|
|
423
|
+
const [session, pid, path10, activeFlags] = line.split(" ");
|
|
424
|
+
return { session, pid: parseInt(pid, 10) || 0, path: path10 || "", active: activeFlags === "11" };
|
|
365
425
|
});
|
|
366
426
|
const byName = new Map(sessions2.map((s) => [s.name, s]));
|
|
367
427
|
for (const pane of panes) {
|
|
@@ -383,21 +443,35 @@ async function enrichWithPanesAndClaude(sessions2) {
|
|
|
383
443
|
} catch {
|
|
384
444
|
}
|
|
385
445
|
}
|
|
386
|
-
function
|
|
387
|
-
return `'${v.replace(/'/g, `'\\''`)}'`;
|
|
388
|
-
}
|
|
389
|
-
function buildTmuxLaunch(name, launch, shell) {
|
|
446
|
+
function buildTmuxLaunch(name, launch) {
|
|
390
447
|
const trimmed = launch && launch.trim() ? launch.trim() : void 0;
|
|
391
448
|
if (IS_WIN) {
|
|
392
|
-
const
|
|
393
|
-
if (trimmed)
|
|
394
|
-
return { file: "wsl.exe", args };
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
if (
|
|
398
|
-
|
|
449
|
+
const args2 = ["tmux", "new-session", "-A", "-s", name];
|
|
450
|
+
if (trimmed) args2.push(trimmed);
|
|
451
|
+
return { file: "wsl.exe", args: args2 };
|
|
452
|
+
}
|
|
453
|
+
const tmuxBin = resolveTmuxPath();
|
|
454
|
+
if (!tmuxBin) return null;
|
|
455
|
+
execFileAsync2(tmuxBin, ["set-option", "-g", "aggressive-resize", "on"], { timeout: 4e3 }).catch(
|
|
456
|
+
() => {
|
|
457
|
+
}
|
|
458
|
+
);
|
|
459
|
+
const args = ["new-session", "-A", "-s", name];
|
|
460
|
+
if (trimmed) args.push(trimmed);
|
|
461
|
+
return { file: tmuxBin, args };
|
|
399
462
|
}
|
|
400
463
|
|
|
464
|
+
// src/heartbeat.ts
|
|
465
|
+
var import_os4 = __toESM(require("os"));
|
|
466
|
+
var import_path7 = __toESM(require("path"));
|
|
467
|
+
var import_child_process4 = require("child_process");
|
|
468
|
+
|
|
469
|
+
// src/pty-session.ts
|
|
470
|
+
var pty = __toESM(require("node-pty"));
|
|
471
|
+
var import_fs6 = require("fs");
|
|
472
|
+
var import_path6 = require("path");
|
|
473
|
+
var import_os3 = require("os");
|
|
474
|
+
|
|
401
475
|
// src/shell.ts
|
|
402
476
|
var import_child_process3 = require("child_process");
|
|
403
477
|
function detectShell(preference) {
|
|
@@ -413,33 +487,24 @@ function detectShell(preference) {
|
|
|
413
487
|
return process.env.SHELL || "/bin/bash";
|
|
414
488
|
}
|
|
415
489
|
|
|
416
|
-
// src/heartbeat.ts
|
|
417
|
-
var import_os4 = __toESM(require("os"));
|
|
418
|
-
var import_path5 = __toESM(require("path"));
|
|
419
|
-
var import_child_process4 = require("child_process");
|
|
420
|
-
|
|
421
490
|
// src/pty-session.ts
|
|
422
|
-
var pty = __toESM(require("node-pty"));
|
|
423
|
-
var import_fs4 = require("fs");
|
|
424
|
-
var import_path4 = require("path");
|
|
425
|
-
var import_os3 = require("os");
|
|
426
491
|
var THRESHOLD = 3;
|
|
427
|
-
var INIT_DIR = (0,
|
|
428
|
-
var BASH_INIT = (0,
|
|
429
|
-
var ZSH_DIR = (0,
|
|
430
|
-
var ZSH_RC = (0,
|
|
431
|
-
var ZSH_ENV = (0,
|
|
492
|
+
var INIT_DIR = (0, import_path6.join)((0, import_os3.tmpdir)(), "crc-shell-init");
|
|
493
|
+
var BASH_INIT = (0, import_path6.join)(INIT_DIR, "bashrc");
|
|
494
|
+
var ZSH_DIR = (0, import_path6.join)(INIT_DIR, "zsh");
|
|
495
|
+
var ZSH_RC = (0, import_path6.join)(ZSH_DIR, ".zshrc");
|
|
496
|
+
var ZSH_ENV = (0, import_path6.join)(ZSH_DIR, ".zshenv");
|
|
432
497
|
var INIT_VERSION = "2";
|
|
433
498
|
function ensureInitFiles() {
|
|
434
|
-
const versionFile = (0,
|
|
435
|
-
if ((0,
|
|
499
|
+
const versionFile = (0, import_path6.join)(INIT_DIR, ".version");
|
|
500
|
+
if ((0, import_fs6.existsSync)(versionFile)) {
|
|
436
501
|
try {
|
|
437
|
-
if ((0,
|
|
502
|
+
if ((0, import_fs6.readFileSync)(versionFile, "utf-8").trim() === INIT_VERSION) return;
|
|
438
503
|
} catch {
|
|
439
504
|
}
|
|
440
505
|
}
|
|
441
|
-
(0,
|
|
442
|
-
(0,
|
|
506
|
+
(0, import_fs6.mkdirSync)(ZSH_DIR, { recursive: true });
|
|
507
|
+
(0, import_fs6.writeFileSync)(BASH_INIT, [
|
|
443
508
|
"[ -f ~/.bash_profile ] && source ~/.bash_profile",
|
|
444
509
|
"[ -f ~/.bashrc ] && source ~/.bashrc",
|
|
445
510
|
"__crc_s=$SECONDS",
|
|
@@ -450,11 +515,11 @@ function ensureInitFiles() {
|
|
|
450
515
|
`PROMPT_COMMAND='[ "$__crc_armed" = 1 ] && [ \${__crc_elapsed:-0} -ge ${THRESHOLD} ] && printf "\\a"; __crc_armed=1; eval "$__crc_orig_pc"'`,
|
|
451
516
|
""
|
|
452
517
|
].join("\n"), "utf-8");
|
|
453
|
-
(0,
|
|
518
|
+
(0, import_fs6.writeFileSync)(ZSH_ENV, [
|
|
454
519
|
'[ -f "${ZDOTDIR_ORIG:-$HOME}/.zshenv" ] && source "${ZDOTDIR_ORIG:-$HOME}/.zshenv"',
|
|
455
520
|
""
|
|
456
521
|
].join("\n"), "utf-8");
|
|
457
|
-
(0,
|
|
522
|
+
(0, import_fs6.writeFileSync)(ZSH_RC, [
|
|
458
523
|
'[ -f "${ZDOTDIR_ORIG:-$HOME}/.zshrc" ] && source "${ZDOTDIR_ORIG:-$HOME}/.zshrc"',
|
|
459
524
|
"__crc_s=$SECONDS",
|
|
460
525
|
"__crc_armed=0",
|
|
@@ -467,14 +532,14 @@ function ensureInitFiles() {
|
|
|
467
532
|
"fi",
|
|
468
533
|
""
|
|
469
534
|
].join("\n"), "utf-8");
|
|
470
|
-
(0,
|
|
535
|
+
(0, import_fs6.writeFileSync)(versionFile, INIT_VERSION, "utf-8");
|
|
471
536
|
}
|
|
472
537
|
function resolveCwd(cwd) {
|
|
473
538
|
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];
|
|
474
539
|
for (const c of candidates) {
|
|
475
540
|
if (c) {
|
|
476
541
|
try {
|
|
477
|
-
if ((0,
|
|
542
|
+
if ((0, import_fs6.existsSync)(c)) return c;
|
|
478
543
|
} catch {
|
|
479
544
|
}
|
|
480
545
|
}
|
|
@@ -700,7 +765,7 @@ function buildHeartbeat(homeDir) {
|
|
|
700
765
|
memoryUsage: Math.round((totalMem - freeMem) / totalMem * 100),
|
|
701
766
|
uptime: Math.round(import_os4.default.uptime()),
|
|
702
767
|
activeSessions: getActiveSessionCount(),
|
|
703
|
-
pathSeparator:
|
|
768
|
+
pathSeparator: import_path7.default.sep,
|
|
704
769
|
homeDirectory: homeDir || import_os4.default.homedir(),
|
|
705
770
|
rootPaths: getRootPaths(),
|
|
706
771
|
capabilities: { terminal: true, fileTransfer: false }
|
|
@@ -708,33 +773,33 @@ function buildHeartbeat(homeDir) {
|
|
|
708
773
|
}
|
|
709
774
|
|
|
710
775
|
// src/file-explorer.ts
|
|
711
|
-
var
|
|
712
|
-
var
|
|
776
|
+
var import_fs7 = __toESM(require("fs"));
|
|
777
|
+
var import_path8 = __toESM(require("path"));
|
|
713
778
|
var import_os5 = __toESM(require("os"));
|
|
714
|
-
var SECRET_DIR =
|
|
779
|
+
var SECRET_DIR = import_path8.default.join(import_os5.default.homedir(), ".crc-agent");
|
|
715
780
|
function isInSecretDir(resolved) {
|
|
716
|
-
const rel =
|
|
717
|
-
return rel === "" || !rel.startsWith(".." +
|
|
781
|
+
const rel = import_path8.default.relative(SECRET_DIR, resolved);
|
|
782
|
+
return rel === "" || !rel.startsWith(".." + import_path8.default.sep) && rel !== ".." && !import_path8.default.isAbsolute(rel);
|
|
718
783
|
}
|
|
719
784
|
function listDirectory(dirPath) {
|
|
720
785
|
try {
|
|
721
|
-
const resolved =
|
|
786
|
+
const resolved = import_path8.default.resolve(dirPath);
|
|
722
787
|
if (isInSecretDir(resolved)) {
|
|
723
788
|
return { entries: [], error: "Access denied" };
|
|
724
789
|
}
|
|
725
|
-
if (!
|
|
790
|
+
if (!import_fs7.default.existsSync(resolved)) {
|
|
726
791
|
return { entries: [], error: "Path does not exist" };
|
|
727
792
|
}
|
|
728
|
-
const stat =
|
|
793
|
+
const stat = import_fs7.default.statSync(resolved);
|
|
729
794
|
if (!stat.isDirectory()) {
|
|
730
795
|
return { entries: [], error: "Not a directory" };
|
|
731
796
|
}
|
|
732
|
-
const dirents =
|
|
797
|
+
const dirents = import_fs7.default.readdirSync(resolved, { withFileTypes: true });
|
|
733
798
|
const entries = [];
|
|
734
799
|
for (const dirent of dirents) {
|
|
735
800
|
try {
|
|
736
|
-
const fullPath =
|
|
737
|
-
const s =
|
|
801
|
+
const fullPath = import_path8.default.join(resolved, dirent.name);
|
|
802
|
+
const s = import_fs7.default.statSync(fullPath);
|
|
738
803
|
entries.push({
|
|
739
804
|
name: dirent.name,
|
|
740
805
|
isDirectory: dirent.isDirectory(),
|
|
@@ -756,22 +821,22 @@ function listDirectory(dirPath) {
|
|
|
756
821
|
}
|
|
757
822
|
async function downloadFile(filePath, serverUrl, secret, agentId) {
|
|
758
823
|
try {
|
|
759
|
-
const resolved =
|
|
824
|
+
const resolved = import_path8.default.resolve(filePath);
|
|
760
825
|
if (isInSecretDir(resolved)) {
|
|
761
826
|
return { error: "Access denied" };
|
|
762
827
|
}
|
|
763
|
-
if (!
|
|
828
|
+
if (!import_fs7.default.existsSync(resolved)) {
|
|
764
829
|
return { error: "File does not exist" };
|
|
765
830
|
}
|
|
766
|
-
const stat =
|
|
831
|
+
const stat = import_fs7.default.statSync(resolved);
|
|
767
832
|
if (stat.isDirectory()) {
|
|
768
833
|
return { error: "Cannot download a directory" };
|
|
769
834
|
}
|
|
770
835
|
if (stat.size > FILE_MAX_SIZE) {
|
|
771
836
|
return { error: `File too large (max ${FILE_MAX_SIZE} bytes)` };
|
|
772
837
|
}
|
|
773
|
-
const fileName =
|
|
774
|
-
const fileStream =
|
|
838
|
+
const fileName = import_path8.default.basename(resolved);
|
|
839
|
+
const fileStream = import_fs7.default.createReadStream(resolved);
|
|
775
840
|
const baseUrl = serverUrl.replace("wss://", "https://").replace("ws://", "http://");
|
|
776
841
|
const url = `${baseUrl}/api/files/receive`;
|
|
777
842
|
const res = await fetch(url, {
|
|
@@ -804,13 +869,13 @@ async function downloadFile(filePath, serverUrl, secret, agentId) {
|
|
|
804
869
|
// src/vpn-manager.ts
|
|
805
870
|
var import_child_process5 = require("child_process");
|
|
806
871
|
var import_util3 = require("util");
|
|
807
|
-
var
|
|
808
|
-
var
|
|
872
|
+
var import_fs8 = __toESM(require("fs"));
|
|
873
|
+
var import_path9 = __toESM(require("path"));
|
|
809
874
|
var import_os6 = __toESM(require("os"));
|
|
810
875
|
var execAsync = (0, import_util3.promisify)(import_child_process5.exec);
|
|
811
876
|
var isWindows = process.platform === "win32";
|
|
812
|
-
var VPN_DIR =
|
|
813
|
-
function
|
|
877
|
+
var VPN_DIR = import_path9.default.join(import_os6.default.homedir(), ".crc-agent", "vpn");
|
|
878
|
+
function shq(value) {
|
|
814
879
|
return value.replace(/'/g, "'\\''");
|
|
815
880
|
}
|
|
816
881
|
function psq(value) {
|
|
@@ -828,11 +893,11 @@ async function runCmd(cmd, shell) {
|
|
|
828
893
|
}
|
|
829
894
|
function getConfigPath(profile) {
|
|
830
895
|
const file = profile.configFile || `${profile.id}.conf`;
|
|
831
|
-
if (
|
|
832
|
-
return
|
|
896
|
+
if (import_path9.default.isAbsolute(file)) return file;
|
|
897
|
+
return import_path9.default.join(VPN_DIR, file);
|
|
833
898
|
}
|
|
834
899
|
async function getScutilStatus(serviceName) {
|
|
835
|
-
const { stdout } = await runCmd(`scutil --nc status '${
|
|
900
|
+
const { stdout } = await runCmd(`scutil --nc status '${shq(serviceName)}'`);
|
|
836
901
|
const firstLine = stdout.trim().split("\n")[0];
|
|
837
902
|
if (firstLine === "Connected") return "connected";
|
|
838
903
|
if (firstLine === "Connecting") return "connecting";
|
|
@@ -840,11 +905,11 @@ async function getScutilStatus(serviceName) {
|
|
|
840
905
|
return "disconnected";
|
|
841
906
|
}
|
|
842
907
|
async function scutilStart(serviceName) {
|
|
843
|
-
const { stderr } = await runCmd(`scutil --nc start '${
|
|
908
|
+
const { stderr } = await runCmd(`scutil --nc start '${shq(serviceName)}'`);
|
|
844
909
|
return stderr || null;
|
|
845
910
|
}
|
|
846
911
|
async function scutilStop(serviceName) {
|
|
847
|
-
const { stderr } = await runCmd(`scutil --nc stop '${
|
|
912
|
+
const { stderr } = await runCmd(`scutil --nc stop '${shq(serviceName)}'`);
|
|
848
913
|
return stderr || null;
|
|
849
914
|
}
|
|
850
915
|
async function runOsascript(script) {
|
|
@@ -893,7 +958,7 @@ async function getWireGuardStatus(profile) {
|
|
|
893
958
|
return getScutilStatus(profile.serviceName);
|
|
894
959
|
}
|
|
895
960
|
const tunnelName = profile.tunnelName || profile.id;
|
|
896
|
-
const { stdout } = await runCmd(`wg show '${
|
|
961
|
+
const { stdout } = await runCmd(`wg show '${shq(tunnelName)}' 2>/dev/null`);
|
|
897
962
|
return stdout.trim() ? "connected" : "disconnected";
|
|
898
963
|
}
|
|
899
964
|
async function getOpenVpnStatus(profile) {
|
|
@@ -945,7 +1010,7 @@ async function connectWireGuard(profile) {
|
|
|
945
1010
|
const tunnelName = profile.tunnelName || profile.id;
|
|
946
1011
|
if (isWindows) {
|
|
947
1012
|
const confPath2 = getConfigPath(profile);
|
|
948
|
-
if (!
|
|
1013
|
+
if (!import_fs8.default.existsSync(confPath2)) {
|
|
949
1014
|
return `Config file not found: ${confPath2}`;
|
|
950
1015
|
}
|
|
951
1016
|
const script = `
|
|
@@ -965,13 +1030,13 @@ async function connectWireGuard(profile) {
|
|
|
965
1030
|
return scutilStart(profile.serviceName);
|
|
966
1031
|
}
|
|
967
1032
|
const confPath = getConfigPath(profile);
|
|
968
|
-
const { stderr } = await runCmd(`sudo wg-quick up '${
|
|
1033
|
+
const { stderr } = await runCmd(`sudo wg-quick up '${shq(confPath)}'`);
|
|
969
1034
|
return stderr || null;
|
|
970
1035
|
}
|
|
971
1036
|
async function connectOpenVpn(profile) {
|
|
972
1037
|
if (isWindows) {
|
|
973
1038
|
const confPath2 = getConfigPath(profile);
|
|
974
|
-
if (!
|
|
1039
|
+
if (!import_fs8.default.existsSync(confPath2)) {
|
|
975
1040
|
return `Config file not found: ${confPath2}`;
|
|
976
1041
|
}
|
|
977
1042
|
const connector = "C:\\Program Files\\OpenVPN Connect\\ovpnconnector.exe";
|
|
@@ -998,16 +1063,16 @@ async function connectOpenVpn(profile) {
|
|
|
998
1063
|
return scutilStart(profile.serviceName);
|
|
999
1064
|
}
|
|
1000
1065
|
const confPath = getConfigPath(profile);
|
|
1001
|
-
if (!
|
|
1066
|
+
if (!import_fs8.default.existsSync(confPath)) {
|
|
1002
1067
|
return `Config file not found: ${confPath}`;
|
|
1003
1068
|
}
|
|
1004
|
-
const { stderr } = await runCmd(`sudo openvpn --config '${
|
|
1069
|
+
const { stderr } = await runCmd(`sudo openvpn --config '${shq(confPath)}' --daemon`);
|
|
1005
1070
|
return stderr || null;
|
|
1006
1071
|
}
|
|
1007
1072
|
async function connectAzure(profile) {
|
|
1008
1073
|
if (isWindows) {
|
|
1009
1074
|
const confPath = getConfigPath(profile);
|
|
1010
|
-
if (!
|
|
1075
|
+
if (!import_fs8.default.existsSync(confPath)) {
|
|
1011
1076
|
return `Config file not found: ${confPath}`;
|
|
1012
1077
|
}
|
|
1013
1078
|
await runCmd(`& 'AzureVpn.exe' -i '${psq(confPath)}' 2>&1`);
|
|
@@ -1051,7 +1116,7 @@ async function disconnectWireGuard(profile) {
|
|
|
1051
1116
|
return scutilStop(profile.serviceName);
|
|
1052
1117
|
}
|
|
1053
1118
|
const confPath = getConfigPath(profile);
|
|
1054
|
-
const { stderr } = await runCmd(`sudo wg-quick down '${
|
|
1119
|
+
const { stderr } = await runCmd(`sudo wg-quick down '${shq(confPath)}'`);
|
|
1055
1120
|
return stderr || null;
|
|
1056
1121
|
}
|
|
1057
1122
|
async function disconnectOpenVpn(profile) {
|
|
@@ -1129,8 +1194,8 @@ async function disconnectVpn(configs, profileId) {
|
|
|
1129
1194
|
}
|
|
1130
1195
|
|
|
1131
1196
|
// src/claude-sessions.ts
|
|
1132
|
-
var
|
|
1133
|
-
var
|
|
1197
|
+
var import_fs9 = __toESM(require("fs"));
|
|
1198
|
+
var import_path10 = __toESM(require("path"));
|
|
1134
1199
|
var import_os7 = __toESM(require("os"));
|
|
1135
1200
|
var import_readline = __toESM(require("readline"));
|
|
1136
1201
|
|
|
@@ -1158,10 +1223,10 @@ function findProjectDirs(allDirs, filterProjectPath) {
|
|
|
1158
1223
|
}
|
|
1159
1224
|
|
|
1160
1225
|
// src/claude-sessions.ts
|
|
1161
|
-
var CLAUDE_PROJECTS_DIR =
|
|
1226
|
+
var CLAUDE_PROJECTS_DIR = import_path10.default.join(import_os7.default.homedir(), ".claude", "projects");
|
|
1162
1227
|
async function parseSessionFile(filePath) {
|
|
1163
1228
|
try {
|
|
1164
|
-
const stream =
|
|
1229
|
+
const stream = import_fs9.default.createReadStream(filePath, { encoding: "utf-8" });
|
|
1165
1230
|
const rl = import_readline.default.createInterface({ input: stream, crlfDelay: Infinity });
|
|
1166
1231
|
let firstMessage = "";
|
|
1167
1232
|
let lastTimestamp = "";
|
|
@@ -1209,17 +1274,17 @@ async function parseSessionFile(filePath) {
|
|
|
1209
1274
|
}
|
|
1210
1275
|
async function listClaudeSessions(filterProjectPath) {
|
|
1211
1276
|
const sessions2 = [];
|
|
1212
|
-
if (!
|
|
1277
|
+
if (!import_fs9.default.existsSync(CLAUDE_PROJECTS_DIR)) return sessions2;
|
|
1213
1278
|
const isDirSafe = (d) => {
|
|
1214
1279
|
try {
|
|
1215
|
-
return
|
|
1280
|
+
return import_fs9.default.statSync(import_path10.default.join(CLAUDE_PROJECTS_DIR, d)).isDirectory();
|
|
1216
1281
|
} catch {
|
|
1217
1282
|
return false;
|
|
1218
1283
|
}
|
|
1219
1284
|
};
|
|
1220
1285
|
let allEntries;
|
|
1221
1286
|
try {
|
|
1222
|
-
allEntries =
|
|
1287
|
+
allEntries = import_fs9.default.readdirSync(CLAUDE_PROJECTS_DIR);
|
|
1223
1288
|
} catch {
|
|
1224
1289
|
return sessions2;
|
|
1225
1290
|
}
|
|
@@ -1232,16 +1297,16 @@ async function listClaudeSessions(filterProjectPath) {
|
|
|
1232
1297
|
projectDirs = allEntries.filter((d) => isDirSafe(d) && !d.startsWith("-private-"));
|
|
1233
1298
|
}
|
|
1234
1299
|
for (const dirName of projectDirs) {
|
|
1235
|
-
const dirPath =
|
|
1300
|
+
const dirPath = import_path10.default.join(CLAUDE_PROJECTS_DIR, dirName);
|
|
1236
1301
|
let files;
|
|
1237
1302
|
try {
|
|
1238
|
-
files =
|
|
1303
|
+
files = import_fs9.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
|
|
1239
1304
|
} catch {
|
|
1240
1305
|
continue;
|
|
1241
1306
|
}
|
|
1242
1307
|
for (const file of files) {
|
|
1243
1308
|
const sessionId = file.replace(".jsonl", "");
|
|
1244
|
-
const parsed = await parseSessionFile(
|
|
1309
|
+
const parsed = await parseSessionFile(import_path10.default.join(dirPath, file));
|
|
1245
1310
|
if (!parsed) continue;
|
|
1246
1311
|
const projectPath = parsed.cwd || dirName.replace(/^-/, "/").replace(/-/g, "/");
|
|
1247
1312
|
sessions2.push({
|
|
@@ -1261,21 +1326,21 @@ async function listClaudeSessions(filterProjectPath) {
|
|
|
1261
1326
|
}
|
|
1262
1327
|
|
|
1263
1328
|
// src/claude-conversation.ts
|
|
1264
|
-
var
|
|
1265
|
-
var
|
|
1329
|
+
var import_fs10 = __toESM(require("fs"));
|
|
1330
|
+
var import_path11 = __toESM(require("path"));
|
|
1266
1331
|
var import_os8 = __toESM(require("os"));
|
|
1267
1332
|
var import_readline2 = __toESM(require("readline"));
|
|
1268
|
-
var CLAUDE_PROJECTS_DIR2 =
|
|
1333
|
+
var CLAUDE_PROJECTS_DIR2 = import_path11.default.join(import_os8.default.homedir(), ".claude", "projects");
|
|
1269
1334
|
function findProjectDir(projectPath) {
|
|
1270
1335
|
const encoded = encodeProjectPath(projectPath);
|
|
1271
|
-
const dirPath =
|
|
1272
|
-
if (
|
|
1273
|
-
if (!
|
|
1336
|
+
const dirPath = import_path11.default.join(CLAUDE_PROJECTS_DIR2, encoded);
|
|
1337
|
+
if (import_fs10.default.existsSync(dirPath)) return dirPath;
|
|
1338
|
+
if (!import_fs10.default.existsSync(CLAUDE_PROJECTS_DIR2)) return null;
|
|
1274
1339
|
let allDirs;
|
|
1275
1340
|
try {
|
|
1276
|
-
allDirs =
|
|
1341
|
+
allDirs = import_fs10.default.readdirSync(CLAUDE_PROJECTS_DIR2).filter((d) => {
|
|
1277
1342
|
try {
|
|
1278
|
-
return
|
|
1343
|
+
return import_fs10.default.statSync(import_path11.default.join(CLAUDE_PROJECTS_DIR2, d)).isDirectory();
|
|
1279
1344
|
} catch {
|
|
1280
1345
|
return false;
|
|
1281
1346
|
}
|
|
@@ -1285,23 +1350,23 @@ function findProjectDir(projectPath) {
|
|
|
1285
1350
|
}
|
|
1286
1351
|
const matches = findProjectDirs(allDirs, projectPath);
|
|
1287
1352
|
if (matches.length > 0) {
|
|
1288
|
-
return
|
|
1353
|
+
return import_path11.default.join(CLAUDE_PROJECTS_DIR2, matches[0]);
|
|
1289
1354
|
}
|
|
1290
1355
|
return null;
|
|
1291
1356
|
}
|
|
1292
1357
|
function findSessionFileById(sessionId) {
|
|
1293
1358
|
if (!/^[A-Za-z0-9._-]+$/.test(sessionId)) return null;
|
|
1294
|
-
if (!
|
|
1359
|
+
if (!import_fs10.default.existsSync(CLAUDE_PROJECTS_DIR2)) return null;
|
|
1295
1360
|
let dirs;
|
|
1296
1361
|
try {
|
|
1297
|
-
dirs =
|
|
1362
|
+
dirs = import_fs10.default.readdirSync(CLAUDE_PROJECTS_DIR2);
|
|
1298
1363
|
} catch {
|
|
1299
1364
|
return null;
|
|
1300
1365
|
}
|
|
1301
1366
|
for (const dir of dirs) {
|
|
1302
|
-
const candidate =
|
|
1367
|
+
const candidate = import_path11.default.join(CLAUDE_PROJECTS_DIR2, dir, `${sessionId}.jsonl`);
|
|
1303
1368
|
try {
|
|
1304
|
-
if (
|
|
1369
|
+
if (import_fs10.default.statSync(candidate).isFile()) return candidate;
|
|
1305
1370
|
} catch {
|
|
1306
1371
|
}
|
|
1307
1372
|
}
|
|
@@ -1312,20 +1377,20 @@ function findLatestSessionFile(projectPath) {
|
|
|
1312
1377
|
if (!dirPath) return null;
|
|
1313
1378
|
let entries;
|
|
1314
1379
|
try {
|
|
1315
|
-
entries =
|
|
1380
|
+
entries = import_fs10.default.readdirSync(dirPath);
|
|
1316
1381
|
} catch {
|
|
1317
1382
|
return null;
|
|
1318
1383
|
}
|
|
1319
1384
|
const files = entries.filter((f) => f.endsWith(".jsonl") && !f.includes("/")).map((f) => {
|
|
1320
1385
|
try {
|
|
1321
|
-
return { name: f, mtime:
|
|
1386
|
+
return { name: f, mtime: import_fs10.default.statSync(import_path11.default.join(dirPath, f)).mtimeMs };
|
|
1322
1387
|
} catch {
|
|
1323
1388
|
return null;
|
|
1324
1389
|
}
|
|
1325
1390
|
}).filter((f) => f !== null).sort((a, b) => b.mtime - a.mtime);
|
|
1326
1391
|
if (files.length === 0) return null;
|
|
1327
1392
|
return {
|
|
1328
|
-
filePath:
|
|
1393
|
+
filePath: import_path11.default.join(dirPath, files[0].name),
|
|
1329
1394
|
sessionId: files[0].name.replace(".jsonl", "")
|
|
1330
1395
|
};
|
|
1331
1396
|
}
|
|
@@ -1335,8 +1400,8 @@ async function readConversation(projectPath, afterLine = 0, specificSessionId) {
|
|
|
1335
1400
|
if (specificSessionId) {
|
|
1336
1401
|
sessionId = specificSessionId;
|
|
1337
1402
|
const dirPath = findProjectDir(projectPath);
|
|
1338
|
-
const direct = dirPath ?
|
|
1339
|
-
if (direct &&
|
|
1403
|
+
const direct = dirPath ? import_path11.default.join(dirPath, `${specificSessionId}.jsonl`) : null;
|
|
1404
|
+
if (direct && import_fs10.default.existsSync(direct)) {
|
|
1340
1405
|
filePath = direct;
|
|
1341
1406
|
} else {
|
|
1342
1407
|
const byId = findSessionFileById(specificSessionId);
|
|
@@ -1350,7 +1415,7 @@ async function readConversation(projectPath, afterLine = 0, specificSessionId) {
|
|
|
1350
1415
|
sessionId = found.sessionId;
|
|
1351
1416
|
}
|
|
1352
1417
|
try {
|
|
1353
|
-
const stream =
|
|
1418
|
+
const stream = import_fs10.default.createReadStream(filePath, { encoding: "utf-8" });
|
|
1354
1419
|
const rl = import_readline2.default.createInterface({ input: stream, crlfDelay: Infinity });
|
|
1355
1420
|
const messages = [];
|
|
1356
1421
|
let lineNum = 0;
|
|
@@ -1439,6 +1504,7 @@ if (!isConfigured()) {
|
|
|
1439
1504
|
}
|
|
1440
1505
|
var config = loadConfig();
|
|
1441
1506
|
logger.info({ agentId: config.agentId, serverUrl: config.serverUrl }, "Starting agent");
|
|
1507
|
+
ensurePtyHelperExecutable();
|
|
1442
1508
|
process.on("unhandledRejection", (reason) => {
|
|
1443
1509
|
logger.error({ reason }, "Unhandled promise rejection (agent kept alive)");
|
|
1444
1510
|
});
|
|
@@ -1498,7 +1564,18 @@ startLocalControl(LOCAL_CONTROL_PORT, (raw) => {
|
|
|
1498
1564
|
socket.on(TERMINAL_OPEN, (payload) => {
|
|
1499
1565
|
const { sessionId, cols, rows, tmux, launch } = payload;
|
|
1500
1566
|
if (!sessionId) return;
|
|
1501
|
-
|
|
1567
|
+
let launchSpec;
|
|
1568
|
+
if (tmux) {
|
|
1569
|
+
const spec = buildTmuxLaunch(tmux, launch);
|
|
1570
|
+
if (!spec) {
|
|
1571
|
+
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";
|
|
1572
|
+
socket.emit(TERMINAL_OUTPUT, { sessionId, data: msg });
|
|
1573
|
+
socket.emit(TERMINAL_EXIT, { sessionId, exitCode: 1 });
|
|
1574
|
+
logger.warn({ sessionId, tmux }, "tmux launch requested but tmux not found");
|
|
1575
|
+
return;
|
|
1576
|
+
}
|
|
1577
|
+
launchSpec = spec;
|
|
1578
|
+
}
|
|
1502
1579
|
createTerminalSession(
|
|
1503
1580
|
sessionId,
|
|
1504
1581
|
cols,
|