@solongate/proxy 0.60.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 +643 -223
- package/dist/tui/index.js +626 -207
- 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,60 +7018,290 @@ var init_hooks = __esm({
|
|
|
7018
7018
|
}
|
|
7019
7019
|
});
|
|
7020
7020
|
|
|
7021
|
-
// src/tui/panels/
|
|
7022
|
-
import { Box as Box2, Text as Text2
|
|
7023
|
-
import { useState as useState2 } from "react";
|
|
7021
|
+
// src/tui/panels/Live.tsx
|
|
7022
|
+
import { Box as Box2, Text as Text2 } from "ink";
|
|
7024
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
|
+
|
|
7091
|
+
// src/tui/panels/Policies.tsx
|
|
7092
|
+
import { Box as Box3, Text as Text3, useInput } from "ink";
|
|
7093
|
+
import TextInput from "ink-text-input";
|
|
7094
|
+
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
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
|
-
const [sel, setSel] = useState2(0);
|
|
7027
7106
|
const list4 = useLoader(() => api.policies.list());
|
|
7028
7107
|
const policies = list4.data?.policies ?? [];
|
|
7029
|
-
const
|
|
7108
|
+
const [view, setView] = useState2("list");
|
|
7109
|
+
const [pi, setPi] = useState2(0);
|
|
7110
|
+
const [ri, setRi] = useState2(0);
|
|
7111
|
+
const [fi, setFi] = useState2(0);
|
|
7112
|
+
const [rules, setRules] = useState2([]);
|
|
7113
|
+
const [mode, setMode] = useState2("denylist");
|
|
7114
|
+
const [dirty, setDirty] = useState2(false);
|
|
7115
|
+
const [editing, setEditing] = useState2(false);
|
|
7116
|
+
const [editVal, setEditVal] = useState2("");
|
|
7117
|
+
const [status, setStatus] = useState2(null);
|
|
7118
|
+
const selected = policies[Math.min(pi, Math.max(0, policies.length - 1))];
|
|
7030
7119
|
const detail = useLoader(() => selected ? api.policies.get(selected.id) : Promise.resolve(null), [selected?.id]);
|
|
7120
|
+
useEffect2(() => {
|
|
7121
|
+
if (detail.data) {
|
|
7122
|
+
setRules(detail.data.rules);
|
|
7123
|
+
setMode(detail.data.mode ?? "denylist");
|
|
7124
|
+
setDirty(false);
|
|
7125
|
+
setRi(0);
|
|
7126
|
+
}
|
|
7127
|
+
}, [detail.data]);
|
|
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;
|
|
7136
|
+
setStatus("Saving\u2026");
|
|
7137
|
+
try {
|
|
7138
|
+
const res = await api.policies.update(selected.id, {
|
|
7139
|
+
id: selected.id,
|
|
7140
|
+
name: detail.data.name,
|
|
7141
|
+
description: detail.data.description,
|
|
7142
|
+
mode,
|
|
7143
|
+
agents: detail.data.agents,
|
|
7144
|
+
rules
|
|
7145
|
+
});
|
|
7146
|
+
setRules(res.rules);
|
|
7147
|
+
setDirty(false);
|
|
7148
|
+
setStatus("\u2713 Saved v" + res._version);
|
|
7149
|
+
list4.reload();
|
|
7150
|
+
} catch (e) {
|
|
7151
|
+
setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
|
|
7152
|
+
}
|
|
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
|
+
};
|
|
7031
7162
|
useInput(
|
|
7032
|
-
(
|
|
7033
|
-
if (
|
|
7034
|
-
|
|
7163
|
+
(input, key) => {
|
|
7164
|
+
if (view === "list") {
|
|
7165
|
+
if (key.upArrow) setPi((n) => Math.max(0, n - 1));
|
|
7166
|
+
else if (key.downArrow) setPi((n) => Math.min(policies.length - 1, n + 1));
|
|
7167
|
+
else if (key.return || key.rightArrow) {
|
|
7168
|
+
setStatus(null);
|
|
7169
|
+
setView("rules");
|
|
7170
|
+
}
|
|
7171
|
+
return;
|
|
7172
|
+
}
|
|
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;
|
|
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();
|
|
7035
7215
|
},
|
|
7036
|
-
{ isActive: focused }
|
|
7216
|
+
{ isActive: focused && !editing }
|
|
7037
7217
|
);
|
|
7038
|
-
|
|
7039
|
-
/* @__PURE__ */
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
|
|
7043
|
-
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
|
|
7047
|
-
|
|
7048
|
-
|
|
7049
|
-
|
|
7050
|
-
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
|
|
7218
|
+
if (view === "list") {
|
|
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: [
|
|
7222
|
+
(i === pi ? "\u25B8 " : " ") + truncate2(p.name, 26).padEnd(27),
|
|
7223
|
+
/* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
|
|
7224
|
+
p.mode.padEnd(10),
|
|
7225
|
+
" ",
|
|
7226
|
+
p.rules.length,
|
|
7227
|
+
" rules \xB7 v",
|
|
7228
|
+
p.version
|
|
7229
|
+
] })
|
|
7230
|
+
] }, p.id)) })
|
|
7231
|
+
] }) });
|
|
7232
|
+
}
|
|
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
|
|
7054
7241
|
] }),
|
|
7055
|
-
/* @__PURE__ */
|
|
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(
|
|
7056
7244
|
Table,
|
|
7057
7245
|
{
|
|
7058
7246
|
columns: [
|
|
7247
|
+
{ header: "", width: 2 },
|
|
7248
|
+
{ header: "ON", width: 2 },
|
|
7059
7249
|
{ header: "EFFECT", width: 7 },
|
|
7060
7250
|
{ header: "PRIO", width: 4 },
|
|
7061
|
-
{ header: "TOOL", width:
|
|
7062
|
-
{ header: "
|
|
7251
|
+
{ header: "TOOL", width: 20 },
|
|
7252
|
+
{ header: "CONSTRAINTS", width: 22 }
|
|
7063
7253
|
],
|
|
7064
|
-
rows:
|
|
7065
|
-
{ value:
|
|
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 },
|
|
7066
7258
|
{ value: String(r.priority), dim: true },
|
|
7067
|
-
{ value: truncate2(r.toolPattern,
|
|
7068
|
-
{ value: truncate2(r.description || "\u2014",
|
|
7259
|
+
{ value: truncate2(r.toolPattern, 20), color: theme.accent, dim: !r.enabled },
|
|
7260
|
+
{ value: ruleSummary(r) || truncate2(r.description || "\u2014", 22), dim: true }
|
|
7069
7261
|
])
|
|
7070
7262
|
}
|
|
7071
|
-
)
|
|
7072
|
-
|
|
7073
|
-
|
|
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
|
|
7274
|
+
] }),
|
|
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
|
+
] });
|
|
7074
7303
|
}
|
|
7304
|
+
var constr, setConstr, FIELDS;
|
|
7075
7305
|
var init_Policies = __esm({
|
|
7076
7306
|
"src/tui/panels/Policies.tsx"() {
|
|
7077
7307
|
"use strict";
|
|
@@ -7079,31 +7309,61 @@ var init_Policies = __esm({
|
|
|
7079
7309
|
init_components();
|
|
7080
7310
|
init_hooks();
|
|
7081
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
|
+
];
|
|
7082
7333
|
}
|
|
7083
7334
|
});
|
|
7084
7335
|
|
|
7085
7336
|
// src/tui/panels/RateLimit.tsx
|
|
7086
|
-
import { Box as
|
|
7087
|
-
import { useEffect as
|
|
7088
|
-
import { jsx as
|
|
7337
|
+
import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
|
|
7338
|
+
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
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
|
+
}
|
|
7089
7349
|
function RateLimitPanel({ focused }) {
|
|
7090
7350
|
const layersQ = useLoader(() => api.settings.getSecurityLayers());
|
|
7091
7351
|
const historyQ = useLoader(() => api.settings.getRateLimitHistory());
|
|
7352
|
+
const insightsQ = useLoader(() => api.stats.securityInsights(7));
|
|
7092
7353
|
const [draft, setDraft] = useState3(null);
|
|
7093
7354
|
const [dirty, setDirty] = useState3(false);
|
|
7094
7355
|
const [fi, setFi] = useState3(0);
|
|
7095
7356
|
const [status, setStatus] = useState3(null);
|
|
7096
|
-
|
|
7357
|
+
useEffect3(() => {
|
|
7097
7358
|
if (layersQ.data && !draft) setDraft({ ...layersQ.data.layers.rateLimit });
|
|
7098
7359
|
}, [layersQ.data, draft]);
|
|
7099
|
-
const adjust = (dir) => {
|
|
7360
|
+
const adjust = (dir, step) => {
|
|
7100
7361
|
if (!draft) return;
|
|
7101
|
-
const field =
|
|
7362
|
+
const field = FIELDS2[fi];
|
|
7102
7363
|
if (field === "mode") {
|
|
7103
7364
|
const idx = (MODES.indexOf(draft.mode) + dir + MODES.length) % MODES.length;
|
|
7104
7365
|
setDraft({ ...draft, mode: MODES[idx] });
|
|
7105
7366
|
} else {
|
|
7106
|
-
const step = 10;
|
|
7107
7367
|
setDraft({ ...draft, [field]: Math.max(0, draft[field] + dir * step) });
|
|
7108
7368
|
}
|
|
7109
7369
|
setDirty(true);
|
|
@@ -7113,8 +7373,7 @@ function RateLimitPanel({ focused }) {
|
|
|
7113
7373
|
if (!draft || !layersQ.data) return;
|
|
7114
7374
|
setStatus("Saving\u2026");
|
|
7115
7375
|
try {
|
|
7116
|
-
const
|
|
7117
|
-
const res = await api.settings.setSecurityLayers(next);
|
|
7376
|
+
const res = await api.settings.setSecurityLayers({ ...layersQ.data.layers, rateLimit: draft });
|
|
7118
7377
|
setDraft({ ...res.layers.rateLimit });
|
|
7119
7378
|
setDirty(false);
|
|
7120
7379
|
setStatus("\u2713 Saved");
|
|
@@ -7125,41 +7384,82 @@ function RateLimitPanel({ focused }) {
|
|
|
7125
7384
|
};
|
|
7126
7385
|
useInput2(
|
|
7127
7386
|
(input, key) => {
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
else if (key.
|
|
7131
|
-
else if (key.
|
|
7387
|
+
const step = key.shift ? 10 : 1;
|
|
7388
|
+
if (key.upArrow) setFi((n) => (n - 1 + FIELDS2.length) % FIELDS2.length);
|
|
7389
|
+
else if (key.downArrow) setFi((n) => (n + 1) % FIELDS2.length);
|
|
7390
|
+
else if (key.leftArrow) adjust(-1, step);
|
|
7391
|
+
else if (key.rightArrow) adjust(1, step);
|
|
7132
7392
|
else if (input === "s") void save();
|
|
7133
7393
|
},
|
|
7134
7394
|
{ isActive: focused }
|
|
7135
7395
|
);
|
|
7136
|
-
const history = historyQ.data?.history ?? [];
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
7396
|
+
const history = (historyQ.data?.history ?? []).slice().sort((a, b) => b.ts - a.ts);
|
|
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 }) })
|
|
7144
7408
|
] }),
|
|
7145
|
-
|
|
7146
|
-
/* @__PURE__ */
|
|
7147
|
-
/* @__PURE__ */
|
|
7148
|
-
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: " " + history.length + " changes" })
|
|
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
|
|
7149
7412
|
] }) : null,
|
|
7150
|
-
/* @__PURE__ */
|
|
7151
|
-
|
|
7152
|
-
|
|
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: [
|
|
7433
|
+
"Change history ",
|
|
7434
|
+
history.length ? `(${history.length})` : ""
|
|
7435
|
+
] }),
|
|
7436
|
+
history.length ? /* @__PURE__ */ jsx4(
|
|
7437
|
+
Table,
|
|
7438
|
+
{
|
|
7439
|
+
columns: [
|
|
7440
|
+
{ header: "WHEN", width: 10 },
|
|
7441
|
+
{ header: "/MIN", width: 6 },
|
|
7442
|
+
{ header: "/HOUR", width: 7 },
|
|
7443
|
+
{ header: "/DAY", width: 6 }
|
|
7444
|
+
],
|
|
7445
|
+
rows: history.slice(0, 4).map((h, i) => [
|
|
7446
|
+
{ value: ago(h.ts) + " ago", dim: true, bold: i === 0 },
|
|
7447
|
+
{ value: String(h.minute), bold: i === 0 },
|
|
7448
|
+
{ value: h.hour === 0 ? "off" : String(h.hour), dim: h.hour === 0 },
|
|
7449
|
+
{ value: h.day === 0 ? "off" : String(h.day), dim: h.day === 0 }
|
|
7450
|
+
])
|
|
7451
|
+
}
|
|
7452
|
+
) : /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " no changes" })
|
|
7153
7453
|
] })
|
|
7154
7454
|
] }) : null });
|
|
7155
7455
|
}
|
|
7156
|
-
function
|
|
7157
|
-
return /* @__PURE__ */
|
|
7158
|
-
/* @__PURE__ */
|
|
7456
|
+
function FieldRow({ label, active: active2, children }) {
|
|
7457
|
+
return /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
7458
|
+
/* @__PURE__ */ jsx4(Text4, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + label.padEnd(12) }),
|
|
7159
7459
|
children
|
|
7160
7460
|
] });
|
|
7161
7461
|
}
|
|
7162
|
-
var MODES,
|
|
7462
|
+
var MODES, FIELDS2, BLOCKS2;
|
|
7163
7463
|
var init_RateLimit = __esm({
|
|
7164
7464
|
"src/tui/panels/RateLimit.tsx"() {
|
|
7165
7465
|
"use strict";
|
|
@@ -7168,81 +7468,138 @@ var init_RateLimit = __esm({
|
|
|
7168
7468
|
init_hooks();
|
|
7169
7469
|
init_theme();
|
|
7170
7470
|
MODES = ["off", "detect", "block"];
|
|
7171
|
-
|
|
7471
|
+
FIELDS2 = ["mode", "perMinute", "perHour", "perDay"];
|
|
7472
|
+
BLOCKS2 = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
|
|
7172
7473
|
}
|
|
7173
7474
|
});
|
|
7174
7475
|
|
|
7175
7476
|
// src/tui/panels/Dlp.tsx
|
|
7176
|
-
import { Box as
|
|
7177
|
-
import
|
|
7178
|
-
import {
|
|
7477
|
+
import { Box as Box5, Text as Text5, useInput as useInput3 } from "ink";
|
|
7478
|
+
import TextInput2 from "ink-text-input";
|
|
7479
|
+
import { useEffect as useEffect4, useState as useState4 } from "react";
|
|
7480
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
7179
7481
|
function DlpPanel({ focused }) {
|
|
7180
7482
|
const q = useLoader(() => api.settings.getSecurityLayers());
|
|
7181
7483
|
const [dlp, setDlp] = useState4(null);
|
|
7182
7484
|
const [available, setAvailable] = useState4([]);
|
|
7183
7485
|
const [sel, setSel] = useState4(0);
|
|
7486
|
+
const [dirty, setDirty] = useState4(false);
|
|
7184
7487
|
const [status, setStatus] = useState4(null);
|
|
7185
|
-
|
|
7488
|
+
const [adding, setAdding] = useState4(null);
|
|
7489
|
+
const [newName, setNewName] = useState4("");
|
|
7490
|
+
const [newRe, setNewRe] = useState4("");
|
|
7491
|
+
useEffect4(() => {
|
|
7186
7492
|
if (q.data && !dlp) {
|
|
7187
|
-
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] });
|
|
7188
7494
|
setAvailable(q.data.availablePatterns);
|
|
7189
7495
|
}
|
|
7190
7496
|
}, [q.data, dlp]);
|
|
7191
|
-
const
|
|
7192
|
-
|
|
7193
|
-
|
|
7497
|
+
const mutate = (next) => {
|
|
7498
|
+
setDlp(next);
|
|
7499
|
+
setDirty(true);
|
|
7500
|
+
setStatus(null);
|
|
7501
|
+
};
|
|
7502
|
+
const save = async () => {
|
|
7503
|
+
if (!dlp || !q.data) return;
|
|
7194
7504
|
setStatus("Saving\u2026");
|
|
7195
7505
|
try {
|
|
7196
|
-
const res = await api.settings.setSecurityLayers({ ...q.data.layers, dlp
|
|
7197
|
-
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);
|
|
7198
7509
|
setStatus("\u2713 Saved");
|
|
7199
7510
|
} catch (e) {
|
|
7200
7511
|
setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
|
|
7201
7512
|
}
|
|
7202
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;
|
|
7203
7521
|
useInput3(
|
|
7204
7522
|
(input, key) => {
|
|
7205
7523
|
if (!dlp) return;
|
|
7206
7524
|
if (key.upArrow) setSel((n) => Math.max(0, n - 1));
|
|
7207
|
-
else if (key.downArrow) setSel((n) => Math.min(
|
|
7208
|
-
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) {
|
|
7209
7534
|
const p = available[sel];
|
|
7210
|
-
if (!p) return;
|
|
7211
7535
|
const set = new Set(dlp.patterns);
|
|
7212
7536
|
if (set.has(p)) set.delete(p);
|
|
7213
7537
|
else set.add(p);
|
|
7214
|
-
|
|
7215
|
-
} else if (input === "
|
|
7216
|
-
const
|
|
7217
|
-
|
|
7218
|
-
|
|
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();
|
|
7219
7545
|
},
|
|
7220
|
-
{ isActive: focused }
|
|
7546
|
+
{ isActive: focused && !adding }
|
|
7221
7547
|
);
|
|
7222
7548
|
const enabled = new Set(dlp?.patterns ?? []);
|
|
7223
|
-
return /* @__PURE__ */
|
|
7224
|
-
/* @__PURE__ */
|
|
7225
|
-
/* @__PURE__ */
|
|
7226
|
-
/* @__PURE__ */
|
|
7227
|
-
/* @__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
|
|
7228
7554
|
] }),
|
|
7229
|
-
/* @__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) => {
|
|
7230
7557
|
const on = enabled.has(p);
|
|
7231
|
-
|
|
7232
|
-
|
|
7233
|
-
|
|
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 })
|
|
7234
7562
|
] }, p);
|
|
7235
7563
|
}) }),
|
|
7236
|
-
|
|
7237
|
-
/* @__PURE__ */
|
|
7238
|
-
dlp.custom.
|
|
7239
|
-
|
|
7240
|
-
|
|
7241
|
-
|
|
7242
|
-
|
|
7243
|
-
|
|
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
|
+
)
|
|
7244
7601
|
] }) : null,
|
|
7245
|
-
status ? /* @__PURE__ */
|
|
7602
|
+
status ? /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) }) : null
|
|
7246
7603
|
] }) : null });
|
|
7247
7604
|
}
|
|
7248
7605
|
var MODES2;
|
|
@@ -7258,8 +7615,8 @@ var init_Dlp = __esm({
|
|
|
7258
7615
|
});
|
|
7259
7616
|
|
|
7260
7617
|
// src/tui/panels/Stats.tsx
|
|
7261
|
-
import { Box as
|
|
7262
|
-
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";
|
|
7263
7620
|
function StatsPanel({ active: active2 }) {
|
|
7264
7621
|
const stats = useLoader(() => api.stats.get());
|
|
7265
7622
|
const ts = useLoader(() => api.stats.timeseries({ period: "24h" }));
|
|
@@ -7269,50 +7626,50 @@ function StatsPanel({ active: active2 }) {
|
|
|
7269
7626
|
}, 5e3, active2);
|
|
7270
7627
|
const s = stats.data;
|
|
7271
7628
|
const points = ts.data?.timeseries ?? [];
|
|
7272
|
-
return /* @__PURE__ */
|
|
7273
|
-
/* @__PURE__ */
|
|
7274
|
-
/* @__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: [
|
|
7275
7632
|
s.total_calls,
|
|
7276
7633
|
" "
|
|
7277
7634
|
] }),
|
|
7278
|
-
/* @__PURE__ */
|
|
7279
|
-
/* @__PURE__ */
|
|
7635
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "calls " }),
|
|
7636
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.ok, children: [
|
|
7280
7637
|
s.allowed,
|
|
7281
7638
|
" allowed"
|
|
7282
7639
|
] }),
|
|
7283
|
-
/* @__PURE__ */
|
|
7284
|
-
/* @__PURE__ */
|
|
7640
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " " }),
|
|
7641
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.bad, children: [
|
|
7285
7642
|
s.denied,
|
|
7286
7643
|
" denied"
|
|
7287
7644
|
] })
|
|
7288
7645
|
] }),
|
|
7289
|
-
/* @__PURE__ */
|
|
7646
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
7290
7647
|
s.active_policies,
|
|
7291
7648
|
" policies \xB7 ",
|
|
7292
7649
|
s.registered_tools,
|
|
7293
7650
|
" tools"
|
|
7294
7651
|
] }),
|
|
7295
|
-
/* @__PURE__ */
|
|
7296
|
-
/* @__PURE__ */
|
|
7652
|
+
/* @__PURE__ */ jsxs6(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
7653
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
7297
7654
|
"Last 24h ",
|
|
7298
7655
|
ts.data ? `(per ${ts.data.granularity})` : ""
|
|
7299
7656
|
] }),
|
|
7300
|
-
/* @__PURE__ */
|
|
7301
|
-
/* @__PURE__ */
|
|
7302
|
-
/* @__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) })
|
|
7303
7660
|
] }),
|
|
7304
|
-
/* @__PURE__ */
|
|
7305
|
-
/* @__PURE__ */
|
|
7306
|
-
/* @__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) })
|
|
7307
7664
|
] }),
|
|
7308
|
-
/* @__PURE__ */
|
|
7309
|
-
/* @__PURE__ */
|
|
7310
|
-
/* @__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) })
|
|
7311
7668
|
] })
|
|
7312
7669
|
] }),
|
|
7313
|
-
/* @__PURE__ */
|
|
7314
|
-
/* @__PURE__ */
|
|
7315
|
-
/* @__PURE__ */
|
|
7670
|
+
/* @__PURE__ */ jsxs6(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
7671
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "Recent" }),
|
|
7672
|
+
/* @__PURE__ */ jsx6(
|
|
7316
7673
|
Table,
|
|
7317
7674
|
{
|
|
7318
7675
|
columns: [
|
|
@@ -7343,52 +7700,172 @@ var init_Stats = __esm({
|
|
|
7343
7700
|
});
|
|
7344
7701
|
|
|
7345
7702
|
// src/tui/panels/Audit.tsx
|
|
7346
|
-
import { Box as
|
|
7703
|
+
import { Box as Box7, Text as Text7, useInput as useInput4 } from "ink";
|
|
7704
|
+
import TextInput3 from "ink-text-input";
|
|
7347
7705
|
import { useState as useState5 } from "react";
|
|
7348
|
-
import { jsx as
|
|
7706
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
7349
7707
|
function AuditPanel({ active: active2, focused }) {
|
|
7350
|
-
const [
|
|
7351
|
-
const
|
|
7352
|
-
const
|
|
7353
|
-
|
|
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
|
+
);
|
|
7354
7732
|
useInput4(
|
|
7355
|
-
(input) => {
|
|
7356
|
-
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
|
+
}
|
|
7357
7754
|
},
|
|
7358
|
-
{ isActive: focused }
|
|
7755
|
+
{ isActive: focused && !editing }
|
|
7359
7756
|
);
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
/* @__PURE__ */
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
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
|
+
":"
|
|
7367
7802
|
] }),
|
|
7368
|
-
/* @__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(
|
|
7369
7835
|
Table,
|
|
7370
7836
|
{
|
|
7371
7837
|
columns: [
|
|
7838
|
+
{ header: "", width: 2 },
|
|
7372
7839
|
{ header: "DECISION", width: 9 },
|
|
7373
7840
|
{ header: "TOOL", width: 18 },
|
|
7374
7841
|
{ header: "AGENT", width: 14 },
|
|
7375
|
-
{ header: "REASON", width:
|
|
7842
|
+
{ header: "REASON", width: 24 },
|
|
7376
7843
|
{ header: "DLP", width: 4 },
|
|
7377
7844
|
{ header: "WHEN", width: 6 }
|
|
7378
7845
|
],
|
|
7379
|
-
rows: entries.map((e) =>
|
|
7380
|
-
{ value: e.decision, color: decisionColor(e.decision) },
|
|
7381
|
-
{ value: truncate2(e.tool_name, 18), color: theme.accent },
|
|
7382
|
-
{ value: truncate2(e.agent_name ?? "\u2014", 14), dim: true },
|
|
7383
|
-
{ value: truncate2(e.reason ?? "\u2014", 26) },
|
|
7384
|
-
{ 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 },
|
|
7385
|
-
{ value: ago(e.created_at), dim: true }
|
|
7386
|
-
])
|
|
7846
|
+
rows: entries.slice(0, 12).map((e, i) => rowFor(e, i === sel && focused))
|
|
7387
7847
|
}
|
|
7388
|
-
)
|
|
7848
|
+
)
|
|
7389
7849
|
] }) });
|
|
7390
7850
|
}
|
|
7391
|
-
|
|
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;
|
|
7392
7869
|
var init_Audit = __esm({
|
|
7393
7870
|
"src/tui/panels/Audit.tsx"() {
|
|
7394
7871
|
"use strict";
|
|
@@ -7396,65 +7873,8 @@ var init_Audit = __esm({
|
|
|
7396
7873
|
init_components();
|
|
7397
7874
|
init_hooks();
|
|
7398
7875
|
init_theme();
|
|
7399
|
-
|
|
7400
|
-
|
|
7401
|
-
});
|
|
7402
|
-
|
|
7403
|
-
// src/tui/panels/Agents.tsx
|
|
7404
|
-
import { Box as Box7, Text as Text7 } from "ink";
|
|
7405
|
-
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
7406
|
-
function AgentsPanel({ active: active2 }) {
|
|
7407
|
-
const agents = useLoader(() => api.agents.live({ limit: 14 }));
|
|
7408
|
-
usePoll(agents.reload, 4e3, active2);
|
|
7409
|
-
const data = agents.data;
|
|
7410
|
-
const rows = data?.agents ?? [];
|
|
7411
|
-
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: [
|
|
7412
|
-
data ? /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
7413
|
-
/* @__PURE__ */ jsxs7(Text7, { color: theme.ok, children: [
|
|
7414
|
-
data.counts.active,
|
|
7415
|
-
" active"
|
|
7416
|
-
] }),
|
|
7417
|
-
" \xB7 ",
|
|
7418
|
-
/* @__PURE__ */ jsxs7(Text7, { color: theme.warn, children: [
|
|
7419
|
-
data.counts.idle,
|
|
7420
|
-
" idle"
|
|
7421
|
-
] }),
|
|
7422
|
-
" \xB7 ",
|
|
7423
|
-
data.counts.deactivated,
|
|
7424
|
-
" off"
|
|
7425
|
-
] }) : null,
|
|
7426
|
-
/* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(
|
|
7427
|
-
Table,
|
|
7428
|
-
{
|
|
7429
|
-
columns: [
|
|
7430
|
-
{ header: "STATUS", width: 7 },
|
|
7431
|
-
{ header: "AGENT", width: 20 },
|
|
7432
|
-
{ header: "CALLS", width: 6 },
|
|
7433
|
-
{ header: "DENY", width: 5 },
|
|
7434
|
-
{ header: "DLP", width: 4 },
|
|
7435
|
-
{ header: "TRUST", width: 6 },
|
|
7436
|
-
{ header: "CHARACTER", width: 20 }
|
|
7437
|
-
],
|
|
7438
|
-
rows: rows.map((a) => [
|
|
7439
|
-
{ value: a.status, color: modeColor(a.status) },
|
|
7440
|
-
{ value: truncate2(a.agent_name ?? a.agent_id ?? a.session_id, 20), color: theme.accent },
|
|
7441
|
-
{ value: String(a.total_calls) },
|
|
7442
|
-
{ value: String(a.denied_calls), color: a.denied_calls ? theme.bad : void 0, dim: !a.denied_calls },
|
|
7443
|
-
{ value: String(a.dlp_events), color: a.dlp_events ? theme.bad : void 0, dim: !a.dlp_events },
|
|
7444
|
-
{ value: `${a.trust_score}` },
|
|
7445
|
-
{ value: truncate2(a.character || "\u2014", 20), dim: true }
|
|
7446
|
-
])
|
|
7447
|
-
}
|
|
7448
|
-
) })
|
|
7449
|
-
] }) });
|
|
7450
|
-
}
|
|
7451
|
-
var init_Agents = __esm({
|
|
7452
|
-
"src/tui/panels/Agents.tsx"() {
|
|
7453
|
-
"use strict";
|
|
7454
|
-
init_api_client();
|
|
7455
|
-
init_components();
|
|
7456
|
-
init_hooks();
|
|
7457
|
-
init_theme();
|
|
7876
|
+
DECISIONS = [void 0, "DENY", "ALLOW"];
|
|
7877
|
+
SIGNALS = [void 0, "dlp", "ratelimit"];
|
|
7458
7878
|
}
|
|
7459
7879
|
});
|
|
7460
7880
|
|
|
@@ -7507,19 +7927,19 @@ var init_App = __esm({
|
|
|
7507
7927
|
init_cli_utils();
|
|
7508
7928
|
init_components();
|
|
7509
7929
|
init_theme();
|
|
7930
|
+
init_Live();
|
|
7510
7931
|
init_Policies();
|
|
7511
7932
|
init_RateLimit();
|
|
7512
7933
|
init_Dlp();
|
|
7513
7934
|
init_Stats();
|
|
7514
7935
|
init_Audit();
|
|
7515
|
-
init_Agents();
|
|
7516
7936
|
SECTIONS = [
|
|
7937
|
+
{ label: "Live", Panel: LivePanel },
|
|
7517
7938
|
{ label: "Policies", Panel: PoliciesPanel },
|
|
7518
7939
|
{ label: "Rate Limit", Panel: RateLimitPanel },
|
|
7519
7940
|
{ label: "DLP", Panel: DlpPanel },
|
|
7520
7941
|
{ label: "Stats", Panel: StatsPanel },
|
|
7521
|
-
{ label: "Audit", Panel: AuditPanel }
|
|
7522
|
-
{ label: "Agents", Panel: AgentsPanel }
|
|
7942
|
+
{ label: "Audit", Panel: AuditPanel }
|
|
7523
7943
|
];
|
|
7524
7944
|
BANNER_HEX = ["#1432A0", "#2850BE", "#3C6ED7", "#5A8CE6", "#82AAF0", "#AAC8FA"];
|
|
7525
7945
|
}
|
|
@@ -7584,10 +8004,10 @@ function truncate3(s, n) {
|
|
|
7584
8004
|
function sparkline2(values) {
|
|
7585
8005
|
if (values.length === 0) return "";
|
|
7586
8006
|
const max = Math.max(...values, 0);
|
|
7587
|
-
if (max === 0) return
|
|
7588
|
-
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("");
|
|
7589
8009
|
}
|
|
7590
|
-
var out, err, dim, bold, green, red, yellow, cyan, ANSI, width,
|
|
8010
|
+
var out, err, dim, bold, green, red, yellow, cyan, ANSI, width, BLOCKS3;
|
|
7591
8011
|
var init_format = __esm({
|
|
7592
8012
|
"src/commands/format.ts"() {
|
|
7593
8013
|
"use strict";
|
|
@@ -7602,7 +8022,7 @@ var init_format = __esm({
|
|
|
7602
8022
|
cyan = (s) => `${c.cyan}${s}${c.reset}`;
|
|
7603
8023
|
ANSI = /\x1b\[[0-9;]*m/g;
|
|
7604
8024
|
width = (s) => s.replace(ANSI, "").length;
|
|
7605
|
-
|
|
8025
|
+
BLOCKS3 = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
|
|
7606
8026
|
}
|
|
7607
8027
|
});
|
|
7608
8028
|
|