@solongate/proxy 0.81.15 → 0.81.17

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
@@ -7089,6 +7089,145 @@ var init_api_client = __esm({
7089
7089
  }
7090
7090
  });
7091
7091
 
7092
+ // src/tui/notify.ts
7093
+ import { spawn, execFileSync as execFileSync2 } from "child_process";
7094
+ import { writeFileSync as writeFileSync3, existsSync as existsSync4 } from "fs";
7095
+ import { join as join6 } from "path";
7096
+ import { tmpdir as tmpdir2 } from "os";
7097
+ function captureFocusTarget() {
7098
+ try {
7099
+ if (process.platform === "linux") {
7100
+ focusToken = (process.env.WINDOWID || "").trim() || null;
7101
+ if (!focusToken) {
7102
+ try {
7103
+ focusToken = execFileSync2("xdotool", ["getactivewindow"], { encoding: "utf-8", timeout: 1500, stdio: ["ignore", "pipe", "ignore"] }).trim() || null;
7104
+ } catch {
7105
+ focusToken = null;
7106
+ }
7107
+ }
7108
+ } else if (process.platform === "win32") {
7109
+ const ps = `Add-Type -Name W -Namespace N -MemberDefinition '[DllImport("user32.dll")]public static extern System.IntPtr GetForegroundWindow();' | Out-Null; [N.W]::GetForegroundWindow().ToInt64()`;
7110
+ try {
7111
+ focusToken = execFileSync2("powershell", ["-NoProfile", "-NonInteractive", "-Command", ps], { encoding: "utf-8", timeout: 4e3, stdio: ["ignore", "pipe", "ignore"] }).trim() || null;
7112
+ } catch {
7113
+ focusToken = null;
7114
+ }
7115
+ }
7116
+ } catch {
7117
+ focusToken = null;
7118
+ }
7119
+ }
7120
+ function desktopNotify(title, msg) {
7121
+ try {
7122
+ if (process.platform === "linux") linuxNotify(title, msg);
7123
+ else if (process.platform === "win32") winNotify(title, msg);
7124
+ else if (process.platform === "darwin") macNotify(title, msg);
7125
+ } catch {
7126
+ }
7127
+ }
7128
+ function linuxNotify(title, msg) {
7129
+ let p;
7130
+ try {
7131
+ p = spawn("notify-send", ["--wait", "--action=default=Open", "-a", "SolonGate", title, msg], { stdio: ["ignore", "pipe", "ignore"] });
7132
+ } catch {
7133
+ return;
7134
+ }
7135
+ p.on("error", () => {
7136
+ });
7137
+ p.stdout?.on("data", (d) => {
7138
+ if (String(d).trim() === "default") raiseLinuxWindow();
7139
+ });
7140
+ p.unref();
7141
+ }
7142
+ function raiseLinuxWindow() {
7143
+ if (!focusToken) return;
7144
+ try {
7145
+ const x = spawn("xdotool", ["windowactivate", focusToken], { stdio: "ignore" });
7146
+ x.on("error", () => {
7147
+ try {
7148
+ const hex = "0x" + Number(focusToken).toString(16);
7149
+ const w = spawn("wmctrl", ["-ia", hex], { stdio: "ignore" });
7150
+ w.on("error", () => {
7151
+ });
7152
+ w.unref();
7153
+ } catch {
7154
+ }
7155
+ });
7156
+ x.unref();
7157
+ } catch {
7158
+ }
7159
+ }
7160
+ function ensureWinScript() {
7161
+ if (winScriptPath && existsSync4(winScriptPath)) return winScriptPath;
7162
+ const p = join6(tmpdir2(), "solongate-toast.ps1");
7163
+ const script = [
7164
+ "param([string]$Title,[string]$Msg,[string]$Hwnd)",
7165
+ "Add-Type -AssemblyName System.Windows.Forms",
7166
+ "Add-Type -AssemblyName System.Drawing",
7167
+ `Add-Type -Name Win -Namespace Sg -MemberDefinition '[DllImport("user32.dll")]public static extern bool SetForegroundWindow(System.IntPtr h);[DllImport("user32.dll")]public static extern bool ShowWindow(System.IntPtr h,int c);'`,
7168
+ "$ni=New-Object System.Windows.Forms.NotifyIcon",
7169
+ "$ni.Icon=[System.Drawing.SystemIcons]::Information",
7170
+ "$ni.Visible=$true",
7171
+ "$script:clicked=$false",
7172
+ "$ni.add_BalloonTipClicked({$script:clicked=$true})",
7173
+ "$ni.ShowBalloonTip(8000,$Title,$Msg,[System.Windows.Forms.ToolTipIcon]::Warning)",
7174
+ "$sw=[System.Diagnostics.Stopwatch]::StartNew()",
7175
+ "while($sw.Elapsed.TotalSeconds -lt 9 -and -not $script:clicked){[System.Windows.Forms.Application]::DoEvents();Start-Sleep -Milliseconds 100}",
7176
+ "if($script:clicked -and $Hwnd){try{$h=[System.IntPtr][int64]$Hwnd;[Sg.Win]::ShowWindow($h,9)|Out-Null;[Sg.Win]::SetForegroundWindow($h)|Out-Null}catch{}}",
7177
+ "$ni.Visible=$false;$ni.Dispose()"
7178
+ ].join("\n");
7179
+ try {
7180
+ writeFileSync3(p, script);
7181
+ winScriptPath = p;
7182
+ return p;
7183
+ } catch {
7184
+ return null;
7185
+ }
7186
+ }
7187
+ function winNotify(title, msg) {
7188
+ const script = ensureWinScript();
7189
+ if (!script) return;
7190
+ try {
7191
+ const p = spawn("powershell", ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File", script, "-Title", title, "-Msg", msg, "-Hwnd", focusToken || ""], { stdio: "ignore", detached: true, windowsHide: true });
7192
+ p.on("error", () => {
7193
+ });
7194
+ p.unref();
7195
+ } catch {
7196
+ }
7197
+ }
7198
+ function macBundleId() {
7199
+ const prog = (process.env.TERM_PROGRAM || "").toLowerCase();
7200
+ if (prog.includes("iterm")) return "com.googlecode.iterm2";
7201
+ if (prog.includes("apple_terminal")) return "com.apple.Terminal";
7202
+ if (prog.includes("vscode")) return "com.microsoft.VSCode";
7203
+ return "com.apple.Terminal";
7204
+ }
7205
+ function macNotify(title, msg) {
7206
+ try {
7207
+ const p = spawn("terminal-notifier", ["-title", title, "-message", msg, "-activate", macBundleId(), "-sender", macBundleId()], { stdio: "ignore", detached: true });
7208
+ p.on("error", () => {
7209
+ try {
7210
+ const esc = (s) => s.replace(/"/g, '\\"');
7211
+ const a = spawn("osascript", ["-e", `display notification "${esc(msg)}" with title "${esc(title)}"`], { stdio: "ignore", detached: true });
7212
+ a.on("error", () => {
7213
+ });
7214
+ a.unref();
7215
+ } catch {
7216
+ }
7217
+ });
7218
+ p.unref();
7219
+ } catch {
7220
+ }
7221
+ }
7222
+ var focusToken, winScriptPath;
7223
+ var init_notify = __esm({
7224
+ "src/tui/notify.ts"() {
7225
+ "use strict";
7226
+ focusToken = null;
7227
+ winScriptPath = null;
7228
+ }
7229
+ });
7230
+
7092
7231
  // src/tui/hooks.ts
7093
7232
  import { useCallback, useEffect, useRef, useState } from "react";
7094
7233
  function useLoader(fn, deps = []) {
@@ -7137,10 +7276,9 @@ var init_hooks = __esm({
7137
7276
  // src/tui/panels/Live.tsx
7138
7277
  import { Box as Box2, Text as Text2, useInput } from "ink";
7139
7278
  import TextInput from "ink-text-input";
7140
- import { spawn } from "child_process";
7141
- import { closeSync, mkdirSync as mkdirSync3, openSync, readSync, statSync, writeFileSync as writeFileSync3 } from "fs";
7279
+ import { closeSync, mkdirSync as mkdirSync3, openSync, readSync, statSync, writeFileSync as writeFileSync4 } from "fs";
7142
7280
  import { homedir as homedir4 } from "os";
7143
- import { join as join6 } from "path";
7281
+ import { join as join7 } from "path";
7144
7282
  import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
7145
7283
  import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
7146
7284
  function tailLines(file, maxBytes = 131072) {
@@ -7271,7 +7409,7 @@ function LivePanel({ active: active2 }) {
7271
7409
  const [sel, setSel] = useState2(0);
7272
7410
  const [actionMsg, setActionMsg] = useState2(null);
7273
7411
  const notifiedRef = useRef2(/* @__PURE__ */ new Set());
7274
- const notifyReady = useRef2(false);
7412
+ const openedAtRef = useRef2(Date.now());
7275
7413
  const activePolicyRef = useRef2(null);
7276
7414
  const [mode, setMode] = useState2("stream");
7277
7415
  const [pickIdx, setPickIdx] = useState2(0);
@@ -7455,13 +7593,7 @@ function LivePanel({ active: active2 }) {
7455
7593
  process.stdout.write("\x07");
7456
7594
  } catch {
7457
7595
  }
7458
- try {
7459
- const child = spawn("notify-send", ["-a", "SolonGate", title, msg], { stdio: "ignore", detached: true });
7460
- child.on("error", () => {
7461
- });
7462
- child.unref();
7463
- } catch {
7464
- }
7596
+ desktopNotify(title, msg);
7465
7597
  },
7466
7598
  [pushLog]
7467
7599
  );
@@ -7479,14 +7611,10 @@ function LivePanel({ active: active2 }) {
7479
7611
  useEffect2(() => {
7480
7612
  if (!active2) return;
7481
7613
  const notable = mergedRef.current.filter((e) => e.decision !== "ALLOW" || e.dlp || e.burst);
7482
- if (!notifyReady.current) {
7483
- for (const e of notable) notifiedRef.current.add(e.id);
7484
- notifyReady.current = true;
7485
- return;
7486
- }
7487
7614
  for (const e of notable) {
7488
7615
  if (notifiedRef.current.has(e.id)) continue;
7489
7616
  notifiedRef.current.add(e.id);
7617
+ if (!(e.at > openedAtRef.current)) continue;
7490
7618
  const who = e.agent ? ` \xB7 ${truncate2(e.agent, 20)}` : "";
7491
7619
  if (e.dlp) fireAlert("sec:" + e.id, "DLP hit", `SECRET in ${e.tool}${who}: ${truncate2(e.detail, 60)}`, "bad");
7492
7620
  else if (e.decision !== "ALLOW") fireAlert("sec:" + e.id, "Call denied", `DENY ${e.tool}${who}: ${truncate2(e.rule ?? e.detail, 60)}`, "bad");
@@ -7679,10 +7807,10 @@ function LivePanel({ active: active2 }) {
7679
7807
  else if (input === "x") toggleSignal("dlp");
7680
7808
  else if (input === "r") toggleSignal("ratelimit");
7681
7809
  else if (input === "e") {
7682
- const file = join6(homedir4(), ".solongate", "live-export.jsonl");
7810
+ const file = join7(homedir4(), ".solongate", "live-export.jsonl");
7683
7811
  try {
7684
- mkdirSync3(join6(homedir4(), ".solongate"), { recursive: true });
7685
- writeFileSync3(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
7812
+ mkdirSync3(join7(homedir4(), ".solongate"), { recursive: true });
7813
+ writeFileSync4(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
7686
7814
  setActionMsg({ text: `\u2713 exported ${visibleDesc.length} lines \u2192 ${file}`, level: "ok", until: Date.now() + 6e3 });
7687
7815
  } catch (err2) {
7688
7816
  setActionMsg({ text: "\u2717 export failed: " + (err2 instanceof Error ? err2.message : String(err2)), level: "bad", until: Date.now() + 6e3 });
@@ -7942,14 +8070,15 @@ var init_Live = __esm({
7942
8070
  "use strict";
7943
8071
  init_api_client();
7944
8072
  init_config2();
8073
+ init_notify();
7945
8074
  init_hooks();
7946
8075
  init_theme();
7947
8076
  CONFIG = loadConfig();
7948
8077
  SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
7949
8078
  BG = "#12234f";
7950
8079
  DIM_FLOOR = "#233457";
7951
- LOCAL_LOG = join6(homedir4(), ".solongate", "local-logs", "solongate-audit.jsonl");
7952
- RING = join6(process.cwd(), ".solongate", ".eval-ring.jsonl");
8080
+ LOCAL_LOG = join7(homedir4(), ".solongate", "local-logs", "solongate-audit.jsonl");
8081
+ RING = join7(process.cwd(), ".solongate", ".eval-ring.jsonl");
7953
8082
  hhmmss = (ts) => {
7954
8083
  const d = new Date(ts);
7955
8084
  return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
@@ -9165,6 +9294,7 @@ async function launchTui() {
9165
9294
  process.stderr.write("Not logged in. Run `solongate login` first.\n");
9166
9295
  return;
9167
9296
  }
9297
+ captureFocusTarget();
9168
9298
  process.stdout.write("\x1B[?1049h\x1B[H");
9169
9299
  try {
9170
9300
  const { waitUntilExit } = render(/* @__PURE__ */ jsx10(App, {}));
@@ -9178,6 +9308,7 @@ var init_tui = __esm({
9178
9308
  "use strict";
9179
9309
  init_App();
9180
9310
  init_client();
9311
+ init_notify();
9181
9312
  }
9182
9313
  });
9183
9314
 
@@ -9869,9 +10000,9 @@ var init_agents2 = __esm({
9869
10000
  });
9870
10001
 
9871
10002
  // src/commands/doctor.ts
9872
- import { existsSync as existsSync4, statSync as statSync2 } from "fs";
10003
+ import { existsSync as existsSync5, statSync as statSync2 } from "fs";
9873
10004
  import { homedir as homedir5 } from "os";
9874
- import { join as join7 } from "path";
10005
+ import { join as join8 } from "path";
9875
10006
  async function run6(argv) {
9876
10007
  const { flags } = parse(argv);
9877
10008
  const json = flagBool(flags, "json");
@@ -9901,7 +10032,7 @@ async function run6(argv) {
9901
10032
  } catch {
9902
10033
  }
9903
10034
  }
9904
- if (existsSync4(LOCAL_LOG2)) {
10035
+ if (existsSync5(LOCAL_LOG2)) {
9905
10036
  const st = statSync2(LOCAL_LOG2);
9906
10037
  const ageMin = (Date.now() - st.mtimeMs) / 6e4;
9907
10038
  checks.push({ name: "local logs", ok: true, detail: `on \xB7 ${(st.size / 1024).toFixed(0)}KB \xB7 last write ${ageMin < 1 ? "just now" : Math.round(ageMin) + "m ago"}` });
@@ -9931,14 +10062,14 @@ var init_doctor = __esm({
9931
10062
  init_api_client();
9932
10063
  init_format();
9933
10064
  init_args();
9934
- LOCAL_LOG2 = join7(homedir5(), ".solongate", "local-logs", "solongate-audit.jsonl");
10065
+ LOCAL_LOG2 = join8(homedir5(), ".solongate", "local-logs", "solongate-audit.jsonl");
9935
10066
  }
9936
10067
  });
9937
10068
 
9938
10069
  // src/commands/watch.ts
9939
- import { closeSync as closeSync2, existsSync as existsSync5, openSync as openSync2, readSync as readSync2, statSync as statSync3 } from "fs";
10070
+ import { closeSync as closeSync2, existsSync as existsSync6, openSync as openSync2, readSync as readSync2, statSync as statSync3 } from "fs";
9940
10071
  import { homedir as homedir6 } from "os";
9941
- import { join as join8 } from "path";
10072
+ import { join as join9 } from "path";
9942
10073
  function tailLocal(file, maxBytes = 131072) {
9943
10074
  try {
9944
10075
  const size = statSync3(file).size;
@@ -9982,7 +10113,7 @@ async function run7(argv) {
9982
10113
  for (const r of rows) if (keep(r)) print(r, json);
9983
10114
  };
9984
10115
  const pollLocal = () => {
9985
- if (cloudOnly || !existsSync5(LOCAL_LOG3)) return;
10116
+ if (cloudOnly || !existsSync6(LOCAL_LOG3)) return;
9986
10117
  const rows = [];
9987
10118
  for (const line of tailLocal(LOCAL_LOG3)) {
9988
10119
  try {
@@ -10045,7 +10176,7 @@ var init_watch = __esm({
10045
10176
  init_cli_utils();
10046
10177
  init_args();
10047
10178
  init_format();
10048
- LOCAL_LOG3 = join8(homedir6(), ".solongate", "local-logs", "solongate-audit.jsonl");
10179
+ LOCAL_LOG3 = join9(homedir6(), ".solongate", "local-logs", "solongate-audit.jsonl");
10049
10180
  trunc = (s, n) => s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
10050
10181
  time = (ms) => new Date(ms).toTimeString().slice(0, 8);
10051
10182
  }
@@ -10347,32 +10478,32 @@ __export(global_install_exports, {
10347
10478
  runGlobalRestore: () => runGlobalRestore,
10348
10479
  unlockProtected: () => unlockProtected
10349
10480
  });
10350
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
10351
- import { resolve as resolve4, join as join9, dirname } from "path";
10481
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
10482
+ import { resolve as resolve4, join as join10, dirname } from "path";
10352
10483
  import { homedir as homedir7 } from "os";
10353
10484
  import { fileURLToPath } from "url";
10354
10485
  import { createInterface } from "readline";
10355
- import { execFileSync as execFileSync2 } from "child_process";
10486
+ import { execFileSync as execFileSync3 } from "child_process";
10356
10487
  function lockFile(file) {
10357
- if (!existsSync6(file)) return;
10488
+ if (!existsSync7(file)) return;
10358
10489
  try {
10359
10490
  if (process.platform === "win32") {
10360
10491
  try {
10361
- execFileSync2("icacls", [file, "/deny", "*S-1-1-0:(WD,AD,DC,DE)"], { stdio: "ignore" });
10492
+ execFileSync3("icacls", [file, "/deny", "*S-1-1-0:(WD,AD,DC,DE)"], { stdio: "ignore" });
10362
10493
  } catch {
10363
10494
  }
10364
10495
  try {
10365
- execFileSync2("attrib", ["+R", file], { stdio: "ignore" });
10496
+ execFileSync3("attrib", ["+R", file], { stdio: "ignore" });
10366
10497
  } catch {
10367
10498
  }
10368
10499
  } else if (process.platform === "darwin") {
10369
10500
  try {
10370
- execFileSync2("chflags", ["uchg", file], { stdio: "ignore" });
10501
+ execFileSync3("chflags", ["uchg", file], { stdio: "ignore" });
10371
10502
  } catch {
10372
10503
  }
10373
10504
  } else {
10374
10505
  try {
10375
- execFileSync2("chattr", ["+i", file], { stdio: "ignore" });
10506
+ execFileSync3("chattr", ["+i", file], { stdio: "ignore" });
10376
10507
  } catch {
10377
10508
  }
10378
10509
  }
@@ -10380,29 +10511,29 @@ function lockFile(file) {
10380
10511
  }
10381
10512
  }
10382
10513
  function unlockFile(file) {
10383
- if (!existsSync6(file)) return;
10514
+ if (!existsSync7(file)) return;
10384
10515
  try {
10385
10516
  if (process.platform === "win32") {
10386
10517
  try {
10387
- execFileSync2("icacls", [file, "/remove:d", "*S-1-1-0"], { stdio: "ignore" });
10518
+ execFileSync3("icacls", [file, "/remove:d", "*S-1-1-0"], { stdio: "ignore" });
10388
10519
  } catch {
10389
10520
  }
10390
10521
  try {
10391
- execFileSync2("icacls", [file, "/reset"], { stdio: "ignore" });
10522
+ execFileSync3("icacls", [file, "/reset"], { stdio: "ignore" });
10392
10523
  } catch {
10393
10524
  }
10394
10525
  try {
10395
- execFileSync2("attrib", ["-R", file], { stdio: "ignore" });
10526
+ execFileSync3("attrib", ["-R", file], { stdio: "ignore" });
10396
10527
  } catch {
10397
10528
  }
10398
10529
  } else if (process.platform === "darwin") {
10399
10530
  try {
10400
- execFileSync2("chflags", ["nouchg", file], { stdio: "ignore" });
10531
+ execFileSync3("chflags", ["nouchg", file], { stdio: "ignore" });
10401
10532
  } catch {
10402
10533
  }
10403
10534
  } else {
10404
10535
  try {
10405
- execFileSync2("chattr", ["-i", file], { stdio: "ignore" });
10536
+ execFileSync3("chattr", ["-i", file], { stdio: "ignore" });
10406
10537
  } catch {
10407
10538
  }
10408
10539
  }
@@ -10412,10 +10543,10 @@ function unlockFile(file) {
10412
10543
  function protectedTargets() {
10413
10544
  const p = globalPaths();
10414
10545
  return [
10415
- join9(p.hooksDir, "guard.mjs"),
10416
- join9(p.hooksDir, "audit.mjs"),
10417
- join9(p.hooksDir, "stop.mjs"),
10418
- join9(p.hooksDir, "shield.mjs"),
10546
+ join10(p.hooksDir, "guard.mjs"),
10547
+ join10(p.hooksDir, "audit.mjs"),
10548
+ join10(p.hooksDir, "stop.mjs"),
10549
+ join10(p.hooksDir, "shield.mjs"),
10419
10550
  p.configPath,
10420
10551
  p.settingsPath
10421
10552
  ];
@@ -10428,25 +10559,25 @@ function unlockProtected() {
10428
10559
  }
10429
10560
  function globalPaths() {
10430
10561
  const home = homedir7();
10431
- const sgDir = join9(home, ".solongate");
10432
- const hooksDir = join9(sgDir, "hooks");
10433
- const claudeDir = join9(home, ".claude");
10562
+ const sgDir = join10(home, ".solongate");
10563
+ const hooksDir = join10(sgDir, "hooks");
10564
+ const claudeDir = join10(home, ".claude");
10434
10565
  return {
10435
10566
  home,
10436
10567
  sgDir,
10437
10568
  hooksDir,
10438
10569
  claudeDir,
10439
- settingsPath: join9(claudeDir, "settings.json"),
10440
- backupPath: join9(claudeDir, "settings.solongate.bak"),
10441
- configPath: join9(sgDir, "cloud-guard.json")
10570
+ settingsPath: join10(claudeDir, "settings.json"),
10571
+ backupPath: join10(claudeDir, "settings.solongate.bak"),
10572
+ configPath: join10(sgDir, "cloud-guard.json")
10442
10573
  };
10443
10574
  }
10444
10575
  function readHook(filename) {
10445
- return readFileSync7(join9(HOOKS_DIR, filename), "utf-8");
10576
+ return readFileSync7(join10(HOOKS_DIR, filename), "utf-8");
10446
10577
  }
10447
10578
  function readGuard() {
10448
- const bundled = join9(HOOKS_DIR, "guard.bundled.mjs");
10449
- return existsSync6(bundled) ? readFileSync7(bundled, "utf-8") : readHook("guard.mjs");
10579
+ const bundled = join10(HOOKS_DIR, "guard.bundled.mjs");
10580
+ return existsSync7(bundled) ? readFileSync7(bundled, "utf-8") : readHook("guard.mjs");
10450
10581
  }
10451
10582
  function ask(question) {
10452
10583
  const rl = createInterface({ input: process.stdin, output: process.stderr });
@@ -10459,14 +10590,14 @@ function runGlobalRestore() {
10459
10590
  const p = globalPaths();
10460
10591
  unlockProtected();
10461
10592
  removeClaudeShim();
10462
- if (existsSync6(p.backupPath)) {
10463
- writeFileSync4(p.settingsPath, readFileSync7(p.backupPath, "utf-8"));
10593
+ if (existsSync7(p.backupPath)) {
10594
+ writeFileSync5(p.settingsPath, readFileSync7(p.backupPath, "utf-8"));
10464
10595
  console.log(` Restored ${p.settingsPath} from backup.`);
10465
- } else if (existsSync6(p.settingsPath)) {
10596
+ } else if (existsSync7(p.settingsPath)) {
10466
10597
  try {
10467
10598
  const s = JSON.parse(readFileSync7(p.settingsPath, "utf-8"));
10468
10599
  delete s.hooks;
10469
- writeFileSync4(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
10600
+ writeFileSync5(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
10470
10601
  console.log(` Removed SolonGate hooks from ${p.settingsPath}.`);
10471
10602
  } catch {
10472
10603
  }
@@ -10481,7 +10612,7 @@ function escapeRe(s) {
10481
10612
  function resolveRealClaude() {
10482
10613
  try {
10483
10614
  const finder = process.platform === "win32" ? "where" : "which";
10484
- const out2 = execFileSync2(finder, ["claude"], { encoding: "utf-8" }).split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
10615
+ const out2 = execFileSync3(finder, ["claude"], { encoding: "utf-8" }).split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
10485
10616
  if (process.platform === "win32") {
10486
10617
  const low = (s) => s.toLowerCase();
10487
10618
  return out2.find((l) => low(l).endsWith(".cmd")) || out2.find((l) => low(l).endsWith(".exe")) || out2.find((l) => low(l).endsWith(".bat")) || out2[0] || null;
@@ -10494,24 +10625,24 @@ function resolveRealClaude() {
10494
10625
  function shimTargets() {
10495
10626
  if (process.platform === "win32") {
10496
10627
  try {
10497
- const prof = execFileSync2("powershell", ["-NoProfile", "-Command", "$PROFILE.CurrentUserAllHosts"], { encoding: "utf-8" }).trim();
10628
+ const prof = execFileSync3("powershell", ["-NoProfile", "-Command", "$PROFILE.CurrentUserAllHosts"], { encoding: "utf-8" }).trim();
10498
10629
  return prof ? [prof] : [];
10499
10630
  } catch {
10500
10631
  return [];
10501
10632
  }
10502
10633
  }
10503
- return [".bashrc", ".zshrc", ".profile"].map((f) => join9(homedir7(), f)).filter((f) => existsSync6(f));
10634
+ return [".bashrc", ".zshrc", ".profile"].map((f) => join10(homedir7(), f)).filter((f) => existsSync7(f));
10504
10635
  }
10505
10636
  function writeShimBlock(file, block2) {
10506
10637
  const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
10507
- let content = existsSync6(file) ? readFileSync7(file, "utf-8") : "";
10638
+ let content = existsSync7(file) ? readFileSync7(file, "utf-8") : "";
10508
10639
  content = content.replace(re, "");
10509
10640
  if (block2) {
10510
10641
  if (content.length && !content.endsWith("\n")) content += "\n";
10511
10642
  content += block2 + "\n";
10512
10643
  }
10513
10644
  mkdirSync4(dirname(file), { recursive: true });
10514
- writeFileSync4(file, content);
10645
+ writeFileSync5(file, content);
10515
10646
  }
10516
10647
  function installClaudeShim(shieldPath) {
10517
10648
  const real = resolveRealClaude();
@@ -10565,19 +10696,19 @@ async function runGlobalInstall(opts = {}) {
10565
10696
  mkdirSync4(p.hooksDir, { recursive: true });
10566
10697
  mkdirSync4(p.claudeDir, { recursive: true });
10567
10698
  unlockProtected();
10568
- writeFileSync4(join9(p.hooksDir, "guard.mjs"), readGuard());
10569
- writeFileSync4(join9(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
10570
- writeFileSync4(join9(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
10571
- writeFileSync4(join9(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
10699
+ writeFileSync5(join10(p.hooksDir, "guard.mjs"), readGuard());
10700
+ writeFileSync5(join10(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
10701
+ writeFileSync5(join10(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
10702
+ writeFileSync5(join10(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
10572
10703
  console.log(` Installed hooks \u2192 ${p.hooksDir}`);
10573
- installClaudeShim(join9(p.hooksDir, "shield.mjs"));
10574
- writeFileSync4(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
10704
+ installClaudeShim(join10(p.hooksDir, "shield.mjs"));
10705
+ writeFileSync5(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
10575
10706
  console.log(` Wrote ${p.configPath}`);
10576
10707
  let existing = {};
10577
- if (existsSync6(p.settingsPath)) {
10708
+ if (existsSync7(p.settingsPath)) {
10578
10709
  const raw = readFileSync7(p.settingsPath, "utf-8");
10579
- if (!existsSync6(p.backupPath)) {
10580
- writeFileSync4(p.backupPath, raw);
10710
+ if (!existsSync7(p.backupPath)) {
10711
+ writeFileSync5(p.backupPath, raw);
10581
10712
  console.log(` Backed up existing settings \u2192 ${p.backupPath}`);
10582
10713
  }
10583
10714
  try {
@@ -10586,9 +10717,9 @@ async function runGlobalInstall(opts = {}) {
10586
10717
  existing = {};
10587
10718
  }
10588
10719
  }
10589
- const guardAbs = join9(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
10590
- const auditAbs = join9(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
10591
- const stopAbs = join9(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
10720
+ const guardAbs = join10(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
10721
+ const auditAbs = join10(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
10722
+ const stopAbs = join10(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
10592
10723
  const nodeBin = process.execPath.replace(/\\/g, "/");
10593
10724
  const call = process.platform === "win32" ? "& " : "";
10594
10725
  const hookCmd = (script) => `${call}"${nodeBin}" "${script}" claude-code "Claude Code"`;
@@ -10600,7 +10731,7 @@ async function runGlobalInstall(opts = {}) {
10600
10731
  Stop: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(stopAbs) }] }]
10601
10732
  }
10602
10733
  };
10603
- writeFileSync4(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
10734
+ writeFileSync5(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
10604
10735
  console.log(` Registered global hooks \u2192 ${p.settingsPath}`);
10605
10736
  if (process.env["SOLONGATE_OS_LOCK"] === "1") {
10606
10737
  lockProtected();
@@ -10780,7 +10911,7 @@ import { createServer, request as httpRequest } from "http";
10780
10911
  import { request as httpsRequest } from "https";
10781
10912
  import { spawn as spawn3 } from "child_process";
10782
10913
  import { URL as URL2 } from "url";
10783
- import { readFileSync as readFileSync8, existsSync as existsSync7, readdirSync, statSync as statSync4 } from "fs";
10914
+ import { readFileSync as readFileSync8, existsSync as existsSync8, readdirSync, statSync as statSync4 } from "fs";
10784
10915
  import { resolve as resolve5 } from "path";
10785
10916
  import { homedir as homedir8 } from "os";
10786
10917
  function findCacheFile() {
@@ -10788,7 +10919,7 @@ function findCacheFile() {
10788
10919
  const envSel = process.env.SOLONGATE_AGENT_ID;
10789
10920
  if (envSel) {
10790
10921
  const f = resolve5(dir, ".policy-cache-" + envSel.replace(/[^a-zA-Z0-9_-]/g, "_") + ".json");
10791
- if (existsSync7(f)) return f;
10922
+ if (existsSync8(f)) return f;
10792
10923
  }
10793
10924
  let best = null, bestTs = -1;
10794
10925
  try {
@@ -10809,7 +10940,7 @@ function findCacheFile() {
10809
10940
  function loadCfg() {
10810
10941
  try {
10811
10942
  const f = findCacheFile();
10812
- if (f && existsSync7(f)) {
10943
+ if (f && existsSync8(f)) {
10813
10944
  const c2 = JSON.parse(readFileSync8(f, "utf-8"));
10814
10945
  const d = c2?.security?.dlpRedact;
10815
10946
  const g = c2?.security?.ghost;
@@ -11099,7 +11230,7 @@ __export(logs_server_exports, {
11099
11230
  });
11100
11231
  import { createServer as createServer2 } from "http";
11101
11232
  import { readFileSync as readFileSync9, statSync as statSync5 } from "fs";
11102
- import { resolve as resolve6, join as join10, isAbsolute } from "path";
11233
+ import { resolve as resolve6, join as join11, isAbsolute } from "path";
11103
11234
  import { homedir as homedir9 } from "os";
11104
11235
  import { readdirSync as readdirSync2 } from "fs";
11105
11236
  function allowedOrigins() {
@@ -11125,7 +11256,7 @@ async function findLogDir() {
11125
11256
  const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
11126
11257
  for (const f of files) {
11127
11258
  try {
11128
- const c2 = JSON.parse(readFileSync9(join10(base, f), "utf-8"));
11259
+ const c2 = JSON.parse(readFileSync9(join11(base, f), "utf-8"));
11129
11260
  const p = c2?.security?.localLogs?.path;
11130
11261
  if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
11131
11262
  } catch {
@@ -11134,7 +11265,7 @@ async function findLogDir() {
11134
11265
  } catch {
11135
11266
  }
11136
11267
  try {
11137
- const cfgRaw = readFileSync9(join10(base, "cloud-guard.json"), "utf-8");
11268
+ const cfgRaw = readFileSync9(join11(base, "cloud-guard.json"), "utf-8");
11138
11269
  const { apiKey, apiUrl } = JSON.parse(cfgRaw);
11139
11270
  if (apiKey) {
11140
11271
  const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
@@ -11163,7 +11294,7 @@ function setCors(req, res) {
11163
11294
  }
11164
11295
  function fileInfo(dir) {
11165
11296
  if (!dir) return { file: null, exists: false, size: 0, mtimeMs: 0 };
11166
- const file = join10(dir, LOG_FILENAME);
11297
+ const file = join11(dir, LOG_FILENAME);
11167
11298
  try {
11168
11299
  const st = statSync5(file);
11169
11300
  return { file, exists: true, size: st.size, mtimeMs: st.mtimeMs };
@@ -11276,7 +11407,7 @@ var init_logs_server = __esm({
11276
11407
 
11277
11408
  // src/inject.ts
11278
11409
  var inject_exports = {};
11279
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync5, existsSync as existsSync8, copyFileSync } from "fs";
11410
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as existsSync9, copyFileSync } from "fs";
11280
11411
  import { resolve as resolve7 } from "path";
11281
11412
  import { execSync } from "child_process";
11282
11413
  function parseInjectArgs(argv) {
@@ -11334,7 +11465,7 @@ WHAT IT DOES
11334
11465
  `);
11335
11466
  }
11336
11467
  function detectProject() {
11337
- if (!existsSync8(resolve7("package.json"))) return false;
11468
+ if (!existsSync9(resolve7("package.json"))) return false;
11338
11469
  try {
11339
11470
  const pkg = JSON.parse(readFileSync10(resolve7("package.json"), "utf-8"));
11340
11471
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
@@ -11350,13 +11481,13 @@ function findTsEntryFile() {
11350
11481
  const binPath = typeof pkg.bin === "string" ? pkg.bin : Object.values(pkg.bin)[0];
11351
11482
  if (typeof binPath === "string") {
11352
11483
  const srcPath = binPath.replace(/^\.\/dist\//, "./src/").replace(/\.js$/, ".ts");
11353
- if (existsSync8(resolve7(srcPath))) return resolve7(srcPath);
11354
- if (existsSync8(resolve7(binPath))) return resolve7(binPath);
11484
+ if (existsSync9(resolve7(srcPath))) return resolve7(srcPath);
11485
+ if (existsSync9(resolve7(binPath))) return resolve7(binPath);
11355
11486
  }
11356
11487
  }
11357
11488
  if (pkg.main) {
11358
11489
  const srcPath = pkg.main.replace(/^\.\/dist\//, "./src/").replace(/\.js$/, ".ts");
11359
- if (existsSync8(resolve7(srcPath))) return resolve7(srcPath);
11490
+ if (existsSync9(resolve7(srcPath))) return resolve7(srcPath);
11360
11491
  }
11361
11492
  } catch {
11362
11493
  }
@@ -11370,7 +11501,7 @@ function findTsEntryFile() {
11370
11501
  ];
11371
11502
  for (const c2 of candidates) {
11372
11503
  const full = resolve7(c2);
11373
- if (existsSync8(full)) {
11504
+ if (existsSync9(full)) {
11374
11505
  try {
11375
11506
  const content = readFileSync10(full, "utf-8");
11376
11507
  if (content.includes("McpServer") || content.includes("McpServer")) {
@@ -11381,13 +11512,13 @@ function findTsEntryFile() {
11381
11512
  }
11382
11513
  }
11383
11514
  for (const c2 of candidates) {
11384
- if (existsSync8(resolve7(c2))) return resolve7(c2);
11515
+ if (existsSync9(resolve7(c2))) return resolve7(c2);
11385
11516
  }
11386
11517
  return null;
11387
11518
  }
11388
11519
  function detectPackageManager() {
11389
- if (existsSync8(resolve7("pnpm-lock.yaml"))) return "pnpm";
11390
- if (existsSync8(resolve7("yarn.lock"))) return "yarn";
11520
+ if (existsSync9(resolve7("pnpm-lock.yaml"))) return "pnpm";
11521
+ if (existsSync9(resolve7("yarn.lock"))) return "yarn";
11391
11522
  return "npm";
11392
11523
  }
11393
11524
  function installSdk() {
@@ -11530,7 +11661,7 @@ async function main2() {
11530
11661
  }
11531
11662
  log3(" Language: TypeScript");
11532
11663
  const entryFile = opts.file ? resolve7(opts.file) : findTsEntryFile();
11533
- if (!entryFile || !existsSync8(entryFile)) {
11664
+ if (!entryFile || !existsSync9(entryFile)) {
11534
11665
  log3(` Could not find entry file.${opts.file ? ` File not found: ${opts.file}` : ""}`);
11535
11666
  log3("");
11536
11667
  log3(" Specify it manually: --file <path>");
@@ -11543,7 +11674,7 @@ async function main2() {
11543
11674
  log3("");
11544
11675
  const backupPath = entryFile + ".solongate-backup";
11545
11676
  if (opts.restore) {
11546
- if (!existsSync8(backupPath)) {
11677
+ if (!existsSync9(backupPath)) {
11547
11678
  log3(" No backup found. Nothing to restore.");
11548
11679
  process.exit(1);
11549
11680
  }
@@ -11579,12 +11710,12 @@ async function main2() {
11579
11710
  log3(" To apply: npx @solongate/proxy inject");
11580
11711
  process.exit(0);
11581
11712
  }
11582
- if (!existsSync8(backupPath)) {
11713
+ if (!existsSync9(backupPath)) {
11583
11714
  copyFileSync(entryFile, backupPath);
11584
11715
  log3("");
11585
11716
  log3(` Backup: ${backupPath}`);
11586
11717
  }
11587
- writeFileSync5(entryFile, result.modified);
11718
+ writeFileSync6(entryFile, result.modified);
11588
11719
  log3("");
11589
11720
  log3(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
11590
11721
  log3(" \u2502 SolonGate SDK injected successfully! \u2502");
@@ -11613,8 +11744,8 @@ var init_inject = __esm({
11613
11744
 
11614
11745
  // src/create.ts
11615
11746
  var create_exports = {};
11616
- import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync6, existsSync as existsSync9 } from "fs";
11617
- import { resolve as resolve8, join as join11 } from "path";
11747
+ import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync7, existsSync as existsSync10 } from "fs";
11748
+ import { resolve as resolve8, join as join12 } from "path";
11618
11749
  import { execSync as execSync2 } from "child_process";
11619
11750
  function withSpinner(message, fn) {
11620
11751
  const frames = ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"];
@@ -11693,8 +11824,8 @@ EXAMPLES
11693
11824
  `);
11694
11825
  }
11695
11826
  function createProject(dir, name, _policy) {
11696
- writeFileSync6(
11697
- join11(dir, "package.json"),
11827
+ writeFileSync7(
11828
+ join12(dir, "package.json"),
11698
11829
  JSON.stringify(
11699
11830
  {
11700
11831
  name,
@@ -11723,8 +11854,8 @@ function createProject(dir, name, _policy) {
11723
11854
  2
11724
11855
  ) + "\n"
11725
11856
  );
11726
- writeFileSync6(
11727
- join11(dir, "tsconfig.json"),
11857
+ writeFileSync7(
11858
+ join12(dir, "tsconfig.json"),
11728
11859
  JSON.stringify(
11729
11860
  {
11730
11861
  compilerOptions: {
@@ -11744,9 +11875,9 @@ function createProject(dir, name, _policy) {
11744
11875
  2
11745
11876
  ) + "\n"
11746
11877
  );
11747
- mkdirSync5(join11(dir, "src"), { recursive: true });
11748
- writeFileSync6(
11749
- join11(dir, "src", "index.ts"),
11878
+ mkdirSync5(join12(dir, "src"), { recursive: true });
11879
+ writeFileSync7(
11880
+ join12(dir, "src", "index.ts"),
11750
11881
  `#!/usr/bin/env node
11751
11882
 
11752
11883
  console.log = (...args: unknown[]) => {
@@ -11787,8 +11918,8 @@ console.log('');
11787
11918
  console.log('Press Ctrl+C to stop.');
11788
11919
  `
11789
11920
  );
11790
- writeFileSync6(
11791
- join11(dir, ".mcp.json"),
11921
+ writeFileSync7(
11922
+ join12(dir, ".mcp.json"),
11792
11923
  JSON.stringify(
11793
11924
  {
11794
11925
  mcpServers: {
@@ -11805,13 +11936,13 @@ console.log('Press Ctrl+C to stop.');
11805
11936
  2
11806
11937
  ) + "\n"
11807
11938
  );
11808
- writeFileSync6(
11809
- join11(dir, ".env"),
11939
+ writeFileSync7(
11940
+ join12(dir, ".env"),
11810
11941
  `SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
11811
11942
  `
11812
11943
  );
11813
- writeFileSync6(
11814
- join11(dir, ".gitignore"),
11944
+ writeFileSync7(
11945
+ join12(dir, ".gitignore"),
11815
11946
  `node_modules/
11816
11947
  dist/
11817
11948
  *.solongate-backup
@@ -11825,7 +11956,7 @@ async function main3() {
11825
11956
  const opts = parseCreateArgs(process.argv);
11826
11957
  const dir = resolve8(opts.name);
11827
11958
  printBanner("Create MCP Server");
11828
- if (existsSync9(dir)) {
11959
+ if (existsSync10(dir)) {
11829
11960
  log3(` ${c.red}Error:${c.reset} Directory "${opts.name}" already exists.`);
11830
11961
  process.exit(1);
11831
11962
  }
@@ -11904,12 +12035,12 @@ var init_create = __esm({
11904
12035
 
11905
12036
  // src/pull-push.ts
11906
12037
  var pull_push_exports = {};
11907
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, existsSync as existsSync10 } from "fs";
12038
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, existsSync as existsSync11 } from "fs";
11908
12039
  import { resolve as resolve9 } from "path";
11909
12040
  function loadEnv() {
11910
12041
  if (process.env.SOLONGATE_API_KEY) return;
11911
12042
  const envPath = resolve9(".env");
11912
- if (!existsSync10(envPath)) return;
12043
+ if (!existsSync11(envPath)) return;
11913
12044
  try {
11914
12045
  const content = readFileSync11(envPath, "utf-8");
11915
12046
  for (const line of content.split("\n")) {
@@ -12099,7 +12230,7 @@ async function pull(apiKey, file, policyId) {
12099
12230
  const policy = await fetchCloudPolicy(apiKey, API_URL, policyId);
12100
12231
  const { id: _id, ...policyWithoutId } = policy;
12101
12232
  const json = JSON.stringify(policyWithoutId, null, 2) + "\n";
12102
- writeFileSync7(file, json, "utf-8");
12233
+ writeFileSync8(file, json, "utf-8");
12103
12234
  log5("");
12104
12235
  log5(green2(" Saved to: ") + file);
12105
12236
  log5(` ${dim2("Name:")} ${policy.name}`);
@@ -12111,7 +12242,7 @@ async function pull(apiKey, file, policyId) {
12111
12242
  log5("");
12112
12243
  }
12113
12244
  async function push(apiKey, file, policyId) {
12114
- if (!existsSync10(file)) {
12245
+ if (!existsSync11(file)) {
12115
12246
  log5(red2(`ERROR: File not found: ${file}`));
12116
12247
  process.exit(1);
12117
12248
  }
package/dist/tui/index.js CHANGED
@@ -150,10 +150,9 @@ function KeyHints({ hints }) {
150
150
  // src/tui/panels/Live.tsx
151
151
  import { Box as Box2, Text as Text2, useInput } from "ink";
152
152
  import TextInput from "ink-text-input";
153
- import { spawn } from "child_process";
154
- import { closeSync, mkdirSync, openSync, readSync, statSync, writeFileSync } from "fs";
153
+ import { closeSync, mkdirSync, openSync, readSync, statSync, writeFileSync as writeFileSync2 } from "fs";
155
154
  import { homedir as homedir3 } from "os";
156
- import { join as join3 } from "path";
155
+ import { join as join4 } from "path";
157
156
  import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
158
157
 
159
158
  // src/api-client/client.ts
@@ -489,6 +488,139 @@ function list4() {
489
488
  // src/api-client/index.ts
490
489
  var api = { policies: policies_exports, settings: settings_exports, stats: stats_exports, audit: audit_exports, agents: agents_exports, keys: keys_exports, mcp: mcp_exports };
491
490
 
491
+ // src/tui/notify.ts
492
+ import { spawn, execFileSync } from "child_process";
493
+ import { writeFileSync, existsSync as existsSync2 } from "fs";
494
+ import { join as join3 } from "path";
495
+ import { tmpdir } from "os";
496
+ var focusToken = null;
497
+ function captureFocusTarget() {
498
+ try {
499
+ if (process.platform === "linux") {
500
+ focusToken = (process.env.WINDOWID || "").trim() || null;
501
+ if (!focusToken) {
502
+ try {
503
+ focusToken = execFileSync("xdotool", ["getactivewindow"], { encoding: "utf-8", timeout: 1500, stdio: ["ignore", "pipe", "ignore"] }).trim() || null;
504
+ } catch {
505
+ focusToken = null;
506
+ }
507
+ }
508
+ } else if (process.platform === "win32") {
509
+ const ps = `Add-Type -Name W -Namespace N -MemberDefinition '[DllImport("user32.dll")]public static extern System.IntPtr GetForegroundWindow();' | Out-Null; [N.W]::GetForegroundWindow().ToInt64()`;
510
+ try {
511
+ focusToken = execFileSync("powershell", ["-NoProfile", "-NonInteractive", "-Command", ps], { encoding: "utf-8", timeout: 4e3, stdio: ["ignore", "pipe", "ignore"] }).trim() || null;
512
+ } catch {
513
+ focusToken = null;
514
+ }
515
+ }
516
+ } catch {
517
+ focusToken = null;
518
+ }
519
+ }
520
+ function desktopNotify(title, msg) {
521
+ try {
522
+ if (process.platform === "linux") linuxNotify(title, msg);
523
+ else if (process.platform === "win32") winNotify(title, msg);
524
+ else if (process.platform === "darwin") macNotify(title, msg);
525
+ } catch {
526
+ }
527
+ }
528
+ function linuxNotify(title, msg) {
529
+ let p;
530
+ try {
531
+ p = spawn("notify-send", ["--wait", "--action=default=Open", "-a", "SolonGate", title, msg], { stdio: ["ignore", "pipe", "ignore"] });
532
+ } catch {
533
+ return;
534
+ }
535
+ p.on("error", () => {
536
+ });
537
+ p.stdout?.on("data", (d) => {
538
+ if (String(d).trim() === "default") raiseLinuxWindow();
539
+ });
540
+ p.unref();
541
+ }
542
+ function raiseLinuxWindow() {
543
+ if (!focusToken) return;
544
+ try {
545
+ const x = spawn("xdotool", ["windowactivate", focusToken], { stdio: "ignore" });
546
+ x.on("error", () => {
547
+ try {
548
+ const hex = "0x" + Number(focusToken).toString(16);
549
+ const w = spawn("wmctrl", ["-ia", hex], { stdio: "ignore" });
550
+ w.on("error", () => {
551
+ });
552
+ w.unref();
553
+ } catch {
554
+ }
555
+ });
556
+ x.unref();
557
+ } catch {
558
+ }
559
+ }
560
+ var winScriptPath = null;
561
+ function ensureWinScript() {
562
+ if (winScriptPath && existsSync2(winScriptPath)) return winScriptPath;
563
+ const p = join3(tmpdir(), "solongate-toast.ps1");
564
+ const script = [
565
+ "param([string]$Title,[string]$Msg,[string]$Hwnd)",
566
+ "Add-Type -AssemblyName System.Windows.Forms",
567
+ "Add-Type -AssemblyName System.Drawing",
568
+ `Add-Type -Name Win -Namespace Sg -MemberDefinition '[DllImport("user32.dll")]public static extern bool SetForegroundWindow(System.IntPtr h);[DllImport("user32.dll")]public static extern bool ShowWindow(System.IntPtr h,int c);'`,
569
+ "$ni=New-Object System.Windows.Forms.NotifyIcon",
570
+ "$ni.Icon=[System.Drawing.SystemIcons]::Information",
571
+ "$ni.Visible=$true",
572
+ "$script:clicked=$false",
573
+ "$ni.add_BalloonTipClicked({$script:clicked=$true})",
574
+ "$ni.ShowBalloonTip(8000,$Title,$Msg,[System.Windows.Forms.ToolTipIcon]::Warning)",
575
+ "$sw=[System.Diagnostics.Stopwatch]::StartNew()",
576
+ "while($sw.Elapsed.TotalSeconds -lt 9 -and -not $script:clicked){[System.Windows.Forms.Application]::DoEvents();Start-Sleep -Milliseconds 100}",
577
+ "if($script:clicked -and $Hwnd){try{$h=[System.IntPtr][int64]$Hwnd;[Sg.Win]::ShowWindow($h,9)|Out-Null;[Sg.Win]::SetForegroundWindow($h)|Out-Null}catch{}}",
578
+ "$ni.Visible=$false;$ni.Dispose()"
579
+ ].join("\n");
580
+ try {
581
+ writeFileSync(p, script);
582
+ winScriptPath = p;
583
+ return p;
584
+ } catch {
585
+ return null;
586
+ }
587
+ }
588
+ function winNotify(title, msg) {
589
+ const script = ensureWinScript();
590
+ if (!script) return;
591
+ try {
592
+ const p = spawn("powershell", ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File", script, "-Title", title, "-Msg", msg, "-Hwnd", focusToken || ""], { stdio: "ignore", detached: true, windowsHide: true });
593
+ p.on("error", () => {
594
+ });
595
+ p.unref();
596
+ } catch {
597
+ }
598
+ }
599
+ function macBundleId() {
600
+ const prog = (process.env.TERM_PROGRAM || "").toLowerCase();
601
+ if (prog.includes("iterm")) return "com.googlecode.iterm2";
602
+ if (prog.includes("apple_terminal")) return "com.apple.Terminal";
603
+ if (prog.includes("vscode")) return "com.microsoft.VSCode";
604
+ return "com.apple.Terminal";
605
+ }
606
+ function macNotify(title, msg) {
607
+ try {
608
+ const p = spawn("terminal-notifier", ["-title", title, "-message", msg, "-activate", macBundleId(), "-sender", macBundleId()], { stdio: "ignore", detached: true });
609
+ p.on("error", () => {
610
+ try {
611
+ const esc = (s) => s.replace(/"/g, '\\"');
612
+ const a = spawn("osascript", ["-e", `display notification "${esc(msg)}" with title "${esc(title)}"`], { stdio: "ignore", detached: true });
613
+ a.on("error", () => {
614
+ });
615
+ a.unref();
616
+ } catch {
617
+ }
618
+ });
619
+ p.unref();
620
+ } catch {
621
+ }
622
+ }
623
+
492
624
  // src/tui/hooks.ts
493
625
  import { useCallback, useEffect, useRef, useState } from "react";
494
626
  function useLoader(fn, deps = []) {
@@ -535,8 +667,8 @@ var CONFIG = loadConfig();
535
667
  var SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
536
668
  var BG = "#12234f";
537
669
  var DIM_FLOOR = "#233457";
538
- var LOCAL_LOG = join3(homedir3(), ".solongate", "local-logs", "solongate-audit.jsonl");
539
- var RING = join3(process.cwd(), ".solongate", ".eval-ring.jsonl");
670
+ var LOCAL_LOG = join4(homedir3(), ".solongate", "local-logs", "solongate-audit.jsonl");
671
+ var RING = join4(process.cwd(), ".solongate", ".eval-ring.jsonl");
540
672
  var hhmmss = (ts) => {
541
673
  const d = new Date(ts);
542
674
  return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
@@ -681,7 +813,7 @@ function LivePanel({ active: active2 }) {
681
813
  const [sel, setSel] = useState2(0);
682
814
  const [actionMsg, setActionMsg] = useState2(null);
683
815
  const notifiedRef = useRef2(/* @__PURE__ */ new Set());
684
- const notifyReady = useRef2(false);
816
+ const openedAtRef = useRef2(Date.now());
685
817
  const activePolicyRef = useRef2(null);
686
818
  const [mode, setMode] = useState2("stream");
687
819
  const [pickIdx, setPickIdx] = useState2(0);
@@ -865,13 +997,7 @@ function LivePanel({ active: active2 }) {
865
997
  process.stdout.write("\x07");
866
998
  } catch {
867
999
  }
868
- try {
869
- const child = spawn("notify-send", ["-a", "SolonGate", title, msg], { stdio: "ignore", detached: true });
870
- child.on("error", () => {
871
- });
872
- child.unref();
873
- } catch {
874
- }
1000
+ desktopNotify(title, msg);
875
1001
  },
876
1002
  [pushLog]
877
1003
  );
@@ -889,14 +1015,10 @@ function LivePanel({ active: active2 }) {
889
1015
  useEffect2(() => {
890
1016
  if (!active2) return;
891
1017
  const notable = mergedRef.current.filter((e) => e.decision !== "ALLOW" || e.dlp || e.burst);
892
- if (!notifyReady.current) {
893
- for (const e of notable) notifiedRef.current.add(e.id);
894
- notifyReady.current = true;
895
- return;
896
- }
897
1018
  for (const e of notable) {
898
1019
  if (notifiedRef.current.has(e.id)) continue;
899
1020
  notifiedRef.current.add(e.id);
1021
+ if (!(e.at > openedAtRef.current)) continue;
900
1022
  const who = e.agent ? ` \xB7 ${truncate(e.agent, 20)}` : "";
901
1023
  if (e.dlp) fireAlert("sec:" + e.id, "DLP hit", `SECRET in ${e.tool}${who}: ${truncate(e.detail, 60)}`, "bad");
902
1024
  else if (e.decision !== "ALLOW") fireAlert("sec:" + e.id, "Call denied", `DENY ${e.tool}${who}: ${truncate(e.rule ?? e.detail, 60)}`, "bad");
@@ -1089,10 +1211,10 @@ function LivePanel({ active: active2 }) {
1089
1211
  else if (input === "x") toggleSignal("dlp");
1090
1212
  else if (input === "r") toggleSignal("ratelimit");
1091
1213
  else if (input === "e") {
1092
- const file = join3(homedir3(), ".solongate", "live-export.jsonl");
1214
+ const file = join4(homedir3(), ".solongate", "live-export.jsonl");
1093
1215
  try {
1094
- mkdirSync(join3(homedir3(), ".solongate"), { recursive: true });
1095
- writeFileSync(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
1216
+ mkdirSync(join4(homedir3(), ".solongate"), { recursive: true });
1217
+ writeFileSync2(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
1096
1218
  setActionMsg({ text: `\u2713 exported ${visibleDesc.length} lines \u2192 ${file}`, level: "ok", until: Date.now() + 6e3 });
1097
1219
  } catch (err) {
1098
1220
  setActionMsg({ text: "\u2717 export failed: " + (err instanceof Error ? err.message : String(err)), level: "bad", until: Date.now() + 6e3 });
@@ -2461,6 +2583,7 @@ async function launchTui() {
2461
2583
  process.stderr.write("Not logged in. Run `solongate login` first.\n");
2462
2584
  return;
2463
2585
  }
2586
+ captureFocusTarget();
2464
2587
  process.stdout.write("\x1B[?1049h\x1B[H");
2465
2588
  try {
2466
2589
  const { waitUntilExit } = render(/* @__PURE__ */ jsx10(App, {}));
@@ -0,0 +1,4 @@
1
+ /** Capture the current terminal window once, at TUI startup. Cheap + best-effort. */
2
+ export declare function captureFocusTarget(): void;
3
+ /** Fire a desktop toast; clicking it raises the dataroom terminal. */
4
+ export declare function desktopNotify(title: string, msg: string): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.81.15",
3
+ "version": "0.81.17",
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": {