@solongate/proxy 0.81.80 → 0.81.81

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.
@@ -11,6 +11,20 @@ export declare function globalPaths(): {
11
11
  };
12
12
  export declare function clearGuardUpdateCheck(): boolean;
13
13
  export declare function runGlobalRestore(): void;
14
+ /**
15
+ * TUI-safe (re)install — the dataroom's one-key "install / update guard" action.
16
+ * Writes the hook files THIS CLI ships (readGuard = the current package's bundle,
17
+ * so a self-updated dataroom installs the newest hooks DIRECTLY — no waiting for a
18
+ * tool call) and registers them in the global Claude settings, reusing the key
19
+ * already stored in cloud-guard.json. No prompt, no stdout, no process.exit — safe
20
+ * to call from Ink. Returns a status; asks the user to log in when no key exists.
21
+ * The auto-shield shell shim is left to `solongate` login (it can print), so this
22
+ * touches only the guard/audit/stop hooks — the primary protection.
23
+ */
24
+ export declare function installGlobalQuiet(): {
25
+ ok: boolean;
26
+ message: string;
27
+ };
14
28
  /**
15
29
  * Are the SolonGate guard hooks currently installed in the global Claude settings
16
30
  * on THIS device? Reflects a local install/remove IMMEDIATELY — unlike the cloud
@@ -137,6 +137,54 @@ function runGlobalRestore() {
137
137
  }
138
138
  console.log(" Global SolonGate enforcement uninstalled. Restart Claude Code.");
139
139
  }
140
+ function installGlobalQuiet() {
141
+ try {
142
+ const p = globalPaths();
143
+ let apiKey = process.env["SOLONGATE_API_KEY"] || "";
144
+ let apiUrl = process.env["SOLONGATE_API_URL"] || "https://api.solongate.com";
145
+ try {
146
+ const cfg = JSON.parse(readFileSync(p.configPath, "utf-8"));
147
+ if (cfg && typeof cfg.apiKey === "string") apiKey = apiKey || cfg.apiKey;
148
+ if (cfg && typeof cfg.apiUrl === "string") apiUrl = cfg.apiUrl;
149
+ } catch {
150
+ }
151
+ if (!apiKey) return { ok: false, message: "no login on this device \u2014 add an account first (Accounts \u2192 + add)" };
152
+ mkdirSync(p.hooksDir, { recursive: true });
153
+ mkdirSync(p.claudeDir, { recursive: true });
154
+ unlockProtected();
155
+ writeFileSync(join(p.hooksDir, "guard.mjs"), readGuard());
156
+ writeFileSync(join(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
157
+ writeFileSync(join(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
158
+ writeFileSync(join(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
159
+ writeFileSync(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
160
+ let existing = {};
161
+ if (existsSync(p.settingsPath)) {
162
+ const raw = readFileSync(p.settingsPath, "utf-8");
163
+ if (!existsSync(p.backupPath)) writeFileSync(p.backupPath, raw);
164
+ try {
165
+ existing = JSON.parse(raw);
166
+ } catch {
167
+ existing = {};
168
+ }
169
+ }
170
+ const nodeBin = process.execPath.replace(/\\/g, "/");
171
+ const call = process.platform === "win32" ? "& " : "";
172
+ const hookCmd = (script) => `${call}"${nodeBin}" "${join(p.hooksDir, script).replace(/\\/g, "/")}" claude-code "Claude Code"`;
173
+ const merged = {
174
+ ...existing,
175
+ hooks: {
176
+ PreToolUse: [{ matcher: "", hooks: [{ type: "command", command: hookCmd("guard.mjs") }] }],
177
+ PostToolUse: [{ matcher: "", hooks: [{ type: "command", command: hookCmd("audit.mjs") }] }],
178
+ Stop: [{ matcher: "", hooks: [{ type: "command", command: hookCmd("stop.mjs") }] }]
179
+ }
180
+ };
181
+ writeFileSync(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
182
+ if (process.env["SOLONGATE_OS_LOCK"] === "1") lockProtected();
183
+ return { ok: true, message: "guard installed \u2014 active in NEW Claude Code sessions (open a new one)" };
184
+ } catch (e) {
185
+ return { ok: false, message: e instanceof Error ? e.message : String(e) };
186
+ }
187
+ }
140
188
  function isGuardInstalled() {
141
189
  try {
142
190
  const p = globalPaths();
@@ -309,6 +357,7 @@ export {
309
357
  clearGuardUpdateCheck,
310
358
  globalPaths,
311
359
  installClaudeShim,
360
+ installGlobalQuiet,
312
361
  installGlobalWithKey,
313
362
  isGuardInstalled,
314
363
  lockProtected,
package/dist/index.js CHANGED
@@ -10091,6 +10091,32 @@ import { homedir as homedir9 } from "os";
10091
10091
  import { fileURLToPath as fileURLToPath3 } from "url";
10092
10092
  import { createInterface } from "readline";
10093
10093
  import { execFileSync as execFileSync2 } from "child_process";
10094
+ function lockFile(file) {
10095
+ if (!existsSync4(file)) return;
10096
+ try {
10097
+ if (process.platform === "win32") {
10098
+ try {
10099
+ execFileSync2("icacls", [file, "/deny", "*S-1-1-0:(WD,AD,DC,DE)"], { stdio: "ignore" });
10100
+ } catch {
10101
+ }
10102
+ try {
10103
+ execFileSync2("attrib", ["+R", file], { stdio: "ignore" });
10104
+ } catch {
10105
+ }
10106
+ } else if (process.platform === "darwin") {
10107
+ try {
10108
+ execFileSync2("chflags", ["uchg", file], { stdio: "ignore" });
10109
+ } catch {
10110
+ }
10111
+ } else {
10112
+ try {
10113
+ execFileSync2("chattr", ["+i", file], { stdio: "ignore" });
10114
+ } catch {
10115
+ }
10116
+ }
10117
+ } catch {
10118
+ }
10119
+ }
10094
10120
  function unlockFile(file) {
10095
10121
  if (!existsSync4(file)) return;
10096
10122
  try {
@@ -10132,6 +10158,9 @@ function protectedTargets() {
10132
10158
  p.settingsPath
10133
10159
  ];
10134
10160
  }
10161
+ function lockProtected() {
10162
+ for (const f of protectedTargets()) lockFile(f);
10163
+ }
10135
10164
  function unlockProtected() {
10136
10165
  for (const f of protectedTargets()) unlockFile(f);
10137
10166
  }
@@ -10150,12 +10179,59 @@ function globalPaths() {
10150
10179
  configPath: join11(sgDir, "cloud-guard.json")
10151
10180
  };
10152
10181
  }
10153
- function clearGuardUpdateCheck() {
10182
+ function readHook(filename) {
10183
+ return readFileSync9(join11(HOOKS_DIR, filename), "utf-8");
10184
+ }
10185
+ function readGuard() {
10186
+ const bundled = join11(HOOKS_DIR, "guard.bundled.mjs");
10187
+ return existsSync4(bundled) ? readFileSync9(bundled, "utf-8") : readHook("guard.mjs");
10188
+ }
10189
+ function installGlobalQuiet() {
10154
10190
  try {
10155
- rmSync2(join11(globalPaths().sgDir, ".hook-update-check"), { force: true });
10156
- return true;
10157
- } catch {
10158
- return false;
10191
+ const p = globalPaths();
10192
+ let apiKey = process.env["SOLONGATE_API_KEY"] || "";
10193
+ let apiUrl = process.env["SOLONGATE_API_URL"] || "https://api.solongate.com";
10194
+ try {
10195
+ const cfg = JSON.parse(readFileSync9(p.configPath, "utf-8"));
10196
+ if (cfg && typeof cfg.apiKey === "string") apiKey = apiKey || cfg.apiKey;
10197
+ if (cfg && typeof cfg.apiUrl === "string") apiUrl = cfg.apiUrl;
10198
+ } catch {
10199
+ }
10200
+ if (!apiKey) return { ok: false, message: "no login on this device \u2014 add an account first (Accounts \u2192 + add)" };
10201
+ mkdirSync8(p.hooksDir, { recursive: true });
10202
+ mkdirSync8(p.claudeDir, { recursive: true });
10203
+ unlockProtected();
10204
+ writeFileSync9(join11(p.hooksDir, "guard.mjs"), readGuard());
10205
+ writeFileSync9(join11(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
10206
+ writeFileSync9(join11(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
10207
+ writeFileSync9(join11(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
10208
+ writeFileSync9(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
10209
+ let existing = {};
10210
+ if (existsSync4(p.settingsPath)) {
10211
+ const raw = readFileSync9(p.settingsPath, "utf-8");
10212
+ if (!existsSync4(p.backupPath)) writeFileSync9(p.backupPath, raw);
10213
+ try {
10214
+ existing = JSON.parse(raw);
10215
+ } catch {
10216
+ existing = {};
10217
+ }
10218
+ }
10219
+ const nodeBin = process.execPath.replace(/\\/g, "/");
10220
+ const call = process.platform === "win32" ? "& " : "";
10221
+ const hookCmd = (script) => `${call}"${nodeBin}" "${join11(p.hooksDir, script).replace(/\\/g, "/")}" claude-code "Claude Code"`;
10222
+ const merged = {
10223
+ ...existing,
10224
+ hooks: {
10225
+ PreToolUse: [{ matcher: "", hooks: [{ type: "command", command: hookCmd("guard.mjs") }] }],
10226
+ PostToolUse: [{ matcher: "", hooks: [{ type: "command", command: hookCmd("audit.mjs") }] }],
10227
+ Stop: [{ matcher: "", hooks: [{ type: "command", command: hookCmd("stop.mjs") }] }]
10228
+ }
10229
+ };
10230
+ writeFileSync9(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
10231
+ if (process.env["SOLONGATE_OS_LOCK"] === "1") lockProtected();
10232
+ return { ok: true, message: "guard installed \u2014 active in NEW Claude Code sessions (open a new one)" };
10233
+ } catch (e) {
10234
+ return { ok: false, message: e instanceof Error ? e.message : String(e) };
10159
10235
  }
10160
10236
  }
10161
10237
  function isGuardInstalled() {
@@ -10279,6 +10355,19 @@ function SettingsPanel({
10279
10355
  const local = localQ.data;
10280
10356
  const guard = guardQ.data;
10281
10357
  const selfProt = selfQ.data;
10358
+ const autoInstalledRef = useRef3(false);
10359
+ useEffect6(() => {
10360
+ if (autoInstalledRef.current) return;
10361
+ if (guardHere && guard && guard.installed != null && !guard.up_to_date) {
10362
+ autoInstalledRef.current = true;
10363
+ const res = installGlobalQuiet();
10364
+ if (res.ok) {
10365
+ setGuardHere(isGuardInstalled());
10366
+ setMsg({ text: `\u2713 guard auto-updated \u2192 v${guard.latest} hooks written \xB7 open a new Claude Code session to apply`, level: "ok" });
10367
+ guardQ.reload();
10368
+ }
10369
+ }
10370
+ }, [guardHere, guard]);
10282
10371
  const [hidden, setHidden] = useState7(/* @__PURE__ */ new Set());
10283
10372
  const webhooks = (whQ.data?.webhooks ?? []).filter((w) => !hidden.has("wh:" + w.id));
10284
10373
  const alerts = (alertQ.data?.rules ?? []).filter((r) => !hidden.has("alert:" + r.id));
@@ -10404,19 +10493,10 @@ function SettingsPanel({
10404
10493
  } else if (r.kind === "acct-add") {
10405
10494
  beginLogin();
10406
10495
  } else if (r.kind === "guard") {
10407
- if (!guardHere) {
10408
- setMsg({ text: "guard is removed on this device \u2014 log in again (Accounts \u2192 + add) to reinstall it", level: "bad" });
10409
- return;
10410
- }
10411
- if (!guard) return;
10412
- if (guard.up_to_date) {
10413
- setMsg({ text: `guard already on the latest hook (v${guard.latest})`, level: "ok" });
10414
- return;
10415
- }
10416
- const armed = clearGuardUpdateCheck();
10417
- setMsg(
10418
- armed ? { text: `\u2713 update armed. now make your agent RUN any command (e.g. "echo ok"), not just chat; the guard installs v${guard.latest} on the next executed command`, level: "ok" } : { text: "\u2717 could not arm the update (check ~/.solongate permissions)", level: "bad" }
10419
- );
10496
+ const res = installGlobalQuiet();
10497
+ setMsg({ text: (res.ok ? "\u2713 " : "\u2717 ") + res.message, level: res.ok ? "ok" : "bad" });
10498
+ setGuardHere(isGuardInstalled());
10499
+ guardQ.reload();
10420
10500
  } else if (r.kind === "self") {
10421
10501
  if (!selfProt) return;
10422
10502
  run12(selfProt.enabled ? "self-protection disabled" : "self-protection enabled", () => api.settings.setSelfProtection(!selfProt.enabled), selfQ.reload);
@@ -10688,12 +10768,13 @@ function SettingsPanel({
10688
10768
  /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "guard".padEnd(11) }),
10689
10769
  !guardHere ? /* @__PURE__ */ jsxs7(Fragment4, { children: [
10690
10770
  /* @__PURE__ */ jsx7(Text7, { color: theme.bad, bold: true, children: "removed" }),
10691
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2014 not installed on this device \xB7 new Claude Code sessions are unguarded" })
10771
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2014 new Claude Code sessions are unguarded \xB7 " }),
10772
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accentBright, children: "enter to install" })
10692
10773
  ] }) : guard ? /* @__PURE__ */ jsxs7(Fragment4, { children: [
10693
10774
  /* @__PURE__ */ jsx7(Text7, { color: guard.up_to_date ? theme.ok : theme.warn, children: `v${guard.installed ?? "?"}` }),
10694
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: guard.up_to_date ? " (latest)" : ` \u2192 v${guard.latest} available \xB7 enter update` }),
10775
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: guard.up_to_date ? " (latest) \xB7 enter reinstall" : ` \u2192 v${guard.latest} available \xB7 enter update` }),
10695
10776
  /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: ` \xB7 ${guard.device_count} device${guard.device_count === 1 ? "" : "s"}${guard.outdated_count ? ` \xB7 ${guard.outdated_count} outdated` : ""} \xB7 d remove` })
10696
- ] }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "loading\u2026" })
10777
+ ] }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "installed \xB7 enter reinstall/update \xB7 d remove" })
10697
10778
  ] });
10698
10779
  case "self":
10699
10780
  return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
@@ -10757,7 +10838,7 @@ function SettingsPanel({
10757
10838
  const sectionOf = (r) => r.kind === "acct" || r.kind === "acct-add" ? "ACCOUNTS" : r.kind === "guard" || r.kind === "self" ? "PROTECTION" : r.kind === "ll-enabled" || r.kind === "ll-path" || r.kind === "ll-server" ? "LOCAL LOGS" : r.kind === "wh" || r.kind === "wh-add" ? "WEBHOOKS" : "ALERTS";
10758
10839
  const SECTION_DESC = {
10759
10840
  ACCOUNTS: `on this device (${accounts.length}) \xB7 \u25CF viewing \xB7 ACTIVE = guard key \xB7 x removes`,
10760
- PROTECTION: "guard hook: enter update \xB7 d remove \xB7 + self-protection toggle",
10841
+ PROTECTION: "guard hook: enter install/update (writes the files) \xB7 d remove \xB7 + self-protection",
10761
10842
  "LOCAL LOGS": "mirror every decision to a file + dashboard link",
10762
10843
  WEBHOOKS: `POST events to a URL (${webhooks.length}) \xB7 t tests`,
10763
10844
  ALERTS: `one email + one telegram (${alerts.length}) \xB7 enter edits \xB7 m on/off`
package/dist/tui/index.js CHANGED
@@ -3148,6 +3148,32 @@ import { createInterface } from "readline";
3148
3148
  import { execFileSync } from "child_process";
3149
3149
  var __dirname = dirname(fileURLToPath(import.meta.url));
3150
3150
  var HOOKS_DIR = resolve2(__dirname, "..", "hooks");
3151
+ function lockFile(file) {
3152
+ if (!existsSync2(file)) return;
3153
+ try {
3154
+ if (process.platform === "win32") {
3155
+ try {
3156
+ execFileSync("icacls", [file, "/deny", "*S-1-1-0:(WD,AD,DC,DE)"], { stdio: "ignore" });
3157
+ } catch {
3158
+ }
3159
+ try {
3160
+ execFileSync("attrib", ["+R", file], { stdio: "ignore" });
3161
+ } catch {
3162
+ }
3163
+ } else if (process.platform === "darwin") {
3164
+ try {
3165
+ execFileSync("chflags", ["uchg", file], { stdio: "ignore" });
3166
+ } catch {
3167
+ }
3168
+ } else {
3169
+ try {
3170
+ execFileSync("chattr", ["+i", file], { stdio: "ignore" });
3171
+ } catch {
3172
+ }
3173
+ }
3174
+ } catch {
3175
+ }
3176
+ }
3151
3177
  function unlockFile(file) {
3152
3178
  if (!existsSync2(file)) return;
3153
3179
  try {
@@ -3189,6 +3215,9 @@ function protectedTargets() {
3189
3215
  p.settingsPath
3190
3216
  ];
3191
3217
  }
3218
+ function lockProtected() {
3219
+ for (const f of protectedTargets()) lockFile(f);
3220
+ }
3192
3221
  function unlockProtected() {
3193
3222
  for (const f of protectedTargets()) unlockFile(f);
3194
3223
  }
@@ -3207,12 +3236,59 @@ function globalPaths() {
3207
3236
  configPath: join6(sgDir, "cloud-guard.json")
3208
3237
  };
3209
3238
  }
3210
- function clearGuardUpdateCheck() {
3239
+ function readHook(filename) {
3240
+ return readFileSync4(join6(HOOKS_DIR, filename), "utf-8");
3241
+ }
3242
+ function readGuard() {
3243
+ const bundled = join6(HOOKS_DIR, "guard.bundled.mjs");
3244
+ return existsSync2(bundled) ? readFileSync4(bundled, "utf-8") : readHook("guard.mjs");
3245
+ }
3246
+ function installGlobalQuiet() {
3211
3247
  try {
3212
- rmSync(join6(globalPaths().sgDir, ".hook-update-check"), { force: true });
3213
- return true;
3214
- } catch {
3215
- return false;
3248
+ const p = globalPaths();
3249
+ let apiKey = process.env["SOLONGATE_API_KEY"] || "";
3250
+ let apiUrl = process.env["SOLONGATE_API_URL"] || "https://api.solongate.com";
3251
+ try {
3252
+ const cfg = JSON.parse(readFileSync4(p.configPath, "utf-8"));
3253
+ if (cfg && typeof cfg.apiKey === "string") apiKey = apiKey || cfg.apiKey;
3254
+ if (cfg && typeof cfg.apiUrl === "string") apiUrl = cfg.apiUrl;
3255
+ } catch {
3256
+ }
3257
+ if (!apiKey) return { ok: false, message: "no login on this device \u2014 add an account first (Accounts \u2192 + add)" };
3258
+ mkdirSync4(p.hooksDir, { recursive: true });
3259
+ mkdirSync4(p.claudeDir, { recursive: true });
3260
+ unlockProtected();
3261
+ writeFileSync5(join6(p.hooksDir, "guard.mjs"), readGuard());
3262
+ writeFileSync5(join6(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
3263
+ writeFileSync5(join6(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
3264
+ writeFileSync5(join6(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
3265
+ writeFileSync5(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
3266
+ let existing = {};
3267
+ if (existsSync2(p.settingsPath)) {
3268
+ const raw = readFileSync4(p.settingsPath, "utf-8");
3269
+ if (!existsSync2(p.backupPath)) writeFileSync5(p.backupPath, raw);
3270
+ try {
3271
+ existing = JSON.parse(raw);
3272
+ } catch {
3273
+ existing = {};
3274
+ }
3275
+ }
3276
+ const nodeBin = process.execPath.replace(/\\/g, "/");
3277
+ const call = process.platform === "win32" ? "& " : "";
3278
+ const hookCmd = (script) => `${call}"${nodeBin}" "${join6(p.hooksDir, script).replace(/\\/g, "/")}" claude-code "Claude Code"`;
3279
+ const merged = {
3280
+ ...existing,
3281
+ hooks: {
3282
+ PreToolUse: [{ matcher: "", hooks: [{ type: "command", command: hookCmd("guard.mjs") }] }],
3283
+ PostToolUse: [{ matcher: "", hooks: [{ type: "command", command: hookCmd("audit.mjs") }] }],
3284
+ Stop: [{ matcher: "", hooks: [{ type: "command", command: hookCmd("stop.mjs") }] }]
3285
+ }
3286
+ };
3287
+ writeFileSync5(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
3288
+ if (process.env["SOLONGATE_OS_LOCK"] === "1") lockProtected();
3289
+ return { ok: true, message: "guard installed \u2014 active in NEW Claude Code sessions (open a new one)" };
3290
+ } catch (e) {
3291
+ return { ok: false, message: e instanceof Error ? e.message : String(e) };
3216
3292
  }
3217
3293
  }
3218
3294
  function isGuardInstalled() {
@@ -3416,6 +3492,19 @@ function SettingsPanel({
3416
3492
  const local = localQ.data;
3417
3493
  const guard = guardQ.data;
3418
3494
  const selfProt = selfQ.data;
3495
+ const autoInstalledRef = useRef3(false);
3496
+ useEffect6(() => {
3497
+ if (autoInstalledRef.current) return;
3498
+ if (guardHere && guard && guard.installed != null && !guard.up_to_date) {
3499
+ autoInstalledRef.current = true;
3500
+ const res = installGlobalQuiet();
3501
+ if (res.ok) {
3502
+ setGuardHere(isGuardInstalled());
3503
+ setMsg({ text: `\u2713 guard auto-updated \u2192 v${guard.latest} hooks written \xB7 open a new Claude Code session to apply`, level: "ok" });
3504
+ guardQ.reload();
3505
+ }
3506
+ }
3507
+ }, [guardHere, guard]);
3419
3508
  const [hidden, setHidden] = useState7(/* @__PURE__ */ new Set());
3420
3509
  const webhooks = (whQ.data?.webhooks ?? []).filter((w) => !hidden.has("wh:" + w.id));
3421
3510
  const alerts = (alertQ.data?.rules ?? []).filter((r) => !hidden.has("alert:" + r.id));
@@ -3541,19 +3630,10 @@ function SettingsPanel({
3541
3630
  } else if (r.kind === "acct-add") {
3542
3631
  beginLogin();
3543
3632
  } else if (r.kind === "guard") {
3544
- if (!guardHere) {
3545
- setMsg({ text: "guard is removed on this device \u2014 log in again (Accounts \u2192 + add) to reinstall it", level: "bad" });
3546
- return;
3547
- }
3548
- if (!guard) return;
3549
- if (guard.up_to_date) {
3550
- setMsg({ text: `guard already on the latest hook (v${guard.latest})`, level: "ok" });
3551
- return;
3552
- }
3553
- const armed = clearGuardUpdateCheck();
3554
- setMsg(
3555
- armed ? { text: `\u2713 update armed. now make your agent RUN any command (e.g. "echo ok"), not just chat; the guard installs v${guard.latest} on the next executed command`, level: "ok" } : { text: "\u2717 could not arm the update (check ~/.solongate permissions)", level: "bad" }
3556
- );
3633
+ const res = installGlobalQuiet();
3634
+ setMsg({ text: (res.ok ? "\u2713 " : "\u2717 ") + res.message, level: res.ok ? "ok" : "bad" });
3635
+ setGuardHere(isGuardInstalled());
3636
+ guardQ.reload();
3557
3637
  } else if (r.kind === "self") {
3558
3638
  if (!selfProt) return;
3559
3639
  run(selfProt.enabled ? "self-protection disabled" : "self-protection enabled", () => api.settings.setSelfProtection(!selfProt.enabled), selfQ.reload);
@@ -3825,12 +3905,13 @@ function SettingsPanel({
3825
3905
  /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "guard".padEnd(11) }),
3826
3906
  !guardHere ? /* @__PURE__ */ jsxs7(Fragment4, { children: [
3827
3907
  /* @__PURE__ */ jsx7(Text7, { color: theme.bad, bold: true, children: "removed" }),
3828
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2014 not installed on this device \xB7 new Claude Code sessions are unguarded" })
3908
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2014 new Claude Code sessions are unguarded \xB7 " }),
3909
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accentBright, children: "enter to install" })
3829
3910
  ] }) : guard ? /* @__PURE__ */ jsxs7(Fragment4, { children: [
3830
3911
  /* @__PURE__ */ jsx7(Text7, { color: guard.up_to_date ? theme.ok : theme.warn, children: `v${guard.installed ?? "?"}` }),
3831
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: guard.up_to_date ? " (latest)" : ` \u2192 v${guard.latest} available \xB7 enter update` }),
3912
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: guard.up_to_date ? " (latest) \xB7 enter reinstall" : ` \u2192 v${guard.latest} available \xB7 enter update` }),
3832
3913
  /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: ` \xB7 ${guard.device_count} device${guard.device_count === 1 ? "" : "s"}${guard.outdated_count ? ` \xB7 ${guard.outdated_count} outdated` : ""} \xB7 d remove` })
3833
- ] }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "loading\u2026" })
3914
+ ] }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "installed \xB7 enter reinstall/update \xB7 d remove" })
3834
3915
  ] });
3835
3916
  case "self":
3836
3917
  return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
@@ -3894,7 +3975,7 @@ function SettingsPanel({
3894
3975
  const sectionOf = (r) => r.kind === "acct" || r.kind === "acct-add" ? "ACCOUNTS" : r.kind === "guard" || r.kind === "self" ? "PROTECTION" : r.kind === "ll-enabled" || r.kind === "ll-path" || r.kind === "ll-server" ? "LOCAL LOGS" : r.kind === "wh" || r.kind === "wh-add" ? "WEBHOOKS" : "ALERTS";
3895
3976
  const SECTION_DESC = {
3896
3977
  ACCOUNTS: `on this device (${accounts.length}) \xB7 \u25CF viewing \xB7 ACTIVE = guard key \xB7 x removes`,
3897
- PROTECTION: "guard hook: enter update \xB7 d remove \xB7 + self-protection toggle",
3978
+ PROTECTION: "guard hook: enter install/update (writes the files) \xB7 d remove \xB7 + self-protection",
3898
3979
  "LOCAL LOGS": "mirror every decision to a file + dashboard link",
3899
3980
  WEBHOOKS: `POST events to a URL (${webhooks.length}) \xB7 t tests`,
3900
3981
  ALERTS: `one email + one telegram (${alerts.length}) \xB7 enter edits \xB7 m on/off`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.81.80",
3
+ "version": "0.81.81",
4
4
  "description": "AI tool security proxy: protect any AI tool server with customizable policies, path/command constraints, rate limiting, and audit logging. No code changes required.",
5
5
  "type": "module",
6
6
  "bin": {