@suveren/gateway 0.8.4 → 0.8.6
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/control-plane/index.mjs +170 -83
- package/dist/mcp-server/http.mjs +75 -0
- package/dist/ui/assets/index-BJqF5rmx.css +1 -0
- package/dist/ui/assets/index-DWpjjPIf.js +110 -0
- package/dist/ui/index.html +2 -2
- package/node_modules/@types/node/README.md +2 -2
- package/node_modules/@types/node/buffer.d.ts +1 -0
- package/node_modules/@types/node/child_process.d.ts +1 -1
- package/node_modules/@types/node/crypto.d.ts +20 -8
- package/node_modules/@types/node/diagnostics_channel.d.ts +4 -4
- package/node_modules/@types/node/ffi.d.ts +14 -0
- package/node_modules/@types/node/http.d.ts +1 -1
- package/node_modules/@types/node/http2.d.ts +3 -2
- package/node_modules/@types/node/net.d.ts +16 -4
- package/node_modules/@types/node/package.json +3 -18
- package/node_modules/@types/node/perf_hooks.d.ts +31 -19
- package/node_modules/@types/node/process.d.ts +1 -0
- package/node_modules/@types/node/quic.d.ts +45 -5
- package/node_modules/@types/node/stream/iter.d.ts +9 -12
- package/node_modules/@types/node/stream/web.d.ts +20 -0
- package/node_modules/@types/node/test/reporters.d.ts +1 -0
- package/node_modules/@types/node/test.d.ts +72 -0
- package/node_modules/@types/node/tls.d.ts +40 -25
- package/node_modules/@types/node/vfs.d.ts +7 -6
- package/node_modules/@types/node/vm.d.ts +2 -1
- package/node_modules/@types/node/web-globals/streams.d.ts +4 -0
- package/node_modules/@types/node/worker_threads.d.ts +4 -1
- package/node_modules/@types/node/zlib.d.ts +17 -0
- package/node_modules/body-parser/HISTORY.md +156 -145
- package/node_modules/body-parser/package.json +2 -2
- package/node_modules/express/History.md +583 -576
- package/node_modules/express/lib/request.js +2 -2
- package/node_modules/express/package.json +3 -3
- package/node_modules/fast-uri/index.js +10 -6
- package/node_modules/fast-uri/package.json +1 -1
- package/node_modules/fast-uri/test/security-normalization.test.js +38 -0
- package/node_modules/hono/README.md +1 -1
- package/node_modules/hono/dist/adapter/aws-lambda/handler.js +5 -4
- package/node_modules/hono/dist/cjs/adapter/aws-lambda/handler.js +5 -4
- package/node_modules/hono/dist/cjs/context.js +1 -1
- package/node_modules/hono/dist/cjs/helper/accepts/accepts.js +1 -1
- package/node_modules/hono/dist/cjs/jsx/dom/render.js +44 -8
- package/node_modules/hono/dist/cjs/middleware/language/language.js +4 -1
- package/node_modules/hono/dist/cjs/request.js +4 -1
- package/node_modules/hono/dist/cjs/utils/accept.js +4 -7
- package/node_modules/hono/dist/context.js +1 -1
- package/node_modules/hono/dist/helper/accepts/accepts.js +1 -1
- package/node_modules/hono/dist/jsx/dom/render.js +44 -8
- package/node_modules/hono/dist/middleware/language/language.js +4 -1
- package/node_modules/hono/dist/request.js +4 -1
- package/node_modules/hono/dist/types/helper/proxy/index.d.ts +1 -1
- package/node_modules/hono/dist/types/helper/route/index.d.ts +2 -2
- package/node_modules/hono/dist/types/middleware/combine/index.d.ts +3 -1
- package/node_modules/hono/dist/types/utils/types.d.ts +1 -1
- package/node_modules/hono/dist/utils/accept.js +4 -7
- package/node_modules/hono/package.json +1 -1
- package/node_modules/ip-address/README.md +114 -114
- package/node_modules/ip-address/dist/common.d.ts +5 -0
- package/node_modules/ip-address/dist/common.js +12 -0
- package/node_modules/ip-address/dist/common.js.map +1 -1
- package/node_modules/ip-address/dist/ipv4.d.ts +10 -3
- package/node_modules/ip-address/dist/ipv4.js +20 -5
- package/node_modules/ip-address/dist/ipv4.js.map +1 -1
- package/node_modules/ip-address/dist/ipv6.d.ts +5 -1
- package/node_modules/ip-address/dist/ipv6.js +18 -3
- package/node_modules/ip-address/dist/ipv6.js.map +1 -1
- package/node_modules/ip-address/package.json +1 -1
- package/node_modules/proxy-addr/HISTORY.md +60 -54
- package/node_modules/proxy-addr/README.md +1 -1
- package/node_modules/proxy-addr/index.js +32 -0
- package/node_modules/proxy-addr/package.json +8 -4
- package/node_modules/qs/.github/THREAT_MODEL.md +3 -3
- package/node_modules/qs/CHANGELOG.md +15 -0
- package/node_modules/qs/README.md +19 -1
- package/node_modules/qs/dist/qs.js +21 -21
- package/node_modules/qs/eslint.config.mjs +1 -1
- package/node_modules/qs/lib/parse.js +3 -4
- package/node_modules/qs/lib/stringify.js +23 -8
- package/node_modules/qs/lib/utils.js +10 -3
- package/node_modules/qs/package.json +4 -4
- package/node_modules/qs/test/parse.js +192 -3
- package/node_modules/qs/test/stringify.js +317 -0
- package/node_modules/qs/test/utils.js +12 -0
- package/node_modules/undici-types/client.d.ts +8 -3
- package/node_modules/undici-types/connector.d.ts +1 -0
- package/node_modules/undici-types/cookies.d.ts +1 -1
- package/node_modules/undici-types/dispatcher.d.ts +3 -3
- package/node_modules/undici-types/errors.d.ts +10 -0
- package/node_modules/undici-types/fetch.d.ts +5 -1
- package/node_modules/undici-types/handlers.d.ts +2 -0
- package/node_modules/undici-types/interceptors.d.ts +1 -1
- package/node_modules/undici-types/mock-interceptor.d.ts +5 -1
- package/node_modules/undici-types/package.json +1 -1
- package/node_modules/undici-types/proxy-agent.d.ts +7 -0
- package/package.json +1 -1
- package/dist/ui/assets/index-BYVb_0oG.css +0 -1
- package/dist/ui/assets/index-Z0OQ-dd1.js +0 -110
|
@@ -5,9 +5,9 @@ import {
|
|
|
5
5
|
|
|
6
6
|
// src/index.ts
|
|
7
7
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
8
|
-
import { existsSync as
|
|
9
|
-
import { dirname as
|
|
10
|
-
import { homedir as
|
|
8
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7, renameSync as renameSync2 } from "fs";
|
|
9
|
+
import { dirname as dirname6, join as join8 } from "path";
|
|
10
|
+
import { homedir as homedir7 } from "os";
|
|
11
11
|
import express from "express";
|
|
12
12
|
import { createProxyMiddleware } from "http-proxy-middleware";
|
|
13
13
|
|
|
@@ -1042,6 +1042,8 @@ A good Intent can describe:
|
|
|
1042
1042
|
- What the agent should try to achieve (the goal).
|
|
1043
1043
|
- Soft rules or watch-outs the agent must honor even inside bounds (e.g. "never publish on weekends", "only reply in German").
|
|
1044
1044
|
|
|
1045
|
+
If an Agent Brief is provided, it is the user's standing orders that every agent already receives before any Intent. Use it to answer the user's questions and to keep the Intent consistent with it; do not repeat rules the brief already states \u2014 the Intent should add what is specific to this authorization.
|
|
1046
|
+
|
|
1045
1047
|
Your role is to coach: ask clarifying questions, reflect the user's words back, surface edge cases they haven't thought about. You may propose concrete phrasings on request.
|
|
1046
1048
|
|
|
1047
1049
|
CRITICAL \u2014 ask before you draft.
|
|
@@ -1062,23 +1064,34 @@ Once they have answered, produce the draft inside a single fenced block:
|
|
|
1062
1064
|
|
|
1063
1065
|
Only use the fence for full-document drafts the user can click "Apply". For partial suggestions, questions, comments, or reflections, write prose without the fence.`
|
|
1064
1066
|
};
|
|
1067
|
+
function buildChatGrounding(target, currentText, agentBrief) {
|
|
1068
|
+
const parts = [];
|
|
1069
|
+
if (target.kind === "intent") {
|
|
1070
|
+
if (target.profileId) parts.push(`Profile: ${target.profileId}`);
|
|
1071
|
+
if (target.path) parts.push(`Path: ${target.path}`);
|
|
1072
|
+
if (target.bounds) parts.push(`Bounds: ${target.bounds}`);
|
|
1073
|
+
if (agentBrief?.trim()) {
|
|
1074
|
+
parts.push(
|
|
1075
|
+
`Agent Brief (standing orders every agent already receives; the Intent builds on this, it does not repeat it):
|
|
1076
|
+
"""
|
|
1077
|
+
${agentBrief.trim()}
|
|
1078
|
+
"""`
|
|
1079
|
+
);
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
parts.push(
|
|
1083
|
+
currentText.trim() ? `Current draft:
|
|
1084
|
+
"""
|
|
1085
|
+
${currentText}
|
|
1086
|
+
"""` : "Current draft is empty."
|
|
1087
|
+
);
|
|
1088
|
+
return parts.join("\n");
|
|
1089
|
+
}
|
|
1065
1090
|
async function getAIChatResponse(config, request) {
|
|
1066
1091
|
const { getPromptOverride } = await import("./ai-prompts-store-5JISFV4Z.mjs");
|
|
1067
1092
|
const override = await getPromptOverride(request.target.kind);
|
|
1068
1093
|
const systemPrompt = override ?? CHAT_SYSTEM_PROMPTS[request.target.kind];
|
|
1069
|
-
const
|
|
1070
|
-
if (request.target.kind === "intent") {
|
|
1071
|
-
if (request.target.profileId) groundingParts.push(`Profile: ${request.target.profileId}`);
|
|
1072
|
-
if (request.target.path) groundingParts.push(`Path: ${request.target.path}`);
|
|
1073
|
-
if (request.target.bounds) groundingParts.push(`Bounds: ${request.target.bounds}`);
|
|
1074
|
-
}
|
|
1075
|
-
groundingParts.push(
|
|
1076
|
-
request.currentText.trim() ? `Current draft:
|
|
1077
|
-
"""
|
|
1078
|
-
${request.currentText}
|
|
1079
|
-
"""` : "Current draft is empty."
|
|
1080
|
-
);
|
|
1081
|
-
const grounding = groundingParts.join("\n");
|
|
1094
|
+
const grounding = buildChatGrounding(request.target, request.currentText, request.agentBrief);
|
|
1082
1095
|
const messages = [
|
|
1083
1096
|
{ role: "system", content: systemPrompt },
|
|
1084
1097
|
{ role: "user", content: grounding },
|
|
@@ -1280,6 +1293,21 @@ async function testAIConnectivity(config) {
|
|
|
1280
1293
|
}
|
|
1281
1294
|
}
|
|
1282
1295
|
|
|
1296
|
+
// src/lib/agent-brief-store.ts
|
|
1297
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
1298
|
+
import { homedir as homedir2 } from "os";
|
|
1299
|
+
import { join as join2 } from "path";
|
|
1300
|
+
var AGENT_CONTEXT_MAX_BYTES = 16 * 1024;
|
|
1301
|
+
function agentBriefPath() {
|
|
1302
|
+
const dataDir = process.env.SUVEREN_DATA_DIR ?? join2(homedir2(), ".suveren");
|
|
1303
|
+
return join2(dataDir, "context.md");
|
|
1304
|
+
}
|
|
1305
|
+
function readAgentBrief() {
|
|
1306
|
+
const filePath = agentBriefPath();
|
|
1307
|
+
if (!existsSync2(filePath)) return "";
|
|
1308
|
+
return readFileSync2(filePath, "utf-8");
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1283
1311
|
// src/routes/ai.ts
|
|
1284
1312
|
var describeScope = (c = {}) => {
|
|
1285
1313
|
const vals = Object.values(c).map((v) => String(v).trim()).filter(Boolean);
|
|
@@ -1326,7 +1354,7 @@ function createAIRouter(vault2) {
|
|
|
1326
1354
|
res.status(400).json({ error: "Missing messages array" });
|
|
1327
1355
|
return;
|
|
1328
1356
|
}
|
|
1329
|
-
const result = await getAIChatResponse(config, request);
|
|
1357
|
+
const result = await getAIChatResponse(config, { ...request, agentBrief: readAgentBrief() });
|
|
1330
1358
|
res.json(result);
|
|
1331
1359
|
});
|
|
1332
1360
|
router.post("/intent-review", async (req, res) => {
|
|
@@ -1641,28 +1669,28 @@ function createMCPRouter() {
|
|
|
1641
1669
|
// src/routes/autostart.ts
|
|
1642
1670
|
import { Router as Router6 } from "express";
|
|
1643
1671
|
import { execFile } from "child_process";
|
|
1644
|
-
import { existsSync as
|
|
1672
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1645
1673
|
import { dirname as pathDirname, resolve } from "path";
|
|
1646
1674
|
import { fileURLToPath } from "url";
|
|
1647
1675
|
import { promisify } from "util";
|
|
1648
1676
|
var run = promisify(execFile);
|
|
1649
1677
|
var SUPPORTED = /* @__PURE__ */ new Set(["darwin", "win32", "linux"]);
|
|
1650
1678
|
var MODULE_DIR = pathDirname(fileURLToPath(import.meta.url));
|
|
1651
|
-
function findCli(
|
|
1679
|
+
function findCli(dirname7 = MODULE_DIR) {
|
|
1652
1680
|
const candidates = [
|
|
1653
1681
|
// Bundled (the shipped layout): dist/control-plane → dist → root → bin
|
|
1654
|
-
resolve(
|
|
1682
|
+
resolve(dirname7, "..", "..", "bin", "suveren-gateway.js"),
|
|
1655
1683
|
// Bundled, were the routes/ directory ever preserved.
|
|
1656
|
-
resolve(
|
|
1684
|
+
resolve(dirname7, "..", "..", "..", "bin", "suveren-gateway.js"),
|
|
1657
1685
|
// Dev, running from source: the repo's own bundle directory.
|
|
1658
|
-
resolve(
|
|
1659
|
-
resolve(
|
|
1686
|
+
resolve(dirname7, "..", "..", "..", "..", "bundle", "bin", "suveren-gateway.js"),
|
|
1687
|
+
resolve(dirname7, "..", "..", "..", "..", "..", "bundle", "bin", "suveren-gateway.js")
|
|
1660
1688
|
// Deliberately NO process.cwd() fallback: it makes the result depend on
|
|
1661
1689
|
// where the process happened to be launched from, so the same install
|
|
1662
1690
|
// resolves differently between a shell and a login service — and it would
|
|
1663
1691
|
// mask a genuinely missing CLI whenever the cwd happened to be the repo.
|
|
1664
1692
|
];
|
|
1665
|
-
return candidates.find(
|
|
1693
|
+
return candidates.find(existsSync3) ?? null;
|
|
1666
1694
|
}
|
|
1667
1695
|
async function cli(args) {
|
|
1668
1696
|
const bin = findCli();
|
|
@@ -1743,15 +1771,15 @@ function createAutostartRouter() {
|
|
|
1743
1771
|
import { spawn, spawnSync } from "child_process";
|
|
1744
1772
|
|
|
1745
1773
|
// src/lib/notifier-icon.ts
|
|
1746
|
-
import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as
|
|
1747
|
-
import { homedir as
|
|
1748
|
-
import { join as
|
|
1774
|
+
import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
|
|
1775
|
+
import { homedir as homedir3 } from "os";
|
|
1776
|
+
import { join as join3, dirname as dirname2 } from "path";
|
|
1749
1777
|
var ICON_BASE64 = "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";
|
|
1750
1778
|
function ensureNotifierIcon(dataDir) {
|
|
1751
1779
|
try {
|
|
1752
|
-
const dir = dataDir ?? process.env.SUVEREN_DATA_DIR ??
|
|
1753
|
-
const path =
|
|
1754
|
-
if (!
|
|
1780
|
+
const dir = dataDir ?? process.env.SUVEREN_DATA_DIR ?? join3(homedir3(), ".suveren");
|
|
1781
|
+
const path = join3(dir, "notifier-icon.png");
|
|
1782
|
+
if (!existsSync4(path)) {
|
|
1755
1783
|
mkdirSync2(dirname2(path), { recursive: true });
|
|
1756
1784
|
writeFileSync2(path, Buffer.from(ICON_BASE64, "base64"));
|
|
1757
1785
|
}
|
|
@@ -1947,17 +1975,17 @@ import { Router as Router9 } from "express";
|
|
|
1947
1975
|
|
|
1948
1976
|
// src/routes/approved-intents.ts
|
|
1949
1977
|
import { Router as Router8 } from "express";
|
|
1950
|
-
import { readFileSync as
|
|
1951
|
-
import { homedir as
|
|
1952
|
-
import { join as
|
|
1953
|
-
var SUVEREN_DATA_DIR = process.env.SUVEREN_DATA_DIR ??
|
|
1954
|
-
var FILE_PATH =
|
|
1978
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
1979
|
+
import { homedir as homedir4 } from "os";
|
|
1980
|
+
import { join as join4 } from "path";
|
|
1981
|
+
var SUVEREN_DATA_DIR = process.env.SUVEREN_DATA_DIR ?? join4(homedir4(), ".suveren");
|
|
1982
|
+
var FILE_PATH = join4(SUVEREN_DATA_DIR, "approved-intents.enc.json");
|
|
1955
1983
|
function readFile() {
|
|
1956
|
-
if (!
|
|
1984
|
+
if (!existsSync5(FILE_PATH)) {
|
|
1957
1985
|
return { version: 1, entries: {} };
|
|
1958
1986
|
}
|
|
1959
1987
|
try {
|
|
1960
|
-
return JSON.parse(
|
|
1988
|
+
return JSON.parse(readFileSync3(FILE_PATH, "utf-8"));
|
|
1961
1989
|
} catch {
|
|
1962
1990
|
return { version: 1, entries: {} };
|
|
1963
1991
|
}
|
|
@@ -2366,19 +2394,19 @@ data: null
|
|
|
2366
2394
|
import { Router as Router12 } from "express";
|
|
2367
2395
|
|
|
2368
2396
|
// src/lib/gateway-settings.ts
|
|
2369
|
-
import { readFileSync as
|
|
2370
|
-
import { homedir as
|
|
2371
|
-
import { join as
|
|
2397
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "fs";
|
|
2398
|
+
import { homedir as homedir5 } from "os";
|
|
2399
|
+
import { join as join5, dirname as dirname4 } from "path";
|
|
2372
2400
|
var DEFAULT_SETTINGS = {
|
|
2373
2401
|
desktopNotifications: true
|
|
2374
2402
|
};
|
|
2375
2403
|
function settingsPath(dataDir) {
|
|
2376
|
-
const dir = dataDir ?? process.env.SUVEREN_DATA_DIR ??
|
|
2377
|
-
return
|
|
2404
|
+
const dir = dataDir ?? process.env.SUVEREN_DATA_DIR ?? join5(homedir5(), ".suveren");
|
|
2405
|
+
return join5(dir, "settings.json");
|
|
2378
2406
|
}
|
|
2379
2407
|
function readSettings(dataDir) {
|
|
2380
2408
|
try {
|
|
2381
|
-
const raw = JSON.parse(
|
|
2409
|
+
const raw = JSON.parse(readFileSync4(settingsPath(dataDir), "utf8"));
|
|
2382
2410
|
return {
|
|
2383
2411
|
desktopNotifications: typeof raw.desktopNotifications === "boolean" ? raw.desktopNotifications : DEFAULT_SETTINGS.desktopNotifications
|
|
2384
2412
|
};
|
|
@@ -2421,8 +2449,73 @@ function createGatewaySettingsRouter() {
|
|
|
2421
2449
|
return router;
|
|
2422
2450
|
}
|
|
2423
2451
|
|
|
2424
|
-
// src/routes/
|
|
2452
|
+
// src/routes/archived-mandates.ts
|
|
2425
2453
|
import { Router as Router13 } from "express";
|
|
2454
|
+
|
|
2455
|
+
// src/lib/archived-mandates-store.ts
|
|
2456
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, renameSync } from "fs";
|
|
2457
|
+
import { homedir as homedir6 } from "os";
|
|
2458
|
+
import { join as join6, dirname as dirname5 } from "path";
|
|
2459
|
+
var ID_PATTERN = /^[A-Za-z0-9_.:-]{1,200}$/;
|
|
2460
|
+
function isValidMandateId(id) {
|
|
2461
|
+
return typeof id === "string" && ID_PATTERN.test(id);
|
|
2462
|
+
}
|
|
2463
|
+
function storePath(dataDir) {
|
|
2464
|
+
const dir = dataDir ?? process.env.SUVEREN_DATA_DIR ?? join6(homedir6(), ".suveren");
|
|
2465
|
+
return join6(dir, "archived-mandates.json");
|
|
2466
|
+
}
|
|
2467
|
+
function readArchived(dataDir) {
|
|
2468
|
+
try {
|
|
2469
|
+
const raw = JSON.parse(readFileSync5(storePath(dataDir), "utf8"));
|
|
2470
|
+
if (!Array.isArray(raw.archived)) return [];
|
|
2471
|
+
return [...new Set(raw.archived.filter(isValidMandateId))];
|
|
2472
|
+
} catch {
|
|
2473
|
+
return [];
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
function writeArchived(ids, dataDir) {
|
|
2477
|
+
const next = [...new Set(ids.filter(isValidMandateId))].sort();
|
|
2478
|
+
const path = storePath(dataDir);
|
|
2479
|
+
mkdirSync5(dirname5(path), { recursive: true });
|
|
2480
|
+
const tmp = `${path}.tmp`;
|
|
2481
|
+
writeFileSync5(tmp, JSON.stringify({ archived: next }, null, 2) + "\n", "utf8");
|
|
2482
|
+
renameSync(tmp, path);
|
|
2483
|
+
return next;
|
|
2484
|
+
}
|
|
2485
|
+
function archiveMandate(id, dataDir) {
|
|
2486
|
+
if (!isValidMandateId(id)) throw new Error("invalid mandate id");
|
|
2487
|
+
return writeArchived([...readArchived(dataDir), id], dataDir);
|
|
2488
|
+
}
|
|
2489
|
+
function unarchiveMandate(id, dataDir) {
|
|
2490
|
+
if (!isValidMandateId(id)) throw new Error("invalid mandate id");
|
|
2491
|
+
return writeArchived(readArchived(dataDir).filter((x) => x !== id), dataDir);
|
|
2492
|
+
}
|
|
2493
|
+
|
|
2494
|
+
// src/routes/archived-mandates.ts
|
|
2495
|
+
function createArchivedMandatesRouter(dataDir) {
|
|
2496
|
+
const router = Router13();
|
|
2497
|
+
router.get("/", (_req, res) => {
|
|
2498
|
+
res.json({ archived: readArchived(dataDir) });
|
|
2499
|
+
});
|
|
2500
|
+
const change = (fn) => (req, res) => {
|
|
2501
|
+
const id = req.params.id;
|
|
2502
|
+
if (!isValidMandateId(id)) {
|
|
2503
|
+
res.status(400).json({ error: "invalid mandate id" });
|
|
2504
|
+
return;
|
|
2505
|
+
}
|
|
2506
|
+
try {
|
|
2507
|
+
res.json({ archived: fn(id, dataDir) });
|
|
2508
|
+
} catch (err) {
|
|
2509
|
+
res.status(500).json({ error: `Could not save: ${err.message}` });
|
|
2510
|
+
}
|
|
2511
|
+
};
|
|
2512
|
+
router.put("/:id", change(archiveMandate));
|
|
2513
|
+
router.delete("/:id", change(unarchiveMandate));
|
|
2514
|
+
return router;
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
// src/routes/internal-events.ts
|
|
2518
|
+
import { Router as Router14 } from "express";
|
|
2426
2519
|
import { timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
2427
2520
|
var ALLOWED = ["proposal-added", "action-approval-needed"];
|
|
2428
2521
|
function secretMatches(provided, expected) {
|
|
@@ -2434,7 +2527,7 @@ function secretMatches(provided, expected) {
|
|
|
2434
2527
|
return timingSafeEqual2(a, b);
|
|
2435
2528
|
}
|
|
2436
2529
|
function createInternalEventsRouter(getSecret) {
|
|
2437
|
-
const router =
|
|
2530
|
+
const router = Router14();
|
|
2438
2531
|
router.post("/event", (req, res) => {
|
|
2439
2532
|
if (!secretMatches(req.headers["x-internal-secret"], getSecret())) {
|
|
2440
2533
|
res.status(401).json({ error: "Unauthorized" });
|
|
@@ -2538,22 +2631,24 @@ function startNotificationDispatcher(opts = {}) {
|
|
|
2538
2631
|
}
|
|
2539
2632
|
|
|
2540
2633
|
// src/lib/denials-reader.ts
|
|
2541
|
-
import { existsSync as
|
|
2542
|
-
import { join as
|
|
2634
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
2635
|
+
import { join as join7 } from "path";
|
|
2543
2636
|
function loadDenials(dataDir, decrypt) {
|
|
2544
|
-
const encPath =
|
|
2545
|
-
const plainPath =
|
|
2546
|
-
if (
|
|
2547
|
-
const { blob } = JSON.parse(
|
|
2637
|
+
const encPath = join7(dataDir, "denials.enc.json");
|
|
2638
|
+
const plainPath = join7(dataDir, "denials.json");
|
|
2639
|
+
if (existsSync6(encPath)) {
|
|
2640
|
+
const { blob } = JSON.parse(readFileSync6(encPath, "utf-8"));
|
|
2548
2641
|
return JSON.parse(decrypt(blob)).records ?? [];
|
|
2549
2642
|
}
|
|
2550
|
-
if (
|
|
2551
|
-
return JSON.parse(
|
|
2643
|
+
if (existsSync6(plainPath)) {
|
|
2644
|
+
return JSON.parse(readFileSync6(plainPath, "utf-8")).records ?? [];
|
|
2552
2645
|
}
|
|
2553
2646
|
return [];
|
|
2554
2647
|
}
|
|
2648
|
+
var DENIAL_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
2555
2649
|
function selectDenials(records, opts = {}) {
|
|
2556
|
-
|
|
2650
|
+
const cutoff = (opts.now ?? Date.now()) - DENIAL_MAX_AGE_MS;
|
|
2651
|
+
let out = records.filter((r) => r.ts >= cutoff).sort((a, b) => b.ts - a.ts);
|
|
2557
2652
|
if (opts.since !== void 0 && Number.isFinite(opts.since)) out = out.filter((r) => r.ts >= opts.since);
|
|
2558
2653
|
const count = out.length;
|
|
2559
2654
|
if (opts.limit !== void 0 && Number.isFinite(opts.limit) && opts.limit > 0) out = out.slice(0, opts.limit);
|
|
@@ -2563,11 +2658,11 @@ function selectDenials(records, opts = {}) {
|
|
|
2563
2658
|
// src/index.ts
|
|
2564
2659
|
(function migrateDataDir() {
|
|
2565
2660
|
if (process.env.SUVEREN_DATA_DIR) return;
|
|
2566
|
-
const oldDir =
|
|
2567
|
-
const newDir =
|
|
2568
|
-
if (!
|
|
2661
|
+
const oldDir = join8(homedir7(), ".hap");
|
|
2662
|
+
const newDir = join8(homedir7(), ".suveren");
|
|
2663
|
+
if (!existsSync7(newDir) && existsSync7(oldDir)) {
|
|
2569
2664
|
try {
|
|
2570
|
-
|
|
2665
|
+
renameSync2(oldDir, newDir);
|
|
2571
2666
|
console.error(`[Control Plane] Migrated data directory: ${oldDir} \u2192 ${newDir}`);
|
|
2572
2667
|
} catch (err) {
|
|
2573
2668
|
console.error(`[Control Plane] FATAL: could not migrate data directory ${oldDir} \u2192 ${newDir}:`, err);
|
|
@@ -2578,7 +2673,7 @@ function selectDenials(records, opts = {}) {
|
|
|
2578
2673
|
var SP_URL2 = process.env.SUVEREN_AS_URL ?? "https://www.suveren.ai";
|
|
2579
2674
|
var port = parseInt(process.env.SUVEREN_CP_PORT ?? "3402", 10);
|
|
2580
2675
|
var HAP_MODE = process.env.HAP_MODE ?? "personal";
|
|
2581
|
-
var UI_DIST = process.env.HAP_UI_DIST ??
|
|
2676
|
+
var UI_DIST = process.env.HAP_UI_DIST ?? join8(import.meta.dirname ?? __dirname, "../../ui/dist");
|
|
2582
2677
|
var vault = new Vault();
|
|
2583
2678
|
var internalSecret2 = process.env.SUVEREN_INTERNAL_SECRET ?? randomBytes2(32).toString("hex");
|
|
2584
2679
|
setInternalSecret(internalSecret2);
|
|
@@ -2815,6 +2910,7 @@ app.use("/ai-prompts", jsonParser, authGuard, createAIPromptsRouter());
|
|
|
2815
2910
|
app.use("/mcp", jsonParser, authGuard, createMCPRouter());
|
|
2816
2911
|
app.use("/autostart", jsonParser, authGuard, createAutostartRouter());
|
|
2817
2912
|
app.use("/api/gateway-settings", jsonParser, authGuard, createGatewaySettingsRouter());
|
|
2913
|
+
app.use("/api/archived-mandates", jsonParser, authGuard, createArchivedMandatesRouter());
|
|
2818
2914
|
app.use("/api/encrypt-intent", jsonParser, authGuard, createEncryptIntentRouter());
|
|
2819
2915
|
app.use("/api/evidence-export", authGuard, createEvidenceExportRouter(SP_URL2, vault));
|
|
2820
2916
|
app.use("/api/evidence", authGuard, createEvidenceRouter());
|
|
@@ -2948,7 +3044,7 @@ app.get("/active-authorizations", authGuard, async (_req, res) => {
|
|
|
2948
3044
|
});
|
|
2949
3045
|
app.get("/denials", authGuard, (req, res) => {
|
|
2950
3046
|
try {
|
|
2951
|
-
const dataDir = process.env.SUVEREN_DATA_DIR ??
|
|
3047
|
+
const dataDir = process.env.SUVEREN_DATA_DIR ?? join8(homedir7(), ".suveren");
|
|
2952
3048
|
const all = loadDenials(dataDir, (blob) => vault.decrypt(blob));
|
|
2953
3049
|
const since = Number(req.query.since);
|
|
2954
3050
|
const limit = Number(req.query.limit);
|
|
@@ -2980,18 +3076,9 @@ app.post("/resync-gates", authGuard, async (_req, res) => {
|
|
|
2980
3076
|
res.status(500).json({ error: "Failed to resync gates with MCP server" });
|
|
2981
3077
|
}
|
|
2982
3078
|
});
|
|
2983
|
-
var AGENT_CONTEXT_MAX_BYTES = 16 * 1024;
|
|
2984
|
-
var SUVEREN_DATA_DIR2 = process.env.SUVEREN_DATA_DIR ?? join6(homedir5(), ".suveren");
|
|
2985
3079
|
app.get("/agent-brief/context", authGuard, async (_req, res) => {
|
|
2986
3080
|
try {
|
|
2987
|
-
|
|
2988
|
-
const filePath = join6(SUVEREN_DATA_DIR2, "context.md");
|
|
2989
|
-
if (!fileExists(filePath)) {
|
|
2990
|
-
res.json({ content: "" });
|
|
2991
|
-
return;
|
|
2992
|
-
}
|
|
2993
|
-
const content = readFileSync6(filePath, "utf-8");
|
|
2994
|
-
res.json({ content });
|
|
3081
|
+
res.json({ content: readAgentBrief() });
|
|
2995
3082
|
} catch (err) {
|
|
2996
3083
|
console.error("[Control Plane] agent-brief/context GET failed:", err);
|
|
2997
3084
|
res.status(500).json({ error: err instanceof Error ? err.message : "Failed to read context" });
|
|
@@ -3008,12 +3095,12 @@ app.put("/agent-brief/context", jsonParser, authGuard, async (req, res) => {
|
|
|
3008
3095
|
res.status(413).json({ error: `Context exceeds ${AGENT_CONTEXT_MAX_BYTES}-byte cap` });
|
|
3009
3096
|
return;
|
|
3010
3097
|
}
|
|
3011
|
-
const { mkdirSync:
|
|
3012
|
-
|
|
3013
|
-
|
|
3098
|
+
const { mkdirSync: mkdirSync6, writeFileSync: writeFileSync6, renameSync: renameSync3 } = await import("fs");
|
|
3099
|
+
const filePath = agentBriefPath();
|
|
3100
|
+
mkdirSync6(dirname6(filePath), { recursive: true });
|
|
3014
3101
|
const tmpPath = `${filePath}.tmp`;
|
|
3015
|
-
|
|
3016
|
-
|
|
3102
|
+
writeFileSync6(tmpPath, content, "utf-8");
|
|
3103
|
+
renameSync3(tmpPath, filePath);
|
|
3017
3104
|
res.json({ ok: true });
|
|
3018
3105
|
} catch (err) {
|
|
3019
3106
|
console.error("[Control Plane] agent-brief/context PUT failed:", err);
|
|
@@ -3062,13 +3149,13 @@ app.use(
|
|
|
3062
3149
|
})
|
|
3063
3150
|
);
|
|
3064
3151
|
function detectInstallMethod() {
|
|
3065
|
-
if (
|
|
3152
|
+
if (existsSync7("/.dockerenv")) return "docker";
|
|
3066
3153
|
const dir = import.meta.dirname ?? __dirname;
|
|
3067
3154
|
if (dir.includes("/node_modules/@suveren/gateway/")) return "npm";
|
|
3068
3155
|
let cursor = dir;
|
|
3069
3156
|
for (let i = 0; i < 8; i++) {
|
|
3070
|
-
if (
|
|
3071
|
-
const parent =
|
|
3157
|
+
if (existsSync7(join8(cursor, ".git"))) return "dev";
|
|
3158
|
+
const parent = dirname6(cursor);
|
|
3072
3159
|
if (parent === cursor) break;
|
|
3073
3160
|
cursor = parent;
|
|
3074
3161
|
}
|
|
@@ -3080,10 +3167,10 @@ function detectRunningVersion() {
|
|
|
3080
3167
|
return process.env.HAP_BUILD_SHA ?? "dev";
|
|
3081
3168
|
}
|
|
3082
3169
|
const dir = import.meta.dirname ?? __dirname;
|
|
3083
|
-
const bundlePkg =
|
|
3084
|
-
if (
|
|
3170
|
+
const bundlePkg = join8(dir, "..", "..", "package.json");
|
|
3171
|
+
if (existsSync7(bundlePkg)) {
|
|
3085
3172
|
try {
|
|
3086
|
-
const pkg = JSON.parse(
|
|
3173
|
+
const pkg = JSON.parse(readFileSync7(bundlePkg, "utf8"));
|
|
3087
3174
|
if (pkg.name === "@suveren/gateway" && typeof pkg.version === "string") {
|
|
3088
3175
|
return pkg.version;
|
|
3089
3176
|
}
|
|
@@ -3111,7 +3198,7 @@ app.get("/health", async (req, res) => {
|
|
|
3111
3198
|
}
|
|
3112
3199
|
});
|
|
3113
3200
|
});
|
|
3114
|
-
if (
|
|
3201
|
+
if (existsSync7(UI_DIST)) {
|
|
3115
3202
|
app.use(express.static(UI_DIST, {
|
|
3116
3203
|
setHeaders: (res, filePath) => {
|
|
3117
3204
|
if (filePath.endsWith("index.html")) {
|
|
@@ -3121,7 +3208,7 @@ if (existsSync6(UI_DIST)) {
|
|
|
3121
3208
|
}));
|
|
3122
3209
|
app.get("*", (_req, res) => {
|
|
3123
3210
|
res.setHeader("Cache-Control", "no-store, must-revalidate");
|
|
3124
|
-
res.sendFile(
|
|
3211
|
+
res.sendFile(join8(UI_DIST, "index.html"));
|
|
3125
3212
|
});
|
|
3126
3213
|
} else {
|
|
3127
3214
|
app.get("/", (_req, res) => {
|
package/dist/mcp-server/http.mjs
CHANGED
|
@@ -3464,6 +3464,76 @@ function getAllManifests() {
|
|
|
3464
3464
|
return Array.from(manifests.values());
|
|
3465
3465
|
}
|
|
3466
3466
|
|
|
3467
|
+
// src/lib/remote-auth-preflight.ts
|
|
3468
|
+
var AUTH_HEADER_RE = /^authorization:\s*(.+)$/i;
|
|
3469
|
+
function remotePreflightTarget(command, args) {
|
|
3470
|
+
const bin = command.split(/[\\/]/).pop() ?? command;
|
|
3471
|
+
if (bin !== "mcp-remote") return null;
|
|
3472
|
+
const url = args.find((a) => /^https?:\/\//i.test(a));
|
|
3473
|
+
if (!url) return null;
|
|
3474
|
+
for (let i = 0; i < args.length - 1; i++) {
|
|
3475
|
+
if (args[i] !== "--header") continue;
|
|
3476
|
+
const m = AUTH_HEADER_RE.exec(args[i + 1]);
|
|
3477
|
+
if (m) return { url, authorization: m[1].trim() };
|
|
3478
|
+
}
|
|
3479
|
+
return null;
|
|
3480
|
+
}
|
|
3481
|
+
function isBlankCredential(authorization) {
|
|
3482
|
+
return authorization.split(/\s+/).filter(Boolean).length < 2;
|
|
3483
|
+
}
|
|
3484
|
+
async function preflightRemoteAuth(target, fetchImpl = fetch, timeoutMs = 8e3) {
|
|
3485
|
+
const host = safeHost(target.url);
|
|
3486
|
+
if (isBlankCredential(target.authorization)) {
|
|
3487
|
+
return {
|
|
3488
|
+
ok: false,
|
|
3489
|
+
reason: `no credential configured for ${host} \u2014 the saved token is empty. Not started: the remote would open a browser login instead.`
|
|
3490
|
+
};
|
|
3491
|
+
}
|
|
3492
|
+
const ctrl = new AbortController();
|
|
3493
|
+
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
3494
|
+
try {
|
|
3495
|
+
const res = await fetchImpl(target.url, {
|
|
3496
|
+
method: "POST",
|
|
3497
|
+
headers: {
|
|
3498
|
+
Authorization: target.authorization,
|
|
3499
|
+
"Content-Type": "application/json",
|
|
3500
|
+
Accept: "application/json, text/event-stream"
|
|
3501
|
+
},
|
|
3502
|
+
body: JSON.stringify({
|
|
3503
|
+
jsonrpc: "2.0",
|
|
3504
|
+
id: 0,
|
|
3505
|
+
method: "initialize",
|
|
3506
|
+
params: {
|
|
3507
|
+
protocolVersion: "2025-03-26",
|
|
3508
|
+
capabilities: {},
|
|
3509
|
+
clientInfo: { name: "suveren-gateway-preflight", version: "0" }
|
|
3510
|
+
}
|
|
3511
|
+
}),
|
|
3512
|
+
signal: ctrl.signal
|
|
3513
|
+
});
|
|
3514
|
+
res.body?.cancel().catch(() => {
|
|
3515
|
+
});
|
|
3516
|
+
if (res.status === 401 || res.status === 403) {
|
|
3517
|
+
return {
|
|
3518
|
+
ok: false,
|
|
3519
|
+
reason: `${host} rejected the configured credential (HTTP ${res.status}). Not started: the remote would open a browser login instead. Re-enter the token under Integrations.`
|
|
3520
|
+
};
|
|
3521
|
+
}
|
|
3522
|
+
return { ok: true };
|
|
3523
|
+
} catch {
|
|
3524
|
+
return { ok: true };
|
|
3525
|
+
} finally {
|
|
3526
|
+
clearTimeout(timer);
|
|
3527
|
+
}
|
|
3528
|
+
}
|
|
3529
|
+
function safeHost(url) {
|
|
3530
|
+
try {
|
|
3531
|
+
return new URL(url).host;
|
|
3532
|
+
} catch {
|
|
3533
|
+
return url;
|
|
3534
|
+
}
|
|
3535
|
+
}
|
|
3536
|
+
|
|
3467
3537
|
// src/lib/integration-manager.ts
|
|
3468
3538
|
var DEFAULT_DATA_DIR2 = process.env.SUVEREN_DATA_DIR ?? join10(homedir8(), ".suveren");
|
|
3469
3539
|
var INTEGRATIONS_DIR = process.env.SUVEREN_INTEGRATIONS_DIR ?? join10(DEFAULT_DATA_DIR2, "integrations");
|
|
@@ -3707,6 +3777,11 @@ var IntegrationManager = class {
|
|
|
3707
3777
|
const env = this.resolveEnvKeys(config);
|
|
3708
3778
|
const interpolate = (s) => s.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_m, name) => env[name] ?? process.env[name] ?? "");
|
|
3709
3779
|
const args = config.args.map(interpolate);
|
|
3780
|
+
const remote = remotePreflightTarget(config.command, args);
|
|
3781
|
+
if (remote) {
|
|
3782
|
+
const verdict = await preflightRemoteAuth(remote);
|
|
3783
|
+
if (!verdict.ok) throw new Error(`${config.id}: ${verdict.reason}`);
|
|
3784
|
+
}
|
|
3710
3785
|
const transport = new StdioClientTransport({
|
|
3711
3786
|
command: config.command,
|
|
3712
3787
|
args,
|