@suveren/gateway 0.29.0 → 0.30.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/integrations/gmail.json +13 -2
- package/content/integrations/mail.json +12 -2
- package/content/integrations/mollie.json +10 -0
- package/dist/control-plane/index.mjs +125 -37
- package/dist/mcp-server/http.mjs +772 -420
- package/dist/ui/assets/index-C2BnlNib.js +123 -0
- package/dist/ui/assets/{index-DUIlMVlP.css → index-DPgewsLG.css} +1 -1
- package/dist/ui/index.html +2 -2
- package/node_modules/qs/.github/THREAT_MODEL.md +4 -2
- package/node_modules/qs/CHANGELOG.md +38 -14
- package/node_modules/qs/README.md +31 -3
- package/node_modules/qs/dist/qs.js +20 -20
- package/node_modules/qs/lib/parse.js +94 -4
- package/node_modules/qs/lib/stringify.js +4 -4
- package/node_modules/qs/lib/utils.js +130 -6
- package/node_modules/qs/package.json +4 -4
- package/node_modules/qs/test/parse.js +174 -0
- package/node_modules/qs/test/stringify.js +263 -33
- package/node_modules/qs/test/utils.js +184 -0
- package/node_modules/sanitize-html/index.js +47 -10
- package/node_modules/sanitize-html/package.json +2 -3
- package/package.json +1 -1
- package/dist/ui/assets/index-C3vTF2Ye.js +0 -123
- package/node_modules/deepmerge/.editorconfig +0 -7
- package/node_modules/deepmerge/.eslintcache +0 -1
- package/node_modules/deepmerge/changelog.md +0 -167
- package/node_modules/deepmerge/dist/cjs.js +0 -133
- package/node_modules/deepmerge/dist/umd.js +0 -139
- package/node_modules/deepmerge/index.d.ts +0 -20
- package/node_modules/deepmerge/index.js +0 -106
- package/node_modules/deepmerge/license.txt +0 -21
- package/node_modules/deepmerge/package.json +0 -42
- package/node_modules/deepmerge/readme.md +0 -264
- package/node_modules/deepmerge/rollup.config.js +0 -22
|
@@ -145,6 +145,8 @@
|
|
|
145
145
|
},
|
|
146
146
|
"list_messages": {
|
|
147
147
|
"category": "read",
|
|
148
|
+
"boundField": "read_access",
|
|
149
|
+
"requiredValue": "unlimited",
|
|
148
150
|
"read": {
|
|
149
151
|
"ageField": "read_age_days",
|
|
150
152
|
"queryArg": "q",
|
|
@@ -158,6 +160,8 @@
|
|
|
158
160
|
},
|
|
159
161
|
"get_message": {
|
|
160
162
|
"category": "read",
|
|
163
|
+
"boundField": "read_access",
|
|
164
|
+
"requiredValue": "unlimited",
|
|
161
165
|
"read": {
|
|
162
166
|
"ageField": "read_age_days",
|
|
163
167
|
"resultDatePath": "internalDate",
|
|
@@ -175,6 +179,8 @@
|
|
|
175
179
|
},
|
|
176
180
|
"list_drafts": {
|
|
177
181
|
"category": "read",
|
|
182
|
+
"boundField": "read_access",
|
|
183
|
+
"requiredValue": "unlimited",
|
|
178
184
|
"read": {
|
|
179
185
|
"ageField": "read_age_days",
|
|
180
186
|
"queryArg": "q",
|
|
@@ -187,6 +193,8 @@
|
|
|
187
193
|
},
|
|
188
194
|
"get_draft": {
|
|
189
195
|
"category": "read",
|
|
196
|
+
"boundField": "read_access",
|
|
197
|
+
"requiredValue": "unlimited",
|
|
190
198
|
"read": {
|
|
191
199
|
"ageField": "read_age_days",
|
|
192
200
|
"resultDatePath": "message.internalDate",
|
|
@@ -207,8 +215,11 @@
|
|
|
207
215
|
},
|
|
208
216
|
"get_attachment": {
|
|
209
217
|
"category": "read",
|
|
210
|
-
"
|
|
211
|
-
"
|
|
218
|
+
"boundField": "read_access",
|
|
219
|
+
"requiredValue": "unlimited",
|
|
220
|
+
"read": {
|
|
221
|
+
"ageField": "read_age_days"
|
|
222
|
+
}
|
|
212
223
|
},
|
|
213
224
|
"list_labels": {
|
|
214
225
|
"category": "read",
|
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
"profile": "email",
|
|
8
8
|
"npmPackage": "@humanagencyp/email-mcp",
|
|
9
9
|
"npmVersion": "0.3.0",
|
|
10
|
+
"readPolicy": {
|
|
11
|
+
"defaultAgeDays": 30
|
|
12
|
+
},
|
|
10
13
|
"mcp": {
|
|
11
14
|
"command": "email-mcp",
|
|
12
15
|
"args": []
|
|
@@ -61,12 +64,19 @@
|
|
|
61
64
|
"list_messages": {
|
|
62
65
|
"category": "read",
|
|
63
66
|
"boundField": "read_access",
|
|
64
|
-
"requiredValue": "unlimited"
|
|
67
|
+
"requiredValue": "unlimited",
|
|
68
|
+
"read": {
|
|
69
|
+
"ageField": "read_age_days"
|
|
70
|
+
}
|
|
65
71
|
},
|
|
66
72
|
"get_message": {
|
|
67
73
|
"category": "read",
|
|
68
74
|
"boundField": "read_access",
|
|
69
|
-
"requiredValue": "unlimited"
|
|
75
|
+
"requiredValue": "unlimited",
|
|
76
|
+
"read": {
|
|
77
|
+
"ageField": "read_age_days",
|
|
78
|
+
"resultDatePath": "received_at"
|
|
79
|
+
}
|
|
70
80
|
},
|
|
71
81
|
"send_message": {
|
|
72
82
|
"executionMapping": {
|
|
@@ -129,6 +129,16 @@
|
|
|
129
129
|
"staticExecution": {
|
|
130
130
|
"action_type": "charge"
|
|
131
131
|
}
|
|
132
|
+
},
|
|
133
|
+
"ListProfiles": {
|
|
134
|
+
"category": "read",
|
|
135
|
+
"readGovernance": "none",
|
|
136
|
+
"readGovernanceReason": "Returns the organisation's own payment profile configuration (names, websites, status) — no customer or payment data."
|
|
137
|
+
},
|
|
138
|
+
"GetOrganizationDetails": {
|
|
139
|
+
"category": "read",
|
|
140
|
+
"readGovernance": "none",
|
|
141
|
+
"readGovernanceReason": "Returns the connected Mollie organisation's own name/registration details — no customer or payment data."
|
|
132
142
|
}
|
|
133
143
|
}
|
|
134
144
|
},
|
|
@@ -5,9 +5,9 @@ import {
|
|
|
5
5
|
|
|
6
6
|
// src/index.ts
|
|
7
7
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
8
|
-
import { existsSync as existsSync13, readFileSync as
|
|
9
|
-
import { dirname as
|
|
10
|
-
import { homedir as
|
|
8
|
+
import { existsSync as existsSync13, readFileSync as readFileSync16, renameSync as renameSync4 } from "fs";
|
|
9
|
+
import { dirname as dirname11, join as join16 } from "path";
|
|
10
|
+
import { homedir as homedir9 } from "os";
|
|
11
11
|
import express from "express";
|
|
12
12
|
import { createProxyMiddleware } from "http-proxy-middleware";
|
|
13
13
|
|
|
@@ -643,6 +643,13 @@ async function getSkippedCommitments() {
|
|
|
643
643
|
if (!res.ok) throw new Error("Failed to fetch skipped commitments");
|
|
644
644
|
return res.json();
|
|
645
645
|
}
|
|
646
|
+
async function getMyPendingProposals() {
|
|
647
|
+
const res = await fetch(`${MCP_BASE}/internal/my-pending-proposals`, {
|
|
648
|
+
headers: internalHeaders()
|
|
649
|
+
});
|
|
650
|
+
if (!res.ok) throw new Error("Failed to fetch my pending proposals");
|
|
651
|
+
return res.json();
|
|
652
|
+
}
|
|
646
653
|
async function getLocalEvidence() {
|
|
647
654
|
const res = await fetch(`${MCP_BASE}/internal/evidence`, {
|
|
648
655
|
headers: internalHeaders()
|
|
@@ -2258,6 +2265,7 @@ function createAuthRouter(vault2, logoutAuth, loginRateLimit2, onSessionEstablis
|
|
|
2258
2265
|
}
|
|
2259
2266
|
vault2.wipe();
|
|
2260
2267
|
await vault2.deriveAndSetKey(apiKey);
|
|
2268
|
+
vault2.setSpCookie(sessionCookie);
|
|
2261
2269
|
}
|
|
2262
2270
|
const sessionExpiresAt = typeof data.sessionExpiresAt === "number" ? data.sessionExpiresAt : null;
|
|
2263
2271
|
vault2.setSessionExpiresAt(sessionExpiresAt);
|
|
@@ -3376,15 +3384,15 @@ import { promisify } from "util";
|
|
|
3376
3384
|
var run = promisify(execFile);
|
|
3377
3385
|
var SUPPORTED = /* @__PURE__ */ new Set(["darwin", "win32", "linux"]);
|
|
3378
3386
|
var MODULE_DIR = pathDirname(fileURLToPath(import.meta.url));
|
|
3379
|
-
function findCli(
|
|
3387
|
+
function findCli(dirname12 = MODULE_DIR) {
|
|
3380
3388
|
const candidates = [
|
|
3381
3389
|
// Bundled (the shipped layout): dist/control-plane → dist → root → bin
|
|
3382
|
-
resolve(
|
|
3390
|
+
resolve(dirname12, "..", "..", "bin", "suveren-gateway.js"),
|
|
3383
3391
|
// Bundled, were the routes/ directory ever preserved.
|
|
3384
|
-
resolve(
|
|
3392
|
+
resolve(dirname12, "..", "..", "..", "bin", "suveren-gateway.js"),
|
|
3385
3393
|
// Dev, running from source: the repo's own bundle directory.
|
|
3386
|
-
resolve(
|
|
3387
|
-
resolve(
|
|
3394
|
+
resolve(dirname12, "..", "..", "..", "..", "bundle", "bin", "suveren-gateway.js"),
|
|
3395
|
+
resolve(dirname12, "..", "..", "..", "..", "..", "bundle", "bin", "suveren-gateway.js")
|
|
3388
3396
|
// Deliberately NO process.cwd() fallback: it makes the result depend on
|
|
3389
3397
|
// where the process happened to be launched from, so the same install
|
|
3390
3398
|
// resolves differently between a shell and a login service — and it would
|
|
@@ -4622,10 +4630,62 @@ function createArchivedMandatesRouter(dataDir) {
|
|
|
4622
4630
|
return router;
|
|
4623
4631
|
}
|
|
4624
4632
|
|
|
4625
|
-
// src/routes/
|
|
4633
|
+
// src/routes/blocked-seen.ts
|
|
4626
4634
|
import { Router as Router19 } from "express";
|
|
4635
|
+
|
|
4636
|
+
// src/lib/blocked-seen-store.ts
|
|
4637
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync9, mkdirSync as mkdirSync9, renameSync as renameSync3 } from "fs";
|
|
4638
|
+
import { homedir as homedir8 } from "os";
|
|
4639
|
+
import { join as join14, dirname as dirname10 } from "path";
|
|
4640
|
+
function storePath2(dataDir) {
|
|
4641
|
+
const dir = dataDir ?? process.env.SUVEREN_DATA_DIR ?? join14(homedir8(), ".suveren");
|
|
4642
|
+
return join14(dir, "blocked-seen.json");
|
|
4643
|
+
}
|
|
4644
|
+
function readLastSeenBlockedAt(dataDir) {
|
|
4645
|
+
try {
|
|
4646
|
+
const raw = JSON.parse(readFileSync14(storePath2(dataDir), "utf8"));
|
|
4647
|
+
return typeof raw.lastSeenBlockedAt === "number" && Number.isFinite(raw.lastSeenBlockedAt) ? raw.lastSeenBlockedAt : 0;
|
|
4648
|
+
} catch {
|
|
4649
|
+
return 0;
|
|
4650
|
+
}
|
|
4651
|
+
}
|
|
4652
|
+
function markBlockedSeen(ts, dataDir) {
|
|
4653
|
+
const safeTs = typeof ts === "number" && Number.isFinite(ts) ? ts : Date.now();
|
|
4654
|
+
const path = storePath2(dataDir);
|
|
4655
|
+
mkdirSync9(dirname10(path), { recursive: true });
|
|
4656
|
+
const tmp = `${path}.tmp`;
|
|
4657
|
+
writeFileSync9(tmp, JSON.stringify({ lastSeenBlockedAt: safeTs }, null, 2) + "\n", "utf8");
|
|
4658
|
+
renameSync3(tmp, path);
|
|
4659
|
+
return safeTs;
|
|
4660
|
+
}
|
|
4661
|
+
|
|
4662
|
+
// src/routes/blocked-seen.ts
|
|
4663
|
+
function createBlockedSeenRouter(dataDir) {
|
|
4664
|
+
const router = Router19();
|
|
4665
|
+
router.get("/", (_req, res) => {
|
|
4666
|
+
res.json({ lastSeenBlockedAt: readLastSeenBlockedAt(dataDir) });
|
|
4667
|
+
});
|
|
4668
|
+
router.post("/", (req, res) => {
|
|
4669
|
+
const body = req.body;
|
|
4670
|
+
const ts = typeof body?.ts === "number" && Number.isFinite(body.ts) ? body.ts : Date.now();
|
|
4671
|
+
try {
|
|
4672
|
+
res.json({ lastSeenBlockedAt: markBlockedSeen(ts, dataDir) });
|
|
4673
|
+
} catch (err) {
|
|
4674
|
+
res.status(500).json({ error: `Could not save: ${err.message}` });
|
|
4675
|
+
}
|
|
4676
|
+
});
|
|
4677
|
+
return router;
|
|
4678
|
+
}
|
|
4679
|
+
|
|
4680
|
+
// src/routes/internal-events.ts
|
|
4681
|
+
import { Router as Router20 } from "express";
|
|
4627
4682
|
import { timingSafeEqual as timingSafeEqual3 } from "crypto";
|
|
4628
|
-
var ALLOWED = [
|
|
4683
|
+
var ALLOWED = [
|
|
4684
|
+
"proposal-added",
|
|
4685
|
+
"action-approval-needed",
|
|
4686
|
+
"execution-failed",
|
|
4687
|
+
"denial-recorded"
|
|
4688
|
+
];
|
|
4629
4689
|
var SESSION_EXPIRED = "session-expired";
|
|
4630
4690
|
var AS_KEY_MISMATCH = "as-key-mismatch";
|
|
4631
4691
|
var AS_TLS_MISMATCH = "as-tls-mismatch";
|
|
@@ -4638,7 +4698,7 @@ function secretMatches2(provided, expected) {
|
|
|
4638
4698
|
return timingSafeEqual3(a, b);
|
|
4639
4699
|
}
|
|
4640
4700
|
function createInternalEventsRouter(getSecret, onSessionExpired, onAsKeyMismatch, onAsTlsMismatch) {
|
|
4641
|
-
const router =
|
|
4701
|
+
const router = Router20();
|
|
4642
4702
|
router.post("/event", (req, res) => {
|
|
4643
4703
|
if (!secretMatches2(req.headers["x-internal-secret"], getSecret())) {
|
|
4644
4704
|
res.status(401).json({ error: "Unauthorized" });
|
|
@@ -4672,7 +4732,7 @@ function createInternalEventsRouter(getSecret, onSessionExpired, onAsKeyMismatch
|
|
|
4672
4732
|
}
|
|
4673
4733
|
|
|
4674
4734
|
// src/routes/internal-mandate.ts
|
|
4675
|
-
import { Router as
|
|
4735
|
+
import { Router as Router21 } from "express";
|
|
4676
4736
|
import { timingSafeEqual as timingSafeEqual4, randomUUID } from "crypto";
|
|
4677
4737
|
|
|
4678
4738
|
// src/lib/mandate-ceremony.ts
|
|
@@ -4854,7 +4914,7 @@ function secretMatches3(provided, expected) {
|
|
|
4854
4914
|
return a.length === b.length && timingSafeEqual4(a, b);
|
|
4855
4915
|
}
|
|
4856
4916
|
function createInternalMandateRouter(getSecret, deps) {
|
|
4857
|
-
const router =
|
|
4917
|
+
const router = Router21();
|
|
4858
4918
|
router.post("/mandate", async (req, res) => {
|
|
4859
4919
|
if (!secretMatches3(req.header("X-Internal-Secret") ?? void 0, getSecret())) {
|
|
4860
4920
|
res.status(401).json({ error: "unauthorized" });
|
|
@@ -4887,9 +4947,13 @@ function createInternalMandateRouter(getSecret, deps) {
|
|
|
4887
4947
|
}
|
|
4888
4948
|
|
|
4889
4949
|
// src/lib/notification-dispatcher.ts
|
|
4890
|
-
var
|
|
4950
|
+
var REVIEW_EVENTS = ["proposal-added", "action-approval-needed"];
|
|
4951
|
+
var FAILURE_EVENTS = ["execution-failed"];
|
|
4952
|
+
var TRIGGER_EVENTS = [...REVIEW_EVENTS, ...FAILURE_EVENTS];
|
|
4891
4953
|
var TITLE = "Suveren";
|
|
4892
4954
|
var WAITING_MESSAGE = "Something is waiting for your review.";
|
|
4955
|
+
var FAILURE_MESSAGE = "An approved action failed.";
|
|
4956
|
+
var MIXED_MESSAGE = "Something needs your attention.";
|
|
4893
4957
|
var FLOOD_THRESHOLD = 10;
|
|
4894
4958
|
var floodMessage = (n) => `Unusual volume: ${n} requests in the last minute.`;
|
|
4895
4959
|
var DEBOUNCE_MS = 5e3;
|
|
@@ -4900,6 +4964,9 @@ var NotificationDispatcher = class {
|
|
|
4900
4964
|
pendingWhileCoolingDown = false;
|
|
4901
4965
|
/** Events observed since the current window opened — the flood signal only. */
|
|
4902
4966
|
observedInWindow = 0;
|
|
4967
|
+
/** Which kinds were observed since the current window opened — selects the message. */
|
|
4968
|
+
reviewSeenInWindow = false;
|
|
4969
|
+
failureSeenInWindow = false;
|
|
4903
4970
|
unsubscribe = null;
|
|
4904
4971
|
now;
|
|
4905
4972
|
setTimeoutFn;
|
|
@@ -4917,7 +4984,8 @@ var NotificationDispatcher = class {
|
|
|
4917
4984
|
start() {
|
|
4918
4985
|
if (this.unsubscribe) return this.unsubscribe;
|
|
4919
4986
|
const off = eventBus.subscribe((event) => {
|
|
4920
|
-
if (
|
|
4987
|
+
if (FAILURE_EVENTS.includes(event.type)) this.onTrigger("failure");
|
|
4988
|
+
else if (REVIEW_EVENTS.includes(event.type)) this.onTrigger("review");
|
|
4921
4989
|
});
|
|
4922
4990
|
this.unsubscribe = () => {
|
|
4923
4991
|
off();
|
|
@@ -4929,9 +4997,15 @@ var NotificationDispatcher = class {
|
|
|
4929
4997
|
};
|
|
4930
4998
|
return this.unsubscribe;
|
|
4931
4999
|
}
|
|
4932
|
-
/**
|
|
4933
|
-
|
|
5000
|
+
/**
|
|
5001
|
+
* Exposed for tests; the bus subscription calls this. `kind` defaults to
|
|
5002
|
+
* 'review' so every existing call site/test (written before FAILURE_EVENTS
|
|
5003
|
+
* existed) keeps its exact behaviour.
|
|
5004
|
+
*/
|
|
5005
|
+
onTrigger(kind = "review") {
|
|
4934
5006
|
this.observedInWindow += 1;
|
|
5007
|
+
if (kind === "failure") this.failureSeenInWindow = true;
|
|
5008
|
+
else this.reviewSeenInWindow = true;
|
|
4935
5009
|
if (this.cooldownTimer) {
|
|
4936
5010
|
this.pendingWhileCoolingDown = true;
|
|
4937
5011
|
return;
|
|
@@ -4944,7 +5018,11 @@ var NotificationDispatcher = class {
|
|
|
4944
5018
|
}
|
|
4945
5019
|
fire() {
|
|
4946
5020
|
const count = this.observedInWindow;
|
|
5021
|
+
const sawReview = this.reviewSeenInWindow;
|
|
5022
|
+
const sawFailure = this.failureSeenInWindow;
|
|
4947
5023
|
this.observedInWindow = 0;
|
|
5024
|
+
this.reviewSeenInWindow = false;
|
|
5025
|
+
this.failureSeenInWindow = false;
|
|
4948
5026
|
let enabled = true;
|
|
4949
5027
|
try {
|
|
4950
5028
|
enabled = this.settingsFn().desktopNotifications;
|
|
@@ -4955,7 +5033,7 @@ var NotificationDispatcher = class {
|
|
|
4955
5033
|
`[Control Plane] Review pending \u2014 desktop notification ${enabled ? "sent" : "suppressed (turned off)"}`
|
|
4956
5034
|
);
|
|
4957
5035
|
if (enabled) {
|
|
4958
|
-
const message = count >= FLOOD_THRESHOLD ? floodMessage(count) : WAITING_MESSAGE;
|
|
5036
|
+
const message = count >= FLOOD_THRESHOLD ? floodMessage(count) : sawReview && sawFailure ? MIXED_MESSAGE : sawFailure ? FAILURE_MESSAGE : WAITING_MESSAGE;
|
|
4959
5037
|
this.notifyFn(TITLE, message, process.platform, this.url);
|
|
4960
5038
|
}
|
|
4961
5039
|
this.cooldownTimer = this.setTimeoutFn(() => {
|
|
@@ -5039,17 +5117,17 @@ function buildSessionHealth(vault2) {
|
|
|
5039
5117
|
}
|
|
5040
5118
|
|
|
5041
5119
|
// src/lib/denials-reader.ts
|
|
5042
|
-
import { existsSync as existsSync12, readFileSync as
|
|
5043
|
-
import { join as
|
|
5120
|
+
import { existsSync as existsSync12, readFileSync as readFileSync15 } from "fs";
|
|
5121
|
+
import { join as join15 } from "path";
|
|
5044
5122
|
function loadDenials(dataDir, decrypt) {
|
|
5045
|
-
const encPath =
|
|
5046
|
-
const plainPath =
|
|
5123
|
+
const encPath = join15(dataDir, "denials.enc.json");
|
|
5124
|
+
const plainPath = join15(dataDir, "denials.json");
|
|
5047
5125
|
if (existsSync12(encPath)) {
|
|
5048
|
-
const { blob } = JSON.parse(
|
|
5126
|
+
const { blob } = JSON.parse(readFileSync15(encPath, "utf-8"));
|
|
5049
5127
|
return JSON.parse(decrypt(blob)).records ?? [];
|
|
5050
5128
|
}
|
|
5051
5129
|
if (existsSync12(plainPath)) {
|
|
5052
|
-
return JSON.parse(
|
|
5130
|
+
return JSON.parse(readFileSync15(plainPath, "utf-8")).records ?? [];
|
|
5053
5131
|
}
|
|
5054
5132
|
return [];
|
|
5055
5133
|
}
|
|
@@ -5073,11 +5151,11 @@ function isSimulationMode() {
|
|
|
5073
5151
|
// src/index.ts
|
|
5074
5152
|
(function migrateDataDir() {
|
|
5075
5153
|
if (process.env.SUVEREN_DATA_DIR) return;
|
|
5076
|
-
const oldDir =
|
|
5077
|
-
const newDir =
|
|
5154
|
+
const oldDir = join16(homedir9(), ".hap");
|
|
5155
|
+
const newDir = join16(homedir9(), ".suveren");
|
|
5078
5156
|
if (!existsSync13(newDir) && existsSync13(oldDir)) {
|
|
5079
5157
|
try {
|
|
5080
|
-
|
|
5158
|
+
renameSync4(oldDir, newDir);
|
|
5081
5159
|
console.error(`[Control Plane] Migrated data directory: ${oldDir} \u2192 ${newDir}`);
|
|
5082
5160
|
} catch (err) {
|
|
5083
5161
|
console.error(`[Control Plane] FATAL: could not migrate data directory ${oldDir} \u2192 ${newDir}:`, err);
|
|
@@ -5085,7 +5163,7 @@ function isSimulationMode() {
|
|
|
5085
5163
|
}
|
|
5086
5164
|
}
|
|
5087
5165
|
})();
|
|
5088
|
-
var DATA_DIR = process.env.SUVEREN_DATA_DIR ??
|
|
5166
|
+
var DATA_DIR = process.env.SUVEREN_DATA_DIR ?? join16(homedir9(), ".suveren");
|
|
5089
5167
|
var SP_URL = resolveAsUrl(DATA_DIR);
|
|
5090
5168
|
validatePinTlsForUrl(resolvePinTls(DATA_DIR), SP_URL);
|
|
5091
5169
|
var POLICY_LOCKED = [...readPolicy().locked];
|
|
@@ -5093,7 +5171,7 @@ var POLICY_LOCKED_INSTALL_METHOD = readPolicy().policy.installMethod;
|
|
|
5093
5171
|
var POLICY_LOCKED_TEAM = readPolicy().policy.team;
|
|
5094
5172
|
var port = parseInt(process.env.SUVEREN_CP_PORT ?? "3402", 10);
|
|
5095
5173
|
var HAP_MODE = process.env.HAP_MODE ?? "personal";
|
|
5096
|
-
var UI_DIST = process.env.HAP_UI_DIST ??
|
|
5174
|
+
var UI_DIST = process.env.HAP_UI_DIST ?? join16(import.meta.dirname ?? __dirname, "../../ui/dist");
|
|
5097
5175
|
var vault = new Vault(DATA_DIR);
|
|
5098
5176
|
var shownSnapshotStore = new ShownSnapshotStore(DATA_DIR);
|
|
5099
5177
|
(function repairIfAsUrlChanged() {
|
|
@@ -5402,6 +5480,7 @@ app.use("/mcp", jsonParser, authGuard, createMCPRouter());
|
|
|
5402
5480
|
app.use("/autostart", jsonParser, authGuard, createAutostartRouter());
|
|
5403
5481
|
app.use("/api/gateway-settings", jsonParser, authGuard, createGatewaySettingsRouter());
|
|
5404
5482
|
app.use("/api/archived-mandates", jsonParser, authGuard, createArchivedMandatesRouter());
|
|
5483
|
+
app.use("/api/blocked-seen", jsonParser, authGuard, createBlockedSeenRouter());
|
|
5405
5484
|
app.use("/api/encrypt-intent", jsonParser, authGuard, createEncryptIntentRouter());
|
|
5406
5485
|
app.use("/api/evidence-export", authGuard, createEvidenceExportRouter(SP_URL, vault, DATA_DIR, lockAsTlsMismatch));
|
|
5407
5486
|
app.use("/proposal-status", jsonParser, authGuard, createProposalStatusRouter(SP_URL, vault, DATA_DIR, lockAsTlsMismatch, shownSnapshotStore));
|
|
@@ -5546,6 +5625,15 @@ app.get("/skipped-commitments", authGuard, async (_req, res) => {
|
|
|
5546
5625
|
res.status(500).json({ error: "Failed to fetch skipped commitments from MCP server" });
|
|
5547
5626
|
}
|
|
5548
5627
|
});
|
|
5628
|
+
app.get("/my-pending-proposals", authGuard, async (_req, res) => {
|
|
5629
|
+
try {
|
|
5630
|
+
const data = await getMyPendingProposals();
|
|
5631
|
+
res.json(data);
|
|
5632
|
+
} catch (err) {
|
|
5633
|
+
console.error("[Control Plane] My-pending-proposals retrieval error:", err);
|
|
5634
|
+
res.status(500).json({ error: "Failed to fetch pending proposals from MCP server" });
|
|
5635
|
+
}
|
|
5636
|
+
});
|
|
5549
5637
|
app.get("/as-pairing", authGuard, (_req, res) => {
|
|
5550
5638
|
const pairing = readPairing(DATA_DIR);
|
|
5551
5639
|
const paired = pairing !== null && pairing.asUrl === SP_URL;
|
|
@@ -5569,7 +5657,7 @@ app.get("/as-pairing", authGuard, (_req, res) => {
|
|
|
5569
5657
|
});
|
|
5570
5658
|
app.get("/denials", authGuard, (req, res) => {
|
|
5571
5659
|
try {
|
|
5572
|
-
const dataDir = process.env.SUVEREN_DATA_DIR ??
|
|
5660
|
+
const dataDir = process.env.SUVEREN_DATA_DIR ?? join16(homedir9(), ".suveren");
|
|
5573
5661
|
const all = loadDenials(dataDir, (blob) => vault.decrypt(blob));
|
|
5574
5662
|
const since = Number(req.query.since);
|
|
5575
5663
|
const limit = Number(req.query.limit);
|
|
@@ -5628,12 +5716,12 @@ app.put("/agent-brief/context", jsonParser, authGuard, async (req, res) => {
|
|
|
5628
5716
|
res.status(413).json({ error: `Context exceeds ${AGENT_CONTEXT_MAX_BYTES}-byte cap` });
|
|
5629
5717
|
return;
|
|
5630
5718
|
}
|
|
5631
|
-
const { mkdirSync:
|
|
5719
|
+
const { mkdirSync: mkdirSync10, writeFileSync: writeFileSync10, renameSync: renameSync5 } = await import("fs");
|
|
5632
5720
|
const filePath = agentBriefPath();
|
|
5633
|
-
|
|
5721
|
+
mkdirSync10(dirname11(filePath), { recursive: true });
|
|
5634
5722
|
const tmpPath = `${filePath}.tmp`;
|
|
5635
|
-
|
|
5636
|
-
|
|
5723
|
+
writeFileSync10(tmpPath, content, "utf-8");
|
|
5724
|
+
renameSync5(tmpPath, filePath);
|
|
5637
5725
|
res.json({ ok: true });
|
|
5638
5726
|
} catch (err) {
|
|
5639
5727
|
console.error("[Control Plane] agent-brief/context PUT failed:", err);
|
|
@@ -5725,10 +5813,10 @@ function detectRunningVersion() {
|
|
|
5725
5813
|
return process.env.HAP_BUILD_SHA ?? "dev";
|
|
5726
5814
|
}
|
|
5727
5815
|
const dir = import.meta.dirname ?? __dirname;
|
|
5728
|
-
const bundlePkg =
|
|
5816
|
+
const bundlePkg = join16(dir, "..", "..", "package.json");
|
|
5729
5817
|
if (existsSync13(bundlePkg)) {
|
|
5730
5818
|
try {
|
|
5731
|
-
const pkg = JSON.parse(
|
|
5819
|
+
const pkg = JSON.parse(readFileSync16(bundlePkg, "utf8"));
|
|
5732
5820
|
if (pkg.name === "@suveren/gateway" && typeof pkg.version === "string") {
|
|
5733
5821
|
return pkg.version;
|
|
5734
5822
|
}
|
|
@@ -5780,7 +5868,7 @@ if (existsSync13(UI_DIST)) {
|
|
|
5780
5868
|
}));
|
|
5781
5869
|
app.get("*", (_req, res) => {
|
|
5782
5870
|
res.setHeader("Cache-Control", "no-store, must-revalidate");
|
|
5783
|
-
res.sendFile(
|
|
5871
|
+
res.sendFile(join16(UI_DIST, "index.html"));
|
|
5784
5872
|
});
|
|
5785
5873
|
} else {
|
|
5786
5874
|
app.get("/", (_req, res) => {
|