@suveren/gateway 0.8.6 → 0.8.7
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 +262 -46
- package/dist/mcp-server/http.mjs +73 -9
- package/dist/ui/assets/index-tzStUGuj.js +110 -0
- package/dist/ui/icons/apple-touch-icon.png +0 -0
- package/dist/ui/icons/icon-192.png +0 -0
- package/dist/ui/icons/icon-512.png +0 -0
- package/dist/ui/icons/icon-maskable-512.png +0 -0
- package/dist/ui/index.html +7 -1
- package/dist/ui/manifest.webmanifest +15 -0
- package/node_modules/@modelcontextprotocol/sdk/dist/cjs/client/auth.d.ts +15 -4
- package/node_modules/@modelcontextprotocol/sdk/dist/cjs/client/auth.d.ts.map +1 -1
- package/node_modules/@modelcontextprotocol/sdk/dist/cjs/client/auth.js +22 -3
- package/node_modules/@modelcontextprotocol/sdk/dist/cjs/client/auth.js.map +1 -1
- package/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/express.d.ts.map +1 -1
- package/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/express.js +3 -1
- package/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/express.js.map +1 -1
- package/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/requestBody.d.ts +25 -0
- package/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/requestBody.d.ts.map +1 -0
- package/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/requestBody.js +64 -0
- package/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/requestBody.js.map +1 -0
- package/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/webStandardStreamableHttp.d.ts +10 -1
- package/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/webStandardStreamableHttp.d.ts.map +1 -1
- package/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/webStandardStreamableHttp.js +13 -1
- package/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/webStandardStreamableHttp.js.map +1 -1
- package/node_modules/@modelcontextprotocol/sdk/dist/esm/client/auth.d.ts +15 -4
- package/node_modules/@modelcontextprotocol/sdk/dist/esm/client/auth.d.ts.map +1 -1
- package/node_modules/@modelcontextprotocol/sdk/dist/esm/client/auth.js +22 -3
- package/node_modules/@modelcontextprotocol/sdk/dist/esm/client/auth.js.map +1 -1
- package/node_modules/@modelcontextprotocol/sdk/dist/esm/server/express.d.ts.map +1 -1
- package/node_modules/@modelcontextprotocol/sdk/dist/esm/server/express.js +3 -1
- package/node_modules/@modelcontextprotocol/sdk/dist/esm/server/express.js.map +1 -1
- package/node_modules/@modelcontextprotocol/sdk/dist/esm/server/requestBody.d.ts +25 -0
- package/node_modules/@modelcontextprotocol/sdk/dist/esm/server/requestBody.d.ts.map +1 -0
- package/node_modules/@modelcontextprotocol/sdk/dist/esm/server/requestBody.js +58 -0
- package/node_modules/@modelcontextprotocol/sdk/dist/esm/server/requestBody.js.map +1 -0
- package/node_modules/@modelcontextprotocol/sdk/dist/esm/server/webStandardStreamableHttp.d.ts +10 -1
- package/node_modules/@modelcontextprotocol/sdk/dist/esm/server/webStandardStreamableHttp.d.ts.map +1 -1
- package/node_modules/@modelcontextprotocol/sdk/dist/esm/server/webStandardStreamableHttp.js +13 -1
- package/node_modules/@modelcontextprotocol/sdk/dist/esm/server/webStandardStreamableHttp.js.map +1 -1
- package/node_modules/@modelcontextprotocol/sdk/package.json +1 -1
- package/node_modules/hono/dist/adapter/aws-lambda/handler.js +6 -3
- package/node_modules/hono/dist/adapter/lambda-edge/handler.js +26 -12
- package/node_modules/hono/dist/cjs/adapter/aws-lambda/handler.js +6 -3
- package/node_modules/hono/dist/cjs/adapter/lambda-edge/handler.js +26 -12
- package/node_modules/hono/dist/cjs/helper/accepts/accepts.js +7 -5
- package/node_modules/hono/dist/cjs/jsx/components.js +1 -1
- package/node_modules/hono/dist/cjs/jsx/streaming.js +1 -1
- package/node_modules/hono/dist/cjs/middleware/pretty-json/index.js +7 -1
- package/node_modules/hono/dist/cjs/router/linear-router/router.js +3 -3
- package/node_modules/hono/dist/cjs/utils/jwt/jwt.js +7 -6
- package/node_modules/hono/dist/helper/accepts/accepts.js +7 -5
- package/node_modules/hono/dist/jsx/components.js +1 -1
- package/node_modules/hono/dist/jsx/streaming.js +1 -1
- package/node_modules/hono/dist/middleware/pretty-json/index.js +7 -1
- package/node_modules/hono/dist/router/linear-router/router.js +3 -3
- package/node_modules/hono/dist/utils/jwt/jwt.js +7 -6
- package/node_modules/hono/package.json +1 -1
- package/package.json +1 -1
- package/dist/ui/assets/index-DWpjjPIf.js +0 -110
package/dist/mcp-server/http.mjs
CHANGED
|
@@ -134,6 +134,13 @@ var SPClient = class {
|
|
|
134
134
|
sessionCookie = "";
|
|
135
135
|
apiKey = "";
|
|
136
136
|
receiptRetry;
|
|
137
|
+
/** Set when a call comes back 401 while we held a session — distinct from
|
|
138
|
+
* never having logged in. Cleared the moment a fresh cookie is configured. */
|
|
139
|
+
lockReason = null;
|
|
140
|
+
/** Single-flight per client instance: only the FIRST 401 after a session
|
|
141
|
+
* was active tells the control plane. Concurrent in-flight calls that were
|
|
142
|
+
* all sent under the same now-dead cookie would otherwise all report it. */
|
|
143
|
+
sessionExpiredNotified = false;
|
|
137
144
|
/** The AS base URL this client talks to (for provenance records). */
|
|
138
145
|
get url() {
|
|
139
146
|
return this.baseUrl;
|
|
@@ -143,6 +150,22 @@ var SPClient = class {
|
|
|
143
150
|
}
|
|
144
151
|
setSessionCookie(cookie) {
|
|
145
152
|
this.sessionCookie = cookie;
|
|
153
|
+
this.lockReason = null;
|
|
154
|
+
this.sessionExpiredNotified = false;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Drop the session because the control plane learned (by whatever path —
|
|
158
|
+
* one of OUR calls 401ing, or its own AS proxy 401ing) that it ended.
|
|
159
|
+
* Idempotent: safe to call redundantly from the control-plane's push.
|
|
160
|
+
*/
|
|
161
|
+
clearSession() {
|
|
162
|
+
this.sessionCookie = "";
|
|
163
|
+
this.lockReason = "expired";
|
|
164
|
+
}
|
|
165
|
+
/** Why we are currently locked, if known. `null` means the default
|
|
166
|
+
* boot-locked case (nobody has signed in yet this run). */
|
|
167
|
+
getLockReason() {
|
|
168
|
+
return this.sessionCookie === "" ? this.lockReason : null;
|
|
146
169
|
}
|
|
147
170
|
/**
|
|
148
171
|
* Has anyone signed in yet this run?
|
|
@@ -174,10 +197,20 @@ var SPClient = class {
|
|
|
174
197
|
if (this.apiKey) {
|
|
175
198
|
headers["X-API-Key"] = this.apiKey;
|
|
176
199
|
}
|
|
177
|
-
|
|
200
|
+
const hadSession = this.sessionCookie !== "";
|
|
201
|
+
const res = await globalThis.fetch(url, {
|
|
178
202
|
...init,
|
|
179
203
|
headers
|
|
180
204
|
});
|
|
205
|
+
if (res.status === 401 && hadSession) {
|
|
206
|
+
this.sessionCookie = "";
|
|
207
|
+
this.lockReason = "expired";
|
|
208
|
+
if (!this.sessionExpiredNotified) {
|
|
209
|
+
this.sessionExpiredNotified = true;
|
|
210
|
+
void notifyControlPlane("session-expired");
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return res;
|
|
181
214
|
}
|
|
182
215
|
/**
|
|
183
216
|
* Get SP public key.
|
|
@@ -1578,14 +1611,22 @@ function uiUrl() {
|
|
|
1578
1611
|
const port2 = process.env.SUVEREN_CP_PORT ?? "3400";
|
|
1579
1612
|
return `http://localhost:${port2}`;
|
|
1580
1613
|
}
|
|
1581
|
-
function
|
|
1582
|
-
|
|
1583
|
-
return `${what} Suveren gateway is running but LOCKED, so it cannot read any authorizations or credentials.
|
|
1614
|
+
function bootLockedNotice(what) {
|
|
1615
|
+
return `${what} Suveren gateway is running but LOCKED, so it cannot read any mandates or credentials.
|
|
1584
1616
|
|
|
1585
|
-
This is not the same as having no
|
|
1617
|
+
This is not the same as having no mandates \u2014 yours are on disk, encrypted, and unreadable until someone unlocks them.
|
|
1586
1618
|
|
|
1587
1619
|
TELL THE USER: open ${uiUrl()} and enter your Suveren API key to unlock the gateway. It boots locked by design after every restart; the key is never stored.`;
|
|
1588
1620
|
}
|
|
1621
|
+
function expiredSessionNotice(what) {
|
|
1622
|
+
return `${what} Suveren gateway's sign-in has ended (sign-ins last 30 days, or it was revoked), so it is now LOCKED.
|
|
1623
|
+
|
|
1624
|
+
TELL THE USER: open ${uiUrl()} and enter your Suveren API key to sign in again. Your mandates are safe; nothing ran.`;
|
|
1625
|
+
}
|
|
1626
|
+
function lockedNotice(action, reason = "restart") {
|
|
1627
|
+
const what = action ? `Cannot ${action}: the` : "The";
|
|
1628
|
+
return reason === "expired" ? expiredSessionNotice(what) : bootLockedNotice(what);
|
|
1629
|
+
}
|
|
1589
1630
|
|
|
1590
1631
|
// src/tools/authorizations.ts
|
|
1591
1632
|
import { getProfile as getProfile4 } from "@hap/core";
|
|
@@ -2109,7 +2150,7 @@ function createGatedToolHandlerInner(tool, integrationManager2, state2) {
|
|
|
2109
2150
|
const lockedGuard = (inner) => async (args) => {
|
|
2110
2151
|
if (!state2.spClient.isUnlocked()) {
|
|
2111
2152
|
return {
|
|
2112
|
-
content: [{ type: "text", text: lockedNotice(`use ${tool.namespacedName}
|
|
2153
|
+
content: [{ type: "text", text: lockedNotice(`use ${tool.namespacedName}`, state2.spClient.getLockReason() ?? "restart") }],
|
|
2113
2154
|
isError: true
|
|
2114
2155
|
};
|
|
2115
2156
|
}
|
|
@@ -2421,6 +2462,12 @@ Proposal ID: ${proposal.id}. Check status with check-pending-commitments(proposa
|
|
|
2421
2462
|
}]
|
|
2422
2463
|
};
|
|
2423
2464
|
} catch (err) {
|
|
2465
|
+
if (!state2.spClient.isUnlocked()) {
|
|
2466
|
+
return {
|
|
2467
|
+
content: [{ type: "text", text: lockedNotice(`use ${tool.namespacedName}`, "expired") }],
|
|
2468
|
+
isError: true
|
|
2469
|
+
};
|
|
2470
|
+
}
|
|
2424
2471
|
return {
|
|
2425
2472
|
content: [{ type: "text", text: `Failed to submit proposal: ${err instanceof Error ? err.message : String(err)}` }],
|
|
2426
2473
|
isError: true
|
|
@@ -2516,6 +2563,12 @@ Proposal ID: ${proposal.id}. Use check-pending-commitments to track status.`
|
|
|
2516
2563
|
}]
|
|
2517
2564
|
};
|
|
2518
2565
|
} catch (proposalErr) {
|
|
2566
|
+
if (!state2.spClient.isUnlocked()) {
|
|
2567
|
+
return {
|
|
2568
|
+
content: [{ type: "text", text: lockedNotice(`use ${tool.namespacedName}`, "expired") }],
|
|
2569
|
+
isError: true
|
|
2570
|
+
};
|
|
2571
|
+
}
|
|
2519
2572
|
return {
|
|
2520
2573
|
content: [{ type: "text", text: `Failed to submit approval proposal: ${proposalErr instanceof Error ? proposalErr.message : String(proposalErr)}` }],
|
|
2521
2574
|
isError: true
|
|
@@ -2537,6 +2590,12 @@ Proposal ID: ${proposal.id}. Use check-pending-commitments to track status.`
|
|
|
2537
2590
|
isError: true
|
|
2538
2591
|
};
|
|
2539
2592
|
}
|
|
2593
|
+
if (!state2.spClient.isUnlocked()) {
|
|
2594
|
+
return {
|
|
2595
|
+
content: [{ type: "text", text: lockedNotice(`use ${tool.namespacedName}`, "expired") }],
|
|
2596
|
+
isError: true
|
|
2597
|
+
};
|
|
2598
|
+
}
|
|
2540
2599
|
return {
|
|
2541
2600
|
content: [{ type: "text", text: `SP unavailable \u2014 tool call blocked. ${err instanceof Error ? err.message : ""}` }],
|
|
2542
2601
|
isError: true
|
|
@@ -2667,7 +2726,7 @@ function buildCapabilityMap(profileId, toolGating, integrationManager2) {
|
|
|
2667
2726
|
function listAuthorizationsHandler(state2, integrationManager2, contextDir) {
|
|
2668
2727
|
return async (args) => {
|
|
2669
2728
|
if (!state2.spClient.isUnlocked()) {
|
|
2670
|
-
return { content: [{ type: "text", text: lockedNotice("list authorizations") }] };
|
|
2729
|
+
return { content: [{ type: "text", text: lockedNotice("list authorizations", state2.spClient.getLockReason() ?? "restart") }] };
|
|
2671
2730
|
}
|
|
2672
2731
|
const authorizations = state2.getEnrichedAuthorizations();
|
|
2673
2732
|
const now = Math.floor(Date.now() / 1e3);
|
|
@@ -2861,7 +2920,7 @@ function shortProfileName3(profileId) {
|
|
|
2861
2920
|
function listIntegrationsHandler(state2, integrationManager2) {
|
|
2862
2921
|
return async () => {
|
|
2863
2922
|
if (!state2.spClient.isUnlocked()) {
|
|
2864
|
-
return { content: [{ type: "text", text: lockedNotice("list integrations") }] };
|
|
2923
|
+
return { content: [{ type: "text", text: lockedNotice("list integrations", state2.spClient.getLockReason() ?? "restart") }] };
|
|
2865
2924
|
}
|
|
2866
2925
|
if (!integrationManager2) {
|
|
2867
2926
|
return { content: [{ type: "text", text: "No integration manager available." }] };
|
|
@@ -3067,7 +3126,7 @@ Result: ${resultText}` };
|
|
|
3067
3126
|
function checkPendingCommitmentsHandler(state2, integrationManager2) {
|
|
3068
3127
|
return async (args) => {
|
|
3069
3128
|
if (!state2.spClient.isUnlocked()) {
|
|
3070
|
-
return { content: [{ type: "text", text: lockedNotice("check commitments") }] };
|
|
3129
|
+
return { content: [{ type: "text", text: lockedNotice("check commitments", state2.spClient.getLockReason() ?? "restart") }] };
|
|
3071
3130
|
}
|
|
3072
3131
|
try {
|
|
3073
3132
|
if (args.proposal_id) {
|
|
@@ -4312,6 +4371,11 @@ app.post("/internal/configure", internalOnly, (req, res) => {
|
|
|
4312
4371
|
}
|
|
4313
4372
|
res.json({ ok: true });
|
|
4314
4373
|
});
|
|
4374
|
+
app.post("/internal/clear-session", internalOnly, (_req, res) => {
|
|
4375
|
+
state.spClient.clearSession();
|
|
4376
|
+
console.error("[Suveren MCP] Session cleared by control-plane (AS session ended)");
|
|
4377
|
+
res.json({ ok: true });
|
|
4378
|
+
});
|
|
4315
4379
|
app.post("/internal/gate-content", internalOnly, async (req, res) => {
|
|
4316
4380
|
try {
|
|
4317
4381
|
const { authorizationId, boundsHash, contextHash, context, contextLabels, path: rawPath, gateContent } = req.body;
|