@solongate/proxy 0.78.0 → 0.80.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-client/index.d.ts +4 -0
- package/dist/api-client/keys.d.ts +17 -0
- package/dist/api-client/mcp.d.ts +10 -0
- package/dist/api-client/settings.d.ts +52 -0
- package/dist/commands/alerts.d.ts +1 -0
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/index.d.ts +1 -1
- package/dist/commands/index.js +426 -3
- package/dist/commands/keys.d.ts +1 -0
- package/dist/commands/mcp.d.ts +1 -0
- package/dist/commands/watch.d.ts +1 -0
- package/dist/commands/webhooks.d.ts +1 -0
- package/dist/index.js +730 -90
- package/dist/tui/index.js +221 -12
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6867,10 +6867,18 @@ 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,
|
|
6873
|
-
|
|
6878
|
+
getSelfProtection: () => getSelfProtection,
|
|
6879
|
+
getWebhooks: () => getWebhooks,
|
|
6880
|
+
setSecurityLayers: () => setSecurityLayers,
|
|
6881
|
+
setSelfProtection: () => setSelfProtection
|
|
6874
6882
|
});
|
|
6875
6883
|
function getSecurityLayers() {
|
|
6876
6884
|
return request("GET", "/settings/security-layers");
|
|
@@ -6887,6 +6895,30 @@ function clearRateLimitHistory() {
|
|
|
6887
6895
|
function getGuardStatus() {
|
|
6888
6896
|
return request("GET", "/settings/guard-status");
|
|
6889
6897
|
}
|
|
6898
|
+
function getSelfProtection() {
|
|
6899
|
+
return request("GET", "/settings/self-protection");
|
|
6900
|
+
}
|
|
6901
|
+
function setSelfProtection(enabled) {
|
|
6902
|
+
return request("PUT", "/settings/self-protection", { body: { enabled } });
|
|
6903
|
+
}
|
|
6904
|
+
function getAlerts() {
|
|
6905
|
+
return request("GET", "/settings/denial-alerts");
|
|
6906
|
+
}
|
|
6907
|
+
function createAlert(body) {
|
|
6908
|
+
return request("POST", "/settings/denial-alerts", { body });
|
|
6909
|
+
}
|
|
6910
|
+
function deleteAlert(id) {
|
|
6911
|
+
return request("DELETE", "/settings/denial-alerts", { query: { id } });
|
|
6912
|
+
}
|
|
6913
|
+
function getWebhooks() {
|
|
6914
|
+
return request("GET", "/settings/denial-webhook");
|
|
6915
|
+
}
|
|
6916
|
+
function createWebhook(body) {
|
|
6917
|
+
return request("POST", "/settings/denial-webhook", { body });
|
|
6918
|
+
}
|
|
6919
|
+
function deleteWebhook(id) {
|
|
6920
|
+
return request("DELETE", "/settings/denial-webhook", { query: { id } });
|
|
6921
|
+
}
|
|
6890
6922
|
var init_settings = __esm({
|
|
6891
6923
|
"src/api-client/settings.ts"() {
|
|
6892
6924
|
"use strict";
|
|
@@ -6971,6 +7003,44 @@ var init_agents = __esm({
|
|
|
6971
7003
|
}
|
|
6972
7004
|
});
|
|
6973
7005
|
|
|
7006
|
+
// src/api-client/keys.ts
|
|
7007
|
+
var keys_exports = {};
|
|
7008
|
+
__export(keys_exports, {
|
|
7009
|
+
create: () => create2,
|
|
7010
|
+
list: () => list3,
|
|
7011
|
+
revoke: () => revoke
|
|
7012
|
+
});
|
|
7013
|
+
function list3() {
|
|
7014
|
+
return request("GET", "/keys");
|
|
7015
|
+
}
|
|
7016
|
+
function create2(name, isLive = true) {
|
|
7017
|
+
return request("POST", "/keys", { body: { name, is_live: isLive } });
|
|
7018
|
+
}
|
|
7019
|
+
function revoke(id) {
|
|
7020
|
+
return request("DELETE", `/keys/${encodeURIComponent(id)}`);
|
|
7021
|
+
}
|
|
7022
|
+
var init_keys = __esm({
|
|
7023
|
+
"src/api-client/keys.ts"() {
|
|
7024
|
+
"use strict";
|
|
7025
|
+
init_client();
|
|
7026
|
+
}
|
|
7027
|
+
});
|
|
7028
|
+
|
|
7029
|
+
// src/api-client/mcp.ts
|
|
7030
|
+
var mcp_exports = {};
|
|
7031
|
+
__export(mcp_exports, {
|
|
7032
|
+
list: () => list4
|
|
7033
|
+
});
|
|
7034
|
+
function list4() {
|
|
7035
|
+
return request("GET", "/mcp-servers");
|
|
7036
|
+
}
|
|
7037
|
+
var init_mcp = __esm({
|
|
7038
|
+
"src/api-client/mcp.ts"() {
|
|
7039
|
+
"use strict";
|
|
7040
|
+
init_client();
|
|
7041
|
+
}
|
|
7042
|
+
});
|
|
7043
|
+
|
|
6974
7044
|
// src/api-client/index.ts
|
|
6975
7045
|
var api;
|
|
6976
7046
|
var init_api_client = __esm({
|
|
@@ -6983,7 +7053,9 @@ var init_api_client = __esm({
|
|
|
6983
7053
|
init_stats();
|
|
6984
7054
|
init_audit();
|
|
6985
7055
|
init_agents();
|
|
6986
|
-
|
|
7056
|
+
init_keys();
|
|
7057
|
+
init_mcp();
|
|
7058
|
+
api = { policies: policies_exports, settings: settings_exports, stats: stats_exports, audit: audit_exports, agents: agents_exports, keys: keys_exports, mcp: mcp_exports };
|
|
6987
7059
|
}
|
|
6988
7060
|
});
|
|
6989
7061
|
|
|
@@ -7870,12 +7942,13 @@ function ruleSummary(r) {
|
|
|
7870
7942
|
return bits.join(" ");
|
|
7871
7943
|
}
|
|
7872
7944
|
function PoliciesPanel({ focused }) {
|
|
7873
|
-
const
|
|
7874
|
-
const policies =
|
|
7945
|
+
const list6 = useLoader(() => api.policies.list());
|
|
7946
|
+
const policies = list6.data?.policies ?? [];
|
|
7875
7947
|
const [view, setView] = useState3("list");
|
|
7876
7948
|
const [pi, setPi] = useState3(0);
|
|
7877
7949
|
const [ri, setRi] = useState3(0);
|
|
7878
7950
|
const [fi, setFi] = useState3(0);
|
|
7951
|
+
const [vi, setVi] = useState3(0);
|
|
7879
7952
|
const [rules, setRules] = useState3([]);
|
|
7880
7953
|
const [mode, setMode] = useState3("denylist");
|
|
7881
7954
|
const [dirty, setDirty] = useState3(false);
|
|
@@ -7884,6 +7957,10 @@ function PoliciesPanel({ focused }) {
|
|
|
7884
7957
|
const [status, setStatus] = useState3(null);
|
|
7885
7958
|
const selected = policies[Math.min(pi, Math.max(0, policies.length - 1))];
|
|
7886
7959
|
const detail = useLoader(() => selected ? api.policies.get(selected.id) : Promise.resolve(null), [selected?.id]);
|
|
7960
|
+
const versionsQ = useLoader(
|
|
7961
|
+
() => view === "versions" && selected ? api.policies.versions(selected.id, { limit: 50 }) : Promise.resolve(null),
|
|
7962
|
+
[view, selected?.id]
|
|
7963
|
+
);
|
|
7887
7964
|
useEffect3(() => {
|
|
7888
7965
|
if (detail.data) {
|
|
7889
7966
|
setRules(detail.data.rules);
|
|
@@ -7913,7 +7990,7 @@ function PoliciesPanel({ focused }) {
|
|
|
7913
7990
|
setRules(res.rules);
|
|
7914
7991
|
setDirty(false);
|
|
7915
7992
|
setStatus("\u2713 Saved v" + res._version);
|
|
7916
|
-
|
|
7993
|
+
list6.reload();
|
|
7917
7994
|
} catch (e) {
|
|
7918
7995
|
setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
|
|
7919
7996
|
}
|
|
@@ -7958,10 +8035,41 @@ function PoliciesPanel({ focused }) {
|
|
|
7958
8035
|
}
|
|
7959
8036
|
} else if (input === "m") {
|
|
7960
8037
|
mutate(rules, mode === "denylist" ? "whitelist" : "denylist");
|
|
8038
|
+
} else if (input === "n") {
|
|
8039
|
+
const nr = { id: `rule-${Date.now()}`, description: "", effect: "DENY", priority: 100, toolPattern: "*", minimumTrustLevel: "UNTRUSTED", enabled: true };
|
|
8040
|
+
mutate([nr, ...rules]);
|
|
8041
|
+
setRi(0);
|
|
8042
|
+
setFi(0);
|
|
8043
|
+
setView("rule");
|
|
8044
|
+
} else if (input === "D") {
|
|
8045
|
+
setStatus("Dry-running\u2026");
|
|
8046
|
+
void api.policies.dryRun({ rules, mode }).then((res) => setStatus(`dry-run ${res.evaluated} calls \xB7 allow ${res.would_allow} / deny ${res.would_deny} \xB7 newly blocked ${res.newly_blocked} \xB7 newly allowed ${res.newly_allowed}`)).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
|
|
8047
|
+
} else if (input === "v") {
|
|
8048
|
+
setVi(0);
|
|
8049
|
+
setView("versions");
|
|
7961
8050
|
} else if (input === "s") void save();
|
|
7962
8051
|
else if (input === "x") discard();
|
|
7963
8052
|
return;
|
|
7964
8053
|
}
|
|
8054
|
+
if (view === "versions") {
|
|
8055
|
+
const vers = versionsQ.data?.versions ?? [];
|
|
8056
|
+
if (key.leftArrow) return void setView("rules");
|
|
8057
|
+
if (key.upArrow) setVi((n) => Math.max(0, n - 1));
|
|
8058
|
+
else if (key.downArrow) setVi((n) => Math.min(vers.length - 1, n + 1));
|
|
8059
|
+
else if (key.return) {
|
|
8060
|
+
const v = vers[vi];
|
|
8061
|
+
if (v && selected) {
|
|
8062
|
+
setStatus("Rolling back\u2026");
|
|
8063
|
+
void api.policies.rollback(selected.id, v.version).then((r) => {
|
|
8064
|
+
setStatus(`\u2713 rolled back to v${v.version} \u2192 new v${r.version}`);
|
|
8065
|
+
detail.reload();
|
|
8066
|
+
list6.reload();
|
|
8067
|
+
setView("rules");
|
|
8068
|
+
}).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
|
|
8069
|
+
}
|
|
8070
|
+
}
|
|
8071
|
+
return;
|
|
8072
|
+
}
|
|
7965
8073
|
const rule2 = rules[ri];
|
|
7966
8074
|
if (!rule2) return setView("rules");
|
|
7967
8075
|
const field = FIELDS[fi];
|
|
@@ -7983,7 +8091,7 @@ function PoliciesPanel({ focused }) {
|
|
|
7983
8091
|
{ isActive: focused && !editing }
|
|
7984
8092
|
);
|
|
7985
8093
|
if (view === "list") {
|
|
7986
|
-
return /* @__PURE__ */ jsx3(DataView, { loading:
|
|
8094
|
+
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: [
|
|
7987
8095
|
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: focused ? "\u2191\u2193 select \xB7 enter open" : "press \u2192 to browse" }),
|
|
7988
8096
|
/* @__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: [
|
|
7989
8097
|
(i === pi ? "\u25B8 " : " ") + truncate2(p.name, 26).padEnd(27),
|
|
@@ -8006,7 +8114,7 @@ function PoliciesPanel({ focused }) {
|
|
|
8006
8114
|
/* @__PURE__ */ jsx3(Text3, { color: mode === "whitelist" ? theme.warn : void 0, children: mode }),
|
|
8007
8115
|
dirtyTag
|
|
8008
8116
|
] }),
|
|
8009
|
-
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "\u2191\u2193
|
|
8117
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "\u2191\u2193 \xB7 enter edit \xB7 space on/off \xB7 e effect \xB7 n new \xB7 d del \xB7 m mode \xB7 D dry-run \xB7 v versions \xB7 s save \xB7 \u2190 back" }),
|
|
8010
8118
|
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(
|
|
8011
8119
|
Table,
|
|
8012
8120
|
{
|
|
@@ -8032,6 +8140,33 @@ function PoliciesPanel({ focused }) {
|
|
|
8032
8140
|
status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
8033
8141
|
] }) });
|
|
8034
8142
|
}
|
|
8143
|
+
if (view === "versions") {
|
|
8144
|
+
const vers = versionsQ.data?.versions ?? [];
|
|
8145
|
+
return /* @__PURE__ */ jsx3(DataView, { loading: versionsQ.loading && !versionsQ.data, error: versionsQ.error, empty: !!versionsQ.data && vers.length === 0, emptyText: "No versions.", children: /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
8146
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.accentBright, children: truncate2(selected?.name ?? "", 28) }),
|
|
8147
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "\u2191\u2193 select \xB7 enter roll back to that version \xB7 \u2190 back" }),
|
|
8148
|
+
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(
|
|
8149
|
+
Table,
|
|
8150
|
+
{
|
|
8151
|
+
columns: [
|
|
8152
|
+
{ header: "", width: 2 },
|
|
8153
|
+
{ header: "VER", width: 5 },
|
|
8154
|
+
{ header: "RULES", width: 5 },
|
|
8155
|
+
{ header: "REASON", width: 34 },
|
|
8156
|
+
{ header: "WHEN", width: 16 }
|
|
8157
|
+
],
|
|
8158
|
+
rows: vers.map((v, i) => [
|
|
8159
|
+
{ value: i === vi ? "\u25B8" : "", color: theme.accentBright },
|
|
8160
|
+
{ value: `v${v.version}`, bold: i === vi },
|
|
8161
|
+
{ value: String(v.rules_count), dim: true },
|
|
8162
|
+
{ value: truncate2(v.reason || "\u2014", 34) },
|
|
8163
|
+
{ value: truncate2(v.created_at, 16), dim: true }
|
|
8164
|
+
])
|
|
8165
|
+
}
|
|
8166
|
+
) }),
|
|
8167
|
+
status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
8168
|
+
] }) });
|
|
8169
|
+
}
|
|
8035
8170
|
const rule = rules[ri];
|
|
8036
8171
|
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
8037
8172
|
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
@@ -8255,12 +8390,19 @@ function DlpPanel({ focused }) {
|
|
|
8255
8390
|
const [adding, setAdding] = useState5(null);
|
|
8256
8391
|
const [newName, setNewName] = useState5("");
|
|
8257
8392
|
const [newRe, setNewRe] = useState5("");
|
|
8393
|
+
const [ghostOn, setGhostOn] = useState5(false);
|
|
8394
|
+
const spQ = useLoader(() => api.settings.getSelfProtection());
|
|
8395
|
+
const [selfProt, setSelfProt] = useState5(null);
|
|
8258
8396
|
useEffect5(() => {
|
|
8259
8397
|
if (q.data && !dlp) {
|
|
8260
8398
|
setDlp({ ...q.data.layers.dlp, patterns: [...q.data.layers.dlp.patterns], custom: [...q.data.layers.dlp.custom] });
|
|
8261
8399
|
setAvailable(q.data.availablePatterns);
|
|
8400
|
+
setGhostOn(q.data.layers.ghost.mode === "on");
|
|
8262
8401
|
}
|
|
8263
8402
|
}, [q.data, dlp]);
|
|
8403
|
+
useEffect5(() => {
|
|
8404
|
+
if (spQ.data && selfProt === null) setSelfProt(spQ.data.enabled);
|
|
8405
|
+
}, [spQ.data, selfProt]);
|
|
8264
8406
|
const mutate = (next) => {
|
|
8265
8407
|
setDlp(next);
|
|
8266
8408
|
setDirty(true);
|
|
@@ -8270,14 +8412,27 @@ function DlpPanel({ focused }) {
|
|
|
8270
8412
|
if (!dlp || !q.data) return;
|
|
8271
8413
|
setStatus("Saving\u2026");
|
|
8272
8414
|
try {
|
|
8273
|
-
const
|
|
8415
|
+
const ghost = { ...q.data.layers.ghost, mode: ghostOn ? "on" : "off" };
|
|
8416
|
+
const res = await api.settings.setSecurityLayers({ ...q.data.layers, dlp, ghost });
|
|
8274
8417
|
setDlp({ ...res.layers.dlp, patterns: [...res.layers.dlp.patterns], custom: [...res.layers.dlp.custom] });
|
|
8418
|
+
setGhostOn(res.layers.ghost.mode === "on");
|
|
8275
8419
|
setDirty(false);
|
|
8276
8420
|
setStatus("\u2713 Saved");
|
|
8277
8421
|
} catch (e) {
|
|
8278
8422
|
setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
|
|
8279
8423
|
}
|
|
8280
8424
|
};
|
|
8425
|
+
const toggleSelfProt = async () => {
|
|
8426
|
+
const next = !(selfProt ?? false);
|
|
8427
|
+
setSelfProt(next);
|
|
8428
|
+
try {
|
|
8429
|
+
await api.settings.setSelfProtection(next);
|
|
8430
|
+
setStatus(`\u2713 self-protection ${next ? "on" : "off"}`);
|
|
8431
|
+
} catch (e) {
|
|
8432
|
+
setSelfProt(!next);
|
|
8433
|
+
setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
|
|
8434
|
+
}
|
|
8435
|
+
};
|
|
8281
8436
|
const discard = () => {
|
|
8282
8437
|
if (q.data) setDlp({ ...q.data.layers.dlp, patterns: [...q.data.layers.dlp.patterns], custom: [...q.data.layers.dlp.custom] });
|
|
8283
8438
|
setDirty(false);
|
|
@@ -8307,7 +8462,12 @@ function DlpPanel({ focused }) {
|
|
|
8307
8462
|
const ci = sel - available.length;
|
|
8308
8463
|
mutate({ ...dlp, custom: dlp.custom.filter((_, i) => i !== ci) });
|
|
8309
8464
|
setSel((n) => Math.max(0, n - 1));
|
|
8310
|
-
} else if (input === "
|
|
8465
|
+
} else if (input === "g") {
|
|
8466
|
+
setGhostOn((v) => !v);
|
|
8467
|
+
setDirty(true);
|
|
8468
|
+
setStatus(null);
|
|
8469
|
+
} else if (input === "P") void toggleSelfProt();
|
|
8470
|
+
else if (input === "s") void save();
|
|
8311
8471
|
else if (input === "x") discard();
|
|
8312
8472
|
},
|
|
8313
8473
|
{ isActive: focused && !adding }
|
|
@@ -8319,7 +8479,13 @@ function DlpPanel({ focused }) {
|
|
|
8319
8479
|
/* @__PURE__ */ jsx5(Text5, { color: modeColor(dlp.mode), children: dlp.mode }),
|
|
8320
8480
|
dirty ? /* @__PURE__ */ jsx5(Text5, { color: theme.warn, children: " \u25CF unsaved (s save \xB7 x discard)" }) : null
|
|
8321
8481
|
] }),
|
|
8322
|
-
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: focused ? "\u2191\u2193
|
|
8482
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: focused ? "\u2191\u2193 \xB7 space toggle \xB7 m mode \xB7 a add \xB7 d remove \xB7 g ghost \xB7 P self-protect \xB7 s save" : "press \u2192 to edit" }),
|
|
8483
|
+
/* @__PURE__ */ jsxs5(Box5, { children: [
|
|
8484
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: "ghost: " }),
|
|
8485
|
+
/* @__PURE__ */ jsx5(Text5, { color: ghostOn ? theme.ok : theme.dim, children: ghostOn ? "on" : "off" }),
|
|
8486
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " self-protection: " }),
|
|
8487
|
+
/* @__PURE__ */ jsx5(Text5, { color: selfProt ? theme.ok : theme.dim, children: selfProt === null ? "\u2026" : selfProt ? "on" : "off" })
|
|
8488
|
+
] }),
|
|
8323
8489
|
/* @__PURE__ */ jsx5(Box5, { marginTop: 1, flexDirection: "column", children: available.map((p, i) => {
|
|
8324
8490
|
const on = enabled.has(p);
|
|
8325
8491
|
const active2 = focused && sel === i;
|
|
@@ -8387,12 +8553,15 @@ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
|
8387
8553
|
function StatsPanel({ active: active2 }) {
|
|
8388
8554
|
const stats = useLoader(() => api.stats.get());
|
|
8389
8555
|
const ts = useLoader(() => api.stats.timeseries({ period: "24h" }));
|
|
8556
|
+
const drift2 = useLoader(() => api.stats.drift(7));
|
|
8390
8557
|
usePoll(() => {
|
|
8391
8558
|
stats.reload();
|
|
8392
8559
|
ts.reload();
|
|
8393
8560
|
}, 5e3, active2);
|
|
8561
|
+
usePoll(drift2.reload, 3e4, active2);
|
|
8394
8562
|
const s = stats.data;
|
|
8395
8563
|
const points = ts.data?.timeseries ?? [];
|
|
8564
|
+
const driftRules = (drift2.data?.rules ?? []).slice(0, 5);
|
|
8396
8565
|
return /* @__PURE__ */ jsx6(DataView, { loading: stats.loading && !s, error: stats.error, children: s ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
8397
8566
|
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
8398
8567
|
/* @__PURE__ */ jsxs6(Text6, { bold: true, children: [
|
|
@@ -8445,7 +8614,7 @@ function StatsPanel({ active: active2 }) {
|
|
|
8445
8614
|
{ header: "MS", width: 5 },
|
|
8446
8615
|
{ header: "WHEN", width: 6 }
|
|
8447
8616
|
],
|
|
8448
|
-
rows: (s.recent_activity ?? []).slice(0,
|
|
8617
|
+
rows: (s.recent_activity ?? []).slice(0, 6).map((a) => [
|
|
8449
8618
|
{ value: a.decision, color: decisionColor(a.decision) },
|
|
8450
8619
|
{ value: truncate2(a.tool_name, 20), color: theme.accent },
|
|
8451
8620
|
{ value: String(a.evaluation_time_ms ?? "\u2014"), dim: true },
|
|
@@ -8453,6 +8622,31 @@ function StatsPanel({ active: active2 }) {
|
|
|
8453
8622
|
])
|
|
8454
8623
|
}
|
|
8455
8624
|
)
|
|
8625
|
+
] }),
|
|
8626
|
+
/* @__PURE__ */ jsxs6(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
8627
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
8628
|
+
"Denial drift ",
|
|
8629
|
+
drift2.data ? `(7d: ${drift2.data.total_current} now vs ${drift2.data.total_previous} prev)` : ""
|
|
8630
|
+
] }),
|
|
8631
|
+
driftRules.length ? /* @__PURE__ */ jsx6(
|
|
8632
|
+
Table,
|
|
8633
|
+
{
|
|
8634
|
+
columns: [
|
|
8635
|
+
{ header: "NOW", width: 5 },
|
|
8636
|
+
{ header: "PREV", width: 5 },
|
|
8637
|
+
{ header: "\u0394", width: 6 },
|
|
8638
|
+
{ header: "RULE", width: 22 },
|
|
8639
|
+
{ header: "REASON", width: 26 }
|
|
8640
|
+
],
|
|
8641
|
+
rows: driftRules.map((r) => [
|
|
8642
|
+
{ value: String(r.current), bold: true },
|
|
8643
|
+
{ value: String(r.previous), dim: true },
|
|
8644
|
+
{ value: r.is_new ? "NEW" : r.spike ? `+${r.delta}` : String(r.delta), color: r.is_new ? theme.ok : r.spike ? theme.bad : void 0 },
|
|
8645
|
+
{ value: truncate2(r.rule_id ?? "\u2014", 22), color: theme.accent },
|
|
8646
|
+
{ value: truncate2(r.reason ?? r.last_tool ?? "\u2014", 26), dim: true }
|
|
8647
|
+
])
|
|
8648
|
+
}
|
|
8649
|
+
) : /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " no denials in window" })
|
|
8456
8650
|
] })
|
|
8457
8651
|
] }) : null });
|
|
8458
8652
|
}
|
|
@@ -8678,7 +8872,16 @@ function App() {
|
|
|
8678
8872
|
const { exit } = useApp();
|
|
8679
8873
|
const [section, setSection] = useState7(0);
|
|
8680
8874
|
const [focus, setFocus] = useState7("nav");
|
|
8875
|
+
const [help, setHelp] = useState7(false);
|
|
8681
8876
|
useInput6((input, key) => {
|
|
8877
|
+
if (help) {
|
|
8878
|
+
setHelp(false);
|
|
8879
|
+
return;
|
|
8880
|
+
}
|
|
8881
|
+
if (input === "?" && focus === "nav") {
|
|
8882
|
+
setHelp(true);
|
|
8883
|
+
return;
|
|
8884
|
+
}
|
|
8682
8885
|
if (focus === "nav") {
|
|
8683
8886
|
if (key.upArrow) setSection((n) => (n - 1 + SECTIONS.length) % SECTIONS.length);
|
|
8684
8887
|
else if (key.downArrow) setSection((n) => (n + 1) % SECTIONS.length);
|
|
@@ -8692,6 +8895,7 @@ function App() {
|
|
|
8692
8895
|
const cols = process.stdout.columns ?? 100;
|
|
8693
8896
|
const rows = process.stdout.rows ?? 30;
|
|
8694
8897
|
const current = SECTIONS[section];
|
|
8898
|
+
if (help) return /* @__PURE__ */ jsx8(HelpOverlay, { cols, rows });
|
|
8695
8899
|
if (current.label === "Live" && focus === "panel") {
|
|
8696
8900
|
return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx8(LivePanel, { active: true, focused: true }) });
|
|
8697
8901
|
}
|
|
@@ -8702,10 +8906,23 @@ function App() {
|
|
|
8702
8906
|
/* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => /* @__PURE__ */ jsx8(Text8, { color: i === section ? theme.accentBright : void 0, bold: i === section, children: (i === section ? "\u25B8 " : " ") + s.label }, s.label)) }),
|
|
8703
8907
|
/* @__PURE__ */ jsx8(Box8, { flexGrow: 1, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, children: /* @__PURE__ */ jsx8(Panel, { active: focus === "panel" || current.label !== "Live", focused: focus === "panel" }) })
|
|
8704
8908
|
] }),
|
|
8705
|
-
/* @__PURE__ */ jsx8(Box8, { children: focus === "nav" ? /* @__PURE__ */ jsx8(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ["q", "quit"]] }) : /* @__PURE__ */ jsx8(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
|
|
8909
|
+
/* @__PURE__ */ jsx8(Box8, { children: focus === "nav" ? /* @__PURE__ */ jsx8(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ["?", "help"], ["q", "quit"]] }) : /* @__PURE__ */ jsx8(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
|
|
8706
8910
|
] });
|
|
8707
8911
|
}
|
|
8708
|
-
|
|
8912
|
+
function HelpOverlay({ cols, rows }) {
|
|
8913
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
|
|
8914
|
+
/* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
|
|
8915
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginBottom: 1, children: [
|
|
8916
|
+
/* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accent, children: group }),
|
|
8917
|
+
keys.map(([k, desc]) => /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
8918
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
|
|
8919
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: desc })
|
|
8920
|
+
] }, k))
|
|
8921
|
+
] }, group)) }),
|
|
8922
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "press any key to close" })
|
|
8923
|
+
] });
|
|
8924
|
+
}
|
|
8925
|
+
var SECTIONS, BANNER_HEX, HELP;
|
|
8709
8926
|
var init_App = __esm({
|
|
8710
8927
|
"src/tui/App.tsx"() {
|
|
8711
8928
|
"use strict";
|
|
@@ -8727,6 +8944,12 @@ var init_App = __esm({
|
|
|
8727
8944
|
{ label: "Audit", Panel: AuditPanel }
|
|
8728
8945
|
];
|
|
8729
8946
|
BANNER_HEX = ["#1432A0", "#2850BE", "#3C6ED7", "#5A8CE6", "#82AAF0", "#AAC8FA"];
|
|
8947
|
+
HELP = [
|
|
8948
|
+
["Global", [["\u2191\u2193", "move between sections"], ["\u2192 / enter", "open a section"], ["\u2190 / esc", "back to the menu"], ["?", "this help"], ["q", "quit"]]],
|
|
8949
|
+
["Live", [["\u2191\u2193", "select a stream row"], ["w", "whitelist the selected DENY"], ["b", "block the selected ALLOW"], ["d / x / r", "filter denies / dlp / rate-limit"], ["f", "local / cloud filter"], ["/", "search"], ["s", "sessions (\u2191\u2193 pick, enter open)"], ["space", "copy mode (freeze)"]]],
|
|
8950
|
+
["Policies", [["\u2191\u2193", "browse / select"], ["enter", "open rules \u2192 open a rule"], ["space", "toggle a rule on/off"], ["e", "flip effect"], ["n", "new rule"], ["d", "delete rule"], ["m", "flip mode"], ["D", "dry-run the draft"], ["v", "versions / rollback"], ["s", "save"], ["x", "discard"]]],
|
|
8951
|
+
["Rate limit / DLP", [["\u2191\u2193", "field / pattern"], ["\u2190\u2192", "adjust (shift = \xB110)"], ["space", "toggle pattern"], ["m", "mode"], ["a / d", "add / remove custom"], ["g", "ghost mode"], ["P", "self-protection"], ["s", "save"]]]
|
|
8952
|
+
];
|
|
8730
8953
|
}
|
|
8731
8954
|
});
|
|
8732
8955
|
|
|
@@ -9334,6 +9557,16 @@ async function run5(argv) {
|
|
|
9334
9557
|
else err(green(` \u2713 Whitelisted (${res2.scope})`) + dim(` \u2192 ${res2.policy_id} v${res2.policy_version}`));
|
|
9335
9558
|
return 0;
|
|
9336
9559
|
}
|
|
9560
|
+
if (positionals[0] === "block") {
|
|
9561
|
+
const id = positionals[1];
|
|
9562
|
+
if (!id) return err(" Usage: audit block <logId> [--scope exact|tool]"), 1;
|
|
9563
|
+
const scope = flagStr(flags, "scope") ?? "exact";
|
|
9564
|
+
const res2 = await api.audit.block(id, scope);
|
|
9565
|
+
if (json) return printJson(res2), 0;
|
|
9566
|
+
if (res2.deduped) err(green(" \u2713 ") + dim("Equivalent DENY already present."));
|
|
9567
|
+
else err(green(` \u2713 Blocked (${res2.scope})`) + dim(` \u2192 ${res2.policy_id} v${res2.policy_version}`));
|
|
9568
|
+
return 0;
|
|
9569
|
+
}
|
|
9337
9570
|
const query = {
|
|
9338
9571
|
filter: flagStr(flags, "filter"),
|
|
9339
9572
|
tool: flagStr(flags, "tool"),
|
|
@@ -9441,6 +9674,395 @@ var init_agents2 = __esm({
|
|
|
9441
9674
|
}
|
|
9442
9675
|
});
|
|
9443
9676
|
|
|
9677
|
+
// src/commands/doctor.ts
|
|
9678
|
+
import { existsSync as existsSync4, statSync as statSync2 } from "fs";
|
|
9679
|
+
import { homedir as homedir4 } from "os";
|
|
9680
|
+
import { join as join6 } from "path";
|
|
9681
|
+
async function run6(argv) {
|
|
9682
|
+
const { flags } = parse(argv);
|
|
9683
|
+
const json = flagBool(flags, "json");
|
|
9684
|
+
const checks = [];
|
|
9685
|
+
if (!isAuthenticated()) {
|
|
9686
|
+
checks.push({ name: "login", ok: false, detail: "not logged in \u2014 run `solongate login`" });
|
|
9687
|
+
} else {
|
|
9688
|
+
const { apiUrl } = resolveCredentials();
|
|
9689
|
+
checks.push({ name: "login", ok: true, detail: `paired \xB7 ${apiUrl}` });
|
|
9690
|
+
try {
|
|
9691
|
+
const active2 = await api.policies.active();
|
|
9692
|
+
if (active2.policy) {
|
|
9693
|
+
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}` });
|
|
9694
|
+
} else {
|
|
9695
|
+
checks.push({ name: "active policy", ok: "warn", detail: "no policy resolves \u2014 every call falls back to default" });
|
|
9696
|
+
}
|
|
9697
|
+
const sec = active2.security;
|
|
9698
|
+
checks.push({ name: "rate limit", ok: sec?.rateLimit ? true : "warn", detail: sec?.rateLimit ? `${sec.rateLimit.perMinute}/min` : "off" });
|
|
9699
|
+
checks.push({ name: "dlp", ok: sec?.dlpBlock ? true : "warn", detail: sec?.dlpBlock ? `block \xB7 ${sec.dlpBlock.patterns.length} patterns` : sec?.dlpRedact ? "redact" : "off" });
|
|
9700
|
+
checks.push({ name: "self-protection", ok: active2.self_protection_enabled ? true : "warn", detail: active2.self_protection_enabled ? "on" : "off" });
|
|
9701
|
+
} catch (e) {
|
|
9702
|
+
checks.push({ name: "api", ok: false, detail: "unreachable: " + (e instanceof Error ? e.message : String(e)) });
|
|
9703
|
+
}
|
|
9704
|
+
try {
|
|
9705
|
+
const g = await api.settings.getGuardStatus();
|
|
9706
|
+
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\`` });
|
|
9707
|
+
} catch {
|
|
9708
|
+
}
|
|
9709
|
+
}
|
|
9710
|
+
if (existsSync4(LOCAL_LOG2)) {
|
|
9711
|
+
const st = statSync2(LOCAL_LOG2);
|
|
9712
|
+
const ageMin = (Date.now() - st.mtimeMs) / 6e4;
|
|
9713
|
+
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"}` });
|
|
9714
|
+
} else {
|
|
9715
|
+
checks.push({ name: "local logs", ok: "warn", detail: "off (logs go to cloud) \u2014 enable in dashboard \u2192 Settings" });
|
|
9716
|
+
}
|
|
9717
|
+
if (json) return printJson(checks), checks.some((c2) => c2.ok === false) ? 1 : 0;
|
|
9718
|
+
err("");
|
|
9719
|
+
err(` ${bold("SolonGate doctor")}`);
|
|
9720
|
+
err("");
|
|
9721
|
+
for (const c2 of checks) {
|
|
9722
|
+
const mark = c2.ok === true ? green("\u2713") : c2.ok === "warn" ? yellow("!") : red("\u2717");
|
|
9723
|
+
err(` ${mark} ${c2.name.padEnd(16)} ${dim(c2.detail)}`);
|
|
9724
|
+
}
|
|
9725
|
+
err("");
|
|
9726
|
+
const bad = checks.filter((c2) => c2.ok === false).length;
|
|
9727
|
+
const warn = checks.filter((c2) => c2.ok === "warn").length;
|
|
9728
|
+
if (bad) err(` ${red(`${bad} problem(s)`)}${warn ? dim(` \xB7 ${warn} warning(s)`) : ""}`);
|
|
9729
|
+
else if (warn) err(` ${yellow(`${warn} warning(s)`)} ${dim("\u2014 guard is working")}`);
|
|
9730
|
+
else err(` ${green("all good")}`);
|
|
9731
|
+
return bad ? 1 : 0;
|
|
9732
|
+
}
|
|
9733
|
+
var LOCAL_LOG2;
|
|
9734
|
+
var init_doctor = __esm({
|
|
9735
|
+
"src/commands/doctor.ts"() {
|
|
9736
|
+
"use strict";
|
|
9737
|
+
init_api_client();
|
|
9738
|
+
init_format();
|
|
9739
|
+
init_args();
|
|
9740
|
+
LOCAL_LOG2 = join6(homedir4(), ".solongate", "local-logs", "solongate-audit.jsonl");
|
|
9741
|
+
}
|
|
9742
|
+
});
|
|
9743
|
+
|
|
9744
|
+
// src/commands/watch.ts
|
|
9745
|
+
import { closeSync as closeSync2, existsSync as existsSync5, openSync as openSync2, readSync as readSync2, statSync as statSync3 } from "fs";
|
|
9746
|
+
import { homedir as homedir5 } from "os";
|
|
9747
|
+
import { join as join7 } from "path";
|
|
9748
|
+
function tailLocal(file, maxBytes = 131072) {
|
|
9749
|
+
try {
|
|
9750
|
+
const size = statSync3(file).size;
|
|
9751
|
+
const start = Math.max(0, size - maxBytes);
|
|
9752
|
+
const fd = openSync2(file, "r");
|
|
9753
|
+
const buf = Buffer.alloc(size - start);
|
|
9754
|
+
readSync2(fd, buf, 0, buf.length, start);
|
|
9755
|
+
closeSync2(fd);
|
|
9756
|
+
const lines = buf.toString("utf-8").split("\n").filter(Boolean);
|
|
9757
|
+
if (start > 0) lines.shift();
|
|
9758
|
+
return lines;
|
|
9759
|
+
} catch {
|
|
9760
|
+
return [];
|
|
9761
|
+
}
|
|
9762
|
+
}
|
|
9763
|
+
function print(r, json) {
|
|
9764
|
+
if (json) return out(JSON.stringify(r));
|
|
9765
|
+
const dec = r.decision === "ALLOW" ? c.green : c.red;
|
|
9766
|
+
const src = r.source === "local" ? c.green + "LOC" : c.blue4 + "CLD";
|
|
9767
|
+
out(
|
|
9768
|
+
`${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}`
|
|
9769
|
+
);
|
|
9770
|
+
}
|
|
9771
|
+
async function run7(argv) {
|
|
9772
|
+
const { flags } = parse(argv);
|
|
9773
|
+
const json = flagBool(flags, "json");
|
|
9774
|
+
const decisionFilter = (flagStr(flags, "filter") || "").toUpperCase();
|
|
9775
|
+
const toolFilter = (flagStr(flags, "tool") || "").toLowerCase();
|
|
9776
|
+
const localOnly = flagBool(flags, "local-only");
|
|
9777
|
+
const cloudOnly = flagBool(flags, "cloud-only");
|
|
9778
|
+
const keep = (r) => {
|
|
9779
|
+
if (decisionFilter && r.decision.toUpperCase() !== decisionFilter && !(decisionFilter === "DENY" && r.decision === "DENIED")) return false;
|
|
9780
|
+
if (toolFilter && !r.tool.toLowerCase().includes(toolFilter)) return false;
|
|
9781
|
+
return true;
|
|
9782
|
+
};
|
|
9783
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9784
|
+
let lastLocalTs = 0;
|
|
9785
|
+
let first = true;
|
|
9786
|
+
const emit = (rows) => {
|
|
9787
|
+
rows.sort((a, b) => a.at - b.at);
|
|
9788
|
+
for (const r of rows) if (keep(r)) print(r, json);
|
|
9789
|
+
};
|
|
9790
|
+
const pollLocal = () => {
|
|
9791
|
+
if (cloudOnly || !existsSync5(LOCAL_LOG3)) return;
|
|
9792
|
+
const rows = [];
|
|
9793
|
+
for (const line of tailLocal(LOCAL_LOG3)) {
|
|
9794
|
+
try {
|
|
9795
|
+
const j = JSON.parse(line);
|
|
9796
|
+
const at = Date.parse(String(j.ts ?? ""));
|
|
9797
|
+
if (!Number.isFinite(at) || at <= lastLocalTs) continue;
|
|
9798
|
+
rows.push({
|
|
9799
|
+
at,
|
|
9800
|
+
tool: String(j.tool ?? "?"),
|
|
9801
|
+
decision: String(j.decision ?? "ALLOW"),
|
|
9802
|
+
permission: String(j.permission ?? ""),
|
|
9803
|
+
detail: (j.arguments ? JSON.stringify(j.arguments) : String(j.reason ?? "")).replace(/\s+/g, " "),
|
|
9804
|
+
agent: String(j.agent_name ?? ""),
|
|
9805
|
+
source: "local",
|
|
9806
|
+
dlp: !!j.dlp
|
|
9807
|
+
});
|
|
9808
|
+
} catch {
|
|
9809
|
+
}
|
|
9810
|
+
}
|
|
9811
|
+
if (rows.length) lastLocalTs = rows[rows.length - 1].at;
|
|
9812
|
+
if (!first) emit(rows);
|
|
9813
|
+
};
|
|
9814
|
+
const pollCloud = async () => {
|
|
9815
|
+
if (localOnly) return;
|
|
9816
|
+
try {
|
|
9817
|
+
const res = await api.audit.list({ limit: 50 });
|
|
9818
|
+
const rows = [];
|
|
9819
|
+
for (const e of res.entries) {
|
|
9820
|
+
if (seen.has(e.id)) continue;
|
|
9821
|
+
seen.add(e.id);
|
|
9822
|
+
rows.push({
|
|
9823
|
+
at: Date.parse(e.created_at),
|
|
9824
|
+
tool: e.tool_name,
|
|
9825
|
+
decision: e.decision,
|
|
9826
|
+
permission: e.permission ?? "",
|
|
9827
|
+
detail: (e.arguments_summary ? JSON.stringify(e.arguments_summary) : e.reason ?? "").replace(/\s+/g, " "),
|
|
9828
|
+
agent: e.agent_name ?? "",
|
|
9829
|
+
source: "cloud",
|
|
9830
|
+
dlp: !!e.dlp_matches?.length
|
|
9831
|
+
});
|
|
9832
|
+
}
|
|
9833
|
+
if (!first) emit(rows);
|
|
9834
|
+
} catch {
|
|
9835
|
+
}
|
|
9836
|
+
};
|
|
9837
|
+
if (!json) err(` ${c.dim}watching guard stream \u2014 Ctrl+C to stop${c.reset}`);
|
|
9838
|
+
pollLocal();
|
|
9839
|
+
await pollCloud();
|
|
9840
|
+
first = false;
|
|
9841
|
+
return new Promise(() => {
|
|
9842
|
+
setInterval(pollLocal, 2e3);
|
|
9843
|
+
setInterval(() => void pollCloud(), 4e3);
|
|
9844
|
+
});
|
|
9845
|
+
}
|
|
9846
|
+
var LOCAL_LOG3, trunc, time;
|
|
9847
|
+
var init_watch = __esm({
|
|
9848
|
+
"src/commands/watch.ts"() {
|
|
9849
|
+
"use strict";
|
|
9850
|
+
init_api_client();
|
|
9851
|
+
init_cli_utils();
|
|
9852
|
+
init_args();
|
|
9853
|
+
init_format();
|
|
9854
|
+
LOCAL_LOG3 = join7(homedir5(), ".solongate", "local-logs", "solongate-audit.jsonl");
|
|
9855
|
+
trunc = (s, n) => s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
|
|
9856
|
+
time = (ms) => new Date(ms).toTimeString().slice(0, 8);
|
|
9857
|
+
}
|
|
9858
|
+
});
|
|
9859
|
+
|
|
9860
|
+
// src/commands/keys.ts
|
|
9861
|
+
async function run8(argv) {
|
|
9862
|
+
const { positionals, flags } = parse(argv);
|
|
9863
|
+
const sub = positionals[0] ?? "list";
|
|
9864
|
+
const json = flagBool(flags, "json");
|
|
9865
|
+
switch (sub) {
|
|
9866
|
+
case "help":
|
|
9867
|
+
return err(USAGE6), 0;
|
|
9868
|
+
case "list": {
|
|
9869
|
+
const { keys } = await api.keys.list();
|
|
9870
|
+
if (json) return printJson(keys), 0;
|
|
9871
|
+
if (!keys.length) return err(dim(" No API keys.")), 0;
|
|
9872
|
+
table(
|
|
9873
|
+
["ID", "NAME", "PREFIX", "CREATED"],
|
|
9874
|
+
keys.map((k) => [dim(truncate3(k.id, 10)), truncate3(k.name, 24), cyan(k.key_prefix), dim(k.created_at)])
|
|
9875
|
+
);
|
|
9876
|
+
return 0;
|
|
9877
|
+
}
|
|
9878
|
+
case "create": {
|
|
9879
|
+
const name = flagStr(flags, "name");
|
|
9880
|
+
if (!name) return err(" Usage: keys create --name <name>"), 1;
|
|
9881
|
+
const res = await api.keys.create(name, !flagBool(flags, "test"));
|
|
9882
|
+
if (json) return printJson(res), 0;
|
|
9883
|
+
err(green(` \u2713 Created "${res.name}"`));
|
|
9884
|
+
err("");
|
|
9885
|
+
err(" " + bold(res.key));
|
|
9886
|
+
err(dim(" \u2191 shown only once \u2014 copy it now."));
|
|
9887
|
+
return 0;
|
|
9888
|
+
}
|
|
9889
|
+
case "revoke": {
|
|
9890
|
+
const id = positionals[1];
|
|
9891
|
+
if (!id) return err(" Usage: keys revoke <id>"), 1;
|
|
9892
|
+
await api.keys.revoke(id);
|
|
9893
|
+
if (json) return printJson({ ok: true, id }), 0;
|
|
9894
|
+
return err(green(` \u2713 Revoked ${id}`)), 0;
|
|
9895
|
+
}
|
|
9896
|
+
default:
|
|
9897
|
+
return err(USAGE6), 1;
|
|
9898
|
+
}
|
|
9899
|
+
}
|
|
9900
|
+
var USAGE6;
|
|
9901
|
+
var init_keys2 = __esm({
|
|
9902
|
+
"src/commands/keys.ts"() {
|
|
9903
|
+
"use strict";
|
|
9904
|
+
init_api_client();
|
|
9905
|
+
init_args();
|
|
9906
|
+
init_format();
|
|
9907
|
+
USAGE6 = `${bold("solongate keys")} \u2014 API keys
|
|
9908
|
+
|
|
9909
|
+
keys list List keys (prefix only)
|
|
9910
|
+
keys create --name <n> Create a key (secret shown once)
|
|
9911
|
+
keys revoke <id> Revoke a key
|
|
9912
|
+
|
|
9913
|
+
Add --json for machine-readable output.`;
|
|
9914
|
+
}
|
|
9915
|
+
});
|
|
9916
|
+
|
|
9917
|
+
// src/commands/mcp.ts
|
|
9918
|
+
async function run9(argv) {
|
|
9919
|
+
const { flags } = parse(argv);
|
|
9920
|
+
const json = flagBool(flags, "json");
|
|
9921
|
+
const { servers } = await api.mcp.list();
|
|
9922
|
+
if (json) return printJson(servers), 0;
|
|
9923
|
+
if (!servers.length) return err(dim(" No MCP servers registered.")), 0;
|
|
9924
|
+
table(
|
|
9925
|
+
["STATUS", "NAME", "TARGET"],
|
|
9926
|
+
servers.map((s) => [
|
|
9927
|
+
s.status === "active" ? green(s.status) : yellow(s.status),
|
|
9928
|
+
cyan(truncate3(s.name, 24)),
|
|
9929
|
+
dim(truncate3(s.url || s.command || "\u2014", 46))
|
|
9930
|
+
])
|
|
9931
|
+
);
|
|
9932
|
+
return 0;
|
|
9933
|
+
}
|
|
9934
|
+
var init_mcp2 = __esm({
|
|
9935
|
+
"src/commands/mcp.ts"() {
|
|
9936
|
+
"use strict";
|
|
9937
|
+
init_api_client();
|
|
9938
|
+
init_args();
|
|
9939
|
+
init_format();
|
|
9940
|
+
}
|
|
9941
|
+
});
|
|
9942
|
+
|
|
9943
|
+
// src/commands/alerts.ts
|
|
9944
|
+
async function run10(argv) {
|
|
9945
|
+
const { positionals, flags } = parse(argv);
|
|
9946
|
+
const sub = positionals[0] ?? "list";
|
|
9947
|
+
const json = flagBool(flags, "json");
|
|
9948
|
+
switch (sub) {
|
|
9949
|
+
case "help":
|
|
9950
|
+
return err(USAGE7), 0;
|
|
9951
|
+
case "list": {
|
|
9952
|
+
const { rules } = await api.settings.getAlerts();
|
|
9953
|
+
if (json) return printJson(rules), 0;
|
|
9954
|
+
if (!rules.length) return err(dim(" No alert rules.")), 0;
|
|
9955
|
+
table(
|
|
9956
|
+
["ID", "ON", "SIGNAL", "THRESH", "WINDOW", "CHANNELS"],
|
|
9957
|
+
rules.map((r) => [
|
|
9958
|
+
dim(truncate3(r.id, 10)),
|
|
9959
|
+
r.enabled ? green("\u25CF") : dim("\u25CB"),
|
|
9960
|
+
cyan(r.signal),
|
|
9961
|
+
`${r.threshold}`,
|
|
9962
|
+
`${r.windowSeconds}s`,
|
|
9963
|
+
dim([...r.emails ?? [], ...(r.telegram ?? []).map((t) => "tg:" + t), ...(r.slackUrls ?? []).map(() => "slack")].join(" ") || "\u2014")
|
|
9964
|
+
])
|
|
9965
|
+
);
|
|
9966
|
+
return 0;
|
|
9967
|
+
}
|
|
9968
|
+
case "add": {
|
|
9969
|
+
const email = flagStr(flags, "email");
|
|
9970
|
+
const telegram = flagStr(flags, "telegram");
|
|
9971
|
+
const slack = flagStr(flags, "slack");
|
|
9972
|
+
if (!email && !telegram && !slack) return err(" Need a channel: --email / --telegram / --slack"), 1;
|
|
9973
|
+
const res = await api.settings.createAlert({
|
|
9974
|
+
signal: flagStr(flags, "signal") ?? "deny",
|
|
9975
|
+
threshold: flagNum(flags, "threshold") ?? 5,
|
|
9976
|
+
windowSeconds: flagNum(flags, "window") ?? 300,
|
|
9977
|
+
emails: email ? [email] : void 0,
|
|
9978
|
+
telegram: telegram ? [telegram] : void 0,
|
|
9979
|
+
slackUrl: slack
|
|
9980
|
+
});
|
|
9981
|
+
if (json) return printJson(res.rule), 0;
|
|
9982
|
+
return err(green(` \u2713 Alert added (${res.rule.signal}, ${res.rule.threshold}/${res.rule.windowSeconds}s)`)), 0;
|
|
9983
|
+
}
|
|
9984
|
+
case "remove": {
|
|
9985
|
+
const id = positionals[1];
|
|
9986
|
+
if (!id) return err(" Usage: alerts remove <id>"), 1;
|
|
9987
|
+
await api.settings.deleteAlert(id);
|
|
9988
|
+
if (json) return printJson({ ok: true, id }), 0;
|
|
9989
|
+
return err(green(` \u2713 Removed ${id}`)), 0;
|
|
9990
|
+
}
|
|
9991
|
+
default:
|
|
9992
|
+
return err(USAGE7), 1;
|
|
9993
|
+
}
|
|
9994
|
+
}
|
|
9995
|
+
var USAGE7;
|
|
9996
|
+
var init_alerts = __esm({
|
|
9997
|
+
"src/commands/alerts.ts"() {
|
|
9998
|
+
"use strict";
|
|
9999
|
+
init_api_client();
|
|
10000
|
+
init_args();
|
|
10001
|
+
init_format();
|
|
10002
|
+
USAGE7 = `${bold("solongate alerts")} \u2014 spike alerts (Telegram / email)
|
|
10003
|
+
|
|
10004
|
+
alerts list
|
|
10005
|
+
alerts add --signal deny|dlp|ratelimit|any --threshold N --window S
|
|
10006
|
+
(--email <a> | --telegram <chatId> | --slack <url>)
|
|
10007
|
+
alerts remove <id>
|
|
10008
|
+
|
|
10009
|
+
Add --json for machine-readable output.`;
|
|
10010
|
+
}
|
|
10011
|
+
});
|
|
10012
|
+
|
|
10013
|
+
// src/commands/webhooks.ts
|
|
10014
|
+
async function run11(argv) {
|
|
10015
|
+
const { positionals, flags } = parse(argv);
|
|
10016
|
+
const sub = positionals[0] ?? "list";
|
|
10017
|
+
const json = flagBool(flags, "json");
|
|
10018
|
+
switch (sub) {
|
|
10019
|
+
case "help":
|
|
10020
|
+
return err(USAGE8), 0;
|
|
10021
|
+
case "list": {
|
|
10022
|
+
const { webhooks } = await api.settings.getWebhooks();
|
|
10023
|
+
if (json) return printJson(webhooks), 0;
|
|
10024
|
+
if (!webhooks.length) return err(dim(" No webhooks.")), 0;
|
|
10025
|
+
table(
|
|
10026
|
+
["ID", "ON", "EVENTS", "URL"],
|
|
10027
|
+
webhooks.map((w) => [dim(truncate3(w.id, 10)), w.enabled ? green("\u25CF") : dim("\u25CB"), cyan(w.events), dim(truncate3(w.url, 46))])
|
|
10028
|
+
);
|
|
10029
|
+
return 0;
|
|
10030
|
+
}
|
|
10031
|
+
case "add": {
|
|
10032
|
+
const url = flagStr(flags, "url");
|
|
10033
|
+
if (!url) return err(" Usage: webhooks add --url <https://\u2026> [--events denials|allowed|all]"), 1;
|
|
10034
|
+
const res = await api.settings.createWebhook({ url, events: flagStr(flags, "events") ?? "denials" });
|
|
10035
|
+
if (json) return printJson(res.webhook), 0;
|
|
10036
|
+
return err(green(` \u2713 Webhook added (${res.webhook.events}) \u2192 ${res.webhook.url}`)), 0;
|
|
10037
|
+
}
|
|
10038
|
+
case "remove": {
|
|
10039
|
+
const id = positionals[1];
|
|
10040
|
+
if (!id) return err(" Usage: webhooks remove <id>"), 1;
|
|
10041
|
+
await api.settings.deleteWebhook(id);
|
|
10042
|
+
if (json) return printJson({ ok: true, id }), 0;
|
|
10043
|
+
return err(green(` \u2713 Removed ${id}`)), 0;
|
|
10044
|
+
}
|
|
10045
|
+
default:
|
|
10046
|
+
return err(USAGE8), 1;
|
|
10047
|
+
}
|
|
10048
|
+
}
|
|
10049
|
+
var USAGE8;
|
|
10050
|
+
var init_webhooks = __esm({
|
|
10051
|
+
"src/commands/webhooks.ts"() {
|
|
10052
|
+
"use strict";
|
|
10053
|
+
init_api_client();
|
|
10054
|
+
init_args();
|
|
10055
|
+
init_format();
|
|
10056
|
+
USAGE8 = `${bold("solongate webhooks")} \u2014 event webhooks
|
|
10057
|
+
|
|
10058
|
+
webhooks list
|
|
10059
|
+
webhooks add --url <https://\u2026> [--events denials|allowed|all]
|
|
10060
|
+
webhooks remove <id>
|
|
10061
|
+
|
|
10062
|
+
Add --json for machine-readable output.`;
|
|
10063
|
+
}
|
|
10064
|
+
});
|
|
10065
|
+
|
|
9444
10066
|
// src/commands/index.ts
|
|
9445
10067
|
var commands_exports = {};
|
|
9446
10068
|
__export(commands_exports, {
|
|
@@ -9463,6 +10085,18 @@ async function dispatch(command, argv) {
|
|
|
9463
10085
|
return runAgents(argv);
|
|
9464
10086
|
case "agent":
|
|
9465
10087
|
return runAgent(argv);
|
|
10088
|
+
case "doctor":
|
|
10089
|
+
return run6(argv);
|
|
10090
|
+
case "watch":
|
|
10091
|
+
return run7(argv);
|
|
10092
|
+
case "keys":
|
|
10093
|
+
return run8(argv);
|
|
10094
|
+
case "mcp":
|
|
10095
|
+
return run9(argv);
|
|
10096
|
+
case "alerts":
|
|
10097
|
+
return run10(argv);
|
|
10098
|
+
case "webhooks":
|
|
10099
|
+
return run11(argv);
|
|
9466
10100
|
default:
|
|
9467
10101
|
err(` Unknown command: ${command}`);
|
|
9468
10102
|
return 1;
|
|
@@ -9497,7 +10131,13 @@ var init_commands = __esm({
|
|
|
9497
10131
|
init_stats2();
|
|
9498
10132
|
init_audit2();
|
|
9499
10133
|
init_agents2();
|
|
9500
|
-
|
|
10134
|
+
init_doctor();
|
|
10135
|
+
init_watch();
|
|
10136
|
+
init_keys2();
|
|
10137
|
+
init_mcp2();
|
|
10138
|
+
init_alerts();
|
|
10139
|
+
init_webhooks();
|
|
10140
|
+
COMMAND_NAMES = ["policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent", "doctor", "watch", "keys", "mcp", "alerts", "webhooks"];
|
|
9501
10141
|
}
|
|
9502
10142
|
});
|
|
9503
10143
|
|
|
@@ -9513,14 +10153,14 @@ __export(global_install_exports, {
|
|
|
9513
10153
|
runGlobalRestore: () => runGlobalRestore,
|
|
9514
10154
|
unlockProtected: () => unlockProtected
|
|
9515
10155
|
});
|
|
9516
|
-
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as
|
|
9517
|
-
import { resolve as resolve4, join as
|
|
9518
|
-
import { homedir as
|
|
10156
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
|
|
10157
|
+
import { resolve as resolve4, join as join8, dirname } from "path";
|
|
10158
|
+
import { homedir as homedir6 } from "os";
|
|
9519
10159
|
import { fileURLToPath } from "url";
|
|
9520
10160
|
import { createInterface } from "readline";
|
|
9521
10161
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
9522
10162
|
function lockFile(file) {
|
|
9523
|
-
if (!
|
|
10163
|
+
if (!existsSync6(file)) return;
|
|
9524
10164
|
try {
|
|
9525
10165
|
if (process.platform === "win32") {
|
|
9526
10166
|
try {
|
|
@@ -9546,7 +10186,7 @@ function lockFile(file) {
|
|
|
9546
10186
|
}
|
|
9547
10187
|
}
|
|
9548
10188
|
function unlockFile(file) {
|
|
9549
|
-
if (!
|
|
10189
|
+
if (!existsSync6(file)) return;
|
|
9550
10190
|
try {
|
|
9551
10191
|
if (process.platform === "win32") {
|
|
9552
10192
|
try {
|
|
@@ -9578,10 +10218,10 @@ function unlockFile(file) {
|
|
|
9578
10218
|
function protectedTargets() {
|
|
9579
10219
|
const p = globalPaths();
|
|
9580
10220
|
return [
|
|
9581
|
-
|
|
9582
|
-
|
|
9583
|
-
|
|
9584
|
-
|
|
10221
|
+
join8(p.hooksDir, "guard.mjs"),
|
|
10222
|
+
join8(p.hooksDir, "audit.mjs"),
|
|
10223
|
+
join8(p.hooksDir, "stop.mjs"),
|
|
10224
|
+
join8(p.hooksDir, "shield.mjs"),
|
|
9585
10225
|
p.configPath,
|
|
9586
10226
|
p.settingsPath
|
|
9587
10227
|
];
|
|
@@ -9593,26 +10233,26 @@ function unlockProtected() {
|
|
|
9593
10233
|
for (const f of protectedTargets()) unlockFile(f);
|
|
9594
10234
|
}
|
|
9595
10235
|
function globalPaths() {
|
|
9596
|
-
const home =
|
|
9597
|
-
const sgDir =
|
|
9598
|
-
const hooksDir =
|
|
9599
|
-
const claudeDir =
|
|
10236
|
+
const home = homedir6();
|
|
10237
|
+
const sgDir = join8(home, ".solongate");
|
|
10238
|
+
const hooksDir = join8(sgDir, "hooks");
|
|
10239
|
+
const claudeDir = join8(home, ".claude");
|
|
9600
10240
|
return {
|
|
9601
10241
|
home,
|
|
9602
10242
|
sgDir,
|
|
9603
10243
|
hooksDir,
|
|
9604
10244
|
claudeDir,
|
|
9605
|
-
settingsPath:
|
|
9606
|
-
backupPath:
|
|
9607
|
-
configPath:
|
|
10245
|
+
settingsPath: join8(claudeDir, "settings.json"),
|
|
10246
|
+
backupPath: join8(claudeDir, "settings.solongate.bak"),
|
|
10247
|
+
configPath: join8(sgDir, "cloud-guard.json")
|
|
9608
10248
|
};
|
|
9609
10249
|
}
|
|
9610
10250
|
function readHook(filename) {
|
|
9611
|
-
return readFileSync6(
|
|
10251
|
+
return readFileSync6(join8(HOOKS_DIR, filename), "utf-8");
|
|
9612
10252
|
}
|
|
9613
10253
|
function readGuard() {
|
|
9614
|
-
const bundled =
|
|
9615
|
-
return
|
|
10254
|
+
const bundled = join8(HOOKS_DIR, "guard.bundled.mjs");
|
|
10255
|
+
return existsSync6(bundled) ? readFileSync6(bundled, "utf-8") : readHook("guard.mjs");
|
|
9616
10256
|
}
|
|
9617
10257
|
function ask(question) {
|
|
9618
10258
|
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
@@ -9625,10 +10265,10 @@ function runGlobalRestore() {
|
|
|
9625
10265
|
const p = globalPaths();
|
|
9626
10266
|
unlockProtected();
|
|
9627
10267
|
removeClaudeShim();
|
|
9628
|
-
if (
|
|
10268
|
+
if (existsSync6(p.backupPath)) {
|
|
9629
10269
|
writeFileSync3(p.settingsPath, readFileSync6(p.backupPath, "utf-8"));
|
|
9630
10270
|
console.log(` Restored ${p.settingsPath} from backup.`);
|
|
9631
|
-
} else if (
|
|
10271
|
+
} else if (existsSync6(p.settingsPath)) {
|
|
9632
10272
|
try {
|
|
9633
10273
|
const s = JSON.parse(readFileSync6(p.settingsPath, "utf-8"));
|
|
9634
10274
|
delete s.hooks;
|
|
@@ -9666,11 +10306,11 @@ function shimTargets() {
|
|
|
9666
10306
|
return [];
|
|
9667
10307
|
}
|
|
9668
10308
|
}
|
|
9669
|
-
return [".bashrc", ".zshrc", ".profile"].map((f) =>
|
|
10309
|
+
return [".bashrc", ".zshrc", ".profile"].map((f) => join8(homedir6(), f)).filter((f) => existsSync6(f));
|
|
9670
10310
|
}
|
|
9671
10311
|
function writeShimBlock(file, block2) {
|
|
9672
10312
|
const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
|
|
9673
|
-
let content =
|
|
10313
|
+
let content = existsSync6(file) ? readFileSync6(file, "utf-8") : "";
|
|
9674
10314
|
content = content.replace(re, "");
|
|
9675
10315
|
if (block2) {
|
|
9676
10316
|
if (content.length && !content.endsWith("\n")) content += "\n";
|
|
@@ -9737,18 +10377,18 @@ async function runGlobalInstall(opts = {}) {
|
|
|
9737
10377
|
mkdirSync3(p.hooksDir, { recursive: true });
|
|
9738
10378
|
mkdirSync3(p.claudeDir, { recursive: true });
|
|
9739
10379
|
unlockProtected();
|
|
9740
|
-
writeFileSync3(
|
|
9741
|
-
writeFileSync3(
|
|
9742
|
-
writeFileSync3(
|
|
9743
|
-
writeFileSync3(
|
|
10380
|
+
writeFileSync3(join8(p.hooksDir, "guard.mjs"), readGuard());
|
|
10381
|
+
writeFileSync3(join8(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
|
|
10382
|
+
writeFileSync3(join8(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
|
|
10383
|
+
writeFileSync3(join8(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
|
|
9744
10384
|
console.log(` Installed hooks \u2192 ${p.hooksDir}`);
|
|
9745
10385
|
removeClaudeShim();
|
|
9746
10386
|
writeFileSync3(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
|
|
9747
10387
|
console.log(` Wrote ${p.configPath}`);
|
|
9748
10388
|
let existing = {};
|
|
9749
|
-
if (
|
|
10389
|
+
if (existsSync6(p.settingsPath)) {
|
|
9750
10390
|
const raw = readFileSync6(p.settingsPath, "utf-8");
|
|
9751
|
-
if (!
|
|
10391
|
+
if (!existsSync6(p.backupPath)) {
|
|
9752
10392
|
writeFileSync3(p.backupPath, raw);
|
|
9753
10393
|
console.log(` Backed up existing settings \u2192 ${p.backupPath}`);
|
|
9754
10394
|
}
|
|
@@ -9758,9 +10398,9 @@ async function runGlobalInstall(opts = {}) {
|
|
|
9758
10398
|
existing = {};
|
|
9759
10399
|
}
|
|
9760
10400
|
}
|
|
9761
|
-
const guardAbs =
|
|
9762
|
-
const auditAbs =
|
|
9763
|
-
const stopAbs =
|
|
10401
|
+
const guardAbs = join8(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
|
|
10402
|
+
const auditAbs = join8(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
|
|
10403
|
+
const stopAbs = join8(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
|
|
9764
10404
|
const nodeBin = process.execPath.replace(/\\/g, "/");
|
|
9765
10405
|
const merged = {
|
|
9766
10406
|
...existing,
|
|
@@ -9950,22 +10590,22 @@ import { createServer, request as httpRequest } from "http";
|
|
|
9950
10590
|
import { request as httpsRequest } from "https";
|
|
9951
10591
|
import { spawn as spawn3 } from "child_process";
|
|
9952
10592
|
import { URL as URL2 } from "url";
|
|
9953
|
-
import { readFileSync as readFileSync7, existsSync as
|
|
10593
|
+
import { readFileSync as readFileSync7, existsSync as existsSync7, readdirSync, statSync as statSync4 } from "fs";
|
|
9954
10594
|
import { resolve as resolve5 } from "path";
|
|
9955
|
-
import { homedir as
|
|
10595
|
+
import { homedir as homedir7 } from "os";
|
|
9956
10596
|
function findCacheFile() {
|
|
9957
|
-
const dir = resolve5(
|
|
10597
|
+
const dir = resolve5(homedir7(), ".solongate");
|
|
9958
10598
|
const envSel = process.env.SOLONGATE_AGENT_ID;
|
|
9959
10599
|
if (envSel) {
|
|
9960
10600
|
const f = resolve5(dir, ".policy-cache-" + envSel.replace(/[^a-zA-Z0-9_-]/g, "_") + ".json");
|
|
9961
|
-
if (
|
|
10601
|
+
if (existsSync7(f)) return f;
|
|
9962
10602
|
}
|
|
9963
10603
|
let best = null, bestTs = -1;
|
|
9964
10604
|
try {
|
|
9965
10605
|
for (const name of readdirSync(dir)) {
|
|
9966
10606
|
if (name.startsWith(".policy-cache-") && name.endsWith(".json")) {
|
|
9967
10607
|
const full = resolve5(dir, name);
|
|
9968
|
-
const ts =
|
|
10608
|
+
const ts = statSync4(full).mtimeMs;
|
|
9969
10609
|
if (ts > bestTs) {
|
|
9970
10610
|
bestTs = ts;
|
|
9971
10611
|
best = full;
|
|
@@ -9979,7 +10619,7 @@ function findCacheFile() {
|
|
|
9979
10619
|
function loadCfg() {
|
|
9980
10620
|
try {
|
|
9981
10621
|
const f = findCacheFile();
|
|
9982
|
-
if (f &&
|
|
10622
|
+
if (f && existsSync7(f)) {
|
|
9983
10623
|
const c2 = JSON.parse(readFileSync7(f, "utf-8"));
|
|
9984
10624
|
const d = c2?.security?.dlpRedact;
|
|
9985
10625
|
const g = c2?.security?.ghost;
|
|
@@ -10256,9 +10896,9 @@ __export(logs_server_exports, {
|
|
|
10256
10896
|
runLogsServer: () => runLogsServer
|
|
10257
10897
|
});
|
|
10258
10898
|
import { createServer as createServer2 } from "http";
|
|
10259
|
-
import { readFileSync as readFileSync8, statSync as
|
|
10260
|
-
import { resolve as resolve6, join as
|
|
10261
|
-
import { homedir as
|
|
10899
|
+
import { readFileSync as readFileSync8, statSync as statSync5 } from "fs";
|
|
10900
|
+
import { resolve as resolve6, join as join9, isAbsolute } from "path";
|
|
10901
|
+
import { homedir as homedir8 } from "os";
|
|
10262
10902
|
import { readdirSync as readdirSync2 } from "fs";
|
|
10263
10903
|
function allowedOrigins() {
|
|
10264
10904
|
const base = [
|
|
@@ -10275,15 +10915,15 @@ function resolveLocalLogDir(rawPath) {
|
|
|
10275
10915
|
const dir = String(rawPath || "").trim().replace(/[\\/]+$/, "");
|
|
10276
10916
|
if (!dir) return null;
|
|
10277
10917
|
if (isAbsolute(dir)) return dir;
|
|
10278
|
-
return resolve6(
|
|
10918
|
+
return resolve6(homedir8(), ".solongate", "local-logs");
|
|
10279
10919
|
}
|
|
10280
10920
|
async function findLogDir() {
|
|
10281
|
-
const base = resolve6(
|
|
10921
|
+
const base = resolve6(homedir8(), ".solongate");
|
|
10282
10922
|
try {
|
|
10283
10923
|
const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
|
|
10284
10924
|
for (const f of files) {
|
|
10285
10925
|
try {
|
|
10286
|
-
const c2 = JSON.parse(readFileSync8(
|
|
10926
|
+
const c2 = JSON.parse(readFileSync8(join9(base, f), "utf-8"));
|
|
10287
10927
|
const p = c2?.security?.localLogs?.path;
|
|
10288
10928
|
if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
|
|
10289
10929
|
} catch {
|
|
@@ -10292,7 +10932,7 @@ async function findLogDir() {
|
|
|
10292
10932
|
} catch {
|
|
10293
10933
|
}
|
|
10294
10934
|
try {
|
|
10295
|
-
const cfgRaw = readFileSync8(
|
|
10935
|
+
const cfgRaw = readFileSync8(join9(base, "cloud-guard.json"), "utf-8");
|
|
10296
10936
|
const { apiKey, apiUrl } = JSON.parse(cfgRaw);
|
|
10297
10937
|
if (apiKey) {
|
|
10298
10938
|
const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
|
|
@@ -10321,9 +10961,9 @@ function setCors(req, res) {
|
|
|
10321
10961
|
}
|
|
10322
10962
|
function fileInfo(dir) {
|
|
10323
10963
|
if (!dir) return { file: null, exists: false, size: 0, mtimeMs: 0 };
|
|
10324
|
-
const file =
|
|
10964
|
+
const file = join9(dir, LOG_FILENAME);
|
|
10325
10965
|
try {
|
|
10326
|
-
const st =
|
|
10966
|
+
const st = statSync5(file);
|
|
10327
10967
|
return { file, exists: true, size: st.size, mtimeMs: st.mtimeMs };
|
|
10328
10968
|
} catch {
|
|
10329
10969
|
return { file, exists: false, size: 0, mtimeMs: 0 };
|
|
@@ -10434,7 +11074,7 @@ var init_logs_server = __esm({
|
|
|
10434
11074
|
|
|
10435
11075
|
// src/inject.ts
|
|
10436
11076
|
var inject_exports = {};
|
|
10437
|
-
import { readFileSync as readFileSync9, writeFileSync as writeFileSync4, existsSync as
|
|
11077
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync4, existsSync as existsSync8, copyFileSync } from "fs";
|
|
10438
11078
|
import { resolve as resolve7 } from "path";
|
|
10439
11079
|
import { execSync } from "child_process";
|
|
10440
11080
|
function parseInjectArgs(argv) {
|
|
@@ -10492,7 +11132,7 @@ WHAT IT DOES
|
|
|
10492
11132
|
`);
|
|
10493
11133
|
}
|
|
10494
11134
|
function detectProject() {
|
|
10495
|
-
if (!
|
|
11135
|
+
if (!existsSync8(resolve7("package.json"))) return false;
|
|
10496
11136
|
try {
|
|
10497
11137
|
const pkg = JSON.parse(readFileSync9(resolve7("package.json"), "utf-8"));
|
|
10498
11138
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
@@ -10508,13 +11148,13 @@ function findTsEntryFile() {
|
|
|
10508
11148
|
const binPath = typeof pkg.bin === "string" ? pkg.bin : Object.values(pkg.bin)[0];
|
|
10509
11149
|
if (typeof binPath === "string") {
|
|
10510
11150
|
const srcPath = binPath.replace(/^\.\/dist\//, "./src/").replace(/\.js$/, ".ts");
|
|
10511
|
-
if (
|
|
10512
|
-
if (
|
|
11151
|
+
if (existsSync8(resolve7(srcPath))) return resolve7(srcPath);
|
|
11152
|
+
if (existsSync8(resolve7(binPath))) return resolve7(binPath);
|
|
10513
11153
|
}
|
|
10514
11154
|
}
|
|
10515
11155
|
if (pkg.main) {
|
|
10516
11156
|
const srcPath = pkg.main.replace(/^\.\/dist\//, "./src/").replace(/\.js$/, ".ts");
|
|
10517
|
-
if (
|
|
11157
|
+
if (existsSync8(resolve7(srcPath))) return resolve7(srcPath);
|
|
10518
11158
|
}
|
|
10519
11159
|
} catch {
|
|
10520
11160
|
}
|
|
@@ -10528,7 +11168,7 @@ function findTsEntryFile() {
|
|
|
10528
11168
|
];
|
|
10529
11169
|
for (const c2 of candidates) {
|
|
10530
11170
|
const full = resolve7(c2);
|
|
10531
|
-
if (
|
|
11171
|
+
if (existsSync8(full)) {
|
|
10532
11172
|
try {
|
|
10533
11173
|
const content = readFileSync9(full, "utf-8");
|
|
10534
11174
|
if (content.includes("McpServer") || content.includes("McpServer")) {
|
|
@@ -10539,13 +11179,13 @@ function findTsEntryFile() {
|
|
|
10539
11179
|
}
|
|
10540
11180
|
}
|
|
10541
11181
|
for (const c2 of candidates) {
|
|
10542
|
-
if (
|
|
11182
|
+
if (existsSync8(resolve7(c2))) return resolve7(c2);
|
|
10543
11183
|
}
|
|
10544
11184
|
return null;
|
|
10545
11185
|
}
|
|
10546
11186
|
function detectPackageManager() {
|
|
10547
|
-
if (
|
|
10548
|
-
if (
|
|
11187
|
+
if (existsSync8(resolve7("pnpm-lock.yaml"))) return "pnpm";
|
|
11188
|
+
if (existsSync8(resolve7("yarn.lock"))) return "yarn";
|
|
10549
11189
|
return "npm";
|
|
10550
11190
|
}
|
|
10551
11191
|
function installSdk() {
|
|
@@ -10688,7 +11328,7 @@ async function main2() {
|
|
|
10688
11328
|
}
|
|
10689
11329
|
log3(" Language: TypeScript");
|
|
10690
11330
|
const entryFile = opts.file ? resolve7(opts.file) : findTsEntryFile();
|
|
10691
|
-
if (!entryFile || !
|
|
11331
|
+
if (!entryFile || !existsSync8(entryFile)) {
|
|
10692
11332
|
log3(` Could not find entry file.${opts.file ? ` File not found: ${opts.file}` : ""}`);
|
|
10693
11333
|
log3("");
|
|
10694
11334
|
log3(" Specify it manually: --file <path>");
|
|
@@ -10701,7 +11341,7 @@ async function main2() {
|
|
|
10701
11341
|
log3("");
|
|
10702
11342
|
const backupPath = entryFile + ".solongate-backup";
|
|
10703
11343
|
if (opts.restore) {
|
|
10704
|
-
if (!
|
|
11344
|
+
if (!existsSync8(backupPath)) {
|
|
10705
11345
|
log3(" No backup found. Nothing to restore.");
|
|
10706
11346
|
process.exit(1);
|
|
10707
11347
|
}
|
|
@@ -10737,7 +11377,7 @@ async function main2() {
|
|
|
10737
11377
|
log3(" To apply: npx @solongate/proxy inject");
|
|
10738
11378
|
process.exit(0);
|
|
10739
11379
|
}
|
|
10740
|
-
if (!
|
|
11380
|
+
if (!existsSync8(backupPath)) {
|
|
10741
11381
|
copyFileSync(entryFile, backupPath);
|
|
10742
11382
|
log3("");
|
|
10743
11383
|
log3(` Backup: ${backupPath}`);
|
|
@@ -10771,8 +11411,8 @@ var init_inject = __esm({
|
|
|
10771
11411
|
|
|
10772
11412
|
// src/create.ts
|
|
10773
11413
|
var create_exports = {};
|
|
10774
|
-
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5, existsSync as
|
|
10775
|
-
import { resolve as resolve8, join as
|
|
11414
|
+
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5, existsSync as existsSync9 } from "fs";
|
|
11415
|
+
import { resolve as resolve8, join as join10 } from "path";
|
|
10776
11416
|
import { execSync as execSync2 } from "child_process";
|
|
10777
11417
|
function withSpinner(message, fn) {
|
|
10778
11418
|
const frames = ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"];
|
|
@@ -10852,7 +11492,7 @@ EXAMPLES
|
|
|
10852
11492
|
}
|
|
10853
11493
|
function createProject(dir, name, _policy) {
|
|
10854
11494
|
writeFileSync5(
|
|
10855
|
-
|
|
11495
|
+
join10(dir, "package.json"),
|
|
10856
11496
|
JSON.stringify(
|
|
10857
11497
|
{
|
|
10858
11498
|
name,
|
|
@@ -10882,7 +11522,7 @@ function createProject(dir, name, _policy) {
|
|
|
10882
11522
|
) + "\n"
|
|
10883
11523
|
);
|
|
10884
11524
|
writeFileSync5(
|
|
10885
|
-
|
|
11525
|
+
join10(dir, "tsconfig.json"),
|
|
10886
11526
|
JSON.stringify(
|
|
10887
11527
|
{
|
|
10888
11528
|
compilerOptions: {
|
|
@@ -10902,9 +11542,9 @@ function createProject(dir, name, _policy) {
|
|
|
10902
11542
|
2
|
|
10903
11543
|
) + "\n"
|
|
10904
11544
|
);
|
|
10905
|
-
mkdirSync4(
|
|
11545
|
+
mkdirSync4(join10(dir, "src"), { recursive: true });
|
|
10906
11546
|
writeFileSync5(
|
|
10907
|
-
|
|
11547
|
+
join10(dir, "src", "index.ts"),
|
|
10908
11548
|
`#!/usr/bin/env node
|
|
10909
11549
|
|
|
10910
11550
|
console.log = (...args: unknown[]) => {
|
|
@@ -10946,7 +11586,7 @@ console.log('Press Ctrl+C to stop.');
|
|
|
10946
11586
|
`
|
|
10947
11587
|
);
|
|
10948
11588
|
writeFileSync5(
|
|
10949
|
-
|
|
11589
|
+
join10(dir, ".mcp.json"),
|
|
10950
11590
|
JSON.stringify(
|
|
10951
11591
|
{
|
|
10952
11592
|
mcpServers: {
|
|
@@ -10964,12 +11604,12 @@ console.log('Press Ctrl+C to stop.');
|
|
|
10964
11604
|
) + "\n"
|
|
10965
11605
|
);
|
|
10966
11606
|
writeFileSync5(
|
|
10967
|
-
|
|
11607
|
+
join10(dir, ".env"),
|
|
10968
11608
|
`SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
|
|
10969
11609
|
`
|
|
10970
11610
|
);
|
|
10971
11611
|
writeFileSync5(
|
|
10972
|
-
|
|
11612
|
+
join10(dir, ".gitignore"),
|
|
10973
11613
|
`node_modules/
|
|
10974
11614
|
dist/
|
|
10975
11615
|
*.solongate-backup
|
|
@@ -10983,7 +11623,7 @@ async function main3() {
|
|
|
10983
11623
|
const opts = parseCreateArgs(process.argv);
|
|
10984
11624
|
const dir = resolve8(opts.name);
|
|
10985
11625
|
printBanner("Create MCP Server");
|
|
10986
|
-
if (
|
|
11626
|
+
if (existsSync9(dir)) {
|
|
10987
11627
|
log3(` ${c.red}Error:${c.reset} Directory "${opts.name}" already exists.`);
|
|
10988
11628
|
process.exit(1);
|
|
10989
11629
|
}
|
|
@@ -11062,12 +11702,12 @@ var init_create = __esm({
|
|
|
11062
11702
|
|
|
11063
11703
|
// src/pull-push.ts
|
|
11064
11704
|
var pull_push_exports = {};
|
|
11065
|
-
import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as
|
|
11705
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as existsSync10 } from "fs";
|
|
11066
11706
|
import { resolve as resolve9 } from "path";
|
|
11067
11707
|
function loadEnv() {
|
|
11068
11708
|
if (process.env.SOLONGATE_API_KEY) return;
|
|
11069
11709
|
const envPath = resolve9(".env");
|
|
11070
|
-
if (!
|
|
11710
|
+
if (!existsSync10(envPath)) return;
|
|
11071
11711
|
try {
|
|
11072
11712
|
const content = readFileSync10(envPath, "utf-8");
|
|
11073
11713
|
for (const line of content.split("\n")) {
|
|
@@ -11134,7 +11774,7 @@ async function listPolicies(apiKey) {
|
|
|
11134
11774
|
const data = await res.json();
|
|
11135
11775
|
return data.policies ?? [];
|
|
11136
11776
|
}
|
|
11137
|
-
async function
|
|
11777
|
+
async function list5(apiKey, policyId) {
|
|
11138
11778
|
const policies = await listPolicies(apiKey);
|
|
11139
11779
|
if (policies.length === 0) {
|
|
11140
11780
|
log5(yellow2("No policies found. Create one in the dashboard first."));
|
|
@@ -11269,7 +11909,7 @@ async function pull(apiKey, file, policyId) {
|
|
|
11269
11909
|
log5("");
|
|
11270
11910
|
}
|
|
11271
11911
|
async function push(apiKey, file, policyId) {
|
|
11272
|
-
if (!
|
|
11912
|
+
if (!existsSync10(file)) {
|
|
11273
11913
|
log5(red2(`ERROR: File not found: ${file}`));
|
|
11274
11914
|
process.exit(1);
|
|
11275
11915
|
}
|
|
@@ -11337,7 +11977,7 @@ async function main4() {
|
|
|
11337
11977
|
} else if (command === "push") {
|
|
11338
11978
|
await push(apiKey, file, policyId);
|
|
11339
11979
|
} else if (command === "list" || command === "ls") {
|
|
11340
|
-
await
|
|
11980
|
+
await list5(apiKey, policyId);
|
|
11341
11981
|
} else {
|
|
11342
11982
|
log5(red2(`Unknown command: ${command}`));
|
|
11343
11983
|
log5("");
|
|
@@ -14402,7 +15042,7 @@ ${msg.content.text}`;
|
|
|
14402
15042
|
|
|
14403
15043
|
// src/index.ts
|
|
14404
15044
|
init_cli_utils();
|
|
14405
|
-
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"]);
|
|
15045
|
+
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"]);
|
|
14406
15046
|
var IS_HUMAN_CLI = process.argv.length <= 2 || CLI_SUBCOMMANDS.has(process.argv[2] ?? "");
|
|
14407
15047
|
if (!IS_HUMAN_CLI) {
|
|
14408
15048
|
console.log = (...args) => {
|
|
@@ -14451,7 +15091,7 @@ async function main5() {
|
|
|
14451
15091
|
await launchTui2();
|
|
14452
15092
|
return;
|
|
14453
15093
|
}
|
|
14454
|
-
const MGMT_COMMANDS = /* @__PURE__ */ new Set(["policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent"]);
|
|
15094
|
+
const MGMT_COMMANDS = /* @__PURE__ */ new Set(["policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent", "doctor", "watch", "keys", "mcp", "alerts", "webhooks"]);
|
|
14455
15095
|
if (MGMT_COMMANDS.has(subcommand ?? "")) {
|
|
14456
15096
|
const { runCommand: runCommand2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
|
|
14457
15097
|
const code = await runCommand2(subcommand, process.argv.slice(3));
|