@solongate/proxy 0.82.10 → 0.82.11
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 +3 -0
- package/dist/global-install.js +68 -2
- package/dist/index.js +62 -2
- package/dist/login.js +42 -2
- package/dist/tui/index.js +62 -2
- package/package.json +1 -1
package/dist/global-install.d.ts
CHANGED
|
@@ -5,9 +5,12 @@ export declare function globalPaths(): {
|
|
|
5
5
|
sgDir: string;
|
|
6
6
|
hooksDir: string;
|
|
7
7
|
claudeDir: string;
|
|
8
|
+
geminiDir: string;
|
|
8
9
|
settingsPath: string;
|
|
9
10
|
backupPath: string;
|
|
10
11
|
configPath: string;
|
|
12
|
+
geminiSettingsPath: string;
|
|
13
|
+
geminiBackupPath: string;
|
|
11
14
|
};
|
|
12
15
|
export declare function clearGuardUpdateCheck(): boolean;
|
|
13
16
|
export declare function runGlobalRestore(): void;
|
package/dist/global-install.js
CHANGED
|
@@ -71,7 +71,8 @@ function protectedTargets() {
|
|
|
71
71
|
join(p.hooksDir, "stop.mjs"),
|
|
72
72
|
join(p.hooksDir, "shield.mjs"),
|
|
73
73
|
p.configPath,
|
|
74
|
-
p.settingsPath
|
|
74
|
+
p.settingsPath,
|
|
75
|
+
p.geminiSettingsPath
|
|
75
76
|
];
|
|
76
77
|
}
|
|
77
78
|
function lockProtected() {
|
|
@@ -85,14 +86,21 @@ function globalPaths() {
|
|
|
85
86
|
const sgDir = join(home, ".solongate");
|
|
86
87
|
const hooksDir = join(sgDir, "hooks");
|
|
87
88
|
const claudeDir = join(home, ".claude");
|
|
89
|
+
const geminiDir = join(home, ".gemini");
|
|
88
90
|
return {
|
|
89
91
|
home,
|
|
90
92
|
sgDir,
|
|
91
93
|
hooksDir,
|
|
92
94
|
claudeDir,
|
|
95
|
+
geminiDir,
|
|
93
96
|
settingsPath: join(claudeDir, "settings.json"),
|
|
94
97
|
backupPath: join(claudeDir, "settings.solongate.bak"),
|
|
95
|
-
configPath: join(sgDir, "cloud-guard.json")
|
|
98
|
+
configPath: join(sgDir, "cloud-guard.json"),
|
|
99
|
+
// Gemini CLI reads hooks from ~/.gemini/settings.json (user-level). Only the
|
|
100
|
+
// guard is registered there (BeforeTool); Gemini's ALLOW-path audit is
|
|
101
|
+
// covered by the passive session-log collector (~/.gemini/tmp), not a hook.
|
|
102
|
+
geminiSettingsPath: join(geminiDir, "settings.json"),
|
|
103
|
+
geminiBackupPath: join(geminiDir, "settings.solongate.bak")
|
|
96
104
|
};
|
|
97
105
|
}
|
|
98
106
|
function clearGuardUpdateCheck() {
|
|
@@ -110,6 +118,50 @@ function readGuard() {
|
|
|
110
118
|
const bundled = join(HOOKS_DIR, "guard.bundled.mjs");
|
|
111
119
|
return existsSync(bundled) ? readFileSync(bundled, "utf-8") : readHook("guard.mjs");
|
|
112
120
|
}
|
|
121
|
+
function geminiHookCommand(guardAbs) {
|
|
122
|
+
const nodeBin = process.execPath.replace(/\\/g, "/");
|
|
123
|
+
const call = process.platform === "win32" ? "& " : "";
|
|
124
|
+
return `${call}"${nodeBin}" "${guardAbs.replace(/\\/g, "/")}" gemini-cli "Gemini CLI"`;
|
|
125
|
+
}
|
|
126
|
+
function installGeminiGuard(p, guardAbs) {
|
|
127
|
+
mkdirSync(p.geminiDir, { recursive: true });
|
|
128
|
+
let existing = {};
|
|
129
|
+
if (existsSync(p.geminiSettingsPath)) {
|
|
130
|
+
const raw = readFileSync(p.geminiSettingsPath, "utf-8");
|
|
131
|
+
if (!existsSync(p.geminiBackupPath)) writeFileSync(p.geminiBackupPath, raw);
|
|
132
|
+
try {
|
|
133
|
+
existing = JSON.parse(raw);
|
|
134
|
+
} catch {
|
|
135
|
+
existing = {};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const existingHooks = existing.hooks && typeof existing.hooks === "object" ? existing.hooks : {};
|
|
139
|
+
const merged = {
|
|
140
|
+
...existing,
|
|
141
|
+
hooks: {
|
|
142
|
+
...existingHooks,
|
|
143
|
+
BeforeTool: [{ matcher: "", hooks: [{ type: "command", command: geminiHookCommand(guardAbs) }] }]
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
writeFileSync(p.geminiSettingsPath, JSON.stringify(merged, null, 2) + "\n");
|
|
147
|
+
}
|
|
148
|
+
function removeGeminiGuard(p) {
|
|
149
|
+
if (!existsSync(p.geminiSettingsPath)) return;
|
|
150
|
+
try {
|
|
151
|
+
const s = JSON.parse(readFileSync(p.geminiSettingsPath, "utf-8"));
|
|
152
|
+
const hooks = s.hooks && typeof s.hooks === "object" ? s.hooks : null;
|
|
153
|
+
if (!hooks || !Array.isArray(hooks.BeforeTool)) return;
|
|
154
|
+
const kept = hooks.BeforeTool.filter((entry) => {
|
|
155
|
+
const inner = Array.isArray(entry?.hooks) ? entry.hooks : [];
|
|
156
|
+
return !inner.some((h) => typeof h?.command === "string" && h.command.includes(".solongate"));
|
|
157
|
+
});
|
|
158
|
+
if (kept.length) hooks.BeforeTool = kept;
|
|
159
|
+
else delete hooks.BeforeTool;
|
|
160
|
+
if (Object.keys(hooks).length === 0) delete s.hooks;
|
|
161
|
+
writeFileSync(p.geminiSettingsPath, JSON.stringify(s, null, 2) + "\n");
|
|
162
|
+
} catch {
|
|
163
|
+
}
|
|
164
|
+
}
|
|
113
165
|
function ask(question) {
|
|
114
166
|
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
115
167
|
return new Promise((res) => rl.question(question, (a) => {
|
|
@@ -121,6 +173,7 @@ function runGlobalRestore() {
|
|
|
121
173
|
const p = globalPaths();
|
|
122
174
|
unlockProtected();
|
|
123
175
|
removeClaudeShim();
|
|
176
|
+
removeGeminiGuard(p);
|
|
124
177
|
if (existsSync(p.backupPath)) {
|
|
125
178
|
writeFileSync(p.settingsPath, readFileSync(p.backupPath, "utf-8"));
|
|
126
179
|
console.log(` Restored ${p.settingsPath} from backup.`);
|
|
@@ -179,6 +232,10 @@ function installGlobalQuiet() {
|
|
|
179
232
|
}
|
|
180
233
|
};
|
|
181
234
|
writeFileSync(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
|
|
235
|
+
try {
|
|
236
|
+
installGeminiGuard(p, join(p.hooksDir, "guard.mjs").replace(/\\/g, "/"));
|
|
237
|
+
} catch {
|
|
238
|
+
}
|
|
182
239
|
if (process.env["SOLONGATE_OS_LOCK"] === "1") lockProtected();
|
|
183
240
|
return { ok: true, message: "guard installed (open a new session)" };
|
|
184
241
|
} catch (e) {
|
|
@@ -218,6 +275,10 @@ function uninstallGlobalQuiet() {
|
|
|
218
275
|
const p = globalPaths();
|
|
219
276
|
unlockProtected();
|
|
220
277
|
removeClaudeShim();
|
|
278
|
+
try {
|
|
279
|
+
removeGeminiGuard(p);
|
|
280
|
+
} catch {
|
|
281
|
+
}
|
|
221
282
|
if (!existsSync(p.settingsPath)) return { ok: true, message: "guard removed (open a new session)" };
|
|
222
283
|
const s = JSON.parse(readFileSync(p.settingsPath, "utf-8"));
|
|
223
284
|
delete s.hooks;
|
|
@@ -356,6 +417,11 @@ async function runGlobalInstall(opts = {}) {
|
|
|
356
417
|
};
|
|
357
418
|
writeFileSync(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
|
|
358
419
|
console.log(` Registered global hooks \u2192 ${p.settingsPath}`);
|
|
420
|
+
try {
|
|
421
|
+
installGeminiGuard(p, guardAbs);
|
|
422
|
+
console.log(` Registered Gemini CLI guard \u2192 ${p.geminiSettingsPath}`);
|
|
423
|
+
} catch {
|
|
424
|
+
}
|
|
359
425
|
if (process.env["SOLONGATE_OS_LOCK"] === "1") {
|
|
360
426
|
lockProtected();
|
|
361
427
|
console.log(" Locked protection files (OS-level read-only/immutable).");
|
package/dist/index.js
CHANGED
|
@@ -10210,7 +10210,8 @@ function protectedTargets() {
|
|
|
10210
10210
|
join11(p.hooksDir, "stop.mjs"),
|
|
10211
10211
|
join11(p.hooksDir, "shield.mjs"),
|
|
10212
10212
|
p.configPath,
|
|
10213
|
-
p.settingsPath
|
|
10213
|
+
p.settingsPath,
|
|
10214
|
+
p.geminiSettingsPath
|
|
10214
10215
|
];
|
|
10215
10216
|
}
|
|
10216
10217
|
function lockProtected() {
|
|
@@ -10224,14 +10225,21 @@ function globalPaths() {
|
|
|
10224
10225
|
const sgDir = join11(home, ".solongate");
|
|
10225
10226
|
const hooksDir = join11(sgDir, "hooks");
|
|
10226
10227
|
const claudeDir = join11(home, ".claude");
|
|
10228
|
+
const geminiDir = join11(home, ".gemini");
|
|
10227
10229
|
return {
|
|
10228
10230
|
home,
|
|
10229
10231
|
sgDir,
|
|
10230
10232
|
hooksDir,
|
|
10231
10233
|
claudeDir,
|
|
10234
|
+
geminiDir,
|
|
10232
10235
|
settingsPath: join11(claudeDir, "settings.json"),
|
|
10233
10236
|
backupPath: join11(claudeDir, "settings.solongate.bak"),
|
|
10234
|
-
configPath: join11(sgDir, "cloud-guard.json")
|
|
10237
|
+
configPath: join11(sgDir, "cloud-guard.json"),
|
|
10238
|
+
// Gemini CLI reads hooks from ~/.gemini/settings.json (user-level). Only the
|
|
10239
|
+
// guard is registered there (BeforeTool); Gemini's ALLOW-path audit is
|
|
10240
|
+
// covered by the passive session-log collector (~/.gemini/tmp), not a hook.
|
|
10241
|
+
geminiSettingsPath: join11(geminiDir, "settings.json"),
|
|
10242
|
+
geminiBackupPath: join11(geminiDir, "settings.solongate.bak")
|
|
10235
10243
|
};
|
|
10236
10244
|
}
|
|
10237
10245
|
function readHook(filename) {
|
|
@@ -10241,6 +10249,50 @@ function readGuard() {
|
|
|
10241
10249
|
const bundled = join11(HOOKS_DIR, "guard.bundled.mjs");
|
|
10242
10250
|
return existsSync4(bundled) ? readFileSync9(bundled, "utf-8") : readHook("guard.mjs");
|
|
10243
10251
|
}
|
|
10252
|
+
function geminiHookCommand(guardAbs) {
|
|
10253
|
+
const nodeBin = process.execPath.replace(/\\/g, "/");
|
|
10254
|
+
const call = process.platform === "win32" ? "& " : "";
|
|
10255
|
+
return `${call}"${nodeBin}" "${guardAbs.replace(/\\/g, "/")}" gemini-cli "Gemini CLI"`;
|
|
10256
|
+
}
|
|
10257
|
+
function installGeminiGuard(p, guardAbs) {
|
|
10258
|
+
mkdirSync8(p.geminiDir, { recursive: true });
|
|
10259
|
+
let existing = {};
|
|
10260
|
+
if (existsSync4(p.geminiSettingsPath)) {
|
|
10261
|
+
const raw = readFileSync9(p.geminiSettingsPath, "utf-8");
|
|
10262
|
+
if (!existsSync4(p.geminiBackupPath)) writeFileSync9(p.geminiBackupPath, raw);
|
|
10263
|
+
try {
|
|
10264
|
+
existing = JSON.parse(raw);
|
|
10265
|
+
} catch {
|
|
10266
|
+
existing = {};
|
|
10267
|
+
}
|
|
10268
|
+
}
|
|
10269
|
+
const existingHooks = existing.hooks && typeof existing.hooks === "object" ? existing.hooks : {};
|
|
10270
|
+
const merged = {
|
|
10271
|
+
...existing,
|
|
10272
|
+
hooks: {
|
|
10273
|
+
...existingHooks,
|
|
10274
|
+
BeforeTool: [{ matcher: "", hooks: [{ type: "command", command: geminiHookCommand(guardAbs) }] }]
|
|
10275
|
+
}
|
|
10276
|
+
};
|
|
10277
|
+
writeFileSync9(p.geminiSettingsPath, JSON.stringify(merged, null, 2) + "\n");
|
|
10278
|
+
}
|
|
10279
|
+
function removeGeminiGuard(p) {
|
|
10280
|
+
if (!existsSync4(p.geminiSettingsPath)) return;
|
|
10281
|
+
try {
|
|
10282
|
+
const s = JSON.parse(readFileSync9(p.geminiSettingsPath, "utf-8"));
|
|
10283
|
+
const hooks = s.hooks && typeof s.hooks === "object" ? s.hooks : null;
|
|
10284
|
+
if (!hooks || !Array.isArray(hooks.BeforeTool)) return;
|
|
10285
|
+
const kept = hooks.BeforeTool.filter((entry) => {
|
|
10286
|
+
const inner = Array.isArray(entry?.hooks) ? entry.hooks : [];
|
|
10287
|
+
return !inner.some((h) => typeof h?.command === "string" && h.command.includes(".solongate"));
|
|
10288
|
+
});
|
|
10289
|
+
if (kept.length) hooks.BeforeTool = kept;
|
|
10290
|
+
else delete hooks.BeforeTool;
|
|
10291
|
+
if (Object.keys(hooks).length === 0) delete s.hooks;
|
|
10292
|
+
writeFileSync9(p.geminiSettingsPath, JSON.stringify(s, null, 2) + "\n");
|
|
10293
|
+
} catch {
|
|
10294
|
+
}
|
|
10295
|
+
}
|
|
10244
10296
|
function installGlobalQuiet() {
|
|
10245
10297
|
try {
|
|
10246
10298
|
const p = globalPaths();
|
|
@@ -10283,6 +10335,10 @@ function installGlobalQuiet() {
|
|
|
10283
10335
|
}
|
|
10284
10336
|
};
|
|
10285
10337
|
writeFileSync9(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
|
|
10338
|
+
try {
|
|
10339
|
+
installGeminiGuard(p, join11(p.hooksDir, "guard.mjs").replace(/\\/g, "/"));
|
|
10340
|
+
} catch {
|
|
10341
|
+
}
|
|
10286
10342
|
if (process.env["SOLONGATE_OS_LOCK"] === "1") lockProtected();
|
|
10287
10343
|
return { ok: true, message: "guard installed (open a new session)" };
|
|
10288
10344
|
} catch (e) {
|
|
@@ -10322,6 +10378,10 @@ function uninstallGlobalQuiet() {
|
|
|
10322
10378
|
const p = globalPaths();
|
|
10323
10379
|
unlockProtected();
|
|
10324
10380
|
removeClaudeShim();
|
|
10381
|
+
try {
|
|
10382
|
+
removeGeminiGuard(p);
|
|
10383
|
+
} catch {
|
|
10384
|
+
}
|
|
10325
10385
|
if (!existsSync4(p.settingsPath)) return { ok: true, message: "guard removed (open a new session)" };
|
|
10326
10386
|
const s = JSON.parse(readFileSync9(p.settingsPath, "utf-8"));
|
|
10327
10387
|
delete s.hooks;
|
package/dist/login.js
CHANGED
|
@@ -98,7 +98,8 @@ function protectedTargets() {
|
|
|
98
98
|
join(p.hooksDir, "stop.mjs"),
|
|
99
99
|
join(p.hooksDir, "shield.mjs"),
|
|
100
100
|
p.configPath,
|
|
101
|
-
p.settingsPath
|
|
101
|
+
p.settingsPath,
|
|
102
|
+
p.geminiSettingsPath
|
|
102
103
|
];
|
|
103
104
|
}
|
|
104
105
|
function lockProtected() {
|
|
@@ -112,14 +113,21 @@ function globalPaths() {
|
|
|
112
113
|
const sgDir = join(home, ".solongate");
|
|
113
114
|
const hooksDir = join(sgDir, "hooks");
|
|
114
115
|
const claudeDir = join(home, ".claude");
|
|
116
|
+
const geminiDir = join(home, ".gemini");
|
|
115
117
|
return {
|
|
116
118
|
home,
|
|
117
119
|
sgDir,
|
|
118
120
|
hooksDir,
|
|
119
121
|
claudeDir,
|
|
122
|
+
geminiDir,
|
|
120
123
|
settingsPath: join(claudeDir, "settings.json"),
|
|
121
124
|
backupPath: join(claudeDir, "settings.solongate.bak"),
|
|
122
|
-
configPath: join(sgDir, "cloud-guard.json")
|
|
125
|
+
configPath: join(sgDir, "cloud-guard.json"),
|
|
126
|
+
// Gemini CLI reads hooks from ~/.gemini/settings.json (user-level). Only the
|
|
127
|
+
// guard is registered there (BeforeTool); Gemini's ALLOW-path audit is
|
|
128
|
+
// covered by the passive session-log collector (~/.gemini/tmp), not a hook.
|
|
129
|
+
geminiSettingsPath: join(geminiDir, "settings.json"),
|
|
130
|
+
geminiBackupPath: join(geminiDir, "settings.solongate.bak")
|
|
123
131
|
};
|
|
124
132
|
}
|
|
125
133
|
function readHook(filename) {
|
|
@@ -129,6 +137,33 @@ function readGuard() {
|
|
|
129
137
|
const bundled = join(HOOKS_DIR, "guard.bundled.mjs");
|
|
130
138
|
return existsSync(bundled) ? readFileSync(bundled, "utf-8") : readHook("guard.mjs");
|
|
131
139
|
}
|
|
140
|
+
function geminiHookCommand(guardAbs) {
|
|
141
|
+
const nodeBin = process.execPath.replace(/\\/g, "/");
|
|
142
|
+
const call = process.platform === "win32" ? "& " : "";
|
|
143
|
+
return `${call}"${nodeBin}" "${guardAbs.replace(/\\/g, "/")}" gemini-cli "Gemini CLI"`;
|
|
144
|
+
}
|
|
145
|
+
function installGeminiGuard(p, guardAbs) {
|
|
146
|
+
mkdirSync(p.geminiDir, { recursive: true });
|
|
147
|
+
let existing = {};
|
|
148
|
+
if (existsSync(p.geminiSettingsPath)) {
|
|
149
|
+
const raw = readFileSync(p.geminiSettingsPath, "utf-8");
|
|
150
|
+
if (!existsSync(p.geminiBackupPath)) writeFileSync(p.geminiBackupPath, raw);
|
|
151
|
+
try {
|
|
152
|
+
existing = JSON.parse(raw);
|
|
153
|
+
} catch {
|
|
154
|
+
existing = {};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const existingHooks = existing.hooks && typeof existing.hooks === "object" ? existing.hooks : {};
|
|
158
|
+
const merged = {
|
|
159
|
+
...existing,
|
|
160
|
+
hooks: {
|
|
161
|
+
...existingHooks,
|
|
162
|
+
BeforeTool: [{ matcher: "", hooks: [{ type: "command", command: geminiHookCommand(guardAbs) }] }]
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
writeFileSync(p.geminiSettingsPath, JSON.stringify(merged, null, 2) + "\n");
|
|
166
|
+
}
|
|
132
167
|
function ask(question) {
|
|
133
168
|
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
134
169
|
return new Promise((res) => rl.question(question, (a) => {
|
|
@@ -257,6 +292,11 @@ async function runGlobalInstall(opts = {}) {
|
|
|
257
292
|
};
|
|
258
293
|
writeFileSync(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
|
|
259
294
|
console.log(` Registered global hooks \u2192 ${p.settingsPath}`);
|
|
295
|
+
try {
|
|
296
|
+
installGeminiGuard(p, guardAbs);
|
|
297
|
+
console.log(` Registered Gemini CLI guard \u2192 ${p.geminiSettingsPath}`);
|
|
298
|
+
} catch {
|
|
299
|
+
}
|
|
260
300
|
if (process.env["SOLONGATE_OS_LOCK"] === "1") {
|
|
261
301
|
lockProtected();
|
|
262
302
|
console.log(" Locked protection files (OS-level read-only/immutable).");
|
package/dist/tui/index.js
CHANGED
|
@@ -3268,7 +3268,8 @@ function protectedTargets() {
|
|
|
3268
3268
|
join6(p.hooksDir, "stop.mjs"),
|
|
3269
3269
|
join6(p.hooksDir, "shield.mjs"),
|
|
3270
3270
|
p.configPath,
|
|
3271
|
-
p.settingsPath
|
|
3271
|
+
p.settingsPath,
|
|
3272
|
+
p.geminiSettingsPath
|
|
3272
3273
|
];
|
|
3273
3274
|
}
|
|
3274
3275
|
function lockProtected() {
|
|
@@ -3282,14 +3283,21 @@ function globalPaths() {
|
|
|
3282
3283
|
const sgDir = join6(home, ".solongate");
|
|
3283
3284
|
const hooksDir = join6(sgDir, "hooks");
|
|
3284
3285
|
const claudeDir = join6(home, ".claude");
|
|
3286
|
+
const geminiDir = join6(home, ".gemini");
|
|
3285
3287
|
return {
|
|
3286
3288
|
home,
|
|
3287
3289
|
sgDir,
|
|
3288
3290
|
hooksDir,
|
|
3289
3291
|
claudeDir,
|
|
3292
|
+
geminiDir,
|
|
3290
3293
|
settingsPath: join6(claudeDir, "settings.json"),
|
|
3291
3294
|
backupPath: join6(claudeDir, "settings.solongate.bak"),
|
|
3292
|
-
configPath: join6(sgDir, "cloud-guard.json")
|
|
3295
|
+
configPath: join6(sgDir, "cloud-guard.json"),
|
|
3296
|
+
// Gemini CLI reads hooks from ~/.gemini/settings.json (user-level). Only the
|
|
3297
|
+
// guard is registered there (BeforeTool); Gemini's ALLOW-path audit is
|
|
3298
|
+
// covered by the passive session-log collector (~/.gemini/tmp), not a hook.
|
|
3299
|
+
geminiSettingsPath: join6(geminiDir, "settings.json"),
|
|
3300
|
+
geminiBackupPath: join6(geminiDir, "settings.solongate.bak")
|
|
3293
3301
|
};
|
|
3294
3302
|
}
|
|
3295
3303
|
function readHook(filename) {
|
|
@@ -3299,6 +3307,50 @@ function readGuard() {
|
|
|
3299
3307
|
const bundled = join6(HOOKS_DIR, "guard.bundled.mjs");
|
|
3300
3308
|
return existsSync2(bundled) ? readFileSync4(bundled, "utf-8") : readHook("guard.mjs");
|
|
3301
3309
|
}
|
|
3310
|
+
function geminiHookCommand(guardAbs) {
|
|
3311
|
+
const nodeBin = process.execPath.replace(/\\/g, "/");
|
|
3312
|
+
const call = process.platform === "win32" ? "& " : "";
|
|
3313
|
+
return `${call}"${nodeBin}" "${guardAbs.replace(/\\/g, "/")}" gemini-cli "Gemini CLI"`;
|
|
3314
|
+
}
|
|
3315
|
+
function installGeminiGuard(p, guardAbs) {
|
|
3316
|
+
mkdirSync4(p.geminiDir, { recursive: true });
|
|
3317
|
+
let existing = {};
|
|
3318
|
+
if (existsSync2(p.geminiSettingsPath)) {
|
|
3319
|
+
const raw = readFileSync4(p.geminiSettingsPath, "utf-8");
|
|
3320
|
+
if (!existsSync2(p.geminiBackupPath)) writeFileSync5(p.geminiBackupPath, raw);
|
|
3321
|
+
try {
|
|
3322
|
+
existing = JSON.parse(raw);
|
|
3323
|
+
} catch {
|
|
3324
|
+
existing = {};
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
const existingHooks = existing.hooks && typeof existing.hooks === "object" ? existing.hooks : {};
|
|
3328
|
+
const merged = {
|
|
3329
|
+
...existing,
|
|
3330
|
+
hooks: {
|
|
3331
|
+
...existingHooks,
|
|
3332
|
+
BeforeTool: [{ matcher: "", hooks: [{ type: "command", command: geminiHookCommand(guardAbs) }] }]
|
|
3333
|
+
}
|
|
3334
|
+
};
|
|
3335
|
+
writeFileSync5(p.geminiSettingsPath, JSON.stringify(merged, null, 2) + "\n");
|
|
3336
|
+
}
|
|
3337
|
+
function removeGeminiGuard(p) {
|
|
3338
|
+
if (!existsSync2(p.geminiSettingsPath)) return;
|
|
3339
|
+
try {
|
|
3340
|
+
const s = JSON.parse(readFileSync4(p.geminiSettingsPath, "utf-8"));
|
|
3341
|
+
const hooks = s.hooks && typeof s.hooks === "object" ? s.hooks : null;
|
|
3342
|
+
if (!hooks || !Array.isArray(hooks.BeforeTool)) return;
|
|
3343
|
+
const kept = hooks.BeforeTool.filter((entry) => {
|
|
3344
|
+
const inner = Array.isArray(entry?.hooks) ? entry.hooks : [];
|
|
3345
|
+
return !inner.some((h) => typeof h?.command === "string" && h.command.includes(".solongate"));
|
|
3346
|
+
});
|
|
3347
|
+
if (kept.length) hooks.BeforeTool = kept;
|
|
3348
|
+
else delete hooks.BeforeTool;
|
|
3349
|
+
if (Object.keys(hooks).length === 0) delete s.hooks;
|
|
3350
|
+
writeFileSync5(p.geminiSettingsPath, JSON.stringify(s, null, 2) + "\n");
|
|
3351
|
+
} catch {
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3302
3354
|
function installGlobalQuiet() {
|
|
3303
3355
|
try {
|
|
3304
3356
|
const p = globalPaths();
|
|
@@ -3341,6 +3393,10 @@ function installGlobalQuiet() {
|
|
|
3341
3393
|
}
|
|
3342
3394
|
};
|
|
3343
3395
|
writeFileSync5(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
|
|
3396
|
+
try {
|
|
3397
|
+
installGeminiGuard(p, join6(p.hooksDir, "guard.mjs").replace(/\\/g, "/"));
|
|
3398
|
+
} catch {
|
|
3399
|
+
}
|
|
3344
3400
|
if (process.env["SOLONGATE_OS_LOCK"] === "1") lockProtected();
|
|
3345
3401
|
return { ok: true, message: "guard installed (open a new session)" };
|
|
3346
3402
|
} catch (e) {
|
|
@@ -3380,6 +3436,10 @@ function uninstallGlobalQuiet() {
|
|
|
3380
3436
|
const p = globalPaths();
|
|
3381
3437
|
unlockProtected();
|
|
3382
3438
|
removeClaudeShim();
|
|
3439
|
+
try {
|
|
3440
|
+
removeGeminiGuard(p);
|
|
3441
|
+
} catch {
|
|
3442
|
+
}
|
|
3383
3443
|
if (!existsSync2(p.settingsPath)) return { ok: true, message: "guard removed (open a new session)" };
|
|
3384
3444
|
const s = JSON.parse(readFileSync4(p.settingsPath, "utf-8"));
|
|
3385
3445
|
delete s.hooks;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.82.
|
|
3
|
+
"version": "0.82.11",
|
|
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": {
|