@bulletproof-sh/ctrl-daemon 0.0.14 → 0.0.16
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 +94 -82
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ var __export = (target, all) => {
|
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
// src/index.ts
|
|
14
|
+
import * as fs3 from "fs";
|
|
14
15
|
import * as path3 from "path";
|
|
15
16
|
|
|
16
17
|
// src/analytics.ts
|
|
@@ -4842,10 +4843,19 @@ function restoreConsole() {
|
|
|
4842
4843
|
}
|
|
4843
4844
|
|
|
4844
4845
|
// src/sessionWatcher.ts
|
|
4845
|
-
|
|
4846
|
+
var SHARED_BUF_SIZE = 64 * 1024;
|
|
4847
|
+
var sharedReadBuf = Buffer.allocUnsafe(SHARED_BUF_SIZE);
|
|
4848
|
+
function startSessionWatcher(agentId, filePath, agents, openFds, staleAgents, fileWatchers, pollingTimers, waitingTimers, permissionTimers, broadcast) {
|
|
4849
|
+
try {
|
|
4850
|
+
const fd = fs.openSync(filePath, "r");
|
|
4851
|
+
openFds.set(agentId, fd);
|
|
4852
|
+
} catch (e) {
|
|
4853
|
+
daemonLog(`Failed to open fd for agent ${agentId}: ${e}`);
|
|
4854
|
+
return;
|
|
4855
|
+
}
|
|
4846
4856
|
try {
|
|
4847
4857
|
const watcher = fs.watch(filePath, () => {
|
|
4848
|
-
readNewLines(agentId, agents, waitingTimers, permissionTimers, broadcast);
|
|
4858
|
+
readNewLines(agentId, agents, openFds, staleAgents, waitingTimers, permissionTimers, broadcast);
|
|
4849
4859
|
});
|
|
4850
4860
|
fileWatchers.set(agentId, watcher);
|
|
4851
4861
|
} catch (e) {
|
|
@@ -4856,47 +4866,58 @@ function startSessionWatcher(agentId, filePath, agents, fileWatchers, pollingTim
|
|
|
4856
4866
|
clearInterval(interval);
|
|
4857
4867
|
return;
|
|
4858
4868
|
}
|
|
4859
|
-
readNewLines(agentId, agents, waitingTimers, permissionTimers, broadcast);
|
|
4869
|
+
readNewLines(agentId, agents, openFds, staleAgents, waitingTimers, permissionTimers, broadcast);
|
|
4860
4870
|
}, FILE_WATCHER_POLL_INTERVAL_MS);
|
|
4861
4871
|
pollingTimers.set(agentId, interval);
|
|
4862
4872
|
}
|
|
4863
|
-
function readNewLines(agentId, agents, waitingTimers, permissionTimers, broadcast) {
|
|
4873
|
+
function readNewLines(agentId, agents, openFds, staleAgents, waitingTimers, permissionTimers, broadcast) {
|
|
4864
4874
|
const agent = agents.get(agentId);
|
|
4865
4875
|
if (!agent)
|
|
4866
4876
|
return;
|
|
4877
|
+
const fd = openFds.get(agentId);
|
|
4878
|
+
if (fd === undefined)
|
|
4879
|
+
return;
|
|
4867
4880
|
try {
|
|
4868
|
-
const stat = fs.
|
|
4881
|
+
const stat = fs.fstatSync(fd);
|
|
4869
4882
|
if (stat.size <= agent.fileOffset)
|
|
4870
4883
|
return;
|
|
4871
|
-
const
|
|
4872
|
-
const
|
|
4873
|
-
fs.readSync(fd, buf, 0,
|
|
4874
|
-
fs.closeSync(fd);
|
|
4884
|
+
const readSize = stat.size - agent.fileOffset;
|
|
4885
|
+
const buf = readSize <= sharedReadBuf.length ? sharedReadBuf : Buffer.allocUnsafe(readSize);
|
|
4886
|
+
const bytesRead = fs.readSync(fd, buf, 0, readSize, agent.fileOffset);
|
|
4875
4887
|
agent.fileOffset = stat.size;
|
|
4876
|
-
const text = agent.lineBuffer + buf.toString("utf-8");
|
|
4888
|
+
const text = agent.lineBuffer + buf.toString("utf-8", 0, bytesRead);
|
|
4877
4889
|
const lines = text.split(`
|
|
4878
4890
|
`);
|
|
4879
4891
|
agent.lineBuffer = lines.pop() || "";
|
|
4880
|
-
|
|
4881
|
-
if (hasLines) {
|
|
4882
|
-
agent.lastActivityAt = Date.now();
|
|
4883
|
-
cancelWaitingTimer(agentId, waitingTimers);
|
|
4884
|
-
cancelPermissionTimer(agentId, permissionTimers);
|
|
4885
|
-
if (agent.permissionSent) {
|
|
4886
|
-
agent.permissionSent = false;
|
|
4887
|
-
broadcast({ type: "agentToolPermissionClear", id: agentId });
|
|
4888
|
-
}
|
|
4889
|
-
}
|
|
4892
|
+
let activityUpdated = false;
|
|
4890
4893
|
for (const line of lines) {
|
|
4891
4894
|
if (!line.trim())
|
|
4892
4895
|
continue;
|
|
4896
|
+
if (!activityUpdated) {
|
|
4897
|
+
agent.lastActivityAt = Date.now();
|
|
4898
|
+
cancelWaitingTimer(agentId, waitingTimers);
|
|
4899
|
+
cancelPermissionTimer(agentId, permissionTimers);
|
|
4900
|
+
if (agent.permissionSent) {
|
|
4901
|
+
agent.permissionSent = false;
|
|
4902
|
+
broadcast({ type: "agentToolPermissionClear", id: agentId });
|
|
4903
|
+
}
|
|
4904
|
+
activityUpdated = true;
|
|
4905
|
+
}
|
|
4893
4906
|
processTranscriptLine(agentId, line, agents, waitingTimers, permissionTimers, broadcast);
|
|
4894
4907
|
}
|
|
4895
4908
|
} catch (e) {
|
|
4896
4909
|
daemonLog(`Read error for agent ${agentId}: ${e}`);
|
|
4910
|
+
staleAgents.add(agentId);
|
|
4897
4911
|
}
|
|
4898
4912
|
}
|
|
4899
|
-
function stopSessionWatcher(agentId, fileWatchers, pollingTimers, waitingTimers, permissionTimers) {
|
|
4913
|
+
function stopSessionWatcher(agentId, openFds, fileWatchers, pollingTimers, waitingTimers, permissionTimers) {
|
|
4914
|
+
const fd = openFds.get(agentId);
|
|
4915
|
+
if (fd !== undefined) {
|
|
4916
|
+
try {
|
|
4917
|
+
fs.closeSync(fd);
|
|
4918
|
+
} catch {}
|
|
4919
|
+
openFds.delete(agentId);
|
|
4920
|
+
}
|
|
4900
4921
|
fileWatchers.get(agentId)?.close();
|
|
4901
4922
|
fileWatchers.delete(agentId);
|
|
4902
4923
|
const pt = pollingTimers.get(agentId);
|
|
@@ -4960,7 +4981,7 @@ function collectAllJsonlFiles(projectsRoot) {
|
|
|
4960
4981
|
}
|
|
4961
4982
|
return files;
|
|
4962
4983
|
}
|
|
4963
|
-
function startProjectScanner(rootDir, scanAll, agents, fileWatchers, pollingTimers, waitingTimers, permissionTimers, broadcast, idleTimeoutMs) {
|
|
4984
|
+
function startProjectScanner(rootDir, scanAll, agents, openFds, staleAgents, fileWatchers, pollingTimers, waitingTimers, permissionTimers, broadcast, idleTimeoutMs) {
|
|
4964
4985
|
const knownJsonlFiles = new Set;
|
|
4965
4986
|
let nextAgentId = 1;
|
|
4966
4987
|
function scan() {
|
|
@@ -4993,17 +5014,18 @@ function startProjectScanner(rootDir, scanAll, agents, fileWatchers, pollingTime
|
|
|
4993
5014
|
agents.set(id, agent);
|
|
4994
5015
|
daemonLog(`Agent ${id}: watching ${path2.basename(filePath)}`);
|
|
4995
5016
|
broadcast({ type: "agentCreated", id });
|
|
4996
|
-
startSessionWatcher(id, filePath, agents, fileWatchers, pollingTimers, waitingTimers, permissionTimers, broadcast);
|
|
4997
|
-
readNewLines(id, agents, waitingTimers, permissionTimers, broadcast);
|
|
5017
|
+
startSessionWatcher(id, filePath, agents, openFds, staleAgents, fileWatchers, pollingTimers, waitingTimers, permissionTimers, broadcast);
|
|
5018
|
+
readNewLines(id, agents, openFds, staleAgents, waitingTimers, permissionTimers, broadcast);
|
|
4998
5019
|
}
|
|
4999
5020
|
for (const [agentId, agent] of agents) {
|
|
5000
5021
|
const lastActivity = agent.lastActivityAt || 0;
|
|
5001
5022
|
const idle = now - lastActivity > idleTimeoutMs;
|
|
5002
|
-
const removed =
|
|
5023
|
+
const removed = staleAgents.has(agentId);
|
|
5003
5024
|
if (removed || idle) {
|
|
5004
5025
|
const reason = removed ? "JSONL removed" : "idle timeout";
|
|
5005
5026
|
daemonLog(`Agent ${agentId}: ${reason}, closing`);
|
|
5006
|
-
|
|
5027
|
+
staleAgents.delete(agentId);
|
|
5028
|
+
stopSessionWatcher(agentId, openFds, fileWatchers, pollingTimers, waitingTimers, permissionTimers);
|
|
5007
5029
|
agents.delete(agentId);
|
|
5008
5030
|
knownJsonlFiles.delete(agent.jsonlFile);
|
|
5009
5031
|
broadcast({ type: "agentClosed", id: agentId });
|
|
@@ -5118,6 +5140,7 @@ var CURSOR_SHOW = "\x1B[?25h";
|
|
|
5118
5140
|
var CLEAR_SCREEN = "\x1B[2J";
|
|
5119
5141
|
var RESET_ATTRS = "\x1B[0m";
|
|
5120
5142
|
var resizeCallback = null;
|
|
5143
|
+
var moveToTable = [];
|
|
5121
5144
|
function onSigwinch() {
|
|
5122
5145
|
resizeCallback?.();
|
|
5123
5146
|
}
|
|
@@ -5144,8 +5167,19 @@ function offResize() {
|
|
|
5144
5167
|
resizeCallback = null;
|
|
5145
5168
|
process.removeListener("SIGWINCH", onSigwinch);
|
|
5146
5169
|
}
|
|
5170
|
+
function buildMoveToTable(rows, cols) {
|
|
5171
|
+
const table = [];
|
|
5172
|
+
for (let r = 0;r <= rows; r++) {
|
|
5173
|
+
const row = [];
|
|
5174
|
+
for (let c = 0;c <= cols; c++) {
|
|
5175
|
+
row.push(`\x1B[${r};${c}H`);
|
|
5176
|
+
}
|
|
5177
|
+
table.push(row);
|
|
5178
|
+
}
|
|
5179
|
+
moveToTable = table;
|
|
5180
|
+
}
|
|
5147
5181
|
function moveTo(row, col) {
|
|
5148
|
-
return `\x1B[${row};${col}H`;
|
|
5182
|
+
return moveToTable[row]?.[col] ?? `\x1B[${row};${col}H`;
|
|
5149
5183
|
}
|
|
5150
5184
|
|
|
5151
5185
|
// src/tui/renderer.ts
|
|
@@ -5182,7 +5216,7 @@ function setCell(buf, row, col, char, fg, bold, dim) {
|
|
|
5182
5216
|
}
|
|
5183
5217
|
}
|
|
5184
5218
|
function flushDiff(current, previous) {
|
|
5185
|
-
|
|
5219
|
+
const parts = [];
|
|
5186
5220
|
let lastFg = "\x00";
|
|
5187
5221
|
let lastBold = false;
|
|
5188
5222
|
let lastDim = false;
|
|
@@ -5198,7 +5232,7 @@ function flushDiff(current, previous) {
|
|
|
5198
5232
|
const prev = prevRow[c];
|
|
5199
5233
|
if (cur.char === " " && cur.fg === "" && prev.char === " " && prev.fg === "") {
|
|
5200
5234
|
if (runStart !== -1) {
|
|
5201
|
-
|
|
5235
|
+
parts.push(flushRun(curRow, r, runStart, c, lastFg, lastBold, lastDim));
|
|
5202
5236
|
const last = curRow[c - 1];
|
|
5203
5237
|
lastFg = last.fg;
|
|
5204
5238
|
lastBold = last.bold;
|
|
@@ -5216,7 +5250,7 @@ function flushDiff(current, previous) {
|
|
|
5216
5250
|
if (runStart === -1)
|
|
5217
5251
|
runStart = c;
|
|
5218
5252
|
} else if (runStart !== -1) {
|
|
5219
|
-
|
|
5253
|
+
parts.push(flushRun(curRow, r, runStart, c, lastFg, lastBold, lastDim));
|
|
5220
5254
|
const last = curRow[c - 1];
|
|
5221
5255
|
lastFg = last.fg;
|
|
5222
5256
|
lastBold = last.bold;
|
|
@@ -5225,9 +5259,9 @@ function flushDiff(current, previous) {
|
|
|
5225
5259
|
}
|
|
5226
5260
|
}
|
|
5227
5261
|
}
|
|
5228
|
-
if (
|
|
5229
|
-
|
|
5230
|
-
process.stdout.write(
|
|
5262
|
+
if (parts.length > 0) {
|
|
5263
|
+
parts.push(RESET);
|
|
5264
|
+
process.stdout.write(parts.join(""));
|
|
5231
5265
|
}
|
|
5232
5266
|
}
|
|
5233
5267
|
function flushRun(row, r, start, end, lastFg, lastBold, lastDim) {
|
|
@@ -5466,9 +5500,7 @@ for (let offset = 0;offset <= 4; offset++) {
|
|
|
5466
5500
|
}
|
|
5467
5501
|
GRADIENT_STRINGS.push(arr);
|
|
5468
5502
|
}
|
|
5469
|
-
var
|
|
5470
|
-
var LIGHTNING_NEAR = fg256(159);
|
|
5471
|
-
var LIGHTNING_TAIL = fg256(49);
|
|
5503
|
+
var LIGHTNING_COLORS = [fg256(231), fg256(159), fg256(49)];
|
|
5472
5504
|
function renderRain(layers, buf, panel) {
|
|
5473
5505
|
const bufRows = buf.rows;
|
|
5474
5506
|
const bufCols = buf.cols;
|
|
@@ -5483,51 +5515,22 @@ function renderRain(layers, buf, panel) {
|
|
|
5483
5515
|
const gradStrs = GRADIENT_STRINGS[brightnessOffset];
|
|
5484
5516
|
const colCount = Math.min(layer.columns.length, bufCols);
|
|
5485
5517
|
for (let col = 0;col < colCount; col++) {
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
const gradIdx = trailLen > 1 ? i * gradLen / (trailLen - 1) | 0 : 0;
|
|
5503
|
-
color = gradStrs[Math.min(gradIdx, gradLen)];
|
|
5504
|
-
}
|
|
5505
|
-
const bold = i === 0 || drop.isLightning;
|
|
5506
|
-
const dim = !drop.isLightning && i > trailLen * 0.7;
|
|
5507
|
-
setCell(buf, row, col, drop.chars[i], color, bold, dim);
|
|
5508
|
-
}
|
|
5509
|
-
}
|
|
5510
|
-
} else {
|
|
5511
|
-
const drops = layer.columns[col].drops;
|
|
5512
|
-
for (let d = 0;d < drops.length; d++) {
|
|
5513
|
-
const drop = drops[d];
|
|
5514
|
-
const headRow = drop.y | 0;
|
|
5515
|
-
const trailLen = drop.trailLen;
|
|
5516
|
-
for (let i = 0;i < trailLen; i++) {
|
|
5517
|
-
const row = headRow - i;
|
|
5518
|
-
if (row < 0 || row >= bufRows)
|
|
5519
|
-
continue;
|
|
5520
|
-
let color;
|
|
5521
|
-
if (drop.isLightning) {
|
|
5522
|
-
color = i === 0 ? LIGHTNING_HEAD : i === 1 ? LIGHTNING_NEAR : LIGHTNING_TAIL;
|
|
5523
|
-
} else {
|
|
5524
|
-
const gradIdx = trailLen > 1 ? i * gradLen / (trailLen - 1) | 0 : 0;
|
|
5525
|
-
color = gradStrs[Math.min(gradIdx, gradLen)];
|
|
5526
|
-
}
|
|
5527
|
-
const bold = i === 0 || drop.isLightning;
|
|
5528
|
-
const dim = !drop.isLightning && i > trailLen * 0.7;
|
|
5529
|
-
setCell(buf, row, col, drop.chars[i], color, bold, dim);
|
|
5530
|
-
}
|
|
5518
|
+
const checkPanel = panelVisible && col >= panelX && col < panelX2;
|
|
5519
|
+
const drops = layer.columns[col].drops;
|
|
5520
|
+
for (let d = 0;d < drops.length; d++) {
|
|
5521
|
+
const drop = drops[d];
|
|
5522
|
+
const headRow = drop.y | 0;
|
|
5523
|
+
const trailLen = drop.trailLen;
|
|
5524
|
+
for (let i = 0;i < trailLen; i++) {
|
|
5525
|
+
const row = headRow - i;
|
|
5526
|
+
if (row < 0 || row >= bufRows)
|
|
5527
|
+
continue;
|
|
5528
|
+
if (checkPanel && row >= panelY && row < panelY2)
|
|
5529
|
+
continue;
|
|
5530
|
+
const color = drop.isLightning ? LIGHTNING_COLORS[Math.min(i, 2)] : gradStrs[Math.min(trailLen > 1 ? i * gradLen / (trailLen - 1) | 0 : 0, gradLen)];
|
|
5531
|
+
const bold = i === 0 || drop.isLightning;
|
|
5532
|
+
const dim = !drop.isLightning && i > trailLen * 0.7;
|
|
5533
|
+
setCell(buf, row, col, drop.chars[i], color, bold, dim);
|
|
5531
5534
|
}
|
|
5532
5535
|
}
|
|
5533
5536
|
}
|
|
@@ -5554,6 +5557,7 @@ var tuiOptions = null;
|
|
|
5554
5557
|
function handleResize() {
|
|
5555
5558
|
const { rows, cols } = getTerminalSize();
|
|
5556
5559
|
clearScreen();
|
|
5560
|
+
buildMoveToTable(rows, cols);
|
|
5557
5561
|
bufA = createFrameBuffer(rows, cols);
|
|
5558
5562
|
bufB = createFrameBuffer(rows, cols);
|
|
5559
5563
|
currentIsA = true;
|
|
@@ -5587,6 +5591,7 @@ function startTui(options) {
|
|
|
5587
5591
|
tuiOptions = options;
|
|
5588
5592
|
enterAltScreen();
|
|
5589
5593
|
const { rows, cols } = getTerminalSize();
|
|
5594
|
+
buildMoveToTable(rows, cols);
|
|
5590
5595
|
bufA = createFrameBuffer(rows, cols);
|
|
5591
5596
|
bufB = createFrameBuffer(rows, cols);
|
|
5592
5597
|
currentIsA = true;
|
|
@@ -5754,6 +5759,8 @@ async function main() {
|
|
|
5754
5759
|
scanDirs = [projectsRoot];
|
|
5755
5760
|
}
|
|
5756
5761
|
const agents = new Map;
|
|
5762
|
+
const openFds = new Map;
|
|
5763
|
+
const staleAgents = new Set;
|
|
5757
5764
|
const fileWatchers = new Map;
|
|
5758
5765
|
const pollingTimers = new Map;
|
|
5759
5766
|
const waitingTimers = new Map;
|
|
@@ -5784,7 +5791,7 @@ async function main() {
|
|
|
5784
5791
|
...analyticsConfig
|
|
5785
5792
|
});
|
|
5786
5793
|
const scanAll = !projectDir;
|
|
5787
|
-
const scanner = startProjectScanner(scanDirs[0], scanAll, agents, fileWatchers, pollingTimers, waitingTimers, permissionTimers, broadcast, idleTimeoutMs);
|
|
5794
|
+
const scanner = startProjectScanner(scanDirs[0], scanAll, agents, openFds, staleAgents, fileWatchers, pollingTimers, waitingTimers, permissionTimers, broadcast, idleTimeoutMs);
|
|
5788
5795
|
const updateCheckTimer = setInterval(async () => {
|
|
5789
5796
|
const msg = await checkForUpdate();
|
|
5790
5797
|
if (msg) {
|
|
@@ -5798,6 +5805,11 @@ async function main() {
|
|
|
5798
5805
|
[ctrl-daemon] Shutting down...`);
|
|
5799
5806
|
scanner.stop();
|
|
5800
5807
|
server.stop();
|
|
5808
|
+
for (const fd of openFds.values()) {
|
|
5809
|
+
try {
|
|
5810
|
+
fs3.closeSync(fd);
|
|
5811
|
+
} catch {}
|
|
5812
|
+
}
|
|
5801
5813
|
for (const watcher of fileWatchers.values())
|
|
5802
5814
|
watcher.close();
|
|
5803
5815
|
for (const timer of pollingTimers.values())
|