@suveren/gateway 0.19.2 → 0.19.4
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 +14 -0
- package/dist/mcp-server/http.mjs +150 -74
- package/dist/ui/assets/index-BseWI4iJ.js +116 -0
- package/dist/ui/assets/{index-kObsFuwK.css → index-RCFQUjaF.css} +1 -1
- package/dist/ui/index.html +2 -2
- package/node_modules/@hono/node-server/package.json +2 -2
- package/package.json +1 -1
- package/profiles/delegation/0.2.profile.json +117 -0
- package/profiles/index.json +1 -0
- package/dist/ui/assets/index-BE0Yn5xB.js +0 -111
|
@@ -599,6 +599,13 @@ async function exportReport(gatewayVersion) {
|
|
|
599
599
|
const filename = /filename="([^"]+)"/.exec(res.headers.get("content-disposition") ?? "")?.[1] ?? "suveren-report.html";
|
|
600
600
|
return { html, filename };
|
|
601
601
|
}
|
|
602
|
+
async function getAgentContact() {
|
|
603
|
+
const res = await fetch(`${MCP_BASE}/internal/agent-contact`, {
|
|
604
|
+
headers: internalHeaders()
|
|
605
|
+
});
|
|
606
|
+
if (!res.ok) throw new Error("Failed to fetch agent contact");
|
|
607
|
+
return res.json();
|
|
608
|
+
}
|
|
602
609
|
async function getMcpHealth() {
|
|
603
610
|
const res = await fetch(`${MCP_BASE}/health`);
|
|
604
611
|
if (!res.ok) throw new Error("MCP server unreachable");
|
|
@@ -2870,6 +2877,13 @@ function createMCPRouter() {
|
|
|
2870
2877
|
res.status(502).json({ error: "MCP server unreachable" });
|
|
2871
2878
|
}
|
|
2872
2879
|
});
|
|
2880
|
+
router.get("/agent-contact", async (_req, res) => {
|
|
2881
|
+
try {
|
|
2882
|
+
res.json(await getAgentContact());
|
|
2883
|
+
} catch {
|
|
2884
|
+
res.status(502).json({ error: "MCP server unreachable" });
|
|
2885
|
+
}
|
|
2886
|
+
});
|
|
2873
2887
|
router.get("/integrations", async (_req, res) => {
|
|
2874
2888
|
try {
|
|
2875
2889
|
const data = await getIntegrations();
|
package/dist/mcp-server/http.mjs
CHANGED
|
@@ -4158,6 +4158,13 @@ function buildMandateBrief(opts) {
|
|
|
4158
4158
|
}
|
|
4159
4159
|
lines.push("");
|
|
4160
4160
|
}
|
|
4161
|
+
if (isSimulationMode()) {
|
|
4162
|
+
const canSetUp = active.some((a) => shortProfileName(a.profileId) === "delegation");
|
|
4163
|
+
lines.push("=== GETTING STARTED ===");
|
|
4164
|
+
lines.push("");
|
|
4165
|
+
lines.push(canSetUp ? "If the person asks how to start with Suveren or how to set up their work with it: call setup__get_guide first and follow its steps in order." : `If the person asks how to start with Suveren: they first create a "Delegation" mandate in the Suveren Gateway at ${uiUrl()}. With it you can read the setup guide and lead them through the setup.`);
|
|
4166
|
+
lines.push("");
|
|
4167
|
+
}
|
|
4161
4168
|
lines.push("When you receive a task, call list-authorizations(domain) to load full details for the relevant domain.");
|
|
4162
4169
|
return lines.join("\n");
|
|
4163
4170
|
}
|
|
@@ -4947,6 +4954,54 @@ function createMcpServer(state2, integrationManager2) {
|
|
|
4947
4954
|
return { server, gatekeeper: state2.gatekeeper, refreshTools, registerProxiedTools };
|
|
4948
4955
|
}
|
|
4949
4956
|
|
|
4957
|
+
// src/lib/agent-contact.ts
|
|
4958
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync10, readFileSync as readFileSync13, renameSync as renameSync4, writeFileSync as writeFileSync10 } from "fs";
|
|
4959
|
+
import { dirname as dirname10, join as join12 } from "path";
|
|
4960
|
+
var FILE = "agent-contact.json";
|
|
4961
|
+
var REWRITE_AFTER_MS = 6e4;
|
|
4962
|
+
var AgentContactStore = class {
|
|
4963
|
+
path;
|
|
4964
|
+
current;
|
|
4965
|
+
constructor(dataDir2) {
|
|
4966
|
+
this.path = join12(dataDir2, FILE);
|
|
4967
|
+
this.current = this.load();
|
|
4968
|
+
}
|
|
4969
|
+
load() {
|
|
4970
|
+
if (!existsSync12(this.path)) return null;
|
|
4971
|
+
try {
|
|
4972
|
+
const v = JSON.parse(readFileSync13(this.path, "utf8"));
|
|
4973
|
+
if (typeof v.clientName !== "string" || typeof v.firstSeenAt !== "string" || typeof v.lastSeenAt !== "string") return null;
|
|
4974
|
+
return { clientName: v.clientName, clientVersion: v.clientVersion, firstSeenAt: v.firstSeenAt, lastSeenAt: v.lastSeenAt };
|
|
4975
|
+
} catch {
|
|
4976
|
+
return null;
|
|
4977
|
+
}
|
|
4978
|
+
}
|
|
4979
|
+
read() {
|
|
4980
|
+
return this.current;
|
|
4981
|
+
}
|
|
4982
|
+
/** Record a completed MCP initialize. `now` is injectable for tests. */
|
|
4983
|
+
record(client, now = /* @__PURE__ */ new Date()) {
|
|
4984
|
+
const clientName = client?.name?.trim() || "Unknown AI client";
|
|
4985
|
+
const prev = this.current;
|
|
4986
|
+
const due = !prev || prev.clientName !== clientName || now.getTime() - Date.parse(prev.lastSeenAt) >= REWRITE_AFTER_MS;
|
|
4987
|
+
this.current = {
|
|
4988
|
+
clientName,
|
|
4989
|
+
clientVersion: client?.version,
|
|
4990
|
+
firstSeenAt: prev?.firstSeenAt ?? now.toISOString(),
|
|
4991
|
+
lastSeenAt: now.toISOString()
|
|
4992
|
+
};
|
|
4993
|
+
if (!due) return;
|
|
4994
|
+
try {
|
|
4995
|
+
mkdirSync10(dirname10(this.path), { recursive: true });
|
|
4996
|
+
const tmp = `${this.path}.tmp`;
|
|
4997
|
+
writeFileSync10(tmp, JSON.stringify(this.current, null, 2));
|
|
4998
|
+
renameSync4(tmp, this.path);
|
|
4999
|
+
} catch (err) {
|
|
5000
|
+
console.error("[Suveren MCP] Could not save agent contact:", err);
|
|
5001
|
+
}
|
|
5002
|
+
}
|
|
5003
|
+
};
|
|
5004
|
+
|
|
4950
5005
|
// src/lib/gate-content.ts
|
|
4951
5006
|
import { computeIntentHash } from "@hap/core";
|
|
4952
5007
|
import { decodeAttestationBlob as decodeAttestationBlob2 } from "@hap/core";
|
|
@@ -4974,16 +5029,16 @@ function verifyGateContentHashes(content, auth) {
|
|
|
4974
5029
|
}
|
|
4975
5030
|
|
|
4976
5031
|
// src/lib/integration-registry.ts
|
|
4977
|
-
import { mkdirSync as
|
|
5032
|
+
import { mkdirSync as mkdirSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync11, existsSync as existsSync13 } from "fs";
|
|
4978
5033
|
import { homedir as homedir9 } from "os";
|
|
4979
|
-
import { dirname as
|
|
4980
|
-
var DEFAULT_DATA_DIR = process.env.SUVEREN_DATA_DIR ??
|
|
5034
|
+
import { dirname as dirname11, join as join13 } from "path";
|
|
5035
|
+
var DEFAULT_DATA_DIR = process.env.SUVEREN_DATA_DIR ?? join13(homedir9(), ".suveren");
|
|
4981
5036
|
var IntegrationRegistry = class {
|
|
4982
5037
|
integrations = /* @__PURE__ */ new Map();
|
|
4983
5038
|
filePath;
|
|
4984
5039
|
constructor(dataDir2) {
|
|
4985
5040
|
const dir = dataDir2 ?? DEFAULT_DATA_DIR;
|
|
4986
|
-
this.filePath =
|
|
5041
|
+
this.filePath = join13(dir, "integrations.json");
|
|
4987
5042
|
this.load();
|
|
4988
5043
|
}
|
|
4989
5044
|
// ─── CRUD ───────────────────────────────────────────────────────────────
|
|
@@ -5014,12 +5069,12 @@ var IntegrationRegistry = class {
|
|
|
5014
5069
|
}
|
|
5015
5070
|
// ─── Load / Save ────────────────────────────────────────────────────────
|
|
5016
5071
|
load() {
|
|
5017
|
-
if (!
|
|
5018
|
-
|
|
5072
|
+
if (!existsSync13(this.filePath)) {
|
|
5073
|
+
mkdirSync11(dirname11(this.filePath), { recursive: true });
|
|
5019
5074
|
return;
|
|
5020
5075
|
}
|
|
5021
5076
|
try {
|
|
5022
|
-
const raw =
|
|
5077
|
+
const raw = readFileSync14(this.filePath, "utf-8");
|
|
5023
5078
|
const data = JSON.parse(raw);
|
|
5024
5079
|
const migrated = data.integrations.map((i) => {
|
|
5025
5080
|
if ("toolGating" in i && !("profile" in i)) {
|
|
@@ -5048,23 +5103,23 @@ var IntegrationRegistry = class {
|
|
|
5048
5103
|
version: 1,
|
|
5049
5104
|
integrations: Array.from(this.integrations.values())
|
|
5050
5105
|
};
|
|
5051
|
-
|
|
5052
|
-
|
|
5106
|
+
mkdirSync11(dirname11(this.filePath), { recursive: true });
|
|
5107
|
+
writeFileSync11(this.filePath, JSON.stringify(data, null, 2), { encoding: "utf-8" });
|
|
5053
5108
|
}
|
|
5054
5109
|
};
|
|
5055
5110
|
|
|
5056
5111
|
// src/lib/integration-manager.ts
|
|
5057
5112
|
import { homedir as homedir10 } from "os";
|
|
5058
|
-
import { join as
|
|
5059
|
-
import { existsSync as
|
|
5113
|
+
import { join as join15, delimiter } from "path";
|
|
5114
|
+
import { existsSync as existsSync15, writeFileSync as writeFileSync12, mkdirSync as mkdirSync12, readFileSync as readFileSync16, rmSync } from "fs";
|
|
5060
5115
|
import { execFile } from "child_process";
|
|
5061
5116
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
5062
5117
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
5063
5118
|
import { getProfile as getProfile5 } from "@hap/core";
|
|
5064
5119
|
|
|
5065
5120
|
// src/lib/manifest-loader.ts
|
|
5066
|
-
import { readFileSync as
|
|
5067
|
-
import { join as
|
|
5121
|
+
import { readFileSync as readFileSync15, existsSync as existsSync14 } from "fs";
|
|
5122
|
+
import { join as join14, resolve } from "path";
|
|
5068
5123
|
var EXACT_SEMVER_RE = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
5069
5124
|
function isExactSemver(value) {
|
|
5070
5125
|
return typeof value === "string" && EXACT_SEMVER_RE.test(value);
|
|
@@ -5082,25 +5137,25 @@ function invalidNpmPinReason(manifest) {
|
|
|
5082
5137
|
var manifests = /* @__PURE__ */ new Map();
|
|
5083
5138
|
function loadManifests(integrationsDir) {
|
|
5084
5139
|
const dir = resolve(
|
|
5085
|
-
integrationsDir ?? process.env.SUVEREN_MANIFESTS_DIR ??
|
|
5140
|
+
integrationsDir ?? process.env.SUVEREN_MANIFESTS_DIR ?? join14(import.meta.dirname ?? __dirname, "..", "..", "..", "..", "content", "integrations")
|
|
5086
5141
|
);
|
|
5087
|
-
const indexPath =
|
|
5088
|
-
if (!
|
|
5142
|
+
const indexPath = join14(dir, "index.json");
|
|
5143
|
+
if (!existsSync14(indexPath)) {
|
|
5089
5144
|
console.error(`[ManifestLoader] No index.json found at ${indexPath}, skipping manifest loading`);
|
|
5090
5145
|
return 0;
|
|
5091
5146
|
}
|
|
5092
5147
|
let index;
|
|
5093
5148
|
try {
|
|
5094
|
-
index = JSON.parse(
|
|
5149
|
+
index = JSON.parse(readFileSync15(indexPath, "utf-8"));
|
|
5095
5150
|
} catch (err) {
|
|
5096
5151
|
console.error(`[ManifestLoader] Failed to parse ${indexPath}:`, err);
|
|
5097
5152
|
return 0;
|
|
5098
5153
|
}
|
|
5099
5154
|
let loaded = 0;
|
|
5100
5155
|
for (const [id, relativePath] of Object.entries(index.integrations)) {
|
|
5101
|
-
const manifestPath =
|
|
5156
|
+
const manifestPath = join14(dir, relativePath);
|
|
5102
5157
|
try {
|
|
5103
|
-
const manifest = JSON.parse(
|
|
5158
|
+
const manifest = JSON.parse(readFileSync15(manifestPath, "utf-8"));
|
|
5104
5159
|
const pinReason = invalidNpmPinReason(manifest);
|
|
5105
5160
|
if (pinReason) {
|
|
5106
5161
|
console.error(`[ManifestLoader] Refusing manifest ${id} (${manifestPath}): ${pinReason}`);
|
|
@@ -5247,8 +5302,8 @@ function inheritedConnectorEnv(parent, passEnv = []) {
|
|
|
5247
5302
|
}
|
|
5248
5303
|
|
|
5249
5304
|
// src/lib/integration-manager.ts
|
|
5250
|
-
var DEFAULT_DATA_DIR2 = process.env.SUVEREN_DATA_DIR ??
|
|
5251
|
-
var INTEGRATIONS_DIR = process.env.SUVEREN_INTEGRATIONS_DIR ??
|
|
5305
|
+
var DEFAULT_DATA_DIR2 = process.env.SUVEREN_DATA_DIR ?? join15(homedir10(), ".suveren");
|
|
5306
|
+
var INTEGRATIONS_DIR = process.env.SUVEREN_INTEGRATIONS_DIR ?? join15(DEFAULT_DATA_DIR2, "integrations");
|
|
5252
5307
|
var OFFLINE_MODE = process.env.SUVEREN_OFFLINE === "1";
|
|
5253
5308
|
var installLock = Promise.resolve();
|
|
5254
5309
|
function withInstallLock(task) {
|
|
@@ -5256,7 +5311,7 @@ function withInstallLock(task) {
|
|
|
5256
5311
|
installLock = run.then(() => void 0, () => void 0);
|
|
5257
5312
|
return run;
|
|
5258
5313
|
}
|
|
5259
|
-
var INTEGRATIONS_BIN =
|
|
5314
|
+
var INTEGRATIONS_BIN = join15(INTEGRATIONS_DIR, "node_modules", ".bin");
|
|
5260
5315
|
function getIntegrationsBinDir() {
|
|
5261
5316
|
return INTEGRATIONS_BIN;
|
|
5262
5317
|
}
|
|
@@ -5265,12 +5320,12 @@ function buildPath() {
|
|
|
5265
5320
|
return [INTEGRATIONS_BIN, base].join(delimiter);
|
|
5266
5321
|
}
|
|
5267
5322
|
function ensureIntegrationsDir() {
|
|
5268
|
-
if (!
|
|
5269
|
-
|
|
5323
|
+
if (!existsSync15(INTEGRATIONS_DIR)) {
|
|
5324
|
+
mkdirSync12(INTEGRATIONS_DIR, { recursive: true });
|
|
5270
5325
|
}
|
|
5271
|
-
const pkgPath =
|
|
5272
|
-
if (!
|
|
5273
|
-
|
|
5326
|
+
const pkgPath = join15(INTEGRATIONS_DIR, "package.json");
|
|
5327
|
+
if (!existsSync15(pkgPath)) {
|
|
5328
|
+
writeFileSync12(pkgPath, JSON.stringify({ name: "suveren-integrations", version: "1.0.0", private: true }, null, 2));
|
|
5274
5329
|
}
|
|
5275
5330
|
}
|
|
5276
5331
|
function readAgeOf(config) {
|
|
@@ -5359,19 +5414,19 @@ var IntegrationManager = class {
|
|
|
5359
5414
|
* exist, and treat anything else as not-installed (and reinstallable).
|
|
5360
5415
|
*/
|
|
5361
5416
|
isUsableInstall(npmPackage) {
|
|
5362
|
-
const pkgDir =
|
|
5363
|
-
const pkgJson =
|
|
5364
|
-
if (!
|
|
5417
|
+
const pkgDir = join15(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"));
|
|
5418
|
+
const pkgJson = join15(pkgDir, "package.json");
|
|
5419
|
+
if (!existsSync15(pkgDir) || !existsSync15(pkgJson)) return false;
|
|
5365
5420
|
try {
|
|
5366
|
-
const pkg = JSON.parse(
|
|
5421
|
+
const pkg = JSON.parse(readFileSync16(pkgJson, "utf8"));
|
|
5367
5422
|
const defaultBinName = (pkg.name ?? npmPackage).split("/").pop() ?? "";
|
|
5368
5423
|
const binMap = typeof pkg.bin === "string" ? { [defaultBinName]: pkg.bin } : pkg.bin ?? {};
|
|
5369
5424
|
const binEntries = Object.entries(binMap);
|
|
5370
5425
|
for (const [binName, target] of binEntries) {
|
|
5371
|
-
if (!
|
|
5426
|
+
if (!existsSync15(join15(pkgDir, target))) return false;
|
|
5372
5427
|
if (!this.binShimExists(binName)) return false;
|
|
5373
5428
|
}
|
|
5374
|
-
if (binEntries.length === 0 && pkg.main && !
|
|
5429
|
+
if (binEntries.length === 0 && pkg.main && !existsSync15(join15(pkgDir, pkg.main))) return false;
|
|
5375
5430
|
return true;
|
|
5376
5431
|
} catch {
|
|
5377
5432
|
return false;
|
|
@@ -5386,9 +5441,9 @@ var IntegrationManager = class {
|
|
|
5386
5441
|
*/
|
|
5387
5442
|
readInstalledVersion(npmPackage) {
|
|
5388
5443
|
if (!this.isUsableInstall(npmPackage)) return null;
|
|
5389
|
-
const pkgJson =
|
|
5444
|
+
const pkgJson = join15(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"), "package.json");
|
|
5390
5445
|
try {
|
|
5391
|
-
const pkg = JSON.parse(
|
|
5446
|
+
const pkg = JSON.parse(readFileSync16(pkgJson, "utf8"));
|
|
5392
5447
|
return typeof pkg.version === "string" ? pkg.version : null;
|
|
5393
5448
|
} catch {
|
|
5394
5449
|
return null;
|
|
@@ -5402,7 +5457,7 @@ var IntegrationManager = class {
|
|
|
5402
5457
|
binShimExists(binName) {
|
|
5403
5458
|
if (!binName) return false;
|
|
5404
5459
|
const candidates = process.platform === "win32" ? [binName, `${binName}.cmd`, `${binName}.CMD`, `${binName}.ps1`] : [binName];
|
|
5405
|
-
return candidates.some((c) =>
|
|
5460
|
+
return candidates.some((c) => existsSync15(join15(INTEGRATIONS_BIN, c)));
|
|
5406
5461
|
}
|
|
5407
5462
|
/**
|
|
5408
5463
|
* The exact version THIS gateway's CURRENTLY LOADED manifest pins
|
|
@@ -5492,8 +5547,8 @@ var IntegrationManager = class {
|
|
|
5492
5547
|
* nothing at all.
|
|
5493
5548
|
*/
|
|
5494
5549
|
async installNow(npmPackage, exactVersion, cleanFirst = true) {
|
|
5495
|
-
const pkgDir =
|
|
5496
|
-
if (cleanFirst &&
|
|
5550
|
+
const pkgDir = join15(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"));
|
|
5551
|
+
if (cleanFirst && existsSync15(pkgDir)) {
|
|
5497
5552
|
console.error(`[IntegrationManager] Removing ${exactVersion ? "outdated" : "unusable"} install of ${npmPackage}`);
|
|
5498
5553
|
try {
|
|
5499
5554
|
rmSync(pkgDir, { recursive: true, force: true });
|
|
@@ -5766,6 +5821,7 @@ var IntegrationManager = class {
|
|
|
5766
5821
|
name: def.name,
|
|
5767
5822
|
description: def.description ?? "",
|
|
5768
5823
|
profile: def.profile,
|
|
5824
|
+
...def.intentHint ? { intentHint: def.intentHint } : {},
|
|
5769
5825
|
available: !def.simulationOnly || isSimulationMode()
|
|
5770
5826
|
}));
|
|
5771
5827
|
}
|
|
@@ -6398,28 +6454,28 @@ function agentAccess(token, opensSession) {
|
|
|
6398
6454
|
}
|
|
6399
6455
|
|
|
6400
6456
|
// src/lib/profile-loader.ts
|
|
6401
|
-
import { readFileSync as
|
|
6402
|
-
import { join as
|
|
6457
|
+
import { readFileSync as readFileSync17, existsSync as existsSync16 } from "fs";
|
|
6458
|
+
import { join as join16, resolve as resolve2 } from "path";
|
|
6403
6459
|
import { registerProfile } from "@hap/core";
|
|
6404
6460
|
function loadProfiles(profilesDir) {
|
|
6405
|
-
const dir = resolve2(profilesDir ?? process.env.SUVEREN_PROFILES_DIR ??
|
|
6406
|
-
const indexPath =
|
|
6407
|
-
if (!
|
|
6461
|
+
const dir = resolve2(profilesDir ?? process.env.SUVEREN_PROFILES_DIR ?? join16(import.meta.dirname ?? __dirname, "..", "..", "..", "..", "..", "hap-profiles"));
|
|
6462
|
+
const indexPath = join16(dir, "index.json");
|
|
6463
|
+
if (!existsSync16(indexPath)) {
|
|
6408
6464
|
warnNoProfiles(`No index.json at ${indexPath}`);
|
|
6409
6465
|
return 0;
|
|
6410
6466
|
}
|
|
6411
6467
|
let index;
|
|
6412
6468
|
try {
|
|
6413
|
-
index = JSON.parse(
|
|
6469
|
+
index = JSON.parse(readFileSync17(indexPath, "utf-8"));
|
|
6414
6470
|
} catch (err) {
|
|
6415
6471
|
console.error(`[ProfileLoader] Failed to parse ${indexPath}:`, err);
|
|
6416
6472
|
return 0;
|
|
6417
6473
|
}
|
|
6418
6474
|
let loaded = 0;
|
|
6419
6475
|
for (const [profileId, relativePath] of Object.entries(index.profiles)) {
|
|
6420
|
-
const profilePath =
|
|
6476
|
+
const profilePath = join16(dir, relativePath);
|
|
6421
6477
|
try {
|
|
6422
|
-
const profile = JSON.parse(
|
|
6478
|
+
const profile = JSON.parse(readFileSync17(profilePath, "utf-8"));
|
|
6423
6479
|
registerProfile(profileId, profile);
|
|
6424
6480
|
const shortName = profileId.split("/").pop()?.replace(/@.*$/, "");
|
|
6425
6481
|
if (shortName && shortName !== profileId) {
|
|
@@ -6757,20 +6813,20 @@ async function controlPlaneMandate(dryRun, request) {
|
|
|
6757
6813
|
}
|
|
6758
6814
|
|
|
6759
6815
|
// src/lib/guides.ts
|
|
6760
|
-
import { existsSync as
|
|
6761
|
-
import { dirname as
|
|
6816
|
+
import { existsSync as existsSync17, readdirSync, readFileSync as readFileSync18 } from "fs";
|
|
6817
|
+
import { dirname as dirname12, join as join17 } from "path";
|
|
6762
6818
|
var FILE_RE = /^(?:(\d+)-)?([a-z0-9][a-z0-9-]*)\.md$/;
|
|
6763
6819
|
function builtinGuidesDir() {
|
|
6764
6820
|
if (process.env.SUVEREN_BUILTIN_GUIDES_DIR) return process.env.SUVEREN_BUILTIN_GUIDES_DIR;
|
|
6765
6821
|
let dir = import.meta.dirname ?? __dirname;
|
|
6766
6822
|
for (let i = 0; i < 8; i++) {
|
|
6767
|
-
const candidate =
|
|
6768
|
-
if (
|
|
6769
|
-
const parent =
|
|
6823
|
+
const candidate = join17(dir, "content", "guides");
|
|
6824
|
+
if (existsSync17(candidate)) return candidate;
|
|
6825
|
+
const parent = dirname12(dir);
|
|
6770
6826
|
if (parent === dir) break;
|
|
6771
6827
|
dir = parent;
|
|
6772
6828
|
}
|
|
6773
|
-
return
|
|
6829
|
+
return join17(import.meta.dirname ?? __dirname, "content", "guides");
|
|
6774
6830
|
}
|
|
6775
6831
|
function parse(body) {
|
|
6776
6832
|
const lines = body.split(/\r?\n/);
|
|
@@ -6781,14 +6837,14 @@ function parse(body) {
|
|
|
6781
6837
|
return { title, summary };
|
|
6782
6838
|
}
|
|
6783
6839
|
function readDir(dir, source) {
|
|
6784
|
-
if (!dir || !
|
|
6840
|
+
if (!dir || !existsSync17(dir)) return [];
|
|
6785
6841
|
const out = [];
|
|
6786
6842
|
for (const file of readdirSync(dir)) {
|
|
6787
6843
|
const m = FILE_RE.exec(file);
|
|
6788
6844
|
if (!m) continue;
|
|
6789
6845
|
let body;
|
|
6790
6846
|
try {
|
|
6791
|
-
body =
|
|
6847
|
+
body = readFileSync18(join17(dir, file), "utf8");
|
|
6792
6848
|
} catch {
|
|
6793
6849
|
continue;
|
|
6794
6850
|
}
|
|
@@ -6824,11 +6880,20 @@ function guideHeader(systems) {
|
|
|
6824
6880
|
lines.push(`- **${s.id}** \u2014 ${parts.join("; ")}`);
|
|
6825
6881
|
}
|
|
6826
6882
|
}
|
|
6883
|
+
lines.push("");
|
|
6884
|
+
lines.push(`**Approvals:** every mandate and brief you propose waits until the person approves it in the Suveren Gateway at ${uiUrl()}/approvals. Tell them each time you propose one.`);
|
|
6827
6885
|
return lines.join("\n");
|
|
6828
6886
|
}
|
|
6829
6887
|
|
|
6830
6888
|
// src/lib/builtins/setup.ts
|
|
6831
|
-
var DELEGATION_PROFILE = "
|
|
6889
|
+
var DELEGATION_PROFILE = "delegation";
|
|
6890
|
+
var DELEGATION_INTENT_HINT = [
|
|
6891
|
+
"Why \u2014 With test data only, I want to find out which routine parts of my work an AI could take over, so people gain time \u2014 before anything touches real systems.",
|
|
6892
|
+
"",
|
|
6893
|
+
"Goal \u2014 Follow the setup guide: interview me about the company, the team and the work; prepare test data from real cases, renamed to .example names; propose the mandates and the working instructions (agent brief) that the AI doing the work will need.",
|
|
6894
|
+
"",
|
|
6895
|
+
"Watch out \u2014 You only propose; nothing happens until I approve it in the Suveren Gateway. Ask, don't guess: never invent a rule, a limit or a step. One proposal at a time. Steps customers would see start with my review. Never use real names or customer data \u2014 proposals are stored readable on the Suveren server. Never give the working AI a reporting mandate during a test run. Keep the setup and the working AI in separate conversations."
|
|
6896
|
+
].join("\n");
|
|
6832
6897
|
function briefRefusal(args) {
|
|
6833
6898
|
if (typeof args.content !== "string" || args.content.trim() === "") return "`content` must be the complete new brief (markdown text).";
|
|
6834
6899
|
const bytes = Buffer.byteLength(args.content, "utf-8");
|
|
@@ -6854,8 +6919,13 @@ function connectedSystems(im) {
|
|
|
6854
6919
|
function setupBuiltin(deps) {
|
|
6855
6920
|
return {
|
|
6856
6921
|
id: "setup",
|
|
6857
|
-
|
|
6922
|
+
// Shown as the picker card title (apps/ui AuthorizePicker) — the person
|
|
6923
|
+
// reads this as the mandate they're giving, so it must match the profile
|
|
6924
|
+
// name they'll see everywhere else (Mandates page, dashboard first-run
|
|
6925
|
+
// card), not the internal "test setup" framing. Description is unchanged.
|
|
6926
|
+
name: "Delegation",
|
|
6858
6927
|
description: "Your AI sets up a test of itself: it reads the setup guides and proposes its agent brief and its mandates. Every proposal waits for your approval. Simulation mode only.",
|
|
6928
|
+
intentHint: DELEGATION_INTENT_HINT,
|
|
6859
6929
|
profile: DELEGATION_PROFILE,
|
|
6860
6930
|
simulation: true,
|
|
6861
6931
|
simulationOnly: true,
|
|
@@ -6893,7 +6963,7 @@ function setupBuiltin(deps) {
|
|
|
6893
6963
|
tools: [
|
|
6894
6964
|
{
|
|
6895
6965
|
name: "get_guide",
|
|
6896
|
-
description: "Simulation mode only: the setup guides \u2014 how to interview the person, build the test data, propose mandates and the agent brief, set up the working AI's process, and what can make a test misleading. Call without `topic` for the list and the order to follow; then read each topic before doing that step.",
|
|
6966
|
+
description: "Start here when the person asks how to begin with Suveren or how to set up their work with it. Simulation mode only: the setup guides \u2014 how to interview the person, build the test data, propose mandates and the agent brief, set up the working AI's process, and what can make a test misleading. Call without `topic` for the list and the order to follow; then read each topic before doing that step.",
|
|
6897
6967
|
inputSchema: {
|
|
6898
6968
|
type: "object",
|
|
6899
6969
|
properties: {
|
|
@@ -7020,8 +7090,8 @@ function registerBuiltins(deps, factories = BUILTIN_FACTORIES) {
|
|
|
7020
7090
|
import { decodeAttestationBlob as decodeAttestationBlob3 } from "@hap/core";
|
|
7021
7091
|
|
|
7022
7092
|
// src/lib/committed-executor.ts
|
|
7023
|
-
import { existsSync as
|
|
7024
|
-
import { dirname as
|
|
7093
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync13, readFileSync as readFileSync19, unlinkSync as unlinkSync6, writeFileSync as writeFileSync13 } from "fs";
|
|
7094
|
+
import { dirname as dirname13, join as join18 } from "path";
|
|
7025
7095
|
var CommittedExecutor = class {
|
|
7026
7096
|
constructor(run) {
|
|
7027
7097
|
this.run = run;
|
|
@@ -7049,7 +7119,7 @@ var ExecutorLock = class {
|
|
|
7049
7119
|
path;
|
|
7050
7120
|
held = false;
|
|
7051
7121
|
constructor(dataDir2) {
|
|
7052
|
-
this.path =
|
|
7122
|
+
this.path = join18(dataDir2, "executor.lock");
|
|
7053
7123
|
}
|
|
7054
7124
|
/** Try to take the lock. Returns the holder's pid when someone else has it. */
|
|
7055
7125
|
acquire() {
|
|
@@ -7057,9 +7127,9 @@ var ExecutorLock = class {
|
|
|
7057
7127
|
if (current && current.pid !== process.pid && isAlive(current.pid)) {
|
|
7058
7128
|
return { held: false, holderPid: current.pid };
|
|
7059
7129
|
}
|
|
7060
|
-
|
|
7130
|
+
mkdirSync13(dirname13(this.path), { recursive: true, mode: 448 });
|
|
7061
7131
|
const lock = { pid: process.pid, startedAt: Math.floor(Date.now() / 1e3) };
|
|
7062
|
-
|
|
7132
|
+
writeFileSync13(this.path, JSON.stringify(lock), { mode: 384 });
|
|
7063
7133
|
this.held = true;
|
|
7064
7134
|
return { held: true };
|
|
7065
7135
|
}
|
|
@@ -7081,9 +7151,9 @@ var ExecutorLock = class {
|
|
|
7081
7151
|
}
|
|
7082
7152
|
}
|
|
7083
7153
|
read() {
|
|
7084
|
-
if (!
|
|
7154
|
+
if (!existsSync18(this.path)) return null;
|
|
7085
7155
|
try {
|
|
7086
|
-
const parsed = JSON.parse(
|
|
7156
|
+
const parsed = JSON.parse(readFileSync19(this.path, "utf8"));
|
|
7087
7157
|
return typeof parsed?.pid === "number" ? parsed : null;
|
|
7088
7158
|
} catch {
|
|
7089
7159
|
return null;
|
|
@@ -7101,19 +7171,19 @@ function isAlive(pid) {
|
|
|
7101
7171
|
|
|
7102
7172
|
// bin/http.ts
|
|
7103
7173
|
import { homedir as homedir11 } from "os";
|
|
7104
|
-
import { join as
|
|
7174
|
+
import { join as join20 } from "path";
|
|
7105
7175
|
|
|
7106
7176
|
// src/lib/mcp-as-tracker.ts
|
|
7107
|
-
import { existsSync as
|
|
7108
|
-
import { dirname as
|
|
7177
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync14, readFileSync as readFileSync20, writeFileSync as writeFileSync14 } from "fs";
|
|
7178
|
+
import { dirname as dirname14, join as join19 } from "path";
|
|
7109
7179
|
function trackerPath(dataDir2) {
|
|
7110
|
-
return
|
|
7180
|
+
return join19(dataDir2, "mcp-as-pairing.json");
|
|
7111
7181
|
}
|
|
7112
7182
|
function readMcpPairedAsUrl(dataDir2) {
|
|
7113
7183
|
const path = trackerPath(dataDir2);
|
|
7114
|
-
if (!
|
|
7184
|
+
if (!existsSync19(path)) return null;
|
|
7115
7185
|
try {
|
|
7116
|
-
const data = JSON.parse(
|
|
7186
|
+
const data = JSON.parse(readFileSync20(path, "utf-8"));
|
|
7117
7187
|
return typeof data.asUrl === "string" ? data.asUrl : null;
|
|
7118
7188
|
} catch {
|
|
7119
7189
|
return null;
|
|
@@ -7121,12 +7191,12 @@ function readMcpPairedAsUrl(dataDir2) {
|
|
|
7121
7191
|
}
|
|
7122
7192
|
function writeMcpPairedAsUrl(dataDir2, asUrl) {
|
|
7123
7193
|
const path = trackerPath(dataDir2);
|
|
7124
|
-
|
|
7125
|
-
|
|
7194
|
+
mkdirSync14(dirname14(path), { recursive: true, mode: 448 });
|
|
7195
|
+
writeFileSync14(path, JSON.stringify({ asUrl }, null, 2), { encoding: "utf-8", mode: 384 });
|
|
7126
7196
|
}
|
|
7127
7197
|
|
|
7128
7198
|
// bin/http.ts
|
|
7129
|
-
var dataDir = process.env.SUVEREN_DATA_DIR ??
|
|
7199
|
+
var dataDir = process.env.SUVEREN_DATA_DIR ?? join20(homedir11(), ".suveren");
|
|
7130
7200
|
var spUrl = resolveAsUrl(dataDir);
|
|
7131
7201
|
validatePinTlsForUrl(resolvePinTls(dataDir), spUrl);
|
|
7132
7202
|
var port = parseInt(process.env.SUVEREN_MCP_PORT ?? "3430", 10);
|
|
@@ -7139,6 +7209,7 @@ if (bindProblem) {
|
|
|
7139
7209
|
}
|
|
7140
7210
|
setAsBaseUrl(spUrl);
|
|
7141
7211
|
var state = new SharedState(spUrl, void 0, dataDir);
|
|
7212
|
+
var agentContact = new AgentContactStore(dataDir);
|
|
7142
7213
|
var reportSources = {
|
|
7143
7214
|
archive: state.receiptArchive,
|
|
7144
7215
|
runExport: createConnectorExportRunner({ integrationsBinDir: getIntegrationsBinDir(), dataDir })
|
|
@@ -7422,6 +7493,7 @@ var guardAgent = agentAccess(
|
|
|
7422
7493
|
app.get("/sse", guardAgent, async (_req, res) => {
|
|
7423
7494
|
const transport = new SSEServerTransport("/messages", res);
|
|
7424
7495
|
const { server, refreshTools, registerProxiedTools } = createMcpServer(state, integrationManager);
|
|
7496
|
+
server.server.oninitialized = () => agentContact.record(server.server.getClientVersion());
|
|
7425
7497
|
const sessionId = transport.sessionId;
|
|
7426
7498
|
sseSessions.set(sessionId, transport);
|
|
7427
7499
|
activeSessions.set(sessionId, { refreshTools, registerProxiedTools });
|
|
@@ -7463,6 +7535,7 @@ app.all("/mcp", guardAgent, async (req, res) => {
|
|
|
7463
7535
|
}
|
|
7464
7536
|
};
|
|
7465
7537
|
const { server, refreshTools, registerProxiedTools } = createMcpServer(state, integrationManager);
|
|
7538
|
+
server.server.oninitialized = () => agentContact.record(server.server.getClientVersion());
|
|
7466
7539
|
await server.connect(transport);
|
|
7467
7540
|
await transport.handleRequest(req, res, req.body);
|
|
7468
7541
|
if (transport.sessionId && !streamableSessions.has(transport.sessionId)) {
|
|
@@ -7493,6 +7566,9 @@ app.get("/health", (_req, res) => {
|
|
|
7493
7566
|
builtins: integrationManager.getBuiltins()
|
|
7494
7567
|
});
|
|
7495
7568
|
});
|
|
7569
|
+
app.get("/internal/agent-contact", internalOnly, (_req, res) => {
|
|
7570
|
+
res.json({ contact: agentContact.read() });
|
|
7571
|
+
});
|
|
7496
7572
|
app.get("/internal/tool-display", internalOnly, (_req, res) => {
|
|
7497
7573
|
res.json({ tools: integrationManager.getToolDisplay() });
|
|
7498
7574
|
});
|
|
@@ -7813,7 +7889,7 @@ app.listen(port, bindHost, () => {
|
|
|
7813
7889
|
(proposal) => executeCommitted(proposal, state, integrationManager)
|
|
7814
7890
|
);
|
|
7815
7891
|
installCommittedExecutor(committedExecutor);
|
|
7816
|
-
const dataDir2 = process.env.SUVEREN_DATA_DIR ??
|
|
7892
|
+
const dataDir2 = process.env.SUVEREN_DATA_DIR ?? join20(homedir11(), ".suveren");
|
|
7817
7893
|
const executorLock = new ExecutorLock(dataDir2);
|
|
7818
7894
|
const lock = executorLock.acquire();
|
|
7819
7895
|
if (!lock.held) {
|