@solongate/proxy 0.81.88 → 0.81.90

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
@@ -8840,6 +8840,10 @@ function PoliciesPanel({ focused }) {
8840
8840
  setRi(0);
8841
8841
  }
8842
8842
  }, [detail.data]);
8843
+ usePoll(() => {
8844
+ list6.reloadQuiet();
8845
+ activeQ.reloadQuiet();
8846
+ }, 6e3);
8843
8847
  const mutate = (nextRules, nextMode = mode) => {
8844
8848
  setRules(nextRules);
8845
8849
  setMode(nextMode);
@@ -8876,6 +8880,13 @@ function PoliciesPanel({ focused }) {
8876
8880
  };
8877
8881
  useInput2(
8878
8882
  (input, key) => {
8883
+ if (input === "r" && !editing) {
8884
+ list6.reload();
8885
+ activeQ.reload();
8886
+ detail.reload();
8887
+ setStatus("refreshed");
8888
+ return;
8889
+ }
8879
8890
  if (view === "list") {
8880
8891
  if (key.upArrow) setPi((n) => Math.max(0, n - 1));
8881
8892
  else if (key.downArrow) setPi((n) => Math.min(policies.length - 1, n + 1));
@@ -8966,7 +8977,7 @@ function PoliciesPanel({ focused }) {
8966
8977
  /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: activeBy ? ` (${activeBy})` : "" })
8967
8978
  ] }) : /* @__PURE__ */ jsx3(Text3, { color: theme.bad, bold: true, children: "\u25CB NONE \u2014 nothing enforced, grants refused" })
8968
8979
  ] }),
8969
- /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: focused ? "\u2191\u2193 select \xB7 enter open \xB7 a activate \xB7 x deactivate (none)" : "press \u2192 to browse" }),
8980
+ /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: focused ? "\u2191\u2193 select \xB7 enter open \xB7 a activate \xB7 x deactivate \xB7 r refresh" : "press \u2192 to browse" }),
8970
8981
  /* @__PURE__ */ jsx3(Box3, { marginTop: 1, flexDirection: "column", children: policies.map((p, i) => /* @__PURE__ */ jsxs3(Text3, { color: i === pi ? theme.accentBright : void 0, bold: i === pi, children: [
8971
8982
  (i === pi ? "\u25B8 " : " ") + truncate2(p.name, 26).padEnd(27),
8972
8983
  /* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
@@ -9097,8 +9108,13 @@ function RateLimitPanel({ focused }) {
9097
9108
  const [fi, setFi] = useState4(0);
9098
9109
  const [status, setStatus] = useState4(null);
9099
9110
  useEffect4(() => {
9100
- if (layersQ.data && !draft) setDraft({ ...layersQ.data.layers.rateLimit });
9101
- }, [layersQ.data, draft]);
9111
+ if (layersQ.data && !dirty) setDraft({ ...layersQ.data.layers.rateLimit });
9112
+ }, [layersQ.data, dirty]);
9113
+ usePoll(() => {
9114
+ if (!dirty) layersQ.reloadQuiet();
9115
+ historyQ.reloadQuiet();
9116
+ insightsQ.reloadQuiet();
9117
+ }, 6e3);
9102
9118
  const adjust = (dir, step) => {
9103
9119
  if (!draft) return;
9104
9120
  const field = FIELDS2[fi];
@@ -9129,25 +9145,31 @@ function RateLimitPanel({ focused }) {
9129
9145
  else if (key.leftArrow) adjust(-1, step);
9130
9146
  else if (key.rightArrow) adjust(1, step);
9131
9147
  else if (input === "s") void save();
9148
+ else if (input === "r") {
9149
+ layersQ.reload();
9150
+ historyQ.reload();
9151
+ insightsQ.reload();
9152
+ setStatus("refreshed");
9153
+ }
9132
9154
  },
9133
9155
  { isActive: focused }
9134
9156
  );
9135
9157
  const history = (historyQ.data?.history ?? []).slice().sort((a, b) => b.ts - a.ts);
9136
9158
  const lastChange = history[0];
9137
9159
  const peaks = insightsQ.data?.["peaks"] ?? { minute: 0, hour: 0, day: 0 };
9138
- const anomalies2 = (insightsQ.data?.["anomalies"] ?? []).slice().sort((a, b) => new Date(b.minute).getTime() - new Date(a.minute).getTime());
9160
+ const anomalies2 = (insightsQ.data?.["anomalies"] ?? []).slice().sort((a, b) => b.count - a.count || new Date(b.minute).getTime() - new Date(a.minute).getTime());
9139
9161
  return /* @__PURE__ */ jsx4(DataView, { loading: layersQ.loading && !draft, error: layersQ.error, children: draft ? (() => {
9140
9162
  const lim = draft.perMinute;
9141
9163
  const now = insightsQ.data ? insightsQ.data["activity"]?.minute?.slice(-1)[0]?.count ?? 0 : 0;
9142
9164
  const loadPct = lim > 0 ? Math.min(100, Math.round(now / lim * 100)) : 0;
9143
- const peakFlag = (peak) => lim > 0 ? peak > lim ? ` OVER ${lim}` : peak === lim ? ` at ${lim}` : ` / ${lim}` : "";
9165
+ const last24h = (insightsQ.data?.["activity"]?.hour ?? []).reduce((s, b) => s + b.count, 0);
9144
9166
  const hasLoad = lim > 0;
9145
- const fixed = 1 + 4 + (hasLoad ? 1 : 0) + 1 + 1 + 1 + 1 + (dirty || status ? 1 : 0) + 1;
9167
+ const fixed = 1 + 4 + (hasLoad ? 1 : 0) + 5 + 1 + 1 + 1 + 1 + (dirty || status ? 1 : 0) + 1;
9146
9168
  const burstBudget = Math.max(1, rows - fixed);
9147
9169
  const shown = anomalies2.slice(0, burstBudget);
9148
9170
  const bw = Math.max(10, cols - 30);
9149
9171
  return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", height: rows, overflow: "hidden", children: [
9150
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 field \xB7 \u2190\u2192 \xB11 \xB7 shift+\u2190\u2192 \xB110 \xB7 s save" : "press \u2192 to edit" }),
9172
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 field \xB7 \u2190\u2192 \xB11 \xB7 shift+\u2190\u2192 \xB110 \xB7 s save \xB7 r refresh" : "press \u2192 to edit" }),
9151
9173
  /* @__PURE__ */ jsxs4(FieldRow, { label: "Mode", active: focused && fi === 0, children: [
9152
9174
  /* @__PURE__ */ jsx4(Text4, { color: modeColor(draft.mode), bold: true, children: draft.mode }),
9153
9175
  /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: draft.mode === "off" ? " no limit" : draft.mode === "detect" ? " flag bursts, never block" : " DENY calls over the limit" })
@@ -9156,35 +9178,40 @@ function RateLimitPanel({ focused }) {
9156
9178
  /* @__PURE__ */ jsx4(FieldRow, { label: "Per hour", active: focused && fi === 2, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perHour || "off" }) }),
9157
9179
  /* @__PURE__ */ jsx4(FieldRow, { label: "Per day", active: focused && fi === 3, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perDay || "off" }) }),
9158
9180
  hasLoad ? /* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
9159
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "load".padEnd(13) }),
9160
- /* @__PURE__ */ jsx4(Text4, { color: loadPct >= 100 ? theme.bad : loadPct >= 80 ? theme.warn : theme.ok, children: "\u2588".repeat(Math.min(20, Math.round(now / lim * 20))) }),
9161
- /* @__PURE__ */ jsx4(Text4, { color: "#233457", children: "\u2591".repeat(Math.max(0, 20 - Math.round(now / lim * 20))) }),
9181
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "load now".padEnd(13) }),
9182
+ /* @__PURE__ */ jsx4(Text4, { color: loadPct >= 100 ? theme.bad : loadPct >= 80 ? theme.warn : theme.ok, children: "\u2588".repeat(Math.min(18, Math.round(now / lim * 18))) }),
9183
+ /* @__PURE__ */ jsx4(Text4, { color: "#233457", children: "\u2591".repeat(Math.max(0, 18 - Math.round(now / lim * 18))) }),
9162
9184
  /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: ` ${now}/${lim} this minute (${loadPct}%)` })
9163
9185
  ] }) : null,
9164
- /* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
9165
- /* @__PURE__ */ jsx4(Text4, { color: theme.accentBright, bold: true, children: "Busiest 7d " }),
9166
- /* @__PURE__ */ jsx4(Text4, { color: peaks.minute > lim && lim > 0 ? theme.bad : theme.dim, children: `min ${peaks.minute}${peakFlag(peaks.minute)}` }),
9167
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: ` \xB7 hr ${peaks.hour} \xB7 day ${peaks.day}` })
9168
- ] }),
9169
9186
  /* @__PURE__ */ jsxs4(Text4, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
9170
- `Recent bursts 7d${anomalies2.length ? ` (${anomalies2.length})` : ""}`,
9171
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " over the per-minute limit \xB7 Bypassed = detect (not blocked)" })
9187
+ "Busiest 7d",
9188
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " most calls in one window vs your limit" })
9189
+ ] }),
9190
+ /* @__PURE__ */ jsx4(BusiestRow, { label: "minute", peak: peaks.minute, limit: draft.perMinute }),
9191
+ /* @__PURE__ */ jsx4(BusiestRow, { label: "hour", peak: peaks.hour, limit: draft.perHour }),
9192
+ /* @__PURE__ */ jsx4(BusiestRow, { label: "day", peak: peaks.day, limit: draft.perDay }),
9193
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, wrap: "truncate", children: `activity`.padEnd(11) + ` last 24h \xB7 ${last24h} calls \xB7 busiest hour ${peaks.hour}/hr` }),
9194
+ /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
9195
+ /* @__PURE__ */ jsxs4(Text4, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
9196
+ `Recent bursts 7d${anomalies2.length ? ` (${anomalies2.length})` : ""}`,
9197
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " over the per-minute limit \xB7 Bypassed = detect (not blocked)" })
9198
+ ] }),
9199
+ anomalies2.length === 0 ? /* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
9200
+ " ",
9201
+ insightsQ.loading ? "loading\u2026" : "no bursts in the last 7 days"
9202
+ ] }) : /* @__PURE__ */ jsx4(
9203
+ Table,
9204
+ {
9205
+ columns: [{ header: "WHEN", width: 13 }, { header: "RESULT", width: 9 }, { header: "CALLS", width: 7 }, { header: "AGENT", width: Math.max(8, bw - 29) }],
9206
+ rows: shown.map((a) => [
9207
+ { value: when(new Date(a.minute).getTime()), dim: true },
9208
+ { value: a.blocked ? "Blocked" : "Bypassed", color: a.blocked ? theme.bad : theme.warn },
9209
+ { value: `${a.count}/${a.limit}`, color: theme.warn },
9210
+ { value: truncate2(a.agent, Math.max(8, bw - 29)), dim: true }
9211
+ ])
9212
+ }
9213
+ )
9172
9214
  ] }),
9173
- anomalies2.length === 0 ? /* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
9174
- " ",
9175
- insightsQ.loading ? "loading\u2026" : "no bursts in the last 7 days"
9176
- ] }) : /* @__PURE__ */ jsx4(
9177
- Table,
9178
- {
9179
- columns: [{ header: "WHEN", width: 13 }, { header: "RESULT", width: 9 }, { header: "CALLS", width: 7 }, { header: "AGENT", width: Math.max(8, bw - 29) }],
9180
- rows: shown.map((a) => [
9181
- { value: when(new Date(a.minute).getTime()), dim: true },
9182
- { value: a.blocked ? "Blocked" : "Bypassed", color: a.blocked ? theme.bad : theme.warn },
9183
- { value: `${a.count}/${a.limit}`, color: theme.warn },
9184
- { value: truncate2(a.agent, Math.max(8, bw - 29)), dim: true }
9185
- ])
9186
- }
9187
- ),
9188
9215
  /* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
9189
9216
  /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "limit now " }),
9190
9217
  /* @__PURE__ */ jsx4(Text4, { children: lim > 0 ? `${lim}/min` : "no per-minute limit" }),
@@ -9195,6 +9222,15 @@ function RateLimitPanel({ focused }) {
9195
9222
  ] });
9196
9223
  })() : null });
9197
9224
  }
9225
+ function BusiestRow({ label, peak, limit }) {
9226
+ const ctx = limit > 0 ? peak > limit ? `over ${limit}` : peak === limit ? `at limit ${limit}` : `limit ${limit}` : "no limit set";
9227
+ const hot = limit > 0 && peak > limit;
9228
+ return /* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
9229
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: (" " + label).padEnd(11) }),
9230
+ /* @__PURE__ */ jsx4(Text4, { color: hot ? theme.bad : void 0, bold: hot, children: String(peak).padStart(4) + " calls" }),
9231
+ /* @__PURE__ */ jsx4(Text4, { color: hot ? theme.bad : theme.dim, children: " \xB7 " + ctx })
9232
+ ] });
9233
+ }
9198
9234
  function FieldRow({ label, active: active2, children }) {
9199
9235
  return /* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
9200
9236
  /* @__PURE__ */ jsx4(Text4, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + label.padEnd(11) }),
@@ -9237,13 +9273,16 @@ function DlpPanel({ focused }) {
9237
9273
  const [newName, setNewName] = useState5("");
9238
9274
  const [input, setInput] = useState5("");
9239
9275
  useEffect5(() => {
9240
- if (q.data && !dlp) {
9276
+ if (q.data && !dirty) {
9241
9277
  setDlp({ ...q.data.layers.dlp, patterns: [...q.data.layers.dlp.patterns], custom: [...q.data.layers.dlp.custom] });
9242
9278
  setAvailable(q.data.availablePatterns);
9243
9279
  setGhostOn(q.data.layers.ghost.mode === "on");
9244
9280
  setGhostPats([...q.data.layers.ghost.patterns]);
9245
9281
  }
9246
- }, [q.data, dlp]);
9282
+ }, [q.data, dirty]);
9283
+ usePoll(() => {
9284
+ if (!dirty && !adding) q.reloadQuiet();
9285
+ }, 6e3);
9247
9286
  const mutate = (next) => {
9248
9287
  setDlp(next);
9249
9288
  setDirty(true);
package/dist/tui/index.js CHANGED
@@ -1958,6 +1958,10 @@ function PoliciesPanel({ focused }) {
1958
1958
  setRi(0);
1959
1959
  }
1960
1960
  }, [detail.data]);
1961
+ usePoll(() => {
1962
+ list5.reloadQuiet();
1963
+ activeQ.reloadQuiet();
1964
+ }, 6e3);
1961
1965
  const mutate = (nextRules, nextMode = mode) => {
1962
1966
  setRules(nextRules);
1963
1967
  setMode(nextMode);
@@ -1994,6 +1998,13 @@ function PoliciesPanel({ focused }) {
1994
1998
  };
1995
1999
  useInput2(
1996
2000
  (input, key) => {
2001
+ if (input === "r" && !editing) {
2002
+ list5.reload();
2003
+ activeQ.reload();
2004
+ detail.reload();
2005
+ setStatus("refreshed");
2006
+ return;
2007
+ }
1997
2008
  if (view === "list") {
1998
2009
  if (key.upArrow) setPi((n) => Math.max(0, n - 1));
1999
2010
  else if (key.downArrow) setPi((n) => Math.min(policies.length - 1, n + 1));
@@ -2084,7 +2095,7 @@ function PoliciesPanel({ focused }) {
2084
2095
  /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: activeBy ? ` (${activeBy})` : "" })
2085
2096
  ] }) : /* @__PURE__ */ jsx3(Text3, { color: theme.bad, bold: true, children: "\u25CB NONE \u2014 nothing enforced, grants refused" })
2086
2097
  ] }),
2087
- /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: focused ? "\u2191\u2193 select \xB7 enter open \xB7 a activate \xB7 x deactivate (none)" : "press \u2192 to browse" }),
2098
+ /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: focused ? "\u2191\u2193 select \xB7 enter open \xB7 a activate \xB7 x deactivate \xB7 r refresh" : "press \u2192 to browse" }),
2088
2099
  /* @__PURE__ */ jsx3(Box3, { marginTop: 1, flexDirection: "column", children: policies.map((p, i) => /* @__PURE__ */ jsxs3(Text3, { color: i === pi ? theme.accentBright : void 0, bold: i === pi, children: [
2089
2100
  (i === pi ? "\u25B8 " : " ") + truncate(p.name, 26).padEnd(27),
2090
2101
  /* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
@@ -2190,8 +2201,13 @@ function RateLimitPanel({ focused }) {
2190
2201
  const [fi, setFi] = useState4(0);
2191
2202
  const [status, setStatus] = useState4(null);
2192
2203
  useEffect4(() => {
2193
- if (layersQ.data && !draft) setDraft({ ...layersQ.data.layers.rateLimit });
2194
- }, [layersQ.data, draft]);
2204
+ if (layersQ.data && !dirty) setDraft({ ...layersQ.data.layers.rateLimit });
2205
+ }, [layersQ.data, dirty]);
2206
+ usePoll(() => {
2207
+ if (!dirty) layersQ.reloadQuiet();
2208
+ historyQ.reloadQuiet();
2209
+ insightsQ.reloadQuiet();
2210
+ }, 6e3);
2195
2211
  const adjust = (dir, step) => {
2196
2212
  if (!draft) return;
2197
2213
  const field = FIELDS2[fi];
@@ -2222,25 +2238,31 @@ function RateLimitPanel({ focused }) {
2222
2238
  else if (key.leftArrow) adjust(-1, step);
2223
2239
  else if (key.rightArrow) adjust(1, step);
2224
2240
  else if (input === "s") void save();
2241
+ else if (input === "r") {
2242
+ layersQ.reload();
2243
+ historyQ.reload();
2244
+ insightsQ.reload();
2245
+ setStatus("refreshed");
2246
+ }
2225
2247
  },
2226
2248
  { isActive: focused }
2227
2249
  );
2228
2250
  const history = (historyQ.data?.history ?? []).slice().sort((a, b) => b.ts - a.ts);
2229
2251
  const lastChange = history[0];
2230
2252
  const peaks = insightsQ.data?.["peaks"] ?? { minute: 0, hour: 0, day: 0 };
2231
- const anomalies2 = (insightsQ.data?.["anomalies"] ?? []).slice().sort((a, b) => new Date(b.minute).getTime() - new Date(a.minute).getTime());
2253
+ const anomalies2 = (insightsQ.data?.["anomalies"] ?? []).slice().sort((a, b) => b.count - a.count || new Date(b.minute).getTime() - new Date(a.minute).getTime());
2232
2254
  return /* @__PURE__ */ jsx4(DataView, { loading: layersQ.loading && !draft, error: layersQ.error, children: draft ? (() => {
2233
2255
  const lim = draft.perMinute;
2234
2256
  const now = insightsQ.data ? insightsQ.data["activity"]?.minute?.slice(-1)[0]?.count ?? 0 : 0;
2235
2257
  const loadPct = lim > 0 ? Math.min(100, Math.round(now / lim * 100)) : 0;
2236
- const peakFlag = (peak) => lim > 0 ? peak > lim ? ` OVER ${lim}` : peak === lim ? ` at ${lim}` : ` / ${lim}` : "";
2258
+ const last24h = (insightsQ.data?.["activity"]?.hour ?? []).reduce((s, b) => s + b.count, 0);
2237
2259
  const hasLoad = lim > 0;
2238
- const fixed = 1 + 4 + (hasLoad ? 1 : 0) + 1 + 1 + 1 + 1 + (dirty || status ? 1 : 0) + 1;
2260
+ const fixed = 1 + 4 + (hasLoad ? 1 : 0) + 5 + 1 + 1 + 1 + 1 + (dirty || status ? 1 : 0) + 1;
2239
2261
  const burstBudget = Math.max(1, rows - fixed);
2240
2262
  const shown = anomalies2.slice(0, burstBudget);
2241
2263
  const bw = Math.max(10, cols - 30);
2242
2264
  return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", height: rows, overflow: "hidden", children: [
2243
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 field \xB7 \u2190\u2192 \xB11 \xB7 shift+\u2190\u2192 \xB110 \xB7 s save" : "press \u2192 to edit" }),
2265
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 field \xB7 \u2190\u2192 \xB11 \xB7 shift+\u2190\u2192 \xB110 \xB7 s save \xB7 r refresh" : "press \u2192 to edit" }),
2244
2266
  /* @__PURE__ */ jsxs4(FieldRow, { label: "Mode", active: focused && fi === 0, children: [
2245
2267
  /* @__PURE__ */ jsx4(Text4, { color: modeColor(draft.mode), bold: true, children: draft.mode }),
2246
2268
  /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: draft.mode === "off" ? " no limit" : draft.mode === "detect" ? " flag bursts, never block" : " DENY calls over the limit" })
@@ -2249,35 +2271,40 @@ function RateLimitPanel({ focused }) {
2249
2271
  /* @__PURE__ */ jsx4(FieldRow, { label: "Per hour", active: focused && fi === 2, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perHour || "off" }) }),
2250
2272
  /* @__PURE__ */ jsx4(FieldRow, { label: "Per day", active: focused && fi === 3, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perDay || "off" }) }),
2251
2273
  hasLoad ? /* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
2252
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "load".padEnd(13) }),
2253
- /* @__PURE__ */ jsx4(Text4, { color: loadPct >= 100 ? theme.bad : loadPct >= 80 ? theme.warn : theme.ok, children: "\u2588".repeat(Math.min(20, Math.round(now / lim * 20))) }),
2254
- /* @__PURE__ */ jsx4(Text4, { color: "#233457", children: "\u2591".repeat(Math.max(0, 20 - Math.round(now / lim * 20))) }),
2274
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "load now".padEnd(13) }),
2275
+ /* @__PURE__ */ jsx4(Text4, { color: loadPct >= 100 ? theme.bad : loadPct >= 80 ? theme.warn : theme.ok, children: "\u2588".repeat(Math.min(18, Math.round(now / lim * 18))) }),
2276
+ /* @__PURE__ */ jsx4(Text4, { color: "#233457", children: "\u2591".repeat(Math.max(0, 18 - Math.round(now / lim * 18))) }),
2255
2277
  /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: ` ${now}/${lim} this minute (${loadPct}%)` })
2256
2278
  ] }) : null,
2257
- /* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
2258
- /* @__PURE__ */ jsx4(Text4, { color: theme.accentBright, bold: true, children: "Busiest 7d " }),
2259
- /* @__PURE__ */ jsx4(Text4, { color: peaks.minute > lim && lim > 0 ? theme.bad : theme.dim, children: `min ${peaks.minute}${peakFlag(peaks.minute)}` }),
2260
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: ` \xB7 hr ${peaks.hour} \xB7 day ${peaks.day}` })
2261
- ] }),
2262
2279
  /* @__PURE__ */ jsxs4(Text4, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
2263
- `Recent bursts 7d${anomalies2.length ? ` (${anomalies2.length})` : ""}`,
2264
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " over the per-minute limit \xB7 Bypassed = detect (not blocked)" })
2280
+ "Busiest 7d",
2281
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " most calls in one window vs your limit" })
2282
+ ] }),
2283
+ /* @__PURE__ */ jsx4(BusiestRow, { label: "minute", peak: peaks.minute, limit: draft.perMinute }),
2284
+ /* @__PURE__ */ jsx4(BusiestRow, { label: "hour", peak: peaks.hour, limit: draft.perHour }),
2285
+ /* @__PURE__ */ jsx4(BusiestRow, { label: "day", peak: peaks.day, limit: draft.perDay }),
2286
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, wrap: "truncate", children: `activity`.padEnd(11) + ` last 24h \xB7 ${last24h} calls \xB7 busiest hour ${peaks.hour}/hr` }),
2287
+ /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
2288
+ /* @__PURE__ */ jsxs4(Text4, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
2289
+ `Recent bursts 7d${anomalies2.length ? ` (${anomalies2.length})` : ""}`,
2290
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " over the per-minute limit \xB7 Bypassed = detect (not blocked)" })
2291
+ ] }),
2292
+ anomalies2.length === 0 ? /* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
2293
+ " ",
2294
+ insightsQ.loading ? "loading\u2026" : "no bursts in the last 7 days"
2295
+ ] }) : /* @__PURE__ */ jsx4(
2296
+ Table,
2297
+ {
2298
+ columns: [{ header: "WHEN", width: 13 }, { header: "RESULT", width: 9 }, { header: "CALLS", width: 7 }, { header: "AGENT", width: Math.max(8, bw - 29) }],
2299
+ rows: shown.map((a) => [
2300
+ { value: when(new Date(a.minute).getTime()), dim: true },
2301
+ { value: a.blocked ? "Blocked" : "Bypassed", color: a.blocked ? theme.bad : theme.warn },
2302
+ { value: `${a.count}/${a.limit}`, color: theme.warn },
2303
+ { value: truncate(a.agent, Math.max(8, bw - 29)), dim: true }
2304
+ ])
2305
+ }
2306
+ )
2265
2307
  ] }),
2266
- anomalies2.length === 0 ? /* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
2267
- " ",
2268
- insightsQ.loading ? "loading\u2026" : "no bursts in the last 7 days"
2269
- ] }) : /* @__PURE__ */ jsx4(
2270
- Table,
2271
- {
2272
- columns: [{ header: "WHEN", width: 13 }, { header: "RESULT", width: 9 }, { header: "CALLS", width: 7 }, { header: "AGENT", width: Math.max(8, bw - 29) }],
2273
- rows: shown.map((a) => [
2274
- { value: when(new Date(a.minute).getTime()), dim: true },
2275
- { value: a.blocked ? "Blocked" : "Bypassed", color: a.blocked ? theme.bad : theme.warn },
2276
- { value: `${a.count}/${a.limit}`, color: theme.warn },
2277
- { value: truncate(a.agent, Math.max(8, bw - 29)), dim: true }
2278
- ])
2279
- }
2280
- ),
2281
2308
  /* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
2282
2309
  /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "limit now " }),
2283
2310
  /* @__PURE__ */ jsx4(Text4, { children: lim > 0 ? `${lim}/min` : "no per-minute limit" }),
@@ -2288,6 +2315,15 @@ function RateLimitPanel({ focused }) {
2288
2315
  ] });
2289
2316
  })() : null });
2290
2317
  }
2318
+ function BusiestRow({ label, peak, limit }) {
2319
+ const ctx = limit > 0 ? peak > limit ? `over ${limit}` : peak === limit ? `at limit ${limit}` : `limit ${limit}` : "no limit set";
2320
+ const hot = limit > 0 && peak > limit;
2321
+ return /* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
2322
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: (" " + label).padEnd(11) }),
2323
+ /* @__PURE__ */ jsx4(Text4, { color: hot ? theme.bad : void 0, bold: hot, children: String(peak).padStart(4) + " calls" }),
2324
+ /* @__PURE__ */ jsx4(Text4, { color: hot ? theme.bad : theme.dim, children: " \xB7 " + ctx })
2325
+ ] });
2326
+ }
2291
2327
  function FieldRow({ label, active: active2, children }) {
2292
2328
  return /* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
2293
2329
  /* @__PURE__ */ jsx4(Text4, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + label.padEnd(11) }),
@@ -2315,13 +2351,16 @@ function DlpPanel({ focused }) {
2315
2351
  const [newName, setNewName] = useState5("");
2316
2352
  const [input, setInput] = useState5("");
2317
2353
  useEffect5(() => {
2318
- if (q.data && !dlp) {
2354
+ if (q.data && !dirty) {
2319
2355
  setDlp({ ...q.data.layers.dlp, patterns: [...q.data.layers.dlp.patterns], custom: [...q.data.layers.dlp.custom] });
2320
2356
  setAvailable(q.data.availablePatterns);
2321
2357
  setGhostOn(q.data.layers.ghost.mode === "on");
2322
2358
  setGhostPats([...q.data.layers.ghost.patterns]);
2323
2359
  }
2324
- }, [q.data, dlp]);
2360
+ }, [q.data, dirty]);
2361
+ usePoll(() => {
2362
+ if (!dirty && !adding) q.reloadQuiet();
2363
+ }, 6e3);
2325
2364
  const mutate = (next) => {
2326
2365
  setDlp(next);
2327
2366
  setDirty(true);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.81.88",
3
+ "version": "0.81.90",
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": {