@solongate/proxy 0.69.0 → 0.70.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 +215 -80
- package/dist/tui/index.js +215 -80
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7020,6 +7020,7 @@ var init_hooks = __esm({
|
|
|
7020
7020
|
|
|
7021
7021
|
// src/tui/panels/Live.tsx
|
|
7022
7022
|
import { Box as Box2, Text as Text2, useInput } from "ink";
|
|
7023
|
+
import { spawnSync } from "child_process";
|
|
7023
7024
|
import { closeSync, mkdirSync as mkdirSync3, openSync, readSync, statSync, writeFileSync as writeFileSync3 } from "fs";
|
|
7024
7025
|
import { homedir as homedir3 } from "os";
|
|
7025
7026
|
import { dirname, join as join5 } from "path";
|
|
@@ -7040,8 +7041,21 @@ function tailLines(file, maxBytes = 131072) {
|
|
|
7040
7041
|
return [];
|
|
7041
7042
|
}
|
|
7042
7043
|
}
|
|
7043
|
-
function
|
|
7044
|
+
function copyToClipboard(text) {
|
|
7045
|
+
const tools = [
|
|
7046
|
+
["wl-copy", []],
|
|
7047
|
+
["xclip", ["-selection", "clipboard"]],
|
|
7048
|
+
["xsel", ["--clipboard", "--input"]]
|
|
7049
|
+
];
|
|
7050
|
+
for (const [cmd, args] of tools) {
|
|
7051
|
+
try {
|
|
7052
|
+
const r = spawnSync(cmd, args, { input: text, timeout: 2e3 });
|
|
7053
|
+
if (r.status === 0) return cmd;
|
|
7054
|
+
} catch {
|
|
7055
|
+
}
|
|
7056
|
+
}
|
|
7044
7057
|
process.stdout.write(`\x1B]52;c;${Buffer.from(text, "utf-8").toString("base64")}\x07`);
|
|
7058
|
+
return "osc52";
|
|
7045
7059
|
}
|
|
7046
7060
|
function cloudItem(e) {
|
|
7047
7061
|
return {
|
|
@@ -7107,6 +7121,25 @@ function PaneTitle({ label, extra, width: width2 }) {
|
|
|
7107
7121
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: tail + "\u2500".repeat(Math.max(0, width2 - used)) })
|
|
7108
7122
|
] });
|
|
7109
7123
|
}
|
|
7124
|
+
function StreamLine({ e }) {
|
|
7125
|
+
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7126
|
+
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7127
|
+
"[",
|
|
7128
|
+
hhmmss(e.at),
|
|
7129
|
+
" "
|
|
7130
|
+
] }),
|
|
7131
|
+
/* @__PURE__ */ jsx2(Text2, { color: e.source === "local" ? theme.ok : "#4f6db8", children: e.source === "local" ? "LOC" : "CLD" }),
|
|
7132
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "] " }),
|
|
7133
|
+
/* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), bold: e.decision !== "ALLOW", children: e.decision.padEnd(6) }),
|
|
7134
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate2(e.tool, 12).padEnd(13) }),
|
|
7135
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.permission.padEnd(5) }),
|
|
7136
|
+
/* @__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
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: truncate2(e.agent ?? "-", 11).padEnd(12) }),
|
|
7138
|
+
e.dlp ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: "DLP! " }) : null,
|
|
7139
|
+
e.rule && e.decision !== "ALLOW" ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: truncate2(e.rule, 14) + " " }) : null,
|
|
7140
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.detail })
|
|
7141
|
+
] });
|
|
7142
|
+
}
|
|
7110
7143
|
function LivePanel({ active: active2 }) {
|
|
7111
7144
|
const [s, setS] = useState2(null);
|
|
7112
7145
|
const [tsData, setTsData] = useState2(null);
|
|
@@ -7119,6 +7152,11 @@ function LivePanel({ active: active2 }) {
|
|
|
7119
7152
|
const [filter, setFilter] = useState2("all");
|
|
7120
7153
|
const [scroll, setScroll] = useState2(0);
|
|
7121
7154
|
const [toast, setToast] = useState2(null);
|
|
7155
|
+
const [mode, setMode] = useState2("stream");
|
|
7156
|
+
const [pickIdx, setPickIdx] = useState2(0);
|
|
7157
|
+
const [detail, setDetail] = useState2(null);
|
|
7158
|
+
const [detailCloud, setDetailCloud] = useState2([]);
|
|
7159
|
+
const [detailScroll, setDetailScroll] = useState2(0);
|
|
7122
7160
|
const seenRef = useRef2(/* @__PURE__ */ new Set());
|
|
7123
7161
|
const lastLocalTs = useRef2(0);
|
|
7124
7162
|
const pausedUntil = useRef2(0);
|
|
@@ -7212,7 +7250,8 @@ function LivePanel({ active: active2 }) {
|
|
|
7212
7250
|
if (fresh.length) setCloudBuf((prev) => [...prev, ...fresh.map(cloudItem).sort((a, b) => a.at - b.at)].slice(-400));
|
|
7213
7251
|
const denies = fresh.filter((e) => e.decision !== "ALLOW").length;
|
|
7214
7252
|
if (firstLoad) pushLog(`cloud link up \xB7 api ${ms}ms \xB7 ${fresh.length} calls`);
|
|
7215
|
-
else pushLog(`api ${ms}ms \xB7 +${fresh.length} cloud${denies ? ` \xB7 ${denies} DENIED` : ""}`, denies ? "bad" :
|
|
7253
|
+
else if (fresh.length) pushLog(`api ${ms}ms \xB7 +${fresh.length} cloud${denies ? ` \xB7 ${denies} DENIED` : ""}`, denies ? "bad" : "warn");
|
|
7254
|
+
else pushLog(`api ${ms}ms \xB7 idle`, "ok");
|
|
7216
7255
|
} catch (e) {
|
|
7217
7256
|
onApiError(e);
|
|
7218
7257
|
}
|
|
@@ -7259,6 +7298,19 @@ function LivePanel({ active: active2 }) {
|
|
|
7259
7298
|
usePoll(() => {
|
|
7260
7299
|
if (!paused()) guard.reload();
|
|
7261
7300
|
}, 6e4, active2);
|
|
7301
|
+
useEffect2(() => {
|
|
7302
|
+
if (!detail) return;
|
|
7303
|
+
setDetailCloud([]);
|
|
7304
|
+
setDetailScroll(0);
|
|
7305
|
+
let alive = true;
|
|
7306
|
+
api.audit.list({ session_id: detail.id, limit: 100 }).then((r) => {
|
|
7307
|
+
if (alive) setDetailCloud(r.entries.map(cloudItem));
|
|
7308
|
+
}).catch(() => {
|
|
7309
|
+
});
|
|
7310
|
+
return () => {
|
|
7311
|
+
alive = false;
|
|
7312
|
+
};
|
|
7313
|
+
}, [detail]);
|
|
7262
7314
|
const [tick, setTick] = useState2(0);
|
|
7263
7315
|
useEffect2(() => {
|
|
7264
7316
|
if (!active2) return;
|
|
@@ -7273,12 +7325,13 @@ function LivePanel({ active: active2 }) {
|
|
|
7273
7325
|
const nowMs = Date.now();
|
|
7274
7326
|
const mergedAll = [...cloudBuf, ...localBuf].sort((a, b) => a.at - b.at);
|
|
7275
7327
|
const filtered = filter === "all" ? mergedAll : mergedAll.filter((e) => e.source === filter);
|
|
7276
|
-
const
|
|
7328
|
+
const visibleDesc = filtered.slice(-STREAM_KEEP).reverse();
|
|
7277
7329
|
const points = tsData?.timeseries ?? [];
|
|
7330
|
+
const nowMin = Math.floor(nowMs / 6e4);
|
|
7278
7331
|
const minuteCounts = new Array(60).fill(0);
|
|
7279
7332
|
const minuteHot = new Array(60).fill(false);
|
|
7280
7333
|
for (const e of mergedAll) {
|
|
7281
|
-
const idx = 59 - Math.floor(
|
|
7334
|
+
const idx = 59 - (nowMin - Math.floor(e.at / 6e4));
|
|
7282
7335
|
if (idx >= 0 && idx < 60) {
|
|
7283
7336
|
minuteCounts[idx]++;
|
|
7284
7337
|
if (e.decision !== "ALLOW") minuteHot[idx] = true;
|
|
@@ -7294,8 +7347,8 @@ function LivePanel({ active: active2 }) {
|
|
|
7294
7347
|
const minuteNow = minuteCounts[59] ?? 0;
|
|
7295
7348
|
const dlpBars = (ins.dlpByPattern ?? []).slice(0, 2);
|
|
7296
7349
|
const maxDlpBar = dlpBars[0]?.count ?? 1;
|
|
7297
|
-
const
|
|
7298
|
-
const lastDeny =
|
|
7350
|
+
const denialsAll = mergedAll.filter((e) => e.decision !== "ALLOW");
|
|
7351
|
+
const lastDeny = denialsAll[denialsAll.length - 1];
|
|
7299
7352
|
const toolCounts = /* @__PURE__ */ new Map();
|
|
7300
7353
|
for (const e of mergedAll) toolCounts.set(e.tool, (toolCounts.get(e.tool) ?? 0) + 1);
|
|
7301
7354
|
const topTools = [...toolCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
|
|
@@ -7316,7 +7369,19 @@ function LivePanel({ active: active2 }) {
|
|
|
7316
7369
|
if (e.agent) cur.agent = e.agent;
|
|
7317
7370
|
localSess.set(e.session, cur);
|
|
7318
7371
|
}
|
|
7319
|
-
const
|
|
7372
|
+
const combinedSess = [
|
|
7373
|
+
...[...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 })),
|
|
7374
|
+
...sess.filter((a) => !localSess.has(a.session_id)).map((a) => ({
|
|
7375
|
+
source: "cloud",
|
|
7376
|
+
id: a.session_id,
|
|
7377
|
+
agent: a.agent_name ?? a.session_id,
|
|
7378
|
+
calls: a.total_calls,
|
|
7379
|
+
denies: a.denied_calls,
|
|
7380
|
+
lastAt: Date.parse(a.last_seen_at),
|
|
7381
|
+
trust: a.trust_score,
|
|
7382
|
+
isMe: false
|
|
7383
|
+
}))
|
|
7384
|
+
];
|
|
7320
7385
|
const chartH = rows >= 36 ? 6 : 4;
|
|
7321
7386
|
const colH = rows >= 32 ? 7 : 5;
|
|
7322
7387
|
const streamRows = Math.max(4, rows - 5 - (1 + chartH) - colH);
|
|
@@ -7324,48 +7389,154 @@ function LivePanel({ active: active2 }) {
|
|
|
7324
7389
|
const leftW = Math.floor(innerW * 0.55);
|
|
7325
7390
|
const rightW = innerW - leftW - 2;
|
|
7326
7391
|
const colW = Math.max(20, Math.floor((innerW - 4) / 3));
|
|
7327
|
-
const
|
|
7392
|
+
const pickable = combinedSess.slice(0, colH - 1);
|
|
7393
|
+
const maxScroll = Math.max(0, visibleDesc.length - streamRows);
|
|
7328
7394
|
const clampedScroll = Math.min(scroll, maxScroll);
|
|
7329
|
-
const
|
|
7330
|
-
const
|
|
7395
|
+
const windowed = visibleDesc.slice(clampedScroll, clampedScroll + streamRows);
|
|
7396
|
+
const detailEntries = detail ? (() => {
|
|
7397
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7398
|
+
const all = [];
|
|
7399
|
+
for (const e of [...detailCloud, ...mergedAll.filter((x) => x.session === detail.id)]) {
|
|
7400
|
+
const key = `${e.at}:${e.tool}:${e.decision}`;
|
|
7401
|
+
if (seen.has(key)) continue;
|
|
7402
|
+
seen.add(key);
|
|
7403
|
+
all.push(e);
|
|
7404
|
+
}
|
|
7405
|
+
return all.sort((a, b) => b.at - a.at);
|
|
7406
|
+
})() : [];
|
|
7331
7407
|
useInput(
|
|
7332
7408
|
(input, key) => {
|
|
7409
|
+
if (mode === "detail") {
|
|
7410
|
+
const maxD = Math.max(0, detailEntries.length - (rows - 10));
|
|
7411
|
+
if (key.leftArrow) {
|
|
7412
|
+
setMode("stream");
|
|
7413
|
+
setDetail(null);
|
|
7414
|
+
} else if (key.upArrow) setDetailScroll((n) => Math.max(0, n - 1));
|
|
7415
|
+
else if (key.downArrow) setDetailScroll((n) => Math.min(maxD, n + 1));
|
|
7416
|
+
else if (key.pageUp) setDetailScroll((n) => Math.max(0, n - 10));
|
|
7417
|
+
else if (key.pageDown) setDetailScroll((n) => Math.min(maxD, n + 10));
|
|
7418
|
+
return;
|
|
7419
|
+
}
|
|
7420
|
+
if (mode === "pick") {
|
|
7421
|
+
if (key.upArrow) setPickIdx((n) => Math.max(0, n - 1));
|
|
7422
|
+
else if (key.downArrow) setPickIdx((n) => Math.min(pickable.length - 1, n + 1));
|
|
7423
|
+
else if (key.return) {
|
|
7424
|
+
const row = pickable[pickIdx];
|
|
7425
|
+
if (row) {
|
|
7426
|
+
setDetail(row);
|
|
7427
|
+
setMode("detail");
|
|
7428
|
+
}
|
|
7429
|
+
} else if (input === "s" || key.leftArrow) setMode("stream");
|
|
7430
|
+
return;
|
|
7431
|
+
}
|
|
7333
7432
|
if (input === "f") {
|
|
7334
7433
|
setFilter((f) => FILTERS[(FILTERS.indexOf(f) + 1) % FILTERS.length]);
|
|
7335
7434
|
setScroll(0);
|
|
7336
|
-
} else if (
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
else if (key.
|
|
7435
|
+
} else if (input === "s") {
|
|
7436
|
+
setPickIdx(0);
|
|
7437
|
+
setMode("pick");
|
|
7438
|
+
} else if (key.downArrow) setScroll((n) => Math.min(maxScroll, n + 1));
|
|
7439
|
+
else if (key.upArrow) setScroll((n) => Math.max(0, n - 1));
|
|
7440
|
+
else if (key.pageDown) setScroll((n) => Math.min(maxScroll, n + streamRows));
|
|
7441
|
+
else if (key.pageUp) setScroll((n) => Math.max(0, n - streamRows));
|
|
7340
7442
|
else if (input === "c") {
|
|
7341
|
-
const dump =
|
|
7443
|
+
const dump = visibleDesc.map(
|
|
7342
7444
|
(e) => `[${new Date(e.at).toISOString()}] ${e.source.toUpperCase().padEnd(5)} ${e.decision.padEnd(6)} ${e.tool.padEnd(16)} ${e.permission.padEnd(5)} ${e.evalMs != null ? String(e.evalMs).padStart(5) + "ms " : " "}${(e.agent ?? "-").padEnd(14)} ${e.session ? e.session.slice(0, 8) : "- "} ${e.detail}`
|
|
7343
7445
|
).join("\n");
|
|
7446
|
+
let via = "file";
|
|
7344
7447
|
try {
|
|
7345
7448
|
mkdirSync3(dirname(DUMP), { recursive: true });
|
|
7346
7449
|
writeFileSync3(DUMP, dump + "\n");
|
|
7347
7450
|
} catch {
|
|
7348
7451
|
}
|
|
7349
|
-
|
|
7350
|
-
setToast({ msg: `copied ${
|
|
7452
|
+
via = copyToClipboard(dump);
|
|
7453
|
+
setToast({ msg: `copied ${visibleDesc.length} lines via ${via} + ${DUMP}`, until: Date.now() + 5e3 });
|
|
7351
7454
|
}
|
|
7352
7455
|
},
|
|
7353
7456
|
{ isActive: active2 }
|
|
7354
7457
|
);
|
|
7355
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;
|
|
7356
7460
|
if (s === null && localOn === null) {
|
|
7357
7461
|
return /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7358
7462
|
spin,
|
|
7359
7463
|
" connecting\u2026"
|
|
7360
7464
|
] });
|
|
7361
7465
|
}
|
|
7466
|
+
const titleBar = /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7467
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: " SOLONGATE LIVE " }),
|
|
7468
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` ${spin} up ${fmtUp(nowMs - startRef.current)} \xB7 ${hhmmss(nowMs)} \xB7 api ${latNow}ms ` }),
|
|
7469
|
+
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 " }),
|
|
7470
|
+
toast && nowMs < toast.until ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#123d1f", color: "#7bd88f", bold: true, children: ` ${toast.msg} ` }) : null
|
|
7471
|
+
] });
|
|
7472
|
+
if (mode === "detail" && detail) {
|
|
7473
|
+
const d = detailEntries;
|
|
7474
|
+
const allow = d.filter((e) => e.decision === "ALLOW").length;
|
|
7475
|
+
const deny = d.length - allow;
|
|
7476
|
+
const dlpN = d.filter((e) => e.dlp).length;
|
|
7477
|
+
const evals = d.filter((e) => e.evalMs != null).map((e) => e.evalMs);
|
|
7478
|
+
const evalAvg = evals.length ? Math.round(evals.reduce((a, b) => a + b, 0) / evals.length) : null;
|
|
7479
|
+
const tCounts = /* @__PURE__ */ new Map();
|
|
7480
|
+
const pCounts = /* @__PURE__ */ new Map();
|
|
7481
|
+
for (const e of d) {
|
|
7482
|
+
tCounts.set(e.tool, (tCounts.get(e.tool) ?? 0) + 1);
|
|
7483
|
+
if (e.permission) pCounts.set(e.permission, (pCounts.get(e.permission) ?? 0) + 1);
|
|
7484
|
+
}
|
|
7485
|
+
const top = [...tCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 6);
|
|
7486
|
+
const perms = [...pCounts.entries()].sort((a, b) => b[1] - a[1]);
|
|
7487
|
+
const first = d[d.length - 1]?.at;
|
|
7488
|
+
const last = d[0]?.at;
|
|
7489
|
+
const tlRows = Math.max(4, rows - 10);
|
|
7490
|
+
const maxD = Math.max(0, d.length - tlRows);
|
|
7491
|
+
const dScroll = Math.min(detailScroll, maxD);
|
|
7492
|
+
const tl = d.slice(dScroll, dScroll + tlRows);
|
|
7493
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
7494
|
+
titleBar,
|
|
7495
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7496
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#AAC8FA", bold: true, children: ` SESSION ${detail.id.slice(0, 8)} ` }),
|
|
7497
|
+
/* @__PURE__ */ jsx2(Text2, { color: detail.source === "local" ? theme.ok : "#4f6db8", children: " " + detail.source.toUpperCase() }),
|
|
7498
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, children: " " + truncate2(detail.isMe ? detail.agent + " (this machine)" : detail.agent, 34) }),
|
|
7499
|
+
detail.trust != null ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ` trust ${detail.trust}/100` }) : null,
|
|
7500
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2190 back" })
|
|
7501
|
+
] }),
|
|
7502
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7503
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 calls " }),
|
|
7504
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, children: d.length }),
|
|
7505
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 allow " }),
|
|
7506
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: allow }),
|
|
7507
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 deny " }),
|
|
7508
|
+
/* @__PURE__ */ jsx2(Text2, { color: deny ? theme.bad : theme.dim, bold: deny > 0, children: deny }),
|
|
7509
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 dlp " }),
|
|
7510
|
+
/* @__PURE__ */ jsx2(Text2, { color: dlpN ? theme.bad : theme.dim, children: dlpN }),
|
|
7511
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 avg eval " }),
|
|
7512
|
+
/* @__PURE__ */ jsx2(Text2, { children: evalAvg != null ? `${evalAvg}ms` : "\u2014" }),
|
|
7513
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 first " }),
|
|
7514
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: first ? `${hhmmss(first)} (${ago(first)} ago)` : "\u2014" }),
|
|
7515
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 last " }),
|
|
7516
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: last ? `${hhmmss(last)} (${ago(last)} ago)` : "\u2014" }),
|
|
7517
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
7518
|
+
] }),
|
|
7519
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7520
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 tools " }),
|
|
7521
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: top.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014" }),
|
|
7522
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 perms " }),
|
|
7523
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: perms.map(([p, c2]) => `${p}\xD7${c2}`).join(" ") || "\u2014" }),
|
|
7524
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
7525
|
+
] }),
|
|
7526
|
+
/* @__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 }),
|
|
7527
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: tlRows, overflow: "hidden", children: [
|
|
7528
|
+
tl.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: detailCloud.length === 0 ? spin + " loading history\u2026" : "no entries" }) : null,
|
|
7529
|
+
tl.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e }, e.id))
|
|
7530
|
+
] }),
|
|
7531
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7532
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " SESSION " }),
|
|
7533
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` ${detail.id} ` }),
|
|
7534
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " \u2191\u2193 scroll \xB7 \u2190 back \xB7 esc menu " })
|
|
7535
|
+
] })
|
|
7536
|
+
] });
|
|
7537
|
+
}
|
|
7362
7538
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
7363
|
-
|
|
7364
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: " SOLONGATE LIVE " }),
|
|
7365
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` ${spin} up ${fmtUp(nowMs - startRef.current)} \xB7 ${hhmmss(nowMs)} \xB7 api ${latNow}ms ` }),
|
|
7366
|
-
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 " }),
|
|
7367
|
-
toast && nowMs < toast.until ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#123d1f", color: "#7bd88f", bold: true, children: ` ${toast.msg} ` }) : null
|
|
7368
|
-
] }),
|
|
7539
|
+
titleBar,
|
|
7369
7540
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7370
7541
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 calls " }),
|
|
7371
7542
|
/* @__PURE__ */ jsx2(Text2, { bold: true, children: s ? s.total_calls : "\xB7\xB7\xB7\xB7" }),
|
|
@@ -7382,7 +7553,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7382
7553
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 ghost " }),
|
|
7383
7554
|
/* @__PURE__ */ jsx2(Text2, { color: gh?.mode === "on" ? theme.ok : theme.dim, children: gh?.mode ?? "?" }),
|
|
7384
7555
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 sess " }),
|
|
7385
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: `${
|
|
7556
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: `${combinedSess.length} (${sessCounts?.active ?? 0} live)` }),
|
|
7386
7557
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 hooks " }),
|
|
7387
7558
|
/* @__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" }),
|
|
7388
7559
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
@@ -7411,7 +7582,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7411
7582
|
/* @__PURE__ */ jsx2(Text2, { color: dl?.mode === "block" ? theme.ok : dl?.mode === "detect" ? theme.warn : theme.dim, children: (dl?.mode ?? "?").padEnd(7) }),
|
|
7412
7583
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: `${(dl?.patterns ?? []).length} builtin \xB7 ${(dl?.custom ?? []).length} custom` })
|
|
7413
7584
|
] }),
|
|
7414
|
-
dlpBars.length ? dlpBars.map((
|
|
7585
|
+
dlpBars.length ? dlpBars.map((d2) => /* @__PURE__ */ jsx2(HBar, { label: truncate2(d2.pattern, 10), value: d2.count, max: maxDlpBar, width: Math.max(6, colW - 16), color: theme.bad }, d2.pattern)) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " no dlp hits in last 7 days" }),
|
|
7415
7586
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7416
7587
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "GUARD".padEnd(10) }),
|
|
7417
7588
|
ring ? /* @__PURE__ */ jsxs2(Text2, { color: theme.ok, children: [
|
|
@@ -7421,48 +7592,28 @@ function LivePanel({ active: active2 }) {
|
|
|
7421
7592
|
] })
|
|
7422
7593
|
] }),
|
|
7423
7594
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, overflow: "hidden", children: [
|
|
7424
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: `${
|
|
7425
|
-
|
|
7426
|
-
|
|
7427
|
-
const freshDot = nowMs -
|
|
7428
|
-
const
|
|
7429
|
-
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7430
|
-
/* @__PURE__ */ jsxs2(Text2, { color: freshDot ? theme.ok : theme.dim, children: [
|
|
7431
|
-
freshDot ? "\u25CF" : "\u25CB",
|
|
7595
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: mode === "pick" ? "\u2191\u2193 pick \xB7 enter open" : `${combinedSess.length} \xB7 s = inspect`, width: colW }),
|
|
7596
|
+
pickable.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "scanning\u2026" }) : null,
|
|
7597
|
+
pickable.map((r, i) => {
|
|
7598
|
+
const freshDot = nowMs - r.lastAt < 9e4;
|
|
7599
|
+
const sel = mode === "pick" && i === pickIdx;
|
|
7600
|
+
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", backgroundColor: sel ? "#1c2f63" : void 0, bold: sel || r.isMe, children: [
|
|
7601
|
+
/* @__PURE__ */ jsxs2(Text2, { color: sel ? theme.accentBright : freshDot ? theme.ok : theme.dim, children: [
|
|
7602
|
+
sel ? "\u25B8" : freshDot ? "\u25CF" : "\u25CB",
|
|
7432
7603
|
" "
|
|
7433
7604
|
] }),
|
|
7434
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: "LOC " }),
|
|
7435
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.accentBright,
|
|
7605
|
+
/* @__PURE__ */ jsx2(Text2, { color: r.source === "local" ? theme.ok : "#4f6db8", children: r.source === "local" ? "LOC " : "CLD " }),
|
|
7606
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, children: truncate2(r.isMe ? "this machine" : r.agent, 12).padEnd(13) }),
|
|
7436
7607
|
/* @__PURE__ */ jsxs2(Text2, { children: [
|
|
7437
|
-
String(
|
|
7608
|
+
String(r.calls).padStart(4),
|
|
7438
7609
|
"c "
|
|
7439
7610
|
] }),
|
|
7440
|
-
/* @__PURE__ */ jsxs2(Text2, { color:
|
|
7441
|
-
String(
|
|
7611
|
+
/* @__PURE__ */ jsxs2(Text2, { color: r.denies ? theme.bad : theme.dim, children: [
|
|
7612
|
+
String(r.denies).padStart(3),
|
|
7442
7613
|
"d "
|
|
7443
7614
|
] }),
|
|
7444
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ago(
|
|
7445
|
-
] }, id);
|
|
7446
|
-
}),
|
|
7447
|
-
sess.slice(0, Math.max(0, colH - 1 - Math.min(localSessList.length, Math.max(1, colH - 2)))).map((a) => {
|
|
7448
|
-
const dot = sessionDot(a.status);
|
|
7449
|
-
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7450
|
-
/* @__PURE__ */ jsxs2(Text2, { color: dot.color, children: [
|
|
7451
|
-
dot.ch,
|
|
7452
|
-
" "
|
|
7453
|
-
] }),
|
|
7454
|
-
/* @__PURE__ */ jsx2(Text2, { color: "#4f6db8", children: "CLD " }),
|
|
7455
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate2(a.agent_name ?? a.session_id, 12).padEnd(13) }),
|
|
7456
|
-
/* @__PURE__ */ jsxs2(Text2, { children: [
|
|
7457
|
-
String(a.total_calls).padStart(4),
|
|
7458
|
-
"c "
|
|
7459
|
-
] }),
|
|
7460
|
-
/* @__PURE__ */ jsxs2(Text2, { color: a.denied_calls ? theme.bad : theme.dim, children: [
|
|
7461
|
-
String(a.denied_calls).padStart(3),
|
|
7462
|
-
"d "
|
|
7463
|
-
] }),
|
|
7464
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: `t${a.trust_score} ` + ago(a.last_seen_at) })
|
|
7465
|
-
] }, a.session_id);
|
|
7615
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: (r.trust != null ? `t${r.trust} ` : "") + ago(r.lastAt) })
|
|
7616
|
+
] }, r.id);
|
|
7466
7617
|
})
|
|
7467
7618
|
] }),
|
|
7468
7619
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, overflow: "hidden", children: [
|
|
@@ -7481,7 +7632,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7481
7632
|
PaneTitle,
|
|
7482
7633
|
{
|
|
7483
7634
|
label: "TOOL STREAM",
|
|
7484
|
-
extra: `last ${STREAM_KEEP} \xB7 filter ${filter}${clampedScroll > 0 ? ` \xB7 \
|
|
7635
|
+
extra: `newest first \xB7 last ${STREAM_KEEP} \xB7 filter ${filter}${clampedScroll > 0 ? ` \xB7 \u25BC${clampedScroll} older` : ""} \xB7 f filter \xB7 \u2191\u2193 scroll \xB7 c copy \xB7 s sessions`,
|
|
7485
7636
|
width: innerW
|
|
7486
7637
|
}
|
|
7487
7638
|
),
|
|
@@ -7494,28 +7645,12 @@ function LivePanel({ active: active2 }) {
|
|
|
7494
7645
|
spin,
|
|
7495
7646
|
" awaiting traffic\u2026"
|
|
7496
7647
|
] }) : null,
|
|
7497
|
-
windowed.map((e) => /* @__PURE__ */
|
|
7498
|
-
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7499
|
-
"[",
|
|
7500
|
-
hhmmss(e.at),
|
|
7501
|
-
" "
|
|
7502
|
-
] }),
|
|
7503
|
-
/* @__PURE__ */ jsx2(Text2, { color: e.source === "local" ? theme.ok : "#4f6db8", children: e.source === "local" ? "LOC" : "CLD" }),
|
|
7504
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "] " }),
|
|
7505
|
-
/* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), bold: e.decision !== "ALLOW", children: e.decision.padEnd(6) }),
|
|
7506
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate2(e.tool, 12).padEnd(13) }),
|
|
7507
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.permission.padEnd(5) }),
|
|
7508
|
-
/* @__PURE__ */ jsx2(Text2, { color: e.evalMs != null && e.evalMs > 500 ? theme.warn : theme.dim, children: (e.evalMs != null ? `${e.evalMs}ms` : "\u2014").padEnd(7) }),
|
|
7509
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: truncate2(e.agent ?? "-", 11).padEnd(12) }),
|
|
7510
|
-
e.dlp ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: "DLP! " }) : null,
|
|
7511
|
-
e.rule && e.decision !== "ALLOW" ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: truncate2(e.rule, 14) + " " }) : null,
|
|
7512
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.detail })
|
|
7513
|
-
] }, e.id))
|
|
7648
|
+
windowed.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e }, e.id))
|
|
7514
7649
|
] }),
|
|
7515
7650
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7516
7651
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " LIVE " }),
|
|
7517
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` local ${localOn ? "on" : "off"} \xB7 ${localBuf.length} loc/${cloudBuf.length} cld
|
|
7518
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " f filter \xB7
|
|
7652
|
+
/* @__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"} ` }),
|
|
7653
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " f filter \xB7 s sessions \xB7 c copy \xB7 esc menu \xB7 q quit " })
|
|
7519
7654
|
] })
|
|
7520
7655
|
] });
|
|
7521
7656
|
}
|
package/dist/tui/index.js
CHANGED
|
@@ -126,6 +126,7 @@ function KeyHints({ hints }) {
|
|
|
126
126
|
|
|
127
127
|
// src/tui/panels/Live.tsx
|
|
128
128
|
import { Box as Box2, Text as Text2, useInput } from "ink";
|
|
129
|
+
import { spawnSync } from "child_process";
|
|
129
130
|
import { closeSync, mkdirSync, openSync, readSync, statSync, writeFileSync } from "fs";
|
|
130
131
|
import { homedir as homedir2 } from "os";
|
|
131
132
|
import { dirname, join as join2 } from "path";
|
|
@@ -465,8 +466,21 @@ function tailLines(file, maxBytes = 131072) {
|
|
|
465
466
|
return [];
|
|
466
467
|
}
|
|
467
468
|
}
|
|
468
|
-
function
|
|
469
|
+
function copyToClipboard(text) {
|
|
470
|
+
const tools = [
|
|
471
|
+
["wl-copy", []],
|
|
472
|
+
["xclip", ["-selection", "clipboard"]],
|
|
473
|
+
["xsel", ["--clipboard", "--input"]]
|
|
474
|
+
];
|
|
475
|
+
for (const [cmd, args] of tools) {
|
|
476
|
+
try {
|
|
477
|
+
const r = spawnSync(cmd, args, { input: text, timeout: 2e3 });
|
|
478
|
+
if (r.status === 0) return cmd;
|
|
479
|
+
} catch {
|
|
480
|
+
}
|
|
481
|
+
}
|
|
469
482
|
process.stdout.write(`\x1B]52;c;${Buffer.from(text, "utf-8").toString("base64")}\x07`);
|
|
483
|
+
return "osc52";
|
|
470
484
|
}
|
|
471
485
|
function cloudItem(e) {
|
|
472
486
|
return {
|
|
@@ -532,6 +546,25 @@ function PaneTitle({ label, extra, width }) {
|
|
|
532
546
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: tail + "\u2500".repeat(Math.max(0, width - used)) })
|
|
533
547
|
] });
|
|
534
548
|
}
|
|
549
|
+
function StreamLine({ e }) {
|
|
550
|
+
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
551
|
+
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
552
|
+
"[",
|
|
553
|
+
hhmmss(e.at),
|
|
554
|
+
" "
|
|
555
|
+
] }),
|
|
556
|
+
/* @__PURE__ */ jsx2(Text2, { color: e.source === "local" ? theme.ok : "#4f6db8", children: e.source === "local" ? "LOC" : "CLD" }),
|
|
557
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "] " }),
|
|
558
|
+
/* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), bold: e.decision !== "ALLOW", children: e.decision.padEnd(6) }),
|
|
559
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate(e.tool, 12).padEnd(13) }),
|
|
560
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.permission.padEnd(5) }),
|
|
561
|
+
/* @__PURE__ */ jsx2(Text2, { color: e.evalMs != null && e.evalMs > 500 ? theme.warn : theme.dim, children: (e.evalMs != null ? `${e.evalMs}ms` : "\u2014").padEnd(7) }),
|
|
562
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: truncate(e.agent ?? "-", 11).padEnd(12) }),
|
|
563
|
+
e.dlp ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: "DLP! " }) : null,
|
|
564
|
+
e.rule && e.decision !== "ALLOW" ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: truncate(e.rule, 14) + " " }) : null,
|
|
565
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.detail })
|
|
566
|
+
] });
|
|
567
|
+
}
|
|
535
568
|
var FILTERS = ["all", "local", "cloud"];
|
|
536
569
|
function LivePanel({ active: active2 }) {
|
|
537
570
|
const [s, setS] = useState2(null);
|
|
@@ -545,6 +578,11 @@ function LivePanel({ active: active2 }) {
|
|
|
545
578
|
const [filter, setFilter] = useState2("all");
|
|
546
579
|
const [scroll, setScroll] = useState2(0);
|
|
547
580
|
const [toast, setToast] = useState2(null);
|
|
581
|
+
const [mode, setMode] = useState2("stream");
|
|
582
|
+
const [pickIdx, setPickIdx] = useState2(0);
|
|
583
|
+
const [detail, setDetail] = useState2(null);
|
|
584
|
+
const [detailCloud, setDetailCloud] = useState2([]);
|
|
585
|
+
const [detailScroll, setDetailScroll] = useState2(0);
|
|
548
586
|
const seenRef = useRef2(/* @__PURE__ */ new Set());
|
|
549
587
|
const lastLocalTs = useRef2(0);
|
|
550
588
|
const pausedUntil = useRef2(0);
|
|
@@ -638,7 +676,8 @@ function LivePanel({ active: active2 }) {
|
|
|
638
676
|
if (fresh.length) setCloudBuf((prev) => [...prev, ...fresh.map(cloudItem).sort((a, b) => a.at - b.at)].slice(-400));
|
|
639
677
|
const denies = fresh.filter((e) => e.decision !== "ALLOW").length;
|
|
640
678
|
if (firstLoad) pushLog(`cloud link up \xB7 api ${ms}ms \xB7 ${fresh.length} calls`);
|
|
641
|
-
else pushLog(`api ${ms}ms \xB7 +${fresh.length} cloud${denies ? ` \xB7 ${denies} DENIED` : ""}`, denies ? "bad" :
|
|
679
|
+
else if (fresh.length) pushLog(`api ${ms}ms \xB7 +${fresh.length} cloud${denies ? ` \xB7 ${denies} DENIED` : ""}`, denies ? "bad" : "warn");
|
|
680
|
+
else pushLog(`api ${ms}ms \xB7 idle`, "ok");
|
|
642
681
|
} catch (e) {
|
|
643
682
|
onApiError(e);
|
|
644
683
|
}
|
|
@@ -685,6 +724,19 @@ function LivePanel({ active: active2 }) {
|
|
|
685
724
|
usePoll(() => {
|
|
686
725
|
if (!paused()) guard.reload();
|
|
687
726
|
}, 6e4, active2);
|
|
727
|
+
useEffect2(() => {
|
|
728
|
+
if (!detail) return;
|
|
729
|
+
setDetailCloud([]);
|
|
730
|
+
setDetailScroll(0);
|
|
731
|
+
let alive = true;
|
|
732
|
+
api.audit.list({ session_id: detail.id, limit: 100 }).then((r) => {
|
|
733
|
+
if (alive) setDetailCloud(r.entries.map(cloudItem));
|
|
734
|
+
}).catch(() => {
|
|
735
|
+
});
|
|
736
|
+
return () => {
|
|
737
|
+
alive = false;
|
|
738
|
+
};
|
|
739
|
+
}, [detail]);
|
|
688
740
|
const [tick, setTick] = useState2(0);
|
|
689
741
|
useEffect2(() => {
|
|
690
742
|
if (!active2) return;
|
|
@@ -699,12 +751,13 @@ function LivePanel({ active: active2 }) {
|
|
|
699
751
|
const nowMs = Date.now();
|
|
700
752
|
const mergedAll = [...cloudBuf, ...localBuf].sort((a, b) => a.at - b.at);
|
|
701
753
|
const filtered = filter === "all" ? mergedAll : mergedAll.filter((e) => e.source === filter);
|
|
702
|
-
const
|
|
754
|
+
const visibleDesc = filtered.slice(-STREAM_KEEP).reverse();
|
|
703
755
|
const points = tsData?.timeseries ?? [];
|
|
756
|
+
const nowMin = Math.floor(nowMs / 6e4);
|
|
704
757
|
const minuteCounts = new Array(60).fill(0);
|
|
705
758
|
const minuteHot = new Array(60).fill(false);
|
|
706
759
|
for (const e of mergedAll) {
|
|
707
|
-
const idx = 59 - Math.floor(
|
|
760
|
+
const idx = 59 - (nowMin - Math.floor(e.at / 6e4));
|
|
708
761
|
if (idx >= 0 && idx < 60) {
|
|
709
762
|
minuteCounts[idx]++;
|
|
710
763
|
if (e.decision !== "ALLOW") minuteHot[idx] = true;
|
|
@@ -720,8 +773,8 @@ function LivePanel({ active: active2 }) {
|
|
|
720
773
|
const minuteNow = minuteCounts[59] ?? 0;
|
|
721
774
|
const dlpBars = (ins.dlpByPattern ?? []).slice(0, 2);
|
|
722
775
|
const maxDlpBar = dlpBars[0]?.count ?? 1;
|
|
723
|
-
const
|
|
724
|
-
const lastDeny =
|
|
776
|
+
const denialsAll = mergedAll.filter((e) => e.decision !== "ALLOW");
|
|
777
|
+
const lastDeny = denialsAll[denialsAll.length - 1];
|
|
725
778
|
const toolCounts = /* @__PURE__ */ new Map();
|
|
726
779
|
for (const e of mergedAll) toolCounts.set(e.tool, (toolCounts.get(e.tool) ?? 0) + 1);
|
|
727
780
|
const topTools = [...toolCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
|
|
@@ -742,7 +795,19 @@ function LivePanel({ active: active2 }) {
|
|
|
742
795
|
if (e.agent) cur.agent = e.agent;
|
|
743
796
|
localSess.set(e.session, cur);
|
|
744
797
|
}
|
|
745
|
-
const
|
|
798
|
+
const combinedSess = [
|
|
799
|
+
...[...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 })),
|
|
800
|
+
...sess.filter((a) => !localSess.has(a.session_id)).map((a) => ({
|
|
801
|
+
source: "cloud",
|
|
802
|
+
id: a.session_id,
|
|
803
|
+
agent: a.agent_name ?? a.session_id,
|
|
804
|
+
calls: a.total_calls,
|
|
805
|
+
denies: a.denied_calls,
|
|
806
|
+
lastAt: Date.parse(a.last_seen_at),
|
|
807
|
+
trust: a.trust_score,
|
|
808
|
+
isMe: false
|
|
809
|
+
}))
|
|
810
|
+
];
|
|
746
811
|
const chartH = rows >= 36 ? 6 : 4;
|
|
747
812
|
const colH = rows >= 32 ? 7 : 5;
|
|
748
813
|
const streamRows = Math.max(4, rows - 5 - (1 + chartH) - colH);
|
|
@@ -750,48 +815,154 @@ function LivePanel({ active: active2 }) {
|
|
|
750
815
|
const leftW = Math.floor(innerW * 0.55);
|
|
751
816
|
const rightW = innerW - leftW - 2;
|
|
752
817
|
const colW = Math.max(20, Math.floor((innerW - 4) / 3));
|
|
753
|
-
const
|
|
818
|
+
const pickable = combinedSess.slice(0, colH - 1);
|
|
819
|
+
const maxScroll = Math.max(0, visibleDesc.length - streamRows);
|
|
754
820
|
const clampedScroll = Math.min(scroll, maxScroll);
|
|
755
|
-
const
|
|
756
|
-
const
|
|
821
|
+
const windowed = visibleDesc.slice(clampedScroll, clampedScroll + streamRows);
|
|
822
|
+
const detailEntries = detail ? (() => {
|
|
823
|
+
const seen = /* @__PURE__ */ new Set();
|
|
824
|
+
const all = [];
|
|
825
|
+
for (const e of [...detailCloud, ...mergedAll.filter((x) => x.session === detail.id)]) {
|
|
826
|
+
const key = `${e.at}:${e.tool}:${e.decision}`;
|
|
827
|
+
if (seen.has(key)) continue;
|
|
828
|
+
seen.add(key);
|
|
829
|
+
all.push(e);
|
|
830
|
+
}
|
|
831
|
+
return all.sort((a, b) => b.at - a.at);
|
|
832
|
+
})() : [];
|
|
757
833
|
useInput(
|
|
758
834
|
(input, key) => {
|
|
835
|
+
if (mode === "detail") {
|
|
836
|
+
const maxD = Math.max(0, detailEntries.length - (rows - 10));
|
|
837
|
+
if (key.leftArrow) {
|
|
838
|
+
setMode("stream");
|
|
839
|
+
setDetail(null);
|
|
840
|
+
} else if (key.upArrow) setDetailScroll((n) => Math.max(0, n - 1));
|
|
841
|
+
else if (key.downArrow) setDetailScroll((n) => Math.min(maxD, n + 1));
|
|
842
|
+
else if (key.pageUp) setDetailScroll((n) => Math.max(0, n - 10));
|
|
843
|
+
else if (key.pageDown) setDetailScroll((n) => Math.min(maxD, n + 10));
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
if (mode === "pick") {
|
|
847
|
+
if (key.upArrow) setPickIdx((n) => Math.max(0, n - 1));
|
|
848
|
+
else if (key.downArrow) setPickIdx((n) => Math.min(pickable.length - 1, n + 1));
|
|
849
|
+
else if (key.return) {
|
|
850
|
+
const row = pickable[pickIdx];
|
|
851
|
+
if (row) {
|
|
852
|
+
setDetail(row);
|
|
853
|
+
setMode("detail");
|
|
854
|
+
}
|
|
855
|
+
} else if (input === "s" || key.leftArrow) setMode("stream");
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
759
858
|
if (input === "f") {
|
|
760
859
|
setFilter((f) => FILTERS[(FILTERS.indexOf(f) + 1) % FILTERS.length]);
|
|
761
860
|
setScroll(0);
|
|
762
|
-
} else if (
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
else if (key.
|
|
861
|
+
} else if (input === "s") {
|
|
862
|
+
setPickIdx(0);
|
|
863
|
+
setMode("pick");
|
|
864
|
+
} else if (key.downArrow) setScroll((n) => Math.min(maxScroll, n + 1));
|
|
865
|
+
else if (key.upArrow) setScroll((n) => Math.max(0, n - 1));
|
|
866
|
+
else if (key.pageDown) setScroll((n) => Math.min(maxScroll, n + streamRows));
|
|
867
|
+
else if (key.pageUp) setScroll((n) => Math.max(0, n - streamRows));
|
|
766
868
|
else if (input === "c") {
|
|
767
|
-
const dump =
|
|
869
|
+
const dump = visibleDesc.map(
|
|
768
870
|
(e) => `[${new Date(e.at).toISOString()}] ${e.source.toUpperCase().padEnd(5)} ${e.decision.padEnd(6)} ${e.tool.padEnd(16)} ${e.permission.padEnd(5)} ${e.evalMs != null ? String(e.evalMs).padStart(5) + "ms " : " "}${(e.agent ?? "-").padEnd(14)} ${e.session ? e.session.slice(0, 8) : "- "} ${e.detail}`
|
|
769
871
|
).join("\n");
|
|
872
|
+
let via = "file";
|
|
770
873
|
try {
|
|
771
874
|
mkdirSync(dirname(DUMP), { recursive: true });
|
|
772
875
|
writeFileSync(DUMP, dump + "\n");
|
|
773
876
|
} catch {
|
|
774
877
|
}
|
|
775
|
-
|
|
776
|
-
setToast({ msg: `copied ${
|
|
878
|
+
via = copyToClipboard(dump);
|
|
879
|
+
setToast({ msg: `copied ${visibleDesc.length} lines via ${via} + ${DUMP}`, until: Date.now() + 5e3 });
|
|
777
880
|
}
|
|
778
881
|
},
|
|
779
882
|
{ isActive: active2 }
|
|
780
883
|
);
|
|
781
884
|
const sessionDot = (st) => st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\u25CC", color: theme.warn } : { ch: "\u25CB", color: theme.dim };
|
|
885
|
+
void sessionDot;
|
|
782
886
|
if (s === null && localOn === null) {
|
|
783
887
|
return /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
784
888
|
spin,
|
|
785
889
|
" connecting\u2026"
|
|
786
890
|
] });
|
|
787
891
|
}
|
|
892
|
+
const titleBar = /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
893
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: " SOLONGATE LIVE " }),
|
|
894
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` ${spin} up ${fmtUp(nowMs - startRef.current)} \xB7 ${hhmmss(nowMs)} \xB7 api ${latNow}ms ` }),
|
|
895
|
+
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 " }),
|
|
896
|
+
toast && nowMs < toast.until ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#123d1f", color: "#7bd88f", bold: true, children: ` ${toast.msg} ` }) : null
|
|
897
|
+
] });
|
|
898
|
+
if (mode === "detail" && detail) {
|
|
899
|
+
const d = detailEntries;
|
|
900
|
+
const allow = d.filter((e) => e.decision === "ALLOW").length;
|
|
901
|
+
const deny = d.length - allow;
|
|
902
|
+
const dlpN = d.filter((e) => e.dlp).length;
|
|
903
|
+
const evals = d.filter((e) => e.evalMs != null).map((e) => e.evalMs);
|
|
904
|
+
const evalAvg = evals.length ? Math.round(evals.reduce((a, b) => a + b, 0) / evals.length) : null;
|
|
905
|
+
const tCounts = /* @__PURE__ */ new Map();
|
|
906
|
+
const pCounts = /* @__PURE__ */ new Map();
|
|
907
|
+
for (const e of d) {
|
|
908
|
+
tCounts.set(e.tool, (tCounts.get(e.tool) ?? 0) + 1);
|
|
909
|
+
if (e.permission) pCounts.set(e.permission, (pCounts.get(e.permission) ?? 0) + 1);
|
|
910
|
+
}
|
|
911
|
+
const top = [...tCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 6);
|
|
912
|
+
const perms = [...pCounts.entries()].sort((a, b) => b[1] - a[1]);
|
|
913
|
+
const first = d[d.length - 1]?.at;
|
|
914
|
+
const last = d[0]?.at;
|
|
915
|
+
const tlRows = Math.max(4, rows - 10);
|
|
916
|
+
const maxD = Math.max(0, d.length - tlRows);
|
|
917
|
+
const dScroll = Math.min(detailScroll, maxD);
|
|
918
|
+
const tl = d.slice(dScroll, dScroll + tlRows);
|
|
919
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
920
|
+
titleBar,
|
|
921
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
922
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#AAC8FA", bold: true, children: ` SESSION ${detail.id.slice(0, 8)} ` }),
|
|
923
|
+
/* @__PURE__ */ jsx2(Text2, { color: detail.source === "local" ? theme.ok : "#4f6db8", children: " " + detail.source.toUpperCase() }),
|
|
924
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, children: " " + truncate(detail.isMe ? detail.agent + " (this machine)" : detail.agent, 34) }),
|
|
925
|
+
detail.trust != null ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ` trust ${detail.trust}/100` }) : null,
|
|
926
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2190 back" })
|
|
927
|
+
] }),
|
|
928
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
929
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 calls " }),
|
|
930
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, children: d.length }),
|
|
931
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 allow " }),
|
|
932
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: allow }),
|
|
933
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 deny " }),
|
|
934
|
+
/* @__PURE__ */ jsx2(Text2, { color: deny ? theme.bad : theme.dim, bold: deny > 0, children: deny }),
|
|
935
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 dlp " }),
|
|
936
|
+
/* @__PURE__ */ jsx2(Text2, { color: dlpN ? theme.bad : theme.dim, children: dlpN }),
|
|
937
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 avg eval " }),
|
|
938
|
+
/* @__PURE__ */ jsx2(Text2, { children: evalAvg != null ? `${evalAvg}ms` : "\u2014" }),
|
|
939
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 first " }),
|
|
940
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: first ? `${hhmmss(first)} (${ago(first)} ago)` : "\u2014" }),
|
|
941
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 last " }),
|
|
942
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: last ? `${hhmmss(last)} (${ago(last)} ago)` : "\u2014" }),
|
|
943
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
944
|
+
] }),
|
|
945
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
946
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 tools " }),
|
|
947
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: top.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014" }),
|
|
948
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 perms " }),
|
|
949
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: perms.map(([p, c2]) => `${p}\xD7${c2}`).join(" ") || "\u2014" }),
|
|
950
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
951
|
+
] }),
|
|
952
|
+
/* @__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 }),
|
|
953
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: tlRows, overflow: "hidden", children: [
|
|
954
|
+
tl.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: detailCloud.length === 0 ? spin + " loading history\u2026" : "no entries" }) : null,
|
|
955
|
+
tl.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e }, e.id))
|
|
956
|
+
] }),
|
|
957
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
958
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " SESSION " }),
|
|
959
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` ${detail.id} ` }),
|
|
960
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " \u2191\u2193 scroll \xB7 \u2190 back \xB7 esc menu " })
|
|
961
|
+
] })
|
|
962
|
+
] });
|
|
963
|
+
}
|
|
788
964
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
789
|
-
|
|
790
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: " SOLONGATE LIVE " }),
|
|
791
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` ${spin} up ${fmtUp(nowMs - startRef.current)} \xB7 ${hhmmss(nowMs)} \xB7 api ${latNow}ms ` }),
|
|
792
|
-
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 " }),
|
|
793
|
-
toast && nowMs < toast.until ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#123d1f", color: "#7bd88f", bold: true, children: ` ${toast.msg} ` }) : null
|
|
794
|
-
] }),
|
|
965
|
+
titleBar,
|
|
795
966
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
796
967
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 calls " }),
|
|
797
968
|
/* @__PURE__ */ jsx2(Text2, { bold: true, children: s ? s.total_calls : "\xB7\xB7\xB7\xB7" }),
|
|
@@ -808,7 +979,7 @@ function LivePanel({ active: active2 }) {
|
|
|
808
979
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 ghost " }),
|
|
809
980
|
/* @__PURE__ */ jsx2(Text2, { color: gh?.mode === "on" ? theme.ok : theme.dim, children: gh?.mode ?? "?" }),
|
|
810
981
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 sess " }),
|
|
811
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: `${
|
|
982
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: `${combinedSess.length} (${sessCounts?.active ?? 0} live)` }),
|
|
812
983
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 hooks " }),
|
|
813
984
|
/* @__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" }),
|
|
814
985
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
@@ -837,7 +1008,7 @@ function LivePanel({ active: active2 }) {
|
|
|
837
1008
|
/* @__PURE__ */ jsx2(Text2, { color: dl?.mode === "block" ? theme.ok : dl?.mode === "detect" ? theme.warn : theme.dim, children: (dl?.mode ?? "?").padEnd(7) }),
|
|
838
1009
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: `${(dl?.patterns ?? []).length} builtin \xB7 ${(dl?.custom ?? []).length} custom` })
|
|
839
1010
|
] }),
|
|
840
|
-
dlpBars.length ? dlpBars.map((
|
|
1011
|
+
dlpBars.length ? dlpBars.map((d2) => /* @__PURE__ */ jsx2(HBar, { label: truncate(d2.pattern, 10), value: d2.count, max: maxDlpBar, width: Math.max(6, colW - 16), color: theme.bad }, d2.pattern)) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " no dlp hits in last 7 days" }),
|
|
841
1012
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
842
1013
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "GUARD".padEnd(10) }),
|
|
843
1014
|
ring ? /* @__PURE__ */ jsxs2(Text2, { color: theme.ok, children: [
|
|
@@ -847,48 +1018,28 @@ function LivePanel({ active: active2 }) {
|
|
|
847
1018
|
] })
|
|
848
1019
|
] }),
|
|
849
1020
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, overflow: "hidden", children: [
|
|
850
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: `${
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
const freshDot = nowMs -
|
|
854
|
-
const
|
|
855
|
-
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
856
|
-
/* @__PURE__ */ jsxs2(Text2, { color: freshDot ? theme.ok : theme.dim, children: [
|
|
857
|
-
freshDot ? "\u25CF" : "\u25CB",
|
|
1021
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: mode === "pick" ? "\u2191\u2193 pick \xB7 enter open" : `${combinedSess.length} \xB7 s = inspect`, width: colW }),
|
|
1022
|
+
pickable.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "scanning\u2026" }) : null,
|
|
1023
|
+
pickable.map((r, i) => {
|
|
1024
|
+
const freshDot = nowMs - r.lastAt < 9e4;
|
|
1025
|
+
const sel = mode === "pick" && i === pickIdx;
|
|
1026
|
+
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", backgroundColor: sel ? "#1c2f63" : void 0, bold: sel || r.isMe, children: [
|
|
1027
|
+
/* @__PURE__ */ jsxs2(Text2, { color: sel ? theme.accentBright : freshDot ? theme.ok : theme.dim, children: [
|
|
1028
|
+
sel ? "\u25B8" : freshDot ? "\u25CF" : "\u25CB",
|
|
858
1029
|
" "
|
|
859
1030
|
] }),
|
|
860
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: "LOC " }),
|
|
861
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.accentBright,
|
|
1031
|
+
/* @__PURE__ */ jsx2(Text2, { color: r.source === "local" ? theme.ok : "#4f6db8", children: r.source === "local" ? "LOC " : "CLD " }),
|
|
1032
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, children: truncate(r.isMe ? "this machine" : r.agent, 12).padEnd(13) }),
|
|
862
1033
|
/* @__PURE__ */ jsxs2(Text2, { children: [
|
|
863
|
-
String(
|
|
1034
|
+
String(r.calls).padStart(4),
|
|
864
1035
|
"c "
|
|
865
1036
|
] }),
|
|
866
|
-
/* @__PURE__ */ jsxs2(Text2, { color:
|
|
867
|
-
String(
|
|
1037
|
+
/* @__PURE__ */ jsxs2(Text2, { color: r.denies ? theme.bad : theme.dim, children: [
|
|
1038
|
+
String(r.denies).padStart(3),
|
|
868
1039
|
"d "
|
|
869
1040
|
] }),
|
|
870
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ago(
|
|
871
|
-
] }, id);
|
|
872
|
-
}),
|
|
873
|
-
sess.slice(0, Math.max(0, colH - 1 - Math.min(localSessList.length, Math.max(1, colH - 2)))).map((a) => {
|
|
874
|
-
const dot = sessionDot(a.status);
|
|
875
|
-
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
876
|
-
/* @__PURE__ */ jsxs2(Text2, { color: dot.color, children: [
|
|
877
|
-
dot.ch,
|
|
878
|
-
" "
|
|
879
|
-
] }),
|
|
880
|
-
/* @__PURE__ */ jsx2(Text2, { color: "#4f6db8", children: "CLD " }),
|
|
881
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate(a.agent_name ?? a.session_id, 12).padEnd(13) }),
|
|
882
|
-
/* @__PURE__ */ jsxs2(Text2, { children: [
|
|
883
|
-
String(a.total_calls).padStart(4),
|
|
884
|
-
"c "
|
|
885
|
-
] }),
|
|
886
|
-
/* @__PURE__ */ jsxs2(Text2, { color: a.denied_calls ? theme.bad : theme.dim, children: [
|
|
887
|
-
String(a.denied_calls).padStart(3),
|
|
888
|
-
"d "
|
|
889
|
-
] }),
|
|
890
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: `t${a.trust_score} ` + ago(a.last_seen_at) })
|
|
891
|
-
] }, a.session_id);
|
|
1041
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: (r.trust != null ? `t${r.trust} ` : "") + ago(r.lastAt) })
|
|
1042
|
+
] }, r.id);
|
|
892
1043
|
})
|
|
893
1044
|
] }),
|
|
894
1045
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, overflow: "hidden", children: [
|
|
@@ -907,7 +1058,7 @@ function LivePanel({ active: active2 }) {
|
|
|
907
1058
|
PaneTitle,
|
|
908
1059
|
{
|
|
909
1060
|
label: "TOOL STREAM",
|
|
910
|
-
extra: `last ${STREAM_KEEP} \xB7 filter ${filter}${clampedScroll > 0 ? ` \xB7 \
|
|
1061
|
+
extra: `newest first \xB7 last ${STREAM_KEEP} \xB7 filter ${filter}${clampedScroll > 0 ? ` \xB7 \u25BC${clampedScroll} older` : ""} \xB7 f filter \xB7 \u2191\u2193 scroll \xB7 c copy \xB7 s sessions`,
|
|
911
1062
|
width: innerW
|
|
912
1063
|
}
|
|
913
1064
|
),
|
|
@@ -920,28 +1071,12 @@ function LivePanel({ active: active2 }) {
|
|
|
920
1071
|
spin,
|
|
921
1072
|
" awaiting traffic\u2026"
|
|
922
1073
|
] }) : null,
|
|
923
|
-
windowed.map((e) => /* @__PURE__ */
|
|
924
|
-
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
925
|
-
"[",
|
|
926
|
-
hhmmss(e.at),
|
|
927
|
-
" "
|
|
928
|
-
] }),
|
|
929
|
-
/* @__PURE__ */ jsx2(Text2, { color: e.source === "local" ? theme.ok : "#4f6db8", children: e.source === "local" ? "LOC" : "CLD" }),
|
|
930
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "] " }),
|
|
931
|
-
/* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), bold: e.decision !== "ALLOW", children: e.decision.padEnd(6) }),
|
|
932
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate(e.tool, 12).padEnd(13) }),
|
|
933
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.permission.padEnd(5) }),
|
|
934
|
-
/* @__PURE__ */ jsx2(Text2, { color: e.evalMs != null && e.evalMs > 500 ? theme.warn : theme.dim, children: (e.evalMs != null ? `${e.evalMs}ms` : "\u2014").padEnd(7) }),
|
|
935
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: truncate(e.agent ?? "-", 11).padEnd(12) }),
|
|
936
|
-
e.dlp ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: "DLP! " }) : null,
|
|
937
|
-
e.rule && e.decision !== "ALLOW" ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: truncate(e.rule, 14) + " " }) : null,
|
|
938
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.detail })
|
|
939
|
-
] }, e.id))
|
|
1074
|
+
windowed.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e }, e.id))
|
|
940
1075
|
] }),
|
|
941
1076
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
942
1077
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " LIVE " }),
|
|
943
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` local ${localOn ? "on" : "off"} \xB7 ${localBuf.length} loc/${cloudBuf.length} cld
|
|
944
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " f filter \xB7
|
|
1078
|
+
/* @__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"} ` }),
|
|
1079
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " f filter \xB7 s sessions \xB7 c copy \xB7 esc menu \xB7 q quit " })
|
|
945
1080
|
] })
|
|
946
1081
|
] });
|
|
947
1082
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.70.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": {
|