ragent-cli 1.11.4 → 1.11.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +296 -44
- package/dist/sbom.json +5 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -31,7 +31,7 @@ var require_package = __commonJS({
|
|
|
31
31
|
"package.json"(exports2, module2) {
|
|
32
32
|
module2.exports = {
|
|
33
33
|
name: "ragent-cli",
|
|
34
|
-
version: "1.11.
|
|
34
|
+
version: "1.11.6",
|
|
35
35
|
description: "CLI agent for rAgent Live \u2014 browser-first terminal control plane for AI coding agents",
|
|
36
36
|
main: "dist/index.js",
|
|
37
37
|
bin: {
|
|
@@ -111,7 +111,7 @@ var require_package = __commonJS({
|
|
|
111
111
|
});
|
|
112
112
|
|
|
113
113
|
// src/index.ts
|
|
114
|
-
var
|
|
114
|
+
var fs8 = __toESM(require("fs"));
|
|
115
115
|
var import_commander = require("commander");
|
|
116
116
|
|
|
117
117
|
// src/constants.ts
|
|
@@ -311,12 +311,12 @@ async function maybeWarnUpdate() {
|
|
|
311
311
|
}
|
|
312
312
|
|
|
313
313
|
// src/commands/connect.ts
|
|
314
|
-
var
|
|
314
|
+
var os10 = __toESM(require("os"));
|
|
315
315
|
|
|
316
316
|
// src/agent.ts
|
|
317
|
-
var
|
|
318
|
-
var
|
|
319
|
-
var
|
|
317
|
+
var fs6 = __toESM(require("fs"));
|
|
318
|
+
var os9 = __toESM(require("os"));
|
|
319
|
+
var path5 = __toESM(require("path"));
|
|
320
320
|
var import_ws5 = __toESM(require("ws"));
|
|
321
321
|
|
|
322
322
|
// src/auth.ts
|
|
@@ -1288,14 +1288,14 @@ function pathMatchesAgentTranscript(target, agentType) {
|
|
|
1288
1288
|
if (agentType === "Gemini CLI") return isGeminiTranscript;
|
|
1289
1289
|
return isClaudeTranscript || isCodexTranscript || isGeminiTranscript;
|
|
1290
1290
|
}
|
|
1291
|
-
function getNewestPath(paths) {
|
|
1291
|
+
function getNewestPath(paths, options) {
|
|
1292
1292
|
const ranked = paths.map((target) => {
|
|
1293
1293
|
try {
|
|
1294
1294
|
return { target, mtimeMs: fs2.statSync(target).mtimeMs };
|
|
1295
1295
|
} catch {
|
|
1296
1296
|
return null;
|
|
1297
1297
|
}
|
|
1298
|
-
}).filter((entry) => entry !== null).sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
1298
|
+
}).filter((entry) => entry !== null).filter((entry) => options?.minMtimeMs === void 0 || entry.mtimeMs >= options.minMtimeMs).sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
1299
1299
|
return ranked[0]?.target ?? null;
|
|
1300
1300
|
}
|
|
1301
1301
|
function discoverTranscriptForPid(pid, agentType) {
|
|
@@ -1327,6 +1327,79 @@ function listChildPids(pid) {
|
|
|
1327
1327
|
return [];
|
|
1328
1328
|
}
|
|
1329
1329
|
}
|
|
1330
|
+
function getProcessCommand(pid) {
|
|
1331
|
+
try {
|
|
1332
|
+
return (0, import_child_process.execFileSync)("ps", ["-p", String(pid), "-o", "args="], {
|
|
1333
|
+
encoding: "utf-8",
|
|
1334
|
+
timeout: 3e3,
|
|
1335
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
1336
|
+
}).trim() || null;
|
|
1337
|
+
} catch {
|
|
1338
|
+
return null;
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
function commandMatchesAgent(command, agentType) {
|
|
1342
|
+
if (!command || !agentType) return false;
|
|
1343
|
+
const parts = command.toLowerCase().trim().split(/\s+/);
|
|
1344
|
+
const binary = parts[0]?.split("/").pop() ?? "";
|
|
1345
|
+
const scriptArg = parts[1]?.split("/").pop() ?? "";
|
|
1346
|
+
if (agentType === "Claude Code") {
|
|
1347
|
+
return binary === "claude" || binary === "claude-code" || command.includes("claude-code");
|
|
1348
|
+
}
|
|
1349
|
+
if (agentType === "Codex CLI") {
|
|
1350
|
+
return binary === "codex" || scriptArg === "codex" || command.includes("codex-cli");
|
|
1351
|
+
}
|
|
1352
|
+
if (agentType === "Gemini CLI") {
|
|
1353
|
+
return binary === "gemini" || scriptArg === "gemini";
|
|
1354
|
+
}
|
|
1355
|
+
return false;
|
|
1356
|
+
}
|
|
1357
|
+
function getProcessStartEpochMs(pid) {
|
|
1358
|
+
try {
|
|
1359
|
+
const stat = fs2.readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
1360
|
+
const closeParen = stat.lastIndexOf(")");
|
|
1361
|
+
if (closeParen < 0) return null;
|
|
1362
|
+
const rest = stat.slice(closeParen + 2).split(" ");
|
|
1363
|
+
const startTicks = Number(rest[19]);
|
|
1364
|
+
if (!Number.isFinite(startTicks) || startTicks < 0) return null;
|
|
1365
|
+
const procStat = fs2.readFileSync("/proc/stat", "utf8");
|
|
1366
|
+
const bootLine = procStat.split("\n").find((line) => line.startsWith("btime "));
|
|
1367
|
+
const bootSeconds = Number(bootLine?.split(/\s+/)[1]);
|
|
1368
|
+
if (!Number.isFinite(bootSeconds) || bootSeconds <= 0) return null;
|
|
1369
|
+
const ticksPerSecond = Number((0, import_child_process.execFileSync)("getconf", ["CLK_TCK"], {
|
|
1370
|
+
encoding: "utf-8",
|
|
1371
|
+
timeout: 3e3
|
|
1372
|
+
}).trim());
|
|
1373
|
+
if (!Number.isFinite(ticksPerSecond) || ticksPerSecond <= 0) return null;
|
|
1374
|
+
return (bootSeconds + startTicks / ticksPerSecond) * 1e3;
|
|
1375
|
+
} catch {
|
|
1376
|
+
return null;
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
function findAgentProcessInfos(rootPid, agentType) {
|
|
1380
|
+
const queue = [rootPid];
|
|
1381
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1382
|
+
const infos = [];
|
|
1383
|
+
while (queue.length > 0 && visited.size < 64) {
|
|
1384
|
+
const pid = queue.shift();
|
|
1385
|
+
if (!pid || visited.has(pid)) continue;
|
|
1386
|
+
visited.add(pid);
|
|
1387
|
+
const command = getProcessCommand(pid);
|
|
1388
|
+
if (commandMatchesAgent(command, agentType)) {
|
|
1389
|
+
let cwd = null;
|
|
1390
|
+
try {
|
|
1391
|
+
cwd = fs2.readlinkSync(`/proc/${pid}/cwd`);
|
|
1392
|
+
} catch {
|
|
1393
|
+
cwd = null;
|
|
1394
|
+
}
|
|
1395
|
+
infos.push({ pid, command, startEpochMs: getProcessStartEpochMs(pid), cwd });
|
|
1396
|
+
}
|
|
1397
|
+
for (const childPid of listChildPids(pid)) {
|
|
1398
|
+
if (!visited.has(childPid)) queue.push(childPid);
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
return infos;
|
|
1402
|
+
}
|
|
1330
1403
|
function discoverViaProc(rootPid, agentType) {
|
|
1331
1404
|
const queue = [rootPid];
|
|
1332
1405
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -1347,10 +1420,10 @@ function discoverViaProc(rootPid, agentType) {
|
|
|
1347
1420
|
}
|
|
1348
1421
|
return getNewestPath(candidates);
|
|
1349
1422
|
}
|
|
1350
|
-
function discoverViaProcessCwd(pid) {
|
|
1423
|
+
function discoverViaProcessCwd(pid, minMtimeMs) {
|
|
1351
1424
|
try {
|
|
1352
1425
|
const processCwd = fs2.readlinkSync(`/proc/${pid}/cwd`);
|
|
1353
|
-
return processCwd ? discoverViaCwd(processCwd, pid) : null;
|
|
1426
|
+
return processCwd ? discoverViaCwd(processCwd, pid, minMtimeMs) : null;
|
|
1354
1427
|
} catch {
|
|
1355
1428
|
return null;
|
|
1356
1429
|
}
|
|
@@ -1378,6 +1451,35 @@ function resolveUserHome(pid) {
|
|
|
1378
1451
|
return null;
|
|
1379
1452
|
}
|
|
1380
1453
|
}
|
|
1454
|
+
function discoverCodexTranscriptFromHome(pid, minMtimeMs) {
|
|
1455
|
+
const userHome = resolveUserHome(pid);
|
|
1456
|
+
if (!userHome) return null;
|
|
1457
|
+
const sessionsDir = path2.join(userHome, ".codex", "sessions");
|
|
1458
|
+
if (!fs2.existsSync(sessionsDir)) return null;
|
|
1459
|
+
const matches = [];
|
|
1460
|
+
const stack = [sessionsDir];
|
|
1461
|
+
let visited = 0;
|
|
1462
|
+
while (stack.length > 0 && visited < 3e3) {
|
|
1463
|
+
const current = stack.pop();
|
|
1464
|
+
if (!current) continue;
|
|
1465
|
+
visited++;
|
|
1466
|
+
let entries;
|
|
1467
|
+
try {
|
|
1468
|
+
entries = fs2.readdirSync(current, { withFileTypes: true });
|
|
1469
|
+
} catch {
|
|
1470
|
+
continue;
|
|
1471
|
+
}
|
|
1472
|
+
for (const entry of entries) {
|
|
1473
|
+
const fullPath = path2.join(current, entry.name);
|
|
1474
|
+
if (entry.isDirectory()) {
|
|
1475
|
+
stack.push(fullPath);
|
|
1476
|
+
} else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
1477
|
+
matches.push(fullPath);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
return getNewestPath(matches, { minMtimeMs });
|
|
1482
|
+
}
|
|
1381
1483
|
function discoverGeminiTranscriptFromHome(pid) {
|
|
1382
1484
|
const userHome = resolveUserHome(pid);
|
|
1383
1485
|
if (!userHome) return null;
|
|
@@ -1407,27 +1509,21 @@ function discoverGeminiTranscriptFromHome(pid) {
|
|
|
1407
1509
|
}
|
|
1408
1510
|
return getNewestPath(matches);
|
|
1409
1511
|
}
|
|
1410
|
-
function discoverViaCwd(paneCwd, pid) {
|
|
1512
|
+
function discoverViaCwd(paneCwd, pid, minMtimeMs) {
|
|
1411
1513
|
const userHome = resolveUserHome(pid);
|
|
1412
1514
|
if (!userHome) return null;
|
|
1413
1515
|
const claudeProjectsDir = path2.join(userHome, ".claude", "projects");
|
|
1414
1516
|
if (!fs2.existsSync(claudeProjectsDir)) return null;
|
|
1415
|
-
|
|
1517
|
+
const possibleCwds = /* @__PURE__ */ new Set([paneCwd]);
|
|
1416
1518
|
try {
|
|
1417
|
-
|
|
1519
|
+
possibleCwds.add(fs2.realpathSync(paneCwd));
|
|
1418
1520
|
} catch {
|
|
1419
|
-
return null;
|
|
1420
1521
|
}
|
|
1421
|
-
const
|
|
1422
|
-
|
|
1423
|
-
if (!fs2.existsSync(projectDir)) return null;
|
|
1522
|
+
const projectDirs = Array.from(possibleCwds).map((cwd) => path2.join(claudeProjectsDir, cwd.replace(/\//g, "-"))).filter((projectDir, index, arr) => arr.indexOf(projectDir) === index).filter((projectDir) => fs2.existsSync(projectDir));
|
|
1523
|
+
if (projectDirs.length === 0) return null;
|
|
1424
1524
|
try {
|
|
1425
|
-
const jsonlFiles = fs2.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).map((f) =>
|
|
1426
|
-
|
|
1427
|
-
const stat = fs2.statSync(fullPath);
|
|
1428
|
-
return { path: fullPath, mtime: stat.mtimeMs };
|
|
1429
|
-
}).sort((a, b) => b.mtime - a.mtime);
|
|
1430
|
-
return jsonlFiles.length > 0 ? jsonlFiles[0].path : null;
|
|
1525
|
+
const jsonlFiles = projectDirs.flatMap((projectDir) => fs2.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).map((f) => path2.join(projectDir, f)));
|
|
1526
|
+
return getNewestPath(jsonlFiles, { minMtimeMs });
|
|
1431
1527
|
} catch {
|
|
1432
1528
|
return null;
|
|
1433
1529
|
}
|
|
@@ -1438,9 +1534,18 @@ function discoverTranscriptFile(sessionId, agentType) {
|
|
|
1438
1534
|
const procResult = discoverViaProc(processPid, agentType);
|
|
1439
1535
|
if (procResult) return procResult;
|
|
1440
1536
|
if (agentType === "Claude Code") {
|
|
1441
|
-
const
|
|
1537
|
+
const startMs = getProcessStartEpochMs(processPid);
|
|
1538
|
+
const cwdResult = discoverViaProcessCwd(processPid, startMs ? startMs - 6e4 : void 0);
|
|
1442
1539
|
if (cwdResult) return cwdResult;
|
|
1443
1540
|
}
|
|
1541
|
+
if (agentType === "Codex CLI") {
|
|
1542
|
+
const startMs = getProcessStartEpochMs(processPid);
|
|
1543
|
+
const command = getProcessCommand(processPid);
|
|
1544
|
+
if (commandMatchesAgent(command, agentType)) {
|
|
1545
|
+
const codexResult = discoverCodexTranscriptFromHome(processPid, startMs ? startMs - 6e4 : void 0);
|
|
1546
|
+
if (codexResult) return codexResult;
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1444
1549
|
if (agentType === "Gemini CLI") {
|
|
1445
1550
|
const geminiResult = discoverGeminiTranscriptFromHome(processPid);
|
|
1446
1551
|
if (geminiResult) return geminiResult;
|
|
@@ -1469,6 +1574,16 @@ function discoverTranscriptFile(sessionId, agentType) {
|
|
|
1469
1574
|
} catch {
|
|
1470
1575
|
}
|
|
1471
1576
|
if (agentType === "Claude Code") {
|
|
1577
|
+
const agentInfos = panePid ? findAgentProcessInfos(panePid, agentType) : [];
|
|
1578
|
+
for (const info of agentInfos) {
|
|
1579
|
+
if (!info.cwd) continue;
|
|
1580
|
+
const cwdResult = discoverViaCwd(
|
|
1581
|
+
info.cwd,
|
|
1582
|
+
info.pid,
|
|
1583
|
+
info.startEpochMs ? info.startEpochMs - 6e4 : void 0
|
|
1584
|
+
);
|
|
1585
|
+
if (cwdResult) return cwdResult;
|
|
1586
|
+
}
|
|
1472
1587
|
try {
|
|
1473
1588
|
const paneCwd = (0, import_child_process.execFileSync)(
|
|
1474
1589
|
"tmux",
|
|
@@ -1479,6 +1594,16 @@ function discoverTranscriptFile(sessionId, agentType) {
|
|
|
1479
1594
|
} catch {
|
|
1480
1595
|
}
|
|
1481
1596
|
}
|
|
1597
|
+
if (agentType === "Codex CLI") {
|
|
1598
|
+
const agentInfos = panePid ? findAgentProcessInfos(panePid, agentType) : [];
|
|
1599
|
+
const newestForAgent = getNewestPath(
|
|
1600
|
+
agentInfos.map((info) => discoverCodexTranscriptFromHome(
|
|
1601
|
+
info.pid,
|
|
1602
|
+
info.startEpochMs ? info.startEpochMs - 6e4 : void 0
|
|
1603
|
+
)).filter((value) => Boolean(value))
|
|
1604
|
+
);
|
|
1605
|
+
if (newestForAgent) return newestForAgent;
|
|
1606
|
+
}
|
|
1482
1607
|
if (agentType === "Gemini CLI") {
|
|
1483
1608
|
return discoverGeminiTranscriptFromHome(panePid ?? void 0);
|
|
1484
1609
|
}
|
|
@@ -3657,6 +3782,10 @@ var SequenceTracker = class {
|
|
|
3657
3782
|
var import_node_child_process3 = require("child_process");
|
|
3658
3783
|
var pty2 = __toESM(require("node-pty"));
|
|
3659
3784
|
var log9 = createLogger("pty");
|
|
3785
|
+
var MIN_TMUX_COLS = 20;
|
|
3786
|
+
var MAX_TMUX_COLS = 500;
|
|
3787
|
+
var MIN_TMUX_ROWS = 5;
|
|
3788
|
+
var MAX_TMUX_ROWS = 200;
|
|
3660
3789
|
var ANSI_KEY_SEQUENCES = /* @__PURE__ */ new Map([
|
|
3661
3790
|
["\x1B[A", "Up"],
|
|
3662
3791
|
["\x1B[B", "Down"],
|
|
@@ -3756,6 +3885,13 @@ function execTmuxSendKeys(args) {
|
|
|
3756
3885
|
});
|
|
3757
3886
|
});
|
|
3758
3887
|
}
|
|
3888
|
+
function isValidTmuxSessionName(target) {
|
|
3889
|
+
const sessionName = target.split(":")[0].split(".")[0];
|
|
3890
|
+
return /^[a-zA-Z0-9_][a-zA-Z0-9_.-]*$/.test(sessionName);
|
|
3891
|
+
}
|
|
3892
|
+
function clampDimension(value, min, max) {
|
|
3893
|
+
return Math.max(min, Math.min(max, Math.floor(value)));
|
|
3894
|
+
}
|
|
3759
3895
|
function isInteractiveShell(command) {
|
|
3760
3896
|
const trimmed = String(command).trim();
|
|
3761
3897
|
return ["bash", "sh", "zsh", "fish"].includes(trimmed);
|
|
@@ -3786,8 +3922,8 @@ async function sendInputToTmux(sessionId, data) {
|
|
|
3786
3922
|
if (!sessionId.startsWith("tmux:")) return;
|
|
3787
3923
|
const target = sessionId.slice("tmux:".length).trim();
|
|
3788
3924
|
if (!target) return;
|
|
3789
|
-
|
|
3790
|
-
|
|
3925
|
+
if (!isValidTmuxSessionName(target)) {
|
|
3926
|
+
const sessionName = target.split(":")[0].split(".")[0];
|
|
3791
3927
|
log9.warn("invalid tmux session name", { sessionName });
|
|
3792
3928
|
return;
|
|
3793
3929
|
}
|
|
@@ -3804,6 +3940,35 @@ async function sendInputToTmux(sessionId, data) {
|
|
|
3804
3940
|
log9.warn("failed to send input", { sessionId, error: message });
|
|
3805
3941
|
}
|
|
3806
3942
|
}
|
|
3943
|
+
async function resizeTmuxPaneBySessionId(sessionId, cols, rows) {
|
|
3944
|
+
if (!sessionId.startsWith("tmux:")) return false;
|
|
3945
|
+
const target = sessionId.slice("tmux:".length).trim();
|
|
3946
|
+
if (!target) return false;
|
|
3947
|
+
if (!isValidTmuxSessionName(target)) {
|
|
3948
|
+
const sessionName = target.split(":")[0].split(".")[0];
|
|
3949
|
+
log9.warn("invalid tmux session name", { sessionName });
|
|
3950
|
+
return false;
|
|
3951
|
+
}
|
|
3952
|
+
if (!Number.isFinite(cols) || !Number.isFinite(rows)) return false;
|
|
3953
|
+
const safeCols = clampDimension(cols, MIN_TMUX_COLS, MAX_TMUX_COLS);
|
|
3954
|
+
const safeRows = clampDimension(rows, MIN_TMUX_ROWS, MAX_TMUX_ROWS);
|
|
3955
|
+
return new Promise((resolve) => {
|
|
3956
|
+
(0, import_node_child_process3.execFile)(
|
|
3957
|
+
"tmux",
|
|
3958
|
+
["resize-pane", "-t", target, "-x", String(safeCols), "-y", String(safeRows)],
|
|
3959
|
+
{ timeout: 5e3 },
|
|
3960
|
+
(err) => {
|
|
3961
|
+
if (err) {
|
|
3962
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3963
|
+
log9.warn("failed to resize tmux pane", { sessionId, error: message });
|
|
3964
|
+
resolve(false);
|
|
3965
|
+
return;
|
|
3966
|
+
}
|
|
3967
|
+
resolve(true);
|
|
3968
|
+
}
|
|
3969
|
+
);
|
|
3970
|
+
});
|
|
3971
|
+
}
|
|
3807
3972
|
async function stopAllDetachedTmuxSessions() {
|
|
3808
3973
|
try {
|
|
3809
3974
|
const raw = await execAsync(
|
|
@@ -4196,6 +4361,9 @@ var ConnectionManager = class {
|
|
|
4196
4361
|
// src/control-dispatcher.ts
|
|
4197
4362
|
var crypto2 = __toESM(require("crypto"));
|
|
4198
4363
|
var import_child_process5 = require("child_process");
|
|
4364
|
+
var fs5 = __toESM(require("fs"));
|
|
4365
|
+
var os8 = __toESM(require("os"));
|
|
4366
|
+
var path4 = __toESM(require("path"));
|
|
4199
4367
|
var import_ws4 = __toESM(require("ws"));
|
|
4200
4368
|
|
|
4201
4369
|
// src/service.ts
|
|
@@ -4853,6 +5021,10 @@ function isProcessAlive(pid) {
|
|
|
4853
5021
|
function delay(ms) {
|
|
4854
5022
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
4855
5023
|
}
|
|
5024
|
+
function safeAttachmentName(name) {
|
|
5025
|
+
const base = path4.basename(name || "attachment").replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
5026
|
+
return base.slice(0, 120) || "attachment";
|
|
5027
|
+
}
|
|
4856
5028
|
var ControlDispatcher = class {
|
|
4857
5029
|
shell;
|
|
4858
5030
|
streamer;
|
|
@@ -4931,9 +5103,10 @@ var ControlDispatcher = class {
|
|
|
4931
5103
|
"disconnect",
|
|
4932
5104
|
"kill-process",
|
|
4933
5105
|
"start-agent",
|
|
4934
|
-
"provision"
|
|
5106
|
+
"provision",
|
|
5107
|
+
"upload-file"
|
|
4935
5108
|
]);
|
|
4936
|
-
if (dangerousActions.has(action) && this.connection.sessionKey) {
|
|
5109
|
+
if (dangerousActions.has(action) && this.connection.sessionKey && payload._verifiedEncrypted !== true) {
|
|
4937
5110
|
if (!this.verifyMessageHmac(payload)) {
|
|
4938
5111
|
log13.warn("rejecting control action \u2014 HMAC verification failed", { action });
|
|
4939
5112
|
return;
|
|
@@ -5065,9 +5238,42 @@ var ControlDispatcher = class {
|
|
|
5065
5238
|
this.transcriptWatcher.handleSyncRequest(sessionId, fromSeq);
|
|
5066
5239
|
}
|
|
5067
5240
|
return;
|
|
5241
|
+
case "upload-file":
|
|
5242
|
+
this.handleUploadFile(sessionId, payload);
|
|
5243
|
+
return;
|
|
5068
5244
|
default:
|
|
5069
5245
|
}
|
|
5070
5246
|
}
|
|
5247
|
+
handleUploadFile(sessionId, payload) {
|
|
5248
|
+
if (!sessionId) return;
|
|
5249
|
+
const fileName = typeof payload.fileName === "string" ? payload.fileName : "attachment";
|
|
5250
|
+
const contentBase64 = typeof payload.contentBase64 === "string" ? payload.contentBase64 : "";
|
|
5251
|
+
if (!contentBase64 || contentBase64.length > 8 * 1024 * 1024) {
|
|
5252
|
+
log13.warn("rejecting attachment upload \u2014 invalid size", { sessionId, fileName });
|
|
5253
|
+
return;
|
|
5254
|
+
}
|
|
5255
|
+
let content;
|
|
5256
|
+
try {
|
|
5257
|
+
content = Buffer.from(contentBase64, "base64");
|
|
5258
|
+
} catch {
|
|
5259
|
+
log13.warn("rejecting attachment upload \u2014 invalid base64", { sessionId, fileName });
|
|
5260
|
+
return;
|
|
5261
|
+
}
|
|
5262
|
+
if (content.length === 0 || content.length > 6 * 1024 * 1024) {
|
|
5263
|
+
log13.warn("rejecting attachment upload \u2014 decoded size out of bounds", {
|
|
5264
|
+
sessionId,
|
|
5265
|
+
fileName,
|
|
5266
|
+
bytes: content.length
|
|
5267
|
+
});
|
|
5268
|
+
return;
|
|
5269
|
+
}
|
|
5270
|
+
const uploadDir = path4.join(os8.homedir(), ".cache", "ragent", "attachments");
|
|
5271
|
+
fs5.mkdirSync(uploadDir, { recursive: true, mode: 448 });
|
|
5272
|
+
const writtenPath = path4.join(uploadDir, `${Date.now()}-${safeAttachmentName(fileName)}`);
|
|
5273
|
+
fs5.writeFileSync(writtenPath, content, { mode: 384 });
|
|
5274
|
+
log13.info("attachment uploaded", { sessionId, path: writtenPath, bytes: content.length });
|
|
5275
|
+
this.handleInput(writtenPath, sessionId);
|
|
5276
|
+
}
|
|
5071
5277
|
/** Handle input routing to the correct target. */
|
|
5072
5278
|
handleInput(data, sessionId) {
|
|
5073
5279
|
this.connection.requestInventorySync(800);
|
|
@@ -5080,10 +5286,17 @@ var ControlDispatcher = class {
|
|
|
5080
5286
|
this.streamer.writeInput(sessionId, data);
|
|
5081
5287
|
}
|
|
5082
5288
|
}
|
|
5083
|
-
/** Handle resize routing
|
|
5289
|
+
/** Handle resize routing. */
|
|
5084
5290
|
handleResize(cols, rows, sessionId) {
|
|
5085
5291
|
if (!sessionId || sessionId.startsWith("pty:")) {
|
|
5086
5292
|
this.shell.resize(cols, rows);
|
|
5293
|
+
} else if (sessionId.startsWith("tmux:")) {
|
|
5294
|
+
resizeTmuxPaneBySessionId(sessionId, cols, rows).then((resized) => {
|
|
5295
|
+
if (resized) this.streamer.resyncStream(sessionId);
|
|
5296
|
+
}).catch((error) => {
|
|
5297
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5298
|
+
log13.warn("tmux resize failed", { sessionId, error: message });
|
|
5299
|
+
});
|
|
5087
5300
|
} else if (sessionId.startsWith("screen:") || sessionId.startsWith("zellij:")) {
|
|
5088
5301
|
this.streamer.resize(sessionId, cols, rows);
|
|
5089
5302
|
}
|
|
@@ -5645,11 +5858,11 @@ var ApprovalEnforcer = class {
|
|
|
5645
5858
|
var log15 = createLogger("agent");
|
|
5646
5859
|
var MAX_TRANSPORT_CHUNK_BYTES = 24 * 1024;
|
|
5647
5860
|
function pidFilePath(hostId) {
|
|
5648
|
-
return
|
|
5861
|
+
return path5.join(CONFIG_DIR, `agent-${hostId}.pid`);
|
|
5649
5862
|
}
|
|
5650
5863
|
function readPidFile(filePath) {
|
|
5651
5864
|
try {
|
|
5652
|
-
const raw =
|
|
5865
|
+
const raw = fs6.readFileSync(filePath, "utf8").trim();
|
|
5653
5866
|
const pid = Number.parseInt(raw, 10);
|
|
5654
5867
|
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
5655
5868
|
} catch {
|
|
@@ -5674,7 +5887,7 @@ function acquirePidLock(hostId) {
|
|
|
5674
5887
|
Stop it first with: kill ${existingPid} \u2014 or: ragent service stop`
|
|
5675
5888
|
);
|
|
5676
5889
|
}
|
|
5677
|
-
|
|
5890
|
+
fs6.writeFileSync(lockPath, `${process.pid}
|
|
5678
5891
|
`, "utf8");
|
|
5679
5892
|
return lockPath;
|
|
5680
5893
|
}
|
|
@@ -5682,7 +5895,7 @@ function releasePidLock(lockPath) {
|
|
|
5682
5895
|
try {
|
|
5683
5896
|
const currentPid = readPidFile(lockPath);
|
|
5684
5897
|
if (currentPid === process.pid) {
|
|
5685
|
-
|
|
5898
|
+
fs6.unlinkSync(lockPath);
|
|
5686
5899
|
}
|
|
5687
5900
|
} catch {
|
|
5688
5901
|
}
|
|
@@ -5691,7 +5904,7 @@ function resolveRunOptions(commandOptions) {
|
|
|
5691
5904
|
const config = loadConfig();
|
|
5692
5905
|
const portal = commandOptions.portal || config.portal || DEFAULT_PORTAL;
|
|
5693
5906
|
const hostId = sanitizeHostId(commandOptions.id || config.hostId || inferHostId());
|
|
5694
|
-
const hostName = commandOptions.name || config.hostName ||
|
|
5907
|
+
const hostName = commandOptions.name || config.hostName || os9.hostname();
|
|
5695
5908
|
const command = commandOptions.command || config.command || "bash";
|
|
5696
5909
|
const agentToken = commandOptions.agentToken || config.agentToken || "";
|
|
5697
5910
|
return { portal, hostId, hostName, command, agentToken };
|
|
@@ -5927,7 +6140,7 @@ async function runAgent(rawOptions) {
|
|
|
5927
6140
|
sendToGroup(ws, groups.privateGroup, {
|
|
5928
6141
|
type: "register",
|
|
5929
6142
|
hostName: options.hostName,
|
|
5930
|
-
environment:
|
|
6143
|
+
environment: os9.platform()
|
|
5931
6144
|
});
|
|
5932
6145
|
conn.replayBufferedOutput((chunk) => {
|
|
5933
6146
|
for (const outputChunk of splitTransportChunks(chunk)) {
|
|
@@ -6019,7 +6232,46 @@ async function runAgent(rawOptions) {
|
|
|
6019
6232
|
const sid = typeof payload.sessionId === "string" ? payload.sessionId.trim() : "";
|
|
6020
6233
|
dispatcher.handleResize(payload.cols, payload.rows, sid);
|
|
6021
6234
|
} else if (payload.type === "control" && typeof payload.action === "string") {
|
|
6022
|
-
|
|
6235
|
+
let controlPayload = payload;
|
|
6236
|
+
if (typeof payload.enc === "string" && typeof payload.iv === "string" && conn.sessionKey) {
|
|
6237
|
+
const inSeq = typeof payload.seq === "number" ? payload.seq : null;
|
|
6238
|
+
const rxSeq = conn.getRxSeqForViewer(payload.viewerId);
|
|
6239
|
+
if (inSeq === null) {
|
|
6240
|
+
log15.warn("rejecting encrypted control frame without seq");
|
|
6241
|
+
return;
|
|
6242
|
+
}
|
|
6243
|
+
if (!rxSeq.accept(inSeq)) {
|
|
6244
|
+
log15.warn("rejecting replayed/out-of-order control", {
|
|
6245
|
+
seq: inSeq,
|
|
6246
|
+
lastSeen: rxSeq.lastSeq,
|
|
6247
|
+
viewerId: typeof payload.viewerId === "string" ? payload.viewerId : "_legacy"
|
|
6248
|
+
});
|
|
6249
|
+
return;
|
|
6250
|
+
}
|
|
6251
|
+
const decrypted = decryptPayload(
|
|
6252
|
+
payload.enc,
|
|
6253
|
+
payload.iv,
|
|
6254
|
+
conn.sessionKey,
|
|
6255
|
+
inSeq
|
|
6256
|
+
);
|
|
6257
|
+
if (decrypted === null) {
|
|
6258
|
+
log15.warn("failed to decrypt control \u2014 ignoring");
|
|
6259
|
+
return;
|
|
6260
|
+
}
|
|
6261
|
+
try {
|
|
6262
|
+
controlPayload = {
|
|
6263
|
+
...JSON.parse(decrypted),
|
|
6264
|
+
_verifiedEncrypted: true
|
|
6265
|
+
};
|
|
6266
|
+
} catch {
|
|
6267
|
+
log15.warn("failed to parse encrypted control payload");
|
|
6268
|
+
return;
|
|
6269
|
+
}
|
|
6270
|
+
} else if (conn.sessionKey && payload.action === "upload-file") {
|
|
6271
|
+
log15.warn("rejecting plaintext attachment upload while encrypted channel is active");
|
|
6272
|
+
return;
|
|
6273
|
+
}
|
|
6274
|
+
await dispatcher.handleControlAction(controlPayload);
|
|
6023
6275
|
} else if (payload.type === "start-agent") {
|
|
6024
6276
|
await dispatcher.handleControlAction({ ...payload, action: "start-agent" });
|
|
6025
6277
|
} else if (payload.type === "provision") {
|
|
@@ -6190,7 +6442,7 @@ var log16 = createLogger("connect");
|
|
|
6190
6442
|
async function connectMachine(opts) {
|
|
6191
6443
|
const portal = opts.portal || DEFAULT_PORTAL;
|
|
6192
6444
|
const hostId = sanitizeHostId(opts.id || inferHostId());
|
|
6193
|
-
const hostName = opts.name ||
|
|
6445
|
+
const hostName = opts.name || os10.hostname();
|
|
6194
6446
|
const command = opts.command || "bash";
|
|
6195
6447
|
if (!opts.token) {
|
|
6196
6448
|
throw new Error("Connection token is required.");
|
|
@@ -6265,13 +6517,13 @@ function registerRunCommand(program2) {
|
|
|
6265
6517
|
}
|
|
6266
6518
|
|
|
6267
6519
|
// src/commands/doctor.ts
|
|
6268
|
-
var
|
|
6520
|
+
var os11 = __toESM(require("os"));
|
|
6269
6521
|
var log18 = createLogger("doctor");
|
|
6270
6522
|
async function runDoctor(opts) {
|
|
6271
6523
|
const options = resolveRunOptions(opts);
|
|
6272
6524
|
const checks = [];
|
|
6273
|
-
const platformOk =
|
|
6274
|
-
checks.push({ name: "platform", ok: platformOk, detail:
|
|
6525
|
+
const platformOk = os11.platform() === "linux";
|
|
6526
|
+
checks.push({ name: "platform", ok: platformOk, detail: os11.platform() });
|
|
6275
6527
|
checks.push({
|
|
6276
6528
|
name: "node",
|
|
6277
6529
|
ok: Number(process.versions.node.split(".")[0]) >= 20,
|
|
@@ -6456,7 +6708,7 @@ function registerServiceCommand(program2) {
|
|
|
6456
6708
|
}
|
|
6457
6709
|
|
|
6458
6710
|
// src/commands/uninstall.ts
|
|
6459
|
-
var
|
|
6711
|
+
var fs7 = __toESM(require("fs"));
|
|
6460
6712
|
var log21 = createLogger("uninstall");
|
|
6461
6713
|
async function uninstallAgent(opts) {
|
|
6462
6714
|
const config = loadConfig();
|
|
@@ -6488,8 +6740,8 @@ async function uninstallAgent(opts) {
|
|
|
6488
6740
|
}
|
|
6489
6741
|
log21.info("stopping and removing service");
|
|
6490
6742
|
await uninstallService().catch(() => void 0);
|
|
6491
|
-
if (
|
|
6492
|
-
|
|
6743
|
+
if (fs7.existsSync(CONFIG_DIR)) {
|
|
6744
|
+
fs7.rmSync(CONFIG_DIR, { recursive: true, force: true });
|
|
6493
6745
|
log21.info("removed config directory", { path: CONFIG_DIR });
|
|
6494
6746
|
}
|
|
6495
6747
|
try {
|
|
@@ -6592,7 +6844,7 @@ function showStatus() {
|
|
|
6592
6844
|
ragent v${CURRENT_VERSION}
|
|
6593
6845
|
`);
|
|
6594
6846
|
try {
|
|
6595
|
-
const raw =
|
|
6847
|
+
const raw = fs8.readFileSync(CONFIG_FILE, "utf8");
|
|
6596
6848
|
const config = JSON.parse(raw);
|
|
6597
6849
|
if (config.portal && config.agentToken) {
|
|
6598
6850
|
console.log(` Status: Connected`);
|
package/dist/sbom.json
CHANGED
|
@@ -1166,8 +1166,8 @@
|
|
|
1166
1166
|
{
|
|
1167
1167
|
"type": "library",
|
|
1168
1168
|
"name": "ragent-cli",
|
|
1169
|
-
"version": "1.11.
|
|
1170
|
-
"bom-ref": "ragent-live|ragent-cli@1.11.
|
|
1169
|
+
"version": "1.11.6",
|
|
1170
|
+
"bom-ref": "ragent-live|ragent-cli@1.11.6",
|
|
1171
1171
|
"author": "Intellimetrics",
|
|
1172
1172
|
"description": "CLI agent for rAgent Live — browser-first terminal control plane for AI coding agents",
|
|
1173
1173
|
"licenses": [
|
|
@@ -1178,7 +1178,7 @@
|
|
|
1178
1178
|
}
|
|
1179
1179
|
}
|
|
1180
1180
|
],
|
|
1181
|
-
"purl": "pkg:npm/ragent-cli@1.11.
|
|
1181
|
+
"purl": "pkg:npm/ragent-cli@1.11.6",
|
|
1182
1182
|
"externalReferences": [
|
|
1183
1183
|
{
|
|
1184
1184
|
"url": "https://github.com/chadlindell/ragent-live/issues",
|
|
@@ -1303,7 +1303,7 @@
|
|
|
1303
1303
|
"ragent-live|@emnapi/wasi-threads@1.2.1",
|
|
1304
1304
|
"ragent-live|@pkgjs/parseargs@0.11.0",
|
|
1305
1305
|
"ragent-live|@tybys/wasm-util@0.10.1",
|
|
1306
|
-
"ragent-live|ragent-cli@1.11.
|
|
1306
|
+
"ragent-live|ragent-cli@1.11.6"
|
|
1307
1307
|
]
|
|
1308
1308
|
},
|
|
1309
1309
|
{
|
|
@@ -1436,7 +1436,7 @@
|
|
|
1436
1436
|
]
|
|
1437
1437
|
},
|
|
1438
1438
|
{
|
|
1439
|
-
"ref": "ragent-live|ragent-cli@1.11.
|
|
1439
|
+
"ref": "ragent-live|ragent-cli@1.11.6",
|
|
1440
1440
|
"dependsOn": [
|
|
1441
1441
|
"ragent-live|@azure/web-pubsub-client@1.0.4",
|
|
1442
1442
|
"ragent-live|commander@14.0.3",
|