@solongate/proxy 0.79.0 → 0.81.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/api-client/settings.d.ts +6 -0
- package/dist/commands/index.js +9 -1
- package/dist/index.js +489 -136
- package/dist/tui/config.d.ts +6 -0
- package/dist/tui/index.js +385 -51
- package/dist/tui/panels/Agents.d.ts +4 -0
- package/dist/tui/theme.d.ts +2 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6702,6 +6702,34 @@ var init_client = __esm({
|
|
|
6702
6702
|
}
|
|
6703
6703
|
});
|
|
6704
6704
|
|
|
6705
|
+
// src/tui/config.ts
|
|
6706
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
6707
|
+
import { homedir as homedir3 } from "os";
|
|
6708
|
+
import { join as join5 } from "path";
|
|
6709
|
+
function loadConfig() {
|
|
6710
|
+
if (cached2) return cached2;
|
|
6711
|
+
const defaults = { notifications: true };
|
|
6712
|
+
try {
|
|
6713
|
+
const raw = readFileSync5(join5(homedir3(), ".solongate", "tui-config.json"), "utf-8");
|
|
6714
|
+
const j = JSON.parse(raw);
|
|
6715
|
+
cached2 = {
|
|
6716
|
+
notifications: j.notifications !== false,
|
|
6717
|
+
accent: typeof j.accent === "string" ? j.accent : void 0,
|
|
6718
|
+
pollMs: typeof j.pollMs === "number" ? j.pollMs : void 0
|
|
6719
|
+
};
|
|
6720
|
+
} catch {
|
|
6721
|
+
cached2 = defaults;
|
|
6722
|
+
}
|
|
6723
|
+
return cached2;
|
|
6724
|
+
}
|
|
6725
|
+
var cached2;
|
|
6726
|
+
var init_config2 = __esm({
|
|
6727
|
+
"src/tui/config.ts"() {
|
|
6728
|
+
"use strict";
|
|
6729
|
+
cached2 = null;
|
|
6730
|
+
}
|
|
6731
|
+
});
|
|
6732
|
+
|
|
6705
6733
|
// src/tui/theme.ts
|
|
6706
6734
|
function decisionColor(decision) {
|
|
6707
6735
|
const d = (decision || "").toUpperCase();
|
|
@@ -6735,13 +6763,15 @@ function ago(ts) {
|
|
|
6735
6763
|
if (s < 86400) return `${Math.floor(s / 3600)}h`;
|
|
6736
6764
|
return `${Math.floor(s / 86400)}d`;
|
|
6737
6765
|
}
|
|
6738
|
-
var theme, BLOCKS;
|
|
6766
|
+
var accent, theme, BLOCKS;
|
|
6739
6767
|
var init_theme = __esm({
|
|
6740
6768
|
"src/tui/theme.ts"() {
|
|
6741
6769
|
"use strict";
|
|
6770
|
+
init_config2();
|
|
6771
|
+
accent = loadConfig().accent;
|
|
6742
6772
|
theme = {
|
|
6743
|
-
accent: "cyan",
|
|
6744
|
-
accentBright: "#5a8ce6",
|
|
6773
|
+
accent: accent || "cyan",
|
|
6774
|
+
accentBright: accent || "#5a8ce6",
|
|
6745
6775
|
ok: "green",
|
|
6746
6776
|
warn: "yellow",
|
|
6747
6777
|
bad: "red",
|
|
@@ -6875,8 +6905,10 @@ __export(settings_exports, {
|
|
|
6875
6905
|
getGuardStatus: () => getGuardStatus,
|
|
6876
6906
|
getRateLimitHistory: () => getRateLimitHistory,
|
|
6877
6907
|
getSecurityLayers: () => getSecurityLayers,
|
|
6908
|
+
getSelfProtection: () => getSelfProtection,
|
|
6878
6909
|
getWebhooks: () => getWebhooks,
|
|
6879
|
-
setSecurityLayers: () => setSecurityLayers
|
|
6910
|
+
setSecurityLayers: () => setSecurityLayers,
|
|
6911
|
+
setSelfProtection: () => setSelfProtection
|
|
6880
6912
|
});
|
|
6881
6913
|
function getSecurityLayers() {
|
|
6882
6914
|
return request("GET", "/settings/security-layers");
|
|
@@ -6893,6 +6925,12 @@ function clearRateLimitHistory() {
|
|
|
6893
6925
|
function getGuardStatus() {
|
|
6894
6926
|
return request("GET", "/settings/guard-status");
|
|
6895
6927
|
}
|
|
6928
|
+
function getSelfProtection() {
|
|
6929
|
+
return request("GET", "/settings/self-protection");
|
|
6930
|
+
}
|
|
6931
|
+
function setSelfProtection(enabled) {
|
|
6932
|
+
return request("PUT", "/settings/self-protection", { body: { enabled } });
|
|
6933
|
+
}
|
|
6896
6934
|
function getAlerts() {
|
|
6897
6935
|
return request("GET", "/settings/denial-alerts");
|
|
6898
6936
|
}
|
|
@@ -7100,9 +7138,9 @@ var init_hooks = __esm({
|
|
|
7100
7138
|
import { Box as Box2, Text as Text2, useInput } from "ink";
|
|
7101
7139
|
import TextInput from "ink-text-input";
|
|
7102
7140
|
import { spawn } from "child_process";
|
|
7103
|
-
import { closeSync, openSync, readSync, statSync } from "fs";
|
|
7104
|
-
import { homedir as
|
|
7105
|
-
import { join as
|
|
7141
|
+
import { closeSync, mkdirSync as mkdirSync3, openSync, readSync, statSync, writeFileSync as writeFileSync3 } from "fs";
|
|
7142
|
+
import { homedir as homedir4 } from "os";
|
|
7143
|
+
import { join as join6 } from "path";
|
|
7106
7144
|
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
7107
7145
|
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
7108
7146
|
function tailLines(file, maxBytes = 131072) {
|
|
@@ -7412,6 +7450,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7412
7450
|
(id, title, msg, level = "warn") => {
|
|
7413
7451
|
pushLog(`${level === "bad" ? "\u26D4" : "\u23F8"} ${msg}`, level);
|
|
7414
7452
|
setAlerts((a) => [...a.filter((x) => x.id !== id), { id, msg, level, until: Date.now() + 15e3 }].slice(-4));
|
|
7453
|
+
if (!CONFIG.notifications) return;
|
|
7415
7454
|
try {
|
|
7416
7455
|
process.stdout.write("\x07");
|
|
7417
7456
|
} catch {
|
|
@@ -7639,7 +7678,16 @@ function LivePanel({ active: active2 }) {
|
|
|
7639
7678
|
else if (input === "d") toggleSignal("deny");
|
|
7640
7679
|
else if (input === "x") toggleSignal("dlp");
|
|
7641
7680
|
else if (input === "r") toggleSignal("ratelimit");
|
|
7642
|
-
else if (
|
|
7681
|
+
else if (input === "e") {
|
|
7682
|
+
const file = join6(homedir4(), ".solongate", "live-export.jsonl");
|
|
7683
|
+
try {
|
|
7684
|
+
mkdirSync3(join6(homedir4(), ".solongate"), { recursive: true });
|
|
7685
|
+
writeFileSync3(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
|
|
7686
|
+
setActionMsg({ text: `\u2713 exported ${visibleDesc.length} lines \u2192 ${file}`, level: "ok", until: Date.now() + 6e3 });
|
|
7687
|
+
} catch (err2) {
|
|
7688
|
+
setActionMsg({ text: "\u2717 export failed: " + (err2 instanceof Error ? err2.message : String(err2)), level: "bad", until: Date.now() + 6e3 });
|
|
7689
|
+
}
|
|
7690
|
+
} else if (key.downArrow) setSel((n) => Math.min(visibleDesc.length - 1, n + 1));
|
|
7643
7691
|
else if (key.upArrow) setSel((n) => Math.max(0, n - 1));
|
|
7644
7692
|
else if (key.pageDown) setSel((n) => Math.min(visibleDesc.length - 1, n + streamBodyRows));
|
|
7645
7693
|
else if (key.pageUp) setSel((n) => Math.max(0, n - streamBodyRows));
|
|
@@ -7888,18 +7936,20 @@ function LivePanel({ active: active2 }) {
|
|
|
7888
7936
|
] })
|
|
7889
7937
|
] });
|
|
7890
7938
|
}
|
|
7891
|
-
var SPIN, BG, DIM_FLOOR, LOCAL_LOG, RING, hhmmss, fmtUp, FILTERS, sessStatus, STATUS_STYLE;
|
|
7939
|
+
var CONFIG, SPIN, BG, DIM_FLOOR, LOCAL_LOG, RING, hhmmss, fmtUp, FILTERS, sessStatus, STATUS_STYLE;
|
|
7892
7940
|
var init_Live = __esm({
|
|
7893
7941
|
"src/tui/panels/Live.tsx"() {
|
|
7894
7942
|
"use strict";
|
|
7895
7943
|
init_api_client();
|
|
7944
|
+
init_config2();
|
|
7896
7945
|
init_hooks();
|
|
7897
7946
|
init_theme();
|
|
7947
|
+
CONFIG = loadConfig();
|
|
7898
7948
|
SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
7899
7949
|
BG = "#12234f";
|
|
7900
7950
|
DIM_FLOOR = "#233457";
|
|
7901
|
-
LOCAL_LOG =
|
|
7902
|
-
RING =
|
|
7951
|
+
LOCAL_LOG = join6(homedir4(), ".solongate", "local-logs", "solongate-audit.jsonl");
|
|
7952
|
+
RING = join6(process.cwd(), ".solongate", ".eval-ring.jsonl");
|
|
7903
7953
|
hhmmss = (ts) => {
|
|
7904
7954
|
const d = new Date(ts);
|
|
7905
7955
|
return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
|
|
@@ -7923,7 +7973,7 @@ var init_Live = __esm({
|
|
|
7923
7973
|
import { Box as Box3, Text as Text3, useInput as useInput2 } from "ink";
|
|
7924
7974
|
import TextInput2 from "ink-text-input";
|
|
7925
7975
|
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
7926
|
-
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
7976
|
+
import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
7927
7977
|
function ruleSummary(r) {
|
|
7928
7978
|
const bits = [];
|
|
7929
7979
|
for (const [g, tag] of [["commandConstraints", "cmd"], ["pathConstraints", "path"], ["filenameConstraints", "file"], ["urlConstraints", "url"]]) {
|
|
@@ -7940,6 +7990,7 @@ function PoliciesPanel({ focused }) {
|
|
|
7940
7990
|
const [pi, setPi] = useState3(0);
|
|
7941
7991
|
const [ri, setRi] = useState3(0);
|
|
7942
7992
|
const [fi, setFi] = useState3(0);
|
|
7993
|
+
const [vi, setVi] = useState3(0);
|
|
7943
7994
|
const [rules, setRules] = useState3([]);
|
|
7944
7995
|
const [mode, setMode] = useState3("denylist");
|
|
7945
7996
|
const [dirty, setDirty] = useState3(false);
|
|
@@ -7948,6 +7999,15 @@ function PoliciesPanel({ focused }) {
|
|
|
7948
7999
|
const [status, setStatus] = useState3(null);
|
|
7949
8000
|
const selected = policies[Math.min(pi, Math.max(0, policies.length - 1))];
|
|
7950
8001
|
const detail = useLoader(() => selected ? api.policies.get(selected.id) : Promise.resolve(null), [selected?.id]);
|
|
8002
|
+
const versionsQ = useLoader(
|
|
8003
|
+
() => view === "versions" && selected ? api.policies.versions(selected.id, { limit: 50 }) : Promise.resolve(null),
|
|
8004
|
+
[view, selected?.id]
|
|
8005
|
+
);
|
|
8006
|
+
const diffVer = versionsQ.data?.versions[vi]?.version;
|
|
8007
|
+
const diffQ = useLoader(
|
|
8008
|
+
() => view === "versions" && selected && diffVer !== void 0 ? api.policies.get(selected.id, diffVer) : Promise.resolve(null),
|
|
8009
|
+
[view, selected?.id, diffVer]
|
|
8010
|
+
);
|
|
7951
8011
|
useEffect3(() => {
|
|
7952
8012
|
if (detail.data) {
|
|
7953
8013
|
setRules(detail.data.rules);
|
|
@@ -8022,10 +8082,41 @@ function PoliciesPanel({ focused }) {
|
|
|
8022
8082
|
}
|
|
8023
8083
|
} else if (input === "m") {
|
|
8024
8084
|
mutate(rules, mode === "denylist" ? "whitelist" : "denylist");
|
|
8085
|
+
} else if (input === "n") {
|
|
8086
|
+
const nr = { id: `rule-${Date.now()}`, description: "", effect: "DENY", priority: 100, toolPattern: "*", minimumTrustLevel: "UNTRUSTED", enabled: true };
|
|
8087
|
+
mutate([nr, ...rules]);
|
|
8088
|
+
setRi(0);
|
|
8089
|
+
setFi(0);
|
|
8090
|
+
setView("rule");
|
|
8091
|
+
} else if (input === "D") {
|
|
8092
|
+
setStatus("Dry-running\u2026");
|
|
8093
|
+
void api.policies.dryRun({ rules, mode }).then((res) => setStatus(`dry-run ${res.evaluated} calls \xB7 allow ${res.would_allow} / deny ${res.would_deny} \xB7 newly blocked ${res.newly_blocked} \xB7 newly allowed ${res.newly_allowed}`)).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
|
|
8094
|
+
} else if (input === "v") {
|
|
8095
|
+
setVi(0);
|
|
8096
|
+
setView("versions");
|
|
8025
8097
|
} else if (input === "s") void save();
|
|
8026
8098
|
else if (input === "x") discard();
|
|
8027
8099
|
return;
|
|
8028
8100
|
}
|
|
8101
|
+
if (view === "versions") {
|
|
8102
|
+
const vers = versionsQ.data?.versions ?? [];
|
|
8103
|
+
if (key.leftArrow) return void setView("rules");
|
|
8104
|
+
if (key.upArrow) setVi((n) => Math.max(0, n - 1));
|
|
8105
|
+
else if (key.downArrow) setVi((n) => Math.min(vers.length - 1, n + 1));
|
|
8106
|
+
else if (key.return) {
|
|
8107
|
+
const v = vers[vi];
|
|
8108
|
+
if (v && selected) {
|
|
8109
|
+
setStatus("Rolling back\u2026");
|
|
8110
|
+
void api.policies.rollback(selected.id, v.version).then((r) => {
|
|
8111
|
+
setStatus(`\u2713 rolled back to v${v.version} \u2192 new v${r.version}`);
|
|
8112
|
+
detail.reload();
|
|
8113
|
+
list6.reload();
|
|
8114
|
+
setView("rules");
|
|
8115
|
+
}).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
|
|
8116
|
+
}
|
|
8117
|
+
}
|
|
8118
|
+
return;
|
|
8119
|
+
}
|
|
8029
8120
|
const rule2 = rules[ri];
|
|
8030
8121
|
if (!rule2) return setView("rules");
|
|
8031
8122
|
const field = FIELDS[fi];
|
|
@@ -8070,7 +8161,7 @@ function PoliciesPanel({ focused }) {
|
|
|
8070
8161
|
/* @__PURE__ */ jsx3(Text3, { color: mode === "whitelist" ? theme.warn : void 0, children: mode }),
|
|
8071
8162
|
dirtyTag
|
|
8072
8163
|
] }),
|
|
8073
|
-
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "\u2191\u2193
|
|
8164
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "\u2191\u2193 \xB7 enter edit \xB7 space on/off \xB7 e effect \xB7 n new \xB7 d del \xB7 m mode \xB7 D dry-run \xB7 v versions \xB7 s save \xB7 \u2190 back" }),
|
|
8074
8165
|
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(
|
|
8075
8166
|
Table,
|
|
8076
8167
|
{
|
|
@@ -8096,6 +8187,50 @@ function PoliciesPanel({ focused }) {
|
|
|
8096
8187
|
status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
8097
8188
|
] }) });
|
|
8098
8189
|
}
|
|
8190
|
+
if (view === "versions") {
|
|
8191
|
+
const vers = versionsQ.data?.versions ?? [];
|
|
8192
|
+
return /* @__PURE__ */ jsx3(DataView, { loading: versionsQ.loading && !versionsQ.data, error: versionsQ.error, empty: !!versionsQ.data && vers.length === 0, emptyText: "No versions.", children: /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
8193
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.accentBright, children: truncate2(selected?.name ?? "", 28) }),
|
|
8194
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "\u2191\u2193 select \xB7 enter roll back to that version \xB7 \u2190 back" }),
|
|
8195
|
+
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(
|
|
8196
|
+
Table,
|
|
8197
|
+
{
|
|
8198
|
+
columns: [
|
|
8199
|
+
{ header: "", width: 2 },
|
|
8200
|
+
{ header: "VER", width: 5 },
|
|
8201
|
+
{ header: "RULES", width: 5 },
|
|
8202
|
+
{ header: "REASON", width: 34 },
|
|
8203
|
+
{ header: "WHEN", width: 16 }
|
|
8204
|
+
],
|
|
8205
|
+
rows: vers.map((v, i) => [
|
|
8206
|
+
{ value: i === vi ? "\u25B8" : "", color: theme.accentBright },
|
|
8207
|
+
{ value: `v${v.version}`, bold: i === vi },
|
|
8208
|
+
{ value: String(v.rules_count), dim: true },
|
|
8209
|
+
{ value: truncate2(v.reason || "\u2014", 34) },
|
|
8210
|
+
{ value: truncate2(v.created_at, 16), dim: true }
|
|
8211
|
+
])
|
|
8212
|
+
}
|
|
8213
|
+
) }),
|
|
8214
|
+
/* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
|
|
8215
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: `Diff v${diffVer ?? "?"} vs current draft` }),
|
|
8216
|
+
(() => {
|
|
8217
|
+
const old = diffQ.data?.rules ?? [];
|
|
8218
|
+
const cur = rules;
|
|
8219
|
+
const oldIds = new Set(old.map((r) => r.id));
|
|
8220
|
+
const curIds = new Set(cur.map((r) => r.id));
|
|
8221
|
+
const added = cur.filter((r) => !oldIds.has(r.id));
|
|
8222
|
+
const removed = old.filter((r) => !curIds.has(r.id));
|
|
8223
|
+
if (diffQ.loading && !diffQ.data) return /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: " loading\u2026" });
|
|
8224
|
+
if (!added.length && !removed.length) return /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: " identical rule set" });
|
|
8225
|
+
return /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
8226
|
+
added.slice(0, 4).map((r) => /* @__PURE__ */ jsx3(Text3, { wrap: "truncate", color: theme.ok, children: ` + ${r.effect} ${truncate2(r.toolPattern, 18)} ${truncate2(r.description || r.id, 30)}` }, "a" + r.id)),
|
|
8227
|
+
removed.slice(0, 4).map((r) => /* @__PURE__ */ jsx3(Text3, { wrap: "truncate", color: theme.bad, children: ` - ${r.effect} ${truncate2(r.toolPattern, 18)} ${truncate2(r.description || r.id, 30)}` }, "r" + r.id))
|
|
8228
|
+
] });
|
|
8229
|
+
})()
|
|
8230
|
+
] }),
|
|
8231
|
+
status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
8232
|
+
] }) });
|
|
8233
|
+
}
|
|
8099
8234
|
const rule = rules[ri];
|
|
8100
8235
|
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
8101
8236
|
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
@@ -8237,6 +8372,20 @@ function RateLimitPanel({ focused }) {
|
|
|
8237
8372
|
/* @__PURE__ */ jsx4(FieldRow, { label: "Per hour", active: focused && fi === 2, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perHour === 0 ? "off" : draft.perHour }) }),
|
|
8238
8373
|
/* @__PURE__ */ jsx4(FieldRow, { label: "Per day", active: focused && fi === 3, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perDay === 0 ? "off" : draft.perDay }) })
|
|
8239
8374
|
] }),
|
|
8375
|
+
draft.perMinute > 0 ? (() => {
|
|
8376
|
+
const now = minuteSeries.slice(-1)[0] ?? 0;
|
|
8377
|
+
const pct = Math.min(100, Math.round(now / draft.perMinute * 100));
|
|
8378
|
+
const width2 = 30;
|
|
8379
|
+
const filled = Math.min(width2, Math.round(now / draft.perMinute * width2));
|
|
8380
|
+
const col = pct >= 100 ? theme.bad : pct >= 80 ? theme.warn : theme.ok;
|
|
8381
|
+
return /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
|
|
8382
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "load " }),
|
|
8383
|
+
/* @__PURE__ */ jsx4(Text4, { color: col, children: "\u2588".repeat(filled) }),
|
|
8384
|
+
/* @__PURE__ */ jsx4(Text4, { color: "#233457", children: "\u2591".repeat(Math.max(0, width2 - filled)) }),
|
|
8385
|
+
/* @__PURE__ */ jsx4(Text4, { color: col, children: ` ${now}/${draft.perMinute} ` }),
|
|
8386
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: `(${pct}% this min)` })
|
|
8387
|
+
] });
|
|
8388
|
+
})() : null,
|
|
8240
8389
|
dirty || status ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
|
|
8241
8390
|
dirty ? /* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: "\u25CF unsaved \u2014 press s to apply " }) : null,
|
|
8242
8391
|
status ? /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
@@ -8319,12 +8468,19 @@ function DlpPanel({ focused }) {
|
|
|
8319
8468
|
const [adding, setAdding] = useState5(null);
|
|
8320
8469
|
const [newName, setNewName] = useState5("");
|
|
8321
8470
|
const [newRe, setNewRe] = useState5("");
|
|
8471
|
+
const [ghostOn, setGhostOn] = useState5(false);
|
|
8472
|
+
const spQ = useLoader(() => api.settings.getSelfProtection());
|
|
8473
|
+
const [selfProt, setSelfProt] = useState5(null);
|
|
8322
8474
|
useEffect5(() => {
|
|
8323
8475
|
if (q.data && !dlp) {
|
|
8324
8476
|
setDlp({ ...q.data.layers.dlp, patterns: [...q.data.layers.dlp.patterns], custom: [...q.data.layers.dlp.custom] });
|
|
8325
8477
|
setAvailable(q.data.availablePatterns);
|
|
8478
|
+
setGhostOn(q.data.layers.ghost.mode === "on");
|
|
8326
8479
|
}
|
|
8327
8480
|
}, [q.data, dlp]);
|
|
8481
|
+
useEffect5(() => {
|
|
8482
|
+
if (spQ.data && selfProt === null) setSelfProt(spQ.data.enabled);
|
|
8483
|
+
}, [spQ.data, selfProt]);
|
|
8328
8484
|
const mutate = (next) => {
|
|
8329
8485
|
setDlp(next);
|
|
8330
8486
|
setDirty(true);
|
|
@@ -8334,14 +8490,27 @@ function DlpPanel({ focused }) {
|
|
|
8334
8490
|
if (!dlp || !q.data) return;
|
|
8335
8491
|
setStatus("Saving\u2026");
|
|
8336
8492
|
try {
|
|
8337
|
-
const
|
|
8493
|
+
const ghost = { ...q.data.layers.ghost, mode: ghostOn ? "on" : "off" };
|
|
8494
|
+
const res = await api.settings.setSecurityLayers({ ...q.data.layers, dlp, ghost });
|
|
8338
8495
|
setDlp({ ...res.layers.dlp, patterns: [...res.layers.dlp.patterns], custom: [...res.layers.dlp.custom] });
|
|
8496
|
+
setGhostOn(res.layers.ghost.mode === "on");
|
|
8339
8497
|
setDirty(false);
|
|
8340
8498
|
setStatus("\u2713 Saved");
|
|
8341
8499
|
} catch (e) {
|
|
8342
8500
|
setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
|
|
8343
8501
|
}
|
|
8344
8502
|
};
|
|
8503
|
+
const toggleSelfProt = async () => {
|
|
8504
|
+
const next = !(selfProt ?? false);
|
|
8505
|
+
setSelfProt(next);
|
|
8506
|
+
try {
|
|
8507
|
+
await api.settings.setSelfProtection(next);
|
|
8508
|
+
setStatus(`\u2713 self-protection ${next ? "on" : "off"}`);
|
|
8509
|
+
} catch (e) {
|
|
8510
|
+
setSelfProt(!next);
|
|
8511
|
+
setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
|
|
8512
|
+
}
|
|
8513
|
+
};
|
|
8345
8514
|
const discard = () => {
|
|
8346
8515
|
if (q.data) setDlp({ ...q.data.layers.dlp, patterns: [...q.data.layers.dlp.patterns], custom: [...q.data.layers.dlp.custom] });
|
|
8347
8516
|
setDirty(false);
|
|
@@ -8371,7 +8540,12 @@ function DlpPanel({ focused }) {
|
|
|
8371
8540
|
const ci = sel - available.length;
|
|
8372
8541
|
mutate({ ...dlp, custom: dlp.custom.filter((_, i) => i !== ci) });
|
|
8373
8542
|
setSel((n) => Math.max(0, n - 1));
|
|
8374
|
-
} else if (input === "
|
|
8543
|
+
} else if (input === "g") {
|
|
8544
|
+
setGhostOn((v) => !v);
|
|
8545
|
+
setDirty(true);
|
|
8546
|
+
setStatus(null);
|
|
8547
|
+
} else if (input === "P") void toggleSelfProt();
|
|
8548
|
+
else if (input === "s") void save();
|
|
8375
8549
|
else if (input === "x") discard();
|
|
8376
8550
|
},
|
|
8377
8551
|
{ isActive: focused && !adding }
|
|
@@ -8383,7 +8557,13 @@ function DlpPanel({ focused }) {
|
|
|
8383
8557
|
/* @__PURE__ */ jsx5(Text5, { color: modeColor(dlp.mode), children: dlp.mode }),
|
|
8384
8558
|
dirty ? /* @__PURE__ */ jsx5(Text5, { color: theme.warn, children: " \u25CF unsaved (s save \xB7 x discard)" }) : null
|
|
8385
8559
|
] }),
|
|
8386
|
-
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: focused ? "\u2191\u2193
|
|
8560
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: focused ? "\u2191\u2193 \xB7 space toggle \xB7 m mode \xB7 a add \xB7 d remove \xB7 g ghost \xB7 P self-protect \xB7 s save" : "press \u2192 to edit" }),
|
|
8561
|
+
/* @__PURE__ */ jsxs5(Box5, { children: [
|
|
8562
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: "ghost: " }),
|
|
8563
|
+
/* @__PURE__ */ jsx5(Text5, { color: ghostOn ? theme.ok : theme.dim, children: ghostOn ? "on" : "off" }),
|
|
8564
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " self-protection: " }),
|
|
8565
|
+
/* @__PURE__ */ jsx5(Text5, { color: selfProt ? theme.ok : theme.dim, children: selfProt === null ? "\u2026" : selfProt ? "on" : "off" })
|
|
8566
|
+
] }),
|
|
8387
8567
|
/* @__PURE__ */ jsx5(Box5, { marginTop: 1, flexDirection: "column", children: available.map((p, i) => {
|
|
8388
8568
|
const on = enabled.has(p);
|
|
8389
8569
|
const active2 = focused && sel === i;
|
|
@@ -8430,10 +8610,27 @@ function DlpPanel({ focused }) {
|
|
|
8430
8610
|
}
|
|
8431
8611
|
)
|
|
8432
8612
|
] }) : null,
|
|
8613
|
+
adding === "re" ? /* @__PURE__ */ jsx5(RegexTest, { re: newRe }) : null,
|
|
8433
8614
|
status ? /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) }) : null
|
|
8434
8615
|
] }) : null });
|
|
8435
8616
|
}
|
|
8436
|
-
|
|
8617
|
+
function RegexTest({ re }) {
|
|
8618
|
+
if (!re.trim()) return /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " regex: type a pattern\u2026" });
|
|
8619
|
+
let rx = null;
|
|
8620
|
+
let err2 = "";
|
|
8621
|
+
try {
|
|
8622
|
+
rx = new RegExp(re);
|
|
8623
|
+
} catch (e) {
|
|
8624
|
+
err2 = e instanceof Error ? e.message : String(e);
|
|
8625
|
+
}
|
|
8626
|
+
if (!rx) return /* @__PURE__ */ jsx5(Text5, { color: theme.bad, children: ` \u2717 invalid: ${err2.slice(0, 50)}` });
|
|
8627
|
+
const hits = SAMPLES.filter((s) => rx.test(s));
|
|
8628
|
+
return /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
|
|
8629
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.ok, children: " \u2713 valid" }),
|
|
8630
|
+
/* @__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)" })
|
|
8631
|
+
] });
|
|
8632
|
+
}
|
|
8633
|
+
var MODES2, SAMPLES;
|
|
8437
8634
|
var init_Dlp = __esm({
|
|
8438
8635
|
"src/tui/panels/Dlp.tsx"() {
|
|
8439
8636
|
"use strict";
|
|
@@ -8442,6 +8639,7 @@ var init_Dlp = __esm({
|
|
|
8442
8639
|
init_hooks();
|
|
8443
8640
|
init_theme();
|
|
8444
8641
|
MODES2 = ["off", "detect", "block"];
|
|
8642
|
+
SAMPLES = ["AKIA1234567890ABCD00", "sk-ant-api03-xxxxxxxx", "ghp_16chars0000000000000000000000000000", "password=hunter2", "Bearer eyJhbGciOi"];
|
|
8445
8643
|
}
|
|
8446
8644
|
});
|
|
8447
8645
|
|
|
@@ -8451,12 +8649,15 @@ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
|
8451
8649
|
function StatsPanel({ active: active2 }) {
|
|
8452
8650
|
const stats = useLoader(() => api.stats.get());
|
|
8453
8651
|
const ts = useLoader(() => api.stats.timeseries({ period: "24h" }));
|
|
8652
|
+
const drift2 = useLoader(() => api.stats.drift(7));
|
|
8454
8653
|
usePoll(() => {
|
|
8455
8654
|
stats.reload();
|
|
8456
8655
|
ts.reload();
|
|
8457
8656
|
}, 5e3, active2);
|
|
8657
|
+
usePoll(drift2.reload, 3e4, active2);
|
|
8458
8658
|
const s = stats.data;
|
|
8459
8659
|
const points = ts.data?.timeseries ?? [];
|
|
8660
|
+
const driftRules = (drift2.data?.rules ?? []).slice(0, 5);
|
|
8460
8661
|
return /* @__PURE__ */ jsx6(DataView, { loading: stats.loading && !s, error: stats.error, children: s ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
8461
8662
|
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
8462
8663
|
/* @__PURE__ */ jsxs6(Text6, { bold: true, children: [
|
|
@@ -8509,7 +8710,7 @@ function StatsPanel({ active: active2 }) {
|
|
|
8509
8710
|
{ header: "MS", width: 5 },
|
|
8510
8711
|
{ header: "WHEN", width: 6 }
|
|
8511
8712
|
],
|
|
8512
|
-
rows: (s.recent_activity ?? []).slice(0,
|
|
8713
|
+
rows: (s.recent_activity ?? []).slice(0, 6).map((a) => [
|
|
8513
8714
|
{ value: a.decision, color: decisionColor(a.decision) },
|
|
8514
8715
|
{ value: truncate2(a.tool_name, 20), color: theme.accent },
|
|
8515
8716
|
{ value: String(a.evaluation_time_ms ?? "\u2014"), dim: true },
|
|
@@ -8517,6 +8718,31 @@ function StatsPanel({ active: active2 }) {
|
|
|
8517
8718
|
])
|
|
8518
8719
|
}
|
|
8519
8720
|
)
|
|
8721
|
+
] }),
|
|
8722
|
+
/* @__PURE__ */ jsxs6(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
8723
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
8724
|
+
"Denial drift ",
|
|
8725
|
+
drift2.data ? `(7d: ${drift2.data.total_current} now vs ${drift2.data.total_previous} prev)` : ""
|
|
8726
|
+
] }),
|
|
8727
|
+
driftRules.length ? /* @__PURE__ */ jsx6(
|
|
8728
|
+
Table,
|
|
8729
|
+
{
|
|
8730
|
+
columns: [
|
|
8731
|
+
{ header: "NOW", width: 5 },
|
|
8732
|
+
{ header: "PREV", width: 5 },
|
|
8733
|
+
{ header: "\u0394", width: 6 },
|
|
8734
|
+
{ header: "RULE", width: 22 },
|
|
8735
|
+
{ header: "REASON", width: 26 }
|
|
8736
|
+
],
|
|
8737
|
+
rows: driftRules.map((r) => [
|
|
8738
|
+
{ value: String(r.current), bold: true },
|
|
8739
|
+
{ value: String(r.previous), dim: true },
|
|
8740
|
+
{ value: r.is_new ? "NEW" : r.spike ? `+${r.delta}` : String(r.delta), color: r.is_new ? theme.ok : r.spike ? theme.bad : void 0 },
|
|
8741
|
+
{ value: truncate2(r.rule_id ?? "\u2014", 22), color: theme.accent },
|
|
8742
|
+
{ value: truncate2(r.reason ?? r.last_tool ?? "\u2014", 26), dim: true }
|
|
8743
|
+
])
|
|
8744
|
+
}
|
|
8745
|
+
) : /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " no denials in window" })
|
|
8520
8746
|
] })
|
|
8521
8747
|
] }) : null });
|
|
8522
8748
|
}
|
|
@@ -8709,18 +8935,114 @@ var init_Audit = __esm({
|
|
|
8709
8935
|
}
|
|
8710
8936
|
});
|
|
8711
8937
|
|
|
8712
|
-
// src/tui/
|
|
8713
|
-
import { Box as Box8, Text as Text8,
|
|
8938
|
+
// src/tui/panels/Agents.tsx
|
|
8939
|
+
import { Box as Box8, Text as Text8, useInput as useInput6 } from "ink";
|
|
8714
8940
|
import { useState as useState7 } from "react";
|
|
8715
|
-
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
8941
|
+
import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
8942
|
+
function Bar({ label, value, max = 100, width: width2 = 16, color = theme.accent }) {
|
|
8943
|
+
const filled = max > 0 ? Math.round(Math.min(value, max) / max * width2) : 0;
|
|
8944
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
8945
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: label.padEnd(9) }),
|
|
8946
|
+
/* @__PURE__ */ jsx8(Text8, { color, children: "\u2588".repeat(filled) }),
|
|
8947
|
+
/* @__PURE__ */ jsx8(Text8, { color: "#233457", children: "\u2591".repeat(Math.max(0, width2 - filled)) }),
|
|
8948
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " " + value })
|
|
8949
|
+
] });
|
|
8950
|
+
}
|
|
8951
|
+
function AgentsPanel({ focused }) {
|
|
8952
|
+
const live2 = useLoader(() => api.agents.live({ limit: 30, includeDeactivated: true }));
|
|
8953
|
+
usePoll(live2.reload, 6e3, focused ? false : true);
|
|
8954
|
+
const [sel, setSel] = useState7(0);
|
|
8955
|
+
const agents = (live2.data?.agents ?? []).filter((a) => a.agent_id);
|
|
8956
|
+
const selected = agents[Math.min(sel, Math.max(0, agents.length - 1))];
|
|
8957
|
+
const detail = useLoader(() => selected?.agent_id ? api.agents.get(selected.agent_id) : Promise.resolve(null), [selected?.agent_id]);
|
|
8958
|
+
useInput6(
|
|
8959
|
+
(_input, key) => {
|
|
8960
|
+
if (key.upArrow) setSel((n) => Math.max(0, n - 1));
|
|
8961
|
+
else if (key.downArrow) setSel((n) => Math.min(agents.length - 1, n + 1));
|
|
8962
|
+
},
|
|
8963
|
+
{ isActive: focused }
|
|
8964
|
+
);
|
|
8965
|
+
const d = detail.data;
|
|
8966
|
+
const base = d?.baseline;
|
|
8967
|
+
const radar = base?.radar;
|
|
8968
|
+
const anomalies2 = d?.anomalies ?? [];
|
|
8969
|
+
const tmap = d?.trust_map;
|
|
8970
|
+
const tools = tmap?.tools ?? [];
|
|
8971
|
+
const resources = tmap?.resources ?? [];
|
|
8972
|
+
const listW = 30;
|
|
8973
|
+
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: [
|
|
8974
|
+
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: listW, marginRight: 2, overflow: "hidden", children: [
|
|
8975
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: focused ? "\u2191\u2193 select agent" : "press \u2192 to browse" }),
|
|
8976
|
+
agents.slice(0, 16).map((a, i) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", backgroundColor: focused && i === sel ? "#1c2f63" : void 0, bold: i === sel, children: [
|
|
8977
|
+
/* @__PURE__ */ jsxs8(Text8, { color: statusColor(a.status), children: [
|
|
8978
|
+
a.status === "active" ? "\u25CF" : a.status === "idle" ? "\u25D0" : "\u25CB",
|
|
8979
|
+
" "
|
|
8980
|
+
] }),
|
|
8981
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate2(a.agent_name ?? a.agent_id ?? "?", 16).padEnd(17) }),
|
|
8982
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: `t${a.trust_score}` })
|
|
8983
|
+
] }, a.session_id))
|
|
8984
|
+
] }),
|
|
8985
|
+
/* @__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: [
|
|
8986
|
+
/* @__PURE__ */ jsxs8(Box8, { children: [
|
|
8987
|
+
/* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: truncate2(selected.agent_name ?? selected.agent_id ?? "?", 22) }),
|
|
8988
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: ` trust ${base?.trustScore ?? "\u2014"}/100 \xB7 ${base?.character ?? ""}` })
|
|
8989
|
+
] }),
|
|
8990
|
+
base?.characterBlurb ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: truncate2(String(base.characterBlurb), 70) }) : null,
|
|
8991
|
+
radar ? /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
|
|
8992
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Behaviour radar (0\u2013100)" }),
|
|
8993
|
+
/* @__PURE__ */ jsx8(Bar, { label: "read", value: Math.round((radar.read ?? 0) * 100) }),
|
|
8994
|
+
/* @__PURE__ */ jsx8(Bar, { label: "write", value: Math.round((radar.write ?? 0) * 100), color: theme.warn }),
|
|
8995
|
+
/* @__PURE__ */ jsx8(Bar, { label: "execute", value: Math.round((radar.execute ?? 0) * 100), color: theme.warn }),
|
|
8996
|
+
/* @__PURE__ */ jsx8(Bar, { label: "network", value: Math.round((radar.network ?? 0) * 100) }),
|
|
8997
|
+
/* @__PURE__ */ jsx8(Bar, { label: "complian.", value: Math.round((radar.compliance ?? 0) * 100), color: theme.ok })
|
|
8998
|
+
] }) : null,
|
|
8999
|
+
/* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
|
|
9000
|
+
/* @__PURE__ */ jsxs8(Text8, { color: theme.dim, children: [
|
|
9001
|
+
"Anomalies ",
|
|
9002
|
+
anomalies2.length ? `(${anomalies2.length})` : ""
|
|
9003
|
+
] }),
|
|
9004
|
+
anomalies2.length === 0 ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " none" }) : anomalies2.slice(0, 4).map((an, i) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
9005
|
+
/* @__PURE__ */ jsx8(Text8, { color: sevColor(String(an.severity)), children: ` ${String(an.severity).toUpperCase().slice(0, 4).padEnd(5)}` }),
|
|
9006
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: String(an.kind).padEnd(12) }),
|
|
9007
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: truncate2(String(an.description ?? ""), 44) })
|
|
9008
|
+
] }, i))
|
|
9009
|
+
] }),
|
|
9010
|
+
/* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
|
|
9011
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Trust map \xB7 top tools" }),
|
|
9012
|
+
tools.slice(0, 4).map((t, i) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
9013
|
+
/* @__PURE__ */ jsx8(Text8, { color: t.anomalous ? theme.bad : theme.accent, children: ` ${truncate2(String(t.name), 16).padEnd(17)}` }),
|
|
9014
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: `${t.count}\xD7 ${t.permission ?? ""}` })
|
|
9015
|
+
] }, i)),
|
|
9016
|
+
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}"} ${truncate2(String(r.value), 40)} ${r.count}\xD7` }, "r" + i))
|
|
9017
|
+
] })
|
|
9018
|
+
] }) })
|
|
9019
|
+
] }) });
|
|
9020
|
+
}
|
|
9021
|
+
var statusColor, sevColor;
|
|
9022
|
+
var init_Agents = __esm({
|
|
9023
|
+
"src/tui/panels/Agents.tsx"() {
|
|
9024
|
+
"use strict";
|
|
9025
|
+
init_api_client();
|
|
9026
|
+
init_components();
|
|
9027
|
+
init_hooks();
|
|
9028
|
+
init_theme();
|
|
9029
|
+
statusColor = (s) => s === "active" ? theme.ok : s === "idle" ? theme.warn : theme.dim;
|
|
9030
|
+
sevColor = (s) => s === "high" ? theme.bad : s === "medium" ? theme.warn : theme.dim;
|
|
9031
|
+
}
|
|
9032
|
+
});
|
|
9033
|
+
|
|
9034
|
+
// src/tui/App.tsx
|
|
9035
|
+
import { Box as Box9, Text as Text9, useApp, useInput as useInput7 } from "ink";
|
|
9036
|
+
import { useState as useState8 } from "react";
|
|
9037
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
8716
9038
|
function LiveHint() {
|
|
8717
|
-
return /* @__PURE__ */
|
|
8718
|
-
/* @__PURE__ */
|
|
8719
|
-
/* @__PURE__ */
|
|
8720
|
-
/* @__PURE__ */
|
|
8721
|
-
/* @__PURE__ */
|
|
9039
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
9040
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
|
|
9041
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
|
|
9042
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
|
|
9043
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
8722
9044
|
"press ",
|
|
8723
|
-
/* @__PURE__ */
|
|
9045
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: "enter" }),
|
|
8724
9046
|
" to go live"
|
|
8725
9047
|
] }) })
|
|
8726
9048
|
] });
|
|
@@ -8728,21 +9050,30 @@ function LiveHint() {
|
|
|
8728
9050
|
function Banner() {
|
|
8729
9051
|
const wide = (process.stdout.columns ?? 80) >= 82;
|
|
8730
9052
|
if (!wide) {
|
|
8731
|
-
return /* @__PURE__ */
|
|
8732
|
-
/* @__PURE__ */
|
|
8733
|
-
/* @__PURE__ */
|
|
9053
|
+
return /* @__PURE__ */ jsxs9(Box9, { children: [
|
|
9054
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate" }),
|
|
9055
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2014 security control center" })
|
|
8734
9056
|
] });
|
|
8735
9057
|
}
|
|
8736
|
-
return /* @__PURE__ */
|
|
8737
|
-
BANNER_FULL.map((line, i) => /* @__PURE__ */
|
|
8738
|
-
/* @__PURE__ */
|
|
9058
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
9059
|
+
BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx9(Text9, { bold: true, color: BANNER_HEX[i], children: line }, i)),
|
|
9060
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
|
|
8739
9061
|
] });
|
|
8740
9062
|
}
|
|
8741
9063
|
function App() {
|
|
8742
9064
|
const { exit } = useApp();
|
|
8743
|
-
const [section, setSection] =
|
|
8744
|
-
const [focus, setFocus] =
|
|
8745
|
-
|
|
9065
|
+
const [section, setSection] = useState8(0);
|
|
9066
|
+
const [focus, setFocus] = useState8("nav");
|
|
9067
|
+
const [help, setHelp] = useState8(false);
|
|
9068
|
+
useInput7((input, key) => {
|
|
9069
|
+
if (help) {
|
|
9070
|
+
setHelp(false);
|
|
9071
|
+
return;
|
|
9072
|
+
}
|
|
9073
|
+
if (input === "?" && focus === "nav") {
|
|
9074
|
+
setHelp(true);
|
|
9075
|
+
return;
|
|
9076
|
+
}
|
|
8746
9077
|
if (focus === "nav") {
|
|
8747
9078
|
if (key.upArrow) setSection((n) => (n - 1 + SECTIONS.length) % SECTIONS.length);
|
|
8748
9079
|
else if (key.downArrow) setSection((n) => (n + 1) % SECTIONS.length);
|
|
@@ -8756,20 +9087,34 @@ function App() {
|
|
|
8756
9087
|
const cols = process.stdout.columns ?? 100;
|
|
8757
9088
|
const rows = process.stdout.rows ?? 30;
|
|
8758
9089
|
const current = SECTIONS[section];
|
|
9090
|
+
if (help) return /* @__PURE__ */ jsx9(HelpOverlay, { cols, rows });
|
|
8759
9091
|
if (current.label === "Live" && focus === "panel") {
|
|
8760
|
-
return /* @__PURE__ */
|
|
9092
|
+
return /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx9(LivePanel, { active: true, focused: true }) });
|
|
8761
9093
|
}
|
|
8762
9094
|
const Panel = current.Panel;
|
|
8763
|
-
return /* @__PURE__ */
|
|
8764
|
-
/* @__PURE__ */
|
|
8765
|
-
/* @__PURE__ */
|
|
8766
|
-
/* @__PURE__ */
|
|
8767
|
-
/* @__PURE__ */
|
|
9095
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
|
|
9096
|
+
/* @__PURE__ */ jsx9(Banner, {}),
|
|
9097
|
+
/* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexGrow: 1, children: [
|
|
9098
|
+
/* @__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)) }),
|
|
9099
|
+
/* @__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" }) })
|
|
8768
9100
|
] }),
|
|
8769
|
-
/* @__PURE__ */
|
|
9101
|
+
/* @__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"]] }) })
|
|
9102
|
+
] });
|
|
9103
|
+
}
|
|
9104
|
+
function HelpOverlay({ cols, rows }) {
|
|
9105
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
|
|
9106
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
|
|
9107
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, children: [
|
|
9108
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accent, children: group }),
|
|
9109
|
+
keys.map(([k, desc]) => /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
9110
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
|
|
9111
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: desc })
|
|
9112
|
+
] }, k))
|
|
9113
|
+
] }, group)) }),
|
|
9114
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "press any key to close" })
|
|
8770
9115
|
] });
|
|
8771
9116
|
}
|
|
8772
|
-
var SECTIONS, BANNER_HEX;
|
|
9117
|
+
var SECTIONS, BANNER_HEX, HELP;
|
|
8773
9118
|
var init_App = __esm({
|
|
8774
9119
|
"src/tui/App.tsx"() {
|
|
8775
9120
|
"use strict";
|
|
@@ -8782,8 +9127,10 @@ var init_App = __esm({
|
|
|
8782
9127
|
init_Dlp();
|
|
8783
9128
|
init_Stats();
|
|
8784
9129
|
init_Audit();
|
|
9130
|
+
init_Agents();
|
|
8785
9131
|
SECTIONS = [
|
|
8786
9132
|
{ label: "Live", Panel: LiveHint },
|
|
9133
|
+
{ label: "Agents", Panel: AgentsPanel },
|
|
8787
9134
|
{ label: "Policies", Panel: PoliciesPanel },
|
|
8788
9135
|
{ label: "Rate Limit", Panel: RateLimitPanel },
|
|
8789
9136
|
{ label: "DLP", Panel: DlpPanel },
|
|
@@ -8791,6 +9138,12 @@ var init_App = __esm({
|
|
|
8791
9138
|
{ label: "Audit", Panel: AuditPanel }
|
|
8792
9139
|
];
|
|
8793
9140
|
BANNER_HEX = ["#1432A0", "#2850BE", "#3C6ED7", "#5A8CE6", "#82AAF0", "#AAC8FA"];
|
|
9141
|
+
HELP = [
|
|
9142
|
+
["Global", [["\u2191\u2193", "move between sections"], ["\u2192 / enter", "open a section"], ["\u2190 / esc", "back to the menu"], ["?", "this help"], ["q", "quit"]]],
|
|
9143
|
+
["Live", [["\u2191\u2193", "select a stream row"], ["w", "whitelist the selected DENY"], ["b", "block the selected ALLOW"], ["d / x / r", "filter denies / dlp / rate-limit"], ["f", "local / cloud filter"], ["/", "search"], ["s", "sessions (\u2191\u2193 pick, enter open)"], ["space", "copy mode (freeze)"]]],
|
|
9144
|
+
["Policies", [["\u2191\u2193", "browse / select"], ["enter", "open rules \u2192 open a rule"], ["space", "toggle a rule on/off"], ["e", "flip effect"], ["n", "new rule"], ["d", "delete rule"], ["m", "flip mode"], ["D", "dry-run the draft"], ["v", "versions / rollback"], ["s", "save"], ["x", "discard"]]],
|
|
9145
|
+
["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"]]]
|
|
9146
|
+
];
|
|
8794
9147
|
}
|
|
8795
9148
|
});
|
|
8796
9149
|
|
|
@@ -8800,7 +9153,7 @@ __export(tui_exports, {
|
|
|
8800
9153
|
launchTui: () => launchTui
|
|
8801
9154
|
});
|
|
8802
9155
|
import { render } from "ink";
|
|
8803
|
-
import { jsx as
|
|
9156
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
8804
9157
|
async function launchTui() {
|
|
8805
9158
|
if (!process.stdout.isTTY || !process.stdin.isTTY) {
|
|
8806
9159
|
process.stderr.write(
|
|
@@ -8814,7 +9167,7 @@ async function launchTui() {
|
|
|
8814
9167
|
}
|
|
8815
9168
|
process.stdout.write("\x1B[?1049h\x1B[H");
|
|
8816
9169
|
try {
|
|
8817
|
-
const { waitUntilExit } = render(/* @__PURE__ */
|
|
9170
|
+
const { waitUntilExit } = render(/* @__PURE__ */ jsx10(App, {}));
|
|
8818
9171
|
await waitUntilExit();
|
|
8819
9172
|
} finally {
|
|
8820
9173
|
process.stdout.write("\x1B[?1049l");
|
|
@@ -8926,7 +9279,7 @@ var init_args = __esm({
|
|
|
8926
9279
|
});
|
|
8927
9280
|
|
|
8928
9281
|
// src/commands/policy.ts
|
|
8929
|
-
import { readFileSync as
|
|
9282
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
8930
9283
|
async function run(argv) {
|
|
8931
9284
|
const { positionals, flags } = parse(argv);
|
|
8932
9285
|
const sub = positionals[0];
|
|
@@ -9101,7 +9454,7 @@ function printRules(rules) {
|
|
|
9101
9454
|
}
|
|
9102
9455
|
async function resolveRules(target) {
|
|
9103
9456
|
if (target.endsWith(".json")) {
|
|
9104
|
-
const parsed = JSON.parse(
|
|
9457
|
+
const parsed = JSON.parse(readFileSync6(target, "utf-8"));
|
|
9105
9458
|
return parsed.rules ?? [];
|
|
9106
9459
|
}
|
|
9107
9460
|
const p = await api.policies.get(target);
|
|
@@ -9465,7 +9818,7 @@ async function runAgents(argv) {
|
|
|
9465
9818
|
table(
|
|
9466
9819
|
["STATUS", "AGENT", "CALLS", "DENY", "DLP", "TRUST", "CHARACTER"],
|
|
9467
9820
|
res.agents.map((a) => [
|
|
9468
|
-
|
|
9821
|
+
statusColor2(a.status),
|
|
9469
9822
|
cyan(truncate3(a.agent_name ?? a.agent_id ?? a.session_id, 20)),
|
|
9470
9823
|
String(a.total_calls),
|
|
9471
9824
|
a.denied_calls ? red(String(a.denied_calls)) : dim("0"),
|
|
@@ -9484,7 +9837,7 @@ async function runAgent(argv) {
|
|
|
9484
9837
|
const a = await api.agents.get(id);
|
|
9485
9838
|
if (json) return printJson(a), 0;
|
|
9486
9839
|
err("");
|
|
9487
|
-
err(` ${bold(a.agent_id)} status: ${
|
|
9840
|
+
err(` ${bold(a.agent_id)} status: ${statusColor2(String(a.status))}`);
|
|
9488
9841
|
const base = a.baseline;
|
|
9489
9842
|
if (base) {
|
|
9490
9843
|
err(` ${dim(base.character ?? "")} trust ${bold(String(base.trustScore ?? "\u2014"))}/100 deny-rate ${(Number(base.denyRate ?? 0) * 100).toFixed(0)}%`);
|
|
@@ -9504,21 +9857,21 @@ async function runAgent(argv) {
|
|
|
9504
9857
|
}
|
|
9505
9858
|
return 0;
|
|
9506
9859
|
}
|
|
9507
|
-
var
|
|
9860
|
+
var statusColor2;
|
|
9508
9861
|
var init_agents2 = __esm({
|
|
9509
9862
|
"src/commands/agents.ts"() {
|
|
9510
9863
|
"use strict";
|
|
9511
9864
|
init_api_client();
|
|
9512
9865
|
init_args();
|
|
9513
9866
|
init_format();
|
|
9514
|
-
|
|
9867
|
+
statusColor2 = (s) => s === "active" ? green(s) : s === "idle" ? yellow(s) : dim(s);
|
|
9515
9868
|
}
|
|
9516
9869
|
});
|
|
9517
9870
|
|
|
9518
9871
|
// src/commands/doctor.ts
|
|
9519
9872
|
import { existsSync as existsSync4, statSync as statSync2 } from "fs";
|
|
9520
|
-
import { homedir as
|
|
9521
|
-
import { join as
|
|
9873
|
+
import { homedir as homedir5 } from "os";
|
|
9874
|
+
import { join as join7 } from "path";
|
|
9522
9875
|
async function run6(argv) {
|
|
9523
9876
|
const { flags } = parse(argv);
|
|
9524
9877
|
const json = flagBool(flags, "json");
|
|
@@ -9578,14 +9931,14 @@ var init_doctor = __esm({
|
|
|
9578
9931
|
init_api_client();
|
|
9579
9932
|
init_format();
|
|
9580
9933
|
init_args();
|
|
9581
|
-
LOCAL_LOG2 =
|
|
9934
|
+
LOCAL_LOG2 = join7(homedir5(), ".solongate", "local-logs", "solongate-audit.jsonl");
|
|
9582
9935
|
}
|
|
9583
9936
|
});
|
|
9584
9937
|
|
|
9585
9938
|
// src/commands/watch.ts
|
|
9586
9939
|
import { closeSync as closeSync2, existsSync as existsSync5, openSync as openSync2, readSync as readSync2, statSync as statSync3 } from "fs";
|
|
9587
|
-
import { homedir as
|
|
9588
|
-
import { join as
|
|
9940
|
+
import { homedir as homedir6 } from "os";
|
|
9941
|
+
import { join as join8 } from "path";
|
|
9589
9942
|
function tailLocal(file, maxBytes = 131072) {
|
|
9590
9943
|
try {
|
|
9591
9944
|
const size = statSync3(file).size;
|
|
@@ -9692,7 +10045,7 @@ var init_watch = __esm({
|
|
|
9692
10045
|
init_cli_utils();
|
|
9693
10046
|
init_args();
|
|
9694
10047
|
init_format();
|
|
9695
|
-
LOCAL_LOG3 =
|
|
10048
|
+
LOCAL_LOG3 = join8(homedir6(), ".solongate", "local-logs", "solongate-audit.jsonl");
|
|
9696
10049
|
trunc = (s, n) => s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
|
|
9697
10050
|
time = (ms) => new Date(ms).toTimeString().slice(0, 8);
|
|
9698
10051
|
}
|
|
@@ -9994,9 +10347,9 @@ __export(global_install_exports, {
|
|
|
9994
10347
|
runGlobalRestore: () => runGlobalRestore,
|
|
9995
10348
|
unlockProtected: () => unlockProtected
|
|
9996
10349
|
});
|
|
9997
|
-
import { readFileSync as
|
|
9998
|
-
import { resolve as resolve4, join as
|
|
9999
|
-
import { homedir as
|
|
10350
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
|
|
10351
|
+
import { resolve as resolve4, join as join9, dirname } from "path";
|
|
10352
|
+
import { homedir as homedir7 } from "os";
|
|
10000
10353
|
import { fileURLToPath } from "url";
|
|
10001
10354
|
import { createInterface } from "readline";
|
|
10002
10355
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
@@ -10059,10 +10412,10 @@ function unlockFile(file) {
|
|
|
10059
10412
|
function protectedTargets() {
|
|
10060
10413
|
const p = globalPaths();
|
|
10061
10414
|
return [
|
|
10062
|
-
|
|
10063
|
-
|
|
10064
|
-
|
|
10065
|
-
|
|
10415
|
+
join9(p.hooksDir, "guard.mjs"),
|
|
10416
|
+
join9(p.hooksDir, "audit.mjs"),
|
|
10417
|
+
join9(p.hooksDir, "stop.mjs"),
|
|
10418
|
+
join9(p.hooksDir, "shield.mjs"),
|
|
10066
10419
|
p.configPath,
|
|
10067
10420
|
p.settingsPath
|
|
10068
10421
|
];
|
|
@@ -10074,26 +10427,26 @@ function unlockProtected() {
|
|
|
10074
10427
|
for (const f of protectedTargets()) unlockFile(f);
|
|
10075
10428
|
}
|
|
10076
10429
|
function globalPaths() {
|
|
10077
|
-
const home =
|
|
10078
|
-
const sgDir =
|
|
10079
|
-
const hooksDir =
|
|
10080
|
-
const claudeDir =
|
|
10430
|
+
const home = homedir7();
|
|
10431
|
+
const sgDir = join9(home, ".solongate");
|
|
10432
|
+
const hooksDir = join9(sgDir, "hooks");
|
|
10433
|
+
const claudeDir = join9(home, ".claude");
|
|
10081
10434
|
return {
|
|
10082
10435
|
home,
|
|
10083
10436
|
sgDir,
|
|
10084
10437
|
hooksDir,
|
|
10085
10438
|
claudeDir,
|
|
10086
|
-
settingsPath:
|
|
10087
|
-
backupPath:
|
|
10088
|
-
configPath:
|
|
10439
|
+
settingsPath: join9(claudeDir, "settings.json"),
|
|
10440
|
+
backupPath: join9(claudeDir, "settings.solongate.bak"),
|
|
10441
|
+
configPath: join9(sgDir, "cloud-guard.json")
|
|
10089
10442
|
};
|
|
10090
10443
|
}
|
|
10091
10444
|
function readHook(filename) {
|
|
10092
|
-
return
|
|
10445
|
+
return readFileSync7(join9(HOOKS_DIR, filename), "utf-8");
|
|
10093
10446
|
}
|
|
10094
10447
|
function readGuard() {
|
|
10095
|
-
const bundled =
|
|
10096
|
-
return existsSync6(bundled) ?
|
|
10448
|
+
const bundled = join9(HOOKS_DIR, "guard.bundled.mjs");
|
|
10449
|
+
return existsSync6(bundled) ? readFileSync7(bundled, "utf-8") : readHook("guard.mjs");
|
|
10097
10450
|
}
|
|
10098
10451
|
function ask(question) {
|
|
10099
10452
|
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
@@ -10107,13 +10460,13 @@ function runGlobalRestore() {
|
|
|
10107
10460
|
unlockProtected();
|
|
10108
10461
|
removeClaudeShim();
|
|
10109
10462
|
if (existsSync6(p.backupPath)) {
|
|
10110
|
-
|
|
10463
|
+
writeFileSync4(p.settingsPath, readFileSync7(p.backupPath, "utf-8"));
|
|
10111
10464
|
console.log(` Restored ${p.settingsPath} from backup.`);
|
|
10112
10465
|
} else if (existsSync6(p.settingsPath)) {
|
|
10113
10466
|
try {
|
|
10114
|
-
const s = JSON.parse(
|
|
10467
|
+
const s = JSON.parse(readFileSync7(p.settingsPath, "utf-8"));
|
|
10115
10468
|
delete s.hooks;
|
|
10116
|
-
|
|
10469
|
+
writeFileSync4(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
|
|
10117
10470
|
console.log(` Removed SolonGate hooks from ${p.settingsPath}.`);
|
|
10118
10471
|
} catch {
|
|
10119
10472
|
}
|
|
@@ -10147,18 +10500,18 @@ function shimTargets() {
|
|
|
10147
10500
|
return [];
|
|
10148
10501
|
}
|
|
10149
10502
|
}
|
|
10150
|
-
return [".bashrc", ".zshrc", ".profile"].map((f) =>
|
|
10503
|
+
return [".bashrc", ".zshrc", ".profile"].map((f) => join9(homedir7(), f)).filter((f) => existsSync6(f));
|
|
10151
10504
|
}
|
|
10152
10505
|
function writeShimBlock(file, block2) {
|
|
10153
10506
|
const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
|
|
10154
|
-
let content = existsSync6(file) ?
|
|
10507
|
+
let content = existsSync6(file) ? readFileSync7(file, "utf-8") : "";
|
|
10155
10508
|
content = content.replace(re, "");
|
|
10156
10509
|
if (block2) {
|
|
10157
10510
|
if (content.length && !content.endsWith("\n")) content += "\n";
|
|
10158
10511
|
content += block2 + "\n";
|
|
10159
10512
|
}
|
|
10160
|
-
|
|
10161
|
-
|
|
10513
|
+
mkdirSync4(dirname(file), { recursive: true });
|
|
10514
|
+
writeFileSync4(file, content);
|
|
10162
10515
|
}
|
|
10163
10516
|
function installClaudeShim(shieldPath) {
|
|
10164
10517
|
const real = resolveRealClaude();
|
|
@@ -10202,7 +10555,7 @@ async function runGlobalInstall(opts = {}) {
|
|
|
10202
10555
|
let apiKey = opts.apiKey || process.env["SOLONGATE_API_KEY"] || "";
|
|
10203
10556
|
if (!apiKey || apiKey === "sg_live_your_key_here") {
|
|
10204
10557
|
try {
|
|
10205
|
-
const cfg = JSON.parse(
|
|
10558
|
+
const cfg = JSON.parse(readFileSync7(p.configPath, "utf-8"));
|
|
10206
10559
|
if (cfg && typeof cfg.apiKey === "string") apiKey = cfg.apiKey;
|
|
10207
10560
|
} catch {
|
|
10208
10561
|
}
|
|
@@ -10215,22 +10568,22 @@ async function runGlobalInstall(opts = {}) {
|
|
|
10215
10568
|
process.exit(1);
|
|
10216
10569
|
}
|
|
10217
10570
|
const apiUrl = opts.apiUrl || process.env["SOLONGATE_API_URL"] || "https://api.solongate.com";
|
|
10218
|
-
|
|
10219
|
-
|
|
10571
|
+
mkdirSync4(p.hooksDir, { recursive: true });
|
|
10572
|
+
mkdirSync4(p.claudeDir, { recursive: true });
|
|
10220
10573
|
unlockProtected();
|
|
10221
|
-
|
|
10222
|
-
|
|
10223
|
-
|
|
10224
|
-
|
|
10574
|
+
writeFileSync4(join9(p.hooksDir, "guard.mjs"), readGuard());
|
|
10575
|
+
writeFileSync4(join9(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
|
|
10576
|
+
writeFileSync4(join9(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
|
|
10577
|
+
writeFileSync4(join9(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
|
|
10225
10578
|
console.log(` Installed hooks \u2192 ${p.hooksDir}`);
|
|
10226
10579
|
removeClaudeShim();
|
|
10227
|
-
|
|
10580
|
+
writeFileSync4(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
|
|
10228
10581
|
console.log(` Wrote ${p.configPath}`);
|
|
10229
10582
|
let existing = {};
|
|
10230
10583
|
if (existsSync6(p.settingsPath)) {
|
|
10231
|
-
const raw =
|
|
10584
|
+
const raw = readFileSync7(p.settingsPath, "utf-8");
|
|
10232
10585
|
if (!existsSync6(p.backupPath)) {
|
|
10233
|
-
|
|
10586
|
+
writeFileSync4(p.backupPath, raw);
|
|
10234
10587
|
console.log(` Backed up existing settings \u2192 ${p.backupPath}`);
|
|
10235
10588
|
}
|
|
10236
10589
|
try {
|
|
@@ -10239,9 +10592,9 @@ async function runGlobalInstall(opts = {}) {
|
|
|
10239
10592
|
existing = {};
|
|
10240
10593
|
}
|
|
10241
10594
|
}
|
|
10242
|
-
const guardAbs =
|
|
10243
|
-
const auditAbs =
|
|
10244
|
-
const stopAbs =
|
|
10595
|
+
const guardAbs = join9(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
|
|
10596
|
+
const auditAbs = join9(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
|
|
10597
|
+
const stopAbs = join9(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
|
|
10245
10598
|
const nodeBin = process.execPath.replace(/\\/g, "/");
|
|
10246
10599
|
const merged = {
|
|
10247
10600
|
...existing,
|
|
@@ -10251,7 +10604,7 @@ async function runGlobalInstall(opts = {}) {
|
|
|
10251
10604
|
Stop: [{ matcher: "", hooks: [{ type: "command", command: `"${nodeBin}" "${stopAbs}" claude-code "Claude Code"` }] }]
|
|
10252
10605
|
}
|
|
10253
10606
|
};
|
|
10254
|
-
|
|
10607
|
+
writeFileSync4(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
|
|
10255
10608
|
console.log(` Registered global hooks \u2192 ${p.settingsPath}`);
|
|
10256
10609
|
if (process.env["SOLONGATE_OS_LOCK"] === "1") {
|
|
10257
10610
|
lockProtected();
|
|
@@ -10431,11 +10784,11 @@ import { createServer, request as httpRequest } from "http";
|
|
|
10431
10784
|
import { request as httpsRequest } from "https";
|
|
10432
10785
|
import { spawn as spawn3 } from "child_process";
|
|
10433
10786
|
import { URL as URL2 } from "url";
|
|
10434
|
-
import { readFileSync as
|
|
10787
|
+
import { readFileSync as readFileSync8, existsSync as existsSync7, readdirSync, statSync as statSync4 } from "fs";
|
|
10435
10788
|
import { resolve as resolve5 } from "path";
|
|
10436
|
-
import { homedir as
|
|
10789
|
+
import { homedir as homedir8 } from "os";
|
|
10437
10790
|
function findCacheFile() {
|
|
10438
|
-
const dir = resolve5(
|
|
10791
|
+
const dir = resolve5(homedir8(), ".solongate");
|
|
10439
10792
|
const envSel = process.env.SOLONGATE_AGENT_ID;
|
|
10440
10793
|
if (envSel) {
|
|
10441
10794
|
const f = resolve5(dir, ".policy-cache-" + envSel.replace(/[^a-zA-Z0-9_-]/g, "_") + ".json");
|
|
@@ -10461,7 +10814,7 @@ function loadCfg() {
|
|
|
10461
10814
|
try {
|
|
10462
10815
|
const f = findCacheFile();
|
|
10463
10816
|
if (f && existsSync7(f)) {
|
|
10464
|
-
const c2 = JSON.parse(
|
|
10817
|
+
const c2 = JSON.parse(readFileSync8(f, "utf-8"));
|
|
10465
10818
|
const d = c2?.security?.dlpRedact;
|
|
10466
10819
|
const g = c2?.security?.ghost;
|
|
10467
10820
|
const ghost = g && Array.isArray(g.patterns) ? g.patterns : [];
|
|
@@ -10737,9 +11090,9 @@ __export(logs_server_exports, {
|
|
|
10737
11090
|
runLogsServer: () => runLogsServer
|
|
10738
11091
|
});
|
|
10739
11092
|
import { createServer as createServer2 } from "http";
|
|
10740
|
-
import { readFileSync as
|
|
10741
|
-
import { resolve as resolve6, join as
|
|
10742
|
-
import { homedir as
|
|
11093
|
+
import { readFileSync as readFileSync9, statSync as statSync5 } from "fs";
|
|
11094
|
+
import { resolve as resolve6, join as join10, isAbsolute } from "path";
|
|
11095
|
+
import { homedir as homedir9 } from "os";
|
|
10743
11096
|
import { readdirSync as readdirSync2 } from "fs";
|
|
10744
11097
|
function allowedOrigins() {
|
|
10745
11098
|
const base = [
|
|
@@ -10756,15 +11109,15 @@ function resolveLocalLogDir(rawPath) {
|
|
|
10756
11109
|
const dir = String(rawPath || "").trim().replace(/[\\/]+$/, "");
|
|
10757
11110
|
if (!dir) return null;
|
|
10758
11111
|
if (isAbsolute(dir)) return dir;
|
|
10759
|
-
return resolve6(
|
|
11112
|
+
return resolve6(homedir9(), ".solongate", "local-logs");
|
|
10760
11113
|
}
|
|
10761
11114
|
async function findLogDir() {
|
|
10762
|
-
const base = resolve6(
|
|
11115
|
+
const base = resolve6(homedir9(), ".solongate");
|
|
10763
11116
|
try {
|
|
10764
11117
|
const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
|
|
10765
11118
|
for (const f of files) {
|
|
10766
11119
|
try {
|
|
10767
|
-
const c2 = JSON.parse(
|
|
11120
|
+
const c2 = JSON.parse(readFileSync9(join10(base, f), "utf-8"));
|
|
10768
11121
|
const p = c2?.security?.localLogs?.path;
|
|
10769
11122
|
if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
|
|
10770
11123
|
} catch {
|
|
@@ -10773,7 +11126,7 @@ async function findLogDir() {
|
|
|
10773
11126
|
} catch {
|
|
10774
11127
|
}
|
|
10775
11128
|
try {
|
|
10776
|
-
const cfgRaw =
|
|
11129
|
+
const cfgRaw = readFileSync9(join10(base, "cloud-guard.json"), "utf-8");
|
|
10777
11130
|
const { apiKey, apiUrl } = JSON.parse(cfgRaw);
|
|
10778
11131
|
if (apiKey) {
|
|
10779
11132
|
const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
|
|
@@ -10802,7 +11155,7 @@ function setCors(req, res) {
|
|
|
10802
11155
|
}
|
|
10803
11156
|
function fileInfo(dir) {
|
|
10804
11157
|
if (!dir) return { file: null, exists: false, size: 0, mtimeMs: 0 };
|
|
10805
|
-
const file =
|
|
11158
|
+
const file = join10(dir, LOG_FILENAME);
|
|
10806
11159
|
try {
|
|
10807
11160
|
const st = statSync5(file);
|
|
10808
11161
|
return { file, exists: true, size: st.size, mtimeMs: st.mtimeMs };
|
|
@@ -10863,7 +11216,7 @@ async function runLogsServer() {
|
|
|
10863
11216
|
return;
|
|
10864
11217
|
}
|
|
10865
11218
|
try {
|
|
10866
|
-
const text =
|
|
11219
|
+
const text = readFileSync9(info.file, "utf-8");
|
|
10867
11220
|
res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8", "Last-Modified": lastMod, "X-Solongate-Exists": "1" });
|
|
10868
11221
|
res.end(text);
|
|
10869
11222
|
} catch {
|
|
@@ -10915,7 +11268,7 @@ var init_logs_server = __esm({
|
|
|
10915
11268
|
|
|
10916
11269
|
// src/inject.ts
|
|
10917
11270
|
var inject_exports = {};
|
|
10918
|
-
import { readFileSync as
|
|
11271
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync5, existsSync as existsSync8, copyFileSync } from "fs";
|
|
10919
11272
|
import { resolve as resolve7 } from "path";
|
|
10920
11273
|
import { execSync } from "child_process";
|
|
10921
11274
|
function parseInjectArgs(argv) {
|
|
@@ -10975,7 +11328,7 @@ WHAT IT DOES
|
|
|
10975
11328
|
function detectProject() {
|
|
10976
11329
|
if (!existsSync8(resolve7("package.json"))) return false;
|
|
10977
11330
|
try {
|
|
10978
|
-
const pkg = JSON.parse(
|
|
11331
|
+
const pkg = JSON.parse(readFileSync10(resolve7("package.json"), "utf-8"));
|
|
10979
11332
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
10980
11333
|
return !!(allDeps["@modelcontextprotocol/sdk"] || allDeps["@modelcontextprotocol/server"]);
|
|
10981
11334
|
} catch {
|
|
@@ -10984,7 +11337,7 @@ function detectProject() {
|
|
|
10984
11337
|
}
|
|
10985
11338
|
function findTsEntryFile() {
|
|
10986
11339
|
try {
|
|
10987
|
-
const pkg = JSON.parse(
|
|
11340
|
+
const pkg = JSON.parse(readFileSync10(resolve7("package.json"), "utf-8"));
|
|
10988
11341
|
if (pkg.bin) {
|
|
10989
11342
|
const binPath = typeof pkg.bin === "string" ? pkg.bin : Object.values(pkg.bin)[0];
|
|
10990
11343
|
if (typeof binPath === "string") {
|
|
@@ -11011,7 +11364,7 @@ function findTsEntryFile() {
|
|
|
11011
11364
|
const full = resolve7(c2);
|
|
11012
11365
|
if (existsSync8(full)) {
|
|
11013
11366
|
try {
|
|
11014
|
-
const content =
|
|
11367
|
+
const content = readFileSync10(full, "utf-8");
|
|
11015
11368
|
if (content.includes("McpServer") || content.includes("McpServer")) {
|
|
11016
11369
|
return full;
|
|
11017
11370
|
}
|
|
@@ -11031,7 +11384,7 @@ function detectPackageManager() {
|
|
|
11031
11384
|
}
|
|
11032
11385
|
function installSdk() {
|
|
11033
11386
|
try {
|
|
11034
|
-
const pkg = JSON.parse(
|
|
11387
|
+
const pkg = JSON.parse(readFileSync10(resolve7("package.json"), "utf-8"));
|
|
11035
11388
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
11036
11389
|
if (allDeps["@solongate/proxy"]) {
|
|
11037
11390
|
log3(" @solongate/proxy already installed");
|
|
@@ -11052,7 +11405,7 @@ function installSdk() {
|
|
|
11052
11405
|
}
|
|
11053
11406
|
}
|
|
11054
11407
|
function injectTypeScript(filePath) {
|
|
11055
|
-
const original =
|
|
11408
|
+
const original = readFileSync10(filePath, "utf-8");
|
|
11056
11409
|
const changes = [];
|
|
11057
11410
|
let modified = original;
|
|
11058
11411
|
if (modified.includes("SecureMcpServer")) {
|
|
@@ -11223,7 +11576,7 @@ async function main2() {
|
|
|
11223
11576
|
log3("");
|
|
11224
11577
|
log3(` Backup: ${backupPath}`);
|
|
11225
11578
|
}
|
|
11226
|
-
|
|
11579
|
+
writeFileSync5(entryFile, result.modified);
|
|
11227
11580
|
log3("");
|
|
11228
11581
|
log3(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
11229
11582
|
log3(" \u2502 SolonGate SDK injected successfully! \u2502");
|
|
@@ -11252,8 +11605,8 @@ var init_inject = __esm({
|
|
|
11252
11605
|
|
|
11253
11606
|
// src/create.ts
|
|
11254
11607
|
var create_exports = {};
|
|
11255
|
-
import { mkdirSync as
|
|
11256
|
-
import { resolve as resolve8, join as
|
|
11608
|
+
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync6, existsSync as existsSync9 } from "fs";
|
|
11609
|
+
import { resolve as resolve8, join as join11 } from "path";
|
|
11257
11610
|
import { execSync as execSync2 } from "child_process";
|
|
11258
11611
|
function withSpinner(message, fn) {
|
|
11259
11612
|
const frames = ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"];
|
|
@@ -11332,8 +11685,8 @@ EXAMPLES
|
|
|
11332
11685
|
`);
|
|
11333
11686
|
}
|
|
11334
11687
|
function createProject(dir, name, _policy) {
|
|
11335
|
-
|
|
11336
|
-
|
|
11688
|
+
writeFileSync6(
|
|
11689
|
+
join11(dir, "package.json"),
|
|
11337
11690
|
JSON.stringify(
|
|
11338
11691
|
{
|
|
11339
11692
|
name,
|
|
@@ -11362,8 +11715,8 @@ function createProject(dir, name, _policy) {
|
|
|
11362
11715
|
2
|
|
11363
11716
|
) + "\n"
|
|
11364
11717
|
);
|
|
11365
|
-
|
|
11366
|
-
|
|
11718
|
+
writeFileSync6(
|
|
11719
|
+
join11(dir, "tsconfig.json"),
|
|
11367
11720
|
JSON.stringify(
|
|
11368
11721
|
{
|
|
11369
11722
|
compilerOptions: {
|
|
@@ -11383,9 +11736,9 @@ function createProject(dir, name, _policy) {
|
|
|
11383
11736
|
2
|
|
11384
11737
|
) + "\n"
|
|
11385
11738
|
);
|
|
11386
|
-
|
|
11387
|
-
|
|
11388
|
-
|
|
11739
|
+
mkdirSync5(join11(dir, "src"), { recursive: true });
|
|
11740
|
+
writeFileSync6(
|
|
11741
|
+
join11(dir, "src", "index.ts"),
|
|
11389
11742
|
`#!/usr/bin/env node
|
|
11390
11743
|
|
|
11391
11744
|
console.log = (...args: unknown[]) => {
|
|
@@ -11426,8 +11779,8 @@ console.log('');
|
|
|
11426
11779
|
console.log('Press Ctrl+C to stop.');
|
|
11427
11780
|
`
|
|
11428
11781
|
);
|
|
11429
|
-
|
|
11430
|
-
|
|
11782
|
+
writeFileSync6(
|
|
11783
|
+
join11(dir, ".mcp.json"),
|
|
11431
11784
|
JSON.stringify(
|
|
11432
11785
|
{
|
|
11433
11786
|
mcpServers: {
|
|
@@ -11444,13 +11797,13 @@ console.log('Press Ctrl+C to stop.');
|
|
|
11444
11797
|
2
|
|
11445
11798
|
) + "\n"
|
|
11446
11799
|
);
|
|
11447
|
-
|
|
11448
|
-
|
|
11800
|
+
writeFileSync6(
|
|
11801
|
+
join11(dir, ".env"),
|
|
11449
11802
|
`SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
|
|
11450
11803
|
`
|
|
11451
11804
|
);
|
|
11452
|
-
|
|
11453
|
-
|
|
11805
|
+
writeFileSync6(
|
|
11806
|
+
join11(dir, ".gitignore"),
|
|
11454
11807
|
`node_modules/
|
|
11455
11808
|
dist/
|
|
11456
11809
|
*.solongate-backup
|
|
@@ -11469,7 +11822,7 @@ async function main3() {
|
|
|
11469
11822
|
process.exit(1);
|
|
11470
11823
|
}
|
|
11471
11824
|
withSpinner(`Setting up ${opts.name}...`, () => {
|
|
11472
|
-
|
|
11825
|
+
mkdirSync5(dir, { recursive: true });
|
|
11473
11826
|
createProject(dir, opts.name, opts.policy);
|
|
11474
11827
|
});
|
|
11475
11828
|
if (!opts.noInstall) {
|
|
@@ -11543,14 +11896,14 @@ var init_create = __esm({
|
|
|
11543
11896
|
|
|
11544
11897
|
// src/pull-push.ts
|
|
11545
11898
|
var pull_push_exports = {};
|
|
11546
|
-
import { readFileSync as
|
|
11899
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, existsSync as existsSync10 } from "fs";
|
|
11547
11900
|
import { resolve as resolve9 } from "path";
|
|
11548
11901
|
function loadEnv() {
|
|
11549
11902
|
if (process.env.SOLONGATE_API_KEY) return;
|
|
11550
11903
|
const envPath = resolve9(".env");
|
|
11551
11904
|
if (!existsSync10(envPath)) return;
|
|
11552
11905
|
try {
|
|
11553
|
-
const content =
|
|
11906
|
+
const content = readFileSync11(envPath, "utf-8");
|
|
11554
11907
|
for (const line of content.split("\n")) {
|
|
11555
11908
|
const trimmed = line.trim();
|
|
11556
11909
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -11738,7 +12091,7 @@ async function pull(apiKey, file, policyId) {
|
|
|
11738
12091
|
const policy = await fetchCloudPolicy(apiKey, API_URL, policyId);
|
|
11739
12092
|
const { id: _id, ...policyWithoutId } = policy;
|
|
11740
12093
|
const json = JSON.stringify(policyWithoutId, null, 2) + "\n";
|
|
11741
|
-
|
|
12094
|
+
writeFileSync7(file, json, "utf-8");
|
|
11742
12095
|
log5("");
|
|
11743
12096
|
log5(green2(" Saved to: ") + file);
|
|
11744
12097
|
log5(` ${dim2("Name:")} ${policy.name}`);
|
|
@@ -11767,7 +12120,7 @@ async function push(apiKey, file, policyId) {
|
|
|
11767
12120
|
log5(" solongate-proxy list");
|
|
11768
12121
|
process.exit(1);
|
|
11769
12122
|
}
|
|
11770
|
-
const content =
|
|
12123
|
+
const content = readFileSync11(file, "utf-8");
|
|
11771
12124
|
let policy;
|
|
11772
12125
|
try {
|
|
11773
12126
|
policy = JSON.parse(content);
|