@solongate/proxy 0.81.22 → 0.81.24

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
@@ -6844,6 +6844,45 @@ var init_components = __esm({
6844
6844
  }
6845
6845
  });
6846
6846
 
6847
+ // src/tui/local-log.ts
6848
+ import { closeSync, openSync, readSync, statSync } from "fs";
6849
+ import { homedir as homedir4 } from "os";
6850
+ import { join as join6 } from "path";
6851
+ function tailLines(file, maxBytes = 131072) {
6852
+ try {
6853
+ const size = statSync(file).size;
6854
+ const start = Math.max(0, size - maxBytes);
6855
+ const fd = openSync(file, "r");
6856
+ const buf = Buffer.alloc(size - start);
6857
+ readSync(fd, buf, 0, buf.length, start);
6858
+ closeSync(fd);
6859
+ const lines = buf.toString("utf-8").split("\n").filter(Boolean);
6860
+ if (start > 0) lines.shift();
6861
+ return lines;
6862
+ } catch {
6863
+ return [];
6864
+ }
6865
+ }
6866
+ function parseLocalLines(lines) {
6867
+ const out2 = [];
6868
+ for (const line of lines) {
6869
+ try {
6870
+ const j = JSON.parse(line);
6871
+ const at = Date.parse(j.ts ?? "");
6872
+ if (Number.isFinite(at)) out2.push({ ...j, at });
6873
+ } catch {
6874
+ }
6875
+ }
6876
+ return out2;
6877
+ }
6878
+ var LOCAL_LOG;
6879
+ var init_local_log = __esm({
6880
+ "src/tui/local-log.ts"() {
6881
+ "use strict";
6882
+ LOCAL_LOG = join6(homedir4(), ".solongate", "local-logs", "solongate-audit.jsonl");
6883
+ }
6884
+ });
6885
+
6847
6886
  // src/api-client/types.ts
6848
6887
  var init_types = __esm({
6849
6888
  "src/api-client/types.ts"() {
@@ -7222,26 +7261,11 @@ var init_hooks = __esm({
7222
7261
  // src/tui/panels/Live.tsx
7223
7262
  import { Box as Box2, Text as Text2, useInput } from "ink";
7224
7263
  import TextInput from "ink-text-input";
7225
- import { closeSync, mkdirSync as mkdirSync3, openSync, readSync, statSync, writeFileSync as writeFileSync3 } from "fs";
7226
- import { homedir as homedir4 } from "os";
7227
- import { join as join6 } from "path";
7264
+ import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
7265
+ import { homedir as homedir5 } from "os";
7266
+ import { join as join7 } from "path";
7228
7267
  import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
7229
7268
  import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
7230
- function tailLines(file, maxBytes = 131072) {
7231
- try {
7232
- const size = statSync(file).size;
7233
- const start = Math.max(0, size - maxBytes);
7234
- const fd = openSync(file, "r");
7235
- const buf = Buffer.alloc(size - start);
7236
- readSync(fd, buf, 0, buf.length, start);
7237
- closeSync(fd);
7238
- const lines = buf.toString("utf-8").split("\n").filter(Boolean);
7239
- if (start > 0) lines.shift();
7240
- return lines;
7241
- } catch {
7242
- return [];
7243
- }
7244
- }
7245
7269
  function extractTarget(detail) {
7246
7270
  try {
7247
7271
  const j = JSON.parse(detail);
@@ -7785,9 +7809,9 @@ function LivePanel({ active: active2 }) {
7785
7809
  else if (input === "x") toggleSignal("dlp");
7786
7810
  else if (input === "r") toggleSignal("ratelimit");
7787
7811
  else if (input === "e") {
7788
- const file = join6(homedir4(), ".solongate", "live-export.jsonl");
7812
+ const file = join7(homedir5(), ".solongate", "live-export.jsonl");
7789
7813
  try {
7790
- mkdirSync3(join6(homedir4(), ".solongate"), { recursive: true });
7814
+ mkdirSync3(join7(homedir5(), ".solongate"), { recursive: true });
7791
7815
  writeFileSync3(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
7792
7816
  setActionMsg({ text: `\u2713 exported ${visibleDesc.length} lines \u2192 ${file}`, level: "ok", until: Date.now() + 6e3 });
7793
7817
  } catch (err2) {
@@ -8103,10 +8127,11 @@ function LivePanel({ active: active2 }) {
8103
8127
  ] })
8104
8128
  ] });
8105
8129
  }
8106
- var CONFIG, SPIN, BG, DIM_FLOOR, LOCAL_LOG, RING, hhmmss, fmtUp, FILTERS, sessStatus, STATUS_STYLE, LIVE_HELP;
8130
+ var CONFIG, SPIN, BG, DIM_FLOOR, RING, hhmmss, fmtUp, FILTERS, sessStatus, STATUS_STYLE, LIVE_HELP;
8107
8131
  var init_Live = __esm({
8108
8132
  "src/tui/panels/Live.tsx"() {
8109
8133
  "use strict";
8134
+ init_local_log();
8110
8135
  init_api_client();
8111
8136
  init_config2();
8112
8137
  init_notify();
@@ -8116,8 +8141,7 @@ var init_Live = __esm({
8116
8141
  SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
8117
8142
  BG = "#12234f";
8118
8143
  DIM_FLOOR = "#233457";
8119
- LOCAL_LOG = join6(homedir4(), ".solongate", "local-logs", "solongate-audit.jsonl");
8120
- RING = join6(process.cwd(), ".solongate", ".eval-ring.jsonl");
8144
+ RING = join7(process.cwd(), ".solongate", ".eval-ring.jsonl");
8121
8145
  hhmmss = (ts) => {
8122
8146
  const d = new Date(ts);
8123
8147
  return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
@@ -8193,11 +8217,13 @@ function ruleSummary(r) {
8193
8217
  function PoliciesPanel({ focused }) {
8194
8218
  const list6 = useLoader(() => api.policies.list());
8195
8219
  const policies = list6.data?.policies ?? [];
8220
+ const activeQ = useLoader(() => api.policies.active());
8221
+ const activeId = activeQ.data?.policy?.id ?? null;
8222
+ const activeBy = activeQ.data?.matched_by;
8196
8223
  const [view, setView] = useState3("list");
8197
8224
  const [pi, setPi] = useState3(0);
8198
8225
  const [ri, setRi] = useState3(0);
8199
8226
  const [fi, setFi] = useState3(0);
8200
- const [vi, setVi] = useState3(0);
8201
8227
  const [rules, setRules] = useState3([]);
8202
8228
  const [mode, setMode] = useState3("denylist");
8203
8229
  const [dirty, setDirty] = useState3(false);
@@ -8206,15 +8232,6 @@ function PoliciesPanel({ focused }) {
8206
8232
  const [status, setStatus] = useState3(null);
8207
8233
  const selected = policies[Math.min(pi, Math.max(0, policies.length - 1))];
8208
8234
  const detail = useLoader(() => selected ? api.policies.get(selected.id) : Promise.resolve(null), [selected?.id]);
8209
- const versionsQ = useLoader(
8210
- () => view === "versions" && selected ? api.policies.versions(selected.id, { limit: 50 }) : Promise.resolve(null),
8211
- [view, selected?.id]
8212
- );
8213
- const diffVer = versionsQ.data?.versions[vi]?.version;
8214
- const diffQ = useLoader(
8215
- () => view === "versions" && selected && diffVer !== void 0 ? api.policies.get(selected.id, diffVer) : Promise.resolve(null),
8216
- [view, selected?.id, diffVer]
8217
- );
8218
8235
  useEffect3(() => {
8219
8236
  if (detail.data) {
8220
8237
  setRules(detail.data.rules);
@@ -8265,6 +8282,20 @@ function PoliciesPanel({ focused }) {
8265
8282
  else if (key.return || key.rightArrow) {
8266
8283
  setStatus(null);
8267
8284
  setView("rules");
8285
+ } else if (input === "a") {
8286
+ if (selected) {
8287
+ setStatus("Activating\u2026");
8288
+ void api.policies.setActive(selected.id).then(() => {
8289
+ setStatus(`\u2713 "${selected.name}" is now the ACTIVE policy (pinned) \xB7 reaches agents in ~30s`);
8290
+ activeQ.reload();
8291
+ }).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
8292
+ }
8293
+ } else if (input === "x") {
8294
+ setStatus("Deactivating\u2026");
8295
+ void api.policies.setActive(null).then(() => {
8296
+ setStatus("\u2713 active policy: NONE \u2014 no rules enforced, w/b grants are refused");
8297
+ activeQ.reload();
8298
+ }).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
8268
8299
  }
8269
8300
  return;
8270
8301
  }
@@ -8298,32 +8329,10 @@ function PoliciesPanel({ focused }) {
8298
8329
  } else if (input === "D") {
8299
8330
  setStatus("Dry-running\u2026");
8300
8331
  void api.policies.dryRun({ rules, mode }).then((res) => setStatus(`dry-run ${res.evaluated} calls \xB7 allow ${res.would_allow} / deny ${res.would_deny} \xB7 newly blocked ${res.newly_blocked} \xB7 newly allowed ${res.newly_allowed}`)).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
8301
- } else if (input === "v") {
8302
- setVi(0);
8303
- setView("versions");
8304
8332
  } else if (input === "s") void save();
8305
8333
  else if (input === "x") discard();
8306
8334
  return;
8307
8335
  }
8308
- if (view === "versions") {
8309
- const vers = versionsQ.data?.versions ?? [];
8310
- if (key.leftArrow) return void setView("rules");
8311
- if (key.upArrow) setVi((n) => Math.max(0, n - 1));
8312
- else if (key.downArrow) setVi((n) => Math.min(vers.length - 1, n + 1));
8313
- else if (key.return) {
8314
- const v = vers[vi];
8315
- if (v && selected) {
8316
- setStatus("Rolling back\u2026");
8317
- void api.policies.rollback(selected.id, v.version).then((r) => {
8318
- setStatus(`\u2713 rolled back to v${v.version} \u2192 new v${r.version}`);
8319
- detail.reload();
8320
- list6.reload();
8321
- setView("rules");
8322
- }).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
8323
- }
8324
- }
8325
- return;
8326
- }
8327
8336
  const rule2 = rules[ri];
8328
8337
  if (!rule2) return setView("rules");
8329
8338
  const field = FIELDS[fi];
@@ -8345,18 +8354,30 @@ function PoliciesPanel({ focused }) {
8345
8354
  { isActive: focused && !editing }
8346
8355
  );
8347
8356
  if (view === "list") {
8357
+ const activeName = activeId ? policies.find((p) => p.id === activeId)?.name ?? activeId : null;
8348
8358
  return /* @__PURE__ */ jsx3(DataView, { loading: list6.loading && !list6.data, error: list6.error, empty: !!list6.data && policies.length === 0, emptyText: "No policies.", children: /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
8349
- /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: focused ? "\u2191\u2193 select \xB7 enter open" : "press \u2192 to browse" }),
8359
+ /* @__PURE__ */ jsxs3(Box3, { children: [
8360
+ /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "active: " }),
8361
+ activeQ.loading && !activeQ.data ? /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "\u2026" }) : activeName ? /* @__PURE__ */ jsxs3(Fragment3, { children: [
8362
+ /* @__PURE__ */ jsxs3(Text3, { color: theme.ok, bold: true, children: [
8363
+ "\u25CF ",
8364
+ truncate2(activeName, 28)
8365
+ ] }),
8366
+ /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: activeBy ? ` (${activeBy})` : "" })
8367
+ ] }) : /* @__PURE__ */ jsx3(Text3, { color: theme.bad, bold: true, children: "\u25CB NONE \u2014 nothing enforced, grants refused" })
8368
+ ] }),
8369
+ /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: focused ? "\u2191\u2193 select \xB7 enter open \xB7 a activate \xB7 x deactivate (none)" : "press \u2192 to browse" }),
8350
8370
  /* @__PURE__ */ jsx3(Box3, { marginTop: 1, flexDirection: "column", children: policies.map((p, i) => /* @__PURE__ */ jsxs3(Text3, { color: i === pi ? theme.accentBright : void 0, bold: i === pi, children: [
8351
8371
  (i === pi ? "\u25B8 " : " ") + truncate2(p.name, 26).padEnd(27),
8352
8372
  /* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
8353
8373
  p.mode.padEnd(10),
8354
8374
  " ",
8355
8375
  p.rules.length,
8356
- " rules \xB7 v",
8357
- p.version
8358
- ] })
8359
- ] }, p.id)) })
8376
+ " rules"
8377
+ ] }),
8378
+ p.id === activeId ? /* @__PURE__ */ jsx3(Text3, { color: theme.ok, bold: true, children: " \u25CF ACTIVE" }) : null
8379
+ ] }, p.id)) }),
8380
+ status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
8360
8381
  ] }) });
8361
8382
  }
8362
8383
  const dirtyTag = dirty ? /* @__PURE__ */ jsx3(Text3, { color: theme.warn, children: " \u25CF unsaved (s save \xB7 x discard)" }) : null;
@@ -8368,7 +8389,7 @@ function PoliciesPanel({ focused }) {
8368
8389
  /* @__PURE__ */ jsx3(Text3, { color: mode === "whitelist" ? theme.warn : void 0, children: mode }),
8369
8390
  dirtyTag
8370
8391
  ] }),
8371
- /* @__PURE__ */ jsx3(Text3, { color: theme.dim, 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 v versions \xB7 s save \xB7 \u2190 back" }),
8392
+ /* @__PURE__ */ jsx3(Text3, { color: theme.dim, 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" }),
8372
8393
  /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(
8373
8394
  Table,
8374
8395
  {
@@ -8394,50 +8415,6 @@ function PoliciesPanel({ focused }) {
8394
8415
  status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
8395
8416
  ] }) });
8396
8417
  }
8397
- if (view === "versions") {
8398
- const vers = versionsQ.data?.versions ?? [];
8399
- return /* @__PURE__ */ jsx3(DataView, { loading: versionsQ.loading && !versionsQ.data, error: versionsQ.error, empty: !!versionsQ.data && vers.length === 0, emptyText: "No versions.", children: /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
8400
- /* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.accentBright, children: truncate2(selected?.name ?? "", 28) }),
8401
- /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "\u2191\u2193 select \xB7 enter roll back to that version \xB7 \u2190 back" }),
8402
- /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(
8403
- Table,
8404
- {
8405
- columns: [
8406
- { header: "", width: 2 },
8407
- { header: "VER", width: 5 },
8408
- { header: "RULES", width: 5 },
8409
- { header: "REASON", width: 34 },
8410
- { header: "WHEN", width: 16 }
8411
- ],
8412
- rows: vers.map((v, i) => [
8413
- { value: i === vi ? "\u25B8" : "", color: theme.accentBright },
8414
- { value: `v${v.version}`, bold: i === vi },
8415
- { value: String(v.rules_count), dim: true },
8416
- { value: truncate2(v.reason || "\u2014", 34) },
8417
- { value: truncate2(v.created_at, 16), dim: true }
8418
- ])
8419
- }
8420
- ) }),
8421
- /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
8422
- /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: `Diff v${diffVer ?? "?"} vs current draft` }),
8423
- (() => {
8424
- const old = diffQ.data?.rules ?? [];
8425
- const cur = rules;
8426
- const oldIds = new Set(old.map((r) => r.id));
8427
- const curIds = new Set(cur.map((r) => r.id));
8428
- const added = cur.filter((r) => !oldIds.has(r.id));
8429
- const removed = old.filter((r) => !curIds.has(r.id));
8430
- if (diffQ.loading && !diffQ.data) return /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: " loading\u2026" });
8431
- if (!added.length && !removed.length) return /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: " identical rule set" });
8432
- return /* @__PURE__ */ jsxs3(Fragment3, { children: [
8433
- added.slice(0, 4).map((r) => /* @__PURE__ */ jsx3(Text3, { wrap: "truncate", color: theme.ok, children: ` + ${r.effect} ${truncate2(r.toolPattern, 18)} ${truncate2(r.description || r.id, 30)}` }, "a" + r.id)),
8434
- removed.slice(0, 4).map((r) => /* @__PURE__ */ jsx3(Text3, { wrap: "truncate", color: theme.bad, children: ` - ${r.effect} ${truncate2(r.toolPattern, 18)} ${truncate2(r.description || r.id, 30)}` }, "r" + r.id))
8435
- ] });
8436
- })()
8437
- ] }),
8438
- status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
8439
- ] }) });
8440
- }
8441
8418
  const rule = rules[ri];
8442
8419
  return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
8443
8420
  /* @__PURE__ */ jsxs3(Box3, { children: [
@@ -8850,177 +8827,202 @@ var init_Dlp = __esm({
8850
8827
  }
8851
8828
  });
8852
8829
 
8853
- // src/tui/panels/Stats.tsx
8854
- import { Box as Box6, Text as Text6 } from "ink";
8855
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
8856
- function StatsPanel({ active: active2 }) {
8857
- const stats = useLoader(() => api.stats.get());
8858
- const ts = useLoader(() => api.stats.timeseries({ period: "24h" }));
8859
- const drift2 = useLoader(() => api.stats.drift(7));
8860
- usePoll(() => {
8861
- stats.reload();
8862
- ts.reload();
8863
- }, 5e3, active2);
8864
- usePoll(drift2.reload, 3e4, active2);
8865
- const s = stats.data;
8866
- const points = ts.data?.timeseries ?? [];
8867
- const driftRules = (drift2.data?.rules ?? []).slice(0, 5);
8868
- return /* @__PURE__ */ jsx6(DataView, { loading: stats.loading && !s, error: stats.error, children: s ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
8869
- /* @__PURE__ */ jsxs6(Box6, { children: [
8870
- /* @__PURE__ */ jsxs6(Text6, { bold: true, children: [
8871
- s.total_calls,
8872
- " "
8873
- ] }),
8874
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "calls " }),
8875
- /* @__PURE__ */ jsxs6(Text6, { color: theme.ok, children: [
8876
- s.allowed,
8877
- " allowed"
8878
- ] }),
8879
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " " }),
8880
- /* @__PURE__ */ jsxs6(Text6, { color: theme.bad, children: [
8881
- s.denied,
8882
- " denied"
8883
- ] })
8884
- ] }),
8885
- /* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
8886
- s.active_policies,
8887
- " policies \xB7 ",
8888
- s.registered_tools,
8889
- " tools"
8890
- ] }),
8891
- /* @__PURE__ */ jsxs6(Box6, { marginTop: 1, flexDirection: "column", children: [
8892
- /* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
8893
- "Last 24h ",
8894
- ts.data ? `(per ${ts.data.granularity})` : ""
8895
- ] }),
8896
- /* @__PURE__ */ jsxs6(Box6, { children: [
8897
- /* @__PURE__ */ jsx6(Text6, { children: "total " }),
8898
- /* @__PURE__ */ jsx6(Text6, { color: theme.accent, children: sparkline(points.map((p) => p.total), 64) })
8899
- ] }),
8900
- /* @__PURE__ */ jsxs6(Box6, { children: [
8901
- /* @__PURE__ */ jsx6(Text6, { children: "allowed " }),
8902
- /* @__PURE__ */ jsx6(Text6, { color: theme.ok, children: sparkline(points.map((p) => p.allowed), 64) })
8903
- ] }),
8904
- /* @__PURE__ */ jsxs6(Box6, { children: [
8905
- /* @__PURE__ */ jsx6(Text6, { children: "denied " }),
8906
- /* @__PURE__ */ jsx6(Text6, { color: theme.bad, children: sparkline(points.map((p) => p.denied), 64) })
8907
- ] })
8908
- ] }),
8909
- /* @__PURE__ */ jsxs6(Box6, { marginTop: 1, flexDirection: "column", children: [
8910
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "Recent" }),
8911
- /* @__PURE__ */ jsx6(
8912
- Table,
8913
- {
8914
- columns: [
8915
- { header: "DECISION", width: 9 },
8916
- { header: "TOOL", width: 20 },
8917
- { header: "MS", width: 5 },
8918
- { header: "WHEN", width: 6 }
8919
- ],
8920
- rows: (s.recent_activity ?? []).slice(0, 6).map((a) => [
8921
- { value: a.decision, color: decisionColor(a.decision) },
8922
- { value: truncate2(a.tool_name, 20), color: theme.accent },
8923
- { value: String(a.evaluation_time_ms ?? "\u2014"), dim: true },
8924
- { value: ago(a.created_at), dim: true }
8925
- ])
8926
- }
8927
- )
8928
- ] }),
8929
- /* @__PURE__ */ jsxs6(Box6, { marginTop: 1, flexDirection: "column", children: [
8930
- /* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
8931
- "Denial drift ",
8932
- drift2.data ? `(7d: ${drift2.data.total_current} now vs ${drift2.data.total_previous} prev)` : ""
8933
- ] }),
8934
- driftRules.length ? /* @__PURE__ */ jsx6(
8935
- Table,
8936
- {
8937
- columns: [
8938
- { header: "NOW", width: 5 },
8939
- { header: "PREV", width: 5 },
8940
- { header: "\u0394", width: 6 },
8941
- { header: "RULE", width: 22 },
8942
- { header: "REASON", width: 26 }
8943
- ],
8944
- rows: driftRules.map((r) => [
8945
- { value: String(r.current), bold: true },
8946
- { value: String(r.previous), dim: true },
8947
- { value: r.is_new ? "NEW" : r.spike ? `+${r.delta}` : String(r.delta), color: r.is_new ? theme.ok : r.spike ? theme.bad : void 0 },
8948
- { value: truncate2(r.rule_id ?? "\u2014", 22), color: theme.accent },
8949
- { value: truncate2(r.reason ?? r.last_tool ?? "\u2014", 26), dim: true }
8950
- ])
8951
- }
8952
- ) : /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " no denials in window" })
8953
- ] })
8954
- ] }) : null });
8955
- }
8956
- var init_Stats = __esm({
8957
- "src/tui/panels/Stats.tsx"() {
8958
- "use strict";
8959
- init_api_client();
8960
- init_components();
8961
- init_hooks();
8962
- init_theme();
8963
- }
8964
- });
8965
-
8966
8830
  // src/tui/panels/Audit.tsx
8967
- import { Box as Box7, Text as Text7, useInput as useInput5 } from "ink";
8831
+ import { Box as Box6, Text as Text6, useInput as useInput5 } from "ink";
8968
8832
  import TextInput4 from "ink-text-input";
8969
8833
  import { useState as useState6 } from "react";
8970
- import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
8834
+ import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
8835
+ function loadLocalRows() {
8836
+ return parseLocalLines(tailLines(LOCAL_LOG, LOCAL_MAX_BYTES)).map((j, i) => ({
8837
+ id: "l:" + j.at + ":" + i,
8838
+ at: j.at,
8839
+ tool: j.tool ?? "?",
8840
+ decision: j.decision ?? "ALLOW",
8841
+ permission: j.permission ?? "\u2014",
8842
+ trust: j.trust_level ?? "\u2014",
8843
+ agent: j.agent_name ?? null,
8844
+ session: j.session_id ?? null,
8845
+ reason: j.reason ?? null,
8846
+ rule: j.matched_rule_id ?? null,
8847
+ evalMs: j.evaluation_time_ms ?? null,
8848
+ dlp: j.dlp ? Array.isArray(j.dlp) ? j.dlp.map(String) : ["dlp"] : [],
8849
+ burst: !!j.rate_limit_burst,
8850
+ args: j.arguments ? JSON.stringify(j.arguments) : null
8851
+ })).sort((a, b) => b.at - a.at);
8852
+ }
8971
8853
  function AuditPanel({ active: active2, focused }) {
8972
8854
  const { cols, rows } = usePanelSize();
8855
+ const [source, setSource] = useState6("cloud");
8856
+ const [view, setView] = useState6("logs");
8973
8857
  const [di, setDi] = useState6(0);
8974
8858
  const [gi, setGi] = useState6(0);
8975
8859
  const [tool, setTool] = useState6("");
8976
8860
  const [agent, setAgent] = useState6("");
8977
8861
  const [search, setSearch] = useState6("");
8862
+ const [sessFilter, setSessFilter] = useState6("");
8863
+ const [page, setPage] = useState6(0);
8978
8864
  const [sel, setSel] = useState6(0);
8979
- const [view, setView] = useState6("list");
8980
8865
  const [detailScroll, setDetailScroll] = useState6(0);
8981
8866
  const [editing, setEditing] = useState6(null);
8867
+ const [si, setSi] = useState6(0);
8868
+ const [sessSearch, setSessSearch] = useState6("");
8869
+ const [sessSel, setSessSel] = useState6(0);
8870
+ const toTop = () => setSel(0);
8871
+ const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
8872
+ const tsQ = useLoader(() => source === "cloud" ? api.stats.timeseries({ period: "24h" }) : Promise.resolve(null), [source]);
8873
+ usePoll(() => {
8874
+ statsQ.reload();
8875
+ tsQ.reload();
8876
+ }, 15e3, active2 && source === "cloud");
8982
8877
  const query = {
8983
8878
  filter: DECISIONS[di],
8984
8879
  signal: SIGNALS[gi],
8985
8880
  tool: tool || void 0,
8986
8881
  agent_name: agent || void 0,
8987
8882
  search: search || void 0,
8988
- limit: FETCH_LIMIT
8883
+ session_id: sessFilter || void 0,
8884
+ limit: PAGE,
8885
+ offset: page * PAGE
8989
8886
  };
8990
- const auditQ = useLoader(() => api.audit.list(query), [di, gi, tool, agent, search]);
8991
- usePoll(auditQ.reload, 6e3, active2 && view === "list" && !editing);
8992
- const entries = auditQ.data?.entries ?? [];
8993
- const current = entries[Math.min(sel, Math.max(0, entries.length - 1))];
8887
+ const cloudQ = useLoader(
8888
+ () => source === "cloud" ? api.audit.list(query) : Promise.resolve(null),
8889
+ [source, di, gi, tool, agent, search, sessFilter, page]
8890
+ );
8891
+ usePoll(cloudQ.reload, 6e3, active2 && source === "cloud" && view === "logs" && !editing && page === 0);
8892
+ const localQ = useLoader(() => source === "local" ? Promise.resolve(loadLocalRows()) : Promise.resolve(null), [source]);
8893
+ usePoll(localQ.reload, 6e3, active2 && source === "local" && view === "logs" && !editing && page === 0);
8894
+ let pageRows = [];
8895
+ let total = 0;
8896
+ if (source === "cloud") {
8897
+ pageRows = (cloudQ.data?.entries ?? []).map(cloudRow).sort((a, b) => b.at - a.at);
8898
+ total = cloudQ.data?.total ?? 0;
8899
+ } else {
8900
+ const all = localQ.data ?? [];
8901
+ const q = search.trim().toLowerCase();
8902
+ const filtered = all.filter((r) => {
8903
+ if (DECISIONS[di] && r.decision !== DECISIONS[di]) return false;
8904
+ if (SIGNALS[gi] === "dlp" && r.dlp.length === 0) return false;
8905
+ if (SIGNALS[gi] === "ratelimit" && !r.burst) return false;
8906
+ if (tool && !r.tool.toLowerCase().includes(tool.toLowerCase())) return false;
8907
+ if (agent && (r.agent ?? "").toLowerCase() !== agent.toLowerCase()) return false;
8908
+ if (sessFilter && r.session !== sessFilter) return false;
8909
+ if (q && !`${r.tool} ${r.agent ?? ""} ${r.reason ?? ""} ${r.args ?? ""}`.toLowerCase().includes(q)) return false;
8910
+ return true;
8911
+ });
8912
+ total = filtered.length;
8913
+ pageRows = filtered.slice(page * PAGE, page * PAGE + PAGE);
8914
+ }
8915
+ const pages = Math.max(1, Math.ceil(total / PAGE));
8916
+ const current = pageRows[Math.min(sel, Math.max(0, pageRows.length - 1))];
8917
+ const agentsQ = useLoader(
8918
+ () => source === "cloud" ? api.agents.live({ limit: 100, includeDeactivated: true }) : Promise.resolve(null),
8919
+ [source]
8920
+ );
8921
+ usePoll(agentsQ.reload, 8e3, active2 && source === "cloud" && view === "sessions");
8922
+ let sessions = [];
8923
+ if (source === "cloud") {
8924
+ sessions = (agentsQ.data?.agents ?? []).map((a) => ({
8925
+ id: a.session_id,
8926
+ agent: a.agent_name ?? a.session_id,
8927
+ status: a.status === "deactivated" ? "ended" : a.status,
8928
+ calls: a.total_calls,
8929
+ denies: a.denied_calls,
8930
+ dlp: a.dlp_events,
8931
+ trust: a.trust_score,
8932
+ lastAt: Date.parse(a.last_seen_at)
8933
+ }));
8934
+ } else {
8935
+ const bySess = /* @__PURE__ */ new Map();
8936
+ for (const r of localQ.data ?? []) {
8937
+ if (!r.session) continue;
8938
+ const cur = bySess.get(r.session) ?? { id: r.session, agent: r.agent ?? "local agent", status: "ended", calls: 0, denies: 0, dlp: 0, trust: null, lastAt: 0 };
8939
+ cur.calls++;
8940
+ if (r.decision !== "ALLOW") cur.denies++;
8941
+ if (r.dlp.length) cur.dlp++;
8942
+ cur.lastAt = Math.max(cur.lastAt, r.at);
8943
+ if (r.agent) cur.agent = r.agent;
8944
+ bySess.set(r.session, cur);
8945
+ }
8946
+ sessions = [...bySess.values()];
8947
+ }
8948
+ const sq = sessSearch.trim().toLowerCase();
8949
+ const sessionsFiltered = sessions.map((s) => ({ ...s, status: source === "cloud" ? s.status : sessStatus2(s.lastAt) })).filter((s) => SESS_STATUS[si] ? s.status === SESS_STATUS[si] : true).filter((s) => !sq || `${s.agent} ${s.id}`.toLowerCase().includes(sq)).sort((a, b) => b.lastAt - a.lastAt);
8950
+ const currentSess = sessionsFiltered[Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1))];
8994
8951
  const sessionQ = useLoader(
8995
- () => view === "detail" && current?.session_id ? api.audit.list({ session_id: current.session_id, limit: 40 }) : Promise.resolve(null),
8996
- [view, current?.session_id]
8952
+ () => view === "detail" && current?.session && source === "cloud" ? api.audit.list({ session_id: current.session, limit: 40 }) : Promise.resolve(null),
8953
+ [view, current?.session, source]
8997
8954
  );
8998
8955
  useInput5(
8999
8956
  (input, key) => {
9000
8957
  if (view === "detail") {
9001
- if (key.leftArrow || key.escape) setView("list");
8958
+ if (key.leftArrow || key.escape) setView("logs");
9002
8959
  else if (key.upArrow) setDetailScroll((n) => Math.max(0, n - 1));
9003
8960
  else if (key.downArrow) setDetailScroll((n) => n + 1);
9004
8961
  else if (key.pageUp) setDetailScroll((n) => Math.max(0, n - 10));
9005
8962
  else if (key.pageDown) setDetailScroll((n) => n + 10);
9006
8963
  return;
9007
8964
  }
9008
- const clearSel = () => setSel(0);
8965
+ if (input === "s") {
8966
+ setSource((s) => s === "cloud" ? "local" : "cloud");
8967
+ setPage(0);
8968
+ toTop();
8969
+ setSessSel(0);
8970
+ return;
8971
+ }
8972
+ if (input === "v") {
8973
+ setView((v) => v === "logs" ? "sessions" : "logs");
8974
+ return;
8975
+ }
8976
+ if (view === "sessions") {
8977
+ if (key.upArrow) setSessSel((n) => Math.max(0, n - 1));
8978
+ else if (key.downArrow) setSessSel((n) => Math.min(sessionsFiltered.length - 1, n + 1));
8979
+ else if (key.pageUp) setSessSel((n) => Math.max(0, n - 10));
8980
+ else if (key.pageDown) setSessSel((n) => Math.min(sessionsFiltered.length - 1, n + 10));
8981
+ else if (key.return || key.rightArrow) {
8982
+ if (currentSess) {
8983
+ setSessFilter(currentSess.id);
8984
+ setPage(0);
8985
+ toTop();
8986
+ setView("logs");
8987
+ }
8988
+ } else if (input === "f") {
8989
+ setSi((n) => (n + 1) % SESS_STATUS.length);
8990
+ setSessSel(0);
8991
+ } else if (input === "/") setEditing("sess-search");
8992
+ else if (input === "c") {
8993
+ setSi(0);
8994
+ setSessSearch("");
8995
+ setSessSel(0);
8996
+ }
8997
+ return;
8998
+ }
9009
8999
  if (key.upArrow) setSel((n) => Math.max(0, n - 1));
9010
- else if (key.downArrow) setSel((n) => Math.min(entries.length - 1, n + 1));
9000
+ else if (key.downArrow) setSel((n) => Math.min(pageRows.length - 1, n + 1));
9011
9001
  else if (key.pageUp) setSel((n) => Math.max(0, n - 10));
9012
- else if (key.pageDown) setSel((n) => Math.min(entries.length - 1, n + 10));
9002
+ else if (key.pageDown) setSel((n) => Math.min(pageRows.length - 1, n + 10));
9013
9003
  else if (key.return || key.rightArrow) {
9014
9004
  if (current) {
9015
9005
  setDetailScroll(0);
9016
9006
  setView("detail");
9017
9007
  }
9008
+ } else if (input === "]") {
9009
+ if (page < pages - 1) {
9010
+ setPage(page + 1);
9011
+ toTop();
9012
+ }
9013
+ } else if (input === "[") {
9014
+ if (page > 0) {
9015
+ setPage(page - 1);
9016
+ toTop();
9017
+ }
9018
9018
  } else if (input === "f") {
9019
9019
  setDi((n) => (n + 1) % DECISIONS.length);
9020
- clearSel();
9020
+ setPage(0);
9021
+ toTop();
9021
9022
  } else if (input === "g") {
9022
9023
  setGi((n) => (n + 1) % SIGNALS.length);
9023
- clearSel();
9024
+ setPage(0);
9025
+ toTop();
9024
9026
  } else if (input === "t") setEditing("tool");
9025
9027
  else if (input === "n") setEditing("agent");
9026
9028
  else if (input === "/") setEditing("search");
@@ -9030,46 +9032,89 @@ function AuditPanel({ active: active2, focused }) {
9030
9032
  setTool("");
9031
9033
  setAgent("");
9032
9034
  setSearch("");
9033
- clearSel();
9035
+ setSessFilter("");
9036
+ setPage(0);
9037
+ toTop();
9034
9038
  }
9035
9039
  },
9036
9040
  { isActive: focused && !editing }
9037
9041
  );
9042
+ const points = tsQ.data?.timeseries ?? [];
9043
+ const localAll = localQ.data ?? [];
9044
+ const strip = source === "cloud" ? /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
9045
+ /* @__PURE__ */ jsx6(Text6, { bold: true, children: statsQ.data ? statsQ.data.total_calls : "\xB7\xB7\xB7\xB7" }),
9046
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " calls \xB7 " }),
9047
+ /* @__PURE__ */ jsxs6(Text6, { color: theme.ok, children: [
9048
+ statsQ.data?.allowed ?? "\xB7\xB7\xB7",
9049
+ " allow"
9050
+ ] }),
9051
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \xB7 " }),
9052
+ /* @__PURE__ */ jsxs6(Text6, { color: theme.bad, children: [
9053
+ statsQ.data?.denied ?? "\xB7\xB7",
9054
+ " deny"
9055
+ ] }),
9056
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: ` \xB7 ${statsQ.data?.active_policies ?? "\xB7"} policies \xB7 ${statsQ.data?.registered_tools ?? "\xB7"} tools \xB7 24h ` }),
9057
+ /* @__PURE__ */ jsx6(Text6, { color: theme.accent, children: sparkline(points.map((p) => p.total), 24) }),
9058
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " deny " }),
9059
+ /* @__PURE__ */ jsx6(Text6, { color: theme.bad, children: sparkline(points.map((p) => p.denied), 24) })
9060
+ ] }) : /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
9061
+ /* @__PURE__ */ jsx6(Text6, { bold: true, children: localAll.length }),
9062
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " calls in local file \xB7 " }),
9063
+ /* @__PURE__ */ jsxs6(Text6, { color: theme.ok, children: [
9064
+ localAll.filter((r) => r.decision === "ALLOW").length,
9065
+ " allow"
9066
+ ] }),
9067
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \xB7 " }),
9068
+ /* @__PURE__ */ jsxs6(Text6, { color: theme.bad, children: [
9069
+ localAll.filter((r) => r.decision !== "ALLOW").length,
9070
+ " deny"
9071
+ ] }),
9072
+ /* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
9073
+ " \xB7 ",
9074
+ LOCAL_LOG
9075
+ ] })
9076
+ ] });
9077
+ const srcChip = /* @__PURE__ */ jsxs6(Text6, { children: [
9078
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "src:" }),
9079
+ /* @__PURE__ */ jsx6(Text6, { color: source === "local" ? theme.ok : "#4f6db8", bold: true, children: source }),
9080
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " (s) " })
9081
+ ] });
9038
9082
  if (view === "detail" && current) {
9039
- const sessionCalls = sessionQ.data?.entries ?? [];
9083
+ const sessionCalls = source === "cloud" ? (sessionQ.data?.entries ?? []).map(cloudRow) : localAll.filter((r) => r.session && r.session === current.session).slice(0, 40);
9040
9084
  const bodyW = Math.max(20, cols - 2);
9041
9085
  const reasonLines = wrapLines("reason: " + (current.reason ?? "\u2014"), bodyW);
9042
- const argLines = current.arguments_summary ? wrapLines(prettyJson(JSON.stringify(current.arguments_summary)), bodyW) : ["(no arguments recorded)"];
9043
- const sessionRows = current.session_id ? Math.min(4, sessionCalls.length) + 2 : 1;
9086
+ const argLines = current.args ? wrapLines(prettyJson(current.args), bodyW) : ["(no arguments recorded)"];
9087
+ const sessionRows = current.session ? Math.min(4, sessionCalls.length) + 2 : 1;
9044
9088
  const reasonShown = reasonLines.slice(0, 4);
9045
9089
  const argBudget = Math.max(3, rows - 2 - 5 - reasonShown.length - 1 - sessionRows - 1);
9046
9090
  const maxScroll = Math.max(0, argLines.length - argBudget);
9047
9091
  const off = Math.min(detailScroll, maxScroll);
9048
9092
  const argWin = argLines.slice(off, off + argBudget);
9049
- return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
9050
- /* @__PURE__ */ jsxs7(Box7, { children: [
9051
- /* @__PURE__ */ jsx7(Text7, { color: decisionColor(current.decision), bold: true, children: current.decision }),
9052
- /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: " " + current.tool_name }),
9053
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " " + current.permission + " \xB7 " + current.trust_level })
9093
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
9094
+ /* @__PURE__ */ jsxs6(Box6, { children: [
9095
+ /* @__PURE__ */ jsx6(Text6, { color: decisionColor(current.decision), bold: true, children: current.decision }),
9096
+ /* @__PURE__ */ jsx6(Text6, { color: theme.accent, children: " " + current.tool }),
9097
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " " + current.permission + " \xB7 " + current.trust + " " }),
9098
+ srcChip
9054
9099
  ] }),
9055
- reasonShown.map((l, i) => /* @__PURE__ */ jsx7(Text7, { wrap: "truncate", children: i === 0 ? /* @__PURE__ */ jsxs7(Fragment4, { children: [
9056
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "reason " }),
9057
- /* @__PURE__ */ jsx7(Text7, { children: l.slice("reason: ".length) })
9058
- ] }) : /* @__PURE__ */ jsx7(Text7, { children: " " + l }) }, "r" + i)),
9059
- /* @__PURE__ */ jsx7(Detail, { label: "rule", value: current.matched_rule_id ?? "\u2014" }),
9060
- /* @__PURE__ */ jsx7(Detail, { label: "agent", value: current.agent_name ?? "\u2014" }),
9061
- /* @__PURE__ */ jsx7(Detail, { label: "session", value: current.session_id ?? "\u2014" }),
9062
- /* @__PURE__ */ jsx7(Detail, { label: "dlp", value: current.dlp_matches?.length ? current.dlp_matches.join(", ") : "none", color: current.dlp_matches?.length ? theme.bad : void 0 }),
9063
- /* @__PURE__ */ jsx7(Detail, { label: "when", value: new Date(current.created_at).toLocaleString() + (current.evaluation_time_ms != null ? ` \xB7 eval ${current.evaluation_time_ms}ms` : "") }),
9064
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: `arguments \u2014 full \xB7 ${argLines.length} lines${maxScroll ? ` \xB7 \u25BC${maxScroll - off} more \xB7 \u2191\u2193 scroll` : ""}` }),
9065
- /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", height: argBudget, overflow: "hidden", children: argWin.map((l, i) => /* @__PURE__ */ jsx7(Text7, { wrap: "truncate", children: l || " " }, off + i)) }),
9066
- current.session_id ? /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
9067
- /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
9100
+ reasonShown.map((l, i) => /* @__PURE__ */ jsx6(Text6, { wrap: "truncate", children: i === 0 ? /* @__PURE__ */ jsxs6(Fragment4, { children: [
9101
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "reason " }),
9102
+ /* @__PURE__ */ jsx6(Text6, { children: l.slice("reason: ".length) })
9103
+ ] }) : /* @__PURE__ */ jsx6(Text6, { children: " " + l }) }, "r" + i)),
9104
+ /* @__PURE__ */ jsx6(Detail, { label: "rule", value: current.rule ?? "\u2014" }),
9105
+ /* @__PURE__ */ jsx6(Detail, { label: "agent", value: current.agent ?? "\u2014" }),
9106
+ /* @__PURE__ */ jsx6(Detail, { label: "session", value: current.session ?? "\u2014" }),
9107
+ /* @__PURE__ */ jsx6(Detail, { label: "dlp", value: current.dlp.length ? current.dlp.join(", ") : "none", color: current.dlp.length ? theme.bad : void 0 }),
9108
+ /* @__PURE__ */ jsx6(Detail, { label: "when", value: new Date(current.at).toLocaleString() + (current.evalMs != null ? ` \xB7 eval ${current.evalMs}ms` : "") }),
9109
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: `arguments \u2014 full \xB7 ${argLines.length} lines${maxScroll ? ` \xB7 \u25BC${maxScroll - off} more \xB7 \u2191\u2193 scroll` : ""}` }),
9110
+ /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", height: argBudget, overflow: "hidden", children: argWin.map((l, i) => /* @__PURE__ */ jsx6(Text6, { wrap: "truncate", children: l || " " }, off + i)) }),
9111
+ current.session ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
9112
+ /* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
9068
9113
  "Session \xB7 ",
9069
9114
  sessionCalls.length,
9070
9115
  " calls"
9071
9116
  ] }),
9072
- /* @__PURE__ */ jsx7(
9117
+ /* @__PURE__ */ jsx6(
9073
9118
  Table,
9074
9119
  {
9075
9120
  columns: [
@@ -9080,59 +9125,136 @@ function AuditPanel({ active: active2, focused }) {
9080
9125
  ],
9081
9126
  rows: sessionCalls.slice(0, Math.max(1, sessionRows - 2)).map((e) => [
9082
9127
  { value: e.decision, color: decisionColor(e.decision) },
9083
- { value: truncate2(e.tool_name, 20), color: theme.accent },
9128
+ { value: truncate2(e.tool, 20), color: theme.accent },
9084
9129
  { value: truncate2(e.reason ?? "\u2014", Math.max(16, cols - 48)) },
9085
- { value: ago(e.created_at), dim: true }
9130
+ { value: ago(e.at), dim: true }
9086
9131
  ])
9087
9132
  }
9088
9133
  )
9089
- ] }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "(no session id)" }),
9090
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2191\u2193 scroll arguments \xB7 \u2190/esc back to list" })
9134
+ ] }) : /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "(no session id)" }),
9135
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "\u2191\u2193 scroll arguments \xB7 \u2190/esc back to logs" })
9091
9136
  ] });
9092
9137
  }
9093
- const chip = (label, val, on) => /* @__PURE__ */ jsxs7(Text7, { children: [
9094
- /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
9138
+ const chip = (label, val, on) => /* @__PURE__ */ jsxs6(Text6, { children: [
9139
+ /* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
9095
9140
  label,
9096
9141
  ":"
9097
9142
  ] }),
9098
- /* @__PURE__ */ jsx7(Text7, { color: on ? theme.accentBright : theme.dim, children: val }),
9099
- /* @__PURE__ */ jsx7(Text7, { children: " " })
9143
+ /* @__PURE__ */ jsx6(Text6, { color: on ? theme.accentBright : theme.dim, children: val }),
9144
+ /* @__PURE__ */ jsx6(Text6, { children: " " })
9100
9145
  ] });
9101
- const headerRows = 4 + (editing ? 1 : 0);
9146
+ if (view === "sessions") {
9147
+ const headerRows2 = 5 + (editing ? 1 : 0);
9148
+ const listRows2 = Math.max(4, rows - headerRows2);
9149
+ const selC = Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1));
9150
+ const start2 = Math.min(Math.max(0, selC - Math.floor((listRows2 - 1) / 2)), Math.max(0, sessionsFiltered.length - listRows2));
9151
+ const win = sessionsFiltered.slice(start2, start2 + listRows2);
9152
+ const loading2 = source === "cloud" ? agentsQ.loading && !agentsQ.data : localQ.loading && !localQ.data;
9153
+ return /* @__PURE__ */ jsx6(DataView, { loading: loading2, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
9154
+ strip,
9155
+ /* @__PURE__ */ jsxs6(Box6, { children: [
9156
+ srcChip,
9157
+ /* @__PURE__ */ jsx6(Text6, { color: theme.accentBright, bold: true, children: "SESSIONS" }),
9158
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \xB7 logs (v) " }),
9159
+ chip("status", SESS_STATUS[si] ?? "all", si !== 0),
9160
+ chip("search", sessSearch || "\xB7", !!sessSearch)
9161
+ ] }),
9162
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter \u2192 session logs \xB7 f status \xB7 / search \xB7 s source \xB7 v logs \xB7 c clear" : "press \u2192 to browse" }),
9163
+ editing === "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
9164
+ /* @__PURE__ */ jsx6(Text6, { color: theme.warn, children: "search: " }),
9165
+ /* @__PURE__ */ jsx6(
9166
+ TextInput4,
9167
+ {
9168
+ value: sessSearch,
9169
+ onChange: (v) => {
9170
+ setSessSearch(v);
9171
+ setSessSel(0);
9172
+ },
9173
+ onSubmit: () => setEditing(null)
9174
+ }
9175
+ )
9176
+ ] }) : null,
9177
+ /* @__PURE__ */ jsx6(Text6, { 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}` : ""}` }),
9178
+ /* @__PURE__ */ jsx6(
9179
+ Table,
9180
+ {
9181
+ columns: [
9182
+ { header: "", width: 2 },
9183
+ { header: "STATUS", width: 8 },
9184
+ { header: "AGENT", width: 18 },
9185
+ { header: "SESSION", width: 10 },
9186
+ { header: "CALLS", width: 6 },
9187
+ { header: "DENY", width: 5 },
9188
+ { header: "DLP", width: 4 },
9189
+ { header: "TRUST", width: 5 },
9190
+ { header: "LAST", width: 6 }
9191
+ ],
9192
+ rows: win.map((r, i) => {
9193
+ const st = STATUS_DOT[r.status];
9194
+ const isSel = start2 + i === selC && focused;
9195
+ return [
9196
+ { value: isSel ? "\u25B8" : "", color: theme.accentBright },
9197
+ { value: `${st.ch} ${r.status}`, color: st.color },
9198
+ { value: truncate2(r.agent, 18), color: theme.accent, bold: isSel },
9199
+ { value: r.id.slice(0, 8), dim: true },
9200
+ { value: String(r.calls) },
9201
+ { value: String(r.denies), color: r.denies ? theme.bad : void 0, dim: !r.denies },
9202
+ { value: String(r.dlp), color: r.dlp ? theme.bad : void 0, dim: !r.dlp },
9203
+ { value: r.trust != null ? String(r.trust) : "\u2014", dim: true },
9204
+ { value: ago(r.lastAt), dim: true }
9205
+ ];
9206
+ })
9207
+ }
9208
+ ),
9209
+ sessionsFiltered.length === 0 ? /* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
9210
+ "(no sessions",
9211
+ source === "local" ? " in the local file" : "",
9212
+ ")"
9213
+ ] }) : null
9214
+ ] }) });
9215
+ }
9216
+ const headerRows = 6 + (editing && editing !== "sess-search" ? 1 : 0);
9102
9217
  const listRows = Math.max(4, rows - headerRows);
9103
- const selClamped = Math.min(sel, Math.max(0, entries.length - 1));
9104
- const maxStart = Math.max(0, entries.length - listRows);
9218
+ const selClamped = Math.min(sel, Math.max(0, pageRows.length - 1));
9219
+ const maxStart = Math.max(0, pageRows.length - listRows);
9105
9220
  const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
9106
- const windowed = entries.slice(start, start + listRows);
9221
+ const windowed = pageRows.slice(start, start + listRows);
9107
9222
  const reasonW = Math.min(60, Math.max(12, cols - 67));
9108
- return /* @__PURE__ */ jsx7(DataView, { loading: auditQ.loading && !auditQ.data, error: auditQ.error, children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
9109
- /* @__PURE__ */ jsxs7(Box7, { children: [
9223
+ const loading = source === "cloud" ? cloudQ.loading && !cloudQ.data : localQ.loading && !localQ.data;
9224
+ return /* @__PURE__ */ jsx6(DataView, { loading, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
9225
+ strip,
9226
+ /* @__PURE__ */ jsxs6(Box6, { children: [
9227
+ srcChip,
9228
+ /* @__PURE__ */ jsx6(Text6, { color: theme.accentBright, bold: true, children: "LOGS" }),
9229
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \xB7 sessions (v) " }),
9110
9230
  chip("dec", DECISIONS[di] ?? "all", di !== 0),
9111
9231
  chip("sig", SIGNALS[gi] ?? "all", gi !== 0),
9112
9232
  chip("tool", tool || "\xB7", !!tool),
9113
9233
  chip("agent", agent || "\xB7", !!agent),
9114
- chip("search", search || "\xB7", !!search)
9234
+ chip("search", search || "\xB7", !!search),
9235
+ sessFilter ? chip("sess", sessFilter.slice(0, 8), true) : null
9115
9236
  ] }),
9116
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 f dec \xB7 g sig \xB7 t tool \xB7 n agent \xB7 / search \xB7 c clear" : "press \u2192 to browse" }),
9117
- editing ? /* @__PURE__ */ jsxs7(Box7, { children: [
9118
- /* @__PURE__ */ jsxs7(Text7, { color: theme.warn, children: [
9237
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 [ ] page \xB7 f dec \xB7 g sig \xB7 t tool \xB7 n agent \xB7 / search \xB7 s source \xB7 v sessions \xB7 c clear" : "press \u2192 to browse" }),
9238
+ editing && editing !== "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
9239
+ /* @__PURE__ */ jsxs6(Text6, { color: theme.warn, children: [
9119
9240
  editing,
9120
9241
  ": "
9121
9242
  ] }),
9122
- /* @__PURE__ */ jsx7(
9243
+ /* @__PURE__ */ jsx6(
9123
9244
  TextInput4,
9124
9245
  {
9125
9246
  value: editing === "tool" ? tool : editing === "agent" ? agent : search,
9126
9247
  onChange: (v) => {
9127
9248
  (editing === "tool" ? setTool : editing === "agent" ? setAgent : setSearch)(v);
9128
- setSel(0);
9249
+ setPage(0);
9250
+ toTop();
9129
9251
  },
9130
9252
  onSubmit: () => setEditing(null)
9131
9253
  }
9132
9254
  )
9133
9255
  ] }) : null,
9134
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, wrap: "truncate", children: `${auditQ.data?.total ?? 0} matched \xB7 ${entries.length} loaded \xB7 ${selClamped + 1}/${entries.length}${start ? ` \xB7 \u25B2${start} newer` : ""}${start + listRows < entries.length ? ` \xB7 \u25BC${entries.length - start - listRows} older` : ""}` }),
9135
- /* @__PURE__ */ jsx7(
9256
+ /* @__PURE__ */ jsx6(Text6, { 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` : ""}` }),
9257
+ /* @__PURE__ */ jsx6(
9136
9258
  Table,
9137
9259
  {
9138
9260
  columns: [
@@ -9146,51 +9268,81 @@ function AuditPanel({ active: active2, focused }) {
9146
9268
  ],
9147
9269
  rows: windowed.map((e, i) => rowFor(e, start + i === selClamped && focused, reasonW))
9148
9270
  }
9149
- )
9271
+ ),
9272
+ pageRows.length === 0 ? /* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
9273
+ "(no entries",
9274
+ source === "local" ? " in the local file" : "",
9275
+ " \u2014 c clears filters)"
9276
+ ] }) : null
9150
9277
  ] }) });
9151
9278
  }
9152
9279
  function rowFor(e, active2, reasonW) {
9153
9280
  return [
9154
9281
  { value: active2 ? "\u25B8" : "", color: theme.accentBright },
9155
9282
  { value: e.decision, color: decisionColor(e.decision) },
9156
- { value: truncate2(e.tool_name, 18), color: theme.accent },
9157
- { value: truncate2(e.agent_name ?? "\u2014", 14), dim: true },
9158
- { value: truncate2(e.reason ?? "\u2014", reasonW) },
9159
- { value: e.dlp_matches?.length ? String(e.dlp_matches.length) : "0", color: e.dlp_matches?.length ? theme.bad : void 0, dim: !e.dlp_matches?.length },
9160
- { value: ago(e.created_at), dim: true }
9283
+ { value: truncate2(e.tool, 18), color: theme.accent },
9284
+ { value: truncate2(e.agent ?? "\u2014", 14), dim: true },
9285
+ { value: truncate2(e.reason ?? e.args ?? "\u2014", reasonW) },
9286
+ { value: e.dlp.length ? String(e.dlp.length) : "0", color: e.dlp.length ? theme.bad : void 0, dim: !e.dlp.length },
9287
+ { value: ago(e.at), dim: true }
9161
9288
  ];
9162
9289
  }
9163
9290
  function Detail({ label, value, color }) {
9164
- return /* @__PURE__ */ jsxs7(Box7, { children: [
9165
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: label.padEnd(9) }),
9166
- /* @__PURE__ */ jsx7(Text7, { color, wrap: "truncate", children: value })
9291
+ return /* @__PURE__ */ jsxs6(Box6, { children: [
9292
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: label.padEnd(9) }),
9293
+ /* @__PURE__ */ jsx6(Text6, { color, wrap: "truncate", children: value })
9167
9294
  ] });
9168
9295
  }
9169
- var DECISIONS, SIGNALS, FETCH_LIMIT;
9296
+ var DECISIONS, SIGNALS, SESS_STATUS, PAGE, LOCAL_MAX_BYTES, cloudRow, sessStatus2, STATUS_DOT;
9170
9297
  var init_Audit = __esm({
9171
9298
  "src/tui/panels/Audit.tsx"() {
9172
9299
  "use strict";
9173
9300
  init_api_client();
9174
9301
  init_components();
9175
9302
  init_hooks();
9303
+ init_local_log();
9176
9304
  init_theme();
9177
9305
  DECISIONS = [void 0, "DENY", "ALLOW"];
9178
9306
  SIGNALS = [void 0, "dlp", "ratelimit"];
9179
- FETCH_LIMIT = 500;
9307
+ SESS_STATUS = [void 0, "active", "idle", "ended"];
9308
+ PAGE = 500;
9309
+ LOCAL_MAX_BYTES = 16 * 1024 * 1024;
9310
+ cloudRow = (e) => ({
9311
+ id: e.id,
9312
+ at: Date.parse(e.created_at),
9313
+ tool: e.tool_name,
9314
+ decision: e.decision,
9315
+ permission: e.permission,
9316
+ trust: e.trust_level,
9317
+ agent: e.agent_name,
9318
+ session: e.session_id,
9319
+ reason: e.reason,
9320
+ rule: e.matched_rule_id,
9321
+ evalMs: e.evaluation_time_ms,
9322
+ dlp: e.dlp_matches ?? [],
9323
+ burst: !!e.rate_limit_burst,
9324
+ args: e.arguments_summary ? JSON.stringify(e.arguments_summary) : null
9325
+ });
9326
+ sessStatus2 = (lastAt) => Date.now() - lastAt < 6e4 ? "active" : Date.now() - lastAt < 3e5 ? "idle" : "ended";
9327
+ STATUS_DOT = {
9328
+ active: { ch: "\u25CF", color: theme.ok },
9329
+ idle: { ch: "\u25D0", color: theme.warn },
9330
+ ended: { ch: "\u25CB", color: theme.dim }
9331
+ };
9180
9332
  }
9181
9333
  });
9182
9334
 
9183
9335
  // src/tui/panels/Agents.tsx
9184
- import { Box as Box8, Text as Text8, useInput as useInput6 } from "ink";
9336
+ import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
9185
9337
  import { useState as useState7 } from "react";
9186
- import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
9338
+ import { Fragment as Fragment5, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
9187
9339
  function Bar({ label, value, max = 100, width: width2 = 16, color = theme.accent }) {
9188
9340
  const filled = max > 0 ? Math.round(Math.min(value, max) / max * width2) : 0;
9189
- return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
9190
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: label.padEnd(9) }),
9191
- /* @__PURE__ */ jsx8(Text8, { color, children: "\u2588".repeat(filled) }),
9192
- /* @__PURE__ */ jsx8(Text8, { color: "#233457", children: "\u2591".repeat(Math.max(0, width2 - filled)) }),
9193
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " " + value })
9341
+ return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
9342
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: label.padEnd(9) }),
9343
+ /* @__PURE__ */ jsx7(Text7, { color, children: "\u2588".repeat(filled) }),
9344
+ /* @__PURE__ */ jsx7(Text7, { color: "#233457", children: "\u2591".repeat(Math.max(0, width2 - filled)) }),
9345
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " " + value })
9194
9346
  ] });
9195
9347
  }
9196
9348
  function AgentsPanel({ focused }) {
@@ -9215,50 +9367,50 @@ function AgentsPanel({ focused }) {
9215
9367
  const tools = tmap?.tools ?? [];
9216
9368
  const resources = tmap?.resources ?? [];
9217
9369
  const listW = 30;
9218
- return /* @__PURE__ */ jsx8(DataView, { loading: live2.loading && !live2.data, error: live2.error, empty: !!live2.data && agents.length === 0, emptyText: "No identified agents yet.", children: /* @__PURE__ */ jsxs8(Box8, { children: [
9219
- /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: listW, marginRight: 2, overflow: "hidden", children: [
9220
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: focused ? "\u2191\u2193 select agent" : "press \u2192 to browse" }),
9221
- agents.slice(0, 16).map((a, i) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", backgroundColor: focused && i === sel ? "#1c2f63" : void 0, bold: i === sel, children: [
9222
- /* @__PURE__ */ jsxs8(Text8, { color: statusColor(a.status), children: [
9370
+ return /* @__PURE__ */ jsx7(DataView, { loading: live2.loading && !live2.data, error: live2.error, empty: !!live2.data && agents.length === 0, emptyText: "No identified agents yet.", children: /* @__PURE__ */ jsxs7(Box7, { children: [
9371
+ /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", width: listW, marginRight: 2, overflow: "hidden", children: [
9372
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: focused ? "\u2191\u2193 select agent" : "press \u2192 to browse" }),
9373
+ agents.slice(0, 16).map((a, i) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", backgroundColor: focused && i === sel ? "#1c2f63" : void 0, bold: i === sel, children: [
9374
+ /* @__PURE__ */ jsxs7(Text7, { color: statusColor(a.status), children: [
9223
9375
  a.status === "active" ? "\u25CF" : a.status === "idle" ? "\u25D0" : "\u25CB",
9224
9376
  " "
9225
9377
  ] }),
9226
- /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate2(a.agent_name ?? a.agent_id ?? "?", 16).padEnd(17) }),
9227
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: `t${a.trust_score}` })
9378
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(a.agent_name ?? a.agent_id ?? "?", 16).padEnd(17) }),
9379
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: `t${a.trust_score}` })
9228
9380
  ] }, a.session_id))
9229
9381
  ] }),
9230
- /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: !selected ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "select an agent" }) : detail.loading && !d ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "loading profile\u2026" }) : !d ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "no profile (agent has no calls yet)" }) : /* @__PURE__ */ jsxs8(Fragment5, { children: [
9231
- /* @__PURE__ */ jsxs8(Box8, { children: [
9232
- /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: truncate2(selected.agent_name ?? selected.agent_id ?? "?", 22) }),
9233
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: ` trust ${base?.trustScore ?? "\u2014"}/100 \xB7 ${base?.character ?? ""}` })
9382
+ /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: !selected ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "select an agent" }) : detail.loading && !d ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "loading profile\u2026" }) : !d ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "no profile (agent has no calls yet)" }) : /* @__PURE__ */ jsxs7(Fragment5, { children: [
9383
+ /* @__PURE__ */ jsxs7(Box7, { children: [
9384
+ /* @__PURE__ */ jsx7(Text7, { bold: true, color: theme.accentBright, children: truncate2(selected.agent_name ?? selected.agent_id ?? "?", 22) }),
9385
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: ` trust ${base?.trustScore ?? "\u2014"}/100 \xB7 ${base?.character ?? ""}` })
9234
9386
  ] }),
9235
- base?.characterBlurb ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: truncate2(String(base.characterBlurb), 70) }) : null,
9236
- radar ? /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
9237
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Behaviour radar (0\u2013100)" }),
9238
- /* @__PURE__ */ jsx8(Bar, { label: "read", value: Math.round((radar.read ?? 0) * 100) }),
9239
- /* @__PURE__ */ jsx8(Bar, { label: "write", value: Math.round((radar.write ?? 0) * 100), color: theme.warn }),
9240
- /* @__PURE__ */ jsx8(Bar, { label: "execute", value: Math.round((radar.execute ?? 0) * 100), color: theme.warn }),
9241
- /* @__PURE__ */ jsx8(Bar, { label: "network", value: Math.round((radar.network ?? 0) * 100) }),
9242
- /* @__PURE__ */ jsx8(Bar, { label: "complian.", value: Math.round((radar.compliance ?? 0) * 100), color: theme.ok })
9387
+ base?.characterBlurb ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: truncate2(String(base.characterBlurb), 70) }) : null,
9388
+ radar ? /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
9389
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "Behaviour radar (0\u2013100)" }),
9390
+ /* @__PURE__ */ jsx7(Bar, { label: "read", value: Math.round((radar.read ?? 0) * 100) }),
9391
+ /* @__PURE__ */ jsx7(Bar, { label: "write", value: Math.round((radar.write ?? 0) * 100), color: theme.warn }),
9392
+ /* @__PURE__ */ jsx7(Bar, { label: "execute", value: Math.round((radar.execute ?? 0) * 100), color: theme.warn }),
9393
+ /* @__PURE__ */ jsx7(Bar, { label: "network", value: Math.round((radar.network ?? 0) * 100) }),
9394
+ /* @__PURE__ */ jsx7(Bar, { label: "complian.", value: Math.round((radar.compliance ?? 0) * 100), color: theme.ok })
9243
9395
  ] }) : null,
9244
- /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
9245
- /* @__PURE__ */ jsxs8(Text8, { color: theme.dim, children: [
9396
+ /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
9397
+ /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
9246
9398
  "Anomalies ",
9247
9399
  anomalies2.length ? `(${anomalies2.length})` : ""
9248
9400
  ] }),
9249
- anomalies2.length === 0 ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " none" }) : anomalies2.slice(0, 4).map((an, i) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
9250
- /* @__PURE__ */ jsx8(Text8, { color: sevColor(String(an.severity)), children: ` ${String(an.severity).toUpperCase().slice(0, 4).padEnd(5)}` }),
9251
- /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: String(an.kind).padEnd(12) }),
9252
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: truncate2(String(an.description ?? ""), 44) })
9401
+ anomalies2.length === 0 ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " none" }) : anomalies2.slice(0, 4).map((an, i) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
9402
+ /* @__PURE__ */ jsx7(Text7, { color: sevColor(String(an.severity)), children: ` ${String(an.severity).toUpperCase().slice(0, 4).padEnd(5)}` }),
9403
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: String(an.kind).padEnd(12) }),
9404
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: truncate2(String(an.description ?? ""), 44) })
9253
9405
  ] }, i))
9254
9406
  ] }),
9255
- /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
9256
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Trust map \xB7 top tools" }),
9257
- tools.slice(0, 4).map((t, i) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
9258
- /* @__PURE__ */ jsx8(Text8, { color: t.anomalous ? theme.bad : theme.accent, children: ` ${truncate2(String(t.name), 16).padEnd(17)}` }),
9259
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: `${t.count}\xD7 ${t.permission ?? ""}` })
9407
+ /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
9408
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "Trust map \xB7 top tools" }),
9409
+ tools.slice(0, 4).map((t, i) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
9410
+ /* @__PURE__ */ jsx7(Text7, { color: t.anomalous ? theme.bad : theme.accent, children: ` ${truncate2(String(t.name), 16).padEnd(17)}` }),
9411
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: `${t.count}\xD7 ${t.permission ?? ""}` })
9260
9412
  ] }, i)),
9261
- resources.slice(0, 3).map((r, i) => /* @__PURE__ */ jsx8(Text8, { wrap: "truncate", color: r.anomalous ? theme.bad : theme.dim, children: ` ${r.type === "domain" ? "\u{1F310}" : "\u{1F4C1}"} ${truncate2(String(r.value), 40)} ${r.count}\xD7` }, "r" + i))
9413
+ resources.slice(0, 3).map((r, i) => /* @__PURE__ */ jsx7(Text7, { wrap: "truncate", color: r.anomalous ? theme.bad : theme.dim, children: ` ${r.type === "domain" ? "\u{1F310}" : "\u{1F4C1}"} ${truncate2(String(r.value), 40)} ${r.count}\xD7` }, "r" + i))
9262
9414
  ] })
9263
9415
  ] }) })
9264
9416
  ] }) });
@@ -9277,10 +9429,10 @@ var init_Agents = __esm({
9277
9429
  });
9278
9430
 
9279
9431
  // src/tui/panels/Settings.tsx
9280
- import { Box as Box9, Text as Text9, useInput as useInput7 } from "ink";
9432
+ import { Box as Box8, Text as Text8, useInput as useInput7 } from "ink";
9281
9433
  import TextInput5 from "ink-text-input";
9282
9434
  import { useState as useState8 } from "react";
9283
- import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
9435
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
9284
9436
  function alertBodyFor(channel) {
9285
9437
  const c2 = channel.trim();
9286
9438
  const base = { name: "dataroom alert", threshold: 5, windowSeconds: 60, signal: "any" };
@@ -9405,7 +9557,7 @@ function SettingsPanel({ active: active2, focused }) {
9405
9557
  const loading = localQ.loading && !localQ.data || whQ.loading && !whQ.data || alertQ.loading && !alertQ.data;
9406
9558
  const error = localQ.error ?? whQ.error ?? alertQ.error;
9407
9559
  const mark = (r) => keyOf(r) === keyOf(cur) && focused ? "\u25B8 " : " ";
9408
- const onOff = (on) => on ? /* @__PURE__ */ jsx9(Text9, { color: theme.ok, children: "on " }) : /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "off" });
9560
+ const onOff = (on) => on ? /* @__PURE__ */ jsx8(Text8, { color: theme.ok, children: "on " }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "off" });
9409
9561
  const chanOf = (rule) => [...(rule.slackUrls ?? []).map((u) => "slack " + u), ...rule.emails ?? [], ...(rule.telegram ?? []).map((t) => "tg " + t)].join(", ") || "\u2014";
9410
9562
  const listBudget = Math.max(4, rows - 8);
9411
9563
  const start = Math.min(Math.max(0, selClamped - Math.floor(listBudget / 2)), Math.max(0, rowsAll.length - listBudget));
@@ -9413,68 +9565,68 @@ function SettingsPanel({ active: active2, focused }) {
9413
9565
  const i = rowsAll.findIndex((x) => keyOf(x) === keyOf(r));
9414
9566
  return i >= start && i < start + listBudget;
9415
9567
  };
9416
- return /* @__PURE__ */ jsx9(DataView, { loading, error, children: /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
9417
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: focused ? "\u2191\u2193 move \xB7 enter/space toggle-edit-add \xB7 e events \xB7 d delete \xB7 r refresh" : "press \u2192 to configure" }),
9418
- editing ? /* @__PURE__ */ jsxs9(Box9, { children: [
9419
- /* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: editing === "path" ? "local log path: " : editing === "wh-url" ? "webhook url: " : "channel (slack url / email / telegram id): " }),
9420
- /* @__PURE__ */ jsx9(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
9421
- ] }) : msg ? /* @__PURE__ */ jsx9(Text9, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : /* @__PURE__ */ jsx9(Text9, { children: " " }),
9422
- inWindow(rowsAll[0]) || inWindow(rowsAll[1]) ? /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexDirection: "column", children: [
9423
- /* @__PURE__ */ jsxs9(Text9, { bold: true, color: theme.accentBright, children: [
9568
+ return /* @__PURE__ */ jsx8(DataView, { loading, error, children: /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
9569
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: focused ? "\u2191\u2193 move \xB7 enter/space toggle-edit-add \xB7 e events \xB7 d delete \xB7 r refresh" : "press \u2192 to configure" }),
9570
+ editing ? /* @__PURE__ */ jsxs8(Box8, { children: [
9571
+ /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: editing === "path" ? "local log path: " : editing === "wh-url" ? "webhook url: " : "channel (slack url / email / telegram id): " }),
9572
+ /* @__PURE__ */ jsx8(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
9573
+ ] }) : msg ? /* @__PURE__ */ jsx8(Text8, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : /* @__PURE__ */ jsx8(Text8, { children: " " }),
9574
+ inWindow(rowsAll[0]) || inWindow(rowsAll[1]) ? /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
9575
+ /* @__PURE__ */ jsxs8(Text8, { bold: true, color: theme.accentBright, children: [
9424
9576
  "LOCAL LOGS ",
9425
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "\u2014 hooks mirror every decision to a file on the agent machine" })
9577
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "\u2014 hooks mirror every decision to a file on the agent machine" })
9426
9578
  ] }),
9427
- /* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
9428
- /* @__PURE__ */ jsx9(Text9, { color: keyOf(cur) === "ll-enabled" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[0]) }),
9429
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "enabled ".padEnd(10) }),
9579
+ /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
9580
+ /* @__PURE__ */ jsx8(Text8, { color: keyOf(cur) === "ll-enabled" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[0]) }),
9581
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "enabled ".padEnd(10) }),
9430
9582
  onOff(!!local?.enabled),
9431
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " enter toggles" })
9583
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " enter toggles" })
9432
9584
  ] }),
9433
- /* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
9434
- /* @__PURE__ */ jsx9(Text9, { color: keyOf(cur) === "ll-path" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[1]) }),
9435
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "path ".padEnd(10) }),
9436
- local?.path ? /* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: truncate2(local.path, cols - 14) }) : /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "not set \u2014 enter to edit" })
9585
+ /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
9586
+ /* @__PURE__ */ jsx8(Text8, { color: keyOf(cur) === "ll-path" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[1]) }),
9587
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "path ".padEnd(10) }),
9588
+ local?.path ? /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate2(local.path, cols - 14) }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "not set \u2014 enter to edit" })
9437
9589
  ] })
9438
9590
  ] }) : null,
9439
- /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexDirection: "column", children: [
9440
- /* @__PURE__ */ jsxs9(Text9, { bold: true, color: theme.accentBright, children: [
9591
+ /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
9592
+ /* @__PURE__ */ jsxs8(Text8, { bold: true, color: theme.accentBright, children: [
9441
9593
  "WEBHOOKS ",
9442
- /* @__PURE__ */ jsxs9(Text9, { color: theme.dim, children: [
9594
+ /* @__PURE__ */ jsxs8(Text8, { color: theme.dim, children: [
9443
9595
  "\u2014 POST every matching event to your endpoint (",
9444
9596
  webhooks.length,
9445
9597
  ")"
9446
9598
  ] })
9447
9599
  ] }),
9448
- webhooks.filter((wh) => inWindow({ kind: "wh", wh })).map((wh) => /* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
9449
- /* @__PURE__ */ jsx9(Text9, { color: keyOf(cur) === "wh:" + wh.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh", wh }) }),
9600
+ webhooks.filter((wh) => inWindow({ kind: "wh", wh })).map((wh) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
9601
+ /* @__PURE__ */ jsx8(Text8, { color: keyOf(cur) === "wh:" + wh.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh", wh }) }),
9450
9602
  onOff(wh.enabled),
9451
- /* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: (" " + wh.events).padEnd(9) }),
9452
- /* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: truncate2(wh.url, cols - 16) })
9603
+ /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: (" " + wh.events).padEnd(9) }),
9604
+ /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate2(wh.url, cols - 16) })
9453
9605
  ] }, wh.id)),
9454
- inWindow(rowsAll.find((r) => r.kind === "wh-add")) ? /* @__PURE__ */ jsxs9(Text9, { children: [
9455
- /* @__PURE__ */ jsx9(Text9, { color: cur.kind === "wh-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh-add" }) }),
9456
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "+ add webhook (enter)" })
9606
+ inWindow(rowsAll.find((r) => r.kind === "wh-add")) ? /* @__PURE__ */ jsxs8(Text8, { children: [
9607
+ /* @__PURE__ */ jsx8(Text8, { color: cur.kind === "wh-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh-add" }) }),
9608
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add webhook (enter)" })
9457
9609
  ] }) : null
9458
9610
  ] }),
9459
- /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexDirection: "column", children: [
9460
- /* @__PURE__ */ jsxs9(Text9, { bold: true, color: theme.accentBright, children: [
9611
+ /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
9612
+ /* @__PURE__ */ jsxs8(Text8, { bold: true, color: theme.accentBright, children: [
9461
9613
  "ALERTS ",
9462
- /* @__PURE__ */ jsxs9(Text9, { color: theme.dim, children: [
9614
+ /* @__PURE__ */ jsxs8(Text8, { color: theme.dim, children: [
9463
9615
  "\u2014 notify on a signal spike (",
9464
9616
  alerts.length,
9465
9617
  ")"
9466
9618
  ] })
9467
9619
  ] }),
9468
- alerts.filter((rule) => inWindow({ kind: "alert", rule })).map((rule) => /* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
9469
- /* @__PURE__ */ jsx9(Text9, { color: keyOf(cur) === "alert:" + rule.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert", rule }) }),
9620
+ alerts.filter((rule) => inWindow({ kind: "alert", rule })).map((rule) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
9621
+ /* @__PURE__ */ jsx8(Text8, { color: keyOf(cur) === "alert:" + rule.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert", rule }) }),
9470
9622
  onOff(rule.enabled),
9471
- /* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: (" " + rule.signal).padEnd(11) }),
9472
- /* @__PURE__ */ jsx9(Text9, { children: `\u2265${rule.threshold}/${rule.windowSeconds}s ` }),
9473
- /* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: truncate2(chanOf(rule), cols - 26) })
9623
+ /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: (" " + rule.signal).padEnd(11) }),
9624
+ /* @__PURE__ */ jsx8(Text8, { children: `\u2265${rule.threshold}/${rule.windowSeconds}s ` }),
9625
+ /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate2(chanOf(rule), cols - 26) })
9474
9626
  ] }, rule.id)),
9475
- inWindow(rowsAll[rowsAll.length - 1]) ? /* @__PURE__ */ jsxs9(Text9, { children: [
9476
- /* @__PURE__ */ jsx9(Text9, { color: cur.kind === "alert-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert-add" }) }),
9477
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "+ add alert (enter \u2014 one channel: slack url, email or telegram chat id)" })
9627
+ inWindow(rowsAll[rowsAll.length - 1]) ? /* @__PURE__ */ jsxs8(Text8, { children: [
9628
+ /* @__PURE__ */ jsx8(Text8, { color: cur.kind === "alert-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert-add" }) }),
9629
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add alert (enter \u2014 one channel: slack url, email or telegram chat id)" })
9478
9630
  ] }) : null
9479
9631
  ] })
9480
9632
  ] }) });
@@ -9492,17 +9644,17 @@ var init_Settings = __esm({
9492
9644
  });
9493
9645
 
9494
9646
  // src/tui/App.tsx
9495
- import { Box as Box10, Text as Text10, useApp, useInput as useInput8 } from "ink";
9647
+ import { Box as Box9, Text as Text9, useApp, useInput as useInput8 } from "ink";
9496
9648
  import { useState as useState9 } from "react";
9497
- import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
9649
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
9498
9650
  function LiveHint() {
9499
- return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
9500
- /* @__PURE__ */ jsx10(Text10, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
9501
- /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
9502
- /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
9503
- /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsxs10(Text10, { children: [
9651
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
9652
+ /* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
9653
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
9654
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
9655
+ /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { children: [
9504
9656
  "press ",
9505
- /* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: "enter" }),
9657
+ /* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: "enter" }),
9506
9658
  " to go live"
9507
9659
  ] }) })
9508
9660
  ] });
@@ -9510,14 +9662,14 @@ function LiveHint() {
9510
9662
  function Banner({ cols }) {
9511
9663
  const wide = cols >= 82;
9512
9664
  if (!wide) {
9513
- return /* @__PURE__ */ jsxs10(Box10, { children: [
9514
- /* @__PURE__ */ jsx10(Text10, { bold: true, color: theme.accentBright, children: "SolonGate" }),
9515
- /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " \u2014 security control center" })
9665
+ return /* @__PURE__ */ jsxs9(Box9, { children: [
9666
+ /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate" }),
9667
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2014 security control center" })
9516
9668
  ] });
9517
9669
  }
9518
- return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
9519
- BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx10(Text10, { bold: true, color: BANNER_HEX[i], children: line }, i)),
9520
- /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
9670
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
9671
+ BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx9(Text9, { bold: true, color: BANNER_HEX[i], children: line }, i)),
9672
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
9521
9673
  ] });
9522
9674
  }
9523
9675
  function App() {
@@ -9546,31 +9698,31 @@ function App() {
9546
9698
  });
9547
9699
  const { cols, rows } = useTermSize();
9548
9700
  const current = SECTIONS[section];
9549
- if (help) return /* @__PURE__ */ jsx10(HelpOverlay, { cols, rows });
9701
+ if (help) return /* @__PURE__ */ jsx9(HelpOverlay, { cols, rows });
9550
9702
  if (current.label === "Live" && focus === "panel") {
9551
- return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx10(LivePanel, { active: true, focused: true }) });
9703
+ return /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx9(LivePanel, { active: true, focused: true }) });
9552
9704
  }
9553
9705
  const Panel = current.Panel;
9554
- return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
9555
- /* @__PURE__ */ jsx10(Banner, { cols }),
9556
- /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, flexGrow: 1, children: [
9557
- /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => /* @__PURE__ */ jsx10(Text10, { color: i === section ? theme.accentBright : void 0, bold: i === section, children: (i === section ? "\u25B8 " : " ") + s.label }, s.label)) }),
9558
- /* @__PURE__ */ jsx10(Box10, { flexGrow: 1, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, children: /* @__PURE__ */ jsx10(Panel, { active: focus === "panel" || current.label !== "Live", focused: focus === "panel" }) })
9706
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
9707
+ /* @__PURE__ */ jsx9(Banner, { cols }),
9708
+ /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexGrow: 1, children: [
9709
+ /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => /* @__PURE__ */ jsx9(Text9, { color: i === section ? theme.accentBright : void 0, bold: i === section, children: (i === section ? "\u25B8 " : " ") + s.label }, s.label)) }),
9710
+ /* @__PURE__ */ jsx9(Box9, { flexGrow: 1, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, children: /* @__PURE__ */ jsx9(Panel, { active: focus === "panel" || current.label !== "Live", focused: focus === "panel" }) })
9559
9711
  ] }),
9560
- /* @__PURE__ */ jsx10(Box10, { children: focus === "nav" ? /* @__PURE__ */ jsx10(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ["?", "help"], ["q", "quit"]] }) : /* @__PURE__ */ jsx10(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
9712
+ /* @__PURE__ */ jsx9(Box9, { children: focus === "nav" ? /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ["?", "help"], ["q", "quit"]] }) : /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
9561
9713
  ] });
9562
9714
  }
9563
9715
  function HelpOverlay({ cols, rows }) {
9564
- return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
9565
- /* @__PURE__ */ jsx10(Text10, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
9566
- /* @__PURE__ */ jsx10(Box10, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", marginBottom: 1, children: [
9567
- /* @__PURE__ */ jsx10(Text10, { bold: true, color: theme.accent, children: group }),
9568
- keys.map(([k, desc]) => /* @__PURE__ */ jsxs10(Text10, { children: [
9569
- /* @__PURE__ */ jsx10(Text10, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
9570
- /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: desc })
9716
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
9717
+ /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
9718
+ /* @__PURE__ */ jsx9(Box9, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, children: [
9719
+ /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accent, children: group }),
9720
+ keys.map(([k, desc]) => /* @__PURE__ */ jsxs9(Text9, { children: [
9721
+ /* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
9722
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: desc })
9571
9723
  ] }, k))
9572
9724
  ] }, group)) }),
9573
- /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "press any key to close" })
9725
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "press any key to close" })
9574
9726
  ] });
9575
9727
  }
9576
9728
  var SECTIONS, BANNER_HEX, HELP;
@@ -9584,7 +9736,6 @@ var init_App = __esm({
9584
9736
  init_Policies();
9585
9737
  init_RateLimit();
9586
9738
  init_Dlp();
9587
- init_Stats();
9588
9739
  init_Audit();
9589
9740
  init_Agents();
9590
9741
  init_Settings();
@@ -9595,7 +9746,6 @@ var init_App = __esm({
9595
9746
  { label: "Policies", Panel: PoliciesPanel },
9596
9747
  { label: "Rate Limit", Panel: RateLimitPanel },
9597
9748
  { label: "DLP", Panel: DlpPanel },
9598
- { label: "Stats", Panel: StatsPanel },
9599
9749
  { label: "Audit", Panel: AuditPanel },
9600
9750
  { label: "Settings", Panel: SettingsPanel }
9601
9751
  ];
@@ -9603,9 +9753,9 @@ var init_App = __esm({
9603
9753
  HELP = [
9604
9754
  ["Global", [["\u2191\u2193", "move between sections"], ["\u2192 / enter", "open a section"], ["\u2190 / esc", "back to the menu"], ["?", "this help"], ["q", "quit"]]],
9605
9755
  ["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)"]]],
9606
- ["Policies", [["\u2191\u2193", "browse / select"], ["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"], ["v", "versions / rollback"], ["s", "save"], ["x", "discard"]]],
9756
+ ["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"]]],
9607
9757
  ["Rate limit / DLP", [["\u2191\u2193", "field / pattern"], ["\u2190\u2192", "adjust (shift = \xB110)"], ["space", "toggle pattern"], ["m", "mode"], ["a / d", "add / remove custom"], ["g", "ghost mode"], ["P", "self-protection"], ["s", "save"]]],
9608
- ["Audit", [["\u2191\u2193", "select (list scrolls)"], ["enter", "full entry detail"], ["f / g", "decision / signal filter"], ["t / n / /", "tool / agent / search"], ["c", "clear filters"]]],
9758
+ ["Audit", [["v", "logs \u2194 sessions"], ["s", "source: cloud \u2194 local"], ["[ / ]", "prev / next page (500 each)"], ["\u2191\u2193", "select (list scrolls)"], ["enter", "full entry / session logs"], ["f / g", "decision / signal filter"], ["t / n / /", "tool / agent / search"], ["c", "clear filters"]]],
9609
9759
  ["Settings", [["\u2191\u2193", "move"], ["enter / space", "toggle \xB7 edit \xB7 add"], ["e", "webhook events"], ["d d", "delete"], ["r", "refresh"]]]
9610
9760
  ];
9611
9761
  }
@@ -9617,7 +9767,7 @@ __export(tui_exports, {
9617
9767
  launchTui: () => launchTui
9618
9768
  });
9619
9769
  import { render } from "ink";
9620
- import { jsx as jsx11 } from "react/jsx-runtime";
9770
+ import { jsx as jsx10 } from "react/jsx-runtime";
9621
9771
  async function launchTui() {
9622
9772
  if (!process.stdout.isTTY || !process.stdin.isTTY) {
9623
9773
  process.stderr.write(
@@ -9631,7 +9781,7 @@ async function launchTui() {
9631
9781
  }
9632
9782
  process.stdout.write("\x1B[?1049h\x1B[H");
9633
9783
  try {
9634
- const { waitUntilExit } = render(/* @__PURE__ */ jsx11(App, {}));
9784
+ const { waitUntilExit } = render(/* @__PURE__ */ jsx10(App, {}));
9635
9785
  await waitUntilExit();
9636
9786
  } finally {
9637
9787
  process.stdout.write("\x1B[?1049l");
@@ -10334,8 +10484,8 @@ var init_agents2 = __esm({
10334
10484
 
10335
10485
  // src/commands/doctor.ts
10336
10486
  import { existsSync as existsSync4, statSync as statSync2 } from "fs";
10337
- import { homedir as homedir5 } from "os";
10338
- import { join as join7 } from "path";
10487
+ import { homedir as homedir6 } from "os";
10488
+ import { join as join8 } from "path";
10339
10489
  async function run6(argv) {
10340
10490
  const { flags } = parse(argv);
10341
10491
  const json = flagBool(flags, "json");
@@ -10395,14 +10545,14 @@ var init_doctor = __esm({
10395
10545
  init_api_client();
10396
10546
  init_format();
10397
10547
  init_args();
10398
- LOCAL_LOG2 = join7(homedir5(), ".solongate", "local-logs", "solongate-audit.jsonl");
10548
+ LOCAL_LOG2 = join8(homedir6(), ".solongate", "local-logs", "solongate-audit.jsonl");
10399
10549
  }
10400
10550
  });
10401
10551
 
10402
10552
  // src/commands/watch.ts
10403
10553
  import { closeSync as closeSync2, existsSync as existsSync5, openSync as openSync2, readSync as readSync2, statSync as statSync3 } from "fs";
10404
- import { homedir as homedir6 } from "os";
10405
- import { join as join8 } from "path";
10554
+ import { homedir as homedir7 } from "os";
10555
+ import { join as join9 } from "path";
10406
10556
  function tailLocal(file, maxBytes = 131072) {
10407
10557
  try {
10408
10558
  const size = statSync3(file).size;
@@ -10509,7 +10659,7 @@ var init_watch = __esm({
10509
10659
  init_cli_utils();
10510
10660
  init_args();
10511
10661
  init_format();
10512
- LOCAL_LOG3 = join8(homedir6(), ".solongate", "local-logs", "solongate-audit.jsonl");
10662
+ LOCAL_LOG3 = join9(homedir7(), ".solongate", "local-logs", "solongate-audit.jsonl");
10513
10663
  trunc = (s, n) => s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
10514
10664
  time = (ms) => new Date(ms).toTimeString().slice(0, 8);
10515
10665
  }
@@ -10812,8 +10962,8 @@ __export(global_install_exports, {
10812
10962
  unlockProtected: () => unlockProtected
10813
10963
  });
10814
10964
  import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
10815
- import { resolve as resolve4, join as join9, dirname } from "path";
10816
- import { homedir as homedir7 } from "os";
10965
+ import { resolve as resolve4, join as join10, dirname } from "path";
10966
+ import { homedir as homedir8 } from "os";
10817
10967
  import { fileURLToPath } from "url";
10818
10968
  import { createInterface } from "readline";
10819
10969
  import { execFileSync as execFileSync2 } from "child_process";
@@ -10876,10 +11026,10 @@ function unlockFile(file) {
10876
11026
  function protectedTargets() {
10877
11027
  const p = globalPaths();
10878
11028
  return [
10879
- join9(p.hooksDir, "guard.mjs"),
10880
- join9(p.hooksDir, "audit.mjs"),
10881
- join9(p.hooksDir, "stop.mjs"),
10882
- join9(p.hooksDir, "shield.mjs"),
11029
+ join10(p.hooksDir, "guard.mjs"),
11030
+ join10(p.hooksDir, "audit.mjs"),
11031
+ join10(p.hooksDir, "stop.mjs"),
11032
+ join10(p.hooksDir, "shield.mjs"),
10883
11033
  p.configPath,
10884
11034
  p.settingsPath
10885
11035
  ];
@@ -10891,25 +11041,25 @@ function unlockProtected() {
10891
11041
  for (const f of protectedTargets()) unlockFile(f);
10892
11042
  }
10893
11043
  function globalPaths() {
10894
- const home = homedir7();
10895
- const sgDir = join9(home, ".solongate");
10896
- const hooksDir = join9(sgDir, "hooks");
10897
- const claudeDir = join9(home, ".claude");
11044
+ const home = homedir8();
11045
+ const sgDir = join10(home, ".solongate");
11046
+ const hooksDir = join10(sgDir, "hooks");
11047
+ const claudeDir = join10(home, ".claude");
10898
11048
  return {
10899
11049
  home,
10900
11050
  sgDir,
10901
11051
  hooksDir,
10902
11052
  claudeDir,
10903
- settingsPath: join9(claudeDir, "settings.json"),
10904
- backupPath: join9(claudeDir, "settings.solongate.bak"),
10905
- configPath: join9(sgDir, "cloud-guard.json")
11053
+ settingsPath: join10(claudeDir, "settings.json"),
11054
+ backupPath: join10(claudeDir, "settings.solongate.bak"),
11055
+ configPath: join10(sgDir, "cloud-guard.json")
10906
11056
  };
10907
11057
  }
10908
11058
  function readHook(filename) {
10909
- return readFileSync7(join9(HOOKS_DIR, filename), "utf-8");
11059
+ return readFileSync7(join10(HOOKS_DIR, filename), "utf-8");
10910
11060
  }
10911
11061
  function readGuard() {
10912
- const bundled = join9(HOOKS_DIR, "guard.bundled.mjs");
11062
+ const bundled = join10(HOOKS_DIR, "guard.bundled.mjs");
10913
11063
  return existsSync6(bundled) ? readFileSync7(bundled, "utf-8") : readHook("guard.mjs");
10914
11064
  }
10915
11065
  function ask(question) {
@@ -10964,7 +11114,7 @@ function shimTargets() {
10964
11114
  return [];
10965
11115
  }
10966
11116
  }
10967
- return [".bashrc", ".zshrc", ".profile"].map((f) => join9(homedir7(), f)).filter((f) => existsSync6(f));
11117
+ return [".bashrc", ".zshrc", ".profile"].map((f) => join10(homedir8(), f)).filter((f) => existsSync6(f));
10968
11118
  }
10969
11119
  function writeShimBlock(file, block2) {
10970
11120
  const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
@@ -11029,12 +11179,12 @@ async function runGlobalInstall(opts = {}) {
11029
11179
  mkdirSync4(p.hooksDir, { recursive: true });
11030
11180
  mkdirSync4(p.claudeDir, { recursive: true });
11031
11181
  unlockProtected();
11032
- writeFileSync4(join9(p.hooksDir, "guard.mjs"), readGuard());
11033
- writeFileSync4(join9(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
11034
- writeFileSync4(join9(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
11035
- writeFileSync4(join9(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
11182
+ writeFileSync4(join10(p.hooksDir, "guard.mjs"), readGuard());
11183
+ writeFileSync4(join10(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
11184
+ writeFileSync4(join10(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
11185
+ writeFileSync4(join10(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
11036
11186
  console.log(` Installed hooks \u2192 ${p.hooksDir}`);
11037
- installClaudeShim(join9(p.hooksDir, "shield.mjs"));
11187
+ installClaudeShim(join10(p.hooksDir, "shield.mjs"));
11038
11188
  writeFileSync4(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
11039
11189
  console.log(` Wrote ${p.configPath}`);
11040
11190
  let existing = {};
@@ -11050,9 +11200,9 @@ async function runGlobalInstall(opts = {}) {
11050
11200
  existing = {};
11051
11201
  }
11052
11202
  }
11053
- const guardAbs = join9(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
11054
- const auditAbs = join9(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
11055
- const stopAbs = join9(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
11203
+ const guardAbs = join10(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
11204
+ const auditAbs = join10(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
11205
+ const stopAbs = join10(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
11056
11206
  const nodeBin = process.execPath.replace(/\\/g, "/");
11057
11207
  const call = process.platform === "win32" ? "& " : "";
11058
11208
  const hookCmd = (script) => `${call}"${nodeBin}" "${script}" claude-code "Claude Code"`;
@@ -11246,9 +11396,9 @@ import { spawn as spawn3 } from "child_process";
11246
11396
  import { URL as URL2 } from "url";
11247
11397
  import { readFileSync as readFileSync8, existsSync as existsSync7, readdirSync, statSync as statSync4 } from "fs";
11248
11398
  import { resolve as resolve5 } from "path";
11249
- import { homedir as homedir8 } from "os";
11399
+ import { homedir as homedir9 } from "os";
11250
11400
  function findCacheFile() {
11251
- const dir = resolve5(homedir8(), ".solongate");
11401
+ const dir = resolve5(homedir9(), ".solongate");
11252
11402
  const envSel = process.env.SOLONGATE_AGENT_ID;
11253
11403
  if (envSel) {
11254
11404
  const f = resolve5(dir, ".policy-cache-" + envSel.replace(/[^a-zA-Z0-9_-]/g, "_") + ".json");
@@ -11563,8 +11713,8 @@ __export(logs_server_exports, {
11563
11713
  });
11564
11714
  import { createServer as createServer2 } from "http";
11565
11715
  import { readFileSync as readFileSync9, statSync as statSync5 } from "fs";
11566
- import { resolve as resolve6, join as join10, isAbsolute } from "path";
11567
- import { homedir as homedir9 } from "os";
11716
+ import { resolve as resolve6, join as join11, isAbsolute } from "path";
11717
+ import { homedir as homedir10 } from "os";
11568
11718
  import { readdirSync as readdirSync2 } from "fs";
11569
11719
  function allowedOrigins() {
11570
11720
  const base = [
@@ -11581,15 +11731,15 @@ function resolveLocalLogDir(rawPath) {
11581
11731
  const dir = String(rawPath || "").trim().replace(/[\\/]+$/, "");
11582
11732
  if (!dir) return null;
11583
11733
  if (isAbsolute(dir)) return dir;
11584
- return resolve6(homedir9(), ".solongate", "local-logs");
11734
+ return resolve6(homedir10(), ".solongate", "local-logs");
11585
11735
  }
11586
11736
  async function findLogDir() {
11587
- const base = resolve6(homedir9(), ".solongate");
11737
+ const base = resolve6(homedir10(), ".solongate");
11588
11738
  try {
11589
11739
  const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
11590
11740
  for (const f of files) {
11591
11741
  try {
11592
- const c2 = JSON.parse(readFileSync9(join10(base, f), "utf-8"));
11742
+ const c2 = JSON.parse(readFileSync9(join11(base, f), "utf-8"));
11593
11743
  const p = c2?.security?.localLogs?.path;
11594
11744
  if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
11595
11745
  } catch {
@@ -11598,7 +11748,7 @@ async function findLogDir() {
11598
11748
  } catch {
11599
11749
  }
11600
11750
  try {
11601
- const cfgRaw = readFileSync9(join10(base, "cloud-guard.json"), "utf-8");
11751
+ const cfgRaw = readFileSync9(join11(base, "cloud-guard.json"), "utf-8");
11602
11752
  const { apiKey, apiUrl } = JSON.parse(cfgRaw);
11603
11753
  if (apiKey) {
11604
11754
  const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
@@ -11627,7 +11777,7 @@ function setCors(req, res) {
11627
11777
  }
11628
11778
  function fileInfo(dir) {
11629
11779
  if (!dir) return { file: null, exists: false, size: 0, mtimeMs: 0 };
11630
- const file = join10(dir, LOG_FILENAME);
11780
+ const file = join11(dir, LOG_FILENAME);
11631
11781
  try {
11632
11782
  const st = statSync5(file);
11633
11783
  return { file, exists: true, size: st.size, mtimeMs: st.mtimeMs };
@@ -12078,7 +12228,7 @@ var init_inject = __esm({
12078
12228
  // src/create.ts
12079
12229
  var create_exports = {};
12080
12230
  import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync6, existsSync as existsSync9 } from "fs";
12081
- import { resolve as resolve8, join as join11 } from "path";
12231
+ import { resolve as resolve8, join as join12 } from "path";
12082
12232
  import { execSync as execSync2 } from "child_process";
12083
12233
  function withSpinner(message, fn) {
12084
12234
  const frames = ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"];
@@ -12158,7 +12308,7 @@ EXAMPLES
12158
12308
  }
12159
12309
  function createProject(dir, name, _policy) {
12160
12310
  writeFileSync6(
12161
- join11(dir, "package.json"),
12311
+ join12(dir, "package.json"),
12162
12312
  JSON.stringify(
12163
12313
  {
12164
12314
  name,
@@ -12188,7 +12338,7 @@ function createProject(dir, name, _policy) {
12188
12338
  ) + "\n"
12189
12339
  );
12190
12340
  writeFileSync6(
12191
- join11(dir, "tsconfig.json"),
12341
+ join12(dir, "tsconfig.json"),
12192
12342
  JSON.stringify(
12193
12343
  {
12194
12344
  compilerOptions: {
@@ -12208,9 +12358,9 @@ function createProject(dir, name, _policy) {
12208
12358
  2
12209
12359
  ) + "\n"
12210
12360
  );
12211
- mkdirSync5(join11(dir, "src"), { recursive: true });
12361
+ mkdirSync5(join12(dir, "src"), { recursive: true });
12212
12362
  writeFileSync6(
12213
- join11(dir, "src", "index.ts"),
12363
+ join12(dir, "src", "index.ts"),
12214
12364
  `#!/usr/bin/env node
12215
12365
 
12216
12366
  console.log = (...args: unknown[]) => {
@@ -12252,7 +12402,7 @@ console.log('Press Ctrl+C to stop.');
12252
12402
  `
12253
12403
  );
12254
12404
  writeFileSync6(
12255
- join11(dir, ".mcp.json"),
12405
+ join12(dir, ".mcp.json"),
12256
12406
  JSON.stringify(
12257
12407
  {
12258
12408
  mcpServers: {
@@ -12270,12 +12420,12 @@ console.log('Press Ctrl+C to stop.');
12270
12420
  ) + "\n"
12271
12421
  );
12272
12422
  writeFileSync6(
12273
- join11(dir, ".env"),
12423
+ join12(dir, ".env"),
12274
12424
  `SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
12275
12425
  `
12276
12426
  );
12277
12427
  writeFileSync6(
12278
- join11(dir, ".gitignore"),
12428
+ join12(dir, ".gitignore"),
12279
12429
  `node_modules/
12280
12430
  dist/
12281
12431
  *.solongate-backup