cli-remote-agent 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +213 -104
- package/dist/cli.js.map +1 -1
- package/dist/index.js +186 -94
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -251,20 +251,104 @@ function startLocalControl(port, onHook) {
|
|
|
251
251
|
return server;
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
-
// src/
|
|
255
|
-
var
|
|
256
|
-
var
|
|
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
|
+
|
|
292
|
+
// src/single-instance.ts
|
|
257
293
|
var import_fs4 = __toESM(require("fs"));
|
|
258
294
|
var import_path4 = __toESM(require("path"));
|
|
295
|
+
var import_child_process = require("child_process");
|
|
296
|
+
var LOCK_FILE = import_path4.default.join(CONFIG_DIR, "agent.lock");
|
|
297
|
+
function isRunningAgent(pid) {
|
|
298
|
+
try {
|
|
299
|
+
process.kill(pid, 0);
|
|
300
|
+
} catch {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
if (process.platform === "win32") return true;
|
|
304
|
+
try {
|
|
305
|
+
const out = (0, import_child_process.execFileSync)("ps", ["-p", String(pid), "-o", "command="], { encoding: "utf8" });
|
|
306
|
+
return /crc-agent|cli-remote-agent|agent[\/\\](dist|src)[\/\\](cli|index)/.test(out);
|
|
307
|
+
} catch {
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
function acquireSingleInstanceLock() {
|
|
312
|
+
if (process.env.CRC_ALLOW_MULTIPLE === "1") return { ok: true };
|
|
313
|
+
try {
|
|
314
|
+
if (import_fs4.default.existsSync(LOCK_FILE)) {
|
|
315
|
+
const prev = parseInt(import_fs4.default.readFileSync(LOCK_FILE, "utf8").trim(), 10);
|
|
316
|
+
if (prev && prev !== process.pid && isRunningAgent(prev)) {
|
|
317
|
+
return { ok: false, pid: prev };
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
import_fs4.default.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
321
|
+
import_fs4.default.writeFileSync(LOCK_FILE, String(process.pid));
|
|
322
|
+
const release = () => {
|
|
323
|
+
try {
|
|
324
|
+
if (parseInt(import_fs4.default.readFileSync(LOCK_FILE, "utf8").trim(), 10) === process.pid) {
|
|
325
|
+
import_fs4.default.unlinkSync(LOCK_FILE);
|
|
326
|
+
}
|
|
327
|
+
} catch {
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
process.on("exit", release);
|
|
331
|
+
return { ok: true };
|
|
332
|
+
} catch (err) {
|
|
333
|
+
logger.warn({ error: err?.message }, "Could not manage single-instance lock");
|
|
334
|
+
return { ok: true };
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// src/tmux.ts
|
|
339
|
+
var import_child_process3 = require("child_process");
|
|
340
|
+
var import_util2 = require("util");
|
|
341
|
+
var import_fs6 = __toESM(require("fs"));
|
|
342
|
+
var import_path6 = __toESM(require("path"));
|
|
259
343
|
|
|
260
344
|
// src/claude-live.ts
|
|
261
|
-
var
|
|
262
|
-
var
|
|
345
|
+
var import_fs5 = __toESM(require("fs"));
|
|
346
|
+
var import_path5 = __toESM(require("path"));
|
|
263
347
|
var import_os2 = __toESM(require("os"));
|
|
264
|
-
var
|
|
348
|
+
var import_child_process2 = require("child_process");
|
|
265
349
|
var import_util = require("util");
|
|
266
|
-
var execFileAsync = (0, import_util.promisify)(
|
|
267
|
-
var LIVE_SESSIONS_DIR =
|
|
350
|
+
var execFileAsync = (0, import_util.promisify)(import_child_process2.execFile);
|
|
351
|
+
var LIVE_SESSIONS_DIR = import_path5.default.join(import_os2.default.homedir(), ".claude", "sessions");
|
|
268
352
|
function isAlive(pid) {
|
|
269
353
|
try {
|
|
270
354
|
process.kill(pid, 0);
|
|
@@ -276,14 +360,14 @@ function isAlive(pid) {
|
|
|
276
360
|
function getLiveClaudeSessions() {
|
|
277
361
|
let files;
|
|
278
362
|
try {
|
|
279
|
-
files =
|
|
363
|
+
files = import_fs5.default.readdirSync(LIVE_SESSIONS_DIR).filter((f) => f.endsWith(".json"));
|
|
280
364
|
} catch {
|
|
281
365
|
return [];
|
|
282
366
|
}
|
|
283
367
|
const out = [];
|
|
284
368
|
for (const f of files) {
|
|
285
369
|
try {
|
|
286
|
-
const obj = JSON.parse(
|
|
370
|
+
const obj = JSON.parse(import_fs5.default.readFileSync(import_path5.default.join(LIVE_SESSIONS_DIR, f), "utf-8"));
|
|
287
371
|
if (typeof obj?.pid !== "number" || !isAlive(obj.pid)) continue;
|
|
288
372
|
out.push({
|
|
289
373
|
pid: obj.pid,
|
|
@@ -320,19 +404,19 @@ function isDescendantOf(pid, ancestor, parents) {
|
|
|
320
404
|
}
|
|
321
405
|
|
|
322
406
|
// src/tmux.ts
|
|
323
|
-
var execFileAsync2 = (0, import_util2.promisify)(
|
|
407
|
+
var execFileAsync2 = (0, import_util2.promisify)(import_child_process3.execFile);
|
|
324
408
|
var IS_WIN = process.platform === "win32";
|
|
325
409
|
var cachedTmuxPath;
|
|
326
410
|
function resolveTmuxPath() {
|
|
327
411
|
if (cachedTmuxPath !== void 0) return cachedTmuxPath;
|
|
328
|
-
const dirs = (process.env.PATH || "").split(
|
|
412
|
+
const dirs = (process.env.PATH || "").split(import_path6.default.delimiter).filter(Boolean);
|
|
329
413
|
for (const extra of ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin", "/opt/local/bin"]) {
|
|
330
414
|
if (!dirs.includes(extra)) dirs.push(extra);
|
|
331
415
|
}
|
|
332
416
|
for (const dir of dirs) {
|
|
333
|
-
const candidate =
|
|
417
|
+
const candidate = import_path6.default.join(dir, "tmux");
|
|
334
418
|
try {
|
|
335
|
-
|
|
419
|
+
import_fs6.default.accessSync(candidate, import_fs6.default.constants.X_OK);
|
|
336
420
|
cachedTmuxPath = candidate;
|
|
337
421
|
return candidate;
|
|
338
422
|
} catch {
|
|
@@ -382,8 +466,8 @@ async function enrichWithPanesAndClaude(sessions2) {
|
|
|
382
466
|
const { file, args } = tmuxCmd(["list-panes", "-a", "-F", paneFmt]);
|
|
383
467
|
const { stdout } = await execFileAsync2(file, args, { timeout: 6e3 });
|
|
384
468
|
const panes = stdout.split("\n").map((line) => line.replace(/\r$/, "").trim()).filter(Boolean).map((line) => {
|
|
385
|
-
const [session, pid,
|
|
386
|
-
return { session, pid: parseInt(pid, 10) || 0, path:
|
|
469
|
+
const [session, pid, path11, activeFlags] = line.split(" ");
|
|
470
|
+
return { session, pid: parseInt(pid, 10) || 0, path: path11 || "", active: activeFlags === "11" };
|
|
387
471
|
});
|
|
388
472
|
const byName = new Map(sessions2.map((s) => [s.name, s]));
|
|
389
473
|
for (const pane of panes) {
|
|
@@ -425,22 +509,22 @@ function buildTmuxLaunch(name, launch) {
|
|
|
425
509
|
|
|
426
510
|
// src/heartbeat.ts
|
|
427
511
|
var import_os4 = __toESM(require("os"));
|
|
428
|
-
var
|
|
429
|
-
var
|
|
512
|
+
var import_path8 = __toESM(require("path"));
|
|
513
|
+
var import_child_process5 = require("child_process");
|
|
430
514
|
|
|
431
515
|
// src/pty-session.ts
|
|
432
516
|
var pty = __toESM(require("node-pty"));
|
|
433
|
-
var
|
|
434
|
-
var
|
|
517
|
+
var import_fs7 = require("fs");
|
|
518
|
+
var import_path7 = require("path");
|
|
435
519
|
var import_os3 = require("os");
|
|
436
520
|
|
|
437
521
|
// src/shell.ts
|
|
438
|
-
var
|
|
522
|
+
var import_child_process4 = require("child_process");
|
|
439
523
|
function detectShell(preference) {
|
|
440
524
|
if (preference !== "auto") return preference;
|
|
441
525
|
if (process.platform === "win32") {
|
|
442
526
|
try {
|
|
443
|
-
(0,
|
|
527
|
+
(0, import_child_process4.execSync)("where pwsh", { stdio: "ignore" });
|
|
444
528
|
return "pwsh.exe";
|
|
445
529
|
} catch {
|
|
446
530
|
return "powershell.exe";
|
|
@@ -451,22 +535,22 @@ function detectShell(preference) {
|
|
|
451
535
|
|
|
452
536
|
// src/pty-session.ts
|
|
453
537
|
var THRESHOLD = 3;
|
|
454
|
-
var INIT_DIR = (0,
|
|
455
|
-
var BASH_INIT = (0,
|
|
456
|
-
var ZSH_DIR = (0,
|
|
457
|
-
var ZSH_RC = (0,
|
|
458
|
-
var ZSH_ENV = (0,
|
|
538
|
+
var INIT_DIR = (0, import_path7.join)((0, import_os3.tmpdir)(), "crc-shell-init");
|
|
539
|
+
var BASH_INIT = (0, import_path7.join)(INIT_DIR, "bashrc");
|
|
540
|
+
var ZSH_DIR = (0, import_path7.join)(INIT_DIR, "zsh");
|
|
541
|
+
var ZSH_RC = (0, import_path7.join)(ZSH_DIR, ".zshrc");
|
|
542
|
+
var ZSH_ENV = (0, import_path7.join)(ZSH_DIR, ".zshenv");
|
|
459
543
|
var INIT_VERSION = "2";
|
|
460
544
|
function ensureInitFiles() {
|
|
461
|
-
const versionFile = (0,
|
|
462
|
-
if ((0,
|
|
545
|
+
const versionFile = (0, import_path7.join)(INIT_DIR, ".version");
|
|
546
|
+
if ((0, import_fs7.existsSync)(versionFile)) {
|
|
463
547
|
try {
|
|
464
|
-
if ((0,
|
|
548
|
+
if ((0, import_fs7.readFileSync)(versionFile, "utf-8").trim() === INIT_VERSION) return;
|
|
465
549
|
} catch {
|
|
466
550
|
}
|
|
467
551
|
}
|
|
468
|
-
(0,
|
|
469
|
-
(0,
|
|
552
|
+
(0, import_fs7.mkdirSync)(ZSH_DIR, { recursive: true });
|
|
553
|
+
(0, import_fs7.writeFileSync)(BASH_INIT, [
|
|
470
554
|
"[ -f ~/.bash_profile ] && source ~/.bash_profile",
|
|
471
555
|
"[ -f ~/.bashrc ] && source ~/.bashrc",
|
|
472
556
|
"__crc_s=$SECONDS",
|
|
@@ -477,11 +561,11 @@ function ensureInitFiles() {
|
|
|
477
561
|
`PROMPT_COMMAND='[ "$__crc_armed" = 1 ] && [ \${__crc_elapsed:-0} -ge ${THRESHOLD} ] && printf "\\a"; __crc_armed=1; eval "$__crc_orig_pc"'`,
|
|
478
562
|
""
|
|
479
563
|
].join("\n"), "utf-8");
|
|
480
|
-
(0,
|
|
564
|
+
(0, import_fs7.writeFileSync)(ZSH_ENV, [
|
|
481
565
|
'[ -f "${ZDOTDIR_ORIG:-$HOME}/.zshenv" ] && source "${ZDOTDIR_ORIG:-$HOME}/.zshenv"',
|
|
482
566
|
""
|
|
483
567
|
].join("\n"), "utf-8");
|
|
484
|
-
(0,
|
|
568
|
+
(0, import_fs7.writeFileSync)(ZSH_RC, [
|
|
485
569
|
'[ -f "${ZDOTDIR_ORIG:-$HOME}/.zshrc" ] && source "${ZDOTDIR_ORIG:-$HOME}/.zshrc"',
|
|
486
570
|
"__crc_s=$SECONDS",
|
|
487
571
|
"__crc_armed=0",
|
|
@@ -494,14 +578,14 @@ function ensureInitFiles() {
|
|
|
494
578
|
"fi",
|
|
495
579
|
""
|
|
496
580
|
].join("\n"), "utf-8");
|
|
497
|
-
(0,
|
|
581
|
+
(0, import_fs7.writeFileSync)(versionFile, INIT_VERSION, "utf-8");
|
|
498
582
|
}
|
|
499
583
|
function resolveCwd(cwd) {
|
|
500
584
|
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];
|
|
501
585
|
for (const c of candidates) {
|
|
502
586
|
if (c) {
|
|
503
587
|
try {
|
|
504
|
-
if ((0,
|
|
588
|
+
if ((0, import_fs7.existsSync)(c)) return c;
|
|
505
589
|
} catch {
|
|
506
590
|
}
|
|
507
591
|
}
|
|
@@ -708,7 +792,7 @@ function getCpuUsage() {
|
|
|
708
792
|
function getRootPaths() {
|
|
709
793
|
if (process.platform === "win32") {
|
|
710
794
|
try {
|
|
711
|
-
const output = (0,
|
|
795
|
+
const output = (0, import_child_process5.execSync)("wmic logicaldisk get name", { encoding: "utf-8" });
|
|
712
796
|
return output.split("\n").map((l) => l.trim()).filter((l) => /^[A-Z]:$/.test(l)).map((l) => l + "\\");
|
|
713
797
|
} catch {
|
|
714
798
|
return ["C:\\"];
|
|
@@ -727,7 +811,7 @@ function buildHeartbeat(homeDir) {
|
|
|
727
811
|
memoryUsage: Math.round((totalMem - freeMem) / totalMem * 100),
|
|
728
812
|
uptime: Math.round(import_os4.default.uptime()),
|
|
729
813
|
activeSessions: getActiveSessionCount(),
|
|
730
|
-
pathSeparator:
|
|
814
|
+
pathSeparator: import_path8.default.sep,
|
|
731
815
|
homeDirectory: homeDir || import_os4.default.homedir(),
|
|
732
816
|
rootPaths: getRootPaths(),
|
|
733
817
|
capabilities: { terminal: true, fileTransfer: false }
|
|
@@ -735,33 +819,33 @@ function buildHeartbeat(homeDir) {
|
|
|
735
819
|
}
|
|
736
820
|
|
|
737
821
|
// src/file-explorer.ts
|
|
738
|
-
var
|
|
739
|
-
var
|
|
822
|
+
var import_fs8 = __toESM(require("fs"));
|
|
823
|
+
var import_path9 = __toESM(require("path"));
|
|
740
824
|
var import_os5 = __toESM(require("os"));
|
|
741
|
-
var SECRET_DIR =
|
|
825
|
+
var SECRET_DIR = import_path9.default.join(import_os5.default.homedir(), ".crc-agent");
|
|
742
826
|
function isInSecretDir(resolved) {
|
|
743
|
-
const rel =
|
|
744
|
-
return rel === "" || !rel.startsWith(".." +
|
|
827
|
+
const rel = import_path9.default.relative(SECRET_DIR, resolved);
|
|
828
|
+
return rel === "" || !rel.startsWith(".." + import_path9.default.sep) && rel !== ".." && !import_path9.default.isAbsolute(rel);
|
|
745
829
|
}
|
|
746
830
|
function listDirectory(dirPath) {
|
|
747
831
|
try {
|
|
748
|
-
const resolved =
|
|
832
|
+
const resolved = import_path9.default.resolve(dirPath);
|
|
749
833
|
if (isInSecretDir(resolved)) {
|
|
750
834
|
return { entries: [], error: "Access denied" };
|
|
751
835
|
}
|
|
752
|
-
if (!
|
|
836
|
+
if (!import_fs8.default.existsSync(resolved)) {
|
|
753
837
|
return { entries: [], error: "Path does not exist" };
|
|
754
838
|
}
|
|
755
|
-
const stat =
|
|
839
|
+
const stat = import_fs8.default.statSync(resolved);
|
|
756
840
|
if (!stat.isDirectory()) {
|
|
757
841
|
return { entries: [], error: "Not a directory" };
|
|
758
842
|
}
|
|
759
|
-
const dirents =
|
|
843
|
+
const dirents = import_fs8.default.readdirSync(resolved, { withFileTypes: true });
|
|
760
844
|
const entries = [];
|
|
761
845
|
for (const dirent of dirents) {
|
|
762
846
|
try {
|
|
763
|
-
const fullPath =
|
|
764
|
-
const s =
|
|
847
|
+
const fullPath = import_path9.default.join(resolved, dirent.name);
|
|
848
|
+
const s = import_fs8.default.statSync(fullPath);
|
|
765
849
|
entries.push({
|
|
766
850
|
name: dirent.name,
|
|
767
851
|
isDirectory: dirent.isDirectory(),
|
|
@@ -783,22 +867,22 @@ function listDirectory(dirPath) {
|
|
|
783
867
|
}
|
|
784
868
|
async function downloadFile(filePath, serverUrl, secret, agentId) {
|
|
785
869
|
try {
|
|
786
|
-
const resolved =
|
|
870
|
+
const resolved = import_path9.default.resolve(filePath);
|
|
787
871
|
if (isInSecretDir(resolved)) {
|
|
788
872
|
return { error: "Access denied" };
|
|
789
873
|
}
|
|
790
|
-
if (!
|
|
874
|
+
if (!import_fs8.default.existsSync(resolved)) {
|
|
791
875
|
return { error: "File does not exist" };
|
|
792
876
|
}
|
|
793
|
-
const stat =
|
|
877
|
+
const stat = import_fs8.default.statSync(resolved);
|
|
794
878
|
if (stat.isDirectory()) {
|
|
795
879
|
return { error: "Cannot download a directory" };
|
|
796
880
|
}
|
|
797
881
|
if (stat.size > FILE_MAX_SIZE) {
|
|
798
882
|
return { error: `File too large (max ${FILE_MAX_SIZE} bytes)` };
|
|
799
883
|
}
|
|
800
|
-
const fileName =
|
|
801
|
-
const fileStream =
|
|
884
|
+
const fileName = import_path9.default.basename(resolved);
|
|
885
|
+
const fileStream = import_fs8.default.createReadStream(resolved);
|
|
802
886
|
const baseUrl = serverUrl.replace("wss://", "https://").replace("ws://", "http://");
|
|
803
887
|
const url = `${baseUrl}/api/files/receive`;
|
|
804
888
|
const res = await fetch(url, {
|
|
@@ -829,14 +913,14 @@ async function downloadFile(filePath, serverUrl, secret, agentId) {
|
|
|
829
913
|
}
|
|
830
914
|
|
|
831
915
|
// src/vpn-manager.ts
|
|
832
|
-
var
|
|
916
|
+
var import_child_process6 = require("child_process");
|
|
833
917
|
var import_util3 = require("util");
|
|
834
|
-
var
|
|
835
|
-
var
|
|
918
|
+
var import_fs9 = __toESM(require("fs"));
|
|
919
|
+
var import_path10 = __toESM(require("path"));
|
|
836
920
|
var import_os6 = __toESM(require("os"));
|
|
837
|
-
var execAsync = (0, import_util3.promisify)(
|
|
921
|
+
var execAsync = (0, import_util3.promisify)(import_child_process6.exec);
|
|
838
922
|
var isWindows = process.platform === "win32";
|
|
839
|
-
var VPN_DIR =
|
|
923
|
+
var VPN_DIR = import_path10.default.join(import_os6.default.homedir(), ".crc-agent", "vpn");
|
|
840
924
|
function shq(value) {
|
|
841
925
|
return value.replace(/'/g, "'\\''");
|
|
842
926
|
}
|
|
@@ -855,8 +939,8 @@ async function runCmd(cmd, shell) {
|
|
|
855
939
|
}
|
|
856
940
|
function getConfigPath(profile) {
|
|
857
941
|
const file = profile.configFile || `${profile.id}.conf`;
|
|
858
|
-
if (
|
|
859
|
-
return
|
|
942
|
+
if (import_path10.default.isAbsolute(file)) return file;
|
|
943
|
+
return import_path10.default.join(VPN_DIR, file);
|
|
860
944
|
}
|
|
861
945
|
async function getScutilStatus(serviceName) {
|
|
862
946
|
const { stdout } = await runCmd(`scutil --nc status '${shq(serviceName)}'`);
|
|
@@ -972,7 +1056,7 @@ async function connectWireGuard(profile) {
|
|
|
972
1056
|
const tunnelName = profile.tunnelName || profile.id;
|
|
973
1057
|
if (isWindows) {
|
|
974
1058
|
const confPath2 = getConfigPath(profile);
|
|
975
|
-
if (!
|
|
1059
|
+
if (!import_fs9.default.existsSync(confPath2)) {
|
|
976
1060
|
return `Config file not found: ${confPath2}`;
|
|
977
1061
|
}
|
|
978
1062
|
const script = `
|
|
@@ -998,7 +1082,7 @@ async function connectWireGuard(profile) {
|
|
|
998
1082
|
async function connectOpenVpn(profile) {
|
|
999
1083
|
if (isWindows) {
|
|
1000
1084
|
const confPath2 = getConfigPath(profile);
|
|
1001
|
-
if (!
|
|
1085
|
+
if (!import_fs9.default.existsSync(confPath2)) {
|
|
1002
1086
|
return `Config file not found: ${confPath2}`;
|
|
1003
1087
|
}
|
|
1004
1088
|
const connector = "C:\\Program Files\\OpenVPN Connect\\ovpnconnector.exe";
|
|
@@ -1025,7 +1109,7 @@ async function connectOpenVpn(profile) {
|
|
|
1025
1109
|
return scutilStart(profile.serviceName);
|
|
1026
1110
|
}
|
|
1027
1111
|
const confPath = getConfigPath(profile);
|
|
1028
|
-
if (!
|
|
1112
|
+
if (!import_fs9.default.existsSync(confPath)) {
|
|
1029
1113
|
return `Config file not found: ${confPath}`;
|
|
1030
1114
|
}
|
|
1031
1115
|
const { stderr } = await runCmd(`sudo openvpn --config '${shq(confPath)}' --daemon`);
|
|
@@ -1034,7 +1118,7 @@ async function connectOpenVpn(profile) {
|
|
|
1034
1118
|
async function connectAzure(profile) {
|
|
1035
1119
|
if (isWindows) {
|
|
1036
1120
|
const confPath = getConfigPath(profile);
|
|
1037
|
-
if (!
|
|
1121
|
+
if (!import_fs9.default.existsSync(confPath)) {
|
|
1038
1122
|
return `Config file not found: ${confPath}`;
|
|
1039
1123
|
}
|
|
1040
1124
|
await runCmd(`& 'AzureVpn.exe' -i '${psq(confPath)}' 2>&1`);
|
|
@@ -1156,8 +1240,8 @@ async function disconnectVpn(configs, profileId) {
|
|
|
1156
1240
|
}
|
|
1157
1241
|
|
|
1158
1242
|
// src/claude-sessions.ts
|
|
1159
|
-
var
|
|
1160
|
-
var
|
|
1243
|
+
var import_fs10 = __toESM(require("fs"));
|
|
1244
|
+
var import_path11 = __toESM(require("path"));
|
|
1161
1245
|
var import_os7 = __toESM(require("os"));
|
|
1162
1246
|
var import_readline = __toESM(require("readline"));
|
|
1163
1247
|
|
|
@@ -1185,10 +1269,10 @@ function findProjectDirs(allDirs, filterProjectPath) {
|
|
|
1185
1269
|
}
|
|
1186
1270
|
|
|
1187
1271
|
// src/claude-sessions.ts
|
|
1188
|
-
var CLAUDE_PROJECTS_DIR =
|
|
1272
|
+
var CLAUDE_PROJECTS_DIR = import_path11.default.join(import_os7.default.homedir(), ".claude", "projects");
|
|
1189
1273
|
async function parseSessionFile(filePath) {
|
|
1190
1274
|
try {
|
|
1191
|
-
const stream =
|
|
1275
|
+
const stream = import_fs10.default.createReadStream(filePath, { encoding: "utf-8" });
|
|
1192
1276
|
const rl = import_readline.default.createInterface({ input: stream, crlfDelay: Infinity });
|
|
1193
1277
|
let firstMessage = "";
|
|
1194
1278
|
let lastTimestamp = "";
|
|
@@ -1236,17 +1320,17 @@ async function parseSessionFile(filePath) {
|
|
|
1236
1320
|
}
|
|
1237
1321
|
async function listClaudeSessions(filterProjectPath) {
|
|
1238
1322
|
const sessions2 = [];
|
|
1239
|
-
if (!
|
|
1323
|
+
if (!import_fs10.default.existsSync(CLAUDE_PROJECTS_DIR)) return sessions2;
|
|
1240
1324
|
const isDirSafe = (d) => {
|
|
1241
1325
|
try {
|
|
1242
|
-
return
|
|
1326
|
+
return import_fs10.default.statSync(import_path11.default.join(CLAUDE_PROJECTS_DIR, d)).isDirectory();
|
|
1243
1327
|
} catch {
|
|
1244
1328
|
return false;
|
|
1245
1329
|
}
|
|
1246
1330
|
};
|
|
1247
1331
|
let allEntries;
|
|
1248
1332
|
try {
|
|
1249
|
-
allEntries =
|
|
1333
|
+
allEntries = import_fs10.default.readdirSync(CLAUDE_PROJECTS_DIR);
|
|
1250
1334
|
} catch {
|
|
1251
1335
|
return sessions2;
|
|
1252
1336
|
}
|
|
@@ -1259,16 +1343,16 @@ async function listClaudeSessions(filterProjectPath) {
|
|
|
1259
1343
|
projectDirs = allEntries.filter((d) => isDirSafe(d) && !d.startsWith("-private-"));
|
|
1260
1344
|
}
|
|
1261
1345
|
for (const dirName of projectDirs) {
|
|
1262
|
-
const dirPath =
|
|
1346
|
+
const dirPath = import_path11.default.join(CLAUDE_PROJECTS_DIR, dirName);
|
|
1263
1347
|
let files;
|
|
1264
1348
|
try {
|
|
1265
|
-
files =
|
|
1349
|
+
files = import_fs10.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
|
|
1266
1350
|
} catch {
|
|
1267
1351
|
continue;
|
|
1268
1352
|
}
|
|
1269
1353
|
for (const file of files) {
|
|
1270
1354
|
const sessionId = file.replace(".jsonl", "");
|
|
1271
|
-
const parsed = await parseSessionFile(
|
|
1355
|
+
const parsed = await parseSessionFile(import_path11.default.join(dirPath, file));
|
|
1272
1356
|
if (!parsed) continue;
|
|
1273
1357
|
const projectPath = parsed.cwd || dirName.replace(/^-/, "/").replace(/-/g, "/");
|
|
1274
1358
|
sessions2.push({
|
|
@@ -1288,21 +1372,21 @@ async function listClaudeSessions(filterProjectPath) {
|
|
|
1288
1372
|
}
|
|
1289
1373
|
|
|
1290
1374
|
// src/claude-conversation.ts
|
|
1291
|
-
var
|
|
1292
|
-
var
|
|
1375
|
+
var import_fs11 = __toESM(require("fs"));
|
|
1376
|
+
var import_path12 = __toESM(require("path"));
|
|
1293
1377
|
var import_os8 = __toESM(require("os"));
|
|
1294
1378
|
var import_readline2 = __toESM(require("readline"));
|
|
1295
|
-
var CLAUDE_PROJECTS_DIR2 =
|
|
1379
|
+
var CLAUDE_PROJECTS_DIR2 = import_path12.default.join(import_os8.default.homedir(), ".claude", "projects");
|
|
1296
1380
|
function findProjectDir(projectPath) {
|
|
1297
1381
|
const encoded = encodeProjectPath(projectPath);
|
|
1298
|
-
const dirPath =
|
|
1299
|
-
if (
|
|
1300
|
-
if (!
|
|
1382
|
+
const dirPath = import_path12.default.join(CLAUDE_PROJECTS_DIR2, encoded);
|
|
1383
|
+
if (import_fs11.default.existsSync(dirPath)) return dirPath;
|
|
1384
|
+
if (!import_fs11.default.existsSync(CLAUDE_PROJECTS_DIR2)) return null;
|
|
1301
1385
|
let allDirs;
|
|
1302
1386
|
try {
|
|
1303
|
-
allDirs =
|
|
1387
|
+
allDirs = import_fs11.default.readdirSync(CLAUDE_PROJECTS_DIR2).filter((d) => {
|
|
1304
1388
|
try {
|
|
1305
|
-
return
|
|
1389
|
+
return import_fs11.default.statSync(import_path12.default.join(CLAUDE_PROJECTS_DIR2, d)).isDirectory();
|
|
1306
1390
|
} catch {
|
|
1307
1391
|
return false;
|
|
1308
1392
|
}
|
|
@@ -1312,23 +1396,23 @@ function findProjectDir(projectPath) {
|
|
|
1312
1396
|
}
|
|
1313
1397
|
const matches = findProjectDirs(allDirs, projectPath);
|
|
1314
1398
|
if (matches.length > 0) {
|
|
1315
|
-
return
|
|
1399
|
+
return import_path12.default.join(CLAUDE_PROJECTS_DIR2, matches[0]);
|
|
1316
1400
|
}
|
|
1317
1401
|
return null;
|
|
1318
1402
|
}
|
|
1319
1403
|
function findSessionFileById(sessionId) {
|
|
1320
1404
|
if (!/^[A-Za-z0-9._-]+$/.test(sessionId)) return null;
|
|
1321
|
-
if (!
|
|
1405
|
+
if (!import_fs11.default.existsSync(CLAUDE_PROJECTS_DIR2)) return null;
|
|
1322
1406
|
let dirs;
|
|
1323
1407
|
try {
|
|
1324
|
-
dirs =
|
|
1408
|
+
dirs = import_fs11.default.readdirSync(CLAUDE_PROJECTS_DIR2);
|
|
1325
1409
|
} catch {
|
|
1326
1410
|
return null;
|
|
1327
1411
|
}
|
|
1328
1412
|
for (const dir of dirs) {
|
|
1329
|
-
const candidate =
|
|
1413
|
+
const candidate = import_path12.default.join(CLAUDE_PROJECTS_DIR2, dir, `${sessionId}.jsonl`);
|
|
1330
1414
|
try {
|
|
1331
|
-
if (
|
|
1415
|
+
if (import_fs11.default.statSync(candidate).isFile()) return candidate;
|
|
1332
1416
|
} catch {
|
|
1333
1417
|
}
|
|
1334
1418
|
}
|
|
@@ -1339,20 +1423,20 @@ function findLatestSessionFile(projectPath) {
|
|
|
1339
1423
|
if (!dirPath) return null;
|
|
1340
1424
|
let entries;
|
|
1341
1425
|
try {
|
|
1342
|
-
entries =
|
|
1426
|
+
entries = import_fs11.default.readdirSync(dirPath);
|
|
1343
1427
|
} catch {
|
|
1344
1428
|
return null;
|
|
1345
1429
|
}
|
|
1346
1430
|
const files = entries.filter((f) => f.endsWith(".jsonl") && !f.includes("/")).map((f) => {
|
|
1347
1431
|
try {
|
|
1348
|
-
return { name: f, mtime:
|
|
1432
|
+
return { name: f, mtime: import_fs11.default.statSync(import_path12.default.join(dirPath, f)).mtimeMs };
|
|
1349
1433
|
} catch {
|
|
1350
1434
|
return null;
|
|
1351
1435
|
}
|
|
1352
1436
|
}).filter((f) => f !== null).sort((a, b) => b.mtime - a.mtime);
|
|
1353
1437
|
if (files.length === 0) return null;
|
|
1354
1438
|
return {
|
|
1355
|
-
filePath:
|
|
1439
|
+
filePath: import_path12.default.join(dirPath, files[0].name),
|
|
1356
1440
|
sessionId: files[0].name.replace(".jsonl", "")
|
|
1357
1441
|
};
|
|
1358
1442
|
}
|
|
@@ -1362,8 +1446,8 @@ async function readConversation(projectPath, afterLine = 0, specificSessionId) {
|
|
|
1362
1446
|
if (specificSessionId) {
|
|
1363
1447
|
sessionId = specificSessionId;
|
|
1364
1448
|
const dirPath = findProjectDir(projectPath);
|
|
1365
|
-
const direct = dirPath ?
|
|
1366
|
-
if (direct &&
|
|
1449
|
+
const direct = dirPath ? import_path12.default.join(dirPath, `${specificSessionId}.jsonl`) : null;
|
|
1450
|
+
if (direct && import_fs11.default.existsSync(direct)) {
|
|
1367
1451
|
filePath = direct;
|
|
1368
1452
|
} else {
|
|
1369
1453
|
const byId = findSessionFileById(specificSessionId);
|
|
@@ -1377,7 +1461,7 @@ async function readConversation(projectPath, afterLine = 0, specificSessionId) {
|
|
|
1377
1461
|
sessionId = found.sessionId;
|
|
1378
1462
|
}
|
|
1379
1463
|
try {
|
|
1380
|
-
const stream =
|
|
1464
|
+
const stream = import_fs11.default.createReadStream(filePath, { encoding: "utf-8" });
|
|
1381
1465
|
const rl = import_readline2.default.createInterface({ input: stream, crlfDelay: Infinity });
|
|
1382
1466
|
const messages = [];
|
|
1383
1467
|
let lineNum = 0;
|
|
@@ -1466,6 +1550,14 @@ if (!isConfigured()) {
|
|
|
1466
1550
|
}
|
|
1467
1551
|
var config = loadConfig();
|
|
1468
1552
|
logger.info({ agentId: config.agentId, serverUrl: config.serverUrl }, "Starting agent");
|
|
1553
|
+
var lock = acquireSingleInstanceLock();
|
|
1554
|
+
if (!lock.ok) {
|
|
1555
|
+
console.error(
|
|
1556
|
+
`Another crc-agent is already running on this machine (PID ${lock.pid}). Stop it first, or set CRC_ALLOW_MULTIPLE=1 to override.`
|
|
1557
|
+
);
|
|
1558
|
+
process.exit(1);
|
|
1559
|
+
}
|
|
1560
|
+
ensurePtyHelperExecutable();
|
|
1469
1561
|
process.on("unhandledRejection", (reason) => {
|
|
1470
1562
|
logger.error({ reason }, "Unhandled promise rejection (agent kept alive)");
|
|
1471
1563
|
});
|