ragent-cli 1.11.12 → 1.11.14
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 +252 -229
- 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.14",
|
|
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: {
|
|
@@ -3538,6 +3538,7 @@ function discoverTranscriptFileImpl(sessionId, agentType) {
|
|
|
3538
3538
|
var MAX_PARTIAL_BUFFER = 256 * 1024;
|
|
3539
3539
|
var MAX_REPLAY_TURNS = 1e3;
|
|
3540
3540
|
var POLL_INTERVAL_MS = 800;
|
|
3541
|
+
var DOC_READ_DEBOUNCE_MS = 200;
|
|
3541
3542
|
var REDISCOVERY_INTERVAL_MS = 5e3;
|
|
3542
3543
|
var ENABLE_RETRY_DELAYS_MS = [1e3, 2e3, 5e3];
|
|
3543
3544
|
var TranscriptWatcher = class {
|
|
@@ -3554,6 +3555,7 @@ var TranscriptWatcher = class {
|
|
|
3554
3555
|
snapshot;
|
|
3555
3556
|
watcher = null;
|
|
3556
3557
|
pollTimer = null;
|
|
3558
|
+
docReadDebounceTimer = null;
|
|
3557
3559
|
stopped = false;
|
|
3558
3560
|
subscriberCount = 0;
|
|
3559
3561
|
documentFingerprint = "";
|
|
@@ -3608,6 +3610,10 @@ var TranscriptWatcher = class {
|
|
|
3608
3610
|
clearInterval(this.pollTimer);
|
|
3609
3611
|
this.pollTimer = null;
|
|
3610
3612
|
}
|
|
3613
|
+
if (this.docReadDebounceTimer) {
|
|
3614
|
+
clearTimeout(this.docReadDebounceTimer);
|
|
3615
|
+
this.docReadDebounceTimer = null;
|
|
3616
|
+
}
|
|
3611
3617
|
}
|
|
3612
3618
|
/** Get accumulated turns for replay (up to MAX_REPLAY_TURNS). */
|
|
3613
3619
|
getReplayTurns(fromSeq) {
|
|
@@ -3666,7 +3672,16 @@ var TranscriptWatcher = class {
|
|
|
3666
3672
|
startWatching() {
|
|
3667
3673
|
try {
|
|
3668
3674
|
this.watcher = fs2.watch(this.filePath, () => {
|
|
3669
|
-
if (
|
|
3675
|
+
if (this.stopped) return;
|
|
3676
|
+
if (this.parser.parseDocument) {
|
|
3677
|
+
if (this.docReadDebounceTimer) return;
|
|
3678
|
+
this.docReadDebounceTimer = setTimeout(() => {
|
|
3679
|
+
this.docReadDebounceTimer = null;
|
|
3680
|
+
if (!this.stopped) this.readNewData();
|
|
3681
|
+
}, DOC_READ_DEBOUNCE_MS);
|
|
3682
|
+
return;
|
|
3683
|
+
}
|
|
3684
|
+
this.readNewData();
|
|
3670
3685
|
});
|
|
3671
3686
|
this.watcher.on("error", () => {
|
|
3672
3687
|
this.watcher?.close();
|
|
@@ -3721,16 +3736,20 @@ var TranscriptWatcher = class {
|
|
|
3721
3736
|
}
|
|
3722
3737
|
}
|
|
3723
3738
|
readDocument(force) {
|
|
3724
|
-
let content;
|
|
3725
3739
|
let stat;
|
|
3726
3740
|
try {
|
|
3727
3741
|
stat = fs2.statSync(this.filePath);
|
|
3728
|
-
content = fs2.readFileSync(this.filePath, "utf-8");
|
|
3729
3742
|
} catch {
|
|
3730
3743
|
return;
|
|
3731
3744
|
}
|
|
3732
3745
|
const fingerprint = `${stat.mtimeMs}:${stat.size}`;
|
|
3733
3746
|
if (!force && fingerprint === this.documentFingerprint) return;
|
|
3747
|
+
let content;
|
|
3748
|
+
try {
|
|
3749
|
+
content = fs2.readFileSync(this.filePath, "utf-8");
|
|
3750
|
+
} catch {
|
|
3751
|
+
return;
|
|
3752
|
+
}
|
|
3734
3753
|
this.documentFingerprint = fingerprint;
|
|
3735
3754
|
const parsedTurns = this.parser.parseDocument?.(content) ?? [];
|
|
3736
3755
|
this.seq++;
|
|
@@ -9673,6 +9692,7 @@ function resolveRuntimeTranscriptSource(sessionId) {
|
|
|
9673
9692
|
}
|
|
9674
9693
|
|
|
9675
9694
|
// src/sessions.ts
|
|
9695
|
+
var log15 = createLogger("sessions");
|
|
9676
9696
|
var isMac = os5.platform() === "darwin";
|
|
9677
9697
|
var MARKDOWN_AGENT_TYPES = /* @__PURE__ */ new Set(["Claude Code", "Codex CLI", "Gemini CLI", "Antigravity CLI"]);
|
|
9678
9698
|
var isTmuxAvailable = null;
|
|
@@ -10070,18 +10090,15 @@ function transcriptSourceForSession(agentType, sessionId) {
|
|
|
10070
10090
|
}
|
|
10071
10091
|
async function collectBareAgentProcesses(excludePids, processList, processByPid) {
|
|
10072
10092
|
try {
|
|
10073
|
-
const tStart = performance.now();
|
|
10074
10093
|
if (!processList || !processByPid) {
|
|
10075
10094
|
const list = await getProcessTable();
|
|
10076
10095
|
processList = list;
|
|
10077
10096
|
processByPid = new Map(list.map((p) => [p.pid, p]));
|
|
10078
10097
|
}
|
|
10079
|
-
const tInit = performance.now();
|
|
10080
10098
|
const sessions = [];
|
|
10081
10099
|
const seen = /* @__PURE__ */ new Set();
|
|
10082
10100
|
const excluded = excludePids ?? /* @__PURE__ */ new Set();
|
|
10083
10101
|
const parentByPid = new Map(processList.map((row) => [row.pid, row.ppid]));
|
|
10084
|
-
const tParentMap = performance.now();
|
|
10085
10102
|
const hasTrackedAncestor = (pid, tracked) => {
|
|
10086
10103
|
const visited = /* @__PURE__ */ new Set();
|
|
10087
10104
|
let current = parentByPid.get(pid) ?? 0;
|
|
@@ -10092,7 +10109,6 @@ async function collectBareAgentProcesses(excludePids, processList, processByPid)
|
|
|
10092
10109
|
}
|
|
10093
10110
|
return false;
|
|
10094
10111
|
};
|
|
10095
|
-
const tFilterStart = performance.now();
|
|
10096
10112
|
const candidates = processList.filter((row) => {
|
|
10097
10113
|
const { pid, tty, stat, args } = row;
|
|
10098
10114
|
if (!hasControllingTty(tty)) return false;
|
|
@@ -10103,8 +10119,6 @@ async function collectBareAgentProcesses(excludePids, processList, processByPid)
|
|
|
10103
10119
|
if (!hasKeyword) return false;
|
|
10104
10120
|
return true;
|
|
10105
10121
|
});
|
|
10106
|
-
const tFilterEnd = performance.now();
|
|
10107
|
-
const tCwdsStart = performance.now();
|
|
10108
10122
|
const cwds = await Promise.all(
|
|
10109
10123
|
candidates.map(async (row) => {
|
|
10110
10124
|
const cwd = await getProcessWorkingDir(row.pid);
|
|
@@ -10112,40 +10126,22 @@ async function collectBareAgentProcesses(excludePids, processList, processByPid)
|
|
|
10112
10126
|
})
|
|
10113
10127
|
);
|
|
10114
10128
|
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
10129
|
for (const row of candidates) {
|
|
10123
10130
|
const { pid, args } = row;
|
|
10124
|
-
|
|
10125
|
-
const skip = seen.has(pid) || hasTrackedAncestor(pid, seen);
|
|
10126
|
-
tLoopAncestor += performance.now() - t0;
|
|
10127
|
-
if (skip) continue;
|
|
10131
|
+
if (seen.has(pid) || hasTrackedAncestor(pid, seen)) continue;
|
|
10128
10132
|
const workingDir = cwdByPid.get(pid) || null;
|
|
10129
|
-
const t1 = performance.now();
|
|
10130
10133
|
let classification = classifyAgent(args);
|
|
10131
10134
|
if (!classification) {
|
|
10132
10135
|
classification = classifyAgent(args, { workingDir: workingDir ?? void 0 });
|
|
10133
10136
|
}
|
|
10134
|
-
tLoopClassify += performance.now() - t1;
|
|
10135
10137
|
if (!classification) continue;
|
|
10136
10138
|
seen.add(pid);
|
|
10137
10139
|
const dirName = workingDir ? workingDir.split("/").pop() : null;
|
|
10138
10140
|
const displayName = dirName && dirName !== "/" && dirName !== "" ? `${classification.agentType} (${dirName})` : `${classification.agentType} (pid ${pid})`;
|
|
10139
|
-
const t2 = performance.now();
|
|
10140
10141
|
const vscodeDetected = await detectVscodeEnvironment(pid, processByPid);
|
|
10141
|
-
tLoopVscode += performance.now() - t2;
|
|
10142
10142
|
const id = `process:${pid}`;
|
|
10143
|
-
const t3 = performance.now();
|
|
10144
10143
|
const mdAvail = markdownAvailableForSession(id, classification.agentType);
|
|
10145
|
-
tLoopMarkdown += performance.now() - t3;
|
|
10146
|
-
const t4 = performance.now();
|
|
10147
10144
|
const startTimes = collectStartTimes([pid]);
|
|
10148
|
-
tLoopStartTimes += performance.now() - t4;
|
|
10149
10145
|
sessions.push({
|
|
10150
10146
|
id,
|
|
10151
10147
|
type: "process",
|
|
@@ -10163,20 +10159,11 @@ async function collectBareAgentProcesses(excludePids, processList, processByPid)
|
|
|
10163
10159
|
startTimes
|
|
10164
10160
|
});
|
|
10165
10161
|
}
|
|
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`);
|
|
10177
10162
|
return sessions;
|
|
10178
10163
|
} catch (err) {
|
|
10179
|
-
|
|
10164
|
+
log15.error("collectBareAgentProcesses failed", {
|
|
10165
|
+
error: err instanceof Error ? err.stack || err.message : String(err)
|
|
10166
|
+
});
|
|
10180
10167
|
return [];
|
|
10181
10168
|
}
|
|
10182
10169
|
}
|
|
@@ -10342,7 +10329,7 @@ async function getChildAgentInfo(parentPid, childrenByPpid, visited = /* @__PURE
|
|
|
10342
10329
|
}
|
|
10343
10330
|
|
|
10344
10331
|
// src/auth.ts
|
|
10345
|
-
var
|
|
10332
|
+
var log16 = createLogger("auth");
|
|
10346
10333
|
var AuthError = class extends Error {
|
|
10347
10334
|
constructor(message) {
|
|
10348
10335
|
super(message);
|
|
@@ -10360,7 +10347,7 @@ function decodeJwtExp(token) {
|
|
|
10360
10347
|
}
|
|
10361
10348
|
}
|
|
10362
10349
|
async function startSessionWithMachineSecret(params) {
|
|
10363
|
-
|
|
10350
|
+
log16.info("attempting session recovery with machine credential");
|
|
10364
10351
|
const response = await fetch(`${params.portal}/api/agent/session/start`, {
|
|
10365
10352
|
method: "POST",
|
|
10366
10353
|
headers: { "Content-Type": "application/json" },
|
|
@@ -10393,7 +10380,7 @@ async function startSessionWithMachineSecret(params) {
|
|
|
10393
10380
|
patch.refreshExpiresAt = data.refreshExpiresAt || "";
|
|
10394
10381
|
}
|
|
10395
10382
|
saveConfigPatch(patch);
|
|
10396
|
-
|
|
10383
|
+
log16.info("session recovered via machine credential");
|
|
10397
10384
|
return data.agentToken;
|
|
10398
10385
|
}
|
|
10399
10386
|
async function refreshTokenIfNeeded(params) {
|
|
@@ -10404,7 +10391,7 @@ async function refreshTokenIfNeeded(params) {
|
|
|
10404
10391
|
const remainingSeconds = exp - Math.floor(Date.now() / 1e3);
|
|
10405
10392
|
const threshold2 = remainingSeconds < 3600 ? 120 : 3600;
|
|
10406
10393
|
if (remainingSeconds > threshold2) return params.agentToken;
|
|
10407
|
-
|
|
10394
|
+
log16.info("token expiring \u2014 refreshing", { minutesRemaining: Math.round(remainingSeconds / 60) });
|
|
10408
10395
|
try {
|
|
10409
10396
|
const headers = { "Content-Type": "application/json" };
|
|
10410
10397
|
let body = {};
|
|
@@ -10420,7 +10407,7 @@ async function refreshTokenIfNeeded(params) {
|
|
|
10420
10407
|
});
|
|
10421
10408
|
if (!response.ok) {
|
|
10422
10409
|
const errorData = await response.json().catch(() => ({}));
|
|
10423
|
-
|
|
10410
|
+
log16.warn("token refresh failed", { error: errorData.error || response.status });
|
|
10424
10411
|
if (config.machineSecret && config.hostId) {
|
|
10425
10412
|
try {
|
|
10426
10413
|
return await startSessionWithMachineSecret({
|
|
@@ -10431,7 +10418,7 @@ async function refreshTokenIfNeeded(params) {
|
|
|
10431
10418
|
} catch (mcError) {
|
|
10432
10419
|
if (mcError instanceof AuthError) throw mcError;
|
|
10433
10420
|
const mcMessage = mcError instanceof Error ? mcError.message : String(mcError);
|
|
10434
|
-
|
|
10421
|
+
log16.warn("machine credential recovery failed", { error: mcMessage });
|
|
10435
10422
|
}
|
|
10436
10423
|
}
|
|
10437
10424
|
return params.agentToken;
|
|
@@ -10450,12 +10437,12 @@ async function refreshTokenIfNeeded(params) {
|
|
|
10450
10437
|
patch.machineSecret = data.machineSecret;
|
|
10451
10438
|
}
|
|
10452
10439
|
saveConfigPatch(patch);
|
|
10453
|
-
|
|
10440
|
+
log16.info("token refreshed successfully");
|
|
10454
10441
|
return data.agentToken;
|
|
10455
10442
|
} catch (error) {
|
|
10456
10443
|
if (error instanceof AuthError) throw error;
|
|
10457
10444
|
const message = error instanceof Error ? error.message : String(error);
|
|
10458
|
-
|
|
10445
|
+
log16.warn("token refresh error", { error: message });
|
|
10459
10446
|
return params.agentToken;
|
|
10460
10447
|
}
|
|
10461
10448
|
}
|
|
@@ -10518,7 +10505,7 @@ async function deregisterHost(params) {
|
|
|
10518
10505
|
body: JSON.stringify({})
|
|
10519
10506
|
});
|
|
10520
10507
|
if (response.status === 401 || response.status === 403) {
|
|
10521
|
-
|
|
10508
|
+
log16.warn("token expired \u2014 could not deregister from server. Host may remain visible in dashboard until manually removed");
|
|
10522
10509
|
return false;
|
|
10523
10510
|
}
|
|
10524
10511
|
return response.ok;
|
|
@@ -10658,6 +10645,9 @@ function getQueuePressure() {
|
|
|
10658
10645
|
else if (level < PRESSURE_LOW) pressureIsHigh = false;
|
|
10659
10646
|
return { level, isHigh: pressureIsHigh };
|
|
10660
10647
|
}
|
|
10648
|
+
function getDroppedFrameCount() {
|
|
10649
|
+
return droppedFrames;
|
|
10650
|
+
}
|
|
10661
10651
|
function drainQueue() {
|
|
10662
10652
|
if (!drainWs || drainWs.readyState !== import_ws.default.OPEN) {
|
|
10663
10653
|
stopDrainTimer();
|
|
@@ -10741,7 +10731,7 @@ var import_node_path = require("path");
|
|
|
10741
10731
|
var import_node_os = require("os");
|
|
10742
10732
|
var import_node_string_decoder = require("string_decoder");
|
|
10743
10733
|
var pty = __toESM(require("node-pty"));
|
|
10744
|
-
var
|
|
10734
|
+
var log17 = createLogger("session-streamer");
|
|
10745
10735
|
var BACKPRESSURE_RESUME_CHECK_MS = 100;
|
|
10746
10736
|
var STOP_DEBOUNCE_MS = 2e3;
|
|
10747
10737
|
var MAX_CONCURRENT_STREAMS = 20;
|
|
@@ -10843,6 +10833,10 @@ var SessionStreamer = class {
|
|
|
10843
10833
|
disconnectBufferBytes = /* @__PURE__ */ new Map();
|
|
10844
10834
|
/** Timer that resumes paused PTYs once the WS queue drains. See #332. */
|
|
10845
10835
|
backpressureResumeTimer = null;
|
|
10836
|
+
/** Dropped-frame count at the last resume-timer check. */
|
|
10837
|
+
lastSeenDroppedFrames = 0;
|
|
10838
|
+
/** Sessions whose viewers need a capture-pane resync after frame drops. */
|
|
10839
|
+
pendingDropResync = /* @__PURE__ */ new Set();
|
|
10846
10840
|
constructor(sendFn, onStreamStopped) {
|
|
10847
10841
|
this.sendFn = sendFn;
|
|
10848
10842
|
this.onStreamStopped = onStreamStopped;
|
|
@@ -10853,6 +10847,14 @@ var SessionStreamer = class {
|
|
|
10853
10847
|
* When the WS queue is saturated, PTY onData stops firing after we call
|
|
10854
10848
|
* ptyProc.pause(). We need an independent loop to check pressure and
|
|
10855
10849
|
* resume each paused PTY once the queue drains. See #332.
|
|
10850
|
+
*
|
|
10851
|
+
* The same loop heals tmux-pipe streams after frame drops: tmux pipes
|
|
10852
|
+
* have no pause primitive, so under sustained pressure the send queue
|
|
10853
|
+
* overflows and drops frames — severing ANSI sequences and leaving
|
|
10854
|
+
* every viewer garbled until the next full repaint. Once pressure
|
|
10855
|
+
* clears, any drop since the last check triggers a capture-pane resync
|
|
10856
|
+
* (one stream per tick, so the resyncs themselves don't re-saturate
|
|
10857
|
+
* the queue).
|
|
10856
10858
|
*/
|
|
10857
10859
|
startBackpressureResumeTimer() {
|
|
10858
10860
|
if (this.backpressureResumeTimer) return;
|
|
@@ -10864,11 +10866,32 @@ var SessionStreamer = class {
|
|
|
10864
10866
|
try {
|
|
10865
10867
|
stream.ptyProc.resume();
|
|
10866
10868
|
stream.ptyPaused = false;
|
|
10867
|
-
|
|
10869
|
+
log17.info("pty-attach resumed after WS queue drained", { sessionId: stream.sessionId });
|
|
10868
10870
|
} catch {
|
|
10869
10871
|
}
|
|
10870
10872
|
}
|
|
10871
10873
|
}
|
|
10874
|
+
const dropped = getDroppedFrameCount();
|
|
10875
|
+
if (dropped > this.lastSeenDroppedFrames) {
|
|
10876
|
+
for (const stream of this.active.values()) {
|
|
10877
|
+
if (stream.streamType === "tmux-pipe" && !stream.stopped && !stream.initializing) {
|
|
10878
|
+
this.pendingDropResync.add(stream.sessionId);
|
|
10879
|
+
}
|
|
10880
|
+
}
|
|
10881
|
+
this.lastSeenDroppedFrames = dropped;
|
|
10882
|
+
log17.warn("frames dropped under backpressure; scheduling stream resyncs", {
|
|
10883
|
+
droppedTotal: dropped,
|
|
10884
|
+
streams: this.pendingDropResync.size
|
|
10885
|
+
});
|
|
10886
|
+
}
|
|
10887
|
+
const next = this.pendingDropResync.values().next();
|
|
10888
|
+
if (!next.done) {
|
|
10889
|
+
this.pendingDropResync.delete(next.value);
|
|
10890
|
+
const stream = this.active.get(next.value);
|
|
10891
|
+
if (stream && !stream.stopped) {
|
|
10892
|
+
this.resyncStream(next.value);
|
|
10893
|
+
}
|
|
10894
|
+
}
|
|
10872
10895
|
}, BACKPRESSURE_RESUME_CHECK_MS);
|
|
10873
10896
|
}
|
|
10874
10897
|
/**
|
|
@@ -10883,7 +10906,7 @@ var SessionStreamer = class {
|
|
|
10883
10906
|
try {
|
|
10884
10907
|
stream.ptyProc.pause();
|
|
10885
10908
|
stream.ptyPaused = true;
|
|
10886
|
-
|
|
10909
|
+
log17.warn("pty-attach paused under WS backpressure", {
|
|
10887
10910
|
sessionId: stream.sessionId,
|
|
10888
10911
|
queueLevel: pressure.level.toFixed(2)
|
|
10889
10912
|
});
|
|
@@ -10907,7 +10930,7 @@ var SessionStreamer = class {
|
|
|
10907
10930
|
}
|
|
10908
10931
|
}
|
|
10909
10932
|
if (entries.some((e) => e.startsWith("s-"))) {
|
|
10910
|
-
|
|
10933
|
+
log17.info("cleaned up stale stream directories from previous run");
|
|
10911
10934
|
}
|
|
10912
10935
|
} catch {
|
|
10913
10936
|
}
|
|
@@ -10925,7 +10948,7 @@ var SessionStreamer = class {
|
|
|
10925
10948
|
if (viewerKey) {
|
|
10926
10949
|
this.active.get(sessionId)?.viewerIds.add(viewerKey);
|
|
10927
10950
|
}
|
|
10928
|
-
|
|
10951
|
+
log17.info("cancelled pending stop (re-mounted)", { sessionId });
|
|
10929
10952
|
return true;
|
|
10930
10953
|
}
|
|
10931
10954
|
}
|
|
@@ -10936,7 +10959,7 @@ var SessionStreamer = class {
|
|
|
10936
10959
|
return true;
|
|
10937
10960
|
}
|
|
10938
10961
|
if (this.active.size >= MAX_CONCURRENT_STREAMS) {
|
|
10939
|
-
|
|
10962
|
+
log17.warn("stream limit reached; rejecting", {
|
|
10940
10963
|
limit: MAX_CONCURRENT_STREAMS,
|
|
10941
10964
|
sessionId
|
|
10942
10965
|
});
|
|
@@ -10967,7 +10990,7 @@ var SessionStreamer = class {
|
|
|
10967
10990
|
stream.ptyProc.write(data);
|
|
10968
10991
|
} catch (error) {
|
|
10969
10992
|
const message = error instanceof Error ? error.message : String(error);
|
|
10970
|
-
|
|
10993
|
+
log17.warn("failed to write input", { sessionId, error: message });
|
|
10971
10994
|
}
|
|
10972
10995
|
}
|
|
10973
10996
|
/**
|
|
@@ -10981,7 +11004,7 @@ var SessionStreamer = class {
|
|
|
10981
11004
|
} catch (error) {
|
|
10982
11005
|
const message = error instanceof Error ? error.message : String(error);
|
|
10983
11006
|
if (!message.includes("EBADF")) {
|
|
10984
|
-
|
|
11007
|
+
log17.warn("resize failed", { sessionId, error: message });
|
|
10985
11008
|
}
|
|
10986
11009
|
}
|
|
10987
11010
|
}
|
|
@@ -11007,7 +11030,7 @@ var SessionStreamer = class {
|
|
|
11007
11030
|
this.active.delete(sessionId);
|
|
11008
11031
|
this.disconnectBuffers.delete(sessionId);
|
|
11009
11032
|
this.disconnectBufferBytes.delete(sessionId);
|
|
11010
|
-
|
|
11033
|
+
log17.info("stopped streaming", { sessionId });
|
|
11011
11034
|
}, STOP_DEBOUNCE_MS);
|
|
11012
11035
|
this.pendingStops.set(sessionId, timer);
|
|
11013
11036
|
}
|
|
@@ -11032,7 +11055,7 @@ var SessionStreamer = class {
|
|
|
11032
11055
|
this.pendingStops.clear();
|
|
11033
11056
|
for (const [id, stream] of this.active) {
|
|
11034
11057
|
this.cleanupStream(stream);
|
|
11035
|
-
|
|
11058
|
+
log17.info("stopped streaming", { sessionId: id });
|
|
11036
11059
|
}
|
|
11037
11060
|
this.active.clear();
|
|
11038
11061
|
this.disconnectBuffers.clear();
|
|
@@ -11170,13 +11193,13 @@ var SessionStreamer = class {
|
|
|
11170
11193
|
}
|
|
11171
11194
|
stream.initializing = false;
|
|
11172
11195
|
stream.initBuffer = [];
|
|
11173
|
-
|
|
11196
|
+
log17.info("resync capture", { sessionId });
|
|
11174
11197
|
return true;
|
|
11175
11198
|
} catch (error) {
|
|
11176
11199
|
stream.initializing = false;
|
|
11177
11200
|
stream.initBuffer = [];
|
|
11178
11201
|
const message = error instanceof Error ? error.message : String(error);
|
|
11179
|
-
|
|
11202
|
+
log17.warn("failed to resync stream", { sessionId, error: message });
|
|
11180
11203
|
return false;
|
|
11181
11204
|
}
|
|
11182
11205
|
}
|
|
@@ -11230,7 +11253,7 @@ var SessionStreamer = class {
|
|
|
11230
11253
|
} catch (error) {
|
|
11231
11254
|
this.cleanupStream(stream);
|
|
11232
11255
|
const message = error instanceof Error ? error.message : String(error);
|
|
11233
|
-
|
|
11256
|
+
log17.warn("failed pipe-pane", { sessionId, error: message });
|
|
11234
11257
|
return false;
|
|
11235
11258
|
}
|
|
11236
11259
|
const catProc = (0, import_node_child_process2.spawn)("cat", [fifoPath], { env: cleanEnv, stdio: ["ignore", "pipe", "ignore"] });
|
|
@@ -11258,7 +11281,7 @@ var SessionStreamer = class {
|
|
|
11258
11281
|
this.cleanupStream(stream);
|
|
11259
11282
|
this.active.delete(sessionId);
|
|
11260
11283
|
this.pendingStops.delete(sessionId);
|
|
11261
|
-
|
|
11284
|
+
log17.info("session stream ended", { sessionId });
|
|
11262
11285
|
this.onStreamStopped?.(sessionId);
|
|
11263
11286
|
}
|
|
11264
11287
|
});
|
|
@@ -11315,11 +11338,11 @@ var SessionStreamer = class {
|
|
|
11315
11338
|
}
|
|
11316
11339
|
stream.initializing = false;
|
|
11317
11340
|
stream.initBuffer = [];
|
|
11318
|
-
|
|
11341
|
+
log17.info("started streaming (tmux)", { sessionId, pane: paneTarget });
|
|
11319
11342
|
return true;
|
|
11320
11343
|
} catch (error) {
|
|
11321
11344
|
const message = error instanceof Error ? error.message : String(error);
|
|
11322
|
-
|
|
11345
|
+
log17.warn("failed to start stream", { sessionId, error: message });
|
|
11323
11346
|
return false;
|
|
11324
11347
|
}
|
|
11325
11348
|
}
|
|
@@ -11365,15 +11388,15 @@ var SessionStreamer = class {
|
|
|
11365
11388
|
stream.stopped = true;
|
|
11366
11389
|
this.active.delete(sessionId);
|
|
11367
11390
|
this.pendingStops.delete(sessionId);
|
|
11368
|
-
|
|
11391
|
+
log17.info("session stream ended", { sessionId });
|
|
11369
11392
|
this.onStreamStopped?.(sessionId);
|
|
11370
11393
|
}
|
|
11371
11394
|
});
|
|
11372
|
-
|
|
11395
|
+
log17.info("started streaming (screen)", { sessionId, screen: sessionName });
|
|
11373
11396
|
return true;
|
|
11374
11397
|
} catch (error) {
|
|
11375
11398
|
const message = error instanceof Error ? error.message : String(error);
|
|
11376
|
-
|
|
11399
|
+
log17.warn("failed to start screen stream", { sessionId, error: message });
|
|
11377
11400
|
return false;
|
|
11378
11401
|
}
|
|
11379
11402
|
}
|
|
@@ -11419,15 +11442,15 @@ var SessionStreamer = class {
|
|
|
11419
11442
|
stream.stopped = true;
|
|
11420
11443
|
this.active.delete(sessionId);
|
|
11421
11444
|
this.pendingStops.delete(sessionId);
|
|
11422
|
-
|
|
11445
|
+
log17.info("session stream ended", { sessionId });
|
|
11423
11446
|
this.onStreamStopped?.(sessionId);
|
|
11424
11447
|
}
|
|
11425
11448
|
});
|
|
11426
|
-
|
|
11449
|
+
log17.info("started streaming (zellij)", { sessionId, zellij: sessionName });
|
|
11427
11450
|
return true;
|
|
11428
11451
|
} catch (error) {
|
|
11429
11452
|
const message = error instanceof Error ? error.message : String(error);
|
|
11430
|
-
|
|
11453
|
+
log17.warn("failed to start zellij stream", { sessionId, error: message });
|
|
11431
11454
|
return false;
|
|
11432
11455
|
}
|
|
11433
11456
|
}
|
|
@@ -11438,14 +11461,14 @@ var SessionStreamer = class {
|
|
|
11438
11461
|
const pid = parseProcessPid2(sessionId);
|
|
11439
11462
|
if (!pid) return false;
|
|
11440
11463
|
if (!(0, import_node_fs.existsSync)(`/proc/${pid}`)) {
|
|
11441
|
-
|
|
11464
|
+
log17.warn("process does not exist (no /proc entry)", { pid });
|
|
11442
11465
|
this.sendFn(sessionId, "\r\n[rAgent] Process is no longer running. It will be removed on next sync.\r\n");
|
|
11443
11466
|
return false;
|
|
11444
11467
|
}
|
|
11445
11468
|
try {
|
|
11446
11469
|
const stat = (0, import_node_fs.readFileSync)(`/proc/${pid}/stat`, "utf8");
|
|
11447
11470
|
if (stat.includes(") Z")) {
|
|
11448
|
-
|
|
11471
|
+
log17.warn("process is a zombie", { pid });
|
|
11449
11472
|
this.sendFn(sessionId, "\r\n[rAgent] Process has exited (zombie). It will be removed on next sync.\r\n");
|
|
11450
11473
|
return false;
|
|
11451
11474
|
}
|
|
@@ -11519,14 +11542,14 @@ var SessionStreamer = class {
|
|
|
11519
11542
|
stream.stopped = true;
|
|
11520
11543
|
this.active.delete(sessionId);
|
|
11521
11544
|
this.pendingStops.delete(sessionId);
|
|
11522
|
-
|
|
11545
|
+
log17.info("session stream ended", { sessionId });
|
|
11523
11546
|
this.onStreamStopped?.(sessionId);
|
|
11524
11547
|
});
|
|
11525
|
-
|
|
11548
|
+
log17.info("started streaming (process trace)", { sessionId, pid });
|
|
11526
11549
|
return true;
|
|
11527
11550
|
} catch (error) {
|
|
11528
11551
|
const message = error instanceof Error ? error.message : String(error);
|
|
11529
|
-
|
|
11552
|
+
log17.warn("failed to start process stream", { sessionId, error: message });
|
|
11530
11553
|
return false;
|
|
11531
11554
|
}
|
|
11532
11555
|
}
|
|
@@ -11577,7 +11600,7 @@ var SessionStreamer = class {
|
|
|
11577
11600
|
|
|
11578
11601
|
// src/crypto-channel.ts
|
|
11579
11602
|
var import_node_crypto = require("crypto");
|
|
11580
|
-
var
|
|
11603
|
+
var log18 = createLogger("crypto-channel");
|
|
11581
11604
|
var AAD_BYTES = 4;
|
|
11582
11605
|
function deriveAesKey(sessionKey) {
|
|
11583
11606
|
return Buffer.from(sessionKey.slice(0, 64), "hex");
|
|
@@ -11619,7 +11642,7 @@ function decryptPayload(enc, iv, sessionKey, seq) {
|
|
|
11619
11642
|
return decrypted.toString("utf-8");
|
|
11620
11643
|
} catch (error) {
|
|
11621
11644
|
const message = error instanceof Error ? error.message : String(error);
|
|
11622
|
-
|
|
11645
|
+
log18.warn("decrypt failed", { seq, encBytes: enc.length, error: message });
|
|
11623
11646
|
return null;
|
|
11624
11647
|
}
|
|
11625
11648
|
}
|
|
@@ -11653,7 +11676,7 @@ var SequenceTracker = class {
|
|
|
11653
11676
|
// src/pty.ts
|
|
11654
11677
|
var import_node_child_process3 = require("child_process");
|
|
11655
11678
|
var pty2 = __toESM(require("node-pty"));
|
|
11656
|
-
var
|
|
11679
|
+
var log19 = createLogger("pty");
|
|
11657
11680
|
var MIN_TMUX_COLS = 20;
|
|
11658
11681
|
var MAX_TMUX_COLS = 500;
|
|
11659
11682
|
var MIN_TMUX_ROWS = 5;
|
|
@@ -11801,7 +11824,7 @@ async function sendInputToTmux(sessionId, data) {
|
|
|
11801
11824
|
if (!target) return;
|
|
11802
11825
|
if (!isValidTmuxSessionName(target)) {
|
|
11803
11826
|
const sessionName = target.split(":")[0].split(".")[0];
|
|
11804
|
-
|
|
11827
|
+
log19.warn("invalid tmux session name", { sessionName });
|
|
11805
11828
|
return;
|
|
11806
11829
|
}
|
|
11807
11830
|
try {
|
|
@@ -11832,7 +11855,7 @@ async function sendInputToTmux(sessionId, data) {
|
|
|
11832
11855
|
}
|
|
11833
11856
|
} catch (error) {
|
|
11834
11857
|
const message = error instanceof Error ? error.message : String(error);
|
|
11835
|
-
|
|
11858
|
+
log19.warn("failed to send input", { sessionId, error: message });
|
|
11836
11859
|
}
|
|
11837
11860
|
}
|
|
11838
11861
|
async function resizeTmuxPaneBySessionId(sessionId, cols, rows) {
|
|
@@ -11841,7 +11864,7 @@ async function resizeTmuxPaneBySessionId(sessionId, cols, rows) {
|
|
|
11841
11864
|
if (!target) return false;
|
|
11842
11865
|
if (!isValidTmuxSessionName(target)) {
|
|
11843
11866
|
const sessionName = target.split(":")[0].split(".")[0];
|
|
11844
|
-
|
|
11867
|
+
log19.warn("invalid tmux session name", { sessionName });
|
|
11845
11868
|
return false;
|
|
11846
11869
|
}
|
|
11847
11870
|
if (!Number.isFinite(cols) || !Number.isFinite(rows)) return false;
|
|
@@ -11855,7 +11878,7 @@ async function resizeTmuxPaneBySessionId(sessionId, cols, rows) {
|
|
|
11855
11878
|
(err) => {
|
|
11856
11879
|
if (err) {
|
|
11857
11880
|
const message = err instanceof Error ? err.message : String(err);
|
|
11858
|
-
|
|
11881
|
+
log19.warn("failed to resize tmux pane", { sessionId, error: message });
|
|
11859
11882
|
resolve(false);
|
|
11860
11883
|
return;
|
|
11861
11884
|
}
|
|
@@ -11889,7 +11912,7 @@ async function stopAllDetachedTmuxSessions() {
|
|
|
11889
11912
|
}
|
|
11890
11913
|
|
|
11891
11914
|
// src/shell-manager.ts
|
|
11892
|
-
var
|
|
11915
|
+
var log20 = createLogger("shell");
|
|
11893
11916
|
var ShellManager = class {
|
|
11894
11917
|
ptyProcess = null;
|
|
11895
11918
|
suppressNextRespawn = false;
|
|
@@ -11930,7 +11953,7 @@ var ShellManager = class {
|
|
|
11930
11953
|
} catch (error) {
|
|
11931
11954
|
const message = error instanceof Error ? error.message : String(error);
|
|
11932
11955
|
if (!message.includes("EBADF")) {
|
|
11933
|
-
|
|
11956
|
+
log20.warn("resize failed", { error: message });
|
|
11934
11957
|
}
|
|
11935
11958
|
}
|
|
11936
11959
|
}
|
|
@@ -11946,7 +11969,7 @@ var ShellManager = class {
|
|
|
11946
11969
|
return;
|
|
11947
11970
|
}
|
|
11948
11971
|
if (!this.shouldRun) return;
|
|
11949
|
-
|
|
11972
|
+
log20.warn("shell exited; restarting shell process");
|
|
11950
11973
|
setTimeout(() => {
|
|
11951
11974
|
if (this.shouldRun) this.spawnOrRespawn();
|
|
11952
11975
|
}, 200);
|
|
@@ -12052,7 +12075,7 @@ var InventoryManager = class {
|
|
|
12052
12075
|
|
|
12053
12076
|
// src/connection-manager.ts
|
|
12054
12077
|
var import_ws3 = __toESM(require("ws"));
|
|
12055
|
-
var
|
|
12078
|
+
var log21 = createLogger("connection");
|
|
12056
12079
|
var OPPORTUNISTIC_INVENTORY_SYNC_MS = 1200;
|
|
12057
12080
|
var MIN_INVENTORY_SYNC_GAP_MS = 1500;
|
|
12058
12081
|
var ConnectionManager = class {
|
|
@@ -12084,7 +12107,7 @@ var ConnectionManager = class {
|
|
|
12084
12107
|
tracker = new SequenceTracker();
|
|
12085
12108
|
this.rxSeqByViewer.set(key, tracker);
|
|
12086
12109
|
if (!isValid) {
|
|
12087
|
-
|
|
12110
|
+
log21.warn("rxSeq fell back to _legacy bucket \u2014 portal sent empty viewerId", { typeofViewerId: typeof viewerId });
|
|
12088
12111
|
}
|
|
12089
12112
|
}
|
|
12090
12113
|
return tracker;
|
|
@@ -12170,14 +12193,14 @@ var ConnectionManager = class {
|
|
|
12170
12193
|
try {
|
|
12171
12194
|
this.activeSocket.send(JSON.stringify({ type: "ping" }));
|
|
12172
12195
|
} catch (err) {
|
|
12173
|
-
|
|
12196
|
+
log21.error("failed to send JSON ping", { error: err.message });
|
|
12174
12197
|
}
|
|
12175
12198
|
try {
|
|
12176
12199
|
this.activeSocket.ping();
|
|
12177
12200
|
} catch {
|
|
12178
12201
|
}
|
|
12179
12202
|
this.wsPongTimeout = setTimeout(() => {
|
|
12180
|
-
|
|
12203
|
+
log21.warn("no pong received within 10s; closing stale connection");
|
|
12181
12204
|
try {
|
|
12182
12205
|
this.activeSocket?.terminate();
|
|
12183
12206
|
} catch {
|
|
@@ -12247,7 +12270,7 @@ var ConnectionManager = class {
|
|
|
12247
12270
|
replayBufferedOutput(sendChunk) {
|
|
12248
12271
|
const buffered = this.outputBuffer.drain();
|
|
12249
12272
|
if (buffered.length > 0) {
|
|
12250
|
-
|
|
12273
|
+
log21.info("replaying buffered output chunks", {
|
|
12251
12274
|
chunks: buffered.length,
|
|
12252
12275
|
bytes: buffered.reduce((sum, c) => sum + Buffer.byteLength(c, "utf8"), 0)
|
|
12253
12276
|
});
|
|
@@ -12289,7 +12312,7 @@ var import_child_process6 = require("child_process");
|
|
|
12289
12312
|
var fs5 = __toESM(require("fs"));
|
|
12290
12313
|
var os8 = __toESM(require("os"));
|
|
12291
12314
|
var path4 = __toESM(require("path"));
|
|
12292
|
-
var
|
|
12315
|
+
var log22 = createLogger("service");
|
|
12293
12316
|
function assertConfiguredAgentToken() {
|
|
12294
12317
|
const config = loadConfig();
|
|
12295
12318
|
if (!config.agentToken) {
|
|
@@ -12355,7 +12378,7 @@ async function installSystemdService(opts = {}) {
|
|
|
12355
12378
|
await runSystemctlUser(["restart", SERVICE_NAME]);
|
|
12356
12379
|
}
|
|
12357
12380
|
saveConfigPatch({ serviceBackend: "systemd" });
|
|
12358
|
-
|
|
12381
|
+
log22.info("installed systemd user service", { path: SERVICE_FILE });
|
|
12359
12382
|
}
|
|
12360
12383
|
function readFallbackPid() {
|
|
12361
12384
|
try {
|
|
@@ -12394,7 +12417,7 @@ async function startPidfileService() {
|
|
|
12394
12417
|
assertConfiguredAgentToken();
|
|
12395
12418
|
const existingPid = readFallbackPid();
|
|
12396
12419
|
if (existingPid && isProcessRunning(existingPid)) {
|
|
12397
|
-
|
|
12420
|
+
log22.info("service already running", { pid: existingPid });
|
|
12398
12421
|
return;
|
|
12399
12422
|
}
|
|
12400
12423
|
ensureConfigDir();
|
|
@@ -12411,8 +12434,8 @@ async function startPidfileService() {
|
|
|
12411
12434
|
fs5.writeFileSync(FALLBACK_PID_FILE, `${child.pid}
|
|
12412
12435
|
`, "utf8");
|
|
12413
12436
|
saveConfigPatch({ serviceBackend: "pidfile" });
|
|
12414
|
-
|
|
12415
|
-
|
|
12437
|
+
log22.info("started fallback background service", { pid: child.pid });
|
|
12438
|
+
log22.info("logs", { path: FALLBACK_LOG_FILE });
|
|
12416
12439
|
}
|
|
12417
12440
|
async function stopPidfileService() {
|
|
12418
12441
|
const pid = readFallbackPid();
|
|
@@ -12421,7 +12444,7 @@ async function stopPidfileService() {
|
|
|
12421
12444
|
fs5.unlinkSync(FALLBACK_PID_FILE);
|
|
12422
12445
|
} catch {
|
|
12423
12446
|
}
|
|
12424
|
-
|
|
12447
|
+
log22.info("service is not running");
|
|
12425
12448
|
return;
|
|
12426
12449
|
}
|
|
12427
12450
|
process.kill(pid, "SIGTERM");
|
|
@@ -12436,7 +12459,7 @@ async function stopPidfileService() {
|
|
|
12436
12459
|
fs5.unlinkSync(FALLBACK_PID_FILE);
|
|
12437
12460
|
} catch {
|
|
12438
12461
|
}
|
|
12439
|
-
|
|
12462
|
+
log22.info("stopped fallback background service", { pid });
|
|
12440
12463
|
}
|
|
12441
12464
|
async function ensureServiceInstalled(opts = {}) {
|
|
12442
12465
|
const wantsSystemd = await canUseSystemdUser();
|
|
@@ -12448,7 +12471,7 @@ async function ensureServiceInstalled(opts = {}) {
|
|
|
12448
12471
|
if (opts.start) {
|
|
12449
12472
|
await startPidfileService();
|
|
12450
12473
|
} else {
|
|
12451
|
-
|
|
12474
|
+
log22.info("systemd user manager unavailable; using fallback pidfile backend");
|
|
12452
12475
|
}
|
|
12453
12476
|
return "pidfile";
|
|
12454
12477
|
}
|
|
@@ -12456,7 +12479,7 @@ async function startService() {
|
|
|
12456
12479
|
const backend = getConfiguredServiceBackend();
|
|
12457
12480
|
if (backend === "systemd") {
|
|
12458
12481
|
await runSystemctlUser(["start", SERVICE_NAME]);
|
|
12459
|
-
|
|
12482
|
+
log22.info("started service via systemd");
|
|
12460
12483
|
return;
|
|
12461
12484
|
}
|
|
12462
12485
|
if (backend === "pidfile") {
|
|
@@ -12469,7 +12492,7 @@ async function stopService() {
|
|
|
12469
12492
|
const backend = getConfiguredServiceBackend();
|
|
12470
12493
|
if (backend === "systemd") {
|
|
12471
12494
|
await runSystemctlUser(["stop", SERVICE_NAME]);
|
|
12472
|
-
|
|
12495
|
+
log22.info("stopped service via systemd");
|
|
12473
12496
|
return;
|
|
12474
12497
|
}
|
|
12475
12498
|
await stopPidfileService();
|
|
@@ -12487,7 +12510,7 @@ function killStaleAgentProcesses() {
|
|
|
12487
12510
|
try {
|
|
12488
12511
|
process.kill(pid, 0);
|
|
12489
12512
|
process.kill(pid, "SIGTERM");
|
|
12490
|
-
|
|
12513
|
+
log22.info("stopped stale agent process", { pid });
|
|
12491
12514
|
} catch {
|
|
12492
12515
|
}
|
|
12493
12516
|
fs5.unlinkSync(pidPath);
|
|
@@ -12502,7 +12525,7 @@ async function restartService() {
|
|
|
12502
12525
|
killStaleAgentProcesses();
|
|
12503
12526
|
if (backend === "systemd") {
|
|
12504
12527
|
await runSystemctlUser(["restart", SERVICE_NAME]);
|
|
12505
|
-
|
|
12528
|
+
log22.info("restarted service via systemd");
|
|
12506
12529
|
return;
|
|
12507
12530
|
}
|
|
12508
12531
|
await stopPidfileService();
|
|
@@ -12518,17 +12541,17 @@ async function printServiceStatus() {
|
|
|
12518
12541
|
if (status) {
|
|
12519
12542
|
process.stdout.write(status);
|
|
12520
12543
|
} else {
|
|
12521
|
-
|
|
12544
|
+
log22.info("systemd service is not installed or has no status");
|
|
12522
12545
|
}
|
|
12523
12546
|
return;
|
|
12524
12547
|
}
|
|
12525
12548
|
const pid = readFallbackPid();
|
|
12526
12549
|
if (pid && isProcessRunning(pid)) {
|
|
12527
|
-
|
|
12528
|
-
|
|
12550
|
+
log22.info("fallback service running", { pid });
|
|
12551
|
+
log22.info("logs", { path: FALLBACK_LOG_FILE });
|
|
12529
12552
|
return;
|
|
12530
12553
|
}
|
|
12531
|
-
|
|
12554
|
+
log22.info("service is not running");
|
|
12532
12555
|
}
|
|
12533
12556
|
async function printServiceLogs(opts) {
|
|
12534
12557
|
const lines = Number.parseInt(String(opts.lines || 100), 10) || 100;
|
|
@@ -12554,7 +12577,7 @@ async function printServiceLogs(opts) {
|
|
|
12554
12577
|
return;
|
|
12555
12578
|
}
|
|
12556
12579
|
if (!fs5.existsSync(FALLBACK_LOG_FILE)) {
|
|
12557
|
-
|
|
12580
|
+
log22.info("no log file found", { path: FALLBACK_LOG_FILE });
|
|
12558
12581
|
return;
|
|
12559
12582
|
}
|
|
12560
12583
|
if (follow) {
|
|
@@ -12586,7 +12609,7 @@ async function uninstallService() {
|
|
|
12586
12609
|
const config = loadConfig();
|
|
12587
12610
|
delete config.serviceBackend;
|
|
12588
12611
|
saveConfig(config);
|
|
12589
|
-
|
|
12612
|
+
log22.info("service uninstalled");
|
|
12590
12613
|
}
|
|
12591
12614
|
function requestStopSelfService() {
|
|
12592
12615
|
const backend = getConfiguredServiceBackend();
|
|
@@ -12937,7 +12960,7 @@ async function installLatestCliFromNpm() {
|
|
|
12937
12960
|
}
|
|
12938
12961
|
|
|
12939
12962
|
// src/control-dispatcher.ts
|
|
12940
|
-
var
|
|
12963
|
+
var log23 = createLogger("control");
|
|
12941
12964
|
var TMUX_RESYNC_DEBOUNCE_MS = 150;
|
|
12942
12965
|
var TMUX_INPUT_COALESCE_MS = 4;
|
|
12943
12966
|
function collectProcessTreePids(rootPid, visited = /* @__PURE__ */ new Set()) {
|
|
@@ -13009,7 +13032,7 @@ var ControlDispatcher = class {
|
|
|
13009
13032
|
/** Arrow form so callers can pass it directly without `.bind(this)`. */
|
|
13010
13033
|
emitNativeOps = (sessionId, ops, seq) => {
|
|
13011
13034
|
if (!this.sendNativeOps) {
|
|
13012
|
-
|
|
13035
|
+
log23.warn("native-stream ops produced but no transport sink installed", {
|
|
13013
13036
|
sessionId,
|
|
13014
13037
|
opCount: ops.length
|
|
13015
13038
|
});
|
|
@@ -13078,11 +13101,11 @@ var ControlDispatcher = class {
|
|
|
13078
13101
|
if (!sessionKey) return true;
|
|
13079
13102
|
const receivedHmac = payload.hmac;
|
|
13080
13103
|
if (typeof receivedHmac !== "string") {
|
|
13081
|
-
|
|
13104
|
+
log23.warn("control message missing HMAC signature");
|
|
13082
13105
|
return false;
|
|
13083
13106
|
}
|
|
13084
13107
|
if (!/^[0-9a-f]{64}$/i.test(receivedHmac)) {
|
|
13085
|
-
|
|
13108
|
+
log23.warn("control message has malformed HMAC signature");
|
|
13086
13109
|
return false;
|
|
13087
13110
|
}
|
|
13088
13111
|
const { hmac: _, ...payloadWithoutHmac } = payload;
|
|
@@ -13110,7 +13133,7 @@ var ControlDispatcher = class {
|
|
|
13110
13133
|
]);
|
|
13111
13134
|
if (dangerousActions.has(action) && this.connection.sessionKey && payload._verifiedEncrypted !== true) {
|
|
13112
13135
|
if (!this.verifyMessageHmac(payload)) {
|
|
13113
|
-
|
|
13136
|
+
log23.warn("rejecting control action \u2014 HMAC verification failed", { action });
|
|
13114
13137
|
return;
|
|
13115
13138
|
}
|
|
13116
13139
|
}
|
|
@@ -13156,10 +13179,10 @@ var ControlDispatcher = class {
|
|
|
13156
13179
|
this.transcriptWatcher?.disableMarkdown(sessionId);
|
|
13157
13180
|
try {
|
|
13158
13181
|
await stopTmuxPaneBySessionId(sessionId);
|
|
13159
|
-
|
|
13182
|
+
log23.info("closed remote session", { sessionId });
|
|
13160
13183
|
} catch (error) {
|
|
13161
13184
|
const message = error instanceof Error ? error.message : String(error);
|
|
13162
|
-
|
|
13185
|
+
log23.warn("failed to close session", { sessionId, error: message });
|
|
13163
13186
|
}
|
|
13164
13187
|
await delay(300);
|
|
13165
13188
|
await this.syncInventory(true);
|
|
@@ -13170,7 +13193,7 @@ var ControlDispatcher = class {
|
|
|
13170
13193
|
{
|
|
13171
13194
|
const pid = parseProcessPid2(sessionId);
|
|
13172
13195
|
if (pid === null) {
|
|
13173
|
-
|
|
13196
|
+
log23.warn("kill-process: could not parse PID", { sessionId });
|
|
13174
13197
|
return;
|
|
13175
13198
|
}
|
|
13176
13199
|
this.transcriptWatcher?.disableMarkdown(sessionId);
|
|
@@ -13186,11 +13209,11 @@ var ControlDispatcher = class {
|
|
|
13186
13209
|
const code = error.code;
|
|
13187
13210
|
if (code !== "ESRCH") {
|
|
13188
13211
|
const message = error instanceof Error ? error.message : String(error);
|
|
13189
|
-
|
|
13212
|
+
log23.warn("failed to send SIGTERM", { pid: targetPid, error: message });
|
|
13190
13213
|
}
|
|
13191
13214
|
}
|
|
13192
13215
|
}
|
|
13193
|
-
|
|
13216
|
+
log23.info("sent SIGTERM to processes", { signaled, sessionId });
|
|
13194
13217
|
await new Promise((resolve) => setTimeout(resolve, 1500));
|
|
13195
13218
|
for (const targetPid of processTree) {
|
|
13196
13219
|
if (!isProcessAlive(targetPid)) continue;
|
|
@@ -13200,13 +13223,13 @@ var ControlDispatcher = class {
|
|
|
13200
13223
|
const code = error.code;
|
|
13201
13224
|
if (code !== "ESRCH") {
|
|
13202
13225
|
const message = error instanceof Error ? error.message : String(error);
|
|
13203
|
-
|
|
13226
|
+
log23.warn("failed to send SIGKILL", { pid: targetPid, error: message });
|
|
13204
13227
|
}
|
|
13205
13228
|
}
|
|
13206
13229
|
}
|
|
13207
13230
|
} catch (error) {
|
|
13208
13231
|
const message = error instanceof Error ? error.message : String(error);
|
|
13209
|
-
|
|
13232
|
+
log23.warn("failed to kill session", { sessionId, error: message });
|
|
13210
13233
|
}
|
|
13211
13234
|
await this.syncInventory(true);
|
|
13212
13235
|
}
|
|
@@ -13221,7 +13244,7 @@ var ControlDispatcher = class {
|
|
|
13221
13244
|
}
|
|
13222
13245
|
}
|
|
13223
13246
|
const killed = await stopAllDetachedTmuxSessions();
|
|
13224
|
-
|
|
13247
|
+
log23.info("killed detached tmux sessions", { count: killed });
|
|
13225
13248
|
await delay(300);
|
|
13226
13249
|
await this.syncInventory(true);
|
|
13227
13250
|
return;
|
|
@@ -13261,18 +13284,18 @@ var ControlDispatcher = class {
|
|
|
13261
13284
|
const fileName = typeof payload.fileName === "string" ? payload.fileName : "attachment";
|
|
13262
13285
|
const contentBase64 = typeof payload.contentBase64 === "string" ? payload.contentBase64 : "";
|
|
13263
13286
|
if (!contentBase64 || contentBase64.length > 8 * 1024 * 1024) {
|
|
13264
|
-
|
|
13287
|
+
log23.warn("rejecting attachment upload \u2014 invalid size", { sessionId, fileName });
|
|
13265
13288
|
return;
|
|
13266
13289
|
}
|
|
13267
13290
|
let content;
|
|
13268
13291
|
try {
|
|
13269
13292
|
content = Buffer.from(contentBase64, "base64");
|
|
13270
13293
|
} catch {
|
|
13271
|
-
|
|
13294
|
+
log23.warn("rejecting attachment upload \u2014 invalid base64", { sessionId, fileName });
|
|
13272
13295
|
return;
|
|
13273
13296
|
}
|
|
13274
13297
|
if (content.length === 0 || content.length > 6 * 1024 * 1024) {
|
|
13275
|
-
|
|
13298
|
+
log23.warn("rejecting attachment upload \u2014 decoded size out of bounds", {
|
|
13276
13299
|
sessionId,
|
|
13277
13300
|
fileName,
|
|
13278
13301
|
bytes: content.length
|
|
@@ -13283,7 +13306,7 @@ var ControlDispatcher = class {
|
|
|
13283
13306
|
fs6.mkdirSync(uploadDir, { recursive: true, mode: 448 });
|
|
13284
13307
|
const writtenPath = path5.join(uploadDir, `${Date.now()}-${safeAttachmentName(fileName)}`);
|
|
13285
13308
|
fs6.writeFileSync(writtenPath, content, { mode: 384 });
|
|
13286
|
-
|
|
13309
|
+
log23.info("attachment uploaded", { sessionId, path: writtenPath, bytes: content.length });
|
|
13287
13310
|
this.handleInput(writtenPath, sessionId);
|
|
13288
13311
|
}
|
|
13289
13312
|
/** Handle input routing to the correct target. */
|
|
@@ -13346,7 +13369,7 @@ var ControlDispatcher = class {
|
|
|
13346
13369
|
this.tmuxResizeState.delete(sessionId);
|
|
13347
13370
|
}
|
|
13348
13371
|
const message = error instanceof Error ? error.message : String(error);
|
|
13349
|
-
|
|
13372
|
+
log23.warn("tmux resize failed", { sessionId, error: message });
|
|
13350
13373
|
});
|
|
13351
13374
|
} else if (sessionId.startsWith("screen:") || sessionId.startsWith("zellij:")) {
|
|
13352
13375
|
this.streamer.resize(sessionId, cols, rows);
|
|
@@ -13354,30 +13377,30 @@ var ControlDispatcher = class {
|
|
|
13354
13377
|
}
|
|
13355
13378
|
/** Update the installed connector package, then restart when a service backend exists. */
|
|
13356
13379
|
async updateAgentFromNpm() {
|
|
13357
|
-
|
|
13380
|
+
log23.info("updating ragent CLI", { current: CURRENT_VERSION });
|
|
13358
13381
|
try {
|
|
13359
13382
|
await installLatestCliFromNpm();
|
|
13360
13383
|
} catch (error) {
|
|
13361
13384
|
const message = error instanceof Error ? error.message : String(error);
|
|
13362
|
-
|
|
13385
|
+
log23.error("failed to update ragent CLI", { error: message });
|
|
13363
13386
|
return;
|
|
13364
13387
|
}
|
|
13365
13388
|
const backend = getConfiguredServiceBackend();
|
|
13366
13389
|
if (!backend) {
|
|
13367
|
-
|
|
13390
|
+
log23.warn("ragent CLI updated, but no service backend is configured; restart the connector manually to use the new version");
|
|
13368
13391
|
return;
|
|
13369
13392
|
}
|
|
13370
|
-
|
|
13393
|
+
log23.info("ragent CLI updated; restarting connector service", { backend });
|
|
13371
13394
|
requestRestartSelfService();
|
|
13372
13395
|
}
|
|
13373
13396
|
/** Handle provision request from dashboard. */
|
|
13374
13397
|
async handleProvision(payload) {
|
|
13375
13398
|
const provReq = validateProvisionRequest(payload);
|
|
13376
13399
|
if (!provReq) {
|
|
13377
|
-
|
|
13400
|
+
log23.warn("rejecting provision request \u2014 malformed payload");
|
|
13378
13401
|
return;
|
|
13379
13402
|
}
|
|
13380
|
-
|
|
13403
|
+
log23.info("provision request", { name: provReq.manifest.name, provisionId: provReq.provisionId });
|
|
13381
13404
|
const sendProgress = (progress) => {
|
|
13382
13405
|
const ws = this.connection.activeSocket;
|
|
13383
13406
|
if (ws && ws.readyState === import_ws4.default.OPEN && this.connection.activeGroups.registryGroup) {
|
|
@@ -13425,7 +13448,7 @@ var ControlDispatcher = class {
|
|
|
13425
13448
|
status: "unavailable",
|
|
13426
13449
|
reason: result.reason
|
|
13427
13450
|
});
|
|
13428
|
-
|
|
13451
|
+
log23.info("could not enable markdown", { sessionId, reason: result.reason });
|
|
13429
13452
|
} else {
|
|
13430
13453
|
this.sendMarkdownStatus({
|
|
13431
13454
|
type: "markdown",
|
|
@@ -13459,24 +13482,24 @@ var ControlDispatcher = class {
|
|
|
13459
13482
|
const sessionName = typeof payload?.sessionName === "string" && payload.sessionName.trim().length > 0 ? payload.sessionName.trim() : `agent-${Date.now().toString(36)}`;
|
|
13460
13483
|
const cmd = typeof payload?.command === "string" && payload.command.trim().length > 0 ? payload.command.trim() : null;
|
|
13461
13484
|
if (!cmd) {
|
|
13462
|
-
|
|
13485
|
+
log23.warn("start-agent: no command provided, ignoring");
|
|
13463
13486
|
return;
|
|
13464
13487
|
}
|
|
13465
13488
|
if (sessionName.length > 128 || !/^[a-zA-Z0-9_-]+$/.test(sessionName)) {
|
|
13466
|
-
|
|
13489
|
+
log23.warn("start-agent: invalid session name, ignoring");
|
|
13467
13490
|
return;
|
|
13468
13491
|
}
|
|
13469
13492
|
const danger = detectDangerousCommand(cmd);
|
|
13470
13493
|
if (danger) {
|
|
13471
13494
|
if (!this._approvalEnforcer) {
|
|
13472
|
-
|
|
13495
|
+
log23.warn("start-agent: rejected dangerous command (no approval enforcer)", {
|
|
13473
13496
|
category: danger.category,
|
|
13474
13497
|
pattern: danger.pattern,
|
|
13475
13498
|
command: cmd
|
|
13476
13499
|
});
|
|
13477
13500
|
return;
|
|
13478
13501
|
}
|
|
13479
|
-
|
|
13502
|
+
log23.info("start-agent: dangerous command \u2014 awaiting approval", {
|
|
13480
13503
|
category: danger.category,
|
|
13481
13504
|
pattern: danger.pattern,
|
|
13482
13505
|
sessionName
|
|
@@ -13487,28 +13510,28 @@ var ControlDispatcher = class {
|
|
|
13487
13510
|
sessionName
|
|
13488
13511
|
);
|
|
13489
13512
|
if (response === null) {
|
|
13490
|
-
|
|
13513
|
+
log23.warn("start-agent: dangerous command not covered by approval policy, rejecting", {
|
|
13491
13514
|
category: danger.category,
|
|
13492
13515
|
command: cmd
|
|
13493
13516
|
});
|
|
13494
13517
|
return;
|
|
13495
13518
|
}
|
|
13496
13519
|
if (response.type !== "approved") {
|
|
13497
|
-
|
|
13520
|
+
log23.warn("start-agent: dangerous command denied or timed out", {
|
|
13498
13521
|
category: danger.category,
|
|
13499
13522
|
sessionName,
|
|
13500
13523
|
responseType: response.type
|
|
13501
13524
|
});
|
|
13502
13525
|
return;
|
|
13503
13526
|
}
|
|
13504
|
-
|
|
13527
|
+
log23.info("start-agent: dangerous command approved", {
|
|
13505
13528
|
category: danger.category,
|
|
13506
13529
|
sessionName
|
|
13507
13530
|
});
|
|
13508
13531
|
}
|
|
13509
13532
|
const rawWorkingDir = typeof payload?.workingDir === "string" && payload.workingDir.trim().length > 0 ? payload.workingDir.trim() : void 0;
|
|
13510
13533
|
if (rawWorkingDir !== void 0 && !isSafeWorkingDir(rawWorkingDir)) {
|
|
13511
|
-
|
|
13534
|
+
log23.warn("start-agent: rejected unsafe workingDir", { workingDir: rawWorkingDir });
|
|
13512
13535
|
return;
|
|
13513
13536
|
}
|
|
13514
13537
|
const workingDir = rawWorkingDir;
|
|
@@ -13557,7 +13580,7 @@ var ControlDispatcher = class {
|
|
|
13557
13580
|
}
|
|
13558
13581
|
if (outcome) {
|
|
13559
13582
|
if (outcome.ok) {
|
|
13560
|
-
|
|
13583
|
+
log23.info("start-agent: launched native-stream", {
|
|
13561
13584
|
sessionId: outcome.sessionId,
|
|
13562
13585
|
kind: outcome.kind,
|
|
13563
13586
|
source: outcome.transcriptSource
|
|
@@ -13565,7 +13588,7 @@ var ControlDispatcher = class {
|
|
|
13565
13588
|
await this.syncInventory(true);
|
|
13566
13589
|
return;
|
|
13567
13590
|
}
|
|
13568
|
-
|
|
13591
|
+
log23.info(
|
|
13569
13592
|
"start-agent: native-stream unavailable; falling through to tmux file-watch",
|
|
13570
13593
|
{ reason: outcome.reason, kind: outcome.kind }
|
|
13571
13594
|
);
|
|
@@ -13585,11 +13608,11 @@ var ControlDispatcher = class {
|
|
|
13585
13608
|
let tmuxLaunchOk = false;
|
|
13586
13609
|
try {
|
|
13587
13610
|
(0, import_child_process8.execFileSync)("tmux", tmuxArgs, { stdio: "ignore" });
|
|
13588
|
-
|
|
13611
|
+
log23.info("started agent session", { sessionName, command: cmd });
|
|
13589
13612
|
tmuxLaunchOk = true;
|
|
13590
13613
|
} catch (error) {
|
|
13591
13614
|
const message = error instanceof Error ? error.message : String(error);
|
|
13592
|
-
|
|
13615
|
+
log23.error("failed to start agent session", { sessionName, error: message });
|
|
13593
13616
|
}
|
|
13594
13617
|
if (tmuxLaunchOk && pendingFallbackOutcome && pendingFallbackOutcome.userMessage) {
|
|
13595
13618
|
const tmuxSessionId = this.discoverNewlyCreatedPaneId(sessionName);
|
|
@@ -13628,7 +13651,7 @@ var ControlDispatcher = class {
|
|
|
13628
13651
|
return `tmux:${sessionName}:${info}`;
|
|
13629
13652
|
}
|
|
13630
13653
|
} catch (err) {
|
|
13631
|
-
|
|
13654
|
+
log23.warn("display-message failed; status block target may not bind", {
|
|
13632
13655
|
sessionName,
|
|
13633
13656
|
err: err instanceof Error ? err.message : String(err)
|
|
13634
13657
|
});
|
|
@@ -13896,7 +13919,7 @@ function isGrantExpired(grant) {
|
|
|
13896
13919
|
}
|
|
13897
13920
|
|
|
13898
13921
|
// src/approval-enforcer.ts
|
|
13899
|
-
var
|
|
13922
|
+
var log24 = createLogger("approval");
|
|
13900
13923
|
var ApprovalEnforcer = class {
|
|
13901
13924
|
policy;
|
|
13902
13925
|
secret;
|
|
@@ -13920,7 +13943,7 @@ var ApprovalEnforcer = class {
|
|
|
13920
13943
|
async checkCommand(command, hostId, sessionId) {
|
|
13921
13944
|
const request = matchPolicy(command, this.policy, hostId, sessionId);
|
|
13922
13945
|
if (!request) return null;
|
|
13923
|
-
|
|
13946
|
+
log24.info("approval pending", {
|
|
13924
13947
|
action: request.action,
|
|
13925
13948
|
severity: request.severity,
|
|
13926
13949
|
requestId: request.requestId
|
|
@@ -13929,7 +13952,7 @@ var ApprovalEnforcer = class {
|
|
|
13929
13952
|
const timer = setTimeout(() => {
|
|
13930
13953
|
this.pendingRequests.delete(request.requestId);
|
|
13931
13954
|
const response = { type: "timeout" };
|
|
13932
|
-
|
|
13955
|
+
log24.info("approval timeout", {
|
|
13933
13956
|
action: request.action,
|
|
13934
13957
|
severity: request.severity,
|
|
13935
13958
|
requestId: request.requestId
|
|
@@ -13951,7 +13974,7 @@ var ApprovalEnforcer = class {
|
|
|
13951
13974
|
* {@link handleResponse}.
|
|
13952
13975
|
*/
|
|
13953
13976
|
async requestApproval(request) {
|
|
13954
|
-
|
|
13977
|
+
log24.info("approval pending (direct)", {
|
|
13955
13978
|
action: request.action,
|
|
13956
13979
|
severity: request.severity,
|
|
13957
13980
|
requestId: request.requestId
|
|
@@ -13960,7 +13983,7 @@ var ApprovalEnforcer = class {
|
|
|
13960
13983
|
const timer = setTimeout(() => {
|
|
13961
13984
|
this.pendingRequests.delete(request.requestId);
|
|
13962
13985
|
const response = { type: "timeout" };
|
|
13963
|
-
|
|
13986
|
+
log24.info("approval timeout (direct)", {
|
|
13964
13987
|
action: request.action,
|
|
13965
13988
|
severity: request.severity,
|
|
13966
13989
|
requestId: request.requestId
|
|
@@ -13980,7 +14003,7 @@ var ApprovalEnforcer = class {
|
|
|
13980
14003
|
if (!requestId) return;
|
|
13981
14004
|
const pending = this.pendingRequests.get(requestId);
|
|
13982
14005
|
if (!pending) {
|
|
13983
|
-
|
|
14006
|
+
log24.warn("received response for unknown approval request", { requestId });
|
|
13984
14007
|
return;
|
|
13985
14008
|
}
|
|
13986
14009
|
if (response.type === "approved") {
|
|
@@ -13989,7 +14012,7 @@ var ApprovalEnforcer = class {
|
|
|
13989
14012
|
pending.request
|
|
13990
14013
|
);
|
|
13991
14014
|
if (bindingMismatch) {
|
|
13992
|
-
|
|
14015
|
+
log24.info("approval rejected (grant binding mismatch)", {
|
|
13993
14016
|
action: pending.request.action,
|
|
13994
14017
|
severity: pending.request.severity,
|
|
13995
14018
|
requestId,
|
|
@@ -14009,7 +14032,7 @@ var ApprovalEnforcer = class {
|
|
|
14009
14032
|
return;
|
|
14010
14033
|
}
|
|
14011
14034
|
if (!this.acceptGrant(response.grant)) {
|
|
14012
|
-
|
|
14035
|
+
log24.info("approval rejected (invalid grant)", {
|
|
14013
14036
|
action: pending.request.action,
|
|
14014
14037
|
severity: pending.request.severity,
|
|
14015
14038
|
requestId
|
|
@@ -14019,13 +14042,13 @@ var ApprovalEnforcer = class {
|
|
|
14019
14042
|
pending.resolve({ type: "denied", denial: { requestId, deniedBy: "system", deniedAt: (/* @__PURE__ */ new Date()).toISOString(), reason: "Invalid or expired grant" } });
|
|
14020
14043
|
return;
|
|
14021
14044
|
}
|
|
14022
|
-
|
|
14045
|
+
log24.info("approval approved", {
|
|
14023
14046
|
action: pending.request.action,
|
|
14024
14047
|
severity: pending.request.severity,
|
|
14025
14048
|
requestId
|
|
14026
14049
|
});
|
|
14027
14050
|
} else if (response.type === "denied") {
|
|
14028
|
-
|
|
14051
|
+
log24.info("approval denied", {
|
|
14029
14052
|
action: pending.request.action,
|
|
14030
14053
|
severity: pending.request.severity,
|
|
14031
14054
|
requestId,
|
|
@@ -14066,7 +14089,7 @@ var ApprovalEnforcer = class {
|
|
|
14066
14089
|
for (const [id, entry] of this.pendingRequests) {
|
|
14067
14090
|
clearTimeout(entry.timer);
|
|
14068
14091
|
this.pendingRequests.delete(id);
|
|
14069
|
-
|
|
14092
|
+
log24.info("approval stopped", {
|
|
14070
14093
|
action: entry.request.action,
|
|
14071
14094
|
severity: entry.request.severity,
|
|
14072
14095
|
requestId: id
|
|
@@ -14102,7 +14125,7 @@ var ApprovalEnforcer = class {
|
|
|
14102
14125
|
if (entry.request.sessionId !== sessionId) continue;
|
|
14103
14126
|
clearTimeout(entry.timer);
|
|
14104
14127
|
this.pendingRequests.delete(id);
|
|
14105
|
-
|
|
14128
|
+
log24.info("approval cancelled (session torn down)", {
|
|
14106
14129
|
action: entry.request.action,
|
|
14107
14130
|
severity: entry.request.severity,
|
|
14108
14131
|
requestId: id,
|
|
@@ -14157,7 +14180,7 @@ var ApprovalEnforcer = class {
|
|
|
14157
14180
|
};
|
|
14158
14181
|
|
|
14159
14182
|
// src/agent.ts
|
|
14160
|
-
var
|
|
14183
|
+
var log25 = createLogger("agent");
|
|
14161
14184
|
var MAX_TRANSPORT_CHUNK_BYTES = 24 * 1024;
|
|
14162
14185
|
function pidFilePath(hostId) {
|
|
14163
14186
|
return path6.join(CONFIG_DIR, `agent-${hostId}.pid`);
|
|
@@ -14242,15 +14265,15 @@ async function runAgent(rawOptions) {
|
|
|
14242
14265
|
const config = loadConfig();
|
|
14243
14266
|
if (config.redaction?.enabled === false || rawOptions.redact === false) {
|
|
14244
14267
|
setRedactionEnabled(false);
|
|
14245
|
-
|
|
14268
|
+
log25.info("secret redaction disabled");
|
|
14246
14269
|
}
|
|
14247
|
-
|
|
14248
|
-
|
|
14270
|
+
log25.info("connector started", { hostName: options.hostName, hostId: options.hostId });
|
|
14271
|
+
log25.info("portal", { portal: options.portal });
|
|
14249
14272
|
try {
|
|
14250
14273
|
const existingSessions = await collectSessionInventory(options.hostId, options.command);
|
|
14251
14274
|
const tmuxCount = existingSessions.filter((s) => s.type === "tmux").length;
|
|
14252
14275
|
if (tmuxCount > 0) {
|
|
14253
|
-
|
|
14276
|
+
log25.info("found existing tmux sessions", { count: tmuxCount });
|
|
14254
14277
|
}
|
|
14255
14278
|
} catch {
|
|
14256
14279
|
}
|
|
@@ -14484,7 +14507,7 @@ async function runAgent(rawOptions) {
|
|
|
14484
14507
|
dispatcher.setApprovalEnforcer(null);
|
|
14485
14508
|
}
|
|
14486
14509
|
ws.on("open", async () => {
|
|
14487
|
-
|
|
14510
|
+
log25.info("connector connected to relay");
|
|
14488
14511
|
conn.resetReconnectDelay();
|
|
14489
14512
|
ws.send(JSON.stringify({ type: "joinGroup", group: groups.privateGroup }));
|
|
14490
14513
|
ws.send(JSON.stringify({ type: "joinGroup", group: groups.registryGroup }));
|
|
@@ -14552,9 +14575,9 @@ async function runAgent(rawOptions) {
|
|
|
14552
14575
|
const inSeq = typeof payload.seq === "number" ? payload.seq : null;
|
|
14553
14576
|
const rxSeq = conn.getRxSeqForViewer(payload.viewerId);
|
|
14554
14577
|
if (inSeq === null) {
|
|
14555
|
-
|
|
14578
|
+
log25.warn("rejecting encrypted input frame without seq (#310)");
|
|
14556
14579
|
} else if (!rxSeq.accept(inSeq)) {
|
|
14557
|
-
|
|
14580
|
+
log25.warn("rejecting replayed/out-of-order input", {
|
|
14558
14581
|
seq: inSeq,
|
|
14559
14582
|
lastSeen: rxSeq.lastSeq,
|
|
14560
14583
|
viewerId: typeof payload.viewerId === "string" ? payload.viewerId : "_legacy"
|
|
@@ -14567,13 +14590,13 @@ async function runAgent(rawOptions) {
|
|
|
14567
14590
|
inSeq
|
|
14568
14591
|
);
|
|
14569
14592
|
if (inputData === null) {
|
|
14570
|
-
|
|
14593
|
+
log25.warn("failed to decrypt input \u2014 ignoring");
|
|
14571
14594
|
}
|
|
14572
14595
|
}
|
|
14573
14596
|
} else if (typeof payload.data === "string" && !conn.sessionKey) {
|
|
14574
14597
|
inputData = payload.data;
|
|
14575
14598
|
} else if (conn.sessionKey) {
|
|
14576
|
-
|
|
14599
|
+
log25.warn("rejecting plaintext input frame while encrypted channel is active");
|
|
14577
14600
|
}
|
|
14578
14601
|
if (inputData !== null) {
|
|
14579
14602
|
const sid = typeof payload.sessionId === "string" ? payload.sessionId.trim() : "";
|
|
@@ -14581,7 +14604,7 @@ async function runAgent(rawOptions) {
|
|
|
14581
14604
|
}
|
|
14582
14605
|
} else if (payload.type === "resize" && Number.isInteger(payload.cols) && Number.isInteger(payload.rows)) {
|
|
14583
14606
|
if (conn.sessionKey && !dispatcher.verifyMessageHmac(payload)) {
|
|
14584
|
-
|
|
14607
|
+
log25.warn("rejecting resize frame \u2014 HMAC verification failed");
|
|
14585
14608
|
return;
|
|
14586
14609
|
}
|
|
14587
14610
|
const sid = typeof payload.sessionId === "string" ? payload.sessionId.trim() : "";
|
|
@@ -14593,11 +14616,11 @@ async function runAgent(rawOptions) {
|
|
|
14593
14616
|
const inSeq = typeof payload.seq === "number" ? payload.seq : null;
|
|
14594
14617
|
const rxSeq = conn.getRxSeqForViewer(payload.viewerId);
|
|
14595
14618
|
if (inSeq === null) {
|
|
14596
|
-
|
|
14619
|
+
log25.warn("rejecting encrypted control frame without seq");
|
|
14597
14620
|
return;
|
|
14598
14621
|
}
|
|
14599
14622
|
if (!rxSeq.accept(inSeq)) {
|
|
14600
|
-
|
|
14623
|
+
log25.warn("rejecting replayed/out-of-order control", {
|
|
14601
14624
|
seq: inSeq,
|
|
14602
14625
|
lastSeen: rxSeq.lastSeq,
|
|
14603
14626
|
viewerId: typeof payload.viewerId === "string" ? payload.viewerId : "_legacy"
|
|
@@ -14611,7 +14634,7 @@ async function runAgent(rawOptions) {
|
|
|
14611
14634
|
inSeq
|
|
14612
14635
|
);
|
|
14613
14636
|
if (decrypted === null) {
|
|
14614
|
-
|
|
14637
|
+
log25.warn("failed to decrypt control \u2014 ignoring");
|
|
14615
14638
|
return;
|
|
14616
14639
|
}
|
|
14617
14640
|
try {
|
|
@@ -14620,11 +14643,11 @@ async function runAgent(rawOptions) {
|
|
|
14620
14643
|
_verifiedEncrypted: true
|
|
14621
14644
|
};
|
|
14622
14645
|
} catch {
|
|
14623
|
-
|
|
14646
|
+
log25.warn("failed to parse encrypted control payload");
|
|
14624
14647
|
return;
|
|
14625
14648
|
}
|
|
14626
14649
|
} else if (conn.sessionKey && payload.action === "upload-file") {
|
|
14627
|
-
|
|
14650
|
+
log25.warn("rejecting plaintext attachment upload while encrypted channel is active");
|
|
14628
14651
|
return;
|
|
14629
14652
|
}
|
|
14630
14653
|
await dispatcher.handleControlAction(controlPayload);
|
|
@@ -14644,13 +14667,13 @@ async function runAgent(rawOptions) {
|
|
|
14644
14667
|
}
|
|
14645
14668
|
});
|
|
14646
14669
|
ws.on("error", (error) => {
|
|
14647
|
-
|
|
14670
|
+
log25.error("websocket error", { error: error.message });
|
|
14648
14671
|
});
|
|
14649
14672
|
ws.on("close", (code, reason) => {
|
|
14650
14673
|
const fields = {};
|
|
14651
14674
|
if (code) fields.code = code;
|
|
14652
14675
|
if (reason.length > 0) fields.reason = reason.toString();
|
|
14653
|
-
|
|
14676
|
+
log25.info("relay disconnected; output will be buffered until reconnect", fields);
|
|
14654
14677
|
conn.cleanup();
|
|
14655
14678
|
resolve();
|
|
14656
14679
|
});
|
|
@@ -14667,35 +14690,35 @@ async function runAgent(rawOptions) {
|
|
|
14667
14690
|
});
|
|
14668
14691
|
inventory.updateOptions(options);
|
|
14669
14692
|
dispatcher.updateOptions(options);
|
|
14670
|
-
|
|
14693
|
+
log25.info("recovered from auth failure via machine credential; reconnecting");
|
|
14671
14694
|
continue;
|
|
14672
14695
|
} catch (mcError) {
|
|
14673
14696
|
if (mcError instanceof AuthError) {
|
|
14674
|
-
|
|
14697
|
+
log25.error("auth recovery failed", { error: mcError.message });
|
|
14675
14698
|
} else {
|
|
14676
14699
|
const mcMsg = mcError instanceof Error ? mcError.message : String(mcError);
|
|
14677
|
-
|
|
14700
|
+
log25.error("machine credential recovery failed", { error: mcMsg });
|
|
14678
14701
|
}
|
|
14679
14702
|
}
|
|
14680
14703
|
}
|
|
14681
|
-
|
|
14682
|
-
|
|
14704
|
+
log25.error("auth error", { error: error.message });
|
|
14705
|
+
log25.error(
|
|
14683
14706
|
"connector token is invalid or revoked. Stopping. Re-connect with: ragent connect --token <token>"
|
|
14684
14707
|
);
|
|
14685
14708
|
dispatcher.shouldRun = false;
|
|
14686
14709
|
break;
|
|
14687
14710
|
}
|
|
14688
14711
|
const message = error instanceof Error ? error.message : String(error);
|
|
14689
|
-
|
|
14712
|
+
log25.error("relay connect failed", { error: message });
|
|
14690
14713
|
}
|
|
14691
14714
|
if (!dispatcher.shouldRun) break;
|
|
14692
14715
|
if (dispatcher.reconnectRequested) {
|
|
14693
|
-
|
|
14716
|
+
log25.info("reconnecting to relay");
|
|
14694
14717
|
await wait(300);
|
|
14695
14718
|
continue;
|
|
14696
14719
|
}
|
|
14697
14720
|
const jitteredDelay = conn.reconnectDelay * (0.5 + Math.random());
|
|
14698
|
-
|
|
14721
|
+
log25.info("disconnected; reconnecting", { seconds: Math.round(jitteredDelay / 1e3) });
|
|
14699
14722
|
await wait(jitteredDelay);
|
|
14700
14723
|
conn.reconnectDelay = Math.min(conn.reconnectDelay * 1.5, MAX_RECONNECT_DELAY_MS);
|
|
14701
14724
|
}
|
|
@@ -14795,7 +14818,7 @@ function printCommandArt(title, subtitle = "remote agent control") {
|
|
|
14795
14818
|
}
|
|
14796
14819
|
|
|
14797
14820
|
// src/commands/connect.ts
|
|
14798
|
-
var
|
|
14821
|
+
var log26 = createLogger("connect");
|
|
14799
14822
|
async function connectMachine(opts) {
|
|
14800
14823
|
const portal = opts.portal || DEFAULT_PORTAL;
|
|
14801
14824
|
const hostId = sanitizeHostId(opts.id || inferHostId());
|
|
@@ -14827,12 +14850,12 @@ async function connectMachine(opts) {
|
|
|
14827
14850
|
nextConfig.refreshExpiresAt = claimed.refreshExpiresAt;
|
|
14828
14851
|
}
|
|
14829
14852
|
saveConfig(nextConfig);
|
|
14830
|
-
|
|
14831
|
-
|
|
14832
|
-
|
|
14853
|
+
log26.info("machine connected", { hostName, hostId });
|
|
14854
|
+
log26.info("machine name (override with --name <friendly-name>)", { hostName });
|
|
14855
|
+
log26.info("config saved", { path: CONFIG_FILE });
|
|
14833
14856
|
if (opts.asService) {
|
|
14834
14857
|
await ensureServiceInstalled({ enable: true, start: true });
|
|
14835
|
-
|
|
14858
|
+
log26.info("service mode enabled");
|
|
14836
14859
|
return;
|
|
14837
14860
|
}
|
|
14838
14861
|
if (opts.run !== false) {
|
|
@@ -14852,14 +14875,14 @@ function registerConnectCommand(program2) {
|
|
|
14852
14875
|
await connectMachine(opts);
|
|
14853
14876
|
} catch (error) {
|
|
14854
14877
|
const message = error instanceof Error ? error.message : String(error);
|
|
14855
|
-
|
|
14878
|
+
log26.error("connect failed", { error: message });
|
|
14856
14879
|
process.exit(1);
|
|
14857
14880
|
}
|
|
14858
14881
|
});
|
|
14859
14882
|
}
|
|
14860
14883
|
|
|
14861
14884
|
// src/commands/run.ts
|
|
14862
|
-
var
|
|
14885
|
+
var log27 = createLogger("run");
|
|
14863
14886
|
function registerRunCommand(program2) {
|
|
14864
14887
|
program2.command("run").description("Run connector for a connected machine").option("--portal <url>", "Portal base URL").option("--agent-token <token>", "Connector token").option("-i, --id <id>", "Machine ID").option("-n, --name <name>", "Machine name").option("-c, --command <command>", "CLI command to run").option("--no-redact", "Disable secret redaction in terminal output").action(async (opts) => {
|
|
14865
14888
|
try {
|
|
@@ -14867,7 +14890,7 @@ function registerRunCommand(program2) {
|
|
|
14867
14890
|
await runAgent(opts);
|
|
14868
14891
|
} catch (error) {
|
|
14869
14892
|
const message = error instanceof Error ? error.message : String(error);
|
|
14870
|
-
|
|
14893
|
+
log27.error("run failed", { error: message });
|
|
14871
14894
|
process.exit(1);
|
|
14872
14895
|
}
|
|
14873
14896
|
});
|
|
@@ -14875,7 +14898,7 @@ function registerRunCommand(program2) {
|
|
|
14875
14898
|
|
|
14876
14899
|
// src/commands/doctor.ts
|
|
14877
14900
|
var os12 = __toESM(require("os"));
|
|
14878
|
-
var
|
|
14901
|
+
var log28 = createLogger("doctor");
|
|
14879
14902
|
async function runDoctor(opts) {
|
|
14880
14903
|
const options = resolveRunOptions(opts);
|
|
14881
14904
|
const checks = [];
|
|
@@ -14911,27 +14934,27 @@ async function runDoctor(opts) {
|
|
|
14911
14934
|
console.log(`${check.ok ? "PASS" : "FAIL"} ${check.name}: ${check.detail}`);
|
|
14912
14935
|
});
|
|
14913
14936
|
if (!platformOk) {
|
|
14914
|
-
|
|
14937
|
+
log28.info("Linux is required for the connector");
|
|
14915
14938
|
}
|
|
14916
14939
|
if (!tmuxOk) {
|
|
14917
14940
|
const recipe = await chooseTmuxInstallCommand();
|
|
14918
14941
|
if (recipe) {
|
|
14919
|
-
|
|
14942
|
+
log28.info("tmux install suggestion", { command: recipe.command });
|
|
14920
14943
|
}
|
|
14921
14944
|
if (opts.fix) {
|
|
14922
14945
|
try {
|
|
14923
14946
|
const installed = await installTmuxInteractively();
|
|
14924
14947
|
if (installed) {
|
|
14925
|
-
|
|
14948
|
+
log28.info("tmux installation complete");
|
|
14926
14949
|
} else {
|
|
14927
|
-
|
|
14950
|
+
log28.info("tmux installation skipped or incomplete");
|
|
14928
14951
|
}
|
|
14929
14952
|
} catch (error) {
|
|
14930
14953
|
const message = error instanceof Error ? error.message : String(error);
|
|
14931
|
-
|
|
14954
|
+
log28.error("failed to install tmux", { error: message });
|
|
14932
14955
|
}
|
|
14933
14956
|
} else {
|
|
14934
|
-
|
|
14957
|
+
log28.info("run `ragent doctor --fix` to install missing dependencies interactively");
|
|
14935
14958
|
}
|
|
14936
14959
|
}
|
|
14937
14960
|
}
|
|
@@ -14943,30 +14966,30 @@ function registerDoctorCommand(program2) {
|
|
|
14943
14966
|
}
|
|
14944
14967
|
|
|
14945
14968
|
// src/commands/update.ts
|
|
14946
|
-
var
|
|
14969
|
+
var log29 = createLogger("update");
|
|
14947
14970
|
function registerUpdateCommand(program2) {
|
|
14948
14971
|
program2.command("update").description("Update ragent CLI from npm").option("--check", "Check for updates only; do not install").option("--no-restart", "Do not restart the service after updating").action(async (opts) => {
|
|
14949
14972
|
printCommandArt("Update", "checking npm registry for newer ragent-cli versions");
|
|
14950
14973
|
const latestVersion = await checkForUpdate({ force: true });
|
|
14951
14974
|
if (!latestVersion) {
|
|
14952
|
-
|
|
14975
|
+
log29.info("you are up to date", { version: CURRENT_VERSION });
|
|
14953
14976
|
return;
|
|
14954
14977
|
}
|
|
14955
|
-
|
|
14978
|
+
log29.info("update available", { current: CURRENT_VERSION, latest: latestVersion });
|
|
14956
14979
|
if (opts.check) return;
|
|
14957
14980
|
await installLatestCliFromNpm();
|
|
14958
|
-
|
|
14981
|
+
log29.info("updated", { version: latestVersion });
|
|
14959
14982
|
const backend = getConfiguredServiceBackend();
|
|
14960
14983
|
if (backend && opts.restart) {
|
|
14961
|
-
|
|
14984
|
+
log29.info("restarting service", { backend });
|
|
14962
14985
|
try {
|
|
14963
14986
|
await restartService();
|
|
14964
14987
|
} catch (err) {
|
|
14965
|
-
|
|
14966
|
-
|
|
14988
|
+
log29.error("failed to restart service", { error: err instanceof Error ? err.message : String(err) });
|
|
14989
|
+
log29.info("please restart manually: ragent service restart");
|
|
14967
14990
|
}
|
|
14968
14991
|
} else if (!backend) {
|
|
14969
|
-
|
|
14992
|
+
log29.info("no service backend detected. If ragent is running manually, restart it to use the new version");
|
|
14970
14993
|
}
|
|
14971
14994
|
});
|
|
14972
14995
|
}
|
|
@@ -14990,7 +15013,7 @@ function registerSessionsCommand(program2) {
|
|
|
14990
15013
|
}
|
|
14991
15014
|
|
|
14992
15015
|
// src/commands/service.ts
|
|
14993
|
-
var
|
|
15016
|
+
var log30 = createLogger("service-cmd");
|
|
14994
15017
|
function registerServiceCommand(program2) {
|
|
14995
15018
|
const service = program2.command("service").description("Manage background connector service");
|
|
14996
15019
|
service.command("install").description("Install service (systemd user unit when available)").option("--start", "Start service immediately").option("--no-enable", "Do not enable autostart in systemd").action(async (opts) => {
|
|
@@ -15001,7 +15024,7 @@ function registerServiceCommand(program2) {
|
|
|
15001
15024
|
});
|
|
15002
15025
|
} catch (error) {
|
|
15003
15026
|
const message = error instanceof Error ? error.message : String(error);
|
|
15004
|
-
|
|
15027
|
+
log30.error("service install failed", { error: message });
|
|
15005
15028
|
process.exit(1);
|
|
15006
15029
|
}
|
|
15007
15030
|
});
|
|
@@ -15010,7 +15033,7 @@ function registerServiceCommand(program2) {
|
|
|
15010
15033
|
await startService();
|
|
15011
15034
|
} catch (error) {
|
|
15012
15035
|
const message = error instanceof Error ? error.message : String(error);
|
|
15013
|
-
|
|
15036
|
+
log30.error("service start failed", { error: message });
|
|
15014
15037
|
process.exit(1);
|
|
15015
15038
|
}
|
|
15016
15039
|
});
|
|
@@ -15019,7 +15042,7 @@ function registerServiceCommand(program2) {
|
|
|
15019
15042
|
await stopService();
|
|
15020
15043
|
} catch (error) {
|
|
15021
15044
|
const message = error instanceof Error ? error.message : String(error);
|
|
15022
|
-
|
|
15045
|
+
log30.error("service stop failed", { error: message });
|
|
15023
15046
|
process.exit(1);
|
|
15024
15047
|
}
|
|
15025
15048
|
});
|
|
@@ -15028,7 +15051,7 @@ function registerServiceCommand(program2) {
|
|
|
15028
15051
|
await restartService();
|
|
15029
15052
|
} catch (error) {
|
|
15030
15053
|
const message = error instanceof Error ? error.message : String(error);
|
|
15031
|
-
|
|
15054
|
+
log30.error("service restart failed", { error: message });
|
|
15032
15055
|
process.exit(1);
|
|
15033
15056
|
}
|
|
15034
15057
|
});
|
|
@@ -15037,7 +15060,7 @@ function registerServiceCommand(program2) {
|
|
|
15037
15060
|
await printServiceStatus();
|
|
15038
15061
|
} catch (error) {
|
|
15039
15062
|
const message = error instanceof Error ? error.message : String(error);
|
|
15040
|
-
|
|
15063
|
+
log30.error("service status failed", { error: message });
|
|
15041
15064
|
process.exit(1);
|
|
15042
15065
|
}
|
|
15043
15066
|
});
|
|
@@ -15046,7 +15069,7 @@ function registerServiceCommand(program2) {
|
|
|
15046
15069
|
await printServiceLogs(opts);
|
|
15047
15070
|
} catch (error) {
|
|
15048
15071
|
const message = error instanceof Error ? error.message : String(error);
|
|
15049
|
-
|
|
15072
|
+
log30.error("service logs failed", { error: message });
|
|
15050
15073
|
process.exit(1);
|
|
15051
15074
|
}
|
|
15052
15075
|
});
|
|
@@ -15055,7 +15078,7 @@ function registerServiceCommand(program2) {
|
|
|
15055
15078
|
await uninstallService();
|
|
15056
15079
|
} catch (error) {
|
|
15057
15080
|
const message = error instanceof Error ? error.message : String(error);
|
|
15058
|
-
|
|
15081
|
+
log30.error("service uninstall failed", { error: message });
|
|
15059
15082
|
process.exit(1);
|
|
15060
15083
|
}
|
|
15061
15084
|
});
|
|
@@ -15063,7 +15086,7 @@ function registerServiceCommand(program2) {
|
|
|
15063
15086
|
|
|
15064
15087
|
// src/commands/uninstall.ts
|
|
15065
15088
|
var fs8 = __toESM(require("fs"));
|
|
15066
|
-
var
|
|
15089
|
+
var log31 = createLogger("uninstall");
|
|
15067
15090
|
async function uninstallAgent(opts) {
|
|
15068
15091
|
const config = loadConfig();
|
|
15069
15092
|
const hostName = config.hostName || config.hostId || "this machine";
|
|
@@ -15073,12 +15096,12 @@ async function uninstallAgent(opts) {
|
|
|
15073
15096
|
false
|
|
15074
15097
|
);
|
|
15075
15098
|
if (!confirmed) {
|
|
15076
|
-
|
|
15099
|
+
log31.info("uninstall cancelled");
|
|
15077
15100
|
return;
|
|
15078
15101
|
}
|
|
15079
15102
|
}
|
|
15080
15103
|
if (config.portal && config.agentToken) {
|
|
15081
|
-
|
|
15104
|
+
log31.info("deregistering from server");
|
|
15082
15105
|
try {
|
|
15083
15106
|
const token = await refreshTokenIfNeeded({
|
|
15084
15107
|
portal: config.portal,
|
|
@@ -15086,24 +15109,24 @@ async function uninstallAgent(opts) {
|
|
|
15086
15109
|
});
|
|
15087
15110
|
const ok = await deregisterHost({ portal: config.portal, agentToken: token });
|
|
15088
15111
|
if (ok) {
|
|
15089
|
-
|
|
15112
|
+
log31.info("host removed from server");
|
|
15090
15113
|
}
|
|
15091
15114
|
} catch {
|
|
15092
|
-
|
|
15115
|
+
log31.warn("could not reach server \u2014 host may need manual removal from dashboard");
|
|
15093
15116
|
}
|
|
15094
15117
|
}
|
|
15095
|
-
|
|
15118
|
+
log31.info("stopping and removing service");
|
|
15096
15119
|
await uninstallService().catch(() => void 0);
|
|
15097
15120
|
if (fs8.existsSync(CONFIG_DIR)) {
|
|
15098
15121
|
fs8.rmSync(CONFIG_DIR, { recursive: true, force: true });
|
|
15099
|
-
|
|
15122
|
+
log31.info("removed config directory", { path: CONFIG_DIR });
|
|
15100
15123
|
}
|
|
15101
15124
|
try {
|
|
15102
15125
|
await execAsync(`npm unlink -g ${PACKAGE_NAME}`, { timeout: 15e3 });
|
|
15103
|
-
|
|
15126
|
+
log31.info("unlinked global package", { package: PACKAGE_NAME });
|
|
15104
15127
|
} catch {
|
|
15105
15128
|
}
|
|
15106
|
-
|
|
15129
|
+
log31.info("uninstall complete. rAgent has been removed from this machine");
|
|
15107
15130
|
}
|
|
15108
15131
|
function registerUninstallCommand(parent) {
|
|
15109
15132
|
parent.command("uninstall").description("Remove rAgent from this machine (stops service, deletes config, unlinks CLI)").option("-y, --yes", "Skip confirmation prompt").action(async (opts) => {
|
|
@@ -15112,7 +15135,7 @@ function registerUninstallCommand(parent) {
|
|
|
15112
15135
|
await uninstallAgent(opts);
|
|
15113
15136
|
} catch (error) {
|
|
15114
15137
|
const message = error instanceof Error ? error.message : String(error);
|
|
15115
|
-
|
|
15138
|
+
log31.error("uninstall failed", { error: message });
|
|
15116
15139
|
process.exit(1);
|
|
15117
15140
|
}
|
|
15118
15141
|
});
|
|
@@ -15171,10 +15194,10 @@ function registerDiscoverCommand(program2) {
|
|
|
15171
15194
|
}
|
|
15172
15195
|
|
|
15173
15196
|
// src/index.ts
|
|
15174
|
-
var
|
|
15197
|
+
var log32 = createLogger("cli");
|
|
15175
15198
|
process.on("unhandledRejection", (reason) => {
|
|
15176
15199
|
const message = reason instanceof Error ? reason.stack || reason.message : String(reason);
|
|
15177
|
-
|
|
15200
|
+
log32.error("unhandled promise rejection", { error: message });
|
|
15178
15201
|
});
|
|
15179
15202
|
import_commander.program.name("ragent").description("Connect machines to rAgent Live").version(CURRENT_VERSION);
|
|
15180
15203
|
registerConnectCommand(import_commander.program);
|