@solongate/proxy 0.77.2 → 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/audit.d.ts +8 -0
- 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/policies.d.ts +1 -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 +421 -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 +710 -121
- package/dist/tui/index.js +197 -39
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6867,9 +6867,15 @@ var init_policies = __esm({
|
|
|
6867
6867
|
var settings_exports = {};
|
|
6868
6868
|
__export(settings_exports, {
|
|
6869
6869
|
clearRateLimitHistory: () => clearRateLimitHistory,
|
|
6870
|
+
createAlert: () => createAlert,
|
|
6871
|
+
createWebhook: () => createWebhook,
|
|
6872
|
+
deleteAlert: () => deleteAlert,
|
|
6873
|
+
deleteWebhook: () => deleteWebhook,
|
|
6874
|
+
getAlerts: () => getAlerts,
|
|
6870
6875
|
getGuardStatus: () => getGuardStatus,
|
|
6871
6876
|
getRateLimitHistory: () => getRateLimitHistory,
|
|
6872
6877
|
getSecurityLayers: () => getSecurityLayers,
|
|
6878
|
+
getWebhooks: () => getWebhooks,
|
|
6873
6879
|
setSecurityLayers: () => setSecurityLayers
|
|
6874
6880
|
});
|
|
6875
6881
|
function getSecurityLayers() {
|
|
@@ -6887,6 +6893,24 @@ function clearRateLimitHistory() {
|
|
|
6887
6893
|
function getGuardStatus() {
|
|
6888
6894
|
return request("GET", "/settings/guard-status");
|
|
6889
6895
|
}
|
|
6896
|
+
function getAlerts() {
|
|
6897
|
+
return request("GET", "/settings/denial-alerts");
|
|
6898
|
+
}
|
|
6899
|
+
function createAlert(body) {
|
|
6900
|
+
return request("POST", "/settings/denial-alerts", { body });
|
|
6901
|
+
}
|
|
6902
|
+
function deleteAlert(id) {
|
|
6903
|
+
return request("DELETE", "/settings/denial-alerts", { query: { id } });
|
|
6904
|
+
}
|
|
6905
|
+
function getWebhooks() {
|
|
6906
|
+
return request("GET", "/settings/denial-webhook");
|
|
6907
|
+
}
|
|
6908
|
+
function createWebhook(body) {
|
|
6909
|
+
return request("POST", "/settings/denial-webhook", { body });
|
|
6910
|
+
}
|
|
6911
|
+
function deleteWebhook(id) {
|
|
6912
|
+
return request("DELETE", "/settings/denial-webhook", { query: { id } });
|
|
6913
|
+
}
|
|
6890
6914
|
var init_settings = __esm({
|
|
6891
6915
|
"src/api-client/settings.ts"() {
|
|
6892
6916
|
"use strict";
|
|
@@ -6924,6 +6948,7 @@ var init_stats = __esm({
|
|
|
6924
6948
|
// src/api-client/audit.ts
|
|
6925
6949
|
var audit_exports = {};
|
|
6926
6950
|
__export(audit_exports, {
|
|
6951
|
+
block: () => block,
|
|
6927
6952
|
list: () => list2,
|
|
6928
6953
|
whitelist: () => whitelist
|
|
6929
6954
|
});
|
|
@@ -6933,6 +6958,9 @@ function list2(query = {}) {
|
|
|
6933
6958
|
function whitelist(id, scope = "exact") {
|
|
6934
6959
|
return request("POST", `/audit-logs/${encodeURIComponent(id)}/whitelist`, { body: { scope } });
|
|
6935
6960
|
}
|
|
6961
|
+
function block(id, scope = "exact") {
|
|
6962
|
+
return request("POST", `/audit-logs/${encodeURIComponent(id)}/block`, { body: { scope } });
|
|
6963
|
+
}
|
|
6936
6964
|
var init_audit = __esm({
|
|
6937
6965
|
"src/api-client/audit.ts"() {
|
|
6938
6966
|
"use strict";
|
|
@@ -6967,6 +6995,44 @@ var init_agents = __esm({
|
|
|
6967
6995
|
}
|
|
6968
6996
|
});
|
|
6969
6997
|
|
|
6998
|
+
// src/api-client/keys.ts
|
|
6999
|
+
var keys_exports = {};
|
|
7000
|
+
__export(keys_exports, {
|
|
7001
|
+
create: () => create2,
|
|
7002
|
+
list: () => list3,
|
|
7003
|
+
revoke: () => revoke
|
|
7004
|
+
});
|
|
7005
|
+
function list3() {
|
|
7006
|
+
return request("GET", "/keys");
|
|
7007
|
+
}
|
|
7008
|
+
function create2(name, isLive = true) {
|
|
7009
|
+
return request("POST", "/keys", { body: { name, is_live: isLive } });
|
|
7010
|
+
}
|
|
7011
|
+
function revoke(id) {
|
|
7012
|
+
return request("DELETE", `/keys/${encodeURIComponent(id)}`);
|
|
7013
|
+
}
|
|
7014
|
+
var init_keys = __esm({
|
|
7015
|
+
"src/api-client/keys.ts"() {
|
|
7016
|
+
"use strict";
|
|
7017
|
+
init_client();
|
|
7018
|
+
}
|
|
7019
|
+
});
|
|
7020
|
+
|
|
7021
|
+
// src/api-client/mcp.ts
|
|
7022
|
+
var mcp_exports = {};
|
|
7023
|
+
__export(mcp_exports, {
|
|
7024
|
+
list: () => list4
|
|
7025
|
+
});
|
|
7026
|
+
function list4() {
|
|
7027
|
+
return request("GET", "/mcp-servers");
|
|
7028
|
+
}
|
|
7029
|
+
var init_mcp = __esm({
|
|
7030
|
+
"src/api-client/mcp.ts"() {
|
|
7031
|
+
"use strict";
|
|
7032
|
+
init_client();
|
|
7033
|
+
}
|
|
7034
|
+
});
|
|
7035
|
+
|
|
6970
7036
|
// src/api-client/index.ts
|
|
6971
7037
|
var api;
|
|
6972
7038
|
var init_api_client = __esm({
|
|
@@ -6979,7 +7045,9 @@ var init_api_client = __esm({
|
|
|
6979
7045
|
init_stats();
|
|
6980
7046
|
init_audit();
|
|
6981
7047
|
init_agents();
|
|
6982
|
-
|
|
7048
|
+
init_keys();
|
|
7049
|
+
init_mcp();
|
|
7050
|
+
api = { policies: policies_exports, settings: settings_exports, stats: stats_exports, audit: audit_exports, agents: agents_exports, keys: keys_exports, mcp: mcp_exports };
|
|
6983
7051
|
}
|
|
6984
7052
|
});
|
|
6985
7053
|
|
|
@@ -7052,6 +7120,19 @@ function tailLines(file, maxBytes = 131072) {
|
|
|
7052
7120
|
return [];
|
|
7053
7121
|
}
|
|
7054
7122
|
}
|
|
7123
|
+
function extractTarget(detail) {
|
|
7124
|
+
try {
|
|
7125
|
+
const j = JSON.parse(detail);
|
|
7126
|
+
const cmd = String(j.command ?? j.cmd ?? "").trim();
|
|
7127
|
+
if (cmd) return { kind: "command", value: cmd };
|
|
7128
|
+
const fp = String(j.file_path ?? j.path ?? "").trim();
|
|
7129
|
+
if (fp) return { kind: "path", value: fp.split(/[\\/]/).pop() || fp };
|
|
7130
|
+
const url = String(j.url ?? "").trim();
|
|
7131
|
+
if (url) return { kind: "url", value: url };
|
|
7132
|
+
} catch {
|
|
7133
|
+
}
|
|
7134
|
+
return null;
|
|
7135
|
+
}
|
|
7055
7136
|
function cloudItem(e) {
|
|
7056
7137
|
return {
|
|
7057
7138
|
id: "c:" + e.id,
|
|
@@ -7061,6 +7142,7 @@ function cloudItem(e) {
|
|
|
7061
7142
|
permission: (e.permission ?? "").slice(0, 4),
|
|
7062
7143
|
detail: (e.arguments_summary ? JSON.stringify(e.arguments_summary) : e.reason ?? "").replace(/\s+/g, " "),
|
|
7063
7144
|
dlp: !!e.dlp_matches?.length,
|
|
7145
|
+
burst: !!e.rate_limit_burst,
|
|
7064
7146
|
source: "cloud",
|
|
7065
7147
|
session: e.session_id ?? void 0,
|
|
7066
7148
|
agent: e.agent_name ?? void 0,
|
|
@@ -7116,8 +7198,9 @@ function PaneTitle({ label, extra, width: width2 }) {
|
|
|
7116
7198
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: tail + "\u2500".repeat(Math.max(0, width2 - used)) })
|
|
7117
7199
|
] });
|
|
7118
7200
|
}
|
|
7119
|
-
function StreamLine({ e, loc }) {
|
|
7120
|
-
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7201
|
+
function StreamLine({ e, loc, selected }) {
|
|
7202
|
+
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", backgroundColor: selected ? "#1c2f63" : void 0, children: [
|
|
7203
|
+
/* @__PURE__ */ jsx2(Text2, { color: selected ? theme.accentBright : theme.dim, children: selected ? "\u25B8" : " " }),
|
|
7121
7204
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7122
7205
|
"[",
|
|
7123
7206
|
hhmmss(e.at),
|
|
@@ -7144,9 +7227,14 @@ function LivePanel({ active: active2 }) {
|
|
|
7144
7227
|
const [ring, setRing] = useState2(null);
|
|
7145
7228
|
const [log6, setLog] = useState2([]);
|
|
7146
7229
|
const [filter, setFilter] = useState2("all");
|
|
7230
|
+
const [signal, setSignal] = useState2("none");
|
|
7147
7231
|
const [search, setSearch] = useState2("");
|
|
7148
7232
|
const [editingSearch, setEditingSearch] = useState2(false);
|
|
7149
|
-
const [
|
|
7233
|
+
const [sel, setSel] = useState2(0);
|
|
7234
|
+
const [actionMsg, setActionMsg] = useState2(null);
|
|
7235
|
+
const notifiedRef = useRef2(/* @__PURE__ */ new Set());
|
|
7236
|
+
const notifyReady = useRef2(false);
|
|
7237
|
+
const activePolicyRef = useRef2(null);
|
|
7150
7238
|
const [mode, setMode] = useState2("stream");
|
|
7151
7239
|
const [pickIdx, setPickIdx] = useState2(0);
|
|
7152
7240
|
const [detail, setDetail] = useState2(null);
|
|
@@ -7156,6 +7244,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7156
7244
|
const lastLocalTs = useRef2(0);
|
|
7157
7245
|
const pausedUntil = useRef2(0);
|
|
7158
7246
|
const sessRef = useRef2([]);
|
|
7247
|
+
const mergedRef = useRef2([]);
|
|
7159
7248
|
const prevStatus = useRef2(/* @__PURE__ */ new Map());
|
|
7160
7249
|
const [alerts, setAlerts] = useState2([]);
|
|
7161
7250
|
const [frozen, setFrozen] = useState2(false);
|
|
@@ -7204,6 +7293,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7204
7293
|
permission: (j.permission ?? "").slice(0, 4),
|
|
7205
7294
|
detail: (j.arguments ? JSON.stringify(j.arguments) : j.reason ?? "").replace(/\s+/g, " "),
|
|
7206
7295
|
dlp: !!j.dlp,
|
|
7296
|
+
burst: !!j.rate_limit_burst,
|
|
7207
7297
|
source: "local",
|
|
7208
7298
|
session: j.session_id,
|
|
7209
7299
|
agent: j.agent_name,
|
|
@@ -7318,30 +7408,52 @@ function LivePanel({ active: active2 }) {
|
|
|
7318
7408
|
return () => clearInterval(t);
|
|
7319
7409
|
}, [active2, frozen]);
|
|
7320
7410
|
const startRef = useRef2(Date.now());
|
|
7411
|
+
const fireAlert = useCallback2(
|
|
7412
|
+
(id, title, msg, level = "warn") => {
|
|
7413
|
+
pushLog(`${level === "bad" ? "\u26D4" : "\u23F8"} ${msg}`, level);
|
|
7414
|
+
setAlerts((a) => [...a.filter((x) => x.id !== id), { id, msg, level, until: Date.now() + 15e3 }].slice(-4));
|
|
7415
|
+
try {
|
|
7416
|
+
process.stdout.write("\x07");
|
|
7417
|
+
} catch {
|
|
7418
|
+
}
|
|
7419
|
+
try {
|
|
7420
|
+
const child = spawn("notify-send", ["-a", "SolonGate", title, msg], { stdio: "ignore", detached: true });
|
|
7421
|
+
child.on("error", () => {
|
|
7422
|
+
});
|
|
7423
|
+
child.unref();
|
|
7424
|
+
} catch {
|
|
7425
|
+
}
|
|
7426
|
+
},
|
|
7427
|
+
[pushLog]
|
|
7428
|
+
);
|
|
7321
7429
|
useEffect2(() => {
|
|
7322
7430
|
if (!active2) return;
|
|
7323
7431
|
for (const r of sessRef.current) {
|
|
7324
7432
|
const prev = prevStatus.current.get(r.id);
|
|
7325
7433
|
if (prev && prev === "active" && r.status === "idle") {
|
|
7326
7434
|
const name = r.isMe ? `${r.agent} (this machine)` : r.agent;
|
|
7327
|
-
|
|
7328
|
-
pushLog(`\u23F8 ${msg}`, "warn");
|
|
7329
|
-
setAlerts((a) => [...a.filter((x) => x.id !== r.id), { id: r.id, msg, until: Date.now() + 15e3 }].slice(-4));
|
|
7330
|
-
try {
|
|
7331
|
-
process.stdout.write("\x07");
|
|
7332
|
-
} catch {
|
|
7333
|
-
}
|
|
7334
|
-
try {
|
|
7335
|
-
const child = spawn("notify-send", ["-a", "SolonGate", "Agent idle", msg], { stdio: "ignore", detached: true });
|
|
7336
|
-
child.on("error", () => {
|
|
7337
|
-
});
|
|
7338
|
-
child.unref();
|
|
7339
|
-
} catch {
|
|
7340
|
-
}
|
|
7435
|
+
fireAlert("idle:" + r.id, "Agent idle", `${name} went IDLE, no tool call for 60s`, "warn");
|
|
7341
7436
|
}
|
|
7342
7437
|
prevStatus.current.set(r.id, r.status);
|
|
7343
7438
|
}
|
|
7344
|
-
}, [tick, active2,
|
|
7439
|
+
}, [tick, active2, fireAlert]);
|
|
7440
|
+
useEffect2(() => {
|
|
7441
|
+
if (!active2) return;
|
|
7442
|
+
const notable = mergedRef.current.filter((e) => e.decision !== "ALLOW" || e.dlp || e.burst);
|
|
7443
|
+
if (!notifyReady.current) {
|
|
7444
|
+
for (const e of notable) notifiedRef.current.add(e.id);
|
|
7445
|
+
notifyReady.current = true;
|
|
7446
|
+
return;
|
|
7447
|
+
}
|
|
7448
|
+
for (const e of notable) {
|
|
7449
|
+
if (notifiedRef.current.has(e.id)) continue;
|
|
7450
|
+
notifiedRef.current.add(e.id);
|
|
7451
|
+
const who = e.agent ? ` \xB7 ${truncate2(e.agent, 20)}` : "";
|
|
7452
|
+
if (e.dlp) fireAlert("sec:" + e.id, "DLP hit", `SECRET in ${e.tool}${who}: ${truncate2(e.detail, 60)}`, "bad");
|
|
7453
|
+
else if (e.decision !== "ALLOW") fireAlert("sec:" + e.id, "Call denied", `DENY ${e.tool}${who}: ${truncate2(e.rule ?? e.detail, 60)}`, "bad");
|
|
7454
|
+
else if (e.burst) fireAlert("sec:" + e.id, "Rate-limit burst", `BURST ${e.tool}${who}`, "warn");
|
|
7455
|
+
}
|
|
7456
|
+
}, [tick, active2, fireAlert]);
|
|
7345
7457
|
const cols = process.stdout.columns ?? 100;
|
|
7346
7458
|
const rows = process.stdout.rows ?? 30;
|
|
7347
7459
|
const ins = insights.data ?? {};
|
|
@@ -7354,6 +7466,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7354
7466
|
}
|
|
7355
7467
|
const cloudDeduped = cloudBuf.filter((e) => !localKeys.has(`${e.tool}|${e.decision}|${Math.round(e.at / 6e4)}`));
|
|
7356
7468
|
const mergedAll = [...cloudDeduped, ...localBuf].sort((a, b) => a.at - b.at);
|
|
7469
|
+
mergedRef.current = mergedAll;
|
|
7357
7470
|
const nowSlot = Math.floor(nowMs / 1e4);
|
|
7358
7471
|
const traffic = new Array(60).fill(0);
|
|
7359
7472
|
const trafficHot = new Array(60).fill(false);
|
|
@@ -7413,7 +7526,8 @@ function LivePanel({ active: active2 }) {
|
|
|
7413
7526
|
const isLoc = (e) => e.source === "local" || !!e.session && localSessIds.has(e.session);
|
|
7414
7527
|
const q = search.trim().toLowerCase();
|
|
7415
7528
|
const matches = (e) => !q || `${e.tool} ${e.agent ?? ""} ${e.decision} ${e.permission} ${e.rule ?? ""} ${e.detail}`.toLowerCase().includes(q);
|
|
7416
|
-
const
|
|
7529
|
+
const signalOk = (e) => signal === "none" ? true : signal === "deny" ? e.decision !== "ALLOW" : signal === "dlp" ? e.dlp : e.burst;
|
|
7530
|
+
const filtered = (filter === "all" ? mergedAll : mergedAll.filter((e) => filter === "local" ? isLoc(e) : !isLoc(e))).filter(matches).filter(signalOk);
|
|
7417
7531
|
const visibleDesc = filtered.slice().reverse();
|
|
7418
7532
|
const chartH = rows >= 36 ? 6 : 4;
|
|
7419
7533
|
const colH = rows >= 32 ? 7 : 5;
|
|
@@ -7424,9 +7538,11 @@ function LivePanel({ active: active2 }) {
|
|
|
7424
7538
|
const colW = Math.max(20, Math.floor((innerW - 4) / 3));
|
|
7425
7539
|
const pickable = combinedSess.slice(0, colH - 1);
|
|
7426
7540
|
const streamBodyRows = Math.max(3, streamRows - 1);
|
|
7541
|
+
const selClamped = Math.min(Math.max(0, sel), Math.max(0, visibleDesc.length - 1));
|
|
7427
7542
|
const maxScroll = Math.max(0, visibleDesc.length - streamBodyRows);
|
|
7428
|
-
const clampedScroll = Math.min(
|
|
7543
|
+
const clampedScroll = Math.min(Math.max(0, selClamped - Math.floor((streamBodyRows - 1) / 2)), maxScroll);
|
|
7429
7544
|
const windowed = visibleDesc.slice(clampedScroll, clampedScroll + streamBodyRows);
|
|
7545
|
+
const selEntry = visibleDesc[selClamped];
|
|
7430
7546
|
const detailEntries = detail ? (() => {
|
|
7431
7547
|
const seen = /* @__PURE__ */ new Set();
|
|
7432
7548
|
const all = [];
|
|
@@ -7438,6 +7554,42 @@ function LivePanel({ active: active2 }) {
|
|
|
7438
7554
|
}
|
|
7439
7555
|
return all.sort((a, b) => b.at - a.at);
|
|
7440
7556
|
})() : [];
|
|
7557
|
+
const doAction = async (act) => {
|
|
7558
|
+
const e = selEntry;
|
|
7559
|
+
if (!e) return;
|
|
7560
|
+
const ok = act === "whitelist" ? e.decision !== "ALLOW" : e.decision === "ALLOW";
|
|
7561
|
+
if (!ok) {
|
|
7562
|
+
setActionMsg({ text: act === "whitelist" ? "select a DENY to whitelist (w)" : "select an ALLOW to block (b)", level: "bad", until: Date.now() + 4e3 });
|
|
7563
|
+
return;
|
|
7564
|
+
}
|
|
7565
|
+
setActionMsg({ text: act === "whitelist" ? "whitelisting\u2026" : "blocking\u2026", level: "ok", until: Date.now() + 4e3 });
|
|
7566
|
+
try {
|
|
7567
|
+
let res;
|
|
7568
|
+
if (e.source === "cloud") {
|
|
7569
|
+
const realId = e.id.slice(2);
|
|
7570
|
+
res = act === "whitelist" ? await api.audit.whitelist(realId) : await api.audit.block(realId);
|
|
7571
|
+
} else {
|
|
7572
|
+
let pid = activePolicyRef.current;
|
|
7573
|
+
if (!pid) {
|
|
7574
|
+
const a = await api.policies.active();
|
|
7575
|
+
pid = a.policy?.id ?? null;
|
|
7576
|
+
activePolicyRef.current = pid;
|
|
7577
|
+
}
|
|
7578
|
+
if (!pid) throw new Error("no active policy resolves for this machine");
|
|
7579
|
+
const t = extractTarget(e.detail);
|
|
7580
|
+
res = await api.policies.addRule(pid, { toolPattern: e.tool, kind: t?.kind ?? "tool", value: t?.value, effect: act === "whitelist" ? "ALLOW" : "DENY" });
|
|
7581
|
+
}
|
|
7582
|
+
const verb = act === "whitelist" ? "ALLOW" : "DENY";
|
|
7583
|
+
setActionMsg({
|
|
7584
|
+
text: res.deduped ? `${verb} rule already present` : `${verb} rule added \u2192 ${res.policy_id ?? "?"}${res.policy_version ? " v" + res.policy_version : ""} \xB7 reaches agents in ~30s`,
|
|
7585
|
+
level: "ok",
|
|
7586
|
+
until: Date.now() + 7e3
|
|
7587
|
+
});
|
|
7588
|
+
pushLog(`${act === "whitelist" ? "\u2713 whitelisted" : "\u26D4 blocked"} ${e.tool}`, act === "whitelist" ? "ok" : "warn");
|
|
7589
|
+
} catch (err2) {
|
|
7590
|
+
setActionMsg({ text: "\u2717 " + (err2 instanceof Error ? err2.message : String(err2)), level: "bad", until: Date.now() + 7e3 });
|
|
7591
|
+
}
|
|
7592
|
+
};
|
|
7441
7593
|
useInput(
|
|
7442
7594
|
(input, key) => {
|
|
7443
7595
|
if (input === " ") {
|
|
@@ -7472,16 +7624,25 @@ function LivePanel({ active: active2 }) {
|
|
|
7472
7624
|
} else if (input === "s" || key.leftArrow) setMode("stream");
|
|
7473
7625
|
return;
|
|
7474
7626
|
}
|
|
7627
|
+
const toggleSignal = (s2) => {
|
|
7628
|
+
setSignal((cur) => cur === s2 ? "none" : s2);
|
|
7629
|
+
setSel(0);
|
|
7630
|
+
};
|
|
7475
7631
|
if (input === "f") {
|
|
7476
7632
|
setFilter((f) => FILTERS[(FILTERS.indexOf(f) + 1) % FILTERS.length]);
|
|
7477
|
-
|
|
7633
|
+
setSel(0);
|
|
7478
7634
|
} else if (input === "s") {
|
|
7479
7635
|
setPickIdx(0);
|
|
7480
7636
|
setMode("pick");
|
|
7481
|
-
} else if (
|
|
7482
|
-
else if (
|
|
7483
|
-
else if (
|
|
7484
|
-
else if (
|
|
7637
|
+
} else if (input === "w") void doAction("whitelist");
|
|
7638
|
+
else if (input === "b") void doAction("block");
|
|
7639
|
+
else if (input === "d") toggleSignal("deny");
|
|
7640
|
+
else if (input === "x") toggleSignal("dlp");
|
|
7641
|
+
else if (input === "r") toggleSignal("ratelimit");
|
|
7642
|
+
else if (key.downArrow) setSel((n) => Math.min(visibleDesc.length - 1, n + 1));
|
|
7643
|
+
else if (key.upArrow) setSel((n) => Math.max(0, n - 1));
|
|
7644
|
+
else if (key.pageDown) setSel((n) => Math.min(visibleDesc.length - 1, n + streamBodyRows));
|
|
7645
|
+
else if (key.pageUp) setSel((n) => Math.max(0, n - streamBodyRows));
|
|
7485
7646
|
},
|
|
7486
7647
|
{ isActive: active2 && !editingSearch }
|
|
7487
7648
|
);
|
|
@@ -7498,7 +7659,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7498
7659
|
return;
|
|
7499
7660
|
}
|
|
7500
7661
|
setSearch(v);
|
|
7501
|
-
|
|
7662
|
+
setSel(0);
|
|
7502
7663
|
setDetailScroll(0);
|
|
7503
7664
|
},
|
|
7504
7665
|
onSubmit: () => setEditingSearch(false)
|
|
@@ -7618,7 +7779,18 @@ function LivePanel({ active: active2 }) {
|
|
|
7618
7779
|
/* @__PURE__ */ jsx2(Text2, { color: guard.data?.up_to_date ? theme.ok : theme.warn, children: guard.data ? `v${guard.data.installed ?? "?"}${guard.data.up_to_date ? "" : "\u2192v" + guard.data.latest} ${guard.data.device_count}dev` : "\xB7" }),
|
|
7619
7780
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
7620
7781
|
] }),
|
|
7621
|
-
alerts.filter((a) => nowMs < a.until).slice(-1).map((a) => /* @__PURE__ */ jsx2(
|
|
7782
|
+
alerts.filter((a) => nowMs < a.until).slice(-1).map((a) => /* @__PURE__ */ jsx2(
|
|
7783
|
+
Text2,
|
|
7784
|
+
{
|
|
7785
|
+
wrap: "truncate",
|
|
7786
|
+
backgroundColor: a.level === "bad" ? "#3d1220" : "#3d2a12",
|
|
7787
|
+
color: a.level === "bad" ? "#ff6b6b" : "#ffb454",
|
|
7788
|
+
bold: true,
|
|
7789
|
+
children: ` ${a.level === "bad" ? "\u26D4 ALERT" : "\u23F8 IDLE"} \xB7 ${truncate2(a.msg, innerW - 14)} `
|
|
7790
|
+
},
|
|
7791
|
+
a.id
|
|
7792
|
+
)),
|
|
7793
|
+
actionMsg && nowMs < actionMsg.until ? /* @__PURE__ */ jsx2(Text2, { wrap: "truncate", backgroundColor: actionMsg.level === "bad" ? "#3d1220" : "#123d1f", color: actionMsg.level === "bad" ? "#ff6b6b" : "#7bd88f", bold: true, children: ` ${truncate2(actionMsg.text, innerW - 4)} ` }) : null,
|
|
7622
7794
|
/* @__PURE__ */ jsxs2(Box2, { height: 1 + chartH, children: [
|
|
7623
7795
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: leftW, marginRight: 2, children: [
|
|
7624
7796
|
/* @__PURE__ */ jsx2(PaneTitle, { label: "TRAFFIC", extra: `${trafficLabel} \xB7 peak ${Math.max(0, ...traffic)} \xB7 red = denials`, width: leftW }),
|
|
@@ -7656,11 +7828,11 @@ function LivePanel({ active: active2 }) {
|
|
|
7656
7828
|
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: mode === "pick" ? "\u2191\u2193 pick \xB7 enter open" : `${combinedSess.length} \xB7 s = inspect`, width: colW }),
|
|
7657
7829
|
pickable.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "scanning\u2026" }) : null,
|
|
7658
7830
|
pickable.map((r, i) => {
|
|
7659
|
-
const
|
|
7831
|
+
const sel2 = mode === "pick" && i === pickIdx;
|
|
7660
7832
|
const st = STATUS_STYLE[r.status];
|
|
7661
|
-
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", backgroundColor:
|
|
7662
|
-
/* @__PURE__ */ jsxs2(Text2, { color:
|
|
7663
|
-
|
|
7833
|
+
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", backgroundColor: sel2 ? "#1c2f63" : void 0, bold: sel2 || r.isMe, children: [
|
|
7834
|
+
/* @__PURE__ */ jsxs2(Text2, { color: sel2 ? theme.accentBright : st.color, children: [
|
|
7835
|
+
sel2 ? "\u25B8" : st.dot,
|
|
7664
7836
|
" "
|
|
7665
7837
|
] }),
|
|
7666
7838
|
/* @__PURE__ */ jsx2(Text2, { color: st.color, children: st.label.padEnd(7) }),
|
|
@@ -7693,7 +7865,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7693
7865
|
PaneTitle,
|
|
7694
7866
|
{
|
|
7695
7867
|
label: "TOOL STREAM",
|
|
7696
|
-
extra: `${visibleDesc.length}
|
|
7868
|
+
extra: `${selClamped + 1}/${visibleDesc.length}${signal !== "none" ? ` \xB7 ${signal}` : ""}${q ? " \xB7 search" : ""} \xB7 \u2191\u2193 select \xB7 w allow \xB7 b block \xB7 d/x/r ${filter} \xB7 s`,
|
|
7697
7869
|
width: innerW
|
|
7698
7870
|
}
|
|
7699
7871
|
),
|
|
@@ -7707,12 +7879,12 @@ function LivePanel({ active: active2 }) {
|
|
|
7707
7879
|
spin,
|
|
7708
7880
|
" awaiting traffic\u2026"
|
|
7709
7881
|
] }) : null,
|
|
7710
|
-
windowed.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e) }, e.id))
|
|
7882
|
+
windowed.map((e, i) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e), selected: clampedScroll + i === selClamped }, e.id))
|
|
7711
7883
|
] }),
|
|
7712
7884
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7713
7885
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " LIVE " }),
|
|
7714
7886
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` local ${localOn ? "on" : "off"} \xB7 ${localBuf.length} loc/${cloudBuf.length} cld \xB7 top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} ` }),
|
|
7715
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: "
|
|
7887
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " \u2191\u2193 select \xB7 w allow \xB7 b block \xB7 d/x/r \xB7 / search \xB7 f \xB7 s \xB7 space copy \xB7 esc \xB7 q " })
|
|
7716
7888
|
] })
|
|
7717
7889
|
] });
|
|
7718
7890
|
}
|
|
@@ -7762,8 +7934,8 @@ function ruleSummary(r) {
|
|
|
7762
7934
|
return bits.join(" ");
|
|
7763
7935
|
}
|
|
7764
7936
|
function PoliciesPanel({ focused }) {
|
|
7765
|
-
const
|
|
7766
|
-
const policies =
|
|
7937
|
+
const list6 = useLoader(() => api.policies.list());
|
|
7938
|
+
const policies = list6.data?.policies ?? [];
|
|
7767
7939
|
const [view, setView] = useState3("list");
|
|
7768
7940
|
const [pi, setPi] = useState3(0);
|
|
7769
7941
|
const [ri, setRi] = useState3(0);
|
|
@@ -7805,7 +7977,7 @@ function PoliciesPanel({ focused }) {
|
|
|
7805
7977
|
setRules(res.rules);
|
|
7806
7978
|
setDirty(false);
|
|
7807
7979
|
setStatus("\u2713 Saved v" + res._version);
|
|
7808
|
-
|
|
7980
|
+
list6.reload();
|
|
7809
7981
|
} catch (e) {
|
|
7810
7982
|
setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
|
|
7811
7983
|
}
|
|
@@ -7875,7 +8047,7 @@ function PoliciesPanel({ focused }) {
|
|
|
7875
8047
|
{ isActive: focused && !editing }
|
|
7876
8048
|
);
|
|
7877
8049
|
if (view === "list") {
|
|
7878
|
-
return /* @__PURE__ */ jsx3(DataView, { loading:
|
|
8050
|
+
return /* @__PURE__ */ jsx3(DataView, { loading: list6.loading && !list6.data, error: list6.error, empty: !!list6.data && policies.length === 0, emptyText: "No policies.", children: /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
7879
8051
|
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: focused ? "\u2191\u2193 select \xB7 enter open" : "press \u2192 to browse" }),
|
|
7880
8052
|
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, flexDirection: "column", children: policies.map((p, i) => /* @__PURE__ */ jsxs3(Text3, { color: i === pi ? theme.accentBright : void 0, bold: i === pi, children: [
|
|
7881
8053
|
(i === pi ? "\u25B8 " : " ") + truncate2(p.name, 26).padEnd(27),
|
|
@@ -9226,6 +9398,16 @@ async function run5(argv) {
|
|
|
9226
9398
|
else err(green(` \u2713 Whitelisted (${res2.scope})`) + dim(` \u2192 ${res2.policy_id} v${res2.policy_version}`));
|
|
9227
9399
|
return 0;
|
|
9228
9400
|
}
|
|
9401
|
+
if (positionals[0] === "block") {
|
|
9402
|
+
const id = positionals[1];
|
|
9403
|
+
if (!id) return err(" Usage: audit block <logId> [--scope exact|tool]"), 1;
|
|
9404
|
+
const scope = flagStr(flags, "scope") ?? "exact";
|
|
9405
|
+
const res2 = await api.audit.block(id, scope);
|
|
9406
|
+
if (json) return printJson(res2), 0;
|
|
9407
|
+
if (res2.deduped) err(green(" \u2713 ") + dim("Equivalent DENY already present."));
|
|
9408
|
+
else err(green(` \u2713 Blocked (${res2.scope})`) + dim(` \u2192 ${res2.policy_id} v${res2.policy_version}`));
|
|
9409
|
+
return 0;
|
|
9410
|
+
}
|
|
9229
9411
|
const query = {
|
|
9230
9412
|
filter: flagStr(flags, "filter"),
|
|
9231
9413
|
tool: flagStr(flags, "tool"),
|
|
@@ -9333,6 +9515,395 @@ var init_agents2 = __esm({
|
|
|
9333
9515
|
}
|
|
9334
9516
|
});
|
|
9335
9517
|
|
|
9518
|
+
// src/commands/doctor.ts
|
|
9519
|
+
import { existsSync as existsSync4, statSync as statSync2 } from "fs";
|
|
9520
|
+
import { homedir as homedir4 } from "os";
|
|
9521
|
+
import { join as join6 } from "path";
|
|
9522
|
+
async function run6(argv) {
|
|
9523
|
+
const { flags } = parse(argv);
|
|
9524
|
+
const json = flagBool(flags, "json");
|
|
9525
|
+
const checks = [];
|
|
9526
|
+
if (!isAuthenticated()) {
|
|
9527
|
+
checks.push({ name: "login", ok: false, detail: "not logged in \u2014 run `solongate login`" });
|
|
9528
|
+
} else {
|
|
9529
|
+
const { apiUrl } = resolveCredentials();
|
|
9530
|
+
checks.push({ name: "login", ok: true, detail: `paired \xB7 ${apiUrl}` });
|
|
9531
|
+
try {
|
|
9532
|
+
const active2 = await api.policies.active();
|
|
9533
|
+
if (active2.policy) {
|
|
9534
|
+
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}` });
|
|
9535
|
+
} else {
|
|
9536
|
+
checks.push({ name: "active policy", ok: "warn", detail: "no policy resolves \u2014 every call falls back to default" });
|
|
9537
|
+
}
|
|
9538
|
+
const sec = active2.security;
|
|
9539
|
+
checks.push({ name: "rate limit", ok: sec?.rateLimit ? true : "warn", detail: sec?.rateLimit ? `${sec.rateLimit.perMinute}/min` : "off" });
|
|
9540
|
+
checks.push({ name: "dlp", ok: sec?.dlpBlock ? true : "warn", detail: sec?.dlpBlock ? `block \xB7 ${sec.dlpBlock.patterns.length} patterns` : sec?.dlpRedact ? "redact" : "off" });
|
|
9541
|
+
checks.push({ name: "self-protection", ok: active2.self_protection_enabled ? true : "warn", detail: active2.self_protection_enabled ? "on" : "off" });
|
|
9542
|
+
} catch (e) {
|
|
9543
|
+
checks.push({ name: "api", ok: false, detail: "unreachable: " + (e instanceof Error ? e.message : String(e)) });
|
|
9544
|
+
}
|
|
9545
|
+
try {
|
|
9546
|
+
const g = await api.settings.getGuardStatus();
|
|
9547
|
+
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\`` });
|
|
9548
|
+
} catch {
|
|
9549
|
+
}
|
|
9550
|
+
}
|
|
9551
|
+
if (existsSync4(LOCAL_LOG2)) {
|
|
9552
|
+
const st = statSync2(LOCAL_LOG2);
|
|
9553
|
+
const ageMin = (Date.now() - st.mtimeMs) / 6e4;
|
|
9554
|
+
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"}` });
|
|
9555
|
+
} else {
|
|
9556
|
+
checks.push({ name: "local logs", ok: "warn", detail: "off (logs go to cloud) \u2014 enable in dashboard \u2192 Settings" });
|
|
9557
|
+
}
|
|
9558
|
+
if (json) return printJson(checks), checks.some((c2) => c2.ok === false) ? 1 : 0;
|
|
9559
|
+
err("");
|
|
9560
|
+
err(` ${bold("SolonGate doctor")}`);
|
|
9561
|
+
err("");
|
|
9562
|
+
for (const c2 of checks) {
|
|
9563
|
+
const mark = c2.ok === true ? green("\u2713") : c2.ok === "warn" ? yellow("!") : red("\u2717");
|
|
9564
|
+
err(` ${mark} ${c2.name.padEnd(16)} ${dim(c2.detail)}`);
|
|
9565
|
+
}
|
|
9566
|
+
err("");
|
|
9567
|
+
const bad = checks.filter((c2) => c2.ok === false).length;
|
|
9568
|
+
const warn = checks.filter((c2) => c2.ok === "warn").length;
|
|
9569
|
+
if (bad) err(` ${red(`${bad} problem(s)`)}${warn ? dim(` \xB7 ${warn} warning(s)`) : ""}`);
|
|
9570
|
+
else if (warn) err(` ${yellow(`${warn} warning(s)`)} ${dim("\u2014 guard is working")}`);
|
|
9571
|
+
else err(` ${green("all good")}`);
|
|
9572
|
+
return bad ? 1 : 0;
|
|
9573
|
+
}
|
|
9574
|
+
var LOCAL_LOG2;
|
|
9575
|
+
var init_doctor = __esm({
|
|
9576
|
+
"src/commands/doctor.ts"() {
|
|
9577
|
+
"use strict";
|
|
9578
|
+
init_api_client();
|
|
9579
|
+
init_format();
|
|
9580
|
+
init_args();
|
|
9581
|
+
LOCAL_LOG2 = join6(homedir4(), ".solongate", "local-logs", "solongate-audit.jsonl");
|
|
9582
|
+
}
|
|
9583
|
+
});
|
|
9584
|
+
|
|
9585
|
+
// src/commands/watch.ts
|
|
9586
|
+
import { closeSync as closeSync2, existsSync as existsSync5, openSync as openSync2, readSync as readSync2, statSync as statSync3 } from "fs";
|
|
9587
|
+
import { homedir as homedir5 } from "os";
|
|
9588
|
+
import { join as join7 } from "path";
|
|
9589
|
+
function tailLocal(file, maxBytes = 131072) {
|
|
9590
|
+
try {
|
|
9591
|
+
const size = statSync3(file).size;
|
|
9592
|
+
const start = Math.max(0, size - maxBytes);
|
|
9593
|
+
const fd = openSync2(file, "r");
|
|
9594
|
+
const buf = Buffer.alloc(size - start);
|
|
9595
|
+
readSync2(fd, buf, 0, buf.length, start);
|
|
9596
|
+
closeSync2(fd);
|
|
9597
|
+
const lines = buf.toString("utf-8").split("\n").filter(Boolean);
|
|
9598
|
+
if (start > 0) lines.shift();
|
|
9599
|
+
return lines;
|
|
9600
|
+
} catch {
|
|
9601
|
+
return [];
|
|
9602
|
+
}
|
|
9603
|
+
}
|
|
9604
|
+
function print(r, json) {
|
|
9605
|
+
if (json) return out(JSON.stringify(r));
|
|
9606
|
+
const dec = r.decision === "ALLOW" ? c.green : c.red;
|
|
9607
|
+
const src = r.source === "local" ? c.green + "LOC" : c.blue4 + "CLD";
|
|
9608
|
+
out(
|
|
9609
|
+
`${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}`
|
|
9610
|
+
);
|
|
9611
|
+
}
|
|
9612
|
+
async function run7(argv) {
|
|
9613
|
+
const { flags } = parse(argv);
|
|
9614
|
+
const json = flagBool(flags, "json");
|
|
9615
|
+
const decisionFilter = (flagStr(flags, "filter") || "").toUpperCase();
|
|
9616
|
+
const toolFilter = (flagStr(flags, "tool") || "").toLowerCase();
|
|
9617
|
+
const localOnly = flagBool(flags, "local-only");
|
|
9618
|
+
const cloudOnly = flagBool(flags, "cloud-only");
|
|
9619
|
+
const keep = (r) => {
|
|
9620
|
+
if (decisionFilter && r.decision.toUpperCase() !== decisionFilter && !(decisionFilter === "DENY" && r.decision === "DENIED")) return false;
|
|
9621
|
+
if (toolFilter && !r.tool.toLowerCase().includes(toolFilter)) return false;
|
|
9622
|
+
return true;
|
|
9623
|
+
};
|
|
9624
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9625
|
+
let lastLocalTs = 0;
|
|
9626
|
+
let first = true;
|
|
9627
|
+
const emit = (rows) => {
|
|
9628
|
+
rows.sort((a, b) => a.at - b.at);
|
|
9629
|
+
for (const r of rows) if (keep(r)) print(r, json);
|
|
9630
|
+
};
|
|
9631
|
+
const pollLocal = () => {
|
|
9632
|
+
if (cloudOnly || !existsSync5(LOCAL_LOG3)) return;
|
|
9633
|
+
const rows = [];
|
|
9634
|
+
for (const line of tailLocal(LOCAL_LOG3)) {
|
|
9635
|
+
try {
|
|
9636
|
+
const j = JSON.parse(line);
|
|
9637
|
+
const at = Date.parse(String(j.ts ?? ""));
|
|
9638
|
+
if (!Number.isFinite(at) || at <= lastLocalTs) continue;
|
|
9639
|
+
rows.push({
|
|
9640
|
+
at,
|
|
9641
|
+
tool: String(j.tool ?? "?"),
|
|
9642
|
+
decision: String(j.decision ?? "ALLOW"),
|
|
9643
|
+
permission: String(j.permission ?? ""),
|
|
9644
|
+
detail: (j.arguments ? JSON.stringify(j.arguments) : String(j.reason ?? "")).replace(/\s+/g, " "),
|
|
9645
|
+
agent: String(j.agent_name ?? ""),
|
|
9646
|
+
source: "local",
|
|
9647
|
+
dlp: !!j.dlp
|
|
9648
|
+
});
|
|
9649
|
+
} catch {
|
|
9650
|
+
}
|
|
9651
|
+
}
|
|
9652
|
+
if (rows.length) lastLocalTs = rows[rows.length - 1].at;
|
|
9653
|
+
if (!first) emit(rows);
|
|
9654
|
+
};
|
|
9655
|
+
const pollCloud = async () => {
|
|
9656
|
+
if (localOnly) return;
|
|
9657
|
+
try {
|
|
9658
|
+
const res = await api.audit.list({ limit: 50 });
|
|
9659
|
+
const rows = [];
|
|
9660
|
+
for (const e of res.entries) {
|
|
9661
|
+
if (seen.has(e.id)) continue;
|
|
9662
|
+
seen.add(e.id);
|
|
9663
|
+
rows.push({
|
|
9664
|
+
at: Date.parse(e.created_at),
|
|
9665
|
+
tool: e.tool_name,
|
|
9666
|
+
decision: e.decision,
|
|
9667
|
+
permission: e.permission ?? "",
|
|
9668
|
+
detail: (e.arguments_summary ? JSON.stringify(e.arguments_summary) : e.reason ?? "").replace(/\s+/g, " "),
|
|
9669
|
+
agent: e.agent_name ?? "",
|
|
9670
|
+
source: "cloud",
|
|
9671
|
+
dlp: !!e.dlp_matches?.length
|
|
9672
|
+
});
|
|
9673
|
+
}
|
|
9674
|
+
if (!first) emit(rows);
|
|
9675
|
+
} catch {
|
|
9676
|
+
}
|
|
9677
|
+
};
|
|
9678
|
+
if (!json) err(` ${c.dim}watching guard stream \u2014 Ctrl+C to stop${c.reset}`);
|
|
9679
|
+
pollLocal();
|
|
9680
|
+
await pollCloud();
|
|
9681
|
+
first = false;
|
|
9682
|
+
return new Promise(() => {
|
|
9683
|
+
setInterval(pollLocal, 2e3);
|
|
9684
|
+
setInterval(() => void pollCloud(), 4e3);
|
|
9685
|
+
});
|
|
9686
|
+
}
|
|
9687
|
+
var LOCAL_LOG3, trunc, time;
|
|
9688
|
+
var init_watch = __esm({
|
|
9689
|
+
"src/commands/watch.ts"() {
|
|
9690
|
+
"use strict";
|
|
9691
|
+
init_api_client();
|
|
9692
|
+
init_cli_utils();
|
|
9693
|
+
init_args();
|
|
9694
|
+
init_format();
|
|
9695
|
+
LOCAL_LOG3 = join7(homedir5(), ".solongate", "local-logs", "solongate-audit.jsonl");
|
|
9696
|
+
trunc = (s, n) => s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
|
|
9697
|
+
time = (ms) => new Date(ms).toTimeString().slice(0, 8);
|
|
9698
|
+
}
|
|
9699
|
+
});
|
|
9700
|
+
|
|
9701
|
+
// src/commands/keys.ts
|
|
9702
|
+
async function run8(argv) {
|
|
9703
|
+
const { positionals, flags } = parse(argv);
|
|
9704
|
+
const sub = positionals[0] ?? "list";
|
|
9705
|
+
const json = flagBool(flags, "json");
|
|
9706
|
+
switch (sub) {
|
|
9707
|
+
case "help":
|
|
9708
|
+
return err(USAGE6), 0;
|
|
9709
|
+
case "list": {
|
|
9710
|
+
const { keys } = await api.keys.list();
|
|
9711
|
+
if (json) return printJson(keys), 0;
|
|
9712
|
+
if (!keys.length) return err(dim(" No API keys.")), 0;
|
|
9713
|
+
table(
|
|
9714
|
+
["ID", "NAME", "PREFIX", "CREATED"],
|
|
9715
|
+
keys.map((k) => [dim(truncate3(k.id, 10)), truncate3(k.name, 24), cyan(k.key_prefix), dim(k.created_at)])
|
|
9716
|
+
);
|
|
9717
|
+
return 0;
|
|
9718
|
+
}
|
|
9719
|
+
case "create": {
|
|
9720
|
+
const name = flagStr(flags, "name");
|
|
9721
|
+
if (!name) return err(" Usage: keys create --name <name>"), 1;
|
|
9722
|
+
const res = await api.keys.create(name, !flagBool(flags, "test"));
|
|
9723
|
+
if (json) return printJson(res), 0;
|
|
9724
|
+
err(green(` \u2713 Created "${res.name}"`));
|
|
9725
|
+
err("");
|
|
9726
|
+
err(" " + bold(res.key));
|
|
9727
|
+
err(dim(" \u2191 shown only once \u2014 copy it now."));
|
|
9728
|
+
return 0;
|
|
9729
|
+
}
|
|
9730
|
+
case "revoke": {
|
|
9731
|
+
const id = positionals[1];
|
|
9732
|
+
if (!id) return err(" Usage: keys revoke <id>"), 1;
|
|
9733
|
+
await api.keys.revoke(id);
|
|
9734
|
+
if (json) return printJson({ ok: true, id }), 0;
|
|
9735
|
+
return err(green(` \u2713 Revoked ${id}`)), 0;
|
|
9736
|
+
}
|
|
9737
|
+
default:
|
|
9738
|
+
return err(USAGE6), 1;
|
|
9739
|
+
}
|
|
9740
|
+
}
|
|
9741
|
+
var USAGE6;
|
|
9742
|
+
var init_keys2 = __esm({
|
|
9743
|
+
"src/commands/keys.ts"() {
|
|
9744
|
+
"use strict";
|
|
9745
|
+
init_api_client();
|
|
9746
|
+
init_args();
|
|
9747
|
+
init_format();
|
|
9748
|
+
USAGE6 = `${bold("solongate keys")} \u2014 API keys
|
|
9749
|
+
|
|
9750
|
+
keys list List keys (prefix only)
|
|
9751
|
+
keys create --name <n> Create a key (secret shown once)
|
|
9752
|
+
keys revoke <id> Revoke a key
|
|
9753
|
+
|
|
9754
|
+
Add --json for machine-readable output.`;
|
|
9755
|
+
}
|
|
9756
|
+
});
|
|
9757
|
+
|
|
9758
|
+
// src/commands/mcp.ts
|
|
9759
|
+
async function run9(argv) {
|
|
9760
|
+
const { flags } = parse(argv);
|
|
9761
|
+
const json = flagBool(flags, "json");
|
|
9762
|
+
const { servers } = await api.mcp.list();
|
|
9763
|
+
if (json) return printJson(servers), 0;
|
|
9764
|
+
if (!servers.length) return err(dim(" No MCP servers registered.")), 0;
|
|
9765
|
+
table(
|
|
9766
|
+
["STATUS", "NAME", "TARGET"],
|
|
9767
|
+
servers.map((s) => [
|
|
9768
|
+
s.status === "active" ? green(s.status) : yellow(s.status),
|
|
9769
|
+
cyan(truncate3(s.name, 24)),
|
|
9770
|
+
dim(truncate3(s.url || s.command || "\u2014", 46))
|
|
9771
|
+
])
|
|
9772
|
+
);
|
|
9773
|
+
return 0;
|
|
9774
|
+
}
|
|
9775
|
+
var init_mcp2 = __esm({
|
|
9776
|
+
"src/commands/mcp.ts"() {
|
|
9777
|
+
"use strict";
|
|
9778
|
+
init_api_client();
|
|
9779
|
+
init_args();
|
|
9780
|
+
init_format();
|
|
9781
|
+
}
|
|
9782
|
+
});
|
|
9783
|
+
|
|
9784
|
+
// src/commands/alerts.ts
|
|
9785
|
+
async function run10(argv) {
|
|
9786
|
+
const { positionals, flags } = parse(argv);
|
|
9787
|
+
const sub = positionals[0] ?? "list";
|
|
9788
|
+
const json = flagBool(flags, "json");
|
|
9789
|
+
switch (sub) {
|
|
9790
|
+
case "help":
|
|
9791
|
+
return err(USAGE7), 0;
|
|
9792
|
+
case "list": {
|
|
9793
|
+
const { rules } = await api.settings.getAlerts();
|
|
9794
|
+
if (json) return printJson(rules), 0;
|
|
9795
|
+
if (!rules.length) return err(dim(" No alert rules.")), 0;
|
|
9796
|
+
table(
|
|
9797
|
+
["ID", "ON", "SIGNAL", "THRESH", "WINDOW", "CHANNELS"],
|
|
9798
|
+
rules.map((r) => [
|
|
9799
|
+
dim(truncate3(r.id, 10)),
|
|
9800
|
+
r.enabled ? green("\u25CF") : dim("\u25CB"),
|
|
9801
|
+
cyan(r.signal),
|
|
9802
|
+
`${r.threshold}`,
|
|
9803
|
+
`${r.windowSeconds}s`,
|
|
9804
|
+
dim([...r.emails ?? [], ...(r.telegram ?? []).map((t) => "tg:" + t), ...(r.slackUrls ?? []).map(() => "slack")].join(" ") || "\u2014")
|
|
9805
|
+
])
|
|
9806
|
+
);
|
|
9807
|
+
return 0;
|
|
9808
|
+
}
|
|
9809
|
+
case "add": {
|
|
9810
|
+
const email = flagStr(flags, "email");
|
|
9811
|
+
const telegram = flagStr(flags, "telegram");
|
|
9812
|
+
const slack = flagStr(flags, "slack");
|
|
9813
|
+
if (!email && !telegram && !slack) return err(" Need a channel: --email / --telegram / --slack"), 1;
|
|
9814
|
+
const res = await api.settings.createAlert({
|
|
9815
|
+
signal: flagStr(flags, "signal") ?? "deny",
|
|
9816
|
+
threshold: flagNum(flags, "threshold") ?? 5,
|
|
9817
|
+
windowSeconds: flagNum(flags, "window") ?? 300,
|
|
9818
|
+
emails: email ? [email] : void 0,
|
|
9819
|
+
telegram: telegram ? [telegram] : void 0,
|
|
9820
|
+
slackUrl: slack
|
|
9821
|
+
});
|
|
9822
|
+
if (json) return printJson(res.rule), 0;
|
|
9823
|
+
return err(green(` \u2713 Alert added (${res.rule.signal}, ${res.rule.threshold}/${res.rule.windowSeconds}s)`)), 0;
|
|
9824
|
+
}
|
|
9825
|
+
case "remove": {
|
|
9826
|
+
const id = positionals[1];
|
|
9827
|
+
if (!id) return err(" Usage: alerts remove <id>"), 1;
|
|
9828
|
+
await api.settings.deleteAlert(id);
|
|
9829
|
+
if (json) return printJson({ ok: true, id }), 0;
|
|
9830
|
+
return err(green(` \u2713 Removed ${id}`)), 0;
|
|
9831
|
+
}
|
|
9832
|
+
default:
|
|
9833
|
+
return err(USAGE7), 1;
|
|
9834
|
+
}
|
|
9835
|
+
}
|
|
9836
|
+
var USAGE7;
|
|
9837
|
+
var init_alerts = __esm({
|
|
9838
|
+
"src/commands/alerts.ts"() {
|
|
9839
|
+
"use strict";
|
|
9840
|
+
init_api_client();
|
|
9841
|
+
init_args();
|
|
9842
|
+
init_format();
|
|
9843
|
+
USAGE7 = `${bold("solongate alerts")} \u2014 spike alerts (Telegram / email)
|
|
9844
|
+
|
|
9845
|
+
alerts list
|
|
9846
|
+
alerts add --signal deny|dlp|ratelimit|any --threshold N --window S
|
|
9847
|
+
(--email <a> | --telegram <chatId> | --slack <url>)
|
|
9848
|
+
alerts remove <id>
|
|
9849
|
+
|
|
9850
|
+
Add --json for machine-readable output.`;
|
|
9851
|
+
}
|
|
9852
|
+
});
|
|
9853
|
+
|
|
9854
|
+
// src/commands/webhooks.ts
|
|
9855
|
+
async function run11(argv) {
|
|
9856
|
+
const { positionals, flags } = parse(argv);
|
|
9857
|
+
const sub = positionals[0] ?? "list";
|
|
9858
|
+
const json = flagBool(flags, "json");
|
|
9859
|
+
switch (sub) {
|
|
9860
|
+
case "help":
|
|
9861
|
+
return err(USAGE8), 0;
|
|
9862
|
+
case "list": {
|
|
9863
|
+
const { webhooks } = await api.settings.getWebhooks();
|
|
9864
|
+
if (json) return printJson(webhooks), 0;
|
|
9865
|
+
if (!webhooks.length) return err(dim(" No webhooks.")), 0;
|
|
9866
|
+
table(
|
|
9867
|
+
["ID", "ON", "EVENTS", "URL"],
|
|
9868
|
+
webhooks.map((w) => [dim(truncate3(w.id, 10)), w.enabled ? green("\u25CF") : dim("\u25CB"), cyan(w.events), dim(truncate3(w.url, 46))])
|
|
9869
|
+
);
|
|
9870
|
+
return 0;
|
|
9871
|
+
}
|
|
9872
|
+
case "add": {
|
|
9873
|
+
const url = flagStr(flags, "url");
|
|
9874
|
+
if (!url) return err(" Usage: webhooks add --url <https://\u2026> [--events denials|allowed|all]"), 1;
|
|
9875
|
+
const res = await api.settings.createWebhook({ url, events: flagStr(flags, "events") ?? "denials" });
|
|
9876
|
+
if (json) return printJson(res.webhook), 0;
|
|
9877
|
+
return err(green(` \u2713 Webhook added (${res.webhook.events}) \u2192 ${res.webhook.url}`)), 0;
|
|
9878
|
+
}
|
|
9879
|
+
case "remove": {
|
|
9880
|
+
const id = positionals[1];
|
|
9881
|
+
if (!id) return err(" Usage: webhooks remove <id>"), 1;
|
|
9882
|
+
await api.settings.deleteWebhook(id);
|
|
9883
|
+
if (json) return printJson({ ok: true, id }), 0;
|
|
9884
|
+
return err(green(` \u2713 Removed ${id}`)), 0;
|
|
9885
|
+
}
|
|
9886
|
+
default:
|
|
9887
|
+
return err(USAGE8), 1;
|
|
9888
|
+
}
|
|
9889
|
+
}
|
|
9890
|
+
var USAGE8;
|
|
9891
|
+
var init_webhooks = __esm({
|
|
9892
|
+
"src/commands/webhooks.ts"() {
|
|
9893
|
+
"use strict";
|
|
9894
|
+
init_api_client();
|
|
9895
|
+
init_args();
|
|
9896
|
+
init_format();
|
|
9897
|
+
USAGE8 = `${bold("solongate webhooks")} \u2014 event webhooks
|
|
9898
|
+
|
|
9899
|
+
webhooks list
|
|
9900
|
+
webhooks add --url <https://\u2026> [--events denials|allowed|all]
|
|
9901
|
+
webhooks remove <id>
|
|
9902
|
+
|
|
9903
|
+
Add --json for machine-readable output.`;
|
|
9904
|
+
}
|
|
9905
|
+
});
|
|
9906
|
+
|
|
9336
9907
|
// src/commands/index.ts
|
|
9337
9908
|
var commands_exports = {};
|
|
9338
9909
|
__export(commands_exports, {
|
|
@@ -9355,6 +9926,18 @@ async function dispatch(command, argv) {
|
|
|
9355
9926
|
return runAgents(argv);
|
|
9356
9927
|
case "agent":
|
|
9357
9928
|
return runAgent(argv);
|
|
9929
|
+
case "doctor":
|
|
9930
|
+
return run6(argv);
|
|
9931
|
+
case "watch":
|
|
9932
|
+
return run7(argv);
|
|
9933
|
+
case "keys":
|
|
9934
|
+
return run8(argv);
|
|
9935
|
+
case "mcp":
|
|
9936
|
+
return run9(argv);
|
|
9937
|
+
case "alerts":
|
|
9938
|
+
return run10(argv);
|
|
9939
|
+
case "webhooks":
|
|
9940
|
+
return run11(argv);
|
|
9358
9941
|
default:
|
|
9359
9942
|
err(` Unknown command: ${command}`);
|
|
9360
9943
|
return 1;
|
|
@@ -9389,7 +9972,13 @@ var init_commands = __esm({
|
|
|
9389
9972
|
init_stats2();
|
|
9390
9973
|
init_audit2();
|
|
9391
9974
|
init_agents2();
|
|
9392
|
-
|
|
9975
|
+
init_doctor();
|
|
9976
|
+
init_watch();
|
|
9977
|
+
init_keys2();
|
|
9978
|
+
init_mcp2();
|
|
9979
|
+
init_alerts();
|
|
9980
|
+
init_webhooks();
|
|
9981
|
+
COMMAND_NAMES = ["policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent", "doctor", "watch", "keys", "mcp", "alerts", "webhooks"];
|
|
9393
9982
|
}
|
|
9394
9983
|
});
|
|
9395
9984
|
|
|
@@ -9405,14 +9994,14 @@ __export(global_install_exports, {
|
|
|
9405
9994
|
runGlobalRestore: () => runGlobalRestore,
|
|
9406
9995
|
unlockProtected: () => unlockProtected
|
|
9407
9996
|
});
|
|
9408
|
-
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as
|
|
9409
|
-
import { resolve as resolve4, join as
|
|
9410
|
-
import { homedir as
|
|
9997
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
|
|
9998
|
+
import { resolve as resolve4, join as join8, dirname } from "path";
|
|
9999
|
+
import { homedir as homedir6 } from "os";
|
|
9411
10000
|
import { fileURLToPath } from "url";
|
|
9412
10001
|
import { createInterface } from "readline";
|
|
9413
10002
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
9414
10003
|
function lockFile(file) {
|
|
9415
|
-
if (!
|
|
10004
|
+
if (!existsSync6(file)) return;
|
|
9416
10005
|
try {
|
|
9417
10006
|
if (process.platform === "win32") {
|
|
9418
10007
|
try {
|
|
@@ -9438,7 +10027,7 @@ function lockFile(file) {
|
|
|
9438
10027
|
}
|
|
9439
10028
|
}
|
|
9440
10029
|
function unlockFile(file) {
|
|
9441
|
-
if (!
|
|
10030
|
+
if (!existsSync6(file)) return;
|
|
9442
10031
|
try {
|
|
9443
10032
|
if (process.platform === "win32") {
|
|
9444
10033
|
try {
|
|
@@ -9470,10 +10059,10 @@ function unlockFile(file) {
|
|
|
9470
10059
|
function protectedTargets() {
|
|
9471
10060
|
const p = globalPaths();
|
|
9472
10061
|
return [
|
|
9473
|
-
|
|
9474
|
-
|
|
9475
|
-
|
|
9476
|
-
|
|
10062
|
+
join8(p.hooksDir, "guard.mjs"),
|
|
10063
|
+
join8(p.hooksDir, "audit.mjs"),
|
|
10064
|
+
join8(p.hooksDir, "stop.mjs"),
|
|
10065
|
+
join8(p.hooksDir, "shield.mjs"),
|
|
9477
10066
|
p.configPath,
|
|
9478
10067
|
p.settingsPath
|
|
9479
10068
|
];
|
|
@@ -9485,26 +10074,26 @@ function unlockProtected() {
|
|
|
9485
10074
|
for (const f of protectedTargets()) unlockFile(f);
|
|
9486
10075
|
}
|
|
9487
10076
|
function globalPaths() {
|
|
9488
|
-
const home =
|
|
9489
|
-
const sgDir =
|
|
9490
|
-
const hooksDir =
|
|
9491
|
-
const claudeDir =
|
|
10077
|
+
const home = homedir6();
|
|
10078
|
+
const sgDir = join8(home, ".solongate");
|
|
10079
|
+
const hooksDir = join8(sgDir, "hooks");
|
|
10080
|
+
const claudeDir = join8(home, ".claude");
|
|
9492
10081
|
return {
|
|
9493
10082
|
home,
|
|
9494
10083
|
sgDir,
|
|
9495
10084
|
hooksDir,
|
|
9496
10085
|
claudeDir,
|
|
9497
|
-
settingsPath:
|
|
9498
|
-
backupPath:
|
|
9499
|
-
configPath:
|
|
10086
|
+
settingsPath: join8(claudeDir, "settings.json"),
|
|
10087
|
+
backupPath: join8(claudeDir, "settings.solongate.bak"),
|
|
10088
|
+
configPath: join8(sgDir, "cloud-guard.json")
|
|
9500
10089
|
};
|
|
9501
10090
|
}
|
|
9502
10091
|
function readHook(filename) {
|
|
9503
|
-
return readFileSync6(
|
|
10092
|
+
return readFileSync6(join8(HOOKS_DIR, filename), "utf-8");
|
|
9504
10093
|
}
|
|
9505
10094
|
function readGuard() {
|
|
9506
|
-
const bundled =
|
|
9507
|
-
return
|
|
10095
|
+
const bundled = join8(HOOKS_DIR, "guard.bundled.mjs");
|
|
10096
|
+
return existsSync6(bundled) ? readFileSync6(bundled, "utf-8") : readHook("guard.mjs");
|
|
9508
10097
|
}
|
|
9509
10098
|
function ask(question) {
|
|
9510
10099
|
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
@@ -9517,10 +10106,10 @@ function runGlobalRestore() {
|
|
|
9517
10106
|
const p = globalPaths();
|
|
9518
10107
|
unlockProtected();
|
|
9519
10108
|
removeClaudeShim();
|
|
9520
|
-
if (
|
|
10109
|
+
if (existsSync6(p.backupPath)) {
|
|
9521
10110
|
writeFileSync3(p.settingsPath, readFileSync6(p.backupPath, "utf-8"));
|
|
9522
10111
|
console.log(` Restored ${p.settingsPath} from backup.`);
|
|
9523
|
-
} else if (
|
|
10112
|
+
} else if (existsSync6(p.settingsPath)) {
|
|
9524
10113
|
try {
|
|
9525
10114
|
const s = JSON.parse(readFileSync6(p.settingsPath, "utf-8"));
|
|
9526
10115
|
delete s.hooks;
|
|
@@ -9558,15 +10147,15 @@ function shimTargets() {
|
|
|
9558
10147
|
return [];
|
|
9559
10148
|
}
|
|
9560
10149
|
}
|
|
9561
|
-
return [".bashrc", ".zshrc", ".profile"].map((f) =>
|
|
10150
|
+
return [".bashrc", ".zshrc", ".profile"].map((f) => join8(homedir6(), f)).filter((f) => existsSync6(f));
|
|
9562
10151
|
}
|
|
9563
|
-
function writeShimBlock(file,
|
|
10152
|
+
function writeShimBlock(file, block2) {
|
|
9564
10153
|
const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
|
|
9565
|
-
let content =
|
|
10154
|
+
let content = existsSync6(file) ? readFileSync6(file, "utf-8") : "";
|
|
9566
10155
|
content = content.replace(re, "");
|
|
9567
|
-
if (
|
|
10156
|
+
if (block2) {
|
|
9568
10157
|
if (content.length && !content.endsWith("\n")) content += "\n";
|
|
9569
|
-
content +=
|
|
10158
|
+
content += block2 + "\n";
|
|
9570
10159
|
}
|
|
9571
10160
|
mkdirSync3(dirname(file), { recursive: true });
|
|
9572
10161
|
writeFileSync3(file, content);
|
|
@@ -9580,7 +10169,7 @@ function installClaudeShim(shieldPath) {
|
|
|
9580
10169
|
const node = process.execPath.replace(/\\/g, "/");
|
|
9581
10170
|
const shield = shieldPath.replace(/\\/g, "/");
|
|
9582
10171
|
const win = process.platform === "win32";
|
|
9583
|
-
const
|
|
10172
|
+
const block2 = win ? `${SHIM_BEGIN}
|
|
9584
10173
|
function claude { & "${node}" "${shield}" -- "${real}" @args }
|
|
9585
10174
|
${SHIM_END}` : `${SHIM_BEGIN}
|
|
9586
10175
|
claude() { "${node}" "${shield}" -- "${real}" "$@"; }
|
|
@@ -9592,7 +10181,7 @@ ${SHIM_END}`;
|
|
|
9592
10181
|
}
|
|
9593
10182
|
for (const file of targets) {
|
|
9594
10183
|
try {
|
|
9595
|
-
writeShimBlock(file,
|
|
10184
|
+
writeShimBlock(file, block2);
|
|
9596
10185
|
console.log(` Auto-shield on: \`claude\` now masks secrets (${file})`);
|
|
9597
10186
|
} catch (e) {
|
|
9598
10187
|
console.log(` (Could not enable auto-shield in ${file}: ${e.message})`);
|
|
@@ -9629,18 +10218,18 @@ async function runGlobalInstall(opts = {}) {
|
|
|
9629
10218
|
mkdirSync3(p.hooksDir, { recursive: true });
|
|
9630
10219
|
mkdirSync3(p.claudeDir, { recursive: true });
|
|
9631
10220
|
unlockProtected();
|
|
9632
|
-
writeFileSync3(
|
|
9633
|
-
writeFileSync3(
|
|
9634
|
-
writeFileSync3(
|
|
9635
|
-
writeFileSync3(
|
|
10221
|
+
writeFileSync3(join8(p.hooksDir, "guard.mjs"), readGuard());
|
|
10222
|
+
writeFileSync3(join8(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
|
|
10223
|
+
writeFileSync3(join8(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
|
|
10224
|
+
writeFileSync3(join8(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
|
|
9636
10225
|
console.log(` Installed hooks \u2192 ${p.hooksDir}`);
|
|
9637
10226
|
removeClaudeShim();
|
|
9638
10227
|
writeFileSync3(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
|
|
9639
10228
|
console.log(` Wrote ${p.configPath}`);
|
|
9640
10229
|
let existing = {};
|
|
9641
|
-
if (
|
|
10230
|
+
if (existsSync6(p.settingsPath)) {
|
|
9642
10231
|
const raw = readFileSync6(p.settingsPath, "utf-8");
|
|
9643
|
-
if (!
|
|
10232
|
+
if (!existsSync6(p.backupPath)) {
|
|
9644
10233
|
writeFileSync3(p.backupPath, raw);
|
|
9645
10234
|
console.log(` Backed up existing settings \u2192 ${p.backupPath}`);
|
|
9646
10235
|
}
|
|
@@ -9650,9 +10239,9 @@ async function runGlobalInstall(opts = {}) {
|
|
|
9650
10239
|
existing = {};
|
|
9651
10240
|
}
|
|
9652
10241
|
}
|
|
9653
|
-
const guardAbs =
|
|
9654
|
-
const auditAbs =
|
|
9655
|
-
const stopAbs =
|
|
10242
|
+
const guardAbs = join8(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
|
|
10243
|
+
const auditAbs = join8(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
|
|
10244
|
+
const stopAbs = join8(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
|
|
9656
10245
|
const nodeBin = process.execPath.replace(/\\/g, "/");
|
|
9657
10246
|
const merged = {
|
|
9658
10247
|
...existing,
|
|
@@ -9842,22 +10431,22 @@ import { createServer, request as httpRequest } from "http";
|
|
|
9842
10431
|
import { request as httpsRequest } from "https";
|
|
9843
10432
|
import { spawn as spawn3 } from "child_process";
|
|
9844
10433
|
import { URL as URL2 } from "url";
|
|
9845
|
-
import { readFileSync as readFileSync7, existsSync as
|
|
10434
|
+
import { readFileSync as readFileSync7, existsSync as existsSync7, readdirSync, statSync as statSync4 } from "fs";
|
|
9846
10435
|
import { resolve as resolve5 } from "path";
|
|
9847
|
-
import { homedir as
|
|
10436
|
+
import { homedir as homedir7 } from "os";
|
|
9848
10437
|
function findCacheFile() {
|
|
9849
|
-
const dir = resolve5(
|
|
10438
|
+
const dir = resolve5(homedir7(), ".solongate");
|
|
9850
10439
|
const envSel = process.env.SOLONGATE_AGENT_ID;
|
|
9851
10440
|
if (envSel) {
|
|
9852
10441
|
const f = resolve5(dir, ".policy-cache-" + envSel.replace(/[^a-zA-Z0-9_-]/g, "_") + ".json");
|
|
9853
|
-
if (
|
|
10442
|
+
if (existsSync7(f)) return f;
|
|
9854
10443
|
}
|
|
9855
10444
|
let best = null, bestTs = -1;
|
|
9856
10445
|
try {
|
|
9857
10446
|
for (const name of readdirSync(dir)) {
|
|
9858
10447
|
if (name.startsWith(".policy-cache-") && name.endsWith(".json")) {
|
|
9859
10448
|
const full = resolve5(dir, name);
|
|
9860
|
-
const ts =
|
|
10449
|
+
const ts = statSync4(full).mtimeMs;
|
|
9861
10450
|
if (ts > bestTs) {
|
|
9862
10451
|
bestTs = ts;
|
|
9863
10452
|
best = full;
|
|
@@ -9871,7 +10460,7 @@ function findCacheFile() {
|
|
|
9871
10460
|
function loadCfg() {
|
|
9872
10461
|
try {
|
|
9873
10462
|
const f = findCacheFile();
|
|
9874
|
-
if (f &&
|
|
10463
|
+
if (f && existsSync7(f)) {
|
|
9875
10464
|
const c2 = JSON.parse(readFileSync7(f, "utf-8"));
|
|
9876
10465
|
const d = c2?.security?.dlpRedact;
|
|
9877
10466
|
const g = c2?.security?.ghost;
|
|
@@ -10148,9 +10737,9 @@ __export(logs_server_exports, {
|
|
|
10148
10737
|
runLogsServer: () => runLogsServer
|
|
10149
10738
|
});
|
|
10150
10739
|
import { createServer as createServer2 } from "http";
|
|
10151
|
-
import { readFileSync as readFileSync8, statSync as
|
|
10152
|
-
import { resolve as resolve6, join as
|
|
10153
|
-
import { homedir as
|
|
10740
|
+
import { readFileSync as readFileSync8, statSync as statSync5 } from "fs";
|
|
10741
|
+
import { resolve as resolve6, join as join9, isAbsolute } from "path";
|
|
10742
|
+
import { homedir as homedir8 } from "os";
|
|
10154
10743
|
import { readdirSync as readdirSync2 } from "fs";
|
|
10155
10744
|
function allowedOrigins() {
|
|
10156
10745
|
const base = [
|
|
@@ -10167,15 +10756,15 @@ function resolveLocalLogDir(rawPath) {
|
|
|
10167
10756
|
const dir = String(rawPath || "").trim().replace(/[\\/]+$/, "");
|
|
10168
10757
|
if (!dir) return null;
|
|
10169
10758
|
if (isAbsolute(dir)) return dir;
|
|
10170
|
-
return resolve6(
|
|
10759
|
+
return resolve6(homedir8(), ".solongate", "local-logs");
|
|
10171
10760
|
}
|
|
10172
10761
|
async function findLogDir() {
|
|
10173
|
-
const base = resolve6(
|
|
10762
|
+
const base = resolve6(homedir8(), ".solongate");
|
|
10174
10763
|
try {
|
|
10175
10764
|
const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
|
|
10176
10765
|
for (const f of files) {
|
|
10177
10766
|
try {
|
|
10178
|
-
const c2 = JSON.parse(readFileSync8(
|
|
10767
|
+
const c2 = JSON.parse(readFileSync8(join9(base, f), "utf-8"));
|
|
10179
10768
|
const p = c2?.security?.localLogs?.path;
|
|
10180
10769
|
if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
|
|
10181
10770
|
} catch {
|
|
@@ -10184,7 +10773,7 @@ async function findLogDir() {
|
|
|
10184
10773
|
} catch {
|
|
10185
10774
|
}
|
|
10186
10775
|
try {
|
|
10187
|
-
const cfgRaw = readFileSync8(
|
|
10776
|
+
const cfgRaw = readFileSync8(join9(base, "cloud-guard.json"), "utf-8");
|
|
10188
10777
|
const { apiKey, apiUrl } = JSON.parse(cfgRaw);
|
|
10189
10778
|
if (apiKey) {
|
|
10190
10779
|
const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
|
|
@@ -10213,9 +10802,9 @@ function setCors(req, res) {
|
|
|
10213
10802
|
}
|
|
10214
10803
|
function fileInfo(dir) {
|
|
10215
10804
|
if (!dir) return { file: null, exists: false, size: 0, mtimeMs: 0 };
|
|
10216
|
-
const file =
|
|
10805
|
+
const file = join9(dir, LOG_FILENAME);
|
|
10217
10806
|
try {
|
|
10218
|
-
const st =
|
|
10807
|
+
const st = statSync5(file);
|
|
10219
10808
|
return { file, exists: true, size: st.size, mtimeMs: st.mtimeMs };
|
|
10220
10809
|
} catch {
|
|
10221
10810
|
return { file, exists: false, size: 0, mtimeMs: 0 };
|
|
@@ -10326,7 +10915,7 @@ var init_logs_server = __esm({
|
|
|
10326
10915
|
|
|
10327
10916
|
// src/inject.ts
|
|
10328
10917
|
var inject_exports = {};
|
|
10329
|
-
import { readFileSync as readFileSync9, writeFileSync as writeFileSync4, existsSync as
|
|
10918
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync4, existsSync as existsSync8, copyFileSync } from "fs";
|
|
10330
10919
|
import { resolve as resolve7 } from "path";
|
|
10331
10920
|
import { execSync } from "child_process";
|
|
10332
10921
|
function parseInjectArgs(argv) {
|
|
@@ -10384,7 +10973,7 @@ WHAT IT DOES
|
|
|
10384
10973
|
`);
|
|
10385
10974
|
}
|
|
10386
10975
|
function detectProject() {
|
|
10387
|
-
if (!
|
|
10976
|
+
if (!existsSync8(resolve7("package.json"))) return false;
|
|
10388
10977
|
try {
|
|
10389
10978
|
const pkg = JSON.parse(readFileSync9(resolve7("package.json"), "utf-8"));
|
|
10390
10979
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
@@ -10400,13 +10989,13 @@ function findTsEntryFile() {
|
|
|
10400
10989
|
const binPath = typeof pkg.bin === "string" ? pkg.bin : Object.values(pkg.bin)[0];
|
|
10401
10990
|
if (typeof binPath === "string") {
|
|
10402
10991
|
const srcPath = binPath.replace(/^\.\/dist\//, "./src/").replace(/\.js$/, ".ts");
|
|
10403
|
-
if (
|
|
10404
|
-
if (
|
|
10992
|
+
if (existsSync8(resolve7(srcPath))) return resolve7(srcPath);
|
|
10993
|
+
if (existsSync8(resolve7(binPath))) return resolve7(binPath);
|
|
10405
10994
|
}
|
|
10406
10995
|
}
|
|
10407
10996
|
if (pkg.main) {
|
|
10408
10997
|
const srcPath = pkg.main.replace(/^\.\/dist\//, "./src/").replace(/\.js$/, ".ts");
|
|
10409
|
-
if (
|
|
10998
|
+
if (existsSync8(resolve7(srcPath))) return resolve7(srcPath);
|
|
10410
10999
|
}
|
|
10411
11000
|
} catch {
|
|
10412
11001
|
}
|
|
@@ -10420,7 +11009,7 @@ function findTsEntryFile() {
|
|
|
10420
11009
|
];
|
|
10421
11010
|
for (const c2 of candidates) {
|
|
10422
11011
|
const full = resolve7(c2);
|
|
10423
|
-
if (
|
|
11012
|
+
if (existsSync8(full)) {
|
|
10424
11013
|
try {
|
|
10425
11014
|
const content = readFileSync9(full, "utf-8");
|
|
10426
11015
|
if (content.includes("McpServer") || content.includes("McpServer")) {
|
|
@@ -10431,13 +11020,13 @@ function findTsEntryFile() {
|
|
|
10431
11020
|
}
|
|
10432
11021
|
}
|
|
10433
11022
|
for (const c2 of candidates) {
|
|
10434
|
-
if (
|
|
11023
|
+
if (existsSync8(resolve7(c2))) return resolve7(c2);
|
|
10435
11024
|
}
|
|
10436
11025
|
return null;
|
|
10437
11026
|
}
|
|
10438
11027
|
function detectPackageManager() {
|
|
10439
|
-
if (
|
|
10440
|
-
if (
|
|
11028
|
+
if (existsSync8(resolve7("pnpm-lock.yaml"))) return "pnpm";
|
|
11029
|
+
if (existsSync8(resolve7("yarn.lock"))) return "yarn";
|
|
10441
11030
|
return "npm";
|
|
10442
11031
|
}
|
|
10443
11032
|
function installSdk() {
|
|
@@ -10580,7 +11169,7 @@ async function main2() {
|
|
|
10580
11169
|
}
|
|
10581
11170
|
log3(" Language: TypeScript");
|
|
10582
11171
|
const entryFile = opts.file ? resolve7(opts.file) : findTsEntryFile();
|
|
10583
|
-
if (!entryFile || !
|
|
11172
|
+
if (!entryFile || !existsSync8(entryFile)) {
|
|
10584
11173
|
log3(` Could not find entry file.${opts.file ? ` File not found: ${opts.file}` : ""}`);
|
|
10585
11174
|
log3("");
|
|
10586
11175
|
log3(" Specify it manually: --file <path>");
|
|
@@ -10593,7 +11182,7 @@ async function main2() {
|
|
|
10593
11182
|
log3("");
|
|
10594
11183
|
const backupPath = entryFile + ".solongate-backup";
|
|
10595
11184
|
if (opts.restore) {
|
|
10596
|
-
if (!
|
|
11185
|
+
if (!existsSync8(backupPath)) {
|
|
10597
11186
|
log3(" No backup found. Nothing to restore.");
|
|
10598
11187
|
process.exit(1);
|
|
10599
11188
|
}
|
|
@@ -10629,7 +11218,7 @@ async function main2() {
|
|
|
10629
11218
|
log3(" To apply: npx @solongate/proxy inject");
|
|
10630
11219
|
process.exit(0);
|
|
10631
11220
|
}
|
|
10632
|
-
if (!
|
|
11221
|
+
if (!existsSync8(backupPath)) {
|
|
10633
11222
|
copyFileSync(entryFile, backupPath);
|
|
10634
11223
|
log3("");
|
|
10635
11224
|
log3(` Backup: ${backupPath}`);
|
|
@@ -10663,8 +11252,8 @@ var init_inject = __esm({
|
|
|
10663
11252
|
|
|
10664
11253
|
// src/create.ts
|
|
10665
11254
|
var create_exports = {};
|
|
10666
|
-
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5, existsSync as
|
|
10667
|
-
import { resolve as resolve8, join as
|
|
11255
|
+
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5, existsSync as existsSync9 } from "fs";
|
|
11256
|
+
import { resolve as resolve8, join as join10 } from "path";
|
|
10668
11257
|
import { execSync as execSync2 } from "child_process";
|
|
10669
11258
|
function withSpinner(message, fn) {
|
|
10670
11259
|
const frames = ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"];
|
|
@@ -10744,7 +11333,7 @@ EXAMPLES
|
|
|
10744
11333
|
}
|
|
10745
11334
|
function createProject(dir, name, _policy) {
|
|
10746
11335
|
writeFileSync5(
|
|
10747
|
-
|
|
11336
|
+
join10(dir, "package.json"),
|
|
10748
11337
|
JSON.stringify(
|
|
10749
11338
|
{
|
|
10750
11339
|
name,
|
|
@@ -10774,7 +11363,7 @@ function createProject(dir, name, _policy) {
|
|
|
10774
11363
|
) + "\n"
|
|
10775
11364
|
);
|
|
10776
11365
|
writeFileSync5(
|
|
10777
|
-
|
|
11366
|
+
join10(dir, "tsconfig.json"),
|
|
10778
11367
|
JSON.stringify(
|
|
10779
11368
|
{
|
|
10780
11369
|
compilerOptions: {
|
|
@@ -10794,9 +11383,9 @@ function createProject(dir, name, _policy) {
|
|
|
10794
11383
|
2
|
|
10795
11384
|
) + "\n"
|
|
10796
11385
|
);
|
|
10797
|
-
mkdirSync4(
|
|
11386
|
+
mkdirSync4(join10(dir, "src"), { recursive: true });
|
|
10798
11387
|
writeFileSync5(
|
|
10799
|
-
|
|
11388
|
+
join10(dir, "src", "index.ts"),
|
|
10800
11389
|
`#!/usr/bin/env node
|
|
10801
11390
|
|
|
10802
11391
|
console.log = (...args: unknown[]) => {
|
|
@@ -10838,7 +11427,7 @@ console.log('Press Ctrl+C to stop.');
|
|
|
10838
11427
|
`
|
|
10839
11428
|
);
|
|
10840
11429
|
writeFileSync5(
|
|
10841
|
-
|
|
11430
|
+
join10(dir, ".mcp.json"),
|
|
10842
11431
|
JSON.stringify(
|
|
10843
11432
|
{
|
|
10844
11433
|
mcpServers: {
|
|
@@ -10856,12 +11445,12 @@ console.log('Press Ctrl+C to stop.');
|
|
|
10856
11445
|
) + "\n"
|
|
10857
11446
|
);
|
|
10858
11447
|
writeFileSync5(
|
|
10859
|
-
|
|
11448
|
+
join10(dir, ".env"),
|
|
10860
11449
|
`SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
|
|
10861
11450
|
`
|
|
10862
11451
|
);
|
|
10863
11452
|
writeFileSync5(
|
|
10864
|
-
|
|
11453
|
+
join10(dir, ".gitignore"),
|
|
10865
11454
|
`node_modules/
|
|
10866
11455
|
dist/
|
|
10867
11456
|
*.solongate-backup
|
|
@@ -10875,7 +11464,7 @@ async function main3() {
|
|
|
10875
11464
|
const opts = parseCreateArgs(process.argv);
|
|
10876
11465
|
const dir = resolve8(opts.name);
|
|
10877
11466
|
printBanner("Create MCP Server");
|
|
10878
|
-
if (
|
|
11467
|
+
if (existsSync9(dir)) {
|
|
10879
11468
|
log3(` ${c.red}Error:${c.reset} Directory "${opts.name}" already exists.`);
|
|
10880
11469
|
process.exit(1);
|
|
10881
11470
|
}
|
|
@@ -10954,12 +11543,12 @@ var init_create = __esm({
|
|
|
10954
11543
|
|
|
10955
11544
|
// src/pull-push.ts
|
|
10956
11545
|
var pull_push_exports = {};
|
|
10957
|
-
import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as
|
|
11546
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as existsSync10 } from "fs";
|
|
10958
11547
|
import { resolve as resolve9 } from "path";
|
|
10959
11548
|
function loadEnv() {
|
|
10960
11549
|
if (process.env.SOLONGATE_API_KEY) return;
|
|
10961
11550
|
const envPath = resolve9(".env");
|
|
10962
|
-
if (!
|
|
11551
|
+
if (!existsSync10(envPath)) return;
|
|
10963
11552
|
try {
|
|
10964
11553
|
const content = readFileSync10(envPath, "utf-8");
|
|
10965
11554
|
for (const line of content.split("\n")) {
|
|
@@ -11026,7 +11615,7 @@ async function listPolicies(apiKey) {
|
|
|
11026
11615
|
const data = await res.json();
|
|
11027
11616
|
return data.policies ?? [];
|
|
11028
11617
|
}
|
|
11029
|
-
async function
|
|
11618
|
+
async function list5(apiKey, policyId) {
|
|
11030
11619
|
const policies = await listPolicies(apiKey);
|
|
11031
11620
|
if (policies.length === 0) {
|
|
11032
11621
|
log5(yellow2("No policies found. Create one in the dashboard first."));
|
|
@@ -11161,7 +11750,7 @@ async function pull(apiKey, file, policyId) {
|
|
|
11161
11750
|
log5("");
|
|
11162
11751
|
}
|
|
11163
11752
|
async function push(apiKey, file, policyId) {
|
|
11164
|
-
if (!
|
|
11753
|
+
if (!existsSync10(file)) {
|
|
11165
11754
|
log5(red2(`ERROR: File not found: ${file}`));
|
|
11166
11755
|
process.exit(1);
|
|
11167
11756
|
}
|
|
@@ -11229,7 +11818,7 @@ async function main4() {
|
|
|
11229
11818
|
} else if (command === "push") {
|
|
11230
11819
|
await push(apiKey, file, policyId);
|
|
11231
11820
|
} else if (command === "list" || command === "ls") {
|
|
11232
|
-
await
|
|
11821
|
+
await list5(apiKey, policyId);
|
|
11233
11822
|
} else {
|
|
11234
11823
|
log5(red2(`Unknown command: ${command}`));
|
|
11235
11824
|
log5("");
|
|
@@ -14294,7 +14883,7 @@ ${msg.content.text}`;
|
|
|
14294
14883
|
|
|
14295
14884
|
// src/index.ts
|
|
14296
14885
|
init_cli_utils();
|
|
14297
|
-
var CLI_SUBCOMMANDS = /* @__PURE__ */ new Set(["login", "logout", "shield", "create", "inject", "pull", "push", "list", "ls", "logs-server", "local-logs", "policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent", "ui", "dashboard", "tui"]);
|
|
14886
|
+
var CLI_SUBCOMMANDS = /* @__PURE__ */ new Set(["login", "logout", "shield", "create", "inject", "pull", "push", "list", "ls", "logs-server", "local-logs", "policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent", "doctor", "watch", "keys", "mcp", "alerts", "webhooks", "ui", "dashboard", "tui"]);
|
|
14298
14887
|
var IS_HUMAN_CLI = process.argv.length <= 2 || CLI_SUBCOMMANDS.has(process.argv[2] ?? "");
|
|
14299
14888
|
if (!IS_HUMAN_CLI) {
|
|
14300
14889
|
console.log = (...args) => {
|
|
@@ -14343,7 +14932,7 @@ async function main5() {
|
|
|
14343
14932
|
await launchTui2();
|
|
14344
14933
|
return;
|
|
14345
14934
|
}
|
|
14346
|
-
const MGMT_COMMANDS = /* @__PURE__ */ new Set(["policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent"]);
|
|
14935
|
+
const MGMT_COMMANDS = /* @__PURE__ */ new Set(["policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent", "doctor", "watch", "keys", "mcp", "alerts", "webhooks"]);
|
|
14347
14936
|
if (MGMT_COMMANDS.has(subcommand ?? "")) {
|
|
14348
14937
|
const { runCommand: runCommand2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
|
|
14349
14938
|
const code = await runCommand2(subcommand, process.argv.slice(3));
|