cli-remote-agent 1.0.7 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +166 -104
- package/dist/cli.js.map +1 -1
- package/dist/index.js +147 -94
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -289,20 +289,66 @@ function ensurePtyHelperExecutable() {
|
|
|
289
289
|
}
|
|
290
290
|
}
|
|
291
291
|
|
|
292
|
+
// src/single-instance.ts
|
|
293
|
+
var import_fs4 = __toESM(require("fs"));
|
|
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
|
+
|
|
292
338
|
// src/tmux.ts
|
|
293
|
-
var
|
|
339
|
+
var import_child_process3 = require("child_process");
|
|
294
340
|
var import_util2 = require("util");
|
|
295
|
-
var
|
|
296
|
-
var
|
|
341
|
+
var import_fs6 = __toESM(require("fs"));
|
|
342
|
+
var import_path6 = __toESM(require("path"));
|
|
297
343
|
|
|
298
344
|
// src/claude-live.ts
|
|
299
|
-
var
|
|
300
|
-
var
|
|
345
|
+
var import_fs5 = __toESM(require("fs"));
|
|
346
|
+
var import_path5 = __toESM(require("path"));
|
|
301
347
|
var import_os2 = __toESM(require("os"));
|
|
302
|
-
var
|
|
348
|
+
var import_child_process2 = require("child_process");
|
|
303
349
|
var import_util = require("util");
|
|
304
|
-
var execFileAsync = (0, import_util.promisify)(
|
|
305
|
-
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");
|
|
306
352
|
function isAlive(pid) {
|
|
307
353
|
try {
|
|
308
354
|
process.kill(pid, 0);
|
|
@@ -314,14 +360,14 @@ function isAlive(pid) {
|
|
|
314
360
|
function getLiveClaudeSessions() {
|
|
315
361
|
let files;
|
|
316
362
|
try {
|
|
317
|
-
files =
|
|
363
|
+
files = import_fs5.default.readdirSync(LIVE_SESSIONS_DIR).filter((f) => f.endsWith(".json"));
|
|
318
364
|
} catch {
|
|
319
365
|
return [];
|
|
320
366
|
}
|
|
321
367
|
const out = [];
|
|
322
368
|
for (const f of files) {
|
|
323
369
|
try {
|
|
324
|
-
const obj = JSON.parse(
|
|
370
|
+
const obj = JSON.parse(import_fs5.default.readFileSync(import_path5.default.join(LIVE_SESSIONS_DIR, f), "utf-8"));
|
|
325
371
|
if (typeof obj?.pid !== "number" || !isAlive(obj.pid)) continue;
|
|
326
372
|
out.push({
|
|
327
373
|
pid: obj.pid,
|
|
@@ -358,19 +404,19 @@ function isDescendantOf(pid, ancestor, parents) {
|
|
|
358
404
|
}
|
|
359
405
|
|
|
360
406
|
// src/tmux.ts
|
|
361
|
-
var execFileAsync2 = (0, import_util2.promisify)(
|
|
407
|
+
var execFileAsync2 = (0, import_util2.promisify)(import_child_process3.execFile);
|
|
362
408
|
var IS_WIN = process.platform === "win32";
|
|
363
409
|
var cachedTmuxPath;
|
|
364
410
|
function resolveTmuxPath() {
|
|
365
411
|
if (cachedTmuxPath !== void 0) return cachedTmuxPath;
|
|
366
|
-
const dirs = (process.env.PATH || "").split(
|
|
412
|
+
const dirs = (process.env.PATH || "").split(import_path6.default.delimiter).filter(Boolean);
|
|
367
413
|
for (const extra of ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin", "/opt/local/bin"]) {
|
|
368
414
|
if (!dirs.includes(extra)) dirs.push(extra);
|
|
369
415
|
}
|
|
370
416
|
for (const dir of dirs) {
|
|
371
|
-
const candidate =
|
|
417
|
+
const candidate = import_path6.default.join(dir, "tmux");
|
|
372
418
|
try {
|
|
373
|
-
|
|
419
|
+
import_fs6.default.accessSync(candidate, import_fs6.default.constants.X_OK);
|
|
374
420
|
cachedTmuxPath = candidate;
|
|
375
421
|
return candidate;
|
|
376
422
|
} catch {
|
|
@@ -420,8 +466,8 @@ async function enrichWithPanesAndClaude(sessions2) {
|
|
|
420
466
|
const { file, args } = tmuxCmd(["list-panes", "-a", "-F", paneFmt]);
|
|
421
467
|
const { stdout } = await execFileAsync2(file, args, { timeout: 6e3 });
|
|
422
468
|
const panes = stdout.split("\n").map((line) => line.replace(/\r$/, "").trim()).filter(Boolean).map((line) => {
|
|
423
|
-
const [session, pid,
|
|
424
|
-
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" };
|
|
425
471
|
});
|
|
426
472
|
const byName = new Map(sessions2.map((s) => [s.name, s]));
|
|
427
473
|
for (const pane of panes) {
|
|
@@ -463,22 +509,22 @@ function buildTmuxLaunch(name, launch) {
|
|
|
463
509
|
|
|
464
510
|
// src/heartbeat.ts
|
|
465
511
|
var import_os4 = __toESM(require("os"));
|
|
466
|
-
var
|
|
467
|
-
var
|
|
512
|
+
var import_path8 = __toESM(require("path"));
|
|
513
|
+
var import_child_process5 = require("child_process");
|
|
468
514
|
|
|
469
515
|
// src/pty-session.ts
|
|
470
516
|
var pty = __toESM(require("node-pty"));
|
|
471
|
-
var
|
|
472
|
-
var
|
|
517
|
+
var import_fs7 = require("fs");
|
|
518
|
+
var import_path7 = require("path");
|
|
473
519
|
var import_os3 = require("os");
|
|
474
520
|
|
|
475
521
|
// src/shell.ts
|
|
476
|
-
var
|
|
522
|
+
var import_child_process4 = require("child_process");
|
|
477
523
|
function detectShell(preference) {
|
|
478
524
|
if (preference !== "auto") return preference;
|
|
479
525
|
if (process.platform === "win32") {
|
|
480
526
|
try {
|
|
481
|
-
(0,
|
|
527
|
+
(0, import_child_process4.execSync)("where pwsh", { stdio: "ignore" });
|
|
482
528
|
return "pwsh.exe";
|
|
483
529
|
} catch {
|
|
484
530
|
return "powershell.exe";
|
|
@@ -489,22 +535,22 @@ function detectShell(preference) {
|
|
|
489
535
|
|
|
490
536
|
// src/pty-session.ts
|
|
491
537
|
var THRESHOLD = 3;
|
|
492
|
-
var INIT_DIR = (0,
|
|
493
|
-
var BASH_INIT = (0,
|
|
494
|
-
var ZSH_DIR = (0,
|
|
495
|
-
var ZSH_RC = (0,
|
|
496
|
-
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");
|
|
497
543
|
var INIT_VERSION = "2";
|
|
498
544
|
function ensureInitFiles() {
|
|
499
|
-
const versionFile = (0,
|
|
500
|
-
if ((0,
|
|
545
|
+
const versionFile = (0, import_path7.join)(INIT_DIR, ".version");
|
|
546
|
+
if ((0, import_fs7.existsSync)(versionFile)) {
|
|
501
547
|
try {
|
|
502
|
-
if ((0,
|
|
548
|
+
if ((0, import_fs7.readFileSync)(versionFile, "utf-8").trim() === INIT_VERSION) return;
|
|
503
549
|
} catch {
|
|
504
550
|
}
|
|
505
551
|
}
|
|
506
|
-
(0,
|
|
507
|
-
(0,
|
|
552
|
+
(0, import_fs7.mkdirSync)(ZSH_DIR, { recursive: true });
|
|
553
|
+
(0, import_fs7.writeFileSync)(BASH_INIT, [
|
|
508
554
|
"[ -f ~/.bash_profile ] && source ~/.bash_profile",
|
|
509
555
|
"[ -f ~/.bashrc ] && source ~/.bashrc",
|
|
510
556
|
"__crc_s=$SECONDS",
|
|
@@ -515,11 +561,11 @@ function ensureInitFiles() {
|
|
|
515
561
|
`PROMPT_COMMAND='[ "$__crc_armed" = 1 ] && [ \${__crc_elapsed:-0} -ge ${THRESHOLD} ] && printf "\\a"; __crc_armed=1; eval "$__crc_orig_pc"'`,
|
|
516
562
|
""
|
|
517
563
|
].join("\n"), "utf-8");
|
|
518
|
-
(0,
|
|
564
|
+
(0, import_fs7.writeFileSync)(ZSH_ENV, [
|
|
519
565
|
'[ -f "${ZDOTDIR_ORIG:-$HOME}/.zshenv" ] && source "${ZDOTDIR_ORIG:-$HOME}/.zshenv"',
|
|
520
566
|
""
|
|
521
567
|
].join("\n"), "utf-8");
|
|
522
|
-
(0,
|
|
568
|
+
(0, import_fs7.writeFileSync)(ZSH_RC, [
|
|
523
569
|
'[ -f "${ZDOTDIR_ORIG:-$HOME}/.zshrc" ] && source "${ZDOTDIR_ORIG:-$HOME}/.zshrc"',
|
|
524
570
|
"__crc_s=$SECONDS",
|
|
525
571
|
"__crc_armed=0",
|
|
@@ -532,14 +578,14 @@ function ensureInitFiles() {
|
|
|
532
578
|
"fi",
|
|
533
579
|
""
|
|
534
580
|
].join("\n"), "utf-8");
|
|
535
|
-
(0,
|
|
581
|
+
(0, import_fs7.writeFileSync)(versionFile, INIT_VERSION, "utf-8");
|
|
536
582
|
}
|
|
537
583
|
function resolveCwd(cwd) {
|
|
538
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];
|
|
539
585
|
for (const c of candidates) {
|
|
540
586
|
if (c) {
|
|
541
587
|
try {
|
|
542
|
-
if ((0,
|
|
588
|
+
if ((0, import_fs7.existsSync)(c)) return c;
|
|
543
589
|
} catch {
|
|
544
590
|
}
|
|
545
591
|
}
|
|
@@ -746,7 +792,7 @@ function getCpuUsage() {
|
|
|
746
792
|
function getRootPaths() {
|
|
747
793
|
if (process.platform === "win32") {
|
|
748
794
|
try {
|
|
749
|
-
const output = (0,
|
|
795
|
+
const output = (0, import_child_process5.execSync)("wmic logicaldisk get name", { encoding: "utf-8" });
|
|
750
796
|
return output.split("\n").map((l) => l.trim()).filter((l) => /^[A-Z]:$/.test(l)).map((l) => l + "\\");
|
|
751
797
|
} catch {
|
|
752
798
|
return ["C:\\"];
|
|
@@ -765,7 +811,7 @@ function buildHeartbeat(homeDir) {
|
|
|
765
811
|
memoryUsage: Math.round((totalMem - freeMem) / totalMem * 100),
|
|
766
812
|
uptime: Math.round(import_os4.default.uptime()),
|
|
767
813
|
activeSessions: getActiveSessionCount(),
|
|
768
|
-
pathSeparator:
|
|
814
|
+
pathSeparator: import_path8.default.sep,
|
|
769
815
|
homeDirectory: homeDir || import_os4.default.homedir(),
|
|
770
816
|
rootPaths: getRootPaths(),
|
|
771
817
|
capabilities: { terminal: true, fileTransfer: false }
|
|
@@ -773,33 +819,33 @@ function buildHeartbeat(homeDir) {
|
|
|
773
819
|
}
|
|
774
820
|
|
|
775
821
|
// src/file-explorer.ts
|
|
776
|
-
var
|
|
777
|
-
var
|
|
822
|
+
var import_fs8 = __toESM(require("fs"));
|
|
823
|
+
var import_path9 = __toESM(require("path"));
|
|
778
824
|
var import_os5 = __toESM(require("os"));
|
|
779
|
-
var SECRET_DIR =
|
|
825
|
+
var SECRET_DIR = import_path9.default.join(import_os5.default.homedir(), ".crc-agent");
|
|
780
826
|
function isInSecretDir(resolved) {
|
|
781
|
-
const rel =
|
|
782
|
-
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);
|
|
783
829
|
}
|
|
784
830
|
function listDirectory(dirPath) {
|
|
785
831
|
try {
|
|
786
|
-
const resolved =
|
|
832
|
+
const resolved = import_path9.default.resolve(dirPath);
|
|
787
833
|
if (isInSecretDir(resolved)) {
|
|
788
834
|
return { entries: [], error: "Access denied" };
|
|
789
835
|
}
|
|
790
|
-
if (!
|
|
836
|
+
if (!import_fs8.default.existsSync(resolved)) {
|
|
791
837
|
return { entries: [], error: "Path does not exist" };
|
|
792
838
|
}
|
|
793
|
-
const stat =
|
|
839
|
+
const stat = import_fs8.default.statSync(resolved);
|
|
794
840
|
if (!stat.isDirectory()) {
|
|
795
841
|
return { entries: [], error: "Not a directory" };
|
|
796
842
|
}
|
|
797
|
-
const dirents =
|
|
843
|
+
const dirents = import_fs8.default.readdirSync(resolved, { withFileTypes: true });
|
|
798
844
|
const entries = [];
|
|
799
845
|
for (const dirent of dirents) {
|
|
800
846
|
try {
|
|
801
|
-
const fullPath =
|
|
802
|
-
const s =
|
|
847
|
+
const fullPath = import_path9.default.join(resolved, dirent.name);
|
|
848
|
+
const s = import_fs8.default.statSync(fullPath);
|
|
803
849
|
entries.push({
|
|
804
850
|
name: dirent.name,
|
|
805
851
|
isDirectory: dirent.isDirectory(),
|
|
@@ -821,22 +867,22 @@ function listDirectory(dirPath) {
|
|
|
821
867
|
}
|
|
822
868
|
async function downloadFile(filePath, serverUrl, secret, agentId) {
|
|
823
869
|
try {
|
|
824
|
-
const resolved =
|
|
870
|
+
const resolved = import_path9.default.resolve(filePath);
|
|
825
871
|
if (isInSecretDir(resolved)) {
|
|
826
872
|
return { error: "Access denied" };
|
|
827
873
|
}
|
|
828
|
-
if (!
|
|
874
|
+
if (!import_fs8.default.existsSync(resolved)) {
|
|
829
875
|
return { error: "File does not exist" };
|
|
830
876
|
}
|
|
831
|
-
const stat =
|
|
877
|
+
const stat = import_fs8.default.statSync(resolved);
|
|
832
878
|
if (stat.isDirectory()) {
|
|
833
879
|
return { error: "Cannot download a directory" };
|
|
834
880
|
}
|
|
835
881
|
if (stat.size > FILE_MAX_SIZE) {
|
|
836
882
|
return { error: `File too large (max ${FILE_MAX_SIZE} bytes)` };
|
|
837
883
|
}
|
|
838
|
-
const fileName =
|
|
839
|
-
const fileStream =
|
|
884
|
+
const fileName = import_path9.default.basename(resolved);
|
|
885
|
+
const fileStream = import_fs8.default.createReadStream(resolved);
|
|
840
886
|
const baseUrl = serverUrl.replace("wss://", "https://").replace("ws://", "http://");
|
|
841
887
|
const url = `${baseUrl}/api/files/receive`;
|
|
842
888
|
const res = await fetch(url, {
|
|
@@ -867,14 +913,14 @@ async function downloadFile(filePath, serverUrl, secret, agentId) {
|
|
|
867
913
|
}
|
|
868
914
|
|
|
869
915
|
// src/vpn-manager.ts
|
|
870
|
-
var
|
|
916
|
+
var import_child_process6 = require("child_process");
|
|
871
917
|
var import_util3 = require("util");
|
|
872
|
-
var
|
|
873
|
-
var
|
|
918
|
+
var import_fs9 = __toESM(require("fs"));
|
|
919
|
+
var import_path10 = __toESM(require("path"));
|
|
874
920
|
var import_os6 = __toESM(require("os"));
|
|
875
|
-
var execAsync = (0, import_util3.promisify)(
|
|
921
|
+
var execAsync = (0, import_util3.promisify)(import_child_process6.exec);
|
|
876
922
|
var isWindows = process.platform === "win32";
|
|
877
|
-
var VPN_DIR =
|
|
923
|
+
var VPN_DIR = import_path10.default.join(import_os6.default.homedir(), ".crc-agent", "vpn");
|
|
878
924
|
function shq(value) {
|
|
879
925
|
return value.replace(/'/g, "'\\''");
|
|
880
926
|
}
|
|
@@ -893,8 +939,8 @@ async function runCmd(cmd, shell) {
|
|
|
893
939
|
}
|
|
894
940
|
function getConfigPath(profile) {
|
|
895
941
|
const file = profile.configFile || `${profile.id}.conf`;
|
|
896
|
-
if (
|
|
897
|
-
return
|
|
942
|
+
if (import_path10.default.isAbsolute(file)) return file;
|
|
943
|
+
return import_path10.default.join(VPN_DIR, file);
|
|
898
944
|
}
|
|
899
945
|
async function getScutilStatus(serviceName) {
|
|
900
946
|
const { stdout } = await runCmd(`scutil --nc status '${shq(serviceName)}'`);
|
|
@@ -1010,7 +1056,7 @@ async function connectWireGuard(profile) {
|
|
|
1010
1056
|
const tunnelName = profile.tunnelName || profile.id;
|
|
1011
1057
|
if (isWindows) {
|
|
1012
1058
|
const confPath2 = getConfigPath(profile);
|
|
1013
|
-
if (!
|
|
1059
|
+
if (!import_fs9.default.existsSync(confPath2)) {
|
|
1014
1060
|
return `Config file not found: ${confPath2}`;
|
|
1015
1061
|
}
|
|
1016
1062
|
const script = `
|
|
@@ -1036,7 +1082,7 @@ async function connectWireGuard(profile) {
|
|
|
1036
1082
|
async function connectOpenVpn(profile) {
|
|
1037
1083
|
if (isWindows) {
|
|
1038
1084
|
const confPath2 = getConfigPath(profile);
|
|
1039
|
-
if (!
|
|
1085
|
+
if (!import_fs9.default.existsSync(confPath2)) {
|
|
1040
1086
|
return `Config file not found: ${confPath2}`;
|
|
1041
1087
|
}
|
|
1042
1088
|
const connector = "C:\\Program Files\\OpenVPN Connect\\ovpnconnector.exe";
|
|
@@ -1063,7 +1109,7 @@ async function connectOpenVpn(profile) {
|
|
|
1063
1109
|
return scutilStart(profile.serviceName);
|
|
1064
1110
|
}
|
|
1065
1111
|
const confPath = getConfigPath(profile);
|
|
1066
|
-
if (!
|
|
1112
|
+
if (!import_fs9.default.existsSync(confPath)) {
|
|
1067
1113
|
return `Config file not found: ${confPath}`;
|
|
1068
1114
|
}
|
|
1069
1115
|
const { stderr } = await runCmd(`sudo openvpn --config '${shq(confPath)}' --daemon`);
|
|
@@ -1072,7 +1118,7 @@ async function connectOpenVpn(profile) {
|
|
|
1072
1118
|
async function connectAzure(profile) {
|
|
1073
1119
|
if (isWindows) {
|
|
1074
1120
|
const confPath = getConfigPath(profile);
|
|
1075
|
-
if (!
|
|
1121
|
+
if (!import_fs9.default.existsSync(confPath)) {
|
|
1076
1122
|
return `Config file not found: ${confPath}`;
|
|
1077
1123
|
}
|
|
1078
1124
|
await runCmd(`& 'AzureVpn.exe' -i '${psq(confPath)}' 2>&1`);
|
|
@@ -1194,8 +1240,8 @@ async function disconnectVpn(configs, profileId) {
|
|
|
1194
1240
|
}
|
|
1195
1241
|
|
|
1196
1242
|
// src/claude-sessions.ts
|
|
1197
|
-
var
|
|
1198
|
-
var
|
|
1243
|
+
var import_fs10 = __toESM(require("fs"));
|
|
1244
|
+
var import_path11 = __toESM(require("path"));
|
|
1199
1245
|
var import_os7 = __toESM(require("os"));
|
|
1200
1246
|
var import_readline = __toESM(require("readline"));
|
|
1201
1247
|
|
|
@@ -1223,10 +1269,10 @@ function findProjectDirs(allDirs, filterProjectPath) {
|
|
|
1223
1269
|
}
|
|
1224
1270
|
|
|
1225
1271
|
// src/claude-sessions.ts
|
|
1226
|
-
var CLAUDE_PROJECTS_DIR =
|
|
1272
|
+
var CLAUDE_PROJECTS_DIR = import_path11.default.join(import_os7.default.homedir(), ".claude", "projects");
|
|
1227
1273
|
async function parseSessionFile(filePath) {
|
|
1228
1274
|
try {
|
|
1229
|
-
const stream =
|
|
1275
|
+
const stream = import_fs10.default.createReadStream(filePath, { encoding: "utf-8" });
|
|
1230
1276
|
const rl = import_readline.default.createInterface({ input: stream, crlfDelay: Infinity });
|
|
1231
1277
|
let firstMessage = "";
|
|
1232
1278
|
let lastTimestamp = "";
|
|
@@ -1274,17 +1320,17 @@ async function parseSessionFile(filePath) {
|
|
|
1274
1320
|
}
|
|
1275
1321
|
async function listClaudeSessions(filterProjectPath) {
|
|
1276
1322
|
const sessions2 = [];
|
|
1277
|
-
if (!
|
|
1323
|
+
if (!import_fs10.default.existsSync(CLAUDE_PROJECTS_DIR)) return sessions2;
|
|
1278
1324
|
const isDirSafe = (d) => {
|
|
1279
1325
|
try {
|
|
1280
|
-
return
|
|
1326
|
+
return import_fs10.default.statSync(import_path11.default.join(CLAUDE_PROJECTS_DIR, d)).isDirectory();
|
|
1281
1327
|
} catch {
|
|
1282
1328
|
return false;
|
|
1283
1329
|
}
|
|
1284
1330
|
};
|
|
1285
1331
|
let allEntries;
|
|
1286
1332
|
try {
|
|
1287
|
-
allEntries =
|
|
1333
|
+
allEntries = import_fs10.default.readdirSync(CLAUDE_PROJECTS_DIR);
|
|
1288
1334
|
} catch {
|
|
1289
1335
|
return sessions2;
|
|
1290
1336
|
}
|
|
@@ -1297,16 +1343,16 @@ async function listClaudeSessions(filterProjectPath) {
|
|
|
1297
1343
|
projectDirs = allEntries.filter((d) => isDirSafe(d) && !d.startsWith("-private-"));
|
|
1298
1344
|
}
|
|
1299
1345
|
for (const dirName of projectDirs) {
|
|
1300
|
-
const dirPath =
|
|
1346
|
+
const dirPath = import_path11.default.join(CLAUDE_PROJECTS_DIR, dirName);
|
|
1301
1347
|
let files;
|
|
1302
1348
|
try {
|
|
1303
|
-
files =
|
|
1349
|
+
files = import_fs10.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
|
|
1304
1350
|
} catch {
|
|
1305
1351
|
continue;
|
|
1306
1352
|
}
|
|
1307
1353
|
for (const file of files) {
|
|
1308
1354
|
const sessionId = file.replace(".jsonl", "");
|
|
1309
|
-
const parsed = await parseSessionFile(
|
|
1355
|
+
const parsed = await parseSessionFile(import_path11.default.join(dirPath, file));
|
|
1310
1356
|
if (!parsed) continue;
|
|
1311
1357
|
const projectPath = parsed.cwd || dirName.replace(/^-/, "/").replace(/-/g, "/");
|
|
1312
1358
|
sessions2.push({
|
|
@@ -1326,21 +1372,21 @@ async function listClaudeSessions(filterProjectPath) {
|
|
|
1326
1372
|
}
|
|
1327
1373
|
|
|
1328
1374
|
// src/claude-conversation.ts
|
|
1329
|
-
var
|
|
1330
|
-
var
|
|
1375
|
+
var import_fs11 = __toESM(require("fs"));
|
|
1376
|
+
var import_path12 = __toESM(require("path"));
|
|
1331
1377
|
var import_os8 = __toESM(require("os"));
|
|
1332
1378
|
var import_readline2 = __toESM(require("readline"));
|
|
1333
|
-
var CLAUDE_PROJECTS_DIR2 =
|
|
1379
|
+
var CLAUDE_PROJECTS_DIR2 = import_path12.default.join(import_os8.default.homedir(), ".claude", "projects");
|
|
1334
1380
|
function findProjectDir(projectPath) {
|
|
1335
1381
|
const encoded = encodeProjectPath(projectPath);
|
|
1336
|
-
const dirPath =
|
|
1337
|
-
if (
|
|
1338
|
-
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;
|
|
1339
1385
|
let allDirs;
|
|
1340
1386
|
try {
|
|
1341
|
-
allDirs =
|
|
1387
|
+
allDirs = import_fs11.default.readdirSync(CLAUDE_PROJECTS_DIR2).filter((d) => {
|
|
1342
1388
|
try {
|
|
1343
|
-
return
|
|
1389
|
+
return import_fs11.default.statSync(import_path12.default.join(CLAUDE_PROJECTS_DIR2, d)).isDirectory();
|
|
1344
1390
|
} catch {
|
|
1345
1391
|
return false;
|
|
1346
1392
|
}
|
|
@@ -1350,23 +1396,23 @@ function findProjectDir(projectPath) {
|
|
|
1350
1396
|
}
|
|
1351
1397
|
const matches = findProjectDirs(allDirs, projectPath);
|
|
1352
1398
|
if (matches.length > 0) {
|
|
1353
|
-
return
|
|
1399
|
+
return import_path12.default.join(CLAUDE_PROJECTS_DIR2, matches[0]);
|
|
1354
1400
|
}
|
|
1355
1401
|
return null;
|
|
1356
1402
|
}
|
|
1357
1403
|
function findSessionFileById(sessionId) {
|
|
1358
1404
|
if (!/^[A-Za-z0-9._-]+$/.test(sessionId)) return null;
|
|
1359
|
-
if (!
|
|
1405
|
+
if (!import_fs11.default.existsSync(CLAUDE_PROJECTS_DIR2)) return null;
|
|
1360
1406
|
let dirs;
|
|
1361
1407
|
try {
|
|
1362
|
-
dirs =
|
|
1408
|
+
dirs = import_fs11.default.readdirSync(CLAUDE_PROJECTS_DIR2);
|
|
1363
1409
|
} catch {
|
|
1364
1410
|
return null;
|
|
1365
1411
|
}
|
|
1366
1412
|
for (const dir of dirs) {
|
|
1367
|
-
const candidate =
|
|
1413
|
+
const candidate = import_path12.default.join(CLAUDE_PROJECTS_DIR2, dir, `${sessionId}.jsonl`);
|
|
1368
1414
|
try {
|
|
1369
|
-
if (
|
|
1415
|
+
if (import_fs11.default.statSync(candidate).isFile()) return candidate;
|
|
1370
1416
|
} catch {
|
|
1371
1417
|
}
|
|
1372
1418
|
}
|
|
@@ -1377,20 +1423,20 @@ function findLatestSessionFile(projectPath) {
|
|
|
1377
1423
|
if (!dirPath) return null;
|
|
1378
1424
|
let entries;
|
|
1379
1425
|
try {
|
|
1380
|
-
entries =
|
|
1426
|
+
entries = import_fs11.default.readdirSync(dirPath);
|
|
1381
1427
|
} catch {
|
|
1382
1428
|
return null;
|
|
1383
1429
|
}
|
|
1384
1430
|
const files = entries.filter((f) => f.endsWith(".jsonl") && !f.includes("/")).map((f) => {
|
|
1385
1431
|
try {
|
|
1386
|
-
return { name: f, mtime:
|
|
1432
|
+
return { name: f, mtime: import_fs11.default.statSync(import_path12.default.join(dirPath, f)).mtimeMs };
|
|
1387
1433
|
} catch {
|
|
1388
1434
|
return null;
|
|
1389
1435
|
}
|
|
1390
1436
|
}).filter((f) => f !== null).sort((a, b) => b.mtime - a.mtime);
|
|
1391
1437
|
if (files.length === 0) return null;
|
|
1392
1438
|
return {
|
|
1393
|
-
filePath:
|
|
1439
|
+
filePath: import_path12.default.join(dirPath, files[0].name),
|
|
1394
1440
|
sessionId: files[0].name.replace(".jsonl", "")
|
|
1395
1441
|
};
|
|
1396
1442
|
}
|
|
@@ -1400,8 +1446,8 @@ async function readConversation(projectPath, afterLine = 0, specificSessionId) {
|
|
|
1400
1446
|
if (specificSessionId) {
|
|
1401
1447
|
sessionId = specificSessionId;
|
|
1402
1448
|
const dirPath = findProjectDir(projectPath);
|
|
1403
|
-
const direct = dirPath ?
|
|
1404
|
-
if (direct &&
|
|
1449
|
+
const direct = dirPath ? import_path12.default.join(dirPath, `${specificSessionId}.jsonl`) : null;
|
|
1450
|
+
if (direct && import_fs11.default.existsSync(direct)) {
|
|
1405
1451
|
filePath = direct;
|
|
1406
1452
|
} else {
|
|
1407
1453
|
const byId = findSessionFileById(specificSessionId);
|
|
@@ -1415,7 +1461,7 @@ async function readConversation(projectPath, afterLine = 0, specificSessionId) {
|
|
|
1415
1461
|
sessionId = found.sessionId;
|
|
1416
1462
|
}
|
|
1417
1463
|
try {
|
|
1418
|
-
const stream =
|
|
1464
|
+
const stream = import_fs11.default.createReadStream(filePath, { encoding: "utf-8" });
|
|
1419
1465
|
const rl = import_readline2.default.createInterface({ input: stream, crlfDelay: Infinity });
|
|
1420
1466
|
const messages = [];
|
|
1421
1467
|
let lineNum = 0;
|
|
@@ -1504,6 +1550,13 @@ if (!isConfigured()) {
|
|
|
1504
1550
|
}
|
|
1505
1551
|
var config = loadConfig();
|
|
1506
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
|
+
}
|
|
1507
1560
|
ensurePtyHelperExecutable();
|
|
1508
1561
|
process.on("unhandledRejection", (reason) => {
|
|
1509
1562
|
logger.error({ reason }, "Unhandled promise rejection (agent kept alive)");
|