@solongate/proxy 0.74.0 → 0.75.0
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 +59 -45
- package/dist/tui/index.js +59 -45
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7030,6 +7030,7 @@ var init_hooks = __esm({
|
|
|
7030
7030
|
|
|
7031
7031
|
// src/tui/panels/Live.tsx
|
|
7032
7032
|
import { Box as Box2, Text as Text2, useInput } from "ink";
|
|
7033
|
+
import TextInput from "ink-text-input";
|
|
7033
7034
|
import { closeSync, openSync, readSync, statSync } from "fs";
|
|
7034
7035
|
import { homedir as homedir3 } from "os";
|
|
7035
7036
|
import { join as join5 } from "path";
|
|
@@ -7135,7 +7136,6 @@ function StreamLine({ e, loc }) {
|
|
|
7135
7136
|
}
|
|
7136
7137
|
function LivePanel({ active: active2 }) {
|
|
7137
7138
|
const [s, setS] = useState2(null);
|
|
7138
|
-
const [tsData, setTsData] = useState2(null);
|
|
7139
7139
|
const [lat, setLat] = useState2([]);
|
|
7140
7140
|
const [cloudBuf, setCloudBuf] = useState2([]);
|
|
7141
7141
|
const [localBuf, setLocalBuf] = useState2([]);
|
|
@@ -7143,6 +7143,8 @@ function LivePanel({ active: active2 }) {
|
|
|
7143
7143
|
const [ring, setRing] = useState2(null);
|
|
7144
7144
|
const [log6, setLog] = useState2([]);
|
|
7145
7145
|
const [filter, setFilter] = useState2("all");
|
|
7146
|
+
const [search, setSearch] = useState2("");
|
|
7147
|
+
const [editingSearch, setEditingSearch] = useState2(false);
|
|
7146
7148
|
const [scroll, setScroll] = useState2(0);
|
|
7147
7149
|
const [mode, setMode] = useState2("stream");
|
|
7148
7150
|
const [pickIdx, setPickIdx] = useState2(0);
|
|
@@ -7262,38 +7264,26 @@ function LivePanel({ active: active2 }) {
|
|
|
7262
7264
|
onApiError(e);
|
|
7263
7265
|
}
|
|
7264
7266
|
}, [onApiError]);
|
|
7265
|
-
const pollSlow = useCallback2(async () => {
|
|
7266
|
-
if (paused()) return;
|
|
7267
|
-
try {
|
|
7268
|
-
const td = await api.stats.timeseries({ period: "24h" });
|
|
7269
|
-
if (!frozenRef.current) setTsData(td);
|
|
7270
|
-
} catch (e) {
|
|
7271
|
-
onApiError(e);
|
|
7272
|
-
}
|
|
7273
|
-
}, [onApiError]);
|
|
7274
7267
|
useEffect2(() => {
|
|
7275
7268
|
if (!active2) return;
|
|
7276
7269
|
void pollFeed();
|
|
7277
7270
|
void pollMedium();
|
|
7278
|
-
void
|
|
7279
|
-
const
|
|
7280
|
-
const t2 = setInterval(() => void pollMedium(), 1e4);
|
|
7281
|
-
const t3 = setInterval(() => void pollSlow(), 3e4);
|
|
7271
|
+
const t1 = setInterval(() => void pollFeed(), 3e3);
|
|
7272
|
+
const t2 = setInterval(() => void pollMedium(), 8e3);
|
|
7282
7273
|
return () => {
|
|
7283
7274
|
clearInterval(t1);
|
|
7284
7275
|
clearInterval(t2);
|
|
7285
|
-
clearInterval(t3);
|
|
7286
7276
|
};
|
|
7287
|
-
}, [active2, pollFeed, pollMedium
|
|
7277
|
+
}, [active2, pollFeed, pollMedium]);
|
|
7288
7278
|
const sessions = useLoader(() => api.agents.live({ limit: 30, includeDeactivated: true }));
|
|
7289
7279
|
const insights = useLoader(() => api.stats.securityInsights(7));
|
|
7290
7280
|
const guard = useLoader(() => api.settings.getGuardStatus());
|
|
7291
7281
|
usePoll(() => {
|
|
7292
7282
|
if (!paused()) sessions.reload();
|
|
7293
|
-
},
|
|
7283
|
+
}, 8e3, active2);
|
|
7294
7284
|
usePoll(() => {
|
|
7295
7285
|
if (!paused()) insights.reload();
|
|
7296
|
-
},
|
|
7286
|
+
}, 2e4, active2);
|
|
7297
7287
|
usePoll(() => {
|
|
7298
7288
|
if (!paused()) guard.reload();
|
|
7299
7289
|
}, 6e4, active2);
|
|
@@ -7329,21 +7319,17 @@ function LivePanel({ active: active2 }) {
|
|
|
7329
7319
|
}
|
|
7330
7320
|
const cloudDeduped = cloudBuf.filter((e) => !localKeys.has(`${e.tool}|${e.decision}|${Math.round(e.at / 6e4)}`));
|
|
7331
7321
|
const mergedAll = [...cloudDeduped, ...localBuf].sort((a, b) => a.at - b.at);
|
|
7332
|
-
const points = tsData?.timeseries ?? [];
|
|
7333
7322
|
const nowSlot = Math.floor(nowMs / 1e4);
|
|
7334
|
-
const
|
|
7335
|
-
const
|
|
7323
|
+
const traffic = new Array(60).fill(0);
|
|
7324
|
+
const trafficHot = new Array(60).fill(false);
|
|
7336
7325
|
for (const e of mergedAll) {
|
|
7337
7326
|
const idx = 59 - (nowSlot - Math.floor(e.at / 1e4));
|
|
7338
7327
|
if (idx >= 0 && idx < 60) {
|
|
7339
|
-
|
|
7340
|
-
if (e.decision !== "ALLOW")
|
|
7328
|
+
traffic[idx]++;
|
|
7329
|
+
if (e.decision !== "ALLOW") trafficHot[idx] = true;
|
|
7341
7330
|
}
|
|
7342
7331
|
}
|
|
7343
|
-
const
|
|
7344
|
-
const traffic = localTraffic ? slotCounts : points.map((p) => p.total);
|
|
7345
|
-
const trafficHot = localTraffic ? slotHot : points.map((p) => p.denied > 0);
|
|
7346
|
-
const trafficLabel = localTraffic ? "calls/10s \xB7 last 10m" : "24h \xB7 cloud";
|
|
7332
|
+
const trafficLabel = "calls/10s \xB7 last 10m \xB7 live";
|
|
7347
7333
|
const rl = ins.layers?.rateLimit;
|
|
7348
7334
|
const dl = ins.layers?.dlp;
|
|
7349
7335
|
const gh = ins.layers?.ghost;
|
|
@@ -7388,7 +7374,9 @@ function LivePanel({ active: active2 }) {
|
|
|
7388
7374
|
const localSessIds = new Set(localSess.keys());
|
|
7389
7375
|
if (ring?.session) localSessIds.add(ring.session);
|
|
7390
7376
|
const isLoc = (e) => e.source === "local" || !!e.session && localSessIds.has(e.session);
|
|
7391
|
-
const
|
|
7377
|
+
const q = search.trim().toLowerCase();
|
|
7378
|
+
const matches = (e) => !q || `${e.tool} ${e.agent ?? ""} ${e.decision} ${e.permission} ${e.rule ?? ""} ${e.detail}`.toLowerCase().includes(q);
|
|
7379
|
+
const filtered = (filter === "all" ? mergedAll : mergedAll.filter((e) => filter === "local" ? isLoc(e) : !isLoc(e))).filter(matches);
|
|
7392
7380
|
const visibleDesc = filtered.slice(-STREAM_KEEP).reverse();
|
|
7393
7381
|
const chartH = rows >= 36 ? 6 : 4;
|
|
7394
7382
|
const colH = rows >= 32 ? 7 : 5;
|
|
@@ -7408,12 +7396,21 @@ function LivePanel({ active: active2 }) {
|
|
|
7408
7396
|
const key = `${e.at}:${e.tool}:${e.decision}`;
|
|
7409
7397
|
if (seen.has(key)) continue;
|
|
7410
7398
|
seen.add(key);
|
|
7411
|
-
all.push(e);
|
|
7399
|
+
if (matches(e)) all.push(e);
|
|
7412
7400
|
}
|
|
7413
7401
|
return all.sort((a, b) => b.at - a.at);
|
|
7414
7402
|
})() : [];
|
|
7415
7403
|
useInput(
|
|
7416
7404
|
(input, key) => {
|
|
7405
|
+
if (input === " ") {
|
|
7406
|
+
setFrozen((f) => !f);
|
|
7407
|
+
return;
|
|
7408
|
+
}
|
|
7409
|
+
if (frozen) return;
|
|
7410
|
+
if (input === "/" && mode !== "pick") {
|
|
7411
|
+
setEditingSearch(true);
|
|
7412
|
+
return;
|
|
7413
|
+
}
|
|
7417
7414
|
if (mode === "detail") {
|
|
7418
7415
|
const maxD = Math.max(0, detailEntries.length - (rows - 10));
|
|
7419
7416
|
if (key.leftArrow) {
|
|
@@ -7437,11 +7434,6 @@ function LivePanel({ active: active2 }) {
|
|
|
7437
7434
|
} else if (input === "s" || key.leftArrow) setMode("stream");
|
|
7438
7435
|
return;
|
|
7439
7436
|
}
|
|
7440
|
-
if (input === " ") {
|
|
7441
|
-
setFrozen((f) => !f);
|
|
7442
|
-
return;
|
|
7443
|
-
}
|
|
7444
|
-
if (frozen) return;
|
|
7445
7437
|
if (input === "f") {
|
|
7446
7438
|
setFilter((f) => FILTERS[(FILTERS.indexOf(f) + 1) % FILTERS.length]);
|
|
7447
7439
|
setScroll(0);
|
|
@@ -7453,8 +7445,28 @@ function LivePanel({ active: active2 }) {
|
|
|
7453
7445
|
else if (key.pageDown) setScroll((n) => Math.min(maxScroll, n + streamRows));
|
|
7454
7446
|
else if (key.pageUp) setScroll((n) => Math.max(0, n - streamRows));
|
|
7455
7447
|
},
|
|
7456
|
-
{ isActive: active2 }
|
|
7448
|
+
{ isActive: active2 && !editingSearch }
|
|
7457
7449
|
);
|
|
7450
|
+
const searchRow = editingSearch ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7451
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: "search: " }),
|
|
7452
|
+
/* @__PURE__ */ jsx2(
|
|
7453
|
+
TextInput,
|
|
7454
|
+
{
|
|
7455
|
+
value: search,
|
|
7456
|
+
onChange: (v) => {
|
|
7457
|
+
setSearch(v);
|
|
7458
|
+
setScroll(0);
|
|
7459
|
+
setDetailScroll(0);
|
|
7460
|
+
},
|
|
7461
|
+
onSubmit: () => setEditingSearch(false)
|
|
7462
|
+
}
|
|
7463
|
+
),
|
|
7464
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " (enter done \xB7 empty = clear)" })
|
|
7465
|
+
] }) : search ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7466
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: "search: " }),
|
|
7467
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, bold: true, children: search }),
|
|
7468
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ` ${filtered.length} match \xB7 / edit` })
|
|
7469
|
+
] }) : null;
|
|
7458
7470
|
const sessionDot = (st) => st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\u25CC", color: theme.warn } : { ch: "\u25CB", color: theme.dim };
|
|
7459
7471
|
void sessionDot;
|
|
7460
7472
|
if (s === null && localOn === null) {
|
|
@@ -7522,7 +7534,8 @@ function LivePanel({ active: active2 }) {
|
|
|
7522
7534
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: perms.map(([p, c2]) => `${p}\xD7${c2}`).join(" ") || "\u2014" }),
|
|
7523
7535
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
7524
7536
|
] }),
|
|
7525
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "TIMELINE", extra: `${d.length} calls \xB7 newest first${dScroll ? ` \xB7 \u25BC${dScroll} older` : ""} \xB7 \u2191\u2193 scroll \xB7 \u2190 back`, width: innerW }),
|
|
7537
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "TIMELINE", extra: `${d.length} calls \xB7 newest first${dScroll ? ` \xB7 \u25BC${dScroll} older` : ""} \xB7 \u2191\u2193 scroll \xB7 / search \xB7 \u2190 back`, width: innerW }),
|
|
7538
|
+
searchRow,
|
|
7526
7539
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: tlRows, overflow: "hidden", children: [
|
|
7527
7540
|
tl.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: detailCloud.length === 0 ? spin + " loading history\u2026" : "no entries" }) : null,
|
|
7528
7541
|
tl.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e) }, e.id))
|
|
@@ -7631,11 +7644,12 @@ function LivePanel({ active: active2 }) {
|
|
|
7631
7644
|
PaneTitle,
|
|
7632
7645
|
{
|
|
7633
7646
|
label: "TOOL STREAM",
|
|
7634
|
-
extra: `newest first \xB7 last ${STREAM_KEEP} \xB7 filter ${filter}${clampedScroll > 0 ? ` \xB7 \u25BC${clampedScroll} older` : ""} \xB7 f filter \xB7 \u2191\u2193 scroll \xB7 s sessions`,
|
|
7647
|
+
extra: `newest first \xB7 last ${STREAM_KEEP} \xB7 filter ${filter}${clampedScroll > 0 ? ` \xB7 \u25BC${clampedScroll} older` : ""} \xB7 f filter \xB7 / search \xB7 \u2191\u2193 scroll \xB7 s sessions`,
|
|
7635
7648
|
width: innerW
|
|
7636
7649
|
}
|
|
7637
7650
|
),
|
|
7638
7651
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: streamRows, overflow: "hidden", children: [
|
|
7652
|
+
searchRow,
|
|
7639
7653
|
localOn === false ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7640
7654
|
/* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: "local logs off" }),
|
|
7641
7655
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2014 enable: dashboard \u2192 Settings \u2192 Local logs \xB7 hooks write ~/.solongate/local-logs \xB7 view: `solongate logs-server`" })
|
|
@@ -7649,7 +7663,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7649
7663
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7650
7664
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " LIVE " }),
|
|
7651
7665
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` local ${localOn ? "on" : "off"} \xB7 ${localBuf.length} loc/${cloudBuf.length} cld \xB7 top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} ` }),
|
|
7652
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " space copy-mode \xB7 f filter \xB7 s sessions \xB7 esc menu \xB7 q quit " })
|
|
7666
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " space copy-mode \xB7 / search \xB7 f filter \xB7 s sessions \xB7 esc menu \xB7 q quit " })
|
|
7653
7667
|
] })
|
|
7654
7668
|
] });
|
|
7655
7669
|
}
|
|
@@ -7681,7 +7695,7 @@ var init_Live = __esm({
|
|
|
7681
7695
|
|
|
7682
7696
|
// src/tui/panels/Policies.tsx
|
|
7683
7697
|
import { Box as Box3, Text as Text3, useInput as useInput2 } from "ink";
|
|
7684
|
-
import
|
|
7698
|
+
import TextInput2 from "ink-text-input";
|
|
7685
7699
|
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
7686
7700
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
7687
7701
|
function ruleSummary(r) {
|
|
@@ -7870,7 +7884,7 @@ function PoliciesPanel({ focused }) {
|
|
|
7870
7884
|
return /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
7871
7885
|
/* @__PURE__ */ jsx3(Text3, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + f.label.padEnd(13) }),
|
|
7872
7886
|
active2 && editing && f.kind === "text" ? /* @__PURE__ */ jsx3(
|
|
7873
|
-
|
|
7887
|
+
TextInput2,
|
|
7874
7888
|
{
|
|
7875
7889
|
value: editVal,
|
|
7876
7890
|
onChange: setEditVal,
|
|
@@ -8066,7 +8080,7 @@ var init_RateLimit = __esm({
|
|
|
8066
8080
|
|
|
8067
8081
|
// src/tui/panels/Dlp.tsx
|
|
8068
8082
|
import { Box as Box5, Text as Text5, useInput as useInput4 } from "ink";
|
|
8069
|
-
import
|
|
8083
|
+
import TextInput3 from "ink-text-input";
|
|
8070
8084
|
import { useEffect as useEffect5, useState as useState5 } from "react";
|
|
8071
8085
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
8072
8086
|
function DlpPanel({ focused }) {
|
|
@@ -8168,7 +8182,7 @@ function DlpPanel({ focused }) {
|
|
|
8168
8182
|
adding ? /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, children: [
|
|
8169
8183
|
/* @__PURE__ */ jsx5(Text5, { color: theme.warn, children: adding === "name" ? "New pattern name: " : `Regex for "${newName}": ` }),
|
|
8170
8184
|
adding === "name" ? /* @__PURE__ */ jsx5(
|
|
8171
|
-
|
|
8185
|
+
TextInput3,
|
|
8172
8186
|
{
|
|
8173
8187
|
value: newName,
|
|
8174
8188
|
onChange: setNewName,
|
|
@@ -8179,7 +8193,7 @@ function DlpPanel({ focused }) {
|
|
|
8179
8193
|
}
|
|
8180
8194
|
}
|
|
8181
8195
|
) : /* @__PURE__ */ jsx5(
|
|
8182
|
-
|
|
8196
|
+
TextInput3,
|
|
8183
8197
|
{
|
|
8184
8198
|
value: newRe,
|
|
8185
8199
|
onChange: setNewRe,
|
|
@@ -8292,7 +8306,7 @@ var init_Stats = __esm({
|
|
|
8292
8306
|
|
|
8293
8307
|
// src/tui/panels/Audit.tsx
|
|
8294
8308
|
import { Box as Box7, Text as Text7, useInput as useInput5 } from "ink";
|
|
8295
|
-
import
|
|
8309
|
+
import TextInput4 from "ink-text-input";
|
|
8296
8310
|
import { useState as useState6 } from "react";
|
|
8297
8311
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
8298
8312
|
function AuditPanel({ active: active2, focused }) {
|
|
@@ -8409,7 +8423,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
8409
8423
|
": "
|
|
8410
8424
|
] }),
|
|
8411
8425
|
/* @__PURE__ */ jsx7(
|
|
8412
|
-
|
|
8426
|
+
TextInput4,
|
|
8413
8427
|
{
|
|
8414
8428
|
value: editing === "tool" ? tool : editing === "agent" ? agent : search,
|
|
8415
8429
|
onChange: editing === "tool" ? setTool : editing === "agent" ? setAgent : setSearch,
|
package/dist/tui/index.js
CHANGED
|
@@ -126,6 +126,7 @@ function KeyHints({ hints }) {
|
|
|
126
126
|
|
|
127
127
|
// src/tui/panels/Live.tsx
|
|
128
128
|
import { Box as Box2, Text as Text2, useInput } from "ink";
|
|
129
|
+
import TextInput from "ink-text-input";
|
|
129
130
|
import { closeSync, openSync, readSync, statSync } from "fs";
|
|
130
131
|
import { homedir as homedir2 } from "os";
|
|
131
132
|
import { join as join2 } from "path";
|
|
@@ -560,7 +561,6 @@ function StreamLine({ e, loc }) {
|
|
|
560
561
|
var FILTERS = ["all", "local", "cloud"];
|
|
561
562
|
function LivePanel({ active: active2 }) {
|
|
562
563
|
const [s, setS] = useState2(null);
|
|
563
|
-
const [tsData, setTsData] = useState2(null);
|
|
564
564
|
const [lat, setLat] = useState2([]);
|
|
565
565
|
const [cloudBuf, setCloudBuf] = useState2([]);
|
|
566
566
|
const [localBuf, setLocalBuf] = useState2([]);
|
|
@@ -568,6 +568,8 @@ function LivePanel({ active: active2 }) {
|
|
|
568
568
|
const [ring, setRing] = useState2(null);
|
|
569
569
|
const [log, setLog] = useState2([]);
|
|
570
570
|
const [filter, setFilter] = useState2("all");
|
|
571
|
+
const [search, setSearch] = useState2("");
|
|
572
|
+
const [editingSearch, setEditingSearch] = useState2(false);
|
|
571
573
|
const [scroll, setScroll] = useState2(0);
|
|
572
574
|
const [mode, setMode] = useState2("stream");
|
|
573
575
|
const [pickIdx, setPickIdx] = useState2(0);
|
|
@@ -687,38 +689,26 @@ function LivePanel({ active: active2 }) {
|
|
|
687
689
|
onApiError(e);
|
|
688
690
|
}
|
|
689
691
|
}, [onApiError]);
|
|
690
|
-
const pollSlow = useCallback2(async () => {
|
|
691
|
-
if (paused()) return;
|
|
692
|
-
try {
|
|
693
|
-
const td = await api.stats.timeseries({ period: "24h" });
|
|
694
|
-
if (!frozenRef.current) setTsData(td);
|
|
695
|
-
} catch (e) {
|
|
696
|
-
onApiError(e);
|
|
697
|
-
}
|
|
698
|
-
}, [onApiError]);
|
|
699
692
|
useEffect2(() => {
|
|
700
693
|
if (!active2) return;
|
|
701
694
|
void pollFeed();
|
|
702
695
|
void pollMedium();
|
|
703
|
-
void
|
|
704
|
-
const
|
|
705
|
-
const t2 = setInterval(() => void pollMedium(), 1e4);
|
|
706
|
-
const t3 = setInterval(() => void pollSlow(), 3e4);
|
|
696
|
+
const t1 = setInterval(() => void pollFeed(), 3e3);
|
|
697
|
+
const t2 = setInterval(() => void pollMedium(), 8e3);
|
|
707
698
|
return () => {
|
|
708
699
|
clearInterval(t1);
|
|
709
700
|
clearInterval(t2);
|
|
710
|
-
clearInterval(t3);
|
|
711
701
|
};
|
|
712
|
-
}, [active2, pollFeed, pollMedium
|
|
702
|
+
}, [active2, pollFeed, pollMedium]);
|
|
713
703
|
const sessions = useLoader(() => api.agents.live({ limit: 30, includeDeactivated: true }));
|
|
714
704
|
const insights = useLoader(() => api.stats.securityInsights(7));
|
|
715
705
|
const guard = useLoader(() => api.settings.getGuardStatus());
|
|
716
706
|
usePoll(() => {
|
|
717
707
|
if (!paused()) sessions.reload();
|
|
718
|
-
},
|
|
708
|
+
}, 8e3, active2);
|
|
719
709
|
usePoll(() => {
|
|
720
710
|
if (!paused()) insights.reload();
|
|
721
|
-
},
|
|
711
|
+
}, 2e4, active2);
|
|
722
712
|
usePoll(() => {
|
|
723
713
|
if (!paused()) guard.reload();
|
|
724
714
|
}, 6e4, active2);
|
|
@@ -754,21 +744,17 @@ function LivePanel({ active: active2 }) {
|
|
|
754
744
|
}
|
|
755
745
|
const cloudDeduped = cloudBuf.filter((e) => !localKeys.has(`${e.tool}|${e.decision}|${Math.round(e.at / 6e4)}`));
|
|
756
746
|
const mergedAll = [...cloudDeduped, ...localBuf].sort((a, b) => a.at - b.at);
|
|
757
|
-
const points = tsData?.timeseries ?? [];
|
|
758
747
|
const nowSlot = Math.floor(nowMs / 1e4);
|
|
759
|
-
const
|
|
760
|
-
const
|
|
748
|
+
const traffic = new Array(60).fill(0);
|
|
749
|
+
const trafficHot = new Array(60).fill(false);
|
|
761
750
|
for (const e of mergedAll) {
|
|
762
751
|
const idx = 59 - (nowSlot - Math.floor(e.at / 1e4));
|
|
763
752
|
if (idx >= 0 && idx < 60) {
|
|
764
|
-
|
|
765
|
-
if (e.decision !== "ALLOW")
|
|
753
|
+
traffic[idx]++;
|
|
754
|
+
if (e.decision !== "ALLOW") trafficHot[idx] = true;
|
|
766
755
|
}
|
|
767
756
|
}
|
|
768
|
-
const
|
|
769
|
-
const traffic = localTraffic ? slotCounts : points.map((p) => p.total);
|
|
770
|
-
const trafficHot = localTraffic ? slotHot : points.map((p) => p.denied > 0);
|
|
771
|
-
const trafficLabel = localTraffic ? "calls/10s \xB7 last 10m" : "24h \xB7 cloud";
|
|
757
|
+
const trafficLabel = "calls/10s \xB7 last 10m \xB7 live";
|
|
772
758
|
const rl = ins.layers?.rateLimit;
|
|
773
759
|
const dl = ins.layers?.dlp;
|
|
774
760
|
const gh = ins.layers?.ghost;
|
|
@@ -813,7 +799,9 @@ function LivePanel({ active: active2 }) {
|
|
|
813
799
|
const localSessIds = new Set(localSess.keys());
|
|
814
800
|
if (ring?.session) localSessIds.add(ring.session);
|
|
815
801
|
const isLoc = (e) => e.source === "local" || !!e.session && localSessIds.has(e.session);
|
|
816
|
-
const
|
|
802
|
+
const q = search.trim().toLowerCase();
|
|
803
|
+
const matches = (e) => !q || `${e.tool} ${e.agent ?? ""} ${e.decision} ${e.permission} ${e.rule ?? ""} ${e.detail}`.toLowerCase().includes(q);
|
|
804
|
+
const filtered = (filter === "all" ? mergedAll : mergedAll.filter((e) => filter === "local" ? isLoc(e) : !isLoc(e))).filter(matches);
|
|
817
805
|
const visibleDesc = filtered.slice(-STREAM_KEEP).reverse();
|
|
818
806
|
const chartH = rows >= 36 ? 6 : 4;
|
|
819
807
|
const colH = rows >= 32 ? 7 : 5;
|
|
@@ -833,12 +821,21 @@ function LivePanel({ active: active2 }) {
|
|
|
833
821
|
const key = `${e.at}:${e.tool}:${e.decision}`;
|
|
834
822
|
if (seen.has(key)) continue;
|
|
835
823
|
seen.add(key);
|
|
836
|
-
all.push(e);
|
|
824
|
+
if (matches(e)) all.push(e);
|
|
837
825
|
}
|
|
838
826
|
return all.sort((a, b) => b.at - a.at);
|
|
839
827
|
})() : [];
|
|
840
828
|
useInput(
|
|
841
829
|
(input, key) => {
|
|
830
|
+
if (input === " ") {
|
|
831
|
+
setFrozen((f) => !f);
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
if (frozen) return;
|
|
835
|
+
if (input === "/" && mode !== "pick") {
|
|
836
|
+
setEditingSearch(true);
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
842
839
|
if (mode === "detail") {
|
|
843
840
|
const maxD = Math.max(0, detailEntries.length - (rows - 10));
|
|
844
841
|
if (key.leftArrow) {
|
|
@@ -862,11 +859,6 @@ function LivePanel({ active: active2 }) {
|
|
|
862
859
|
} else if (input === "s" || key.leftArrow) setMode("stream");
|
|
863
860
|
return;
|
|
864
861
|
}
|
|
865
|
-
if (input === " ") {
|
|
866
|
-
setFrozen((f) => !f);
|
|
867
|
-
return;
|
|
868
|
-
}
|
|
869
|
-
if (frozen) return;
|
|
870
862
|
if (input === "f") {
|
|
871
863
|
setFilter((f) => FILTERS[(FILTERS.indexOf(f) + 1) % FILTERS.length]);
|
|
872
864
|
setScroll(0);
|
|
@@ -878,8 +870,28 @@ function LivePanel({ active: active2 }) {
|
|
|
878
870
|
else if (key.pageDown) setScroll((n) => Math.min(maxScroll, n + streamRows));
|
|
879
871
|
else if (key.pageUp) setScroll((n) => Math.max(0, n - streamRows));
|
|
880
872
|
},
|
|
881
|
-
{ isActive: active2 }
|
|
873
|
+
{ isActive: active2 && !editingSearch }
|
|
882
874
|
);
|
|
875
|
+
const searchRow = editingSearch ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
876
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: "search: " }),
|
|
877
|
+
/* @__PURE__ */ jsx2(
|
|
878
|
+
TextInput,
|
|
879
|
+
{
|
|
880
|
+
value: search,
|
|
881
|
+
onChange: (v) => {
|
|
882
|
+
setSearch(v);
|
|
883
|
+
setScroll(0);
|
|
884
|
+
setDetailScroll(0);
|
|
885
|
+
},
|
|
886
|
+
onSubmit: () => setEditingSearch(false)
|
|
887
|
+
}
|
|
888
|
+
),
|
|
889
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " (enter done \xB7 empty = clear)" })
|
|
890
|
+
] }) : search ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
891
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: "search: " }),
|
|
892
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, bold: true, children: search }),
|
|
893
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ` ${filtered.length} match \xB7 / edit` })
|
|
894
|
+
] }) : null;
|
|
883
895
|
const sessionDot = (st) => st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\u25CC", color: theme.warn } : { ch: "\u25CB", color: theme.dim };
|
|
884
896
|
void sessionDot;
|
|
885
897
|
if (s === null && localOn === null) {
|
|
@@ -947,7 +959,8 @@ function LivePanel({ active: active2 }) {
|
|
|
947
959
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: perms.map(([p, c2]) => `${p}\xD7${c2}`).join(" ") || "\u2014" }),
|
|
948
960
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
949
961
|
] }),
|
|
950
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "TIMELINE", extra: `${d.length} calls \xB7 newest first${dScroll ? ` \xB7 \u25BC${dScroll} older` : ""} \xB7 \u2191\u2193 scroll \xB7 \u2190 back`, width: innerW }),
|
|
962
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "TIMELINE", extra: `${d.length} calls \xB7 newest first${dScroll ? ` \xB7 \u25BC${dScroll} older` : ""} \xB7 \u2191\u2193 scroll \xB7 / search \xB7 \u2190 back`, width: innerW }),
|
|
963
|
+
searchRow,
|
|
951
964
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: tlRows, overflow: "hidden", children: [
|
|
952
965
|
tl.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: detailCloud.length === 0 ? spin + " loading history\u2026" : "no entries" }) : null,
|
|
953
966
|
tl.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e) }, e.id))
|
|
@@ -1056,11 +1069,12 @@ function LivePanel({ active: active2 }) {
|
|
|
1056
1069
|
PaneTitle,
|
|
1057
1070
|
{
|
|
1058
1071
|
label: "TOOL STREAM",
|
|
1059
|
-
extra: `newest first \xB7 last ${STREAM_KEEP} \xB7 filter ${filter}${clampedScroll > 0 ? ` \xB7 \u25BC${clampedScroll} older` : ""} \xB7 f filter \xB7 \u2191\u2193 scroll \xB7 s sessions`,
|
|
1072
|
+
extra: `newest first \xB7 last ${STREAM_KEEP} \xB7 filter ${filter}${clampedScroll > 0 ? ` \xB7 \u25BC${clampedScroll} older` : ""} \xB7 f filter \xB7 / search \xB7 \u2191\u2193 scroll \xB7 s sessions`,
|
|
1060
1073
|
width: innerW
|
|
1061
1074
|
}
|
|
1062
1075
|
),
|
|
1063
1076
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: streamRows, overflow: "hidden", children: [
|
|
1077
|
+
searchRow,
|
|
1064
1078
|
localOn === false ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
1065
1079
|
/* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: "local logs off" }),
|
|
1066
1080
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2014 enable: dashboard \u2192 Settings \u2192 Local logs \xB7 hooks write ~/.solongate/local-logs \xB7 view: `solongate logs-server`" })
|
|
@@ -1074,14 +1088,14 @@ function LivePanel({ active: active2 }) {
|
|
|
1074
1088
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
1075
1089
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " LIVE " }),
|
|
1076
1090
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` local ${localOn ? "on" : "off"} \xB7 ${localBuf.length} loc/${cloudBuf.length} cld \xB7 top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} ` }),
|
|
1077
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " space copy-mode \xB7 f filter \xB7 s sessions \xB7 esc menu \xB7 q quit " })
|
|
1091
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " space copy-mode \xB7 / search \xB7 f filter \xB7 s sessions \xB7 esc menu \xB7 q quit " })
|
|
1078
1092
|
] })
|
|
1079
1093
|
] });
|
|
1080
1094
|
}
|
|
1081
1095
|
|
|
1082
1096
|
// src/tui/panels/Policies.tsx
|
|
1083
1097
|
import { Box as Box3, Text as Text3, useInput as useInput2 } from "ink";
|
|
1084
|
-
import
|
|
1098
|
+
import TextInput2 from "ink-text-input";
|
|
1085
1099
|
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
1086
1100
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1087
1101
|
var constr = (r, g, side) => (r[g]?.[side] ?? []).join(", ");
|
|
@@ -1291,7 +1305,7 @@ function PoliciesPanel({ focused }) {
|
|
|
1291
1305
|
return /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
1292
1306
|
/* @__PURE__ */ jsx3(Text3, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + f.label.padEnd(13) }),
|
|
1293
1307
|
active2 && editing && f.kind === "text" ? /* @__PURE__ */ jsx3(
|
|
1294
|
-
|
|
1308
|
+
TextInput2,
|
|
1295
1309
|
{
|
|
1296
1310
|
value: editVal,
|
|
1297
1311
|
onChange: setEditVal,
|
|
@@ -1446,7 +1460,7 @@ function FieldRow({ label, active: active2, children }) {
|
|
|
1446
1460
|
|
|
1447
1461
|
// src/tui/panels/Dlp.tsx
|
|
1448
1462
|
import { Box as Box5, Text as Text5, useInput as useInput4 } from "ink";
|
|
1449
|
-
import
|
|
1463
|
+
import TextInput3 from "ink-text-input";
|
|
1450
1464
|
import { useEffect as useEffect5, useState as useState5 } from "react";
|
|
1451
1465
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1452
1466
|
var MODES2 = ["off", "detect", "block"];
|
|
@@ -1549,7 +1563,7 @@ function DlpPanel({ focused }) {
|
|
|
1549
1563
|
adding ? /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, children: [
|
|
1550
1564
|
/* @__PURE__ */ jsx5(Text5, { color: theme.warn, children: adding === "name" ? "New pattern name: " : `Regex for "${newName}": ` }),
|
|
1551
1565
|
adding === "name" ? /* @__PURE__ */ jsx5(
|
|
1552
|
-
|
|
1566
|
+
TextInput3,
|
|
1553
1567
|
{
|
|
1554
1568
|
value: newName,
|
|
1555
1569
|
onChange: setNewName,
|
|
@@ -1560,7 +1574,7 @@ function DlpPanel({ focused }) {
|
|
|
1560
1574
|
}
|
|
1561
1575
|
}
|
|
1562
1576
|
) : /* @__PURE__ */ jsx5(
|
|
1563
|
-
|
|
1577
|
+
TextInput3,
|
|
1564
1578
|
{
|
|
1565
1579
|
value: newRe,
|
|
1566
1580
|
onChange: setNewRe,
|
|
@@ -1653,7 +1667,7 @@ function StatsPanel({ active: active2 }) {
|
|
|
1653
1667
|
|
|
1654
1668
|
// src/tui/panels/Audit.tsx
|
|
1655
1669
|
import { Box as Box7, Text as Text7, useInput as useInput5 } from "ink";
|
|
1656
|
-
import
|
|
1670
|
+
import TextInput4 from "ink-text-input";
|
|
1657
1671
|
import { useState as useState6 } from "react";
|
|
1658
1672
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1659
1673
|
var DECISIONS = [void 0, "DENY", "ALLOW"];
|
|
@@ -1772,7 +1786,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
1772
1786
|
": "
|
|
1773
1787
|
] }),
|
|
1774
1788
|
/* @__PURE__ */ jsx7(
|
|
1775
|
-
|
|
1789
|
+
TextInput4,
|
|
1776
1790
|
{
|
|
1777
1791
|
value: editing === "tool" ? tool : editing === "agent" ? agent : search,
|
|
1778
1792
|
onChange: editing === "tool" ? setTool : editing === "agent" ? setAgent : setSearch,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.75.0",
|
|
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": {
|