@solongate/proxy 0.81.25 → 0.81.27
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 +203 -55
- package/dist/tui/index.js +162 -14
- 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,9 @@ 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);
|
|
8919
|
+
const [showHelp, setShowHelp] = useState6(false);
|
|
8876
8920
|
const toTop = () => setSel(0);
|
|
8877
8921
|
const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
|
|
8878
8922
|
const tsQ = useLoader(() => source === "cloud" ? api.stats.timeseries({ period: "24h" }) : Promise.resolve(null), [source]);
|
|
@@ -8958,8 +9002,41 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
8958
9002
|
() => view === "detail" && current?.session && source === "cloud" ? api.audit.list({ session_id: current.session, limit: 40 }) : Promise.resolve(null),
|
|
8959
9003
|
[view, current?.session, source]
|
|
8960
9004
|
);
|
|
9005
|
+
const logsLoading = (source === "cloud" ? cloudQ.loading : localQ.loading) && !editing;
|
|
9006
|
+
const sessLoading = source === "cloud" ? agentsQ.loading : localQ.loading;
|
|
9007
|
+
const doDelete = (kind) => {
|
|
9008
|
+
setMsg({ text: "deleting\u2026", level: "ok" });
|
|
9009
|
+
const run12 = async () => {
|
|
9010
|
+
if (source === "cloud") {
|
|
9011
|
+
if (kind === "one") {
|
|
9012
|
+
if (!current) throw new Error("nothing selected");
|
|
9013
|
+
await api.audit.remove([current.id]);
|
|
9014
|
+
} else {
|
|
9015
|
+
await api.audit.removeAll();
|
|
9016
|
+
}
|
|
9017
|
+
cloudQ.reload();
|
|
9018
|
+
statsQ.reloadQuiet();
|
|
9019
|
+
} else {
|
|
9020
|
+
const n = kind === "one" && current ? deleteLocalEntry(current.at, current.tool, current.session) : kind === "all" ? clearLocalLog() : 0;
|
|
9021
|
+
if (!n) throw new Error("entry not found in the local file");
|
|
9022
|
+
localQ.reload();
|
|
9023
|
+
}
|
|
9024
|
+
};
|
|
9025
|
+
run12().then(() => {
|
|
9026
|
+
setMsg({ text: kind === "one" ? "\u2713 entry deleted" : `\u2713 ALL ${source} logs deleted`, level: "ok" });
|
|
9027
|
+
toTop();
|
|
9028
|
+
}).catch((e) => setMsg({ text: "\u2717 " + (e instanceof Error ? e.message : String(e)), level: "bad" }));
|
|
9029
|
+
};
|
|
8961
9030
|
useInput5(
|
|
8962
9031
|
(input, key) => {
|
|
9032
|
+
if (showHelp) {
|
|
9033
|
+
setShowHelp(false);
|
|
9034
|
+
return;
|
|
9035
|
+
}
|
|
9036
|
+
if (input === "?") {
|
|
9037
|
+
setShowHelp(true);
|
|
9038
|
+
return;
|
|
9039
|
+
}
|
|
8963
9040
|
if (view === "detail") {
|
|
8964
9041
|
if (key.leftArrow || key.escape) setView("logs");
|
|
8965
9042
|
else if (key.upArrow) setDetailScroll((n) => Math.max(0, n - 1));
|
|
@@ -8968,6 +9045,11 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
8968
9045
|
else if (key.pageDown) setDetailScroll((n) => n + 10);
|
|
8969
9046
|
return;
|
|
8970
9047
|
}
|
|
9048
|
+
if (view === "logs" ? logsLoading : sessLoading) return;
|
|
9049
|
+
if (confirm && input !== "x" && input !== "X") {
|
|
9050
|
+
setConfirm(null);
|
|
9051
|
+
setMsg(null);
|
|
9052
|
+
}
|
|
8971
9053
|
if (input === "s") {
|
|
8972
9054
|
setSource((s) => s === "cloud" ? "local" : "cloud");
|
|
8973
9055
|
setPage(0);
|
|
@@ -9029,6 +9111,23 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9029
9111
|
setGi((n) => (n + 1) % SIGNALS.length);
|
|
9030
9112
|
setPage(0);
|
|
9031
9113
|
toTop();
|
|
9114
|
+
} else if (input === "x") {
|
|
9115
|
+
if (!current) return;
|
|
9116
|
+
if (confirm?.kind !== "one" || confirm.key !== current.id) {
|
|
9117
|
+
setConfirm({ kind: "one", key: current.id });
|
|
9118
|
+
setMsg({ text: `delete ${current.decision} ${current.tool} (${ago(current.at)} ago)? press x again`, level: "bad" });
|
|
9119
|
+
return;
|
|
9120
|
+
}
|
|
9121
|
+
setConfirm(null);
|
|
9122
|
+
doDelete("one");
|
|
9123
|
+
} else if (input === "X") {
|
|
9124
|
+
if (confirm?.kind !== "all") {
|
|
9125
|
+
setConfirm({ kind: "all", key: "all" });
|
|
9126
|
+
setMsg({ text: `DELETE ALL ${source} logs (${total} entries)? press X again`, level: "bad" });
|
|
9127
|
+
return;
|
|
9128
|
+
}
|
|
9129
|
+
setConfirm(null);
|
|
9130
|
+
doDelete("all");
|
|
9032
9131
|
} else if (input === "t") setEditing("tool");
|
|
9033
9132
|
else if (input === "n") setEditing("agent");
|
|
9034
9133
|
else if (input === "/") setEditing("search");
|
|
@@ -9085,6 +9184,19 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9085
9184
|
/* @__PURE__ */ jsx6(Text6, { color: source === "local" ? theme.ok : "#4f6db8", bold: true, children: source }),
|
|
9086
9185
|
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " (s) " })
|
|
9087
9186
|
] });
|
|
9187
|
+
if (showHelp) {
|
|
9188
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
9189
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: theme.accentBright, children: "AUDIT \u2014 all keys" }),
|
|
9190
|
+
AUDIT_HELP.map(([group, keys]) => /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginTop: 1, children: [
|
|
9191
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: theme.accent, children: group }),
|
|
9192
|
+
keys.map(([k, desc]) => /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
|
|
9193
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.accentBright, children: (" " + k).padEnd(20) }),
|
|
9194
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: desc })
|
|
9195
|
+
] }, k))
|
|
9196
|
+
] }, group)),
|
|
9197
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "press any key to close" }) })
|
|
9198
|
+
] });
|
|
9199
|
+
}
|
|
9088
9200
|
if (view === "detail" && current) {
|
|
9089
9201
|
const sessionCalls = source === "cloud" ? (sessionQ.data?.entries ?? []).map(cloudRow) : localAll.filter((r) => r.session && r.session === current.session).slice(0, 40);
|
|
9090
9202
|
const bodyW = Math.max(20, cols - 2);
|
|
@@ -9155,8 +9267,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9155
9267
|
const selC = Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1));
|
|
9156
9268
|
const start2 = Math.min(Math.max(0, selC - Math.floor((listRows2 - 1) / 2)), Math.max(0, sessionsFiltered.length - listRows2));
|
|
9157
9269
|
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: [
|
|
9270
|
+
return /* @__PURE__ */ jsx6(DataView, { loading: sessLoading, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
9160
9271
|
strip,
|
|
9161
9272
|
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
9162
9273
|
srcChip,
|
|
@@ -9165,7 +9276,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9165
9276
|
chip("status", SESS_STATUS[si] ?? "all", si !== 0),
|
|
9166
9277
|
chip("search", sessSearch || "\xB7", !!sessSearch)
|
|
9167
9278
|
] }),
|
|
9168
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter \u2192 session logs \xB7
|
|
9279
|
+
/* @__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" }),
|
|
9169
9280
|
editing === "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
9170
9281
|
/* @__PURE__ */ jsx6(Text6, { color: theme.warn, children: "search: " }),
|
|
9171
9282
|
/* @__PURE__ */ jsx6(
|
|
@@ -9219,15 +9330,14 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9219
9330
|
] }) : null
|
|
9220
9331
|
] }) });
|
|
9221
9332
|
}
|
|
9222
|
-
const headerRows = 6 + (editing && editing !== "sess-search" ? 1 : 0);
|
|
9333
|
+
const headerRows = 6 + (editing && editing !== "sess-search" ? 1 : 0) + (msg ? 1 : 0);
|
|
9223
9334
|
const listRows = Math.max(4, rows - headerRows);
|
|
9224
9335
|
const selClamped = Math.min(sel, Math.max(0, pageRows.length - 1));
|
|
9225
9336
|
const maxStart = Math.max(0, pageRows.length - listRows);
|
|
9226
9337
|
const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
|
|
9227
9338
|
const windowed = pageRows.slice(start, start + listRows);
|
|
9228
9339
|
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: [
|
|
9340
|
+
return /* @__PURE__ */ jsx6(DataView, { loading: logsLoading, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
9231
9341
|
strip,
|
|
9232
9342
|
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
9233
9343
|
srcChip,
|
|
@@ -9240,7 +9350,8 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
9240
9350
|
chip("search", search || "\xB7", !!search),
|
|
9241
9351
|
sessFilter ? chip("sess", sessFilter.slice(0, 8), true) : null
|
|
9242
9352
|
] }),
|
|
9243
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 \u2190\u2192 page \xB7
|
|
9353
|
+
/* @__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" }),
|
|
9354
|
+
msg ? /* @__PURE__ */ jsx6(Text6, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : null,
|
|
9244
9355
|
editing && editing !== "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
9245
9356
|
/* @__PURE__ */ jsxs6(Text6, { color: theme.warn, children: [
|
|
9246
9357
|
editing,
|
|
@@ -9299,7 +9410,7 @@ function Detail({ label, value, color }) {
|
|
|
9299
9410
|
/* @__PURE__ */ jsx6(Text6, { color, wrap: "truncate", children: value })
|
|
9300
9411
|
] });
|
|
9301
9412
|
}
|
|
9302
|
-
var DECISIONS, SIGNALS, SESS_STATUS, PAGE, LOCAL_MAX_BYTES, cloudRow, sessStatus2, STATUS_DOT;
|
|
9413
|
+
var DECISIONS, SIGNALS, SESS_STATUS, PAGE, LOCAL_MAX_BYTES, cloudRow, AUDIT_HELP, sessStatus2, STATUS_DOT;
|
|
9303
9414
|
var init_Audit = __esm({
|
|
9304
9415
|
"src/tui/panels/Audit.tsx"() {
|
|
9305
9416
|
"use strict";
|
|
@@ -9329,6 +9440,43 @@ var init_Audit = __esm({
|
|
|
9329
9440
|
burst: !!e.rate_limit_burst,
|
|
9330
9441
|
args: e.arguments_summary ? JSON.stringify(e.arguments_summary) : null
|
|
9331
9442
|
});
|
|
9443
|
+
AUDIT_HELP = [
|
|
9444
|
+
[
|
|
9445
|
+
"Logs",
|
|
9446
|
+
[
|
|
9447
|
+
["\u2191\u2193 / PgUp PgDn", "select a row (window follows)"],
|
|
9448
|
+
["enter", "open the FULL entry (reason + arguments)"],
|
|
9449
|
+
["\u2190 \u2192", "previous / next page (500 per page, jumps to top)"],
|
|
9450
|
+
["f", "decision filter: all \u2192 DENY \u2192 ALLOW"],
|
|
9451
|
+
["g", "signal filter: all \u2192 dlp \u2192 ratelimit"],
|
|
9452
|
+
["t / n", "tool / agent filter (type, enter done)"],
|
|
9453
|
+
["/", "free-text search"],
|
|
9454
|
+
["x", "delete the selected entry (press x twice)"],
|
|
9455
|
+
["X", "delete ALL logs of the current source (press X twice)"],
|
|
9456
|
+
["c", "clear every filter (incl. session)"]
|
|
9457
|
+
]
|
|
9458
|
+
],
|
|
9459
|
+
[
|
|
9460
|
+
"Sessions",
|
|
9461
|
+
[
|
|
9462
|
+
["\u2191\u2193", "select a session"],
|
|
9463
|
+
["enter", "open that session's logs"],
|
|
9464
|
+
["f", "status filter: all \u2192 active \u2192 idle \u2192 ended"],
|
|
9465
|
+
["/", "search agent / session id"],
|
|
9466
|
+
["c", "clear session filters"]
|
|
9467
|
+
]
|
|
9468
|
+
],
|
|
9469
|
+
[
|
|
9470
|
+
"Anywhere in Audit",
|
|
9471
|
+
[
|
|
9472
|
+
["v", "switch logs \u2194 sessions"],
|
|
9473
|
+
["s", "switch source cloud \u2194 local file"],
|
|
9474
|
+
["?", "this help \xB7 any key closes"],
|
|
9475
|
+
["esc", "back to the menu"]
|
|
9476
|
+
]
|
|
9477
|
+
],
|
|
9478
|
+
["Entry detail", [["\u2191\u2193 / PgUp PgDn", "scroll the arguments"], ["\u2190 / esc", "back to the list"]]]
|
|
9479
|
+
];
|
|
9332
9480
|
sessStatus2 = (lastAt) => Date.now() - lastAt < 6e4 ? "active" : Date.now() - lastAt < 3e5 ? "idle" : "ended";
|
|
9333
9481
|
STATUS_DOT = {
|
|
9334
9482
|
active: { ch: "\u25CF", color: theme.ok },
|
|
@@ -9663,7 +9811,7 @@ var init_App = __esm({
|
|
|
9663
9811
|
["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
9812
|
["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
9813
|
["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"]]],
|
|
9814
|
+
["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
9815
|
["Settings", [["\u2191\u2193", "move"], ["enter / space", "toggle \xB7 edit \xB7 add"], ["e", "webhook events"], ["d d", "delete"], ["r", "refresh"]]]
|
|
9668
9816
|
];
|
|
9669
9817
|
}
|
|
@@ -9801,7 +9949,7 @@ var init_args = __esm({
|
|
|
9801
9949
|
});
|
|
9802
9950
|
|
|
9803
9951
|
// src/commands/policy.ts
|
|
9804
|
-
import { readFileSync as
|
|
9952
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
9805
9953
|
async function run(argv) {
|
|
9806
9954
|
const { positionals, flags } = parse(argv);
|
|
9807
9955
|
const sub = positionals[0];
|
|
@@ -9976,7 +10124,7 @@ function printRules(rules) {
|
|
|
9976
10124
|
}
|
|
9977
10125
|
async function resolveRules(target) {
|
|
9978
10126
|
if (target.endsWith(".json")) {
|
|
9979
|
-
const parsed = JSON.parse(
|
|
10127
|
+
const parsed = JSON.parse(readFileSync7(target, "utf-8"));
|
|
9980
10128
|
return parsed.rules ?? [];
|
|
9981
10129
|
}
|
|
9982
10130
|
const p = await api.policies.get(target);
|
|
@@ -10869,7 +11017,7 @@ __export(global_install_exports, {
|
|
|
10869
11017
|
runGlobalRestore: () => runGlobalRestore,
|
|
10870
11018
|
unlockProtected: () => unlockProtected
|
|
10871
11019
|
});
|
|
10872
|
-
import { readFileSync as
|
|
11020
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
|
|
10873
11021
|
import { resolve as resolve4, join as join10, dirname } from "path";
|
|
10874
11022
|
import { homedir as homedir8 } from "os";
|
|
10875
11023
|
import { fileURLToPath } from "url";
|
|
@@ -10964,11 +11112,11 @@ function globalPaths() {
|
|
|
10964
11112
|
};
|
|
10965
11113
|
}
|
|
10966
11114
|
function readHook(filename) {
|
|
10967
|
-
return
|
|
11115
|
+
return readFileSync8(join10(HOOKS_DIR, filename), "utf-8");
|
|
10968
11116
|
}
|
|
10969
11117
|
function readGuard() {
|
|
10970
11118
|
const bundled = join10(HOOKS_DIR, "guard.bundled.mjs");
|
|
10971
|
-
return existsSync6(bundled) ?
|
|
11119
|
+
return existsSync6(bundled) ? readFileSync8(bundled, "utf-8") : readHook("guard.mjs");
|
|
10972
11120
|
}
|
|
10973
11121
|
function ask(question) {
|
|
10974
11122
|
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
@@ -10982,13 +11130,13 @@ function runGlobalRestore() {
|
|
|
10982
11130
|
unlockProtected();
|
|
10983
11131
|
removeClaudeShim();
|
|
10984
11132
|
if (existsSync6(p.backupPath)) {
|
|
10985
|
-
|
|
11133
|
+
writeFileSync5(p.settingsPath, readFileSync8(p.backupPath, "utf-8"));
|
|
10986
11134
|
console.log(` Restored ${p.settingsPath} from backup.`);
|
|
10987
11135
|
} else if (existsSync6(p.settingsPath)) {
|
|
10988
11136
|
try {
|
|
10989
|
-
const s = JSON.parse(
|
|
11137
|
+
const s = JSON.parse(readFileSync8(p.settingsPath, "utf-8"));
|
|
10990
11138
|
delete s.hooks;
|
|
10991
|
-
|
|
11139
|
+
writeFileSync5(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
|
|
10992
11140
|
console.log(` Removed SolonGate hooks from ${p.settingsPath}.`);
|
|
10993
11141
|
} catch {
|
|
10994
11142
|
}
|
|
@@ -11026,14 +11174,14 @@ function shimTargets() {
|
|
|
11026
11174
|
}
|
|
11027
11175
|
function writeShimBlock(file, block2) {
|
|
11028
11176
|
const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
|
|
11029
|
-
let content = existsSync6(file) ?
|
|
11177
|
+
let content = existsSync6(file) ? readFileSync8(file, "utf-8") : "";
|
|
11030
11178
|
content = content.replace(re, "");
|
|
11031
11179
|
if (block2) {
|
|
11032
11180
|
if (content.length && !content.endsWith("\n")) content += "\n";
|
|
11033
11181
|
content += block2 + "\n";
|
|
11034
11182
|
}
|
|
11035
11183
|
mkdirSync4(dirname(file), { recursive: true });
|
|
11036
|
-
|
|
11184
|
+
writeFileSync5(file, content);
|
|
11037
11185
|
}
|
|
11038
11186
|
function installClaudeShim(shieldPath) {
|
|
11039
11187
|
const real = resolveRealClaude();
|
|
@@ -11071,7 +11219,7 @@ async function runGlobalInstall(opts = {}) {
|
|
|
11071
11219
|
let apiKey = opts.apiKey || process.env["SOLONGATE_API_KEY"] || "";
|
|
11072
11220
|
if (!apiKey || apiKey === "sg_live_your_key_here") {
|
|
11073
11221
|
try {
|
|
11074
|
-
const cfg = JSON.parse(
|
|
11222
|
+
const cfg = JSON.parse(readFileSync8(p.configPath, "utf-8"));
|
|
11075
11223
|
if (cfg && typeof cfg.apiKey === "string") apiKey = cfg.apiKey;
|
|
11076
11224
|
} catch {
|
|
11077
11225
|
}
|
|
@@ -11087,19 +11235,19 @@ async function runGlobalInstall(opts = {}) {
|
|
|
11087
11235
|
mkdirSync4(p.hooksDir, { recursive: true });
|
|
11088
11236
|
mkdirSync4(p.claudeDir, { recursive: true });
|
|
11089
11237
|
unlockProtected();
|
|
11090
|
-
|
|
11091
|
-
|
|
11092
|
-
|
|
11093
|
-
|
|
11238
|
+
writeFileSync5(join10(p.hooksDir, "guard.mjs"), readGuard());
|
|
11239
|
+
writeFileSync5(join10(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
|
|
11240
|
+
writeFileSync5(join10(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
|
|
11241
|
+
writeFileSync5(join10(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
|
|
11094
11242
|
console.log(` Installed hooks \u2192 ${p.hooksDir}`);
|
|
11095
11243
|
installClaudeShim(join10(p.hooksDir, "shield.mjs"));
|
|
11096
|
-
|
|
11244
|
+
writeFileSync5(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
|
|
11097
11245
|
console.log(` Wrote ${p.configPath}`);
|
|
11098
11246
|
let existing = {};
|
|
11099
11247
|
if (existsSync6(p.settingsPath)) {
|
|
11100
|
-
const raw =
|
|
11248
|
+
const raw = readFileSync8(p.settingsPath, "utf-8");
|
|
11101
11249
|
if (!existsSync6(p.backupPath)) {
|
|
11102
|
-
|
|
11250
|
+
writeFileSync5(p.backupPath, raw);
|
|
11103
11251
|
console.log(` Backed up existing settings \u2192 ${p.backupPath}`);
|
|
11104
11252
|
}
|
|
11105
11253
|
try {
|
|
@@ -11122,7 +11270,7 @@ async function runGlobalInstall(opts = {}) {
|
|
|
11122
11270
|
Stop: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(stopAbs) }] }]
|
|
11123
11271
|
}
|
|
11124
11272
|
};
|
|
11125
|
-
|
|
11273
|
+
writeFileSync5(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
|
|
11126
11274
|
console.log(` Registered global hooks \u2192 ${p.settingsPath}`);
|
|
11127
11275
|
if (process.env["SOLONGATE_OS_LOCK"] === "1") {
|
|
11128
11276
|
lockProtected();
|
|
@@ -11302,7 +11450,7 @@ import { createServer, request as httpRequest } from "http";
|
|
|
11302
11450
|
import { request as httpsRequest } from "https";
|
|
11303
11451
|
import { spawn as spawn3 } from "child_process";
|
|
11304
11452
|
import { URL as URL2 } from "url";
|
|
11305
|
-
import { readFileSync as
|
|
11453
|
+
import { readFileSync as readFileSync9, existsSync as existsSync7, readdirSync, statSync as statSync4 } from "fs";
|
|
11306
11454
|
import { resolve as resolve5 } from "path";
|
|
11307
11455
|
import { homedir as homedir9 } from "os";
|
|
11308
11456
|
function findCacheFile() {
|
|
@@ -11332,7 +11480,7 @@ function loadCfg() {
|
|
|
11332
11480
|
try {
|
|
11333
11481
|
const f = findCacheFile();
|
|
11334
11482
|
if (f && existsSync7(f)) {
|
|
11335
|
-
const c2 = JSON.parse(
|
|
11483
|
+
const c2 = JSON.parse(readFileSync9(f, "utf-8"));
|
|
11336
11484
|
const d = c2?.security?.dlpRedact;
|
|
11337
11485
|
const g = c2?.security?.ghost;
|
|
11338
11486
|
const ghost = g && Array.isArray(g.patterns) ? g.patterns : [];
|
|
@@ -11620,7 +11768,7 @@ __export(logs_server_exports, {
|
|
|
11620
11768
|
runLogsServer: () => runLogsServer
|
|
11621
11769
|
});
|
|
11622
11770
|
import { createServer as createServer2 } from "http";
|
|
11623
|
-
import { readFileSync as
|
|
11771
|
+
import { readFileSync as readFileSync10, statSync as statSync5 } from "fs";
|
|
11624
11772
|
import { resolve as resolve6, join as join11, isAbsolute } from "path";
|
|
11625
11773
|
import { homedir as homedir10 } from "os";
|
|
11626
11774
|
import { readdirSync as readdirSync2 } from "fs";
|
|
@@ -11647,7 +11795,7 @@ async function findLogDir() {
|
|
|
11647
11795
|
const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
|
|
11648
11796
|
for (const f of files) {
|
|
11649
11797
|
try {
|
|
11650
|
-
const c2 = JSON.parse(
|
|
11798
|
+
const c2 = JSON.parse(readFileSync10(join11(base, f), "utf-8"));
|
|
11651
11799
|
const p = c2?.security?.localLogs?.path;
|
|
11652
11800
|
if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
|
|
11653
11801
|
} catch {
|
|
@@ -11656,7 +11804,7 @@ async function findLogDir() {
|
|
|
11656
11804
|
} catch {
|
|
11657
11805
|
}
|
|
11658
11806
|
try {
|
|
11659
|
-
const cfgRaw =
|
|
11807
|
+
const cfgRaw = readFileSync10(join11(base, "cloud-guard.json"), "utf-8");
|
|
11660
11808
|
const { apiKey, apiUrl } = JSON.parse(cfgRaw);
|
|
11661
11809
|
if (apiKey) {
|
|
11662
11810
|
const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
|
|
@@ -11746,7 +11894,7 @@ async function runLogsServer() {
|
|
|
11746
11894
|
return;
|
|
11747
11895
|
}
|
|
11748
11896
|
try {
|
|
11749
|
-
const text =
|
|
11897
|
+
const text = readFileSync10(info.file, "utf-8");
|
|
11750
11898
|
res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8", "Last-Modified": lastMod, "X-Solongate-Exists": "1" });
|
|
11751
11899
|
res.end(text);
|
|
11752
11900
|
} catch {
|
|
@@ -11798,7 +11946,7 @@ var init_logs_server = __esm({
|
|
|
11798
11946
|
|
|
11799
11947
|
// src/inject.ts
|
|
11800
11948
|
var inject_exports = {};
|
|
11801
|
-
import { readFileSync as
|
|
11949
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, existsSync as existsSync8, copyFileSync } from "fs";
|
|
11802
11950
|
import { resolve as resolve7 } from "path";
|
|
11803
11951
|
import { execSync } from "child_process";
|
|
11804
11952
|
function parseInjectArgs(argv) {
|
|
@@ -11858,7 +12006,7 @@ WHAT IT DOES
|
|
|
11858
12006
|
function detectProject() {
|
|
11859
12007
|
if (!existsSync8(resolve7("package.json"))) return false;
|
|
11860
12008
|
try {
|
|
11861
|
-
const pkg = JSON.parse(
|
|
12009
|
+
const pkg = JSON.parse(readFileSync11(resolve7("package.json"), "utf-8"));
|
|
11862
12010
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
11863
12011
|
return !!(allDeps["@modelcontextprotocol/sdk"] || allDeps["@modelcontextprotocol/server"]);
|
|
11864
12012
|
} catch {
|
|
@@ -11867,7 +12015,7 @@ function detectProject() {
|
|
|
11867
12015
|
}
|
|
11868
12016
|
function findTsEntryFile() {
|
|
11869
12017
|
try {
|
|
11870
|
-
const pkg = JSON.parse(
|
|
12018
|
+
const pkg = JSON.parse(readFileSync11(resolve7("package.json"), "utf-8"));
|
|
11871
12019
|
if (pkg.bin) {
|
|
11872
12020
|
const binPath = typeof pkg.bin === "string" ? pkg.bin : Object.values(pkg.bin)[0];
|
|
11873
12021
|
if (typeof binPath === "string") {
|
|
@@ -11894,7 +12042,7 @@ function findTsEntryFile() {
|
|
|
11894
12042
|
const full = resolve7(c2);
|
|
11895
12043
|
if (existsSync8(full)) {
|
|
11896
12044
|
try {
|
|
11897
|
-
const content =
|
|
12045
|
+
const content = readFileSync11(full, "utf-8");
|
|
11898
12046
|
if (content.includes("McpServer") || content.includes("McpServer")) {
|
|
11899
12047
|
return full;
|
|
11900
12048
|
}
|
|
@@ -11914,7 +12062,7 @@ function detectPackageManager() {
|
|
|
11914
12062
|
}
|
|
11915
12063
|
function installSdk() {
|
|
11916
12064
|
try {
|
|
11917
|
-
const pkg = JSON.parse(
|
|
12065
|
+
const pkg = JSON.parse(readFileSync11(resolve7("package.json"), "utf-8"));
|
|
11918
12066
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
11919
12067
|
if (allDeps["@solongate/proxy"]) {
|
|
11920
12068
|
log3(" @solongate/proxy already installed");
|
|
@@ -11935,7 +12083,7 @@ function installSdk() {
|
|
|
11935
12083
|
}
|
|
11936
12084
|
}
|
|
11937
12085
|
function injectTypeScript(filePath) {
|
|
11938
|
-
const original =
|
|
12086
|
+
const original = readFileSync11(filePath, "utf-8");
|
|
11939
12087
|
const changes = [];
|
|
11940
12088
|
let modified = original;
|
|
11941
12089
|
if (modified.includes("SecureMcpServer")) {
|
|
@@ -12106,7 +12254,7 @@ async function main2() {
|
|
|
12106
12254
|
log3("");
|
|
12107
12255
|
log3(` Backup: ${backupPath}`);
|
|
12108
12256
|
}
|
|
12109
|
-
|
|
12257
|
+
writeFileSync6(entryFile, result.modified);
|
|
12110
12258
|
log3("");
|
|
12111
12259
|
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
12260
|
log3(" \u2502 SolonGate SDK injected successfully! \u2502");
|
|
@@ -12135,7 +12283,7 @@ var init_inject = __esm({
|
|
|
12135
12283
|
|
|
12136
12284
|
// src/create.ts
|
|
12137
12285
|
var create_exports = {};
|
|
12138
|
-
import { mkdirSync as mkdirSync5, writeFileSync as
|
|
12286
|
+
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync7, existsSync as existsSync9 } from "fs";
|
|
12139
12287
|
import { resolve as resolve8, join as join12 } from "path";
|
|
12140
12288
|
import { execSync as execSync2 } from "child_process";
|
|
12141
12289
|
function withSpinner(message, fn) {
|
|
@@ -12215,7 +12363,7 @@ EXAMPLES
|
|
|
12215
12363
|
`);
|
|
12216
12364
|
}
|
|
12217
12365
|
function createProject(dir, name, _policy) {
|
|
12218
|
-
|
|
12366
|
+
writeFileSync7(
|
|
12219
12367
|
join12(dir, "package.json"),
|
|
12220
12368
|
JSON.stringify(
|
|
12221
12369
|
{
|
|
@@ -12245,7 +12393,7 @@ function createProject(dir, name, _policy) {
|
|
|
12245
12393
|
2
|
|
12246
12394
|
) + "\n"
|
|
12247
12395
|
);
|
|
12248
|
-
|
|
12396
|
+
writeFileSync7(
|
|
12249
12397
|
join12(dir, "tsconfig.json"),
|
|
12250
12398
|
JSON.stringify(
|
|
12251
12399
|
{
|
|
@@ -12267,7 +12415,7 @@ function createProject(dir, name, _policy) {
|
|
|
12267
12415
|
) + "\n"
|
|
12268
12416
|
);
|
|
12269
12417
|
mkdirSync5(join12(dir, "src"), { recursive: true });
|
|
12270
|
-
|
|
12418
|
+
writeFileSync7(
|
|
12271
12419
|
join12(dir, "src", "index.ts"),
|
|
12272
12420
|
`#!/usr/bin/env node
|
|
12273
12421
|
|
|
@@ -12309,7 +12457,7 @@ console.log('');
|
|
|
12309
12457
|
console.log('Press Ctrl+C to stop.');
|
|
12310
12458
|
`
|
|
12311
12459
|
);
|
|
12312
|
-
|
|
12460
|
+
writeFileSync7(
|
|
12313
12461
|
join12(dir, ".mcp.json"),
|
|
12314
12462
|
JSON.stringify(
|
|
12315
12463
|
{
|
|
@@ -12327,12 +12475,12 @@ console.log('Press Ctrl+C to stop.');
|
|
|
12327
12475
|
2
|
|
12328
12476
|
) + "\n"
|
|
12329
12477
|
);
|
|
12330
|
-
|
|
12478
|
+
writeFileSync7(
|
|
12331
12479
|
join12(dir, ".env"),
|
|
12332
12480
|
`SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
|
|
12333
12481
|
`
|
|
12334
12482
|
);
|
|
12335
|
-
|
|
12483
|
+
writeFileSync7(
|
|
12336
12484
|
join12(dir, ".gitignore"),
|
|
12337
12485
|
`node_modules/
|
|
12338
12486
|
dist/
|
|
@@ -12426,14 +12574,14 @@ var init_create = __esm({
|
|
|
12426
12574
|
|
|
12427
12575
|
// src/pull-push.ts
|
|
12428
12576
|
var pull_push_exports = {};
|
|
12429
|
-
import { readFileSync as
|
|
12577
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, existsSync as existsSync10 } from "fs";
|
|
12430
12578
|
import { resolve as resolve9 } from "path";
|
|
12431
12579
|
function loadEnv() {
|
|
12432
12580
|
if (process.env.SOLONGATE_API_KEY) return;
|
|
12433
12581
|
const envPath = resolve9(".env");
|
|
12434
12582
|
if (!existsSync10(envPath)) return;
|
|
12435
12583
|
try {
|
|
12436
|
-
const content =
|
|
12584
|
+
const content = readFileSync12(envPath, "utf-8");
|
|
12437
12585
|
for (const line of content.split("\n")) {
|
|
12438
12586
|
const trimmed = line.trim();
|
|
12439
12587
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -12621,7 +12769,7 @@ async function pull(apiKey, file, policyId) {
|
|
|
12621
12769
|
const policy = await fetchCloudPolicy(apiKey, API_URL, policyId);
|
|
12622
12770
|
const { id: _id, ...policyWithoutId } = policy;
|
|
12623
12771
|
const json = JSON.stringify(policyWithoutId, null, 2) + "\n";
|
|
12624
|
-
|
|
12772
|
+
writeFileSync8(file, json, "utf-8");
|
|
12625
12773
|
log5("");
|
|
12626
12774
|
log5(green2(" Saved to: ") + file);
|
|
12627
12775
|
log5(` ${dim2("Name:")} ${policy.name}`);
|
|
@@ -12650,7 +12798,7 @@ async function push(apiKey, file, policyId) {
|
|
|
12650
12798
|
log5(" solongate-proxy list");
|
|
12651
12799
|
process.exit(1);
|
|
12652
12800
|
}
|
|
12653
|
-
const content =
|
|
12801
|
+
const content = readFileSync12(file, "utf-8");
|
|
12654
12802
|
let policy;
|
|
12655
12803
|
try {
|
|
12656
12804
|
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 });
|
|
@@ -2214,6 +2255,43 @@ function loadLocalRows() {
|
|
|
2214
2255
|
args: j.arguments ? JSON.stringify(j.arguments) : null
|
|
2215
2256
|
})).sort((a, b) => b.at - a.at);
|
|
2216
2257
|
}
|
|
2258
|
+
var AUDIT_HELP = [
|
|
2259
|
+
[
|
|
2260
|
+
"Logs",
|
|
2261
|
+
[
|
|
2262
|
+
["\u2191\u2193 / PgUp PgDn", "select a row (window follows)"],
|
|
2263
|
+
["enter", "open the FULL entry (reason + arguments)"],
|
|
2264
|
+
["\u2190 \u2192", "previous / next page (500 per page, jumps to top)"],
|
|
2265
|
+
["f", "decision filter: all \u2192 DENY \u2192 ALLOW"],
|
|
2266
|
+
["g", "signal filter: all \u2192 dlp \u2192 ratelimit"],
|
|
2267
|
+
["t / n", "tool / agent filter (type, enter done)"],
|
|
2268
|
+
["/", "free-text search"],
|
|
2269
|
+
["x", "delete the selected entry (press x twice)"],
|
|
2270
|
+
["X", "delete ALL logs of the current source (press X twice)"],
|
|
2271
|
+
["c", "clear every filter (incl. session)"]
|
|
2272
|
+
]
|
|
2273
|
+
],
|
|
2274
|
+
[
|
|
2275
|
+
"Sessions",
|
|
2276
|
+
[
|
|
2277
|
+
["\u2191\u2193", "select a session"],
|
|
2278
|
+
["enter", "open that session's logs"],
|
|
2279
|
+
["f", "status filter: all \u2192 active \u2192 idle \u2192 ended"],
|
|
2280
|
+
["/", "search agent / session id"],
|
|
2281
|
+
["c", "clear session filters"]
|
|
2282
|
+
]
|
|
2283
|
+
],
|
|
2284
|
+
[
|
|
2285
|
+
"Anywhere in Audit",
|
|
2286
|
+
[
|
|
2287
|
+
["v", "switch logs \u2194 sessions"],
|
|
2288
|
+
["s", "switch source cloud \u2194 local file"],
|
|
2289
|
+
["?", "this help \xB7 any key closes"],
|
|
2290
|
+
["esc", "back to the menu"]
|
|
2291
|
+
]
|
|
2292
|
+
],
|
|
2293
|
+
["Entry detail", [["\u2191\u2193 / PgUp PgDn", "scroll the arguments"], ["\u2190 / esc", "back to the list"]]]
|
|
2294
|
+
];
|
|
2217
2295
|
var sessStatus2 = (lastAt) => Date.now() - lastAt < 6e4 ? "active" : Date.now() - lastAt < 3e5 ? "idle" : "ended";
|
|
2218
2296
|
var STATUS_DOT = {
|
|
2219
2297
|
active: { ch: "\u25CF", color: theme.ok },
|
|
@@ -2237,6 +2315,9 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2237
2315
|
const [si, setSi] = useState6(0);
|
|
2238
2316
|
const [sessSearch, setSessSearch] = useState6("");
|
|
2239
2317
|
const [sessSel, setSessSel] = useState6(0);
|
|
2318
|
+
const [confirm, setConfirm] = useState6(null);
|
|
2319
|
+
const [msg, setMsg] = useState6(null);
|
|
2320
|
+
const [showHelp, setShowHelp] = useState6(false);
|
|
2240
2321
|
const toTop = () => setSel(0);
|
|
2241
2322
|
const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
|
|
2242
2323
|
const tsQ = useLoader(() => source === "cloud" ? api.stats.timeseries({ period: "24h" }) : Promise.resolve(null), [source]);
|
|
@@ -2322,8 +2403,41 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2322
2403
|
() => view === "detail" && current?.session && source === "cloud" ? api.audit.list({ session_id: current.session, limit: 40 }) : Promise.resolve(null),
|
|
2323
2404
|
[view, current?.session, source]
|
|
2324
2405
|
);
|
|
2406
|
+
const logsLoading = (source === "cloud" ? cloudQ.loading : localQ.loading) && !editing;
|
|
2407
|
+
const sessLoading = source === "cloud" ? agentsQ.loading : localQ.loading;
|
|
2408
|
+
const doDelete = (kind) => {
|
|
2409
|
+
setMsg({ text: "deleting\u2026", level: "ok" });
|
|
2410
|
+
const run = async () => {
|
|
2411
|
+
if (source === "cloud") {
|
|
2412
|
+
if (kind === "one") {
|
|
2413
|
+
if (!current) throw new Error("nothing selected");
|
|
2414
|
+
await api.audit.remove([current.id]);
|
|
2415
|
+
} else {
|
|
2416
|
+
await api.audit.removeAll();
|
|
2417
|
+
}
|
|
2418
|
+
cloudQ.reload();
|
|
2419
|
+
statsQ.reloadQuiet();
|
|
2420
|
+
} else {
|
|
2421
|
+
const n = kind === "one" && current ? deleteLocalEntry(current.at, current.tool, current.session) : kind === "all" ? clearLocalLog() : 0;
|
|
2422
|
+
if (!n) throw new Error("entry not found in the local file");
|
|
2423
|
+
localQ.reload();
|
|
2424
|
+
}
|
|
2425
|
+
};
|
|
2426
|
+
run().then(() => {
|
|
2427
|
+
setMsg({ text: kind === "one" ? "\u2713 entry deleted" : `\u2713 ALL ${source} logs deleted`, level: "ok" });
|
|
2428
|
+
toTop();
|
|
2429
|
+
}).catch((e) => setMsg({ text: "\u2717 " + (e instanceof Error ? e.message : String(e)), level: "bad" }));
|
|
2430
|
+
};
|
|
2325
2431
|
useInput5(
|
|
2326
2432
|
(input, key) => {
|
|
2433
|
+
if (showHelp) {
|
|
2434
|
+
setShowHelp(false);
|
|
2435
|
+
return;
|
|
2436
|
+
}
|
|
2437
|
+
if (input === "?") {
|
|
2438
|
+
setShowHelp(true);
|
|
2439
|
+
return;
|
|
2440
|
+
}
|
|
2327
2441
|
if (view === "detail") {
|
|
2328
2442
|
if (key.leftArrow || key.escape) setView("logs");
|
|
2329
2443
|
else if (key.upArrow) setDetailScroll((n) => Math.max(0, n - 1));
|
|
@@ -2332,6 +2446,11 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2332
2446
|
else if (key.pageDown) setDetailScroll((n) => n + 10);
|
|
2333
2447
|
return;
|
|
2334
2448
|
}
|
|
2449
|
+
if (view === "logs" ? logsLoading : sessLoading) return;
|
|
2450
|
+
if (confirm && input !== "x" && input !== "X") {
|
|
2451
|
+
setConfirm(null);
|
|
2452
|
+
setMsg(null);
|
|
2453
|
+
}
|
|
2335
2454
|
if (input === "s") {
|
|
2336
2455
|
setSource((s) => s === "cloud" ? "local" : "cloud");
|
|
2337
2456
|
setPage(0);
|
|
@@ -2393,6 +2512,23 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2393
2512
|
setGi((n) => (n + 1) % SIGNALS.length);
|
|
2394
2513
|
setPage(0);
|
|
2395
2514
|
toTop();
|
|
2515
|
+
} else if (input === "x") {
|
|
2516
|
+
if (!current) return;
|
|
2517
|
+
if (confirm?.kind !== "one" || confirm.key !== current.id) {
|
|
2518
|
+
setConfirm({ kind: "one", key: current.id });
|
|
2519
|
+
setMsg({ text: `delete ${current.decision} ${current.tool} (${ago(current.at)} ago)? press x again`, level: "bad" });
|
|
2520
|
+
return;
|
|
2521
|
+
}
|
|
2522
|
+
setConfirm(null);
|
|
2523
|
+
doDelete("one");
|
|
2524
|
+
} else if (input === "X") {
|
|
2525
|
+
if (confirm?.kind !== "all") {
|
|
2526
|
+
setConfirm({ kind: "all", key: "all" });
|
|
2527
|
+
setMsg({ text: `DELETE ALL ${source} logs (${total} entries)? press X again`, level: "bad" });
|
|
2528
|
+
return;
|
|
2529
|
+
}
|
|
2530
|
+
setConfirm(null);
|
|
2531
|
+
doDelete("all");
|
|
2396
2532
|
} else if (input === "t") setEditing("tool");
|
|
2397
2533
|
else if (input === "n") setEditing("agent");
|
|
2398
2534
|
else if (input === "/") setEditing("search");
|
|
@@ -2449,6 +2585,19 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2449
2585
|
/* @__PURE__ */ jsx6(Text6, { color: source === "local" ? theme.ok : "#4f6db8", bold: true, children: source }),
|
|
2450
2586
|
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " (s) " })
|
|
2451
2587
|
] });
|
|
2588
|
+
if (showHelp) {
|
|
2589
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
2590
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: theme.accentBright, children: "AUDIT \u2014 all keys" }),
|
|
2591
|
+
AUDIT_HELP.map(([group, keys]) => /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginTop: 1, children: [
|
|
2592
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: theme.accent, children: group }),
|
|
2593
|
+
keys.map(([k, desc]) => /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
|
|
2594
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.accentBright, children: (" " + k).padEnd(20) }),
|
|
2595
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: desc })
|
|
2596
|
+
] }, k))
|
|
2597
|
+
] }, group)),
|
|
2598
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "press any key to close" }) })
|
|
2599
|
+
] });
|
|
2600
|
+
}
|
|
2452
2601
|
if (view === "detail" && current) {
|
|
2453
2602
|
const sessionCalls = source === "cloud" ? (sessionQ.data?.entries ?? []).map(cloudRow) : localAll.filter((r) => r.session && r.session === current.session).slice(0, 40);
|
|
2454
2603
|
const bodyW = Math.max(20, cols - 2);
|
|
@@ -2519,8 +2668,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2519
2668
|
const selC = Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1));
|
|
2520
2669
|
const start2 = Math.min(Math.max(0, selC - Math.floor((listRows2 - 1) / 2)), Math.max(0, sessionsFiltered.length - listRows2));
|
|
2521
2670
|
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: [
|
|
2671
|
+
return /* @__PURE__ */ jsx6(DataView, { loading: sessLoading, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
2524
2672
|
strip,
|
|
2525
2673
|
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
2526
2674
|
srcChip,
|
|
@@ -2529,7 +2677,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2529
2677
|
chip("status", SESS_STATUS[si] ?? "all", si !== 0),
|
|
2530
2678
|
chip("search", sessSearch || "\xB7", !!sessSearch)
|
|
2531
2679
|
] }),
|
|
2532
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter \u2192 session logs \xB7
|
|
2680
|
+
/* @__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" }),
|
|
2533
2681
|
editing === "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
2534
2682
|
/* @__PURE__ */ jsx6(Text6, { color: theme.warn, children: "search: " }),
|
|
2535
2683
|
/* @__PURE__ */ jsx6(
|
|
@@ -2583,15 +2731,14 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2583
2731
|
] }) : null
|
|
2584
2732
|
] }) });
|
|
2585
2733
|
}
|
|
2586
|
-
const headerRows = 6 + (editing && editing !== "sess-search" ? 1 : 0);
|
|
2734
|
+
const headerRows = 6 + (editing && editing !== "sess-search" ? 1 : 0) + (msg ? 1 : 0);
|
|
2587
2735
|
const listRows = Math.max(4, rows - headerRows);
|
|
2588
2736
|
const selClamped = Math.min(sel, Math.max(0, pageRows.length - 1));
|
|
2589
2737
|
const maxStart = Math.max(0, pageRows.length - listRows);
|
|
2590
2738
|
const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
|
|
2591
2739
|
const windowed = pageRows.slice(start, start + listRows);
|
|
2592
2740
|
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: [
|
|
2741
|
+
return /* @__PURE__ */ jsx6(DataView, { loading: logsLoading, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
2595
2742
|
strip,
|
|
2596
2743
|
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
2597
2744
|
srcChip,
|
|
@@ -2604,7 +2751,8 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2604
2751
|
chip("search", search || "\xB7", !!search),
|
|
2605
2752
|
sessFilter ? chip("sess", sessFilter.slice(0, 8), true) : null
|
|
2606
2753
|
] }),
|
|
2607
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 \u2190\u2192 page \xB7
|
|
2754
|
+
/* @__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" }),
|
|
2755
|
+
msg ? /* @__PURE__ */ jsx6(Text6, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate(msg.text, cols) }) : null,
|
|
2608
2756
|
editing && editing !== "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
2609
2757
|
/* @__PURE__ */ jsxs6(Text6, { color: theme.warn, children: [
|
|
2610
2758
|
editing,
|
|
@@ -2950,7 +3098,7 @@ var HELP = [
|
|
|
2950
3098
|
["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
3099
|
["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
3100
|
["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"]]],
|
|
3101
|
+
["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
3102
|
["Settings", [["\u2191\u2193", "move"], ["enter / space", "toggle \xB7 edit \xB7 add"], ["e", "webhook events"], ["d d", "delete"], ["r", "refresh"]]]
|
|
2955
3103
|
];
|
|
2956
3104
|
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.27",
|
|
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": {
|