@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.
- package/dist/global-install.d.ts +14 -0
- package/dist/global-install.js +49 -0
- package/dist/index.js +103 -22
- package/dist/tui/index.js +103 -22
- package/package.json +1 -1
package/dist/global-install.d.ts
CHANGED
|
@@ -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
|
package/dist/global-install.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
10156
|
-
|
|
10157
|
-
|
|
10158
|
-
|
|
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
|
-
|
|
10408
|
-
|
|
10409
|
-
|
|
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
|
|
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: "
|
|
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
|
|
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
|
|
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
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
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
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
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
|
|
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: "
|
|
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
|
|
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.
|
|
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": {
|