@suveren/gateway 0.16.1 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/content/guides/1-interview.md +31 -0
- package/content/guides/2-package.md +30 -0
- package/content/guides/3-mandates.md +34 -0
- package/content/guides/4-brief.md +30 -0
- package/content/guides/5-agent-process.md +39 -0
- package/content/guides/6-risks.md +14 -0
- package/dist/control-plane/index.mjs +272 -17
- package/dist/mcp-server/http.mjs +250 -23
- package/dist/ui/assets/index-BKgX8_Zh.js +111 -0
- package/dist/ui/index.html +1 -1
- package/package.json +1 -1
- package/server.js +3 -0
- package/dist/ui/assets/index-tdGbzBko.js +0 -110
package/dist/mcp-server/http.mjs
CHANGED
|
@@ -2984,7 +2984,7 @@ function createGatedToolHandler(tool, integrationManager2, state2) {
|
|
|
2984
2984
|
const gateable = !!tool.gating?.profile && tool.gating.category !== "disabled";
|
|
2985
2985
|
const inner = async (args) => {
|
|
2986
2986
|
const normalized = normalizeIncomingArgs(tool, args);
|
|
2987
|
-
const refusal = gateable ? integrationManager2.precheckBuiltin?.(tool, normalized) : null;
|
|
2987
|
+
const refusal = gateable ? await integrationManager2.precheckBuiltin?.(tool, normalized) : null;
|
|
2988
2988
|
if (refusal) return { content: [{ type: "text", text: refusal }], isError: true };
|
|
2989
2989
|
return gated(normalized);
|
|
2990
2990
|
};
|
|
@@ -5248,6 +5248,18 @@ var IntegrationManager = class {
|
|
|
5248
5248
|
available: !def.simulationOnly || isSimulationMode()
|
|
5249
5249
|
}));
|
|
5250
5250
|
}
|
|
5251
|
+
/**
|
|
5252
|
+
* What the approval screen needs to show a proposal for each tool: its argument
|
|
5253
|
+
* schema (labels from the descriptions) and the optional approvalView hints.
|
|
5254
|
+
* Keyed by the namespaced tool name a proposal carries.
|
|
5255
|
+
*/
|
|
5256
|
+
getToolDisplay() {
|
|
5257
|
+
const out = {};
|
|
5258
|
+
for (const t of this.getAllTools()) {
|
|
5259
|
+
out[t.namespacedName] = { inputSchema: t.inputSchema, ...t.gating?.approvalView ? { approvalView: t.gating.approvalView } : {} };
|
|
5260
|
+
}
|
|
5261
|
+
return out;
|
|
5262
|
+
}
|
|
5251
5263
|
/** Whether `id` names a built-in (in-process) integration. */
|
|
5252
5264
|
isBuiltin(id) {
|
|
5253
5265
|
return this.builtins.has(id);
|
|
@@ -5266,14 +5278,14 @@ var IntegrationManager = class {
|
|
|
5266
5278
|
* no ticket): `simulationOnly` outside simulation mode, then the tool's `validate`.
|
|
5267
5279
|
* Null for connector tools and for calls that may proceed.
|
|
5268
5280
|
*/
|
|
5269
|
-
precheckBuiltin(tool, args) {
|
|
5281
|
+
async precheckBuiltin(tool, args) {
|
|
5270
5282
|
const builtin = this.builtins.get(tool.integrationId);
|
|
5271
5283
|
if (!builtin) return null;
|
|
5272
5284
|
if (builtin.def.simulationOnly && !isSimulationMode()) {
|
|
5273
5285
|
return `Refused: "${tool.namespacedName}" is not available. No ticket was requested.`;
|
|
5274
5286
|
}
|
|
5275
5287
|
const def = builtin.def.tools.find((t) => t.name === tool.originalName);
|
|
5276
|
-
const refusal = def?.validate?.(args);
|
|
5288
|
+
const refusal = await def?.validate?.(args);
|
|
5277
5289
|
return refusal ? `Refused: ${refusal} No ticket was requested.` : null;
|
|
5278
5290
|
}
|
|
5279
5291
|
/**
|
|
@@ -5484,7 +5496,8 @@ var IntegrationManager = class {
|
|
|
5484
5496
|
readGovernance: ext.readGovernance,
|
|
5485
5497
|
readGovernanceReason: ext.readGovernanceReason,
|
|
5486
5498
|
blockedArgs: ext.blockedArgs,
|
|
5487
|
-
hideUnlessAuthorized: ext.hideUnlessAuthorized
|
|
5499
|
+
hideUnlessAuthorized: ext.hideUnlessAuthorized,
|
|
5500
|
+
approvalView: ext.approvalView
|
|
5488
5501
|
};
|
|
5489
5502
|
}
|
|
5490
5503
|
return {
|
|
@@ -5499,7 +5512,8 @@ var IntegrationManager = class {
|
|
|
5499
5512
|
blockedArgs: ext.blockedArgs,
|
|
5500
5513
|
argEncoding: ext.argEncoding,
|
|
5501
5514
|
argNormalization: ext.argNormalization,
|
|
5502
|
-
hideUnlessAuthorized: ext.hideUnlessAuthorized
|
|
5515
|
+
hideUnlessAuthorized: ext.hideUnlessAuthorized,
|
|
5516
|
+
approvalView: ext.approvalView
|
|
5503
5517
|
};
|
|
5504
5518
|
}
|
|
5505
5519
|
return {
|
|
@@ -5606,6 +5620,98 @@ function builtinText(text) {
|
|
|
5606
5620
|
return { content: [{ type: "text", text }] };
|
|
5607
5621
|
}
|
|
5608
5622
|
|
|
5623
|
+
// src/lib/cp-mandate.ts
|
|
5624
|
+
var CP_PORT2 = process.env.SUVEREN_CP_PORT ?? "3402";
|
|
5625
|
+
var CP_BASE2 = process.env.SUVEREN_CP_INTERNAL_URL ?? `http://127.0.0.1:${CP_PORT2}`;
|
|
5626
|
+
async function controlPlaneMandate(dryRun, request) {
|
|
5627
|
+
const secret = process.env.SUVEREN_INTERNAL_SECRET ?? "";
|
|
5628
|
+
if (!secret) return { ok: false, message: "the gateway is not fully started (no control plane connection)." };
|
|
5629
|
+
try {
|
|
5630
|
+
const res = await fetch(`${CP_BASE2}/internal/mandate`, {
|
|
5631
|
+
method: "POST",
|
|
5632
|
+
headers: { "Content-Type": "application/json", "X-Internal-Secret": secret },
|
|
5633
|
+
body: JSON.stringify({ dryRun, request }),
|
|
5634
|
+
signal: AbortSignal.timeout(3e4)
|
|
5635
|
+
});
|
|
5636
|
+
const body = await res.json().catch(() => ({}));
|
|
5637
|
+
if (res.ok) return { ok: true, ...body };
|
|
5638
|
+
return { ok: false, message: typeof body.message === "string" ? body.message : `control plane answered ${res.status}` };
|
|
5639
|
+
} catch (err) {
|
|
5640
|
+
return { ok: false, message: `the control plane could not be reached (${err instanceof Error ? err.message : String(err)}).` };
|
|
5641
|
+
}
|
|
5642
|
+
}
|
|
5643
|
+
|
|
5644
|
+
// src/lib/guides.ts
|
|
5645
|
+
import { existsSync as existsSync16, readdirSync, readFileSync as readFileSync17 } from "fs";
|
|
5646
|
+
import { dirname as dirname11, join as join16 } from "path";
|
|
5647
|
+
var FILE_RE = /^(?:(\d+)-)?([a-z0-9][a-z0-9-]*)\.md$/;
|
|
5648
|
+
function builtinGuidesDir() {
|
|
5649
|
+
if (process.env.SUVEREN_BUILTIN_GUIDES_DIR) return process.env.SUVEREN_BUILTIN_GUIDES_DIR;
|
|
5650
|
+
let dir = import.meta.dirname ?? __dirname;
|
|
5651
|
+
for (let i = 0; i < 8; i++) {
|
|
5652
|
+
const candidate = join16(dir, "content", "guides");
|
|
5653
|
+
if (existsSync16(candidate)) return candidate;
|
|
5654
|
+
const parent = dirname11(dir);
|
|
5655
|
+
if (parent === dir) break;
|
|
5656
|
+
dir = parent;
|
|
5657
|
+
}
|
|
5658
|
+
return join16(import.meta.dirname ?? __dirname, "content", "guides");
|
|
5659
|
+
}
|
|
5660
|
+
function parse(body) {
|
|
5661
|
+
const lines = body.split(/\r?\n/);
|
|
5662
|
+
const titleLine = lines.find((l) => l.startsWith("# ")) ?? "";
|
|
5663
|
+
const title = titleLine.slice(2).trim();
|
|
5664
|
+
const after = lines.slice(lines.indexOf(titleLine) + 1);
|
|
5665
|
+
const summary = after.find((l) => l.trim() !== "" && !l.startsWith("#"))?.trim() ?? "";
|
|
5666
|
+
return { title, summary };
|
|
5667
|
+
}
|
|
5668
|
+
function readDir(dir, source) {
|
|
5669
|
+
if (!dir || !existsSync16(dir)) return [];
|
|
5670
|
+
const out = [];
|
|
5671
|
+
for (const file of readdirSync(dir)) {
|
|
5672
|
+
const m = FILE_RE.exec(file);
|
|
5673
|
+
if (!m) continue;
|
|
5674
|
+
let body;
|
|
5675
|
+
try {
|
|
5676
|
+
body = readFileSync17(join16(dir, file), "utf8");
|
|
5677
|
+
} catch {
|
|
5678
|
+
continue;
|
|
5679
|
+
}
|
|
5680
|
+
const { title, summary } = parse(body);
|
|
5681
|
+
out.push({ topic: m[2], order: m[1] ? Number(m[1]) : Number.POSITIVE_INFINITY, title: title || m[2], summary, body, source });
|
|
5682
|
+
}
|
|
5683
|
+
return out;
|
|
5684
|
+
}
|
|
5685
|
+
function loadGuides(builtinDir = builtinGuidesDir(), overrideDir = process.env.SUVEREN_GUIDES_DIR) {
|
|
5686
|
+
const byTopic = /* @__PURE__ */ new Map();
|
|
5687
|
+
for (const g of readDir(builtinDir, "default")) byTopic.set(g.topic, g);
|
|
5688
|
+
for (const g of readDir(overrideDir, "override")) {
|
|
5689
|
+
const replaced = byTopic.get(g.topic);
|
|
5690
|
+
byTopic.set(g.topic, { ...g, order: Number.isFinite(g.order) ? g.order : replaced?.order ?? Number.POSITIVE_INFINITY });
|
|
5691
|
+
}
|
|
5692
|
+
return [...byTopic.values()].sort((a, b) => a.order - b.order || a.topic.localeCompare(b.topic));
|
|
5693
|
+
}
|
|
5694
|
+
function guideHeader(systems) {
|
|
5695
|
+
const lines = [
|
|
5696
|
+
"> This guide is in English. Talk to the person in their language, and write the brief and mandate intents in the language the team uses.",
|
|
5697
|
+
"",
|
|
5698
|
+
"**Systems connected to this gateway:**",
|
|
5699
|
+
""
|
|
5700
|
+
];
|
|
5701
|
+
if (systems.length === 0) {
|
|
5702
|
+
lines.push("- none yet \u2014 ask the person to connect the systems the work needs before you continue.");
|
|
5703
|
+
} else {
|
|
5704
|
+
for (const s of systems) {
|
|
5705
|
+
const parts = [`profile \`${s.profile}\``];
|
|
5706
|
+
if (s.actionTypes.length) parts.push(`action types ${s.actionTypes.map((a) => `\`${a}\``).join(", ")}`);
|
|
5707
|
+
if (s.writeTools.length) parts.push(`changes via ${s.writeTools.map((t) => `\`${t}\``).join(", ")}`);
|
|
5708
|
+
if (s.readTools.length) parts.push(`reads via ${s.readTools.map((t) => `\`${t}\``).join(", ")}`);
|
|
5709
|
+
lines.push(`- **${s.id}** \u2014 ${parts.join("; ")}`);
|
|
5710
|
+
}
|
|
5711
|
+
}
|
|
5712
|
+
return lines.join("\n");
|
|
5713
|
+
}
|
|
5714
|
+
|
|
5609
5715
|
// src/lib/builtins/setup.ts
|
|
5610
5716
|
var DELEGATION_PROFILE = "github.com/humanagencyprotocol/hap-profiles/delegation@0.1";
|
|
5611
5717
|
function briefRefusal(args) {
|
|
@@ -5614,11 +5720,27 @@ function briefRefusal(args) {
|
|
|
5614
5720
|
if (bytes > CONTEXT_MAX_BYTES) return `the brief is ${bytes} bytes; the limit is ${CONTEXT_MAX_BYTES}. Shorten it.`;
|
|
5615
5721
|
return void 0;
|
|
5616
5722
|
}
|
|
5617
|
-
function
|
|
5723
|
+
function connectedSystems(im) {
|
|
5724
|
+
const byId = /* @__PURE__ */ new Map();
|
|
5725
|
+
for (const t of im.getAllTools()) {
|
|
5726
|
+
if (im.isBuiltin(t.integrationId) || !t.gating?.profile || t.gating.category === "disabled") continue;
|
|
5727
|
+
const s = byId.get(t.integrationId) ?? { id: t.integrationId, profile: t.gating.profile, actionTypes: [], writeTools: [], readTools: [] };
|
|
5728
|
+
if (t.gating.category === "read") {
|
|
5729
|
+
s.readTools.push(t.originalName);
|
|
5730
|
+
} else {
|
|
5731
|
+
s.writeTools.push(t.originalName);
|
|
5732
|
+
const at = t.gating.staticExecution?.action_type;
|
|
5733
|
+
if (typeof at === "string" && !s.actionTypes.includes(at)) s.actionTypes.push(at);
|
|
5734
|
+
}
|
|
5735
|
+
byId.set(t.integrationId, s);
|
|
5736
|
+
}
|
|
5737
|
+
return [...byId.values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
5738
|
+
}
|
|
5739
|
+
function setupBuiltin(deps) {
|
|
5618
5740
|
return {
|
|
5619
5741
|
id: "setup",
|
|
5620
5742
|
name: "Test setup",
|
|
5621
|
-
description: "Your AI
|
|
5743
|
+
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.",
|
|
5622
5744
|
profile: DELEGATION_PROFILE,
|
|
5623
5745
|
simulation: true,
|
|
5624
5746
|
simulationOnly: true,
|
|
@@ -5627,11 +5749,67 @@ function setupBuiltin(_deps) {
|
|
|
5627
5749
|
set_agent_brief: {
|
|
5628
5750
|
executionMapping: {},
|
|
5629
5751
|
staticExecution: { action_type: "brief" },
|
|
5630
|
-
hideUnlessAuthorized: true
|
|
5752
|
+
hideUnlessAuthorized: true,
|
|
5753
|
+
approvalView: { content: { label: "New agent brief", kind: "markdown" } }
|
|
5754
|
+
},
|
|
5755
|
+
get_guide: {
|
|
5756
|
+
category: "read",
|
|
5757
|
+
boundField: "read_access",
|
|
5758
|
+
requiredValue: "unlimited"
|
|
5759
|
+
},
|
|
5760
|
+
create_mandate: {
|
|
5761
|
+
executionMapping: {},
|
|
5762
|
+
staticExecution: { action_type: "mandate" },
|
|
5763
|
+
hideUnlessAuthorized: true,
|
|
5764
|
+
// Shown like the mandate screen: limits and scope by the profile's own names.
|
|
5765
|
+
approvalView: {
|
|
5766
|
+
team: { label: "Team" },
|
|
5767
|
+
profile: { label: "Profile", kind: "profile" },
|
|
5768
|
+
limits: { label: "Limits", kind: "profile-limits", profileArg: "profile" },
|
|
5769
|
+
scope: { label: "Scope", kind: "profile-scope", profileArg: "profile" },
|
|
5770
|
+
intent: { label: "Intent", kind: "markdown" },
|
|
5771
|
+
mode: { label: "Mode" },
|
|
5772
|
+
duration_hours: { label: "Valid for (hours)" },
|
|
5773
|
+
title: { label: "Title" }
|
|
5774
|
+
}
|
|
5631
5775
|
}
|
|
5632
5776
|
}
|
|
5633
5777
|
},
|
|
5634
5778
|
tools: [
|
|
5779
|
+
{
|
|
5780
|
+
name: "get_guide",
|
|
5781
|
+
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.",
|
|
5782
|
+
inputSchema: {
|
|
5783
|
+
type: "object",
|
|
5784
|
+
properties: {
|
|
5785
|
+
topic: { type: "string", description: 'The guide to read, e.g. "interview". Omit for the list of topics.' }
|
|
5786
|
+
},
|
|
5787
|
+
required: []
|
|
5788
|
+
},
|
|
5789
|
+
handler: async (args) => {
|
|
5790
|
+
if (!isSimulationMode()) return { ...builtinText("Refused: not available outside simulation mode."), isError: true };
|
|
5791
|
+
const guides = loadGuides();
|
|
5792
|
+
const header = guideHeader(connectedSystems(deps.integrationManager));
|
|
5793
|
+
const topic = typeof args.topic === "string" ? args.topic.trim().toLowerCase() : "";
|
|
5794
|
+
if (!topic) {
|
|
5795
|
+
const list = guides.map((g, i) => `${i + 1}. **${g.topic}** \u2014 ${g.summary}`).join("\n");
|
|
5796
|
+
return builtinText(`${header}
|
|
5797
|
+
|
|
5798
|
+
**Setup guides, in order:**
|
|
5799
|
+
|
|
5800
|
+
${list || "- none installed"}
|
|
5801
|
+
|
|
5802
|
+
Read each topic with get_guide(topic) before doing that step.`);
|
|
5803
|
+
}
|
|
5804
|
+
const guide = guides.find((g) => g.topic === topic);
|
|
5805
|
+
if (!guide) {
|
|
5806
|
+
return { ...builtinText(`Unknown topic "${topic}". Topics: ${guides.map((g) => g.topic).join(", ") || "none installed"}.`), isError: true };
|
|
5807
|
+
}
|
|
5808
|
+
return builtinText(`${header}
|
|
5809
|
+
|
|
5810
|
+
${guide.body.trim()}`);
|
|
5811
|
+
}
|
|
5812
|
+
},
|
|
5635
5813
|
{
|
|
5636
5814
|
name: "set_agent_brief",
|
|
5637
5815
|
description: "Simulation mode only: propose a new agent brief \u2014 the standing instructions every agent session starts with. `content` replaces the whole brief (markdown, at most 16 KB). A person approves or rejects the proposal; only after approval is the brief replaced, and it applies from the next session. Write the complete brief, not a change to the current one.",
|
|
@@ -5656,10 +5834,56 @@ function setupBuiltin(_deps) {
|
|
|
5656
5834
|
`Agent brief replaced (${Buffer.byteLength(args.content, "utf-8")} bytes). It applies from the next agent session.`
|
|
5657
5835
|
);
|
|
5658
5836
|
}
|
|
5837
|
+
},
|
|
5838
|
+
{
|
|
5839
|
+
name: "create_mandate",
|
|
5840
|
+
description: "Simulation mode only: propose a new mandate for the person running this gateway \u2014 the authority another tool needs (e.g. sales quotes up to a value). A person approves or rejects the proposal; only after approval is the mandate created, exactly as if they had signed it on the mandate screen. Checked against the profile before the proposal: unknown limits, a mode the profile does not allow, or a team where the person may not give this mandate are refused at once. Create only.",
|
|
5841
|
+
inputSchema: {
|
|
5842
|
+
type: "object",
|
|
5843
|
+
properties: {
|
|
5844
|
+
team: { type: "string", description: "Team name or id. Omit for the personal workspace." },
|
|
5845
|
+
profile: { type: "string", description: 'Profile, e.g. "sales" (newest version) or a full profile id.' },
|
|
5846
|
+
limits: { type: "object", description: "The limits (bounds) by field name, as the profile defines them." },
|
|
5847
|
+
scope: { type: "object", description: "The scope (context) by field name, as the profile defines it." },
|
|
5848
|
+
intent: { type: "string", description: "Why, goal and watch-outs \u2014 signed with the mandate (at most 2000 characters)." },
|
|
5849
|
+
mode: { type: "string", enum: ["review", "automatic"], description: "review = each action needs approval; automatic = within the limits." },
|
|
5850
|
+
duration_hours: { type: "number", description: "How long the mandate is valid, in hours. Omit for the profile default." },
|
|
5851
|
+
title: { type: "string", description: "A short name for the mandate." },
|
|
5852
|
+
receipt_id: {
|
|
5853
|
+
type: "string",
|
|
5854
|
+
description: "Authorization reference for this call, set by the governing gateway \u2014 agents do not set this."
|
|
5855
|
+
}
|
|
5856
|
+
},
|
|
5857
|
+
required: ["profile", "limits", "intent", "mode"]
|
|
5858
|
+
},
|
|
5859
|
+
validate: async (args) => {
|
|
5860
|
+
const check = await controlPlaneMandate(true, mandateRequest(args));
|
|
5861
|
+
return check.ok ? void 0 : check.message;
|
|
5862
|
+
},
|
|
5863
|
+
handler: async (args) => {
|
|
5864
|
+
if (!isSimulationMode()) return { ...builtinText("Refused: not available outside simulation mode."), isError: true };
|
|
5865
|
+
const r = await controlPlaneMandate(false, mandateRequest(args));
|
|
5866
|
+
if (!r.ok) return { ...builtinText(`Refused: ${r.message}`), isError: true };
|
|
5867
|
+
return builtinText(
|
|
5868
|
+
`Mandate created: ${r.authorizationId} \u2014 profile ${r.profileId}, ${r.groupName}, mode ${r.mode}, valid for ${Math.round((r.ttlSeconds ?? 0) / 3600)} h. It is active now.`
|
|
5869
|
+
);
|
|
5870
|
+
}
|
|
5659
5871
|
}
|
|
5660
5872
|
]
|
|
5661
5873
|
};
|
|
5662
5874
|
}
|
|
5875
|
+
function mandateRequest(args) {
|
|
5876
|
+
return {
|
|
5877
|
+
team: typeof args.team === "string" && args.team.trim() ? args.team.trim() : void 0,
|
|
5878
|
+
profile: args.profile,
|
|
5879
|
+
limits: args.limits,
|
|
5880
|
+
scope: args.scope,
|
|
5881
|
+
intent: args.intent,
|
|
5882
|
+
mode: args.mode,
|
|
5883
|
+
durationHours: args.duration_hours,
|
|
5884
|
+
title: args.title
|
|
5885
|
+
};
|
|
5886
|
+
}
|
|
5663
5887
|
|
|
5664
5888
|
// src/lib/builtins/index.ts
|
|
5665
5889
|
var BUILTIN_FACTORIES = [setupBuiltin];
|
|
@@ -5681,8 +5905,8 @@ function registerBuiltins(deps, factories = BUILTIN_FACTORIES) {
|
|
|
5681
5905
|
import { decodeAttestationBlob as decodeAttestationBlob3 } from "@hap/core";
|
|
5682
5906
|
|
|
5683
5907
|
// src/lib/committed-executor.ts
|
|
5684
|
-
import { existsSync as
|
|
5685
|
-
import { dirname as
|
|
5908
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync12, readFileSync as readFileSync18, unlinkSync as unlinkSync6, writeFileSync as writeFileSync12 } from "fs";
|
|
5909
|
+
import { dirname as dirname12, join as join17 } from "path";
|
|
5686
5910
|
var CommittedExecutor = class {
|
|
5687
5911
|
constructor(run) {
|
|
5688
5912
|
this.run = run;
|
|
@@ -5710,7 +5934,7 @@ var ExecutorLock = class {
|
|
|
5710
5934
|
path;
|
|
5711
5935
|
held = false;
|
|
5712
5936
|
constructor(dataDir2) {
|
|
5713
|
-
this.path =
|
|
5937
|
+
this.path = join17(dataDir2, "executor.lock");
|
|
5714
5938
|
}
|
|
5715
5939
|
/** Try to take the lock. Returns the holder's pid when someone else has it. */
|
|
5716
5940
|
acquire() {
|
|
@@ -5718,7 +5942,7 @@ var ExecutorLock = class {
|
|
|
5718
5942
|
if (current && current.pid !== process.pid && isAlive(current.pid)) {
|
|
5719
5943
|
return { held: false, holderPid: current.pid };
|
|
5720
5944
|
}
|
|
5721
|
-
mkdirSync12(
|
|
5945
|
+
mkdirSync12(dirname12(this.path), { recursive: true, mode: 448 });
|
|
5722
5946
|
const lock = { pid: process.pid, startedAt: Math.floor(Date.now() / 1e3) };
|
|
5723
5947
|
writeFileSync12(this.path, JSON.stringify(lock), { mode: 384 });
|
|
5724
5948
|
this.held = true;
|
|
@@ -5742,9 +5966,9 @@ var ExecutorLock = class {
|
|
|
5742
5966
|
}
|
|
5743
5967
|
}
|
|
5744
5968
|
read() {
|
|
5745
|
-
if (!
|
|
5969
|
+
if (!existsSync17(this.path)) return null;
|
|
5746
5970
|
try {
|
|
5747
|
-
const parsed = JSON.parse(
|
|
5971
|
+
const parsed = JSON.parse(readFileSync18(this.path, "utf8"));
|
|
5748
5972
|
return typeof parsed?.pid === "number" ? parsed : null;
|
|
5749
5973
|
} catch {
|
|
5750
5974
|
return null;
|
|
@@ -5762,19 +5986,19 @@ function isAlive(pid) {
|
|
|
5762
5986
|
|
|
5763
5987
|
// bin/http.ts
|
|
5764
5988
|
import { homedir as homedir10 } from "os";
|
|
5765
|
-
import { join as
|
|
5989
|
+
import { join as join19 } from "path";
|
|
5766
5990
|
|
|
5767
5991
|
// src/lib/mcp-as-tracker.ts
|
|
5768
|
-
import { existsSync as
|
|
5769
|
-
import { dirname as
|
|
5992
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync13, readFileSync as readFileSync19, writeFileSync as writeFileSync13 } from "fs";
|
|
5993
|
+
import { dirname as dirname13, join as join18 } from "path";
|
|
5770
5994
|
function trackerPath(dataDir2) {
|
|
5771
|
-
return
|
|
5995
|
+
return join18(dataDir2, "mcp-as-pairing.json");
|
|
5772
5996
|
}
|
|
5773
5997
|
function readMcpPairedAsUrl(dataDir2) {
|
|
5774
5998
|
const path = trackerPath(dataDir2);
|
|
5775
|
-
if (!
|
|
5999
|
+
if (!existsSync18(path)) return null;
|
|
5776
6000
|
try {
|
|
5777
|
-
const data = JSON.parse(
|
|
6001
|
+
const data = JSON.parse(readFileSync19(path, "utf-8"));
|
|
5778
6002
|
return typeof data.asUrl === "string" ? data.asUrl : null;
|
|
5779
6003
|
} catch {
|
|
5780
6004
|
return null;
|
|
@@ -5782,12 +6006,12 @@ function readMcpPairedAsUrl(dataDir2) {
|
|
|
5782
6006
|
}
|
|
5783
6007
|
function writeMcpPairedAsUrl(dataDir2, asUrl) {
|
|
5784
6008
|
const path = trackerPath(dataDir2);
|
|
5785
|
-
mkdirSync13(
|
|
6009
|
+
mkdirSync13(dirname13(path), { recursive: true, mode: 448 });
|
|
5786
6010
|
writeFileSync13(path, JSON.stringify({ asUrl }, null, 2), { encoding: "utf-8", mode: 384 });
|
|
5787
6011
|
}
|
|
5788
6012
|
|
|
5789
6013
|
// bin/http.ts
|
|
5790
|
-
var dataDir = process.env.SUVEREN_DATA_DIR ??
|
|
6014
|
+
var dataDir = process.env.SUVEREN_DATA_DIR ?? join19(homedir10(), ".suveren");
|
|
5791
6015
|
var spUrl = resolveAsUrl(dataDir);
|
|
5792
6016
|
validatePinTlsForUrl(resolvePinTls(dataDir), spUrl);
|
|
5793
6017
|
var port = parseInt(process.env.SUVEREN_MCP_PORT ?? "3430", 10);
|
|
@@ -6151,6 +6375,9 @@ app.get("/health", (_req, res) => {
|
|
|
6151
6375
|
builtins: integrationManager.getBuiltins()
|
|
6152
6376
|
});
|
|
6153
6377
|
});
|
|
6378
|
+
app.get("/internal/tool-display", internalOnly, (_req, res) => {
|
|
6379
|
+
res.json({ tools: integrationManager.getToolDisplay() });
|
|
6380
|
+
});
|
|
6154
6381
|
app.get("/internal/gate-content", internalOnly, (req, res) => {
|
|
6155
6382
|
const path = req.query.path;
|
|
6156
6383
|
const gates = state.gateStore.getAll();
|
|
@@ -6318,7 +6545,7 @@ app.listen(port, "0.0.0.0", () => {
|
|
|
6318
6545
|
(proposal) => executeCommitted(proposal, state, integrationManager)
|
|
6319
6546
|
);
|
|
6320
6547
|
installCommittedExecutor(committedExecutor);
|
|
6321
|
-
const dataDir2 = process.env.SUVEREN_DATA_DIR ??
|
|
6548
|
+
const dataDir2 = process.env.SUVEREN_DATA_DIR ?? join19(homedir10(), ".suveren");
|
|
6322
6549
|
const executorLock = new ExecutorLock(dataDir2);
|
|
6323
6550
|
const lock = executorLock.acquire();
|
|
6324
6551
|
if (!lock.held) {
|