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