@solongate/proxy 0.78.0 → 0.80.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/api-client/index.d.ts +4 -0
- package/dist/api-client/keys.d.ts +17 -0
- package/dist/api-client/mcp.d.ts +10 -0
- package/dist/api-client/settings.d.ts +52 -0
- package/dist/commands/alerts.d.ts +1 -0
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/index.d.ts +1 -1
- package/dist/commands/index.js +426 -3
- package/dist/commands/keys.d.ts +1 -0
- package/dist/commands/mcp.d.ts +1 -0
- package/dist/commands/watch.d.ts +1 -0
- package/dist/commands/webhooks.d.ts +1 -0
- package/dist/index.js +730 -90
- package/dist/tui/index.js +221 -12
- package/package.json +1 -1
|
@@ -9,10 +9,14 @@ import * as settings from './settings.js';
|
|
|
9
9
|
import * as stats from './stats.js';
|
|
10
10
|
import * as audit from './audit.js';
|
|
11
11
|
import * as agents from './agents.js';
|
|
12
|
+
import * as keys from './keys.js';
|
|
13
|
+
import * as mcp from './mcp.js';
|
|
12
14
|
export declare const api: {
|
|
13
15
|
policies: typeof policies;
|
|
14
16
|
settings: typeof settings;
|
|
15
17
|
stats: typeof stats;
|
|
16
18
|
audit: typeof audit;
|
|
17
19
|
agents: typeof agents;
|
|
20
|
+
keys: typeof keys;
|
|
21
|
+
mcp: typeof mcp;
|
|
18
22
|
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface ApiKey {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
key_prefix: string;
|
|
5
|
+
is_live?: boolean;
|
|
6
|
+
created_at: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function list(): Promise<{
|
|
9
|
+
keys: ApiKey[];
|
|
10
|
+
}>;
|
|
11
|
+
export declare function create(name: string, isLive?: boolean): Promise<{
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
key: string;
|
|
15
|
+
key_prefix: string;
|
|
16
|
+
}>;
|
|
17
|
+
export declare function revoke(id: string): Promise<unknown>;
|
|
@@ -20,3 +20,55 @@ export interface GuardStatus {
|
|
|
20
20
|
outdated_count: number;
|
|
21
21
|
}
|
|
22
22
|
export declare function getGuardStatus(): Promise<GuardStatus>;
|
|
23
|
+
export declare function getSelfProtection(): Promise<{
|
|
24
|
+
enabled: boolean;
|
|
25
|
+
}>;
|
|
26
|
+
export declare function setSelfProtection(enabled: boolean): Promise<{
|
|
27
|
+
enabled: boolean;
|
|
28
|
+
}>;
|
|
29
|
+
export interface AlertRule {
|
|
30
|
+
id: string;
|
|
31
|
+
name: string;
|
|
32
|
+
enabled: boolean;
|
|
33
|
+
threshold: number;
|
|
34
|
+
windowSeconds: number;
|
|
35
|
+
signal: 'any' | 'deny' | 'dlp' | 'ratelimit';
|
|
36
|
+
slackUrls?: string[];
|
|
37
|
+
emails?: string[];
|
|
38
|
+
telegram?: string[];
|
|
39
|
+
createdAt: string;
|
|
40
|
+
}
|
|
41
|
+
export declare function getAlerts(): Promise<{
|
|
42
|
+
rules: AlertRule[];
|
|
43
|
+
}>;
|
|
44
|
+
export declare function createAlert(body: Partial<AlertRule> & {
|
|
45
|
+
emails?: string[];
|
|
46
|
+
telegram?: string[];
|
|
47
|
+
slackUrl?: string;
|
|
48
|
+
}): Promise<{
|
|
49
|
+
rule: AlertRule;
|
|
50
|
+
}>;
|
|
51
|
+
export declare function deleteAlert(id: string): Promise<{
|
|
52
|
+
ok: true;
|
|
53
|
+
}>;
|
|
54
|
+
export interface DenialWebhook {
|
|
55
|
+
id: string;
|
|
56
|
+
url: string;
|
|
57
|
+
enabled: boolean;
|
|
58
|
+
events: 'denials' | 'allowed' | 'all';
|
|
59
|
+
headers?: Record<string, string>;
|
|
60
|
+
createdAt: string;
|
|
61
|
+
}
|
|
62
|
+
export declare function getWebhooks(): Promise<{
|
|
63
|
+
webhooks: DenialWebhook[];
|
|
64
|
+
}>;
|
|
65
|
+
export declare function createWebhook(body: {
|
|
66
|
+
url: string;
|
|
67
|
+
events?: 'denials' | 'allowed' | 'all';
|
|
68
|
+
headers?: Record<string, string>;
|
|
69
|
+
}): Promise<{
|
|
70
|
+
webhook: DenialWebhook;
|
|
71
|
+
}>;
|
|
72
|
+
export declare function deleteWebhook(id: string): Promise<{
|
|
73
|
+
ok: true;
|
|
74
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function run(argv: string[]): Promise<number>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function run(argv: string[]): Promise<number>;
|
package/dist/commands/index.d.ts
CHANGED
|
@@ -4,4 +4,4 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export declare function runCommand(command: string, argv: string[]): Promise<number>;
|
|
6
6
|
/** The subcommand names this router owns (used by src/index.ts to route). */
|
|
7
|
-
export declare const COMMAND_NAMES: readonly ["policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent"];
|
|
7
|
+
export declare const COMMAND_NAMES: readonly ["policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent", "doctor", "watch", "keys", "mcp", "alerts", "webhooks"];
|
package/dist/commands/index.js
CHANGED
|
@@ -53,6 +53,14 @@ function dotenvApiKey() {
|
|
|
53
53
|
return void 0;
|
|
54
54
|
}
|
|
55
55
|
var cached = null;
|
|
56
|
+
function isAuthenticated() {
|
|
57
|
+
try {
|
|
58
|
+
resolveCredentials();
|
|
59
|
+
return true;
|
|
60
|
+
} catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
56
64
|
function resolveCredentials(apiUrlOverride) {
|
|
57
65
|
if (cached && !apiUrlOverride) return cached;
|
|
58
66
|
const file = loginCredentialFile();
|
|
@@ -188,10 +196,18 @@ function dryRun(body) {
|
|
|
188
196
|
var settings_exports = {};
|
|
189
197
|
__export(settings_exports, {
|
|
190
198
|
clearRateLimitHistory: () => clearRateLimitHistory,
|
|
199
|
+
createAlert: () => createAlert,
|
|
200
|
+
createWebhook: () => createWebhook,
|
|
201
|
+
deleteAlert: () => deleteAlert,
|
|
202
|
+
deleteWebhook: () => deleteWebhook,
|
|
203
|
+
getAlerts: () => getAlerts,
|
|
191
204
|
getGuardStatus: () => getGuardStatus,
|
|
192
205
|
getRateLimitHistory: () => getRateLimitHistory,
|
|
193
206
|
getSecurityLayers: () => getSecurityLayers,
|
|
194
|
-
|
|
207
|
+
getSelfProtection: () => getSelfProtection,
|
|
208
|
+
getWebhooks: () => getWebhooks,
|
|
209
|
+
setSecurityLayers: () => setSecurityLayers,
|
|
210
|
+
setSelfProtection: () => setSelfProtection
|
|
195
211
|
});
|
|
196
212
|
function getSecurityLayers() {
|
|
197
213
|
return request("GET", "/settings/security-layers");
|
|
@@ -208,6 +224,30 @@ function clearRateLimitHistory() {
|
|
|
208
224
|
function getGuardStatus() {
|
|
209
225
|
return request("GET", "/settings/guard-status");
|
|
210
226
|
}
|
|
227
|
+
function getSelfProtection() {
|
|
228
|
+
return request("GET", "/settings/self-protection");
|
|
229
|
+
}
|
|
230
|
+
function setSelfProtection(enabled) {
|
|
231
|
+
return request("PUT", "/settings/self-protection", { body: { enabled } });
|
|
232
|
+
}
|
|
233
|
+
function getAlerts() {
|
|
234
|
+
return request("GET", "/settings/denial-alerts");
|
|
235
|
+
}
|
|
236
|
+
function createAlert(body) {
|
|
237
|
+
return request("POST", "/settings/denial-alerts", { body });
|
|
238
|
+
}
|
|
239
|
+
function deleteAlert(id) {
|
|
240
|
+
return request("DELETE", "/settings/denial-alerts", { query: { id } });
|
|
241
|
+
}
|
|
242
|
+
function getWebhooks() {
|
|
243
|
+
return request("GET", "/settings/denial-webhook");
|
|
244
|
+
}
|
|
245
|
+
function createWebhook(body) {
|
|
246
|
+
return request("POST", "/settings/denial-webhook", { body });
|
|
247
|
+
}
|
|
248
|
+
function deleteWebhook(id) {
|
|
249
|
+
return request("DELETE", "/settings/denial-webhook", { query: { id } });
|
|
250
|
+
}
|
|
211
251
|
|
|
212
252
|
// src/api-client/stats.ts
|
|
213
253
|
var stats_exports = {};
|
|
@@ -268,8 +308,34 @@ function anomalies(id, limit) {
|
|
|
268
308
|
});
|
|
269
309
|
}
|
|
270
310
|
|
|
311
|
+
// src/api-client/keys.ts
|
|
312
|
+
var keys_exports = {};
|
|
313
|
+
__export(keys_exports, {
|
|
314
|
+
create: () => create2,
|
|
315
|
+
list: () => list3,
|
|
316
|
+
revoke: () => revoke
|
|
317
|
+
});
|
|
318
|
+
function list3() {
|
|
319
|
+
return request("GET", "/keys");
|
|
320
|
+
}
|
|
321
|
+
function create2(name, isLive = true) {
|
|
322
|
+
return request("POST", "/keys", { body: { name, is_live: isLive } });
|
|
323
|
+
}
|
|
324
|
+
function revoke(id) {
|
|
325
|
+
return request("DELETE", `/keys/${encodeURIComponent(id)}`);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// src/api-client/mcp.ts
|
|
329
|
+
var mcp_exports = {};
|
|
330
|
+
__export(mcp_exports, {
|
|
331
|
+
list: () => list4
|
|
332
|
+
});
|
|
333
|
+
function list4() {
|
|
334
|
+
return request("GET", "/mcp-servers");
|
|
335
|
+
}
|
|
336
|
+
|
|
271
337
|
// src/api-client/index.ts
|
|
272
|
-
var api = { policies: policies_exports, settings: settings_exports, stats: stats_exports, audit: audit_exports, agents: agents_exports };
|
|
338
|
+
var api = { policies: policies_exports, settings: settings_exports, stats: stats_exports, audit: audit_exports, agents: agents_exports, keys: keys_exports, mcp: mcp_exports };
|
|
273
339
|
|
|
274
340
|
// src/cli-utils.ts
|
|
275
341
|
var c = {
|
|
@@ -825,6 +891,16 @@ async function run5(argv) {
|
|
|
825
891
|
else err(green(` \u2713 Whitelisted (${res2.scope})`) + dim(` \u2192 ${res2.policy_id} v${res2.policy_version}`));
|
|
826
892
|
return 0;
|
|
827
893
|
}
|
|
894
|
+
if (positionals[0] === "block") {
|
|
895
|
+
const id = positionals[1];
|
|
896
|
+
if (!id) return err(" Usage: audit block <logId> [--scope exact|tool]"), 1;
|
|
897
|
+
const scope = flagStr(flags, "scope") ?? "exact";
|
|
898
|
+
const res2 = await api.audit.block(id, scope);
|
|
899
|
+
if (json) return printJson(res2), 0;
|
|
900
|
+
if (res2.deduped) err(green(" \u2713 ") + dim("Equivalent DENY already present."));
|
|
901
|
+
else err(green(` \u2713 Blocked (${res2.scope})`) + dim(` \u2192 ${res2.policy_id} v${res2.policy_version}`));
|
|
902
|
+
return 0;
|
|
903
|
+
}
|
|
828
904
|
const query = {
|
|
829
905
|
filter: flagStr(flags, "filter"),
|
|
830
906
|
tool: flagStr(flags, "tool"),
|
|
@@ -906,6 +982,341 @@ async function runAgent(argv) {
|
|
|
906
982
|
return 0;
|
|
907
983
|
}
|
|
908
984
|
|
|
985
|
+
// src/commands/doctor.ts
|
|
986
|
+
import { existsSync as existsSync2, statSync } from "fs";
|
|
987
|
+
import { homedir as homedir2 } from "os";
|
|
988
|
+
import { join as join2 } from "path";
|
|
989
|
+
var LOCAL_LOG = join2(homedir2(), ".solongate", "local-logs", "solongate-audit.jsonl");
|
|
990
|
+
async function run6(argv) {
|
|
991
|
+
const { flags } = parse(argv);
|
|
992
|
+
const json = flagBool(flags, "json");
|
|
993
|
+
const checks = [];
|
|
994
|
+
if (!isAuthenticated()) {
|
|
995
|
+
checks.push({ name: "login", ok: false, detail: "not logged in \u2014 run `solongate login`" });
|
|
996
|
+
} else {
|
|
997
|
+
const { apiUrl } = resolveCredentials();
|
|
998
|
+
checks.push({ name: "login", ok: true, detail: `paired \xB7 ${apiUrl}` });
|
|
999
|
+
try {
|
|
1000
|
+
const active2 = await api.policies.active();
|
|
1001
|
+
if (active2.policy) {
|
|
1002
|
+
checks.push({ name: "active policy", ok: true, detail: `${active2.policy.name} v${active2.version} \xB7 ${active2.policy.mode ?? "denylist"} \xB7 matched by ${active2.matched_by}` });
|
|
1003
|
+
} else {
|
|
1004
|
+
checks.push({ name: "active policy", ok: "warn", detail: "no policy resolves \u2014 every call falls back to default" });
|
|
1005
|
+
}
|
|
1006
|
+
const sec = active2.security;
|
|
1007
|
+
checks.push({ name: "rate limit", ok: sec?.rateLimit ? true : "warn", detail: sec?.rateLimit ? `${sec.rateLimit.perMinute}/min` : "off" });
|
|
1008
|
+
checks.push({ name: "dlp", ok: sec?.dlpBlock ? true : "warn", detail: sec?.dlpBlock ? `block \xB7 ${sec.dlpBlock.patterns.length} patterns` : sec?.dlpRedact ? "redact" : "off" });
|
|
1009
|
+
checks.push({ name: "self-protection", ok: active2.self_protection_enabled ? true : "warn", detail: active2.self_protection_enabled ? "on" : "off" });
|
|
1010
|
+
} catch (e) {
|
|
1011
|
+
checks.push({ name: "api", ok: false, detail: "unreachable: " + (e instanceof Error ? e.message : String(e)) });
|
|
1012
|
+
}
|
|
1013
|
+
try {
|
|
1014
|
+
const g = await api.settings.getGuardStatus();
|
|
1015
|
+
checks.push({ name: "guard hook", ok: g.up_to_date ? true : "warn", detail: g.up_to_date ? `v${g.installed} (latest) \xB7 ${g.device_count} device(s)` : `v${g.installed} \u2192 v${g.latest} available \xB7 run \`solongate login\`` });
|
|
1016
|
+
} catch {
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
if (existsSync2(LOCAL_LOG)) {
|
|
1020
|
+
const st = statSync(LOCAL_LOG);
|
|
1021
|
+
const ageMin = (Date.now() - st.mtimeMs) / 6e4;
|
|
1022
|
+
checks.push({ name: "local logs", ok: true, detail: `on \xB7 ${(st.size / 1024).toFixed(0)}KB \xB7 last write ${ageMin < 1 ? "just now" : Math.round(ageMin) + "m ago"}` });
|
|
1023
|
+
} else {
|
|
1024
|
+
checks.push({ name: "local logs", ok: "warn", detail: "off (logs go to cloud) \u2014 enable in dashboard \u2192 Settings" });
|
|
1025
|
+
}
|
|
1026
|
+
if (json) return printJson(checks), checks.some((c2) => c2.ok === false) ? 1 : 0;
|
|
1027
|
+
err("");
|
|
1028
|
+
err(` ${bold("SolonGate doctor")}`);
|
|
1029
|
+
err("");
|
|
1030
|
+
for (const c2 of checks) {
|
|
1031
|
+
const mark = c2.ok === true ? green("\u2713") : c2.ok === "warn" ? yellow("!") : red("\u2717");
|
|
1032
|
+
err(` ${mark} ${c2.name.padEnd(16)} ${dim(c2.detail)}`);
|
|
1033
|
+
}
|
|
1034
|
+
err("");
|
|
1035
|
+
const bad = checks.filter((c2) => c2.ok === false).length;
|
|
1036
|
+
const warn = checks.filter((c2) => c2.ok === "warn").length;
|
|
1037
|
+
if (bad) err(` ${red(`${bad} problem(s)`)}${warn ? dim(` \xB7 ${warn} warning(s)`) : ""}`);
|
|
1038
|
+
else if (warn) err(` ${yellow(`${warn} warning(s)`)} ${dim("\u2014 guard is working")}`);
|
|
1039
|
+
else err(` ${green("all good")}`);
|
|
1040
|
+
return bad ? 1 : 0;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
// src/commands/watch.ts
|
|
1044
|
+
import { closeSync, existsSync as existsSync3, openSync, readSync, statSync as statSync2 } from "fs";
|
|
1045
|
+
import { homedir as homedir3 } from "os";
|
|
1046
|
+
import { join as join3 } from "path";
|
|
1047
|
+
var LOCAL_LOG2 = join3(homedir3(), ".solongate", "local-logs", "solongate-audit.jsonl");
|
|
1048
|
+
var trunc = (s, n) => s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
|
|
1049
|
+
var time = (ms) => new Date(ms).toTimeString().slice(0, 8);
|
|
1050
|
+
function tailLocal(file, maxBytes = 131072) {
|
|
1051
|
+
try {
|
|
1052
|
+
const size = statSync2(file).size;
|
|
1053
|
+
const start = Math.max(0, size - maxBytes);
|
|
1054
|
+
const fd = openSync(file, "r");
|
|
1055
|
+
const buf = Buffer.alloc(size - start);
|
|
1056
|
+
readSync(fd, buf, 0, buf.length, start);
|
|
1057
|
+
closeSync(fd);
|
|
1058
|
+
const lines = buf.toString("utf-8").split("\n").filter(Boolean);
|
|
1059
|
+
if (start > 0) lines.shift();
|
|
1060
|
+
return lines;
|
|
1061
|
+
} catch {
|
|
1062
|
+
return [];
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
function print(r, json) {
|
|
1066
|
+
if (json) return out(JSON.stringify(r));
|
|
1067
|
+
const dec = r.decision === "ALLOW" ? c.green : c.red;
|
|
1068
|
+
const src = r.source === "local" ? c.green + "LOC" : c.blue4 + "CLD";
|
|
1069
|
+
out(
|
|
1070
|
+
`${c.dim}[${time(r.at)}]${c.reset} ${src}${c.reset} ${dec}${r.decision.padEnd(6)}${c.reset} ${c.cyan}${trunc(r.tool, 12).padEnd(13)}${c.reset}${c.dim}${r.permission.slice(0, 4).padEnd(5)}${c.reset}${r.dlp ? c.red + "DLP! " + c.reset : ""}${c.dim}${trunc(r.agent || "-", 12).padEnd(13)}${r.detail}${c.reset}`
|
|
1071
|
+
);
|
|
1072
|
+
}
|
|
1073
|
+
async function run7(argv) {
|
|
1074
|
+
const { flags } = parse(argv);
|
|
1075
|
+
const json = flagBool(flags, "json");
|
|
1076
|
+
const decisionFilter = (flagStr(flags, "filter") || "").toUpperCase();
|
|
1077
|
+
const toolFilter = (flagStr(flags, "tool") || "").toLowerCase();
|
|
1078
|
+
const localOnly = flagBool(flags, "local-only");
|
|
1079
|
+
const cloudOnly = flagBool(flags, "cloud-only");
|
|
1080
|
+
const keep = (r) => {
|
|
1081
|
+
if (decisionFilter && r.decision.toUpperCase() !== decisionFilter && !(decisionFilter === "DENY" && r.decision === "DENIED")) return false;
|
|
1082
|
+
if (toolFilter && !r.tool.toLowerCase().includes(toolFilter)) return false;
|
|
1083
|
+
return true;
|
|
1084
|
+
};
|
|
1085
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1086
|
+
let lastLocalTs = 0;
|
|
1087
|
+
let first = true;
|
|
1088
|
+
const emit = (rows) => {
|
|
1089
|
+
rows.sort((a, b) => a.at - b.at);
|
|
1090
|
+
for (const r of rows) if (keep(r)) print(r, json);
|
|
1091
|
+
};
|
|
1092
|
+
const pollLocal = () => {
|
|
1093
|
+
if (cloudOnly || !existsSync3(LOCAL_LOG2)) return;
|
|
1094
|
+
const rows = [];
|
|
1095
|
+
for (const line of tailLocal(LOCAL_LOG2)) {
|
|
1096
|
+
try {
|
|
1097
|
+
const j = JSON.parse(line);
|
|
1098
|
+
const at = Date.parse(String(j.ts ?? ""));
|
|
1099
|
+
if (!Number.isFinite(at) || at <= lastLocalTs) continue;
|
|
1100
|
+
rows.push({
|
|
1101
|
+
at,
|
|
1102
|
+
tool: String(j.tool ?? "?"),
|
|
1103
|
+
decision: String(j.decision ?? "ALLOW"),
|
|
1104
|
+
permission: String(j.permission ?? ""),
|
|
1105
|
+
detail: (j.arguments ? JSON.stringify(j.arguments) : String(j.reason ?? "")).replace(/\s+/g, " "),
|
|
1106
|
+
agent: String(j.agent_name ?? ""),
|
|
1107
|
+
source: "local",
|
|
1108
|
+
dlp: !!j.dlp
|
|
1109
|
+
});
|
|
1110
|
+
} catch {
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
if (rows.length) lastLocalTs = rows[rows.length - 1].at;
|
|
1114
|
+
if (!first) emit(rows);
|
|
1115
|
+
};
|
|
1116
|
+
const pollCloud = async () => {
|
|
1117
|
+
if (localOnly) return;
|
|
1118
|
+
try {
|
|
1119
|
+
const res = await api.audit.list({ limit: 50 });
|
|
1120
|
+
const rows = [];
|
|
1121
|
+
for (const e of res.entries) {
|
|
1122
|
+
if (seen.has(e.id)) continue;
|
|
1123
|
+
seen.add(e.id);
|
|
1124
|
+
rows.push({
|
|
1125
|
+
at: Date.parse(e.created_at),
|
|
1126
|
+
tool: e.tool_name,
|
|
1127
|
+
decision: e.decision,
|
|
1128
|
+
permission: e.permission ?? "",
|
|
1129
|
+
detail: (e.arguments_summary ? JSON.stringify(e.arguments_summary) : e.reason ?? "").replace(/\s+/g, " "),
|
|
1130
|
+
agent: e.agent_name ?? "",
|
|
1131
|
+
source: "cloud",
|
|
1132
|
+
dlp: !!e.dlp_matches?.length
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
if (!first) emit(rows);
|
|
1136
|
+
} catch {
|
|
1137
|
+
}
|
|
1138
|
+
};
|
|
1139
|
+
if (!json) err(` ${c.dim}watching guard stream \u2014 Ctrl+C to stop${c.reset}`);
|
|
1140
|
+
pollLocal();
|
|
1141
|
+
await pollCloud();
|
|
1142
|
+
first = false;
|
|
1143
|
+
return new Promise(() => {
|
|
1144
|
+
setInterval(pollLocal, 2e3);
|
|
1145
|
+
setInterval(() => void pollCloud(), 4e3);
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
// src/commands/keys.ts
|
|
1150
|
+
var USAGE6 = `${bold("solongate keys")} \u2014 API keys
|
|
1151
|
+
|
|
1152
|
+
keys list List keys (prefix only)
|
|
1153
|
+
keys create --name <n> Create a key (secret shown once)
|
|
1154
|
+
keys revoke <id> Revoke a key
|
|
1155
|
+
|
|
1156
|
+
Add --json for machine-readable output.`;
|
|
1157
|
+
async function run8(argv) {
|
|
1158
|
+
const { positionals, flags } = parse(argv);
|
|
1159
|
+
const sub = positionals[0] ?? "list";
|
|
1160
|
+
const json = flagBool(flags, "json");
|
|
1161
|
+
switch (sub) {
|
|
1162
|
+
case "help":
|
|
1163
|
+
return err(USAGE6), 0;
|
|
1164
|
+
case "list": {
|
|
1165
|
+
const { keys } = await api.keys.list();
|
|
1166
|
+
if (json) return printJson(keys), 0;
|
|
1167
|
+
if (!keys.length) return err(dim(" No API keys.")), 0;
|
|
1168
|
+
table(
|
|
1169
|
+
["ID", "NAME", "PREFIX", "CREATED"],
|
|
1170
|
+
keys.map((k) => [dim(truncate(k.id, 10)), truncate(k.name, 24), cyan(k.key_prefix), dim(k.created_at)])
|
|
1171
|
+
);
|
|
1172
|
+
return 0;
|
|
1173
|
+
}
|
|
1174
|
+
case "create": {
|
|
1175
|
+
const name = flagStr(flags, "name");
|
|
1176
|
+
if (!name) return err(" Usage: keys create --name <name>"), 1;
|
|
1177
|
+
const res = await api.keys.create(name, !flagBool(flags, "test"));
|
|
1178
|
+
if (json) return printJson(res), 0;
|
|
1179
|
+
err(green(` \u2713 Created "${res.name}"`));
|
|
1180
|
+
err("");
|
|
1181
|
+
err(" " + bold(res.key));
|
|
1182
|
+
err(dim(" \u2191 shown only once \u2014 copy it now."));
|
|
1183
|
+
return 0;
|
|
1184
|
+
}
|
|
1185
|
+
case "revoke": {
|
|
1186
|
+
const id = positionals[1];
|
|
1187
|
+
if (!id) return err(" Usage: keys revoke <id>"), 1;
|
|
1188
|
+
await api.keys.revoke(id);
|
|
1189
|
+
if (json) return printJson({ ok: true, id }), 0;
|
|
1190
|
+
return err(green(` \u2713 Revoked ${id}`)), 0;
|
|
1191
|
+
}
|
|
1192
|
+
default:
|
|
1193
|
+
return err(USAGE6), 1;
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
// src/commands/mcp.ts
|
|
1198
|
+
async function run9(argv) {
|
|
1199
|
+
const { flags } = parse(argv);
|
|
1200
|
+
const json = flagBool(flags, "json");
|
|
1201
|
+
const { servers } = await api.mcp.list();
|
|
1202
|
+
if (json) return printJson(servers), 0;
|
|
1203
|
+
if (!servers.length) return err(dim(" No MCP servers registered.")), 0;
|
|
1204
|
+
table(
|
|
1205
|
+
["STATUS", "NAME", "TARGET"],
|
|
1206
|
+
servers.map((s) => [
|
|
1207
|
+
s.status === "active" ? green(s.status) : yellow(s.status),
|
|
1208
|
+
cyan(truncate(s.name, 24)),
|
|
1209
|
+
dim(truncate(s.url || s.command || "\u2014", 46))
|
|
1210
|
+
])
|
|
1211
|
+
);
|
|
1212
|
+
return 0;
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
// src/commands/alerts.ts
|
|
1216
|
+
var USAGE7 = `${bold("solongate alerts")} \u2014 spike alerts (Telegram / email)
|
|
1217
|
+
|
|
1218
|
+
alerts list
|
|
1219
|
+
alerts add --signal deny|dlp|ratelimit|any --threshold N --window S
|
|
1220
|
+
(--email <a> | --telegram <chatId> | --slack <url>)
|
|
1221
|
+
alerts remove <id>
|
|
1222
|
+
|
|
1223
|
+
Add --json for machine-readable output.`;
|
|
1224
|
+
async function run10(argv) {
|
|
1225
|
+
const { positionals, flags } = parse(argv);
|
|
1226
|
+
const sub = positionals[0] ?? "list";
|
|
1227
|
+
const json = flagBool(flags, "json");
|
|
1228
|
+
switch (sub) {
|
|
1229
|
+
case "help":
|
|
1230
|
+
return err(USAGE7), 0;
|
|
1231
|
+
case "list": {
|
|
1232
|
+
const { rules } = await api.settings.getAlerts();
|
|
1233
|
+
if (json) return printJson(rules), 0;
|
|
1234
|
+
if (!rules.length) return err(dim(" No alert rules.")), 0;
|
|
1235
|
+
table(
|
|
1236
|
+
["ID", "ON", "SIGNAL", "THRESH", "WINDOW", "CHANNELS"],
|
|
1237
|
+
rules.map((r) => [
|
|
1238
|
+
dim(truncate(r.id, 10)),
|
|
1239
|
+
r.enabled ? green("\u25CF") : dim("\u25CB"),
|
|
1240
|
+
cyan(r.signal),
|
|
1241
|
+
`${r.threshold}`,
|
|
1242
|
+
`${r.windowSeconds}s`,
|
|
1243
|
+
dim([...r.emails ?? [], ...(r.telegram ?? []).map((t) => "tg:" + t), ...(r.slackUrls ?? []).map(() => "slack")].join(" ") || "\u2014")
|
|
1244
|
+
])
|
|
1245
|
+
);
|
|
1246
|
+
return 0;
|
|
1247
|
+
}
|
|
1248
|
+
case "add": {
|
|
1249
|
+
const email = flagStr(flags, "email");
|
|
1250
|
+
const telegram = flagStr(flags, "telegram");
|
|
1251
|
+
const slack = flagStr(flags, "slack");
|
|
1252
|
+
if (!email && !telegram && !slack) return err(" Need a channel: --email / --telegram / --slack"), 1;
|
|
1253
|
+
const res = await api.settings.createAlert({
|
|
1254
|
+
signal: flagStr(flags, "signal") ?? "deny",
|
|
1255
|
+
threshold: flagNum(flags, "threshold") ?? 5,
|
|
1256
|
+
windowSeconds: flagNum(flags, "window") ?? 300,
|
|
1257
|
+
emails: email ? [email] : void 0,
|
|
1258
|
+
telegram: telegram ? [telegram] : void 0,
|
|
1259
|
+
slackUrl: slack
|
|
1260
|
+
});
|
|
1261
|
+
if (json) return printJson(res.rule), 0;
|
|
1262
|
+
return err(green(` \u2713 Alert added (${res.rule.signal}, ${res.rule.threshold}/${res.rule.windowSeconds}s)`)), 0;
|
|
1263
|
+
}
|
|
1264
|
+
case "remove": {
|
|
1265
|
+
const id = positionals[1];
|
|
1266
|
+
if (!id) return err(" Usage: alerts remove <id>"), 1;
|
|
1267
|
+
await api.settings.deleteAlert(id);
|
|
1268
|
+
if (json) return printJson({ ok: true, id }), 0;
|
|
1269
|
+
return err(green(` \u2713 Removed ${id}`)), 0;
|
|
1270
|
+
}
|
|
1271
|
+
default:
|
|
1272
|
+
return err(USAGE7), 1;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
// src/commands/webhooks.ts
|
|
1277
|
+
var USAGE8 = `${bold("solongate webhooks")} \u2014 event webhooks
|
|
1278
|
+
|
|
1279
|
+
webhooks list
|
|
1280
|
+
webhooks add --url <https://\u2026> [--events denials|allowed|all]
|
|
1281
|
+
webhooks remove <id>
|
|
1282
|
+
|
|
1283
|
+
Add --json for machine-readable output.`;
|
|
1284
|
+
async function run11(argv) {
|
|
1285
|
+
const { positionals, flags } = parse(argv);
|
|
1286
|
+
const sub = positionals[0] ?? "list";
|
|
1287
|
+
const json = flagBool(flags, "json");
|
|
1288
|
+
switch (sub) {
|
|
1289
|
+
case "help":
|
|
1290
|
+
return err(USAGE8), 0;
|
|
1291
|
+
case "list": {
|
|
1292
|
+
const { webhooks } = await api.settings.getWebhooks();
|
|
1293
|
+
if (json) return printJson(webhooks), 0;
|
|
1294
|
+
if (!webhooks.length) return err(dim(" No webhooks.")), 0;
|
|
1295
|
+
table(
|
|
1296
|
+
["ID", "ON", "EVENTS", "URL"],
|
|
1297
|
+
webhooks.map((w) => [dim(truncate(w.id, 10)), w.enabled ? green("\u25CF") : dim("\u25CB"), cyan(w.events), dim(truncate(w.url, 46))])
|
|
1298
|
+
);
|
|
1299
|
+
return 0;
|
|
1300
|
+
}
|
|
1301
|
+
case "add": {
|
|
1302
|
+
const url = flagStr(flags, "url");
|
|
1303
|
+
if (!url) return err(" Usage: webhooks add --url <https://\u2026> [--events denials|allowed|all]"), 1;
|
|
1304
|
+
const res = await api.settings.createWebhook({ url, events: flagStr(flags, "events") ?? "denials" });
|
|
1305
|
+
if (json) return printJson(res.webhook), 0;
|
|
1306
|
+
return err(green(` \u2713 Webhook added (${res.webhook.events}) \u2192 ${res.webhook.url}`)), 0;
|
|
1307
|
+
}
|
|
1308
|
+
case "remove": {
|
|
1309
|
+
const id = positionals[1];
|
|
1310
|
+
if (!id) return err(" Usage: webhooks remove <id>"), 1;
|
|
1311
|
+
await api.settings.deleteWebhook(id);
|
|
1312
|
+
if (json) return printJson({ ok: true, id }), 0;
|
|
1313
|
+
return err(green(` \u2713 Removed ${id}`)), 0;
|
|
1314
|
+
}
|
|
1315
|
+
default:
|
|
1316
|
+
return err(USAGE8), 1;
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
|
|
909
1320
|
// src/commands/index.ts
|
|
910
1321
|
async function dispatch(command, argv) {
|
|
911
1322
|
switch (command) {
|
|
@@ -923,6 +1334,18 @@ async function dispatch(command, argv) {
|
|
|
923
1334
|
return runAgents(argv);
|
|
924
1335
|
case "agent":
|
|
925
1336
|
return runAgent(argv);
|
|
1337
|
+
case "doctor":
|
|
1338
|
+
return run6(argv);
|
|
1339
|
+
case "watch":
|
|
1340
|
+
return run7(argv);
|
|
1341
|
+
case "keys":
|
|
1342
|
+
return run8(argv);
|
|
1343
|
+
case "mcp":
|
|
1344
|
+
return run9(argv);
|
|
1345
|
+
case "alerts":
|
|
1346
|
+
return run10(argv);
|
|
1347
|
+
case "webhooks":
|
|
1348
|
+
return run11(argv);
|
|
926
1349
|
default:
|
|
927
1350
|
err(` Unknown command: ${command}`);
|
|
928
1351
|
return 1;
|
|
@@ -945,7 +1368,7 @@ async function runCommand(command, argv) {
|
|
|
945
1368
|
return 1;
|
|
946
1369
|
}
|
|
947
1370
|
}
|
|
948
|
-
var COMMAND_NAMES = ["policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent"];
|
|
1371
|
+
var COMMAND_NAMES = ["policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent", "doctor", "watch", "keys", "mcp", "alerts", "webhooks"];
|
|
949
1372
|
export {
|
|
950
1373
|
COMMAND_NAMES,
|
|
951
1374
|
runCommand
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function run(argv: string[]): Promise<number>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function run(argv: string[]): Promise<number>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function run(argv: string[]): Promise<number>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function run(argv: string[]): Promise<number>;
|