@solongate/proxy 0.81.27 → 0.81.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +130 -96
- package/dist/tui/index.js +61 -27
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8877,6 +8877,9 @@ var init_Dlp = __esm({
|
|
|
8877
8877
|
// src/tui/panels/Audit.tsx
|
|
8878
8878
|
import { Box as Box6, Text as Text6, useInput as useInput5 } from "ink";
|
|
8879
8879
|
import TextInput4 from "ink-text-input";
|
|
8880
|
+
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
8881
|
+
import { homedir as homedir6 } from "os";
|
|
8882
|
+
import { join as join8 } from "path";
|
|
8880
8883
|
import { useState as useState6 } from "react";
|
|
8881
8884
|
import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
8882
8885
|
function loadLocalRows() {
|
|
@@ -8917,13 +8920,14 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
8917
8920
|
const [confirm, setConfirm] = useState6(null);
|
|
8918
8921
|
const [msg, setMsg] = useState6(null);
|
|
8919
8922
|
const [showHelp, setShowHelp] = useState6(false);
|
|
8923
|
+
const [frozen, setFrozen] = useState6(false);
|
|
8920
8924
|
const toTop = () => setSel(0);
|
|
8921
8925
|
const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
|
|
8922
8926
|
const tsQ = useLoader(() => source === "cloud" ? api.stats.timeseries({ period: "24h" }) : Promise.resolve(null), [source]);
|
|
8923
8927
|
usePoll(() => {
|
|
8924
8928
|
statsQ.reloadQuiet();
|
|
8925
8929
|
tsQ.reloadQuiet();
|
|
8926
|
-
}, 15e3, active2 && source === "cloud");
|
|
8930
|
+
}, 15e3, active2 && source === "cloud" && !frozen);
|
|
8927
8931
|
const query = {
|
|
8928
8932
|
filter: DECISIONS[di],
|
|
8929
8933
|
signal: SIGNALS[gi],
|
|
@@ -8938,29 +8942,28 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
8938
8942
|
() => source === "cloud" ? api.audit.list(query) : Promise.resolve(null),
|
|
8939
8943
|
[source, di, gi, tool, agent, search, sessFilter, page]
|
|
8940
8944
|
);
|
|
8941
|
-
usePoll(cloudQ.reloadQuiet, 6e3, active2 && source === "cloud" && view === "logs" && !editing && page === 0);
|
|
8945
|
+
usePoll(cloudQ.reloadQuiet, 6e3, active2 && source === "cloud" && view === "logs" && !editing && page === 0 && !frozen);
|
|
8942
8946
|
const localQ = useLoader(() => source === "local" ? Promise.resolve(loadLocalRows()) : Promise.resolve(null), [source]);
|
|
8943
|
-
usePoll(localQ.reloadQuiet, 6e3, active2 && source === "local" && view === "logs" && !editing && page === 0);
|
|
8947
|
+
usePoll(localQ.reloadQuiet, 6e3, active2 && source === "local" && view === "logs" && !editing && page === 0 && !frozen);
|
|
8948
|
+
const q = search.trim().toLowerCase();
|
|
8949
|
+
const localFiltered = (localQ.data ?? []).filter((r) => {
|
|
8950
|
+
if (DECISIONS[di] && r.decision !== DECISIONS[di]) return false;
|
|
8951
|
+
if (SIGNALS[gi] === "dlp" && r.dlp.length === 0) return false;
|
|
8952
|
+
if (SIGNALS[gi] === "ratelimit" && !r.burst) return false;
|
|
8953
|
+
if (tool && !r.tool.toLowerCase().includes(tool.toLowerCase())) return false;
|
|
8954
|
+
if (agent && (r.agent ?? "").toLowerCase() !== agent.toLowerCase()) return false;
|
|
8955
|
+
if (sessFilter && r.session !== sessFilter) return false;
|
|
8956
|
+
if (q && !`${r.tool} ${r.agent ?? ""} ${r.reason ?? ""} ${r.args ?? ""}`.toLowerCase().includes(q)) return false;
|
|
8957
|
+
return true;
|
|
8958
|
+
});
|
|
8944
8959
|
let pageRows = [];
|
|
8945
8960
|
let total = 0;
|
|
8946
8961
|
if (source === "cloud") {
|
|
8947
8962
|
pageRows = (cloudQ.data?.entries ?? []).map(cloudRow).sort((a, b) => b.at - a.at);
|
|
8948
8963
|
total = cloudQ.data?.total ?? 0;
|
|
8949
8964
|
} else {
|
|
8950
|
-
|
|
8951
|
-
|
|
8952
|
-
const filtered = all.filter((r) => {
|
|
8953
|
-
if (DECISIONS[di] && r.decision !== DECISIONS[di]) return false;
|
|
8954
|
-
if (SIGNALS[gi] === "dlp" && r.dlp.length === 0) return false;
|
|
8955
|
-
if (SIGNALS[gi] === "ratelimit" && !r.burst) return false;
|
|
8956
|
-
if (tool && !r.tool.toLowerCase().includes(tool.toLowerCase())) return false;
|
|
8957
|
-
if (agent && (r.agent ?? "").toLowerCase() !== agent.toLowerCase()) return false;
|
|
8958
|
-
if (sessFilter && r.session !== sessFilter) return false;
|
|
8959
|
-
if (q && !`${r.tool} ${r.agent ?? ""} ${r.reason ?? ""} ${r.args ?? ""}`.toLowerCase().includes(q)) return false;
|
|
8960
|
-
return true;
|
|
8961
|
-
});
|
|
8962
|
-
total = filtered.length;
|
|
8963
|
-
pageRows = filtered.slice(page * PAGE, page * PAGE + PAGE);
|
|
8965
|
+
total = localFiltered.length;
|
|
8966
|
+
pageRows = localFiltered.slice(page * PAGE, page * PAGE + PAGE);
|
|
8964
8967
|
}
|
|
8965
8968
|
const pages = Math.max(1, Math.ceil(total / PAGE));
|
|
8966
8969
|
const current = pageRows[Math.min(sel, Math.max(0, pageRows.length - 1))];
|
|
@@ -8968,7 +8971,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
8968
8971
|
() => source === "cloud" ? api.agents.live({ limit: 100, includeDeactivated: true }) : Promise.resolve(null),
|
|
8969
8972
|
[source]
|
|
8970
8973
|
);
|
|
8971
|
-
usePoll(agentsQ.reloadQuiet, 8e3, active2 && source === "cloud" && view === "sessions");
|
|
8974
|
+
usePoll(agentsQ.reloadQuiet, 8e3, active2 && source === "cloud" && view === "sessions" && !frozen);
|
|
8972
8975
|
let sessions = [];
|
|
8973
8976
|
if (source === "cloud") {
|
|
8974
8977
|
sessions = (agentsQ.data?.agents ?? []).map((a) => ({
|
|
@@ -9027,8 +9030,30 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9027
9030
|
toTop();
|
|
9028
9031
|
}).catch((e) => setMsg({ text: "\u2717 " + (e instanceof Error ? e.message : String(e)), level: "bad" }));
|
|
9029
9032
|
};
|
|
9033
|
+
const doExport = (kind) => {
|
|
9034
|
+
setMsg({ text: "exporting\u2026", level: "ok" });
|
|
9035
|
+
const run12 = async () => {
|
|
9036
|
+
const dir = join8(homedir6(), ".solongate");
|
|
9037
|
+
const file = join8(dir, `audit-export-${source}.jsonl`);
|
|
9038
|
+
let rows2;
|
|
9039
|
+
if (kind === "page") rows2 = pageRows;
|
|
9040
|
+
else if (source === "cloud") {
|
|
9041
|
+
const r = await api.audit.list({ ...query, limit: 1e4, offset: 0 });
|
|
9042
|
+
rows2 = r.entries.map(cloudRow).sort((a, b) => b.at - a.at);
|
|
9043
|
+
} else rows2 = localFiltered;
|
|
9044
|
+
mkdirSync4(dir, { recursive: true });
|
|
9045
|
+
writeFileSync5(file, rows2.map((x) => JSON.stringify(x)).join("\n") + (rows2.length ? "\n" : ""));
|
|
9046
|
+
return { n: rows2.length, file };
|
|
9047
|
+
};
|
|
9048
|
+
run12().then(({ n, file }) => setMsg({ text: `\u2713 exported ${n} rows \u2192 ${file}`, level: "ok" })).catch((e) => setMsg({ text: "\u2717 export failed: " + (e instanceof Error ? e.message : String(e)), level: "bad" }));
|
|
9049
|
+
};
|
|
9030
9050
|
useInput5(
|
|
9031
9051
|
(input, key) => {
|
|
9052
|
+
if (input === " ") {
|
|
9053
|
+
setFrozen((f) => !f);
|
|
9054
|
+
return;
|
|
9055
|
+
}
|
|
9056
|
+
if (frozen) return;
|
|
9032
9057
|
if (showHelp) {
|
|
9033
9058
|
setShowHelp(false);
|
|
9034
9059
|
return;
|
|
@@ -9115,7 +9140,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9115
9140
|
if (!current) return;
|
|
9116
9141
|
if (confirm?.kind !== "one" || confirm.key !== current.id) {
|
|
9117
9142
|
setConfirm({ kind: "one", key: current.id });
|
|
9118
|
-
setMsg({ text: `delete ${current.decision} ${current.tool} (${ago(current.at)} ago)
|
|
9143
|
+
setMsg({ text: `x = delete ONLY the selected entry: ${current.decision} ${current.tool} (${ago(current.at)} ago) \u2014 press x again`, level: "bad" });
|
|
9119
9144
|
return;
|
|
9120
9145
|
}
|
|
9121
9146
|
setConfirm(null);
|
|
@@ -9123,12 +9148,14 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9123
9148
|
} else if (input === "X") {
|
|
9124
9149
|
if (confirm?.kind !== "all") {
|
|
9125
9150
|
setConfirm({ kind: "all", key: "all" });
|
|
9126
|
-
setMsg({ text:
|
|
9151
|
+
setMsg({ text: `\u26A0 X = delete ALL ${source} logs \u2014 every one of the ${total} matched entries! press X again`, level: "bad" });
|
|
9127
9152
|
return;
|
|
9128
9153
|
}
|
|
9129
9154
|
setConfirm(null);
|
|
9130
9155
|
doDelete("all");
|
|
9131
|
-
} else if (input === "
|
|
9156
|
+
} else if (input === "e") doExport("page");
|
|
9157
|
+
else if (input === "E") doExport("all");
|
|
9158
|
+
else if (input === "t") setEditing("tool");
|
|
9132
9159
|
else if (input === "n") setEditing("agent");
|
|
9133
9160
|
else if (input === "/") setEditing("search");
|
|
9134
9161
|
else if (input === "c") {
|
|
@@ -9179,6 +9206,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9179
9206
|
LOCAL_LOG
|
|
9180
9207
|
] })
|
|
9181
9208
|
] });
|
|
9209
|
+
const copyBanner = frozen ? /* @__PURE__ */ jsx6(Text6, { backgroundColor: "#123d1f", color: "#7bd88f", bold: true, wrap: "truncate", children: " \u23F5 COPY MODE \u2014 screen frozen, select & copy freely \xB7 space resume " }) : null;
|
|
9182
9210
|
const srcChip = /* @__PURE__ */ jsxs6(Text6, { children: [
|
|
9183
9211
|
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "src:" }),
|
|
9184
9212
|
/* @__PURE__ */ jsx6(Text6, { color: source === "local" ? theme.ok : "#4f6db8", bold: true, children: source }),
|
|
@@ -9204,11 +9232,12 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9204
9232
|
const argLines = current.args ? wrapLines(prettyJson(current.args), bodyW) : ["(no arguments recorded)"];
|
|
9205
9233
|
const sessionRows = current.session ? Math.min(4, sessionCalls.length) + 2 : 1;
|
|
9206
9234
|
const reasonShown = reasonLines.slice(0, 4);
|
|
9207
|
-
const argBudget = Math.max(3, rows - 2 - 5 - reasonShown.length - 1 - sessionRows - 1);
|
|
9235
|
+
const argBudget = Math.max(3, rows - 2 - 5 - reasonShown.length - 1 - sessionRows - 1 - (frozen ? 1 : 0));
|
|
9208
9236
|
const maxScroll = Math.max(0, argLines.length - argBudget);
|
|
9209
9237
|
const off = Math.min(detailScroll, maxScroll);
|
|
9210
9238
|
const argWin = argLines.slice(off, off + argBudget);
|
|
9211
9239
|
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
9240
|
+
copyBanner,
|
|
9212
9241
|
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
9213
9242
|
/* @__PURE__ */ jsx6(Text6, { color: decisionColor(current.decision), bold: true, children: current.decision }),
|
|
9214
9243
|
/* @__PURE__ */ jsx6(Text6, { color: theme.accent, children: " " + current.tool }),
|
|
@@ -9267,8 +9296,9 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9267
9296
|
const selC = Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1));
|
|
9268
9297
|
const start2 = Math.min(Math.max(0, selC - Math.floor((listRows2 - 1) / 2)), Math.max(0, sessionsFiltered.length - listRows2));
|
|
9269
9298
|
const win = sessionsFiltered.slice(start2, start2 + listRows2);
|
|
9270
|
-
return /* @__PURE__ */ jsx6(DataView, { loading: sessLoading, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
9299
|
+
return /* @__PURE__ */ jsx6(DataView, { loading: sessLoading && !frozen, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
9271
9300
|
strip,
|
|
9301
|
+
copyBanner,
|
|
9272
9302
|
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
9273
9303
|
srcChip,
|
|
9274
9304
|
/* @__PURE__ */ jsx6(Text6, { color: theme.accentBright, bold: true, children: "SESSIONS" }),
|
|
@@ -9330,15 +9360,16 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9330
9360
|
] }) : null
|
|
9331
9361
|
] }) });
|
|
9332
9362
|
}
|
|
9333
|
-
const headerRows = 6 + (editing && editing !== "sess-search" ? 1 : 0) + (msg ? 1 : 0);
|
|
9363
|
+
const headerRows = 6 + (editing && editing !== "sess-search" ? 1 : 0) + (msg ? 1 : 0) + (frozen ? 1 : 0);
|
|
9334
9364
|
const listRows = Math.max(4, rows - headerRows);
|
|
9335
9365
|
const selClamped = Math.min(sel, Math.max(0, pageRows.length - 1));
|
|
9336
9366
|
const maxStart = Math.max(0, pageRows.length - listRows);
|
|
9337
9367
|
const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
|
|
9338
9368
|
const windowed = pageRows.slice(start, start + listRows);
|
|
9339
9369
|
const reasonW = Math.min(60, Math.max(12, cols - 67));
|
|
9340
|
-
return /* @__PURE__ */ jsx6(DataView, { loading: logsLoading, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
9370
|
+
return /* @__PURE__ */ jsx6(DataView, { loading: logsLoading && !frozen, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
9341
9371
|
strip,
|
|
9372
|
+
copyBanner,
|
|
9342
9373
|
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
9343
9374
|
srcChip,
|
|
9344
9375
|
/* @__PURE__ */ jsx6(Text6, { color: theme.accentBright, bold: true, children: "LOGS" }),
|
|
@@ -9451,8 +9482,10 @@ var init_Audit = __esm({
|
|
|
9451
9482
|
["g", "signal filter: all \u2192 dlp \u2192 ratelimit"],
|
|
9452
9483
|
["t / n", "tool / agent filter (type, enter done)"],
|
|
9453
9484
|
["/", "free-text search"],
|
|
9454
|
-
["x", "delete the selected entry (press x twice)"],
|
|
9455
|
-
["X", "delete ALL logs of the
|
|
9485
|
+
["x", "delete ONLY the selected entry (press x twice)"],
|
|
9486
|
+
["X", "delete ALL matched logs of the source (press X twice)"],
|
|
9487
|
+
["e", "export this page \u2192 ~/.solongate/audit-export-<src>.jsonl"],
|
|
9488
|
+
["E", "export ALL matched rows (cloud: up to 10k)"],
|
|
9456
9489
|
["c", "clear every filter (incl. session)"]
|
|
9457
9490
|
]
|
|
9458
9491
|
],
|
|
@@ -9471,6 +9504,7 @@ var init_Audit = __esm({
|
|
|
9471
9504
|
[
|
|
9472
9505
|
["v", "switch logs \u2194 sessions"],
|
|
9473
9506
|
["s", "switch source cloud \u2194 local file"],
|
|
9507
|
+
["space", "copy mode: freeze screen for mouse selection"],
|
|
9474
9508
|
["?", "this help \xB7 any key closes"],
|
|
9475
9509
|
["esc", "back to the menu"]
|
|
9476
9510
|
]
|
|
@@ -10540,8 +10574,8 @@ var init_agents2 = __esm({
|
|
|
10540
10574
|
|
|
10541
10575
|
// src/commands/doctor.ts
|
|
10542
10576
|
import { existsSync as existsSync4, statSync as statSync2 } from "fs";
|
|
10543
|
-
import { homedir as
|
|
10544
|
-
import { join as
|
|
10577
|
+
import { homedir as homedir7 } from "os";
|
|
10578
|
+
import { join as join9 } from "path";
|
|
10545
10579
|
async function run6(argv) {
|
|
10546
10580
|
const { flags } = parse(argv);
|
|
10547
10581
|
const json = flagBool(flags, "json");
|
|
@@ -10601,14 +10635,14 @@ var init_doctor = __esm({
|
|
|
10601
10635
|
init_api_client();
|
|
10602
10636
|
init_format();
|
|
10603
10637
|
init_args();
|
|
10604
|
-
LOCAL_LOG2 =
|
|
10638
|
+
LOCAL_LOG2 = join9(homedir7(), ".solongate", "local-logs", "solongate-audit.jsonl");
|
|
10605
10639
|
}
|
|
10606
10640
|
});
|
|
10607
10641
|
|
|
10608
10642
|
// src/commands/watch.ts
|
|
10609
10643
|
import { closeSync as closeSync2, existsSync as existsSync5, openSync as openSync2, readSync as readSync2, statSync as statSync3 } from "fs";
|
|
10610
|
-
import { homedir as
|
|
10611
|
-
import { join as
|
|
10644
|
+
import { homedir as homedir8 } from "os";
|
|
10645
|
+
import { join as join10 } from "path";
|
|
10612
10646
|
function tailLocal(file, maxBytes = 131072) {
|
|
10613
10647
|
try {
|
|
10614
10648
|
const size = statSync3(file).size;
|
|
@@ -10715,7 +10749,7 @@ var init_watch = __esm({
|
|
|
10715
10749
|
init_cli_utils();
|
|
10716
10750
|
init_args();
|
|
10717
10751
|
init_format();
|
|
10718
|
-
LOCAL_LOG3 =
|
|
10752
|
+
LOCAL_LOG3 = join10(homedir8(), ".solongate", "local-logs", "solongate-audit.jsonl");
|
|
10719
10753
|
trunc = (s, n) => s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
|
|
10720
10754
|
time = (ms) => new Date(ms).toTimeString().slice(0, 8);
|
|
10721
10755
|
}
|
|
@@ -11017,9 +11051,9 @@ __export(global_install_exports, {
|
|
|
11017
11051
|
runGlobalRestore: () => runGlobalRestore,
|
|
11018
11052
|
unlockProtected: () => unlockProtected
|
|
11019
11053
|
});
|
|
11020
|
-
import { readFileSync as readFileSync8, writeFileSync as
|
|
11021
|
-
import { resolve as resolve4, join as
|
|
11022
|
-
import { homedir as
|
|
11054
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync6, mkdirSync as mkdirSync5 } from "fs";
|
|
11055
|
+
import { resolve as resolve4, join as join11, dirname } from "path";
|
|
11056
|
+
import { homedir as homedir9 } from "os";
|
|
11023
11057
|
import { fileURLToPath } from "url";
|
|
11024
11058
|
import { createInterface } from "readline";
|
|
11025
11059
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
@@ -11082,10 +11116,10 @@ function unlockFile(file) {
|
|
|
11082
11116
|
function protectedTargets() {
|
|
11083
11117
|
const p = globalPaths();
|
|
11084
11118
|
return [
|
|
11085
|
-
|
|
11086
|
-
|
|
11087
|
-
|
|
11088
|
-
|
|
11119
|
+
join11(p.hooksDir, "guard.mjs"),
|
|
11120
|
+
join11(p.hooksDir, "audit.mjs"),
|
|
11121
|
+
join11(p.hooksDir, "stop.mjs"),
|
|
11122
|
+
join11(p.hooksDir, "shield.mjs"),
|
|
11089
11123
|
p.configPath,
|
|
11090
11124
|
p.settingsPath
|
|
11091
11125
|
];
|
|
@@ -11097,25 +11131,25 @@ function unlockProtected() {
|
|
|
11097
11131
|
for (const f of protectedTargets()) unlockFile(f);
|
|
11098
11132
|
}
|
|
11099
11133
|
function globalPaths() {
|
|
11100
|
-
const home =
|
|
11101
|
-
const sgDir =
|
|
11102
|
-
const hooksDir =
|
|
11103
|
-
const claudeDir =
|
|
11134
|
+
const home = homedir9();
|
|
11135
|
+
const sgDir = join11(home, ".solongate");
|
|
11136
|
+
const hooksDir = join11(sgDir, "hooks");
|
|
11137
|
+
const claudeDir = join11(home, ".claude");
|
|
11104
11138
|
return {
|
|
11105
11139
|
home,
|
|
11106
11140
|
sgDir,
|
|
11107
11141
|
hooksDir,
|
|
11108
11142
|
claudeDir,
|
|
11109
|
-
settingsPath:
|
|
11110
|
-
backupPath:
|
|
11111
|
-
configPath:
|
|
11143
|
+
settingsPath: join11(claudeDir, "settings.json"),
|
|
11144
|
+
backupPath: join11(claudeDir, "settings.solongate.bak"),
|
|
11145
|
+
configPath: join11(sgDir, "cloud-guard.json")
|
|
11112
11146
|
};
|
|
11113
11147
|
}
|
|
11114
11148
|
function readHook(filename) {
|
|
11115
|
-
return readFileSync8(
|
|
11149
|
+
return readFileSync8(join11(HOOKS_DIR, filename), "utf-8");
|
|
11116
11150
|
}
|
|
11117
11151
|
function readGuard() {
|
|
11118
|
-
const bundled =
|
|
11152
|
+
const bundled = join11(HOOKS_DIR, "guard.bundled.mjs");
|
|
11119
11153
|
return existsSync6(bundled) ? readFileSync8(bundled, "utf-8") : readHook("guard.mjs");
|
|
11120
11154
|
}
|
|
11121
11155
|
function ask(question) {
|
|
@@ -11130,13 +11164,13 @@ function runGlobalRestore() {
|
|
|
11130
11164
|
unlockProtected();
|
|
11131
11165
|
removeClaudeShim();
|
|
11132
11166
|
if (existsSync6(p.backupPath)) {
|
|
11133
|
-
|
|
11167
|
+
writeFileSync6(p.settingsPath, readFileSync8(p.backupPath, "utf-8"));
|
|
11134
11168
|
console.log(` Restored ${p.settingsPath} from backup.`);
|
|
11135
11169
|
} else if (existsSync6(p.settingsPath)) {
|
|
11136
11170
|
try {
|
|
11137
11171
|
const s = JSON.parse(readFileSync8(p.settingsPath, "utf-8"));
|
|
11138
11172
|
delete s.hooks;
|
|
11139
|
-
|
|
11173
|
+
writeFileSync6(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
|
|
11140
11174
|
console.log(` Removed SolonGate hooks from ${p.settingsPath}.`);
|
|
11141
11175
|
} catch {
|
|
11142
11176
|
}
|
|
@@ -11170,7 +11204,7 @@ function shimTargets() {
|
|
|
11170
11204
|
return [];
|
|
11171
11205
|
}
|
|
11172
11206
|
}
|
|
11173
|
-
return [".bashrc", ".zshrc", ".profile"].map((f) =>
|
|
11207
|
+
return [".bashrc", ".zshrc", ".profile"].map((f) => join11(homedir9(), f)).filter((f) => existsSync6(f));
|
|
11174
11208
|
}
|
|
11175
11209
|
function writeShimBlock(file, block2) {
|
|
11176
11210
|
const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
|
|
@@ -11180,8 +11214,8 @@ function writeShimBlock(file, block2) {
|
|
|
11180
11214
|
if (content.length && !content.endsWith("\n")) content += "\n";
|
|
11181
11215
|
content += block2 + "\n";
|
|
11182
11216
|
}
|
|
11183
|
-
|
|
11184
|
-
|
|
11217
|
+
mkdirSync5(dirname(file), { recursive: true });
|
|
11218
|
+
writeFileSync6(file, content);
|
|
11185
11219
|
}
|
|
11186
11220
|
function installClaudeShim(shieldPath) {
|
|
11187
11221
|
const real = resolveRealClaude();
|
|
@@ -11232,22 +11266,22 @@ async function runGlobalInstall(opts = {}) {
|
|
|
11232
11266
|
process.exit(1);
|
|
11233
11267
|
}
|
|
11234
11268
|
const apiUrl = opts.apiUrl || process.env["SOLONGATE_API_URL"] || "https://api.solongate.com";
|
|
11235
|
-
|
|
11236
|
-
|
|
11269
|
+
mkdirSync5(p.hooksDir, { recursive: true });
|
|
11270
|
+
mkdirSync5(p.claudeDir, { recursive: true });
|
|
11237
11271
|
unlockProtected();
|
|
11238
|
-
|
|
11239
|
-
|
|
11240
|
-
|
|
11241
|
-
|
|
11272
|
+
writeFileSync6(join11(p.hooksDir, "guard.mjs"), readGuard());
|
|
11273
|
+
writeFileSync6(join11(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
|
|
11274
|
+
writeFileSync6(join11(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
|
|
11275
|
+
writeFileSync6(join11(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
|
|
11242
11276
|
console.log(` Installed hooks \u2192 ${p.hooksDir}`);
|
|
11243
|
-
installClaudeShim(
|
|
11244
|
-
|
|
11277
|
+
installClaudeShim(join11(p.hooksDir, "shield.mjs"));
|
|
11278
|
+
writeFileSync6(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
|
|
11245
11279
|
console.log(` Wrote ${p.configPath}`);
|
|
11246
11280
|
let existing = {};
|
|
11247
11281
|
if (existsSync6(p.settingsPath)) {
|
|
11248
11282
|
const raw = readFileSync8(p.settingsPath, "utf-8");
|
|
11249
11283
|
if (!existsSync6(p.backupPath)) {
|
|
11250
|
-
|
|
11284
|
+
writeFileSync6(p.backupPath, raw);
|
|
11251
11285
|
console.log(` Backed up existing settings \u2192 ${p.backupPath}`);
|
|
11252
11286
|
}
|
|
11253
11287
|
try {
|
|
@@ -11256,9 +11290,9 @@ async function runGlobalInstall(opts = {}) {
|
|
|
11256
11290
|
existing = {};
|
|
11257
11291
|
}
|
|
11258
11292
|
}
|
|
11259
|
-
const guardAbs =
|
|
11260
|
-
const auditAbs =
|
|
11261
|
-
const stopAbs =
|
|
11293
|
+
const guardAbs = join11(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
|
|
11294
|
+
const auditAbs = join11(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
|
|
11295
|
+
const stopAbs = join11(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
|
|
11262
11296
|
const nodeBin = process.execPath.replace(/\\/g, "/");
|
|
11263
11297
|
const call = process.platform === "win32" ? "& " : "";
|
|
11264
11298
|
const hookCmd = (script) => `${call}"${nodeBin}" "${script}" claude-code "Claude Code"`;
|
|
@@ -11270,7 +11304,7 @@ async function runGlobalInstall(opts = {}) {
|
|
|
11270
11304
|
Stop: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(stopAbs) }] }]
|
|
11271
11305
|
}
|
|
11272
11306
|
};
|
|
11273
|
-
|
|
11307
|
+
writeFileSync6(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
|
|
11274
11308
|
console.log(` Registered global hooks \u2192 ${p.settingsPath}`);
|
|
11275
11309
|
if (process.env["SOLONGATE_OS_LOCK"] === "1") {
|
|
11276
11310
|
lockProtected();
|
|
@@ -11452,9 +11486,9 @@ import { spawn as spawn3 } from "child_process";
|
|
|
11452
11486
|
import { URL as URL2 } from "url";
|
|
11453
11487
|
import { readFileSync as readFileSync9, existsSync as existsSync7, readdirSync, statSync as statSync4 } from "fs";
|
|
11454
11488
|
import { resolve as resolve5 } from "path";
|
|
11455
|
-
import { homedir as
|
|
11489
|
+
import { homedir as homedir10 } from "os";
|
|
11456
11490
|
function findCacheFile() {
|
|
11457
|
-
const dir = resolve5(
|
|
11491
|
+
const dir = resolve5(homedir10(), ".solongate");
|
|
11458
11492
|
const envSel = process.env.SOLONGATE_AGENT_ID;
|
|
11459
11493
|
if (envSel) {
|
|
11460
11494
|
const f = resolve5(dir, ".policy-cache-" + envSel.replace(/[^a-zA-Z0-9_-]/g, "_") + ".json");
|
|
@@ -11769,8 +11803,8 @@ __export(logs_server_exports, {
|
|
|
11769
11803
|
});
|
|
11770
11804
|
import { createServer as createServer2 } from "http";
|
|
11771
11805
|
import { readFileSync as readFileSync10, statSync as statSync5 } from "fs";
|
|
11772
|
-
import { resolve as resolve6, join as
|
|
11773
|
-
import { homedir as
|
|
11806
|
+
import { resolve as resolve6, join as join12, isAbsolute } from "path";
|
|
11807
|
+
import { homedir as homedir11 } from "os";
|
|
11774
11808
|
import { readdirSync as readdirSync2 } from "fs";
|
|
11775
11809
|
function allowedOrigins() {
|
|
11776
11810
|
const base = [
|
|
@@ -11787,15 +11821,15 @@ function resolveLocalLogDir(rawPath) {
|
|
|
11787
11821
|
const dir = String(rawPath || "").trim().replace(/[\\/]+$/, "");
|
|
11788
11822
|
if (!dir) return null;
|
|
11789
11823
|
if (isAbsolute(dir)) return dir;
|
|
11790
|
-
return resolve6(
|
|
11824
|
+
return resolve6(homedir11(), ".solongate", "local-logs");
|
|
11791
11825
|
}
|
|
11792
11826
|
async function findLogDir() {
|
|
11793
|
-
const base = resolve6(
|
|
11827
|
+
const base = resolve6(homedir11(), ".solongate");
|
|
11794
11828
|
try {
|
|
11795
11829
|
const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
|
|
11796
11830
|
for (const f of files) {
|
|
11797
11831
|
try {
|
|
11798
|
-
const c2 = JSON.parse(readFileSync10(
|
|
11832
|
+
const c2 = JSON.parse(readFileSync10(join12(base, f), "utf-8"));
|
|
11799
11833
|
const p = c2?.security?.localLogs?.path;
|
|
11800
11834
|
if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
|
|
11801
11835
|
} catch {
|
|
@@ -11804,7 +11838,7 @@ async function findLogDir() {
|
|
|
11804
11838
|
} catch {
|
|
11805
11839
|
}
|
|
11806
11840
|
try {
|
|
11807
|
-
const cfgRaw = readFileSync10(
|
|
11841
|
+
const cfgRaw = readFileSync10(join12(base, "cloud-guard.json"), "utf-8");
|
|
11808
11842
|
const { apiKey, apiUrl } = JSON.parse(cfgRaw);
|
|
11809
11843
|
if (apiKey) {
|
|
11810
11844
|
const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
|
|
@@ -11833,7 +11867,7 @@ function setCors(req, res) {
|
|
|
11833
11867
|
}
|
|
11834
11868
|
function fileInfo(dir) {
|
|
11835
11869
|
if (!dir) return { file: null, exists: false, size: 0, mtimeMs: 0 };
|
|
11836
|
-
const file =
|
|
11870
|
+
const file = join12(dir, LOG_FILENAME);
|
|
11837
11871
|
try {
|
|
11838
11872
|
const st = statSync5(file);
|
|
11839
11873
|
return { file, exists: true, size: st.size, mtimeMs: st.mtimeMs };
|
|
@@ -11946,7 +11980,7 @@ var init_logs_server = __esm({
|
|
|
11946
11980
|
|
|
11947
11981
|
// src/inject.ts
|
|
11948
11982
|
var inject_exports = {};
|
|
11949
|
-
import { readFileSync as readFileSync11, writeFileSync as
|
|
11983
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, existsSync as existsSync8, copyFileSync } from "fs";
|
|
11950
11984
|
import { resolve as resolve7 } from "path";
|
|
11951
11985
|
import { execSync } from "child_process";
|
|
11952
11986
|
function parseInjectArgs(argv) {
|
|
@@ -12254,7 +12288,7 @@ async function main2() {
|
|
|
12254
12288
|
log3("");
|
|
12255
12289
|
log3(` Backup: ${backupPath}`);
|
|
12256
12290
|
}
|
|
12257
|
-
|
|
12291
|
+
writeFileSync7(entryFile, result.modified);
|
|
12258
12292
|
log3("");
|
|
12259
12293
|
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");
|
|
12260
12294
|
log3(" \u2502 SolonGate SDK injected successfully! \u2502");
|
|
@@ -12283,8 +12317,8 @@ var init_inject = __esm({
|
|
|
12283
12317
|
|
|
12284
12318
|
// src/create.ts
|
|
12285
12319
|
var create_exports = {};
|
|
12286
|
-
import { mkdirSync as
|
|
12287
|
-
import { resolve as resolve8, join as
|
|
12320
|
+
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync8, existsSync as existsSync9 } from "fs";
|
|
12321
|
+
import { resolve as resolve8, join as join13 } from "path";
|
|
12288
12322
|
import { execSync as execSync2 } from "child_process";
|
|
12289
12323
|
function withSpinner(message, fn) {
|
|
12290
12324
|
const frames = ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"];
|
|
@@ -12363,8 +12397,8 @@ EXAMPLES
|
|
|
12363
12397
|
`);
|
|
12364
12398
|
}
|
|
12365
12399
|
function createProject(dir, name, _policy) {
|
|
12366
|
-
|
|
12367
|
-
|
|
12400
|
+
writeFileSync8(
|
|
12401
|
+
join13(dir, "package.json"),
|
|
12368
12402
|
JSON.stringify(
|
|
12369
12403
|
{
|
|
12370
12404
|
name,
|
|
@@ -12393,8 +12427,8 @@ function createProject(dir, name, _policy) {
|
|
|
12393
12427
|
2
|
|
12394
12428
|
) + "\n"
|
|
12395
12429
|
);
|
|
12396
|
-
|
|
12397
|
-
|
|
12430
|
+
writeFileSync8(
|
|
12431
|
+
join13(dir, "tsconfig.json"),
|
|
12398
12432
|
JSON.stringify(
|
|
12399
12433
|
{
|
|
12400
12434
|
compilerOptions: {
|
|
@@ -12414,9 +12448,9 @@ function createProject(dir, name, _policy) {
|
|
|
12414
12448
|
2
|
|
12415
12449
|
) + "\n"
|
|
12416
12450
|
);
|
|
12417
|
-
|
|
12418
|
-
|
|
12419
|
-
|
|
12451
|
+
mkdirSync6(join13(dir, "src"), { recursive: true });
|
|
12452
|
+
writeFileSync8(
|
|
12453
|
+
join13(dir, "src", "index.ts"),
|
|
12420
12454
|
`#!/usr/bin/env node
|
|
12421
12455
|
|
|
12422
12456
|
console.log = (...args: unknown[]) => {
|
|
@@ -12457,8 +12491,8 @@ console.log('');
|
|
|
12457
12491
|
console.log('Press Ctrl+C to stop.');
|
|
12458
12492
|
`
|
|
12459
12493
|
);
|
|
12460
|
-
|
|
12461
|
-
|
|
12494
|
+
writeFileSync8(
|
|
12495
|
+
join13(dir, ".mcp.json"),
|
|
12462
12496
|
JSON.stringify(
|
|
12463
12497
|
{
|
|
12464
12498
|
mcpServers: {
|
|
@@ -12475,13 +12509,13 @@ console.log('Press Ctrl+C to stop.');
|
|
|
12475
12509
|
2
|
|
12476
12510
|
) + "\n"
|
|
12477
12511
|
);
|
|
12478
|
-
|
|
12479
|
-
|
|
12512
|
+
writeFileSync8(
|
|
12513
|
+
join13(dir, ".env"),
|
|
12480
12514
|
`SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
|
|
12481
12515
|
`
|
|
12482
12516
|
);
|
|
12483
|
-
|
|
12484
|
-
|
|
12517
|
+
writeFileSync8(
|
|
12518
|
+
join13(dir, ".gitignore"),
|
|
12485
12519
|
`node_modules/
|
|
12486
12520
|
dist/
|
|
12487
12521
|
*.solongate-backup
|
|
@@ -12500,7 +12534,7 @@ async function main3() {
|
|
|
12500
12534
|
process.exit(1);
|
|
12501
12535
|
}
|
|
12502
12536
|
withSpinner(`Setting up ${opts.name}...`, () => {
|
|
12503
|
-
|
|
12537
|
+
mkdirSync6(dir, { recursive: true });
|
|
12504
12538
|
createProject(dir, opts.name, opts.policy);
|
|
12505
12539
|
});
|
|
12506
12540
|
if (!opts.noInstall) {
|
|
@@ -12574,7 +12608,7 @@ var init_create = __esm({
|
|
|
12574
12608
|
|
|
12575
12609
|
// src/pull-push.ts
|
|
12576
12610
|
var pull_push_exports = {};
|
|
12577
|
-
import { readFileSync as readFileSync12, writeFileSync as
|
|
12611
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync9, existsSync as existsSync10 } from "fs";
|
|
12578
12612
|
import { resolve as resolve9 } from "path";
|
|
12579
12613
|
function loadEnv() {
|
|
12580
12614
|
if (process.env.SOLONGATE_API_KEY) return;
|
|
@@ -12769,7 +12803,7 @@ async function pull(apiKey, file, policyId) {
|
|
|
12769
12803
|
const policy = await fetchCloudPolicy(apiKey, API_URL, policyId);
|
|
12770
12804
|
const { id: _id, ...policyWithoutId } = policy;
|
|
12771
12805
|
const json = JSON.stringify(policyWithoutId, null, 2) + "\n";
|
|
12772
|
-
|
|
12806
|
+
writeFileSync9(file, json, "utf-8");
|
|
12773
12807
|
log5("");
|
|
12774
12808
|
log5(green2(" Saved to: ") + file);
|
|
12775
12809
|
log5(` ${dim2("Name:")} ${policy.name}`);
|
package/dist/tui/index.js
CHANGED
|
@@ -2214,6 +2214,9 @@ function RegexTest({ re }) {
|
|
|
2214
2214
|
// src/tui/panels/Audit.tsx
|
|
2215
2215
|
import { Box as Box6, Text as Text6, useInput as useInput5 } from "ink";
|
|
2216
2216
|
import TextInput4 from "ink-text-input";
|
|
2217
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
2218
|
+
import { homedir as homedir5 } from "os";
|
|
2219
|
+
import { join as join5 } from "path";
|
|
2217
2220
|
import { useState as useState6 } from "react";
|
|
2218
2221
|
import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2219
2222
|
var DECISIONS = [void 0, "DENY", "ALLOW"];
|
|
@@ -2266,8 +2269,10 @@ var AUDIT_HELP = [
|
|
|
2266
2269
|
["g", "signal filter: all \u2192 dlp \u2192 ratelimit"],
|
|
2267
2270
|
["t / n", "tool / agent filter (type, enter done)"],
|
|
2268
2271
|
["/", "free-text search"],
|
|
2269
|
-
["x", "delete the selected entry (press x twice)"],
|
|
2270
|
-
["X", "delete ALL logs of the
|
|
2272
|
+
["x", "delete ONLY the selected entry (press x twice)"],
|
|
2273
|
+
["X", "delete ALL matched logs of the source (press X twice)"],
|
|
2274
|
+
["e", "export this page \u2192 ~/.solongate/audit-export-<src>.jsonl"],
|
|
2275
|
+
["E", "export ALL matched rows (cloud: up to 10k)"],
|
|
2271
2276
|
["c", "clear every filter (incl. session)"]
|
|
2272
2277
|
]
|
|
2273
2278
|
],
|
|
@@ -2286,6 +2291,7 @@ var AUDIT_HELP = [
|
|
|
2286
2291
|
[
|
|
2287
2292
|
["v", "switch logs \u2194 sessions"],
|
|
2288
2293
|
["s", "switch source cloud \u2194 local file"],
|
|
2294
|
+
["space", "copy mode: freeze screen for mouse selection"],
|
|
2289
2295
|
["?", "this help \xB7 any key closes"],
|
|
2290
2296
|
["esc", "back to the menu"]
|
|
2291
2297
|
]
|
|
@@ -2318,13 +2324,14 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2318
2324
|
const [confirm, setConfirm] = useState6(null);
|
|
2319
2325
|
const [msg, setMsg] = useState6(null);
|
|
2320
2326
|
const [showHelp, setShowHelp] = useState6(false);
|
|
2327
|
+
const [frozen, setFrozen] = useState6(false);
|
|
2321
2328
|
const toTop = () => setSel(0);
|
|
2322
2329
|
const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
|
|
2323
2330
|
const tsQ = useLoader(() => source === "cloud" ? api.stats.timeseries({ period: "24h" }) : Promise.resolve(null), [source]);
|
|
2324
2331
|
usePoll(() => {
|
|
2325
2332
|
statsQ.reloadQuiet();
|
|
2326
2333
|
tsQ.reloadQuiet();
|
|
2327
|
-
}, 15e3, active2 && source === "cloud");
|
|
2334
|
+
}, 15e3, active2 && source === "cloud" && !frozen);
|
|
2328
2335
|
const query = {
|
|
2329
2336
|
filter: DECISIONS[di],
|
|
2330
2337
|
signal: SIGNALS[gi],
|
|
@@ -2339,29 +2346,28 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2339
2346
|
() => source === "cloud" ? api.audit.list(query) : Promise.resolve(null),
|
|
2340
2347
|
[source, di, gi, tool, agent, search, sessFilter, page]
|
|
2341
2348
|
);
|
|
2342
|
-
usePoll(cloudQ.reloadQuiet, 6e3, active2 && source === "cloud" && view === "logs" && !editing && page === 0);
|
|
2349
|
+
usePoll(cloudQ.reloadQuiet, 6e3, active2 && source === "cloud" && view === "logs" && !editing && page === 0 && !frozen);
|
|
2343
2350
|
const localQ = useLoader(() => source === "local" ? Promise.resolve(loadLocalRows()) : Promise.resolve(null), [source]);
|
|
2344
|
-
usePoll(localQ.reloadQuiet, 6e3, active2 && source === "local" && view === "logs" && !editing && page === 0);
|
|
2351
|
+
usePoll(localQ.reloadQuiet, 6e3, active2 && source === "local" && view === "logs" && !editing && page === 0 && !frozen);
|
|
2352
|
+
const q = search.trim().toLowerCase();
|
|
2353
|
+
const localFiltered = (localQ.data ?? []).filter((r) => {
|
|
2354
|
+
if (DECISIONS[di] && r.decision !== DECISIONS[di]) return false;
|
|
2355
|
+
if (SIGNALS[gi] === "dlp" && r.dlp.length === 0) return false;
|
|
2356
|
+
if (SIGNALS[gi] === "ratelimit" && !r.burst) return false;
|
|
2357
|
+
if (tool && !r.tool.toLowerCase().includes(tool.toLowerCase())) return false;
|
|
2358
|
+
if (agent && (r.agent ?? "").toLowerCase() !== agent.toLowerCase()) return false;
|
|
2359
|
+
if (sessFilter && r.session !== sessFilter) return false;
|
|
2360
|
+
if (q && !`${r.tool} ${r.agent ?? ""} ${r.reason ?? ""} ${r.args ?? ""}`.toLowerCase().includes(q)) return false;
|
|
2361
|
+
return true;
|
|
2362
|
+
});
|
|
2345
2363
|
let pageRows = [];
|
|
2346
2364
|
let total = 0;
|
|
2347
2365
|
if (source === "cloud") {
|
|
2348
2366
|
pageRows = (cloudQ.data?.entries ?? []).map(cloudRow).sort((a, b) => b.at - a.at);
|
|
2349
2367
|
total = cloudQ.data?.total ?? 0;
|
|
2350
2368
|
} else {
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
const filtered = all.filter((r) => {
|
|
2354
|
-
if (DECISIONS[di] && r.decision !== DECISIONS[di]) return false;
|
|
2355
|
-
if (SIGNALS[gi] === "dlp" && r.dlp.length === 0) return false;
|
|
2356
|
-
if (SIGNALS[gi] === "ratelimit" && !r.burst) return false;
|
|
2357
|
-
if (tool && !r.tool.toLowerCase().includes(tool.toLowerCase())) return false;
|
|
2358
|
-
if (agent && (r.agent ?? "").toLowerCase() !== agent.toLowerCase()) return false;
|
|
2359
|
-
if (sessFilter && r.session !== sessFilter) return false;
|
|
2360
|
-
if (q && !`${r.tool} ${r.agent ?? ""} ${r.reason ?? ""} ${r.args ?? ""}`.toLowerCase().includes(q)) return false;
|
|
2361
|
-
return true;
|
|
2362
|
-
});
|
|
2363
|
-
total = filtered.length;
|
|
2364
|
-
pageRows = filtered.slice(page * PAGE, page * PAGE + PAGE);
|
|
2369
|
+
total = localFiltered.length;
|
|
2370
|
+
pageRows = localFiltered.slice(page * PAGE, page * PAGE + PAGE);
|
|
2365
2371
|
}
|
|
2366
2372
|
const pages = Math.max(1, Math.ceil(total / PAGE));
|
|
2367
2373
|
const current = pageRows[Math.min(sel, Math.max(0, pageRows.length - 1))];
|
|
@@ -2369,7 +2375,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2369
2375
|
() => source === "cloud" ? api.agents.live({ limit: 100, includeDeactivated: true }) : Promise.resolve(null),
|
|
2370
2376
|
[source]
|
|
2371
2377
|
);
|
|
2372
|
-
usePoll(agentsQ.reloadQuiet, 8e3, active2 && source === "cloud" && view === "sessions");
|
|
2378
|
+
usePoll(agentsQ.reloadQuiet, 8e3, active2 && source === "cloud" && view === "sessions" && !frozen);
|
|
2373
2379
|
let sessions = [];
|
|
2374
2380
|
if (source === "cloud") {
|
|
2375
2381
|
sessions = (agentsQ.data?.agents ?? []).map((a) => ({
|
|
@@ -2428,8 +2434,30 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2428
2434
|
toTop();
|
|
2429
2435
|
}).catch((e) => setMsg({ text: "\u2717 " + (e instanceof Error ? e.message : String(e)), level: "bad" }));
|
|
2430
2436
|
};
|
|
2437
|
+
const doExport = (kind) => {
|
|
2438
|
+
setMsg({ text: "exporting\u2026", level: "ok" });
|
|
2439
|
+
const run = async () => {
|
|
2440
|
+
const dir = join5(homedir5(), ".solongate");
|
|
2441
|
+
const file = join5(dir, `audit-export-${source}.jsonl`);
|
|
2442
|
+
let rows2;
|
|
2443
|
+
if (kind === "page") rows2 = pageRows;
|
|
2444
|
+
else if (source === "cloud") {
|
|
2445
|
+
const r = await api.audit.list({ ...query, limit: 1e4, offset: 0 });
|
|
2446
|
+
rows2 = r.entries.map(cloudRow).sort((a, b) => b.at - a.at);
|
|
2447
|
+
} else rows2 = localFiltered;
|
|
2448
|
+
mkdirSync2(dir, { recursive: true });
|
|
2449
|
+
writeFileSync3(file, rows2.map((x) => JSON.stringify(x)).join("\n") + (rows2.length ? "\n" : ""));
|
|
2450
|
+
return { n: rows2.length, file };
|
|
2451
|
+
};
|
|
2452
|
+
run().then(({ n, file }) => setMsg({ text: `\u2713 exported ${n} rows \u2192 ${file}`, level: "ok" })).catch((e) => setMsg({ text: "\u2717 export failed: " + (e instanceof Error ? e.message : String(e)), level: "bad" }));
|
|
2453
|
+
};
|
|
2431
2454
|
useInput5(
|
|
2432
2455
|
(input, key) => {
|
|
2456
|
+
if (input === " ") {
|
|
2457
|
+
setFrozen((f) => !f);
|
|
2458
|
+
return;
|
|
2459
|
+
}
|
|
2460
|
+
if (frozen) return;
|
|
2433
2461
|
if (showHelp) {
|
|
2434
2462
|
setShowHelp(false);
|
|
2435
2463
|
return;
|
|
@@ -2516,7 +2544,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2516
2544
|
if (!current) return;
|
|
2517
2545
|
if (confirm?.kind !== "one" || confirm.key !== current.id) {
|
|
2518
2546
|
setConfirm({ kind: "one", key: current.id });
|
|
2519
|
-
setMsg({ text: `delete ${current.decision} ${current.tool} (${ago(current.at)} ago)
|
|
2547
|
+
setMsg({ text: `x = delete ONLY the selected entry: ${current.decision} ${current.tool} (${ago(current.at)} ago) \u2014 press x again`, level: "bad" });
|
|
2520
2548
|
return;
|
|
2521
2549
|
}
|
|
2522
2550
|
setConfirm(null);
|
|
@@ -2524,12 +2552,14 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2524
2552
|
} else if (input === "X") {
|
|
2525
2553
|
if (confirm?.kind !== "all") {
|
|
2526
2554
|
setConfirm({ kind: "all", key: "all" });
|
|
2527
|
-
setMsg({ text:
|
|
2555
|
+
setMsg({ text: `\u26A0 X = delete ALL ${source} logs \u2014 every one of the ${total} matched entries! press X again`, level: "bad" });
|
|
2528
2556
|
return;
|
|
2529
2557
|
}
|
|
2530
2558
|
setConfirm(null);
|
|
2531
2559
|
doDelete("all");
|
|
2532
|
-
} else if (input === "
|
|
2560
|
+
} else if (input === "e") doExport("page");
|
|
2561
|
+
else if (input === "E") doExport("all");
|
|
2562
|
+
else if (input === "t") setEditing("tool");
|
|
2533
2563
|
else if (input === "n") setEditing("agent");
|
|
2534
2564
|
else if (input === "/") setEditing("search");
|
|
2535
2565
|
else if (input === "c") {
|
|
@@ -2580,6 +2610,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2580
2610
|
LOCAL_LOG
|
|
2581
2611
|
] })
|
|
2582
2612
|
] });
|
|
2613
|
+
const copyBanner = frozen ? /* @__PURE__ */ jsx6(Text6, { backgroundColor: "#123d1f", color: "#7bd88f", bold: true, wrap: "truncate", children: " \u23F5 COPY MODE \u2014 screen frozen, select & copy freely \xB7 space resume " }) : null;
|
|
2583
2614
|
const srcChip = /* @__PURE__ */ jsxs6(Text6, { children: [
|
|
2584
2615
|
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "src:" }),
|
|
2585
2616
|
/* @__PURE__ */ jsx6(Text6, { color: source === "local" ? theme.ok : "#4f6db8", bold: true, children: source }),
|
|
@@ -2605,11 +2636,12 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2605
2636
|
const argLines = current.args ? wrapLines(prettyJson(current.args), bodyW) : ["(no arguments recorded)"];
|
|
2606
2637
|
const sessionRows = current.session ? Math.min(4, sessionCalls.length) + 2 : 1;
|
|
2607
2638
|
const reasonShown = reasonLines.slice(0, 4);
|
|
2608
|
-
const argBudget = Math.max(3, rows - 2 - 5 - reasonShown.length - 1 - sessionRows - 1);
|
|
2639
|
+
const argBudget = Math.max(3, rows - 2 - 5 - reasonShown.length - 1 - sessionRows - 1 - (frozen ? 1 : 0));
|
|
2609
2640
|
const maxScroll = Math.max(0, argLines.length - argBudget);
|
|
2610
2641
|
const off = Math.min(detailScroll, maxScroll);
|
|
2611
2642
|
const argWin = argLines.slice(off, off + argBudget);
|
|
2612
2643
|
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
2644
|
+
copyBanner,
|
|
2613
2645
|
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
2614
2646
|
/* @__PURE__ */ jsx6(Text6, { color: decisionColor(current.decision), bold: true, children: current.decision }),
|
|
2615
2647
|
/* @__PURE__ */ jsx6(Text6, { color: theme.accent, children: " " + current.tool }),
|
|
@@ -2668,8 +2700,9 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2668
2700
|
const selC = Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1));
|
|
2669
2701
|
const start2 = Math.min(Math.max(0, selC - Math.floor((listRows2 - 1) / 2)), Math.max(0, sessionsFiltered.length - listRows2));
|
|
2670
2702
|
const win = sessionsFiltered.slice(start2, start2 + listRows2);
|
|
2671
|
-
return /* @__PURE__ */ jsx6(DataView, { loading: sessLoading, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
2703
|
+
return /* @__PURE__ */ jsx6(DataView, { loading: sessLoading && !frozen, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
2672
2704
|
strip,
|
|
2705
|
+
copyBanner,
|
|
2673
2706
|
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
2674
2707
|
srcChip,
|
|
2675
2708
|
/* @__PURE__ */ jsx6(Text6, { color: theme.accentBright, bold: true, children: "SESSIONS" }),
|
|
@@ -2731,15 +2764,16 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2731
2764
|
] }) : null
|
|
2732
2765
|
] }) });
|
|
2733
2766
|
}
|
|
2734
|
-
const headerRows = 6 + (editing && editing !== "sess-search" ? 1 : 0) + (msg ? 1 : 0);
|
|
2767
|
+
const headerRows = 6 + (editing && editing !== "sess-search" ? 1 : 0) + (msg ? 1 : 0) + (frozen ? 1 : 0);
|
|
2735
2768
|
const listRows = Math.max(4, rows - headerRows);
|
|
2736
2769
|
const selClamped = Math.min(sel, Math.max(0, pageRows.length - 1));
|
|
2737
2770
|
const maxStart = Math.max(0, pageRows.length - listRows);
|
|
2738
2771
|
const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
|
|
2739
2772
|
const windowed = pageRows.slice(start, start + listRows);
|
|
2740
2773
|
const reasonW = Math.min(60, Math.max(12, cols - 67));
|
|
2741
|
-
return /* @__PURE__ */ jsx6(DataView, { loading: logsLoading, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
2774
|
+
return /* @__PURE__ */ jsx6(DataView, { loading: logsLoading && !frozen, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
2742
2775
|
strip,
|
|
2776
|
+
copyBanner,
|
|
2743
2777
|
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
2744
2778
|
srcChip,
|
|
2745
2779
|
/* @__PURE__ */ jsx6(Text6, { color: theme.accentBright, bold: true, children: "LOGS" }),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.81.
|
|
3
|
+
"version": "0.81.29",
|
|
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": {
|