@solongate/proxy 0.81.25 → 0.81.26
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/audit.d.ts +13 -0
- package/dist/commands/index.js +8 -0
- package/dist/index.js +142 -53
- package/dist/tui/index.js +102 -13
- package/dist/tui/local-log.d.ts +4 -0
- package/package.json +1 -1
|
@@ -12,6 +12,19 @@ export interface AuditQuery {
|
|
|
12
12
|
signal?: 'dlp' | 'ratelimit';
|
|
13
13
|
}
|
|
14
14
|
export declare function list(query?: AuditQuery): Promise<AuditList>;
|
|
15
|
+
export declare function remove(ids: string[]): Promise<{
|
|
16
|
+
deleted: {
|
|
17
|
+
logs: number;
|
|
18
|
+
agents: number;
|
|
19
|
+
};
|
|
20
|
+
}>;
|
|
21
|
+
/** Deletes EVERY audit log of the project (agents/sessions are kept). */
|
|
22
|
+
export declare function removeAll(): Promise<{
|
|
23
|
+
deleted: {
|
|
24
|
+
logs: number;
|
|
25
|
+
agents: number;
|
|
26
|
+
};
|
|
27
|
+
}>;
|
|
15
28
|
export declare function whitelist(id: string, scope?: 'exact' | 'tool'): Promise<{
|
|
16
29
|
ok: true;
|
|
17
30
|
deduped: boolean;
|
package/dist/commands/index.js
CHANGED
|
@@ -291,11 +291,19 @@ var audit_exports = {};
|
|
|
291
291
|
__export(audit_exports, {
|
|
292
292
|
block: () => block,
|
|
293
293
|
list: () => list2,
|
|
294
|
+
remove: () => remove2,
|
|
295
|
+
removeAll: () => removeAll,
|
|
294
296
|
whitelist: () => whitelist
|
|
295
297
|
});
|
|
296
298
|
function list2(query = {}) {
|
|
297
299
|
return request("GET", "/audit-logs", { query });
|
|
298
300
|
}
|
|
301
|
+
function remove2(ids) {
|
|
302
|
+
return request("DELETE", "/audit-logs", { body: { ids } });
|
|
303
|
+
}
|
|
304
|
+
function removeAll() {
|
|
305
|
+
return request("DELETE", "/audit-logs", { body: { scope: "logs" } });
|
|
306
|
+
}
|
|
299
307
|
function whitelist(id, scope = "exact") {
|
|
300
308
|
return request("POST", `/audit-logs/${encodeURIComponent(id)}/whitelist`, { body: { scope } });
|
|
301
309
|
}
|
package/dist/index.js
CHANGED
|
@@ -6845,7 +6845,7 @@ var init_components = __esm({
|
|
|
6845
6845
|
});
|
|
6846
6846
|
|
|
6847
6847
|
// src/tui/local-log.ts
|
|
6848
|
-
import { closeSync, openSync, readSync, statSync } from "fs";
|
|
6848
|
+
import { closeSync, openSync, readFileSync as readFileSync6, readSync, statSync, writeFileSync as writeFileSync3 } from "fs";
|
|
6849
6849
|
import { homedir as homedir4 } from "os";
|
|
6850
6850
|
import { join as join6 } from "path";
|
|
6851
6851
|
function tailLines(file, maxBytes = 131072) {
|
|
@@ -6863,6 +6863,39 @@ function tailLines(file, maxBytes = 131072) {
|
|
|
6863
6863
|
return [];
|
|
6864
6864
|
}
|
|
6865
6865
|
}
|
|
6866
|
+
function deleteLocalEntry(at, tool, session) {
|
|
6867
|
+
try {
|
|
6868
|
+
const lines = readFileSync6(LOCAL_LOG, "utf-8").split("\n");
|
|
6869
|
+
let removed = 0;
|
|
6870
|
+
const kept = lines.filter((line) => {
|
|
6871
|
+
if (!line.trim()) return false;
|
|
6872
|
+
if (removed) return true;
|
|
6873
|
+
try {
|
|
6874
|
+
const j = JSON.parse(line);
|
|
6875
|
+
const hit = Date.parse(j.ts ?? "") === at && (j.tool ?? "?") === tool && (!session || j.session_id === session);
|
|
6876
|
+
if (hit) {
|
|
6877
|
+
removed++;
|
|
6878
|
+
return false;
|
|
6879
|
+
}
|
|
6880
|
+
} catch {
|
|
6881
|
+
}
|
|
6882
|
+
return true;
|
|
6883
|
+
});
|
|
6884
|
+
if (removed) writeFileSync3(LOCAL_LOG, kept.length ? kept.join("\n") + "\n" : "");
|
|
6885
|
+
return removed;
|
|
6886
|
+
} catch {
|
|
6887
|
+
return 0;
|
|
6888
|
+
}
|
|
6889
|
+
}
|
|
6890
|
+
function clearLocalLog() {
|
|
6891
|
+
try {
|
|
6892
|
+
const n = readFileSync6(LOCAL_LOG, "utf-8").split("\n").filter(Boolean).length;
|
|
6893
|
+
writeFileSync3(LOCAL_LOG, "");
|
|
6894
|
+
return n;
|
|
6895
|
+
} catch {
|
|
6896
|
+
return 0;
|
|
6897
|
+
}
|
|
6898
|
+
}
|
|
6866
6899
|
function parseLocalLines(lines) {
|
|
6867
6900
|
const out2 = [];
|
|
6868
6901
|
for (const line of lines) {
|
|
@@ -7062,11 +7095,19 @@ var audit_exports = {};
|
|
|
7062
7095
|
__export(audit_exports, {
|
|
7063
7096
|
block: () => block,
|
|
7064
7097
|
list: () => list2,
|
|
7098
|
+
remove: () => remove2,
|
|
7099
|
+
removeAll: () => removeAll,
|
|
7065
7100
|
whitelist: () => whitelist
|
|
7066
7101
|
});
|
|
7067
7102
|
function list2(query = {}) {
|
|
7068
7103
|
return request("GET", "/audit-logs", { query });
|
|
7069
7104
|
}
|
|
7105
|
+
function remove2(ids) {
|
|
7106
|
+
return request("DELETE", "/audit-logs", { body: { ids } });
|
|
7107
|
+
}
|
|
7108
|
+
function removeAll() {
|
|
7109
|
+
return request("DELETE", "/audit-logs", { body: { scope: "logs" } });
|
|
7110
|
+
}
|
|
7070
7111
|
function whitelist(id, scope = "exact") {
|
|
7071
7112
|
return request("POST", `/audit-logs/${encodeURIComponent(id)}/whitelist`, { body: { scope } });
|
|
7072
7113
|
}
|
|
@@ -7267,7 +7308,7 @@ var init_hooks = __esm({
|
|
|
7267
7308
|
// src/tui/panels/Live.tsx
|
|
7268
7309
|
import { Box as Box2, Text as Text2, useInput } from "ink";
|
|
7269
7310
|
import TextInput from "ink-text-input";
|
|
7270
|
-
import { mkdirSync as mkdirSync3, writeFileSync as
|
|
7311
|
+
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
7271
7312
|
import { homedir as homedir5 } from "os";
|
|
7272
7313
|
import { join as join7 } from "path";
|
|
7273
7314
|
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
@@ -7818,7 +7859,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7818
7859
|
const file = join7(homedir5(), ".solongate", "live-export.jsonl");
|
|
7819
7860
|
try {
|
|
7820
7861
|
mkdirSync3(join7(homedir5(), ".solongate"), { recursive: true });
|
|
7821
|
-
|
|
7862
|
+
writeFileSync4(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
|
|
7822
7863
|
setActionMsg({ text: `\u2713 exported ${visibleDesc.length} lines \u2192 ${file}`, level: "ok", until: Date.now() + 6e3 });
|
|
7823
7864
|
} catch (err2) {
|
|
7824
7865
|
setActionMsg({ text: "\u2717 export failed: " + (err2 instanceof Error ? err2.message : String(err2)), level: "bad", until: Date.now() + 6e3 });
|
|
@@ -8873,6 +8914,8 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
8873
8914
|
const [si, setSi] = useState6(0);
|
|
8874
8915
|
const [sessSearch, setSessSearch] = useState6("");
|
|
8875
8916
|
const [sessSel, setSessSel] = useState6(0);
|
|
8917
|
+
const [confirm, setConfirm] = useState6(null);
|
|
8918
|
+
const [msg, setMsg] = useState6(null);
|
|
8876
8919
|
const toTop = () => setSel(0);
|
|
8877
8920
|
const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
|
|
8878
8921
|
const tsQ = useLoader(() => source === "cloud" ? api.stats.timeseries({ period: "24h" }) : Promise.resolve(null), [source]);
|
|
@@ -8958,6 +9001,31 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
8958
9001
|
() => view === "detail" && current?.session && source === "cloud" ? api.audit.list({ session_id: current.session, limit: 40 }) : Promise.resolve(null),
|
|
8959
9002
|
[view, current?.session, source]
|
|
8960
9003
|
);
|
|
9004
|
+
const logsLoading = (source === "cloud" ? cloudQ.loading : localQ.loading) && !editing;
|
|
9005
|
+
const sessLoading = source === "cloud" ? agentsQ.loading : localQ.loading;
|
|
9006
|
+
const doDelete = (kind) => {
|
|
9007
|
+
setMsg({ text: "deleting\u2026", level: "ok" });
|
|
9008
|
+
const run12 = async () => {
|
|
9009
|
+
if (source === "cloud") {
|
|
9010
|
+
if (kind === "one") {
|
|
9011
|
+
if (!current) throw new Error("nothing selected");
|
|
9012
|
+
await api.audit.remove([current.id]);
|
|
9013
|
+
} else {
|
|
9014
|
+
await api.audit.removeAll();
|
|
9015
|
+
}
|
|
9016
|
+
cloudQ.reload();
|
|
9017
|
+
statsQ.reloadQuiet();
|
|
9018
|
+
} else {
|
|
9019
|
+
const n = kind === "one" && current ? deleteLocalEntry(current.at, current.tool, current.session) : kind === "all" ? clearLocalLog() : 0;
|
|
9020
|
+
if (!n) throw new Error("entry not found in the local file");
|
|
9021
|
+
localQ.reload();
|
|
9022
|
+
}
|
|
9023
|
+
};
|
|
9024
|
+
run12().then(() => {
|
|
9025
|
+
setMsg({ text: kind === "one" ? "\u2713 entry deleted" : `\u2713 ALL ${source} logs deleted`, level: "ok" });
|
|
9026
|
+
toTop();
|
|
9027
|
+
}).catch((e) => setMsg({ text: "\u2717 " + (e instanceof Error ? e.message : String(e)), level: "bad" }));
|
|
9028
|
+
};
|
|
8961
9029
|
useInput5(
|
|
8962
9030
|
(input, key) => {
|
|
8963
9031
|
if (view === "detail") {
|
|
@@ -8968,6 +9036,11 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
8968
9036
|
else if (key.pageDown) setDetailScroll((n) => n + 10);
|
|
8969
9037
|
return;
|
|
8970
9038
|
}
|
|
9039
|
+
if (view === "logs" ? logsLoading : sessLoading) return;
|
|
9040
|
+
if (confirm && input !== "x" && input !== "X") {
|
|
9041
|
+
setConfirm(null);
|
|
9042
|
+
setMsg(null);
|
|
9043
|
+
}
|
|
8971
9044
|
if (input === "s") {
|
|
8972
9045
|
setSource((s) => s === "cloud" ? "local" : "cloud");
|
|
8973
9046
|
setPage(0);
|
|
@@ -9029,6 +9102,23 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9029
9102
|
setGi((n) => (n + 1) % SIGNALS.length);
|
|
9030
9103
|
setPage(0);
|
|
9031
9104
|
toTop();
|
|
9105
|
+
} else if (input === "x") {
|
|
9106
|
+
if (!current) return;
|
|
9107
|
+
if (confirm?.kind !== "one" || confirm.key !== current.id) {
|
|
9108
|
+
setConfirm({ kind: "one", key: current.id });
|
|
9109
|
+
setMsg({ text: `delete ${current.decision} ${current.tool} (${ago(current.at)} ago)? press x again`, level: "bad" });
|
|
9110
|
+
return;
|
|
9111
|
+
}
|
|
9112
|
+
setConfirm(null);
|
|
9113
|
+
doDelete("one");
|
|
9114
|
+
} else if (input === "X") {
|
|
9115
|
+
if (confirm?.kind !== "all") {
|
|
9116
|
+
setConfirm({ kind: "all", key: "all" });
|
|
9117
|
+
setMsg({ text: `DELETE ALL ${source} logs (${total} entries)? press X again`, level: "bad" });
|
|
9118
|
+
return;
|
|
9119
|
+
}
|
|
9120
|
+
setConfirm(null);
|
|
9121
|
+
doDelete("all");
|
|
9032
9122
|
} else if (input === "t") setEditing("tool");
|
|
9033
9123
|
else if (input === "n") setEditing("agent");
|
|
9034
9124
|
else if (input === "/") setEditing("search");
|
|
@@ -9155,8 +9245,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9155
9245
|
const selC = Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1));
|
|
9156
9246
|
const start2 = Math.min(Math.max(0, selC - Math.floor((listRows2 - 1) / 2)), Math.max(0, sessionsFiltered.length - listRows2));
|
|
9157
9247
|
const win = sessionsFiltered.slice(start2, start2 + listRows2);
|
|
9158
|
-
|
|
9159
|
-
return /* @__PURE__ */ jsx6(DataView, { loading: loading2, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
9248
|
+
return /* @__PURE__ */ jsx6(DataView, { loading: sessLoading, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
9160
9249
|
strip,
|
|
9161
9250
|
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
9162
9251
|
srcChip,
|
|
@@ -9219,15 +9308,14 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9219
9308
|
] }) : null
|
|
9220
9309
|
] }) });
|
|
9221
9310
|
}
|
|
9222
|
-
const headerRows = 6 + (editing && editing !== "sess-search" ? 1 : 0);
|
|
9311
|
+
const headerRows = 6 + (editing && editing !== "sess-search" ? 1 : 0) + (msg ? 1 : 0);
|
|
9223
9312
|
const listRows = Math.max(4, rows - headerRows);
|
|
9224
9313
|
const selClamped = Math.min(sel, Math.max(0, pageRows.length - 1));
|
|
9225
9314
|
const maxStart = Math.max(0, pageRows.length - listRows);
|
|
9226
9315
|
const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
|
|
9227
9316
|
const windowed = pageRows.slice(start, start + listRows);
|
|
9228
9317
|
const reasonW = Math.min(60, Math.max(12, cols - 67));
|
|
9229
|
-
|
|
9230
|
-
return /* @__PURE__ */ jsx6(DataView, { loading, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
9318
|
+
return /* @__PURE__ */ jsx6(DataView, { loading: logsLoading, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
9231
9319
|
strip,
|
|
9232
9320
|
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
9233
9321
|
srcChip,
|
|
@@ -9240,7 +9328,8 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9240
9328
|
chip("search", search || "\xB7", !!search),
|
|
9241
9329
|
sessFilter ? chip("sess", sessFilter.slice(0, 8), true) : null
|
|
9242
9330
|
] }),
|
|
9243
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 \u2190\u2192 page \xB7 f dec \xB7 g sig \xB7 t tool \xB7 n agent \xB7 / search \xB7 s source \xB7 v sessions \xB7 c clear" : "press \u2192 to browse" }),
|
|
9331
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 \u2190\u2192 page \xB7 f dec \xB7 g sig \xB7 t tool \xB7 n agent \xB7 / search \xB7 x del \xB7 X del ALL \xB7 s source \xB7 v sessions \xB7 c clear" : "press \u2192 to browse" }),
|
|
9332
|
+
msg ? /* @__PURE__ */ jsx6(Text6, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : null,
|
|
9244
9333
|
editing && editing !== "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
9245
9334
|
/* @__PURE__ */ jsxs6(Text6, { color: theme.warn, children: [
|
|
9246
9335
|
editing,
|
|
@@ -9663,7 +9752,7 @@ var init_App = __esm({
|
|
|
9663
9752
|
["Live", [["\u2191\u2193", "select a stream row"], ["enter", "full entry content"], ["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)"]]],
|
|
9664
9753
|
["Policies", [["\u2191\u2193", "browse / select"], ["a", "activate (pin) selected policy"], ["x", "deactivate \u2014 no active policy"], ["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"], ["s", "save"], ["x", "discard"]]],
|
|
9665
9754
|
["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"]]],
|
|
9666
|
-
["Audit", [["v", "logs \u2194 sessions"], ["s", "source: cloud \u2194 local"], ["\u2190 \u2192", "prev / next page (500 each)"], ["\u2191\u2193", "select (list scrolls)"], ["enter", "full entry / session logs"], ["f / g", "decision / signal filter"], ["t / n / /", "tool / agent / search"], ["c", "clear filters"]]],
|
|
9755
|
+
["Audit", [["v", "logs \u2194 sessions"], ["s", "source: cloud \u2194 local"], ["\u2190 \u2192", "prev / next page (500 each)"], ["\u2191\u2193", "select (list scrolls)"], ["enter", "full entry / session logs"], ["f / g", "decision / signal filter"], ["t / n / /", "tool / agent / search"], ["x / X", "delete entry / ALL (press twice)"], ["c", "clear filters"]]],
|
|
9667
9756
|
["Settings", [["\u2191\u2193", "move"], ["enter / space", "toggle \xB7 edit \xB7 add"], ["e", "webhook events"], ["d d", "delete"], ["r", "refresh"]]]
|
|
9668
9757
|
];
|
|
9669
9758
|
}
|
|
@@ -9801,7 +9890,7 @@ var init_args = __esm({
|
|
|
9801
9890
|
});
|
|
9802
9891
|
|
|
9803
9892
|
// src/commands/policy.ts
|
|
9804
|
-
import { readFileSync as
|
|
9893
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
9805
9894
|
async function run(argv) {
|
|
9806
9895
|
const { positionals, flags } = parse(argv);
|
|
9807
9896
|
const sub = positionals[0];
|
|
@@ -9976,7 +10065,7 @@ function printRules(rules) {
|
|
|
9976
10065
|
}
|
|
9977
10066
|
async function resolveRules(target) {
|
|
9978
10067
|
if (target.endsWith(".json")) {
|
|
9979
|
-
const parsed = JSON.parse(
|
|
10068
|
+
const parsed = JSON.parse(readFileSync7(target, "utf-8"));
|
|
9980
10069
|
return parsed.rules ?? [];
|
|
9981
10070
|
}
|
|
9982
10071
|
const p = await api.policies.get(target);
|
|
@@ -10869,7 +10958,7 @@ __export(global_install_exports, {
|
|
|
10869
10958
|
runGlobalRestore: () => runGlobalRestore,
|
|
10870
10959
|
unlockProtected: () => unlockProtected
|
|
10871
10960
|
});
|
|
10872
|
-
import { readFileSync as
|
|
10961
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
|
|
10873
10962
|
import { resolve as resolve4, join as join10, dirname } from "path";
|
|
10874
10963
|
import { homedir as homedir8 } from "os";
|
|
10875
10964
|
import { fileURLToPath } from "url";
|
|
@@ -10964,11 +11053,11 @@ function globalPaths() {
|
|
|
10964
11053
|
};
|
|
10965
11054
|
}
|
|
10966
11055
|
function readHook(filename) {
|
|
10967
|
-
return
|
|
11056
|
+
return readFileSync8(join10(HOOKS_DIR, filename), "utf-8");
|
|
10968
11057
|
}
|
|
10969
11058
|
function readGuard() {
|
|
10970
11059
|
const bundled = join10(HOOKS_DIR, "guard.bundled.mjs");
|
|
10971
|
-
return existsSync6(bundled) ?
|
|
11060
|
+
return existsSync6(bundled) ? readFileSync8(bundled, "utf-8") : readHook("guard.mjs");
|
|
10972
11061
|
}
|
|
10973
11062
|
function ask(question) {
|
|
10974
11063
|
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
@@ -10982,13 +11071,13 @@ function runGlobalRestore() {
|
|
|
10982
11071
|
unlockProtected();
|
|
10983
11072
|
removeClaudeShim();
|
|
10984
11073
|
if (existsSync6(p.backupPath)) {
|
|
10985
|
-
|
|
11074
|
+
writeFileSync5(p.settingsPath, readFileSync8(p.backupPath, "utf-8"));
|
|
10986
11075
|
console.log(` Restored ${p.settingsPath} from backup.`);
|
|
10987
11076
|
} else if (existsSync6(p.settingsPath)) {
|
|
10988
11077
|
try {
|
|
10989
|
-
const s = JSON.parse(
|
|
11078
|
+
const s = JSON.parse(readFileSync8(p.settingsPath, "utf-8"));
|
|
10990
11079
|
delete s.hooks;
|
|
10991
|
-
|
|
11080
|
+
writeFileSync5(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
|
|
10992
11081
|
console.log(` Removed SolonGate hooks from ${p.settingsPath}.`);
|
|
10993
11082
|
} catch {
|
|
10994
11083
|
}
|
|
@@ -11026,14 +11115,14 @@ function shimTargets() {
|
|
|
11026
11115
|
}
|
|
11027
11116
|
function writeShimBlock(file, block2) {
|
|
11028
11117
|
const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
|
|
11029
|
-
let content = existsSync6(file) ?
|
|
11118
|
+
let content = existsSync6(file) ? readFileSync8(file, "utf-8") : "";
|
|
11030
11119
|
content = content.replace(re, "");
|
|
11031
11120
|
if (block2) {
|
|
11032
11121
|
if (content.length && !content.endsWith("\n")) content += "\n";
|
|
11033
11122
|
content += block2 + "\n";
|
|
11034
11123
|
}
|
|
11035
11124
|
mkdirSync4(dirname(file), { recursive: true });
|
|
11036
|
-
|
|
11125
|
+
writeFileSync5(file, content);
|
|
11037
11126
|
}
|
|
11038
11127
|
function installClaudeShim(shieldPath) {
|
|
11039
11128
|
const real = resolveRealClaude();
|
|
@@ -11071,7 +11160,7 @@ async function runGlobalInstall(opts = {}) {
|
|
|
11071
11160
|
let apiKey = opts.apiKey || process.env["SOLONGATE_API_KEY"] || "";
|
|
11072
11161
|
if (!apiKey || apiKey === "sg_live_your_key_here") {
|
|
11073
11162
|
try {
|
|
11074
|
-
const cfg = JSON.parse(
|
|
11163
|
+
const cfg = JSON.parse(readFileSync8(p.configPath, "utf-8"));
|
|
11075
11164
|
if (cfg && typeof cfg.apiKey === "string") apiKey = cfg.apiKey;
|
|
11076
11165
|
} catch {
|
|
11077
11166
|
}
|
|
@@ -11087,19 +11176,19 @@ async function runGlobalInstall(opts = {}) {
|
|
|
11087
11176
|
mkdirSync4(p.hooksDir, { recursive: true });
|
|
11088
11177
|
mkdirSync4(p.claudeDir, { recursive: true });
|
|
11089
11178
|
unlockProtected();
|
|
11090
|
-
|
|
11091
|
-
|
|
11092
|
-
|
|
11093
|
-
|
|
11179
|
+
writeFileSync5(join10(p.hooksDir, "guard.mjs"), readGuard());
|
|
11180
|
+
writeFileSync5(join10(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
|
|
11181
|
+
writeFileSync5(join10(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
|
|
11182
|
+
writeFileSync5(join10(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
|
|
11094
11183
|
console.log(` Installed hooks \u2192 ${p.hooksDir}`);
|
|
11095
11184
|
installClaudeShim(join10(p.hooksDir, "shield.mjs"));
|
|
11096
|
-
|
|
11185
|
+
writeFileSync5(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
|
|
11097
11186
|
console.log(` Wrote ${p.configPath}`);
|
|
11098
11187
|
let existing = {};
|
|
11099
11188
|
if (existsSync6(p.settingsPath)) {
|
|
11100
|
-
const raw =
|
|
11189
|
+
const raw = readFileSync8(p.settingsPath, "utf-8");
|
|
11101
11190
|
if (!existsSync6(p.backupPath)) {
|
|
11102
|
-
|
|
11191
|
+
writeFileSync5(p.backupPath, raw);
|
|
11103
11192
|
console.log(` Backed up existing settings \u2192 ${p.backupPath}`);
|
|
11104
11193
|
}
|
|
11105
11194
|
try {
|
|
@@ -11122,7 +11211,7 @@ async function runGlobalInstall(opts = {}) {
|
|
|
11122
11211
|
Stop: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(stopAbs) }] }]
|
|
11123
11212
|
}
|
|
11124
11213
|
};
|
|
11125
|
-
|
|
11214
|
+
writeFileSync5(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
|
|
11126
11215
|
console.log(` Registered global hooks \u2192 ${p.settingsPath}`);
|
|
11127
11216
|
if (process.env["SOLONGATE_OS_LOCK"] === "1") {
|
|
11128
11217
|
lockProtected();
|
|
@@ -11302,7 +11391,7 @@ import { createServer, request as httpRequest } from "http";
|
|
|
11302
11391
|
import { request as httpsRequest } from "https";
|
|
11303
11392
|
import { spawn as spawn3 } from "child_process";
|
|
11304
11393
|
import { URL as URL2 } from "url";
|
|
11305
|
-
import { readFileSync as
|
|
11394
|
+
import { readFileSync as readFileSync9, existsSync as existsSync7, readdirSync, statSync as statSync4 } from "fs";
|
|
11306
11395
|
import { resolve as resolve5 } from "path";
|
|
11307
11396
|
import { homedir as homedir9 } from "os";
|
|
11308
11397
|
function findCacheFile() {
|
|
@@ -11332,7 +11421,7 @@ function loadCfg() {
|
|
|
11332
11421
|
try {
|
|
11333
11422
|
const f = findCacheFile();
|
|
11334
11423
|
if (f && existsSync7(f)) {
|
|
11335
|
-
const c2 = JSON.parse(
|
|
11424
|
+
const c2 = JSON.parse(readFileSync9(f, "utf-8"));
|
|
11336
11425
|
const d = c2?.security?.dlpRedact;
|
|
11337
11426
|
const g = c2?.security?.ghost;
|
|
11338
11427
|
const ghost = g && Array.isArray(g.patterns) ? g.patterns : [];
|
|
@@ -11620,7 +11709,7 @@ __export(logs_server_exports, {
|
|
|
11620
11709
|
runLogsServer: () => runLogsServer
|
|
11621
11710
|
});
|
|
11622
11711
|
import { createServer as createServer2 } from "http";
|
|
11623
|
-
import { readFileSync as
|
|
11712
|
+
import { readFileSync as readFileSync10, statSync as statSync5 } from "fs";
|
|
11624
11713
|
import { resolve as resolve6, join as join11, isAbsolute } from "path";
|
|
11625
11714
|
import { homedir as homedir10 } from "os";
|
|
11626
11715
|
import { readdirSync as readdirSync2 } from "fs";
|
|
@@ -11647,7 +11736,7 @@ async function findLogDir() {
|
|
|
11647
11736
|
const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
|
|
11648
11737
|
for (const f of files) {
|
|
11649
11738
|
try {
|
|
11650
|
-
const c2 = JSON.parse(
|
|
11739
|
+
const c2 = JSON.parse(readFileSync10(join11(base, f), "utf-8"));
|
|
11651
11740
|
const p = c2?.security?.localLogs?.path;
|
|
11652
11741
|
if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
|
|
11653
11742
|
} catch {
|
|
@@ -11656,7 +11745,7 @@ async function findLogDir() {
|
|
|
11656
11745
|
} catch {
|
|
11657
11746
|
}
|
|
11658
11747
|
try {
|
|
11659
|
-
const cfgRaw =
|
|
11748
|
+
const cfgRaw = readFileSync10(join11(base, "cloud-guard.json"), "utf-8");
|
|
11660
11749
|
const { apiKey, apiUrl } = JSON.parse(cfgRaw);
|
|
11661
11750
|
if (apiKey) {
|
|
11662
11751
|
const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
|
|
@@ -11746,7 +11835,7 @@ async function runLogsServer() {
|
|
|
11746
11835
|
return;
|
|
11747
11836
|
}
|
|
11748
11837
|
try {
|
|
11749
|
-
const text =
|
|
11838
|
+
const text = readFileSync10(info.file, "utf-8");
|
|
11750
11839
|
res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8", "Last-Modified": lastMod, "X-Solongate-Exists": "1" });
|
|
11751
11840
|
res.end(text);
|
|
11752
11841
|
} catch {
|
|
@@ -11798,7 +11887,7 @@ var init_logs_server = __esm({
|
|
|
11798
11887
|
|
|
11799
11888
|
// src/inject.ts
|
|
11800
11889
|
var inject_exports = {};
|
|
11801
|
-
import { readFileSync as
|
|
11890
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, existsSync as existsSync8, copyFileSync } from "fs";
|
|
11802
11891
|
import { resolve as resolve7 } from "path";
|
|
11803
11892
|
import { execSync } from "child_process";
|
|
11804
11893
|
function parseInjectArgs(argv) {
|
|
@@ -11858,7 +11947,7 @@ WHAT IT DOES
|
|
|
11858
11947
|
function detectProject() {
|
|
11859
11948
|
if (!existsSync8(resolve7("package.json"))) return false;
|
|
11860
11949
|
try {
|
|
11861
|
-
const pkg = JSON.parse(
|
|
11950
|
+
const pkg = JSON.parse(readFileSync11(resolve7("package.json"), "utf-8"));
|
|
11862
11951
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
11863
11952
|
return !!(allDeps["@modelcontextprotocol/sdk"] || allDeps["@modelcontextprotocol/server"]);
|
|
11864
11953
|
} catch {
|
|
@@ -11867,7 +11956,7 @@ function detectProject() {
|
|
|
11867
11956
|
}
|
|
11868
11957
|
function findTsEntryFile() {
|
|
11869
11958
|
try {
|
|
11870
|
-
const pkg = JSON.parse(
|
|
11959
|
+
const pkg = JSON.parse(readFileSync11(resolve7("package.json"), "utf-8"));
|
|
11871
11960
|
if (pkg.bin) {
|
|
11872
11961
|
const binPath = typeof pkg.bin === "string" ? pkg.bin : Object.values(pkg.bin)[0];
|
|
11873
11962
|
if (typeof binPath === "string") {
|
|
@@ -11894,7 +11983,7 @@ function findTsEntryFile() {
|
|
|
11894
11983
|
const full = resolve7(c2);
|
|
11895
11984
|
if (existsSync8(full)) {
|
|
11896
11985
|
try {
|
|
11897
|
-
const content =
|
|
11986
|
+
const content = readFileSync11(full, "utf-8");
|
|
11898
11987
|
if (content.includes("McpServer") || content.includes("McpServer")) {
|
|
11899
11988
|
return full;
|
|
11900
11989
|
}
|
|
@@ -11914,7 +12003,7 @@ function detectPackageManager() {
|
|
|
11914
12003
|
}
|
|
11915
12004
|
function installSdk() {
|
|
11916
12005
|
try {
|
|
11917
|
-
const pkg = JSON.parse(
|
|
12006
|
+
const pkg = JSON.parse(readFileSync11(resolve7("package.json"), "utf-8"));
|
|
11918
12007
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
11919
12008
|
if (allDeps["@solongate/proxy"]) {
|
|
11920
12009
|
log3(" @solongate/proxy already installed");
|
|
@@ -11935,7 +12024,7 @@ function installSdk() {
|
|
|
11935
12024
|
}
|
|
11936
12025
|
}
|
|
11937
12026
|
function injectTypeScript(filePath) {
|
|
11938
|
-
const original =
|
|
12027
|
+
const original = readFileSync11(filePath, "utf-8");
|
|
11939
12028
|
const changes = [];
|
|
11940
12029
|
let modified = original;
|
|
11941
12030
|
if (modified.includes("SecureMcpServer")) {
|
|
@@ -12106,7 +12195,7 @@ async function main2() {
|
|
|
12106
12195
|
log3("");
|
|
12107
12196
|
log3(` Backup: ${backupPath}`);
|
|
12108
12197
|
}
|
|
12109
|
-
|
|
12198
|
+
writeFileSync6(entryFile, result.modified);
|
|
12110
12199
|
log3("");
|
|
12111
12200
|
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");
|
|
12112
12201
|
log3(" \u2502 SolonGate SDK injected successfully! \u2502");
|
|
@@ -12135,7 +12224,7 @@ var init_inject = __esm({
|
|
|
12135
12224
|
|
|
12136
12225
|
// src/create.ts
|
|
12137
12226
|
var create_exports = {};
|
|
12138
|
-
import { mkdirSync as mkdirSync5, writeFileSync as
|
|
12227
|
+
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync7, existsSync as existsSync9 } from "fs";
|
|
12139
12228
|
import { resolve as resolve8, join as join12 } from "path";
|
|
12140
12229
|
import { execSync as execSync2 } from "child_process";
|
|
12141
12230
|
function withSpinner(message, fn) {
|
|
@@ -12215,7 +12304,7 @@ EXAMPLES
|
|
|
12215
12304
|
`);
|
|
12216
12305
|
}
|
|
12217
12306
|
function createProject(dir, name, _policy) {
|
|
12218
|
-
|
|
12307
|
+
writeFileSync7(
|
|
12219
12308
|
join12(dir, "package.json"),
|
|
12220
12309
|
JSON.stringify(
|
|
12221
12310
|
{
|
|
@@ -12245,7 +12334,7 @@ function createProject(dir, name, _policy) {
|
|
|
12245
12334
|
2
|
|
12246
12335
|
) + "\n"
|
|
12247
12336
|
);
|
|
12248
|
-
|
|
12337
|
+
writeFileSync7(
|
|
12249
12338
|
join12(dir, "tsconfig.json"),
|
|
12250
12339
|
JSON.stringify(
|
|
12251
12340
|
{
|
|
@@ -12267,7 +12356,7 @@ function createProject(dir, name, _policy) {
|
|
|
12267
12356
|
) + "\n"
|
|
12268
12357
|
);
|
|
12269
12358
|
mkdirSync5(join12(dir, "src"), { recursive: true });
|
|
12270
|
-
|
|
12359
|
+
writeFileSync7(
|
|
12271
12360
|
join12(dir, "src", "index.ts"),
|
|
12272
12361
|
`#!/usr/bin/env node
|
|
12273
12362
|
|
|
@@ -12309,7 +12398,7 @@ console.log('');
|
|
|
12309
12398
|
console.log('Press Ctrl+C to stop.');
|
|
12310
12399
|
`
|
|
12311
12400
|
);
|
|
12312
|
-
|
|
12401
|
+
writeFileSync7(
|
|
12313
12402
|
join12(dir, ".mcp.json"),
|
|
12314
12403
|
JSON.stringify(
|
|
12315
12404
|
{
|
|
@@ -12327,12 +12416,12 @@ console.log('Press Ctrl+C to stop.');
|
|
|
12327
12416
|
2
|
|
12328
12417
|
) + "\n"
|
|
12329
12418
|
);
|
|
12330
|
-
|
|
12419
|
+
writeFileSync7(
|
|
12331
12420
|
join12(dir, ".env"),
|
|
12332
12421
|
`SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
|
|
12333
12422
|
`
|
|
12334
12423
|
);
|
|
12335
|
-
|
|
12424
|
+
writeFileSync7(
|
|
12336
12425
|
join12(dir, ".gitignore"),
|
|
12337
12426
|
`node_modules/
|
|
12338
12427
|
dist/
|
|
@@ -12426,14 +12515,14 @@ var init_create = __esm({
|
|
|
12426
12515
|
|
|
12427
12516
|
// src/pull-push.ts
|
|
12428
12517
|
var pull_push_exports = {};
|
|
12429
|
-
import { readFileSync as
|
|
12518
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, existsSync as existsSync10 } from "fs";
|
|
12430
12519
|
import { resolve as resolve9 } from "path";
|
|
12431
12520
|
function loadEnv() {
|
|
12432
12521
|
if (process.env.SOLONGATE_API_KEY) return;
|
|
12433
12522
|
const envPath = resolve9(".env");
|
|
12434
12523
|
if (!existsSync10(envPath)) return;
|
|
12435
12524
|
try {
|
|
12436
|
-
const content =
|
|
12525
|
+
const content = readFileSync12(envPath, "utf-8");
|
|
12437
12526
|
for (const line of content.split("\n")) {
|
|
12438
12527
|
const trimmed = line.trim();
|
|
12439
12528
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -12621,7 +12710,7 @@ async function pull(apiKey, file, policyId) {
|
|
|
12621
12710
|
const policy = await fetchCloudPolicy(apiKey, API_URL, policyId);
|
|
12622
12711
|
const { id: _id, ...policyWithoutId } = policy;
|
|
12623
12712
|
const json = JSON.stringify(policyWithoutId, null, 2) + "\n";
|
|
12624
|
-
|
|
12713
|
+
writeFileSync8(file, json, "utf-8");
|
|
12625
12714
|
log5("");
|
|
12626
12715
|
log5(green2(" Saved to: ") + file);
|
|
12627
12716
|
log5(` ${dim2("Name:")} ${policy.name}`);
|
|
@@ -12650,7 +12739,7 @@ async function push(apiKey, file, policyId) {
|
|
|
12650
12739
|
log5(" solongate-proxy list");
|
|
12651
12740
|
process.exit(1);
|
|
12652
12741
|
}
|
|
12653
|
-
const content =
|
|
12742
|
+
const content = readFileSync12(file, "utf-8");
|
|
12654
12743
|
let policy;
|
|
12655
12744
|
try {
|
|
12656
12745
|
policy = JSON.parse(content);
|
package/dist/tui/index.js
CHANGED
|
@@ -169,12 +169,12 @@ function KeyHints({ hints }) {
|
|
|
169
169
|
// src/tui/panels/Live.tsx
|
|
170
170
|
import { Box as Box2, Text as Text2, useInput } from "ink";
|
|
171
171
|
import TextInput from "ink-text-input";
|
|
172
|
-
import { mkdirSync, writeFileSync } from "fs";
|
|
172
|
+
import { mkdirSync, writeFileSync as writeFileSync2 } from "fs";
|
|
173
173
|
import { homedir as homedir4 } from "os";
|
|
174
174
|
import { join as join4 } from "path";
|
|
175
175
|
|
|
176
176
|
// src/tui/local-log.ts
|
|
177
|
-
import { closeSync, openSync, readSync, statSync } from "fs";
|
|
177
|
+
import { closeSync, openSync, readFileSync as readFileSync2, readSync, statSync, writeFileSync } from "fs";
|
|
178
178
|
import { homedir as homedir2 } from "os";
|
|
179
179
|
import { join as join2 } from "path";
|
|
180
180
|
var LOCAL_LOG = join2(homedir2(), ".solongate", "local-logs", "solongate-audit.jsonl");
|
|
@@ -193,6 +193,39 @@ function tailLines(file, maxBytes = 131072) {
|
|
|
193
193
|
return [];
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
|
+
function deleteLocalEntry(at, tool, session) {
|
|
197
|
+
try {
|
|
198
|
+
const lines = readFileSync2(LOCAL_LOG, "utf-8").split("\n");
|
|
199
|
+
let removed = 0;
|
|
200
|
+
const kept = lines.filter((line) => {
|
|
201
|
+
if (!line.trim()) return false;
|
|
202
|
+
if (removed) return true;
|
|
203
|
+
try {
|
|
204
|
+
const j = JSON.parse(line);
|
|
205
|
+
const hit = Date.parse(j.ts ?? "") === at && (j.tool ?? "?") === tool && (!session || j.session_id === session);
|
|
206
|
+
if (hit) {
|
|
207
|
+
removed++;
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
} catch {
|
|
211
|
+
}
|
|
212
|
+
return true;
|
|
213
|
+
});
|
|
214
|
+
if (removed) writeFileSync(LOCAL_LOG, kept.length ? kept.join("\n") + "\n" : "");
|
|
215
|
+
return removed;
|
|
216
|
+
} catch {
|
|
217
|
+
return 0;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
function clearLocalLog() {
|
|
221
|
+
try {
|
|
222
|
+
const n = readFileSync2(LOCAL_LOG, "utf-8").split("\n").filter(Boolean).length;
|
|
223
|
+
writeFileSync(LOCAL_LOG, "");
|
|
224
|
+
return n;
|
|
225
|
+
} catch {
|
|
226
|
+
return 0;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
196
229
|
function parseLocalLines(lines) {
|
|
197
230
|
const out = [];
|
|
198
231
|
for (const line of lines) {
|
|
@@ -210,7 +243,7 @@ function parseLocalLines(lines) {
|
|
|
210
243
|
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
211
244
|
|
|
212
245
|
// src/api-client/client.ts
|
|
213
|
-
import { readFileSync as
|
|
246
|
+
import { readFileSync as readFileSync3, existsSync } from "fs";
|
|
214
247
|
import { resolve, join as join3 } from "path";
|
|
215
248
|
import { homedir as homedir3 } from "os";
|
|
216
249
|
var DEFAULT_API_URL = "https://api.solongate.com";
|
|
@@ -234,7 +267,7 @@ function loginCredentialFile() {
|
|
|
234
267
|
try {
|
|
235
268
|
const p = join3(homedir3(), ".solongate", "cloud-guard.json");
|
|
236
269
|
if (!existsSync(p)) return {};
|
|
237
|
-
const c2 = JSON.parse(
|
|
270
|
+
const c2 = JSON.parse(readFileSync3(p, "utf-8"));
|
|
238
271
|
return c2 && typeof c2 === "object" ? c2 : {};
|
|
239
272
|
} catch {
|
|
240
273
|
return {};
|
|
@@ -244,7 +277,7 @@ function dotenvApiKey() {
|
|
|
244
277
|
try {
|
|
245
278
|
const envPath = resolve(".env");
|
|
246
279
|
if (!existsSync(envPath)) return void 0;
|
|
247
|
-
for (const line of
|
|
280
|
+
for (const line of readFileSync3(envPath, "utf-8").split("\n")) {
|
|
248
281
|
const trimmed = line.trim();
|
|
249
282
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
250
283
|
const eq = trimmed.indexOf("=");
|
|
@@ -496,11 +529,19 @@ var audit_exports = {};
|
|
|
496
529
|
__export(audit_exports, {
|
|
497
530
|
block: () => block,
|
|
498
531
|
list: () => list2,
|
|
532
|
+
remove: () => remove2,
|
|
533
|
+
removeAll: () => removeAll,
|
|
499
534
|
whitelist: () => whitelist
|
|
500
535
|
});
|
|
501
536
|
function list2(query = {}) {
|
|
502
537
|
return request("GET", "/audit-logs", { query });
|
|
503
538
|
}
|
|
539
|
+
function remove2(ids) {
|
|
540
|
+
return request("DELETE", "/audit-logs", { body: { ids } });
|
|
541
|
+
}
|
|
542
|
+
function removeAll() {
|
|
543
|
+
return request("DELETE", "/audit-logs", { body: { scope: "logs" } });
|
|
544
|
+
}
|
|
504
545
|
function whitelist(id, scope = "exact") {
|
|
505
546
|
return request("POST", `/audit-logs/${encodeURIComponent(id)}/whitelist`, { body: { scope } });
|
|
506
547
|
}
|
|
@@ -1257,7 +1298,7 @@ function LivePanel({ active: active2 }) {
|
|
|
1257
1298
|
const file = join4(homedir4(), ".solongate", "live-export.jsonl");
|
|
1258
1299
|
try {
|
|
1259
1300
|
mkdirSync(join4(homedir4(), ".solongate"), { recursive: true });
|
|
1260
|
-
|
|
1301
|
+
writeFileSync2(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
|
|
1261
1302
|
setActionMsg({ text: `\u2713 exported ${visibleDesc.length} lines \u2192 ${file}`, level: "ok", until: Date.now() + 6e3 });
|
|
1262
1303
|
} catch (err) {
|
|
1263
1304
|
setActionMsg({ text: "\u2717 export failed: " + (err instanceof Error ? err.message : String(err)), level: "bad", until: Date.now() + 6e3 });
|
|
@@ -2237,6 +2278,8 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2237
2278
|
const [si, setSi] = useState6(0);
|
|
2238
2279
|
const [sessSearch, setSessSearch] = useState6("");
|
|
2239
2280
|
const [sessSel, setSessSel] = useState6(0);
|
|
2281
|
+
const [confirm, setConfirm] = useState6(null);
|
|
2282
|
+
const [msg, setMsg] = useState6(null);
|
|
2240
2283
|
const toTop = () => setSel(0);
|
|
2241
2284
|
const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
|
|
2242
2285
|
const tsQ = useLoader(() => source === "cloud" ? api.stats.timeseries({ period: "24h" }) : Promise.resolve(null), [source]);
|
|
@@ -2322,6 +2365,31 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2322
2365
|
() => view === "detail" && current?.session && source === "cloud" ? api.audit.list({ session_id: current.session, limit: 40 }) : Promise.resolve(null),
|
|
2323
2366
|
[view, current?.session, source]
|
|
2324
2367
|
);
|
|
2368
|
+
const logsLoading = (source === "cloud" ? cloudQ.loading : localQ.loading) && !editing;
|
|
2369
|
+
const sessLoading = source === "cloud" ? agentsQ.loading : localQ.loading;
|
|
2370
|
+
const doDelete = (kind) => {
|
|
2371
|
+
setMsg({ text: "deleting\u2026", level: "ok" });
|
|
2372
|
+
const run = async () => {
|
|
2373
|
+
if (source === "cloud") {
|
|
2374
|
+
if (kind === "one") {
|
|
2375
|
+
if (!current) throw new Error("nothing selected");
|
|
2376
|
+
await api.audit.remove([current.id]);
|
|
2377
|
+
} else {
|
|
2378
|
+
await api.audit.removeAll();
|
|
2379
|
+
}
|
|
2380
|
+
cloudQ.reload();
|
|
2381
|
+
statsQ.reloadQuiet();
|
|
2382
|
+
} else {
|
|
2383
|
+
const n = kind === "one" && current ? deleteLocalEntry(current.at, current.tool, current.session) : kind === "all" ? clearLocalLog() : 0;
|
|
2384
|
+
if (!n) throw new Error("entry not found in the local file");
|
|
2385
|
+
localQ.reload();
|
|
2386
|
+
}
|
|
2387
|
+
};
|
|
2388
|
+
run().then(() => {
|
|
2389
|
+
setMsg({ text: kind === "one" ? "\u2713 entry deleted" : `\u2713 ALL ${source} logs deleted`, level: "ok" });
|
|
2390
|
+
toTop();
|
|
2391
|
+
}).catch((e) => setMsg({ text: "\u2717 " + (e instanceof Error ? e.message : String(e)), level: "bad" }));
|
|
2392
|
+
};
|
|
2325
2393
|
useInput5(
|
|
2326
2394
|
(input, key) => {
|
|
2327
2395
|
if (view === "detail") {
|
|
@@ -2332,6 +2400,11 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2332
2400
|
else if (key.pageDown) setDetailScroll((n) => n + 10);
|
|
2333
2401
|
return;
|
|
2334
2402
|
}
|
|
2403
|
+
if (view === "logs" ? logsLoading : sessLoading) return;
|
|
2404
|
+
if (confirm && input !== "x" && input !== "X") {
|
|
2405
|
+
setConfirm(null);
|
|
2406
|
+
setMsg(null);
|
|
2407
|
+
}
|
|
2335
2408
|
if (input === "s") {
|
|
2336
2409
|
setSource((s) => s === "cloud" ? "local" : "cloud");
|
|
2337
2410
|
setPage(0);
|
|
@@ -2393,6 +2466,23 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2393
2466
|
setGi((n) => (n + 1) % SIGNALS.length);
|
|
2394
2467
|
setPage(0);
|
|
2395
2468
|
toTop();
|
|
2469
|
+
} else if (input === "x") {
|
|
2470
|
+
if (!current) return;
|
|
2471
|
+
if (confirm?.kind !== "one" || confirm.key !== current.id) {
|
|
2472
|
+
setConfirm({ kind: "one", key: current.id });
|
|
2473
|
+
setMsg({ text: `delete ${current.decision} ${current.tool} (${ago(current.at)} ago)? press x again`, level: "bad" });
|
|
2474
|
+
return;
|
|
2475
|
+
}
|
|
2476
|
+
setConfirm(null);
|
|
2477
|
+
doDelete("one");
|
|
2478
|
+
} else if (input === "X") {
|
|
2479
|
+
if (confirm?.kind !== "all") {
|
|
2480
|
+
setConfirm({ kind: "all", key: "all" });
|
|
2481
|
+
setMsg({ text: `DELETE ALL ${source} logs (${total} entries)? press X again`, level: "bad" });
|
|
2482
|
+
return;
|
|
2483
|
+
}
|
|
2484
|
+
setConfirm(null);
|
|
2485
|
+
doDelete("all");
|
|
2396
2486
|
} else if (input === "t") setEditing("tool");
|
|
2397
2487
|
else if (input === "n") setEditing("agent");
|
|
2398
2488
|
else if (input === "/") setEditing("search");
|
|
@@ -2519,8 +2609,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2519
2609
|
const selC = Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1));
|
|
2520
2610
|
const start2 = Math.min(Math.max(0, selC - Math.floor((listRows2 - 1) / 2)), Math.max(0, sessionsFiltered.length - listRows2));
|
|
2521
2611
|
const win = sessionsFiltered.slice(start2, start2 + listRows2);
|
|
2522
|
-
|
|
2523
|
-
return /* @__PURE__ */ jsx6(DataView, { loading: loading2, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
2612
|
+
return /* @__PURE__ */ jsx6(DataView, { loading: sessLoading, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
2524
2613
|
strip,
|
|
2525
2614
|
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
2526
2615
|
srcChip,
|
|
@@ -2583,15 +2672,14 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2583
2672
|
] }) : null
|
|
2584
2673
|
] }) });
|
|
2585
2674
|
}
|
|
2586
|
-
const headerRows = 6 + (editing && editing !== "sess-search" ? 1 : 0);
|
|
2675
|
+
const headerRows = 6 + (editing && editing !== "sess-search" ? 1 : 0) + (msg ? 1 : 0);
|
|
2587
2676
|
const listRows = Math.max(4, rows - headerRows);
|
|
2588
2677
|
const selClamped = Math.min(sel, Math.max(0, pageRows.length - 1));
|
|
2589
2678
|
const maxStart = Math.max(0, pageRows.length - listRows);
|
|
2590
2679
|
const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
|
|
2591
2680
|
const windowed = pageRows.slice(start, start + listRows);
|
|
2592
2681
|
const reasonW = Math.min(60, Math.max(12, cols - 67));
|
|
2593
|
-
|
|
2594
|
-
return /* @__PURE__ */ jsx6(DataView, { loading, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
2682
|
+
return /* @__PURE__ */ jsx6(DataView, { loading: logsLoading, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
2595
2683
|
strip,
|
|
2596
2684
|
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
2597
2685
|
srcChip,
|
|
@@ -2604,7 +2692,8 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2604
2692
|
chip("search", search || "\xB7", !!search),
|
|
2605
2693
|
sessFilter ? chip("sess", sessFilter.slice(0, 8), true) : null
|
|
2606
2694
|
] }),
|
|
2607
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 \u2190\u2192 page \xB7 f dec \xB7 g sig \xB7 t tool \xB7 n agent \xB7 / search \xB7 s source \xB7 v sessions \xB7 c clear" : "press \u2192 to browse" }),
|
|
2695
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 \u2190\u2192 page \xB7 f dec \xB7 g sig \xB7 t tool \xB7 n agent \xB7 / search \xB7 x del \xB7 X del ALL \xB7 s source \xB7 v sessions \xB7 c clear" : "press \u2192 to browse" }),
|
|
2696
|
+
msg ? /* @__PURE__ */ jsx6(Text6, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate(msg.text, cols) }) : null,
|
|
2608
2697
|
editing && editing !== "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
2609
2698
|
/* @__PURE__ */ jsxs6(Text6, { color: theme.warn, children: [
|
|
2610
2699
|
editing,
|
|
@@ -2950,7 +3039,7 @@ var HELP = [
|
|
|
2950
3039
|
["Live", [["\u2191\u2193", "select a stream row"], ["enter", "full entry content"], ["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)"]]],
|
|
2951
3040
|
["Policies", [["\u2191\u2193", "browse / select"], ["a", "activate (pin) selected policy"], ["x", "deactivate \u2014 no active policy"], ["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"], ["s", "save"], ["x", "discard"]]],
|
|
2952
3041
|
["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"]]],
|
|
2953
|
-
["Audit", [["v", "logs \u2194 sessions"], ["s", "source: cloud \u2194 local"], ["\u2190 \u2192", "prev / next page (500 each)"], ["\u2191\u2193", "select (list scrolls)"], ["enter", "full entry / session logs"], ["f / g", "decision / signal filter"], ["t / n / /", "tool / agent / search"], ["c", "clear filters"]]],
|
|
3042
|
+
["Audit", [["v", "logs \u2194 sessions"], ["s", "source: cloud \u2194 local"], ["\u2190 \u2192", "prev / next page (500 each)"], ["\u2191\u2193", "select (list scrolls)"], ["enter", "full entry / session logs"], ["f / g", "decision / signal filter"], ["t / n / /", "tool / agent / search"], ["x / X", "delete entry / ALL (press twice)"], ["c", "clear filters"]]],
|
|
2954
3043
|
["Settings", [["\u2191\u2193", "move"], ["enter / space", "toggle \xB7 edit \xB7 add"], ["e", "webhook events"], ["d d", "delete"], ["r", "refresh"]]]
|
|
2955
3044
|
];
|
|
2956
3045
|
function HelpOverlay({ cols, rows }) {
|
package/dist/tui/local-log.d.ts
CHANGED
|
@@ -17,6 +17,10 @@ export interface LocalLogLine {
|
|
|
17
17
|
matched_rule_id?: string;
|
|
18
18
|
rate_limit_burst?: boolean;
|
|
19
19
|
}
|
|
20
|
+
/** Delete ONE matching line (ts + tool [+ session]) from the local log file. */
|
|
21
|
+
export declare function deleteLocalEntry(at: number, tool: string, session?: string | null): number;
|
|
22
|
+
/** Empty the local log file. Returns how many lines were removed. */
|
|
23
|
+
export declare function clearLocalLog(): number;
|
|
20
24
|
export declare function parseLocalLines(lines: string[]): Array<LocalLogLine & {
|
|
21
25
|
at: number;
|
|
22
26
|
}>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.81.
|
|
3
|
+
"version": "0.81.26",
|
|
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": {
|