@solongate/proxy 0.82.39 → 0.82.40
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/commands/index.d.ts +1 -1
- package/dist/commands/index.js +5 -5
- package/dist/index.js +392 -729
- package/package.json +1 -1
- package/dist/pull-push.d.ts +0 -17
- package/dist/pull-push.js +0 -358
package/dist/index.js
CHANGED
|
@@ -32,361 +32,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
32
32
|
mod
|
|
33
33
|
));
|
|
34
34
|
|
|
35
|
-
// src/config.ts
|
|
36
|
-
import { readFileSync, existsSync } from "fs";
|
|
37
|
-
import { appendFile } from "fs/promises";
|
|
38
|
-
import { resolve, join } from "path";
|
|
39
|
-
import { homedir } from "os";
|
|
40
|
-
function loginCredential() {
|
|
41
|
-
try {
|
|
42
|
-
const p = join(homedir(), ".solongate", "cloud-guard.json");
|
|
43
|
-
if (!existsSync(p)) return {};
|
|
44
|
-
const c2 = JSON.parse(readFileSync(p, "utf-8"));
|
|
45
|
-
return c2 && typeof c2 === "object" ? c2 : {};
|
|
46
|
-
} catch {
|
|
47
|
-
return {};
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
async function fetchCloudPolicy(apiKey, apiUrl, policyId) {
|
|
51
|
-
let resolvedId = policyId;
|
|
52
|
-
if (!resolvedId) {
|
|
53
|
-
const listRes = await fetch(`${apiUrl}/api/v1/policies`, {
|
|
54
|
-
headers: { "Authorization": `Bearer ${apiKey}` },
|
|
55
|
-
signal: AbortSignal.timeout(1e4)
|
|
56
|
-
});
|
|
57
|
-
if (!listRes.ok) {
|
|
58
|
-
const body = await listRes.text().catch(() => "");
|
|
59
|
-
throw new Error(`Failed to list policies from cloud (${listRes.status}): ${body}`);
|
|
60
|
-
}
|
|
61
|
-
const listData = await listRes.json();
|
|
62
|
-
const policies = listData.policies ?? [];
|
|
63
|
-
if (policies.length === 0) {
|
|
64
|
-
throw new Error("No policies found in cloud. Create one in the dashboard first.");
|
|
65
|
-
}
|
|
66
|
-
resolvedId = policies[0].id;
|
|
67
|
-
}
|
|
68
|
-
const url = `${apiUrl}/api/v1/policies/${resolvedId}`;
|
|
69
|
-
const res = await fetch(url, {
|
|
70
|
-
headers: { "Authorization": `Bearer ${apiKey}` },
|
|
71
|
-
signal: AbortSignal.timeout(1e4)
|
|
72
|
-
});
|
|
73
|
-
if (!res.ok) {
|
|
74
|
-
const body = await res.text().catch(() => "");
|
|
75
|
-
throw new Error(`Failed to fetch policy from cloud (${res.status}): ${body}`);
|
|
76
|
-
}
|
|
77
|
-
const data = await res.json();
|
|
78
|
-
return {
|
|
79
|
-
id: String(data.id ?? "cloud"),
|
|
80
|
-
name: String(data.name ?? "Cloud Policy"),
|
|
81
|
-
description: String(data.description ?? ""),
|
|
82
|
-
version: Number(data._version ?? 1),
|
|
83
|
-
rules: data.rules ?? [],
|
|
84
|
-
createdAt: String(data._created_at ?? ""),
|
|
85
|
-
updatedAt: ""
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
async function fetchCloudPolicyWasm(apiKey, apiUrl, policyId) {
|
|
89
|
-
try {
|
|
90
|
-
const res = await fetch(`${apiUrl}/api/v1/policies/${policyId}/wasm`, {
|
|
91
|
-
headers: { "Authorization": `Bearer ${apiKey}` },
|
|
92
|
-
signal: AbortSignal.timeout(1e4)
|
|
93
|
-
});
|
|
94
|
-
if (!res.ok) return null;
|
|
95
|
-
return new Uint8Array(await res.arrayBuffer());
|
|
96
|
-
} catch {
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
async function sendAuditLog(apiKey, apiUrl, entry) {
|
|
101
|
-
const url = `${apiUrl}/api/v1/audit-logs`;
|
|
102
|
-
const body = JSON.stringify({
|
|
103
|
-
...entry,
|
|
104
|
-
agent_id: entry.agent_id,
|
|
105
|
-
agent_name: entry.agent_name
|
|
106
|
-
});
|
|
107
|
-
for (let attempt = 0; attempt < AUDIT_MAX_RETRIES; attempt++) {
|
|
108
|
-
try {
|
|
109
|
-
const res = await fetch(url, {
|
|
110
|
-
method: "POST",
|
|
111
|
-
headers: {
|
|
112
|
-
"Authorization": `Bearer ${apiKey}`,
|
|
113
|
-
"Content-Type": "application/json"
|
|
114
|
-
},
|
|
115
|
-
body,
|
|
116
|
-
signal: AbortSignal.timeout(5e3)
|
|
117
|
-
});
|
|
118
|
-
if (res.ok) return;
|
|
119
|
-
if (res.status >= 400 && res.status < 500) {
|
|
120
|
-
const resBody = await res.text().catch(() => "");
|
|
121
|
-
process.stderr.write(`[SolonGate] Audit log rejected (${res.status}): ${resBody}
|
|
122
|
-
`);
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
} catch {
|
|
126
|
-
}
|
|
127
|
-
if (attempt < AUDIT_MAX_RETRIES - 1) {
|
|
128
|
-
await new Promise((r) => setTimeout(r, 500 * Math.pow(2, attempt)));
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
process.stderr.write(`[SolonGate] Audit log failed after ${AUDIT_MAX_RETRIES} retries, saving to local backup.
|
|
132
|
-
`);
|
|
133
|
-
try {
|
|
134
|
-
const line = JSON.stringify({ ...entry, timestamp: (/* @__PURE__ */ new Date()).toISOString() }) + "\n";
|
|
135
|
-
appendFile(AUDIT_LOG_BACKUP_PATH, line, "utf-8").catch((err2) => {
|
|
136
|
-
process.stderr.write(`[SolonGate] Audit backup write error: ${err2 instanceof Error ? err2.message : String(err2)}
|
|
137
|
-
`);
|
|
138
|
-
});
|
|
139
|
-
} catch (err2) {
|
|
140
|
-
process.stderr.write(`[SolonGate] Audit backup write error: ${err2 instanceof Error ? err2.message : String(err2)}
|
|
141
|
-
`);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
function ensureCatchAllAllow(policy) {
|
|
145
|
-
const hasCatchAllAllow = policy.rules.some(
|
|
146
|
-
(r) => r.effect === "ALLOW" && r.toolPattern === "*" && r.enabled !== false
|
|
147
|
-
);
|
|
148
|
-
if (hasCatchAllAllow) return policy;
|
|
149
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
150
|
-
return {
|
|
151
|
-
...policy,
|
|
152
|
-
rules: [
|
|
153
|
-
...policy.rules,
|
|
154
|
-
{
|
|
155
|
-
id: "_solongate-catch-all-allow",
|
|
156
|
-
description: "Auto-added: allow everything not explicitly denied",
|
|
157
|
-
effect: "ALLOW",
|
|
158
|
-
priority: 9999,
|
|
159
|
-
toolPattern: "*",
|
|
160
|
-
minimumTrustLevel: "UNTRUSTED",
|
|
161
|
-
enabled: true,
|
|
162
|
-
createdAt: now,
|
|
163
|
-
updatedAt: now
|
|
164
|
-
}
|
|
165
|
-
]
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
function loadPolicy(source) {
|
|
169
|
-
let policy;
|
|
170
|
-
if (typeof source === "object") {
|
|
171
|
-
policy = source;
|
|
172
|
-
} else {
|
|
173
|
-
const filePath = resolve(source);
|
|
174
|
-
if (existsSync(filePath)) {
|
|
175
|
-
const content = readFileSync(filePath, "utf-8");
|
|
176
|
-
policy = JSON.parse(content);
|
|
177
|
-
} else {
|
|
178
|
-
return DEFAULT_POLICY;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
return ensureCatchAllAllow(policy);
|
|
182
|
-
}
|
|
183
|
-
function parseArgs(argv) {
|
|
184
|
-
const args = argv.slice(2);
|
|
185
|
-
let policySource;
|
|
186
|
-
let name = "solongate-proxy";
|
|
187
|
-
let verbose = false;
|
|
188
|
-
let rateLimitPerTool;
|
|
189
|
-
let globalRateLimit;
|
|
190
|
-
let configFile;
|
|
191
|
-
let apiKey;
|
|
192
|
-
let apiUrl;
|
|
193
|
-
let upstreamUrl;
|
|
194
|
-
let upstreamTransport;
|
|
195
|
-
let port;
|
|
196
|
-
let policyId;
|
|
197
|
-
let agentName;
|
|
198
|
-
let separatorIndex = args.indexOf("--");
|
|
199
|
-
const flags = separatorIndex >= 0 ? args.slice(0, separatorIndex) : args;
|
|
200
|
-
let upstreamArgs = separatorIndex >= 0 ? args.slice(separatorIndex + 1) : [];
|
|
201
|
-
for (let i = 0; i < flags.length; i++) {
|
|
202
|
-
if (!flags[i].startsWith("--")) {
|
|
203
|
-
if (upstreamArgs.length === 0) {
|
|
204
|
-
upstreamArgs.push(...flags.slice(i));
|
|
205
|
-
}
|
|
206
|
-
break;
|
|
207
|
-
}
|
|
208
|
-
switch (flags[i]) {
|
|
209
|
-
case "--policy":
|
|
210
|
-
policySource = flags[++i];
|
|
211
|
-
break;
|
|
212
|
-
case "--name":
|
|
213
|
-
name = flags[++i];
|
|
214
|
-
break;
|
|
215
|
-
case "--verbose":
|
|
216
|
-
verbose = true;
|
|
217
|
-
break;
|
|
218
|
-
case "--rate-limit":
|
|
219
|
-
rateLimitPerTool = parseInt(flags[++i], 10);
|
|
220
|
-
break;
|
|
221
|
-
case "--global-rate-limit":
|
|
222
|
-
globalRateLimit = parseInt(flags[++i], 10);
|
|
223
|
-
break;
|
|
224
|
-
case "--config":
|
|
225
|
-
configFile = flags[++i];
|
|
226
|
-
break;
|
|
227
|
-
case "--api-key":
|
|
228
|
-
apiKey = flags[++i];
|
|
229
|
-
break;
|
|
230
|
-
case "--api-url":
|
|
231
|
-
apiUrl = flags[++i];
|
|
232
|
-
break;
|
|
233
|
-
case "--upstream-url":
|
|
234
|
-
upstreamUrl = flags[++i];
|
|
235
|
-
break;
|
|
236
|
-
case "--upstream-transport":
|
|
237
|
-
upstreamTransport = flags[++i];
|
|
238
|
-
break;
|
|
239
|
-
case "--port":
|
|
240
|
-
port = parseInt(flags[++i], 10);
|
|
241
|
-
break;
|
|
242
|
-
case "--policy-id":
|
|
243
|
-
case "--id":
|
|
244
|
-
policyId = flags[++i];
|
|
245
|
-
break;
|
|
246
|
-
case "--agent-name":
|
|
247
|
-
agentName = flags[++i];
|
|
248
|
-
break;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
if (apiKey && /^\$\{.+\}$/.test(apiKey)) {
|
|
252
|
-
apiKey = void 0;
|
|
253
|
-
}
|
|
254
|
-
if (!apiKey) {
|
|
255
|
-
const dotenvPath = resolve(".env");
|
|
256
|
-
if (existsSync(dotenvPath)) {
|
|
257
|
-
const dotenvContent = readFileSync(dotenvPath, "utf-8");
|
|
258
|
-
const match = dotenvContent.match(/^SOLONGATE_API_KEY=(sg_(?:live|test)_\w+)/m);
|
|
259
|
-
if (match) apiKey = match[1];
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
if (!apiKey) {
|
|
263
|
-
const envKey = process.env.SOLONGATE_API_KEY;
|
|
264
|
-
if (envKey && !/^\$\{.+\}$/.test(envKey)) {
|
|
265
|
-
apiKey = envKey;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
if (!apiKey) {
|
|
269
|
-
const cred = loginCredential();
|
|
270
|
-
if (cred.apiKey) apiKey = cred.apiKey;
|
|
271
|
-
}
|
|
272
|
-
if (!apiKey) {
|
|
273
|
-
throw new Error(
|
|
274
|
-
"Not logged in. Run this once to get started:\n\n solongate\n\n then add your account from the Accounts panel.\n"
|
|
275
|
-
);
|
|
276
|
-
}
|
|
277
|
-
if (!apiKey.startsWith("sg_live_") && !apiKey.startsWith("sg_test_")) {
|
|
278
|
-
throw new Error(
|
|
279
|
-
"Invalid API key format. Keys must start with 'sg_live_' or 'sg_test_'.\nGet your API key at https://solongate.com\n"
|
|
280
|
-
);
|
|
281
|
-
}
|
|
282
|
-
const resolvedPolicyPath = policySource ? resolvePolicyPath(policySource) : null;
|
|
283
|
-
if (configFile) {
|
|
284
|
-
const filePath = resolve(configFile);
|
|
285
|
-
const content = readFileSync(filePath, "utf-8");
|
|
286
|
-
const fileConfig = JSON.parse(content);
|
|
287
|
-
if (!fileConfig.upstream) {
|
|
288
|
-
throw new Error('Config file must include "upstream" with at least "command" or "url"');
|
|
289
|
-
}
|
|
290
|
-
const cfgPolicySource = fileConfig.policy ?? policySource ?? "policy.json";
|
|
291
|
-
return {
|
|
292
|
-
upstream: fileConfig.upstream,
|
|
293
|
-
policy: loadPolicy(cfgPolicySource),
|
|
294
|
-
name: fileConfig.name ?? name,
|
|
295
|
-
verbose: fileConfig.verbose ?? verbose,
|
|
296
|
-
rateLimitPerTool: fileConfig.rateLimitPerTool ?? rateLimitPerTool,
|
|
297
|
-
globalRateLimit: fileConfig.globalRateLimit ?? globalRateLimit,
|
|
298
|
-
apiKey: apiKey ?? fileConfig.apiKey,
|
|
299
|
-
apiUrl: apiUrl ?? fileConfig.apiUrl,
|
|
300
|
-
port: port ?? fileConfig.port,
|
|
301
|
-
policyPath: resolvePolicyPath(cfgPolicySource) ?? void 0,
|
|
302
|
-
policyId: policyId ?? fileConfig.policyId,
|
|
303
|
-
agentName
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
if (upstreamUrl) {
|
|
307
|
-
const transport = upstreamTransport ?? (upstreamUrl.includes("/sse") ? "sse" : "http");
|
|
308
|
-
return {
|
|
309
|
-
upstream: {
|
|
310
|
-
transport,
|
|
311
|
-
command: "",
|
|
312
|
-
// not used for URL-based transports
|
|
313
|
-
url: upstreamUrl
|
|
314
|
-
},
|
|
315
|
-
policy: loadPolicy(policySource ?? "policy.json"),
|
|
316
|
-
name,
|
|
317
|
-
verbose,
|
|
318
|
-
rateLimitPerTool,
|
|
319
|
-
globalRateLimit,
|
|
320
|
-
apiKey,
|
|
321
|
-
apiUrl,
|
|
322
|
-
port,
|
|
323
|
-
policyPath: resolvedPolicyPath ?? void 0,
|
|
324
|
-
policyId,
|
|
325
|
-
agentName
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
if (upstreamArgs.length === 0) {
|
|
329
|
-
throw new Error(
|
|
330
|
-
"No upstream server command provided.\n\nIf you just want to get started, run:\n solongate\n"
|
|
331
|
-
);
|
|
332
|
-
}
|
|
333
|
-
const [command, ...commandArgs] = upstreamArgs;
|
|
334
|
-
return {
|
|
335
|
-
upstream: {
|
|
336
|
-
transport: upstreamTransport ?? "stdio",
|
|
337
|
-
command,
|
|
338
|
-
args: commandArgs,
|
|
339
|
-
env: { PATH: process.env.PATH ?? "", HOME: process.env.HOME ?? "", USERPROFILE: process.env.USERPROFILE ?? "" }
|
|
340
|
-
},
|
|
341
|
-
policy: loadPolicy(policySource ?? "policy.json"),
|
|
342
|
-
name,
|
|
343
|
-
verbose,
|
|
344
|
-
rateLimitPerTool,
|
|
345
|
-
globalRateLimit,
|
|
346
|
-
apiKey,
|
|
347
|
-
apiUrl,
|
|
348
|
-
port,
|
|
349
|
-
policyPath: resolvedPolicyPath ?? void 0,
|
|
350
|
-
policyId,
|
|
351
|
-
agentName
|
|
352
|
-
};
|
|
353
|
-
}
|
|
354
|
-
function resolvePolicyPath(source) {
|
|
355
|
-
const filePath = resolve(source);
|
|
356
|
-
if (existsSync(filePath)) return filePath;
|
|
357
|
-
return null;
|
|
358
|
-
}
|
|
359
|
-
var DEFAULT_API_URL, AUDIT_MAX_RETRIES, AUDIT_LOG_BACKUP_PATH, DEFAULT_POLICY;
|
|
360
|
-
var init_config = __esm({
|
|
361
|
-
"src/config.ts"() {
|
|
362
|
-
"use strict";
|
|
363
|
-
DEFAULT_API_URL = "https://api.solongate.com";
|
|
364
|
-
AUDIT_MAX_RETRIES = 3;
|
|
365
|
-
AUDIT_LOG_BACKUP_PATH = resolve(".solongate-audit-backup.jsonl");
|
|
366
|
-
DEFAULT_POLICY = {
|
|
367
|
-
id: "default",
|
|
368
|
-
name: "Default (Allow All)",
|
|
369
|
-
description: "Allows all tools by default. Add DENY rules from the dashboard to restrict.",
|
|
370
|
-
version: 1,
|
|
371
|
-
rules: [
|
|
372
|
-
{
|
|
373
|
-
id: "_default-allow-all",
|
|
374
|
-
description: "Allow all tools by default",
|
|
375
|
-
effect: "ALLOW",
|
|
376
|
-
priority: 9999,
|
|
377
|
-
toolPattern: "*",
|
|
378
|
-
minimumTrustLevel: "UNTRUSTED",
|
|
379
|
-
enabled: true,
|
|
380
|
-
createdAt: "",
|
|
381
|
-
updatedAt: ""
|
|
382
|
-
}
|
|
383
|
-
],
|
|
384
|
-
createdAt: "",
|
|
385
|
-
updatedAt: ""
|
|
386
|
-
};
|
|
387
|
-
}
|
|
388
|
-
});
|
|
389
|
-
|
|
390
35
|
// ../../node_modules/.pnpm/@open-policy-agent+opa-wasm@1.10.0/node_modules/@open-policy-agent/opa-wasm/src/builtins/json.js
|
|
391
36
|
var require_json = __commonJS({
|
|
392
37
|
"../../node_modules/.pnpm/@open-policy-agent+opa-wasm@1.10.0/node_modules/@open-policy-agent/opa-wasm/src/builtins/json.js"(exports, module) {
|
|
@@ -3398,12 +3043,12 @@ ${ctx.indent}`;
|
|
|
3398
3043
|
for (const {
|
|
3399
3044
|
format,
|
|
3400
3045
|
test,
|
|
3401
|
-
resolve:
|
|
3046
|
+
resolve: resolve6
|
|
3402
3047
|
} of tags) {
|
|
3403
3048
|
if (test) {
|
|
3404
3049
|
const match = str.match(test);
|
|
3405
3050
|
if (match) {
|
|
3406
|
-
let res =
|
|
3051
|
+
let res = resolve6.apply(null, match);
|
|
3407
3052
|
if (!(res instanceof Scalar)) res = new Scalar(res);
|
|
3408
3053
|
if (format) res.format = format;
|
|
3409
3054
|
return res;
|
|
@@ -6610,9 +6255,9 @@ async function fetchLatest() {
|
|
|
6610
6255
|
function spawnGlobalInstall(version) {
|
|
6611
6256
|
try {
|
|
6612
6257
|
mkdirSync3(join4(homedir2(), ".solongate"), { recursive: true });
|
|
6613
|
-
const
|
|
6258
|
+
const log3 = openSync(LOG_FILE, "a");
|
|
6614
6259
|
const p = spawn("npm", ["install", "-g", `${PKG}@${version}`], {
|
|
6615
|
-
stdio: ["ignore",
|
|
6260
|
+
stdio: ["ignore", log3, log3],
|
|
6616
6261
|
detached: true,
|
|
6617
6262
|
windowsHide: true,
|
|
6618
6263
|
shell: process.platform === "win32"
|
|
@@ -6626,7 +6271,7 @@ function spawnGlobalInstall(version) {
|
|
|
6626
6271
|
}
|
|
6627
6272
|
}
|
|
6628
6273
|
function runGlobalInstall(version) {
|
|
6629
|
-
return new Promise((
|
|
6274
|
+
return new Promise((resolve6) => {
|
|
6630
6275
|
try {
|
|
6631
6276
|
mkdirSync3(join4(homedir2(), ".solongate"), { recursive: true });
|
|
6632
6277
|
execFile(
|
|
@@ -6641,11 +6286,11 @@ ${stderr}
|
|
|
6641
6286
|
`, { flag: "a" });
|
|
6642
6287
|
} catch {
|
|
6643
6288
|
}
|
|
6644
|
-
|
|
6289
|
+
resolve6(!err2);
|
|
6645
6290
|
}
|
|
6646
6291
|
);
|
|
6647
6292
|
} catch {
|
|
6648
|
-
|
|
6293
|
+
resolve6(false);
|
|
6649
6294
|
}
|
|
6650
6295
|
});
|
|
6651
6296
|
}
|
|
@@ -6760,11 +6405,11 @@ function startLogsServerDaemon() {
|
|
|
6760
6405
|
}
|
|
6761
6406
|
try {
|
|
6762
6407
|
mkdirSync4(DIR, { recursive: true });
|
|
6763
|
-
const
|
|
6408
|
+
const log3 = openSync2(LOG_FILE2, "a");
|
|
6764
6409
|
const cli = join5(dirname2(fileURLToPath2(import.meta.url)), "index.js");
|
|
6765
6410
|
const p = spawn2(process.execPath, [cli, "logs-server"], {
|
|
6766
6411
|
detached: true,
|
|
6767
|
-
stdio: ["ignore",
|
|
6412
|
+
stdio: ["ignore", log3, log3],
|
|
6768
6413
|
windowsHide: true
|
|
6769
6414
|
});
|
|
6770
6415
|
p.on("error", () => {
|
|
@@ -6826,7 +6471,7 @@ function loadConfig() {
|
|
|
6826
6471
|
return cached;
|
|
6827
6472
|
}
|
|
6828
6473
|
var cached;
|
|
6829
|
-
var
|
|
6474
|
+
var init_config = __esm({
|
|
6830
6475
|
"src/tui/config.ts"() {
|
|
6831
6476
|
"use strict";
|
|
6832
6477
|
cached = null;
|
|
@@ -6881,7 +6526,7 @@ var accent, theme;
|
|
|
6881
6526
|
var init_theme = __esm({
|
|
6882
6527
|
"src/tui/theme.ts"() {
|
|
6883
6528
|
"use strict";
|
|
6884
|
-
|
|
6529
|
+
init_config();
|
|
6885
6530
|
accent = loadConfig().accent;
|
|
6886
6531
|
theme = {
|
|
6887
6532
|
accent: accent || "white",
|
|
@@ -7071,21 +6716,21 @@ import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, mkdirSy
|
|
|
7071
6716
|
import { resolve as resolve3, join as join8 } from "path";
|
|
7072
6717
|
import { homedir as homedir6 } from "os";
|
|
7073
6718
|
function listAccounts() {
|
|
7074
|
-
let
|
|
6719
|
+
let list5 = [];
|
|
7075
6720
|
try {
|
|
7076
6721
|
const raw = JSON.parse(readFileSync8(accountsFile(), "utf-8"));
|
|
7077
|
-
if (Array.isArray(raw))
|
|
6722
|
+
if (Array.isArray(raw)) list5 = raw.filter((a) => a && typeof a.apiKey === "string");
|
|
7078
6723
|
} catch {
|
|
7079
6724
|
}
|
|
7080
6725
|
const active2 = loginCredentialFile();
|
|
7081
|
-
if (active2.apiKey && !
|
|
7082
|
-
|
|
6726
|
+
if (active2.apiKey && !list5.some((a) => a.apiKey === active2.apiKey)) {
|
|
6727
|
+
list5.unshift({ apiKey: active2.apiKey, apiUrl: active2.apiUrl || DEFAULT_API_URL2 });
|
|
7083
6728
|
}
|
|
7084
|
-
return
|
|
6729
|
+
return list5;
|
|
7085
6730
|
}
|
|
7086
6731
|
function saveAccount(acc) {
|
|
7087
6732
|
try {
|
|
7088
|
-
const
|
|
6733
|
+
const list5 = (() => {
|
|
7089
6734
|
try {
|
|
7090
6735
|
const raw = JSON.parse(readFileSync8(accountsFile(), "utf-8"));
|
|
7091
6736
|
return Array.isArray(raw) ? raw.filter((a) => a && a.apiKey) : [];
|
|
@@ -7093,7 +6738,7 @@ function saveAccount(acc) {
|
|
|
7093
6738
|
return [];
|
|
7094
6739
|
}
|
|
7095
6740
|
})();
|
|
7096
|
-
const next =
|
|
6741
|
+
const next = list5.filter((a) => a.apiKey !== acc.apiKey);
|
|
7097
6742
|
next.unshift({ ...acc, addedAt: acc.addedAt ?? Date.now() });
|
|
7098
6743
|
mkdirSync5(join8(homedir6(), ".solongate"), { recursive: true });
|
|
7099
6744
|
writeFileSync6(accountsFile(), JSON.stringify(next, null, 2));
|
|
@@ -7102,7 +6747,7 @@ function saveAccount(acc) {
|
|
|
7102
6747
|
}
|
|
7103
6748
|
function removeAccount(apiKey) {
|
|
7104
6749
|
try {
|
|
7105
|
-
const
|
|
6750
|
+
const list5 = (() => {
|
|
7106
6751
|
try {
|
|
7107
6752
|
const raw = JSON.parse(readFileSync8(accountsFile(), "utf-8"));
|
|
7108
6753
|
return Array.isArray(raw) ? raw.filter((a) => a && a.apiKey) : [];
|
|
@@ -7110,7 +6755,7 @@ function removeAccount(apiKey) {
|
|
|
7110
6755
|
return [];
|
|
7111
6756
|
}
|
|
7112
6757
|
})();
|
|
7113
|
-
writeFileSync6(accountsFile(), JSON.stringify(
|
|
6758
|
+
writeFileSync6(accountsFile(), JSON.stringify(list5.filter((a) => a.apiKey !== apiKey), null, 2));
|
|
7114
6759
|
} catch {
|
|
7115
6760
|
}
|
|
7116
6761
|
}
|
|
@@ -7846,7 +7491,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7846
7491
|
const [localBuf, setLocalBuf] = useState2([]);
|
|
7847
7492
|
const [localOn, setLocalOn] = useState2(null);
|
|
7848
7493
|
const [ring, setRing] = useState2(null);
|
|
7849
|
-
const [
|
|
7494
|
+
const [log3, setLog] = useState2([]);
|
|
7850
7495
|
const [filter, setFilter] = useState2("all");
|
|
7851
7496
|
const [signal, setSignal] = useState2("none");
|
|
7852
7497
|
const [search, setSearch] = useState2("");
|
|
@@ -8449,9 +8094,9 @@ function LivePanel({ active: active2 }) {
|
|
|
8449
8094
|
const allHits = ins.dlpByPattern ?? [];
|
|
8450
8095
|
const maxHit = allHits[0]?.count ?? 1;
|
|
8451
8096
|
const L = [];
|
|
8452
|
-
const
|
|
8453
|
-
const blank = () =>
|
|
8454
|
-
const section = (label, m, note) =>
|
|
8097
|
+
const push = (el) => L.push(/* @__PURE__ */ jsx2(Box2, { children: el }, L.length));
|
|
8098
|
+
const blank = () => push(/* @__PURE__ */ jsx2(Text2, { children: " " }));
|
|
8099
|
+
const section = (label, m, note) => push(
|
|
8455
8100
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
8456
8101
|
/* @__PURE__ */ jsxs2(Text2, { bold: true, color: theme.accentBright, children: [
|
|
8457
8102
|
"\u258E",
|
|
@@ -8461,7 +8106,7 @@ function LivePanel({ active: active2 }) {
|
|
|
8461
8106
|
note ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: note }) : null
|
|
8462
8107
|
] })
|
|
8463
8108
|
);
|
|
8464
|
-
const kv = (k, v) =>
|
|
8109
|
+
const kv = (k, v) => push(
|
|
8465
8110
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
8466
8111
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: (" " + k).padEnd(16) }),
|
|
8467
8112
|
v
|
|
@@ -8470,18 +8115,18 @@ function LivePanel({ active: active2 }) {
|
|
|
8470
8115
|
section("RATELIMIT", rl?.mode, rl?.mode === "block" ? "over-limit calls are DENIED" : rl?.mode === "detect" ? "over-limit calls are observed & flagged rl:yes" : "no limit enforcement");
|
|
8471
8116
|
kv("limits", /* @__PURE__ */ jsx2(Text2, { children: rl?.perMinute || rl?.perHour || rl?.perDay ? `${rl?.perMinute || "\u2014"}/min \xB7 ${rl?.perHour || "\u2014"}/hour \xB7 ${rl?.perDay || "\u2014"}/day` : "none configured" }));
|
|
8472
8117
|
kv("load now", /* @__PURE__ */ jsx2(Text2, { children: `${minuteNow} calls in the last 60s` }));
|
|
8473
|
-
if (rl?.perMinute)
|
|
8118
|
+
if (rl?.perMinute) push(/* @__PURE__ */ jsx2(HBar, { label: " load/min", value: minuteNow, max: rl.perMinute, width: barW, color: minuteNow > rl.perMinute ? theme.bad : theme.accent }));
|
|
8474
8119
|
kv("bursts", /* @__PURE__ */ jsx2(Text2, { color: burstsInBuf ? theme.warn : theme.dim, children: `${burstsInBuf} flagged in the live buffer` }));
|
|
8475
8120
|
blank();
|
|
8476
8121
|
section("DLP", dl?.mode, dl?.mode === "block" ? "secrets in arguments are DENIED + redacted" : dl?.mode === "detect" ? "secrets observed & flagged dlp:yes, output redacted" : "no secret scanning");
|
|
8477
8122
|
kv("hits", /* @__PURE__ */ jsx2(Text2, { color: dlpInBuf ? theme.bad : theme.dim, children: `${dlpInBuf} flagged in the live buffer` }));
|
|
8478
8123
|
const builtin = dl?.patterns ?? [];
|
|
8479
8124
|
kv("builtin", /* @__PURE__ */ jsx2(Text2, { children: builtin.length ? `${builtin.length} patterns enabled` : "none enabled" }));
|
|
8480
|
-
for (const line of wrapLines(builtin.join(" \xB7 "), Math.max(20, innerW - 18)))
|
|
8125
|
+
for (const line of wrapLines(builtin.join(" \xB7 "), Math.max(20, innerW - 18))) push(/* @__PURE__ */ jsx2(Text2, { wrap: "truncate", color: theme.dim, children: " " + line }));
|
|
8481
8126
|
const custom = dl?.custom ?? [];
|
|
8482
8127
|
kv("custom", /* @__PURE__ */ jsx2(Text2, { children: custom.length ? `${custom.length} patterns` : "none" }));
|
|
8483
8128
|
for (const c2 of custom)
|
|
8484
|
-
|
|
8129
|
+
push(
|
|
8485
8130
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
8486
8131
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " " }),
|
|
8487
8132
|
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate2(c2.name ?? "custom", 24).padEnd(25) }),
|
|
@@ -8489,12 +8134,12 @@ function LivePanel({ active: active2 }) {
|
|
|
8489
8134
|
] })
|
|
8490
8135
|
);
|
|
8491
8136
|
kv("pattern hits", /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: allHits.length ? "last 7 days" : "none in the last 7 days" }));
|
|
8492
|
-
for (const h of allHits)
|
|
8137
|
+
for (const h of allHits) push(/* @__PURE__ */ jsx2(HBar, { label: " " + truncate2(h.pattern, 10), value: h.count, max: maxHit, width: barW, color: theme.bad }));
|
|
8493
8138
|
blank();
|
|
8494
8139
|
section("GHOST", gh?.mode, gh?.mode === "on" ? "matching paths are hidden from agents" : "no hidden paths");
|
|
8495
8140
|
const ghostPats = gh?.patterns ?? [];
|
|
8496
8141
|
kv("patterns", /* @__PURE__ */ jsx2(Text2, { children: ghostPats.length ? `${ghostPats.length}` : "none" }));
|
|
8497
|
-
for (const line of wrapLines(ghostPats.join(" \xB7 "), Math.max(20, innerW - 18)))
|
|
8142
|
+
for (const line of wrapLines(ghostPats.join(" \xB7 "), Math.max(20, innerW - 18))) push(/* @__PURE__ */ jsx2(Text2, { wrap: "truncate", color: theme.dim, children: " " + line }));
|
|
8498
8143
|
blank();
|
|
8499
8144
|
section("GUARD", guard.data ? guard.data.up_to_date ? "ok" : "stale" : "?", "the PreToolUse hook enforcing all of the above");
|
|
8500
8145
|
kv(
|
|
@@ -8690,7 +8335,7 @@ function LivePanel({ active: active2 }) {
|
|
|
8690
8335
|
] }),
|
|
8691
8336
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, overflow: "hidden", children: [
|
|
8692
8337
|
/* @__PURE__ */ jsx2(PaneTitle, { label: "EVENT LOG", extra: "system heartbeat", width: colW }),
|
|
8693
|
-
|
|
8338
|
+
log3.slice(-(colH - 1)).map((l, i) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
8694
8339
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
8695
8340
|
hhmmss(l.ts),
|
|
8696
8341
|
" "
|
|
@@ -8733,7 +8378,7 @@ var init_Live = __esm({
|
|
|
8733
8378
|
"use strict";
|
|
8734
8379
|
init_local_log();
|
|
8735
8380
|
init_api_client();
|
|
8736
|
-
|
|
8381
|
+
init_config();
|
|
8737
8382
|
init_notify();
|
|
8738
8383
|
init_hooks();
|
|
8739
8384
|
init_components();
|
|
@@ -8807,8 +8452,8 @@ function requestDryRun(policyId) {
|
|
|
8807
8452
|
pendingPolicyId = policyId;
|
|
8808
8453
|
}
|
|
8809
8454
|
function DryRunPanel({ focused }) {
|
|
8810
|
-
const
|
|
8811
|
-
const policies =
|
|
8455
|
+
const list5 = useLoader(() => api.policies.list());
|
|
8456
|
+
const policies = list5.data?.policies ?? [];
|
|
8812
8457
|
const [pi, setPi] = useState3(0);
|
|
8813
8458
|
const [limit, setLimit] = useState3(DEFAULT_LIMIT);
|
|
8814
8459
|
const [editLogs, setEditLogs] = useState3(null);
|
|
@@ -8863,7 +8508,7 @@ function DryRunPanel({ focused }) {
|
|
|
8863
8508
|
const w = Math.max(40, cols);
|
|
8864
8509
|
const lines = [];
|
|
8865
8510
|
const head = (t) => lines.push(/* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.accentBright, children: t }, `h${lines.length}`));
|
|
8866
|
-
const
|
|
8511
|
+
const dim2 = (t) => lines.push(/* @__PURE__ */ jsx3(Text3, { color: theme.dim, wrap: "truncate", children: t }, `d${lines.length}`));
|
|
8867
8512
|
const row = (t) => lines.push(/* @__PURE__ */ jsx3(Text3, { wrap: "truncate", children: t }, `r${lines.length}`));
|
|
8868
8513
|
const blank = () => lines.push(/* @__PURE__ */ jsx3(Text3, { children: " " }, `b${lines.length}`));
|
|
8869
8514
|
if (res) {
|
|
@@ -8889,7 +8534,7 @@ function DryRunPanel({ focused }) {
|
|
|
8889
8534
|
blank();
|
|
8890
8535
|
if (res.per_rule.length) {
|
|
8891
8536
|
head("Per-rule impact");
|
|
8892
|
-
|
|
8537
|
+
dim2(` ${"RULE".padEnd(38)}${"MATCHED".padEnd(9)}${"%".padEnd(8)}${"NEW-BLK".padEnd(9)}NEW-ALW`);
|
|
8893
8538
|
for (const r of res.per_rule.slice(0, 30)) {
|
|
8894
8539
|
row(` ${truncate2(r.rule_id, 36).padEnd(38)}${String(r.matched).padEnd(9)}${pct(r.matched, s.evaluated).padEnd(8)}${String(r.newly_blocked).padEnd(9)}${r.newly_allowed}`);
|
|
8895
8540
|
}
|
|
@@ -8897,7 +8542,7 @@ function DryRunPanel({ focused }) {
|
|
|
8897
8542
|
}
|
|
8898
8543
|
if (res.per_agent.length) {
|
|
8899
8544
|
head("Per-agent impact");
|
|
8900
|
-
|
|
8545
|
+
dim2(` ${"AGENT".padEnd(24)}${"EVALUATED".padEnd(11)}${"CHANGED".padEnd(9)}${"NEW-BLK".padEnd(9)}NEW-ALW`);
|
|
8901
8546
|
for (const a of res.per_agent) {
|
|
8902
8547
|
row(` ${truncate2(a.agent || "-", 22).padEnd(24)}${String(a.evaluated).padEnd(11)}${String(a.changed).padEnd(9)}${String(a.newly_blocked).padEnd(9)}${a.newly_allowed}`);
|
|
8903
8548
|
}
|
|
@@ -8905,7 +8550,7 @@ function DryRunPanel({ focused }) {
|
|
|
8905
8550
|
}
|
|
8906
8551
|
if (res.per_tool.length) {
|
|
8907
8552
|
head("Per-tool breakdown");
|
|
8908
|
-
|
|
8553
|
+
dim2(` ${"TOOL".padEnd(24)}${"EVALUATED".padEnd(11)}${"CHANGED".padEnd(9)}${"NEW-BLK".padEnd(9)}NEW-ALW`);
|
|
8909
8554
|
for (const t of res.per_tool) {
|
|
8910
8555
|
row(` ${truncate2(t.tool || "-", 22).padEnd(24)}${String(t.evaluated).padEnd(11)}${String(t.changed).padEnd(9)}${String(t.newly_blocked).padEnd(9)}${t.newly_allowed}`);
|
|
8911
8556
|
}
|
|
@@ -8935,7 +8580,7 @@ function DryRunPanel({ focused }) {
|
|
|
8935
8580
|
const win = lines.slice(start, start + budget);
|
|
8936
8581
|
const above = start;
|
|
8937
8582
|
const below = Math.max(0, lines.length - (start + budget));
|
|
8938
|
-
return /* @__PURE__ */ jsx3(DataView, { loading:
|
|
8583
|
+
return /* @__PURE__ */ jsx3(DataView, { loading: list5.loading && !list5.data, error: list5.error, children: /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
8939
8584
|
/* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.accentBright, children: "Policy Dry Run" }),
|
|
8940
8585
|
/* @__PURE__ */ jsxs3(Text3, { wrap: "truncate", children: [
|
|
8941
8586
|
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: " policy " }),
|
|
@@ -9014,8 +8659,8 @@ function ruleSummary(r) {
|
|
|
9014
8659
|
return bits.join(" ");
|
|
9015
8660
|
}
|
|
9016
8661
|
function PoliciesPanel({ focused }) {
|
|
9017
|
-
const
|
|
9018
|
-
const policies =
|
|
8662
|
+
const list5 = useLoader(() => api.policies.list());
|
|
8663
|
+
const policies = list5.data?.policies ?? [];
|
|
9019
8664
|
const activeQ = useLoader(() => api.policies.active());
|
|
9020
8665
|
const activeId = activeQ.data?.policy?.id ?? null;
|
|
9021
8666
|
const activeBy = activeQ.data?.matched_by;
|
|
@@ -9041,7 +8686,7 @@ function PoliciesPanel({ focused }) {
|
|
|
9041
8686
|
}
|
|
9042
8687
|
}, [detail.data]);
|
|
9043
8688
|
usePoll(() => {
|
|
9044
|
-
|
|
8689
|
+
list5.reloadQuiet();
|
|
9045
8690
|
activeQ.reloadQuiet();
|
|
9046
8691
|
}, 3e3);
|
|
9047
8692
|
const mutate = (nextRules, nextMode = mode) => {
|
|
@@ -9065,7 +8710,7 @@ function PoliciesPanel({ focused }) {
|
|
|
9065
8710
|
setRules(res.rules);
|
|
9066
8711
|
setDirty(false);
|
|
9067
8712
|
setStatus("\u2713 Saved");
|
|
9068
|
-
|
|
8713
|
+
list5.reload();
|
|
9069
8714
|
} catch (e) {
|
|
9070
8715
|
setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
|
|
9071
8716
|
}
|
|
@@ -9086,14 +8731,14 @@ function PoliciesPanel({ focused }) {
|
|
|
9086
8731
|
setStatus("Creating\u2026");
|
|
9087
8732
|
void api.policies.create({ id: `policy-${Date.now()}`, name: n, rules: [], mode: "denylist" }).then(() => {
|
|
9088
8733
|
setStatus(`\u2713 created "${n}" - open it and press n to add rules`);
|
|
9089
|
-
|
|
8734
|
+
list5.reload();
|
|
9090
8735
|
}).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
|
|
9091
8736
|
}
|
|
9092
8737
|
useInput3(
|
|
9093
8738
|
(input, key) => {
|
|
9094
8739
|
if (creating !== null) return;
|
|
9095
8740
|
if (key.ctrl && input === "r" && !editing) {
|
|
9096
|
-
|
|
8741
|
+
list5.reload();
|
|
9097
8742
|
activeQ.reload();
|
|
9098
8743
|
detail.reload();
|
|
9099
8744
|
setStatus(`\u27F3 refreshed ${(/* @__PURE__ */ new Date()).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: false })}`);
|
|
@@ -9189,7 +8834,7 @@ function PoliciesPanel({ focused }) {
|
|
|
9189
8834
|
const lWin = policies.slice(lStart, lStart + lBudget);
|
|
9190
8835
|
const lAbove = lStart;
|
|
9191
8836
|
const lBelow = Math.max(0, policies.length - (lStart + lBudget));
|
|
9192
|
-
return /* @__PURE__ */ jsx4(DataView, { loading:
|
|
8837
|
+
return /* @__PURE__ */ jsx4(DataView, { loading: list5.loading && !list5.data, error: list5.error, empty: !!list5.data && policies.length === 0, emptyText: "No policies.", children: /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
9193
8838
|
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
9194
8839
|
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "active: " }),
|
|
9195
8840
|
activeQ.loading && !activeQ.data ? /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "\u2026" }) : activeName ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
@@ -11418,9 +11063,9 @@ function App() {
|
|
|
11418
11063
|
viewAccount(accounts[(acctIdx + 1) % accounts.length]);
|
|
11419
11064
|
};
|
|
11420
11065
|
const syncAccounts = () => {
|
|
11421
|
-
const
|
|
11422
|
-
setAccounts(
|
|
11423
|
-
const next =
|
|
11066
|
+
const list5 = listAccounts();
|
|
11067
|
+
setAccounts(list5);
|
|
11068
|
+
const next = list5.find((a) => a.apiKey === viewKey) ?? list5[0];
|
|
11424
11069
|
setViewCredentials(next ? { apiKey: next.apiKey, apiUrl: next.apiUrl } : null);
|
|
11425
11070
|
setViewKey(next?.apiKey);
|
|
11426
11071
|
setViewNonce((n) => n + 1);
|
|
@@ -12196,7 +11841,7 @@ async function runAgents(argv) {
|
|
|
12196
11841
|
const res = await api.agents.live({ limit: flagNum(flags, "limit"), includeDeactivated: flagBool(flags, "all") });
|
|
12197
11842
|
if (json) return printJson(res), 0;
|
|
12198
11843
|
err("");
|
|
12199
|
-
err(`
|
|
11844
|
+
err(` Sessions ${green(String(res.counts.active))} active ${yellow(String(res.counts.idle))} idle ${dim(String(res.counts.deactivated) + " off")}`);
|
|
12200
11845
|
if (!res.agents.length) return err(dim(" No agent sessions.")), 0;
|
|
12201
11846
|
table(
|
|
12202
11847
|
["STATUS", "AGENT", "CALLS", "DENY", "DLP", "TRUST", "CHARACTER"],
|
|
@@ -12216,7 +11861,7 @@ async function runAgent(argv) {
|
|
|
12216
11861
|
const { positionals, flags } = parse(argv);
|
|
12217
11862
|
const json = flagBool(flags, "json");
|
|
12218
11863
|
const id = positionals[0];
|
|
12219
|
-
if (!id) return err(" Usage: solongate
|
|
11864
|
+
if (!id) return err(" Usage: solongate session <id> [--json]"), 1;
|
|
12220
11865
|
const a = await api.agents.get(id);
|
|
12221
11866
|
if (json) return printJson(a), 0;
|
|
12222
11867
|
err("");
|
|
@@ -12571,9 +12216,9 @@ async function dispatch(command, argv) {
|
|
|
12571
12216
|
return run4(argv);
|
|
12572
12217
|
case "audit":
|
|
12573
12218
|
return run5(argv);
|
|
12574
|
-
case "
|
|
12219
|
+
case "sessions":
|
|
12575
12220
|
return runAgents(argv);
|
|
12576
|
-
case "
|
|
12221
|
+
case "session":
|
|
12577
12222
|
return runAgent(argv);
|
|
12578
12223
|
case "doctor":
|
|
12579
12224
|
return run6(argv);
|
|
@@ -12621,7 +12266,7 @@ var init_commands = __esm({
|
|
|
12621
12266
|
init_watch();
|
|
12622
12267
|
init_alerts();
|
|
12623
12268
|
init_webhooks();
|
|
12624
|
-
COMMAND_NAMES = ["policy", "ratelimit", "dlp", "stats", "audit", "
|
|
12269
|
+
COMMAND_NAMES = ["policy", "ratelimit", "dlp", "stats", "audit", "sessions", "session", "doctor", "watch", "alerts", "webhooks"];
|
|
12625
12270
|
}
|
|
12626
12271
|
});
|
|
12627
12272
|
|
|
@@ -12829,331 +12474,361 @@ var init_logs_server = __esm({
|
|
|
12829
12474
|
LOG_FILENAME = "solongate-audit.jsonl";
|
|
12830
12475
|
DEFAULT_PORT = 8788;
|
|
12831
12476
|
}
|
|
12832
|
-
});
|
|
12833
|
-
|
|
12834
|
-
// src/
|
|
12835
|
-
|
|
12836
|
-
import {
|
|
12837
|
-
import {
|
|
12838
|
-
|
|
12839
|
-
|
|
12840
|
-
|
|
12841
|
-
|
|
12842
|
-
|
|
12843
|
-
|
|
12844
|
-
|
|
12845
|
-
|
|
12846
|
-
|
|
12847
|
-
|
|
12848
|
-
|
|
12849
|
-
|
|
12850
|
-
|
|
12851
|
-
|
|
12852
|
-
|
|
12853
|
-
|
|
12477
|
+
});
|
|
12478
|
+
|
|
12479
|
+
// src/index.ts
|
|
12480
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
12481
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
12482
|
+
import { dirname as dirname4, join as join16 } from "path";
|
|
12483
|
+
|
|
12484
|
+
// src/config.ts
|
|
12485
|
+
import { readFileSync, existsSync } from "fs";
|
|
12486
|
+
import { appendFile } from "fs/promises";
|
|
12487
|
+
import { resolve, join } from "path";
|
|
12488
|
+
import { homedir } from "os";
|
|
12489
|
+
function loginCredential() {
|
|
12490
|
+
try {
|
|
12491
|
+
const p = join(homedir(), ".solongate", "cloud-guard.json");
|
|
12492
|
+
if (!existsSync(p)) return {};
|
|
12493
|
+
const c2 = JSON.parse(readFileSync(p, "utf-8"));
|
|
12494
|
+
return c2 && typeof c2 === "object" ? c2 : {};
|
|
12495
|
+
} catch {
|
|
12496
|
+
return {};
|
|
12497
|
+
}
|
|
12498
|
+
}
|
|
12499
|
+
var DEFAULT_API_URL = "https://api.solongate.com";
|
|
12500
|
+
async function fetchCloudPolicy(apiKey, apiUrl, policyId) {
|
|
12501
|
+
let resolvedId = policyId;
|
|
12502
|
+
if (!resolvedId) {
|
|
12503
|
+
const listRes = await fetch(`${apiUrl}/api/v1/policies`, {
|
|
12504
|
+
headers: { "Authorization": `Bearer ${apiKey}` },
|
|
12505
|
+
signal: AbortSignal.timeout(1e4)
|
|
12506
|
+
});
|
|
12507
|
+
if (!listRes.ok) {
|
|
12508
|
+
const body = await listRes.text().catch(() => "");
|
|
12509
|
+
throw new Error(`Failed to list policies from cloud (${listRes.status}): ${body}`);
|
|
12510
|
+
}
|
|
12511
|
+
const listData = await listRes.json();
|
|
12512
|
+
const policies = listData.policies ?? [];
|
|
12513
|
+
if (policies.length === 0) {
|
|
12514
|
+
throw new Error("No policies found in cloud. Create one in the dashboard first.");
|
|
12515
|
+
}
|
|
12516
|
+
resolvedId = policies[0].id;
|
|
12517
|
+
}
|
|
12518
|
+
const url = `${apiUrl}/api/v1/policies/${resolvedId}`;
|
|
12519
|
+
const res = await fetch(url, {
|
|
12520
|
+
headers: { "Authorization": `Bearer ${apiKey}` },
|
|
12521
|
+
signal: AbortSignal.timeout(1e4)
|
|
12522
|
+
});
|
|
12523
|
+
if (!res.ok) {
|
|
12524
|
+
const body = await res.text().catch(() => "");
|
|
12525
|
+
throw new Error(`Failed to fetch policy from cloud (${res.status}): ${body}`);
|
|
12526
|
+
}
|
|
12527
|
+
const data = await res.json();
|
|
12528
|
+
return {
|
|
12529
|
+
id: String(data.id ?? "cloud"),
|
|
12530
|
+
name: String(data.name ?? "Cloud Policy"),
|
|
12531
|
+
description: String(data.description ?? ""),
|
|
12532
|
+
version: Number(data._version ?? 1),
|
|
12533
|
+
rules: data.rules ?? [],
|
|
12534
|
+
createdAt: String(data._created_at ?? ""),
|
|
12535
|
+
updatedAt: ""
|
|
12536
|
+
};
|
|
12537
|
+
}
|
|
12538
|
+
async function fetchCloudPolicyWasm(apiKey, apiUrl, policyId) {
|
|
12539
|
+
try {
|
|
12540
|
+
const res = await fetch(`${apiUrl}/api/v1/policies/${policyId}/wasm`, {
|
|
12541
|
+
headers: { "Authorization": `Bearer ${apiKey}` },
|
|
12542
|
+
signal: AbortSignal.timeout(1e4)
|
|
12543
|
+
});
|
|
12544
|
+
if (!res.ok) return null;
|
|
12545
|
+
return new Uint8Array(await res.arrayBuffer());
|
|
12546
|
+
} catch {
|
|
12547
|
+
return null;
|
|
12548
|
+
}
|
|
12549
|
+
}
|
|
12550
|
+
var AUDIT_MAX_RETRIES = 3;
|
|
12551
|
+
var AUDIT_LOG_BACKUP_PATH = resolve(".solongate-audit-backup.jsonl");
|
|
12552
|
+
async function sendAuditLog(apiKey, apiUrl, entry) {
|
|
12553
|
+
const url = `${apiUrl}/api/v1/audit-logs`;
|
|
12554
|
+
const body = JSON.stringify({
|
|
12555
|
+
...entry,
|
|
12556
|
+
agent_id: entry.agent_id,
|
|
12557
|
+
agent_name: entry.agent_name
|
|
12558
|
+
});
|
|
12559
|
+
for (let attempt = 0; attempt < AUDIT_MAX_RETRIES; attempt++) {
|
|
12560
|
+
try {
|
|
12561
|
+
const res = await fetch(url, {
|
|
12562
|
+
method: "POST",
|
|
12563
|
+
headers: {
|
|
12564
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
12565
|
+
"Content-Type": "application/json"
|
|
12566
|
+
},
|
|
12567
|
+
body,
|
|
12568
|
+
signal: AbortSignal.timeout(5e3)
|
|
12569
|
+
});
|
|
12570
|
+
if (res.ok) return;
|
|
12571
|
+
if (res.status >= 400 && res.status < 500) {
|
|
12572
|
+
const resBody = await res.text().catch(() => "");
|
|
12573
|
+
process.stderr.write(`[SolonGate] Audit log rejected (${res.status}): ${resBody}
|
|
12574
|
+
`);
|
|
12575
|
+
return;
|
|
12576
|
+
}
|
|
12577
|
+
} catch {
|
|
12578
|
+
}
|
|
12579
|
+
if (attempt < AUDIT_MAX_RETRIES - 1) {
|
|
12580
|
+
await new Promise((r) => setTimeout(r, 500 * Math.pow(2, attempt)));
|
|
12581
|
+
}
|
|
12582
|
+
}
|
|
12583
|
+
process.stderr.write(`[SolonGate] Audit log failed after ${AUDIT_MAX_RETRIES} retries, saving to local backup.
|
|
12584
|
+
`);
|
|
12585
|
+
try {
|
|
12586
|
+
const line = JSON.stringify({ ...entry, timestamp: (/* @__PURE__ */ new Date()).toISOString() }) + "\n";
|
|
12587
|
+
appendFile(AUDIT_LOG_BACKUP_PATH, line, "utf-8").catch((err2) => {
|
|
12588
|
+
process.stderr.write(`[SolonGate] Audit backup write error: ${err2 instanceof Error ? err2.message : String(err2)}
|
|
12589
|
+
`);
|
|
12590
|
+
});
|
|
12591
|
+
} catch (err2) {
|
|
12592
|
+
process.stderr.write(`[SolonGate] Audit backup write error: ${err2 instanceof Error ? err2.message : String(err2)}
|
|
12593
|
+
`);
|
|
12594
|
+
}
|
|
12595
|
+
}
|
|
12596
|
+
var DEFAULT_POLICY = {
|
|
12597
|
+
id: "default",
|
|
12598
|
+
name: "Default (Allow All)",
|
|
12599
|
+
description: "Allows all tools by default. Add DENY rules from the dashboard to restrict.",
|
|
12600
|
+
version: 1,
|
|
12601
|
+
rules: [
|
|
12602
|
+
{
|
|
12603
|
+
id: "_default-allow-all",
|
|
12604
|
+
description: "Allow all tools by default",
|
|
12605
|
+
effect: "ALLOW",
|
|
12606
|
+
priority: 9999,
|
|
12607
|
+
toolPattern: "*",
|
|
12608
|
+
minimumTrustLevel: "UNTRUSTED",
|
|
12609
|
+
enabled: true,
|
|
12610
|
+
createdAt: "",
|
|
12611
|
+
updatedAt: ""
|
|
12612
|
+
}
|
|
12613
|
+
],
|
|
12614
|
+
createdAt: "",
|
|
12615
|
+
updatedAt: ""
|
|
12616
|
+
};
|
|
12617
|
+
function ensureCatchAllAllow(policy) {
|
|
12618
|
+
const hasCatchAllAllow = policy.rules.some(
|
|
12619
|
+
(r) => r.effect === "ALLOW" && r.toolPattern === "*" && r.enabled !== false
|
|
12620
|
+
);
|
|
12621
|
+
if (hasCatchAllAllow) return policy;
|
|
12622
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
12623
|
+
return {
|
|
12624
|
+
...policy,
|
|
12625
|
+
rules: [
|
|
12626
|
+
...policy.rules,
|
|
12627
|
+
{
|
|
12628
|
+
id: "_solongate-catch-all-allow",
|
|
12629
|
+
description: "Auto-added: allow everything not explicitly denied",
|
|
12630
|
+
effect: "ALLOW",
|
|
12631
|
+
priority: 9999,
|
|
12632
|
+
toolPattern: "*",
|
|
12633
|
+
minimumTrustLevel: "UNTRUSTED",
|
|
12634
|
+
enabled: true,
|
|
12635
|
+
createdAt: now,
|
|
12636
|
+
updatedAt: now
|
|
12854
12637
|
}
|
|
12638
|
+
]
|
|
12639
|
+
};
|
|
12640
|
+
}
|
|
12641
|
+
function loadPolicy(source) {
|
|
12642
|
+
let policy;
|
|
12643
|
+
if (typeof source === "object") {
|
|
12644
|
+
policy = source;
|
|
12645
|
+
} else {
|
|
12646
|
+
const filePath = resolve(source);
|
|
12647
|
+
if (existsSync(filePath)) {
|
|
12648
|
+
const content = readFileSync(filePath, "utf-8");
|
|
12649
|
+
policy = JSON.parse(content);
|
|
12650
|
+
} else {
|
|
12651
|
+
return DEFAULT_POLICY;
|
|
12855
12652
|
}
|
|
12856
|
-
} catch {
|
|
12857
12653
|
}
|
|
12654
|
+
return ensureCatchAllAllow(policy);
|
|
12858
12655
|
}
|
|
12859
|
-
function
|
|
12860
|
-
|
|
12861
|
-
|
|
12862
|
-
|
|
12863
|
-
let
|
|
12864
|
-
let
|
|
12656
|
+
function parseArgs(argv) {
|
|
12657
|
+
const args = argv.slice(2);
|
|
12658
|
+
let policySource;
|
|
12659
|
+
let name = "solongate-proxy";
|
|
12660
|
+
let verbose = false;
|
|
12661
|
+
let rateLimitPerTool;
|
|
12662
|
+
let globalRateLimit;
|
|
12663
|
+
let configFile;
|
|
12664
|
+
let apiKey;
|
|
12665
|
+
let apiUrl;
|
|
12666
|
+
let upstreamUrl;
|
|
12667
|
+
let upstreamTransport;
|
|
12668
|
+
let port;
|
|
12865
12669
|
let policyId;
|
|
12866
|
-
|
|
12867
|
-
|
|
12670
|
+
let agentName;
|
|
12671
|
+
let separatorIndex = args.indexOf("--");
|
|
12672
|
+
const flags = separatorIndex >= 0 ? args.slice(0, separatorIndex) : args;
|
|
12673
|
+
let upstreamArgs = separatorIndex >= 0 ? args.slice(separatorIndex + 1) : [];
|
|
12674
|
+
for (let i = 0; i < flags.length; i++) {
|
|
12675
|
+
if (!flags[i].startsWith("--")) {
|
|
12676
|
+
if (upstreamArgs.length === 0) {
|
|
12677
|
+
upstreamArgs.push(...flags.slice(i));
|
|
12678
|
+
}
|
|
12679
|
+
break;
|
|
12680
|
+
}
|
|
12681
|
+
switch (flags[i]) {
|
|
12682
|
+
case "--policy":
|
|
12683
|
+
policySource = flags[++i];
|
|
12684
|
+
break;
|
|
12685
|
+
case "--name":
|
|
12686
|
+
name = flags[++i];
|
|
12687
|
+
break;
|
|
12688
|
+
case "--verbose":
|
|
12689
|
+
verbose = true;
|
|
12690
|
+
break;
|
|
12691
|
+
case "--rate-limit":
|
|
12692
|
+
rateLimitPerTool = parseInt(flags[++i], 10);
|
|
12693
|
+
break;
|
|
12694
|
+
case "--global-rate-limit":
|
|
12695
|
+
globalRateLimit = parseInt(flags[++i], 10);
|
|
12696
|
+
break;
|
|
12697
|
+
case "--config":
|
|
12698
|
+
configFile = flags[++i];
|
|
12699
|
+
break;
|
|
12868
12700
|
case "--api-key":
|
|
12869
|
-
apiKey =
|
|
12701
|
+
apiKey = flags[++i];
|
|
12870
12702
|
break;
|
|
12871
|
-
case "--
|
|
12872
|
-
|
|
12873
|
-
|
|
12874
|
-
case "-
|
|
12875
|
-
|
|
12876
|
-
|
|
12703
|
+
case "--api-url":
|
|
12704
|
+
apiUrl = flags[++i];
|
|
12705
|
+
break;
|
|
12706
|
+
case "--upstream-url":
|
|
12707
|
+
upstreamUrl = flags[++i];
|
|
12708
|
+
break;
|
|
12709
|
+
case "--upstream-transport":
|
|
12710
|
+
upstreamTransport = flags[++i];
|
|
12711
|
+
break;
|
|
12712
|
+
case "--port":
|
|
12713
|
+
port = parseInt(flags[++i], 10);
|
|
12877
12714
|
break;
|
|
12878
12715
|
case "--policy-id":
|
|
12879
12716
|
case "--id":
|
|
12880
|
-
policyId =
|
|
12717
|
+
policyId = flags[++i];
|
|
12718
|
+
break;
|
|
12719
|
+
case "--agent-name":
|
|
12720
|
+
agentName = flags[++i];
|
|
12881
12721
|
break;
|
|
12882
12722
|
}
|
|
12883
12723
|
}
|
|
12724
|
+
if (apiKey && /^\$\{.+\}$/.test(apiKey)) {
|
|
12725
|
+
apiKey = void 0;
|
|
12726
|
+
}
|
|
12884
12727
|
if (!apiKey) {
|
|
12885
|
-
|
|
12886
|
-
|
|
12887
|
-
|
|
12888
|
-
|
|
12889
|
-
|
|
12890
|
-
|
|
12891
|
-
log3(` SOLONGATE_API_KEY=sg_live_... solongate-proxy ${command}`);
|
|
12892
|
-
process.exit(1);
|
|
12728
|
+
const dotenvPath = resolve(".env");
|
|
12729
|
+
if (existsSync(dotenvPath)) {
|
|
12730
|
+
const dotenvContent = readFileSync(dotenvPath, "utf-8");
|
|
12731
|
+
const match = dotenvContent.match(/^SOLONGATE_API_KEY=(sg_(?:live|test)_\w+)/m);
|
|
12732
|
+
if (match) apiKey = match[1];
|
|
12733
|
+
}
|
|
12893
12734
|
}
|
|
12894
|
-
if (!apiKey
|
|
12895
|
-
|
|
12896
|
-
|
|
12735
|
+
if (!apiKey) {
|
|
12736
|
+
const envKey = process.env.SOLONGATE_API_KEY;
|
|
12737
|
+
if (envKey && !/^\$\{.+\}$/.test(envKey)) {
|
|
12738
|
+
apiKey = envKey;
|
|
12739
|
+
}
|
|
12897
12740
|
}
|
|
12898
|
-
|
|
12899
|
-
|
|
12900
|
-
|
|
12901
|
-
const res = await fetch(`${API_URL}/api/v1/policies`, {
|
|
12902
|
-
headers: { "Authorization": `Bearer ${apiKey}` }
|
|
12903
|
-
});
|
|
12904
|
-
if (!res.ok) throw new Error(`Failed to list policies (${res.status})`);
|
|
12905
|
-
const data = await res.json();
|
|
12906
|
-
return data.policies ?? [];
|
|
12907
|
-
}
|
|
12908
|
-
async function list5(apiKey, policyId) {
|
|
12909
|
-
const policies = await listPolicies(apiKey);
|
|
12910
|
-
if (policies.length === 0) {
|
|
12911
|
-
log3(yellow2("No policies found. Create one in the dashboard first."));
|
|
12912
|
-
log3(dim2(" https://dashboard.solongate.com/policies"));
|
|
12913
|
-
return;
|
|
12741
|
+
if (!apiKey) {
|
|
12742
|
+
const cred = loginCredential();
|
|
12743
|
+
if (cred.apiKey) apiKey = cred.apiKey;
|
|
12914
12744
|
}
|
|
12915
|
-
if (
|
|
12916
|
-
|
|
12917
|
-
|
|
12918
|
-
|
|
12919
|
-
log3("");
|
|
12920
|
-
log3("Available policies:");
|
|
12921
|
-
for (const p of policies) {
|
|
12922
|
-
log3(` ${dim2("\u2022")} ${p.id}`);
|
|
12923
|
-
}
|
|
12924
|
-
process.exit(1);
|
|
12925
|
-
}
|
|
12926
|
-
const full = await fetchCloudPolicy(apiKey, API_URL, policyId);
|
|
12927
|
-
printPolicyDetail(full);
|
|
12928
|
-
return;
|
|
12745
|
+
if (!apiKey) {
|
|
12746
|
+
throw new Error(
|
|
12747
|
+
"Not logged in. Run this once to get started:\n\n solongate\n\n then add your account from the Accounts panel.\n"
|
|
12748
|
+
);
|
|
12929
12749
|
}
|
|
12930
|
-
|
|
12931
|
-
|
|
12932
|
-
|
|
12933
|
-
|
|
12934
|
-
const fullPolicies = await Promise.all(
|
|
12935
|
-
policies.map(
|
|
12936
|
-
(p) => fetchCloudPolicy(apiKey, API_URL, p.id).then((full) => ({ policy: p, rules: full.rules })).catch(() => ({ policy: p, rules: [] }))
|
|
12937
|
-
)
|
|
12938
|
-
);
|
|
12939
|
-
for (const { policy, rules } of fullPolicies) {
|
|
12940
|
-
printPolicySummary(policy, [...rules]);
|
|
12941
|
-
}
|
|
12942
|
-
log3(dim2(" \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"));
|
|
12943
|
-
log3("");
|
|
12944
|
-
log3(` ${dim2("View details:")} solongate-proxy list --policy-id <ID>`);
|
|
12945
|
-
log3(` ${dim2("Pull policy:")} solongate-proxy pull --policy-id <ID>`);
|
|
12946
|
-
log3(` ${dim2("Push policy:")} solongate-proxy push --policy-id <ID>`);
|
|
12947
|
-
log3("");
|
|
12948
|
-
}
|
|
12949
|
-
function printPolicySummary(p, rules) {
|
|
12950
|
-
const ruleCount = rules.length;
|
|
12951
|
-
const allowCount = rules.filter((r) => r.effect === "ALLOW").length;
|
|
12952
|
-
const denyCount = rules.filter((r) => r.effect === "DENY").length;
|
|
12953
|
-
log3(` ${cyan2(p.id)}`);
|
|
12954
|
-
log3(` ${bold2(p.name)} ${dim2(`v${p.version ?? "?"}`)}`);
|
|
12955
|
-
log3(` ${dim2("Rules:")} ${ruleCount} ${green2(`${allowCount} ALLOW`)} ${red2(`${denyCount} DENY`)}`);
|
|
12956
|
-
if (p.created_at) {
|
|
12957
|
-
log3(` ${dim2("Updated:")} ${new Date(p.created_at).toLocaleString()}`);
|
|
12958
|
-
}
|
|
12959
|
-
log3("");
|
|
12960
|
-
}
|
|
12961
|
-
function printPolicyDetail(policy) {
|
|
12962
|
-
log3("");
|
|
12963
|
-
log3(bold2(` ${policy.name}`));
|
|
12964
|
-
log3(` ${dim2("ID:")} ${cyan2(policy.id)} ${dim2("Version:")} ${policy.version} ${dim2("Rules:")} ${policy.rules.length}`);
|
|
12965
|
-
log3("");
|
|
12966
|
-
if (policy.rules.length === 0) {
|
|
12967
|
-
log3(yellow2(" No rules defined."));
|
|
12968
|
-
log3("");
|
|
12969
|
-
return;
|
|
12750
|
+
if (!apiKey.startsWith("sg_live_") && !apiKey.startsWith("sg_test_")) {
|
|
12751
|
+
throw new Error(
|
|
12752
|
+
"Invalid API key format. Keys must start with 'sg_live_' or 'sg_test_'.\nGet your API key at https://solongate.com\n"
|
|
12753
|
+
);
|
|
12970
12754
|
}
|
|
12971
|
-
|
|
12972
|
-
|
|
12973
|
-
const
|
|
12974
|
-
|
|
12975
|
-
|
|
12976
|
-
if (
|
|
12977
|
-
|
|
12978
|
-
}
|
|
12979
|
-
log3(` ${dim2(`${rule.permission} trust:${rule.minimumTrustLevel || "UNTRUSTED"}`)}`);
|
|
12980
|
-
if (rule.pathConstraints) {
|
|
12981
|
-
const pc = rule.pathConstraints;
|
|
12982
|
-
if (pc.rootDirectory) log3(` ${magenta("ROOT")} ${pc.rootDirectory}`);
|
|
12983
|
-
if (pc.allowed?.length) log3(` ${green2("PATHS")} ${pc.allowed.join(", ")}`);
|
|
12984
|
-
if (pc.denied?.length) log3(` ${red2("DENY")} ${pc.denied.join(", ")}`);
|
|
12985
|
-
}
|
|
12986
|
-
if (rule.commandConstraints) {
|
|
12987
|
-
const cc = rule.commandConstraints;
|
|
12988
|
-
if (cc.allowed?.length) log3(` ${green2("CMDS")} ${cc.allowed.join(", ")}`);
|
|
12989
|
-
if (cc.denied?.length) log3(` ${red2("DENY")} ${cc.denied.join(", ")}`);
|
|
12990
|
-
}
|
|
12991
|
-
if (rule.filenameConstraints) {
|
|
12992
|
-
const fc = rule.filenameConstraints;
|
|
12993
|
-
if (fc.allowed?.length) log3(` ${green2("FILES")} ${fc.allowed.join(", ")}`);
|
|
12994
|
-
if (fc.denied?.length) log3(` ${red2("DENY")} ${fc.denied.join(", ")}`);
|
|
12995
|
-
}
|
|
12996
|
-
if (rule.urlConstraints) {
|
|
12997
|
-
const uc = rule.urlConstraints;
|
|
12998
|
-
if (uc.allowed?.length) log3(` ${green2("URLS")} ${uc.allowed.join(", ")}`);
|
|
12999
|
-
if (uc.denied?.length) log3(` ${red2("DENY")} ${uc.denied.join(", ")}`);
|
|
13000
|
-
}
|
|
13001
|
-
}
|
|
13002
|
-
log3("");
|
|
13003
|
-
log3(dim2(" \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"));
|
|
13004
|
-
log3("");
|
|
13005
|
-
}
|
|
13006
|
-
async function pull(apiKey, file, policyId) {
|
|
13007
|
-
if (!policyId) {
|
|
13008
|
-
const policies = await listPolicies(apiKey);
|
|
13009
|
-
if (policies.length === 0) {
|
|
13010
|
-
log3(red2("No policies found. Create one in the dashboard first."));
|
|
13011
|
-
process.exit(1);
|
|
12755
|
+
const resolvedPolicyPath = policySource ? resolvePolicyPath(policySource) : null;
|
|
12756
|
+
if (configFile) {
|
|
12757
|
+
const filePath = resolve(configFile);
|
|
12758
|
+
const content = readFileSync(filePath, "utf-8");
|
|
12759
|
+
const fileConfig = JSON.parse(content);
|
|
12760
|
+
if (!fileConfig.upstream) {
|
|
12761
|
+
throw new Error('Config file must include "upstream" with at least "command" or "url"');
|
|
13012
12762
|
}
|
|
13013
|
-
|
|
13014
|
-
|
|
13015
|
-
|
|
13016
|
-
|
|
13017
|
-
|
|
13018
|
-
|
|
13019
|
-
|
|
13020
|
-
|
|
13021
|
-
|
|
13022
|
-
|
|
13023
|
-
|
|
13024
|
-
|
|
13025
|
-
|
|
13026
|
-
|
|
13027
|
-
|
|
13028
|
-
const policy = await fetchCloudPolicy(apiKey, API_URL, policyId);
|
|
13029
|
-
const { id: _id, ...policyWithoutId } = policy;
|
|
13030
|
-
const json = JSON.stringify(policyWithoutId, null, 2) + "\n";
|
|
13031
|
-
writeFileSync10(file, json, "utf-8");
|
|
13032
|
-
log3("");
|
|
13033
|
-
log3(green2(" Saved to: ") + file);
|
|
13034
|
-
log3(` ${dim2("Name:")} ${policy.name}`);
|
|
13035
|
-
log3(` ${dim2("Version:")} ${policy.version}`);
|
|
13036
|
-
log3(` ${dim2("Rules:")} ${policy.rules.length}`);
|
|
13037
|
-
log3("");
|
|
13038
|
-
log3(dim2("The policy file does not contain an ID."));
|
|
13039
|
-
log3(dim2("Use --policy-id to specify the target when pushing/pulling."));
|
|
13040
|
-
log3("");
|
|
13041
|
-
}
|
|
13042
|
-
async function push(apiKey, file, policyId) {
|
|
13043
|
-
if (!existsSync7(file)) {
|
|
13044
|
-
log3(red2(`ERROR: File not found: ${file}`));
|
|
13045
|
-
process.exit(1);
|
|
12763
|
+
const cfgPolicySource = fileConfig.policy ?? policySource ?? "policy.json";
|
|
12764
|
+
return {
|
|
12765
|
+
upstream: fileConfig.upstream,
|
|
12766
|
+
policy: loadPolicy(cfgPolicySource),
|
|
12767
|
+
name: fileConfig.name ?? name,
|
|
12768
|
+
verbose: fileConfig.verbose ?? verbose,
|
|
12769
|
+
rateLimitPerTool: fileConfig.rateLimitPerTool ?? rateLimitPerTool,
|
|
12770
|
+
globalRateLimit: fileConfig.globalRateLimit ?? globalRateLimit,
|
|
12771
|
+
apiKey: apiKey ?? fileConfig.apiKey,
|
|
12772
|
+
apiUrl: apiUrl ?? fileConfig.apiUrl,
|
|
12773
|
+
port: port ?? fileConfig.port,
|
|
12774
|
+
policyPath: resolvePolicyPath(cfgPolicySource) ?? void 0,
|
|
12775
|
+
policyId: policyId ?? fileConfig.policyId,
|
|
12776
|
+
agentName
|
|
12777
|
+
};
|
|
13046
12778
|
}
|
|
13047
|
-
if (
|
|
13048
|
-
|
|
13049
|
-
|
|
13050
|
-
|
|
13051
|
-
|
|
13052
|
-
|
|
13053
|
-
|
|
13054
|
-
|
|
13055
|
-
|
|
13056
|
-
|
|
13057
|
-
|
|
13058
|
-
|
|
12779
|
+
if (upstreamUrl) {
|
|
12780
|
+
const transport = upstreamTransport ?? (upstreamUrl.includes("/sse") ? "sse" : "http");
|
|
12781
|
+
return {
|
|
12782
|
+
upstream: {
|
|
12783
|
+
transport,
|
|
12784
|
+
command: "",
|
|
12785
|
+
// not used for URL-based transports
|
|
12786
|
+
url: upstreamUrl
|
|
12787
|
+
},
|
|
12788
|
+
policy: loadPolicy(policySource ?? "policy.json"),
|
|
12789
|
+
name,
|
|
12790
|
+
verbose,
|
|
12791
|
+
rateLimitPerTool,
|
|
12792
|
+
globalRateLimit,
|
|
12793
|
+
apiKey,
|
|
12794
|
+
apiUrl,
|
|
12795
|
+
port,
|
|
12796
|
+
policyPath: resolvedPolicyPath ?? void 0,
|
|
12797
|
+
policyId,
|
|
12798
|
+
agentName
|
|
12799
|
+
};
|
|
13059
12800
|
}
|
|
13060
|
-
|
|
13061
|
-
|
|
13062
|
-
|
|
13063
|
-
|
|
13064
|
-
} catch {
|
|
13065
|
-
log3(red2(`ERROR: Invalid JSON in ${file}`));
|
|
13066
|
-
process.exit(1);
|
|
12801
|
+
if (upstreamArgs.length === 0) {
|
|
12802
|
+
throw new Error(
|
|
12803
|
+
"No upstream server command provided.\n\nIf you just want to get started, run:\n solongate\n"
|
|
12804
|
+
);
|
|
13067
12805
|
}
|
|
13068
|
-
|
|
13069
|
-
|
|
13070
|
-
|
|
13071
|
-
|
|
13072
|
-
|
|
13073
|
-
|
|
13074
|
-
|
|
13075
|
-
const method = checkRes.ok ? "PUT" : "POST";
|
|
13076
|
-
const url = checkRes.ok ? `${API_URL}/api/v1/policies/${policyId}` : `${API_URL}/api/v1/policies`;
|
|
13077
|
-
const res = await fetch(url, {
|
|
13078
|
-
method,
|
|
13079
|
-
headers: {
|
|
13080
|
-
"Authorization": `Bearer ${apiKey}`,
|
|
13081
|
-
"Content-Type": "application/json"
|
|
12806
|
+
const [command, ...commandArgs] = upstreamArgs;
|
|
12807
|
+
return {
|
|
12808
|
+
upstream: {
|
|
12809
|
+
transport: upstreamTransport ?? "stdio",
|
|
12810
|
+
command,
|
|
12811
|
+
args: commandArgs,
|
|
12812
|
+
env: { PATH: process.env.PATH ?? "", HOME: process.env.HOME ?? "", USERPROFILE: process.env.USERPROFILE ?? "" }
|
|
13082
12813
|
},
|
|
13083
|
-
|
|
13084
|
-
|
|
13085
|
-
|
|
13086
|
-
|
|
13087
|
-
|
|
13088
|
-
|
|
13089
|
-
|
|
13090
|
-
|
|
13091
|
-
|
|
13092
|
-
|
|
13093
|
-
|
|
13094
|
-
|
|
13095
|
-
}
|
|
13096
|
-
const data = await res.json();
|
|
13097
|
-
log3("");
|
|
13098
|
-
log3(green2(` Pushed to cloud: v${data._version ?? "created"}`));
|
|
13099
|
-
log3(` ${dim2("Policy ID:")} ${policyId}`);
|
|
13100
|
-
log3(` ${dim2("Method:")} ${method === "PUT" ? "Updated existing" : "Created new"}`);
|
|
13101
|
-
log3("");
|
|
12814
|
+
policy: loadPolicy(policySource ?? "policy.json"),
|
|
12815
|
+
name,
|
|
12816
|
+
verbose,
|
|
12817
|
+
rateLimitPerTool,
|
|
12818
|
+
globalRateLimit,
|
|
12819
|
+
apiKey,
|
|
12820
|
+
apiUrl,
|
|
12821
|
+
port,
|
|
12822
|
+
policyPath: resolvedPolicyPath ?? void 0,
|
|
12823
|
+
policyId,
|
|
12824
|
+
agentName
|
|
12825
|
+
};
|
|
13102
12826
|
}
|
|
13103
|
-
|
|
13104
|
-
const
|
|
13105
|
-
|
|
13106
|
-
|
|
13107
|
-
await pull(apiKey, file, policyId);
|
|
13108
|
-
} else if (command === "push") {
|
|
13109
|
-
await push(apiKey, file, policyId);
|
|
13110
|
-
} else if (command === "list" || command === "ls") {
|
|
13111
|
-
await list5(apiKey, policyId);
|
|
13112
|
-
} else {
|
|
13113
|
-
log3(red2(`Unknown command: ${command}`));
|
|
13114
|
-
log3("");
|
|
13115
|
-
log3(bold2("Usage:"));
|
|
13116
|
-
log3(" solongate-proxy list List all policies");
|
|
13117
|
-
log3(" solongate-proxy list --policy-id <ID> Show policy details");
|
|
13118
|
-
log3(" solongate-proxy pull --policy-id <ID> Pull policy to local file");
|
|
13119
|
-
log3(" solongate-proxy push --policy-id <ID> Push local file to cloud");
|
|
13120
|
-
log3("");
|
|
13121
|
-
log3(bold2("Flags:"));
|
|
13122
|
-
log3(" --policy-id, --id <ID> Cloud policy ID (required for push)");
|
|
13123
|
-
log3(" --file, -f <path> Local file path (default: policy.json)");
|
|
13124
|
-
log3(" --api-key <key> API key (or set SOLONGATE_API_KEY)");
|
|
13125
|
-
log3("");
|
|
13126
|
-
process.exit(1);
|
|
13127
|
-
}
|
|
13128
|
-
} catch (err2) {
|
|
13129
|
-
log3(red2(`ERROR: ${err2 instanceof Error ? err2.message : String(err2)}`));
|
|
13130
|
-
process.exit(1);
|
|
13131
|
-
}
|
|
12827
|
+
function resolvePolicyPath(source) {
|
|
12828
|
+
const filePath = resolve(source);
|
|
12829
|
+
if (existsSync(filePath)) return filePath;
|
|
12830
|
+
return null;
|
|
13132
12831
|
}
|
|
13133
|
-
var log3, dim2, bold2, green2, red2, yellow2, cyan2, magenta, API_URL;
|
|
13134
|
-
var init_pull_push = __esm({
|
|
13135
|
-
"src/pull-push.ts"() {
|
|
13136
|
-
"use strict";
|
|
13137
|
-
init_config();
|
|
13138
|
-
log3 = (...args) => process.stderr.write(`${args.map(String).join(" ")}
|
|
13139
|
-
`);
|
|
13140
|
-
dim2 = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
13141
|
-
bold2 = (s) => `\x1B[1m${s}\x1B[0m`;
|
|
13142
|
-
green2 = (s) => `\x1B[32m${s}\x1B[0m`;
|
|
13143
|
-
red2 = (s) => `\x1B[31m${s}\x1B[0m`;
|
|
13144
|
-
yellow2 = (s) => `\x1B[33m${s}\x1B[0m`;
|
|
13145
|
-
cyan2 = (s) => `\x1B[36m${s}\x1B[0m`;
|
|
13146
|
-
magenta = (s) => `\x1B[35m${s}\x1B[0m`;
|
|
13147
|
-
API_URL = "https://api.solongate.com";
|
|
13148
|
-
main();
|
|
13149
|
-
}
|
|
13150
|
-
});
|
|
13151
|
-
|
|
13152
|
-
// src/index.ts
|
|
13153
|
-
init_config();
|
|
13154
|
-
import { readFileSync as readFileSync13 } from "fs";
|
|
13155
|
-
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
13156
|
-
import { dirname as dirname4, join as join16 } from "path";
|
|
13157
12832
|
|
|
13158
12833
|
// src/proxy.ts
|
|
13159
12834
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -15348,11 +15023,7 @@ var SolonGate = class {
|
|
|
15348
15023
|
}
|
|
15349
15024
|
};
|
|
15350
15025
|
|
|
15351
|
-
// src/proxy.ts
|
|
15352
|
-
init_config();
|
|
15353
|
-
|
|
15354
15026
|
// src/sync.ts
|
|
15355
|
-
init_config();
|
|
15356
15027
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, watch, existsSync as existsSync2 } from "fs";
|
|
15357
15028
|
var log = (...args) => process.stderr.write(`[SolonGate Sync] ${args.map(String).join(" ")}
|
|
15358
15029
|
`);
|
|
@@ -15577,7 +15248,7 @@ var Mutex = class {
|
|
|
15577
15248
|
this.locked = true;
|
|
15578
15249
|
return;
|
|
15579
15250
|
}
|
|
15580
|
-
return new Promise((
|
|
15251
|
+
return new Promise((resolve6, reject) => {
|
|
15581
15252
|
const timer = setTimeout(() => {
|
|
15582
15253
|
const idx = this.queue.indexOf(onReady);
|
|
15583
15254
|
if (idx !== -1) this.queue.splice(idx, 1);
|
|
@@ -15585,7 +15256,7 @@ var Mutex = class {
|
|
|
15585
15256
|
}, timeoutMs);
|
|
15586
15257
|
const onReady = () => {
|
|
15587
15258
|
clearTimeout(timer);
|
|
15588
|
-
|
|
15259
|
+
resolve6();
|
|
15589
15260
|
};
|
|
15590
15261
|
this.queue.push(onReady);
|
|
15591
15262
|
});
|
|
@@ -16176,7 +15847,7 @@ ${msg.content.text}`;
|
|
|
16176
15847
|
|
|
16177
15848
|
// src/index.ts
|
|
16178
15849
|
init_cli_utils();
|
|
16179
|
-
var CLI_SUBCOMMANDS = /* @__PURE__ */ new Set(["
|
|
15850
|
+
var CLI_SUBCOMMANDS = /* @__PURE__ */ new Set(["logs-server", "local-logs", "policy", "ratelimit", "dlp", "stats", "audit", "sessions", "session", "doctor", "watch", "alerts", "webhooks", "dataroom"]);
|
|
16180
15851
|
var CLI_INFO_ARGS = /* @__PURE__ */ new Set(["login", "help", "--help", "-h", "--version", "-v", "version"]);
|
|
16181
15852
|
var IS_HUMAN_CLI = process.argv.length <= 2 || CLI_SUBCOMMANDS.has(process.argv[2] ?? "") || CLI_INFO_ARGS.has(process.argv[2] ?? "");
|
|
16182
15853
|
if (!IS_HUMAN_CLI) {
|
|
@@ -16196,7 +15867,7 @@ if (!IS_HUMAN_CLI) {
|
|
|
16196
15867
|
var PKG_VERSION = (() => {
|
|
16197
15868
|
try {
|
|
16198
15869
|
const p = join16(dirname4(fileURLToPath4(import.meta.url)), "..", "package.json");
|
|
16199
|
-
return JSON.parse(
|
|
15870
|
+
return JSON.parse(readFileSync12(p, "utf-8")).version || "unknown";
|
|
16200
15871
|
} catch {
|
|
16201
15872
|
return "unknown";
|
|
16202
15873
|
}
|
|
@@ -16268,8 +15939,8 @@ function printHelp() {
|
|
|
16268
15939
|
cmd("audit block <logId> [--scope exact|tool]", "turn a call into a DENY rule");
|
|
16269
15940
|
cmd("stats [timeseries|drift]", "traffic & security statistics");
|
|
16270
15941
|
cmd("watch [--filter DENY] [--tool <s>]", "live-tail tool calls (Ctrl+C to stop)");
|
|
16271
|
-
cmd("
|
|
16272
|
-
cmd("
|
|
15942
|
+
cmd("sessions [--all]", "live agent-session feed (calls, denies, trust)");
|
|
15943
|
+
cmd("session <id>", "one session's detail");
|
|
16273
15944
|
head("Alerts & webhooks");
|
|
16274
15945
|
cmd("alerts list");
|
|
16275
15946
|
cmd("alerts add --signal deny|dlp|ratelimit|any --threshold N --window S (--email <a> | --telegram <id> | --slack <url>)", "spike alert");
|
|
@@ -16277,16 +15948,12 @@ function printHelp() {
|
|
|
16277
15948
|
cmd("webhooks list");
|
|
16278
15949
|
cmd("webhooks add --url <https://\u2026> [--events denials|allowed|all]", "event webhook");
|
|
16279
15950
|
cmd("webhooks remove <id>");
|
|
16280
|
-
head("Policy sync");
|
|
16281
|
-
cmd("list [--policy-id <ID>]", "list cloud policies (or show one)");
|
|
16282
|
-
cmd("pull --policy-id <ID> [--file f]", "pull a cloud policy to a local file");
|
|
16283
|
-
cmd("push --policy-id <ID> [--file f]", "push a local policy file to the cloud");
|
|
16284
15951
|
console.log("");
|
|
16285
15952
|
console.log(` ${c.dim}Add ${c.reset}${c.cyan}--json${c.reset}${c.dim} to most read commands for machine output.${c.reset}`);
|
|
16286
15953
|
console.log(` ${c.dim}Details for a command: ${c.reset}${c.cyan}solongate <command> help${c.reset}${c.dim} \xB7 Dashboard: ${c.reset}${c.cyan}https://dashboard.solongate.com${c.reset}`);
|
|
16287
15954
|
console.log("");
|
|
16288
15955
|
}
|
|
16289
|
-
async function
|
|
15956
|
+
async function main() {
|
|
16290
15957
|
const subcommand = process.argv[2];
|
|
16291
15958
|
if (subcommand === "--help" || subcommand === "-h" || subcommand === "help") {
|
|
16292
15959
|
printHelp();
|
|
@@ -16321,7 +15988,7 @@ async function main2() {
|
|
|
16321
15988
|
await launchTui2();
|
|
16322
15989
|
return;
|
|
16323
15990
|
}
|
|
16324
|
-
const MGMT_COMMANDS = /* @__PURE__ */ new Set(["policy", "ratelimit", "dlp", "stats", "audit", "
|
|
15991
|
+
const MGMT_COMMANDS = /* @__PURE__ */ new Set(["policy", "ratelimit", "dlp", "stats", "audit", "sessions", "session", "doctor", "watch", "alerts", "webhooks"]);
|
|
16325
15992
|
if (MGMT_COMMANDS.has(subcommand ?? "")) {
|
|
16326
15993
|
const { runCommand: runCommand2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
|
|
16327
15994
|
const code = await runCommand2(subcommand, process.argv.slice(3));
|
|
@@ -16339,10 +16006,6 @@ async function main2() {
|
|
|
16339
16006
|
await runLogsServer2();
|
|
16340
16007
|
return;
|
|
16341
16008
|
}
|
|
16342
|
-
if (subcommand === "pull" || subcommand === "push" || subcommand === "list" || subcommand === "ls") {
|
|
16343
|
-
await Promise.resolve().then(() => (init_pull_push(), pull_push_exports));
|
|
16344
|
-
return;
|
|
16345
|
-
}
|
|
16346
16009
|
const hasProxySeparator = process.argv.includes("--");
|
|
16347
16010
|
const looksLikeProxyFlag = (subcommand ?? "").startsWith("-");
|
|
16348
16011
|
if (subcommand && !hasProxySeparator && !looksLikeProxyFlag) {
|
|
@@ -16365,4 +16028,4 @@ async function main2() {
|
|
|
16365
16028
|
process.exit(1);
|
|
16366
16029
|
}
|
|
16367
16030
|
}
|
|
16368
|
-
|
|
16031
|
+
main();
|