@solongate/proxy 0.81.24 → 0.81.25
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 +115 -207
- package/dist/tui/hooks.d.ts +3 -0
- package/dist/tui/index.js +111 -192
- package/package.json +1 -1
- package/dist/tui/panels/Agents.d.ts +0 -4
- package/dist/tui/panels/Stats.d.ts +0 -3
package/dist/index.js
CHANGED
|
@@ -6814,7 +6814,7 @@ function DataView({
|
|
|
6814
6814
|
"\u2717 ",
|
|
6815
6815
|
error
|
|
6816
6816
|
] });
|
|
6817
|
-
if (loading) return /* @__PURE__ */ jsx(Text, { color: theme.
|
|
6817
|
+
if (loading) return /* @__PURE__ */ jsx(Text, { color: theme.warn, children: "\u27F3 loading\u2026" });
|
|
6818
6818
|
if (empty) return /* @__PURE__ */ jsx(Text, { color: theme.dim, children: emptyText ?? "No data." });
|
|
6819
6819
|
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
6820
6820
|
}
|
|
@@ -7203,6 +7203,7 @@ function useLoader(fn, deps = []) {
|
|
|
7203
7203
|
const [loading, setLoading] = useState(true);
|
|
7204
7204
|
const [nonce, setNonce] = useState(0);
|
|
7205
7205
|
const alive = useRef(true);
|
|
7206
|
+
const quiet = useRef(false);
|
|
7206
7207
|
const fnRef = useRef(fn);
|
|
7207
7208
|
fnRef.current = fn;
|
|
7208
7209
|
useEffect(() => {
|
|
@@ -7212,7 +7213,8 @@ function useLoader(fn, deps = []) {
|
|
|
7212
7213
|
};
|
|
7213
7214
|
}, []);
|
|
7214
7215
|
useEffect(() => {
|
|
7215
|
-
setLoading(true);
|
|
7216
|
+
if (!quiet.current) setLoading(true);
|
|
7217
|
+
quiet.current = false;
|
|
7216
7218
|
fnRef.current().then((d) => {
|
|
7217
7219
|
if (!alive.current) return;
|
|
7218
7220
|
setData(d);
|
|
@@ -7225,7 +7227,11 @@ function useLoader(fn, deps = []) {
|
|
|
7225
7227
|
});
|
|
7226
7228
|
}, [nonce, ...deps]);
|
|
7227
7229
|
const reload = useCallback(() => setNonce((n) => n + 1), []);
|
|
7228
|
-
|
|
7230
|
+
const reloadQuiet = useCallback(() => {
|
|
7231
|
+
quiet.current = true;
|
|
7232
|
+
setNonce((n) => n + 1);
|
|
7233
|
+
}, []);
|
|
7234
|
+
return { data, error, loading, reload, reloadQuiet };
|
|
7229
7235
|
}
|
|
7230
7236
|
function usePoll(reload, intervalMs, enabled = true) {
|
|
7231
7237
|
useEffect(() => {
|
|
@@ -7529,13 +7535,13 @@ function LivePanel({ active: active2 }) {
|
|
|
7529
7535
|
const insights = useLoader(() => api.stats.securityInsights(7));
|
|
7530
7536
|
const guard = useLoader(() => api.settings.getGuardStatus());
|
|
7531
7537
|
usePoll(() => {
|
|
7532
|
-
if (!paused()) sessions.
|
|
7538
|
+
if (!paused()) sessions.reloadQuiet();
|
|
7533
7539
|
}, 8e3, active2);
|
|
7534
7540
|
usePoll(() => {
|
|
7535
|
-
if (!paused()) insights.
|
|
7541
|
+
if (!paused()) insights.reloadQuiet();
|
|
7536
7542
|
}, 2e4, active2);
|
|
7537
7543
|
usePoll(() => {
|
|
7538
|
-
if (!paused()) guard.
|
|
7544
|
+
if (!paused()) guard.reloadQuiet();
|
|
7539
7545
|
}, 6e4, active2);
|
|
7540
7546
|
useEffect2(() => {
|
|
7541
7547
|
if (!detail) return;
|
|
@@ -8871,8 +8877,8 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
8871
8877
|
const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
|
|
8872
8878
|
const tsQ = useLoader(() => source === "cloud" ? api.stats.timeseries({ period: "24h" }) : Promise.resolve(null), [source]);
|
|
8873
8879
|
usePoll(() => {
|
|
8874
|
-
statsQ.
|
|
8875
|
-
tsQ.
|
|
8880
|
+
statsQ.reloadQuiet();
|
|
8881
|
+
tsQ.reloadQuiet();
|
|
8876
8882
|
}, 15e3, active2 && source === "cloud");
|
|
8877
8883
|
const query = {
|
|
8878
8884
|
filter: DECISIONS[di],
|
|
@@ -8888,9 +8894,9 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
8888
8894
|
() => source === "cloud" ? api.audit.list(query) : Promise.resolve(null),
|
|
8889
8895
|
[source, di, gi, tool, agent, search, sessFilter, page]
|
|
8890
8896
|
);
|
|
8891
|
-
usePoll(cloudQ.
|
|
8897
|
+
usePoll(cloudQ.reloadQuiet, 6e3, active2 && source === "cloud" && view === "logs" && !editing && page === 0);
|
|
8892
8898
|
const localQ = useLoader(() => source === "local" ? Promise.resolve(loadLocalRows()) : Promise.resolve(null), [source]);
|
|
8893
|
-
usePoll(localQ.
|
|
8899
|
+
usePoll(localQ.reloadQuiet, 6e3, active2 && source === "local" && view === "logs" && !editing && page === 0);
|
|
8894
8900
|
let pageRows = [];
|
|
8895
8901
|
let total = 0;
|
|
8896
8902
|
if (source === "cloud") {
|
|
@@ -8918,7 +8924,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
8918
8924
|
() => source === "cloud" ? api.agents.live({ limit: 100, includeDeactivated: true }) : Promise.resolve(null),
|
|
8919
8925
|
[source]
|
|
8920
8926
|
);
|
|
8921
|
-
usePoll(agentsQ.
|
|
8927
|
+
usePoll(agentsQ.reloadQuiet, 8e3, active2 && source === "cloud" && view === "sessions");
|
|
8922
8928
|
let sessions = [];
|
|
8923
8929
|
if (source === "cloud") {
|
|
8924
8930
|
sessions = (agentsQ.data?.agents ?? []).map((a) => ({
|
|
@@ -8978,7 +8984,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
8978
8984
|
else if (key.downArrow) setSessSel((n) => Math.min(sessionsFiltered.length - 1, n + 1));
|
|
8979
8985
|
else if (key.pageUp) setSessSel((n) => Math.max(0, n - 10));
|
|
8980
8986
|
else if (key.pageDown) setSessSel((n) => Math.min(sessionsFiltered.length - 1, n + 10));
|
|
8981
|
-
else if (key.return
|
|
8987
|
+
else if (key.return) {
|
|
8982
8988
|
if (currentSess) {
|
|
8983
8989
|
setSessFilter(currentSess.id);
|
|
8984
8990
|
setPage(0);
|
|
@@ -9000,17 +9006,17 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9000
9006
|
else if (key.downArrow) setSel((n) => Math.min(pageRows.length - 1, n + 1));
|
|
9001
9007
|
else if (key.pageUp) setSel((n) => Math.max(0, n - 10));
|
|
9002
9008
|
else if (key.pageDown) setSel((n) => Math.min(pageRows.length - 1, n + 10));
|
|
9003
|
-
else if (key.return
|
|
9009
|
+
else if (key.return) {
|
|
9004
9010
|
if (current) {
|
|
9005
9011
|
setDetailScroll(0);
|
|
9006
9012
|
setView("detail");
|
|
9007
9013
|
}
|
|
9008
|
-
} else if (
|
|
9014
|
+
} else if (key.rightArrow) {
|
|
9009
9015
|
if (page < pages - 1) {
|
|
9010
9016
|
setPage(page + 1);
|
|
9011
9017
|
toTop();
|
|
9012
9018
|
}
|
|
9013
|
-
} else if (
|
|
9019
|
+
} else if (key.leftArrow) {
|
|
9014
9020
|
if (page > 0) {
|
|
9015
9021
|
setPage(page - 1);
|
|
9016
9022
|
toTop();
|
|
@@ -9149,7 +9155,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9149
9155
|
const selC = Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1));
|
|
9150
9156
|
const start2 = Math.min(Math.max(0, selC - Math.floor((listRows2 - 1) / 2)), Math.max(0, sessionsFiltered.length - listRows2));
|
|
9151
9157
|
const win = sessionsFiltered.slice(start2, start2 + listRows2);
|
|
9152
|
-
const loading2 = source === "cloud" ? agentsQ.loading
|
|
9158
|
+
const loading2 = source === "cloud" ? agentsQ.loading : localQ.loading;
|
|
9153
9159
|
return /* @__PURE__ */ jsx6(DataView, { loading: loading2, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
9154
9160
|
strip,
|
|
9155
9161
|
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
@@ -9220,7 +9226,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9220
9226
|
const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
|
|
9221
9227
|
const windowed = pageRows.slice(start, start + listRows);
|
|
9222
9228
|
const reasonW = Math.min(60, Math.max(12, cols - 67));
|
|
9223
|
-
const loading = source === "cloud" ? cloudQ.loading
|
|
9229
|
+
const loading = (source === "cloud" ? cloudQ.loading : localQ.loading) && !editing;
|
|
9224
9230
|
return /* @__PURE__ */ jsx6(DataView, { loading, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
9225
9231
|
strip,
|
|
9226
9232
|
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
@@ -9234,7 +9240,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9234
9240
|
chip("search", search || "\xB7", !!search),
|
|
9235
9241
|
sessFilter ? chip("sess", sessFilter.slice(0, 8), true) : null
|
|
9236
9242
|
] }),
|
|
9237
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7
|
|
9243
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 \u2190\u2192 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
9244
|
editing && editing !== "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
9239
9245
|
/* @__PURE__ */ jsxs6(Text6, { color: theme.warn, children: [
|
|
9240
9246
|
editing,
|
|
@@ -9332,107 +9338,11 @@ var init_Audit = __esm({
|
|
|
9332
9338
|
}
|
|
9333
9339
|
});
|
|
9334
9340
|
|
|
9335
|
-
// src/tui/panels/Agents.tsx
|
|
9336
|
-
import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
|
|
9337
|
-
import { useState as useState7 } from "react";
|
|
9338
|
-
import { Fragment as Fragment5, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
9339
|
-
function Bar({ label, value, max = 100, width: width2 = 16, color = theme.accent }) {
|
|
9340
|
-
const filled = max > 0 ? Math.round(Math.min(value, max) / max * width2) : 0;
|
|
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 })
|
|
9346
|
-
] });
|
|
9347
|
-
}
|
|
9348
|
-
function AgentsPanel({ focused }) {
|
|
9349
|
-
const live2 = useLoader(() => api.agents.live({ limit: 30, includeDeactivated: true }));
|
|
9350
|
-
usePoll(live2.reload, 6e3, focused ? false : true);
|
|
9351
|
-
const [sel, setSel] = useState7(0);
|
|
9352
|
-
const agents = (live2.data?.agents ?? []).filter((a) => a.agent_id);
|
|
9353
|
-
const selected = agents[Math.min(sel, Math.max(0, agents.length - 1))];
|
|
9354
|
-
const detail = useLoader(() => selected?.agent_id ? api.agents.get(selected.agent_id) : Promise.resolve(null), [selected?.agent_id]);
|
|
9355
|
-
useInput6(
|
|
9356
|
-
(_input, key) => {
|
|
9357
|
-
if (key.upArrow) setSel((n) => Math.max(0, n - 1));
|
|
9358
|
-
else if (key.downArrow) setSel((n) => Math.min(agents.length - 1, n + 1));
|
|
9359
|
-
},
|
|
9360
|
-
{ isActive: focused }
|
|
9361
|
-
);
|
|
9362
|
-
const d = detail.data;
|
|
9363
|
-
const base = d?.baseline;
|
|
9364
|
-
const radar = base?.radar;
|
|
9365
|
-
const anomalies2 = d?.anomalies ?? [];
|
|
9366
|
-
const tmap = d?.trust_map;
|
|
9367
|
-
const tools = tmap?.tools ?? [];
|
|
9368
|
-
const resources = tmap?.resources ?? [];
|
|
9369
|
-
const listW = 30;
|
|
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: [
|
|
9375
|
-
a.status === "active" ? "\u25CF" : a.status === "idle" ? "\u25D0" : "\u25CB",
|
|
9376
|
-
" "
|
|
9377
|
-
] }),
|
|
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}` })
|
|
9380
|
-
] }, a.session_id))
|
|
9381
|
-
] }),
|
|
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 ?? ""}` })
|
|
9386
|
-
] }),
|
|
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 })
|
|
9395
|
-
] }) : null,
|
|
9396
|
-
/* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
9397
|
-
/* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
9398
|
-
"Anomalies ",
|
|
9399
|
-
anomalies2.length ? `(${anomalies2.length})` : ""
|
|
9400
|
-
] }),
|
|
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) })
|
|
9405
|
-
] }, i))
|
|
9406
|
-
] }),
|
|
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 ?? ""}` })
|
|
9412
|
-
] }, i)),
|
|
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))
|
|
9414
|
-
] })
|
|
9415
|
-
] }) })
|
|
9416
|
-
] }) });
|
|
9417
|
-
}
|
|
9418
|
-
var statusColor, sevColor;
|
|
9419
|
-
var init_Agents = __esm({
|
|
9420
|
-
"src/tui/panels/Agents.tsx"() {
|
|
9421
|
-
"use strict";
|
|
9422
|
-
init_api_client();
|
|
9423
|
-
init_components();
|
|
9424
|
-
init_hooks();
|
|
9425
|
-
init_theme();
|
|
9426
|
-
statusColor = (s) => s === "active" ? theme.ok : s === "idle" ? theme.warn : theme.dim;
|
|
9427
|
-
sevColor = (s) => s === "high" ? theme.bad : s === "medium" ? theme.warn : theme.dim;
|
|
9428
|
-
}
|
|
9429
|
-
});
|
|
9430
|
-
|
|
9431
9341
|
// src/tui/panels/Settings.tsx
|
|
9432
|
-
import { Box as
|
|
9342
|
+
import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
|
|
9433
9343
|
import TextInput5 from "ink-text-input";
|
|
9434
|
-
import { useState as
|
|
9435
|
-
import { jsx as
|
|
9344
|
+
import { useState as useState7 } from "react";
|
|
9345
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
9436
9346
|
function alertBodyFor(channel) {
|
|
9437
9347
|
const c2 = channel.trim();
|
|
9438
9348
|
const base = { name: "dataroom alert", threshold: 5, windowSeconds: 60, signal: "any" };
|
|
@@ -9443,12 +9353,12 @@ function alertBodyFor(channel) {
|
|
|
9443
9353
|
function SettingsPanel({ active: active2, focused }) {
|
|
9444
9354
|
void active2;
|
|
9445
9355
|
const { cols, rows } = usePanelSize();
|
|
9446
|
-
const [sel, setSel] =
|
|
9447
|
-
const [editing, setEditing] =
|
|
9448
|
-
const [input, setInput] =
|
|
9449
|
-
const [confirmDel, setConfirmDel] =
|
|
9450
|
-
const [msg, setMsg] =
|
|
9451
|
-
const [busy, setBusy] =
|
|
9356
|
+
const [sel, setSel] = useState7(0);
|
|
9357
|
+
const [editing, setEditing] = useState7(null);
|
|
9358
|
+
const [input, setInput] = useState7("");
|
|
9359
|
+
const [confirmDel, setConfirmDel] = useState7(null);
|
|
9360
|
+
const [msg, setMsg] = useState7(null);
|
|
9361
|
+
const [busy, setBusy] = useState7(false);
|
|
9452
9362
|
const localQ = useLoader(() => api.settings.getLocalLogs());
|
|
9453
9363
|
const whQ = useLoader(() => api.settings.getWebhooks());
|
|
9454
9364
|
const alertQ = useLoader(() => api.settings.getAlerts());
|
|
@@ -9518,7 +9428,7 @@ function SettingsPanel({ active: active2, focused }) {
|
|
|
9518
9428
|
run12("alert rule added", () => api.settings.createAlert(alertBodyFor(v)), alertQ.reload);
|
|
9519
9429
|
}
|
|
9520
9430
|
};
|
|
9521
|
-
|
|
9431
|
+
useInput6(
|
|
9522
9432
|
(inp, key) => {
|
|
9523
9433
|
setMsg(null);
|
|
9524
9434
|
if (key.upArrow) {
|
|
@@ -9557,7 +9467,7 @@ function SettingsPanel({ active: active2, focused }) {
|
|
|
9557
9467
|
const loading = localQ.loading && !localQ.data || whQ.loading && !whQ.data || alertQ.loading && !alertQ.data;
|
|
9558
9468
|
const error = localQ.error ?? whQ.error ?? alertQ.error;
|
|
9559
9469
|
const mark = (r) => keyOf(r) === keyOf(cur) && focused ? "\u25B8 " : " ";
|
|
9560
|
-
const onOff = (on) => on ? /* @__PURE__ */
|
|
9470
|
+
const onOff = (on) => on ? /* @__PURE__ */ jsx7(Text7, { color: theme.ok, children: "on " }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "off" });
|
|
9561
9471
|
const chanOf = (rule) => [...(rule.slackUrls ?? []).map((u) => "slack " + u), ...rule.emails ?? [], ...(rule.telegram ?? []).map((t) => "tg " + t)].join(", ") || "\u2014";
|
|
9562
9472
|
const listBudget = Math.max(4, rows - 8);
|
|
9563
9473
|
const start = Math.min(Math.max(0, selClamped - Math.floor(listBudget / 2)), Math.max(0, rowsAll.length - listBudget));
|
|
@@ -9565,68 +9475,68 @@ function SettingsPanel({ active: active2, focused }) {
|
|
|
9565
9475
|
const i = rowsAll.findIndex((x) => keyOf(x) === keyOf(r));
|
|
9566
9476
|
return i >= start && i < start + listBudget;
|
|
9567
9477
|
};
|
|
9568
|
-
return /* @__PURE__ */
|
|
9569
|
-
/* @__PURE__ */
|
|
9570
|
-
editing ? /* @__PURE__ */
|
|
9571
|
-
/* @__PURE__ */
|
|
9572
|
-
/* @__PURE__ */
|
|
9573
|
-
] }) : msg ? /* @__PURE__ */
|
|
9574
|
-
inWindow(rowsAll[0]) || inWindow(rowsAll[1]) ? /* @__PURE__ */
|
|
9575
|
-
/* @__PURE__ */
|
|
9478
|
+
return /* @__PURE__ */ jsx7(DataView, { loading, error, children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
9479
|
+
/* @__PURE__ */ jsx7(Text7, { 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" }),
|
|
9480
|
+
editing ? /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
9481
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: editing === "path" ? "local log path: " : editing === "wh-url" ? "webhook url: " : "channel (slack url / email / telegram id): " }),
|
|
9482
|
+
/* @__PURE__ */ jsx7(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
|
|
9483
|
+
] }) : msg ? /* @__PURE__ */ jsx7(Text7, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : /* @__PURE__ */ jsx7(Text7, { children: " " }),
|
|
9484
|
+
inWindow(rowsAll[0]) || inWindow(rowsAll[1]) ? /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
9485
|
+
/* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
|
|
9576
9486
|
"LOCAL LOGS ",
|
|
9577
|
-
/* @__PURE__ */
|
|
9487
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2014 hooks mirror every decision to a file on the agent machine" })
|
|
9578
9488
|
] }),
|
|
9579
|
-
/* @__PURE__ */
|
|
9580
|
-
/* @__PURE__ */
|
|
9581
|
-
/* @__PURE__ */
|
|
9489
|
+
/* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
9490
|
+
/* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "ll-enabled" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[0]) }),
|
|
9491
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "enabled ".padEnd(10) }),
|
|
9582
9492
|
onOff(!!local?.enabled),
|
|
9583
|
-
/* @__PURE__ */
|
|
9493
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " enter toggles" })
|
|
9584
9494
|
] }),
|
|
9585
|
-
/* @__PURE__ */
|
|
9586
|
-
/* @__PURE__ */
|
|
9587
|
-
/* @__PURE__ */
|
|
9588
|
-
local?.path ? /* @__PURE__ */
|
|
9495
|
+
/* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
9496
|
+
/* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "ll-path" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[1]) }),
|
|
9497
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "path ".padEnd(10) }),
|
|
9498
|
+
local?.path ? /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(local.path, cols - 14) }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "not set \u2014 enter to edit" })
|
|
9589
9499
|
] })
|
|
9590
9500
|
] }) : null,
|
|
9591
|
-
/* @__PURE__ */
|
|
9592
|
-
/* @__PURE__ */
|
|
9501
|
+
/* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
9502
|
+
/* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
|
|
9593
9503
|
"WEBHOOKS ",
|
|
9594
|
-
/* @__PURE__ */
|
|
9504
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
9595
9505
|
"\u2014 POST every matching event to your endpoint (",
|
|
9596
9506
|
webhooks.length,
|
|
9597
9507
|
")"
|
|
9598
9508
|
] })
|
|
9599
9509
|
] }),
|
|
9600
|
-
webhooks.filter((wh) => inWindow({ kind: "wh", wh })).map((wh) => /* @__PURE__ */
|
|
9601
|
-
/* @__PURE__ */
|
|
9510
|
+
webhooks.filter((wh) => inWindow({ kind: "wh", wh })).map((wh) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
9511
|
+
/* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "wh:" + wh.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh", wh }) }),
|
|
9602
9512
|
onOff(wh.enabled),
|
|
9603
|
-
/* @__PURE__ */
|
|
9604
|
-
/* @__PURE__ */
|
|
9513
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: (" " + wh.events).padEnd(9) }),
|
|
9514
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(wh.url, cols - 16) })
|
|
9605
9515
|
] }, wh.id)),
|
|
9606
|
-
inWindow(rowsAll.find((r) => r.kind === "wh-add")) ? /* @__PURE__ */
|
|
9607
|
-
/* @__PURE__ */
|
|
9608
|
-
/* @__PURE__ */
|
|
9516
|
+
inWindow(rowsAll.find((r) => r.kind === "wh-add")) ? /* @__PURE__ */ jsxs7(Text7, { children: [
|
|
9517
|
+
/* @__PURE__ */ jsx7(Text7, { color: cur.kind === "wh-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh-add" }) }),
|
|
9518
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "+ add webhook (enter)" })
|
|
9609
9519
|
] }) : null
|
|
9610
9520
|
] }),
|
|
9611
|
-
/* @__PURE__ */
|
|
9612
|
-
/* @__PURE__ */
|
|
9521
|
+
/* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
9522
|
+
/* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
|
|
9613
9523
|
"ALERTS ",
|
|
9614
|
-
/* @__PURE__ */
|
|
9524
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
9615
9525
|
"\u2014 notify on a signal spike (",
|
|
9616
9526
|
alerts.length,
|
|
9617
9527
|
")"
|
|
9618
9528
|
] })
|
|
9619
9529
|
] }),
|
|
9620
|
-
alerts.filter((rule) => inWindow({ kind: "alert", rule })).map((rule) => /* @__PURE__ */
|
|
9621
|
-
/* @__PURE__ */
|
|
9530
|
+
alerts.filter((rule) => inWindow({ kind: "alert", rule })).map((rule) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
9531
|
+
/* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "alert:" + rule.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert", rule }) }),
|
|
9622
9532
|
onOff(rule.enabled),
|
|
9623
|
-
/* @__PURE__ */
|
|
9624
|
-
/* @__PURE__ */
|
|
9625
|
-
/* @__PURE__ */
|
|
9533
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: (" " + rule.signal).padEnd(11) }),
|
|
9534
|
+
/* @__PURE__ */ jsx7(Text7, { children: `\u2265${rule.threshold}/${rule.windowSeconds}s ` }),
|
|
9535
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(chanOf(rule), cols - 26) })
|
|
9626
9536
|
] }, rule.id)),
|
|
9627
|
-
inWindow(rowsAll[rowsAll.length - 1]) ? /* @__PURE__ */
|
|
9628
|
-
/* @__PURE__ */
|
|
9629
|
-
/* @__PURE__ */
|
|
9537
|
+
inWindow(rowsAll[rowsAll.length - 1]) ? /* @__PURE__ */ jsxs7(Text7, { children: [
|
|
9538
|
+
/* @__PURE__ */ jsx7(Text7, { color: cur.kind === "alert-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert-add" }) }),
|
|
9539
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "+ add alert (enter \u2014 one channel: slack url, email or telegram chat id)" })
|
|
9630
9540
|
] }) : null
|
|
9631
9541
|
] })
|
|
9632
9542
|
] }) });
|
|
@@ -9644,17 +9554,17 @@ var init_Settings = __esm({
|
|
|
9644
9554
|
});
|
|
9645
9555
|
|
|
9646
9556
|
// src/tui/App.tsx
|
|
9647
|
-
import { Box as
|
|
9648
|
-
import { useState as
|
|
9649
|
-
import { jsx as
|
|
9557
|
+
import { Box as Box8, Text as Text8, useApp, useInput as useInput7 } from "ink";
|
|
9558
|
+
import { useState as useState8 } from "react";
|
|
9559
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
9650
9560
|
function LiveHint() {
|
|
9651
|
-
return /* @__PURE__ */
|
|
9652
|
-
/* @__PURE__ */
|
|
9653
|
-
/* @__PURE__ */
|
|
9654
|
-
/* @__PURE__ */
|
|
9655
|
-
/* @__PURE__ */
|
|
9561
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
9562
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
|
|
9563
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
|
|
9564
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
|
|
9565
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
9656
9566
|
"press ",
|
|
9657
|
-
/* @__PURE__ */
|
|
9567
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: "enter" }),
|
|
9658
9568
|
" to go live"
|
|
9659
9569
|
] }) })
|
|
9660
9570
|
] });
|
|
@@ -9662,22 +9572,22 @@ function LiveHint() {
|
|
|
9662
9572
|
function Banner({ cols }) {
|
|
9663
9573
|
const wide = cols >= 82;
|
|
9664
9574
|
if (!wide) {
|
|
9665
|
-
return /* @__PURE__ */
|
|
9666
|
-
/* @__PURE__ */
|
|
9667
|
-
/* @__PURE__ */
|
|
9575
|
+
return /* @__PURE__ */ jsxs8(Box8, { children: [
|
|
9576
|
+
/* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: "SolonGate" }),
|
|
9577
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " \u2014 security control center" })
|
|
9668
9578
|
] });
|
|
9669
9579
|
}
|
|
9670
|
-
return /* @__PURE__ */
|
|
9671
|
-
BANNER_FULL.map((line, i) => /* @__PURE__ */
|
|
9672
|
-
/* @__PURE__ */
|
|
9580
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
9581
|
+
BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx8(Text8, { bold: true, color: BANNER_HEX[i], children: line }, i)),
|
|
9582
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
|
|
9673
9583
|
] });
|
|
9674
9584
|
}
|
|
9675
9585
|
function App() {
|
|
9676
9586
|
const { exit } = useApp();
|
|
9677
|
-
const [section, setSection] =
|
|
9678
|
-
const [focus, setFocus] =
|
|
9679
|
-
const [help, setHelp] =
|
|
9680
|
-
|
|
9587
|
+
const [section, setSection] = useState8(0);
|
|
9588
|
+
const [focus, setFocus] = useState8("nav");
|
|
9589
|
+
const [help, setHelp] = useState8(false);
|
|
9590
|
+
useInput7((input, key) => {
|
|
9681
9591
|
if (help) {
|
|
9682
9592
|
setHelp(false);
|
|
9683
9593
|
return;
|
|
@@ -9698,31 +9608,31 @@ function App() {
|
|
|
9698
9608
|
});
|
|
9699
9609
|
const { cols, rows } = useTermSize();
|
|
9700
9610
|
const current = SECTIONS[section];
|
|
9701
|
-
if (help) return /* @__PURE__ */
|
|
9611
|
+
if (help) return /* @__PURE__ */ jsx8(HelpOverlay, { cols, rows });
|
|
9702
9612
|
if (current.label === "Live" && focus === "panel") {
|
|
9703
|
-
return /* @__PURE__ */
|
|
9613
|
+
return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx8(LivePanel, { active: true, focused: true }) });
|
|
9704
9614
|
}
|
|
9705
9615
|
const Panel = current.Panel;
|
|
9706
|
-
return /* @__PURE__ */
|
|
9707
|
-
/* @__PURE__ */
|
|
9708
|
-
/* @__PURE__ */
|
|
9709
|
-
/* @__PURE__ */
|
|
9710
|
-
/* @__PURE__ */
|
|
9616
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
|
|
9617
|
+
/* @__PURE__ */ jsx8(Banner, { cols }),
|
|
9618
|
+
/* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexGrow: 1, children: [
|
|
9619
|
+
/* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => /* @__PURE__ */ jsx8(Text8, { color: i === section ? theme.accentBright : void 0, bold: i === section, children: (i === section ? "\u25B8 " : " ") + s.label }, s.label)) }),
|
|
9620
|
+
/* @__PURE__ */ jsx8(Box8, { flexGrow: 1, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, children: /* @__PURE__ */ jsx8(Panel, { active: focus === "panel" || current.label !== "Live", focused: focus === "panel" }) })
|
|
9711
9621
|
] }),
|
|
9712
|
-
/* @__PURE__ */
|
|
9622
|
+
/* @__PURE__ */ jsx8(Box8, { children: focus === "nav" ? /* @__PURE__ */ jsx8(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ["?", "help"], ["q", "quit"]] }) : /* @__PURE__ */ jsx8(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
|
|
9713
9623
|
] });
|
|
9714
9624
|
}
|
|
9715
9625
|
function HelpOverlay({ cols, rows }) {
|
|
9716
|
-
return /* @__PURE__ */
|
|
9717
|
-
/* @__PURE__ */
|
|
9718
|
-
/* @__PURE__ */
|
|
9719
|
-
/* @__PURE__ */
|
|
9720
|
-
keys.map(([k, desc]) => /* @__PURE__ */
|
|
9721
|
-
/* @__PURE__ */
|
|
9722
|
-
/* @__PURE__ */
|
|
9626
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
|
|
9627
|
+
/* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
|
|
9628
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginBottom: 1, children: [
|
|
9629
|
+
/* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accent, children: group }),
|
|
9630
|
+
keys.map(([k, desc]) => /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
9631
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
|
|
9632
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: desc })
|
|
9723
9633
|
] }, k))
|
|
9724
9634
|
] }, group)) }),
|
|
9725
|
-
/* @__PURE__ */
|
|
9635
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "press any key to close" })
|
|
9726
9636
|
] });
|
|
9727
9637
|
}
|
|
9728
9638
|
var SECTIONS, BANNER_HEX, HELP;
|
|
@@ -9737,12 +9647,10 @@ var init_App = __esm({
|
|
|
9737
9647
|
init_RateLimit();
|
|
9738
9648
|
init_Dlp();
|
|
9739
9649
|
init_Audit();
|
|
9740
|
-
init_Agents();
|
|
9741
9650
|
init_Settings();
|
|
9742
9651
|
init_hooks();
|
|
9743
9652
|
SECTIONS = [
|
|
9744
9653
|
{ label: "Live", Panel: LiveHint },
|
|
9745
|
-
{ label: "Agents", Panel: AgentsPanel },
|
|
9746
9654
|
{ label: "Policies", Panel: PoliciesPanel },
|
|
9747
9655
|
{ label: "Rate Limit", Panel: RateLimitPanel },
|
|
9748
9656
|
{ label: "DLP", Panel: DlpPanel },
|
|
@@ -9755,7 +9663,7 @@ var init_App = __esm({
|
|
|
9755
9663
|
["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)"]]],
|
|
9756
9664
|
["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"]]],
|
|
9757
9665
|
["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"]]],
|
|
9758
|
-
["Audit", [["v", "logs \u2194 sessions"], ["s", "source: cloud \u2194 local"], ["
|
|
9666
|
+
["Audit", [["v", "logs \u2194 sessions"], ["s", "source: cloud \u2194 local"], ["\u2190 \u2192", "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"]]],
|
|
9759
9667
|
["Settings", [["\u2191\u2193", "move"], ["enter / space", "toggle \xB7 edit \xB7 add"], ["e", "webhook events"], ["d d", "delete"], ["r", "refresh"]]]
|
|
9760
9668
|
];
|
|
9761
9669
|
}
|
|
@@ -9767,7 +9675,7 @@ __export(tui_exports, {
|
|
|
9767
9675
|
launchTui: () => launchTui
|
|
9768
9676
|
});
|
|
9769
9677
|
import { render } from "ink";
|
|
9770
|
-
import { jsx as
|
|
9678
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
9771
9679
|
async function launchTui() {
|
|
9772
9680
|
if (!process.stdout.isTTY || !process.stdin.isTTY) {
|
|
9773
9681
|
process.stderr.write(
|
|
@@ -9781,7 +9689,7 @@ async function launchTui() {
|
|
|
9781
9689
|
}
|
|
9782
9690
|
process.stdout.write("\x1B[?1049h\x1B[H");
|
|
9783
9691
|
try {
|
|
9784
|
-
const { waitUntilExit } = render(/* @__PURE__ */
|
|
9692
|
+
const { waitUntilExit } = render(/* @__PURE__ */ jsx9(App, {}));
|
|
9785
9693
|
await waitUntilExit();
|
|
9786
9694
|
} finally {
|
|
9787
9695
|
process.stdout.write("\x1B[?1049l");
|
|
@@ -10432,7 +10340,7 @@ async function runAgents(argv) {
|
|
|
10432
10340
|
table(
|
|
10433
10341
|
["STATUS", "AGENT", "CALLS", "DENY", "DLP", "TRUST", "CHARACTER"],
|
|
10434
10342
|
res.agents.map((a) => [
|
|
10435
|
-
|
|
10343
|
+
statusColor(a.status),
|
|
10436
10344
|
cyan(truncate3(a.agent_name ?? a.agent_id ?? a.session_id, 20)),
|
|
10437
10345
|
String(a.total_calls),
|
|
10438
10346
|
a.denied_calls ? red(String(a.denied_calls)) : dim("0"),
|
|
@@ -10451,7 +10359,7 @@ async function runAgent(argv) {
|
|
|
10451
10359
|
const a = await api.agents.get(id);
|
|
10452
10360
|
if (json) return printJson(a), 0;
|
|
10453
10361
|
err("");
|
|
10454
|
-
err(` ${bold(a.agent_id)} status: ${
|
|
10362
|
+
err(` ${bold(a.agent_id)} status: ${statusColor(String(a.status))}`);
|
|
10455
10363
|
const base = a.baseline;
|
|
10456
10364
|
if (base) {
|
|
10457
10365
|
err(` ${dim(base.character ?? "")} trust ${bold(String(base.trustScore ?? "\u2014"))}/100 deny-rate ${(Number(base.denyRate ?? 0) * 100).toFixed(0)}%`);
|
|
@@ -10471,14 +10379,14 @@ async function runAgent(argv) {
|
|
|
10471
10379
|
}
|
|
10472
10380
|
return 0;
|
|
10473
10381
|
}
|
|
10474
|
-
var
|
|
10382
|
+
var statusColor;
|
|
10475
10383
|
var init_agents2 = __esm({
|
|
10476
10384
|
"src/commands/agents.ts"() {
|
|
10477
10385
|
"use strict";
|
|
10478
10386
|
init_api_client();
|
|
10479
10387
|
init_args();
|
|
10480
10388
|
init_format();
|
|
10481
|
-
|
|
10389
|
+
statusColor = (s) => s === "active" ? green(s) : s === "idle" ? yellow(s) : dim(s);
|
|
10482
10390
|
}
|
|
10483
10391
|
});
|
|
10484
10392
|
|
package/dist/tui/hooks.d.ts
CHANGED
|
@@ -3,6 +3,9 @@ export interface LoaderState<T> {
|
|
|
3
3
|
error: string | null;
|
|
4
4
|
loading: boolean;
|
|
5
5
|
reload: () => void;
|
|
6
|
+
/** Refetch WITHOUT flipping `loading` — for background polls, so the
|
|
7
|
+
* visible "loading…" is reserved for user actions (filters, paging). */
|
|
8
|
+
reloadQuiet: () => void;
|
|
6
9
|
}
|
|
7
10
|
/**
|
|
8
11
|
* Load async data once (and on demand via reload()). Safe against unmount —
|
package/dist/tui/index.js
CHANGED
|
@@ -8,8 +8,8 @@ var __export = (target, all) => {
|
|
|
8
8
|
import { render } from "ink";
|
|
9
9
|
|
|
10
10
|
// src/tui/App.tsx
|
|
11
|
-
import { Box as
|
|
12
|
-
import { useState as
|
|
11
|
+
import { Box as Box8, Text as Text8, useApp, useInput as useInput7 } from "ink";
|
|
12
|
+
import { useState as useState8 } from "react";
|
|
13
13
|
|
|
14
14
|
// src/cli-utils.ts
|
|
15
15
|
var c = {
|
|
@@ -142,7 +142,7 @@ function DataView({
|
|
|
142
142
|
"\u2717 ",
|
|
143
143
|
error
|
|
144
144
|
] });
|
|
145
|
-
if (loading) return /* @__PURE__ */ jsx(Text, { color: theme.
|
|
145
|
+
if (loading) return /* @__PURE__ */ jsx(Text, { color: theme.warn, children: "\u27F3 loading\u2026" });
|
|
146
146
|
if (empty) return /* @__PURE__ */ jsx(Text, { color: theme.dim, children: emptyText ?? "No data." });
|
|
147
147
|
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
148
148
|
}
|
|
@@ -593,6 +593,7 @@ function useLoader(fn, deps = []) {
|
|
|
593
593
|
const [loading, setLoading] = useState(true);
|
|
594
594
|
const [nonce, setNonce] = useState(0);
|
|
595
595
|
const alive = useRef(true);
|
|
596
|
+
const quiet = useRef(false);
|
|
596
597
|
const fnRef = useRef(fn);
|
|
597
598
|
fnRef.current = fn;
|
|
598
599
|
useEffect(() => {
|
|
@@ -602,7 +603,8 @@ function useLoader(fn, deps = []) {
|
|
|
602
603
|
};
|
|
603
604
|
}, []);
|
|
604
605
|
useEffect(() => {
|
|
605
|
-
setLoading(true);
|
|
606
|
+
if (!quiet.current) setLoading(true);
|
|
607
|
+
quiet.current = false;
|
|
606
608
|
fnRef.current().then((d) => {
|
|
607
609
|
if (!alive.current) return;
|
|
608
610
|
setData(d);
|
|
@@ -615,7 +617,11 @@ function useLoader(fn, deps = []) {
|
|
|
615
617
|
});
|
|
616
618
|
}, [nonce, ...deps]);
|
|
617
619
|
const reload = useCallback(() => setNonce((n) => n + 1), []);
|
|
618
|
-
|
|
620
|
+
const reloadQuiet = useCallback(() => {
|
|
621
|
+
quiet.current = true;
|
|
622
|
+
setNonce((n) => n + 1);
|
|
623
|
+
}, []);
|
|
624
|
+
return { data, error, loading, reload, reloadQuiet };
|
|
619
625
|
}
|
|
620
626
|
function usePoll(reload, intervalMs, enabled = true) {
|
|
621
627
|
useEffect(() => {
|
|
@@ -968,13 +974,13 @@ function LivePanel({ active: active2 }) {
|
|
|
968
974
|
const insights = useLoader(() => api.stats.securityInsights(7));
|
|
969
975
|
const guard = useLoader(() => api.settings.getGuardStatus());
|
|
970
976
|
usePoll(() => {
|
|
971
|
-
if (!paused()) sessions.
|
|
977
|
+
if (!paused()) sessions.reloadQuiet();
|
|
972
978
|
}, 8e3, active2);
|
|
973
979
|
usePoll(() => {
|
|
974
|
-
if (!paused()) insights.
|
|
980
|
+
if (!paused()) insights.reloadQuiet();
|
|
975
981
|
}, 2e4, active2);
|
|
976
982
|
usePoll(() => {
|
|
977
|
-
if (!paused()) guard.
|
|
983
|
+
if (!paused()) guard.reloadQuiet();
|
|
978
984
|
}, 6e4, active2);
|
|
979
985
|
useEffect2(() => {
|
|
980
986
|
if (!detail) return;
|
|
@@ -2235,8 +2241,8 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2235
2241
|
const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
|
|
2236
2242
|
const tsQ = useLoader(() => source === "cloud" ? api.stats.timeseries({ period: "24h" }) : Promise.resolve(null), [source]);
|
|
2237
2243
|
usePoll(() => {
|
|
2238
|
-
statsQ.
|
|
2239
|
-
tsQ.
|
|
2244
|
+
statsQ.reloadQuiet();
|
|
2245
|
+
tsQ.reloadQuiet();
|
|
2240
2246
|
}, 15e3, active2 && source === "cloud");
|
|
2241
2247
|
const query = {
|
|
2242
2248
|
filter: DECISIONS[di],
|
|
@@ -2252,9 +2258,9 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2252
2258
|
() => source === "cloud" ? api.audit.list(query) : Promise.resolve(null),
|
|
2253
2259
|
[source, di, gi, tool, agent, search, sessFilter, page]
|
|
2254
2260
|
);
|
|
2255
|
-
usePoll(cloudQ.
|
|
2261
|
+
usePoll(cloudQ.reloadQuiet, 6e3, active2 && source === "cloud" && view === "logs" && !editing && page === 0);
|
|
2256
2262
|
const localQ = useLoader(() => source === "local" ? Promise.resolve(loadLocalRows()) : Promise.resolve(null), [source]);
|
|
2257
|
-
usePoll(localQ.
|
|
2263
|
+
usePoll(localQ.reloadQuiet, 6e3, active2 && source === "local" && view === "logs" && !editing && page === 0);
|
|
2258
2264
|
let pageRows = [];
|
|
2259
2265
|
let total = 0;
|
|
2260
2266
|
if (source === "cloud") {
|
|
@@ -2282,7 +2288,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2282
2288
|
() => source === "cloud" ? api.agents.live({ limit: 100, includeDeactivated: true }) : Promise.resolve(null),
|
|
2283
2289
|
[source]
|
|
2284
2290
|
);
|
|
2285
|
-
usePoll(agentsQ.
|
|
2291
|
+
usePoll(agentsQ.reloadQuiet, 8e3, active2 && source === "cloud" && view === "sessions");
|
|
2286
2292
|
let sessions = [];
|
|
2287
2293
|
if (source === "cloud") {
|
|
2288
2294
|
sessions = (agentsQ.data?.agents ?? []).map((a) => ({
|
|
@@ -2342,7 +2348,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2342
2348
|
else if (key.downArrow) setSessSel((n) => Math.min(sessionsFiltered.length - 1, n + 1));
|
|
2343
2349
|
else if (key.pageUp) setSessSel((n) => Math.max(0, n - 10));
|
|
2344
2350
|
else if (key.pageDown) setSessSel((n) => Math.min(sessionsFiltered.length - 1, n + 10));
|
|
2345
|
-
else if (key.return
|
|
2351
|
+
else if (key.return) {
|
|
2346
2352
|
if (currentSess) {
|
|
2347
2353
|
setSessFilter(currentSess.id);
|
|
2348
2354
|
setPage(0);
|
|
@@ -2364,17 +2370,17 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2364
2370
|
else if (key.downArrow) setSel((n) => Math.min(pageRows.length - 1, n + 1));
|
|
2365
2371
|
else if (key.pageUp) setSel((n) => Math.max(0, n - 10));
|
|
2366
2372
|
else if (key.pageDown) setSel((n) => Math.min(pageRows.length - 1, n + 10));
|
|
2367
|
-
else if (key.return
|
|
2373
|
+
else if (key.return) {
|
|
2368
2374
|
if (current) {
|
|
2369
2375
|
setDetailScroll(0);
|
|
2370
2376
|
setView("detail");
|
|
2371
2377
|
}
|
|
2372
|
-
} else if (
|
|
2378
|
+
} else if (key.rightArrow) {
|
|
2373
2379
|
if (page < pages - 1) {
|
|
2374
2380
|
setPage(page + 1);
|
|
2375
2381
|
toTop();
|
|
2376
2382
|
}
|
|
2377
|
-
} else if (
|
|
2383
|
+
} else if (key.leftArrow) {
|
|
2378
2384
|
if (page > 0) {
|
|
2379
2385
|
setPage(page - 1);
|
|
2380
2386
|
toTop();
|
|
@@ -2513,7 +2519,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2513
2519
|
const selC = Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1));
|
|
2514
2520
|
const start2 = Math.min(Math.max(0, selC - Math.floor((listRows2 - 1) / 2)), Math.max(0, sessionsFiltered.length - listRows2));
|
|
2515
2521
|
const win = sessionsFiltered.slice(start2, start2 + listRows2);
|
|
2516
|
-
const loading2 = source === "cloud" ? agentsQ.loading
|
|
2522
|
+
const loading2 = source === "cloud" ? agentsQ.loading : localQ.loading;
|
|
2517
2523
|
return /* @__PURE__ */ jsx6(DataView, { loading: loading2, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
2518
2524
|
strip,
|
|
2519
2525
|
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
@@ -2584,7 +2590,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2584
2590
|
const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
|
|
2585
2591
|
const windowed = pageRows.slice(start, start + listRows);
|
|
2586
2592
|
const reasonW = Math.min(60, Math.max(12, cols - 67));
|
|
2587
|
-
const loading = source === "cloud" ? cloudQ.loading
|
|
2593
|
+
const loading = (source === "cloud" ? cloudQ.loading : localQ.loading) && !editing;
|
|
2588
2594
|
return /* @__PURE__ */ jsx6(DataView, { loading, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
2589
2595
|
strip,
|
|
2590
2596
|
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
@@ -2598,7 +2604,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2598
2604
|
chip("search", search || "\xB7", !!search),
|
|
2599
2605
|
sessFilter ? chip("sess", sessFilter.slice(0, 8), true) : null
|
|
2600
2606
|
] }),
|
|
2601
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7
|
|
2607
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 \u2190\u2192 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" }),
|
|
2602
2608
|
editing && editing !== "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
2603
2609
|
/* @__PURE__ */ jsxs6(Text6, { color: theme.warn, children: [
|
|
2604
2610
|
editing,
|
|
@@ -2658,97 +2664,11 @@ function Detail({ label, value, color }) {
|
|
|
2658
2664
|
] });
|
|
2659
2665
|
}
|
|
2660
2666
|
|
|
2661
|
-
// src/tui/panels/Agents.tsx
|
|
2662
|
-
import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
|
|
2663
|
-
import { useState as useState7 } from "react";
|
|
2664
|
-
import { Fragment as Fragment5, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2665
|
-
var statusColor = (s) => s === "active" ? theme.ok : s === "idle" ? theme.warn : theme.dim;
|
|
2666
|
-
var sevColor = (s) => s === "high" ? theme.bad : s === "medium" ? theme.warn : theme.dim;
|
|
2667
|
-
function Bar({ label, value, max = 100, width = 16, color = theme.accent }) {
|
|
2668
|
-
const filled = max > 0 ? Math.round(Math.min(value, max) / max * width) : 0;
|
|
2669
|
-
return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
2670
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: label.padEnd(9) }),
|
|
2671
|
-
/* @__PURE__ */ jsx7(Text7, { color, children: "\u2588".repeat(filled) }),
|
|
2672
|
-
/* @__PURE__ */ jsx7(Text7, { color: "#233457", children: "\u2591".repeat(Math.max(0, width - filled)) }),
|
|
2673
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " " + value })
|
|
2674
|
-
] });
|
|
2675
|
-
}
|
|
2676
|
-
function AgentsPanel({ focused }) {
|
|
2677
|
-
const live2 = useLoader(() => api.agents.live({ limit: 30, includeDeactivated: true }));
|
|
2678
|
-
usePoll(live2.reload, 6e3, focused ? false : true);
|
|
2679
|
-
const [sel, setSel] = useState7(0);
|
|
2680
|
-
const agents = (live2.data?.agents ?? []).filter((a) => a.agent_id);
|
|
2681
|
-
const selected = agents[Math.min(sel, Math.max(0, agents.length - 1))];
|
|
2682
|
-
const detail = useLoader(() => selected?.agent_id ? api.agents.get(selected.agent_id) : Promise.resolve(null), [selected?.agent_id]);
|
|
2683
|
-
useInput6(
|
|
2684
|
-
(_input, key) => {
|
|
2685
|
-
if (key.upArrow) setSel((n) => Math.max(0, n - 1));
|
|
2686
|
-
else if (key.downArrow) setSel((n) => Math.min(agents.length - 1, n + 1));
|
|
2687
|
-
},
|
|
2688
|
-
{ isActive: focused }
|
|
2689
|
-
);
|
|
2690
|
-
const d = detail.data;
|
|
2691
|
-
const base = d?.baseline;
|
|
2692
|
-
const radar = base?.radar;
|
|
2693
|
-
const anomalies2 = d?.anomalies ?? [];
|
|
2694
|
-
const tmap = d?.trust_map;
|
|
2695
|
-
const tools = tmap?.tools ?? [];
|
|
2696
|
-
const resources = tmap?.resources ?? [];
|
|
2697
|
-
const listW = 30;
|
|
2698
|
-
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: [
|
|
2699
|
-
/* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", width: listW, marginRight: 2, overflow: "hidden", children: [
|
|
2700
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: focused ? "\u2191\u2193 select agent" : "press \u2192 to browse" }),
|
|
2701
|
-
agents.slice(0, 16).map((a, i) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", backgroundColor: focused && i === sel ? "#1c2f63" : void 0, bold: i === sel, children: [
|
|
2702
|
-
/* @__PURE__ */ jsxs7(Text7, { color: statusColor(a.status), children: [
|
|
2703
|
-
a.status === "active" ? "\u25CF" : a.status === "idle" ? "\u25D0" : "\u25CB",
|
|
2704
|
-
" "
|
|
2705
|
-
] }),
|
|
2706
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate(a.agent_name ?? a.agent_id ?? "?", 16).padEnd(17) }),
|
|
2707
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: `t${a.trust_score}` })
|
|
2708
|
-
] }, a.session_id))
|
|
2709
|
-
] }),
|
|
2710
|
-
/* @__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: [
|
|
2711
|
-
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
2712
|
-
/* @__PURE__ */ jsx7(Text7, { bold: true, color: theme.accentBright, children: truncate(selected.agent_name ?? selected.agent_id ?? "?", 22) }),
|
|
2713
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: ` trust ${base?.trustScore ?? "\u2014"}/100 \xB7 ${base?.character ?? ""}` })
|
|
2714
|
-
] }),
|
|
2715
|
-
base?.characterBlurb ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: truncate(String(base.characterBlurb), 70) }) : null,
|
|
2716
|
-
radar ? /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
2717
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "Behaviour radar (0\u2013100)" }),
|
|
2718
|
-
/* @__PURE__ */ jsx7(Bar, { label: "read", value: Math.round((radar.read ?? 0) * 100) }),
|
|
2719
|
-
/* @__PURE__ */ jsx7(Bar, { label: "write", value: Math.round((radar.write ?? 0) * 100), color: theme.warn }),
|
|
2720
|
-
/* @__PURE__ */ jsx7(Bar, { label: "execute", value: Math.round((radar.execute ?? 0) * 100), color: theme.warn }),
|
|
2721
|
-
/* @__PURE__ */ jsx7(Bar, { label: "network", value: Math.round((radar.network ?? 0) * 100) }),
|
|
2722
|
-
/* @__PURE__ */ jsx7(Bar, { label: "complian.", value: Math.round((radar.compliance ?? 0) * 100), color: theme.ok })
|
|
2723
|
-
] }) : null,
|
|
2724
|
-
/* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
2725
|
-
/* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
2726
|
-
"Anomalies ",
|
|
2727
|
-
anomalies2.length ? `(${anomalies2.length})` : ""
|
|
2728
|
-
] }),
|
|
2729
|
-
anomalies2.length === 0 ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " none" }) : anomalies2.slice(0, 4).map((an, i) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
2730
|
-
/* @__PURE__ */ jsx7(Text7, { color: sevColor(String(an.severity)), children: ` ${String(an.severity).toUpperCase().slice(0, 4).padEnd(5)}` }),
|
|
2731
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: String(an.kind).padEnd(12) }),
|
|
2732
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: truncate(String(an.description ?? ""), 44) })
|
|
2733
|
-
] }, i))
|
|
2734
|
-
] }),
|
|
2735
|
-
/* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
2736
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "Trust map \xB7 top tools" }),
|
|
2737
|
-
tools.slice(0, 4).map((t, i) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
2738
|
-
/* @__PURE__ */ jsx7(Text7, { color: t.anomalous ? theme.bad : theme.accent, children: ` ${truncate(String(t.name), 16).padEnd(17)}` }),
|
|
2739
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: `${t.count}\xD7 ${t.permission ?? ""}` })
|
|
2740
|
-
] }, i)),
|
|
2741
|
-
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}"} ${truncate(String(r.value), 40)} ${r.count}\xD7` }, "r" + i))
|
|
2742
|
-
] })
|
|
2743
|
-
] }) })
|
|
2744
|
-
] }) });
|
|
2745
|
-
}
|
|
2746
|
-
|
|
2747
2667
|
// src/tui/panels/Settings.tsx
|
|
2748
|
-
import { Box as
|
|
2668
|
+
import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
|
|
2749
2669
|
import TextInput5 from "ink-text-input";
|
|
2750
|
-
import { useState as
|
|
2751
|
-
import { jsx as
|
|
2670
|
+
import { useState as useState7 } from "react";
|
|
2671
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2752
2672
|
var EVENTS = ["denials", "allowed", "all"];
|
|
2753
2673
|
function alertBodyFor(channel) {
|
|
2754
2674
|
const c2 = channel.trim();
|
|
@@ -2760,12 +2680,12 @@ function alertBodyFor(channel) {
|
|
|
2760
2680
|
function SettingsPanel({ active: active2, focused }) {
|
|
2761
2681
|
void active2;
|
|
2762
2682
|
const { cols, rows } = usePanelSize();
|
|
2763
|
-
const [sel, setSel] =
|
|
2764
|
-
const [editing, setEditing] =
|
|
2765
|
-
const [input, setInput] =
|
|
2766
|
-
const [confirmDel, setConfirmDel] =
|
|
2767
|
-
const [msg, setMsg] =
|
|
2768
|
-
const [busy, setBusy] =
|
|
2683
|
+
const [sel, setSel] = useState7(0);
|
|
2684
|
+
const [editing, setEditing] = useState7(null);
|
|
2685
|
+
const [input, setInput] = useState7("");
|
|
2686
|
+
const [confirmDel, setConfirmDel] = useState7(null);
|
|
2687
|
+
const [msg, setMsg] = useState7(null);
|
|
2688
|
+
const [busy, setBusy] = useState7(false);
|
|
2769
2689
|
const localQ = useLoader(() => api.settings.getLocalLogs());
|
|
2770
2690
|
const whQ = useLoader(() => api.settings.getWebhooks());
|
|
2771
2691
|
const alertQ = useLoader(() => api.settings.getAlerts());
|
|
@@ -2835,7 +2755,7 @@ function SettingsPanel({ active: active2, focused }) {
|
|
|
2835
2755
|
run("alert rule added", () => api.settings.createAlert(alertBodyFor(v)), alertQ.reload);
|
|
2836
2756
|
}
|
|
2837
2757
|
};
|
|
2838
|
-
|
|
2758
|
+
useInput6(
|
|
2839
2759
|
(inp, key) => {
|
|
2840
2760
|
setMsg(null);
|
|
2841
2761
|
if (key.upArrow) {
|
|
@@ -2874,7 +2794,7 @@ function SettingsPanel({ active: active2, focused }) {
|
|
|
2874
2794
|
const loading = localQ.loading && !localQ.data || whQ.loading && !whQ.data || alertQ.loading && !alertQ.data;
|
|
2875
2795
|
const error = localQ.error ?? whQ.error ?? alertQ.error;
|
|
2876
2796
|
const mark = (r) => keyOf(r) === keyOf(cur) && focused ? "\u25B8 " : " ";
|
|
2877
|
-
const onOff = (on) => on ? /* @__PURE__ */
|
|
2797
|
+
const onOff = (on) => on ? /* @__PURE__ */ jsx7(Text7, { color: theme.ok, children: "on " }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "off" });
|
|
2878
2798
|
const chanOf = (rule) => [...(rule.slackUrls ?? []).map((u) => "slack " + u), ...rule.emails ?? [], ...(rule.telegram ?? []).map((t) => "tg " + t)].join(", ") || "\u2014";
|
|
2879
2799
|
const listBudget = Math.max(4, rows - 8);
|
|
2880
2800
|
const start = Math.min(Math.max(0, selClamped - Math.floor(listBudget / 2)), Math.max(0, rowsAll.length - listBudget));
|
|
@@ -2882,90 +2802,89 @@ function SettingsPanel({ active: active2, focused }) {
|
|
|
2882
2802
|
const i = rowsAll.findIndex((x) => keyOf(x) === keyOf(r));
|
|
2883
2803
|
return i >= start && i < start + listBudget;
|
|
2884
2804
|
};
|
|
2885
|
-
return /* @__PURE__ */
|
|
2886
|
-
/* @__PURE__ */
|
|
2887
|
-
editing ? /* @__PURE__ */
|
|
2888
|
-
/* @__PURE__ */
|
|
2889
|
-
/* @__PURE__ */
|
|
2890
|
-
] }) : msg ? /* @__PURE__ */
|
|
2891
|
-
inWindow(rowsAll[0]) || inWindow(rowsAll[1]) ? /* @__PURE__ */
|
|
2892
|
-
/* @__PURE__ */
|
|
2805
|
+
return /* @__PURE__ */ jsx7(DataView, { loading, error, children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
2806
|
+
/* @__PURE__ */ jsx7(Text7, { 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" }),
|
|
2807
|
+
editing ? /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
2808
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: editing === "path" ? "local log path: " : editing === "wh-url" ? "webhook url: " : "channel (slack url / email / telegram id): " }),
|
|
2809
|
+
/* @__PURE__ */ jsx7(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
|
|
2810
|
+
] }) : msg ? /* @__PURE__ */ jsx7(Text7, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate(msg.text, cols) }) : /* @__PURE__ */ jsx7(Text7, { children: " " }),
|
|
2811
|
+
inWindow(rowsAll[0]) || inWindow(rowsAll[1]) ? /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
2812
|
+
/* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
|
|
2893
2813
|
"LOCAL LOGS ",
|
|
2894
|
-
/* @__PURE__ */
|
|
2814
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2014 hooks mirror every decision to a file on the agent machine" })
|
|
2895
2815
|
] }),
|
|
2896
|
-
/* @__PURE__ */
|
|
2897
|
-
/* @__PURE__ */
|
|
2898
|
-
/* @__PURE__ */
|
|
2816
|
+
/* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
2817
|
+
/* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "ll-enabled" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[0]) }),
|
|
2818
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "enabled ".padEnd(10) }),
|
|
2899
2819
|
onOff(!!local?.enabled),
|
|
2900
|
-
/* @__PURE__ */
|
|
2820
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " enter toggles" })
|
|
2901
2821
|
] }),
|
|
2902
|
-
/* @__PURE__ */
|
|
2903
|
-
/* @__PURE__ */
|
|
2904
|
-
/* @__PURE__ */
|
|
2905
|
-
local?.path ? /* @__PURE__ */
|
|
2822
|
+
/* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
2823
|
+
/* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "ll-path" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[1]) }),
|
|
2824
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "path ".padEnd(10) }),
|
|
2825
|
+
local?.path ? /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate(local.path, cols - 14) }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "not set \u2014 enter to edit" })
|
|
2906
2826
|
] })
|
|
2907
2827
|
] }) : null,
|
|
2908
|
-
/* @__PURE__ */
|
|
2909
|
-
/* @__PURE__ */
|
|
2828
|
+
/* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
2829
|
+
/* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
|
|
2910
2830
|
"WEBHOOKS ",
|
|
2911
|
-
/* @__PURE__ */
|
|
2831
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
2912
2832
|
"\u2014 POST every matching event to your endpoint (",
|
|
2913
2833
|
webhooks.length,
|
|
2914
2834
|
")"
|
|
2915
2835
|
] })
|
|
2916
2836
|
] }),
|
|
2917
|
-
webhooks.filter((wh) => inWindow({ kind: "wh", wh })).map((wh) => /* @__PURE__ */
|
|
2918
|
-
/* @__PURE__ */
|
|
2837
|
+
webhooks.filter((wh) => inWindow({ kind: "wh", wh })).map((wh) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
2838
|
+
/* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "wh:" + wh.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh", wh }) }),
|
|
2919
2839
|
onOff(wh.enabled),
|
|
2920
|
-
/* @__PURE__ */
|
|
2921
|
-
/* @__PURE__ */
|
|
2840
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: (" " + wh.events).padEnd(9) }),
|
|
2841
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate(wh.url, cols - 16) })
|
|
2922
2842
|
] }, wh.id)),
|
|
2923
|
-
inWindow(rowsAll.find((r) => r.kind === "wh-add")) ? /* @__PURE__ */
|
|
2924
|
-
/* @__PURE__ */
|
|
2925
|
-
/* @__PURE__ */
|
|
2843
|
+
inWindow(rowsAll.find((r) => r.kind === "wh-add")) ? /* @__PURE__ */ jsxs7(Text7, { children: [
|
|
2844
|
+
/* @__PURE__ */ jsx7(Text7, { color: cur.kind === "wh-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh-add" }) }),
|
|
2845
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "+ add webhook (enter)" })
|
|
2926
2846
|
] }) : null
|
|
2927
2847
|
] }),
|
|
2928
|
-
/* @__PURE__ */
|
|
2929
|
-
/* @__PURE__ */
|
|
2848
|
+
/* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
2849
|
+
/* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
|
|
2930
2850
|
"ALERTS ",
|
|
2931
|
-
/* @__PURE__ */
|
|
2851
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
2932
2852
|
"\u2014 notify on a signal spike (",
|
|
2933
2853
|
alerts.length,
|
|
2934
2854
|
")"
|
|
2935
2855
|
] })
|
|
2936
2856
|
] }),
|
|
2937
|
-
alerts.filter((rule) => inWindow({ kind: "alert", rule })).map((rule) => /* @__PURE__ */
|
|
2938
|
-
/* @__PURE__ */
|
|
2857
|
+
alerts.filter((rule) => inWindow({ kind: "alert", rule })).map((rule) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
2858
|
+
/* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "alert:" + rule.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert", rule }) }),
|
|
2939
2859
|
onOff(rule.enabled),
|
|
2940
|
-
/* @__PURE__ */
|
|
2941
|
-
/* @__PURE__ */
|
|
2942
|
-
/* @__PURE__ */
|
|
2860
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: (" " + rule.signal).padEnd(11) }),
|
|
2861
|
+
/* @__PURE__ */ jsx7(Text7, { children: `\u2265${rule.threshold}/${rule.windowSeconds}s ` }),
|
|
2862
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate(chanOf(rule), cols - 26) })
|
|
2943
2863
|
] }, rule.id)),
|
|
2944
|
-
inWindow(rowsAll[rowsAll.length - 1]) ? /* @__PURE__ */
|
|
2945
|
-
/* @__PURE__ */
|
|
2946
|
-
/* @__PURE__ */
|
|
2864
|
+
inWindow(rowsAll[rowsAll.length - 1]) ? /* @__PURE__ */ jsxs7(Text7, { children: [
|
|
2865
|
+
/* @__PURE__ */ jsx7(Text7, { color: cur.kind === "alert-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert-add" }) }),
|
|
2866
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "+ add alert (enter \u2014 one channel: slack url, email or telegram chat id)" })
|
|
2947
2867
|
] }) : null
|
|
2948
2868
|
] })
|
|
2949
2869
|
] }) });
|
|
2950
2870
|
}
|
|
2951
2871
|
|
|
2952
2872
|
// src/tui/App.tsx
|
|
2953
|
-
import { jsx as
|
|
2873
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2954
2874
|
function LiveHint() {
|
|
2955
|
-
return /* @__PURE__ */
|
|
2956
|
-
/* @__PURE__ */
|
|
2957
|
-
/* @__PURE__ */
|
|
2958
|
-
/* @__PURE__ */
|
|
2959
|
-
/* @__PURE__ */
|
|
2875
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
2876
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
|
|
2877
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
|
|
2878
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
|
|
2879
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
2960
2880
|
"press ",
|
|
2961
|
-
/* @__PURE__ */
|
|
2881
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: "enter" }),
|
|
2962
2882
|
" to go live"
|
|
2963
2883
|
] }) })
|
|
2964
2884
|
] });
|
|
2965
2885
|
}
|
|
2966
2886
|
var SECTIONS = [
|
|
2967
2887
|
{ label: "Live", Panel: LiveHint },
|
|
2968
|
-
{ label: "Agents", Panel: AgentsPanel },
|
|
2969
2888
|
{ label: "Policies", Panel: PoliciesPanel },
|
|
2970
2889
|
{ label: "Rate Limit", Panel: RateLimitPanel },
|
|
2971
2890
|
{ label: "DLP", Panel: DlpPanel },
|
|
@@ -2976,22 +2895,22 @@ var BANNER_HEX = ["#1432A0", "#2850BE", "#3C6ED7", "#5A8CE6", "#82AAF0", "#AAC8F
|
|
|
2976
2895
|
function Banner({ cols }) {
|
|
2977
2896
|
const wide = cols >= 82;
|
|
2978
2897
|
if (!wide) {
|
|
2979
|
-
return /* @__PURE__ */
|
|
2980
|
-
/* @__PURE__ */
|
|
2981
|
-
/* @__PURE__ */
|
|
2898
|
+
return /* @__PURE__ */ jsxs8(Box8, { children: [
|
|
2899
|
+
/* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: "SolonGate" }),
|
|
2900
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " \u2014 security control center" })
|
|
2982
2901
|
] });
|
|
2983
2902
|
}
|
|
2984
|
-
return /* @__PURE__ */
|
|
2985
|
-
BANNER_FULL.map((line, i) => /* @__PURE__ */
|
|
2986
|
-
/* @__PURE__ */
|
|
2903
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
2904
|
+
BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx8(Text8, { bold: true, color: BANNER_HEX[i], children: line }, i)),
|
|
2905
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
|
|
2987
2906
|
] });
|
|
2988
2907
|
}
|
|
2989
2908
|
function App() {
|
|
2990
2909
|
const { exit } = useApp();
|
|
2991
|
-
const [section, setSection] =
|
|
2992
|
-
const [focus, setFocus] =
|
|
2993
|
-
const [help, setHelp] =
|
|
2994
|
-
|
|
2910
|
+
const [section, setSection] = useState8(0);
|
|
2911
|
+
const [focus, setFocus] = useState8("nav");
|
|
2912
|
+
const [help, setHelp] = useState8(false);
|
|
2913
|
+
useInput7((input, key) => {
|
|
2995
2914
|
if (help) {
|
|
2996
2915
|
setHelp(false);
|
|
2997
2916
|
return;
|
|
@@ -3012,18 +2931,18 @@ function App() {
|
|
|
3012
2931
|
});
|
|
3013
2932
|
const { cols, rows } = useTermSize();
|
|
3014
2933
|
const current = SECTIONS[section];
|
|
3015
|
-
if (help) return /* @__PURE__ */
|
|
2934
|
+
if (help) return /* @__PURE__ */ jsx8(HelpOverlay, { cols, rows });
|
|
3016
2935
|
if (current.label === "Live" && focus === "panel") {
|
|
3017
|
-
return /* @__PURE__ */
|
|
2936
|
+
return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx8(LivePanel, { active: true, focused: true }) });
|
|
3018
2937
|
}
|
|
3019
2938
|
const Panel = current.Panel;
|
|
3020
|
-
return /* @__PURE__ */
|
|
3021
|
-
/* @__PURE__ */
|
|
3022
|
-
/* @__PURE__ */
|
|
3023
|
-
/* @__PURE__ */
|
|
3024
|
-
/* @__PURE__ */
|
|
2939
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
|
|
2940
|
+
/* @__PURE__ */ jsx8(Banner, { cols }),
|
|
2941
|
+
/* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexGrow: 1, children: [
|
|
2942
|
+
/* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => /* @__PURE__ */ jsx8(Text8, { color: i === section ? theme.accentBright : void 0, bold: i === section, children: (i === section ? "\u25B8 " : " ") + s.label }, s.label)) }),
|
|
2943
|
+
/* @__PURE__ */ jsx8(Box8, { flexGrow: 1, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, children: /* @__PURE__ */ jsx8(Panel, { active: focus === "panel" || current.label !== "Live", focused: focus === "panel" }) })
|
|
3025
2944
|
] }),
|
|
3026
|
-
/* @__PURE__ */
|
|
2945
|
+
/* @__PURE__ */ jsx8(Box8, { children: focus === "nav" ? /* @__PURE__ */ jsx8(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ["?", "help"], ["q", "quit"]] }) : /* @__PURE__ */ jsx8(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
|
|
3027
2946
|
] });
|
|
3028
2947
|
}
|
|
3029
2948
|
var HELP = [
|
|
@@ -3031,25 +2950,25 @@ var HELP = [
|
|
|
3031
2950
|
["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)"]]],
|
|
3032
2951
|
["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"]]],
|
|
3033
2952
|
["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"]]],
|
|
3034
|
-
["Audit", [["v", "logs \u2194 sessions"], ["s", "source: cloud \u2194 local"], ["
|
|
2953
|
+
["Audit", [["v", "logs \u2194 sessions"], ["s", "source: cloud \u2194 local"], ["\u2190 \u2192", "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"]]],
|
|
3035
2954
|
["Settings", [["\u2191\u2193", "move"], ["enter / space", "toggle \xB7 edit \xB7 add"], ["e", "webhook events"], ["d d", "delete"], ["r", "refresh"]]]
|
|
3036
2955
|
];
|
|
3037
2956
|
function HelpOverlay({ cols, rows }) {
|
|
3038
|
-
return /* @__PURE__ */
|
|
3039
|
-
/* @__PURE__ */
|
|
3040
|
-
/* @__PURE__ */
|
|
3041
|
-
/* @__PURE__ */
|
|
3042
|
-
keys.map(([k, desc]) => /* @__PURE__ */
|
|
3043
|
-
/* @__PURE__ */
|
|
3044
|
-
/* @__PURE__ */
|
|
2957
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
|
|
2958
|
+
/* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
|
|
2959
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginBottom: 1, children: [
|
|
2960
|
+
/* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accent, children: group }),
|
|
2961
|
+
keys.map(([k, desc]) => /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
2962
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
|
|
2963
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: desc })
|
|
3045
2964
|
] }, k))
|
|
3046
2965
|
] }, group)) }),
|
|
3047
|
-
/* @__PURE__ */
|
|
2966
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "press any key to close" })
|
|
3048
2967
|
] });
|
|
3049
2968
|
}
|
|
3050
2969
|
|
|
3051
2970
|
// src/tui/index.tsx
|
|
3052
|
-
import { jsx as
|
|
2971
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
3053
2972
|
async function launchTui() {
|
|
3054
2973
|
if (!process.stdout.isTTY || !process.stdin.isTTY) {
|
|
3055
2974
|
process.stderr.write(
|
|
@@ -3063,7 +2982,7 @@ async function launchTui() {
|
|
|
3063
2982
|
}
|
|
3064
2983
|
process.stdout.write("\x1B[?1049h\x1B[H");
|
|
3065
2984
|
try {
|
|
3066
|
-
const { waitUntilExit } = render(/* @__PURE__ */
|
|
2985
|
+
const { waitUntilExit } = render(/* @__PURE__ */ jsx9(App, {}));
|
|
3067
2986
|
await waitUntilExit();
|
|
3068
2987
|
} finally {
|
|
3069
2988
|
process.stdout.write("\x1B[?1049l");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.81.
|
|
3
|
+
"version": "0.81.25",
|
|
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": {
|