@suveren/gateway 0.2.8 → 0.2.10

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.
@@ -1553,9 +1553,10 @@ async function executeCommitted(proposal, state2, integrationManager2) {
1553
1553
  `[Suveren MCP] Warning: proposal ${proposal.id} has no action_type in executionContext. Bounds check may be skipped. Fix the integration manifest for ${proposal.tool}.`
1554
1554
  );
1555
1555
  }
1556
+ let receiptId;
1556
1557
  try {
1557
1558
  const boundsHash = proposal.frameHash.split(":").slice(0, 2).join(":");
1558
- await state2.spClient.postReceipt({
1559
+ const { receipt } = await state2.spClient.postReceipt({
1559
1560
  boundsHash,
1560
1561
  profileId: proposal.profileId,
1561
1562
  action: proposal.tool,
@@ -1565,6 +1566,7 @@ async function executeCommitted(proposal, state2, integrationManager2) {
1565
1566
  proposalId: proposal.id,
1566
1567
  toolArgs: proposal.toolArgs
1567
1568
  });
1569
+ receiptId = typeof receipt?.id === "string" ? receipt.id : void 0;
1568
1570
  } catch (err) {
1569
1571
  if (err instanceof SPReceiptError) {
1570
1572
  const code = err.body.errors?.[0]?.code;
@@ -1584,7 +1586,20 @@ async function executeCommitted(proposal, state2, integrationManager2) {
1584
1586
  };
1585
1587
  }
1586
1588
  try {
1587
- const result = await integrationManager2.callTool(integrationId, toolName, proposal.toolArgs);
1589
+ let outgoingArgs = proposal.toolArgs;
1590
+ if (shouldAttachFooter() && receiptId) {
1591
+ const discovered = integrationManager2.getAllTools().find((t) => t.integrationId === integrationId && t.originalName === toolName);
1592
+ if (discovered) {
1593
+ outgoingArgs = appendVerificationFooter(discovered, proposal.toolArgs, receiptId);
1594
+ }
1595
+ }
1596
+ const result = await integrationManager2.callTool(integrationId, toolName, outgoingArgs);
1597
+ state2.executionLog.record({
1598
+ profileId: proposal.profileId,
1599
+ path: proposal.path,
1600
+ execution: proposal.executionContext,
1601
+ timestamp: Math.floor(Date.now() / 1e3)
1602
+ });
1588
1603
  const resultText = result.content?.[0]?.text ?? JSON.stringify(result);
1589
1604
  return { text: `Proposal ${proposal.id} committed and executed.
1590
1605
  Result: ${resultText}` };
@@ -2244,7 +2259,7 @@ function loadProfiles(profilesDir) {
2244
2259
  const dir = resolve(profilesDir ?? process.env.SUVEREN_PROFILES_DIR ?? join6(import.meta.dirname ?? __dirname, "..", "..", "..", "..", "..", "hap-profiles"));
2245
2260
  const indexPath = join6(dir, "index.json");
2246
2261
  if (!existsSync6(indexPath)) {
2247
- console.error(`[ProfileLoader] No index.json found at ${indexPath}, skipping profile loading`);
2262
+ warnNoProfiles(`No index.json at ${indexPath}`);
2248
2263
  return 0;
2249
2264
  }
2250
2265
  let index;
@@ -2269,9 +2284,26 @@ function loadProfiles(profilesDir) {
2269
2284
  console.error(`[ProfileLoader] Failed to load profile ${profileId} from ${profilePath}:`, err);
2270
2285
  }
2271
2286
  }
2272
- console.error(`[ProfileLoader] Loaded ${loaded} profile(s) from ${dir}`);
2287
+ if (loaded === 0) {
2288
+ warnNoProfiles(`index.json at ${indexPath} registered 0 profiles`);
2289
+ } else {
2290
+ console.error(`[ProfileLoader] Loaded ${loaded} profile(s) from ${dir}`);
2291
+ }
2273
2292
  return loaded;
2274
2293
  }
2294
+ function warnNoProfiles(reason) {
2295
+ console.error(
2296
+ `
2297
+ \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
2298
+ \u2551 \u26A0 NO PROFILES LOADED \u2014 every gated action will be REJECTED with \u2551
2299
+ \u2551 "Unknown profile". This gateway is misconfigured. \u2551
2300
+ \u2551 Reason: ${reason}
2301
+ \u2551 Fix: install a build that bundles profiles, or set \u2551
2302
+ \u2551 SUVEREN_PROFILES_DIR to a hap-profiles checkout, then restart. \u2551
2303
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
2304
+ `
2305
+ );
2306
+ }
2275
2307
 
2276
2308
  // src/lib/manifest-loader.ts
2277
2309
  import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
@@ -2605,11 +2637,13 @@ app.all("/mcp", async (req, res) => {
2605
2637
  res.status(405).json({ error: "Method not allowed" });
2606
2638
  }
2607
2639
  });
2640
+ var profilesLoaded = 0;
2608
2641
  app.get("/health", (_req, res) => {
2609
2642
  res.json({
2610
2643
  status: "ok",
2611
2644
  transports: ["sse", "streamable-http"],
2612
2645
  sp: spUrl,
2646
+ profilesLoaded,
2613
2647
  activeSessions: activeSessions.size,
2614
2648
  storedGates: state.gateStore.getAll().length,
2615
2649
  serviceCredentials: Array.from(serviceCredentials.keys()),
@@ -2699,7 +2733,7 @@ app.listen(port, "0.0.0.0", () => {
2699
2733
  console.error(`[Suveren MCP] SSE: http://0.0.0.0:${port}/sse`);
2700
2734
  console.error(`[Suveren MCP] Streamable: http://0.0.0.0:${port}/mcp`);
2701
2735
  console.error(`[Suveren MCP] SP server: ${spUrl}`);
2702
- loadProfiles();
2736
+ profilesLoaded = loadProfiles();
2703
2737
  loadManifests();
2704
2738
  if (integrationRegistry.getEnabled().length === 0) {
2705
2739
  const personalManifests = getAllManifests().filter((m) => m.personalDefault);
@@ -2744,59 +2778,10 @@ app.listen(port, "0.0.0.0", () => {
2744
2778
  const committed = await state.spClient.getCommittedProposals();
2745
2779
  for (const proposal of committed) {
2746
2780
  try {
2747
- let integrationId;
2748
- let toolName;
2749
- if (proposal.tool.includes("___")) {
2750
- const parts = proposal.tool.split("___");
2751
- if (parts.length !== 2) {
2752
- console.error(`[Suveren MCP] Invalid tool name in proposal ${proposal.id}: ${proposal.tool}`);
2753
- continue;
2754
- }
2755
- [integrationId, toolName] = parts;
2756
- } else {
2757
- const sep = proposal.tool.indexOf("__");
2758
- if (sep < 0) {
2759
- console.error(`[Suveren MCP] Invalid tool name in proposal ${proposal.id}: ${proposal.tool}`);
2760
- continue;
2761
- }
2762
- integrationId = proposal.tool.slice(0, sep);
2763
- toolName = proposal.tool.slice(sep + 2);
2764
- }
2765
- const proposalActionType = typeof proposal.executionContext.action_type === "string" ? proposal.executionContext.action_type : void 0;
2766
- if (!proposalActionType) {
2767
- console.error(
2768
- `[Suveren MCP] Warning: proposal ${proposal.id} has no action_type in executionContext. Bounds check may be skipped. Fix the integration manifest for ${proposal.tool}.`
2769
- );
2770
- }
2771
- try {
2772
- await state.spClient.postReceipt({
2773
- attestationHash: proposal.frameHash,
2774
- profileId: proposal.profileId,
2775
- path: proposal.path,
2776
- action: proposal.tool,
2777
- actionType: proposalActionType,
2778
- executionContext: proposal.executionContext,
2779
- amount: typeof proposal.executionContext.amount === "number" ? proposal.executionContext.amount : void 0,
2780
- proposalId: proposal.id,
2781
- toolArgs: proposal.toolArgs
2782
- });
2783
- } catch (err) {
2784
- console.error(`[Suveren MCP] Receipt failed for proposal ${proposal.id}:`, err instanceof Error ? err.message : err);
2785
- continue;
2786
- }
2787
- try {
2788
- await integrationManager.callTool(integrationId, toolName, proposal.toolArgs);
2789
- } catch (err) {
2790
- console.error(`[Suveren MCP] Tool execution failed for proposal ${proposal.id} after receipt issued:`, err);
2791
- continue;
2792
- }
2793
- state.executionLog.record({
2794
- profileId: proposal.profileId,
2795
- path: proposal.path,
2796
- execution: proposal.executionContext,
2797
- timestamp: Math.floor(Date.now() / 1e3)
2798
- });
2799
- console.error(`[Suveren MCP] Auto-executed proposal ${proposal.id}: ${proposal.tool}`);
2781
+ const { text, isError } = await executeCommitted(proposal, state, integrationManager);
2782
+ console.error(
2783
+ isError ? `[Suveren MCP] Auto-exec proposal ${proposal.id}: ${text}` : `[Suveren MCP] Auto-executed proposal ${proposal.id}: ${proposal.tool}`
2784
+ );
2800
2785
  } catch (err) {
2801
2786
  console.error(`[Suveren MCP] Failed to execute proposal ${proposal.id}:`, err);
2802
2787
  }