ragent-cli 1.11.11 → 1.11.12
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 +499 -175
- 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.12",
|
|
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: {
|
|
@@ -1991,20 +1991,43 @@ function setBlock(turns, turnId, blockId, block) {
|
|
|
1991
1991
|
};
|
|
1992
1992
|
}
|
|
1993
1993
|
var MAX_SNAPSHOT_TURNS = 1e3;
|
|
1994
|
+
var MAX_SNAPSHOT_SIZE_CHARS = 45e4;
|
|
1994
1995
|
function trimSnapshot(snapshot, maxTurns = MAX_SNAPSHOT_TURNS) {
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
1996
|
+
let currentSnapshot = snapshot;
|
|
1997
|
+
if (currentSnapshot.turnOrder.length > maxTurns) {
|
|
1998
|
+
const keptOrder = currentSnapshot.turnOrder.slice(-maxTurns);
|
|
1999
|
+
const keptTurns = {};
|
|
2000
|
+
for (const id of keptOrder) {
|
|
2001
|
+
if (currentSnapshot.turns[id]) {
|
|
2002
|
+
keptTurns[id] = currentSnapshot.turns[id];
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
currentSnapshot = {
|
|
2006
|
+
...currentSnapshot,
|
|
2007
|
+
turnOrder: keptOrder,
|
|
2008
|
+
turns: keptTurns
|
|
2009
|
+
};
|
|
2010
|
+
}
|
|
2011
|
+
let serialized = JSON.stringify(currentSnapshot);
|
|
2012
|
+
if (serialized.length <= MAX_SNAPSHOT_SIZE_CHARS) {
|
|
2013
|
+
return currentSnapshot;
|
|
2014
|
+
}
|
|
2015
|
+
while (currentSnapshot.turnOrder.length > 1 && serialized.length > MAX_SNAPSHOT_SIZE_CHARS) {
|
|
2016
|
+
const keptOrder = currentSnapshot.turnOrder.slice(1);
|
|
2017
|
+
const keptTurns = {};
|
|
2018
|
+
for (const id of keptOrder) {
|
|
2019
|
+
if (currentSnapshot.turns[id]) {
|
|
2020
|
+
keptTurns[id] = currentSnapshot.turns[id];
|
|
2021
|
+
}
|
|
2001
2022
|
}
|
|
2023
|
+
currentSnapshot = {
|
|
2024
|
+
...currentSnapshot,
|
|
2025
|
+
turnOrder: keptOrder,
|
|
2026
|
+
turns: keptTurns
|
|
2027
|
+
};
|
|
2028
|
+
serialized = JSON.stringify(currentSnapshot);
|
|
2002
2029
|
}
|
|
2003
|
-
return
|
|
2004
|
-
...snapshot,
|
|
2005
|
-
turnOrder: keptOrder,
|
|
2006
|
-
turns: keptTurns
|
|
2007
|
-
};
|
|
2030
|
+
return currentSnapshot;
|
|
2008
2031
|
}
|
|
2009
2032
|
|
|
2010
2033
|
// src/native-stream-manager.ts
|
|
@@ -2541,6 +2564,8 @@ function releaseSessionSource(sessionId) {
|
|
|
2541
2564
|
|
|
2542
2565
|
// src/transcript-watcher.ts
|
|
2543
2566
|
var log7 = createLogger("transcript-watcher");
|
|
2567
|
+
var cachedTicksPerSecond = null;
|
|
2568
|
+
var transcriptDiscoveryCache = /* @__PURE__ */ new Map();
|
|
2544
2569
|
var ClaudeCodeParser = class {
|
|
2545
2570
|
name = "claude-code";
|
|
2546
2571
|
/** Maps tool_use id → tool name for matching results back to tools. */
|
|
@@ -3054,6 +3079,13 @@ function discoverTranscriptForPid(pid, agentType) {
|
|
|
3054
3079
|
}
|
|
3055
3080
|
}
|
|
3056
3081
|
function listChildPids(pid) {
|
|
3082
|
+
if (os3.platform() !== "darwin") {
|
|
3083
|
+
try {
|
|
3084
|
+
const content = fs2.readFileSync(`/proc/${pid}/task/${pid}/children`, "utf8");
|
|
3085
|
+
return content.trim().split(/\s+/).map((value) => parseInt(value, 10)).filter((value) => Number.isFinite(value) && value > 0);
|
|
3086
|
+
} catch {
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3057
3089
|
try {
|
|
3058
3090
|
return (0, import_child_process2.execFileSync)("pgrep", ["-P", String(pid)], {
|
|
3059
3091
|
encoding: "utf-8",
|
|
@@ -3064,6 +3096,16 @@ function listChildPids(pid) {
|
|
|
3064
3096
|
}
|
|
3065
3097
|
}
|
|
3066
3098
|
function getProcessCommand(pid) {
|
|
3099
|
+
if (os3.platform() !== "darwin") {
|
|
3100
|
+
try {
|
|
3101
|
+
const raw = fs2.readFileSync(`/proc/${pid}/cmdline`, "utf8");
|
|
3102
|
+
const parts = raw.split("\0").filter(Boolean);
|
|
3103
|
+
if (parts.length > 0) {
|
|
3104
|
+
return parts.join(" ");
|
|
3105
|
+
}
|
|
3106
|
+
} catch {
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3067
3109
|
try {
|
|
3068
3110
|
return (0, import_child_process2.execFileSync)("ps", ["-p", String(pid), "-o", "args="], {
|
|
3069
3111
|
encoding: "utf-8",
|
|
@@ -3105,10 +3147,13 @@ function getProcessStartEpochMs(pid) {
|
|
|
3105
3147
|
const bootLine = procStat.split("\n").find((line) => line.startsWith("btime "));
|
|
3106
3148
|
const bootSeconds = Number(bootLine?.split(/\s+/)[1]);
|
|
3107
3149
|
if (!Number.isFinite(bootSeconds) || bootSeconds <= 0) return null;
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3150
|
+
if (cachedTicksPerSecond === null) {
|
|
3151
|
+
cachedTicksPerSecond = Number((0, import_child_process2.execFileSync)("getconf", ["CLK_TCK"], {
|
|
3152
|
+
encoding: "utf-8",
|
|
3153
|
+
timeout: 3e3
|
|
3154
|
+
}).trim());
|
|
3155
|
+
}
|
|
3156
|
+
const ticksPerSecond = cachedTicksPerSecond;
|
|
3112
3157
|
if (!Number.isFinite(ticksPerSecond) || ticksPerSecond <= 0) return null;
|
|
3113
3158
|
return (bootSeconds + startTicks / ticksPerSecond) * 1e3;
|
|
3114
3159
|
} catch {
|
|
@@ -3254,29 +3299,66 @@ function discoverAntigravityTranscriptFromHome(pid, minMtimeMs) {
|
|
|
3254
3299
|
const brainDir = path2.join(userHome, ".gemini", "antigravity-cli", "brain");
|
|
3255
3300
|
if (!fs2.existsSync(brainDir)) return null;
|
|
3256
3301
|
const matches = [];
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
entries = fs2.readdirSync(current, { withFileTypes: true });
|
|
3266
|
-
} catch {
|
|
3267
|
-
continue;
|
|
3268
|
-
}
|
|
3269
|
-
for (const entry of entries) {
|
|
3270
|
-
const fullPath = path2.join(current, entry.name);
|
|
3271
|
-
if (entry.isDirectory()) {
|
|
3272
|
-
stack.push(fullPath);
|
|
3273
|
-
} else if (entry.isFile() && entry.name === "transcript.jsonl") {
|
|
3274
|
-
matches.push(fullPath);
|
|
3302
|
+
try {
|
|
3303
|
+
const folders = fs2.readdirSync(brainDir, { withFileTypes: true });
|
|
3304
|
+
for (const folder of folders) {
|
|
3305
|
+
if (folder.isDirectory()) {
|
|
3306
|
+
const transcriptPath = path2.join(brainDir, folder.name, ".system_generated", "logs", "transcript.jsonl");
|
|
3307
|
+
if (fs2.existsSync(transcriptPath)) {
|
|
3308
|
+
matches.push(transcriptPath);
|
|
3309
|
+
}
|
|
3275
3310
|
}
|
|
3276
3311
|
}
|
|
3312
|
+
} catch {
|
|
3277
3313
|
}
|
|
3278
3314
|
return getNewestPath(matches, { minMtimeMs });
|
|
3279
3315
|
}
|
|
3316
|
+
function discoverAntigravityViaCwd(paneCwd, pid) {
|
|
3317
|
+
const userHome = resolveUserHome(pid);
|
|
3318
|
+
if (!userHome) return null;
|
|
3319
|
+
const historyFile = path2.join(userHome, ".gemini", "antigravity-cli", "history.jsonl");
|
|
3320
|
+
if (!fs2.existsSync(historyFile)) return null;
|
|
3321
|
+
let targetCwd = paneCwd;
|
|
3322
|
+
try {
|
|
3323
|
+
targetCwd = fs2.realpathSync(paneCwd);
|
|
3324
|
+
} catch {
|
|
3325
|
+
}
|
|
3326
|
+
try {
|
|
3327
|
+
const content = fs2.readFileSync(historyFile, "utf-8");
|
|
3328
|
+
const lines = content.split("\n").filter(Boolean).reverse();
|
|
3329
|
+
for (const line of lines) {
|
|
3330
|
+
try {
|
|
3331
|
+
const entry = JSON.parse(line);
|
|
3332
|
+
if (entry.workspace) {
|
|
3333
|
+
let entryCwd = entry.workspace;
|
|
3334
|
+
try {
|
|
3335
|
+
entryCwd = fs2.realpathSync(entry.workspace);
|
|
3336
|
+
} catch {
|
|
3337
|
+
}
|
|
3338
|
+
if (entryCwd === targetCwd && entry.conversationId) {
|
|
3339
|
+
const transcriptPath = path2.join(
|
|
3340
|
+
userHome,
|
|
3341
|
+
".gemini",
|
|
3342
|
+
"antigravity-cli",
|
|
3343
|
+
"brain",
|
|
3344
|
+
entry.conversationId,
|
|
3345
|
+
".system_generated",
|
|
3346
|
+
"logs",
|
|
3347
|
+
"transcript.jsonl"
|
|
3348
|
+
);
|
|
3349
|
+
if (fs2.existsSync(transcriptPath)) {
|
|
3350
|
+
return transcriptPath;
|
|
3351
|
+
}
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
} catch {
|
|
3355
|
+
continue;
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
} catch {
|
|
3359
|
+
}
|
|
3360
|
+
return null;
|
|
3361
|
+
}
|
|
3280
3362
|
function discoverViaCwd(paneCwd, pid, minMtimeMs) {
|
|
3281
3363
|
const userHome = resolveUserHome(pid);
|
|
3282
3364
|
if (!userHome) return null;
|
|
@@ -3296,31 +3378,72 @@ function discoverViaCwd(paneCwd, pid, minMtimeMs) {
|
|
|
3296
3378
|
return null;
|
|
3297
3379
|
}
|
|
3298
3380
|
}
|
|
3299
|
-
function discoverTranscriptFile(sessionId, agentType) {
|
|
3381
|
+
function discoverTranscriptFile(sessionId, agentType, forceRefresh = false) {
|
|
3382
|
+
const now2 = Date.now();
|
|
3383
|
+
const cacheKey = `${sessionId}:${agentType || ""}`;
|
|
3384
|
+
if (!forceRefresh) {
|
|
3385
|
+
const cached = transcriptDiscoveryCache.get(cacheKey);
|
|
3386
|
+
if (cached && cached.expiresAt > now2) {
|
|
3387
|
+
return cached.value;
|
|
3388
|
+
}
|
|
3389
|
+
}
|
|
3390
|
+
const result = discoverTranscriptFileImpl(sessionId, agentType);
|
|
3391
|
+
const isProcessSession = sessionId.startsWith("process:");
|
|
3392
|
+
const ttl = result ? isProcessSession ? 3600 * 1e3 : 5e3 : 5e3;
|
|
3393
|
+
transcriptDiscoveryCache.set(cacheKey, {
|
|
3394
|
+
value: result,
|
|
3395
|
+
expiresAt: Date.now() + ttl
|
|
3396
|
+
});
|
|
3397
|
+
return result;
|
|
3398
|
+
}
|
|
3399
|
+
function discoverTranscriptFileImpl(sessionId, agentType) {
|
|
3300
3400
|
const processPid = parseProcessPid(sessionId);
|
|
3301
3401
|
if (processPid) {
|
|
3402
|
+
const t0 = performance.now();
|
|
3302
3403
|
const procResult = discoverViaProc(processPid, agentType);
|
|
3303
|
-
if (procResult)
|
|
3404
|
+
if (procResult) {
|
|
3405
|
+
console.log(`[DISCOVER] discoverViaProc found file for PID ${processPid} in ${(performance.now() - t0).toFixed(2)} ms`);
|
|
3406
|
+
return procResult;
|
|
3407
|
+
}
|
|
3304
3408
|
if (agentType === "Claude Code") {
|
|
3409
|
+
const t1 = performance.now();
|
|
3305
3410
|
const startMs = getProcessStartEpochMs(processPid);
|
|
3306
3411
|
const cwdResult = discoverViaProcessCwd(processPid, startMs ? startMs - 6e4 : void 0);
|
|
3412
|
+
console.log(`[DISCOVER] Claude Code discovery for PID ${processPid} took ${(performance.now() - t1).toFixed(2)} ms`);
|
|
3307
3413
|
if (cwdResult) return cwdResult;
|
|
3308
3414
|
}
|
|
3309
3415
|
if (agentType === "Codex CLI") {
|
|
3416
|
+
const t1 = performance.now();
|
|
3310
3417
|
const startMs = getProcessStartEpochMs(processPid);
|
|
3311
3418
|
const command = getProcessCommand(processPid);
|
|
3312
3419
|
if (commandMatchesAgent(command, agentType)) {
|
|
3313
3420
|
const codexResult = discoverCodexTranscriptFromHome(processPid, startMs ? startMs - 6e4 : void 0);
|
|
3421
|
+
console.log(`[DISCOVER] Codex CLI discovery for PID ${processPid} took ${(performance.now() - t1).toFixed(2)} ms`);
|
|
3314
3422
|
if (codexResult) return codexResult;
|
|
3315
3423
|
}
|
|
3316
3424
|
}
|
|
3317
3425
|
if (agentType === "Gemini CLI") {
|
|
3426
|
+
const t1 = performance.now();
|
|
3318
3427
|
const geminiResult = discoverGeminiTranscriptFromHome(processPid);
|
|
3428
|
+
console.log(`[DISCOVER] Gemini CLI discovery for PID ${processPid} took ${(performance.now() - t1).toFixed(2)} ms`);
|
|
3319
3429
|
if (geminiResult) return geminiResult;
|
|
3320
3430
|
}
|
|
3321
3431
|
if (agentType === "Antigravity CLI") {
|
|
3432
|
+
const t1 = performance.now();
|
|
3322
3433
|
const startMs = getProcessStartEpochMs(processPid);
|
|
3434
|
+
try {
|
|
3435
|
+
const processCwd = fs2.readlinkSync(`/proc/${processPid}/cwd`);
|
|
3436
|
+
if (processCwd) {
|
|
3437
|
+
const cwdResult = discoverAntigravityViaCwd(processCwd, processPid);
|
|
3438
|
+
if (cwdResult) {
|
|
3439
|
+
console.log(`[DISCOVER] Antigravity CLI discovery (CWD path) for PID ${processPid} took ${(performance.now() - t1).toFixed(2)} ms`);
|
|
3440
|
+
return cwdResult;
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
} catch {
|
|
3444
|
+
}
|
|
3323
3445
|
const antigravityResult = discoverAntigravityTranscriptFromHome(processPid, startMs ? startMs - 6e4 : void 0);
|
|
3446
|
+
console.log(`[DISCOVER] Antigravity CLI discovery (home path) for PID ${processPid} took ${(performance.now() - t1).toFixed(2)} ms`);
|
|
3324
3447
|
if (antigravityResult) return antigravityResult;
|
|
3325
3448
|
}
|
|
3326
3449
|
return null;
|
|
@@ -3380,6 +3503,34 @@ function discoverTranscriptFile(sessionId, agentType) {
|
|
|
3380
3503
|
if (agentType === "Gemini CLI") {
|
|
3381
3504
|
return discoverGeminiTranscriptFromHome(panePid ?? void 0);
|
|
3382
3505
|
}
|
|
3506
|
+
if (agentType === "Antigravity CLI") {
|
|
3507
|
+
const agentInfos = panePid ? findAgentProcessInfos(panePid, agentType) : [];
|
|
3508
|
+
for (const info of agentInfos) {
|
|
3509
|
+
if (info.cwd) {
|
|
3510
|
+
const cwdResult = discoverAntigravityViaCwd(info.cwd, info.pid);
|
|
3511
|
+
if (cwdResult) return cwdResult;
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
try {
|
|
3515
|
+
const paneCwd = (0, import_child_process2.execFileSync)(
|
|
3516
|
+
"tmux",
|
|
3517
|
+
["display-message", "-t", paneTarget, "-p", "#{pane_current_path}"],
|
|
3518
|
+
{ env: cleanEnv, timeout: 5e3, encoding: "utf-8" }
|
|
3519
|
+
).trim();
|
|
3520
|
+
if (paneCwd) {
|
|
3521
|
+
const cwdResult = discoverAntigravityViaCwd(paneCwd, panePid ?? void 0);
|
|
3522
|
+
if (cwdResult) return cwdResult;
|
|
3523
|
+
}
|
|
3524
|
+
} catch {
|
|
3525
|
+
}
|
|
3526
|
+
const newestForAgent = getNewestPath(
|
|
3527
|
+
agentInfos.map((info) => discoverAntigravityTranscriptFromHome(
|
|
3528
|
+
info.pid,
|
|
3529
|
+
info.startEpochMs ? info.startEpochMs - 6e4 : void 0
|
|
3530
|
+
)).filter((value) => Boolean(value))
|
|
3531
|
+
);
|
|
3532
|
+
if (newestForAgent) return newestForAgent;
|
|
3533
|
+
}
|
|
3383
3534
|
return null;
|
|
3384
3535
|
}
|
|
3385
3536
|
return null;
|
|
@@ -3695,7 +3846,7 @@ var TranscriptWatcherManager = class {
|
|
|
3695
3846
|
const existing = this.active.get(sessionId);
|
|
3696
3847
|
let replacingExisting = false;
|
|
3697
3848
|
if (existing) {
|
|
3698
|
-
const newPath = discoverTranscriptFile(sessionId, agentType);
|
|
3849
|
+
const newPath = discoverTranscriptFile(sessionId, agentType, true);
|
|
3699
3850
|
const agentChanged = existing.agentType !== requestedAgentType;
|
|
3700
3851
|
const pathChanged = Boolean(newPath && newPath !== existing.filePath);
|
|
3701
3852
|
const existingPathGone = !fs2.existsSync(existing.filePath);
|
|
@@ -3720,7 +3871,7 @@ var TranscriptWatcherManager = class {
|
|
|
3720
3871
|
this.cancelEnableRetry(sessionId);
|
|
3721
3872
|
return { ok: false, reason: "unsupported_agent" };
|
|
3722
3873
|
}
|
|
3723
|
-
const filePath = discoverTranscriptFile(sessionId, agentType);
|
|
3874
|
+
const filePath = discoverTranscriptFile(sessionId, agentType, isRetry || replacingExisting);
|
|
3724
3875
|
if (!filePath) {
|
|
3725
3876
|
log7.info("no transcript file found", { sessionId });
|
|
3726
3877
|
this.armEnableRetry(sessionId, agentType);
|
|
@@ -3846,7 +3997,7 @@ var TranscriptWatcherManager = class {
|
|
|
3846
3997
|
this.stopRediscovery(sessionId);
|
|
3847
3998
|
return;
|
|
3848
3999
|
}
|
|
3849
|
-
const newPath = discoverTranscriptFile(sessionId, agentType);
|
|
4000
|
+
const newPath = discoverTranscriptFile(sessionId, agentType, true);
|
|
3850
4001
|
if (!newPath || newPath === session.filePath) return;
|
|
3851
4002
|
log7.info("transcript file changed", { sessionId, newPath });
|
|
3852
4003
|
session.watcher.stop();
|
|
@@ -9524,6 +9675,39 @@ function resolveRuntimeTranscriptSource(sessionId) {
|
|
|
9524
9675
|
// src/sessions.ts
|
|
9525
9676
|
var isMac = os5.platform() === "darwin";
|
|
9526
9677
|
var MARKDOWN_AGENT_TYPES = /* @__PURE__ */ new Set(["Claude Code", "Codex CLI", "Gemini CLI", "Antigravity CLI"]);
|
|
9678
|
+
var isTmuxAvailable = null;
|
|
9679
|
+
var isScreenAvailable = null;
|
|
9680
|
+
var isZellijAvailable = null;
|
|
9681
|
+
async function checkTmuxAvailable() {
|
|
9682
|
+
if (isTmuxAvailable !== null) return isTmuxAvailable;
|
|
9683
|
+
try {
|
|
9684
|
+
await execAsync("tmux -V");
|
|
9685
|
+
isTmuxAvailable = true;
|
|
9686
|
+
} catch {
|
|
9687
|
+
isTmuxAvailable = false;
|
|
9688
|
+
}
|
|
9689
|
+
return isTmuxAvailable;
|
|
9690
|
+
}
|
|
9691
|
+
async function checkScreenAvailable() {
|
|
9692
|
+
if (isScreenAvailable !== null) return isScreenAvailable;
|
|
9693
|
+
try {
|
|
9694
|
+
await execAsync("screen -v");
|
|
9695
|
+
isScreenAvailable = true;
|
|
9696
|
+
} catch {
|
|
9697
|
+
isScreenAvailable = false;
|
|
9698
|
+
}
|
|
9699
|
+
return isScreenAvailable;
|
|
9700
|
+
}
|
|
9701
|
+
async function checkZellijAvailable() {
|
|
9702
|
+
if (isZellijAvailable !== null) return isZellijAvailable;
|
|
9703
|
+
try {
|
|
9704
|
+
await execAsync("zellij --version");
|
|
9705
|
+
isZellijAvailable = true;
|
|
9706
|
+
} catch {
|
|
9707
|
+
isZellijAvailable = false;
|
|
9708
|
+
}
|
|
9709
|
+
return isZellijAvailable;
|
|
9710
|
+
}
|
|
9527
9711
|
function getProcessStartTime(pid) {
|
|
9528
9712
|
if (isMac) return null;
|
|
9529
9713
|
try {
|
|
@@ -9540,7 +9724,38 @@ function getProcessStartTime(pid) {
|
|
|
9540
9724
|
function collectStartTimes(pids) {
|
|
9541
9725
|
return pids.map((pid) => getProcessStartTime(pid) ?? 0);
|
|
9542
9726
|
}
|
|
9543
|
-
async function
|
|
9727
|
+
async function getProcessTable() {
|
|
9728
|
+
try {
|
|
9729
|
+
const raw = await execAsync("ps axo pid,ppid,tty,stat,comm,args");
|
|
9730
|
+
const rows = [];
|
|
9731
|
+
const lines = raw.split("\n");
|
|
9732
|
+
for (let i = 0; i < lines.length; i++) {
|
|
9733
|
+
const trimmed = lines[i].trim();
|
|
9734
|
+
if (!trimmed) continue;
|
|
9735
|
+
if (i === 0 && /^\s*PID\b/i.test(lines[i])) continue;
|
|
9736
|
+
const parts = trimmed.split(/\s+/);
|
|
9737
|
+
if (parts.length < 6) continue;
|
|
9738
|
+
const pid = Number(parts[0]);
|
|
9739
|
+
const ppid = Number(parts[1]);
|
|
9740
|
+
const tty = parts[2];
|
|
9741
|
+
const stat = parts[3];
|
|
9742
|
+
const comm = parts[4];
|
|
9743
|
+
const args = parts.slice(5).join(" ");
|
|
9744
|
+
rows.push({
|
|
9745
|
+
pid,
|
|
9746
|
+
ppid: Number.isFinite(ppid) ? ppid : 0,
|
|
9747
|
+
tty,
|
|
9748
|
+
stat,
|
|
9749
|
+
comm,
|
|
9750
|
+
args
|
|
9751
|
+
});
|
|
9752
|
+
}
|
|
9753
|
+
return rows;
|
|
9754
|
+
} catch {
|
|
9755
|
+
return [];
|
|
9756
|
+
}
|
|
9757
|
+
}
|
|
9758
|
+
async function detectVscodeEnvironment(pid, processByPid) {
|
|
9544
9759
|
if (!isMac) {
|
|
9545
9760
|
try {
|
|
9546
9761
|
const environ = fs4.readFileSync(`/proc/${pid}/environ`, "utf8");
|
|
@@ -9550,17 +9765,36 @@ async function detectVscodeEnvironment(pid) {
|
|
|
9550
9765
|
} catch {
|
|
9551
9766
|
}
|
|
9552
9767
|
}
|
|
9553
|
-
|
|
9554
|
-
|
|
9555
|
-
|
|
9556
|
-
|
|
9557
|
-
|
|
9558
|
-
|
|
9559
|
-
|
|
9560
|
-
|
|
9768
|
+
if (!processByPid || processByPid.size === 0) {
|
|
9769
|
+
try {
|
|
9770
|
+
const raw = await execAsync(`ps -p ${pid} -o ppid=`);
|
|
9771
|
+
const ppid = Number(raw.trim());
|
|
9772
|
+
if (ppid > 1) {
|
|
9773
|
+
const parentInfo = await execAsync(`ps -p ${ppid} -o comm=`);
|
|
9774
|
+
const parentComm = parentInfo.trim().toLowerCase();
|
|
9775
|
+
if (parentComm === "code" || parentComm === "code-server") {
|
|
9776
|
+
return true;
|
|
9777
|
+
}
|
|
9561
9778
|
}
|
|
9779
|
+
} catch {
|
|
9562
9780
|
}
|
|
9563
|
-
|
|
9781
|
+
return false;
|
|
9782
|
+
}
|
|
9783
|
+
let currentPid = pid;
|
|
9784
|
+
const visited = /* @__PURE__ */ new Set();
|
|
9785
|
+
while (currentPid > 1 && !visited.has(currentPid)) {
|
|
9786
|
+
visited.add(currentPid);
|
|
9787
|
+
const proc = processByPid.get(currentPid);
|
|
9788
|
+
if (!proc) break;
|
|
9789
|
+
const parentPid = proc.ppid;
|
|
9790
|
+
if (parentPid <= 1) break;
|
|
9791
|
+
const parentProc = processByPid.get(parentPid);
|
|
9792
|
+
if (!parentProc) break;
|
|
9793
|
+
const parentComm = parentProc.comm.toLowerCase();
|
|
9794
|
+
if (parentComm === "code" || parentComm === "code-server") {
|
|
9795
|
+
return true;
|
|
9796
|
+
}
|
|
9797
|
+
currentPid = parentPid;
|
|
9564
9798
|
}
|
|
9565
9799
|
return false;
|
|
9566
9800
|
}
|
|
@@ -9619,12 +9853,14 @@ function classifyFromCommand(command) {
|
|
|
9619
9853
|
}
|
|
9620
9854
|
return { agentType, confidence: 0.7 };
|
|
9621
9855
|
}
|
|
9622
|
-
async function collectTmuxSessions() {
|
|
9623
|
-
|
|
9624
|
-
await execAsync("tmux -V");
|
|
9625
|
-
} catch {
|
|
9856
|
+
async function collectTmuxSessions(processByPid) {
|
|
9857
|
+
if (!await checkTmuxAvailable()) {
|
|
9626
9858
|
return [];
|
|
9627
9859
|
}
|
|
9860
|
+
if (!processByPid) {
|
|
9861
|
+
const list = await getProcessTable();
|
|
9862
|
+
processByPid = new Map(list.map((p) => [p.pid, p]));
|
|
9863
|
+
}
|
|
9628
9864
|
try {
|
|
9629
9865
|
const raw = await execAsync(
|
|
9630
9866
|
"tmux list-panes -a -F '#{session_name}|#{window_index}|#{pane_index}|#{pane_current_command}|#{session_attached}|#{window_activity}|#{pane_pid}|#{window_layout}|#{pane_current_path}|#{window_name}|#{pane_title}|#{window_flags}|#{session_group}|#{session_grouped}'"
|
|
@@ -9658,11 +9894,14 @@ async function collectTmuxSessions() {
|
|
|
9658
9894
|
const pid = Number(panePid);
|
|
9659
9895
|
if (pid > 0) pids.push(pid);
|
|
9660
9896
|
const classification = classifyAgent(command, { workingDir: paneCurrentPath });
|
|
9661
|
-
const vscodeDetected = pid > 0 ? await detectVscodeEnvironment(pid) : false;
|
|
9897
|
+
const vscodeDetected = pid > 0 ? await detectVscodeEnvironment(pid, processByPid) : false;
|
|
9898
|
+
const dirName = paneCurrentPath ? paneCurrentPath.split("/").filter(Boolean).pop() : "";
|
|
9899
|
+
const baseName = `${sessionName}:${windowIndex}.${paneIndex}`;
|
|
9900
|
+
const finalName = dirName ? `${baseName} (${dirName})` : baseName;
|
|
9662
9901
|
results.push({
|
|
9663
9902
|
id,
|
|
9664
9903
|
type: "tmux",
|
|
9665
|
-
name:
|
|
9904
|
+
name: finalName,
|
|
9666
9905
|
status: Number(attachedCount) > 0 ? "active" : "detached",
|
|
9667
9906
|
command,
|
|
9668
9907
|
agentType: classification?.agentType,
|
|
@@ -9687,10 +9926,8 @@ async function collectTmuxSessions() {
|
|
|
9687
9926
|
return [];
|
|
9688
9927
|
}
|
|
9689
9928
|
}
|
|
9690
|
-
async function collectScreenSessions() {
|
|
9691
|
-
|
|
9692
|
-
await execAsync("screen -v");
|
|
9693
|
-
} catch {
|
|
9929
|
+
async function collectScreenSessions(childrenByPpid) {
|
|
9930
|
+
if (!await checkScreenAvailable()) {
|
|
9694
9931
|
return [];
|
|
9695
9932
|
}
|
|
9696
9933
|
try {
|
|
@@ -9702,7 +9939,7 @@ async function collectScreenSessions() {
|
|
|
9702
9939
|
if (!match) continue;
|
|
9703
9940
|
const [, screenPid, sessionName, state] = match;
|
|
9704
9941
|
const pid = Number(screenPid);
|
|
9705
|
-
const childInfo = await getChildAgentInfo(pid);
|
|
9942
|
+
const childInfo = await getChildAgentInfo(pid, childrenByPpid);
|
|
9706
9943
|
if (!childInfo) continue;
|
|
9707
9944
|
const id = `screen:${sessionName}:${screenPid}`;
|
|
9708
9945
|
const allPids = [pid, ...childInfo.childPids];
|
|
@@ -9733,10 +9970,8 @@ async function collectScreenSessions() {
|
|
|
9733
9970
|
return [];
|
|
9734
9971
|
}
|
|
9735
9972
|
}
|
|
9736
|
-
async function collectZellijSessions() {
|
|
9737
|
-
|
|
9738
|
-
await execAsync("zellij --version");
|
|
9739
|
-
} catch {
|
|
9973
|
+
async function collectZellijSessions(childrenByPpid, processList) {
|
|
9974
|
+
if (!await checkZellijAvailable()) {
|
|
9740
9975
|
return [];
|
|
9741
9976
|
}
|
|
9742
9977
|
try {
|
|
@@ -9745,19 +9980,25 @@ async function collectZellijSessions() {
|
|
|
9745
9980
|
for (const line of raw.split("\n")) {
|
|
9746
9981
|
const sessionName = line.trim();
|
|
9747
9982
|
if (!sessionName) continue;
|
|
9748
|
-
let serverPids;
|
|
9749
|
-
|
|
9750
|
-
|
|
9751
|
-
|
|
9752
|
-
);
|
|
9753
|
-
|
|
9754
|
-
|
|
9755
|
-
|
|
9983
|
+
let serverPids = [];
|
|
9984
|
+
if (processList) {
|
|
9985
|
+
serverPids = processList.filter(
|
|
9986
|
+
(p) => p.args.includes("zellij") && p.args.includes(`--session ${sessionName}`)
|
|
9987
|
+
).map((p) => p.pid);
|
|
9988
|
+
} else {
|
|
9989
|
+
try {
|
|
9990
|
+
const psOut = await execAsync(
|
|
9991
|
+
`ps axo pid,args | grep 'zellij.*--session ${sessionName}' | grep -v grep`
|
|
9992
|
+
);
|
|
9993
|
+
serverPids = psOut.split("\n").map((l) => Number(l.trim().split(/\s+/)[0])).filter((p) => p > 0);
|
|
9994
|
+
} catch {
|
|
9995
|
+
serverPids = [];
|
|
9996
|
+
}
|
|
9756
9997
|
}
|
|
9757
9998
|
const allChildPids = [];
|
|
9758
9999
|
let foundAgent = null;
|
|
9759
10000
|
for (const spid of serverPids) {
|
|
9760
|
-
const childInfo = await getChildAgentInfo(spid);
|
|
10001
|
+
const childInfo = await getChildAgentInfo(spid, childrenByPpid);
|
|
9761
10002
|
if (childInfo) {
|
|
9762
10003
|
foundAgent = childInfo;
|
|
9763
10004
|
allChildPids.push(...childInfo.childPids);
|
|
@@ -9809,7 +10050,7 @@ async function getProcessWorkingDir(pid) {
|
|
|
9809
10050
|
}
|
|
9810
10051
|
return null;
|
|
9811
10052
|
}
|
|
9812
|
-
const cwd = await
|
|
10053
|
+
const cwd = await fs4.promises.readlink(`/proc/${pid}/cwd`);
|
|
9813
10054
|
return cwd.trim() || null;
|
|
9814
10055
|
} catch {
|
|
9815
10056
|
return null;
|
|
@@ -9827,35 +10068,20 @@ function transcriptSourceForSession(agentType, sessionId) {
|
|
|
9827
10068
|
if (!agentType) return void 0;
|
|
9828
10069
|
return getTranscriptSourceForAgentType(agentType);
|
|
9829
10070
|
}
|
|
9830
|
-
async function collectBareAgentProcesses(excludePids) {
|
|
10071
|
+
async function collectBareAgentProcesses(excludePids, processList, processByPid) {
|
|
9831
10072
|
try {
|
|
9832
|
-
const
|
|
10073
|
+
const tStart = performance.now();
|
|
10074
|
+
if (!processList || !processByPid) {
|
|
10075
|
+
const list = await getProcessTable();
|
|
10076
|
+
processList = list;
|
|
10077
|
+
processByPid = new Map(list.map((p) => [p.pid, p]));
|
|
10078
|
+
}
|
|
10079
|
+
const tInit = performance.now();
|
|
9833
10080
|
const sessions = [];
|
|
9834
10081
|
const seen = /* @__PURE__ */ new Set();
|
|
9835
10082
|
const excluded = excludePids ?? /* @__PURE__ */ new Set();
|
|
9836
|
-
const
|
|
9837
|
-
const
|
|
9838
|
-
for (let i = 0; i < lines.length; i++) {
|
|
9839
|
-
const trimmed = lines[i].trim();
|
|
9840
|
-
if (!trimmed) continue;
|
|
9841
|
-
if (i === 0 && /^\s*PID\b/i.test(lines[i])) continue;
|
|
9842
|
-
const parts = trimmed.split(/\s+/);
|
|
9843
|
-
if (parts.length < 6) continue;
|
|
9844
|
-
const pid = Number(parts[0]);
|
|
9845
|
-
const ppid = Number(parts[1]);
|
|
9846
|
-
const tty = parts[2];
|
|
9847
|
-
const stat = parts[3];
|
|
9848
|
-
const args = parts.slice(5).join(" ");
|
|
9849
|
-
rows.push({
|
|
9850
|
-
pid,
|
|
9851
|
-
ppid: Number.isFinite(ppid) ? ppid : 0,
|
|
9852
|
-
tty,
|
|
9853
|
-
stat,
|
|
9854
|
-
args
|
|
9855
|
-
});
|
|
9856
|
-
}
|
|
9857
|
-
rows.sort((a, b) => a.pid - b.pid);
|
|
9858
|
-
const parentByPid = new Map(rows.map((row) => [row.pid, row.ppid]));
|
|
10083
|
+
const parentByPid = new Map(processList.map((row) => [row.pid, row.ppid]));
|
|
10084
|
+
const tParentMap = performance.now();
|
|
9859
10085
|
const hasTrackedAncestor = (pid, tracked) => {
|
|
9860
10086
|
const visited = /* @__PURE__ */ new Set();
|
|
9861
10087
|
let current = parentByPid.get(pid) ?? 0;
|
|
@@ -9866,24 +10092,60 @@ async function collectBareAgentProcesses(excludePids) {
|
|
|
9866
10092
|
}
|
|
9867
10093
|
return false;
|
|
9868
10094
|
};
|
|
9869
|
-
|
|
10095
|
+
const tFilterStart = performance.now();
|
|
10096
|
+
const candidates = processList.filter((row) => {
|
|
9870
10097
|
const { pid, tty, stat, args } = row;
|
|
9871
|
-
if (!hasControllingTty(tty))
|
|
9872
|
-
if (stat.startsWith("Z"))
|
|
9873
|
-
if (excluded.has(pid) || hasTrackedAncestor(pid, excluded))
|
|
9874
|
-
|
|
10098
|
+
if (!hasControllingTty(tty)) return false;
|
|
10099
|
+
if (stat.startsWith("Z")) return false;
|
|
10100
|
+
if (excluded.has(pid) || hasTrackedAncestor(pid, excluded)) return false;
|
|
10101
|
+
const lowerArgs = args.toLowerCase();
|
|
10102
|
+
const hasKeyword = lowerArgs.includes("claude") || lowerArgs.includes("codex") || lowerArgs.includes("aider") || lowerArgs.includes("cursor") || lowerArgs.includes("windsurf") || lowerArgs.includes("gemini") || lowerArgs.includes("agy") || lowerArgs.includes("antigravity") || lowerArgs.includes("amazon") || lowerArgs.includes("copilot");
|
|
10103
|
+
if (!hasKeyword) return false;
|
|
10104
|
+
return true;
|
|
10105
|
+
});
|
|
10106
|
+
const tFilterEnd = performance.now();
|
|
10107
|
+
const tCwdsStart = performance.now();
|
|
10108
|
+
const cwds = await Promise.all(
|
|
10109
|
+
candidates.map(async (row) => {
|
|
10110
|
+
const cwd = await getProcessWorkingDir(row.pid);
|
|
10111
|
+
return { pid: row.pid, cwd };
|
|
10112
|
+
})
|
|
10113
|
+
);
|
|
10114
|
+
const cwdByPid = new Map(cwds.map((c) => [c.pid, c.cwd]));
|
|
10115
|
+
const tCwdsEnd = performance.now();
|
|
10116
|
+
let tLoopAncestor = 0;
|
|
10117
|
+
let tLoopClassify = 0;
|
|
10118
|
+
let tLoopVscode = 0;
|
|
10119
|
+
let tLoopMarkdown = 0;
|
|
10120
|
+
let tLoopStartTimes = 0;
|
|
10121
|
+
const tLoopStart = performance.now();
|
|
10122
|
+
for (const row of candidates) {
|
|
10123
|
+
const { pid, args } = row;
|
|
10124
|
+
const t0 = performance.now();
|
|
10125
|
+
const skip = seen.has(pid) || hasTrackedAncestor(pid, seen);
|
|
10126
|
+
tLoopAncestor += performance.now() - t0;
|
|
10127
|
+
if (skip) continue;
|
|
10128
|
+
const workingDir = cwdByPid.get(pid) || null;
|
|
10129
|
+
const t1 = performance.now();
|
|
9875
10130
|
let classification = classifyAgent(args);
|
|
9876
10131
|
if (!classification) {
|
|
9877
|
-
|
|
9878
|
-
classification = classifyAgent(args, { workingDir: inferredWorkingDir ?? void 0 });
|
|
10132
|
+
classification = classifyAgent(args, { workingDir: workingDir ?? void 0 });
|
|
9879
10133
|
}
|
|
10134
|
+
tLoopClassify += performance.now() - t1;
|
|
9880
10135
|
if (!classification) continue;
|
|
9881
10136
|
seen.add(pid);
|
|
9882
|
-
const workingDir = await getProcessWorkingDir(pid);
|
|
9883
10137
|
const dirName = workingDir ? workingDir.split("/").pop() : null;
|
|
9884
10138
|
const displayName = dirName && dirName !== "/" && dirName !== "" ? `${classification.agentType} (${dirName})` : `${classification.agentType} (pid ${pid})`;
|
|
9885
|
-
const
|
|
10139
|
+
const t2 = performance.now();
|
|
10140
|
+
const vscodeDetected = await detectVscodeEnvironment(pid, processByPid);
|
|
10141
|
+
tLoopVscode += performance.now() - t2;
|
|
9886
10142
|
const id = `process:${pid}`;
|
|
10143
|
+
const t3 = performance.now();
|
|
10144
|
+
const mdAvail = markdownAvailableForSession(id, classification.agentType);
|
|
10145
|
+
tLoopMarkdown += performance.now() - t3;
|
|
10146
|
+
const t4 = performance.now();
|
|
10147
|
+
const startTimes = collectStartTimes([pid]);
|
|
10148
|
+
tLoopStartTimes += performance.now() - t4;
|
|
9887
10149
|
sessions.push({
|
|
9888
10150
|
id,
|
|
9889
10151
|
type: "process",
|
|
@@ -9892,32 +10154,54 @@ async function collectBareAgentProcesses(excludePids) {
|
|
|
9892
10154
|
command: args,
|
|
9893
10155
|
agentType: classification.agentType,
|
|
9894
10156
|
agentConfidence: classification.confidence,
|
|
9895
|
-
markdownAvailable:
|
|
10157
|
+
markdownAvailable: mdAvail,
|
|
9896
10158
|
transcriptSource: transcriptSourceForSession(classification.agentType, id),
|
|
9897
10159
|
workingDir: workingDir || void 0,
|
|
9898
10160
|
environment: vscodeDetected ? "vscode" : void 0,
|
|
9899
10161
|
lastActivityAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9900
10162
|
pids: [pid],
|
|
9901
|
-
startTimes
|
|
10163
|
+
startTimes
|
|
9902
10164
|
});
|
|
9903
10165
|
}
|
|
10166
|
+
const tLoopEnd = performance.now();
|
|
10167
|
+
console.log(`[PROFILE-BARE] Init took ${(tInit - tStart).toFixed(2)} ms`);
|
|
10168
|
+
console.log(`[PROFILE-BARE] Parent map took ${(tParentMap - tInit).toFixed(2)} ms`);
|
|
10169
|
+
console.log(`[PROFILE-BARE] Filtering took ${(tFilterEnd - tFilterStart).toFixed(2)} ms`);
|
|
10170
|
+
console.log(`[PROFILE-BARE] CWDs took ${(tCwdsEnd - tCwdsStart).toFixed(2)} ms`);
|
|
10171
|
+
console.log(`[PROFILE-BARE] Loop total took ${(tLoopEnd - tLoopStart).toFixed(2)} ms`);
|
|
10172
|
+
console.log(` [PROFILE-BARE-LOOP] Ancestor checks: ${tLoopAncestor.toFixed(2)} ms`);
|
|
10173
|
+
console.log(` [PROFILE-BARE-LOOP] Classify: ${tLoopClassify.toFixed(2)} ms`);
|
|
10174
|
+
console.log(` [PROFILE-BARE-LOOP] VS Code env: ${tLoopVscode.toFixed(2)} ms`);
|
|
10175
|
+
console.log(` [PROFILE-BARE-LOOP] Markdown check: ${tLoopMarkdown.toFixed(2)} ms`);
|
|
10176
|
+
console.log(` [PROFILE-BARE-LOOP] Start times: ${tLoopStartTimes.toFixed(2)} ms`);
|
|
9904
10177
|
return sessions;
|
|
9905
|
-
} catch {
|
|
10178
|
+
} catch (err) {
|
|
10179
|
+
console.error("[PROFILE-BARE] Error:", err);
|
|
9906
10180
|
return [];
|
|
9907
10181
|
}
|
|
9908
10182
|
}
|
|
9909
10183
|
async function collectSessionInventory(hostId, command) {
|
|
10184
|
+
const processList = await getProcessTable();
|
|
10185
|
+
const processByPid = /* @__PURE__ */ new Map();
|
|
10186
|
+
const childrenByPpid = /* @__PURE__ */ new Map();
|
|
10187
|
+
for (const p of processList) {
|
|
10188
|
+
processByPid.set(p.pid, p);
|
|
10189
|
+
if (!childrenByPpid.has(p.ppid)) {
|
|
10190
|
+
childrenByPpid.set(p.ppid, []);
|
|
10191
|
+
}
|
|
10192
|
+
childrenByPpid.get(p.ppid).push(p);
|
|
10193
|
+
}
|
|
9910
10194
|
const [tmux, screen, zellij] = await Promise.all([
|
|
9911
|
-
collectTmuxSessions(),
|
|
9912
|
-
collectScreenSessions(),
|
|
9913
|
-
collectZellijSessions()
|
|
10195
|
+
collectTmuxSessions(processByPid),
|
|
10196
|
+
collectScreenSessions(childrenByPpid),
|
|
10197
|
+
collectZellijSessions(childrenByPpid, processList)
|
|
9914
10198
|
]);
|
|
9915
10199
|
const multiplexerPids = /* @__PURE__ */ new Set();
|
|
9916
10200
|
for (const s of [...screen, ...zellij]) {
|
|
9917
10201
|
if (s.pids) s.pids.forEach((p) => multiplexerPids.add(p));
|
|
9918
10202
|
}
|
|
9919
10203
|
const tmuxChildTasks = tmux.flatMap(
|
|
9920
|
-
(s) => (s.pids ?? []).map(async (pid) => ({ session: s, pid, childInfo: await getChildAgentInfo(pid) }))
|
|
10204
|
+
(s) => (s.pids ?? []).map(async (pid) => ({ session: s, pid, childInfo: await getChildAgentInfo(pid, childrenByPpid) }))
|
|
9921
10205
|
);
|
|
9922
10206
|
const tmuxChildResults = await Promise.all(tmuxChildTasks);
|
|
9923
10207
|
for (const { session: s, pid, childInfo } of tmuxChildResults) {
|
|
@@ -9932,7 +10216,7 @@ async function collectSessionInventory(hostId, command) {
|
|
|
9932
10216
|
s.markdownAvailable = markdownAvailableForSession(s.id, s.agentType);
|
|
9933
10217
|
s.transcriptSource = transcriptSourceForSession(s.agentType, s.id);
|
|
9934
10218
|
}
|
|
9935
|
-
const bare = await collectBareAgentProcesses(multiplexerPids);
|
|
10219
|
+
const bare = await collectBareAgentProcesses(multiplexerPids, processList, processByPid);
|
|
9936
10220
|
const ptyClassification = classifyAgent(command);
|
|
9937
10221
|
const ptySession = {
|
|
9938
10222
|
id: `pty:${hostId}`,
|
|
@@ -9985,7 +10269,7 @@ function detectAgentType(command) {
|
|
|
9985
10269
|
return "Windsurf";
|
|
9986
10270
|
}
|
|
9987
10271
|
if (binary === "gemini" || scriptArg === "gemini") return "Gemini CLI";
|
|
9988
|
-
if (binary === "agy" || binary === "antigravity" || binary === "antigravity-cli") return "Antigravity CLI";
|
|
10272
|
+
if (binary === "agy" || scriptArg === "agy" || binary === "antigravity" || scriptArg === "antigravity" || binary === "antigravity-cli" || scriptArg === "antigravity-cli") return "Antigravity CLI";
|
|
9989
10273
|
if (binary === "amazon-q" || binary === "amazon_q") return "Amazon Q";
|
|
9990
10274
|
if (binary === "copilot" || scriptArg === "copilot") return "Copilot CLI";
|
|
9991
10275
|
return void 0;
|
|
@@ -9999,42 +10283,62 @@ function sessionInventoryFingerprint(sessions) {
|
|
|
9999
10283
|
return `${s.id}|${s.type}|${s.name}|${s.status}|${s.command || ""}|${s.runningCommand || ""}|${pidStr}|${markdownFlag}|${sourceFlag}`;
|
|
10000
10284
|
}).join("\n");
|
|
10001
10285
|
}
|
|
10002
|
-
async function getChildAgentInfo(parentPid, visited = /* @__PURE__ */ new Set()) {
|
|
10286
|
+
async function getChildAgentInfo(parentPid, childrenByPpid, visited = /* @__PURE__ */ new Set()) {
|
|
10003
10287
|
if (visited.has(parentPid)) return null;
|
|
10004
10288
|
visited.add(parentPid);
|
|
10005
|
-
|
|
10006
|
-
|
|
10007
|
-
|
|
10008
|
-
|
|
10009
|
-
|
|
10010
|
-
|
|
10011
|
-
|
|
10012
|
-
|
|
10013
|
-
|
|
10014
|
-
|
|
10015
|
-
|
|
10016
|
-
|
|
10017
|
-
|
|
10018
|
-
|
|
10019
|
-
|
|
10020
|
-
|
|
10021
|
-
|
|
10022
|
-
|
|
10023
|
-
|
|
10024
|
-
|
|
10025
|
-
|
|
10026
|
-
|
|
10027
|
-
|
|
10028
|
-
deeper
|
|
10029
|
-
|
|
10030
|
-
|
|
10289
|
+
if (!childrenByPpid || childrenByPpid.size === 0) {
|
|
10290
|
+
try {
|
|
10291
|
+
const pgrepOut = await execAsync(`pgrep -P ${parentPid}`);
|
|
10292
|
+
const pidList = pgrepOut.split("\n").map((l) => l.trim()).filter(Boolean).map(Number).filter((p) => p > 0);
|
|
10293
|
+
if (pidList.length === 0) return null;
|
|
10294
|
+
const raw = await execAsync(`ps -p ${pidList.join(",")} -o pid,args`);
|
|
10295
|
+
const descendantPids2 = new Set(pidList);
|
|
10296
|
+
let foundAgent2 = null;
|
|
10297
|
+
const lines = raw.split("\n");
|
|
10298
|
+
for (let i = 0; i < lines.length; i++) {
|
|
10299
|
+
const trimmed = lines[i].trim();
|
|
10300
|
+
if (!trimmed) continue;
|
|
10301
|
+
if (i === 0 && /^\s*PID\b/i.test(lines[i])) continue;
|
|
10302
|
+
const spaceIdx = trimmed.indexOf(" ");
|
|
10303
|
+
if (spaceIdx < 0) continue;
|
|
10304
|
+
const pid = Number(trimmed.slice(0, spaceIdx));
|
|
10305
|
+
const args = trimmed.slice(spaceIdx + 1).trim();
|
|
10306
|
+
if (pid <= 0) continue;
|
|
10307
|
+
const agentType = detectAgentType(args);
|
|
10308
|
+
if (agentType && !foundAgent2) {
|
|
10309
|
+
foundAgent2 = { command: args, agentType };
|
|
10310
|
+
}
|
|
10311
|
+
const deeper = await getChildAgentInfo(pid, void 0, visited);
|
|
10312
|
+
if (deeper) {
|
|
10313
|
+
deeper.childPids.forEach((childPid) => descendantPids2.add(childPid));
|
|
10314
|
+
if (!foundAgent2) {
|
|
10315
|
+
foundAgent2 = { command: deeper.command, agentType: deeper.agentType };
|
|
10316
|
+
}
|
|
10031
10317
|
}
|
|
10032
10318
|
}
|
|
10319
|
+
return foundAgent2 ? { ...foundAgent2, childPids: [...descendantPids2] } : null;
|
|
10320
|
+
} catch {
|
|
10321
|
+
return null;
|
|
10322
|
+
}
|
|
10323
|
+
}
|
|
10324
|
+
const children = childrenByPpid.get(parentPid) ?? [];
|
|
10325
|
+
if (children.length === 0) return null;
|
|
10326
|
+
const descendantPids = new Set(children.map((c) => c.pid));
|
|
10327
|
+
let foundAgent = null;
|
|
10328
|
+
for (const child of children) {
|
|
10329
|
+
const agentType = detectAgentType(child.args);
|
|
10330
|
+
if (agentType && !foundAgent) {
|
|
10331
|
+
foundAgent = { command: child.args, agentType };
|
|
10332
|
+
}
|
|
10333
|
+
const deeper = await getChildAgentInfo(child.pid, childrenByPpid, visited);
|
|
10334
|
+
if (deeper) {
|
|
10335
|
+
deeper.childPids.forEach((childPid) => descendantPids.add(childPid));
|
|
10336
|
+
if (!foundAgent) {
|
|
10337
|
+
foundAgent = { command: deeper.command, agentType: deeper.agentType };
|
|
10338
|
+
}
|
|
10033
10339
|
}
|
|
10034
|
-
return foundAgent ? { ...foundAgent, childPids: [...descendantPids] } : null;
|
|
10035
|
-
} catch {
|
|
10036
|
-
return null;
|
|
10037
10340
|
}
|
|
10341
|
+
return foundAgent ? { ...foundAgent, childPids: [...descendantPids] } : null;
|
|
10038
10342
|
}
|
|
10039
10343
|
|
|
10040
10344
|
// src/auth.ts
|
|
@@ -11667,16 +11971,16 @@ var InventoryManager = class {
|
|
|
11667
11971
|
this.options = options;
|
|
11668
11972
|
}
|
|
11669
11973
|
/** Announce to the registry group via WebSocket. */
|
|
11670
|
-
async announceToRegistry(ws, registryGroup, type = "heartbeat") {
|
|
11974
|
+
async announceToRegistry(ws, registryGroup, type = "heartbeat", sessions) {
|
|
11671
11975
|
if (ws.readyState !== import_ws2.default.OPEN || !registryGroup) return;
|
|
11672
|
-
const
|
|
11976
|
+
const finalSessions = sessions || await collectSessionInventory(this.options.hostId, this.options.command);
|
|
11673
11977
|
const vitals = this.collectVitals();
|
|
11674
11978
|
sendToGroup(ws, registryGroup, {
|
|
11675
11979
|
type,
|
|
11676
11980
|
hostId: this.options.hostId,
|
|
11677
11981
|
hostName: this.options.hostName,
|
|
11678
11982
|
environment: os7.hostname(),
|
|
11679
|
-
sessions,
|
|
11983
|
+
sessions: finalSessions,
|
|
11680
11984
|
vitals,
|
|
11681
11985
|
agentVersion: CURRENT_VERSION,
|
|
11682
11986
|
lastSeenAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -11693,7 +11997,7 @@ var InventoryManager = class {
|
|
|
11693
11997
|
const checkpointDue = Date.now() - this.lastHttpHeartbeatAt > HTTP_HEARTBEAT_MS;
|
|
11694
11998
|
if (changed || force) {
|
|
11695
11999
|
if (ws && ws.readyState === import_ws2.default.OPEN) {
|
|
11696
|
-
await this.announceToRegistry(ws, groups.registryGroup, "inventory");
|
|
12000
|
+
await this.announceToRegistry(ws, groups.registryGroup, "inventory", sessions);
|
|
11697
12001
|
}
|
|
11698
12002
|
this.lastSentFingerprint = fingerprint;
|
|
11699
12003
|
}
|
|
@@ -11819,10 +12123,36 @@ var ConnectionManager = class {
|
|
|
11819
12123
|
resetReconnectDelay() {
|
|
11820
12124
|
this.reconnectDelay = DEFAULT_RECONNECT_DELAY_MS;
|
|
11821
12125
|
}
|
|
12126
|
+
/**
|
|
12127
|
+
* Clear all active connection timers to prevent leaks.
|
|
12128
|
+
*/
|
|
12129
|
+
clearTimers() {
|
|
12130
|
+
if (this.wsPingTimer) {
|
|
12131
|
+
clearInterval(this.wsPingTimer);
|
|
12132
|
+
this.wsPingTimer = null;
|
|
12133
|
+
}
|
|
12134
|
+
if (this.wsPongTimeout) {
|
|
12135
|
+
clearTimeout(this.wsPongTimeout);
|
|
12136
|
+
this.wsPongTimeout = null;
|
|
12137
|
+
}
|
|
12138
|
+
if (this.wsHeartbeatTimer) {
|
|
12139
|
+
clearInterval(this.wsHeartbeatTimer);
|
|
12140
|
+
this.wsHeartbeatTimer = null;
|
|
12141
|
+
}
|
|
12142
|
+
if (this.inventorySyncTimer) {
|
|
12143
|
+
clearInterval(this.inventorySyncTimer);
|
|
12144
|
+
this.inventorySyncTimer = null;
|
|
12145
|
+
}
|
|
12146
|
+
if (this.inventorySyncKickTimer) {
|
|
12147
|
+
clearTimeout(this.inventorySyncKickTimer);
|
|
12148
|
+
this.inventorySyncKickTimer = null;
|
|
12149
|
+
}
|
|
12150
|
+
}
|
|
11822
12151
|
/**
|
|
11823
12152
|
* Start periodic timers (heartbeat, inventory sync, ping/pong).
|
|
11824
12153
|
*/
|
|
11825
12154
|
startTimers(onWsHeartbeat, onInventorySync) {
|
|
12155
|
+
this.clearTimers();
|
|
11826
12156
|
this.onInventorySync = onInventorySync;
|
|
11827
12157
|
this.wsHeartbeatTimer = setInterval(async () => {
|
|
11828
12158
|
if (!this.isReady()) return;
|
|
@@ -11833,7 +12163,19 @@ var ConnectionManager = class {
|
|
|
11833
12163
|
}, INVENTORY_SYNC_MS);
|
|
11834
12164
|
this.wsPingTimer = setInterval(() => {
|
|
11835
12165
|
if (!this.activeSocket || this.activeSocket.readyState !== import_ws3.default.OPEN) return;
|
|
11836
|
-
this.
|
|
12166
|
+
if (this.wsPongTimeout) {
|
|
12167
|
+
clearTimeout(this.wsPongTimeout);
|
|
12168
|
+
this.wsPongTimeout = null;
|
|
12169
|
+
}
|
|
12170
|
+
try {
|
|
12171
|
+
this.activeSocket.send(JSON.stringify({ type: "ping" }));
|
|
12172
|
+
} catch (err) {
|
|
12173
|
+
log20.error("failed to send JSON ping", { error: err.message });
|
|
12174
|
+
}
|
|
12175
|
+
try {
|
|
12176
|
+
this.activeSocket.ping();
|
|
12177
|
+
} catch {
|
|
12178
|
+
}
|
|
11837
12179
|
this.wsPongTimeout = setTimeout(() => {
|
|
11838
12180
|
log20.warn("no pong received within 10s; closing stale connection");
|
|
11839
12181
|
try {
|
|
@@ -11884,26 +12226,7 @@ var ConnectionManager = class {
|
|
|
11884
12226
|
*/
|
|
11885
12227
|
cleanup(opts = {}) {
|
|
11886
12228
|
if (opts.stopStreams) this.sessionStreamer.stopAll();
|
|
11887
|
-
|
|
11888
|
-
clearInterval(this.wsPingTimer);
|
|
11889
|
-
this.wsPingTimer = null;
|
|
11890
|
-
}
|
|
11891
|
-
if (this.wsPongTimeout) {
|
|
11892
|
-
clearTimeout(this.wsPongTimeout);
|
|
11893
|
-
this.wsPongTimeout = null;
|
|
11894
|
-
}
|
|
11895
|
-
if (this.wsHeartbeatTimer) {
|
|
11896
|
-
clearInterval(this.wsHeartbeatTimer);
|
|
11897
|
-
this.wsHeartbeatTimer = null;
|
|
11898
|
-
}
|
|
11899
|
-
if (this.inventorySyncTimer) {
|
|
11900
|
-
clearInterval(this.inventorySyncTimer);
|
|
11901
|
-
this.inventorySyncTimer = null;
|
|
11902
|
-
}
|
|
11903
|
-
if (this.inventorySyncKickTimer) {
|
|
11904
|
-
clearTimeout(this.inventorySyncKickTimer);
|
|
11905
|
-
this.inventorySyncKickTimer = null;
|
|
11906
|
-
}
|
|
12229
|
+
this.clearTimers();
|
|
11907
12230
|
if (this.activeSocket) {
|
|
11908
12231
|
this.activeSocket.removeAllListeners();
|
|
11909
12232
|
try {
|
|
@@ -12965,7 +13288,6 @@ var ControlDispatcher = class {
|
|
|
12965
13288
|
}
|
|
12966
13289
|
/** Handle input routing to the correct target. */
|
|
12967
13290
|
handleInput(data, sessionId) {
|
|
12968
|
-
this.connection.requestInventorySync(800);
|
|
12969
13291
|
if (!sessionId || sessionId.startsWith("pty:")) {
|
|
12970
13292
|
this.shell.write(data);
|
|
12971
13293
|
} else if (sessionId.startsWith("tmux:")) {
|
|
@@ -13936,7 +14258,6 @@ async function runAgent(rawOptions) {
|
|
|
13936
14258
|
const ptySessionId = `pty:${options.hostId}`;
|
|
13937
14259
|
const sessionStreamer = new SessionStreamer(
|
|
13938
14260
|
(sessionId, data, viewerId) => {
|
|
13939
|
-
conn.requestInventorySync();
|
|
13940
14261
|
if (!conn.isReady()) {
|
|
13941
14262
|
sessionStreamer.bufferOutput(sessionId, data);
|
|
13942
14263
|
return;
|
|
@@ -13968,7 +14289,6 @@ async function runAgent(rawOptions) {
|
|
|
13968
14289
|
);
|
|
13969
14290
|
const conn = new ConnectionManager(sessionStreamer, outputBuffer);
|
|
13970
14291
|
const sendOutput = (chunk) => {
|
|
13971
|
-
conn.requestInventorySync();
|
|
13972
14292
|
if (!conn.isReady()) {
|
|
13973
14293
|
outputBuffer.push(chunk);
|
|
13974
14294
|
return;
|
|
@@ -14220,6 +14540,10 @@ async function runAgent(rawOptions) {
|
|
|
14220
14540
|
} catch {
|
|
14221
14541
|
return;
|
|
14222
14542
|
}
|
|
14543
|
+
if (msg.type === "pong") {
|
|
14544
|
+
conn.onPong();
|
|
14545
|
+
return;
|
|
14546
|
+
}
|
|
14223
14547
|
if (msg.type === "message" && msg.group === groups.privateGroup) {
|
|
14224
14548
|
const payload = msg.data || {};
|
|
14225
14549
|
if (payload.type === "input") {
|
package/dist/sbom.json
CHANGED
|
@@ -1299,8 +1299,8 @@
|
|
|
1299
1299
|
{
|
|
1300
1300
|
"type": "library",
|
|
1301
1301
|
"name": "ragent-cli",
|
|
1302
|
-
"version": "1.11.
|
|
1303
|
-
"bom-ref": "ragent-live|ragent-cli@1.11.
|
|
1302
|
+
"version": "1.11.12",
|
|
1303
|
+
"bom-ref": "ragent-live|ragent-cli@1.11.12",
|
|
1304
1304
|
"author": "Intellimetrics",
|
|
1305
1305
|
"description": "CLI agent for rAgent Live — browser-first terminal control plane for AI coding agents",
|
|
1306
1306
|
"licenses": [
|
|
@@ -1311,7 +1311,7 @@
|
|
|
1311
1311
|
}
|
|
1312
1312
|
}
|
|
1313
1313
|
],
|
|
1314
|
-
"purl": "pkg:npm/ragent-cli@1.11.
|
|
1314
|
+
"purl": "pkg:npm/ragent-cli@1.11.12",
|
|
1315
1315
|
"externalReferences": [
|
|
1316
1316
|
{
|
|
1317
1317
|
"url": "https://github.com/chadlindell/ragent-live/issues",
|
|
@@ -1436,7 +1436,7 @@
|
|
|
1436
1436
|
"ragent-live|@emnapi/wasi-threads@1.2.1",
|
|
1437
1437
|
"ragent-live|@pkgjs/parseargs@0.11.0",
|
|
1438
1438
|
"ragent-live|@tybys/wasm-util@0.10.1",
|
|
1439
|
-
"ragent-live|ragent-cli@1.11.
|
|
1439
|
+
"ragent-live|ragent-cli@1.11.12"
|
|
1440
1440
|
]
|
|
1441
1441
|
},
|
|
1442
1442
|
{
|
|
@@ -1584,7 +1584,7 @@
|
|
|
1584
1584
|
]
|
|
1585
1585
|
},
|
|
1586
1586
|
{
|
|
1587
|
-
"ref": "ragent-live|ragent-cli@1.11.
|
|
1587
|
+
"ref": "ragent-live|ragent-cli@1.11.12",
|
|
1588
1588
|
"dependsOn": [
|
|
1589
1589
|
"ragent-live|@azure/web-pubsub-client@1.0.4",
|
|
1590
1590
|
"ragent-live|@openai/codex-sdk@0.130.0",
|