@solongate/proxy 0.80.0 → 0.81.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/global-install.js +5 -3
- package/dist/index.js +338 -142
- package/dist/login.js +5 -3
- package/dist/tui/config.d.ts +6 -0
- package/dist/tui/index.js +230 -55
- package/dist/tui/panels/Agents.d.ts +4 -0
- package/dist/tui/theme.d.ts +2 -3
- package/package.json +1 -1
package/dist/login.js
CHANGED
|
@@ -217,12 +217,14 @@ async function runGlobalInstall(opts = {}) {
|
|
|
217
217
|
const auditAbs = join(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
|
|
218
218
|
const stopAbs = join(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
|
|
219
219
|
const nodeBin = process.execPath.replace(/\\/g, "/");
|
|
220
|
+
const call = process.platform === "win32" ? "& " : "";
|
|
221
|
+
const hookCmd = (script) => `${call}"${nodeBin}" "${script}" claude-code "Claude Code"`;
|
|
220
222
|
const merged = {
|
|
221
223
|
...existing,
|
|
222
224
|
hooks: {
|
|
223
|
-
PreToolUse: [{ matcher: "", hooks: [{ type: "command", command:
|
|
224
|
-
PostToolUse: [{ matcher: "", hooks: [{ type: "command", command:
|
|
225
|
-
Stop: [{ matcher: "", hooks: [{ type: "command", command:
|
|
225
|
+
PreToolUse: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(guardAbs) }] }],
|
|
226
|
+
PostToolUse: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(auditAbs) }] }],
|
|
227
|
+
Stop: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(stopAbs) }] }]
|
|
226
228
|
}
|
|
227
229
|
};
|
|
228
230
|
writeFileSync(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
|
package/dist/tui/index.js
CHANGED
|
@@ -8,8 +8,8 @@ var __export = (target, all) => {
|
|
|
8
8
|
import { render } from "ink";
|
|
9
9
|
|
|
10
10
|
// src/tui/App.tsx
|
|
11
|
-
import { Box as
|
|
12
|
-
import { useState as
|
|
11
|
+
import { Box as Box9, Text as Text9, useApp, useInput as useInput7 } from "ink";
|
|
12
|
+
import { useState as useState8 } from "react";
|
|
13
13
|
|
|
14
14
|
// src/cli-utils.ts
|
|
15
15
|
var c = {
|
|
@@ -44,10 +44,33 @@ var BANNER_COLORS = [c.blue1, c.blue2, c.blue3, c.blue4, c.blue5, c.blue6];
|
|
|
44
44
|
// src/tui/components.tsx
|
|
45
45
|
import { Box, Text } from "ink";
|
|
46
46
|
|
|
47
|
+
// src/tui/config.ts
|
|
48
|
+
import { readFileSync } from "fs";
|
|
49
|
+
import { homedir } from "os";
|
|
50
|
+
import { join } from "path";
|
|
51
|
+
var cached = null;
|
|
52
|
+
function loadConfig() {
|
|
53
|
+
if (cached) return cached;
|
|
54
|
+
const defaults = { notifications: true };
|
|
55
|
+
try {
|
|
56
|
+
const raw = readFileSync(join(homedir(), ".solongate", "tui-config.json"), "utf-8");
|
|
57
|
+
const j = JSON.parse(raw);
|
|
58
|
+
cached = {
|
|
59
|
+
notifications: j.notifications !== false,
|
|
60
|
+
accent: typeof j.accent === "string" ? j.accent : void 0,
|
|
61
|
+
pollMs: typeof j.pollMs === "number" ? j.pollMs : void 0
|
|
62
|
+
};
|
|
63
|
+
} catch {
|
|
64
|
+
cached = defaults;
|
|
65
|
+
}
|
|
66
|
+
return cached;
|
|
67
|
+
}
|
|
68
|
+
|
|
47
69
|
// src/tui/theme.ts
|
|
70
|
+
var accent = loadConfig().accent;
|
|
48
71
|
var theme = {
|
|
49
|
-
accent: "cyan",
|
|
50
|
-
accentBright: "#5a8ce6",
|
|
72
|
+
accent: accent || "cyan",
|
|
73
|
+
accentBright: accent || "#5a8ce6",
|
|
51
74
|
ok: "green",
|
|
52
75
|
warn: "yellow",
|
|
53
76
|
bad: "red",
|
|
@@ -128,15 +151,15 @@ function KeyHints({ hints }) {
|
|
|
128
151
|
import { Box as Box2, Text as Text2, useInput } from "ink";
|
|
129
152
|
import TextInput from "ink-text-input";
|
|
130
153
|
import { spawn } from "child_process";
|
|
131
|
-
import { closeSync, openSync, readSync, statSync } from "fs";
|
|
132
|
-
import { homedir as
|
|
133
|
-
import { join as
|
|
154
|
+
import { closeSync, mkdirSync, openSync, readSync, statSync, writeFileSync } from "fs";
|
|
155
|
+
import { homedir as homedir3 } from "os";
|
|
156
|
+
import { join as join3 } from "path";
|
|
134
157
|
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
135
158
|
|
|
136
159
|
// src/api-client/client.ts
|
|
137
|
-
import { readFileSync, existsSync } from "fs";
|
|
138
|
-
import { resolve, join } from "path";
|
|
139
|
-
import { homedir } from "os";
|
|
160
|
+
import { readFileSync as readFileSync2, existsSync } from "fs";
|
|
161
|
+
import { resolve, join as join2 } from "path";
|
|
162
|
+
import { homedir as homedir2 } from "os";
|
|
140
163
|
var DEFAULT_API_URL = "https://api.solongate.com";
|
|
141
164
|
var ApiError = class extends Error {
|
|
142
165
|
status;
|
|
@@ -156,9 +179,9 @@ var NotAuthenticatedError = class extends Error {
|
|
|
156
179
|
};
|
|
157
180
|
function loginCredentialFile() {
|
|
158
181
|
try {
|
|
159
|
-
const p =
|
|
182
|
+
const p = join2(homedir2(), ".solongate", "cloud-guard.json");
|
|
160
183
|
if (!existsSync(p)) return {};
|
|
161
|
-
const c2 = JSON.parse(
|
|
184
|
+
const c2 = JSON.parse(readFileSync2(p, "utf-8"));
|
|
162
185
|
return c2 && typeof c2 === "object" ? c2 : {};
|
|
163
186
|
} catch {
|
|
164
187
|
return {};
|
|
@@ -168,7 +191,7 @@ function dotenvApiKey() {
|
|
|
168
191
|
try {
|
|
169
192
|
const envPath = resolve(".env");
|
|
170
193
|
if (!existsSync(envPath)) return void 0;
|
|
171
|
-
for (const line of
|
|
194
|
+
for (const line of readFileSync2(envPath, "utf-8").split("\n")) {
|
|
172
195
|
const trimmed = line.trim();
|
|
173
196
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
174
197
|
const eq = trimmed.indexOf("=");
|
|
@@ -181,7 +204,7 @@ function dotenvApiKey() {
|
|
|
181
204
|
}
|
|
182
205
|
return void 0;
|
|
183
206
|
}
|
|
184
|
-
var
|
|
207
|
+
var cached2 = null;
|
|
185
208
|
function isAuthenticated() {
|
|
186
209
|
try {
|
|
187
210
|
resolveCredentials();
|
|
@@ -191,13 +214,13 @@ function isAuthenticated() {
|
|
|
191
214
|
}
|
|
192
215
|
}
|
|
193
216
|
function resolveCredentials(apiUrlOverride) {
|
|
194
|
-
if (
|
|
217
|
+
if (cached2 && !apiUrlOverride) return cached2;
|
|
195
218
|
const file = loginCredentialFile();
|
|
196
219
|
const apiKey = process.env["SOLONGATE_API_KEY"] || file.apiKey || dotenvApiKey();
|
|
197
220
|
if (!apiKey) throw new NotAuthenticatedError();
|
|
198
221
|
const apiUrl = apiUrlOverride || process.env["SOLONGATE_API_URL"] || file.apiUrl || DEFAULT_API_URL;
|
|
199
222
|
const creds = { apiKey, apiUrl: apiUrl.replace(/\/$/, "") };
|
|
200
|
-
if (!apiUrlOverride)
|
|
223
|
+
if (!apiUrlOverride) cached2 = creds;
|
|
201
224
|
return creds;
|
|
202
225
|
}
|
|
203
226
|
function buildUrl(base, path, query) {
|
|
@@ -508,11 +531,12 @@ function usePoll(reload, intervalMs, enabled = true) {
|
|
|
508
531
|
|
|
509
532
|
// src/tui/panels/Live.tsx
|
|
510
533
|
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
534
|
+
var CONFIG = loadConfig();
|
|
511
535
|
var SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
512
536
|
var BG = "#12234f";
|
|
513
537
|
var DIM_FLOOR = "#233457";
|
|
514
|
-
var LOCAL_LOG =
|
|
515
|
-
var RING =
|
|
538
|
+
var LOCAL_LOG = join3(homedir3(), ".solongate", "local-logs", "solongate-audit.jsonl");
|
|
539
|
+
var RING = join3(process.cwd(), ".solongate", ".eval-ring.jsonl");
|
|
516
540
|
var hhmmss = (ts) => {
|
|
517
541
|
const d = new Date(ts);
|
|
518
542
|
return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
|
|
@@ -836,6 +860,7 @@ function LivePanel({ active: active2 }) {
|
|
|
836
860
|
(id, title, msg, level = "warn") => {
|
|
837
861
|
pushLog(`${level === "bad" ? "\u26D4" : "\u23F8"} ${msg}`, level);
|
|
838
862
|
setAlerts((a) => [...a.filter((x) => x.id !== id), { id, msg, level, until: Date.now() + 15e3 }].slice(-4));
|
|
863
|
+
if (!CONFIG.notifications) return;
|
|
839
864
|
try {
|
|
840
865
|
process.stdout.write("\x07");
|
|
841
866
|
} catch {
|
|
@@ -1063,7 +1088,16 @@ function LivePanel({ active: active2 }) {
|
|
|
1063
1088
|
else if (input === "d") toggleSignal("deny");
|
|
1064
1089
|
else if (input === "x") toggleSignal("dlp");
|
|
1065
1090
|
else if (input === "r") toggleSignal("ratelimit");
|
|
1066
|
-
else if (
|
|
1091
|
+
else if (input === "e") {
|
|
1092
|
+
const file = join3(homedir3(), ".solongate", "live-export.jsonl");
|
|
1093
|
+
try {
|
|
1094
|
+
mkdirSync(join3(homedir3(), ".solongate"), { recursive: true });
|
|
1095
|
+
writeFileSync(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
|
|
1096
|
+
setActionMsg({ text: `\u2713 exported ${visibleDesc.length} lines \u2192 ${file}`, level: "ok", until: Date.now() + 6e3 });
|
|
1097
|
+
} catch (err) {
|
|
1098
|
+
setActionMsg({ text: "\u2717 export failed: " + (err instanceof Error ? err.message : String(err)), level: "bad", until: Date.now() + 6e3 });
|
|
1099
|
+
}
|
|
1100
|
+
} else if (key.downArrow) setSel((n) => Math.min(visibleDesc.length - 1, n + 1));
|
|
1067
1101
|
else if (key.upArrow) setSel((n) => Math.max(0, n - 1));
|
|
1068
1102
|
else if (key.pageDown) setSel((n) => Math.min(visibleDesc.length - 1, n + streamBodyRows));
|
|
1069
1103
|
else if (key.pageUp) setSel((n) => Math.max(0, n - streamBodyRows));
|
|
@@ -1317,7 +1351,7 @@ function LivePanel({ active: active2 }) {
|
|
|
1317
1351
|
import { Box as Box3, Text as Text3, useInput as useInput2 } from "ink";
|
|
1318
1352
|
import TextInput2 from "ink-text-input";
|
|
1319
1353
|
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
1320
|
-
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1354
|
+
import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1321
1355
|
var constr = (r, g, side) => (r[g]?.[side] ?? []).join(", ");
|
|
1322
1356
|
var setConstr = (r, g, side, val) => {
|
|
1323
1357
|
const arr = val.split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -1368,6 +1402,11 @@ function PoliciesPanel({ focused }) {
|
|
|
1368
1402
|
() => view === "versions" && selected ? api.policies.versions(selected.id, { limit: 50 }) : Promise.resolve(null),
|
|
1369
1403
|
[view, selected?.id]
|
|
1370
1404
|
);
|
|
1405
|
+
const diffVer = versionsQ.data?.versions[vi]?.version;
|
|
1406
|
+
const diffQ = useLoader(
|
|
1407
|
+
() => view === "versions" && selected && diffVer !== void 0 ? api.policies.get(selected.id, diffVer) : Promise.resolve(null),
|
|
1408
|
+
[view, selected?.id, diffVer]
|
|
1409
|
+
);
|
|
1371
1410
|
useEffect3(() => {
|
|
1372
1411
|
if (detail.data) {
|
|
1373
1412
|
setRules(detail.data.rules);
|
|
@@ -1571,6 +1610,23 @@ function PoliciesPanel({ focused }) {
|
|
|
1571
1610
|
])
|
|
1572
1611
|
}
|
|
1573
1612
|
) }),
|
|
1613
|
+
/* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
|
|
1614
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: `Diff v${diffVer ?? "?"} vs current draft` }),
|
|
1615
|
+
(() => {
|
|
1616
|
+
const old = diffQ.data?.rules ?? [];
|
|
1617
|
+
const cur = rules;
|
|
1618
|
+
const oldIds = new Set(old.map((r) => r.id));
|
|
1619
|
+
const curIds = new Set(cur.map((r) => r.id));
|
|
1620
|
+
const added = cur.filter((r) => !oldIds.has(r.id));
|
|
1621
|
+
const removed = old.filter((r) => !curIds.has(r.id));
|
|
1622
|
+
if (diffQ.loading && !diffQ.data) return /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: " loading\u2026" });
|
|
1623
|
+
if (!added.length && !removed.length) return /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: " identical rule set" });
|
|
1624
|
+
return /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
1625
|
+
added.slice(0, 4).map((r) => /* @__PURE__ */ jsx3(Text3, { wrap: "truncate", color: theme.ok, children: ` + ${r.effect} ${truncate(r.toolPattern, 18)} ${truncate(r.description || r.id, 30)}` }, "a" + r.id)),
|
|
1626
|
+
removed.slice(0, 4).map((r) => /* @__PURE__ */ jsx3(Text3, { wrap: "truncate", color: theme.bad, children: ` - ${r.effect} ${truncate(r.toolPattern, 18)} ${truncate(r.description || r.id, 30)}` }, "r" + r.id))
|
|
1627
|
+
] });
|
|
1628
|
+
})()
|
|
1629
|
+
] }),
|
|
1574
1630
|
status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
1575
1631
|
] }) });
|
|
1576
1632
|
}
|
|
@@ -1687,6 +1743,20 @@ function RateLimitPanel({ focused }) {
|
|
|
1687
1743
|
/* @__PURE__ */ jsx4(FieldRow, { label: "Per hour", active: focused && fi === 2, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perHour === 0 ? "off" : draft.perHour }) }),
|
|
1688
1744
|
/* @__PURE__ */ jsx4(FieldRow, { label: "Per day", active: focused && fi === 3, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perDay === 0 ? "off" : draft.perDay }) })
|
|
1689
1745
|
] }),
|
|
1746
|
+
draft.perMinute > 0 ? (() => {
|
|
1747
|
+
const now = minuteSeries.slice(-1)[0] ?? 0;
|
|
1748
|
+
const pct = Math.min(100, Math.round(now / draft.perMinute * 100));
|
|
1749
|
+
const width = 30;
|
|
1750
|
+
const filled = Math.min(width, Math.round(now / draft.perMinute * width));
|
|
1751
|
+
const col = pct >= 100 ? theme.bad : pct >= 80 ? theme.warn : theme.ok;
|
|
1752
|
+
return /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
|
|
1753
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "load " }),
|
|
1754
|
+
/* @__PURE__ */ jsx4(Text4, { color: col, children: "\u2588".repeat(filled) }),
|
|
1755
|
+
/* @__PURE__ */ jsx4(Text4, { color: "#233457", children: "\u2591".repeat(Math.max(0, width - filled)) }),
|
|
1756
|
+
/* @__PURE__ */ jsx4(Text4, { color: col, children: ` ${now}/${draft.perMinute} ` }),
|
|
1757
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: `(${pct}% this min)` })
|
|
1758
|
+
] });
|
|
1759
|
+
})() : null,
|
|
1690
1760
|
dirty || status ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
|
|
1691
1761
|
dirty ? /* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: "\u25CF unsaved \u2014 press s to apply " }) : null,
|
|
1692
1762
|
status ? /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
@@ -1899,9 +1969,27 @@ function DlpPanel({ focused }) {
|
|
|
1899
1969
|
}
|
|
1900
1970
|
)
|
|
1901
1971
|
] }) : null,
|
|
1972
|
+
adding === "re" ? /* @__PURE__ */ jsx5(RegexTest, { re: newRe }) : null,
|
|
1902
1973
|
status ? /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) }) : null
|
|
1903
1974
|
] }) : null });
|
|
1904
1975
|
}
|
|
1976
|
+
var SAMPLES = ["AKIA1234567890ABCD00", "sk-ant-api03-xxxxxxxx", "ghp_16chars0000000000000000000000000000", "password=hunter2", "Bearer eyJhbGciOi"];
|
|
1977
|
+
function RegexTest({ re }) {
|
|
1978
|
+
if (!re.trim()) return /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " regex: type a pattern\u2026" });
|
|
1979
|
+
let rx = null;
|
|
1980
|
+
let err = "";
|
|
1981
|
+
try {
|
|
1982
|
+
rx = new RegExp(re);
|
|
1983
|
+
} catch (e) {
|
|
1984
|
+
err = e instanceof Error ? e.message : String(e);
|
|
1985
|
+
}
|
|
1986
|
+
if (!rx) return /* @__PURE__ */ jsx5(Text5, { color: theme.bad, children: ` \u2717 invalid: ${err.slice(0, 50)}` });
|
|
1987
|
+
const hits = SAMPLES.filter((s) => rx.test(s));
|
|
1988
|
+
return /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
|
|
1989
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.ok, children: " \u2713 valid" }),
|
|
1990
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: hits.length ? ` matches ${hits.length}/${SAMPLES.length} samples: ${hits.map((h) => h.slice(0, 8)).join(", ")}` : " (no sample match \u2014 will only catch your custom secrets)" })
|
|
1991
|
+
] });
|
|
1992
|
+
}
|
|
1905
1993
|
|
|
1906
1994
|
// src/tui/panels/Stats.tsx
|
|
1907
1995
|
import { Box as Box6, Text as Text6 } from "ink";
|
|
@@ -2176,22 +2264,109 @@ function Detail({ label, value, color }) {
|
|
|
2176
2264
|
] });
|
|
2177
2265
|
}
|
|
2178
2266
|
|
|
2267
|
+
// src/tui/panels/Agents.tsx
|
|
2268
|
+
import { Box as Box8, Text as Text8, useInput as useInput6 } from "ink";
|
|
2269
|
+
import { useState as useState7 } from "react";
|
|
2270
|
+
import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2271
|
+
var statusColor = (s) => s === "active" ? theme.ok : s === "idle" ? theme.warn : theme.dim;
|
|
2272
|
+
var sevColor = (s) => s === "high" ? theme.bad : s === "medium" ? theme.warn : theme.dim;
|
|
2273
|
+
function Bar({ label, value, max = 100, width = 16, color = theme.accent }) {
|
|
2274
|
+
const filled = max > 0 ? Math.round(Math.min(value, max) / max * width) : 0;
|
|
2275
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
2276
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: label.padEnd(9) }),
|
|
2277
|
+
/* @__PURE__ */ jsx8(Text8, { color, children: "\u2588".repeat(filled) }),
|
|
2278
|
+
/* @__PURE__ */ jsx8(Text8, { color: "#233457", children: "\u2591".repeat(Math.max(0, width - filled)) }),
|
|
2279
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " " + value })
|
|
2280
|
+
] });
|
|
2281
|
+
}
|
|
2282
|
+
function AgentsPanel({ focused }) {
|
|
2283
|
+
const live2 = useLoader(() => api.agents.live({ limit: 30, includeDeactivated: true }));
|
|
2284
|
+
usePoll(live2.reload, 6e3, focused ? false : true);
|
|
2285
|
+
const [sel, setSel] = useState7(0);
|
|
2286
|
+
const agents = (live2.data?.agents ?? []).filter((a) => a.agent_id);
|
|
2287
|
+
const selected = agents[Math.min(sel, Math.max(0, agents.length - 1))];
|
|
2288
|
+
const detail = useLoader(() => selected?.agent_id ? api.agents.get(selected.agent_id) : Promise.resolve(null), [selected?.agent_id]);
|
|
2289
|
+
useInput6(
|
|
2290
|
+
(_input, key) => {
|
|
2291
|
+
if (key.upArrow) setSel((n) => Math.max(0, n - 1));
|
|
2292
|
+
else if (key.downArrow) setSel((n) => Math.min(agents.length - 1, n + 1));
|
|
2293
|
+
},
|
|
2294
|
+
{ isActive: focused }
|
|
2295
|
+
);
|
|
2296
|
+
const d = detail.data;
|
|
2297
|
+
const base = d?.baseline;
|
|
2298
|
+
const radar = base?.radar;
|
|
2299
|
+
const anomalies2 = d?.anomalies ?? [];
|
|
2300
|
+
const tmap = d?.trust_map;
|
|
2301
|
+
const tools = tmap?.tools ?? [];
|
|
2302
|
+
const resources = tmap?.resources ?? [];
|
|
2303
|
+
const listW = 30;
|
|
2304
|
+
return /* @__PURE__ */ jsx8(DataView, { loading: live2.loading && !live2.data, error: live2.error, empty: !!live2.data && agents.length === 0, emptyText: "No identified agents yet.", children: /* @__PURE__ */ jsxs8(Box8, { children: [
|
|
2305
|
+
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: listW, marginRight: 2, overflow: "hidden", children: [
|
|
2306
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: focused ? "\u2191\u2193 select agent" : "press \u2192 to browse" }),
|
|
2307
|
+
agents.slice(0, 16).map((a, i) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", backgroundColor: focused && i === sel ? "#1c2f63" : void 0, bold: i === sel, children: [
|
|
2308
|
+
/* @__PURE__ */ jsxs8(Text8, { color: statusColor(a.status), children: [
|
|
2309
|
+
a.status === "active" ? "\u25CF" : a.status === "idle" ? "\u25D0" : "\u25CB",
|
|
2310
|
+
" "
|
|
2311
|
+
] }),
|
|
2312
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate(a.agent_name ?? a.agent_id ?? "?", 16).padEnd(17) }),
|
|
2313
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: `t${a.trust_score}` })
|
|
2314
|
+
] }, a.session_id))
|
|
2315
|
+
] }),
|
|
2316
|
+
/* @__PURE__ */ jsx8(Box8, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: !selected ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "select an agent" }) : detail.loading && !d ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "loading profile\u2026" }) : !d ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "no profile (agent has no calls yet)" }) : /* @__PURE__ */ jsxs8(Fragment4, { children: [
|
|
2317
|
+
/* @__PURE__ */ jsxs8(Box8, { children: [
|
|
2318
|
+
/* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: truncate(selected.agent_name ?? selected.agent_id ?? "?", 22) }),
|
|
2319
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: ` trust ${base?.trustScore ?? "\u2014"}/100 \xB7 ${base?.character ?? ""}` })
|
|
2320
|
+
] }),
|
|
2321
|
+
base?.characterBlurb ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: truncate(String(base.characterBlurb), 70) }) : null,
|
|
2322
|
+
radar ? /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
|
|
2323
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Behaviour radar (0\u2013100)" }),
|
|
2324
|
+
/* @__PURE__ */ jsx8(Bar, { label: "read", value: Math.round((radar.read ?? 0) * 100) }),
|
|
2325
|
+
/* @__PURE__ */ jsx8(Bar, { label: "write", value: Math.round((radar.write ?? 0) * 100), color: theme.warn }),
|
|
2326
|
+
/* @__PURE__ */ jsx8(Bar, { label: "execute", value: Math.round((radar.execute ?? 0) * 100), color: theme.warn }),
|
|
2327
|
+
/* @__PURE__ */ jsx8(Bar, { label: "network", value: Math.round((radar.network ?? 0) * 100) }),
|
|
2328
|
+
/* @__PURE__ */ jsx8(Bar, { label: "complian.", value: Math.round((radar.compliance ?? 0) * 100), color: theme.ok })
|
|
2329
|
+
] }) : null,
|
|
2330
|
+
/* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
|
|
2331
|
+
/* @__PURE__ */ jsxs8(Text8, { color: theme.dim, children: [
|
|
2332
|
+
"Anomalies ",
|
|
2333
|
+
anomalies2.length ? `(${anomalies2.length})` : ""
|
|
2334
|
+
] }),
|
|
2335
|
+
anomalies2.length === 0 ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " none" }) : anomalies2.slice(0, 4).map((an, i) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
2336
|
+
/* @__PURE__ */ jsx8(Text8, { color: sevColor(String(an.severity)), children: ` ${String(an.severity).toUpperCase().slice(0, 4).padEnd(5)}` }),
|
|
2337
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: String(an.kind).padEnd(12) }),
|
|
2338
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: truncate(String(an.description ?? ""), 44) })
|
|
2339
|
+
] }, i))
|
|
2340
|
+
] }),
|
|
2341
|
+
/* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
|
|
2342
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Trust map \xB7 top tools" }),
|
|
2343
|
+
tools.slice(0, 4).map((t, i) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
2344
|
+
/* @__PURE__ */ jsx8(Text8, { color: t.anomalous ? theme.bad : theme.accent, children: ` ${truncate(String(t.name), 16).padEnd(17)}` }),
|
|
2345
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: `${t.count}\xD7 ${t.permission ?? ""}` })
|
|
2346
|
+
] }, i)),
|
|
2347
|
+
resources.slice(0, 3).map((r, i) => /* @__PURE__ */ jsx8(Text8, { wrap: "truncate", color: r.anomalous ? theme.bad : theme.dim, children: ` ${r.type === "domain" ? "\u{1F310}" : "\u{1F4C1}"} ${truncate(String(r.value), 40)} ${r.count}\xD7` }, "r" + i))
|
|
2348
|
+
] })
|
|
2349
|
+
] }) })
|
|
2350
|
+
] }) });
|
|
2351
|
+
}
|
|
2352
|
+
|
|
2179
2353
|
// src/tui/App.tsx
|
|
2180
|
-
import { jsx as
|
|
2354
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2181
2355
|
function LiveHint() {
|
|
2182
|
-
return /* @__PURE__ */
|
|
2183
|
-
/* @__PURE__ */
|
|
2184
|
-
/* @__PURE__ */
|
|
2185
|
-
/* @__PURE__ */
|
|
2186
|
-
/* @__PURE__ */
|
|
2356
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
2357
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
|
|
2358
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
|
|
2359
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
|
|
2360
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
2187
2361
|
"press ",
|
|
2188
|
-
/* @__PURE__ */
|
|
2362
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: "enter" }),
|
|
2189
2363
|
" to go live"
|
|
2190
2364
|
] }) })
|
|
2191
2365
|
] });
|
|
2192
2366
|
}
|
|
2193
2367
|
var SECTIONS = [
|
|
2194
2368
|
{ label: "Live", Panel: LiveHint },
|
|
2369
|
+
{ label: "Agents", Panel: AgentsPanel },
|
|
2195
2370
|
{ label: "Policies", Panel: PoliciesPanel },
|
|
2196
2371
|
{ label: "Rate Limit", Panel: RateLimitPanel },
|
|
2197
2372
|
{ label: "DLP", Panel: DlpPanel },
|
|
@@ -2202,22 +2377,22 @@ var BANNER_HEX = ["#1432A0", "#2850BE", "#3C6ED7", "#5A8CE6", "#82AAF0", "#AAC8F
|
|
|
2202
2377
|
function Banner() {
|
|
2203
2378
|
const wide = (process.stdout.columns ?? 80) >= 82;
|
|
2204
2379
|
if (!wide) {
|
|
2205
|
-
return /* @__PURE__ */
|
|
2206
|
-
/* @__PURE__ */
|
|
2207
|
-
/* @__PURE__ */
|
|
2380
|
+
return /* @__PURE__ */ jsxs9(Box9, { children: [
|
|
2381
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate" }),
|
|
2382
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2014 security control center" })
|
|
2208
2383
|
] });
|
|
2209
2384
|
}
|
|
2210
|
-
return /* @__PURE__ */
|
|
2211
|
-
BANNER_FULL.map((line, i) => /* @__PURE__ */
|
|
2212
|
-
/* @__PURE__ */
|
|
2385
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
2386
|
+
BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx9(Text9, { bold: true, color: BANNER_HEX[i], children: line }, i)),
|
|
2387
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
|
|
2213
2388
|
] });
|
|
2214
2389
|
}
|
|
2215
2390
|
function App() {
|
|
2216
2391
|
const { exit } = useApp();
|
|
2217
|
-
const [section, setSection] =
|
|
2218
|
-
const [focus, setFocus] =
|
|
2219
|
-
const [help, setHelp] =
|
|
2220
|
-
|
|
2392
|
+
const [section, setSection] = useState8(0);
|
|
2393
|
+
const [focus, setFocus] = useState8("nav");
|
|
2394
|
+
const [help, setHelp] = useState8(false);
|
|
2395
|
+
useInput7((input, key) => {
|
|
2221
2396
|
if (help) {
|
|
2222
2397
|
setHelp(false);
|
|
2223
2398
|
return;
|
|
@@ -2239,18 +2414,18 @@ function App() {
|
|
|
2239
2414
|
const cols = process.stdout.columns ?? 100;
|
|
2240
2415
|
const rows = process.stdout.rows ?? 30;
|
|
2241
2416
|
const current = SECTIONS[section];
|
|
2242
|
-
if (help) return /* @__PURE__ */
|
|
2417
|
+
if (help) return /* @__PURE__ */ jsx9(HelpOverlay, { cols, rows });
|
|
2243
2418
|
if (current.label === "Live" && focus === "panel") {
|
|
2244
|
-
return /* @__PURE__ */
|
|
2419
|
+
return /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx9(LivePanel, { active: true, focused: true }) });
|
|
2245
2420
|
}
|
|
2246
2421
|
const Panel = current.Panel;
|
|
2247
|
-
return /* @__PURE__ */
|
|
2248
|
-
/* @__PURE__ */
|
|
2249
|
-
/* @__PURE__ */
|
|
2250
|
-
/* @__PURE__ */
|
|
2251
|
-
/* @__PURE__ */
|
|
2422
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
|
|
2423
|
+
/* @__PURE__ */ jsx9(Banner, {}),
|
|
2424
|
+
/* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexGrow: 1, children: [
|
|
2425
|
+
/* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => /* @__PURE__ */ jsx9(Text9, { color: i === section ? theme.accentBright : void 0, bold: i === section, children: (i === section ? "\u25B8 " : " ") + s.label }, s.label)) }),
|
|
2426
|
+
/* @__PURE__ */ jsx9(Box9, { flexGrow: 1, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, children: /* @__PURE__ */ jsx9(Panel, { active: focus === "panel" || current.label !== "Live", focused: focus === "panel" }) })
|
|
2252
2427
|
] }),
|
|
2253
|
-
/* @__PURE__ */
|
|
2428
|
+
/* @__PURE__ */ jsx9(Box9, { children: focus === "nav" ? /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ["?", "help"], ["q", "quit"]] }) : /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
|
|
2254
2429
|
] });
|
|
2255
2430
|
}
|
|
2256
2431
|
var HELP = [
|
|
@@ -2260,21 +2435,21 @@ var HELP = [
|
|
|
2260
2435
|
["Rate limit / DLP", [["\u2191\u2193", "field / pattern"], ["\u2190\u2192", "adjust (shift = \xB110)"], ["space", "toggle pattern"], ["m", "mode"], ["a / d", "add / remove custom"], ["g", "ghost mode"], ["P", "self-protection"], ["s", "save"]]]
|
|
2261
2436
|
];
|
|
2262
2437
|
function HelpOverlay({ cols, rows }) {
|
|
2263
|
-
return /* @__PURE__ */
|
|
2264
|
-
/* @__PURE__ */
|
|
2265
|
-
/* @__PURE__ */
|
|
2266
|
-
/* @__PURE__ */
|
|
2267
|
-
keys.map(([k, desc]) => /* @__PURE__ */
|
|
2268
|
-
/* @__PURE__ */
|
|
2269
|
-
/* @__PURE__ */
|
|
2438
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
|
|
2439
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
|
|
2440
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, children: [
|
|
2441
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accent, children: group }),
|
|
2442
|
+
keys.map(([k, desc]) => /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
2443
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
|
|
2444
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: desc })
|
|
2270
2445
|
] }, k))
|
|
2271
2446
|
] }, group)) }),
|
|
2272
|
-
/* @__PURE__ */
|
|
2447
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "press any key to close" })
|
|
2273
2448
|
] });
|
|
2274
2449
|
}
|
|
2275
2450
|
|
|
2276
2451
|
// src/tui/index.tsx
|
|
2277
|
-
import { jsx as
|
|
2452
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
2278
2453
|
async function launchTui() {
|
|
2279
2454
|
if (!process.stdout.isTTY || !process.stdin.isTTY) {
|
|
2280
2455
|
process.stderr.write(
|
|
@@ -2288,7 +2463,7 @@ async function launchTui() {
|
|
|
2288
2463
|
}
|
|
2289
2464
|
process.stdout.write("\x1B[?1049h\x1B[H");
|
|
2290
2465
|
try {
|
|
2291
|
-
const { waitUntilExit } = render(/* @__PURE__ */
|
|
2466
|
+
const { waitUntilExit } = render(/* @__PURE__ */ jsx10(App, {}));
|
|
2292
2467
|
await waitUntilExit();
|
|
2293
2468
|
} finally {
|
|
2294
2469
|
process.stdout.write("\x1B[?1049l");
|
package/dist/tui/theme.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
/** Shared colors + small helpers for the Ink TUI. */
|
|
2
1
|
export declare const theme: {
|
|
3
|
-
readonly accent:
|
|
4
|
-
readonly accentBright:
|
|
2
|
+
readonly accent: string;
|
|
3
|
+
readonly accentBright: string;
|
|
5
4
|
readonly ok: "green";
|
|
6
5
|
readonly warn: "yellow";
|
|
7
6
|
readonly bad: "red";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.81.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": {
|