@solongate/proxy 0.81.87 → 0.81.89

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
@@ -9087,16 +9087,8 @@ var init_Policies = __esm({
9087
9087
  import { Box as Box4, Text as Text4, useInput as useInput3 } from "ink";
9088
9088
  import { useEffect as useEffect4, useState as useState4 } from "react";
9089
9089
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
9090
- function ColoredSpark({ values, limit, width: width2 = 60 }) {
9091
- const v = values.length > width2 ? values.slice(values.length - width2) : values;
9092
- const max = Math.max(1, ...v);
9093
- return /* @__PURE__ */ jsx4(Text4, { children: v.map((n, i) => {
9094
- const ch = BLOCKS[Math.min(BLOCKS.length - 1, Math.round(n / max * (BLOCKS.length - 1)))];
9095
- const over = limit > 0 && n > limit;
9096
- return /* @__PURE__ */ jsx4(Text4, { color: over ? theme.bad : theme.accent, children: ch }, i);
9097
- }) });
9098
- }
9099
9090
  function RateLimitPanel({ focused }) {
9091
+ const { cols, rows } = usePanelSize();
9100
9092
  const layersQ = useLoader(() => api.settings.getSecurityLayers());
9101
9093
  const historyQ = useLoader(() => api.settings.getRateLimitHistory());
9102
9094
  const insightsQ = useLoader(() => api.stats.securityInsights(7));
@@ -9110,12 +9102,8 @@ function RateLimitPanel({ focused }) {
9110
9102
  const adjust = (dir, step) => {
9111
9103
  if (!draft) return;
9112
9104
  const field = FIELDS2[fi];
9113
- if (field === "mode") {
9114
- const idx = (MODES.indexOf(draft.mode) + dir + MODES.length) % MODES.length;
9115
- setDraft({ ...draft, mode: MODES[idx] });
9116
- } else {
9117
- setDraft({ ...draft, [field]: Math.max(0, draft[field] + dir * step) });
9118
- }
9105
+ if (field === "mode") setDraft({ ...draft, mode: MODES[(MODES.indexOf(draft.mode) + dir + MODES.length) % MODES.length] });
9106
+ else setDraft({ ...draft, [field]: Math.max(0, draft[field] + dir * step) });
9119
9107
  setDirty(true);
9120
9108
  setStatus(null);
9121
9109
  };
@@ -9128,6 +9116,7 @@ function RateLimitPanel({ focused }) {
9128
9116
  setDirty(false);
9129
9117
  setStatus("\u2713 Saved");
9130
9118
  historyQ.reload();
9119
+ insightsQ.reload();
9131
9120
  } catch (e) {
9132
9121
  setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
9133
9122
  }
@@ -9144,86 +9133,89 @@ function RateLimitPanel({ focused }) {
9144
9133
  { isActive: focused }
9145
9134
  );
9146
9135
  const history = (historyQ.data?.history ?? []).slice().sort((a, b) => b.ts - a.ts);
9147
- const activity = insightsQ.data?.["activity"] ?? {};
9148
- const minuteSeries = (activity.minute ?? []).map((b) => b.count);
9149
- const hourSeries = (activity.hour ?? []).map((b) => b.count);
9150
- const bursts = minuteSeries.filter((c2) => draft && draft.perMinute > 0 && c2 > draft.perMinute).length;
9151
- return /* @__PURE__ */ jsx4(DataView, { loading: layersQ.loading && !draft, error: layersQ.error, children: draft ? /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
9152
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: focused ? "\u2191\u2193 field \xB7 \u2190\u2192 \xB11 \xB7 shift+\u2190\u2192 \xB110 \xB7 s save" : "press \u2192 to edit" }),
9153
- /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
9154
- /* @__PURE__ */ jsx4(FieldRow, { label: "Mode", active: focused && fi === 0, children: /* @__PURE__ */ jsx4(Text4, { color: modeColor(draft.mode), children: draft.mode }) }),
9155
- /* @__PURE__ */ jsx4(FieldRow, { label: "Per minute", active: focused && fi === 1, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perMinute }) }),
9156
- /* @__PURE__ */ jsx4(FieldRow, { label: "Per hour", active: focused && fi === 2, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perHour === 0 ? "off" : draft.perHour }) }),
9157
- /* @__PURE__ */ jsx4(FieldRow, { label: "Per day", active: focused && fi === 3, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perDay === 0 ? "off" : draft.perDay }) })
9158
- ] }),
9159
- draft.perMinute > 0 ? (() => {
9160
- const now = minuteSeries.slice(-1)[0] ?? 0;
9161
- const pct = Math.min(100, Math.round(now / draft.perMinute * 100));
9162
- const width2 = 30;
9163
- const filled = Math.min(width2, Math.round(now / draft.perMinute * width2));
9164
- const col = pct >= 100 ? theme.bad : pct >= 80 ? theme.warn : theme.ok;
9165
- return /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
9166
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "load " }),
9167
- /* @__PURE__ */ jsx4(Text4, { color: col, children: "\u2588".repeat(filled) }),
9168
- /* @__PURE__ */ jsx4(Text4, { color: "#233457", children: "\u2591".repeat(Math.max(0, width2 - filled)) }),
9169
- /* @__PURE__ */ jsx4(Text4, { color: col, children: ` ${now}/${draft.perMinute} ` }),
9170
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: `(${pct}% this min)` })
9171
- ] });
9172
- })() : null,
9173
- dirty || status ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
9174
- dirty ? /* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: "\u25CF unsaved \u2014 press s to apply " }) : null,
9175
- status ? /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
9176
- ] }) : null,
9177
- /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
9178
- /* @__PURE__ */ jsxs4(Box4, { children: [
9179
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "Activity " }),
9180
- bursts > 0 ? /* @__PURE__ */ jsxs4(Text4, { color: theme.bad, children: [
9181
- bursts,
9182
- " burst min"
9183
- ] }) : /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "no bursts" }),
9184
- insightsQ.loading && !insightsQ.data ? /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " \u2026" }) : null
9136
+ const lastChange = history[0];
9137
+ 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());
9139
+ return /* @__PURE__ */ jsx4(DataView, { loading: layersQ.loading && !draft, error: layersQ.error, children: draft ? (() => {
9140
+ const lim = draft.perMinute;
9141
+ const now = insightsQ.data ? insightsQ.data["activity"]?.minute?.slice(-1)[0]?.count ?? 0 : 0;
9142
+ const loadPct = lim > 0 ? Math.min(100, Math.round(now / lim * 100)) : 0;
9143
+ const last24h = (insightsQ.data?.["activity"]?.hour ?? []).reduce((s, b) => s + b.count, 0);
9144
+ const hasLoad = lim > 0;
9145
+ const fixed = 1 + 4 + (hasLoad ? 1 : 0) + 5 + 1 + 1 + 1 + 1 + (dirty || status ? 1 : 0) + 1;
9146
+ const burstBudget = Math.max(1, rows - fixed);
9147
+ const shown = anomalies2.slice(0, burstBudget);
9148
+ const bw = Math.max(10, cols - 30);
9149
+ 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" }),
9151
+ /* @__PURE__ */ jsxs4(FieldRow, { label: "Mode", active: focused && fi === 0, children: [
9152
+ /* @__PURE__ */ jsx4(Text4, { color: modeColor(draft.mode), bold: true, children: draft.mode }),
9153
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: draft.mode === "off" ? " no limit" : draft.mode === "detect" ? " flag bursts, never block" : " DENY calls over the limit" })
9185
9154
  ] }),
9186
- /* @__PURE__ */ jsxs4(Box4, { children: [
9187
- /* @__PURE__ */ jsx4(Text4, { children: "/min " }),
9188
- minuteSeries.length ? /* @__PURE__ */ jsx4(ColoredSpark, { values: minuteSeries, limit: draft.perMinute }) : /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "no data" })
9155
+ /* @__PURE__ */ jsx4(FieldRow, { label: "Per minute", active: focused && fi === 1, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perMinute || "off" }) }),
9156
+ /* @__PURE__ */ jsx4(FieldRow, { label: "Per hour", active: focused && fi === 2, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perHour || "off" }) }),
9157
+ /* @__PURE__ */ jsx4(FieldRow, { label: "Per day", active: focused && fi === 3, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perDay || "off" }) }),
9158
+ hasLoad ? /* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
9159
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "load now".padEnd(13) }),
9160
+ /* @__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))) }),
9161
+ /* @__PURE__ */ jsx4(Text4, { color: "#233457", children: "\u2591".repeat(Math.max(0, 18 - Math.round(now / lim * 18))) }),
9162
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: ` ${now}/${lim} this minute (${loadPct}%)` })
9163
+ ] }) : null,
9164
+ /* @__PURE__ */ jsxs4(Text4, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
9165
+ "Busiest 7d",
9166
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " most calls in one window vs your limit" })
9189
9167
  ] }),
9190
- /* @__PURE__ */ jsxs4(Box4, { children: [
9191
- /* @__PURE__ */ jsx4(Text4, { children: "/hour " }),
9192
- hourSeries.length ? /* @__PURE__ */ jsx4(ColoredSpark, { values: hourSeries, limit: draft.perHour, width: 24 }) : /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "no data" })
9193
- ] })
9194
- ] }),
9195
- /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
9196
- /* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
9197
- "Change history ",
9198
- history.length ? `(${history.length})` : ""
9168
+ /* @__PURE__ */ jsx4(BusiestRow, { label: "minute", peak: peaks.minute, limit: draft.perMinute }),
9169
+ /* @__PURE__ */ jsx4(BusiestRow, { label: "hour", peak: peaks.hour, limit: draft.perHour }),
9170
+ /* @__PURE__ */ jsx4(BusiestRow, { label: "day", peak: peaks.day, limit: draft.perDay }),
9171
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, wrap: "truncate", children: `activity`.padEnd(11) + ` last 24h \xB7 ${last24h} calls \xB7 busiest hour ${peaks.hour}/hr` }),
9172
+ /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
9173
+ /* @__PURE__ */ jsxs4(Text4, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
9174
+ `Recent bursts 7d${anomalies2.length ? ` (${anomalies2.length})` : ""}`,
9175
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " over the per-minute limit \xB7 Bypassed = detect (not blocked)" })
9176
+ ] }),
9177
+ anomalies2.length === 0 ? /* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
9178
+ " ",
9179
+ insightsQ.loading ? "loading\u2026" : "no bursts in the last 7 days"
9180
+ ] }) : /* @__PURE__ */ jsx4(
9181
+ Table,
9182
+ {
9183
+ columns: [{ header: "WHEN", width: 13 }, { header: "RESULT", width: 9 }, { header: "CALLS", width: 7 }, { header: "AGENT", width: Math.max(8, bw - 29) }],
9184
+ rows: shown.map((a) => [
9185
+ { value: when(new Date(a.minute).getTime()), dim: true },
9186
+ { value: a.blocked ? "Blocked" : "Bypassed", color: a.blocked ? theme.bad : theme.warn },
9187
+ { value: `${a.count}/${a.limit}`, color: theme.warn },
9188
+ { value: truncate2(a.agent, Math.max(8, bw - 29)), dim: true }
9189
+ ])
9190
+ }
9191
+ )
9199
9192
  ] }),
9200
- history.length ? /* @__PURE__ */ jsx4(
9201
- Table,
9202
- {
9203
- columns: [
9204
- { header: "WHEN", width: 10 },
9205
- { header: "/MIN", width: 6 },
9206
- { header: "/HOUR", width: 7 },
9207
- { header: "/DAY", width: 6 }
9208
- ],
9209
- rows: history.slice(0, 4).map((h, i) => [
9210
- { value: ago(h.ts) + " ago", dim: true, bold: i === 0 },
9211
- { value: String(h.minute), bold: i === 0 },
9212
- { value: h.hour === 0 ? "off" : String(h.hour), dim: h.hour === 0 },
9213
- { value: h.day === 0 ? "off" : String(h.day), dim: h.day === 0 }
9214
- ])
9215
- }
9216
- ) : /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " no changes" })
9217
- ] })
9218
- ] }) : null });
9193
+ /* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
9194
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "limit now " }),
9195
+ /* @__PURE__ */ jsx4(Text4, { children: lim > 0 ? `${lim}/min` : "no per-minute limit" }),
9196
+ lastChange ? /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: ` \xB7 changed ${when(lastChange.ts)}` }) : null,
9197
+ dirty ? /* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: " \u25CF unsaved (s)" }) : null,
9198
+ status ? /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: " " + status }) : null
9199
+ ] })
9200
+ ] });
9201
+ })() : null });
9202
+ }
9203
+ function BusiestRow({ label, peak, limit }) {
9204
+ const ctx = limit > 0 ? peak > limit ? `over ${limit}` : peak === limit ? `at limit ${limit}` : `limit ${limit}` : "no limit set";
9205
+ const hot = limit > 0 && peak > limit;
9206
+ return /* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
9207
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: (" " + label).padEnd(11) }),
9208
+ /* @__PURE__ */ jsx4(Text4, { color: hot ? theme.bad : void 0, bold: hot, children: String(peak).padStart(4) + " calls" }),
9209
+ /* @__PURE__ */ jsx4(Text4, { color: hot ? theme.bad : theme.dim, children: " \xB7 " + ctx })
9210
+ ] });
9219
9211
  }
9220
9212
  function FieldRow({ label, active: active2, children }) {
9221
- return /* @__PURE__ */ jsxs4(Box4, { children: [
9222
- /* @__PURE__ */ jsx4(Text4, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + label.padEnd(12) }),
9213
+ return /* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
9214
+ /* @__PURE__ */ jsx4(Text4, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + label.padEnd(11) }),
9223
9215
  children
9224
9216
  ] });
9225
9217
  }
9226
- var MODES, FIELDS2, BLOCKS;
9218
+ var MODES, FIELDS2, when;
9227
9219
  var init_RateLimit = __esm({
9228
9220
  "src/tui/panels/RateLimit.tsx"() {
9229
9221
  "use strict";
@@ -9233,7 +9225,10 @@ var init_RateLimit = __esm({
9233
9225
  init_theme();
9234
9226
  MODES = ["off", "detect", "block"];
9235
9227
  FIELDS2 = ["mode", "perMinute", "perHour", "perDay"];
9236
- BLOCKS = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
9228
+ when = (ms) => {
9229
+ const d = new Date(ms);
9230
+ return Number.isNaN(d.getTime()) ? "\u2014" : d.toLocaleDateString([], { month: "short", day: "numeric" }) + " " + d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", hour12: false });
9231
+ };
9237
9232
  }
9238
9233
  });
9239
9234
 
@@ -11211,10 +11206,10 @@ function truncate3(s, n) {
11211
11206
  function sparkline(values) {
11212
11207
  if (values.length === 0) return "";
11213
11208
  const max = Math.max(...values, 0);
11214
- if (max === 0) return BLOCKS2[0].repeat(values.length);
11215
- return values.map((v) => BLOCKS2[Math.min(BLOCKS2.length - 1, Math.round(v / max * (BLOCKS2.length - 1)))]).join("");
11209
+ if (max === 0) return BLOCKS[0].repeat(values.length);
11210
+ return values.map((v) => BLOCKS[Math.min(BLOCKS.length - 1, Math.round(v / max * (BLOCKS.length - 1)))]).join("");
11216
11211
  }
11217
- var out, err, dim, bold, green, red, yellow, cyan, ANSI, width, BLOCKS2;
11212
+ var out, err, dim, bold, green, red, yellow, cyan, ANSI, width, BLOCKS;
11218
11213
  var init_format = __esm({
11219
11214
  "src/commands/format.ts"() {
11220
11215
  "use strict";
@@ -11229,7 +11224,7 @@ var init_format = __esm({
11229
11224
  cyan = (s) => `${c.cyan}${s}${c.reset}`;
11230
11225
  ANSI = /\x1b\[[0-9;]*m/g;
11231
11226
  width = (s) => s.replace(ANSI, "").length;
11232
- BLOCKS2 = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
11227
+ BLOCKS = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
11233
11228
  }
11234
11229
  });
11235
11230
 
package/dist/tui/index.js CHANGED
@@ -2176,17 +2176,12 @@ import { useEffect as useEffect4, useState as useState4 } from "react";
2176
2176
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
2177
2177
  var MODES = ["off", "detect", "block"];
2178
2178
  var FIELDS2 = ["mode", "perMinute", "perHour", "perDay"];
2179
- var BLOCKS = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
2180
- function ColoredSpark({ values, limit, width = 60 }) {
2181
- const v = values.length > width ? values.slice(values.length - width) : values;
2182
- const max = Math.max(1, ...v);
2183
- return /* @__PURE__ */ jsx4(Text4, { children: v.map((n, i) => {
2184
- const ch = BLOCKS[Math.min(BLOCKS.length - 1, Math.round(n / max * (BLOCKS.length - 1)))];
2185
- const over = limit > 0 && n > limit;
2186
- return /* @__PURE__ */ jsx4(Text4, { color: over ? theme.bad : theme.accent, children: ch }, i);
2187
- }) });
2188
- }
2179
+ var when = (ms) => {
2180
+ const d = new Date(ms);
2181
+ return Number.isNaN(d.getTime()) ? "\u2014" : d.toLocaleDateString([], { month: "short", day: "numeric" }) + " " + d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", hour12: false });
2182
+ };
2189
2183
  function RateLimitPanel({ focused }) {
2184
+ const { cols, rows } = usePanelSize();
2190
2185
  const layersQ = useLoader(() => api.settings.getSecurityLayers());
2191
2186
  const historyQ = useLoader(() => api.settings.getRateLimitHistory());
2192
2187
  const insightsQ = useLoader(() => api.stats.securityInsights(7));
@@ -2200,12 +2195,8 @@ function RateLimitPanel({ focused }) {
2200
2195
  const adjust = (dir, step) => {
2201
2196
  if (!draft) return;
2202
2197
  const field = FIELDS2[fi];
2203
- if (field === "mode") {
2204
- const idx = (MODES.indexOf(draft.mode) + dir + MODES.length) % MODES.length;
2205
- setDraft({ ...draft, mode: MODES[idx] });
2206
- } else {
2207
- setDraft({ ...draft, [field]: Math.max(0, draft[field] + dir * step) });
2208
- }
2198
+ if (field === "mode") setDraft({ ...draft, mode: MODES[(MODES.indexOf(draft.mode) + dir + MODES.length) % MODES.length] });
2199
+ else setDraft({ ...draft, [field]: Math.max(0, draft[field] + dir * step) });
2209
2200
  setDirty(true);
2210
2201
  setStatus(null);
2211
2202
  };
@@ -2218,6 +2209,7 @@ function RateLimitPanel({ focused }) {
2218
2209
  setDirty(false);
2219
2210
  setStatus("\u2713 Saved");
2220
2211
  historyQ.reload();
2212
+ insightsQ.reload();
2221
2213
  } catch (e) {
2222
2214
  setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
2223
2215
  }
@@ -2234,82 +2226,85 @@ function RateLimitPanel({ focused }) {
2234
2226
  { isActive: focused }
2235
2227
  );
2236
2228
  const history = (historyQ.data?.history ?? []).slice().sort((a, b) => b.ts - a.ts);
2237
- const activity = insightsQ.data?.["activity"] ?? {};
2238
- const minuteSeries = (activity.minute ?? []).map((b) => b.count);
2239
- const hourSeries = (activity.hour ?? []).map((b) => b.count);
2240
- const bursts = minuteSeries.filter((c2) => draft && draft.perMinute > 0 && c2 > draft.perMinute).length;
2241
- return /* @__PURE__ */ jsx4(DataView, { loading: layersQ.loading && !draft, error: layersQ.error, children: draft ? /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
2242
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: focused ? "\u2191\u2193 field \xB7 \u2190\u2192 \xB11 \xB7 shift+\u2190\u2192 \xB110 \xB7 s save" : "press \u2192 to edit" }),
2243
- /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
2244
- /* @__PURE__ */ jsx4(FieldRow, { label: "Mode", active: focused && fi === 0, children: /* @__PURE__ */ jsx4(Text4, { color: modeColor(draft.mode), children: draft.mode }) }),
2245
- /* @__PURE__ */ jsx4(FieldRow, { label: "Per minute", active: focused && fi === 1, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perMinute }) }),
2246
- /* @__PURE__ */ jsx4(FieldRow, { label: "Per hour", active: focused && fi === 2, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perHour === 0 ? "off" : draft.perHour }) }),
2247
- /* @__PURE__ */ jsx4(FieldRow, { label: "Per day", active: focused && fi === 3, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perDay === 0 ? "off" : draft.perDay }) })
2248
- ] }),
2249
- draft.perMinute > 0 ? (() => {
2250
- const now = minuteSeries.slice(-1)[0] ?? 0;
2251
- const pct = Math.min(100, Math.round(now / draft.perMinute * 100));
2252
- const width = 30;
2253
- const filled = Math.min(width, Math.round(now / draft.perMinute * width));
2254
- const col = pct >= 100 ? theme.bad : pct >= 80 ? theme.warn : theme.ok;
2255
- return /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
2256
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "load " }),
2257
- /* @__PURE__ */ jsx4(Text4, { color: col, children: "\u2588".repeat(filled) }),
2258
- /* @__PURE__ */ jsx4(Text4, { color: "#233457", children: "\u2591".repeat(Math.max(0, width - filled)) }),
2259
- /* @__PURE__ */ jsx4(Text4, { color: col, children: ` ${now}/${draft.perMinute} ` }),
2260
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: `(${pct}% this min)` })
2261
- ] });
2262
- })() : null,
2263
- dirty || status ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
2264
- dirty ? /* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: "\u25CF unsaved \u2014 press s to apply " }) : null,
2265
- status ? /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
2266
- ] }) : null,
2267
- /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
2268
- /* @__PURE__ */ jsxs4(Box4, { children: [
2269
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "Activity " }),
2270
- bursts > 0 ? /* @__PURE__ */ jsxs4(Text4, { color: theme.bad, children: [
2271
- bursts,
2272
- " burst min"
2273
- ] }) : /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "no bursts" }),
2274
- insightsQ.loading && !insightsQ.data ? /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " \u2026" }) : null
2229
+ const lastChange = history[0];
2230
+ 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());
2232
+ return /* @__PURE__ */ jsx4(DataView, { loading: layersQ.loading && !draft, error: layersQ.error, children: draft ? (() => {
2233
+ const lim = draft.perMinute;
2234
+ const now = insightsQ.data ? insightsQ.data["activity"]?.minute?.slice(-1)[0]?.count ?? 0 : 0;
2235
+ const loadPct = lim > 0 ? Math.min(100, Math.round(now / lim * 100)) : 0;
2236
+ const last24h = (insightsQ.data?.["activity"]?.hour ?? []).reduce((s, b) => s + b.count, 0);
2237
+ const hasLoad = lim > 0;
2238
+ const fixed = 1 + 4 + (hasLoad ? 1 : 0) + 5 + 1 + 1 + 1 + 1 + (dirty || status ? 1 : 0) + 1;
2239
+ const burstBudget = Math.max(1, rows - fixed);
2240
+ const shown = anomalies2.slice(0, burstBudget);
2241
+ const bw = Math.max(10, cols - 30);
2242
+ 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" }),
2244
+ /* @__PURE__ */ jsxs4(FieldRow, { label: "Mode", active: focused && fi === 0, children: [
2245
+ /* @__PURE__ */ jsx4(Text4, { color: modeColor(draft.mode), bold: true, children: draft.mode }),
2246
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: draft.mode === "off" ? " no limit" : draft.mode === "detect" ? " flag bursts, never block" : " DENY calls over the limit" })
2275
2247
  ] }),
2276
- /* @__PURE__ */ jsxs4(Box4, { children: [
2277
- /* @__PURE__ */ jsx4(Text4, { children: "/min " }),
2278
- minuteSeries.length ? /* @__PURE__ */ jsx4(ColoredSpark, { values: minuteSeries, limit: draft.perMinute }) : /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "no data" })
2248
+ /* @__PURE__ */ jsx4(FieldRow, { label: "Per minute", active: focused && fi === 1, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perMinute || "off" }) }),
2249
+ /* @__PURE__ */ jsx4(FieldRow, { label: "Per hour", active: focused && fi === 2, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perHour || "off" }) }),
2250
+ /* @__PURE__ */ jsx4(FieldRow, { label: "Per day", active: focused && fi === 3, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perDay || "off" }) }),
2251
+ hasLoad ? /* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
2252
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "load now".padEnd(13) }),
2253
+ /* @__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))) }),
2254
+ /* @__PURE__ */ jsx4(Text4, { color: "#233457", children: "\u2591".repeat(Math.max(0, 18 - Math.round(now / lim * 18))) }),
2255
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: ` ${now}/${lim} this minute (${loadPct}%)` })
2256
+ ] }) : null,
2257
+ /* @__PURE__ */ jsxs4(Text4, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
2258
+ "Busiest 7d",
2259
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " most calls in one window vs your limit" })
2279
2260
  ] }),
2280
- /* @__PURE__ */ jsxs4(Box4, { children: [
2281
- /* @__PURE__ */ jsx4(Text4, { children: "/hour " }),
2282
- hourSeries.length ? /* @__PURE__ */ jsx4(ColoredSpark, { values: hourSeries, limit: draft.perHour, width: 24 }) : /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "no data" })
2283
- ] })
2284
- ] }),
2285
- /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
2286
- /* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
2287
- "Change history ",
2288
- history.length ? `(${history.length})` : ""
2261
+ /* @__PURE__ */ jsx4(BusiestRow, { label: "minute", peak: peaks.minute, limit: draft.perMinute }),
2262
+ /* @__PURE__ */ jsx4(BusiestRow, { label: "hour", peak: peaks.hour, limit: draft.perHour }),
2263
+ /* @__PURE__ */ jsx4(BusiestRow, { label: "day", peak: peaks.day, limit: draft.perDay }),
2264
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, wrap: "truncate", children: `activity`.padEnd(11) + ` last 24h \xB7 ${last24h} calls \xB7 busiest hour ${peaks.hour}/hr` }),
2265
+ /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
2266
+ /* @__PURE__ */ jsxs4(Text4, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
2267
+ `Recent bursts 7d${anomalies2.length ? ` (${anomalies2.length})` : ""}`,
2268
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " over the per-minute limit \xB7 Bypassed = detect (not blocked)" })
2269
+ ] }),
2270
+ anomalies2.length === 0 ? /* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
2271
+ " ",
2272
+ insightsQ.loading ? "loading\u2026" : "no bursts in the last 7 days"
2273
+ ] }) : /* @__PURE__ */ jsx4(
2274
+ Table,
2275
+ {
2276
+ columns: [{ header: "WHEN", width: 13 }, { header: "RESULT", width: 9 }, { header: "CALLS", width: 7 }, { header: "AGENT", width: Math.max(8, bw - 29) }],
2277
+ rows: shown.map((a) => [
2278
+ { value: when(new Date(a.minute).getTime()), dim: true },
2279
+ { value: a.blocked ? "Blocked" : "Bypassed", color: a.blocked ? theme.bad : theme.warn },
2280
+ { value: `${a.count}/${a.limit}`, color: theme.warn },
2281
+ { value: truncate(a.agent, Math.max(8, bw - 29)), dim: true }
2282
+ ])
2283
+ }
2284
+ )
2289
2285
  ] }),
2290
- history.length ? /* @__PURE__ */ jsx4(
2291
- Table,
2292
- {
2293
- columns: [
2294
- { header: "WHEN", width: 10 },
2295
- { header: "/MIN", width: 6 },
2296
- { header: "/HOUR", width: 7 },
2297
- { header: "/DAY", width: 6 }
2298
- ],
2299
- rows: history.slice(0, 4).map((h, i) => [
2300
- { value: ago(h.ts) + " ago", dim: true, bold: i === 0 },
2301
- { value: String(h.minute), bold: i === 0 },
2302
- { value: h.hour === 0 ? "off" : String(h.hour), dim: h.hour === 0 },
2303
- { value: h.day === 0 ? "off" : String(h.day), dim: h.day === 0 }
2304
- ])
2305
- }
2306
- ) : /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " no changes" })
2307
- ] })
2308
- ] }) : null });
2286
+ /* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
2287
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "limit now " }),
2288
+ /* @__PURE__ */ jsx4(Text4, { children: lim > 0 ? `${lim}/min` : "no per-minute limit" }),
2289
+ lastChange ? /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: ` \xB7 changed ${when(lastChange.ts)}` }) : null,
2290
+ dirty ? /* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: " \u25CF unsaved (s)" }) : null,
2291
+ status ? /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: " " + status }) : null
2292
+ ] })
2293
+ ] });
2294
+ })() : null });
2295
+ }
2296
+ function BusiestRow({ label, peak, limit }) {
2297
+ const ctx = limit > 0 ? peak > limit ? `over ${limit}` : peak === limit ? `at limit ${limit}` : `limit ${limit}` : "no limit set";
2298
+ const hot = limit > 0 && peak > limit;
2299
+ return /* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
2300
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: (" " + label).padEnd(11) }),
2301
+ /* @__PURE__ */ jsx4(Text4, { color: hot ? theme.bad : void 0, bold: hot, children: String(peak).padStart(4) + " calls" }),
2302
+ /* @__PURE__ */ jsx4(Text4, { color: hot ? theme.bad : theme.dim, children: " \xB7 " + ctx })
2303
+ ] });
2309
2304
  }
2310
2305
  function FieldRow({ label, active: active2, children }) {
2311
- return /* @__PURE__ */ jsxs4(Box4, { children: [
2312
- /* @__PURE__ */ jsx4(Text4, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + label.padEnd(12) }),
2306
+ return /* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
2307
+ /* @__PURE__ */ jsx4(Text4, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + label.padEnd(11) }),
2313
2308
  children
2314
2309
  ] });
2315
2310
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.81.87",
3
+ "version": "0.81.89",
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": {