@suveren/gateway 0.19.1 → 0.19.3
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/bin/suveren-gateway.js +4 -0
- package/content/integrations/erp.json +2 -1
- package/dist/control-plane/index.mjs +17 -2
- package/dist/mcp-server/http.mjs +288 -100
- package/dist/ui/assets/index-CmPQVcac.js +116 -0
- package/dist/ui/assets/{index-kObsFuwK.css → index-CxjHVIsT.css} +1 -1
- package/dist/ui/index.html +2 -2
- package/node_modules/@hono/node-server/package.json +2 -2
- package/package.json +4 -4
- package/dist/ui/assets/index-DF3kB2R3.js +0 -111
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}`);
|
|
@@ -5192,9 +5247,63 @@ function safeHost(url) {
|
|
|
5192
5247
|
}
|
|
5193
5248
|
}
|
|
5194
5249
|
|
|
5250
|
+
// src/lib/connector-env.ts
|
|
5251
|
+
var RUNTIME_VARS = [
|
|
5252
|
+
// POSIX
|
|
5253
|
+
"HOME",
|
|
5254
|
+
"USER",
|
|
5255
|
+
"LOGNAME",
|
|
5256
|
+
"SHELL",
|
|
5257
|
+
"TERM",
|
|
5258
|
+
"TMPDIR",
|
|
5259
|
+
"LANG",
|
|
5260
|
+
"LC_ALL",
|
|
5261
|
+
"LC_CTYPE",
|
|
5262
|
+
"TZ",
|
|
5263
|
+
"XDG_CONFIG_HOME",
|
|
5264
|
+
"XDG_DATA_HOME",
|
|
5265
|
+
"XDG_CACHE_HOME",
|
|
5266
|
+
"XDG_RUNTIME_DIR",
|
|
5267
|
+
// Windows (names are case-insensitive there; matched case-insensitively below)
|
|
5268
|
+
"APPDATA",
|
|
5269
|
+
"LOCALAPPDATA",
|
|
5270
|
+
"USERPROFILE",
|
|
5271
|
+
"USERNAME",
|
|
5272
|
+
"HOMEDRIVE",
|
|
5273
|
+
"HOMEPATH",
|
|
5274
|
+
"SYSTEMROOT",
|
|
5275
|
+
"SYSTEMDRIVE",
|
|
5276
|
+
"WINDIR",
|
|
5277
|
+
"COMSPEC",
|
|
5278
|
+
"PATHEXT",
|
|
5279
|
+
"TEMP",
|
|
5280
|
+
"TMP",
|
|
5281
|
+
"PROGRAMFILES",
|
|
5282
|
+
"PROGRAMFILES(X86)",
|
|
5283
|
+
"PROGRAMDATA",
|
|
5284
|
+
"PROCESSOR_ARCHITECTURE",
|
|
5285
|
+
"NUMBER_OF_PROCESSORS"
|
|
5286
|
+
];
|
|
5287
|
+
var NETWORK_VARS = [
|
|
5288
|
+
"HTTP_PROXY",
|
|
5289
|
+
"HTTPS_PROXY",
|
|
5290
|
+
"NO_PROXY",
|
|
5291
|
+
"NODE_EXTRA_CA_CERTS",
|
|
5292
|
+
"SSL_CERT_FILE"
|
|
5293
|
+
];
|
|
5294
|
+
function inheritedConnectorEnv(parent, passEnv = []) {
|
|
5295
|
+
const allowed = new Set([...RUNTIME_VARS, ...NETWORK_VARS, ...passEnv].map((n) => n.toUpperCase()));
|
|
5296
|
+
const out = {};
|
|
5297
|
+
for (const [name, value] of Object.entries(parent)) {
|
|
5298
|
+
if (value === void 0) continue;
|
|
5299
|
+
if (allowed.has(name.toUpperCase())) out[name] = value;
|
|
5300
|
+
}
|
|
5301
|
+
return out;
|
|
5302
|
+
}
|
|
5303
|
+
|
|
5195
5304
|
// src/lib/integration-manager.ts
|
|
5196
|
-
var DEFAULT_DATA_DIR2 = process.env.SUVEREN_DATA_DIR ??
|
|
5197
|
-
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");
|
|
5198
5307
|
var OFFLINE_MODE = process.env.SUVEREN_OFFLINE === "1";
|
|
5199
5308
|
var installLock = Promise.resolve();
|
|
5200
5309
|
function withInstallLock(task) {
|
|
@@ -5202,7 +5311,7 @@ function withInstallLock(task) {
|
|
|
5202
5311
|
installLock = run.then(() => void 0, () => void 0);
|
|
5203
5312
|
return run;
|
|
5204
5313
|
}
|
|
5205
|
-
var INTEGRATIONS_BIN =
|
|
5314
|
+
var INTEGRATIONS_BIN = join15(INTEGRATIONS_DIR, "node_modules", ".bin");
|
|
5206
5315
|
function getIntegrationsBinDir() {
|
|
5207
5316
|
return INTEGRATIONS_BIN;
|
|
5208
5317
|
}
|
|
@@ -5211,12 +5320,12 @@ function buildPath() {
|
|
|
5211
5320
|
return [INTEGRATIONS_BIN, base].join(delimiter);
|
|
5212
5321
|
}
|
|
5213
5322
|
function ensureIntegrationsDir() {
|
|
5214
|
-
if (!
|
|
5215
|
-
|
|
5323
|
+
if (!existsSync15(INTEGRATIONS_DIR)) {
|
|
5324
|
+
mkdirSync12(INTEGRATIONS_DIR, { recursive: true });
|
|
5216
5325
|
}
|
|
5217
|
-
const pkgPath =
|
|
5218
|
-
if (!
|
|
5219
|
-
|
|
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));
|
|
5220
5329
|
}
|
|
5221
5330
|
}
|
|
5222
5331
|
function readAgeOf(config) {
|
|
@@ -5305,19 +5414,19 @@ var IntegrationManager = class {
|
|
|
5305
5414
|
* exist, and treat anything else as not-installed (and reinstallable).
|
|
5306
5415
|
*/
|
|
5307
5416
|
isUsableInstall(npmPackage) {
|
|
5308
|
-
const pkgDir =
|
|
5309
|
-
const pkgJson =
|
|
5310
|
-
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;
|
|
5311
5420
|
try {
|
|
5312
|
-
const pkg = JSON.parse(
|
|
5421
|
+
const pkg = JSON.parse(readFileSync16(pkgJson, "utf8"));
|
|
5313
5422
|
const defaultBinName = (pkg.name ?? npmPackage).split("/").pop() ?? "";
|
|
5314
5423
|
const binMap = typeof pkg.bin === "string" ? { [defaultBinName]: pkg.bin } : pkg.bin ?? {};
|
|
5315
5424
|
const binEntries = Object.entries(binMap);
|
|
5316
5425
|
for (const [binName, target] of binEntries) {
|
|
5317
|
-
if (!
|
|
5426
|
+
if (!existsSync15(join15(pkgDir, target))) return false;
|
|
5318
5427
|
if (!this.binShimExists(binName)) return false;
|
|
5319
5428
|
}
|
|
5320
|
-
if (binEntries.length === 0 && pkg.main && !
|
|
5429
|
+
if (binEntries.length === 0 && pkg.main && !existsSync15(join15(pkgDir, pkg.main))) return false;
|
|
5321
5430
|
return true;
|
|
5322
5431
|
} catch {
|
|
5323
5432
|
return false;
|
|
@@ -5332,9 +5441,9 @@ var IntegrationManager = class {
|
|
|
5332
5441
|
*/
|
|
5333
5442
|
readInstalledVersion(npmPackage) {
|
|
5334
5443
|
if (!this.isUsableInstall(npmPackage)) return null;
|
|
5335
|
-
const pkgJson =
|
|
5444
|
+
const pkgJson = join15(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"), "package.json");
|
|
5336
5445
|
try {
|
|
5337
|
-
const pkg = JSON.parse(
|
|
5446
|
+
const pkg = JSON.parse(readFileSync16(pkgJson, "utf8"));
|
|
5338
5447
|
return typeof pkg.version === "string" ? pkg.version : null;
|
|
5339
5448
|
} catch {
|
|
5340
5449
|
return null;
|
|
@@ -5348,7 +5457,7 @@ var IntegrationManager = class {
|
|
|
5348
5457
|
binShimExists(binName) {
|
|
5349
5458
|
if (!binName) return false;
|
|
5350
5459
|
const candidates = process.platform === "win32" ? [binName, `${binName}.cmd`, `${binName}.CMD`, `${binName}.ps1`] : [binName];
|
|
5351
|
-
return candidates.some((c) =>
|
|
5460
|
+
return candidates.some((c) => existsSync15(join15(INTEGRATIONS_BIN, c)));
|
|
5352
5461
|
}
|
|
5353
5462
|
/**
|
|
5354
5463
|
* The exact version THIS gateway's CURRENTLY LOADED manifest pins
|
|
@@ -5438,8 +5547,8 @@ var IntegrationManager = class {
|
|
|
5438
5547
|
* nothing at all.
|
|
5439
5548
|
*/
|
|
5440
5549
|
async installNow(npmPackage, exactVersion, cleanFirst = true) {
|
|
5441
|
-
const pkgDir =
|
|
5442
|
-
if (cleanFirst &&
|
|
5550
|
+
const pkgDir = join15(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"));
|
|
5551
|
+
if (cleanFirst && existsSync15(pkgDir)) {
|
|
5443
5552
|
console.error(`[IntegrationManager] Removing ${exactVersion ? "outdated" : "unusable"} install of ${npmPackage}`);
|
|
5444
5553
|
try {
|
|
5445
5554
|
rmSync(pkgDir, { recursive: true, force: true });
|
|
@@ -5585,7 +5694,8 @@ var IntegrationManager = class {
|
|
|
5585
5694
|
if (envVar) env[envVar] = "simulation";
|
|
5586
5695
|
}
|
|
5587
5696
|
}
|
|
5588
|
-
const
|
|
5697
|
+
const inherited = inheritedConnectorEnv(process.env, getManifest(config.id)?.mcp.passEnv);
|
|
5698
|
+
const interpolate = (s) => s.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_m, name) => env[name] ?? inherited[name] ?? "");
|
|
5589
5699
|
const args = config.args.map(interpolate);
|
|
5590
5700
|
const remote = remotePreflightTarget(config.command, args);
|
|
5591
5701
|
if (remote) {
|
|
@@ -5595,13 +5705,13 @@ var IntegrationManager = class {
|
|
|
5595
5705
|
const transport = new StdioClientTransport({
|
|
5596
5706
|
command: config.command,
|
|
5597
5707
|
args,
|
|
5598
|
-
//
|
|
5599
|
-
//
|
|
5600
|
-
//
|
|
5601
|
-
//
|
|
5602
|
-
//
|
|
5708
|
+
// Only an allowlist of the gateway's environment — never its own
|
|
5709
|
+
// secrets (connector-env.ts). The allowlist includes
|
|
5710
|
+
// NODE_EXTRA_CA_CERTS, which is how a saved `--ca-file` reaches an
|
|
5711
|
+
// integration: bundle/server.js sets it on THIS process before the MCP
|
|
5712
|
+
// server starts, and Node reads it only at process start.
|
|
5603
5713
|
env: {
|
|
5604
|
-
...
|
|
5714
|
+
...inherited,
|
|
5605
5715
|
PATH: buildPath(),
|
|
5606
5716
|
HAP_DATA_DIR: DEFAULT_DATA_DIR2,
|
|
5607
5717
|
...config.env,
|
|
@@ -6275,29 +6385,96 @@ async function scopeReportSourcesToStoredWindow(base, stored, opts) {
|
|
|
6275
6385
|
return { ok: true, window, sources: scopedSources(base, window, loadedAt) };
|
|
6276
6386
|
}
|
|
6277
6387
|
|
|
6388
|
+
// src/lib/agent-access.ts
|
|
6389
|
+
import { timingSafeEqual } from "crypto";
|
|
6390
|
+
var DEFAULT_BIND_HOST = "127.0.0.1";
|
|
6391
|
+
function resolveBindHost(env = process.env) {
|
|
6392
|
+
const raw = env.SUVEREN_BIND_HOST?.trim();
|
|
6393
|
+
return raw ? raw : DEFAULT_BIND_HOST;
|
|
6394
|
+
}
|
|
6395
|
+
function isLoopbackBind(host) {
|
|
6396
|
+
const h = host.replace(/^\[|\]$/g, "").toLowerCase();
|
|
6397
|
+
return h === "localhost" || h === "::1" || /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(h);
|
|
6398
|
+
}
|
|
6399
|
+
function resolveAgentToken(env = process.env) {
|
|
6400
|
+
return env.SUVEREN_MCP_TOKEN?.trim() ?? "";
|
|
6401
|
+
}
|
|
6402
|
+
function bindRefusal(env = process.env) {
|
|
6403
|
+
const host = resolveBindHost(env);
|
|
6404
|
+
if (isLoopbackBind(host)) return null;
|
|
6405
|
+
if (resolveAgentToken(env)) return null;
|
|
6406
|
+
if (env.SUVEREN_CONTAINER === "1") return null;
|
|
6407
|
+
return `SUVEREN_BIND_HOST=${host} would let other machines connect as this person's agent, but SUVEREN_MCP_TOKEN is not set. Set a token (long and random), or remove SUVEREN_BIND_HOST to listen on this machine only.`;
|
|
6408
|
+
}
|
|
6409
|
+
function hostnameOf(hostHeader) {
|
|
6410
|
+
const trimmed = hostHeader.trim().toLowerCase();
|
|
6411
|
+
const bracketed = trimmed.match(/^\[([^\]]+)\](?::\d+)?$/);
|
|
6412
|
+
if (bracketed) return bracketed[1];
|
|
6413
|
+
return trimmed.replace(/:\d+$/, "");
|
|
6414
|
+
}
|
|
6415
|
+
function isLocalHost(hostHeader) {
|
|
6416
|
+
if (!hostHeader) return false;
|
|
6417
|
+
const name = hostnameOf(hostHeader);
|
|
6418
|
+
if (name === "localhost" || name.endsWith(".localhost")) return true;
|
|
6419
|
+
if (/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(name)) return true;
|
|
6420
|
+
if (name.includes(":")) return true;
|
|
6421
|
+
return false;
|
|
6422
|
+
}
|
|
6423
|
+
function presentedToken(req) {
|
|
6424
|
+
const auth = req.headers.authorization;
|
|
6425
|
+
if (typeof auth === "string") {
|
|
6426
|
+
const m = auth.match(/^Bearer\s+(.+)$/i);
|
|
6427
|
+
if (m) return m[1].trim();
|
|
6428
|
+
}
|
|
6429
|
+
const q = req.query.token;
|
|
6430
|
+
return typeof q === "string" ? q : "";
|
|
6431
|
+
}
|
|
6432
|
+
function sameToken(a, b) {
|
|
6433
|
+
const ab = Buffer.from(a);
|
|
6434
|
+
const bb = Buffer.from(b);
|
|
6435
|
+
return ab.length === bb.length && timingSafeEqual(ab, bb);
|
|
6436
|
+
}
|
|
6437
|
+
function agentAccess(token, opensSession) {
|
|
6438
|
+
return (req, res, next) => {
|
|
6439
|
+
if (!token) {
|
|
6440
|
+
if (!isLocalHost(req.headers.host)) {
|
|
6441
|
+
res.status(403).json({ error: "Forbidden host" });
|
|
6442
|
+
return;
|
|
6443
|
+
}
|
|
6444
|
+
next();
|
|
6445
|
+
return;
|
|
6446
|
+
}
|
|
6447
|
+
if (opensSession(req) && !sameToken(presentedToken(req), token)) {
|
|
6448
|
+
res.status(401).json({ error: "Missing or wrong gateway token" });
|
|
6449
|
+
return;
|
|
6450
|
+
}
|
|
6451
|
+
next();
|
|
6452
|
+
};
|
|
6453
|
+
}
|
|
6454
|
+
|
|
6278
6455
|
// src/lib/profile-loader.ts
|
|
6279
|
-
import { readFileSync as
|
|
6280
|
-
import { join as
|
|
6456
|
+
import { readFileSync as readFileSync17, existsSync as existsSync16 } from "fs";
|
|
6457
|
+
import { join as join16, resolve as resolve2 } from "path";
|
|
6281
6458
|
import { registerProfile } from "@hap/core";
|
|
6282
6459
|
function loadProfiles(profilesDir) {
|
|
6283
|
-
const dir = resolve2(profilesDir ?? process.env.SUVEREN_PROFILES_DIR ??
|
|
6284
|
-
const indexPath =
|
|
6285
|
-
if (!
|
|
6460
|
+
const dir = resolve2(profilesDir ?? process.env.SUVEREN_PROFILES_DIR ?? join16(import.meta.dirname ?? __dirname, "..", "..", "..", "..", "..", "hap-profiles"));
|
|
6461
|
+
const indexPath = join16(dir, "index.json");
|
|
6462
|
+
if (!existsSync16(indexPath)) {
|
|
6286
6463
|
warnNoProfiles(`No index.json at ${indexPath}`);
|
|
6287
6464
|
return 0;
|
|
6288
6465
|
}
|
|
6289
6466
|
let index;
|
|
6290
6467
|
try {
|
|
6291
|
-
index = JSON.parse(
|
|
6468
|
+
index = JSON.parse(readFileSync17(indexPath, "utf-8"));
|
|
6292
6469
|
} catch (err) {
|
|
6293
6470
|
console.error(`[ProfileLoader] Failed to parse ${indexPath}:`, err);
|
|
6294
6471
|
return 0;
|
|
6295
6472
|
}
|
|
6296
6473
|
let loaded = 0;
|
|
6297
6474
|
for (const [profileId, relativePath] of Object.entries(index.profiles)) {
|
|
6298
|
-
const profilePath =
|
|
6475
|
+
const profilePath = join16(dir, relativePath);
|
|
6299
6476
|
try {
|
|
6300
|
-
const profile = JSON.parse(
|
|
6477
|
+
const profile = JSON.parse(readFileSync17(profilePath, "utf-8"));
|
|
6301
6478
|
registerProfile(profileId, profile);
|
|
6302
6479
|
const shortName = profileId.split("/").pop()?.replace(/@.*$/, "");
|
|
6303
6480
|
if (shortName && shortName !== profileId) {
|
|
@@ -6635,20 +6812,20 @@ async function controlPlaneMandate(dryRun, request) {
|
|
|
6635
6812
|
}
|
|
6636
6813
|
|
|
6637
6814
|
// src/lib/guides.ts
|
|
6638
|
-
import { existsSync as
|
|
6639
|
-
import { dirname as
|
|
6815
|
+
import { existsSync as existsSync17, readdirSync, readFileSync as readFileSync18 } from "fs";
|
|
6816
|
+
import { dirname as dirname12, join as join17 } from "path";
|
|
6640
6817
|
var FILE_RE = /^(?:(\d+)-)?([a-z0-9][a-z0-9-]*)\.md$/;
|
|
6641
6818
|
function builtinGuidesDir() {
|
|
6642
6819
|
if (process.env.SUVEREN_BUILTIN_GUIDES_DIR) return process.env.SUVEREN_BUILTIN_GUIDES_DIR;
|
|
6643
6820
|
let dir = import.meta.dirname ?? __dirname;
|
|
6644
6821
|
for (let i = 0; i < 8; i++) {
|
|
6645
|
-
const candidate =
|
|
6646
|
-
if (
|
|
6647
|
-
const parent =
|
|
6822
|
+
const candidate = join17(dir, "content", "guides");
|
|
6823
|
+
if (existsSync17(candidate)) return candidate;
|
|
6824
|
+
const parent = dirname12(dir);
|
|
6648
6825
|
if (parent === dir) break;
|
|
6649
6826
|
dir = parent;
|
|
6650
6827
|
}
|
|
6651
|
-
return
|
|
6828
|
+
return join17(import.meta.dirname ?? __dirname, "content", "guides");
|
|
6652
6829
|
}
|
|
6653
6830
|
function parse(body) {
|
|
6654
6831
|
const lines = body.split(/\r?\n/);
|
|
@@ -6659,14 +6836,14 @@ function parse(body) {
|
|
|
6659
6836
|
return { title, summary };
|
|
6660
6837
|
}
|
|
6661
6838
|
function readDir(dir, source) {
|
|
6662
|
-
if (!dir || !
|
|
6839
|
+
if (!dir || !existsSync17(dir)) return [];
|
|
6663
6840
|
const out = [];
|
|
6664
6841
|
for (const file of readdirSync(dir)) {
|
|
6665
6842
|
const m = FILE_RE.exec(file);
|
|
6666
6843
|
if (!m) continue;
|
|
6667
6844
|
let body;
|
|
6668
6845
|
try {
|
|
6669
|
-
body =
|
|
6846
|
+
body = readFileSync18(join17(dir, file), "utf8");
|
|
6670
6847
|
} catch {
|
|
6671
6848
|
continue;
|
|
6672
6849
|
}
|
|
@@ -6702,6 +6879,8 @@ function guideHeader(systems) {
|
|
|
6702
6879
|
lines.push(`- **${s.id}** \u2014 ${parts.join("; ")}`);
|
|
6703
6880
|
}
|
|
6704
6881
|
}
|
|
6882
|
+
lines.push("");
|
|
6883
|
+
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.`);
|
|
6705
6884
|
return lines.join("\n");
|
|
6706
6885
|
}
|
|
6707
6886
|
|
|
@@ -6732,7 +6911,11 @@ function connectedSystems(im) {
|
|
|
6732
6911
|
function setupBuiltin(deps) {
|
|
6733
6912
|
return {
|
|
6734
6913
|
id: "setup",
|
|
6735
|
-
|
|
6914
|
+
// Shown as the picker card title (apps/ui AuthorizePicker) — the person
|
|
6915
|
+
// reads this as the mandate they're giving, so it must match the profile
|
|
6916
|
+
// name they'll see everywhere else (Mandates page, dashboard first-run
|
|
6917
|
+
// card), not the internal "test setup" framing. Description is unchanged.
|
|
6918
|
+
name: "Delegation",
|
|
6736
6919
|
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.",
|
|
6737
6920
|
profile: DELEGATION_PROFILE,
|
|
6738
6921
|
simulation: true,
|
|
@@ -6771,7 +6954,7 @@ function setupBuiltin(deps) {
|
|
|
6771
6954
|
tools: [
|
|
6772
6955
|
{
|
|
6773
6956
|
name: "get_guide",
|
|
6774
|
-
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.",
|
|
6957
|
+
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.",
|
|
6775
6958
|
inputSchema: {
|
|
6776
6959
|
type: "object",
|
|
6777
6960
|
properties: {
|
|
@@ -6898,8 +7081,8 @@ function registerBuiltins(deps, factories = BUILTIN_FACTORIES) {
|
|
|
6898
7081
|
import { decodeAttestationBlob as decodeAttestationBlob3 } from "@hap/core";
|
|
6899
7082
|
|
|
6900
7083
|
// src/lib/committed-executor.ts
|
|
6901
|
-
import { existsSync as
|
|
6902
|
-
import { dirname as
|
|
7084
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync13, readFileSync as readFileSync19, unlinkSync as unlinkSync6, writeFileSync as writeFileSync13 } from "fs";
|
|
7085
|
+
import { dirname as dirname13, join as join18 } from "path";
|
|
6903
7086
|
var CommittedExecutor = class {
|
|
6904
7087
|
constructor(run) {
|
|
6905
7088
|
this.run = run;
|
|
@@ -6927,7 +7110,7 @@ var ExecutorLock = class {
|
|
|
6927
7110
|
path;
|
|
6928
7111
|
held = false;
|
|
6929
7112
|
constructor(dataDir2) {
|
|
6930
|
-
this.path =
|
|
7113
|
+
this.path = join18(dataDir2, "executor.lock");
|
|
6931
7114
|
}
|
|
6932
7115
|
/** Try to take the lock. Returns the holder's pid when someone else has it. */
|
|
6933
7116
|
acquire() {
|
|
@@ -6935,9 +7118,9 @@ var ExecutorLock = class {
|
|
|
6935
7118
|
if (current && current.pid !== process.pid && isAlive(current.pid)) {
|
|
6936
7119
|
return { held: false, holderPid: current.pid };
|
|
6937
7120
|
}
|
|
6938
|
-
|
|
7121
|
+
mkdirSync13(dirname13(this.path), { recursive: true, mode: 448 });
|
|
6939
7122
|
const lock = { pid: process.pid, startedAt: Math.floor(Date.now() / 1e3) };
|
|
6940
|
-
|
|
7123
|
+
writeFileSync13(this.path, JSON.stringify(lock), { mode: 384 });
|
|
6941
7124
|
this.held = true;
|
|
6942
7125
|
return { held: true };
|
|
6943
7126
|
}
|
|
@@ -6959,9 +7142,9 @@ var ExecutorLock = class {
|
|
|
6959
7142
|
}
|
|
6960
7143
|
}
|
|
6961
7144
|
read() {
|
|
6962
|
-
if (!
|
|
7145
|
+
if (!existsSync18(this.path)) return null;
|
|
6963
7146
|
try {
|
|
6964
|
-
const parsed = JSON.parse(
|
|
7147
|
+
const parsed = JSON.parse(readFileSync19(this.path, "utf8"));
|
|
6965
7148
|
return typeof parsed?.pid === "number" ? parsed : null;
|
|
6966
7149
|
} catch {
|
|
6967
7150
|
return null;
|
|
@@ -6979,19 +7162,19 @@ function isAlive(pid) {
|
|
|
6979
7162
|
|
|
6980
7163
|
// bin/http.ts
|
|
6981
7164
|
import { homedir as homedir11 } from "os";
|
|
6982
|
-
import { join as
|
|
7165
|
+
import { join as join20 } from "path";
|
|
6983
7166
|
|
|
6984
7167
|
// src/lib/mcp-as-tracker.ts
|
|
6985
|
-
import { existsSync as
|
|
6986
|
-
import { dirname as
|
|
7168
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync14, readFileSync as readFileSync20, writeFileSync as writeFileSync14 } from "fs";
|
|
7169
|
+
import { dirname as dirname14, join as join19 } from "path";
|
|
6987
7170
|
function trackerPath(dataDir2) {
|
|
6988
|
-
return
|
|
7171
|
+
return join19(dataDir2, "mcp-as-pairing.json");
|
|
6989
7172
|
}
|
|
6990
7173
|
function readMcpPairedAsUrl(dataDir2) {
|
|
6991
7174
|
const path = trackerPath(dataDir2);
|
|
6992
|
-
if (!
|
|
7175
|
+
if (!existsSync19(path)) return null;
|
|
6993
7176
|
try {
|
|
6994
|
-
const data = JSON.parse(
|
|
7177
|
+
const data = JSON.parse(readFileSync20(path, "utf-8"));
|
|
6995
7178
|
return typeof data.asUrl === "string" ? data.asUrl : null;
|
|
6996
7179
|
} catch {
|
|
6997
7180
|
return null;
|
|
@@ -6999,17 +7182,25 @@ function readMcpPairedAsUrl(dataDir2) {
|
|
|
6999
7182
|
}
|
|
7000
7183
|
function writeMcpPairedAsUrl(dataDir2, asUrl) {
|
|
7001
7184
|
const path = trackerPath(dataDir2);
|
|
7002
|
-
|
|
7003
|
-
|
|
7185
|
+
mkdirSync14(dirname14(path), { recursive: true, mode: 448 });
|
|
7186
|
+
writeFileSync14(path, JSON.stringify({ asUrl }, null, 2), { encoding: "utf-8", mode: 384 });
|
|
7004
7187
|
}
|
|
7005
7188
|
|
|
7006
7189
|
// bin/http.ts
|
|
7007
|
-
var dataDir = process.env.SUVEREN_DATA_DIR ??
|
|
7190
|
+
var dataDir = process.env.SUVEREN_DATA_DIR ?? join20(homedir11(), ".suveren");
|
|
7008
7191
|
var spUrl = resolveAsUrl(dataDir);
|
|
7009
7192
|
validatePinTlsForUrl(resolvePinTls(dataDir), spUrl);
|
|
7010
7193
|
var port = parseInt(process.env.SUVEREN_MCP_PORT ?? "3430", 10);
|
|
7194
|
+
var bindHost = resolveBindHost();
|
|
7195
|
+
var agentToken = resolveAgentToken();
|
|
7196
|
+
var bindProblem = bindRefusal();
|
|
7197
|
+
if (bindProblem) {
|
|
7198
|
+
console.error(`[Suveren MCP] Refusing to start: ${bindProblem}`);
|
|
7199
|
+
process.exit(1);
|
|
7200
|
+
}
|
|
7011
7201
|
setAsBaseUrl(spUrl);
|
|
7012
7202
|
var state = new SharedState(spUrl, void 0, dataDir);
|
|
7203
|
+
var agentContact = new AgentContactStore(dataDir);
|
|
7013
7204
|
var reportSources = {
|
|
7014
7205
|
archive: state.receiptArchive,
|
|
7015
7206
|
runExport: createConnectorExportRunner({ integrationsBinDir: getIntegrationsBinDir(), dataDir })
|
|
@@ -7051,16 +7242,6 @@ integrationManager.setOnConfigMigrated((id, updates) => {
|
|
|
7051
7242
|
});
|
|
7052
7243
|
var app = express();
|
|
7053
7244
|
app.use(express.json());
|
|
7054
|
-
app.use((_req, res, next) => {
|
|
7055
|
-
res.header("Access-Control-Allow-Origin", "http://localhost:3000");
|
|
7056
|
-
res.header("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
|
|
7057
|
-
res.header("Access-Control-Allow-Headers", "Content-Type, mcp-session-id");
|
|
7058
|
-
if (_req.method === "OPTIONS") {
|
|
7059
|
-
res.sendStatus(204);
|
|
7060
|
-
return;
|
|
7061
|
-
}
|
|
7062
|
-
next();
|
|
7063
|
-
});
|
|
7064
7245
|
var INTERNAL_SECRET = process.env.SUVEREN_INTERNAL_SECRET ?? "";
|
|
7065
7246
|
function internalOnly(req, res, next) {
|
|
7066
7247
|
const ip = req.ip ?? req.socket.remoteAddress ?? "";
|
|
@@ -7296,9 +7477,14 @@ app.post("/internal/stop-all-running", internalOnly, async (_req, res) => {
|
|
|
7296
7477
|
}
|
|
7297
7478
|
});
|
|
7298
7479
|
var sseSessions = /* @__PURE__ */ new Map();
|
|
7299
|
-
|
|
7480
|
+
var guardAgent = agentAccess(
|
|
7481
|
+
agentToken,
|
|
7482
|
+
(req) => req.path === "/sse" && req.method === "GET" || req.path === "/mcp" && req.method === "POST" && !req.headers["mcp-session-id"]
|
|
7483
|
+
);
|
|
7484
|
+
app.get("/sse", guardAgent, async (_req, res) => {
|
|
7300
7485
|
const transport = new SSEServerTransport("/messages", res);
|
|
7301
7486
|
const { server, refreshTools, registerProxiedTools } = createMcpServer(state, integrationManager);
|
|
7487
|
+
server.server.oninitialized = () => agentContact.record(server.server.getClientVersion());
|
|
7302
7488
|
const sessionId = transport.sessionId;
|
|
7303
7489
|
sseSessions.set(sessionId, transport);
|
|
7304
7490
|
activeSessions.set(sessionId, { refreshTools, registerProxiedTools });
|
|
@@ -7308,12 +7494,9 @@ app.get("/sse", async (_req, res) => {
|
|
|
7308
7494
|
activeSessions.delete(sessionId);
|
|
7309
7495
|
console.error(`[Suveren MCP] SSE session ${sessionId} closed`);
|
|
7310
7496
|
});
|
|
7311
|
-
server.registerTool("debug_test_tool", { description: "Debug test" }, async () => ({
|
|
7312
|
-
content: [{ type: "text", text: "debug" }]
|
|
7313
|
-
}));
|
|
7314
7497
|
await server.connect(transport);
|
|
7315
7498
|
});
|
|
7316
|
-
app.post("/messages", async (req, res) => {
|
|
7499
|
+
app.post("/messages", guardAgent, async (req, res) => {
|
|
7317
7500
|
const sessionId = req.query.sessionId;
|
|
7318
7501
|
const transport = sseSessions.get(sessionId);
|
|
7319
7502
|
if (!transport) {
|
|
@@ -7323,7 +7506,7 @@ app.post("/messages", async (req, res) => {
|
|
|
7323
7506
|
await transport.handlePostMessage(req, res, req.body);
|
|
7324
7507
|
});
|
|
7325
7508
|
var streamableSessions = /* @__PURE__ */ new Map();
|
|
7326
|
-
app.all("/mcp", async (req, res) => {
|
|
7509
|
+
app.all("/mcp", guardAgent, async (req, res) => {
|
|
7327
7510
|
const sessionId = req.headers["mcp-session-id"];
|
|
7328
7511
|
if (req.method === "GET" || req.method === "POST" || req.method === "DELETE") {
|
|
7329
7512
|
if (sessionId && streamableSessions.has(sessionId)) {
|
|
@@ -7343,6 +7526,7 @@ app.all("/mcp", async (req, res) => {
|
|
|
7343
7526
|
}
|
|
7344
7527
|
};
|
|
7345
7528
|
const { server, refreshTools, registerProxiedTools } = createMcpServer(state, integrationManager);
|
|
7529
|
+
server.server.oninitialized = () => agentContact.record(server.server.getClientVersion());
|
|
7346
7530
|
await server.connect(transport);
|
|
7347
7531
|
await transport.handleRequest(req, res, req.body);
|
|
7348
7532
|
if (transport.sessionId && !streamableSessions.has(transport.sessionId)) {
|
|
@@ -7373,6 +7557,9 @@ app.get("/health", (_req, res) => {
|
|
|
7373
7557
|
builtins: integrationManager.getBuiltins()
|
|
7374
7558
|
});
|
|
7375
7559
|
});
|
|
7560
|
+
app.get("/internal/agent-contact", internalOnly, (_req, res) => {
|
|
7561
|
+
res.json({ contact: agentContact.read() });
|
|
7562
|
+
});
|
|
7376
7563
|
app.get("/internal/tool-display", internalOnly, (_req, res) => {
|
|
7377
7564
|
res.json({ tools: integrationManager.getToolDisplay() });
|
|
7378
7565
|
});
|
|
@@ -7642,10 +7829,11 @@ process.on("SIGINT", async () => {
|
|
|
7642
7829
|
await integrationManager.shutdown();
|
|
7643
7830
|
process.exit(0);
|
|
7644
7831
|
});
|
|
7645
|
-
app.listen(port,
|
|
7646
|
-
console.error(`[Suveren MCP] HTTP server listening on http
|
|
7647
|
-
console.error(`[Suveren MCP] SSE: http
|
|
7648
|
-
console.error(`[Suveren MCP] Streamable: http
|
|
7832
|
+
app.listen(port, bindHost, () => {
|
|
7833
|
+
console.error(`[Suveren MCP] HTTP server listening on http://${bindHost}:${port}`);
|
|
7834
|
+
console.error(`[Suveren MCP] SSE: http://${bindHost}:${port}/sse`);
|
|
7835
|
+
console.error(`[Suveren MCP] Streamable: http://${bindHost}:${port}/mcp`);
|
|
7836
|
+
console.error(`[Suveren MCP] Agent token: ${agentToken ? "required" : "not set (this machine only)"}`);
|
|
7649
7837
|
console.error(`[Suveren MCP] SP server: ${spUrl}`);
|
|
7650
7838
|
profilesLoaded = loadProfiles();
|
|
7651
7839
|
loadManifests();
|
|
@@ -7692,7 +7880,7 @@ app.listen(port, "0.0.0.0", () => {
|
|
|
7692
7880
|
(proposal) => executeCommitted(proposal, state, integrationManager)
|
|
7693
7881
|
);
|
|
7694
7882
|
installCommittedExecutor(committedExecutor);
|
|
7695
|
-
const dataDir2 = process.env.SUVEREN_DATA_DIR ??
|
|
7883
|
+
const dataDir2 = process.env.SUVEREN_DATA_DIR ?? join20(homedir11(), ".suveren");
|
|
7696
7884
|
const executorLock = new ExecutorLock(dataDir2);
|
|
7697
7885
|
const lock = executorLock.acquire();
|
|
7698
7886
|
if (!lock.held) {
|