@solongate/proxy 0.73.0 → 0.74.0
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 +137 -154
- package/dist/tui/index.js +136 -153
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7034,7 +7034,7 @@ import { closeSync, openSync, readSync, statSync } from "fs";
|
|
|
7034
7034
|
import { homedir as homedir3 } from "os";
|
|
7035
7035
|
import { join as join5 } from "path";
|
|
7036
7036
|
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
7037
|
-
import {
|
|
7037
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
7038
7038
|
function tailLines(file, maxBytes = 131072) {
|
|
7039
7039
|
try {
|
|
7040
7040
|
const size = statSync(file).size;
|
|
@@ -7059,7 +7059,6 @@ function cloudItem(e) {
|
|
|
7059
7059
|
permission: (e.permission ?? "").slice(0, 4),
|
|
7060
7060
|
detail: (e.arguments_summary ? JSON.stringify(e.arguments_summary) : e.reason ?? "").replace(/\s+/g, " "),
|
|
7061
7061
|
dlp: !!e.dlp_matches?.length,
|
|
7062
|
-
burst: !!e.rate_limit_burst,
|
|
7063
7062
|
source: "cloud",
|
|
7064
7063
|
session: e.session_id ?? void 0,
|
|
7065
7064
|
agent: e.agent_name ?? void 0,
|
|
@@ -7115,12 +7114,6 @@ function PaneTitle({ label, extra, width: width2 }) {
|
|
|
7115
7114
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: tail + "\u2500".repeat(Math.max(0, width2 - used)) })
|
|
7116
7115
|
] });
|
|
7117
7116
|
}
|
|
7118
|
-
function Stat({ label, value, color }) {
|
|
7119
|
-
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginRight: 3, children: [
|
|
7120
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: label }),
|
|
7121
|
-
/* @__PURE__ */ jsx2(Text2, { bold: true, color, children: String(value) })
|
|
7122
|
-
] });
|
|
7123
|
-
}
|
|
7124
7117
|
function StreamLine({ e, loc }) {
|
|
7125
7118
|
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7126
7119
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
@@ -7136,7 +7129,6 @@ function StreamLine({ e, loc }) {
|
|
|
7136
7129
|
/* @__PURE__ */ jsx2(Text2, { color: e.evalMs != null && e.evalMs > 500 ? theme.warn : theme.dim, children: (e.evalMs != null ? `${e.evalMs}ms` : "\u2014").padEnd(7) }),
|
|
7137
7130
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: truncate2(e.agent ?? "-", 11).padEnd(12) }),
|
|
7138
7131
|
e.dlp ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: "DLP! " }) : null,
|
|
7139
|
-
e.burst ? /* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: "BURST " }) : null,
|
|
7140
7132
|
e.rule && e.decision !== "ALLOW" ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: truncate2(e.rule, 14) + " " }) : null,
|
|
7141
7133
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.detail })
|
|
7142
7134
|
] });
|
|
@@ -7153,10 +7145,10 @@ function LivePanel({ active: active2 }) {
|
|
|
7153
7145
|
const [filter, setFilter] = useState2("all");
|
|
7154
7146
|
const [scroll, setScroll] = useState2(0);
|
|
7155
7147
|
const [mode, setMode] = useState2("stream");
|
|
7156
|
-
const [sessFilter, setSessFilter] = useState2("all");
|
|
7157
7148
|
const [pickIdx, setPickIdx] = useState2(0);
|
|
7149
|
+
const [detail, setDetail] = useState2(null);
|
|
7158
7150
|
const [detailCloud, setDetailCloud] = useState2([]);
|
|
7159
|
-
const [
|
|
7151
|
+
const [detailScroll, setDetailScroll] = useState2(0);
|
|
7160
7152
|
const seenRef = useRef2(/* @__PURE__ */ new Set());
|
|
7161
7153
|
const lastLocalTs = useRef2(0);
|
|
7162
7154
|
const pausedUntil = useRef2(0);
|
|
@@ -7201,7 +7193,6 @@ function LivePanel({ active: active2 }) {
|
|
|
7201
7193
|
permission: (j.permission ?? "").slice(0, 4),
|
|
7202
7194
|
detail: (j.arguments ? JSON.stringify(j.arguments) : j.reason ?? "").replace(/\s+/g, " "),
|
|
7203
7195
|
dlp: !!j.dlp,
|
|
7204
|
-
burst: !!j.rate_limit_burst,
|
|
7205
7196
|
source: "local",
|
|
7206
7197
|
session: j.session_id,
|
|
7207
7198
|
agent: j.agent_name,
|
|
@@ -7306,6 +7297,19 @@ function LivePanel({ active: active2 }) {
|
|
|
7306
7297
|
usePoll(() => {
|
|
7307
7298
|
if (!paused()) guard.reload();
|
|
7308
7299
|
}, 6e4, active2);
|
|
7300
|
+
useEffect2(() => {
|
|
7301
|
+
if (!detail) return;
|
|
7302
|
+
setDetailCloud([]);
|
|
7303
|
+
setDetailScroll(0);
|
|
7304
|
+
let alive = true;
|
|
7305
|
+
api.audit.list({ session_id: detail.id, limit: 100 }).then((r) => {
|
|
7306
|
+
if (alive) setDetailCloud(r.entries.map(cloudItem));
|
|
7307
|
+
}).catch(() => {
|
|
7308
|
+
});
|
|
7309
|
+
return () => {
|
|
7310
|
+
alive = false;
|
|
7311
|
+
};
|
|
7312
|
+
}, [detail]);
|
|
7309
7313
|
const [tick, setTick] = useState2(0);
|
|
7310
7314
|
useEffect2(() => {
|
|
7311
7315
|
if (!active2 || frozen) return;
|
|
@@ -7325,37 +7329,6 @@ function LivePanel({ active: active2 }) {
|
|
|
7325
7329
|
}
|
|
7326
7330
|
const cloudDeduped = cloudBuf.filter((e) => !localKeys.has(`${e.tool}|${e.decision}|${Math.round(e.at / 6e4)}`));
|
|
7327
7331
|
const mergedAll = [...cloudDeduped, ...localBuf].sort((a, b) => a.at - b.at);
|
|
7328
|
-
const localSess = /* @__PURE__ */ new Map();
|
|
7329
|
-
for (const e of localBuf) {
|
|
7330
|
-
if (!e.session) continue;
|
|
7331
|
-
const cur = localSess.get(e.session) ?? { agent: e.agent ?? "local agent", calls: 0, denies: 0, dlp: 0, lastAt: 0 };
|
|
7332
|
-
cur.calls++;
|
|
7333
|
-
if (e.decision !== "ALLOW") cur.denies++;
|
|
7334
|
-
if (e.dlp) cur.dlp++;
|
|
7335
|
-
cur.lastAt = Math.max(cur.lastAt, e.at);
|
|
7336
|
-
if (e.agent) cur.agent = e.agent;
|
|
7337
|
-
localSess.set(e.session, cur);
|
|
7338
|
-
}
|
|
7339
|
-
const combinedSessAll = [
|
|
7340
|
-
...[...localSess.entries()].sort((a, b) => b[1].lastAt - a[1].lastAt).map(([id, v]) => ({ source: "local", id, agent: v.agent, calls: v.calls, denies: v.denies, dlp: v.dlp, lastAt: v.lastAt, isMe: ring?.session === id })),
|
|
7341
|
-
...(sessions.data?.agents ?? []).filter((a) => !localSess.has(a.session_id)).sort((a, b) => Date.parse(b.last_seen_at) - Date.parse(a.last_seen_at)).map((a) => ({
|
|
7342
|
-
source: "cloud",
|
|
7343
|
-
id: a.session_id,
|
|
7344
|
-
agent: a.agent_name ?? a.session_id,
|
|
7345
|
-
calls: a.total_calls,
|
|
7346
|
-
denies: a.denied_calls,
|
|
7347
|
-
dlp: a.dlp_events,
|
|
7348
|
-
lastAt: Date.parse(a.last_seen_at),
|
|
7349
|
-
trust: a.trust_score,
|
|
7350
|
-
isMe: false
|
|
7351
|
-
}))
|
|
7352
|
-
];
|
|
7353
|
-
const combinedSess = sessFilter === "all" ? combinedSessAll : combinedSessAll.filter((r) => sessStatus(r.lastAt, nowMs) === sessFilter);
|
|
7354
|
-
const localSessIds = new Set(localSess.keys());
|
|
7355
|
-
if (ring?.session) localSessIds.add(ring.session);
|
|
7356
|
-
const isLoc = (e) => e.source === "local" || !!e.session && localSessIds.has(e.session);
|
|
7357
|
-
const filtered = filter === "all" ? mergedAll : mergedAll.filter((e) => filter === "local" ? isLoc(e) : !isLoc(e));
|
|
7358
|
-
const visibleDesc = filtered.slice(-STREAM_KEEP).reverse();
|
|
7359
7332
|
const points = tsData?.timeseries ?? [];
|
|
7360
7333
|
const nowSlot = Math.floor(nowMs / 1e4);
|
|
7361
7334
|
const slotCounts = new Array(60).fill(0);
|
|
@@ -7387,6 +7360,36 @@ function LivePanel({ active: active2 }) {
|
|
|
7387
7360
|
const latMed = sortedLat.length ? sortedLat[Math.floor(sortedLat.length / 2)] : 0;
|
|
7388
7361
|
const latHotAt = Math.max(2e3, latMed * 2.5);
|
|
7389
7362
|
const backingOff = Date.now() < pausedUntil.current;
|
|
7363
|
+
const sess = (sessions.data?.agents ?? []).slice().sort((a, b) => Date.parse(b.last_seen_at) - Date.parse(a.last_seen_at));
|
|
7364
|
+
const sessCounts = sessions.data?.counts;
|
|
7365
|
+
const localSess = /* @__PURE__ */ new Map();
|
|
7366
|
+
for (const e of localBuf) {
|
|
7367
|
+
if (!e.session) continue;
|
|
7368
|
+
const cur = localSess.get(e.session) ?? { agent: e.agent ?? "local agent", calls: 0, denies: 0, lastAt: 0 };
|
|
7369
|
+
cur.calls++;
|
|
7370
|
+
if (e.decision !== "ALLOW") cur.denies++;
|
|
7371
|
+
cur.lastAt = Math.max(cur.lastAt, e.at);
|
|
7372
|
+
if (e.agent) cur.agent = e.agent;
|
|
7373
|
+
localSess.set(e.session, cur);
|
|
7374
|
+
}
|
|
7375
|
+
const combinedSess = [
|
|
7376
|
+
...[...localSess.entries()].sort((a, b) => b[1].lastAt - a[1].lastAt).map(([id, v]) => ({ source: "local", id, agent: v.agent, calls: v.calls, denies: v.denies, lastAt: v.lastAt, isMe: ring?.session === id })),
|
|
7377
|
+
...sess.filter((a) => !localSess.has(a.session_id)).map((a) => ({
|
|
7378
|
+
source: "cloud",
|
|
7379
|
+
id: a.session_id,
|
|
7380
|
+
agent: a.agent_name ?? a.session_id,
|
|
7381
|
+
calls: a.total_calls,
|
|
7382
|
+
denies: a.denied_calls,
|
|
7383
|
+
lastAt: Date.parse(a.last_seen_at),
|
|
7384
|
+
trust: a.trust_score,
|
|
7385
|
+
isMe: false
|
|
7386
|
+
}))
|
|
7387
|
+
];
|
|
7388
|
+
const localSessIds = new Set(localSess.keys());
|
|
7389
|
+
if (ring?.session) localSessIds.add(ring.session);
|
|
7390
|
+
const isLoc = (e) => e.source === "local" || !!e.session && localSessIds.has(e.session);
|
|
7391
|
+
const filtered = filter === "all" ? mergedAll : mergedAll.filter((e) => filter === "local" ? isLoc(e) : !isLoc(e));
|
|
7392
|
+
const visibleDesc = filtered.slice(-STREAM_KEEP).reverse();
|
|
7390
7393
|
const chartH = rows >= 36 ? 6 : 4;
|
|
7391
7394
|
const colH = rows >= 32 ? 7 : 5;
|
|
7392
7395
|
const streamRows = Math.max(4, rows - 5 - (1 + chartH) - colH);
|
|
@@ -7394,25 +7397,14 @@ function LivePanel({ active: active2 }) {
|
|
|
7394
7397
|
const leftW = Math.floor(innerW * 0.55);
|
|
7395
7398
|
const rightW = innerW - leftW - 2;
|
|
7396
7399
|
const colW = Math.max(20, Math.floor((innerW - 4) / 3));
|
|
7400
|
+
const pickable = combinedSess.slice(0, colH - 1);
|
|
7397
7401
|
const maxScroll = Math.max(0, visibleDesc.length - streamRows);
|
|
7398
7402
|
const clampedScroll = Math.min(scroll, maxScroll);
|
|
7399
7403
|
const windowed = visibleDesc.slice(clampedScroll, clampedScroll + streamRows);
|
|
7400
|
-
const
|
|
7401
|
-
const selected = combinedSess[selIdx];
|
|
7402
|
-
useEffect2(() => {
|
|
7403
|
-
if (mode !== "sessions" || !selected?.id) return;
|
|
7404
|
-
setTlScroll(0);
|
|
7405
|
-
const t = setTimeout(() => {
|
|
7406
|
-
api.audit.list({ session_id: selected.id, limit: 100 }).then((r) => {
|
|
7407
|
-
if (!frozenRef.current) setDetailCloud(r.entries.map(cloudItem));
|
|
7408
|
-
}).catch(() => setDetailCloud([]));
|
|
7409
|
-
}, 400);
|
|
7410
|
-
return () => clearTimeout(t);
|
|
7411
|
-
}, [mode, selected?.id]);
|
|
7412
|
-
const detailEntries = selected ? (() => {
|
|
7404
|
+
const detailEntries = detail ? (() => {
|
|
7413
7405
|
const seen = /* @__PURE__ */ new Set();
|
|
7414
7406
|
const all = [];
|
|
7415
|
-
for (const e of [...detailCloud, ...mergedAll.filter((x) => x.session ===
|
|
7407
|
+
for (const e of [...detailCloud, ...mergedAll.filter((x) => x.session === detail.id)]) {
|
|
7416
7408
|
const key = `${e.at}:${e.tool}:${e.decision}`;
|
|
7417
7409
|
if (seen.has(key)) continue;
|
|
7418
7410
|
seen.add(key);
|
|
@@ -7422,30 +7414,40 @@ function LivePanel({ active: active2 }) {
|
|
|
7422
7414
|
})() : [];
|
|
7423
7415
|
useInput(
|
|
7424
7416
|
(input, key) => {
|
|
7417
|
+
if (mode === "detail") {
|
|
7418
|
+
const maxD = Math.max(0, detailEntries.length - (rows - 10));
|
|
7419
|
+
if (key.leftArrow) {
|
|
7420
|
+
setMode("stream");
|
|
7421
|
+
setDetail(null);
|
|
7422
|
+
} else if (key.upArrow) setDetailScroll((n) => Math.max(0, n - 1));
|
|
7423
|
+
else if (key.downArrow) setDetailScroll((n) => Math.min(maxD, n + 1));
|
|
7424
|
+
else if (key.pageUp) setDetailScroll((n) => Math.max(0, n - 10));
|
|
7425
|
+
else if (key.pageDown) setDetailScroll((n) => Math.min(maxD, n + 10));
|
|
7426
|
+
return;
|
|
7427
|
+
}
|
|
7428
|
+
if (mode === "pick") {
|
|
7429
|
+
if (key.upArrow) setPickIdx((n) => Math.max(0, n - 1));
|
|
7430
|
+
else if (key.downArrow) setPickIdx((n) => Math.min(pickable.length - 1, n + 1));
|
|
7431
|
+
else if (key.return) {
|
|
7432
|
+
const row = pickable[pickIdx];
|
|
7433
|
+
if (row) {
|
|
7434
|
+
setDetail(row);
|
|
7435
|
+
setMode("detail");
|
|
7436
|
+
}
|
|
7437
|
+
} else if (input === "s" || key.leftArrow) setMode("stream");
|
|
7438
|
+
return;
|
|
7439
|
+
}
|
|
7425
7440
|
if (input === " ") {
|
|
7426
7441
|
setFrozen((f) => !f);
|
|
7427
7442
|
return;
|
|
7428
7443
|
}
|
|
7429
7444
|
if (frozen) return;
|
|
7430
|
-
if (mode === "sessions") {
|
|
7431
|
-
if (key.leftArrow) {
|
|
7432
|
-
setMode("stream");
|
|
7433
|
-
setDetailCloud([]);
|
|
7434
|
-
} else if (key.upArrow) setPickIdx((n) => Math.max(0, n - 1));
|
|
7435
|
-
else if (key.downArrow) setPickIdx((n) => Math.min(combinedSess.length - 1, n + 1));
|
|
7436
|
-
else if (input === "f") {
|
|
7437
|
-
setSessFilter((f) => SESS_FILTERS[(SESS_FILTERS.indexOf(f) + 1) % SESS_FILTERS.length]);
|
|
7438
|
-
setPickIdx(0);
|
|
7439
|
-
} else if (key.pageUp) setTlScroll((n) => Math.max(0, n - 10));
|
|
7440
|
-
else if (key.pageDown) setTlScroll((n) => n + 10);
|
|
7441
|
-
return;
|
|
7442
|
-
}
|
|
7443
7445
|
if (input === "f") {
|
|
7444
7446
|
setFilter((f) => FILTERS[(FILTERS.indexOf(f) + 1) % FILTERS.length]);
|
|
7445
7447
|
setScroll(0);
|
|
7446
7448
|
} else if (input === "s") {
|
|
7447
7449
|
setPickIdx(0);
|
|
7448
|
-
setMode("
|
|
7450
|
+
setMode("pick");
|
|
7449
7451
|
} else if (key.downArrow) setScroll((n) => Math.min(maxScroll, n + 1));
|
|
7450
7452
|
else if (key.upArrow) setScroll((n) => Math.max(0, n - 1));
|
|
7451
7453
|
else if (key.pageDown) setScroll((n) => Math.min(maxScroll, n + streamRows));
|
|
@@ -7453,6 +7455,8 @@ function LivePanel({ active: active2 }) {
|
|
|
7453
7455
|
},
|
|
7454
7456
|
{ isActive: active2 }
|
|
7455
7457
|
);
|
|
7458
|
+
const sessionDot = (st) => st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\u25CC", color: theme.warn } : { ch: "\u25CB", color: theme.dim };
|
|
7459
|
+
void sessionDot;
|
|
7456
7460
|
if (s === null && localOn === null) {
|
|
7457
7461
|
return /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7458
7462
|
spin,
|
|
@@ -7464,89 +7468,69 @@ function LivePanel({ active: active2 }) {
|
|
|
7464
7468
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` ${spin} up ${fmtUp(nowMs - startRef.current)} \xB7 ${hhmmss(nowMs)} \xB7 api ${latNow}ms ` }),
|
|
7465
7469
|
frozen ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#123d1f", color: "#7bd88f", bold: true, children: " \u23F5 COPY MODE \u2014 screen frozen, select & copy freely \xB7 space resume " }) : backingOff ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d2a12", color: "#ffb454", bold: true, children: " RATE LIMITED \xB7 backing off " }) : lastDeny ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d1220", color: "#ff6b6b", bold: true, children: ` \u26A0 ${hhmmss(lastDeny.at)} ${lastDeny.tool} DENIED ` }) : /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#4f6db8", children: " \u2713 clean " })
|
|
7466
7470
|
] });
|
|
7467
|
-
if (mode === "
|
|
7468
|
-
const listW = Math.min(44, Math.floor(innerW * 0.38));
|
|
7469
|
-
const detailW = innerW - listW - 2;
|
|
7470
|
-
const bodyH = rows - 4;
|
|
7471
|
-
const listRows = bodyH - 1;
|
|
7471
|
+
if (mode === "detail" && detail) {
|
|
7472
7472
|
const d = detailEntries;
|
|
7473
7473
|
const allow = d.filter((e) => e.decision === "ALLOW").length;
|
|
7474
7474
|
const deny = d.length - allow;
|
|
7475
7475
|
const dlpN = d.filter((e) => e.dlp).length;
|
|
7476
|
-
const burstN = d.filter((e) => e.burst).length;
|
|
7477
7476
|
const evals = d.filter((e) => e.evalMs != null).map((e) => e.evalMs);
|
|
7478
7477
|
const evalAvg = evals.length ? Math.round(evals.reduce((a, b) => a + b, 0) / evals.length) : null;
|
|
7479
7478
|
const tCounts = /* @__PURE__ */ new Map();
|
|
7480
|
-
|
|
7481
|
-
|
|
7479
|
+
const pCounts = /* @__PURE__ */ new Map();
|
|
7480
|
+
for (const e of d) {
|
|
7481
|
+
tCounts.set(e.tool, (tCounts.get(e.tool) ?? 0) + 1);
|
|
7482
|
+
if (e.permission) pCounts.set(e.permission, (pCounts.get(e.permission) ?? 0) + 1);
|
|
7483
|
+
}
|
|
7484
|
+
const top = [...tCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 6);
|
|
7485
|
+
const perms = [...pCounts.entries()].sort((a, b) => b[1] - a[1]);
|
|
7482
7486
|
const first = d[d.length - 1]?.at;
|
|
7483
7487
|
const last = d[0]?.at;
|
|
7484
|
-
const tlRows = Math.max(
|
|
7485
|
-
const
|
|
7486
|
-
const
|
|
7487
|
-
const tl = d.slice(
|
|
7488
|
+
const tlRows = Math.max(4, rows - 10);
|
|
7489
|
+
const maxD = Math.max(0, d.length - tlRows);
|
|
7490
|
+
const dScroll = Math.min(detailScroll, maxD);
|
|
7491
|
+
const tl = d.slice(dScroll, dScroll + tlRows);
|
|
7488
7492
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
7489
7493
|
titleBar,
|
|
7490
|
-
/* @__PURE__ */
|
|
7491
|
-
|
|
7492
|
-
/* @__PURE__ */
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
|
|
7496
|
-
|
|
7497
|
-
|
|
7498
|
-
|
|
7499
|
-
|
|
7500
|
-
|
|
7501
|
-
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
|
|
7509
|
-
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
] }),
|
|
7526
|
-
/* @__PURE__ */ jsxs2(Box2, { marginTop: 1, children: [
|
|
7527
|
-
/* @__PURE__ */ jsx2(Stat, { label: "Total", value: d.length }),
|
|
7528
|
-
/* @__PURE__ */ jsx2(Stat, { label: "Allowed", value: allow, color: theme.ok }),
|
|
7529
|
-
/* @__PURE__ */ jsx2(Stat, { label: "Denied", value: deny, color: deny ? theme.bad : void 0 }),
|
|
7530
|
-
/* @__PURE__ */ jsx2(Stat, { label: "DLP", value: dlpN, color: dlpN ? theme.bad : void 0 }),
|
|
7531
|
-
/* @__PURE__ */ jsx2(Stat, { label: "Rate limit", value: burstN, color: burstN ? theme.warn : void 0 }),
|
|
7532
|
-
/* @__PURE__ */ jsx2(Stat, { label: "Avg eval", value: evalAvg != null ? `${evalAvg}ms` : "\u2014" }),
|
|
7533
|
-
selected.trust != null ? /* @__PURE__ */ jsx2(Stat, { label: "Trust", value: `${selected.trust}/100`, color: theme.accent }) : null
|
|
7534
|
-
] }),
|
|
7535
|
-
/* @__PURE__ */ jsx2(Text2, { wrap: "truncate", color: theme.dim, children: `first ${first ? `${hhmmss(first)} (${ago(first)} ago)` : "\u2014"} \xB7 last ${last ? `${hhmmss(last)} (${ago(last)} ago)` : "\u2014"} \xB7 tools ${top.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"}` }),
|
|
7536
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "TIMELINE", extra: `${d.length} events \xB7 newest first${tScroll ? ` \xB7 \u25BC${tScroll}` : ""}`, width: detailW }),
|
|
7537
|
-
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", overflow: "hidden", flexGrow: 1, children: [
|
|
7538
|
-
tl.length === 0 ? /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7539
|
-
spin,
|
|
7540
|
-
" loading history\u2026"
|
|
7541
|
-
] }) : null,
|
|
7542
|
-
tl.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e) }, e.id))
|
|
7543
|
-
] })
|
|
7544
|
-
] }) })
|
|
7494
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7495
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#AAC8FA", bold: true, children: ` SESSION ${detail.id.slice(0, 8)} ` }),
|
|
7496
|
+
/* @__PURE__ */ jsx2(Text2, { color: detail.source === "local" ? theme.ok : "#4f6db8", children: " " + detail.source.toUpperCase() }),
|
|
7497
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, children: " " + truncate2(detail.isMe ? detail.agent + " (this machine)" : detail.agent, 34) }),
|
|
7498
|
+
detail.trust != null ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ` trust ${detail.trust}/100` }) : null,
|
|
7499
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2190 back" })
|
|
7500
|
+
] }),
|
|
7501
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7502
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 calls " }),
|
|
7503
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, children: d.length }),
|
|
7504
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 allow " }),
|
|
7505
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: allow }),
|
|
7506
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 deny " }),
|
|
7507
|
+
/* @__PURE__ */ jsx2(Text2, { color: deny ? theme.bad : theme.dim, bold: deny > 0, children: deny }),
|
|
7508
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 dlp " }),
|
|
7509
|
+
/* @__PURE__ */ jsx2(Text2, { color: dlpN ? theme.bad : theme.dim, children: dlpN }),
|
|
7510
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 avg eval " }),
|
|
7511
|
+
/* @__PURE__ */ jsx2(Text2, { children: evalAvg != null ? `${evalAvg}ms` : "\u2014" }),
|
|
7512
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 first " }),
|
|
7513
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: first ? `${hhmmss(first)} (${ago(first)} ago)` : "\u2014" }),
|
|
7514
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 last " }),
|
|
7515
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: last ? `${hhmmss(last)} (${ago(last)} ago)` : "\u2014" }),
|
|
7516
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
7517
|
+
] }),
|
|
7518
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7519
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 tools " }),
|
|
7520
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: top.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014" }),
|
|
7521
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 perms " }),
|
|
7522
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: perms.map(([p, c2]) => `${p}\xD7${c2}`).join(" ") || "\u2014" }),
|
|
7523
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
7524
|
+
] }),
|
|
7525
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "TIMELINE", extra: `${d.length} calls \xB7 newest first${dScroll ? ` \xB7 \u25BC${dScroll} older` : ""} \xB7 \u2191\u2193 scroll \xB7 \u2190 back`, width: innerW }),
|
|
7526
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: tlRows, overflow: "hidden", children: [
|
|
7527
|
+
tl.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: detailCloud.length === 0 ? spin + " loading history\u2026" : "no entries" }) : null,
|
|
7528
|
+
tl.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e) }, e.id))
|
|
7545
7529
|
] }),
|
|
7546
7530
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7547
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: "
|
|
7548
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` ${
|
|
7549
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " \u2191\u2193
|
|
7531
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " SESSION " }),
|
|
7532
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` ${detail.id} ` }),
|
|
7533
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " \u2191\u2193 scroll \xB7 \u2190 back \xB7 esc menu " })
|
|
7550
7534
|
] })
|
|
7551
7535
|
] });
|
|
7552
7536
|
}
|
|
@@ -7568,7 +7552,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7568
7552
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 ghost " }),
|
|
7569
7553
|
/* @__PURE__ */ jsx2(Text2, { color: gh?.mode === "on" ? theme.ok : theme.dim, children: gh?.mode ?? "?" }),
|
|
7570
7554
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 sess " }),
|
|
7571
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children:
|
|
7555
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: `${combinedSess.length} (${sessCounts?.active ?? 0} live)` }),
|
|
7572
7556
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 hooks " }),
|
|
7573
7557
|
/* @__PURE__ */ jsx2(Text2, { color: guard.data?.up_to_date ? theme.ok : theme.warn, children: guard.data ? `v${guard.data.installed ?? "?"}${guard.data.up_to_date ? "" : "\u2192v" + guard.data.latest} ${guard.data.device_count}dev` : "\xB7" }),
|
|
7574
7558
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
@@ -7607,13 +7591,14 @@ function LivePanel({ active: active2 }) {
|
|
|
7607
7591
|
] })
|
|
7608
7592
|
] }),
|
|
7609
7593
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, overflow: "hidden", children: [
|
|
7610
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: "
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
const
|
|
7614
|
-
|
|
7615
|
-
|
|
7616
|
-
|
|
7594
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: mode === "pick" ? "\u2191\u2193 pick \xB7 enter open" : `${combinedSess.length} \xB7 s = inspect`, width: colW }),
|
|
7595
|
+
pickable.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "scanning\u2026" }) : null,
|
|
7596
|
+
pickable.map((r, i) => {
|
|
7597
|
+
const freshDot = nowMs - r.lastAt < 9e4;
|
|
7598
|
+
const sel = mode === "pick" && i === pickIdx;
|
|
7599
|
+
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", backgroundColor: sel ? "#1c2f63" : void 0, bold: sel || r.isMe, children: [
|
|
7600
|
+
/* @__PURE__ */ jsxs2(Text2, { color: sel ? theme.accentBright : freshDot ? theme.ok : theme.dim, children: [
|
|
7601
|
+
sel ? "\u25B8" : freshDot ? "\u25CF" : "\u25CB",
|
|
7617
7602
|
" "
|
|
7618
7603
|
] }),
|
|
7619
7604
|
/* @__PURE__ */ jsx2(Text2, { color: r.source === "local" ? theme.ok : "#4f6db8", children: r.source === "local" ? "LOC " : "CLD " }),
|
|
@@ -7626,7 +7611,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7626
7611
|
String(r.denies).padStart(3),
|
|
7627
7612
|
"d "
|
|
7628
7613
|
] }),
|
|
7629
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ago(r.lastAt) })
|
|
7614
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: (r.trust != null ? `t${r.trust} ` : "") + ago(r.lastAt) })
|
|
7630
7615
|
] }, r.id);
|
|
7631
7616
|
})
|
|
7632
7617
|
] }),
|
|
@@ -7663,12 +7648,12 @@ function LivePanel({ active: active2 }) {
|
|
|
7663
7648
|
] }),
|
|
7664
7649
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7665
7650
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " LIVE " }),
|
|
7666
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` local ${localOn ? "on" : "off"} \xB7 ${localBuf.length} loc/${
|
|
7651
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` local ${localOn ? "on" : "off"} \xB7 ${localBuf.length} loc/${cloudBuf.length} cld \xB7 top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} ` }),
|
|
7667
7652
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " space copy-mode \xB7 f filter \xB7 s sessions \xB7 esc menu \xB7 q quit " })
|
|
7668
7653
|
] })
|
|
7669
7654
|
] });
|
|
7670
7655
|
}
|
|
7671
|
-
var SPIN, BG, DIM_FLOOR, LOCAL_LOG, RING, STREAM_KEEP, hhmmss, fmtUp, FILTERS
|
|
7656
|
+
var SPIN, BG, DIM_FLOOR, LOCAL_LOG, RING, STREAM_KEEP, hhmmss, fmtUp, FILTERS;
|
|
7672
7657
|
var init_Live = __esm({
|
|
7673
7658
|
"src/tui/panels/Live.tsx"() {
|
|
7674
7659
|
"use strict";
|
|
@@ -7691,8 +7676,6 @@ var init_Live = __esm({
|
|
|
7691
7676
|
return `${p(Math.floor(s / 3600))}:${p(Math.floor(s % 3600 / 60))}:${p(s % 60)}`;
|
|
7692
7677
|
};
|
|
7693
7678
|
FILTERS = ["all", "local", "cloud"];
|
|
7694
|
-
SESS_FILTERS = ["all", "active", "idle", "ended"];
|
|
7695
|
-
sessStatus = (lastAt, now) => now - lastAt < 9e4 ? "active" : now - lastAt < 3e5 ? "idle" : "ended";
|
|
7696
7679
|
}
|
|
7697
7680
|
});
|
|
7698
7681
|
|
package/dist/tui/index.js
CHANGED
|
@@ -443,7 +443,7 @@ function usePoll(reload, intervalMs, enabled = true) {
|
|
|
443
443
|
}
|
|
444
444
|
|
|
445
445
|
// src/tui/panels/Live.tsx
|
|
446
|
-
import {
|
|
446
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
447
447
|
var SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
448
448
|
var BG = "#12234f";
|
|
449
449
|
var DIM_FLOOR = "#233457";
|
|
@@ -483,7 +483,6 @@ function cloudItem(e) {
|
|
|
483
483
|
permission: (e.permission ?? "").slice(0, 4),
|
|
484
484
|
detail: (e.arguments_summary ? JSON.stringify(e.arguments_summary) : e.reason ?? "").replace(/\s+/g, " "),
|
|
485
485
|
dlp: !!e.dlp_matches?.length,
|
|
486
|
-
burst: !!e.rate_limit_burst,
|
|
487
486
|
source: "cloud",
|
|
488
487
|
session: e.session_id ?? void 0,
|
|
489
488
|
agent: e.agent_name ?? void 0,
|
|
@@ -539,12 +538,6 @@ function PaneTitle({ label, extra, width }) {
|
|
|
539
538
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: tail + "\u2500".repeat(Math.max(0, width - used)) })
|
|
540
539
|
] });
|
|
541
540
|
}
|
|
542
|
-
function Stat({ label, value, color }) {
|
|
543
|
-
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginRight: 3, children: [
|
|
544
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: label }),
|
|
545
|
-
/* @__PURE__ */ jsx2(Text2, { bold: true, color, children: String(value) })
|
|
546
|
-
] });
|
|
547
|
-
}
|
|
548
541
|
function StreamLine({ e, loc }) {
|
|
549
542
|
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
550
543
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
@@ -560,14 +553,11 @@ function StreamLine({ e, loc }) {
|
|
|
560
553
|
/* @__PURE__ */ jsx2(Text2, { color: e.evalMs != null && e.evalMs > 500 ? theme.warn : theme.dim, children: (e.evalMs != null ? `${e.evalMs}ms` : "\u2014").padEnd(7) }),
|
|
561
554
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: truncate(e.agent ?? "-", 11).padEnd(12) }),
|
|
562
555
|
e.dlp ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: "DLP! " }) : null,
|
|
563
|
-
e.burst ? /* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: "BURST " }) : null,
|
|
564
556
|
e.rule && e.decision !== "ALLOW" ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: truncate(e.rule, 14) + " " }) : null,
|
|
565
557
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.detail })
|
|
566
558
|
] });
|
|
567
559
|
}
|
|
568
560
|
var FILTERS = ["all", "local", "cloud"];
|
|
569
|
-
var SESS_FILTERS = ["all", "active", "idle", "ended"];
|
|
570
|
-
var sessStatus = (lastAt, now) => now - lastAt < 9e4 ? "active" : now - lastAt < 3e5 ? "idle" : "ended";
|
|
571
561
|
function LivePanel({ active: active2 }) {
|
|
572
562
|
const [s, setS] = useState2(null);
|
|
573
563
|
const [tsData, setTsData] = useState2(null);
|
|
@@ -580,10 +570,10 @@ function LivePanel({ active: active2 }) {
|
|
|
580
570
|
const [filter, setFilter] = useState2("all");
|
|
581
571
|
const [scroll, setScroll] = useState2(0);
|
|
582
572
|
const [mode, setMode] = useState2("stream");
|
|
583
|
-
const [sessFilter, setSessFilter] = useState2("all");
|
|
584
573
|
const [pickIdx, setPickIdx] = useState2(0);
|
|
574
|
+
const [detail, setDetail] = useState2(null);
|
|
585
575
|
const [detailCloud, setDetailCloud] = useState2([]);
|
|
586
|
-
const [
|
|
576
|
+
const [detailScroll, setDetailScroll] = useState2(0);
|
|
587
577
|
const seenRef = useRef2(/* @__PURE__ */ new Set());
|
|
588
578
|
const lastLocalTs = useRef2(0);
|
|
589
579
|
const pausedUntil = useRef2(0);
|
|
@@ -628,7 +618,6 @@ function LivePanel({ active: active2 }) {
|
|
|
628
618
|
permission: (j.permission ?? "").slice(0, 4),
|
|
629
619
|
detail: (j.arguments ? JSON.stringify(j.arguments) : j.reason ?? "").replace(/\s+/g, " "),
|
|
630
620
|
dlp: !!j.dlp,
|
|
631
|
-
burst: !!j.rate_limit_burst,
|
|
632
621
|
source: "local",
|
|
633
622
|
session: j.session_id,
|
|
634
623
|
agent: j.agent_name,
|
|
@@ -733,6 +722,19 @@ function LivePanel({ active: active2 }) {
|
|
|
733
722
|
usePoll(() => {
|
|
734
723
|
if (!paused()) guard.reload();
|
|
735
724
|
}, 6e4, active2);
|
|
725
|
+
useEffect2(() => {
|
|
726
|
+
if (!detail) return;
|
|
727
|
+
setDetailCloud([]);
|
|
728
|
+
setDetailScroll(0);
|
|
729
|
+
let alive = true;
|
|
730
|
+
api.audit.list({ session_id: detail.id, limit: 100 }).then((r) => {
|
|
731
|
+
if (alive) setDetailCloud(r.entries.map(cloudItem));
|
|
732
|
+
}).catch(() => {
|
|
733
|
+
});
|
|
734
|
+
return () => {
|
|
735
|
+
alive = false;
|
|
736
|
+
};
|
|
737
|
+
}, [detail]);
|
|
736
738
|
const [tick, setTick] = useState2(0);
|
|
737
739
|
useEffect2(() => {
|
|
738
740
|
if (!active2 || frozen) return;
|
|
@@ -752,37 +754,6 @@ function LivePanel({ active: active2 }) {
|
|
|
752
754
|
}
|
|
753
755
|
const cloudDeduped = cloudBuf.filter((e) => !localKeys.has(`${e.tool}|${e.decision}|${Math.round(e.at / 6e4)}`));
|
|
754
756
|
const mergedAll = [...cloudDeduped, ...localBuf].sort((a, b) => a.at - b.at);
|
|
755
|
-
const localSess = /* @__PURE__ */ new Map();
|
|
756
|
-
for (const e of localBuf) {
|
|
757
|
-
if (!e.session) continue;
|
|
758
|
-
const cur = localSess.get(e.session) ?? { agent: e.agent ?? "local agent", calls: 0, denies: 0, dlp: 0, lastAt: 0 };
|
|
759
|
-
cur.calls++;
|
|
760
|
-
if (e.decision !== "ALLOW") cur.denies++;
|
|
761
|
-
if (e.dlp) cur.dlp++;
|
|
762
|
-
cur.lastAt = Math.max(cur.lastAt, e.at);
|
|
763
|
-
if (e.agent) cur.agent = e.agent;
|
|
764
|
-
localSess.set(e.session, cur);
|
|
765
|
-
}
|
|
766
|
-
const combinedSessAll = [
|
|
767
|
-
...[...localSess.entries()].sort((a, b) => b[1].lastAt - a[1].lastAt).map(([id, v]) => ({ source: "local", id, agent: v.agent, calls: v.calls, denies: v.denies, dlp: v.dlp, lastAt: v.lastAt, isMe: ring?.session === id })),
|
|
768
|
-
...(sessions.data?.agents ?? []).filter((a) => !localSess.has(a.session_id)).sort((a, b) => Date.parse(b.last_seen_at) - Date.parse(a.last_seen_at)).map((a) => ({
|
|
769
|
-
source: "cloud",
|
|
770
|
-
id: a.session_id,
|
|
771
|
-
agent: a.agent_name ?? a.session_id,
|
|
772
|
-
calls: a.total_calls,
|
|
773
|
-
denies: a.denied_calls,
|
|
774
|
-
dlp: a.dlp_events,
|
|
775
|
-
lastAt: Date.parse(a.last_seen_at),
|
|
776
|
-
trust: a.trust_score,
|
|
777
|
-
isMe: false
|
|
778
|
-
}))
|
|
779
|
-
];
|
|
780
|
-
const combinedSess = sessFilter === "all" ? combinedSessAll : combinedSessAll.filter((r) => sessStatus(r.lastAt, nowMs) === sessFilter);
|
|
781
|
-
const localSessIds = new Set(localSess.keys());
|
|
782
|
-
if (ring?.session) localSessIds.add(ring.session);
|
|
783
|
-
const isLoc = (e) => e.source === "local" || !!e.session && localSessIds.has(e.session);
|
|
784
|
-
const filtered = filter === "all" ? mergedAll : mergedAll.filter((e) => filter === "local" ? isLoc(e) : !isLoc(e));
|
|
785
|
-
const visibleDesc = filtered.slice(-STREAM_KEEP).reverse();
|
|
786
757
|
const points = tsData?.timeseries ?? [];
|
|
787
758
|
const nowSlot = Math.floor(nowMs / 1e4);
|
|
788
759
|
const slotCounts = new Array(60).fill(0);
|
|
@@ -814,6 +785,36 @@ function LivePanel({ active: active2 }) {
|
|
|
814
785
|
const latMed = sortedLat.length ? sortedLat[Math.floor(sortedLat.length / 2)] : 0;
|
|
815
786
|
const latHotAt = Math.max(2e3, latMed * 2.5);
|
|
816
787
|
const backingOff = Date.now() < pausedUntil.current;
|
|
788
|
+
const sess = (sessions.data?.agents ?? []).slice().sort((a, b) => Date.parse(b.last_seen_at) - Date.parse(a.last_seen_at));
|
|
789
|
+
const sessCounts = sessions.data?.counts;
|
|
790
|
+
const localSess = /* @__PURE__ */ new Map();
|
|
791
|
+
for (const e of localBuf) {
|
|
792
|
+
if (!e.session) continue;
|
|
793
|
+
const cur = localSess.get(e.session) ?? { agent: e.agent ?? "local agent", calls: 0, denies: 0, lastAt: 0 };
|
|
794
|
+
cur.calls++;
|
|
795
|
+
if (e.decision !== "ALLOW") cur.denies++;
|
|
796
|
+
cur.lastAt = Math.max(cur.lastAt, e.at);
|
|
797
|
+
if (e.agent) cur.agent = e.agent;
|
|
798
|
+
localSess.set(e.session, cur);
|
|
799
|
+
}
|
|
800
|
+
const combinedSess = [
|
|
801
|
+
...[...localSess.entries()].sort((a, b) => b[1].lastAt - a[1].lastAt).map(([id, v]) => ({ source: "local", id, agent: v.agent, calls: v.calls, denies: v.denies, lastAt: v.lastAt, isMe: ring?.session === id })),
|
|
802
|
+
...sess.filter((a) => !localSess.has(a.session_id)).map((a) => ({
|
|
803
|
+
source: "cloud",
|
|
804
|
+
id: a.session_id,
|
|
805
|
+
agent: a.agent_name ?? a.session_id,
|
|
806
|
+
calls: a.total_calls,
|
|
807
|
+
denies: a.denied_calls,
|
|
808
|
+
lastAt: Date.parse(a.last_seen_at),
|
|
809
|
+
trust: a.trust_score,
|
|
810
|
+
isMe: false
|
|
811
|
+
}))
|
|
812
|
+
];
|
|
813
|
+
const localSessIds = new Set(localSess.keys());
|
|
814
|
+
if (ring?.session) localSessIds.add(ring.session);
|
|
815
|
+
const isLoc = (e) => e.source === "local" || !!e.session && localSessIds.has(e.session);
|
|
816
|
+
const filtered = filter === "all" ? mergedAll : mergedAll.filter((e) => filter === "local" ? isLoc(e) : !isLoc(e));
|
|
817
|
+
const visibleDesc = filtered.slice(-STREAM_KEEP).reverse();
|
|
817
818
|
const chartH = rows >= 36 ? 6 : 4;
|
|
818
819
|
const colH = rows >= 32 ? 7 : 5;
|
|
819
820
|
const streamRows = Math.max(4, rows - 5 - (1 + chartH) - colH);
|
|
@@ -821,25 +822,14 @@ function LivePanel({ active: active2 }) {
|
|
|
821
822
|
const leftW = Math.floor(innerW * 0.55);
|
|
822
823
|
const rightW = innerW - leftW - 2;
|
|
823
824
|
const colW = Math.max(20, Math.floor((innerW - 4) / 3));
|
|
825
|
+
const pickable = combinedSess.slice(0, colH - 1);
|
|
824
826
|
const maxScroll = Math.max(0, visibleDesc.length - streamRows);
|
|
825
827
|
const clampedScroll = Math.min(scroll, maxScroll);
|
|
826
828
|
const windowed = visibleDesc.slice(clampedScroll, clampedScroll + streamRows);
|
|
827
|
-
const
|
|
828
|
-
const selected = combinedSess[selIdx];
|
|
829
|
-
useEffect2(() => {
|
|
830
|
-
if (mode !== "sessions" || !selected?.id) return;
|
|
831
|
-
setTlScroll(0);
|
|
832
|
-
const t = setTimeout(() => {
|
|
833
|
-
api.audit.list({ session_id: selected.id, limit: 100 }).then((r) => {
|
|
834
|
-
if (!frozenRef.current) setDetailCloud(r.entries.map(cloudItem));
|
|
835
|
-
}).catch(() => setDetailCloud([]));
|
|
836
|
-
}, 400);
|
|
837
|
-
return () => clearTimeout(t);
|
|
838
|
-
}, [mode, selected?.id]);
|
|
839
|
-
const detailEntries = selected ? (() => {
|
|
829
|
+
const detailEntries = detail ? (() => {
|
|
840
830
|
const seen = /* @__PURE__ */ new Set();
|
|
841
831
|
const all = [];
|
|
842
|
-
for (const e of [...detailCloud, ...mergedAll.filter((x) => x.session ===
|
|
832
|
+
for (const e of [...detailCloud, ...mergedAll.filter((x) => x.session === detail.id)]) {
|
|
843
833
|
const key = `${e.at}:${e.tool}:${e.decision}`;
|
|
844
834
|
if (seen.has(key)) continue;
|
|
845
835
|
seen.add(key);
|
|
@@ -849,30 +839,40 @@ function LivePanel({ active: active2 }) {
|
|
|
849
839
|
})() : [];
|
|
850
840
|
useInput(
|
|
851
841
|
(input, key) => {
|
|
842
|
+
if (mode === "detail") {
|
|
843
|
+
const maxD = Math.max(0, detailEntries.length - (rows - 10));
|
|
844
|
+
if (key.leftArrow) {
|
|
845
|
+
setMode("stream");
|
|
846
|
+
setDetail(null);
|
|
847
|
+
} else if (key.upArrow) setDetailScroll((n) => Math.max(0, n - 1));
|
|
848
|
+
else if (key.downArrow) setDetailScroll((n) => Math.min(maxD, n + 1));
|
|
849
|
+
else if (key.pageUp) setDetailScroll((n) => Math.max(0, n - 10));
|
|
850
|
+
else if (key.pageDown) setDetailScroll((n) => Math.min(maxD, n + 10));
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
if (mode === "pick") {
|
|
854
|
+
if (key.upArrow) setPickIdx((n) => Math.max(0, n - 1));
|
|
855
|
+
else if (key.downArrow) setPickIdx((n) => Math.min(pickable.length - 1, n + 1));
|
|
856
|
+
else if (key.return) {
|
|
857
|
+
const row = pickable[pickIdx];
|
|
858
|
+
if (row) {
|
|
859
|
+
setDetail(row);
|
|
860
|
+
setMode("detail");
|
|
861
|
+
}
|
|
862
|
+
} else if (input === "s" || key.leftArrow) setMode("stream");
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
852
865
|
if (input === " ") {
|
|
853
866
|
setFrozen((f) => !f);
|
|
854
867
|
return;
|
|
855
868
|
}
|
|
856
869
|
if (frozen) return;
|
|
857
|
-
if (mode === "sessions") {
|
|
858
|
-
if (key.leftArrow) {
|
|
859
|
-
setMode("stream");
|
|
860
|
-
setDetailCloud([]);
|
|
861
|
-
} else if (key.upArrow) setPickIdx((n) => Math.max(0, n - 1));
|
|
862
|
-
else if (key.downArrow) setPickIdx((n) => Math.min(combinedSess.length - 1, n + 1));
|
|
863
|
-
else if (input === "f") {
|
|
864
|
-
setSessFilter((f) => SESS_FILTERS[(SESS_FILTERS.indexOf(f) + 1) % SESS_FILTERS.length]);
|
|
865
|
-
setPickIdx(0);
|
|
866
|
-
} else if (key.pageUp) setTlScroll((n) => Math.max(0, n - 10));
|
|
867
|
-
else if (key.pageDown) setTlScroll((n) => n + 10);
|
|
868
|
-
return;
|
|
869
|
-
}
|
|
870
870
|
if (input === "f") {
|
|
871
871
|
setFilter((f) => FILTERS[(FILTERS.indexOf(f) + 1) % FILTERS.length]);
|
|
872
872
|
setScroll(0);
|
|
873
873
|
} else if (input === "s") {
|
|
874
874
|
setPickIdx(0);
|
|
875
|
-
setMode("
|
|
875
|
+
setMode("pick");
|
|
876
876
|
} else if (key.downArrow) setScroll((n) => Math.min(maxScroll, n + 1));
|
|
877
877
|
else if (key.upArrow) setScroll((n) => Math.max(0, n - 1));
|
|
878
878
|
else if (key.pageDown) setScroll((n) => Math.min(maxScroll, n + streamRows));
|
|
@@ -880,6 +880,8 @@ function LivePanel({ active: active2 }) {
|
|
|
880
880
|
},
|
|
881
881
|
{ isActive: active2 }
|
|
882
882
|
);
|
|
883
|
+
const sessionDot = (st) => st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\u25CC", color: theme.warn } : { ch: "\u25CB", color: theme.dim };
|
|
884
|
+
void sessionDot;
|
|
883
885
|
if (s === null && localOn === null) {
|
|
884
886
|
return /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
885
887
|
spin,
|
|
@@ -891,89 +893,69 @@ function LivePanel({ active: active2 }) {
|
|
|
891
893
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` ${spin} up ${fmtUp(nowMs - startRef.current)} \xB7 ${hhmmss(nowMs)} \xB7 api ${latNow}ms ` }),
|
|
892
894
|
frozen ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#123d1f", color: "#7bd88f", bold: true, children: " \u23F5 COPY MODE \u2014 screen frozen, select & copy freely \xB7 space resume " }) : backingOff ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d2a12", color: "#ffb454", bold: true, children: " RATE LIMITED \xB7 backing off " }) : lastDeny ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d1220", color: "#ff6b6b", bold: true, children: ` \u26A0 ${hhmmss(lastDeny.at)} ${lastDeny.tool} DENIED ` }) : /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#4f6db8", children: " \u2713 clean " })
|
|
893
895
|
] });
|
|
894
|
-
if (mode === "
|
|
895
|
-
const listW = Math.min(44, Math.floor(innerW * 0.38));
|
|
896
|
-
const detailW = innerW - listW - 2;
|
|
897
|
-
const bodyH = rows - 4;
|
|
898
|
-
const listRows = bodyH - 1;
|
|
896
|
+
if (mode === "detail" && detail) {
|
|
899
897
|
const d = detailEntries;
|
|
900
898
|
const allow = d.filter((e) => e.decision === "ALLOW").length;
|
|
901
899
|
const deny = d.length - allow;
|
|
902
900
|
const dlpN = d.filter((e) => e.dlp).length;
|
|
903
|
-
const burstN = d.filter((e) => e.burst).length;
|
|
904
901
|
const evals = d.filter((e) => e.evalMs != null).map((e) => e.evalMs);
|
|
905
902
|
const evalAvg = evals.length ? Math.round(evals.reduce((a, b) => a + b, 0) / evals.length) : null;
|
|
906
903
|
const tCounts = /* @__PURE__ */ new Map();
|
|
907
|
-
|
|
908
|
-
|
|
904
|
+
const pCounts = /* @__PURE__ */ new Map();
|
|
905
|
+
for (const e of d) {
|
|
906
|
+
tCounts.set(e.tool, (tCounts.get(e.tool) ?? 0) + 1);
|
|
907
|
+
if (e.permission) pCounts.set(e.permission, (pCounts.get(e.permission) ?? 0) + 1);
|
|
908
|
+
}
|
|
909
|
+
const top = [...tCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 6);
|
|
910
|
+
const perms = [...pCounts.entries()].sort((a, b) => b[1] - a[1]);
|
|
909
911
|
const first = d[d.length - 1]?.at;
|
|
910
912
|
const last = d[0]?.at;
|
|
911
|
-
const tlRows = Math.max(
|
|
912
|
-
const
|
|
913
|
-
const
|
|
914
|
-
const tl = d.slice(
|
|
913
|
+
const tlRows = Math.max(4, rows - 10);
|
|
914
|
+
const maxD = Math.max(0, d.length - tlRows);
|
|
915
|
+
const dScroll = Math.min(detailScroll, maxD);
|
|
916
|
+
const tl = d.slice(dScroll, dScroll + tlRows);
|
|
915
917
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
916
918
|
titleBar,
|
|
917
|
-
/* @__PURE__ */
|
|
918
|
-
|
|
919
|
-
/* @__PURE__ */
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
] }),
|
|
953
|
-
/* @__PURE__ */ jsxs2(Box2, { marginTop: 1, children: [
|
|
954
|
-
/* @__PURE__ */ jsx2(Stat, { label: "Total", value: d.length }),
|
|
955
|
-
/* @__PURE__ */ jsx2(Stat, { label: "Allowed", value: allow, color: theme.ok }),
|
|
956
|
-
/* @__PURE__ */ jsx2(Stat, { label: "Denied", value: deny, color: deny ? theme.bad : void 0 }),
|
|
957
|
-
/* @__PURE__ */ jsx2(Stat, { label: "DLP", value: dlpN, color: dlpN ? theme.bad : void 0 }),
|
|
958
|
-
/* @__PURE__ */ jsx2(Stat, { label: "Rate limit", value: burstN, color: burstN ? theme.warn : void 0 }),
|
|
959
|
-
/* @__PURE__ */ jsx2(Stat, { label: "Avg eval", value: evalAvg != null ? `${evalAvg}ms` : "\u2014" }),
|
|
960
|
-
selected.trust != null ? /* @__PURE__ */ jsx2(Stat, { label: "Trust", value: `${selected.trust}/100`, color: theme.accent }) : null
|
|
961
|
-
] }),
|
|
962
|
-
/* @__PURE__ */ jsx2(Text2, { wrap: "truncate", color: theme.dim, children: `first ${first ? `${hhmmss(first)} (${ago(first)} ago)` : "\u2014"} \xB7 last ${last ? `${hhmmss(last)} (${ago(last)} ago)` : "\u2014"} \xB7 tools ${top.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"}` }),
|
|
963
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "TIMELINE", extra: `${d.length} events \xB7 newest first${tScroll ? ` \xB7 \u25BC${tScroll}` : ""}`, width: detailW }),
|
|
964
|
-
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", overflow: "hidden", flexGrow: 1, children: [
|
|
965
|
-
tl.length === 0 ? /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
966
|
-
spin,
|
|
967
|
-
" loading history\u2026"
|
|
968
|
-
] }) : null,
|
|
969
|
-
tl.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e) }, e.id))
|
|
970
|
-
] })
|
|
971
|
-
] }) })
|
|
919
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
920
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#AAC8FA", bold: true, children: ` SESSION ${detail.id.slice(0, 8)} ` }),
|
|
921
|
+
/* @__PURE__ */ jsx2(Text2, { color: detail.source === "local" ? theme.ok : "#4f6db8", children: " " + detail.source.toUpperCase() }),
|
|
922
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, children: " " + truncate(detail.isMe ? detail.agent + " (this machine)" : detail.agent, 34) }),
|
|
923
|
+
detail.trust != null ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ` trust ${detail.trust}/100` }) : null,
|
|
924
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2190 back" })
|
|
925
|
+
] }),
|
|
926
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
927
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 calls " }),
|
|
928
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, children: d.length }),
|
|
929
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 allow " }),
|
|
930
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: allow }),
|
|
931
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 deny " }),
|
|
932
|
+
/* @__PURE__ */ jsx2(Text2, { color: deny ? theme.bad : theme.dim, bold: deny > 0, children: deny }),
|
|
933
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 dlp " }),
|
|
934
|
+
/* @__PURE__ */ jsx2(Text2, { color: dlpN ? theme.bad : theme.dim, children: dlpN }),
|
|
935
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 avg eval " }),
|
|
936
|
+
/* @__PURE__ */ jsx2(Text2, { children: evalAvg != null ? `${evalAvg}ms` : "\u2014" }),
|
|
937
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 first " }),
|
|
938
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: first ? `${hhmmss(first)} (${ago(first)} ago)` : "\u2014" }),
|
|
939
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 last " }),
|
|
940
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: last ? `${hhmmss(last)} (${ago(last)} ago)` : "\u2014" }),
|
|
941
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
942
|
+
] }),
|
|
943
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
944
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 tools " }),
|
|
945
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: top.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014" }),
|
|
946
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 perms " }),
|
|
947
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: perms.map(([p, c2]) => `${p}\xD7${c2}`).join(" ") || "\u2014" }),
|
|
948
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
949
|
+
] }),
|
|
950
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "TIMELINE", extra: `${d.length} calls \xB7 newest first${dScroll ? ` \xB7 \u25BC${dScroll} older` : ""} \xB7 \u2191\u2193 scroll \xB7 \u2190 back`, width: innerW }),
|
|
951
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: tlRows, overflow: "hidden", children: [
|
|
952
|
+
tl.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: detailCloud.length === 0 ? spin + " loading history\u2026" : "no entries" }) : null,
|
|
953
|
+
tl.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e) }, e.id))
|
|
972
954
|
] }),
|
|
973
955
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
974
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: "
|
|
975
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` ${
|
|
976
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " \u2191\u2193
|
|
956
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " SESSION " }),
|
|
957
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` ${detail.id} ` }),
|
|
958
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " \u2191\u2193 scroll \xB7 \u2190 back \xB7 esc menu " })
|
|
977
959
|
] })
|
|
978
960
|
] });
|
|
979
961
|
}
|
|
@@ -995,7 +977,7 @@ function LivePanel({ active: active2 }) {
|
|
|
995
977
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 ghost " }),
|
|
996
978
|
/* @__PURE__ */ jsx2(Text2, { color: gh?.mode === "on" ? theme.ok : theme.dim, children: gh?.mode ?? "?" }),
|
|
997
979
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 sess " }),
|
|
998
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children:
|
|
980
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: `${combinedSess.length} (${sessCounts?.active ?? 0} live)` }),
|
|
999
981
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 hooks " }),
|
|
1000
982
|
/* @__PURE__ */ jsx2(Text2, { color: guard.data?.up_to_date ? theme.ok : theme.warn, children: guard.data ? `v${guard.data.installed ?? "?"}${guard.data.up_to_date ? "" : "\u2192v" + guard.data.latest} ${guard.data.device_count}dev` : "\xB7" }),
|
|
1001
983
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
@@ -1034,13 +1016,14 @@ function LivePanel({ active: active2 }) {
|
|
|
1034
1016
|
] })
|
|
1035
1017
|
] }),
|
|
1036
1018
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, overflow: "hidden", children: [
|
|
1037
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: "
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
const
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1019
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: mode === "pick" ? "\u2191\u2193 pick \xB7 enter open" : `${combinedSess.length} \xB7 s = inspect`, width: colW }),
|
|
1020
|
+
pickable.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "scanning\u2026" }) : null,
|
|
1021
|
+
pickable.map((r, i) => {
|
|
1022
|
+
const freshDot = nowMs - r.lastAt < 9e4;
|
|
1023
|
+
const sel = mode === "pick" && i === pickIdx;
|
|
1024
|
+
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", backgroundColor: sel ? "#1c2f63" : void 0, bold: sel || r.isMe, children: [
|
|
1025
|
+
/* @__PURE__ */ jsxs2(Text2, { color: sel ? theme.accentBright : freshDot ? theme.ok : theme.dim, children: [
|
|
1026
|
+
sel ? "\u25B8" : freshDot ? "\u25CF" : "\u25CB",
|
|
1044
1027
|
" "
|
|
1045
1028
|
] }),
|
|
1046
1029
|
/* @__PURE__ */ jsx2(Text2, { color: r.source === "local" ? theme.ok : "#4f6db8", children: r.source === "local" ? "LOC " : "CLD " }),
|
|
@@ -1053,7 +1036,7 @@ function LivePanel({ active: active2 }) {
|
|
|
1053
1036
|
String(r.denies).padStart(3),
|
|
1054
1037
|
"d "
|
|
1055
1038
|
] }),
|
|
1056
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ago(r.lastAt) })
|
|
1039
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: (r.trust != null ? `t${r.trust} ` : "") + ago(r.lastAt) })
|
|
1057
1040
|
] }, r.id);
|
|
1058
1041
|
})
|
|
1059
1042
|
] }),
|
|
@@ -1090,7 +1073,7 @@ function LivePanel({ active: active2 }) {
|
|
|
1090
1073
|
] }),
|
|
1091
1074
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
1092
1075
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " LIVE " }),
|
|
1093
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` local ${localOn ? "on" : "off"} \xB7 ${localBuf.length} loc/${
|
|
1076
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` local ${localOn ? "on" : "off"} \xB7 ${localBuf.length} loc/${cloudBuf.length} cld \xB7 top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} ` }),
|
|
1094
1077
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " space copy-mode \xB7 f filter \xB7 s sessions \xB7 esc menu \xB7 q quit " })
|
|
1095
1078
|
] })
|
|
1096
1079
|
] });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.74.0",
|
|
4
4
|
"description": "AI tool security proxy: protect any AI tool server with customizable policies, path/command constraints, rate limiting, and audit logging. No code changes required.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|