@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/login.js CHANGED
@@ -26,7 +26,7 @@ var c = {
26
26
  var BANNER_COLORS = [c.blue1, c.blue2, c.blue3, c.blue4, c.blue5, c.blue6];
27
27
 
28
28
  // src/global-install.ts
29
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
29
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, rmSync } from "fs";
30
30
  import { resolve, join, dirname } from "path";
31
31
  import { homedir } from "os";
32
32
  import { fileURLToPath } from "url";
@@ -1,4 +1,35 @@
1
1
  import type { ReactNode } from 'react';
2
+ /** HH:MM:SS for a timestamp (ms or ISO); `--:--:--` when unparseable. */
3
+ export declare const hhmmss: (ts: string | number) => string;
4
+ /** Section divider title used across the Live/Audit consoles. */
5
+ export declare function PaneTitle({ label, extra, width }: {
6
+ label: string;
7
+ extra?: string;
8
+ width: number;
9
+ }): JSX.Element;
10
+ /**
11
+ * One tool-stream row, shared verbatim by Live (rolling buffer) and Audit (full
12
+ * history). `loc` tags LOC vs CLD; `selected` highlights + shows the ▸ cursor.
13
+ * Columns are fixed-width and NEVER conditional so scrolling can't reshuffle the
14
+ * line and glitch the frame.
15
+ */
16
+ export interface StreamRow {
17
+ at: number;
18
+ tool: string;
19
+ decision: string;
20
+ permission: string;
21
+ detail: string;
22
+ dlp: boolean;
23
+ burst: boolean;
24
+ agent?: string | null;
25
+ evalMs?: number | null;
26
+ rule?: string | null;
27
+ }
28
+ export declare function StreamLine({ e, loc, selected }: {
29
+ e: StreamRow;
30
+ loc: boolean;
31
+ selected?: boolean;
32
+ }): JSX.Element;
2
33
  export declare function Panel({ title, subtitle, children }: {
3
34
  title: string;
4
35
  subtitle?: string;
package/dist/tui/index.js CHANGED
@@ -5,9 +5,9 @@ var __export = (target, all) => {
5
5
  };
6
6
 
7
7
  // src/tui/index.tsx
8
- import { appendFileSync, mkdirSync as mkdirSync6 } from "fs";
9
- import { homedir as homedir8 } from "os";
10
- import { join as join8 } from "path";
8
+ import { appendFileSync, mkdirSync as mkdirSync7 } from "fs";
9
+ import { homedir as homedir9 } from "os";
10
+ import { join as join9 } from "path";
11
11
  import { render } from "ink";
12
12
 
13
13
  // src/tui/App.tsx
@@ -125,6 +125,43 @@ function ago(ts) {
125
125
 
126
126
  // src/tui/components.tsx
127
127
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
128
+ var hhmmss = (ts) => {
129
+ const d = new Date(ts);
130
+ return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
131
+ };
132
+ function PaneTitle({ label, extra, width }) {
133
+ const tail = extra ? ` ${extra} ` : " ";
134
+ const used = 2 + 1 + label.length + tail.length;
135
+ return /* @__PURE__ */ jsxs(Text, { wrap: "truncate", children: [
136
+ /* @__PURE__ */ jsx(Text, { color: theme.dim, children: "\u2500 " }),
137
+ /* @__PURE__ */ jsxs(Text, { color: theme.accentBright, bold: true, children: [
138
+ "\u258E",
139
+ label
140
+ ] }),
141
+ /* @__PURE__ */ jsx(Text, { color: theme.dim, children: tail + "\u2500".repeat(Math.max(0, width - used)) })
142
+ ] });
143
+ }
144
+ function StreamLine({ e, loc, selected }) {
145
+ return /* @__PURE__ */ jsxs(Text, { wrap: "truncate", backgroundColor: selected ? "#1c2f63" : void 0, children: [
146
+ /* @__PURE__ */ jsx(Text, { color: selected ? theme.accentBright : theme.dim, children: selected ? "\u25B8" : " " }),
147
+ /* @__PURE__ */ jsxs(Text, { color: theme.dim, children: [
148
+ "[",
149
+ hhmmss(e.at),
150
+ " "
151
+ ] }),
152
+ /* @__PURE__ */ jsx(Text, { color: loc ? theme.ok : "white", children: loc ? "LOC" : "CLD" }),
153
+ /* @__PURE__ */ jsx(Text, { color: theme.dim, children: "] " }),
154
+ /* @__PURE__ */ jsx(Text, { color: decisionColor(e.decision), bold: e.decision !== "ALLOW", children: e.decision.padEnd(6) }),
155
+ /* @__PURE__ */ jsx(Text, { color: theme.accent, children: truncate(e.tool, 12).padEnd(13) }),
156
+ /* @__PURE__ */ jsx(Text, { color: theme.dim, children: (e.permission ?? "").padEnd(5) }),
157
+ /* @__PURE__ */ jsx(Text, { color: e.evalMs != null && e.evalMs > 500 ? theme.warn : theme.dim, children: (e.evalMs != null ? `${e.evalMs}ms` : "\u2014").padEnd(7) }),
158
+ /* @__PURE__ */ jsx(Text, { color: theme.dim, children: truncate(e.agent ?? "-", 11).padEnd(12) }),
159
+ /* @__PURE__ */ jsx(Text, { color: e.dlp ? theme.bad : theme.dim, bold: e.dlp, children: ("dlp:" + (e.dlp ? "yes" : "no")).padEnd(8) }),
160
+ /* @__PURE__ */ jsx(Text, { color: e.burst ? theme.warn : theme.dim, bold: e.burst, children: ("rl:" + (e.burst ? "yes" : "no")).padEnd(7) }),
161
+ e.rule && e.decision !== "ALLOW" ? /* @__PURE__ */ jsx(Text, { color: theme.bad, children: truncate(e.rule, 14) + " " }) : null,
162
+ /* @__PURE__ */ jsx(Text, { color: theme.dim, children: e.detail })
163
+ ] });
164
+ }
128
165
  function DataView({
129
166
  loading,
130
167
  error,
@@ -824,10 +861,6 @@ var SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826"
824
861
  var BG = "#12234f";
825
862
  var DIM_FLOOR = "#233457";
826
863
  var RING = join4(process.cwd(), ".solongate", ".eval-ring.jsonl");
827
- var hhmmss = (ts) => {
828
- const d = new Date(ts);
829
- return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
830
- };
831
864
  var fmtUp = (ms) => {
832
865
  const s = Math.floor(ms / 1e3);
833
866
  const p = (n) => String(n).padStart(2, "0");
@@ -899,39 +932,6 @@ function HBar({ label, value, max, width, color }) {
899
932
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " " + value })
900
933
  ] });
901
934
  }
902
- function PaneTitle({ label, extra, width }) {
903
- const tail = extra ? ` ${extra} ` : " ";
904
- const used = 2 + 1 + label.length + tail.length;
905
- return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
906
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2500 " }),
907
- /* @__PURE__ */ jsxs2(Text2, { color: theme.accentBright, bold: true, children: [
908
- "\u258E",
909
- label
910
- ] }),
911
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: tail + "\u2500".repeat(Math.max(0, width - used)) })
912
- ] });
913
- }
914
- function StreamLine({ e, loc, selected }) {
915
- return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", backgroundColor: selected ? "#1c2f63" : void 0, children: [
916
- /* @__PURE__ */ jsx2(Text2, { color: selected ? theme.accentBright : theme.dim, children: selected ? "\u25B8" : " " }),
917
- /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
918
- "[",
919
- hhmmss(e.at),
920
- " "
921
- ] }),
922
- /* @__PURE__ */ jsx2(Text2, { color: loc ? theme.ok : "white", children: loc ? "LOC" : "CLD" }),
923
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "] " }),
924
- /* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), bold: e.decision !== "ALLOW", children: e.decision.padEnd(6) }),
925
- /* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate(e.tool, 12).padEnd(13) }),
926
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.permission.padEnd(5) }),
927
- /* @__PURE__ */ jsx2(Text2, { color: e.evalMs != null && e.evalMs > 500 ? theme.warn : theme.dim, children: (e.evalMs != null ? `${e.evalMs}ms` : "\u2014").padEnd(7) }),
928
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: truncate(e.agent ?? "-", 11).padEnd(12) }),
929
- /* @__PURE__ */ jsx2(Text2, { color: e.dlp ? theme.bad : theme.dim, bold: e.dlp, children: ("dlp:" + (e.dlp ? "yes" : "no")).padEnd(8) }),
930
- /* @__PURE__ */ jsx2(Text2, { color: e.burst ? theme.warn : theme.dim, bold: e.burst, children: ("rl:" + (e.burst ? "yes" : "no")).padEnd(7) }),
931
- e.rule && e.decision !== "ALLOW" ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: truncate(e.rule, 14) + " " }) : null,
932
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.detail })
933
- ] });
934
- }
935
935
  var FILTERS = ["all", "local", "cloud"];
936
936
  var sessStatus = (lastAt, now) => now - lastAt < 6e4 ? "active" : now - lastAt < 3e5 ? "idle" : "ended";
937
937
  var STATUS_STYLE = {
@@ -2472,12 +2472,25 @@ import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
2472
2472
  import { homedir as homedir5 } from "os";
2473
2473
  import { join as join5 } from "path";
2474
2474
  import { useState as useState6 } from "react";
2475
- import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
2475
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
2476
2476
  var DECISIONS = [void 0, "DENY", "ALLOW"];
2477
2477
  var SIGNALS = [void 0, "dlp", "ratelimit"];
2478
2478
  var SESS_STATUS = [void 0, "active", "idle", "ended"];
2479
2479
  var PAGE = 500;
2480
2480
  var LOCAL_MAX_BYTES = 16 * 1024 * 1024;
2481
+ var BG2 = "#12234f";
2482
+ var toStream = (e) => ({
2483
+ at: e.at,
2484
+ tool: e.tool,
2485
+ decision: e.decision,
2486
+ permission: (e.permission ?? "").slice(0, 4),
2487
+ detail: (e.args ?? e.reason ?? "").replace(/\s+/g, " "),
2488
+ dlp: e.dlp.length > 0,
2489
+ burst: e.burst,
2490
+ agent: e.agent,
2491
+ evalMs: e.evalMs,
2492
+ rule: e.rule
2493
+ });
2481
2494
  var cloudRow = (e) => {
2482
2495
  const rs = reasonSignals(e.reason);
2483
2496
  return {
@@ -2557,7 +2570,7 @@ var AUDIT_HELP = [
2557
2570
  ["esc", "back to the menu"]
2558
2571
  ]
2559
2572
  ],
2560
- ["Entry detail", [["\u2191\u2193 / PgUp PgDn", "scroll the arguments"], ["\u2190 / esc", "back to the list"]]]
2573
+ ["Entry (full content)", [["\u2191\u2193 / PgUp PgDn", "scroll the reason + arguments"], ["space", "copy mode (freeze, then select)"], ["\u2190 / esc", "back to the list"]]]
2561
2574
  ];
2562
2575
  var sessStatus2 = (lastAt) => Date.now() - lastAt < 6e4 ? "active" : Date.now() - lastAt < 3e5 ? "idle" : "ended";
2563
2576
  var STATUS_DOT = {
@@ -2662,10 +2675,6 @@ function AuditPanel({ active: active2, focused }) {
2662
2675
  const sq = sessSearch.trim().toLowerCase();
2663
2676
  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);
2664
2677
  const currentSess = sessionsFiltered[Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1))];
2665
- const sessionQ = useLoader(
2666
- () => view === "detail" && current?.session && source === "cloud" ? api.audit.list({ session_id: current.session, limit: 40 }) : Promise.resolve(null),
2667
- [view, current?.session, source]
2668
- );
2669
2678
  const logsLoading = (source === "cloud" ? cloudQ.loading : localQ.loading) && !editing;
2670
2679
  const sessLoading = source === "cloud" ? agentsQ.loading : localQ.loading;
2671
2680
  const doDelete = (kind) => {
@@ -2891,61 +2900,56 @@ function AuditPanel({ active: active2, focused }) {
2891
2900
  ] });
2892
2901
  }
2893
2902
  if (view === "detail" && current) {
2894
- const sessionCalls = source === "cloud" ? (sessionQ.data?.entries ?? []).map(cloudRow) : localAll.filter((r) => r.session && r.session === current.session).slice(0, 40);
2895
- const bodyW = Math.max(20, cols - 2);
2896
- const reasonLines = wrapLines("reason: " + (current.reason ?? "\u2014"), bodyW);
2897
- const argLines = current.args ? wrapLines(prettyJson(current.args), bodyW) : ["(no arguments recorded)"];
2898
- const sessionRows = current.session ? Math.min(4, sessionCalls.length) + 2 : 1;
2899
- const reasonShown = reasonLines.slice(0, 4);
2900
- const argBudget = Math.max(3, rows - 2 - 5 - reasonShown.length - 1 - sessionRows - 1 - (frozen ? 1 : 0));
2901
- const maxScroll = Math.max(0, argLines.length - argBudget);
2903
+ const e = current;
2904
+ const loc = source === "local";
2905
+ const bodyW = Math.max(20, cols - 4);
2906
+ const innerW = cols - 2;
2907
+ const contentLines = [];
2908
+ if (e.reason && e.decision !== "ALLOW") contentLines.push(...wrapLines("reason: " + e.reason, bodyW), "");
2909
+ const argsText = e.args ? prettyJson(e.args) : e.reason && e.decision === "ALLOW" ? e.reason : "(no arguments recorded)";
2910
+ contentLines.push(...wrapLines(argsText, bodyW));
2911
+ const bodyRows = Math.max(4, rows - 7 - (frozen ? 1 : 0));
2912
+ const maxScroll = Math.max(0, contentLines.length - bodyRows);
2902
2913
  const off = Math.min(detailScroll, maxScroll);
2903
- const argWin = argLines.slice(off, off + argBudget);
2914
+ const win = contentLines.slice(off, off + bodyRows);
2904
2915
  return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
2905
2916
  copyBanner,
2906
- /* @__PURE__ */ jsxs6(Box6, { children: [
2907
- /* @__PURE__ */ jsx6(Text6, { color: decisionColor(current.decision), bold: true, children: current.decision }),
2908
- /* @__PURE__ */ jsx6(Text6, { color: theme.accent, children: " " + current.tool }),
2909
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " " + current.permission + " \xB7 " + current.trust + " " }),
2910
- srcChip
2917
+ /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
2918
+ /* @__PURE__ */ jsx6(Text6, { backgroundColor: BG2, color: "white", bold: true, children: " ENTRY " }),
2919
+ /* @__PURE__ */ jsx6(Text6, { color: decisionColor(e.decision), bold: true, children: " " + e.decision }),
2920
+ /* @__PURE__ */ jsx6(Text6, { color: theme.accent, bold: true, children: " " + e.tool }),
2921
+ /* @__PURE__ */ jsx6(Text6, { color: loc ? theme.ok : "white", children: " " + (loc ? "LOC" : "CLD") }),
2922
+ e.dlp.length ? /* @__PURE__ */ jsx6(Text6, { color: theme.bad, children: " DLP!" }) : null,
2923
+ e.burst ? /* @__PURE__ */ jsx6(Text6, { color: theme.warn, children: " BURST" }) : null,
2924
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \u2190 back" })
2911
2925
  ] }),
2912
- reasonShown.map((l, i) => /* @__PURE__ */ jsx6(Text6, { wrap: "truncate", children: i === 0 ? /* @__PURE__ */ jsxs6(Fragment4, { children: [
2913
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "reason " }),
2914
- /* @__PURE__ */ jsx6(Text6, { children: l.slice("reason: ".length) })
2915
- ] }) : /* @__PURE__ */ jsx6(Text6, { children: " " + l }) }, "r" + i)),
2916
- /* @__PURE__ */ jsx6(Detail, { label: "rule", value: current.rule ?? "\u2014" }),
2917
- /* @__PURE__ */ jsx6(Detail, { label: "agent", value: current.agent ?? "\u2014" }),
2918
- /* @__PURE__ */ jsx6(Detail, { label: "session", value: current.session ?? "\u2014" }),
2919
- /* @__PURE__ */ jsx6(Detail, { label: "dlp", value: current.dlp.length ? current.dlp.join(", ") : "none", color: current.dlp.length ? theme.bad : void 0 }),
2920
- /* @__PURE__ */ jsx6(Detail, { label: "ratelimit", value: current.burst ? "BURST \u2014 over the limit for this agent" : "no", color: current.burst ? theme.bad : void 0 }),
2921
- /* @__PURE__ */ jsx6(Detail, { label: "when", value: new Date(current.at).toLocaleString() + (current.evalMs != null ? ` \xB7 eval ${current.evalMs}ms` : "") }),
2922
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: `arguments \u2014 full \xB7 ${argLines.length} lines${maxScroll ? ` \xB7 \u25BC${maxScroll - off} more \xB7 \u2191\u2193 scroll` : ""}` }),
2923
- /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", height: argBudget, overflow: "hidden", children: argWin.map((l, i) => /* @__PURE__ */ jsx6(Text6, { wrap: "truncate", children: l || " " }, off + i)) }),
2924
- current.session ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
2925
- /* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
2926
- "Session \xB7 ",
2927
- sessionCalls.length,
2928
- " calls"
2929
- ] }),
2930
- /* @__PURE__ */ jsx6(
2931
- Table,
2932
- {
2933
- columns: [
2934
- { header: "DECISION", width: 9 },
2935
- { header: "TOOL", width: 20 },
2936
- { header: "REASON", width: Math.max(16, cols - 48) },
2937
- { header: "WHEN", width: 6 }
2938
- ],
2939
- rows: sessionCalls.slice(0, Math.max(1, sessionRows - 2)).map((e) => [
2940
- { value: e.decision, color: decisionColor(e.decision) },
2941
- { value: truncate(e.tool, 20), color: theme.accent },
2942
- { value: truncate(e.reason ?? "\u2014", Math.max(16, cols - 48)) },
2943
- { value: ago(e.at), dim: true }
2944
- ])
2945
- }
2946
- )
2947
- ] }) : /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "(no session id)" }),
2948
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "\u2191\u2193 scroll arguments \xB7 \u2190/esc back to logs" })
2926
+ /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
2927
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "\u2502 when " }),
2928
+ /* @__PURE__ */ jsx6(Text6, { children: new Date(e.at).toLocaleString() }),
2929
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \u2502 perm " }),
2930
+ /* @__PURE__ */ jsx6(Text6, { children: e.permission || "\u2014" }),
2931
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \u2502 trust " }),
2932
+ /* @__PURE__ */ jsx6(Text6, { children: e.trust || "\u2014" }),
2933
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \u2502 eval " }),
2934
+ /* @__PURE__ */ jsx6(Text6, { color: e.evalMs != null && e.evalMs > 500 ? theme.warn : void 0, children: e.evalMs != null ? `${e.evalMs}ms` : "\u2014" }),
2935
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \u2502 agent " }),
2936
+ /* @__PURE__ */ jsx6(Text6, { children: e.agent ?? "\u2014" }),
2937
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \u2502" })
2938
+ ] }),
2939
+ /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
2940
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "\u2502 session " }),
2941
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: e.session ?? "\u2014" }),
2942
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \u2502 rule " }),
2943
+ /* @__PURE__ */ jsx6(Text6, { color: e.rule && e.decision !== "ALLOW" ? theme.bad : theme.dim, children: e.rule ?? "\u2014" }),
2944
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \u2502" })
2945
+ ] }),
2946
+ /* @__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 }),
2947
+ /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", height: bodyRows, overflow: "hidden", children: win.map((l, i) => /* @__PURE__ */ jsx6(Text6, { wrap: "truncate", children: l || " " }, off + i)) }),
2948
+ /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
2949
+ /* @__PURE__ */ jsx6(Text6, { backgroundColor: BG2, color: "white", bold: true, children: " ENTRY " }),
2950
+ /* @__PURE__ */ jsx6(Text6, { backgroundColor: "#0b1530", color: "white", children: ` ${e.id} ` }),
2951
+ /* @__PURE__ */ jsx6(Text6, { backgroundColor: BG2, color: "white", children: " \u2191\u2193 scroll \xB7 space copy \xB7 ? all keys \xB7 \u2190 back \xB7 esc menu " })
2952
+ ] })
2949
2953
  ] });
2950
2954
  }
2951
2955
  const chip = (label, val, on) => /* @__PURE__ */ jsxs6(Text6, { children: [
@@ -3032,7 +3036,6 @@ function AuditPanel({ active: active2, focused }) {
3032
3036
  const maxStart = Math.max(0, pageRows.length - listRows);
3033
3037
  const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
3034
3038
  const windowed = pageRows.slice(start, start + listRows);
3035
- const reasonW = Math.min(58, Math.max(12, cols - 71));
3036
3039
  return /* @__PURE__ */ jsx6(DataView, { loading: logsLoading && !frozen, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
3037
3040
  strip,
3038
3041
  copyBanner,
@@ -3068,21 +3071,7 @@ function AuditPanel({ active: active2, focused }) {
3068
3071
  )
3069
3072
  ] }) : null,
3070
3073
  /* @__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` : ""}` }),
3071
- /* @__PURE__ */ jsx6(
3072
- Table,
3073
- {
3074
- columns: [
3075
- { header: "", width: 2 },
3076
- { header: "DECISION", width: 9 },
3077
- { header: "TOOL", width: 18 },
3078
- { header: "AGENT", width: 14 },
3079
- { header: "REASON", width: reasonW },
3080
- { header: "SIGNAL", width: 8 },
3081
- { header: "WHEN", width: 6 }
3082
- ],
3083
- rows: windowed.map((e, i) => rowFor(e, start + i === selClamped && focused, reasonW))
3084
- }
3085
- ),
3074
+ /* @__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)) }),
3086
3075
  pageRows.length === 0 ? /* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
3087
3076
  "(no entries",
3088
3077
  source === "local" ? " in the local file" : "",
@@ -3090,43 +3079,58 @@ function AuditPanel({ active: active2, focused }) {
3090
3079
  ] }) : null
3091
3080
  ] }) });
3092
3081
  }
3093
- function rowFor(e, active2, reasonW) {
3094
- const sig = [e.dlp.length ? `DLP${e.dlp.length > 1 ? e.dlp.length : ""}` : "", e.burst ? "RL" : ""].filter(Boolean).join(" ");
3095
- return [
3096
- { value: active2 ? "\u25B8" : "", color: theme.accentBright },
3097
- { value: e.decision, color: decisionColor(e.decision) },
3098
- { value: truncate(e.tool, 18), color: theme.accent },
3099
- { value: truncate(e.agent ?? "\u2014", 14), dim: true },
3100
- { value: truncate(e.reason ?? e.args ?? "\u2014", reasonW) },
3101
- { value: sig || "\xB7", color: sig ? theme.bad : void 0, dim: !sig },
3102
- { value: ago(e.at), dim: true }
3103
- ];
3104
- }
3105
- function Detail({ label, value, color }) {
3106
- return /* @__PURE__ */ jsxs6(Box6, { children: [
3107
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: label.padEnd(9) }),
3108
- /* @__PURE__ */ jsx6(Text6, { color, wrap: "truncate", children: value })
3109
- ] });
3110
- }
3111
3082
 
3112
3083
  // src/tui/panels/Settings.tsx
3113
3084
  import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
3114
3085
  import TextInput5 from "ink-text-input";
3115
3086
  import { useEffect as useEffect6, useRef as useRef3, useState as useState7 } from "react";
3116
3087
 
3117
- // src/logs-server-daemon.ts
3118
- import { spawn as spawn3 } from "child_process";
3119
- import { mkdirSync as mkdirSync4, openSync as openSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "fs";
3088
+ // src/global-install.ts
3089
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync5, existsSync as existsSync2, mkdirSync as mkdirSync4, rmSync } from "fs";
3090
+ import { resolve as resolve2, join as join6, dirname } from "path";
3120
3091
  import { homedir as homedir6 } from "os";
3121
- import { dirname, join as join6 } from "path";
3122
3092
  import { fileURLToPath } from "url";
3123
- var DIR = join6(homedir6(), ".solongate");
3124
- var STATE_FILE = join6(DIR, ".logs-server.json");
3125
- var LOG_FILE = join6(DIR, "logs-server.log");
3093
+ import { createInterface } from "readline";
3094
+ import { execFileSync } from "child_process";
3095
+ var __dirname = dirname(fileURLToPath(import.meta.url));
3096
+ var HOOKS_DIR = resolve2(__dirname, "..", "hooks");
3097
+ function globalPaths() {
3098
+ const home = homedir6();
3099
+ const sgDir = join6(home, ".solongate");
3100
+ const hooksDir = join6(sgDir, "hooks");
3101
+ const claudeDir = join6(home, ".claude");
3102
+ return {
3103
+ home,
3104
+ sgDir,
3105
+ hooksDir,
3106
+ claudeDir,
3107
+ settingsPath: join6(claudeDir, "settings.json"),
3108
+ backupPath: join6(claudeDir, "settings.solongate.bak"),
3109
+ configPath: join6(sgDir, "cloud-guard.json")
3110
+ };
3111
+ }
3112
+ function clearGuardUpdateCheck() {
3113
+ try {
3114
+ rmSync(join6(globalPaths().sgDir, ".hook-update-check"), { force: true });
3115
+ return true;
3116
+ } catch {
3117
+ return false;
3118
+ }
3119
+ }
3120
+
3121
+ // src/logs-server-daemon.ts
3122
+ import { spawn as spawn3 } from "child_process";
3123
+ import { mkdirSync as mkdirSync5, openSync as openSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync6 } from "fs";
3124
+ import { homedir as homedir7 } from "os";
3125
+ import { dirname as dirname2, join as join7 } from "path";
3126
+ import { fileURLToPath as fileURLToPath2 } from "url";
3127
+ var DIR = join7(homedir7(), ".solongate");
3128
+ var STATE_FILE = join7(DIR, ".logs-server.json");
3129
+ var LOG_FILE = join7(DIR, "logs-server.log");
3126
3130
  var LOGS_SERVER_PORT = 8788;
3127
3131
  function readState() {
3128
3132
  try {
3129
- const s = JSON.parse(readFileSync4(STATE_FILE, "utf-8"));
3133
+ const s = JSON.parse(readFileSync5(STATE_FILE, "utf-8"));
3130
3134
  return s && typeof s === "object" ? s : {};
3131
3135
  } catch {
3132
3136
  return {};
@@ -3134,8 +3138,8 @@ function readState() {
3134
3138
  }
3135
3139
  function writeState(s) {
3136
3140
  try {
3137
- mkdirSync4(DIR, { recursive: true });
3138
- writeFileSync5(STATE_FILE, JSON.stringify(s));
3141
+ mkdirSync5(DIR, { recursive: true });
3142
+ writeFileSync6(STATE_FILE, JSON.stringify(s));
3139
3143
  } catch {
3140
3144
  }
3141
3145
  }
@@ -3160,9 +3164,9 @@ function startLogsServerDaemon() {
3160
3164
  return { ...cur, desired: "on" };
3161
3165
  }
3162
3166
  try {
3163
- mkdirSync4(DIR, { recursive: true });
3167
+ mkdirSync5(DIR, { recursive: true });
3164
3168
  const log = openSync2(LOG_FILE, "a");
3165
- const cli = join6(dirname(fileURLToPath(import.meta.url)), "index.js");
3169
+ const cli = join7(dirname2(fileURLToPath2(import.meta.url)), "index.js");
3166
3170
  const p = spawn3(process.execPath, [cli, "logs-server"], {
3167
3171
  detached: true,
3168
3172
  stdio: ["ignore", log, log],
@@ -3190,7 +3194,7 @@ function stopLogsServerDaemon() {
3190
3194
  }
3191
3195
 
3192
3196
  // src/tui/panels/Settings.tsx
3193
- import { Fragment as Fragment5, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
3197
+ import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
3194
3198
  var EVENTS = ["denials", "allowed", "all"];
3195
3199
  var SPIN2 = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
3196
3200
  var SIGNALS2 = ["any", "deny", "dlp", "ratelimit"];
@@ -3370,6 +3374,16 @@ function SettingsPanel({
3370
3374
  setMsg({ text: `viewing ${acctLabel(r.acc)}`, level: "ok" });
3371
3375
  } else if (r.kind === "acct-add") {
3372
3376
  beginLogin();
3377
+ } else if (r.kind === "guard") {
3378
+ if (!guard) return;
3379
+ if (guard.up_to_date) {
3380
+ setMsg({ text: `guard already on the latest hook (v${guard.latest})`, level: "ok" });
3381
+ return;
3382
+ }
3383
+ const armed = clearGuardUpdateCheck();
3384
+ setMsg(
3385
+ 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" }
3386
+ );
3373
3387
  } else if (r.kind === "self") {
3374
3388
  if (!selfProt) return;
3375
3389
  run(selfProt.enabled ? "self-protection disabled" : "self-protection enabled", () => api.settings.setSelfProtection(!selfProt.enabled), selfQ.reload);
@@ -3608,9 +3622,9 @@ function SettingsPanel({
3608
3622
  return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
3609
3623
  cursor(r),
3610
3624
  /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "guard".padEnd(11) }),
3611
- guard ? /* @__PURE__ */ jsxs7(Fragment5, { children: [
3625
+ guard ? /* @__PURE__ */ jsxs7(Fragment4, { children: [
3612
3626
  /* @__PURE__ */ jsx7(Text7, { color: guard.up_to_date ? theme.ok : theme.warn, children: `v${guard.installed ?? "?"}` }),
3613
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: guard.up_to_date ? " (latest)" : ` \u2192 v${guard.latest} available` }),
3627
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: guard.up_to_date ? " (latest)" : ` \u2192 v${guard.latest} available \xB7 enter to update` }),
3614
3628
  /* @__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` : ""}` })
3615
3629
  ] }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "loading\u2026" })
3616
3630
  ] });
@@ -3676,7 +3690,7 @@ function SettingsPanel({
3676
3690
  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";
3677
3691
  const SECTION_DESC = {
3678
3692
  ACCOUNTS: `on this device (${accounts.length}) \xB7 \u25CF viewing \xB7 ACTIVE = guard key \xB7 x removes`,
3679
- PROTECTION: "guard hook version + self-protection toggle",
3693
+ PROTECTION: "guard hook version (enter to update) + self-protection toggle",
3680
3694
  "LOCAL LOGS": "mirror every decision to a file + dashboard link",
3681
3695
  WEBHOOKS: `POST events to a URL (${webhooks.length}) \xB7 t tests`,
3682
3696
  ALERTS: `one email + one telegram (${alerts.length}) \xB7 enter edits \xB7 m on/off`
@@ -3734,18 +3748,18 @@ function SettingsPanel({
3734
3748
 
3735
3749
  // src/self-update.ts
3736
3750
  import { execFile, spawn as spawn4 } from "child_process";
3737
- import { mkdirSync as mkdirSync5, openSync as openSync3, readFileSync as readFileSync5, realpathSync, writeFileSync as writeFileSync6 } from "fs";
3738
- import { homedir as homedir7 } from "os";
3739
- import { dirname as dirname2, join as join7 } from "path";
3740
- import { fileURLToPath as fileURLToPath2 } from "url";
3751
+ import { mkdirSync as mkdirSync6, openSync as openSync3, readFileSync as readFileSync6, realpathSync, writeFileSync as writeFileSync7 } from "fs";
3752
+ import { homedir as homedir8 } from "os";
3753
+ import { dirname as dirname3, join as join8 } from "path";
3754
+ import { fileURLToPath as fileURLToPath3 } from "url";
3741
3755
  var PKG = "@solongate/proxy";
3742
3756
  var CHECK_EVERY_MS = 30 * 60 * 1e3;
3743
3757
  var ATTEMPT_EVERY_MS = 6 * 60 * 60 * 1e3;
3744
- var STATE_FILE2 = join7(homedir7(), ".solongate", ".self-update.json");
3745
- var LOG_FILE2 = join7(homedir7(), ".solongate", "self-update.log");
3758
+ var STATE_FILE2 = join8(homedir8(), ".solongate", ".self-update.json");
3759
+ var LOG_FILE2 = join8(homedir8(), ".solongate", "self-update.log");
3746
3760
  function readState2() {
3747
3761
  try {
3748
- const s = JSON.parse(readFileSync5(STATE_FILE2, "utf-8"));
3762
+ const s = JSON.parse(readFileSync6(STATE_FILE2, "utf-8"));
3749
3763
  return s && typeof s === "object" ? s : {};
3750
3764
  } catch {
3751
3765
  return {};
@@ -3753,14 +3767,14 @@ function readState2() {
3753
3767
  }
3754
3768
  function writeState2(s) {
3755
3769
  try {
3756
- mkdirSync5(join7(homedir7(), ".solongate"), { recursive: true });
3757
- writeFileSync6(STATE_FILE2, JSON.stringify(s));
3770
+ mkdirSync6(join8(homedir8(), ".solongate"), { recursive: true });
3771
+ writeFileSync7(STATE_FILE2, JSON.stringify(s));
3758
3772
  } catch {
3759
3773
  }
3760
3774
  }
3761
3775
  function currentVersion() {
3762
3776
  try {
3763
- const pkg = JSON.parse(readFileSync5(join7(dirname2(fileURLToPath2(import.meta.url)), "..", "package.json"), "utf-8"));
3777
+ const pkg = JSON.parse(readFileSync6(join8(dirname3(fileURLToPath3(import.meta.url)), "..", "package.json"), "utf-8"));
3764
3778
  return pkg.version ?? "0.0.0";
3765
3779
  } catch {
3766
3780
  return "0.0.0";
@@ -3792,13 +3806,13 @@ async function fetchLatest() {
3792
3806
  }
3793
3807
  }
3794
3808
  function npmGlobalRoot() {
3795
- return new Promise((resolve2) => {
3809
+ return new Promise((resolve3) => {
3796
3810
  try {
3797
3811
  execFile("npm", ["root", "-g"], { timeout: 5e3, windowsHide: true, shell: process.platform === "win32" }, (err, stdout) => {
3798
- resolve2(err ? null : stdout.trim() || null);
3812
+ resolve3(err ? null : stdout.trim() || null);
3799
3813
  });
3800
3814
  } catch {
3801
- resolve2(null);
3815
+ resolve3(null);
3802
3816
  }
3803
3817
  });
3804
3818
  }
@@ -3814,26 +3828,26 @@ async function isGlobalInstall() {
3814
3828
  }
3815
3829
  }
3816
3830
  function runGlobalInstall(version) {
3817
- return new Promise((resolve2) => {
3831
+ return new Promise((resolve3) => {
3818
3832
  try {
3819
- mkdirSync5(join7(homedir7(), ".solongate"), { recursive: true });
3833
+ mkdirSync6(join8(homedir8(), ".solongate"), { recursive: true });
3820
3834
  execFile(
3821
3835
  "npm",
3822
3836
  ["install", "-g", `${PKG}@${version}`],
3823
3837
  { timeout: 3e5, windowsHide: true, shell: process.platform === "win32" },
3824
3838
  (err, stdout, stderr) => {
3825
3839
  try {
3826
- writeFileSync6(LOG_FILE2, `${(/* @__PURE__ */ new Date()).toISOString()} install ${version}: ${err ? "FAILED" : "ok"}
3840
+ writeFileSync7(LOG_FILE2, `${(/* @__PURE__ */ new Date()).toISOString()} install ${version}: ${err ? "FAILED" : "ok"}
3827
3841
  ${stdout}
3828
3842
  ${stderr}
3829
3843
  `, { flag: "a" });
3830
3844
  } catch {
3831
3845
  }
3832
- resolve2(!err);
3846
+ resolve3(!err);
3833
3847
  }
3834
3848
  );
3835
3849
  } catch {
3836
- resolve2(false);
3850
+ resolve3(false);
3837
3851
  }
3838
3852
  });
3839
3853
  }
@@ -4037,11 +4051,11 @@ async function launchTui() {
4037
4051
  return;
4038
4052
  }
4039
4053
  process.stdout.write("\x1B[?1049h\x1B[H");
4040
- const debugLog = join8(homedir8(), ".solongate", "dataroom-debug.log");
4054
+ const debugLog = join9(homedir9(), ".solongate", "dataroom-debug.log");
4041
4055
  const saved = { log: console.log, warn: console.warn, error: console.error, info: console.info, debug: console.debug };
4042
4056
  const toFile = (level) => (...args) => {
4043
4057
  try {
4044
- mkdirSync6(join8(homedir8(), ".solongate"), { recursive: true });
4058
+ mkdirSync7(join9(homedir9(), ".solongate"), { recursive: true });
4045
4059
  appendFileSync(debugLog, `${(/* @__PURE__ */ new Date()).toISOString()} [${level}] ${args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}
4046
4060
  `);
4047
4061
  } catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.81.69",
3
+ "version": "0.81.70",
4
4
  "description": "AI tool security proxy: protect any AI tool server with customizable policies, path/command constraints, rate limiting, and audit logging. No code changes required.",
5
5
  "type": "module",
6
6
  "bin": {