@solongate/proxy 0.81.59 → 0.81.61

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.
@@ -70,12 +70,6 @@ export declare function updateAlert(id: string, patch: {
70
70
  }): Promise<{
71
71
  rule: AlertRule;
72
72
  }>;
73
- export declare function sendTestAlert(id: string): Promise<{
74
- delivered: {
75
- telegram: boolean;
76
- email: boolean;
77
- };
78
- }>;
79
73
  export interface DenialWebhook {
80
74
  id: string;
81
75
  url: string;
@@ -221,7 +221,6 @@ __export(settings_exports, {
221
221
  getSecurityLayers: () => getSecurityLayers,
222
222
  getSelfProtection: () => getSelfProtection,
223
223
  getWebhooks: () => getWebhooks,
224
- sendTestAlert: () => sendTestAlert,
225
224
  sendTestWebhook: () => sendTestWebhook,
226
225
  setAlertEnabled: () => setAlertEnabled,
227
226
  setLocalLogs: () => setLocalLogs,
@@ -272,9 +271,6 @@ function setAlertEnabled(id, enabled) {
272
271
  function updateAlert(id, patch) {
273
272
  return request("PATCH", "/settings/denial-alerts", { query: { id }, body: patch });
274
273
  }
275
- function sendTestAlert(id) {
276
- return request("POST", "/settings/denial-alerts/send-test", { body: { id } });
277
- }
278
274
  function getWebhooks() {
279
275
  return request("GET", "/settings/denial-webhook");
280
276
  }
package/dist/index.js CHANGED
@@ -7408,7 +7408,6 @@ __export(settings_exports, {
7408
7408
  getSecurityLayers: () => getSecurityLayers,
7409
7409
  getSelfProtection: () => getSelfProtection,
7410
7410
  getWebhooks: () => getWebhooks,
7411
- sendTestAlert: () => sendTestAlert,
7412
7411
  sendTestWebhook: () => sendTestWebhook,
7413
7412
  setAlertEnabled: () => setAlertEnabled,
7414
7413
  setLocalLogs: () => setLocalLogs,
@@ -7459,9 +7458,6 @@ function setAlertEnabled(id, enabled) {
7459
7458
  function updateAlert(id, patch) {
7460
7459
  return request("PATCH", "/settings/denial-alerts", { query: { id }, body: patch });
7461
7460
  }
7462
- function sendTestAlert(id) {
7463
- return request("POST", "/settings/denial-alerts/send-test", { body: { id } });
7464
- }
7465
7461
  function getWebhooks() {
7466
7462
  return request("GET", "/settings/denial-webhook");
7467
7463
  }
@@ -10239,16 +10235,6 @@ function SettingsPanel({
10239
10235
  if (id) run12("alert updated", () => api.settings.updateAlert(id, body), alertQ.reload);
10240
10236
  else run12(`${editor.channel} alert added`, () => api.settings.createAlert({ name: "dataroom alert", ...body }), alertQ.reload);
10241
10237
  };
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
- };
10252
10238
  const activate = (r) => {
10253
10239
  if (r.kind === "acct") {
10254
10240
  setViewCredentials({ apiKey: r.acc.apiKey, apiUrl: r.acc.apiUrl });
@@ -10327,11 +10313,6 @@ function SettingsPanel({
10327
10313
  setEditing("alert-target");
10328
10314
  return;
10329
10315
  }
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
10316
  if (key.return) {
10336
10317
  saveEditor();
10337
10318
  return;
@@ -10378,8 +10359,6 @@ function SettingsPanel({
10378
10359
  run12("webhook test sent \u2014 check your endpoint", () => api.settings.sendTestWebhook(cur.wh.id).then((r) => {
10379
10360
  if (!r.delivered) throw new Error("endpoint rejected the test (non-2xx)");
10380
10361
  }), whQ.reload);
10381
- } else if (inp === "t" && cur.kind === "alert") {
10382
- testAlert(cur.rule);
10383
10362
  } else if (inp === "e" && cur.kind === "ll-path") activate(cur);
10384
10363
  else if (inp === "e" && cur.kind === "wh") {
10385
10364
  const next = EVENTS[(EVENTS.indexOf(cur.wh.events) + 1) % EVENTS.length];
@@ -10447,11 +10426,7 @@ function SettingsPanel({
10447
10426
  editor.channel,
10448
10427
  " alert"
10449
10428
  ] }),
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
- ] }),
10429
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2191\u2193 field \xB7 \u2190\u2192 change \xB7 e edit target \xB7 enter save \xB7 esc cancel" }),
10455
10430
  editing === "alert-target" ? /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, children: [
10456
10431
  /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: editor.channel === "email" ? "e-mail address: " : "telegram chat id (from @userinfobot): " }),
10457
10432
  /* @__PURE__ */ jsx7(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
@@ -10573,7 +10548,7 @@ function SettingsPanel({
10573
10548
  PROTECTION: "guard hook version + self-protection toggle",
10574
10549
  "LOCAL LOGS": "mirror every decision to a file + dashboard link",
10575
10550
  WEBHOOKS: `POST events to a URL (${webhooks.length}) \xB7 t tests`,
10576
- ALERTS: `one email + one telegram (${alerts.length}) \xB7 enter edits \xB7 t tests`
10551
+ ALERTS: `one email + one telegram (${alerts.length}) \xB7 enter edits`
10577
10552
  };
10578
10553
  const lineEls = [];
10579
10554
  const lineKey = [];
@@ -10595,7 +10570,7 @@ function SettingsPanel({
10595
10570
  lineKey.push("");
10596
10571
  if ((sec === "WEBHOOKS" || sec === "ALERTS") && local?.enabled) {
10597
10572
  lineEls.push(
10598
- /* @__PURE__ */ jsx7(Text7, { wrap: "truncate", color: theme.warn, children: ` \u26A0 local logs on \u2014 real denials stay on this machine, so ${sec.toLowerCase()} do NOT fire (t test still works)` }, "warn:" + sec)
10573
+ /* @__PURE__ */ jsx7(Text7, { wrap: "truncate", color: theme.warn, children: ` \u26A0 local logs on \u2014 real denials stay on this machine, so ${sec.toLowerCase()} do NOT fire${sec === "WEBHOOKS" ? " (t test still works)" : ""}` }, "warn:" + sec)
10599
10574
  );
10600
10575
  lineKey.push("");
10601
10576
  }
package/dist/tui/index.js CHANGED
@@ -564,7 +564,6 @@ __export(settings_exports, {
564
564
  getSecurityLayers: () => getSecurityLayers,
565
565
  getSelfProtection: () => getSelfProtection,
566
566
  getWebhooks: () => getWebhooks,
567
- sendTestAlert: () => sendTestAlert,
568
567
  sendTestWebhook: () => sendTestWebhook,
569
568
  setAlertEnabled: () => setAlertEnabled,
570
569
  setLocalLogs: () => setLocalLogs,
@@ -615,9 +614,6 @@ function setAlertEnabled(id, enabled) {
615
614
  function updateAlert(id, patch) {
616
615
  return request("PATCH", "/settings/denial-alerts", { query: { id }, body: patch });
617
616
  }
618
- function sendTestAlert(id) {
619
- return request("POST", "/settings/denial-alerts/send-test", { body: { id } });
620
- }
621
617
  function getWebhooks() {
622
618
  return request("GET", "/settings/denial-webhook");
623
619
  }
@@ -3366,16 +3362,6 @@ function SettingsPanel({
3366
3362
  if (id) run("alert updated", () => api.settings.updateAlert(id, body), alertQ.reload);
3367
3363
  else run(`${editor.channel} alert added`, () => api.settings.createAlert({ name: "dataroom alert", ...body }), alertQ.reload);
3368
3364
  };
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
- };
3379
3365
  const activate = (r) => {
3380
3366
  if (r.kind === "acct") {
3381
3367
  setViewCredentials({ apiKey: r.acc.apiKey, apiUrl: r.acc.apiUrl });
@@ -3454,11 +3440,6 @@ function SettingsPanel({
3454
3440
  setEditing("alert-target");
3455
3441
  return;
3456
3442
  }
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
3443
  if (key.return) {
3463
3444
  saveEditor();
3464
3445
  return;
@@ -3505,8 +3486,6 @@ function SettingsPanel({
3505
3486
  run("webhook test sent \u2014 check your endpoint", () => api.settings.sendTestWebhook(cur.wh.id).then((r) => {
3506
3487
  if (!r.delivered) throw new Error("endpoint rejected the test (non-2xx)");
3507
3488
  }), whQ.reload);
3508
- } else if (inp === "t" && cur.kind === "alert") {
3509
- testAlert(cur.rule);
3510
3489
  } else if (inp === "e" && cur.kind === "ll-path") activate(cur);
3511
3490
  else if (inp === "e" && cur.kind === "wh") {
3512
3491
  const next = EVENTS[(EVENTS.indexOf(cur.wh.events) + 1) % EVENTS.length];
@@ -3574,11 +3553,7 @@ function SettingsPanel({
3574
3553
  editor.channel,
3575
3554
  " alert"
3576
3555
  ] }),
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
- ] }),
3556
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2191\u2193 field \xB7 \u2190\u2192 change \xB7 e edit target \xB7 enter save \xB7 esc cancel" }),
3582
3557
  editing === "alert-target" ? /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, children: [
3583
3558
  /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: editor.channel === "email" ? "e-mail address: " : "telegram chat id (from @userinfobot): " }),
3584
3559
  /* @__PURE__ */ jsx7(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
@@ -3700,7 +3675,7 @@ function SettingsPanel({
3700
3675
  PROTECTION: "guard hook version + self-protection toggle",
3701
3676
  "LOCAL LOGS": "mirror every decision to a file + dashboard link",
3702
3677
  WEBHOOKS: `POST events to a URL (${webhooks.length}) \xB7 t tests`,
3703
- ALERTS: `one email + one telegram (${alerts.length}) \xB7 enter edits \xB7 t tests`
3678
+ ALERTS: `one email + one telegram (${alerts.length}) \xB7 enter edits`
3704
3679
  };
3705
3680
  const lineEls = [];
3706
3681
  const lineKey = [];
@@ -3722,7 +3697,7 @@ function SettingsPanel({
3722
3697
  lineKey.push("");
3723
3698
  if ((sec === "WEBHOOKS" || sec === "ALERTS") && local?.enabled) {
3724
3699
  lineEls.push(
3725
- /* @__PURE__ */ jsx7(Text7, { wrap: "truncate", color: theme.warn, children: ` \u26A0 local logs on \u2014 real denials stay on this machine, so ${sec.toLowerCase()} do NOT fire (t test still works)` }, "warn:" + sec)
3700
+ /* @__PURE__ */ jsx7(Text7, { wrap: "truncate", color: theme.warn, children: ` \u26A0 local logs on \u2014 real denials stay on this machine, so ${sec.toLowerCase()} do NOT fire${sec === "WEBHOOKS" ? " (t test still works)" : ""}` }, "warn:" + sec)
3726
3701
  );
3727
3702
  lineKey.push("");
3728
3703
  }
@@ -15,3 +15,15 @@
15
15
  {"ms":2,"ts":1784326365762,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
16
16
  {"ms":3,"ts":1784326398280,"tool":"Edit","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
17
17
  {"ms":1,"ts":1784326403539,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
18
+ {"ms":2,"ts":1784359530309,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
19
+ {"ms":4,"ts":1784359565744,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
20
+ {"ms":3,"ts":1784359892056,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
21
+ {"ms":2,"ts":1784359899408,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
22
+ {"ms":3,"ts":1784359942766,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
23
+ {"ms":3,"ts":1784359948446,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
24
+ {"ms":5,"ts":1784360088630,"tool":"Write","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
25
+ {"ms":2,"ts":1784360093227,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
26
+ {"ms":4,"ts":1784360125661,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
27
+ {"ms":2,"ts":1784360134508,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
28
+ {"ms":5,"ts":1784360234461,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
29
+ {"ms":4,"ts":1784360256656,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
@@ -1 +1 @@
1
- {"tool":"Bash","ts":1784138162061}
1
+ {"tool":"Bash","ts":1784360236505}
@@ -1 +1 @@
1
- {"ms":1,"ts":1784326403539,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
1
+ {"ms":4,"ts":1784360256656,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
@@ -1 +1 @@
1
- 1784326398329
1
+ 1784360236504
@@ -6536,10 +6536,26 @@ import { resolve, join, dirname, isAbsolute } from "node:path";
6536
6536
  import { homedir } from "node:os";
6537
6537
  import { gunzipSync } from "node:zlib";
6538
6538
  import { createHash } from "node:crypto";
6539
- var HOOK_VERSION = 34;
6539
+ var HOOK_VERSION = 35;
6540
6540
  function localLogsOnly(security) {
6541
- const l = security && security.localLogs;
6542
- return !!(l && l.enabled && typeof l.path === "string" && l.path.trim());
6541
+ if (security && typeof security === "object") {
6542
+ const l = security.localLogs;
6543
+ return !!(l && l.enabled && typeof l.path === "string" && l.path.trim());
6544
+ }
6545
+ try {
6546
+ const m = JSON.parse(readFileSync(join(resolve(homedir(), ".solongate"), ".local-logs-mode.json"), "utf-8"));
6547
+ return !!(m && m.localOnly);
6548
+ } catch {
6549
+ return false;
6550
+ }
6551
+ }
6552
+ function writeLocalMarker(security) {
6553
+ try {
6554
+ const l = security && security.localLogs;
6555
+ const localOnly = !!(l && l.enabled && typeof l.path === "string" && l.path.trim());
6556
+ writeFileSync(join(resolve(homedir(), ".solongate"), ".local-logs-mode.json"), JSON.stringify({ localOnly, ts: Date.now() }));
6557
+ } catch {
6558
+ }
6543
6559
  }
6544
6560
  function resolveLocalLogDir(rawPath) {
6545
6561
  const dir = String(rawPath || "").trim().replace(/[\\/]+$/, "");
@@ -6753,6 +6769,7 @@ async function refreshPolicyCache() {
6753
6769
  writeFileSync(cacheFile, JSON.stringify({ _ts: Date.now(), policy, selfProtect, security, hookVersions }));
6754
6770
  } catch {
6755
6771
  }
6772
+ writeLocalMarker(security);
6756
6773
  } catch {
6757
6774
  }
6758
6775
  }
@@ -7751,8 +7768,10 @@ if (!REFRESH_MODE) {
7751
7768
  dashboardPolicy = cached.policy;
7752
7769
  if (typeof cached.selfProtect === "boolean")
7753
7770
  selfProtectEnabled = cached.selfProtect;
7754
- if (cached.security !== void 0)
7771
+ if (cached.security !== void 0) {
7755
7772
  securityCfg = cached.security;
7773
+ writeLocalMarker(securityCfg);
7774
+ }
7756
7775
  if (cached.hookVersions)
7757
7776
  CLOUD_HOOK_VERSIONS = cached.hookVersions;
7758
7777
  }
package/hooks/guard.mjs CHANGED
@@ -32,13 +32,36 @@ import { createHash } from 'node:crypto';
32
32
  // the installed hook self-updates when the cloud version is higher (see
33
33
  // maybeSelfUpdate). This is what makes guard fixes propagate without a manual
34
34
  // reinstall — the same trust model as the OPA WASM this hook already runs.
35
- const HOOK_VERSION = 34;
35
+ const HOOK_VERSION = 35;
36
36
 
37
37
  // True when local log storage is ON. In that mode logs are kept LOCAL ONLY and
38
38
  // nothing is sent to the cloud audit log.
39
39
  function localLogsOnly(security) {
40
- const l = security && security.localLogs;
41
- return !!(l && l.enabled && typeof l.path === 'string' && l.path.trim());
40
+ if (security && typeof security === 'object') {
41
+ const l = security.localLogs;
42
+ return !!(l && l.enabled && typeof l.path === 'string' && l.path.trim());
43
+ }
44
+ // security UNKNOWN (cold start / policy-cache miss / mid-toggle): consult the
45
+ // persisted local-mode marker so a DENY is NEVER leaked to the cloud when this
46
+ // device is in local-only mode. Without this fallback the first call(s) before
47
+ // the policy cache warms POST the denial to the cloud and fire webhooks/alerts
48
+ // even though the user chose local-only.
49
+ try {
50
+ const m = JSON.parse(readFileSync(join(resolve(homedir(), '.solongate'), '.local-logs-mode.json'), 'utf-8'));
51
+ return !!(m && m.localOnly);
52
+ } catch { return false; }
53
+ }
54
+
55
+ // Persist whether this device is in local-only mode, INDEPENDENT of the policy
56
+ // cache, so localLogsOnly() answers correctly even on a cache-miss call. Written
57
+ // on every policy refresh; switching back to cloud sets localOnly:false so cloud
58
+ // POSTs resume.
59
+ function writeLocalMarker(security) {
60
+ try {
61
+ const l = security && security.localLogs;
62
+ const localOnly = !!(l && l.enabled && typeof l.path === 'string' && l.path.trim());
63
+ writeFileSync(join(resolve(homedir(), '.solongate'), '.local-logs-mode.json'), JSON.stringify({ localOnly, ts: Date.now() }));
64
+ } catch {}
42
65
  }
43
66
 
44
67
  // Resolve the FOLDER local logs may be written into. It MUST be absolute on
@@ -279,6 +302,7 @@ async function refreshPolicyCache() {
279
302
  } catch {}
280
303
  // Always advance _ts (success or failure) so the hot path backs off between refreshes.
281
304
  try { writeFileSync(cacheFile, JSON.stringify({ _ts: Date.now(), policy, selfProtect, security, hookVersions })); } catch {}
305
+ writeLocalMarker(security);
282
306
  } catch {}
283
307
  }
284
308
  if (REFRESH_MODE) { try { setTimeout(() => { try { process.exit(process.exitCode || 0); } catch {} }, 8000).unref(); } catch {} refreshPolicyCache().finally(() => { process.exitCode = 0; }); }
@@ -1553,7 +1577,7 @@ if (!REFRESH_MODE) { try { input += readFileSync(0, 'utf-8'); } catch {} }
1553
1577
  refreshDue = false;
1554
1578
  if (cached.policy) dashboardPolicy = cached.policy;
1555
1579
  if (typeof cached.selfProtect === 'boolean') selfProtectEnabled = cached.selfProtect;
1556
- if (cached.security !== undefined) securityCfg = cached.security;
1580
+ if (cached.security !== undefined) { securityCfg = cached.security; writeLocalMarker(securityCfg); }
1557
1581
  if (cached.hookVersions) CLOUD_HOOK_VERSIONS = cached.hookVersions;
1558
1582
  }
1559
1583
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.81.59",
3
+ "version": "0.81.61",
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": {