@solongate/proxy 0.82.42 → 0.82.43

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,9 +6903,9 @@ 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],
@@ -6443,22 +6942,22 @@ var DIR, STATE_FILE2, LOG_FILE2, LOGS_SERVER_PORT;
6443
6942
  var init_logs_server_daemon = __esm({
6444
6943
  "src/logs-server-daemon.ts"() {
6445
6944
  "use strict";
6446
- DIR = join5(homedir3(), ".solongate");
6447
- STATE_FILE2 = join5(DIR, ".logs-server.json");
6448
- LOG_FILE2 = join5(DIR, "logs-server.log");
6945
+ DIR = join6(homedir4(), ".solongate");
6946
+ STATE_FILE2 = join6(DIR, ".logs-server.json");
6947
+ LOG_FILE2 = join6(DIR, "logs-server.log");
6449
6948
  LOGS_SERVER_PORT = 8788;
6450
6949
  }
6451
6950
  });
6452
6951
 
6453
6952
  // 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";
6953
+ import { readFileSync as readFileSync7 } from "fs";
6954
+ import { homedir as homedir5 } from "os";
6955
+ import { join as join7 } from "path";
6457
6956
  function loadConfig() {
6458
6957
  if (cached) return cached;
6459
6958
  const defaults = { notifications: true };
6460
6959
  try {
6461
- const raw = readFileSync6(join6(homedir4(), ".solongate", "tui-config.json"), "utf-8");
6960
+ const raw = readFileSync7(join7(homedir5(), ".solongate", "tui-config.json"), "utf-8");
6462
6961
  const j = JSON.parse(raw);
6463
6962
  cached = {
6464
6963
  notifications: j.notifications !== false,
@@ -6629,9 +7128,9 @@ var init_components = __esm({
6629
7128
  });
6630
7129
 
6631
7130
  // 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";
7131
+ import { closeSync, openSync as openSync3, readFileSync as readFileSync8, readSync, statSync, writeFileSync as writeFileSync6 } from "fs";
7132
+ import { homedir as homedir6 } from "os";
7133
+ import { join as join8 } from "path";
6635
7134
  function tailLines(file, maxBytes = 131072) {
6636
7135
  try {
6637
7136
  const size = statSync(file).size;
@@ -6649,7 +7148,7 @@ function tailLines(file, maxBytes = 131072) {
6649
7148
  }
6650
7149
  function deleteLocalEntry(at, tool, session) {
6651
7150
  try {
6652
- const lines = readFileSync7(LOCAL_LOG, "utf-8").split("\n");
7151
+ const lines = readFileSync8(LOCAL_LOG, "utf-8").split("\n");
6653
7152
  let removed = 0;
6654
7153
  const kept = lines.filter((line) => {
6655
7154
  if (!line.trim()) return false;
@@ -6665,7 +7164,7 @@ function deleteLocalEntry(at, tool, session) {
6665
7164
  }
6666
7165
  return true;
6667
7166
  });
6668
- if (removed) writeFileSync5(LOCAL_LOG, kept.length ? kept.join("\n") + "\n" : "");
7167
+ if (removed) writeFileSync6(LOCAL_LOG, kept.length ? kept.join("\n") + "\n" : "");
6669
7168
  return removed;
6670
7169
  } catch {
6671
7170
  return 0;
@@ -6673,8 +7172,8 @@ function deleteLocalEntry(at, tool, session) {
6673
7172
  }
6674
7173
  function clearLocalLog() {
6675
7174
  try {
6676
- const n = readFileSync7(LOCAL_LOG, "utf-8").split("\n").filter(Boolean).length;
6677
- writeFileSync5(LOCAL_LOG, "");
7175
+ const n = readFileSync8(LOCAL_LOG, "utf-8").split("\n").filter(Boolean).length;
7176
+ writeFileSync6(LOCAL_LOG, "");
6678
7177
  return n;
6679
7178
  } catch {
6680
7179
  return 0;
@@ -6705,20 +7204,20 @@ var LOCAL_LOG, DLP_REASON, RL_REASON;
6705
7204
  var init_local_log = __esm({
6706
7205
  "src/tui/local-log.ts"() {
6707
7206
  "use strict";
6708
- LOCAL_LOG = join7(homedir5(), ".solongate", "local-logs", "solongate-audit.jsonl");
7207
+ LOCAL_LOG = join8(homedir6(), ".solongate", "local-logs", "solongate-audit.jsonl");
6709
7208
  DLP_REASON = /security layer \(dlp\)/i;
6710
7209
  RL_REASON = /security layer \(rate limit\)|rate[- ]?limit(?:ed)?\b.*exceed|exceeded \d+ calls/i;
6711
7210
  }
6712
7211
  });
6713
7212
 
6714
7213
  // 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";
7214
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync4 } from "fs";
7215
+ import { resolve as resolve4, join as join9 } from "path";
7216
+ import { homedir as homedir7 } from "os";
6718
7217
  function listAccounts() {
6719
7218
  let list5 = [];
6720
7219
  try {
6721
- const raw = JSON.parse(readFileSync8(accountsFile(), "utf-8"));
7220
+ const raw = JSON.parse(readFileSync9(accountsFile(), "utf-8"));
6722
7221
  if (Array.isArray(raw)) list5 = raw.filter((a) => a && typeof a.apiKey === "string");
6723
7222
  } catch {
6724
7223
  }
@@ -6732,7 +7231,7 @@ function saveAccount(acc) {
6732
7231
  try {
6733
7232
  const list5 = (() => {
6734
7233
  try {
6735
- const raw = JSON.parse(readFileSync8(accountsFile(), "utf-8"));
7234
+ const raw = JSON.parse(readFileSync9(accountsFile(), "utf-8"));
6736
7235
  return Array.isArray(raw) ? raw.filter((a) => a && a.apiKey) : [];
6737
7236
  } catch {
6738
7237
  return [];
@@ -6740,8 +7239,8 @@ function saveAccount(acc) {
6740
7239
  })();
6741
7240
  const next = list5.filter((a) => a.apiKey !== acc.apiKey);
6742
7241
  next.unshift({ ...acc, addedAt: acc.addedAt ?? Date.now() });
6743
- mkdirSync5(join8(homedir6(), ".solongate"), { recursive: true });
6744
- writeFileSync6(accountsFile(), JSON.stringify(next, null, 2));
7242
+ mkdirSync6(join9(homedir7(), ".solongate"), { recursive: true });
7243
+ writeFileSync7(accountsFile(), JSON.stringify(next, null, 2));
6745
7244
  } catch {
6746
7245
  }
6747
7246
  }
@@ -6749,13 +7248,13 @@ function removeAccount(apiKey) {
6749
7248
  try {
6750
7249
  const list5 = (() => {
6751
7250
  try {
6752
- const raw = JSON.parse(readFileSync8(accountsFile(), "utf-8"));
7251
+ const raw = JSON.parse(readFileSync9(accountsFile(), "utf-8"));
6753
7252
  return Array.isArray(raw) ? raw.filter((a) => a && a.apiKey) : [];
6754
7253
  } catch {
6755
7254
  return [];
6756
7255
  }
6757
7256
  })();
6758
- writeFileSync6(accountsFile(), JSON.stringify(list5.filter((a) => a.apiKey !== apiKey), null, 2));
7257
+ writeFileSync7(accountsFile(), JSON.stringify(list5.filter((a) => a.apiKey !== apiKey), null, 2));
6759
7258
  } catch {
6760
7259
  }
6761
7260
  }
@@ -6768,15 +7267,15 @@ function isActiveAccount(apiKey) {
6768
7267
  }
6769
7268
  function setActiveAccount(creds) {
6770
7269
  try {
6771
- const dir = join8(homedir6(), ".solongate");
6772
- mkdirSync5(dir, { recursive: true });
6773
- const p = join8(dir, ["cloud", "guard.json"].join("-"));
7270
+ const dir = join9(homedir7(), ".solongate");
7271
+ mkdirSync6(dir, { recursive: true });
7272
+ const p = join9(dir, ["cloud", "guard.json"].join("-"));
6774
7273
  let existing = {};
6775
7274
  try {
6776
- existing = JSON.parse(readFileSync8(p, "utf-8"));
7275
+ existing = JSON.parse(readFileSync9(p, "utf-8"));
6777
7276
  } catch {
6778
7277
  }
6779
- writeFileSync6(p, JSON.stringify({ ...existing, apiKey: creds.apiKey, apiUrl: creds.apiUrl }, null, 2));
7278
+ writeFileSync7(p, JSON.stringify({ ...existing, apiKey: creds.apiKey, apiUrl: creds.apiUrl }, null, 2));
6780
7279
  cached2 = null;
6781
7280
  return true;
6782
7281
  } catch {
@@ -6785,19 +7284,19 @@ function setActiveAccount(creds) {
6785
7284
  }
6786
7285
  function clearActiveCredential() {
6787
7286
  try {
6788
- const p = join8(homedir6(), ".solongate", ["cloud", "guard.json"].join("-"));
6789
- if (!existsSync3(p)) {
7287
+ const p = join9(homedir7(), ".solongate", ["cloud", "guard.json"].join("-"));
7288
+ if (!existsSync4(p)) {
6790
7289
  cached2 = null;
6791
7290
  return true;
6792
7291
  }
6793
7292
  let existing = {};
6794
7293
  try {
6795
- existing = JSON.parse(readFileSync8(p, "utf-8"));
7294
+ existing = JSON.parse(readFileSync9(p, "utf-8"));
6796
7295
  } catch {
6797
7296
  }
6798
7297
  delete existing.apiKey;
6799
7298
  delete existing.apiUrl;
6800
- writeFileSync6(p, JSON.stringify(existing, null, 2));
7299
+ writeFileSync7(p, JSON.stringify(existing, null, 2));
6801
7300
  cached2 = null;
6802
7301
  viewOverride = null;
6803
7302
  return true;
@@ -6807,9 +7306,9 @@ function clearActiveCredential() {
6807
7306
  }
6808
7307
  function loginCredentialFile() {
6809
7308
  try {
6810
- const p = join8(homedir6(), ".solongate", "cloud-guard.json");
6811
- if (!existsSync3(p)) return {};
6812
- const c2 = JSON.parse(readFileSync8(p, "utf-8"));
7309
+ const p = join9(homedir7(), ".solongate", "cloud-guard.json");
7310
+ if (!existsSync4(p)) return {};
7311
+ const c2 = JSON.parse(readFileSync9(p, "utf-8"));
6813
7312
  return c2 && typeof c2 === "object" ? c2 : {};
6814
7313
  } catch {
6815
7314
  return {};
@@ -6817,9 +7316,9 @@ function loginCredentialFile() {
6817
7316
  }
6818
7317
  function dotenvApiKey() {
6819
7318
  try {
6820
- const envPath = resolve3(".env");
6821
- if (!existsSync3(envPath)) return void 0;
6822
- for (const line of readFileSync8(envPath, "utf-8").split("\n")) {
7319
+ const envPath = resolve4(".env");
7320
+ if (!existsSync4(envPath)) return void 0;
7321
+ for (const line of readFileSync9(envPath, "utf-8").split("\n")) {
6823
7322
  const trimmed = line.trim();
6824
7323
  if (!trimmed || trimmed.startsWith("#")) continue;
6825
7324
  const eq = trimmed.indexOf("=");
@@ -6922,7 +7421,7 @@ var init_client = __esm({
6922
7421
  "src/api-client/client.ts"() {
6923
7422
  "use strict";
6924
7423
  DEFAULT_API_URL2 = "https://api.solongate.com";
6925
- accountsFile = () => join8(homedir6(), ".solongate", "accounts.json");
7424
+ accountsFile = () => join9(homedir7(), ".solongate", "accounts.json");
6926
7425
  viewOverride = null;
6927
7426
  ApiError = class extends Error {
6928
7427
  status;
@@ -7413,9 +7912,9 @@ var init_hooks = __esm({
7413
7912
  // src/tui/panels/Live.tsx
7414
7913
  import { Box as Box2, Text as Text2, useInput } from "ink";
7415
7914
  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";
7915
+ import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
7916
+ import { homedir as homedir8 } from "os";
7917
+ import { join as join10 } from "path";
7419
7918
  import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
7420
7919
  import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
7421
7920
  function extractTarget(detail) {
@@ -7973,10 +8472,10 @@ function LivePanel({ active: active2 }) {
7973
8472
  else if (input === "x") toggleSignal("dlp");
7974
8473
  else if (input === "r") toggleSignal("ratelimit");
7975
8474
  else if (input === "e") {
7976
- const file = join9(homedir7(), ".solongate", "live-export.jsonl");
8475
+ const file = join10(homedir8(), ".solongate", "live-export.jsonl");
7977
8476
  try {
7978
- mkdirSync6(join9(homedir7(), ".solongate"), { recursive: true });
7979
- writeFileSync7(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
8477
+ mkdirSync7(join10(homedir8(), ".solongate"), { recursive: true });
8478
+ writeFileSync8(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
7980
8479
  setActionMsg({ text: `\u2713 exported ${visibleDesc.length} lines \u2192 ${file}`, level: "ok", until: Date.now() + 6e3 });
7981
8480
  } catch (err2) {
7982
8481
  setActionMsg({ text: "\u2717 export failed: " + (err2 instanceof Error ? err2.message : String(err2)), level: "bad", until: Date.now() + 6e3 });
@@ -8387,7 +8886,7 @@ var init_Live = __esm({
8387
8886
  SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
8388
8887
  BG = "#12234f";
8389
8888
  DIM_FLOOR = "#233457";
8390
- RING = join9(process.cwd(), ".solongate", ".eval-ring.jsonl");
8889
+ RING = join10(process.cwd(), ".solongate", ".eval-ring.jsonl");
8391
8890
  fmtUp = (ms) => {
8392
8891
  const s = Math.floor(ms / 1e3);
8393
8892
  const p = (n) => String(n).padStart(2, "0");
@@ -9390,9 +9889,9 @@ var init_Dlp = __esm({
9390
9889
  // src/tui/panels/Audit.tsx
9391
9890
  import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
9392
9891
  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";
9892
+ import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync9 } from "fs";
9893
+ import { homedir as homedir9 } from "os";
9894
+ import { join as join11 } from "path";
9396
9895
  import { useState as useState7 } from "react";
9397
9896
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
9398
9897
  function loadLocalRows() {
@@ -9542,16 +10041,16 @@ function AuditPanel({ active: active2, focused }) {
9542
10041
  const doExport = (kind) => {
9543
10042
  setMsg({ text: "exporting\u2026", level: "ok" });
9544
10043
  const run10 = async () => {
9545
- const dir = join10(homedir8(), ".solongate");
9546
- const file = join10(dir, `audit-export-${source}.jsonl`);
10044
+ const dir = join11(homedir9(), ".solongate");
10045
+ const file = join11(dir, `audit-export-${source}.jsonl`);
9547
10046
  let rows2;
9548
10047
  if (kind === "page") rows2 = pageRows;
9549
10048
  else if (source === "cloud") {
9550
10049
  const r = await api.audit.list({ ...query, limit: 1e4, offset: 0 });
9551
10050
  rows2 = r.entries.map(cloudRow).sort((a, b) => b.at - a.at);
9552
10051
  } else rows2 = localFiltered;
9553
- mkdirSync7(dir, { recursive: true });
9554
- writeFileSync8(file, rows2.map((x) => JSON.stringify(x)).join("\n") + (rows2.length ? "\n" : ""));
10052
+ mkdirSync8(dir, { recursive: true });
10053
+ writeFileSync9(file, rows2.map((x) => JSON.stringify(x)).join("\n") + (rows2.length ? "\n" : ""));
9555
10054
  return { n: rows2.length, file };
9556
10055
  };
9557
10056
  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 +10521,6 @@ var init_Audit = __esm({
10022
10521
  }
10023
10522
  });
10024
10523
 
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
10524
  // src/tui/panels/Settings.tsx
10332
10525
  import { Box as Box8, Text as Text8, useInput as useInput7 } from "ink";
10333
10526
  import TextInput6 from "ink-text-input";
@@ -15862,7 +16055,7 @@ ${msg.content.text}`;
15862
16055
 
15863
16056
  // src/index.ts
15864
16057
  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"]);
16058
+ var CLI_SUBCOMMANDS = /* @__PURE__ */ new Set(["update", "logs-server", "local-logs", "policy", "ratelimit", "dlp", "stats", "audit", "sessions", "session", "doctor", "watch", "alerts", "webhooks", "dataroom"]);
15866
16059
  var CLI_INFO_ARGS = /* @__PURE__ */ new Set(["login", "help", "--help", "-h", "--version", "-v", "version"]);
15867
16060
  var IS_HUMAN_CLI = process.argv.length <= 2 || CLI_SUBCOMMANDS.has(process.argv[2] ?? "") || CLI_INFO_ARGS.has(process.argv[2] ?? "");
15868
16061
  if (!IS_HUMAN_CLI) {
@@ -15923,6 +16116,7 @@ function printHelp() {
15923
16116
  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
16117
  head("Setup & status");
15925
16118
  cmd("solongate", "open the dataroom UI (login, policies, audit, settings)");
16119
+ cmd("update", "update SolonGate to the newest version and refresh the guard");
15926
16120
  cmd("doctor", "health check: login, policy, guard, local logs");
15927
16121
  cmd("doctor --json", "the same health check as machine-readable JSON");
15928
16122
  cmd("logs-server start", "start the local audit-log service for the dashboard (background)");
@@ -16018,6 +16212,10 @@ async function main() {
16018
16212
  console.log("");
16019
16213
  return;
16020
16214
  }
16215
+ if (subcommand === "update") {
16216
+ const { runUpdateCommand: runUpdateCommand2 } = await Promise.resolve().then(() => (init_self_update(), self_update_exports));
16217
+ process.exit(await runUpdateCommand2());
16218
+ }
16021
16219
  if (subcommand === "logs-server" || subcommand === "local-logs") {
16022
16220
  const { runLogsServer: runLogsServer2 } = await Promise.resolve().then(() => (init_logs_server(), logs_server_exports));
16023
16221
  await runLogsServer2();