@solongate/proxy 0.81.26 → 0.81.28
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 +171 -89
- package/dist/tui/index.js +101 -19
- 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() {
|
|
@@ -8916,6 +8919,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
8916
8919
|
const [sessSel, setSessSel] = useState6(0);
|
|
8917
8920
|
const [confirm, setConfirm] = useState6(null);
|
|
8918
8921
|
const [msg, setMsg] = useState6(null);
|
|
8922
|
+
const [showHelp, setShowHelp] = useState6(false);
|
|
8919
8923
|
const toTop = () => setSel(0);
|
|
8920
8924
|
const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
|
|
8921
8925
|
const tsQ = useLoader(() => source === "cloud" ? api.stats.timeseries({ period: "24h" }) : Promise.resolve(null), [source]);
|
|
@@ -8940,26 +8944,25 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
8940
8944
|
usePoll(cloudQ.reloadQuiet, 6e3, active2 && source === "cloud" && view === "logs" && !editing && page === 0);
|
|
8941
8945
|
const localQ = useLoader(() => source === "local" ? Promise.resolve(loadLocalRows()) : Promise.resolve(null), [source]);
|
|
8942
8946
|
usePoll(localQ.reloadQuiet, 6e3, active2 && source === "local" && view === "logs" && !editing && page === 0);
|
|
8947
|
+
const q = search.trim().toLowerCase();
|
|
8948
|
+
const localFiltered = (localQ.data ?? []).filter((r) => {
|
|
8949
|
+
if (DECISIONS[di] && r.decision !== DECISIONS[di]) return false;
|
|
8950
|
+
if (SIGNALS[gi] === "dlp" && r.dlp.length === 0) return false;
|
|
8951
|
+
if (SIGNALS[gi] === "ratelimit" && !r.burst) return false;
|
|
8952
|
+
if (tool && !r.tool.toLowerCase().includes(tool.toLowerCase())) return false;
|
|
8953
|
+
if (agent && (r.agent ?? "").toLowerCase() !== agent.toLowerCase()) return false;
|
|
8954
|
+
if (sessFilter && r.session !== sessFilter) return false;
|
|
8955
|
+
if (q && !`${r.tool} ${r.agent ?? ""} ${r.reason ?? ""} ${r.args ?? ""}`.toLowerCase().includes(q)) return false;
|
|
8956
|
+
return true;
|
|
8957
|
+
});
|
|
8943
8958
|
let pageRows = [];
|
|
8944
8959
|
let total = 0;
|
|
8945
8960
|
if (source === "cloud") {
|
|
8946
8961
|
pageRows = (cloudQ.data?.entries ?? []).map(cloudRow).sort((a, b) => b.at - a.at);
|
|
8947
8962
|
total = cloudQ.data?.total ?? 0;
|
|
8948
8963
|
} else {
|
|
8949
|
-
|
|
8950
|
-
|
|
8951
|
-
const filtered = all.filter((r) => {
|
|
8952
|
-
if (DECISIONS[di] && r.decision !== DECISIONS[di]) return false;
|
|
8953
|
-
if (SIGNALS[gi] === "dlp" && r.dlp.length === 0) return false;
|
|
8954
|
-
if (SIGNALS[gi] === "ratelimit" && !r.burst) return false;
|
|
8955
|
-
if (tool && !r.tool.toLowerCase().includes(tool.toLowerCase())) return false;
|
|
8956
|
-
if (agent && (r.agent ?? "").toLowerCase() !== agent.toLowerCase()) return false;
|
|
8957
|
-
if (sessFilter && r.session !== sessFilter) return false;
|
|
8958
|
-
if (q && !`${r.tool} ${r.agent ?? ""} ${r.reason ?? ""} ${r.args ?? ""}`.toLowerCase().includes(q)) return false;
|
|
8959
|
-
return true;
|
|
8960
|
-
});
|
|
8961
|
-
total = filtered.length;
|
|
8962
|
-
pageRows = filtered.slice(page * PAGE, page * PAGE + PAGE);
|
|
8964
|
+
total = localFiltered.length;
|
|
8965
|
+
pageRows = localFiltered.slice(page * PAGE, page * PAGE + PAGE);
|
|
8963
8966
|
}
|
|
8964
8967
|
const pages = Math.max(1, Math.ceil(total / PAGE));
|
|
8965
8968
|
const current = pageRows[Math.min(sel, Math.max(0, pageRows.length - 1))];
|
|
@@ -9026,8 +9029,33 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9026
9029
|
toTop();
|
|
9027
9030
|
}).catch((e) => setMsg({ text: "\u2717 " + (e instanceof Error ? e.message : String(e)), level: "bad" }));
|
|
9028
9031
|
};
|
|
9032
|
+
const doExport = (kind) => {
|
|
9033
|
+
setMsg({ text: "exporting\u2026", level: "ok" });
|
|
9034
|
+
const run12 = async () => {
|
|
9035
|
+
const dir = join8(homedir6(), ".solongate");
|
|
9036
|
+
const file = join8(dir, `audit-export-${source}.jsonl`);
|
|
9037
|
+
let rows2;
|
|
9038
|
+
if (kind === "page") rows2 = pageRows;
|
|
9039
|
+
else if (source === "cloud") {
|
|
9040
|
+
const r = await api.audit.list({ ...query, limit: 1e4, offset: 0 });
|
|
9041
|
+
rows2 = r.entries.map(cloudRow).sort((a, b) => b.at - a.at);
|
|
9042
|
+
} else rows2 = localFiltered;
|
|
9043
|
+
mkdirSync4(dir, { recursive: true });
|
|
9044
|
+
writeFileSync5(file, rows2.map((x) => JSON.stringify(x)).join("\n") + (rows2.length ? "\n" : ""));
|
|
9045
|
+
return { n: rows2.length, file };
|
|
9046
|
+
};
|
|
9047
|
+
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" }));
|
|
9048
|
+
};
|
|
9029
9049
|
useInput5(
|
|
9030
9050
|
(input, key) => {
|
|
9051
|
+
if (showHelp) {
|
|
9052
|
+
setShowHelp(false);
|
|
9053
|
+
return;
|
|
9054
|
+
}
|
|
9055
|
+
if (input === "?") {
|
|
9056
|
+
setShowHelp(true);
|
|
9057
|
+
return;
|
|
9058
|
+
}
|
|
9031
9059
|
if (view === "detail") {
|
|
9032
9060
|
if (key.leftArrow || key.escape) setView("logs");
|
|
9033
9061
|
else if (key.upArrow) setDetailScroll((n) => Math.max(0, n - 1));
|
|
@@ -9106,7 +9134,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9106
9134
|
if (!current) return;
|
|
9107
9135
|
if (confirm?.kind !== "one" || confirm.key !== current.id) {
|
|
9108
9136
|
setConfirm({ kind: "one", key: current.id });
|
|
9109
|
-
setMsg({ text: `delete ${current.decision} ${current.tool} (${ago(current.at)} ago)
|
|
9137
|
+
setMsg({ text: `x = delete ONLY the selected entry: ${current.decision} ${current.tool} (${ago(current.at)} ago) \u2014 press x again`, level: "bad" });
|
|
9110
9138
|
return;
|
|
9111
9139
|
}
|
|
9112
9140
|
setConfirm(null);
|
|
@@ -9114,12 +9142,14 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9114
9142
|
} else if (input === "X") {
|
|
9115
9143
|
if (confirm?.kind !== "all") {
|
|
9116
9144
|
setConfirm({ kind: "all", key: "all" });
|
|
9117
|
-
setMsg({ text:
|
|
9145
|
+
setMsg({ text: `\u26A0 X = delete ALL ${source} logs \u2014 every one of the ${total} matched entries! press X again`, level: "bad" });
|
|
9118
9146
|
return;
|
|
9119
9147
|
}
|
|
9120
9148
|
setConfirm(null);
|
|
9121
9149
|
doDelete("all");
|
|
9122
|
-
} else if (input === "
|
|
9150
|
+
} else if (input === "e") doExport("page");
|
|
9151
|
+
else if (input === "E") doExport("all");
|
|
9152
|
+
else if (input === "t") setEditing("tool");
|
|
9123
9153
|
else if (input === "n") setEditing("agent");
|
|
9124
9154
|
else if (input === "/") setEditing("search");
|
|
9125
9155
|
else if (input === "c") {
|
|
@@ -9175,6 +9205,19 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9175
9205
|
/* @__PURE__ */ jsx6(Text6, { color: source === "local" ? theme.ok : "#4f6db8", bold: true, children: source }),
|
|
9176
9206
|
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " (s) " })
|
|
9177
9207
|
] });
|
|
9208
|
+
if (showHelp) {
|
|
9209
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
9210
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: theme.accentBright, children: "AUDIT \u2014 all keys" }),
|
|
9211
|
+
AUDIT_HELP.map(([group, keys]) => /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginTop: 1, children: [
|
|
9212
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: theme.accent, children: group }),
|
|
9213
|
+
keys.map(([k, desc]) => /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
|
|
9214
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.accentBright, children: (" " + k).padEnd(20) }),
|
|
9215
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: desc })
|
|
9216
|
+
] }, k))
|
|
9217
|
+
] }, group)),
|
|
9218
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "press any key to close" }) })
|
|
9219
|
+
] });
|
|
9220
|
+
}
|
|
9178
9221
|
if (view === "detail" && current) {
|
|
9179
9222
|
const sessionCalls = source === "cloud" ? (sessionQ.data?.entries ?? []).map(cloudRow) : localAll.filter((r) => r.session && r.session === current.session).slice(0, 40);
|
|
9180
9223
|
const bodyW = Math.max(20, cols - 2);
|
|
@@ -9254,7 +9297,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9254
9297
|
chip("status", SESS_STATUS[si] ?? "all", si !== 0),
|
|
9255
9298
|
chip("search", sessSearch || "\xB7", !!sessSearch)
|
|
9256
9299
|
] }),
|
|
9257
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter \u2192 session logs \xB7
|
|
9300
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter \u2192 session logs \xB7 v logs \xB7 ? all keys" : "press \u2192 to browse" }),
|
|
9258
9301
|
editing === "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
9259
9302
|
/* @__PURE__ */ jsx6(Text6, { color: theme.warn, children: "search: " }),
|
|
9260
9303
|
/* @__PURE__ */ jsx6(
|
|
@@ -9328,7 +9371,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9328
9371
|
chip("search", search || "\xB7", !!search),
|
|
9329
9372
|
sessFilter ? chip("sess", sessFilter.slice(0, 8), true) : null
|
|
9330
9373
|
] }),
|
|
9331
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 \u2190\u2192 page \xB7
|
|
9374
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 \u2190\u2192 page \xB7 v sessions \xB7 ? all keys" : "press \u2192 to browse" }),
|
|
9332
9375
|
msg ? /* @__PURE__ */ jsx6(Text6, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : null,
|
|
9333
9376
|
editing && editing !== "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
9334
9377
|
/* @__PURE__ */ jsxs6(Text6, { color: theme.warn, children: [
|
|
@@ -9388,7 +9431,7 @@ function Detail({ label, value, color }) {
|
|
|
9388
9431
|
/* @__PURE__ */ jsx6(Text6, { color, wrap: "truncate", children: value })
|
|
9389
9432
|
] });
|
|
9390
9433
|
}
|
|
9391
|
-
var DECISIONS, SIGNALS, SESS_STATUS, PAGE, LOCAL_MAX_BYTES, cloudRow, sessStatus2, STATUS_DOT;
|
|
9434
|
+
var DECISIONS, SIGNALS, SESS_STATUS, PAGE, LOCAL_MAX_BYTES, cloudRow, AUDIT_HELP, sessStatus2, STATUS_DOT;
|
|
9392
9435
|
var init_Audit = __esm({
|
|
9393
9436
|
"src/tui/panels/Audit.tsx"() {
|
|
9394
9437
|
"use strict";
|
|
@@ -9418,6 +9461,45 @@ var init_Audit = __esm({
|
|
|
9418
9461
|
burst: !!e.rate_limit_burst,
|
|
9419
9462
|
args: e.arguments_summary ? JSON.stringify(e.arguments_summary) : null
|
|
9420
9463
|
});
|
|
9464
|
+
AUDIT_HELP = [
|
|
9465
|
+
[
|
|
9466
|
+
"Logs",
|
|
9467
|
+
[
|
|
9468
|
+
["\u2191\u2193 / PgUp PgDn", "select a row (window follows)"],
|
|
9469
|
+
["enter", "open the FULL entry (reason + arguments)"],
|
|
9470
|
+
["\u2190 \u2192", "previous / next page (500 per page, jumps to top)"],
|
|
9471
|
+
["f", "decision filter: all \u2192 DENY \u2192 ALLOW"],
|
|
9472
|
+
["g", "signal filter: all \u2192 dlp \u2192 ratelimit"],
|
|
9473
|
+
["t / n", "tool / agent filter (type, enter done)"],
|
|
9474
|
+
["/", "free-text search"],
|
|
9475
|
+
["x", "delete ONLY the selected entry (press x twice)"],
|
|
9476
|
+
["X", "delete ALL matched logs of the source (press X twice)"],
|
|
9477
|
+
["e", "export this page \u2192 ~/.solongate/audit-export-<src>.jsonl"],
|
|
9478
|
+
["E", "export ALL matched rows (cloud: up to 10k)"],
|
|
9479
|
+
["c", "clear every filter (incl. session)"]
|
|
9480
|
+
]
|
|
9481
|
+
],
|
|
9482
|
+
[
|
|
9483
|
+
"Sessions",
|
|
9484
|
+
[
|
|
9485
|
+
["\u2191\u2193", "select a session"],
|
|
9486
|
+
["enter", "open that session's logs"],
|
|
9487
|
+
["f", "status filter: all \u2192 active \u2192 idle \u2192 ended"],
|
|
9488
|
+
["/", "search agent / session id"],
|
|
9489
|
+
["c", "clear session filters"]
|
|
9490
|
+
]
|
|
9491
|
+
],
|
|
9492
|
+
[
|
|
9493
|
+
"Anywhere in Audit",
|
|
9494
|
+
[
|
|
9495
|
+
["v", "switch logs \u2194 sessions"],
|
|
9496
|
+
["s", "switch source cloud \u2194 local file"],
|
|
9497
|
+
["?", "this help \xB7 any key closes"],
|
|
9498
|
+
["esc", "back to the menu"]
|
|
9499
|
+
]
|
|
9500
|
+
],
|
|
9501
|
+
["Entry detail", [["\u2191\u2193 / PgUp PgDn", "scroll the arguments"], ["\u2190 / esc", "back to the list"]]]
|
|
9502
|
+
];
|
|
9421
9503
|
sessStatus2 = (lastAt) => Date.now() - lastAt < 6e4 ? "active" : Date.now() - lastAt < 3e5 ? "idle" : "ended";
|
|
9422
9504
|
STATUS_DOT = {
|
|
9423
9505
|
active: { ch: "\u25CF", color: theme.ok },
|
|
@@ -10481,8 +10563,8 @@ var init_agents2 = __esm({
|
|
|
10481
10563
|
|
|
10482
10564
|
// src/commands/doctor.ts
|
|
10483
10565
|
import { existsSync as existsSync4, statSync as statSync2 } from "fs";
|
|
10484
|
-
import { homedir as
|
|
10485
|
-
import { join as
|
|
10566
|
+
import { homedir as homedir7 } from "os";
|
|
10567
|
+
import { join as join9 } from "path";
|
|
10486
10568
|
async function run6(argv) {
|
|
10487
10569
|
const { flags } = parse(argv);
|
|
10488
10570
|
const json = flagBool(flags, "json");
|
|
@@ -10542,14 +10624,14 @@ var init_doctor = __esm({
|
|
|
10542
10624
|
init_api_client();
|
|
10543
10625
|
init_format();
|
|
10544
10626
|
init_args();
|
|
10545
|
-
LOCAL_LOG2 =
|
|
10627
|
+
LOCAL_LOG2 = join9(homedir7(), ".solongate", "local-logs", "solongate-audit.jsonl");
|
|
10546
10628
|
}
|
|
10547
10629
|
});
|
|
10548
10630
|
|
|
10549
10631
|
// src/commands/watch.ts
|
|
10550
10632
|
import { closeSync as closeSync2, existsSync as existsSync5, openSync as openSync2, readSync as readSync2, statSync as statSync3 } from "fs";
|
|
10551
|
-
import { homedir as
|
|
10552
|
-
import { join as
|
|
10633
|
+
import { homedir as homedir8 } from "os";
|
|
10634
|
+
import { join as join10 } from "path";
|
|
10553
10635
|
function tailLocal(file, maxBytes = 131072) {
|
|
10554
10636
|
try {
|
|
10555
10637
|
const size = statSync3(file).size;
|
|
@@ -10656,7 +10738,7 @@ var init_watch = __esm({
|
|
|
10656
10738
|
init_cli_utils();
|
|
10657
10739
|
init_args();
|
|
10658
10740
|
init_format();
|
|
10659
|
-
LOCAL_LOG3 =
|
|
10741
|
+
LOCAL_LOG3 = join10(homedir8(), ".solongate", "local-logs", "solongate-audit.jsonl");
|
|
10660
10742
|
trunc = (s, n) => s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
|
|
10661
10743
|
time = (ms) => new Date(ms).toTimeString().slice(0, 8);
|
|
10662
10744
|
}
|
|
@@ -10958,9 +11040,9 @@ __export(global_install_exports, {
|
|
|
10958
11040
|
runGlobalRestore: () => runGlobalRestore,
|
|
10959
11041
|
unlockProtected: () => unlockProtected
|
|
10960
11042
|
});
|
|
10961
|
-
import { readFileSync as readFileSync8, writeFileSync as
|
|
10962
|
-
import { resolve as resolve4, join as
|
|
10963
|
-
import { homedir as
|
|
11043
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync6, mkdirSync as mkdirSync5 } from "fs";
|
|
11044
|
+
import { resolve as resolve4, join as join11, dirname } from "path";
|
|
11045
|
+
import { homedir as homedir9 } from "os";
|
|
10964
11046
|
import { fileURLToPath } from "url";
|
|
10965
11047
|
import { createInterface } from "readline";
|
|
10966
11048
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
@@ -11023,10 +11105,10 @@ function unlockFile(file) {
|
|
|
11023
11105
|
function protectedTargets() {
|
|
11024
11106
|
const p = globalPaths();
|
|
11025
11107
|
return [
|
|
11026
|
-
|
|
11027
|
-
|
|
11028
|
-
|
|
11029
|
-
|
|
11108
|
+
join11(p.hooksDir, "guard.mjs"),
|
|
11109
|
+
join11(p.hooksDir, "audit.mjs"),
|
|
11110
|
+
join11(p.hooksDir, "stop.mjs"),
|
|
11111
|
+
join11(p.hooksDir, "shield.mjs"),
|
|
11030
11112
|
p.configPath,
|
|
11031
11113
|
p.settingsPath
|
|
11032
11114
|
];
|
|
@@ -11038,25 +11120,25 @@ function unlockProtected() {
|
|
|
11038
11120
|
for (const f of protectedTargets()) unlockFile(f);
|
|
11039
11121
|
}
|
|
11040
11122
|
function globalPaths() {
|
|
11041
|
-
const home =
|
|
11042
|
-
const sgDir =
|
|
11043
|
-
const hooksDir =
|
|
11044
|
-
const claudeDir =
|
|
11123
|
+
const home = homedir9();
|
|
11124
|
+
const sgDir = join11(home, ".solongate");
|
|
11125
|
+
const hooksDir = join11(sgDir, "hooks");
|
|
11126
|
+
const claudeDir = join11(home, ".claude");
|
|
11045
11127
|
return {
|
|
11046
11128
|
home,
|
|
11047
11129
|
sgDir,
|
|
11048
11130
|
hooksDir,
|
|
11049
11131
|
claudeDir,
|
|
11050
|
-
settingsPath:
|
|
11051
|
-
backupPath:
|
|
11052
|
-
configPath:
|
|
11132
|
+
settingsPath: join11(claudeDir, "settings.json"),
|
|
11133
|
+
backupPath: join11(claudeDir, "settings.solongate.bak"),
|
|
11134
|
+
configPath: join11(sgDir, "cloud-guard.json")
|
|
11053
11135
|
};
|
|
11054
11136
|
}
|
|
11055
11137
|
function readHook(filename) {
|
|
11056
|
-
return readFileSync8(
|
|
11138
|
+
return readFileSync8(join11(HOOKS_DIR, filename), "utf-8");
|
|
11057
11139
|
}
|
|
11058
11140
|
function readGuard() {
|
|
11059
|
-
const bundled =
|
|
11141
|
+
const bundled = join11(HOOKS_DIR, "guard.bundled.mjs");
|
|
11060
11142
|
return existsSync6(bundled) ? readFileSync8(bundled, "utf-8") : readHook("guard.mjs");
|
|
11061
11143
|
}
|
|
11062
11144
|
function ask(question) {
|
|
@@ -11071,13 +11153,13 @@ function runGlobalRestore() {
|
|
|
11071
11153
|
unlockProtected();
|
|
11072
11154
|
removeClaudeShim();
|
|
11073
11155
|
if (existsSync6(p.backupPath)) {
|
|
11074
|
-
|
|
11156
|
+
writeFileSync6(p.settingsPath, readFileSync8(p.backupPath, "utf-8"));
|
|
11075
11157
|
console.log(` Restored ${p.settingsPath} from backup.`);
|
|
11076
11158
|
} else if (existsSync6(p.settingsPath)) {
|
|
11077
11159
|
try {
|
|
11078
11160
|
const s = JSON.parse(readFileSync8(p.settingsPath, "utf-8"));
|
|
11079
11161
|
delete s.hooks;
|
|
11080
|
-
|
|
11162
|
+
writeFileSync6(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
|
|
11081
11163
|
console.log(` Removed SolonGate hooks from ${p.settingsPath}.`);
|
|
11082
11164
|
} catch {
|
|
11083
11165
|
}
|
|
@@ -11111,7 +11193,7 @@ function shimTargets() {
|
|
|
11111
11193
|
return [];
|
|
11112
11194
|
}
|
|
11113
11195
|
}
|
|
11114
|
-
return [".bashrc", ".zshrc", ".profile"].map((f) =>
|
|
11196
|
+
return [".bashrc", ".zshrc", ".profile"].map((f) => join11(homedir9(), f)).filter((f) => existsSync6(f));
|
|
11115
11197
|
}
|
|
11116
11198
|
function writeShimBlock(file, block2) {
|
|
11117
11199
|
const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
|
|
@@ -11121,8 +11203,8 @@ function writeShimBlock(file, block2) {
|
|
|
11121
11203
|
if (content.length && !content.endsWith("\n")) content += "\n";
|
|
11122
11204
|
content += block2 + "\n";
|
|
11123
11205
|
}
|
|
11124
|
-
|
|
11125
|
-
|
|
11206
|
+
mkdirSync5(dirname(file), { recursive: true });
|
|
11207
|
+
writeFileSync6(file, content);
|
|
11126
11208
|
}
|
|
11127
11209
|
function installClaudeShim(shieldPath) {
|
|
11128
11210
|
const real = resolveRealClaude();
|
|
@@ -11173,22 +11255,22 @@ async function runGlobalInstall(opts = {}) {
|
|
|
11173
11255
|
process.exit(1);
|
|
11174
11256
|
}
|
|
11175
11257
|
const apiUrl = opts.apiUrl || process.env["SOLONGATE_API_URL"] || "https://api.solongate.com";
|
|
11176
|
-
|
|
11177
|
-
|
|
11258
|
+
mkdirSync5(p.hooksDir, { recursive: true });
|
|
11259
|
+
mkdirSync5(p.claudeDir, { recursive: true });
|
|
11178
11260
|
unlockProtected();
|
|
11179
|
-
|
|
11180
|
-
|
|
11181
|
-
|
|
11182
|
-
|
|
11261
|
+
writeFileSync6(join11(p.hooksDir, "guard.mjs"), readGuard());
|
|
11262
|
+
writeFileSync6(join11(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
|
|
11263
|
+
writeFileSync6(join11(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
|
|
11264
|
+
writeFileSync6(join11(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
|
|
11183
11265
|
console.log(` Installed hooks \u2192 ${p.hooksDir}`);
|
|
11184
|
-
installClaudeShim(
|
|
11185
|
-
|
|
11266
|
+
installClaudeShim(join11(p.hooksDir, "shield.mjs"));
|
|
11267
|
+
writeFileSync6(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
|
|
11186
11268
|
console.log(` Wrote ${p.configPath}`);
|
|
11187
11269
|
let existing = {};
|
|
11188
11270
|
if (existsSync6(p.settingsPath)) {
|
|
11189
11271
|
const raw = readFileSync8(p.settingsPath, "utf-8");
|
|
11190
11272
|
if (!existsSync6(p.backupPath)) {
|
|
11191
|
-
|
|
11273
|
+
writeFileSync6(p.backupPath, raw);
|
|
11192
11274
|
console.log(` Backed up existing settings \u2192 ${p.backupPath}`);
|
|
11193
11275
|
}
|
|
11194
11276
|
try {
|
|
@@ -11197,9 +11279,9 @@ async function runGlobalInstall(opts = {}) {
|
|
|
11197
11279
|
existing = {};
|
|
11198
11280
|
}
|
|
11199
11281
|
}
|
|
11200
|
-
const guardAbs =
|
|
11201
|
-
const auditAbs =
|
|
11202
|
-
const stopAbs =
|
|
11282
|
+
const guardAbs = join11(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
|
|
11283
|
+
const auditAbs = join11(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
|
|
11284
|
+
const stopAbs = join11(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
|
|
11203
11285
|
const nodeBin = process.execPath.replace(/\\/g, "/");
|
|
11204
11286
|
const call = process.platform === "win32" ? "& " : "";
|
|
11205
11287
|
const hookCmd = (script) => `${call}"${nodeBin}" "${script}" claude-code "Claude Code"`;
|
|
@@ -11211,7 +11293,7 @@ async function runGlobalInstall(opts = {}) {
|
|
|
11211
11293
|
Stop: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(stopAbs) }] }]
|
|
11212
11294
|
}
|
|
11213
11295
|
};
|
|
11214
|
-
|
|
11296
|
+
writeFileSync6(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
|
|
11215
11297
|
console.log(` Registered global hooks \u2192 ${p.settingsPath}`);
|
|
11216
11298
|
if (process.env["SOLONGATE_OS_LOCK"] === "1") {
|
|
11217
11299
|
lockProtected();
|
|
@@ -11393,9 +11475,9 @@ import { spawn as spawn3 } from "child_process";
|
|
|
11393
11475
|
import { URL as URL2 } from "url";
|
|
11394
11476
|
import { readFileSync as readFileSync9, existsSync as existsSync7, readdirSync, statSync as statSync4 } from "fs";
|
|
11395
11477
|
import { resolve as resolve5 } from "path";
|
|
11396
|
-
import { homedir as
|
|
11478
|
+
import { homedir as homedir10 } from "os";
|
|
11397
11479
|
function findCacheFile() {
|
|
11398
|
-
const dir = resolve5(
|
|
11480
|
+
const dir = resolve5(homedir10(), ".solongate");
|
|
11399
11481
|
const envSel = process.env.SOLONGATE_AGENT_ID;
|
|
11400
11482
|
if (envSel) {
|
|
11401
11483
|
const f = resolve5(dir, ".policy-cache-" + envSel.replace(/[^a-zA-Z0-9_-]/g, "_") + ".json");
|
|
@@ -11710,8 +11792,8 @@ __export(logs_server_exports, {
|
|
|
11710
11792
|
});
|
|
11711
11793
|
import { createServer as createServer2 } from "http";
|
|
11712
11794
|
import { readFileSync as readFileSync10, statSync as statSync5 } from "fs";
|
|
11713
|
-
import { resolve as resolve6, join as
|
|
11714
|
-
import { homedir as
|
|
11795
|
+
import { resolve as resolve6, join as join12, isAbsolute } from "path";
|
|
11796
|
+
import { homedir as homedir11 } from "os";
|
|
11715
11797
|
import { readdirSync as readdirSync2 } from "fs";
|
|
11716
11798
|
function allowedOrigins() {
|
|
11717
11799
|
const base = [
|
|
@@ -11728,15 +11810,15 @@ function resolveLocalLogDir(rawPath) {
|
|
|
11728
11810
|
const dir = String(rawPath || "").trim().replace(/[\\/]+$/, "");
|
|
11729
11811
|
if (!dir) return null;
|
|
11730
11812
|
if (isAbsolute(dir)) return dir;
|
|
11731
|
-
return resolve6(
|
|
11813
|
+
return resolve6(homedir11(), ".solongate", "local-logs");
|
|
11732
11814
|
}
|
|
11733
11815
|
async function findLogDir() {
|
|
11734
|
-
const base = resolve6(
|
|
11816
|
+
const base = resolve6(homedir11(), ".solongate");
|
|
11735
11817
|
try {
|
|
11736
11818
|
const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
|
|
11737
11819
|
for (const f of files) {
|
|
11738
11820
|
try {
|
|
11739
|
-
const c2 = JSON.parse(readFileSync10(
|
|
11821
|
+
const c2 = JSON.parse(readFileSync10(join12(base, f), "utf-8"));
|
|
11740
11822
|
const p = c2?.security?.localLogs?.path;
|
|
11741
11823
|
if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
|
|
11742
11824
|
} catch {
|
|
@@ -11745,7 +11827,7 @@ async function findLogDir() {
|
|
|
11745
11827
|
} catch {
|
|
11746
11828
|
}
|
|
11747
11829
|
try {
|
|
11748
|
-
const cfgRaw = readFileSync10(
|
|
11830
|
+
const cfgRaw = readFileSync10(join12(base, "cloud-guard.json"), "utf-8");
|
|
11749
11831
|
const { apiKey, apiUrl } = JSON.parse(cfgRaw);
|
|
11750
11832
|
if (apiKey) {
|
|
11751
11833
|
const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
|
|
@@ -11774,7 +11856,7 @@ function setCors(req, res) {
|
|
|
11774
11856
|
}
|
|
11775
11857
|
function fileInfo(dir) {
|
|
11776
11858
|
if (!dir) return { file: null, exists: false, size: 0, mtimeMs: 0 };
|
|
11777
|
-
const file =
|
|
11859
|
+
const file = join12(dir, LOG_FILENAME);
|
|
11778
11860
|
try {
|
|
11779
11861
|
const st = statSync5(file);
|
|
11780
11862
|
return { file, exists: true, size: st.size, mtimeMs: st.mtimeMs };
|
|
@@ -11887,7 +11969,7 @@ var init_logs_server = __esm({
|
|
|
11887
11969
|
|
|
11888
11970
|
// src/inject.ts
|
|
11889
11971
|
var inject_exports = {};
|
|
11890
|
-
import { readFileSync as readFileSync11, writeFileSync as
|
|
11972
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, existsSync as existsSync8, copyFileSync } from "fs";
|
|
11891
11973
|
import { resolve as resolve7 } from "path";
|
|
11892
11974
|
import { execSync } from "child_process";
|
|
11893
11975
|
function parseInjectArgs(argv) {
|
|
@@ -12195,7 +12277,7 @@ async function main2() {
|
|
|
12195
12277
|
log3("");
|
|
12196
12278
|
log3(` Backup: ${backupPath}`);
|
|
12197
12279
|
}
|
|
12198
|
-
|
|
12280
|
+
writeFileSync7(entryFile, result.modified);
|
|
12199
12281
|
log3("");
|
|
12200
12282
|
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");
|
|
12201
12283
|
log3(" \u2502 SolonGate SDK injected successfully! \u2502");
|
|
@@ -12224,8 +12306,8 @@ var init_inject = __esm({
|
|
|
12224
12306
|
|
|
12225
12307
|
// src/create.ts
|
|
12226
12308
|
var create_exports = {};
|
|
12227
|
-
import { mkdirSync as
|
|
12228
|
-
import { resolve as resolve8, join as
|
|
12309
|
+
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync8, existsSync as existsSync9 } from "fs";
|
|
12310
|
+
import { resolve as resolve8, join as join13 } from "path";
|
|
12229
12311
|
import { execSync as execSync2 } from "child_process";
|
|
12230
12312
|
function withSpinner(message, fn) {
|
|
12231
12313
|
const frames = ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"];
|
|
@@ -12304,8 +12386,8 @@ EXAMPLES
|
|
|
12304
12386
|
`);
|
|
12305
12387
|
}
|
|
12306
12388
|
function createProject(dir, name, _policy) {
|
|
12307
|
-
|
|
12308
|
-
|
|
12389
|
+
writeFileSync8(
|
|
12390
|
+
join13(dir, "package.json"),
|
|
12309
12391
|
JSON.stringify(
|
|
12310
12392
|
{
|
|
12311
12393
|
name,
|
|
@@ -12334,8 +12416,8 @@ function createProject(dir, name, _policy) {
|
|
|
12334
12416
|
2
|
|
12335
12417
|
) + "\n"
|
|
12336
12418
|
);
|
|
12337
|
-
|
|
12338
|
-
|
|
12419
|
+
writeFileSync8(
|
|
12420
|
+
join13(dir, "tsconfig.json"),
|
|
12339
12421
|
JSON.stringify(
|
|
12340
12422
|
{
|
|
12341
12423
|
compilerOptions: {
|
|
@@ -12355,9 +12437,9 @@ function createProject(dir, name, _policy) {
|
|
|
12355
12437
|
2
|
|
12356
12438
|
) + "\n"
|
|
12357
12439
|
);
|
|
12358
|
-
|
|
12359
|
-
|
|
12360
|
-
|
|
12440
|
+
mkdirSync6(join13(dir, "src"), { recursive: true });
|
|
12441
|
+
writeFileSync8(
|
|
12442
|
+
join13(dir, "src", "index.ts"),
|
|
12361
12443
|
`#!/usr/bin/env node
|
|
12362
12444
|
|
|
12363
12445
|
console.log = (...args: unknown[]) => {
|
|
@@ -12398,8 +12480,8 @@ console.log('');
|
|
|
12398
12480
|
console.log('Press Ctrl+C to stop.');
|
|
12399
12481
|
`
|
|
12400
12482
|
);
|
|
12401
|
-
|
|
12402
|
-
|
|
12483
|
+
writeFileSync8(
|
|
12484
|
+
join13(dir, ".mcp.json"),
|
|
12403
12485
|
JSON.stringify(
|
|
12404
12486
|
{
|
|
12405
12487
|
mcpServers: {
|
|
@@ -12416,13 +12498,13 @@ console.log('Press Ctrl+C to stop.');
|
|
|
12416
12498
|
2
|
|
12417
12499
|
) + "\n"
|
|
12418
12500
|
);
|
|
12419
|
-
|
|
12420
|
-
|
|
12501
|
+
writeFileSync8(
|
|
12502
|
+
join13(dir, ".env"),
|
|
12421
12503
|
`SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
|
|
12422
12504
|
`
|
|
12423
12505
|
);
|
|
12424
|
-
|
|
12425
|
-
|
|
12506
|
+
writeFileSync8(
|
|
12507
|
+
join13(dir, ".gitignore"),
|
|
12426
12508
|
`node_modules/
|
|
12427
12509
|
dist/
|
|
12428
12510
|
*.solongate-backup
|
|
@@ -12441,7 +12523,7 @@ async function main3() {
|
|
|
12441
12523
|
process.exit(1);
|
|
12442
12524
|
}
|
|
12443
12525
|
withSpinner(`Setting up ${opts.name}...`, () => {
|
|
12444
|
-
|
|
12526
|
+
mkdirSync6(dir, { recursive: true });
|
|
12445
12527
|
createProject(dir, opts.name, opts.policy);
|
|
12446
12528
|
});
|
|
12447
12529
|
if (!opts.noInstall) {
|
|
@@ -12515,7 +12597,7 @@ var init_create = __esm({
|
|
|
12515
12597
|
|
|
12516
12598
|
// src/pull-push.ts
|
|
12517
12599
|
var pull_push_exports = {};
|
|
12518
|
-
import { readFileSync as readFileSync12, writeFileSync as
|
|
12600
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync9, existsSync as existsSync10 } from "fs";
|
|
12519
12601
|
import { resolve as resolve9 } from "path";
|
|
12520
12602
|
function loadEnv() {
|
|
12521
12603
|
if (process.env.SOLONGATE_API_KEY) return;
|
|
@@ -12710,7 +12792,7 @@ async function pull(apiKey, file, policyId) {
|
|
|
12710
12792
|
const policy = await fetchCloudPolicy(apiKey, API_URL, policyId);
|
|
12711
12793
|
const { id: _id, ...policyWithoutId } = policy;
|
|
12712
12794
|
const json = JSON.stringify(policyWithoutId, null, 2) + "\n";
|
|
12713
|
-
|
|
12795
|
+
writeFileSync9(file, json, "utf-8");
|
|
12714
12796
|
log5("");
|
|
12715
12797
|
log5(green2(" Saved to: ") + file);
|
|
12716
12798
|
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"];
|
|
@@ -2255,6 +2258,45 @@ function loadLocalRows() {
|
|
|
2255
2258
|
args: j.arguments ? JSON.stringify(j.arguments) : null
|
|
2256
2259
|
})).sort((a, b) => b.at - a.at);
|
|
2257
2260
|
}
|
|
2261
|
+
var AUDIT_HELP = [
|
|
2262
|
+
[
|
|
2263
|
+
"Logs",
|
|
2264
|
+
[
|
|
2265
|
+
["\u2191\u2193 / PgUp PgDn", "select a row (window follows)"],
|
|
2266
|
+
["enter", "open the FULL entry (reason + arguments)"],
|
|
2267
|
+
["\u2190 \u2192", "previous / next page (500 per page, jumps to top)"],
|
|
2268
|
+
["f", "decision filter: all \u2192 DENY \u2192 ALLOW"],
|
|
2269
|
+
["g", "signal filter: all \u2192 dlp \u2192 ratelimit"],
|
|
2270
|
+
["t / n", "tool / agent filter (type, enter done)"],
|
|
2271
|
+
["/", "free-text search"],
|
|
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)"],
|
|
2276
|
+
["c", "clear every filter (incl. session)"]
|
|
2277
|
+
]
|
|
2278
|
+
],
|
|
2279
|
+
[
|
|
2280
|
+
"Sessions",
|
|
2281
|
+
[
|
|
2282
|
+
["\u2191\u2193", "select a session"],
|
|
2283
|
+
["enter", "open that session's logs"],
|
|
2284
|
+
["f", "status filter: all \u2192 active \u2192 idle \u2192 ended"],
|
|
2285
|
+
["/", "search agent / session id"],
|
|
2286
|
+
["c", "clear session filters"]
|
|
2287
|
+
]
|
|
2288
|
+
],
|
|
2289
|
+
[
|
|
2290
|
+
"Anywhere in Audit",
|
|
2291
|
+
[
|
|
2292
|
+
["v", "switch logs \u2194 sessions"],
|
|
2293
|
+
["s", "switch source cloud \u2194 local file"],
|
|
2294
|
+
["?", "this help \xB7 any key closes"],
|
|
2295
|
+
["esc", "back to the menu"]
|
|
2296
|
+
]
|
|
2297
|
+
],
|
|
2298
|
+
["Entry detail", [["\u2191\u2193 / PgUp PgDn", "scroll the arguments"], ["\u2190 / esc", "back to the list"]]]
|
|
2299
|
+
];
|
|
2258
2300
|
var sessStatus2 = (lastAt) => Date.now() - lastAt < 6e4 ? "active" : Date.now() - lastAt < 3e5 ? "idle" : "ended";
|
|
2259
2301
|
var STATUS_DOT = {
|
|
2260
2302
|
active: { ch: "\u25CF", color: theme.ok },
|
|
@@ -2280,6 +2322,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2280
2322
|
const [sessSel, setSessSel] = useState6(0);
|
|
2281
2323
|
const [confirm, setConfirm] = useState6(null);
|
|
2282
2324
|
const [msg, setMsg] = useState6(null);
|
|
2325
|
+
const [showHelp, setShowHelp] = useState6(false);
|
|
2283
2326
|
const toTop = () => setSel(0);
|
|
2284
2327
|
const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
|
|
2285
2328
|
const tsQ = useLoader(() => source === "cloud" ? api.stats.timeseries({ period: "24h" }) : Promise.resolve(null), [source]);
|
|
@@ -2304,26 +2347,25 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2304
2347
|
usePoll(cloudQ.reloadQuiet, 6e3, active2 && source === "cloud" && view === "logs" && !editing && page === 0);
|
|
2305
2348
|
const localQ = useLoader(() => source === "local" ? Promise.resolve(loadLocalRows()) : Promise.resolve(null), [source]);
|
|
2306
2349
|
usePoll(localQ.reloadQuiet, 6e3, active2 && source === "local" && view === "logs" && !editing && page === 0);
|
|
2350
|
+
const q = search.trim().toLowerCase();
|
|
2351
|
+
const localFiltered = (localQ.data ?? []).filter((r) => {
|
|
2352
|
+
if (DECISIONS[di] && r.decision !== DECISIONS[di]) return false;
|
|
2353
|
+
if (SIGNALS[gi] === "dlp" && r.dlp.length === 0) return false;
|
|
2354
|
+
if (SIGNALS[gi] === "ratelimit" && !r.burst) return false;
|
|
2355
|
+
if (tool && !r.tool.toLowerCase().includes(tool.toLowerCase())) return false;
|
|
2356
|
+
if (agent && (r.agent ?? "").toLowerCase() !== agent.toLowerCase()) return false;
|
|
2357
|
+
if (sessFilter && r.session !== sessFilter) return false;
|
|
2358
|
+
if (q && !`${r.tool} ${r.agent ?? ""} ${r.reason ?? ""} ${r.args ?? ""}`.toLowerCase().includes(q)) return false;
|
|
2359
|
+
return true;
|
|
2360
|
+
});
|
|
2307
2361
|
let pageRows = [];
|
|
2308
2362
|
let total = 0;
|
|
2309
2363
|
if (source === "cloud") {
|
|
2310
2364
|
pageRows = (cloudQ.data?.entries ?? []).map(cloudRow).sort((a, b) => b.at - a.at);
|
|
2311
2365
|
total = cloudQ.data?.total ?? 0;
|
|
2312
2366
|
} else {
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
const filtered = all.filter((r) => {
|
|
2316
|
-
if (DECISIONS[di] && r.decision !== DECISIONS[di]) return false;
|
|
2317
|
-
if (SIGNALS[gi] === "dlp" && r.dlp.length === 0) return false;
|
|
2318
|
-
if (SIGNALS[gi] === "ratelimit" && !r.burst) return false;
|
|
2319
|
-
if (tool && !r.tool.toLowerCase().includes(tool.toLowerCase())) return false;
|
|
2320
|
-
if (agent && (r.agent ?? "").toLowerCase() !== agent.toLowerCase()) return false;
|
|
2321
|
-
if (sessFilter && r.session !== sessFilter) return false;
|
|
2322
|
-
if (q && !`${r.tool} ${r.agent ?? ""} ${r.reason ?? ""} ${r.args ?? ""}`.toLowerCase().includes(q)) return false;
|
|
2323
|
-
return true;
|
|
2324
|
-
});
|
|
2325
|
-
total = filtered.length;
|
|
2326
|
-
pageRows = filtered.slice(page * PAGE, page * PAGE + PAGE);
|
|
2367
|
+
total = localFiltered.length;
|
|
2368
|
+
pageRows = localFiltered.slice(page * PAGE, page * PAGE + PAGE);
|
|
2327
2369
|
}
|
|
2328
2370
|
const pages = Math.max(1, Math.ceil(total / PAGE));
|
|
2329
2371
|
const current = pageRows[Math.min(sel, Math.max(0, pageRows.length - 1))];
|
|
@@ -2390,8 +2432,33 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2390
2432
|
toTop();
|
|
2391
2433
|
}).catch((e) => setMsg({ text: "\u2717 " + (e instanceof Error ? e.message : String(e)), level: "bad" }));
|
|
2392
2434
|
};
|
|
2435
|
+
const doExport = (kind) => {
|
|
2436
|
+
setMsg({ text: "exporting\u2026", level: "ok" });
|
|
2437
|
+
const run = async () => {
|
|
2438
|
+
const dir = join5(homedir5(), ".solongate");
|
|
2439
|
+
const file = join5(dir, `audit-export-${source}.jsonl`);
|
|
2440
|
+
let rows2;
|
|
2441
|
+
if (kind === "page") rows2 = pageRows;
|
|
2442
|
+
else if (source === "cloud") {
|
|
2443
|
+
const r = await api.audit.list({ ...query, limit: 1e4, offset: 0 });
|
|
2444
|
+
rows2 = r.entries.map(cloudRow).sort((a, b) => b.at - a.at);
|
|
2445
|
+
} else rows2 = localFiltered;
|
|
2446
|
+
mkdirSync2(dir, { recursive: true });
|
|
2447
|
+
writeFileSync3(file, rows2.map((x) => JSON.stringify(x)).join("\n") + (rows2.length ? "\n" : ""));
|
|
2448
|
+
return { n: rows2.length, file };
|
|
2449
|
+
};
|
|
2450
|
+
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" }));
|
|
2451
|
+
};
|
|
2393
2452
|
useInput5(
|
|
2394
2453
|
(input, key) => {
|
|
2454
|
+
if (showHelp) {
|
|
2455
|
+
setShowHelp(false);
|
|
2456
|
+
return;
|
|
2457
|
+
}
|
|
2458
|
+
if (input === "?") {
|
|
2459
|
+
setShowHelp(true);
|
|
2460
|
+
return;
|
|
2461
|
+
}
|
|
2395
2462
|
if (view === "detail") {
|
|
2396
2463
|
if (key.leftArrow || key.escape) setView("logs");
|
|
2397
2464
|
else if (key.upArrow) setDetailScroll((n) => Math.max(0, n - 1));
|
|
@@ -2470,7 +2537,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2470
2537
|
if (!current) return;
|
|
2471
2538
|
if (confirm?.kind !== "one" || confirm.key !== current.id) {
|
|
2472
2539
|
setConfirm({ kind: "one", key: current.id });
|
|
2473
|
-
setMsg({ text: `delete ${current.decision} ${current.tool} (${ago(current.at)} ago)
|
|
2540
|
+
setMsg({ text: `x = delete ONLY the selected entry: ${current.decision} ${current.tool} (${ago(current.at)} ago) \u2014 press x again`, level: "bad" });
|
|
2474
2541
|
return;
|
|
2475
2542
|
}
|
|
2476
2543
|
setConfirm(null);
|
|
@@ -2478,12 +2545,14 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2478
2545
|
} else if (input === "X") {
|
|
2479
2546
|
if (confirm?.kind !== "all") {
|
|
2480
2547
|
setConfirm({ kind: "all", key: "all" });
|
|
2481
|
-
setMsg({ text:
|
|
2548
|
+
setMsg({ text: `\u26A0 X = delete ALL ${source} logs \u2014 every one of the ${total} matched entries! press X again`, level: "bad" });
|
|
2482
2549
|
return;
|
|
2483
2550
|
}
|
|
2484
2551
|
setConfirm(null);
|
|
2485
2552
|
doDelete("all");
|
|
2486
|
-
} else if (input === "
|
|
2553
|
+
} else if (input === "e") doExport("page");
|
|
2554
|
+
else if (input === "E") doExport("all");
|
|
2555
|
+
else if (input === "t") setEditing("tool");
|
|
2487
2556
|
else if (input === "n") setEditing("agent");
|
|
2488
2557
|
else if (input === "/") setEditing("search");
|
|
2489
2558
|
else if (input === "c") {
|
|
@@ -2539,6 +2608,19 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2539
2608
|
/* @__PURE__ */ jsx6(Text6, { color: source === "local" ? theme.ok : "#4f6db8", bold: true, children: source }),
|
|
2540
2609
|
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " (s) " })
|
|
2541
2610
|
] });
|
|
2611
|
+
if (showHelp) {
|
|
2612
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
2613
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: theme.accentBright, children: "AUDIT \u2014 all keys" }),
|
|
2614
|
+
AUDIT_HELP.map(([group, keys]) => /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginTop: 1, children: [
|
|
2615
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: theme.accent, children: group }),
|
|
2616
|
+
keys.map(([k, desc]) => /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
|
|
2617
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.accentBright, children: (" " + k).padEnd(20) }),
|
|
2618
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: desc })
|
|
2619
|
+
] }, k))
|
|
2620
|
+
] }, group)),
|
|
2621
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "press any key to close" }) })
|
|
2622
|
+
] });
|
|
2623
|
+
}
|
|
2542
2624
|
if (view === "detail" && current) {
|
|
2543
2625
|
const sessionCalls = source === "cloud" ? (sessionQ.data?.entries ?? []).map(cloudRow) : localAll.filter((r) => r.session && r.session === current.session).slice(0, 40);
|
|
2544
2626
|
const bodyW = Math.max(20, cols - 2);
|
|
@@ -2618,7 +2700,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2618
2700
|
chip("status", SESS_STATUS[si] ?? "all", si !== 0),
|
|
2619
2701
|
chip("search", sessSearch || "\xB7", !!sessSearch)
|
|
2620
2702
|
] }),
|
|
2621
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter \u2192 session logs \xB7
|
|
2703
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter \u2192 session logs \xB7 v logs \xB7 ? all keys" : "press \u2192 to browse" }),
|
|
2622
2704
|
editing === "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
2623
2705
|
/* @__PURE__ */ jsx6(Text6, { color: theme.warn, children: "search: " }),
|
|
2624
2706
|
/* @__PURE__ */ jsx6(
|
|
@@ -2692,7 +2774,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2692
2774
|
chip("search", search || "\xB7", !!search),
|
|
2693
2775
|
sessFilter ? chip("sess", sessFilter.slice(0, 8), true) : null
|
|
2694
2776
|
] }),
|
|
2695
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 \u2190\u2192 page \xB7
|
|
2777
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 \u2190\u2192 page \xB7 v sessions \xB7 ? all keys" : "press \u2192 to browse" }),
|
|
2696
2778
|
msg ? /* @__PURE__ */ jsx6(Text6, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate(msg.text, cols) }) : null,
|
|
2697
2779
|
editing && editing !== "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
2698
2780
|
/* @__PURE__ */ jsxs6(Text6, { color: theme.warn, children: [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.81.
|
|
3
|
+
"version": "0.81.28",
|
|
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": {
|