@solongate/proxy 0.83.13 → 0.83.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7581,7 +7581,7 @@ function StreamLine({ e, loc, selected, date }) {
7581
7581
  hhmmss(e.at),
7582
7582
  " "
7583
7583
  ] }),
7584
- /* @__PURE__ */ jsx(Text, { color: loc ? theme.ok : "white", children: loc ? "LOC" : "CLD" }),
7584
+ /* @__PURE__ */ jsx(Text, { color: loc ? theme.ok : "white", children: loc ? "HERE" : "AWAY" }),
7585
7585
  /* @__PURE__ */ jsx(Text, { color: theme.dim, children: "] " }),
7586
7586
  /* @__PURE__ */ jsx(Text, { color: decisionColor(e.decision), bold: e.decision !== "ALLOW", children: e.decision.padEnd(6) }),
7587
7587
  /* @__PURE__ */ jsx(Text, { color: theme.accent, children: truncate2(e.tool, 12).padEnd(13) }),
@@ -9175,7 +9175,7 @@ function LivePanel({ active: active2 }) {
9175
9175
  /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "white", bold: true, children: " ENTRY " }),
9176
9176
  /* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), bold: true, children: " " + e.decision }),
9177
9177
  /* @__PURE__ */ jsx2(Text2, { color: theme.accent, bold: true, children: " " + e.tool }),
9178
- /* @__PURE__ */ jsx2(Text2, { color: isLoc(e) ? theme.ok : "white", children: " " + (isLoc(e) ? "LOC" : "CLD") }),
9178
+ /* @__PURE__ */ jsx2(Text2, { color: isLoc(e) ? theme.ok : "white", children: " " + (isLoc(e) ? "HERE" : "AWAY") }),
9179
9179
  e.dlp ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: " DLP!" }) : null,
9180
9180
  e.burst ? /* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: " BURST" }) : null,
9181
9181
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2190 back" })
@@ -9500,14 +9500,14 @@ function LivePanel({ active: active2 }) {
9500
9500
  filter === "all" ? /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
9501
9501
  spin,
9502
9502
  " awaiting traffic\u2026"
9503
- ] }) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: `${spin} no ${filter === "local" ? "LOC (this machine)" : "CLD (other devices)"} calls in the buffer \xB7 f switches source` })
9503
+ ] }) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: `${spin} no ${filter === "local" ? "HERE (this machine)" : "AWAY (other devices)"} calls in the buffer \xB7 f switches origin` })
9504
9504
  ) : null,
9505
9505
  windowed.map((e, i) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e), selected: clampedScroll + i === selClamped }, e.id))
9506
9506
  ] }),
9507
9507
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
9508
9508
  /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "white", bold: true, children: " LIVE " }),
9509
- /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "white", children: ` local ${localOn ? "on" : "off"} \xB7 ${localBuf.length} loc/${cloudBuf.length} cld \xB7 source ${filter === "all" ? "all" : filter === "local" ? "LOC only" : "CLD only"} \xB7 top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} ` }),
9510
- /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "white", children: ` \u2191\u2193 select \xB7 enter full entry \xB7 f source \xB7 / search \xB7 space copy \xB7 ? all keys \xB7 esc menu \xB7 q quit ` })
9509
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "white", children: ` local-log ${localOn ? "on" : "off"} \xB7 ${localBuf.length} here/${cloudBuf.length} away \xB7 origin ${filter === "all" ? "all" : filter === "local" ? "HERE only" : "AWAY only"} \xB7 top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} ` }),
9510
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "white", children: ` \u2191\u2193 select \xB7 enter full entry \xB7 f origin \xB7 / search \xB7 space copy \xB7 ? all keys \xB7 esc menu \xB7 q quit ` })
9511
9511
  ] })
9512
9512
  ] });
9513
9513
  }
@@ -9548,7 +9548,7 @@ var init_Live = __esm({
9548
9548
  ["w", "whitelist the selected DENY (adds ALLOW rule)"],
9549
9549
  ["b", "block the selected ALLOW (adds DENY rule)"],
9550
9550
  ["d / x / r", "filter: denies / dlp hits / rate-limit bursts"],
9551
- ["f", "source: all \u2192 LOC (this machine) \u2192 CLD (other devices)"],
9551
+ ["f", "origin: all \u2192 HERE (this machine) \u2192 AWAY (other devices)"],
9552
9552
  ["/", "live search (tool, agent, command\u2026) \xB7 enter done"],
9553
9553
  ["s", "session picker"],
9554
9554
  ["l", "layers detail (rate limit \xB7 dlp \xB7 ghost \xB7 guard)"],
@@ -11187,7 +11187,7 @@ function AuditPanel({ active: active2, focused }) {
11187
11187
  /* @__PURE__ */ jsx7(Text7, { backgroundColor: BG2, color: "white", bold: true, children: " ENTRY " }),
11188
11188
  /* @__PURE__ */ jsx7(Text7, { color: decisionColor(e.decision), bold: true, children: " " + e.decision }),
11189
11189
  /* @__PURE__ */ jsx7(Text7, { color: theme.accent, bold: true, children: " " + e.tool }),
11190
- /* @__PURE__ */ jsx7(Text7, { color: loc ? theme.ok : "white", children: " " + (loc ? "LOC" : "CLD") }),
11190
+ /* @__PURE__ */ jsx7(Text7, { color: loc ? theme.ok : "white", children: " " + (loc ? "HERE" : "AWAY") }),
11191
11191
  e.dlp.length ? /* @__PURE__ */ jsx7(Text7, { color: theme.bad, children: " DLP!" }) : null,
11192
11192
  e.burst ? /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: " BURST" }) : null,
11193
11193
  /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2190 back" })
@@ -11,7 +11,11 @@ export declare function PaneTitle({ label, extra, width }: {
11
11
  }): JSX.Element;
12
12
  /**
13
13
  * One tool-stream row, shared verbatim by Live (rolling buffer) and Audit (full
14
- * history). `loc` tags LOC vs CLD; `selected` highlights + shows the cursor.
14
+ * history). `loc` tags where the call HAPPENED HERE (this machine) vs AWAY
15
+ * (another paired device). Deliberately not LOC/CLD: `local` already names the
16
+ * local-log STORAGE setting shown in the same panel, and one word meaning two
17
+ * things two lines apart had people reading a cloud-stored call as on-disk.
18
+ * `selected` highlights + shows the ▸ cursor.
15
19
  * Columns are fixed-width and NEVER conditional so scrolling can't reshuffle the
16
20
  * line and glitch the frame.
17
21
  */
package/dist/tui/index.js CHANGED
@@ -745,7 +745,7 @@ function StreamLine({ e, loc, selected, date }) {
745
745
  hhmmss(e.at),
746
746
  " "
747
747
  ] }),
748
- /* @__PURE__ */ jsx(Text, { color: loc ? theme.ok : "white", children: loc ? "LOC" : "CLD" }),
748
+ /* @__PURE__ */ jsx(Text, { color: loc ? theme.ok : "white", children: loc ? "HERE" : "AWAY" }),
749
749
  /* @__PURE__ */ jsx(Text, { color: theme.dim, children: "] " }),
750
750
  /* @__PURE__ */ jsx(Text, { color: decisionColor(e.decision), bold: e.decision !== "ALLOW", children: e.decision.padEnd(6) }),
751
751
  /* @__PURE__ */ jsx(Text, { color: theme.accent, children: truncate(e.tool, 12).padEnd(13) }),
@@ -1682,7 +1682,7 @@ var LIVE_HELP = [
1682
1682
  ["w", "whitelist the selected DENY (adds ALLOW rule)"],
1683
1683
  ["b", "block the selected ALLOW (adds DENY rule)"],
1684
1684
  ["d / x / r", "filter: denies / dlp hits / rate-limit bursts"],
1685
- ["f", "source: all \u2192 LOC (this machine) \u2192 CLD (other devices)"],
1685
+ ["f", "origin: all \u2192 HERE (this machine) \u2192 AWAY (other devices)"],
1686
1686
  ["/", "live search (tool, agent, command\u2026) \xB7 enter done"],
1687
1687
  ["s", "session picker"],
1688
1688
  ["l", "layers detail (rate limit \xB7 dlp \xB7 ghost \xB7 guard)"],
@@ -2283,7 +2283,7 @@ function LivePanel({ active: active2 }) {
2283
2283
  /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "white", bold: true, children: " ENTRY " }),
2284
2284
  /* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), bold: true, children: " " + e.decision }),
2285
2285
  /* @__PURE__ */ jsx2(Text2, { color: theme.accent, bold: true, children: " " + e.tool }),
2286
- /* @__PURE__ */ jsx2(Text2, { color: isLoc(e) ? theme.ok : "white", children: " " + (isLoc(e) ? "LOC" : "CLD") }),
2286
+ /* @__PURE__ */ jsx2(Text2, { color: isLoc(e) ? theme.ok : "white", children: " " + (isLoc(e) ? "HERE" : "AWAY") }),
2287
2287
  e.dlp ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: " DLP!" }) : null,
2288
2288
  e.burst ? /* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: " BURST" }) : null,
2289
2289
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2190 back" })
@@ -2608,14 +2608,14 @@ function LivePanel({ active: active2 }) {
2608
2608
  filter === "all" ? /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
2609
2609
  spin,
2610
2610
  " awaiting traffic\u2026"
2611
- ] }) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: `${spin} no ${filter === "local" ? "LOC (this machine)" : "CLD (other devices)"} calls in the buffer \xB7 f switches source` })
2611
+ ] }) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: `${spin} no ${filter === "local" ? "HERE (this machine)" : "AWAY (other devices)"} calls in the buffer \xB7 f switches origin` })
2612
2612
  ) : null,
2613
2613
  windowed.map((e, i) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e), selected: clampedScroll + i === selClamped }, e.id))
2614
2614
  ] }),
2615
2615
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
2616
2616
  /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "white", bold: true, children: " LIVE " }),
2617
- /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "white", children: ` local ${localOn ? "on" : "off"} \xB7 ${localBuf.length} loc/${cloudBuf.length} cld \xB7 source ${filter === "all" ? "all" : filter === "local" ? "LOC only" : "CLD only"} \xB7 top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} ` }),
2618
- /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "white", children: ` \u2191\u2193 select \xB7 enter full entry \xB7 f source \xB7 / search \xB7 space copy \xB7 ? all keys \xB7 esc menu \xB7 q quit ` })
2617
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "white", children: ` local-log ${localOn ? "on" : "off"} \xB7 ${localBuf.length} here/${cloudBuf.length} away \xB7 origin ${filter === "all" ? "all" : filter === "local" ? "HERE only" : "AWAY only"} \xB7 top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} ` }),
2618
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "white", children: ` \u2191\u2193 select \xB7 enter full entry \xB7 f origin \xB7 / search \xB7 space copy \xB7 ? all keys \xB7 esc menu \xB7 q quit ` })
2619
2619
  ] })
2620
2620
  ] });
2621
2621
  }
@@ -4262,7 +4262,7 @@ function AuditPanel({ active: active2, focused }) {
4262
4262
  /* @__PURE__ */ jsx7(Text7, { backgroundColor: BG2, color: "white", bold: true, children: " ENTRY " }),
4263
4263
  /* @__PURE__ */ jsx7(Text7, { color: decisionColor(e.decision), bold: true, children: " " + e.decision }),
4264
4264
  /* @__PURE__ */ jsx7(Text7, { color: theme.accent, bold: true, children: " " + e.tool }),
4265
- /* @__PURE__ */ jsx7(Text7, { color: loc ? theme.ok : "white", children: " " + (loc ? "LOC" : "CLD") }),
4265
+ /* @__PURE__ */ jsx7(Text7, { color: loc ? theme.ok : "white", children: " " + (loc ? "HERE" : "AWAY") }),
4266
4266
  e.dlp.length ? /* @__PURE__ */ jsx7(Text7, { color: theme.bad, children: " DLP!" }) : null,
4267
4267
  e.burst ? /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: " BURST" }) : null,
4268
4268
  /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2190 back" })
@@ -6536,7 +6536,7 @@ 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 = 76;
6539
+ var HOOK_VERSION = 77;
6540
6540
  function localLogsOnly(security) {
6541
6541
  if (security !== void 0) {
6542
6542
  const l = security && security.localLogs;
@@ -6714,6 +6714,30 @@ function installedHookVersion(fileName) {
6714
6714
  return 0;
6715
6715
  }
6716
6716
  }
6717
+ function registeredClients() {
6718
+ const home = resolve(homedir());
6719
+ const has = (p, needle) => {
6720
+ try {
6721
+ const s = safeReadFileSync(p);
6722
+ if (!s)
6723
+ return false;
6724
+ return needle ? s.includes(needle) : true;
6725
+ } catch {
6726
+ return false;
6727
+ }
6728
+ };
6729
+ const out = [];
6730
+ if (has(join(home, ".claude", "settings.json"), ".solongate"))
6731
+ out.push("claude-code");
6732
+ if (has(join(home, ".gemini", "config", "hooks.json"), ".solongate"))
6733
+ out.push("antigravity");
6734
+ if (has(join(process.env.CODEX_HOME ? resolve(process.env.CODEX_HOME) : join(home, ".codex"), "hooks.json"), ".solongate"))
6735
+ out.push("codex");
6736
+ const xdg = process.env.XDG_CONFIG_HOME ? resolve(process.env.XDG_CONFIG_HOME) : join(home, ".config");
6737
+ if (has(join(xdg, "opencode", "plugins", "solongate.js"), "tool.execute.before"))
6738
+ out.push("opencode");
6739
+ return out;
6740
+ }
6717
6741
  var CLOUD_HOOK_VERSIONS = null;
6718
6742
  function hooksBehindCloud() {
6719
6743
  const v = CLOUD_HOOK_VERSIONS;
@@ -6808,7 +6832,10 @@ async function refreshPolicyCache() {
6808
6832
  } catch {
6809
6833
  }
6810
6834
  try {
6811
- const res = await fetch(API_URL + "/api/v1/policies/active?agent_id=" + encodeURIComponent(AGENT_ID || "") + "&hv=" + HOOK_VERSION, { headers: AUTH_HEADERS, signal: AbortSignal.timeout(8e3) });
6835
+ const res = await fetch(
6836
+ API_URL + "/api/v1/policies/active?agent_id=" + encodeURIComponent(AGENT_ID || "") + "&hv=" + HOOK_VERSION + "&clients=" + encodeURIComponent(registeredClients().join(",")),
6837
+ { headers: AUTH_HEADERS, signal: AbortSignal.timeout(8e3) }
6838
+ );
6812
6839
  if (res.ok) {
6813
6840
  const body = await res.json();
6814
6841
  if (typeof body?.self_protection_enabled === "boolean")
package/hooks/guard.mjs CHANGED
@@ -32,7 +32,7 @@ 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 = 76;
35
+ const HOOK_VERSION = 77;
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 — local and cloud are one or the
@@ -313,6 +313,36 @@ function installedHookVersion(fileName) {
313
313
  } catch { return 0; }
314
314
  }
315
315
 
316
+ /**
317
+ * Which clients the guard is REGISTERED for on this machine.
318
+ *
319
+ * The dashboard used to infer this from "whose guard has called in", which
320
+ * answers a different question than `repair` and `doctor` do — a client that is
321
+ * installed but has not been opened in days reads as unguarded, and the three
322
+ * surfaces contradicted each other while using identical wording. The machine
323
+ * is the only thing that can see its own files, so it reports them.
324
+ *
325
+ * Cheap on purpose: four existence/substring checks against files this hook
326
+ * already treats as tamper targets, run on a poll that is throttled to 10s.
327
+ */
328
+ function registeredClients() {
329
+ const home = resolve(homedir());
330
+ const has = (p, needle) => {
331
+ try {
332
+ const s = safeReadFileSync(p);
333
+ if (!s) return false;
334
+ return needle ? s.includes(needle) : true;
335
+ } catch { return false; }
336
+ };
337
+ const out = [];
338
+ if (has(join(home, '.claude', 'settings.json'), '.solongate')) out.push('claude-code');
339
+ if (has(join(home, '.gemini', 'config', 'hooks.json'), '.solongate')) out.push('antigravity');
340
+ if (has(join(process.env.CODEX_HOME ? resolve(process.env.CODEX_HOME) : join(home, '.codex'), 'hooks.json'), '.solongate')) out.push('codex');
341
+ const xdg = process.env.XDG_CONFIG_HOME ? resolve(process.env.XDG_CONFIG_HOME) : join(home, '.config');
342
+ if (has(join(xdg, 'opencode', 'plugins', 'solongate.js'), 'tool.execute.before')) out.push('opencode');
343
+ return out;
344
+ }
345
+
316
346
  // Latest hook versions the cloud reports on /policies/active (hook_versions).
317
347
  // Captured during the policy fetch of THIS run (or its short-lived cache); lets
318
348
  // maybeSelfUpdate() know it is behind and bypass the 6h stamp entirely.
@@ -425,7 +455,12 @@ async function refreshPolicyCache() {
425
455
  }
426
456
  } catch {}
427
457
  try {
428
- const res = await fetch(API_URL + '/api/v1/policies/active?agent_id=' + encodeURIComponent(AGENT_ID || '') + '&hv=' + HOOK_VERSION, { headers: AUTH_HEADERS, signal: AbortSignal.timeout(8000) });
458
+ const res = await fetch(
459
+ API_URL + '/api/v1/policies/active?agent_id=' + encodeURIComponent(AGENT_ID || '')
460
+ + '&hv=' + HOOK_VERSION
461
+ + '&clients=' + encodeURIComponent(registeredClients().join(',')),
462
+ { headers: AUTH_HEADERS, signal: AbortSignal.timeout(8000) },
463
+ );
429
464
  if (res.ok) {
430
465
  const body = await res.json();
431
466
  if (typeof body?.self_protection_enabled === 'boolean') selfProtect = body.self_protection_enabled;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.83.13",
3
+ "version": "0.83.15",
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": {