@solongate/proxy 0.70.0 → 0.71.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.
@@ -84,17 +84,27 @@ async function request(method, path, opts = {}) {
84
84
  headers["Content-Type"] = "application/json";
85
85
  bodyInit = JSON.stringify(opts.body);
86
86
  }
87
+ const attempt = () => fetch(url, {
88
+ method,
89
+ headers,
90
+ body: bodyInit,
91
+ signal: AbortSignal.timeout(opts.timeoutMs ?? 15e3)
92
+ });
87
93
  let res;
88
94
  try {
89
- res = await fetch(url, {
90
- method,
91
- headers,
92
- body: bodyInit,
93
- signal: AbortSignal.timeout(opts.timeoutMs ?? 15e3)
94
- });
95
- } catch (err2) {
96
- const msg = err2 instanceof Error ? err2.message : String(err2);
97
- throw new ApiError(0, "NETWORK_ERROR", `Cannot reach SolonGate API: ${msg}`);
95
+ res = await attempt();
96
+ } catch (firstErr) {
97
+ if (method !== "GET") {
98
+ const msg = firstErr instanceof Error ? firstErr.message : String(firstErr);
99
+ throw new ApiError(0, "NETWORK_ERROR", `Cannot reach SolonGate API: ${msg}`);
100
+ }
101
+ await new Promise((r) => setTimeout(r, 800));
102
+ try {
103
+ res = await attempt();
104
+ } catch (err2) {
105
+ const msg = err2 instanceof Error ? err2.message : String(err2);
106
+ throw new ApiError(0, "NETWORK_ERROR", `Cannot reach SolonGate API: ${msg}`);
107
+ }
98
108
  }
99
109
  const text = await res.text().catch(() => "");
100
110
  let json = void 0;
package/dist/index.js CHANGED
@@ -6633,17 +6633,27 @@ async function request(method, path, opts = {}) {
6633
6633
  headers["Content-Type"] = "application/json";
6634
6634
  bodyInit = JSON.stringify(opts.body);
6635
6635
  }
6636
+ const attempt = () => fetch(url, {
6637
+ method,
6638
+ headers,
6639
+ body: bodyInit,
6640
+ signal: AbortSignal.timeout(opts.timeoutMs ?? 15e3)
6641
+ });
6636
6642
  let res;
6637
6643
  try {
6638
- res = await fetch(url, {
6639
- method,
6640
- headers,
6641
- body: bodyInit,
6642
- signal: AbortSignal.timeout(opts.timeoutMs ?? 15e3)
6643
- });
6644
- } catch (err2) {
6645
- const msg = err2 instanceof Error ? err2.message : String(err2);
6646
- throw new ApiError(0, "NETWORK_ERROR", `Cannot reach SolonGate API: ${msg}`);
6644
+ res = await attempt();
6645
+ } catch (firstErr) {
6646
+ if (method !== "GET") {
6647
+ const msg = firstErr instanceof Error ? firstErr.message : String(firstErr);
6648
+ throw new ApiError(0, "NETWORK_ERROR", `Cannot reach SolonGate API: ${msg}`);
6649
+ }
6650
+ await new Promise((r) => setTimeout(r, 800));
6651
+ try {
6652
+ res = await attempt();
6653
+ } catch (err2) {
6654
+ const msg = err2 instanceof Error ? err2.message : String(err2);
6655
+ throw new ApiError(0, "NETWORK_ERROR", `Cannot reach SolonGate API: ${msg}`);
6656
+ }
6647
6657
  }
6648
6658
  const text = await res.text().catch(() => "");
6649
6659
  let json = void 0;
@@ -7055,7 +7065,7 @@ function copyToClipboard(text) {
7055
7065
  }
7056
7066
  }
7057
7067
  process.stdout.write(`\x1B]52;c;${Buffer.from(text, "utf-8").toString("base64")}\x07`);
7058
- return "osc52";
7068
+ return null;
7059
7069
  }
7060
7070
  function cloudItem(e) {
7061
7071
  return {
@@ -7121,14 +7131,14 @@ function PaneTitle({ label, extra, width: width2 }) {
7121
7131
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: tail + "\u2500".repeat(Math.max(0, width2 - used)) })
7122
7132
  ] });
7123
7133
  }
7124
- function StreamLine({ e }) {
7134
+ function StreamLine({ e, loc }) {
7125
7135
  return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7126
7136
  /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
7127
7137
  "[",
7128
7138
  hhmmss(e.at),
7129
7139
  " "
7130
7140
  ] }),
7131
- /* @__PURE__ */ jsx2(Text2, { color: e.source === "local" ? theme.ok : "#4f6db8", children: e.source === "local" ? "LOC" : "CLD" }),
7141
+ /* @__PURE__ */ jsx2(Text2, { color: loc ? theme.ok : "#4f6db8", children: loc ? "LOC" : "CLD" }),
7132
7142
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "] " }),
7133
7143
  /* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), bold: e.decision !== "ALLOW", children: e.decision.padEnd(6) }),
7134
7144
  /* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate2(e.tool, 12).padEnd(13) }),
@@ -7323,9 +7333,13 @@ function LivePanel({ active: active2 }) {
7323
7333
  const ins = insights.data ?? {};
7324
7334
  const spin = SPIN[tick % SPIN.length];
7325
7335
  const nowMs = Date.now();
7326
- const mergedAll = [...cloudBuf, ...localBuf].sort((a, b) => a.at - b.at);
7327
- const filtered = filter === "all" ? mergedAll : mergedAll.filter((e) => e.source === filter);
7328
- const visibleDesc = filtered.slice(-STREAM_KEEP).reverse();
7336
+ const localKeys = /* @__PURE__ */ new Set();
7337
+ for (const e of localBuf) {
7338
+ const m = Math.round(e.at / 6e4);
7339
+ for (const d of [-1, 0, 1]) localKeys.add(`${e.tool}|${e.decision}|${m + d}`);
7340
+ }
7341
+ const cloudDeduped = cloudBuf.filter((e) => !localKeys.has(`${e.tool}|${e.decision}|${Math.round(e.at / 6e4)}`));
7342
+ const mergedAll = [...cloudDeduped, ...localBuf].sort((a, b) => a.at - b.at);
7329
7343
  const points = tsData?.timeseries ?? [];
7330
7344
  const nowMin = Math.floor(nowMs / 6e4);
7331
7345
  const minuteCounts = new Array(60).fill(0);
@@ -7382,6 +7396,11 @@ function LivePanel({ active: active2 }) {
7382
7396
  isMe: false
7383
7397
  }))
7384
7398
  ];
7399
+ const localSessIds = new Set(localSess.keys());
7400
+ if (ring?.session) localSessIds.add(ring.session);
7401
+ const isLoc = (e) => e.source === "local" || !!e.session && localSessIds.has(e.session);
7402
+ const filtered = filter === "all" ? mergedAll : mergedAll.filter((e) => filter === "local" ? isLoc(e) : !isLoc(e));
7403
+ const visibleDesc = filtered.slice(-STREAM_KEEP).reverse();
7385
7404
  const chartH = rows >= 36 ? 6 : 4;
7386
7405
  const colH = rows >= 32 ? 7 : 5;
7387
7406
  const streamRows = Math.max(4, rows - 5 - (1 + chartH) - colH);
@@ -7443,14 +7462,15 @@ function LivePanel({ active: active2 }) {
7443
7462
  const dump = visibleDesc.map(
7444
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}`
7445
7464
  ).join("\n");
7446
- let via = "file";
7447
7465
  try {
7448
7466
  mkdirSync3(dirname(DUMP), { recursive: true });
7449
7467
  writeFileSync3(DUMP, dump + "\n");
7450
7468
  } catch {
7451
7469
  }
7452
- via = copyToClipboard(dump);
7453
- setToast({ msg: `copied ${visibleDesc.length} lines via ${via} + ${DUMP}`, until: Date.now() + 5e3 });
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
+ );
7454
7474
  }
7455
7475
  },
7456
7476
  { isActive: active2 }
@@ -7526,7 +7546,7 @@ function LivePanel({ active: active2 }) {
7526
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 }),
7527
7547
  /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: tlRows, overflow: "hidden", children: [
7528
7548
  tl.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: detailCloud.length === 0 ? spin + " loading history\u2026" : "no entries" }) : null,
7529
- tl.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e }, e.id))
7549
+ tl.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e) }, e.id))
7530
7550
  ] }),
7531
7551
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7532
7552
  /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " SESSION " }),
@@ -7645,7 +7665,7 @@ function LivePanel({ active: active2 }) {
7645
7665
  spin,
7646
7666
  " awaiting traffic\u2026"
7647
7667
  ] }) : null,
7648
- windowed.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e }, e.id))
7668
+ windowed.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e) }, e.id))
7649
7669
  ] }),
7650
7670
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7651
7671
  /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " LIVE " }),
package/dist/tui/index.js CHANGED
@@ -220,17 +220,27 @@ async function request(method, path, opts = {}) {
220
220
  headers["Content-Type"] = "application/json";
221
221
  bodyInit = JSON.stringify(opts.body);
222
222
  }
223
+ const attempt = () => fetch(url, {
224
+ method,
225
+ headers,
226
+ body: bodyInit,
227
+ signal: AbortSignal.timeout(opts.timeoutMs ?? 15e3)
228
+ });
223
229
  let res;
224
230
  try {
225
- res = await fetch(url, {
226
- method,
227
- headers,
228
- body: bodyInit,
229
- signal: AbortSignal.timeout(opts.timeoutMs ?? 15e3)
230
- });
231
- } catch (err) {
232
- const msg = err instanceof Error ? err.message : String(err);
233
- throw new ApiError(0, "NETWORK_ERROR", `Cannot reach SolonGate API: ${msg}`);
231
+ res = await attempt();
232
+ } catch (firstErr) {
233
+ if (method !== "GET") {
234
+ const msg = firstErr instanceof Error ? firstErr.message : String(firstErr);
235
+ throw new ApiError(0, "NETWORK_ERROR", `Cannot reach SolonGate API: ${msg}`);
236
+ }
237
+ await new Promise((r) => setTimeout(r, 800));
238
+ try {
239
+ res = await attempt();
240
+ } catch (err) {
241
+ const msg = err instanceof Error ? err.message : String(err);
242
+ throw new ApiError(0, "NETWORK_ERROR", `Cannot reach SolonGate API: ${msg}`);
243
+ }
234
244
  }
235
245
  const text = await res.text().catch(() => "");
236
246
  let json = void 0;
@@ -480,7 +490,7 @@ function copyToClipboard(text) {
480
490
  }
481
491
  }
482
492
  process.stdout.write(`\x1B]52;c;${Buffer.from(text, "utf-8").toString("base64")}\x07`);
483
- return "osc52";
493
+ return null;
484
494
  }
485
495
  function cloudItem(e) {
486
496
  return {
@@ -546,14 +556,14 @@ function PaneTitle({ label, extra, width }) {
546
556
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: tail + "\u2500".repeat(Math.max(0, width - used)) })
547
557
  ] });
548
558
  }
549
- function StreamLine({ e }) {
559
+ function StreamLine({ e, loc }) {
550
560
  return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
551
561
  /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
552
562
  "[",
553
563
  hhmmss(e.at),
554
564
  " "
555
565
  ] }),
556
- /* @__PURE__ */ jsx2(Text2, { color: e.source === "local" ? theme.ok : "#4f6db8", children: e.source === "local" ? "LOC" : "CLD" }),
566
+ /* @__PURE__ */ jsx2(Text2, { color: loc ? theme.ok : "#4f6db8", children: loc ? "LOC" : "CLD" }),
557
567
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "] " }),
558
568
  /* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), bold: e.decision !== "ALLOW", children: e.decision.padEnd(6) }),
559
569
  /* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate(e.tool, 12).padEnd(13) }),
@@ -749,9 +759,13 @@ function LivePanel({ active: active2 }) {
749
759
  const ins = insights.data ?? {};
750
760
  const spin = SPIN[tick % SPIN.length];
751
761
  const nowMs = Date.now();
752
- const mergedAll = [...cloudBuf, ...localBuf].sort((a, b) => a.at - b.at);
753
- const filtered = filter === "all" ? mergedAll : mergedAll.filter((e) => e.source === filter);
754
- const visibleDesc = filtered.slice(-STREAM_KEEP).reverse();
762
+ const localKeys = /* @__PURE__ */ new Set();
763
+ for (const e of localBuf) {
764
+ const m = Math.round(e.at / 6e4);
765
+ for (const d of [-1, 0, 1]) localKeys.add(`${e.tool}|${e.decision}|${m + d}`);
766
+ }
767
+ const cloudDeduped = cloudBuf.filter((e) => !localKeys.has(`${e.tool}|${e.decision}|${Math.round(e.at / 6e4)}`));
768
+ const mergedAll = [...cloudDeduped, ...localBuf].sort((a, b) => a.at - b.at);
755
769
  const points = tsData?.timeseries ?? [];
756
770
  const nowMin = Math.floor(nowMs / 6e4);
757
771
  const minuteCounts = new Array(60).fill(0);
@@ -808,6 +822,11 @@ function LivePanel({ active: active2 }) {
808
822
  isMe: false
809
823
  }))
810
824
  ];
825
+ const localSessIds = new Set(localSess.keys());
826
+ if (ring?.session) localSessIds.add(ring.session);
827
+ const isLoc = (e) => e.source === "local" || !!e.session && localSessIds.has(e.session);
828
+ const filtered = filter === "all" ? mergedAll : mergedAll.filter((e) => filter === "local" ? isLoc(e) : !isLoc(e));
829
+ const visibleDesc = filtered.slice(-STREAM_KEEP).reverse();
811
830
  const chartH = rows >= 36 ? 6 : 4;
812
831
  const colH = rows >= 32 ? 7 : 5;
813
832
  const streamRows = Math.max(4, rows - 5 - (1 + chartH) - colH);
@@ -869,14 +888,15 @@ function LivePanel({ active: active2 }) {
869
888
  const dump = visibleDesc.map(
870
889
  (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}`
871
890
  ).join("\n");
872
- let via = "file";
873
891
  try {
874
892
  mkdirSync(dirname(DUMP), { recursive: true });
875
893
  writeFileSync(DUMP, dump + "\n");
876
894
  } catch {
877
895
  }
878
- via = copyToClipboard(dump);
879
- setToast({ msg: `copied ${visibleDesc.length} lines via ${via} + ${DUMP}`, until: Date.now() + 5e3 });
896
+ const via = copyToClipboard(dump);
897
+ setToast(
898
+ 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 }
899
+ );
880
900
  }
881
901
  },
882
902
  { isActive: active2 }
@@ -952,7 +972,7 @@ function LivePanel({ active: active2 }) {
952
972
  /* @__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 }),
953
973
  /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: tlRows, overflow: "hidden", children: [
954
974
  tl.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: detailCloud.length === 0 ? spin + " loading history\u2026" : "no entries" }) : null,
955
- tl.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e }, e.id))
975
+ tl.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e) }, e.id))
956
976
  ] }),
957
977
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
958
978
  /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " SESSION " }),
@@ -1071,7 +1091,7 @@ function LivePanel({ active: active2 }) {
1071
1091
  spin,
1072
1092
  " awaiting traffic\u2026"
1073
1093
  ] }) : null,
1074
- windowed.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e }, e.id))
1094
+ windowed.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e) }, e.id))
1075
1095
  ] }),
1076
1096
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
1077
1097
  /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " LIVE " }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.70.0",
3
+ "version": "0.71.0",
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": {