@solongate/proxy 0.61.0 → 0.62.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +575 -273
- package/dist/tui/index.js +559 -258
- package/dist/tui/panels/Live.d.ts +4 -0
- package/package.json +1 -1
- package/dist/tui/panels/Agents.d.ts +0 -3
package/dist/index.js
CHANGED
|
@@ -7018,18 +7018,102 @@ var init_hooks = __esm({
|
|
|
7018
7018
|
}
|
|
7019
7019
|
});
|
|
7020
7020
|
|
|
7021
|
+
// src/tui/panels/Live.tsx
|
|
7022
|
+
import { Box as Box2, Text as Text2 } from "ink";
|
|
7023
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
7024
|
+
function Tile({ label, value, color }) {
|
|
7025
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, marginRight: 1, minWidth: 14, children: [
|
|
7026
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: label }),
|
|
7027
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, color, children: value })
|
|
7028
|
+
] });
|
|
7029
|
+
}
|
|
7030
|
+
function LivePanel({ active: active2 }) {
|
|
7031
|
+
const stats = useLoader(() => api.stats.get());
|
|
7032
|
+
const ts = useLoader(() => api.stats.timeseries({ period: "24h" }));
|
|
7033
|
+
const feed = useLoader(() => api.audit.list({ limit: 12 }));
|
|
7034
|
+
usePoll(() => {
|
|
7035
|
+
stats.reload();
|
|
7036
|
+
ts.reload();
|
|
7037
|
+
feed.reload();
|
|
7038
|
+
}, 2e3, active2);
|
|
7039
|
+
const s = stats.data;
|
|
7040
|
+
const points = ts.data?.timeseries ?? [];
|
|
7041
|
+
const entries = feed.data?.entries ?? [];
|
|
7042
|
+
const denyRate = s && s.total_calls > 0 ? Math.round(s.denied / s.total_calls * 100) : 0;
|
|
7043
|
+
return /* @__PURE__ */ jsx2(DataView, { loading: stats.loading && !s, error: stats.error, children: /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
7044
|
+
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
7045
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: "\u25CF" }),
|
|
7046
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " LIVE \xB7 refreshing every 2s" })
|
|
7047
|
+
] }),
|
|
7048
|
+
s ? /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, children: [
|
|
7049
|
+
/* @__PURE__ */ jsx2(Tile, { label: "total calls", value: String(s.total_calls) }),
|
|
7050
|
+
/* @__PURE__ */ jsx2(Tile, { label: "allowed", value: String(s.allowed), color: theme.ok }),
|
|
7051
|
+
/* @__PURE__ */ jsx2(Tile, { label: "denied", value: String(s.denied), color: theme.bad }),
|
|
7052
|
+
/* @__PURE__ */ jsx2(Tile, { label: "deny rate", value: denyRate + "%", color: denyRate > 10 ? theme.bad : void 0 }),
|
|
7053
|
+
/* @__PURE__ */ jsx2(Tile, { label: "policies", value: String(s.active_policies), color: theme.accent })
|
|
7054
|
+
] }) : null,
|
|
7055
|
+
/* @__PURE__ */ jsxs2(Box2, { marginTop: 1, flexDirection: "column", children: [
|
|
7056
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "Activity \xB7 24h" }),
|
|
7057
|
+
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
7058
|
+
/* @__PURE__ */ jsx2(Text2, { children: "calls " }),
|
|
7059
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: sparkline(points.map((p) => p.total), 60) })
|
|
7060
|
+
] }),
|
|
7061
|
+
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
7062
|
+
/* @__PURE__ */ jsx2(Text2, { children: "denied " }),
|
|
7063
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: sparkline(points.map((p) => p.denied), 60) })
|
|
7064
|
+
] })
|
|
7065
|
+
] }),
|
|
7066
|
+
/* @__PURE__ */ jsxs2(Box2, { marginTop: 1, flexDirection: "column", children: [
|
|
7067
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "Live feed" }),
|
|
7068
|
+
entries.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " waiting for tool calls\u2026" }) : entries.slice(0, 10).map((e) => /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
7069
|
+
/* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), children: e.decision.padEnd(8) }),
|
|
7070
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate2(e.tool_name, 16).padEnd(17) }),
|
|
7071
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: truncate2(e.agent_name ?? "\u2014", 14).padEnd(15) }),
|
|
7072
|
+
e.dlp_matches?.length ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: "DLP " }) : /* @__PURE__ */ jsx2(Text2, { children: " " }),
|
|
7073
|
+
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7074
|
+
ago(e.created_at),
|
|
7075
|
+
" ago"
|
|
7076
|
+
] })
|
|
7077
|
+
] }, e.id))
|
|
7078
|
+
] })
|
|
7079
|
+
] }) });
|
|
7080
|
+
}
|
|
7081
|
+
var init_Live = __esm({
|
|
7082
|
+
"src/tui/panels/Live.tsx"() {
|
|
7083
|
+
"use strict";
|
|
7084
|
+
init_api_client();
|
|
7085
|
+
init_components();
|
|
7086
|
+
init_hooks();
|
|
7087
|
+
init_theme();
|
|
7088
|
+
}
|
|
7089
|
+
});
|
|
7090
|
+
|
|
7021
7091
|
// src/tui/panels/Policies.tsx
|
|
7022
|
-
import { Box as
|
|
7092
|
+
import { Box as Box3, Text as Text3, useInput } from "ink";
|
|
7093
|
+
import TextInput from "ink-text-input";
|
|
7023
7094
|
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
7024
|
-
import { jsx as
|
|
7095
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
7096
|
+
function ruleSummary(r) {
|
|
7097
|
+
const bits = [];
|
|
7098
|
+
for (const [g, tag] of [["commandConstraints", "cmd"], ["pathConstraints", "path"], ["filenameConstraints", "file"], ["urlConstraints", "url"]]) {
|
|
7099
|
+
const c2 = r[g];
|
|
7100
|
+
const n = (c2?.allowed?.length ?? 0) + (c2?.denied?.length ?? 0);
|
|
7101
|
+
if (n) bits.push(`${tag}:${n}`);
|
|
7102
|
+
}
|
|
7103
|
+
return bits.join(" ");
|
|
7104
|
+
}
|
|
7025
7105
|
function PoliciesPanel({ focused }) {
|
|
7026
7106
|
const list4 = useLoader(() => api.policies.list());
|
|
7027
7107
|
const policies = list4.data?.policies ?? [];
|
|
7028
7108
|
const [view, setView] = useState2("list");
|
|
7029
7109
|
const [pi, setPi] = useState2(0);
|
|
7030
7110
|
const [ri, setRi] = useState2(0);
|
|
7031
|
-
const [
|
|
7111
|
+
const [fi, setFi] = useState2(0);
|
|
7112
|
+
const [rules, setRules] = useState2([]);
|
|
7032
7113
|
const [mode, setMode] = useState2("denylist");
|
|
7114
|
+
const [dirty, setDirty] = useState2(false);
|
|
7115
|
+
const [editing, setEditing] = useState2(false);
|
|
7116
|
+
const [editVal, setEditVal] = useState2("");
|
|
7033
7117
|
const [status, setStatus] = useState2(null);
|
|
7034
7118
|
const selected = policies[Math.min(pi, Math.max(0, policies.length - 1))];
|
|
7035
7119
|
const detail = useLoader(() => selected ? api.policies.get(selected.id) : Promise.resolve(null), [selected?.id]);
|
|
@@ -7037,28 +7121,44 @@ function PoliciesPanel({ focused }) {
|
|
|
7037
7121
|
if (detail.data) {
|
|
7038
7122
|
setRules(detail.data.rules);
|
|
7039
7123
|
setMode(detail.data.mode ?? "denylist");
|
|
7124
|
+
setDirty(false);
|
|
7040
7125
|
setRi(0);
|
|
7041
7126
|
}
|
|
7042
7127
|
}, [detail.data]);
|
|
7043
|
-
const
|
|
7044
|
-
|
|
7128
|
+
const mutate = (nextRules, nextMode = mode) => {
|
|
7129
|
+
setRules(nextRules);
|
|
7130
|
+
setMode(nextMode);
|
|
7131
|
+
setDirty(true);
|
|
7132
|
+
setStatus(null);
|
|
7133
|
+
};
|
|
7134
|
+
const save = async () => {
|
|
7135
|
+
if (!selected || !detail.data) return;
|
|
7045
7136
|
setStatus("Saving\u2026");
|
|
7046
7137
|
try {
|
|
7047
7138
|
const res = await api.policies.update(selected.id, {
|
|
7048
7139
|
id: selected.id,
|
|
7049
7140
|
name: detail.data.name,
|
|
7050
7141
|
description: detail.data.description,
|
|
7051
|
-
mode
|
|
7142
|
+
mode,
|
|
7052
7143
|
agents: detail.data.agents,
|
|
7053
|
-
rules
|
|
7144
|
+
rules
|
|
7054
7145
|
});
|
|
7055
7146
|
setRules(res.rules);
|
|
7147
|
+
setDirty(false);
|
|
7056
7148
|
setStatus("\u2713 Saved v" + res._version);
|
|
7057
7149
|
list4.reload();
|
|
7058
7150
|
} catch (e) {
|
|
7059
7151
|
setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
|
|
7060
7152
|
}
|
|
7061
7153
|
};
|
|
7154
|
+
const discard = () => {
|
|
7155
|
+
if (detail.data) {
|
|
7156
|
+
setRules(detail.data.rules);
|
|
7157
|
+
setMode(detail.data.mode ?? "denylist");
|
|
7158
|
+
}
|
|
7159
|
+
setDirty(false);
|
|
7160
|
+
setStatus("discarded");
|
|
7161
|
+
};
|
|
7062
7162
|
useInput(
|
|
7063
7163
|
(input, key) => {
|
|
7064
7164
|
if (view === "list") {
|
|
@@ -7066,44 +7166,61 @@ function PoliciesPanel({ focused }) {
|
|
|
7066
7166
|
else if (key.downArrow) setPi((n) => Math.min(policies.length - 1, n + 1));
|
|
7067
7167
|
else if (key.return || key.rightArrow) {
|
|
7068
7168
|
setStatus(null);
|
|
7069
|
-
setView("
|
|
7169
|
+
setView("rules");
|
|
7070
7170
|
}
|
|
7071
7171
|
return;
|
|
7072
7172
|
}
|
|
7073
|
-
|
|
7074
|
-
|
|
7075
|
-
|
|
7076
|
-
|
|
7077
|
-
|
|
7078
|
-
|
|
7079
|
-
|
|
7080
|
-
|
|
7081
|
-
|
|
7082
|
-
|
|
7083
|
-
|
|
7084
|
-
|
|
7085
|
-
|
|
7086
|
-
if (
|
|
7087
|
-
|
|
7088
|
-
|
|
7089
|
-
|
|
7090
|
-
|
|
7091
|
-
|
|
7092
|
-
}
|
|
7093
|
-
|
|
7094
|
-
|
|
7095
|
-
|
|
7096
|
-
|
|
7173
|
+
if (view === "rules") {
|
|
7174
|
+
if (key.leftArrow) return setView("list"), void setStatus(null);
|
|
7175
|
+
if (key.upArrow) setRi((n) => Math.max(0, n - 1));
|
|
7176
|
+
else if (key.downArrow) setRi((n) => Math.min(rules.length - 1, n + 1));
|
|
7177
|
+
else if (key.return || key.rightArrow) {
|
|
7178
|
+
if (rules[ri]) {
|
|
7179
|
+
setFi(0);
|
|
7180
|
+
setView("rule");
|
|
7181
|
+
}
|
|
7182
|
+
} else if (input === " ") {
|
|
7183
|
+
if (rules[ri]) mutate(rules.map((r, i) => i === ri ? { ...r, enabled: !r.enabled } : r));
|
|
7184
|
+
} else if (input === "e") {
|
|
7185
|
+
if (rules[ri]) mutate(rules.map((r, i) => i === ri ? { ...r, effect: r.effect === "ALLOW" ? "DENY" : "ALLOW" } : r));
|
|
7186
|
+
} else if (input === "d") {
|
|
7187
|
+
if (rules[ri]) {
|
|
7188
|
+
const next = rules.filter((_, i) => i !== ri);
|
|
7189
|
+
setRi((n) => Math.max(0, Math.min(n, next.length - 1)));
|
|
7190
|
+
mutate(next);
|
|
7191
|
+
}
|
|
7192
|
+
} else if (input === "m") {
|
|
7193
|
+
mutate(rules, mode === "denylist" ? "whitelist" : "denylist");
|
|
7194
|
+
} else if (input === "s") void save();
|
|
7195
|
+
else if (input === "x") discard();
|
|
7196
|
+
return;
|
|
7097
7197
|
}
|
|
7198
|
+
const rule2 = rules[ri];
|
|
7199
|
+
if (!rule2) return setView("rules");
|
|
7200
|
+
const field = FIELDS[fi];
|
|
7201
|
+
if (key.leftArrow) setView("rules");
|
|
7202
|
+
else if (key.upArrow) setFi((n) => Math.max(0, n - 1));
|
|
7203
|
+
else if (key.downArrow) setFi((n) => Math.min(FIELDS.length - 1, n + 1));
|
|
7204
|
+
else if (field.kind === "effect" && (input === " " || key.rightArrow)) {
|
|
7205
|
+
mutate(rules.map((r, i) => i === ri ? { ...r, effect: r.effect === "ALLOW" ? "DENY" : "ALLOW" } : r));
|
|
7206
|
+
} else if (field.kind === "enabled" && (input === " " || key.rightArrow)) {
|
|
7207
|
+
mutate(rules.map((r, i) => i === ri ? { ...r, enabled: !r.enabled } : r));
|
|
7208
|
+
} else if (field.kind === "priority" && (key.rightArrow || key.leftArrow)) {
|
|
7209
|
+
const d = key.rightArrow ? 1 : -1;
|
|
7210
|
+
mutate(rules.map((r, i) => i === ri ? { ...r, priority: Math.max(0, r.priority + d) } : r));
|
|
7211
|
+
} else if (field.kind === "text" && key.return) {
|
|
7212
|
+
setEditVal(field.get(rule2));
|
|
7213
|
+
setEditing(true);
|
|
7214
|
+
} else if (input === "s") void save();
|
|
7098
7215
|
},
|
|
7099
|
-
{ isActive: focused }
|
|
7216
|
+
{ isActive: focused && !editing }
|
|
7100
7217
|
);
|
|
7101
7218
|
if (view === "list") {
|
|
7102
|
-
return /* @__PURE__ */
|
|
7103
|
-
/* @__PURE__ */
|
|
7104
|
-
/* @__PURE__ */
|
|
7219
|
+
return /* @__PURE__ */ jsx3(DataView, { loading: list4.loading && !list4.data, error: list4.error, empty: !!list4.data && policies.length === 0, emptyText: "No policies.", children: /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
7220
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: focused ? "\u2191\u2193 select \xB7 enter open" : "press \u2192 to browse" }),
|
|
7221
|
+
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, flexDirection: "column", children: policies.map((p, i) => /* @__PURE__ */ jsxs3(Text3, { color: i === pi ? theme.accentBright : void 0, bold: i === pi, children: [
|
|
7105
7222
|
(i === pi ? "\u25B8 " : " ") + truncate2(p.name, 26).padEnd(27),
|
|
7106
|
-
/* @__PURE__ */
|
|
7223
|
+
/* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
|
|
7107
7224
|
p.mode.padEnd(10),
|
|
7108
7225
|
" ",
|
|
7109
7226
|
p.rules.length,
|
|
@@ -7113,37 +7230,78 @@ function PoliciesPanel({ focused }) {
|
|
|
7113
7230
|
] }, p.id)) })
|
|
7114
7231
|
] }) });
|
|
7115
7232
|
}
|
|
7116
|
-
const
|
|
7117
|
-
|
|
7118
|
-
/* @__PURE__ */
|
|
7119
|
-
/* @__PURE__ */
|
|
7120
|
-
|
|
7121
|
-
|
|
7233
|
+
const dirtyTag = dirty ? /* @__PURE__ */ jsx3(Text3, { color: theme.warn, children: " \u25CF unsaved (s save \xB7 x discard)" }) : null;
|
|
7234
|
+
if (view === "rules") {
|
|
7235
|
+
return /* @__PURE__ */ jsx3(DataView, { loading: detail.loading && !detail.data, error: detail.error, children: /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
7236
|
+
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
7237
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.accentBright, children: truncate2(selected?.name ?? "", 28) }),
|
|
7238
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: " mode: " }),
|
|
7239
|
+
/* @__PURE__ */ jsx3(Text3, { color: mode === "whitelist" ? theme.warn : void 0, children: mode }),
|
|
7240
|
+
dirtyTag
|
|
7241
|
+
] }),
|
|
7242
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "\u2191\u2193 rule \xB7 enter edit \xB7 space on/off \xB7 e effect \xB7 d delete \xB7 m mode \xB7 s save \xB7 \u2190 back" }),
|
|
7243
|
+
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(
|
|
7244
|
+
Table,
|
|
7245
|
+
{
|
|
7246
|
+
columns: [
|
|
7247
|
+
{ header: "", width: 2 },
|
|
7248
|
+
{ header: "ON", width: 2 },
|
|
7249
|
+
{ header: "EFFECT", width: 7 },
|
|
7250
|
+
{ header: "PRIO", width: 4 },
|
|
7251
|
+
{ header: "TOOL", width: 20 },
|
|
7252
|
+
{ header: "CONSTRAINTS", width: 22 }
|
|
7253
|
+
],
|
|
7254
|
+
rows: rules.map((r, i) => [
|
|
7255
|
+
{ value: i === ri ? "\u25B8" : "", color: theme.accentBright },
|
|
7256
|
+
{ value: r.enabled ? "\u25CF" : "\u25CB", color: r.enabled ? theme.ok : theme.dim },
|
|
7257
|
+
{ value: r.effect, color: r.effect === "ALLOW" ? theme.ok : theme.bad, dim: !r.enabled },
|
|
7258
|
+
{ value: String(r.priority), dim: true },
|
|
7259
|
+
{ value: truncate2(r.toolPattern, 20), color: theme.accent, dim: !r.enabled },
|
|
7260
|
+
{ value: ruleSummary(r) || truncate2(r.description || "\u2014", 22), dim: true }
|
|
7261
|
+
])
|
|
7262
|
+
}
|
|
7263
|
+
) }),
|
|
7264
|
+
rules.length === 0 ? /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "(no rules)" }) : null,
|
|
7265
|
+
status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
7266
|
+
] }) });
|
|
7267
|
+
}
|
|
7268
|
+
const rule = rules[ri];
|
|
7269
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
7270
|
+
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
7271
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.accentBright, children: "Rule " }),
|
|
7272
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: truncate2(rule?.id ?? "", 26) }),
|
|
7273
|
+
dirtyTag
|
|
7122
7274
|
] }),
|
|
7123
|
-
/* @__PURE__ */
|
|
7124
|
-
/* @__PURE__ */
|
|
7125
|
-
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
|
|
7131
|
-
{
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
-
|
|
7145
|
-
|
|
7275
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "\u2191\u2193 field \xB7 enter edit text \xB7 space/\u2192 toggle \xB7 s save \xB7 \u2190 back" }),
|
|
7276
|
+
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, flexDirection: "column", children: FIELDS.map((f, i) => {
|
|
7277
|
+
const val = rule ? f.get(rule) : "";
|
|
7278
|
+
const active2 = i === fi;
|
|
7279
|
+
return /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
7280
|
+
/* @__PURE__ */ jsx3(Text3, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + f.label.padEnd(13) }),
|
|
7281
|
+
active2 && editing && f.kind === "text" ? /* @__PURE__ */ jsx3(
|
|
7282
|
+
TextInput,
|
|
7283
|
+
{
|
|
7284
|
+
value: editVal,
|
|
7285
|
+
onChange: setEditVal,
|
|
7286
|
+
onSubmit: (v) => {
|
|
7287
|
+
if (rule && f.set) mutate(rules.map((r, idx) => idx === ri ? f.set(r, v) : r));
|
|
7288
|
+
setEditing(false);
|
|
7289
|
+
}
|
|
7290
|
+
}
|
|
7291
|
+
) : /* @__PURE__ */ jsx3(
|
|
7292
|
+
Text3,
|
|
7293
|
+
{
|
|
7294
|
+
color: f.kind === "effect" ? val === "ALLOW" ? theme.ok : theme.bad : void 0,
|
|
7295
|
+
dimColor: !val,
|
|
7296
|
+
children: val || "\u2014"
|
|
7297
|
+
}
|
|
7298
|
+
)
|
|
7299
|
+
] }, f.label);
|
|
7300
|
+
}) }),
|
|
7301
|
+
status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
7302
|
+
] });
|
|
7146
7303
|
}
|
|
7304
|
+
var constr, setConstr, FIELDS;
|
|
7147
7305
|
var init_Policies = __esm({
|
|
7148
7306
|
"src/tui/panels/Policies.tsx"() {
|
|
7149
7307
|
"use strict";
|
|
@@ -7151,13 +7309,43 @@ var init_Policies = __esm({
|
|
|
7151
7309
|
init_components();
|
|
7152
7310
|
init_hooks();
|
|
7153
7311
|
init_theme();
|
|
7312
|
+
constr = (r, g, side) => (r[g]?.[side] ?? []).join(", ");
|
|
7313
|
+
setConstr = (r, g, side, val) => {
|
|
7314
|
+
const arr = val.split(",").map((s) => s.trim()).filter(Boolean);
|
|
7315
|
+
const prev = r[g] ?? {};
|
|
7316
|
+
return { ...r, [g]: { ...prev, [side]: arr } };
|
|
7317
|
+
};
|
|
7318
|
+
FIELDS = [
|
|
7319
|
+
{ label: "Effect", kind: "effect", get: (r) => r.effect },
|
|
7320
|
+
{ label: "Enabled", kind: "enabled", get: (r) => r.enabled ? "yes" : "no" },
|
|
7321
|
+
{ label: "Priority", kind: "priority", get: (r) => String(r.priority) },
|
|
7322
|
+
{ label: "Tool pattern", kind: "text", get: (r) => r.toolPattern, set: (r, v) => ({ ...r, toolPattern: v || "*" }) },
|
|
7323
|
+
{ label: "Description", kind: "text", get: (r) => r.description ?? "", set: (r, v) => ({ ...r, description: v }) },
|
|
7324
|
+
{ label: "Cmd allow", kind: "text", get: (r) => constr(r, "commandConstraints", "allowed"), set: (r, v) => setConstr(r, "commandConstraints", "allowed", v) },
|
|
7325
|
+
{ label: "Cmd deny", kind: "text", get: (r) => constr(r, "commandConstraints", "denied"), set: (r, v) => setConstr(r, "commandConstraints", "denied", v) },
|
|
7326
|
+
{ label: "Path allow", kind: "text", get: (r) => constr(r, "pathConstraints", "allowed"), set: (r, v) => setConstr(r, "pathConstraints", "allowed", v) },
|
|
7327
|
+
{ label: "Path deny", kind: "text", get: (r) => constr(r, "pathConstraints", "denied"), set: (r, v) => setConstr(r, "pathConstraints", "denied", v) },
|
|
7328
|
+
{ label: "File allow", kind: "text", get: (r) => constr(r, "filenameConstraints", "allowed"), set: (r, v) => setConstr(r, "filenameConstraints", "allowed", v) },
|
|
7329
|
+
{ label: "File deny", kind: "text", get: (r) => constr(r, "filenameConstraints", "denied"), set: (r, v) => setConstr(r, "filenameConstraints", "denied", v) },
|
|
7330
|
+
{ label: "URL allow", kind: "text", get: (r) => constr(r, "urlConstraints", "allowed"), set: (r, v) => setConstr(r, "urlConstraints", "allowed", v) },
|
|
7331
|
+
{ label: "URL deny", kind: "text", get: (r) => constr(r, "urlConstraints", "denied"), set: (r, v) => setConstr(r, "urlConstraints", "denied", v) }
|
|
7332
|
+
];
|
|
7154
7333
|
}
|
|
7155
7334
|
});
|
|
7156
7335
|
|
|
7157
7336
|
// src/tui/panels/RateLimit.tsx
|
|
7158
|
-
import { Box as
|
|
7337
|
+
import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
|
|
7159
7338
|
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
7160
|
-
import { jsx as
|
|
7339
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
7340
|
+
function ColoredSpark({ values, limit, width: width2 = 60 }) {
|
|
7341
|
+
const v = values.length > width2 ? values.slice(values.length - width2) : values;
|
|
7342
|
+
const max = Math.max(1, ...v);
|
|
7343
|
+
return /* @__PURE__ */ jsx4(Text4, { children: v.map((n, i) => {
|
|
7344
|
+
const ch = BLOCKS2[Math.min(BLOCKS2.length - 1, Math.round(n / max * (BLOCKS2.length - 1)))];
|
|
7345
|
+
const over = limit > 0 && n > limit;
|
|
7346
|
+
return /* @__PURE__ */ jsx4(Text4, { color: over ? theme.bad : theme.accent, children: ch }, i);
|
|
7347
|
+
}) });
|
|
7348
|
+
}
|
|
7161
7349
|
function RateLimitPanel({ focused }) {
|
|
7162
7350
|
const layersQ = useLoader(() => api.settings.getSecurityLayers());
|
|
7163
7351
|
const historyQ = useLoader(() => api.settings.getRateLimitHistory());
|
|
@@ -7171,7 +7359,7 @@ function RateLimitPanel({ focused }) {
|
|
|
7171
7359
|
}, [layersQ.data, draft]);
|
|
7172
7360
|
const adjust = (dir, step) => {
|
|
7173
7361
|
if (!draft) return;
|
|
7174
|
-
const field =
|
|
7362
|
+
const field = FIELDS2[fi];
|
|
7175
7363
|
if (field === "mode") {
|
|
7176
7364
|
const idx = (MODES.indexOf(draft.mode) + dir + MODES.length) % MODES.length;
|
|
7177
7365
|
setDraft({ ...draft, mode: MODES[idx] });
|
|
@@ -7185,8 +7373,7 @@ function RateLimitPanel({ focused }) {
|
|
|
7185
7373
|
if (!draft || !layersQ.data) return;
|
|
7186
7374
|
setStatus("Saving\u2026");
|
|
7187
7375
|
try {
|
|
7188
|
-
const
|
|
7189
|
-
const res = await api.settings.setSecurityLayers(next);
|
|
7376
|
+
const res = await api.settings.setSecurityLayers({ ...layersQ.data.layers, rateLimit: draft });
|
|
7190
7377
|
setDraft({ ...res.layers.rateLimit });
|
|
7191
7378
|
setDirty(false);
|
|
7192
7379
|
setStatus("\u2713 Saved");
|
|
@@ -7198,8 +7385,8 @@ function RateLimitPanel({ focused }) {
|
|
|
7198
7385
|
useInput2(
|
|
7199
7386
|
(input, key) => {
|
|
7200
7387
|
const step = key.shift ? 10 : 1;
|
|
7201
|
-
if (key.upArrow) setFi((n) => (n - 1 +
|
|
7202
|
-
else if (key.downArrow) setFi((n) => (n + 1) %
|
|
7388
|
+
if (key.upArrow) setFi((n) => (n - 1 + FIELDS2.length) % FIELDS2.length);
|
|
7389
|
+
else if (key.downArrow) setFi((n) => (n + 1) % FIELDS2.length);
|
|
7203
7390
|
else if (key.leftArrow) adjust(-1, step);
|
|
7204
7391
|
else if (key.rightArrow) adjust(1, step);
|
|
7205
7392
|
else if (input === "s") void save();
|
|
@@ -7207,77 +7394,72 @@ function RateLimitPanel({ focused }) {
|
|
|
7207
7394
|
{ isActive: focused }
|
|
7208
7395
|
);
|
|
7209
7396
|
const history = (historyQ.data?.history ?? []).slice().sort((a, b) => b.ts - a.ts);
|
|
7210
|
-
const
|
|
7211
|
-
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
/* @__PURE__ */
|
|
7397
|
+
const activity = insightsQ.data?.["activity"] ?? {};
|
|
7398
|
+
const minuteSeries = (activity.minute ?? []).map((b) => b.count);
|
|
7399
|
+
const hourSeries = (activity.hour ?? []).map((b) => b.count);
|
|
7400
|
+
const bursts = minuteSeries.filter((c2) => draft && draft.perMinute > 0 && c2 > draft.perMinute).length;
|
|
7401
|
+
return /* @__PURE__ */ jsx4(DataView, { loading: layersQ.loading && !draft, error: layersQ.error, children: draft ? /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
7402
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: focused ? "\u2191\u2193 field \xB7 \u2190\u2192 \xB11 \xB7 shift+\u2190\u2192 \xB110 \xB7 s save" : "press \u2192 to edit" }),
|
|
7403
|
+
/* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
|
|
7404
|
+
/* @__PURE__ */ jsx4(FieldRow, { label: "Mode", active: focused && fi === 0, children: /* @__PURE__ */ jsx4(Text4, { color: modeColor(draft.mode), children: draft.mode }) }),
|
|
7405
|
+
/* @__PURE__ */ jsx4(FieldRow, { label: "Per minute", active: focused && fi === 1, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perMinute }) }),
|
|
7406
|
+
/* @__PURE__ */ jsx4(FieldRow, { label: "Per hour", active: focused && fi === 2, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perHour === 0 ? "off" : draft.perHour }) }),
|
|
7407
|
+
/* @__PURE__ */ jsx4(FieldRow, { label: "Per day", active: focused && fi === 3, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perDay === 0 ? "off" : draft.perDay }) })
|
|
7218
7408
|
] }),
|
|
7219
|
-
dirty || status ? /* @__PURE__ */
|
|
7220
|
-
dirty ? /* @__PURE__ */
|
|
7221
|
-
status ? /* @__PURE__ */
|
|
7409
|
+
dirty || status ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
|
|
7410
|
+
dirty ? /* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: "\u25CF unsaved \u2014 press s to apply " }) : null,
|
|
7411
|
+
status ? /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
7222
7412
|
] }) : null,
|
|
7223
|
-
/* @__PURE__ */
|
|
7224
|
-
/* @__PURE__ */
|
|
7413
|
+
/* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
|
|
7414
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
7415
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "Activity " }),
|
|
7416
|
+
bursts > 0 ? /* @__PURE__ */ jsxs4(Text4, { color: theme.bad, children: [
|
|
7417
|
+
bursts,
|
|
7418
|
+
" burst min"
|
|
7419
|
+
] }) : /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "no bursts" }),
|
|
7420
|
+
insightsQ.loading && !insightsQ.data ? /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " \u2026" }) : null
|
|
7421
|
+
] }),
|
|
7422
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
7423
|
+
/* @__PURE__ */ jsx4(Text4, { children: "/min " }),
|
|
7424
|
+
minuteSeries.length ? /* @__PURE__ */ jsx4(ColoredSpark, { values: minuteSeries, limit: draft.perMinute }) : /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "no data" })
|
|
7425
|
+
] }),
|
|
7426
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
7427
|
+
/* @__PURE__ */ jsx4(Text4, { children: "/hour " }),
|
|
7428
|
+
hourSeries.length ? /* @__PURE__ */ jsx4(ColoredSpark, { values: hourSeries, limit: draft.perHour, width: 24 }) : /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "no data" })
|
|
7429
|
+
] })
|
|
7430
|
+
] }),
|
|
7431
|
+
/* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
|
|
7432
|
+
/* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
|
|
7225
7433
|
"Change history ",
|
|
7226
7434
|
history.length ? `(${history.length})` : ""
|
|
7227
7435
|
] }),
|
|
7228
|
-
history.length ? /* @__PURE__ */
|
|
7436
|
+
history.length ? /* @__PURE__ */ jsx4(
|
|
7229
7437
|
Table,
|
|
7230
7438
|
{
|
|
7231
7439
|
columns: [
|
|
7232
|
-
{ header: "WHEN", width:
|
|
7440
|
+
{ header: "WHEN", width: 10 },
|
|
7233
7441
|
{ header: "/MIN", width: 6 },
|
|
7234
7442
|
{ header: "/HOUR", width: 7 },
|
|
7235
7443
|
{ header: "/DAY", width: 6 }
|
|
7236
7444
|
],
|
|
7237
|
-
rows: history.slice(0,
|
|
7445
|
+
rows: history.slice(0, 4).map((h, i) => [
|
|
7238
7446
|
{ value: ago(h.ts) + " ago", dim: true, bold: i === 0 },
|
|
7239
7447
|
{ value: String(h.minute), bold: i === 0 },
|
|
7240
7448
|
{ value: h.hour === 0 ? "off" : String(h.hour), dim: h.hour === 0 },
|
|
7241
7449
|
{ value: h.day === 0 ? "off" : String(h.day), dim: h.day === 0 }
|
|
7242
7450
|
])
|
|
7243
7451
|
}
|
|
7244
|
-
) : /* @__PURE__ */
|
|
7245
|
-
] }),
|
|
7246
|
-
/* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
|
|
7247
|
-
/* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
|
|
7248
|
-
"Recent bursts ",
|
|
7249
|
-
insightsQ.loading && !insightsQ.data ? "\u2026" : `(${bursts.length})`
|
|
7250
|
-
] }),
|
|
7251
|
-
bursts.length ? /* @__PURE__ */ jsx3(
|
|
7252
|
-
Table,
|
|
7253
|
-
{
|
|
7254
|
-
columns: [
|
|
7255
|
-
{ header: "RESULT", width: 9 },
|
|
7256
|
-
{ header: "AGENT", width: 16 },
|
|
7257
|
-
{ header: "CALLS/LIMIT", width: 12 },
|
|
7258
|
-
{ header: "WHEN", width: 8 }
|
|
7259
|
-
],
|
|
7260
|
-
rows: bursts.map((b) => [
|
|
7261
|
-
{ value: b.blocked ? "blocked" : "BYPASSED", color: b.blocked ? theme.accent : theme.bad, bold: !b.blocked },
|
|
7262
|
-
{ value: (b.agent || "\u2014").slice(0, 16), dim: true },
|
|
7263
|
-
{ value: `${b.count}/${b.limit}` },
|
|
7264
|
-
{ value: ago(b.minute) + " ago", dim: true }
|
|
7265
|
-
])
|
|
7266
|
-
}
|
|
7267
|
-
) : /* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
|
|
7268
|
-
" ",
|
|
7269
|
-
insightsQ.error ? insightsQ.error : "none in 7d"
|
|
7270
|
-
] })
|
|
7452
|
+
) : /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " no changes" })
|
|
7271
7453
|
] })
|
|
7272
7454
|
] }) : null });
|
|
7273
7455
|
}
|
|
7274
7456
|
function FieldRow({ label, active: active2, children }) {
|
|
7275
|
-
return /* @__PURE__ */
|
|
7276
|
-
/* @__PURE__ */
|
|
7457
|
+
return /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
7458
|
+
/* @__PURE__ */ jsx4(Text4, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + label.padEnd(12) }),
|
|
7277
7459
|
children
|
|
7278
7460
|
] });
|
|
7279
7461
|
}
|
|
7280
|
-
var MODES,
|
|
7462
|
+
var MODES, FIELDS2, BLOCKS2;
|
|
7281
7463
|
var init_RateLimit = __esm({
|
|
7282
7464
|
"src/tui/panels/RateLimit.tsx"() {
|
|
7283
7465
|
"use strict";
|
|
@@ -7286,81 +7468,138 @@ var init_RateLimit = __esm({
|
|
|
7286
7468
|
init_hooks();
|
|
7287
7469
|
init_theme();
|
|
7288
7470
|
MODES = ["off", "detect", "block"];
|
|
7289
|
-
|
|
7471
|
+
FIELDS2 = ["mode", "perMinute", "perHour", "perDay"];
|
|
7472
|
+
BLOCKS2 = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
|
|
7290
7473
|
}
|
|
7291
7474
|
});
|
|
7292
7475
|
|
|
7293
7476
|
// src/tui/panels/Dlp.tsx
|
|
7294
|
-
import { Box as
|
|
7477
|
+
import { Box as Box5, Text as Text5, useInput as useInput3 } from "ink";
|
|
7478
|
+
import TextInput2 from "ink-text-input";
|
|
7295
7479
|
import { useEffect as useEffect4, useState as useState4 } from "react";
|
|
7296
|
-
import { jsx as
|
|
7480
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
7297
7481
|
function DlpPanel({ focused }) {
|
|
7298
7482
|
const q = useLoader(() => api.settings.getSecurityLayers());
|
|
7299
7483
|
const [dlp, setDlp] = useState4(null);
|
|
7300
7484
|
const [available, setAvailable] = useState4([]);
|
|
7301
7485
|
const [sel, setSel] = useState4(0);
|
|
7486
|
+
const [dirty, setDirty] = useState4(false);
|
|
7302
7487
|
const [status, setStatus] = useState4(null);
|
|
7488
|
+
const [adding, setAdding] = useState4(null);
|
|
7489
|
+
const [newName, setNewName] = useState4("");
|
|
7490
|
+
const [newRe, setNewRe] = useState4("");
|
|
7303
7491
|
useEffect4(() => {
|
|
7304
7492
|
if (q.data && !dlp) {
|
|
7305
|
-
setDlp({ ...q.data.layers.dlp, patterns: [...q.data.layers.dlp.patterns] });
|
|
7493
|
+
setDlp({ ...q.data.layers.dlp, patterns: [...q.data.layers.dlp.patterns], custom: [...q.data.layers.dlp.custom] });
|
|
7306
7494
|
setAvailable(q.data.availablePatterns);
|
|
7307
7495
|
}
|
|
7308
7496
|
}, [q.data, dlp]);
|
|
7309
|
-
const
|
|
7310
|
-
|
|
7311
|
-
|
|
7497
|
+
const mutate = (next) => {
|
|
7498
|
+
setDlp(next);
|
|
7499
|
+
setDirty(true);
|
|
7500
|
+
setStatus(null);
|
|
7501
|
+
};
|
|
7502
|
+
const save = async () => {
|
|
7503
|
+
if (!dlp || !q.data) return;
|
|
7312
7504
|
setStatus("Saving\u2026");
|
|
7313
7505
|
try {
|
|
7314
|
-
const res = await api.settings.setSecurityLayers({ ...q.data.layers, dlp
|
|
7315
|
-
setDlp({ ...res.layers.dlp, patterns: [...res.layers.dlp.patterns] });
|
|
7506
|
+
const res = await api.settings.setSecurityLayers({ ...q.data.layers, dlp });
|
|
7507
|
+
setDlp({ ...res.layers.dlp, patterns: [...res.layers.dlp.patterns], custom: [...res.layers.dlp.custom] });
|
|
7508
|
+
setDirty(false);
|
|
7316
7509
|
setStatus("\u2713 Saved");
|
|
7317
7510
|
} catch (e) {
|
|
7318
7511
|
setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
|
|
7319
7512
|
}
|
|
7320
7513
|
};
|
|
7514
|
+
const discard = () => {
|
|
7515
|
+
if (q.data) setDlp({ ...q.data.layers.dlp, patterns: [...q.data.layers.dlp.patterns], custom: [...q.data.layers.dlp.custom] });
|
|
7516
|
+
setDirty(false);
|
|
7517
|
+
setStatus("discarded");
|
|
7518
|
+
};
|
|
7519
|
+
const customCount = dlp?.custom.length ?? 0;
|
|
7520
|
+
const total = available.length + customCount;
|
|
7321
7521
|
useInput3(
|
|
7322
7522
|
(input, key) => {
|
|
7323
7523
|
if (!dlp) return;
|
|
7324
7524
|
if (key.upArrow) setSel((n) => Math.max(0, n - 1));
|
|
7325
|
-
else if (key.downArrow) setSel((n) => Math.min(
|
|
7326
|
-
else if (input === "
|
|
7525
|
+
else if (key.downArrow) setSel((n) => Math.min(total - 1, n + 1));
|
|
7526
|
+
else if (input === "m") {
|
|
7527
|
+
const idx = (MODES2.indexOf(dlp.mode) + 1) % MODES2.length;
|
|
7528
|
+
mutate({ ...dlp, mode: MODES2[idx] });
|
|
7529
|
+
} else if (input === "a") {
|
|
7530
|
+
setNewName("");
|
|
7531
|
+
setNewRe("");
|
|
7532
|
+
setAdding("name");
|
|
7533
|
+
} else if (input === " " && sel < available.length) {
|
|
7327
7534
|
const p = available[sel];
|
|
7328
|
-
if (!p) return;
|
|
7329
7535
|
const set = new Set(dlp.patterns);
|
|
7330
7536
|
if (set.has(p)) set.delete(p);
|
|
7331
7537
|
else set.add(p);
|
|
7332
|
-
|
|
7333
|
-
} else if (input === "
|
|
7334
|
-
const
|
|
7335
|
-
|
|
7336
|
-
|
|
7538
|
+
mutate({ ...dlp, patterns: [...set] });
|
|
7539
|
+
} else if (input === "d" && sel >= available.length) {
|
|
7540
|
+
const ci = sel - available.length;
|
|
7541
|
+
mutate({ ...dlp, custom: dlp.custom.filter((_, i) => i !== ci) });
|
|
7542
|
+
setSel((n) => Math.max(0, n - 1));
|
|
7543
|
+
} else if (input === "s") void save();
|
|
7544
|
+
else if (input === "x") discard();
|
|
7337
7545
|
},
|
|
7338
|
-
{ isActive: focused }
|
|
7546
|
+
{ isActive: focused && !adding }
|
|
7339
7547
|
);
|
|
7340
7548
|
const enabled = new Set(dlp?.patterns ?? []);
|
|
7341
|
-
return /* @__PURE__ */
|
|
7342
|
-
/* @__PURE__ */
|
|
7343
|
-
/* @__PURE__ */
|
|
7344
|
-
/* @__PURE__ */
|
|
7345
|
-
/* @__PURE__ */
|
|
7549
|
+
return /* @__PURE__ */ jsx5(DataView, { loading: q.loading && !dlp, error: q.error, children: dlp ? /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
7550
|
+
/* @__PURE__ */ jsxs5(Box5, { children: [
|
|
7551
|
+
/* @__PURE__ */ jsx5(Text5, { children: "mode: " }),
|
|
7552
|
+
/* @__PURE__ */ jsx5(Text5, { color: modeColor(dlp.mode), children: dlp.mode }),
|
|
7553
|
+
dirty ? /* @__PURE__ */ jsx5(Text5, { color: theme.warn, children: " \u25CF unsaved (s save \xB7 x discard)" }) : null
|
|
7346
7554
|
] }),
|
|
7347
|
-
/* @__PURE__ */
|
|
7555
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: focused ? "\u2191\u2193 move \xB7 space toggle \xB7 m mode \xB7 a add \xB7 d remove \xB7 s save" : "press \u2192 to edit" }),
|
|
7556
|
+
/* @__PURE__ */ jsx5(Box5, { marginTop: 1, flexDirection: "column", children: available.map((p, i) => {
|
|
7348
7557
|
const on = enabled.has(p);
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
7558
|
+
const active2 = focused && sel === i;
|
|
7559
|
+
return /* @__PURE__ */ jsxs5(Text5, { color: active2 ? theme.accentBright : void 0, bold: active2, children: [
|
|
7560
|
+
(active2 ? "\u25B8 " : " ") + (on ? "\u25CF " : "\u25CB "),
|
|
7561
|
+
/* @__PURE__ */ jsx5(Text5, { color: on ? theme.ok : theme.dim, children: p })
|
|
7352
7562
|
] }, p);
|
|
7353
7563
|
}) }),
|
|
7354
|
-
|
|
7355
|
-
/* @__PURE__ */
|
|
7356
|
-
dlp.custom.
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7564
|
+
/* @__PURE__ */ jsxs5(Box5, { marginTop: 1, flexDirection: "column", children: [
|
|
7565
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: "Custom patterns" }),
|
|
7566
|
+
dlp.custom.length === 0 ? /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " (none \u2014 press a to add)" }) : null,
|
|
7567
|
+
dlp.custom.map((c2, i) => {
|
|
7568
|
+
const active2 = focused && sel === available.length + i;
|
|
7569
|
+
return /* @__PURE__ */ jsxs5(Text5, { color: active2 ? theme.accentBright : void 0, bold: active2, children: [
|
|
7570
|
+
active2 ? "\u25B8 " : " ",
|
|
7571
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.accent, children: c2.name }),
|
|
7572
|
+
" ",
|
|
7573
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: c2.re })
|
|
7574
|
+
] }, c2.name + i);
|
|
7575
|
+
})
|
|
7576
|
+
] }),
|
|
7577
|
+
adding ? /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, children: [
|
|
7578
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.warn, children: adding === "name" ? "New pattern name: " : `Regex for "${newName}": ` }),
|
|
7579
|
+
adding === "name" ? /* @__PURE__ */ jsx5(
|
|
7580
|
+
TextInput2,
|
|
7581
|
+
{
|
|
7582
|
+
value: newName,
|
|
7583
|
+
onChange: setNewName,
|
|
7584
|
+
onSubmit: (v) => {
|
|
7585
|
+
if (!v.trim()) return setAdding(null);
|
|
7586
|
+
setNewName(v.trim());
|
|
7587
|
+
setAdding("re");
|
|
7588
|
+
}
|
|
7589
|
+
}
|
|
7590
|
+
) : /* @__PURE__ */ jsx5(
|
|
7591
|
+
TextInput2,
|
|
7592
|
+
{
|
|
7593
|
+
value: newRe,
|
|
7594
|
+
onChange: setNewRe,
|
|
7595
|
+
onSubmit: (v) => {
|
|
7596
|
+
if (v.trim() && dlp) mutate({ ...dlp, custom: [...dlp.custom, { name: newName, re: v.trim() }] });
|
|
7597
|
+
setAdding(null);
|
|
7598
|
+
}
|
|
7599
|
+
}
|
|
7600
|
+
)
|
|
7362
7601
|
] }) : null,
|
|
7363
|
-
status ? /* @__PURE__ */
|
|
7602
|
+
status ? /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) }) : null
|
|
7364
7603
|
] }) : null });
|
|
7365
7604
|
}
|
|
7366
7605
|
var MODES2;
|
|
@@ -7376,8 +7615,8 @@ var init_Dlp = __esm({
|
|
|
7376
7615
|
});
|
|
7377
7616
|
|
|
7378
7617
|
// src/tui/panels/Stats.tsx
|
|
7379
|
-
import { Box as
|
|
7380
|
-
import { jsx as
|
|
7618
|
+
import { Box as Box6, Text as Text6 } from "ink";
|
|
7619
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
7381
7620
|
function StatsPanel({ active: active2 }) {
|
|
7382
7621
|
const stats = useLoader(() => api.stats.get());
|
|
7383
7622
|
const ts = useLoader(() => api.stats.timeseries({ period: "24h" }));
|
|
@@ -7387,50 +7626,50 @@ function StatsPanel({ active: active2 }) {
|
|
|
7387
7626
|
}, 5e3, active2);
|
|
7388
7627
|
const s = stats.data;
|
|
7389
7628
|
const points = ts.data?.timeseries ?? [];
|
|
7390
|
-
return /* @__PURE__ */
|
|
7391
|
-
/* @__PURE__ */
|
|
7392
|
-
/* @__PURE__ */
|
|
7629
|
+
return /* @__PURE__ */ jsx6(DataView, { loading: stats.loading && !s, error: stats.error, children: s ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
7630
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
7631
|
+
/* @__PURE__ */ jsxs6(Text6, { bold: true, children: [
|
|
7393
7632
|
s.total_calls,
|
|
7394
7633
|
" "
|
|
7395
7634
|
] }),
|
|
7396
|
-
/* @__PURE__ */
|
|
7397
|
-
/* @__PURE__ */
|
|
7635
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "calls " }),
|
|
7636
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.ok, children: [
|
|
7398
7637
|
s.allowed,
|
|
7399
7638
|
" allowed"
|
|
7400
7639
|
] }),
|
|
7401
|
-
/* @__PURE__ */
|
|
7402
|
-
/* @__PURE__ */
|
|
7640
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " " }),
|
|
7641
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.bad, children: [
|
|
7403
7642
|
s.denied,
|
|
7404
7643
|
" denied"
|
|
7405
7644
|
] })
|
|
7406
7645
|
] }),
|
|
7407
|
-
/* @__PURE__ */
|
|
7646
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
7408
7647
|
s.active_policies,
|
|
7409
7648
|
" policies \xB7 ",
|
|
7410
7649
|
s.registered_tools,
|
|
7411
7650
|
" tools"
|
|
7412
7651
|
] }),
|
|
7413
|
-
/* @__PURE__ */
|
|
7414
|
-
/* @__PURE__ */
|
|
7652
|
+
/* @__PURE__ */ jsxs6(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
7653
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
7415
7654
|
"Last 24h ",
|
|
7416
7655
|
ts.data ? `(per ${ts.data.granularity})` : ""
|
|
7417
7656
|
] }),
|
|
7418
|
-
/* @__PURE__ */
|
|
7419
|
-
/* @__PURE__ */
|
|
7420
|
-
/* @__PURE__ */
|
|
7657
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
7658
|
+
/* @__PURE__ */ jsx6(Text6, { children: "total " }),
|
|
7659
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.accent, children: sparkline(points.map((p) => p.total), 64) })
|
|
7421
7660
|
] }),
|
|
7422
|
-
/* @__PURE__ */
|
|
7423
|
-
/* @__PURE__ */
|
|
7424
|
-
/* @__PURE__ */
|
|
7661
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
7662
|
+
/* @__PURE__ */ jsx6(Text6, { children: "allowed " }),
|
|
7663
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.ok, children: sparkline(points.map((p) => p.allowed), 64) })
|
|
7425
7664
|
] }),
|
|
7426
|
-
/* @__PURE__ */
|
|
7427
|
-
/* @__PURE__ */
|
|
7428
|
-
/* @__PURE__ */
|
|
7665
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
7666
|
+
/* @__PURE__ */ jsx6(Text6, { children: "denied " }),
|
|
7667
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.bad, children: sparkline(points.map((p) => p.denied), 64) })
|
|
7429
7668
|
] })
|
|
7430
7669
|
] }),
|
|
7431
|
-
/* @__PURE__ */
|
|
7432
|
-
/* @__PURE__ */
|
|
7433
|
-
/* @__PURE__ */
|
|
7670
|
+
/* @__PURE__ */ jsxs6(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
7671
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "Recent" }),
|
|
7672
|
+
/* @__PURE__ */ jsx6(
|
|
7434
7673
|
Table,
|
|
7435
7674
|
{
|
|
7436
7675
|
columns: [
|
|
@@ -7461,52 +7700,172 @@ var init_Stats = __esm({
|
|
|
7461
7700
|
});
|
|
7462
7701
|
|
|
7463
7702
|
// src/tui/panels/Audit.tsx
|
|
7464
|
-
import { Box as
|
|
7703
|
+
import { Box as Box7, Text as Text7, useInput as useInput4 } from "ink";
|
|
7704
|
+
import TextInput3 from "ink-text-input";
|
|
7465
7705
|
import { useState as useState5 } from "react";
|
|
7466
|
-
import { jsx as
|
|
7706
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
7467
7707
|
function AuditPanel({ active: active2, focused }) {
|
|
7468
|
-
const [
|
|
7469
|
-
const
|
|
7470
|
-
const
|
|
7471
|
-
|
|
7708
|
+
const [di, setDi] = useState5(0);
|
|
7709
|
+
const [gi, setGi] = useState5(0);
|
|
7710
|
+
const [tool, setTool] = useState5("");
|
|
7711
|
+
const [agent, setAgent] = useState5("");
|
|
7712
|
+
const [search, setSearch] = useState5("");
|
|
7713
|
+
const [sel, setSel] = useState5(0);
|
|
7714
|
+
const [view, setView] = useState5("list");
|
|
7715
|
+
const [editing, setEditing] = useState5(null);
|
|
7716
|
+
const query = {
|
|
7717
|
+
filter: DECISIONS[di],
|
|
7718
|
+
signal: SIGNALS[gi],
|
|
7719
|
+
tool: tool || void 0,
|
|
7720
|
+
agent_name: agent || void 0,
|
|
7721
|
+
search: search || void 0,
|
|
7722
|
+
limit: 100
|
|
7723
|
+
};
|
|
7724
|
+
const auditQ = useLoader(() => api.audit.list(query), [di, gi, tool, agent, search]);
|
|
7725
|
+
usePoll(auditQ.reload, 6e3, active2 && view === "list" && !editing);
|
|
7726
|
+
const entries = auditQ.data?.entries ?? [];
|
|
7727
|
+
const current = entries[Math.min(sel, Math.max(0, entries.length - 1))];
|
|
7728
|
+
const sessionQ = useLoader(
|
|
7729
|
+
() => view === "detail" && current?.session_id ? api.audit.list({ session_id: current.session_id, limit: 40 }) : Promise.resolve(null),
|
|
7730
|
+
[view, current?.session_id]
|
|
7731
|
+
);
|
|
7472
7732
|
useInput4(
|
|
7473
|
-
(input) => {
|
|
7474
|
-
if (
|
|
7733
|
+
(input, key) => {
|
|
7734
|
+
if (view === "detail") {
|
|
7735
|
+
if (key.leftArrow || key.escape) setView("list");
|
|
7736
|
+
return;
|
|
7737
|
+
}
|
|
7738
|
+
if (key.upArrow) setSel((n) => Math.max(0, n - 1));
|
|
7739
|
+
else if (key.downArrow) setSel((n) => Math.min(entries.length - 1, n + 1));
|
|
7740
|
+
else if (key.return || key.rightArrow) {
|
|
7741
|
+
if (current) setView("detail");
|
|
7742
|
+
} else if (input === "f") setDi((n) => (n + 1) % DECISIONS.length);
|
|
7743
|
+
else if (input === "g") setGi((n) => (n + 1) % SIGNALS.length);
|
|
7744
|
+
else if (input === "t") setEditing("tool");
|
|
7745
|
+
else if (input === "n") setEditing("agent");
|
|
7746
|
+
else if (input === "/") setEditing("search");
|
|
7747
|
+
else if (input === "c") {
|
|
7748
|
+
setDi(0);
|
|
7749
|
+
setGi(0);
|
|
7750
|
+
setTool("");
|
|
7751
|
+
setAgent("");
|
|
7752
|
+
setSearch("");
|
|
7753
|
+
}
|
|
7475
7754
|
},
|
|
7476
|
-
{ isActive: focused }
|
|
7755
|
+
{ isActive: focused && !editing }
|
|
7477
7756
|
);
|
|
7478
|
-
|
|
7479
|
-
|
|
7480
|
-
/* @__PURE__ */
|
|
7481
|
-
|
|
7482
|
-
|
|
7483
|
-
|
|
7484
|
-
|
|
7757
|
+
if (view === "detail" && current) {
|
|
7758
|
+
const sessionCalls = sessionQ.data?.entries ?? [];
|
|
7759
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
7760
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
7761
|
+
/* @__PURE__ */ jsx7(Text7, { color: decisionColor(current.decision), bold: true, children: current.decision }),
|
|
7762
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: " " + current.tool_name }),
|
|
7763
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " " + current.permission + " \xB7 " + current.trust_level })
|
|
7764
|
+
] }),
|
|
7765
|
+
/* @__PURE__ */ jsx7(Detail, { label: "reason", value: current.reason ?? "\u2014" }),
|
|
7766
|
+
/* @__PURE__ */ jsx7(Detail, { label: "rule", value: current.matched_rule_id ?? "\u2014" }),
|
|
7767
|
+
/* @__PURE__ */ jsx7(Detail, { label: "agent", value: current.agent_name ?? "\u2014" }),
|
|
7768
|
+
/* @__PURE__ */ jsx7(Detail, { label: "session", value: current.session_id ?? "\u2014" }),
|
|
7769
|
+
/* @__PURE__ */ jsx7(Detail, { label: "dlp", value: current.dlp_matches?.length ? current.dlp_matches.join(", ") : "none", color: current.dlp_matches?.length ? theme.bad : void 0 }),
|
|
7770
|
+
/* @__PURE__ */ jsx7(Detail, { label: "when", value: new Date(current.created_at).toLocaleString() }),
|
|
7771
|
+
current.arguments_summary ? /* @__PURE__ */ jsx7(Detail, { label: "args", value: truncate2(JSON.stringify(current.arguments_summary), 60) }) : null,
|
|
7772
|
+
/* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
7773
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
7774
|
+
"Session ",
|
|
7775
|
+
current.session_id ? `\xB7 ${sessionCalls.length} calls` : "(no session id)"
|
|
7776
|
+
] }),
|
|
7777
|
+
current.session_id ? /* @__PURE__ */ jsx7(
|
|
7778
|
+
Table,
|
|
7779
|
+
{
|
|
7780
|
+
columns: [
|
|
7781
|
+
{ header: "DECISION", width: 9 },
|
|
7782
|
+
{ header: "TOOL", width: 20 },
|
|
7783
|
+
{ header: "REASON", width: 26 },
|
|
7784
|
+
{ header: "WHEN", width: 6 }
|
|
7785
|
+
],
|
|
7786
|
+
rows: sessionCalls.slice(0, 12).map((e) => [
|
|
7787
|
+
{ value: e.decision, color: decisionColor(e.decision) },
|
|
7788
|
+
{ value: truncate2(e.tool_name, 20), color: theme.accent },
|
|
7789
|
+
{ value: truncate2(e.reason ?? "\u2014", 26) },
|
|
7790
|
+
{ value: ago(e.created_at), dim: true }
|
|
7791
|
+
])
|
|
7792
|
+
}
|
|
7793
|
+
) : null
|
|
7794
|
+
] }),
|
|
7795
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2190 back to list" })
|
|
7796
|
+
] });
|
|
7797
|
+
}
|
|
7798
|
+
const chip = (label, val, on) => /* @__PURE__ */ jsxs7(Text7, { children: [
|
|
7799
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
7800
|
+
label,
|
|
7801
|
+
":"
|
|
7485
7802
|
] }),
|
|
7486
|
-
/* @__PURE__ */
|
|
7803
|
+
/* @__PURE__ */ jsx7(Text7, { color: on ? theme.accentBright : theme.dim, children: val }),
|
|
7804
|
+
/* @__PURE__ */ jsx7(Text7, { children: " " })
|
|
7805
|
+
] });
|
|
7806
|
+
return /* @__PURE__ */ jsx7(DataView, { loading: auditQ.loading && !auditQ.data, error: auditQ.error, children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
7807
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
7808
|
+
chip("dec", DECISIONS[di] ?? "all", di !== 0),
|
|
7809
|
+
chip("sig", SIGNALS[gi] ?? "all", gi !== 0),
|
|
7810
|
+
chip("tool", tool || "\xB7", !!tool),
|
|
7811
|
+
chip("agent", agent || "\xB7", !!agent),
|
|
7812
|
+
chip("search", search || "\xB7", !!search)
|
|
7813
|
+
] }),
|
|
7814
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: focused ? "\u2191\u2193 \xB7 enter detail \xB7 f dec \xB7 g sig \xB7 t tool \xB7 n agent \xB7 / search \xB7 c clear" : "press \u2192 to browse" }),
|
|
7815
|
+
editing ? /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, children: [
|
|
7816
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.warn, children: [
|
|
7817
|
+
editing,
|
|
7818
|
+
": "
|
|
7819
|
+
] }),
|
|
7820
|
+
/* @__PURE__ */ jsx7(
|
|
7821
|
+
TextInput3,
|
|
7822
|
+
{
|
|
7823
|
+
value: editing === "tool" ? tool : editing === "agent" ? agent : search,
|
|
7824
|
+
onChange: editing === "tool" ? setTool : editing === "agent" ? setAgent : setSearch,
|
|
7825
|
+
onSubmit: () => setEditing(null)
|
|
7826
|
+
}
|
|
7827
|
+
)
|
|
7828
|
+
] }) : null,
|
|
7829
|
+
/* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
7830
|
+
auditQ.data?.total ?? 0,
|
|
7831
|
+
" matched \xB7 showing ",
|
|
7832
|
+
Math.min(entries.length, 12)
|
|
7833
|
+
] }) }),
|
|
7834
|
+
/* @__PURE__ */ jsx7(
|
|
7487
7835
|
Table,
|
|
7488
7836
|
{
|
|
7489
7837
|
columns: [
|
|
7838
|
+
{ header: "", width: 2 },
|
|
7490
7839
|
{ header: "DECISION", width: 9 },
|
|
7491
7840
|
{ header: "TOOL", width: 18 },
|
|
7492
7841
|
{ header: "AGENT", width: 14 },
|
|
7493
|
-
{ header: "REASON", width:
|
|
7842
|
+
{ header: "REASON", width: 24 },
|
|
7494
7843
|
{ header: "DLP", width: 4 },
|
|
7495
7844
|
{ header: "WHEN", width: 6 }
|
|
7496
7845
|
],
|
|
7497
|
-
rows: entries.map((e) =>
|
|
7498
|
-
{ value: e.decision, color: decisionColor(e.decision) },
|
|
7499
|
-
{ value: truncate2(e.tool_name, 18), color: theme.accent },
|
|
7500
|
-
{ value: truncate2(e.agent_name ?? "\u2014", 14), dim: true },
|
|
7501
|
-
{ value: truncate2(e.reason ?? "\u2014", 26) },
|
|
7502
|
-
{ value: e.dlp_matches?.length ? String(e.dlp_matches.length) : "0", color: e.dlp_matches?.length ? theme.bad : void 0, dim: !e.dlp_matches?.length },
|
|
7503
|
-
{ value: ago(e.created_at), dim: true }
|
|
7504
|
-
])
|
|
7846
|
+
rows: entries.slice(0, 12).map((e, i) => rowFor(e, i === sel && focused))
|
|
7505
7847
|
}
|
|
7506
|
-
)
|
|
7848
|
+
)
|
|
7507
7849
|
] }) });
|
|
7508
7850
|
}
|
|
7509
|
-
|
|
7851
|
+
function rowFor(e, active2) {
|
|
7852
|
+
return [
|
|
7853
|
+
{ value: active2 ? "\u25B8" : "", color: theme.accentBright },
|
|
7854
|
+
{ value: e.decision, color: decisionColor(e.decision) },
|
|
7855
|
+
{ value: truncate2(e.tool_name, 18), color: theme.accent },
|
|
7856
|
+
{ value: truncate2(e.agent_name ?? "\u2014", 14), dim: true },
|
|
7857
|
+
{ value: truncate2(e.reason ?? "\u2014", 24) },
|
|
7858
|
+
{ value: e.dlp_matches?.length ? String(e.dlp_matches.length) : "0", color: e.dlp_matches?.length ? theme.bad : void 0, dim: !e.dlp_matches?.length },
|
|
7859
|
+
{ value: ago(e.created_at), dim: true }
|
|
7860
|
+
];
|
|
7861
|
+
}
|
|
7862
|
+
function Detail({ label, value, color }) {
|
|
7863
|
+
return /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
7864
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: label.padEnd(9) }),
|
|
7865
|
+
/* @__PURE__ */ jsx7(Text7, { color, children: value })
|
|
7866
|
+
] });
|
|
7867
|
+
}
|
|
7868
|
+
var DECISIONS, SIGNALS;
|
|
7510
7869
|
var init_Audit = __esm({
|
|
7511
7870
|
"src/tui/panels/Audit.tsx"() {
|
|
7512
7871
|
"use strict";
|
|
@@ -7514,65 +7873,8 @@ var init_Audit = __esm({
|
|
|
7514
7873
|
init_components();
|
|
7515
7874
|
init_hooks();
|
|
7516
7875
|
init_theme();
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
});
|
|
7520
|
-
|
|
7521
|
-
// src/tui/panels/Agents.tsx
|
|
7522
|
-
import { Box as Box7, Text as Text7 } from "ink";
|
|
7523
|
-
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
7524
|
-
function AgentsPanel({ active: active2 }) {
|
|
7525
|
-
const agents = useLoader(() => api.agents.live({ limit: 14 }));
|
|
7526
|
-
usePoll(agents.reload, 4e3, active2);
|
|
7527
|
-
const data = agents.data;
|
|
7528
|
-
const rows = data?.agents ?? [];
|
|
7529
|
-
return /* @__PURE__ */ jsx7(DataView, { loading: agents.loading && !data, error: agents.error, empty: !!data && rows.length === 0, emptyText: "No agent sessions.", children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
7530
|
-
data ? /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
7531
|
-
/* @__PURE__ */ jsxs7(Text7, { color: theme.ok, children: [
|
|
7532
|
-
data.counts.active,
|
|
7533
|
-
" active"
|
|
7534
|
-
] }),
|
|
7535
|
-
" \xB7 ",
|
|
7536
|
-
/* @__PURE__ */ jsxs7(Text7, { color: theme.warn, children: [
|
|
7537
|
-
data.counts.idle,
|
|
7538
|
-
" idle"
|
|
7539
|
-
] }),
|
|
7540
|
-
" \xB7 ",
|
|
7541
|
-
data.counts.deactivated,
|
|
7542
|
-
" off"
|
|
7543
|
-
] }) : null,
|
|
7544
|
-
/* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(
|
|
7545
|
-
Table,
|
|
7546
|
-
{
|
|
7547
|
-
columns: [
|
|
7548
|
-
{ header: "STATUS", width: 7 },
|
|
7549
|
-
{ header: "AGENT", width: 20 },
|
|
7550
|
-
{ header: "CALLS", width: 6 },
|
|
7551
|
-
{ header: "DENY", width: 5 },
|
|
7552
|
-
{ header: "DLP", width: 4 },
|
|
7553
|
-
{ header: "TRUST", width: 6 },
|
|
7554
|
-
{ header: "CHARACTER", width: 20 }
|
|
7555
|
-
],
|
|
7556
|
-
rows: rows.map((a) => [
|
|
7557
|
-
{ value: a.status, color: modeColor(a.status) },
|
|
7558
|
-
{ value: truncate2(a.agent_name ?? a.agent_id ?? a.session_id, 20), color: theme.accent },
|
|
7559
|
-
{ value: String(a.total_calls) },
|
|
7560
|
-
{ value: String(a.denied_calls), color: a.denied_calls ? theme.bad : void 0, dim: !a.denied_calls },
|
|
7561
|
-
{ value: String(a.dlp_events), color: a.dlp_events ? theme.bad : void 0, dim: !a.dlp_events },
|
|
7562
|
-
{ value: `${a.trust_score}` },
|
|
7563
|
-
{ value: truncate2(a.character || "\u2014", 20), dim: true }
|
|
7564
|
-
])
|
|
7565
|
-
}
|
|
7566
|
-
) })
|
|
7567
|
-
] }) });
|
|
7568
|
-
}
|
|
7569
|
-
var init_Agents = __esm({
|
|
7570
|
-
"src/tui/panels/Agents.tsx"() {
|
|
7571
|
-
"use strict";
|
|
7572
|
-
init_api_client();
|
|
7573
|
-
init_components();
|
|
7574
|
-
init_hooks();
|
|
7575
|
-
init_theme();
|
|
7876
|
+
DECISIONS = [void 0, "DENY", "ALLOW"];
|
|
7877
|
+
SIGNALS = [void 0, "dlp", "ratelimit"];
|
|
7576
7878
|
}
|
|
7577
7879
|
});
|
|
7578
7880
|
|
|
@@ -7625,19 +7927,19 @@ var init_App = __esm({
|
|
|
7625
7927
|
init_cli_utils();
|
|
7626
7928
|
init_components();
|
|
7627
7929
|
init_theme();
|
|
7930
|
+
init_Live();
|
|
7628
7931
|
init_Policies();
|
|
7629
7932
|
init_RateLimit();
|
|
7630
7933
|
init_Dlp();
|
|
7631
7934
|
init_Stats();
|
|
7632
7935
|
init_Audit();
|
|
7633
|
-
init_Agents();
|
|
7634
7936
|
SECTIONS = [
|
|
7937
|
+
{ label: "Live", Panel: LivePanel },
|
|
7635
7938
|
{ label: "Policies", Panel: PoliciesPanel },
|
|
7636
7939
|
{ label: "Rate Limit", Panel: RateLimitPanel },
|
|
7637
7940
|
{ label: "DLP", Panel: DlpPanel },
|
|
7638
7941
|
{ label: "Stats", Panel: StatsPanel },
|
|
7639
|
-
{ label: "Audit", Panel: AuditPanel }
|
|
7640
|
-
{ label: "Agents", Panel: AgentsPanel }
|
|
7942
|
+
{ label: "Audit", Panel: AuditPanel }
|
|
7641
7943
|
];
|
|
7642
7944
|
BANNER_HEX = ["#1432A0", "#2850BE", "#3C6ED7", "#5A8CE6", "#82AAF0", "#AAC8FA"];
|
|
7643
7945
|
}
|
|
@@ -7702,10 +8004,10 @@ function truncate3(s, n) {
|
|
|
7702
8004
|
function sparkline2(values) {
|
|
7703
8005
|
if (values.length === 0) return "";
|
|
7704
8006
|
const max = Math.max(...values, 0);
|
|
7705
|
-
if (max === 0) return
|
|
7706
|
-
return values.map((v) =>
|
|
8007
|
+
if (max === 0) return BLOCKS3[0].repeat(values.length);
|
|
8008
|
+
return values.map((v) => BLOCKS3[Math.min(BLOCKS3.length - 1, Math.round(v / max * (BLOCKS3.length - 1)))]).join("");
|
|
7707
8009
|
}
|
|
7708
|
-
var out, err, dim, bold, green, red, yellow, cyan, ANSI, width,
|
|
8010
|
+
var out, err, dim, bold, green, red, yellow, cyan, ANSI, width, BLOCKS3;
|
|
7709
8011
|
var init_format = __esm({
|
|
7710
8012
|
"src/commands/format.ts"() {
|
|
7711
8013
|
"use strict";
|
|
@@ -7720,7 +8022,7 @@ var init_format = __esm({
|
|
|
7720
8022
|
cyan = (s) => `${c.cyan}${s}${c.reset}`;
|
|
7721
8023
|
ANSI = /\x1b\[[0-9;]*m/g;
|
|
7722
8024
|
width = (s) => s.replace(ANSI, "").length;
|
|
7723
|
-
|
|
8025
|
+
BLOCKS3 = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
|
|
7724
8026
|
}
|
|
7725
8027
|
});
|
|
7726
8028
|
|