@solongate/proxy 0.70.0 → 0.72.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/commands/index.js +19 -9
- package/dist/index.js +59 -27
- package/dist/tui/index.js +59 -27
- package/package.json +1 -1
package/dist/commands/index.js
CHANGED
|
@@ -84,17 +84,27 @@ async function request(method, path, opts = {}) {
|
|
|
84
84
|
headers["Content-Type"] = "application/json";
|
|
85
85
|
bodyInit = JSON.stringify(opts.body);
|
|
86
86
|
}
|
|
87
|
+
const attempt = () => fetch(url, {
|
|
88
|
+
method,
|
|
89
|
+
headers,
|
|
90
|
+
body: bodyInit,
|
|
91
|
+
signal: AbortSignal.timeout(opts.timeoutMs ?? 15e3)
|
|
92
|
+
});
|
|
87
93
|
let res;
|
|
88
94
|
try {
|
|
89
|
-
res = await
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
res = await attempt();
|
|
96
|
+
} catch (firstErr) {
|
|
97
|
+
if (method !== "GET") {
|
|
98
|
+
const msg = firstErr instanceof Error ? firstErr.message : String(firstErr);
|
|
99
|
+
throw new ApiError(0, "NETWORK_ERROR", `Cannot reach SolonGate API: ${msg}`);
|
|
100
|
+
}
|
|
101
|
+
await new Promise((r) => setTimeout(r, 800));
|
|
102
|
+
try {
|
|
103
|
+
res = await attempt();
|
|
104
|
+
} catch (err2) {
|
|
105
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
106
|
+
throw new ApiError(0, "NETWORK_ERROR", `Cannot reach SolonGate API: ${msg}`);
|
|
107
|
+
}
|
|
98
108
|
}
|
|
99
109
|
const text = await res.text().catch(() => "");
|
|
100
110
|
let json = void 0;
|
package/dist/index.js
CHANGED
|
@@ -6633,17 +6633,27 @@ async function request(method, path, opts = {}) {
|
|
|
6633
6633
|
headers["Content-Type"] = "application/json";
|
|
6634
6634
|
bodyInit = JSON.stringify(opts.body);
|
|
6635
6635
|
}
|
|
6636
|
+
const attempt = () => fetch(url, {
|
|
6637
|
+
method,
|
|
6638
|
+
headers,
|
|
6639
|
+
body: bodyInit,
|
|
6640
|
+
signal: AbortSignal.timeout(opts.timeoutMs ?? 15e3)
|
|
6641
|
+
});
|
|
6636
6642
|
let res;
|
|
6637
6643
|
try {
|
|
6638
|
-
res = await
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
}
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6644
|
+
res = await attempt();
|
|
6645
|
+
} catch (firstErr) {
|
|
6646
|
+
if (method !== "GET") {
|
|
6647
|
+
const msg = firstErr instanceof Error ? firstErr.message : String(firstErr);
|
|
6648
|
+
throw new ApiError(0, "NETWORK_ERROR", `Cannot reach SolonGate API: ${msg}`);
|
|
6649
|
+
}
|
|
6650
|
+
await new Promise((r) => setTimeout(r, 800));
|
|
6651
|
+
try {
|
|
6652
|
+
res = await attempt();
|
|
6653
|
+
} catch (err2) {
|
|
6654
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
6655
|
+
throw new ApiError(0, "NETWORK_ERROR", `Cannot reach SolonGate API: ${msg}`);
|
|
6656
|
+
}
|
|
6647
6657
|
}
|
|
6648
6658
|
const text = await res.text().catch(() => "");
|
|
6649
6659
|
let json = void 0;
|
|
@@ -7055,7 +7065,7 @@ function copyToClipboard(text) {
|
|
|
7055
7065
|
}
|
|
7056
7066
|
}
|
|
7057
7067
|
process.stdout.write(`\x1B]52;c;${Buffer.from(text, "utf-8").toString("base64")}\x07`);
|
|
7058
|
-
return
|
|
7068
|
+
return null;
|
|
7059
7069
|
}
|
|
7060
7070
|
function cloudItem(e) {
|
|
7061
7071
|
return {
|
|
@@ -7121,14 +7131,14 @@ function PaneTitle({ label, extra, width: width2 }) {
|
|
|
7121
7131
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: tail + "\u2500".repeat(Math.max(0, width2 - used)) })
|
|
7122
7132
|
] });
|
|
7123
7133
|
}
|
|
7124
|
-
function StreamLine({ e }) {
|
|
7134
|
+
function StreamLine({ e, loc }) {
|
|
7125
7135
|
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7126
7136
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7127
7137
|
"[",
|
|
7128
7138
|
hhmmss(e.at),
|
|
7129
7139
|
" "
|
|
7130
7140
|
] }),
|
|
7131
|
-
/* @__PURE__ */ jsx2(Text2, { color:
|
|
7141
|
+
/* @__PURE__ */ jsx2(Text2, { color: loc ? theme.ok : "#4f6db8", children: loc ? "LOC" : "CLD" }),
|
|
7132
7142
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "] " }),
|
|
7133
7143
|
/* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), bold: e.decision !== "ALLOW", children: e.decision.padEnd(6) }),
|
|
7134
7144
|
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate2(e.tool, 12).padEnd(13) }),
|
|
@@ -7160,6 +7170,9 @@ function LivePanel({ active: active2 }) {
|
|
|
7160
7170
|
const seenRef = useRef2(/* @__PURE__ */ new Set());
|
|
7161
7171
|
const lastLocalTs = useRef2(0);
|
|
7162
7172
|
const pausedUntil = useRef2(0);
|
|
7173
|
+
const [frozen, setFrozen] = useState2(false);
|
|
7174
|
+
const frozenRef = useRef2(false);
|
|
7175
|
+
frozenRef.current = frozen;
|
|
7163
7176
|
const pushLog = useCallback2((msg, level = "ok") => {
|
|
7164
7177
|
setLog((prev) => [...prev.slice(-59), { ts: Date.now(), msg, level }]);
|
|
7165
7178
|
}, []);
|
|
@@ -7175,8 +7188,9 @@ function LivePanel({ active: active2 }) {
|
|
|
7175
7188
|
},
|
|
7176
7189
|
[pushLog]
|
|
7177
7190
|
);
|
|
7178
|
-
const paused = () => Date.now() < pausedUntil.current;
|
|
7191
|
+
const paused = () => frozenRef.current || Date.now() < pausedUntil.current;
|
|
7179
7192
|
const pollLocal = useCallback2(() => {
|
|
7193
|
+
if (frozenRef.current) return;
|
|
7180
7194
|
const lines = tailLines(LOCAL_LOG);
|
|
7181
7195
|
if (!lines.length) {
|
|
7182
7196
|
setLocalOn((prev) => prev === null ? false : prev);
|
|
@@ -7242,6 +7256,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7242
7256
|
const t0 = Date.now();
|
|
7243
7257
|
try {
|
|
7244
7258
|
const fd = await api.audit.list({ limit: 50 });
|
|
7259
|
+
if (frozenRef.current) return;
|
|
7245
7260
|
const ms = Date.now() - t0;
|
|
7246
7261
|
setLat((l) => [...l, ms].slice(-240));
|
|
7247
7262
|
const fresh = fd.entries.filter((e) => !seenRef.current.has(e.id));
|
|
@@ -7259,7 +7274,8 @@ function LivePanel({ active: active2 }) {
|
|
|
7259
7274
|
const pollMedium = useCallback2(async () => {
|
|
7260
7275
|
if (paused()) return;
|
|
7261
7276
|
try {
|
|
7262
|
-
|
|
7277
|
+
const sd = await api.stats.get();
|
|
7278
|
+
if (!frozenRef.current) setS(sd);
|
|
7263
7279
|
} catch (e) {
|
|
7264
7280
|
onApiError(e);
|
|
7265
7281
|
}
|
|
@@ -7267,7 +7283,8 @@ function LivePanel({ active: active2 }) {
|
|
|
7267
7283
|
const pollSlow = useCallback2(async () => {
|
|
7268
7284
|
if (paused()) return;
|
|
7269
7285
|
try {
|
|
7270
|
-
|
|
7286
|
+
const td = await api.stats.timeseries({ period: "24h" });
|
|
7287
|
+
if (!frozenRef.current) setTsData(td);
|
|
7271
7288
|
} catch (e) {
|
|
7272
7289
|
onApiError(e);
|
|
7273
7290
|
}
|
|
@@ -7313,19 +7330,23 @@ function LivePanel({ active: active2 }) {
|
|
|
7313
7330
|
}, [detail]);
|
|
7314
7331
|
const [tick, setTick] = useState2(0);
|
|
7315
7332
|
useEffect2(() => {
|
|
7316
|
-
if (!active2) return;
|
|
7333
|
+
if (!active2 || frozen) return;
|
|
7317
7334
|
const t = setInterval(() => setTick((n) => n + 1), 500);
|
|
7318
7335
|
return () => clearInterval(t);
|
|
7319
|
-
}, [active2]);
|
|
7336
|
+
}, [active2, frozen]);
|
|
7320
7337
|
const startRef = useRef2(Date.now());
|
|
7321
7338
|
const cols = process.stdout.columns ?? 100;
|
|
7322
7339
|
const rows = process.stdout.rows ?? 30;
|
|
7323
7340
|
const ins = insights.data ?? {};
|
|
7324
7341
|
const spin = SPIN[tick % SPIN.length];
|
|
7325
7342
|
const nowMs = Date.now();
|
|
7326
|
-
const
|
|
7327
|
-
|
|
7328
|
-
|
|
7343
|
+
const localKeys = /* @__PURE__ */ new Set();
|
|
7344
|
+
for (const e of localBuf) {
|
|
7345
|
+
const m = Math.round(e.at / 6e4);
|
|
7346
|
+
for (const d of [-1, 0, 1]) localKeys.add(`${e.tool}|${e.decision}|${m + d}`);
|
|
7347
|
+
}
|
|
7348
|
+
const cloudDeduped = cloudBuf.filter((e) => !localKeys.has(`${e.tool}|${e.decision}|${Math.round(e.at / 6e4)}`));
|
|
7349
|
+
const mergedAll = [...cloudDeduped, ...localBuf].sort((a, b) => a.at - b.at);
|
|
7329
7350
|
const points = tsData?.timeseries ?? [];
|
|
7330
7351
|
const nowMin = Math.floor(nowMs / 6e4);
|
|
7331
7352
|
const minuteCounts = new Array(60).fill(0);
|
|
@@ -7382,6 +7403,11 @@ function LivePanel({ active: active2 }) {
|
|
|
7382
7403
|
isMe: false
|
|
7383
7404
|
}))
|
|
7384
7405
|
];
|
|
7406
|
+
const localSessIds = new Set(localSess.keys());
|
|
7407
|
+
if (ring?.session) localSessIds.add(ring.session);
|
|
7408
|
+
const isLoc = (e) => e.source === "local" || !!e.session && localSessIds.has(e.session);
|
|
7409
|
+
const filtered = filter === "all" ? mergedAll : mergedAll.filter((e) => filter === "local" ? isLoc(e) : !isLoc(e));
|
|
7410
|
+
const visibleDesc = filtered.slice(-STREAM_KEEP).reverse();
|
|
7385
7411
|
const chartH = rows >= 36 ? 6 : 4;
|
|
7386
7412
|
const colH = rows >= 32 ? 7 : 5;
|
|
7387
7413
|
const streamRows = Math.max(4, rows - 5 - (1 + chartH) - colH);
|
|
@@ -7429,6 +7455,11 @@ function LivePanel({ active: active2 }) {
|
|
|
7429
7455
|
} else if (input === "s" || key.leftArrow) setMode("stream");
|
|
7430
7456
|
return;
|
|
7431
7457
|
}
|
|
7458
|
+
if (input === " ") {
|
|
7459
|
+
setFrozen((f) => !f);
|
|
7460
|
+
return;
|
|
7461
|
+
}
|
|
7462
|
+
if (frozen) return;
|
|
7432
7463
|
if (input === "f") {
|
|
7433
7464
|
setFilter((f) => FILTERS[(FILTERS.indexOf(f) + 1) % FILTERS.length]);
|
|
7434
7465
|
setScroll(0);
|
|
@@ -7443,14 +7474,15 @@ function LivePanel({ active: active2 }) {
|
|
|
7443
7474
|
const dump = visibleDesc.map(
|
|
7444
7475
|
(e) => `[${new Date(e.at).toISOString()}] ${e.source.toUpperCase().padEnd(5)} ${e.decision.padEnd(6)} ${e.tool.padEnd(16)} ${e.permission.padEnd(5)} ${e.evalMs != null ? String(e.evalMs).padStart(5) + "ms " : " "}${(e.agent ?? "-").padEnd(14)} ${e.session ? e.session.slice(0, 8) : "- "} ${e.detail}`
|
|
7445
7476
|
).join("\n");
|
|
7446
|
-
let via = "file";
|
|
7447
7477
|
try {
|
|
7448
7478
|
mkdirSync3(dirname(DUMP), { recursive: true });
|
|
7449
7479
|
writeFileSync3(DUMP, dump + "\n");
|
|
7450
7480
|
} catch {
|
|
7451
7481
|
}
|
|
7452
|
-
via = copyToClipboard(dump);
|
|
7453
|
-
setToast(
|
|
7482
|
+
const via = copyToClipboard(dump);
|
|
7483
|
+
setToast(
|
|
7484
|
+
via ? { msg: `\u2713 ${visibleDesc.length} lines in clipboard (${via})`, until: Date.now() + 4e3 } : { msg: "\u2717 no clipboard tool \u2014 install wl-clipboard or xclip", until: Date.now() + 6e3 }
|
|
7485
|
+
);
|
|
7454
7486
|
}
|
|
7455
7487
|
},
|
|
7456
7488
|
{ isActive: active2 }
|
|
@@ -7466,7 +7498,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7466
7498
|
const titleBar = /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7467
7499
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: " SOLONGATE LIVE " }),
|
|
7468
7500
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` ${spin} up ${fmtUp(nowMs - startRef.current)} \xB7 ${hhmmss(nowMs)} \xB7 api ${latNow}ms ` }),
|
|
7469
|
-
backingOff ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d2a12", color: "#ffb454", bold: true, children: " RATE LIMITED \xB7 backing off " }) : lastDeny ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d1220", color: "#ff6b6b", bold: true, children: ` \u26A0 ${hhmmss(lastDeny.at)} ${lastDeny.tool} DENIED ` }) : /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#4f6db8", children: " \u2713 clean " }),
|
|
7501
|
+
frozen ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#123d1f", color: "#7bd88f", bold: true, children: " \u23F5 COPY MODE \u2014 screen frozen, select & copy freely \xB7 space resume " }) : backingOff ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d2a12", color: "#ffb454", bold: true, children: " RATE LIMITED \xB7 backing off " }) : lastDeny ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d1220", color: "#ff6b6b", bold: true, children: ` \u26A0 ${hhmmss(lastDeny.at)} ${lastDeny.tool} DENIED ` }) : /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#4f6db8", children: " \u2713 clean " }),
|
|
7470
7502
|
toast && nowMs < toast.until ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#123d1f", color: "#7bd88f", bold: true, children: ` ${toast.msg} ` }) : null
|
|
7471
7503
|
] });
|
|
7472
7504
|
if (mode === "detail" && detail) {
|
|
@@ -7526,7 +7558,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7526
7558
|
/* @__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 }),
|
|
7527
7559
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: tlRows, overflow: "hidden", children: [
|
|
7528
7560
|
tl.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: detailCloud.length === 0 ? spin + " loading history\u2026" : "no entries" }) : null,
|
|
7529
|
-
tl.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e }, e.id))
|
|
7561
|
+
tl.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e) }, e.id))
|
|
7530
7562
|
] }),
|
|
7531
7563
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7532
7564
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " SESSION " }),
|
|
@@ -7645,12 +7677,12 @@ function LivePanel({ active: active2 }) {
|
|
|
7645
7677
|
spin,
|
|
7646
7678
|
" awaiting traffic\u2026"
|
|
7647
7679
|
] }) : null,
|
|
7648
|
-
windowed.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e }, e.id))
|
|
7680
|
+
windowed.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e) }, e.id))
|
|
7649
7681
|
] }),
|
|
7650
7682
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7651
7683
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " LIVE " }),
|
|
7652
7684
|
/* @__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"} ` }),
|
|
7653
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " f filter \xB7 s sessions \xB7 c copy \xB7 esc menu \xB7 q quit " })
|
|
7685
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " space copy-mode \xB7 f filter \xB7 s sessions \xB7 c quick-copy \xB7 esc menu \xB7 q quit " })
|
|
7654
7686
|
] })
|
|
7655
7687
|
] });
|
|
7656
7688
|
}
|
package/dist/tui/index.js
CHANGED
|
@@ -220,17 +220,27 @@ async function request(method, path, opts = {}) {
|
|
|
220
220
|
headers["Content-Type"] = "application/json";
|
|
221
221
|
bodyInit = JSON.stringify(opts.body);
|
|
222
222
|
}
|
|
223
|
+
const attempt = () => fetch(url, {
|
|
224
|
+
method,
|
|
225
|
+
headers,
|
|
226
|
+
body: bodyInit,
|
|
227
|
+
signal: AbortSignal.timeout(opts.timeoutMs ?? 15e3)
|
|
228
|
+
});
|
|
223
229
|
let res;
|
|
224
230
|
try {
|
|
225
|
-
res = await
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
231
|
+
res = await attempt();
|
|
232
|
+
} catch (firstErr) {
|
|
233
|
+
if (method !== "GET") {
|
|
234
|
+
const msg = firstErr instanceof Error ? firstErr.message : String(firstErr);
|
|
235
|
+
throw new ApiError(0, "NETWORK_ERROR", `Cannot reach SolonGate API: ${msg}`);
|
|
236
|
+
}
|
|
237
|
+
await new Promise((r) => setTimeout(r, 800));
|
|
238
|
+
try {
|
|
239
|
+
res = await attempt();
|
|
240
|
+
} catch (err) {
|
|
241
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
242
|
+
throw new ApiError(0, "NETWORK_ERROR", `Cannot reach SolonGate API: ${msg}`);
|
|
243
|
+
}
|
|
234
244
|
}
|
|
235
245
|
const text = await res.text().catch(() => "");
|
|
236
246
|
let json = void 0;
|
|
@@ -480,7 +490,7 @@ function copyToClipboard(text) {
|
|
|
480
490
|
}
|
|
481
491
|
}
|
|
482
492
|
process.stdout.write(`\x1B]52;c;${Buffer.from(text, "utf-8").toString("base64")}\x07`);
|
|
483
|
-
return
|
|
493
|
+
return null;
|
|
484
494
|
}
|
|
485
495
|
function cloudItem(e) {
|
|
486
496
|
return {
|
|
@@ -546,14 +556,14 @@ function PaneTitle({ label, extra, width }) {
|
|
|
546
556
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: tail + "\u2500".repeat(Math.max(0, width - used)) })
|
|
547
557
|
] });
|
|
548
558
|
}
|
|
549
|
-
function StreamLine({ e }) {
|
|
559
|
+
function StreamLine({ e, loc }) {
|
|
550
560
|
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
551
561
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
552
562
|
"[",
|
|
553
563
|
hhmmss(e.at),
|
|
554
564
|
" "
|
|
555
565
|
] }),
|
|
556
|
-
/* @__PURE__ */ jsx2(Text2, { color:
|
|
566
|
+
/* @__PURE__ */ jsx2(Text2, { color: loc ? theme.ok : "#4f6db8", children: loc ? "LOC" : "CLD" }),
|
|
557
567
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "] " }),
|
|
558
568
|
/* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), bold: e.decision !== "ALLOW", children: e.decision.padEnd(6) }),
|
|
559
569
|
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate(e.tool, 12).padEnd(13) }),
|
|
@@ -586,6 +596,9 @@ function LivePanel({ active: active2 }) {
|
|
|
586
596
|
const seenRef = useRef2(/* @__PURE__ */ new Set());
|
|
587
597
|
const lastLocalTs = useRef2(0);
|
|
588
598
|
const pausedUntil = useRef2(0);
|
|
599
|
+
const [frozen, setFrozen] = useState2(false);
|
|
600
|
+
const frozenRef = useRef2(false);
|
|
601
|
+
frozenRef.current = frozen;
|
|
589
602
|
const pushLog = useCallback2((msg, level = "ok") => {
|
|
590
603
|
setLog((prev) => [...prev.slice(-59), { ts: Date.now(), msg, level }]);
|
|
591
604
|
}, []);
|
|
@@ -601,8 +614,9 @@ function LivePanel({ active: active2 }) {
|
|
|
601
614
|
},
|
|
602
615
|
[pushLog]
|
|
603
616
|
);
|
|
604
|
-
const paused = () => Date.now() < pausedUntil.current;
|
|
617
|
+
const paused = () => frozenRef.current || Date.now() < pausedUntil.current;
|
|
605
618
|
const pollLocal = useCallback2(() => {
|
|
619
|
+
if (frozenRef.current) return;
|
|
606
620
|
const lines = tailLines(LOCAL_LOG);
|
|
607
621
|
if (!lines.length) {
|
|
608
622
|
setLocalOn((prev) => prev === null ? false : prev);
|
|
@@ -668,6 +682,7 @@ function LivePanel({ active: active2 }) {
|
|
|
668
682
|
const t0 = Date.now();
|
|
669
683
|
try {
|
|
670
684
|
const fd = await api.audit.list({ limit: 50 });
|
|
685
|
+
if (frozenRef.current) return;
|
|
671
686
|
const ms = Date.now() - t0;
|
|
672
687
|
setLat((l) => [...l, ms].slice(-240));
|
|
673
688
|
const fresh = fd.entries.filter((e) => !seenRef.current.has(e.id));
|
|
@@ -685,7 +700,8 @@ function LivePanel({ active: active2 }) {
|
|
|
685
700
|
const pollMedium = useCallback2(async () => {
|
|
686
701
|
if (paused()) return;
|
|
687
702
|
try {
|
|
688
|
-
|
|
703
|
+
const sd = await api.stats.get();
|
|
704
|
+
if (!frozenRef.current) setS(sd);
|
|
689
705
|
} catch (e) {
|
|
690
706
|
onApiError(e);
|
|
691
707
|
}
|
|
@@ -693,7 +709,8 @@ function LivePanel({ active: active2 }) {
|
|
|
693
709
|
const pollSlow = useCallback2(async () => {
|
|
694
710
|
if (paused()) return;
|
|
695
711
|
try {
|
|
696
|
-
|
|
712
|
+
const td = await api.stats.timeseries({ period: "24h" });
|
|
713
|
+
if (!frozenRef.current) setTsData(td);
|
|
697
714
|
} catch (e) {
|
|
698
715
|
onApiError(e);
|
|
699
716
|
}
|
|
@@ -739,19 +756,23 @@ function LivePanel({ active: active2 }) {
|
|
|
739
756
|
}, [detail]);
|
|
740
757
|
const [tick, setTick] = useState2(0);
|
|
741
758
|
useEffect2(() => {
|
|
742
|
-
if (!active2) return;
|
|
759
|
+
if (!active2 || frozen) return;
|
|
743
760
|
const t = setInterval(() => setTick((n) => n + 1), 500);
|
|
744
761
|
return () => clearInterval(t);
|
|
745
|
-
}, [active2]);
|
|
762
|
+
}, [active2, frozen]);
|
|
746
763
|
const startRef = useRef2(Date.now());
|
|
747
764
|
const cols = process.stdout.columns ?? 100;
|
|
748
765
|
const rows = process.stdout.rows ?? 30;
|
|
749
766
|
const ins = insights.data ?? {};
|
|
750
767
|
const spin = SPIN[tick % SPIN.length];
|
|
751
768
|
const nowMs = Date.now();
|
|
752
|
-
const
|
|
753
|
-
|
|
754
|
-
|
|
769
|
+
const localKeys = /* @__PURE__ */ new Set();
|
|
770
|
+
for (const e of localBuf) {
|
|
771
|
+
const m = Math.round(e.at / 6e4);
|
|
772
|
+
for (const d of [-1, 0, 1]) localKeys.add(`${e.tool}|${e.decision}|${m + d}`);
|
|
773
|
+
}
|
|
774
|
+
const cloudDeduped = cloudBuf.filter((e) => !localKeys.has(`${e.tool}|${e.decision}|${Math.round(e.at / 6e4)}`));
|
|
775
|
+
const mergedAll = [...cloudDeduped, ...localBuf].sort((a, b) => a.at - b.at);
|
|
755
776
|
const points = tsData?.timeseries ?? [];
|
|
756
777
|
const nowMin = Math.floor(nowMs / 6e4);
|
|
757
778
|
const minuteCounts = new Array(60).fill(0);
|
|
@@ -808,6 +829,11 @@ function LivePanel({ active: active2 }) {
|
|
|
808
829
|
isMe: false
|
|
809
830
|
}))
|
|
810
831
|
];
|
|
832
|
+
const localSessIds = new Set(localSess.keys());
|
|
833
|
+
if (ring?.session) localSessIds.add(ring.session);
|
|
834
|
+
const isLoc = (e) => e.source === "local" || !!e.session && localSessIds.has(e.session);
|
|
835
|
+
const filtered = filter === "all" ? mergedAll : mergedAll.filter((e) => filter === "local" ? isLoc(e) : !isLoc(e));
|
|
836
|
+
const visibleDesc = filtered.slice(-STREAM_KEEP).reverse();
|
|
811
837
|
const chartH = rows >= 36 ? 6 : 4;
|
|
812
838
|
const colH = rows >= 32 ? 7 : 5;
|
|
813
839
|
const streamRows = Math.max(4, rows - 5 - (1 + chartH) - colH);
|
|
@@ -855,6 +881,11 @@ function LivePanel({ active: active2 }) {
|
|
|
855
881
|
} else if (input === "s" || key.leftArrow) setMode("stream");
|
|
856
882
|
return;
|
|
857
883
|
}
|
|
884
|
+
if (input === " ") {
|
|
885
|
+
setFrozen((f) => !f);
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
if (frozen) return;
|
|
858
889
|
if (input === "f") {
|
|
859
890
|
setFilter((f) => FILTERS[(FILTERS.indexOf(f) + 1) % FILTERS.length]);
|
|
860
891
|
setScroll(0);
|
|
@@ -869,14 +900,15 @@ function LivePanel({ active: active2 }) {
|
|
|
869
900
|
const dump = visibleDesc.map(
|
|
870
901
|
(e) => `[${new Date(e.at).toISOString()}] ${e.source.toUpperCase().padEnd(5)} ${e.decision.padEnd(6)} ${e.tool.padEnd(16)} ${e.permission.padEnd(5)} ${e.evalMs != null ? String(e.evalMs).padStart(5) + "ms " : " "}${(e.agent ?? "-").padEnd(14)} ${e.session ? e.session.slice(0, 8) : "- "} ${e.detail}`
|
|
871
902
|
).join("\n");
|
|
872
|
-
let via = "file";
|
|
873
903
|
try {
|
|
874
904
|
mkdirSync(dirname(DUMP), { recursive: true });
|
|
875
905
|
writeFileSync(DUMP, dump + "\n");
|
|
876
906
|
} catch {
|
|
877
907
|
}
|
|
878
|
-
via = copyToClipboard(dump);
|
|
879
|
-
setToast(
|
|
908
|
+
const via = copyToClipboard(dump);
|
|
909
|
+
setToast(
|
|
910
|
+
via ? { msg: `\u2713 ${visibleDesc.length} lines in clipboard (${via})`, until: Date.now() + 4e3 } : { msg: "\u2717 no clipboard tool \u2014 install wl-clipboard or xclip", until: Date.now() + 6e3 }
|
|
911
|
+
);
|
|
880
912
|
}
|
|
881
913
|
},
|
|
882
914
|
{ isActive: active2 }
|
|
@@ -892,7 +924,7 @@ function LivePanel({ active: active2 }) {
|
|
|
892
924
|
const titleBar = /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
893
925
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: " SOLONGATE LIVE " }),
|
|
894
926
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` ${spin} up ${fmtUp(nowMs - startRef.current)} \xB7 ${hhmmss(nowMs)} \xB7 api ${latNow}ms ` }),
|
|
895
|
-
backingOff ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d2a12", color: "#ffb454", bold: true, children: " RATE LIMITED \xB7 backing off " }) : lastDeny ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d1220", color: "#ff6b6b", bold: true, children: ` \u26A0 ${hhmmss(lastDeny.at)} ${lastDeny.tool} DENIED ` }) : /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#4f6db8", children: " \u2713 clean " }),
|
|
927
|
+
frozen ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#123d1f", color: "#7bd88f", bold: true, children: " \u23F5 COPY MODE \u2014 screen frozen, select & copy freely \xB7 space resume " }) : backingOff ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d2a12", color: "#ffb454", bold: true, children: " RATE LIMITED \xB7 backing off " }) : lastDeny ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d1220", color: "#ff6b6b", bold: true, children: ` \u26A0 ${hhmmss(lastDeny.at)} ${lastDeny.tool} DENIED ` }) : /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#4f6db8", children: " \u2713 clean " }),
|
|
896
928
|
toast && nowMs < toast.until ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#123d1f", color: "#7bd88f", bold: true, children: ` ${toast.msg} ` }) : null
|
|
897
929
|
] });
|
|
898
930
|
if (mode === "detail" && detail) {
|
|
@@ -952,7 +984,7 @@ function LivePanel({ active: active2 }) {
|
|
|
952
984
|
/* @__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 }),
|
|
953
985
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: tlRows, overflow: "hidden", children: [
|
|
954
986
|
tl.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: detailCloud.length === 0 ? spin + " loading history\u2026" : "no entries" }) : null,
|
|
955
|
-
tl.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e }, e.id))
|
|
987
|
+
tl.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e) }, e.id))
|
|
956
988
|
] }),
|
|
957
989
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
958
990
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " SESSION " }),
|
|
@@ -1071,12 +1103,12 @@ function LivePanel({ active: active2 }) {
|
|
|
1071
1103
|
spin,
|
|
1072
1104
|
" awaiting traffic\u2026"
|
|
1073
1105
|
] }) : null,
|
|
1074
|
-
windowed.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e }, e.id))
|
|
1106
|
+
windowed.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e) }, e.id))
|
|
1075
1107
|
] }),
|
|
1076
1108
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
1077
1109
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " LIVE " }),
|
|
1078
1110
|
/* @__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"} ` }),
|
|
1079
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " f filter \xB7 s sessions \xB7 c copy \xB7 esc menu \xB7 q quit " })
|
|
1111
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " space copy-mode \xB7 f filter \xB7 s sessions \xB7 c quick-copy \xB7 esc menu \xB7 q quit " })
|
|
1080
1112
|
] })
|
|
1081
1113
|
] });
|
|
1082
1114
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.72.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": {
|