@solongate/proxy 0.71.0 → 0.73.0

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.
Files changed (3) hide show
  1. package/dist/index.js +208 -213
  2. package/dist/tui/index.js +178 -183
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7030,12 +7030,11 @@ var init_hooks = __esm({
7030
7030
 
7031
7031
  // src/tui/panels/Live.tsx
7032
7032
  import { Box as Box2, Text as Text2, useInput } from "ink";
7033
- import { spawnSync } from "child_process";
7034
- import { closeSync, mkdirSync as mkdirSync3, openSync, readSync, statSync, writeFileSync as writeFileSync3 } from "fs";
7033
+ import { closeSync, openSync, readSync, statSync } from "fs";
7035
7034
  import { homedir as homedir3 } from "os";
7036
- import { dirname, join as join5 } from "path";
7035
+ import { join as join5 } from "path";
7037
7036
  import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
7038
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
7037
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
7039
7038
  function tailLines(file, maxBytes = 131072) {
7040
7039
  try {
7041
7040
  const size = statSync(file).size;
@@ -7051,22 +7050,6 @@ function tailLines(file, maxBytes = 131072) {
7051
7050
  return [];
7052
7051
  }
7053
7052
  }
7054
- function copyToClipboard(text) {
7055
- const tools = [
7056
- ["wl-copy", []],
7057
- ["xclip", ["-selection", "clipboard"]],
7058
- ["xsel", ["--clipboard", "--input"]]
7059
- ];
7060
- for (const [cmd, args] of tools) {
7061
- try {
7062
- const r = spawnSync(cmd, args, { input: text, timeout: 2e3 });
7063
- if (r.status === 0) return cmd;
7064
- } catch {
7065
- }
7066
- }
7067
- process.stdout.write(`\x1B]52;c;${Buffer.from(text, "utf-8").toString("base64")}\x07`);
7068
- return null;
7069
- }
7070
7053
  function cloudItem(e) {
7071
7054
  return {
7072
7055
  id: "c:" + e.id,
@@ -7076,6 +7059,7 @@ function cloudItem(e) {
7076
7059
  permission: (e.permission ?? "").slice(0, 4),
7077
7060
  detail: (e.arguments_summary ? JSON.stringify(e.arguments_summary) : e.reason ?? "").replace(/\s+/g, " "),
7078
7061
  dlp: !!e.dlp_matches?.length,
7062
+ burst: !!e.rate_limit_burst,
7079
7063
  source: "cloud",
7080
7064
  session: e.session_id ?? void 0,
7081
7065
  agent: e.agent_name ?? void 0,
@@ -7131,6 +7115,12 @@ function PaneTitle({ label, extra, width: width2 }) {
7131
7115
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: tail + "\u2500".repeat(Math.max(0, width2 - used)) })
7132
7116
  ] });
7133
7117
  }
7118
+ function Stat({ label, value, color }) {
7119
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginRight: 3, children: [
7120
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: label }),
7121
+ /* @__PURE__ */ jsx2(Text2, { bold: true, color, children: String(value) })
7122
+ ] });
7123
+ }
7134
7124
  function StreamLine({ e, loc }) {
7135
7125
  return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7136
7126
  /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
@@ -7146,6 +7136,7 @@ function StreamLine({ e, loc }) {
7146
7136
  /* @__PURE__ */ jsx2(Text2, { color: e.evalMs != null && e.evalMs > 500 ? theme.warn : theme.dim, children: (e.evalMs != null ? `${e.evalMs}ms` : "\u2014").padEnd(7) }),
7147
7137
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: truncate2(e.agent ?? "-", 11).padEnd(12) }),
7148
7138
  e.dlp ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: "DLP! " }) : null,
7139
+ e.burst ? /* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: "BURST " }) : null,
7149
7140
  e.rule && e.decision !== "ALLOW" ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: truncate2(e.rule, 14) + " " }) : null,
7150
7141
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.detail })
7151
7142
  ] });
@@ -7161,15 +7152,17 @@ function LivePanel({ active: active2 }) {
7161
7152
  const [log6, setLog] = useState2([]);
7162
7153
  const [filter, setFilter] = useState2("all");
7163
7154
  const [scroll, setScroll] = useState2(0);
7164
- const [toast, setToast] = useState2(null);
7165
7155
  const [mode, setMode] = useState2("stream");
7156
+ const [sessFilter, setSessFilter] = useState2("all");
7166
7157
  const [pickIdx, setPickIdx] = useState2(0);
7167
- const [detail, setDetail] = useState2(null);
7168
7158
  const [detailCloud, setDetailCloud] = useState2([]);
7169
- const [detailScroll, setDetailScroll] = useState2(0);
7159
+ const [tlScroll, setTlScroll] = useState2(0);
7170
7160
  const seenRef = useRef2(/* @__PURE__ */ new Set());
7171
7161
  const lastLocalTs = useRef2(0);
7172
7162
  const pausedUntil = useRef2(0);
7163
+ const [frozen, setFrozen] = useState2(false);
7164
+ const frozenRef = useRef2(false);
7165
+ frozenRef.current = frozen;
7173
7166
  const pushLog = useCallback2((msg, level = "ok") => {
7174
7167
  setLog((prev) => [...prev.slice(-59), { ts: Date.now(), msg, level }]);
7175
7168
  }, []);
@@ -7185,8 +7178,9 @@ function LivePanel({ active: active2 }) {
7185
7178
  },
7186
7179
  [pushLog]
7187
7180
  );
7188
- const paused = () => Date.now() < pausedUntil.current;
7181
+ const paused = () => frozenRef.current || Date.now() < pausedUntil.current;
7189
7182
  const pollLocal = useCallback2(() => {
7183
+ if (frozenRef.current) return;
7190
7184
  const lines = tailLines(LOCAL_LOG);
7191
7185
  if (!lines.length) {
7192
7186
  setLocalOn((prev) => prev === null ? false : prev);
@@ -7207,6 +7201,7 @@ function LivePanel({ active: active2 }) {
7207
7201
  permission: (j.permission ?? "").slice(0, 4),
7208
7202
  detail: (j.arguments ? JSON.stringify(j.arguments) : j.reason ?? "").replace(/\s+/g, " "),
7209
7203
  dlp: !!j.dlp,
7204
+ burst: !!j.rate_limit_burst,
7210
7205
  source: "local",
7211
7206
  session: j.session_id,
7212
7207
  agent: j.agent_name,
@@ -7252,6 +7247,7 @@ function LivePanel({ active: active2 }) {
7252
7247
  const t0 = Date.now();
7253
7248
  try {
7254
7249
  const fd = await api.audit.list({ limit: 50 });
7250
+ if (frozenRef.current) return;
7255
7251
  const ms = Date.now() - t0;
7256
7252
  setLat((l) => [...l, ms].slice(-240));
7257
7253
  const fresh = fd.entries.filter((e) => !seenRef.current.has(e.id));
@@ -7269,7 +7265,8 @@ function LivePanel({ active: active2 }) {
7269
7265
  const pollMedium = useCallback2(async () => {
7270
7266
  if (paused()) return;
7271
7267
  try {
7272
- setS(await api.stats.get());
7268
+ const sd = await api.stats.get();
7269
+ if (!frozenRef.current) setS(sd);
7273
7270
  } catch (e) {
7274
7271
  onApiError(e);
7275
7272
  }
@@ -7277,7 +7274,8 @@ function LivePanel({ active: active2 }) {
7277
7274
  const pollSlow = useCallback2(async () => {
7278
7275
  if (paused()) return;
7279
7276
  try {
7280
- setTsData(await api.stats.timeseries({ period: "24h" }));
7277
+ const td = await api.stats.timeseries({ period: "24h" });
7278
+ if (!frozenRef.current) setTsData(td);
7281
7279
  } catch (e) {
7282
7280
  onApiError(e);
7283
7281
  }
@@ -7308,25 +7306,12 @@ function LivePanel({ active: active2 }) {
7308
7306
  usePoll(() => {
7309
7307
  if (!paused()) guard.reload();
7310
7308
  }, 6e4, active2);
7311
- useEffect2(() => {
7312
- if (!detail) return;
7313
- setDetailCloud([]);
7314
- setDetailScroll(0);
7315
- let alive = true;
7316
- api.audit.list({ session_id: detail.id, limit: 100 }).then((r) => {
7317
- if (alive) setDetailCloud(r.entries.map(cloudItem));
7318
- }).catch(() => {
7319
- });
7320
- return () => {
7321
- alive = false;
7322
- };
7323
- }, [detail]);
7324
7309
  const [tick, setTick] = useState2(0);
7325
7310
  useEffect2(() => {
7326
- if (!active2) return;
7311
+ if (!active2 || frozen) return;
7327
7312
  const t = setInterval(() => setTick((n) => n + 1), 500);
7328
7313
  return () => clearInterval(t);
7329
- }, [active2]);
7314
+ }, [active2, frozen]);
7330
7315
  const startRef = useRef2(Date.now());
7331
7316
  const cols = process.stdout.columns ?? 100;
7332
7317
  const rows = process.stdout.rows ?? 30;
@@ -7340,67 +7325,68 @@ function LivePanel({ active: active2 }) {
7340
7325
  }
7341
7326
  const cloudDeduped = cloudBuf.filter((e) => !localKeys.has(`${e.tool}|${e.decision}|${Math.round(e.at / 6e4)}`));
7342
7327
  const mergedAll = [...cloudDeduped, ...localBuf].sort((a, b) => a.at - b.at);
7343
- const points = tsData?.timeseries ?? [];
7344
- const nowMin = Math.floor(nowMs / 6e4);
7345
- const minuteCounts = new Array(60).fill(0);
7346
- const minuteHot = new Array(60).fill(false);
7347
- for (const e of mergedAll) {
7348
- const idx = 59 - (nowMin - Math.floor(e.at / 6e4));
7349
- if (idx >= 0 && idx < 60) {
7350
- minuteCounts[idx]++;
7351
- if (e.decision !== "ALLOW") minuteHot[idx] = true;
7352
- }
7353
- }
7354
- const localTraffic = localOn === true && minuteCounts.some((c2) => c2 > 0);
7355
- const traffic = localTraffic ? minuteCounts : points.map((p) => p.total);
7356
- const trafficHot = localTraffic ? minuteHot : points.map((p) => p.denied > 0);
7357
- const trafficLabel = localTraffic ? "calls/min \xB7 60m \xB7 local+cloud" : "24h \xB7 cloud";
7358
- const rl = ins.layers?.rateLimit;
7359
- const dl = ins.layers?.dlp;
7360
- const gh = ins.layers?.ghost;
7361
- const minuteNow = minuteCounts[59] ?? 0;
7362
- const dlpBars = (ins.dlpByPattern ?? []).slice(0, 2);
7363
- const maxDlpBar = dlpBars[0]?.count ?? 1;
7364
- const denialsAll = mergedAll.filter((e) => e.decision !== "ALLOW");
7365
- const lastDeny = denialsAll[denialsAll.length - 1];
7366
- const toolCounts = /* @__PURE__ */ new Map();
7367
- for (const e of mergedAll) toolCounts.set(e.tool, (toolCounts.get(e.tool) ?? 0) + 1);
7368
- const topTools = [...toolCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
7369
- const latNow = lat[lat.length - 1] ?? 0;
7370
- const sortedLat = [...lat].sort((a, b) => a - b);
7371
- const latMed = sortedLat.length ? sortedLat[Math.floor(sortedLat.length / 2)] : 0;
7372
- const latHotAt = Math.max(2e3, latMed * 2.5);
7373
- const backingOff = Date.now() < pausedUntil.current;
7374
- const sess = (sessions.data?.agents ?? []).slice().sort((a, b) => Date.parse(b.last_seen_at) - Date.parse(a.last_seen_at));
7375
- const sessCounts = sessions.data?.counts;
7376
7328
  const localSess = /* @__PURE__ */ new Map();
7377
7329
  for (const e of localBuf) {
7378
7330
  if (!e.session) continue;
7379
- const cur = localSess.get(e.session) ?? { agent: e.agent ?? "local agent", calls: 0, denies: 0, lastAt: 0 };
7331
+ const cur = localSess.get(e.session) ?? { agent: e.agent ?? "local agent", calls: 0, denies: 0, dlp: 0, lastAt: 0 };
7380
7332
  cur.calls++;
7381
7333
  if (e.decision !== "ALLOW") cur.denies++;
7334
+ if (e.dlp) cur.dlp++;
7382
7335
  cur.lastAt = Math.max(cur.lastAt, e.at);
7383
7336
  if (e.agent) cur.agent = e.agent;
7384
7337
  localSess.set(e.session, cur);
7385
7338
  }
7386
- const combinedSess = [
7387
- ...[...localSess.entries()].sort((a, b) => b[1].lastAt - a[1].lastAt).map(([id, v]) => ({ source: "local", id, agent: v.agent, calls: v.calls, denies: v.denies, lastAt: v.lastAt, isMe: ring?.session === id })),
7388
- ...sess.filter((a) => !localSess.has(a.session_id)).map((a) => ({
7339
+ const combinedSessAll = [
7340
+ ...[...localSess.entries()].sort((a, b) => b[1].lastAt - a[1].lastAt).map(([id, v]) => ({ source: "local", id, agent: v.agent, calls: v.calls, denies: v.denies, dlp: v.dlp, lastAt: v.lastAt, isMe: ring?.session === id })),
7341
+ ...(sessions.data?.agents ?? []).filter((a) => !localSess.has(a.session_id)).sort((a, b) => Date.parse(b.last_seen_at) - Date.parse(a.last_seen_at)).map((a) => ({
7389
7342
  source: "cloud",
7390
7343
  id: a.session_id,
7391
7344
  agent: a.agent_name ?? a.session_id,
7392
7345
  calls: a.total_calls,
7393
7346
  denies: a.denied_calls,
7347
+ dlp: a.dlp_events,
7394
7348
  lastAt: Date.parse(a.last_seen_at),
7395
7349
  trust: a.trust_score,
7396
7350
  isMe: false
7397
7351
  }))
7398
7352
  ];
7353
+ const combinedSess = sessFilter === "all" ? combinedSessAll : combinedSessAll.filter((r) => sessStatus(r.lastAt, nowMs) === sessFilter);
7399
7354
  const localSessIds = new Set(localSess.keys());
7400
7355
  if (ring?.session) localSessIds.add(ring.session);
7401
7356
  const isLoc = (e) => e.source === "local" || !!e.session && localSessIds.has(e.session);
7402
7357
  const filtered = filter === "all" ? mergedAll : mergedAll.filter((e) => filter === "local" ? isLoc(e) : !isLoc(e));
7403
7358
  const visibleDesc = filtered.slice(-STREAM_KEEP).reverse();
7359
+ const points = tsData?.timeseries ?? [];
7360
+ const nowSlot = Math.floor(nowMs / 1e4);
7361
+ const slotCounts = new Array(60).fill(0);
7362
+ const slotHot = new Array(60).fill(false);
7363
+ for (const e of mergedAll) {
7364
+ const idx = 59 - (nowSlot - Math.floor(e.at / 1e4));
7365
+ if (idx >= 0 && idx < 60) {
7366
+ slotCounts[idx]++;
7367
+ if (e.decision !== "ALLOW") slotHot[idx] = true;
7368
+ }
7369
+ }
7370
+ const localTraffic = localOn === true && slotCounts.some((c2) => c2 > 0);
7371
+ const traffic = localTraffic ? slotCounts : points.map((p) => p.total);
7372
+ const trafficHot = localTraffic ? slotHot : points.map((p) => p.denied > 0);
7373
+ const trafficLabel = localTraffic ? "calls/10s \xB7 last 10m" : "24h \xB7 cloud";
7374
+ const rl = ins.layers?.rateLimit;
7375
+ const dl = ins.layers?.dlp;
7376
+ const gh = ins.layers?.ghost;
7377
+ const minuteNow = mergedAll.filter((e) => nowMs - e.at < 6e4).length;
7378
+ const dlpBars = (ins.dlpByPattern ?? []).slice(0, 2);
7379
+ const maxDlpBar = dlpBars[0]?.count ?? 1;
7380
+ const denialsAll = mergedAll.filter((e) => e.decision !== "ALLOW");
7381
+ const lastDeny = denialsAll[denialsAll.length - 1];
7382
+ const toolCounts = /* @__PURE__ */ new Map();
7383
+ for (const e of mergedAll) toolCounts.set(e.tool, (toolCounts.get(e.tool) ?? 0) + 1);
7384
+ const topTools = [...toolCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
7385
+ const latNow = lat[lat.length - 1] ?? 0;
7386
+ const sortedLat = [...lat].sort((a, b) => a - b);
7387
+ const latMed = sortedLat.length ? sortedLat[Math.floor(sortedLat.length / 2)] : 0;
7388
+ const latHotAt = Math.max(2e3, latMed * 2.5);
7389
+ const backingOff = Date.now() < pausedUntil.current;
7404
7390
  const chartH = rows >= 36 ? 6 : 4;
7405
7391
  const colH = rows >= 32 ? 7 : 5;
7406
7392
  const streamRows = Math.max(4, rows - 5 - (1 + chartH) - colH);
@@ -7408,14 +7394,25 @@ function LivePanel({ active: active2 }) {
7408
7394
  const leftW = Math.floor(innerW * 0.55);
7409
7395
  const rightW = innerW - leftW - 2;
7410
7396
  const colW = Math.max(20, Math.floor((innerW - 4) / 3));
7411
- const pickable = combinedSess.slice(0, colH - 1);
7412
7397
  const maxScroll = Math.max(0, visibleDesc.length - streamRows);
7413
7398
  const clampedScroll = Math.min(scroll, maxScroll);
7414
7399
  const windowed = visibleDesc.slice(clampedScroll, clampedScroll + streamRows);
7415
- const detailEntries = detail ? (() => {
7400
+ const selIdx = Math.min(pickIdx, Math.max(0, combinedSess.length - 1));
7401
+ const selected = combinedSess[selIdx];
7402
+ useEffect2(() => {
7403
+ if (mode !== "sessions" || !selected?.id) return;
7404
+ setTlScroll(0);
7405
+ const t = setTimeout(() => {
7406
+ api.audit.list({ session_id: selected.id, limit: 100 }).then((r) => {
7407
+ if (!frozenRef.current) setDetailCloud(r.entries.map(cloudItem));
7408
+ }).catch(() => setDetailCloud([]));
7409
+ }, 400);
7410
+ return () => clearTimeout(t);
7411
+ }, [mode, selected?.id]);
7412
+ const detailEntries = selected ? (() => {
7416
7413
  const seen = /* @__PURE__ */ new Set();
7417
7414
  const all = [];
7418
- for (const e of [...detailCloud, ...mergedAll.filter((x) => x.session === detail.id)]) {
7415
+ for (const e of [...detailCloud, ...mergedAll.filter((x) => x.session === selected.id)]) {
7419
7416
  const key = `${e.at}:${e.tool}:${e.decision}`;
7420
7417
  if (seen.has(key)) continue;
7421
7418
  seen.add(key);
@@ -7425,27 +7422,22 @@ function LivePanel({ active: active2 }) {
7425
7422
  })() : [];
7426
7423
  useInput(
7427
7424
  (input, key) => {
7428
- if (mode === "detail") {
7429
- const maxD = Math.max(0, detailEntries.length - (rows - 10));
7430
- if (key.leftArrow) {
7431
- setMode("stream");
7432
- setDetail(null);
7433
- } else if (key.upArrow) setDetailScroll((n) => Math.max(0, n - 1));
7434
- else if (key.downArrow) setDetailScroll((n) => Math.min(maxD, n + 1));
7435
- else if (key.pageUp) setDetailScroll((n) => Math.max(0, n - 10));
7436
- else if (key.pageDown) setDetailScroll((n) => Math.min(maxD, n + 10));
7425
+ if (input === " ") {
7426
+ setFrozen((f) => !f);
7437
7427
  return;
7438
7428
  }
7439
- if (mode === "pick") {
7440
- if (key.upArrow) setPickIdx((n) => Math.max(0, n - 1));
7441
- else if (key.downArrow) setPickIdx((n) => Math.min(pickable.length - 1, n + 1));
7442
- else if (key.return) {
7443
- const row = pickable[pickIdx];
7444
- if (row) {
7445
- setDetail(row);
7446
- setMode("detail");
7447
- }
7448
- } else if (input === "s" || key.leftArrow) setMode("stream");
7429
+ if (frozen) return;
7430
+ if (mode === "sessions") {
7431
+ if (key.leftArrow) {
7432
+ setMode("stream");
7433
+ setDetailCloud([]);
7434
+ } else if (key.upArrow) setPickIdx((n) => Math.max(0, n - 1));
7435
+ else if (key.downArrow) setPickIdx((n) => Math.min(combinedSess.length - 1, n + 1));
7436
+ else if (input === "f") {
7437
+ setSessFilter((f) => SESS_FILTERS[(SESS_FILTERS.indexOf(f) + 1) % SESS_FILTERS.length]);
7438
+ setPickIdx(0);
7439
+ } else if (key.pageUp) setTlScroll((n) => Math.max(0, n - 10));
7440
+ else if (key.pageDown) setTlScroll((n) => n + 10);
7449
7441
  return;
7450
7442
  }
7451
7443
  if (input === "f") {
@@ -7453,30 +7445,14 @@ function LivePanel({ active: active2 }) {
7453
7445
  setScroll(0);
7454
7446
  } else if (input === "s") {
7455
7447
  setPickIdx(0);
7456
- setMode("pick");
7448
+ setMode("sessions");
7457
7449
  } else if (key.downArrow) setScroll((n) => Math.min(maxScroll, n + 1));
7458
7450
  else if (key.upArrow) setScroll((n) => Math.max(0, n - 1));
7459
7451
  else if (key.pageDown) setScroll((n) => Math.min(maxScroll, n + streamRows));
7460
7452
  else if (key.pageUp) setScroll((n) => Math.max(0, n - streamRows));
7461
- else if (input === "c") {
7462
- const dump = visibleDesc.map(
7463
- (e) => `[${new Date(e.at).toISOString()}] ${e.source.toUpperCase().padEnd(5)} ${e.decision.padEnd(6)} ${e.tool.padEnd(16)} ${e.permission.padEnd(5)} ${e.evalMs != null ? String(e.evalMs).padStart(5) + "ms " : " "}${(e.agent ?? "-").padEnd(14)} ${e.session ? e.session.slice(0, 8) : "- "} ${e.detail}`
7464
- ).join("\n");
7465
- try {
7466
- mkdirSync3(dirname(DUMP), { recursive: true });
7467
- writeFileSync3(DUMP, dump + "\n");
7468
- } catch {
7469
- }
7470
- const via = copyToClipboard(dump);
7471
- setToast(
7472
- via ? { msg: `\u2713 ${visibleDesc.length} lines in clipboard (${via})`, until: Date.now() + 4e3 } : { msg: "\u2717 no clipboard tool \u2014 install wl-clipboard or xclip", until: Date.now() + 6e3 }
7473
- );
7474
- }
7475
7453
  },
7476
7454
  { isActive: active2 }
7477
7455
  );
7478
- const sessionDot = (st) => st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\u25CC", color: theme.warn } : { ch: "\u25CB", color: theme.dim };
7479
- void sessionDot;
7480
7456
  if (s === null && localOn === null) {
7481
7457
  return /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
7482
7458
  spin,
@@ -7486,72 +7462,91 @@ function LivePanel({ active: active2 }) {
7486
7462
  const titleBar = /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7487
7463
  /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: " SOLONGATE LIVE " }),
7488
7464
  /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` ${spin} up ${fmtUp(nowMs - startRef.current)} \xB7 ${hhmmss(nowMs)} \xB7 api ${latNow}ms ` }),
7489
- backingOff ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d2a12", color: "#ffb454", bold: true, children: " RATE LIMITED \xB7 backing off " }) : lastDeny ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d1220", color: "#ff6b6b", bold: true, children: ` \u26A0 ${hhmmss(lastDeny.at)} ${lastDeny.tool} DENIED ` }) : /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#4f6db8", children: " \u2713 clean " }),
7490
- toast && nowMs < toast.until ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#123d1f", color: "#7bd88f", bold: true, children: ` ${toast.msg} ` }) : null
7465
+ frozen ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#123d1f", color: "#7bd88f", bold: true, children: " \u23F5 COPY MODE \u2014 screen frozen, select & copy freely \xB7 space resume " }) : backingOff ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d2a12", color: "#ffb454", bold: true, children: " RATE LIMITED \xB7 backing off " }) : lastDeny ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d1220", color: "#ff6b6b", bold: true, children: ` \u26A0 ${hhmmss(lastDeny.at)} ${lastDeny.tool} DENIED ` }) : /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#4f6db8", children: " \u2713 clean " })
7491
7466
  ] });
7492
- if (mode === "detail" && detail) {
7467
+ if (mode === "sessions") {
7468
+ const listW = Math.min(44, Math.floor(innerW * 0.38));
7469
+ const detailW = innerW - listW - 2;
7470
+ const bodyH = rows - 4;
7471
+ const listRows = bodyH - 1;
7493
7472
  const d = detailEntries;
7494
7473
  const allow = d.filter((e) => e.decision === "ALLOW").length;
7495
7474
  const deny = d.length - allow;
7496
7475
  const dlpN = d.filter((e) => e.dlp).length;
7476
+ const burstN = d.filter((e) => e.burst).length;
7497
7477
  const evals = d.filter((e) => e.evalMs != null).map((e) => e.evalMs);
7498
7478
  const evalAvg = evals.length ? Math.round(evals.reduce((a, b) => a + b, 0) / evals.length) : null;
7499
7479
  const tCounts = /* @__PURE__ */ new Map();
7500
- const pCounts = /* @__PURE__ */ new Map();
7501
- for (const e of d) {
7502
- tCounts.set(e.tool, (tCounts.get(e.tool) ?? 0) + 1);
7503
- if (e.permission) pCounts.set(e.permission, (pCounts.get(e.permission) ?? 0) + 1);
7504
- }
7505
- const top = [...tCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 6);
7506
- const perms = [...pCounts.entries()].sort((a, b) => b[1] - a[1]);
7480
+ for (const e of d) tCounts.set(e.tool, (tCounts.get(e.tool) ?? 0) + 1);
7481
+ const top = [...tCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5);
7507
7482
  const first = d[d.length - 1]?.at;
7508
7483
  const last = d[0]?.at;
7509
- const tlRows = Math.max(4, rows - 10);
7510
- const maxD = Math.max(0, d.length - tlRows);
7511
- const dScroll = Math.min(detailScroll, maxD);
7512
- const tl = d.slice(dScroll, dScroll + tlRows);
7484
+ const tlRows = Math.max(3, bodyH - 8);
7485
+ const maxTl = Math.max(0, d.length - tlRows);
7486
+ const tScroll = Math.min(tlScroll, maxTl);
7487
+ const tl = d.slice(tScroll, tScroll + tlRows);
7513
7488
  return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
7514
7489
  titleBar,
7515
- /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7516
- /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#AAC8FA", bold: true, children: ` SESSION ${detail.id.slice(0, 8)} ` }),
7517
- /* @__PURE__ */ jsx2(Text2, { color: detail.source === "local" ? theme.ok : "#4f6db8", children: " " + detail.source.toUpperCase() }),
7518
- /* @__PURE__ */ jsx2(Text2, { bold: true, children: " " + truncate2(detail.isMe ? detail.agent + " (this machine)" : detail.agent, 34) }),
7519
- detail.trust != null ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ` trust ${detail.trust}/100` }) : null,
7520
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2190 back" })
7521
- ] }),
7522
- /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7523
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 calls " }),
7524
- /* @__PURE__ */ jsx2(Text2, { bold: true, children: d.length }),
7525
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 allow " }),
7526
- /* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: allow }),
7527
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 deny " }),
7528
- /* @__PURE__ */ jsx2(Text2, { color: deny ? theme.bad : theme.dim, bold: deny > 0, children: deny }),
7529
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 dlp " }),
7530
- /* @__PURE__ */ jsx2(Text2, { color: dlpN ? theme.bad : theme.dim, children: dlpN }),
7531
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 avg eval " }),
7532
- /* @__PURE__ */ jsx2(Text2, { children: evalAvg != null ? `${evalAvg}ms` : "\u2014" }),
7533
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 first " }),
7534
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: first ? `${hhmmss(first)} (${ago(first)} ago)` : "\u2014" }),
7535
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 last " }),
7536
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: last ? `${hhmmss(last)} (${ago(last)} ago)` : "\u2014" }),
7537
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
7538
- ] }),
7539
- /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7540
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 tools " }),
7541
- /* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: top.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014" }),
7542
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 perms " }),
7543
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: perms.map(([p, c2]) => `${p}\xD7${c2}`).join(" ") || "\u2014" }),
7544
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
7545
- ] }),
7546
- /* @__PURE__ */ jsx2(PaneTitle, { label: "TIMELINE", extra: `${d.length} calls \xB7 newest first${dScroll ? ` \xB7 \u25BC${dScroll} older` : ""} \xB7 \u2191\u2193 scroll \xB7 \u2190 back`, width: innerW }),
7547
- /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: tlRows, overflow: "hidden", children: [
7548
- tl.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: detailCloud.length === 0 ? spin + " loading history\u2026" : "no entries" }) : null,
7549
- tl.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e) }, e.id))
7490
+ /* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: `${combinedSess.length} shown \xB7 filter ${sessFilter} (f) \xB7 \u2191\u2193 select \xB7 PgUp/Dn timeline \xB7 \u2190 back`, width: innerW }),
7491
+ /* @__PURE__ */ jsxs2(Box2, { height: bodyH, children: [
7492
+ /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: listW, marginRight: 2, overflow: "hidden", children: [
7493
+ combinedSess.length === 0 ? /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
7494
+ "no sessions",
7495
+ sessFilter !== "all" ? ` (${sessFilter})` : ""
7496
+ ] }) : null,
7497
+ combinedSess.slice(0, listRows).map((r, i) => {
7498
+ const st = sessStatus(r.lastAt, nowMs);
7499
+ const dot = st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\u25CC", color: theme.warn } : { ch: "\u25CB", color: theme.dim };
7500
+ const sel = i === selIdx;
7501
+ return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", backgroundColor: sel ? "#1c2f63" : void 0, bold: sel, children: [
7502
+ /* @__PURE__ */ jsxs2(Text2, { color: sel ? theme.accentBright : dot.color, children: [
7503
+ sel ? "\u25B8" : dot.ch,
7504
+ " "
7505
+ ] }),
7506
+ /* @__PURE__ */ jsx2(Text2, { color: r.source === "local" ? theme.ok : "#4f6db8", children: r.source === "local" ? "LOC " : "CLD " }),
7507
+ /* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, children: truncate2(r.isMe ? "this machine" : r.agent, 13).padEnd(14) }),
7508
+ /* @__PURE__ */ jsxs2(Text2, { children: [
7509
+ String(r.calls).padStart(4),
7510
+ "c "
7511
+ ] }),
7512
+ /* @__PURE__ */ jsxs2(Text2, { color: r.denies ? theme.bad : theme.dim, children: [
7513
+ String(r.denies).padStart(3),
7514
+ "d "
7515
+ ] }),
7516
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ago(r.lastAt) })
7517
+ ] }, r.id);
7518
+ })
7519
+ ] }),
7520
+ /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", width: detailW, overflow: "hidden", children: !selected ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "select a session to see its run timeline" }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
7521
+ /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7522
+ /* @__PURE__ */ jsx2(Text2, { color: sessStatus(selected.lastAt, nowMs) === "active" ? theme.ok : theme.dim, children: sessStatus(selected.lastAt, nowMs) === "active" ? "\u25CF " : "\u25CB " }),
7523
+ /* @__PURE__ */ jsx2(Text2, { bold: true, children: truncate2(selected.isMe ? selected.agent + " (this machine)" : selected.agent, 30) }),
7524
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " " + selected.id })
7525
+ ] }),
7526
+ /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, children: [
7527
+ /* @__PURE__ */ jsx2(Stat, { label: "Total", value: d.length }),
7528
+ /* @__PURE__ */ jsx2(Stat, { label: "Allowed", value: allow, color: theme.ok }),
7529
+ /* @__PURE__ */ jsx2(Stat, { label: "Denied", value: deny, color: deny ? theme.bad : void 0 }),
7530
+ /* @__PURE__ */ jsx2(Stat, { label: "DLP", value: dlpN, color: dlpN ? theme.bad : void 0 }),
7531
+ /* @__PURE__ */ jsx2(Stat, { label: "Rate limit", value: burstN, color: burstN ? theme.warn : void 0 }),
7532
+ /* @__PURE__ */ jsx2(Stat, { label: "Avg eval", value: evalAvg != null ? `${evalAvg}ms` : "\u2014" }),
7533
+ selected.trust != null ? /* @__PURE__ */ jsx2(Stat, { label: "Trust", value: `${selected.trust}/100`, color: theme.accent }) : null
7534
+ ] }),
7535
+ /* @__PURE__ */ jsx2(Text2, { wrap: "truncate", color: theme.dim, children: `first ${first ? `${hhmmss(first)} (${ago(first)} ago)` : "\u2014"} \xB7 last ${last ? `${hhmmss(last)} (${ago(last)} ago)` : "\u2014"} \xB7 tools ${top.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"}` }),
7536
+ /* @__PURE__ */ jsx2(PaneTitle, { label: "TIMELINE", extra: `${d.length} events \xB7 newest first${tScroll ? ` \xB7 \u25BC${tScroll}` : ""}`, width: detailW }),
7537
+ /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", overflow: "hidden", flexGrow: 1, children: [
7538
+ tl.length === 0 ? /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
7539
+ spin,
7540
+ " loading history\u2026"
7541
+ ] }) : null,
7542
+ tl.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e) }, e.id))
7543
+ ] })
7544
+ ] }) })
7550
7545
  ] }),
7551
7546
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7552
- /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " SESSION " }),
7553
- /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` ${detail.id} ` }),
7554
- /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " \u2191\u2193 scroll \xB7 \u2190 back \xB7 esc menu " })
7547
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " SESSIONS " }),
7548
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` ${combinedSess.length} sessions \xB7 space copy-mode ` }),
7549
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " \u2191\u2193 select \xB7 f status \xB7 \u2190 back \xB7 esc menu " })
7555
7550
  ] })
7556
7551
  ] });
7557
7552
  }
@@ -7573,7 +7568,7 @@ function LivePanel({ active: active2 }) {
7573
7568
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 ghost " }),
7574
7569
  /* @__PURE__ */ jsx2(Text2, { color: gh?.mode === "on" ? theme.ok : theme.dim, children: gh?.mode ?? "?" }),
7575
7570
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 sess " }),
7576
- /* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: `${combinedSess.length} (${sessCounts?.active ?? 0} live)` }),
7571
+ /* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: String(combinedSessAll.length) }),
7577
7572
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 hooks " }),
7578
7573
  /* @__PURE__ */ jsx2(Text2, { color: guard.data?.up_to_date ? theme.ok : theme.warn, children: guard.data ? `v${guard.data.installed ?? "?"}${guard.data.up_to_date ? "" : "\u2192v" + guard.data.latest} ${guard.data.device_count}dev` : "\xB7" }),
7579
7574
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
@@ -7612,14 +7607,13 @@ function LivePanel({ active: active2 }) {
7612
7607
  ] })
7613
7608
  ] }),
7614
7609
  /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, overflow: "hidden", children: [
7615
- /* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: mode === "pick" ? "\u2191\u2193 pick \xB7 enter open" : `${combinedSess.length} \xB7 s = inspect`, width: colW }),
7616
- pickable.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "scanning\u2026" }) : null,
7617
- pickable.map((r, i) => {
7618
- const freshDot = nowMs - r.lastAt < 9e4;
7619
- const sel = mode === "pick" && i === pickIdx;
7620
- return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", backgroundColor: sel ? "#1c2f63" : void 0, bold: sel || r.isMe, children: [
7621
- /* @__PURE__ */ jsxs2(Text2, { color: sel ? theme.accentBright : freshDot ? theme.ok : theme.dim, children: [
7622
- sel ? "\u25B8" : freshDot ? "\u25CF" : "\u25CB",
7610
+ /* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: "press s to inspect", width: colW }),
7611
+ combinedSessAll.slice(0, colH - 1).map((r) => {
7612
+ const st = sessStatus(r.lastAt, nowMs);
7613
+ const dot = st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\u25CC", color: theme.warn } : { ch: "\u25CB", color: theme.dim };
7614
+ return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", bold: r.isMe, children: [
7615
+ /* @__PURE__ */ jsxs2(Text2, { color: dot.color, children: [
7616
+ dot.ch,
7623
7617
  " "
7624
7618
  ] }),
7625
7619
  /* @__PURE__ */ jsx2(Text2, { color: r.source === "local" ? theme.ok : "#4f6db8", children: r.source === "local" ? "LOC " : "CLD " }),
@@ -7632,7 +7626,7 @@ function LivePanel({ active: active2 }) {
7632
7626
  String(r.denies).padStart(3),
7633
7627
  "d "
7634
7628
  ] }),
7635
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: (r.trust != null ? `t${r.trust} ` : "") + ago(r.lastAt) })
7629
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ago(r.lastAt) })
7636
7630
  ] }, r.id);
7637
7631
  })
7638
7632
  ] }),
@@ -7652,7 +7646,7 @@ function LivePanel({ active: active2 }) {
7652
7646
  PaneTitle,
7653
7647
  {
7654
7648
  label: "TOOL STREAM",
7655
- extra: `newest first \xB7 last ${STREAM_KEEP} \xB7 filter ${filter}${clampedScroll > 0 ? ` \xB7 \u25BC${clampedScroll} older` : ""} \xB7 f filter \xB7 \u2191\u2193 scroll \xB7 c copy \xB7 s sessions`,
7649
+ extra: `newest first \xB7 last ${STREAM_KEEP} \xB7 filter ${filter}${clampedScroll > 0 ? ` \xB7 \u25BC${clampedScroll} older` : ""} \xB7 f filter \xB7 \u2191\u2193 scroll \xB7 s sessions`,
7656
7650
  width: innerW
7657
7651
  }
7658
7652
  ),
@@ -7669,12 +7663,12 @@ function LivePanel({ active: active2 }) {
7669
7663
  ] }),
7670
7664
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7671
7665
  /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " LIVE " }),
7672
- /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` local ${localOn ? "on" : "off"} \xB7 ${localBuf.length} loc/${cloudBuf.length} cld \xB7 top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} ` }),
7673
- /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " f filter \xB7 s sessions \xB7 c copy \xB7 esc menu \xB7 q quit " })
7666
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` local ${localOn ? "on" : "off"} \xB7 ${localBuf.length} loc/${cloudDeduped.length} cld \xB7 top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} ` }),
7667
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " space copy-mode \xB7 f filter \xB7 s sessions \xB7 esc menu \xB7 q quit " })
7674
7668
  ] })
7675
7669
  ] });
7676
7670
  }
7677
- var SPIN, BG, DIM_FLOOR, LOCAL_LOG, RING, DUMP, STREAM_KEEP, hhmmss, fmtUp, FILTERS;
7671
+ var SPIN, BG, DIM_FLOOR, LOCAL_LOG, RING, STREAM_KEEP, hhmmss, fmtUp, FILTERS, SESS_FILTERS, sessStatus;
7678
7672
  var init_Live = __esm({
7679
7673
  "src/tui/panels/Live.tsx"() {
7680
7674
  "use strict";
@@ -7686,7 +7680,6 @@ var init_Live = __esm({
7686
7680
  DIM_FLOOR = "#233457";
7687
7681
  LOCAL_LOG = join5(homedir3(), ".solongate", "local-logs", "solongate-audit.jsonl");
7688
7682
  RING = join5(process.cwd(), ".solongate", ".eval-ring.jsonl");
7689
- DUMP = join5(homedir3(), ".solongate", "live-stream.log");
7690
7683
  STREAM_KEEP = 100;
7691
7684
  hhmmss = (ts) => {
7692
7685
  const d = new Date(ts);
@@ -7698,6 +7691,8 @@ var init_Live = __esm({
7698
7691
  return `${p(Math.floor(s / 3600))}:${p(Math.floor(s % 3600 / 60))}:${p(s % 60)}`;
7699
7692
  };
7700
7693
  FILTERS = ["all", "local", "cloud"];
7694
+ SESS_FILTERS = ["all", "active", "idle", "ended"];
7695
+ sessStatus = (lastAt, now) => now - lastAt < 9e4 ? "active" : now - lastAt < 3e5 ? "idle" : "ended";
7701
7696
  }
7702
7697
  });
7703
7698
 
@@ -9359,8 +9354,8 @@ __export(global_install_exports, {
9359
9354
  runGlobalRestore: () => runGlobalRestore,
9360
9355
  unlockProtected: () => unlockProtected
9361
9356
  });
9362
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync4, mkdirSync as mkdirSync4 } from "fs";
9363
- import { resolve as resolve4, join as join6, dirname as dirname2 } from "path";
9357
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
9358
+ import { resolve as resolve4, join as join6, dirname } from "path";
9364
9359
  import { homedir as homedir4 } from "os";
9365
9360
  import { fileURLToPath } from "url";
9366
9361
  import { createInterface } from "readline";
@@ -9472,13 +9467,13 @@ function runGlobalRestore() {
9472
9467
  unlockProtected();
9473
9468
  removeClaudeShim();
9474
9469
  if (existsSync4(p.backupPath)) {
9475
- writeFileSync4(p.settingsPath, readFileSync6(p.backupPath, "utf-8"));
9470
+ writeFileSync3(p.settingsPath, readFileSync6(p.backupPath, "utf-8"));
9476
9471
  console.log(` Restored ${p.settingsPath} from backup.`);
9477
9472
  } else if (existsSync4(p.settingsPath)) {
9478
9473
  try {
9479
9474
  const s = JSON.parse(readFileSync6(p.settingsPath, "utf-8"));
9480
9475
  delete s.hooks;
9481
- writeFileSync4(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
9476
+ writeFileSync3(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
9482
9477
  console.log(` Removed SolonGate hooks from ${p.settingsPath}.`);
9483
9478
  } catch {
9484
9479
  }
@@ -9522,8 +9517,8 @@ function writeShimBlock(file, block) {
9522
9517
  if (content.length && !content.endsWith("\n")) content += "\n";
9523
9518
  content += block + "\n";
9524
9519
  }
9525
- mkdirSync4(dirname2(file), { recursive: true });
9526
- writeFileSync4(file, content);
9520
+ mkdirSync3(dirname(file), { recursive: true });
9521
+ writeFileSync3(file, content);
9527
9522
  }
9528
9523
  function installClaudeShim(shieldPath) {
9529
9524
  const real = resolveRealClaude();
@@ -9580,22 +9575,22 @@ async function runGlobalInstall(opts = {}) {
9580
9575
  process.exit(1);
9581
9576
  }
9582
9577
  const apiUrl = opts.apiUrl || process.env["SOLONGATE_API_URL"] || "https://api.solongate.com";
9583
- mkdirSync4(p.hooksDir, { recursive: true });
9584
- mkdirSync4(p.claudeDir, { recursive: true });
9578
+ mkdirSync3(p.hooksDir, { recursive: true });
9579
+ mkdirSync3(p.claudeDir, { recursive: true });
9585
9580
  unlockProtected();
9586
- writeFileSync4(join6(p.hooksDir, "guard.mjs"), readGuard());
9587
- writeFileSync4(join6(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
9588
- writeFileSync4(join6(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
9589
- writeFileSync4(join6(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
9581
+ writeFileSync3(join6(p.hooksDir, "guard.mjs"), readGuard());
9582
+ writeFileSync3(join6(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
9583
+ writeFileSync3(join6(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
9584
+ writeFileSync3(join6(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
9590
9585
  console.log(` Installed hooks \u2192 ${p.hooksDir}`);
9591
9586
  removeClaudeShim();
9592
- writeFileSync4(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
9587
+ writeFileSync3(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
9593
9588
  console.log(` Wrote ${p.configPath}`);
9594
9589
  let existing = {};
9595
9590
  if (existsSync4(p.settingsPath)) {
9596
9591
  const raw = readFileSync6(p.settingsPath, "utf-8");
9597
9592
  if (!existsSync4(p.backupPath)) {
9598
- writeFileSync4(p.backupPath, raw);
9593
+ writeFileSync3(p.backupPath, raw);
9599
9594
  console.log(` Backed up existing settings \u2192 ${p.backupPath}`);
9600
9595
  }
9601
9596
  try {
@@ -9616,7 +9611,7 @@ async function runGlobalInstall(opts = {}) {
9616
9611
  Stop: [{ matcher: "", hooks: [{ type: "command", command: `"${nodeBin}" "${stopAbs}" claude-code "Claude Code"` }] }]
9617
9612
  }
9618
9613
  };
9619
- writeFileSync4(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
9614
+ writeFileSync3(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
9620
9615
  console.log(` Registered global hooks \u2192 ${p.settingsPath}`);
9621
9616
  if (process.env["SOLONGATE_OS_LOCK"] === "1") {
9622
9617
  lockProtected();
@@ -9630,7 +9625,7 @@ var __dirname, HOOKS_DIR, SHIM_BEGIN, SHIM_END;
9630
9625
  var init_global_install = __esm({
9631
9626
  "src/global-install.ts"() {
9632
9627
  "use strict";
9633
- __dirname = dirname2(fileURLToPath(import.meta.url));
9628
+ __dirname = dirname(fileURLToPath(import.meta.url));
9634
9629
  HOOKS_DIR = resolve4(__dirname, "..", "hooks");
9635
9630
  SHIM_BEGIN = "# >>> SolonGate shield (auto secret redaction) >>>";
9636
9631
  SHIM_END = "# <<< SolonGate shield <<<";
@@ -10280,7 +10275,7 @@ var init_logs_server = __esm({
10280
10275
 
10281
10276
  // src/inject.ts
10282
10277
  var inject_exports = {};
10283
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, existsSync as existsSync6, copyFileSync } from "fs";
10278
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync4, existsSync as existsSync6, copyFileSync } from "fs";
10284
10279
  import { resolve as resolve7 } from "path";
10285
10280
  import { execSync } from "child_process";
10286
10281
  function parseInjectArgs(argv) {
@@ -10588,7 +10583,7 @@ async function main2() {
10588
10583
  log3("");
10589
10584
  log3(` Backup: ${backupPath}`);
10590
10585
  }
10591
- writeFileSync5(entryFile, result.modified);
10586
+ writeFileSync4(entryFile, result.modified);
10592
10587
  log3("");
10593
10588
  log3(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
10594
10589
  log3(" \u2502 SolonGate SDK injected successfully! \u2502");
@@ -10617,7 +10612,7 @@ var init_inject = __esm({
10617
10612
 
10618
10613
  // src/create.ts
10619
10614
  var create_exports = {};
10620
- import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync6, existsSync as existsSync7 } from "fs";
10615
+ import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5, existsSync as existsSync7 } from "fs";
10621
10616
  import { resolve as resolve8, join as join8 } from "path";
10622
10617
  import { execSync as execSync2 } from "child_process";
10623
10618
  function withSpinner(message, fn) {
@@ -10697,7 +10692,7 @@ EXAMPLES
10697
10692
  `);
10698
10693
  }
10699
10694
  function createProject(dir, name, _policy) {
10700
- writeFileSync6(
10695
+ writeFileSync5(
10701
10696
  join8(dir, "package.json"),
10702
10697
  JSON.stringify(
10703
10698
  {
@@ -10727,7 +10722,7 @@ function createProject(dir, name, _policy) {
10727
10722
  2
10728
10723
  ) + "\n"
10729
10724
  );
10730
- writeFileSync6(
10725
+ writeFileSync5(
10731
10726
  join8(dir, "tsconfig.json"),
10732
10727
  JSON.stringify(
10733
10728
  {
@@ -10748,8 +10743,8 @@ function createProject(dir, name, _policy) {
10748
10743
  2
10749
10744
  ) + "\n"
10750
10745
  );
10751
- mkdirSync5(join8(dir, "src"), { recursive: true });
10752
- writeFileSync6(
10746
+ mkdirSync4(join8(dir, "src"), { recursive: true });
10747
+ writeFileSync5(
10753
10748
  join8(dir, "src", "index.ts"),
10754
10749
  `#!/usr/bin/env node
10755
10750
 
@@ -10791,7 +10786,7 @@ console.log('');
10791
10786
  console.log('Press Ctrl+C to stop.');
10792
10787
  `
10793
10788
  );
10794
- writeFileSync6(
10789
+ writeFileSync5(
10795
10790
  join8(dir, ".mcp.json"),
10796
10791
  JSON.stringify(
10797
10792
  {
@@ -10809,12 +10804,12 @@ console.log('Press Ctrl+C to stop.');
10809
10804
  2
10810
10805
  ) + "\n"
10811
10806
  );
10812
- writeFileSync6(
10807
+ writeFileSync5(
10813
10808
  join8(dir, ".env"),
10814
10809
  `SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
10815
10810
  `
10816
10811
  );
10817
- writeFileSync6(
10812
+ writeFileSync5(
10818
10813
  join8(dir, ".gitignore"),
10819
10814
  `node_modules/
10820
10815
  dist/
@@ -10834,7 +10829,7 @@ async function main3() {
10834
10829
  process.exit(1);
10835
10830
  }
10836
10831
  withSpinner(`Setting up ${opts.name}...`, () => {
10837
- mkdirSync5(dir, { recursive: true });
10832
+ mkdirSync4(dir, { recursive: true });
10838
10833
  createProject(dir, opts.name, opts.policy);
10839
10834
  });
10840
10835
  if (!opts.noInstall) {
@@ -10908,7 +10903,7 @@ var init_create = __esm({
10908
10903
 
10909
10904
  // src/pull-push.ts
10910
10905
  var pull_push_exports = {};
10911
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, existsSync as existsSync8 } from "fs";
10906
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as existsSync8 } from "fs";
10912
10907
  import { resolve as resolve9 } from "path";
10913
10908
  function loadEnv() {
10914
10909
  if (process.env.SOLONGATE_API_KEY) return;
@@ -11103,7 +11098,7 @@ async function pull(apiKey, file, policyId) {
11103
11098
  const policy = await fetchCloudPolicy(apiKey, API_URL, policyId);
11104
11099
  const { id: _id, ...policyWithoutId } = policy;
11105
11100
  const json = JSON.stringify(policyWithoutId, null, 2) + "\n";
11106
- writeFileSync7(file, json, "utf-8");
11101
+ writeFileSync6(file, json, "utf-8");
11107
11102
  log5("");
11108
11103
  log5(green2(" Saved to: ") + file);
11109
11104
  log5(` ${dim2("Name:")} ${policy.name}`);