@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/index.js CHANGED
@@ -5671,15 +5671,15 @@ var require_dist = __commonJS({
5671
5671
  }
5672
5672
  };
5673
5673
  function parseAllDocuments(src, options) {
5674
- const stream = [];
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
- stream.push(doc);
5679
+ stream2.push(doc);
5680
5680
  prev = doc;
5681
5681
  }
5682
- return stream;
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, groups] of Object.entries(v)) {
6721
- if (Array.isArray(groups)) out2.hooks[ev] = groups;
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, groups] of Object.entries(file.hooks)) if (groups.length) hooks[ev] = groups;
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, groups] of Object.entries(file.hooks)) {
6783
- const kept = groups.filter((g) => !isOurCodexGroup(g));
6784
- if (kept.length !== groups.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 = (label, ok, yes, no) => ({ label, ok, detail: ok ? yes : no });
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", label = "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} "${label}"`;
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} "${label}"`;
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 + label.length + tail.length;
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
- label
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, label], i) => /* @__PURE__ */ jsxs(Text, { color: theme.dim, children: [
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
- label,
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
- 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 };
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: label.padEnd(10) }),
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 windowed = visibleDesc.slice(clampedScroll, clampedScroll + streamBodyRows);
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 = (label, m, note) => push(
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
- label.padEnd(10)
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
- windowed.length === 0 ? (
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
- windowed.map((e, i) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e), selected: clampedScroll + i === selClamped }, e.id))
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/DryRun.tsx
10073
+ // src/tui/panels/FleetLive.tsx
9970
10074
  import { Box as Box3, Text as Text3, useInput as useInput2 } from "ink";
9971
- import TextInput2 from "ink-text-input";
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] = useState3(0);
9981
- const [limit, setLimit] = useState3(DEFAULT_LIMIT);
9982
- const [editLogs, setEditLogs] = useState3(null);
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] = useState3(null);
9986
- const [running, setRunning] = useState3(false);
9987
- const [err2, setErr] = useState3(null);
9988
- const [off, setOff] = useState3(0);
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
- useEffect3(() => {
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
- useInput2((input, key) => {
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__ */ jsx3(Text3, { bold: true, color: theme.accentBright, children: t }, `h${lines.length}`));
10034
- const dim2 = (t) => lines.push(/* @__PURE__ */ jsx3(Text3, { color: theme.dim, wrap: "truncate", children: t }, `d${lines.length}`));
10035
- const row = (t) => lines.push(/* @__PURE__ */ jsx3(Text3, { wrap: "truncate", children: t }, `r${lines.length}`));
10036
- const blank = () => lines.push(/* @__PURE__ */ jsx3(Text3, { children: " " }, `b${lines.length}`));
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__ */ jsxs3(Text3, { wrap: "truncate", children: [
10819
+ /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
10048
10820
  " newly blocked ",
10049
- /* @__PURE__ */ jsx3(Text3, { color: theme.bad, bold: true, children: String(s.newly_blocked) }),
10821
+ /* @__PURE__ */ jsx5(Text5, { color: theme.bad, bold: true, children: String(s.newly_blocked) }),
10050
10822
  " newly allowed ",
10051
- /* @__PURE__ */ jsx3(Text3, { color: theme.ok, bold: true, children: String(s.newly_allowed) }),
10823
+ /* @__PURE__ */ jsx5(Text5, { color: theme.ok, bold: true, children: String(s.newly_allowed) }),
10052
10824
  " unchanged ",
10053
- /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: String(s.unchanged) })
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__ */ jsxs3(Text3, { wrap: "truncate", children: [
10860
+ /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
10089
10861
  " ",
10090
- /* @__PURE__ */ jsx3(Text3, { color: theme.accent, children: truncate2(c2.tool, 16).padEnd(17) }),
10091
- /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: truncate2(c2.agent || "-", 14).padEnd(15) }),
10092
- /* @__PURE__ */ jsx3(Text3, { color: newlyAllowed ? theme.ok : theme.bad, children: flip.padEnd(14) }),
10093
- /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: truncate2(c2.preview.replace(/\s+/g, " "), Math.max(10, w - 50)) })
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__ */ jsx3(DataView, { loading: list5.loading && !list5.data, error: list5.error, children: /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
10107
- /* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.accentBright, children: "Policy Dry Run" }),
10108
- /* @__PURE__ */ jsxs3(Text3, { wrap: "truncate", children: [
10109
- /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: " policy " }),
10110
- /* @__PURE__ */ jsx3(Text3, { color: theme.accent, children: "\u2039 " + truncate2(selected?.name ?? "none", 30) + " \u203A" }),
10111
- /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: ` ${policies.length ? pi + 1 : 0}/${policies.length} press p \xB7 mode ${selected?.mode ?? "-"}` })
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__ */ jsxs3(Box3, { children: [
10114
- /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: " logs " }),
10115
- /* @__PURE__ */ jsx3(Text3, { color: theme.warn, children: "how many recent logs to replay: " }),
10116
- /* @__PURE__ */ jsx3(TextInput2, { value: editLogs, onChange: setEditLogs, onSubmit: commitLogs }),
10117
- /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: ` (max ${Math.min(MAX_LIMIT, totalLogs && totalLogs > 0 ? totalLogs : MAX_LIMIT)})` })
10118
- ] }) : /* @__PURE__ */ jsxs3(Text3, { wrap: "truncate", children: [
10119
- /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: " logs " }),
10120
- /* @__PURE__ */ jsx3(Text3, { color: theme.accent, children: "\u2039 " + String(limit) + " \u203A" }),
10121
- /* @__PURE__ */ jsx3(Text3, { 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}` : ""}` })
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__ */ jsx3(Text3, { 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" }),
10124
- running ? /* @__PURE__ */ jsx3(Text3, { color: theme.warn, children: "replaying recent traffic\u2026" }) : null,
10125
- err2 ? /* @__PURE__ */ jsx3(Text3, { color: theme.bad, wrap: "truncate", children: "\u2717 " + err2 }) : null,
10126
- !running && !err2 && !res ? /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "no result yet, press r to run" }) : null,
10127
- /* @__PURE__ */ jsx3(Box3, { marginTop: 1, flexDirection: "column", children: win })
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(label) {
10147
- pending = label;
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 Box4, Text as Text4, useInput as useInput3 } from "ink";
10172
- import TextInput3 from "ink-text-input";
10173
- import { useEffect as useEffect4, useState as useState4 } from "react";
10174
- import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
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] = useState4("list");
10192
- const [pi, setPi] = useState4(0);
10193
- const [ri, setRi] = useState4(0);
10194
- const [fi, setFi] = useState4(0);
10195
- const [rules, setRules] = useState4([]);
10196
- const [mode, setMode] = useState4("denylist");
10197
- const [dirty, setDirty] = useState4(false);
10198
- const [editing, setEditing] = useState4(false);
10199
- const [editVal, setEditVal] = useState4("");
10200
- const [status, setStatus] = useState4(null);
10201
- const [pendingDel, setPendingDel] = useState4(null);
10202
- const [permCursor, setPermCursor] = useState4(0);
10203
- const [matchSel, setMatchSel] = useState4(0);
10204
- const [busyCreate, setBusyCreate] = useState4(false);
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
- useEffect4(() => {
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] = useState4(null);
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
- useEffect4(() => {
11045
+ useEffect5(() => {
10274
11046
  if (busyCreate && policies.length > 0) setBusyCreate(false);
10275
11047
  }, [busyCreate, policies.length]);
10276
- useInput3(
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__ */ jsx4(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__ */ jsxs4(Box4, { flexDirection: "column", children: [
10448
- /* @__PURE__ */ jsxs4(Box4, { children: [
10449
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "active: " }),
10450
- activeQ.loading && !activeQ.data ? /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "\u2026" }) : activeName ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
10451
- /* @__PURE__ */ jsxs4(Text4, { color: theme.ok, bold: true, children: [
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__ */ jsx4(Text4, { color: theme.dim, children: activeBy ? ` (${activeBy})` : "" })
10456
- ] }) : /* @__PURE__ */ jsx4(Text4, { color: theme.bad, bold: true, children: "\u25CB NONE \u2014 nothing enforced, grants refused" })
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__ */ jsx4(Text4, { 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" }),
10459
- creating !== null ? /* @__PURE__ */ jsxs4(Box4, { children: [
10460
- /* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: "new policy name: " }),
10461
- /* @__PURE__ */ jsx4(TextInput3, { value: creating, onChange: setCreating, onSubmit: commitNewPolicy })
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__ */ jsx4(Box4, { marginTop: 1, flexDirection: "column", children: lWin.map((p, wi) => {
11235
+ /* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexDirection: "column", children: lWin.map((p, wi) => {
10464
11236
  const i = lStart + wi;
10465
- return /* @__PURE__ */ jsxs4(Text4, { color: i === pi ? theme.accentBright : void 0, bold: i === pi, children: [
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__ */ jsxs4(Text4, { color: theme.dim, children: [
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__ */ jsx4(Text4, { color: theme.ok, bold: true, children: " \u25CF ACTIVE" }) : null
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__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
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__ */ jsx4(Text4, { color: theme.warn, children: " \u25CF unsaved (s save \xB7 x discard)" }) : null;
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__ */ jsx4(DataView, { loading: detail.loading && !detail.data, error: detail.error, children: /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
10490
- /* @__PURE__ */ jsxs4(Box4, { children: [
10491
- /* @__PURE__ */ jsx4(Text4, { bold: true, color: theme.accentBright, children: truncate2(selected?.name ?? "", 28) }),
10492
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " mode: " }),
10493
- /* @__PURE__ */ jsx4(Text4, { color: mode === "whitelist" ? theme.warn : void 0, children: mode }),
10494
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: ` ${rules.length} rules` }),
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__ */ jsx4(Text4, { 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}` : ""}` }),
10498
- /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(
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__ */ jsx4(Text4, { color: theme.dim, children: "(no rules)" }) : null,
10523
- status ? /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
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__ */ jsxs4(Box4, { flexDirection: "column", children: [
10531
- /* @__PURE__ */ jsxs4(Box4, { children: [
10532
- /* @__PURE__ */ jsx4(Text4, { bold: true, color: theme.accentBright, children: "Match values " }),
10533
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: r ? `${r.effect} \xB7 ${t}` : "" }),
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__ */ jsx4(Text4, { 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" }),
10537
- /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
10538
- items.length === 0 ? /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "(no values \u2014 press a to add one)" }) : null,
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__ */ jsxs4(Box4, { children: [
10545
- /* @__PURE__ */ jsx4(Text4, { color: theme.accentBright, children: "\u25B8 " }),
10546
- /* @__PURE__ */ jsx4(Text4, { color: lE ? theme.ok : theme.dim, children: lE ? "\u2731 " : "\xB7 " }),
10547
- /* @__PURE__ */ jsx4(
10548
- TextInput3,
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__ */ jsx4(Text4, { color: rE ? theme.ok : theme.dim, children: rE ? " \u2731" : " \xB7" })
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__ */ jsxs4(Box4, { children: [
10568
- /* @__PURE__ */ jsx4(Text4, { color: sel ? theme.accentBright : void 0, children: sel ? "\u25B8 " : " " }),
10569
- /* @__PURE__ */ jsx4(Text4, { color: lOn ? theme.ok : theme.dim, children: lOn ? "\u2731 " : "\xB7 " }),
10570
- /* @__PURE__ */ jsx4(Text4, { dimColor: !it, children: it || "(empty)" }),
10571
- /* @__PURE__ */ jsx4(Text4, { color: rOn ? theme.ok : theme.dim, children: rOn ? " \u2731" : " \xB7" })
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__ */ jsx4(Text4, { 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)." }),
10576
- status ? /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
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__ */ jsxs4(Box4, { flexDirection: "column", children: [
10581
- /* @__PURE__ */ jsxs4(Box4, { children: [
10582
- /* @__PURE__ */ jsx4(Text4, { bold: true, color: theme.accentBright, children: "Rule " }),
10583
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: rule?.id ?? "" }),
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__ */ jsx4(Text4, { 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" }),
10587
- /* @__PURE__ */ jsx4(Box4, { marginTop: 1, flexDirection: "column", children: FIELDS.map((f, i) => {
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 label = /* @__PURE__ */ jsx4(Text4, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + f.label.padEnd(13) });
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__ */ jsxs4(Box4, { children: [
10592
- label,
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__ */ jsxs4(Text4, { color: on ? theme.ok : theme.bad, inverse: cur, children: [
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__ */ jsxs4(Box4, { children: [
10607
- label,
10608
- /* @__PURE__ */ jsx4(Text4, { dimColor: !preview, children: preview || "\u2014" }),
10609
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: items.length ? ` (${items.length} value${items.length > 1 ? "s" : ""} \xB7 enter: edit)` : " (enter: add values)" })
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__ */ jsxs4(Box4, { children: [
10614
- label,
10615
- active2 && editing && f.kind === "text" ? /* @__PURE__ */ jsx4(
10616
- TextInput3,
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__ */ jsx4(
10626
- Text4,
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__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
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 Box5, Text as Text5, useInput as useInput4 } from "ink";
10740
- import { useEffect as useEffect5, useState as useState5 } from "react";
10741
- import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
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] = useState5(null);
10748
- const [dirty, setDirty] = useState5(false);
10749
- const [sel, setSel] = useState5(0);
10750
- const [status, setStatus] = useState5(null);
10751
- useEffect5(() => {
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
- useInput4(
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__ */ jsx5(DataView, { loading: layersQ.loading && !draft, error: layersQ.error, children: draft ? (() => {
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__ */ jsxs5(Box5, { flexDirection: "column", height: rows, overflow: "hidden", children: [
10827
- /* @__PURE__ */ jsx5(Text5, { 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" }),
10828
- /* @__PURE__ */ jsxs5(FieldRow, { label: "Mode", active: focused && fi === 0, children: [
10829
- /* @__PURE__ */ jsx5(Text5, { color: modeColor(draft.mode), bold: true, children: draft.mode }),
10830
- /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: draft.mode === "off" ? " no limit" : draft.mode === "detect" ? " flag bursts, never block" : " DENY calls over the limit" })
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__ */ jsx5(FieldRow, { label: "Per minute", active: focused && fi === 1, children: /* @__PURE__ */ jsx5(Text5, { bold: true, children: draft.perMinute || "off" }) }),
10833
- /* @__PURE__ */ jsx5(FieldRow, { label: "Per hour", active: focused && fi === 2, children: /* @__PURE__ */ jsx5(Text5, { bold: true, children: draft.perHour || "off" }) }),
10834
- /* @__PURE__ */ jsx5(FieldRow, { label: "Per day", active: focused && fi === 3, children: /* @__PURE__ */ jsx5(Text5, { bold: true, children: draft.perDay || "off" }) }),
10835
- showExtras && /* @__PURE__ */ jsxs5(Fragment4, { children: [
10836
- hasLoad ? /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
10837
- /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: "load now".padEnd(13) }),
10838
- /* @__PURE__ */ jsx5(Text5, { color: loadPct >= 100 ? theme.bad : loadPct >= 80 ? theme.warn : theme.ok, children: "\u2588".repeat(Math.min(18, Math.round(now / lim * 18))) }),
10839
- /* @__PURE__ */ jsx5(Text5, { color: "#233457", children: "\u2591".repeat(Math.max(0, 18 - Math.round(now / lim * 18))) }),
10840
- /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: ` ${now}/${lim} this minute (${loadPct}%)` })
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__ */ jsxs5(Text5, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
11614
+ /* @__PURE__ */ jsxs7(Text7, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
10843
11615
  "Busiest 7d",
10844
- /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " most calls in one window vs your limit" })
11616
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " most calls in one window vs your limit" })
10845
11617
  ] }),
10846
- /* @__PURE__ */ jsx5(BusiestRow, { label: "minute", peak: peaks.minute, limit: draft.perMinute }),
10847
- /* @__PURE__ */ jsx5(BusiestRow, { label: "hour", peak: peaks.hour, limit: draft.perHour }),
10848
- /* @__PURE__ */ jsx5(BusiestRow, { label: "day", peak: peaks.day, limit: draft.perDay }),
10849
- /* @__PURE__ */ jsx5(Text5, { color: theme.dim, wrap: "truncate", children: `activity`.padEnd(11) + ` last 24h \xB7 ${last24h} calls \xB7 busiest hour ${peaks.hour}/hr` })
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__ */ jsxs5(Box5, { flexDirection: "column", children: [
10852
- /* @__PURE__ */ jsxs5(Text5, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
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__ */ jsx5(Text5, { color: theme.warn, children: ` ${off + 1}-${Math.min(off + burstBudget, anomalies2.length)}/${anomalies2.length}` }) : null,
10855
- /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " over the per-minute limit \xB7 Bypassed = detect (not blocked)" })
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__ */ jsxs5(Text5, { color: theme.dim, children: [
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__ */ jsx5(Box5, { flexDirection: "column", height: burstBudget + 1, overflow: "hidden", children: /* @__PURE__ */ jsx5(
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__ */ jsx5(Text5, { children: " " }) : null,
10882
- /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
10883
- /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: "limit now " }),
10884
- /* @__PURE__ */ jsx5(Text5, { children: lim > 0 ? `${lim}/min` : "no per-minute limit" }),
10885
- lastChange ? /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: ` \xB7 changed ${when(lastChange.ts)}` }) : null,
10886
- dirty ? /* @__PURE__ */ jsx5(Text5, { color: theme.warn, children: " \u25CF unsaved (s)" }) : null,
10887
- status ? /* @__PURE__ */ jsx5(Text5, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: " " + status }) : null
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__ */ jsxs5(Text5, { wrap: "truncate", children: [
10896
- /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: (" " + label).padEnd(11) }),
10897
- /* @__PURE__ */ jsx5(Text5, { color: hot ? theme.bad : void 0, bold: hot, children: String(peak).padStart(4) + " calls" }),
10898
- /* @__PURE__ */ jsx5(Text5, { color: hot ? theme.bad : theme.dim, children: " \xB7 " + ctx })
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__ */ jsxs5(Text5, { wrap: "truncate", children: [
10903
- /* @__PURE__ */ jsx5(Text5, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + label.padEnd(11) }),
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 Box6, Text as Text6, useInput as useInput5 } from "ink";
10926
- import TextInput4 from "ink-text-input";
10927
- import { useEffect as useEffect6, useState as useState6 } from "react";
10928
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
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] = useState6(null);
10933
- const [available, setAvailable] = useState6([]);
10934
- const [ghostOn, setGhostOn] = useState6(false);
10935
- const [ghostPats, setGhostPats] = useState6([]);
10936
- const [sel, setSel] = useState6(0);
10937
- const [dirty, setDirty] = useState6(false);
10938
- const [status, setStatus] = useState6(null);
10939
- const [adding, setAdding] = useState6(null);
10940
- const [newName, setNewName] = useState6("");
10941
- const [input, setInput] = useState6("");
10942
- const [editIdx, setEditIdx] = useState6(null);
10943
- useEffect6(() => {
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
- useInput5(
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__ */ jsx6(Text6, { color: on ? theme.ok : theme.dim, children: on ? "\u2731 " : "\xB7 " });
11054
- const starR = (on) => /* @__PURE__ */ jsx6(Text6, { color: on ? theme.ok : theme.dim, children: on ? " \u2731" : " \xB7" });
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, label, extra) => lines.push({
11057
- node: /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
11058
- /* @__PURE__ */ jsx6(Text6, { color: theme.accentBright, bold: true, children: label }),
11059
- extra ? /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " " + extra }) : null
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__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: text }, key), entry: -1 });
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__ */ jsxs6(Text6, { wrap: "truncate", backgroundColor: isCur ? "#1c2f63" : void 0, bold: isCur, children: [
11072
- /* @__PURE__ */ jsx6(Text6, { color: isCur ? theme.accentBright : theme.dim, children: isCur ? "\u25B8 " : " " }),
11073
- /* @__PURE__ */ jsx6(Text6, { color: on ? theme.ok : theme.dim, children: on ? "\u25CF " : "\u25CB " }),
11074
- /* @__PURE__ */ jsx6(Text6, { color: on ? void 0 : theme.dim, children: p })
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__ */ jsxs6(Text6, { wrap: "truncate", backgroundColor: isCur ? "#1c2f63" : void 0, bold: isCur, children: [
11088
- /* @__PURE__ */ jsx6(Text6, { color: isCur ? theme.accentBright : theme.dim, children: isCur ? "\u25B8 " : " " }),
11089
- /* @__PURE__ */ jsx6(Text6, { color: theme.accent, children: c2.name }),
11090
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " " }),
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__ */ jsx6(Text6, { color: theme.dim, children: truncate2(w.core, Math.max(6, cols - c2.name.length - 14)) }),
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__ */ jsxs6(Text6, { wrap: "truncate", backgroundColor: isCur ? "#1c2f63" : void 0, bold: isCur, children: [
11107
- /* @__PURE__ */ jsx6(Text6, { color: isCur ? theme.accentBright : theme.dim, children: isCur ? "\u25B8 " : " " }),
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__ */ jsx6(Text6, { color: ghostOn ? theme.accent : theme.dim, children: truncate2(w.core, Math.max(6, cols - 12)) }),
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__ */ jsx6(Text6, { color: theme.dim, children: " (ghost off)" }) : null
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__ */ jsx6(DataView, { loading: q.loading && !dlp, error: q.error, children: dlp ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
11126
- /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
11127
- /* @__PURE__ */ jsx6(Text6, { children: "mode: " }),
11128
- /* @__PURE__ */ jsx6(Text6, { color: modeColor(dlp.mode), bold: true, children: dlp.mode }),
11129
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: dlp.mode === "off" ? " (scanning disabled)" : dlp.mode === "detect" ? " (flag dlp:yes, redact output)" : " (deny + redact secrets)" }),
11130
- dirty ? /* @__PURE__ */ jsx6(Text6, { color: theme.warn, children: " \u25CF unsaved (s save \xB7 x discard)" }) : null
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__ */ jsx6(Text6, { 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" }),
11133
- adding ? /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", children: /* @__PURE__ */ jsxs6(Box6, { children: [
11134
- /* @__PURE__ */ jsx6(Text6, { 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): ` }),
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__ */ jsx6(
11137
- TextInput4,
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__ */ jsx6(Box6, { marginTop: 1, flexDirection: "column", height: budget, overflow: "hidden", children: win.map((l) => l.node) }),
11180
- status ? /* @__PURE__ */ jsx6(Text6, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, wrap: "truncate", children: status }) : null
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 Box7, Text as Text7, useInput as useInput6 } from "ink";
11197
- import TextInput5 from "ink-text-input";
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 useState7 } from "react";
11202
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
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] = useState7("cloud");
11228
- const [view, setView] = useState7("logs");
11229
- const [di, setDi] = useState7(0);
11230
- const [gi, setGi] = useState7(0);
11231
- const [tool, setTool] = useState7("");
11232
- const [agent, setAgent] = useState7("");
11233
- const [search, setSearch] = useState7("");
11234
- const [sessFilter, setSessFilter] = useState7("");
11235
- const [page, setPage] = useState7(0);
11236
- const [sel, setSel] = useState7(0);
11237
- const [detailScroll, setDetailScroll] = useState7(0);
11238
- const [editing, setEditing] = useState7(null);
11239
- const [si, setSi] = useState7(0);
11240
- const [sessSearch, setSessSearch] = useState7("");
11241
- const [sessSel, setSessSel] = useState7(0);
11242
- const [msg, setMsg] = useState7(null);
11243
- const [showHelp, setShowHelp] = useState7(false);
11244
- const [frozen, setFrozen] = useState7(false);
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
- useInput6(
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__ */ jsxs7(Text7, { wrap: "truncate", children: [
11457
- /* @__PURE__ */ jsx7(Text7, { bold: true, children: statsQ.data ? statsQ.data.total_calls : "\xB7\xB7\xB7\xB7" }),
11458
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " calls \xB7 " }),
11459
- /* @__PURE__ */ jsxs7(Text7, { color: theme.ok, children: [
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__ */ jsx7(Text7, { color: theme.dim, children: " \xB7 " }),
11464
- /* @__PURE__ */ jsxs7(Text7, { color: theme.bad, children: [
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__ */ jsx7(Text7, { color: theme.dim, children: ` \xB7 ${statsQ.data?.active_policies ?? "\xB7"} policies` })
11469
- ] }) : /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
11470
- /* @__PURE__ */ jsx7(Text7, { bold: true, children: localAll.length }),
11471
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " calls in local file \xB7 " }),
11472
- /* @__PURE__ */ jsxs7(Text7, { color: theme.ok, children: [
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__ */ jsx7(Text7, { color: theme.dim, children: " \xB7 " }),
11477
- /* @__PURE__ */ jsxs7(Text7, { color: theme.bad, children: [
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__ */ jsxs7(Text7, { color: theme.dim, children: [
12253
+ /* @__PURE__ */ jsxs9(Text9, { color: theme.dim, children: [
11482
12254
  " \xB7 ",
11483
12255
  localLogFile()
11484
12256
  ] })
11485
12257
  ] });
11486
- const copyBanner = frozen ? /* @__PURE__ */ jsx7(Text7, { backgroundColor: "#123d1f", color: "#7bd88f", bold: true, wrap: "truncate", children: " \u23F5 COPY MODE \u2014 screen frozen, select & copy freely \xB7 space resume " }) : null;
11487
- const srcChip = /* @__PURE__ */ jsxs7(Text7, { children: [
11488
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "src:" }),
11489
- /* @__PURE__ */ jsx7(Text7, { color: source === "local" ? theme.ok : "#4f6db8", bold: true, children: source }),
11490
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " (s) " })
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 = (groups) => /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", width: "50%", paddingRight: 2, children: groups.map(([group, keys]) => /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", marginBottom: 1, children: [
11494
- /* @__PURE__ */ jsx7(Text7, { bold: true, color: theme.accent, children: group }),
11495
- keys.map(([k, desc]) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
11496
- /* @__PURE__ */ jsx7(Text7, { color: theme.accentBright, children: (" " + k).padEnd(19) }),
11497
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: desc })
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__ */ jsxs7(Box7, { flexDirection: "column", children: [
12272
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
11501
12273
  copyBanner,
11502
- /* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
12274
+ /* @__PURE__ */ jsxs9(Text9, { bold: true, color: theme.accentBright, children: [
11503
12275
  "AUDIT \u2014 all keys",
11504
12276
  " ",
11505
- /* @__PURE__ */ jsx7(Text7, { 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)" })
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__ */ jsxs7(Box7, { marginTop: 1, children: [
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__ */ jsxs7(Box7, { flexDirection: "column", children: [
12298
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
11527
12299
  copyBanner,
11528
- /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
11529
- /* @__PURE__ */ jsx7(Text7, { backgroundColor: BG2, color: "white", bold: true, children: " ENTRY " }),
11530
- /* @__PURE__ */ jsx7(Text7, { color: decisionColor(e.decision), bold: true, children: " " + e.decision }),
11531
- /* @__PURE__ */ jsx7(Text7, { color: theme.accent, bold: true, children: " " + e.tool }),
11532
- /* @__PURE__ */ jsx7(Text7, { color: loc ? theme.ok : "white", children: " " + (loc ? "LOC" : "CLD") }),
11533
- e.dlp.length ? /* @__PURE__ */ jsx7(Text7, { color: theme.bad, children: " DLP!" }) : null,
11534
- e.burst ? /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: " BURST" }) : null,
11535
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2190 back" })
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__ */ jsxs7(Text7, { wrap: "truncate", children: [
11538
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2502 when " }),
11539
- /* @__PURE__ */ jsx7(Text7, { children: new Date(e.at).toLocaleString() }),
11540
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2502 perm " }),
11541
- /* @__PURE__ */ jsx7(Text7, { children: e.permission || "\u2014" }),
11542
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2502 trust " }),
11543
- /* @__PURE__ */ jsx7(Text7, { children: e.trust || "\u2014" }),
11544
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2502 eval " }),
11545
- /* @__PURE__ */ jsx7(Text7, { color: e.evalMs != null && e.evalMs > 500 ? theme.warn : void 0, children: e.evalMs != null ? `${e.evalMs}ms` : "\u2014" }),
11546
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2502 agent " }),
11547
- /* @__PURE__ */ jsx7(Text7, { children: e.agent ?? "\u2014" }),
11548
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2502" })
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__ */ jsxs7(Text7, { wrap: "truncate", children: [
11551
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2502 session " }),
11552
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: e.session ?? "\u2014" }),
11553
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2502 rule " }),
11554
- /* @__PURE__ */ jsx7(Text7, { color: e.rule && e.decision !== "ALLOW" ? theme.bad : theme.dim, children: e.rule ?? "\u2014" }),
11555
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2502" })
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__ */ jsx7(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 }),
11558
- /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", height: bodyRows, overflow: "hidden", children: win.map((l, i) => /* @__PURE__ */ jsx7(Text7, { wrap: "truncate", children: l || " " }, off + i)) }),
11559
- /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
11560
- /* @__PURE__ */ jsx7(Text7, { backgroundColor: BG2, color: "white", bold: true, children: " ENTRY " }),
11561
- /* @__PURE__ */ jsx7(Text7, { backgroundColor: "#0b1530", color: "white", children: ` ${e.id} ` }),
11562
- /* @__PURE__ */ jsx7(Text7, { backgroundColor: BG2, color: "white", children: " \u2191\u2193 scroll \xB7 space copy \xB7 ? all keys \xB7 \u2190 back \xB7 esc menu " })
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 = (label, val, on) => /* @__PURE__ */ jsxs7(Text7, { children: [
11567
- /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
11568
- label,
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__ */ jsx7(Text7, { color: on ? theme.accentBright : theme.dim, children: val }),
11572
- /* @__PURE__ */ jsx7(Text7, { children: " " })
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__ */ jsx7(DataView, { loading: sessLoading && !frozen, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
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__ */ jsxs7(Box7, { children: [
12355
+ /* @__PURE__ */ jsxs9(Box9, { children: [
11584
12356
  srcChip,
11585
- /* @__PURE__ */ jsx7(Text7, { color: theme.accentBright, bold: true, children: "SESSIONS" }),
11586
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \xB7 logs (v) " }),
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__ */ jsx7(Text7, { 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" }),
11591
- editing === "sess-search" ? /* @__PURE__ */ jsxs7(Box7, { children: [
11592
- /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: "search: " }),
11593
- /* @__PURE__ */ jsx7(
11594
- TextInput5,
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__ */ jsx7(Text7, { 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}` : ""}` }),
11606
- /* @__PURE__ */ jsx7(
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__ */ jsxs7(Text7, { color: theme.dim, children: [
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 windowed = pageRows.slice(start, start + listRows);
11650
- return /* @__PURE__ */ jsx7(DataView, { loading: logsLoading && !frozen, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
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__ */ jsxs7(Box7, { children: [
12425
+ /* @__PURE__ */ jsxs9(Box9, { children: [
11654
12426
  srcChip,
11655
- /* @__PURE__ */ jsx7(Text7, { color: theme.accentBright, bold: true, children: "LOGS" }),
11656
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \xB7 sessions (v) " }),
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__ */ jsx7(Text7, { 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" }),
11665
- msg ? /* @__PURE__ */ jsx7(Text7, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : null,
11666
- editing && editing !== "sess-search" ? /* @__PURE__ */ jsxs7(Box7, { children: [
11667
- /* @__PURE__ */ jsxs7(Text7, { color: theme.warn, children: [
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__ */ jsx7(
11672
- TextInput5,
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__ */ jsx7(Text7, { 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` : ""}` }),
11685
- /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", overflow: "hidden", children: windowed.map((e, i) => /* @__PURE__ */ jsx7(StreamLine, { e: toStream(e), loc: source === "local", selected: start + i === selClamped && focused, date: true }, e.id)) }),
11686
- pageRows.length === 0 ? /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
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 Box8, Text as Text8, useInput as useInput7 } from "ink";
12068
- import TextInput6 from "ink-text-input";
12069
- import { useEffect as useEffect7, useRef as useRef3, useState as useState8 } from "react";
12070
- import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
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] = useState8(0);
12081
- const [editing, setEditing] = useState8(null);
12082
- const [input, setInput] = useState8("");
12083
- const [confirmDel, setConfirmDel] = useState8(null);
12084
- const [msg, setMsg] = useState8(null);
12085
- const [busy, setBusy] = useState8(false);
12086
- const [editor, setEditor] = useState8(null);
12087
- const [latest, setLatest] = useState8(null);
12088
- const [diagBusy, setDiagBusy] = useState8(null);
12089
- const [autoUp, setAutoUp] = useState8(() => autoUpdateEnabled());
12090
- const [updBusy, setUpdBusy] = useState8(false);
12091
- const [llSetting, setLlSetting] = useState8(null);
12092
- useEffect7(() => {
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] = useState8(null);
12104
- useEffect7(() => {
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] = useState8(null);
12115
- const [diagStep, setDiagStep] = useState8(0);
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
- useEffect7(() => {
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
- useEffect7(() => {
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] = useState8(() => listAccounts());
12143
- const [login, setLogin] = useState8(null);
12144
- const [tick, setTick] = useState8(0);
12145
- const abort = useRef3(false);
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] = useState8(() => isGuardInstalled());
12148
- const [guardVer, setGuardVer] = useState8(() => installedGuardVersion());
12149
- const [guardOld, setGuardOld] = useState8(() => guardHookOutdated());
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] = useState8(codexNeedsTrust);
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] = useState8(() => logsServerStatus());
12164
- useEffect7(() => {
12935
+ const [srv, setSrv] = useState10(() => logsServerStatus());
12936
+ useEffect8(() => {
12165
12937
  const t = setInterval(() => setSrv(logsServerStatus()), 3e3);
12166
12938
  return () => clearInterval(t);
12167
12939
  }, []);
12168
- useEffect7(() => {
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 = useRef3(false);
12182
- useEffect7(() => {
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] = useState8(/* @__PURE__ */ new Set());
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 = (label, fn, reload) => {
13002
+ const run12 = (label2, fn, reload) => {
12231
13003
  if (busy) return;
12232
13004
  setBusy(true);
12233
- setMsg({ text: label + "\u2026", level: "ok" });
13005
+ setMsg({ text: label2 + "\u2026", level: "ok" });
12234
13006
  fn().then(() => {
12235
- setMsg({ text: "\u2713 " + label, level: "ok" });
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
- useInput7(
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__ */ jsxs8(Box8, { flexDirection: "column", children: [
12613
- /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: "Add an account" }),
12614
- login.phase === "starting" ? /* @__PURE__ */ jsxs8(Text8, { color: theme.dim, children: [
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__ */ jsxs8(Box8, { flexDirection: "column", marginTop: 1, children: [
12618
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Opening your browser to authorize. If it didn't open, visit:" }),
12619
- /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, bold: true, wrap: "truncate", children: login.url }),
12620
- /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, children: [
12621
- /* @__PURE__ */ jsxs8(Text8, { color: theme.warn, children: [
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__ */ jsx8(Text8, { color: theme.dim, children: "esc to cancel" })
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, label, value, hint) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
12632
- /* @__PURE__ */ jsx8(Text8, { color: editor.field === idx ? theme.accentBright : theme.dim, children: editor.field === idx ? "\u25B8 " : " " }),
12633
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: label.padEnd(11) }),
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__ */ jsx8(Text8, { color: theme.dim, children: " " + hint }) : null
13407
+ editor.field === idx && hint ? /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " " + hint }) : null
12636
13408
  ] });
12637
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
12638
- /* @__PURE__ */ jsxs8(Text8, { bold: true, color: theme.accentBright, children: [
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__ */ jsx8(Text8, { color: theme.dim, children: "\u2191\u2193 field \xB7 \u2190\u2192 change \xB7 e edit target \xB7 enter save \xB7 esc cancel" }),
12645
- editing === "alert-target" ? /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, children: [
12646
- /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: editor.channel === "email" ? "e-mail address: " : "telegram chat id (from @userinfobot): " }),
12647
- /* @__PURE__ */ jsx8(TextInput6, { value: input, onChange: setInput, onSubmit: submitInput })
12648
- ] }) : msg ? /* @__PURE__ */ jsx8(Text8, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : /* @__PURE__ */ jsx8(Text8, { children: " " }),
12649
- /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
12650
- fieldRow(0, "target", editor.target ? /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate2(editor.target, cols - 16) }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "empty \u2014 press e" }), "press e to edit"),
12651
- fieldRow(1, "signal", /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: SIGNAL_LABEL[editor.signal] }), "\u2190\u2192 change"),
12652
- fieldRow(2, "threshold", /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: `\u2265 ${editor.threshold}` }), "\u2190\u2192 \xB15"),
12653
- fieldRow(3, "window", /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: winLabel(editor.windowSeconds) }), "\u2190\u2192 change"),
12654
- editor.id ? fieldRow(4, "status", editor.enabled ? /* @__PURE__ */ jsx8(Text8, { color: theme.ok, children: "on" }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "off" }), "\u2190\u2192 turn on/off") : null
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__ */ jsx8(Box8, { marginTop: 1, children: editor.id && !editor.enabled ? /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: "disabled \u2014 this alert will NOT fire until you set status back to on" }) : /* @__PURE__ */ jsxs8(Text8, { color: theme.dim, children: [
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__ */ jsx8(Text8, { color: theme.accent, children: SIGNAL_LABEL[editor.signal] }),
13430
+ /* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: SIGNAL_LABEL[editor.signal] }),
12659
13431
  " reach ",
12660
- /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: editor.threshold }),
13432
+ /* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: editor.threshold }),
12661
13433
  " within",
12662
13434
  " ",
12663
- /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: winLabel(editor.windowSeconds) })
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__ */ jsx8(Text8, { color: theme.ok, children: "on " }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "off" });
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__ */ jsx8(Text8, { color: isCur(r) ? theme.accentBright : theme.dim, children: isCur(r) ? "\u25B8 " : " " });
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__ */ jsxs8(Text8, { wrap: "truncate", children: [
13454
+ return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
12683
13455
  cursor(r),
12684
- /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate2(acctLabel(a), 30).padEnd(31) }),
12685
- isView ? /* @__PURE__ */ jsx8(Text8, { color: theme.ok, children: "\u25CF viewing " }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " " }),
12686
- isActive ? /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: "ACTIVE " }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " " }),
12687
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: a.project ? truncate2(a.project, 20) : "" })
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__ */ jsxs8(Text8, { wrap: "truncate", children: [
13463
+ return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
12692
13464
  cursor(r),
12693
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add account (enter \u2014 device login in the browser)" })
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__ */ jsxs8(Text8, { wrap: "truncate", children: [
13468
+ return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
12697
13469
  cursor(r),
12698
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "guard".padEnd(11) }),
12699
- !guardHere ? /* @__PURE__ */ jsxs8(Fragment5, { children: [
12700
- /* @__PURE__ */ jsx8(Text8, { color: theme.bad, bold: true, children: "removed" }),
12701
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " \xB7 " }),
12702
- /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, children: "enter to install" })
12703
- ] }) : /* @__PURE__ */ jsxs8(Fragment5, { children: [
12704
- /* @__PURE__ */ jsx8(Text8, { color: guardOld ? theme.warn : theme.ok, children: `v${guardVer ?? "?"}` }),
12705
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: guardOld ? " \xB7 update available \xB7 enter update" : " (latest) \xB7 enter reinstall" }),
12706
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " \xB7 d remove" }),
12707
- codexTrust ? /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: " \xB7 codex: run /hooks once to trust" }) : null
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__ */ jsxs8(Text8, { wrap: "truncate", children: [
13483
+ return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
12712
13484
  cursor(r),
12713
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "self-prot".padEnd(11) }),
12714
- selfProt ? onOff(selfProt.enabled) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "\u2026" }),
12715
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " blocks agents editing SolonGate\u2019s own hooks/config \xB7 enter toggles" })
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__ */ jsxs8(Text8, { wrap: "truncate", children: [
13490
+ return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
12719
13491
  cursor(r),
12720
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "doctor".padEnd(11) }),
12721
- diagBusy === "doctor" ? /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, children: `${spin} running health check\u2026` }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "health check: login, policy, guard, hooks, local logs \xB7 enter runs it" })
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__ */ jsxs8(Text8, { wrap: "truncate", children: [
13496
+ return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
12725
13497
  cursor(r),
12726
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "repair".padEnd(11) }),
12727
- diagBusy === "repair" ? /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, children: `${spin} restoring protection\u2026` }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "restore every protection file after tampering or deletion \xB7 enter runs it" })
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__ */ jsxs8(Text8, { wrap: "truncate", children: [
13503
+ return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
12732
13504
  cursor(r),
12733
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "version".padEnd(11) }),
12734
- /* @__PURE__ */ jsx8(Text8, { color: behind ? theme.warn : theme.ok, children: `v${ver}` }),
12735
- updBusy ? /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, children: ` ${spin} updating\u2026` }) : behind && canInstall === false ? (
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__ */ jsx8(Text8, { color: theme.warn, children: ` v${latest} on npm \xB7 needs sudo here \xB7 enter shows how` })
12738
- ) : behind ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: ` v${latest} on npm \xB7 enter updates now` }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: latest ? " latest \xB7 enter checks again" : " enter checks npm and updates" })
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__ */ jsxs8(Text8, { wrap: "truncate", children: [
13514
+ return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
12743
13515
  cursor(r),
12744
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "auto".padEnd(11) }),
13516
+ /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "auto".padEnd(11) }),
12745
13517
  onOff(autoUp),
12746
- /* @__PURE__ */ jsx8(Text8, { 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" })
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__ */ jsxs8(Text8, { wrap: "truncate", children: [
13521
+ return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
12750
13522
  cursor(r),
12751
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "enabled ".padEnd(11) }),
13523
+ /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "enabled ".padEnd(11) }),
12752
13524
  onOff(!!local?.enabled),
12753
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " enter toggles" })
13525
+ /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " enter toggles" })
12754
13526
  ] });
12755
13527
  case "ll-path":
12756
- return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
13528
+ return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
12757
13529
  cursor(r),
12758
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "path ".padEnd(11) }),
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__ */ jsxs8(Fragment5, { children: [
12765
- /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: truncate2(local.path, Math.max(12, Math.floor((cols - 20) / 2))) }),
12766
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: ` not valid here \u2192 ${truncate2(llSetting.file, Math.max(12, Math.floor((cols - 20) / 2)))}` })
12767
- ] }) : /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate2(local.path, cols - 14) })
12768
- ) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "not set \u2014 enter to edit" })
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__ */ jsxs8(Text8, { wrap: "truncate", children: [
13543
+ return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
12772
13544
  cursor(r),
12773
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "dashboard".padEnd(11) }),
12774
- srv.running ? /* @__PURE__ */ jsx8(Text8, { color: theme.ok, children: `running 127.0.0.1:${srv.port}` }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "stopped" }),
12775
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: srv.running ? " survives closing the dataroom \xB7 enter stops" : " enter starts the dashboard local-logs link" })
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__ */ jsxs8(Text8, { wrap: "truncate", children: [
13550
+ return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
12779
13551
  cursor(r),
12780
13552
  onOff(r.wh.enabled),
12781
- /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: (" " + r.wh.events).padEnd(9) }),
12782
- /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate2(r.wh.url, cols - 16) })
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__ */ jsxs8(Text8, { wrap: "truncate", children: [
13557
+ return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
12786
13558
  cursor(r),
12787
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add webhook (enter)" })
13559
+ /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "+ add webhook (enter)" })
12788
13560
  ] });
12789
13561
  case "alert":
12790
- return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
13562
+ return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
12791
13563
  cursor(r),
12792
13564
  onOff(r.rule.enabled),
12793
- /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: (" " + SIGNAL_LABEL[r.rule.signal]).padEnd(13) }),
12794
- /* @__PURE__ */ jsx8(Text8, { children: `\u2265${r.rule.threshold}/${winLabel(r.rule.windowSeconds)} `.padEnd(11) }),
12795
- /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate2(chanOf(r.rule), cols - 34) })
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__ */ jsxs8(Text8, { wrap: "truncate", children: [
13570
+ return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
12799
13571
  cursor(r),
12800
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add email alert (enter)" })
13572
+ /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "+ add email alert (enter)" })
12801
13573
  ] });
12802
13574
  case "alert-add-tg":
12803
- return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
13575
+ return /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", children: [
12804
13576
  cursor(r),
12805
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add telegram alert (enter \u2014 numeric chat id from @userinfobot)" })
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__ */ jsx8(Text8, { children: " " }, "sp:" + sec));
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__ */ jsxs8(Text8, { wrap: "truncate", children: [
12831
- /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: sec }),
12832
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " \u2014 " + SECTION_DESC[sec] })
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__ */ jsx8(Text8, { 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)
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__ */ jsx8(Box8, { children: rowLine(r) }, keyOf(r)));
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((label, i) => {
13618
+ DIAG_STEPS[r.kind].forEach((label2, i) => {
12847
13619
  const done = i < diagStep;
12848
13620
  lineEls.push(
12849
- /* @__PURE__ */ jsx8(Text8, { wrap: "truncate", color: done ? theme.ok : i === diagStep ? theme.accentBright : theme.dim, children: ` ${done ? "\u2713" : i === diagStep ? spin : " "} ${label}` }, `step:${r.kind}:${i}`)
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__ */ jsx8(
12858
- Text8,
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__ */ jsx8(Text8, { color: theme.dim, children: " both channels in use \u2014 remove one (d) to change it" }, "both-note")
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__ */ jsx8(Text8, { children: " " }, "ver-sp"));
13649
+ lineEls.push(/* @__PURE__ */ jsx10(Text10, { children: " " }, "ver-sp"));
12878
13650
  lineKey.push("");
12879
13651
  lineEls.push(
12880
- /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", color: theme.dim, children: [
13652
+ /* @__PURE__ */ jsxs10(Text10, { wrap: "truncate", color: theme.dim, children: [
12881
13653
  `solongate v${ver}`,
12882
- latest ? newerThan(latest, ver) ? /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: ` \xB7 v${latest} on npm \u2014 ${autoUp ? "auto-updating" : "UPDATES above"}` }) : /* @__PURE__ */ jsx8(Text8, { color: theme.ok, children: " \xB7 latest" }) : null
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__ */ jsx8(DataView, { loading, error, children: /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
12894
- /* @__PURE__ */ jsx8(Text8, { 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" }),
12895
- authError ? /* @__PURE__ */ jsx8(Text8, { wrap: "truncate", color: theme.bad, children: truncate2(
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__ */ jsxs8(Box8, { children: [
12900
- /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: editing === "path" ? "local log path: " : "webhook url: " }),
12901
- /* @__PURE__ */ jsx8(TextInput6, { value: input, onChange: setInput, onSubmit: submitInput })
12902
- ] }) : msg ? /* @__PURE__ */ jsx8(Text8, { wrap: "truncate", color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : login?.msg ? /* @__PURE__ */ jsx8(Text8, { wrap: "truncate", color: login.phase === "error" ? theme.bad : theme.ok, children: login.msg }) : /* @__PURE__ */ jsx8(Text8, { children: " " }),
12903
- /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", height: budget, overflow: "hidden", children: win })
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 Box9, Text as Text9, useApp, useInput as useInput8 } from "ink";
12942
- import { useEffect as useEffect8, useState as useState9 } from "react";
12943
- import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
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__ */ jsxs9(Box9, { flexDirection: "column", children: [
12946
- /* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
12947
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
12948
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
12949
- /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { children: [
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__ */ jsx9(Text9, { color: theme.accent, children: "enter" }),
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__ */ jsxs9(Box9, { children: [
12960
- /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate" }),
12961
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2014 security control center" })
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__ */ jsxs9(Box9, { flexDirection: "column", children: [
12965
- BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx9(Text9, { bold: true, color: BANNER_HEX[i], children: line }, i)),
12966
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
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] = useState9(() => listAccounts());
12972
- const [viewKey, setViewKey] = useState9(() => accounts[0]?.apiKey);
12973
- const [viewNonce, setViewNonce] = useState9(0);
12974
- const [section, setSection] = useState9(accounts.length === 0 ? SETTINGS_SECTION : 0);
12975
- const [focus, setFocus] = useState9("nav");
12976
- useEffect8(
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 label = takePendingSection();
12979
- if (!label) return;
12980
- const idx = SECTIONS.findIndex((s) => s.label === 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] = useState9(false);
12989
- const [update2, setUpdate] = useState9({ kind: "idle" });
12990
- useEffect8(() => {
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
- useEffect8(() => {
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
- useEffect8(() => {
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
- useInput8((input, key) => {
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__ */ jsx9(HelpOverlay, { cols, rows });
13075
- if (current.label === "Live" && focus === "panel") {
13076
- return /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: cols, height: rows - 1, overflow: "hidden", children: /* @__PURE__ */ jsx9(LivePanel, { active: true, focused: true }, viewNonce) });
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__ */ jsx9(SettingsPanel, { active: true, focused: focus === "panel", viewApiKey: viewKey, onView: viewAccount, onAccountsChanged: syncAccounts }) : /* @__PURE__ */ jsx9(Panel, { active: focus === "panel" || current.label !== "Live", focused: focus === "panel" });
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__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows - 1, paddingX: 1, paddingTop: 1, overflow: "hidden", children: [
13085
- /* @__PURE__ */ jsx9(Banner, { cols }),
13086
- /* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
13087
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "account: " }),
13088
- /* @__PURE__ */ jsx9(Text9, { color: locked ? theme.warn : theme.accentBright, bold: true, children: acctLabel2 }),
13089
- locked ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 log in from Settings to unlock the dataroom" }) : accounts.length > 1 ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: ` (${acctIdx + 1}/${accounts.length} \xB7 a switch \xB7 Settings to manage)` }) : /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 Settings to add another" }),
13090
- update2.kind === "updating" ? /* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: ` \u2191 updating to v${update2.version}\u2026` }) : update2.kind === "updated" ? /* @__PURE__ */ jsx9(Text9, { color: theme.ok, bold: true, children: ` \u2191 v${update2.version} installed \xB7 restart (q, then solongate) to apply` }) : update2.kind === "available" ? /* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: ` \u2191 v${update2.version} out \xB7 Settings \u2192 UPDATES` }) : update2.kind === "needs-admin" ? /* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: ` \u2191 v${update2.version} needs admin rights \xB7 Settings \u2192 UPDATES` }) : null
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__ */ jsxs9(Box9, { marginTop: 1, flexGrow: 1, children: [
13093
- /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", flexShrink: 0, width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => {
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__ */ jsx9(Text9, { color: isCur ? theme.accentBright : disabled ? theme.dim : void 0, bold: isCur, dimColor: disabled, children: (isCur ? "\u25B8 " : disabled ? "\u2298 " : " ") + s.label }, s.label);
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__ */ jsx9(Box9, { flexGrow: 1, minWidth: 0, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, overflow: "hidden", children: panelBody })
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__ */ jsx9(Box9, { children: locked ? /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2192/enter", "open Settings"], ["n", "log in"], ["q", "quit"]] }) : focus === "nav" ? /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ...accounts.length > 1 ? [["a", "account"]] : [], ["?", "help"], ["q", "quit"]] }) : /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
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__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
13106
- /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
13107
- /* @__PURE__ */ jsx9(Box9, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, children: [
13108
- /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accent, children: group }),
13109
- keys.map(([k, desc]) => /* @__PURE__ */ jsxs9(Text9, { children: [
13110
- /* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
13111
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: desc })
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__ */ jsx9(Text9, { color: theme.dim, children: "press any key to close" })
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 jsx10 } from "react/jsx-runtime";
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__ */ jsx10(App, {}), { patchConsole: false });
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 groups = ["commandConstraints", "pathConstraints", "filenameConstraints", "urlConstraints"];
15289
- return !groups.some((g) => {
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
  });