@solongate/proxy 0.82.28 → 0.82.30

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
@@ -8814,6 +8814,7 @@ function PoliciesPanel({ focused }) {
8814
8814
  const activeQ = useLoader(() => api.policies.active());
8815
8815
  const activeId = activeQ.data?.policy?.id ?? null;
8816
8816
  const activeBy = activeQ.data?.matched_by;
8817
+ const { rows: panelRows } = usePanelSize();
8817
8818
  const [view, setView] = useState3("list");
8818
8819
  const [pi, setPi] = useState3(0);
8819
8820
  const [ri, setRi] = useState3(0);
@@ -8905,6 +8906,13 @@ function PoliciesPanel({ focused }) {
8905
8906
  return;
8906
8907
  }
8907
8908
  if (view === "rules") {
8909
+ if (key.ctrl && input === "d") {
8910
+ if (selected) {
8911
+ openBrowser(`${DASHBOARD_URL}/playground?policy=${encodeURIComponent(selected.id)}`);
8912
+ setStatus("Opened the dry-run playground in your browser.");
8913
+ }
8914
+ return;
8915
+ }
8908
8916
  if (key.leftArrow) return setView("list"), void setStatus(null);
8909
8917
  if (key.upArrow) setRi((n) => Math.max(0, n - 1));
8910
8918
  else if (key.downArrow) setRi((n) => Math.min(rules.length - 1, n + 1));
@@ -8933,7 +8941,12 @@ function PoliciesPanel({ focused }) {
8933
8941
  setView("rule");
8934
8942
  } else if (input === "D") {
8935
8943
  setStatus("Dry-running\u2026");
8936
- void api.policies.dryRun({ rules, mode }).then((res) => setStatus(`dry-run ${res.evaluated} calls \xB7 allow ${res.would_allow} / deny ${res.would_deny} \xB7 newly blocked ${res.newly_blocked} \xB7 newly allowed ${res.newly_allowed}`)).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
8944
+ void api.policies.dryRun({ rules, mode }).then((res) => setStatus(
8945
+ `dry-run: replayed ${res.evaluated} recent calls against ${rules.length} rules
8946
+ would allow ${res.would_allow} would deny ${res.would_deny}
8947
+ newly allowed ${res.newly_allowed} newly blocked ${res.newly_blocked} unchanged ${res.unchanged}` + (res.sample_newly_blocked?.length ? `
8948
+ now blocked e.g.: ${res.sample_newly_blocked.slice(0, 3).map((s) => truncate2(s.tool, 24)).join(", ")}` : "")
8949
+ )).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
8937
8950
  } else if (input === "s") void save();
8938
8951
  else if (input === "x") discard();
8939
8952
  return;
@@ -8960,6 +8973,12 @@ function PoliciesPanel({ focused }) {
8960
8973
  );
8961
8974
  if (view === "list") {
8962
8975
  const activeName = activeId ? policies.find((p) => p.id === activeId)?.name ?? activeId : null;
8976
+ const lHead = 3 + (status ? 1 : 0);
8977
+ const lBudget = Math.max(3, panelRows - lHead);
8978
+ const lStart = Math.min(Math.max(0, pi - Math.floor(lBudget / 2)), Math.max(0, policies.length - lBudget));
8979
+ const lWin = policies.slice(lStart, lStart + lBudget);
8980
+ const lAbove = lStart;
8981
+ const lBelow = Math.max(0, policies.length - (lStart + lBudget));
8963
8982
  return /* @__PURE__ */ jsx3(DataView, { loading: list6.loading && !list6.data, error: list6.error, empty: !!list6.data && policies.length === 0, emptyText: "No policies.", children: /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
8964
8983
  /* @__PURE__ */ jsxs3(Box3, { children: [
8965
8984
  /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "active: " }),
@@ -8971,30 +8990,42 @@ function PoliciesPanel({ focused }) {
8971
8990
  /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: activeBy ? ` (${activeBy})` : "" })
8972
8991
  ] }) : /* @__PURE__ */ jsx3(Text3, { color: theme.bad, bold: true, children: "\u25CB NONE \u2014 nothing enforced, grants refused" })
8973
8992
  ] }),
8974
- /* @__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" }),
8975
- /* @__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: [
8976
- (i === pi ? "\u25B8 " : " ") + truncate2(p.name, 26).padEnd(27),
8977
- /* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
8978
- p.mode.padEnd(10),
8979
- " ",
8980
- p.rules.length,
8981
- " rules"
8982
- ] }),
8983
- p.id === activeId ? /* @__PURE__ */ jsx3(Text3, { color: theme.ok, bold: true, children: " \u25CF ACTIVE" }) : null
8984
- ] }, p.id)) }),
8993
+ /* @__PURE__ */ jsx3(Text3, { color: theme.dim, wrap: "truncate", children: focused ? `\u2191\u2193 select \xB7 enter open \xB7 a activate \xB7 x deactivate \xB7 ^R refresh${lAbove ? ` \xB7 \u25B2${lAbove}` : ""}${lBelow ? ` \xB7 \u25BC${lBelow}` : ""}` : "press \u2192 to browse" }),
8994
+ /* @__PURE__ */ jsx3(Box3, { marginTop: 1, flexDirection: "column", children: lWin.map((p, wi) => {
8995
+ const i = lStart + wi;
8996
+ return /* @__PURE__ */ jsxs3(Text3, { color: i === pi ? theme.accentBright : void 0, bold: i === pi, children: [
8997
+ (i === pi ? "\u25B8 " : " ") + truncate2(p.name, 26).padEnd(27),
8998
+ /* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
8999
+ p.mode.padEnd(10),
9000
+ " ",
9001
+ p.rules.length,
9002
+ " rules"
9003
+ ] }),
9004
+ p.id === activeId ? /* @__PURE__ */ jsx3(Text3, { color: theme.ok, bold: true, children: " \u25CF ACTIVE" }) : null
9005
+ ] }, p.id);
9006
+ }) }),
8985
9007
  status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
8986
9008
  ] }) });
8987
9009
  }
8988
9010
  const dirtyTag = dirty ? /* @__PURE__ */ jsx3(Text3, { color: theme.warn, children: " \u25CF unsaved (s save \xB7 x discard)" }) : null;
8989
9011
  if (view === "rules") {
9012
+ const rStatusLines = status ? status.split("\n").length : 0;
9013
+ const rHead = 4 + rStatusLines;
9014
+ const rBudget = Math.max(3, panelRows - rHead);
9015
+ const rMaxStart = Math.max(0, rules.length - rBudget);
9016
+ const rStart = Math.min(Math.max(0, ri - Math.floor(rBudget / 2)), rMaxStart);
9017
+ const rWin = rules.slice(rStart, rStart + rBudget);
9018
+ const rAbove = rStart;
9019
+ const rBelow = Math.max(0, rules.length - (rStart + rBudget));
8990
9020
  return /* @__PURE__ */ jsx3(DataView, { loading: detail.loading && !detail.data, error: detail.error, children: /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
8991
9021
  /* @__PURE__ */ jsxs3(Box3, { children: [
8992
9022
  /* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.accentBright, children: truncate2(selected?.name ?? "", 28) }),
8993
9023
  /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: " mode: " }),
8994
9024
  /* @__PURE__ */ jsx3(Text3, { color: mode === "whitelist" ? theme.warn : void 0, children: mode }),
9025
+ /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: ` ${rules.length} rules` }),
8995
9026
  dirtyTag
8996
9027
  ] }),
8997
- /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "\u2191\u2193 \xB7 enter edit \xB7 space on/off \xB7 e effect \xB7 n new \xB7 d del \xB7 m mode \xB7 D dry-run \xB7 s save \xB7 \u2190 back" }),
9028
+ /* @__PURE__ */ jsx3(Text3, { color: theme.dim, wrap: "truncate", children: `\u2191\u2193 \xB7 enter edit \xB7 space on/off \xB7 e effect \xB7 n new \xB7 d del \xB7 m mode \xB7 D dry-run \xB7 ^D playground \xB7 s save \xB7 \u2190 back${rAbove ? ` \xB7 \u25B2${rAbove}` : ""}${rBelow ? ` \xB7 \u25BC${rBelow}` : ""}` }),
8998
9029
  /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(
8999
9030
  Table,
9000
9031
  {
@@ -9007,15 +9038,18 @@ function PoliciesPanel({ focused }) {
9007
9038
  { header: "DESCRIPTION", width: 30 },
9008
9039
  { header: "CONSTRAINTS", width: 14 }
9009
9040
  ],
9010
- rows: rules.map((r, i) => [
9011
- { value: i === ri ? "\u25B8" : "", color: theme.accentBright },
9012
- { value: r.enabled ? "\u25CF" : "\u25CB", color: r.enabled ? theme.ok : theme.dim },
9013
- { value: r.effect, color: r.effect === "ALLOW" ? theme.ok : theme.bad, dim: !r.enabled },
9014
- { value: String(r.priority), dim: true },
9015
- { value: truncate2(r.toolPattern, 12), color: theme.accent, dim: !r.enabled },
9016
- { value: truncate2(r.description || "-", 30), dim: !r.enabled },
9017
- { value: ruleSummary(r) || "-", dim: true }
9018
- ])
9041
+ rows: rWin.map((r, wi) => {
9042
+ const i = rStart + wi;
9043
+ return [
9044
+ { value: i === ri ? "\u25B8" : "", color: theme.accentBright },
9045
+ { value: r.enabled ? "\u25CF" : "\u25CB", color: r.enabled ? theme.ok : theme.dim },
9046
+ { value: r.effect, color: r.effect === "ALLOW" ? theme.ok : theme.bad, dim: !r.enabled },
9047
+ { value: String(r.priority), dim: true },
9048
+ { value: truncate2(r.toolPattern, 12), color: theme.accent, dim: !r.enabled },
9049
+ { value: truncate2(r.description || "-", 30), dim: !r.enabled },
9050
+ { value: ruleSummary(r) || "-", dim: true }
9051
+ ];
9052
+ })
9019
9053
  }
9020
9054
  ) }),
9021
9055
  rules.length === 0 ? /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "(no rules)" }) : null,
@@ -9058,14 +9092,16 @@ function PoliciesPanel({ focused }) {
9058
9092
  status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
9059
9093
  ] });
9060
9094
  }
9061
- var constr, setConstr, FIELDS;
9095
+ var DASHBOARD_URL, constr, setConstr, FIELDS;
9062
9096
  var init_Policies = __esm({
9063
9097
  "src/tui/panels/Policies.tsx"() {
9064
9098
  "use strict";
9065
9099
  init_api_client();
9100
+ init_device_login();
9066
9101
  init_components();
9067
9102
  init_hooks();
9068
9103
  init_theme();
9104
+ DASHBOARD_URL = "https://dashboard.solongate.com";
9069
9105
  constr = (r, g, side) => (r[g]?.[side] ?? []).join(", ");
9070
9106
  setConstr = (r, g, side, val) => {
9071
9107
  const arr = val.split(",").map((s) => s.trim()).filter(Boolean);
package/dist/tui/index.js CHANGED
@@ -1903,6 +1903,7 @@ import { Box as Box3, Text as Text3, useInput as useInput2 } from "ink";
1903
1903
  import TextInput2 from "ink-text-input";
1904
1904
  import { useEffect as useEffect3, useState as useState3 } from "react";
1905
1905
  import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1906
+ var DASHBOARD_URL = "https://dashboard.solongate.com";
1906
1907
  var constr = (r, g, side) => (r[g]?.[side] ?? []).join(", ");
1907
1908
  var setConstr = (r, g, side, val) => {
1908
1909
  const arr = val.split(",").map((s) => s.trim()).filter(Boolean);
@@ -1939,6 +1940,7 @@ function PoliciesPanel({ focused }) {
1939
1940
  const activeQ = useLoader(() => api.policies.active());
1940
1941
  const activeId = activeQ.data?.policy?.id ?? null;
1941
1942
  const activeBy = activeQ.data?.matched_by;
1943
+ const { rows: panelRows } = usePanelSize();
1942
1944
  const [view, setView] = useState3("list");
1943
1945
  const [pi, setPi] = useState3(0);
1944
1946
  const [ri, setRi] = useState3(0);
@@ -2030,6 +2032,13 @@ function PoliciesPanel({ focused }) {
2030
2032
  return;
2031
2033
  }
2032
2034
  if (view === "rules") {
2035
+ if (key.ctrl && input === "d") {
2036
+ if (selected) {
2037
+ openBrowser(`${DASHBOARD_URL}/playground?policy=${encodeURIComponent(selected.id)}`);
2038
+ setStatus("Opened the dry-run playground in your browser.");
2039
+ }
2040
+ return;
2041
+ }
2033
2042
  if (key.leftArrow) return setView("list"), void setStatus(null);
2034
2043
  if (key.upArrow) setRi((n) => Math.max(0, n - 1));
2035
2044
  else if (key.downArrow) setRi((n) => Math.min(rules.length - 1, n + 1));
@@ -2058,7 +2067,12 @@ function PoliciesPanel({ focused }) {
2058
2067
  setView("rule");
2059
2068
  } else if (input === "D") {
2060
2069
  setStatus("Dry-running\u2026");
2061
- void api.policies.dryRun({ rules, mode }).then((res) => setStatus(`dry-run ${res.evaluated} calls \xB7 allow ${res.would_allow} / deny ${res.would_deny} \xB7 newly blocked ${res.newly_blocked} \xB7 newly allowed ${res.newly_allowed}`)).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
2070
+ void api.policies.dryRun({ rules, mode }).then((res) => setStatus(
2071
+ `dry-run: replayed ${res.evaluated} recent calls against ${rules.length} rules
2072
+ would allow ${res.would_allow} would deny ${res.would_deny}
2073
+ newly allowed ${res.newly_allowed} newly blocked ${res.newly_blocked} unchanged ${res.unchanged}` + (res.sample_newly_blocked?.length ? `
2074
+ now blocked e.g.: ${res.sample_newly_blocked.slice(0, 3).map((s) => truncate(s.tool, 24)).join(", ")}` : "")
2075
+ )).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
2062
2076
  } else if (input === "s") void save();
2063
2077
  else if (input === "x") discard();
2064
2078
  return;
@@ -2085,6 +2099,12 @@ function PoliciesPanel({ focused }) {
2085
2099
  );
2086
2100
  if (view === "list") {
2087
2101
  const activeName = activeId ? policies.find((p) => p.id === activeId)?.name ?? activeId : null;
2102
+ const lHead = 3 + (status ? 1 : 0);
2103
+ const lBudget = Math.max(3, panelRows - lHead);
2104
+ const lStart = Math.min(Math.max(0, pi - Math.floor(lBudget / 2)), Math.max(0, policies.length - lBudget));
2105
+ const lWin = policies.slice(lStart, lStart + lBudget);
2106
+ const lAbove = lStart;
2107
+ const lBelow = Math.max(0, policies.length - (lStart + lBudget));
2088
2108
  return /* @__PURE__ */ jsx3(DataView, { loading: list5.loading && !list5.data, error: list5.error, empty: !!list5.data && policies.length === 0, emptyText: "No policies.", children: /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
2089
2109
  /* @__PURE__ */ jsxs3(Box3, { children: [
2090
2110
  /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "active: " }),
@@ -2096,30 +2116,42 @@ function PoliciesPanel({ focused }) {
2096
2116
  /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: activeBy ? ` (${activeBy})` : "" })
2097
2117
  ] }) : /* @__PURE__ */ jsx3(Text3, { color: theme.bad, bold: true, children: "\u25CB NONE \u2014 nothing enforced, grants refused" })
2098
2118
  ] }),
2099
- /* @__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" }),
2100
- /* @__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: [
2101
- (i === pi ? "\u25B8 " : " ") + truncate(p.name, 26).padEnd(27),
2102
- /* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
2103
- p.mode.padEnd(10),
2104
- " ",
2105
- p.rules.length,
2106
- " rules"
2107
- ] }),
2108
- p.id === activeId ? /* @__PURE__ */ jsx3(Text3, { color: theme.ok, bold: true, children: " \u25CF ACTIVE" }) : null
2109
- ] }, p.id)) }),
2119
+ /* @__PURE__ */ jsx3(Text3, { color: theme.dim, wrap: "truncate", children: focused ? `\u2191\u2193 select \xB7 enter open \xB7 a activate \xB7 x deactivate \xB7 ^R refresh${lAbove ? ` \xB7 \u25B2${lAbove}` : ""}${lBelow ? ` \xB7 \u25BC${lBelow}` : ""}` : "press \u2192 to browse" }),
2120
+ /* @__PURE__ */ jsx3(Box3, { marginTop: 1, flexDirection: "column", children: lWin.map((p, wi) => {
2121
+ const i = lStart + wi;
2122
+ return /* @__PURE__ */ jsxs3(Text3, { color: i === pi ? theme.accentBright : void 0, bold: i === pi, children: [
2123
+ (i === pi ? "\u25B8 " : " ") + truncate(p.name, 26).padEnd(27),
2124
+ /* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
2125
+ p.mode.padEnd(10),
2126
+ " ",
2127
+ p.rules.length,
2128
+ " rules"
2129
+ ] }),
2130
+ p.id === activeId ? /* @__PURE__ */ jsx3(Text3, { color: theme.ok, bold: true, children: " \u25CF ACTIVE" }) : null
2131
+ ] }, p.id);
2132
+ }) }),
2110
2133
  status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
2111
2134
  ] }) });
2112
2135
  }
2113
2136
  const dirtyTag = dirty ? /* @__PURE__ */ jsx3(Text3, { color: theme.warn, children: " \u25CF unsaved (s save \xB7 x discard)" }) : null;
2114
2137
  if (view === "rules") {
2138
+ const rStatusLines = status ? status.split("\n").length : 0;
2139
+ const rHead = 4 + rStatusLines;
2140
+ const rBudget = Math.max(3, panelRows - rHead);
2141
+ const rMaxStart = Math.max(0, rules.length - rBudget);
2142
+ const rStart = Math.min(Math.max(0, ri - Math.floor(rBudget / 2)), rMaxStart);
2143
+ const rWin = rules.slice(rStart, rStart + rBudget);
2144
+ const rAbove = rStart;
2145
+ const rBelow = Math.max(0, rules.length - (rStart + rBudget));
2115
2146
  return /* @__PURE__ */ jsx3(DataView, { loading: detail.loading && !detail.data, error: detail.error, children: /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
2116
2147
  /* @__PURE__ */ jsxs3(Box3, { children: [
2117
2148
  /* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.accentBright, children: truncate(selected?.name ?? "", 28) }),
2118
2149
  /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: " mode: " }),
2119
2150
  /* @__PURE__ */ jsx3(Text3, { color: mode === "whitelist" ? theme.warn : void 0, children: mode }),
2151
+ /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: ` ${rules.length} rules` }),
2120
2152
  dirtyTag
2121
2153
  ] }),
2122
- /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "\u2191\u2193 \xB7 enter edit \xB7 space on/off \xB7 e effect \xB7 n new \xB7 d del \xB7 m mode \xB7 D dry-run \xB7 s save \xB7 \u2190 back" }),
2154
+ /* @__PURE__ */ jsx3(Text3, { color: theme.dim, wrap: "truncate", children: `\u2191\u2193 \xB7 enter edit \xB7 space on/off \xB7 e effect \xB7 n new \xB7 d del \xB7 m mode \xB7 D dry-run \xB7 ^D playground \xB7 s save \xB7 \u2190 back${rAbove ? ` \xB7 \u25B2${rAbove}` : ""}${rBelow ? ` \xB7 \u25BC${rBelow}` : ""}` }),
2123
2155
  /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(
2124
2156
  Table,
2125
2157
  {
@@ -2132,15 +2164,18 @@ function PoliciesPanel({ focused }) {
2132
2164
  { header: "DESCRIPTION", width: 30 },
2133
2165
  { header: "CONSTRAINTS", width: 14 }
2134
2166
  ],
2135
- rows: rules.map((r, i) => [
2136
- { value: i === ri ? "\u25B8" : "", color: theme.accentBright },
2137
- { value: r.enabled ? "\u25CF" : "\u25CB", color: r.enabled ? theme.ok : theme.dim },
2138
- { value: r.effect, color: r.effect === "ALLOW" ? theme.ok : theme.bad, dim: !r.enabled },
2139
- { value: String(r.priority), dim: true },
2140
- { value: truncate(r.toolPattern, 12), color: theme.accent, dim: !r.enabled },
2141
- { value: truncate(r.description || "-", 30), dim: !r.enabled },
2142
- { value: ruleSummary(r) || "-", dim: true }
2143
- ])
2167
+ rows: rWin.map((r, wi) => {
2168
+ const i = rStart + wi;
2169
+ return [
2170
+ { value: i === ri ? "\u25B8" : "", color: theme.accentBright },
2171
+ { value: r.enabled ? "\u25CF" : "\u25CB", color: r.enabled ? theme.ok : theme.dim },
2172
+ { value: r.effect, color: r.effect === "ALLOW" ? theme.ok : theme.bad, dim: !r.enabled },
2173
+ { value: String(r.priority), dim: true },
2174
+ { value: truncate(r.toolPattern, 12), color: theme.accent, dim: !r.enabled },
2175
+ { value: truncate(r.description || "-", 30), dim: !r.enabled },
2176
+ { value: ruleSummary(r) || "-", dim: true }
2177
+ ];
2178
+ })
2144
2179
  }
2145
2180
  ) }),
2146
2181
  rules.length === 0 ? /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "(no rules)" }) : null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.82.28",
3
+ "version": "0.82.30",
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": {