@solongate/proxy 0.82.42 → 0.82.44

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
@@ -6187,6 +6187,476 @@ var init_cli_utils = __esm({
6187
6187
  }
6188
6188
  });
6189
6189
 
6190
+ // src/global-install.ts
6191
+ var global_install_exports = {};
6192
+ __export(global_install_exports, {
6193
+ clearGuardUpdateCheck: () => clearGuardUpdateCheck,
6194
+ globalPaths: () => globalPaths,
6195
+ guardHookOutdated: () => guardHookOutdated,
6196
+ installClaudeShim: () => installClaudeShim,
6197
+ installGlobalQuiet: () => installGlobalQuiet,
6198
+ installGlobalWithKey: () => installGlobalWithKey,
6199
+ installedGuardVersion: () => installedGuardVersion,
6200
+ isGuardInstalled: () => isGuardInstalled,
6201
+ lockProtected: () => lockProtected,
6202
+ removeClaudeShim: () => removeClaudeShim,
6203
+ runGlobalInstall: () => runGlobalInstall,
6204
+ runGlobalRestore: () => runGlobalRestore,
6205
+ uninstallGlobalQuiet: () => uninstallGlobalQuiet,
6206
+ unlockProtected: () => unlockProtected
6207
+ });
6208
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync3, rmSync as rmSync2 } from "fs";
6209
+ import { resolve as resolve3, join as join4, dirname } from "path";
6210
+ import { homedir as homedir2 } from "os";
6211
+ import { fileURLToPath } from "url";
6212
+ import { createInterface } from "readline";
6213
+ import { execFileSync as execFileSync2 } from "child_process";
6214
+ function lockFile(file) {
6215
+ if (!existsSync3(file)) return;
6216
+ try {
6217
+ if (process.platform === "win32") {
6218
+ try {
6219
+ execFileSync2("icacls", [file, "/deny", "*S-1-1-0:(WD,AD,DC,DE)"], { stdio: "ignore" });
6220
+ } catch {
6221
+ }
6222
+ try {
6223
+ execFileSync2("attrib", ["+R", file], { stdio: "ignore" });
6224
+ } catch {
6225
+ }
6226
+ } else if (process.platform === "darwin") {
6227
+ try {
6228
+ execFileSync2("chflags", ["uchg", file], { stdio: "ignore" });
6229
+ } catch {
6230
+ }
6231
+ } else {
6232
+ try {
6233
+ execFileSync2("chattr", ["+i", file], { stdio: "ignore" });
6234
+ } catch {
6235
+ }
6236
+ }
6237
+ } catch {
6238
+ }
6239
+ }
6240
+ function unlockFile(file) {
6241
+ if (!existsSync3(file)) return;
6242
+ try {
6243
+ if (process.platform === "win32") {
6244
+ try {
6245
+ execFileSync2("icacls", [file, "/remove:d", "*S-1-1-0"], { stdio: "ignore" });
6246
+ } catch {
6247
+ }
6248
+ try {
6249
+ execFileSync2("icacls", [file, "/reset"], { stdio: "ignore" });
6250
+ } catch {
6251
+ }
6252
+ try {
6253
+ execFileSync2("attrib", ["-R", file], { stdio: "ignore" });
6254
+ } catch {
6255
+ }
6256
+ } else if (process.platform === "darwin") {
6257
+ try {
6258
+ execFileSync2("chflags", ["nouchg", file], { stdio: "ignore" });
6259
+ } catch {
6260
+ }
6261
+ } else {
6262
+ try {
6263
+ execFileSync2("chattr", ["-i", file], { stdio: "ignore" });
6264
+ } catch {
6265
+ }
6266
+ }
6267
+ } catch {
6268
+ }
6269
+ }
6270
+ function protectedTargets() {
6271
+ const p = globalPaths();
6272
+ return [
6273
+ join4(p.hooksDir, "guard.mjs"),
6274
+ join4(p.hooksDir, "audit.mjs"),
6275
+ join4(p.hooksDir, "stop.mjs"),
6276
+ join4(p.hooksDir, "shield.mjs"),
6277
+ p.configPath,
6278
+ p.settingsPath,
6279
+ p.antigravityHooksPath
6280
+ ];
6281
+ }
6282
+ function lockProtected() {
6283
+ for (const f of protectedTargets()) lockFile(f);
6284
+ }
6285
+ function unlockProtected() {
6286
+ for (const f of protectedTargets()) unlockFile(f);
6287
+ }
6288
+ function globalPaths() {
6289
+ const home = homedir2();
6290
+ const sgDir = join4(home, ".solongate");
6291
+ const hooksDir = join4(sgDir, "hooks");
6292
+ const claudeDir = join4(home, ".claude");
6293
+ const antigravityDir = join4(home, ".gemini", "config");
6294
+ return {
6295
+ home,
6296
+ sgDir,
6297
+ hooksDir,
6298
+ claudeDir,
6299
+ antigravityDir,
6300
+ settingsPath: join4(claudeDir, "settings.json"),
6301
+ backupPath: join4(claudeDir, "settings.solongate.bak"),
6302
+ configPath: join4(sgDir, "cloud-guard.json"),
6303
+ // Antigravity reads global hooks from ~/.gemini/config/hooks.json. Only the
6304
+ // guard is registered there (PreToolUse); Antigravity's ALLOW-path audit is
6305
+ // covered by the passive session-log collector, not a hook.
6306
+ antigravityHooksPath: join4(antigravityDir, "hooks.json"),
6307
+ antigravityBackupPath: join4(antigravityDir, "hooks.solongate.bak")
6308
+ };
6309
+ }
6310
+ function clearGuardUpdateCheck() {
6311
+ try {
6312
+ rmSync2(join4(globalPaths().sgDir, ".hook-update-check"), { force: true });
6313
+ return true;
6314
+ } catch {
6315
+ return false;
6316
+ }
6317
+ }
6318
+ function readHook(filename) {
6319
+ return readFileSync4(join4(HOOKS_DIR, filename), "utf-8");
6320
+ }
6321
+ function firstAccountCredential() {
6322
+ try {
6323
+ const raw = JSON.parse(readFileSync4(join4(homedir2(), ".solongate", "accounts.json"), "utf-8"));
6324
+ if (Array.isArray(raw)) {
6325
+ const acc = raw.find((a) => a && typeof a.apiKey === "string" && a.apiKey);
6326
+ if (acc) return { apiKey: acc.apiKey, apiUrl: typeof acc.apiUrl === "string" ? acc.apiUrl : void 0 };
6327
+ }
6328
+ } catch {
6329
+ }
6330
+ return {};
6331
+ }
6332
+ function readGuard() {
6333
+ const bundled = join4(HOOKS_DIR, "guard.bundled.mjs");
6334
+ return existsSync3(bundled) ? readFileSync4(bundled, "utf-8") : readHook("guard.mjs");
6335
+ }
6336
+ function antigravityHookCommand(guardAbs) {
6337
+ const nodeBin = process.execPath.replace(/\\/g, "/");
6338
+ const call = process.platform === "win32" ? "& " : "";
6339
+ return `${call}"${nodeBin}" "${guardAbs.replace(/\\/g, "/")}" antigravity "Antigravity"`;
6340
+ }
6341
+ function installAntigravityGuard(p, guardAbs) {
6342
+ mkdirSync3(p.antigravityDir, { recursive: true });
6343
+ let existing = {};
6344
+ if (existsSync3(p.antigravityHooksPath)) {
6345
+ const raw = readFileSync4(p.antigravityHooksPath, "utf-8");
6346
+ if (!existsSync3(p.antigravityBackupPath)) writeFileSync3(p.antigravityBackupPath, raw);
6347
+ try {
6348
+ existing = JSON.parse(raw);
6349
+ } catch {
6350
+ existing = {};
6351
+ }
6352
+ }
6353
+ const merged = {
6354
+ ...existing,
6355
+ [ANTIGRAVITY_GROUP]: {
6356
+ PreToolUse: [{ matcher: "", hooks: [{ type: "command", command: antigravityHookCommand(guardAbs) }] }]
6357
+ }
6358
+ };
6359
+ writeFileSync3(p.antigravityHooksPath, JSON.stringify(merged, null, 2) + "\n");
6360
+ }
6361
+ function removeAntigravityGuard(p) {
6362
+ if (!existsSync3(p.antigravityHooksPath)) return;
6363
+ try {
6364
+ const s = JSON.parse(readFileSync4(p.antigravityHooksPath, "utf-8"));
6365
+ if (!(ANTIGRAVITY_GROUP in s)) return;
6366
+ delete s[ANTIGRAVITY_GROUP];
6367
+ writeFileSync3(p.antigravityHooksPath, JSON.stringify(s, null, 2) + "\n");
6368
+ } catch {
6369
+ }
6370
+ }
6371
+ function ask(question) {
6372
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
6373
+ return new Promise((res) => rl.question(question, (a) => {
6374
+ rl.close();
6375
+ res(a.trim());
6376
+ }));
6377
+ }
6378
+ function runGlobalRestore() {
6379
+ const p = globalPaths();
6380
+ unlockProtected();
6381
+ removeClaudeShim();
6382
+ removeAntigravityGuard(p);
6383
+ if (existsSync3(p.backupPath)) {
6384
+ writeFileSync3(p.settingsPath, readFileSync4(p.backupPath, "utf-8"));
6385
+ console.log(` Restored ${p.settingsPath} from backup.`);
6386
+ } else if (existsSync3(p.settingsPath)) {
6387
+ try {
6388
+ const s = JSON.parse(readFileSync4(p.settingsPath, "utf-8"));
6389
+ delete s.hooks;
6390
+ writeFileSync3(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
6391
+ console.log(` Removed SolonGate hooks from ${p.settingsPath}.`);
6392
+ } catch {
6393
+ }
6394
+ } else {
6395
+ console.log(" Nothing to restore \u2014 no global Claude Code settings found.");
6396
+ }
6397
+ console.log(" Global SolonGate enforcement uninstalled. Restart Claude Code.");
6398
+ }
6399
+ function installGlobalQuiet() {
6400
+ try {
6401
+ const p = globalPaths();
6402
+ let apiKey = process.env["SOLONGATE_API_KEY"] || "";
6403
+ let apiUrl = process.env["SOLONGATE_API_URL"] || "https://api.solongate.com";
6404
+ try {
6405
+ const cfg = JSON.parse(readFileSync4(p.configPath, "utf-8"));
6406
+ if (cfg && typeof cfg.apiKey === "string") apiKey = apiKey || cfg.apiKey;
6407
+ if (cfg && typeof cfg.apiUrl === "string") apiUrl = cfg.apiUrl;
6408
+ } catch {
6409
+ }
6410
+ if (!apiKey) {
6411
+ const acc = firstAccountCredential();
6412
+ if (acc.apiKey) {
6413
+ apiKey = acc.apiKey;
6414
+ if (acc.apiUrl) apiUrl = acc.apiUrl;
6415
+ }
6416
+ }
6417
+ if (!apiKey) return { ok: false, message: "no login on this device \u2014 add an account first (Accounts \u2192 + add)" };
6418
+ mkdirSync3(p.hooksDir, { recursive: true });
6419
+ mkdirSync3(p.claudeDir, { recursive: true });
6420
+ unlockProtected();
6421
+ writeFileSync3(join4(p.hooksDir, "guard.mjs"), readGuard());
6422
+ writeFileSync3(join4(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
6423
+ writeFileSync3(join4(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
6424
+ writeFileSync3(join4(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
6425
+ writeFileSync3(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
6426
+ let existing = {};
6427
+ if (existsSync3(p.settingsPath)) {
6428
+ const raw = readFileSync4(p.settingsPath, "utf-8");
6429
+ if (!existsSync3(p.backupPath)) writeFileSync3(p.backupPath, raw);
6430
+ try {
6431
+ existing = JSON.parse(raw);
6432
+ } catch {
6433
+ existing = {};
6434
+ }
6435
+ }
6436
+ const nodeBin = process.execPath.replace(/\\/g, "/");
6437
+ const call = process.platform === "win32" ? "& " : "";
6438
+ const hookCmd = (script) => `${call}"${nodeBin}" "${join4(p.hooksDir, script).replace(/\\/g, "/")}" claude-code "Claude Code"`;
6439
+ const merged = {
6440
+ ...existing,
6441
+ hooks: {
6442
+ PreToolUse: [{ matcher: "", hooks: [{ type: "command", command: hookCmd("guard.mjs") }] }],
6443
+ PostToolUse: [{ matcher: "", hooks: [{ type: "command", command: hookCmd("audit.mjs") }] }],
6444
+ Stop: [{ matcher: "", hooks: [{ type: "command", command: hookCmd("stop.mjs") }] }]
6445
+ }
6446
+ };
6447
+ writeFileSync3(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
6448
+ try {
6449
+ installAntigravityGuard(p, join4(p.hooksDir, "guard.mjs").replace(/\\/g, "/"));
6450
+ } catch {
6451
+ }
6452
+ if (process.env["SOLONGATE_OS_LOCK"] === "1") lockProtected();
6453
+ return { ok: true, message: "guard installed (open a new session)" };
6454
+ } catch (e) {
6455
+ return { ok: false, message: e instanceof Error ? e.message : String(e) };
6456
+ }
6457
+ }
6458
+ function installedGuardVersion() {
6459
+ try {
6460
+ const p = globalPaths();
6461
+ const s = readFileSync4(join4(p.hooksDir, "guard.mjs"), "utf-8");
6462
+ const m = s.match(/HOOK_VERSION\s*=\s*(\d+)/);
6463
+ return m ? parseInt(m[1], 10) : null;
6464
+ } catch {
6465
+ return null;
6466
+ }
6467
+ }
6468
+ function guardHookOutdated() {
6469
+ try {
6470
+ const p = globalPaths();
6471
+ return readFileSync4(join4(p.hooksDir, "guard.mjs"), "utf-8") !== readGuard();
6472
+ } catch {
6473
+ return false;
6474
+ }
6475
+ }
6476
+ function isGuardInstalled() {
6477
+ try {
6478
+ const p = globalPaths();
6479
+ if (!existsSync3(p.settingsPath)) return false;
6480
+ const s = JSON.parse(readFileSync4(p.settingsPath, "utf-8"));
6481
+ return !!s.hooks && JSON.stringify(s.hooks).includes(".solongate");
6482
+ } catch {
6483
+ return false;
6484
+ }
6485
+ }
6486
+ function uninstallGlobalQuiet() {
6487
+ try {
6488
+ const p = globalPaths();
6489
+ unlockProtected();
6490
+ removeClaudeShim();
6491
+ try {
6492
+ removeAntigravityGuard(p);
6493
+ } catch {
6494
+ }
6495
+ if (!existsSync3(p.settingsPath)) return { ok: true, message: "guard removed (open a new session)" };
6496
+ const s = JSON.parse(readFileSync4(p.settingsPath, "utf-8"));
6497
+ delete s.hooks;
6498
+ writeFileSync3(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
6499
+ return { ok: true, message: "guard removed (open a new session)" };
6500
+ } catch (e) {
6501
+ return { ok: false, message: e instanceof Error ? e.message : String(e) };
6502
+ }
6503
+ }
6504
+ function escapeRe(s) {
6505
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6506
+ }
6507
+ function resolveRealClaude() {
6508
+ try {
6509
+ const finder = process.platform === "win32" ? "where" : "which";
6510
+ const out2 = execFileSync2(finder, ["claude"], { encoding: "utf-8" }).split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
6511
+ if (process.platform === "win32") {
6512
+ const low = (s) => s.toLowerCase();
6513
+ 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;
6514
+ }
6515
+ return out2[0] || null;
6516
+ } catch {
6517
+ return null;
6518
+ }
6519
+ }
6520
+ function shimTargets() {
6521
+ if (process.platform === "win32") {
6522
+ try {
6523
+ const prof = execFileSync2("powershell", ["-NoProfile", "-Command", "$PROFILE.CurrentUserAllHosts"], { encoding: "utf-8" }).trim();
6524
+ return prof ? [prof] : [];
6525
+ } catch {
6526
+ return [];
6527
+ }
6528
+ }
6529
+ return [".bashrc", ".zshrc", ".profile"].map((f) => join4(homedir2(), f)).filter((f) => existsSync3(f));
6530
+ }
6531
+ function writeShimBlock(file, block2) {
6532
+ const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
6533
+ let content = existsSync3(file) ? readFileSync4(file, "utf-8") : "";
6534
+ content = content.replace(re, "");
6535
+ if (block2) {
6536
+ if (content.length && !content.endsWith("\n")) content += "\n";
6537
+ content += block2 + "\n";
6538
+ }
6539
+ mkdirSync3(dirname(file), { recursive: true });
6540
+ writeFileSync3(file, content);
6541
+ }
6542
+ function installClaudeShim(shieldPath) {
6543
+ const real = resolveRealClaude();
6544
+ if (!real) {
6545
+ console.log(" (Claude Code not found on PATH \u2014 skipped auto-shield. Install it, then re-open `solongate` \u2192 Settings \u2192 guard.)");
6546
+ return;
6547
+ }
6548
+ const node = process.execPath.replace(/\\/g, "/");
6549
+ const shield = shieldPath.replace(/\\/g, "/");
6550
+ const win = process.platform === "win32";
6551
+ const block2 = win ? `${SHIM_BEGIN}
6552
+ function claude { & "${node}" "${shield}" -- "${real}" @args }
6553
+ ${SHIM_END}` : `${SHIM_BEGIN}
6554
+ claude() { "${node}" "${shield}" -- "${real}" "$@"; }
6555
+ ${SHIM_END}`;
6556
+ const targets = shimTargets();
6557
+ if (targets.length === 0) return;
6558
+ for (const file of targets) {
6559
+ try {
6560
+ writeShimBlock(file, block2);
6561
+ } catch {
6562
+ }
6563
+ }
6564
+ }
6565
+ function removeClaudeShim() {
6566
+ for (const file of shimTargets()) {
6567
+ try {
6568
+ writeShimBlock(file, null);
6569
+ } catch {
6570
+ }
6571
+ }
6572
+ }
6573
+ async function runGlobalInstall(opts = {}) {
6574
+ const p = globalPaths();
6575
+ let apiKey = opts.apiKey || process.env["SOLONGATE_API_KEY"] || "";
6576
+ if (!apiKey || apiKey === "sg_live_your_key_here") {
6577
+ try {
6578
+ const cfg = JSON.parse(readFileSync4(p.configPath, "utf-8"));
6579
+ if (cfg && typeof cfg.apiKey === "string") apiKey = cfg.apiKey;
6580
+ } catch {
6581
+ }
6582
+ }
6583
+ if (!apiKey || apiKey === "sg_live_your_key_here") {
6584
+ const acc = firstAccountCredential();
6585
+ if (acc.apiKey) apiKey = acc.apiKey;
6586
+ }
6587
+ if (!apiKey || apiKey === "sg_live_your_key_here") {
6588
+ apiKey = await ask(" Enter your SolonGate API key (sg_live_\u2026 from https://dashboard.solongate.com): ");
6589
+ }
6590
+ if (!apiKey.startsWith("sg_live_") && !apiKey.startsWith("sg_test_")) {
6591
+ console.log(" Invalid API key. Must start with sg_live_ or sg_test_");
6592
+ process.exit(1);
6593
+ }
6594
+ const apiUrl = opts.apiUrl || process.env["SOLONGATE_API_URL"] || "https://api.solongate.com";
6595
+ mkdirSync3(p.hooksDir, { recursive: true });
6596
+ mkdirSync3(p.claudeDir, { recursive: true });
6597
+ unlockProtected();
6598
+ writeFileSync3(join4(p.hooksDir, "guard.mjs"), readGuard());
6599
+ writeFileSync3(join4(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
6600
+ writeFileSync3(join4(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
6601
+ writeFileSync3(join4(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
6602
+ console.log(` Installed hooks \u2192 ${p.hooksDir}`);
6603
+ installClaudeShim(join4(p.hooksDir, "shield.mjs"));
6604
+ writeFileSync3(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
6605
+ console.log(` Wrote ${p.configPath}`);
6606
+ let existing = {};
6607
+ if (existsSync3(p.settingsPath)) {
6608
+ const raw = readFileSync4(p.settingsPath, "utf-8");
6609
+ if (!existsSync3(p.backupPath)) {
6610
+ writeFileSync3(p.backupPath, raw);
6611
+ console.log(` Backed up existing settings \u2192 ${p.backupPath}`);
6612
+ }
6613
+ try {
6614
+ existing = JSON.parse(raw);
6615
+ } catch {
6616
+ existing = {};
6617
+ }
6618
+ }
6619
+ const guardAbs = join4(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
6620
+ const auditAbs = join4(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
6621
+ const stopAbs = join4(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
6622
+ const nodeBin = process.execPath.replace(/\\/g, "/");
6623
+ const call = process.platform === "win32" ? "& " : "";
6624
+ const hookCmd = (script) => `${call}"${nodeBin}" "${script}" claude-code "Claude Code"`;
6625
+ const merged = {
6626
+ ...existing,
6627
+ hooks: {
6628
+ PreToolUse: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(guardAbs) }] }],
6629
+ PostToolUse: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(auditAbs) }] }],
6630
+ Stop: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(stopAbs) }] }]
6631
+ }
6632
+ };
6633
+ writeFileSync3(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
6634
+ console.log(` Registered global hooks \u2192 ${p.settingsPath}`);
6635
+ try {
6636
+ installAntigravityGuard(p, guardAbs);
6637
+ console.log(` Registered Antigravity CLI guard \u2192 ${p.antigravityHooksPath}`);
6638
+ } catch {
6639
+ }
6640
+ if (process.env["SOLONGATE_OS_LOCK"] === "1") {
6641
+ lockProtected();
6642
+ console.log(" Locked protection files (OS-level read-only/immutable).");
6643
+ }
6644
+ }
6645
+ async function installGlobalWithKey(apiKey, apiUrl) {
6646
+ await runGlobalInstall({ apiKey, apiUrl });
6647
+ }
6648
+ var __dirname, HOOKS_DIR, ANTIGRAVITY_GROUP, SHIM_BEGIN, SHIM_END;
6649
+ var init_global_install = __esm({
6650
+ "src/global-install.ts"() {
6651
+ "use strict";
6652
+ __dirname = dirname(fileURLToPath(import.meta.url));
6653
+ HOOKS_DIR = resolve3(__dirname, "..", "hooks");
6654
+ ANTIGRAVITY_GROUP = "solongate-guard";
6655
+ SHIM_BEGIN = "# >>> SolonGate shield (auto secret redaction) >>>";
6656
+ SHIM_END = "# <<< SolonGate shield <<<";
6657
+ }
6658
+ });
6659
+
6190
6660
  // src/self-update.ts
6191
6661
  var self_update_exports = {};
6192
6662
  __export(self_update_exports, {
@@ -6194,16 +6664,17 @@ __export(self_update_exports, {
6194
6664
  latestVersion: () => latestVersion,
6195
6665
  maybeSelfUpdate: () => maybeSelfUpdate,
6196
6666
  newerThan: () => newerThan,
6667
+ runUpdateCommand: () => runUpdateCommand,
6197
6668
  tuiUpdateFlow: () => tuiUpdateFlow
6198
6669
  });
6199
6670
  import { execFile, spawn } from "child_process";
6200
- import { mkdirSync as mkdirSync3, openSync, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
6201
- import { homedir as homedir2 } from "os";
6202
- import { dirname, join as join4 } from "path";
6203
- import { fileURLToPath } from "url";
6671
+ import { mkdirSync as mkdirSync4, openSync, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
6672
+ import { homedir as homedir3 } from "os";
6673
+ import { dirname as dirname2, join as join5 } from "path";
6674
+ import { fileURLToPath as fileURLToPath2 } from "url";
6204
6675
  function readState() {
6205
6676
  try {
6206
- const s = JSON.parse(readFileSync4(STATE_FILE, "utf-8"));
6677
+ const s = JSON.parse(readFileSync5(STATE_FILE, "utf-8"));
6207
6678
  return s && typeof s === "object" ? s : {};
6208
6679
  } catch {
6209
6680
  return {};
@@ -6211,14 +6682,14 @@ function readState() {
6211
6682
  }
6212
6683
  function writeState(s) {
6213
6684
  try {
6214
- mkdirSync3(join4(homedir2(), ".solongate"), { recursive: true });
6215
- writeFileSync3(STATE_FILE, JSON.stringify(s));
6685
+ mkdirSync4(join5(homedir3(), ".solongate"), { recursive: true });
6686
+ writeFileSync4(STATE_FILE, JSON.stringify(s));
6216
6687
  } catch {
6217
6688
  }
6218
6689
  }
6219
6690
  function currentVersion() {
6220
6691
  try {
6221
- const pkg = JSON.parse(readFileSync4(join4(dirname(fileURLToPath(import.meta.url)), "..", "package.json"), "utf-8"));
6692
+ const pkg = JSON.parse(readFileSync5(join5(dirname2(fileURLToPath2(import.meta.url)), "..", "package.json"), "utf-8"));
6222
6693
  return pkg.version ?? "0.0.0";
6223
6694
  } catch {
6224
6695
  return "0.0.0";
@@ -6254,7 +6725,7 @@ async function fetchLatest() {
6254
6725
  }
6255
6726
  function spawnGlobalInstall(version) {
6256
6727
  try {
6257
- mkdirSync3(join4(homedir2(), ".solongate"), { recursive: true });
6728
+ mkdirSync4(join5(homedir3(), ".solongate"), { recursive: true });
6258
6729
  const log3 = openSync(LOG_FILE, "a");
6259
6730
  const p = spawn("npm", ["install", "-g", `${PKG}@${version}`], {
6260
6731
  stdio: ["ignore", log3, log3],
@@ -6270,17 +6741,45 @@ function spawnGlobalInstall(version) {
6270
6741
  return false;
6271
6742
  }
6272
6743
  }
6273
- function runGlobalInstall(version) {
6744
+ async function runUpdateCommand() {
6745
+ const out2 = (s) => void process.stderr.write(s + "\n");
6746
+ const cur = currentVersion();
6747
+ out2(` current ${cur}`);
6748
+ const latest = await latestVersion();
6749
+ if (!latest) {
6750
+ out2(" could not reach the npm registry. Try again, or: npm i -g @solongate/proxy@latest");
6751
+ return 1;
6752
+ }
6753
+ if (newerThan(latest, cur)) {
6754
+ out2(` updating ${cur} -> ${latest} ...`);
6755
+ const ok = await runGlobalInstall2(latest);
6756
+ if (!ok) {
6757
+ out2(" update failed. Try: npm i -g @solongate/proxy@latest");
6758
+ return 1;
6759
+ }
6760
+ out2(` \u2713 updated to ${latest} (open a new session to use it)`);
6761
+ } else {
6762
+ out2(` \u2713 already up to date`);
6763
+ }
6764
+ try {
6765
+ const { installGlobalQuiet: installGlobalQuiet2, installedGuardVersion: installedGuardVersion2 } = await Promise.resolve().then(() => (init_global_install(), global_install_exports));
6766
+ const r = installGlobalQuiet2();
6767
+ out2(r.ok ? ` \u2713 guard hooks refreshed (v${installedGuardVersion2() ?? "?"})` : ` guard: ${r.message}`);
6768
+ } catch {
6769
+ }
6770
+ return 0;
6771
+ }
6772
+ function runGlobalInstall2(version) {
6274
6773
  return new Promise((resolve6) => {
6275
6774
  try {
6276
- mkdirSync3(join4(homedir2(), ".solongate"), { recursive: true });
6775
+ mkdirSync4(join5(homedir3(), ".solongate"), { recursive: true });
6277
6776
  execFile(
6278
6777
  "npm",
6279
6778
  ["install", "-g", `${PKG}@${version}`],
6280
6779
  { timeout: 3e5, windowsHide: true, shell: process.platform === "win32" },
6281
6780
  (err2, stdout, stderr) => {
6282
6781
  try {
6283
- writeFileSync3(LOG_FILE, `${(/* @__PURE__ */ new Date()).toISOString()} install ${version}: ${err2 ? "FAILED" : "ok"}
6782
+ writeFileSync4(LOG_FILE, `${(/* @__PURE__ */ new Date()).toISOString()} install ${version}: ${err2 ? "FAILED" : "ok"}
6284
6783
  ${stdout}
6285
6784
  ${stderr}
6286
6785
  `, { flag: "a" });
@@ -6306,7 +6805,7 @@ async function tuiUpdateFlow(onStatus) {
6306
6805
  return;
6307
6806
  }
6308
6807
  onStatus({ kind: "updating", version: latest });
6309
- if (await runGlobalInstall(latest)) {
6808
+ if (await runGlobalInstall2(latest)) {
6310
6809
  writeState({ ...readState(), installed: latest });
6311
6810
  onStatus({ kind: "updated", version: latest });
6312
6811
  }
@@ -6345,8 +6844,8 @@ var init_self_update = __esm({
6345
6844
  PKG = "@solongate/proxy";
6346
6845
  CHECK_EVERY_MS = 30 * 60 * 1e3;
6347
6846
  ATTEMPT_EVERY_MS = 6 * 60 * 60 * 1e3;
6348
- STATE_FILE = join4(homedir2(), ".solongate", ".self-update.json");
6349
- LOG_FILE = join4(homedir2(), ".solongate", "self-update.log");
6847
+ STATE_FILE = join5(homedir3(), ".solongate", ".self-update.json");
6848
+ LOG_FILE = join5(homedir3(), ".solongate", "self-update.log");
6350
6849
  }
6351
6850
  });
6352
6851
 
@@ -6361,13 +6860,13 @@ __export(logs_server_daemon_exports, {
6361
6860
  stopLogsServerDaemon: () => stopLogsServerDaemon
6362
6861
  });
6363
6862
  import { spawn as spawn2 } from "child_process";
6364
- import { mkdirSync as mkdirSync4, openSync as openSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
6365
- import { homedir as homedir3 } from "os";
6366
- import { dirname as dirname2, join as join5 } from "path";
6367
- import { fileURLToPath as fileURLToPath2 } from "url";
6863
+ import { mkdirSync as mkdirSync5, openSync as openSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
6864
+ import { homedir as homedir4 } from "os";
6865
+ import { dirname as dirname3, join as join6 } from "path";
6866
+ import { fileURLToPath as fileURLToPath3 } from "url";
6368
6867
  function readState2() {
6369
6868
  try {
6370
- const s = JSON.parse(readFileSync5(STATE_FILE2, "utf-8"));
6869
+ const s = JSON.parse(readFileSync6(STATE_FILE2, "utf-8"));
6371
6870
  return s && typeof s === "object" ? s : {};
6372
6871
  } catch {
6373
6872
  return {};
@@ -6375,8 +6874,8 @@ function readState2() {
6375
6874
  }
6376
6875
  function writeState2(s) {
6377
6876
  try {
6378
- mkdirSync4(DIR, { recursive: true });
6379
- writeFileSync4(STATE_FILE2, JSON.stringify(s));
6877
+ mkdirSync5(DIR, { recursive: true });
6878
+ writeFileSync5(STATE_FILE2, JSON.stringify(s));
6380
6879
  } catch {
6381
6880
  }
6382
6881
  }
@@ -6404,13 +6903,16 @@ function startLogsServerDaemon() {
6404
6903
  return { ...cur, desired: "on" };
6405
6904
  }
6406
6905
  try {
6407
- mkdirSync4(DIR, { recursive: true });
6906
+ mkdirSync5(DIR, { recursive: true });
6408
6907
  const log3 = openSync2(LOG_FILE2, "a");
6409
- const cli = join5(dirname2(fileURLToPath2(import.meta.url)), "index.js");
6908
+ const cli = join6(dirname3(fileURLToPath3(import.meta.url)), "index.js");
6410
6909
  const p = spawn2(process.execPath, [cli, "logs-server"], {
6411
6910
  detached: true,
6412
6911
  stdio: ["ignore", log3, log3],
6413
- windowsHide: true
6912
+ windowsHide: true,
6913
+ // Spawned by US, not by an agent: exempt from the human-only TTY gate
6914
+ // (a detached daemon has no terminal by definition).
6915
+ env: { ...process.env, SOLONGATE_INTERNAL: "1" }
6414
6916
  });
6415
6917
  p.on("error", () => {
6416
6918
  });
@@ -6443,22 +6945,22 @@ var DIR, STATE_FILE2, LOG_FILE2, LOGS_SERVER_PORT;
6443
6945
  var init_logs_server_daemon = __esm({
6444
6946
  "src/logs-server-daemon.ts"() {
6445
6947
  "use strict";
6446
- DIR = join5(homedir3(), ".solongate");
6447
- STATE_FILE2 = join5(DIR, ".logs-server.json");
6448
- LOG_FILE2 = join5(DIR, "logs-server.log");
6948
+ DIR = join6(homedir4(), ".solongate");
6949
+ STATE_FILE2 = join6(DIR, ".logs-server.json");
6950
+ LOG_FILE2 = join6(DIR, "logs-server.log");
6449
6951
  LOGS_SERVER_PORT = 8788;
6450
6952
  }
6451
6953
  });
6452
6954
 
6453
6955
  // src/tui/config.ts
6454
- import { readFileSync as readFileSync6 } from "fs";
6455
- import { homedir as homedir4 } from "os";
6456
- import { join as join6 } from "path";
6956
+ import { readFileSync as readFileSync7 } from "fs";
6957
+ import { homedir as homedir5 } from "os";
6958
+ import { join as join7 } from "path";
6457
6959
  function loadConfig() {
6458
6960
  if (cached) return cached;
6459
6961
  const defaults = { notifications: true };
6460
6962
  try {
6461
- const raw = readFileSync6(join6(homedir4(), ".solongate", "tui-config.json"), "utf-8");
6963
+ const raw = readFileSync7(join7(homedir5(), ".solongate", "tui-config.json"), "utf-8");
6462
6964
  const j = JSON.parse(raw);
6463
6965
  cached = {
6464
6966
  notifications: j.notifications !== false,
@@ -6629,9 +7131,9 @@ var init_components = __esm({
6629
7131
  });
6630
7132
 
6631
7133
  // src/tui/local-log.ts
6632
- import { closeSync, openSync as openSync3, readFileSync as readFileSync7, readSync, statSync, writeFileSync as writeFileSync5 } from "fs";
6633
- import { homedir as homedir5 } from "os";
6634
- import { join as join7 } from "path";
7134
+ import { closeSync, openSync as openSync3, readFileSync as readFileSync8, readSync, statSync, writeFileSync as writeFileSync6 } from "fs";
7135
+ import { homedir as homedir6 } from "os";
7136
+ import { join as join8 } from "path";
6635
7137
  function tailLines(file, maxBytes = 131072) {
6636
7138
  try {
6637
7139
  const size = statSync(file).size;
@@ -6649,7 +7151,7 @@ function tailLines(file, maxBytes = 131072) {
6649
7151
  }
6650
7152
  function deleteLocalEntry(at, tool, session) {
6651
7153
  try {
6652
- const lines = readFileSync7(LOCAL_LOG, "utf-8").split("\n");
7154
+ const lines = readFileSync8(LOCAL_LOG, "utf-8").split("\n");
6653
7155
  let removed = 0;
6654
7156
  const kept = lines.filter((line) => {
6655
7157
  if (!line.trim()) return false;
@@ -6665,7 +7167,7 @@ function deleteLocalEntry(at, tool, session) {
6665
7167
  }
6666
7168
  return true;
6667
7169
  });
6668
- if (removed) writeFileSync5(LOCAL_LOG, kept.length ? kept.join("\n") + "\n" : "");
7170
+ if (removed) writeFileSync6(LOCAL_LOG, kept.length ? kept.join("\n") + "\n" : "");
6669
7171
  return removed;
6670
7172
  } catch {
6671
7173
  return 0;
@@ -6673,8 +7175,8 @@ function deleteLocalEntry(at, tool, session) {
6673
7175
  }
6674
7176
  function clearLocalLog() {
6675
7177
  try {
6676
- const n = readFileSync7(LOCAL_LOG, "utf-8").split("\n").filter(Boolean).length;
6677
- writeFileSync5(LOCAL_LOG, "");
7178
+ const n = readFileSync8(LOCAL_LOG, "utf-8").split("\n").filter(Boolean).length;
7179
+ writeFileSync6(LOCAL_LOG, "");
6678
7180
  return n;
6679
7181
  } catch {
6680
7182
  return 0;
@@ -6705,20 +7207,20 @@ var LOCAL_LOG, DLP_REASON, RL_REASON;
6705
7207
  var init_local_log = __esm({
6706
7208
  "src/tui/local-log.ts"() {
6707
7209
  "use strict";
6708
- LOCAL_LOG = join7(homedir5(), ".solongate", "local-logs", "solongate-audit.jsonl");
7210
+ LOCAL_LOG = join8(homedir6(), ".solongate", "local-logs", "solongate-audit.jsonl");
6709
7211
  DLP_REASON = /security layer \(dlp\)/i;
6710
7212
  RL_REASON = /security layer \(rate limit\)|rate[- ]?limit(?:ed)?\b.*exceed|exceeded \d+ calls/i;
6711
7213
  }
6712
7214
  });
6713
7215
 
6714
7216
  // src/api-client/client.ts
6715
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, existsSync as existsSync3 } from "fs";
6716
- import { resolve as resolve3, join as join8 } from "path";
6717
- import { homedir as homedir6 } from "os";
7217
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync4 } from "fs";
7218
+ import { resolve as resolve4, join as join9 } from "path";
7219
+ import { homedir as homedir7 } from "os";
6718
7220
  function listAccounts() {
6719
7221
  let list5 = [];
6720
7222
  try {
6721
- const raw = JSON.parse(readFileSync8(accountsFile(), "utf-8"));
7223
+ const raw = JSON.parse(readFileSync9(accountsFile(), "utf-8"));
6722
7224
  if (Array.isArray(raw)) list5 = raw.filter((a) => a && typeof a.apiKey === "string");
6723
7225
  } catch {
6724
7226
  }
@@ -6732,7 +7234,7 @@ function saveAccount(acc) {
6732
7234
  try {
6733
7235
  const list5 = (() => {
6734
7236
  try {
6735
- const raw = JSON.parse(readFileSync8(accountsFile(), "utf-8"));
7237
+ const raw = JSON.parse(readFileSync9(accountsFile(), "utf-8"));
6736
7238
  return Array.isArray(raw) ? raw.filter((a) => a && a.apiKey) : [];
6737
7239
  } catch {
6738
7240
  return [];
@@ -6740,8 +7242,8 @@ function saveAccount(acc) {
6740
7242
  })();
6741
7243
  const next = list5.filter((a) => a.apiKey !== acc.apiKey);
6742
7244
  next.unshift({ ...acc, addedAt: acc.addedAt ?? Date.now() });
6743
- mkdirSync5(join8(homedir6(), ".solongate"), { recursive: true });
6744
- writeFileSync6(accountsFile(), JSON.stringify(next, null, 2));
7245
+ mkdirSync6(join9(homedir7(), ".solongate"), { recursive: true });
7246
+ writeFileSync7(accountsFile(), JSON.stringify(next, null, 2));
6745
7247
  } catch {
6746
7248
  }
6747
7249
  }
@@ -6749,13 +7251,13 @@ function removeAccount(apiKey) {
6749
7251
  try {
6750
7252
  const list5 = (() => {
6751
7253
  try {
6752
- const raw = JSON.parse(readFileSync8(accountsFile(), "utf-8"));
7254
+ const raw = JSON.parse(readFileSync9(accountsFile(), "utf-8"));
6753
7255
  return Array.isArray(raw) ? raw.filter((a) => a && a.apiKey) : [];
6754
7256
  } catch {
6755
7257
  return [];
6756
7258
  }
6757
7259
  })();
6758
- writeFileSync6(accountsFile(), JSON.stringify(list5.filter((a) => a.apiKey !== apiKey), null, 2));
7260
+ writeFileSync7(accountsFile(), JSON.stringify(list5.filter((a) => a.apiKey !== apiKey), null, 2));
6759
7261
  } catch {
6760
7262
  }
6761
7263
  }
@@ -6768,15 +7270,15 @@ function isActiveAccount(apiKey) {
6768
7270
  }
6769
7271
  function setActiveAccount(creds) {
6770
7272
  try {
6771
- const dir = join8(homedir6(), ".solongate");
6772
- mkdirSync5(dir, { recursive: true });
6773
- const p = join8(dir, ["cloud", "guard.json"].join("-"));
7273
+ const dir = join9(homedir7(), ".solongate");
7274
+ mkdirSync6(dir, { recursive: true });
7275
+ const p = join9(dir, ["cloud", "guard.json"].join("-"));
6774
7276
  let existing = {};
6775
7277
  try {
6776
- existing = JSON.parse(readFileSync8(p, "utf-8"));
7278
+ existing = JSON.parse(readFileSync9(p, "utf-8"));
6777
7279
  } catch {
6778
7280
  }
6779
- writeFileSync6(p, JSON.stringify({ ...existing, apiKey: creds.apiKey, apiUrl: creds.apiUrl }, null, 2));
7281
+ writeFileSync7(p, JSON.stringify({ ...existing, apiKey: creds.apiKey, apiUrl: creds.apiUrl }, null, 2));
6780
7282
  cached2 = null;
6781
7283
  return true;
6782
7284
  } catch {
@@ -6785,19 +7287,19 @@ function setActiveAccount(creds) {
6785
7287
  }
6786
7288
  function clearActiveCredential() {
6787
7289
  try {
6788
- const p = join8(homedir6(), ".solongate", ["cloud", "guard.json"].join("-"));
6789
- if (!existsSync3(p)) {
7290
+ const p = join9(homedir7(), ".solongate", ["cloud", "guard.json"].join("-"));
7291
+ if (!existsSync4(p)) {
6790
7292
  cached2 = null;
6791
7293
  return true;
6792
7294
  }
6793
7295
  let existing = {};
6794
7296
  try {
6795
- existing = JSON.parse(readFileSync8(p, "utf-8"));
7297
+ existing = JSON.parse(readFileSync9(p, "utf-8"));
6796
7298
  } catch {
6797
7299
  }
6798
7300
  delete existing.apiKey;
6799
7301
  delete existing.apiUrl;
6800
- writeFileSync6(p, JSON.stringify(existing, null, 2));
7302
+ writeFileSync7(p, JSON.stringify(existing, null, 2));
6801
7303
  cached2 = null;
6802
7304
  viewOverride = null;
6803
7305
  return true;
@@ -6807,9 +7309,9 @@ function clearActiveCredential() {
6807
7309
  }
6808
7310
  function loginCredentialFile() {
6809
7311
  try {
6810
- const p = join8(homedir6(), ".solongate", "cloud-guard.json");
6811
- if (!existsSync3(p)) return {};
6812
- const c2 = JSON.parse(readFileSync8(p, "utf-8"));
7312
+ const p = join9(homedir7(), ".solongate", "cloud-guard.json");
7313
+ if (!existsSync4(p)) return {};
7314
+ const c2 = JSON.parse(readFileSync9(p, "utf-8"));
6813
7315
  return c2 && typeof c2 === "object" ? c2 : {};
6814
7316
  } catch {
6815
7317
  return {};
@@ -6817,9 +7319,9 @@ function loginCredentialFile() {
6817
7319
  }
6818
7320
  function dotenvApiKey() {
6819
7321
  try {
6820
- const envPath = resolve3(".env");
6821
- if (!existsSync3(envPath)) return void 0;
6822
- for (const line of readFileSync8(envPath, "utf-8").split("\n")) {
7322
+ const envPath = resolve4(".env");
7323
+ if (!existsSync4(envPath)) return void 0;
7324
+ for (const line of readFileSync9(envPath, "utf-8").split("\n")) {
6823
7325
  const trimmed = line.trim();
6824
7326
  if (!trimmed || trimmed.startsWith("#")) continue;
6825
7327
  const eq = trimmed.indexOf("=");
@@ -6922,7 +7424,7 @@ var init_client = __esm({
6922
7424
  "src/api-client/client.ts"() {
6923
7425
  "use strict";
6924
7426
  DEFAULT_API_URL2 = "https://api.solongate.com";
6925
- accountsFile = () => join8(homedir6(), ".solongate", "accounts.json");
7427
+ accountsFile = () => join9(homedir7(), ".solongate", "accounts.json");
6926
7428
  viewOverride = null;
6927
7429
  ApiError = class extends Error {
6928
7430
  status;
@@ -7413,9 +7915,9 @@ var init_hooks = __esm({
7413
7915
  // src/tui/panels/Live.tsx
7414
7916
  import { Box as Box2, Text as Text2, useInput } from "ink";
7415
7917
  import TextInput from "ink-text-input";
7416
- import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
7417
- import { homedir as homedir7 } from "os";
7418
- import { join as join9 } from "path";
7918
+ import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
7919
+ import { homedir as homedir8 } from "os";
7920
+ import { join as join10 } from "path";
7419
7921
  import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
7420
7922
  import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
7421
7923
  function extractTarget(detail) {
@@ -7973,10 +8475,10 @@ function LivePanel({ active: active2 }) {
7973
8475
  else if (input === "x") toggleSignal("dlp");
7974
8476
  else if (input === "r") toggleSignal("ratelimit");
7975
8477
  else if (input === "e") {
7976
- const file = join9(homedir7(), ".solongate", "live-export.jsonl");
8478
+ const file = join10(homedir8(), ".solongate", "live-export.jsonl");
7977
8479
  try {
7978
- mkdirSync6(join9(homedir7(), ".solongate"), { recursive: true });
7979
- writeFileSync7(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
8480
+ mkdirSync7(join10(homedir8(), ".solongate"), { recursive: true });
8481
+ writeFileSync8(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
7980
8482
  setActionMsg({ text: `\u2713 exported ${visibleDesc.length} lines \u2192 ${file}`, level: "ok", until: Date.now() + 6e3 });
7981
8483
  } catch (err2) {
7982
8484
  setActionMsg({ text: "\u2717 export failed: " + (err2 instanceof Error ? err2.message : String(err2)), level: "bad", until: Date.now() + 6e3 });
@@ -8387,7 +8889,7 @@ var init_Live = __esm({
8387
8889
  SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
8388
8890
  BG = "#12234f";
8389
8891
  DIM_FLOOR = "#233457";
8390
- RING = join9(process.cwd(), ".solongate", ".eval-ring.jsonl");
8892
+ RING = join10(process.cwd(), ".solongate", ".eval-ring.jsonl");
8391
8893
  fmtUp = (ms) => {
8392
8894
  const s = Math.floor(ms / 1e3);
8393
8895
  const p = (n) => String(n).padStart(2, "0");
@@ -9390,9 +9892,9 @@ var init_Dlp = __esm({
9390
9892
  // src/tui/panels/Audit.tsx
9391
9893
  import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
9392
9894
  import TextInput5 from "ink-text-input";
9393
- import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
9394
- import { homedir as homedir8 } from "os";
9395
- import { join as join10 } from "path";
9895
+ import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync9 } from "fs";
9896
+ import { homedir as homedir9 } from "os";
9897
+ import { join as join11 } from "path";
9396
9898
  import { useState as useState7 } from "react";
9397
9899
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
9398
9900
  function loadLocalRows() {
@@ -9542,16 +10044,16 @@ function AuditPanel({ active: active2, focused }) {
9542
10044
  const doExport = (kind) => {
9543
10045
  setMsg({ text: "exporting\u2026", level: "ok" });
9544
10046
  const run10 = async () => {
9545
- const dir = join10(homedir8(), ".solongate");
9546
- const file = join10(dir, `audit-export-${source}.jsonl`);
10047
+ const dir = join11(homedir9(), ".solongate");
10048
+ const file = join11(dir, `audit-export-${source}.jsonl`);
9547
10049
  let rows2;
9548
10050
  if (kind === "page") rows2 = pageRows;
9549
10051
  else if (source === "cloud") {
9550
10052
  const r = await api.audit.list({ ...query, limit: 1e4, offset: 0 });
9551
10053
  rows2 = r.entries.map(cloudRow).sort((a, b) => b.at - a.at);
9552
10054
  } else rows2 = localFiltered;
9553
- mkdirSync7(dir, { recursive: true });
9554
- writeFileSync8(file, rows2.map((x) => JSON.stringify(x)).join("\n") + (rows2.length ? "\n" : ""));
10055
+ mkdirSync8(dir, { recursive: true });
10056
+ writeFileSync9(file, rows2.map((x) => JSON.stringify(x)).join("\n") + (rows2.length ? "\n" : ""));
9555
10057
  return { n: rows2.length, file };
9556
10058
  };
9557
10059
  run10().then(({ n, file }) => setMsg({ text: `\u2713 exported ${n} rows \u2192 ${file}`, level: "ok" })).catch((e) => setMsg({ text: "\u2717 export failed: " + (e instanceof Error ? e.message : String(e)), level: "bad" }));
@@ -10022,312 +10524,6 @@ var init_Audit = __esm({
10022
10524
  }
10023
10525
  });
10024
10526
 
10025
- // src/global-install.ts
10026
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync9, existsSync as existsSync4, mkdirSync as mkdirSync8, rmSync as rmSync2 } from "fs";
10027
- import { resolve as resolve4, join as join11, dirname as dirname3 } from "path";
10028
- import { homedir as homedir9 } from "os";
10029
- import { fileURLToPath as fileURLToPath3 } from "url";
10030
- import { createInterface } from "readline";
10031
- import { execFileSync as execFileSync2 } from "child_process";
10032
- function lockFile(file) {
10033
- if (!existsSync4(file)) return;
10034
- try {
10035
- if (process.platform === "win32") {
10036
- try {
10037
- execFileSync2("icacls", [file, "/deny", "*S-1-1-0:(WD,AD,DC,DE)"], { stdio: "ignore" });
10038
- } catch {
10039
- }
10040
- try {
10041
- execFileSync2("attrib", ["+R", file], { stdio: "ignore" });
10042
- } catch {
10043
- }
10044
- } else if (process.platform === "darwin") {
10045
- try {
10046
- execFileSync2("chflags", ["uchg", file], { stdio: "ignore" });
10047
- } catch {
10048
- }
10049
- } else {
10050
- try {
10051
- execFileSync2("chattr", ["+i", file], { stdio: "ignore" });
10052
- } catch {
10053
- }
10054
- }
10055
- } catch {
10056
- }
10057
- }
10058
- function unlockFile(file) {
10059
- if (!existsSync4(file)) return;
10060
- try {
10061
- if (process.platform === "win32") {
10062
- try {
10063
- execFileSync2("icacls", [file, "/remove:d", "*S-1-1-0"], { stdio: "ignore" });
10064
- } catch {
10065
- }
10066
- try {
10067
- execFileSync2("icacls", [file, "/reset"], { stdio: "ignore" });
10068
- } catch {
10069
- }
10070
- try {
10071
- execFileSync2("attrib", ["-R", file], { stdio: "ignore" });
10072
- } catch {
10073
- }
10074
- } else if (process.platform === "darwin") {
10075
- try {
10076
- execFileSync2("chflags", ["nouchg", file], { stdio: "ignore" });
10077
- } catch {
10078
- }
10079
- } else {
10080
- try {
10081
- execFileSync2("chattr", ["-i", file], { stdio: "ignore" });
10082
- } catch {
10083
- }
10084
- }
10085
- } catch {
10086
- }
10087
- }
10088
- function protectedTargets() {
10089
- const p = globalPaths();
10090
- return [
10091
- join11(p.hooksDir, "guard.mjs"),
10092
- join11(p.hooksDir, "audit.mjs"),
10093
- join11(p.hooksDir, "stop.mjs"),
10094
- join11(p.hooksDir, "shield.mjs"),
10095
- p.configPath,
10096
- p.settingsPath,
10097
- p.antigravityHooksPath
10098
- ];
10099
- }
10100
- function lockProtected() {
10101
- for (const f of protectedTargets()) lockFile(f);
10102
- }
10103
- function unlockProtected() {
10104
- for (const f of protectedTargets()) unlockFile(f);
10105
- }
10106
- function globalPaths() {
10107
- const home = homedir9();
10108
- const sgDir = join11(home, ".solongate");
10109
- const hooksDir = join11(sgDir, "hooks");
10110
- const claudeDir = join11(home, ".claude");
10111
- const antigravityDir = join11(home, ".gemini", "config");
10112
- return {
10113
- home,
10114
- sgDir,
10115
- hooksDir,
10116
- claudeDir,
10117
- antigravityDir,
10118
- settingsPath: join11(claudeDir, "settings.json"),
10119
- backupPath: join11(claudeDir, "settings.solongate.bak"),
10120
- configPath: join11(sgDir, "cloud-guard.json"),
10121
- // Antigravity reads global hooks from ~/.gemini/config/hooks.json. Only the
10122
- // guard is registered there (PreToolUse); Antigravity's ALLOW-path audit is
10123
- // covered by the passive session-log collector, not a hook.
10124
- antigravityHooksPath: join11(antigravityDir, "hooks.json"),
10125
- antigravityBackupPath: join11(antigravityDir, "hooks.solongate.bak")
10126
- };
10127
- }
10128
- function readHook(filename) {
10129
- return readFileSync9(join11(HOOKS_DIR, filename), "utf-8");
10130
- }
10131
- function firstAccountCredential() {
10132
- try {
10133
- const raw = JSON.parse(readFileSync9(join11(homedir9(), ".solongate", "accounts.json"), "utf-8"));
10134
- if (Array.isArray(raw)) {
10135
- const acc = raw.find((a) => a && typeof a.apiKey === "string" && a.apiKey);
10136
- if (acc) return { apiKey: acc.apiKey, apiUrl: typeof acc.apiUrl === "string" ? acc.apiUrl : void 0 };
10137
- }
10138
- } catch {
10139
- }
10140
- return {};
10141
- }
10142
- function readGuard() {
10143
- const bundled = join11(HOOKS_DIR, "guard.bundled.mjs");
10144
- return existsSync4(bundled) ? readFileSync9(bundled, "utf-8") : readHook("guard.mjs");
10145
- }
10146
- function antigravityHookCommand(guardAbs) {
10147
- const nodeBin = process.execPath.replace(/\\/g, "/");
10148
- const call = process.platform === "win32" ? "& " : "";
10149
- return `${call}"${nodeBin}" "${guardAbs.replace(/\\/g, "/")}" antigravity "Antigravity"`;
10150
- }
10151
- function installAntigravityGuard(p, guardAbs) {
10152
- mkdirSync8(p.antigravityDir, { recursive: true });
10153
- let existing = {};
10154
- if (existsSync4(p.antigravityHooksPath)) {
10155
- const raw = readFileSync9(p.antigravityHooksPath, "utf-8");
10156
- if (!existsSync4(p.antigravityBackupPath)) writeFileSync9(p.antigravityBackupPath, raw);
10157
- try {
10158
- existing = JSON.parse(raw);
10159
- } catch {
10160
- existing = {};
10161
- }
10162
- }
10163
- const merged = {
10164
- ...existing,
10165
- [ANTIGRAVITY_GROUP]: {
10166
- PreToolUse: [{ matcher: "", hooks: [{ type: "command", command: antigravityHookCommand(guardAbs) }] }]
10167
- }
10168
- };
10169
- writeFileSync9(p.antigravityHooksPath, JSON.stringify(merged, null, 2) + "\n");
10170
- }
10171
- function removeAntigravityGuard(p) {
10172
- if (!existsSync4(p.antigravityHooksPath)) return;
10173
- try {
10174
- const s = JSON.parse(readFileSync9(p.antigravityHooksPath, "utf-8"));
10175
- if (!(ANTIGRAVITY_GROUP in s)) return;
10176
- delete s[ANTIGRAVITY_GROUP];
10177
- writeFileSync9(p.antigravityHooksPath, JSON.stringify(s, null, 2) + "\n");
10178
- } catch {
10179
- }
10180
- }
10181
- function installGlobalQuiet() {
10182
- try {
10183
- const p = globalPaths();
10184
- let apiKey = process.env["SOLONGATE_API_KEY"] || "";
10185
- let apiUrl = process.env["SOLONGATE_API_URL"] || "https://api.solongate.com";
10186
- try {
10187
- const cfg = JSON.parse(readFileSync9(p.configPath, "utf-8"));
10188
- if (cfg && typeof cfg.apiKey === "string") apiKey = apiKey || cfg.apiKey;
10189
- if (cfg && typeof cfg.apiUrl === "string") apiUrl = cfg.apiUrl;
10190
- } catch {
10191
- }
10192
- if (!apiKey) {
10193
- const acc = firstAccountCredential();
10194
- if (acc.apiKey) {
10195
- apiKey = acc.apiKey;
10196
- if (acc.apiUrl) apiUrl = acc.apiUrl;
10197
- }
10198
- }
10199
- if (!apiKey) return { ok: false, message: "no login on this device \u2014 add an account first (Accounts \u2192 + add)" };
10200
- mkdirSync8(p.hooksDir, { recursive: true });
10201
- mkdirSync8(p.claudeDir, { recursive: true });
10202
- unlockProtected();
10203
- writeFileSync9(join11(p.hooksDir, "guard.mjs"), readGuard());
10204
- writeFileSync9(join11(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
10205
- writeFileSync9(join11(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
10206
- writeFileSync9(join11(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
10207
- writeFileSync9(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
10208
- let existing = {};
10209
- if (existsSync4(p.settingsPath)) {
10210
- const raw = readFileSync9(p.settingsPath, "utf-8");
10211
- if (!existsSync4(p.backupPath)) writeFileSync9(p.backupPath, raw);
10212
- try {
10213
- existing = JSON.parse(raw);
10214
- } catch {
10215
- existing = {};
10216
- }
10217
- }
10218
- const nodeBin = process.execPath.replace(/\\/g, "/");
10219
- const call = process.platform === "win32" ? "& " : "";
10220
- const hookCmd = (script) => `${call}"${nodeBin}" "${join11(p.hooksDir, script).replace(/\\/g, "/")}" claude-code "Claude Code"`;
10221
- const merged = {
10222
- ...existing,
10223
- hooks: {
10224
- PreToolUse: [{ matcher: "", hooks: [{ type: "command", command: hookCmd("guard.mjs") }] }],
10225
- PostToolUse: [{ matcher: "", hooks: [{ type: "command", command: hookCmd("audit.mjs") }] }],
10226
- Stop: [{ matcher: "", hooks: [{ type: "command", command: hookCmd("stop.mjs") }] }]
10227
- }
10228
- };
10229
- writeFileSync9(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
10230
- try {
10231
- installAntigravityGuard(p, join11(p.hooksDir, "guard.mjs").replace(/\\/g, "/"));
10232
- } catch {
10233
- }
10234
- if (process.env["SOLONGATE_OS_LOCK"] === "1") lockProtected();
10235
- return { ok: true, message: "guard installed (open a new session)" };
10236
- } catch (e) {
10237
- return { ok: false, message: e instanceof Error ? e.message : String(e) };
10238
- }
10239
- }
10240
- function installedGuardVersion() {
10241
- try {
10242
- const p = globalPaths();
10243
- const s = readFileSync9(join11(p.hooksDir, "guard.mjs"), "utf-8");
10244
- const m = s.match(/HOOK_VERSION\s*=\s*(\d+)/);
10245
- return m ? parseInt(m[1], 10) : null;
10246
- } catch {
10247
- return null;
10248
- }
10249
- }
10250
- function guardHookOutdated() {
10251
- try {
10252
- const p = globalPaths();
10253
- return readFileSync9(join11(p.hooksDir, "guard.mjs"), "utf-8") !== readGuard();
10254
- } catch {
10255
- return false;
10256
- }
10257
- }
10258
- function isGuardInstalled() {
10259
- try {
10260
- const p = globalPaths();
10261
- if (!existsSync4(p.settingsPath)) return false;
10262
- const s = JSON.parse(readFileSync9(p.settingsPath, "utf-8"));
10263
- return !!s.hooks && JSON.stringify(s.hooks).includes(".solongate");
10264
- } catch {
10265
- return false;
10266
- }
10267
- }
10268
- function uninstallGlobalQuiet() {
10269
- try {
10270
- const p = globalPaths();
10271
- unlockProtected();
10272
- removeClaudeShim();
10273
- try {
10274
- removeAntigravityGuard(p);
10275
- } catch {
10276
- }
10277
- if (!existsSync4(p.settingsPath)) return { ok: true, message: "guard removed (open a new session)" };
10278
- const s = JSON.parse(readFileSync9(p.settingsPath, "utf-8"));
10279
- delete s.hooks;
10280
- writeFileSync9(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
10281
- return { ok: true, message: "guard removed (open a new session)" };
10282
- } catch (e) {
10283
- return { ok: false, message: e instanceof Error ? e.message : String(e) };
10284
- }
10285
- }
10286
- function escapeRe(s) {
10287
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
10288
- }
10289
- function shimTargets() {
10290
- if (process.platform === "win32") {
10291
- try {
10292
- const prof = execFileSync2("powershell", ["-NoProfile", "-Command", "$PROFILE.CurrentUserAllHosts"], { encoding: "utf-8" }).trim();
10293
- return prof ? [prof] : [];
10294
- } catch {
10295
- return [];
10296
- }
10297
- }
10298
- return [".bashrc", ".zshrc", ".profile"].map((f) => join11(homedir9(), f)).filter((f) => existsSync4(f));
10299
- }
10300
- function writeShimBlock(file, block2) {
10301
- const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
10302
- let content = existsSync4(file) ? readFileSync9(file, "utf-8") : "";
10303
- content = content.replace(re, "");
10304
- if (block2) {
10305
- if (content.length && !content.endsWith("\n")) content += "\n";
10306
- content += block2 + "\n";
10307
- }
10308
- mkdirSync8(dirname3(file), { recursive: true });
10309
- writeFileSync9(file, content);
10310
- }
10311
- function removeClaudeShim() {
10312
- for (const file of shimTargets()) {
10313
- try {
10314
- writeShimBlock(file, null);
10315
- } catch {
10316
- }
10317
- }
10318
- }
10319
- var __dirname, HOOKS_DIR, ANTIGRAVITY_GROUP, SHIM_BEGIN, SHIM_END;
10320
- var init_global_install = __esm({
10321
- "src/global-install.ts"() {
10322
- "use strict";
10323
- __dirname = dirname3(fileURLToPath3(import.meta.url));
10324
- HOOKS_DIR = resolve4(__dirname, "..", "hooks");
10325
- ANTIGRAVITY_GROUP = "solongate-guard";
10326
- SHIM_BEGIN = "# >>> SolonGate shield (auto secret redaction) >>>";
10327
- SHIM_END = "# <<< SolonGate shield <<<";
10328
- }
10329
- });
10330
-
10331
10527
  // src/tui/panels/Settings.tsx
10332
10528
  import { Box as Box8, Text as Text8, useInput as useInput7 } from "ink";
10333
10529
  import TextInput6 from "ink-text-input";
@@ -15862,7 +16058,7 @@ ${msg.content.text}`;
15862
16058
 
15863
16059
  // src/index.ts
15864
16060
  init_cli_utils();
15865
- var CLI_SUBCOMMANDS = /* @__PURE__ */ new Set(["logs-server", "local-logs", "policy", "ratelimit", "dlp", "stats", "audit", "sessions", "session", "doctor", "watch", "alerts", "webhooks", "dataroom"]);
16061
+ var CLI_SUBCOMMANDS = /* @__PURE__ */ new Set(["update", "logs-server", "local-logs", "policy", "ratelimit", "dlp", "stats", "audit", "sessions", "session", "doctor", "watch", "alerts", "webhooks", "dataroom"]);
15866
16062
  var CLI_INFO_ARGS = /* @__PURE__ */ new Set(["login", "help", "--help", "-h", "--version", "-v", "version"]);
15867
16063
  var IS_HUMAN_CLI = process.argv.length <= 2 || CLI_SUBCOMMANDS.has(process.argv[2] ?? "") || CLI_INFO_ARGS.has(process.argv[2] ?? "");
15868
16064
  if (!IS_HUMAN_CLI) {
@@ -15923,6 +16119,7 @@ function printHelp() {
15923
16119
  console.log(` ${c.dim}Usage:${c.reset} ${c.cyan}solongate${c.reset} ${c.dim}[command]${c.reset} ${c.dim}(no command opens the dataroom: all of this in a terminal UI)${c.reset}`);
15924
16120
  head("Setup & status");
15925
16121
  cmd("solongate", "open the dataroom UI (login, policies, audit, settings)");
16122
+ cmd("update", "update SolonGate to the newest version and refresh the guard");
15926
16123
  cmd("doctor", "health check: login, policy, guard, local logs");
15927
16124
  cmd("doctor --json", "the same health check as machine-readable JSON");
15928
16125
  cmd("logs-server start", "start the local audit-log service for the dashboard (background)");
@@ -15970,8 +16167,38 @@ function printHelp() {
15970
16167
  console.log(` ${c.dim}Details for a command: ${c.reset}${c.cyan}solongate <command> help${c.reset}${c.dim} \xB7 Dashboard: ${c.reset}${c.cyan}https://dashboard.solongate.com${c.reset}`);
15971
16168
  console.log("");
15972
16169
  }
16170
+ var AGENT_ENV_MARKERS = [
16171
+ "CLAUDECODE",
16172
+ "CLAUDE_CODE",
16173
+ "CLAUDE_CODE_ENTRYPOINT",
16174
+ "ANTIGRAVITY",
16175
+ "ANTIGRAVITY_CLI",
16176
+ "GEMINI_CLI",
16177
+ "CURSOR_TRACE_ID",
16178
+ "AIDER_CHAT",
16179
+ "OPENAI_CODEX",
16180
+ "CODEX_SANDBOX",
16181
+ "OPENCLAW",
16182
+ "REPLIT_CLI",
16183
+ "DEVIN_session"
16184
+ ];
16185
+ function assertHumanTerminal() {
16186
+ if (process.env["SOLONGATE_INTERNAL"] === "1") return;
16187
+ const marker = AGENT_ENV_MARKERS.find((k) => process.env[k]);
16188
+ const isTty = Boolean(process.stdin.isTTY && process.stdout.isTTY);
16189
+ if (isTty && !marker) return;
16190
+ const w = (s) => void process.stderr.write(s + "\n");
16191
+ w("");
16192
+ w(" SolonGate is human-only.");
16193
+ w(" These commands control your security policy, so they cannot be run by an AI");
16194
+ w(" agent or any non-interactive process. Run them yourself, in a terminal.");
16195
+ w(marker ? ` (refused: agent environment detected via ${marker})` : " (refused: no interactive terminal)");
16196
+ w("");
16197
+ process.exit(1);
16198
+ }
15973
16199
  async function main() {
15974
16200
  const subcommand = process.argv[2];
16201
+ if (IS_HUMAN_CLI) assertHumanTerminal();
15975
16202
  if (subcommand === "--help" || subcommand === "-h" || subcommand === "help") {
15976
16203
  printHelp();
15977
16204
  return;
@@ -16018,6 +16245,10 @@ async function main() {
16018
16245
  console.log("");
16019
16246
  return;
16020
16247
  }
16248
+ if (subcommand === "update") {
16249
+ const { runUpdateCommand: runUpdateCommand2 } = await Promise.resolve().then(() => (init_self_update(), self_update_exports));
16250
+ process.exit(await runUpdateCommand2());
16251
+ }
16021
16252
  if (subcommand === "logs-server" || subcommand === "local-logs") {
16022
16253
  const { runLogsServer: runLogsServer2 } = await Promise.resolve().then(() => (init_logs_server(), logs_server_exports));
16023
16254
  await runLogsServer2();