@solongate/proxy 0.81.16 → 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) {
@@ -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
  );
@@ -7675,10 +7807,10 @@ function LivePanel({ active: active2 }) {
7675
7807
  else if (input === "x") toggleSignal("dlp");
7676
7808
  else if (input === "r") toggleSignal("ratelimit");
7677
7809
  else if (input === "e") {
7678
- const file = join6(homedir4(), ".solongate", "live-export.jsonl");
7810
+ const file = join7(homedir4(), ".solongate", "live-export.jsonl");
7679
7811
  try {
7680
- mkdirSync3(join6(homedir4(), ".solongate"), { recursive: true });
7681
- 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");
7682
7814
  setActionMsg({ text: `\u2713 exported ${visibleDesc.length} lines \u2192 ${file}`, level: "ok", until: Date.now() + 6e3 });
7683
7815
  } catch (err2) {
7684
7816
  setActionMsg({ text: "\u2717 export failed: " + (err2 instanceof Error ? err2.message : String(err2)), level: "bad", until: Date.now() + 6e3 });
@@ -7938,14 +8070,15 @@ var init_Live = __esm({
7938
8070
  "use strict";
7939
8071
  init_api_client();
7940
8072
  init_config2();
8073
+ init_notify();
7941
8074
  init_hooks();
7942
8075
  init_theme();
7943
8076
  CONFIG = loadConfig();
7944
8077
  SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
7945
8078
  BG = "#12234f";
7946
8079
  DIM_FLOOR = "#233457";
7947
- LOCAL_LOG = join6(homedir4(), ".solongate", "local-logs", "solongate-audit.jsonl");
7948
- 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");
7949
8082
  hhmmss = (ts) => {
7950
8083
  const d = new Date(ts);
7951
8084
  return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
@@ -9161,6 +9294,7 @@ async function launchTui() {
9161
9294
  process.stderr.write("Not logged in. Run `solongate login` first.\n");
9162
9295
  return;
9163
9296
  }
9297
+ captureFocusTarget();
9164
9298
  process.stdout.write("\x1B[?1049h\x1B[H");
9165
9299
  try {
9166
9300
  const { waitUntilExit } = render(/* @__PURE__ */ jsx10(App, {}));
@@ -9174,6 +9308,7 @@ var init_tui = __esm({
9174
9308
  "use strict";
9175
9309
  init_App();
9176
9310
  init_client();
9311
+ init_notify();
9177
9312
  }
9178
9313
  });
9179
9314
 
@@ -9865,9 +10000,9 @@ var init_agents2 = __esm({
9865
10000
  });
9866
10001
 
9867
10002
  // src/commands/doctor.ts
9868
- import { existsSync as existsSync4, statSync as statSync2 } from "fs";
10003
+ import { existsSync as existsSync5, statSync as statSync2 } from "fs";
9869
10004
  import { homedir as homedir5 } from "os";
9870
- import { join as join7 } from "path";
10005
+ import { join as join8 } from "path";
9871
10006
  async function run6(argv) {
9872
10007
  const { flags } = parse(argv);
9873
10008
  const json = flagBool(flags, "json");
@@ -9897,7 +10032,7 @@ async function run6(argv) {
9897
10032
  } catch {
9898
10033
  }
9899
10034
  }
9900
- if (existsSync4(LOCAL_LOG2)) {
10035
+ if (existsSync5(LOCAL_LOG2)) {
9901
10036
  const st = statSync2(LOCAL_LOG2);
9902
10037
  const ageMin = (Date.now() - st.mtimeMs) / 6e4;
9903
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"}` });
@@ -9927,14 +10062,14 @@ var init_doctor = __esm({
9927
10062
  init_api_client();
9928
10063
  init_format();
9929
10064
  init_args();
9930
- LOCAL_LOG2 = join7(homedir5(), ".solongate", "local-logs", "solongate-audit.jsonl");
10065
+ LOCAL_LOG2 = join8(homedir5(), ".solongate", "local-logs", "solongate-audit.jsonl");
9931
10066
  }
9932
10067
  });
9933
10068
 
9934
10069
  // src/commands/watch.ts
9935
- 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";
9936
10071
  import { homedir as homedir6 } from "os";
9937
- import { join as join8 } from "path";
10072
+ import { join as join9 } from "path";
9938
10073
  function tailLocal(file, maxBytes = 131072) {
9939
10074
  try {
9940
10075
  const size = statSync3(file).size;
@@ -9978,7 +10113,7 @@ async function run7(argv) {
9978
10113
  for (const r of rows) if (keep(r)) print(r, json);
9979
10114
  };
9980
10115
  const pollLocal = () => {
9981
- if (cloudOnly || !existsSync5(LOCAL_LOG3)) return;
10116
+ if (cloudOnly || !existsSync6(LOCAL_LOG3)) return;
9982
10117
  const rows = [];
9983
10118
  for (const line of tailLocal(LOCAL_LOG3)) {
9984
10119
  try {
@@ -10041,7 +10176,7 @@ var init_watch = __esm({
10041
10176
  init_cli_utils();
10042
10177
  init_args();
10043
10178
  init_format();
10044
- LOCAL_LOG3 = join8(homedir6(), ".solongate", "local-logs", "solongate-audit.jsonl");
10179
+ LOCAL_LOG3 = join9(homedir6(), ".solongate", "local-logs", "solongate-audit.jsonl");
10045
10180
  trunc = (s, n) => s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
10046
10181
  time = (ms) => new Date(ms).toTimeString().slice(0, 8);
10047
10182
  }
@@ -10343,32 +10478,32 @@ __export(global_install_exports, {
10343
10478
  runGlobalRestore: () => runGlobalRestore,
10344
10479
  unlockProtected: () => unlockProtected
10345
10480
  });
10346
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
10347
- 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";
10348
10483
  import { homedir as homedir7 } from "os";
10349
10484
  import { fileURLToPath } from "url";
10350
10485
  import { createInterface } from "readline";
10351
- import { execFileSync as execFileSync2 } from "child_process";
10486
+ import { execFileSync as execFileSync3 } from "child_process";
10352
10487
  function lockFile(file) {
10353
- if (!existsSync6(file)) return;
10488
+ if (!existsSync7(file)) return;
10354
10489
  try {
10355
10490
  if (process.platform === "win32") {
10356
10491
  try {
10357
- 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" });
10358
10493
  } catch {
10359
10494
  }
10360
10495
  try {
10361
- execFileSync2("attrib", ["+R", file], { stdio: "ignore" });
10496
+ execFileSync3("attrib", ["+R", file], { stdio: "ignore" });
10362
10497
  } catch {
10363
10498
  }
10364
10499
  } else if (process.platform === "darwin") {
10365
10500
  try {
10366
- execFileSync2("chflags", ["uchg", file], { stdio: "ignore" });
10501
+ execFileSync3("chflags", ["uchg", file], { stdio: "ignore" });
10367
10502
  } catch {
10368
10503
  }
10369
10504
  } else {
10370
10505
  try {
10371
- execFileSync2("chattr", ["+i", file], { stdio: "ignore" });
10506
+ execFileSync3("chattr", ["+i", file], { stdio: "ignore" });
10372
10507
  } catch {
10373
10508
  }
10374
10509
  }
@@ -10376,29 +10511,29 @@ function lockFile(file) {
10376
10511
  }
10377
10512
  }
10378
10513
  function unlockFile(file) {
10379
- if (!existsSync6(file)) return;
10514
+ if (!existsSync7(file)) return;
10380
10515
  try {
10381
10516
  if (process.platform === "win32") {
10382
10517
  try {
10383
- execFileSync2("icacls", [file, "/remove:d", "*S-1-1-0"], { stdio: "ignore" });
10518
+ execFileSync3("icacls", [file, "/remove:d", "*S-1-1-0"], { stdio: "ignore" });
10384
10519
  } catch {
10385
10520
  }
10386
10521
  try {
10387
- execFileSync2("icacls", [file, "/reset"], { stdio: "ignore" });
10522
+ execFileSync3("icacls", [file, "/reset"], { stdio: "ignore" });
10388
10523
  } catch {
10389
10524
  }
10390
10525
  try {
10391
- execFileSync2("attrib", ["-R", file], { stdio: "ignore" });
10526
+ execFileSync3("attrib", ["-R", file], { stdio: "ignore" });
10392
10527
  } catch {
10393
10528
  }
10394
10529
  } else if (process.platform === "darwin") {
10395
10530
  try {
10396
- execFileSync2("chflags", ["nouchg", file], { stdio: "ignore" });
10531
+ execFileSync3("chflags", ["nouchg", file], { stdio: "ignore" });
10397
10532
  } catch {
10398
10533
  }
10399
10534
  } else {
10400
10535
  try {
10401
- execFileSync2("chattr", ["-i", file], { stdio: "ignore" });
10536
+ execFileSync3("chattr", ["-i", file], { stdio: "ignore" });
10402
10537
  } catch {
10403
10538
  }
10404
10539
  }
@@ -10408,10 +10543,10 @@ function unlockFile(file) {
10408
10543
  function protectedTargets() {
10409
10544
  const p = globalPaths();
10410
10545
  return [
10411
- join9(p.hooksDir, "guard.mjs"),
10412
- join9(p.hooksDir, "audit.mjs"),
10413
- join9(p.hooksDir, "stop.mjs"),
10414
- 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"),
10415
10550
  p.configPath,
10416
10551
  p.settingsPath
10417
10552
  ];
@@ -10424,25 +10559,25 @@ function unlockProtected() {
10424
10559
  }
10425
10560
  function globalPaths() {
10426
10561
  const home = homedir7();
10427
- const sgDir = join9(home, ".solongate");
10428
- const hooksDir = join9(sgDir, "hooks");
10429
- const claudeDir = join9(home, ".claude");
10562
+ const sgDir = join10(home, ".solongate");
10563
+ const hooksDir = join10(sgDir, "hooks");
10564
+ const claudeDir = join10(home, ".claude");
10430
10565
  return {
10431
10566
  home,
10432
10567
  sgDir,
10433
10568
  hooksDir,
10434
10569
  claudeDir,
10435
- settingsPath: join9(claudeDir, "settings.json"),
10436
- backupPath: join9(claudeDir, "settings.solongate.bak"),
10437
- 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")
10438
10573
  };
10439
10574
  }
10440
10575
  function readHook(filename) {
10441
- return readFileSync7(join9(HOOKS_DIR, filename), "utf-8");
10576
+ return readFileSync7(join10(HOOKS_DIR, filename), "utf-8");
10442
10577
  }
10443
10578
  function readGuard() {
10444
- const bundled = join9(HOOKS_DIR, "guard.bundled.mjs");
10445
- 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");
10446
10581
  }
10447
10582
  function ask(question) {
10448
10583
  const rl = createInterface({ input: process.stdin, output: process.stderr });
@@ -10455,14 +10590,14 @@ function runGlobalRestore() {
10455
10590
  const p = globalPaths();
10456
10591
  unlockProtected();
10457
10592
  removeClaudeShim();
10458
- if (existsSync6(p.backupPath)) {
10459
- writeFileSync4(p.settingsPath, readFileSync7(p.backupPath, "utf-8"));
10593
+ if (existsSync7(p.backupPath)) {
10594
+ writeFileSync5(p.settingsPath, readFileSync7(p.backupPath, "utf-8"));
10460
10595
  console.log(` Restored ${p.settingsPath} from backup.`);
10461
- } else if (existsSync6(p.settingsPath)) {
10596
+ } else if (existsSync7(p.settingsPath)) {
10462
10597
  try {
10463
10598
  const s = JSON.parse(readFileSync7(p.settingsPath, "utf-8"));
10464
10599
  delete s.hooks;
10465
- writeFileSync4(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
10600
+ writeFileSync5(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
10466
10601
  console.log(` Removed SolonGate hooks from ${p.settingsPath}.`);
10467
10602
  } catch {
10468
10603
  }
@@ -10477,7 +10612,7 @@ function escapeRe(s) {
10477
10612
  function resolveRealClaude() {
10478
10613
  try {
10479
10614
  const finder = process.platform === "win32" ? "where" : "which";
10480
- 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);
10481
10616
  if (process.platform === "win32") {
10482
10617
  const low = (s) => s.toLowerCase();
10483
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;
@@ -10490,24 +10625,24 @@ function resolveRealClaude() {
10490
10625
  function shimTargets() {
10491
10626
  if (process.platform === "win32") {
10492
10627
  try {
10493
- 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();
10494
10629
  return prof ? [prof] : [];
10495
10630
  } catch {
10496
10631
  return [];
10497
10632
  }
10498
10633
  }
10499
- 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));
10500
10635
  }
10501
10636
  function writeShimBlock(file, block2) {
10502
10637
  const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
10503
- let content = existsSync6(file) ? readFileSync7(file, "utf-8") : "";
10638
+ let content = existsSync7(file) ? readFileSync7(file, "utf-8") : "";
10504
10639
  content = content.replace(re, "");
10505
10640
  if (block2) {
10506
10641
  if (content.length && !content.endsWith("\n")) content += "\n";
10507
10642
  content += block2 + "\n";
10508
10643
  }
10509
10644
  mkdirSync4(dirname(file), { recursive: true });
10510
- writeFileSync4(file, content);
10645
+ writeFileSync5(file, content);
10511
10646
  }
10512
10647
  function installClaudeShim(shieldPath) {
10513
10648
  const real = resolveRealClaude();
@@ -10561,19 +10696,19 @@ async function runGlobalInstall(opts = {}) {
10561
10696
  mkdirSync4(p.hooksDir, { recursive: true });
10562
10697
  mkdirSync4(p.claudeDir, { recursive: true });
10563
10698
  unlockProtected();
10564
- writeFileSync4(join9(p.hooksDir, "guard.mjs"), readGuard());
10565
- writeFileSync4(join9(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
10566
- writeFileSync4(join9(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
10567
- 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"));
10568
10703
  console.log(` Installed hooks \u2192 ${p.hooksDir}`);
10569
- installClaudeShim(join9(p.hooksDir, "shield.mjs"));
10570
- 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");
10571
10706
  console.log(` Wrote ${p.configPath}`);
10572
10707
  let existing = {};
10573
- if (existsSync6(p.settingsPath)) {
10708
+ if (existsSync7(p.settingsPath)) {
10574
10709
  const raw = readFileSync7(p.settingsPath, "utf-8");
10575
- if (!existsSync6(p.backupPath)) {
10576
- writeFileSync4(p.backupPath, raw);
10710
+ if (!existsSync7(p.backupPath)) {
10711
+ writeFileSync5(p.backupPath, raw);
10577
10712
  console.log(` Backed up existing settings \u2192 ${p.backupPath}`);
10578
10713
  }
10579
10714
  try {
@@ -10582,9 +10717,9 @@ async function runGlobalInstall(opts = {}) {
10582
10717
  existing = {};
10583
10718
  }
10584
10719
  }
10585
- const guardAbs = join9(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
10586
- const auditAbs = join9(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
10587
- 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, "/");
10588
10723
  const nodeBin = process.execPath.replace(/\\/g, "/");
10589
10724
  const call = process.platform === "win32" ? "& " : "";
10590
10725
  const hookCmd = (script) => `${call}"${nodeBin}" "${script}" claude-code "Claude Code"`;
@@ -10596,7 +10731,7 @@ async function runGlobalInstall(opts = {}) {
10596
10731
  Stop: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(stopAbs) }] }]
10597
10732
  }
10598
10733
  };
10599
- writeFileSync4(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
10734
+ writeFileSync5(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
10600
10735
  console.log(` Registered global hooks \u2192 ${p.settingsPath}`);
10601
10736
  if (process.env["SOLONGATE_OS_LOCK"] === "1") {
10602
10737
  lockProtected();
@@ -10776,7 +10911,7 @@ import { createServer, request as httpRequest } from "http";
10776
10911
  import { request as httpsRequest } from "https";
10777
10912
  import { spawn as spawn3 } from "child_process";
10778
10913
  import { URL as URL2 } from "url";
10779
- 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";
10780
10915
  import { resolve as resolve5 } from "path";
10781
10916
  import { homedir as homedir8 } from "os";
10782
10917
  function findCacheFile() {
@@ -10784,7 +10919,7 @@ function findCacheFile() {
10784
10919
  const envSel = process.env.SOLONGATE_AGENT_ID;
10785
10920
  if (envSel) {
10786
10921
  const f = resolve5(dir, ".policy-cache-" + envSel.replace(/[^a-zA-Z0-9_-]/g, "_") + ".json");
10787
- if (existsSync7(f)) return f;
10922
+ if (existsSync8(f)) return f;
10788
10923
  }
10789
10924
  let best = null, bestTs = -1;
10790
10925
  try {
@@ -10805,7 +10940,7 @@ function findCacheFile() {
10805
10940
  function loadCfg() {
10806
10941
  try {
10807
10942
  const f = findCacheFile();
10808
- if (f && existsSync7(f)) {
10943
+ if (f && existsSync8(f)) {
10809
10944
  const c2 = JSON.parse(readFileSync8(f, "utf-8"));
10810
10945
  const d = c2?.security?.dlpRedact;
10811
10946
  const g = c2?.security?.ghost;
@@ -11095,7 +11230,7 @@ __export(logs_server_exports, {
11095
11230
  });
11096
11231
  import { createServer as createServer2 } from "http";
11097
11232
  import { readFileSync as readFileSync9, statSync as statSync5 } from "fs";
11098
- import { resolve as resolve6, join as join10, isAbsolute } from "path";
11233
+ import { resolve as resolve6, join as join11, isAbsolute } from "path";
11099
11234
  import { homedir as homedir9 } from "os";
11100
11235
  import { readdirSync as readdirSync2 } from "fs";
11101
11236
  function allowedOrigins() {
@@ -11121,7 +11256,7 @@ async function findLogDir() {
11121
11256
  const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
11122
11257
  for (const f of files) {
11123
11258
  try {
11124
- const c2 = JSON.parse(readFileSync9(join10(base, f), "utf-8"));
11259
+ const c2 = JSON.parse(readFileSync9(join11(base, f), "utf-8"));
11125
11260
  const p = c2?.security?.localLogs?.path;
11126
11261
  if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
11127
11262
  } catch {
@@ -11130,7 +11265,7 @@ async function findLogDir() {
11130
11265
  } catch {
11131
11266
  }
11132
11267
  try {
11133
- const cfgRaw = readFileSync9(join10(base, "cloud-guard.json"), "utf-8");
11268
+ const cfgRaw = readFileSync9(join11(base, "cloud-guard.json"), "utf-8");
11134
11269
  const { apiKey, apiUrl } = JSON.parse(cfgRaw);
11135
11270
  if (apiKey) {
11136
11271
  const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
@@ -11159,7 +11294,7 @@ function setCors(req, res) {
11159
11294
  }
11160
11295
  function fileInfo(dir) {
11161
11296
  if (!dir) return { file: null, exists: false, size: 0, mtimeMs: 0 };
11162
- const file = join10(dir, LOG_FILENAME);
11297
+ const file = join11(dir, LOG_FILENAME);
11163
11298
  try {
11164
11299
  const st = statSync5(file);
11165
11300
  return { file, exists: true, size: st.size, mtimeMs: st.mtimeMs };
@@ -11272,7 +11407,7 @@ var init_logs_server = __esm({
11272
11407
 
11273
11408
  // src/inject.ts
11274
11409
  var inject_exports = {};
11275
- 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";
11276
11411
  import { resolve as resolve7 } from "path";
11277
11412
  import { execSync } from "child_process";
11278
11413
  function parseInjectArgs(argv) {
@@ -11330,7 +11465,7 @@ WHAT IT DOES
11330
11465
  `);
11331
11466
  }
11332
11467
  function detectProject() {
11333
- if (!existsSync8(resolve7("package.json"))) return false;
11468
+ if (!existsSync9(resolve7("package.json"))) return false;
11334
11469
  try {
11335
11470
  const pkg = JSON.parse(readFileSync10(resolve7("package.json"), "utf-8"));
11336
11471
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
@@ -11346,13 +11481,13 @@ function findTsEntryFile() {
11346
11481
  const binPath = typeof pkg.bin === "string" ? pkg.bin : Object.values(pkg.bin)[0];
11347
11482
  if (typeof binPath === "string") {
11348
11483
  const srcPath = binPath.replace(/^\.\/dist\//, "./src/").replace(/\.js$/, ".ts");
11349
- if (existsSync8(resolve7(srcPath))) return resolve7(srcPath);
11350
- if (existsSync8(resolve7(binPath))) return resolve7(binPath);
11484
+ if (existsSync9(resolve7(srcPath))) return resolve7(srcPath);
11485
+ if (existsSync9(resolve7(binPath))) return resolve7(binPath);
11351
11486
  }
11352
11487
  }
11353
11488
  if (pkg.main) {
11354
11489
  const srcPath = pkg.main.replace(/^\.\/dist\//, "./src/").replace(/\.js$/, ".ts");
11355
- if (existsSync8(resolve7(srcPath))) return resolve7(srcPath);
11490
+ if (existsSync9(resolve7(srcPath))) return resolve7(srcPath);
11356
11491
  }
11357
11492
  } catch {
11358
11493
  }
@@ -11366,7 +11501,7 @@ function findTsEntryFile() {
11366
11501
  ];
11367
11502
  for (const c2 of candidates) {
11368
11503
  const full = resolve7(c2);
11369
- if (existsSync8(full)) {
11504
+ if (existsSync9(full)) {
11370
11505
  try {
11371
11506
  const content = readFileSync10(full, "utf-8");
11372
11507
  if (content.includes("McpServer") || content.includes("McpServer")) {
@@ -11377,13 +11512,13 @@ function findTsEntryFile() {
11377
11512
  }
11378
11513
  }
11379
11514
  for (const c2 of candidates) {
11380
- if (existsSync8(resolve7(c2))) return resolve7(c2);
11515
+ if (existsSync9(resolve7(c2))) return resolve7(c2);
11381
11516
  }
11382
11517
  return null;
11383
11518
  }
11384
11519
  function detectPackageManager() {
11385
- if (existsSync8(resolve7("pnpm-lock.yaml"))) return "pnpm";
11386
- if (existsSync8(resolve7("yarn.lock"))) return "yarn";
11520
+ if (existsSync9(resolve7("pnpm-lock.yaml"))) return "pnpm";
11521
+ if (existsSync9(resolve7("yarn.lock"))) return "yarn";
11387
11522
  return "npm";
11388
11523
  }
11389
11524
  function installSdk() {
@@ -11526,7 +11661,7 @@ async function main2() {
11526
11661
  }
11527
11662
  log3(" Language: TypeScript");
11528
11663
  const entryFile = opts.file ? resolve7(opts.file) : findTsEntryFile();
11529
- if (!entryFile || !existsSync8(entryFile)) {
11664
+ if (!entryFile || !existsSync9(entryFile)) {
11530
11665
  log3(` Could not find entry file.${opts.file ? ` File not found: ${opts.file}` : ""}`);
11531
11666
  log3("");
11532
11667
  log3(" Specify it manually: --file <path>");
@@ -11539,7 +11674,7 @@ async function main2() {
11539
11674
  log3("");
11540
11675
  const backupPath = entryFile + ".solongate-backup";
11541
11676
  if (opts.restore) {
11542
- if (!existsSync8(backupPath)) {
11677
+ if (!existsSync9(backupPath)) {
11543
11678
  log3(" No backup found. Nothing to restore.");
11544
11679
  process.exit(1);
11545
11680
  }
@@ -11575,12 +11710,12 @@ async function main2() {
11575
11710
  log3(" To apply: npx @solongate/proxy inject");
11576
11711
  process.exit(0);
11577
11712
  }
11578
- if (!existsSync8(backupPath)) {
11713
+ if (!existsSync9(backupPath)) {
11579
11714
  copyFileSync(entryFile, backupPath);
11580
11715
  log3("");
11581
11716
  log3(` Backup: ${backupPath}`);
11582
11717
  }
11583
- writeFileSync5(entryFile, result.modified);
11718
+ writeFileSync6(entryFile, result.modified);
11584
11719
  log3("");
11585
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");
11586
11721
  log3(" \u2502 SolonGate SDK injected successfully! \u2502");
@@ -11609,8 +11744,8 @@ var init_inject = __esm({
11609
11744
 
11610
11745
  // src/create.ts
11611
11746
  var create_exports = {};
11612
- import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync6, existsSync as existsSync9 } from "fs";
11613
- 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";
11614
11749
  import { execSync as execSync2 } from "child_process";
11615
11750
  function withSpinner(message, fn) {
11616
11751
  const frames = ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"];
@@ -11689,8 +11824,8 @@ EXAMPLES
11689
11824
  `);
11690
11825
  }
11691
11826
  function createProject(dir, name, _policy) {
11692
- writeFileSync6(
11693
- join11(dir, "package.json"),
11827
+ writeFileSync7(
11828
+ join12(dir, "package.json"),
11694
11829
  JSON.stringify(
11695
11830
  {
11696
11831
  name,
@@ -11719,8 +11854,8 @@ function createProject(dir, name, _policy) {
11719
11854
  2
11720
11855
  ) + "\n"
11721
11856
  );
11722
- writeFileSync6(
11723
- join11(dir, "tsconfig.json"),
11857
+ writeFileSync7(
11858
+ join12(dir, "tsconfig.json"),
11724
11859
  JSON.stringify(
11725
11860
  {
11726
11861
  compilerOptions: {
@@ -11740,9 +11875,9 @@ function createProject(dir, name, _policy) {
11740
11875
  2
11741
11876
  ) + "\n"
11742
11877
  );
11743
- mkdirSync5(join11(dir, "src"), { recursive: true });
11744
- writeFileSync6(
11745
- join11(dir, "src", "index.ts"),
11878
+ mkdirSync5(join12(dir, "src"), { recursive: true });
11879
+ writeFileSync7(
11880
+ join12(dir, "src", "index.ts"),
11746
11881
  `#!/usr/bin/env node
11747
11882
 
11748
11883
  console.log = (...args: unknown[]) => {
@@ -11783,8 +11918,8 @@ console.log('');
11783
11918
  console.log('Press Ctrl+C to stop.');
11784
11919
  `
11785
11920
  );
11786
- writeFileSync6(
11787
- join11(dir, ".mcp.json"),
11921
+ writeFileSync7(
11922
+ join12(dir, ".mcp.json"),
11788
11923
  JSON.stringify(
11789
11924
  {
11790
11925
  mcpServers: {
@@ -11801,13 +11936,13 @@ console.log('Press Ctrl+C to stop.');
11801
11936
  2
11802
11937
  ) + "\n"
11803
11938
  );
11804
- writeFileSync6(
11805
- join11(dir, ".env"),
11939
+ writeFileSync7(
11940
+ join12(dir, ".env"),
11806
11941
  `SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
11807
11942
  `
11808
11943
  );
11809
- writeFileSync6(
11810
- join11(dir, ".gitignore"),
11944
+ writeFileSync7(
11945
+ join12(dir, ".gitignore"),
11811
11946
  `node_modules/
11812
11947
  dist/
11813
11948
  *.solongate-backup
@@ -11821,7 +11956,7 @@ async function main3() {
11821
11956
  const opts = parseCreateArgs(process.argv);
11822
11957
  const dir = resolve8(opts.name);
11823
11958
  printBanner("Create MCP Server");
11824
- if (existsSync9(dir)) {
11959
+ if (existsSync10(dir)) {
11825
11960
  log3(` ${c.red}Error:${c.reset} Directory "${opts.name}" already exists.`);
11826
11961
  process.exit(1);
11827
11962
  }
@@ -11900,12 +12035,12 @@ var init_create = __esm({
11900
12035
 
11901
12036
  // src/pull-push.ts
11902
12037
  var pull_push_exports = {};
11903
- 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";
11904
12039
  import { resolve as resolve9 } from "path";
11905
12040
  function loadEnv() {
11906
12041
  if (process.env.SOLONGATE_API_KEY) return;
11907
12042
  const envPath = resolve9(".env");
11908
- if (!existsSync10(envPath)) return;
12043
+ if (!existsSync11(envPath)) return;
11909
12044
  try {
11910
12045
  const content = readFileSync11(envPath, "utf-8");
11911
12046
  for (const line of content.split("\n")) {
@@ -12095,7 +12230,7 @@ async function pull(apiKey, file, policyId) {
12095
12230
  const policy = await fetchCloudPolicy(apiKey, API_URL, policyId);
12096
12231
  const { id: _id, ...policyWithoutId } = policy;
12097
12232
  const json = JSON.stringify(policyWithoutId, null, 2) + "\n";
12098
- writeFileSync7(file, json, "utf-8");
12233
+ writeFileSync8(file, json, "utf-8");
12099
12234
  log5("");
12100
12235
  log5(green2(" Saved to: ") + file);
12101
12236
  log5(` ${dim2("Name:")} ${policy.name}`);
@@ -12107,7 +12242,7 @@ async function pull(apiKey, file, policyId) {
12107
12242
  log5("");
12108
12243
  }
12109
12244
  async function push(apiKey, file, policyId) {
12110
- if (!existsSync10(file)) {
12245
+ if (!existsSync11(file)) {
12111
12246
  log5(red2(`ERROR: File not found: ${file}`));
12112
12247
  process.exit(1);
12113
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);
@@ -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
  );
@@ -1085,10 +1211,10 @@ function LivePanel({ active: active2 }) {
1085
1211
  else if (input === "x") toggleSignal("dlp");
1086
1212
  else if (input === "r") toggleSignal("ratelimit");
1087
1213
  else if (input === "e") {
1088
- const file = join3(homedir3(), ".solongate", "live-export.jsonl");
1214
+ const file = join4(homedir3(), ".solongate", "live-export.jsonl");
1089
1215
  try {
1090
- mkdirSync(join3(homedir3(), ".solongate"), { recursive: true });
1091
- 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");
1092
1218
  setActionMsg({ text: `\u2713 exported ${visibleDesc.length} lines \u2192 ${file}`, level: "ok", until: Date.now() + 6e3 });
1093
1219
  } catch (err) {
1094
1220
  setActionMsg({ text: "\u2717 export failed: " + (err instanceof Error ? err.message : String(err)), level: "bad", until: Date.now() + 6e3 });
@@ -2457,6 +2583,7 @@ async function launchTui() {
2457
2583
  process.stderr.write("Not logged in. Run `solongate login` first.\n");
2458
2584
  return;
2459
2585
  }
2586
+ captureFocusTarget();
2460
2587
  process.stdout.write("\x1B[?1049h\x1B[H");
2461
2588
  try {
2462
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.16",
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": {