@solongate/proxy 0.83.1 → 0.83.3
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 +159 -22
- package/dist/self-update.d.ts +42 -8
- package/dist/tui/index.js +108 -12
- package/hooks/guard.bundled.mjs +42 -3
- package/hooks/guard.mjs +46 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6923,12 +6923,18 @@ var init_global_install = __esm({
|
|
|
6923
6923
|
// src/self-update.ts
|
|
6924
6924
|
var self_update_exports = {};
|
|
6925
6925
|
__export(self_update_exports, {
|
|
6926
|
+
adminInstallCommand: () => adminInstallCommand,
|
|
6927
|
+
autoUpdateEnabled: () => autoUpdateEnabled,
|
|
6928
|
+
autoUpdateForcedByEnv: () => autoUpdateForcedByEnv,
|
|
6926
6929
|
currentVersion: () => currentVersion,
|
|
6927
6930
|
latestVersion: () => latestVersion,
|
|
6928
6931
|
maybeSelfUpdate: () => maybeSelfUpdate,
|
|
6929
6932
|
newerThan: () => newerThan,
|
|
6933
|
+
runAutoUpdateCommand: () => runAutoUpdateCommand,
|
|
6930
6934
|
runUpdateCommand: () => runUpdateCommand,
|
|
6931
|
-
|
|
6935
|
+
setAutoUpdate: () => setAutoUpdate,
|
|
6936
|
+
tuiUpdateFlow: () => tuiUpdateFlow,
|
|
6937
|
+
updateNow: () => updateNow
|
|
6932
6938
|
});
|
|
6933
6939
|
import { execFile, execFileSync as execFileSync3, spawn } from "child_process";
|
|
6934
6940
|
import { mkdirSync as mkdirSync4, openSync, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
@@ -6950,6 +6956,20 @@ function writeState(s) {
|
|
|
6950
6956
|
} catch {
|
|
6951
6957
|
}
|
|
6952
6958
|
}
|
|
6959
|
+
function autoUpdateEnabled() {
|
|
6960
|
+
const env = (process.env.SOLONGATE_AUTO_UPDATE ?? "").trim().toLowerCase();
|
|
6961
|
+
if (env === "1" || env === "true" || env === "on" || env === "yes") return true;
|
|
6962
|
+
if (env === "0" || env === "false" || env === "off" || env === "no") return false;
|
|
6963
|
+
return readState().auto === true;
|
|
6964
|
+
}
|
|
6965
|
+
function autoUpdateForcedByEnv() {
|
|
6966
|
+
const env = (process.env.SOLONGATE_AUTO_UPDATE ?? "").trim().toLowerCase();
|
|
6967
|
+
return ["1", "true", "on", "yes", "0", "false", "off", "no"].includes(env);
|
|
6968
|
+
}
|
|
6969
|
+
function setAutoUpdate(on) {
|
|
6970
|
+
const s = readState();
|
|
6971
|
+
writeState(on ? { ...s, auto: true, needsAdmin: void 0 } : { ...s, auto: false });
|
|
6972
|
+
}
|
|
6953
6973
|
function currentVersion() {
|
|
6954
6974
|
try {
|
|
6955
6975
|
const pkg = JSON.parse(readFileSync5(join5(dirname2(fileURLToPath2(import.meta.url)), "..", "package.json"), "utf-8"));
|
|
@@ -7015,11 +7035,18 @@ async function runUpdateCommand() {
|
|
|
7015
7035
|
}
|
|
7016
7036
|
if (newerThan(latest, cur)) {
|
|
7017
7037
|
out2(` updating ${cur} -> ${latest} ...`);
|
|
7018
|
-
const
|
|
7019
|
-
if (!ok) {
|
|
7020
|
-
|
|
7038
|
+
const r = await runGlobalInstall2(latest);
|
|
7039
|
+
if (!r.ok) {
|
|
7040
|
+
if (r.needsAdmin) {
|
|
7041
|
+
writeState({ ...readState(), needsAdmin: latest });
|
|
7042
|
+
out2(` npm could not write to the global folder (it needs admin rights).`);
|
|
7043
|
+
out2(` Run: ${adminInstallCommand()}`);
|
|
7044
|
+
} else {
|
|
7045
|
+
out2(" update failed. Try: npm i -g @solongate/proxy@latest");
|
|
7046
|
+
}
|
|
7021
7047
|
return 1;
|
|
7022
7048
|
}
|
|
7049
|
+
writeState({ ...readState(), needsAdmin: void 0 });
|
|
7023
7050
|
out2(` \u2713 updated to ${latest} (open a new session to use it)`);
|
|
7024
7051
|
} else {
|
|
7025
7052
|
out2(` \u2713 already up to date`);
|
|
@@ -7049,21 +7076,61 @@ function runGlobalInstall2(version) {
|
|
|
7049
7076
|
["install", "-g", `${PKG}@${version}`],
|
|
7050
7077
|
{ timeout: 3e5, windowsHide: true, shell: process.platform === "win32" },
|
|
7051
7078
|
(err2, stdout, stderr) => {
|
|
7079
|
+
const output = `${stdout}
|
|
7080
|
+
${stderr}`;
|
|
7052
7081
|
try {
|
|
7053
7082
|
writeFileSync4(LOG_FILE, `${(/* @__PURE__ */ new Date()).toISOString()} install ${version}: ${err2 ? "FAILED" : "ok"}
|
|
7054
|
-
${
|
|
7055
|
-
${stderr}
|
|
7083
|
+
${output}
|
|
7056
7084
|
`, { flag: "a" });
|
|
7057
7085
|
} catch {
|
|
7058
7086
|
}
|
|
7059
|
-
resolve6(!err2);
|
|
7087
|
+
resolve6({ ok: !err2, needsAdmin: !!err2 && NEEDS_ADMIN_RE.test(output) });
|
|
7060
7088
|
}
|
|
7061
7089
|
);
|
|
7062
7090
|
} catch {
|
|
7063
|
-
resolve6(false);
|
|
7091
|
+
resolve6({ ok: false, needsAdmin: false });
|
|
7064
7092
|
}
|
|
7065
7093
|
});
|
|
7066
7094
|
}
|
|
7095
|
+
function adminInstallCommand() {
|
|
7096
|
+
return process.platform === "win32" ? `npm i -g ${PKG}@latest (in an Administrator terminal)` : `sudo npm i -g ${PKG}@latest`;
|
|
7097
|
+
}
|
|
7098
|
+
async function updateNow() {
|
|
7099
|
+
const cur = currentVersion();
|
|
7100
|
+
const latest = await fetchLatest();
|
|
7101
|
+
if (!latest) return { status: "unreachable", version: cur };
|
|
7102
|
+
if (!newerThan(latest, cur)) return { status: "current", version: cur };
|
|
7103
|
+
const r = await runGlobalInstall2(latest);
|
|
7104
|
+
if (r.ok) {
|
|
7105
|
+
writeState({ ...readState(), installed: latest, needsAdmin: void 0 });
|
|
7106
|
+
return { status: "updated", version: latest };
|
|
7107
|
+
}
|
|
7108
|
+
if (r.needsAdmin) {
|
|
7109
|
+
writeState({ ...readState(), needsAdmin: latest });
|
|
7110
|
+
return { status: "needs-admin", version: latest };
|
|
7111
|
+
}
|
|
7112
|
+
return { status: "failed", version: latest };
|
|
7113
|
+
}
|
|
7114
|
+
function runAutoUpdateCommand(arg) {
|
|
7115
|
+
const out2 = (s) => void process.stderr.write(s + "\n");
|
|
7116
|
+
const envForced = autoUpdateForcedByEnv();
|
|
7117
|
+
if (arg === void 0) {
|
|
7118
|
+
out2(` auto-update is ${autoUpdateEnabled() ? "on" : "off"}${envForced ? " (forced by SOLONGATE_AUTO_UPDATE)" : ""}`);
|
|
7119
|
+
out2(" change it with: solongate update auto on|off");
|
|
7120
|
+
return 0;
|
|
7121
|
+
}
|
|
7122
|
+
const v = arg.trim().toLowerCase();
|
|
7123
|
+
const on = ["on", "1", "true", "yes", "enable", "enabled"].includes(v);
|
|
7124
|
+
const off = ["off", "0", "false", "no", "disable", "disabled"].includes(v);
|
|
7125
|
+
if (!on && !off) {
|
|
7126
|
+
out2(` unknown value "${arg}" \u2014 use: solongate update auto on|off`);
|
|
7127
|
+
return 1;
|
|
7128
|
+
}
|
|
7129
|
+
setAutoUpdate(on);
|
|
7130
|
+
out2(on ? " \u2713 auto-update on \u2014 new versions install in the background" : " \u2713 auto-update off \u2014 update with: solongate update");
|
|
7131
|
+
if (envForced) out2(` note: SOLONGATE_AUTO_UPDATE is set and overrides this while it stays set`);
|
|
7132
|
+
return 0;
|
|
7133
|
+
}
|
|
7067
7134
|
async function tuiUpdateFlow(onStatus) {
|
|
7068
7135
|
try {
|
|
7069
7136
|
const current = currentVersion();
|
|
@@ -7075,10 +7142,22 @@ async function tuiUpdateFlow(onStatus) {
|
|
|
7075
7142
|
onStatus({ kind: "updated", version: latest });
|
|
7076
7143
|
return;
|
|
7077
7144
|
}
|
|
7145
|
+
if (!autoUpdateEnabled()) {
|
|
7146
|
+
onStatus({ kind: "available", version: latest });
|
|
7147
|
+
return;
|
|
7148
|
+
}
|
|
7149
|
+
if (readState().needsAdmin === latest) {
|
|
7150
|
+
onStatus({ kind: "needs-admin", version: latest });
|
|
7151
|
+
return;
|
|
7152
|
+
}
|
|
7078
7153
|
onStatus({ kind: "updating", version: latest });
|
|
7079
|
-
|
|
7080
|
-
|
|
7154
|
+
const r = await runGlobalInstall2(latest);
|
|
7155
|
+
if (r.ok) {
|
|
7156
|
+
writeState({ ...readState(), installed: latest, needsAdmin: void 0 });
|
|
7081
7157
|
onStatus({ kind: "updated", version: latest });
|
|
7158
|
+
} else if (r.needsAdmin) {
|
|
7159
|
+
writeState({ ...readState(), needsAdmin: latest });
|
|
7160
|
+
onStatus({ kind: "needs-admin", version: latest });
|
|
7082
7161
|
}
|
|
7083
7162
|
} catch {
|
|
7084
7163
|
}
|
|
@@ -7097,17 +7176,20 @@ function maybeSelfUpdate(notify = (l) => process.stderr.write(l + "\n")) {
|
|
|
7097
7176
|
}
|
|
7098
7177
|
if (!latest || !newerThan(latest, current)) return;
|
|
7099
7178
|
const attempts = readState().attempts ?? {};
|
|
7100
|
-
if (now - (attempts[latest] ?? 0)
|
|
7101
|
-
|
|
7102
|
-
|
|
7103
|
-
|
|
7104
|
-
|
|
7179
|
+
if (now - (attempts[latest] ?? 0) < ATTEMPT_EVERY_MS) return;
|
|
7180
|
+
writeState({ ...readState(), attempts: { [latest]: now } });
|
|
7181
|
+
if (!autoUpdateEnabled()) {
|
|
7182
|
+
notify(`${c.dim}\u2191 solongate v${latest} available (running v${current}) \u2014 run ${c.reset}${c.cyan}solongate update${c.reset}`);
|
|
7183
|
+
return;
|
|
7184
|
+
}
|
|
7185
|
+
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}`);
|
|
7105
7187
|
}
|
|
7106
7188
|
} catch {
|
|
7107
7189
|
}
|
|
7108
7190
|
})();
|
|
7109
7191
|
}
|
|
7110
|
-
var PKG, CHECK_EVERY_MS, ATTEMPT_EVERY_MS, STATE_FILE, LOG_FILE;
|
|
7192
|
+
var PKG, CHECK_EVERY_MS, ATTEMPT_EVERY_MS, STATE_FILE, LOG_FILE, NEEDS_ADMIN_RE;
|
|
7111
7193
|
var init_self_update = __esm({
|
|
7112
7194
|
"src/self-update.ts"() {
|
|
7113
7195
|
"use strict";
|
|
@@ -7117,6 +7199,7 @@ var init_self_update = __esm({
|
|
|
7117
7199
|
ATTEMPT_EVERY_MS = 6 * 60 * 60 * 1e3;
|
|
7118
7200
|
STATE_FILE = join5(homedir3(), ".solongate", ".self-update.json");
|
|
7119
7201
|
LOG_FILE = join5(homedir3(), ".solongate", "self-update.log");
|
|
7202
|
+
NEEDS_ADMIN_RE = /\bEACCES\b|\bEPERM\b|permission denied|operation not permitted/i;
|
|
7120
7203
|
}
|
|
7121
7204
|
});
|
|
7122
7205
|
|
|
@@ -11458,6 +11541,8 @@ function SettingsPanel({
|
|
|
11458
11541
|
const [editor, setEditor] = useState8(null);
|
|
11459
11542
|
const [latest, setLatest] = useState8(null);
|
|
11460
11543
|
const [diagBusy, setDiagBusy] = useState8(null);
|
|
11544
|
+
const [autoUp, setAutoUp] = useState8(() => autoUpdateEnabled());
|
|
11545
|
+
const [updBusy, setUpdBusy] = useState8(false);
|
|
11461
11546
|
const [diag, setDiag] = useState8(null);
|
|
11462
11547
|
const [diagStep, setDiagStep] = useState8(0);
|
|
11463
11548
|
const DIAG_STEPS = {
|
|
@@ -11515,10 +11600,10 @@ function SettingsPanel({
|
|
|
11515
11600
|
}, []);
|
|
11516
11601
|
useEffect7(() => {
|
|
11517
11602
|
const loggingIn = login && login.phase !== "done" && login.phase !== "error";
|
|
11518
|
-
if (!loggingIn && !diagBusy) return;
|
|
11603
|
+
if (!loggingIn && !diagBusy && !updBusy) return;
|
|
11519
11604
|
const t = setInterval(() => setTick((n) => n + 1), 120);
|
|
11520
11605
|
return () => clearInterval(t);
|
|
11521
|
-
}, [login, diagBusy]);
|
|
11606
|
+
}, [login, diagBusy, updBusy]);
|
|
11522
11607
|
const localQ = useLoader(() => listAccounts().length ? api.settings.getLocalLogs() : Promise.resolve(null));
|
|
11523
11608
|
const whQ = useLoader(() => listAccounts().length ? api.settings.getWebhooks() : Promise.resolve(null));
|
|
11524
11609
|
const alertQ = useLoader(() => listAccounts().length ? api.settings.getAlerts() : Promise.resolve(null));
|
|
@@ -11552,6 +11637,8 @@ function SettingsPanel({
|
|
|
11552
11637
|
{ kind: "self" },
|
|
11553
11638
|
{ kind: "doctor" },
|
|
11554
11639
|
{ kind: "repair" },
|
|
11640
|
+
{ kind: "cli-update" },
|
|
11641
|
+
{ kind: "auto-update" },
|
|
11555
11642
|
{ kind: "ll-enabled" },
|
|
11556
11643
|
{ kind: "ll-path" },
|
|
11557
11644
|
{ kind: "ll-server" },
|
|
@@ -11731,6 +11818,34 @@ function SettingsPanel({
|
|
|
11731
11818
|
} else if (r.kind === "self") {
|
|
11732
11819
|
if (!selfProt) return;
|
|
11733
11820
|
run10(selfProt.enabled ? "self-protection disabled" : "self-protection enabled", () => api.settings.setSelfProtection(!selfProt.enabled), selfQ.reload);
|
|
11821
|
+
} else if (r.kind === "cli-update") {
|
|
11822
|
+
if (updBusy) return;
|
|
11823
|
+
setUpdBusy(true);
|
|
11824
|
+
setMsg({ text: "updating\u2026", level: "ok" });
|
|
11825
|
+
void (async () => {
|
|
11826
|
+
try {
|
|
11827
|
+
const res = await updateNow();
|
|
11828
|
+
if (res.status === "updated") setMsg({ text: `\u2713 v${res.version} installed \xB7 restart (q, then solongate) to apply`, level: "ok" });
|
|
11829
|
+
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" });
|
|
11832
|
+
else setMsg({ text: `\u2717 update to v${res.version} failed \u2014 see ~/.solongate/self-update.log`, level: "bad" });
|
|
11833
|
+
setLatest(await latestVersion());
|
|
11834
|
+
} finally {
|
|
11835
|
+
setUpdBusy(false);
|
|
11836
|
+
}
|
|
11837
|
+
})();
|
|
11838
|
+
} else if (r.kind === "auto-update") {
|
|
11839
|
+
if (autoUpdateForcedByEnv()) {
|
|
11840
|
+
setMsg({ text: "SOLONGATE_AUTO_UPDATE is set \u2014 unset it to change this here", level: "bad" });
|
|
11841
|
+
return;
|
|
11842
|
+
}
|
|
11843
|
+
const next = !autoUp;
|
|
11844
|
+
setAutoUpdate(next);
|
|
11845
|
+
setAutoUp(next);
|
|
11846
|
+
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" }
|
|
11848
|
+
);
|
|
11734
11849
|
} else if (r.kind === "ll-enabled") {
|
|
11735
11850
|
if (!local) return;
|
|
11736
11851
|
if (!local.enabled && !local.path.trim()) {
|
|
@@ -11829,7 +11944,7 @@ function SettingsPanel({
|
|
|
11829
11944
|
if (ok) clearLocalLog();
|
|
11830
11945
|
setMsg(ok ? { text: `\u2713 ${acctLabel(cur.acc)} is now the ACTIVE key (guard + logging)`, level: "ok" } : { text: "\u2717 could not set active", level: "bad" });
|
|
11831
11946
|
refreshAccounts();
|
|
11832
|
-
} else if (cur.kind === "wh" || cur.kind === "alert" || cur.kind === "self" || cur.kind === "ll-enabled" || cur.kind === "ll-server") {
|
|
11947
|
+
} else if (cur.kind === "wh" || cur.kind === "alert" || cur.kind === "self" || cur.kind === "auto-update" || cur.kind === "ll-enabled" || cur.kind === "ll-server") {
|
|
11833
11948
|
if (cur.kind === "alert") run10(cur.rule.enabled ? "alert disabled" : "alert enabled", () => api.settings.setAlertEnabled(cur.rule.id, !cur.rule.enabled), alertQ.reload);
|
|
11834
11949
|
else activate(cur);
|
|
11835
11950
|
}
|
|
@@ -12041,6 +12156,22 @@ function SettingsPanel({
|
|
|
12041
12156
|
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "repair".padEnd(11) }),
|
|
12042
12157
|
diagBusy === "repair" ? /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, children: `${spin} restoring protection\u2026` }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "restore every protection file after tampering or deletion \xB7 enter runs it" })
|
|
12043
12158
|
] });
|
|
12159
|
+
case "cli-update": {
|
|
12160
|
+
const behind = latest ? newerThan(latest, ver) : false;
|
|
12161
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
12162
|
+
cursor(r),
|
|
12163
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "version".padEnd(11) }),
|
|
12164
|
+
/* @__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" })
|
|
12166
|
+
] });
|
|
12167
|
+
}
|
|
12168
|
+
case "auto-update":
|
|
12169
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
12170
|
+
cursor(r),
|
|
12171
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "auto".padEnd(11) }),
|
|
12172
|
+
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" })
|
|
12174
|
+
] });
|
|
12044
12175
|
case "ll-enabled":
|
|
12045
12176
|
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
12046
12177
|
cursor(r),
|
|
@@ -12093,10 +12224,11 @@ function SettingsPanel({
|
|
|
12093
12224
|
] });
|
|
12094
12225
|
}
|
|
12095
12226
|
};
|
|
12096
|
-
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 === "ll-enabled" || r.kind === "ll-path" || r.kind === "ll-server" ? "LOCAL LOGS" : r.kind === "wh" || r.kind === "wh-add" ? "WEBHOOKS" : "ALERTS";
|
|
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";
|
|
12097
12228
|
const SECTION_DESC = {
|
|
12098
12229
|
ACCOUNTS: `on this device (${accounts.length}) \xB7 \u25CF viewing \xB7 ACTIVE = guard key \xB7 x removes`,
|
|
12099
12230
|
PROTECTION: "guard hook: enter install/update \xB7 d remove \xB7 self-protection \xB7 doctor + repair",
|
|
12231
|
+
UPDATES: "the solongate CLI itself \xB7 background auto-update is off unless you turn it on",
|
|
12100
12232
|
"LOCAL LOGS": "mirror every decision to a file + dashboard link",
|
|
12101
12233
|
WEBHOOKS: `POST events to a URL (${webhooks.length}) \xB7 t tests`,
|
|
12102
12234
|
ALERTS: `one email + one telegram (${alerts.length}) \xB7 enter edits \xB7 m on/off`
|
|
@@ -12165,7 +12297,7 @@ function SettingsPanel({
|
|
|
12165
12297
|
lineEls.push(
|
|
12166
12298
|
/* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", color: theme.dim, children: [
|
|
12167
12299
|
`solongate v${ver}`,
|
|
12168
|
-
latest ? newerThan(latest, ver) ? /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: ` \xB7 v${latest} on npm \u2014 auto-updating` }) : /* @__PURE__ */ jsx8(Text8, { color: theme.ok, children: " \xB7 latest" }) : null
|
|
12300
|
+
latest ? newerThan(latest, ver) ? /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: ` \xB7 v${latest} on npm \u2014 ${autoUp ? "auto-updating" : "UPDATES above"}` }) : /* @__PURE__ */ jsx8(Text8, { color: theme.ok, children: " \xB7 latest" }) : null
|
|
12169
12301
|
] }, "ver")
|
|
12170
12302
|
);
|
|
12171
12303
|
lineKey.push("");
|
|
@@ -12394,7 +12526,7 @@ function App() {
|
|
|
12394
12526
|
// a row off whichever panel is open.
|
|
12395
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" })
|
|
12396
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" }),
|
|
12397
|
-
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` }) : null
|
|
12529
|
+
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
|
|
12398
12530
|
] }),
|
|
12399
12531
|
/* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexGrow: 1, children: [
|
|
12400
12532
|
/* @__PURE__ */ jsx9(Box9, { flexDirection: "column", flexShrink: 0, width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => {
|
|
@@ -17026,6 +17158,7 @@ function printHelp() {
|
|
|
17026
17158
|
head("Setup & status");
|
|
17027
17159
|
cmd("solongate", "open the dataroom UI (login, policies, audit, settings)");
|
|
17028
17160
|
cmd("update", "update SolonGate to the newest version and refresh the guard");
|
|
17161
|
+
cmd("update auto on|off", "background auto-update (default off \u2014 on macOS npm -g often needs sudo)");
|
|
17029
17162
|
cmd("repair", "restore the guard + hook + settings files if they were deleted or disarmed");
|
|
17030
17163
|
cmd("doctor", "health check: login, policy, guard, local logs");
|
|
17031
17164
|
cmd("doctor --json", "the same health check as machine-readable JSON");
|
|
@@ -17173,6 +17306,10 @@ async function main() {
|
|
|
17173
17306
|
return;
|
|
17174
17307
|
}
|
|
17175
17308
|
if (subcommand === "update") {
|
|
17309
|
+
if (process.argv[3] === "auto") {
|
|
17310
|
+
const { runAutoUpdateCommand: runAutoUpdateCommand2 } = await Promise.resolve().then(() => (init_self_update(), self_update_exports));
|
|
17311
|
+
process.exit(runAutoUpdateCommand2(process.argv[4]));
|
|
17312
|
+
}
|
|
17176
17313
|
const { runUpdateCommand: runUpdateCommand2 } = await Promise.resolve().then(() => (init_self_update(), self_update_exports));
|
|
17177
17314
|
process.exit(await runUpdateCommand2());
|
|
17178
17315
|
}
|
package/dist/self-update.d.ts
CHANGED
|
@@ -1,30 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is the background updater turned on? Default: NO — see the file header.
|
|
3
|
+
* SOLONGATE_AUTO_UPDATE=on|off (1/0, true/false) overrides the stored setting
|
|
4
|
+
* for one run, so a CI image or a managed fleet can force either way without
|
|
5
|
+
* writing to the user's home directory.
|
|
6
|
+
*/
|
|
7
|
+
export declare function autoUpdateEnabled(): boolean;
|
|
8
|
+
/** True when the env var is deciding, so the UI can say the row is overridden. */
|
|
9
|
+
export declare function autoUpdateForcedByEnv(): boolean;
|
|
10
|
+
/** Turn the background updater on/off. Clears the "npm refused" memo on enable. */
|
|
11
|
+
export declare function setAutoUpdate(on: boolean): void;
|
|
1
12
|
export declare function currentVersion(): string;
|
|
2
13
|
/** true when `b` is a strictly newer semver than `a` (numeric triples only). */
|
|
3
14
|
export declare function newerThan(b: string, a: string): boolean;
|
|
4
15
|
/** The latest version published to npm, or null if it can't be reached. */
|
|
5
16
|
export declare function latestVersion(): Promise<string | null>;
|
|
6
|
-
/** Foreground global install — resolves true on success, output → update log. */
|
|
7
17
|
/**
|
|
8
|
-
* `solongate update` —
|
|
9
|
-
* background
|
|
10
|
-
* installed guard hooks. Returns a process exit
|
|
18
|
+
* `solongate update` — the normal way to update, and the ONLY way when the
|
|
19
|
+
* background updater is off (which is the default): installs the newest CLI
|
|
20
|
+
* globally, then refreshes the installed guard hooks. Returns a process exit
|
|
21
|
+
* code.
|
|
11
22
|
*/
|
|
12
23
|
export declare function runUpdateCommand(): Promise<number>;
|
|
24
|
+
/** The exact command that fixes a root-owned global prefix. */
|
|
25
|
+
export declare function adminInstallCommand(): string;
|
|
26
|
+
/**
|
|
27
|
+
* Install the newest version NOW, whatever the auto-update setting says — the
|
|
28
|
+
* dataroom's UPDATES row and `solongate update` both run through here.
|
|
29
|
+
*/
|
|
30
|
+
export declare function updateNow(): Promise<{
|
|
31
|
+
status: 'updated' | 'current' | 'needs-admin' | 'unreachable' | 'failed';
|
|
32
|
+
version: string;
|
|
33
|
+
}>;
|
|
34
|
+
/**
|
|
35
|
+
* `solongate update auto [on|off]` — show or change the background updater.
|
|
36
|
+
* Kept separate from `runUpdateCommand` so `solongate update` stays "update me
|
|
37
|
+
* now" and never changes a setting as a side effect.
|
|
38
|
+
*/
|
|
39
|
+
export declare function runAutoUpdateCommand(arg?: string): number;
|
|
13
40
|
/** What the dataroom shows about the updater. */
|
|
14
41
|
export type UpdateStatus = {
|
|
15
42
|
kind: 'idle';
|
|
43
|
+
} | {
|
|
44
|
+
kind: 'available';
|
|
45
|
+
version: string;
|
|
16
46
|
} | {
|
|
17
47
|
kind: 'updating';
|
|
18
48
|
version: string;
|
|
19
49
|
} | {
|
|
20
50
|
kind: 'updated';
|
|
21
51
|
version: string;
|
|
52
|
+
} | {
|
|
53
|
+
kind: 'needs-admin';
|
|
54
|
+
version: string;
|
|
22
55
|
};
|
|
23
56
|
/**
|
|
24
|
-
* The dataroom's updater
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
57
|
+
* The dataroom's updater: check the registry EVERY time the TUI opens (and
|
|
58
|
+
* periodically while it stays open). With auto-update ON, install in the
|
|
59
|
+
* background and ask for a restart; with it OFF (the default), only report that
|
|
60
|
+
* a version is available. `onStatus` drives the in-app status line; nothing is
|
|
61
|
+
* ever written to the terminal directly.
|
|
28
62
|
*/
|
|
29
63
|
export declare function tuiUpdateFlow(onStatus: (s: UpdateStatus) => void): Promise<void>;
|
|
30
64
|
/**
|
package/dist/tui/index.js
CHANGED
|
@@ -4504,6 +4504,7 @@ var CHECK_EVERY_MS = 30 * 60 * 1e3;
|
|
|
4504
4504
|
var ATTEMPT_EVERY_MS = 6 * 60 * 60 * 1e3;
|
|
4505
4505
|
var STATE_FILE2 = join9(homedir9(), ".solongate", ".self-update.json");
|
|
4506
4506
|
var LOG_FILE2 = join9(homedir9(), ".solongate", "self-update.log");
|
|
4507
|
+
var NEEDS_ADMIN_RE = /\bEACCES\b|\bEPERM\b|permission denied|operation not permitted/i;
|
|
4507
4508
|
function readState2() {
|
|
4508
4509
|
try {
|
|
4509
4510
|
const s = JSON.parse(readFileSync7(STATE_FILE2, "utf-8"));
|
|
@@ -4519,6 +4520,20 @@ function writeState2(s) {
|
|
|
4519
4520
|
} catch {
|
|
4520
4521
|
}
|
|
4521
4522
|
}
|
|
4523
|
+
function autoUpdateEnabled() {
|
|
4524
|
+
const env = (process.env.SOLONGATE_AUTO_UPDATE ?? "").trim().toLowerCase();
|
|
4525
|
+
if (env === "1" || env === "true" || env === "on" || env === "yes") return true;
|
|
4526
|
+
if (env === "0" || env === "false" || env === "off" || env === "no") return false;
|
|
4527
|
+
return readState2().auto === true;
|
|
4528
|
+
}
|
|
4529
|
+
function autoUpdateForcedByEnv() {
|
|
4530
|
+
const env = (process.env.SOLONGATE_AUTO_UPDATE ?? "").trim().toLowerCase();
|
|
4531
|
+
return ["1", "true", "on", "yes", "0", "false", "off", "no"].includes(env);
|
|
4532
|
+
}
|
|
4533
|
+
function setAutoUpdate(on) {
|
|
4534
|
+
const s = readState2();
|
|
4535
|
+
writeState2(on ? { ...s, auto: true, needsAdmin: void 0 } : { ...s, auto: false });
|
|
4536
|
+
}
|
|
4522
4537
|
function currentVersion() {
|
|
4523
4538
|
try {
|
|
4524
4539
|
const pkg = JSON.parse(readFileSync7(join9(dirname3(fileURLToPath3(import.meta.url)), "..", "package.json"), "utf-8"));
|
|
@@ -4564,21 +4579,41 @@ function runGlobalInstall(version) {
|
|
|
4564
4579
|
["install", "-g", `${PKG}@${version}`],
|
|
4565
4580
|
{ timeout: 3e5, windowsHide: true, shell: process.platform === "win32" },
|
|
4566
4581
|
(err2, stdout, stderr) => {
|
|
4582
|
+
const output = `${stdout}
|
|
4583
|
+
${stderr}`;
|
|
4567
4584
|
try {
|
|
4568
4585
|
writeFileSync7(LOG_FILE2, `${(/* @__PURE__ */ new Date()).toISOString()} install ${version}: ${err2 ? "FAILED" : "ok"}
|
|
4569
|
-
${
|
|
4570
|
-
${stderr}
|
|
4586
|
+
${output}
|
|
4571
4587
|
`, { flag: "a" });
|
|
4572
4588
|
} catch {
|
|
4573
4589
|
}
|
|
4574
|
-
resolve3(!err2);
|
|
4590
|
+
resolve3({ ok: !err2, needsAdmin: !!err2 && NEEDS_ADMIN_RE.test(output) });
|
|
4575
4591
|
}
|
|
4576
4592
|
);
|
|
4577
4593
|
} catch {
|
|
4578
|
-
resolve3(false);
|
|
4594
|
+
resolve3({ ok: false, needsAdmin: false });
|
|
4579
4595
|
}
|
|
4580
4596
|
});
|
|
4581
4597
|
}
|
|
4598
|
+
function adminInstallCommand() {
|
|
4599
|
+
return process.platform === "win32" ? `npm i -g ${PKG}@latest (in an Administrator terminal)` : `sudo npm i -g ${PKG}@latest`;
|
|
4600
|
+
}
|
|
4601
|
+
async function updateNow() {
|
|
4602
|
+
const cur = currentVersion();
|
|
4603
|
+
const latest = await fetchLatest();
|
|
4604
|
+
if (!latest) return { status: "unreachable", version: cur };
|
|
4605
|
+
if (!newerThan(latest, cur)) return { status: "current", version: cur };
|
|
4606
|
+
const r = await runGlobalInstall(latest);
|
|
4607
|
+
if (r.ok) {
|
|
4608
|
+
writeState2({ ...readState2(), installed: latest, needsAdmin: void 0 });
|
|
4609
|
+
return { status: "updated", version: latest };
|
|
4610
|
+
}
|
|
4611
|
+
if (r.needsAdmin) {
|
|
4612
|
+
writeState2({ ...readState2(), needsAdmin: latest });
|
|
4613
|
+
return { status: "needs-admin", version: latest };
|
|
4614
|
+
}
|
|
4615
|
+
return { status: "failed", version: latest };
|
|
4616
|
+
}
|
|
4582
4617
|
async function tuiUpdateFlow(onStatus) {
|
|
4583
4618
|
try {
|
|
4584
4619
|
const current = currentVersion();
|
|
@@ -4590,10 +4625,22 @@ async function tuiUpdateFlow(onStatus) {
|
|
|
4590
4625
|
onStatus({ kind: "updated", version: latest });
|
|
4591
4626
|
return;
|
|
4592
4627
|
}
|
|
4628
|
+
if (!autoUpdateEnabled()) {
|
|
4629
|
+
onStatus({ kind: "available", version: latest });
|
|
4630
|
+
return;
|
|
4631
|
+
}
|
|
4632
|
+
if (readState2().needsAdmin === latest) {
|
|
4633
|
+
onStatus({ kind: "needs-admin", version: latest });
|
|
4634
|
+
return;
|
|
4635
|
+
}
|
|
4593
4636
|
onStatus({ kind: "updating", version: latest });
|
|
4594
|
-
|
|
4595
|
-
|
|
4637
|
+
const r = await runGlobalInstall(latest);
|
|
4638
|
+
if (r.ok) {
|
|
4639
|
+
writeState2({ ...readState2(), installed: latest, needsAdmin: void 0 });
|
|
4596
4640
|
onStatus({ kind: "updated", version: latest });
|
|
4641
|
+
} else if (r.needsAdmin) {
|
|
4642
|
+
writeState2({ ...readState2(), needsAdmin: latest });
|
|
4643
|
+
onStatus({ kind: "needs-admin", version: latest });
|
|
4597
4644
|
}
|
|
4598
4645
|
} catch {
|
|
4599
4646
|
}
|
|
@@ -4654,6 +4701,8 @@ function SettingsPanel({
|
|
|
4654
4701
|
const [editor, setEditor] = useState8(null);
|
|
4655
4702
|
const [latest, setLatest] = useState8(null);
|
|
4656
4703
|
const [diagBusy, setDiagBusy] = useState8(null);
|
|
4704
|
+
const [autoUp, setAutoUp] = useState8(() => autoUpdateEnabled());
|
|
4705
|
+
const [updBusy, setUpdBusy] = useState8(false);
|
|
4657
4706
|
const [diag, setDiag] = useState8(null);
|
|
4658
4707
|
const [diagStep, setDiagStep] = useState8(0);
|
|
4659
4708
|
const DIAG_STEPS = {
|
|
@@ -4711,10 +4760,10 @@ function SettingsPanel({
|
|
|
4711
4760
|
}, []);
|
|
4712
4761
|
useEffect7(() => {
|
|
4713
4762
|
const loggingIn = login && login.phase !== "done" && login.phase !== "error";
|
|
4714
|
-
if (!loggingIn && !diagBusy) return;
|
|
4763
|
+
if (!loggingIn && !diagBusy && !updBusy) return;
|
|
4715
4764
|
const t = setInterval(() => setTick((n) => n + 1), 120);
|
|
4716
4765
|
return () => clearInterval(t);
|
|
4717
|
-
}, [login, diagBusy]);
|
|
4766
|
+
}, [login, diagBusy, updBusy]);
|
|
4718
4767
|
const localQ = useLoader(() => listAccounts().length ? api.settings.getLocalLogs() : Promise.resolve(null));
|
|
4719
4768
|
const whQ = useLoader(() => listAccounts().length ? api.settings.getWebhooks() : Promise.resolve(null));
|
|
4720
4769
|
const alertQ = useLoader(() => listAccounts().length ? api.settings.getAlerts() : Promise.resolve(null));
|
|
@@ -4748,6 +4797,8 @@ function SettingsPanel({
|
|
|
4748
4797
|
{ kind: "self" },
|
|
4749
4798
|
{ kind: "doctor" },
|
|
4750
4799
|
{ kind: "repair" },
|
|
4800
|
+
{ kind: "cli-update" },
|
|
4801
|
+
{ kind: "auto-update" },
|
|
4751
4802
|
{ kind: "ll-enabled" },
|
|
4752
4803
|
{ kind: "ll-path" },
|
|
4753
4804
|
{ kind: "ll-server" },
|
|
@@ -4927,6 +4978,34 @@ function SettingsPanel({
|
|
|
4927
4978
|
} else if (r.kind === "self") {
|
|
4928
4979
|
if (!selfProt) return;
|
|
4929
4980
|
run(selfProt.enabled ? "self-protection disabled" : "self-protection enabled", () => api.settings.setSelfProtection(!selfProt.enabled), selfQ.reload);
|
|
4981
|
+
} else if (r.kind === "cli-update") {
|
|
4982
|
+
if (updBusy) return;
|
|
4983
|
+
setUpdBusy(true);
|
|
4984
|
+
setMsg({ text: "updating\u2026", level: "ok" });
|
|
4985
|
+
void (async () => {
|
|
4986
|
+
try {
|
|
4987
|
+
const res = await updateNow();
|
|
4988
|
+
if (res.status === "updated") setMsg({ text: `\u2713 v${res.version} installed \xB7 restart (q, then solongate) to apply`, level: "ok" });
|
|
4989
|
+
else if (res.status === "current") setMsg({ text: `\u2713 already on the latest version (v${res.version})`, level: "ok" });
|
|
4990
|
+
else if (res.status === "needs-admin") setMsg({ text: `\u2717 npm needs admin rights here \u2014 run: ${adminInstallCommand()}`, level: "bad" });
|
|
4991
|
+
else if (res.status === "unreachable") setMsg({ text: "\u2717 could not reach the npm registry", level: "bad" });
|
|
4992
|
+
else setMsg({ text: `\u2717 update to v${res.version} failed \u2014 see ~/.solongate/self-update.log`, level: "bad" });
|
|
4993
|
+
setLatest(await latestVersion());
|
|
4994
|
+
} finally {
|
|
4995
|
+
setUpdBusy(false);
|
|
4996
|
+
}
|
|
4997
|
+
})();
|
|
4998
|
+
} else if (r.kind === "auto-update") {
|
|
4999
|
+
if (autoUpdateForcedByEnv()) {
|
|
5000
|
+
setMsg({ text: "SOLONGATE_AUTO_UPDATE is set \u2014 unset it to change this here", level: "bad" });
|
|
5001
|
+
return;
|
|
5002
|
+
}
|
|
5003
|
+
const next = !autoUp;
|
|
5004
|
+
setAutoUpdate(next);
|
|
5005
|
+
setAutoUp(next);
|
|
5006
|
+
setMsg(
|
|
5007
|
+
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" }
|
|
5008
|
+
);
|
|
4930
5009
|
} else if (r.kind === "ll-enabled") {
|
|
4931
5010
|
if (!local) return;
|
|
4932
5011
|
if (!local.enabled && !local.path.trim()) {
|
|
@@ -5025,7 +5104,7 @@ function SettingsPanel({
|
|
|
5025
5104
|
if (ok) clearLocalLog();
|
|
5026
5105
|
setMsg(ok ? { text: `\u2713 ${acctLabel(cur.acc)} is now the ACTIVE key (guard + logging)`, level: "ok" } : { text: "\u2717 could not set active", level: "bad" });
|
|
5027
5106
|
refreshAccounts();
|
|
5028
|
-
} else if (cur.kind === "wh" || cur.kind === "alert" || cur.kind === "self" || cur.kind === "ll-enabled" || cur.kind === "ll-server") {
|
|
5107
|
+
} else if (cur.kind === "wh" || cur.kind === "alert" || cur.kind === "self" || cur.kind === "auto-update" || cur.kind === "ll-enabled" || cur.kind === "ll-server") {
|
|
5029
5108
|
if (cur.kind === "alert") run(cur.rule.enabled ? "alert disabled" : "alert enabled", () => api.settings.setAlertEnabled(cur.rule.id, !cur.rule.enabled), alertQ.reload);
|
|
5030
5109
|
else activate(cur);
|
|
5031
5110
|
}
|
|
@@ -5237,6 +5316,22 @@ function SettingsPanel({
|
|
|
5237
5316
|
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "repair".padEnd(11) }),
|
|
5238
5317
|
diagBusy === "repair" ? /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, children: `${spin} restoring protection\u2026` }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "restore every protection file after tampering or deletion \xB7 enter runs it" })
|
|
5239
5318
|
] });
|
|
5319
|
+
case "cli-update": {
|
|
5320
|
+
const behind = latest ? newerThan(latest, ver) : false;
|
|
5321
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
5322
|
+
cursor(r),
|
|
5323
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "version".padEnd(11) }),
|
|
5324
|
+
/* @__PURE__ */ jsx8(Text8, { color: behind ? theme.warn : theme.ok, children: `v${ver}` }),
|
|
5325
|
+
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" })
|
|
5326
|
+
] });
|
|
5327
|
+
}
|
|
5328
|
+
case "auto-update":
|
|
5329
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
5330
|
+
cursor(r),
|
|
5331
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "auto".padEnd(11) }),
|
|
5332
|
+
onOff(autoUp),
|
|
5333
|
+
/* @__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" })
|
|
5334
|
+
] });
|
|
5240
5335
|
case "ll-enabled":
|
|
5241
5336
|
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
5242
5337
|
cursor(r),
|
|
@@ -5289,10 +5384,11 @@ function SettingsPanel({
|
|
|
5289
5384
|
] });
|
|
5290
5385
|
}
|
|
5291
5386
|
};
|
|
5292
|
-
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 === "ll-enabled" || r.kind === "ll-path" || r.kind === "ll-server" ? "LOCAL LOGS" : r.kind === "wh" || r.kind === "wh-add" ? "WEBHOOKS" : "ALERTS";
|
|
5387
|
+
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";
|
|
5293
5388
|
const SECTION_DESC = {
|
|
5294
5389
|
ACCOUNTS: `on this device (${accounts.length}) \xB7 \u25CF viewing \xB7 ACTIVE = guard key \xB7 x removes`,
|
|
5295
5390
|
PROTECTION: "guard hook: enter install/update \xB7 d remove \xB7 self-protection \xB7 doctor + repair",
|
|
5391
|
+
UPDATES: "the solongate CLI itself \xB7 background auto-update is off unless you turn it on",
|
|
5296
5392
|
"LOCAL LOGS": "mirror every decision to a file + dashboard link",
|
|
5297
5393
|
WEBHOOKS: `POST events to a URL (${webhooks.length}) \xB7 t tests`,
|
|
5298
5394
|
ALERTS: `one email + one telegram (${alerts.length}) \xB7 enter edits \xB7 m on/off`
|
|
@@ -5361,7 +5457,7 @@ function SettingsPanel({
|
|
|
5361
5457
|
lineEls.push(
|
|
5362
5458
|
/* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", color: theme.dim, children: [
|
|
5363
5459
|
`solongate v${ver}`,
|
|
5364
|
-
latest ? newerThan(latest, ver) ? /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: ` \xB7 v${latest} on npm \u2014 auto-updating` }) : /* @__PURE__ */ jsx8(Text8, { color: theme.ok, children: " \xB7 latest" }) : null
|
|
5460
|
+
latest ? newerThan(latest, ver) ? /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: ` \xB7 v${latest} on npm \u2014 ${autoUp ? "auto-updating" : "UPDATES above"}` }) : /* @__PURE__ */ jsx8(Text8, { color: theme.ok, children: " \xB7 latest" }) : null
|
|
5365
5461
|
] }, "ver")
|
|
5366
5462
|
);
|
|
5367
5463
|
lineKey.push("");
|
|
@@ -5566,7 +5662,7 @@ function App() {
|
|
|
5566
5662
|
// a row off whichever panel is open.
|
|
5567
5663
|
/* @__PURE__ */ jsx9(Text9, { color: theme.bad, bold: true, children: " \xB7 allowance used up \xB7 every call denied \xB7 create an account to carry on" })
|
|
5568
5664
|
) : 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" }),
|
|
5569
|
-
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` }) : null
|
|
5665
|
+
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
|
|
5570
5666
|
] }),
|
|
5571
5667
|
/* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexGrow: 1, children: [
|
|
5572
5668
|
/* @__PURE__ */ jsx9(Box9, { flexDirection: "column", flexShrink: 0, width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => {
|
package/hooks/guard.bundled.mjs
CHANGED
|
@@ -6536,7 +6536,7 @@ import { resolve, join, dirname, isAbsolute } from "node:path";
|
|
|
6536
6536
|
import { homedir } from "node:os";
|
|
6537
6537
|
import { gunzipSync } from "node:zlib";
|
|
6538
6538
|
import { createHash } from "node:crypto";
|
|
6539
|
-
var HOOK_VERSION =
|
|
6539
|
+
var HOOK_VERSION = 70;
|
|
6540
6540
|
function localLogsOnly(security) {
|
|
6541
6541
|
if (security && typeof security === "object") {
|
|
6542
6542
|
const l = security.localLogs;
|
|
@@ -7951,13 +7951,52 @@ function rateLimitCheck(agentKey, limits) {
|
|
|
7951
7951
|
return null;
|
|
7952
7952
|
}
|
|
7953
7953
|
}
|
|
7954
|
+
function quotaTally(used) {
|
|
7955
|
+
const dir = resolve(homedir(), ".solongate");
|
|
7956
|
+
const markFile = join(dir, ".quota-mark");
|
|
7957
|
+
const tallyFile = join(dir, ".quota-tally");
|
|
7958
|
+
let n = 0;
|
|
7959
|
+
try {
|
|
7960
|
+
let mark = null;
|
|
7961
|
+
if (existsSync(markFile)) {
|
|
7962
|
+
try {
|
|
7963
|
+
mark = readFileSync(markFile, "utf-8").trim();
|
|
7964
|
+
} catch {
|
|
7965
|
+
}
|
|
7966
|
+
}
|
|
7967
|
+
if (mark !== String(used)) {
|
|
7968
|
+
writeFileSync(markFile, String(used));
|
|
7969
|
+
writeFileSync(tallyFile, "");
|
|
7970
|
+
} else if (existsSync(tallyFile)) {
|
|
7971
|
+
try {
|
|
7972
|
+
n = statSync(tallyFile).size;
|
|
7973
|
+
} catch {
|
|
7974
|
+
}
|
|
7975
|
+
}
|
|
7976
|
+
} catch {
|
|
7977
|
+
}
|
|
7978
|
+
return {
|
|
7979
|
+
n,
|
|
7980
|
+
add() {
|
|
7981
|
+
try {
|
|
7982
|
+
appendFileSync(tallyFile, ".");
|
|
7983
|
+
} catch {
|
|
7984
|
+
}
|
|
7985
|
+
}
|
|
7986
|
+
};
|
|
7987
|
+
}
|
|
7954
7988
|
function securityLayerCheck(toolName, args, cfg, agentKey) {
|
|
7955
7989
|
if (!cfg)
|
|
7956
7990
|
return null;
|
|
7957
7991
|
try {
|
|
7958
7992
|
const q = cfg.guestQuota;
|
|
7959
|
-
if (q
|
|
7960
|
-
|
|
7993
|
+
if (q) {
|
|
7994
|
+
const tally = quotaTally(q.used ?? 0);
|
|
7995
|
+
const spent = (q.used ?? 0) + tally.n;
|
|
7996
|
+
if (q.exhausted || spent >= q.limit) {
|
|
7997
|
+
return `Guest allowance used up (${q.limit} tool calls). Create an account at https://auth.solongate.com to carry on. Your project, its policy and everything recorded carry over, and this device keeps working. To stop guarding this machine instead: solongate, Settings, guard, then press d.`;
|
|
7998
|
+
}
|
|
7999
|
+
tally.add();
|
|
7961
8000
|
}
|
|
7962
8001
|
if (cfg.dlpBlock) {
|
|
7963
8002
|
const hit = dlpScan(args, cfg.dlpBlock);
|
package/hooks/guard.mjs
CHANGED
|
@@ -32,7 +32,7 @@ import { createHash } from 'node:crypto';
|
|
|
32
32
|
// the installed hook self-updates when the cloud version is higher (see
|
|
33
33
|
// maybeSelfUpdate). This is what makes guard fixes propagate without a manual
|
|
34
34
|
// reinstall — the same trust model as the OPA WASM this hook already runs.
|
|
35
|
-
const HOOK_VERSION =
|
|
35
|
+
const HOOK_VERSION = 70;
|
|
36
36
|
|
|
37
37
|
// True when local log storage is ON. In that mode logs are kept LOCAL ONLY and
|
|
38
38
|
// nothing is sent to the cloud audit log.
|
|
@@ -1761,6 +1761,40 @@ function rateLimitCheck(agentKey, limits) {
|
|
|
1761
1761
|
}
|
|
1762
1762
|
}
|
|
1763
1763
|
|
|
1764
|
+
// The allowance figure the API sends is built from audit rows, and those are
|
|
1765
|
+
// written AFTER a call runs. Under a burst of parallel tool calls every hook
|
|
1766
|
+
// therefore reads the same pre-burst figure, and the allowance overshoots by
|
|
1767
|
+
// roughly a batch before the count catches up.
|
|
1768
|
+
//
|
|
1769
|
+
// The guard is the thing making the calls, so it can close that gap without
|
|
1770
|
+
// another round trip: it tallies what it has already let through since the
|
|
1771
|
+
// server's figure last moved, and adds it in. One byte is appended per call and
|
|
1772
|
+
// the file's LENGTH is the count, because many hooks run at once and a
|
|
1773
|
+
// read-modify-write would lose increments; an O_APPEND write of a single byte
|
|
1774
|
+
// does not interleave.
|
|
1775
|
+
function quotaTally(used) {
|
|
1776
|
+
const dir = resolve(homedir(), '.solongate');
|
|
1777
|
+
const markFile = join(dir, '.quota-mark');
|
|
1778
|
+
const tallyFile = join(dir, '.quota-tally');
|
|
1779
|
+
let n = 0;
|
|
1780
|
+
try {
|
|
1781
|
+
let mark = null;
|
|
1782
|
+
if (existsSync(markFile)) { try { mark = readFileSync(markFile, 'utf-8').trim(); } catch {} }
|
|
1783
|
+
// A figure that has moved means the audit rows caught up and absorbed the
|
|
1784
|
+
// local tally, so it starts again from there.
|
|
1785
|
+
if (mark !== String(used)) {
|
|
1786
|
+
writeFileSync(markFile, String(used));
|
|
1787
|
+
writeFileSync(tallyFile, '');
|
|
1788
|
+
} else if (existsSync(tallyFile)) {
|
|
1789
|
+
try { n = statSync(tallyFile).size; } catch {}
|
|
1790
|
+
}
|
|
1791
|
+
} catch { /* a missing tally only costs accuracy near the edge, never the gate */ }
|
|
1792
|
+
return {
|
|
1793
|
+
n,
|
|
1794
|
+
add() { try { appendFileSync(tallyFile, '.'); } catch {} },
|
|
1795
|
+
};
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1764
1798
|
// Runs all enabled enforcement layers; returns a deny reason or null (allow).
|
|
1765
1799
|
function securityLayerCheck(toolName, args, cfg, agentKey) {
|
|
1766
1800
|
if (!cfg) return null;
|
|
@@ -1771,8 +1805,17 @@ function securityLayerCheck(toolName, args, cfg, agentKey) {
|
|
|
1771
1805
|
// that is stopped and told why. Checked first, so no other layer can let a
|
|
1772
1806
|
// call through after the allowance is gone.
|
|
1773
1807
|
const q = cfg.guestQuota;
|
|
1774
|
-
if (q
|
|
1775
|
-
|
|
1808
|
+
if (q) {
|
|
1809
|
+
// The server's figure plus what this device has run since, so a burst
|
|
1810
|
+
// cannot spend past the allowance while the audit rows are still in flight.
|
|
1811
|
+
const tally = quotaTally(q.used ?? 0);
|
|
1812
|
+
const spent = (q.used ?? 0) + tally.n;
|
|
1813
|
+
if (q.exhausted || spent >= q.limit) {
|
|
1814
|
+
return `Guest allowance used up (${q.limit} tool calls). Create an account at https://auth.solongate.com to carry on. Your project, its policy and everything recorded carry over, and this device keeps working. To stop guarding this machine instead: solongate, Settings, guard, then press d.`;
|
|
1815
|
+
}
|
|
1816
|
+
// Counted here rather than after the verdict, because a call the policy
|
|
1817
|
+
// goes on to deny is still recorded and still spends the allowance.
|
|
1818
|
+
tally.add();
|
|
1776
1819
|
}
|
|
1777
1820
|
if (cfg.dlpBlock) {
|
|
1778
1821
|
const hit = dlpScan(args, cfg.dlpBlock);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.83.
|
|
3
|
+
"version": "0.83.3",
|
|
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": {
|