@solongate/proxy 0.75.1 → 0.76.1
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 +11 -11
- package/dist/index.js +105 -54
- package/dist/tui/index.js +100 -49
- package/package.json +1 -1
package/dist/commands/index.js
CHANGED
|
@@ -86,26 +86,26 @@ async function request(method, path, opts = {}) {
|
|
|
86
86
|
}
|
|
87
87
|
const attempt = () => fetch(url, {
|
|
88
88
|
method,
|
|
89
|
-
headers,
|
|
89
|
+
headers: { ...headers, Connection: "close" },
|
|
90
90
|
body: bodyInit,
|
|
91
91
|
signal: AbortSignal.timeout(opts.timeoutMs ?? 15e3)
|
|
92
92
|
});
|
|
93
|
+
const maxTries = method === "GET" ? 3 : 1;
|
|
93
94
|
let res;
|
|
94
|
-
|
|
95
|
-
|
|
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));
|
|
95
|
+
let lastErr;
|
|
96
|
+
for (let i = 0; i < maxTries; i++) {
|
|
102
97
|
try {
|
|
103
98
|
res = await attempt();
|
|
99
|
+
break;
|
|
104
100
|
} catch (err2) {
|
|
105
|
-
|
|
106
|
-
|
|
101
|
+
lastErr = err2;
|
|
102
|
+
if (i < maxTries - 1) await new Promise((r) => setTimeout(r, 600 * (i + 1)));
|
|
107
103
|
}
|
|
108
104
|
}
|
|
105
|
+
if (!res) {
|
|
106
|
+
const msg = lastErr instanceof Error ? lastErr.message : String(lastErr);
|
|
107
|
+
throw new ApiError(0, "NETWORK_ERROR", `Cannot reach SolonGate API: ${msg}`);
|
|
108
|
+
}
|
|
109
109
|
const text = await res.text().catch(() => "");
|
|
110
110
|
let json = void 0;
|
|
111
111
|
if (text) {
|
package/dist/index.js
CHANGED
|
@@ -6635,26 +6635,26 @@ async function request(method, path, opts = {}) {
|
|
|
6635
6635
|
}
|
|
6636
6636
|
const attempt = () => fetch(url, {
|
|
6637
6637
|
method,
|
|
6638
|
-
headers,
|
|
6638
|
+
headers: { ...headers, Connection: "close" },
|
|
6639
6639
|
body: bodyInit,
|
|
6640
6640
|
signal: AbortSignal.timeout(opts.timeoutMs ?? 15e3)
|
|
6641
6641
|
});
|
|
6642
|
+
const maxTries = method === "GET" ? 3 : 1;
|
|
6642
6643
|
let res;
|
|
6643
|
-
|
|
6644
|
-
|
|
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));
|
|
6644
|
+
let lastErr;
|
|
6645
|
+
for (let i = 0; i < maxTries; i++) {
|
|
6651
6646
|
try {
|
|
6652
6647
|
res = await attempt();
|
|
6648
|
+
break;
|
|
6653
6649
|
} catch (err2) {
|
|
6654
|
-
|
|
6655
|
-
|
|
6650
|
+
lastErr = err2;
|
|
6651
|
+
if (i < maxTries - 1) await new Promise((r) => setTimeout(r, 600 * (i + 1)));
|
|
6656
6652
|
}
|
|
6657
6653
|
}
|
|
6654
|
+
if (!res) {
|
|
6655
|
+
const msg = lastErr instanceof Error ? lastErr.message : String(lastErr);
|
|
6656
|
+
throw new ApiError(0, "NETWORK_ERROR", `Cannot reach SolonGate API: ${msg}`);
|
|
6657
|
+
}
|
|
6658
6658
|
const text = await res.text().catch(() => "");
|
|
6659
6659
|
let json = void 0;
|
|
6660
6660
|
if (text) {
|
|
@@ -7031,11 +7031,12 @@ var init_hooks = __esm({
|
|
|
7031
7031
|
// src/tui/panels/Live.tsx
|
|
7032
7032
|
import { Box as Box2, Text as Text2, useInput } from "ink";
|
|
7033
7033
|
import TextInput from "ink-text-input";
|
|
7034
|
+
import { spawn } from "child_process";
|
|
7034
7035
|
import { closeSync, openSync, readSync, statSync } from "fs";
|
|
7035
7036
|
import { homedir as homedir3 } from "os";
|
|
7036
7037
|
import { join as join5 } from "path";
|
|
7037
7038
|
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
7038
|
-
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
7039
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
7039
7040
|
function tailLines(file, maxBytes = 131072) {
|
|
7040
7041
|
try {
|
|
7041
7042
|
const size = statSync(file).size;
|
|
@@ -7154,18 +7155,26 @@ function LivePanel({ active: active2 }) {
|
|
|
7154
7155
|
const seenRef = useRef2(/* @__PURE__ */ new Set());
|
|
7155
7156
|
const lastLocalTs = useRef2(0);
|
|
7156
7157
|
const pausedUntil = useRef2(0);
|
|
7158
|
+
const sessRef = useRef2([]);
|
|
7159
|
+
const prevStatus = useRef2(/* @__PURE__ */ new Map());
|
|
7160
|
+
const [alerts, setAlerts] = useState2([]);
|
|
7157
7161
|
const [frozen, setFrozen] = useState2(false);
|
|
7158
7162
|
const frozenRef = useRef2(false);
|
|
7159
7163
|
frozenRef.current = frozen;
|
|
7160
7164
|
const pushLog = useCallback2((msg, level = "ok") => {
|
|
7161
7165
|
setLog((prev) => [...prev.slice(-59), { ts: Date.now(), msg, level }]);
|
|
7162
7166
|
}, []);
|
|
7167
|
+
const netFails = useRef2(0);
|
|
7163
7168
|
const onApiError = useCallback2(
|
|
7164
7169
|
(e) => {
|
|
7165
7170
|
const is429 = e instanceof ApiError && e.status === 429;
|
|
7171
|
+
const isNet = e instanceof ApiError && e.status === 0;
|
|
7166
7172
|
if (is429 && Date.now() >= pausedUntil.current) {
|
|
7167
7173
|
pausedUntil.current = Date.now() + 3e4;
|
|
7168
7174
|
pushLog("rate limited by api \xB7 backing off 30s", "warn");
|
|
7175
|
+
} else if (isNet) {
|
|
7176
|
+
netFails.current += 1;
|
|
7177
|
+
if (netFails.current === 3) pushLog("network unreachable \xB7 retrying\u2026", "warn");
|
|
7169
7178
|
} else if (!is429) {
|
|
7170
7179
|
pushLog(`api error \xB7 ${truncate2(e instanceof Error ? e.message : String(e), 40)}`, "bad");
|
|
7171
7180
|
}
|
|
@@ -7241,6 +7250,8 @@ function LivePanel({ active: active2 }) {
|
|
|
7241
7250
|
try {
|
|
7242
7251
|
const fd = await api.audit.list({ limit: 50 });
|
|
7243
7252
|
if (frozenRef.current) return;
|
|
7253
|
+
if (netFails.current >= 3) pushLog("network recovered", "ok");
|
|
7254
|
+
netFails.current = 0;
|
|
7244
7255
|
const ms = Date.now() - t0;
|
|
7245
7256
|
setLat((l) => [...l, ms].slice(-240));
|
|
7246
7257
|
const fresh = fd.entries.filter((e) => !seenRef.current.has(e.id));
|
|
@@ -7307,6 +7318,30 @@ function LivePanel({ active: active2 }) {
|
|
|
7307
7318
|
return () => clearInterval(t);
|
|
7308
7319
|
}, [active2, frozen]);
|
|
7309
7320
|
const startRef = useRef2(Date.now());
|
|
7321
|
+
useEffect2(() => {
|
|
7322
|
+
if (!active2) return;
|
|
7323
|
+
for (const r of sessRef.current) {
|
|
7324
|
+
const prev = prevStatus.current.get(r.id);
|
|
7325
|
+
if (prev && prev === "active" && r.status === "idle") {
|
|
7326
|
+
const name = r.isMe ? `${r.agent} (this machine)` : r.agent;
|
|
7327
|
+
const msg = `${name} went IDLE \u2014 no tool call for 60s`;
|
|
7328
|
+
pushLog(`\u23F8 ${msg}`, "warn");
|
|
7329
|
+
setAlerts((a) => [...a.filter((x) => x.id !== r.id), { id: r.id, msg, until: Date.now() + 15e3 }].slice(-4));
|
|
7330
|
+
try {
|
|
7331
|
+
process.stdout.write("\x07");
|
|
7332
|
+
} catch {
|
|
7333
|
+
}
|
|
7334
|
+
try {
|
|
7335
|
+
const child = spawn("notify-send", ["-a", "SolonGate", "Agent idle", msg], { stdio: "ignore", detached: true });
|
|
7336
|
+
child.on("error", () => {
|
|
7337
|
+
});
|
|
7338
|
+
child.unref();
|
|
7339
|
+
} catch {
|
|
7340
|
+
}
|
|
7341
|
+
}
|
|
7342
|
+
prevStatus.current.set(r.id, r.status);
|
|
7343
|
+
}
|
|
7344
|
+
}, [tick, active2, pushLog]);
|
|
7310
7345
|
const cols = process.stdout.columns ?? 100;
|
|
7311
7346
|
const rows = process.stdout.rows ?? 30;
|
|
7312
7347
|
const ins = insights.data ?? {};
|
|
@@ -7359,7 +7394,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7359
7394
|
localSess.set(e.session, cur);
|
|
7360
7395
|
}
|
|
7361
7396
|
const combinedSess = [
|
|
7362
|
-
...[...localSess.entries()].sort((a, b) => b[1].lastAt - a[1].lastAt).map(([id, v]) => ({ source: "local", id, agent: v.agent, calls: v.calls, denies: v.denies, lastAt: v.lastAt, isMe: ring?.session === id })),
|
|
7397
|
+
...[...localSess.entries()].sort((a, b) => b[1].lastAt - a[1].lastAt).map(([id, v]) => ({ source: "local", id, agent: v.agent, calls: v.calls, denies: v.denies, lastAt: v.lastAt, status: sessStatus(v.lastAt, nowMs), isMe: ring?.session === id })),
|
|
7363
7398
|
...sess.filter((a) => !localSess.has(a.session_id)).map((a) => ({
|
|
7364
7399
|
source: "cloud",
|
|
7365
7400
|
id: a.session_id,
|
|
@@ -7367,10 +7402,12 @@ function LivePanel({ active: active2 }) {
|
|
|
7367
7402
|
calls: a.total_calls,
|
|
7368
7403
|
denies: a.denied_calls,
|
|
7369
7404
|
lastAt: Date.parse(a.last_seen_at),
|
|
7405
|
+
status: sessStatus(Date.parse(a.last_seen_at), nowMs),
|
|
7370
7406
|
trust: a.trust_score,
|
|
7371
7407
|
isMe: false
|
|
7372
7408
|
}))
|
|
7373
7409
|
];
|
|
7410
|
+
sessRef.current = combinedSess;
|
|
7374
7411
|
const localSessIds = new Set(localSess.keys());
|
|
7375
7412
|
if (ring?.session) localSessIds.add(ring.session);
|
|
7376
7413
|
const isLoc = (e) => e.source === "local" || !!e.session && localSessIds.has(e.session);
|
|
@@ -7386,9 +7423,10 @@ function LivePanel({ active: active2 }) {
|
|
|
7386
7423
|
const rightW = innerW - leftW - 2;
|
|
7387
7424
|
const colW = Math.max(20, Math.floor((innerW - 4) / 3));
|
|
7388
7425
|
const pickable = combinedSess.slice(0, colH - 1);
|
|
7389
|
-
const
|
|
7426
|
+
const streamBodyRows = Math.max(3, streamRows - 1);
|
|
7427
|
+
const maxScroll = Math.max(0, visibleDesc.length - streamBodyRows);
|
|
7390
7428
|
const clampedScroll = Math.min(scroll, maxScroll);
|
|
7391
|
-
const windowed = visibleDesc.slice(clampedScroll, clampedScroll +
|
|
7429
|
+
const windowed = visibleDesc.slice(clampedScroll, clampedScroll + streamBodyRows);
|
|
7392
7430
|
const detailEntries = detail ? (() => {
|
|
7393
7431
|
const seen = /* @__PURE__ */ new Set();
|
|
7394
7432
|
const all = [];
|
|
@@ -7447,30 +7485,31 @@ function LivePanel({ active: active2 }) {
|
|
|
7447
7485
|
},
|
|
7448
7486
|
{ isActive: active2 && !editingSearch }
|
|
7449
7487
|
);
|
|
7450
|
-
const searchRow =
|
|
7451
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: "search: " }),
|
|
7452
|
-
/* @__PURE__ */
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
|
|
7462
|
-
|
|
7463
|
-
|
|
7464
|
-
|
|
7465
|
-
|
|
7466
|
-
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
/* @__PURE__ */
|
|
7471
|
-
|
|
7472
|
-
|
|
7473
|
-
|
|
7488
|
+
const searchRow = /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7489
|
+
/* @__PURE__ */ jsx2(Text2, { color: editingSearch ? theme.warn : theme.dim, bold: editingSearch, children: editingSearch ? "\u2315 search: " : "\u2315 search: " }),
|
|
7490
|
+
editingSearch ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
7491
|
+
/* @__PURE__ */ jsx2(
|
|
7492
|
+
TextInput,
|
|
7493
|
+
{
|
|
7494
|
+
value: search,
|
|
7495
|
+
onChange: (v) => {
|
|
7496
|
+
if (v.endsWith("/")) {
|
|
7497
|
+
setEditingSearch(false);
|
|
7498
|
+
return;
|
|
7499
|
+
}
|
|
7500
|
+
setSearch(v);
|
|
7501
|
+
setScroll(0);
|
|
7502
|
+
setDetailScroll(0);
|
|
7503
|
+
},
|
|
7504
|
+
onSubmit: () => setEditingSearch(false)
|
|
7505
|
+
}
|
|
7506
|
+
),
|
|
7507
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ` ${filtered.length} match \xB7 enter/\u200B/ done \xB7 empty clears` })
|
|
7508
|
+
] }) : search ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
7509
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, bold: true, children: search }),
|
|
7510
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ` ${filtered.length} match \xB7 press / to edit \xB7 type to clear` })
|
|
7511
|
+
] }) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "press / to filter the stream by tool, agent, command\u2026" })
|
|
7512
|
+
] });
|
|
7474
7513
|
const sessionDot = (st) => st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\u25CC", color: theme.warn } : { ch: "\u25CB", color: theme.dim };
|
|
7475
7514
|
void sessionDot;
|
|
7476
7515
|
if (s === null && localOn === null) {
|
|
@@ -7502,15 +7541,19 @@ function LivePanel({ active: active2 }) {
|
|
|
7502
7541
|
const first = d[d.length - 1]?.at;
|
|
7503
7542
|
const last = d[0]?.at;
|
|
7504
7543
|
const tlRows = Math.max(4, rows - 10);
|
|
7505
|
-
const
|
|
7544
|
+
const tlBody = Math.max(3, tlRows - 1);
|
|
7545
|
+
const maxD = Math.max(0, d.length - tlBody);
|
|
7506
7546
|
const dScroll = Math.min(detailScroll, maxD);
|
|
7507
|
-
const tl = d.slice(dScroll, dScroll +
|
|
7547
|
+
const tl = d.slice(dScroll, dScroll + tlBody);
|
|
7508
7548
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
7509
7549
|
titleBar,
|
|
7510
|
-
searchRow,
|
|
7511
7550
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7512
7551
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#AAC8FA", bold: true, children: ` SESSION ${detail.id.slice(0, 8)} ` }),
|
|
7513
|
-
|
|
7552
|
+
(() => {
|
|
7553
|
+
const st = STATUS_STYLE[sessStatus(detail.lastAt, nowMs)];
|
|
7554
|
+
return /* @__PURE__ */ jsx2(Text2, { color: st.color, bold: true, children: ` ${st.dot} ${st.label}` });
|
|
7555
|
+
})(),
|
|
7556
|
+
/* @__PURE__ */ jsx2(Text2, { color: detail.source === "local" ? theme.ok : "#4f6db8", children: " " + detail.source.toUpperCase() }),
|
|
7514
7557
|
/* @__PURE__ */ jsx2(Text2, { bold: true, children: " " + truncate2(detail.isMe ? detail.agent + " (this machine)" : detail.agent, 34) }),
|
|
7515
7558
|
detail.trust != null ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ` trust ${detail.trust}/100` }) : null,
|
|
7516
7559
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2190 back" })
|
|
@@ -7541,6 +7584,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7541
7584
|
] }),
|
|
7542
7585
|
/* @__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 }),
|
|
7543
7586
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: tlRows, overflow: "hidden", children: [
|
|
7587
|
+
searchRow,
|
|
7544
7588
|
tl.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: detailCloud.length === 0 ? spin + " loading history\u2026" : "no entries" }) : null,
|
|
7545
7589
|
tl.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e) }, e.id))
|
|
7546
7590
|
] }),
|
|
@@ -7574,7 +7618,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7574
7618
|
/* @__PURE__ */ jsx2(Text2, { color: guard.data?.up_to_date ? theme.ok : theme.warn, children: guard.data ? `v${guard.data.installed ?? "?"}${guard.data.up_to_date ? "" : "\u2192v" + guard.data.latest} ${guard.data.device_count}dev` : "\xB7" }),
|
|
7575
7619
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
7576
7620
|
] }),
|
|
7577
|
-
|
|
7621
|
+
alerts.filter((a) => nowMs < a.until).slice(-1).map((a) => /* @__PURE__ */ jsx2(Text2, { wrap: "truncate", backgroundColor: "#3d2a12", color: "#ffb454", bold: true, children: ` \u23F8 IDLE \xB7 ${truncate2(a.msg, innerW - 12)} ` }, a.id)),
|
|
7578
7622
|
/* @__PURE__ */ jsxs2(Box2, { height: 1 + chartH, children: [
|
|
7579
7623
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: leftW, marginRight: 2, children: [
|
|
7580
7624
|
/* @__PURE__ */ jsx2(PaneTitle, { label: "TRAFFIC", extra: `${trafficLabel} \xB7 peak ${Math.max(0, ...traffic)} \xB7 red = denials`, width: leftW }),
|
|
@@ -7612,14 +7656,14 @@ function LivePanel({ active: active2 }) {
|
|
|
7612
7656
|
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: mode === "pick" ? "\u2191\u2193 pick \xB7 enter open" : `${combinedSess.length} \xB7 s = inspect`, width: colW }),
|
|
7613
7657
|
pickable.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "scanning\u2026" }) : null,
|
|
7614
7658
|
pickable.map((r, i) => {
|
|
7615
|
-
const freshDot = nowMs - r.lastAt < 9e4;
|
|
7616
7659
|
const sel = mode === "pick" && i === pickIdx;
|
|
7660
|
+
const st = STATUS_STYLE[r.status];
|
|
7617
7661
|
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", backgroundColor: sel ? "#1c2f63" : void 0, bold: sel || r.isMe, children: [
|
|
7618
|
-
/* @__PURE__ */ jsxs2(Text2, { color: sel ? theme.accentBright :
|
|
7619
|
-
sel ? "\u25B8" :
|
|
7662
|
+
/* @__PURE__ */ jsxs2(Text2, { color: sel ? theme.accentBright : st.color, children: [
|
|
7663
|
+
sel ? "\u25B8" : st.dot,
|
|
7620
7664
|
" "
|
|
7621
7665
|
] }),
|
|
7622
|
-
/* @__PURE__ */ jsx2(Text2, { color:
|
|
7666
|
+
/* @__PURE__ */ jsx2(Text2, { color: st.color, children: st.label.padEnd(7) }),
|
|
7623
7667
|
/* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, children: truncate2(r.isMe ? "this machine" : r.agent, 12).padEnd(13) }),
|
|
7624
7668
|
/* @__PURE__ */ jsxs2(Text2, { children: [
|
|
7625
7669
|
String(r.calls).padStart(4),
|
|
@@ -7653,7 +7697,8 @@ function LivePanel({ active: active2 }) {
|
|
|
7653
7697
|
width: innerW
|
|
7654
7698
|
}
|
|
7655
7699
|
),
|
|
7656
|
-
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height:
|
|
7700
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: streamRows, overflow: "hidden", children: [
|
|
7701
|
+
searchRow,
|
|
7657
7702
|
localOn === false ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7658
7703
|
/* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: "local logs off" }),
|
|
7659
7704
|
/* @__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`" })
|
|
@@ -7671,7 +7716,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7671
7716
|
] })
|
|
7672
7717
|
] });
|
|
7673
7718
|
}
|
|
7674
|
-
var SPIN, BG, DIM_FLOOR, LOCAL_LOG, RING, STREAM_KEEP, hhmmss, fmtUp, FILTERS;
|
|
7719
|
+
var SPIN, BG, DIM_FLOOR, LOCAL_LOG, RING, STREAM_KEEP, hhmmss, fmtUp, FILTERS, sessStatus, STATUS_STYLE;
|
|
7675
7720
|
var init_Live = __esm({
|
|
7676
7721
|
"src/tui/panels/Live.tsx"() {
|
|
7677
7722
|
"use strict";
|
|
@@ -7694,6 +7739,12 @@ var init_Live = __esm({
|
|
|
7694
7739
|
return `${p(Math.floor(s / 3600))}:${p(Math.floor(s % 3600 / 60))}:${p(s % 60)}`;
|
|
7695
7740
|
};
|
|
7696
7741
|
FILTERS = ["all", "local", "cloud"];
|
|
7742
|
+
sessStatus = (lastAt, now) => now - lastAt < 6e4 ? "active" : now - lastAt < 3e5 ? "idle" : "ended";
|
|
7743
|
+
STATUS_STYLE = {
|
|
7744
|
+
active: { dot: "\u25CF", label: "ACTIVE", color: theme.ok },
|
|
7745
|
+
idle: { dot: "\u25D0", label: "IDLE", color: theme.warn },
|
|
7746
|
+
ended: { dot: "\u25CB", label: "ENDED", color: theme.dim }
|
|
7747
|
+
};
|
|
7697
7748
|
}
|
|
7698
7749
|
});
|
|
7699
7750
|
|
|
@@ -9635,7 +9686,7 @@ var init_global_install = __esm({
|
|
|
9635
9686
|
|
|
9636
9687
|
// src/login.ts
|
|
9637
9688
|
var login_exports = {};
|
|
9638
|
-
import { spawn } from "child_process";
|
|
9689
|
+
import { spawn as spawn2 } from "child_process";
|
|
9639
9690
|
function startSpinner(text) {
|
|
9640
9691
|
if (!process.stderr.isTTY) {
|
|
9641
9692
|
process.stderr.write(` ${text}
|
|
@@ -9671,7 +9722,7 @@ function openBrowser(url) {
|
|
|
9671
9722
|
try {
|
|
9672
9723
|
const cmd = process.platform === "win32" ? "cmd" : process.platform === "darwin" ? "open" : "xdg-open";
|
|
9673
9724
|
const args = process.platform === "win32" ? ["/c", "start", '""', url] : [url];
|
|
9674
|
-
const child =
|
|
9725
|
+
const child = spawn2(cmd, args, { stdio: "ignore", detached: true });
|
|
9675
9726
|
child.on("error", () => {
|
|
9676
9727
|
});
|
|
9677
9728
|
child.unref();
|
|
@@ -9790,7 +9841,7 @@ __export(shield_exports, {
|
|
|
9790
9841
|
});
|
|
9791
9842
|
import { createServer, request as httpRequest } from "http";
|
|
9792
9843
|
import { request as httpsRequest } from "https";
|
|
9793
|
-
import { spawn as
|
|
9844
|
+
import { spawn as spawn3 } from "child_process";
|
|
9794
9845
|
import { URL as URL2 } from "url";
|
|
9795
9846
|
import { readFileSync as readFileSync7, existsSync as existsSync5, readdirSync, statSync as statSync2 } from "fs";
|
|
9796
9847
|
import { resolve as resolve5 } from "path";
|
|
@@ -10038,7 +10089,7 @@ async function runShield() {
|
|
|
10038
10089
|
const upstream = pickUpstream();
|
|
10039
10090
|
const { port, close } = await startProxy(upstream);
|
|
10040
10091
|
log4(`redacting secrets on the LLM path \u2192 masking before ${upstream.host} (127.0.0.1:${port})`);
|
|
10041
|
-
const child =
|
|
10092
|
+
const child = spawn3(cmd[0], cmd.slice(1), {
|
|
10042
10093
|
stdio: "inherit",
|
|
10043
10094
|
env: { ...process.env, ANTHROPIC_BASE_URL: `http://127.0.0.1:${port}` },
|
|
10044
10095
|
shell: process.platform === "win32"
|
package/dist/tui/index.js
CHANGED
|
@@ -127,6 +127,7 @@ function KeyHints({ hints }) {
|
|
|
127
127
|
// src/tui/panels/Live.tsx
|
|
128
128
|
import { Box as Box2, Text as Text2, useInput } from "ink";
|
|
129
129
|
import TextInput from "ink-text-input";
|
|
130
|
+
import { spawn } from "child_process";
|
|
130
131
|
import { closeSync, openSync, readSync, statSync } from "fs";
|
|
131
132
|
import { homedir as homedir2 } from "os";
|
|
132
133
|
import { join as join2 } from "path";
|
|
@@ -222,26 +223,26 @@ async function request(method, path, opts = {}) {
|
|
|
222
223
|
}
|
|
223
224
|
const attempt = () => fetch(url, {
|
|
224
225
|
method,
|
|
225
|
-
headers,
|
|
226
|
+
headers: { ...headers, Connection: "close" },
|
|
226
227
|
body: bodyInit,
|
|
227
228
|
signal: AbortSignal.timeout(opts.timeoutMs ?? 15e3)
|
|
228
229
|
});
|
|
230
|
+
const maxTries = method === "GET" ? 3 : 1;
|
|
229
231
|
let res;
|
|
230
|
-
|
|
231
|
-
|
|
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));
|
|
232
|
+
let lastErr;
|
|
233
|
+
for (let i = 0; i < maxTries; i++) {
|
|
238
234
|
try {
|
|
239
235
|
res = await attempt();
|
|
236
|
+
break;
|
|
240
237
|
} catch (err) {
|
|
241
|
-
|
|
242
|
-
|
|
238
|
+
lastErr = err;
|
|
239
|
+
if (i < maxTries - 1) await new Promise((r) => setTimeout(r, 600 * (i + 1)));
|
|
243
240
|
}
|
|
244
241
|
}
|
|
242
|
+
if (!res) {
|
|
243
|
+
const msg = lastErr instanceof Error ? lastErr.message : String(lastErr);
|
|
244
|
+
throw new ApiError(0, "NETWORK_ERROR", `Cannot reach SolonGate API: ${msg}`);
|
|
245
|
+
}
|
|
245
246
|
const text = await res.text().catch(() => "");
|
|
246
247
|
let json = void 0;
|
|
247
248
|
if (text) {
|
|
@@ -444,7 +445,7 @@ function usePoll(reload, intervalMs, enabled = true) {
|
|
|
444
445
|
}
|
|
445
446
|
|
|
446
447
|
// src/tui/panels/Live.tsx
|
|
447
|
-
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
448
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
448
449
|
var SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
449
450
|
var BG = "#12234f";
|
|
450
451
|
var DIM_FLOOR = "#233457";
|
|
@@ -559,6 +560,12 @@ function StreamLine({ e, loc }) {
|
|
|
559
560
|
] });
|
|
560
561
|
}
|
|
561
562
|
var FILTERS = ["all", "local", "cloud"];
|
|
563
|
+
var sessStatus = (lastAt, now) => now - lastAt < 6e4 ? "active" : now - lastAt < 3e5 ? "idle" : "ended";
|
|
564
|
+
var STATUS_STYLE = {
|
|
565
|
+
active: { dot: "\u25CF", label: "ACTIVE", color: theme.ok },
|
|
566
|
+
idle: { dot: "\u25D0", label: "IDLE", color: theme.warn },
|
|
567
|
+
ended: { dot: "\u25CB", label: "ENDED", color: theme.dim }
|
|
568
|
+
};
|
|
562
569
|
function LivePanel({ active: active2 }) {
|
|
563
570
|
const [s, setS] = useState2(null);
|
|
564
571
|
const [lat, setLat] = useState2([]);
|
|
@@ -579,18 +586,26 @@ function LivePanel({ active: active2 }) {
|
|
|
579
586
|
const seenRef = useRef2(/* @__PURE__ */ new Set());
|
|
580
587
|
const lastLocalTs = useRef2(0);
|
|
581
588
|
const pausedUntil = useRef2(0);
|
|
589
|
+
const sessRef = useRef2([]);
|
|
590
|
+
const prevStatus = useRef2(/* @__PURE__ */ new Map());
|
|
591
|
+
const [alerts, setAlerts] = useState2([]);
|
|
582
592
|
const [frozen, setFrozen] = useState2(false);
|
|
583
593
|
const frozenRef = useRef2(false);
|
|
584
594
|
frozenRef.current = frozen;
|
|
585
595
|
const pushLog = useCallback2((msg, level = "ok") => {
|
|
586
596
|
setLog((prev) => [...prev.slice(-59), { ts: Date.now(), msg, level }]);
|
|
587
597
|
}, []);
|
|
598
|
+
const netFails = useRef2(0);
|
|
588
599
|
const onApiError = useCallback2(
|
|
589
600
|
(e) => {
|
|
590
601
|
const is429 = e instanceof ApiError && e.status === 429;
|
|
602
|
+
const isNet = e instanceof ApiError && e.status === 0;
|
|
591
603
|
if (is429 && Date.now() >= pausedUntil.current) {
|
|
592
604
|
pausedUntil.current = Date.now() + 3e4;
|
|
593
605
|
pushLog("rate limited by api \xB7 backing off 30s", "warn");
|
|
606
|
+
} else if (isNet) {
|
|
607
|
+
netFails.current += 1;
|
|
608
|
+
if (netFails.current === 3) pushLog("network unreachable \xB7 retrying\u2026", "warn");
|
|
594
609
|
} else if (!is429) {
|
|
595
610
|
pushLog(`api error \xB7 ${truncate(e instanceof Error ? e.message : String(e), 40)}`, "bad");
|
|
596
611
|
}
|
|
@@ -666,6 +681,8 @@ function LivePanel({ active: active2 }) {
|
|
|
666
681
|
try {
|
|
667
682
|
const fd = await api.audit.list({ limit: 50 });
|
|
668
683
|
if (frozenRef.current) return;
|
|
684
|
+
if (netFails.current >= 3) pushLog("network recovered", "ok");
|
|
685
|
+
netFails.current = 0;
|
|
669
686
|
const ms = Date.now() - t0;
|
|
670
687
|
setLat((l) => [...l, ms].slice(-240));
|
|
671
688
|
const fresh = fd.entries.filter((e) => !seenRef.current.has(e.id));
|
|
@@ -732,6 +749,30 @@ function LivePanel({ active: active2 }) {
|
|
|
732
749
|
return () => clearInterval(t);
|
|
733
750
|
}, [active2, frozen]);
|
|
734
751
|
const startRef = useRef2(Date.now());
|
|
752
|
+
useEffect2(() => {
|
|
753
|
+
if (!active2) return;
|
|
754
|
+
for (const r of sessRef.current) {
|
|
755
|
+
const prev = prevStatus.current.get(r.id);
|
|
756
|
+
if (prev && prev === "active" && r.status === "idle") {
|
|
757
|
+
const name = r.isMe ? `${r.agent} (this machine)` : r.agent;
|
|
758
|
+
const msg = `${name} went IDLE \u2014 no tool call for 60s`;
|
|
759
|
+
pushLog(`\u23F8 ${msg}`, "warn");
|
|
760
|
+
setAlerts((a) => [...a.filter((x) => x.id !== r.id), { id: r.id, msg, until: Date.now() + 15e3 }].slice(-4));
|
|
761
|
+
try {
|
|
762
|
+
process.stdout.write("\x07");
|
|
763
|
+
} catch {
|
|
764
|
+
}
|
|
765
|
+
try {
|
|
766
|
+
const child = spawn("notify-send", ["-a", "SolonGate", "Agent idle", msg], { stdio: "ignore", detached: true });
|
|
767
|
+
child.on("error", () => {
|
|
768
|
+
});
|
|
769
|
+
child.unref();
|
|
770
|
+
} catch {
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
prevStatus.current.set(r.id, r.status);
|
|
774
|
+
}
|
|
775
|
+
}, [tick, active2, pushLog]);
|
|
735
776
|
const cols = process.stdout.columns ?? 100;
|
|
736
777
|
const rows = process.stdout.rows ?? 30;
|
|
737
778
|
const ins = insights.data ?? {};
|
|
@@ -784,7 +825,7 @@ function LivePanel({ active: active2 }) {
|
|
|
784
825
|
localSess.set(e.session, cur);
|
|
785
826
|
}
|
|
786
827
|
const combinedSess = [
|
|
787
|
-
...[...localSess.entries()].sort((a, b) => b[1].lastAt - a[1].lastAt).map(([id, v]) => ({ source: "local", id, agent: v.agent, calls: v.calls, denies: v.denies, lastAt: v.lastAt, isMe: ring?.session === id })),
|
|
828
|
+
...[...localSess.entries()].sort((a, b) => b[1].lastAt - a[1].lastAt).map(([id, v]) => ({ source: "local", id, agent: v.agent, calls: v.calls, denies: v.denies, lastAt: v.lastAt, status: sessStatus(v.lastAt, nowMs), isMe: ring?.session === id })),
|
|
788
829
|
...sess.filter((a) => !localSess.has(a.session_id)).map((a) => ({
|
|
789
830
|
source: "cloud",
|
|
790
831
|
id: a.session_id,
|
|
@@ -792,10 +833,12 @@ function LivePanel({ active: active2 }) {
|
|
|
792
833
|
calls: a.total_calls,
|
|
793
834
|
denies: a.denied_calls,
|
|
794
835
|
lastAt: Date.parse(a.last_seen_at),
|
|
836
|
+
status: sessStatus(Date.parse(a.last_seen_at), nowMs),
|
|
795
837
|
trust: a.trust_score,
|
|
796
838
|
isMe: false
|
|
797
839
|
}))
|
|
798
840
|
];
|
|
841
|
+
sessRef.current = combinedSess;
|
|
799
842
|
const localSessIds = new Set(localSess.keys());
|
|
800
843
|
if (ring?.session) localSessIds.add(ring.session);
|
|
801
844
|
const isLoc = (e) => e.source === "local" || !!e.session && localSessIds.has(e.session);
|
|
@@ -811,9 +854,10 @@ function LivePanel({ active: active2 }) {
|
|
|
811
854
|
const rightW = innerW - leftW - 2;
|
|
812
855
|
const colW = Math.max(20, Math.floor((innerW - 4) / 3));
|
|
813
856
|
const pickable = combinedSess.slice(0, colH - 1);
|
|
814
|
-
const
|
|
857
|
+
const streamBodyRows = Math.max(3, streamRows - 1);
|
|
858
|
+
const maxScroll = Math.max(0, visibleDesc.length - streamBodyRows);
|
|
815
859
|
const clampedScroll = Math.min(scroll, maxScroll);
|
|
816
|
-
const windowed = visibleDesc.slice(clampedScroll, clampedScroll +
|
|
860
|
+
const windowed = visibleDesc.slice(clampedScroll, clampedScroll + streamBodyRows);
|
|
817
861
|
const detailEntries = detail ? (() => {
|
|
818
862
|
const seen = /* @__PURE__ */ new Set();
|
|
819
863
|
const all = [];
|
|
@@ -872,30 +916,31 @@ function LivePanel({ active: active2 }) {
|
|
|
872
916
|
},
|
|
873
917
|
{ isActive: active2 && !editingSearch }
|
|
874
918
|
);
|
|
875
|
-
const searchRow =
|
|
876
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: "search: " }),
|
|
877
|
-
/* @__PURE__ */
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
/* @__PURE__ */
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
919
|
+
const searchRow = /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
920
|
+
/* @__PURE__ */ jsx2(Text2, { color: editingSearch ? theme.warn : theme.dim, bold: editingSearch, children: editingSearch ? "\u2315 search: " : "\u2315 search: " }),
|
|
921
|
+
editingSearch ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
922
|
+
/* @__PURE__ */ jsx2(
|
|
923
|
+
TextInput,
|
|
924
|
+
{
|
|
925
|
+
value: search,
|
|
926
|
+
onChange: (v) => {
|
|
927
|
+
if (v.endsWith("/")) {
|
|
928
|
+
setEditingSearch(false);
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
setSearch(v);
|
|
932
|
+
setScroll(0);
|
|
933
|
+
setDetailScroll(0);
|
|
934
|
+
},
|
|
935
|
+
onSubmit: () => setEditingSearch(false)
|
|
936
|
+
}
|
|
937
|
+
),
|
|
938
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ` ${filtered.length} match \xB7 enter/\u200B/ done \xB7 empty clears` })
|
|
939
|
+
] }) : search ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
940
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, bold: true, children: search }),
|
|
941
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ` ${filtered.length} match \xB7 press / to edit \xB7 type to clear` })
|
|
942
|
+
] }) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "press / to filter the stream by tool, agent, command\u2026" })
|
|
943
|
+
] });
|
|
899
944
|
const sessionDot = (st) => st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\u25CC", color: theme.warn } : { ch: "\u25CB", color: theme.dim };
|
|
900
945
|
void sessionDot;
|
|
901
946
|
if (s === null && localOn === null) {
|
|
@@ -927,15 +972,19 @@ function LivePanel({ active: active2 }) {
|
|
|
927
972
|
const first = d[d.length - 1]?.at;
|
|
928
973
|
const last = d[0]?.at;
|
|
929
974
|
const tlRows = Math.max(4, rows - 10);
|
|
930
|
-
const
|
|
975
|
+
const tlBody = Math.max(3, tlRows - 1);
|
|
976
|
+
const maxD = Math.max(0, d.length - tlBody);
|
|
931
977
|
const dScroll = Math.min(detailScroll, maxD);
|
|
932
|
-
const tl = d.slice(dScroll, dScroll +
|
|
978
|
+
const tl = d.slice(dScroll, dScroll + tlBody);
|
|
933
979
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
934
980
|
titleBar,
|
|
935
|
-
searchRow,
|
|
936
981
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
937
982
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#AAC8FA", bold: true, children: ` SESSION ${detail.id.slice(0, 8)} ` }),
|
|
938
|
-
|
|
983
|
+
(() => {
|
|
984
|
+
const st = STATUS_STYLE[sessStatus(detail.lastAt, nowMs)];
|
|
985
|
+
return /* @__PURE__ */ jsx2(Text2, { color: st.color, bold: true, children: ` ${st.dot} ${st.label}` });
|
|
986
|
+
})(),
|
|
987
|
+
/* @__PURE__ */ jsx2(Text2, { color: detail.source === "local" ? theme.ok : "#4f6db8", children: " " + detail.source.toUpperCase() }),
|
|
939
988
|
/* @__PURE__ */ jsx2(Text2, { bold: true, children: " " + truncate(detail.isMe ? detail.agent + " (this machine)" : detail.agent, 34) }),
|
|
940
989
|
detail.trust != null ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ` trust ${detail.trust}/100` }) : null,
|
|
941
990
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2190 back" })
|
|
@@ -966,6 +1015,7 @@ function LivePanel({ active: active2 }) {
|
|
|
966
1015
|
] }),
|
|
967
1016
|
/* @__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 }),
|
|
968
1017
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: tlRows, overflow: "hidden", children: [
|
|
1018
|
+
searchRow,
|
|
969
1019
|
tl.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: detailCloud.length === 0 ? spin + " loading history\u2026" : "no entries" }) : null,
|
|
970
1020
|
tl.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e) }, e.id))
|
|
971
1021
|
] }),
|
|
@@ -999,7 +1049,7 @@ function LivePanel({ active: active2 }) {
|
|
|
999
1049
|
/* @__PURE__ */ jsx2(Text2, { color: guard.data?.up_to_date ? theme.ok : theme.warn, children: guard.data ? `v${guard.data.installed ?? "?"}${guard.data.up_to_date ? "" : "\u2192v" + guard.data.latest} ${guard.data.device_count}dev` : "\xB7" }),
|
|
1000
1050
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
1001
1051
|
] }),
|
|
1002
|
-
|
|
1052
|
+
alerts.filter((a) => nowMs < a.until).slice(-1).map((a) => /* @__PURE__ */ jsx2(Text2, { wrap: "truncate", backgroundColor: "#3d2a12", color: "#ffb454", bold: true, children: ` \u23F8 IDLE \xB7 ${truncate(a.msg, innerW - 12)} ` }, a.id)),
|
|
1003
1053
|
/* @__PURE__ */ jsxs2(Box2, { height: 1 + chartH, children: [
|
|
1004
1054
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: leftW, marginRight: 2, children: [
|
|
1005
1055
|
/* @__PURE__ */ jsx2(PaneTitle, { label: "TRAFFIC", extra: `${trafficLabel} \xB7 peak ${Math.max(0, ...traffic)} \xB7 red = denials`, width: leftW }),
|
|
@@ -1037,14 +1087,14 @@ function LivePanel({ active: active2 }) {
|
|
|
1037
1087
|
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: mode === "pick" ? "\u2191\u2193 pick \xB7 enter open" : `${combinedSess.length} \xB7 s = inspect`, width: colW }),
|
|
1038
1088
|
pickable.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "scanning\u2026" }) : null,
|
|
1039
1089
|
pickable.map((r, i) => {
|
|
1040
|
-
const freshDot = nowMs - r.lastAt < 9e4;
|
|
1041
1090
|
const sel = mode === "pick" && i === pickIdx;
|
|
1091
|
+
const st = STATUS_STYLE[r.status];
|
|
1042
1092
|
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", backgroundColor: sel ? "#1c2f63" : void 0, bold: sel || r.isMe, children: [
|
|
1043
|
-
/* @__PURE__ */ jsxs2(Text2, { color: sel ? theme.accentBright :
|
|
1044
|
-
sel ? "\u25B8" :
|
|
1093
|
+
/* @__PURE__ */ jsxs2(Text2, { color: sel ? theme.accentBright : st.color, children: [
|
|
1094
|
+
sel ? "\u25B8" : st.dot,
|
|
1045
1095
|
" "
|
|
1046
1096
|
] }),
|
|
1047
|
-
/* @__PURE__ */ jsx2(Text2, { color:
|
|
1097
|
+
/* @__PURE__ */ jsx2(Text2, { color: st.color, children: st.label.padEnd(7) }),
|
|
1048
1098
|
/* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, children: truncate(r.isMe ? "this machine" : r.agent, 12).padEnd(13) }),
|
|
1049
1099
|
/* @__PURE__ */ jsxs2(Text2, { children: [
|
|
1050
1100
|
String(r.calls).padStart(4),
|
|
@@ -1078,7 +1128,8 @@ function LivePanel({ active: active2 }) {
|
|
|
1078
1128
|
width: innerW
|
|
1079
1129
|
}
|
|
1080
1130
|
),
|
|
1081
|
-
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height:
|
|
1131
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: streamRows, overflow: "hidden", children: [
|
|
1132
|
+
searchRow,
|
|
1082
1133
|
localOn === false ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
1083
1134
|
/* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: "local logs off" }),
|
|
1084
1135
|
/* @__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`" })
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.76.1",
|
|
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": {
|