@solongate/proxy 0.81.95 → 0.81.97

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
@@ -9111,7 +9111,7 @@ function RateLimitPanel({ focused }) {
9111
9111
  const insightsQ = useLoader(() => api.stats.securityInsights(7));
9112
9112
  const [draft, setDraft] = useState4(null);
9113
9113
  const [dirty, setDirty] = useState4(false);
9114
- const [fi, setFi] = useState4(0);
9114
+ const [sel, setSel] = useState4(0);
9115
9115
  const [status, setStatus] = useState4(null);
9116
9116
  useEffect4(() => {
9117
9117
  if (layersQ.data && !dirty) setDraft({ ...layersQ.data.layers.rateLimit });
@@ -9124,7 +9124,7 @@ function RateLimitPanel({ focused }) {
9124
9124
  insightsQ.reloadQuiet();
9125
9125
  }, 1e4);
9126
9126
  const adjust = (dir, step) => {
9127
- if (!draft) return;
9127
+ if (!draft || fi < 0) return;
9128
9128
  const field = FIELDS2[fi];
9129
9129
  if (field === "mode") setDraft({ ...draft, mode: MODES[(MODES.indexOf(draft.mode) + dir + MODES.length) % MODES.length] });
9130
9130
  else setDraft({ ...draft, [field]: Math.max(0, draft[field] + dir * step) });
@@ -9146,11 +9146,28 @@ function RateLimitPanel({ focused }) {
9146
9146
  setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
9147
9147
  }
9148
9148
  };
9149
+ const history = (historyQ.data?.history ?? []).slice().sort((a, b) => b.ts - a.ts);
9150
+ const lastChange = history[0];
9151
+ const peaks = insightsQ.data?.["peaks"] ?? { minute: 0, hour: 0, day: 0 };
9152
+ const anomalies2 = (insightsQ.data?.["anomalies"] ?? []).slice().sort((a, b) => b.count - a.count || new Date(b.minute).getTime() - new Date(a.minute).getTime());
9153
+ const hasLoad = (draft?.perMinute ?? 0) > 0;
9154
+ const showExtras = rows >= 20;
9155
+ const fixed = 1 + 4 + (showExtras ? (hasLoad ? 1 : 0) + 5 : 0) + 1 + 1 + 1 + 1;
9156
+ const burstBudget = Math.max(1, rows - fixed);
9157
+ const maxOff = Math.max(0, anomalies2.length - burstBudget);
9158
+ const total = 4 + anomalies2.length;
9159
+ const selC = Math.min(sel, Math.max(0, total - 1));
9160
+ const onField = selC < 4;
9161
+ const fi = onField ? selC : -1;
9162
+ const bcur = onField ? -1 : selC - 4;
9163
+ const off = bcur < 0 ? 0 : Math.min(Math.max(0, bcur - Math.floor(burstBudget / 2)), maxOff);
9149
9164
  useInput3(
9150
9165
  (input, key) => {
9151
9166
  const step = key.shift ? 10 : 1;
9152
- if (key.upArrow) setFi((n) => (n - 1 + FIELDS2.length) % FIELDS2.length);
9153
- else if (key.downArrow) setFi((n) => (n + 1) % FIELDS2.length);
9167
+ if (key.upArrow) setSel((n) => Math.max(0, n - 1));
9168
+ else if (key.downArrow) setSel((n) => Math.min(total - 1, n + 1));
9169
+ else if (key.pageDown) setSel((n) => Math.min(total - 1, n + burstBudget));
9170
+ else if (key.pageUp) setSel((n) => Math.max(0, n - burstBudget));
9154
9171
  else if (key.leftArrow) adjust(-1, step);
9155
9172
  else if (key.rightArrow) adjust(1, step);
9156
9173
  else if (input === "s") void save();
@@ -9163,23 +9180,15 @@ function RateLimitPanel({ focused }) {
9163
9180
  },
9164
9181
  { isActive: focused }
9165
9182
  );
9166
- const history = (historyQ.data?.history ?? []).slice().sort((a, b) => b.ts - a.ts);
9167
- const lastChange = history[0];
9168
- const peaks = insightsQ.data?.["peaks"] ?? { minute: 0, hour: 0, day: 0 };
9169
- const anomalies2 = (insightsQ.data?.["anomalies"] ?? []).slice().sort((a, b) => b.count - a.count || new Date(b.minute).getTime() - new Date(a.minute).getTime());
9170
9183
  return /* @__PURE__ */ jsx4(DataView, { loading: layersQ.loading && !draft, error: layersQ.error, children: draft ? (() => {
9171
9184
  const lim = draft.perMinute;
9172
9185
  const now = insightsQ.data ? insightsQ.data["activity"]?.minute?.slice(-1)[0]?.count ?? 0 : 0;
9173
9186
  const loadPct = lim > 0 ? Math.min(100, Math.round(now / lim * 100)) : 0;
9174
9187
  const last24h = (insightsQ.data?.["activity"]?.hour ?? []).reduce((s, b) => s + b.count, 0);
9175
- const hasLoad = lim > 0;
9176
- const showExtras = rows >= 20;
9177
- const fixed = 1 + 4 + (showExtras ? (hasLoad ? 1 : 0) + 5 : 0) + 1 + 1 + 1;
9178
- const burstBudget = Math.max(1, rows - fixed);
9179
- const shown = anomalies2.slice(0, burstBudget);
9188
+ const shown = anomalies2.slice(off, off + burstBudget);
9180
9189
  const bw = Math.max(10, cols - 30);
9181
9190
  return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", height: rows, overflow: "hidden", children: [
9182
- /* @__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" }),
9191
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 move (fields + bursts) \xB7 \u2190\u2192 \xB11 \xB7 shift+\u2190\u2192 \xB110 \xB7 s save \xB7 ^R refresh" : "press \u2192 to edit" }),
9183
9192
  /* @__PURE__ */ jsxs4(FieldRow, { label: "Mode", active: focused && fi === 0, children: [
9184
9193
  /* @__PURE__ */ jsx4(Text4, { color: modeColor(draft.mode), bold: true, children: draft.mode }),
9185
9194
  /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: draft.mode === "off" ? " no limit" : draft.mode === "detect" ? " flag bursts, never block" : " DENY calls over the limit" })
@@ -9206,6 +9215,7 @@ function RateLimitPanel({ focused }) {
9206
9215
  /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
9207
9216
  /* @__PURE__ */ jsxs4(Text4, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
9208
9217
  `Recent bursts 7d${anomalies2.length ? ` (${anomalies2.length})` : ""}`,
9218
+ maxOff > 0 ? /* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: ` ${off + 1}-${Math.min(off + burstBudget, anomalies2.length)}/${anomalies2.length}` }) : null,
9209
9219
  /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " over the per-minute limit \xB7 Bypassed = detect (not blocked)" })
9210
9220
  ] }),
9211
9221
  anomalies2.length === 0 ? /* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
@@ -9214,16 +9224,20 @@ function RateLimitPanel({ focused }) {
9214
9224
  ] }) : /* @__PURE__ */ jsx4(
9215
9225
  Table,
9216
9226
  {
9217
- columns: [{ header: "WHEN", width: 13 }, { header: "RESULT", width: 9 }, { header: "CALLS", width: 7 }, { header: "AGENT", width: Math.max(8, bw - 29) }],
9218
- rows: shown.map((a) => [
9219
- { value: when(new Date(a.minute).getTime()), dim: true },
9220
- { value: a.blocked ? "Blocked" : "Bypassed", color: a.blocked ? theme.bad : theme.warn },
9221
- { value: `${a.count}/${a.limit}`, color: theme.warn },
9222
- { value: truncate2(a.agent, Math.max(8, bw - 29)), dim: true }
9223
- ])
9227
+ columns: [{ header: "WHEN", width: 15 }, { header: "RESULT", width: 9 }, { header: "CALLS", width: 7 }, { header: "AGENT", width: Math.max(8, bw - 31) }],
9228
+ rows: shown.map((a, i) => {
9229
+ const isSel = off + i === bcur;
9230
+ return [
9231
+ { value: (isSel ? "\u25B8 " : " ") + when(new Date(a.minute).getTime()), color: isSel ? theme.accentBright : void 0, dim: !isSel },
9232
+ { value: a.blocked ? "Blocked" : "Bypassed", color: a.blocked ? theme.bad : theme.warn },
9233
+ { value: `${a.count}/${a.limit}`, color: theme.warn },
9234
+ { value: truncate2(a.agent, Math.max(8, bw - 31)), dim: !isSel }
9235
+ ];
9236
+ })
9224
9237
  }
9225
9238
  )
9226
9239
  ] }),
9240
+ /* @__PURE__ */ jsx4(Text4, { children: " " }),
9227
9241
  /* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
9228
9242
  /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "limit now " }),
9229
9243
  /* @__PURE__ */ jsx4(Text4, { children: lim > 0 ? `${lim}/min` : "no per-minute limit" }),
package/dist/tui/index.js CHANGED
@@ -2199,7 +2199,7 @@ function RateLimitPanel({ focused }) {
2199
2199
  const insightsQ = useLoader(() => api.stats.securityInsights(7));
2200
2200
  const [draft, setDraft] = useState4(null);
2201
2201
  const [dirty, setDirty] = useState4(false);
2202
- const [fi, setFi] = useState4(0);
2202
+ const [sel, setSel] = useState4(0);
2203
2203
  const [status, setStatus] = useState4(null);
2204
2204
  useEffect4(() => {
2205
2205
  if (layersQ.data && !dirty) setDraft({ ...layersQ.data.layers.rateLimit });
@@ -2212,7 +2212,7 @@ function RateLimitPanel({ focused }) {
2212
2212
  insightsQ.reloadQuiet();
2213
2213
  }, 1e4);
2214
2214
  const adjust = (dir, step) => {
2215
- if (!draft) return;
2215
+ if (!draft || fi < 0) return;
2216
2216
  const field = FIELDS2[fi];
2217
2217
  if (field === "mode") setDraft({ ...draft, mode: MODES[(MODES.indexOf(draft.mode) + dir + MODES.length) % MODES.length] });
2218
2218
  else setDraft({ ...draft, [field]: Math.max(0, draft[field] + dir * step) });
@@ -2234,11 +2234,28 @@ function RateLimitPanel({ focused }) {
2234
2234
  setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
2235
2235
  }
2236
2236
  };
2237
+ const history = (historyQ.data?.history ?? []).slice().sort((a, b) => b.ts - a.ts);
2238
+ const lastChange = history[0];
2239
+ const peaks = insightsQ.data?.["peaks"] ?? { minute: 0, hour: 0, day: 0 };
2240
+ const anomalies2 = (insightsQ.data?.["anomalies"] ?? []).slice().sort((a, b) => b.count - a.count || new Date(b.minute).getTime() - new Date(a.minute).getTime());
2241
+ const hasLoad = (draft?.perMinute ?? 0) > 0;
2242
+ const showExtras = rows >= 20;
2243
+ const fixed = 1 + 4 + (showExtras ? (hasLoad ? 1 : 0) + 5 : 0) + 1 + 1 + 1 + 1;
2244
+ const burstBudget = Math.max(1, rows - fixed);
2245
+ const maxOff = Math.max(0, anomalies2.length - burstBudget);
2246
+ const total = 4 + anomalies2.length;
2247
+ const selC = Math.min(sel, Math.max(0, total - 1));
2248
+ const onField = selC < 4;
2249
+ const fi = onField ? selC : -1;
2250
+ const bcur = onField ? -1 : selC - 4;
2251
+ const off = bcur < 0 ? 0 : Math.min(Math.max(0, bcur - Math.floor(burstBudget / 2)), maxOff);
2237
2252
  useInput3(
2238
2253
  (input, key) => {
2239
2254
  const step = key.shift ? 10 : 1;
2240
- if (key.upArrow) setFi((n) => (n - 1 + FIELDS2.length) % FIELDS2.length);
2241
- else if (key.downArrow) setFi((n) => (n + 1) % FIELDS2.length);
2255
+ if (key.upArrow) setSel((n) => Math.max(0, n - 1));
2256
+ else if (key.downArrow) setSel((n) => Math.min(total - 1, n + 1));
2257
+ else if (key.pageDown) setSel((n) => Math.min(total - 1, n + burstBudget));
2258
+ else if (key.pageUp) setSel((n) => Math.max(0, n - burstBudget));
2242
2259
  else if (key.leftArrow) adjust(-1, step);
2243
2260
  else if (key.rightArrow) adjust(1, step);
2244
2261
  else if (input === "s") void save();
@@ -2251,23 +2268,15 @@ function RateLimitPanel({ focused }) {
2251
2268
  },
2252
2269
  { isActive: focused }
2253
2270
  );
2254
- const history = (historyQ.data?.history ?? []).slice().sort((a, b) => b.ts - a.ts);
2255
- const lastChange = history[0];
2256
- const peaks = insightsQ.data?.["peaks"] ?? { minute: 0, hour: 0, day: 0 };
2257
- const anomalies2 = (insightsQ.data?.["anomalies"] ?? []).slice().sort((a, b) => b.count - a.count || new Date(b.minute).getTime() - new Date(a.minute).getTime());
2258
2271
  return /* @__PURE__ */ jsx4(DataView, { loading: layersQ.loading && !draft, error: layersQ.error, children: draft ? (() => {
2259
2272
  const lim = draft.perMinute;
2260
2273
  const now = insightsQ.data ? insightsQ.data["activity"]?.minute?.slice(-1)[0]?.count ?? 0 : 0;
2261
2274
  const loadPct = lim > 0 ? Math.min(100, Math.round(now / lim * 100)) : 0;
2262
2275
  const last24h = (insightsQ.data?.["activity"]?.hour ?? []).reduce((s, b) => s + b.count, 0);
2263
- const hasLoad = lim > 0;
2264
- const showExtras = rows >= 20;
2265
- const fixed = 1 + 4 + (showExtras ? (hasLoad ? 1 : 0) + 5 : 0) + 1 + 1 + 1;
2266
- const burstBudget = Math.max(1, rows - fixed);
2267
- const shown = anomalies2.slice(0, burstBudget);
2276
+ const shown = anomalies2.slice(off, off + burstBudget);
2268
2277
  const bw = Math.max(10, cols - 30);
2269
2278
  return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", height: rows, overflow: "hidden", children: [
2270
- /* @__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" }),
2279
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 move (fields + bursts) \xB7 \u2190\u2192 \xB11 \xB7 shift+\u2190\u2192 \xB110 \xB7 s save \xB7 ^R refresh" : "press \u2192 to edit" }),
2271
2280
  /* @__PURE__ */ jsxs4(FieldRow, { label: "Mode", active: focused && fi === 0, children: [
2272
2281
  /* @__PURE__ */ jsx4(Text4, { color: modeColor(draft.mode), bold: true, children: draft.mode }),
2273
2282
  /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: draft.mode === "off" ? " no limit" : draft.mode === "detect" ? " flag bursts, never block" : " DENY calls over the limit" })
@@ -2294,6 +2303,7 @@ function RateLimitPanel({ focused }) {
2294
2303
  /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
2295
2304
  /* @__PURE__ */ jsxs4(Text4, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
2296
2305
  `Recent bursts 7d${anomalies2.length ? ` (${anomalies2.length})` : ""}`,
2306
+ maxOff > 0 ? /* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: ` ${off + 1}-${Math.min(off + burstBudget, anomalies2.length)}/${anomalies2.length}` }) : null,
2297
2307
  /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " over the per-minute limit \xB7 Bypassed = detect (not blocked)" })
2298
2308
  ] }),
2299
2309
  anomalies2.length === 0 ? /* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
@@ -2302,16 +2312,20 @@ function RateLimitPanel({ focused }) {
2302
2312
  ] }) : /* @__PURE__ */ jsx4(
2303
2313
  Table,
2304
2314
  {
2305
- columns: [{ header: "WHEN", width: 13 }, { header: "RESULT", width: 9 }, { header: "CALLS", width: 7 }, { header: "AGENT", width: Math.max(8, bw - 29) }],
2306
- rows: shown.map((a) => [
2307
- { value: when(new Date(a.minute).getTime()), dim: true },
2308
- { value: a.blocked ? "Blocked" : "Bypassed", color: a.blocked ? theme.bad : theme.warn },
2309
- { value: `${a.count}/${a.limit}`, color: theme.warn },
2310
- { value: truncate(a.agent, Math.max(8, bw - 29)), dim: true }
2311
- ])
2315
+ columns: [{ header: "WHEN", width: 15 }, { header: "RESULT", width: 9 }, { header: "CALLS", width: 7 }, { header: "AGENT", width: Math.max(8, bw - 31) }],
2316
+ rows: shown.map((a, i) => {
2317
+ const isSel = off + i === bcur;
2318
+ return [
2319
+ { value: (isSel ? "\u25B8 " : " ") + when(new Date(a.minute).getTime()), color: isSel ? theme.accentBright : void 0, dim: !isSel },
2320
+ { value: a.blocked ? "Blocked" : "Bypassed", color: a.blocked ? theme.bad : theme.warn },
2321
+ { value: `${a.count}/${a.limit}`, color: theme.warn },
2322
+ { value: truncate(a.agent, Math.max(8, bw - 31)), dim: !isSel }
2323
+ ];
2324
+ })
2312
2325
  }
2313
2326
  )
2314
2327
  ] }),
2328
+ /* @__PURE__ */ jsx4(Text4, { children: " " }),
2315
2329
  /* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
2316
2330
  /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "limit now " }),
2317
2331
  /* @__PURE__ */ jsx4(Text4, { children: lim > 0 ? `${lim}/min` : "no per-minute limit" }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.81.95",
3
+ "version": "0.81.97",
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": {