@solongate/proxy 0.83.59 → 0.83.60
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/api-client/client.d.ts +15 -1
- package/dist/api-client/fleet.d.ts +163 -0
- package/dist/api-client/index.d.ts +2 -0
- package/dist/commands/index.js +100 -3
- package/dist/index.js +1390 -599
- package/dist/tui/index.js +1357 -594
- package/dist/tui/panels/Fleet.d.ts +4 -0
- package/dist/tui/panels/FleetLive.d.ts +4 -0
- package/package.json +7 -7
package/dist/tui/index.js
CHANGED
|
@@ -495,8 +495,8 @@ function readCodexHooksFile(path) {
|
|
|
495
495
|
continue;
|
|
496
496
|
}
|
|
497
497
|
if (k === "hooks" && v && typeof v === "object") {
|
|
498
|
-
for (const [ev,
|
|
499
|
-
if (Array.isArray(
|
|
498
|
+
for (const [ev, groups2] of Object.entries(v)) {
|
|
499
|
+
if (Array.isArray(groups2)) out.hooks[ev] = groups2;
|
|
500
500
|
}
|
|
501
501
|
continue;
|
|
502
502
|
}
|
|
@@ -510,7 +510,7 @@ function readCodexHooksFile(path) {
|
|
|
510
510
|
}
|
|
511
511
|
function writeCodexHooksFile(path, file) {
|
|
512
512
|
const hooks = {};
|
|
513
|
-
for (const [ev,
|
|
513
|
+
for (const [ev, groups2] of Object.entries(file.hooks)) if (groups2.length) hooks[ev] = groups2;
|
|
514
514
|
const body = { ...file.rest };
|
|
515
515
|
if (file.description) body["description"] = file.description;
|
|
516
516
|
body["hooks"] = hooks;
|
|
@@ -557,9 +557,9 @@ function removeCodexGuard(p) {
|
|
|
557
557
|
try {
|
|
558
558
|
const file = readCodexHooksFile(p.codexHooksPath);
|
|
559
559
|
let changed = false;
|
|
560
|
-
for (const [ev,
|
|
561
|
-
const kept =
|
|
562
|
-
if (kept.length !==
|
|
560
|
+
for (const [ev, groups2] of Object.entries(file.hooks)) {
|
|
561
|
+
const kept = groups2.filter((g) => !isOurCodexGroup(g));
|
|
562
|
+
if (kept.length !== groups2.length) {
|
|
563
563
|
file.hooks[ev] = kept;
|
|
564
564
|
changed = true;
|
|
565
565
|
}
|
|
@@ -691,7 +691,7 @@ function repairQuiet() {
|
|
|
691
691
|
const p = globalPaths();
|
|
692
692
|
const has = (f) => existsSync3(f);
|
|
693
693
|
const guardFile = join4(p.hooksDir, "guard.mjs");
|
|
694
|
-
const line = (
|
|
694
|
+
const line = (label2, ok, yes, no) => ({ label: label2, ok, detail: ok ? yes : no });
|
|
695
695
|
const runtime = () => {
|
|
696
696
|
const r2 = hookCanStart();
|
|
697
697
|
return { label: "hook runtime", ok: r2.ok, detail: r2.ok ? `node ${r2.detail}` : r2.detail };
|
|
@@ -834,13 +834,13 @@ function guardHookOutdated() {
|
|
|
834
834
|
return false;
|
|
835
835
|
}
|
|
836
836
|
}
|
|
837
|
-
function hookCommandFor(hooksDir2, script, client = "claude-code",
|
|
837
|
+
function hookCommandFor(hooksDir2, script, client = "claude-code", label2 = "Claude Code") {
|
|
838
838
|
const target = join4(hooksDir2, script).replace(/\\/g, "/");
|
|
839
839
|
if (process.platform === "win32") {
|
|
840
|
-
return `& "${process.execPath.replace(/\\/g, "/")}" "${target}" ${client} "${
|
|
840
|
+
return `& "${process.execPath.replace(/\\/g, "/")}" "${target}" ${client} "${label2}"`;
|
|
841
841
|
}
|
|
842
842
|
const launcher = join4(hooksDir2, LAUNCHER_NAME).replace(/\\/g, "/");
|
|
843
|
-
return `/bin/sh "${launcher}" "${target}" ${client} "${
|
|
843
|
+
return `/bin/sh "${launcher}" "${target}" ${client} "${label2}"`;
|
|
844
844
|
}
|
|
845
845
|
function writeLauncher(hooksDir2) {
|
|
846
846
|
writeFileSync2(join4(hooksDir2, LAUNCHER_NAME), launcherScript(process.execPath));
|
|
@@ -948,8 +948,8 @@ import { join as join11 } from "path";
|
|
|
948
948
|
import { render } from "ink";
|
|
949
949
|
|
|
950
950
|
// src/tui/App.tsx
|
|
951
|
-
import { Box as
|
|
952
|
-
import { useEffect as
|
|
951
|
+
import { Box as Box11, Text as Text11, useApp, useInput as useInput10 } from "ink";
|
|
952
|
+
import { useEffect as useEffect9, useState as useState11 } from "react";
|
|
953
953
|
|
|
954
954
|
// src/cli-utils.ts
|
|
955
955
|
var c = {
|
|
@@ -1072,14 +1072,14 @@ var ymd = (ts) => {
|
|
|
1072
1072
|
const p = (n) => String(n).padStart(2, "0");
|
|
1073
1073
|
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`;
|
|
1074
1074
|
};
|
|
1075
|
-
function PaneTitle({ label, extra, width }) {
|
|
1075
|
+
function PaneTitle({ label: label2, extra, width }) {
|
|
1076
1076
|
const tail = extra ? ` ${extra} ` : " ";
|
|
1077
|
-
const used = 2 + 1 +
|
|
1077
|
+
const used = 2 + 1 + label2.length + tail.length;
|
|
1078
1078
|
return /* @__PURE__ */ jsxs(Text, { wrap: "truncate", children: [
|
|
1079
1079
|
/* @__PURE__ */ jsx(Text, { color: theme.dim, children: "\u2500 " }),
|
|
1080
1080
|
/* @__PURE__ */ jsxs(Text, { color: theme.accentBright, bold: true, children: [
|
|
1081
1081
|
"\u258E",
|
|
1082
|
-
|
|
1082
|
+
label2
|
|
1083
1083
|
] }),
|
|
1084
1084
|
/* @__PURE__ */ jsx(Text, { color: theme.dim, children: tail + "\u2500".repeat(Math.max(0, width - used)) })
|
|
1085
1085
|
] });
|
|
@@ -1133,10 +1133,10 @@ function Table({ columns, rows }) {
|
|
|
1133
1133
|
] });
|
|
1134
1134
|
}
|
|
1135
1135
|
function KeyHints({ hints }) {
|
|
1136
|
-
return /* @__PURE__ */ jsx(Box, { children: hints.map(([key,
|
|
1136
|
+
return /* @__PURE__ */ jsx(Box, { children: hints.map(([key, label2], i) => /* @__PURE__ */ jsxs(Text, { color: theme.dim, children: [
|
|
1137
1137
|
/* @__PURE__ */ jsx(Text, { color: theme.accent, children: key }),
|
|
1138
1138
|
" ",
|
|
1139
|
-
|
|
1139
|
+
label2,
|
|
1140
1140
|
i < hints.length - 1 ? " " : ""
|
|
1141
1141
|
] }, i)) });
|
|
1142
1142
|
}
|
|
@@ -1471,6 +1471,10 @@ function buildUrl(base, path, query) {
|
|
|
1471
1471
|
if (query) {
|
|
1472
1472
|
for (const [k, v] of Object.entries(query)) {
|
|
1473
1473
|
if (v === void 0) continue;
|
|
1474
|
+
if (Array.isArray(v)) {
|
|
1475
|
+
for (const one of v) url.searchParams.append(k, one);
|
|
1476
|
+
continue;
|
|
1477
|
+
}
|
|
1474
1478
|
url.searchParams.set(k, String(v));
|
|
1475
1479
|
}
|
|
1476
1480
|
}
|
|
@@ -1530,6 +1534,7 @@ async function request(method, path, opts = {}) {
|
|
|
1530
1534
|
if (res.status >= 500) throw new ApiError(res.status, "SERVER_ERROR", text || "SolonGate API had a problem (server error). Please try again in a moment.");
|
|
1531
1535
|
throw new ApiError(res.status, "ERROR", text || res.statusText || `HTTP ${res.status}`);
|
|
1532
1536
|
}
|
|
1537
|
+
if (opts.raw) return text;
|
|
1533
1538
|
return json;
|
|
1534
1539
|
}
|
|
1535
1540
|
|
|
@@ -1809,8 +1814,100 @@ function list4() {
|
|
|
1809
1814
|
return request("GET", "/mcp-servers");
|
|
1810
1815
|
}
|
|
1811
1816
|
|
|
1817
|
+
// src/api-client/fleet.ts
|
|
1818
|
+
var fleet_exports = {};
|
|
1819
|
+
__export(fleet_exports, {
|
|
1820
|
+
activity: () => activity,
|
|
1821
|
+
analytics: () => analytics,
|
|
1822
|
+
assignGroup: () => assignGroup,
|
|
1823
|
+
createGroup: () => createGroup,
|
|
1824
|
+
deleteGroup: () => deleteGroup,
|
|
1825
|
+
groups: () => groups,
|
|
1826
|
+
invite: () => invite,
|
|
1827
|
+
overview: () => overview,
|
|
1828
|
+
renameGroup: () => renameGroup,
|
|
1829
|
+
report: () => report,
|
|
1830
|
+
reportDownload: () => reportDownload,
|
|
1831
|
+
reportSchedule: () => reportSchedule,
|
|
1832
|
+
revoke: () => revoke2,
|
|
1833
|
+
sendReport: () => sendReport,
|
|
1834
|
+
setGroupColor: () => setGroupColor,
|
|
1835
|
+
setGroupPolicy: () => setGroupPolicy,
|
|
1836
|
+
setReportSchedule: () => setReportSchedule,
|
|
1837
|
+
setUserPolicy: () => setUserPolicy,
|
|
1838
|
+
stream: () => stream,
|
|
1839
|
+
summary: () => summary
|
|
1840
|
+
});
|
|
1841
|
+
function overview() {
|
|
1842
|
+
return request("GET", "/fleet");
|
|
1843
|
+
}
|
|
1844
|
+
function summary(opts = {}) {
|
|
1845
|
+
const query = {};
|
|
1846
|
+
if (opts.range) query.range = opts.range;
|
|
1847
|
+
if (opts.group?.length) query.group = opts.group;
|
|
1848
|
+
if (opts.who?.length) query.who = opts.who;
|
|
1849
|
+
if (opts.agent) query.agent = opts.agent;
|
|
1850
|
+
return request("GET", "/fleet/summary", { query });
|
|
1851
|
+
}
|
|
1852
|
+
function groups() {
|
|
1853
|
+
return request("GET", "/fleet/groups");
|
|
1854
|
+
}
|
|
1855
|
+
function createGroup(name, color) {
|
|
1856
|
+
return request("POST", "/fleet/groups", { body: { name, color } });
|
|
1857
|
+
}
|
|
1858
|
+
function renameGroup(name, newName) {
|
|
1859
|
+
return request("POST", "/fleet/groups/rename", { body: { name, new_name: newName } });
|
|
1860
|
+
}
|
|
1861
|
+
function setGroupColor(name, color) {
|
|
1862
|
+
return request("POST", "/fleet/groups/color", { body: { name, color } });
|
|
1863
|
+
}
|
|
1864
|
+
function deleteGroup(name) {
|
|
1865
|
+
return request("POST", "/fleet/groups/delete", { body: { name } });
|
|
1866
|
+
}
|
|
1867
|
+
function setGroupPolicy(name, policyId) {
|
|
1868
|
+
return request("POST", "/fleet/groups/policy", { body: { name, policy_id: policyId } });
|
|
1869
|
+
}
|
|
1870
|
+
function setUserPolicy(ids, policyId) {
|
|
1871
|
+
return request("POST", "/fleet/policy", { body: { ids, policy_id: policyId } });
|
|
1872
|
+
}
|
|
1873
|
+
function assignGroup(ids, group) {
|
|
1874
|
+
return request("POST", "/fleet/group", { body: { ids, group } });
|
|
1875
|
+
}
|
|
1876
|
+
function invite(email) {
|
|
1877
|
+
return request("POST", "/fleet/invitations", { body: { email } });
|
|
1878
|
+
}
|
|
1879
|
+
function revoke2(id) {
|
|
1880
|
+
return request("DELETE", `/fleet/invitations/${encodeURIComponent(id)}`);
|
|
1881
|
+
}
|
|
1882
|
+
function analytics(guest) {
|
|
1883
|
+
return request("GET", "/fleet/analytics", { query: { guest } });
|
|
1884
|
+
}
|
|
1885
|
+
function activity(guest, limit) {
|
|
1886
|
+
const query = { guest };
|
|
1887
|
+
if (limit) query.limit = limit;
|
|
1888
|
+
return request("GET", "/fleet/activity", { query });
|
|
1889
|
+
}
|
|
1890
|
+
function report(span = "day") {
|
|
1891
|
+
return request("GET", "/fleet/report", { query: { span } });
|
|
1892
|
+
}
|
|
1893
|
+
function reportDownload(span, format = "md") {
|
|
1894
|
+
return request("GET", "/fleet/report/download", { query: { span, format }, raw: true });
|
|
1895
|
+
}
|
|
1896
|
+
function sendReport(span = "day") {
|
|
1897
|
+
return request("POST", "/fleet/report/send", { query: { span }, body: {} });
|
|
1898
|
+
}
|
|
1899
|
+
function reportSchedule() {
|
|
1900
|
+
return request("GET", "/settings/reports");
|
|
1901
|
+
}
|
|
1902
|
+
function setReportSchedule(schedule) {
|
|
1903
|
+
return request("PUT", "/settings/reports", { body: schedule });
|
|
1904
|
+
}
|
|
1905
|
+
function stream(limit = 60) {
|
|
1906
|
+
return request("GET", "/fleet/stream", { query: { limit } });
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1812
1909
|
// src/api-client/index.ts
|
|
1813
|
-
var api = { auth: auth_exports, policies: policies_exports, settings: settings_exports, stats: stats_exports, audit: audit_exports, agents: agents_exports, keys: keys_exports, mcp: mcp_exports };
|
|
1910
|
+
var api = { auth: auth_exports, policies: policies_exports, settings: settings_exports, stats: stats_exports, audit: audit_exports, agents: agents_exports, keys: keys_exports, mcp: mcp_exports, fleet: fleet_exports };
|
|
1814
1911
|
|
|
1815
1912
|
// src/tui/notify.ts
|
|
1816
1913
|
import { spawn as spawn3 } from "child_process";
|
|
@@ -1980,10 +2077,10 @@ function ColumnChart({ series, hot, height, width, color, hotColor = theme.bad }
|
|
|
1980
2077
|
}
|
|
1981
2078
|
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: lines });
|
|
1982
2079
|
}
|
|
1983
|
-
function HBar({ label, value, max, width, color }) {
|
|
2080
|
+
function HBar({ label: label2, value, max, width, color }) {
|
|
1984
2081
|
const filled = max > 0 ? Math.round(value / max * width) : 0;
|
|
1985
2082
|
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
1986
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children:
|
|
2083
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: label2.padEnd(10) }),
|
|
1987
2084
|
/* @__PURE__ */ jsx2(Text2, { color, children: "\u2588".repeat(Math.min(width, filled)) }),
|
|
1988
2085
|
/* @__PURE__ */ jsx2(Text2, { color: DIM_FLOOR, children: "\u2591".repeat(Math.max(0, width - filled)) }),
|
|
1989
2086
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " " + value })
|
|
@@ -2384,7 +2481,7 @@ function LivePanel({ active: active2 }) {
|
|
|
2384
2481
|
const selClamped = Math.min(Math.max(0, sel), Math.max(0, visibleDesc.length - 1));
|
|
2385
2482
|
const maxScroll = Math.max(0, visibleDesc.length - streamBodyRows);
|
|
2386
2483
|
const clampedScroll = Math.min(Math.max(0, selClamped - Math.floor((streamBodyRows - 1) / 2)), maxScroll);
|
|
2387
|
-
const
|
|
2484
|
+
const windowed2 = visibleDesc.slice(clampedScroll, clampedScroll + streamBodyRows);
|
|
2388
2485
|
const selEntry = visibleDesc[selClamped];
|
|
2389
2486
|
const detailEntries = detail ? (() => {
|
|
2390
2487
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -2647,11 +2744,11 @@ function LivePanel({ active: active2 }) {
|
|
|
2647
2744
|
const L = [];
|
|
2648
2745
|
const push = (el) => L.push(/* @__PURE__ */ jsx2(Box2, { children: el }, L.length));
|
|
2649
2746
|
const blank = () => push(/* @__PURE__ */ jsx2(Text2, { children: " " }));
|
|
2650
|
-
const section = (
|
|
2747
|
+
const section = (label2, m, note) => push(
|
|
2651
2748
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
2652
2749
|
/* @__PURE__ */ jsxs2(Text2, { bold: true, color: theme.accentBright, children: [
|
|
2653
2750
|
"\u258E",
|
|
2654
|
-
|
|
2751
|
+
label2.padEnd(10)
|
|
2655
2752
|
] }),
|
|
2656
2753
|
/* @__PURE__ */ jsx2(Text2, { bold: true, color: modeColor2(m), children: (m ?? "?").padEnd(8) }),
|
|
2657
2754
|
note ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: note }) : null
|
|
@@ -2924,7 +3021,7 @@ function LivePanel({ active: active2 }) {
|
|
|
2924
3021
|
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: "local logs on" }),
|
|
2925
3022
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ` \u2014 no entries yet \xB7 hooks write ${localPath?.file ?? ""}` })
|
|
2926
3023
|
] }) : null,
|
|
2927
|
-
|
|
3024
|
+
windowed2.length === 0 ? (
|
|
2928
3025
|
// With a source filter on, "awaiting traffic" reads as "nothing is
|
|
2929
3026
|
// happening" when the stream is merely filtered — say which it is.
|
|
2930
3027
|
filter === "all" ? /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
@@ -2932,7 +3029,7 @@ function LivePanel({ active: active2 }) {
|
|
|
2932
3029
|
" awaiting traffic\u2026"
|
|
2933
3030
|
] }) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: `${spin} no ${filter === "local" ? "LOC (local log)" : "CLD (cloud)"} calls in the buffer \xB7 f switches source` })
|
|
2934
3031
|
) : null,
|
|
2935
|
-
|
|
3032
|
+
windowed2.map((e, i) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e), selected: clampedScroll + i === selClamped }, e.id))
|
|
2936
3033
|
] }),
|
|
2937
3034
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
2938
3035
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "white", bold: true, children: " LIVE " }),
|
|
@@ -2942,16 +3039,665 @@ function LivePanel({ active: active2 }) {
|
|
|
2942
3039
|
] });
|
|
2943
3040
|
}
|
|
2944
3041
|
|
|
2945
|
-
// src/tui/panels/
|
|
3042
|
+
// src/tui/panels/FleetLive.tsx
|
|
3043
|
+
import { Box as Box3, Text as Text3, useInput as useInput2 } from "ink";
|
|
3044
|
+
import { useEffect as useEffect3, useMemo, useRef as useRef3, useState as useState3 } from "react";
|
|
3045
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
3046
|
+
var TICK_MS = 2e3;
|
|
3047
|
+
var hhmmss2 = (unix) => {
|
|
3048
|
+
const d = new Date(unix * 1e3);
|
|
3049
|
+
return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
|
|
3050
|
+
};
|
|
3051
|
+
function FleetLivePanel({ focused }) {
|
|
3052
|
+
const { cols, rows } = useTermSize();
|
|
3053
|
+
const streamQ = useLoader(() => api.fleet.stream(80));
|
|
3054
|
+
const summaryQ = useLoader(() => api.fleet.summary({ range: "24h" }));
|
|
3055
|
+
const [paused, setPaused] = useState3(false);
|
|
3056
|
+
const [onlyDenies, setOnlyDenies] = useState3(false);
|
|
3057
|
+
usePoll(() => {
|
|
3058
|
+
if (paused) return;
|
|
3059
|
+
streamQ.reloadQuiet();
|
|
3060
|
+
}, TICK_MS);
|
|
3061
|
+
usePoll(() => {
|
|
3062
|
+
if (paused) return;
|
|
3063
|
+
summaryQ.reloadQuiet();
|
|
3064
|
+
}, 15e3);
|
|
3065
|
+
useInput2(
|
|
3066
|
+
(input) => {
|
|
3067
|
+
if (input === " ") setPaused((p) => !p);
|
|
3068
|
+
else if (input === "d") setOnlyDenies((d) => !d);
|
|
3069
|
+
else if (input === "r") {
|
|
3070
|
+
streamQ.reload();
|
|
3071
|
+
summaryQ.reload();
|
|
3072
|
+
}
|
|
3073
|
+
},
|
|
3074
|
+
{ isActive: focused }
|
|
3075
|
+
);
|
|
3076
|
+
const calls = streamQ.data?.calls ?? [];
|
|
3077
|
+
const seenIds = useRef3(/* @__PURE__ */ new Set());
|
|
3078
|
+
const started = useRef3(Date.now());
|
|
3079
|
+
const [since, setSince] = useState3(() => ({
|
|
3080
|
+
calls: 0,
|
|
3081
|
+
denied: 0,
|
|
3082
|
+
byUser: /* @__PURE__ */ new Map()
|
|
3083
|
+
}));
|
|
3084
|
+
useEffect3(() => {
|
|
3085
|
+
if (calls.length === 0) return;
|
|
3086
|
+
let added = 0;
|
|
3087
|
+
let denied = 0;
|
|
3088
|
+
const byUser = new Map(since.byUser);
|
|
3089
|
+
for (const row of [...calls].reverse()) {
|
|
3090
|
+
if (seenIds.current.has(row.id)) continue;
|
|
3091
|
+
seenIds.current.add(row.id);
|
|
3092
|
+
added++;
|
|
3093
|
+
const isDeny = row.decision.toUpperCase() === "DENY";
|
|
3094
|
+
if (isDeny) denied++;
|
|
3095
|
+
const prev = byUser.get(row.who) ?? { calls: 0, denied: 0, last: 0 };
|
|
3096
|
+
byUser.set(row.who, {
|
|
3097
|
+
calls: prev.calls + 1,
|
|
3098
|
+
denied: prev.denied + (isDeny ? 1 : 0),
|
|
3099
|
+
last: Math.max(prev.last, row.created_at)
|
|
3100
|
+
});
|
|
3101
|
+
}
|
|
3102
|
+
if (added === 0) return;
|
|
3103
|
+
if (seenIds.current.size === added) {
|
|
3104
|
+
setSince({ calls: 0, denied: 0, byUser: /* @__PURE__ */ new Map() });
|
|
3105
|
+
return;
|
|
3106
|
+
}
|
|
3107
|
+
setSince((s) => ({ calls: s.calls + added, denied: s.denied + denied, byUser }));
|
|
3108
|
+
}, [calls]);
|
|
3109
|
+
const shown = useMemo(
|
|
3110
|
+
() => onlyDenies ? calls.filter((c2) => c2.decision.toUpperCase() === "DENY") : calls,
|
|
3111
|
+
[calls, onlyDenies]
|
|
3112
|
+
);
|
|
3113
|
+
const people = useMemo(() => {
|
|
3114
|
+
const list5 = [...since.byUser.entries()].map(([who, s]) => ({ who, ...s }));
|
|
3115
|
+
list5.sort((a, b) => b.calls - a.calls || b.last - a.last);
|
|
3116
|
+
return list5.slice(0, 8);
|
|
3117
|
+
}, [since]);
|
|
3118
|
+
const totals = summaryQ.data?.totals;
|
|
3119
|
+
const elapsed = Math.max(1, Math.round((Date.now() - started.current) / 1e3));
|
|
3120
|
+
const rate = since.calls > 0 ? (since.calls / (elapsed / 60)).toFixed(1) : "0.0";
|
|
3121
|
+
const tickerRows = Math.max(4, rows - 12 - Math.min(people.length, 8));
|
|
3122
|
+
const width = Math.max(40, cols - 4);
|
|
3123
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", width: cols, height: rows - 1, paddingX: 1, overflow: "hidden", children: [
|
|
3124
|
+
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
3125
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.accentBright, children: "\u25B6 FLEET LIVE" }),
|
|
3126
|
+
/* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
|
|
3127
|
+
" " + (summaryQ.data?.developers ?? 0) + " people",
|
|
3128
|
+
totals ? " " + totals.calls + " calls / 24h \xB7 " + totals.denied + " denied" : ""
|
|
3129
|
+
] }),
|
|
3130
|
+
paused ? /* @__PURE__ */ jsx3(Text3, { color: theme.warn, children: " paused" }) : null,
|
|
3131
|
+
onlyDenies ? /* @__PURE__ */ jsx3(Text3, { color: theme.bad, children: " denies only" }) : null
|
|
3132
|
+
] }),
|
|
3133
|
+
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
3134
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "since you opened this: " + since.calls + " calls, " }),
|
|
3135
|
+
/* @__PURE__ */ jsx3(Text3, { color: since.denied > 0 ? theme.bad : theme.dim, children: since.denied + " denied" }),
|
|
3136
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: " \xB7 " + rate + "/min" })
|
|
3137
|
+
] }),
|
|
3138
|
+
people.length > 0 ? /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
|
|
3139
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "Working now" }),
|
|
3140
|
+
people.map((p) => /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
3141
|
+
/* @__PURE__ */ jsx3(Text3, { children: " " + truncate(p.who, 28).padEnd(29) }),
|
|
3142
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: String(p.calls).padStart(5) + " calls " }),
|
|
3143
|
+
/* @__PURE__ */ jsx3(Text3, { color: p.denied > 0 ? theme.bad : theme.dim, children: String(p.denied).padStart(4) + " denied " }),
|
|
3144
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: hhmmss2(p.last) })
|
|
3145
|
+
] }, p.who))
|
|
3146
|
+
] }) : null,
|
|
3147
|
+
/* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", flexGrow: 1, overflow: "hidden", children: [
|
|
3148
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: onlyDenies ? "Refusals" : "Calls" }),
|
|
3149
|
+
streamQ.error ? /* @__PURE__ */ jsx3(Text3, { color: theme.bad, children: "\u2717 " + streamQ.error }) : shown.length === 0 ? /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "Nothing yet. This fills as your fleet works." }) : shown.slice(0, tickerRows).map((row) => /* @__PURE__ */ jsx3(StreamRow, { row, width }, row.id))
|
|
3150
|
+
] }),
|
|
3151
|
+
/* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "space pause \xB7 d denies only \xB7 r refresh \xB7 \u2190/esc back" }) })
|
|
3152
|
+
] });
|
|
3153
|
+
}
|
|
3154
|
+
function StreamRow({ row, width }) {
|
|
3155
|
+
const decision = (row.decision || "").toUpperCase();
|
|
3156
|
+
const who = truncate(row.who, 22).padEnd(23);
|
|
3157
|
+
const tool = truncate(row.tool || "\u2014", 26).padEnd(27);
|
|
3158
|
+
const rest = Math.max(10, width - 23 - 27 - 9 - 8);
|
|
3159
|
+
const tail = decision === "DENY" ? row.reason || "" : row.agent || "";
|
|
3160
|
+
return /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
3161
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: hhmmss2(row.created_at) + " " }),
|
|
3162
|
+
/* @__PURE__ */ jsx3(Text3, { children: who }),
|
|
3163
|
+
/* @__PURE__ */ jsx3(Text3, { color: decisionColor(decision), children: (decision || "\u2014").padEnd(7) }),
|
|
3164
|
+
/* @__PURE__ */ jsx3(Text3, { children: tool }),
|
|
3165
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: truncate(tail, rest) })
|
|
3166
|
+
] });
|
|
3167
|
+
}
|
|
3168
|
+
|
|
3169
|
+
// src/tui/panels/Fleet.tsx
|
|
2946
3170
|
import { Box as Box4, Text as Text4, useInput as useInput3 } from "ink";
|
|
2947
|
-
import
|
|
2948
|
-
import {
|
|
3171
|
+
import TextInput2 from "ink-text-input";
|
|
3172
|
+
import { useMemo as useMemo2, useState as useState4 } from "react";
|
|
3173
|
+
import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
3174
|
+
var SELF = "self";
|
|
3175
|
+
var UNGROUPED = "~ungrouped";
|
|
3176
|
+
var GROUP_COLORS = ["slate", "blue", "green", "amber", "red", "violet", "cyan", "pink"];
|
|
3177
|
+
var shortId = (id) => id.length > 10 ? id.slice(0, 9) + "\u2026" : id;
|
|
3178
|
+
var policyLabel = (id, policies) => {
|
|
3179
|
+
if (!id) return "\u2014";
|
|
3180
|
+
const hit = policies.find((p) => p.id === id);
|
|
3181
|
+
return hit ? hit.name : shortId(id);
|
|
3182
|
+
};
|
|
3183
|
+
function FleetPanel({ focused }) {
|
|
3184
|
+
const { cols, rows } = usePanelSize();
|
|
3185
|
+
const overviewQ = useLoader(() => api.fleet.overview());
|
|
3186
|
+
const groupsQ = useLoader(() => api.fleet.groups());
|
|
3187
|
+
const summaryQ = useLoader(() => api.fleet.summary({ range: "30d" }));
|
|
3188
|
+
const policiesQ = useLoader(() => api.policies.list());
|
|
3189
|
+
const [view, setView] = useState4("roster");
|
|
3190
|
+
const [sel, setSel] = useState4(0);
|
|
3191
|
+
const [status, setStatus] = useState4(null);
|
|
3192
|
+
const [prompt, setPrompt] = useState4(null);
|
|
3193
|
+
const [draft, setDraft] = useState4("");
|
|
3194
|
+
const [pick, setPick] = useState4(0);
|
|
3195
|
+
const [confirm, setConfirm] = useState4(null);
|
|
3196
|
+
usePoll(() => {
|
|
3197
|
+
if (prompt) return;
|
|
3198
|
+
overviewQ.reloadQuiet();
|
|
3199
|
+
groupsQ.reloadQuiet();
|
|
3200
|
+
summaryQ.reloadQuiet();
|
|
3201
|
+
}, 15e3);
|
|
3202
|
+
const policies = policiesQ.data?.policies ?? [];
|
|
3203
|
+
const overview2 = overviewQ.data;
|
|
3204
|
+
const groups2 = groupsQ.data?.groups ?? [];
|
|
3205
|
+
const summary2 = summaryQ.data;
|
|
3206
|
+
const byUser = useMemo2(() => {
|
|
3207
|
+
const map = /* @__PURE__ */ new Map();
|
|
3208
|
+
for (const p of summary2?.people ?? []) map.set(p.role === "host" ? SELF : p.guest_user_id, p);
|
|
3209
|
+
return map;
|
|
3210
|
+
}, [summary2]);
|
|
3211
|
+
const list5 = useMemo2(() => {
|
|
3212
|
+
const accepted = (overview2?.invitations ?? []).filter((m) => m.status === "accepted");
|
|
3213
|
+
const pending2 = (overview2?.invitations ?? []).filter((m) => m.status === "pending");
|
|
3214
|
+
const self = {
|
|
3215
|
+
id: "",
|
|
3216
|
+
guest_user_id: SELF,
|
|
3217
|
+
email: "You (host)",
|
|
3218
|
+
status: "accepted",
|
|
3219
|
+
group: overview2?.host_group ?? "",
|
|
3220
|
+
policy_id: "",
|
|
3221
|
+
top_agent: overview2?.host_top_agent ?? "",
|
|
3222
|
+
variant_id: "",
|
|
3223
|
+
created_at: "",
|
|
3224
|
+
accepted_at: ""
|
|
3225
|
+
};
|
|
3226
|
+
const named = groups2.filter((g) => g.name !== UNGROUPED);
|
|
3227
|
+
const ungrouped = groups2.find((g) => g.name === UNGROUPED) ?? { name: UNGROUPED, members: 0, color: "slate", policy_id: "" };
|
|
3228
|
+
const out = [];
|
|
3229
|
+
const push = (group, members) => {
|
|
3230
|
+
out.push({ kind: "group", group });
|
|
3231
|
+
for (const m of members) {
|
|
3232
|
+
out.push({ kind: "member", group, member: m, person: byUser.get(m.guest_user_id || SELF) });
|
|
3233
|
+
}
|
|
3234
|
+
};
|
|
3235
|
+
const inGroup = (name) => {
|
|
3236
|
+
const mine = accepted.filter((m) => (m.group || "") === name);
|
|
3237
|
+
if ((self.group || "") === name) return [self, ...mine];
|
|
3238
|
+
return mine;
|
|
3239
|
+
};
|
|
3240
|
+
for (const g of named) push(g, inGroup(g.name));
|
|
3241
|
+
push(ungrouped, inGroup(""));
|
|
3242
|
+
if (pending2.length > 0) {
|
|
3243
|
+
const waiting = { name: "Waiting to answer", members: pending2.length, color: "amber", policy_id: "" };
|
|
3244
|
+
out.push({ kind: "group", group: waiting });
|
|
3245
|
+
for (const m of pending2) out.push({ kind: "pending", group: waiting, member: m });
|
|
3246
|
+
}
|
|
3247
|
+
return out;
|
|
3248
|
+
}, [overview2, groups2, byUser]);
|
|
3249
|
+
const current = list5[Math.min(sel, Math.max(0, list5.length - 1))];
|
|
3250
|
+
const say = (msg) => setStatus(msg);
|
|
3251
|
+
const reloadAll = () => {
|
|
3252
|
+
overviewQ.reload();
|
|
3253
|
+
groupsQ.reload();
|
|
3254
|
+
summaryQ.reload();
|
|
3255
|
+
};
|
|
3256
|
+
const run = async (what, fn) => {
|
|
3257
|
+
setStatus(what + "\u2026");
|
|
3258
|
+
try {
|
|
3259
|
+
await fn();
|
|
3260
|
+
setStatus("\u2713 " + what);
|
|
3261
|
+
reloadAll();
|
|
3262
|
+
} catch (e) {
|
|
3263
|
+
setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
|
|
3264
|
+
}
|
|
3265
|
+
};
|
|
3266
|
+
const openPrompt = (p, initial = "") => {
|
|
3267
|
+
setPrompt(p);
|
|
3268
|
+
setDraft(initial);
|
|
3269
|
+
setStatus(null);
|
|
3270
|
+
setPick(0);
|
|
3271
|
+
};
|
|
3272
|
+
const submitPrompt = async (value) => {
|
|
3273
|
+
const v = value.trim();
|
|
3274
|
+
const p = prompt;
|
|
3275
|
+
setPrompt(null);
|
|
3276
|
+
setDraft("");
|
|
3277
|
+
if (!p) return;
|
|
3278
|
+
if (p === "invite") {
|
|
3279
|
+
if (!v) return;
|
|
3280
|
+
await run("Invited " + v, () => api.fleet.invite(v));
|
|
3281
|
+
return;
|
|
3282
|
+
}
|
|
3283
|
+
if (p === "newGroup") {
|
|
3284
|
+
if (!v) return;
|
|
3285
|
+
await run("Created " + v, () => api.fleet.createGroup(v));
|
|
3286
|
+
return;
|
|
3287
|
+
}
|
|
3288
|
+
if (p === "rename") {
|
|
3289
|
+
const g = current?.group;
|
|
3290
|
+
if (!v || !g || g.name === UNGROUPED) return;
|
|
3291
|
+
await run("Renamed to " + v, () => api.fleet.renameGroup(g.name, v));
|
|
3292
|
+
return;
|
|
3293
|
+
}
|
|
3294
|
+
if (p === "assign") {
|
|
3295
|
+
const m = current?.member;
|
|
3296
|
+
if (!m || !m.id) return;
|
|
3297
|
+
await run(v ? "Filed under " + v : "Unfiled", () => api.fleet.assignGroup([m.id], v));
|
|
3298
|
+
return;
|
|
3299
|
+
}
|
|
3300
|
+
if (p === "emails" || p === "telegram" || p === "endTime") {
|
|
3301
|
+
await saveSchedule(p, v);
|
|
3302
|
+
return;
|
|
3303
|
+
}
|
|
3304
|
+
};
|
|
3305
|
+
const reportQ = useLoader(() => view === "reports" ? api.fleet.report("day") : Promise.resolve(null), [view]);
|
|
3306
|
+
const scheduleQ = useLoader(() => view === "reports" ? api.fleet.reportSchedule() : Promise.resolve(null), [view]);
|
|
3307
|
+
const schedule = scheduleQ.data?.schedule;
|
|
3308
|
+
const saveSchedule = async (field, value) => {
|
|
3309
|
+
if (!schedule) return;
|
|
3310
|
+
const next = {};
|
|
3311
|
+
if (field === "emails") next.emails = splitTargets(value);
|
|
3312
|
+
if (field === "telegram") next.telegram = splitTargets(value);
|
|
3313
|
+
if (field === "endTime") next.endMinute = minutesOf(value);
|
|
3314
|
+
await run("Saved schedule", async () => {
|
|
3315
|
+
await api.fleet.setReportSchedule(next);
|
|
3316
|
+
scheduleQ.reload();
|
|
3317
|
+
reportQ.reload();
|
|
3318
|
+
});
|
|
3319
|
+
};
|
|
3320
|
+
const toggleSpan = async (span) => {
|
|
3321
|
+
if (!schedule) return;
|
|
3322
|
+
await run(span + " report " + (schedule[span] ? "off" : "on"), async () => {
|
|
3323
|
+
await api.fleet.setReportSchedule({ [span]: !schedule[span] });
|
|
3324
|
+
scheduleQ.reload();
|
|
3325
|
+
});
|
|
3326
|
+
};
|
|
3327
|
+
useInput3(
|
|
3328
|
+
(input, key) => {
|
|
3329
|
+
if (prompt) {
|
|
3330
|
+
if (key.escape) {
|
|
3331
|
+
setPrompt(null);
|
|
3332
|
+
setDraft("");
|
|
3333
|
+
}
|
|
3334
|
+
return;
|
|
3335
|
+
}
|
|
3336
|
+
if (confirm) {
|
|
3337
|
+
if (input === "y") {
|
|
3338
|
+
const target = confirm;
|
|
3339
|
+
setConfirm(null);
|
|
3340
|
+
void doDelete(target);
|
|
3341
|
+
} else {
|
|
3342
|
+
setConfirm(null);
|
|
3343
|
+
}
|
|
3344
|
+
return;
|
|
3345
|
+
}
|
|
3346
|
+
if (view === "person") {
|
|
3347
|
+
if (key.leftArrow || key.escape) setView("roster");
|
|
3348
|
+
return;
|
|
3349
|
+
}
|
|
3350
|
+
if (view === "reports") {
|
|
3351
|
+
if (key.leftArrow || key.escape) setView("roster");
|
|
3352
|
+
else if (input === "d") void toggleSpan("day");
|
|
3353
|
+
else if (input === "w") void toggleSpan("week");
|
|
3354
|
+
else if (input === "m") void toggleSpan("month");
|
|
3355
|
+
else if (input === "e") openPrompt("emails", (schedule?.emails ?? []).join(", "));
|
|
3356
|
+
else if (input === "t") openPrompt("telegram", (schedule?.telegram ?? []).join(", "));
|
|
3357
|
+
else if (input === "h") openPrompt("endTime", timeOf(schedule?.endMinute ?? 0));
|
|
3358
|
+
else if (input === "s") void run("Sent", () => api.fleet.sendReport("day"));
|
|
3359
|
+
else if (input === "r") {
|
|
3360
|
+
reportQ.reload();
|
|
3361
|
+
scheduleQ.reload();
|
|
3362
|
+
}
|
|
3363
|
+
return;
|
|
3364
|
+
}
|
|
3365
|
+
if (key.upArrow) setSel((s) => Math.max(0, s - 1));
|
|
3366
|
+
else if (key.downArrow) setSel((s) => Math.min(list5.length - 1, s + 1));
|
|
3367
|
+
else if (key.rightArrow || key.return) {
|
|
3368
|
+
if (current?.kind === "member") setView("person");
|
|
3369
|
+
} else if (input === "R") setView("reports");
|
|
3370
|
+
else if (input === "i") openPrompt("invite");
|
|
3371
|
+
else if (input === "n") openPrompt("newGroup");
|
|
3372
|
+
else if (input === "r") {
|
|
3373
|
+
if (current?.kind === "group" && current.group.name !== UNGROUPED) openPrompt("rename", current.group.name);
|
|
3374
|
+
} else if (input === "g") {
|
|
3375
|
+
if (current?.kind === "member" && current.member?.id) openPrompt("assign", current.member.group || "");
|
|
3376
|
+
} else if (input === "c") void cycleColor();
|
|
3377
|
+
else if (input === "p") openPolicyPicker();
|
|
3378
|
+
else if (input === "d") askDelete();
|
|
3379
|
+
else if (input === "x") reloadAll();
|
|
3380
|
+
},
|
|
3381
|
+
{ isActive: focused }
|
|
3382
|
+
);
|
|
3383
|
+
const cycleColor = async () => {
|
|
3384
|
+
const g = current?.group;
|
|
3385
|
+
if (!g || g.name === UNGROUPED || current?.kind !== "group") return;
|
|
3386
|
+
const next = GROUP_COLORS[(GROUP_COLORS.indexOf(g.color) + 1 + GROUP_COLORS.length) % GROUP_COLORS.length];
|
|
3387
|
+
await run("Coloured " + next, () => api.fleet.setGroupColor(g.name, next));
|
|
3388
|
+
};
|
|
3389
|
+
const openPolicyPicker = () => {
|
|
3390
|
+
if (!current) return;
|
|
3391
|
+
if (current.kind === "pending") return;
|
|
3392
|
+
if (policies.length === 0) {
|
|
3393
|
+
say("No policies to choose from yet.");
|
|
3394
|
+
return;
|
|
3395
|
+
}
|
|
3396
|
+
openPrompt("policy");
|
|
3397
|
+
};
|
|
3398
|
+
const choosePolicy = async (id) => {
|
|
3399
|
+
const target = current;
|
|
3400
|
+
setPrompt(null);
|
|
3401
|
+
if (!target) return;
|
|
3402
|
+
if (target.kind === "group") {
|
|
3403
|
+
if (target.group.name === UNGROUPED) {
|
|
3404
|
+
say("Uncategorized is the absence of a group, so it carries no policy.");
|
|
3405
|
+
return;
|
|
3406
|
+
}
|
|
3407
|
+
await run(
|
|
3408
|
+
id ? "Group runs " + policyLabel(id, policies) : "Group names no policy",
|
|
3409
|
+
() => api.fleet.setGroupPolicy(target.group.name, id)
|
|
3410
|
+
);
|
|
3411
|
+
return;
|
|
3412
|
+
}
|
|
3413
|
+
const m = target.member;
|
|
3414
|
+
if (!m?.id) {
|
|
3415
|
+
say("The host's own machine follows their group. Set the group's policy.");
|
|
3416
|
+
return;
|
|
3417
|
+
}
|
|
3418
|
+
await run(
|
|
3419
|
+
id ? "Pinned to " + policyLabel(id, policies) : "Back to their group",
|
|
3420
|
+
() => api.fleet.setUserPolicy([m.id], id)
|
|
3421
|
+
);
|
|
3422
|
+
};
|
|
3423
|
+
const askDelete = () => {
|
|
3424
|
+
if (!current) return;
|
|
3425
|
+
if (current.kind === "group") {
|
|
3426
|
+
if (current.group.name === UNGROUPED) return;
|
|
3427
|
+
setConfirm("group:" + current.group.name);
|
|
3428
|
+
return;
|
|
3429
|
+
}
|
|
3430
|
+
const m = current.member;
|
|
3431
|
+
if (!m?.id) {
|
|
3432
|
+
say("You cannot remove yourself from your own fleet.");
|
|
3433
|
+
return;
|
|
3434
|
+
}
|
|
3435
|
+
setConfirm("member:" + m.id);
|
|
3436
|
+
};
|
|
3437
|
+
const doDelete = async (target) => {
|
|
3438
|
+
const [kind, ...rest] = target.split(":");
|
|
3439
|
+
const value = rest.join(":");
|
|
3440
|
+
if (kind === "group") await run("Deleted " + value, () => api.fleet.deleteGroup(value));
|
|
3441
|
+
else await run("Revoked", () => api.fleet.revoke(value));
|
|
3442
|
+
};
|
|
3443
|
+
const loading = overviewQ.loading || groupsQ.loading;
|
|
3444
|
+
const error = overviewQ.error ?? groupsQ.error;
|
|
3445
|
+
if (overview2 && !overview2.host) {
|
|
3446
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
3447
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "This account does not run a fleet." }),
|
|
3448
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "Ask whoever manages your team for a seat, then come back." })
|
|
3449
|
+
] });
|
|
3450
|
+
}
|
|
3451
|
+
if (view === "person") {
|
|
3452
|
+
return /* @__PURE__ */ jsx4(PersonView, { row: current, policies, cols });
|
|
3453
|
+
}
|
|
3454
|
+
if (view === "reports") {
|
|
3455
|
+
return /* @__PURE__ */ jsx4(
|
|
3456
|
+
ReportsView,
|
|
3457
|
+
{
|
|
3458
|
+
report: reportQ.data,
|
|
3459
|
+
loading: reportQ.loading,
|
|
3460
|
+
error: reportQ.error,
|
|
3461
|
+
schedule,
|
|
3462
|
+
next: scheduleQ.data?.next ?? 0,
|
|
3463
|
+
status,
|
|
3464
|
+
prompt,
|
|
3465
|
+
draft,
|
|
3466
|
+
onChange: setDraft,
|
|
3467
|
+
onSubmit: submitPrompt,
|
|
3468
|
+
cols
|
|
3469
|
+
}
|
|
3470
|
+
);
|
|
3471
|
+
}
|
|
3472
|
+
const visible = windowed(list5, sel, Math.max(4, rows - 6));
|
|
3473
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
3474
|
+
/* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
|
|
3475
|
+
(overview2?.used ?? 0) + " of " + (overview2?.seats ?? 0) + " seats",
|
|
3476
|
+
summary2 ? " " + summary2.totals.calls + " calls \xB7 " + summary2.totals.denied + " denied (30d)" : ""
|
|
3477
|
+
] }) }),
|
|
3478
|
+
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx4(DataView, { loading, error, empty: list5.length === 0, emptyText: "Nobody on this fleet yet. Press i to invite somebody.", children: visible.map((row, i) => {
|
|
3479
|
+
const index = list5.indexOf(row);
|
|
3480
|
+
const selected = index === sel;
|
|
3481
|
+
if (row.kind === "group") {
|
|
3482
|
+
return /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
3483
|
+
/* @__PURE__ */ jsx4(Text4, { color: selected ? theme.accentBright : theme.accent, bold: true, children: (selected ? "\u25B8 " : " ") + label(row.group).padEnd(22) }),
|
|
3484
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: String(row.group.members).padStart(3) + " people " }),
|
|
3485
|
+
/* @__PURE__ */ jsx4(Text4, { color: row.group.policy_id ? theme.ok : theme.dim, children: row.group.policy_id ? policyLabel(row.group.policy_id, policies) : "no group policy" })
|
|
3486
|
+
] }, "g" + index + i);
|
|
3487
|
+
}
|
|
3488
|
+
const m = row.member;
|
|
3489
|
+
const pinned = Boolean(m.policy_id);
|
|
3490
|
+
const runs = pinned ? m.policy_id : row.group.policy_id;
|
|
3491
|
+
return /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
3492
|
+
/* @__PURE__ */ jsx4(Text4, { color: selected ? theme.accentBright : void 0, children: (selected ? " \u25B8 " : " ") + truncate(m.email, 30).padEnd(31) }),
|
|
3493
|
+
row.kind === "pending" ? /* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: "waiting" }) : /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
3494
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: String(row.person?.calls ?? 0).padStart(6) + " calls " }),
|
|
3495
|
+
/* @__PURE__ */ jsx4(Text4, { color: (row.person?.denied ?? 0) > 0 ? theme.bad : theme.dim, children: String(row.person?.denied ?? 0).padStart(4) + " deny " }),
|
|
3496
|
+
/* @__PURE__ */ jsx4(Text4, { color: pinned ? theme.warn : theme.dim, children: runs ? policyLabel(runs, policies) + (pinned ? " (only them)" : "") : "\u2014" })
|
|
3497
|
+
] })
|
|
3498
|
+
] }, "m" + index + i);
|
|
3499
|
+
}) }) }),
|
|
3500
|
+
confirm ? /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: confirm.startsWith("group:") ? "Delete this group? Its people become uncategorized. (y/n)" : "Revoke this person? Their machines stop being managed. (y/n)" }) }) : null,
|
|
3501
|
+
prompt === "policy" ? /* @__PURE__ */ jsx4(
|
|
3502
|
+
PolicyPicker,
|
|
3503
|
+
{
|
|
3504
|
+
policies,
|
|
3505
|
+
pick,
|
|
3506
|
+
onMove: setPick,
|
|
3507
|
+
onChoose: choosePolicy,
|
|
3508
|
+
onCancel: () => setPrompt(null),
|
|
3509
|
+
focused,
|
|
3510
|
+
target: current?.kind === "group" ? label(current.group) : current?.member?.email ?? ""
|
|
3511
|
+
}
|
|
3512
|
+
) : prompt ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
|
|
3513
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.accent, children: promptLabel(prompt) + " " }),
|
|
3514
|
+
/* @__PURE__ */ jsx4(TextInput2, { value: draft, onChange: setDraft, onSubmit: submitPrompt })
|
|
3515
|
+
] }) : null,
|
|
3516
|
+
status ? /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) }) : null
|
|
3517
|
+
] });
|
|
3518
|
+
}
|
|
3519
|
+
var label = (g) => g.name === UNGROUPED ? "Uncategorized" : g.name;
|
|
3520
|
+
var promptLabel = (p) => {
|
|
3521
|
+
switch (p) {
|
|
3522
|
+
case "invite":
|
|
3523
|
+
return "Invite (email):";
|
|
3524
|
+
case "newGroup":
|
|
3525
|
+
return "New group:";
|
|
3526
|
+
case "rename":
|
|
3527
|
+
return "Rename to:";
|
|
3528
|
+
case "assign":
|
|
3529
|
+
return "File under (blank = uncategorized):";
|
|
3530
|
+
case "emails":
|
|
3531
|
+
return "Email the report to:";
|
|
3532
|
+
case "telegram":
|
|
3533
|
+
return "Telegram chat id or @handle:";
|
|
3534
|
+
case "endTime":
|
|
3535
|
+
return "A day ends at (HH:MM):";
|
|
3536
|
+
default:
|
|
3537
|
+
return "";
|
|
3538
|
+
}
|
|
3539
|
+
};
|
|
3540
|
+
function windowed(list5, sel, height) {
|
|
3541
|
+
if (list5.length <= height) return list5;
|
|
3542
|
+
const start = Math.min(Math.max(0, sel - Math.floor(height / 2)), list5.length - height);
|
|
3543
|
+
return list5.slice(start, start + height);
|
|
3544
|
+
}
|
|
3545
|
+
var splitTargets = (v) => v.split(/[\s,;]+/).map((s) => s.trim()).filter(Boolean);
|
|
3546
|
+
var minutesOf = (v) => {
|
|
3547
|
+
const [h, m] = v.split(":");
|
|
3548
|
+
const total = (Number(h) || 0) * 60 + (Number(m) || 0);
|
|
3549
|
+
return total >= 0 && total <= 1439 ? total : 0;
|
|
3550
|
+
};
|
|
3551
|
+
var timeOf = (minute) => String(Math.floor(minute / 60)).padStart(2, "0") + ":" + String(minute % 60).padStart(2, "0");
|
|
3552
|
+
function PolicyPicker({
|
|
3553
|
+
policies,
|
|
3554
|
+
pick,
|
|
3555
|
+
onMove,
|
|
3556
|
+
onChoose,
|
|
3557
|
+
onCancel,
|
|
3558
|
+
focused,
|
|
3559
|
+
target
|
|
3560
|
+
}) {
|
|
3561
|
+
const options = [
|
|
3562
|
+
{ id: "", name: "No policy of its own", detail: "follow the level above" },
|
|
3563
|
+
...policies.map((p) => ({
|
|
3564
|
+
id: p.id,
|
|
3565
|
+
name: p.name,
|
|
3566
|
+
detail: (p.rules?.length ?? 0) + " rules \xB7 " + p.mode
|
|
3567
|
+
}))
|
|
3568
|
+
];
|
|
3569
|
+
useInput3(
|
|
3570
|
+
(_input, key) => {
|
|
3571
|
+
if (key.upArrow) onMove(Math.max(0, pick - 1));
|
|
3572
|
+
else if (key.downArrow) onMove(Math.min(options.length - 1, pick + 1));
|
|
3573
|
+
else if (key.return) onChoose(options[pick]?.id ?? "");
|
|
3574
|
+
else if (key.escape || key.leftArrow) onCancel();
|
|
3575
|
+
},
|
|
3576
|
+
{ isActive: focused }
|
|
3577
|
+
);
|
|
3578
|
+
const visible = windowed(options, pick, 8);
|
|
3579
|
+
return /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
|
|
3580
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.accent, children: "Policy for " + target + " (\u2191\u2193 enter \xB7 esc cancel)" }),
|
|
3581
|
+
visible.map((o, i) => {
|
|
3582
|
+
const index = options.indexOf(o);
|
|
3583
|
+
const selected = index === pick;
|
|
3584
|
+
return /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
3585
|
+
/* @__PURE__ */ jsx4(Text4, { color: selected ? theme.accentBright : void 0, children: (selected ? "\u25B8 " : " ") + truncate(o.name, 30).padEnd(31) }),
|
|
3586
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: o.detail })
|
|
3587
|
+
] }, o.id + i);
|
|
3588
|
+
})
|
|
3589
|
+
] });
|
|
3590
|
+
}
|
|
3591
|
+
function PersonView({ row, policies, cols }) {
|
|
3592
|
+
const m = row?.member;
|
|
3593
|
+
const guest = m?.guest_user_id || SELF;
|
|
3594
|
+
const analyticsQ = useLoader(() => api.fleet.analytics(guest), [guest]);
|
|
3595
|
+
if (!m) return /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "Nobody selected." });
|
|
3596
|
+
const a = analyticsQ.data ?? {};
|
|
3597
|
+
const totals = a["totals"] ?? {};
|
|
3598
|
+
const agents = a["agents"] ?? [];
|
|
3599
|
+
const tools = a["tools"] ?? [];
|
|
3600
|
+
const denies = a["deny_reasons"] ?? [];
|
|
3601
|
+
const runs = m.policy_id || row?.group.policy_id || "";
|
|
3602
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
3603
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
3604
|
+
/* @__PURE__ */ jsx4(Text4, { bold: true, color: theme.accentBright, children: m.email }),
|
|
3605
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " " + (row?.group ? label(row.group) : "") })
|
|
3606
|
+
] }),
|
|
3607
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
3608
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "Runs " }),
|
|
3609
|
+
/* @__PURE__ */ jsx4(Text4, { color: m.policy_id ? theme.warn : theme.ok, children: runs ? policyLabel(runs, policies) + (m.policy_id ? " (only them)" : " (their group)") : "the project default" })
|
|
3610
|
+
] }),
|
|
3611
|
+
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(DataView, { loading: analyticsQ.loading, error: analyticsQ.error, children: /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
3612
|
+
/* @__PURE__ */ jsxs4(Text4, { children: [
|
|
3613
|
+
String(totals.calls ?? 0) + " calls ",
|
|
3614
|
+
/* @__PURE__ */ jsx4(Text4, { color: (totals.denied ?? 0) > 0 ? theme.bad : theme.dim, children: String(totals.denied ?? 0) + " denied" }),
|
|
3615
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " " + String(totals.sessions ?? 0) + " sessions" })
|
|
3616
|
+
] }),
|
|
3617
|
+
denies.length > 0 ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
|
|
3618
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "What was stopped" }),
|
|
3619
|
+
/* @__PURE__ */ jsx4(
|
|
3620
|
+
Table,
|
|
3621
|
+
{
|
|
3622
|
+
columns: [{ header: "reason", width: Math.min(48, cols - 14) }, { header: "times", width: 8 }],
|
|
3623
|
+
rows: denies.slice(0, 6).map((d) => [{ value: d.name, color: theme.bad }, { value: String(d.calls) }])
|
|
3624
|
+
}
|
|
3625
|
+
)
|
|
3626
|
+
] }) : null,
|
|
3627
|
+
tools.length > 0 ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
|
|
3628
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "Most used tools" }),
|
|
3629
|
+
/* @__PURE__ */ jsx4(
|
|
3630
|
+
Table,
|
|
3631
|
+
{
|
|
3632
|
+
columns: [{ header: "tool", width: Math.min(48, cols - 14) }, { header: "calls", width: 8 }],
|
|
3633
|
+
rows: tools.slice(0, 6).map((t) => [{ value: t.name }, { value: String(t.calls) }])
|
|
3634
|
+
}
|
|
3635
|
+
)
|
|
3636
|
+
] }) : null,
|
|
3637
|
+
agents.length > 0 ? /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "Agents: " + agents.slice(0, 5).map((x) => x.name + " (" + x.calls + ")").join(" ") }) }) : null
|
|
3638
|
+
] }) }) }),
|
|
3639
|
+
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "\u2190 back" }) })
|
|
3640
|
+
] });
|
|
3641
|
+
}
|
|
3642
|
+
function ReportsView({
|
|
3643
|
+
report: report2,
|
|
3644
|
+
loading,
|
|
3645
|
+
error,
|
|
3646
|
+
schedule,
|
|
3647
|
+
next,
|
|
3648
|
+
status,
|
|
3649
|
+
prompt,
|
|
3650
|
+
draft,
|
|
3651
|
+
onChange,
|
|
3652
|
+
onSubmit,
|
|
3653
|
+
cols
|
|
3654
|
+
}) {
|
|
3655
|
+
const on = (v) => v ? theme.ok : theme.dim;
|
|
3656
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
3657
|
+
/* @__PURE__ */ jsx4(DataView, { loading, error, children: /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
3658
|
+
/* @__PURE__ */ jsx4(Text4, { bold: true, children: report2?.headline ?? "" }),
|
|
3659
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: report2?.title ?? "" }),
|
|
3660
|
+
report2 && report2.top_denies.length > 0 ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
|
|
3661
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "What was stopped" }),
|
|
3662
|
+
/* @__PURE__ */ jsx4(
|
|
3663
|
+
Table,
|
|
3664
|
+
{
|
|
3665
|
+
columns: [{ header: "reason", width: Math.min(48, cols - 14) }, { header: "times", width: 8 }],
|
|
3666
|
+
rows: report2.top_denies.slice(0, 5).map((d) => [{ value: d.name, color: theme.bad }, { value: String(d.calls) }])
|
|
3667
|
+
}
|
|
3668
|
+
)
|
|
3669
|
+
] }) : null
|
|
3670
|
+
] }) }),
|
|
3671
|
+
/* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
|
|
3672
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "Scheduled" }),
|
|
3673
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
3674
|
+
/* @__PURE__ */ jsx4(Text4, { color: on(schedule?.day), children: "daily " }),
|
|
3675
|
+
/* @__PURE__ */ jsx4(Text4, { color: on(schedule?.week), children: "weekly " }),
|
|
3676
|
+
/* @__PURE__ */ jsx4(Text4, { color: on(schedule?.month), children: "monthly " }),
|
|
3677
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "day ends " + timeOf(schedule?.endMinute ?? 0) + " UTC" })
|
|
3678
|
+
] }),
|
|
3679
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "to " + ((schedule?.emails ?? []).concat(schedule?.telegram ?? []).join(", ") || "nobody yet") }),
|
|
3680
|
+
next > 0 ? /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "next " + new Date(next * 1e3).toISOString().slice(0, 16).replace("T", " ") + " UTC" }) : null
|
|
3681
|
+
] }),
|
|
3682
|
+
prompt ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
|
|
3683
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.accent, children: promptLabel(prompt) + " " }),
|
|
3684
|
+
/* @__PURE__ */ jsx4(TextInput2, { value: draft, onChange, onSubmit })
|
|
3685
|
+
] }) : null,
|
|
3686
|
+
status ? /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) }) : null,
|
|
3687
|
+
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "d daily \xB7 w weekly \xB7 m monthly \xB7 h day ends \xB7 e email \xB7 t telegram \xB7 s send now \xB7 \u2190 back" }) })
|
|
3688
|
+
] });
|
|
3689
|
+
}
|
|
3690
|
+
|
|
3691
|
+
// src/tui/panels/Policies.tsx
|
|
3692
|
+
import { Box as Box6, Text as Text6, useInput as useInput5 } from "ink";
|
|
3693
|
+
import TextInput4 from "ink-text-input";
|
|
3694
|
+
import { useEffect as useEffect5, useState as useState6 } from "react";
|
|
2949
3695
|
|
|
2950
3696
|
// src/tui/panels/DryRun.tsx
|
|
2951
|
-
import { Box as
|
|
2952
|
-
import
|
|
2953
|
-
import { useEffect as
|
|
2954
|
-
import { jsx as
|
|
3697
|
+
import { Box as Box5, Text as Text5, useInput as useInput4 } from "ink";
|
|
3698
|
+
import TextInput3 from "ink-text-input";
|
|
3699
|
+
import { useEffect as useEffect4, useState as useState5 } from "react";
|
|
3700
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2955
3701
|
var DEFAULT_LIMIT = 1e3;
|
|
2956
3702
|
var MAX_LIMIT = 5e3;
|
|
2957
3703
|
var pendingPolicyId = null;
|
|
@@ -2962,15 +3708,15 @@ var pct = (n, d) => d ? (n / d * 100).toFixed(1) + "%" : "0%";
|
|
|
2962
3708
|
function DryRunPanel({ focused }) {
|
|
2963
3709
|
const list5 = useLoader(() => api.policies.list());
|
|
2964
3710
|
const policies = list5.data?.policies ?? [];
|
|
2965
|
-
const [pi, setPi] =
|
|
2966
|
-
const [limit, setLimit] =
|
|
2967
|
-
const [editLogs, setEditLogs] =
|
|
3711
|
+
const [pi, setPi] = useState5(0);
|
|
3712
|
+
const [limit, setLimit] = useState5(DEFAULT_LIMIT);
|
|
3713
|
+
const [editLogs, setEditLogs] = useState5(null);
|
|
2968
3714
|
const statsQ = useLoader(() => api.stats.get());
|
|
2969
3715
|
const totalLogs = statsQ.data?.total_calls ?? null;
|
|
2970
|
-
const [res, setRes] =
|
|
2971
|
-
const [running, setRunning] =
|
|
2972
|
-
const [err2, setErr] =
|
|
2973
|
-
const [off, setOff] =
|
|
3716
|
+
const [res, setRes] = useState5(null);
|
|
3717
|
+
const [running, setRunning] = useState5(false);
|
|
3718
|
+
const [err2, setErr] = useState5(null);
|
|
3719
|
+
const [off, setOff] = useState5(0);
|
|
2974
3720
|
const { cols, rows } = usePanelSize();
|
|
2975
3721
|
const selected = policies[Math.min(pi, Math.max(0, policies.length - 1))];
|
|
2976
3722
|
function run(policyIdx = pi, lim = limit) {
|
|
@@ -2982,7 +3728,7 @@ function DryRunPanel({ focused }) {
|
|
|
2982
3728
|
setOff(0);
|
|
2983
3729
|
api.policies.backtest({ rules: p.rules ?? [], mode: p.mode, limit: lim }).then((r) => setRes(r)).catch((e) => setErr(e instanceof Error ? e.message : String(e))).finally(() => setRunning(false));
|
|
2984
3730
|
}
|
|
2985
|
-
|
|
3731
|
+
useEffect4(() => {
|
|
2986
3732
|
if (policies.length === 0 || res || running || err2) return;
|
|
2987
3733
|
let idx = 0;
|
|
2988
3734
|
if (pendingPolicyId) {
|
|
@@ -2993,7 +3739,7 @@ function DryRunPanel({ focused }) {
|
|
|
2993
3739
|
setPi(idx);
|
|
2994
3740
|
run(idx, limit);
|
|
2995
3741
|
}, [policies.length]);
|
|
2996
|
-
|
|
3742
|
+
useInput4((input, key) => {
|
|
2997
3743
|
if (!focused || editLogs !== null) return;
|
|
2998
3744
|
if (key.upArrow) setOff((n) => Math.max(0, n - 1));
|
|
2999
3745
|
else if (key.downArrow) setOff((n) => n + 1);
|
|
@@ -3015,10 +3761,10 @@ function DryRunPanel({ focused }) {
|
|
|
3015
3761
|
}
|
|
3016
3762
|
const w = Math.max(40, cols);
|
|
3017
3763
|
const lines = [];
|
|
3018
|
-
const head = (t) => lines.push(/* @__PURE__ */
|
|
3019
|
-
const dim2 = (t) => lines.push(/* @__PURE__ */
|
|
3020
|
-
const row = (t) => lines.push(/* @__PURE__ */
|
|
3021
|
-
const blank = () => lines.push(/* @__PURE__ */
|
|
3764
|
+
const head = (t) => lines.push(/* @__PURE__ */ jsx5(Text5, { bold: true, color: theme.accentBright, children: t }, `h${lines.length}`));
|
|
3765
|
+
const dim2 = (t) => lines.push(/* @__PURE__ */ jsx5(Text5, { color: theme.dim, wrap: "truncate", children: t }, `d${lines.length}`));
|
|
3766
|
+
const row = (t) => lines.push(/* @__PURE__ */ jsx5(Text5, { wrap: "truncate", children: t }, `r${lines.length}`));
|
|
3767
|
+
const blank = () => lines.push(/* @__PURE__ */ jsx5(Text5, { children: " " }, `b${lines.length}`));
|
|
3022
3768
|
if (res) {
|
|
3023
3769
|
const s = res.summary;
|
|
3024
3770
|
const denyAfter = s.would_deny;
|
|
@@ -3029,13 +3775,13 @@ function DryRunPanel({ focused }) {
|
|
|
3029
3775
|
head("Summary");
|
|
3030
3776
|
row(` evaluated ${s.evaluated} would allow ${s.would_allow} would deny ${s.would_deny}`);
|
|
3031
3777
|
lines.push(
|
|
3032
|
-
/* @__PURE__ */
|
|
3778
|
+
/* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
|
|
3033
3779
|
" newly blocked ",
|
|
3034
|
-
/* @__PURE__ */
|
|
3780
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.bad, bold: true, children: String(s.newly_blocked) }),
|
|
3035
3781
|
" newly allowed ",
|
|
3036
|
-
/* @__PURE__ */
|
|
3782
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.ok, bold: true, children: String(s.newly_allowed) }),
|
|
3037
3783
|
" unchanged ",
|
|
3038
|
-
/* @__PURE__ */
|
|
3784
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: String(s.unchanged) })
|
|
3039
3785
|
] }, `sum${lines.length}`)
|
|
3040
3786
|
);
|
|
3041
3787
|
row(` deny rate ${before} -> ${after} (${Number(ppShift) > 0 ? "+" : ""}${ppShift}pp)`);
|
|
@@ -3070,12 +3816,12 @@ function DryRunPanel({ focused }) {
|
|
|
3070
3816
|
const flip = `${c2.original}->${c2.predicted}`;
|
|
3071
3817
|
const newlyAllowed = c2.predicted === "ALLOW";
|
|
3072
3818
|
lines.push(
|
|
3073
|
-
/* @__PURE__ */
|
|
3819
|
+
/* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
|
|
3074
3820
|
" ",
|
|
3075
|
-
/* @__PURE__ */
|
|
3076
|
-
/* @__PURE__ */
|
|
3077
|
-
/* @__PURE__ */
|
|
3078
|
-
/* @__PURE__ */
|
|
3821
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.accent, children: truncate(c2.tool, 16).padEnd(17) }),
|
|
3822
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: truncate(c2.agent || "-", 14).padEnd(15) }),
|
|
3823
|
+
/* @__PURE__ */ jsx5(Text5, { color: newlyAllowed ? theme.ok : theme.bad, children: flip.padEnd(14) }),
|
|
3824
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: truncate(c2.preview.replace(/\s+/g, " "), Math.max(10, w - 50)) })
|
|
3079
3825
|
] }, `c${lines.length}`)
|
|
3080
3826
|
);
|
|
3081
3827
|
}
|
|
@@ -3088,36 +3834,36 @@ function DryRunPanel({ focused }) {
|
|
|
3088
3834
|
const win = lines.slice(start, start + budget);
|
|
3089
3835
|
const above = start;
|
|
3090
3836
|
const below = Math.max(0, lines.length - (start + budget));
|
|
3091
|
-
return /* @__PURE__ */
|
|
3092
|
-
/* @__PURE__ */
|
|
3093
|
-
/* @__PURE__ */
|
|
3094
|
-
/* @__PURE__ */
|
|
3095
|
-
/* @__PURE__ */
|
|
3096
|
-
/* @__PURE__ */
|
|
3837
|
+
return /* @__PURE__ */ jsx5(DataView, { loading: list5.loading && !list5.data, error: list5.error, children: /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
3838
|
+
/* @__PURE__ */ jsx5(Text5, { bold: true, color: theme.accentBright, children: "Policy Dry Run" }),
|
|
3839
|
+
/* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
|
|
3840
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " policy " }),
|
|
3841
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.accent, children: "\u2039 " + truncate(selected?.name ?? "none", 30) + " \u203A" }),
|
|
3842
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: ` ${policies.length ? pi + 1 : 0}/${policies.length} press p \xB7 mode ${selected?.mode ?? "-"}` })
|
|
3097
3843
|
] }),
|
|
3098
|
-
editLogs !== null ? /* @__PURE__ */
|
|
3099
|
-
/* @__PURE__ */
|
|
3100
|
-
/* @__PURE__ */
|
|
3101
|
-
/* @__PURE__ */
|
|
3102
|
-
/* @__PURE__ */
|
|
3103
|
-
] }) : /* @__PURE__ */
|
|
3104
|
-
/* @__PURE__ */
|
|
3105
|
-
/* @__PURE__ */
|
|
3106
|
-
/* @__PURE__ */
|
|
3844
|
+
editLogs !== null ? /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
3845
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " logs " }),
|
|
3846
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.warn, children: "how many recent logs to replay: " }),
|
|
3847
|
+
/* @__PURE__ */ jsx5(TextInput3, { value: editLogs, onChange: setEditLogs, onSubmit: commitLogs }),
|
|
3848
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: ` (max ${Math.min(MAX_LIMIT, totalLogs && totalLogs > 0 ? totalLogs : MAX_LIMIT)})` })
|
|
3849
|
+
] }) : /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
|
|
3850
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " logs " }),
|
|
3851
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.accent, children: "\u2039 " + String(limit) + " \u203A" }),
|
|
3852
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: ` press n to type a number max ${Math.min(MAX_LIMIT, totalLogs && totalLogs > 0 ? totalLogs : MAX_LIMIT)}${res?.sampled != null ? ` replayed ${res.sampled}` : ""}` })
|
|
3107
3853
|
] }),
|
|
3108
|
-
/* @__PURE__ */
|
|
3109
|
-
running ? /* @__PURE__ */
|
|
3110
|
-
err2 ? /* @__PURE__ */
|
|
3111
|
-
!running && !err2 && !res ? /* @__PURE__ */
|
|
3112
|
-
/* @__PURE__ */
|
|
3854
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, wrap: "truncate", children: focused ? `\u2191\u2193 scroll \xB7 p policy \xB7 n logs \xB7 r re-run${above ? ` \xB7 \u25B2${above}` : ""}${below ? ` \xB7 \u25BC${below}` : ""}` : "press \u2192 to open" }),
|
|
3855
|
+
running ? /* @__PURE__ */ jsx5(Text5, { color: theme.warn, children: "replaying recent traffic\u2026" }) : null,
|
|
3856
|
+
err2 ? /* @__PURE__ */ jsx5(Text5, { color: theme.bad, wrap: "truncate", children: "\u2717 " + err2 }) : null,
|
|
3857
|
+
!running && !err2 && !res ? /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: "no result yet, press r to run" }) : null,
|
|
3858
|
+
/* @__PURE__ */ jsx5(Box5, { marginTop: 1, flexDirection: "column", children: win })
|
|
3113
3859
|
] }) });
|
|
3114
3860
|
}
|
|
3115
3861
|
|
|
3116
3862
|
// src/tui/nav.ts
|
|
3117
3863
|
var pending = null;
|
|
3118
3864
|
var subs = /* @__PURE__ */ new Set();
|
|
3119
|
-
function requestSection(
|
|
3120
|
-
pending =
|
|
3865
|
+
function requestSection(label2) {
|
|
3866
|
+
pending = label2;
|
|
3121
3867
|
for (const fn of subs) fn();
|
|
3122
3868
|
}
|
|
3123
3869
|
function takePendingSection() {
|
|
@@ -3133,7 +3879,7 @@ function onSectionRequest(fn) {
|
|
|
3133
3879
|
}
|
|
3134
3880
|
|
|
3135
3881
|
// src/tui/panels/Policies.tsx
|
|
3136
|
-
import { Fragment as
|
|
3882
|
+
import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
3137
3883
|
var CTYPES = ["none", "command", "path", "filename", "url"];
|
|
3138
3884
|
var CTYPE_GROUP = {
|
|
3139
3885
|
command: "commandConstraints",
|
|
@@ -3237,23 +3983,23 @@ function PoliciesPanel({ focused }) {
|
|
|
3237
3983
|
const activeId = activeQ.data?.policy?.id ?? null;
|
|
3238
3984
|
const activeBy = activeQ.data?.matched_by;
|
|
3239
3985
|
const { rows: panelRows } = usePanelSize();
|
|
3240
|
-
const [view, setView] =
|
|
3241
|
-
const [pi, setPi] =
|
|
3242
|
-
const [ri, setRi] =
|
|
3243
|
-
const [fi, setFi] =
|
|
3244
|
-
const [rules, setRules] =
|
|
3245
|
-
const [mode, setMode] =
|
|
3246
|
-
const [dirty, setDirty] =
|
|
3247
|
-
const [editing, setEditing] =
|
|
3248
|
-
const [editVal, setEditVal] =
|
|
3249
|
-
const [status, setStatus] =
|
|
3250
|
-
const [pendingDel, setPendingDel] =
|
|
3251
|
-
const [permCursor, setPermCursor] =
|
|
3252
|
-
const [matchSel, setMatchSel] =
|
|
3253
|
-
const [busyCreate, setBusyCreate] =
|
|
3986
|
+
const [view, setView] = useState6("list");
|
|
3987
|
+
const [pi, setPi] = useState6(0);
|
|
3988
|
+
const [ri, setRi] = useState6(0);
|
|
3989
|
+
const [fi, setFi] = useState6(0);
|
|
3990
|
+
const [rules, setRules] = useState6([]);
|
|
3991
|
+
const [mode, setMode] = useState6("denylist");
|
|
3992
|
+
const [dirty, setDirty] = useState6(false);
|
|
3993
|
+
const [editing, setEditing] = useState6(false);
|
|
3994
|
+
const [editVal, setEditVal] = useState6("");
|
|
3995
|
+
const [status, setStatus] = useState6(null);
|
|
3996
|
+
const [pendingDel, setPendingDel] = useState6(null);
|
|
3997
|
+
const [permCursor, setPermCursor] = useState6(0);
|
|
3998
|
+
const [matchSel, setMatchSel] = useState6(0);
|
|
3999
|
+
const [busyCreate, setBusyCreate] = useState6(false);
|
|
3254
4000
|
const selected = policies[Math.min(pi, Math.max(0, policies.length - 1))];
|
|
3255
4001
|
const detail = useLoader(() => selected ? api.policies.get(selected.id) : Promise.resolve(null), [selected?.id]);
|
|
3256
|
-
|
|
4002
|
+
useEffect5(() => {
|
|
3257
4003
|
if (detail.data) {
|
|
3258
4004
|
setRules(detail.data.rules);
|
|
3259
4005
|
setMode(detail.data.mode ?? "denylist");
|
|
@@ -3304,7 +4050,7 @@ function PoliciesPanel({ focused }) {
|
|
|
3304
4050
|
setDirty(false);
|
|
3305
4051
|
setStatus("discarded");
|
|
3306
4052
|
};
|
|
3307
|
-
const [creating, setCreating] =
|
|
4053
|
+
const [creating, setCreating] = useState6(null);
|
|
3308
4054
|
function commitNewPolicy(name) {
|
|
3309
4055
|
const n = name.trim();
|
|
3310
4056
|
setCreating(null);
|
|
@@ -3319,10 +4065,10 @@ function PoliciesPanel({ focused }) {
|
|
|
3319
4065
|
setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
|
|
3320
4066
|
});
|
|
3321
4067
|
}
|
|
3322
|
-
|
|
4068
|
+
useEffect5(() => {
|
|
3323
4069
|
if (busyCreate && policies.length > 0) setBusyCreate(false);
|
|
3324
4070
|
}, [busyCreate, policies.length]);
|
|
3325
|
-
|
|
4071
|
+
useInput5(
|
|
3326
4072
|
(input, key) => {
|
|
3327
4073
|
if (creating !== null) return;
|
|
3328
4074
|
if (key.ctrl && input === "r" && !editing) {
|
|
@@ -3493,39 +4239,39 @@ function PoliciesPanel({ focused }) {
|
|
|
3493
4239
|
const lWin = policies.slice(lStart, lStart + lBudget);
|
|
3494
4240
|
const lAbove = lStart;
|
|
3495
4241
|
const lBelow = Math.max(0, policies.length - (lStart + lBudget));
|
|
3496
|
-
return /* @__PURE__ */
|
|
3497
|
-
/* @__PURE__ */
|
|
3498
|
-
/* @__PURE__ */
|
|
3499
|
-
activeQ.loading && !activeQ.data ? /* @__PURE__ */
|
|
3500
|
-
/* @__PURE__ */
|
|
4242
|
+
return /* @__PURE__ */ jsx6(DataView, { loading: list5.loading && !list5.data, error: list5.error, empty: !!list5.data && policies.length === 0 && creating === null && !busyCreate, emptyText: "No policies yet \u2014 press n to create one.", children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
4243
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
4244
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "active: " }),
|
|
4245
|
+
activeQ.loading && !activeQ.data ? /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "\u2026" }) : activeName ? /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
4246
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.ok, bold: true, children: [
|
|
3501
4247
|
"\u25CF ",
|
|
3502
4248
|
truncate(activeName, 28)
|
|
3503
4249
|
] }),
|
|
3504
|
-
/* @__PURE__ */
|
|
3505
|
-
] }) : /* @__PURE__ */
|
|
4250
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: activeBy ? ` (${activeBy})` : "" })
|
|
4251
|
+
] }) : /* @__PURE__ */ jsx6(Text6, { color: theme.bad, bold: true, children: "\u25CB NONE \u2014 nothing enforced, grants refused" })
|
|
3506
4252
|
] }),
|
|
3507
|
-
/* @__PURE__ */
|
|
3508
|
-
creating !== null ? /* @__PURE__ */
|
|
3509
|
-
/* @__PURE__ */
|
|
3510
|
-
/* @__PURE__ */
|
|
4253
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? `\u2191\u2193 select \xB7 enter open \xB7 n new \xB7 a activate \xB7 x deactivate \xB7 d delete \xB7 ^R refresh${lAbove ? ` \xB7 \u25B2${lAbove}` : ""}${lBelow ? ` \xB7 \u25BC${lBelow}` : ""}` : "press \u2192 to browse" }),
|
|
4254
|
+
creating !== null ? /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
4255
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.warn, children: "new policy name: " }),
|
|
4256
|
+
/* @__PURE__ */ jsx6(TextInput4, { value: creating, onChange: setCreating, onSubmit: commitNewPolicy })
|
|
3511
4257
|
] }) : null,
|
|
3512
|
-
/* @__PURE__ */
|
|
4258
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexDirection: "column", children: lWin.map((p, wi) => {
|
|
3513
4259
|
const i = lStart + wi;
|
|
3514
|
-
return /* @__PURE__ */
|
|
4260
|
+
return /* @__PURE__ */ jsxs6(Text6, { color: i === pi ? theme.accentBright : void 0, bold: i === pi, children: [
|
|
3515
4261
|
(i === pi ? "\u25B8 " : " ") + truncate(p.name, 26).padEnd(27),
|
|
3516
|
-
/* @__PURE__ */
|
|
4262
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
3517
4263
|
p.mode.padEnd(10),
|
|
3518
4264
|
" ",
|
|
3519
4265
|
p.rules.length,
|
|
3520
4266
|
" rules"
|
|
3521
4267
|
] }),
|
|
3522
|
-
p.id === activeId ? /* @__PURE__ */
|
|
4268
|
+
p.id === activeId ? /* @__PURE__ */ jsx6(Text6, { color: theme.ok, bold: true, children: " \u25CF ACTIVE" }) : null
|
|
3523
4269
|
] }, p.id);
|
|
3524
4270
|
}) }),
|
|
3525
|
-
status ? /* @__PURE__ */
|
|
4271
|
+
status ? /* @__PURE__ */ jsx6(Text6, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
3526
4272
|
] }) });
|
|
3527
4273
|
}
|
|
3528
|
-
const dirtyTag = dirty ? /* @__PURE__ */
|
|
4274
|
+
const dirtyTag = dirty ? /* @__PURE__ */ jsx6(Text6, { color: theme.warn, children: " \u25CF unsaved (s save \xB7 x discard)" }) : null;
|
|
3529
4275
|
if (view === "rules") {
|
|
3530
4276
|
const rStatusLines = status ? status.split("\n").length : 0;
|
|
3531
4277
|
const rHead = 4 + rStatusLines;
|
|
@@ -3535,16 +4281,16 @@ function PoliciesPanel({ focused }) {
|
|
|
3535
4281
|
const rWin = rules.slice(rStart, rStart + rBudget);
|
|
3536
4282
|
const rAbove = rStart;
|
|
3537
4283
|
const rBelow = Math.max(0, rules.length - (rStart + rBudget));
|
|
3538
|
-
return /* @__PURE__ */
|
|
3539
|
-
/* @__PURE__ */
|
|
3540
|
-
/* @__PURE__ */
|
|
3541
|
-
/* @__PURE__ */
|
|
3542
|
-
/* @__PURE__ */
|
|
3543
|
-
/* @__PURE__ */
|
|
4284
|
+
return /* @__PURE__ */ jsx6(DataView, { loading: detail.loading && !detail.data, error: detail.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
4285
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
4286
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: theme.accentBright, children: truncate(selected?.name ?? "", 28) }),
|
|
4287
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " mode: " }),
|
|
4288
|
+
/* @__PURE__ */ jsx6(Text6, { color: mode === "whitelist" ? theme.warn : void 0, children: mode }),
|
|
4289
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: ` ${rules.length} rules` }),
|
|
3544
4290
|
dirtyTag
|
|
3545
4291
|
] }),
|
|
3546
|
-
/* @__PURE__ */
|
|
3547
|
-
/* @__PURE__ */
|
|
4292
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: `\u2191\u2193 \xB7 enter edit \xB7 space on/off \xB7 e effect \xB7 n new \xB7 d del \xB7 m mode \xB7 D dry-run \xB7 s save \xB7 \u2190 back${rAbove ? ` \xB7 \u25B2${rAbove}` : ""}${rBelow ? ` \xB7 \u25BC${rBelow}` : ""}` }),
|
|
4293
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(
|
|
3548
4294
|
Table,
|
|
3549
4295
|
{
|
|
3550
4296
|
columns: [
|
|
@@ -3568,33 +4314,33 @@ function PoliciesPanel({ focused }) {
|
|
|
3568
4314
|
})
|
|
3569
4315
|
}
|
|
3570
4316
|
) }),
|
|
3571
|
-
rules.length === 0 ? /* @__PURE__ */
|
|
3572
|
-
status ? /* @__PURE__ */
|
|
4317
|
+
rules.length === 0 ? /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "(no rules)" }) : null,
|
|
4318
|
+
status ? /* @__PURE__ */ jsx6(Text6, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
3573
4319
|
] }) });
|
|
3574
4320
|
}
|
|
3575
4321
|
if (view === "match") {
|
|
3576
4322
|
const r = rules[ri];
|
|
3577
4323
|
const items = r ? matchItems(r) : [];
|
|
3578
4324
|
const t = r ? currentCType(r) : "none";
|
|
3579
|
-
return /* @__PURE__ */
|
|
3580
|
-
/* @__PURE__ */
|
|
3581
|
-
/* @__PURE__ */
|
|
3582
|
-
/* @__PURE__ */
|
|
4325
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
4326
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
4327
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: theme.accentBright, children: "Match values " }),
|
|
4328
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: r ? `${r.effect} \xB7 ${t}` : "" }),
|
|
3583
4329
|
dirtyTag
|
|
3584
4330
|
] }),
|
|
3585
|
-
/* @__PURE__ */
|
|
3586
|
-
/* @__PURE__ */
|
|
3587
|
-
items.length === 0 ? /* @__PURE__ */
|
|
4331
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "\u2191\u2193 select \xB7 enter/e edit \xB7 a add \xB7 [ / ] wildcard L/R \xB7 d remove \xB7 s save \xB7 \u2190 back" }),
|
|
4332
|
+
/* @__PURE__ */ jsxs6(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
4333
|
+
items.length === 0 ? /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "(no values \u2014 press a to add one)" }) : null,
|
|
3588
4334
|
items.map((it, i) => {
|
|
3589
4335
|
const sel = i === matchSel;
|
|
3590
4336
|
if (sel && editing) {
|
|
3591
4337
|
const lE = editVal.startsWith("*");
|
|
3592
4338
|
const rE = editVal.length > 0 && editVal.endsWith("*");
|
|
3593
|
-
return /* @__PURE__ */
|
|
3594
|
-
/* @__PURE__ */
|
|
3595
|
-
/* @__PURE__ */
|
|
3596
|
-
/* @__PURE__ */
|
|
3597
|
-
|
|
4339
|
+
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
4340
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.accentBright, children: "\u25B8 " }),
|
|
4341
|
+
/* @__PURE__ */ jsx6(Text6, { color: lE ? theme.ok : theme.dim, children: lE ? "\u2731 " : "\xB7 " }),
|
|
4342
|
+
/* @__PURE__ */ jsx6(
|
|
4343
|
+
TextInput4,
|
|
3598
4344
|
{
|
|
3599
4345
|
value: editVal,
|
|
3600
4346
|
onChange: setEditVal,
|
|
@@ -3608,41 +4354,41 @@ function PoliciesPanel({ focused }) {
|
|
|
3608
4354
|
}
|
|
3609
4355
|
}
|
|
3610
4356
|
),
|
|
3611
|
-
/* @__PURE__ */
|
|
4357
|
+
/* @__PURE__ */ jsx6(Text6, { color: rE ? theme.ok : theme.dim, children: rE ? " \u2731" : " \xB7" })
|
|
3612
4358
|
] }, i);
|
|
3613
4359
|
}
|
|
3614
4360
|
const lOn = it.startsWith("*");
|
|
3615
4361
|
const rOn = it.length > 0 && it.endsWith("*");
|
|
3616
|
-
return /* @__PURE__ */
|
|
3617
|
-
/* @__PURE__ */
|
|
3618
|
-
/* @__PURE__ */
|
|
3619
|
-
/* @__PURE__ */
|
|
3620
|
-
/* @__PURE__ */
|
|
4362
|
+
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
4363
|
+
/* @__PURE__ */ jsx6(Text6, { color: sel ? theme.accentBright : void 0, children: sel ? "\u25B8 " : " " }),
|
|
4364
|
+
/* @__PURE__ */ jsx6(Text6, { color: lOn ? theme.ok : theme.dim, children: lOn ? "\u2731 " : "\xB7 " }),
|
|
4365
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: !it, children: it || "(empty)" }),
|
|
4366
|
+
/* @__PURE__ */ jsx6(Text6, { color: rOn ? theme.ok : theme.dim, children: rOn ? " \u2731" : " \xB7" })
|
|
3621
4367
|
] }, i);
|
|
3622
4368
|
})
|
|
3623
4369
|
] }),
|
|
3624
|
-
/* @__PURE__ */
|
|
3625
|
-
status ? /* @__PURE__ */
|
|
4370
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " * = wildcard (any run of chars). Type it directly (rm*) or toggle with [ / ]. Green \u2731 = active \xB7 left & right shown as you type. Multiple values = OR (any one matches)." }),
|
|
4371
|
+
status ? /* @__PURE__ */ jsx6(Text6, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
3626
4372
|
] });
|
|
3627
4373
|
}
|
|
3628
4374
|
const rule = rules[ri];
|
|
3629
|
-
return /* @__PURE__ */
|
|
3630
|
-
/* @__PURE__ */
|
|
3631
|
-
/* @__PURE__ */
|
|
3632
|
-
/* @__PURE__ */
|
|
4375
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
4376
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
4377
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: theme.accentBright, children: "Rule " }),
|
|
4378
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: rule?.id ?? "" }),
|
|
3633
4379
|
dirtyTag
|
|
3634
4380
|
] }),
|
|
3635
|
-
/* @__PURE__ */
|
|
3636
|
-
/* @__PURE__ */
|
|
4381
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "\u2191\u2193 field \xB7 enter edit/open \xB7 space toggle \xB7 \u2190\u2192 move/pick \xB7 s save \xB7 \u2190 back" }),
|
|
4382
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexDirection: "column", children: FIELDS.map((f, i) => {
|
|
3637
4383
|
const active2 = i === fi;
|
|
3638
|
-
const
|
|
4384
|
+
const label2 = /* @__PURE__ */ jsx6(Text6, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + f.label.padEnd(13) });
|
|
3639
4385
|
if (f.kind === "perms" && rule) {
|
|
3640
|
-
return /* @__PURE__ */
|
|
3641
|
-
|
|
4386
|
+
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
4387
|
+
label2,
|
|
3642
4388
|
PERMS.map((p, pi2) => {
|
|
3643
4389
|
const on = permList(rule).includes(p);
|
|
3644
4390
|
const cur = active2 && pi2 === permCursor % PERMS.length;
|
|
3645
|
-
return /* @__PURE__ */
|
|
4391
|
+
return /* @__PURE__ */ jsxs6(Text6, { color: on ? theme.ok : theme.bad, inverse: cur, children: [
|
|
3646
4392
|
p,
|
|
3647
4393
|
pi2 < PERMS.length - 1 ? " " : ""
|
|
3648
4394
|
] }, p);
|
|
@@ -3652,17 +4398,17 @@ function PoliciesPanel({ focused }) {
|
|
|
3652
4398
|
if (f.kind === "match" && rule) {
|
|
3653
4399
|
const items = matchItems(rule);
|
|
3654
4400
|
const preview = items.join(", ");
|
|
3655
|
-
return /* @__PURE__ */
|
|
3656
|
-
|
|
3657
|
-
/* @__PURE__ */
|
|
3658
|
-
/* @__PURE__ */
|
|
4401
|
+
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
4402
|
+
label2,
|
|
4403
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: !preview, children: preview || "\u2014" }),
|
|
4404
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: items.length ? ` (${items.length} value${items.length > 1 ? "s" : ""} \xB7 enter: edit)` : " (enter: add values)" })
|
|
3659
4405
|
] }, f.label);
|
|
3660
4406
|
}
|
|
3661
4407
|
const val = rule ? f.get(rule) : "";
|
|
3662
|
-
return /* @__PURE__ */
|
|
3663
|
-
|
|
3664
|
-
active2 && editing && f.kind === "text" ? /* @__PURE__ */
|
|
3665
|
-
|
|
4408
|
+
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
4409
|
+
label2,
|
|
4410
|
+
active2 && editing && f.kind === "text" ? /* @__PURE__ */ jsx6(
|
|
4411
|
+
TextInput4,
|
|
3666
4412
|
{
|
|
3667
4413
|
value: editVal,
|
|
3668
4414
|
onChange: setEditVal,
|
|
@@ -3671,8 +4417,8 @@ function PoliciesPanel({ focused }) {
|
|
|
3671
4417
|
setEditing(false);
|
|
3672
4418
|
}
|
|
3673
4419
|
}
|
|
3674
|
-
) : /* @__PURE__ */
|
|
3675
|
-
|
|
4420
|
+
) : /* @__PURE__ */ jsx6(
|
|
4421
|
+
Text6,
|
|
3676
4422
|
{
|
|
3677
4423
|
color: f.kind === "effect" ? val === "ALLOW" ? theme.ok : theme.bad : void 0,
|
|
3678
4424
|
dimColor: !val,
|
|
@@ -3681,14 +4427,14 @@ function PoliciesPanel({ focused }) {
|
|
|
3681
4427
|
)
|
|
3682
4428
|
] }, f.label);
|
|
3683
4429
|
}) }),
|
|
3684
|
-
status ? /* @__PURE__ */
|
|
4430
|
+
status ? /* @__PURE__ */ jsx6(Text6, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
3685
4431
|
] });
|
|
3686
4432
|
}
|
|
3687
4433
|
|
|
3688
4434
|
// src/tui/panels/RateLimit.tsx
|
|
3689
|
-
import { Box as
|
|
3690
|
-
import { useEffect as
|
|
3691
|
-
import { Fragment as
|
|
4435
|
+
import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
|
|
4436
|
+
import { useEffect as useEffect6, useState as useState7 } from "react";
|
|
4437
|
+
import { Fragment as Fragment5, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
3692
4438
|
var MODES = ["off", "detect", "block"];
|
|
3693
4439
|
var FIELDS2 = ["mode", "perMinute", "perHour", "perDay"];
|
|
3694
4440
|
var when = (ms) => {
|
|
@@ -3700,11 +4446,11 @@ function RateLimitPanel({ focused }) {
|
|
|
3700
4446
|
const layersQ = useLoader(() => api.settings.getSecurityLayers());
|
|
3701
4447
|
const historyQ = useLoader(() => api.settings.getRateLimitHistory());
|
|
3702
4448
|
const insightsQ = useLoader(() => api.stats.securityInsights(7));
|
|
3703
|
-
const [draft, setDraft] =
|
|
3704
|
-
const [dirty, setDirty] =
|
|
3705
|
-
const [sel, setSel] =
|
|
3706
|
-
const [status, setStatus] =
|
|
3707
|
-
|
|
4449
|
+
const [draft, setDraft] = useState7(null);
|
|
4450
|
+
const [dirty, setDirty] = useState7(false);
|
|
4451
|
+
const [sel, setSel] = useState7(0);
|
|
4452
|
+
const [status, setStatus] = useState7(null);
|
|
4453
|
+
useEffect6(() => {
|
|
3708
4454
|
if (layersQ.data && !dirty) setDraft({ ...layersQ.data.layers.rateLimit });
|
|
3709
4455
|
}, [layersQ.data]);
|
|
3710
4456
|
usePoll(() => {
|
|
@@ -3753,7 +4499,7 @@ function RateLimitPanel({ focused }) {
|
|
|
3753
4499
|
const fi = onField ? selC : -1;
|
|
3754
4500
|
const bcur = onField ? -1 : selC - 4;
|
|
3755
4501
|
const off = bcur < 0 ? 0 : Math.min(Math.max(0, bcur - Math.floor(burstBudget / 2)), maxOff);
|
|
3756
|
-
|
|
4502
|
+
useInput6(
|
|
3757
4503
|
(input, key) => {
|
|
3758
4504
|
const step = key.shift ? 10 : 1;
|
|
3759
4505
|
if (key.upArrow) setSel((n) => Math.max(0, n - 1));
|
|
@@ -3772,52 +4518,52 @@ function RateLimitPanel({ focused }) {
|
|
|
3772
4518
|
},
|
|
3773
4519
|
{ isActive: focused }
|
|
3774
4520
|
);
|
|
3775
|
-
return /* @__PURE__ */
|
|
4521
|
+
return /* @__PURE__ */ jsx7(DataView, { loading: layersQ.loading && !draft, error: layersQ.error, children: draft ? (() => {
|
|
3776
4522
|
const lim = draft.perMinute;
|
|
3777
4523
|
const now = insightsQ.data ? insightsQ.data["activity"]?.minute?.slice(-1)[0]?.count ?? 0 : 0;
|
|
3778
4524
|
const loadPct = lim > 0 ? Math.min(100, Math.round(now / lim * 100)) : 0;
|
|
3779
4525
|
const last24h = (insightsQ.data?.["activity"]?.hour ?? []).reduce((s, b) => s + b.count, 0);
|
|
3780
4526
|
const shown = anomalies2.slice(off, off + burstBudget);
|
|
3781
4527
|
const bw = Math.max(10, cols - 30);
|
|
3782
|
-
return /* @__PURE__ */
|
|
3783
|
-
/* @__PURE__ */
|
|
3784
|
-
/* @__PURE__ */
|
|
3785
|
-
/* @__PURE__ */
|
|
3786
|
-
/* @__PURE__ */
|
|
4528
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", height: rows, overflow: "hidden", children: [
|
|
4529
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 move (fields + bursts) \xB7 \u2190\u2192 \xB11 \xB7 shift+\u2190\u2192 \xB110 \xB7 s save \xB7 ^R refresh" : "press \u2192 to edit" }),
|
|
4530
|
+
/* @__PURE__ */ jsxs7(FieldRow, { label: "Mode", active: focused && fi === 0, children: [
|
|
4531
|
+
/* @__PURE__ */ jsx7(Text7, { color: modeColor(draft.mode), bold: true, children: draft.mode }),
|
|
4532
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: draft.mode === "off" ? " no limit" : draft.mode === "detect" ? " flag bursts, never block" : " DENY calls over the limit" })
|
|
3787
4533
|
] }),
|
|
3788
|
-
/* @__PURE__ */
|
|
3789
|
-
/* @__PURE__ */
|
|
3790
|
-
/* @__PURE__ */
|
|
3791
|
-
showExtras && /* @__PURE__ */
|
|
3792
|
-
hasLoad ? /* @__PURE__ */
|
|
3793
|
-
/* @__PURE__ */
|
|
3794
|
-
/* @__PURE__ */
|
|
3795
|
-
/* @__PURE__ */
|
|
3796
|
-
/* @__PURE__ */
|
|
4534
|
+
/* @__PURE__ */ jsx7(FieldRow, { label: "Per minute", active: focused && fi === 1, children: /* @__PURE__ */ jsx7(Text7, { bold: true, children: draft.perMinute || "off" }) }),
|
|
4535
|
+
/* @__PURE__ */ jsx7(FieldRow, { label: "Per hour", active: focused && fi === 2, children: /* @__PURE__ */ jsx7(Text7, { bold: true, children: draft.perHour || "off" }) }),
|
|
4536
|
+
/* @__PURE__ */ jsx7(FieldRow, { label: "Per day", active: focused && fi === 3, children: /* @__PURE__ */ jsx7(Text7, { bold: true, children: draft.perDay || "off" }) }),
|
|
4537
|
+
showExtras && /* @__PURE__ */ jsxs7(Fragment5, { children: [
|
|
4538
|
+
hasLoad ? /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
4539
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "load now".padEnd(13) }),
|
|
4540
|
+
/* @__PURE__ */ jsx7(Text7, { color: loadPct >= 100 ? theme.bad : loadPct >= 80 ? theme.warn : theme.ok, children: "\u2588".repeat(Math.min(18, Math.round(now / lim * 18))) }),
|
|
4541
|
+
/* @__PURE__ */ jsx7(Text7, { color: "#233457", children: "\u2591".repeat(Math.max(0, 18 - Math.round(now / lim * 18))) }),
|
|
4542
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: ` ${now}/${lim} this minute (${loadPct}%)` })
|
|
3797
4543
|
] }) : null,
|
|
3798
|
-
/* @__PURE__ */
|
|
4544
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
|
|
3799
4545
|
"Busiest 7d",
|
|
3800
|
-
/* @__PURE__ */
|
|
4546
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " most calls in one window vs your limit" })
|
|
3801
4547
|
] }),
|
|
3802
|
-
/* @__PURE__ */
|
|
3803
|
-
/* @__PURE__ */
|
|
3804
|
-
/* @__PURE__ */
|
|
3805
|
-
/* @__PURE__ */
|
|
4548
|
+
/* @__PURE__ */ jsx7(BusiestRow, { label: "minute", peak: peaks.minute, limit: draft.perMinute }),
|
|
4549
|
+
/* @__PURE__ */ jsx7(BusiestRow, { label: "hour", peak: peaks.hour, limit: draft.perHour }),
|
|
4550
|
+
/* @__PURE__ */ jsx7(BusiestRow, { label: "day", peak: peaks.day, limit: draft.perDay }),
|
|
4551
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, wrap: "truncate", children: `activity`.padEnd(11) + ` last 24h \xB7 ${last24h} calls \xB7 busiest hour ${peaks.hour}/hr` })
|
|
3806
4552
|
] }),
|
|
3807
|
-
/* @__PURE__ */
|
|
3808
|
-
/* @__PURE__ */
|
|
4553
|
+
/* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
4554
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
|
|
3809
4555
|
`Recent bursts 7d${anomalies2.length ? ` (${anomalies2.length})` : ""}`,
|
|
3810
|
-
maxOff > 0 ? /* @__PURE__ */
|
|
3811
|
-
/* @__PURE__ */
|
|
4556
|
+
maxOff > 0 ? /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: ` ${off + 1}-${Math.min(off + burstBudget, anomalies2.length)}/${anomalies2.length}` }) : null,
|
|
4557
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " over the per-minute limit \xB7 Bypassed = detect (not blocked)" })
|
|
3812
4558
|
] }),
|
|
3813
|
-
anomalies2.length === 0 ? /* @__PURE__ */
|
|
4559
|
+
anomalies2.length === 0 ? /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
3814
4560
|
" ",
|
|
3815
4561
|
insightsQ.loading ? "loading\u2026" : insightsQ.error ? `couldn't load bursts: ${String(insightsQ.error).slice(0, 60)}` : "no bursts in the last 7 days"
|
|
3816
4562
|
] }) : (
|
|
3817
4563
|
// Bounded here as well as on the outer frame, so a list longer than
|
|
3818
4564
|
// the budget is clipped inside its own box instead of shoving the
|
|
3819
4565
|
// footer out of the panel and taking the scroll window with it.
|
|
3820
|
-
/* @__PURE__ */
|
|
4566
|
+
/* @__PURE__ */ jsx7(Box7, { flexDirection: "column", height: burstBudget + 1, overflow: "hidden", children: /* @__PURE__ */ jsx7(
|
|
3821
4567
|
Table,
|
|
3822
4568
|
{
|
|
3823
4569
|
columns: [{ header: "WHEN", width: 15 }, { header: "RESULT", width: 9 }, { header: "CALLS", width: 7 }, { header: "AGENT", width: Math.max(8, bw - 31) }],
|
|
@@ -3834,54 +4580,54 @@ function RateLimitPanel({ focused }) {
|
|
|
3834
4580
|
) })
|
|
3835
4581
|
)
|
|
3836
4582
|
] }),
|
|
3837
|
-
spacerRows ? /* @__PURE__ */
|
|
3838
|
-
/* @__PURE__ */
|
|
3839
|
-
/* @__PURE__ */
|
|
3840
|
-
/* @__PURE__ */
|
|
3841
|
-
lastChange ? /* @__PURE__ */
|
|
3842
|
-
dirty ? /* @__PURE__ */
|
|
3843
|
-
status ? /* @__PURE__ */
|
|
4583
|
+
spacerRows ? /* @__PURE__ */ jsx7(Text7, { children: " " }) : null,
|
|
4584
|
+
/* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
4585
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "limit now " }),
|
|
4586
|
+
/* @__PURE__ */ jsx7(Text7, { children: lim > 0 ? `${lim}/min` : "no per-minute limit" }),
|
|
4587
|
+
lastChange ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: ` \xB7 changed ${when(lastChange.ts)}` }) : null,
|
|
4588
|
+
dirty ? /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: " \u25CF unsaved (s)" }) : null,
|
|
4589
|
+
status ? /* @__PURE__ */ jsx7(Text7, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: " " + status }) : null
|
|
3844
4590
|
] })
|
|
3845
4591
|
] });
|
|
3846
4592
|
})() : null });
|
|
3847
4593
|
}
|
|
3848
|
-
function BusiestRow({ label, peak, limit }) {
|
|
4594
|
+
function BusiestRow({ label: label2, peak, limit }) {
|
|
3849
4595
|
const ctx = limit > 0 ? peak > limit ? `over ${limit}` : peak === limit ? `at limit ${limit}` : `limit ${limit}` : "no limit set";
|
|
3850
4596
|
const hot = limit > 0 && peak > limit;
|
|
3851
|
-
return /* @__PURE__ */
|
|
3852
|
-
/* @__PURE__ */
|
|
3853
|
-
/* @__PURE__ */
|
|
3854
|
-
/* @__PURE__ */
|
|
4597
|
+
return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
4598
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: (" " + label2).padEnd(11) }),
|
|
4599
|
+
/* @__PURE__ */ jsx7(Text7, { color: hot ? theme.bad : void 0, bold: hot, children: String(peak).padStart(4) + " calls" }),
|
|
4600
|
+
/* @__PURE__ */ jsx7(Text7, { color: hot ? theme.bad : theme.dim, children: " \xB7 " + ctx })
|
|
3855
4601
|
] });
|
|
3856
4602
|
}
|
|
3857
|
-
function FieldRow({ label, active: active2, children }) {
|
|
3858
|
-
return /* @__PURE__ */
|
|
3859
|
-
/* @__PURE__ */
|
|
4603
|
+
function FieldRow({ label: label2, active: active2, children }) {
|
|
4604
|
+
return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
4605
|
+
/* @__PURE__ */ jsx7(Text7, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + label2.padEnd(11) }),
|
|
3860
4606
|
children
|
|
3861
4607
|
] });
|
|
3862
4608
|
}
|
|
3863
4609
|
|
|
3864
4610
|
// src/tui/panels/Dlp.tsx
|
|
3865
|
-
import { Box as
|
|
3866
|
-
import
|
|
3867
|
-
import { useEffect as
|
|
3868
|
-
import { jsx as
|
|
4611
|
+
import { Box as Box8, Text as Text8, useInput as useInput7 } from "ink";
|
|
4612
|
+
import TextInput5 from "ink-text-input";
|
|
4613
|
+
import { useEffect as useEffect7, useState as useState8 } from "react";
|
|
4614
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
3869
4615
|
var MODES2 = ["off", "detect", "block"];
|
|
3870
4616
|
function DlpPanel({ focused }) {
|
|
3871
4617
|
const { cols, rows } = usePanelSize();
|
|
3872
4618
|
const q = useLoader(() => api.settings.getSecurityLayers());
|
|
3873
|
-
const [dlp, setDlp] =
|
|
3874
|
-
const [available, setAvailable] =
|
|
3875
|
-
const [ghostOn, setGhostOn] =
|
|
3876
|
-
const [ghostPats, setGhostPats] =
|
|
3877
|
-
const [sel, setSel] =
|
|
3878
|
-
const [dirty, setDirty] =
|
|
3879
|
-
const [status, setStatus] =
|
|
3880
|
-
const [adding, setAdding] =
|
|
3881
|
-
const [newName, setNewName] =
|
|
3882
|
-
const [input, setInput] =
|
|
3883
|
-
const [editIdx, setEditIdx] =
|
|
3884
|
-
|
|
4619
|
+
const [dlp, setDlp] = useState8(null);
|
|
4620
|
+
const [available, setAvailable] = useState8([]);
|
|
4621
|
+
const [ghostOn, setGhostOn] = useState8(false);
|
|
4622
|
+
const [ghostPats, setGhostPats] = useState8([]);
|
|
4623
|
+
const [sel, setSel] = useState8(0);
|
|
4624
|
+
const [dirty, setDirty] = useState8(false);
|
|
4625
|
+
const [status, setStatus] = useState8(null);
|
|
4626
|
+
const [adding, setAdding] = useState8(null);
|
|
4627
|
+
const [newName, setNewName] = useState8("");
|
|
4628
|
+
const [input, setInput] = useState8("");
|
|
4629
|
+
const [editIdx, setEditIdx] = useState8(null);
|
|
4630
|
+
useEffect7(() => {
|
|
3885
4631
|
if (q.data && !dirty) {
|
|
3886
4632
|
setDlp({ ...q.data.layers.dlp, patterns: [...q.data.layers.dlp.patterns], custom: [...q.data.layers.dlp.custom] });
|
|
3887
4633
|
setAvailable(q.data.availablePatterns);
|
|
@@ -3928,7 +4674,7 @@ function DlpPanel({ focused }) {
|
|
|
3928
4674
|
];
|
|
3929
4675
|
const selC = Math.min(sel, Math.max(0, entries.length - 1));
|
|
3930
4676
|
const cur = entries[selC];
|
|
3931
|
-
|
|
4677
|
+
useInput7(
|
|
3932
4678
|
(input2, key) => {
|
|
3933
4679
|
if (!dlp) return;
|
|
3934
4680
|
if (key.ctrl && input2 === "r") {
|
|
@@ -3991,17 +4737,17 @@ function DlpPanel({ focused }) {
|
|
|
3991
4737
|
if (right) core = core.replace(/\*+$/, "");
|
|
3992
4738
|
return { left, core: core || s, right };
|
|
3993
4739
|
};
|
|
3994
|
-
const starL = (on) => /* @__PURE__ */
|
|
3995
|
-
const starR = (on) => /* @__PURE__ */
|
|
4740
|
+
const starL = (on) => /* @__PURE__ */ jsx8(Text8, { color: on ? theme.ok : theme.dim, children: on ? "\u2731 " : "\xB7 " });
|
|
4741
|
+
const starR = (on) => /* @__PURE__ */ jsx8(Text8, { color: on ? theme.ok : theme.dim, children: on ? " \u2731" : " \xB7" });
|
|
3996
4742
|
const lines = [];
|
|
3997
|
-
const header = (key,
|
|
3998
|
-
node: /* @__PURE__ */
|
|
3999
|
-
/* @__PURE__ */
|
|
4000
|
-
extra ? /* @__PURE__ */
|
|
4743
|
+
const header = (key, label2, extra) => lines.push({
|
|
4744
|
+
node: /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4745
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, bold: true, children: label2 }),
|
|
4746
|
+
extra ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " " + extra }) : null
|
|
4001
4747
|
] }, key),
|
|
4002
4748
|
entry: -1
|
|
4003
4749
|
});
|
|
4004
|
-
const note = (key, text) => lines.push({ node: /* @__PURE__ */
|
|
4750
|
+
const note = (key, text) => lines.push({ node: /* @__PURE__ */ jsx8(Text8, { color: theme.dim, wrap: "truncate", children: text }, key), entry: -1 });
|
|
4005
4751
|
let ei = 0;
|
|
4006
4752
|
header("h:builtin", "built-in secret patterns", "space toggles on/off");
|
|
4007
4753
|
available.forEach((p) => {
|
|
@@ -4009,10 +4755,10 @@ function DlpPanel({ focused }) {
|
|
|
4009
4755
|
const isCur = focused && ei === selC;
|
|
4010
4756
|
const idx = ei++;
|
|
4011
4757
|
lines.push({
|
|
4012
|
-
node: /* @__PURE__ */
|
|
4013
|
-
/* @__PURE__ */
|
|
4014
|
-
/* @__PURE__ */
|
|
4015
|
-
/* @__PURE__ */
|
|
4758
|
+
node: /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", backgroundColor: isCur ? "#1c2f63" : void 0, bold: isCur, children: [
|
|
4759
|
+
/* @__PURE__ */ jsx8(Text8, { color: isCur ? theme.accentBright : theme.dim, children: isCur ? "\u25B8 " : " " }),
|
|
4760
|
+
/* @__PURE__ */ jsx8(Text8, { color: on ? theme.ok : theme.dim, children: on ? "\u25CF " : "\u25CB " }),
|
|
4761
|
+
/* @__PURE__ */ jsx8(Text8, { color: on ? void 0 : theme.dim, children: p })
|
|
4016
4762
|
] }, "b:" + p),
|
|
4017
4763
|
entry: idx
|
|
4018
4764
|
});
|
|
@@ -4025,12 +4771,12 @@ function DlpPanel({ focused }) {
|
|
|
4025
4771
|
const idx = ei++;
|
|
4026
4772
|
const w = wcParts(c2.re);
|
|
4027
4773
|
lines.push({
|
|
4028
|
-
node: /* @__PURE__ */
|
|
4029
|
-
/* @__PURE__ */
|
|
4030
|
-
/* @__PURE__ */
|
|
4031
|
-
/* @__PURE__ */
|
|
4774
|
+
node: /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", backgroundColor: isCur ? "#1c2f63" : void 0, bold: isCur, children: [
|
|
4775
|
+
/* @__PURE__ */ jsx8(Text8, { color: isCur ? theme.accentBright : theme.dim, children: isCur ? "\u25B8 " : " " }),
|
|
4776
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: c2.name }),
|
|
4777
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " " }),
|
|
4032
4778
|
starL(w.left),
|
|
4033
|
-
/* @__PURE__ */
|
|
4779
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: truncate(w.core, Math.max(6, cols - c2.name.length - 14)) }),
|
|
4034
4780
|
starR(w.right)
|
|
4035
4781
|
] }, "c:" + c2.name + i),
|
|
4036
4782
|
entry: idx
|
|
@@ -4044,12 +4790,12 @@ function DlpPanel({ focused }) {
|
|
|
4044
4790
|
const idx = ei++;
|
|
4045
4791
|
const w = wcParts(p);
|
|
4046
4792
|
lines.push({
|
|
4047
|
-
node: /* @__PURE__ */
|
|
4048
|
-
/* @__PURE__ */
|
|
4793
|
+
node: /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", backgroundColor: isCur ? "#1c2f63" : void 0, bold: isCur, children: [
|
|
4794
|
+
/* @__PURE__ */ jsx8(Text8, { color: isCur ? theme.accentBright : theme.dim, children: isCur ? "\u25B8 " : " " }),
|
|
4049
4795
|
starL(w.left),
|
|
4050
|
-
/* @__PURE__ */
|
|
4796
|
+
/* @__PURE__ */ jsx8(Text8, { color: ghostOn ? theme.accent : theme.dim, children: truncate(w.core, Math.max(6, cols - 12)) }),
|
|
4051
4797
|
starR(w.right),
|
|
4052
|
-
!ghostOn ? /* @__PURE__ */
|
|
4798
|
+
!ghostOn ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " (ghost off)" }) : null
|
|
4053
4799
|
] }, "g:" + p + i),
|
|
4054
4800
|
entry: idx
|
|
4055
4801
|
});
|
|
@@ -4063,19 +4809,19 @@ function DlpPanel({ focused }) {
|
|
|
4063
4809
|
const win = lines.slice(start, start + budget);
|
|
4064
4810
|
const moreAbove = start;
|
|
4065
4811
|
const moreBelow = Math.max(0, lines.length - (start + budget));
|
|
4066
|
-
return /* @__PURE__ */
|
|
4067
|
-
/* @__PURE__ */
|
|
4068
|
-
/* @__PURE__ */
|
|
4069
|
-
/* @__PURE__ */
|
|
4070
|
-
/* @__PURE__ */
|
|
4071
|
-
dirty ? /* @__PURE__ */
|
|
4812
|
+
return /* @__PURE__ */ jsx8(DataView, { loading: q.loading && !dlp, error: q.error, children: dlp ? /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
4813
|
+
/* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4814
|
+
/* @__PURE__ */ jsx8(Text8, { children: "mode: " }),
|
|
4815
|
+
/* @__PURE__ */ jsx8(Text8, { color: modeColor(dlp.mode), bold: true, children: dlp.mode }),
|
|
4816
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: dlp.mode === "off" ? " (scanning disabled)" : dlp.mode === "detect" ? " (flag dlp:yes, redact output)" : " (deny + redact secrets)" }),
|
|
4817
|
+
dirty ? /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: " \u25CF unsaved (s save \xB7 x discard)" }) : null
|
|
4072
4818
|
] }),
|
|
4073
|
-
/* @__PURE__ */
|
|
4074
|
-
adding ? /* @__PURE__ */
|
|
4075
|
-
/* @__PURE__ */
|
|
4819
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, wrap: "truncate", children: focused ? `\u2191\u2193 move \xB7 space on/off \xB7 m mode \xB7 a add \xB7 r route \xB7 e/enter edit \xB7 d remove \xB7 g ghost \xB7 s save \xB7 ^R refresh${moreAbove ? ` \xB7 \u25B2${moreAbove}` : ""}${moreBelow ? ` \xB7 \u25BC${moreBelow}` : ""}` : "press \u2192 to edit" }),
|
|
4820
|
+
adding ? /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", children: /* @__PURE__ */ jsxs8(Box8, { children: [
|
|
4821
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: adding === "name" ? "new pattern name: " : adding === "re" ? `${editIdx != null ? "edit" : "new"} pattern for "${newName}" (glob, * = any chars): ` : `${editIdx != null ? "edit ghost path" : "ghost path to hide"} (glob, * = any chars, e.g. */.env *.pem): ` }),
|
|
4076
4822
|
adding !== "name" ? starL(input.startsWith("*")) : null,
|
|
4077
|
-
/* @__PURE__ */
|
|
4078
|
-
|
|
4823
|
+
/* @__PURE__ */ jsx8(
|
|
4824
|
+
TextInput5,
|
|
4079
4825
|
{
|
|
4080
4826
|
value: input,
|
|
4081
4827
|
onChange: setInput,
|
|
@@ -4117,19 +4863,19 @@ function DlpPanel({ focused }) {
|
|
|
4117
4863
|
),
|
|
4118
4864
|
adding !== "name" ? starR(input.length > 1 && input.endsWith("*")) : null
|
|
4119
4865
|
] }) }) : null,
|
|
4120
|
-
/* @__PURE__ */
|
|
4121
|
-
status ? /* @__PURE__ */
|
|
4866
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, flexDirection: "column", height: budget, overflow: "hidden", children: win.map((l) => l.node) }),
|
|
4867
|
+
status ? /* @__PURE__ */ jsx8(Text8, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, wrap: "truncate", children: status }) : null
|
|
4122
4868
|
] }) : null });
|
|
4123
4869
|
}
|
|
4124
4870
|
|
|
4125
4871
|
// src/tui/panels/Audit.tsx
|
|
4126
|
-
import { Box as
|
|
4127
|
-
import
|
|
4872
|
+
import { Box as Box9, Text as Text9, useInput as useInput8 } from "ink";
|
|
4873
|
+
import TextInput6 from "ink-text-input";
|
|
4128
4874
|
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
4129
4875
|
import { homedir as homedir7 } from "os";
|
|
4130
4876
|
import { join as join7 } from "path";
|
|
4131
|
-
import { useState as
|
|
4132
|
-
import { jsx as
|
|
4877
|
+
import { useState as useState9 } from "react";
|
|
4878
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
4133
4879
|
var DECISIONS = [void 0, "DENY", "ALLOW"];
|
|
4134
4880
|
var SIGNALS = [void 0, "dlp", "ratelimit"];
|
|
4135
4881
|
var SESS_STATUS = [void 0, "active", "idle", "ended"];
|
|
@@ -4235,24 +4981,24 @@ var STATUS_DOT = {
|
|
|
4235
4981
|
};
|
|
4236
4982
|
function AuditPanel({ active: active2, focused }) {
|
|
4237
4983
|
const { cols, rows } = usePanelSize();
|
|
4238
|
-
const [source, setSource] =
|
|
4239
|
-
const [view, setView] =
|
|
4240
|
-
const [di, setDi] =
|
|
4241
|
-
const [gi, setGi] =
|
|
4242
|
-
const [tool, setTool] =
|
|
4243
|
-
const [agent, setAgent] =
|
|
4244
|
-
const [search, setSearch] =
|
|
4245
|
-
const [sessFilter, setSessFilter] =
|
|
4246
|
-
const [page, setPage] =
|
|
4247
|
-
const [sel, setSel] =
|
|
4248
|
-
const [detailScroll, setDetailScroll] =
|
|
4249
|
-
const [editing, setEditing] =
|
|
4250
|
-
const [si, setSi] =
|
|
4251
|
-
const [sessSearch, setSessSearch] =
|
|
4252
|
-
const [sessSel, setSessSel] =
|
|
4253
|
-
const [msg, setMsg] =
|
|
4254
|
-
const [showHelp, setShowHelp] =
|
|
4255
|
-
const [frozen, setFrozen] =
|
|
4984
|
+
const [source, setSource] = useState9("cloud");
|
|
4985
|
+
const [view, setView] = useState9("logs");
|
|
4986
|
+
const [di, setDi] = useState9(0);
|
|
4987
|
+
const [gi, setGi] = useState9(0);
|
|
4988
|
+
const [tool, setTool] = useState9("");
|
|
4989
|
+
const [agent, setAgent] = useState9("");
|
|
4990
|
+
const [search, setSearch] = useState9("");
|
|
4991
|
+
const [sessFilter, setSessFilter] = useState9("");
|
|
4992
|
+
const [page, setPage] = useState9(0);
|
|
4993
|
+
const [sel, setSel] = useState9(0);
|
|
4994
|
+
const [detailScroll, setDetailScroll] = useState9(0);
|
|
4995
|
+
const [editing, setEditing] = useState9(null);
|
|
4996
|
+
const [si, setSi] = useState9(0);
|
|
4997
|
+
const [sessSearch, setSessSearch] = useState9("");
|
|
4998
|
+
const [sessSel, setSessSel] = useState9(0);
|
|
4999
|
+
const [msg, setMsg] = useState9(null);
|
|
5000
|
+
const [showHelp, setShowHelp] = useState9(false);
|
|
5001
|
+
const [frozen, setFrozen] = useState9(false);
|
|
4256
5002
|
const toTop = () => setSel(0);
|
|
4257
5003
|
const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
|
|
4258
5004
|
usePoll(statsQ.reloadQuiet, 15e3, active2 && source === "cloud" && !frozen);
|
|
@@ -4348,7 +5094,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
4348
5094
|
};
|
|
4349
5095
|
run().then(({ n, file }) => setMsg({ text: `\u2713 exported ${n} rows \u2192 ${file}`, level: "ok" })).catch((e) => setMsg({ text: "\u2717 export failed: " + (e instanceof Error ? e.message : String(e)), level: "bad" }));
|
|
4350
5096
|
};
|
|
4351
|
-
|
|
5097
|
+
useInput8(
|
|
4352
5098
|
(input, key) => {
|
|
4353
5099
|
if (input === " ") {
|
|
4354
5100
|
setFrozen((f) => !f);
|
|
@@ -4464,58 +5210,58 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
4464
5210
|
{ isActive: focused && !editing }
|
|
4465
5211
|
);
|
|
4466
5212
|
const localAll = localQ.data ?? [];
|
|
4467
|
-
const strip = source === "cloud" ? /* @__PURE__ */
|
|
4468
|
-
/* @__PURE__ */
|
|
4469
|
-
/* @__PURE__ */
|
|
4470
|
-
/* @__PURE__ */
|
|
5213
|
+
const strip = source === "cloud" ? /* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
|
|
5214
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, children: statsQ.data ? statsQ.data.total_calls : "\xB7\xB7\xB7\xB7" }),
|
|
5215
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " calls \xB7 " }),
|
|
5216
|
+
/* @__PURE__ */ jsxs9(Text9, { color: theme.ok, children: [
|
|
4471
5217
|
statsQ.data?.allowed ?? "\xB7\xB7\xB7",
|
|
4472
5218
|
" allow"
|
|
4473
5219
|
] }),
|
|
4474
|
-
/* @__PURE__ */
|
|
4475
|
-
/* @__PURE__ */
|
|
5220
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 " }),
|
|
5221
|
+
/* @__PURE__ */ jsxs9(Text9, { color: theme.bad, children: [
|
|
4476
5222
|
statsQ.data?.denied ?? "\xB7\xB7",
|
|
4477
5223
|
" deny"
|
|
4478
5224
|
] }),
|
|
4479
|
-
/* @__PURE__ */
|
|
4480
|
-
] }) : /* @__PURE__ */
|
|
4481
|
-
/* @__PURE__ */
|
|
4482
|
-
/* @__PURE__ */
|
|
4483
|
-
/* @__PURE__ */
|
|
5225
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: ` \xB7 ${statsQ.data?.active_policies ?? "\xB7"} policies` })
|
|
5226
|
+
] }) : /* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
|
|
5227
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, children: localAll.length }),
|
|
5228
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " calls in local file \xB7 " }),
|
|
5229
|
+
/* @__PURE__ */ jsxs9(Text9, { color: theme.ok, children: [
|
|
4484
5230
|
localAll.filter((r) => r.decision === "ALLOW").length,
|
|
4485
5231
|
" allow"
|
|
4486
5232
|
] }),
|
|
4487
|
-
/* @__PURE__ */
|
|
4488
|
-
/* @__PURE__ */
|
|
5233
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 " }),
|
|
5234
|
+
/* @__PURE__ */ jsxs9(Text9, { color: theme.bad, children: [
|
|
4489
5235
|
localAll.filter((r) => r.decision !== "ALLOW").length,
|
|
4490
5236
|
" deny"
|
|
4491
5237
|
] }),
|
|
4492
|
-
/* @__PURE__ */
|
|
5238
|
+
/* @__PURE__ */ jsxs9(Text9, { color: theme.dim, children: [
|
|
4493
5239
|
" \xB7 ",
|
|
4494
5240
|
localLogFile()
|
|
4495
5241
|
] })
|
|
4496
5242
|
] });
|
|
4497
|
-
const copyBanner = frozen ? /* @__PURE__ */
|
|
4498
|
-
const srcChip = /* @__PURE__ */
|
|
4499
|
-
/* @__PURE__ */
|
|
4500
|
-
/* @__PURE__ */
|
|
4501
|
-
/* @__PURE__ */
|
|
5243
|
+
const copyBanner = frozen ? /* @__PURE__ */ jsx9(Text9, { backgroundColor: "#123d1f", color: "#7bd88f", bold: true, wrap: "truncate", children: " \u23F5 COPY MODE \u2014 screen frozen, select & copy freely \xB7 space resume " }) : null;
|
|
5244
|
+
const srcChip = /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
5245
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "src:" }),
|
|
5246
|
+
/* @__PURE__ */ jsx9(Text9, { color: source === "local" ? theme.ok : "#4f6db8", bold: true, children: source }),
|
|
5247
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " (s) " })
|
|
4502
5248
|
] });
|
|
4503
5249
|
if (showHelp) {
|
|
4504
|
-
const colOf = (
|
|
4505
|
-
/* @__PURE__ */
|
|
4506
|
-
keys.map(([k, desc]) => /* @__PURE__ */
|
|
4507
|
-
/* @__PURE__ */
|
|
4508
|
-
/* @__PURE__ */
|
|
5250
|
+
const colOf = (groups2) => /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: "50%", paddingRight: 2, children: groups2.map(([group, keys]) => /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, children: [
|
|
5251
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accent, children: group }),
|
|
5252
|
+
keys.map(([k, desc]) => /* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
|
|
5253
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, children: (" " + k).padEnd(19) }),
|
|
5254
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: desc })
|
|
4509
5255
|
] }, k))
|
|
4510
5256
|
] }, group)) });
|
|
4511
|
-
return /* @__PURE__ */
|
|
5257
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
4512
5258
|
copyBanner,
|
|
4513
|
-
/* @__PURE__ */
|
|
5259
|
+
/* @__PURE__ */ jsxs9(Text9, { bold: true, color: theme.accentBright, children: [
|
|
4514
5260
|
"AUDIT \u2014 all keys",
|
|
4515
5261
|
" ",
|
|
4516
|
-
/* @__PURE__ */
|
|
5262
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: frozen ? "\xB7 screen frozen \u2014 space resumes, other keys are locked" : "\xB7 press any key to close \xB7 space = copy mode (works here too)" })
|
|
4517
5263
|
] }),
|
|
4518
|
-
/* @__PURE__ */
|
|
5264
|
+
/* @__PURE__ */ jsxs9(Box9, { marginTop: 1, children: [
|
|
4519
5265
|
colOf(AUDIT_HELP.slice(0, 1)),
|
|
4520
5266
|
colOf(AUDIT_HELP.slice(1))
|
|
4521
5267
|
] })
|
|
@@ -4534,53 +5280,53 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
4534
5280
|
const maxScroll = Math.max(0, contentLines.length - bodyRows);
|
|
4535
5281
|
const off = Math.min(detailScroll, maxScroll);
|
|
4536
5282
|
const win = contentLines.slice(off, off + bodyRows);
|
|
4537
|
-
return /* @__PURE__ */
|
|
5283
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
4538
5284
|
copyBanner,
|
|
4539
|
-
/* @__PURE__ */
|
|
4540
|
-
/* @__PURE__ */
|
|
4541
|
-
/* @__PURE__ */
|
|
4542
|
-
/* @__PURE__ */
|
|
4543
|
-
/* @__PURE__ */
|
|
4544
|
-
e.dlp.length ? /* @__PURE__ */
|
|
4545
|
-
e.burst ? /* @__PURE__ */
|
|
4546
|
-
/* @__PURE__ */
|
|
5285
|
+
/* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
|
|
5286
|
+
/* @__PURE__ */ jsx9(Text9, { backgroundColor: BG2, color: "white", bold: true, children: " ENTRY " }),
|
|
5287
|
+
/* @__PURE__ */ jsx9(Text9, { color: decisionColor(e.decision), bold: true, children: " " + e.decision }),
|
|
5288
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accent, bold: true, children: " " + e.tool }),
|
|
5289
|
+
/* @__PURE__ */ jsx9(Text9, { color: loc ? theme.ok : "white", children: " " + (loc ? "LOC" : "CLD") }),
|
|
5290
|
+
e.dlp.length ? /* @__PURE__ */ jsx9(Text9, { color: theme.bad, children: " DLP!" }) : null,
|
|
5291
|
+
e.burst ? /* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: " BURST" }) : null,
|
|
5292
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2190 back" })
|
|
4547
5293
|
] }),
|
|
4548
|
-
/* @__PURE__ */
|
|
4549
|
-
/* @__PURE__ */
|
|
4550
|
-
/* @__PURE__ */
|
|
4551
|
-
/* @__PURE__ */
|
|
4552
|
-
/* @__PURE__ */
|
|
4553
|
-
/* @__PURE__ */
|
|
4554
|
-
/* @__PURE__ */
|
|
4555
|
-
/* @__PURE__ */
|
|
4556
|
-
/* @__PURE__ */
|
|
4557
|
-
/* @__PURE__ */
|
|
4558
|
-
/* @__PURE__ */
|
|
4559
|
-
/* @__PURE__ */
|
|
5294
|
+
/* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
|
|
5295
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "\u2502 when " }),
|
|
5296
|
+
/* @__PURE__ */ jsx9(Text9, { children: new Date(e.at).toLocaleString() }),
|
|
5297
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2502 perm " }),
|
|
5298
|
+
/* @__PURE__ */ jsx9(Text9, { children: e.permission || "\u2014" }),
|
|
5299
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2502 trust " }),
|
|
5300
|
+
/* @__PURE__ */ jsx9(Text9, { children: e.trust || "\u2014" }),
|
|
5301
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2502 eval " }),
|
|
5302
|
+
/* @__PURE__ */ jsx9(Text9, { color: e.evalMs != null && e.evalMs > 500 ? theme.warn : void 0, children: e.evalMs != null ? `${e.evalMs}ms` : "\u2014" }),
|
|
5303
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2502 agent " }),
|
|
5304
|
+
/* @__PURE__ */ jsx9(Text9, { children: e.agent ?? "\u2014" }),
|
|
5305
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2502" })
|
|
4560
5306
|
] }),
|
|
4561
|
-
/* @__PURE__ */
|
|
4562
|
-
/* @__PURE__ */
|
|
4563
|
-
/* @__PURE__ */
|
|
4564
|
-
/* @__PURE__ */
|
|
4565
|
-
/* @__PURE__ */
|
|
4566
|
-
/* @__PURE__ */
|
|
5307
|
+
/* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
|
|
5308
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "\u2502 session " }),
|
|
5309
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: e.session ?? "\u2014" }),
|
|
5310
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2502 rule " }),
|
|
5311
|
+
/* @__PURE__ */ jsx9(Text9, { color: e.rule && e.decision !== "ALLOW" ? theme.bad : theme.dim, children: e.rule ?? "\u2014" }),
|
|
5312
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2502" })
|
|
4567
5313
|
] }),
|
|
4568
|
-
/* @__PURE__ */
|
|
4569
|
-
/* @__PURE__ */
|
|
4570
|
-
/* @__PURE__ */
|
|
4571
|
-
/* @__PURE__ */
|
|
4572
|
-
/* @__PURE__ */
|
|
4573
|
-
/* @__PURE__ */
|
|
5314
|
+
/* @__PURE__ */ jsx9(PaneTitle, { label: "FULL CONTENT", extra: `${contentLines.length} lines${maxScroll ? ` \xB7 \u25BC${maxScroll - off} more \xB7 \u2191\u2193 scroll` : ""} \xB7 space copy \xB7 \u2190 back`, width: innerW }),
|
|
5315
|
+
/* @__PURE__ */ jsx9(Box9, { flexDirection: "column", height: bodyRows, overflow: "hidden", children: win.map((l, i) => /* @__PURE__ */ jsx9(Text9, { wrap: "truncate", children: l || " " }, off + i)) }),
|
|
5316
|
+
/* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
|
|
5317
|
+
/* @__PURE__ */ jsx9(Text9, { backgroundColor: BG2, color: "white", bold: true, children: " ENTRY " }),
|
|
5318
|
+
/* @__PURE__ */ jsx9(Text9, { backgroundColor: "#0b1530", color: "white", children: ` ${e.id} ` }),
|
|
5319
|
+
/* @__PURE__ */ jsx9(Text9, { backgroundColor: BG2, color: "white", children: " \u2191\u2193 scroll \xB7 space copy \xB7 ? all keys \xB7 \u2190 back \xB7 esc menu " })
|
|
4574
5320
|
] })
|
|
4575
5321
|
] });
|
|
4576
5322
|
}
|
|
4577
|
-
const chip = (
|
|
4578
|
-
/* @__PURE__ */
|
|
4579
|
-
|
|
5323
|
+
const chip = (label2, val, on) => /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
5324
|
+
/* @__PURE__ */ jsxs9(Text9, { color: theme.dim, children: [
|
|
5325
|
+
label2,
|
|
4580
5326
|
":"
|
|
4581
5327
|
] }),
|
|
4582
|
-
/* @__PURE__ */
|
|
4583
|
-
/* @__PURE__ */
|
|
5328
|
+
/* @__PURE__ */ jsx9(Text9, { color: on ? theme.accentBright : theme.dim, children: val }),
|
|
5329
|
+
/* @__PURE__ */ jsx9(Text9, { children: " " })
|
|
4584
5330
|
] });
|
|
4585
5331
|
if (view === "sessions") {
|
|
4586
5332
|
const headerRows2 = 5 + (editing ? 1 : 0);
|
|
@@ -4588,21 +5334,21 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
4588
5334
|
const selC = Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1));
|
|
4589
5335
|
const start2 = Math.min(Math.max(0, selC - Math.floor((listRows2 - 1) / 2)), Math.max(0, sessionsFiltered.length - listRows2));
|
|
4590
5336
|
const win = sessionsFiltered.slice(start2, start2 + listRows2);
|
|
4591
|
-
return /* @__PURE__ */
|
|
5337
|
+
return /* @__PURE__ */ jsx9(DataView, { loading: sessLoading && !frozen, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
4592
5338
|
strip,
|
|
4593
5339
|
copyBanner,
|
|
4594
|
-
/* @__PURE__ */
|
|
5340
|
+
/* @__PURE__ */ jsxs9(Box9, { children: [
|
|
4595
5341
|
srcChip,
|
|
4596
|
-
/* @__PURE__ */
|
|
4597
|
-
/* @__PURE__ */
|
|
5342
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, bold: true, children: "SESSIONS" }),
|
|
5343
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 logs (v) " }),
|
|
4598
5344
|
chip("status", SESS_STATUS[si] ?? "all", si !== 0),
|
|
4599
5345
|
chip("search", sessSearch || "\xB7", !!sessSearch)
|
|
4600
5346
|
] }),
|
|
4601
|
-
/* @__PURE__ */
|
|
4602
|
-
editing === "sess-search" ? /* @__PURE__ */
|
|
4603
|
-
/* @__PURE__ */
|
|
4604
|
-
/* @__PURE__ */
|
|
4605
|
-
|
|
5347
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter \u2192 session logs \xB7 v logs \xB7 ^R refresh \xB7 ? all keys" : "press \u2192 to browse" }),
|
|
5348
|
+
editing === "sess-search" ? /* @__PURE__ */ jsxs9(Box9, { children: [
|
|
5349
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: "search: " }),
|
|
5350
|
+
/* @__PURE__ */ jsx9(
|
|
5351
|
+
TextInput6,
|
|
4606
5352
|
{
|
|
4607
5353
|
value: sessSearch,
|
|
4608
5354
|
onChange: (v) => {
|
|
@@ -4613,8 +5359,8 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
4613
5359
|
}
|
|
4614
5360
|
)
|
|
4615
5361
|
] }) : null,
|
|
4616
|
-
/* @__PURE__ */
|
|
4617
|
-
/* @__PURE__ */
|
|
5362
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, wrap: "truncate", children: `${sessionsFiltered.length} sessions \xB7 ${selC + 1}/${sessionsFiltered.length}${start2 ? ` \xB7 \u25B2${start2}` : ""}${start2 + listRows2 < sessionsFiltered.length ? ` \xB7 \u25BC${sessionsFiltered.length - start2 - listRows2}` : ""}` }),
|
|
5363
|
+
/* @__PURE__ */ jsx9(
|
|
4618
5364
|
Table,
|
|
4619
5365
|
{
|
|
4620
5366
|
columns: [
|
|
@@ -4645,7 +5391,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
4645
5391
|
})
|
|
4646
5392
|
}
|
|
4647
5393
|
),
|
|
4648
|
-
sessionsFiltered.length === 0 ? /* @__PURE__ */
|
|
5394
|
+
sessionsFiltered.length === 0 ? /* @__PURE__ */ jsxs9(Text9, { color: theme.dim, children: [
|
|
4649
5395
|
"(no sessions",
|
|
4650
5396
|
source === "local" ? " in the local file" : "",
|
|
4651
5397
|
")"
|
|
@@ -4657,14 +5403,14 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
4657
5403
|
const selClamped = Math.min(sel, Math.max(0, pageRows.length - 1));
|
|
4658
5404
|
const maxStart = Math.max(0, pageRows.length - listRows);
|
|
4659
5405
|
const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
|
|
4660
|
-
const
|
|
4661
|
-
return /* @__PURE__ */
|
|
5406
|
+
const windowed2 = pageRows.slice(start, start + listRows);
|
|
5407
|
+
return /* @__PURE__ */ jsx9(DataView, { loading: logsLoading && !frozen, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
4662
5408
|
strip,
|
|
4663
5409
|
copyBanner,
|
|
4664
|
-
/* @__PURE__ */
|
|
5410
|
+
/* @__PURE__ */ jsxs9(Box9, { children: [
|
|
4665
5411
|
srcChip,
|
|
4666
|
-
/* @__PURE__ */
|
|
4667
|
-
/* @__PURE__ */
|
|
5412
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, bold: true, children: "LOGS" }),
|
|
5413
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 sessions (v) " }),
|
|
4668
5414
|
chip("dec", DECISIONS[di] ?? "all", di !== 0),
|
|
4669
5415
|
chip("sig", SIGNALS[gi] ?? "all", gi !== 0),
|
|
4670
5416
|
chip("tool", tool || "\xB7", !!tool),
|
|
@@ -4672,15 +5418,15 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
4672
5418
|
chip("search", search || "\xB7", !!search),
|
|
4673
5419
|
sessFilter ? chip("sess", sessFilter.slice(0, 8), true) : null
|
|
4674
5420
|
] }),
|
|
4675
|
-
/* @__PURE__ */
|
|
4676
|
-
msg ? /* @__PURE__ */
|
|
4677
|
-
editing && editing !== "sess-search" ? /* @__PURE__ */
|
|
4678
|
-
/* @__PURE__ */
|
|
5421
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 \u2190\u2192 page \xB7 v sessions \xB7 ^R refresh \xB7 ? all keys" : "press \u2192 to browse" }),
|
|
5422
|
+
msg ? /* @__PURE__ */ jsx9(Text9, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate(msg.text, cols) }) : null,
|
|
5423
|
+
editing && editing !== "sess-search" ? /* @__PURE__ */ jsxs9(Box9, { children: [
|
|
5424
|
+
/* @__PURE__ */ jsxs9(Text9, { color: theme.warn, children: [
|
|
4679
5425
|
editing,
|
|
4680
5426
|
": "
|
|
4681
5427
|
] }),
|
|
4682
|
-
/* @__PURE__ */
|
|
4683
|
-
|
|
5428
|
+
/* @__PURE__ */ jsx9(
|
|
5429
|
+
TextInput6,
|
|
4684
5430
|
{
|
|
4685
5431
|
value: editing === "tool" ? tool : editing === "agent" ? agent : search,
|
|
4686
5432
|
onChange: (v) => {
|
|
@@ -4692,9 +5438,9 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
4692
5438
|
}
|
|
4693
5439
|
)
|
|
4694
5440
|
] }) : null,
|
|
4695
|
-
/* @__PURE__ */
|
|
4696
|
-
/* @__PURE__ */
|
|
4697
|
-
pageRows.length === 0 ? /* @__PURE__ */
|
|
5441
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, wrap: "truncate", children: `${total} matched \xB7 page ${Math.min(page + 1, pages)}/${pages} \xB7 ${PAGE}/page \xB7 ${pageRows.length ? selClamped + 1 : 0}/${pageRows.length}${start ? ` \xB7 \u25B2${start} newer` : ""}${start + listRows < pageRows.length ? ` \xB7 \u25BC${pageRows.length - start - listRows} older` : ""}` }),
|
|
5442
|
+
/* @__PURE__ */ jsx9(Box9, { flexDirection: "column", overflow: "hidden", children: windowed2.map((e, i) => /* @__PURE__ */ jsx9(StreamLine, { e: toStream(e), loc: source === "local", selected: start + i === selClamped && focused, date: true }, e.id)) }),
|
|
5443
|
+
pageRows.length === 0 ? /* @__PURE__ */ jsxs9(Text9, { color: theme.dim, children: [
|
|
4698
5444
|
"(no entries",
|
|
4699
5445
|
source === "local" ? " in the local file" : "",
|
|
4700
5446
|
" \u2014 c clears filters)"
|
|
@@ -4703,9 +5449,9 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
4703
5449
|
}
|
|
4704
5450
|
|
|
4705
5451
|
// src/tui/panels/Settings.tsx
|
|
4706
|
-
import { Box as
|
|
4707
|
-
import
|
|
4708
|
-
import { useEffect as
|
|
5452
|
+
import { Box as Box10, Text as Text10, useInput as useInput9 } from "ink";
|
|
5453
|
+
import TextInput7 from "ink-text-input";
|
|
5454
|
+
import { useEffect as useEffect8, useRef as useRef4, useState as useState10 } from "react";
|
|
4709
5455
|
init_global_install();
|
|
4710
5456
|
|
|
4711
5457
|
// src/commands/doctor.ts
|
|
@@ -5099,7 +5845,7 @@ async function tuiUpdateFlow(onStatus) {
|
|
|
5099
5845
|
}
|
|
5100
5846
|
|
|
5101
5847
|
// src/tui/panels/Settings.tsx
|
|
5102
|
-
import { Fragment as
|
|
5848
|
+
import { Fragment as Fragment6, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
5103
5849
|
var EVENTS = ["denials", "allowed", "all"];
|
|
5104
5850
|
var SPIN2 = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
5105
5851
|
var SIGNALS2 = ["any", "deny", "dlp", "ratelimit"];
|
|
@@ -5125,19 +5871,19 @@ function SettingsPanel({
|
|
|
5125
5871
|
}) {
|
|
5126
5872
|
void active2;
|
|
5127
5873
|
const { cols, rows } = usePanelSize();
|
|
5128
|
-
const [sel, setSel] =
|
|
5129
|
-
const [editing, setEditing] =
|
|
5130
|
-
const [input, setInput] =
|
|
5131
|
-
const [confirmDel, setConfirmDel] =
|
|
5132
|
-
const [msg, setMsg] =
|
|
5133
|
-
const [busy, setBusy] =
|
|
5134
|
-
const [editor, setEditor] =
|
|
5135
|
-
const [latest, setLatest] =
|
|
5136
|
-
const [diagBusy, setDiagBusy] =
|
|
5137
|
-
const [autoUp, setAutoUp] =
|
|
5138
|
-
const [updBusy, setUpdBusy] =
|
|
5139
|
-
const [llSetting, setLlSetting] =
|
|
5140
|
-
|
|
5874
|
+
const [sel, setSel] = useState10(0);
|
|
5875
|
+
const [editing, setEditing] = useState10(null);
|
|
5876
|
+
const [input, setInput] = useState10("");
|
|
5877
|
+
const [confirmDel, setConfirmDel] = useState10(null);
|
|
5878
|
+
const [msg, setMsg] = useState10(null);
|
|
5879
|
+
const [busy, setBusy] = useState10(false);
|
|
5880
|
+
const [editor, setEditor] = useState10(null);
|
|
5881
|
+
const [latest, setLatest] = useState10(null);
|
|
5882
|
+
const [diagBusy, setDiagBusy] = useState10(null);
|
|
5883
|
+
const [autoUp, setAutoUp] = useState10(() => autoUpdateEnabled());
|
|
5884
|
+
const [updBusy, setUpdBusy] = useState10(false);
|
|
5885
|
+
const [llSetting, setLlSetting] = useState10(null);
|
|
5886
|
+
useEffect8(() => {
|
|
5141
5887
|
const read = () => {
|
|
5142
5888
|
try {
|
|
5143
5889
|
setLlSetting(localLogsSetting());
|
|
@@ -5148,8 +5894,8 @@ function SettingsPanel({
|
|
|
5148
5894
|
const t = setInterval(read, 5e3);
|
|
5149
5895
|
return () => clearInterval(t);
|
|
5150
5896
|
}, []);
|
|
5151
|
-
const [canInstall, setCanInstall] =
|
|
5152
|
-
|
|
5897
|
+
const [canInstall, setCanInstall] = useState10(null);
|
|
5898
|
+
useEffect8(() => {
|
|
5153
5899
|
let live2 = true;
|
|
5154
5900
|
globalInstallCheck().then((r) => {
|
|
5155
5901
|
if (live2) setCanInstall(r.writable);
|
|
@@ -5159,8 +5905,8 @@ function SettingsPanel({
|
|
|
5159
5905
|
live2 = false;
|
|
5160
5906
|
};
|
|
5161
5907
|
}, []);
|
|
5162
|
-
const [diag, setDiag] =
|
|
5163
|
-
const [diagStep, setDiagStep] =
|
|
5908
|
+
const [diag, setDiag] = useState10(null);
|
|
5909
|
+
const [diagStep, setDiagStep] = useState10(0);
|
|
5164
5910
|
const DIAG_STEPS = {
|
|
5165
5911
|
doctor: ["login", "active policy", "guard hook", "agent hooks", "local logs"],
|
|
5166
5912
|
repair: ["inspecting protection files", "rewriting hook files", "registering with agents", "re-locking"]
|
|
@@ -5170,14 +5916,14 @@ function SettingsPanel({
|
|
|
5170
5916
|
const left = min - (Date.now() - started);
|
|
5171
5917
|
if (left > 0) await new Promise((res) => setTimeout(res, left));
|
|
5172
5918
|
};
|
|
5173
|
-
|
|
5919
|
+
useEffect8(() => {
|
|
5174
5920
|
if (!diagBusy) return;
|
|
5175
5921
|
const total = DIAG_STEPS[diagBusy].length;
|
|
5176
5922
|
const step = setInterval(() => setDiagStep((s) => Math.min(s + 1, total - 1)), 220);
|
|
5177
5923
|
return () => clearInterval(step);
|
|
5178
5924
|
}, [diagBusy]);
|
|
5179
5925
|
const ver = currentVersion();
|
|
5180
|
-
|
|
5926
|
+
useEffect8(() => {
|
|
5181
5927
|
let live2 = true;
|
|
5182
5928
|
latestVersion().then((v) => {
|
|
5183
5929
|
if (live2) setLatest(v);
|
|
@@ -5187,20 +5933,20 @@ function SettingsPanel({
|
|
|
5187
5933
|
live2 = false;
|
|
5188
5934
|
};
|
|
5189
5935
|
}, []);
|
|
5190
|
-
const [accounts, setAccounts] =
|
|
5191
|
-
const [login, setLogin] =
|
|
5192
|
-
const [tick, setTick] =
|
|
5193
|
-
const abort =
|
|
5936
|
+
const [accounts, setAccounts] = useState10(() => listAccounts());
|
|
5937
|
+
const [login, setLogin] = useState10(null);
|
|
5938
|
+
const [tick, setTick] = useState10(0);
|
|
5939
|
+
const abort = useRef4(false);
|
|
5194
5940
|
const refreshAccounts = () => setAccounts(listAccounts());
|
|
5195
|
-
const [guardHere, setGuardHere] =
|
|
5196
|
-
const [guardVer, setGuardVer] =
|
|
5197
|
-
const [guardOld, setGuardOld] =
|
|
5941
|
+
const [guardHere, setGuardHere] = useState10(() => isGuardInstalled());
|
|
5942
|
+
const [guardVer, setGuardVer] = useState10(() => installedGuardVersion());
|
|
5943
|
+
const [guardOld, setGuardOld] = useState10(() => guardHookOutdated());
|
|
5198
5944
|
const codexNeedsTrust = () => {
|
|
5199
5945
|
if (!codexDetected()) return false;
|
|
5200
5946
|
const cx = codexHooksStatus();
|
|
5201
5947
|
return cx.registered && !cx.trusted;
|
|
5202
5948
|
};
|
|
5203
|
-
const [codexTrust, setCodexTrust] =
|
|
5949
|
+
const [codexTrust, setCodexTrust] = useState10(codexNeedsTrust);
|
|
5204
5950
|
const refreshGuard = () => {
|
|
5205
5951
|
setGuardHere(isGuardInstalled());
|
|
5206
5952
|
setGuardVer(installedGuardVersion());
|
|
@@ -5208,12 +5954,12 @@ function SettingsPanel({
|
|
|
5208
5954
|
setCodexTrust(codexNeedsTrust());
|
|
5209
5955
|
};
|
|
5210
5956
|
const locked = accounts.length === 0;
|
|
5211
|
-
const [srv, setSrv] =
|
|
5212
|
-
|
|
5957
|
+
const [srv, setSrv] = useState10(() => logsServerStatus());
|
|
5958
|
+
useEffect8(() => {
|
|
5213
5959
|
const t = setInterval(() => setSrv(logsServerStatus()), 3e3);
|
|
5214
5960
|
return () => clearInterval(t);
|
|
5215
5961
|
}, []);
|
|
5216
|
-
|
|
5962
|
+
useEffect8(() => {
|
|
5217
5963
|
const loggingIn = login && login.phase !== "done" && login.phase !== "error";
|
|
5218
5964
|
if (!loggingIn && !diagBusy && !updBusy) return;
|
|
5219
5965
|
const t = setInterval(() => setTick((n) => n + 1), 120);
|
|
@@ -5226,8 +5972,8 @@ function SettingsPanel({
|
|
|
5226
5972
|
const selfQ = useLoader(() => listAccounts().length ? api.settings.getSelfProtection() : Promise.resolve(null));
|
|
5227
5973
|
const local = localQ.data;
|
|
5228
5974
|
const selfProt = selfQ.data;
|
|
5229
|
-
const autoInstalledRef =
|
|
5230
|
-
|
|
5975
|
+
const autoInstalledRef = useRef4(false);
|
|
5976
|
+
useEffect8(() => {
|
|
5231
5977
|
if (autoInstalledRef.current) return;
|
|
5232
5978
|
if (guardHere && guardOld) {
|
|
5233
5979
|
autoInstalledRef.current = true;
|
|
@@ -5239,7 +5985,7 @@ function SettingsPanel({
|
|
|
5239
5985
|
}
|
|
5240
5986
|
}
|
|
5241
5987
|
}, [guardHere, guardOld]);
|
|
5242
|
-
const [hidden, setHidden] =
|
|
5988
|
+
const [hidden, setHidden] = useState10(/* @__PURE__ */ new Set());
|
|
5243
5989
|
const webhooks = (whQ.data?.webhooks ?? []).filter((w) => !hidden.has("wh:" + w.id));
|
|
5244
5990
|
const alerts = (alertQ.data?.rules ?? []).filter((r) => !hidden.has("alert:" + r.id));
|
|
5245
5991
|
const hasEmailAlert = alerts.some((r) => alertChannel(r) === "email");
|
|
@@ -5275,12 +6021,12 @@ function SettingsPanel({
|
|
|
5275
6021
|
guardQ.reload();
|
|
5276
6022
|
selfQ.reload();
|
|
5277
6023
|
};
|
|
5278
|
-
const run = (
|
|
6024
|
+
const run = (label2, fn, reload) => {
|
|
5279
6025
|
if (busy) return;
|
|
5280
6026
|
setBusy(true);
|
|
5281
|
-
setMsg({ text:
|
|
6027
|
+
setMsg({ text: label2 + "\u2026", level: "ok" });
|
|
5282
6028
|
fn().then(() => {
|
|
5283
|
-
setMsg({ text: "\u2713 " +
|
|
6029
|
+
setMsg({ text: "\u2713 " + label2, level: "ok" });
|
|
5284
6030
|
reload();
|
|
5285
6031
|
}).catch((e) => setMsg({ text: "\u2717 " + (e instanceof Error ? e.message : String(e)), level: "bad" })).finally(() => setBusy(false));
|
|
5286
6032
|
};
|
|
@@ -5522,7 +6268,7 @@ function SettingsPanel({
|
|
|
5522
6268
|
setEditor({ ...editor, target: v, field: 1 });
|
|
5523
6269
|
}
|
|
5524
6270
|
};
|
|
5525
|
-
|
|
6271
|
+
useInput9(
|
|
5526
6272
|
(inp, key) => {
|
|
5527
6273
|
if (login && (login.phase === "starting" || login.phase === "waiting")) {
|
|
5528
6274
|
if (key.escape) {
|
|
@@ -5657,58 +6403,58 @@ function SettingsPanel({
|
|
|
5657
6403
|
);
|
|
5658
6404
|
const spin = SPIN2[tick % SPIN2.length];
|
|
5659
6405
|
if (login && (login.phase === "starting" || login.phase === "waiting")) {
|
|
5660
|
-
return /* @__PURE__ */
|
|
5661
|
-
/* @__PURE__ */
|
|
5662
|
-
login.phase === "starting" ? /* @__PURE__ */
|
|
6406
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
|
|
6407
|
+
/* @__PURE__ */ jsx10(Text10, { bold: true, color: theme.accentBright, children: "Add an account" }),
|
|
6408
|
+
login.phase === "starting" ? /* @__PURE__ */ jsxs10(Text10, { color: theme.dim, children: [
|
|
5663
6409
|
spin,
|
|
5664
6410
|
" starting device pairing\u2026"
|
|
5665
|
-
] }) : /* @__PURE__ */
|
|
5666
|
-
/* @__PURE__ */
|
|
5667
|
-
/* @__PURE__ */
|
|
5668
|
-
/* @__PURE__ */
|
|
5669
|
-
/* @__PURE__ */
|
|
6411
|
+
] }) : /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", marginTop: 1, children: [
|
|
6412
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "Opening your browser to authorize. If it didn't open, visit:" }),
|
|
6413
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.accentBright, bold: true, wrap: "truncate", children: login.url }),
|
|
6414
|
+
/* @__PURE__ */ jsxs10(Box10, { marginTop: 1, children: [
|
|
6415
|
+
/* @__PURE__ */ jsxs10(Text10, { color: theme.warn, children: [
|
|
5670
6416
|
spin,
|
|
5671
6417
|
" waiting for authorization\u2026 "
|
|
5672
6418
|
] }),
|
|
5673
|
-
/* @__PURE__ */
|
|
6419
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "esc to cancel" })
|
|
5674
6420
|
] })
|
|
5675
6421
|
] })
|
|
5676
6422
|
] });
|
|
5677
6423
|
}
|
|
5678
6424
|
if (editor) {
|
|
5679
|
-
const fieldRow = (idx,
|
|
5680
|
-
/* @__PURE__ */
|
|
5681
|
-
/* @__PURE__ */
|
|
6425
|
+
const fieldRow = (idx, label2, value, hint) => /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
6426
|
+
/* @__PURE__ */ jsx10(Text10, { color: editor.field === idx ? theme.accentBright : theme.dim, children: editor.field === idx ? "\u25B8 " : " " }),
|
|
6427
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: label2.padEnd(11) }),
|
|
5682
6428
|
value,
|
|
5683
|
-
editor.field === idx && hint ? /* @__PURE__ */
|
|
6429
|
+
editor.field === idx && hint ? /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " " + hint }) : null
|
|
5684
6430
|
] });
|
|
5685
|
-
return /* @__PURE__ */
|
|
5686
|
-
/* @__PURE__ */
|
|
6431
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
|
|
6432
|
+
/* @__PURE__ */ jsxs10(Text10, { bold: true, color: theme.accentBright, children: [
|
|
5687
6433
|
editor.id ? "Edit" : "New",
|
|
5688
6434
|
" ",
|
|
5689
6435
|
editor.channel,
|
|
5690
6436
|
" alert"
|
|
5691
6437
|
] }),
|
|
5692
|
-
/* @__PURE__ */
|
|
5693
|
-
editing === "alert-target" ? /* @__PURE__ */
|
|
5694
|
-
/* @__PURE__ */
|
|
5695
|
-
/* @__PURE__ */
|
|
5696
|
-
] }) : msg ? /* @__PURE__ */
|
|
5697
|
-
/* @__PURE__ */
|
|
5698
|
-
fieldRow(0, "target", editor.target ? /* @__PURE__ */
|
|
5699
|
-
fieldRow(1, "signal", /* @__PURE__ */
|
|
5700
|
-
fieldRow(2, "threshold", /* @__PURE__ */
|
|
5701
|
-
fieldRow(3, "window", /* @__PURE__ */
|
|
5702
|
-
editor.id ? fieldRow(4, "status", editor.enabled ? /* @__PURE__ */
|
|
6438
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "\u2191\u2193 field \xB7 \u2190\u2192 change \xB7 e edit target \xB7 enter save \xB7 esc cancel" }),
|
|
6439
|
+
editing === "alert-target" ? /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, children: [
|
|
6440
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.warn, children: editor.channel === "email" ? "e-mail address: " : "telegram chat id (from @userinfobot): " }),
|
|
6441
|
+
/* @__PURE__ */ jsx10(TextInput7, { value: input, onChange: setInput, onSubmit: submitInput })
|
|
6442
|
+
] }) : msg ? /* @__PURE__ */ jsx10(Text10, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate(msg.text, cols) }) : /* @__PURE__ */ jsx10(Text10, { children: " " }),
|
|
6443
|
+
/* @__PURE__ */ jsxs10(Box10, { marginTop: 1, flexDirection: "column", children: [
|
|
6444
|
+
fieldRow(0, "target", editor.target ? /* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: truncate(editor.target, cols - 16) }) : /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "empty \u2014 press e" }), "press e to edit"),
|
|
6445
|
+
fieldRow(1, "signal", /* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: SIGNAL_LABEL[editor.signal] }), "\u2190\u2192 change"),
|
|
6446
|
+
fieldRow(2, "threshold", /* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: `\u2265 ${editor.threshold}` }), "\u2190\u2192 \xB15"),
|
|
6447
|
+
fieldRow(3, "window", /* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: winLabel(editor.windowSeconds) }), "\u2190\u2192 change"),
|
|
6448
|
+
editor.id ? fieldRow(4, "status", editor.enabled ? /* @__PURE__ */ jsx10(Text10, { color: theme.ok, children: "on" }) : /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "off" }), "\u2190\u2192 turn on/off") : null
|
|
5703
6449
|
] }),
|
|
5704
|
-
/* @__PURE__ */
|
|
6450
|
+
/* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: editor.id && !editor.enabled ? /* @__PURE__ */ jsx10(Text10, { color: theme.warn, children: "disabled \u2014 this alert will NOT fire until you set status back to on" }) : /* @__PURE__ */ jsxs10(Text10, { color: theme.dim, children: [
|
|
5705
6451
|
"fires when ",
|
|
5706
|
-
/* @__PURE__ */
|
|
6452
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: SIGNAL_LABEL[editor.signal] }),
|
|
5707
6453
|
" reach ",
|
|
5708
|
-
/* @__PURE__ */
|
|
6454
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: editor.threshold }),
|
|
5709
6455
|
" within",
|
|
5710
6456
|
" ",
|
|
5711
|
-
/* @__PURE__ */
|
|
6457
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: winLabel(editor.windowSeconds) })
|
|
5712
6458
|
] }) })
|
|
5713
6459
|
] });
|
|
5714
6460
|
}
|
|
@@ -5717,140 +6463,140 @@ function SettingsPanel({
|
|
|
5717
6463
|
const authError = rawError && /invalid api key|authentication|401|unauthor|not logged in/i.test(rawError) ? rawError : null;
|
|
5718
6464
|
const noKey = !!authError && /not logged in/i.test(authError);
|
|
5719
6465
|
const error = authError ? null : rawError;
|
|
5720
|
-
const onOff = (on) => on ? /* @__PURE__ */
|
|
6466
|
+
const onOff = (on) => on ? /* @__PURE__ */ jsx10(Text10, { color: theme.ok, children: "on " }) : /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "off" });
|
|
5721
6467
|
const chanOf = (rule) => [...rule.emails ?? [], ...(rule.telegram ?? []).map((t) => "tg " + t)].join(", ") || "\u2014";
|
|
5722
6468
|
const isCur = (r) => keyOf(r) === keyOf(cur) && focused;
|
|
5723
|
-
const cursor = (r) => /* @__PURE__ */
|
|
6469
|
+
const cursor = (r) => /* @__PURE__ */ jsx10(Text10, { color: isCur(r) ? theme.accentBright : theme.dim, children: isCur(r) ? "\u25B8 " : " " });
|
|
5724
6470
|
const rowLine = (r) => {
|
|
5725
6471
|
switch (r.kind) {
|
|
5726
6472
|
case "acct": {
|
|
5727
6473
|
const a = r.acc;
|
|
5728
6474
|
const isView = viewApiKey ? a.apiKey === viewApiKey : accounts[0]?.apiKey === a.apiKey;
|
|
5729
6475
|
const isActive = isActiveAccount(a.apiKey);
|
|
5730
|
-
return /* @__PURE__ */
|
|
6476
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
5731
6477
|
cursor(r),
|
|
5732
|
-
/* @__PURE__ */
|
|
5733
|
-
isView ? /* @__PURE__ */
|
|
5734
|
-
isActive ? /* @__PURE__ */
|
|
5735
|
-
/* @__PURE__ */
|
|
6478
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: truncate(acctLabel(a), 30).padEnd(31) }),
|
|
6479
|
+
isView ? /* @__PURE__ */ jsx10(Text10, { color: theme.ok, children: "\u25CF viewing " }) : /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " " }),
|
|
6480
|
+
isActive ? /* @__PURE__ */ jsx10(Text10, { color: theme.warn, children: "ACTIVE " }) : /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " " }),
|
|
6481
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: a.project ? truncate(a.project, 20) : "" })
|
|
5736
6482
|
] });
|
|
5737
6483
|
}
|
|
5738
6484
|
case "acct-add":
|
|
5739
|
-
return /* @__PURE__ */
|
|
6485
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
5740
6486
|
cursor(r),
|
|
5741
|
-
/* @__PURE__ */
|
|
6487
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "+ add account (enter \u2014 device login in the browser)" })
|
|
5742
6488
|
] });
|
|
5743
6489
|
case "guard":
|
|
5744
|
-
return /* @__PURE__ */
|
|
6490
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
5745
6491
|
cursor(r),
|
|
5746
|
-
/* @__PURE__ */
|
|
5747
|
-
!guardHere ? /* @__PURE__ */
|
|
5748
|
-
/* @__PURE__ */
|
|
5749
|
-
/* @__PURE__ */
|
|
5750
|
-
/* @__PURE__ */
|
|
5751
|
-
] }) : /* @__PURE__ */
|
|
5752
|
-
/* @__PURE__ */
|
|
5753
|
-
/* @__PURE__ */
|
|
5754
|
-
/* @__PURE__ */
|
|
5755
|
-
codexTrust ? /* @__PURE__ */
|
|
6492
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "guard".padEnd(11) }),
|
|
6493
|
+
!guardHere ? /* @__PURE__ */ jsxs10(Fragment6, { children: [
|
|
6494
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.bad, bold: true, children: "removed" }),
|
|
6495
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " \xB7 " }),
|
|
6496
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.accentBright, children: "enter to install" })
|
|
6497
|
+
] }) : /* @__PURE__ */ jsxs10(Fragment6, { children: [
|
|
6498
|
+
/* @__PURE__ */ jsx10(Text10, { color: guardOld ? theme.warn : theme.ok, children: `v${guardVer ?? "?"}` }),
|
|
6499
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: guardOld ? " \xB7 update available \xB7 enter update" : " (latest) \xB7 enter reinstall" }),
|
|
6500
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " \xB7 d remove" }),
|
|
6501
|
+
codexTrust ? /* @__PURE__ */ jsx10(Text10, { color: theme.warn, children: " \xB7 codex: run /hooks once to trust" }) : null
|
|
5756
6502
|
] })
|
|
5757
6503
|
] });
|
|
5758
6504
|
case "self":
|
|
5759
|
-
return /* @__PURE__ */
|
|
6505
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
5760
6506
|
cursor(r),
|
|
5761
|
-
/* @__PURE__ */
|
|
5762
|
-
selfProt ? onOff(selfProt.enabled) : /* @__PURE__ */
|
|
5763
|
-
/* @__PURE__ */
|
|
6507
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "self-prot".padEnd(11) }),
|
|
6508
|
+
selfProt ? onOff(selfProt.enabled) : /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "\u2026" }),
|
|
6509
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " blocks agents editing SolonGate\u2019s own hooks/config \xB7 enter toggles" })
|
|
5764
6510
|
] });
|
|
5765
6511
|
case "doctor":
|
|
5766
|
-
return /* @__PURE__ */
|
|
6512
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
5767
6513
|
cursor(r),
|
|
5768
|
-
/* @__PURE__ */
|
|
5769
|
-
diagBusy === "doctor" ? /* @__PURE__ */
|
|
6514
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "doctor".padEnd(11) }),
|
|
6515
|
+
diagBusy === "doctor" ? /* @__PURE__ */ jsx10(Text10, { color: theme.accentBright, children: `${spin} running health check\u2026` }) : /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "health check: login, policy, guard, hooks, local logs \xB7 enter runs it" })
|
|
5770
6516
|
] });
|
|
5771
6517
|
case "repair":
|
|
5772
|
-
return /* @__PURE__ */
|
|
6518
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
5773
6519
|
cursor(r),
|
|
5774
|
-
/* @__PURE__ */
|
|
5775
|
-
diagBusy === "repair" ? /* @__PURE__ */
|
|
6520
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "repair".padEnd(11) }),
|
|
6521
|
+
diagBusy === "repair" ? /* @__PURE__ */ jsx10(Text10, { color: theme.accentBright, children: `${spin} restoring protection\u2026` }) : /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "restore every protection file after tampering or deletion \xB7 enter runs it" })
|
|
5776
6522
|
] });
|
|
5777
6523
|
case "cli-update": {
|
|
5778
6524
|
const behind = latest ? newerThan(latest, ver) : false;
|
|
5779
|
-
return /* @__PURE__ */
|
|
6525
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
5780
6526
|
cursor(r),
|
|
5781
|
-
/* @__PURE__ */
|
|
5782
|
-
/* @__PURE__ */
|
|
5783
|
-
updBusy ? /* @__PURE__ */
|
|
6527
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "version".padEnd(11) }),
|
|
6528
|
+
/* @__PURE__ */ jsx10(Text10, { color: behind ? theme.warn : theme.ok, children: `v${ver}` }),
|
|
6529
|
+
updBusy ? /* @__PURE__ */ jsx10(Text10, { color: theme.accentBright, children: ` ${spin} updating\u2026` }) : behind && canInstall === false ? (
|
|
5784
6530
|
// Said before they press enter, not after a minute of npm noise.
|
|
5785
|
-
/* @__PURE__ */
|
|
5786
|
-
) : behind ? /* @__PURE__ */
|
|
6531
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.warn, children: ` v${latest} on npm \xB7 needs sudo here \xB7 enter shows how` })
|
|
6532
|
+
) : behind ? /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: ` v${latest} on npm \xB7 enter updates now` }) : /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: latest ? " latest \xB7 enter checks again" : " enter checks npm and updates" })
|
|
5787
6533
|
] });
|
|
5788
6534
|
}
|
|
5789
6535
|
case "auto-update":
|
|
5790
|
-
return /* @__PURE__ */
|
|
6536
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
5791
6537
|
cursor(r),
|
|
5792
|
-
/* @__PURE__ */
|
|
6538
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "auto".padEnd(11) }),
|
|
5793
6539
|
onOff(autoUp),
|
|
5794
|
-
/* @__PURE__ */
|
|
6540
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: autoUpdateForcedByEnv() ? " set by SOLONGATE_AUTO_UPDATE" : canInstall === false ? " cannot work here: npm needs sudo, so updates stay manual \xB7 enter toggles" : autoUp ? " installs new versions in the background \xB7 enter toggles" : " off: nothing installs on its own (npm -g may need sudo) \xB7 enter toggles" })
|
|
5795
6541
|
] });
|
|
5796
6542
|
case "ll-enabled":
|
|
5797
|
-
return /* @__PURE__ */
|
|
6543
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
5798
6544
|
cursor(r),
|
|
5799
|
-
/* @__PURE__ */
|
|
6545
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "enabled ".padEnd(11) }),
|
|
5800
6546
|
onOff(!!local?.enabled),
|
|
5801
|
-
/* @__PURE__ */
|
|
6547
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " enter toggles" })
|
|
5802
6548
|
] });
|
|
5803
6549
|
case "ll-path":
|
|
5804
|
-
return /* @__PURE__ */
|
|
6550
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
5805
6551
|
cursor(r),
|
|
5806
|
-
/* @__PURE__ */
|
|
6552
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "path ".padEnd(11) }),
|
|
5807
6553
|
local?.path ? (
|
|
5808
6554
|
// The folder is a PROJECT setting shared by every device on it, so
|
|
5809
6555
|
// it can name a path that only exists on another OS. The hooks then
|
|
5810
6556
|
// quietly fall back and this row was the last place still claiming
|
|
5811
6557
|
// the configured path was in use.
|
|
5812
|
-
llSetting && local.enabled && !llSetting.usableHere ? /* @__PURE__ */
|
|
5813
|
-
/* @__PURE__ */
|
|
5814
|
-
/* @__PURE__ */
|
|
5815
|
-
] }) : /* @__PURE__ */
|
|
5816
|
-
) : /* @__PURE__ */
|
|
6558
|
+
llSetting && local.enabled && !llSetting.usableHere ? /* @__PURE__ */ jsxs10(Fragment6, { children: [
|
|
6559
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.warn, children: truncate(local.path, Math.max(12, Math.floor((cols - 20) / 2))) }),
|
|
6560
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: ` not valid here \u2192 ${truncate(llSetting.file, Math.max(12, Math.floor((cols - 20) / 2)))}` })
|
|
6561
|
+
] }) : /* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: truncate(local.path, cols - 14) })
|
|
6562
|
+
) : /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "not set \u2014 enter to edit" })
|
|
5817
6563
|
] });
|
|
5818
6564
|
case "ll-server":
|
|
5819
|
-
return /* @__PURE__ */
|
|
6565
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
5820
6566
|
cursor(r),
|
|
5821
|
-
/* @__PURE__ */
|
|
5822
|
-
srv.running ? /* @__PURE__ */
|
|
5823
|
-
/* @__PURE__ */
|
|
6567
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "dashboard".padEnd(11) }),
|
|
6568
|
+
srv.running ? /* @__PURE__ */ jsx10(Text10, { color: theme.ok, children: `running 127.0.0.1:${srv.port}` }) : /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "stopped" }),
|
|
6569
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: srv.running ? " survives closing the dataroom \xB7 enter stops" : " enter starts the dashboard local-logs link" })
|
|
5824
6570
|
] });
|
|
5825
6571
|
case "wh":
|
|
5826
|
-
return /* @__PURE__ */
|
|
6572
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
5827
6573
|
cursor(r),
|
|
5828
6574
|
onOff(r.wh.enabled),
|
|
5829
|
-
/* @__PURE__ */
|
|
5830
|
-
/* @__PURE__ */
|
|
6575
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.warn, children: (" " + r.wh.events).padEnd(9) }),
|
|
6576
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: truncate(r.wh.url, cols - 16) })
|
|
5831
6577
|
] });
|
|
5832
6578
|
case "wh-add":
|
|
5833
|
-
return /* @__PURE__ */
|
|
6579
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
5834
6580
|
cursor(r),
|
|
5835
|
-
/* @__PURE__ */
|
|
6581
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "+ add webhook (enter)" })
|
|
5836
6582
|
] });
|
|
5837
6583
|
case "alert":
|
|
5838
|
-
return /* @__PURE__ */
|
|
6584
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
5839
6585
|
cursor(r),
|
|
5840
6586
|
onOff(r.rule.enabled),
|
|
5841
|
-
/* @__PURE__ */
|
|
5842
|
-
/* @__PURE__ */
|
|
5843
|
-
/* @__PURE__ */
|
|
6587
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.warn, children: (" " + SIGNAL_LABEL[r.rule.signal]).padEnd(13) }),
|
|
6588
|
+
/* @__PURE__ */ jsx10(Text10, { children: `\u2265${r.rule.threshold}/${winLabel(r.rule.windowSeconds)} `.padEnd(11) }),
|
|
6589
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: truncate(chanOf(r.rule), cols - 34) })
|
|
5844
6590
|
] });
|
|
5845
6591
|
case "alert-add-email":
|
|
5846
|
-
return /* @__PURE__ */
|
|
6592
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
5847
6593
|
cursor(r),
|
|
5848
|
-
/* @__PURE__ */
|
|
6594
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "+ add email alert (enter)" })
|
|
5849
6595
|
] });
|
|
5850
6596
|
case "alert-add-tg":
|
|
5851
|
-
return /* @__PURE__ */
|
|
6597
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
5852
6598
|
cursor(r),
|
|
5853
|
-
/* @__PURE__ */
|
|
6599
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "+ add telegram alert (enter \u2014 numeric chat id from @userinfobot)" })
|
|
5854
6600
|
] });
|
|
5855
6601
|
}
|
|
5856
6602
|
};
|
|
@@ -5870,31 +6616,31 @@ function SettingsPanel({
|
|
|
5870
6616
|
const sec = sectionOf(r);
|
|
5871
6617
|
if (sec !== lastSec) {
|
|
5872
6618
|
if (lastSec) {
|
|
5873
|
-
lineEls.push(/* @__PURE__ */
|
|
6619
|
+
lineEls.push(/* @__PURE__ */ jsx10(Text10, { children: " " }, "sp:" + sec));
|
|
5874
6620
|
lineKey.push("");
|
|
5875
6621
|
}
|
|
5876
6622
|
lastSec = sec;
|
|
5877
6623
|
lineEls.push(
|
|
5878
|
-
/* @__PURE__ */
|
|
5879
|
-
/* @__PURE__ */
|
|
5880
|
-
/* @__PURE__ */
|
|
6624
|
+
/* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
6625
|
+
/* @__PURE__ */ jsx10(Text10, { bold: true, color: theme.accentBright, children: sec }),
|
|
6626
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " \u2014 " + SECTION_DESC[sec] })
|
|
5881
6627
|
] }, "h:" + sec)
|
|
5882
6628
|
);
|
|
5883
6629
|
lineKey.push("");
|
|
5884
6630
|
if ((sec === "WEBHOOKS" || sec === "ALERTS") && local?.enabled) {
|
|
5885
6631
|
lineEls.push(
|
|
5886
|
-
/* @__PURE__ */
|
|
6632
|
+
/* @__PURE__ */ jsx10(Text10, { wrap: "truncate", color: theme.warn, children: ` \u26A0 local logs on \u2014 real denials stay on this machine, so ${sec.toLowerCase()} do NOT fire${sec === "WEBHOOKS" ? " (t test still works)" : ""}` }, "warn:" + sec)
|
|
5887
6633
|
);
|
|
5888
6634
|
lineKey.push("");
|
|
5889
6635
|
}
|
|
5890
6636
|
}
|
|
5891
|
-
lineEls.push(/* @__PURE__ */
|
|
6637
|
+
lineEls.push(/* @__PURE__ */ jsx10(Box10, { children: rowLine(r) }, keyOf(r)));
|
|
5892
6638
|
lineKey.push(keyOf(r));
|
|
5893
6639
|
if ((r.kind === "doctor" || r.kind === "repair") && diagBusy === r.kind) {
|
|
5894
|
-
DIAG_STEPS[r.kind].forEach((
|
|
6640
|
+
DIAG_STEPS[r.kind].forEach((label2, i) => {
|
|
5895
6641
|
const done = i < diagStep;
|
|
5896
6642
|
lineEls.push(
|
|
5897
|
-
/* @__PURE__ */
|
|
6643
|
+
/* @__PURE__ */ jsx10(Text10, { wrap: "truncate", color: done ? theme.ok : i === diagStep ? theme.accentBright : theme.dim, children: ` ${done ? "\u2713" : i === diagStep ? spin : " "} ${label2}` }, `step:${r.kind}:${i}`)
|
|
5898
6644
|
);
|
|
5899
6645
|
lineKey.push("");
|
|
5900
6646
|
});
|
|
@@ -5902,8 +6648,8 @@ function SettingsPanel({
|
|
|
5902
6648
|
if (diag && (r.kind === "doctor" && diag.of === "doctor" || r.kind === "repair" && diag.of === "repair" || r.kind === "cli-update" && diag.of === "cli-update")) {
|
|
5903
6649
|
diag.lines.forEach((l, i) => {
|
|
5904
6650
|
lineEls.push(
|
|
5905
|
-
/* @__PURE__ */
|
|
5906
|
-
|
|
6651
|
+
/* @__PURE__ */ jsx10(
|
|
6652
|
+
Text10,
|
|
5907
6653
|
{
|
|
5908
6654
|
wrap: "truncate",
|
|
5909
6655
|
color: l.level === "ok" ? theme.ok : l.level === "warn" ? theme.warn : l.level === "bad" ? theme.bad : theme.dim,
|
|
@@ -5918,16 +6664,16 @@ function SettingsPanel({
|
|
|
5918
6664
|
});
|
|
5919
6665
|
if (hasEmailAlert && hasTgAlert) {
|
|
5920
6666
|
lineEls.push(
|
|
5921
|
-
/* @__PURE__ */
|
|
6667
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " both channels in use \u2014 remove one (d) to change it" }, "both-note")
|
|
5922
6668
|
);
|
|
5923
6669
|
lineKey.push("");
|
|
5924
6670
|
}
|
|
5925
|
-
lineEls.push(/* @__PURE__ */
|
|
6671
|
+
lineEls.push(/* @__PURE__ */ jsx10(Text10, { children: " " }, "ver-sp"));
|
|
5926
6672
|
lineKey.push("");
|
|
5927
6673
|
lineEls.push(
|
|
5928
|
-
/* @__PURE__ */
|
|
6674
|
+
/* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", color: theme.dim, children: [
|
|
5929
6675
|
`solongate v${ver}`,
|
|
5930
|
-
latest ? newerThan(latest, ver) ? /* @__PURE__ */
|
|
6676
|
+
latest ? newerThan(latest, ver) ? /* @__PURE__ */ jsx10(Text10, { color: theme.warn, children: ` \xB7 v${latest} on npm \u2014 ${autoUp ? "auto-updating" : "UPDATES above"}` }) : /* @__PURE__ */ jsx10(Text10, { color: theme.ok, children: " \xB7 latest" }) : null
|
|
5931
6677
|
] }, "ver")
|
|
5932
6678
|
);
|
|
5933
6679
|
lineKey.push("");
|
|
@@ -5938,36 +6684,50 @@ function SettingsPanel({
|
|
|
5938
6684
|
const win = lineEls.slice(startL, startL + budget);
|
|
5939
6685
|
const moreAbove = startL;
|
|
5940
6686
|
const moreBelow = Math.max(0, lineEls.length - (startL + budget));
|
|
5941
|
-
return /* @__PURE__ */
|
|
5942
|
-
/* @__PURE__ */
|
|
5943
|
-
authError ? /* @__PURE__ */
|
|
6687
|
+
return /* @__PURE__ */ jsx10(DataView, { loading, error, children: /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
|
|
6688
|
+
/* @__PURE__ */ jsx10(Text10, { wrap: "truncate", color: theme.dim, children: focused ? `\u2191\u2193 move \xB7 enter act \xB7 m toggle \xB7 t test \xB7 e edit \xB7 x remove \xB7 d delete \xB7 n add${moreAbove ? ` \xB7 \u25B2${moreAbove}` : ""}${moreBelow ? ` \xB7 \u25BC${moreBelow}` : ""}` : "press \u2192 to configure" }),
|
|
6689
|
+
authError ? /* @__PURE__ */ jsx10(Text10, { wrap: "truncate", color: theme.bad, children: truncate(
|
|
5944
6690
|
noKey ? "\u2717 this device is not paired yet \u2014 press + add account below to sign in and pair it" : "\u2717 this device's key is invalid or was revoked \u2014 press x on the account below to remove it, then + add account to pair again",
|
|
5945
6691
|
cols
|
|
5946
6692
|
) }) : null,
|
|
5947
|
-
editing ? /* @__PURE__ */
|
|
5948
|
-
/* @__PURE__ */
|
|
5949
|
-
/* @__PURE__ */
|
|
5950
|
-
] }) : msg ? /* @__PURE__ */
|
|
5951
|
-
/* @__PURE__ */
|
|
6693
|
+
editing ? /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
6694
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.warn, children: editing === "path" ? "local log path: " : "webhook url: " }),
|
|
6695
|
+
/* @__PURE__ */ jsx10(TextInput7, { value: input, onChange: setInput, onSubmit: submitInput })
|
|
6696
|
+
] }) : msg ? /* @__PURE__ */ jsx10(Text10, { wrap: "truncate", color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate(msg.text, cols) }) : login?.msg ? /* @__PURE__ */ jsx10(Text10, { wrap: "truncate", color: login.phase === "error" ? theme.bad : theme.ok, children: login.msg }) : /* @__PURE__ */ jsx10(Text10, { children: " " }),
|
|
6697
|
+
/* @__PURE__ */ jsx10(Box10, { flexDirection: "column", height: budget, overflow: "hidden", children: win })
|
|
5952
6698
|
] }) });
|
|
5953
6699
|
}
|
|
5954
6700
|
|
|
5955
6701
|
// src/tui/App.tsx
|
|
5956
|
-
import { jsx as
|
|
6702
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
5957
6703
|
function LiveHint() {
|
|
5958
|
-
return /* @__PURE__ */
|
|
5959
|
-
/* @__PURE__ */
|
|
5960
|
-
/* @__PURE__ */
|
|
5961
|
-
/* @__PURE__ */
|
|
5962
|
-
/* @__PURE__ */
|
|
6704
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
6705
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE \u2014 THIS MACHINE" }),
|
|
6706
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
|
|
6707
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
|
|
6708
|
+
/* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsxs11(Text11, { children: [
|
|
6709
|
+
"press ",
|
|
6710
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.accent, children: "enter" }),
|
|
6711
|
+
" to go live"
|
|
6712
|
+
] }) })
|
|
6713
|
+
] });
|
|
6714
|
+
}
|
|
6715
|
+
function FleetLiveHint() {
|
|
6716
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
6717
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE \u2014 THE WHOLE FLEET" }),
|
|
6718
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.dim, children: "Every machine under your policy, as it happens: whose call it was," }),
|
|
6719
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.dim, children: "what was refused, and who is working right now." }),
|
|
6720
|
+
/* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsxs11(Text11, { children: [
|
|
5963
6721
|
"press ",
|
|
5964
|
-
/* @__PURE__ */
|
|
6722
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.accent, children: "enter" }),
|
|
5965
6723
|
" to go live"
|
|
5966
6724
|
] }) })
|
|
5967
6725
|
] });
|
|
5968
6726
|
}
|
|
5969
6727
|
var SECTIONS = [
|
|
5970
|
-
{ label: "Live", Panel: LiveHint },
|
|
6728
|
+
{ label: "Solo Live", Panel: LiveHint },
|
|
6729
|
+
{ label: "Fleet Live", Panel: FleetLiveHint },
|
|
6730
|
+
{ label: "Fleet", Panel: FleetPanel },
|
|
5971
6731
|
{ label: "Policies", Panel: PoliciesPanel },
|
|
5972
6732
|
{ label: "Rate Limit", Panel: RateLimitPanel },
|
|
5973
6733
|
{ label: "DLP", Panel: DlpPanel },
|
|
@@ -5975,33 +6735,34 @@ var SECTIONS = [
|
|
|
5975
6735
|
{ label: "Audit", Panel: AuditPanel },
|
|
5976
6736
|
{ label: "Settings", Panel: SettingsPanel }
|
|
5977
6737
|
];
|
|
6738
|
+
var TAKEOVER = /* @__PURE__ */ new Set(["Solo Live", "Fleet Live"]);
|
|
5978
6739
|
var BANNER_HEX = ["white", "white", "white", "white", "white", "white"];
|
|
5979
6740
|
function Banner({ cols }) {
|
|
5980
6741
|
const wide = cols >= 82;
|
|
5981
6742
|
if (!wide) {
|
|
5982
|
-
return /* @__PURE__ */
|
|
5983
|
-
/* @__PURE__ */
|
|
5984
|
-
/* @__PURE__ */
|
|
6743
|
+
return /* @__PURE__ */ jsxs11(Box11, { children: [
|
|
6744
|
+
/* @__PURE__ */ jsx11(Text11, { bold: true, color: theme.accentBright, children: "SolonGate" }),
|
|
6745
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.dim, children: " \u2014 security control center" })
|
|
5985
6746
|
] });
|
|
5986
6747
|
}
|
|
5987
|
-
return /* @__PURE__ */
|
|
5988
|
-
BANNER_FULL.map((line, i) => /* @__PURE__ */
|
|
5989
|
-
/* @__PURE__ */
|
|
6748
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
6749
|
+
BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx11(Text11, { bold: true, color: BANNER_HEX[i], children: line }, i)),
|
|
6750
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
|
|
5990
6751
|
] });
|
|
5991
6752
|
}
|
|
5992
6753
|
var SETTINGS_SECTION = SECTIONS.findIndex((s) => s.label === "Settings");
|
|
5993
6754
|
function App() {
|
|
5994
6755
|
const { exit } = useApp();
|
|
5995
|
-
const [accounts, setAccounts] =
|
|
5996
|
-
const [viewKey, setViewKey] =
|
|
5997
|
-
const [viewNonce, setViewNonce] =
|
|
5998
|
-
const [section, setSection] =
|
|
5999
|
-
const [focus, setFocus] =
|
|
6000
|
-
|
|
6756
|
+
const [accounts, setAccounts] = useState11(() => listAccounts());
|
|
6757
|
+
const [viewKey, setViewKey] = useState11(() => accounts[0]?.apiKey);
|
|
6758
|
+
const [viewNonce, setViewNonce] = useState11(0);
|
|
6759
|
+
const [section, setSection] = useState11(accounts.length === 0 ? SETTINGS_SECTION : 0);
|
|
6760
|
+
const [focus, setFocus] = useState11("nav");
|
|
6761
|
+
useEffect9(
|
|
6001
6762
|
() => onSectionRequest(() => {
|
|
6002
|
-
const
|
|
6003
|
-
if (!
|
|
6004
|
-
const idx = SECTIONS.findIndex((s) => s.label ===
|
|
6763
|
+
const label2 = takePendingSection();
|
|
6764
|
+
if (!label2) return;
|
|
6765
|
+
const idx = SECTIONS.findIndex((s) => s.label === label2);
|
|
6005
6766
|
if (idx >= 0) {
|
|
6006
6767
|
setSection(idx);
|
|
6007
6768
|
setFocus("panel");
|
|
@@ -6009,9 +6770,9 @@ function App() {
|
|
|
6009
6770
|
}),
|
|
6010
6771
|
[]
|
|
6011
6772
|
);
|
|
6012
|
-
const [help, setHelp] =
|
|
6013
|
-
const [update2, setUpdate] =
|
|
6014
|
-
|
|
6773
|
+
const [help, setHelp] = useState11(false);
|
|
6774
|
+
const [update2, setUpdate] = useState11({ kind: "idle" });
|
|
6775
|
+
useEffect9(() => {
|
|
6015
6776
|
let alive = true;
|
|
6016
6777
|
const run = () => void tuiUpdateFlow((s) => alive && setUpdate(s));
|
|
6017
6778
|
run();
|
|
@@ -6025,7 +6786,7 @@ function App() {
|
|
|
6025
6786
|
const cur = accounts[acctIdx];
|
|
6026
6787
|
const rawEmail = cur?.email ?? "";
|
|
6027
6788
|
const acctLabel2 = !cur ? "not logged in" : rawEmail || cur.user || cur.project || `account \u2026${cur.apiKey.slice(-4)}`;
|
|
6028
|
-
|
|
6789
|
+
useEffect9(() => {
|
|
6029
6790
|
if (!cur || cur.email) return;
|
|
6030
6791
|
let alive = true;
|
|
6031
6792
|
api.auth.me().then((r) => {
|
|
@@ -6059,7 +6820,7 @@ function App() {
|
|
|
6059
6820
|
setViewKey(next?.apiKey);
|
|
6060
6821
|
setViewNonce((n) => n + 1);
|
|
6061
6822
|
};
|
|
6062
|
-
|
|
6823
|
+
useEffect9(() => {
|
|
6063
6824
|
try {
|
|
6064
6825
|
ensureLocalLogOwner(enforcingKey());
|
|
6065
6826
|
} catch {
|
|
@@ -6067,7 +6828,7 @@ function App() {
|
|
|
6067
6828
|
}, [accounts.length]);
|
|
6068
6829
|
const locked = accounts.length === 0;
|
|
6069
6830
|
const effectiveSection = locked ? SETTINGS_SECTION : section;
|
|
6070
|
-
|
|
6831
|
+
useInput10((input, key) => {
|
|
6071
6832
|
if (help) {
|
|
6072
6833
|
setHelp(false);
|
|
6073
6834
|
return;
|
|
@@ -6095,39 +6856,41 @@ function App() {
|
|
|
6095
6856
|
});
|
|
6096
6857
|
const { cols, rows } = useTermSize();
|
|
6097
6858
|
const current = SECTIONS[effectiveSection];
|
|
6098
|
-
if (help) return /* @__PURE__ */
|
|
6099
|
-
if (current.label
|
|
6100
|
-
return /* @__PURE__ */
|
|
6859
|
+
if (help) return /* @__PURE__ */ jsx11(HelpOverlay, { cols, rows });
|
|
6860
|
+
if (TAKEOVER.has(current.label) && focus === "panel") {
|
|
6861
|
+
return /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", width: cols, height: rows - 1, overflow: "hidden", children: current.label === "Fleet Live" ? /* @__PURE__ */ jsx11(FleetLivePanel, { active: true, focused: true }, viewNonce) : /* @__PURE__ */ jsx11(LivePanel, { active: true, focused: true }, viewNonce) });
|
|
6101
6862
|
}
|
|
6102
6863
|
const Panel = current.Panel;
|
|
6103
|
-
const panelBody = current.label === "Settings" ? /* @__PURE__ */
|
|
6864
|
+
const panelBody = current.label === "Settings" ? /* @__PURE__ */ jsx11(SettingsPanel, { active: true, focused: focus === "panel", viewApiKey: viewKey, onView: viewAccount, onAccountsChanged: syncAccounts }) : /* @__PURE__ */ jsx11(Panel, { active: focus === "panel" || !TAKEOVER.has(current.label), focused: focus === "panel" });
|
|
6104
6865
|
return (
|
|
6105
6866
|
// height rows-1 (not rows): once total output reaches stdout.rows ink stops
|
|
6106
6867
|
// diffing and clearTerminal-repaints every frame, which slides the banner.
|
|
6107
6868
|
// overflow hidden makes it a hard guarantee even if a panel over-renders.
|
|
6108
|
-
/* @__PURE__ */
|
|
6109
|
-
/* @__PURE__ */
|
|
6110
|
-
/* @__PURE__ */
|
|
6111
|
-
/* @__PURE__ */
|
|
6112
|
-
/* @__PURE__ */
|
|
6113
|
-
locked ? /* @__PURE__ */
|
|
6114
|
-
update2.kind === "updating" ? /* @__PURE__ */
|
|
6869
|
+
/* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", width: cols, height: rows - 1, paddingX: 1, paddingTop: 1, overflow: "hidden", children: [
|
|
6870
|
+
/* @__PURE__ */ jsx11(Banner, { cols }),
|
|
6871
|
+
/* @__PURE__ */ jsxs11(Text11, { wrap: "truncate", children: [
|
|
6872
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.dim, children: "account: " }),
|
|
6873
|
+
/* @__PURE__ */ jsx11(Text11, { color: locked ? theme.warn : theme.accentBright, bold: true, children: acctLabel2 }),
|
|
6874
|
+
locked ? /* @__PURE__ */ jsx11(Text11, { color: theme.dim, children: " \xB7 log in from Settings to unlock the dataroom" }) : accounts.length > 1 ? /* @__PURE__ */ jsx11(Text11, { color: theme.dim, children: ` (${acctIdx + 1}/${accounts.length} \xB7 a switch \xB7 Settings to manage)` }) : /* @__PURE__ */ jsx11(Text11, { color: theme.dim, children: " \xB7 Settings to add another" }),
|
|
6875
|
+
update2.kind === "updating" ? /* @__PURE__ */ jsx11(Text11, { color: theme.warn, children: ` \u2191 updating to v${update2.version}\u2026` }) : update2.kind === "updated" ? /* @__PURE__ */ jsx11(Text11, { color: theme.ok, bold: true, children: ` \u2191 v${update2.version} installed \xB7 restart (q, then solongate) to apply` }) : update2.kind === "available" ? /* @__PURE__ */ jsx11(Text11, { color: theme.warn, children: ` \u2191 v${update2.version} out \xB7 Settings \u2192 UPDATES` }) : update2.kind === "needs-admin" ? /* @__PURE__ */ jsx11(Text11, { color: theme.warn, children: ` \u2191 v${update2.version} needs admin rights \xB7 Settings \u2192 UPDATES` }) : null
|
|
6115
6876
|
] }),
|
|
6116
|
-
/* @__PURE__ */
|
|
6117
|
-
/* @__PURE__ */
|
|
6877
|
+
/* @__PURE__ */ jsxs11(Box11, { marginTop: 1, flexGrow: 1, children: [
|
|
6878
|
+
/* @__PURE__ */ jsx11(Box11, { flexDirection: "column", flexShrink: 0, width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => {
|
|
6118
6879
|
const isCur = i === effectiveSection;
|
|
6119
6880
|
const disabled = locked && s.label !== "Settings";
|
|
6120
|
-
return /* @__PURE__ */
|
|
6881
|
+
return /* @__PURE__ */ jsx11(Text11, { color: isCur ? theme.accentBright : disabled ? theme.dim : void 0, bold: isCur, dimColor: disabled, children: (isCur ? "\u25B8 " : disabled ? "\u2298 " : " ") + s.label }, s.label);
|
|
6121
6882
|
}) }),
|
|
6122
|
-
/* @__PURE__ */
|
|
6883
|
+
/* @__PURE__ */ jsx11(Box11, { flexGrow: 1, minWidth: 0, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, overflow: "hidden", children: panelBody })
|
|
6123
6884
|
] }, viewNonce),
|
|
6124
|
-
/* @__PURE__ */
|
|
6885
|
+
/* @__PURE__ */ jsx11(Box11, { children: locked ? /* @__PURE__ */ jsx11(KeyHints, { hints: [["\u2192/enter", "open Settings"], ["n", "log in"], ["q", "quit"]] }) : focus === "nav" ? /* @__PURE__ */ jsx11(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ...accounts.length > 1 ? [["a", "account"]] : [], ["?", "help"], ["q", "quit"]] }) : /* @__PURE__ */ jsx11(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
|
|
6125
6886
|
] })
|
|
6126
6887
|
);
|
|
6127
6888
|
}
|
|
6128
6889
|
var HELP = [
|
|
6129
6890
|
["Global", [["\u2191\u2193", "move between sections"], ["\u2192 / enter", "open a section"], ["\u2190 / esc", "back to the menu"], ["?", "this help"], ["q", "quit"]]],
|
|
6130
|
-
["Live", [["\u2191\u2193", "select a stream row"], ["enter", "full entry content"], ["w", "whitelist the selected DENY"], ["b", "block the selected ALLOW"], ["d / x / r", "filter denies / dlp / rate-limit"], ["f", "local / cloud filter"], ["/", "search"], ["s", "sessions (\u2191\u2193 pick, enter open)"], ["space", "copy mode (freeze)"]]],
|
|
6891
|
+
["Solo Live", [["\u2191\u2193", "select a stream row"], ["enter", "full entry content"], ["w", "whitelist the selected DENY"], ["b", "block the selected ALLOW"], ["d / x / r", "filter denies / dlp / rate-limit"], ["f", "local / cloud filter"], ["/", "search"], ["s", "sessions (\u2191\u2193 pick, enter open)"], ["space", "copy mode (freeze)"]]],
|
|
6892
|
+
["Fleet Live", [["space", "pause the ticker"], ["d", "refusals only"], ["r", "refresh"], ["", "counters are since you opened it, so a still number means nothing is happening"]]],
|
|
6893
|
+
["Fleet", [["\u2191\u2193", "move through the groups and the people in them"], ["\u2192 / enter", "open one person"], ["i", "invite somebody"], ["n", "new group"], ["r", "rename the group"], ["c", "cycle its colour"], ["g", "file this person under a group"], ["p", "policy \u2014 on a group it applies to all of them, on a person it is an exception"], ["d", "delete the group / revoke the person"], ["R", "reports: yesterday, the schedule, and sending one now"]]],
|
|
6131
6894
|
["Policies", [["\u2191\u2193", "browse / select"], ["a", "activate (pin) selected policy"], ["x", "deactivate \u2014 no active policy"], ["enter", "open rules \u2192 open a rule"], ["space", "toggle a rule on/off"], ["e", "flip effect"], ["n", "new rule"], ["d", "delete rule"], ["m", "flip mode"], ["D", "dry-run the draft"], ["s", "save"], ["x", "discard"]]],
|
|
6132
6895
|
["Rate limit", [["\u2191\u2193", "field"], ["\u2190\u2192", "adjust (shift = \xB110)"], ["s", "save"]]],
|
|
6133
6896
|
["DLP", [["\u2191\u2193", "move"], ["space", "toggle a built-in pattern on/off"], ["m", "cycle mode"], ["a", "add custom pattern (name \u2192 glob, * = any chars)"], ["r", "add a ghost route (glob path to hide)"], ["d", "remove custom / route"], ["g", "ghost on/off"], ["s", "save \xB7 x discard"]]],
|
|
@@ -6136,21 +6899,21 @@ var HELP = [
|
|
|
6136
6899
|
["Accounts", [["a", "switch account (view another account logged in on this device)"], ["", "the header shows which account you are viewing; guard/logging keep the active key"]]]
|
|
6137
6900
|
];
|
|
6138
6901
|
function HelpOverlay({ cols, rows }) {
|
|
6139
|
-
return /* @__PURE__ */
|
|
6140
|
-
/* @__PURE__ */
|
|
6141
|
-
/* @__PURE__ */
|
|
6142
|
-
/* @__PURE__ */
|
|
6143
|
-
keys.map(([k, desc]) => /* @__PURE__ */
|
|
6144
|
-
/* @__PURE__ */
|
|
6145
|
-
/* @__PURE__ */
|
|
6902
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
|
|
6903
|
+
/* @__PURE__ */ jsx11(Text11, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
|
|
6904
|
+
/* @__PURE__ */ jsx11(Box11, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginBottom: 1, children: [
|
|
6905
|
+
/* @__PURE__ */ jsx11(Text11, { bold: true, color: theme.accent, children: group }),
|
|
6906
|
+
keys.map(([k, desc]) => /* @__PURE__ */ jsxs11(Text11, { children: [
|
|
6907
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
|
|
6908
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.dim, children: desc })
|
|
6146
6909
|
] }, k))
|
|
6147
6910
|
] }, group)) }),
|
|
6148
|
-
/* @__PURE__ */
|
|
6911
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.dim, children: "press any key to close" })
|
|
6149
6912
|
] });
|
|
6150
6913
|
}
|
|
6151
6914
|
|
|
6152
6915
|
// src/tui/index.tsx
|
|
6153
|
-
import { jsx as
|
|
6916
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
6154
6917
|
async function launchTui() {
|
|
6155
6918
|
if (!process.stdout.isTTY || !process.stdin.isTTY) {
|
|
6156
6919
|
process.stderr.write(
|
|
@@ -6175,7 +6938,7 @@ async function launchTui() {
|
|
|
6175
6938
|
console.info = toFile("info");
|
|
6176
6939
|
console.debug = toFile("debug");
|
|
6177
6940
|
try {
|
|
6178
|
-
const { waitUntilExit } = render(/* @__PURE__ */
|
|
6941
|
+
const { waitUntilExit } = render(/* @__PURE__ */ jsx12(App, {}), { patchConsole: false });
|
|
6179
6942
|
await waitUntilExit();
|
|
6180
6943
|
} finally {
|
|
6181
6944
|
Object.assign(console, saved);
|