@solongate/proxy 0.81.23 → 0.81.24
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 +569 -449
- package/dist/tui/index.js +517 -393
- package/dist/tui/local-log.d.ts +22 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6844,6 +6844,45 @@ var init_components = __esm({
|
|
|
6844
6844
|
}
|
|
6845
6845
|
});
|
|
6846
6846
|
|
|
6847
|
+
// src/tui/local-log.ts
|
|
6848
|
+
import { closeSync, openSync, readSync, statSync } from "fs";
|
|
6849
|
+
import { homedir as homedir4 } from "os";
|
|
6850
|
+
import { join as join6 } from "path";
|
|
6851
|
+
function tailLines(file, maxBytes = 131072) {
|
|
6852
|
+
try {
|
|
6853
|
+
const size = statSync(file).size;
|
|
6854
|
+
const start = Math.max(0, size - maxBytes);
|
|
6855
|
+
const fd = openSync(file, "r");
|
|
6856
|
+
const buf = Buffer.alloc(size - start);
|
|
6857
|
+
readSync(fd, buf, 0, buf.length, start);
|
|
6858
|
+
closeSync(fd);
|
|
6859
|
+
const lines = buf.toString("utf-8").split("\n").filter(Boolean);
|
|
6860
|
+
if (start > 0) lines.shift();
|
|
6861
|
+
return lines;
|
|
6862
|
+
} catch {
|
|
6863
|
+
return [];
|
|
6864
|
+
}
|
|
6865
|
+
}
|
|
6866
|
+
function parseLocalLines(lines) {
|
|
6867
|
+
const out2 = [];
|
|
6868
|
+
for (const line of lines) {
|
|
6869
|
+
try {
|
|
6870
|
+
const j = JSON.parse(line);
|
|
6871
|
+
const at = Date.parse(j.ts ?? "");
|
|
6872
|
+
if (Number.isFinite(at)) out2.push({ ...j, at });
|
|
6873
|
+
} catch {
|
|
6874
|
+
}
|
|
6875
|
+
}
|
|
6876
|
+
return out2;
|
|
6877
|
+
}
|
|
6878
|
+
var LOCAL_LOG;
|
|
6879
|
+
var init_local_log = __esm({
|
|
6880
|
+
"src/tui/local-log.ts"() {
|
|
6881
|
+
"use strict";
|
|
6882
|
+
LOCAL_LOG = join6(homedir4(), ".solongate", "local-logs", "solongate-audit.jsonl");
|
|
6883
|
+
}
|
|
6884
|
+
});
|
|
6885
|
+
|
|
6847
6886
|
// src/api-client/types.ts
|
|
6848
6887
|
var init_types = __esm({
|
|
6849
6888
|
"src/api-client/types.ts"() {
|
|
@@ -7222,26 +7261,11 @@ var init_hooks = __esm({
|
|
|
7222
7261
|
// src/tui/panels/Live.tsx
|
|
7223
7262
|
import { Box as Box2, Text as Text2, useInput } from "ink";
|
|
7224
7263
|
import TextInput from "ink-text-input";
|
|
7225
|
-
import {
|
|
7226
|
-
import { homedir as
|
|
7227
|
-
import { join as
|
|
7264
|
+
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
7265
|
+
import { homedir as homedir5 } from "os";
|
|
7266
|
+
import { join as join7 } from "path";
|
|
7228
7267
|
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
7229
7268
|
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
7230
|
-
function tailLines(file, maxBytes = 131072) {
|
|
7231
|
-
try {
|
|
7232
|
-
const size = statSync(file).size;
|
|
7233
|
-
const start = Math.max(0, size - maxBytes);
|
|
7234
|
-
const fd = openSync(file, "r");
|
|
7235
|
-
const buf = Buffer.alloc(size - start);
|
|
7236
|
-
readSync(fd, buf, 0, buf.length, start);
|
|
7237
|
-
closeSync(fd);
|
|
7238
|
-
const lines = buf.toString("utf-8").split("\n").filter(Boolean);
|
|
7239
|
-
if (start > 0) lines.shift();
|
|
7240
|
-
return lines;
|
|
7241
|
-
} catch {
|
|
7242
|
-
return [];
|
|
7243
|
-
}
|
|
7244
|
-
}
|
|
7245
7269
|
function extractTarget(detail) {
|
|
7246
7270
|
try {
|
|
7247
7271
|
const j = JSON.parse(detail);
|
|
@@ -7785,9 +7809,9 @@ function LivePanel({ active: active2 }) {
|
|
|
7785
7809
|
else if (input === "x") toggleSignal("dlp");
|
|
7786
7810
|
else if (input === "r") toggleSignal("ratelimit");
|
|
7787
7811
|
else if (input === "e") {
|
|
7788
|
-
const file =
|
|
7812
|
+
const file = join7(homedir5(), ".solongate", "live-export.jsonl");
|
|
7789
7813
|
try {
|
|
7790
|
-
mkdirSync3(
|
|
7814
|
+
mkdirSync3(join7(homedir5(), ".solongate"), { recursive: true });
|
|
7791
7815
|
writeFileSync3(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
|
|
7792
7816
|
setActionMsg({ text: `\u2713 exported ${visibleDesc.length} lines \u2192 ${file}`, level: "ok", until: Date.now() + 6e3 });
|
|
7793
7817
|
} catch (err2) {
|
|
@@ -8103,10 +8127,11 @@ function LivePanel({ active: active2 }) {
|
|
|
8103
8127
|
] })
|
|
8104
8128
|
] });
|
|
8105
8129
|
}
|
|
8106
|
-
var CONFIG, SPIN, BG, DIM_FLOOR,
|
|
8130
|
+
var CONFIG, SPIN, BG, DIM_FLOOR, RING, hhmmss, fmtUp, FILTERS, sessStatus, STATUS_STYLE, LIVE_HELP;
|
|
8107
8131
|
var init_Live = __esm({
|
|
8108
8132
|
"src/tui/panels/Live.tsx"() {
|
|
8109
8133
|
"use strict";
|
|
8134
|
+
init_local_log();
|
|
8110
8135
|
init_api_client();
|
|
8111
8136
|
init_config2();
|
|
8112
8137
|
init_notify();
|
|
@@ -8116,8 +8141,7 @@ var init_Live = __esm({
|
|
|
8116
8141
|
SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
8117
8142
|
BG = "#12234f";
|
|
8118
8143
|
DIM_FLOOR = "#233457";
|
|
8119
|
-
|
|
8120
|
-
RING = join6(process.cwd(), ".solongate", ".eval-ring.jsonl");
|
|
8144
|
+
RING = join7(process.cwd(), ".solongate", ".eval-ring.jsonl");
|
|
8121
8145
|
hhmmss = (ts) => {
|
|
8122
8146
|
const d = new Date(ts);
|
|
8123
8147
|
return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
|
|
@@ -8200,7 +8224,6 @@ function PoliciesPanel({ focused }) {
|
|
|
8200
8224
|
const [pi, setPi] = useState3(0);
|
|
8201
8225
|
const [ri, setRi] = useState3(0);
|
|
8202
8226
|
const [fi, setFi] = useState3(0);
|
|
8203
|
-
const [vi, setVi] = useState3(0);
|
|
8204
8227
|
const [rules, setRules] = useState3([]);
|
|
8205
8228
|
const [mode, setMode] = useState3("denylist");
|
|
8206
8229
|
const [dirty, setDirty] = useState3(false);
|
|
@@ -8209,15 +8232,6 @@ function PoliciesPanel({ focused }) {
|
|
|
8209
8232
|
const [status, setStatus] = useState3(null);
|
|
8210
8233
|
const selected = policies[Math.min(pi, Math.max(0, policies.length - 1))];
|
|
8211
8234
|
const detail = useLoader(() => selected ? api.policies.get(selected.id) : Promise.resolve(null), [selected?.id]);
|
|
8212
|
-
const versionsQ = useLoader(
|
|
8213
|
-
() => view === "versions" && selected ? api.policies.versions(selected.id, { limit: 50 }) : Promise.resolve(null),
|
|
8214
|
-
[view, selected?.id]
|
|
8215
|
-
);
|
|
8216
|
-
const diffVer = versionsQ.data?.versions[vi]?.version;
|
|
8217
|
-
const diffQ = useLoader(
|
|
8218
|
-
() => view === "versions" && selected && diffVer !== void 0 ? api.policies.get(selected.id, diffVer) : Promise.resolve(null),
|
|
8219
|
-
[view, selected?.id, diffVer]
|
|
8220
|
-
);
|
|
8221
8235
|
useEffect3(() => {
|
|
8222
8236
|
if (detail.data) {
|
|
8223
8237
|
setRules(detail.data.rules);
|
|
@@ -8315,32 +8329,10 @@ function PoliciesPanel({ focused }) {
|
|
|
8315
8329
|
} else if (input === "D") {
|
|
8316
8330
|
setStatus("Dry-running\u2026");
|
|
8317
8331
|
void api.policies.dryRun({ rules, mode }).then((res) => setStatus(`dry-run ${res.evaluated} calls \xB7 allow ${res.would_allow} / deny ${res.would_deny} \xB7 newly blocked ${res.newly_blocked} \xB7 newly allowed ${res.newly_allowed}`)).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
|
|
8318
|
-
} else if (input === "v") {
|
|
8319
|
-
setVi(0);
|
|
8320
|
-
setView("versions");
|
|
8321
8332
|
} else if (input === "s") void save();
|
|
8322
8333
|
else if (input === "x") discard();
|
|
8323
8334
|
return;
|
|
8324
8335
|
}
|
|
8325
|
-
if (view === "versions") {
|
|
8326
|
-
const vers = versionsQ.data?.versions ?? [];
|
|
8327
|
-
if (key.leftArrow) return void setView("rules");
|
|
8328
|
-
if (key.upArrow) setVi((n) => Math.max(0, n - 1));
|
|
8329
|
-
else if (key.downArrow) setVi((n) => Math.min(vers.length - 1, n + 1));
|
|
8330
|
-
else if (key.return) {
|
|
8331
|
-
const v = vers[vi];
|
|
8332
|
-
if (v && selected) {
|
|
8333
|
-
setStatus("Rolling back\u2026");
|
|
8334
|
-
void api.policies.rollback(selected.id, v.version).then((r) => {
|
|
8335
|
-
setStatus(`\u2713 rolled back to v${v.version} \u2192 new v${r.version}`);
|
|
8336
|
-
detail.reload();
|
|
8337
|
-
list6.reload();
|
|
8338
|
-
setView("rules");
|
|
8339
|
-
}).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
|
|
8340
|
-
}
|
|
8341
|
-
}
|
|
8342
|
-
return;
|
|
8343
|
-
}
|
|
8344
8336
|
const rule2 = rules[ri];
|
|
8345
8337
|
if (!rule2) return setView("rules");
|
|
8346
8338
|
const field = FIELDS[fi];
|
|
@@ -8381,8 +8373,7 @@ function PoliciesPanel({ focused }) {
|
|
|
8381
8373
|
p.mode.padEnd(10),
|
|
8382
8374
|
" ",
|
|
8383
8375
|
p.rules.length,
|
|
8384
|
-
" rules
|
|
8385
|
-
p.version
|
|
8376
|
+
" rules"
|
|
8386
8377
|
] }),
|
|
8387
8378
|
p.id === activeId ? /* @__PURE__ */ jsx3(Text3, { color: theme.ok, bold: true, children: " \u25CF ACTIVE" }) : null
|
|
8388
8379
|
] }, p.id)) }),
|
|
@@ -8398,7 +8389,7 @@ function PoliciesPanel({ focused }) {
|
|
|
8398
8389
|
/* @__PURE__ */ jsx3(Text3, { color: mode === "whitelist" ? theme.warn : void 0, children: mode }),
|
|
8399
8390
|
dirtyTag
|
|
8400
8391
|
] }),
|
|
8401
|
-
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "\u2191\u2193 \xB7 enter edit \xB7 space on/off \xB7 e effect \xB7 n new \xB7 d del \xB7 m mode \xB7 D dry-run \xB7
|
|
8392
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "\u2191\u2193 \xB7 enter edit \xB7 space on/off \xB7 e effect \xB7 n new \xB7 d del \xB7 m mode \xB7 D dry-run \xB7 s save \xB7 \u2190 back" }),
|
|
8402
8393
|
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(
|
|
8403
8394
|
Table,
|
|
8404
8395
|
{
|
|
@@ -8424,50 +8415,6 @@ function PoliciesPanel({ focused }) {
|
|
|
8424
8415
|
status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
8425
8416
|
] }) });
|
|
8426
8417
|
}
|
|
8427
|
-
if (view === "versions") {
|
|
8428
|
-
const vers = versionsQ.data?.versions ?? [];
|
|
8429
|
-
return /* @__PURE__ */ jsx3(DataView, { loading: versionsQ.loading && !versionsQ.data, error: versionsQ.error, empty: !!versionsQ.data && vers.length === 0, emptyText: "No versions.", children: /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
8430
|
-
/* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.accentBright, children: truncate2(selected?.name ?? "", 28) }),
|
|
8431
|
-
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "\u2191\u2193 select \xB7 enter roll back to that version \xB7 \u2190 back" }),
|
|
8432
|
-
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(
|
|
8433
|
-
Table,
|
|
8434
|
-
{
|
|
8435
|
-
columns: [
|
|
8436
|
-
{ header: "", width: 2 },
|
|
8437
|
-
{ header: "VER", width: 5 },
|
|
8438
|
-
{ header: "RULES", width: 5 },
|
|
8439
|
-
{ header: "REASON", width: 34 },
|
|
8440
|
-
{ header: "WHEN", width: 16 }
|
|
8441
|
-
],
|
|
8442
|
-
rows: vers.map((v, i) => [
|
|
8443
|
-
{ value: i === vi ? "\u25B8" : "", color: theme.accentBright },
|
|
8444
|
-
{ value: `v${v.version}`, bold: i === vi },
|
|
8445
|
-
{ value: String(v.rules_count), dim: true },
|
|
8446
|
-
{ value: truncate2(v.reason || "\u2014", 34) },
|
|
8447
|
-
{ value: truncate2(v.created_at, 16), dim: true }
|
|
8448
|
-
])
|
|
8449
|
-
}
|
|
8450
|
-
) }),
|
|
8451
|
-
/* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
|
|
8452
|
-
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: `Diff v${diffVer ?? "?"} vs current draft` }),
|
|
8453
|
-
(() => {
|
|
8454
|
-
const old = diffQ.data?.rules ?? [];
|
|
8455
|
-
const cur = rules;
|
|
8456
|
-
const oldIds = new Set(old.map((r) => r.id));
|
|
8457
|
-
const curIds = new Set(cur.map((r) => r.id));
|
|
8458
|
-
const added = cur.filter((r) => !oldIds.has(r.id));
|
|
8459
|
-
const removed = old.filter((r) => !curIds.has(r.id));
|
|
8460
|
-
if (diffQ.loading && !diffQ.data) return /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: " loading\u2026" });
|
|
8461
|
-
if (!added.length && !removed.length) return /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: " identical rule set" });
|
|
8462
|
-
return /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
8463
|
-
added.slice(0, 4).map((r) => /* @__PURE__ */ jsx3(Text3, { wrap: "truncate", color: theme.ok, children: ` + ${r.effect} ${truncate2(r.toolPattern, 18)} ${truncate2(r.description || r.id, 30)}` }, "a" + r.id)),
|
|
8464
|
-
removed.slice(0, 4).map((r) => /* @__PURE__ */ jsx3(Text3, { wrap: "truncate", color: theme.bad, children: ` - ${r.effect} ${truncate2(r.toolPattern, 18)} ${truncate2(r.description || r.id, 30)}` }, "r" + r.id))
|
|
8465
|
-
] });
|
|
8466
|
-
})()
|
|
8467
|
-
] }),
|
|
8468
|
-
status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
8469
|
-
] }) });
|
|
8470
|
-
}
|
|
8471
8418
|
const rule = rules[ri];
|
|
8472
8419
|
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
8473
8420
|
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
@@ -8880,177 +8827,202 @@ var init_Dlp = __esm({
|
|
|
8880
8827
|
}
|
|
8881
8828
|
});
|
|
8882
8829
|
|
|
8883
|
-
// src/tui/panels/Stats.tsx
|
|
8884
|
-
import { Box as Box6, Text as Text6 } from "ink";
|
|
8885
|
-
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
8886
|
-
function StatsPanel({ active: active2 }) {
|
|
8887
|
-
const stats = useLoader(() => api.stats.get());
|
|
8888
|
-
const ts = useLoader(() => api.stats.timeseries({ period: "24h" }));
|
|
8889
|
-
const drift2 = useLoader(() => api.stats.drift(7));
|
|
8890
|
-
usePoll(() => {
|
|
8891
|
-
stats.reload();
|
|
8892
|
-
ts.reload();
|
|
8893
|
-
}, 5e3, active2);
|
|
8894
|
-
usePoll(drift2.reload, 3e4, active2);
|
|
8895
|
-
const s = stats.data;
|
|
8896
|
-
const points = ts.data?.timeseries ?? [];
|
|
8897
|
-
const driftRules = (drift2.data?.rules ?? []).slice(0, 5);
|
|
8898
|
-
return /* @__PURE__ */ jsx6(DataView, { loading: stats.loading && !s, error: stats.error, children: s ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
8899
|
-
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
8900
|
-
/* @__PURE__ */ jsxs6(Text6, { bold: true, children: [
|
|
8901
|
-
s.total_calls,
|
|
8902
|
-
" "
|
|
8903
|
-
] }),
|
|
8904
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "calls " }),
|
|
8905
|
-
/* @__PURE__ */ jsxs6(Text6, { color: theme.ok, children: [
|
|
8906
|
-
s.allowed,
|
|
8907
|
-
" allowed"
|
|
8908
|
-
] }),
|
|
8909
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " " }),
|
|
8910
|
-
/* @__PURE__ */ jsxs6(Text6, { color: theme.bad, children: [
|
|
8911
|
-
s.denied,
|
|
8912
|
-
" denied"
|
|
8913
|
-
] })
|
|
8914
|
-
] }),
|
|
8915
|
-
/* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
8916
|
-
s.active_policies,
|
|
8917
|
-
" policies \xB7 ",
|
|
8918
|
-
s.registered_tools,
|
|
8919
|
-
" tools"
|
|
8920
|
-
] }),
|
|
8921
|
-
/* @__PURE__ */ jsxs6(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
8922
|
-
/* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
8923
|
-
"Last 24h ",
|
|
8924
|
-
ts.data ? `(per ${ts.data.granularity})` : ""
|
|
8925
|
-
] }),
|
|
8926
|
-
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
8927
|
-
/* @__PURE__ */ jsx6(Text6, { children: "total " }),
|
|
8928
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.accent, children: sparkline(points.map((p) => p.total), 64) })
|
|
8929
|
-
] }),
|
|
8930
|
-
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
8931
|
-
/* @__PURE__ */ jsx6(Text6, { children: "allowed " }),
|
|
8932
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.ok, children: sparkline(points.map((p) => p.allowed), 64) })
|
|
8933
|
-
] }),
|
|
8934
|
-
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
8935
|
-
/* @__PURE__ */ jsx6(Text6, { children: "denied " }),
|
|
8936
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.bad, children: sparkline(points.map((p) => p.denied), 64) })
|
|
8937
|
-
] })
|
|
8938
|
-
] }),
|
|
8939
|
-
/* @__PURE__ */ jsxs6(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
8940
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "Recent" }),
|
|
8941
|
-
/* @__PURE__ */ jsx6(
|
|
8942
|
-
Table,
|
|
8943
|
-
{
|
|
8944
|
-
columns: [
|
|
8945
|
-
{ header: "DECISION", width: 9 },
|
|
8946
|
-
{ header: "TOOL", width: 20 },
|
|
8947
|
-
{ header: "MS", width: 5 },
|
|
8948
|
-
{ header: "WHEN", width: 6 }
|
|
8949
|
-
],
|
|
8950
|
-
rows: (s.recent_activity ?? []).slice(0, 6).map((a) => [
|
|
8951
|
-
{ value: a.decision, color: decisionColor(a.decision) },
|
|
8952
|
-
{ value: truncate2(a.tool_name, 20), color: theme.accent },
|
|
8953
|
-
{ value: String(a.evaluation_time_ms ?? "\u2014"), dim: true },
|
|
8954
|
-
{ value: ago(a.created_at), dim: true }
|
|
8955
|
-
])
|
|
8956
|
-
}
|
|
8957
|
-
)
|
|
8958
|
-
] }),
|
|
8959
|
-
/* @__PURE__ */ jsxs6(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
8960
|
-
/* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
8961
|
-
"Denial drift ",
|
|
8962
|
-
drift2.data ? `(7d: ${drift2.data.total_current} now vs ${drift2.data.total_previous} prev)` : ""
|
|
8963
|
-
] }),
|
|
8964
|
-
driftRules.length ? /* @__PURE__ */ jsx6(
|
|
8965
|
-
Table,
|
|
8966
|
-
{
|
|
8967
|
-
columns: [
|
|
8968
|
-
{ header: "NOW", width: 5 },
|
|
8969
|
-
{ header: "PREV", width: 5 },
|
|
8970
|
-
{ header: "\u0394", width: 6 },
|
|
8971
|
-
{ header: "RULE", width: 22 },
|
|
8972
|
-
{ header: "REASON", width: 26 }
|
|
8973
|
-
],
|
|
8974
|
-
rows: driftRules.map((r) => [
|
|
8975
|
-
{ value: String(r.current), bold: true },
|
|
8976
|
-
{ value: String(r.previous), dim: true },
|
|
8977
|
-
{ value: r.is_new ? "NEW" : r.spike ? `+${r.delta}` : String(r.delta), color: r.is_new ? theme.ok : r.spike ? theme.bad : void 0 },
|
|
8978
|
-
{ value: truncate2(r.rule_id ?? "\u2014", 22), color: theme.accent },
|
|
8979
|
-
{ value: truncate2(r.reason ?? r.last_tool ?? "\u2014", 26), dim: true }
|
|
8980
|
-
])
|
|
8981
|
-
}
|
|
8982
|
-
) : /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " no denials in window" })
|
|
8983
|
-
] })
|
|
8984
|
-
] }) : null });
|
|
8985
|
-
}
|
|
8986
|
-
var init_Stats = __esm({
|
|
8987
|
-
"src/tui/panels/Stats.tsx"() {
|
|
8988
|
-
"use strict";
|
|
8989
|
-
init_api_client();
|
|
8990
|
-
init_components();
|
|
8991
|
-
init_hooks();
|
|
8992
|
-
init_theme();
|
|
8993
|
-
}
|
|
8994
|
-
});
|
|
8995
|
-
|
|
8996
8830
|
// src/tui/panels/Audit.tsx
|
|
8997
|
-
import { Box as
|
|
8831
|
+
import { Box as Box6, Text as Text6, useInput as useInput5 } from "ink";
|
|
8998
8832
|
import TextInput4 from "ink-text-input";
|
|
8999
8833
|
import { useState as useState6 } from "react";
|
|
9000
|
-
import { Fragment as Fragment4, jsx as
|
|
8834
|
+
import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
8835
|
+
function loadLocalRows() {
|
|
8836
|
+
return parseLocalLines(tailLines(LOCAL_LOG, LOCAL_MAX_BYTES)).map((j, i) => ({
|
|
8837
|
+
id: "l:" + j.at + ":" + i,
|
|
8838
|
+
at: j.at,
|
|
8839
|
+
tool: j.tool ?? "?",
|
|
8840
|
+
decision: j.decision ?? "ALLOW",
|
|
8841
|
+
permission: j.permission ?? "\u2014",
|
|
8842
|
+
trust: j.trust_level ?? "\u2014",
|
|
8843
|
+
agent: j.agent_name ?? null,
|
|
8844
|
+
session: j.session_id ?? null,
|
|
8845
|
+
reason: j.reason ?? null,
|
|
8846
|
+
rule: j.matched_rule_id ?? null,
|
|
8847
|
+
evalMs: j.evaluation_time_ms ?? null,
|
|
8848
|
+
dlp: j.dlp ? Array.isArray(j.dlp) ? j.dlp.map(String) : ["dlp"] : [],
|
|
8849
|
+
burst: !!j.rate_limit_burst,
|
|
8850
|
+
args: j.arguments ? JSON.stringify(j.arguments) : null
|
|
8851
|
+
})).sort((a, b) => b.at - a.at);
|
|
8852
|
+
}
|
|
9001
8853
|
function AuditPanel({ active: active2, focused }) {
|
|
9002
8854
|
const { cols, rows } = usePanelSize();
|
|
8855
|
+
const [source, setSource] = useState6("cloud");
|
|
8856
|
+
const [view, setView] = useState6("logs");
|
|
9003
8857
|
const [di, setDi] = useState6(0);
|
|
9004
8858
|
const [gi, setGi] = useState6(0);
|
|
9005
8859
|
const [tool, setTool] = useState6("");
|
|
9006
8860
|
const [agent, setAgent] = useState6("");
|
|
9007
8861
|
const [search, setSearch] = useState6("");
|
|
8862
|
+
const [sessFilter, setSessFilter] = useState6("");
|
|
8863
|
+
const [page, setPage] = useState6(0);
|
|
9008
8864
|
const [sel, setSel] = useState6(0);
|
|
9009
|
-
const [view, setView] = useState6("list");
|
|
9010
8865
|
const [detailScroll, setDetailScroll] = useState6(0);
|
|
9011
8866
|
const [editing, setEditing] = useState6(null);
|
|
8867
|
+
const [si, setSi] = useState6(0);
|
|
8868
|
+
const [sessSearch, setSessSearch] = useState6("");
|
|
8869
|
+
const [sessSel, setSessSel] = useState6(0);
|
|
8870
|
+
const toTop = () => setSel(0);
|
|
8871
|
+
const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
|
|
8872
|
+
const tsQ = useLoader(() => source === "cloud" ? api.stats.timeseries({ period: "24h" }) : Promise.resolve(null), [source]);
|
|
8873
|
+
usePoll(() => {
|
|
8874
|
+
statsQ.reload();
|
|
8875
|
+
tsQ.reload();
|
|
8876
|
+
}, 15e3, active2 && source === "cloud");
|
|
9012
8877
|
const query = {
|
|
9013
8878
|
filter: DECISIONS[di],
|
|
9014
8879
|
signal: SIGNALS[gi],
|
|
9015
8880
|
tool: tool || void 0,
|
|
9016
8881
|
agent_name: agent || void 0,
|
|
9017
8882
|
search: search || void 0,
|
|
9018
|
-
|
|
8883
|
+
session_id: sessFilter || void 0,
|
|
8884
|
+
limit: PAGE,
|
|
8885
|
+
offset: page * PAGE
|
|
9019
8886
|
};
|
|
9020
|
-
const
|
|
9021
|
-
|
|
9022
|
-
|
|
9023
|
-
|
|
8887
|
+
const cloudQ = useLoader(
|
|
8888
|
+
() => source === "cloud" ? api.audit.list(query) : Promise.resolve(null),
|
|
8889
|
+
[source, di, gi, tool, agent, search, sessFilter, page]
|
|
8890
|
+
);
|
|
8891
|
+
usePoll(cloudQ.reload, 6e3, active2 && source === "cloud" && view === "logs" && !editing && page === 0);
|
|
8892
|
+
const localQ = useLoader(() => source === "local" ? Promise.resolve(loadLocalRows()) : Promise.resolve(null), [source]);
|
|
8893
|
+
usePoll(localQ.reload, 6e3, active2 && source === "local" && view === "logs" && !editing && page === 0);
|
|
8894
|
+
let pageRows = [];
|
|
8895
|
+
let total = 0;
|
|
8896
|
+
if (source === "cloud") {
|
|
8897
|
+
pageRows = (cloudQ.data?.entries ?? []).map(cloudRow).sort((a, b) => b.at - a.at);
|
|
8898
|
+
total = cloudQ.data?.total ?? 0;
|
|
8899
|
+
} else {
|
|
8900
|
+
const all = localQ.data ?? [];
|
|
8901
|
+
const q = search.trim().toLowerCase();
|
|
8902
|
+
const filtered = all.filter((r) => {
|
|
8903
|
+
if (DECISIONS[di] && r.decision !== DECISIONS[di]) return false;
|
|
8904
|
+
if (SIGNALS[gi] === "dlp" && r.dlp.length === 0) return false;
|
|
8905
|
+
if (SIGNALS[gi] === "ratelimit" && !r.burst) return false;
|
|
8906
|
+
if (tool && !r.tool.toLowerCase().includes(tool.toLowerCase())) return false;
|
|
8907
|
+
if (agent && (r.agent ?? "").toLowerCase() !== agent.toLowerCase()) return false;
|
|
8908
|
+
if (sessFilter && r.session !== sessFilter) return false;
|
|
8909
|
+
if (q && !`${r.tool} ${r.agent ?? ""} ${r.reason ?? ""} ${r.args ?? ""}`.toLowerCase().includes(q)) return false;
|
|
8910
|
+
return true;
|
|
8911
|
+
});
|
|
8912
|
+
total = filtered.length;
|
|
8913
|
+
pageRows = filtered.slice(page * PAGE, page * PAGE + PAGE);
|
|
8914
|
+
}
|
|
8915
|
+
const pages = Math.max(1, Math.ceil(total / PAGE));
|
|
8916
|
+
const current = pageRows[Math.min(sel, Math.max(0, pageRows.length - 1))];
|
|
8917
|
+
const agentsQ = useLoader(
|
|
8918
|
+
() => source === "cloud" ? api.agents.live({ limit: 100, includeDeactivated: true }) : Promise.resolve(null),
|
|
8919
|
+
[source]
|
|
8920
|
+
);
|
|
8921
|
+
usePoll(agentsQ.reload, 8e3, active2 && source === "cloud" && view === "sessions");
|
|
8922
|
+
let sessions = [];
|
|
8923
|
+
if (source === "cloud") {
|
|
8924
|
+
sessions = (agentsQ.data?.agents ?? []).map((a) => ({
|
|
8925
|
+
id: a.session_id,
|
|
8926
|
+
agent: a.agent_name ?? a.session_id,
|
|
8927
|
+
status: a.status === "deactivated" ? "ended" : a.status,
|
|
8928
|
+
calls: a.total_calls,
|
|
8929
|
+
denies: a.denied_calls,
|
|
8930
|
+
dlp: a.dlp_events,
|
|
8931
|
+
trust: a.trust_score,
|
|
8932
|
+
lastAt: Date.parse(a.last_seen_at)
|
|
8933
|
+
}));
|
|
8934
|
+
} else {
|
|
8935
|
+
const bySess = /* @__PURE__ */ new Map();
|
|
8936
|
+
for (const r of localQ.data ?? []) {
|
|
8937
|
+
if (!r.session) continue;
|
|
8938
|
+
const cur = bySess.get(r.session) ?? { id: r.session, agent: r.agent ?? "local agent", status: "ended", calls: 0, denies: 0, dlp: 0, trust: null, lastAt: 0 };
|
|
8939
|
+
cur.calls++;
|
|
8940
|
+
if (r.decision !== "ALLOW") cur.denies++;
|
|
8941
|
+
if (r.dlp.length) cur.dlp++;
|
|
8942
|
+
cur.lastAt = Math.max(cur.lastAt, r.at);
|
|
8943
|
+
if (r.agent) cur.agent = r.agent;
|
|
8944
|
+
bySess.set(r.session, cur);
|
|
8945
|
+
}
|
|
8946
|
+
sessions = [...bySess.values()];
|
|
8947
|
+
}
|
|
8948
|
+
const sq = sessSearch.trim().toLowerCase();
|
|
8949
|
+
const sessionsFiltered = sessions.map((s) => ({ ...s, status: source === "cloud" ? s.status : sessStatus2(s.lastAt) })).filter((s) => SESS_STATUS[si] ? s.status === SESS_STATUS[si] : true).filter((s) => !sq || `${s.agent} ${s.id}`.toLowerCase().includes(sq)).sort((a, b) => b.lastAt - a.lastAt);
|
|
8950
|
+
const currentSess = sessionsFiltered[Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1))];
|
|
9024
8951
|
const sessionQ = useLoader(
|
|
9025
|
-
() => view === "detail" && current?.
|
|
9026
|
-
[view, current?.
|
|
8952
|
+
() => view === "detail" && current?.session && source === "cloud" ? api.audit.list({ session_id: current.session, limit: 40 }) : Promise.resolve(null),
|
|
8953
|
+
[view, current?.session, source]
|
|
9027
8954
|
);
|
|
9028
8955
|
useInput5(
|
|
9029
8956
|
(input, key) => {
|
|
9030
8957
|
if (view === "detail") {
|
|
9031
|
-
if (key.leftArrow || key.escape) setView("
|
|
8958
|
+
if (key.leftArrow || key.escape) setView("logs");
|
|
9032
8959
|
else if (key.upArrow) setDetailScroll((n) => Math.max(0, n - 1));
|
|
9033
8960
|
else if (key.downArrow) setDetailScroll((n) => n + 1);
|
|
9034
8961
|
else if (key.pageUp) setDetailScroll((n) => Math.max(0, n - 10));
|
|
9035
8962
|
else if (key.pageDown) setDetailScroll((n) => n + 10);
|
|
9036
8963
|
return;
|
|
9037
8964
|
}
|
|
9038
|
-
|
|
8965
|
+
if (input === "s") {
|
|
8966
|
+
setSource((s) => s === "cloud" ? "local" : "cloud");
|
|
8967
|
+
setPage(0);
|
|
8968
|
+
toTop();
|
|
8969
|
+
setSessSel(0);
|
|
8970
|
+
return;
|
|
8971
|
+
}
|
|
8972
|
+
if (input === "v") {
|
|
8973
|
+
setView((v) => v === "logs" ? "sessions" : "logs");
|
|
8974
|
+
return;
|
|
8975
|
+
}
|
|
8976
|
+
if (view === "sessions") {
|
|
8977
|
+
if (key.upArrow) setSessSel((n) => Math.max(0, n - 1));
|
|
8978
|
+
else if (key.downArrow) setSessSel((n) => Math.min(sessionsFiltered.length - 1, n + 1));
|
|
8979
|
+
else if (key.pageUp) setSessSel((n) => Math.max(0, n - 10));
|
|
8980
|
+
else if (key.pageDown) setSessSel((n) => Math.min(sessionsFiltered.length - 1, n + 10));
|
|
8981
|
+
else if (key.return || key.rightArrow) {
|
|
8982
|
+
if (currentSess) {
|
|
8983
|
+
setSessFilter(currentSess.id);
|
|
8984
|
+
setPage(0);
|
|
8985
|
+
toTop();
|
|
8986
|
+
setView("logs");
|
|
8987
|
+
}
|
|
8988
|
+
} else if (input === "f") {
|
|
8989
|
+
setSi((n) => (n + 1) % SESS_STATUS.length);
|
|
8990
|
+
setSessSel(0);
|
|
8991
|
+
} else if (input === "/") setEditing("sess-search");
|
|
8992
|
+
else if (input === "c") {
|
|
8993
|
+
setSi(0);
|
|
8994
|
+
setSessSearch("");
|
|
8995
|
+
setSessSel(0);
|
|
8996
|
+
}
|
|
8997
|
+
return;
|
|
8998
|
+
}
|
|
9039
8999
|
if (key.upArrow) setSel((n) => Math.max(0, n - 1));
|
|
9040
|
-
else if (key.downArrow) setSel((n) => Math.min(
|
|
9000
|
+
else if (key.downArrow) setSel((n) => Math.min(pageRows.length - 1, n + 1));
|
|
9041
9001
|
else if (key.pageUp) setSel((n) => Math.max(0, n - 10));
|
|
9042
|
-
else if (key.pageDown) setSel((n) => Math.min(
|
|
9002
|
+
else if (key.pageDown) setSel((n) => Math.min(pageRows.length - 1, n + 10));
|
|
9043
9003
|
else if (key.return || key.rightArrow) {
|
|
9044
9004
|
if (current) {
|
|
9045
9005
|
setDetailScroll(0);
|
|
9046
9006
|
setView("detail");
|
|
9047
9007
|
}
|
|
9008
|
+
} else if (input === "]") {
|
|
9009
|
+
if (page < pages - 1) {
|
|
9010
|
+
setPage(page + 1);
|
|
9011
|
+
toTop();
|
|
9012
|
+
}
|
|
9013
|
+
} else if (input === "[") {
|
|
9014
|
+
if (page > 0) {
|
|
9015
|
+
setPage(page - 1);
|
|
9016
|
+
toTop();
|
|
9017
|
+
}
|
|
9048
9018
|
} else if (input === "f") {
|
|
9049
9019
|
setDi((n) => (n + 1) % DECISIONS.length);
|
|
9050
|
-
|
|
9020
|
+
setPage(0);
|
|
9021
|
+
toTop();
|
|
9051
9022
|
} else if (input === "g") {
|
|
9052
9023
|
setGi((n) => (n + 1) % SIGNALS.length);
|
|
9053
|
-
|
|
9024
|
+
setPage(0);
|
|
9025
|
+
toTop();
|
|
9054
9026
|
} else if (input === "t") setEditing("tool");
|
|
9055
9027
|
else if (input === "n") setEditing("agent");
|
|
9056
9028
|
else if (input === "/") setEditing("search");
|
|
@@ -9060,46 +9032,89 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9060
9032
|
setTool("");
|
|
9061
9033
|
setAgent("");
|
|
9062
9034
|
setSearch("");
|
|
9063
|
-
|
|
9035
|
+
setSessFilter("");
|
|
9036
|
+
setPage(0);
|
|
9037
|
+
toTop();
|
|
9064
9038
|
}
|
|
9065
9039
|
},
|
|
9066
9040
|
{ isActive: focused && !editing }
|
|
9067
9041
|
);
|
|
9042
|
+
const points = tsQ.data?.timeseries ?? [];
|
|
9043
|
+
const localAll = localQ.data ?? [];
|
|
9044
|
+
const strip = source === "cloud" ? /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
|
|
9045
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, children: statsQ.data ? statsQ.data.total_calls : "\xB7\xB7\xB7\xB7" }),
|
|
9046
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " calls \xB7 " }),
|
|
9047
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.ok, children: [
|
|
9048
|
+
statsQ.data?.allowed ?? "\xB7\xB7\xB7",
|
|
9049
|
+
" allow"
|
|
9050
|
+
] }),
|
|
9051
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \xB7 " }),
|
|
9052
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.bad, children: [
|
|
9053
|
+
statsQ.data?.denied ?? "\xB7\xB7",
|
|
9054
|
+
" deny"
|
|
9055
|
+
] }),
|
|
9056
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: ` \xB7 ${statsQ.data?.active_policies ?? "\xB7"} policies \xB7 ${statsQ.data?.registered_tools ?? "\xB7"} tools \xB7 24h ` }),
|
|
9057
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.accent, children: sparkline(points.map((p) => p.total), 24) }),
|
|
9058
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " deny " }),
|
|
9059
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.bad, children: sparkline(points.map((p) => p.denied), 24) })
|
|
9060
|
+
] }) : /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
|
|
9061
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, children: localAll.length }),
|
|
9062
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " calls in local file \xB7 " }),
|
|
9063
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.ok, children: [
|
|
9064
|
+
localAll.filter((r) => r.decision === "ALLOW").length,
|
|
9065
|
+
" allow"
|
|
9066
|
+
] }),
|
|
9067
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \xB7 " }),
|
|
9068
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.bad, children: [
|
|
9069
|
+
localAll.filter((r) => r.decision !== "ALLOW").length,
|
|
9070
|
+
" deny"
|
|
9071
|
+
] }),
|
|
9072
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
9073
|
+
" \xB7 ",
|
|
9074
|
+
LOCAL_LOG
|
|
9075
|
+
] })
|
|
9076
|
+
] });
|
|
9077
|
+
const srcChip = /* @__PURE__ */ jsxs6(Text6, { children: [
|
|
9078
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "src:" }),
|
|
9079
|
+
/* @__PURE__ */ jsx6(Text6, { color: source === "local" ? theme.ok : "#4f6db8", bold: true, children: source }),
|
|
9080
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " (s) " })
|
|
9081
|
+
] });
|
|
9068
9082
|
if (view === "detail" && current) {
|
|
9069
|
-
const sessionCalls = sessionQ.data?.entries ?? [];
|
|
9083
|
+
const sessionCalls = source === "cloud" ? (sessionQ.data?.entries ?? []).map(cloudRow) : localAll.filter((r) => r.session && r.session === current.session).slice(0, 40);
|
|
9070
9084
|
const bodyW = Math.max(20, cols - 2);
|
|
9071
9085
|
const reasonLines = wrapLines("reason: " + (current.reason ?? "\u2014"), bodyW);
|
|
9072
|
-
const argLines = current.
|
|
9073
|
-
const sessionRows = current.
|
|
9086
|
+
const argLines = current.args ? wrapLines(prettyJson(current.args), bodyW) : ["(no arguments recorded)"];
|
|
9087
|
+
const sessionRows = current.session ? Math.min(4, sessionCalls.length) + 2 : 1;
|
|
9074
9088
|
const reasonShown = reasonLines.slice(0, 4);
|
|
9075
9089
|
const argBudget = Math.max(3, rows - 2 - 5 - reasonShown.length - 1 - sessionRows - 1);
|
|
9076
9090
|
const maxScroll = Math.max(0, argLines.length - argBudget);
|
|
9077
9091
|
const off = Math.min(detailScroll, maxScroll);
|
|
9078
9092
|
const argWin = argLines.slice(off, off + argBudget);
|
|
9079
|
-
return /* @__PURE__ */
|
|
9080
|
-
/* @__PURE__ */
|
|
9081
|
-
/* @__PURE__ */
|
|
9082
|
-
/* @__PURE__ */
|
|
9083
|
-
/* @__PURE__ */
|
|
9093
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
9094
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
9095
|
+
/* @__PURE__ */ jsx6(Text6, { color: decisionColor(current.decision), bold: true, children: current.decision }),
|
|
9096
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.accent, children: " " + current.tool }),
|
|
9097
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " " + current.permission + " \xB7 " + current.trust + " " }),
|
|
9098
|
+
srcChip
|
|
9084
9099
|
] }),
|
|
9085
|
-
reasonShown.map((l, i) => /* @__PURE__ */
|
|
9086
|
-
/* @__PURE__ */
|
|
9087
|
-
/* @__PURE__ */
|
|
9088
|
-
] }) : /* @__PURE__ */
|
|
9089
|
-
/* @__PURE__ */
|
|
9090
|
-
/* @__PURE__ */
|
|
9091
|
-
/* @__PURE__ */
|
|
9092
|
-
/* @__PURE__ */
|
|
9093
|
-
/* @__PURE__ */
|
|
9094
|
-
/* @__PURE__ */
|
|
9095
|
-
/* @__PURE__ */
|
|
9096
|
-
current.
|
|
9097
|
-
/* @__PURE__ */
|
|
9100
|
+
reasonShown.map((l, i) => /* @__PURE__ */ jsx6(Text6, { wrap: "truncate", children: i === 0 ? /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
9101
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "reason " }),
|
|
9102
|
+
/* @__PURE__ */ jsx6(Text6, { children: l.slice("reason: ".length) })
|
|
9103
|
+
] }) : /* @__PURE__ */ jsx6(Text6, { children: " " + l }) }, "r" + i)),
|
|
9104
|
+
/* @__PURE__ */ jsx6(Detail, { label: "rule", value: current.rule ?? "\u2014" }),
|
|
9105
|
+
/* @__PURE__ */ jsx6(Detail, { label: "agent", value: current.agent ?? "\u2014" }),
|
|
9106
|
+
/* @__PURE__ */ jsx6(Detail, { label: "session", value: current.session ?? "\u2014" }),
|
|
9107
|
+
/* @__PURE__ */ jsx6(Detail, { label: "dlp", value: current.dlp.length ? current.dlp.join(", ") : "none", color: current.dlp.length ? theme.bad : void 0 }),
|
|
9108
|
+
/* @__PURE__ */ jsx6(Detail, { label: "when", value: new Date(current.at).toLocaleString() + (current.evalMs != null ? ` \xB7 eval ${current.evalMs}ms` : "") }),
|
|
9109
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: `arguments \u2014 full \xB7 ${argLines.length} lines${maxScroll ? ` \xB7 \u25BC${maxScroll - off} more \xB7 \u2191\u2193 scroll` : ""}` }),
|
|
9110
|
+
/* @__PURE__ */ jsx6(Box6, { flexDirection: "column", height: argBudget, overflow: "hidden", children: argWin.map((l, i) => /* @__PURE__ */ jsx6(Text6, { wrap: "truncate", children: l || " " }, off + i)) }),
|
|
9111
|
+
current.session ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
9112
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
9098
9113
|
"Session \xB7 ",
|
|
9099
9114
|
sessionCalls.length,
|
|
9100
9115
|
" calls"
|
|
9101
9116
|
] }),
|
|
9102
|
-
/* @__PURE__ */
|
|
9117
|
+
/* @__PURE__ */ jsx6(
|
|
9103
9118
|
Table,
|
|
9104
9119
|
{
|
|
9105
9120
|
columns: [
|
|
@@ -9110,59 +9125,136 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9110
9125
|
],
|
|
9111
9126
|
rows: sessionCalls.slice(0, Math.max(1, sessionRows - 2)).map((e) => [
|
|
9112
9127
|
{ value: e.decision, color: decisionColor(e.decision) },
|
|
9113
|
-
{ value: truncate2(e.
|
|
9128
|
+
{ value: truncate2(e.tool, 20), color: theme.accent },
|
|
9114
9129
|
{ value: truncate2(e.reason ?? "\u2014", Math.max(16, cols - 48)) },
|
|
9115
|
-
{ value: ago(e.
|
|
9130
|
+
{ value: ago(e.at), dim: true }
|
|
9116
9131
|
])
|
|
9117
9132
|
}
|
|
9118
9133
|
)
|
|
9119
|
-
] }) : /* @__PURE__ */
|
|
9120
|
-
/* @__PURE__ */
|
|
9134
|
+
] }) : /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "(no session id)" }),
|
|
9135
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "\u2191\u2193 scroll arguments \xB7 \u2190/esc back to logs" })
|
|
9121
9136
|
] });
|
|
9122
9137
|
}
|
|
9123
|
-
const chip = (label, val, on) => /* @__PURE__ */
|
|
9124
|
-
/* @__PURE__ */
|
|
9138
|
+
const chip = (label, val, on) => /* @__PURE__ */ jsxs6(Text6, { children: [
|
|
9139
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
9125
9140
|
label,
|
|
9126
9141
|
":"
|
|
9127
9142
|
] }),
|
|
9128
|
-
/* @__PURE__ */
|
|
9129
|
-
/* @__PURE__ */
|
|
9143
|
+
/* @__PURE__ */ jsx6(Text6, { color: on ? theme.accentBright : theme.dim, children: val }),
|
|
9144
|
+
/* @__PURE__ */ jsx6(Text6, { children: " " })
|
|
9130
9145
|
] });
|
|
9131
|
-
|
|
9146
|
+
if (view === "sessions") {
|
|
9147
|
+
const headerRows2 = 5 + (editing ? 1 : 0);
|
|
9148
|
+
const listRows2 = Math.max(4, rows - headerRows2);
|
|
9149
|
+
const selC = Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1));
|
|
9150
|
+
const start2 = Math.min(Math.max(0, selC - Math.floor((listRows2 - 1) / 2)), Math.max(0, sessionsFiltered.length - listRows2));
|
|
9151
|
+
const win = sessionsFiltered.slice(start2, start2 + listRows2);
|
|
9152
|
+
const loading2 = source === "cloud" ? agentsQ.loading && !agentsQ.data : localQ.loading && !localQ.data;
|
|
9153
|
+
return /* @__PURE__ */ jsx6(DataView, { loading: loading2, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
9154
|
+
strip,
|
|
9155
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
9156
|
+
srcChip,
|
|
9157
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.accentBright, bold: true, children: "SESSIONS" }),
|
|
9158
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \xB7 logs (v) " }),
|
|
9159
|
+
chip("status", SESS_STATUS[si] ?? "all", si !== 0),
|
|
9160
|
+
chip("search", sessSearch || "\xB7", !!sessSearch)
|
|
9161
|
+
] }),
|
|
9162
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter \u2192 session logs \xB7 f status \xB7 / search \xB7 s source \xB7 v logs \xB7 c clear" : "press \u2192 to browse" }),
|
|
9163
|
+
editing === "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
9164
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.warn, children: "search: " }),
|
|
9165
|
+
/* @__PURE__ */ jsx6(
|
|
9166
|
+
TextInput4,
|
|
9167
|
+
{
|
|
9168
|
+
value: sessSearch,
|
|
9169
|
+
onChange: (v) => {
|
|
9170
|
+
setSessSearch(v);
|
|
9171
|
+
setSessSel(0);
|
|
9172
|
+
},
|
|
9173
|
+
onSubmit: () => setEditing(null)
|
|
9174
|
+
}
|
|
9175
|
+
)
|
|
9176
|
+
] }) : null,
|
|
9177
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: `${sessionsFiltered.length} sessions \xB7 ${selC + 1}/${sessionsFiltered.length}${start2 ? ` \xB7 \u25B2${start2}` : ""}${start2 + listRows2 < sessionsFiltered.length ? ` \xB7 \u25BC${sessionsFiltered.length - start2 - listRows2}` : ""}` }),
|
|
9178
|
+
/* @__PURE__ */ jsx6(
|
|
9179
|
+
Table,
|
|
9180
|
+
{
|
|
9181
|
+
columns: [
|
|
9182
|
+
{ header: "", width: 2 },
|
|
9183
|
+
{ header: "STATUS", width: 8 },
|
|
9184
|
+
{ header: "AGENT", width: 18 },
|
|
9185
|
+
{ header: "SESSION", width: 10 },
|
|
9186
|
+
{ header: "CALLS", width: 6 },
|
|
9187
|
+
{ header: "DENY", width: 5 },
|
|
9188
|
+
{ header: "DLP", width: 4 },
|
|
9189
|
+
{ header: "TRUST", width: 5 },
|
|
9190
|
+
{ header: "LAST", width: 6 }
|
|
9191
|
+
],
|
|
9192
|
+
rows: win.map((r, i) => {
|
|
9193
|
+
const st = STATUS_DOT[r.status];
|
|
9194
|
+
const isSel = start2 + i === selC && focused;
|
|
9195
|
+
return [
|
|
9196
|
+
{ value: isSel ? "\u25B8" : "", color: theme.accentBright },
|
|
9197
|
+
{ value: `${st.ch} ${r.status}`, color: st.color },
|
|
9198
|
+
{ value: truncate2(r.agent, 18), color: theme.accent, bold: isSel },
|
|
9199
|
+
{ value: r.id.slice(0, 8), dim: true },
|
|
9200
|
+
{ value: String(r.calls) },
|
|
9201
|
+
{ value: String(r.denies), color: r.denies ? theme.bad : void 0, dim: !r.denies },
|
|
9202
|
+
{ value: String(r.dlp), color: r.dlp ? theme.bad : void 0, dim: !r.dlp },
|
|
9203
|
+
{ value: r.trust != null ? String(r.trust) : "\u2014", dim: true },
|
|
9204
|
+
{ value: ago(r.lastAt), dim: true }
|
|
9205
|
+
];
|
|
9206
|
+
})
|
|
9207
|
+
}
|
|
9208
|
+
),
|
|
9209
|
+
sessionsFiltered.length === 0 ? /* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
9210
|
+
"(no sessions",
|
|
9211
|
+
source === "local" ? " in the local file" : "",
|
|
9212
|
+
")"
|
|
9213
|
+
] }) : null
|
|
9214
|
+
] }) });
|
|
9215
|
+
}
|
|
9216
|
+
const headerRows = 6 + (editing && editing !== "sess-search" ? 1 : 0);
|
|
9132
9217
|
const listRows = Math.max(4, rows - headerRows);
|
|
9133
|
-
const selClamped = Math.min(sel, Math.max(0,
|
|
9134
|
-
const maxStart = Math.max(0,
|
|
9218
|
+
const selClamped = Math.min(sel, Math.max(0, pageRows.length - 1));
|
|
9219
|
+
const maxStart = Math.max(0, pageRows.length - listRows);
|
|
9135
9220
|
const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
|
|
9136
|
-
const windowed =
|
|
9221
|
+
const windowed = pageRows.slice(start, start + listRows);
|
|
9137
9222
|
const reasonW = Math.min(60, Math.max(12, cols - 67));
|
|
9138
|
-
|
|
9139
|
-
|
|
9223
|
+
const loading = source === "cloud" ? cloudQ.loading && !cloudQ.data : localQ.loading && !localQ.data;
|
|
9224
|
+
return /* @__PURE__ */ jsx6(DataView, { loading, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
9225
|
+
strip,
|
|
9226
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
9227
|
+
srcChip,
|
|
9228
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.accentBright, bold: true, children: "LOGS" }),
|
|
9229
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \xB7 sessions (v) " }),
|
|
9140
9230
|
chip("dec", DECISIONS[di] ?? "all", di !== 0),
|
|
9141
9231
|
chip("sig", SIGNALS[gi] ?? "all", gi !== 0),
|
|
9142
9232
|
chip("tool", tool || "\xB7", !!tool),
|
|
9143
9233
|
chip("agent", agent || "\xB7", !!agent),
|
|
9144
|
-
chip("search", search || "\xB7", !!search)
|
|
9234
|
+
chip("search", search || "\xB7", !!search),
|
|
9235
|
+
sessFilter ? chip("sess", sessFilter.slice(0, 8), true) : null
|
|
9145
9236
|
] }),
|
|
9146
|
-
/* @__PURE__ */
|
|
9147
|
-
editing ? /* @__PURE__ */
|
|
9148
|
-
/* @__PURE__ */
|
|
9237
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 [ ] page \xB7 f dec \xB7 g sig \xB7 t tool \xB7 n agent \xB7 / search \xB7 s source \xB7 v sessions \xB7 c clear" : "press \u2192 to browse" }),
|
|
9238
|
+
editing && editing !== "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
9239
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.warn, children: [
|
|
9149
9240
|
editing,
|
|
9150
9241
|
": "
|
|
9151
9242
|
] }),
|
|
9152
|
-
/* @__PURE__ */
|
|
9243
|
+
/* @__PURE__ */ jsx6(
|
|
9153
9244
|
TextInput4,
|
|
9154
9245
|
{
|
|
9155
9246
|
value: editing === "tool" ? tool : editing === "agent" ? agent : search,
|
|
9156
9247
|
onChange: (v) => {
|
|
9157
9248
|
(editing === "tool" ? setTool : editing === "agent" ? setAgent : setSearch)(v);
|
|
9158
|
-
|
|
9249
|
+
setPage(0);
|
|
9250
|
+
toTop();
|
|
9159
9251
|
},
|
|
9160
9252
|
onSubmit: () => setEditing(null)
|
|
9161
9253
|
}
|
|
9162
9254
|
)
|
|
9163
9255
|
] }) : null,
|
|
9164
|
-
/* @__PURE__ */
|
|
9165
|
-
/* @__PURE__ */
|
|
9256
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: `${total} matched \xB7 page ${Math.min(page + 1, pages)}/${pages} \xB7 ${PAGE}/page \xB7 ${pageRows.length ? selClamped + 1 : 0}/${pageRows.length}${start ? ` \xB7 \u25B2${start} newer` : ""}${start + listRows < pageRows.length ? ` \xB7 \u25BC${pageRows.length - start - listRows} older` : ""}` }),
|
|
9257
|
+
/* @__PURE__ */ jsx6(
|
|
9166
9258
|
Table,
|
|
9167
9259
|
{
|
|
9168
9260
|
columns: [
|
|
@@ -9176,51 +9268,81 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9176
9268
|
],
|
|
9177
9269
|
rows: windowed.map((e, i) => rowFor(e, start + i === selClamped && focused, reasonW))
|
|
9178
9270
|
}
|
|
9179
|
-
)
|
|
9271
|
+
),
|
|
9272
|
+
pageRows.length === 0 ? /* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
9273
|
+
"(no entries",
|
|
9274
|
+
source === "local" ? " in the local file" : "",
|
|
9275
|
+
" \u2014 c clears filters)"
|
|
9276
|
+
] }) : null
|
|
9180
9277
|
] }) });
|
|
9181
9278
|
}
|
|
9182
9279
|
function rowFor(e, active2, reasonW) {
|
|
9183
9280
|
return [
|
|
9184
9281
|
{ value: active2 ? "\u25B8" : "", color: theme.accentBright },
|
|
9185
9282
|
{ value: e.decision, color: decisionColor(e.decision) },
|
|
9186
|
-
{ value: truncate2(e.
|
|
9187
|
-
{ value: truncate2(e.
|
|
9188
|
-
{ value: truncate2(e.reason ?? "\u2014", reasonW) },
|
|
9189
|
-
{ value: e.
|
|
9190
|
-
{ value: ago(e.
|
|
9283
|
+
{ value: truncate2(e.tool, 18), color: theme.accent },
|
|
9284
|
+
{ value: truncate2(e.agent ?? "\u2014", 14), dim: true },
|
|
9285
|
+
{ value: truncate2(e.reason ?? e.args ?? "\u2014", reasonW) },
|
|
9286
|
+
{ value: e.dlp.length ? String(e.dlp.length) : "0", color: e.dlp.length ? theme.bad : void 0, dim: !e.dlp.length },
|
|
9287
|
+
{ value: ago(e.at), dim: true }
|
|
9191
9288
|
];
|
|
9192
9289
|
}
|
|
9193
9290
|
function Detail({ label, value, color }) {
|
|
9194
|
-
return /* @__PURE__ */
|
|
9195
|
-
/* @__PURE__ */
|
|
9196
|
-
/* @__PURE__ */
|
|
9291
|
+
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
9292
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: label.padEnd(9) }),
|
|
9293
|
+
/* @__PURE__ */ jsx6(Text6, { color, wrap: "truncate", children: value })
|
|
9197
9294
|
] });
|
|
9198
9295
|
}
|
|
9199
|
-
var DECISIONS, SIGNALS,
|
|
9296
|
+
var DECISIONS, SIGNALS, SESS_STATUS, PAGE, LOCAL_MAX_BYTES, cloudRow, sessStatus2, STATUS_DOT;
|
|
9200
9297
|
var init_Audit = __esm({
|
|
9201
9298
|
"src/tui/panels/Audit.tsx"() {
|
|
9202
9299
|
"use strict";
|
|
9203
9300
|
init_api_client();
|
|
9204
9301
|
init_components();
|
|
9205
9302
|
init_hooks();
|
|
9303
|
+
init_local_log();
|
|
9206
9304
|
init_theme();
|
|
9207
9305
|
DECISIONS = [void 0, "DENY", "ALLOW"];
|
|
9208
9306
|
SIGNALS = [void 0, "dlp", "ratelimit"];
|
|
9209
|
-
|
|
9307
|
+
SESS_STATUS = [void 0, "active", "idle", "ended"];
|
|
9308
|
+
PAGE = 500;
|
|
9309
|
+
LOCAL_MAX_BYTES = 16 * 1024 * 1024;
|
|
9310
|
+
cloudRow = (e) => ({
|
|
9311
|
+
id: e.id,
|
|
9312
|
+
at: Date.parse(e.created_at),
|
|
9313
|
+
tool: e.tool_name,
|
|
9314
|
+
decision: e.decision,
|
|
9315
|
+
permission: e.permission,
|
|
9316
|
+
trust: e.trust_level,
|
|
9317
|
+
agent: e.agent_name,
|
|
9318
|
+
session: e.session_id,
|
|
9319
|
+
reason: e.reason,
|
|
9320
|
+
rule: e.matched_rule_id,
|
|
9321
|
+
evalMs: e.evaluation_time_ms,
|
|
9322
|
+
dlp: e.dlp_matches ?? [],
|
|
9323
|
+
burst: !!e.rate_limit_burst,
|
|
9324
|
+
args: e.arguments_summary ? JSON.stringify(e.arguments_summary) : null
|
|
9325
|
+
});
|
|
9326
|
+
sessStatus2 = (lastAt) => Date.now() - lastAt < 6e4 ? "active" : Date.now() - lastAt < 3e5 ? "idle" : "ended";
|
|
9327
|
+
STATUS_DOT = {
|
|
9328
|
+
active: { ch: "\u25CF", color: theme.ok },
|
|
9329
|
+
idle: { ch: "\u25D0", color: theme.warn },
|
|
9330
|
+
ended: { ch: "\u25CB", color: theme.dim }
|
|
9331
|
+
};
|
|
9210
9332
|
}
|
|
9211
9333
|
});
|
|
9212
9334
|
|
|
9213
9335
|
// src/tui/panels/Agents.tsx
|
|
9214
|
-
import { Box as
|
|
9336
|
+
import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
|
|
9215
9337
|
import { useState as useState7 } from "react";
|
|
9216
|
-
import { Fragment as Fragment5, jsx as
|
|
9338
|
+
import { Fragment as Fragment5, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
9217
9339
|
function Bar({ label, value, max = 100, width: width2 = 16, color = theme.accent }) {
|
|
9218
9340
|
const filled = max > 0 ? Math.round(Math.min(value, max) / max * width2) : 0;
|
|
9219
|
-
return /* @__PURE__ */
|
|
9220
|
-
/* @__PURE__ */
|
|
9221
|
-
/* @__PURE__ */
|
|
9222
|
-
/* @__PURE__ */
|
|
9223
|
-
/* @__PURE__ */
|
|
9341
|
+
return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
9342
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: label.padEnd(9) }),
|
|
9343
|
+
/* @__PURE__ */ jsx7(Text7, { color, children: "\u2588".repeat(filled) }),
|
|
9344
|
+
/* @__PURE__ */ jsx7(Text7, { color: "#233457", children: "\u2591".repeat(Math.max(0, width2 - filled)) }),
|
|
9345
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " " + value })
|
|
9224
9346
|
] });
|
|
9225
9347
|
}
|
|
9226
9348
|
function AgentsPanel({ focused }) {
|
|
@@ -9245,50 +9367,50 @@ function AgentsPanel({ focused }) {
|
|
|
9245
9367
|
const tools = tmap?.tools ?? [];
|
|
9246
9368
|
const resources = tmap?.resources ?? [];
|
|
9247
9369
|
const listW = 30;
|
|
9248
|
-
return /* @__PURE__ */
|
|
9249
|
-
/* @__PURE__ */
|
|
9250
|
-
/* @__PURE__ */
|
|
9251
|
-
agents.slice(0, 16).map((a, i) => /* @__PURE__ */
|
|
9252
|
-
/* @__PURE__ */
|
|
9370
|
+
return /* @__PURE__ */ jsx7(DataView, { loading: live2.loading && !live2.data, error: live2.error, empty: !!live2.data && agents.length === 0, emptyText: "No identified agents yet.", children: /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
9371
|
+
/* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", width: listW, marginRight: 2, overflow: "hidden", children: [
|
|
9372
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: focused ? "\u2191\u2193 select agent" : "press \u2192 to browse" }),
|
|
9373
|
+
agents.slice(0, 16).map((a, i) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", backgroundColor: focused && i === sel ? "#1c2f63" : void 0, bold: i === sel, children: [
|
|
9374
|
+
/* @__PURE__ */ jsxs7(Text7, { color: statusColor(a.status), children: [
|
|
9253
9375
|
a.status === "active" ? "\u25CF" : a.status === "idle" ? "\u25D0" : "\u25CB",
|
|
9254
9376
|
" "
|
|
9255
9377
|
] }),
|
|
9256
|
-
/* @__PURE__ */
|
|
9257
|
-
/* @__PURE__ */
|
|
9378
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(a.agent_name ?? a.agent_id ?? "?", 16).padEnd(17) }),
|
|
9379
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: `t${a.trust_score}` })
|
|
9258
9380
|
] }, a.session_id))
|
|
9259
9381
|
] }),
|
|
9260
|
-
/* @__PURE__ */
|
|
9261
|
-
/* @__PURE__ */
|
|
9262
|
-
/* @__PURE__ */
|
|
9263
|
-
/* @__PURE__ */
|
|
9382
|
+
/* @__PURE__ */ jsx7(Box7, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: !selected ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "select an agent" }) : detail.loading && !d ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "loading profile\u2026" }) : !d ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "no profile (agent has no calls yet)" }) : /* @__PURE__ */ jsxs7(Fragment5, { children: [
|
|
9383
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
9384
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, color: theme.accentBright, children: truncate2(selected.agent_name ?? selected.agent_id ?? "?", 22) }),
|
|
9385
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: ` trust ${base?.trustScore ?? "\u2014"}/100 \xB7 ${base?.character ?? ""}` })
|
|
9264
9386
|
] }),
|
|
9265
|
-
base?.characterBlurb ? /* @__PURE__ */
|
|
9266
|
-
radar ? /* @__PURE__ */
|
|
9267
|
-
/* @__PURE__ */
|
|
9268
|
-
/* @__PURE__ */
|
|
9269
|
-
/* @__PURE__ */
|
|
9270
|
-
/* @__PURE__ */
|
|
9271
|
-
/* @__PURE__ */
|
|
9272
|
-
/* @__PURE__ */
|
|
9387
|
+
base?.characterBlurb ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: truncate2(String(base.characterBlurb), 70) }) : null,
|
|
9388
|
+
radar ? /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
9389
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "Behaviour radar (0\u2013100)" }),
|
|
9390
|
+
/* @__PURE__ */ jsx7(Bar, { label: "read", value: Math.round((radar.read ?? 0) * 100) }),
|
|
9391
|
+
/* @__PURE__ */ jsx7(Bar, { label: "write", value: Math.round((radar.write ?? 0) * 100), color: theme.warn }),
|
|
9392
|
+
/* @__PURE__ */ jsx7(Bar, { label: "execute", value: Math.round((radar.execute ?? 0) * 100), color: theme.warn }),
|
|
9393
|
+
/* @__PURE__ */ jsx7(Bar, { label: "network", value: Math.round((radar.network ?? 0) * 100) }),
|
|
9394
|
+
/* @__PURE__ */ jsx7(Bar, { label: "complian.", value: Math.round((radar.compliance ?? 0) * 100), color: theme.ok })
|
|
9273
9395
|
] }) : null,
|
|
9274
|
-
/* @__PURE__ */
|
|
9275
|
-
/* @__PURE__ */
|
|
9396
|
+
/* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
9397
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
9276
9398
|
"Anomalies ",
|
|
9277
9399
|
anomalies2.length ? `(${anomalies2.length})` : ""
|
|
9278
9400
|
] }),
|
|
9279
|
-
anomalies2.length === 0 ? /* @__PURE__ */
|
|
9280
|
-
/* @__PURE__ */
|
|
9281
|
-
/* @__PURE__ */
|
|
9282
|
-
/* @__PURE__ */
|
|
9401
|
+
anomalies2.length === 0 ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " none" }) : anomalies2.slice(0, 4).map((an, i) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
9402
|
+
/* @__PURE__ */ jsx7(Text7, { color: sevColor(String(an.severity)), children: ` ${String(an.severity).toUpperCase().slice(0, 4).padEnd(5)}` }),
|
|
9403
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: String(an.kind).padEnd(12) }),
|
|
9404
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: truncate2(String(an.description ?? ""), 44) })
|
|
9283
9405
|
] }, i))
|
|
9284
9406
|
] }),
|
|
9285
|
-
/* @__PURE__ */
|
|
9286
|
-
/* @__PURE__ */
|
|
9287
|
-
tools.slice(0, 4).map((t, i) => /* @__PURE__ */
|
|
9288
|
-
/* @__PURE__ */
|
|
9289
|
-
/* @__PURE__ */
|
|
9407
|
+
/* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
9408
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "Trust map \xB7 top tools" }),
|
|
9409
|
+
tools.slice(0, 4).map((t, i) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
9410
|
+
/* @__PURE__ */ jsx7(Text7, { color: t.anomalous ? theme.bad : theme.accent, children: ` ${truncate2(String(t.name), 16).padEnd(17)}` }),
|
|
9411
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: `${t.count}\xD7 ${t.permission ?? ""}` })
|
|
9290
9412
|
] }, i)),
|
|
9291
|
-
resources.slice(0, 3).map((r, i) => /* @__PURE__ */
|
|
9413
|
+
resources.slice(0, 3).map((r, i) => /* @__PURE__ */ jsx7(Text7, { wrap: "truncate", color: r.anomalous ? theme.bad : theme.dim, children: ` ${r.type === "domain" ? "\u{1F310}" : "\u{1F4C1}"} ${truncate2(String(r.value), 40)} ${r.count}\xD7` }, "r" + i))
|
|
9292
9414
|
] })
|
|
9293
9415
|
] }) })
|
|
9294
9416
|
] }) });
|
|
@@ -9307,10 +9429,10 @@ var init_Agents = __esm({
|
|
|
9307
9429
|
});
|
|
9308
9430
|
|
|
9309
9431
|
// src/tui/panels/Settings.tsx
|
|
9310
|
-
import { Box as
|
|
9432
|
+
import { Box as Box8, Text as Text8, useInput as useInput7 } from "ink";
|
|
9311
9433
|
import TextInput5 from "ink-text-input";
|
|
9312
9434
|
import { useState as useState8 } from "react";
|
|
9313
|
-
import { jsx as
|
|
9435
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
9314
9436
|
function alertBodyFor(channel) {
|
|
9315
9437
|
const c2 = channel.trim();
|
|
9316
9438
|
const base = { name: "dataroom alert", threshold: 5, windowSeconds: 60, signal: "any" };
|
|
@@ -9435,7 +9557,7 @@ function SettingsPanel({ active: active2, focused }) {
|
|
|
9435
9557
|
const loading = localQ.loading && !localQ.data || whQ.loading && !whQ.data || alertQ.loading && !alertQ.data;
|
|
9436
9558
|
const error = localQ.error ?? whQ.error ?? alertQ.error;
|
|
9437
9559
|
const mark = (r) => keyOf(r) === keyOf(cur) && focused ? "\u25B8 " : " ";
|
|
9438
|
-
const onOff = (on) => on ? /* @__PURE__ */
|
|
9560
|
+
const onOff = (on) => on ? /* @__PURE__ */ jsx8(Text8, { color: theme.ok, children: "on " }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "off" });
|
|
9439
9561
|
const chanOf = (rule) => [...(rule.slackUrls ?? []).map((u) => "slack " + u), ...rule.emails ?? [], ...(rule.telegram ?? []).map((t) => "tg " + t)].join(", ") || "\u2014";
|
|
9440
9562
|
const listBudget = Math.max(4, rows - 8);
|
|
9441
9563
|
const start = Math.min(Math.max(0, selClamped - Math.floor(listBudget / 2)), Math.max(0, rowsAll.length - listBudget));
|
|
@@ -9443,68 +9565,68 @@ function SettingsPanel({ active: active2, focused }) {
|
|
|
9443
9565
|
const i = rowsAll.findIndex((x) => keyOf(x) === keyOf(r));
|
|
9444
9566
|
return i >= start && i < start + listBudget;
|
|
9445
9567
|
};
|
|
9446
|
-
return /* @__PURE__ */
|
|
9447
|
-
/* @__PURE__ */
|
|
9448
|
-
editing ? /* @__PURE__ */
|
|
9449
|
-
/* @__PURE__ */
|
|
9450
|
-
/* @__PURE__ */
|
|
9451
|
-
] }) : msg ? /* @__PURE__ */
|
|
9452
|
-
inWindow(rowsAll[0]) || inWindow(rowsAll[1]) ? /* @__PURE__ */
|
|
9453
|
-
/* @__PURE__ */
|
|
9568
|
+
return /* @__PURE__ */ jsx8(DataView, { loading, error, children: /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
9569
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: focused ? "\u2191\u2193 move \xB7 enter/space toggle-edit-add \xB7 e events \xB7 d delete \xB7 r refresh" : "press \u2192 to configure" }),
|
|
9570
|
+
editing ? /* @__PURE__ */ jsxs8(Box8, { children: [
|
|
9571
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: editing === "path" ? "local log path: " : editing === "wh-url" ? "webhook url: " : "channel (slack url / email / telegram id): " }),
|
|
9572
|
+
/* @__PURE__ */ jsx8(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
|
|
9573
|
+
] }) : msg ? /* @__PURE__ */ jsx8(Text8, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : /* @__PURE__ */ jsx8(Text8, { children: " " }),
|
|
9574
|
+
inWindow(rowsAll[0]) || inWindow(rowsAll[1]) ? /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
|
|
9575
|
+
/* @__PURE__ */ jsxs8(Text8, { bold: true, color: theme.accentBright, children: [
|
|
9454
9576
|
"LOCAL LOGS ",
|
|
9455
|
-
/* @__PURE__ */
|
|
9577
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "\u2014 hooks mirror every decision to a file on the agent machine" })
|
|
9456
9578
|
] }),
|
|
9457
|
-
/* @__PURE__ */
|
|
9458
|
-
/* @__PURE__ */
|
|
9459
|
-
/* @__PURE__ */
|
|
9579
|
+
/* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
9580
|
+
/* @__PURE__ */ jsx8(Text8, { color: keyOf(cur) === "ll-enabled" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[0]) }),
|
|
9581
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "enabled ".padEnd(10) }),
|
|
9460
9582
|
onOff(!!local?.enabled),
|
|
9461
|
-
/* @__PURE__ */
|
|
9583
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " enter toggles" })
|
|
9462
9584
|
] }),
|
|
9463
|
-
/* @__PURE__ */
|
|
9464
|
-
/* @__PURE__ */
|
|
9465
|
-
/* @__PURE__ */
|
|
9466
|
-
local?.path ? /* @__PURE__ */
|
|
9585
|
+
/* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
9586
|
+
/* @__PURE__ */ jsx8(Text8, { color: keyOf(cur) === "ll-path" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[1]) }),
|
|
9587
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "path ".padEnd(10) }),
|
|
9588
|
+
local?.path ? /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate2(local.path, cols - 14) }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "not set \u2014 enter to edit" })
|
|
9467
9589
|
] })
|
|
9468
9590
|
] }) : null,
|
|
9469
|
-
/* @__PURE__ */
|
|
9470
|
-
/* @__PURE__ */
|
|
9591
|
+
/* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
|
|
9592
|
+
/* @__PURE__ */ jsxs8(Text8, { bold: true, color: theme.accentBright, children: [
|
|
9471
9593
|
"WEBHOOKS ",
|
|
9472
|
-
/* @__PURE__ */
|
|
9594
|
+
/* @__PURE__ */ jsxs8(Text8, { color: theme.dim, children: [
|
|
9473
9595
|
"\u2014 POST every matching event to your endpoint (",
|
|
9474
9596
|
webhooks.length,
|
|
9475
9597
|
")"
|
|
9476
9598
|
] })
|
|
9477
9599
|
] }),
|
|
9478
|
-
webhooks.filter((wh) => inWindow({ kind: "wh", wh })).map((wh) => /* @__PURE__ */
|
|
9479
|
-
/* @__PURE__ */
|
|
9600
|
+
webhooks.filter((wh) => inWindow({ kind: "wh", wh })).map((wh) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
9601
|
+
/* @__PURE__ */ jsx8(Text8, { color: keyOf(cur) === "wh:" + wh.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh", wh }) }),
|
|
9480
9602
|
onOff(wh.enabled),
|
|
9481
|
-
/* @__PURE__ */
|
|
9482
|
-
/* @__PURE__ */
|
|
9603
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: (" " + wh.events).padEnd(9) }),
|
|
9604
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate2(wh.url, cols - 16) })
|
|
9483
9605
|
] }, wh.id)),
|
|
9484
|
-
inWindow(rowsAll.find((r) => r.kind === "wh-add")) ? /* @__PURE__ */
|
|
9485
|
-
/* @__PURE__ */
|
|
9486
|
-
/* @__PURE__ */
|
|
9606
|
+
inWindow(rowsAll.find((r) => r.kind === "wh-add")) ? /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
9607
|
+
/* @__PURE__ */ jsx8(Text8, { color: cur.kind === "wh-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh-add" }) }),
|
|
9608
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add webhook (enter)" })
|
|
9487
9609
|
] }) : null
|
|
9488
9610
|
] }),
|
|
9489
|
-
/* @__PURE__ */
|
|
9490
|
-
/* @__PURE__ */
|
|
9611
|
+
/* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
|
|
9612
|
+
/* @__PURE__ */ jsxs8(Text8, { bold: true, color: theme.accentBright, children: [
|
|
9491
9613
|
"ALERTS ",
|
|
9492
|
-
/* @__PURE__ */
|
|
9614
|
+
/* @__PURE__ */ jsxs8(Text8, { color: theme.dim, children: [
|
|
9493
9615
|
"\u2014 notify on a signal spike (",
|
|
9494
9616
|
alerts.length,
|
|
9495
9617
|
")"
|
|
9496
9618
|
] })
|
|
9497
9619
|
] }),
|
|
9498
|
-
alerts.filter((rule) => inWindow({ kind: "alert", rule })).map((rule) => /* @__PURE__ */
|
|
9499
|
-
/* @__PURE__ */
|
|
9620
|
+
alerts.filter((rule) => inWindow({ kind: "alert", rule })).map((rule) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
9621
|
+
/* @__PURE__ */ jsx8(Text8, { color: keyOf(cur) === "alert:" + rule.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert", rule }) }),
|
|
9500
9622
|
onOff(rule.enabled),
|
|
9501
|
-
/* @__PURE__ */
|
|
9502
|
-
/* @__PURE__ */
|
|
9503
|
-
/* @__PURE__ */
|
|
9623
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: (" " + rule.signal).padEnd(11) }),
|
|
9624
|
+
/* @__PURE__ */ jsx8(Text8, { children: `\u2265${rule.threshold}/${rule.windowSeconds}s ` }),
|
|
9625
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate2(chanOf(rule), cols - 26) })
|
|
9504
9626
|
] }, rule.id)),
|
|
9505
|
-
inWindow(rowsAll[rowsAll.length - 1]) ? /* @__PURE__ */
|
|
9506
|
-
/* @__PURE__ */
|
|
9507
|
-
/* @__PURE__ */
|
|
9627
|
+
inWindow(rowsAll[rowsAll.length - 1]) ? /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
9628
|
+
/* @__PURE__ */ jsx8(Text8, { color: cur.kind === "alert-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert-add" }) }),
|
|
9629
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add alert (enter \u2014 one channel: slack url, email or telegram chat id)" })
|
|
9508
9630
|
] }) : null
|
|
9509
9631
|
] })
|
|
9510
9632
|
] }) });
|
|
@@ -9522,17 +9644,17 @@ var init_Settings = __esm({
|
|
|
9522
9644
|
});
|
|
9523
9645
|
|
|
9524
9646
|
// src/tui/App.tsx
|
|
9525
|
-
import { Box as
|
|
9647
|
+
import { Box as Box9, Text as Text9, useApp, useInput as useInput8 } from "ink";
|
|
9526
9648
|
import { useState as useState9 } from "react";
|
|
9527
|
-
import { jsx as
|
|
9649
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
9528
9650
|
function LiveHint() {
|
|
9529
|
-
return /* @__PURE__ */
|
|
9530
|
-
/* @__PURE__ */
|
|
9531
|
-
/* @__PURE__ */
|
|
9532
|
-
/* @__PURE__ */
|
|
9533
|
-
/* @__PURE__ */
|
|
9651
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
9652
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
|
|
9653
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
|
|
9654
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
|
|
9655
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
9534
9656
|
"press ",
|
|
9535
|
-
/* @__PURE__ */
|
|
9657
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: "enter" }),
|
|
9536
9658
|
" to go live"
|
|
9537
9659
|
] }) })
|
|
9538
9660
|
] });
|
|
@@ -9540,14 +9662,14 @@ function LiveHint() {
|
|
|
9540
9662
|
function Banner({ cols }) {
|
|
9541
9663
|
const wide = cols >= 82;
|
|
9542
9664
|
if (!wide) {
|
|
9543
|
-
return /* @__PURE__ */
|
|
9544
|
-
/* @__PURE__ */
|
|
9545
|
-
/* @__PURE__ */
|
|
9665
|
+
return /* @__PURE__ */ jsxs9(Box9, { children: [
|
|
9666
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate" }),
|
|
9667
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2014 security control center" })
|
|
9546
9668
|
] });
|
|
9547
9669
|
}
|
|
9548
|
-
return /* @__PURE__ */
|
|
9549
|
-
BANNER_FULL.map((line, i) => /* @__PURE__ */
|
|
9550
|
-
/* @__PURE__ */
|
|
9670
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
9671
|
+
BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx9(Text9, { bold: true, color: BANNER_HEX[i], children: line }, i)),
|
|
9672
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
|
|
9551
9673
|
] });
|
|
9552
9674
|
}
|
|
9553
9675
|
function App() {
|
|
@@ -9576,31 +9698,31 @@ function App() {
|
|
|
9576
9698
|
});
|
|
9577
9699
|
const { cols, rows } = useTermSize();
|
|
9578
9700
|
const current = SECTIONS[section];
|
|
9579
|
-
if (help) return /* @__PURE__ */
|
|
9701
|
+
if (help) return /* @__PURE__ */ jsx9(HelpOverlay, { cols, rows });
|
|
9580
9702
|
if (current.label === "Live" && focus === "panel") {
|
|
9581
|
-
return /* @__PURE__ */
|
|
9703
|
+
return /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx9(LivePanel, { active: true, focused: true }) });
|
|
9582
9704
|
}
|
|
9583
9705
|
const Panel = current.Panel;
|
|
9584
|
-
return /* @__PURE__ */
|
|
9585
|
-
/* @__PURE__ */
|
|
9586
|
-
/* @__PURE__ */
|
|
9587
|
-
/* @__PURE__ */
|
|
9588
|
-
/* @__PURE__ */
|
|
9706
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
|
|
9707
|
+
/* @__PURE__ */ jsx9(Banner, { cols }),
|
|
9708
|
+
/* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexGrow: 1, children: [
|
|
9709
|
+
/* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => /* @__PURE__ */ jsx9(Text9, { color: i === section ? theme.accentBright : void 0, bold: i === section, children: (i === section ? "\u25B8 " : " ") + s.label }, s.label)) }),
|
|
9710
|
+
/* @__PURE__ */ jsx9(Box9, { flexGrow: 1, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, children: /* @__PURE__ */ jsx9(Panel, { active: focus === "panel" || current.label !== "Live", focused: focus === "panel" }) })
|
|
9589
9711
|
] }),
|
|
9590
|
-
/* @__PURE__ */
|
|
9712
|
+
/* @__PURE__ */ jsx9(Box9, { children: focus === "nav" ? /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ["?", "help"], ["q", "quit"]] }) : /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
|
|
9591
9713
|
] });
|
|
9592
9714
|
}
|
|
9593
9715
|
function HelpOverlay({ cols, rows }) {
|
|
9594
|
-
return /* @__PURE__ */
|
|
9595
|
-
/* @__PURE__ */
|
|
9596
|
-
/* @__PURE__ */
|
|
9597
|
-
/* @__PURE__ */
|
|
9598
|
-
keys.map(([k, desc]) => /* @__PURE__ */
|
|
9599
|
-
/* @__PURE__ */
|
|
9600
|
-
/* @__PURE__ */
|
|
9716
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
|
|
9717
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
|
|
9718
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, children: [
|
|
9719
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accent, children: group }),
|
|
9720
|
+
keys.map(([k, desc]) => /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
9721
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
|
|
9722
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: desc })
|
|
9601
9723
|
] }, k))
|
|
9602
9724
|
] }, group)) }),
|
|
9603
|
-
/* @__PURE__ */
|
|
9725
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "press any key to close" })
|
|
9604
9726
|
] });
|
|
9605
9727
|
}
|
|
9606
9728
|
var SECTIONS, BANNER_HEX, HELP;
|
|
@@ -9614,7 +9736,6 @@ var init_App = __esm({
|
|
|
9614
9736
|
init_Policies();
|
|
9615
9737
|
init_RateLimit();
|
|
9616
9738
|
init_Dlp();
|
|
9617
|
-
init_Stats();
|
|
9618
9739
|
init_Audit();
|
|
9619
9740
|
init_Agents();
|
|
9620
9741
|
init_Settings();
|
|
@@ -9625,7 +9746,6 @@ var init_App = __esm({
|
|
|
9625
9746
|
{ label: "Policies", Panel: PoliciesPanel },
|
|
9626
9747
|
{ label: "Rate Limit", Panel: RateLimitPanel },
|
|
9627
9748
|
{ label: "DLP", Panel: DlpPanel },
|
|
9628
|
-
{ label: "Stats", Panel: StatsPanel },
|
|
9629
9749
|
{ label: "Audit", Panel: AuditPanel },
|
|
9630
9750
|
{ label: "Settings", Panel: SettingsPanel }
|
|
9631
9751
|
];
|
|
@@ -9633,9 +9753,9 @@ var init_App = __esm({
|
|
|
9633
9753
|
HELP = [
|
|
9634
9754
|
["Global", [["\u2191\u2193", "move between sections"], ["\u2192 / enter", "open a section"], ["\u2190 / esc", "back to the menu"], ["?", "this help"], ["q", "quit"]]],
|
|
9635
9755
|
["Live", [["\u2191\u2193", "select a stream row"], ["enter", "full entry content"], ["w", "whitelist the selected DENY"], ["b", "block the selected ALLOW"], ["d / x / r", "filter denies / dlp / rate-limit"], ["f", "local / cloud filter"], ["/", "search"], ["s", "sessions (\u2191\u2193 pick, enter open)"], ["space", "copy mode (freeze)"]]],
|
|
9636
|
-
["Policies", [["\u2191\u2193", "browse / select"], ["a", "activate (pin) selected policy"], ["x", "deactivate \u2014 no active policy"], ["enter", "open rules \u2192 open a rule"], ["space", "toggle a rule on/off"], ["e", "flip effect"], ["n", "new rule"], ["d", "delete rule"], ["m", "flip mode"], ["D", "dry-run the draft"], ["
|
|
9756
|
+
["Policies", [["\u2191\u2193", "browse / select"], ["a", "activate (pin) selected policy"], ["x", "deactivate \u2014 no active policy"], ["enter", "open rules \u2192 open a rule"], ["space", "toggle a rule on/off"], ["e", "flip effect"], ["n", "new rule"], ["d", "delete rule"], ["m", "flip mode"], ["D", "dry-run the draft"], ["s", "save"], ["x", "discard"]]],
|
|
9637
9757
|
["Rate limit / DLP", [["\u2191\u2193", "field / pattern"], ["\u2190\u2192", "adjust (shift = \xB110)"], ["space", "toggle pattern"], ["m", "mode"], ["a / d", "add / remove custom"], ["g", "ghost mode"], ["P", "self-protection"], ["s", "save"]]],
|
|
9638
|
-
["Audit", [["\u2191\u2193", "select (list scrolls)"], ["enter", "full entry
|
|
9758
|
+
["Audit", [["v", "logs \u2194 sessions"], ["s", "source: cloud \u2194 local"], ["[ / ]", "prev / next page (500 each)"], ["\u2191\u2193", "select (list scrolls)"], ["enter", "full entry / session logs"], ["f / g", "decision / signal filter"], ["t / n / /", "tool / agent / search"], ["c", "clear filters"]]],
|
|
9639
9759
|
["Settings", [["\u2191\u2193", "move"], ["enter / space", "toggle \xB7 edit \xB7 add"], ["e", "webhook events"], ["d d", "delete"], ["r", "refresh"]]]
|
|
9640
9760
|
];
|
|
9641
9761
|
}
|
|
@@ -9647,7 +9767,7 @@ __export(tui_exports, {
|
|
|
9647
9767
|
launchTui: () => launchTui
|
|
9648
9768
|
});
|
|
9649
9769
|
import { render } from "ink";
|
|
9650
|
-
import { jsx as
|
|
9770
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
9651
9771
|
async function launchTui() {
|
|
9652
9772
|
if (!process.stdout.isTTY || !process.stdin.isTTY) {
|
|
9653
9773
|
process.stderr.write(
|
|
@@ -9661,7 +9781,7 @@ async function launchTui() {
|
|
|
9661
9781
|
}
|
|
9662
9782
|
process.stdout.write("\x1B[?1049h\x1B[H");
|
|
9663
9783
|
try {
|
|
9664
|
-
const { waitUntilExit } = render(/* @__PURE__ */
|
|
9784
|
+
const { waitUntilExit } = render(/* @__PURE__ */ jsx10(App, {}));
|
|
9665
9785
|
await waitUntilExit();
|
|
9666
9786
|
} finally {
|
|
9667
9787
|
process.stdout.write("\x1B[?1049l");
|
|
@@ -10364,8 +10484,8 @@ var init_agents2 = __esm({
|
|
|
10364
10484
|
|
|
10365
10485
|
// src/commands/doctor.ts
|
|
10366
10486
|
import { existsSync as existsSync4, statSync as statSync2 } from "fs";
|
|
10367
|
-
import { homedir as
|
|
10368
|
-
import { join as
|
|
10487
|
+
import { homedir as homedir6 } from "os";
|
|
10488
|
+
import { join as join8 } from "path";
|
|
10369
10489
|
async function run6(argv) {
|
|
10370
10490
|
const { flags } = parse(argv);
|
|
10371
10491
|
const json = flagBool(flags, "json");
|
|
@@ -10425,14 +10545,14 @@ var init_doctor = __esm({
|
|
|
10425
10545
|
init_api_client();
|
|
10426
10546
|
init_format();
|
|
10427
10547
|
init_args();
|
|
10428
|
-
LOCAL_LOG2 =
|
|
10548
|
+
LOCAL_LOG2 = join8(homedir6(), ".solongate", "local-logs", "solongate-audit.jsonl");
|
|
10429
10549
|
}
|
|
10430
10550
|
});
|
|
10431
10551
|
|
|
10432
10552
|
// src/commands/watch.ts
|
|
10433
10553
|
import { closeSync as closeSync2, existsSync as existsSync5, openSync as openSync2, readSync as readSync2, statSync as statSync3 } from "fs";
|
|
10434
|
-
import { homedir as
|
|
10435
|
-
import { join as
|
|
10554
|
+
import { homedir as homedir7 } from "os";
|
|
10555
|
+
import { join as join9 } from "path";
|
|
10436
10556
|
function tailLocal(file, maxBytes = 131072) {
|
|
10437
10557
|
try {
|
|
10438
10558
|
const size = statSync3(file).size;
|
|
@@ -10539,7 +10659,7 @@ var init_watch = __esm({
|
|
|
10539
10659
|
init_cli_utils();
|
|
10540
10660
|
init_args();
|
|
10541
10661
|
init_format();
|
|
10542
|
-
LOCAL_LOG3 =
|
|
10662
|
+
LOCAL_LOG3 = join9(homedir7(), ".solongate", "local-logs", "solongate-audit.jsonl");
|
|
10543
10663
|
trunc = (s, n) => s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
|
|
10544
10664
|
time = (ms) => new Date(ms).toTimeString().slice(0, 8);
|
|
10545
10665
|
}
|
|
@@ -10842,8 +10962,8 @@ __export(global_install_exports, {
|
|
|
10842
10962
|
unlockProtected: () => unlockProtected
|
|
10843
10963
|
});
|
|
10844
10964
|
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
|
|
10845
|
-
import { resolve as resolve4, join as
|
|
10846
|
-
import { homedir as
|
|
10965
|
+
import { resolve as resolve4, join as join10, dirname } from "path";
|
|
10966
|
+
import { homedir as homedir8 } from "os";
|
|
10847
10967
|
import { fileURLToPath } from "url";
|
|
10848
10968
|
import { createInterface } from "readline";
|
|
10849
10969
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
@@ -10906,10 +11026,10 @@ function unlockFile(file) {
|
|
|
10906
11026
|
function protectedTargets() {
|
|
10907
11027
|
const p = globalPaths();
|
|
10908
11028
|
return [
|
|
10909
|
-
|
|
10910
|
-
|
|
10911
|
-
|
|
10912
|
-
|
|
11029
|
+
join10(p.hooksDir, "guard.mjs"),
|
|
11030
|
+
join10(p.hooksDir, "audit.mjs"),
|
|
11031
|
+
join10(p.hooksDir, "stop.mjs"),
|
|
11032
|
+
join10(p.hooksDir, "shield.mjs"),
|
|
10913
11033
|
p.configPath,
|
|
10914
11034
|
p.settingsPath
|
|
10915
11035
|
];
|
|
@@ -10921,25 +11041,25 @@ function unlockProtected() {
|
|
|
10921
11041
|
for (const f of protectedTargets()) unlockFile(f);
|
|
10922
11042
|
}
|
|
10923
11043
|
function globalPaths() {
|
|
10924
|
-
const home =
|
|
10925
|
-
const sgDir =
|
|
10926
|
-
const hooksDir =
|
|
10927
|
-
const claudeDir =
|
|
11044
|
+
const home = homedir8();
|
|
11045
|
+
const sgDir = join10(home, ".solongate");
|
|
11046
|
+
const hooksDir = join10(sgDir, "hooks");
|
|
11047
|
+
const claudeDir = join10(home, ".claude");
|
|
10928
11048
|
return {
|
|
10929
11049
|
home,
|
|
10930
11050
|
sgDir,
|
|
10931
11051
|
hooksDir,
|
|
10932
11052
|
claudeDir,
|
|
10933
|
-
settingsPath:
|
|
10934
|
-
backupPath:
|
|
10935
|
-
configPath:
|
|
11053
|
+
settingsPath: join10(claudeDir, "settings.json"),
|
|
11054
|
+
backupPath: join10(claudeDir, "settings.solongate.bak"),
|
|
11055
|
+
configPath: join10(sgDir, "cloud-guard.json")
|
|
10936
11056
|
};
|
|
10937
11057
|
}
|
|
10938
11058
|
function readHook(filename) {
|
|
10939
|
-
return readFileSync7(
|
|
11059
|
+
return readFileSync7(join10(HOOKS_DIR, filename), "utf-8");
|
|
10940
11060
|
}
|
|
10941
11061
|
function readGuard() {
|
|
10942
|
-
const bundled =
|
|
11062
|
+
const bundled = join10(HOOKS_DIR, "guard.bundled.mjs");
|
|
10943
11063
|
return existsSync6(bundled) ? readFileSync7(bundled, "utf-8") : readHook("guard.mjs");
|
|
10944
11064
|
}
|
|
10945
11065
|
function ask(question) {
|
|
@@ -10994,7 +11114,7 @@ function shimTargets() {
|
|
|
10994
11114
|
return [];
|
|
10995
11115
|
}
|
|
10996
11116
|
}
|
|
10997
|
-
return [".bashrc", ".zshrc", ".profile"].map((f) =>
|
|
11117
|
+
return [".bashrc", ".zshrc", ".profile"].map((f) => join10(homedir8(), f)).filter((f) => existsSync6(f));
|
|
10998
11118
|
}
|
|
10999
11119
|
function writeShimBlock(file, block2) {
|
|
11000
11120
|
const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
|
|
@@ -11059,12 +11179,12 @@ async function runGlobalInstall(opts = {}) {
|
|
|
11059
11179
|
mkdirSync4(p.hooksDir, { recursive: true });
|
|
11060
11180
|
mkdirSync4(p.claudeDir, { recursive: true });
|
|
11061
11181
|
unlockProtected();
|
|
11062
|
-
writeFileSync4(
|
|
11063
|
-
writeFileSync4(
|
|
11064
|
-
writeFileSync4(
|
|
11065
|
-
writeFileSync4(
|
|
11182
|
+
writeFileSync4(join10(p.hooksDir, "guard.mjs"), readGuard());
|
|
11183
|
+
writeFileSync4(join10(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
|
|
11184
|
+
writeFileSync4(join10(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
|
|
11185
|
+
writeFileSync4(join10(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
|
|
11066
11186
|
console.log(` Installed hooks \u2192 ${p.hooksDir}`);
|
|
11067
|
-
installClaudeShim(
|
|
11187
|
+
installClaudeShim(join10(p.hooksDir, "shield.mjs"));
|
|
11068
11188
|
writeFileSync4(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
|
|
11069
11189
|
console.log(` Wrote ${p.configPath}`);
|
|
11070
11190
|
let existing = {};
|
|
@@ -11080,9 +11200,9 @@ async function runGlobalInstall(opts = {}) {
|
|
|
11080
11200
|
existing = {};
|
|
11081
11201
|
}
|
|
11082
11202
|
}
|
|
11083
|
-
const guardAbs =
|
|
11084
|
-
const auditAbs =
|
|
11085
|
-
const stopAbs =
|
|
11203
|
+
const guardAbs = join10(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
|
|
11204
|
+
const auditAbs = join10(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
|
|
11205
|
+
const stopAbs = join10(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
|
|
11086
11206
|
const nodeBin = process.execPath.replace(/\\/g, "/");
|
|
11087
11207
|
const call = process.platform === "win32" ? "& " : "";
|
|
11088
11208
|
const hookCmd = (script) => `${call}"${nodeBin}" "${script}" claude-code "Claude Code"`;
|
|
@@ -11276,9 +11396,9 @@ import { spawn as spawn3 } from "child_process";
|
|
|
11276
11396
|
import { URL as URL2 } from "url";
|
|
11277
11397
|
import { readFileSync as readFileSync8, existsSync as existsSync7, readdirSync, statSync as statSync4 } from "fs";
|
|
11278
11398
|
import { resolve as resolve5 } from "path";
|
|
11279
|
-
import { homedir as
|
|
11399
|
+
import { homedir as homedir9 } from "os";
|
|
11280
11400
|
function findCacheFile() {
|
|
11281
|
-
const dir = resolve5(
|
|
11401
|
+
const dir = resolve5(homedir9(), ".solongate");
|
|
11282
11402
|
const envSel = process.env.SOLONGATE_AGENT_ID;
|
|
11283
11403
|
if (envSel) {
|
|
11284
11404
|
const f = resolve5(dir, ".policy-cache-" + envSel.replace(/[^a-zA-Z0-9_-]/g, "_") + ".json");
|
|
@@ -11593,8 +11713,8 @@ __export(logs_server_exports, {
|
|
|
11593
11713
|
});
|
|
11594
11714
|
import { createServer as createServer2 } from "http";
|
|
11595
11715
|
import { readFileSync as readFileSync9, statSync as statSync5 } from "fs";
|
|
11596
|
-
import { resolve as resolve6, join as
|
|
11597
|
-
import { homedir as
|
|
11716
|
+
import { resolve as resolve6, join as join11, isAbsolute } from "path";
|
|
11717
|
+
import { homedir as homedir10 } from "os";
|
|
11598
11718
|
import { readdirSync as readdirSync2 } from "fs";
|
|
11599
11719
|
function allowedOrigins() {
|
|
11600
11720
|
const base = [
|
|
@@ -11611,15 +11731,15 @@ function resolveLocalLogDir(rawPath) {
|
|
|
11611
11731
|
const dir = String(rawPath || "").trim().replace(/[\\/]+$/, "");
|
|
11612
11732
|
if (!dir) return null;
|
|
11613
11733
|
if (isAbsolute(dir)) return dir;
|
|
11614
|
-
return resolve6(
|
|
11734
|
+
return resolve6(homedir10(), ".solongate", "local-logs");
|
|
11615
11735
|
}
|
|
11616
11736
|
async function findLogDir() {
|
|
11617
|
-
const base = resolve6(
|
|
11737
|
+
const base = resolve6(homedir10(), ".solongate");
|
|
11618
11738
|
try {
|
|
11619
11739
|
const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
|
|
11620
11740
|
for (const f of files) {
|
|
11621
11741
|
try {
|
|
11622
|
-
const c2 = JSON.parse(readFileSync9(
|
|
11742
|
+
const c2 = JSON.parse(readFileSync9(join11(base, f), "utf-8"));
|
|
11623
11743
|
const p = c2?.security?.localLogs?.path;
|
|
11624
11744
|
if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
|
|
11625
11745
|
} catch {
|
|
@@ -11628,7 +11748,7 @@ async function findLogDir() {
|
|
|
11628
11748
|
} catch {
|
|
11629
11749
|
}
|
|
11630
11750
|
try {
|
|
11631
|
-
const cfgRaw = readFileSync9(
|
|
11751
|
+
const cfgRaw = readFileSync9(join11(base, "cloud-guard.json"), "utf-8");
|
|
11632
11752
|
const { apiKey, apiUrl } = JSON.parse(cfgRaw);
|
|
11633
11753
|
if (apiKey) {
|
|
11634
11754
|
const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
|
|
@@ -11657,7 +11777,7 @@ function setCors(req, res) {
|
|
|
11657
11777
|
}
|
|
11658
11778
|
function fileInfo(dir) {
|
|
11659
11779
|
if (!dir) return { file: null, exists: false, size: 0, mtimeMs: 0 };
|
|
11660
|
-
const file =
|
|
11780
|
+
const file = join11(dir, LOG_FILENAME);
|
|
11661
11781
|
try {
|
|
11662
11782
|
const st = statSync5(file);
|
|
11663
11783
|
return { file, exists: true, size: st.size, mtimeMs: st.mtimeMs };
|
|
@@ -12108,7 +12228,7 @@ var init_inject = __esm({
|
|
|
12108
12228
|
// src/create.ts
|
|
12109
12229
|
var create_exports = {};
|
|
12110
12230
|
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync6, existsSync as existsSync9 } from "fs";
|
|
12111
|
-
import { resolve as resolve8, join as
|
|
12231
|
+
import { resolve as resolve8, join as join12 } from "path";
|
|
12112
12232
|
import { execSync as execSync2 } from "child_process";
|
|
12113
12233
|
function withSpinner(message, fn) {
|
|
12114
12234
|
const frames = ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"];
|
|
@@ -12188,7 +12308,7 @@ EXAMPLES
|
|
|
12188
12308
|
}
|
|
12189
12309
|
function createProject(dir, name, _policy) {
|
|
12190
12310
|
writeFileSync6(
|
|
12191
|
-
|
|
12311
|
+
join12(dir, "package.json"),
|
|
12192
12312
|
JSON.stringify(
|
|
12193
12313
|
{
|
|
12194
12314
|
name,
|
|
@@ -12218,7 +12338,7 @@ function createProject(dir, name, _policy) {
|
|
|
12218
12338
|
) + "\n"
|
|
12219
12339
|
);
|
|
12220
12340
|
writeFileSync6(
|
|
12221
|
-
|
|
12341
|
+
join12(dir, "tsconfig.json"),
|
|
12222
12342
|
JSON.stringify(
|
|
12223
12343
|
{
|
|
12224
12344
|
compilerOptions: {
|
|
@@ -12238,9 +12358,9 @@ function createProject(dir, name, _policy) {
|
|
|
12238
12358
|
2
|
|
12239
12359
|
) + "\n"
|
|
12240
12360
|
);
|
|
12241
|
-
mkdirSync5(
|
|
12361
|
+
mkdirSync5(join12(dir, "src"), { recursive: true });
|
|
12242
12362
|
writeFileSync6(
|
|
12243
|
-
|
|
12363
|
+
join12(dir, "src", "index.ts"),
|
|
12244
12364
|
`#!/usr/bin/env node
|
|
12245
12365
|
|
|
12246
12366
|
console.log = (...args: unknown[]) => {
|
|
@@ -12282,7 +12402,7 @@ console.log('Press Ctrl+C to stop.');
|
|
|
12282
12402
|
`
|
|
12283
12403
|
);
|
|
12284
12404
|
writeFileSync6(
|
|
12285
|
-
|
|
12405
|
+
join12(dir, ".mcp.json"),
|
|
12286
12406
|
JSON.stringify(
|
|
12287
12407
|
{
|
|
12288
12408
|
mcpServers: {
|
|
@@ -12300,12 +12420,12 @@ console.log('Press Ctrl+C to stop.');
|
|
|
12300
12420
|
) + "\n"
|
|
12301
12421
|
);
|
|
12302
12422
|
writeFileSync6(
|
|
12303
|
-
|
|
12423
|
+
join12(dir, ".env"),
|
|
12304
12424
|
`SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
|
|
12305
12425
|
`
|
|
12306
12426
|
);
|
|
12307
12427
|
writeFileSync6(
|
|
12308
|
-
|
|
12428
|
+
join12(dir, ".gitignore"),
|
|
12309
12429
|
`node_modules/
|
|
12310
12430
|
dist/
|
|
12311
12431
|
*.solongate-backup
|