@solongate/proxy 0.81.69 → 0.81.70

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
@@ -6934,6 +6934,39 @@ var init_theme = __esm({
6934
6934
  // src/tui/components.tsx
6935
6935
  import { Box, Text } from "ink";
6936
6936
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6937
+ function PaneTitle({ label, extra, width: width2 }) {
6938
+ const tail = extra ? ` ${extra} ` : " ";
6939
+ const used = 2 + 1 + label.length + tail.length;
6940
+ return /* @__PURE__ */ jsxs(Text, { wrap: "truncate", children: [
6941
+ /* @__PURE__ */ jsx(Text, { color: theme.dim, children: "\u2500 " }),
6942
+ /* @__PURE__ */ jsxs(Text, { color: theme.accentBright, bold: true, children: [
6943
+ "\u258E",
6944
+ label
6945
+ ] }),
6946
+ /* @__PURE__ */ jsx(Text, { color: theme.dim, children: tail + "\u2500".repeat(Math.max(0, width2 - used)) })
6947
+ ] });
6948
+ }
6949
+ function StreamLine({ e, loc, selected }) {
6950
+ return /* @__PURE__ */ jsxs(Text, { wrap: "truncate", backgroundColor: selected ? "#1c2f63" : void 0, children: [
6951
+ /* @__PURE__ */ jsx(Text, { color: selected ? theme.accentBright : theme.dim, children: selected ? "\u25B8" : " " }),
6952
+ /* @__PURE__ */ jsxs(Text, { color: theme.dim, children: [
6953
+ "[",
6954
+ hhmmss(e.at),
6955
+ " "
6956
+ ] }),
6957
+ /* @__PURE__ */ jsx(Text, { color: loc ? theme.ok : "white", children: loc ? "LOC" : "CLD" }),
6958
+ /* @__PURE__ */ jsx(Text, { color: theme.dim, children: "] " }),
6959
+ /* @__PURE__ */ jsx(Text, { color: decisionColor(e.decision), bold: e.decision !== "ALLOW", children: e.decision.padEnd(6) }),
6960
+ /* @__PURE__ */ jsx(Text, { color: theme.accent, children: truncate2(e.tool, 12).padEnd(13) }),
6961
+ /* @__PURE__ */ jsx(Text, { color: theme.dim, children: (e.permission ?? "").padEnd(5) }),
6962
+ /* @__PURE__ */ jsx(Text, { color: e.evalMs != null && e.evalMs > 500 ? theme.warn : theme.dim, children: (e.evalMs != null ? `${e.evalMs}ms` : "\u2014").padEnd(7) }),
6963
+ /* @__PURE__ */ jsx(Text, { color: theme.dim, children: truncate2(e.agent ?? "-", 11).padEnd(12) }),
6964
+ /* @__PURE__ */ jsx(Text, { color: e.dlp ? theme.bad : theme.dim, bold: e.dlp, children: ("dlp:" + (e.dlp ? "yes" : "no")).padEnd(8) }),
6965
+ /* @__PURE__ */ jsx(Text, { color: e.burst ? theme.warn : theme.dim, bold: e.burst, children: ("rl:" + (e.burst ? "yes" : "no")).padEnd(7) }),
6966
+ e.rule && e.decision !== "ALLOW" ? /* @__PURE__ */ jsx(Text, { color: theme.bad, children: truncate2(e.rule, 14) + " " }) : null,
6967
+ /* @__PURE__ */ jsx(Text, { color: theme.dim, children: e.detail })
6968
+ ] });
6969
+ }
6937
6970
  function DataView({
6938
6971
  loading,
6939
6972
  error,
@@ -6968,10 +7001,15 @@ function KeyHints({ hints }) {
6968
7001
  i < hints.length - 1 ? " " : ""
6969
7002
  ] }, i)) });
6970
7003
  }
7004
+ var hhmmss;
6971
7005
  var init_components = __esm({
6972
7006
  "src/tui/components.tsx"() {
6973
7007
  "use strict";
6974
7008
  init_theme();
7009
+ hhmmss = (ts) => {
7010
+ const d = new Date(ts);
7011
+ return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
7012
+ };
6975
7013
  }
6976
7014
  });
6977
7015
 
@@ -7804,39 +7842,6 @@ function HBar({ label, value, max, width: width2, color }) {
7804
7842
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " " + value })
7805
7843
  ] });
7806
7844
  }
7807
- function PaneTitle({ label, extra, width: width2 }) {
7808
- const tail = extra ? ` ${extra} ` : " ";
7809
- const used = 2 + 1 + label.length + tail.length;
7810
- return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7811
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2500 " }),
7812
- /* @__PURE__ */ jsxs2(Text2, { color: theme.accentBright, bold: true, children: [
7813
- "\u258E",
7814
- label
7815
- ] }),
7816
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: tail + "\u2500".repeat(Math.max(0, width2 - used)) })
7817
- ] });
7818
- }
7819
- function StreamLine({ e, loc, selected }) {
7820
- return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", backgroundColor: selected ? "#1c2f63" : void 0, children: [
7821
- /* @__PURE__ */ jsx2(Text2, { color: selected ? theme.accentBright : theme.dim, children: selected ? "\u25B8" : " " }),
7822
- /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
7823
- "[",
7824
- hhmmss(e.at),
7825
- " "
7826
- ] }),
7827
- /* @__PURE__ */ jsx2(Text2, { color: loc ? theme.ok : "white", children: loc ? "LOC" : "CLD" }),
7828
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "] " }),
7829
- /* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), bold: e.decision !== "ALLOW", children: e.decision.padEnd(6) }),
7830
- /* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate2(e.tool, 12).padEnd(13) }),
7831
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.permission.padEnd(5) }),
7832
- /* @__PURE__ */ jsx2(Text2, { color: e.evalMs != null && e.evalMs > 500 ? theme.warn : theme.dim, children: (e.evalMs != null ? `${e.evalMs}ms` : "\u2014").padEnd(7) }),
7833
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: truncate2(e.agent ?? "-", 11).padEnd(12) }),
7834
- /* @__PURE__ */ jsx2(Text2, { color: e.dlp ? theme.bad : theme.dim, bold: e.dlp, children: ("dlp:" + (e.dlp ? "yes" : "no")).padEnd(8) }),
7835
- /* @__PURE__ */ jsx2(Text2, { color: e.burst ? theme.warn : theme.dim, bold: e.burst, children: ("rl:" + (e.burst ? "yes" : "no")).padEnd(7) }),
7836
- e.rule && e.decision !== "ALLOW" ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: truncate2(e.rule, 14) + " " }) : null,
7837
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.detail })
7838
- ] });
7839
- }
7840
7845
  function LivePanel({ active: active2 }) {
7841
7846
  const [s, setS] = useState2(null);
7842
7847
  const [lat, setLat] = useState2([]);
@@ -8725,7 +8730,7 @@ function LivePanel({ active: active2 }) {
8725
8730
  ] })
8726
8731
  ] });
8727
8732
  }
8728
- var CONFIG, SPIN, BG, DIM_FLOOR, RING, hhmmss, fmtUp, FILTERS, sessStatus, STATUS_STYLE, LIVE_HELP;
8733
+ var CONFIG, SPIN, BG, DIM_FLOOR, RING, fmtUp, FILTERS, sessStatus, STATUS_STYLE, LIVE_HELP;
8729
8734
  var init_Live = __esm({
8730
8735
  "src/tui/panels/Live.tsx"() {
8731
8736
  "use strict";
@@ -8734,16 +8739,13 @@ var init_Live = __esm({
8734
8739
  init_config2();
8735
8740
  init_notify();
8736
8741
  init_hooks();
8742
+ init_components();
8737
8743
  init_theme();
8738
8744
  CONFIG = loadConfig();
8739
8745
  SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
8740
8746
  BG = "#12234f";
8741
8747
  DIM_FLOOR = "#233457";
8742
8748
  RING = join9(process.cwd(), ".solongate", ".eval-ring.jsonl");
8743
- hhmmss = (ts) => {
8744
- const d = new Date(ts);
8745
- return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
8746
- };
8747
8749
  fmtUp = (ms) => {
8748
8750
  const s = Math.floor(ms / 1e3);
8749
8751
  const p = (n) => String(n).padStart(2, "0");
@@ -9433,7 +9435,7 @@ import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
9433
9435
  import { homedir as homedir8 } from "os";
9434
9436
  import { join as join10 } from "path";
9435
9437
  import { useState as useState6 } from "react";
9436
- import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
9438
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
9437
9439
  function loadLocalRows() {
9438
9440
  return parseLocalLines(tailLines(LOCAL_LOG, LOCAL_MAX_BYTES)).map((j, i) => {
9439
9441
  const rs = reasonSignals(j.reason);
@@ -9553,10 +9555,6 @@ function AuditPanel({ active: active2, focused }) {
9553
9555
  const sq = sessSearch.trim().toLowerCase();
9554
9556
  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);
9555
9557
  const currentSess = sessionsFiltered[Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1))];
9556
- const sessionQ = useLoader(
9557
- () => view === "detail" && current?.session && source === "cloud" ? api.audit.list({ session_id: current.session, limit: 40 }) : Promise.resolve(null),
9558
- [view, current?.session, source]
9559
- );
9560
9558
  const logsLoading = (source === "cloud" ? cloudQ.loading : localQ.loading) && !editing;
9561
9559
  const sessLoading = source === "cloud" ? agentsQ.loading : localQ.loading;
9562
9560
  const doDelete = (kind) => {
@@ -9782,61 +9780,56 @@ function AuditPanel({ active: active2, focused }) {
9782
9780
  ] });
9783
9781
  }
9784
9782
  if (view === "detail" && current) {
9785
- const sessionCalls = source === "cloud" ? (sessionQ.data?.entries ?? []).map(cloudRow) : localAll.filter((r) => r.session && r.session === current.session).slice(0, 40);
9786
- const bodyW = Math.max(20, cols - 2);
9787
- const reasonLines = wrapLines("reason: " + (current.reason ?? "\u2014"), bodyW);
9788
- const argLines = current.args ? wrapLines(prettyJson(current.args), bodyW) : ["(no arguments recorded)"];
9789
- const sessionRows = current.session ? Math.min(4, sessionCalls.length) + 2 : 1;
9790
- const reasonShown = reasonLines.slice(0, 4);
9791
- const argBudget = Math.max(3, rows - 2 - 5 - reasonShown.length - 1 - sessionRows - 1 - (frozen ? 1 : 0));
9792
- const maxScroll = Math.max(0, argLines.length - argBudget);
9783
+ const e = current;
9784
+ const loc = source === "local";
9785
+ const bodyW = Math.max(20, cols - 4);
9786
+ const innerW = cols - 2;
9787
+ const contentLines = [];
9788
+ if (e.reason && e.decision !== "ALLOW") contentLines.push(...wrapLines("reason: " + e.reason, bodyW), "");
9789
+ const argsText = e.args ? prettyJson(e.args) : e.reason && e.decision === "ALLOW" ? e.reason : "(no arguments recorded)";
9790
+ contentLines.push(...wrapLines(argsText, bodyW));
9791
+ const bodyRows = Math.max(4, rows - 7 - (frozen ? 1 : 0));
9792
+ const maxScroll = Math.max(0, contentLines.length - bodyRows);
9793
9793
  const off = Math.min(detailScroll, maxScroll);
9794
- const argWin = argLines.slice(off, off + argBudget);
9794
+ const win = contentLines.slice(off, off + bodyRows);
9795
9795
  return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
9796
9796
  copyBanner,
9797
- /* @__PURE__ */ jsxs6(Box6, { children: [
9798
- /* @__PURE__ */ jsx6(Text6, { color: decisionColor(current.decision), bold: true, children: current.decision }),
9799
- /* @__PURE__ */ jsx6(Text6, { color: theme.accent, children: " " + current.tool }),
9800
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " " + current.permission + " \xB7 " + current.trust + " " }),
9801
- srcChip
9797
+ /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
9798
+ /* @__PURE__ */ jsx6(Text6, { backgroundColor: BG2, color: "white", bold: true, children: " ENTRY " }),
9799
+ /* @__PURE__ */ jsx6(Text6, { color: decisionColor(e.decision), bold: true, children: " " + e.decision }),
9800
+ /* @__PURE__ */ jsx6(Text6, { color: theme.accent, bold: true, children: " " + e.tool }),
9801
+ /* @__PURE__ */ jsx6(Text6, { color: loc ? theme.ok : "white", children: " " + (loc ? "LOC" : "CLD") }),
9802
+ e.dlp.length ? /* @__PURE__ */ jsx6(Text6, { color: theme.bad, children: " DLP!" }) : null,
9803
+ e.burst ? /* @__PURE__ */ jsx6(Text6, { color: theme.warn, children: " BURST" }) : null,
9804
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \u2190 back" })
9802
9805
  ] }),
9803
- reasonShown.map((l, i) => /* @__PURE__ */ jsx6(Text6, { wrap: "truncate", children: i === 0 ? /* @__PURE__ */ jsxs6(Fragment4, { children: [
9804
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "reason " }),
9805
- /* @__PURE__ */ jsx6(Text6, { children: l.slice("reason: ".length) })
9806
- ] }) : /* @__PURE__ */ jsx6(Text6, { children: " " + l }) }, "r" + i)),
9807
- /* @__PURE__ */ jsx6(Detail, { label: "rule", value: current.rule ?? "\u2014" }),
9808
- /* @__PURE__ */ jsx6(Detail, { label: "agent", value: current.agent ?? "\u2014" }),
9809
- /* @__PURE__ */ jsx6(Detail, { label: "session", value: current.session ?? "\u2014" }),
9810
- /* @__PURE__ */ jsx6(Detail, { label: "dlp", value: current.dlp.length ? current.dlp.join(", ") : "none", color: current.dlp.length ? theme.bad : void 0 }),
9811
- /* @__PURE__ */ jsx6(Detail, { label: "ratelimit", value: current.burst ? "BURST \u2014 over the limit for this agent" : "no", color: current.burst ? theme.bad : void 0 }),
9812
- /* @__PURE__ */ jsx6(Detail, { label: "when", value: new Date(current.at).toLocaleString() + (current.evalMs != null ? ` \xB7 eval ${current.evalMs}ms` : "") }),
9813
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: `arguments \u2014 full \xB7 ${argLines.length} lines${maxScroll ? ` \xB7 \u25BC${maxScroll - off} more \xB7 \u2191\u2193 scroll` : ""}` }),
9814
- /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", height: argBudget, overflow: "hidden", children: argWin.map((l, i) => /* @__PURE__ */ jsx6(Text6, { wrap: "truncate", children: l || " " }, off + i)) }),
9815
- current.session ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
9816
- /* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
9817
- "Session \xB7 ",
9818
- sessionCalls.length,
9819
- " calls"
9820
- ] }),
9821
- /* @__PURE__ */ jsx6(
9822
- Table,
9823
- {
9824
- columns: [
9825
- { header: "DECISION", width: 9 },
9826
- { header: "TOOL", width: 20 },
9827
- { header: "REASON", width: Math.max(16, cols - 48) },
9828
- { header: "WHEN", width: 6 }
9829
- ],
9830
- rows: sessionCalls.slice(0, Math.max(1, sessionRows - 2)).map((e) => [
9831
- { value: e.decision, color: decisionColor(e.decision) },
9832
- { value: truncate2(e.tool, 20), color: theme.accent },
9833
- { value: truncate2(e.reason ?? "\u2014", Math.max(16, cols - 48)) },
9834
- { value: ago(e.at), dim: true }
9835
- ])
9836
- }
9837
- )
9838
- ] }) : /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "(no session id)" }),
9839
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "\u2191\u2193 scroll arguments \xB7 \u2190/esc back to logs" })
9806
+ /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
9807
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "\u2502 when " }),
9808
+ /* @__PURE__ */ jsx6(Text6, { children: new Date(e.at).toLocaleString() }),
9809
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \u2502 perm " }),
9810
+ /* @__PURE__ */ jsx6(Text6, { children: e.permission || "\u2014" }),
9811
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \u2502 trust " }),
9812
+ /* @__PURE__ */ jsx6(Text6, { children: e.trust || "\u2014" }),
9813
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \u2502 eval " }),
9814
+ /* @__PURE__ */ jsx6(Text6, { color: e.evalMs != null && e.evalMs > 500 ? theme.warn : void 0, children: e.evalMs != null ? `${e.evalMs}ms` : "\u2014" }),
9815
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \u2502 agent " }),
9816
+ /* @__PURE__ */ jsx6(Text6, { children: e.agent ?? "\u2014" }),
9817
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \u2502" })
9818
+ ] }),
9819
+ /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
9820
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "\u2502 session " }),
9821
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: e.session ?? "\u2014" }),
9822
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \u2502 rule " }),
9823
+ /* @__PURE__ */ jsx6(Text6, { color: e.rule && e.decision !== "ALLOW" ? theme.bad : theme.dim, children: e.rule ?? "\u2014" }),
9824
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \u2502" })
9825
+ ] }),
9826
+ /* @__PURE__ */ jsx6(PaneTitle, { label: "FULL CONTENT", extra: `${contentLines.length} lines${maxScroll ? ` \xB7 \u25BC${maxScroll - off} more \xB7 \u2191\u2193 scroll` : ""} \xB7 space copy \xB7 \u2190 back`, width: innerW }),
9827
+ /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", height: bodyRows, overflow: "hidden", children: win.map((l, i) => /* @__PURE__ */ jsx6(Text6, { wrap: "truncate", children: l || " " }, off + i)) }),
9828
+ /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
9829
+ /* @__PURE__ */ jsx6(Text6, { backgroundColor: BG2, color: "white", bold: true, children: " ENTRY " }),
9830
+ /* @__PURE__ */ jsx6(Text6, { backgroundColor: "#0b1530", color: "white", children: ` ${e.id} ` }),
9831
+ /* @__PURE__ */ jsx6(Text6, { backgroundColor: BG2, color: "white", children: " \u2191\u2193 scroll \xB7 space copy \xB7 ? all keys \xB7 \u2190 back \xB7 esc menu " })
9832
+ ] })
9840
9833
  ] });
9841
9834
  }
9842
9835
  const chip = (label, val, on) => /* @__PURE__ */ jsxs6(Text6, { children: [
@@ -9923,7 +9916,6 @@ function AuditPanel({ active: active2, focused }) {
9923
9916
  const maxStart = Math.max(0, pageRows.length - listRows);
9924
9917
  const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
9925
9918
  const windowed = pageRows.slice(start, start + listRows);
9926
- const reasonW = Math.min(58, Math.max(12, cols - 71));
9927
9919
  return /* @__PURE__ */ jsx6(DataView, { loading: logsLoading && !frozen, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
9928
9920
  strip,
9929
9921
  copyBanner,
@@ -9959,21 +9951,7 @@ function AuditPanel({ active: active2, focused }) {
9959
9951
  )
9960
9952
  ] }) : null,
9961
9953
  /* @__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` : ""}` }),
9962
- /* @__PURE__ */ jsx6(
9963
- Table,
9964
- {
9965
- columns: [
9966
- { header: "", width: 2 },
9967
- { header: "DECISION", width: 9 },
9968
- { header: "TOOL", width: 18 },
9969
- { header: "AGENT", width: 14 },
9970
- { header: "REASON", width: reasonW },
9971
- { header: "SIGNAL", width: 8 },
9972
- { header: "WHEN", width: 6 }
9973
- ],
9974
- rows: windowed.map((e, i) => rowFor(e, start + i === selClamped && focused, reasonW))
9975
- }
9976
- ),
9954
+ /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", overflow: "hidden", children: windowed.map((e, i) => /* @__PURE__ */ jsx6(StreamLine, { e: toStream(e), loc: source === "local", selected: start + i === selClamped && focused }, e.id)) }),
9977
9955
  pageRows.length === 0 ? /* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
9978
9956
  "(no entries",
9979
9957
  source === "local" ? " in the local file" : "",
@@ -9981,25 +9959,7 @@ function AuditPanel({ active: active2, focused }) {
9981
9959
  ] }) : null
9982
9960
  ] }) });
9983
9961
  }
9984
- function rowFor(e, active2, reasonW) {
9985
- const sig = [e.dlp.length ? `DLP${e.dlp.length > 1 ? e.dlp.length : ""}` : "", e.burst ? "RL" : ""].filter(Boolean).join(" ");
9986
- return [
9987
- { value: active2 ? "\u25B8" : "", color: theme.accentBright },
9988
- { value: e.decision, color: decisionColor(e.decision) },
9989
- { value: truncate2(e.tool, 18), color: theme.accent },
9990
- { value: truncate2(e.agent ?? "\u2014", 14), dim: true },
9991
- { value: truncate2(e.reason ?? e.args ?? "\u2014", reasonW) },
9992
- { value: sig || "\xB7", color: sig ? theme.bad : void 0, dim: !sig },
9993
- { value: ago(e.at), dim: true }
9994
- ];
9995
- }
9996
- function Detail({ label, value, color }) {
9997
- return /* @__PURE__ */ jsxs6(Box6, { children: [
9998
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: label.padEnd(9) }),
9999
- /* @__PURE__ */ jsx6(Text6, { color, wrap: "truncate", children: value })
10000
- ] });
10001
- }
10002
- var DECISIONS, SIGNALS, SESS_STATUS, PAGE, LOCAL_MAX_BYTES, cloudRow, AUDIT_HELP, sessStatus2, STATUS_DOT;
9962
+ var DECISIONS, SIGNALS, SESS_STATUS, PAGE, LOCAL_MAX_BYTES, BG2, toStream, cloudRow, AUDIT_HELP, sessStatus2, STATUS_DOT;
10003
9963
  var init_Audit = __esm({
10004
9964
  "src/tui/panels/Audit.tsx"() {
10005
9965
  "use strict";
@@ -10013,6 +9973,19 @@ var init_Audit = __esm({
10013
9973
  SESS_STATUS = [void 0, "active", "idle", "ended"];
10014
9974
  PAGE = 500;
10015
9975
  LOCAL_MAX_BYTES = 16 * 1024 * 1024;
9976
+ BG2 = "#12234f";
9977
+ toStream = (e) => ({
9978
+ at: e.at,
9979
+ tool: e.tool,
9980
+ decision: e.decision,
9981
+ permission: (e.permission ?? "").slice(0, 4),
9982
+ detail: (e.args ?? e.reason ?? "").replace(/\s+/g, " "),
9983
+ dlp: e.dlp.length > 0,
9984
+ burst: e.burst,
9985
+ agent: e.agent,
9986
+ evalMs: e.evalMs,
9987
+ rule: e.rule
9988
+ });
10016
9989
  cloudRow = (e) => {
10017
9990
  const rs = reasonSignals(e.reason);
10018
9991
  return {
@@ -10070,7 +10043,7 @@ var init_Audit = __esm({
10070
10043
  ["esc", "back to the menu"]
10071
10044
  ]
10072
10045
  ],
10073
- ["Entry detail", [["\u2191\u2193 / PgUp PgDn", "scroll the arguments"], ["\u2190 / esc", "back to the list"]]]
10046
+ ["Entry (full content)", [["\u2191\u2193 / PgUp PgDn", "scroll the reason + arguments"], ["space", "copy mode (freeze, then select)"], ["\u2190 / esc", "back to the list"]]]
10074
10047
  ];
10075
10048
  sessStatus2 = (lastAt) => Date.now() - lastAt < 6e4 ? "active" : Date.now() - lastAt < 3e5 ? "idle" : "ended";
10076
10049
  STATUS_DOT = {
@@ -10081,11 +10054,263 @@ var init_Audit = __esm({
10081
10054
  }
10082
10055
  });
10083
10056
 
10057
+ // src/global-install.ts
10058
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync9, existsSync as existsSync4, mkdirSync as mkdirSync8, rmSync as rmSync2 } from "fs";
10059
+ import { resolve as resolve4, join as join11, dirname as dirname3 } from "path";
10060
+ import { homedir as homedir9 } from "os";
10061
+ import { fileURLToPath as fileURLToPath3 } from "url";
10062
+ import { createInterface } from "readline";
10063
+ import { execFileSync as execFileSync2 } from "child_process";
10064
+ function lockFile(file) {
10065
+ if (!existsSync4(file)) return;
10066
+ try {
10067
+ if (process.platform === "win32") {
10068
+ try {
10069
+ execFileSync2("icacls", [file, "/deny", "*S-1-1-0:(WD,AD,DC,DE)"], { stdio: "ignore" });
10070
+ } catch {
10071
+ }
10072
+ try {
10073
+ execFileSync2("attrib", ["+R", file], { stdio: "ignore" });
10074
+ } catch {
10075
+ }
10076
+ } else if (process.platform === "darwin") {
10077
+ try {
10078
+ execFileSync2("chflags", ["uchg", file], { stdio: "ignore" });
10079
+ } catch {
10080
+ }
10081
+ } else {
10082
+ try {
10083
+ execFileSync2("chattr", ["+i", file], { stdio: "ignore" });
10084
+ } catch {
10085
+ }
10086
+ }
10087
+ } catch {
10088
+ }
10089
+ }
10090
+ function unlockFile(file) {
10091
+ if (!existsSync4(file)) return;
10092
+ try {
10093
+ if (process.platform === "win32") {
10094
+ try {
10095
+ execFileSync2("icacls", [file, "/remove:d", "*S-1-1-0"], { stdio: "ignore" });
10096
+ } catch {
10097
+ }
10098
+ try {
10099
+ execFileSync2("icacls", [file, "/reset"], { stdio: "ignore" });
10100
+ } catch {
10101
+ }
10102
+ try {
10103
+ execFileSync2("attrib", ["-R", file], { stdio: "ignore" });
10104
+ } catch {
10105
+ }
10106
+ } else if (process.platform === "darwin") {
10107
+ try {
10108
+ execFileSync2("chflags", ["nouchg", file], { stdio: "ignore" });
10109
+ } catch {
10110
+ }
10111
+ } else {
10112
+ try {
10113
+ execFileSync2("chattr", ["-i", file], { stdio: "ignore" });
10114
+ } catch {
10115
+ }
10116
+ }
10117
+ } catch {
10118
+ }
10119
+ }
10120
+ function protectedTargets() {
10121
+ const p = globalPaths();
10122
+ return [
10123
+ join11(p.hooksDir, "guard.mjs"),
10124
+ join11(p.hooksDir, "audit.mjs"),
10125
+ join11(p.hooksDir, "stop.mjs"),
10126
+ join11(p.hooksDir, "shield.mjs"),
10127
+ p.configPath,
10128
+ p.settingsPath
10129
+ ];
10130
+ }
10131
+ function lockProtected() {
10132
+ for (const f of protectedTargets()) lockFile(f);
10133
+ }
10134
+ function unlockProtected() {
10135
+ for (const f of protectedTargets()) unlockFile(f);
10136
+ }
10137
+ function globalPaths() {
10138
+ const home = homedir9();
10139
+ const sgDir = join11(home, ".solongate");
10140
+ const hooksDir = join11(sgDir, "hooks");
10141
+ const claudeDir = join11(home, ".claude");
10142
+ return {
10143
+ home,
10144
+ sgDir,
10145
+ hooksDir,
10146
+ claudeDir,
10147
+ settingsPath: join11(claudeDir, "settings.json"),
10148
+ backupPath: join11(claudeDir, "settings.solongate.bak"),
10149
+ configPath: join11(sgDir, "cloud-guard.json")
10150
+ };
10151
+ }
10152
+ function clearGuardUpdateCheck() {
10153
+ try {
10154
+ rmSync2(join11(globalPaths().sgDir, ".hook-update-check"), { force: true });
10155
+ return true;
10156
+ } catch {
10157
+ return false;
10158
+ }
10159
+ }
10160
+ function readHook(filename) {
10161
+ return readFileSync9(join11(HOOKS_DIR, filename), "utf-8");
10162
+ }
10163
+ function readGuard() {
10164
+ const bundled = join11(HOOKS_DIR, "guard.bundled.mjs");
10165
+ return existsSync4(bundled) ? readFileSync9(bundled, "utf-8") : readHook("guard.mjs");
10166
+ }
10167
+ function ask(question) {
10168
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
10169
+ return new Promise((res) => rl.question(question, (a) => {
10170
+ rl.close();
10171
+ res(a.trim());
10172
+ }));
10173
+ }
10174
+ function escapeRe(s) {
10175
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
10176
+ }
10177
+ function resolveRealClaude() {
10178
+ try {
10179
+ const finder = process.platform === "win32" ? "where" : "which";
10180
+ const out2 = execFileSync2(finder, ["claude"], { encoding: "utf-8" }).split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
10181
+ if (process.platform === "win32") {
10182
+ const low = (s) => s.toLowerCase();
10183
+ return out2.find((l) => low(l).endsWith(".cmd")) || out2.find((l) => low(l).endsWith(".exe")) || out2.find((l) => low(l).endsWith(".bat")) || out2[0] || null;
10184
+ }
10185
+ return out2[0] || null;
10186
+ } catch {
10187
+ return null;
10188
+ }
10189
+ }
10190
+ function shimTargets() {
10191
+ if (process.platform === "win32") {
10192
+ try {
10193
+ const prof = execFileSync2("powershell", ["-NoProfile", "-Command", "$PROFILE.CurrentUserAllHosts"], { encoding: "utf-8" }).trim();
10194
+ return prof ? [prof] : [];
10195
+ } catch {
10196
+ return [];
10197
+ }
10198
+ }
10199
+ return [".bashrc", ".zshrc", ".profile"].map((f) => join11(homedir9(), f)).filter((f) => existsSync4(f));
10200
+ }
10201
+ function writeShimBlock(file, block2) {
10202
+ const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
10203
+ let content = existsSync4(file) ? readFileSync9(file, "utf-8") : "";
10204
+ content = content.replace(re, "");
10205
+ if (block2) {
10206
+ if (content.length && !content.endsWith("\n")) content += "\n";
10207
+ content += block2 + "\n";
10208
+ }
10209
+ mkdirSync8(dirname3(file), { recursive: true });
10210
+ writeFileSync9(file, content);
10211
+ }
10212
+ function installClaudeShim(shieldPath) {
10213
+ const real = resolveRealClaude();
10214
+ if (!real) {
10215
+ console.log(" (Claude Code not found on PATH \u2014 skipped auto-shield. Install it, then re-run `login`.)");
10216
+ return;
10217
+ }
10218
+ const node = process.execPath.replace(/\\/g, "/");
10219
+ const shield = shieldPath.replace(/\\/g, "/");
10220
+ const win = process.platform === "win32";
10221
+ const block2 = win ? `${SHIM_BEGIN}
10222
+ function claude { & "${node}" "${shield}" -- "${real}" @args }
10223
+ ${SHIM_END}` : `${SHIM_BEGIN}
10224
+ claude() { "${node}" "${shield}" -- "${real}" "$@"; }
10225
+ ${SHIM_END}`;
10226
+ const targets = shimTargets();
10227
+ if (targets.length === 0) return;
10228
+ for (const file of targets) {
10229
+ try {
10230
+ writeShimBlock(file, block2);
10231
+ } catch {
10232
+ }
10233
+ }
10234
+ }
10235
+ async function runGlobalInstall2(opts = {}) {
10236
+ const p = globalPaths();
10237
+ let apiKey = opts.apiKey || process.env["SOLONGATE_API_KEY"] || "";
10238
+ if (!apiKey || apiKey === "sg_live_your_key_here") {
10239
+ try {
10240
+ const cfg = JSON.parse(readFileSync9(p.configPath, "utf-8"));
10241
+ if (cfg && typeof cfg.apiKey === "string") apiKey = cfg.apiKey;
10242
+ } catch {
10243
+ }
10244
+ }
10245
+ if (!apiKey || apiKey === "sg_live_your_key_here") {
10246
+ apiKey = await ask(" Enter your SolonGate API key (sg_live_\u2026 from https://dashboard.solongate.com): ");
10247
+ }
10248
+ if (!apiKey.startsWith("sg_live_") && !apiKey.startsWith("sg_test_")) {
10249
+ console.log(" Invalid API key. Must start with sg_live_ or sg_test_");
10250
+ process.exit(1);
10251
+ }
10252
+ const apiUrl = opts.apiUrl || process.env["SOLONGATE_API_URL"] || "https://api.solongate.com";
10253
+ mkdirSync8(p.hooksDir, { recursive: true });
10254
+ mkdirSync8(p.claudeDir, { recursive: true });
10255
+ unlockProtected();
10256
+ writeFileSync9(join11(p.hooksDir, "guard.mjs"), readGuard());
10257
+ writeFileSync9(join11(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
10258
+ writeFileSync9(join11(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
10259
+ writeFileSync9(join11(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
10260
+ console.log(` Installed hooks \u2192 ${p.hooksDir}`);
10261
+ installClaudeShim(join11(p.hooksDir, "shield.mjs"));
10262
+ writeFileSync9(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
10263
+ console.log(` Wrote ${p.configPath}`);
10264
+ let existing = {};
10265
+ if (existsSync4(p.settingsPath)) {
10266
+ const raw = readFileSync9(p.settingsPath, "utf-8");
10267
+ if (!existsSync4(p.backupPath)) {
10268
+ writeFileSync9(p.backupPath, raw);
10269
+ console.log(` Backed up existing settings \u2192 ${p.backupPath}`);
10270
+ }
10271
+ try {
10272
+ existing = JSON.parse(raw);
10273
+ } catch {
10274
+ existing = {};
10275
+ }
10276
+ }
10277
+ const guardAbs = join11(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
10278
+ const auditAbs = join11(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
10279
+ const stopAbs = join11(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
10280
+ const nodeBin = process.execPath.replace(/\\/g, "/");
10281
+ const call = process.platform === "win32" ? "& " : "";
10282
+ const hookCmd = (script) => `${call}"${nodeBin}" "${script}" claude-code "Claude Code"`;
10283
+ const merged = {
10284
+ ...existing,
10285
+ hooks: {
10286
+ PreToolUse: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(guardAbs) }] }],
10287
+ PostToolUse: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(auditAbs) }] }],
10288
+ Stop: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(stopAbs) }] }]
10289
+ }
10290
+ };
10291
+ writeFileSync9(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
10292
+ console.log(` Registered global hooks \u2192 ${p.settingsPath}`);
10293
+ if (process.env["SOLONGATE_OS_LOCK"] === "1") {
10294
+ lockProtected();
10295
+ console.log(" Locked protection files (OS-level read-only/immutable).");
10296
+ }
10297
+ }
10298
+ var __dirname, HOOKS_DIR, SHIM_BEGIN, SHIM_END;
10299
+ var init_global_install = __esm({
10300
+ "src/global-install.ts"() {
10301
+ "use strict";
10302
+ __dirname = dirname3(fileURLToPath3(import.meta.url));
10303
+ HOOKS_DIR = resolve4(__dirname, "..", "hooks");
10304
+ SHIM_BEGIN = "# >>> SolonGate shield (auto secret redaction) >>>";
10305
+ SHIM_END = "# <<< SolonGate shield <<<";
10306
+ }
10307
+ });
10308
+
10084
10309
  // src/tui/panels/Settings.tsx
10085
10310
  import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
10086
10311
  import TextInput5 from "ink-text-input";
10087
10312
  import { useEffect as useEffect6, useRef as useRef3, useState as useState7 } from "react";
10088
- import { Fragment as Fragment5, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
10313
+ import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
10089
10314
  function SettingsPanel({
10090
10315
  active: active2,
10091
10316
  focused,
@@ -10249,6 +10474,16 @@ function SettingsPanel({
10249
10474
  setMsg({ text: `viewing ${acctLabel(r.acc)}`, level: "ok" });
10250
10475
  } else if (r.kind === "acct-add") {
10251
10476
  beginLogin();
10477
+ } else if (r.kind === "guard") {
10478
+ if (!guard) return;
10479
+ if (guard.up_to_date) {
10480
+ setMsg({ text: `guard already on the latest hook (v${guard.latest})`, level: "ok" });
10481
+ return;
10482
+ }
10483
+ const armed = clearGuardUpdateCheck();
10484
+ setMsg(
10485
+ armed ? { text: `\u2713 update armed. now make your agent RUN any command (e.g. "echo ok"), not just chat; the guard installs v${guard.latest} on the next executed command`, level: "ok" } : { text: "\u2717 could not arm the update (check ~/.solongate permissions)", level: "bad" }
10486
+ );
10252
10487
  } else if (r.kind === "self") {
10253
10488
  if (!selfProt) return;
10254
10489
  run12(selfProt.enabled ? "self-protection disabled" : "self-protection enabled", () => api.settings.setSelfProtection(!selfProt.enabled), selfQ.reload);
@@ -10487,9 +10722,9 @@ function SettingsPanel({
10487
10722
  return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
10488
10723
  cursor(r),
10489
10724
  /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "guard".padEnd(11) }),
10490
- guard ? /* @__PURE__ */ jsxs7(Fragment5, { children: [
10725
+ guard ? /* @__PURE__ */ jsxs7(Fragment4, { children: [
10491
10726
  /* @__PURE__ */ jsx7(Text7, { color: guard.up_to_date ? theme.ok : theme.warn, children: `v${guard.installed ?? "?"}` }),
10492
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: guard.up_to_date ? " (latest)" : ` \u2192 v${guard.latest} available` }),
10727
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: guard.up_to_date ? " (latest)" : ` \u2192 v${guard.latest} available \xB7 enter to update` }),
10493
10728
  /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: ` \xB7 ${guard.device_count} device${guard.device_count === 1 ? "" : "s"}${guard.outdated_count ? ` \xB7 ${guard.outdated_count} outdated` : ""}` })
10494
10729
  ] }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "loading\u2026" })
10495
10730
  ] });
@@ -10555,7 +10790,7 @@ function SettingsPanel({
10555
10790
  const sectionOf = (r) => r.kind === "acct" || r.kind === "acct-add" ? "ACCOUNTS" : r.kind === "guard" || r.kind === "self" ? "PROTECTION" : r.kind === "ll-enabled" || r.kind === "ll-path" || r.kind === "ll-server" ? "LOCAL LOGS" : r.kind === "wh" || r.kind === "wh-add" ? "WEBHOOKS" : "ALERTS";
10556
10791
  const SECTION_DESC = {
10557
10792
  ACCOUNTS: `on this device (${accounts.length}) \xB7 \u25CF viewing \xB7 ACTIVE = guard key \xB7 x removes`,
10558
- PROTECTION: "guard hook version + self-protection toggle",
10793
+ PROTECTION: "guard hook version (enter to update) + self-protection toggle",
10559
10794
  "LOCAL LOGS": "mirror every decision to a file + dashboard link",
10560
10795
  WEBHOOKS: `POST events to a URL (${webhooks.length}) \xB7 t tests`,
10561
10796
  ALERTS: `one email + one telegram (${alerts.length}) \xB7 enter edits \xB7 m on/off`
@@ -10617,6 +10852,7 @@ var init_Settings = __esm({
10617
10852
  init_api_client();
10618
10853
  init_client();
10619
10854
  init_device_login();
10855
+ init_global_install();
10620
10856
  init_logs_server_daemon();
10621
10857
  init_components();
10622
10858
  init_hooks();
@@ -10834,9 +11070,9 @@ var tui_exports = {};
10834
11070
  __export(tui_exports, {
10835
11071
  launchTui: () => launchTui
10836
11072
  });
10837
- import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync8 } from "fs";
10838
- import { homedir as homedir9 } from "os";
10839
- import { join as join11 } from "path";
11073
+ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync9 } from "fs";
11074
+ import { homedir as homedir10 } from "os";
11075
+ import { join as join12 } from "path";
10840
11076
  import { render } from "ink";
10841
11077
  import { jsx as jsx9 } from "react/jsx-runtime";
10842
11078
  async function launchTui() {
@@ -10847,11 +11083,11 @@ async function launchTui() {
10847
11083
  return;
10848
11084
  }
10849
11085
  process.stdout.write("\x1B[?1049h\x1B[H");
10850
- const debugLog = join11(homedir9(), ".solongate", "dataroom-debug.log");
11086
+ const debugLog = join12(homedir10(), ".solongate", "dataroom-debug.log");
10851
11087
  const saved = { log: console.log, warn: console.warn, error: console.error, info: console.info, debug: console.debug };
10852
11088
  const toFile = (level) => (...args) => {
10853
11089
  try {
10854
- mkdirSync8(join11(homedir9(), ".solongate"), { recursive: true });
11090
+ mkdirSync9(join12(homedir10(), ".solongate"), { recursive: true });
10855
11091
  appendFileSync2(debugLog, `${(/* @__PURE__ */ new Date()).toISOString()} [${level}] ${args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}
10856
11092
  `);
10857
11093
  } catch {
@@ -10975,7 +11211,7 @@ var init_args = __esm({
10975
11211
  });
10976
11212
 
10977
11213
  // src/commands/policy.ts
10978
- import { readFileSync as readFileSync9 } from "fs";
11214
+ import { readFileSync as readFileSync10 } from "fs";
10979
11215
  async function run(argv) {
10980
11216
  const { positionals, flags } = parse(argv);
10981
11217
  const sub = positionals[0];
@@ -11150,7 +11386,7 @@ function printRules(rules) {
11150
11386
  }
11151
11387
  async function resolveRules(target) {
11152
11388
  if (target.endsWith(".json")) {
11153
- const parsed = JSON.parse(readFileSync9(target, "utf-8"));
11389
+ const parsed = JSON.parse(readFileSync10(target, "utf-8"));
11154
11390
  return parsed.rules ?? [];
11155
11391
  }
11156
11392
  const p = await api.policies.get(target);
@@ -11565,9 +11801,9 @@ var init_agents2 = __esm({
11565
11801
  });
11566
11802
 
11567
11803
  // src/commands/doctor.ts
11568
- import { existsSync as existsSync4, statSync as statSync2 } from "fs";
11569
- import { homedir as homedir10 } from "os";
11570
- import { join as join12 } from "path";
11804
+ import { existsSync as existsSync5, statSync as statSync2 } from "fs";
11805
+ import { homedir as homedir11 } from "os";
11806
+ import { join as join13 } from "path";
11571
11807
  async function run6(argv) {
11572
11808
  const { flags } = parse(argv);
11573
11809
  const json = flagBool(flags, "json");
@@ -11597,7 +11833,7 @@ async function run6(argv) {
11597
11833
  } catch {
11598
11834
  }
11599
11835
  }
11600
- if (existsSync4(LOCAL_LOG2)) {
11836
+ if (existsSync5(LOCAL_LOG2)) {
11601
11837
  const st = statSync2(LOCAL_LOG2);
11602
11838
  const ageMin = (Date.now() - st.mtimeMs) / 6e4;
11603
11839
  checks.push({ name: "local logs", ok: true, detail: `on \xB7 ${(st.size / 1024).toFixed(0)}KB \xB7 last write ${ageMin < 1 ? "just now" : Math.round(ageMin) + "m ago"}` });
@@ -11627,14 +11863,14 @@ var init_doctor = __esm({
11627
11863
  init_api_client();
11628
11864
  init_format();
11629
11865
  init_args();
11630
- LOCAL_LOG2 = join12(homedir10(), ".solongate", "local-logs", "solongate-audit.jsonl");
11866
+ LOCAL_LOG2 = join13(homedir11(), ".solongate", "local-logs", "solongate-audit.jsonl");
11631
11867
  }
11632
11868
  });
11633
11869
 
11634
11870
  // src/commands/watch.ts
11635
- import { closeSync as closeSync2, existsSync as existsSync5, openSync as openSync4, readSync as readSync2, statSync as statSync3 } from "fs";
11636
- import { homedir as homedir11 } from "os";
11637
- import { join as join13 } from "path";
11871
+ import { closeSync as closeSync2, existsSync as existsSync6, openSync as openSync4, readSync as readSync2, statSync as statSync3 } from "fs";
11872
+ import { homedir as homedir12 } from "os";
11873
+ import { join as join14 } from "path";
11638
11874
  function tailLocal(file, maxBytes = 131072) {
11639
11875
  try {
11640
11876
  const size = statSync3(file).size;
@@ -11678,7 +11914,7 @@ async function run7(argv) {
11678
11914
  for (const r of rows) if (keep(r)) print(r, json);
11679
11915
  };
11680
11916
  const pollLocal = () => {
11681
- if (cloudOnly || !existsSync5(LOCAL_LOG3)) return;
11917
+ if (cloudOnly || !existsSync6(LOCAL_LOG3)) return;
11682
11918
  const rows = [];
11683
11919
  for (const line of tailLocal(LOCAL_LOG3)) {
11684
11920
  try {
@@ -11741,7 +11977,7 @@ var init_watch = __esm({
11741
11977
  init_cli_utils();
11742
11978
  init_args();
11743
11979
  init_format();
11744
- LOCAL_LOG3 = join13(homedir11(), ".solongate", "local-logs", "solongate-audit.jsonl");
11980
+ LOCAL_LOG3 = join14(homedir12(), ".solongate", "local-logs", "solongate-audit.jsonl");
11745
11981
  trunc = (s, n) => s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
11746
11982
  time = (ms) => new Date(ms).toTimeString().slice(0, 8);
11747
11983
  }
@@ -12031,250 +12267,6 @@ var init_commands = __esm({
12031
12267
  }
12032
12268
  });
12033
12269
 
12034
- // src/global-install.ts
12035
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync9, existsSync as existsSync6, mkdirSync as mkdirSync9 } from "fs";
12036
- import { resolve as resolve4, join as join14, dirname as dirname3 } from "path";
12037
- import { homedir as homedir12 } from "os";
12038
- import { fileURLToPath as fileURLToPath3 } from "url";
12039
- import { createInterface } from "readline";
12040
- import { execFileSync as execFileSync2 } from "child_process";
12041
- function lockFile(file) {
12042
- if (!existsSync6(file)) return;
12043
- try {
12044
- if (process.platform === "win32") {
12045
- try {
12046
- execFileSync2("icacls", [file, "/deny", "*S-1-1-0:(WD,AD,DC,DE)"], { stdio: "ignore" });
12047
- } catch {
12048
- }
12049
- try {
12050
- execFileSync2("attrib", ["+R", file], { stdio: "ignore" });
12051
- } catch {
12052
- }
12053
- } else if (process.platform === "darwin") {
12054
- try {
12055
- execFileSync2("chflags", ["uchg", file], { stdio: "ignore" });
12056
- } catch {
12057
- }
12058
- } else {
12059
- try {
12060
- execFileSync2("chattr", ["+i", file], { stdio: "ignore" });
12061
- } catch {
12062
- }
12063
- }
12064
- } catch {
12065
- }
12066
- }
12067
- function unlockFile(file) {
12068
- if (!existsSync6(file)) return;
12069
- try {
12070
- if (process.platform === "win32") {
12071
- try {
12072
- execFileSync2("icacls", [file, "/remove:d", "*S-1-1-0"], { stdio: "ignore" });
12073
- } catch {
12074
- }
12075
- try {
12076
- execFileSync2("icacls", [file, "/reset"], { stdio: "ignore" });
12077
- } catch {
12078
- }
12079
- try {
12080
- execFileSync2("attrib", ["-R", file], { stdio: "ignore" });
12081
- } catch {
12082
- }
12083
- } else if (process.platform === "darwin") {
12084
- try {
12085
- execFileSync2("chflags", ["nouchg", file], { stdio: "ignore" });
12086
- } catch {
12087
- }
12088
- } else {
12089
- try {
12090
- execFileSync2("chattr", ["-i", file], { stdio: "ignore" });
12091
- } catch {
12092
- }
12093
- }
12094
- } catch {
12095
- }
12096
- }
12097
- function protectedTargets() {
12098
- const p = globalPaths();
12099
- return [
12100
- join14(p.hooksDir, "guard.mjs"),
12101
- join14(p.hooksDir, "audit.mjs"),
12102
- join14(p.hooksDir, "stop.mjs"),
12103
- join14(p.hooksDir, "shield.mjs"),
12104
- p.configPath,
12105
- p.settingsPath
12106
- ];
12107
- }
12108
- function lockProtected() {
12109
- for (const f of protectedTargets()) lockFile(f);
12110
- }
12111
- function unlockProtected() {
12112
- for (const f of protectedTargets()) unlockFile(f);
12113
- }
12114
- function globalPaths() {
12115
- const home = homedir12();
12116
- const sgDir = join14(home, ".solongate");
12117
- const hooksDir = join14(sgDir, "hooks");
12118
- const claudeDir = join14(home, ".claude");
12119
- return {
12120
- home,
12121
- sgDir,
12122
- hooksDir,
12123
- claudeDir,
12124
- settingsPath: join14(claudeDir, "settings.json"),
12125
- backupPath: join14(claudeDir, "settings.solongate.bak"),
12126
- configPath: join14(sgDir, "cloud-guard.json")
12127
- };
12128
- }
12129
- function readHook(filename) {
12130
- return readFileSync10(join14(HOOKS_DIR, filename), "utf-8");
12131
- }
12132
- function readGuard() {
12133
- const bundled = join14(HOOKS_DIR, "guard.bundled.mjs");
12134
- return existsSync6(bundled) ? readFileSync10(bundled, "utf-8") : readHook("guard.mjs");
12135
- }
12136
- function ask(question) {
12137
- const rl = createInterface({ input: process.stdin, output: process.stderr });
12138
- return new Promise((res) => rl.question(question, (a) => {
12139
- rl.close();
12140
- res(a.trim());
12141
- }));
12142
- }
12143
- function escapeRe(s) {
12144
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
12145
- }
12146
- function resolveRealClaude() {
12147
- try {
12148
- const finder = process.platform === "win32" ? "where" : "which";
12149
- const out2 = execFileSync2(finder, ["claude"], { encoding: "utf-8" }).split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
12150
- if (process.platform === "win32") {
12151
- const low = (s) => s.toLowerCase();
12152
- return out2.find((l) => low(l).endsWith(".cmd")) || out2.find((l) => low(l).endsWith(".exe")) || out2.find((l) => low(l).endsWith(".bat")) || out2[0] || null;
12153
- }
12154
- return out2[0] || null;
12155
- } catch {
12156
- return null;
12157
- }
12158
- }
12159
- function shimTargets() {
12160
- if (process.platform === "win32") {
12161
- try {
12162
- const prof = execFileSync2("powershell", ["-NoProfile", "-Command", "$PROFILE.CurrentUserAllHosts"], { encoding: "utf-8" }).trim();
12163
- return prof ? [prof] : [];
12164
- } catch {
12165
- return [];
12166
- }
12167
- }
12168
- return [".bashrc", ".zshrc", ".profile"].map((f) => join14(homedir12(), f)).filter((f) => existsSync6(f));
12169
- }
12170
- function writeShimBlock(file, block2) {
12171
- const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
12172
- let content = existsSync6(file) ? readFileSync10(file, "utf-8") : "";
12173
- content = content.replace(re, "");
12174
- if (block2) {
12175
- if (content.length && !content.endsWith("\n")) content += "\n";
12176
- content += block2 + "\n";
12177
- }
12178
- mkdirSync9(dirname3(file), { recursive: true });
12179
- writeFileSync9(file, content);
12180
- }
12181
- function installClaudeShim(shieldPath) {
12182
- const real = resolveRealClaude();
12183
- if (!real) {
12184
- console.log(" (Claude Code not found on PATH \u2014 skipped auto-shield. Install it, then re-run `login`.)");
12185
- return;
12186
- }
12187
- const node = process.execPath.replace(/\\/g, "/");
12188
- const shield = shieldPath.replace(/\\/g, "/");
12189
- const win = process.platform === "win32";
12190
- const block2 = win ? `${SHIM_BEGIN}
12191
- function claude { & "${node}" "${shield}" -- "${real}" @args }
12192
- ${SHIM_END}` : `${SHIM_BEGIN}
12193
- claude() { "${node}" "${shield}" -- "${real}" "$@"; }
12194
- ${SHIM_END}`;
12195
- const targets = shimTargets();
12196
- if (targets.length === 0) return;
12197
- for (const file of targets) {
12198
- try {
12199
- writeShimBlock(file, block2);
12200
- } catch {
12201
- }
12202
- }
12203
- }
12204
- async function runGlobalInstall2(opts = {}) {
12205
- const p = globalPaths();
12206
- let apiKey = opts.apiKey || process.env["SOLONGATE_API_KEY"] || "";
12207
- if (!apiKey || apiKey === "sg_live_your_key_here") {
12208
- try {
12209
- const cfg = JSON.parse(readFileSync10(p.configPath, "utf-8"));
12210
- if (cfg && typeof cfg.apiKey === "string") apiKey = cfg.apiKey;
12211
- } catch {
12212
- }
12213
- }
12214
- if (!apiKey || apiKey === "sg_live_your_key_here") {
12215
- apiKey = await ask(" Enter your SolonGate API key (sg_live_\u2026 from https://dashboard.solongate.com): ");
12216
- }
12217
- if (!apiKey.startsWith("sg_live_") && !apiKey.startsWith("sg_test_")) {
12218
- console.log(" Invalid API key. Must start with sg_live_ or sg_test_");
12219
- process.exit(1);
12220
- }
12221
- const apiUrl = opts.apiUrl || process.env["SOLONGATE_API_URL"] || "https://api.solongate.com";
12222
- mkdirSync9(p.hooksDir, { recursive: true });
12223
- mkdirSync9(p.claudeDir, { recursive: true });
12224
- unlockProtected();
12225
- writeFileSync9(join14(p.hooksDir, "guard.mjs"), readGuard());
12226
- writeFileSync9(join14(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
12227
- writeFileSync9(join14(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
12228
- writeFileSync9(join14(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
12229
- console.log(` Installed hooks \u2192 ${p.hooksDir}`);
12230
- installClaudeShim(join14(p.hooksDir, "shield.mjs"));
12231
- writeFileSync9(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
12232
- console.log(` Wrote ${p.configPath}`);
12233
- let existing = {};
12234
- if (existsSync6(p.settingsPath)) {
12235
- const raw = readFileSync10(p.settingsPath, "utf-8");
12236
- if (!existsSync6(p.backupPath)) {
12237
- writeFileSync9(p.backupPath, raw);
12238
- console.log(` Backed up existing settings \u2192 ${p.backupPath}`);
12239
- }
12240
- try {
12241
- existing = JSON.parse(raw);
12242
- } catch {
12243
- existing = {};
12244
- }
12245
- }
12246
- const guardAbs = join14(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
12247
- const auditAbs = join14(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
12248
- const stopAbs = join14(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
12249
- const nodeBin = process.execPath.replace(/\\/g, "/");
12250
- const call = process.platform === "win32" ? "& " : "";
12251
- const hookCmd = (script) => `${call}"${nodeBin}" "${script}" claude-code "Claude Code"`;
12252
- const merged = {
12253
- ...existing,
12254
- hooks: {
12255
- PreToolUse: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(guardAbs) }] }],
12256
- PostToolUse: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(auditAbs) }] }],
12257
- Stop: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(stopAbs) }] }]
12258
- }
12259
- };
12260
- writeFileSync9(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
12261
- console.log(` Registered global hooks \u2192 ${p.settingsPath}`);
12262
- if (process.env["SOLONGATE_OS_LOCK"] === "1") {
12263
- lockProtected();
12264
- console.log(" Locked protection files (OS-level read-only/immutable).");
12265
- }
12266
- }
12267
- var __dirname, HOOKS_DIR, SHIM_BEGIN, SHIM_END;
12268
- var init_global_install = __esm({
12269
- "src/global-install.ts"() {
12270
- "use strict";
12271
- __dirname = dirname3(fileURLToPath3(import.meta.url));
12272
- HOOKS_DIR = resolve4(__dirname, "..", "hooks");
12273
- SHIM_BEGIN = "# >>> SolonGate shield (auto secret redaction) >>>";
12274
- SHIM_END = "# <<< SolonGate shield <<<";
12275
- }
12276
- });
12277
-
12278
12270
  // src/login.ts
12279
12271
  var login_exports = {};
12280
12272
  import { spawn as spawn5 } from "child_process";