ragent-cli 1.11.2 → 1.11.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/index.js +480 -45
- 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.4",
|
|
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: {
|
|
@@ -708,9 +708,146 @@ var CodexCliParserV2 = class {
|
|
|
708
708
|
return [];
|
|
709
709
|
}
|
|
710
710
|
};
|
|
711
|
+
function isGeminiHistoryItem(value) {
|
|
712
|
+
if (!value || typeof value !== "object") return false;
|
|
713
|
+
const item = value;
|
|
714
|
+
return Boolean(item.role && (item.parts || item.content || item.message));
|
|
715
|
+
}
|
|
716
|
+
function findGeminiHistory(value, depth = 0) {
|
|
717
|
+
if (depth > 6 || !value || typeof value !== "object") return null;
|
|
718
|
+
if (Array.isArray(value)) {
|
|
719
|
+
if (value.some(isGeminiHistoryItem)) {
|
|
720
|
+
return value.filter(isGeminiHistoryItem);
|
|
721
|
+
}
|
|
722
|
+
for (const entry of value) {
|
|
723
|
+
const found = findGeminiHistory(entry, depth + 1);
|
|
724
|
+
if (found) return found;
|
|
725
|
+
}
|
|
726
|
+
return null;
|
|
727
|
+
}
|
|
728
|
+
const obj = value;
|
|
729
|
+
for (const key of ["history", "messages", "turns", "contents", "conversation"]) {
|
|
730
|
+
const found = findGeminiHistory(obj[key], depth + 1);
|
|
731
|
+
if (found) return found;
|
|
732
|
+
}
|
|
733
|
+
for (const entry of Object.values(obj)) {
|
|
734
|
+
const found = findGeminiHistory(entry, depth + 1);
|
|
735
|
+
if (found) return found;
|
|
736
|
+
}
|
|
737
|
+
return null;
|
|
738
|
+
}
|
|
739
|
+
function geminiPartsFromItem(item) {
|
|
740
|
+
if (Array.isArray(item.parts)) return item.parts;
|
|
741
|
+
const source = item.content ?? item.message;
|
|
742
|
+
if (typeof source === "string") return [{ text: source }];
|
|
743
|
+
if (source && typeof source === "object" && Array.isArray(source.parts)) {
|
|
744
|
+
return source.parts;
|
|
745
|
+
}
|
|
746
|
+
return [];
|
|
747
|
+
}
|
|
748
|
+
function geminiRole(role) {
|
|
749
|
+
const normalized = role?.toLowerCase();
|
|
750
|
+
if (normalized === "user") return "user";
|
|
751
|
+
if (normalized === "model" || normalized === "assistant") return "assistant";
|
|
752
|
+
return null;
|
|
753
|
+
}
|
|
754
|
+
var GeminiCliParserV2 = class {
|
|
755
|
+
name = "gemini-cli-v2";
|
|
756
|
+
parseLine(line) {
|
|
757
|
+
return this.parseDocument(line);
|
|
758
|
+
}
|
|
759
|
+
parseDocument(document) {
|
|
760
|
+
let root;
|
|
761
|
+
try {
|
|
762
|
+
root = JSON.parse(document);
|
|
763
|
+
} catch (error) {
|
|
764
|
+
debugTranscriptParse("gemini", document, error);
|
|
765
|
+
return [];
|
|
766
|
+
}
|
|
767
|
+
const history = findGeminiHistory(root);
|
|
768
|
+
if (!history) return [];
|
|
769
|
+
const ops = [];
|
|
770
|
+
history.forEach((item, index) => {
|
|
771
|
+
const role = geminiRole(item.role);
|
|
772
|
+
if (!role) return;
|
|
773
|
+
const parts = geminiPartsFromItem(item);
|
|
774
|
+
const timestamp = item.timestamp ?? item.createdAt ?? item.createTime ?? now();
|
|
775
|
+
const turnId = item.id ?? `gemini-${index}`;
|
|
776
|
+
const blocks = [];
|
|
777
|
+
for (const part of parts) {
|
|
778
|
+
const text = typeof part.text === "string" ? part.text.trim() : "";
|
|
779
|
+
if (text) {
|
|
780
|
+
blocks.push({
|
|
781
|
+
op: "insert_block",
|
|
782
|
+
turnId,
|
|
783
|
+
block: makeTextBlock(genBlockId("txt"), text, true)
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
if (part.functionCall?.name) {
|
|
787
|
+
const callId = genBlockId("call");
|
|
788
|
+
blocks.push({
|
|
789
|
+
op: "insert_block",
|
|
790
|
+
turnId,
|
|
791
|
+
block: makeToolBlock(
|
|
792
|
+
genBlockId("tool"),
|
|
793
|
+
callId,
|
|
794
|
+
part.functionCall.name,
|
|
795
|
+
"running",
|
|
796
|
+
part.functionCall.args
|
|
797
|
+
)
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
if (part.functionResponse?.name) {
|
|
801
|
+
const output = part.functionResponse.response;
|
|
802
|
+
const blockId = genBlockId("tool");
|
|
803
|
+
blocks.push({
|
|
804
|
+
op: "insert_block",
|
|
805
|
+
turnId,
|
|
806
|
+
block: makeToolBlock(
|
|
807
|
+
blockId,
|
|
808
|
+
genBlockId("call"),
|
|
809
|
+
part.functionResponse.name,
|
|
810
|
+
"completed",
|
|
811
|
+
void 0,
|
|
812
|
+
true
|
|
813
|
+
)
|
|
814
|
+
});
|
|
815
|
+
if (output !== void 0) {
|
|
816
|
+
blocks.push({
|
|
817
|
+
op: "append_tool_output",
|
|
818
|
+
turnId,
|
|
819
|
+
blockId,
|
|
820
|
+
text: typeof output === "string" ? output : JSON.stringify(output, null, 2)
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
if (blocks.length === 0) return;
|
|
826
|
+
ops.push({
|
|
827
|
+
op: "upsert_turn",
|
|
828
|
+
turn: {
|
|
829
|
+
turnId,
|
|
830
|
+
role,
|
|
831
|
+
startedAt: timestamp,
|
|
832
|
+
updatedAt: timestamp,
|
|
833
|
+
isComplete: false
|
|
834
|
+
}
|
|
835
|
+
});
|
|
836
|
+
ops.push(...blocks);
|
|
837
|
+
ops.push({
|
|
838
|
+
op: "complete_turn",
|
|
839
|
+
turnId,
|
|
840
|
+
endedAt: timestamp,
|
|
841
|
+
updatedAt: timestamp
|
|
842
|
+
});
|
|
843
|
+
});
|
|
844
|
+
return ops;
|
|
845
|
+
}
|
|
846
|
+
};
|
|
711
847
|
var V2_FACTORIES = {
|
|
712
848
|
"Claude Code": () => new ClaudeCodeParserV2(),
|
|
713
|
-
"Codex CLI": () => new CodexCliParserV2()
|
|
849
|
+
"Codex CLI": () => new CodexCliParserV2(),
|
|
850
|
+
"Gemini CLI": () => new GeminiCliParserV2()
|
|
714
851
|
};
|
|
715
852
|
function getParserV2(agentType) {
|
|
716
853
|
if (!agentType) return void 0;
|
|
@@ -1046,18 +1183,110 @@ var CodexCliParser = class {
|
|
|
1046
1183
|
return null;
|
|
1047
1184
|
}
|
|
1048
1185
|
};
|
|
1186
|
+
function isGeminiHistoryItem2(value) {
|
|
1187
|
+
if (!value || typeof value !== "object") return false;
|
|
1188
|
+
const item = value;
|
|
1189
|
+
return Boolean(item.role && (item.parts || item.content || item.message));
|
|
1190
|
+
}
|
|
1191
|
+
function findGeminiHistory2(value, depth = 0) {
|
|
1192
|
+
if (depth > 6 || !value || typeof value !== "object") return null;
|
|
1193
|
+
if (Array.isArray(value)) {
|
|
1194
|
+
if (value.some(isGeminiHistoryItem2)) return value.filter(isGeminiHistoryItem2);
|
|
1195
|
+
for (const entry of value) {
|
|
1196
|
+
const found = findGeminiHistory2(entry, depth + 1);
|
|
1197
|
+
if (found) return found;
|
|
1198
|
+
}
|
|
1199
|
+
return null;
|
|
1200
|
+
}
|
|
1201
|
+
const obj = value;
|
|
1202
|
+
for (const key of ["history", "messages", "turns", "contents", "conversation"]) {
|
|
1203
|
+
const found = findGeminiHistory2(obj[key], depth + 1);
|
|
1204
|
+
if (found) return found;
|
|
1205
|
+
}
|
|
1206
|
+
for (const entry of Object.values(obj)) {
|
|
1207
|
+
const found = findGeminiHistory2(entry, depth + 1);
|
|
1208
|
+
if (found) return found;
|
|
1209
|
+
}
|
|
1210
|
+
return null;
|
|
1211
|
+
}
|
|
1212
|
+
function geminiPartsFromItem2(item) {
|
|
1213
|
+
if (Array.isArray(item.parts)) return item.parts;
|
|
1214
|
+
const source = item.content ?? item.message;
|
|
1215
|
+
if (typeof source === "string") return [{ text: source }];
|
|
1216
|
+
if (source && typeof source === "object" && Array.isArray(source.parts)) {
|
|
1217
|
+
return source.parts;
|
|
1218
|
+
}
|
|
1219
|
+
return [];
|
|
1220
|
+
}
|
|
1221
|
+
var GeminiCliParser = class {
|
|
1222
|
+
name = "gemini-cli";
|
|
1223
|
+
parseLine(line) {
|
|
1224
|
+
const turns = this.parseDocument(line);
|
|
1225
|
+
return turns[0] ?? null;
|
|
1226
|
+
}
|
|
1227
|
+
parseDocument(document) {
|
|
1228
|
+
let root;
|
|
1229
|
+
try {
|
|
1230
|
+
root = JSON.parse(document);
|
|
1231
|
+
} catch {
|
|
1232
|
+
return [];
|
|
1233
|
+
}
|
|
1234
|
+
const history = findGeminiHistory2(root);
|
|
1235
|
+
if (!history) return [];
|
|
1236
|
+
const turns = [];
|
|
1237
|
+
history.forEach((item, index) => {
|
|
1238
|
+
const normalizedRole = item.role?.toLowerCase();
|
|
1239
|
+
const role = normalizedRole === "model" || normalizedRole === "assistant" ? "assistant" : normalizedRole === "user" ? "user" : null;
|
|
1240
|
+
if (!role) return;
|
|
1241
|
+
const textBlocks = [];
|
|
1242
|
+
const tools = [];
|
|
1243
|
+
for (const part of geminiPartsFromItem2(item)) {
|
|
1244
|
+
if (typeof part.text === "string" && part.text.trim()) {
|
|
1245
|
+
textBlocks.push(part.text.trim());
|
|
1246
|
+
}
|
|
1247
|
+
if (part.functionCall?.name) {
|
|
1248
|
+
tools.push({
|
|
1249
|
+
name: part.functionCall.name,
|
|
1250
|
+
input: part.functionCall.args,
|
|
1251
|
+
status: "started"
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1254
|
+
if (part.functionResponse?.name) {
|
|
1255
|
+
const response = part.functionResponse.response;
|
|
1256
|
+
tools.push({
|
|
1257
|
+
name: part.functionResponse.name,
|
|
1258
|
+
status: "completed",
|
|
1259
|
+
result: response === void 0 ? void 0 : typeof response === "string" ? response : JSON.stringify(response, null, 2)
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
const content = textBlocks.join("\n").trim();
|
|
1264
|
+
if (!content && tools.length === 0) return;
|
|
1265
|
+
turns.push({
|
|
1266
|
+
turnId: item.id ?? `gemini-${index}`,
|
|
1267
|
+
role,
|
|
1268
|
+
content,
|
|
1269
|
+
tools: tools.length > 0 ? tools : void 0,
|
|
1270
|
+
timestamp: item.timestamp ?? item.createdAt ?? item.createTime ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
1271
|
+
});
|
|
1272
|
+
});
|
|
1273
|
+
return turns;
|
|
1274
|
+
}
|
|
1275
|
+
};
|
|
1049
1276
|
function parseProcessPid(sessionId) {
|
|
1050
1277
|
if (!sessionId.startsWith("process:")) return null;
|
|
1051
1278
|
const pid = parseInt(sessionId.slice("process:".length).split(":")[0] ?? "", 10);
|
|
1052
1279
|
return Number.isFinite(pid) && pid > 0 ? pid : null;
|
|
1053
1280
|
}
|
|
1054
1281
|
function pathMatchesAgentTranscript(target, agentType) {
|
|
1055
|
-
if (!target.endsWith(".jsonl")) return false;
|
|
1282
|
+
if (!target.endsWith(".jsonl") && !target.endsWith(".json")) return false;
|
|
1056
1283
|
const isClaudeTranscript = target.includes("/.claude/");
|
|
1057
1284
|
const isCodexTranscript = target.includes("/.codex/sessions/");
|
|
1285
|
+
const isGeminiTranscript = target.includes("/.gemini/tmp/") && target.includes("/chats/session-") && target.endsWith(".json");
|
|
1058
1286
|
if (agentType === "Claude Code") return isClaudeTranscript;
|
|
1059
1287
|
if (agentType === "Codex CLI") return isCodexTranscript;
|
|
1060
|
-
|
|
1288
|
+
if (agentType === "Gemini CLI") return isGeminiTranscript;
|
|
1289
|
+
return isClaudeTranscript || isCodexTranscript || isGeminiTranscript;
|
|
1061
1290
|
}
|
|
1062
1291
|
function getNewestPath(paths) {
|
|
1063
1292
|
const ranked = paths.map((target) => {
|
|
@@ -1144,11 +1373,40 @@ function resolveUserHome(pid) {
|
|
|
1144
1373
|
} catch {
|
|
1145
1374
|
}
|
|
1146
1375
|
try {
|
|
1147
|
-
return os3.userInfo().homedir || null;
|
|
1376
|
+
return process.env.HOME || os3.userInfo().homedir || null;
|
|
1148
1377
|
} catch {
|
|
1149
1378
|
return null;
|
|
1150
1379
|
}
|
|
1151
1380
|
}
|
|
1381
|
+
function discoverGeminiTranscriptFromHome(pid) {
|
|
1382
|
+
const userHome = resolveUserHome(pid);
|
|
1383
|
+
if (!userHome) return null;
|
|
1384
|
+
const tmpDir = path2.join(userHome, ".gemini", "tmp");
|
|
1385
|
+
if (!fs2.existsSync(tmpDir)) return null;
|
|
1386
|
+
const matches = [];
|
|
1387
|
+
const stack = [tmpDir];
|
|
1388
|
+
let visited = 0;
|
|
1389
|
+
while (stack.length > 0 && visited < 2e3) {
|
|
1390
|
+
const current = stack.pop();
|
|
1391
|
+
if (!current) continue;
|
|
1392
|
+
visited++;
|
|
1393
|
+
let entries;
|
|
1394
|
+
try {
|
|
1395
|
+
entries = fs2.readdirSync(current, { withFileTypes: true });
|
|
1396
|
+
} catch {
|
|
1397
|
+
continue;
|
|
1398
|
+
}
|
|
1399
|
+
for (const entry of entries) {
|
|
1400
|
+
const fullPath = path2.join(current, entry.name);
|
|
1401
|
+
if (entry.isDirectory()) {
|
|
1402
|
+
stack.push(fullPath);
|
|
1403
|
+
} else if (entry.isFile() && entry.name.startsWith("session-") && entry.name.endsWith(".json") && fullPath.includes(`${path2.sep}chats${path2.sep}`)) {
|
|
1404
|
+
matches.push(fullPath);
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
return getNewestPath(matches);
|
|
1409
|
+
}
|
|
1152
1410
|
function discoverViaCwd(paneCwd, pid) {
|
|
1153
1411
|
const userHome = resolveUserHome(pid);
|
|
1154
1412
|
if (!userHome) return null;
|
|
@@ -1183,6 +1441,10 @@ function discoverTranscriptFile(sessionId, agentType) {
|
|
|
1183
1441
|
const cwdResult = discoverViaProcessCwd(processPid);
|
|
1184
1442
|
if (cwdResult) return cwdResult;
|
|
1185
1443
|
}
|
|
1444
|
+
if (agentType === "Gemini CLI") {
|
|
1445
|
+
const geminiResult = discoverGeminiTranscriptFromHome(processPid);
|
|
1446
|
+
if (geminiResult) return geminiResult;
|
|
1447
|
+
}
|
|
1186
1448
|
return null;
|
|
1187
1449
|
}
|
|
1188
1450
|
if (sessionId.startsWith("tmux:")) {
|
|
@@ -1217,6 +1479,9 @@ function discoverTranscriptFile(sessionId, agentType) {
|
|
|
1217
1479
|
} catch {
|
|
1218
1480
|
}
|
|
1219
1481
|
}
|
|
1482
|
+
if (agentType === "Gemini CLI") {
|
|
1483
|
+
return discoverGeminiTranscriptFromHome(panePid ?? void 0);
|
|
1484
|
+
}
|
|
1220
1485
|
return null;
|
|
1221
1486
|
}
|
|
1222
1487
|
return null;
|
|
@@ -1241,6 +1506,7 @@ var TranscriptWatcher = class {
|
|
|
1241
1506
|
pollTimer = null;
|
|
1242
1507
|
stopped = false;
|
|
1243
1508
|
subscriberCount = 0;
|
|
1509
|
+
documentFingerprint = "";
|
|
1244
1510
|
constructor(filePath, parser, callbacks, parserV2, sessionId) {
|
|
1245
1511
|
this.filePath = filePath;
|
|
1246
1512
|
this.parser = parser;
|
|
@@ -1310,6 +1576,10 @@ var TranscriptWatcher = class {
|
|
|
1310
1576
|
}
|
|
1311
1577
|
/** Read and replay the full transcript from the start of the file. */
|
|
1312
1578
|
replayFromStart() {
|
|
1579
|
+
if (this.parser.parseDocument) {
|
|
1580
|
+
this.readDocument(true);
|
|
1581
|
+
return;
|
|
1582
|
+
}
|
|
1313
1583
|
const savedOffset = this.offset;
|
|
1314
1584
|
this.offset = 0;
|
|
1315
1585
|
this.partialLine = "";
|
|
@@ -1351,6 +1621,10 @@ var TranscriptWatcher = class {
|
|
|
1351
1621
|
}
|
|
1352
1622
|
}
|
|
1353
1623
|
readNewData() {
|
|
1624
|
+
if (this.parser.parseDocument) {
|
|
1625
|
+
this.readDocument(false);
|
|
1626
|
+
return;
|
|
1627
|
+
}
|
|
1354
1628
|
let fd;
|
|
1355
1629
|
try {
|
|
1356
1630
|
fd = fs2.openSync(this.filePath, "r");
|
|
@@ -1371,6 +1645,38 @@ var TranscriptWatcher = class {
|
|
|
1371
1645
|
fs2.closeSync(fd);
|
|
1372
1646
|
}
|
|
1373
1647
|
}
|
|
1648
|
+
readDocument(force) {
|
|
1649
|
+
let content;
|
|
1650
|
+
let stat;
|
|
1651
|
+
try {
|
|
1652
|
+
stat = fs2.statSync(this.filePath);
|
|
1653
|
+
content = fs2.readFileSync(this.filePath, "utf-8");
|
|
1654
|
+
} catch {
|
|
1655
|
+
return;
|
|
1656
|
+
}
|
|
1657
|
+
const fingerprint = `${stat.mtimeMs}:${stat.size}`;
|
|
1658
|
+
if (!force && fingerprint === this.documentFingerprint) return;
|
|
1659
|
+
this.documentFingerprint = fingerprint;
|
|
1660
|
+
const parsedTurns = this.parser.parseDocument?.(content) ?? [];
|
|
1661
|
+
this.seq++;
|
|
1662
|
+
this.turns = parsedTurns.slice(-MAX_REPLAY_TURNS).map((turn) => ({
|
|
1663
|
+
turnId: turn.turnId,
|
|
1664
|
+
role: turn.role,
|
|
1665
|
+
content: turn.content,
|
|
1666
|
+
tools: turn.tools,
|
|
1667
|
+
timestamp: turn.timestamp
|
|
1668
|
+
}));
|
|
1669
|
+
this.callbacks.onTurnsSnapshot?.(this.turns, this.seq);
|
|
1670
|
+
if (this.parserV2?.parseDocument && this.callbacks.onSnapshot) {
|
|
1671
|
+
const ops = this.parserV2.parseDocument(content);
|
|
1672
|
+
let nextSnapshot = createEmptySnapshot(this.snapshot.sessionId);
|
|
1673
|
+
if (ops.length > 0) {
|
|
1674
|
+
nextSnapshot = applyOps(nextSnapshot, ops);
|
|
1675
|
+
}
|
|
1676
|
+
this.snapshot = trimSnapshot(nextSnapshot);
|
|
1677
|
+
this.callbacks.onSnapshot(this.snapshot, this.seq);
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1374
1680
|
processChunk(chunk) {
|
|
1375
1681
|
const combined = this.partialLine + chunk;
|
|
1376
1682
|
const lines = combined.split("\n");
|
|
@@ -1413,7 +1719,8 @@ var TranscriptWatcher = class {
|
|
|
1413
1719
|
};
|
|
1414
1720
|
var PARSER_FACTORIES = {
|
|
1415
1721
|
"Claude Code": () => new ClaudeCodeParser(),
|
|
1416
|
-
"Codex CLI": () => new CodexCliParser()
|
|
1722
|
+
"Codex CLI": () => new CodexCliParser(),
|
|
1723
|
+
"Gemini CLI": () => new GeminiCliParser()
|
|
1417
1724
|
};
|
|
1418
1725
|
function getParser(agentType) {
|
|
1419
1726
|
if (!agentType) return void 0;
|
|
@@ -1435,10 +1742,25 @@ var TranscriptWatcherManager = class {
|
|
|
1435
1742
|
}
|
|
1436
1743
|
/** Enable markdown streaming for a session. */
|
|
1437
1744
|
enableMarkdown(sessionId, agentType) {
|
|
1745
|
+
const requestedAgentType = agentType ?? "";
|
|
1438
1746
|
const existing = this.active.get(sessionId);
|
|
1439
1747
|
if (existing) {
|
|
1440
|
-
|
|
1441
|
-
|
|
1748
|
+
const newPath = discoverTranscriptFile(sessionId, agentType);
|
|
1749
|
+
const agentChanged = existing.agentType !== requestedAgentType;
|
|
1750
|
+
const pathChanged = Boolean(newPath && newPath !== existing.filePath);
|
|
1751
|
+
const existingPathGone = !fs2.existsSync(existing.filePath);
|
|
1752
|
+
if (!agentChanged && !pathChanged && !existingPathGone) {
|
|
1753
|
+
existing.watcher.addSubscriber();
|
|
1754
|
+
return { ok: true };
|
|
1755
|
+
}
|
|
1756
|
+
existing.watcher.stop();
|
|
1757
|
+
this.active.delete(sessionId);
|
|
1758
|
+
this.stopRediscovery(sessionId);
|
|
1759
|
+
this.clearTranscriptState(sessionId);
|
|
1760
|
+
if (!newPath) {
|
|
1761
|
+
log5.info("no replacement transcript file found", { sessionId, agentType });
|
|
1762
|
+
return { ok: false, reason: "no_transcript" };
|
|
1763
|
+
}
|
|
1442
1764
|
}
|
|
1443
1765
|
const parser = getParser(agentType);
|
|
1444
1766
|
if (!parser) {
|
|
@@ -1462,6 +1784,12 @@ var TranscriptWatcherManager = class {
|
|
|
1462
1784
|
onOps: this.sendOpsFn ? (ops, seq) => {
|
|
1463
1785
|
this.sendOpsFn(sessionId, ops, seq);
|
|
1464
1786
|
} : void 0,
|
|
1787
|
+
onTurnsSnapshot: (turns, seq) => {
|
|
1788
|
+
this.sendSnapshotFn(sessionId, turns, seq);
|
|
1789
|
+
},
|
|
1790
|
+
onSnapshot: this.sendV2SnapshotFn ? (snapshot, seq) => {
|
|
1791
|
+
this.sendV2SnapshotFn(sessionId, snapshot, seq);
|
|
1792
|
+
} : void 0,
|
|
1465
1793
|
onError: (error) => {
|
|
1466
1794
|
log5.warn("transcript watcher error", { sessionId, error: String(error) });
|
|
1467
1795
|
}
|
|
@@ -1478,6 +1806,12 @@ var TranscriptWatcherManager = class {
|
|
|
1478
1806
|
this.startRediscovery(sessionId, agentType);
|
|
1479
1807
|
return { ok: true };
|
|
1480
1808
|
}
|
|
1809
|
+
clearTranscriptState(sessionId) {
|
|
1810
|
+
this.sendSnapshotFn(sessionId, [], 0);
|
|
1811
|
+
if (this.sendV2SnapshotFn) {
|
|
1812
|
+
this.sendV2SnapshotFn(sessionId, createEmptySnapshot(sessionId), 0);
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1481
1815
|
/** Disable markdown streaming for a session. */
|
|
1482
1816
|
disableMarkdown(sessionId) {
|
|
1483
1817
|
const session = this.active.get(sessionId);
|
|
@@ -1520,6 +1854,12 @@ var TranscriptWatcherManager = class {
|
|
|
1520
1854
|
onOps: this.sendOpsFn ? (ops, seq) => {
|
|
1521
1855
|
this.sendOpsFn(sessionId, ops, seq);
|
|
1522
1856
|
} : void 0,
|
|
1857
|
+
onTurnsSnapshot: (turns, seq) => {
|
|
1858
|
+
this.sendSnapshotFn(sessionId, turns, seq);
|
|
1859
|
+
},
|
|
1860
|
+
onSnapshot: this.sendV2SnapshotFn ? (snapshot, seq) => {
|
|
1861
|
+
this.sendV2SnapshotFn(sessionId, snapshot, seq);
|
|
1862
|
+
} : void 0,
|
|
1523
1863
|
onError: (error) => {
|
|
1524
1864
|
log5.warn("transcript watcher error", { sessionId, error: String(error) });
|
|
1525
1865
|
}
|
|
@@ -1577,7 +1917,7 @@ var TranscriptWatcherManager = class {
|
|
|
1577
1917
|
|
|
1578
1918
|
// src/sessions.ts
|
|
1579
1919
|
var isMac = os4.platform() === "darwin";
|
|
1580
|
-
var MARKDOWN_AGENT_TYPES = /* @__PURE__ */ new Set(["Claude Code", "Codex CLI"]);
|
|
1920
|
+
var MARKDOWN_AGENT_TYPES = /* @__PURE__ */ new Set(["Claude Code", "Codex CLI", "Gemini CLI"]);
|
|
1581
1921
|
function getProcessStartTime(pid) {
|
|
1582
1922
|
if (isMac) return null;
|
|
1583
1923
|
try {
|
|
@@ -1629,6 +1969,7 @@ function classifyAgent(command, options) {
|
|
|
1629
1969
|
".claude": "Claude Code",
|
|
1630
1970
|
"claude-code": "Claude Code",
|
|
1631
1971
|
".codex": "Codex CLI",
|
|
1972
|
+
".gemini": "Gemini CLI",
|
|
1632
1973
|
".aider": "aider"
|
|
1633
1974
|
};
|
|
1634
1975
|
const agentType = cwdAgentMap[dirName];
|
|
@@ -2800,26 +3141,23 @@ var SessionStreamer = class {
|
|
|
2800
3141
|
alternateScreen = altFlag === "1";
|
|
2801
3142
|
} catch {
|
|
2802
3143
|
}
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
);
|
|
2812
|
-
if (scrollback && scrollback.length > 0) {
|
|
2813
|
-
this.sendFn(sessionId, scrollback.replace(/\n/g, "\r\n"));
|
|
2814
|
-
}
|
|
2815
|
-
} catch {
|
|
3144
|
+
try {
|
|
3145
|
+
const scrollback = (0, import_node_child_process2.execFileSync)(
|
|
3146
|
+
"tmux",
|
|
3147
|
+
["capture-pane", "-t", paneTarget, "-p", "-e", "-S", "-5000", "-E", "-1"],
|
|
3148
|
+
{ env: cleanEnv, timeout: 1e4, encoding: "utf-8" }
|
|
3149
|
+
);
|
|
3150
|
+
if (scrollback && scrollback.length > 0) {
|
|
3151
|
+
this.sendFn(sessionId, scrollback.replace(/\n/g, "\r\n"));
|
|
2816
3152
|
}
|
|
2817
|
-
|
|
3153
|
+
} catch {
|
|
2818
3154
|
}
|
|
3155
|
+
this.sendFn(sessionId, "\x1B[?1049l\x1B[0m\x1B[2J\x1B[H");
|
|
2819
3156
|
try {
|
|
3157
|
+
const captureArgs = alternateScreen ? ["capture-pane", "-a", "-t", paneTarget, "-p", "-e"] : ["capture-pane", "-t", paneTarget, "-p", "-e"];
|
|
2820
3158
|
const initial = (0, import_node_child_process2.execFileSync)(
|
|
2821
3159
|
"tmux",
|
|
2822
|
-
|
|
3160
|
+
captureArgs,
|
|
2823
3161
|
{ env: cleanEnv, timeout: 5e3, encoding: "utf-8" }
|
|
2824
3162
|
);
|
|
2825
3163
|
if (initial) {
|
|
@@ -2941,26 +3279,23 @@ var SessionStreamer = class {
|
|
|
2941
3279
|
} catch {
|
|
2942
3280
|
}
|
|
2943
3281
|
stream.alternateScreen = alternateScreen;
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
);
|
|
2953
|
-
if (scrollback && scrollback.length > 0) {
|
|
2954
|
-
this.sendFn(sessionId, scrollback.replace(/\n/g, "\r\n"));
|
|
2955
|
-
}
|
|
2956
|
-
} catch {
|
|
3282
|
+
try {
|
|
3283
|
+
const scrollback = (0, import_node_child_process2.execFileSync)(
|
|
3284
|
+
"tmux",
|
|
3285
|
+
["capture-pane", "-t", paneTarget, "-p", "-e", "-S", "-5000", "-E", "-1"],
|
|
3286
|
+
{ env: cleanEnv, timeout: 1e4, encoding: "utf-8" }
|
|
3287
|
+
);
|
|
3288
|
+
if (scrollback && scrollback.length > 0) {
|
|
3289
|
+
this.sendFn(sessionId, scrollback.replace(/\n/g, "\r\n"));
|
|
2957
3290
|
}
|
|
2958
|
-
|
|
3291
|
+
} catch {
|
|
2959
3292
|
}
|
|
3293
|
+
this.sendFn(sessionId, "\x1B[?1049l\x1B[0m\x1B[2J\x1B[H");
|
|
2960
3294
|
try {
|
|
3295
|
+
const captureArgs = alternateScreen ? ["capture-pane", "-a", "-t", paneTarget, "-p", "-e"] : ["capture-pane", "-t", paneTarget, "-p", "-e"];
|
|
2961
3296
|
const initial = (0, import_node_child_process2.execFileSync)(
|
|
2962
3297
|
"tmux",
|
|
2963
|
-
|
|
3298
|
+
captureArgs,
|
|
2964
3299
|
{ env: cleanEnv, timeout: 5e3, encoding: "utf-8" }
|
|
2965
3300
|
);
|
|
2966
3301
|
if (initial) {
|
|
@@ -3322,6 +3657,105 @@ var SequenceTracker = class {
|
|
|
3322
3657
|
var import_node_child_process3 = require("child_process");
|
|
3323
3658
|
var pty2 = __toESM(require("node-pty"));
|
|
3324
3659
|
var log9 = createLogger("pty");
|
|
3660
|
+
var ANSI_KEY_SEQUENCES = /* @__PURE__ */ new Map([
|
|
3661
|
+
["\x1B[A", "Up"],
|
|
3662
|
+
["\x1B[B", "Down"],
|
|
3663
|
+
["\x1B[C", "Right"],
|
|
3664
|
+
["\x1B[D", "Left"],
|
|
3665
|
+
["\x1B[H", "Home"],
|
|
3666
|
+
["\x1B[F", "End"],
|
|
3667
|
+
["\x1BOH", "Home"],
|
|
3668
|
+
["\x1BOF", "End"],
|
|
3669
|
+
["\x1B[1~", "Home"],
|
|
3670
|
+
["\x1B[4~", "End"],
|
|
3671
|
+
["\x1B[3~", "Delete"],
|
|
3672
|
+
["\x1B[5~", "PageUp"],
|
|
3673
|
+
["\x1B[6~", "PageDown"]
|
|
3674
|
+
]);
|
|
3675
|
+
function controlKeyName(charCode) {
|
|
3676
|
+
if (charCode >= 1 && charCode <= 26) {
|
|
3677
|
+
return `C-${String.fromCharCode(charCode + 96)}`;
|
|
3678
|
+
}
|
|
3679
|
+
switch (charCode) {
|
|
3680
|
+
case 0:
|
|
3681
|
+
return "C-Space";
|
|
3682
|
+
case 27:
|
|
3683
|
+
return "Escape";
|
|
3684
|
+
case 28:
|
|
3685
|
+
return "C-\\";
|
|
3686
|
+
case 29:
|
|
3687
|
+
return "C-]";
|
|
3688
|
+
case 30:
|
|
3689
|
+
return "C-^";
|
|
3690
|
+
case 31:
|
|
3691
|
+
return "C-_";
|
|
3692
|
+
default:
|
|
3693
|
+
return null;
|
|
3694
|
+
}
|
|
3695
|
+
}
|
|
3696
|
+
function appendLiteralSegment(segments, value) {
|
|
3697
|
+
if (!value) return;
|
|
3698
|
+
const previous = segments[segments.length - 1];
|
|
3699
|
+
if (previous?.kind === "literal") {
|
|
3700
|
+
previous.value += value;
|
|
3701
|
+
} else {
|
|
3702
|
+
segments.push({ kind: "literal", value });
|
|
3703
|
+
}
|
|
3704
|
+
}
|
|
3705
|
+
function encodeTmuxInput(data) {
|
|
3706
|
+
const segments = [];
|
|
3707
|
+
let literal = "";
|
|
3708
|
+
const flushLiteral = () => {
|
|
3709
|
+
appendLiteralSegment(segments, literal);
|
|
3710
|
+
literal = "";
|
|
3711
|
+
};
|
|
3712
|
+
for (let i = 0; i < data.length; i++) {
|
|
3713
|
+
const remaining = data.slice(i);
|
|
3714
|
+
const matchedSequence = Array.from(ANSI_KEY_SEQUENCES.entries()).find(([sequence]) => remaining.startsWith(sequence));
|
|
3715
|
+
if (matchedSequence) {
|
|
3716
|
+
flushLiteral();
|
|
3717
|
+
const [sequence, key] = matchedSequence;
|
|
3718
|
+
segments.push({ kind: "key", value: key });
|
|
3719
|
+
i += sequence.length - 1;
|
|
3720
|
+
continue;
|
|
3721
|
+
}
|
|
3722
|
+
const charCode = data.charCodeAt(i);
|
|
3723
|
+
if (data[i] === "\r" || data[i] === "\n") {
|
|
3724
|
+
flushLiteral();
|
|
3725
|
+
segments.push({ kind: "key", value: "Enter" });
|
|
3726
|
+
continue;
|
|
3727
|
+
}
|
|
3728
|
+
if (data[i] === " ") {
|
|
3729
|
+
flushLiteral();
|
|
3730
|
+
segments.push({ kind: "key", value: "Tab" });
|
|
3731
|
+
continue;
|
|
3732
|
+
}
|
|
3733
|
+
if (data[i] === "\x7F" || data[i] === "\b") {
|
|
3734
|
+
flushLiteral();
|
|
3735
|
+
segments.push({ kind: "key", value: "BSpace" });
|
|
3736
|
+
continue;
|
|
3737
|
+
}
|
|
3738
|
+
if (charCode < 32) {
|
|
3739
|
+
const keyName = controlKeyName(charCode);
|
|
3740
|
+
if (keyName) {
|
|
3741
|
+
flushLiteral();
|
|
3742
|
+
segments.push({ kind: "key", value: keyName });
|
|
3743
|
+
continue;
|
|
3744
|
+
}
|
|
3745
|
+
}
|
|
3746
|
+
literal += data[i];
|
|
3747
|
+
}
|
|
3748
|
+
flushLiteral();
|
|
3749
|
+
return segments;
|
|
3750
|
+
}
|
|
3751
|
+
function execTmuxSendKeys(args) {
|
|
3752
|
+
return new Promise((resolve, reject) => {
|
|
3753
|
+
(0, import_node_child_process3.execFile)("tmux", args, { timeout: 5e3 }, (err) => {
|
|
3754
|
+
if (err) reject(err);
|
|
3755
|
+
else resolve();
|
|
3756
|
+
});
|
|
3757
|
+
});
|
|
3758
|
+
}
|
|
3325
3759
|
function isInteractiveShell(command) {
|
|
3326
3760
|
const trimmed = String(command).trim();
|
|
3327
3761
|
return ["bash", "sh", "zsh", "fish"].includes(trimmed);
|
|
@@ -3358,12 +3792,13 @@ async function sendInputToTmux(sessionId, data) {
|
|
|
3358
3792
|
return;
|
|
3359
3793
|
}
|
|
3360
3794
|
try {
|
|
3361
|
-
|
|
3362
|
-
(
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3795
|
+
for (const segment of encodeTmuxInput(data)) {
|
|
3796
|
+
if (segment.kind === "literal") {
|
|
3797
|
+
await execTmuxSendKeys(["send-keys", "-t", target, "-l", "--", segment.value]);
|
|
3798
|
+
} else {
|
|
3799
|
+
await execTmuxSendKeys(["send-keys", "-t", target, "--", segment.value]);
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
3367
3802
|
} catch (error) {
|
|
3368
3803
|
const message = error instanceof Error ? error.message : String(error);
|
|
3369
3804
|
log9.warn("failed to send input", { sessionId, error: message });
|
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.4",
|
|
1170
|
+
"bom-ref": "ragent-live|ragent-cli@1.11.4",
|
|
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.4",
|
|
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.4"
|
|
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.4",
|
|
1440
1440
|
"dependsOn": [
|
|
1441
1441
|
"ragent-live|@azure/web-pubsub-client@1.0.4",
|
|
1442
1442
|
"ragent-live|commander@14.0.3",
|