@solongate/proxy 0.83.3 → 0.83.5

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
@@ -465,13 +465,13 @@ ${offset}${err2}${errEnd}`;
465
465
  return next >= src.length || src[next] === "\n" ? str + "\n" : str;
466
466
  }
467
467
  // ^(---|...)
468
- static atDocumentBoundary(src, offset, sep) {
468
+ static atDocumentBoundary(src, offset, sep2) {
469
469
  const ch0 = src[offset];
470
470
  if (!ch0) return true;
471
471
  const prev = src[offset - 1];
472
472
  if (prev && prev !== "\n") return false;
473
- if (sep) {
474
- if (ch0 !== sep) return false;
473
+ if (sep2) {
474
+ if (ch0 !== sep2) return false;
475
475
  } else {
476
476
  if (ch0 !== Char.DIRECTIVES_END && ch0 !== Char.DOCUMENT_END) return false;
477
477
  }
@@ -1647,7 +1647,7 @@ var require_parse_cst = __commonJS({
1647
1647
  const folded = this.type === PlainValue.Type.BLOCK_FOLDED;
1648
1648
  let atStart = true;
1649
1649
  let str = "";
1650
- let sep = "";
1650
+ let sep2 = "";
1651
1651
  let prevMoreIndented = false;
1652
1652
  for (let i = start; i < end; ++i) {
1653
1653
  for (let j = 0; j < bi; ++j) {
@@ -1656,21 +1656,21 @@ var require_parse_cst = __commonJS({
1656
1656
  }
1657
1657
  const ch2 = src[i];
1658
1658
  if (ch2 === "\n") {
1659
- if (sep === "\n") str += "\n";
1660
- else sep = "\n";
1659
+ if (sep2 === "\n") str += "\n";
1660
+ else sep2 = "\n";
1661
1661
  } else {
1662
1662
  const lineEnd = PlainValue.Node.endOfLine(src, i);
1663
1663
  const line = src.slice(i, lineEnd);
1664
1664
  i = lineEnd;
1665
1665
  if (folded && (ch2 === " " || ch2 === " ") && i < keepStart) {
1666
- if (sep === " ") sep = "\n";
1667
- else if (!prevMoreIndented && !atStart && sep === "\n") sep = "\n\n";
1668
- str += sep + line;
1669
- sep = lineEnd < end && src[lineEnd] || "";
1666
+ if (sep2 === " ") sep2 = "\n";
1667
+ else if (!prevMoreIndented && !atStart && sep2 === "\n") sep2 = "\n\n";
1668
+ str += sep2 + line;
1669
+ sep2 = lineEnd < end && src[lineEnd] || "";
1670
1670
  prevMoreIndented = true;
1671
1671
  } else {
1672
- str += sep + line;
1673
- sep = folded && i < keepStart ? " " : "\n";
1672
+ str += sep2 + line;
1673
+ sep2 = folded && i < keepStart ? " " : "\n";
1674
1674
  prevMoreIndented = false;
1675
1675
  }
1676
1676
  if (atStart && line !== "") atStart = false;
@@ -6612,6 +6612,16 @@ function repairQuiet() {
6612
6612
  }
6613
6613
  async function runRepair() {
6614
6614
  const out2 = (s) => void process.stderr.write(s + "\n");
6615
+ if (process.platform !== "win32" && typeof process.getuid === "function" && process.getuid() === 0 && process.env["SOLONGATE_INTERNAL"] !== "1") {
6616
+ out2("");
6617
+ out2(" Do not run `solongate repair` with sudo.");
6618
+ out2(" It installs the guard into your home directory, and as root it would");
6619
+ out2(` arm root\u2019s home instead${process.env["SUDO_USER"] ? ` of ${process.env["SUDO_USER"]}\u2019s` : ""} \u2014 reporting success while nothing changed for you.`);
6620
+ out2("");
6621
+ out2(" Run it as yourself: solongate repair");
6622
+ out2("");
6623
+ return 1;
6624
+ }
6615
6625
  const rep = repairQuiet();
6616
6626
  out2("");
6617
6627
  out2(" SolonGate repair");
@@ -6924,22 +6934,25 @@ var init_global_install = __esm({
6924
6934
  var self_update_exports = {};
6925
6935
  __export(self_update_exports, {
6926
6936
  adminInstallCommand: () => adminInstallCommand,
6937
+ adminUpdateSteps: () => adminUpdateSteps,
6927
6938
  autoUpdateEnabled: () => autoUpdateEnabled,
6928
6939
  autoUpdateForcedByEnv: () => autoUpdateForcedByEnv,
6929
6940
  currentVersion: () => currentVersion,
6941
+ globalInstallCheck: () => globalInstallCheck,
6930
6942
  latestVersion: () => latestVersion,
6931
6943
  maybeSelfUpdate: () => maybeSelfUpdate,
6932
6944
  newerThan: () => newerThan,
6933
6945
  runAutoUpdateCommand: () => runAutoUpdateCommand,
6934
6946
  runUpdateCommand: () => runUpdateCommand,
6947
+ runningAsRoot: () => runningAsRoot,
6935
6948
  setAutoUpdate: () => setAutoUpdate,
6936
6949
  tuiUpdateFlow: () => tuiUpdateFlow,
6937
6950
  updateNow: () => updateNow
6938
6951
  });
6939
6952
  import { execFile, execFileSync as execFileSync3, spawn } from "child_process";
6940
- import { mkdirSync as mkdirSync4, openSync, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
6953
+ import { access, mkdirSync as mkdirSync4, openSync, readFileSync as readFileSync5, writeFileSync as writeFileSync4, constants as FS } from "fs";
6941
6954
  import { homedir as homedir3 } from "os";
6942
- import { dirname as dirname2, join as join5 } from "path";
6955
+ import { dirname as dirname2, join as join5, sep } from "path";
6943
6956
  import { fileURLToPath as fileURLToPath2 } from "url";
6944
6957
  function readState() {
6945
6958
  try {
@@ -7026,6 +7039,17 @@ function spawnGlobalInstall(version) {
7026
7039
  }
7027
7040
  async function runUpdateCommand() {
7028
7041
  const out2 = (s) => void process.stderr.write(s + "\n");
7042
+ if (runningAsRoot()) {
7043
+ out2("");
7044
+ out2(" Do not run `solongate update` with sudo.");
7045
+ out2(" npm would install fine, but the guard hooks would be written into root\u2019s");
7046
+ out2(" home instead of yours, and your agents would stop being guarded.");
7047
+ out2("");
7048
+ out2(" Run these two instead:");
7049
+ for (const s of adminUpdateSteps()) out2(" " + s);
7050
+ out2("");
7051
+ return 1;
7052
+ }
7029
7053
  const cur = currentVersion();
7030
7054
  out2(` current ${cur}`);
7031
7055
  const latest = await latestVersion();
@@ -7034,13 +7058,26 @@ async function runUpdateCommand() {
7034
7058
  return 1;
7035
7059
  }
7036
7060
  if (newerThan(latest, cur)) {
7061
+ const pre = await globalInstallCheck();
7062
+ if (pre.writable === false) {
7063
+ writeState({ ...readState(), needsAdmin: latest });
7064
+ out2(` v${latest} is out, but npm\u2019s global folder needs admin rights on this machine:`);
7065
+ out2(` ${pre.dir}`);
7066
+ out2(" That is the normal macOS setup \u2014 SolonGate cannot install there on its own.");
7067
+ out2("");
7068
+ out2(" Run these two:");
7069
+ for (const s of adminUpdateSteps()) out2(" " + s);
7070
+ out2("");
7071
+ return 1;
7072
+ }
7037
7073
  out2(` updating ${cur} -> ${latest} ...`);
7038
7074
  const r = await runGlobalInstall2(latest);
7039
7075
  if (!r.ok) {
7040
7076
  if (r.needsAdmin) {
7041
7077
  writeState({ ...readState(), needsAdmin: latest });
7042
- out2(` npm could not write to the global folder (it needs admin rights).`);
7043
- out2(` Run: ${adminInstallCommand()}`);
7078
+ out2(" npm could not write to the global folder (it needs admin rights).");
7079
+ out2(" Run these two:");
7080
+ for (const s of adminUpdateSteps()) out2(" " + s);
7044
7081
  } else {
7045
7082
  out2(" update failed. Try: npm i -g @solongate/proxy@latest");
7046
7083
  }
@@ -7095,11 +7132,43 @@ ${output}
7095
7132
  function adminInstallCommand() {
7096
7133
  return process.platform === "win32" ? `npm i -g ${PKG}@latest (in an Administrator terminal)` : `sudo npm i -g ${PKG}@latest`;
7097
7134
  }
7135
+ function runningAsRoot() {
7136
+ return process.platform !== "win32" && typeof process.getuid === "function" && process.getuid() === 0;
7137
+ }
7138
+ function ownNodeModules() {
7139
+ let dir = dirname2(fileURLToPath2(import.meta.url));
7140
+ for (let i = 0; i < 12; i++) {
7141
+ const parent = dirname2(dir);
7142
+ if (parent === dir) break;
7143
+ if (dir.endsWith(`${sep}node_modules`)) return dir;
7144
+ dir = parent;
7145
+ }
7146
+ return null;
7147
+ }
7148
+ function globalInstallCheck() {
7149
+ prefixCheck ??= new Promise((resolve6) => {
7150
+ try {
7151
+ const dir = ownNodeModules();
7152
+ if (!dir) return resolve6({ writable: null, dir: null });
7153
+ access(dir, FS.W_OK, (e) => resolve6({ writable: !e, dir }));
7154
+ } catch {
7155
+ resolve6({ writable: null, dir: null });
7156
+ }
7157
+ });
7158
+ return prefixCheck;
7159
+ }
7160
+ function adminUpdateSteps() {
7161
+ return [` ${adminInstallCommand()}`, ` solongate repair${process.platform === "win32" ? "" : " (this one WITHOUT sudo)"}`];
7162
+ }
7098
7163
  async function updateNow() {
7099
7164
  const cur = currentVersion();
7100
7165
  const latest = await fetchLatest();
7101
7166
  if (!latest) return { status: "unreachable", version: cur };
7102
7167
  if (!newerThan(latest, cur)) return { status: "current", version: cur };
7168
+ if ((await globalInstallCheck()).writable === false) {
7169
+ writeState({ ...readState(), needsAdmin: latest });
7170
+ return { status: "needs-admin", version: latest };
7171
+ }
7103
7172
  const r = await runGlobalInstall2(latest);
7104
7173
  if (r.ok) {
7105
7174
  writeState({ ...readState(), installed: latest, needsAdmin: void 0 });
@@ -7111,7 +7180,7 @@ async function updateNow() {
7111
7180
  }
7112
7181
  return { status: "failed", version: latest };
7113
7182
  }
7114
- function runAutoUpdateCommand(arg) {
7183
+ async function runAutoUpdateCommand(arg) {
7115
7184
  const out2 = (s) => void process.stderr.write(s + "\n");
7116
7185
  const envForced = autoUpdateForcedByEnv();
7117
7186
  if (arg === void 0) {
@@ -7128,6 +7197,13 @@ function runAutoUpdateCommand(arg) {
7128
7197
  }
7129
7198
  setAutoUpdate(on);
7130
7199
  out2(on ? " \u2713 auto-update on \u2014 new versions install in the background" : " \u2713 auto-update off \u2014 update with: solongate update");
7200
+ if (on && (await globalInstallCheck()).writable === false) {
7201
+ out2("");
7202
+ out2(" Heads up: npm\u2019s global folder needs admin rights on this machine, so a");
7203
+ out2(" background install cannot succeed here. Updates will still be announced,");
7204
+ out2(" and installing them takes:");
7205
+ for (const s of adminUpdateSteps()) out2(" " + s);
7206
+ }
7131
7207
  if (envForced) out2(` note: SOLONGATE_AUTO_UPDATE is set and overrides this while it stays set`);
7132
7208
  return 0;
7133
7209
  }
@@ -7178,18 +7254,25 @@ function maybeSelfUpdate(notify = (l) => process.stderr.write(l + "\n")) {
7178
7254
  const attempts = readState().attempts ?? {};
7179
7255
  if (now - (attempts[latest] ?? 0) < ATTEMPT_EVERY_MS) return;
7180
7256
  writeState({ ...readState(), attempts: { [latest]: now } });
7257
+ const needsAdmin = (await globalInstallCheck()).writable === false;
7258
+ const head = `${c.dim}\u2191 solongate v${latest} available (running v${current})`;
7259
+ if (needsAdmin) {
7260
+ notify(`${head} \u2014 needs admin rights on this machine:${c.reset}`);
7261
+ for (const s of adminUpdateSteps()) notify(`${c.cyan} ${s.trim()}${c.reset}`);
7262
+ return;
7263
+ }
7181
7264
  if (!autoUpdateEnabled()) {
7182
- notify(`${c.dim}\u2191 solongate v${latest} available (running v${current}) \u2014 run ${c.reset}${c.cyan}solongate update${c.reset}`);
7265
+ notify(`${head} \u2014 run ${c.reset}${c.cyan}solongate update${c.reset}`);
7183
7266
  return;
7184
7267
  }
7185
7268
  if (spawnGlobalInstall(latest)) {
7186
- notify(`${c.dim}\u2191 solongate v${latest} available (running v${current}) \u2014 updating in the background, next run uses it${c.reset}`);
7269
+ notify(`${head} \u2014 updating in the background, next run uses it${c.reset}`);
7187
7270
  }
7188
7271
  } catch {
7189
7272
  }
7190
7273
  })();
7191
7274
  }
7192
- var PKG, CHECK_EVERY_MS, ATTEMPT_EVERY_MS, STATE_FILE, LOG_FILE, NEEDS_ADMIN_RE;
7275
+ var PKG, CHECK_EVERY_MS, ATTEMPT_EVERY_MS, STATE_FILE, LOG_FILE, NEEDS_ADMIN_RE, prefixCheck;
7193
7276
  var init_self_update = __esm({
7194
7277
  "src/self-update.ts"() {
7195
7278
  "use strict";
@@ -7200,6 +7283,7 @@ var init_self_update = __esm({
7200
7283
  STATE_FILE = join5(homedir3(), ".solongate", ".self-update.json");
7201
7284
  LOG_FILE = join5(homedir3(), ".solongate", "self-update.log");
7202
7285
  NEEDS_ADMIN_RE = /\bEACCES\b|\bEPERM\b|permission denied|operation not permitted/i;
7286
+ prefixCheck = null;
7203
7287
  }
7204
7288
  });
7205
7289
 
@@ -7632,26 +7716,6 @@ var init_local_log = __esm({
7632
7716
  });
7633
7717
 
7634
7718
  // src/api-client/client.ts
7635
- var client_exports = {};
7636
- __export(client_exports, {
7637
- ApiError: () => ApiError,
7638
- DEFAULT_API_URL: () => DEFAULT_API_URL2,
7639
- GUEST_EMAIL_SUFFIX: () => GUEST_EMAIL_SUFFIX,
7640
- GuestReadOnlyError: () => GuestReadOnlyError,
7641
- NotAuthenticatedError: () => NotAuthenticatedError,
7642
- clearActiveCredential: () => clearActiveCredential,
7643
- enforcingKey: () => enforcingKey,
7644
- isActiveAccount: () => isActiveAccount,
7645
- isAuthenticated: () => isAuthenticated,
7646
- isGuestAccount: () => isGuestAccount,
7647
- listAccounts: () => listAccounts,
7648
- removeAccount: () => removeAccount,
7649
- request: () => request,
7650
- resolveCredentials: () => resolveCredentials,
7651
- saveAccount: () => saveAccount,
7652
- setActiveAccount: () => setActiveAccount,
7653
- setViewCredentials: () => setViewCredentials
7654
- });
7655
7719
  import { readFileSync as readFileSync9, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync5 } from "fs";
7656
7720
  import { resolve as resolve4, join as join9 } from "path";
7657
7721
  import { homedir as homedir7 } from "os";
@@ -7703,15 +7767,6 @@ function setViewCredentials(creds) {
7703
7767
  viewOverride = creds;
7704
7768
  cached2 = null;
7705
7769
  }
7706
- function isGuestAccount() {
7707
- try {
7708
- const active2 = loginCredentialFile().apiKey;
7709
- if (!active2) return false;
7710
- return listAccounts().some((a) => a.apiKey === active2 && (a.email ?? "").endsWith(GUEST_EMAIL_SUFFIX));
7711
- } catch {
7712
- return false;
7713
- }
7714
- }
7715
7770
  function enforcingKey() {
7716
7771
  return loginCredentialFile().apiKey ?? null;
7717
7772
  }
@@ -7814,9 +7869,6 @@ function buildUrl(base, path, query) {
7814
7869
  return url.toString();
7815
7870
  }
7816
7871
  async function request(method, path, opts = {}) {
7817
- if (method !== "GET" && isGuestAccount()) {
7818
- throw new GuestReadOnlyError();
7819
- }
7820
7872
  const creds = resolveCredentials(opts.apiUrl);
7821
7873
  const url = buildUrl(creds.apiUrl, path, opts.query);
7822
7874
  const headers = {
@@ -7872,7 +7924,7 @@ async function request(method, path, opts = {}) {
7872
7924
  }
7873
7925
  return json;
7874
7926
  }
7875
- var DEFAULT_API_URL2, accountsFile, viewOverride, GUEST_EMAIL_SUFFIX, ApiError, GuestReadOnlyError, NotAuthenticatedError, cached2;
7927
+ var DEFAULT_API_URL2, accountsFile, viewOverride, ApiError, NotAuthenticatedError, cached2;
7876
7928
  var init_client = __esm({
7877
7929
  "src/api-client/client.ts"() {
7878
7930
  "use strict";
@@ -7880,7 +7932,6 @@ var init_client = __esm({
7880
7932
  DEFAULT_API_URL2 = "https://api.solongate.com";
7881
7933
  accountsFile = () => join9(homedir7(), ".solongate", "accounts.json");
7882
7934
  viewOverride = null;
7883
- GUEST_EMAIL_SUFFIX = "@anonymous.solongate.invalid";
7884
7935
  ApiError = class extends Error {
7885
7936
  status;
7886
7937
  code;
@@ -7891,12 +7942,6 @@ var init_client = __esm({
7891
7942
  this.code = code;
7892
7943
  }
7893
7944
  };
7894
- GuestReadOnlyError = class extends Error {
7895
- constructor() {
7896
- super("Guest account: sign up at auth.solongate.com to change anything. Your project, its policy and everything recorded carry over.");
7897
- this.name = "GuestReadOnlyError";
7898
- }
7899
- };
7900
7945
  NotAuthenticatedError = class extends Error {
7901
7946
  constructor() {
7902
7947
  super("Not logged in. Run `solongate` and log in from the Accounts panel.");
@@ -9237,18 +9282,18 @@ function LivePanel({ active: active2 }) {
9237
9282
  }
9238
9283
  ) : null,
9239
9284
  activeAction ? /* @__PURE__ */ jsx2(Text2, { wrap: "truncate", backgroundColor: activeAction.level === "bad" ? "#3d1220" : "#123d1f", color: activeAction.level === "bad" ? "#ff6b6b" : "#7bd88f", bold: true, children: ` ${truncate2(activeAction.text, innerW - 4)} ` }) : null,
9240
- /* @__PURE__ */ jsxs2(Box2, { height: 1 + chartH, children: [
9241
- /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: leftW, marginRight: 2, children: [
9285
+ /* @__PURE__ */ jsxs2(Box2, { height: 1 + chartH, overflow: "hidden", children: [
9286
+ /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: leftW, height: 1 + chartH, marginRight: 2, overflow: "hidden", children: [
9242
9287
  /* @__PURE__ */ jsx2(PaneTitle, { label: "TRAFFIC", extra: `${trafficLabel} \xB7 peak ${Math.max(0, ...traffic)} \xB7 red = denials`, width: leftW }),
9243
9288
  /* @__PURE__ */ jsx2(ColumnChart, { series: traffic, hot: trafficHot, height: chartH, width: leftW, color: theme.accent })
9244
9289
  ] }),
9245
- /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: rightW, children: [
9290
+ /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: rightW, height: 1 + chartH, overflow: "hidden", children: [
9246
9291
  /* @__PURE__ */ jsx2(PaneTitle, { label: "API LATENCY", extra: `now ${latNow}ms \xB7 med ${latMed}ms \xB7 amber >${Math.round(latHotAt)}ms`, width: rightW }),
9247
9292
  /* @__PURE__ */ jsx2(ColumnChart, { series: lat, hot: lat.map((v) => v > latHotAt), height: chartH, width: rightW, color: "white", hotColor: "#ffb454" })
9248
9293
  ] })
9249
9294
  ] }),
9250
- /* @__PURE__ */ jsxs2(Box2, { height: colH, children: [
9251
- /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, overflow: "hidden", children: [
9295
+ /* @__PURE__ */ jsxs2(Box2, { height: colH, overflow: "hidden", children: [
9296
+ /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, height: colH, marginRight: 2, overflow: "hidden", children: [
9252
9297
  /* @__PURE__ */ jsx2(PaneTitle, { label: "LAYERS", extra: "l = inspect", width: colW }),
9253
9298
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
9254
9299
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "RATELIMIT ".padEnd(10) }),
@@ -9261,7 +9306,11 @@ function LivePanel({ active: active2 }) {
9261
9306
  /* @__PURE__ */ jsx2(Text2, { color: dl?.mode === "block" ? theme.ok : dl?.mode === "detect" ? theme.warn : theme.dim, children: (dl?.mode ?? "?").padEnd(7) }),
9262
9307
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: `${(dl?.patterns ?? []).length} builtin \xB7 ${(dl?.custom ?? []).length} custom` })
9263
9308
  ] }),
9264
- dlpBars.length ? dlpBars.map((d2) => /* @__PURE__ */ jsx2(HBar, { label: truncate2(d2.pattern, 10), value: d2.count, max: maxDlpBar, width: Math.max(6, colW - 16), color: theme.bad }, d2.pattern)) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " no dlp hits in last 7 days" }),
9309
+ dlpBars.length ? dlpBars.map((d2) => /* @__PURE__ */ jsx2(HBar, { label: truncate2(d2.pattern, 10), value: d2.count, max: maxDlpBar, width: Math.max(6, colW - 16), color: theme.bad }, d2.pattern)) : (
9310
+ // 36 chars against a colW that is ~31 at cols=100: without truncate
9311
+ // this wrapped onto a second line and pushed the column over colH.
9312
+ /* @__PURE__ */ jsx2(Text2, { wrap: "truncate", color: theme.dim, children: " no dlp hits in last 7 days" })
9313
+ ),
9265
9314
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
9266
9315
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "GUARD".padEnd(10) }),
9267
9316
  ring ? /* @__PURE__ */ jsxs2(Text2, { color: theme.ok, children: [
@@ -9270,7 +9319,7 @@ function LivePanel({ active: active2 }) {
9270
9319
  ] }) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "no local ring in cwd" })
9271
9320
  ] })
9272
9321
  ] }),
9273
- /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, overflow: "hidden", children: [
9322
+ /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, height: colH, marginRight: 2, overflow: "hidden", children: [
9274
9323
  /* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: mode === "pick" ? "\u2191\u2193 pick \xB7 enter open" : `${combinedSess.length} \xB7 s = inspect`, width: colW }),
9275
9324
  pickable.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "scanning\u2026" }) : null,
9276
9325
  pickable.map((r, i) => {
@@ -9296,7 +9345,7 @@ function LivePanel({ active: active2 }) {
9296
9345
  ] }, r.id);
9297
9346
  })
9298
9347
  ] }),
9299
- /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, overflow: "hidden", children: [
9348
+ /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, height: colH, overflow: "hidden", children: [
9300
9349
  /* @__PURE__ */ jsx2(PaneTitle, { label: "EVENT LOG", extra: "system heartbeat", width: colW }),
9301
9350
  log3.slice(-(colH - 1)).map((l, i) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
9302
9351
  /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
@@ -11503,26 +11552,7 @@ var init_doctor = __esm({
11503
11552
  import { Box as Box8, Text as Text8, useInput as useInput7 } from "ink";
11504
11553
  import TextInput6 from "ink-text-input";
11505
11554
  import { useEffect as useEffect7, useRef as useRef3, useState as useState8 } from "react";
11506
- import { readFileSync as readFileSync11, readdirSync as readdirSync2 } from "fs";
11507
- import { homedir as homedir11 } from "os";
11508
- import { join as join13 } from "path";
11509
11555
  import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
11510
- function guestAllowance() {
11511
- try {
11512
- const dir = join13(homedir11(), ".solongate");
11513
- for (const f of readdirSync2(dir)) {
11514
- if (!f.startsWith(".policy-cache-") || !f.endsWith(".json")) continue;
11515
- try {
11516
- const c2 = JSON.parse(readFileSync11(join13(dir, f), "utf-8"));
11517
- const q = c2?.security?.guestQuota;
11518
- if (q && typeof q.limit === "number") return { used: q.used ?? 0, limit: q.limit };
11519
- } catch {
11520
- }
11521
- }
11522
- } catch {
11523
- }
11524
- return null;
11525
- }
11526
11556
  function SettingsPanel({
11527
11557
  active: active2,
11528
11558
  focused,
@@ -11543,6 +11573,17 @@ function SettingsPanel({
11543
11573
  const [diagBusy, setDiagBusy] = useState8(null);
11544
11574
  const [autoUp, setAutoUp] = useState8(() => autoUpdateEnabled());
11545
11575
  const [updBusy, setUpdBusy] = useState8(false);
11576
+ const [canInstall, setCanInstall] = useState8(null);
11577
+ useEffect7(() => {
11578
+ let live2 = true;
11579
+ globalInstallCheck().then((r) => {
11580
+ if (live2) setCanInstall(r.writable);
11581
+ }).catch(() => {
11582
+ });
11583
+ return () => {
11584
+ live2 = false;
11585
+ };
11586
+ }, []);
11546
11587
  const [diag, setDiag] = useState8(null);
11547
11588
  const [diagStep, setDiagStep] = useState8(0);
11548
11589
  const DIAG_STEPS = {
@@ -11560,7 +11601,6 @@ function SettingsPanel({
11560
11601
  const step = setInterval(() => setDiagStep((s) => Math.min(s + 1, total - 1)), 220);
11561
11602
  return () => clearInterval(step);
11562
11603
  }, [diagBusy]);
11563
- void guestAllowance;
11564
11604
  const ver = currentVersion();
11565
11605
  useEffect7(() => {
11566
11606
  let live2 = true;
@@ -11822,15 +11862,27 @@ function SettingsPanel({
11822
11862
  if (updBusy) return;
11823
11863
  setUpdBusy(true);
11824
11864
  setMsg({ text: "updating\u2026", level: "ok" });
11865
+ setDiag(null);
11825
11866
  void (async () => {
11826
11867
  try {
11827
11868
  const res = await updateNow();
11828
11869
  if (res.status === "updated") setMsg({ text: `\u2713 v${res.version} installed \xB7 restart (q, then solongate) to apply`, level: "ok" });
11829
11870
  else if (res.status === "current") setMsg({ text: `\u2713 already on the latest version (v${res.version})`, level: "ok" });
11830
- else if (res.status === "needs-admin") setMsg({ text: `\u2717 npm needs admin rights here \u2014 run: ${adminInstallCommand()}`, level: "bad" });
11831
- else if (res.status === "unreachable") setMsg({ text: "\u2717 could not reach the npm registry", level: "bad" });
11871
+ else if (res.status === "needs-admin") {
11872
+ setMsg({ text: `\u2717 v${res.version} needs admin rights \u2014 run the two commands below`, level: "bad" });
11873
+ setDiag({
11874
+ of: "cli-update",
11875
+ lines: [
11876
+ { text: "npm\u2019s global folder belongs to root on this machine (the usual macOS", level: "warn" },
11877
+ { text: "setup), so SolonGate cannot install there by itself. In a terminal:", level: "warn" },
11878
+ ...adminUpdateSteps().map((s) => ({ text: s.trim(), level: "ok" })),
11879
+ { text: "run repair WITHOUT sudo, or the guard hooks land in root\u2019s home", level: "dim" }
11880
+ ]
11881
+ });
11882
+ } else if (res.status === "unreachable") setMsg({ text: "\u2717 could not reach the npm registry", level: "bad" });
11832
11883
  else setMsg({ text: `\u2717 update to v${res.version} failed \u2014 see ~/.solongate/self-update.log`, level: "bad" });
11833
11884
  setLatest(await latestVersion());
11885
+ setCanInstall((await globalInstallCheck()).writable);
11834
11886
  } finally {
11835
11887
  setUpdBusy(false);
11836
11888
  }
@@ -11844,7 +11896,7 @@ function SettingsPanel({
11844
11896
  setAutoUpdate(next);
11845
11897
  setAutoUp(next);
11846
11898
  setMsg(
11847
- next ? { text: "\u2713 auto-update on \u2014 new versions install in the background", level: "ok" } : { text: "\u2713 auto-update off \u2014 update from this row or with: solongate update", level: "ok" }
11899
+ next ? canInstall === false ? { text: "\u2713 auto-update on \u2014 but npm needs sudo here, so it still cannot install on its own", level: "bad" } : { text: "\u2713 auto-update on \u2014 new versions install in the background", level: "ok" } : { text: "\u2713 auto-update off \u2014 update from this row or with: solongate update", level: "ok" }
11848
11900
  );
11849
11901
  } else if (r.kind === "ll-enabled") {
11850
11902
  if (!local) return;
@@ -12113,15 +12165,6 @@ function SettingsPanel({
12113
12165
  cursor(r),
12114
12166
  /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add account (enter \u2014 device login in the browser)" })
12115
12167
  ] });
12116
- case "allowance": {
12117
- const left = Math.max(0, r.limit - r.used);
12118
- return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
12119
- cursor(r),
12120
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "allowance".padEnd(11) }),
12121
- /* @__PURE__ */ jsx8(Text8, { color: left === 0 ? theme.bad : left <= 20 ? theme.warn : theme.ok, bold: true, children: `${r.used}/${r.limit} tool calls` }),
12122
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: left === 0 ? " used up \u2014 every call is denied \xB7 create an account to carry on" : ` ${left} left on this guest account \xB7 create an account for no limit` })
12123
- ] });
12124
- }
12125
12168
  case "guard":
12126
12169
  return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
12127
12170
  cursor(r),
@@ -12162,7 +12205,10 @@ function SettingsPanel({
12162
12205
  cursor(r),
12163
12206
  /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "version".padEnd(11) }),
12164
12207
  /* @__PURE__ */ jsx8(Text8, { color: behind ? theme.warn : theme.ok, children: `v${ver}` }),
12165
- updBusy ? /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, children: ` ${spin} updating\u2026` }) : behind ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: ` v${latest} on npm \xB7 enter updates now` }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: latest ? " latest \xB7 enter checks again" : " enter checks npm and updates" })
12208
+ updBusy ? /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, children: ` ${spin} updating\u2026` }) : behind && canInstall === false ? (
12209
+ // Said before they press enter, not after a minute of npm noise.
12210
+ /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: ` v${latest} on npm \xB7 needs sudo here \xB7 enter shows how` })
12211
+ ) : behind ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: ` v${latest} on npm \xB7 enter updates now` }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: latest ? " latest \xB7 enter checks again" : " enter checks npm and updates" })
12166
12212
  ] });
12167
12213
  }
12168
12214
  case "auto-update":
@@ -12170,7 +12216,7 @@ function SettingsPanel({
12170
12216
  cursor(r),
12171
12217
  /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "auto".padEnd(11) }),
12172
12218
  onOff(autoUp),
12173
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: autoUpdateForcedByEnv() ? " set by SOLONGATE_AUTO_UPDATE" : autoUp ? " installs new versions in the background \xB7 enter toggles" : " off: nothing installs on its own (npm -g may need sudo) \xB7 enter toggles" })
12219
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: autoUpdateForcedByEnv() ? " set by SOLONGATE_AUTO_UPDATE" : canInstall === false ? " cannot work here: npm needs sudo, so updates stay manual \xB7 enter toggles" : autoUp ? " installs new versions in the background \xB7 enter toggles" : " off: nothing installs on its own (npm -g may need sudo) \xB7 enter toggles" })
12174
12220
  ] });
12175
12221
  case "ll-enabled":
12176
12222
  return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
@@ -12224,7 +12270,7 @@ function SettingsPanel({
12224
12270
  ] });
12225
12271
  }
12226
12272
  };
12227
- const sectionOf = (r) => r.kind === "acct" || r.kind === "acct-add" || r.kind === "allowance" ? "ACCOUNTS" : r.kind === "guard" || r.kind === "self" || r.kind === "doctor" || r.kind === "repair" ? "PROTECTION" : r.kind === "cli-update" || r.kind === "auto-update" ? "UPDATES" : r.kind === "ll-enabled" || r.kind === "ll-path" || r.kind === "ll-server" ? "LOCAL LOGS" : r.kind === "wh" || r.kind === "wh-add" ? "WEBHOOKS" : "ALERTS";
12273
+ const sectionOf = (r) => r.kind === "acct" || r.kind === "acct-add" ? "ACCOUNTS" : r.kind === "guard" || r.kind === "self" || r.kind === "doctor" || r.kind === "repair" ? "PROTECTION" : r.kind === "cli-update" || r.kind === "auto-update" ? "UPDATES" : r.kind === "ll-enabled" || r.kind === "ll-path" || r.kind === "ll-server" ? "LOCAL LOGS" : r.kind === "wh" || r.kind === "wh-add" ? "WEBHOOKS" : "ALERTS";
12228
12274
  const SECTION_DESC = {
12229
12275
  ACCOUNTS: `on this device (${accounts.length}) \xB7 \u25CF viewing \xB7 ACTIVE = guard key \xB7 x removes`,
12230
12276
  PROTECTION: "guard hook: enter install/update \xB7 d remove \xB7 self-protection \xB7 doctor + repair",
@@ -12269,7 +12315,7 @@ function SettingsPanel({
12269
12315
  lineKey.push("");
12270
12316
  });
12271
12317
  }
12272
- if (diag && (r.kind === "doctor" && diag.of === "doctor" || r.kind === "repair" && diag.of === "repair")) {
12318
+ if (diag && (r.kind === "doctor" && diag.of === "doctor" || r.kind === "repair" && diag.of === "repair" || r.kind === "cli-update" && diag.of === "cli-update")) {
12273
12319
  diag.lines.forEach((l, i) => {
12274
12320
  lineEls.push(
12275
12321
  /* @__PURE__ */ jsx8(
@@ -12418,23 +12464,7 @@ function App() {
12418
12464
  const acctIdx = Math.max(0, accounts.findIndex((a) => a.apiKey === viewKey));
12419
12465
  const cur = accounts[acctIdx];
12420
12466
  const rawEmail = cur?.email ?? "";
12421
- const isGuestAccount2 = rawEmail.endsWith("@anonymous.solongate.invalid");
12422
- const acctLabel2 = !cur ? "not logged in" : isGuestAccount2 ? "Anonymous" : rawEmail || cur.user || cur.project || `account \u2026${cur.apiKey.slice(-4)}`;
12423
- const [guestSpent, setGuestSpent] = useState9(false);
12424
- useEffect8(() => {
12425
- if (!isGuestAccount2) {
12426
- setGuestSpent(false);
12427
- return;
12428
- }
12429
- let alive = true;
12430
- api.auth.me().then((r) => {
12431
- if (alive) setGuestSpent(r.guest?.exhausted === true);
12432
- }).catch(() => {
12433
- });
12434
- return () => {
12435
- alive = false;
12436
- };
12437
- }, [isGuestAccount2, cur?.apiKey]);
12467
+ const acctLabel2 = !cur ? "not logged in" : rawEmail || cur.user || cur.project || `account \u2026${cur.apiKey.slice(-4)}`;
12438
12468
  useEffect8(() => {
12439
12469
  if (!cur || cur.email) return;
12440
12470
  let alive = true;
@@ -12520,12 +12550,7 @@ function App() {
12520
12550
  /* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
12521
12551
  /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "account: " }),
12522
12552
  /* @__PURE__ */ jsx9(Text9, { color: locked ? theme.warn : theme.accentBright, bold: true, children: acctLabel2 }),
12523
- locked ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 log in from Settings to unlock the dataroom" }) : guestSpent ? (
12524
- // Said on this line rather than on a new one: every panel budgets its
12525
- // height against a fixed shell, so one more line up here silently clips
12526
- // a row off whichever panel is open.
12527
- /* @__PURE__ */ jsx9(Text9, { color: theme.bad, bold: true, children: " \xB7 allowance used up \xB7 every call denied \xB7 create an account to carry on" })
12528
- ) : accounts.length > 1 ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: ` (${acctIdx + 1}/${accounts.length} \xB7 a switch \xB7 Settings to manage)` }) : /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 Settings to add another" }),
12553
+ locked ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 log in from Settings to unlock the dataroom" }) : accounts.length > 1 ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: ` (${acctIdx + 1}/${accounts.length} \xB7 a switch \xB7 Settings to manage)` }) : /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 Settings to add another" }),
12529
12554
  update2.kind === "updating" ? /* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: ` \u2191 updating to v${update2.version}\u2026` }) : update2.kind === "updated" ? /* @__PURE__ */ jsx9(Text9, { color: theme.ok, bold: true, children: ` \u2191 v${update2.version} installed \xB7 restart (q, then solongate) to apply` }) : update2.kind === "available" ? /* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: ` \u2191 v${update2.version} out \xB7 Settings \u2192 UPDATES` }) : update2.kind === "needs-admin" ? /* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: ` \u2191 v${update2.version} needs admin rights \xB7 Settings \u2192 UPDATES` }) : null
12530
12555
  ] }),
12531
12556
  /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexGrow: 1, children: [
@@ -12603,8 +12628,8 @@ __export(tui_exports, {
12603
12628
  launchTui: () => launchTui
12604
12629
  });
12605
12630
  import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync9 } from "fs";
12606
- import { homedir as homedir12 } from "os";
12607
- import { join as join14 } from "path";
12631
+ import { homedir as homedir11 } from "os";
12632
+ import { join as join13 } from "path";
12608
12633
  import { render } from "ink";
12609
12634
  import { jsx as jsx10 } from "react/jsx-runtime";
12610
12635
  async function launchTui() {
@@ -12615,11 +12640,11 @@ async function launchTui() {
12615
12640
  return;
12616
12641
  }
12617
12642
  process.stdout.write("\x1B[?1049h\x1B[H");
12618
- const debugLog = join14(homedir12(), ".solongate", "dataroom-debug.log");
12643
+ const debugLog = join13(homedir11(), ".solongate", "dataroom-debug.log");
12619
12644
  const saved = { log: console.log, warn: console.warn, error: console.error, info: console.info, debug: console.debug };
12620
12645
  const toFile = (level) => (...args) => {
12621
12646
  try {
12622
- mkdirSync9(join14(homedir12(), ".solongate"), { recursive: true });
12647
+ mkdirSync9(join13(homedir11(), ".solongate"), { recursive: true });
12623
12648
  appendFileSync2(debugLog, `${(/* @__PURE__ */ new Date()).toISOString()} [${level}] ${args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}
12624
12649
  `);
12625
12650
  } catch {
@@ -12646,7 +12671,7 @@ var init_tui = __esm({
12646
12671
  });
12647
12672
 
12648
12673
  // src/commands/policy.ts
12649
- import { readFileSync as readFileSync12 } from "fs";
12674
+ import { readFileSync as readFileSync11 } from "fs";
12650
12675
  async function run2(argv) {
12651
12676
  const { positionals, flags } = parse(argv);
12652
12677
  const sub = positionals[0];
@@ -12803,7 +12828,7 @@ function printRules(rules) {
12803
12828
  }
12804
12829
  async function resolveRules(target) {
12805
12830
  if (target.endsWith(".json")) {
12806
- const parsed = JSON.parse(readFileSync12(target, "utf-8"));
12831
+ const parsed = JSON.parse(readFileSync11(target, "utf-8"));
12807
12832
  return parsed.rules ?? [];
12808
12833
  }
12809
12834
  const p = await api.policies.get(target);
@@ -13517,10 +13542,10 @@ __export(logs_server_exports, {
13517
13542
  runLogsServer: () => runLogsServer
13518
13543
  });
13519
13544
  import { createServer } from "http";
13520
- import { readFileSync as readFileSync13, statSync as statSync4 } from "fs";
13521
- import { resolve as resolve5, join as join15, isAbsolute as isAbsolute2 } from "path";
13522
- import { homedir as homedir13 } from "os";
13523
- import { readdirSync as readdirSync3 } from "fs";
13545
+ import { readFileSync as readFileSync12, statSync as statSync4 } from "fs";
13546
+ import { resolve as resolve5, join as join14, isAbsolute as isAbsolute2 } from "path";
13547
+ import { homedir as homedir12 } from "os";
13548
+ import { readdirSync as readdirSync2 } from "fs";
13524
13549
  function allowedOrigins() {
13525
13550
  const base = [
13526
13551
  "https://dashboard.solongate.com",
@@ -13536,15 +13561,15 @@ function resolveLocalLogDir(rawPath) {
13536
13561
  const dir = String(rawPath || "").trim().replace(/[\\/]+$/, "");
13537
13562
  if (!dir) return null;
13538
13563
  if (isAbsolute2(dir)) return dir;
13539
- return resolve5(homedir13(), ".solongate", "local-logs");
13564
+ return resolve5(homedir12(), ".solongate", "local-logs");
13540
13565
  }
13541
13566
  async function findLogDir() {
13542
- const base = resolve5(homedir13(), ".solongate");
13567
+ const base = resolve5(homedir12(), ".solongate");
13543
13568
  try {
13544
- const files = readdirSync3(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
13569
+ const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
13545
13570
  for (const f of files) {
13546
13571
  try {
13547
- const c2 = JSON.parse(readFileSync13(join15(base, f), "utf-8"));
13572
+ const c2 = JSON.parse(readFileSync12(join14(base, f), "utf-8"));
13548
13573
  const p = c2?.security?.localLogs?.path;
13549
13574
  if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
13550
13575
  } catch {
@@ -13553,7 +13578,7 @@ async function findLogDir() {
13553
13578
  } catch {
13554
13579
  }
13555
13580
  try {
13556
- const cfgRaw = readFileSync13(join15(base, "cloud-guard.json"), "utf-8");
13581
+ const cfgRaw = readFileSync12(join14(base, "cloud-guard.json"), "utf-8");
13557
13582
  const { apiKey, apiUrl } = JSON.parse(cfgRaw);
13558
13583
  if (apiKey) {
13559
13584
  const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
@@ -13582,7 +13607,7 @@ function setCors(req, res) {
13582
13607
  }
13583
13608
  function fileInfo(dir) {
13584
13609
  if (!dir) return { file: null, exists: false, size: 0, mtimeMs: 0 };
13585
- const file = join15(dir, LOG_FILENAME);
13610
+ const file = join14(dir, LOG_FILENAME);
13586
13611
  try {
13587
13612
  const st = statSync4(file);
13588
13613
  return { file, exists: true, size: st.size, mtimeMs: st.mtimeMs };
@@ -13665,7 +13690,7 @@ async function runLogsServer() {
13665
13690
  return;
13666
13691
  }
13667
13692
  try {
13668
- const text = readFileSync13(info.file, "utf-8");
13693
+ const text = readFileSync12(info.file, "utf-8");
13669
13694
  res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8", "Last-Modified": lastMod, "X-Solongate-Exists": "1" });
13670
13695
  res.end(text);
13671
13696
  } catch {
@@ -13718,9 +13743,9 @@ var init_logs_server = __esm({
13718
13743
  });
13719
13744
 
13720
13745
  // src/index.ts
13721
- import { readFileSync as readFileSync14 } from "fs";
13746
+ import { readFileSync as readFileSync13 } from "fs";
13722
13747
  import { fileURLToPath as fileURLToPath4 } from "url";
13723
- import { dirname as dirname4, join as join16 } from "path";
13748
+ import { dirname as dirname4, join as join15 } from "path";
13724
13749
 
13725
13750
  // src/config.ts
13726
13751
  import { readFileSync, existsSync } from "fs";
@@ -17115,8 +17140,8 @@ if (!IS_HUMAN_CLI) {
17115
17140
  }
17116
17141
  var PKG_VERSION = (() => {
17117
17142
  try {
17118
- const p = join16(dirname4(fileURLToPath4(import.meta.url)), "..", "package.json");
17119
- return JSON.parse(readFileSync14(p, "utf-8")).version || "unknown";
17143
+ const p = join15(dirname4(fileURLToPath4(import.meta.url)), "..", "package.json");
17144
+ return JSON.parse(readFileSync13(p, "utf-8")).version || "unknown";
17120
17145
  } catch {
17121
17146
  return "unknown";
17122
17147
  }
@@ -17157,7 +17182,7 @@ function printHelp() {
17157
17182
  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}`);
17158
17183
  head("Setup & status");
17159
17184
  cmd("solongate", "open the dataroom UI (login, policies, audit, settings)");
17160
- cmd("update", "update SolonGate to the newest version and refresh the guard");
17185
+ cmd("update", "update SolonGate and refresh the guard (tells you if it needs sudo)");
17161
17186
  cmd("update auto on|off", "background auto-update (default off \u2014 on macOS npm -g often needs sudo)");
17162
17187
  cmd("repair", "restore the guard + hook + settings files if they were deleted or disarmed");
17163
17188
  cmd("doctor", "health check: login, policy, guard, local logs");
@@ -17287,13 +17312,6 @@ async function main() {
17287
17312
  }
17288
17313
  const MGMT_COMMANDS = /* @__PURE__ */ new Set(["policy", "ratelimit", "dlp", "stats", "audit", "sessions", "session", "doctor", "watch", "alerts", "webhooks"]);
17289
17314
  if (MGMT_COMMANDS.has(subcommand ?? "")) {
17290
- const { isGuestAccount: isGuestAccount2 } = await Promise.resolve().then(() => (init_client(), client_exports));
17291
- if (subcommand !== "doctor" && isGuestAccount2()) {
17292
- process.stderr.write(
17293
- "\n This is a guest account, which can look but not change.\n Create an account to use the management commands:\n https://auth.solongate.com\n\n Your project, its policy and everything already recorded carry over,\n and this device keeps working without being paired again.\n\n"
17294
- );
17295
- process.exit(1);
17296
- }
17297
17315
  const { runCommand: runCommand2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
17298
17316
  const code = await runCommand2(subcommand, process.argv.slice(3));
17299
17317
  process.exit(code);
@@ -17308,7 +17326,7 @@ async function main() {
17308
17326
  if (subcommand === "update") {
17309
17327
  if (process.argv[3] === "auto") {
17310
17328
  const { runAutoUpdateCommand: runAutoUpdateCommand2 } = await Promise.resolve().then(() => (init_self_update(), self_update_exports));
17311
- process.exit(runAutoUpdateCommand2(process.argv[4]));
17329
+ process.exit(await runAutoUpdateCommand2(process.argv[4]));
17312
17330
  }
17313
17331
  const { runUpdateCommand: runUpdateCommand2 } = await Promise.resolve().then(() => (init_self_update(), self_update_exports));
17314
17332
  process.exit(await runUpdateCommand2());