@solongate/proxy 0.81.54 → 0.81.56
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/client.d.ts +2 -0
- package/dist/api-client/settings.d.ts +15 -0
- package/dist/commands/index.js +8 -0
- package/dist/index.js +259 -64
- package/dist/tui/index.js +258 -63
- package/package.json +1 -1
|
@@ -15,6 +15,8 @@ export interface SavedAccount {
|
|
|
15
15
|
export declare function listAccounts(): SavedAccount[];
|
|
16
16
|
/** Upsert an account (dedupe by apiKey). Called from `login`. */
|
|
17
17
|
export declare function saveAccount(acc: SavedAccount): void;
|
|
18
|
+
/** Remove a saved account from this device (does NOT revoke the cloud key). */
|
|
19
|
+
export declare function removeAccount(apiKey: string): void;
|
|
18
20
|
export declare function setViewCredentials(creds: Credentials | null): void;
|
|
19
21
|
/** True when the given key is the ACTIVE one the guard hooks use. */
|
|
20
22
|
export declare function isActiveAccount(apiKey: string): boolean;
|
|
@@ -61,6 +61,21 @@ export declare function deleteAlert(id: string): Promise<{
|
|
|
61
61
|
export declare function setAlertEnabled(id: string, enabled: boolean): Promise<{
|
|
62
62
|
ok: true;
|
|
63
63
|
}>;
|
|
64
|
+
export declare function updateAlert(id: string, patch: {
|
|
65
|
+
signal?: AlertRule['signal'];
|
|
66
|
+
threshold?: number;
|
|
67
|
+
windowSeconds?: number;
|
|
68
|
+
emails?: string[];
|
|
69
|
+
telegram?: string[];
|
|
70
|
+
}): Promise<{
|
|
71
|
+
rule: AlertRule;
|
|
72
|
+
}>;
|
|
73
|
+
export declare function sendTestAlert(id: string): Promise<{
|
|
74
|
+
delivered: {
|
|
75
|
+
telegram: boolean;
|
|
76
|
+
email: boolean;
|
|
77
|
+
};
|
|
78
|
+
}>;
|
|
64
79
|
export interface DenialWebhook {
|
|
65
80
|
id: string;
|
|
66
81
|
url: string;
|
package/dist/commands/index.js
CHANGED
|
@@ -221,11 +221,13 @@ __export(settings_exports, {
|
|
|
221
221
|
getSecurityLayers: () => getSecurityLayers,
|
|
222
222
|
getSelfProtection: () => getSelfProtection,
|
|
223
223
|
getWebhooks: () => getWebhooks,
|
|
224
|
+
sendTestAlert: () => sendTestAlert,
|
|
224
225
|
sendTestWebhook: () => sendTestWebhook,
|
|
225
226
|
setAlertEnabled: () => setAlertEnabled,
|
|
226
227
|
setLocalLogs: () => setLocalLogs,
|
|
227
228
|
setSecurityLayers: () => setSecurityLayers,
|
|
228
229
|
setSelfProtection: () => setSelfProtection,
|
|
230
|
+
updateAlert: () => updateAlert,
|
|
229
231
|
updateWebhook: () => updateWebhook
|
|
230
232
|
});
|
|
231
233
|
function getSecurityLayers() {
|
|
@@ -267,6 +269,12 @@ function deleteAlert(id) {
|
|
|
267
269
|
function setAlertEnabled(id, enabled) {
|
|
268
270
|
return request("PATCH", "/settings/denial-alerts", { query: { id }, body: { enabled } });
|
|
269
271
|
}
|
|
272
|
+
function updateAlert(id, patch) {
|
|
273
|
+
return request("PATCH", "/settings/denial-alerts", { query: { id }, body: patch });
|
|
274
|
+
}
|
|
275
|
+
function sendTestAlert(id) {
|
|
276
|
+
return request("POST", "/settings/denial-alerts/send-test", { body: { id } });
|
|
277
|
+
}
|
|
270
278
|
function getWebhooks() {
|
|
271
279
|
return request("GET", "/settings/denial-webhook");
|
|
272
280
|
}
|
package/dist/index.js
CHANGED
|
@@ -7086,6 +7086,20 @@ function saveAccount(acc) {
|
|
|
7086
7086
|
} catch {
|
|
7087
7087
|
}
|
|
7088
7088
|
}
|
|
7089
|
+
function removeAccount(apiKey) {
|
|
7090
|
+
try {
|
|
7091
|
+
const list6 = (() => {
|
|
7092
|
+
try {
|
|
7093
|
+
const raw = JSON.parse(readFileSync8(accountsFile(), "utf-8"));
|
|
7094
|
+
return Array.isArray(raw) ? raw.filter((a) => a && a.apiKey) : [];
|
|
7095
|
+
} catch {
|
|
7096
|
+
return [];
|
|
7097
|
+
}
|
|
7098
|
+
})();
|
|
7099
|
+
writeFileSync6(accountsFile(), JSON.stringify(list6.filter((a) => a.apiKey !== apiKey), null, 2));
|
|
7100
|
+
} catch {
|
|
7101
|
+
}
|
|
7102
|
+
}
|
|
7089
7103
|
function setViewCredentials(creds) {
|
|
7090
7104
|
viewOverride = creds;
|
|
7091
7105
|
cached2 = null;
|
|
@@ -7394,11 +7408,13 @@ __export(settings_exports, {
|
|
|
7394
7408
|
getSecurityLayers: () => getSecurityLayers,
|
|
7395
7409
|
getSelfProtection: () => getSelfProtection,
|
|
7396
7410
|
getWebhooks: () => getWebhooks,
|
|
7411
|
+
sendTestAlert: () => sendTestAlert,
|
|
7397
7412
|
sendTestWebhook: () => sendTestWebhook,
|
|
7398
7413
|
setAlertEnabled: () => setAlertEnabled,
|
|
7399
7414
|
setLocalLogs: () => setLocalLogs,
|
|
7400
7415
|
setSecurityLayers: () => setSecurityLayers,
|
|
7401
7416
|
setSelfProtection: () => setSelfProtection,
|
|
7417
|
+
updateAlert: () => updateAlert,
|
|
7402
7418
|
updateWebhook: () => updateWebhook
|
|
7403
7419
|
});
|
|
7404
7420
|
function getSecurityLayers() {
|
|
@@ -7440,6 +7456,12 @@ function deleteAlert(id) {
|
|
|
7440
7456
|
function setAlertEnabled(id, enabled) {
|
|
7441
7457
|
return request("PATCH", "/settings/denial-alerts", { query: { id }, body: { enabled } });
|
|
7442
7458
|
}
|
|
7459
|
+
function updateAlert(id, patch) {
|
|
7460
|
+
return request("PATCH", "/settings/denial-alerts", { query: { id }, body: patch });
|
|
7461
|
+
}
|
|
7462
|
+
function sendTestAlert(id) {
|
|
7463
|
+
return request("POST", "/settings/denial-alerts/send-test", { body: { id } });
|
|
7464
|
+
}
|
|
7443
7465
|
function getWebhooks() {
|
|
7444
7466
|
return request("GET", "/settings/denial-webhook");
|
|
7445
7467
|
}
|
|
@@ -10076,6 +10098,7 @@ function SettingsPanel({
|
|
|
10076
10098
|
const [confirmDel, setConfirmDel] = useState7(null);
|
|
10077
10099
|
const [msg, setMsg] = useState7(null);
|
|
10078
10100
|
const [busy, setBusy] = useState7(false);
|
|
10101
|
+
const [editor, setEditor] = useState7(null);
|
|
10079
10102
|
const [accounts, setAccounts] = useState7(() => listAccounts());
|
|
10080
10103
|
const [login, setLogin] = useState7(null);
|
|
10081
10104
|
const [tick, setTick] = useState7(0);
|
|
@@ -10095,22 +10118,30 @@ function SettingsPanel({
|
|
|
10095
10118
|
const localQ = useLoader(() => listAccounts().length ? api.settings.getLocalLogs() : Promise.resolve(null));
|
|
10096
10119
|
const whQ = useLoader(() => listAccounts().length ? api.settings.getWebhooks() : Promise.resolve(null));
|
|
10097
10120
|
const alertQ = useLoader(() => listAccounts().length ? api.settings.getAlerts() : Promise.resolve(null));
|
|
10121
|
+
const guardQ = useLoader(() => listAccounts().length ? api.settings.getGuardStatus() : Promise.resolve(null));
|
|
10122
|
+
const selfQ = useLoader(() => listAccounts().length ? api.settings.getSelfProtection() : Promise.resolve(null));
|
|
10098
10123
|
const local = localQ.data;
|
|
10124
|
+
const guard = guardQ.data;
|
|
10125
|
+
const selfProt = selfQ.data;
|
|
10099
10126
|
const [hidden, setHidden] = useState7(/* @__PURE__ */ new Set());
|
|
10100
10127
|
const webhooks = (whQ.data?.webhooks ?? []).filter((w) => !hidden.has("wh:" + w.id));
|
|
10101
10128
|
const alerts = (alertQ.data?.rules ?? []).filter((r) => !hidden.has("alert:" + r.id));
|
|
10129
|
+
const hasEmailAlert = alerts.some((r) => alertChannel(r) === "email");
|
|
10130
|
+
const hasTgAlert = alerts.some((r) => alertChannel(r) === "telegram");
|
|
10102
10131
|
const rowsAll = [
|
|
10103
10132
|
...accounts.map((acc) => ({ kind: "acct", acc })),
|
|
10104
10133
|
{ kind: "acct-add" },
|
|
10105
10134
|
...locked ? [] : [
|
|
10135
|
+
{ kind: "guard" },
|
|
10136
|
+
{ kind: "self" },
|
|
10106
10137
|
{ kind: "ll-enabled" },
|
|
10107
10138
|
{ kind: "ll-path" },
|
|
10108
10139
|
{ kind: "ll-server" },
|
|
10109
10140
|
...webhooks.map((wh) => ({ kind: "wh", wh })),
|
|
10110
10141
|
{ kind: "wh-add" },
|
|
10111
10142
|
...alerts.map((rule) => ({ kind: "alert", rule })),
|
|
10112
|
-
{ kind: "alert-add-email" },
|
|
10113
|
-
{ kind: "alert-add-tg" }
|
|
10143
|
+
...hasEmailAlert ? [] : [{ kind: "alert-add-email" }],
|
|
10144
|
+
...hasTgAlert ? [] : [{ kind: "alert-add-tg" }]
|
|
10114
10145
|
]
|
|
10115
10146
|
];
|
|
10116
10147
|
const selClamped = Math.min(sel, rowsAll.length - 1);
|
|
@@ -10121,6 +10152,8 @@ function SettingsPanel({
|
|
|
10121
10152
|
localQ.reload();
|
|
10122
10153
|
whQ.reload();
|
|
10123
10154
|
alertQ.reload();
|
|
10155
|
+
guardQ.reload();
|
|
10156
|
+
selfQ.reload();
|
|
10124
10157
|
};
|
|
10125
10158
|
const run12 = (label, fn, reload) => {
|
|
10126
10159
|
if (busy) return;
|
|
@@ -10165,6 +10198,57 @@ function SettingsPanel({
|
|
|
10165
10198
|
if (!abort.current) setLogin({ phase: "error", msg: "timed out \u2014 press n to retry" });
|
|
10166
10199
|
})();
|
|
10167
10200
|
};
|
|
10201
|
+
const openAlertEditor = (channel, rule) => {
|
|
10202
|
+
setEditor({
|
|
10203
|
+
id: rule?.id,
|
|
10204
|
+
channel,
|
|
10205
|
+
target: (channel === "email" ? rule?.emails?.[0] : rule?.telegram?.[0]) ?? "",
|
|
10206
|
+
signal: rule?.signal ?? "any",
|
|
10207
|
+
threshold: rule?.threshold ?? THRESH_MIN,
|
|
10208
|
+
windowSeconds: rule?.windowSeconds ?? 300,
|
|
10209
|
+
field: rule ? 1 : 0
|
|
10210
|
+
// new rule starts on the target; existing on signal
|
|
10211
|
+
});
|
|
10212
|
+
if (!rule) {
|
|
10213
|
+
setInput("");
|
|
10214
|
+
setEditing("alert-target");
|
|
10215
|
+
}
|
|
10216
|
+
};
|
|
10217
|
+
const normTarget = (channel, raw) => {
|
|
10218
|
+
const v = raw.trim();
|
|
10219
|
+
if (channel === "email") {
|
|
10220
|
+
const email = v.replace(/^mailto:/i, "").replace(/^["'<]+|["'>]+$/g, "").replace(/[.,;:]+$/, "").replace(/\s+/g, "");
|
|
10221
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) return { value: email, error: "that does not look like an e-mail address" };
|
|
10222
|
+
return { value: email };
|
|
10223
|
+
}
|
|
10224
|
+
const tg = v.replace(/^@/, "").replace(/^(chat[\s_-]*)?id[:=\s]*/i, "").replace(/\s+/g, "");
|
|
10225
|
+
if (!/^-?\d{5,}$/.test(tg)) return { value: tg, error: "a telegram chat id is a number \u2014 get yours from @userinfobot" };
|
|
10226
|
+
return { value: tg };
|
|
10227
|
+
};
|
|
10228
|
+
const saveEditor = () => {
|
|
10229
|
+
if (!editor) return;
|
|
10230
|
+
const t = normTarget(editor.channel, editor.target);
|
|
10231
|
+
if (t.error) {
|
|
10232
|
+
setMsg({ text: "\u2717 " + t.error, level: "bad" });
|
|
10233
|
+
return;
|
|
10234
|
+
}
|
|
10235
|
+
const channels = editor.channel === "email" ? { emails: [t.value], telegram: [] } : { telegram: [t.value], emails: [] };
|
|
10236
|
+
const body = { signal: editor.signal, threshold: editor.threshold, windowSeconds: editor.windowSeconds, ...channels };
|
|
10237
|
+
const id = editor.id;
|
|
10238
|
+
setEditor(null);
|
|
10239
|
+
if (id) run12("alert updated", () => api.settings.updateAlert(id, body), alertQ.reload);
|
|
10240
|
+
else run12(`${editor.channel} alert added`, () => api.settings.createAlert({ name: "dataroom alert", ...body }), alertQ.reload);
|
|
10241
|
+
};
|
|
10242
|
+
const testAlert = (rule) => {
|
|
10243
|
+
run12(
|
|
10244
|
+
"alert test sent \u2014 check your " + (alertChannel(rule) ?? "channel"),
|
|
10245
|
+
() => api.settings.sendTestAlert(rule.id).then((r) => {
|
|
10246
|
+
if (!r.delivered.telegram && !r.delivered.email) throw new Error("no channel on this rule");
|
|
10247
|
+
}),
|
|
10248
|
+
() => {
|
|
10249
|
+
}
|
|
10250
|
+
);
|
|
10251
|
+
};
|
|
10168
10252
|
const activate = (r) => {
|
|
10169
10253
|
if (r.kind === "acct") {
|
|
10170
10254
|
setViewCredentials({ apiKey: r.acc.apiKey, apiUrl: r.acc.apiUrl });
|
|
@@ -10172,6 +10256,9 @@ function SettingsPanel({
|
|
|
10172
10256
|
setMsg({ text: `viewing ${acctLabel(r.acc)}`, level: "ok" });
|
|
10173
10257
|
} else if (r.kind === "acct-add") {
|
|
10174
10258
|
beginLogin();
|
|
10259
|
+
} else if (r.kind === "self") {
|
|
10260
|
+
if (!selfProt) return;
|
|
10261
|
+
run12(selfProt.enabled ? "self-protection disabled" : "self-protection enabled", () => api.settings.setSelfProtection(!selfProt.enabled), selfQ.reload);
|
|
10175
10262
|
} else if (r.kind === "ll-enabled") {
|
|
10176
10263
|
if (!local) return;
|
|
10177
10264
|
if (!local.enabled && !local.path.trim()) {
|
|
@@ -10194,13 +10281,12 @@ function SettingsPanel({
|
|
|
10194
10281
|
setInput("");
|
|
10195
10282
|
setEditing("wh-url");
|
|
10196
10283
|
} else if (r.kind === "alert") {
|
|
10197
|
-
|
|
10284
|
+
const ch = alertChannel(r.rule);
|
|
10285
|
+
if (ch) openAlertEditor(ch, r.rule);
|
|
10198
10286
|
} else if (r.kind === "alert-add-email") {
|
|
10199
|
-
|
|
10200
|
-
|
|
10201
|
-
|
|
10202
|
-
setInput("");
|
|
10203
|
-
setEditing("alert-tg");
|
|
10287
|
+
openAlertEditor("email");
|
|
10288
|
+
} else if (r.kind === "alert-add-tg") {
|
|
10289
|
+
openAlertEditor("telegram");
|
|
10204
10290
|
}
|
|
10205
10291
|
};
|
|
10206
10292
|
const submitInput = () => {
|
|
@@ -10211,22 +10297,15 @@ function SettingsPanel({
|
|
|
10211
10297
|
const enabled = (local?.enabled ?? false) && v.length > 0;
|
|
10212
10298
|
run12(v ? `path saved${enabled ? "" : " (press enter on enabled to turn on)"}` : "path cleared (local logs off)", () => api.settings.setLocalLogs({ enabled, path: v }), localQ.reload);
|
|
10213
10299
|
} else if (which === "wh-url") {
|
|
10214
|
-
|
|
10215
|
-
|
|
10216
|
-
|
|
10217
|
-
|
|
10218
|
-
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v)) {
|
|
10219
|
-
setMsg({ text: "\u2717 that does not look like an e-mail address", level: "bad" });
|
|
10220
|
-
return;
|
|
10221
|
-
}
|
|
10222
|
-
run12("email alert added", () => api.settings.createAlert({ ...ALERT_BASE, emails: [v] }), alertQ.reload);
|
|
10223
|
-
} else if (which === "alert-tg") {
|
|
10224
|
-
if (!v) return;
|
|
10225
|
-
if (!/^-?\d{5,}$/.test(v)) {
|
|
10226
|
-
setMsg({ text: "\u2717 a telegram chat id is a number \u2014 get yours from @userinfobot", level: "bad" });
|
|
10300
|
+
const url = v.replace(/^["'<]+|["'>]+$/g, "").trim();
|
|
10301
|
+
if (!url) return;
|
|
10302
|
+
if (!/^https?:\/\/\S+$/i.test(url)) {
|
|
10303
|
+
setMsg({ text: "\u2717 webhook url must start with http:// or https://", level: "bad" });
|
|
10227
10304
|
return;
|
|
10228
10305
|
}
|
|
10229
|
-
run12("
|
|
10306
|
+
run12("webhook added", () => api.settings.createWebhook({ url, events: "denials" }), whQ.reload);
|
|
10307
|
+
} else if (which === "alert-target" && editor) {
|
|
10308
|
+
setEditor({ ...editor, target: v, field: 1 });
|
|
10230
10309
|
}
|
|
10231
10310
|
};
|
|
10232
10311
|
useInput6(
|
|
@@ -10238,6 +10317,35 @@ function SettingsPanel({
|
|
|
10238
10317
|
}
|
|
10239
10318
|
return;
|
|
10240
10319
|
}
|
|
10320
|
+
if (editor) {
|
|
10321
|
+
if (key.escape) {
|
|
10322
|
+
setEditor(null);
|
|
10323
|
+
return;
|
|
10324
|
+
}
|
|
10325
|
+
if (inp === "e" || key.return && editor.field === 0) {
|
|
10326
|
+
setInput(editor.target);
|
|
10327
|
+
setEditing("alert-target");
|
|
10328
|
+
return;
|
|
10329
|
+
}
|
|
10330
|
+
if (inp === "t" && editor.id) {
|
|
10331
|
+
const rule = alerts.find((a) => a.id === editor.id);
|
|
10332
|
+
if (rule) testAlert(rule);
|
|
10333
|
+
return;
|
|
10334
|
+
}
|
|
10335
|
+
if (key.return) {
|
|
10336
|
+
saveEditor();
|
|
10337
|
+
return;
|
|
10338
|
+
}
|
|
10339
|
+
if (key.upArrow) setEditor({ ...editor, field: (editor.field + EDITOR_FIELDS - 1) % EDITOR_FIELDS });
|
|
10340
|
+
else if (key.downArrow) setEditor({ ...editor, field: (editor.field + 1) % EDITOR_FIELDS });
|
|
10341
|
+
else if (key.leftArrow || key.rightArrow) {
|
|
10342
|
+
const d = key.rightArrow ? 1 : -1;
|
|
10343
|
+
if (editor.field === 1) setEditor({ ...editor, signal: SIGNALS2[(SIGNALS2.indexOf(editor.signal) + d + SIGNALS2.length) % SIGNALS2.length] });
|
|
10344
|
+
else if (editor.field === 2) setEditor({ ...editor, threshold: Math.max(THRESH_MIN, Math.min(THRESH_MAX, editor.threshold + d * 5)) });
|
|
10345
|
+
else if (editor.field === 3) setEditor({ ...editor, windowSeconds: WINDOWS[Math.max(0, Math.min(WINDOWS.length - 1, nearestWindowIdx(editor.windowSeconds) + d))] });
|
|
10346
|
+
}
|
|
10347
|
+
return;
|
|
10348
|
+
}
|
|
10241
10349
|
setMsg(null);
|
|
10242
10350
|
if (key.upArrow) {
|
|
10243
10351
|
setSel((n) => Math.max(0, n - 1));
|
|
@@ -10251,28 +10359,41 @@ function SettingsPanel({
|
|
|
10251
10359
|
const ok = setActiveAccount({ apiKey: cur.acc.apiKey, apiUrl: cur.acc.apiUrl });
|
|
10252
10360
|
setMsg(ok ? { text: `\u2713 ${acctLabel(cur.acc)} is now the ACTIVE key (guard + logging)`, level: "ok" } : { text: "\u2717 could not set active", level: "bad" });
|
|
10253
10361
|
refreshAccounts();
|
|
10254
|
-
} else if (cur.kind === "wh" || cur.kind === "alert" || cur.kind === "ll-enabled" || cur.kind === "ll-server")
|
|
10362
|
+
} else if (cur.kind === "wh" || cur.kind === "alert" || cur.kind === "self" || cur.kind === "ll-enabled" || cur.kind === "ll-server") {
|
|
10363
|
+
if (cur.kind === "alert") run12(cur.rule.enabled ? "alert disabled" : "alert enabled", () => api.settings.setAlertEnabled(cur.rule.id, !cur.rule.enabled), alertQ.reload);
|
|
10364
|
+
else activate(cur);
|
|
10365
|
+
}
|
|
10366
|
+
} else if (inp === "x" && cur.kind === "acct") {
|
|
10367
|
+
const k = "acct:" + cur.acc.apiKey;
|
|
10368
|
+
if (confirmDel !== k) {
|
|
10369
|
+
setConfirmDel(k);
|
|
10370
|
+
setMsg({ text: `press x again to remove ${acctLabel(cur.acc)} from this device (the cloud account is untouched)`, level: "bad" });
|
|
10371
|
+
return;
|
|
10372
|
+
}
|
|
10373
|
+
setConfirmDel(null);
|
|
10374
|
+
removeAccount(cur.acc.apiKey);
|
|
10375
|
+
setMsg({ text: `\u2713 removed ${acctLabel(cur.acc)} from this device`, level: "ok" });
|
|
10376
|
+
refreshAccounts();
|
|
10255
10377
|
} else if (inp === "t" && cur.kind === "wh") {
|
|
10256
|
-
run12(
|
|
10257
|
-
|
|
10258
|
-
|
|
10259
|
-
|
|
10260
|
-
|
|
10261
|
-
|
|
10262
|
-
);
|
|
10263
|
-
} else if (inp === "n") beginLogin();
|
|
10378
|
+
run12("webhook test sent \u2014 check your endpoint", () => api.settings.sendTestWebhook(cur.wh.id).then((r) => {
|
|
10379
|
+
if (!r.delivered) throw new Error("endpoint rejected the test (non-2xx)");
|
|
10380
|
+
}), whQ.reload);
|
|
10381
|
+
} else if (inp === "t" && cur.kind === "alert") {
|
|
10382
|
+
testAlert(cur.rule);
|
|
10383
|
+
} else if (inp === "e" && cur.kind === "ll-path") activate(cur);
|
|
10264
10384
|
else if (inp === "e" && cur.kind === "wh") {
|
|
10265
10385
|
const next = EVENTS[(EVENTS.indexOf(cur.wh.events) + 1) % EVENTS.length];
|
|
10266
10386
|
run12(`webhook events \u2192 ${next}`, () => api.settings.updateWebhook(cur.wh.id, { events: next }), whQ.reload);
|
|
10267
|
-
} else if (inp === "e" && cur.kind === "
|
|
10387
|
+
} else if (inp === "e" && cur.kind === "alert") {
|
|
10388
|
+
const ch = alertChannel(cur.rule);
|
|
10389
|
+
if (ch) openAlertEditor(ch, cur.rule);
|
|
10390
|
+
} else if (inp === "n") beginLogin();
|
|
10268
10391
|
else if (inp === "a") {
|
|
10269
10392
|
if (cur.kind === "wh" || cur.kind === "wh-add") {
|
|
10270
10393
|
setInput("");
|
|
10271
10394
|
setEditing("wh-url");
|
|
10272
|
-
} else if (cur.kind === "alert
|
|
10273
|
-
|
|
10274
|
-
setEditing("alert-email");
|
|
10275
|
-
}
|
|
10395
|
+
} else if (cur.kind === "alert-add-email") openAlertEditor("email");
|
|
10396
|
+
else if (cur.kind === "alert-add-tg") openAlertEditor("telegram");
|
|
10276
10397
|
} else if (inp === "d" && (cur.kind === "wh" || cur.kind === "alert")) {
|
|
10277
10398
|
const k = keyOf(cur);
|
|
10278
10399
|
if (confirmDel !== k) {
|
|
@@ -10312,30 +10433,70 @@ function SettingsPanel({
|
|
|
10312
10433
|
] })
|
|
10313
10434
|
] });
|
|
10314
10435
|
}
|
|
10315
|
-
|
|
10316
|
-
|
|
10436
|
+
if (editor) {
|
|
10437
|
+
const fieldRow = (idx, label, value, hint) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
10438
|
+
/* @__PURE__ */ jsx7(Text7, { color: editor.field === idx ? theme.accentBright : theme.dim, children: editor.field === idx ? "\u25B8 " : " " }),
|
|
10439
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: label.padEnd(11) }),
|
|
10440
|
+
value,
|
|
10441
|
+
editor.field === idx && hint ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " " + hint }) : null
|
|
10442
|
+
] });
|
|
10443
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
10444
|
+
/* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
|
|
10445
|
+
editor.id ? "Edit" : "New",
|
|
10446
|
+
" ",
|
|
10447
|
+
editor.channel,
|
|
10448
|
+
" alert"
|
|
10449
|
+
] }),
|
|
10450
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
10451
|
+
"\u2191\u2193 field \xB7 \u2190\u2192 change \xB7 e edit target \xB7 enter save",
|
|
10452
|
+
editor.id ? " \xB7 t test" : "",
|
|
10453
|
+
" \xB7 esc cancel"
|
|
10454
|
+
] }),
|
|
10455
|
+
editing === "alert-target" ? /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, children: [
|
|
10456
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: editor.channel === "email" ? "e-mail address: " : "telegram chat id (from @userinfobot): " }),
|
|
10457
|
+
/* @__PURE__ */ jsx7(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
|
|
10458
|
+
] }) : msg ? /* @__PURE__ */ jsx7(Text7, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : /* @__PURE__ */ jsx7(Text7, { children: " " }),
|
|
10459
|
+
/* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
10460
|
+
fieldRow(0, "target", editor.target ? /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(editor.target, cols - 16) }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "empty \u2014 press e" }), "press e to edit"),
|
|
10461
|
+
fieldRow(1, "signal", /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: SIGNAL_LABEL[editor.signal] }), "\u2190\u2192 change"),
|
|
10462
|
+
fieldRow(2, "threshold", /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: `\u2265 ${editor.threshold}` }), "\u2190\u2192 \xB15"),
|
|
10463
|
+
fieldRow(3, "window", /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: winLabel(editor.windowSeconds) }), "\u2190\u2192 change")
|
|
10464
|
+
] }),
|
|
10465
|
+
/* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
10466
|
+
"fires when ",
|
|
10467
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: SIGNAL_LABEL[editor.signal] }),
|
|
10468
|
+
" reach ",
|
|
10469
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: editor.threshold }),
|
|
10470
|
+
" within",
|
|
10471
|
+
" ",
|
|
10472
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: winLabel(editor.windowSeconds) })
|
|
10473
|
+
] }) })
|
|
10474
|
+
] });
|
|
10475
|
+
}
|
|
10476
|
+
const loading = !locked && (localQ.loading && !localQ.data || whQ.loading && !whQ.data || alertQ.loading && !alertQ.data || guardQ.loading && !guardQ.data || selfQ.loading && !selfQ.data);
|
|
10477
|
+
const error = locked ? null : localQ.error ?? whQ.error ?? alertQ.error ?? guardQ.error ?? selfQ.error;
|
|
10317
10478
|
const mark = (r) => keyOf(r) === keyOf(cur) && focused ? "\u25B8 " : " ";
|
|
10318
10479
|
const onOff = (on) => on ? /* @__PURE__ */ jsx7(Text7, { color: theme.ok, children: "on " }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "off" });
|
|
10319
10480
|
const chanOf = (rule) => [...rule.emails ?? [], ...(rule.telegram ?? []).map((t) => "tg " + t)].join(", ") || "\u2014";
|
|
10320
|
-
const listBudget = Math.max(4, rows -
|
|
10481
|
+
const listBudget = Math.max(4, rows - 12);
|
|
10321
10482
|
const start = Math.min(Math.max(0, selClamped - Math.floor(listBudget / 2)), Math.max(0, rowsAll.length - listBudget));
|
|
10322
10483
|
const inWindow = (r) => {
|
|
10323
10484
|
const i = rowsAll.findIndex((x) => keyOf(x) === keyOf(r));
|
|
10324
10485
|
return i >= start && i < start + listBudget;
|
|
10325
10486
|
};
|
|
10326
10487
|
return /* @__PURE__ */ jsx7(DataView, { loading, error, children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
10327
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: focused ? "\u2191\u2193 move \xB7 enter
|
|
10488
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: focused ? "\u2191\u2193 move \xB7 enter act \xB7 m toggle \xB7 t test \xB7 e edit \xB7 x remove acct \xB7 d delete \xB7 n add acct \xB7 r refresh" : "press \u2192 to configure" }),
|
|
10328
10489
|
editing ? /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
10329
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: editing === "path" ? "local log path: " :
|
|
10490
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: editing === "path" ? "local log path: " : "webhook url: " }),
|
|
10330
10491
|
/* @__PURE__ */ jsx7(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
|
|
10331
10492
|
] }) : msg ? /* @__PURE__ */ jsx7(Text7, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : login?.msg ? /* @__PURE__ */ jsx7(Text7, { color: login.phase === "error" ? theme.bad : theme.ok, children: login.msg }) : /* @__PURE__ */ jsx7(Text7, { children: " " }),
|
|
10332
10493
|
/* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
10333
10494
|
/* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
|
|
10334
10495
|
"ACCOUNTS ",
|
|
10335
10496
|
/* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
10336
|
-
"\u2014
|
|
10497
|
+
"\u2014 on this device (",
|
|
10337
10498
|
accounts.length,
|
|
10338
|
-
") \xB7 \u25CF viewing
|
|
10499
|
+
") \xB7 \u25CF viewing \xB7 ACTIVE = key the guard uses \xB7 x removes"
|
|
10339
10500
|
] })
|
|
10340
10501
|
] }),
|
|
10341
10502
|
accounts.length === 0 ? /* @__PURE__ */ jsxs7(Text7, { children: [
|
|
@@ -10351,7 +10512,7 @@ function SettingsPanel({
|
|
|
10351
10512
|
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(acctLabel(a), 30).padEnd(31) }),
|
|
10352
10513
|
isView ? /* @__PURE__ */ jsx7(Text7, { color: theme.ok, children: "\u25CF viewing " }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " " }),
|
|
10353
10514
|
isActive ? /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: "ACTIVE " }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " " }),
|
|
10354
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: a.project ? truncate2(a.project,
|
|
10515
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: a.project ? truncate2(a.project, 20) : "" })
|
|
10355
10516
|
] }, a.apiKey);
|
|
10356
10517
|
}),
|
|
10357
10518
|
inWindow({ kind: "acct-add" }) ? /* @__PURE__ */ jsxs7(Text7, { children: [
|
|
@@ -10360,28 +10521,49 @@ function SettingsPanel({
|
|
|
10360
10521
|
] }) : null
|
|
10361
10522
|
] }),
|
|
10362
10523
|
locked ? null : /* @__PURE__ */ jsxs7(Fragment5, { children: [
|
|
10524
|
+
inWindow({ kind: "guard" }) || inWindow({ kind: "self" }) ? /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
10525
|
+
/* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
|
|
10526
|
+
"PROTECTION ",
|
|
10527
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2014 the guard hook enforcing every layer on this project" })
|
|
10528
|
+
] }),
|
|
10529
|
+
inWindow({ kind: "guard" }) ? /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
10530
|
+
/* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "guard" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "guard" }) }),
|
|
10531
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "guard".padEnd(11) }),
|
|
10532
|
+
guard ? /* @__PURE__ */ jsxs7(Fragment5, { children: [
|
|
10533
|
+
/* @__PURE__ */ jsx7(Text7, { color: guard.up_to_date ? theme.ok : theme.warn, children: `v${guard.installed ?? "?"}` }),
|
|
10534
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: guard.up_to_date ? " (latest)" : ` \u2192 v${guard.latest} available` }),
|
|
10535
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: ` \xB7 ${guard.device_count} device${guard.device_count === 1 ? "" : "s"}${guard.outdated_count ? ` \xB7 ${guard.outdated_count} outdated` : ""}` })
|
|
10536
|
+
] }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "loading\u2026" })
|
|
10537
|
+
] }) : null,
|
|
10538
|
+
inWindow({ kind: "self" }) ? /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
10539
|
+
/* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "self" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "self" }) }),
|
|
10540
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "self-prot".padEnd(11) }),
|
|
10541
|
+
selfProt ? onOff(selfProt.enabled) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2026" }),
|
|
10542
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " blocks agents from editing SolonGate\u2019s own hooks/config \xB7 enter toggles" })
|
|
10543
|
+
] }) : null
|
|
10544
|
+
] }) : null,
|
|
10363
10545
|
inWindow({ kind: "ll-enabled" }) || inWindow({ kind: "ll-path" }) || inWindow({ kind: "ll-server" }) ? /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
10364
10546
|
/* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
|
|
10365
10547
|
"LOCAL LOGS ",
|
|
10366
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2014 hooks mirror every decision to a file on
|
|
10548
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2014 hooks mirror every decision to a file on this machine" })
|
|
10367
10549
|
] }),
|
|
10368
|
-
/* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
10550
|
+
inWindow({ kind: "ll-enabled" }) ? /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
10369
10551
|
/* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "ll-enabled" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "ll-enabled" }) }),
|
|
10370
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "enabled ".padEnd(
|
|
10552
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "enabled ".padEnd(11) }),
|
|
10371
10553
|
onOff(!!local?.enabled),
|
|
10372
10554
|
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " enter toggles" })
|
|
10373
|
-
] }),
|
|
10374
|
-
/* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
10555
|
+
] }) : null,
|
|
10556
|
+
inWindow({ kind: "ll-path" }) ? /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
10375
10557
|
/* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "ll-path" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "ll-path" }) }),
|
|
10376
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "path ".padEnd(
|
|
10558
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "path ".padEnd(11) }),
|
|
10377
10559
|
local?.path ? /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(local.path, cols - 14) }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "not set \u2014 enter to edit" })
|
|
10378
|
-
] }),
|
|
10379
|
-
/* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
10560
|
+
] }) : null,
|
|
10561
|
+
inWindow({ kind: "ll-server" }) ? /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
10380
10562
|
/* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "ll-server" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "ll-server" }) }),
|
|
10381
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "dashboard".padEnd(
|
|
10563
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "dashboard".padEnd(11) }),
|
|
10382
10564
|
srv.running ? /* @__PURE__ */ jsx7(Text7, { color: theme.ok, children: `running 127.0.0.1:${srv.port}` }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "stopped" }),
|
|
10383
10565
|
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: srv.running ? " serves local logs to the dashboard \xB7 survives closing the dataroom \xB7 enter stops" : " enter starts the local-logs link for the dashboard" })
|
|
10384
|
-
] })
|
|
10566
|
+
] }) : null
|
|
10385
10567
|
] }) : null,
|
|
10386
10568
|
/* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
10387
10569
|
/* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
|
|
@@ -10389,7 +10571,7 @@ function SettingsPanel({
|
|
|
10389
10571
|
/* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
10390
10572
|
"\u2014 POST every matching event to your endpoint (",
|
|
10391
10573
|
webhooks.length,
|
|
10392
|
-
")"
|
|
10574
|
+
") \xB7 t tests"
|
|
10393
10575
|
] })
|
|
10394
10576
|
] }),
|
|
10395
10577
|
webhooks.filter((wh) => inWindow({ kind: "wh", wh })).map((wh) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
@@ -10407,17 +10589,17 @@ function SettingsPanel({
|
|
|
10407
10589
|
/* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
|
|
10408
10590
|
"ALERTS ",
|
|
10409
10591
|
/* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
10410
|
-
"\u2014
|
|
10592
|
+
"\u2014 spike alerts to one email + one telegram (",
|
|
10411
10593
|
alerts.length,
|
|
10412
|
-
")"
|
|
10594
|
+
") \xB7 enter edits \xB7 t tests"
|
|
10413
10595
|
] })
|
|
10414
10596
|
] }),
|
|
10415
10597
|
alerts.filter((rule) => inWindow({ kind: "alert", rule })).map((rule) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
10416
10598
|
/* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "alert:" + rule.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert", rule }) }),
|
|
10417
10599
|
onOff(rule.enabled),
|
|
10418
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: (" " + rule.signal).padEnd(
|
|
10419
|
-
/* @__PURE__ */ jsx7(Text7, { children: `\u2265${rule.threshold}/${rule.windowSeconds}
|
|
10420
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(chanOf(rule), cols -
|
|
10600
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: (" " + SIGNAL_LABEL[rule.signal]).padEnd(13) }),
|
|
10601
|
+
/* @__PURE__ */ jsx7(Text7, { children: `\u2265${rule.threshold}/${winLabel(rule.windowSeconds)} `.padEnd(11) }),
|
|
10602
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(chanOf(rule), cols - 34) })
|
|
10421
10603
|
] }, rule.id)),
|
|
10422
10604
|
inWindow({ kind: "alert-add-email" }) ? /* @__PURE__ */ jsxs7(Text7, { children: [
|
|
10423
10605
|
/* @__PURE__ */ jsx7(Text7, { color: cur.kind === "alert-add-email" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert-add-email" }) }),
|
|
@@ -10425,13 +10607,14 @@ function SettingsPanel({
|
|
|
10425
10607
|
] }) : null,
|
|
10426
10608
|
inWindow({ kind: "alert-add-tg" }) ? /* @__PURE__ */ jsxs7(Text7, { children: [
|
|
10427
10609
|
/* @__PURE__ */ jsx7(Text7, { color: cur.kind === "alert-add-tg" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert-add-tg" }) }),
|
|
10428
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "+ add telegram alert (enter \u2014 numeric chat id
|
|
10429
|
-
] }) : null
|
|
10610
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "+ add telegram alert (enter \u2014 numeric chat id from @userinfobot)" })
|
|
10611
|
+
] }) : null,
|
|
10612
|
+
hasEmailAlert && hasTgAlert ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " both channels in use \u2014 remove one (d) to change it" }) : null
|
|
10430
10613
|
] })
|
|
10431
10614
|
] })
|
|
10432
10615
|
] }) });
|
|
10433
10616
|
}
|
|
10434
|
-
var EVENTS, SPIN2,
|
|
10617
|
+
var EVENTS, SPIN2, SIGNALS2, SIGNAL_LABEL, WINDOWS, winLabel, THRESH_MIN, THRESH_MAX, nearestWindowIdx, EDITOR_FIELDS, acctLabel, alertChannel;
|
|
10435
10618
|
var init_Settings = __esm({
|
|
10436
10619
|
"src/tui/panels/Settings.tsx"() {
|
|
10437
10620
|
"use strict";
|
|
@@ -10444,8 +10627,20 @@ var init_Settings = __esm({
|
|
|
10444
10627
|
init_theme();
|
|
10445
10628
|
EVENTS = ["denials", "allowed", "all"];
|
|
10446
10629
|
SPIN2 = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
10447
|
-
|
|
10630
|
+
SIGNALS2 = ["any", "deny", "dlp", "ratelimit"];
|
|
10631
|
+
SIGNAL_LABEL = { any: "any signal", deny: "denials", dlp: "dlp secrets", ratelimit: "rate-limit" };
|
|
10632
|
+
WINDOWS = [30, 60, 300, 3600, 86400];
|
|
10633
|
+
winLabel = (s) => s < 60 ? `${s}s` : s < 3600 ? `${s / 60}m` : s < 86400 ? `${s / 3600}h` : `${s / 86400}d`;
|
|
10634
|
+
THRESH_MIN = 5;
|
|
10635
|
+
THRESH_MAX = 100;
|
|
10636
|
+
nearestWindowIdx = (s) => {
|
|
10637
|
+
let best = 0;
|
|
10638
|
+
for (let i = 1; i < WINDOWS.length; i++) if (Math.abs(WINDOWS[i] - s) < Math.abs(WINDOWS[best] - s)) best = i;
|
|
10639
|
+
return best;
|
|
10640
|
+
};
|
|
10641
|
+
EDITOR_FIELDS = 4;
|
|
10448
10642
|
acctLabel = (a) => a.email || a.user || "account \u2026" + a.apiKey.slice(-4);
|
|
10643
|
+
alertChannel = (r) => r.emails && r.emails.length ? "email" : r.telegram && r.telegram.length ? "telegram" : null;
|
|
10449
10644
|
}
|
|
10450
10645
|
});
|
|
10451
10646
|
|
package/dist/tui/index.js
CHANGED
|
@@ -283,6 +283,20 @@ function saveAccount(acc) {
|
|
|
283
283
|
} catch {
|
|
284
284
|
}
|
|
285
285
|
}
|
|
286
|
+
function removeAccount(apiKey) {
|
|
287
|
+
try {
|
|
288
|
+
const list5 = (() => {
|
|
289
|
+
try {
|
|
290
|
+
const raw = JSON.parse(readFileSync3(accountsFile(), "utf-8"));
|
|
291
|
+
return Array.isArray(raw) ? raw.filter((a) => a && a.apiKey) : [];
|
|
292
|
+
} catch {
|
|
293
|
+
return [];
|
|
294
|
+
}
|
|
295
|
+
})();
|
|
296
|
+
writeFileSync2(accountsFile(), JSON.stringify(list5.filter((a) => a.apiKey !== apiKey), null, 2));
|
|
297
|
+
} catch {
|
|
298
|
+
}
|
|
299
|
+
}
|
|
286
300
|
var viewOverride = null;
|
|
287
301
|
function setViewCredentials(creds) {
|
|
288
302
|
viewOverride = creds;
|
|
@@ -550,11 +564,13 @@ __export(settings_exports, {
|
|
|
550
564
|
getSecurityLayers: () => getSecurityLayers,
|
|
551
565
|
getSelfProtection: () => getSelfProtection,
|
|
552
566
|
getWebhooks: () => getWebhooks,
|
|
567
|
+
sendTestAlert: () => sendTestAlert,
|
|
553
568
|
sendTestWebhook: () => sendTestWebhook,
|
|
554
569
|
setAlertEnabled: () => setAlertEnabled,
|
|
555
570
|
setLocalLogs: () => setLocalLogs,
|
|
556
571
|
setSecurityLayers: () => setSecurityLayers,
|
|
557
572
|
setSelfProtection: () => setSelfProtection,
|
|
573
|
+
updateAlert: () => updateAlert,
|
|
558
574
|
updateWebhook: () => updateWebhook
|
|
559
575
|
});
|
|
560
576
|
function getSecurityLayers() {
|
|
@@ -596,6 +612,12 @@ function deleteAlert(id) {
|
|
|
596
612
|
function setAlertEnabled(id, enabled) {
|
|
597
613
|
return request("PATCH", "/settings/denial-alerts", { query: { id }, body: { enabled } });
|
|
598
614
|
}
|
|
615
|
+
function updateAlert(id, patch) {
|
|
616
|
+
return request("PATCH", "/settings/denial-alerts", { query: { id }, body: patch });
|
|
617
|
+
}
|
|
618
|
+
function sendTestAlert(id) {
|
|
619
|
+
return request("POST", "/settings/denial-alerts/send-test", { body: { id } });
|
|
620
|
+
}
|
|
599
621
|
function getWebhooks() {
|
|
600
622
|
return request("GET", "/settings/denial-webhook");
|
|
601
623
|
}
|
|
@@ -3175,8 +3197,20 @@ function stopLogsServerDaemon() {
|
|
|
3175
3197
|
import { Fragment as Fragment5, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
3176
3198
|
var EVENTS = ["denials", "allowed", "all"];
|
|
3177
3199
|
var SPIN2 = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
3178
|
-
var
|
|
3200
|
+
var SIGNALS2 = ["any", "deny", "dlp", "ratelimit"];
|
|
3201
|
+
var SIGNAL_LABEL = { any: "any signal", deny: "denials", dlp: "dlp secrets", ratelimit: "rate-limit" };
|
|
3202
|
+
var WINDOWS = [30, 60, 300, 3600, 86400];
|
|
3203
|
+
var winLabel = (s) => s < 60 ? `${s}s` : s < 3600 ? `${s / 60}m` : s < 86400 ? `${s / 3600}h` : `${s / 86400}d`;
|
|
3204
|
+
var THRESH_MIN = 5;
|
|
3205
|
+
var THRESH_MAX = 100;
|
|
3206
|
+
var nearestWindowIdx = (s) => {
|
|
3207
|
+
let best = 0;
|
|
3208
|
+
for (let i = 1; i < WINDOWS.length; i++) if (Math.abs(WINDOWS[i] - s) < Math.abs(WINDOWS[best] - s)) best = i;
|
|
3209
|
+
return best;
|
|
3210
|
+
};
|
|
3211
|
+
var EDITOR_FIELDS = 4;
|
|
3179
3212
|
var acctLabel = (a) => a.email || a.user || "account \u2026" + a.apiKey.slice(-4);
|
|
3213
|
+
var alertChannel = (r) => r.emails && r.emails.length ? "email" : r.telegram && r.telegram.length ? "telegram" : null;
|
|
3180
3214
|
function SettingsPanel({
|
|
3181
3215
|
active: active2,
|
|
3182
3216
|
focused,
|
|
@@ -3191,6 +3225,7 @@ function SettingsPanel({
|
|
|
3191
3225
|
const [confirmDel, setConfirmDel] = useState7(null);
|
|
3192
3226
|
const [msg, setMsg] = useState7(null);
|
|
3193
3227
|
const [busy, setBusy] = useState7(false);
|
|
3228
|
+
const [editor, setEditor] = useState7(null);
|
|
3194
3229
|
const [accounts, setAccounts] = useState7(() => listAccounts());
|
|
3195
3230
|
const [login, setLogin] = useState7(null);
|
|
3196
3231
|
const [tick, setTick] = useState7(0);
|
|
@@ -3210,22 +3245,30 @@ function SettingsPanel({
|
|
|
3210
3245
|
const localQ = useLoader(() => listAccounts().length ? api.settings.getLocalLogs() : Promise.resolve(null));
|
|
3211
3246
|
const whQ = useLoader(() => listAccounts().length ? api.settings.getWebhooks() : Promise.resolve(null));
|
|
3212
3247
|
const alertQ = useLoader(() => listAccounts().length ? api.settings.getAlerts() : Promise.resolve(null));
|
|
3248
|
+
const guardQ = useLoader(() => listAccounts().length ? api.settings.getGuardStatus() : Promise.resolve(null));
|
|
3249
|
+
const selfQ = useLoader(() => listAccounts().length ? api.settings.getSelfProtection() : Promise.resolve(null));
|
|
3213
3250
|
const local = localQ.data;
|
|
3251
|
+
const guard = guardQ.data;
|
|
3252
|
+
const selfProt = selfQ.data;
|
|
3214
3253
|
const [hidden, setHidden] = useState7(/* @__PURE__ */ new Set());
|
|
3215
3254
|
const webhooks = (whQ.data?.webhooks ?? []).filter((w) => !hidden.has("wh:" + w.id));
|
|
3216
3255
|
const alerts = (alertQ.data?.rules ?? []).filter((r) => !hidden.has("alert:" + r.id));
|
|
3256
|
+
const hasEmailAlert = alerts.some((r) => alertChannel(r) === "email");
|
|
3257
|
+
const hasTgAlert = alerts.some((r) => alertChannel(r) === "telegram");
|
|
3217
3258
|
const rowsAll = [
|
|
3218
3259
|
...accounts.map((acc) => ({ kind: "acct", acc })),
|
|
3219
3260
|
{ kind: "acct-add" },
|
|
3220
3261
|
...locked ? [] : [
|
|
3262
|
+
{ kind: "guard" },
|
|
3263
|
+
{ kind: "self" },
|
|
3221
3264
|
{ kind: "ll-enabled" },
|
|
3222
3265
|
{ kind: "ll-path" },
|
|
3223
3266
|
{ kind: "ll-server" },
|
|
3224
3267
|
...webhooks.map((wh) => ({ kind: "wh", wh })),
|
|
3225
3268
|
{ kind: "wh-add" },
|
|
3226
3269
|
...alerts.map((rule) => ({ kind: "alert", rule })),
|
|
3227
|
-
{ kind: "alert-add-email" },
|
|
3228
|
-
{ kind: "alert-add-tg" }
|
|
3270
|
+
...hasEmailAlert ? [] : [{ kind: "alert-add-email" }],
|
|
3271
|
+
...hasTgAlert ? [] : [{ kind: "alert-add-tg" }]
|
|
3229
3272
|
]
|
|
3230
3273
|
];
|
|
3231
3274
|
const selClamped = Math.min(sel, rowsAll.length - 1);
|
|
@@ -3236,6 +3279,8 @@ function SettingsPanel({
|
|
|
3236
3279
|
localQ.reload();
|
|
3237
3280
|
whQ.reload();
|
|
3238
3281
|
alertQ.reload();
|
|
3282
|
+
guardQ.reload();
|
|
3283
|
+
selfQ.reload();
|
|
3239
3284
|
};
|
|
3240
3285
|
const run = (label, fn, reload) => {
|
|
3241
3286
|
if (busy) return;
|
|
@@ -3280,6 +3325,57 @@ function SettingsPanel({
|
|
|
3280
3325
|
if (!abort.current) setLogin({ phase: "error", msg: "timed out \u2014 press n to retry" });
|
|
3281
3326
|
})();
|
|
3282
3327
|
};
|
|
3328
|
+
const openAlertEditor = (channel, rule) => {
|
|
3329
|
+
setEditor({
|
|
3330
|
+
id: rule?.id,
|
|
3331
|
+
channel,
|
|
3332
|
+
target: (channel === "email" ? rule?.emails?.[0] : rule?.telegram?.[0]) ?? "",
|
|
3333
|
+
signal: rule?.signal ?? "any",
|
|
3334
|
+
threshold: rule?.threshold ?? THRESH_MIN,
|
|
3335
|
+
windowSeconds: rule?.windowSeconds ?? 300,
|
|
3336
|
+
field: rule ? 1 : 0
|
|
3337
|
+
// new rule starts on the target; existing on signal
|
|
3338
|
+
});
|
|
3339
|
+
if (!rule) {
|
|
3340
|
+
setInput("");
|
|
3341
|
+
setEditing("alert-target");
|
|
3342
|
+
}
|
|
3343
|
+
};
|
|
3344
|
+
const normTarget = (channel, raw) => {
|
|
3345
|
+
const v = raw.trim();
|
|
3346
|
+
if (channel === "email") {
|
|
3347
|
+
const email = v.replace(/^mailto:/i, "").replace(/^["'<]+|["'>]+$/g, "").replace(/[.,;:]+$/, "").replace(/\s+/g, "");
|
|
3348
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) return { value: email, error: "that does not look like an e-mail address" };
|
|
3349
|
+
return { value: email };
|
|
3350
|
+
}
|
|
3351
|
+
const tg = v.replace(/^@/, "").replace(/^(chat[\s_-]*)?id[:=\s]*/i, "").replace(/\s+/g, "");
|
|
3352
|
+
if (!/^-?\d{5,}$/.test(tg)) return { value: tg, error: "a telegram chat id is a number \u2014 get yours from @userinfobot" };
|
|
3353
|
+
return { value: tg };
|
|
3354
|
+
};
|
|
3355
|
+
const saveEditor = () => {
|
|
3356
|
+
if (!editor) return;
|
|
3357
|
+
const t = normTarget(editor.channel, editor.target);
|
|
3358
|
+
if (t.error) {
|
|
3359
|
+
setMsg({ text: "\u2717 " + t.error, level: "bad" });
|
|
3360
|
+
return;
|
|
3361
|
+
}
|
|
3362
|
+
const channels = editor.channel === "email" ? { emails: [t.value], telegram: [] } : { telegram: [t.value], emails: [] };
|
|
3363
|
+
const body = { signal: editor.signal, threshold: editor.threshold, windowSeconds: editor.windowSeconds, ...channels };
|
|
3364
|
+
const id = editor.id;
|
|
3365
|
+
setEditor(null);
|
|
3366
|
+
if (id) run("alert updated", () => api.settings.updateAlert(id, body), alertQ.reload);
|
|
3367
|
+
else run(`${editor.channel} alert added`, () => api.settings.createAlert({ name: "dataroom alert", ...body }), alertQ.reload);
|
|
3368
|
+
};
|
|
3369
|
+
const testAlert = (rule) => {
|
|
3370
|
+
run(
|
|
3371
|
+
"alert test sent \u2014 check your " + (alertChannel(rule) ?? "channel"),
|
|
3372
|
+
() => api.settings.sendTestAlert(rule.id).then((r) => {
|
|
3373
|
+
if (!r.delivered.telegram && !r.delivered.email) throw new Error("no channel on this rule");
|
|
3374
|
+
}),
|
|
3375
|
+
() => {
|
|
3376
|
+
}
|
|
3377
|
+
);
|
|
3378
|
+
};
|
|
3283
3379
|
const activate = (r) => {
|
|
3284
3380
|
if (r.kind === "acct") {
|
|
3285
3381
|
setViewCredentials({ apiKey: r.acc.apiKey, apiUrl: r.acc.apiUrl });
|
|
@@ -3287,6 +3383,9 @@ function SettingsPanel({
|
|
|
3287
3383
|
setMsg({ text: `viewing ${acctLabel(r.acc)}`, level: "ok" });
|
|
3288
3384
|
} else if (r.kind === "acct-add") {
|
|
3289
3385
|
beginLogin();
|
|
3386
|
+
} else if (r.kind === "self") {
|
|
3387
|
+
if (!selfProt) return;
|
|
3388
|
+
run(selfProt.enabled ? "self-protection disabled" : "self-protection enabled", () => api.settings.setSelfProtection(!selfProt.enabled), selfQ.reload);
|
|
3290
3389
|
} else if (r.kind === "ll-enabled") {
|
|
3291
3390
|
if (!local) return;
|
|
3292
3391
|
if (!local.enabled && !local.path.trim()) {
|
|
@@ -3309,13 +3408,12 @@ function SettingsPanel({
|
|
|
3309
3408
|
setInput("");
|
|
3310
3409
|
setEditing("wh-url");
|
|
3311
3410
|
} else if (r.kind === "alert") {
|
|
3312
|
-
|
|
3411
|
+
const ch = alertChannel(r.rule);
|
|
3412
|
+
if (ch) openAlertEditor(ch, r.rule);
|
|
3313
3413
|
} else if (r.kind === "alert-add-email") {
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
setInput("");
|
|
3318
|
-
setEditing("alert-tg");
|
|
3414
|
+
openAlertEditor("email");
|
|
3415
|
+
} else if (r.kind === "alert-add-tg") {
|
|
3416
|
+
openAlertEditor("telegram");
|
|
3319
3417
|
}
|
|
3320
3418
|
};
|
|
3321
3419
|
const submitInput = () => {
|
|
@@ -3326,22 +3424,15 @@ function SettingsPanel({
|
|
|
3326
3424
|
const enabled = (local?.enabled ?? false) && v.length > 0;
|
|
3327
3425
|
run(v ? `path saved${enabled ? "" : " (press enter on enabled to turn on)"}` : "path cleared (local logs off)", () => api.settings.setLocalLogs({ enabled, path: v }), localQ.reload);
|
|
3328
3426
|
} else if (which === "wh-url") {
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v)) {
|
|
3334
|
-
setMsg({ text: "\u2717 that does not look like an e-mail address", level: "bad" });
|
|
3427
|
+
const url = v.replace(/^["'<]+|["'>]+$/g, "").trim();
|
|
3428
|
+
if (!url) return;
|
|
3429
|
+
if (!/^https?:\/\/\S+$/i.test(url)) {
|
|
3430
|
+
setMsg({ text: "\u2717 webhook url must start with http:// or https://", level: "bad" });
|
|
3335
3431
|
return;
|
|
3336
3432
|
}
|
|
3337
|
-
run("
|
|
3338
|
-
} else if (which === "alert-
|
|
3339
|
-
|
|
3340
|
-
if (!/^-?\d{5,}$/.test(v)) {
|
|
3341
|
-
setMsg({ text: "\u2717 a telegram chat id is a number \u2014 get yours from @userinfobot", level: "bad" });
|
|
3342
|
-
return;
|
|
3343
|
-
}
|
|
3344
|
-
run("telegram alert added", () => api.settings.createAlert({ ...ALERT_BASE, telegram: [v] }), alertQ.reload);
|
|
3433
|
+
run("webhook added", () => api.settings.createWebhook({ url, events: "denials" }), whQ.reload);
|
|
3434
|
+
} else if (which === "alert-target" && editor) {
|
|
3435
|
+
setEditor({ ...editor, target: v, field: 1 });
|
|
3345
3436
|
}
|
|
3346
3437
|
};
|
|
3347
3438
|
useInput6(
|
|
@@ -3353,6 +3444,35 @@ function SettingsPanel({
|
|
|
3353
3444
|
}
|
|
3354
3445
|
return;
|
|
3355
3446
|
}
|
|
3447
|
+
if (editor) {
|
|
3448
|
+
if (key.escape) {
|
|
3449
|
+
setEditor(null);
|
|
3450
|
+
return;
|
|
3451
|
+
}
|
|
3452
|
+
if (inp === "e" || key.return && editor.field === 0) {
|
|
3453
|
+
setInput(editor.target);
|
|
3454
|
+
setEditing("alert-target");
|
|
3455
|
+
return;
|
|
3456
|
+
}
|
|
3457
|
+
if (inp === "t" && editor.id) {
|
|
3458
|
+
const rule = alerts.find((a) => a.id === editor.id);
|
|
3459
|
+
if (rule) testAlert(rule);
|
|
3460
|
+
return;
|
|
3461
|
+
}
|
|
3462
|
+
if (key.return) {
|
|
3463
|
+
saveEditor();
|
|
3464
|
+
return;
|
|
3465
|
+
}
|
|
3466
|
+
if (key.upArrow) setEditor({ ...editor, field: (editor.field + EDITOR_FIELDS - 1) % EDITOR_FIELDS });
|
|
3467
|
+
else if (key.downArrow) setEditor({ ...editor, field: (editor.field + 1) % EDITOR_FIELDS });
|
|
3468
|
+
else if (key.leftArrow || key.rightArrow) {
|
|
3469
|
+
const d = key.rightArrow ? 1 : -1;
|
|
3470
|
+
if (editor.field === 1) setEditor({ ...editor, signal: SIGNALS2[(SIGNALS2.indexOf(editor.signal) + d + SIGNALS2.length) % SIGNALS2.length] });
|
|
3471
|
+
else if (editor.field === 2) setEditor({ ...editor, threshold: Math.max(THRESH_MIN, Math.min(THRESH_MAX, editor.threshold + d * 5)) });
|
|
3472
|
+
else if (editor.field === 3) setEditor({ ...editor, windowSeconds: WINDOWS[Math.max(0, Math.min(WINDOWS.length - 1, nearestWindowIdx(editor.windowSeconds) + d))] });
|
|
3473
|
+
}
|
|
3474
|
+
return;
|
|
3475
|
+
}
|
|
3356
3476
|
setMsg(null);
|
|
3357
3477
|
if (key.upArrow) {
|
|
3358
3478
|
setSel((n) => Math.max(0, n - 1));
|
|
@@ -3366,28 +3486,41 @@ function SettingsPanel({
|
|
|
3366
3486
|
const ok = setActiveAccount({ apiKey: cur.acc.apiKey, apiUrl: cur.acc.apiUrl });
|
|
3367
3487
|
setMsg(ok ? { text: `\u2713 ${acctLabel(cur.acc)} is now the ACTIVE key (guard + logging)`, level: "ok" } : { text: "\u2717 could not set active", level: "bad" });
|
|
3368
3488
|
refreshAccounts();
|
|
3369
|
-
} else if (cur.kind === "wh" || cur.kind === "alert" || cur.kind === "ll-enabled" || cur.kind === "ll-server")
|
|
3489
|
+
} else if (cur.kind === "wh" || cur.kind === "alert" || cur.kind === "self" || cur.kind === "ll-enabled" || cur.kind === "ll-server") {
|
|
3490
|
+
if (cur.kind === "alert") run(cur.rule.enabled ? "alert disabled" : "alert enabled", () => api.settings.setAlertEnabled(cur.rule.id, !cur.rule.enabled), alertQ.reload);
|
|
3491
|
+
else activate(cur);
|
|
3492
|
+
}
|
|
3493
|
+
} else if (inp === "x" && cur.kind === "acct") {
|
|
3494
|
+
const k = "acct:" + cur.acc.apiKey;
|
|
3495
|
+
if (confirmDel !== k) {
|
|
3496
|
+
setConfirmDel(k);
|
|
3497
|
+
setMsg({ text: `press x again to remove ${acctLabel(cur.acc)} from this device (the cloud account is untouched)`, level: "bad" });
|
|
3498
|
+
return;
|
|
3499
|
+
}
|
|
3500
|
+
setConfirmDel(null);
|
|
3501
|
+
removeAccount(cur.acc.apiKey);
|
|
3502
|
+
setMsg({ text: `\u2713 removed ${acctLabel(cur.acc)} from this device`, level: "ok" });
|
|
3503
|
+
refreshAccounts();
|
|
3370
3504
|
} else if (inp === "t" && cur.kind === "wh") {
|
|
3371
|
-
run(
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
);
|
|
3378
|
-
} else if (inp === "n") beginLogin();
|
|
3505
|
+
run("webhook test sent \u2014 check your endpoint", () => api.settings.sendTestWebhook(cur.wh.id).then((r) => {
|
|
3506
|
+
if (!r.delivered) throw new Error("endpoint rejected the test (non-2xx)");
|
|
3507
|
+
}), whQ.reload);
|
|
3508
|
+
} else if (inp === "t" && cur.kind === "alert") {
|
|
3509
|
+
testAlert(cur.rule);
|
|
3510
|
+
} else if (inp === "e" && cur.kind === "ll-path") activate(cur);
|
|
3379
3511
|
else if (inp === "e" && cur.kind === "wh") {
|
|
3380
3512
|
const next = EVENTS[(EVENTS.indexOf(cur.wh.events) + 1) % EVENTS.length];
|
|
3381
3513
|
run(`webhook events \u2192 ${next}`, () => api.settings.updateWebhook(cur.wh.id, { events: next }), whQ.reload);
|
|
3382
|
-
} else if (inp === "e" && cur.kind === "
|
|
3514
|
+
} else if (inp === "e" && cur.kind === "alert") {
|
|
3515
|
+
const ch = alertChannel(cur.rule);
|
|
3516
|
+
if (ch) openAlertEditor(ch, cur.rule);
|
|
3517
|
+
} else if (inp === "n") beginLogin();
|
|
3383
3518
|
else if (inp === "a") {
|
|
3384
3519
|
if (cur.kind === "wh" || cur.kind === "wh-add") {
|
|
3385
3520
|
setInput("");
|
|
3386
3521
|
setEditing("wh-url");
|
|
3387
|
-
} else if (cur.kind === "alert
|
|
3388
|
-
|
|
3389
|
-
setEditing("alert-email");
|
|
3390
|
-
}
|
|
3522
|
+
} else if (cur.kind === "alert-add-email") openAlertEditor("email");
|
|
3523
|
+
else if (cur.kind === "alert-add-tg") openAlertEditor("telegram");
|
|
3391
3524
|
} else if (inp === "d" && (cur.kind === "wh" || cur.kind === "alert")) {
|
|
3392
3525
|
const k = keyOf(cur);
|
|
3393
3526
|
if (confirmDel !== k) {
|
|
@@ -3427,30 +3560,70 @@ function SettingsPanel({
|
|
|
3427
3560
|
] })
|
|
3428
3561
|
] });
|
|
3429
3562
|
}
|
|
3430
|
-
|
|
3431
|
-
|
|
3563
|
+
if (editor) {
|
|
3564
|
+
const fieldRow = (idx, label, value, hint) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
3565
|
+
/* @__PURE__ */ jsx7(Text7, { color: editor.field === idx ? theme.accentBright : theme.dim, children: editor.field === idx ? "\u25B8 " : " " }),
|
|
3566
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: label.padEnd(11) }),
|
|
3567
|
+
value,
|
|
3568
|
+
editor.field === idx && hint ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " " + hint }) : null
|
|
3569
|
+
] });
|
|
3570
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
3571
|
+
/* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
|
|
3572
|
+
editor.id ? "Edit" : "New",
|
|
3573
|
+
" ",
|
|
3574
|
+
editor.channel,
|
|
3575
|
+
" alert"
|
|
3576
|
+
] }),
|
|
3577
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
3578
|
+
"\u2191\u2193 field \xB7 \u2190\u2192 change \xB7 e edit target \xB7 enter save",
|
|
3579
|
+
editor.id ? " \xB7 t test" : "",
|
|
3580
|
+
" \xB7 esc cancel"
|
|
3581
|
+
] }),
|
|
3582
|
+
editing === "alert-target" ? /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, children: [
|
|
3583
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: editor.channel === "email" ? "e-mail address: " : "telegram chat id (from @userinfobot): " }),
|
|
3584
|
+
/* @__PURE__ */ jsx7(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
|
|
3585
|
+
] }) : msg ? /* @__PURE__ */ jsx7(Text7, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate(msg.text, cols) }) : /* @__PURE__ */ jsx7(Text7, { children: " " }),
|
|
3586
|
+
/* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
3587
|
+
fieldRow(0, "target", editor.target ? /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate(editor.target, cols - 16) }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "empty \u2014 press e" }), "press e to edit"),
|
|
3588
|
+
fieldRow(1, "signal", /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: SIGNAL_LABEL[editor.signal] }), "\u2190\u2192 change"),
|
|
3589
|
+
fieldRow(2, "threshold", /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: `\u2265 ${editor.threshold}` }), "\u2190\u2192 \xB15"),
|
|
3590
|
+
fieldRow(3, "window", /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: winLabel(editor.windowSeconds) }), "\u2190\u2192 change")
|
|
3591
|
+
] }),
|
|
3592
|
+
/* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
3593
|
+
"fires when ",
|
|
3594
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: SIGNAL_LABEL[editor.signal] }),
|
|
3595
|
+
" reach ",
|
|
3596
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: editor.threshold }),
|
|
3597
|
+
" within",
|
|
3598
|
+
" ",
|
|
3599
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: winLabel(editor.windowSeconds) })
|
|
3600
|
+
] }) })
|
|
3601
|
+
] });
|
|
3602
|
+
}
|
|
3603
|
+
const loading = !locked && (localQ.loading && !localQ.data || whQ.loading && !whQ.data || alertQ.loading && !alertQ.data || guardQ.loading && !guardQ.data || selfQ.loading && !selfQ.data);
|
|
3604
|
+
const error = locked ? null : localQ.error ?? whQ.error ?? alertQ.error ?? guardQ.error ?? selfQ.error;
|
|
3432
3605
|
const mark = (r) => keyOf(r) === keyOf(cur) && focused ? "\u25B8 " : " ";
|
|
3433
3606
|
const onOff = (on) => on ? /* @__PURE__ */ jsx7(Text7, { color: theme.ok, children: "on " }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "off" });
|
|
3434
3607
|
const chanOf = (rule) => [...rule.emails ?? [], ...(rule.telegram ?? []).map((t) => "tg " + t)].join(", ") || "\u2014";
|
|
3435
|
-
const listBudget = Math.max(4, rows -
|
|
3608
|
+
const listBudget = Math.max(4, rows - 12);
|
|
3436
3609
|
const start = Math.min(Math.max(0, selClamped - Math.floor(listBudget / 2)), Math.max(0, rowsAll.length - listBudget));
|
|
3437
3610
|
const inWindow = (r) => {
|
|
3438
3611
|
const i = rowsAll.findIndex((x) => keyOf(x) === keyOf(r));
|
|
3439
3612
|
return i >= start && i < start + listBudget;
|
|
3440
3613
|
};
|
|
3441
3614
|
return /* @__PURE__ */ jsx7(DataView, { loading, error, children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
3442
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: focused ? "\u2191\u2193 move \xB7 enter
|
|
3615
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: focused ? "\u2191\u2193 move \xB7 enter act \xB7 m toggle \xB7 t test \xB7 e edit \xB7 x remove acct \xB7 d delete \xB7 n add acct \xB7 r refresh" : "press \u2192 to configure" }),
|
|
3443
3616
|
editing ? /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
3444
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: editing === "path" ? "local log path: " :
|
|
3617
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: editing === "path" ? "local log path: " : "webhook url: " }),
|
|
3445
3618
|
/* @__PURE__ */ jsx7(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
|
|
3446
3619
|
] }) : msg ? /* @__PURE__ */ jsx7(Text7, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate(msg.text, cols) }) : login?.msg ? /* @__PURE__ */ jsx7(Text7, { color: login.phase === "error" ? theme.bad : theme.ok, children: login.msg }) : /* @__PURE__ */ jsx7(Text7, { children: " " }),
|
|
3447
3620
|
/* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
3448
3621
|
/* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
|
|
3449
3622
|
"ACCOUNTS ",
|
|
3450
3623
|
/* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
3451
|
-
"\u2014
|
|
3624
|
+
"\u2014 on this device (",
|
|
3452
3625
|
accounts.length,
|
|
3453
|
-
") \xB7 \u25CF viewing
|
|
3626
|
+
") \xB7 \u25CF viewing \xB7 ACTIVE = key the guard uses \xB7 x removes"
|
|
3454
3627
|
] })
|
|
3455
3628
|
] }),
|
|
3456
3629
|
accounts.length === 0 ? /* @__PURE__ */ jsxs7(Text7, { children: [
|
|
@@ -3466,7 +3639,7 @@ function SettingsPanel({
|
|
|
3466
3639
|
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate(acctLabel(a), 30).padEnd(31) }),
|
|
3467
3640
|
isView ? /* @__PURE__ */ jsx7(Text7, { color: theme.ok, children: "\u25CF viewing " }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " " }),
|
|
3468
3641
|
isActive ? /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: "ACTIVE " }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " " }),
|
|
3469
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: a.project ? truncate(a.project,
|
|
3642
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: a.project ? truncate(a.project, 20) : "" })
|
|
3470
3643
|
] }, a.apiKey);
|
|
3471
3644
|
}),
|
|
3472
3645
|
inWindow({ kind: "acct-add" }) ? /* @__PURE__ */ jsxs7(Text7, { children: [
|
|
@@ -3475,28 +3648,49 @@ function SettingsPanel({
|
|
|
3475
3648
|
] }) : null
|
|
3476
3649
|
] }),
|
|
3477
3650
|
locked ? null : /* @__PURE__ */ jsxs7(Fragment5, { children: [
|
|
3651
|
+
inWindow({ kind: "guard" }) || inWindow({ kind: "self" }) ? /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
3652
|
+
/* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
|
|
3653
|
+
"PROTECTION ",
|
|
3654
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2014 the guard hook enforcing every layer on this project" })
|
|
3655
|
+
] }),
|
|
3656
|
+
inWindow({ kind: "guard" }) ? /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
3657
|
+
/* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "guard" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "guard" }) }),
|
|
3658
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "guard".padEnd(11) }),
|
|
3659
|
+
guard ? /* @__PURE__ */ jsxs7(Fragment5, { children: [
|
|
3660
|
+
/* @__PURE__ */ jsx7(Text7, { color: guard.up_to_date ? theme.ok : theme.warn, children: `v${guard.installed ?? "?"}` }),
|
|
3661
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: guard.up_to_date ? " (latest)" : ` \u2192 v${guard.latest} available` }),
|
|
3662
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: ` \xB7 ${guard.device_count} device${guard.device_count === 1 ? "" : "s"}${guard.outdated_count ? ` \xB7 ${guard.outdated_count} outdated` : ""}` })
|
|
3663
|
+
] }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "loading\u2026" })
|
|
3664
|
+
] }) : null,
|
|
3665
|
+
inWindow({ kind: "self" }) ? /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
3666
|
+
/* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "self" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "self" }) }),
|
|
3667
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "self-prot".padEnd(11) }),
|
|
3668
|
+
selfProt ? onOff(selfProt.enabled) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2026" }),
|
|
3669
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " blocks agents from editing SolonGate\u2019s own hooks/config \xB7 enter toggles" })
|
|
3670
|
+
] }) : null
|
|
3671
|
+
] }) : null,
|
|
3478
3672
|
inWindow({ kind: "ll-enabled" }) || inWindow({ kind: "ll-path" }) || inWindow({ kind: "ll-server" }) ? /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
3479
3673
|
/* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
|
|
3480
3674
|
"LOCAL LOGS ",
|
|
3481
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2014 hooks mirror every decision to a file on
|
|
3675
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2014 hooks mirror every decision to a file on this machine" })
|
|
3482
3676
|
] }),
|
|
3483
|
-
/* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
3677
|
+
inWindow({ kind: "ll-enabled" }) ? /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
3484
3678
|
/* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "ll-enabled" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "ll-enabled" }) }),
|
|
3485
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "enabled ".padEnd(
|
|
3679
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "enabled ".padEnd(11) }),
|
|
3486
3680
|
onOff(!!local?.enabled),
|
|
3487
3681
|
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " enter toggles" })
|
|
3488
|
-
] }),
|
|
3489
|
-
/* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
3682
|
+
] }) : null,
|
|
3683
|
+
inWindow({ kind: "ll-path" }) ? /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
3490
3684
|
/* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "ll-path" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "ll-path" }) }),
|
|
3491
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "path ".padEnd(
|
|
3685
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "path ".padEnd(11) }),
|
|
3492
3686
|
local?.path ? /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate(local.path, cols - 14) }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "not set \u2014 enter to edit" })
|
|
3493
|
-
] }),
|
|
3494
|
-
/* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
3687
|
+
] }) : null,
|
|
3688
|
+
inWindow({ kind: "ll-server" }) ? /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
3495
3689
|
/* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "ll-server" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "ll-server" }) }),
|
|
3496
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "dashboard".padEnd(
|
|
3690
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "dashboard".padEnd(11) }),
|
|
3497
3691
|
srv.running ? /* @__PURE__ */ jsx7(Text7, { color: theme.ok, children: `running 127.0.0.1:${srv.port}` }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "stopped" }),
|
|
3498
3692
|
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: srv.running ? " serves local logs to the dashboard \xB7 survives closing the dataroom \xB7 enter stops" : " enter starts the local-logs link for the dashboard" })
|
|
3499
|
-
] })
|
|
3693
|
+
] }) : null
|
|
3500
3694
|
] }) : null,
|
|
3501
3695
|
/* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
3502
3696
|
/* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
|
|
@@ -3504,7 +3698,7 @@ function SettingsPanel({
|
|
|
3504
3698
|
/* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
3505
3699
|
"\u2014 POST every matching event to your endpoint (",
|
|
3506
3700
|
webhooks.length,
|
|
3507
|
-
")"
|
|
3701
|
+
") \xB7 t tests"
|
|
3508
3702
|
] })
|
|
3509
3703
|
] }),
|
|
3510
3704
|
webhooks.filter((wh) => inWindow({ kind: "wh", wh })).map((wh) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
@@ -3522,17 +3716,17 @@ function SettingsPanel({
|
|
|
3522
3716
|
/* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
|
|
3523
3717
|
"ALERTS ",
|
|
3524
3718
|
/* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
3525
|
-
"\u2014
|
|
3719
|
+
"\u2014 spike alerts to one email + one telegram (",
|
|
3526
3720
|
alerts.length,
|
|
3527
|
-
")"
|
|
3721
|
+
") \xB7 enter edits \xB7 t tests"
|
|
3528
3722
|
] })
|
|
3529
3723
|
] }),
|
|
3530
3724
|
alerts.filter((rule) => inWindow({ kind: "alert", rule })).map((rule) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
3531
3725
|
/* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "alert:" + rule.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert", rule }) }),
|
|
3532
3726
|
onOff(rule.enabled),
|
|
3533
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: (" " + rule.signal).padEnd(
|
|
3534
|
-
/* @__PURE__ */ jsx7(Text7, { children: `\u2265${rule.threshold}/${rule.windowSeconds}
|
|
3535
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate(chanOf(rule), cols -
|
|
3727
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: (" " + SIGNAL_LABEL[rule.signal]).padEnd(13) }),
|
|
3728
|
+
/* @__PURE__ */ jsx7(Text7, { children: `\u2265${rule.threshold}/${winLabel(rule.windowSeconds)} `.padEnd(11) }),
|
|
3729
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate(chanOf(rule), cols - 34) })
|
|
3536
3730
|
] }, rule.id)),
|
|
3537
3731
|
inWindow({ kind: "alert-add-email" }) ? /* @__PURE__ */ jsxs7(Text7, { children: [
|
|
3538
3732
|
/* @__PURE__ */ jsx7(Text7, { color: cur.kind === "alert-add-email" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert-add-email" }) }),
|
|
@@ -3540,8 +3734,9 @@ function SettingsPanel({
|
|
|
3540
3734
|
] }) : null,
|
|
3541
3735
|
inWindow({ kind: "alert-add-tg" }) ? /* @__PURE__ */ jsxs7(Text7, { children: [
|
|
3542
3736
|
/* @__PURE__ */ jsx7(Text7, { color: cur.kind === "alert-add-tg" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert-add-tg" }) }),
|
|
3543
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "+ add telegram alert (enter \u2014 numeric chat id
|
|
3544
|
-
] }) : null
|
|
3737
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "+ add telegram alert (enter \u2014 numeric chat id from @userinfobot)" })
|
|
3738
|
+
] }) : null,
|
|
3739
|
+
hasEmailAlert && hasTgAlert ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " both channels in use \u2014 remove one (d) to change it" }) : null
|
|
3545
3740
|
] })
|
|
3546
3741
|
] })
|
|
3547
3742
|
] }) });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.81.
|
|
3
|
+
"version": "0.81.56",
|
|
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": {
|