cli-remote-agent 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +231 -111
- package/dist/cli.js.map +1 -1
- package/dist/index.js +214 -99
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -57,6 +57,8 @@ var CLAUDE_SESSIONS_RESULT = "claude:sessions:result";
|
|
|
57
57
|
var CLAUDE_HOOK = "claude:hook";
|
|
58
58
|
var TMUX_LIST = "tmux:list";
|
|
59
59
|
var TMUX_LIST_RESULT = "tmux:list:result";
|
|
60
|
+
var TMUX_KILL = "tmux:kill";
|
|
61
|
+
var TMUX_KILL_RESULT = "tmux:kill:result";
|
|
60
62
|
|
|
61
63
|
// ../shared/src/constants.ts
|
|
62
64
|
var HEARTBEAT_INTERVAL = 15e3;
|
|
@@ -250,9 +252,73 @@ function startLocalControl(port, onHook) {
|
|
|
250
252
|
}
|
|
251
253
|
|
|
252
254
|
// src/tmux.ts
|
|
255
|
+
var import_child_process2 = require("child_process");
|
|
256
|
+
var import_util2 = require("util");
|
|
257
|
+
|
|
258
|
+
// src/claude-live.ts
|
|
259
|
+
var import_fs3 = __toESM(require("fs"));
|
|
260
|
+
var import_path3 = __toESM(require("path"));
|
|
261
|
+
var import_os2 = __toESM(require("os"));
|
|
253
262
|
var import_child_process = require("child_process");
|
|
254
263
|
var import_util = require("util");
|
|
255
264
|
var execFileAsync = (0, import_util.promisify)(import_child_process.execFile);
|
|
265
|
+
var LIVE_SESSIONS_DIR = import_path3.default.join(import_os2.default.homedir(), ".claude", "sessions");
|
|
266
|
+
function isAlive(pid) {
|
|
267
|
+
try {
|
|
268
|
+
process.kill(pid, 0);
|
|
269
|
+
return true;
|
|
270
|
+
} catch {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
function getLiveClaudeSessions() {
|
|
275
|
+
let files;
|
|
276
|
+
try {
|
|
277
|
+
files = import_fs3.default.readdirSync(LIVE_SESSIONS_DIR).filter((f) => f.endsWith(".json"));
|
|
278
|
+
} catch {
|
|
279
|
+
return [];
|
|
280
|
+
}
|
|
281
|
+
const out = [];
|
|
282
|
+
for (const f of files) {
|
|
283
|
+
try {
|
|
284
|
+
const obj = JSON.parse(import_fs3.default.readFileSync(import_path3.default.join(LIVE_SESSIONS_DIR, f), "utf-8"));
|
|
285
|
+
if (typeof obj?.pid !== "number" || !isAlive(obj.pid)) continue;
|
|
286
|
+
out.push({
|
|
287
|
+
pid: obj.pid,
|
|
288
|
+
cwd: typeof obj.cwd === "string" ? obj.cwd : "",
|
|
289
|
+
name: typeof obj.name === "string" ? obj.name : void 0,
|
|
290
|
+
status: typeof obj.status === "string" ? obj.status : void 0,
|
|
291
|
+
updatedAt: typeof obj.updatedAt === "number" ? obj.updatedAt : void 0
|
|
292
|
+
});
|
|
293
|
+
} catch {
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
out.sort((a, b) => (a.updatedAt || 0) - (b.updatedAt || 0));
|
|
297
|
+
return out;
|
|
298
|
+
}
|
|
299
|
+
async function getPidParents() {
|
|
300
|
+
const map = /* @__PURE__ */ new Map();
|
|
301
|
+
try {
|
|
302
|
+
const { stdout } = await execFileAsync("ps", ["-axo", "pid=,ppid="], { timeout: 5e3 });
|
|
303
|
+
for (const line of stdout.split("\n")) {
|
|
304
|
+
const m = line.trim().match(/^(\d+)\s+(\d+)$/);
|
|
305
|
+
if (m) map.set(parseInt(m[1], 10), parseInt(m[2], 10));
|
|
306
|
+
}
|
|
307
|
+
} catch {
|
|
308
|
+
}
|
|
309
|
+
return map;
|
|
310
|
+
}
|
|
311
|
+
function isDescendantOf(pid, ancestor, parents) {
|
|
312
|
+
let cur = pid;
|
|
313
|
+
for (let i = 0; i < 25 && cur !== void 0 && cur > 1; i++) {
|
|
314
|
+
if (cur === ancestor) return true;
|
|
315
|
+
cur = parents.get(cur);
|
|
316
|
+
}
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// src/tmux.ts
|
|
321
|
+
var execFileAsync2 = (0, import_util2.promisify)(import_child_process2.execFile);
|
|
256
322
|
var IS_WIN = process.platform === "win32";
|
|
257
323
|
function tmuxCmd(args) {
|
|
258
324
|
return IS_WIN ? { file: "wsl.exe", args: ["tmux", ...args] } : { file: "tmux", args };
|
|
@@ -261,8 +327,8 @@ async function listTmuxSessions() {
|
|
|
261
327
|
const fmt = "#{session_name} #{session_windows} #{session_attached} #{session_activity}";
|
|
262
328
|
const { file, args } = tmuxCmd(["list-sessions", "-F", fmt]);
|
|
263
329
|
try {
|
|
264
|
-
const { stdout } = await
|
|
265
|
-
|
|
330
|
+
const { stdout } = await execFileAsync2(file, args, { timeout: 6e3 });
|
|
331
|
+
const sessions2 = stdout.split("\n").map((line) => line.replace(/\r$/, "").trim()).filter(Boolean).map((line) => {
|
|
266
332
|
const [name, windows, attached, activity] = line.split(" ");
|
|
267
333
|
return {
|
|
268
334
|
name,
|
|
@@ -271,10 +337,52 @@ async function listTmuxSessions() {
|
|
|
271
337
|
activity: parseInt(activity, 10) || void 0
|
|
272
338
|
};
|
|
273
339
|
});
|
|
340
|
+
await enrichWithPanesAndClaude(sessions2);
|
|
341
|
+
return sessions2;
|
|
274
342
|
} catch {
|
|
275
343
|
return [];
|
|
276
344
|
}
|
|
277
345
|
}
|
|
346
|
+
async function killTmuxSession(name) {
|
|
347
|
+
const { file, args } = tmuxCmd(["kill-session", "-t", `=${name}`]);
|
|
348
|
+
try {
|
|
349
|
+
await execFileAsync2(file, args, { timeout: 6e3 });
|
|
350
|
+
return { ok: true };
|
|
351
|
+
} catch (err) {
|
|
352
|
+
const msg = (err?.stderr || err?.message || "tmux kill failed").toString().trim();
|
|
353
|
+
return { ok: false, error: msg };
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
async function enrichWithPanesAndClaude(sessions2) {
|
|
357
|
+
if (IS_WIN || sessions2.length === 0) return;
|
|
358
|
+
try {
|
|
359
|
+
const paneFmt = "#{session_name} #{pane_pid} #{pane_current_path} #{window_active}#{pane_active}";
|
|
360
|
+
const { file, args } = tmuxCmd(["list-panes", "-a", "-F", paneFmt]);
|
|
361
|
+
const { stdout } = await execFileAsync2(file, args, { timeout: 6e3 });
|
|
362
|
+
const panes = stdout.split("\n").map((line) => line.replace(/\r$/, "").trim()).filter(Boolean).map((line) => {
|
|
363
|
+
const [session, pid, path8, activeFlags] = line.split(" ");
|
|
364
|
+
return { session, pid: parseInt(pid, 10) || 0, path: path8 || "", active: activeFlags === "11" };
|
|
365
|
+
});
|
|
366
|
+
const byName = new Map(sessions2.map((s) => [s.name, s]));
|
|
367
|
+
for (const pane of panes) {
|
|
368
|
+
const s = byName.get(pane.session);
|
|
369
|
+
if (s && (!s.path || pane.active)) s.path = pane.path || s.path;
|
|
370
|
+
}
|
|
371
|
+
const live = getLiveClaudeSessions();
|
|
372
|
+
if (live.length === 0) return;
|
|
373
|
+
const parents = await getPidParents();
|
|
374
|
+
for (const cs of live) {
|
|
375
|
+
const target = parents.size > 0 ? panes.find((p) => p.pid > 0 && isDescendantOf(cs.pid, p.pid, parents)) : panes.find((p) => cs.cwd && p.path === cs.cwd);
|
|
376
|
+
if (!target) continue;
|
|
377
|
+
const s = byName.get(target.session);
|
|
378
|
+
if (!s) continue;
|
|
379
|
+
s.claudeTitle = cs.name;
|
|
380
|
+
s.claudeStatus = cs.status;
|
|
381
|
+
if (cs.cwd) s.path = cs.cwd;
|
|
382
|
+
}
|
|
383
|
+
} catch {
|
|
384
|
+
}
|
|
385
|
+
}
|
|
278
386
|
function shq(v) {
|
|
279
387
|
return `'${v.replace(/'/g, `'\\''`)}'`;
|
|
280
388
|
}
|
|
@@ -291,12 +399,12 @@ function buildTmuxLaunch(name, launch, shell) {
|
|
|
291
399
|
}
|
|
292
400
|
|
|
293
401
|
// src/shell.ts
|
|
294
|
-
var
|
|
402
|
+
var import_child_process3 = require("child_process");
|
|
295
403
|
function detectShell(preference) {
|
|
296
404
|
if (preference !== "auto") return preference;
|
|
297
405
|
if (process.platform === "win32") {
|
|
298
406
|
try {
|
|
299
|
-
(0,
|
|
407
|
+
(0, import_child_process3.execSync)("where pwsh", { stdio: "ignore" });
|
|
300
408
|
return "pwsh.exe";
|
|
301
409
|
} catch {
|
|
302
410
|
return "powershell.exe";
|
|
@@ -306,32 +414,32 @@ function detectShell(preference) {
|
|
|
306
414
|
}
|
|
307
415
|
|
|
308
416
|
// src/heartbeat.ts
|
|
309
|
-
var
|
|
310
|
-
var
|
|
311
|
-
var
|
|
417
|
+
var import_os4 = __toESM(require("os"));
|
|
418
|
+
var import_path5 = __toESM(require("path"));
|
|
419
|
+
var import_child_process4 = require("child_process");
|
|
312
420
|
|
|
313
421
|
// src/pty-session.ts
|
|
314
422
|
var pty = __toESM(require("node-pty"));
|
|
315
|
-
var
|
|
316
|
-
var
|
|
317
|
-
var
|
|
423
|
+
var import_fs4 = require("fs");
|
|
424
|
+
var import_path4 = require("path");
|
|
425
|
+
var import_os3 = require("os");
|
|
318
426
|
var THRESHOLD = 3;
|
|
319
|
-
var INIT_DIR = (0,
|
|
320
|
-
var BASH_INIT = (0,
|
|
321
|
-
var ZSH_DIR = (0,
|
|
322
|
-
var ZSH_RC = (0,
|
|
323
|
-
var ZSH_ENV = (0,
|
|
427
|
+
var INIT_DIR = (0, import_path4.join)((0, import_os3.tmpdir)(), "crc-shell-init");
|
|
428
|
+
var BASH_INIT = (0, import_path4.join)(INIT_DIR, "bashrc");
|
|
429
|
+
var ZSH_DIR = (0, import_path4.join)(INIT_DIR, "zsh");
|
|
430
|
+
var ZSH_RC = (0, import_path4.join)(ZSH_DIR, ".zshrc");
|
|
431
|
+
var ZSH_ENV = (0, import_path4.join)(ZSH_DIR, ".zshenv");
|
|
324
432
|
var INIT_VERSION = "2";
|
|
325
433
|
function ensureInitFiles() {
|
|
326
|
-
const versionFile = (0,
|
|
327
|
-
if ((0,
|
|
434
|
+
const versionFile = (0, import_path4.join)(INIT_DIR, ".version");
|
|
435
|
+
if ((0, import_fs4.existsSync)(versionFile)) {
|
|
328
436
|
try {
|
|
329
|
-
if ((0,
|
|
437
|
+
if ((0, import_fs4.readFileSync)(versionFile, "utf-8").trim() === INIT_VERSION) return;
|
|
330
438
|
} catch {
|
|
331
439
|
}
|
|
332
440
|
}
|
|
333
|
-
(0,
|
|
334
|
-
(0,
|
|
441
|
+
(0, import_fs4.mkdirSync)(ZSH_DIR, { recursive: true });
|
|
442
|
+
(0, import_fs4.writeFileSync)(BASH_INIT, [
|
|
335
443
|
"[ -f ~/.bash_profile ] && source ~/.bash_profile",
|
|
336
444
|
"[ -f ~/.bashrc ] && source ~/.bashrc",
|
|
337
445
|
"__crc_s=$SECONDS",
|
|
@@ -342,11 +450,11 @@ function ensureInitFiles() {
|
|
|
342
450
|
`PROMPT_COMMAND='[ "$__crc_armed" = 1 ] && [ \${__crc_elapsed:-0} -ge ${THRESHOLD} ] && printf "\\a"; __crc_armed=1; eval "$__crc_orig_pc"'`,
|
|
343
451
|
""
|
|
344
452
|
].join("\n"), "utf-8");
|
|
345
|
-
(0,
|
|
453
|
+
(0, import_fs4.writeFileSync)(ZSH_ENV, [
|
|
346
454
|
'[ -f "${ZDOTDIR_ORIG:-$HOME}/.zshenv" ] && source "${ZDOTDIR_ORIG:-$HOME}/.zshenv"',
|
|
347
455
|
""
|
|
348
456
|
].join("\n"), "utf-8");
|
|
349
|
-
(0,
|
|
457
|
+
(0, import_fs4.writeFileSync)(ZSH_RC, [
|
|
350
458
|
'[ -f "${ZDOTDIR_ORIG:-$HOME}/.zshrc" ] && source "${ZDOTDIR_ORIG:-$HOME}/.zshrc"',
|
|
351
459
|
"__crc_s=$SECONDS",
|
|
352
460
|
"__crc_armed=0",
|
|
@@ -359,19 +467,19 @@ function ensureInitFiles() {
|
|
|
359
467
|
"fi",
|
|
360
468
|
""
|
|
361
469
|
].join("\n"), "utf-8");
|
|
362
|
-
(0,
|
|
470
|
+
(0, import_fs4.writeFileSync)(versionFile, INIT_VERSION, "utf-8");
|
|
363
471
|
}
|
|
364
472
|
function resolveCwd(cwd) {
|
|
365
|
-
const candidates = process.platform === "win32" ? [cwd, (0,
|
|
473
|
+
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];
|
|
366
474
|
for (const c of candidates) {
|
|
367
475
|
if (c) {
|
|
368
476
|
try {
|
|
369
|
-
if ((0,
|
|
477
|
+
if ((0, import_fs4.existsSync)(c)) return c;
|
|
370
478
|
} catch {
|
|
371
479
|
}
|
|
372
480
|
}
|
|
373
481
|
}
|
|
374
|
-
return (0,
|
|
482
|
+
return (0, import_os3.homedir)();
|
|
375
483
|
}
|
|
376
484
|
var PtySession = class {
|
|
377
485
|
id;
|
|
@@ -415,7 +523,7 @@ var PtySession = class {
|
|
|
415
523
|
name: "xterm-256color",
|
|
416
524
|
cols,
|
|
417
525
|
rows,
|
|
418
|
-
cwd: (0,
|
|
526
|
+
cwd: (0, import_os3.homedir)(),
|
|
419
527
|
env
|
|
420
528
|
});
|
|
421
529
|
}
|
|
@@ -553,7 +661,7 @@ function reapDetachedSessions(maxIdleMs) {
|
|
|
553
661
|
// src/heartbeat.ts
|
|
554
662
|
var prevCpu = null;
|
|
555
663
|
function getCpuUsage() {
|
|
556
|
-
const cpus =
|
|
664
|
+
const cpus = import_os4.default.cpus();
|
|
557
665
|
let idle = 0;
|
|
558
666
|
let total = 0;
|
|
559
667
|
for (const cpu of cpus) {
|
|
@@ -573,7 +681,7 @@ function getCpuUsage() {
|
|
|
573
681
|
function getRootPaths() {
|
|
574
682
|
if (process.platform === "win32") {
|
|
575
683
|
try {
|
|
576
|
-
const output = (0,
|
|
684
|
+
const output = (0, import_child_process4.execSync)("wmic logicaldisk get name", { encoding: "utf-8" });
|
|
577
685
|
return output.split("\n").map((l) => l.trim()).filter((l) => /^[A-Z]:$/.test(l)).map((l) => l + "\\");
|
|
578
686
|
} catch {
|
|
579
687
|
return ["C:\\"];
|
|
@@ -582,51 +690,51 @@ function getRootPaths() {
|
|
|
582
690
|
return ["/"];
|
|
583
691
|
}
|
|
584
692
|
function buildHeartbeat(homeDir) {
|
|
585
|
-
const totalMem =
|
|
586
|
-
const freeMem =
|
|
693
|
+
const totalMem = import_os4.default.totalmem();
|
|
694
|
+
const freeMem = import_os4.default.freemem();
|
|
587
695
|
return {
|
|
588
|
-
hostname:
|
|
696
|
+
hostname: import_os4.default.hostname(),
|
|
589
697
|
platform: process.platform,
|
|
590
|
-
arch:
|
|
698
|
+
arch: import_os4.default.arch(),
|
|
591
699
|
cpuUsage: getCpuUsage(),
|
|
592
700
|
memoryUsage: Math.round((totalMem - freeMem) / totalMem * 100),
|
|
593
|
-
uptime: Math.round(
|
|
701
|
+
uptime: Math.round(import_os4.default.uptime()),
|
|
594
702
|
activeSessions: getActiveSessionCount(),
|
|
595
|
-
pathSeparator:
|
|
596
|
-
homeDirectory: homeDir ||
|
|
703
|
+
pathSeparator: import_path5.default.sep,
|
|
704
|
+
homeDirectory: homeDir || import_os4.default.homedir(),
|
|
597
705
|
rootPaths: getRootPaths(),
|
|
598
706
|
capabilities: { terminal: true, fileTransfer: false }
|
|
599
707
|
};
|
|
600
708
|
}
|
|
601
709
|
|
|
602
710
|
// src/file-explorer.ts
|
|
603
|
-
var
|
|
604
|
-
var
|
|
605
|
-
var
|
|
606
|
-
var SECRET_DIR =
|
|
711
|
+
var import_fs5 = __toESM(require("fs"));
|
|
712
|
+
var import_path6 = __toESM(require("path"));
|
|
713
|
+
var import_os5 = __toESM(require("os"));
|
|
714
|
+
var SECRET_DIR = import_path6.default.join(import_os5.default.homedir(), ".crc-agent");
|
|
607
715
|
function isInSecretDir(resolved) {
|
|
608
|
-
const rel =
|
|
609
|
-
return rel === "" || !rel.startsWith(".." +
|
|
716
|
+
const rel = import_path6.default.relative(SECRET_DIR, resolved);
|
|
717
|
+
return rel === "" || !rel.startsWith(".." + import_path6.default.sep) && rel !== ".." && !import_path6.default.isAbsolute(rel);
|
|
610
718
|
}
|
|
611
719
|
function listDirectory(dirPath) {
|
|
612
720
|
try {
|
|
613
|
-
const resolved =
|
|
721
|
+
const resolved = import_path6.default.resolve(dirPath);
|
|
614
722
|
if (isInSecretDir(resolved)) {
|
|
615
723
|
return { entries: [], error: "Access denied" };
|
|
616
724
|
}
|
|
617
|
-
if (!
|
|
725
|
+
if (!import_fs5.default.existsSync(resolved)) {
|
|
618
726
|
return { entries: [], error: "Path does not exist" };
|
|
619
727
|
}
|
|
620
|
-
const stat =
|
|
728
|
+
const stat = import_fs5.default.statSync(resolved);
|
|
621
729
|
if (!stat.isDirectory()) {
|
|
622
730
|
return { entries: [], error: "Not a directory" };
|
|
623
731
|
}
|
|
624
|
-
const dirents =
|
|
732
|
+
const dirents = import_fs5.default.readdirSync(resolved, { withFileTypes: true });
|
|
625
733
|
const entries = [];
|
|
626
734
|
for (const dirent of dirents) {
|
|
627
735
|
try {
|
|
628
|
-
const fullPath =
|
|
629
|
-
const s =
|
|
736
|
+
const fullPath = import_path6.default.join(resolved, dirent.name);
|
|
737
|
+
const s = import_fs5.default.statSync(fullPath);
|
|
630
738
|
entries.push({
|
|
631
739
|
name: dirent.name,
|
|
632
740
|
isDirectory: dirent.isDirectory(),
|
|
@@ -648,22 +756,22 @@ function listDirectory(dirPath) {
|
|
|
648
756
|
}
|
|
649
757
|
async function downloadFile(filePath, serverUrl, secret, agentId) {
|
|
650
758
|
try {
|
|
651
|
-
const resolved =
|
|
759
|
+
const resolved = import_path6.default.resolve(filePath);
|
|
652
760
|
if (isInSecretDir(resolved)) {
|
|
653
761
|
return { error: "Access denied" };
|
|
654
762
|
}
|
|
655
|
-
if (!
|
|
763
|
+
if (!import_fs5.default.existsSync(resolved)) {
|
|
656
764
|
return { error: "File does not exist" };
|
|
657
765
|
}
|
|
658
|
-
const stat =
|
|
766
|
+
const stat = import_fs5.default.statSync(resolved);
|
|
659
767
|
if (stat.isDirectory()) {
|
|
660
768
|
return { error: "Cannot download a directory" };
|
|
661
769
|
}
|
|
662
770
|
if (stat.size > FILE_MAX_SIZE) {
|
|
663
771
|
return { error: `File too large (max ${FILE_MAX_SIZE} bytes)` };
|
|
664
772
|
}
|
|
665
|
-
const fileName =
|
|
666
|
-
const fileStream =
|
|
773
|
+
const fileName = import_path6.default.basename(resolved);
|
|
774
|
+
const fileStream = import_fs5.default.createReadStream(resolved);
|
|
667
775
|
const baseUrl = serverUrl.replace("wss://", "https://").replace("ws://", "http://");
|
|
668
776
|
const url = `${baseUrl}/api/files/receive`;
|
|
669
777
|
const res = await fetch(url, {
|
|
@@ -694,14 +802,14 @@ async function downloadFile(filePath, serverUrl, secret, agentId) {
|
|
|
694
802
|
}
|
|
695
803
|
|
|
696
804
|
// src/vpn-manager.ts
|
|
697
|
-
var
|
|
698
|
-
var
|
|
699
|
-
var
|
|
700
|
-
var
|
|
701
|
-
var
|
|
702
|
-
var execAsync = (0,
|
|
805
|
+
var import_child_process5 = require("child_process");
|
|
806
|
+
var import_util3 = require("util");
|
|
807
|
+
var import_fs6 = __toESM(require("fs"));
|
|
808
|
+
var import_path7 = __toESM(require("path"));
|
|
809
|
+
var import_os6 = __toESM(require("os"));
|
|
810
|
+
var execAsync = (0, import_util3.promisify)(import_child_process5.exec);
|
|
703
811
|
var isWindows = process.platform === "win32";
|
|
704
|
-
var VPN_DIR =
|
|
812
|
+
var VPN_DIR = import_path7.default.join(import_os6.default.homedir(), ".crc-agent", "vpn");
|
|
705
813
|
function shq2(value) {
|
|
706
814
|
return value.replace(/'/g, "'\\''");
|
|
707
815
|
}
|
|
@@ -720,8 +828,8 @@ async function runCmd(cmd, shell) {
|
|
|
720
828
|
}
|
|
721
829
|
function getConfigPath(profile) {
|
|
722
830
|
const file = profile.configFile || `${profile.id}.conf`;
|
|
723
|
-
if (
|
|
724
|
-
return
|
|
831
|
+
if (import_path7.default.isAbsolute(file)) return file;
|
|
832
|
+
return import_path7.default.join(VPN_DIR, file);
|
|
725
833
|
}
|
|
726
834
|
async function getScutilStatus(serviceName) {
|
|
727
835
|
const { stdout } = await runCmd(`scutil --nc status '${shq2(serviceName)}'`);
|
|
@@ -837,7 +945,7 @@ async function connectWireGuard(profile) {
|
|
|
837
945
|
const tunnelName = profile.tunnelName || profile.id;
|
|
838
946
|
if (isWindows) {
|
|
839
947
|
const confPath2 = getConfigPath(profile);
|
|
840
|
-
if (!
|
|
948
|
+
if (!import_fs6.default.existsSync(confPath2)) {
|
|
841
949
|
return `Config file not found: ${confPath2}`;
|
|
842
950
|
}
|
|
843
951
|
const script = `
|
|
@@ -863,7 +971,7 @@ async function connectWireGuard(profile) {
|
|
|
863
971
|
async function connectOpenVpn(profile) {
|
|
864
972
|
if (isWindows) {
|
|
865
973
|
const confPath2 = getConfigPath(profile);
|
|
866
|
-
if (!
|
|
974
|
+
if (!import_fs6.default.existsSync(confPath2)) {
|
|
867
975
|
return `Config file not found: ${confPath2}`;
|
|
868
976
|
}
|
|
869
977
|
const connector = "C:\\Program Files\\OpenVPN Connect\\ovpnconnector.exe";
|
|
@@ -890,7 +998,7 @@ async function connectOpenVpn(profile) {
|
|
|
890
998
|
return scutilStart(profile.serviceName);
|
|
891
999
|
}
|
|
892
1000
|
const confPath = getConfigPath(profile);
|
|
893
|
-
if (!
|
|
1001
|
+
if (!import_fs6.default.existsSync(confPath)) {
|
|
894
1002
|
return `Config file not found: ${confPath}`;
|
|
895
1003
|
}
|
|
896
1004
|
const { stderr } = await runCmd(`sudo openvpn --config '${shq2(confPath)}' --daemon`);
|
|
@@ -899,7 +1007,7 @@ async function connectOpenVpn(profile) {
|
|
|
899
1007
|
async function connectAzure(profile) {
|
|
900
1008
|
if (isWindows) {
|
|
901
1009
|
const confPath = getConfigPath(profile);
|
|
902
|
-
if (!
|
|
1010
|
+
if (!import_fs6.default.existsSync(confPath)) {
|
|
903
1011
|
return `Config file not found: ${confPath}`;
|
|
904
1012
|
}
|
|
905
1013
|
await runCmd(`& 'AzureVpn.exe' -i '${psq(confPath)}' 2>&1`);
|
|
@@ -1021,9 +1129,9 @@ async function disconnectVpn(configs, profileId) {
|
|
|
1021
1129
|
}
|
|
1022
1130
|
|
|
1023
1131
|
// src/claude-sessions.ts
|
|
1024
|
-
var
|
|
1025
|
-
var
|
|
1026
|
-
var
|
|
1132
|
+
var import_fs7 = __toESM(require("fs"));
|
|
1133
|
+
var import_path8 = __toESM(require("path"));
|
|
1134
|
+
var import_os7 = __toESM(require("os"));
|
|
1027
1135
|
var import_readline = __toESM(require("readline"));
|
|
1028
1136
|
|
|
1029
1137
|
// src/claude-path.ts
|
|
@@ -1050,10 +1158,10 @@ function findProjectDirs(allDirs, filterProjectPath) {
|
|
|
1050
1158
|
}
|
|
1051
1159
|
|
|
1052
1160
|
// src/claude-sessions.ts
|
|
1053
|
-
var CLAUDE_PROJECTS_DIR =
|
|
1161
|
+
var CLAUDE_PROJECTS_DIR = import_path8.default.join(import_os7.default.homedir(), ".claude", "projects");
|
|
1054
1162
|
async function parseSessionFile(filePath) {
|
|
1055
1163
|
try {
|
|
1056
|
-
const stream =
|
|
1164
|
+
const stream = import_fs7.default.createReadStream(filePath, { encoding: "utf-8" });
|
|
1057
1165
|
const rl = import_readline.default.createInterface({ input: stream, crlfDelay: Infinity });
|
|
1058
1166
|
let firstMessage = "";
|
|
1059
1167
|
let lastTimestamp = "";
|
|
@@ -1101,17 +1209,17 @@ async function parseSessionFile(filePath) {
|
|
|
1101
1209
|
}
|
|
1102
1210
|
async function listClaudeSessions(filterProjectPath) {
|
|
1103
1211
|
const sessions2 = [];
|
|
1104
|
-
if (!
|
|
1212
|
+
if (!import_fs7.default.existsSync(CLAUDE_PROJECTS_DIR)) return sessions2;
|
|
1105
1213
|
const isDirSafe = (d) => {
|
|
1106
1214
|
try {
|
|
1107
|
-
return
|
|
1215
|
+
return import_fs7.default.statSync(import_path8.default.join(CLAUDE_PROJECTS_DIR, d)).isDirectory();
|
|
1108
1216
|
} catch {
|
|
1109
1217
|
return false;
|
|
1110
1218
|
}
|
|
1111
1219
|
};
|
|
1112
1220
|
let allEntries;
|
|
1113
1221
|
try {
|
|
1114
|
-
allEntries =
|
|
1222
|
+
allEntries = import_fs7.default.readdirSync(CLAUDE_PROJECTS_DIR);
|
|
1115
1223
|
} catch {
|
|
1116
1224
|
return sessions2;
|
|
1117
1225
|
}
|
|
@@ -1124,16 +1232,16 @@ async function listClaudeSessions(filterProjectPath) {
|
|
|
1124
1232
|
projectDirs = allEntries.filter((d) => isDirSafe(d) && !d.startsWith("-private-"));
|
|
1125
1233
|
}
|
|
1126
1234
|
for (const dirName of projectDirs) {
|
|
1127
|
-
const dirPath =
|
|
1235
|
+
const dirPath = import_path8.default.join(CLAUDE_PROJECTS_DIR, dirName);
|
|
1128
1236
|
let files;
|
|
1129
1237
|
try {
|
|
1130
|
-
files =
|
|
1238
|
+
files = import_fs7.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
|
|
1131
1239
|
} catch {
|
|
1132
1240
|
continue;
|
|
1133
1241
|
}
|
|
1134
1242
|
for (const file of files) {
|
|
1135
1243
|
const sessionId = file.replace(".jsonl", "");
|
|
1136
|
-
const parsed = await parseSessionFile(
|
|
1244
|
+
const parsed = await parseSessionFile(import_path8.default.join(dirPath, file));
|
|
1137
1245
|
if (!parsed) continue;
|
|
1138
1246
|
const projectPath = parsed.cwd || dirName.replace(/^-/, "/").replace(/-/g, "/");
|
|
1139
1247
|
sessions2.push({
|
|
@@ -1153,21 +1261,21 @@ async function listClaudeSessions(filterProjectPath) {
|
|
|
1153
1261
|
}
|
|
1154
1262
|
|
|
1155
1263
|
// src/claude-conversation.ts
|
|
1156
|
-
var
|
|
1157
|
-
var
|
|
1158
|
-
var
|
|
1264
|
+
var import_fs8 = __toESM(require("fs"));
|
|
1265
|
+
var import_path9 = __toESM(require("path"));
|
|
1266
|
+
var import_os8 = __toESM(require("os"));
|
|
1159
1267
|
var import_readline2 = __toESM(require("readline"));
|
|
1160
|
-
var CLAUDE_PROJECTS_DIR2 =
|
|
1268
|
+
var CLAUDE_PROJECTS_DIR2 = import_path9.default.join(import_os8.default.homedir(), ".claude", "projects");
|
|
1161
1269
|
function findProjectDir(projectPath) {
|
|
1162
1270
|
const encoded = encodeProjectPath(projectPath);
|
|
1163
|
-
const dirPath =
|
|
1164
|
-
if (
|
|
1165
|
-
if (!
|
|
1271
|
+
const dirPath = import_path9.default.join(CLAUDE_PROJECTS_DIR2, encoded);
|
|
1272
|
+
if (import_fs8.default.existsSync(dirPath)) return dirPath;
|
|
1273
|
+
if (!import_fs8.default.existsSync(CLAUDE_PROJECTS_DIR2)) return null;
|
|
1166
1274
|
let allDirs;
|
|
1167
1275
|
try {
|
|
1168
|
-
allDirs =
|
|
1276
|
+
allDirs = import_fs8.default.readdirSync(CLAUDE_PROJECTS_DIR2).filter((d) => {
|
|
1169
1277
|
try {
|
|
1170
|
-
return
|
|
1278
|
+
return import_fs8.default.statSync(import_path9.default.join(CLAUDE_PROJECTS_DIR2, d)).isDirectory();
|
|
1171
1279
|
} catch {
|
|
1172
1280
|
return false;
|
|
1173
1281
|
}
|
|
@@ -1177,23 +1285,23 @@ function findProjectDir(projectPath) {
|
|
|
1177
1285
|
}
|
|
1178
1286
|
const matches = findProjectDirs(allDirs, projectPath);
|
|
1179
1287
|
if (matches.length > 0) {
|
|
1180
|
-
return
|
|
1288
|
+
return import_path9.default.join(CLAUDE_PROJECTS_DIR2, matches[0]);
|
|
1181
1289
|
}
|
|
1182
1290
|
return null;
|
|
1183
1291
|
}
|
|
1184
1292
|
function findSessionFileById(sessionId) {
|
|
1185
1293
|
if (!/^[A-Za-z0-9._-]+$/.test(sessionId)) return null;
|
|
1186
|
-
if (!
|
|
1294
|
+
if (!import_fs8.default.existsSync(CLAUDE_PROJECTS_DIR2)) return null;
|
|
1187
1295
|
let dirs;
|
|
1188
1296
|
try {
|
|
1189
|
-
dirs =
|
|
1297
|
+
dirs = import_fs8.default.readdirSync(CLAUDE_PROJECTS_DIR2);
|
|
1190
1298
|
} catch {
|
|
1191
1299
|
return null;
|
|
1192
1300
|
}
|
|
1193
1301
|
for (const dir of dirs) {
|
|
1194
|
-
const candidate =
|
|
1302
|
+
const candidate = import_path9.default.join(CLAUDE_PROJECTS_DIR2, dir, `${sessionId}.jsonl`);
|
|
1195
1303
|
try {
|
|
1196
|
-
if (
|
|
1304
|
+
if (import_fs8.default.statSync(candidate).isFile()) return candidate;
|
|
1197
1305
|
} catch {
|
|
1198
1306
|
}
|
|
1199
1307
|
}
|
|
@@ -1204,20 +1312,20 @@ function findLatestSessionFile(projectPath) {
|
|
|
1204
1312
|
if (!dirPath) return null;
|
|
1205
1313
|
let entries;
|
|
1206
1314
|
try {
|
|
1207
|
-
entries =
|
|
1315
|
+
entries = import_fs8.default.readdirSync(dirPath);
|
|
1208
1316
|
} catch {
|
|
1209
1317
|
return null;
|
|
1210
1318
|
}
|
|
1211
1319
|
const files = entries.filter((f) => f.endsWith(".jsonl") && !f.includes("/")).map((f) => {
|
|
1212
1320
|
try {
|
|
1213
|
-
return { name: f, mtime:
|
|
1321
|
+
return { name: f, mtime: import_fs8.default.statSync(import_path9.default.join(dirPath, f)).mtimeMs };
|
|
1214
1322
|
} catch {
|
|
1215
1323
|
return null;
|
|
1216
1324
|
}
|
|
1217
1325
|
}).filter((f) => f !== null).sort((a, b) => b.mtime - a.mtime);
|
|
1218
1326
|
if (files.length === 0) return null;
|
|
1219
1327
|
return {
|
|
1220
|
-
filePath:
|
|
1328
|
+
filePath: import_path9.default.join(dirPath, files[0].name),
|
|
1221
1329
|
sessionId: files[0].name.replace(".jsonl", "")
|
|
1222
1330
|
};
|
|
1223
1331
|
}
|
|
@@ -1227,8 +1335,8 @@ async function readConversation(projectPath, afterLine = 0, specificSessionId) {
|
|
|
1227
1335
|
if (specificSessionId) {
|
|
1228
1336
|
sessionId = specificSessionId;
|
|
1229
1337
|
const dirPath = findProjectDir(projectPath);
|
|
1230
|
-
const direct = dirPath ?
|
|
1231
|
-
if (direct &&
|
|
1338
|
+
const direct = dirPath ? import_path9.default.join(dirPath, `${specificSessionId}.jsonl`) : null;
|
|
1339
|
+
if (direct && import_fs8.default.existsSync(direct)) {
|
|
1232
1340
|
filePath = direct;
|
|
1233
1341
|
} else {
|
|
1234
1342
|
const byId = findSessionFileById(specificSessionId);
|
|
@@ -1242,7 +1350,7 @@ async function readConversation(projectPath, afterLine = 0, specificSessionId) {
|
|
|
1242
1350
|
sessionId = found.sessionId;
|
|
1243
1351
|
}
|
|
1244
1352
|
try {
|
|
1245
|
-
const stream =
|
|
1353
|
+
const stream = import_fs8.default.createReadStream(filePath, { encoding: "utf-8" });
|
|
1246
1354
|
const rl = import_readline2.default.createInterface({ input: stream, crlfDelay: Infinity });
|
|
1247
1355
|
const messages = [];
|
|
1248
1356
|
let lineNum = 0;
|
|
@@ -1416,6 +1524,13 @@ socket.on(TMUX_LIST, async (payload) => {
|
|
|
1416
1524
|
socket.emit(TMUX_LIST_RESULT, { requestId, sessions: [], error: err?.message || "tmux list failed" });
|
|
1417
1525
|
}
|
|
1418
1526
|
});
|
|
1527
|
+
socket.on(TMUX_KILL, async (payload) => {
|
|
1528
|
+
const { requestId, name } = payload;
|
|
1529
|
+
if (!requestId || !name) return;
|
|
1530
|
+
const result = await killTmuxSession(name);
|
|
1531
|
+
logger.info({ name, ok: result.ok, error: result.error }, "tmux kill requested");
|
|
1532
|
+
socket.emit(TMUX_KILL_RESULT, { requestId, name, ok: result.ok, error: result.error });
|
|
1533
|
+
});
|
|
1419
1534
|
socket.on(TERMINAL_INPUT, (payload) => {
|
|
1420
1535
|
writeToSession(payload.sessionId, payload.data);
|
|
1421
1536
|
});
|
|
@@ -1484,8 +1599,8 @@ socket.on(AGENT_EXEC, async (payload) => {
|
|
|
1484
1599
|
const { requestId, command, cwd } = payload;
|
|
1485
1600
|
if (!requestId) return;
|
|
1486
1601
|
const { exec: exec2 } = await import("child_process");
|
|
1487
|
-
const { promisify:
|
|
1488
|
-
const execAsync2 =
|
|
1602
|
+
const { promisify: promisify4 } = await import("util");
|
|
1603
|
+
const execAsync2 = promisify4(exec2);
|
|
1489
1604
|
try {
|
|
1490
1605
|
const { stdout, stderr } = await execAsync2(command, { cwd, timeout: 3e4 });
|
|
1491
1606
|
socket.emit(AGENT_EXEC_RESULT, { requestId, stdout: stdout || "", stderr: stderr || "" });
|