@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/index.js
CHANGED
|
@@ -5671,15 +5671,15 @@ var require_dist = __commonJS({
|
|
|
5671
5671
|
}
|
|
5672
5672
|
};
|
|
5673
5673
|
function parseAllDocuments(src, options) {
|
|
5674
|
-
const
|
|
5674
|
+
const stream2 = [];
|
|
5675
5675
|
let prev;
|
|
5676
5676
|
for (const cstDoc of parseCst.parse(src)) {
|
|
5677
5677
|
const doc = new Document(options);
|
|
5678
5678
|
doc.parse(cstDoc, prev);
|
|
5679
|
-
|
|
5679
|
+
stream2.push(doc);
|
|
5680
5680
|
prev = doc;
|
|
5681
5681
|
}
|
|
5682
|
-
return
|
|
5682
|
+
return stream2;
|
|
5683
5683
|
}
|
|
5684
5684
|
function parseDocument(src, options) {
|
|
5685
5685
|
const cst = parseCst.parse(src);
|
|
@@ -6717,8 +6717,8 @@ function readCodexHooksFile(path) {
|
|
|
6717
6717
|
continue;
|
|
6718
6718
|
}
|
|
6719
6719
|
if (k === "hooks" && v && typeof v === "object") {
|
|
6720
|
-
for (const [ev,
|
|
6721
|
-
if (Array.isArray(
|
|
6720
|
+
for (const [ev, groups2] of Object.entries(v)) {
|
|
6721
|
+
if (Array.isArray(groups2)) out2.hooks[ev] = groups2;
|
|
6722
6722
|
}
|
|
6723
6723
|
continue;
|
|
6724
6724
|
}
|
|
@@ -6732,7 +6732,7 @@ function readCodexHooksFile(path) {
|
|
|
6732
6732
|
}
|
|
6733
6733
|
function writeCodexHooksFile(path, file) {
|
|
6734
6734
|
const hooks = {};
|
|
6735
|
-
for (const [ev,
|
|
6735
|
+
for (const [ev, groups2] of Object.entries(file.hooks)) if (groups2.length) hooks[ev] = groups2;
|
|
6736
6736
|
const body = { ...file.rest };
|
|
6737
6737
|
if (file.description) body["description"] = file.description;
|
|
6738
6738
|
body["hooks"] = hooks;
|
|
@@ -6779,9 +6779,9 @@ function removeCodexGuard(p) {
|
|
|
6779
6779
|
try {
|
|
6780
6780
|
const file = readCodexHooksFile(p.codexHooksPath);
|
|
6781
6781
|
let changed = false;
|
|
6782
|
-
for (const [ev,
|
|
6783
|
-
const kept =
|
|
6784
|
-
if (kept.length !==
|
|
6782
|
+
for (const [ev, groups2] of Object.entries(file.hooks)) {
|
|
6783
|
+
const kept = groups2.filter((g) => !isOurCodexGroup(g));
|
|
6784
|
+
if (kept.length !== groups2.length) {
|
|
6785
6785
|
file.hooks[ev] = kept;
|
|
6786
6786
|
changed = true;
|
|
6787
6787
|
}
|
|
@@ -6949,7 +6949,7 @@ function repairQuiet() {
|
|
|
6949
6949
|
const p = globalPaths();
|
|
6950
6950
|
const has = (f) => existsSync4(f);
|
|
6951
6951
|
const guardFile = join5(p.hooksDir, "guard.mjs");
|
|
6952
|
-
const line = (
|
|
6952
|
+
const line = (label2, ok, yes, no) => ({ label: label2, ok, detail: ok ? yes : no });
|
|
6953
6953
|
const runtime = () => {
|
|
6954
6954
|
const r2 = hookCanStart();
|
|
6955
6955
|
return { label: "hook runtime", ok: r2.ok, detail: r2.ok ? `node ${r2.detail}` : r2.detail };
|
|
@@ -7126,13 +7126,13 @@ function guardHookOutdated() {
|
|
|
7126
7126
|
return false;
|
|
7127
7127
|
}
|
|
7128
7128
|
}
|
|
7129
|
-
function hookCommandFor(hooksDir2, script, client = "claude-code",
|
|
7129
|
+
function hookCommandFor(hooksDir2, script, client = "claude-code", label2 = "Claude Code") {
|
|
7130
7130
|
const target = join5(hooksDir2, script).replace(/\\/g, "/");
|
|
7131
7131
|
if (process.platform === "win32") {
|
|
7132
|
-
return `& "${process.execPath.replace(/\\/g, "/")}" "${target}" ${client} "${
|
|
7132
|
+
return `& "${process.execPath.replace(/\\/g, "/")}" "${target}" ${client} "${label2}"`;
|
|
7133
7133
|
}
|
|
7134
7134
|
const launcher = join5(hooksDir2, LAUNCHER_NAME).replace(/\\/g, "/");
|
|
7135
|
-
return `/bin/sh "${launcher}" "${target}" ${client} "${
|
|
7135
|
+
return `/bin/sh "${launcher}" "${target}" ${client} "${label2}"`;
|
|
7136
7136
|
}
|
|
7137
7137
|
function writeLauncher(hooksDir2) {
|
|
7138
7138
|
writeFileSync3(join5(hooksDir2, LAUNCHER_NAME), launcherScript(process.execPath));
|
|
@@ -7970,14 +7970,14 @@ var init_theme = __esm({
|
|
|
7970
7970
|
// src/tui/components.tsx
|
|
7971
7971
|
import { Box, Text } from "ink";
|
|
7972
7972
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7973
|
-
function PaneTitle({ label, extra, width: width2 }) {
|
|
7973
|
+
function PaneTitle({ label: label2, extra, width: width2 }) {
|
|
7974
7974
|
const tail = extra ? ` ${extra} ` : " ";
|
|
7975
|
-
const used = 2 + 1 +
|
|
7975
|
+
const used = 2 + 1 + label2.length + tail.length;
|
|
7976
7976
|
return /* @__PURE__ */ jsxs(Text, { wrap: "truncate", children: [
|
|
7977
7977
|
/* @__PURE__ */ jsx(Text, { color: theme.dim, children: "\u2500 " }),
|
|
7978
7978
|
/* @__PURE__ */ jsxs(Text, { color: theme.accentBright, bold: true, children: [
|
|
7979
7979
|
"\u258E",
|
|
7980
|
-
|
|
7980
|
+
label2
|
|
7981
7981
|
] }),
|
|
7982
7982
|
/* @__PURE__ */ jsx(Text, { color: theme.dim, children: tail + "\u2500".repeat(Math.max(0, width2 - used)) })
|
|
7983
7983
|
] });
|
|
@@ -8031,10 +8031,10 @@ function Table({ columns, rows }) {
|
|
|
8031
8031
|
] });
|
|
8032
8032
|
}
|
|
8033
8033
|
function KeyHints({ hints }) {
|
|
8034
|
-
return /* @__PURE__ */ jsx(Box, { children: hints.map(([key,
|
|
8034
|
+
return /* @__PURE__ */ jsx(Box, { children: hints.map(([key, label2], i) => /* @__PURE__ */ jsxs(Text, { color: theme.dim, children: [
|
|
8035
8035
|
/* @__PURE__ */ jsx(Text, { color: theme.accent, children: key }),
|
|
8036
8036
|
" ",
|
|
8037
|
-
|
|
8037
|
+
label2,
|
|
8038
8038
|
i < hints.length - 1 ? " " : ""
|
|
8039
8039
|
] }, i)) });
|
|
8040
8040
|
}
|
|
@@ -8361,6 +8361,10 @@ function buildUrl(base, path, query) {
|
|
|
8361
8361
|
if (query) {
|
|
8362
8362
|
for (const [k, v] of Object.entries(query)) {
|
|
8363
8363
|
if (v === void 0) continue;
|
|
8364
|
+
if (Array.isArray(v)) {
|
|
8365
|
+
for (const one of v) url.searchParams.append(k, one);
|
|
8366
|
+
continue;
|
|
8367
|
+
}
|
|
8364
8368
|
url.searchParams.set(k, String(v));
|
|
8365
8369
|
}
|
|
8366
8370
|
}
|
|
@@ -8420,6 +8424,7 @@ async function request(method, path, opts = {}) {
|
|
|
8420
8424
|
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.");
|
|
8421
8425
|
throw new ApiError(res.status, "ERROR", text || res.statusText || `HTTP ${res.status}`);
|
|
8422
8426
|
}
|
|
8427
|
+
if (opts.raw) return text;
|
|
8423
8428
|
return json;
|
|
8424
8429
|
}
|
|
8425
8430
|
var DEFAULT_API_URL2, accountsFile, viewOverride, ApiError, NotAuthenticatedError, cached2;
|
|
@@ -8787,6 +8792,104 @@ var init_mcp = __esm({
|
|
|
8787
8792
|
}
|
|
8788
8793
|
});
|
|
8789
8794
|
|
|
8795
|
+
// src/api-client/fleet.ts
|
|
8796
|
+
var fleet_exports = {};
|
|
8797
|
+
__export(fleet_exports, {
|
|
8798
|
+
activity: () => activity,
|
|
8799
|
+
analytics: () => analytics,
|
|
8800
|
+
assignGroup: () => assignGroup,
|
|
8801
|
+
createGroup: () => createGroup,
|
|
8802
|
+
deleteGroup: () => deleteGroup,
|
|
8803
|
+
groups: () => groups,
|
|
8804
|
+
invite: () => invite,
|
|
8805
|
+
overview: () => overview,
|
|
8806
|
+
renameGroup: () => renameGroup,
|
|
8807
|
+
report: () => report,
|
|
8808
|
+
reportDownload: () => reportDownload,
|
|
8809
|
+
reportSchedule: () => reportSchedule,
|
|
8810
|
+
revoke: () => revoke2,
|
|
8811
|
+
sendReport: () => sendReport,
|
|
8812
|
+
setGroupColor: () => setGroupColor,
|
|
8813
|
+
setGroupPolicy: () => setGroupPolicy,
|
|
8814
|
+
setReportSchedule: () => setReportSchedule,
|
|
8815
|
+
setUserPolicy: () => setUserPolicy,
|
|
8816
|
+
stream: () => stream,
|
|
8817
|
+
summary: () => summary
|
|
8818
|
+
});
|
|
8819
|
+
function overview() {
|
|
8820
|
+
return request("GET", "/fleet");
|
|
8821
|
+
}
|
|
8822
|
+
function summary(opts = {}) {
|
|
8823
|
+
const query = {};
|
|
8824
|
+
if (opts.range) query.range = opts.range;
|
|
8825
|
+
if (opts.group?.length) query.group = opts.group;
|
|
8826
|
+
if (opts.who?.length) query.who = opts.who;
|
|
8827
|
+
if (opts.agent) query.agent = opts.agent;
|
|
8828
|
+
return request("GET", "/fleet/summary", { query });
|
|
8829
|
+
}
|
|
8830
|
+
function groups() {
|
|
8831
|
+
return request("GET", "/fleet/groups");
|
|
8832
|
+
}
|
|
8833
|
+
function createGroup(name, color) {
|
|
8834
|
+
return request("POST", "/fleet/groups", { body: { name, color } });
|
|
8835
|
+
}
|
|
8836
|
+
function renameGroup(name, newName) {
|
|
8837
|
+
return request("POST", "/fleet/groups/rename", { body: { name, new_name: newName } });
|
|
8838
|
+
}
|
|
8839
|
+
function setGroupColor(name, color) {
|
|
8840
|
+
return request("POST", "/fleet/groups/color", { body: { name, color } });
|
|
8841
|
+
}
|
|
8842
|
+
function deleteGroup(name) {
|
|
8843
|
+
return request("POST", "/fleet/groups/delete", { body: { name } });
|
|
8844
|
+
}
|
|
8845
|
+
function setGroupPolicy(name, policyId) {
|
|
8846
|
+
return request("POST", "/fleet/groups/policy", { body: { name, policy_id: policyId } });
|
|
8847
|
+
}
|
|
8848
|
+
function setUserPolicy(ids, policyId) {
|
|
8849
|
+
return request("POST", "/fleet/policy", { body: { ids, policy_id: policyId } });
|
|
8850
|
+
}
|
|
8851
|
+
function assignGroup(ids, group) {
|
|
8852
|
+
return request("POST", "/fleet/group", { body: { ids, group } });
|
|
8853
|
+
}
|
|
8854
|
+
function invite(email) {
|
|
8855
|
+
return request("POST", "/fleet/invitations", { body: { email } });
|
|
8856
|
+
}
|
|
8857
|
+
function revoke2(id) {
|
|
8858
|
+
return request("DELETE", `/fleet/invitations/${encodeURIComponent(id)}`);
|
|
8859
|
+
}
|
|
8860
|
+
function analytics(guest) {
|
|
8861
|
+
return request("GET", "/fleet/analytics", { query: { guest } });
|
|
8862
|
+
}
|
|
8863
|
+
function activity(guest, limit) {
|
|
8864
|
+
const query = { guest };
|
|
8865
|
+
if (limit) query.limit = limit;
|
|
8866
|
+
return request("GET", "/fleet/activity", { query });
|
|
8867
|
+
}
|
|
8868
|
+
function report(span = "day") {
|
|
8869
|
+
return request("GET", "/fleet/report", { query: { span } });
|
|
8870
|
+
}
|
|
8871
|
+
function reportDownload(span, format = "md") {
|
|
8872
|
+
return request("GET", "/fleet/report/download", { query: { span, format }, raw: true });
|
|
8873
|
+
}
|
|
8874
|
+
function sendReport(span = "day") {
|
|
8875
|
+
return request("POST", "/fleet/report/send", { query: { span }, body: {} });
|
|
8876
|
+
}
|
|
8877
|
+
function reportSchedule() {
|
|
8878
|
+
return request("GET", "/settings/reports");
|
|
8879
|
+
}
|
|
8880
|
+
function setReportSchedule(schedule) {
|
|
8881
|
+
return request("PUT", "/settings/reports", { body: schedule });
|
|
8882
|
+
}
|
|
8883
|
+
function stream(limit = 60) {
|
|
8884
|
+
return request("GET", "/fleet/stream", { query: { limit } });
|
|
8885
|
+
}
|
|
8886
|
+
var init_fleet = __esm({
|
|
8887
|
+
"src/api-client/fleet.ts"() {
|
|
8888
|
+
"use strict";
|
|
8889
|
+
init_client();
|
|
8890
|
+
}
|
|
8891
|
+
});
|
|
8892
|
+
|
|
8790
8893
|
// src/api-client/index.ts
|
|
8791
8894
|
var api;
|
|
8792
8895
|
var init_api_client = __esm({
|
|
@@ -8803,7 +8906,8 @@ var init_api_client = __esm({
|
|
|
8803
8906
|
init_agents();
|
|
8804
8907
|
init_keys();
|
|
8805
8908
|
init_mcp();
|
|
8806
|
-
|
|
8909
|
+
init_fleet();
|
|
8910
|
+
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 };
|
|
8807
8911
|
}
|
|
8808
8912
|
});
|
|
8809
8913
|
|
|
@@ -8973,10 +9077,10 @@ function ColumnChart({ series, hot, height, width: width2, color, hotColor = the
|
|
|
8973
9077
|
}
|
|
8974
9078
|
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: lines });
|
|
8975
9079
|
}
|
|
8976
|
-
function HBar({ label, value, max, width: width2, color }) {
|
|
9080
|
+
function HBar({ label: label2, value, max, width: width2, color }) {
|
|
8977
9081
|
const filled = max > 0 ? Math.round(value / max * width2) : 0;
|
|
8978
9082
|
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
8979
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children:
|
|
9083
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: label2.padEnd(10) }),
|
|
8980
9084
|
/* @__PURE__ */ jsx2(Text2, { color, children: "\u2588".repeat(Math.min(width2, filled)) }),
|
|
8981
9085
|
/* @__PURE__ */ jsx2(Text2, { color: DIM_FLOOR, children: "\u2591".repeat(Math.max(0, width2 - filled)) }),
|
|
8982
9086
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " " + value })
|
|
@@ -9330,7 +9434,7 @@ function LivePanel({ active: active2 }) {
|
|
|
9330
9434
|
const selClamped = Math.min(Math.max(0, sel), Math.max(0, visibleDesc.length - 1));
|
|
9331
9435
|
const maxScroll = Math.max(0, visibleDesc.length - streamBodyRows);
|
|
9332
9436
|
const clampedScroll = Math.min(Math.max(0, selClamped - Math.floor((streamBodyRows - 1) / 2)), maxScroll);
|
|
9333
|
-
const
|
|
9437
|
+
const windowed2 = visibleDesc.slice(clampedScroll, clampedScroll + streamBodyRows);
|
|
9334
9438
|
const selEntry = visibleDesc[selClamped];
|
|
9335
9439
|
const detailEntries = detail ? (() => {
|
|
9336
9440
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -9593,11 +9697,11 @@ function LivePanel({ active: active2 }) {
|
|
|
9593
9697
|
const L = [];
|
|
9594
9698
|
const push = (el) => L.push(/* @__PURE__ */ jsx2(Box2, { children: el }, L.length));
|
|
9595
9699
|
const blank = () => push(/* @__PURE__ */ jsx2(Text2, { children: " " }));
|
|
9596
|
-
const section = (
|
|
9700
|
+
const section = (label2, m, note) => push(
|
|
9597
9701
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
9598
9702
|
/* @__PURE__ */ jsxs2(Text2, { bold: true, color: theme.accentBright, children: [
|
|
9599
9703
|
"\u258E",
|
|
9600
|
-
|
|
9704
|
+
label2.padEnd(10)
|
|
9601
9705
|
] }),
|
|
9602
9706
|
/* @__PURE__ */ jsx2(Text2, { bold: true, color: modeColor5(m), children: (m ?? "?").padEnd(8) }),
|
|
9603
9707
|
note ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: note }) : null
|
|
@@ -9870,7 +9974,7 @@ function LivePanel({ active: active2 }) {
|
|
|
9870
9974
|
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: "local logs on" }),
|
|
9871
9975
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ` \u2014 no entries yet \xB7 hooks write ${localPath?.file ?? ""}` })
|
|
9872
9976
|
] }) : null,
|
|
9873
|
-
|
|
9977
|
+
windowed2.length === 0 ? (
|
|
9874
9978
|
// With a source filter on, "awaiting traffic" reads as "nothing is
|
|
9875
9979
|
// happening" when the stream is merely filtered — say which it is.
|
|
9876
9980
|
filter === "all" ? /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
@@ -9878,7 +9982,7 @@ function LivePanel({ active: active2 }) {
|
|
|
9878
9982
|
" awaiting traffic\u2026"
|
|
9879
9983
|
] }) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: `${spin} no ${filter === "local" ? "LOC (local log)" : "CLD (cloud)"} calls in the buffer \xB7 f switches source` })
|
|
9880
9984
|
) : null,
|
|
9881
|
-
|
|
9985
|
+
windowed2.map((e, i) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e), selected: clampedScroll + i === selClamped }, e.id))
|
|
9882
9986
|
] }),
|
|
9883
9987
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
9884
9988
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "white", bold: true, children: " LIVE " }),
|
|
@@ -9966,26 +10070,694 @@ var init_Live = __esm({
|
|
|
9966
10070
|
}
|
|
9967
10071
|
});
|
|
9968
10072
|
|
|
9969
|
-
// src/tui/panels/
|
|
10073
|
+
// src/tui/panels/FleetLive.tsx
|
|
9970
10074
|
import { Box as Box3, Text as Text3, useInput as useInput2 } from "ink";
|
|
9971
|
-
import
|
|
9972
|
-
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
10075
|
+
import { useEffect as useEffect3, useMemo, useRef as useRef3, useState as useState3 } from "react";
|
|
9973
10076
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
10077
|
+
function FleetLivePanel({ focused }) {
|
|
10078
|
+
const { cols, rows } = useTermSize();
|
|
10079
|
+
const streamQ = useLoader(() => api.fleet.stream(80));
|
|
10080
|
+
const summaryQ = useLoader(() => api.fleet.summary({ range: "24h" }));
|
|
10081
|
+
const [paused, setPaused] = useState3(false);
|
|
10082
|
+
const [onlyDenies, setOnlyDenies] = useState3(false);
|
|
10083
|
+
usePoll(() => {
|
|
10084
|
+
if (paused) return;
|
|
10085
|
+
streamQ.reloadQuiet();
|
|
10086
|
+
}, TICK_MS);
|
|
10087
|
+
usePoll(() => {
|
|
10088
|
+
if (paused) return;
|
|
10089
|
+
summaryQ.reloadQuiet();
|
|
10090
|
+
}, 15e3);
|
|
10091
|
+
useInput2(
|
|
10092
|
+
(input) => {
|
|
10093
|
+
if (input === " ") setPaused((p) => !p);
|
|
10094
|
+
else if (input === "d") setOnlyDenies((d) => !d);
|
|
10095
|
+
else if (input === "r") {
|
|
10096
|
+
streamQ.reload();
|
|
10097
|
+
summaryQ.reload();
|
|
10098
|
+
}
|
|
10099
|
+
},
|
|
10100
|
+
{ isActive: focused }
|
|
10101
|
+
);
|
|
10102
|
+
const calls = streamQ.data?.calls ?? [];
|
|
10103
|
+
const seenIds = useRef3(/* @__PURE__ */ new Set());
|
|
10104
|
+
const started = useRef3(Date.now());
|
|
10105
|
+
const [since, setSince] = useState3(() => ({
|
|
10106
|
+
calls: 0,
|
|
10107
|
+
denied: 0,
|
|
10108
|
+
byUser: /* @__PURE__ */ new Map()
|
|
10109
|
+
}));
|
|
10110
|
+
useEffect3(() => {
|
|
10111
|
+
if (calls.length === 0) return;
|
|
10112
|
+
let added = 0;
|
|
10113
|
+
let denied = 0;
|
|
10114
|
+
const byUser = new Map(since.byUser);
|
|
10115
|
+
for (const row of [...calls].reverse()) {
|
|
10116
|
+
if (seenIds.current.has(row.id)) continue;
|
|
10117
|
+
seenIds.current.add(row.id);
|
|
10118
|
+
added++;
|
|
10119
|
+
const isDeny = row.decision.toUpperCase() === "DENY";
|
|
10120
|
+
if (isDeny) denied++;
|
|
10121
|
+
const prev = byUser.get(row.who) ?? { calls: 0, denied: 0, last: 0 };
|
|
10122
|
+
byUser.set(row.who, {
|
|
10123
|
+
calls: prev.calls + 1,
|
|
10124
|
+
denied: prev.denied + (isDeny ? 1 : 0),
|
|
10125
|
+
last: Math.max(prev.last, row.created_at)
|
|
10126
|
+
});
|
|
10127
|
+
}
|
|
10128
|
+
if (added === 0) return;
|
|
10129
|
+
if (seenIds.current.size === added) {
|
|
10130
|
+
setSince({ calls: 0, denied: 0, byUser: /* @__PURE__ */ new Map() });
|
|
10131
|
+
return;
|
|
10132
|
+
}
|
|
10133
|
+
setSince((s) => ({ calls: s.calls + added, denied: s.denied + denied, byUser }));
|
|
10134
|
+
}, [calls]);
|
|
10135
|
+
const shown = useMemo(
|
|
10136
|
+
() => onlyDenies ? calls.filter((c2) => c2.decision.toUpperCase() === "DENY") : calls,
|
|
10137
|
+
[calls, onlyDenies]
|
|
10138
|
+
);
|
|
10139
|
+
const people = useMemo(() => {
|
|
10140
|
+
const list5 = [...since.byUser.entries()].map(([who, s]) => ({ who, ...s }));
|
|
10141
|
+
list5.sort((a, b) => b.calls - a.calls || b.last - a.last);
|
|
10142
|
+
return list5.slice(0, 8);
|
|
10143
|
+
}, [since]);
|
|
10144
|
+
const totals = summaryQ.data?.totals;
|
|
10145
|
+
const elapsed = Math.max(1, Math.round((Date.now() - started.current) / 1e3));
|
|
10146
|
+
const rate = since.calls > 0 ? (since.calls / (elapsed / 60)).toFixed(1) : "0.0";
|
|
10147
|
+
const tickerRows = Math.max(4, rows - 12 - Math.min(people.length, 8));
|
|
10148
|
+
const width2 = Math.max(40, cols - 4);
|
|
10149
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", width: cols, height: rows - 1, paddingX: 1, overflow: "hidden", children: [
|
|
10150
|
+
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
10151
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.accentBright, children: "\u25B6 FLEET LIVE" }),
|
|
10152
|
+
/* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
|
|
10153
|
+
" " + (summaryQ.data?.developers ?? 0) + " people",
|
|
10154
|
+
totals ? " " + totals.calls + " calls / 24h \xB7 " + totals.denied + " denied" : ""
|
|
10155
|
+
] }),
|
|
10156
|
+
paused ? /* @__PURE__ */ jsx3(Text3, { color: theme.warn, children: " paused" }) : null,
|
|
10157
|
+
onlyDenies ? /* @__PURE__ */ jsx3(Text3, { color: theme.bad, children: " denies only" }) : null
|
|
10158
|
+
] }),
|
|
10159
|
+
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
10160
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "since you opened this: " + since.calls + " calls, " }),
|
|
10161
|
+
/* @__PURE__ */ jsx3(Text3, { color: since.denied > 0 ? theme.bad : theme.dim, children: since.denied + " denied" }),
|
|
10162
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: " \xB7 " + rate + "/min" })
|
|
10163
|
+
] }),
|
|
10164
|
+
people.length > 0 ? /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
|
|
10165
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "Working now" }),
|
|
10166
|
+
people.map((p) => /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
10167
|
+
/* @__PURE__ */ jsx3(Text3, { children: " " + truncate2(p.who, 28).padEnd(29) }),
|
|
10168
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: String(p.calls).padStart(5) + " calls " }),
|
|
10169
|
+
/* @__PURE__ */ jsx3(Text3, { color: p.denied > 0 ? theme.bad : theme.dim, children: String(p.denied).padStart(4) + " denied " }),
|
|
10170
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: hhmmss2(p.last) })
|
|
10171
|
+
] }, p.who))
|
|
10172
|
+
] }) : null,
|
|
10173
|
+
/* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", flexGrow: 1, overflow: "hidden", children: [
|
|
10174
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: onlyDenies ? "Refusals" : "Calls" }),
|
|
10175
|
+
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: width2 }, row.id))
|
|
10176
|
+
] }),
|
|
10177
|
+
/* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "space pause \xB7 d denies only \xB7 r refresh \xB7 \u2190/esc back" }) })
|
|
10178
|
+
] });
|
|
10179
|
+
}
|
|
10180
|
+
function StreamRow({ row, width: width2 }) {
|
|
10181
|
+
const decision = (row.decision || "").toUpperCase();
|
|
10182
|
+
const who = truncate2(row.who, 22).padEnd(23);
|
|
10183
|
+
const tool = truncate2(row.tool || "\u2014", 26).padEnd(27);
|
|
10184
|
+
const rest = Math.max(10, width2 - 23 - 27 - 9 - 8);
|
|
10185
|
+
const tail = decision === "DENY" ? row.reason || "" : row.agent || "";
|
|
10186
|
+
return /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
10187
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: hhmmss2(row.created_at) + " " }),
|
|
10188
|
+
/* @__PURE__ */ jsx3(Text3, { children: who }),
|
|
10189
|
+
/* @__PURE__ */ jsx3(Text3, { color: decisionColor(decision), children: (decision || "\u2014").padEnd(7) }),
|
|
10190
|
+
/* @__PURE__ */ jsx3(Text3, { children: tool }),
|
|
10191
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: truncate2(tail, rest) })
|
|
10192
|
+
] });
|
|
10193
|
+
}
|
|
10194
|
+
var TICK_MS, hhmmss2;
|
|
10195
|
+
var init_FleetLive = __esm({
|
|
10196
|
+
"src/tui/panels/FleetLive.tsx"() {
|
|
10197
|
+
"use strict";
|
|
10198
|
+
init_api_client();
|
|
10199
|
+
init_hooks();
|
|
10200
|
+
init_theme();
|
|
10201
|
+
TICK_MS = 2e3;
|
|
10202
|
+
hhmmss2 = (unix) => {
|
|
10203
|
+
const d = new Date(unix * 1e3);
|
|
10204
|
+
return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
|
|
10205
|
+
};
|
|
10206
|
+
}
|
|
10207
|
+
});
|
|
10208
|
+
|
|
10209
|
+
// src/tui/panels/Fleet.tsx
|
|
10210
|
+
import { Box as Box4, Text as Text4, useInput as useInput3 } from "ink";
|
|
10211
|
+
import TextInput2 from "ink-text-input";
|
|
10212
|
+
import { useMemo as useMemo2, useState as useState4 } from "react";
|
|
10213
|
+
import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
10214
|
+
function FleetPanel({ focused }) {
|
|
10215
|
+
const { cols, rows } = usePanelSize();
|
|
10216
|
+
const overviewQ = useLoader(() => api.fleet.overview());
|
|
10217
|
+
const groupsQ = useLoader(() => api.fleet.groups());
|
|
10218
|
+
const summaryQ = useLoader(() => api.fleet.summary({ range: "30d" }));
|
|
10219
|
+
const policiesQ = useLoader(() => api.policies.list());
|
|
10220
|
+
const [view, setView] = useState4("roster");
|
|
10221
|
+
const [sel, setSel] = useState4(0);
|
|
10222
|
+
const [status, setStatus] = useState4(null);
|
|
10223
|
+
const [prompt, setPrompt] = useState4(null);
|
|
10224
|
+
const [draft, setDraft] = useState4("");
|
|
10225
|
+
const [pick, setPick] = useState4(0);
|
|
10226
|
+
const [confirm, setConfirm] = useState4(null);
|
|
10227
|
+
usePoll(() => {
|
|
10228
|
+
if (prompt) return;
|
|
10229
|
+
overviewQ.reloadQuiet();
|
|
10230
|
+
groupsQ.reloadQuiet();
|
|
10231
|
+
summaryQ.reloadQuiet();
|
|
10232
|
+
}, 15e3);
|
|
10233
|
+
const policies = policiesQ.data?.policies ?? [];
|
|
10234
|
+
const overview2 = overviewQ.data;
|
|
10235
|
+
const groups2 = groupsQ.data?.groups ?? [];
|
|
10236
|
+
const summary2 = summaryQ.data;
|
|
10237
|
+
const byUser = useMemo2(() => {
|
|
10238
|
+
const map = /* @__PURE__ */ new Map();
|
|
10239
|
+
for (const p of summary2?.people ?? []) map.set(p.role === "host" ? SELF : p.guest_user_id, p);
|
|
10240
|
+
return map;
|
|
10241
|
+
}, [summary2]);
|
|
10242
|
+
const list5 = useMemo2(() => {
|
|
10243
|
+
const accepted = (overview2?.invitations ?? []).filter((m) => m.status === "accepted");
|
|
10244
|
+
const pending2 = (overview2?.invitations ?? []).filter((m) => m.status === "pending");
|
|
10245
|
+
const self = {
|
|
10246
|
+
id: "",
|
|
10247
|
+
guest_user_id: SELF,
|
|
10248
|
+
email: "You (host)",
|
|
10249
|
+
status: "accepted",
|
|
10250
|
+
group: overview2?.host_group ?? "",
|
|
10251
|
+
policy_id: "",
|
|
10252
|
+
top_agent: overview2?.host_top_agent ?? "",
|
|
10253
|
+
variant_id: "",
|
|
10254
|
+
created_at: "",
|
|
10255
|
+
accepted_at: ""
|
|
10256
|
+
};
|
|
10257
|
+
const named = groups2.filter((g) => g.name !== UNGROUPED);
|
|
10258
|
+
const ungrouped = groups2.find((g) => g.name === UNGROUPED) ?? { name: UNGROUPED, members: 0, color: "slate", policy_id: "" };
|
|
10259
|
+
const out2 = [];
|
|
10260
|
+
const push = (group, members) => {
|
|
10261
|
+
out2.push({ kind: "group", group });
|
|
10262
|
+
for (const m of members) {
|
|
10263
|
+
out2.push({ kind: "member", group, member: m, person: byUser.get(m.guest_user_id || SELF) });
|
|
10264
|
+
}
|
|
10265
|
+
};
|
|
10266
|
+
const inGroup = (name) => {
|
|
10267
|
+
const mine = accepted.filter((m) => (m.group || "") === name);
|
|
10268
|
+
if ((self.group || "") === name) return [self, ...mine];
|
|
10269
|
+
return mine;
|
|
10270
|
+
};
|
|
10271
|
+
for (const g of named) push(g, inGroup(g.name));
|
|
10272
|
+
push(ungrouped, inGroup(""));
|
|
10273
|
+
if (pending2.length > 0) {
|
|
10274
|
+
const waiting = { name: "Waiting to answer", members: pending2.length, color: "amber", policy_id: "" };
|
|
10275
|
+
out2.push({ kind: "group", group: waiting });
|
|
10276
|
+
for (const m of pending2) out2.push({ kind: "pending", group: waiting, member: m });
|
|
10277
|
+
}
|
|
10278
|
+
return out2;
|
|
10279
|
+
}, [overview2, groups2, byUser]);
|
|
10280
|
+
const current = list5[Math.min(sel, Math.max(0, list5.length - 1))];
|
|
10281
|
+
const say = (msg) => setStatus(msg);
|
|
10282
|
+
const reloadAll = () => {
|
|
10283
|
+
overviewQ.reload();
|
|
10284
|
+
groupsQ.reload();
|
|
10285
|
+
summaryQ.reload();
|
|
10286
|
+
};
|
|
10287
|
+
const run12 = async (what, fn) => {
|
|
10288
|
+
setStatus(what + "\u2026");
|
|
10289
|
+
try {
|
|
10290
|
+
await fn();
|
|
10291
|
+
setStatus("\u2713 " + what);
|
|
10292
|
+
reloadAll();
|
|
10293
|
+
} catch (e) {
|
|
10294
|
+
setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
|
|
10295
|
+
}
|
|
10296
|
+
};
|
|
10297
|
+
const openPrompt = (p, initial = "") => {
|
|
10298
|
+
setPrompt(p);
|
|
10299
|
+
setDraft(initial);
|
|
10300
|
+
setStatus(null);
|
|
10301
|
+
setPick(0);
|
|
10302
|
+
};
|
|
10303
|
+
const submitPrompt = async (value) => {
|
|
10304
|
+
const v = value.trim();
|
|
10305
|
+
const p = prompt;
|
|
10306
|
+
setPrompt(null);
|
|
10307
|
+
setDraft("");
|
|
10308
|
+
if (!p) return;
|
|
10309
|
+
if (p === "invite") {
|
|
10310
|
+
if (!v) return;
|
|
10311
|
+
await run12("Invited " + v, () => api.fleet.invite(v));
|
|
10312
|
+
return;
|
|
10313
|
+
}
|
|
10314
|
+
if (p === "newGroup") {
|
|
10315
|
+
if (!v) return;
|
|
10316
|
+
await run12("Created " + v, () => api.fleet.createGroup(v));
|
|
10317
|
+
return;
|
|
10318
|
+
}
|
|
10319
|
+
if (p === "rename") {
|
|
10320
|
+
const g = current?.group;
|
|
10321
|
+
if (!v || !g || g.name === UNGROUPED) return;
|
|
10322
|
+
await run12("Renamed to " + v, () => api.fleet.renameGroup(g.name, v));
|
|
10323
|
+
return;
|
|
10324
|
+
}
|
|
10325
|
+
if (p === "assign") {
|
|
10326
|
+
const m = current?.member;
|
|
10327
|
+
if (!m || !m.id) return;
|
|
10328
|
+
await run12(v ? "Filed under " + v : "Unfiled", () => api.fleet.assignGroup([m.id], v));
|
|
10329
|
+
return;
|
|
10330
|
+
}
|
|
10331
|
+
if (p === "emails" || p === "telegram" || p === "endTime") {
|
|
10332
|
+
await saveSchedule(p, v);
|
|
10333
|
+
return;
|
|
10334
|
+
}
|
|
10335
|
+
};
|
|
10336
|
+
const reportQ = useLoader(() => view === "reports" ? api.fleet.report("day") : Promise.resolve(null), [view]);
|
|
10337
|
+
const scheduleQ = useLoader(() => view === "reports" ? api.fleet.reportSchedule() : Promise.resolve(null), [view]);
|
|
10338
|
+
const schedule = scheduleQ.data?.schedule;
|
|
10339
|
+
const saveSchedule = async (field, value) => {
|
|
10340
|
+
if (!schedule) return;
|
|
10341
|
+
const next = {};
|
|
10342
|
+
if (field === "emails") next.emails = splitTargets(value);
|
|
10343
|
+
if (field === "telegram") next.telegram = splitTargets(value);
|
|
10344
|
+
if (field === "endTime") next.endMinute = minutesOf(value);
|
|
10345
|
+
await run12("Saved schedule", async () => {
|
|
10346
|
+
await api.fleet.setReportSchedule(next);
|
|
10347
|
+
scheduleQ.reload();
|
|
10348
|
+
reportQ.reload();
|
|
10349
|
+
});
|
|
10350
|
+
};
|
|
10351
|
+
const toggleSpan = async (span) => {
|
|
10352
|
+
if (!schedule) return;
|
|
10353
|
+
await run12(span + " report " + (schedule[span] ? "off" : "on"), async () => {
|
|
10354
|
+
await api.fleet.setReportSchedule({ [span]: !schedule[span] });
|
|
10355
|
+
scheduleQ.reload();
|
|
10356
|
+
});
|
|
10357
|
+
};
|
|
10358
|
+
useInput3(
|
|
10359
|
+
(input, key) => {
|
|
10360
|
+
if (prompt) {
|
|
10361
|
+
if (key.escape) {
|
|
10362
|
+
setPrompt(null);
|
|
10363
|
+
setDraft("");
|
|
10364
|
+
}
|
|
10365
|
+
return;
|
|
10366
|
+
}
|
|
10367
|
+
if (confirm) {
|
|
10368
|
+
if (input === "y") {
|
|
10369
|
+
const target = confirm;
|
|
10370
|
+
setConfirm(null);
|
|
10371
|
+
void doDelete(target);
|
|
10372
|
+
} else {
|
|
10373
|
+
setConfirm(null);
|
|
10374
|
+
}
|
|
10375
|
+
return;
|
|
10376
|
+
}
|
|
10377
|
+
if (view === "person") {
|
|
10378
|
+
if (key.leftArrow || key.escape) setView("roster");
|
|
10379
|
+
return;
|
|
10380
|
+
}
|
|
10381
|
+
if (view === "reports") {
|
|
10382
|
+
if (key.leftArrow || key.escape) setView("roster");
|
|
10383
|
+
else if (input === "d") void toggleSpan("day");
|
|
10384
|
+
else if (input === "w") void toggleSpan("week");
|
|
10385
|
+
else if (input === "m") void toggleSpan("month");
|
|
10386
|
+
else if (input === "e") openPrompt("emails", (schedule?.emails ?? []).join(", "));
|
|
10387
|
+
else if (input === "t") openPrompt("telegram", (schedule?.telegram ?? []).join(", "));
|
|
10388
|
+
else if (input === "h") openPrompt("endTime", timeOf(schedule?.endMinute ?? 0));
|
|
10389
|
+
else if (input === "s") void run12("Sent", () => api.fleet.sendReport("day"));
|
|
10390
|
+
else if (input === "r") {
|
|
10391
|
+
reportQ.reload();
|
|
10392
|
+
scheduleQ.reload();
|
|
10393
|
+
}
|
|
10394
|
+
return;
|
|
10395
|
+
}
|
|
10396
|
+
if (key.upArrow) setSel((s) => Math.max(0, s - 1));
|
|
10397
|
+
else if (key.downArrow) setSel((s) => Math.min(list5.length - 1, s + 1));
|
|
10398
|
+
else if (key.rightArrow || key.return) {
|
|
10399
|
+
if (current?.kind === "member") setView("person");
|
|
10400
|
+
} else if (input === "R") setView("reports");
|
|
10401
|
+
else if (input === "i") openPrompt("invite");
|
|
10402
|
+
else if (input === "n") openPrompt("newGroup");
|
|
10403
|
+
else if (input === "r") {
|
|
10404
|
+
if (current?.kind === "group" && current.group.name !== UNGROUPED) openPrompt("rename", current.group.name);
|
|
10405
|
+
} else if (input === "g") {
|
|
10406
|
+
if (current?.kind === "member" && current.member?.id) openPrompt("assign", current.member.group || "");
|
|
10407
|
+
} else if (input === "c") void cycleColor();
|
|
10408
|
+
else if (input === "p") openPolicyPicker();
|
|
10409
|
+
else if (input === "d") askDelete();
|
|
10410
|
+
else if (input === "x") reloadAll();
|
|
10411
|
+
},
|
|
10412
|
+
{ isActive: focused }
|
|
10413
|
+
);
|
|
10414
|
+
const cycleColor = async () => {
|
|
10415
|
+
const g = current?.group;
|
|
10416
|
+
if (!g || g.name === UNGROUPED || current?.kind !== "group") return;
|
|
10417
|
+
const next = GROUP_COLORS[(GROUP_COLORS.indexOf(g.color) + 1 + GROUP_COLORS.length) % GROUP_COLORS.length];
|
|
10418
|
+
await run12("Coloured " + next, () => api.fleet.setGroupColor(g.name, next));
|
|
10419
|
+
};
|
|
10420
|
+
const openPolicyPicker = () => {
|
|
10421
|
+
if (!current) return;
|
|
10422
|
+
if (current.kind === "pending") return;
|
|
10423
|
+
if (policies.length === 0) {
|
|
10424
|
+
say("No policies to choose from yet.");
|
|
10425
|
+
return;
|
|
10426
|
+
}
|
|
10427
|
+
openPrompt("policy");
|
|
10428
|
+
};
|
|
10429
|
+
const choosePolicy = async (id) => {
|
|
10430
|
+
const target = current;
|
|
10431
|
+
setPrompt(null);
|
|
10432
|
+
if (!target) return;
|
|
10433
|
+
if (target.kind === "group") {
|
|
10434
|
+
if (target.group.name === UNGROUPED) {
|
|
10435
|
+
say("Uncategorized is the absence of a group, so it carries no policy.");
|
|
10436
|
+
return;
|
|
10437
|
+
}
|
|
10438
|
+
await run12(
|
|
10439
|
+
id ? "Group runs " + policyLabel(id, policies) : "Group names no policy",
|
|
10440
|
+
() => api.fleet.setGroupPolicy(target.group.name, id)
|
|
10441
|
+
);
|
|
10442
|
+
return;
|
|
10443
|
+
}
|
|
10444
|
+
const m = target.member;
|
|
10445
|
+
if (!m?.id) {
|
|
10446
|
+
say("The host's own machine follows their group. Set the group's policy.");
|
|
10447
|
+
return;
|
|
10448
|
+
}
|
|
10449
|
+
await run12(
|
|
10450
|
+
id ? "Pinned to " + policyLabel(id, policies) : "Back to their group",
|
|
10451
|
+
() => api.fleet.setUserPolicy([m.id], id)
|
|
10452
|
+
);
|
|
10453
|
+
};
|
|
10454
|
+
const askDelete = () => {
|
|
10455
|
+
if (!current) return;
|
|
10456
|
+
if (current.kind === "group") {
|
|
10457
|
+
if (current.group.name === UNGROUPED) return;
|
|
10458
|
+
setConfirm("group:" + current.group.name);
|
|
10459
|
+
return;
|
|
10460
|
+
}
|
|
10461
|
+
const m = current.member;
|
|
10462
|
+
if (!m?.id) {
|
|
10463
|
+
say("You cannot remove yourself from your own fleet.");
|
|
10464
|
+
return;
|
|
10465
|
+
}
|
|
10466
|
+
setConfirm("member:" + m.id);
|
|
10467
|
+
};
|
|
10468
|
+
const doDelete = async (target) => {
|
|
10469
|
+
const [kind, ...rest] = target.split(":");
|
|
10470
|
+
const value = rest.join(":");
|
|
10471
|
+
if (kind === "group") await run12("Deleted " + value, () => api.fleet.deleteGroup(value));
|
|
10472
|
+
else await run12("Revoked", () => api.fleet.revoke(value));
|
|
10473
|
+
};
|
|
10474
|
+
const loading = overviewQ.loading || groupsQ.loading;
|
|
10475
|
+
const error = overviewQ.error ?? groupsQ.error;
|
|
10476
|
+
if (overview2 && !overview2.host) {
|
|
10477
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
10478
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "This account does not run a fleet." }),
|
|
10479
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "Ask whoever manages your team for a seat, then come back." })
|
|
10480
|
+
] });
|
|
10481
|
+
}
|
|
10482
|
+
if (view === "person") {
|
|
10483
|
+
return /* @__PURE__ */ jsx4(PersonView, { row: current, policies, cols });
|
|
10484
|
+
}
|
|
10485
|
+
if (view === "reports") {
|
|
10486
|
+
return /* @__PURE__ */ jsx4(
|
|
10487
|
+
ReportsView,
|
|
10488
|
+
{
|
|
10489
|
+
report: reportQ.data,
|
|
10490
|
+
loading: reportQ.loading,
|
|
10491
|
+
error: reportQ.error,
|
|
10492
|
+
schedule,
|
|
10493
|
+
next: scheduleQ.data?.next ?? 0,
|
|
10494
|
+
status,
|
|
10495
|
+
prompt,
|
|
10496
|
+
draft,
|
|
10497
|
+
onChange: setDraft,
|
|
10498
|
+
onSubmit: submitPrompt,
|
|
10499
|
+
cols
|
|
10500
|
+
}
|
|
10501
|
+
);
|
|
10502
|
+
}
|
|
10503
|
+
const visible = windowed(list5, sel, Math.max(4, rows - 6));
|
|
10504
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
10505
|
+
/* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
|
|
10506
|
+
(overview2?.used ?? 0) + " of " + (overview2?.seats ?? 0) + " seats",
|
|
10507
|
+
summary2 ? " " + summary2.totals.calls + " calls \xB7 " + summary2.totals.denied + " denied (30d)" : ""
|
|
10508
|
+
] }) }),
|
|
10509
|
+
/* @__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) => {
|
|
10510
|
+
const index = list5.indexOf(row);
|
|
10511
|
+
const selected = index === sel;
|
|
10512
|
+
if (row.kind === "group") {
|
|
10513
|
+
return /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
10514
|
+
/* @__PURE__ */ jsx4(Text4, { color: selected ? theme.accentBright : theme.accent, bold: true, children: (selected ? "\u25B8 " : " ") + label(row.group).padEnd(22) }),
|
|
10515
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: String(row.group.members).padStart(3) + " people " }),
|
|
10516
|
+
/* @__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" })
|
|
10517
|
+
] }, "g" + index + i);
|
|
10518
|
+
}
|
|
10519
|
+
const m = row.member;
|
|
10520
|
+
const pinned = Boolean(m.policy_id);
|
|
10521
|
+
const runs = pinned ? m.policy_id : row.group.policy_id;
|
|
10522
|
+
return /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
10523
|
+
/* @__PURE__ */ jsx4(Text4, { color: selected ? theme.accentBright : void 0, children: (selected ? " \u25B8 " : " ") + truncate2(m.email, 30).padEnd(31) }),
|
|
10524
|
+
row.kind === "pending" ? /* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: "waiting" }) : /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
10525
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: String(row.person?.calls ?? 0).padStart(6) + " calls " }),
|
|
10526
|
+
/* @__PURE__ */ jsx4(Text4, { color: (row.person?.denied ?? 0) > 0 ? theme.bad : theme.dim, children: String(row.person?.denied ?? 0).padStart(4) + " deny " }),
|
|
10527
|
+
/* @__PURE__ */ jsx4(Text4, { color: pinned ? theme.warn : theme.dim, children: runs ? policyLabel(runs, policies) + (pinned ? " (only them)" : "") : "\u2014" })
|
|
10528
|
+
] })
|
|
10529
|
+
] }, "m" + index + i);
|
|
10530
|
+
}) }) }),
|
|
10531
|
+
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,
|
|
10532
|
+
prompt === "policy" ? /* @__PURE__ */ jsx4(
|
|
10533
|
+
PolicyPicker,
|
|
10534
|
+
{
|
|
10535
|
+
policies,
|
|
10536
|
+
pick,
|
|
10537
|
+
onMove: setPick,
|
|
10538
|
+
onChoose: choosePolicy,
|
|
10539
|
+
onCancel: () => setPrompt(null),
|
|
10540
|
+
focused,
|
|
10541
|
+
target: current?.kind === "group" ? label(current.group) : current?.member?.email ?? ""
|
|
10542
|
+
}
|
|
10543
|
+
) : prompt ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
|
|
10544
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.accent, children: promptLabel(prompt) + " " }),
|
|
10545
|
+
/* @__PURE__ */ jsx4(TextInput2, { value: draft, onChange: setDraft, onSubmit: submitPrompt })
|
|
10546
|
+
] }) : null,
|
|
10547
|
+
status ? /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) }) : null
|
|
10548
|
+
] });
|
|
10549
|
+
}
|
|
10550
|
+
function windowed(list5, sel, height) {
|
|
10551
|
+
if (list5.length <= height) return list5;
|
|
10552
|
+
const start = Math.min(Math.max(0, sel - Math.floor(height / 2)), list5.length - height);
|
|
10553
|
+
return list5.slice(start, start + height);
|
|
10554
|
+
}
|
|
10555
|
+
function PolicyPicker({
|
|
10556
|
+
policies,
|
|
10557
|
+
pick,
|
|
10558
|
+
onMove,
|
|
10559
|
+
onChoose,
|
|
10560
|
+
onCancel,
|
|
10561
|
+
focused,
|
|
10562
|
+
target
|
|
10563
|
+
}) {
|
|
10564
|
+
const options = [
|
|
10565
|
+
{ id: "", name: "No policy of its own", detail: "follow the level above" },
|
|
10566
|
+
...policies.map((p) => ({
|
|
10567
|
+
id: p.id,
|
|
10568
|
+
name: p.name,
|
|
10569
|
+
detail: (p.rules?.length ?? 0) + " rules \xB7 " + p.mode
|
|
10570
|
+
}))
|
|
10571
|
+
];
|
|
10572
|
+
useInput3(
|
|
10573
|
+
(_input, key) => {
|
|
10574
|
+
if (key.upArrow) onMove(Math.max(0, pick - 1));
|
|
10575
|
+
else if (key.downArrow) onMove(Math.min(options.length - 1, pick + 1));
|
|
10576
|
+
else if (key.return) onChoose(options[pick]?.id ?? "");
|
|
10577
|
+
else if (key.escape || key.leftArrow) onCancel();
|
|
10578
|
+
},
|
|
10579
|
+
{ isActive: focused }
|
|
10580
|
+
);
|
|
10581
|
+
const visible = windowed(options, pick, 8);
|
|
10582
|
+
return /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
|
|
10583
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.accent, children: "Policy for " + target + " (\u2191\u2193 enter \xB7 esc cancel)" }),
|
|
10584
|
+
visible.map((o, i) => {
|
|
10585
|
+
const index = options.indexOf(o);
|
|
10586
|
+
const selected = index === pick;
|
|
10587
|
+
return /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
10588
|
+
/* @__PURE__ */ jsx4(Text4, { color: selected ? theme.accentBright : void 0, children: (selected ? "\u25B8 " : " ") + truncate2(o.name, 30).padEnd(31) }),
|
|
10589
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: o.detail })
|
|
10590
|
+
] }, o.id + i);
|
|
10591
|
+
})
|
|
10592
|
+
] });
|
|
10593
|
+
}
|
|
10594
|
+
function PersonView({ row, policies, cols }) {
|
|
10595
|
+
const m = row?.member;
|
|
10596
|
+
const guest = m?.guest_user_id || SELF;
|
|
10597
|
+
const analyticsQ = useLoader(() => api.fleet.analytics(guest), [guest]);
|
|
10598
|
+
if (!m) return /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "Nobody selected." });
|
|
10599
|
+
const a = analyticsQ.data ?? {};
|
|
10600
|
+
const totals = a["totals"] ?? {};
|
|
10601
|
+
const agents = a["agents"] ?? [];
|
|
10602
|
+
const tools = a["tools"] ?? [];
|
|
10603
|
+
const denies = a["deny_reasons"] ?? [];
|
|
10604
|
+
const runs = m.policy_id || row?.group.policy_id || "";
|
|
10605
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
10606
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
10607
|
+
/* @__PURE__ */ jsx4(Text4, { bold: true, color: theme.accentBright, children: m.email }),
|
|
10608
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " " + (row?.group ? label(row.group) : "") })
|
|
10609
|
+
] }),
|
|
10610
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
10611
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "Runs " }),
|
|
10612
|
+
/* @__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" })
|
|
10613
|
+
] }),
|
|
10614
|
+
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(DataView, { loading: analyticsQ.loading, error: analyticsQ.error, children: /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
10615
|
+
/* @__PURE__ */ jsxs4(Text4, { children: [
|
|
10616
|
+
String(totals.calls ?? 0) + " calls ",
|
|
10617
|
+
/* @__PURE__ */ jsx4(Text4, { color: (totals.denied ?? 0) > 0 ? theme.bad : theme.dim, children: String(totals.denied ?? 0) + " denied" }),
|
|
10618
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " " + String(totals.sessions ?? 0) + " sessions" })
|
|
10619
|
+
] }),
|
|
10620
|
+
denies.length > 0 ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
|
|
10621
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "What was stopped" }),
|
|
10622
|
+
/* @__PURE__ */ jsx4(
|
|
10623
|
+
Table,
|
|
10624
|
+
{
|
|
10625
|
+
columns: [{ header: "reason", width: Math.min(48, cols - 14) }, { header: "times", width: 8 }],
|
|
10626
|
+
rows: denies.slice(0, 6).map((d) => [{ value: d.name, color: theme.bad }, { value: String(d.calls) }])
|
|
10627
|
+
}
|
|
10628
|
+
)
|
|
10629
|
+
] }) : null,
|
|
10630
|
+
tools.length > 0 ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
|
|
10631
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "Most used tools" }),
|
|
10632
|
+
/* @__PURE__ */ jsx4(
|
|
10633
|
+
Table,
|
|
10634
|
+
{
|
|
10635
|
+
columns: [{ header: "tool", width: Math.min(48, cols - 14) }, { header: "calls", width: 8 }],
|
|
10636
|
+
rows: tools.slice(0, 6).map((t) => [{ value: t.name }, { value: String(t.calls) }])
|
|
10637
|
+
}
|
|
10638
|
+
)
|
|
10639
|
+
] }) : null,
|
|
10640
|
+
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
|
|
10641
|
+
] }) }) }),
|
|
10642
|
+
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "\u2190 back" }) })
|
|
10643
|
+
] });
|
|
10644
|
+
}
|
|
10645
|
+
function ReportsView({
|
|
10646
|
+
report: report2,
|
|
10647
|
+
loading,
|
|
10648
|
+
error,
|
|
10649
|
+
schedule,
|
|
10650
|
+
next,
|
|
10651
|
+
status,
|
|
10652
|
+
prompt,
|
|
10653
|
+
draft,
|
|
10654
|
+
onChange,
|
|
10655
|
+
onSubmit,
|
|
10656
|
+
cols
|
|
10657
|
+
}) {
|
|
10658
|
+
const on = (v) => v ? theme.ok : theme.dim;
|
|
10659
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
10660
|
+
/* @__PURE__ */ jsx4(DataView, { loading, error, children: /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
10661
|
+
/* @__PURE__ */ jsx4(Text4, { bold: true, children: report2?.headline ?? "" }),
|
|
10662
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: report2?.title ?? "" }),
|
|
10663
|
+
report2 && report2.top_denies.length > 0 ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
|
|
10664
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "What was stopped" }),
|
|
10665
|
+
/* @__PURE__ */ jsx4(
|
|
10666
|
+
Table,
|
|
10667
|
+
{
|
|
10668
|
+
columns: [{ header: "reason", width: Math.min(48, cols - 14) }, { header: "times", width: 8 }],
|
|
10669
|
+
rows: report2.top_denies.slice(0, 5).map((d) => [{ value: d.name, color: theme.bad }, { value: String(d.calls) }])
|
|
10670
|
+
}
|
|
10671
|
+
)
|
|
10672
|
+
] }) : null
|
|
10673
|
+
] }) }),
|
|
10674
|
+
/* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
|
|
10675
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "Scheduled" }),
|
|
10676
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
10677
|
+
/* @__PURE__ */ jsx4(Text4, { color: on(schedule?.day), children: "daily " }),
|
|
10678
|
+
/* @__PURE__ */ jsx4(Text4, { color: on(schedule?.week), children: "weekly " }),
|
|
10679
|
+
/* @__PURE__ */ jsx4(Text4, { color: on(schedule?.month), children: "monthly " }),
|
|
10680
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "day ends " + timeOf(schedule?.endMinute ?? 0) + " UTC" })
|
|
10681
|
+
] }),
|
|
10682
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "to " + ((schedule?.emails ?? []).concat(schedule?.telegram ?? []).join(", ") || "nobody yet") }),
|
|
10683
|
+
next > 0 ? /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "next " + new Date(next * 1e3).toISOString().slice(0, 16).replace("T", " ") + " UTC" }) : null
|
|
10684
|
+
] }),
|
|
10685
|
+
prompt ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
|
|
10686
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.accent, children: promptLabel(prompt) + " " }),
|
|
10687
|
+
/* @__PURE__ */ jsx4(TextInput2, { value: draft, onChange, onSubmit })
|
|
10688
|
+
] }) : null,
|
|
10689
|
+
status ? /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) }) : null,
|
|
10690
|
+
/* @__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" }) })
|
|
10691
|
+
] });
|
|
10692
|
+
}
|
|
10693
|
+
var SELF, UNGROUPED, GROUP_COLORS, shortId, policyLabel, label, promptLabel, splitTargets, minutesOf, timeOf;
|
|
10694
|
+
var init_Fleet = __esm({
|
|
10695
|
+
"src/tui/panels/Fleet.tsx"() {
|
|
10696
|
+
"use strict";
|
|
10697
|
+
init_api_client();
|
|
10698
|
+
init_components();
|
|
10699
|
+
init_hooks();
|
|
10700
|
+
init_theme();
|
|
10701
|
+
SELF = "self";
|
|
10702
|
+
UNGROUPED = "~ungrouped";
|
|
10703
|
+
GROUP_COLORS = ["slate", "blue", "green", "amber", "red", "violet", "cyan", "pink"];
|
|
10704
|
+
shortId = (id) => id.length > 10 ? id.slice(0, 9) + "\u2026" : id;
|
|
10705
|
+
policyLabel = (id, policies) => {
|
|
10706
|
+
if (!id) return "\u2014";
|
|
10707
|
+
const hit = policies.find((p) => p.id === id);
|
|
10708
|
+
return hit ? hit.name : shortId(id);
|
|
10709
|
+
};
|
|
10710
|
+
label = (g) => g.name === UNGROUPED ? "Uncategorized" : g.name;
|
|
10711
|
+
promptLabel = (p) => {
|
|
10712
|
+
switch (p) {
|
|
10713
|
+
case "invite":
|
|
10714
|
+
return "Invite (email):";
|
|
10715
|
+
case "newGroup":
|
|
10716
|
+
return "New group:";
|
|
10717
|
+
case "rename":
|
|
10718
|
+
return "Rename to:";
|
|
10719
|
+
case "assign":
|
|
10720
|
+
return "File under (blank = uncategorized):";
|
|
10721
|
+
case "emails":
|
|
10722
|
+
return "Email the report to:";
|
|
10723
|
+
case "telegram":
|
|
10724
|
+
return "Telegram chat id or @handle:";
|
|
10725
|
+
case "endTime":
|
|
10726
|
+
return "A day ends at (HH:MM):";
|
|
10727
|
+
default:
|
|
10728
|
+
return "";
|
|
10729
|
+
}
|
|
10730
|
+
};
|
|
10731
|
+
splitTargets = (v) => v.split(/[\s,;]+/).map((s) => s.trim()).filter(Boolean);
|
|
10732
|
+
minutesOf = (v) => {
|
|
10733
|
+
const [h, m] = v.split(":");
|
|
10734
|
+
const total = (Number(h) || 0) * 60 + (Number(m) || 0);
|
|
10735
|
+
return total >= 0 && total <= 1439 ? total : 0;
|
|
10736
|
+
};
|
|
10737
|
+
timeOf = (minute) => String(Math.floor(minute / 60)).padStart(2, "0") + ":" + String(minute % 60).padStart(2, "0");
|
|
10738
|
+
}
|
|
10739
|
+
});
|
|
10740
|
+
|
|
10741
|
+
// src/tui/panels/DryRun.tsx
|
|
10742
|
+
import { Box as Box5, Text as Text5, useInput as useInput4 } from "ink";
|
|
10743
|
+
import TextInput3 from "ink-text-input";
|
|
10744
|
+
import { useEffect as useEffect4, useState as useState5 } from "react";
|
|
10745
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
9974
10746
|
function requestDryRun(policyId) {
|
|
9975
10747
|
pendingPolicyId = policyId;
|
|
9976
10748
|
}
|
|
9977
10749
|
function DryRunPanel({ focused }) {
|
|
9978
10750
|
const list5 = useLoader(() => api.policies.list());
|
|
9979
10751
|
const policies = list5.data?.policies ?? [];
|
|
9980
|
-
const [pi, setPi] =
|
|
9981
|
-
const [limit, setLimit] =
|
|
9982
|
-
const [editLogs, setEditLogs] =
|
|
10752
|
+
const [pi, setPi] = useState5(0);
|
|
10753
|
+
const [limit, setLimit] = useState5(DEFAULT_LIMIT);
|
|
10754
|
+
const [editLogs, setEditLogs] = useState5(null);
|
|
9983
10755
|
const statsQ = useLoader(() => api.stats.get());
|
|
9984
10756
|
const totalLogs = statsQ.data?.total_calls ?? null;
|
|
9985
|
-
const [res, setRes] =
|
|
9986
|
-
const [running, setRunning] =
|
|
9987
|
-
const [err2, setErr] =
|
|
9988
|
-
const [off, setOff] =
|
|
10757
|
+
const [res, setRes] = useState5(null);
|
|
10758
|
+
const [running, setRunning] = useState5(false);
|
|
10759
|
+
const [err2, setErr] = useState5(null);
|
|
10760
|
+
const [off, setOff] = useState5(0);
|
|
9989
10761
|
const { cols, rows } = usePanelSize();
|
|
9990
10762
|
const selected = policies[Math.min(pi, Math.max(0, policies.length - 1))];
|
|
9991
10763
|
function run12(policyIdx = pi, lim = limit) {
|
|
@@ -9997,7 +10769,7 @@ function DryRunPanel({ focused }) {
|
|
|
9997
10769
|
setOff(0);
|
|
9998
10770
|
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));
|
|
9999
10771
|
}
|
|
10000
|
-
|
|
10772
|
+
useEffect4(() => {
|
|
10001
10773
|
if (policies.length === 0 || res || running || err2) return;
|
|
10002
10774
|
let idx = 0;
|
|
10003
10775
|
if (pendingPolicyId) {
|
|
@@ -10008,7 +10780,7 @@ function DryRunPanel({ focused }) {
|
|
|
10008
10780
|
setPi(idx);
|
|
10009
10781
|
run12(idx, limit);
|
|
10010
10782
|
}, [policies.length]);
|
|
10011
|
-
|
|
10783
|
+
useInput4((input, key) => {
|
|
10012
10784
|
if (!focused || editLogs !== null) return;
|
|
10013
10785
|
if (key.upArrow) setOff((n) => Math.max(0, n - 1));
|
|
10014
10786
|
else if (key.downArrow) setOff((n) => n + 1);
|
|
@@ -10030,10 +10802,10 @@ function DryRunPanel({ focused }) {
|
|
|
10030
10802
|
}
|
|
10031
10803
|
const w = Math.max(40, cols);
|
|
10032
10804
|
const lines = [];
|
|
10033
|
-
const head = (t) => lines.push(/* @__PURE__ */
|
|
10034
|
-
const dim2 = (t) => lines.push(/* @__PURE__ */
|
|
10035
|
-
const row = (t) => lines.push(/* @__PURE__ */
|
|
10036
|
-
const blank = () => lines.push(/* @__PURE__ */
|
|
10805
|
+
const head = (t) => lines.push(/* @__PURE__ */ jsx5(Text5, { bold: true, color: theme.accentBright, children: t }, `h${lines.length}`));
|
|
10806
|
+
const dim2 = (t) => lines.push(/* @__PURE__ */ jsx5(Text5, { color: theme.dim, wrap: "truncate", children: t }, `d${lines.length}`));
|
|
10807
|
+
const row = (t) => lines.push(/* @__PURE__ */ jsx5(Text5, { wrap: "truncate", children: t }, `r${lines.length}`));
|
|
10808
|
+
const blank = () => lines.push(/* @__PURE__ */ jsx5(Text5, { children: " " }, `b${lines.length}`));
|
|
10037
10809
|
if (res) {
|
|
10038
10810
|
const s = res.summary;
|
|
10039
10811
|
const denyAfter = s.would_deny;
|
|
@@ -10044,13 +10816,13 @@ function DryRunPanel({ focused }) {
|
|
|
10044
10816
|
head("Summary");
|
|
10045
10817
|
row(` evaluated ${s.evaluated} would allow ${s.would_allow} would deny ${s.would_deny}`);
|
|
10046
10818
|
lines.push(
|
|
10047
|
-
/* @__PURE__ */
|
|
10819
|
+
/* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
|
|
10048
10820
|
" newly blocked ",
|
|
10049
|
-
/* @__PURE__ */
|
|
10821
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.bad, bold: true, children: String(s.newly_blocked) }),
|
|
10050
10822
|
" newly allowed ",
|
|
10051
|
-
/* @__PURE__ */
|
|
10823
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.ok, bold: true, children: String(s.newly_allowed) }),
|
|
10052
10824
|
" unchanged ",
|
|
10053
|
-
/* @__PURE__ */
|
|
10825
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: String(s.unchanged) })
|
|
10054
10826
|
] }, `sum${lines.length}`)
|
|
10055
10827
|
);
|
|
10056
10828
|
row(` deny rate ${before} -> ${after} (${Number(ppShift) > 0 ? "+" : ""}${ppShift}pp)`);
|
|
@@ -10085,12 +10857,12 @@ function DryRunPanel({ focused }) {
|
|
|
10085
10857
|
const flip = `${c2.original}->${c2.predicted}`;
|
|
10086
10858
|
const newlyAllowed = c2.predicted === "ALLOW";
|
|
10087
10859
|
lines.push(
|
|
10088
|
-
/* @__PURE__ */
|
|
10860
|
+
/* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
|
|
10089
10861
|
" ",
|
|
10090
|
-
/* @__PURE__ */
|
|
10091
|
-
/* @__PURE__ */
|
|
10092
|
-
/* @__PURE__ */
|
|
10093
|
-
/* @__PURE__ */
|
|
10862
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.accent, children: truncate2(c2.tool, 16).padEnd(17) }),
|
|
10863
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: truncate2(c2.agent || "-", 14).padEnd(15) }),
|
|
10864
|
+
/* @__PURE__ */ jsx5(Text5, { color: newlyAllowed ? theme.ok : theme.bad, children: flip.padEnd(14) }),
|
|
10865
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: truncate2(c2.preview.replace(/\s+/g, " "), Math.max(10, w - 50)) })
|
|
10094
10866
|
] }, `c${lines.length}`)
|
|
10095
10867
|
);
|
|
10096
10868
|
}
|
|
@@ -10103,28 +10875,28 @@ function DryRunPanel({ focused }) {
|
|
|
10103
10875
|
const win = lines.slice(start, start + budget);
|
|
10104
10876
|
const above = start;
|
|
10105
10877
|
const below = Math.max(0, lines.length - (start + budget));
|
|
10106
|
-
return /* @__PURE__ */
|
|
10107
|
-
/* @__PURE__ */
|
|
10108
|
-
/* @__PURE__ */
|
|
10109
|
-
/* @__PURE__ */
|
|
10110
|
-
/* @__PURE__ */
|
|
10111
|
-
/* @__PURE__ */
|
|
10878
|
+
return /* @__PURE__ */ jsx5(DataView, { loading: list5.loading && !list5.data, error: list5.error, children: /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
10879
|
+
/* @__PURE__ */ jsx5(Text5, { bold: true, color: theme.accentBright, children: "Policy Dry Run" }),
|
|
10880
|
+
/* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
|
|
10881
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " policy " }),
|
|
10882
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.accent, children: "\u2039 " + truncate2(selected?.name ?? "none", 30) + " \u203A" }),
|
|
10883
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: ` ${policies.length ? pi + 1 : 0}/${policies.length} press p \xB7 mode ${selected?.mode ?? "-"}` })
|
|
10112
10884
|
] }),
|
|
10113
|
-
editLogs !== null ? /* @__PURE__ */
|
|
10114
|
-
/* @__PURE__ */
|
|
10115
|
-
/* @__PURE__ */
|
|
10116
|
-
/* @__PURE__ */
|
|
10117
|
-
/* @__PURE__ */
|
|
10118
|
-
] }) : /* @__PURE__ */
|
|
10119
|
-
/* @__PURE__ */
|
|
10120
|
-
/* @__PURE__ */
|
|
10121
|
-
/* @__PURE__ */
|
|
10885
|
+
editLogs !== null ? /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
10886
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " logs " }),
|
|
10887
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.warn, children: "how many recent logs to replay: " }),
|
|
10888
|
+
/* @__PURE__ */ jsx5(TextInput3, { value: editLogs, onChange: setEditLogs, onSubmit: commitLogs }),
|
|
10889
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: ` (max ${Math.min(MAX_LIMIT, totalLogs && totalLogs > 0 ? totalLogs : MAX_LIMIT)})` })
|
|
10890
|
+
] }) : /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
|
|
10891
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " logs " }),
|
|
10892
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.accent, children: "\u2039 " + String(limit) + " \u203A" }),
|
|
10893
|
+
/* @__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}` : ""}` })
|
|
10122
10894
|
] }),
|
|
10123
|
-
/* @__PURE__ */
|
|
10124
|
-
running ? /* @__PURE__ */
|
|
10125
|
-
err2 ? /* @__PURE__ */
|
|
10126
|
-
!running && !err2 && !res ? /* @__PURE__ */
|
|
10127
|
-
/* @__PURE__ */
|
|
10895
|
+
/* @__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" }),
|
|
10896
|
+
running ? /* @__PURE__ */ jsx5(Text5, { color: theme.warn, children: "replaying recent traffic\u2026" }) : null,
|
|
10897
|
+
err2 ? /* @__PURE__ */ jsx5(Text5, { color: theme.bad, wrap: "truncate", children: "\u2717 " + err2 }) : null,
|
|
10898
|
+
!running && !err2 && !res ? /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: "no result yet, press r to run" }) : null,
|
|
10899
|
+
/* @__PURE__ */ jsx5(Box5, { marginTop: 1, flexDirection: "column", children: win })
|
|
10128
10900
|
] }) });
|
|
10129
10901
|
}
|
|
10130
10902
|
var DEFAULT_LIMIT, MAX_LIMIT, pendingPolicyId, pct;
|
|
@@ -10143,8 +10915,8 @@ var init_DryRun = __esm({
|
|
|
10143
10915
|
});
|
|
10144
10916
|
|
|
10145
10917
|
// src/tui/nav.ts
|
|
10146
|
-
function requestSection(
|
|
10147
|
-
pending =
|
|
10918
|
+
function requestSection(label2) {
|
|
10919
|
+
pending = label2;
|
|
10148
10920
|
for (const fn of subs) fn();
|
|
10149
10921
|
}
|
|
10150
10922
|
function takePendingSection() {
|
|
@@ -10168,10 +10940,10 @@ var init_nav = __esm({
|
|
|
10168
10940
|
});
|
|
10169
10941
|
|
|
10170
10942
|
// src/tui/panels/Policies.tsx
|
|
10171
|
-
import { Box as
|
|
10172
|
-
import
|
|
10173
|
-
import { useEffect as
|
|
10174
|
-
import { Fragment as
|
|
10943
|
+
import { Box as Box6, Text as Text6, useInput as useInput5 } from "ink";
|
|
10944
|
+
import TextInput4 from "ink-text-input";
|
|
10945
|
+
import { useEffect as useEffect5, useState as useState6 } from "react";
|
|
10946
|
+
import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
10175
10947
|
function ruleSummary(r) {
|
|
10176
10948
|
const bits = [];
|
|
10177
10949
|
for (const [g, tag] of [["commandConstraints", "cmd"], ["pathConstraints", "path"], ["filenameConstraints", "file"], ["urlConstraints", "url"]]) {
|
|
@@ -10188,23 +10960,23 @@ function PoliciesPanel({ focused }) {
|
|
|
10188
10960
|
const activeId = activeQ.data?.policy?.id ?? null;
|
|
10189
10961
|
const activeBy = activeQ.data?.matched_by;
|
|
10190
10962
|
const { rows: panelRows } = usePanelSize();
|
|
10191
|
-
const [view, setView] =
|
|
10192
|
-
const [pi, setPi] =
|
|
10193
|
-
const [ri, setRi] =
|
|
10194
|
-
const [fi, setFi] =
|
|
10195
|
-
const [rules, setRules] =
|
|
10196
|
-
const [mode, setMode] =
|
|
10197
|
-
const [dirty, setDirty] =
|
|
10198
|
-
const [editing, setEditing] =
|
|
10199
|
-
const [editVal, setEditVal] =
|
|
10200
|
-
const [status, setStatus] =
|
|
10201
|
-
const [pendingDel, setPendingDel] =
|
|
10202
|
-
const [permCursor, setPermCursor] =
|
|
10203
|
-
const [matchSel, setMatchSel] =
|
|
10204
|
-
const [busyCreate, setBusyCreate] =
|
|
10963
|
+
const [view, setView] = useState6("list");
|
|
10964
|
+
const [pi, setPi] = useState6(0);
|
|
10965
|
+
const [ri, setRi] = useState6(0);
|
|
10966
|
+
const [fi, setFi] = useState6(0);
|
|
10967
|
+
const [rules, setRules] = useState6([]);
|
|
10968
|
+
const [mode, setMode] = useState6("denylist");
|
|
10969
|
+
const [dirty, setDirty] = useState6(false);
|
|
10970
|
+
const [editing, setEditing] = useState6(false);
|
|
10971
|
+
const [editVal, setEditVal] = useState6("");
|
|
10972
|
+
const [status, setStatus] = useState6(null);
|
|
10973
|
+
const [pendingDel, setPendingDel] = useState6(null);
|
|
10974
|
+
const [permCursor, setPermCursor] = useState6(0);
|
|
10975
|
+
const [matchSel, setMatchSel] = useState6(0);
|
|
10976
|
+
const [busyCreate, setBusyCreate] = useState6(false);
|
|
10205
10977
|
const selected = policies[Math.min(pi, Math.max(0, policies.length - 1))];
|
|
10206
10978
|
const detail = useLoader(() => selected ? api.policies.get(selected.id) : Promise.resolve(null), [selected?.id]);
|
|
10207
|
-
|
|
10979
|
+
useEffect5(() => {
|
|
10208
10980
|
if (detail.data) {
|
|
10209
10981
|
setRules(detail.data.rules);
|
|
10210
10982
|
setMode(detail.data.mode ?? "denylist");
|
|
@@ -10255,7 +11027,7 @@ function PoliciesPanel({ focused }) {
|
|
|
10255
11027
|
setDirty(false);
|
|
10256
11028
|
setStatus("discarded");
|
|
10257
11029
|
};
|
|
10258
|
-
const [creating, setCreating] =
|
|
11030
|
+
const [creating, setCreating] = useState6(null);
|
|
10259
11031
|
function commitNewPolicy(name) {
|
|
10260
11032
|
const n = name.trim();
|
|
10261
11033
|
setCreating(null);
|
|
@@ -10270,10 +11042,10 @@ function PoliciesPanel({ focused }) {
|
|
|
10270
11042
|
setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
|
|
10271
11043
|
});
|
|
10272
11044
|
}
|
|
10273
|
-
|
|
11045
|
+
useEffect5(() => {
|
|
10274
11046
|
if (busyCreate && policies.length > 0) setBusyCreate(false);
|
|
10275
11047
|
}, [busyCreate, policies.length]);
|
|
10276
|
-
|
|
11048
|
+
useInput5(
|
|
10277
11049
|
(input, key) => {
|
|
10278
11050
|
if (creating !== null) return;
|
|
10279
11051
|
if (key.ctrl && input === "r" && !editing) {
|
|
@@ -10444,39 +11216,39 @@ function PoliciesPanel({ focused }) {
|
|
|
10444
11216
|
const lWin = policies.slice(lStart, lStart + lBudget);
|
|
10445
11217
|
const lAbove = lStart;
|
|
10446
11218
|
const lBelow = Math.max(0, policies.length - (lStart + lBudget));
|
|
10447
|
-
return /* @__PURE__ */
|
|
10448
|
-
/* @__PURE__ */
|
|
10449
|
-
/* @__PURE__ */
|
|
10450
|
-
activeQ.loading && !activeQ.data ? /* @__PURE__ */
|
|
10451
|
-
/* @__PURE__ */
|
|
11219
|
+
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: [
|
|
11220
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
11221
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "active: " }),
|
|
11222
|
+
activeQ.loading && !activeQ.data ? /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "\u2026" }) : activeName ? /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
11223
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.ok, bold: true, children: [
|
|
10452
11224
|
"\u25CF ",
|
|
10453
11225
|
truncate2(activeName, 28)
|
|
10454
11226
|
] }),
|
|
10455
|
-
/* @__PURE__ */
|
|
10456
|
-
] }) : /* @__PURE__ */
|
|
11227
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: activeBy ? ` (${activeBy})` : "" })
|
|
11228
|
+
] }) : /* @__PURE__ */ jsx6(Text6, { color: theme.bad, bold: true, children: "\u25CB NONE \u2014 nothing enforced, grants refused" })
|
|
10457
11229
|
] }),
|
|
10458
|
-
/* @__PURE__ */
|
|
10459
|
-
creating !== null ? /* @__PURE__ */
|
|
10460
|
-
/* @__PURE__ */
|
|
10461
|
-
/* @__PURE__ */
|
|
11230
|
+
/* @__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" }),
|
|
11231
|
+
creating !== null ? /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
11232
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.warn, children: "new policy name: " }),
|
|
11233
|
+
/* @__PURE__ */ jsx6(TextInput4, { value: creating, onChange: setCreating, onSubmit: commitNewPolicy })
|
|
10462
11234
|
] }) : null,
|
|
10463
|
-
/* @__PURE__ */
|
|
11235
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexDirection: "column", children: lWin.map((p, wi) => {
|
|
10464
11236
|
const i = lStart + wi;
|
|
10465
|
-
return /* @__PURE__ */
|
|
11237
|
+
return /* @__PURE__ */ jsxs6(Text6, { color: i === pi ? theme.accentBright : void 0, bold: i === pi, children: [
|
|
10466
11238
|
(i === pi ? "\u25B8 " : " ") + truncate2(p.name, 26).padEnd(27),
|
|
10467
|
-
/* @__PURE__ */
|
|
11239
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
10468
11240
|
p.mode.padEnd(10),
|
|
10469
11241
|
" ",
|
|
10470
11242
|
p.rules.length,
|
|
10471
11243
|
" rules"
|
|
10472
11244
|
] }),
|
|
10473
|
-
p.id === activeId ? /* @__PURE__ */
|
|
11245
|
+
p.id === activeId ? /* @__PURE__ */ jsx6(Text6, { color: theme.ok, bold: true, children: " \u25CF ACTIVE" }) : null
|
|
10474
11246
|
] }, p.id);
|
|
10475
11247
|
}) }),
|
|
10476
|
-
status ? /* @__PURE__ */
|
|
11248
|
+
status ? /* @__PURE__ */ jsx6(Text6, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
10477
11249
|
] }) });
|
|
10478
11250
|
}
|
|
10479
|
-
const dirtyTag = dirty ? /* @__PURE__ */
|
|
11251
|
+
const dirtyTag = dirty ? /* @__PURE__ */ jsx6(Text6, { color: theme.warn, children: " \u25CF unsaved (s save \xB7 x discard)" }) : null;
|
|
10480
11252
|
if (view === "rules") {
|
|
10481
11253
|
const rStatusLines = status ? status.split("\n").length : 0;
|
|
10482
11254
|
const rHead = 4 + rStatusLines;
|
|
@@ -10486,16 +11258,16 @@ function PoliciesPanel({ focused }) {
|
|
|
10486
11258
|
const rWin = rules.slice(rStart, rStart + rBudget);
|
|
10487
11259
|
const rAbove = rStart;
|
|
10488
11260
|
const rBelow = Math.max(0, rules.length - (rStart + rBudget));
|
|
10489
|
-
return /* @__PURE__ */
|
|
10490
|
-
/* @__PURE__ */
|
|
10491
|
-
/* @__PURE__ */
|
|
10492
|
-
/* @__PURE__ */
|
|
10493
|
-
/* @__PURE__ */
|
|
10494
|
-
/* @__PURE__ */
|
|
11261
|
+
return /* @__PURE__ */ jsx6(DataView, { loading: detail.loading && !detail.data, error: detail.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
11262
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
11263
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: theme.accentBright, children: truncate2(selected?.name ?? "", 28) }),
|
|
11264
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " mode: " }),
|
|
11265
|
+
/* @__PURE__ */ jsx6(Text6, { color: mode === "whitelist" ? theme.warn : void 0, children: mode }),
|
|
11266
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: ` ${rules.length} rules` }),
|
|
10495
11267
|
dirtyTag
|
|
10496
11268
|
] }),
|
|
10497
|
-
/* @__PURE__ */
|
|
10498
|
-
/* @__PURE__ */
|
|
11269
|
+
/* @__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}` : ""}` }),
|
|
11270
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(
|
|
10499
11271
|
Table,
|
|
10500
11272
|
{
|
|
10501
11273
|
columns: [
|
|
@@ -10519,33 +11291,33 @@ function PoliciesPanel({ focused }) {
|
|
|
10519
11291
|
})
|
|
10520
11292
|
}
|
|
10521
11293
|
) }),
|
|
10522
|
-
rules.length === 0 ? /* @__PURE__ */
|
|
10523
|
-
status ? /* @__PURE__ */
|
|
11294
|
+
rules.length === 0 ? /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "(no rules)" }) : null,
|
|
11295
|
+
status ? /* @__PURE__ */ jsx6(Text6, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
10524
11296
|
] }) });
|
|
10525
11297
|
}
|
|
10526
11298
|
if (view === "match") {
|
|
10527
11299
|
const r = rules[ri];
|
|
10528
11300
|
const items = r ? matchItems(r) : [];
|
|
10529
11301
|
const t = r ? currentCType(r) : "none";
|
|
10530
|
-
return /* @__PURE__ */
|
|
10531
|
-
/* @__PURE__ */
|
|
10532
|
-
/* @__PURE__ */
|
|
10533
|
-
/* @__PURE__ */
|
|
11302
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
11303
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
11304
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: theme.accentBright, children: "Match values " }),
|
|
11305
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: r ? `${r.effect} \xB7 ${t}` : "" }),
|
|
10534
11306
|
dirtyTag
|
|
10535
11307
|
] }),
|
|
10536
|
-
/* @__PURE__ */
|
|
10537
|
-
/* @__PURE__ */
|
|
10538
|
-
items.length === 0 ? /* @__PURE__ */
|
|
11308
|
+
/* @__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" }),
|
|
11309
|
+
/* @__PURE__ */ jsxs6(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
11310
|
+
items.length === 0 ? /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "(no values \u2014 press a to add one)" }) : null,
|
|
10539
11311
|
items.map((it, i) => {
|
|
10540
11312
|
const sel = i === matchSel;
|
|
10541
11313
|
if (sel && editing) {
|
|
10542
11314
|
const lE = editVal.startsWith("*");
|
|
10543
11315
|
const rE = editVal.length > 0 && editVal.endsWith("*");
|
|
10544
|
-
return /* @__PURE__ */
|
|
10545
|
-
/* @__PURE__ */
|
|
10546
|
-
/* @__PURE__ */
|
|
10547
|
-
/* @__PURE__ */
|
|
10548
|
-
|
|
11316
|
+
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
11317
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.accentBright, children: "\u25B8 " }),
|
|
11318
|
+
/* @__PURE__ */ jsx6(Text6, { color: lE ? theme.ok : theme.dim, children: lE ? "\u2731 " : "\xB7 " }),
|
|
11319
|
+
/* @__PURE__ */ jsx6(
|
|
11320
|
+
TextInput4,
|
|
10549
11321
|
{
|
|
10550
11322
|
value: editVal,
|
|
10551
11323
|
onChange: setEditVal,
|
|
@@ -10559,41 +11331,41 @@ function PoliciesPanel({ focused }) {
|
|
|
10559
11331
|
}
|
|
10560
11332
|
}
|
|
10561
11333
|
),
|
|
10562
|
-
/* @__PURE__ */
|
|
11334
|
+
/* @__PURE__ */ jsx6(Text6, { color: rE ? theme.ok : theme.dim, children: rE ? " \u2731" : " \xB7" })
|
|
10563
11335
|
] }, i);
|
|
10564
11336
|
}
|
|
10565
11337
|
const lOn = it.startsWith("*");
|
|
10566
11338
|
const rOn = it.length > 0 && it.endsWith("*");
|
|
10567
|
-
return /* @__PURE__ */
|
|
10568
|
-
/* @__PURE__ */
|
|
10569
|
-
/* @__PURE__ */
|
|
10570
|
-
/* @__PURE__ */
|
|
10571
|
-
/* @__PURE__ */
|
|
11339
|
+
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
11340
|
+
/* @__PURE__ */ jsx6(Text6, { color: sel ? theme.accentBright : void 0, children: sel ? "\u25B8 " : " " }),
|
|
11341
|
+
/* @__PURE__ */ jsx6(Text6, { color: lOn ? theme.ok : theme.dim, children: lOn ? "\u2731 " : "\xB7 " }),
|
|
11342
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: !it, children: it || "(empty)" }),
|
|
11343
|
+
/* @__PURE__ */ jsx6(Text6, { color: rOn ? theme.ok : theme.dim, children: rOn ? " \u2731" : " \xB7" })
|
|
10572
11344
|
] }, i);
|
|
10573
11345
|
})
|
|
10574
11346
|
] }),
|
|
10575
|
-
/* @__PURE__ */
|
|
10576
|
-
status ? /* @__PURE__ */
|
|
11347
|
+
/* @__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)." }),
|
|
11348
|
+
status ? /* @__PURE__ */ jsx6(Text6, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
10577
11349
|
] });
|
|
10578
11350
|
}
|
|
10579
11351
|
const rule = rules[ri];
|
|
10580
|
-
return /* @__PURE__ */
|
|
10581
|
-
/* @__PURE__ */
|
|
10582
|
-
/* @__PURE__ */
|
|
10583
|
-
/* @__PURE__ */
|
|
11352
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
11353
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
11354
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: theme.accentBright, children: "Rule " }),
|
|
11355
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: rule?.id ?? "" }),
|
|
10584
11356
|
dirtyTag
|
|
10585
11357
|
] }),
|
|
10586
|
-
/* @__PURE__ */
|
|
10587
|
-
/* @__PURE__ */
|
|
11358
|
+
/* @__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" }),
|
|
11359
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexDirection: "column", children: FIELDS.map((f, i) => {
|
|
10588
11360
|
const active2 = i === fi;
|
|
10589
|
-
const
|
|
11361
|
+
const label2 = /* @__PURE__ */ jsx6(Text6, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + f.label.padEnd(13) });
|
|
10590
11362
|
if (f.kind === "perms" && rule) {
|
|
10591
|
-
return /* @__PURE__ */
|
|
10592
|
-
|
|
11363
|
+
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
11364
|
+
label2,
|
|
10593
11365
|
PERMS.map((p, pi2) => {
|
|
10594
11366
|
const on = permList(rule).includes(p);
|
|
10595
11367
|
const cur = active2 && pi2 === permCursor % PERMS.length;
|
|
10596
|
-
return /* @__PURE__ */
|
|
11368
|
+
return /* @__PURE__ */ jsxs6(Text6, { color: on ? theme.ok : theme.bad, inverse: cur, children: [
|
|
10597
11369
|
p,
|
|
10598
11370
|
pi2 < PERMS.length - 1 ? " " : ""
|
|
10599
11371
|
] }, p);
|
|
@@ -10603,17 +11375,17 @@ function PoliciesPanel({ focused }) {
|
|
|
10603
11375
|
if (f.kind === "match" && rule) {
|
|
10604
11376
|
const items = matchItems(rule);
|
|
10605
11377
|
const preview = items.join(", ");
|
|
10606
|
-
return /* @__PURE__ */
|
|
10607
|
-
|
|
10608
|
-
/* @__PURE__ */
|
|
10609
|
-
/* @__PURE__ */
|
|
11378
|
+
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
11379
|
+
label2,
|
|
11380
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: !preview, children: preview || "\u2014" }),
|
|
11381
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: items.length ? ` (${items.length} value${items.length > 1 ? "s" : ""} \xB7 enter: edit)` : " (enter: add values)" })
|
|
10610
11382
|
] }, f.label);
|
|
10611
11383
|
}
|
|
10612
11384
|
const val = rule ? f.get(rule) : "";
|
|
10613
|
-
return /* @__PURE__ */
|
|
10614
|
-
|
|
10615
|
-
active2 && editing && f.kind === "text" ? /* @__PURE__ */
|
|
10616
|
-
|
|
11385
|
+
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
11386
|
+
label2,
|
|
11387
|
+
active2 && editing && f.kind === "text" ? /* @__PURE__ */ jsx6(
|
|
11388
|
+
TextInput4,
|
|
10617
11389
|
{
|
|
10618
11390
|
value: editVal,
|
|
10619
11391
|
onChange: setEditVal,
|
|
@@ -10622,8 +11394,8 @@ function PoliciesPanel({ focused }) {
|
|
|
10622
11394
|
setEditing(false);
|
|
10623
11395
|
}
|
|
10624
11396
|
}
|
|
10625
|
-
) : /* @__PURE__ */
|
|
10626
|
-
|
|
11397
|
+
) : /* @__PURE__ */ jsx6(
|
|
11398
|
+
Text6,
|
|
10627
11399
|
{
|
|
10628
11400
|
color: f.kind === "effect" ? val === "ALLOW" ? theme.ok : theme.bad : void 0,
|
|
10629
11401
|
dimColor: !val,
|
|
@@ -10632,7 +11404,7 @@ function PoliciesPanel({ focused }) {
|
|
|
10632
11404
|
)
|
|
10633
11405
|
] }, f.label);
|
|
10634
11406
|
}) }),
|
|
10635
|
-
status ? /* @__PURE__ */
|
|
11407
|
+
status ? /* @__PURE__ */ jsx6(Text6, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
10636
11408
|
] });
|
|
10637
11409
|
}
|
|
10638
11410
|
var CTYPES, CTYPE_GROUP, currentCType, clearConstraints, setCType, cValue, setCValue, matchItems, setMatchItems, flipStar, migrateEffect, PERMS, permList, permIsAny, togglePerm, permDisplay, ruleHasValues, isBlanketRule, FIELDS;
|
|
@@ -10736,19 +11508,19 @@ var init_Policies = __esm({
|
|
|
10736
11508
|
});
|
|
10737
11509
|
|
|
10738
11510
|
// src/tui/panels/RateLimit.tsx
|
|
10739
|
-
import { Box as
|
|
10740
|
-
import { useEffect as
|
|
10741
|
-
import { Fragment as
|
|
11511
|
+
import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
|
|
11512
|
+
import { useEffect as useEffect6, useState as useState7 } from "react";
|
|
11513
|
+
import { Fragment as Fragment5, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
10742
11514
|
function RateLimitPanel({ focused }) {
|
|
10743
11515
|
const { cols, rows } = usePanelSize();
|
|
10744
11516
|
const layersQ = useLoader(() => api.settings.getSecurityLayers());
|
|
10745
11517
|
const historyQ = useLoader(() => api.settings.getRateLimitHistory());
|
|
10746
11518
|
const insightsQ = useLoader(() => api.stats.securityInsights(7));
|
|
10747
|
-
const [draft, setDraft] =
|
|
10748
|
-
const [dirty, setDirty] =
|
|
10749
|
-
const [sel, setSel] =
|
|
10750
|
-
const [status, setStatus] =
|
|
10751
|
-
|
|
11519
|
+
const [draft, setDraft] = useState7(null);
|
|
11520
|
+
const [dirty, setDirty] = useState7(false);
|
|
11521
|
+
const [sel, setSel] = useState7(0);
|
|
11522
|
+
const [status, setStatus] = useState7(null);
|
|
11523
|
+
useEffect6(() => {
|
|
10752
11524
|
if (layersQ.data && !dirty) setDraft({ ...layersQ.data.layers.rateLimit });
|
|
10753
11525
|
}, [layersQ.data]);
|
|
10754
11526
|
usePoll(() => {
|
|
@@ -10797,7 +11569,7 @@ function RateLimitPanel({ focused }) {
|
|
|
10797
11569
|
const fi = onField ? selC : -1;
|
|
10798
11570
|
const bcur = onField ? -1 : selC - 4;
|
|
10799
11571
|
const off = bcur < 0 ? 0 : Math.min(Math.max(0, bcur - Math.floor(burstBudget / 2)), maxOff);
|
|
10800
|
-
|
|
11572
|
+
useInput6(
|
|
10801
11573
|
(input, key) => {
|
|
10802
11574
|
const step = key.shift ? 10 : 1;
|
|
10803
11575
|
if (key.upArrow) setSel((n) => Math.max(0, n - 1));
|
|
@@ -10816,52 +11588,52 @@ function RateLimitPanel({ focused }) {
|
|
|
10816
11588
|
},
|
|
10817
11589
|
{ isActive: focused }
|
|
10818
11590
|
);
|
|
10819
|
-
return /* @__PURE__ */
|
|
11591
|
+
return /* @__PURE__ */ jsx7(DataView, { loading: layersQ.loading && !draft, error: layersQ.error, children: draft ? (() => {
|
|
10820
11592
|
const lim = draft.perMinute;
|
|
10821
11593
|
const now = insightsQ.data ? insightsQ.data["activity"]?.minute?.slice(-1)[0]?.count ?? 0 : 0;
|
|
10822
11594
|
const loadPct = lim > 0 ? Math.min(100, Math.round(now / lim * 100)) : 0;
|
|
10823
11595
|
const last24h = (insightsQ.data?.["activity"]?.hour ?? []).reduce((s, b) => s + b.count, 0);
|
|
10824
11596
|
const shown = anomalies2.slice(off, off + burstBudget);
|
|
10825
11597
|
const bw = Math.max(10, cols - 30);
|
|
10826
|
-
return /* @__PURE__ */
|
|
10827
|
-
/* @__PURE__ */
|
|
10828
|
-
/* @__PURE__ */
|
|
10829
|
-
/* @__PURE__ */
|
|
10830
|
-
/* @__PURE__ */
|
|
11598
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", height: rows, overflow: "hidden", children: [
|
|
11599
|
+
/* @__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" }),
|
|
11600
|
+
/* @__PURE__ */ jsxs7(FieldRow, { label: "Mode", active: focused && fi === 0, children: [
|
|
11601
|
+
/* @__PURE__ */ jsx7(Text7, { color: modeColor(draft.mode), bold: true, children: draft.mode }),
|
|
11602
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: draft.mode === "off" ? " no limit" : draft.mode === "detect" ? " flag bursts, never block" : " DENY calls over the limit" })
|
|
10831
11603
|
] }),
|
|
10832
|
-
/* @__PURE__ */
|
|
10833
|
-
/* @__PURE__ */
|
|
10834
|
-
/* @__PURE__ */
|
|
10835
|
-
showExtras && /* @__PURE__ */
|
|
10836
|
-
hasLoad ? /* @__PURE__ */
|
|
10837
|
-
/* @__PURE__ */
|
|
10838
|
-
/* @__PURE__ */
|
|
10839
|
-
/* @__PURE__ */
|
|
10840
|
-
/* @__PURE__ */
|
|
11604
|
+
/* @__PURE__ */ jsx7(FieldRow, { label: "Per minute", active: focused && fi === 1, children: /* @__PURE__ */ jsx7(Text7, { bold: true, children: draft.perMinute || "off" }) }),
|
|
11605
|
+
/* @__PURE__ */ jsx7(FieldRow, { label: "Per hour", active: focused && fi === 2, children: /* @__PURE__ */ jsx7(Text7, { bold: true, children: draft.perHour || "off" }) }),
|
|
11606
|
+
/* @__PURE__ */ jsx7(FieldRow, { label: "Per day", active: focused && fi === 3, children: /* @__PURE__ */ jsx7(Text7, { bold: true, children: draft.perDay || "off" }) }),
|
|
11607
|
+
showExtras && /* @__PURE__ */ jsxs7(Fragment5, { children: [
|
|
11608
|
+
hasLoad ? /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
11609
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "load now".padEnd(13) }),
|
|
11610
|
+
/* @__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))) }),
|
|
11611
|
+
/* @__PURE__ */ jsx7(Text7, { color: "#233457", children: "\u2591".repeat(Math.max(0, 18 - Math.round(now / lim * 18))) }),
|
|
11612
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: ` ${now}/${lim} this minute (${loadPct}%)` })
|
|
10841
11613
|
] }) : null,
|
|
10842
|
-
/* @__PURE__ */
|
|
11614
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
|
|
10843
11615
|
"Busiest 7d",
|
|
10844
|
-
/* @__PURE__ */
|
|
11616
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " most calls in one window vs your limit" })
|
|
10845
11617
|
] }),
|
|
10846
|
-
/* @__PURE__ */
|
|
10847
|
-
/* @__PURE__ */
|
|
10848
|
-
/* @__PURE__ */
|
|
10849
|
-
/* @__PURE__ */
|
|
11618
|
+
/* @__PURE__ */ jsx7(BusiestRow, { label: "minute", peak: peaks.minute, limit: draft.perMinute }),
|
|
11619
|
+
/* @__PURE__ */ jsx7(BusiestRow, { label: "hour", peak: peaks.hour, limit: draft.perHour }),
|
|
11620
|
+
/* @__PURE__ */ jsx7(BusiestRow, { label: "day", peak: peaks.day, limit: draft.perDay }),
|
|
11621
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, wrap: "truncate", children: `activity`.padEnd(11) + ` last 24h \xB7 ${last24h} calls \xB7 busiest hour ${peaks.hour}/hr` })
|
|
10850
11622
|
] }),
|
|
10851
|
-
/* @__PURE__ */
|
|
10852
|
-
/* @__PURE__ */
|
|
11623
|
+
/* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
11624
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
|
|
10853
11625
|
`Recent bursts 7d${anomalies2.length ? ` (${anomalies2.length})` : ""}`,
|
|
10854
|
-
maxOff > 0 ? /* @__PURE__ */
|
|
10855
|
-
/* @__PURE__ */
|
|
11626
|
+
maxOff > 0 ? /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: ` ${off + 1}-${Math.min(off + burstBudget, anomalies2.length)}/${anomalies2.length}` }) : null,
|
|
11627
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " over the per-minute limit \xB7 Bypassed = detect (not blocked)" })
|
|
10856
11628
|
] }),
|
|
10857
|
-
anomalies2.length === 0 ? /* @__PURE__ */
|
|
11629
|
+
anomalies2.length === 0 ? /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
10858
11630
|
" ",
|
|
10859
11631
|
insightsQ.loading ? "loading\u2026" : insightsQ.error ? `couldn't load bursts: ${String(insightsQ.error).slice(0, 60)}` : "no bursts in the last 7 days"
|
|
10860
11632
|
] }) : (
|
|
10861
11633
|
// Bounded here as well as on the outer frame, so a list longer than
|
|
10862
11634
|
// the budget is clipped inside its own box instead of shoving the
|
|
10863
11635
|
// footer out of the panel and taking the scroll window with it.
|
|
10864
|
-
/* @__PURE__ */
|
|
11636
|
+
/* @__PURE__ */ jsx7(Box7, { flexDirection: "column", height: burstBudget + 1, overflow: "hidden", children: /* @__PURE__ */ jsx7(
|
|
10865
11637
|
Table,
|
|
10866
11638
|
{
|
|
10867
11639
|
columns: [{ header: "WHEN", width: 15 }, { header: "RESULT", width: 9 }, { header: "CALLS", width: 7 }, { header: "AGENT", width: Math.max(8, bw - 31) }],
|
|
@@ -10878,29 +11650,29 @@ function RateLimitPanel({ focused }) {
|
|
|
10878
11650
|
) })
|
|
10879
11651
|
)
|
|
10880
11652
|
] }),
|
|
10881
|
-
spacerRows ? /* @__PURE__ */
|
|
10882
|
-
/* @__PURE__ */
|
|
10883
|
-
/* @__PURE__ */
|
|
10884
|
-
/* @__PURE__ */
|
|
10885
|
-
lastChange ? /* @__PURE__ */
|
|
10886
|
-
dirty ? /* @__PURE__ */
|
|
10887
|
-
status ? /* @__PURE__ */
|
|
11653
|
+
spacerRows ? /* @__PURE__ */ jsx7(Text7, { children: " " }) : null,
|
|
11654
|
+
/* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
11655
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "limit now " }),
|
|
11656
|
+
/* @__PURE__ */ jsx7(Text7, { children: lim > 0 ? `${lim}/min` : "no per-minute limit" }),
|
|
11657
|
+
lastChange ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: ` \xB7 changed ${when(lastChange.ts)}` }) : null,
|
|
11658
|
+
dirty ? /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: " \u25CF unsaved (s)" }) : null,
|
|
11659
|
+
status ? /* @__PURE__ */ jsx7(Text7, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: " " + status }) : null
|
|
10888
11660
|
] })
|
|
10889
11661
|
] });
|
|
10890
11662
|
})() : null });
|
|
10891
11663
|
}
|
|
10892
|
-
function BusiestRow({ label, peak, limit }) {
|
|
11664
|
+
function BusiestRow({ label: label2, peak, limit }) {
|
|
10893
11665
|
const ctx = limit > 0 ? peak > limit ? `over ${limit}` : peak === limit ? `at limit ${limit}` : `limit ${limit}` : "no limit set";
|
|
10894
11666
|
const hot = limit > 0 && peak > limit;
|
|
10895
|
-
return /* @__PURE__ */
|
|
10896
|
-
/* @__PURE__ */
|
|
10897
|
-
/* @__PURE__ */
|
|
10898
|
-
/* @__PURE__ */
|
|
11667
|
+
return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
11668
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: (" " + label2).padEnd(11) }),
|
|
11669
|
+
/* @__PURE__ */ jsx7(Text7, { color: hot ? theme.bad : void 0, bold: hot, children: String(peak).padStart(4) + " calls" }),
|
|
11670
|
+
/* @__PURE__ */ jsx7(Text7, { color: hot ? theme.bad : theme.dim, children: " \xB7 " + ctx })
|
|
10899
11671
|
] });
|
|
10900
11672
|
}
|
|
10901
|
-
function FieldRow({ label, active: active2, children }) {
|
|
10902
|
-
return /* @__PURE__ */
|
|
10903
|
-
/* @__PURE__ */
|
|
11673
|
+
function FieldRow({ label: label2, active: active2, children }) {
|
|
11674
|
+
return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
11675
|
+
/* @__PURE__ */ jsx7(Text7, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + label2.padEnd(11) }),
|
|
10904
11676
|
children
|
|
10905
11677
|
] });
|
|
10906
11678
|
}
|
|
@@ -10922,25 +11694,25 @@ var init_RateLimit = __esm({
|
|
|
10922
11694
|
});
|
|
10923
11695
|
|
|
10924
11696
|
// src/tui/panels/Dlp.tsx
|
|
10925
|
-
import { Box as
|
|
10926
|
-
import
|
|
10927
|
-
import { useEffect as
|
|
10928
|
-
import { jsx as
|
|
11697
|
+
import { Box as Box8, Text as Text8, useInput as useInput7 } from "ink";
|
|
11698
|
+
import TextInput5 from "ink-text-input";
|
|
11699
|
+
import { useEffect as useEffect7, useState as useState8 } from "react";
|
|
11700
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
10929
11701
|
function DlpPanel({ focused }) {
|
|
10930
11702
|
const { cols, rows } = usePanelSize();
|
|
10931
11703
|
const q = useLoader(() => api.settings.getSecurityLayers());
|
|
10932
|
-
const [dlp, setDlp] =
|
|
10933
|
-
const [available, setAvailable] =
|
|
10934
|
-
const [ghostOn, setGhostOn] =
|
|
10935
|
-
const [ghostPats, setGhostPats] =
|
|
10936
|
-
const [sel, setSel] =
|
|
10937
|
-
const [dirty, setDirty] =
|
|
10938
|
-
const [status, setStatus] =
|
|
10939
|
-
const [adding, setAdding] =
|
|
10940
|
-
const [newName, setNewName] =
|
|
10941
|
-
const [input, setInput] =
|
|
10942
|
-
const [editIdx, setEditIdx] =
|
|
10943
|
-
|
|
11704
|
+
const [dlp, setDlp] = useState8(null);
|
|
11705
|
+
const [available, setAvailable] = useState8([]);
|
|
11706
|
+
const [ghostOn, setGhostOn] = useState8(false);
|
|
11707
|
+
const [ghostPats, setGhostPats] = useState8([]);
|
|
11708
|
+
const [sel, setSel] = useState8(0);
|
|
11709
|
+
const [dirty, setDirty] = useState8(false);
|
|
11710
|
+
const [status, setStatus] = useState8(null);
|
|
11711
|
+
const [adding, setAdding] = useState8(null);
|
|
11712
|
+
const [newName, setNewName] = useState8("");
|
|
11713
|
+
const [input, setInput] = useState8("");
|
|
11714
|
+
const [editIdx, setEditIdx] = useState8(null);
|
|
11715
|
+
useEffect7(() => {
|
|
10944
11716
|
if (q.data && !dirty) {
|
|
10945
11717
|
setDlp({ ...q.data.layers.dlp, patterns: [...q.data.layers.dlp.patterns], custom: [...q.data.layers.dlp.custom] });
|
|
10946
11718
|
setAvailable(q.data.availablePatterns);
|
|
@@ -10987,7 +11759,7 @@ function DlpPanel({ focused }) {
|
|
|
10987
11759
|
];
|
|
10988
11760
|
const selC = Math.min(sel, Math.max(0, entries.length - 1));
|
|
10989
11761
|
const cur = entries[selC];
|
|
10990
|
-
|
|
11762
|
+
useInput7(
|
|
10991
11763
|
(input2, key) => {
|
|
10992
11764
|
if (!dlp) return;
|
|
10993
11765
|
if (key.ctrl && input2 === "r") {
|
|
@@ -11050,17 +11822,17 @@ function DlpPanel({ focused }) {
|
|
|
11050
11822
|
if (right) core = core.replace(/\*+$/, "");
|
|
11051
11823
|
return { left, core: core || s, right };
|
|
11052
11824
|
};
|
|
11053
|
-
const starL = (on) => /* @__PURE__ */
|
|
11054
|
-
const starR = (on) => /* @__PURE__ */
|
|
11825
|
+
const starL = (on) => /* @__PURE__ */ jsx8(Text8, { color: on ? theme.ok : theme.dim, children: on ? "\u2731 " : "\xB7 " });
|
|
11826
|
+
const starR = (on) => /* @__PURE__ */ jsx8(Text8, { color: on ? theme.ok : theme.dim, children: on ? " \u2731" : " \xB7" });
|
|
11055
11827
|
const lines = [];
|
|
11056
|
-
const header = (key,
|
|
11057
|
-
node: /* @__PURE__ */
|
|
11058
|
-
/* @__PURE__ */
|
|
11059
|
-
extra ? /* @__PURE__ */
|
|
11828
|
+
const header = (key, label2, extra) => lines.push({
|
|
11829
|
+
node: /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
11830
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, bold: true, children: label2 }),
|
|
11831
|
+
extra ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " " + extra }) : null
|
|
11060
11832
|
] }, key),
|
|
11061
11833
|
entry: -1
|
|
11062
11834
|
});
|
|
11063
|
-
const note = (key, text) => lines.push({ node: /* @__PURE__ */
|
|
11835
|
+
const note = (key, text) => lines.push({ node: /* @__PURE__ */ jsx8(Text8, { color: theme.dim, wrap: "truncate", children: text }, key), entry: -1 });
|
|
11064
11836
|
let ei = 0;
|
|
11065
11837
|
header("h:builtin", "built-in secret patterns", "space toggles on/off");
|
|
11066
11838
|
available.forEach((p) => {
|
|
@@ -11068,10 +11840,10 @@ function DlpPanel({ focused }) {
|
|
|
11068
11840
|
const isCur = focused && ei === selC;
|
|
11069
11841
|
const idx = ei++;
|
|
11070
11842
|
lines.push({
|
|
11071
|
-
node: /* @__PURE__ */
|
|
11072
|
-
/* @__PURE__ */
|
|
11073
|
-
/* @__PURE__ */
|
|
11074
|
-
/* @__PURE__ */
|
|
11843
|
+
node: /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", backgroundColor: isCur ? "#1c2f63" : void 0, bold: isCur, children: [
|
|
11844
|
+
/* @__PURE__ */ jsx8(Text8, { color: isCur ? theme.accentBright : theme.dim, children: isCur ? "\u25B8 " : " " }),
|
|
11845
|
+
/* @__PURE__ */ jsx8(Text8, { color: on ? theme.ok : theme.dim, children: on ? "\u25CF " : "\u25CB " }),
|
|
11846
|
+
/* @__PURE__ */ jsx8(Text8, { color: on ? void 0 : theme.dim, children: p })
|
|
11075
11847
|
] }, "b:" + p),
|
|
11076
11848
|
entry: idx
|
|
11077
11849
|
});
|
|
@@ -11084,12 +11856,12 @@ function DlpPanel({ focused }) {
|
|
|
11084
11856
|
const idx = ei++;
|
|
11085
11857
|
const w = wcParts(c2.re);
|
|
11086
11858
|
lines.push({
|
|
11087
|
-
node: /* @__PURE__ */
|
|
11088
|
-
/* @__PURE__ */
|
|
11089
|
-
/* @__PURE__ */
|
|
11090
|
-
/* @__PURE__ */
|
|
11859
|
+
node: /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", backgroundColor: isCur ? "#1c2f63" : void 0, bold: isCur, children: [
|
|
11860
|
+
/* @__PURE__ */ jsx8(Text8, { color: isCur ? theme.accentBright : theme.dim, children: isCur ? "\u25B8 " : " " }),
|
|
11861
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: c2.name }),
|
|
11862
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " " }),
|
|
11091
11863
|
starL(w.left),
|
|
11092
|
-
/* @__PURE__ */
|
|
11864
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: truncate2(w.core, Math.max(6, cols - c2.name.length - 14)) }),
|
|
11093
11865
|
starR(w.right)
|
|
11094
11866
|
] }, "c:" + c2.name + i),
|
|
11095
11867
|
entry: idx
|
|
@@ -11103,12 +11875,12 @@ function DlpPanel({ focused }) {
|
|
|
11103
11875
|
const idx = ei++;
|
|
11104
11876
|
const w = wcParts(p);
|
|
11105
11877
|
lines.push({
|
|
11106
|
-
node: /* @__PURE__ */
|
|
11107
|
-
/* @__PURE__ */
|
|
11878
|
+
node: /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", backgroundColor: isCur ? "#1c2f63" : void 0, bold: isCur, children: [
|
|
11879
|
+
/* @__PURE__ */ jsx8(Text8, { color: isCur ? theme.accentBright : theme.dim, children: isCur ? "\u25B8 " : " " }),
|
|
11108
11880
|
starL(w.left),
|
|
11109
|
-
/* @__PURE__ */
|
|
11881
|
+
/* @__PURE__ */ jsx8(Text8, { color: ghostOn ? theme.accent : theme.dim, children: truncate2(w.core, Math.max(6, cols - 12)) }),
|
|
11110
11882
|
starR(w.right),
|
|
11111
|
-
!ghostOn ? /* @__PURE__ */
|
|
11883
|
+
!ghostOn ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " (ghost off)" }) : null
|
|
11112
11884
|
] }, "g:" + p + i),
|
|
11113
11885
|
entry: idx
|
|
11114
11886
|
});
|
|
@@ -11122,19 +11894,19 @@ function DlpPanel({ focused }) {
|
|
|
11122
11894
|
const win = lines.slice(start, start + budget);
|
|
11123
11895
|
const moreAbove = start;
|
|
11124
11896
|
const moreBelow = Math.max(0, lines.length - (start + budget));
|
|
11125
|
-
return /* @__PURE__ */
|
|
11126
|
-
/* @__PURE__ */
|
|
11127
|
-
/* @__PURE__ */
|
|
11128
|
-
/* @__PURE__ */
|
|
11129
|
-
/* @__PURE__ */
|
|
11130
|
-
dirty ? /* @__PURE__ */
|
|
11897
|
+
return /* @__PURE__ */ jsx8(DataView, { loading: q.loading && !dlp, error: q.error, children: dlp ? /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
11898
|
+
/* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
11899
|
+
/* @__PURE__ */ jsx8(Text8, { children: "mode: " }),
|
|
11900
|
+
/* @__PURE__ */ jsx8(Text8, { color: modeColor(dlp.mode), bold: true, children: dlp.mode }),
|
|
11901
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: dlp.mode === "off" ? " (scanning disabled)" : dlp.mode === "detect" ? " (flag dlp:yes, redact output)" : " (deny + redact secrets)" }),
|
|
11902
|
+
dirty ? /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: " \u25CF unsaved (s save \xB7 x discard)" }) : null
|
|
11131
11903
|
] }),
|
|
11132
|
-
/* @__PURE__ */
|
|
11133
|
-
adding ? /* @__PURE__ */
|
|
11134
|
-
/* @__PURE__ */
|
|
11904
|
+
/* @__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" }),
|
|
11905
|
+
adding ? /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", children: /* @__PURE__ */ jsxs8(Box8, { children: [
|
|
11906
|
+
/* @__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): ` }),
|
|
11135
11907
|
adding !== "name" ? starL(input.startsWith("*")) : null,
|
|
11136
|
-
/* @__PURE__ */
|
|
11137
|
-
|
|
11908
|
+
/* @__PURE__ */ jsx8(
|
|
11909
|
+
TextInput5,
|
|
11138
11910
|
{
|
|
11139
11911
|
value: input,
|
|
11140
11912
|
onChange: setInput,
|
|
@@ -11176,8 +11948,8 @@ function DlpPanel({ focused }) {
|
|
|
11176
11948
|
),
|
|
11177
11949
|
adding !== "name" ? starR(input.length > 1 && input.endsWith("*")) : null
|
|
11178
11950
|
] }) }) : null,
|
|
11179
|
-
/* @__PURE__ */
|
|
11180
|
-
status ? /* @__PURE__ */
|
|
11951
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, flexDirection: "column", height: budget, overflow: "hidden", children: win.map((l) => l.node) }),
|
|
11952
|
+
status ? /* @__PURE__ */ jsx8(Text8, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, wrap: "truncate", children: status }) : null
|
|
11181
11953
|
] }) : null });
|
|
11182
11954
|
}
|
|
11183
11955
|
var MODES2;
|
|
@@ -11193,13 +11965,13 @@ var init_Dlp = __esm({
|
|
|
11193
11965
|
});
|
|
11194
11966
|
|
|
11195
11967
|
// src/tui/panels/Audit.tsx
|
|
11196
|
-
import { Box as
|
|
11197
|
-
import
|
|
11968
|
+
import { Box as Box9, Text as Text9, useInput as useInput8 } from "ink";
|
|
11969
|
+
import TextInput6 from "ink-text-input";
|
|
11198
11970
|
import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync9 } from "fs";
|
|
11199
11971
|
import { homedir as homedir10 } from "os";
|
|
11200
11972
|
import { join as join12 } from "path";
|
|
11201
|
-
import { useState as
|
|
11202
|
-
import { jsx as
|
|
11973
|
+
import { useState as useState9 } from "react";
|
|
11974
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
11203
11975
|
function loadLocalRows() {
|
|
11204
11976
|
return parseLocalLines(tailLines(localLogFile(), LOCAL_MAX_BYTES)).map((j, i) => {
|
|
11205
11977
|
const rs = reasonSignals(j.reason);
|
|
@@ -11224,24 +11996,24 @@ function loadLocalRows() {
|
|
|
11224
11996
|
}
|
|
11225
11997
|
function AuditPanel({ active: active2, focused }) {
|
|
11226
11998
|
const { cols, rows } = usePanelSize();
|
|
11227
|
-
const [source, setSource] =
|
|
11228
|
-
const [view, setView] =
|
|
11229
|
-
const [di, setDi] =
|
|
11230
|
-
const [gi, setGi] =
|
|
11231
|
-
const [tool, setTool] =
|
|
11232
|
-
const [agent, setAgent] =
|
|
11233
|
-
const [search, setSearch] =
|
|
11234
|
-
const [sessFilter, setSessFilter] =
|
|
11235
|
-
const [page, setPage] =
|
|
11236
|
-
const [sel, setSel] =
|
|
11237
|
-
const [detailScroll, setDetailScroll] =
|
|
11238
|
-
const [editing, setEditing] =
|
|
11239
|
-
const [si, setSi] =
|
|
11240
|
-
const [sessSearch, setSessSearch] =
|
|
11241
|
-
const [sessSel, setSessSel] =
|
|
11242
|
-
const [msg, setMsg] =
|
|
11243
|
-
const [showHelp, setShowHelp] =
|
|
11244
|
-
const [frozen, setFrozen] =
|
|
11999
|
+
const [source, setSource] = useState9("cloud");
|
|
12000
|
+
const [view, setView] = useState9("logs");
|
|
12001
|
+
const [di, setDi] = useState9(0);
|
|
12002
|
+
const [gi, setGi] = useState9(0);
|
|
12003
|
+
const [tool, setTool] = useState9("");
|
|
12004
|
+
const [agent, setAgent] = useState9("");
|
|
12005
|
+
const [search, setSearch] = useState9("");
|
|
12006
|
+
const [sessFilter, setSessFilter] = useState9("");
|
|
12007
|
+
const [page, setPage] = useState9(0);
|
|
12008
|
+
const [sel, setSel] = useState9(0);
|
|
12009
|
+
const [detailScroll, setDetailScroll] = useState9(0);
|
|
12010
|
+
const [editing, setEditing] = useState9(null);
|
|
12011
|
+
const [si, setSi] = useState9(0);
|
|
12012
|
+
const [sessSearch, setSessSearch] = useState9("");
|
|
12013
|
+
const [sessSel, setSessSel] = useState9(0);
|
|
12014
|
+
const [msg, setMsg] = useState9(null);
|
|
12015
|
+
const [showHelp, setShowHelp] = useState9(false);
|
|
12016
|
+
const [frozen, setFrozen] = useState9(false);
|
|
11245
12017
|
const toTop = () => setSel(0);
|
|
11246
12018
|
const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
|
|
11247
12019
|
usePoll(statsQ.reloadQuiet, 15e3, active2 && source === "cloud" && !frozen);
|
|
@@ -11337,7 +12109,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
11337
12109
|
};
|
|
11338
12110
|
run12().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" }));
|
|
11339
12111
|
};
|
|
11340
|
-
|
|
12112
|
+
useInput8(
|
|
11341
12113
|
(input, key) => {
|
|
11342
12114
|
if (input === " ") {
|
|
11343
12115
|
setFrozen((f) => !f);
|
|
@@ -11453,58 +12225,58 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
11453
12225
|
{ isActive: focused && !editing }
|
|
11454
12226
|
);
|
|
11455
12227
|
const localAll = localQ.data ?? [];
|
|
11456
|
-
const strip = source === "cloud" ? /* @__PURE__ */
|
|
11457
|
-
/* @__PURE__ */
|
|
11458
|
-
/* @__PURE__ */
|
|
11459
|
-
/* @__PURE__ */
|
|
12228
|
+
const strip = source === "cloud" ? /* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
|
|
12229
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, children: statsQ.data ? statsQ.data.total_calls : "\xB7\xB7\xB7\xB7" }),
|
|
12230
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " calls \xB7 " }),
|
|
12231
|
+
/* @__PURE__ */ jsxs9(Text9, { color: theme.ok, children: [
|
|
11460
12232
|
statsQ.data?.allowed ?? "\xB7\xB7\xB7",
|
|
11461
12233
|
" allow"
|
|
11462
12234
|
] }),
|
|
11463
|
-
/* @__PURE__ */
|
|
11464
|
-
/* @__PURE__ */
|
|
12235
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 " }),
|
|
12236
|
+
/* @__PURE__ */ jsxs9(Text9, { color: theme.bad, children: [
|
|
11465
12237
|
statsQ.data?.denied ?? "\xB7\xB7",
|
|
11466
12238
|
" deny"
|
|
11467
12239
|
] }),
|
|
11468
|
-
/* @__PURE__ */
|
|
11469
|
-
] }) : /* @__PURE__ */
|
|
11470
|
-
/* @__PURE__ */
|
|
11471
|
-
/* @__PURE__ */
|
|
11472
|
-
/* @__PURE__ */
|
|
12240
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: ` \xB7 ${statsQ.data?.active_policies ?? "\xB7"} policies` })
|
|
12241
|
+
] }) : /* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
|
|
12242
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, children: localAll.length }),
|
|
12243
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " calls in local file \xB7 " }),
|
|
12244
|
+
/* @__PURE__ */ jsxs9(Text9, { color: theme.ok, children: [
|
|
11473
12245
|
localAll.filter((r) => r.decision === "ALLOW").length,
|
|
11474
12246
|
" allow"
|
|
11475
12247
|
] }),
|
|
11476
|
-
/* @__PURE__ */
|
|
11477
|
-
/* @__PURE__ */
|
|
12248
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 " }),
|
|
12249
|
+
/* @__PURE__ */ jsxs9(Text9, { color: theme.bad, children: [
|
|
11478
12250
|
localAll.filter((r) => r.decision !== "ALLOW").length,
|
|
11479
12251
|
" deny"
|
|
11480
12252
|
] }),
|
|
11481
|
-
/* @__PURE__ */
|
|
12253
|
+
/* @__PURE__ */ jsxs9(Text9, { color: theme.dim, children: [
|
|
11482
12254
|
" \xB7 ",
|
|
11483
12255
|
localLogFile()
|
|
11484
12256
|
] })
|
|
11485
12257
|
] });
|
|
11486
|
-
const copyBanner = frozen ? /* @__PURE__ */
|
|
11487
|
-
const srcChip = /* @__PURE__ */
|
|
11488
|
-
/* @__PURE__ */
|
|
11489
|
-
/* @__PURE__ */
|
|
11490
|
-
/* @__PURE__ */
|
|
12258
|
+
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;
|
|
12259
|
+
const srcChip = /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
12260
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "src:" }),
|
|
12261
|
+
/* @__PURE__ */ jsx9(Text9, { color: source === "local" ? theme.ok : "#4f6db8", bold: true, children: source }),
|
|
12262
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " (s) " })
|
|
11491
12263
|
] });
|
|
11492
12264
|
if (showHelp) {
|
|
11493
|
-
const colOf = (
|
|
11494
|
-
/* @__PURE__ */
|
|
11495
|
-
keys.map(([k, desc]) => /* @__PURE__ */
|
|
11496
|
-
/* @__PURE__ */
|
|
11497
|
-
/* @__PURE__ */
|
|
12265
|
+
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: [
|
|
12266
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accent, children: group }),
|
|
12267
|
+
keys.map(([k, desc]) => /* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
|
|
12268
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, children: (" " + k).padEnd(19) }),
|
|
12269
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: desc })
|
|
11498
12270
|
] }, k))
|
|
11499
12271
|
] }, group)) });
|
|
11500
|
-
return /* @__PURE__ */
|
|
12272
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
11501
12273
|
copyBanner,
|
|
11502
|
-
/* @__PURE__ */
|
|
12274
|
+
/* @__PURE__ */ jsxs9(Text9, { bold: true, color: theme.accentBright, children: [
|
|
11503
12275
|
"AUDIT \u2014 all keys",
|
|
11504
12276
|
" ",
|
|
11505
|
-
/* @__PURE__ */
|
|
12277
|
+
/* @__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)" })
|
|
11506
12278
|
] }),
|
|
11507
|
-
/* @__PURE__ */
|
|
12279
|
+
/* @__PURE__ */ jsxs9(Box9, { marginTop: 1, children: [
|
|
11508
12280
|
colOf(AUDIT_HELP.slice(0, 1)),
|
|
11509
12281
|
colOf(AUDIT_HELP.slice(1))
|
|
11510
12282
|
] })
|
|
@@ -11523,53 +12295,53 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
11523
12295
|
const maxScroll = Math.max(0, contentLines.length - bodyRows);
|
|
11524
12296
|
const off = Math.min(detailScroll, maxScroll);
|
|
11525
12297
|
const win = contentLines.slice(off, off + bodyRows);
|
|
11526
|
-
return /* @__PURE__ */
|
|
12298
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
11527
12299
|
copyBanner,
|
|
11528
|
-
/* @__PURE__ */
|
|
11529
|
-
/* @__PURE__ */
|
|
11530
|
-
/* @__PURE__ */
|
|
11531
|
-
/* @__PURE__ */
|
|
11532
|
-
/* @__PURE__ */
|
|
11533
|
-
e.dlp.length ? /* @__PURE__ */
|
|
11534
|
-
e.burst ? /* @__PURE__ */
|
|
11535
|
-
/* @__PURE__ */
|
|
12300
|
+
/* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
|
|
12301
|
+
/* @__PURE__ */ jsx9(Text9, { backgroundColor: BG2, color: "white", bold: true, children: " ENTRY " }),
|
|
12302
|
+
/* @__PURE__ */ jsx9(Text9, { color: decisionColor(e.decision), bold: true, children: " " + e.decision }),
|
|
12303
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accent, bold: true, children: " " + e.tool }),
|
|
12304
|
+
/* @__PURE__ */ jsx9(Text9, { color: loc ? theme.ok : "white", children: " " + (loc ? "LOC" : "CLD") }),
|
|
12305
|
+
e.dlp.length ? /* @__PURE__ */ jsx9(Text9, { color: theme.bad, children: " DLP!" }) : null,
|
|
12306
|
+
e.burst ? /* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: " BURST" }) : null,
|
|
12307
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2190 back" })
|
|
11536
12308
|
] }),
|
|
11537
|
-
/* @__PURE__ */
|
|
11538
|
-
/* @__PURE__ */
|
|
11539
|
-
/* @__PURE__ */
|
|
11540
|
-
/* @__PURE__ */
|
|
11541
|
-
/* @__PURE__ */
|
|
11542
|
-
/* @__PURE__ */
|
|
11543
|
-
/* @__PURE__ */
|
|
11544
|
-
/* @__PURE__ */
|
|
11545
|
-
/* @__PURE__ */
|
|
11546
|
-
/* @__PURE__ */
|
|
11547
|
-
/* @__PURE__ */
|
|
11548
|
-
/* @__PURE__ */
|
|
12309
|
+
/* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
|
|
12310
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "\u2502 when " }),
|
|
12311
|
+
/* @__PURE__ */ jsx9(Text9, { children: new Date(e.at).toLocaleString() }),
|
|
12312
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2502 perm " }),
|
|
12313
|
+
/* @__PURE__ */ jsx9(Text9, { children: e.permission || "\u2014" }),
|
|
12314
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2502 trust " }),
|
|
12315
|
+
/* @__PURE__ */ jsx9(Text9, { children: e.trust || "\u2014" }),
|
|
12316
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2502 eval " }),
|
|
12317
|
+
/* @__PURE__ */ jsx9(Text9, { color: e.evalMs != null && e.evalMs > 500 ? theme.warn : void 0, children: e.evalMs != null ? `${e.evalMs}ms` : "\u2014" }),
|
|
12318
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2502 agent " }),
|
|
12319
|
+
/* @__PURE__ */ jsx9(Text9, { children: e.agent ?? "\u2014" }),
|
|
12320
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2502" })
|
|
11549
12321
|
] }),
|
|
11550
|
-
/* @__PURE__ */
|
|
11551
|
-
/* @__PURE__ */
|
|
11552
|
-
/* @__PURE__ */
|
|
11553
|
-
/* @__PURE__ */
|
|
11554
|
-
/* @__PURE__ */
|
|
11555
|
-
/* @__PURE__ */
|
|
12322
|
+
/* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
|
|
12323
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "\u2502 session " }),
|
|
12324
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: e.session ?? "\u2014" }),
|
|
12325
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2502 rule " }),
|
|
12326
|
+
/* @__PURE__ */ jsx9(Text9, { color: e.rule && e.decision !== "ALLOW" ? theme.bad : theme.dim, children: e.rule ?? "\u2014" }),
|
|
12327
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2502" })
|
|
11556
12328
|
] }),
|
|
11557
|
-
/* @__PURE__ */
|
|
11558
|
-
/* @__PURE__ */
|
|
11559
|
-
/* @__PURE__ */
|
|
11560
|
-
/* @__PURE__ */
|
|
11561
|
-
/* @__PURE__ */
|
|
11562
|
-
/* @__PURE__ */
|
|
12329
|
+
/* @__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 }),
|
|
12330
|
+
/* @__PURE__ */ jsx9(Box9, { flexDirection: "column", height: bodyRows, overflow: "hidden", children: win.map((l, i) => /* @__PURE__ */ jsx9(Text9, { wrap: "truncate", children: l || " " }, off + i)) }),
|
|
12331
|
+
/* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
|
|
12332
|
+
/* @__PURE__ */ jsx9(Text9, { backgroundColor: BG2, color: "white", bold: true, children: " ENTRY " }),
|
|
12333
|
+
/* @__PURE__ */ jsx9(Text9, { backgroundColor: "#0b1530", color: "white", children: ` ${e.id} ` }),
|
|
12334
|
+
/* @__PURE__ */ jsx9(Text9, { backgroundColor: BG2, color: "white", children: " \u2191\u2193 scroll \xB7 space copy \xB7 ? all keys \xB7 \u2190 back \xB7 esc menu " })
|
|
11563
12335
|
] })
|
|
11564
12336
|
] });
|
|
11565
12337
|
}
|
|
11566
|
-
const chip = (
|
|
11567
|
-
/* @__PURE__ */
|
|
11568
|
-
|
|
12338
|
+
const chip = (label2, val, on) => /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
12339
|
+
/* @__PURE__ */ jsxs9(Text9, { color: theme.dim, children: [
|
|
12340
|
+
label2,
|
|
11569
12341
|
":"
|
|
11570
12342
|
] }),
|
|
11571
|
-
/* @__PURE__ */
|
|
11572
|
-
/* @__PURE__ */
|
|
12343
|
+
/* @__PURE__ */ jsx9(Text9, { color: on ? theme.accentBright : theme.dim, children: val }),
|
|
12344
|
+
/* @__PURE__ */ jsx9(Text9, { children: " " })
|
|
11573
12345
|
] });
|
|
11574
12346
|
if (view === "sessions") {
|
|
11575
12347
|
const headerRows2 = 5 + (editing ? 1 : 0);
|
|
@@ -11577,21 +12349,21 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
11577
12349
|
const selC = Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1));
|
|
11578
12350
|
const start2 = Math.min(Math.max(0, selC - Math.floor((listRows2 - 1) / 2)), Math.max(0, sessionsFiltered.length - listRows2));
|
|
11579
12351
|
const win = sessionsFiltered.slice(start2, start2 + listRows2);
|
|
11580
|
-
return /* @__PURE__ */
|
|
12352
|
+
return /* @__PURE__ */ jsx9(DataView, { loading: sessLoading && !frozen, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
11581
12353
|
strip,
|
|
11582
12354
|
copyBanner,
|
|
11583
|
-
/* @__PURE__ */
|
|
12355
|
+
/* @__PURE__ */ jsxs9(Box9, { children: [
|
|
11584
12356
|
srcChip,
|
|
11585
|
-
/* @__PURE__ */
|
|
11586
|
-
/* @__PURE__ */
|
|
12357
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, bold: true, children: "SESSIONS" }),
|
|
12358
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 logs (v) " }),
|
|
11587
12359
|
chip("status", SESS_STATUS[si] ?? "all", si !== 0),
|
|
11588
12360
|
chip("search", sessSearch || "\xB7", !!sessSearch)
|
|
11589
12361
|
] }),
|
|
11590
|
-
/* @__PURE__ */
|
|
11591
|
-
editing === "sess-search" ? /* @__PURE__ */
|
|
11592
|
-
/* @__PURE__ */
|
|
11593
|
-
/* @__PURE__ */
|
|
11594
|
-
|
|
12362
|
+
/* @__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" }),
|
|
12363
|
+
editing === "sess-search" ? /* @__PURE__ */ jsxs9(Box9, { children: [
|
|
12364
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: "search: " }),
|
|
12365
|
+
/* @__PURE__ */ jsx9(
|
|
12366
|
+
TextInput6,
|
|
11595
12367
|
{
|
|
11596
12368
|
value: sessSearch,
|
|
11597
12369
|
onChange: (v) => {
|
|
@@ -11602,8 +12374,8 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
11602
12374
|
}
|
|
11603
12375
|
)
|
|
11604
12376
|
] }) : null,
|
|
11605
|
-
/* @__PURE__ */
|
|
11606
|
-
/* @__PURE__ */
|
|
12377
|
+
/* @__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}` : ""}` }),
|
|
12378
|
+
/* @__PURE__ */ jsx9(
|
|
11607
12379
|
Table,
|
|
11608
12380
|
{
|
|
11609
12381
|
columns: [
|
|
@@ -11634,7 +12406,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
11634
12406
|
})
|
|
11635
12407
|
}
|
|
11636
12408
|
),
|
|
11637
|
-
sessionsFiltered.length === 0 ? /* @__PURE__ */
|
|
12409
|
+
sessionsFiltered.length === 0 ? /* @__PURE__ */ jsxs9(Text9, { color: theme.dim, children: [
|
|
11638
12410
|
"(no sessions",
|
|
11639
12411
|
source === "local" ? " in the local file" : "",
|
|
11640
12412
|
")"
|
|
@@ -11646,14 +12418,14 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
11646
12418
|
const selClamped = Math.min(sel, Math.max(0, pageRows.length - 1));
|
|
11647
12419
|
const maxStart = Math.max(0, pageRows.length - listRows);
|
|
11648
12420
|
const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
|
|
11649
|
-
const
|
|
11650
|
-
return /* @__PURE__ */
|
|
12421
|
+
const windowed2 = pageRows.slice(start, start + listRows);
|
|
12422
|
+
return /* @__PURE__ */ jsx9(DataView, { loading: logsLoading && !frozen, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
11651
12423
|
strip,
|
|
11652
12424
|
copyBanner,
|
|
11653
|
-
/* @__PURE__ */
|
|
12425
|
+
/* @__PURE__ */ jsxs9(Box9, { children: [
|
|
11654
12426
|
srcChip,
|
|
11655
|
-
/* @__PURE__ */
|
|
11656
|
-
/* @__PURE__ */
|
|
12427
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, bold: true, children: "LOGS" }),
|
|
12428
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 sessions (v) " }),
|
|
11657
12429
|
chip("dec", DECISIONS[di] ?? "all", di !== 0),
|
|
11658
12430
|
chip("sig", SIGNALS[gi] ?? "all", gi !== 0),
|
|
11659
12431
|
chip("tool", tool || "\xB7", !!tool),
|
|
@@ -11661,15 +12433,15 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
11661
12433
|
chip("search", search || "\xB7", !!search),
|
|
11662
12434
|
sessFilter ? chip("sess", sessFilter.slice(0, 8), true) : null
|
|
11663
12435
|
] }),
|
|
11664
|
-
/* @__PURE__ */
|
|
11665
|
-
msg ? /* @__PURE__ */
|
|
11666
|
-
editing && editing !== "sess-search" ? /* @__PURE__ */
|
|
11667
|
-
/* @__PURE__ */
|
|
12436
|
+
/* @__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" }),
|
|
12437
|
+
msg ? /* @__PURE__ */ jsx9(Text9, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : null,
|
|
12438
|
+
editing && editing !== "sess-search" ? /* @__PURE__ */ jsxs9(Box9, { children: [
|
|
12439
|
+
/* @__PURE__ */ jsxs9(Text9, { color: theme.warn, children: [
|
|
11668
12440
|
editing,
|
|
11669
12441
|
": "
|
|
11670
12442
|
] }),
|
|
11671
|
-
/* @__PURE__ */
|
|
11672
|
-
|
|
12443
|
+
/* @__PURE__ */ jsx9(
|
|
12444
|
+
TextInput6,
|
|
11673
12445
|
{
|
|
11674
12446
|
value: editing === "tool" ? tool : editing === "agent" ? agent : search,
|
|
11675
12447
|
onChange: (v) => {
|
|
@@ -11681,9 +12453,9 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
11681
12453
|
}
|
|
11682
12454
|
)
|
|
11683
12455
|
] }) : null,
|
|
11684
|
-
/* @__PURE__ */
|
|
11685
|
-
/* @__PURE__ */
|
|
11686
|
-
pageRows.length === 0 ? /* @__PURE__ */
|
|
12456
|
+
/* @__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` : ""}` }),
|
|
12457
|
+
/* @__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)) }),
|
|
12458
|
+
pageRows.length === 0 ? /* @__PURE__ */ jsxs9(Text9, { color: theme.dim, children: [
|
|
11687
12459
|
"(no entries",
|
|
11688
12460
|
source === "local" ? " in the local file" : "",
|
|
11689
12461
|
" \u2014 c clears filters)"
|
|
@@ -12064,10 +12836,10 @@ var init_doctor = __esm({
|
|
|
12064
12836
|
});
|
|
12065
12837
|
|
|
12066
12838
|
// src/tui/panels/Settings.tsx
|
|
12067
|
-
import { Box as
|
|
12068
|
-
import
|
|
12069
|
-
import { useEffect as
|
|
12070
|
-
import { Fragment as
|
|
12839
|
+
import { Box as Box10, Text as Text10, useInput as useInput9 } from "ink";
|
|
12840
|
+
import TextInput7 from "ink-text-input";
|
|
12841
|
+
import { useEffect as useEffect8, useRef as useRef4, useState as useState10 } from "react";
|
|
12842
|
+
import { Fragment as Fragment6, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
12071
12843
|
function SettingsPanel({
|
|
12072
12844
|
active: active2,
|
|
12073
12845
|
focused,
|
|
@@ -12077,19 +12849,19 @@ function SettingsPanel({
|
|
|
12077
12849
|
}) {
|
|
12078
12850
|
void active2;
|
|
12079
12851
|
const { cols, rows } = usePanelSize();
|
|
12080
|
-
const [sel, setSel] =
|
|
12081
|
-
const [editing, setEditing] =
|
|
12082
|
-
const [input, setInput] =
|
|
12083
|
-
const [confirmDel, setConfirmDel] =
|
|
12084
|
-
const [msg, setMsg] =
|
|
12085
|
-
const [busy, setBusy] =
|
|
12086
|
-
const [editor, setEditor] =
|
|
12087
|
-
const [latest, setLatest] =
|
|
12088
|
-
const [diagBusy, setDiagBusy] =
|
|
12089
|
-
const [autoUp, setAutoUp] =
|
|
12090
|
-
const [updBusy, setUpdBusy] =
|
|
12091
|
-
const [llSetting, setLlSetting] =
|
|
12092
|
-
|
|
12852
|
+
const [sel, setSel] = useState10(0);
|
|
12853
|
+
const [editing, setEditing] = useState10(null);
|
|
12854
|
+
const [input, setInput] = useState10("");
|
|
12855
|
+
const [confirmDel, setConfirmDel] = useState10(null);
|
|
12856
|
+
const [msg, setMsg] = useState10(null);
|
|
12857
|
+
const [busy, setBusy] = useState10(false);
|
|
12858
|
+
const [editor, setEditor] = useState10(null);
|
|
12859
|
+
const [latest, setLatest] = useState10(null);
|
|
12860
|
+
const [diagBusy, setDiagBusy] = useState10(null);
|
|
12861
|
+
const [autoUp, setAutoUp] = useState10(() => autoUpdateEnabled());
|
|
12862
|
+
const [updBusy, setUpdBusy] = useState10(false);
|
|
12863
|
+
const [llSetting, setLlSetting] = useState10(null);
|
|
12864
|
+
useEffect8(() => {
|
|
12093
12865
|
const read = () => {
|
|
12094
12866
|
try {
|
|
12095
12867
|
setLlSetting(localLogsSetting());
|
|
@@ -12100,8 +12872,8 @@ function SettingsPanel({
|
|
|
12100
12872
|
const t = setInterval(read, 5e3);
|
|
12101
12873
|
return () => clearInterval(t);
|
|
12102
12874
|
}, []);
|
|
12103
|
-
const [canInstall, setCanInstall] =
|
|
12104
|
-
|
|
12875
|
+
const [canInstall, setCanInstall] = useState10(null);
|
|
12876
|
+
useEffect8(() => {
|
|
12105
12877
|
let live2 = true;
|
|
12106
12878
|
globalInstallCheck().then((r) => {
|
|
12107
12879
|
if (live2) setCanInstall(r.writable);
|
|
@@ -12111,8 +12883,8 @@ function SettingsPanel({
|
|
|
12111
12883
|
live2 = false;
|
|
12112
12884
|
};
|
|
12113
12885
|
}, []);
|
|
12114
|
-
const [diag, setDiag] =
|
|
12115
|
-
const [diagStep, setDiagStep] =
|
|
12886
|
+
const [diag, setDiag] = useState10(null);
|
|
12887
|
+
const [diagStep, setDiagStep] = useState10(0);
|
|
12116
12888
|
const DIAG_STEPS = {
|
|
12117
12889
|
doctor: ["login", "active policy", "guard hook", "agent hooks", "local logs"],
|
|
12118
12890
|
repair: ["inspecting protection files", "rewriting hook files", "registering with agents", "re-locking"]
|
|
@@ -12122,14 +12894,14 @@ function SettingsPanel({
|
|
|
12122
12894
|
const left = min - (Date.now() - started);
|
|
12123
12895
|
if (left > 0) await new Promise((res) => setTimeout(res, left));
|
|
12124
12896
|
};
|
|
12125
|
-
|
|
12897
|
+
useEffect8(() => {
|
|
12126
12898
|
if (!diagBusy) return;
|
|
12127
12899
|
const total = DIAG_STEPS[diagBusy].length;
|
|
12128
12900
|
const step = setInterval(() => setDiagStep((s) => Math.min(s + 1, total - 1)), 220);
|
|
12129
12901
|
return () => clearInterval(step);
|
|
12130
12902
|
}, [diagBusy]);
|
|
12131
12903
|
const ver = currentVersion();
|
|
12132
|
-
|
|
12904
|
+
useEffect8(() => {
|
|
12133
12905
|
let live2 = true;
|
|
12134
12906
|
latestVersion().then((v) => {
|
|
12135
12907
|
if (live2) setLatest(v);
|
|
@@ -12139,20 +12911,20 @@ function SettingsPanel({
|
|
|
12139
12911
|
live2 = false;
|
|
12140
12912
|
};
|
|
12141
12913
|
}, []);
|
|
12142
|
-
const [accounts, setAccounts] =
|
|
12143
|
-
const [login, setLogin] =
|
|
12144
|
-
const [tick, setTick] =
|
|
12145
|
-
const abort =
|
|
12914
|
+
const [accounts, setAccounts] = useState10(() => listAccounts());
|
|
12915
|
+
const [login, setLogin] = useState10(null);
|
|
12916
|
+
const [tick, setTick] = useState10(0);
|
|
12917
|
+
const abort = useRef4(false);
|
|
12146
12918
|
const refreshAccounts = () => setAccounts(listAccounts());
|
|
12147
|
-
const [guardHere, setGuardHere] =
|
|
12148
|
-
const [guardVer, setGuardVer] =
|
|
12149
|
-
const [guardOld, setGuardOld] =
|
|
12919
|
+
const [guardHere, setGuardHere] = useState10(() => isGuardInstalled());
|
|
12920
|
+
const [guardVer, setGuardVer] = useState10(() => installedGuardVersion());
|
|
12921
|
+
const [guardOld, setGuardOld] = useState10(() => guardHookOutdated());
|
|
12150
12922
|
const codexNeedsTrust = () => {
|
|
12151
12923
|
if (!codexDetected()) return false;
|
|
12152
12924
|
const cx = codexHooksStatus();
|
|
12153
12925
|
return cx.registered && !cx.trusted;
|
|
12154
12926
|
};
|
|
12155
|
-
const [codexTrust, setCodexTrust] =
|
|
12927
|
+
const [codexTrust, setCodexTrust] = useState10(codexNeedsTrust);
|
|
12156
12928
|
const refreshGuard = () => {
|
|
12157
12929
|
setGuardHere(isGuardInstalled());
|
|
12158
12930
|
setGuardVer(installedGuardVersion());
|
|
@@ -12160,12 +12932,12 @@ function SettingsPanel({
|
|
|
12160
12932
|
setCodexTrust(codexNeedsTrust());
|
|
12161
12933
|
};
|
|
12162
12934
|
const locked = accounts.length === 0;
|
|
12163
|
-
const [srv, setSrv] =
|
|
12164
|
-
|
|
12935
|
+
const [srv, setSrv] = useState10(() => logsServerStatus());
|
|
12936
|
+
useEffect8(() => {
|
|
12165
12937
|
const t = setInterval(() => setSrv(logsServerStatus()), 3e3);
|
|
12166
12938
|
return () => clearInterval(t);
|
|
12167
12939
|
}, []);
|
|
12168
|
-
|
|
12940
|
+
useEffect8(() => {
|
|
12169
12941
|
const loggingIn = login && login.phase !== "done" && login.phase !== "error";
|
|
12170
12942
|
if (!loggingIn && !diagBusy && !updBusy) return;
|
|
12171
12943
|
const t = setInterval(() => setTick((n) => n + 1), 120);
|
|
@@ -12178,8 +12950,8 @@ function SettingsPanel({
|
|
|
12178
12950
|
const selfQ = useLoader(() => listAccounts().length ? api.settings.getSelfProtection() : Promise.resolve(null));
|
|
12179
12951
|
const local = localQ.data;
|
|
12180
12952
|
const selfProt = selfQ.data;
|
|
12181
|
-
const autoInstalledRef =
|
|
12182
|
-
|
|
12953
|
+
const autoInstalledRef = useRef4(false);
|
|
12954
|
+
useEffect8(() => {
|
|
12183
12955
|
if (autoInstalledRef.current) return;
|
|
12184
12956
|
if (guardHere && guardOld) {
|
|
12185
12957
|
autoInstalledRef.current = true;
|
|
@@ -12191,7 +12963,7 @@ function SettingsPanel({
|
|
|
12191
12963
|
}
|
|
12192
12964
|
}
|
|
12193
12965
|
}, [guardHere, guardOld]);
|
|
12194
|
-
const [hidden, setHidden] =
|
|
12966
|
+
const [hidden, setHidden] = useState10(/* @__PURE__ */ new Set());
|
|
12195
12967
|
const webhooks = (whQ.data?.webhooks ?? []).filter((w) => !hidden.has("wh:" + w.id));
|
|
12196
12968
|
const alerts = (alertQ.data?.rules ?? []).filter((r) => !hidden.has("alert:" + r.id));
|
|
12197
12969
|
const hasEmailAlert = alerts.some((r) => alertChannel(r) === "email");
|
|
@@ -12227,12 +12999,12 @@ function SettingsPanel({
|
|
|
12227
12999
|
guardQ.reload();
|
|
12228
13000
|
selfQ.reload();
|
|
12229
13001
|
};
|
|
12230
|
-
const run12 = (
|
|
13002
|
+
const run12 = (label2, fn, reload) => {
|
|
12231
13003
|
if (busy) return;
|
|
12232
13004
|
setBusy(true);
|
|
12233
|
-
setMsg({ text:
|
|
13005
|
+
setMsg({ text: label2 + "\u2026", level: "ok" });
|
|
12234
13006
|
fn().then(() => {
|
|
12235
|
-
setMsg({ text: "\u2713 " +
|
|
13007
|
+
setMsg({ text: "\u2713 " + label2, level: "ok" });
|
|
12236
13008
|
reload();
|
|
12237
13009
|
}).catch((e) => setMsg({ text: "\u2717 " + (e instanceof Error ? e.message : String(e)), level: "bad" })).finally(() => setBusy(false));
|
|
12238
13010
|
};
|
|
@@ -12474,7 +13246,7 @@ function SettingsPanel({
|
|
|
12474
13246
|
setEditor({ ...editor, target: v, field: 1 });
|
|
12475
13247
|
}
|
|
12476
13248
|
};
|
|
12477
|
-
|
|
13249
|
+
useInput9(
|
|
12478
13250
|
(inp, key) => {
|
|
12479
13251
|
if (login && (login.phase === "starting" || login.phase === "waiting")) {
|
|
12480
13252
|
if (key.escape) {
|
|
@@ -12609,58 +13381,58 @@ function SettingsPanel({
|
|
|
12609
13381
|
);
|
|
12610
13382
|
const spin = SPIN2[tick % SPIN2.length];
|
|
12611
13383
|
if (login && (login.phase === "starting" || login.phase === "waiting")) {
|
|
12612
|
-
return /* @__PURE__ */
|
|
12613
|
-
/* @__PURE__ */
|
|
12614
|
-
login.phase === "starting" ? /* @__PURE__ */
|
|
13384
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
|
|
13385
|
+
/* @__PURE__ */ jsx10(Text10, { bold: true, color: theme.accentBright, children: "Add an account" }),
|
|
13386
|
+
login.phase === "starting" ? /* @__PURE__ */ jsxs10(Text10, { color: theme.dim, children: [
|
|
12615
13387
|
spin,
|
|
12616
13388
|
" starting device pairing\u2026"
|
|
12617
|
-
] }) : /* @__PURE__ */
|
|
12618
|
-
/* @__PURE__ */
|
|
12619
|
-
/* @__PURE__ */
|
|
12620
|
-
/* @__PURE__ */
|
|
12621
|
-
/* @__PURE__ */
|
|
13389
|
+
] }) : /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", marginTop: 1, children: [
|
|
13390
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "Opening your browser to authorize. If it didn't open, visit:" }),
|
|
13391
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.accentBright, bold: true, wrap: "truncate", children: login.url }),
|
|
13392
|
+
/* @__PURE__ */ jsxs10(Box10, { marginTop: 1, children: [
|
|
13393
|
+
/* @__PURE__ */ jsxs10(Text10, { color: theme.warn, children: [
|
|
12622
13394
|
spin,
|
|
12623
13395
|
" waiting for authorization\u2026 "
|
|
12624
13396
|
] }),
|
|
12625
|
-
/* @__PURE__ */
|
|
13397
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "esc to cancel" })
|
|
12626
13398
|
] })
|
|
12627
13399
|
] })
|
|
12628
13400
|
] });
|
|
12629
13401
|
}
|
|
12630
13402
|
if (editor) {
|
|
12631
|
-
const fieldRow = (idx,
|
|
12632
|
-
/* @__PURE__ */
|
|
12633
|
-
/* @__PURE__ */
|
|
13403
|
+
const fieldRow = (idx, label2, value, hint) => /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
13404
|
+
/* @__PURE__ */ jsx10(Text10, { color: editor.field === idx ? theme.accentBright : theme.dim, children: editor.field === idx ? "\u25B8 " : " " }),
|
|
13405
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: label2.padEnd(11) }),
|
|
12634
13406
|
value,
|
|
12635
|
-
editor.field === idx && hint ? /* @__PURE__ */
|
|
13407
|
+
editor.field === idx && hint ? /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " " + hint }) : null
|
|
12636
13408
|
] });
|
|
12637
|
-
return /* @__PURE__ */
|
|
12638
|
-
/* @__PURE__ */
|
|
13409
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
|
|
13410
|
+
/* @__PURE__ */ jsxs10(Text10, { bold: true, color: theme.accentBright, children: [
|
|
12639
13411
|
editor.id ? "Edit" : "New",
|
|
12640
13412
|
" ",
|
|
12641
13413
|
editor.channel,
|
|
12642
13414
|
" alert"
|
|
12643
13415
|
] }),
|
|
12644
|
-
/* @__PURE__ */
|
|
12645
|
-
editing === "alert-target" ? /* @__PURE__ */
|
|
12646
|
-
/* @__PURE__ */
|
|
12647
|
-
/* @__PURE__ */
|
|
12648
|
-
] }) : msg ? /* @__PURE__ */
|
|
12649
|
-
/* @__PURE__ */
|
|
12650
|
-
fieldRow(0, "target", editor.target ? /* @__PURE__ */
|
|
12651
|
-
fieldRow(1, "signal", /* @__PURE__ */
|
|
12652
|
-
fieldRow(2, "threshold", /* @__PURE__ */
|
|
12653
|
-
fieldRow(3, "window", /* @__PURE__ */
|
|
12654
|
-
editor.id ? fieldRow(4, "status", editor.enabled ? /* @__PURE__ */
|
|
13416
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "\u2191\u2193 field \xB7 \u2190\u2192 change \xB7 e edit target \xB7 enter save \xB7 esc cancel" }),
|
|
13417
|
+
editing === "alert-target" ? /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, children: [
|
|
13418
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.warn, children: editor.channel === "email" ? "e-mail address: " : "telegram chat id (from @userinfobot): " }),
|
|
13419
|
+
/* @__PURE__ */ jsx10(TextInput7, { value: input, onChange: setInput, onSubmit: submitInput })
|
|
13420
|
+
] }) : msg ? /* @__PURE__ */ jsx10(Text10, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : /* @__PURE__ */ jsx10(Text10, { children: " " }),
|
|
13421
|
+
/* @__PURE__ */ jsxs10(Box10, { marginTop: 1, flexDirection: "column", children: [
|
|
13422
|
+
fieldRow(0, "target", editor.target ? /* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: truncate2(editor.target, cols - 16) }) : /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "empty \u2014 press e" }), "press e to edit"),
|
|
13423
|
+
fieldRow(1, "signal", /* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: SIGNAL_LABEL[editor.signal] }), "\u2190\u2192 change"),
|
|
13424
|
+
fieldRow(2, "threshold", /* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: `\u2265 ${editor.threshold}` }), "\u2190\u2192 \xB15"),
|
|
13425
|
+
fieldRow(3, "window", /* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: winLabel(editor.windowSeconds) }), "\u2190\u2192 change"),
|
|
13426
|
+
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
|
|
12655
13427
|
] }),
|
|
12656
|
-
/* @__PURE__ */
|
|
13428
|
+
/* @__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: [
|
|
12657
13429
|
"fires when ",
|
|
12658
|
-
/* @__PURE__ */
|
|
13430
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: SIGNAL_LABEL[editor.signal] }),
|
|
12659
13431
|
" reach ",
|
|
12660
|
-
/* @__PURE__ */
|
|
13432
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: editor.threshold }),
|
|
12661
13433
|
" within",
|
|
12662
13434
|
" ",
|
|
12663
|
-
/* @__PURE__ */
|
|
13435
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: winLabel(editor.windowSeconds) })
|
|
12664
13436
|
] }) })
|
|
12665
13437
|
] });
|
|
12666
13438
|
}
|
|
@@ -12669,140 +13441,140 @@ function SettingsPanel({
|
|
|
12669
13441
|
const authError = rawError && /invalid api key|authentication|401|unauthor|not logged in/i.test(rawError) ? rawError : null;
|
|
12670
13442
|
const noKey = !!authError && /not logged in/i.test(authError);
|
|
12671
13443
|
const error = authError ? null : rawError;
|
|
12672
|
-
const onOff = (on) => on ? /* @__PURE__ */
|
|
13444
|
+
const onOff = (on) => on ? /* @__PURE__ */ jsx10(Text10, { color: theme.ok, children: "on " }) : /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "off" });
|
|
12673
13445
|
const chanOf = (rule) => [...rule.emails ?? [], ...(rule.telegram ?? []).map((t) => "tg " + t)].join(", ") || "\u2014";
|
|
12674
13446
|
const isCur = (r) => keyOf(r) === keyOf(cur) && focused;
|
|
12675
|
-
const cursor = (r) => /* @__PURE__ */
|
|
13447
|
+
const cursor = (r) => /* @__PURE__ */ jsx10(Text10, { color: isCur(r) ? theme.accentBright : theme.dim, children: isCur(r) ? "\u25B8 " : " " });
|
|
12676
13448
|
const rowLine = (r) => {
|
|
12677
13449
|
switch (r.kind) {
|
|
12678
13450
|
case "acct": {
|
|
12679
13451
|
const a = r.acc;
|
|
12680
13452
|
const isView = viewApiKey ? a.apiKey === viewApiKey : accounts[0]?.apiKey === a.apiKey;
|
|
12681
13453
|
const isActive = isActiveAccount(a.apiKey);
|
|
12682
|
-
return /* @__PURE__ */
|
|
13454
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
12683
13455
|
cursor(r),
|
|
12684
|
-
/* @__PURE__ */
|
|
12685
|
-
isView ? /* @__PURE__ */
|
|
12686
|
-
isActive ? /* @__PURE__ */
|
|
12687
|
-
/* @__PURE__ */
|
|
13456
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: truncate2(acctLabel(a), 30).padEnd(31) }),
|
|
13457
|
+
isView ? /* @__PURE__ */ jsx10(Text10, { color: theme.ok, children: "\u25CF viewing " }) : /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " " }),
|
|
13458
|
+
isActive ? /* @__PURE__ */ jsx10(Text10, { color: theme.warn, children: "ACTIVE " }) : /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " " }),
|
|
13459
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: a.project ? truncate2(a.project, 20) : "" })
|
|
12688
13460
|
] });
|
|
12689
13461
|
}
|
|
12690
13462
|
case "acct-add":
|
|
12691
|
-
return /* @__PURE__ */
|
|
13463
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
12692
13464
|
cursor(r),
|
|
12693
|
-
/* @__PURE__ */
|
|
13465
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "+ add account (enter \u2014 device login in the browser)" })
|
|
12694
13466
|
] });
|
|
12695
13467
|
case "guard":
|
|
12696
|
-
return /* @__PURE__ */
|
|
13468
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
12697
13469
|
cursor(r),
|
|
12698
|
-
/* @__PURE__ */
|
|
12699
|
-
!guardHere ? /* @__PURE__ */
|
|
12700
|
-
/* @__PURE__ */
|
|
12701
|
-
/* @__PURE__ */
|
|
12702
|
-
/* @__PURE__ */
|
|
12703
|
-
] }) : /* @__PURE__ */
|
|
12704
|
-
/* @__PURE__ */
|
|
12705
|
-
/* @__PURE__ */
|
|
12706
|
-
/* @__PURE__ */
|
|
12707
|
-
codexTrust ? /* @__PURE__ */
|
|
13470
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "guard".padEnd(11) }),
|
|
13471
|
+
!guardHere ? /* @__PURE__ */ jsxs10(Fragment6, { children: [
|
|
13472
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.bad, bold: true, children: "removed" }),
|
|
13473
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " \xB7 " }),
|
|
13474
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.accentBright, children: "enter to install" })
|
|
13475
|
+
] }) : /* @__PURE__ */ jsxs10(Fragment6, { children: [
|
|
13476
|
+
/* @__PURE__ */ jsx10(Text10, { color: guardOld ? theme.warn : theme.ok, children: `v${guardVer ?? "?"}` }),
|
|
13477
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: guardOld ? " \xB7 update available \xB7 enter update" : " (latest) \xB7 enter reinstall" }),
|
|
13478
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " \xB7 d remove" }),
|
|
13479
|
+
codexTrust ? /* @__PURE__ */ jsx10(Text10, { color: theme.warn, children: " \xB7 codex: run /hooks once to trust" }) : null
|
|
12708
13480
|
] })
|
|
12709
13481
|
] });
|
|
12710
13482
|
case "self":
|
|
12711
|
-
return /* @__PURE__ */
|
|
13483
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
12712
13484
|
cursor(r),
|
|
12713
|
-
/* @__PURE__ */
|
|
12714
|
-
selfProt ? onOff(selfProt.enabled) : /* @__PURE__ */
|
|
12715
|
-
/* @__PURE__ */
|
|
13485
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "self-prot".padEnd(11) }),
|
|
13486
|
+
selfProt ? onOff(selfProt.enabled) : /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "\u2026" }),
|
|
13487
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " blocks agents editing SolonGate\u2019s own hooks/config \xB7 enter toggles" })
|
|
12716
13488
|
] });
|
|
12717
13489
|
case "doctor":
|
|
12718
|
-
return /* @__PURE__ */
|
|
13490
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
12719
13491
|
cursor(r),
|
|
12720
|
-
/* @__PURE__ */
|
|
12721
|
-
diagBusy === "doctor" ? /* @__PURE__ */
|
|
13492
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "doctor".padEnd(11) }),
|
|
13493
|
+
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" })
|
|
12722
13494
|
] });
|
|
12723
13495
|
case "repair":
|
|
12724
|
-
return /* @__PURE__ */
|
|
13496
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
12725
13497
|
cursor(r),
|
|
12726
|
-
/* @__PURE__ */
|
|
12727
|
-
diagBusy === "repair" ? /* @__PURE__ */
|
|
13498
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "repair".padEnd(11) }),
|
|
13499
|
+
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" })
|
|
12728
13500
|
] });
|
|
12729
13501
|
case "cli-update": {
|
|
12730
13502
|
const behind = latest ? newerThan(latest, ver) : false;
|
|
12731
|
-
return /* @__PURE__ */
|
|
13503
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
12732
13504
|
cursor(r),
|
|
12733
|
-
/* @__PURE__ */
|
|
12734
|
-
/* @__PURE__ */
|
|
12735
|
-
updBusy ? /* @__PURE__ */
|
|
13505
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "version".padEnd(11) }),
|
|
13506
|
+
/* @__PURE__ */ jsx10(Text10, { color: behind ? theme.warn : theme.ok, children: `v${ver}` }),
|
|
13507
|
+
updBusy ? /* @__PURE__ */ jsx10(Text10, { color: theme.accentBright, children: ` ${spin} updating\u2026` }) : behind && canInstall === false ? (
|
|
12736
13508
|
// Said before they press enter, not after a minute of npm noise.
|
|
12737
|
-
/* @__PURE__ */
|
|
12738
|
-
) : behind ? /* @__PURE__ */
|
|
13509
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.warn, children: ` v${latest} on npm \xB7 needs sudo here \xB7 enter shows how` })
|
|
13510
|
+
) : 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" })
|
|
12739
13511
|
] });
|
|
12740
13512
|
}
|
|
12741
13513
|
case "auto-update":
|
|
12742
|
-
return /* @__PURE__ */
|
|
13514
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
12743
13515
|
cursor(r),
|
|
12744
|
-
/* @__PURE__ */
|
|
13516
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "auto".padEnd(11) }),
|
|
12745
13517
|
onOff(autoUp),
|
|
12746
|
-
/* @__PURE__ */
|
|
13518
|
+
/* @__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" })
|
|
12747
13519
|
] });
|
|
12748
13520
|
case "ll-enabled":
|
|
12749
|
-
return /* @__PURE__ */
|
|
13521
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
12750
13522
|
cursor(r),
|
|
12751
|
-
/* @__PURE__ */
|
|
13523
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "enabled ".padEnd(11) }),
|
|
12752
13524
|
onOff(!!local?.enabled),
|
|
12753
|
-
/* @__PURE__ */
|
|
13525
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " enter toggles" })
|
|
12754
13526
|
] });
|
|
12755
13527
|
case "ll-path":
|
|
12756
|
-
return /* @__PURE__ */
|
|
13528
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
12757
13529
|
cursor(r),
|
|
12758
|
-
/* @__PURE__ */
|
|
13530
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "path ".padEnd(11) }),
|
|
12759
13531
|
local?.path ? (
|
|
12760
13532
|
// The folder is a PROJECT setting shared by every device on it, so
|
|
12761
13533
|
// it can name a path that only exists on another OS. The hooks then
|
|
12762
13534
|
// quietly fall back and this row was the last place still claiming
|
|
12763
13535
|
// the configured path was in use.
|
|
12764
|
-
llSetting && local.enabled && !llSetting.usableHere ? /* @__PURE__ */
|
|
12765
|
-
/* @__PURE__ */
|
|
12766
|
-
/* @__PURE__ */
|
|
12767
|
-
] }) : /* @__PURE__ */
|
|
12768
|
-
) : /* @__PURE__ */
|
|
13536
|
+
llSetting && local.enabled && !llSetting.usableHere ? /* @__PURE__ */ jsxs10(Fragment6, { children: [
|
|
13537
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.warn, children: truncate2(local.path, Math.max(12, Math.floor((cols - 20) / 2))) }),
|
|
13538
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: ` not valid here \u2192 ${truncate2(llSetting.file, Math.max(12, Math.floor((cols - 20) / 2)))}` })
|
|
13539
|
+
] }) : /* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: truncate2(local.path, cols - 14) })
|
|
13540
|
+
) : /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "not set \u2014 enter to edit" })
|
|
12769
13541
|
] });
|
|
12770
13542
|
case "ll-server":
|
|
12771
|
-
return /* @__PURE__ */
|
|
13543
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
12772
13544
|
cursor(r),
|
|
12773
|
-
/* @__PURE__ */
|
|
12774
|
-
srv.running ? /* @__PURE__ */
|
|
12775
|
-
/* @__PURE__ */
|
|
13545
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "dashboard".padEnd(11) }),
|
|
13546
|
+
srv.running ? /* @__PURE__ */ jsx10(Text10, { color: theme.ok, children: `running 127.0.0.1:${srv.port}` }) : /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "stopped" }),
|
|
13547
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: srv.running ? " survives closing the dataroom \xB7 enter stops" : " enter starts the dashboard local-logs link" })
|
|
12776
13548
|
] });
|
|
12777
13549
|
case "wh":
|
|
12778
|
-
return /* @__PURE__ */
|
|
13550
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
12779
13551
|
cursor(r),
|
|
12780
13552
|
onOff(r.wh.enabled),
|
|
12781
|
-
/* @__PURE__ */
|
|
12782
|
-
/* @__PURE__ */
|
|
13553
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.warn, children: (" " + r.wh.events).padEnd(9) }),
|
|
13554
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: truncate2(r.wh.url, cols - 16) })
|
|
12783
13555
|
] });
|
|
12784
13556
|
case "wh-add":
|
|
12785
|
-
return /* @__PURE__ */
|
|
13557
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
12786
13558
|
cursor(r),
|
|
12787
|
-
/* @__PURE__ */
|
|
13559
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "+ add webhook (enter)" })
|
|
12788
13560
|
] });
|
|
12789
13561
|
case "alert":
|
|
12790
|
-
return /* @__PURE__ */
|
|
13562
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
12791
13563
|
cursor(r),
|
|
12792
13564
|
onOff(r.rule.enabled),
|
|
12793
|
-
/* @__PURE__ */
|
|
12794
|
-
/* @__PURE__ */
|
|
12795
|
-
/* @__PURE__ */
|
|
13565
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.warn, children: (" " + SIGNAL_LABEL[r.rule.signal]).padEnd(13) }),
|
|
13566
|
+
/* @__PURE__ */ jsx10(Text10, { children: `\u2265${r.rule.threshold}/${winLabel(r.rule.windowSeconds)} `.padEnd(11) }),
|
|
13567
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: truncate2(chanOf(r.rule), cols - 34) })
|
|
12796
13568
|
] });
|
|
12797
13569
|
case "alert-add-email":
|
|
12798
|
-
return /* @__PURE__ */
|
|
13570
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
12799
13571
|
cursor(r),
|
|
12800
|
-
/* @__PURE__ */
|
|
13572
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "+ add email alert (enter)" })
|
|
12801
13573
|
] });
|
|
12802
13574
|
case "alert-add-tg":
|
|
12803
|
-
return /* @__PURE__ */
|
|
13575
|
+
return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
12804
13576
|
cursor(r),
|
|
12805
|
-
/* @__PURE__ */
|
|
13577
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "+ add telegram alert (enter \u2014 numeric chat id from @userinfobot)" })
|
|
12806
13578
|
] });
|
|
12807
13579
|
}
|
|
12808
13580
|
};
|
|
@@ -12822,31 +13594,31 @@ function SettingsPanel({
|
|
|
12822
13594
|
const sec = sectionOf(r);
|
|
12823
13595
|
if (sec !== lastSec) {
|
|
12824
13596
|
if (lastSec) {
|
|
12825
|
-
lineEls.push(/* @__PURE__ */
|
|
13597
|
+
lineEls.push(/* @__PURE__ */ jsx10(Text10, { children: " " }, "sp:" + sec));
|
|
12826
13598
|
lineKey.push("");
|
|
12827
13599
|
}
|
|
12828
13600
|
lastSec = sec;
|
|
12829
13601
|
lineEls.push(
|
|
12830
|
-
/* @__PURE__ */
|
|
12831
|
-
/* @__PURE__ */
|
|
12832
|
-
/* @__PURE__ */
|
|
13602
|
+
/* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
|
|
13603
|
+
/* @__PURE__ */ jsx10(Text10, { bold: true, color: theme.accentBright, children: sec }),
|
|
13604
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " \u2014 " + SECTION_DESC[sec] })
|
|
12833
13605
|
] }, "h:" + sec)
|
|
12834
13606
|
);
|
|
12835
13607
|
lineKey.push("");
|
|
12836
13608
|
if ((sec === "WEBHOOKS" || sec === "ALERTS") && local?.enabled) {
|
|
12837
13609
|
lineEls.push(
|
|
12838
|
-
/* @__PURE__ */
|
|
13610
|
+
/* @__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)
|
|
12839
13611
|
);
|
|
12840
13612
|
lineKey.push("");
|
|
12841
13613
|
}
|
|
12842
13614
|
}
|
|
12843
|
-
lineEls.push(/* @__PURE__ */
|
|
13615
|
+
lineEls.push(/* @__PURE__ */ jsx10(Box10, { children: rowLine(r) }, keyOf(r)));
|
|
12844
13616
|
lineKey.push(keyOf(r));
|
|
12845
13617
|
if ((r.kind === "doctor" || r.kind === "repair") && diagBusy === r.kind) {
|
|
12846
|
-
DIAG_STEPS[r.kind].forEach((
|
|
13618
|
+
DIAG_STEPS[r.kind].forEach((label2, i) => {
|
|
12847
13619
|
const done = i < diagStep;
|
|
12848
13620
|
lineEls.push(
|
|
12849
|
-
/* @__PURE__ */
|
|
13621
|
+
/* @__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}`)
|
|
12850
13622
|
);
|
|
12851
13623
|
lineKey.push("");
|
|
12852
13624
|
});
|
|
@@ -12854,8 +13626,8 @@ function SettingsPanel({
|
|
|
12854
13626
|
if (diag && (r.kind === "doctor" && diag.of === "doctor" || r.kind === "repair" && diag.of === "repair" || r.kind === "cli-update" && diag.of === "cli-update")) {
|
|
12855
13627
|
diag.lines.forEach((l, i) => {
|
|
12856
13628
|
lineEls.push(
|
|
12857
|
-
/* @__PURE__ */
|
|
12858
|
-
|
|
13629
|
+
/* @__PURE__ */ jsx10(
|
|
13630
|
+
Text10,
|
|
12859
13631
|
{
|
|
12860
13632
|
wrap: "truncate",
|
|
12861
13633
|
color: l.level === "ok" ? theme.ok : l.level === "warn" ? theme.warn : l.level === "bad" ? theme.bad : theme.dim,
|
|
@@ -12870,16 +13642,16 @@ function SettingsPanel({
|
|
|
12870
13642
|
});
|
|
12871
13643
|
if (hasEmailAlert && hasTgAlert) {
|
|
12872
13644
|
lineEls.push(
|
|
12873
|
-
/* @__PURE__ */
|
|
13645
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " both channels in use \u2014 remove one (d) to change it" }, "both-note")
|
|
12874
13646
|
);
|
|
12875
13647
|
lineKey.push("");
|
|
12876
13648
|
}
|
|
12877
|
-
lineEls.push(/* @__PURE__ */
|
|
13649
|
+
lineEls.push(/* @__PURE__ */ jsx10(Text10, { children: " " }, "ver-sp"));
|
|
12878
13650
|
lineKey.push("");
|
|
12879
13651
|
lineEls.push(
|
|
12880
|
-
/* @__PURE__ */
|
|
13652
|
+
/* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", color: theme.dim, children: [
|
|
12881
13653
|
`solongate v${ver}`,
|
|
12882
|
-
latest ? newerThan(latest, ver) ? /* @__PURE__ */
|
|
13654
|
+
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
|
|
12883
13655
|
] }, "ver")
|
|
12884
13656
|
);
|
|
12885
13657
|
lineKey.push("");
|
|
@@ -12890,17 +13662,17 @@ function SettingsPanel({
|
|
|
12890
13662
|
const win = lineEls.slice(startL, startL + budget);
|
|
12891
13663
|
const moreAbove = startL;
|
|
12892
13664
|
const moreBelow = Math.max(0, lineEls.length - (startL + budget));
|
|
12893
|
-
return /* @__PURE__ */
|
|
12894
|
-
/* @__PURE__ */
|
|
12895
|
-
authError ? /* @__PURE__ */
|
|
13665
|
+
return /* @__PURE__ */ jsx10(DataView, { loading, error, children: /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
|
|
13666
|
+
/* @__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" }),
|
|
13667
|
+
authError ? /* @__PURE__ */ jsx10(Text10, { wrap: "truncate", color: theme.bad, children: truncate2(
|
|
12896
13668
|
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",
|
|
12897
13669
|
cols
|
|
12898
13670
|
) }) : null,
|
|
12899
|
-
editing ? /* @__PURE__ */
|
|
12900
|
-
/* @__PURE__ */
|
|
12901
|
-
/* @__PURE__ */
|
|
12902
|
-
] }) : msg ? /* @__PURE__ */
|
|
12903
|
-
/* @__PURE__ */
|
|
13671
|
+
editing ? /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
13672
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.warn, children: editing === "path" ? "local log path: " : "webhook url: " }),
|
|
13673
|
+
/* @__PURE__ */ jsx10(TextInput7, { value: input, onChange: setInput, onSubmit: submitInput })
|
|
13674
|
+
] }) : msg ? /* @__PURE__ */ jsx10(Text10, { wrap: "truncate", color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(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: " " }),
|
|
13675
|
+
/* @__PURE__ */ jsx10(Box10, { flexDirection: "column", height: budget, overflow: "hidden", children: win })
|
|
12904
13676
|
] }) });
|
|
12905
13677
|
}
|
|
12906
13678
|
var EVENTS, SPIN2, SIGNALS2, SIGNAL_LABEL, WINDOWS, winLabel, THRESH_MIN, THRESH_MAX, nearestWindowIdx, editorFieldCount, acctLabel, alertChannel;
|
|
@@ -12938,17 +13710,29 @@ var init_Settings = __esm({
|
|
|
12938
13710
|
});
|
|
12939
13711
|
|
|
12940
13712
|
// src/tui/App.tsx
|
|
12941
|
-
import { Box as
|
|
12942
|
-
import { useEffect as
|
|
12943
|
-
import { jsx as
|
|
13713
|
+
import { Box as Box11, Text as Text11, useApp, useInput as useInput10 } from "ink";
|
|
13714
|
+
import { useEffect as useEffect9, useState as useState11 } from "react";
|
|
13715
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
12944
13716
|
function LiveHint() {
|
|
12945
|
-
return /* @__PURE__ */
|
|
12946
|
-
/* @__PURE__ */
|
|
12947
|
-
/* @__PURE__ */
|
|
12948
|
-
/* @__PURE__ */
|
|
12949
|
-
/* @__PURE__ */
|
|
13717
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
13718
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE \u2014 THIS MACHINE" }),
|
|
13719
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
|
|
13720
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
|
|
13721
|
+
/* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsxs11(Text11, { children: [
|
|
12950
13722
|
"press ",
|
|
12951
|
-
/* @__PURE__ */
|
|
13723
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.accent, children: "enter" }),
|
|
13724
|
+
" to go live"
|
|
13725
|
+
] }) })
|
|
13726
|
+
] });
|
|
13727
|
+
}
|
|
13728
|
+
function FleetLiveHint() {
|
|
13729
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
13730
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE \u2014 THE WHOLE FLEET" }),
|
|
13731
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.dim, children: "Every machine under your policy, as it happens: whose call it was," }),
|
|
13732
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.dim, children: "what was refused, and who is working right now." }),
|
|
13733
|
+
/* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsxs11(Text11, { children: [
|
|
13734
|
+
"press ",
|
|
13735
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.accent, children: "enter" }),
|
|
12952
13736
|
" to go live"
|
|
12953
13737
|
] }) })
|
|
12954
13738
|
] });
|
|
@@ -12956,28 +13740,28 @@ function LiveHint() {
|
|
|
12956
13740
|
function Banner({ cols }) {
|
|
12957
13741
|
const wide = cols >= 82;
|
|
12958
13742
|
if (!wide) {
|
|
12959
|
-
return /* @__PURE__ */
|
|
12960
|
-
/* @__PURE__ */
|
|
12961
|
-
/* @__PURE__ */
|
|
13743
|
+
return /* @__PURE__ */ jsxs11(Box11, { children: [
|
|
13744
|
+
/* @__PURE__ */ jsx11(Text11, { bold: true, color: theme.accentBright, children: "SolonGate" }),
|
|
13745
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.dim, children: " \u2014 security control center" })
|
|
12962
13746
|
] });
|
|
12963
13747
|
}
|
|
12964
|
-
return /* @__PURE__ */
|
|
12965
|
-
BANNER_FULL.map((line, i) => /* @__PURE__ */
|
|
12966
|
-
/* @__PURE__ */
|
|
13748
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
13749
|
+
BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx11(Text11, { bold: true, color: BANNER_HEX[i], children: line }, i)),
|
|
13750
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
|
|
12967
13751
|
] });
|
|
12968
13752
|
}
|
|
12969
13753
|
function App() {
|
|
12970
13754
|
const { exit } = useApp();
|
|
12971
|
-
const [accounts, setAccounts] =
|
|
12972
|
-
const [viewKey, setViewKey] =
|
|
12973
|
-
const [viewNonce, setViewNonce] =
|
|
12974
|
-
const [section, setSection] =
|
|
12975
|
-
const [focus, setFocus] =
|
|
12976
|
-
|
|
13755
|
+
const [accounts, setAccounts] = useState11(() => listAccounts());
|
|
13756
|
+
const [viewKey, setViewKey] = useState11(() => accounts[0]?.apiKey);
|
|
13757
|
+
const [viewNonce, setViewNonce] = useState11(0);
|
|
13758
|
+
const [section, setSection] = useState11(accounts.length === 0 ? SETTINGS_SECTION : 0);
|
|
13759
|
+
const [focus, setFocus] = useState11("nav");
|
|
13760
|
+
useEffect9(
|
|
12977
13761
|
() => onSectionRequest(() => {
|
|
12978
|
-
const
|
|
12979
|
-
if (!
|
|
12980
|
-
const idx = SECTIONS.findIndex((s) => s.label ===
|
|
13762
|
+
const label2 = takePendingSection();
|
|
13763
|
+
if (!label2) return;
|
|
13764
|
+
const idx = SECTIONS.findIndex((s) => s.label === label2);
|
|
12981
13765
|
if (idx >= 0) {
|
|
12982
13766
|
setSection(idx);
|
|
12983
13767
|
setFocus("panel");
|
|
@@ -12985,9 +13769,9 @@ function App() {
|
|
|
12985
13769
|
}),
|
|
12986
13770
|
[]
|
|
12987
13771
|
);
|
|
12988
|
-
const [help, setHelp] =
|
|
12989
|
-
const [update2, setUpdate] =
|
|
12990
|
-
|
|
13772
|
+
const [help, setHelp] = useState11(false);
|
|
13773
|
+
const [update2, setUpdate] = useState11({ kind: "idle" });
|
|
13774
|
+
useEffect9(() => {
|
|
12991
13775
|
let alive = true;
|
|
12992
13776
|
const run12 = () => void tuiUpdateFlow((s) => alive && setUpdate(s));
|
|
12993
13777
|
run12();
|
|
@@ -13001,7 +13785,7 @@ function App() {
|
|
|
13001
13785
|
const cur = accounts[acctIdx];
|
|
13002
13786
|
const rawEmail = cur?.email ?? "";
|
|
13003
13787
|
const acctLabel2 = !cur ? "not logged in" : rawEmail || cur.user || cur.project || `account \u2026${cur.apiKey.slice(-4)}`;
|
|
13004
|
-
|
|
13788
|
+
useEffect9(() => {
|
|
13005
13789
|
if (!cur || cur.email) return;
|
|
13006
13790
|
let alive = true;
|
|
13007
13791
|
api.auth.me().then((r) => {
|
|
@@ -13035,7 +13819,7 @@ function App() {
|
|
|
13035
13819
|
setViewKey(next?.apiKey);
|
|
13036
13820
|
setViewNonce((n) => n + 1);
|
|
13037
13821
|
};
|
|
13038
|
-
|
|
13822
|
+
useEffect9(() => {
|
|
13039
13823
|
try {
|
|
13040
13824
|
ensureLocalLogOwner(enforcingKey());
|
|
13041
13825
|
} catch {
|
|
@@ -13043,7 +13827,7 @@ function App() {
|
|
|
13043
13827
|
}, [accounts.length]);
|
|
13044
13828
|
const locked = accounts.length === 0;
|
|
13045
13829
|
const effectiveSection = locked ? SETTINGS_SECTION : section;
|
|
13046
|
-
|
|
13830
|
+
useInput10((input, key) => {
|
|
13047
13831
|
if (help) {
|
|
13048
13832
|
setHelp(false);
|
|
13049
13833
|
return;
|
|
@@ -13071,50 +13855,50 @@ function App() {
|
|
|
13071
13855
|
});
|
|
13072
13856
|
const { cols, rows } = useTermSize();
|
|
13073
13857
|
const current = SECTIONS[effectiveSection];
|
|
13074
|
-
if (help) return /* @__PURE__ */
|
|
13075
|
-
if (current.label
|
|
13076
|
-
return /* @__PURE__ */
|
|
13858
|
+
if (help) return /* @__PURE__ */ jsx11(HelpOverlay, { cols, rows });
|
|
13859
|
+
if (TAKEOVER.has(current.label) && focus === "panel") {
|
|
13860
|
+
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) });
|
|
13077
13861
|
}
|
|
13078
13862
|
const Panel = current.Panel;
|
|
13079
|
-
const panelBody = current.label === "Settings" ? /* @__PURE__ */
|
|
13863
|
+
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" });
|
|
13080
13864
|
return (
|
|
13081
13865
|
// height rows-1 (not rows): once total output reaches stdout.rows ink stops
|
|
13082
13866
|
// diffing and clearTerminal-repaints every frame, which slides the banner.
|
|
13083
13867
|
// overflow hidden makes it a hard guarantee even if a panel over-renders.
|
|
13084
|
-
/* @__PURE__ */
|
|
13085
|
-
/* @__PURE__ */
|
|
13086
|
-
/* @__PURE__ */
|
|
13087
|
-
/* @__PURE__ */
|
|
13088
|
-
/* @__PURE__ */
|
|
13089
|
-
locked ? /* @__PURE__ */
|
|
13090
|
-
update2.kind === "updating" ? /* @__PURE__ */
|
|
13868
|
+
/* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", width: cols, height: rows - 1, paddingX: 1, paddingTop: 1, overflow: "hidden", children: [
|
|
13869
|
+
/* @__PURE__ */ jsx11(Banner, { cols }),
|
|
13870
|
+
/* @__PURE__ */ jsxs11(Text11, { wrap: "truncate", children: [
|
|
13871
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.dim, children: "account: " }),
|
|
13872
|
+
/* @__PURE__ */ jsx11(Text11, { color: locked ? theme.warn : theme.accentBright, bold: true, children: acctLabel2 }),
|
|
13873
|
+
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" }),
|
|
13874
|
+
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
|
|
13091
13875
|
] }),
|
|
13092
|
-
/* @__PURE__ */
|
|
13093
|
-
/* @__PURE__ */
|
|
13876
|
+
/* @__PURE__ */ jsxs11(Box11, { marginTop: 1, flexGrow: 1, children: [
|
|
13877
|
+
/* @__PURE__ */ jsx11(Box11, { flexDirection: "column", flexShrink: 0, width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => {
|
|
13094
13878
|
const isCur = i === effectiveSection;
|
|
13095
13879
|
const disabled = locked && s.label !== "Settings";
|
|
13096
|
-
return /* @__PURE__ */
|
|
13880
|
+
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);
|
|
13097
13881
|
}) }),
|
|
13098
|
-
/* @__PURE__ */
|
|
13882
|
+
/* @__PURE__ */ jsx11(Box11, { flexGrow: 1, minWidth: 0, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, overflow: "hidden", children: panelBody })
|
|
13099
13883
|
] }, viewNonce),
|
|
13100
|
-
/* @__PURE__ */
|
|
13884
|
+
/* @__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"]] }) })
|
|
13101
13885
|
] })
|
|
13102
13886
|
);
|
|
13103
13887
|
}
|
|
13104
13888
|
function HelpOverlay({ cols, rows }) {
|
|
13105
|
-
return /* @__PURE__ */
|
|
13106
|
-
/* @__PURE__ */
|
|
13107
|
-
/* @__PURE__ */
|
|
13108
|
-
/* @__PURE__ */
|
|
13109
|
-
keys.map(([k, desc]) => /* @__PURE__ */
|
|
13110
|
-
/* @__PURE__ */
|
|
13111
|
-
/* @__PURE__ */
|
|
13889
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
|
|
13890
|
+
/* @__PURE__ */ jsx11(Text11, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
|
|
13891
|
+
/* @__PURE__ */ jsx11(Box11, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginBottom: 1, children: [
|
|
13892
|
+
/* @__PURE__ */ jsx11(Text11, { bold: true, color: theme.accent, children: group }),
|
|
13893
|
+
keys.map(([k, desc]) => /* @__PURE__ */ jsxs11(Text11, { children: [
|
|
13894
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
|
|
13895
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.dim, children: desc })
|
|
13112
13896
|
] }, k))
|
|
13113
13897
|
] }, group)) }),
|
|
13114
|
-
/* @__PURE__ */
|
|
13898
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.dim, children: "press any key to close" })
|
|
13115
13899
|
] });
|
|
13116
13900
|
}
|
|
13117
|
-
var SECTIONS, BANNER_HEX, SETTINGS_SECTION, HELP;
|
|
13901
|
+
var SECTIONS, TAKEOVER, BANNER_HEX, SETTINGS_SECTION, HELP;
|
|
13118
13902
|
var init_App = __esm({
|
|
13119
13903
|
"src/tui/App.tsx"() {
|
|
13120
13904
|
"use strict";
|
|
@@ -13122,6 +13906,8 @@ var init_App = __esm({
|
|
|
13122
13906
|
init_components();
|
|
13123
13907
|
init_theme();
|
|
13124
13908
|
init_Live();
|
|
13909
|
+
init_FleetLive();
|
|
13910
|
+
init_Fleet();
|
|
13125
13911
|
init_Policies();
|
|
13126
13912
|
init_RateLimit();
|
|
13127
13913
|
init_Dlp();
|
|
@@ -13135,7 +13921,9 @@ var init_App = __esm({
|
|
|
13135
13921
|
init_local_log();
|
|
13136
13922
|
init_self_update();
|
|
13137
13923
|
SECTIONS = [
|
|
13138
|
-
{ label: "Live", Panel: LiveHint },
|
|
13924
|
+
{ label: "Solo Live", Panel: LiveHint },
|
|
13925
|
+
{ label: "Fleet Live", Panel: FleetLiveHint },
|
|
13926
|
+
{ label: "Fleet", Panel: FleetPanel },
|
|
13139
13927
|
{ label: "Policies", Panel: PoliciesPanel },
|
|
13140
13928
|
{ label: "Rate Limit", Panel: RateLimitPanel },
|
|
13141
13929
|
{ label: "DLP", Panel: DlpPanel },
|
|
@@ -13143,11 +13931,14 @@ var init_App = __esm({
|
|
|
13143
13931
|
{ label: "Audit", Panel: AuditPanel },
|
|
13144
13932
|
{ label: "Settings", Panel: SettingsPanel }
|
|
13145
13933
|
];
|
|
13934
|
+
TAKEOVER = /* @__PURE__ */ new Set(["Solo Live", "Fleet Live"]);
|
|
13146
13935
|
BANNER_HEX = ["white", "white", "white", "white", "white", "white"];
|
|
13147
13936
|
SETTINGS_SECTION = SECTIONS.findIndex((s) => s.label === "Settings");
|
|
13148
13937
|
HELP = [
|
|
13149
13938
|
["Global", [["\u2191\u2193", "move between sections"], ["\u2192 / enter", "open a section"], ["\u2190 / esc", "back to the menu"], ["?", "this help"], ["q", "quit"]]],
|
|
13150
|
-
["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)"]]],
|
|
13939
|
+
["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)"]]],
|
|
13940
|
+
["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"]]],
|
|
13941
|
+
["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"]]],
|
|
13151
13942
|
["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"]]],
|
|
13152
13943
|
["Rate limit", [["\u2191\u2193", "field"], ["\u2190\u2192", "adjust (shift = \xB110)"], ["s", "save"]]],
|
|
13153
13944
|
["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"]]],
|
|
@@ -13167,7 +13958,7 @@ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync9 } from "fs";
|
|
|
13167
13958
|
import { homedir as homedir12 } from "os";
|
|
13168
13959
|
import { join as join14 } from "path";
|
|
13169
13960
|
import { render } from "ink";
|
|
13170
|
-
import { jsx as
|
|
13961
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
13171
13962
|
async function launchTui() {
|
|
13172
13963
|
if (!process.stdout.isTTY || !process.stdin.isTTY) {
|
|
13173
13964
|
process.stderr.write(
|
|
@@ -13192,7 +13983,7 @@ async function launchTui() {
|
|
|
13192
13983
|
console.info = toFile("info");
|
|
13193
13984
|
console.debug = toFile("debug");
|
|
13194
13985
|
try {
|
|
13195
|
-
const { waitUntilExit } = render(/* @__PURE__ */
|
|
13986
|
+
const { waitUntilExit } = render(/* @__PURE__ */ jsx12(App, {}), { patchConsole: false });
|
|
13196
13987
|
await waitUntilExit();
|
|
13197
13988
|
} finally {
|
|
13198
13989
|
Object.assign(console, saved);
|
|
@@ -15285,8 +16076,8 @@ function analyzeSecurityWarnings(policySet) {
|
|
|
15285
16076
|
(r) => r.effect === "ALLOW" && r.enabled
|
|
15286
16077
|
);
|
|
15287
16078
|
const hasNoConstraint = (r) => {
|
|
15288
|
-
const
|
|
15289
|
-
return !
|
|
16079
|
+
const groups2 = ["commandConstraints", "pathConstraints", "filenameConstraints", "urlConstraints"];
|
|
16080
|
+
return !groups2.some((g) => {
|
|
15290
16081
|
const c2 = r[g];
|
|
15291
16082
|
return c2 ? (c2.allowed?.length ?? 0) + (c2.denied?.length ?? 0) > 0 : false;
|
|
15292
16083
|
});
|