@solongate/proxy 0.66.0 → 0.66.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7079,20 +7079,33 @@ function LivePanel({ active: active2 }) {
7079
7079
  const [buffer, setBuffer] = useState2([]);
7080
7080
  const [log6, setLog] = useState2([]);
7081
7081
  const seenRef = useRef2(/* @__PURE__ */ new Set());
7082
+ const pausedUntil = useRef2(0);
7082
7083
  const pushLog = useCallback2((msg, level = "ok") => {
7083
7084
  setLog((prev) => [...prev.slice(-59), { ts: Date.now(), msg, level }]);
7084
7085
  }, []);
7085
- const doPoll = useCallback2(async () => {
7086
+ const onApiError = useCallback2(
7087
+ (e) => {
7088
+ const is429 = e instanceof ApiError && e.status === 429;
7089
+ if (is429 && Date.now() >= pausedUntil.current) {
7090
+ pausedUntil.current = Date.now() + 3e4;
7091
+ pushLog("rate limited by api \xB7 backing off 30s", "warn");
7092
+ } else if (!is429) {
7093
+ pushLog(`api error \xB7 ${truncate2(e instanceof Error ? e.message : String(e), 40)}`, "bad");
7094
+ }
7095
+ },
7096
+ [pushLog]
7097
+ );
7098
+ const paused = () => Date.now() < pausedUntil.current;
7099
+ const pollFeed = useCallback2(async () => {
7100
+ if (paused()) return;
7086
7101
  const t0 = Date.now();
7087
7102
  try {
7088
- const [sd, fd, td] = await Promise.all([api.stats.get(), api.audit.list({ limit: 50 }), api.stats.timeseries({ period: "24h" })]);
7103
+ const fd = await api.audit.list({ limit: 50 });
7089
7104
  const ms = Date.now() - t0;
7090
- setS(sd);
7091
- setTsData(td);
7092
- setLat((l) => [...l.slice(-47), ms]);
7105
+ setLat((l) => [...l, ms].slice(-240));
7093
7106
  const fresh = fd.entries.filter((e) => !seenRef.current.has(e.id));
7107
+ const firstLoad = seenRef.current.size === 0 && fresh.length > 1;
7094
7108
  for (const e of fresh) seenRef.current.add(e.id);
7095
- const firstLoad = seenRef.current.size === fresh.length && fresh.length > 1;
7096
7109
  if (fresh.length) {
7097
7110
  setBuffer((prev) => [...prev, ...fresh.sort((a, b) => Date.parse(a.created_at) - Date.parse(b.created_at))].slice(-400));
7098
7111
  }
@@ -7105,19 +7118,47 @@ function LivePanel({ active: active2 }) {
7105
7118
  denies || dlp ? "bad" : fresh.length ? "warn" : "ok"
7106
7119
  );
7107
7120
  } catch (e) {
7108
- pushLog(`api error \xB7 ${truncate2(e instanceof Error ? e.message : String(e), 40)}`, "bad");
7121
+ onApiError(e);
7122
+ }
7123
+ }, [pushLog, onApiError]);
7124
+ const pollMedium = useCallback2(async () => {
7125
+ if (paused()) return;
7126
+ try {
7127
+ setS(await api.stats.get());
7128
+ } catch (e) {
7129
+ onApiError(e);
7130
+ }
7131
+ }, [onApiError]);
7132
+ const pollSlow = useCallback2(async () => {
7133
+ if (paused()) return;
7134
+ try {
7135
+ setTsData(await api.stats.timeseries({ period: "24h" }));
7136
+ } catch (e) {
7137
+ onApiError(e);
7109
7138
  }
7110
- }, [pushLog]);
7139
+ }, [onApiError]);
7111
7140
  useEffect2(() => {
7112
7141
  if (!active2) return;
7113
- void doPoll();
7114
- const t = setInterval(() => void doPoll(), 2e3);
7115
- return () => clearInterval(t);
7116
- }, [active2, doPoll]);
7142
+ void pollFeed();
7143
+ void pollMedium();
7144
+ void pollSlow();
7145
+ const t1 = setInterval(() => void pollFeed(), 4e3);
7146
+ const t2 = setInterval(() => void pollMedium(), 1e4);
7147
+ const t3 = setInterval(() => void pollSlow(), 3e4);
7148
+ return () => {
7149
+ clearInterval(t1);
7150
+ clearInterval(t2);
7151
+ clearInterval(t3);
7152
+ };
7153
+ }, [active2, pollFeed, pollMedium, pollSlow]);
7117
7154
  const sessions = useLoader(() => api.agents.live({ limit: 30, includeDeactivated: true }));
7118
7155
  const insights = useLoader(() => api.stats.securityInsights(7));
7119
- usePoll(sessions.reload, 5e3, active2);
7120
- usePoll(insights.reload, 1e4, active2);
7156
+ usePoll(() => {
7157
+ if (!paused()) sessions.reload();
7158
+ }, 1e4, active2);
7159
+ usePoll(() => {
7160
+ if (!paused()) insights.reload();
7161
+ }, 3e4, active2);
7121
7162
  useEffect2(() => {
7122
7163
  const c2 = sessions.data?.counts;
7123
7164
  if (c2) pushLog(`sessions ${c2.active}\u25B2 ${c2.idle}\u25CC ${c2.deactivated}\u25CB`);
@@ -7156,6 +7197,8 @@ function LivePanel({ active: active2 }) {
7156
7197
  const lastDeny = denials[denials.length - 1];
7157
7198
  const latNow = lat[lat.length - 1] ?? 0;
7158
7199
  const latAvg = lat.length ? Math.round(lat.reduce((a, b) => a + b, 0) / lat.length) : 0;
7200
+ const latHotAt = Math.max(1500, latAvg * 1.8);
7201
+ const backingOff = Date.now() < pausedUntil.current;
7159
7202
  const sess = (sessions.data?.agents ?? []).slice().sort((a, b) => Date.parse(b.last_seen_at) - Date.parse(a.last_seen_at));
7160
7203
  const sessCounts = sessions.data?.counts;
7161
7204
  const chartH = rows >= 36 ? 6 : 4;
@@ -7171,7 +7214,7 @@ function LivePanel({ active: active2 }) {
7171
7214
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7172
7215
  /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: " \u26E8 SOLONGATE LIVE " }),
7173
7216
  /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` ${spin} up ${fmtUp(Date.now() - startRef.current)} \xB7 ${hhmmss(Date.now())} \xB7 api ${latNow}ms ` }),
7174
- lastDeny ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d1220", color: "#ff6b6b", bold: true, children: ` \u26A0 ${hhmmss(lastDeny.created_at)} ${lastDeny.tool_name} DENIED ` }) : /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#4f6db8", children: " \u2713 clean " })
7217
+ backingOff ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d2a12", color: "#ffb454", bold: true, children: " \u23F8 RATE LIMITED \xB7 backing off " }) : lastDeny ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d1220", color: "#ff6b6b", bold: true, children: ` \u26A0 ${hhmmss(lastDeny.created_at)} ${lastDeny.tool_name} DENIED ` }) : /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#4f6db8", children: " \u2713 clean " })
7175
7218
  ] }),
7176
7219
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7177
7220
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 calls " }),
@@ -7196,8 +7239,8 @@ function LivePanel({ active: active2 }) {
7196
7239
  /* @__PURE__ */ jsx2(ColumnChart, { series: traffic, hot: trafficHot, height: chartH, width: leftW, color: theme.accent })
7197
7240
  ] }),
7198
7241
  /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: rightW, children: [
7199
- /* @__PURE__ */ jsx2(PaneTitle, { label: "API LATENCY", extra: `live \xB7 now ${latNow}ms \xB7 avg ${latAvg}ms`, width: rightW }),
7200
- /* @__PURE__ */ jsx2(ColumnChart, { series: lat, hot: lat.map((v) => v > 800), height: chartH, width: rightW, color: "#82AAF0" })
7242
+ /* @__PURE__ */ jsx2(PaneTitle, { label: "API LATENCY", extra: `live \xB7 now ${latNow}ms \xB7 avg ${latAvg}ms \xB7 red >${Math.round(latHotAt)}ms`, width: rightW }),
7243
+ /* @__PURE__ */ jsx2(ColumnChart, { series: lat, hot: lat.map((v) => v > latHotAt), height: chartH, width: rightW, color: "#82AAF0" })
7201
7244
  ] })
7202
7245
  ] }),
7203
7246
  /* @__PURE__ */ jsxs2(Box2, { children: [
package/dist/tui/index.js CHANGED
@@ -499,20 +499,33 @@ function LivePanel({ active: active2 }) {
499
499
  const [buffer, setBuffer] = useState2([]);
500
500
  const [log, setLog] = useState2([]);
501
501
  const seenRef = useRef2(/* @__PURE__ */ new Set());
502
+ const pausedUntil = useRef2(0);
502
503
  const pushLog = useCallback2((msg, level = "ok") => {
503
504
  setLog((prev) => [...prev.slice(-59), { ts: Date.now(), msg, level }]);
504
505
  }, []);
505
- const doPoll = useCallback2(async () => {
506
+ const onApiError = useCallback2(
507
+ (e) => {
508
+ const is429 = e instanceof ApiError && e.status === 429;
509
+ if (is429 && Date.now() >= pausedUntil.current) {
510
+ pausedUntil.current = Date.now() + 3e4;
511
+ pushLog("rate limited by api \xB7 backing off 30s", "warn");
512
+ } else if (!is429) {
513
+ pushLog(`api error \xB7 ${truncate(e instanceof Error ? e.message : String(e), 40)}`, "bad");
514
+ }
515
+ },
516
+ [pushLog]
517
+ );
518
+ const paused = () => Date.now() < pausedUntil.current;
519
+ const pollFeed = useCallback2(async () => {
520
+ if (paused()) return;
506
521
  const t0 = Date.now();
507
522
  try {
508
- const [sd, fd, td] = await Promise.all([api.stats.get(), api.audit.list({ limit: 50 }), api.stats.timeseries({ period: "24h" })]);
523
+ const fd = await api.audit.list({ limit: 50 });
509
524
  const ms = Date.now() - t0;
510
- setS(sd);
511
- setTsData(td);
512
- setLat((l) => [...l.slice(-47), ms]);
525
+ setLat((l) => [...l, ms].slice(-240));
513
526
  const fresh = fd.entries.filter((e) => !seenRef.current.has(e.id));
527
+ const firstLoad = seenRef.current.size === 0 && fresh.length > 1;
514
528
  for (const e of fresh) seenRef.current.add(e.id);
515
- const firstLoad = seenRef.current.size === fresh.length && fresh.length > 1;
516
529
  if (fresh.length) {
517
530
  setBuffer((prev) => [...prev, ...fresh.sort((a, b) => Date.parse(a.created_at) - Date.parse(b.created_at))].slice(-400));
518
531
  }
@@ -525,19 +538,47 @@ function LivePanel({ active: active2 }) {
525
538
  denies || dlp ? "bad" : fresh.length ? "warn" : "ok"
526
539
  );
527
540
  } catch (e) {
528
- pushLog(`api error \xB7 ${truncate(e instanceof Error ? e.message : String(e), 40)}`, "bad");
541
+ onApiError(e);
542
+ }
543
+ }, [pushLog, onApiError]);
544
+ const pollMedium = useCallback2(async () => {
545
+ if (paused()) return;
546
+ try {
547
+ setS(await api.stats.get());
548
+ } catch (e) {
549
+ onApiError(e);
550
+ }
551
+ }, [onApiError]);
552
+ const pollSlow = useCallback2(async () => {
553
+ if (paused()) return;
554
+ try {
555
+ setTsData(await api.stats.timeseries({ period: "24h" }));
556
+ } catch (e) {
557
+ onApiError(e);
529
558
  }
530
- }, [pushLog]);
559
+ }, [onApiError]);
531
560
  useEffect2(() => {
532
561
  if (!active2) return;
533
- void doPoll();
534
- const t = setInterval(() => void doPoll(), 2e3);
535
- return () => clearInterval(t);
536
- }, [active2, doPoll]);
562
+ void pollFeed();
563
+ void pollMedium();
564
+ void pollSlow();
565
+ const t1 = setInterval(() => void pollFeed(), 4e3);
566
+ const t2 = setInterval(() => void pollMedium(), 1e4);
567
+ const t3 = setInterval(() => void pollSlow(), 3e4);
568
+ return () => {
569
+ clearInterval(t1);
570
+ clearInterval(t2);
571
+ clearInterval(t3);
572
+ };
573
+ }, [active2, pollFeed, pollMedium, pollSlow]);
537
574
  const sessions = useLoader(() => api.agents.live({ limit: 30, includeDeactivated: true }));
538
575
  const insights = useLoader(() => api.stats.securityInsights(7));
539
- usePoll(sessions.reload, 5e3, active2);
540
- usePoll(insights.reload, 1e4, active2);
576
+ usePoll(() => {
577
+ if (!paused()) sessions.reload();
578
+ }, 1e4, active2);
579
+ usePoll(() => {
580
+ if (!paused()) insights.reload();
581
+ }, 3e4, active2);
541
582
  useEffect2(() => {
542
583
  const c2 = sessions.data?.counts;
543
584
  if (c2) pushLog(`sessions ${c2.active}\u25B2 ${c2.idle}\u25CC ${c2.deactivated}\u25CB`);
@@ -576,6 +617,8 @@ function LivePanel({ active: active2 }) {
576
617
  const lastDeny = denials[denials.length - 1];
577
618
  const latNow = lat[lat.length - 1] ?? 0;
578
619
  const latAvg = lat.length ? Math.round(lat.reduce((a, b) => a + b, 0) / lat.length) : 0;
620
+ const latHotAt = Math.max(1500, latAvg * 1.8);
621
+ const backingOff = Date.now() < pausedUntil.current;
579
622
  const sess = (sessions.data?.agents ?? []).slice().sort((a, b) => Date.parse(b.last_seen_at) - Date.parse(a.last_seen_at));
580
623
  const sessCounts = sessions.data?.counts;
581
624
  const chartH = rows >= 36 ? 6 : 4;
@@ -591,7 +634,7 @@ function LivePanel({ active: active2 }) {
591
634
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
592
635
  /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: " \u26E8 SOLONGATE LIVE " }),
593
636
  /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` ${spin} up ${fmtUp(Date.now() - startRef.current)} \xB7 ${hhmmss(Date.now())} \xB7 api ${latNow}ms ` }),
594
- lastDeny ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d1220", color: "#ff6b6b", bold: true, children: ` \u26A0 ${hhmmss(lastDeny.created_at)} ${lastDeny.tool_name} DENIED ` }) : /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#4f6db8", children: " \u2713 clean " })
637
+ backingOff ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d2a12", color: "#ffb454", bold: true, children: " \u23F8 RATE LIMITED \xB7 backing off " }) : lastDeny ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d1220", color: "#ff6b6b", bold: true, children: ` \u26A0 ${hhmmss(lastDeny.created_at)} ${lastDeny.tool_name} DENIED ` }) : /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#4f6db8", children: " \u2713 clean " })
595
638
  ] }),
596
639
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
597
640
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 calls " }),
@@ -616,8 +659,8 @@ function LivePanel({ active: active2 }) {
616
659
  /* @__PURE__ */ jsx2(ColumnChart, { series: traffic, hot: trafficHot, height: chartH, width: leftW, color: theme.accent })
617
660
  ] }),
618
661
  /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: rightW, children: [
619
- /* @__PURE__ */ jsx2(PaneTitle, { label: "API LATENCY", extra: `live \xB7 now ${latNow}ms \xB7 avg ${latAvg}ms`, width: rightW }),
620
- /* @__PURE__ */ jsx2(ColumnChart, { series: lat, hot: lat.map((v) => v > 800), height: chartH, width: rightW, color: "#82AAF0" })
662
+ /* @__PURE__ */ jsx2(PaneTitle, { label: "API LATENCY", extra: `live \xB7 now ${latNow}ms \xB7 avg ${latAvg}ms \xB7 red >${Math.round(latHotAt)}ms`, width: rightW }),
663
+ /* @__PURE__ */ jsx2(ColumnChart, { series: lat, hot: lat.map((v) => v > latHotAt), height: chartH, width: rightW, color: "#82AAF0" })
621
664
  ] })
622
665
  ] }),
623
666
  /* @__PURE__ */ jsxs2(Box2, { children: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.66.0",
3
+ "version": "0.66.1",
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": {