@suveren/gateway 0.7.4 → 0.7.6
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 +248 -58
- package/dist/mcp-server/http.mjs +408 -65
- package/dist/ui/assets/{index-CkWaRck1.css → index-BYVb_0oG.css} +1 -1
- package/dist/ui/assets/index-CsrM6RQr.js +110 -0
- package/dist/ui/index.html +2 -2
- package/node_modules/@hap/core/dist/index.d.mts +258 -9
- package/node_modules/@hap/core/dist/index.d.ts +258 -9
- package/node_modules/@hap/core/dist/index.js +227 -5
- package/node_modules/@hap/core/dist/index.mjs +213 -4
- package/node_modules/@hap/core/package.json +3 -2
- package/node_modules/@hap/core/src/did-key.ts +126 -0
- package/node_modules/@hap/core/src/identity.ts +21 -5
- package/node_modules/@hap/core/src/index.ts +3 -0
- package/node_modules/@hap/core/src/mandate.ts +207 -0
- package/node_modules/@hap/core/src/receipt.ts +74 -0
- package/node_modules/@hap/core/src/types.ts +81 -5
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/accept.js +54 -0
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/charset.js +10 -34
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/encoding.js +9 -30
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/language.js +15 -36
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/mediaType.js +16 -134
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/LICENSE +22 -0
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/README.md +71 -0
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/dist/index.d.ts +46 -0
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/dist/index.js +176 -0
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/dist/index.js.map +1 -0
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/package.json +52 -0
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/package.json +16 -10
- package/node_modules/@types/node/README.md +1 -1
- package/node_modules/@types/node/buffer.buffer.d.ts +5 -5
- package/node_modules/@types/node/crypto.d.ts +2 -2
- package/node_modules/@types/node/http.d.ts +16 -0
- package/node_modules/@types/node/http2.d.ts +10 -2
- package/node_modules/@types/node/package.json +2 -2
- package/node_modules/@types/node/process.d.ts +52 -0
- package/node_modules/@types/node/quic.d.ts +210 -28
- package/node_modules/@types/node/sqlite.d.ts +6 -2
- package/node_modules/@types/node/test.d.ts +36 -0
- package/node_modules/fast-uri/index.js +201 -48
- package/node_modules/fast-uri/lib/schemes.js +9 -4
- package/node_modules/fast-uri/lib/utils.js +388 -91
- package/node_modules/fast-uri/package.json +1 -1
- package/node_modules/fast-uri/test/component-safe-serialization.test.js +105 -0
- package/node_modules/fast-uri/test/equal.test.js +31 -3
- package/node_modules/fast-uri/test/fixtures/uri-js-parse.json +2 -0
- package/node_modules/fast-uri/test/ipv6-canonical.test.js +34 -0
- package/node_modules/fast-uri/test/ipv6-validation.test.js +90 -0
- package/node_modules/fast-uri/test/malformed-percent.test.js +77 -0
- package/node_modules/fast-uri/test/malformed-urn.test.js +61 -0
- package/node_modules/fast-uri/test/parse.test.js +7 -3
- package/node_modules/fast-uri/test/query-fragment-normalization.test.js +33 -0
- package/node_modules/fast-uri/test/reserved-path-normalization.test.js +109 -0
- package/node_modules/fast-uri/test/scheme-validation.test.js +124 -0
- package/node_modules/fast-uri/test/security-normalization.test.js +101 -0
- package/node_modules/fast-uri/test/security.test.js +75 -3
- package/node_modules/fast-uri/test/urn-full-input.test.js +29 -0
- package/node_modules/fast-uri/test/websocket-query-preservation.test.js +24 -0
- package/node_modules/hono/dist/cjs/client/client.js +25 -11
- package/node_modules/hono/dist/cjs/client/utils.js +4 -1
- package/node_modules/hono/dist/cjs/context.js +4 -0
- package/node_modules/hono/dist/cjs/helper/accepts/accepts.js +36 -2
- package/node_modules/hono/dist/cjs/helper/ssg/ssg.js +1 -1
- package/node_modules/hono/dist/cjs/helper/ssg/utils.js +30 -10
- package/node_modules/hono/dist/cjs/jsx/dom/render.js +2 -0
- package/node_modules/hono/dist/cjs/middleware/cache/index.js +1 -1
- package/node_modules/hono/dist/cjs/middleware/cors/index.js +1 -1
- package/node_modules/hono/dist/cjs/middleware/csrf/index.js +1 -1
- package/node_modules/hono/dist/cjs/middleware/etag/digest.js +1 -1
- package/node_modules/hono/dist/cjs/middleware/etag/index.js +3 -3
- package/node_modules/hono/dist/cjs/middleware/pretty-json/index.js +3 -1
- package/node_modules/hono/dist/cjs/request.js +8 -4
- package/node_modules/hono/dist/cjs/router/linear-router/router.js +7 -2
- package/node_modules/hono/dist/cjs/router/pattern-router/router.js +3 -9
- package/node_modules/hono/dist/cjs/router/reg-exp-router/node.js +10 -3
- package/node_modules/hono/dist/cjs/router/reg-exp-router/router.js +47 -83
- package/node_modules/hono/dist/cjs/router/reg-exp-router/trie.js +2 -1
- package/node_modules/hono/dist/cjs/router/trie-router/node.js +46 -69
- package/node_modules/hono/dist/cjs/router/trie-router/router.js +3 -11
- package/node_modules/hono/dist/cjs/router/utils.js +27 -0
- package/node_modules/hono/dist/cjs/utils/body.js +15 -3
- package/node_modules/hono/dist/cjs/utils/cookie.js +1 -1
- package/node_modules/hono/dist/cjs/utils/ipaddr.js +5 -3
- package/node_modules/hono/dist/cjs/utils/stream.js +7 -1
- package/node_modules/hono/dist/cjs/utils/url.js +9 -1
- package/node_modules/hono/dist/client/client.js +25 -11
- package/node_modules/hono/dist/client/utils.js +4 -1
- package/node_modules/hono/dist/context.js +4 -0
- package/node_modules/hono/dist/helper/accepts/accepts.js +36 -2
- package/node_modules/hono/dist/helper/ssg/ssg.js +1 -1
- package/node_modules/hono/dist/helper/ssg/utils.js +30 -10
- package/node_modules/hono/dist/jsx/dom/render.js +2 -0
- package/node_modules/hono/dist/middleware/cache/index.js +1 -1
- package/node_modules/hono/dist/middleware/cors/index.js +1 -1
- package/node_modules/hono/dist/middleware/csrf/index.js +1 -1
- package/node_modules/hono/dist/middleware/etag/digest.js +1 -1
- package/node_modules/hono/dist/middleware/etag/index.js +3 -3
- package/node_modules/hono/dist/middleware/pretty-json/index.js +3 -1
- package/node_modules/hono/dist/request.js +8 -4
- package/node_modules/hono/dist/router/linear-router/router.js +7 -2
- package/node_modules/hono/dist/router/pattern-router/router.js +3 -9
- package/node_modules/hono/dist/router/reg-exp-router/node.js +6 -2
- package/node_modules/hono/dist/router/reg-exp-router/router.js +53 -84
- package/node_modules/hono/dist/router/reg-exp-router/trie.js +2 -1
- package/node_modules/hono/dist/router/trie-router/node.js +46 -69
- package/node_modules/hono/dist/router/trie-router/router.js +3 -11
- package/node_modules/hono/dist/router/utils.js +5 -0
- package/node_modules/hono/dist/types/context.d.ts +4 -0
- package/node_modules/hono/dist/types/router/reg-exp-router/node.d.ts +3 -0
- package/node_modules/hono/dist/types/router/trie-router/node.d.ts +1 -2
- package/node_modules/hono/dist/types/router/trie-router/router.d.ts +0 -1
- package/node_modules/hono/dist/types/router/utils.d.ts +1 -0
- package/node_modules/hono/dist/types/utils/url.d.ts +4 -0
- package/node_modules/hono/dist/utils/body.js +15 -3
- package/node_modules/hono/dist/utils/cookie.js +1 -1
- package/node_modules/hono/dist/utils/ipaddr.js +5 -3
- package/node_modules/hono/dist/utils/stream.js +7 -1
- package/node_modules/hono/dist/utils/url.js +9 -1
- package/node_modules/hono/package.json +1 -1
- package/node_modules/is-plain-object/README.md +39 -95
- package/node_modules/is-plain-object/dist/is-plain-object.js +0 -2
- package/node_modules/is-plain-object/is-plain-object.d.ts +6 -1
- package/node_modules/is-plain-object/package.json +8 -34
- package/node_modules/jose/dist/types/jwe/compact/encrypt.d.ts +4 -3
- package/node_modules/jose/dist/types/jwe/flattened/encrypt.d.ts +4 -3
- package/node_modules/jose/dist/types/jwe/general/encrypt.d.ts +4 -3
- package/node_modules/jose/dist/types/jwks/remote.d.ts +4 -4
- package/node_modules/jose/dist/types/jws/general/verify.d.ts +3 -3
- package/node_modules/jose/dist/types/jwt/encrypt.d.ts +12 -17
- package/node_modules/jose/dist/types/jwt/sign.d.ts +8 -14
- package/node_modules/jose/dist/types/jwt/unsecured.d.ts +9 -15
- package/node_modules/jose/dist/types/types.d.ts +4 -2
- package/node_modules/jose/dist/webapi/jwe/compact/encrypt.js +30 -8
- package/node_modules/jose/dist/webapi/jwe/flattened/decrypt.js +8 -4
- package/node_modules/jose/dist/webapi/jwe/flattened/encrypt.js +6 -13
- package/node_modules/jose/dist/webapi/jwe/general/decrypt.js +19 -19
- package/node_modules/jose/dist/webapi/jwe/general/encrypt.js +32 -21
- package/node_modules/jose/dist/webapi/jwk/embedded.js +15 -1
- package/node_modules/jose/dist/webapi/jwk/thumbprint.js +11 -5
- package/node_modules/jose/dist/webapi/jwks/local.js +45 -53
- package/node_modules/jose/dist/webapi/jwks/remote.js +82 -103
- package/node_modules/jose/dist/webapi/jws/compact/sign.js +10 -9
- package/node_modules/jose/dist/webapi/jws/flattened/sign.js +2 -1
- package/node_modules/jose/dist/webapi/jws/flattened/verify.js +8 -7
- package/node_modules/jose/dist/webapi/jws/general/sign.js +5 -3
- package/node_modules/jose/dist/webapi/jws/general/verify.js +51 -21
- package/node_modules/jose/dist/webapi/jwt/decrypt.js +7 -9
- package/node_modules/jose/dist/webapi/jwt/encrypt.js +21 -50
- package/node_modules/jose/dist/webapi/jwt/sign.js +8 -41
- package/node_modules/jose/dist/webapi/jwt/unsecured.js +21 -42
- package/node_modules/jose/dist/webapi/key/generate_key_pair.js +6 -2
- package/node_modules/jose/dist/webapi/key/generate_secret.js +7 -6
- package/node_modules/jose/dist/webapi/key/import.js +16 -12
- package/node_modules/jose/dist/webapi/lib/asn1.js +7 -2
- package/node_modules/jose/dist/webapi/lib/content_encryption.js +7 -15
- package/node_modules/jose/dist/webapi/lib/deflate.js +8 -0
- package/node_modules/jose/dist/webapi/lib/helpers.js +1 -1
- package/node_modules/jose/dist/webapi/lib/jwe_decrypt.js +85 -28
- package/node_modules/jose/dist/webapi/lib/jwe_encrypt.js +45 -13
- package/node_modules/jose/dist/webapi/lib/jwk_metadata.js +20 -0
- package/node_modules/jose/dist/webapi/lib/jws_sign.js +47 -36
- package/node_modules/jose/dist/webapi/lib/jws_verify.js +77 -45
- package/node_modules/jose/dist/webapi/lib/jwt_claims_set.js +82 -47
- package/node_modules/jose/dist/webapi/lib/key.js +24 -9
- package/node_modules/jose/dist/webapi/lib/key_management.js +10 -4
- package/node_modules/jose/dist/webapi/lib/key_options.js +6 -0
- package/node_modules/jose/dist/webapi/lib/options.js +26 -0
- package/node_modules/jose/dist/webapi/lib/type_checks.js +11 -13
- package/node_modules/jose/package.json +1 -1
- package/package.json +2 -2
- package/dist/ui/assets/index-0Q2eNytX.js +0 -110
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/HISTORY.md +0 -114
package/dist/mcp-server/http.mjs
CHANGED
|
@@ -55,6 +55,10 @@ var SPClient = class {
|
|
|
55
55
|
sessionCookie = "";
|
|
56
56
|
apiKey = "";
|
|
57
57
|
receiptRetry;
|
|
58
|
+
/** The AS base URL this client talks to (for provenance records). */
|
|
59
|
+
get url() {
|
|
60
|
+
return this.baseUrl;
|
|
61
|
+
}
|
|
58
62
|
setApiKey(key) {
|
|
59
63
|
this.apiKey = key;
|
|
60
64
|
}
|
|
@@ -825,6 +829,229 @@ var DenialLog = class {
|
|
|
825
829
|
}
|
|
826
830
|
};
|
|
827
831
|
|
|
832
|
+
// src/lib/receipt-archive.ts
|
|
833
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4, existsSync as existsSync4, unlinkSync as unlinkSync4 } from "fs";
|
|
834
|
+
import { homedir as homedir4 } from "os";
|
|
835
|
+
import { dirname as dirname4, join as join4 } from "path";
|
|
836
|
+
import { createCipheriv as createCipheriv4, createDecipheriv as createDecipheriv4, randomBytes as randomBytes4 } from "crypto";
|
|
837
|
+
var DEFAULT_DIR4 = process.env.SUVEREN_DATA_DIR ?? join4(homedir4(), ".suveren");
|
|
838
|
+
function stripPreviews(proposal) {
|
|
839
|
+
const args = proposal.toolArgs;
|
|
840
|
+
if (!args || typeof args !== "object") return proposal;
|
|
841
|
+
const { _imagePreview, ...rest } = args;
|
|
842
|
+
if (_imagePreview === void 0) return proposal;
|
|
843
|
+
return { ...proposal, toolArgs: rest };
|
|
844
|
+
}
|
|
845
|
+
var ReceiptArchive = class {
|
|
846
|
+
data = { version: 1, authorizations: {}, receipts: [] };
|
|
847
|
+
baseDir;
|
|
848
|
+
vaultKey = null;
|
|
849
|
+
constructor(filePath) {
|
|
850
|
+
if (filePath && filePath.endsWith(".json")) {
|
|
851
|
+
this.baseDir = dirname4(filePath);
|
|
852
|
+
} else {
|
|
853
|
+
this.baseDir = filePath ?? DEFAULT_DIR4;
|
|
854
|
+
}
|
|
855
|
+
this.loadPlaintext();
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Set the vault key for encryption. Triggers migration from plaintext if needed.
|
|
859
|
+
*/
|
|
860
|
+
setVaultKey(key) {
|
|
861
|
+
this.vaultKey = key;
|
|
862
|
+
if (existsSync4(this.encryptedFilePath)) {
|
|
863
|
+
this.loadEncrypted();
|
|
864
|
+
} else if (this.data.receipts.length > 0 || Object.keys(this.data.authorizations).length > 0) {
|
|
865
|
+
this.persistEncrypted();
|
|
866
|
+
if (existsSync4(this.plaintextFilePath)) {
|
|
867
|
+
try {
|
|
868
|
+
unlinkSync4(this.plaintextFilePath);
|
|
869
|
+
} catch {
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* True when encrypted evidence exists on disk that this process cannot read
|
|
876
|
+
* yet (no vault key). Callers MUST NOT present the archive as "empty" in
|
|
877
|
+
* this state — that would report a hollow success over unreadable evidence.
|
|
878
|
+
*/
|
|
879
|
+
isLocked() {
|
|
880
|
+
return this.vaultKey === null && existsSync4(this.encryptedFilePath);
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Archive a signed receipt (and the grant it was issued under).
|
|
884
|
+
* Idempotent on receipt id — a replayed receipt is recorded once.
|
|
885
|
+
*/
|
|
886
|
+
record(entry, now = Math.floor(Date.now() / 1e3)) {
|
|
887
|
+
let changed = false;
|
|
888
|
+
const receiptId = typeof entry.receipt?.id === "string" ? entry.receipt.id : void 0;
|
|
889
|
+
const alreadyArchived = receiptId !== void 0 && this.data.receipts.some((r) => r.receipt.id === receiptId);
|
|
890
|
+
if (!alreadyArchived) {
|
|
891
|
+
this.data.receipts.push({
|
|
892
|
+
archivedAt: now,
|
|
893
|
+
authorizationId: entry.authorizationId,
|
|
894
|
+
asUrl: entry.asUrl,
|
|
895
|
+
asPublicKey: entry.asPublicKey,
|
|
896
|
+
receipt: entry.receipt,
|
|
897
|
+
...entry.proposal ? { proposal: stripPreviews(entry.proposal) } : {},
|
|
898
|
+
...entry.boundContent !== void 0 ? { boundContent: entry.boundContent } : {}
|
|
899
|
+
});
|
|
900
|
+
changed = true;
|
|
901
|
+
}
|
|
902
|
+
if (entry.authorization) {
|
|
903
|
+
const existing = this.data.authorizations[entry.authorizationId];
|
|
904
|
+
if (!existing) {
|
|
905
|
+
this.data.authorizations[entry.authorizationId] = {
|
|
906
|
+
authorizationId: entry.authorizationId,
|
|
907
|
+
profileId: entry.authorization.profileId,
|
|
908
|
+
boundsHash: entry.authorization.boundsHash,
|
|
909
|
+
contextHash: entry.authorization.contextHash,
|
|
910
|
+
bounds: entry.authorization.bounds,
|
|
911
|
+
context: entry.authorization.context,
|
|
912
|
+
intent: entry.authorization.intent,
|
|
913
|
+
attestations: [...entry.authorization.attestations],
|
|
914
|
+
archivedAt: now
|
|
915
|
+
};
|
|
916
|
+
changed = true;
|
|
917
|
+
} else {
|
|
918
|
+
for (const att of entry.authorization.attestations) {
|
|
919
|
+
const seen = existing.attestations.some(
|
|
920
|
+
(a) => a.domain === att.domain && a.blob === att.blob
|
|
921
|
+
);
|
|
922
|
+
if (!seen) {
|
|
923
|
+
existing.attestations.push(att);
|
|
924
|
+
changed = true;
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
if (!existing.bounds && entry.authorization.bounds) {
|
|
928
|
+
existing.bounds = entry.authorization.bounds;
|
|
929
|
+
changed = true;
|
|
930
|
+
}
|
|
931
|
+
if (!existing.context && entry.authorization.context) {
|
|
932
|
+
existing.context = entry.authorization.context;
|
|
933
|
+
changed = true;
|
|
934
|
+
}
|
|
935
|
+
if (!existing.intent && entry.authorization.intent) {
|
|
936
|
+
existing.intent = entry.authorization.intent;
|
|
937
|
+
changed = true;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
if (changed) this.persist();
|
|
942
|
+
}
|
|
943
|
+
getReceipts() {
|
|
944
|
+
return [...this.data.receipts];
|
|
945
|
+
}
|
|
946
|
+
getAuthorizations() {
|
|
947
|
+
return Object.values(this.data.authorizations);
|
|
948
|
+
}
|
|
949
|
+
get size() {
|
|
950
|
+
return this.data.receipts.length;
|
|
951
|
+
}
|
|
952
|
+
// ─── Encryption helpers ─────────────────────────────────────────────────
|
|
953
|
+
encrypt(plaintext) {
|
|
954
|
+
if (!this.vaultKey) throw new Error("No vault key");
|
|
955
|
+
const iv = randomBytes4(12);
|
|
956
|
+
const cipher = createCipheriv4("aes-256-gcm", this.vaultKey, iv);
|
|
957
|
+
const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
958
|
+
const tag = cipher.getAuthTag();
|
|
959
|
+
return {
|
|
960
|
+
iv: iv.toString("hex"),
|
|
961
|
+
ciphertext: encrypted.toString("hex"),
|
|
962
|
+
tag: tag.toString("hex")
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
decrypt(blob) {
|
|
966
|
+
if (!this.vaultKey) throw new Error("No vault key");
|
|
967
|
+
const decipher = createDecipheriv4(
|
|
968
|
+
"aes-256-gcm",
|
|
969
|
+
this.vaultKey,
|
|
970
|
+
Buffer.from(blob.iv, "hex")
|
|
971
|
+
);
|
|
972
|
+
decipher.setAuthTag(Buffer.from(blob.tag, "hex"));
|
|
973
|
+
const decrypted = Buffer.concat([
|
|
974
|
+
decipher.update(Buffer.from(blob.ciphertext, "hex")),
|
|
975
|
+
decipher.final()
|
|
976
|
+
]);
|
|
977
|
+
return decrypted.toString("utf8");
|
|
978
|
+
}
|
|
979
|
+
// ─── File paths ─────────────────────────────────────────────────────────
|
|
980
|
+
get plaintextFilePath() {
|
|
981
|
+
return join4(this.baseDir, "receipt-archive.json");
|
|
982
|
+
}
|
|
983
|
+
get encryptedFilePath() {
|
|
984
|
+
return join4(this.baseDir, "receipt-archive.enc.json");
|
|
985
|
+
}
|
|
986
|
+
// ─── Load / Persist ─────────────────────────────────────────────────────
|
|
987
|
+
loadPlaintext() {
|
|
988
|
+
if (!existsSync4(this.plaintextFilePath)) {
|
|
989
|
+
mkdirSync4(this.baseDir, { recursive: true });
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
try {
|
|
993
|
+
const raw = readFileSync4(this.plaintextFilePath, "utf-8");
|
|
994
|
+
const parsed = JSON.parse(raw);
|
|
995
|
+
this.data = {
|
|
996
|
+
version: 1,
|
|
997
|
+
authorizations: parsed.authorizations ?? {},
|
|
998
|
+
receipts: parsed.receipts ?? []
|
|
999
|
+
};
|
|
1000
|
+
} catch {
|
|
1001
|
+
console.error(`[ReceiptArchive] Could not parse ${this.plaintextFilePath} \u2014 preserving as .corrupt`);
|
|
1002
|
+
try {
|
|
1003
|
+
writeFileSync4(
|
|
1004
|
+
`${this.plaintextFilePath}.corrupt-${Date.now()}`,
|
|
1005
|
+
readFileSync4(this.plaintextFilePath)
|
|
1006
|
+
);
|
|
1007
|
+
} catch {
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
loadEncrypted() {
|
|
1012
|
+
if (!existsSync4(this.encryptedFilePath)) return;
|
|
1013
|
+
try {
|
|
1014
|
+
const raw = readFileSync4(this.encryptedFilePath, "utf-8");
|
|
1015
|
+
const data = JSON.parse(raw);
|
|
1016
|
+
const decrypted = this.decrypt(data.blob);
|
|
1017
|
+
const parsed = JSON.parse(decrypted);
|
|
1018
|
+
this.data = {
|
|
1019
|
+
version: 1,
|
|
1020
|
+
authorizations: parsed.authorizations ?? {},
|
|
1021
|
+
receipts: parsed.receipts ?? []
|
|
1022
|
+
};
|
|
1023
|
+
} catch (err) {
|
|
1024
|
+
console.error(`[ReceiptArchive] Could not decrypt ${this.encryptedFilePath} \u2014 preserving as .corrupt:`, err);
|
|
1025
|
+
try {
|
|
1026
|
+
writeFileSync4(
|
|
1027
|
+
`${this.encryptedFilePath}.corrupt-${Date.now()}`,
|
|
1028
|
+
readFileSync4(this.encryptedFilePath)
|
|
1029
|
+
);
|
|
1030
|
+
} catch {
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
persist() {
|
|
1035
|
+
if (this.vaultKey) {
|
|
1036
|
+
this.persistEncrypted();
|
|
1037
|
+
} else {
|
|
1038
|
+
this.persistPlaintext();
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
persistPlaintext() {
|
|
1042
|
+
mkdirSync4(this.baseDir, { recursive: true, mode: 448 });
|
|
1043
|
+
writeFileSync4(this.plaintextFilePath, JSON.stringify(this.data, null, 2), { encoding: "utf-8", mode: 384 });
|
|
1044
|
+
}
|
|
1045
|
+
persistEncrypted() {
|
|
1046
|
+
const data = {
|
|
1047
|
+
version: 1,
|
|
1048
|
+
blob: this.encrypt(JSON.stringify(this.data))
|
|
1049
|
+
};
|
|
1050
|
+
mkdirSync4(this.baseDir, { recursive: true, mode: 448 });
|
|
1051
|
+
writeFileSync4(this.encryptedFilePath, JSON.stringify(data, null, 2), { encoding: "utf-8", mode: 384 });
|
|
1052
|
+
}
|
|
1053
|
+
};
|
|
1054
|
+
|
|
828
1055
|
// src/lib/gatekeeper.ts
|
|
829
1056
|
import { verify } from "@hap/core";
|
|
830
1057
|
var MCPGatekeeper = class {
|
|
@@ -898,6 +1125,7 @@ var SharedState = class {
|
|
|
898
1125
|
gateStore;
|
|
899
1126
|
executionLog;
|
|
900
1127
|
denialLog;
|
|
1128
|
+
receiptArchive;
|
|
901
1129
|
gatekeeper;
|
|
902
1130
|
constructor(spUrl2, gateStorePath) {
|
|
903
1131
|
this.spClient = new SPClient(spUrl2);
|
|
@@ -905,8 +1133,50 @@ var SharedState = class {
|
|
|
905
1133
|
this.gateStore = new GateStore(gateStorePath);
|
|
906
1134
|
this.executionLog = new ExecutionLog(gateStorePath);
|
|
907
1135
|
this.denialLog = new DenialLog(gateStorePath);
|
|
1136
|
+
this.receiptArchive = new ReceiptArchive(gateStorePath);
|
|
908
1137
|
this.gatekeeper = new MCPGatekeeper(this.cache, this.executionLog);
|
|
909
1138
|
}
|
|
1139
|
+
/**
|
|
1140
|
+
* Archive a signed receipt into the local receipt archive — the subject's
|
|
1141
|
+
* own durable copy of the evidence (see receipt-archive.ts).
|
|
1142
|
+
*
|
|
1143
|
+
* Best-effort by contract: the AS has already issued the receipt and holds
|
|
1144
|
+
* the authoritative copy, so an archive failure logs loudly but MUST NOT
|
|
1145
|
+
* block the execution it documents.
|
|
1146
|
+
*/
|
|
1147
|
+
async archiveReceipt(receipt, opts) {
|
|
1148
|
+
try {
|
|
1149
|
+
let asPublicKey;
|
|
1150
|
+
try {
|
|
1151
|
+
asPublicKey = await this.cache.getPublicKey();
|
|
1152
|
+
} catch {
|
|
1153
|
+
}
|
|
1154
|
+
this.receiptArchive.record({
|
|
1155
|
+
receipt,
|
|
1156
|
+
authorizationId: opts.authorizationId,
|
|
1157
|
+
asUrl: this.spClient.url,
|
|
1158
|
+
asPublicKey,
|
|
1159
|
+
proposal: opts.proposal,
|
|
1160
|
+
boundContent: opts.boundContent,
|
|
1161
|
+
authorization: opts.profileId ? {
|
|
1162
|
+
profileId: opts.profileId,
|
|
1163
|
+
boundsHash: opts.boundsHash,
|
|
1164
|
+
contextHash: opts.contextHash ?? this.gateStore.get(opts.authorizationId)?.contextHash,
|
|
1165
|
+
bounds: opts.bounds,
|
|
1166
|
+
context: opts.context,
|
|
1167
|
+
// Fall back to the gate store — the review path's cached auth
|
|
1168
|
+
// carries no gate content.
|
|
1169
|
+
intent: opts.intent ?? this.gateStore.get(opts.authorizationId)?.gateContent?.intent,
|
|
1170
|
+
attestations: opts.attestations ?? []
|
|
1171
|
+
} : void 0
|
|
1172
|
+
});
|
|
1173
|
+
} catch (err) {
|
|
1174
|
+
console.error(
|
|
1175
|
+
"[Suveren MCP] Receipt archive write failed (execution proceeds \u2014 AS retains authoritative copy):",
|
|
1176
|
+
err
|
|
1177
|
+
);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
910
1180
|
setGateContent(path, authorizationId, profileId, content, opts) {
|
|
911
1181
|
this.gateStore.set(authorizationId, {
|
|
912
1182
|
authorizationId,
|
|
@@ -1012,17 +1282,17 @@ function formatConsumptionFull(entries) {
|
|
|
1012
1282
|
}
|
|
1013
1283
|
|
|
1014
1284
|
// src/lib/context-loader.ts
|
|
1015
|
-
import { readFileSync as
|
|
1016
|
-
import { homedir as
|
|
1017
|
-
import { join as
|
|
1018
|
-
var
|
|
1285
|
+
import { readFileSync as readFileSync5, existsSync as existsSync5 } from "fs";
|
|
1286
|
+
import { homedir as homedir5 } from "os";
|
|
1287
|
+
import { join as join5 } from "path";
|
|
1288
|
+
var DEFAULT_DIR5 = process.env.SUVEREN_DATA_DIR ?? join5(homedir5(), ".suveren");
|
|
1019
1289
|
var BRIEF_MAX_CHARS = 16 * 1024;
|
|
1020
1290
|
function readContextFile(dataDir) {
|
|
1021
|
-
const dir = dataDir ??
|
|
1022
|
-
const filePath =
|
|
1023
|
-
if (!
|
|
1291
|
+
const dir = dataDir ?? DEFAULT_DIR5;
|
|
1292
|
+
const filePath = join5(dir, "context.md");
|
|
1293
|
+
if (!existsSync5(filePath)) return null;
|
|
1024
1294
|
try {
|
|
1025
|
-
const content =
|
|
1295
|
+
const content = readFileSync5(filePath, "utf-8").trim();
|
|
1026
1296
|
return content || null;
|
|
1027
1297
|
} catch {
|
|
1028
1298
|
return null;
|
|
@@ -1192,7 +1462,7 @@ function appendVerificationFooter(tool, args, receiptId, subject) {
|
|
|
1192
1462
|
// src/lib/content-binding.ts
|
|
1193
1463
|
import {
|
|
1194
1464
|
computeContentHash,
|
|
1195
|
-
|
|
1465
|
+
selectBoundFields,
|
|
1196
1466
|
isFieldBinding,
|
|
1197
1467
|
bindingAppliesTo,
|
|
1198
1468
|
getProfile as getProfile2
|
|
@@ -1212,14 +1482,18 @@ function computeContentBinding(profileId, tool, toolArgs, actionType) {
|
|
|
1212
1482
|
}
|
|
1213
1483
|
return void 0;
|
|
1214
1484
|
}
|
|
1485
|
+
const bound = selectBoundFields(binding, toolArgs);
|
|
1215
1486
|
return {
|
|
1216
|
-
contentHash:
|
|
1217
|
-
contentBinding: { version: binding.version, kind: binding.kind, fields: binding.fields }
|
|
1487
|
+
contentHash: computeContentHash(binding, bound),
|
|
1488
|
+
contentBinding: { version: binding.version, kind: binding.kind, fields: binding.fields },
|
|
1489
|
+
boundContent: bound
|
|
1218
1490
|
};
|
|
1219
1491
|
}
|
|
1220
1492
|
let contentHash;
|
|
1493
|
+
let boundContent;
|
|
1221
1494
|
if (binding.kind === "jcs") {
|
|
1222
1495
|
contentHash = computeContentHash(binding, toolArgs);
|
|
1496
|
+
boundContent = toolArgs;
|
|
1223
1497
|
} else {
|
|
1224
1498
|
const declared = tool?.gating?.contentField;
|
|
1225
1499
|
const field = declared ?? (tool ? detectContentField(tool) : null);
|
|
@@ -1227,10 +1501,12 @@ function computeContentBinding(profileId, tool, toolArgs, actionType) {
|
|
|
1227
1501
|
const raw = typeof toolArgs[field] === "string" ? toolArgs[field] : "";
|
|
1228
1502
|
if (raw === "") return void 0;
|
|
1229
1503
|
contentHash = computeContentHash(binding, raw);
|
|
1504
|
+
boundContent = raw;
|
|
1230
1505
|
}
|
|
1231
1506
|
return {
|
|
1232
1507
|
contentHash,
|
|
1233
|
-
contentBinding: { version: binding.version, kind: binding.kind }
|
|
1508
|
+
contentBinding: { version: binding.version, kind: binding.kind },
|
|
1509
|
+
boundContent
|
|
1234
1510
|
};
|
|
1235
1511
|
}
|
|
1236
1512
|
function attachReceiptId(tool, args, receiptId) {
|
|
@@ -1849,6 +2125,29 @@ function createGatedToolHandlerInner(tool, integrationManager2, state2) {
|
|
|
1849
2125
|
}
|
|
1850
2126
|
}
|
|
1851
2127
|
}
|
|
2128
|
+
const declaredActionType = typeof execution.action_type === "string" && execution.action_type.length > 0 ? execution.action_type : void 0;
|
|
2129
|
+
if (!declaredActionType) {
|
|
2130
|
+
return {
|
|
2131
|
+
content: [{
|
|
2132
|
+
type: "text",
|
|
2133
|
+
text: `Blocked by Gatekeeper: tool ${tool.namespacedName} declares no action_type in its manifest's staticExecution. A write cannot be bounds-checked without its action type \u2014 fix the integration manifest.`
|
|
2134
|
+
}],
|
|
2135
|
+
isError: true
|
|
2136
|
+
};
|
|
2137
|
+
}
|
|
2138
|
+
{
|
|
2139
|
+
const profileDef = getProfile3(profile);
|
|
2140
|
+
const registry = profileDef?.boundsSchema?.actionTypes;
|
|
2141
|
+
if (Array.isArray(registry) && registry.length > 0 && !registry.includes(declaredActionType)) {
|
|
2142
|
+
return {
|
|
2143
|
+
content: [{
|
|
2144
|
+
type: "text",
|
|
2145
|
+
text: `Blocked by Gatekeeper: action_type "${declaredActionType}" is not in profile ${profile}'s actionTypes registry (${registry.join(", ")}). Fix the integration manifest.`
|
|
2146
|
+
}],
|
|
2147
|
+
isError: true
|
|
2148
|
+
};
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
1852
2151
|
const auths = state2.getEnrichedAuthorizations();
|
|
1853
2152
|
const matchingAuths = auths.filter(
|
|
1854
2153
|
(a) => a.complete && profileMatches(a.profileId, profile)
|
|
@@ -1946,12 +2245,7 @@ Proposal ID: ${proposal.id}. Check status with check-pending-commitments(proposa
|
|
|
1946
2245
|
}
|
|
1947
2246
|
let receiptId;
|
|
1948
2247
|
try {
|
|
1949
|
-
const actionType =
|
|
1950
|
-
if (!actionType) {
|
|
1951
|
-
console.error(
|
|
1952
|
-
`[Suveren MCP] Warning: tool ${tool.namespacedName} has no action_type in staticExecution. Bounds check may be skipped. Fix the integration manifest.`
|
|
1953
|
-
);
|
|
1954
|
-
}
|
|
2248
|
+
const actionType = declaredActionType;
|
|
1955
2249
|
const binding = computeContentBinding(auth.profileId, tool, args, actionType);
|
|
1956
2250
|
const { receipt } = await state2.spClient.postReceipt({
|
|
1957
2251
|
authorizationId: authzId,
|
|
@@ -1966,6 +2260,19 @@ Proposal ID: ${proposal.id}. Check status with check-pending-commitments(proposa
|
|
|
1966
2260
|
...binding ?? {}
|
|
1967
2261
|
});
|
|
1968
2262
|
receiptId = typeof receipt?.id === "string" ? receipt.id : void 0;
|
|
2263
|
+
await state2.archiveReceipt(receipt, {
|
|
2264
|
+
authorizationId: authzId,
|
|
2265
|
+
profileId: auth.profileId,
|
|
2266
|
+
boundsHash: auth.boundsHash,
|
|
2267
|
+
contextHash: auth.contextHash,
|
|
2268
|
+
bounds: auth.bounds ?? auth.frame,
|
|
2269
|
+
context: auth.context,
|
|
2270
|
+
intent: auth.gateContent?.intent,
|
|
2271
|
+
attestations: auth.attestations,
|
|
2272
|
+
// Automatic path has no proposal, so without this the archive
|
|
2273
|
+
// would hold a hash of content it cannot reproduce.
|
|
2274
|
+
boundContent: binding?.boundContent
|
|
2275
|
+
});
|
|
1969
2276
|
} catch (err) {
|
|
1970
2277
|
if (err instanceof ContentBindingError) {
|
|
1971
2278
|
return {
|
|
@@ -2434,6 +2741,19 @@ async function executeCommitted(proposal, state2, integrationManager2) {
|
|
|
2434
2741
|
...binding ?? {}
|
|
2435
2742
|
});
|
|
2436
2743
|
receiptId = typeof receipt?.id === "string" ? receipt.id : void 0;
|
|
2744
|
+
await state2.archiveReceipt(receipt, {
|
|
2745
|
+
authorizationId: proposal.authorizationId,
|
|
2746
|
+
profileId: proposal.profileId,
|
|
2747
|
+
boundsHash: cachedAuth?.boundsHash,
|
|
2748
|
+
contextHash: cachedAuth?.contextHash,
|
|
2749
|
+
bounds: cachedAuth?.bounds ?? cachedAuth?.frame,
|
|
2750
|
+
context: cachedAuth?.context,
|
|
2751
|
+
attestations: cachedAuth?.attestations,
|
|
2752
|
+
// What the human actually approved, kept next to the receipt that cites
|
|
2753
|
+
// it — otherwise `proposalId` is a pointer to a server we may not have.
|
|
2754
|
+
proposal,
|
|
2755
|
+
boundContent: binding?.boundContent
|
|
2756
|
+
});
|
|
2437
2757
|
} catch (err) {
|
|
2438
2758
|
if (err instanceof ContentBindingError2) {
|
|
2439
2759
|
return {
|
|
@@ -2756,16 +3076,16 @@ function verifyGateContentHashes(content, auth) {
|
|
|
2756
3076
|
}
|
|
2757
3077
|
|
|
2758
3078
|
// src/lib/integration-registry.ts
|
|
2759
|
-
import { mkdirSync as
|
|
2760
|
-
import { homedir as
|
|
2761
|
-
import { dirname as
|
|
2762
|
-
var DEFAULT_DATA_DIR = process.env.SUVEREN_DATA_DIR ??
|
|
3079
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync6 } from "fs";
|
|
3080
|
+
import { homedir as homedir6 } from "os";
|
|
3081
|
+
import { dirname as dirname5, join as join6 } from "path";
|
|
3082
|
+
var DEFAULT_DATA_DIR = process.env.SUVEREN_DATA_DIR ?? join6(homedir6(), ".suveren");
|
|
2763
3083
|
var IntegrationRegistry = class {
|
|
2764
3084
|
integrations = /* @__PURE__ */ new Map();
|
|
2765
3085
|
filePath;
|
|
2766
3086
|
constructor(dataDir) {
|
|
2767
3087
|
const dir = dataDir ?? DEFAULT_DATA_DIR;
|
|
2768
|
-
this.filePath =
|
|
3088
|
+
this.filePath = join6(dir, "integrations.json");
|
|
2769
3089
|
this.load();
|
|
2770
3090
|
}
|
|
2771
3091
|
// ─── CRUD ───────────────────────────────────────────────────────────────
|
|
@@ -2796,12 +3116,12 @@ var IntegrationRegistry = class {
|
|
|
2796
3116
|
}
|
|
2797
3117
|
// ─── Load / Save ────────────────────────────────────────────────────────
|
|
2798
3118
|
load() {
|
|
2799
|
-
if (!
|
|
2800
|
-
|
|
3119
|
+
if (!existsSync6(this.filePath)) {
|
|
3120
|
+
mkdirSync5(dirname5(this.filePath), { recursive: true });
|
|
2801
3121
|
return;
|
|
2802
3122
|
}
|
|
2803
3123
|
try {
|
|
2804
|
-
const raw =
|
|
3124
|
+
const raw = readFileSync6(this.filePath, "utf-8");
|
|
2805
3125
|
const data = JSON.parse(raw);
|
|
2806
3126
|
const migrated = data.integrations.map((i) => {
|
|
2807
3127
|
if ("toolGating" in i && !("profile" in i)) {
|
|
@@ -2830,33 +3150,33 @@ var IntegrationRegistry = class {
|
|
|
2830
3150
|
version: 1,
|
|
2831
3151
|
integrations: Array.from(this.integrations.values())
|
|
2832
3152
|
};
|
|
2833
|
-
|
|
2834
|
-
|
|
3153
|
+
mkdirSync5(dirname5(this.filePath), { recursive: true });
|
|
3154
|
+
writeFileSync5(this.filePath, JSON.stringify(data, null, 2), { encoding: "utf-8" });
|
|
2835
3155
|
}
|
|
2836
3156
|
};
|
|
2837
3157
|
|
|
2838
3158
|
// src/lib/integration-manager.ts
|
|
2839
|
-
import { homedir as
|
|
2840
|
-
import { join as
|
|
2841
|
-
import { existsSync as
|
|
3159
|
+
import { homedir as homedir7 } from "os";
|
|
3160
|
+
import { join as join7, delimiter } from "path";
|
|
3161
|
+
import { existsSync as existsSync7, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync7, rmSync } from "fs";
|
|
2842
3162
|
import { execFile } from "child_process";
|
|
2843
3163
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2844
3164
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
2845
3165
|
import { getProfile as getProfile5 } from "@hap/core";
|
|
2846
|
-
var DEFAULT_DATA_DIR2 = process.env.SUVEREN_DATA_DIR ??
|
|
2847
|
-
var INTEGRATIONS_DIR = process.env.SUVEREN_INTEGRATIONS_DIR ??
|
|
2848
|
-
var INTEGRATIONS_BIN =
|
|
3166
|
+
var DEFAULT_DATA_DIR2 = process.env.SUVEREN_DATA_DIR ?? join7(homedir7(), ".suveren");
|
|
3167
|
+
var INTEGRATIONS_DIR = process.env.SUVEREN_INTEGRATIONS_DIR ?? join7(DEFAULT_DATA_DIR2, "integrations");
|
|
3168
|
+
var INTEGRATIONS_BIN = join7(INTEGRATIONS_DIR, "node_modules", ".bin");
|
|
2849
3169
|
function buildPath() {
|
|
2850
3170
|
const base = process.env.PATH ?? "";
|
|
2851
3171
|
return [INTEGRATIONS_BIN, base].join(delimiter);
|
|
2852
3172
|
}
|
|
2853
3173
|
function ensureIntegrationsDir() {
|
|
2854
|
-
if (!
|
|
2855
|
-
|
|
3174
|
+
if (!existsSync7(INTEGRATIONS_DIR)) {
|
|
3175
|
+
mkdirSync6(INTEGRATIONS_DIR, { recursive: true });
|
|
2856
3176
|
}
|
|
2857
|
-
const pkgPath =
|
|
2858
|
-
if (!
|
|
2859
|
-
|
|
3177
|
+
const pkgPath = join7(INTEGRATIONS_DIR, "package.json");
|
|
3178
|
+
if (!existsSync7(pkgPath)) {
|
|
3179
|
+
writeFileSync6(pkgPath, JSON.stringify({ name: "suveren-integrations", version: "1.0.0", private: true }, null, 2));
|
|
2860
3180
|
}
|
|
2861
3181
|
}
|
|
2862
3182
|
function readAgeOf(config) {
|
|
@@ -2922,19 +3242,19 @@ var IntegrationManager = class {
|
|
|
2922
3242
|
* exist, and treat anything else as not-installed (and reinstallable).
|
|
2923
3243
|
*/
|
|
2924
3244
|
isUsableInstall(npmPackage) {
|
|
2925
|
-
const pkgDir =
|
|
2926
|
-
const pkgJson =
|
|
2927
|
-
if (!
|
|
3245
|
+
const pkgDir = join7(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"));
|
|
3246
|
+
const pkgJson = join7(pkgDir, "package.json");
|
|
3247
|
+
if (!existsSync7(pkgDir) || !existsSync7(pkgJson)) return false;
|
|
2928
3248
|
try {
|
|
2929
|
-
const pkg = JSON.parse(
|
|
3249
|
+
const pkg = JSON.parse(readFileSync7(pkgJson, "utf8"));
|
|
2930
3250
|
const defaultBinName = (pkg.name ?? npmPackage).split("/").pop() ?? "";
|
|
2931
3251
|
const binMap = typeof pkg.bin === "string" ? { [defaultBinName]: pkg.bin } : pkg.bin ?? {};
|
|
2932
3252
|
const binEntries = Object.entries(binMap);
|
|
2933
3253
|
for (const [binName, target] of binEntries) {
|
|
2934
|
-
if (!
|
|
3254
|
+
if (!existsSync7(join7(pkgDir, target))) return false;
|
|
2935
3255
|
if (!this.binShimExists(binName)) return false;
|
|
2936
3256
|
}
|
|
2937
|
-
if (binEntries.length === 0 && pkg.main && !
|
|
3257
|
+
if (binEntries.length === 0 && pkg.main && !existsSync7(join7(pkgDir, pkg.main))) return false;
|
|
2938
3258
|
return true;
|
|
2939
3259
|
} catch {
|
|
2940
3260
|
return false;
|
|
@@ -2948,7 +3268,7 @@ var IntegrationManager = class {
|
|
|
2948
3268
|
binShimExists(binName) {
|
|
2949
3269
|
if (!binName) return false;
|
|
2950
3270
|
const candidates = process.platform === "win32" ? [binName, `${binName}.cmd`, `${binName}.CMD`, `${binName}.ps1`] : [binName];
|
|
2951
|
-
return candidates.some((c) =>
|
|
3271
|
+
return candidates.some((c) => existsSync7(join7(INTEGRATIONS_BIN, c)));
|
|
2952
3272
|
}
|
|
2953
3273
|
/**
|
|
2954
3274
|
* Install an npm package into the managed integrations directory if not
|
|
@@ -2965,8 +3285,8 @@ var IntegrationManager = class {
|
|
|
2965
3285
|
async ensureInstalled(npmPackage) {
|
|
2966
3286
|
ensureIntegrationsDir();
|
|
2967
3287
|
if (this.isUsableInstall(npmPackage)) return;
|
|
2968
|
-
const pkgDir =
|
|
2969
|
-
if (
|
|
3288
|
+
const pkgDir = join7(INTEGRATIONS_DIR, "node_modules", ...npmPackage.split("/"));
|
|
3289
|
+
if (existsSync7(pkgDir)) {
|
|
2970
3290
|
console.error(`[IntegrationManager] Removing unusable install of ${npmPackage}`);
|
|
2971
3291
|
try {
|
|
2972
3292
|
rmSync(pkgDir, { recursive: true, force: true });
|
|
@@ -3381,28 +3701,28 @@ var IntegrationManager = class {
|
|
|
3381
3701
|
};
|
|
3382
3702
|
|
|
3383
3703
|
// src/lib/profile-loader.ts
|
|
3384
|
-
import { readFileSync as
|
|
3385
|
-
import { join as
|
|
3704
|
+
import { readFileSync as readFileSync8, existsSync as existsSync8 } from "fs";
|
|
3705
|
+
import { join as join8, resolve } from "path";
|
|
3386
3706
|
import { registerProfile } from "@hap/core";
|
|
3387
3707
|
function loadProfiles(profilesDir) {
|
|
3388
|
-
const dir = resolve(profilesDir ?? process.env.SUVEREN_PROFILES_DIR ??
|
|
3389
|
-
const indexPath =
|
|
3390
|
-
if (!
|
|
3708
|
+
const dir = resolve(profilesDir ?? process.env.SUVEREN_PROFILES_DIR ?? join8(import.meta.dirname ?? __dirname, "..", "..", "..", "..", "..", "hap-profiles"));
|
|
3709
|
+
const indexPath = join8(dir, "index.json");
|
|
3710
|
+
if (!existsSync8(indexPath)) {
|
|
3391
3711
|
warnNoProfiles(`No index.json at ${indexPath}`);
|
|
3392
3712
|
return 0;
|
|
3393
3713
|
}
|
|
3394
3714
|
let index;
|
|
3395
3715
|
try {
|
|
3396
|
-
index = JSON.parse(
|
|
3716
|
+
index = JSON.parse(readFileSync8(indexPath, "utf-8"));
|
|
3397
3717
|
} catch (err) {
|
|
3398
3718
|
console.error(`[ProfileLoader] Failed to parse ${indexPath}:`, err);
|
|
3399
3719
|
return 0;
|
|
3400
3720
|
}
|
|
3401
3721
|
let loaded = 0;
|
|
3402
3722
|
for (const [profileId, relativePath] of Object.entries(index.profiles)) {
|
|
3403
|
-
const profilePath =
|
|
3723
|
+
const profilePath = join8(dir, relativePath);
|
|
3404
3724
|
try {
|
|
3405
|
-
const profile = JSON.parse(
|
|
3725
|
+
const profile = JSON.parse(readFileSync8(profilePath, "utf-8"));
|
|
3406
3726
|
registerProfile(profileId, profile);
|
|
3407
3727
|
const shortName = profileId.split("/").pop()?.replace(/@.*$/, "");
|
|
3408
3728
|
if (shortName && shortName !== profileId) {
|
|
@@ -3435,30 +3755,30 @@ function warnNoProfiles(reason) {
|
|
|
3435
3755
|
}
|
|
3436
3756
|
|
|
3437
3757
|
// src/lib/manifest-loader.ts
|
|
3438
|
-
import { readFileSync as
|
|
3439
|
-
import { join as
|
|
3758
|
+
import { readFileSync as readFileSync9, existsSync as existsSync9 } from "fs";
|
|
3759
|
+
import { join as join9, resolve as resolve2 } from "path";
|
|
3440
3760
|
var manifests = /* @__PURE__ */ new Map();
|
|
3441
3761
|
function loadManifests(integrationsDir) {
|
|
3442
3762
|
const dir = resolve2(
|
|
3443
|
-
integrationsDir ?? process.env.SUVEREN_MANIFESTS_DIR ??
|
|
3763
|
+
integrationsDir ?? process.env.SUVEREN_MANIFESTS_DIR ?? join9(import.meta.dirname ?? __dirname, "..", "..", "..", "..", "content", "integrations")
|
|
3444
3764
|
);
|
|
3445
|
-
const indexPath =
|
|
3446
|
-
if (!
|
|
3765
|
+
const indexPath = join9(dir, "index.json");
|
|
3766
|
+
if (!existsSync9(indexPath)) {
|
|
3447
3767
|
console.error(`[ManifestLoader] No index.json found at ${indexPath}, skipping manifest loading`);
|
|
3448
3768
|
return 0;
|
|
3449
3769
|
}
|
|
3450
3770
|
let index;
|
|
3451
3771
|
try {
|
|
3452
|
-
index = JSON.parse(
|
|
3772
|
+
index = JSON.parse(readFileSync9(indexPath, "utf-8"));
|
|
3453
3773
|
} catch (err) {
|
|
3454
3774
|
console.error(`[ManifestLoader] Failed to parse ${indexPath}:`, err);
|
|
3455
3775
|
return 0;
|
|
3456
3776
|
}
|
|
3457
3777
|
let loaded = 0;
|
|
3458
3778
|
for (const [id, relativePath] of Object.entries(index.integrations)) {
|
|
3459
|
-
const manifestPath =
|
|
3779
|
+
const manifestPath = join9(dir, relativePath);
|
|
3460
3780
|
try {
|
|
3461
|
-
const manifest = JSON.parse(
|
|
3781
|
+
const manifest = JSON.parse(readFileSync9(manifestPath, "utf-8"));
|
|
3462
3782
|
manifests.set(id, manifest);
|
|
3463
3783
|
loaded++;
|
|
3464
3784
|
} catch (err) {
|
|
@@ -3476,6 +3796,7 @@ function getAllManifests() {
|
|
|
3476
3796
|
}
|
|
3477
3797
|
|
|
3478
3798
|
// bin/http.ts
|
|
3799
|
+
import { decodeAttestationBlob as decodeAttestationBlob3 } from "@hap/core";
|
|
3479
3800
|
var spUrl = process.env.SUVEREN_AS_URL ?? "https://www.suveren.ai";
|
|
3480
3801
|
var port = parseInt(process.env.SUVEREN_MCP_PORT ?? "3430", 10);
|
|
3481
3802
|
var state = new SharedState(spUrl);
|
|
@@ -3539,7 +3860,8 @@ app.post("/internal/configure", internalOnly, (req, res) => {
|
|
|
3539
3860
|
const key = Buffer.from(vaultKeyHex, "hex");
|
|
3540
3861
|
state.gateStore.setVaultKey(key);
|
|
3541
3862
|
state.denialLog.setVaultKey(key);
|
|
3542
|
-
|
|
3863
|
+
state.receiptArchive.setVaultKey(key);
|
|
3864
|
+
console.error("[Suveren MCP] Vault key configured \u2014 gate store + denial log + receipt archive encryption active");
|
|
3543
3865
|
}
|
|
3544
3866
|
if (apiKey) {
|
|
3545
3867
|
state.spClient.setApiKey(apiKey);
|
|
@@ -3836,6 +4158,27 @@ app.get("/internal/authorizations", internalOnly, (_req, res) => {
|
|
|
3836
4158
|
app.get("/internal/manifests", internalOnly, (_req, res) => {
|
|
3837
4159
|
res.json({ manifests: getAllManifests() });
|
|
3838
4160
|
});
|
|
4161
|
+
app.get("/internal/evidence", internalOnly, (_req, res) => {
|
|
4162
|
+
if (state.receiptArchive.isLocked()) {
|
|
4163
|
+
res.status(503).json({ error: "Vault locked \u2014 the evidence archive is encrypted until sign-in." });
|
|
4164
|
+
return;
|
|
4165
|
+
}
|
|
4166
|
+
const authorizations = state.receiptArchive.getAuthorizations().map((a) => ({
|
|
4167
|
+
...a,
|
|
4168
|
+
attestations: a.attestations.map((att) => {
|
|
4169
|
+
try {
|
|
4170
|
+
return { ...att, decoded: decodeAttestationBlob3(att.blob) };
|
|
4171
|
+
} catch {
|
|
4172
|
+
return att;
|
|
4173
|
+
}
|
|
4174
|
+
})
|
|
4175
|
+
}));
|
|
4176
|
+
res.json({
|
|
4177
|
+
receipts: state.receiptArchive.getReceipts(),
|
|
4178
|
+
authorizations,
|
|
4179
|
+
gates: state.gateStore.getAll()
|
|
4180
|
+
});
|
|
4181
|
+
});
|
|
3839
4182
|
app.get("/internal/brief", internalOnly, (_req, res) => {
|
|
3840
4183
|
try {
|
|
3841
4184
|
const enriched = state.getEnrichedAuthorizations();
|