@solongate/proxy 0.43.0 → 0.44.0
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.js +198 -0
- package/dist/index.js +7404 -2722
- package/dist/init.js +300 -68
- package/dist/lib.js +6759 -2062
- package/dist/login.js +308 -0
- package/dist/pull-push.js +2 -1
- package/hooks/audit.mjs +32 -11
- package/hooks/guard.bundled.mjs +7257 -0
- package/hooks/guard.mjs +703 -1077
- package/package.json +6 -2
package/dist/login.js
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/login.ts
|
|
4
|
+
import { spawn } from "child_process";
|
|
5
|
+
|
|
6
|
+
// src/cli-utils.ts
|
|
7
|
+
var c = {
|
|
8
|
+
reset: "\x1B[0m",
|
|
9
|
+
bold: "\x1B[1m",
|
|
10
|
+
dim: "\x1B[2m",
|
|
11
|
+
italic: "\x1B[3m",
|
|
12
|
+
white: "\x1B[97m",
|
|
13
|
+
gray: "\x1B[90m",
|
|
14
|
+
blue1: "\x1B[38;2;20;50;160m",
|
|
15
|
+
blue2: "\x1B[38;2;40;80;190m",
|
|
16
|
+
blue3: "\x1B[38;2;60;110;215m",
|
|
17
|
+
blue4: "\x1B[38;2;90;140;230m",
|
|
18
|
+
blue5: "\x1B[38;2;130;170;240m",
|
|
19
|
+
blue6: "\x1B[38;2;170;200;250m",
|
|
20
|
+
green: "\x1B[38;2;80;200;120m",
|
|
21
|
+
red: "\x1B[38;2;220;80;80m",
|
|
22
|
+
cyan: "\x1B[38;2;100;200;220m",
|
|
23
|
+
yellow: "\x1B[38;2;220;200;80m",
|
|
24
|
+
bgBlue: "\x1B[48;2;20;50;160m"
|
|
25
|
+
};
|
|
26
|
+
var BANNER_COLORS = [c.blue1, c.blue2, c.blue3, c.blue4, c.blue5, c.blue6];
|
|
27
|
+
|
|
28
|
+
// src/global-install.ts
|
|
29
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
30
|
+
import { resolve, join, dirname } from "path";
|
|
31
|
+
import { homedir } from "os";
|
|
32
|
+
import { fileURLToPath } from "url";
|
|
33
|
+
import { createInterface } from "readline";
|
|
34
|
+
import { execFileSync } from "child_process";
|
|
35
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
36
|
+
var HOOKS_DIR = resolve(__dirname, "..", "hooks");
|
|
37
|
+
function lockFile(file) {
|
|
38
|
+
if (!existsSync(file)) return;
|
|
39
|
+
try {
|
|
40
|
+
if (process.platform === "win32") {
|
|
41
|
+
try {
|
|
42
|
+
execFileSync("icacls", [file, "/deny", "*S-1-1-0:(WD,AD,DC,DE)"], { stdio: "ignore" });
|
|
43
|
+
} catch {
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
execFileSync("attrib", ["+R", file], { stdio: "ignore" });
|
|
47
|
+
} catch {
|
|
48
|
+
}
|
|
49
|
+
} else if (process.platform === "darwin") {
|
|
50
|
+
try {
|
|
51
|
+
execFileSync("chflags", ["uchg", file], { stdio: "ignore" });
|
|
52
|
+
} catch {
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
try {
|
|
56
|
+
execFileSync("chattr", ["+i", file], { stdio: "ignore" });
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
} catch {
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function unlockFile(file) {
|
|
64
|
+
if (!existsSync(file)) return;
|
|
65
|
+
try {
|
|
66
|
+
if (process.platform === "win32") {
|
|
67
|
+
try {
|
|
68
|
+
execFileSync("icacls", [file, "/remove:d", "*S-1-1-0"], { stdio: "ignore" });
|
|
69
|
+
} catch {
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
execFileSync("icacls", [file, "/reset"], { stdio: "ignore" });
|
|
73
|
+
} catch {
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
execFileSync("attrib", ["-R", file], { stdio: "ignore" });
|
|
77
|
+
} catch {
|
|
78
|
+
}
|
|
79
|
+
} else if (process.platform === "darwin") {
|
|
80
|
+
try {
|
|
81
|
+
execFileSync("chflags", ["nouchg", file], { stdio: "ignore" });
|
|
82
|
+
} catch {
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
try {
|
|
86
|
+
execFileSync("chattr", ["-i", file], { stdio: "ignore" });
|
|
87
|
+
} catch {
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} catch {
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function protectedTargets() {
|
|
94
|
+
const p = globalPaths();
|
|
95
|
+
return [
|
|
96
|
+
join(p.hooksDir, "guard.mjs"),
|
|
97
|
+
join(p.hooksDir, "audit.mjs"),
|
|
98
|
+
join(p.hooksDir, "stop.mjs"),
|
|
99
|
+
p.configPath,
|
|
100
|
+
p.settingsPath
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
function lockProtected() {
|
|
104
|
+
for (const f of protectedTargets()) lockFile(f);
|
|
105
|
+
}
|
|
106
|
+
function unlockProtected() {
|
|
107
|
+
for (const f of protectedTargets()) unlockFile(f);
|
|
108
|
+
}
|
|
109
|
+
function globalPaths() {
|
|
110
|
+
const home = homedir();
|
|
111
|
+
const sgDir = join(home, ".solongate");
|
|
112
|
+
const hooksDir = join(sgDir, "hooks");
|
|
113
|
+
const claudeDir = join(home, ".claude");
|
|
114
|
+
return {
|
|
115
|
+
home,
|
|
116
|
+
sgDir,
|
|
117
|
+
hooksDir,
|
|
118
|
+
claudeDir,
|
|
119
|
+
settingsPath: join(claudeDir, "settings.json"),
|
|
120
|
+
backupPath: join(claudeDir, "settings.solongate.bak"),
|
|
121
|
+
configPath: join(sgDir, "cloud-guard.json")
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function readHook(filename) {
|
|
125
|
+
return readFileSync(join(HOOKS_DIR, filename), "utf-8");
|
|
126
|
+
}
|
|
127
|
+
function readGuard() {
|
|
128
|
+
const bundled = join(HOOKS_DIR, "guard.bundled.mjs");
|
|
129
|
+
return existsSync(bundled) ? readFileSync(bundled, "utf-8") : readHook("guard.mjs");
|
|
130
|
+
}
|
|
131
|
+
function ask(question) {
|
|
132
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
133
|
+
return new Promise((res) => rl.question(question, (a) => {
|
|
134
|
+
rl.close();
|
|
135
|
+
res(a.trim());
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
async function runGlobalInstall(opts = {}) {
|
|
139
|
+
const p = globalPaths();
|
|
140
|
+
let apiKey = opts.apiKey || process.env["SOLONGATE_API_KEY"] || "";
|
|
141
|
+
if (!apiKey || apiKey === "sg_live_your_key_here") {
|
|
142
|
+
try {
|
|
143
|
+
const cfg = JSON.parse(readFileSync(p.configPath, "utf-8"));
|
|
144
|
+
if (cfg && typeof cfg.apiKey === "string") apiKey = cfg.apiKey;
|
|
145
|
+
} catch {
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (!apiKey || apiKey === "sg_live_your_key_here") {
|
|
149
|
+
apiKey = await ask(" Enter your SolonGate API key (sg_live_\u2026 from https://dashboard.solongate.com): ");
|
|
150
|
+
}
|
|
151
|
+
if (!apiKey.startsWith("sg_live_") && !apiKey.startsWith("sg_test_")) {
|
|
152
|
+
console.log(" Invalid API key. Must start with sg_live_ or sg_test_");
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
const apiUrl = opts.apiUrl || process.env["SOLONGATE_API_URL"] || "https://api.solongate.com";
|
|
156
|
+
mkdirSync(p.hooksDir, { recursive: true });
|
|
157
|
+
mkdirSync(p.claudeDir, { recursive: true });
|
|
158
|
+
unlockProtected();
|
|
159
|
+
writeFileSync(join(p.hooksDir, "guard.mjs"), readGuard());
|
|
160
|
+
writeFileSync(join(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
|
|
161
|
+
writeFileSync(join(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
|
|
162
|
+
console.log(` Installed hooks \u2192 ${p.hooksDir}`);
|
|
163
|
+
writeFileSync(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
|
|
164
|
+
console.log(` Wrote ${p.configPath}`);
|
|
165
|
+
let existing = {};
|
|
166
|
+
if (existsSync(p.settingsPath)) {
|
|
167
|
+
const raw = readFileSync(p.settingsPath, "utf-8");
|
|
168
|
+
if (!existsSync(p.backupPath)) {
|
|
169
|
+
writeFileSync(p.backupPath, raw);
|
|
170
|
+
console.log(` Backed up existing settings \u2192 ${p.backupPath}`);
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
existing = JSON.parse(raw);
|
|
174
|
+
} catch {
|
|
175
|
+
existing = {};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const guardAbs = join(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
|
|
179
|
+
const auditAbs = join(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
|
|
180
|
+
const stopAbs = join(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
|
|
181
|
+
const merged = {
|
|
182
|
+
...existing,
|
|
183
|
+
hooks: {
|
|
184
|
+
PreToolUse: [{ matcher: "", hooks: [{ type: "command", command: `node "${guardAbs}" claude-code "Claude Code"` }] }],
|
|
185
|
+
PostToolUse: [{ matcher: "", hooks: [{ type: "command", command: `node "${auditAbs}" claude-code "Claude Code"` }] }],
|
|
186
|
+
Stop: [{ matcher: "", hooks: [{ type: "command", command: `node "${stopAbs}" claude-code "Claude Code"` }] }]
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
writeFileSync(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
|
|
190
|
+
console.log(` Registered global hooks \u2192 ${p.settingsPath}`);
|
|
191
|
+
if (process.env["SOLONGATE_OS_LOCK"] === "1") {
|
|
192
|
+
lockProtected();
|
|
193
|
+
console.log(" Locked protection files (OS-level read-only/immutable).");
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// src/login.ts
|
|
198
|
+
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
199
|
+
function parseArgs(argv) {
|
|
200
|
+
const args = argv.slice(3);
|
|
201
|
+
let apiUrl = process.env["SOLONGATE_API_URL"] || "https://api.solongate.com";
|
|
202
|
+
let install = true;
|
|
203
|
+
for (let i = 0; i < args.length; i++) {
|
|
204
|
+
if (args[i] === "--api-url") apiUrl = args[++i] || apiUrl;
|
|
205
|
+
else if (args[i] === "--no-install") install = false;
|
|
206
|
+
}
|
|
207
|
+
return { apiUrl, install };
|
|
208
|
+
}
|
|
209
|
+
function openBrowser(url) {
|
|
210
|
+
try {
|
|
211
|
+
const cmd = process.platform === "win32" ? "cmd" : process.platform === "darwin" ? "open" : "xdg-open";
|
|
212
|
+
const args = process.platform === "win32" ? ["/c", "start", '""', url] : [url];
|
|
213
|
+
spawn(cmd, args, { stdio: "ignore", detached: true }).unref();
|
|
214
|
+
} catch {
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
async function main() {
|
|
218
|
+
const { apiUrl, install } = parseArgs(process.argv);
|
|
219
|
+
console.log("");
|
|
220
|
+
console.log(` ${c.bold}SolonGate \u2014 Login${c.reset}`);
|
|
221
|
+
console.log("");
|
|
222
|
+
let start;
|
|
223
|
+
try {
|
|
224
|
+
const res = await fetch(`${apiUrl}/api/v1/auth/device/start`, { method: "POST" });
|
|
225
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
226
|
+
start = await res.json();
|
|
227
|
+
} catch (e) {
|
|
228
|
+
console.log(` Could not reach SolonGate (${apiUrl}).`);
|
|
229
|
+
console.log(` ${e instanceof Error ? e.message : String(e)}`);
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
const { device_code, user_code, verification_uri_complete, verification_uri, interval, expires_in } = start;
|
|
233
|
+
const verifyUrl = verification_uri_complete || verification_uri;
|
|
234
|
+
console.log(` Opening your browser to authorize this device\u2026`);
|
|
235
|
+
console.log("");
|
|
236
|
+
console.log(` ${c.dim}If it doesn't open, visit this link:${c.reset}`);
|
|
237
|
+
console.log(` ${c.dim}${verifyUrl}${c.reset}`);
|
|
238
|
+
console.log("");
|
|
239
|
+
void user_code;
|
|
240
|
+
openBrowser(verifyUrl);
|
|
241
|
+
const pollMs = Math.max(2, Number(interval) || 3) * 1e3;
|
|
242
|
+
const deadline = Date.now() + (Number(expires_in) || 600) * 1e3;
|
|
243
|
+
let apiKey = "";
|
|
244
|
+
process.stderr.write(" Waiting for authorization");
|
|
245
|
+
while (Date.now() < deadline) {
|
|
246
|
+
await sleep(pollMs);
|
|
247
|
+
process.stderr.write(".");
|
|
248
|
+
try {
|
|
249
|
+
const res = await fetch(`${apiUrl}/api/v1/auth/device/poll`, {
|
|
250
|
+
method: "POST",
|
|
251
|
+
headers: { "Content-Type": "application/json" },
|
|
252
|
+
body: JSON.stringify({ device_code })
|
|
253
|
+
});
|
|
254
|
+
const data = await res.json().catch(() => ({}));
|
|
255
|
+
if (data?.status === "approved" && data?.api_key) {
|
|
256
|
+
apiKey = data.api_key;
|
|
257
|
+
process.stderr.write("\n");
|
|
258
|
+
console.log("");
|
|
259
|
+
const who = data?.user?.name || data?.user?.email;
|
|
260
|
+
const mail = data?.user?.email;
|
|
261
|
+
if (who) {
|
|
262
|
+
const suffix = mail && mail !== who ? ` ${c.dim}(${mail})${c.reset}` : "";
|
|
263
|
+
console.log(` ${c.green}\u2713${c.reset} ${c.bold}Logged in as ${who}${c.reset}${suffix}`);
|
|
264
|
+
} else {
|
|
265
|
+
console.log(` ${c.green}\u2713${c.reset} ${c.bold}Logged in${c.reset}`);
|
|
266
|
+
}
|
|
267
|
+
if (data?.project?.name) {
|
|
268
|
+
console.log(` ${c.dim}Project:${c.reset} ${data.project.name}`);
|
|
269
|
+
}
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
if (data?.status === "expired" || data?.status === "not_found") {
|
|
273
|
+
process.stderr.write("\n");
|
|
274
|
+
console.log(" Code expired. Run `login` again.");
|
|
275
|
+
process.exit(1);
|
|
276
|
+
}
|
|
277
|
+
} catch {
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
if (!apiKey) {
|
|
281
|
+
process.stderr.write("\n");
|
|
282
|
+
console.log(" Timed out waiting for authorization. Run `login` again.");
|
|
283
|
+
process.exit(1);
|
|
284
|
+
}
|
|
285
|
+
if (!install) {
|
|
286
|
+
console.log("");
|
|
287
|
+
console.log(" Logged in. Run `init --global` to enable system-wide enforcement.");
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
console.log("");
|
|
291
|
+
await runGlobalInstall({ apiKey, apiUrl });
|
|
292
|
+
console.log("");
|
|
293
|
+
console.log(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
294
|
+
console.log(" \u2502 You are logged in and protected. \u2502");
|
|
295
|
+
console.log(" \u2502 Every Claude Code session on this machine is \u2502");
|
|
296
|
+
console.log(" \u2502 now guarded by your cloud policy (OPA WASM). \u2502");
|
|
297
|
+
console.log(" \u2502 \u2502");
|
|
298
|
+
console.log(" \u2502 Undo: npx @solongate/proxy init --global \u2502");
|
|
299
|
+
console.log(" \u2502 --restore \u2502");
|
|
300
|
+
console.log(" \u2502 Manage policy: https://dashboard.solongate.com \u2502");
|
|
301
|
+
console.log(" \u2502 Restart Claude Code to apply. \u2502");
|
|
302
|
+
console.log(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
|
|
303
|
+
console.log("");
|
|
304
|
+
}
|
|
305
|
+
main().catch((err) => {
|
|
306
|
+
console.log(`Fatal: ${err instanceof Error ? err.message : String(err)}`);
|
|
307
|
+
process.exit(1);
|
|
308
|
+
});
|
package/dist/pull-push.js
CHANGED
|
@@ -7,7 +7,8 @@ import { resolve as resolve2 } from "path";
|
|
|
7
7
|
// src/config.ts
|
|
8
8
|
import { readFileSync, existsSync } from "fs";
|
|
9
9
|
import { appendFile } from "fs/promises";
|
|
10
|
-
import { resolve } from "path";
|
|
10
|
+
import { resolve, join } from "path";
|
|
11
|
+
import { homedir } from "os";
|
|
11
12
|
async function fetchCloudPolicy(apiKey, apiUrl, policyId) {
|
|
12
13
|
let resolvedId = policyId;
|
|
13
14
|
if (!resolvedId) {
|
package/hooks/audit.mjs
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { readFileSync, existsSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
8
8
|
import { resolve, join } from 'node:path';
|
|
9
|
+
import { homedir } from 'node:os';
|
|
9
10
|
|
|
10
11
|
function loadEnvKey(dir) {
|
|
11
12
|
try {
|
|
@@ -21,6 +22,18 @@ function loadEnvKey(dir) {
|
|
|
21
22
|
} catch { return {}; }
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
// Global cloud config written by `init --global` (~/.solongate/cloud-guard.json).
|
|
26
|
+
// A system-wide PostToolUse hook runs from any cwd, so a project .env can't be
|
|
27
|
+
// relied on for the key — read the absolute global config too.
|
|
28
|
+
function loadGlobalCloudConfig() {
|
|
29
|
+
try {
|
|
30
|
+
const p = resolve(homedir(), '.solongate', 'cloud-guard.json');
|
|
31
|
+
if (!existsSync(p)) return {};
|
|
32
|
+
const cfg = JSON.parse(readFileSync(p, 'utf-8'));
|
|
33
|
+
return (cfg && typeof cfg === 'object') ? cfg : {};
|
|
34
|
+
} catch { return {}; }
|
|
35
|
+
}
|
|
36
|
+
|
|
24
37
|
function guessPermission(toolName) {
|
|
25
38
|
const name = (toolName || '').toLowerCase();
|
|
26
39
|
if (name.includes('exec') || name.includes('shell') || name.includes('run') || name.includes('eval') || name === 'bash') return 'EXECUTE';
|
|
@@ -30,14 +43,16 @@ function guessPermission(toolName) {
|
|
|
30
43
|
}
|
|
31
44
|
|
|
32
45
|
const dotenv = loadEnvKey(process.cwd());
|
|
33
|
-
const
|
|
34
|
-
const
|
|
46
|
+
const globalCfg = loadGlobalCloudConfig();
|
|
47
|
+
const API_KEY = process.env.SOLONGATE_API_KEY || dotenv.SOLONGATE_API_KEY || globalCfg.apiKey || '';
|
|
48
|
+
const API_URL = process.env.SOLONGATE_API_URL || dotenv.SOLONGATE_API_URL || globalCfg.apiUrl || 'https://api.solongate.com';
|
|
35
49
|
|
|
36
50
|
// Agent identity from CLI args: node audit.mjs <agent_id> <agent_name>
|
|
37
51
|
const AGENT_ID = process.argv[2] || 'claude-code';
|
|
38
52
|
const AGENT_NAME = process.argv[3] || 'Claude Code';
|
|
39
53
|
|
|
40
|
-
|
|
54
|
+
// Accept both live and test keys (test keys are used for trials / sandboxes).
|
|
55
|
+
if (!API_KEY || !(API_KEY.startsWith('sg_live_') || API_KEY.startsWith('sg_test_'))) process.exit(0);
|
|
41
56
|
|
|
42
57
|
let input = '';
|
|
43
58
|
process.stdin.on('data', c => input += c);
|
|
@@ -45,12 +60,16 @@ process.stdin.on('end', async () => {
|
|
|
45
60
|
try {
|
|
46
61
|
const data = JSON.parse(input);
|
|
47
62
|
|
|
48
|
-
// Debug: append raw stdin to file for agent detection troubleshooting
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
63
|
+
// Debug: append raw stdin to file for agent detection troubleshooting.
|
|
64
|
+
// Opt-in (SOLONGATE_DEBUG) so a global hook doesn't litter every cwd.
|
|
65
|
+
if (process.env.SOLONGATE_DEBUG) {
|
|
66
|
+
try {
|
|
67
|
+
const debugLine = JSON.stringify({ ts: new Date().toISOString(), argv: process.argv.slice(2), tool_name: data.tool_name || data.toolName, agent_id: AGENT_ID }) + '\n';
|
|
68
|
+
const { appendFileSync: afs, mkdirSync: mds } = await import('node:fs');
|
|
69
|
+
mds(resolve('.solongate'), { recursive: true });
|
|
70
|
+
afs(resolve('.solongate', '.debug-audit-log'), debugLine);
|
|
71
|
+
} catch {}
|
|
72
|
+
}
|
|
54
73
|
|
|
55
74
|
let toolName = data.tool_name || data.toolName || '';
|
|
56
75
|
let toolInput = data.tool_input || data.toolInput || data.params || {};
|
|
@@ -84,10 +103,12 @@ process.stdin.on('end', async () => {
|
|
|
84
103
|
(toolOutput && typeof toolOutput === 'string' && toolOutput.includes('"error"')) ||
|
|
85
104
|
(resultJson && resultJson.includes('"error"'));
|
|
86
105
|
|
|
106
|
+
// Keep the full command/args for audit review (the dashboard shows them in
|
|
107
|
+
// the detail view). Only cap pathologically large values.
|
|
87
108
|
const argsSummary = {};
|
|
88
109
|
for (const [k, v] of Object.entries(toolInput)) {
|
|
89
|
-
argsSummary[k] = typeof v === 'string' && v.length >
|
|
90
|
-
? v.slice(0,
|
|
110
|
+
argsSummary[k] = typeof v === 'string' && v.length > 8000
|
|
111
|
+
? v.slice(0, 8000) + '…'
|
|
91
112
|
: v;
|
|
92
113
|
}
|
|
93
114
|
|