@chrischall/eventbrite-mcp 0.3.1 → 0.3.2
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/dist/bundle.js +982 -327
- package/dist/version.js +1 -1
- package/package.json +2 -2
- package/server.json +2 -2
package/dist/bundle.js
CHANGED
|
@@ -7287,19 +7287,207 @@ var init_errors = __esm({
|
|
|
7287
7287
|
}
|
|
7288
7288
|
});
|
|
7289
7289
|
|
|
7290
|
+
// node_modules/@fetchproxy/protocol/dist/crypto.js
|
|
7291
|
+
async function exportRaw(key, format) {
|
|
7292
|
+
const buf = await subtle.exportKey(format, key);
|
|
7293
|
+
return new Uint8Array(buf);
|
|
7294
|
+
}
|
|
7295
|
+
async function generateX25519() {
|
|
7296
|
+
const kp = await subtle.generateKey({ name: "X25519" }, true, [
|
|
7297
|
+
"deriveBits"
|
|
7298
|
+
]);
|
|
7299
|
+
const priv = await exportRaw(kp.privateKey, "pkcs8");
|
|
7300
|
+
const pub = await exportRaw(kp.publicKey, "raw");
|
|
7301
|
+
return { privateKey: priv.slice(-32), publicKey: pub };
|
|
7302
|
+
}
|
|
7303
|
+
async function generateEd25519() {
|
|
7304
|
+
const kp = await subtle.generateKey({ name: "Ed25519" }, true, [
|
|
7305
|
+
"sign",
|
|
7306
|
+
"verify"
|
|
7307
|
+
]);
|
|
7308
|
+
const priv = await exportRaw(kp.privateKey, "pkcs8");
|
|
7309
|
+
const pub = await exportRaw(kp.publicKey, "raw");
|
|
7310
|
+
return { privateKey: priv.slice(-32), publicKey: pub };
|
|
7311
|
+
}
|
|
7312
|
+
async function importX25519Priv(raw) {
|
|
7313
|
+
const prefix = new Uint8Array([
|
|
7314
|
+
48,
|
|
7315
|
+
46,
|
|
7316
|
+
2,
|
|
7317
|
+
1,
|
|
7318
|
+
0,
|
|
7319
|
+
48,
|
|
7320
|
+
5,
|
|
7321
|
+
6,
|
|
7322
|
+
3,
|
|
7323
|
+
43,
|
|
7324
|
+
101,
|
|
7325
|
+
110,
|
|
7326
|
+
4,
|
|
7327
|
+
34,
|
|
7328
|
+
4,
|
|
7329
|
+
32
|
|
7330
|
+
]);
|
|
7331
|
+
const pkcs8 = new Uint8Array(prefix.length + raw.length);
|
|
7332
|
+
pkcs8.set(prefix, 0);
|
|
7333
|
+
pkcs8.set(raw, prefix.length);
|
|
7334
|
+
return subtle.importKey("pkcs8", pkcs8, { name: "X25519" }, false, [
|
|
7335
|
+
"deriveBits"
|
|
7336
|
+
]);
|
|
7337
|
+
}
|
|
7338
|
+
async function importX25519Pub(raw) {
|
|
7339
|
+
return subtle.importKey("raw", raw, { name: "X25519" }, false, []);
|
|
7340
|
+
}
|
|
7341
|
+
async function importEd25519Priv(raw) {
|
|
7342
|
+
const prefix = new Uint8Array([
|
|
7343
|
+
48,
|
|
7344
|
+
46,
|
|
7345
|
+
2,
|
|
7346
|
+
1,
|
|
7347
|
+
0,
|
|
7348
|
+
48,
|
|
7349
|
+
5,
|
|
7350
|
+
6,
|
|
7351
|
+
3,
|
|
7352
|
+
43,
|
|
7353
|
+
101,
|
|
7354
|
+
112,
|
|
7355
|
+
4,
|
|
7356
|
+
34,
|
|
7357
|
+
4,
|
|
7358
|
+
32
|
|
7359
|
+
]);
|
|
7360
|
+
const pkcs8 = new Uint8Array(prefix.length + raw.length);
|
|
7361
|
+
pkcs8.set(prefix, 0);
|
|
7362
|
+
pkcs8.set(raw, prefix.length);
|
|
7363
|
+
return subtle.importKey("pkcs8", pkcs8, { name: "Ed25519" }, false, ["sign"]);
|
|
7364
|
+
}
|
|
7365
|
+
async function importEd25519Pub(raw) {
|
|
7366
|
+
return subtle.importKey("raw", raw, { name: "Ed25519" }, false, ["verify"]);
|
|
7367
|
+
}
|
|
7368
|
+
async function ecdhX25519(privRaw, pubRaw) {
|
|
7369
|
+
const priv = await importX25519Priv(privRaw);
|
|
7370
|
+
const pub = await importX25519Pub(pubRaw);
|
|
7371
|
+
const bits = await subtle.deriveBits({ name: "X25519", public: pub }, priv, 256);
|
|
7372
|
+
return new Uint8Array(bits);
|
|
7373
|
+
}
|
|
7374
|
+
async function ed25519Sign(privRaw, msg) {
|
|
7375
|
+
const priv = await importEd25519Priv(privRaw);
|
|
7376
|
+
const sig = await subtle.sign({ name: "Ed25519" }, priv, msg);
|
|
7377
|
+
return new Uint8Array(sig);
|
|
7378
|
+
}
|
|
7379
|
+
async function ed25519Verify(pubRaw, msg, sig) {
|
|
7380
|
+
const pub = await importEd25519Pub(pubRaw);
|
|
7381
|
+
return subtle.verify({ name: "Ed25519" }, pub, sig, msg);
|
|
7382
|
+
}
|
|
7383
|
+
async function hkdfSha256(ikm, salt, info, lenBytes) {
|
|
7384
|
+
const baseKey = await subtle.importKey("raw", ikm, { name: "HKDF" }, false, [
|
|
7385
|
+
"deriveBits"
|
|
7386
|
+
]);
|
|
7387
|
+
const bits = await subtle.deriveBits({
|
|
7388
|
+
name: "HKDF",
|
|
7389
|
+
hash: "SHA-256",
|
|
7390
|
+
salt,
|
|
7391
|
+
info
|
|
7392
|
+
}, baseKey, lenBytes * 8);
|
|
7393
|
+
return new Uint8Array(bits);
|
|
7394
|
+
}
|
|
7395
|
+
async function aesGcmSeal(key, iv, plaintext, aad) {
|
|
7396
|
+
const k = await subtle.importKey("raw", key, { name: "AES-GCM" }, false, [
|
|
7397
|
+
"encrypt"
|
|
7398
|
+
]);
|
|
7399
|
+
const ct = await subtle.encrypt({ name: "AES-GCM", iv, additionalData: aad }, k, plaintext);
|
|
7400
|
+
return new Uint8Array(ct);
|
|
7401
|
+
}
|
|
7402
|
+
async function aesGcmOpen(key, iv, ciphertext, aad) {
|
|
7403
|
+
const k = await subtle.importKey("raw", key, { name: "AES-GCM" }, false, [
|
|
7404
|
+
"decrypt"
|
|
7405
|
+
]);
|
|
7406
|
+
const pt = await subtle.decrypt({ name: "AES-GCM", iv, additionalData: aad }, k, ciphertext);
|
|
7407
|
+
return new Uint8Array(pt);
|
|
7408
|
+
}
|
|
7409
|
+
async function sha256(data) {
|
|
7410
|
+
const h = await subtle.digest("SHA-256", data);
|
|
7411
|
+
return new Uint8Array(h);
|
|
7412
|
+
}
|
|
7413
|
+
var subtle;
|
|
7414
|
+
var init_crypto = __esm({
|
|
7415
|
+
"node_modules/@fetchproxy/protocol/dist/crypto.js"() {
|
|
7416
|
+
subtle = globalThis.crypto.subtle;
|
|
7417
|
+
}
|
|
7418
|
+
});
|
|
7419
|
+
|
|
7420
|
+
// node_modules/@fetchproxy/protocol/dist/encoding.js
|
|
7421
|
+
function toB64(bytes) {
|
|
7422
|
+
let s = "";
|
|
7423
|
+
for (let i = 0; i < bytes.length; i++)
|
|
7424
|
+
s += String.fromCharCode(bytes[i]);
|
|
7425
|
+
return btoa(s);
|
|
7426
|
+
}
|
|
7427
|
+
function fromB64(s) {
|
|
7428
|
+
const bin = atob(s);
|
|
7429
|
+
const out = new Uint8Array(bin.length);
|
|
7430
|
+
for (let i = 0; i < bin.length; i++)
|
|
7431
|
+
out[i] = bin.charCodeAt(i);
|
|
7432
|
+
return out;
|
|
7433
|
+
}
|
|
7434
|
+
var init_encoding = __esm({
|
|
7435
|
+
"node_modules/@fetchproxy/protocol/dist/encoding.js"() {
|
|
7436
|
+
}
|
|
7437
|
+
});
|
|
7438
|
+
|
|
7290
7439
|
// node_modules/@fetchproxy/protocol/dist/frames.js
|
|
7291
|
-
function
|
|
7292
|
-
const
|
|
7440
|
+
function helloSignaturePayload(mcpId, sessionNonce, sessionPub, answersExtNonce) {
|
|
7441
|
+
const id = enc.encode(mcpId);
|
|
7442
|
+
const out = new Uint8Array(id.length + sessionNonce.length + sessionPub.length + answersExtNonce.length);
|
|
7443
|
+
out.set(id, 0);
|
|
7444
|
+
out.set(sessionNonce, id.length);
|
|
7445
|
+
out.set(sessionPub, id.length + sessionNonce.length);
|
|
7446
|
+
out.set(answersExtNonce, id.length + sessionNonce.length + sessionPub.length);
|
|
7447
|
+
return out;
|
|
7448
|
+
}
|
|
7449
|
+
function answersNoExtSession(answersExtNonce) {
|
|
7450
|
+
let bytes;
|
|
7451
|
+
try {
|
|
7452
|
+
bytes = fromB64(answersExtNonce);
|
|
7453
|
+
} catch {
|
|
7454
|
+
return false;
|
|
7455
|
+
}
|
|
7456
|
+
if (bytes.length !== ANSWERS_NO_EXT_SESSION.length)
|
|
7457
|
+
return false;
|
|
7458
|
+
for (const b of bytes)
|
|
7459
|
+
if (b !== 0)
|
|
7460
|
+
return false;
|
|
7461
|
+
return true;
|
|
7462
|
+
}
|
|
7463
|
+
function readySignaturePayload(mcpHelloNonce, extHelloNonce, extensionSessionPub, mcpSessionPub) {
|
|
7464
|
+
const out = new Uint8Array(mcpHelloNonce.length + extHelloNonce.length + extensionSessionPub.length + mcpSessionPub.length);
|
|
7293
7465
|
out.set(mcpHelloNonce, 0);
|
|
7294
7466
|
out.set(extHelloNonce, mcpHelloNonce.length);
|
|
7295
7467
|
out.set(extensionSessionPub, mcpHelloNonce.length + extHelloNonce.length);
|
|
7468
|
+
out.set(mcpSessionPub, mcpHelloNonce.length + extHelloNonce.length + extensionSessionPub.length);
|
|
7296
7469
|
return out;
|
|
7297
7470
|
}
|
|
7298
|
-
|
|
7471
|
+
async function transcriptHash(mcpNonce, extNonce, mcpSessionPub, extSessionPub) {
|
|
7472
|
+
const out = new Uint8Array(mcpNonce.length + extNonce.length + mcpSessionPub.length + extSessionPub.length);
|
|
7473
|
+
out.set(mcpNonce, 0);
|
|
7474
|
+
out.set(extNonce, mcpNonce.length);
|
|
7475
|
+
out.set(mcpSessionPub, mcpNonce.length + extNonce.length);
|
|
7476
|
+
out.set(extSessionPub, mcpNonce.length + extNonce.length + mcpSessionPub.length);
|
|
7477
|
+
return sha256(out);
|
|
7478
|
+
}
|
|
7479
|
+
function frameAad(mcpId, seq, direction) {
|
|
7480
|
+
return enc.encode(`fetchproxy/4/frame\0${mcpId}\0${seq}\0${direction}`);
|
|
7481
|
+
}
|
|
7482
|
+
var PROTOCOL_VERSION, HKDF_SESSION_INFO, enc, ANSWERS_NO_EXT_SESSION, KNOWN_CAPABILITIES;
|
|
7299
7483
|
var init_frames = __esm({
|
|
7300
7484
|
"node_modules/@fetchproxy/protocol/dist/frames.js"() {
|
|
7301
|
-
|
|
7302
|
-
|
|
7485
|
+
init_crypto();
|
|
7486
|
+
init_encoding();
|
|
7487
|
+
PROTOCOL_VERSION = 4;
|
|
7488
|
+
HKDF_SESSION_INFO = "fetchproxy/4.0.0/session";
|
|
7489
|
+
enc = new TextEncoder();
|
|
7490
|
+
ANSWERS_NO_EXT_SESSION = new Uint8Array(32);
|
|
7303
7491
|
KNOWN_CAPABILITIES = /* @__PURE__ */ new Set([
|
|
7304
7492
|
"fetch",
|
|
7305
7493
|
"fetch_in_page",
|
|
@@ -7381,6 +7569,143 @@ var init_json_pointer = __esm({
|
|
|
7381
7569
|
}
|
|
7382
7570
|
});
|
|
7383
7571
|
|
|
7572
|
+
// node_modules/@fetchproxy/protocol/dist/public-suffix.js
|
|
7573
|
+
function isPublicSuffix(host) {
|
|
7574
|
+
const normalised = host.trim().toLowerCase().replace(/\.$/, "");
|
|
7575
|
+
if (normalised === "")
|
|
7576
|
+
return false;
|
|
7577
|
+
const labels = normalised.split(".");
|
|
7578
|
+
if (labels.some((l) => l === ""))
|
|
7579
|
+
return false;
|
|
7580
|
+
if (labels.length === 1)
|
|
7581
|
+
return true;
|
|
7582
|
+
if (LISTED_PUBLIC_SUFFIXES.has(normalised))
|
|
7583
|
+
return true;
|
|
7584
|
+
if (labels.length === 2) {
|
|
7585
|
+
const [secondLevel, tld] = labels;
|
|
7586
|
+
if (secondLevel && tld && /^[a-z]{2}$/.test(tld) && CCTLD_ADMIN_LABELS.has(secondLevel)) {
|
|
7587
|
+
return true;
|
|
7588
|
+
}
|
|
7589
|
+
}
|
|
7590
|
+
return false;
|
|
7591
|
+
}
|
|
7592
|
+
var CCTLD_ADMIN_LABELS, LISTED_PUBLIC_SUFFIXES;
|
|
7593
|
+
var init_public_suffix = __esm({
|
|
7594
|
+
"node_modules/@fetchproxy/protocol/dist/public-suffix.js"() {
|
|
7595
|
+
CCTLD_ADMIN_LABELS = /* @__PURE__ */ new Set([
|
|
7596
|
+
"ac",
|
|
7597
|
+
"co",
|
|
7598
|
+
"com",
|
|
7599
|
+
"edu",
|
|
7600
|
+
"gob",
|
|
7601
|
+
"gov",
|
|
7602
|
+
"mil",
|
|
7603
|
+
"net",
|
|
7604
|
+
"nom",
|
|
7605
|
+
"org",
|
|
7606
|
+
"sch"
|
|
7607
|
+
]);
|
|
7608
|
+
LISTED_PUBLIC_SUFFIXES = /* @__PURE__ */ new Set([
|
|
7609
|
+
// --- ICANN, under ccTLDs, outside CCTLD_ADMIN_LABELS ---
|
|
7610
|
+
"ad.jp",
|
|
7611
|
+
"ed.jp",
|
|
7612
|
+
"gr.jp",
|
|
7613
|
+
"lg.jp",
|
|
7614
|
+
"ne.jp",
|
|
7615
|
+
"or.jp",
|
|
7616
|
+
"go.kr",
|
|
7617
|
+
"ne.kr",
|
|
7618
|
+
"or.kr",
|
|
7619
|
+
"pe.kr",
|
|
7620
|
+
"re.kr",
|
|
7621
|
+
"go.id",
|
|
7622
|
+
"my.id",
|
|
7623
|
+
"or.id",
|
|
7624
|
+
"web.id",
|
|
7625
|
+
"go.th",
|
|
7626
|
+
"in.th",
|
|
7627
|
+
"or.th",
|
|
7628
|
+
"me.uk",
|
|
7629
|
+
"ltd.uk",
|
|
7630
|
+
"plc.uk",
|
|
7631
|
+
"nhs.uk",
|
|
7632
|
+
"mod.uk",
|
|
7633
|
+
"police.uk",
|
|
7634
|
+
"parliament.uk",
|
|
7635
|
+
"asn.au",
|
|
7636
|
+
"id.au",
|
|
7637
|
+
"gen.in",
|
|
7638
|
+
"ind.in",
|
|
7639
|
+
"firm.in",
|
|
7640
|
+
"eu.org",
|
|
7641
|
+
// --- Vendor ("private") suffixes: one entry = every customer's site ---
|
|
7642
|
+
"github.io",
|
|
7643
|
+
"githubusercontent.com",
|
|
7644
|
+
"gitlab.io",
|
|
7645
|
+
"bitbucket.io",
|
|
7646
|
+
"pages.dev",
|
|
7647
|
+
"workers.dev",
|
|
7648
|
+
"r2.dev",
|
|
7649
|
+
"trycloudflare.com",
|
|
7650
|
+
"vercel.app",
|
|
7651
|
+
"now.sh",
|
|
7652
|
+
"netlify.app",
|
|
7653
|
+
"netlify.live",
|
|
7654
|
+
"herokuapp.com",
|
|
7655
|
+
"herokussl.com",
|
|
7656
|
+
"appspot.com",
|
|
7657
|
+
"web.app",
|
|
7658
|
+
"firebaseapp.com",
|
|
7659
|
+
"cloudfunctions.net",
|
|
7660
|
+
"blogspot.com",
|
|
7661
|
+
"azurewebsites.net",
|
|
7662
|
+
"azurestaticapps.net",
|
|
7663
|
+
"azureedge.net",
|
|
7664
|
+
"azurecontainer.io",
|
|
7665
|
+
"cloudapp.azure.com",
|
|
7666
|
+
"trafficmanager.net",
|
|
7667
|
+
"s3.amazonaws.com",
|
|
7668
|
+
"compute.amazonaws.com",
|
|
7669
|
+
"compute-1.amazonaws.com",
|
|
7670
|
+
"elasticbeanstalk.com",
|
|
7671
|
+
"cloudfront.net",
|
|
7672
|
+
"awsapprunner.com",
|
|
7673
|
+
"amplifyapp.com",
|
|
7674
|
+
"fly.dev",
|
|
7675
|
+
"onrender.com",
|
|
7676
|
+
"railway.app",
|
|
7677
|
+
"koyeb.app",
|
|
7678
|
+
"deno.dev",
|
|
7679
|
+
"replit.dev",
|
|
7680
|
+
"repl.co",
|
|
7681
|
+
"glitch.me",
|
|
7682
|
+
"surge.sh",
|
|
7683
|
+
"neocities.org",
|
|
7684
|
+
"pythonanywhere.com",
|
|
7685
|
+
"readthedocs.io",
|
|
7686
|
+
"gitbook.io",
|
|
7687
|
+
"myshopify.com",
|
|
7688
|
+
"wixsite.com",
|
|
7689
|
+
"editorx.io",
|
|
7690
|
+
"webflow.io",
|
|
7691
|
+
"bubbleapps.io",
|
|
7692
|
+
"notion.site",
|
|
7693
|
+
"framer.app",
|
|
7694
|
+
"framer.website",
|
|
7695
|
+
"ngrok.io",
|
|
7696
|
+
"ngrok.app",
|
|
7697
|
+
"ngrok-free.app",
|
|
7698
|
+
"loca.lt",
|
|
7699
|
+
"duckdns.org",
|
|
7700
|
+
"ddns.net",
|
|
7701
|
+
"no-ip.org",
|
|
7702
|
+
"dyndns.org",
|
|
7703
|
+
"hopto.org",
|
|
7704
|
+
"zapto.org"
|
|
7705
|
+
]);
|
|
7706
|
+
}
|
|
7707
|
+
});
|
|
7708
|
+
|
|
7384
7709
|
// node_modules/@fetchproxy/protocol/dist/validate.js
|
|
7385
7710
|
function assertObject(x, label) {
|
|
7386
7711
|
if (typeof x !== "object" || x === null || Array.isArray(x)) {
|
|
@@ -7405,6 +7730,18 @@ function assertBase64(x, label) {
|
|
|
7405
7730
|
if (!BASE64_RE.test(x))
|
|
7406
7731
|
throw new ProtocolError(`${label}: invalid base64`);
|
|
7407
7732
|
}
|
|
7733
|
+
function base64ByteLength(s) {
|
|
7734
|
+
if (s.length % 4 !== 0)
|
|
7735
|
+
return null;
|
|
7736
|
+
const pad = s.endsWith("==") ? 2 : s.endsWith("=") ? 1 : 0;
|
|
7737
|
+
return s.length / 4 * 3 - pad;
|
|
7738
|
+
}
|
|
7739
|
+
function assertBase64Bytes(x, label, bytes) {
|
|
7740
|
+
assertBase64(x, label);
|
|
7741
|
+
if (base64ByteLength(x) !== bytes) {
|
|
7742
|
+
throw new ProtocolError(`${label}: expected base64 of ${bytes} raw bytes`);
|
|
7743
|
+
}
|
|
7744
|
+
}
|
|
7408
7745
|
function assertBoolean(x, label) {
|
|
7409
7746
|
if (typeof x !== "boolean") {
|
|
7410
7747
|
throw new ProtocolError(`${label}: must be boolean`);
|
|
@@ -7733,6 +8070,9 @@ function validateHello(raw) {
|
|
|
7733
8070
|
if (!HOSTNAME_RE.test(d)) {
|
|
7734
8071
|
throw new ProtocolError(`hello.domains: invalid hostname ${JSON.stringify(d)}`);
|
|
7735
8072
|
}
|
|
8073
|
+
if (isPublicSuffix(d)) {
|
|
8074
|
+
throw new ProtocolError(`hello.domains: ${JSON.stringify(d)} is a public suffix \u2014 declare a registrable domain (e.g. "example.${d}"), not the suffix every site under it shares`);
|
|
8075
|
+
}
|
|
7736
8076
|
}
|
|
7737
8077
|
if (raw.capabilities !== void 0) {
|
|
7738
8078
|
if (!Array.isArray(raw.capabilities)) {
|
|
@@ -7790,6 +8130,8 @@ function validateHello(raw) {
|
|
|
7790
8130
|
assertBase64(raw.identityX25519Pub, "hello.identityX25519Pub");
|
|
7791
8131
|
assertBase64(raw.identityEd25519Pub, "hello.identityEd25519Pub");
|
|
7792
8132
|
assertBase64(raw.sessionNonce, "hello.sessionNonce");
|
|
8133
|
+
assertBase64Bytes(raw.sessionPub, "hello.sessionPub", 32);
|
|
8134
|
+
assertBase64Bytes(raw.answersExtNonce, "hello.answersExtNonce", 32);
|
|
7793
8135
|
assertBase64(raw.sessionSig, "hello.sessionSig");
|
|
7794
8136
|
return raw;
|
|
7795
8137
|
}
|
|
@@ -7807,11 +8149,34 @@ function validateHello(raw) {
|
|
|
7807
8149
|
}
|
|
7808
8150
|
throw new ProtocolError(`hello.role: must be 'server' or 'extension', got ${String(role)}`);
|
|
7809
8151
|
}
|
|
8152
|
+
function peekHelloVersion(raw) {
|
|
8153
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw))
|
|
8154
|
+
return null;
|
|
8155
|
+
const frame = raw;
|
|
8156
|
+
if (frame.type !== "hello")
|
|
8157
|
+
return null;
|
|
8158
|
+
const protocolVersion = frame.protocolVersion;
|
|
8159
|
+
if (typeof protocolVersion !== "number" || !Number.isInteger(protocolVersion))
|
|
8160
|
+
return null;
|
|
8161
|
+
const accepts = [];
|
|
8162
|
+
if (Array.isArray(frame.accepts)) {
|
|
8163
|
+
for (const a of frame.accepts) {
|
|
8164
|
+
if (typeof a === "string")
|
|
8165
|
+
accepts.push(a);
|
|
8166
|
+
}
|
|
8167
|
+
}
|
|
8168
|
+
return {
|
|
8169
|
+
protocolVersion,
|
|
8170
|
+
mcpId: isValidMcpId(frame.mcpId) ? frame.mcpId : null,
|
|
8171
|
+
accepts
|
|
8172
|
+
};
|
|
8173
|
+
}
|
|
7810
8174
|
function validateReady(raw) {
|
|
7811
8175
|
assertString(raw.mcpId, "ready.mcpId");
|
|
7812
8176
|
if (!isValidMcpId(raw.mcpId))
|
|
7813
8177
|
throw new ProtocolError("ready.mcpId: invalid format");
|
|
7814
8178
|
assertBase64(raw.extensionSessionPub, "ready.extensionSessionPub");
|
|
8179
|
+
assertBase64Bytes(raw.mcpSessionPub, "ready.mcpSessionPub", 32);
|
|
7815
8180
|
assertBase64(raw.sessionSig, "ready.sessionSig");
|
|
7816
8181
|
return raw;
|
|
7817
8182
|
}
|
|
@@ -7830,7 +8195,7 @@ function validatePairPending(raw) {
|
|
|
7830
8195
|
throw new ProtocolError("pair-pending.mcpId: invalid format");
|
|
7831
8196
|
assertString(raw.pairCode, "pair-pending.pairCode");
|
|
7832
8197
|
if (!PAIR_CODE_RE.test(raw.pairCode)) {
|
|
7833
|
-
throw new ProtocolError(`pair-pending.pairCode: must match
|
|
8198
|
+
throw new ProtocolError(`pair-pending.pairCode: must match XXXX-XXXX, got ${String(raw.pairCode)}`);
|
|
7834
8199
|
}
|
|
7835
8200
|
return { type: "pair-pending", mcpId: raw.mcpId, pairCode: raw.pairCode };
|
|
7836
8201
|
}
|
|
@@ -8279,6 +8644,7 @@ var init_validate = __esm({
|
|
|
8279
8644
|
init_frames();
|
|
8280
8645
|
init_mcp_id();
|
|
8281
8646
|
init_json_pointer();
|
|
8647
|
+
init_public_suffix();
|
|
8282
8648
|
ProtocolError = class extends Error {
|
|
8283
8649
|
constructor(message) {
|
|
8284
8650
|
super(message);
|
|
@@ -8292,10 +8658,10 @@ var init_validate = __esm({
|
|
|
8292
8658
|
HEADER_NAME_RE = /^[A-Za-z0-9_\-]{1,128}$/;
|
|
8293
8659
|
HOSTNAME_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)+$/i;
|
|
8294
8660
|
CAPTURE_PATH_RE = /^\/[A-Za-z0-9._~%\-/]*\*?$/;
|
|
8295
|
-
DOM_SELECTOR_RE = /^[
|
|
8661
|
+
DOM_SELECTOR_RE = /^[^\x00-\x1f\x7f]{1,512}$/;
|
|
8296
8662
|
DOM_ATTRIBUTE_RE = /^[A-Za-z_:][A-Za-z0-9_:.\-]{0,127}$/;
|
|
8297
8663
|
GRAPHQL_OP_NAME_RE = /^[_A-Za-z][_0-9A-Za-z]{0,127}$/;
|
|
8298
|
-
PAIR_CODE_RE = /^\d{
|
|
8664
|
+
PAIR_CODE_RE = /^\d{4}-\d{4}$/;
|
|
8299
8665
|
KNOWN_RESPONSE_OPS = /* @__PURE__ */ new Set([
|
|
8300
8666
|
...KNOWN_CAPABILITIES,
|
|
8301
8667
|
"graphql_query"
|
|
@@ -8303,180 +8669,33 @@ var init_validate = __esm({
|
|
|
8303
8669
|
}
|
|
8304
8670
|
});
|
|
8305
8671
|
|
|
8306
|
-
// node_modules/@fetchproxy/protocol/dist/crypto.js
|
|
8307
|
-
async function exportRaw(key, format) {
|
|
8308
|
-
const buf = await subtle.exportKey(format, key);
|
|
8309
|
-
return new Uint8Array(buf);
|
|
8310
|
-
}
|
|
8311
|
-
async function generateX25519() {
|
|
8312
|
-
const kp = await subtle.generateKey({ name: "X25519" }, true, [
|
|
8313
|
-
"deriveBits"
|
|
8314
|
-
]);
|
|
8315
|
-
const priv = await exportRaw(kp.privateKey, "pkcs8");
|
|
8316
|
-
const pub = await exportRaw(kp.publicKey, "raw");
|
|
8317
|
-
return { privateKey: priv.slice(-32), publicKey: pub };
|
|
8318
|
-
}
|
|
8319
|
-
async function generateEd25519() {
|
|
8320
|
-
const kp = await subtle.generateKey({ name: "Ed25519" }, true, [
|
|
8321
|
-
"sign",
|
|
8322
|
-
"verify"
|
|
8323
|
-
]);
|
|
8324
|
-
const priv = await exportRaw(kp.privateKey, "pkcs8");
|
|
8325
|
-
const pub = await exportRaw(kp.publicKey, "raw");
|
|
8326
|
-
return { privateKey: priv.slice(-32), publicKey: pub };
|
|
8327
|
-
}
|
|
8328
|
-
async function importX25519Priv(raw) {
|
|
8329
|
-
const prefix = new Uint8Array([
|
|
8330
|
-
48,
|
|
8331
|
-
46,
|
|
8332
|
-
2,
|
|
8333
|
-
1,
|
|
8334
|
-
0,
|
|
8335
|
-
48,
|
|
8336
|
-
5,
|
|
8337
|
-
6,
|
|
8338
|
-
3,
|
|
8339
|
-
43,
|
|
8340
|
-
101,
|
|
8341
|
-
110,
|
|
8342
|
-
4,
|
|
8343
|
-
34,
|
|
8344
|
-
4,
|
|
8345
|
-
32
|
|
8346
|
-
]);
|
|
8347
|
-
const pkcs8 = new Uint8Array(prefix.length + raw.length);
|
|
8348
|
-
pkcs8.set(prefix, 0);
|
|
8349
|
-
pkcs8.set(raw, prefix.length);
|
|
8350
|
-
return subtle.importKey("pkcs8", pkcs8, { name: "X25519" }, false, [
|
|
8351
|
-
"deriveBits"
|
|
8352
|
-
]);
|
|
8353
|
-
}
|
|
8354
|
-
async function importX25519Pub(raw) {
|
|
8355
|
-
return subtle.importKey("raw", raw, { name: "X25519" }, false, []);
|
|
8356
|
-
}
|
|
8357
|
-
async function importEd25519Priv(raw) {
|
|
8358
|
-
const prefix = new Uint8Array([
|
|
8359
|
-
48,
|
|
8360
|
-
46,
|
|
8361
|
-
2,
|
|
8362
|
-
1,
|
|
8363
|
-
0,
|
|
8364
|
-
48,
|
|
8365
|
-
5,
|
|
8366
|
-
6,
|
|
8367
|
-
3,
|
|
8368
|
-
43,
|
|
8369
|
-
101,
|
|
8370
|
-
112,
|
|
8371
|
-
4,
|
|
8372
|
-
34,
|
|
8373
|
-
4,
|
|
8374
|
-
32
|
|
8375
|
-
]);
|
|
8376
|
-
const pkcs8 = new Uint8Array(prefix.length + raw.length);
|
|
8377
|
-
pkcs8.set(prefix, 0);
|
|
8378
|
-
pkcs8.set(raw, prefix.length);
|
|
8379
|
-
return subtle.importKey("pkcs8", pkcs8, { name: "Ed25519" }, false, ["sign"]);
|
|
8380
|
-
}
|
|
8381
|
-
async function importEd25519Pub(raw) {
|
|
8382
|
-
return subtle.importKey("raw", raw, { name: "Ed25519" }, false, ["verify"]);
|
|
8383
|
-
}
|
|
8384
|
-
async function ecdhX25519(privRaw, pubRaw) {
|
|
8385
|
-
const priv = await importX25519Priv(privRaw);
|
|
8386
|
-
const pub = await importX25519Pub(pubRaw);
|
|
8387
|
-
const bits = await subtle.deriveBits({ name: "X25519", public: pub }, priv, 256);
|
|
8388
|
-
return new Uint8Array(bits);
|
|
8389
|
-
}
|
|
8390
|
-
async function ed25519Sign(privRaw, msg) {
|
|
8391
|
-
const priv = await importEd25519Priv(privRaw);
|
|
8392
|
-
const sig = await subtle.sign({ name: "Ed25519" }, priv, msg);
|
|
8393
|
-
return new Uint8Array(sig);
|
|
8394
|
-
}
|
|
8395
|
-
async function ed25519Verify(pubRaw, msg, sig) {
|
|
8396
|
-
const pub = await importEd25519Pub(pubRaw);
|
|
8397
|
-
return subtle.verify({ name: "Ed25519" }, pub, sig, msg);
|
|
8398
|
-
}
|
|
8399
|
-
async function hkdfSha256(ikm, salt, info, lenBytes) {
|
|
8400
|
-
const baseKey = await subtle.importKey("raw", ikm, { name: "HKDF" }, false, [
|
|
8401
|
-
"deriveBits"
|
|
8402
|
-
]);
|
|
8403
|
-
const bits = await subtle.deriveBits({
|
|
8404
|
-
name: "HKDF",
|
|
8405
|
-
hash: "SHA-256",
|
|
8406
|
-
salt,
|
|
8407
|
-
info
|
|
8408
|
-
}, baseKey, lenBytes * 8);
|
|
8409
|
-
return new Uint8Array(bits);
|
|
8410
|
-
}
|
|
8411
|
-
async function aesGcmSeal(key, iv, plaintext) {
|
|
8412
|
-
const k = await subtle.importKey("raw", key, { name: "AES-GCM" }, false, [
|
|
8413
|
-
"encrypt"
|
|
8414
|
-
]);
|
|
8415
|
-
const ct = await subtle.encrypt({ name: "AES-GCM", iv }, k, plaintext);
|
|
8416
|
-
return new Uint8Array(ct);
|
|
8417
|
-
}
|
|
8418
|
-
async function aesGcmOpen(key, iv, ciphertext) {
|
|
8419
|
-
const k = await subtle.importKey("raw", key, { name: "AES-GCM" }, false, [
|
|
8420
|
-
"decrypt"
|
|
8421
|
-
]);
|
|
8422
|
-
const pt = await subtle.decrypt({ name: "AES-GCM", iv }, k, ciphertext);
|
|
8423
|
-
return new Uint8Array(pt);
|
|
8424
|
-
}
|
|
8425
|
-
async function sha256(data) {
|
|
8426
|
-
const h = await subtle.digest("SHA-256", data);
|
|
8427
|
-
return new Uint8Array(h);
|
|
8428
|
-
}
|
|
8429
|
-
var subtle;
|
|
8430
|
-
var init_crypto = __esm({
|
|
8431
|
-
"node_modules/@fetchproxy/protocol/dist/crypto.js"() {
|
|
8432
|
-
subtle = globalThis.crypto.subtle;
|
|
8433
|
-
}
|
|
8434
|
-
});
|
|
8435
|
-
|
|
8436
|
-
// node_modules/@fetchproxy/protocol/dist/encoding.js
|
|
8437
|
-
function toB64(bytes) {
|
|
8438
|
-
let s = "";
|
|
8439
|
-
for (let i = 0; i < bytes.length; i++)
|
|
8440
|
-
s += String.fromCharCode(bytes[i]);
|
|
8441
|
-
return btoa(s);
|
|
8442
|
-
}
|
|
8443
|
-
function fromB64(s) {
|
|
8444
|
-
const bin = atob(s);
|
|
8445
|
-
const out = new Uint8Array(bin.length);
|
|
8446
|
-
for (let i = 0; i < bin.length; i++)
|
|
8447
|
-
out[i] = bin.charCodeAt(i);
|
|
8448
|
-
return out;
|
|
8449
|
-
}
|
|
8450
|
-
function concatBytes(a, b) {
|
|
8451
|
-
const out = new Uint8Array(a.length + b.length);
|
|
8452
|
-
out.set(a, 0);
|
|
8453
|
-
out.set(b, a.length);
|
|
8454
|
-
return out;
|
|
8455
|
-
}
|
|
8456
|
-
var init_encoding = __esm({
|
|
8457
|
-
"node_modules/@fetchproxy/protocol/dist/encoding.js"() {
|
|
8458
|
-
}
|
|
8459
|
-
});
|
|
8460
|
-
|
|
8461
8672
|
// node_modules/@fetchproxy/protocol/dist/pair-code.js
|
|
8462
|
-
async function
|
|
8463
|
-
const
|
|
8464
|
-
const
|
|
8465
|
-
const
|
|
8466
|
-
|
|
8467
|
-
|
|
8468
|
-
|
|
8469
|
-
const
|
|
8470
|
-
|
|
8471
|
-
|
|
8472
|
-
}
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
|
|
8673
|
+
async function pairTranscript(mcpIdentityPub, extIdentityPub, mcpNonce, extNonce, mcpSessionPub) {
|
|
8674
|
+
const label = enc2.encode(PAIR_LABEL);
|
|
8675
|
+
const parts = [mcpIdentityPub, extIdentityPub, mcpNonce, extNonce, mcpSessionPub];
|
|
8676
|
+
const out = new Uint8Array(label.length + 1 + parts.reduce((n, p) => n + p.length, 0));
|
|
8677
|
+
out.set(label, 0);
|
|
8678
|
+
out[label.length] = 0;
|
|
8679
|
+
let at = label.length + 1;
|
|
8680
|
+
for (const p of parts) {
|
|
8681
|
+
out.set(p, at);
|
|
8682
|
+
at += p.length;
|
|
8683
|
+
}
|
|
8684
|
+
return digits(await sha256(out));
|
|
8685
|
+
}
|
|
8686
|
+
function digits(hash2) {
|
|
8687
|
+
let n = 0n;
|
|
8688
|
+
for (let i = 0; i < 8; i++)
|
|
8689
|
+
n = n << 8n | BigInt(hash2[i] ?? 0);
|
|
8690
|
+
const s = (n % 100000000n).toString().padStart(8, "0");
|
|
8691
|
+
return `${s.slice(0, 4)}-${s.slice(4)}`;
|
|
8692
|
+
}
|
|
8693
|
+
var enc2, PAIR_LABEL;
|
|
8476
8694
|
var init_pair_code = __esm({
|
|
8477
8695
|
"node_modules/@fetchproxy/protocol/dist/pair-code.js"() {
|
|
8478
8696
|
init_crypto();
|
|
8479
|
-
|
|
8697
|
+
enc2 = new TextEncoder();
|
|
8698
|
+
PAIR_LABEL = "fetchproxy/4/pair";
|
|
8480
8699
|
}
|
|
8481
8700
|
});
|
|
8482
8701
|
|
|
@@ -8486,10 +8705,24 @@ function randomIv() {
|
|
|
8486
8705
|
globalThis.crypto.getRandomValues(iv);
|
|
8487
8706
|
return iv;
|
|
8488
8707
|
}
|
|
8489
|
-
|
|
8708
|
+
function base64Length(byteLength) {
|
|
8709
|
+
return Math.ceil(byteLength / 3) * 4;
|
|
8710
|
+
}
|
|
8711
|
+
function encodeInnerFrame(inner) {
|
|
8712
|
+
return enc3.encode(JSON.stringify(inner));
|
|
8713
|
+
}
|
|
8714
|
+
function plaintextOf(inner) {
|
|
8715
|
+
return inner instanceof Uint8Array ? inner : encodeInnerFrame(inner);
|
|
8716
|
+
}
|
|
8717
|
+
function sealedFrameWireBytes(mcpId, seq, inner) {
|
|
8718
|
+
const plaintext = plaintextOf(inner).length;
|
|
8719
|
+
const envelope = enc3.encode(JSON.stringify({ type: "frame", mcpId, seq, iv: "", ciphertext: "" })).length;
|
|
8720
|
+
return envelope + IV_B64_BYTES + base64Length(plaintext + AES_GCM_TAG_BYTES);
|
|
8721
|
+
}
|
|
8722
|
+
async function sealInnerFrame(sessionKey, mcpId, seq, inner, direction) {
|
|
8490
8723
|
const iv = randomIv();
|
|
8491
|
-
const pt =
|
|
8492
|
-
const ct = await aesGcmSeal(sessionKey, iv, pt);
|
|
8724
|
+
const pt = plaintextOf(inner);
|
|
8725
|
+
const ct = await aesGcmSeal(sessionKey, iv, pt, frameAad(mcpId, seq, direction));
|
|
8493
8726
|
return {
|
|
8494
8727
|
type: "frame",
|
|
8495
8728
|
mcpId,
|
|
@@ -8498,18 +8731,18 @@ async function sealInnerFrame(sessionKey, mcpId, seq, inner) {
|
|
|
8498
8731
|
ciphertext: toB64(ct)
|
|
8499
8732
|
};
|
|
8500
8733
|
}
|
|
8501
|
-
async function openEncryptedFrame(sessionKey, frame) {
|
|
8502
|
-
const result = await openEncryptedFrameDetailed(sessionKey, frame);
|
|
8734
|
+
async function openEncryptedFrame(sessionKey, frame, direction) {
|
|
8735
|
+
const result = await openEncryptedFrameDetailed(sessionKey, frame, direction);
|
|
8503
8736
|
if (result.stage === "ok")
|
|
8504
8737
|
return result.inner;
|
|
8505
8738
|
throw result.error instanceof Error ? result.error : new Error(String(result.error));
|
|
8506
8739
|
}
|
|
8507
|
-
async function openEncryptedFrameDetailed(sessionKey, frame) {
|
|
8740
|
+
async function openEncryptedFrameDetailed(sessionKey, frame, direction) {
|
|
8508
8741
|
let pt;
|
|
8509
8742
|
try {
|
|
8510
8743
|
const iv = fromB64(frame.iv);
|
|
8511
8744
|
const ct = fromB64(frame.ciphertext);
|
|
8512
|
-
pt = await aesGcmOpen(sessionKey, iv, ct);
|
|
8745
|
+
pt = await aesGcmOpen(sessionKey, iv, ct, frameAad(frame.mcpId, frame.seq, direction));
|
|
8513
8746
|
} catch (error61) {
|
|
8514
8747
|
return { stage: "decrypt-failed", error: error61 };
|
|
8515
8748
|
}
|
|
@@ -8527,14 +8760,18 @@ async function openEncryptedFrameDetailed(sessionKey, frame) {
|
|
|
8527
8760
|
return { stage: "validation-failed", error: error61, recoveredId };
|
|
8528
8761
|
}
|
|
8529
8762
|
}
|
|
8530
|
-
var
|
|
8763
|
+
var enc3, dec, AES_GCM_TAG_BYTES, IV_B64_BYTES, MAX_FRAME_BYTES;
|
|
8531
8764
|
var init_seal = __esm({
|
|
8532
8765
|
"node_modules/@fetchproxy/protocol/dist/seal.js"() {
|
|
8533
8766
|
init_crypto();
|
|
8534
8767
|
init_encoding();
|
|
8768
|
+
init_frames();
|
|
8535
8769
|
init_validate();
|
|
8536
|
-
|
|
8770
|
+
enc3 = new TextEncoder();
|
|
8537
8771
|
dec = new TextDecoder();
|
|
8772
|
+
AES_GCM_TAG_BYTES = 16;
|
|
8773
|
+
IV_B64_BYTES = 16;
|
|
8774
|
+
MAX_FRAME_BYTES = 42 * 1024 * 1024;
|
|
8538
8775
|
}
|
|
8539
8776
|
});
|
|
8540
8777
|
|
|
@@ -8548,6 +8785,7 @@ var init_dist = __esm({
|
|
|
8548
8785
|
init_pair_code();
|
|
8549
8786
|
init_seal();
|
|
8550
8787
|
init_encoding();
|
|
8788
|
+
init_public_suffix();
|
|
8551
8789
|
init_json_pointer();
|
|
8552
8790
|
}
|
|
8553
8791
|
});
|
|
@@ -12297,7 +12535,7 @@ var init_wrapper = __esm({
|
|
|
12297
12535
|
async function buildServerHello(opts) {
|
|
12298
12536
|
const sessionNonce = new Uint8Array(32);
|
|
12299
12537
|
globalThis.crypto.getRandomValues(sessionNonce);
|
|
12300
|
-
const sig = await ed25519Sign(opts.identity.ed25519Priv,
|
|
12538
|
+
const sig = await ed25519Sign(opts.identity.ed25519Priv, helloSignaturePayload(opts.mcpId, sessionNonce, opts.sessionPub, opts.answersExtNonce));
|
|
12301
12539
|
const hello = {
|
|
12302
12540
|
type: "hello",
|
|
12303
12541
|
protocolVersion: PROTOCOL_VERSION,
|
|
@@ -12310,6 +12548,8 @@ async function buildServerHello(opts) {
|
|
|
12310
12548
|
identityX25519Pub: toB64(opts.identity.x25519Pub),
|
|
12311
12549
|
identityEd25519Pub: toB64(opts.identity.ed25519Pub),
|
|
12312
12550
|
sessionNonce: toB64(sessionNonce),
|
|
12551
|
+
sessionPub: toB64(opts.sessionPub),
|
|
12552
|
+
answersExtNonce: toB64(opts.answersExtNonce),
|
|
12313
12553
|
sessionSig: toB64(sig)
|
|
12314
12554
|
};
|
|
12315
12555
|
if (opts.accepts && opts.accepts.length > 0)
|
|
@@ -12371,14 +12611,31 @@ var init_build_server_hello = __esm({
|
|
|
12371
12611
|
}
|
|
12372
12612
|
});
|
|
12373
12613
|
|
|
12614
|
+
// node_modules/@fetchproxy/server/dist/frame-size.js
|
|
12615
|
+
function encodeOutboundInnerFrame(mcpId, inner) {
|
|
12616
|
+
const plaintext = encodeInnerFrame(inner);
|
|
12617
|
+
const wireBytes = sealedFrameWireBytes(mcpId, Number.MAX_SAFE_INTEGER, plaintext);
|
|
12618
|
+
if (wireBytes > MAX_FRAME_BYTES) {
|
|
12619
|
+
throw new Error(`fetchproxy: this request is too large for one bridge frame: ${wireBytes} bytes on the wire, cap ${MAX_FRAME_BYTES}. The request is refused; the bridge is not closed, so other calls and other MCPs on it are unaffected.`);
|
|
12620
|
+
}
|
|
12621
|
+
return plaintext;
|
|
12622
|
+
}
|
|
12623
|
+
var init_frame_size = __esm({
|
|
12624
|
+
"node_modules/@fetchproxy/server/dist/frame-size.js"() {
|
|
12625
|
+
init_dist();
|
|
12626
|
+
}
|
|
12627
|
+
});
|
|
12628
|
+
|
|
12374
12629
|
// node_modules/@fetchproxy/server/dist/session.js
|
|
12375
|
-
var SessionState;
|
|
12630
|
+
var MAX_INFLIGHT_INBOUND_SEQS, SessionState;
|
|
12376
12631
|
var init_session = __esm({
|
|
12377
12632
|
"node_modules/@fetchproxy/server/dist/session.js"() {
|
|
12633
|
+
MAX_INFLIGHT_INBOUND_SEQS = 1024;
|
|
12378
12634
|
SessionState = class {
|
|
12379
12635
|
sessionKey;
|
|
12380
12636
|
outboundSeq = 0;
|
|
12381
12637
|
lastInboundSeq = 0;
|
|
12638
|
+
inflightInbound = /* @__PURE__ */ new Set();
|
|
12382
12639
|
constructor(sessionKey) {
|
|
12383
12640
|
this.sessionKey = sessionKey;
|
|
12384
12641
|
}
|
|
@@ -12386,17 +12643,55 @@ var init_session = __esm({
|
|
|
12386
12643
|
this.outboundSeq += 1;
|
|
12387
12644
|
return this.outboundSeq;
|
|
12388
12645
|
}
|
|
12389
|
-
|
|
12646
|
+
/**
|
|
12647
|
+
* Take this seq out of circulation for the frame about to be opened, and
|
|
12648
|
+
* say whether it was available. SYNCHRONOUS and single-shot: call it before
|
|
12649
|
+
* the first `await` of the receive path, or a duplicate frame read in the
|
|
12650
|
+
* same pass will be judged against a counter neither frame has moved yet.
|
|
12651
|
+
*
|
|
12652
|
+
* Answering false means "not this frame's to process" — replayed, or a
|
|
12653
|
+
* duplicate of one still in flight. Every true MUST be answered by exactly
|
|
12654
|
+
* one {@link commitInboundSeq} or {@link releaseInboundSeq}.
|
|
12655
|
+
*/
|
|
12656
|
+
claimInboundSeq(seq) {
|
|
12390
12657
|
if (seq <= this.lastInboundSeq)
|
|
12391
12658
|
return false;
|
|
12392
|
-
this.
|
|
12659
|
+
if (this.inflightInbound.has(seq))
|
|
12660
|
+
return false;
|
|
12661
|
+
if (this.inflightInbound.size >= MAX_INFLIGHT_INBOUND_SEQS)
|
|
12662
|
+
return false;
|
|
12663
|
+
this.inflightInbound.add(seq);
|
|
12393
12664
|
return true;
|
|
12394
12665
|
}
|
|
12666
|
+
/**
|
|
12667
|
+
* Give a claim back without spending the seq — for a frame that did not
|
|
12668
|
+
* authenticate, which is a frame that never happened. The counter does not
|
|
12669
|
+
* move, so the genuine frames behind it, carrying lower numbers, are still
|
|
12670
|
+
* accepted. Idempotent, so a caller may release in a `catch` after a commit
|
|
12671
|
+
* it is not sure ran.
|
|
12672
|
+
*/
|
|
12673
|
+
releaseInboundSeq(seq) {
|
|
12674
|
+
this.inflightInbound.delete(seq);
|
|
12675
|
+
}
|
|
12676
|
+
/**
|
|
12677
|
+
* Record a seq as spent. Call only for a frame that authenticated. Never
|
|
12678
|
+
* moves the counter backwards, so an out-of-order commit cannot reopen a
|
|
12679
|
+
* seq an earlier one already closed.
|
|
12680
|
+
*/
|
|
12681
|
+
commitInboundSeq(seq) {
|
|
12682
|
+
this.inflightInbound.delete(seq);
|
|
12683
|
+
if (seq > this.lastInboundSeq)
|
|
12684
|
+
this.lastInboundSeq = seq;
|
|
12685
|
+
}
|
|
12395
12686
|
};
|
|
12396
12687
|
}
|
|
12397
12688
|
});
|
|
12398
12689
|
|
|
12399
12690
|
// node_modules/@fetchproxy/server/dist/session-ready.js
|
|
12691
|
+
function protocolVersionCloseReason(info) {
|
|
12692
|
+
const far = info.peer === "extension" ? "the extension" : "the other MCP";
|
|
12693
|
+
return `protocol version mismatch: this MCP speaks ${info.ourVersion}, ${far} speaks ${info.theirVersion}`;
|
|
12694
|
+
}
|
|
12400
12695
|
async function awaitSessionReady(ready, opts) {
|
|
12401
12696
|
const ms = opts.timeoutMs ?? SESSION_READY_TIMEOUT_MS;
|
|
12402
12697
|
if (ms <= 0)
|
|
@@ -12415,7 +12710,7 @@ async function awaitSessionReady(ready, opts) {
|
|
|
12415
12710
|
clearTimeout(timer);
|
|
12416
12711
|
}
|
|
12417
12712
|
}
|
|
12418
|
-
var SESSION_READY_TIMEOUT_MS, FetchproxySessionNotReadyError, FetchproxyHelloRejectedError;
|
|
12713
|
+
var SESSION_READY_TIMEOUT_MS, FetchproxySessionNotReadyError, FetchproxyHelloRejectedError, MIN_VERSION, FetchproxyProtocolVersionError;
|
|
12419
12714
|
var init_session_ready = __esm({
|
|
12420
12715
|
"node_modules/@fetchproxy/server/dist/session-ready.js"() {
|
|
12421
12716
|
SESSION_READY_TIMEOUT_MS = 3e4;
|
|
@@ -12447,6 +12742,23 @@ var init_session_ready = __esm({
|
|
|
12447
12742
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
12448
12743
|
}
|
|
12449
12744
|
};
|
|
12745
|
+
MIN_VERSION = "3.0.0";
|
|
12746
|
+
FetchproxyProtocolVersionError = class extends Error {
|
|
12747
|
+
/** The protocol version this process speaks. */
|
|
12748
|
+
ourVersion;
|
|
12749
|
+
/** The protocol version the far end announced in the hello we refused. */
|
|
12750
|
+
theirVersion;
|
|
12751
|
+
peer;
|
|
12752
|
+
constructor(info) {
|
|
12753
|
+
const far = info.peer === "extension" ? `the attached browser extension speaks ${info.theirVersion} \u2014 update Transporter (the fetchproxy extension) to ${MIN_VERSION} or later` : `the MCP holding the bridge port speaks ${info.theirVersion} \u2014 upgrade @fetchproxy/server to ${MIN_VERSION} or later in that MCP`;
|
|
12754
|
+
super(`protocol version mismatch: this MCP speaks fetchproxy protocol ${info.ourVersion}, ${far}`);
|
|
12755
|
+
this.name = "FetchproxyProtocolVersionError";
|
|
12756
|
+
this.ourVersion = info.ourVersion;
|
|
12757
|
+
this.theirVersion = info.theirVersion;
|
|
12758
|
+
this.peer = info.peer;
|
|
12759
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
12760
|
+
}
|
|
12761
|
+
};
|
|
12450
12762
|
}
|
|
12451
12763
|
});
|
|
12452
12764
|
|
|
@@ -12536,13 +12848,32 @@ var init_identity = __esm({
|
|
|
12536
12848
|
|
|
12537
12849
|
// node_modules/@fetchproxy/server/dist/extension-trust.js
|
|
12538
12850
|
import { readFile as readFile2, writeFile as writeFile2, rename, unlink, mkdir as mkdir2, chmod as chmod2 } from "node:fs/promises";
|
|
12539
|
-
import { join as join3 } from "node:path";
|
|
12851
|
+
import { isAbsolute as isAbsolute2, join as join3 } from "node:path";
|
|
12852
|
+
function envTrustDir(env = process.env) {
|
|
12853
|
+
const raw = env[TRUST_DIR_ENV];
|
|
12854
|
+
if (raw === void 0)
|
|
12855
|
+
return void 0;
|
|
12856
|
+
const dir = raw.trim();
|
|
12857
|
+
if (dir !== "" && isAbsolute2(dir))
|
|
12858
|
+
return dir;
|
|
12859
|
+
console.warn(`[fetchproxy] ignoring ${TRUST_DIR_ENV}=${JSON.stringify(raw)}: it is not an absolute path. The extension pin will be written beside the identity instead, which is not where you pointed it \u2014 if that directory is read-only, no pin is ever kept.`);
|
|
12860
|
+
return void 0;
|
|
12861
|
+
}
|
|
12862
|
+
function resolveTrustDir(explicit, identityDir) {
|
|
12863
|
+
if (explicit !== void 0)
|
|
12864
|
+
return explicit;
|
|
12865
|
+
return envTrustDir() ?? identityDir ?? defaultIdentityDir();
|
|
12866
|
+
}
|
|
12867
|
+
function defaultTrustDir() {
|
|
12868
|
+
return resolveTrustDir();
|
|
12869
|
+
}
|
|
12540
12870
|
function fileExtensionTrust(args) {
|
|
12871
|
+
const dir = resolveTrustDir(args.trustDir, args.dir);
|
|
12541
12872
|
return {
|
|
12542
12873
|
allowNew: args.allowNew,
|
|
12543
|
-
location: extensionTrustPath(args.serverName,
|
|
12544
|
-
read: () => readExtensionPin(args.serverName,
|
|
12545
|
-
write: (pin) => writeExtensionPin(args.serverName, pin,
|
|
12874
|
+
location: extensionTrustPath(args.serverName, dir),
|
|
12875
|
+
read: () => readExtensionPin(args.serverName, dir),
|
|
12876
|
+
write: (pin) => writeExtensionPin(args.serverName, pin, dir)
|
|
12546
12877
|
};
|
|
12547
12878
|
}
|
|
12548
12879
|
function allowNewExtensionIdentity(explicit, env = process.env) {
|
|
@@ -12569,14 +12900,14 @@ function decideExtensionTrust(args) {
|
|
|
12569
12900
|
message: `[fetchproxy] ${serverName}: refusing an extension whose identity is not the one this MCP paired with. If you re-installed the extension or moved to another browser, re-pair deliberately: run this MCP once with ${TRUST_NEW_EXTENSION_ENV}=1, or delete ${trustPath}. If you did neither, something else is answering as your browser.`
|
|
12570
12901
|
};
|
|
12571
12902
|
}
|
|
12572
|
-
function extensionTrustPath(serverName, dir =
|
|
12903
|
+
function extensionTrustPath(serverName, dir = defaultTrustDir()) {
|
|
12573
12904
|
return join3(dir, `${safeIdentityFileBase(serverName)}.extension-trust.json`);
|
|
12574
12905
|
}
|
|
12575
12906
|
function extensionTrustPathHint(serverName) {
|
|
12576
12907
|
try {
|
|
12577
12908
|
return extensionTrustPath(serverName);
|
|
12578
12909
|
} catch {
|
|
12579
|
-
return join3(
|
|
12910
|
+
return join3(defaultTrustDir(), "<server-name>.extension-trust.json");
|
|
12580
12911
|
}
|
|
12581
12912
|
}
|
|
12582
12913
|
function isPin(x) {
|
|
@@ -12585,7 +12916,7 @@ function isPin(x) {
|
|
|
12585
12916
|
const r = x;
|
|
12586
12917
|
return typeof r.identityX25519Pub === "string" && typeof r.identityEd25519Pub === "string" && typeof r.pinnedAt === "number";
|
|
12587
12918
|
}
|
|
12588
|
-
async function readExtensionPin(serverName, dir =
|
|
12919
|
+
async function readExtensionPin(serverName, dir = defaultTrustDir()) {
|
|
12589
12920
|
const path = extensionTrustPath(serverName, dir);
|
|
12590
12921
|
let raw;
|
|
12591
12922
|
try {
|
|
@@ -12610,7 +12941,7 @@ async function readExtensionPin(serverName, dir = defaultIdentityDir()) {
|
|
|
12610
12941
|
pinnedAt: parsed.pinnedAt
|
|
12611
12942
|
};
|
|
12612
12943
|
}
|
|
12613
|
-
async function writeExtensionPin(serverName, pin, dir =
|
|
12944
|
+
async function writeExtensionPin(serverName, pin, dir = defaultTrustDir()) {
|
|
12614
12945
|
const path = extensionTrustPath(serverName, dir);
|
|
12615
12946
|
await mkdir2(dir, { recursive: true, mode: 448 });
|
|
12616
12947
|
const tmp = `${path}.tmp`;
|
|
@@ -12618,28 +12949,67 @@ async function writeExtensionPin(serverName, pin, dir = defaultIdentityDir()) {
|
|
|
12618
12949
|
await chmod2(tmp, 384);
|
|
12619
12950
|
await rename(tmp, path);
|
|
12620
12951
|
}
|
|
12621
|
-
var TRUST_NEW_EXTENSION_ENV;
|
|
12952
|
+
var TRUST_DIR_ENV, TRUST_NEW_EXTENSION_ENV;
|
|
12622
12953
|
var init_extension_trust = __esm({
|
|
12623
12954
|
"node_modules/@fetchproxy/server/dist/extension-trust.js"() {
|
|
12624
12955
|
init_identity();
|
|
12956
|
+
TRUST_DIR_ENV = "FETCHPROXY_TRUST_DIR";
|
|
12625
12957
|
TRUST_NEW_EXTENSION_ENV = "FETCHPROXY_TRUST_NEW_EXTENSION";
|
|
12626
12958
|
}
|
|
12627
12959
|
});
|
|
12628
12960
|
|
|
12629
12961
|
// node_modules/@fetchproxy/server/dist/host.js
|
|
12962
|
+
function envAllowsLocalOrigins(env = process.env) {
|
|
12963
|
+
const raw = env[ALLOW_LOCAL_ORIGINS_ENV];
|
|
12964
|
+
if (raw === void 0 || raw.trim() === "")
|
|
12965
|
+
return false;
|
|
12966
|
+
if (raw.trim() === "1")
|
|
12967
|
+
return true;
|
|
12968
|
+
console.warn(`[fetchproxy] ignoring ${ALLOW_LOCAL_ORIGINS_ENV}=${JSON.stringify(raw)}: the only value that turns it on is 1. Upgrades from a null or localhost origin stay refused.`);
|
|
12969
|
+
return false;
|
|
12970
|
+
}
|
|
12971
|
+
function originVerdict(origin, allowLocal) {
|
|
12972
|
+
if (origin === void 0)
|
|
12973
|
+
return { allow: true };
|
|
12974
|
+
if (HTTP_ORIGIN_RE.test(origin)) {
|
|
12975
|
+
if (PUBLIC_ORIGIN_RE.test(origin)) {
|
|
12976
|
+
return { allow: false, reason: "origin not allowed", escapable: false };
|
|
12977
|
+
}
|
|
12978
|
+
return allowLocal ? { allow: true } : { allow: false, reason: "localhost page origin not allowed", escapable: true };
|
|
12979
|
+
}
|
|
12980
|
+
if (origin.toLowerCase() === OPAQUE_ORIGIN) {
|
|
12981
|
+
return allowLocal ? { allow: true } : { allow: false, reason: "opaque (null) origin not allowed", escapable: true };
|
|
12982
|
+
}
|
|
12983
|
+
if (EXTENSION_ORIGIN_RE.test(origin))
|
|
12984
|
+
return { allow: true };
|
|
12985
|
+
return { allow: false, reason: "origin not allowed", escapable: false };
|
|
12986
|
+
}
|
|
12630
12987
|
async function startHost(opts) {
|
|
12988
|
+
const allowLocalOrigins = envAllowsLocalOrigins();
|
|
12989
|
+
if (allowLocalOrigins) {
|
|
12990
|
+
console.warn(`[fetchproxy] ${ALLOW_LOCAL_ORIGINS_ENV}=1: accepting WebSocket upgrades from null and localhost page origins. This is a development escape \u2014 any local page the browser has open can reach this concentrator while it is set.`);
|
|
12991
|
+
}
|
|
12992
|
+
const warnedOrigins = /* @__PURE__ */ new Set();
|
|
12631
12993
|
const wss = new import_websocket_server.default({
|
|
12632
12994
|
server: opts.httpServer,
|
|
12995
|
+
maxPayload: opts.maxPayloadBytes ?? MAX_PAYLOAD_BYTES,
|
|
12633
12996
|
verifyClient: (info, cb) => {
|
|
12634
12997
|
const origin = info.req.headers.origin;
|
|
12635
|
-
|
|
12636
|
-
|
|
12998
|
+
const verdict = originVerdict(origin, allowLocalOrigins);
|
|
12999
|
+
if (verdict.allow) {
|
|
13000
|
+
cb(true);
|
|
12637
13001
|
return;
|
|
12638
13002
|
}
|
|
12639
|
-
|
|
13003
|
+
if (verdict.escapable && origin !== void 0 && warnedOrigins.size < 8) {
|
|
13004
|
+
if (!warnedOrigins.has(origin)) {
|
|
13005
|
+
warnedOrigins.add(origin);
|
|
13006
|
+
console.warn(`[fetchproxy] refused a WebSocket upgrade from ${JSON.stringify(origin)}: ${verdict.reason}. If that is you, developing against the bridge, set ${ALLOW_LOCAL_ORIGINS_ENV}=1 on this MCP \u2014 never on a deployed one.`);
|
|
13007
|
+
}
|
|
13008
|
+
}
|
|
13009
|
+
cb(false, 403, verdict.reason);
|
|
12640
13010
|
}
|
|
12641
13011
|
});
|
|
12642
|
-
const
|
|
13012
|
+
const ownHelloBase = {
|
|
12643
13013
|
// 2.6.0: tell the extension it can say WHY it refused a hello, instead of
|
|
12644
13014
|
// leaving us to time out and guess.
|
|
12645
13015
|
accepts: ["hello-rejected"],
|
|
@@ -12658,14 +13028,26 @@ async function startHost(opts) {
|
|
|
12658
13028
|
sessionStoragePointers: opts.ownSessionStoragePointers,
|
|
12659
13029
|
domSelectors: opts.ownDomSelectors,
|
|
12660
13030
|
graphqlOps: opts.ownGraphqlOps
|
|
12661
|
-
}
|
|
12662
|
-
const
|
|
13031
|
+
};
|
|
13032
|
+
const generateSessionKeypair = opts.generateSessionKeypair ?? generateX25519;
|
|
12663
13033
|
let extensionWs = null;
|
|
12664
13034
|
const peers = /* @__PURE__ */ new Map();
|
|
12665
13035
|
const ownInnerListeners = [];
|
|
12666
13036
|
const disconnectListeners = [];
|
|
12667
13037
|
const pendingPairListeners = [];
|
|
12668
13038
|
let ownSession = null;
|
|
13039
|
+
let ownEphemeral = null;
|
|
13040
|
+
function dropOwnSessionAndEphemeral() {
|
|
13041
|
+
if (ownEphemeral)
|
|
13042
|
+
ownEphemeral.priv.fill(0);
|
|
13043
|
+
ownEphemeral = null;
|
|
13044
|
+
return null;
|
|
13045
|
+
}
|
|
13046
|
+
function installOwnEphemeral(next) {
|
|
13047
|
+
if (ownEphemeral)
|
|
13048
|
+
ownEphemeral.priv.fill(0);
|
|
13049
|
+
ownEphemeral = next;
|
|
13050
|
+
}
|
|
12669
13051
|
let ownPendingPairCode = null;
|
|
12670
13052
|
let resolveOwnSession;
|
|
12671
13053
|
let rejectOwnSession;
|
|
@@ -12679,21 +13061,74 @@ async function startHost(opts) {
|
|
|
12679
13061
|
});
|
|
12680
13062
|
}
|
|
12681
13063
|
resetSessionPromise();
|
|
13064
|
+
let ownSessionRefusal = null;
|
|
13065
|
+
function refuseVersionMismatch(ws, raw, identified) {
|
|
13066
|
+
const peek = peekHelloVersion(raw);
|
|
13067
|
+
if (!peek || peek.protocolVersion === PROTOCOL_VERSION)
|
|
13068
|
+
return false;
|
|
13069
|
+
const err = new FetchproxyProtocolVersionError({
|
|
13070
|
+
ourVersion: PROTOCOL_VERSION,
|
|
13071
|
+
theirVersion: peek.protocolVersion,
|
|
13072
|
+
peer: peek.mcpId === null ? "extension" : "mcp"
|
|
13073
|
+
});
|
|
13074
|
+
console.warn(`[fetchproxy] host: ${err.message}`);
|
|
13075
|
+
const nothingBetterAttached = extensionWs === null && extensionClaim === null && ownSession === null;
|
|
13076
|
+
if (err.peer === "extension" && identified !== "peer" && nothingBetterAttached) {
|
|
13077
|
+
ownSessionRefusal = err;
|
|
13078
|
+
rejectOwnSession(err);
|
|
13079
|
+
resetSessionPromise();
|
|
13080
|
+
rejectOwnSession(err);
|
|
13081
|
+
}
|
|
13082
|
+
try {
|
|
13083
|
+
ws.close(1002, protocolVersionCloseReason(err));
|
|
13084
|
+
} catch {
|
|
13085
|
+
}
|
|
13086
|
+
return true;
|
|
13087
|
+
}
|
|
12682
13088
|
let extensionHello = null;
|
|
12683
13089
|
let extensionClaim = null;
|
|
13090
|
+
const pairCodeFor = async (hello, mint) => {
|
|
13091
|
+
if (!mint)
|
|
13092
|
+
return null;
|
|
13093
|
+
try {
|
|
13094
|
+
return await pairTranscript(opts.ownIdentity.x25519Pub, fromB64(hello.identityX25519Pub), mint.nonce, fromB64(hello.sessionNonce), mint.pub);
|
|
13095
|
+
} catch (e) {
|
|
13096
|
+
console.error("[fetchproxy] could not derive the pair code:", e);
|
|
13097
|
+
return null;
|
|
13098
|
+
}
|
|
13099
|
+
};
|
|
12684
13100
|
wss.on("connection", (ws) => {
|
|
12685
13101
|
let identified = null;
|
|
12686
13102
|
let peerMcpId = null;
|
|
12687
13103
|
let closed = false;
|
|
12688
13104
|
let pinOnReady = false;
|
|
13105
|
+
const handshakeTimer = setTimeout(() => {
|
|
13106
|
+
if (identified || closed)
|
|
13107
|
+
return;
|
|
13108
|
+
try {
|
|
13109
|
+
ws.close(1008, "handshake timeout");
|
|
13110
|
+
} catch {
|
|
13111
|
+
}
|
|
13112
|
+
}, opts.handshakeTimeoutMs ?? HANDSHAKE_TIMEOUT_MS);
|
|
13113
|
+
handshakeTimer.unref?.();
|
|
13114
|
+
ws.on("error", (e) => {
|
|
13115
|
+
console.warn(`[fetchproxy] host: socket error: ${String(e)}`);
|
|
13116
|
+
});
|
|
12689
13117
|
ws.on("message", async (data) => {
|
|
12690
13118
|
try {
|
|
13119
|
+
let raw;
|
|
13120
|
+
try {
|
|
13121
|
+
raw = JSON.parse(data.toString());
|
|
13122
|
+
} catch {
|
|
13123
|
+
ws.close(1002, "protocol error");
|
|
13124
|
+
return;
|
|
13125
|
+
}
|
|
12691
13126
|
let frame;
|
|
12692
13127
|
try {
|
|
12693
|
-
const raw = JSON.parse(data.toString());
|
|
12694
13128
|
frame = validateFrame(raw);
|
|
12695
13129
|
} catch {
|
|
12696
|
-
ws
|
|
13130
|
+
if (!refuseVersionMismatch(ws, raw, identified))
|
|
13131
|
+
ws.close(1002, "protocol error");
|
|
12697
13132
|
return;
|
|
12698
13133
|
}
|
|
12699
13134
|
if (frame.type === "hello" && frame.role === "extension") {
|
|
@@ -12737,25 +13172,43 @@ async function startHost(opts) {
|
|
|
12737
13172
|
identified = "extension";
|
|
12738
13173
|
extensionWs = ws;
|
|
12739
13174
|
extensionHello = frame;
|
|
12740
|
-
if (
|
|
13175
|
+
if (ownSessionRefusal) {
|
|
13176
|
+
ownSessionRefusal = null;
|
|
13177
|
+
resetSessionPromise();
|
|
13178
|
+
}
|
|
13179
|
+
const mintedKeypair = await generateSessionKeypair();
|
|
13180
|
+
const mintedHello = await buildServerHello({
|
|
13181
|
+
...ownHelloBase,
|
|
13182
|
+
sessionPub: mintedKeypair.publicKey,
|
|
13183
|
+
// The echo Rule B's gate reads, inside the signed payload: this
|
|
13184
|
+
// hello answers the extension session whose hello triggered it.
|
|
13185
|
+
answersExtNonce: fromB64(frame.sessionNonce)
|
|
13186
|
+
});
|
|
13187
|
+
if (extensionWs !== ws) {
|
|
13188
|
+
mintedKeypair.privateKey.fill(0);
|
|
13189
|
+
return;
|
|
13190
|
+
}
|
|
13191
|
+
const mint = {
|
|
13192
|
+
nonce: fromB64(mintedHello.sessionNonce),
|
|
13193
|
+
pub: mintedKeypair.publicKey
|
|
13194
|
+
};
|
|
13195
|
+
installOwnEphemeral({ ...mint, priv: mintedKeypair.privateKey });
|
|
13196
|
+
for (const slot of peers.values())
|
|
13197
|
+
slot.ws.send(JSON.stringify(frame));
|
|
13198
|
+
ws.send(JSON.stringify(mintedHello));
|
|
13199
|
+
const helloPairCode = await pairCodeFor(frame, mint);
|
|
13200
|
+
if (opts.onPairCode && helloPairCode !== null) {
|
|
12741
13201
|
try {
|
|
12742
|
-
|
|
12743
|
-
opts.onPairCode(code);
|
|
13202
|
+
opts.onPairCode(helloPairCode);
|
|
12744
13203
|
} catch (e) {
|
|
12745
13204
|
console.error("[fetchproxy] onPairCode threw:", e);
|
|
12746
13205
|
}
|
|
12747
13206
|
}
|
|
12748
|
-
for (const slot of peers.values())
|
|
12749
|
-
slot.ws.send(JSON.stringify(frame));
|
|
12750
|
-
ws.send(JSON.stringify(ownHello));
|
|
12751
|
-
for (const slot of peers.values()) {
|
|
12752
|
-
ws.send(JSON.stringify(slot.helloFrame));
|
|
12753
|
-
}
|
|
12754
13207
|
return;
|
|
12755
13208
|
}
|
|
12756
13209
|
if (frame.type === "hello" && frame.role === "server") {
|
|
12757
13210
|
const peerEdPub = fromB64(frame.identityEd25519Pub);
|
|
12758
|
-
const peerSigMsg =
|
|
13211
|
+
const peerSigMsg = helloSignaturePayload(frame.mcpId, fromB64(frame.sessionNonce), fromB64(frame.sessionPub), fromB64(frame.answersExtNonce));
|
|
12759
13212
|
const peerSig = fromB64(frame.sessionSig);
|
|
12760
13213
|
let peerSigOk = false;
|
|
12761
13214
|
try {
|
|
@@ -12780,10 +13233,12 @@ async function startHost(opts) {
|
|
|
12780
13233
|
identified = "peer";
|
|
12781
13234
|
peerMcpId = frame.mcpId;
|
|
12782
13235
|
peers.set(frame.mcpId, { ws, helloFrame: frame });
|
|
12783
|
-
if (
|
|
12784
|
-
extensionWs.send(JSON.stringify(frame));
|
|
12785
|
-
if (extensionHello)
|
|
13236
|
+
if (extensionHello && answersNoExtSession(frame.answersExtNonce)) {
|
|
12786
13237
|
ws.send(JSON.stringify(extensionHello));
|
|
13238
|
+
}
|
|
13239
|
+
if (extensionWs && extensionHello && frame.answersExtNonce === extensionHello.sessionNonce) {
|
|
13240
|
+
extensionWs.send(JSON.stringify(frame));
|
|
13241
|
+
}
|
|
12787
13242
|
return;
|
|
12788
13243
|
}
|
|
12789
13244
|
if (frame.type === "ready") {
|
|
@@ -12793,9 +13248,14 @@ async function startHost(opts) {
|
|
|
12793
13248
|
ws.close(1002, "ready before extension hello");
|
|
12794
13249
|
return;
|
|
12795
13250
|
}
|
|
13251
|
+
const held = ownEphemeral;
|
|
13252
|
+
if (!held || frame.mcpSessionPub !== toB64(held.pub)) {
|
|
13253
|
+
console.warn("[fetchproxy] discarding a ready for a session ephemeral this host no longer holds (the extension reconnected while a hello was in flight)");
|
|
13254
|
+
return;
|
|
13255
|
+
}
|
|
12796
13256
|
const extEdPub = fromB64(extensionHello.identityEd25519Pub);
|
|
12797
13257
|
const extNonce = fromB64(extensionHello.sessionNonce);
|
|
12798
|
-
const msg = readySignaturePayload(
|
|
13258
|
+
const msg = readySignaturePayload(held.nonce, extNonce, fromB64(frame.extensionSessionPub), held.pub);
|
|
12799
13259
|
const sig = fromB64(frame.sessionSig);
|
|
12800
13260
|
let sigOk = false;
|
|
12801
13261
|
try {
|
|
@@ -12821,8 +13281,9 @@ async function startHost(opts) {
|
|
|
12821
13281
|
}
|
|
12822
13282
|
}
|
|
12823
13283
|
const extPub = fromB64(frame.extensionSessionPub);
|
|
12824
|
-
const shared = await ecdhX25519(
|
|
12825
|
-
const
|
|
13284
|
+
const shared = await ecdhX25519(held.priv, extPub);
|
|
13285
|
+
const salt = await transcriptHash(held.nonce, extNonce, held.pub, extPub);
|
|
13286
|
+
const key = await hkdfSha256(shared, salt, enc4.encode(HKDF_SESSION_INFO), 32);
|
|
12826
13287
|
if (extensionWs !== ws)
|
|
12827
13288
|
return;
|
|
12828
13289
|
ownSession = new SessionState(key);
|
|
@@ -12838,11 +13299,19 @@ async function startHost(opts) {
|
|
|
12838
13299
|
if (frame.type === "frame") {
|
|
12839
13300
|
if (identified === "extension") {
|
|
12840
13301
|
if (frame.mcpId === opts.ownMcpId) {
|
|
12841
|
-
|
|
13302
|
+
const session = ownSession;
|
|
13303
|
+
if (!session)
|
|
12842
13304
|
return;
|
|
12843
|
-
if (!
|
|
13305
|
+
if (!session.claimInboundSeq(frame.seq))
|
|
12844
13306
|
return;
|
|
12845
|
-
|
|
13307
|
+
let inner;
|
|
13308
|
+
try {
|
|
13309
|
+
inner = await openEncryptedFrame(session.sessionKey, frame, "e2s");
|
|
13310
|
+
} catch (e) {
|
|
13311
|
+
session.releaseInboundSeq(frame.seq);
|
|
13312
|
+
throw e;
|
|
13313
|
+
}
|
|
13314
|
+
session.commitInboundSeq(frame.seq);
|
|
12846
13315
|
ownInnerListeners.forEach((cb) => cb(inner));
|
|
12847
13316
|
} else {
|
|
12848
13317
|
const slot = peers.get(frame.mcpId);
|
|
@@ -12866,8 +13335,16 @@ async function startHost(opts) {
|
|
|
12866
13335
|
}
|
|
12867
13336
|
if (frame.type === "pair-pending" && identified === "extension") {
|
|
12868
13337
|
if (frame.mcpId === opts.ownMcpId) {
|
|
12869
|
-
|
|
12870
|
-
|
|
13338
|
+
const hello = extensionHello;
|
|
13339
|
+
const mint = ownEphemeral;
|
|
13340
|
+
const derived = hello === null ? null : await pairCodeFor(hello, mint);
|
|
13341
|
+
if (derived === null || frame.pairCode !== derived) {
|
|
13342
|
+
console.error(`[fetchproxy] ${opts.ownServerName}: the extension's pair code (${frame.pairCode}) does not match the one this MCP derived from the pair transcript` + (derived === null ? " (none \u2014 this MCP could not derive its own)" : ` (${derived})`) + " \u2014 refusing to pair (possible MITM between this MCP and the extension)");
|
|
13343
|
+
ws.close(1008, "pair code mismatch");
|
|
13344
|
+
return;
|
|
13345
|
+
}
|
|
13346
|
+
ownPendingPairCode = derived;
|
|
13347
|
+
pendingPairListeners.forEach((cb) => cb(derived));
|
|
12871
13348
|
} else {
|
|
12872
13349
|
const slot = peers.get(frame.mcpId);
|
|
12873
13350
|
if (slot)
|
|
@@ -12884,6 +13361,7 @@ async function startHost(opts) {
|
|
|
12884
13361
|
});
|
|
12885
13362
|
ws.on("close", () => {
|
|
12886
13363
|
closed = true;
|
|
13364
|
+
clearTimeout(handshakeTimer);
|
|
12887
13365
|
if (extensionClaim === ws)
|
|
12888
13366
|
extensionClaim = null;
|
|
12889
13367
|
if (identified === "extension" && extensionWs === ws) {
|
|
@@ -12892,7 +13370,7 @@ async function startHost(opts) {
|
|
|
12892
13370
|
if (!ownSession) {
|
|
12893
13371
|
rejectOwnSession(new Error("extension disconnected before ready"));
|
|
12894
13372
|
}
|
|
12895
|
-
ownSession =
|
|
13373
|
+
ownSession = dropOwnSessionAndEphemeral();
|
|
12896
13374
|
ownPendingPairCode = null;
|
|
12897
13375
|
resetSessionPromise();
|
|
12898
13376
|
disconnectListeners.forEach((cb) => cb());
|
|
@@ -12931,7 +13409,8 @@ async function startHost(opts) {
|
|
|
12931
13409
|
});
|
|
12932
13410
|
if (!extensionWs)
|
|
12933
13411
|
throw new Error("host: no extension connected");
|
|
12934
|
-
const
|
|
13412
|
+
const plaintext = encodeOutboundInnerFrame(opts.ownMcpId, inner);
|
|
13413
|
+
const sealed = await sealInnerFrame(session.sessionKey, opts.ownMcpId, session.nextOutboundSeq(), plaintext, "s2e");
|
|
12935
13414
|
extensionWs.send(JSON.stringify(sealed));
|
|
12936
13415
|
},
|
|
12937
13416
|
onOwnInner: (cb) => {
|
|
@@ -12948,17 +13427,24 @@ async function startHost(opts) {
|
|
|
12948
13427
|
sessionLinked: () => ownSession !== null
|
|
12949
13428
|
};
|
|
12950
13429
|
}
|
|
12951
|
-
var PUBLIC_ORIGIN_RE,
|
|
13430
|
+
var HTTP_ORIGIN_RE, PUBLIC_ORIGIN_RE, EXTENSION_ORIGIN_RE, OPAQUE_ORIGIN, ALLOW_LOCAL_ORIGINS_ENV, HANDSHAKE_TIMEOUT_MS, MAX_PAYLOAD_BYTES, enc4;
|
|
12952
13431
|
var init_host = __esm({
|
|
12953
13432
|
"node_modules/@fetchproxy/server/dist/host.js"() {
|
|
12954
13433
|
init_wrapper();
|
|
12955
13434
|
init_dist();
|
|
12956
13435
|
init_build_server_hello();
|
|
13436
|
+
init_frame_size();
|
|
12957
13437
|
init_session();
|
|
12958
13438
|
init_session_ready();
|
|
12959
13439
|
init_extension_trust();
|
|
12960
|
-
|
|
12961
|
-
|
|
13440
|
+
HTTP_ORIGIN_RE = /^https?:\/\//i;
|
|
13441
|
+
PUBLIC_ORIGIN_RE = /^https?:\/\/(?!(127\.0\.0\.1|localhost|\[::1\])(:|$))/i;
|
|
13442
|
+
EXTENSION_ORIGIN_RE = /^(chrome-extension|moz-extension|safari-web-extension):\/\//i;
|
|
13443
|
+
OPAQUE_ORIGIN = "null";
|
|
13444
|
+
ALLOW_LOCAL_ORIGINS_ENV = "FETCHPROXY_ALLOW_LOCAL_ORIGINS";
|
|
13445
|
+
HANDSHAKE_TIMEOUT_MS = 15e3;
|
|
13446
|
+
MAX_PAYLOAD_BYTES = MAX_FRAME_BYTES;
|
|
13447
|
+
enc4 = new TextEncoder();
|
|
12962
13448
|
}
|
|
12963
13449
|
});
|
|
12964
13450
|
|
|
@@ -12966,10 +13452,26 @@ var init_host = __esm({
|
|
|
12966
13452
|
async function startPeer(opts) {
|
|
12967
13453
|
const ws = new import_websocket.default(`ws://${opts.host}:${opts.port}`);
|
|
12968
13454
|
await new Promise((resolve, reject) => {
|
|
12969
|
-
|
|
12970
|
-
|
|
13455
|
+
const onOpen = () => {
|
|
13456
|
+
cleanup();
|
|
13457
|
+
resolve();
|
|
13458
|
+
};
|
|
13459
|
+
const onError = (e) => {
|
|
13460
|
+
cleanup();
|
|
13461
|
+
reject(e);
|
|
13462
|
+
};
|
|
13463
|
+
const cleanup = () => {
|
|
13464
|
+
ws.off("open", onOpen);
|
|
13465
|
+
ws.off("error", onError);
|
|
13466
|
+
};
|
|
13467
|
+
ws.once("open", onOpen);
|
|
13468
|
+
ws.once("error", onError);
|
|
12971
13469
|
});
|
|
12972
|
-
|
|
13470
|
+
ws.on("error", (e) => {
|
|
13471
|
+
console.warn(`[fetchproxy] peer: socket error: ${String(e)}`);
|
|
13472
|
+
});
|
|
13473
|
+
const generateSessionKeypair = opts.generateSessionKeypair ?? generateX25519;
|
|
13474
|
+
const helloBase = {
|
|
12973
13475
|
identity: opts.identity,
|
|
12974
13476
|
mcpId: opts.mcpId,
|
|
12975
13477
|
serverName: opts.serverName,
|
|
@@ -12989,15 +13491,39 @@ async function startPeer(opts) {
|
|
|
12989
13491
|
// `extensionConnected()` / `sessionLinked()` can go back to false.
|
|
12990
13492
|
// 2.6.0: `hello-rejected` for the same reason, from the extension.
|
|
12991
13493
|
accepts: ["extension-disconnected", "hello-rejected"]
|
|
13494
|
+
};
|
|
13495
|
+
const bootstrapKeypair = await generateSessionKeypair();
|
|
13496
|
+
let bootstrapPriv = bootstrapKeypair.privateKey;
|
|
13497
|
+
const hello = await buildServerHello({
|
|
13498
|
+
...helloBase,
|
|
13499
|
+
sessionPub: bootstrapKeypair.publicKey,
|
|
13500
|
+
// At dial this peer has been told of no extension session, and saying so
|
|
13501
|
+
// is what keeps the host from forwarding a hello whose ephemeral is not
|
|
13502
|
+
// one a session may be opened from.
|
|
13503
|
+
answersExtNonce: ANSWERS_NO_EXT_SESSION
|
|
12992
13504
|
});
|
|
12993
|
-
const sessionNonce = fromB64(hello.sessionNonce);
|
|
12994
13505
|
ws.send(JSON.stringify(hello));
|
|
12995
13506
|
const innerListeners = [];
|
|
12996
13507
|
const renegotiateListeners = [];
|
|
12997
13508
|
const pendingPairListeners = [];
|
|
12998
13509
|
const closeListeners = [];
|
|
12999
13510
|
let session = null;
|
|
13511
|
+
let sessionEphemeral = null;
|
|
13512
|
+
function dropSessionEphemeral() {
|
|
13513
|
+
if (sessionEphemeral)
|
|
13514
|
+
sessionEphemeral.priv.fill(0);
|
|
13515
|
+
sessionEphemeral = null;
|
|
13516
|
+
}
|
|
13517
|
+
function installSessionEphemeral(next) {
|
|
13518
|
+
dropSessionEphemeral();
|
|
13519
|
+
if (bootstrapPriv) {
|
|
13520
|
+
bootstrapPriv.fill(0);
|
|
13521
|
+
bootstrapPriv = null;
|
|
13522
|
+
}
|
|
13523
|
+
sessionEphemeral = next;
|
|
13524
|
+
}
|
|
13000
13525
|
let pendingPairCode = null;
|
|
13526
|
+
let warnedUnverifiablePairCode = false;
|
|
13001
13527
|
let resolveFirstReady;
|
|
13002
13528
|
let rejectFirstReady;
|
|
13003
13529
|
const sessionPromise = new Promise((resolve, reject) => {
|
|
@@ -13006,58 +13532,60 @@ async function startPeer(opts) {
|
|
|
13006
13532
|
});
|
|
13007
13533
|
let extensionHello = null;
|
|
13008
13534
|
let extensionGone = false;
|
|
13009
|
-
let warnedUnverifiable = false;
|
|
13010
13535
|
let cachedPin = void 0;
|
|
13011
|
-
const
|
|
13012
|
-
if (!
|
|
13013
|
-
|
|
13014
|
-
|
|
13015
|
-
|
|
13016
|
-
|
|
13017
|
-
|
|
13018
|
-
|
|
13019
|
-
console.warn(`[fetchproxy] ${opts.serverName}: the concentrator does not forward the extension's identity (pre-1.12.0), so this peer cannot verify which browser it is talking to. Upgrade the MCP holding the bridge port to close this.`);
|
|
13020
|
-
}
|
|
13021
|
-
return true;
|
|
13536
|
+
const pairCodeFor = async (hello2, mint) => {
|
|
13537
|
+
if (!mint)
|
|
13538
|
+
return null;
|
|
13539
|
+
try {
|
|
13540
|
+
return await pairTranscript(opts.identity.x25519Pub, fromB64(hello2.identityX25519Pub), mint.nonce, fromB64(hello2.sessionNonce), mint.pub);
|
|
13541
|
+
} catch (e) {
|
|
13542
|
+
console.error("[fetchproxy] could not derive the pair code:", e);
|
|
13543
|
+
return null;
|
|
13022
13544
|
}
|
|
13023
|
-
|
|
13545
|
+
};
|
|
13546
|
+
const authenticateExtension = async (sessionSig, extensionSessionPub, held, hello2) => {
|
|
13547
|
+
if (!hello2) {
|
|
13548
|
+
console.error(`[fetchproxy] ${opts.serverName}: no extension hello has been relayed to this peer, so there is nothing to derive a v4 session from \u2014 refusing. Upgrade the MCP holding the bridge port to 3.0.0 or later.`);
|
|
13549
|
+
return null;
|
|
13550
|
+
}
|
|
13551
|
+
const payload = readySignaturePayload(held.nonce, fromB64(hello2.sessionNonce), fromB64(extensionSessionPub), held.pub);
|
|
13024
13552
|
let sigOk = false;
|
|
13025
13553
|
try {
|
|
13026
|
-
sigOk = await ed25519Verify(fromB64(
|
|
13554
|
+
sigOk = await ed25519Verify(fromB64(hello2.identityEd25519Pub), payload, fromB64(sessionSig));
|
|
13027
13555
|
} catch {
|
|
13028
13556
|
sigOk = false;
|
|
13029
13557
|
}
|
|
13030
13558
|
if (!sigOk) {
|
|
13031
13559
|
console.warn(`[fetchproxy] ${opts.serverName}: extension session signature invalid \u2014 refusing (the concentrator may be answering in the browser's place)`);
|
|
13032
|
-
return
|
|
13560
|
+
return null;
|
|
13033
13561
|
}
|
|
13034
13562
|
if (cachedPin === void 0) {
|
|
13035
13563
|
try {
|
|
13036
13564
|
cachedPin = await opts.extensionTrust.read();
|
|
13037
13565
|
} catch (e) {
|
|
13038
13566
|
console.error(`[fetchproxy] ${String(e)}`);
|
|
13039
|
-
return
|
|
13567
|
+
return null;
|
|
13040
13568
|
}
|
|
13041
13569
|
}
|
|
13042
13570
|
const pin = cachedPin;
|
|
13043
13571
|
const outcome = decideExtensionTrust({
|
|
13044
13572
|
pin,
|
|
13045
|
-
hello:
|
|
13573
|
+
hello: hello2,
|
|
13046
13574
|
allowNew: opts.extensionTrust.allowNew,
|
|
13047
13575
|
serverName: opts.serverName,
|
|
13048
13576
|
location: opts.extensionTrust.location
|
|
13049
13577
|
});
|
|
13050
13578
|
if (outcome.decision === "refused") {
|
|
13051
13579
|
console.warn(outcome.message);
|
|
13052
|
-
return
|
|
13580
|
+
return null;
|
|
13053
13581
|
}
|
|
13054
13582
|
if (outcome.decision === "replace")
|
|
13055
13583
|
console.warn(outcome.message);
|
|
13056
13584
|
if (outcome.decision !== "pinned") {
|
|
13057
13585
|
try {
|
|
13058
13586
|
const written = {
|
|
13059
|
-
identityX25519Pub:
|
|
13060
|
-
identityEd25519Pub:
|
|
13587
|
+
identityX25519Pub: hello2.identityX25519Pub,
|
|
13588
|
+
identityEd25519Pub: hello2.identityEd25519Pub,
|
|
13061
13589
|
pinnedAt: Date.now()
|
|
13062
13590
|
};
|
|
13063
13591
|
await opts.extensionTrust.write(written);
|
|
@@ -13066,32 +13594,62 @@ async function startPeer(opts) {
|
|
|
13066
13594
|
console.error(`[fetchproxy] could not persist the extension pin: ${String(e)}`);
|
|
13067
13595
|
}
|
|
13068
13596
|
}
|
|
13069
|
-
return
|
|
13597
|
+
return hello2;
|
|
13070
13598
|
};
|
|
13071
13599
|
const onMessage = async (data) => {
|
|
13600
|
+
let raw;
|
|
13072
13601
|
try {
|
|
13073
|
-
|
|
13602
|
+
raw = JSON.parse(data.toString());
|
|
13074
13603
|
const frame = validateFrame(raw);
|
|
13075
13604
|
if (frame.type === "hello" && frame.role === "extension") {
|
|
13076
13605
|
extensionHello = frame;
|
|
13606
|
+
const mintedKeypair = await generateSessionKeypair();
|
|
13607
|
+
const mintedHello = await buildServerHello({
|
|
13608
|
+
...helloBase,
|
|
13609
|
+
sessionPub: mintedKeypair.publicKey,
|
|
13610
|
+
answersExtNonce: fromB64(frame.sessionNonce)
|
|
13611
|
+
});
|
|
13612
|
+
if (extensionHello !== frame) {
|
|
13613
|
+
mintedKeypair.privateKey.fill(0);
|
|
13614
|
+
return;
|
|
13615
|
+
}
|
|
13616
|
+
installSessionEphemeral({
|
|
13617
|
+
nonce: fromB64(mintedHello.sessionNonce),
|
|
13618
|
+
pub: mintedKeypair.publicKey,
|
|
13619
|
+
priv: mintedKeypair.privateKey
|
|
13620
|
+
});
|
|
13621
|
+
ws.send(JSON.stringify(mintedHello));
|
|
13077
13622
|
return;
|
|
13078
13623
|
}
|
|
13079
13624
|
if (frame.type === "extension-disconnected") {
|
|
13080
13625
|
extensionHello = null;
|
|
13081
13626
|
extensionGone = true;
|
|
13627
|
+
dropSessionEphemeral();
|
|
13082
13628
|
pendingPairCode = null;
|
|
13083
13629
|
return;
|
|
13084
13630
|
}
|
|
13085
13631
|
if (frame.type === "ready" && frame.mcpId === opts.mcpId) {
|
|
13086
|
-
const
|
|
13087
|
-
|
|
13632
|
+
const held = sessionEphemeral;
|
|
13633
|
+
const heldExtHello = extensionHello;
|
|
13634
|
+
if (!held || frame.mcpSessionPub !== toB64(held.pub)) {
|
|
13635
|
+
console.warn(`[fetchproxy] ${opts.serverName}: discarding a ready for a session ephemeral this peer no longer holds (the extension reconnected while a hello was in flight)`);
|
|
13636
|
+
return;
|
|
13637
|
+
}
|
|
13638
|
+
const authenticated = await authenticateExtension(frame.sessionSig, frame.extensionSessionPub, held, heldExtHello);
|
|
13639
|
+
if (!authenticated) {
|
|
13088
13640
|
ws.close(1008, "extension identity refused");
|
|
13089
13641
|
rejectFirstReady(new Error("peer: extension identity refused"));
|
|
13090
13642
|
return;
|
|
13091
13643
|
}
|
|
13092
13644
|
const extPub = fromB64(frame.extensionSessionPub);
|
|
13093
|
-
const
|
|
13094
|
-
const
|
|
13645
|
+
const extNonce = fromB64(authenticated.sessionNonce);
|
|
13646
|
+
const shared = await ecdhX25519(held.priv, extPub);
|
|
13647
|
+
const salt = await transcriptHash(held.nonce, extNonce, held.pub, extPub);
|
|
13648
|
+
const sessionKey = await hkdfSha256(shared, salt, enc5.encode(HKDF_SESSION_INFO), 32);
|
|
13649
|
+
if (sessionEphemeral !== held) {
|
|
13650
|
+
console.warn(`[fetchproxy] ${opts.serverName}: dropping a session derived against an ephemeral that was superseded while it was being derived`);
|
|
13651
|
+
return;
|
|
13652
|
+
}
|
|
13095
13653
|
const isRenegotiation = session !== null;
|
|
13096
13654
|
session = new SessionState(sessionKey);
|
|
13097
13655
|
extensionGone = false;
|
|
@@ -13107,16 +13665,42 @@ async function startPeer(opts) {
|
|
|
13107
13665
|
rejectFirstReady(new FetchproxyHelloRejectedError({ mcpId: frame.mcpId, reason: frame.reason }));
|
|
13108
13666
|
}
|
|
13109
13667
|
if (frame.type === "pair-pending" && frame.mcpId === opts.mcpId) {
|
|
13110
|
-
|
|
13111
|
-
|
|
13668
|
+
const hello2 = extensionHello;
|
|
13669
|
+
const mint = sessionEphemeral;
|
|
13670
|
+
const derived = hello2 === null ? null : await pairCodeFor(hello2, mint);
|
|
13671
|
+
if (derived === null) {
|
|
13672
|
+
if (!warnedUnverifiablePairCode) {
|
|
13673
|
+
warnedUnverifiablePairCode = true;
|
|
13674
|
+
console.warn(`[fetchproxy] ${opts.serverName}: a pair code arrived that this peer cannot derive for itself (no extension identity has been relayed to it, or no session ephemeral has been minted in answer to one), so it is not being shown. If the MCP holding the bridge port is older than 1.12.0, upgrading it closes this.`);
|
|
13675
|
+
}
|
|
13676
|
+
return;
|
|
13677
|
+
}
|
|
13678
|
+
if (frame.pairCode !== derived) {
|
|
13679
|
+
console.error(`[fetchproxy] ${opts.serverName}: the extension's pair code (${frame.pairCode}) does not match the one this peer derived from the pair transcript (${derived}) \u2014 refusing to pair (possible MITM between this MCP and the extension)`);
|
|
13680
|
+
ws.close(1008, "pair code mismatch");
|
|
13681
|
+
return;
|
|
13682
|
+
}
|
|
13683
|
+
pendingPairCode = derived;
|
|
13684
|
+
pendingPairListeners.forEach((cb) => cb(derived));
|
|
13112
13685
|
return;
|
|
13113
13686
|
}
|
|
13114
13687
|
if (frame.type === "frame" && frame.mcpId === opts.mcpId) {
|
|
13115
|
-
|
|
13688
|
+
const inboundSession = session;
|
|
13689
|
+
if (!inboundSession)
|
|
13116
13690
|
return;
|
|
13117
|
-
if (!
|
|
13691
|
+
if (!inboundSession.claimInboundSeq(frame.seq))
|
|
13118
13692
|
return;
|
|
13119
|
-
|
|
13693
|
+
let result;
|
|
13694
|
+
try {
|
|
13695
|
+
result = await openEncryptedFrameDetailed(inboundSession.sessionKey, frame, "e2s");
|
|
13696
|
+
} catch (e) {
|
|
13697
|
+
inboundSession.releaseInboundSeq(frame.seq);
|
|
13698
|
+
throw e;
|
|
13699
|
+
}
|
|
13700
|
+
if (result.stage !== "decrypt-failed")
|
|
13701
|
+
inboundSession.commitInboundSeq(frame.seq);
|
|
13702
|
+
else
|
|
13703
|
+
inboundSession.releaseInboundSeq(frame.seq);
|
|
13120
13704
|
if (result.stage === "ok") {
|
|
13121
13705
|
innerListeners.forEach((cb) => cb(result.inner));
|
|
13122
13706
|
} else if (result.stage === "decrypt-failed") {
|
|
@@ -13133,12 +13717,25 @@ async function startPeer(opts) {
|
|
|
13133
13717
|
}
|
|
13134
13718
|
}
|
|
13135
13719
|
} catch (e) {
|
|
13136
|
-
|
|
13720
|
+
const peek = peekHelloVersion(raw);
|
|
13721
|
+
const mismatch = peek && peek.protocolVersion !== PROTOCOL_VERSION ? new FetchproxyProtocolVersionError({
|
|
13722
|
+
ourVersion: PROTOCOL_VERSION,
|
|
13723
|
+
theirVersion: peek.protocolVersion,
|
|
13724
|
+
peer: peek.mcpId === null ? "extension" : "mcp"
|
|
13725
|
+
}) : null;
|
|
13726
|
+
if (mismatch)
|
|
13727
|
+
console.warn(`[fetchproxy] ${opts.serverName}: ${mismatch.message}`);
|
|
13728
|
+
rejectFirstReady(mismatch ?? (e instanceof Error ? e : new Error(String(e))));
|
|
13137
13729
|
}
|
|
13138
13730
|
};
|
|
13139
13731
|
ws.on("message", onMessage);
|
|
13140
13732
|
ws.once("close", () => {
|
|
13141
13733
|
rejectFirstReady(new Error("peer WS closed before ready"));
|
|
13734
|
+
dropSessionEphemeral();
|
|
13735
|
+
if (bootstrapPriv) {
|
|
13736
|
+
bootstrapPriv.fill(0);
|
|
13737
|
+
bootstrapPriv = null;
|
|
13738
|
+
}
|
|
13142
13739
|
closeListeners.forEach((cb) => cb());
|
|
13143
13740
|
});
|
|
13144
13741
|
sessionPromise.catch(() => {
|
|
@@ -13152,7 +13749,8 @@ async function startPeer(opts) {
|
|
|
13152
13749
|
pendingPairCode: () => pendingPairCode
|
|
13153
13750
|
});
|
|
13154
13751
|
const s = session;
|
|
13155
|
-
const
|
|
13752
|
+
const plaintext = encodeOutboundInnerFrame(opts.mcpId, inner);
|
|
13753
|
+
const sealed = await sealInnerFrame(s.sessionKey, opts.mcpId, s.nextOutboundSeq(), plaintext, "s2e");
|
|
13156
13754
|
ws.send(JSON.stringify(sealed));
|
|
13157
13755
|
},
|
|
13158
13756
|
onInner: (cb) => {
|
|
@@ -13174,16 +13772,17 @@ async function startPeer(opts) {
|
|
|
13174
13772
|
};
|
|
13175
13773
|
return handle;
|
|
13176
13774
|
}
|
|
13177
|
-
var
|
|
13775
|
+
var enc5;
|
|
13178
13776
|
var init_peer = __esm({
|
|
13179
13777
|
"node_modules/@fetchproxy/server/dist/peer.js"() {
|
|
13180
13778
|
init_wrapper();
|
|
13181
13779
|
init_dist();
|
|
13182
13780
|
init_build_server_hello();
|
|
13781
|
+
init_frame_size();
|
|
13183
13782
|
init_session();
|
|
13184
13783
|
init_session_ready();
|
|
13185
13784
|
init_extension_trust();
|
|
13186
|
-
|
|
13785
|
+
enc5 = new TextEncoder();
|
|
13187
13786
|
}
|
|
13188
13787
|
});
|
|
13189
13788
|
|
|
@@ -13225,6 +13824,8 @@ function classifyBridgeError(err) {
|
|
|
13225
13824
|
return "session_not_ready";
|
|
13226
13825
|
if (err instanceof FetchproxyTimeoutError)
|
|
13227
13826
|
return "timeout";
|
|
13827
|
+
if (err instanceof FetchproxyWaitedError)
|
|
13828
|
+
return "timeout";
|
|
13228
13829
|
if (err instanceof FetchproxyBridgeDownError)
|
|
13229
13830
|
return "bridge_down";
|
|
13230
13831
|
if (err instanceof FetchproxyHttpError)
|
|
@@ -13262,7 +13863,19 @@ function envWsHost() {
|
|
|
13262
13863
|
return void 0;
|
|
13263
13864
|
return host;
|
|
13264
13865
|
}
|
|
13265
|
-
function
|
|
13866
|
+
function waitedHint(op) {
|
|
13867
|
+
const tail = " This is not a version problem and does not need an update.";
|
|
13868
|
+
if (op === "capture" || op === "capture_redirect") {
|
|
13869
|
+
return "nothing matched before the window closed. Is a tab open on the declared domain and signed in? The wait resolves on the NEXT matching request the PAGE makes, so an idle tab times out however long you wait \u2014 interact with the page, or raise the window." + tail;
|
|
13870
|
+
}
|
|
13871
|
+
if (op === "download") {
|
|
13872
|
+
return "the download did not finish before the window closed. Is a tab open on the declared domain and signed in, and is the file still transferring?" + tail;
|
|
13873
|
+
}
|
|
13874
|
+
return "the extension waited and nothing arrived. Is a tab open on the declared domain and signed in?" + tail;
|
|
13875
|
+
}
|
|
13876
|
+
function protocolErrorFrom(error61, op) {
|
|
13877
|
+
if (error61 === "timeout")
|
|
13878
|
+
return new FetchproxyWaitedError(error61, op);
|
|
13266
13879
|
if (SCOPE_REJECTION.test(error61))
|
|
13267
13880
|
return new FetchproxyScopeError(error61);
|
|
13268
13881
|
if (TAB_OPENING_REJECTION.test(error61))
|
|
@@ -13271,6 +13884,11 @@ function protocolErrorFrom(error61) {
|
|
|
13271
13884
|
return new FetchproxyNoTabError(error61);
|
|
13272
13885
|
return new FetchproxyProtocolError(error61);
|
|
13273
13886
|
}
|
|
13887
|
+
function verbDeadlineMs(transportMs, requestedMs, graceMs = VERB_DEADLINE_GRACE_MS) {
|
|
13888
|
+
if (requestedMs === void 0)
|
|
13889
|
+
return transportMs;
|
|
13890
|
+
return Math.max(requestedMs + graceMs, transportMs);
|
|
13891
|
+
}
|
|
13274
13892
|
function normalizeCookiePath(path) {
|
|
13275
13893
|
if (path === void 0 || path === "")
|
|
13276
13894
|
return void 0;
|
|
@@ -13306,7 +13924,7 @@ function assertUrlInDomains(field, url2, domains) {
|
|
|
13306
13924
|
const declared = domains.map((d) => JSON.stringify(d)).join(", ");
|
|
13307
13925
|
throw new Error(`FetchproxyServer: ${field} host "${host}" is outside declared domains [${declared}] \u2014 must be one of them or a subdomain`);
|
|
13308
13926
|
}
|
|
13309
|
-
var FetchproxyProtocolError, FetchproxyHttpError, FetchproxyBridgeDownError, FetchproxyHintedError, FetchproxyScopeError, FetchproxyNoTabError, FetchproxyTabOpeningError, SCOPE_REJECTION, NO_TAB_REJECTION, TAB_OPENING_REJECTION, FetchproxyTimeoutError, SUBDOMAIN_LABEL_RE, DEFAULT_JSON_OK_STATUSES, FetchproxyServer;
|
|
13927
|
+
var FetchproxyProtocolError, FetchproxyHttpError, FetchproxyBridgeDownError, FetchproxyHintedError, FetchproxyScopeError, FetchproxyNoTabError, FetchproxyTabOpeningError, FetchproxyWaitedError, SCOPE_REJECTION, NO_TAB_REJECTION, TAB_OPENING_REJECTION, VERB_DEADLINE_GRACE_MS, FetchproxyTimeoutError, SUBDOMAIN_LABEL_RE, DEFAULT_JSON_OK_STATUSES, FetchproxyServer;
|
|
13310
13928
|
var init_ws_server = __esm({
|
|
13311
13929
|
"node_modules/@fetchproxy/server/dist/ws-server.js"() {
|
|
13312
13930
|
init_dist();
|
|
@@ -13388,9 +14006,16 @@ var init_ws_server = __esm({
|
|
|
13388
14006
|
this.name = "FetchproxyTabOpeningError";
|
|
13389
14007
|
}
|
|
13390
14008
|
};
|
|
14009
|
+
FetchproxyWaitedError = class extends FetchproxyHintedError {
|
|
14010
|
+
constructor(originalError, op) {
|
|
14011
|
+
super(originalError, waitedHint(op));
|
|
14012
|
+
this.name = "FetchproxyWaitedError";
|
|
14013
|
+
}
|
|
14014
|
+
};
|
|
13391
14015
|
SCOPE_REJECTION = /not in declared/;
|
|
13392
14016
|
NO_TAB_REJECTION = /no tab matching (?!.*(content script loaded|one is still opening))/;
|
|
13393
14017
|
TAB_OPENING_REJECTION = /no tab matching .*one is still opening/;
|
|
14018
|
+
VERB_DEADLINE_GRACE_MS = 15e3;
|
|
13394
14019
|
FetchproxyTimeoutError = class extends FetchproxyProtocolError {
|
|
13395
14020
|
url;
|
|
13396
14021
|
timeoutMs;
|
|
@@ -13409,16 +14034,20 @@ var init_ws_server = __esm({
|
|
|
13409
14034
|
*/
|
|
13410
14035
|
retryAttempted;
|
|
13411
14036
|
/**
|
|
13412
|
-
* 2.4.2+ (#277): what the CALL asked for,
|
|
13413
|
-
*
|
|
13414
|
-
*
|
|
13415
|
-
*
|
|
13416
|
-
*
|
|
14037
|
+
* 2.4.2+ (#277): what the CALL asked for, whenever it asked for anything.
|
|
14038
|
+
*
|
|
14039
|
+
* Until #237 this was recorded only when the call asked for MORE than the
|
|
14040
|
+
* deadline, because asking for more is what the cap silently ignored. The
|
|
14041
|
+
* cap is gone — the call's own `timeoutMs` now GOVERNS its deadline
|
|
14042
|
+
* (`verbDeadlineMs`) — so the interesting case inverted: a timeout here
|
|
14043
|
+
* means the bridge did not answer within the window the CALLER chose, and
|
|
14044
|
+
* the message says which number that was and where it came from.
|
|
13417
14045
|
*/
|
|
13418
14046
|
requestedTimeoutMs;
|
|
13419
14047
|
constructor(args) {
|
|
13420
|
-
const
|
|
13421
|
-
|
|
14048
|
+
const graceMs = args.graceMs ?? VERB_DEADLINE_GRACE_MS;
|
|
14049
|
+
const callerChose = args.requestedTimeoutMs !== void 0 && args.timeoutMs === args.requestedTimeoutMs + graceMs;
|
|
14050
|
+
super(`fetchproxy: ${args.url} did not respond within ${args.timeoutMs}ms` + (callerChose ? ` \u2014 the ${args.requestedTimeoutMs}ms this call asked for, plus ${graceMs}ms for the reply to reach this server. The extension runs its own timer on the value you passed, so a window that is genuinely too short reports what did not happen rather than this.` : ""));
|
|
13422
14051
|
this.name = "FetchproxyTimeoutError";
|
|
13423
14052
|
this.url = args.url;
|
|
13424
14053
|
this.timeoutMs = args.timeoutMs;
|
|
@@ -13516,6 +14145,11 @@ var init_ws_server = __esm({
|
|
|
13516
14145
|
if (!Array.isArray(opts.domains) || opts.domains.length === 0) {
|
|
13517
14146
|
throw new Error("FetchproxyServer: opts.domains must be a non-empty array of hostnames");
|
|
13518
14147
|
}
|
|
14148
|
+
for (const d of opts.domains) {
|
|
14149
|
+
if (typeof d === "string" && isPublicSuffix(d)) {
|
|
14150
|
+
throw new Error(`FetchproxyServer: opts.domains entry ${JSON.stringify(d)} is a public suffix \u2014 declare a registrable domain (e.g. "example.${d}"), not the suffix every site under it shares`);
|
|
14151
|
+
}
|
|
14152
|
+
}
|
|
13519
14153
|
let capabilities;
|
|
13520
14154
|
if (opts.capabilities === void 0) {
|
|
13521
14155
|
capabilities = ["fetch"];
|
|
@@ -13581,6 +14215,7 @@ var init_ws_server = __esm({
|
|
|
13581
14215
|
// back-door is `0` (explicit opt-out) if a caller genuinely wants
|
|
13582
14216
|
// the legacy hang-forever / fail-once-on-SW-eviction behavior.
|
|
13583
14217
|
fetchTimeoutMs: opts.fetchTimeoutMs ?? 3e4,
|
|
14218
|
+
verbDeadlineGraceMs: opts.verbDeadlineGraceMs ?? VERB_DEADLINE_GRACE_MS,
|
|
13584
14219
|
bridgeReviveDelayMs: opts.bridgeReviveDelayMs ?? 2e3,
|
|
13585
14220
|
// 0.10.0+ (#72): keep-alive defaults to 25s — round-3 #71 cohort
|
|
13586
14221
|
// wave showed every Pattern A consumer was opting into this same
|
|
@@ -13599,6 +14234,7 @@ var init_ws_server = __esm({
|
|
|
13599
14234
|
keepAliveIntervalMs: opts.keepAliveIntervalMs ?? 2e4,
|
|
13600
14235
|
keepAliveMaxIdleMs: opts.keepAliveMaxIdleMs ?? 5 * 60 * 1e3,
|
|
13601
14236
|
identityDir: opts.identityDir,
|
|
14237
|
+
trustDir: opts.trustDir,
|
|
13602
14238
|
allowNewExtensionIdentity: opts.allowNewExtensionIdentity,
|
|
13603
14239
|
requireExtensionIdentity: opts.requireExtensionIdentity,
|
|
13604
14240
|
onPairCode: opts.onPairCode
|
|
@@ -13875,7 +14511,9 @@ var init_ws_server = __esm({
|
|
|
13875
14511
|
}
|
|
13876
14512
|
/**
|
|
13877
14513
|
* #208: this MCP's pin on the extension's identity, stored beside its own
|
|
13878
|
-
* identity key and so following `identityDir` wherever the caller put it
|
|
14514
|
+
* identity key and so following `identityDir` wherever the caller put it —
|
|
14515
|
+
* unless `trustDir` (or `FETCHPROXY_TRUST_DIR`) says otherwise, which is
|
|
14516
|
+
* what a host that provisions the identity read-only has to say.
|
|
13879
14517
|
*
|
|
13880
14518
|
* `allowNewExtensionIdentity` falls back to an environment variable when the
|
|
13881
14519
|
* caller expressed no opinion, because the thirteen MCPs that construct this
|
|
@@ -13887,6 +14525,7 @@ var init_ws_server = __esm({
|
|
|
13887
14525
|
return fileExtensionTrust({
|
|
13888
14526
|
serverName: this.opts.serverName,
|
|
13889
14527
|
dir: this.opts.identityDir,
|
|
14528
|
+
trustDir: this.opts.trustDir,
|
|
13890
14529
|
allowNew: allowNewExtensionIdentity(this.opts.allowNewExtensionIdentity)
|
|
13891
14530
|
});
|
|
13892
14531
|
}
|
|
@@ -14049,9 +14688,11 @@ var init_ws_server = __esm({
|
|
|
14049
14688
|
return pending;
|
|
14050
14689
|
}
|
|
14051
14690
|
async _withVerbTimeout(pending, pendingMap, id, url2, requestedTimeoutMs) {
|
|
14052
|
-
const
|
|
14053
|
-
if (
|
|
14691
|
+
const transportMs = this.opts.fetchTimeoutMs;
|
|
14692
|
+
if (transportMs === void 0 || transportMs <= 0)
|
|
14054
14693
|
return pending;
|
|
14694
|
+
const graceMs = this.opts.verbDeadlineGraceMs;
|
|
14695
|
+
const timeoutMs = verbDeadlineMs(transportMs, requestedTimeoutMs, graceMs);
|
|
14055
14696
|
let timer;
|
|
14056
14697
|
const start = Date.now();
|
|
14057
14698
|
try {
|
|
@@ -14067,6 +14708,7 @@ var init_ws_server = __esm({
|
|
|
14067
14708
|
port: this.opts.port,
|
|
14068
14709
|
elapsedMs: Date.now() - start,
|
|
14069
14710
|
retryAttempted: false,
|
|
14711
|
+
graceMs,
|
|
14070
14712
|
...requestedTimeoutMs !== void 0 ? { requestedTimeoutMs } : {}
|
|
14071
14713
|
}));
|
|
14072
14714
|
}, timeoutMs);
|
|
@@ -14127,9 +14769,9 @@ var init_ws_server = __esm({
|
|
|
14127
14769
|
if (opts.subdomain !== void 0)
|
|
14128
14770
|
assertSubdomainLabel(opts.subdomain);
|
|
14129
14771
|
const baseDomain = this.resolveBaseDomain(opts.domain);
|
|
14130
|
-
const
|
|
14772
|
+
const isAbsolute3 = path.startsWith("http://") || path.startsWith("https://");
|
|
14131
14773
|
let host;
|
|
14132
|
-
if (
|
|
14774
|
+
if (isAbsolute3) {
|
|
14133
14775
|
try {
|
|
14134
14776
|
host = new URL(path).host;
|
|
14135
14777
|
} catch {
|
|
@@ -14138,7 +14780,7 @@ var init_ws_server = __esm({
|
|
|
14138
14780
|
} else {
|
|
14139
14781
|
host = opts.subdomain ? `${opts.subdomain}.${baseDomain}` : baseDomain;
|
|
14140
14782
|
}
|
|
14141
|
-
const url2 =
|
|
14783
|
+
const url2 = isAbsolute3 ? path : `https://${host}${path}`;
|
|
14142
14784
|
assertUrlInDomains("request url", url2, this.opts.domains);
|
|
14143
14785
|
let tabUrl = `https://${host}/`;
|
|
14144
14786
|
if (opts.viaTab !== void 0) {
|
|
@@ -14627,8 +15269,9 @@ var init_ws_server = __esm({
|
|
|
14627
15269
|
this.pendingCapture,
|
|
14628
15270
|
id,
|
|
14629
15271
|
`https://${opts.host}${opts.path ?? "/*"}`,
|
|
14630
|
-
// #
|
|
14631
|
-
//
|
|
15272
|
+
// #237: this GOVERNS the deadline (window + reply grace, floored at the
|
|
15273
|
+
// transport bound). Under #277 it did not, and was passed only so the
|
|
15274
|
+
// timeout could name the number that actually fired.
|
|
14632
15275
|
opts.timeoutMs
|
|
14633
15276
|
);
|
|
14634
15277
|
}
|
|
@@ -14720,8 +15363,9 @@ var init_ws_server = __esm({
|
|
|
14720
15363
|
this.pendingRedirect,
|
|
14721
15364
|
id,
|
|
14722
15365
|
`https://${opts.host}${opts.path ?? "/*"}`,
|
|
14723
|
-
// #
|
|
14724
|
-
//
|
|
15366
|
+
// #237: this GOVERNS the deadline (window + reply grace, floored at the
|
|
15367
|
+
// transport bound). Under #277 it did not, and was passed only so the
|
|
15368
|
+
// timeout could name the number that actually fired.
|
|
14725
15369
|
opts.timeoutMs
|
|
14726
15370
|
);
|
|
14727
15371
|
}
|
|
@@ -14810,10 +15454,11 @@ var init_ws_server = __esm({
|
|
|
14810
15454
|
this.pendingDownload,
|
|
14811
15455
|
id,
|
|
14812
15456
|
opts.url,
|
|
14813
|
-
//
|
|
14814
|
-
//
|
|
14815
|
-
//
|
|
14816
|
-
// counted off the two call sites in view
|
|
15457
|
+
// `download` takes a per-call timeoutMs like the two capture verbs, so
|
|
15458
|
+
// it is governed by it like them (#237). It used to hit the identical
|
|
15459
|
+
// CAP and need the identical explanation; missing it then was the point
|
|
15460
|
+
// of #279 — "both verbs" was counted off the two call sites in view
|
|
15461
|
+
// rather than off the type. Three is still the number.
|
|
14817
15462
|
opts.timeoutMs
|
|
14818
15463
|
);
|
|
14819
15464
|
}
|
|
@@ -15042,7 +15687,7 @@ var init_ws_server = __esm({
|
|
|
15042
15687
|
captureCb.reject(new FetchproxyProtocolError(`unexpected ${String(inner.op)} response on capture awaiter`));
|
|
15043
15688
|
}
|
|
15044
15689
|
} else {
|
|
15045
|
-
captureCb.reject(protocolErrorFrom(inner.error));
|
|
15690
|
+
captureCb.reject(protocolErrorFrom(inner.error, "capture"));
|
|
15046
15691
|
}
|
|
15047
15692
|
return;
|
|
15048
15693
|
}
|
|
@@ -15056,7 +15701,7 @@ var init_ws_server = __esm({
|
|
|
15056
15701
|
redirectCb.reject(new FetchproxyProtocolError(`unexpected ${String(inner.op)} response on capture_redirect awaiter`));
|
|
15057
15702
|
}
|
|
15058
15703
|
} else {
|
|
15059
|
-
redirectCb.reject(protocolErrorFrom(inner.error));
|
|
15704
|
+
redirectCb.reject(protocolErrorFrom(inner.error, "capture_redirect"));
|
|
15060
15705
|
}
|
|
15061
15706
|
return;
|
|
15062
15707
|
}
|
|
@@ -15084,7 +15729,7 @@ var init_ws_server = __esm({
|
|
|
15084
15729
|
downloadCb.reject(new FetchproxyProtocolError(`unexpected ${String(inner.op)} response on download awaiter`));
|
|
15085
15730
|
}
|
|
15086
15731
|
} else {
|
|
15087
|
-
downloadCb.reject(protocolErrorFrom(inner.error));
|
|
15732
|
+
downloadCb.reject(protocolErrorFrom(inner.error, "download"));
|
|
15088
15733
|
}
|
|
15089
15734
|
return;
|
|
15090
15735
|
}
|
|
@@ -15187,11 +15832,21 @@ var init_ws_server = __esm({
|
|
|
15187
15832
|
/**
|
|
15188
15833
|
* 2.5.0: the extension link as {@link BridgeSessionState}, read off
|
|
15189
15834
|
* whichever handle is live. A pending pair code outranks "extension not
|
|
15190
|
-
* seen" because
|
|
15191
|
-
*
|
|
15192
|
-
* pair-pending frames. Where the extension is KNOWN to be gone (a host's
|
|
15835
|
+
* seen" because a code could only have come from a `pair-pending` frame
|
|
15836
|
+
* the extension sent. Where the extension is KNOWN to be gone (a host's
|
|
15193
15837
|
* socket closed, a peer told by a 2.5.0+ host) the handle has already
|
|
15194
15838
|
* dropped the code, so the precedence never lies there (#283).
|
|
15839
|
+
*
|
|
15840
|
+
* M1 (bridge review 2026-09-10): a code is now only ever present when the
|
|
15841
|
+
* handle DERIVED it from both identity pubs, and both handles set and clear
|
|
15842
|
+
* that derivation in lockstep with the extension hello — so the precedence
|
|
15843
|
+
* has nothing left to decide: a pair code implies the hello, which implies
|
|
15844
|
+
* `extensionConnected`. A peer behind a pre-1.12.0 host is relayed no
|
|
15845
|
+
* extension hello, cannot derive, and therefore reports
|
|
15846
|
+
* `extension_disconnected` with a null code where it used to report the
|
|
15847
|
+
* wire's number as `pair_pending` — the honest state for a peer that has
|
|
15848
|
+
* never been told an extension is there. That is the trade: a number nobody
|
|
15849
|
+
* can check is worse than no number.
|
|
15195
15850
|
*/
|
|
15196
15851
|
sessionSnapshot() {
|
|
15197
15852
|
const handle = this.hostHandle ?? this.peerHandle;
|
|
@@ -22806,12 +23461,12 @@ var $ZodE164 = /* @__PURE__ */ $constructor("$ZodE164", (inst, def) => {
|
|
|
22806
23461
|
$ZodStringFormat.init(inst, def);
|
|
22807
23462
|
});
|
|
22808
23463
|
var CC_SANITIZE = /[- ]/g;
|
|
22809
|
-
function isLuhnAlgo(
|
|
22810
|
-
let length =
|
|
23464
|
+
function isLuhnAlgo(digits2) {
|
|
23465
|
+
let length = digits2.length;
|
|
22811
23466
|
let bit = 1;
|
|
22812
23467
|
let sum = 0;
|
|
22813
23468
|
while (length) {
|
|
22814
|
-
const value = +
|
|
23469
|
+
const value = +digits2[--length];
|
|
22815
23470
|
bit ^= 1;
|
|
22816
23471
|
sum += bit ? [0, 2, 4, 6, 8, 1, 3, 5, 7, 9][value] : value;
|
|
22817
23472
|
}
|
|
@@ -37543,8 +38198,8 @@ function hex2(_params) {
|
|
|
37543
38198
|
return _stringFormat(ZodCustomStringFormat, "hex", regexes_exports.hex, _params);
|
|
37544
38199
|
}
|
|
37545
38200
|
function hash(alg, params) {
|
|
37546
|
-
const
|
|
37547
|
-
const format = `${alg}_${
|
|
38201
|
+
const enc7 = params?.enc ?? "hex";
|
|
38202
|
+
const format = `${alg}_${enc7}`;
|
|
37548
38203
|
const regex = regexes_exports[format];
|
|
37549
38204
|
if (!regex)
|
|
37550
38205
|
throw new Error(`Unrecognized hash format: ${format}`);
|
|
@@ -45018,7 +45673,7 @@ var pageSchema = {
|
|
|
45018
45673
|
init_errors();
|
|
45019
45674
|
|
|
45020
45675
|
// src/version.ts
|
|
45021
|
-
var VERSION = "0.3.
|
|
45676
|
+
var VERSION = "0.3.2";
|
|
45022
45677
|
|
|
45023
45678
|
// src/client.ts
|
|
45024
45679
|
import { dirname, join } from "path";
|
|
@@ -45577,7 +46232,7 @@ var schemaEventStatus = external_exports.enum(["all", "live", "draft", "started"
|
|
|
45577
46232
|
function qs(params) {
|
|
45578
46233
|
return params.size > 0 ? `?${params}` : "";
|
|
45579
46234
|
}
|
|
45580
|
-
function
|
|
46235
|
+
function enc6(id) {
|
|
45581
46236
|
if (/^\.+$/.test(id)) {
|
|
45582
46237
|
throw new McpToolError(`Invalid id '${id}'.`, {
|
|
45583
46238
|
hint: "Ids must be Eventbrite object ids (normally digits), not path segments."
|
|
@@ -45659,7 +46314,7 @@ function registerAccountTools(server, deps) {
|
|
|
45659
46314
|
if (continuation) params.set("continuation", continuation);
|
|
45660
46315
|
const data = await client2.request(
|
|
45661
46316
|
"GET",
|
|
45662
|
-
`/organizations/${
|
|
46317
|
+
`/organizations/${enc6(org_id)}/events/${qs(params)}`
|
|
45663
46318
|
);
|
|
45664
46319
|
return viewResponse(view, data);
|
|
45665
46320
|
}
|
|
@@ -45682,7 +46337,7 @@ function registerAccountTools(server, deps) {
|
|
|
45682
46337
|
if (continuation) params.set("continuation", continuation);
|
|
45683
46338
|
const data = await client2.request(
|
|
45684
46339
|
"GET",
|
|
45685
|
-
`/organizations/${
|
|
46340
|
+
`/organizations/${enc6(org_id)}/attendees/${qs(params)}`
|
|
45686
46341
|
);
|
|
45687
46342
|
return viewResponse(view, data);
|
|
45688
46343
|
}
|
|
@@ -45703,7 +46358,7 @@ function registerAccountTools(server, deps) {
|
|
|
45703
46358
|
if (continuation) params.set("continuation", continuation);
|
|
45704
46359
|
const data = await client2.request(
|
|
45705
46360
|
"GET",
|
|
45706
|
-
`/organizations/${
|
|
46361
|
+
`/organizations/${enc6(org_id)}/orders/${qs(params)}`
|
|
45707
46362
|
);
|
|
45708
46363
|
return viewResponse(view, data);
|
|
45709
46364
|
}
|
|
@@ -45743,7 +46398,7 @@ function registerAccountTools(server, deps) {
|
|
|
45743
46398
|
if (continuation) params.set("continuation", continuation);
|
|
45744
46399
|
return viewResponse(
|
|
45745
46400
|
view,
|
|
45746
|
-
await client2.request("GET", `/organizations/${
|
|
46401
|
+
await client2.request("GET", `/organizations/${enc6(org_id)}/${noun}/${qs(params)}`)
|
|
45747
46402
|
);
|
|
45748
46403
|
}
|
|
45749
46404
|
);
|
|
@@ -45773,7 +46428,7 @@ function registerAccountTools(server, deps) {
|
|
|
45773
46428
|
if (continuation) params.set("continuation", continuation);
|
|
45774
46429
|
return viewResponse(
|
|
45775
46430
|
view,
|
|
45776
|
-
await client2.request("GET", `/organizations/${
|
|
46431
|
+
await client2.request("GET", `/organizations/${enc6(org_id)}/reports/${kind}/${qs(params)}`)
|
|
45777
46432
|
);
|
|
45778
46433
|
}
|
|
45779
46434
|
);
|
|
@@ -45877,7 +46532,7 @@ function registerEventTools(server, deps) {
|
|
|
45877
46532
|
if (continuation) params.set("continuation", continuation);
|
|
45878
46533
|
return viewResponse(
|
|
45879
46534
|
view,
|
|
45880
|
-
await client2.request("GET", `/events/${
|
|
46535
|
+
await client2.request("GET", `/events/${enc6(event_id)}/attendees/${qs(params)}`)
|
|
45881
46536
|
);
|
|
45882
46537
|
}
|
|
45883
46538
|
);
|
|
@@ -45894,7 +46549,7 @@ function registerEventTools(server, deps) {
|
|
|
45894
46549
|
},
|
|
45895
46550
|
async ({ event_id, attendee_id, view }) => viewResponse(
|
|
45896
46551
|
view,
|
|
45897
|
-
await client2.request("GET", `/events/${
|
|
46552
|
+
await client2.request("GET", `/events/${enc6(event_id)}/attendees/${enc6(attendee_id)}/`)
|
|
45898
46553
|
)
|
|
45899
46554
|
);
|
|
45900
46555
|
server.registerTool(
|
|
@@ -45917,7 +46572,7 @@ function registerEventTools(server, deps) {
|
|
|
45917
46572
|
if (continuation) params.set("continuation", continuation);
|
|
45918
46573
|
return viewResponse(
|
|
45919
46574
|
view,
|
|
45920
|
-
await client2.request("GET", `/events/${
|
|
46575
|
+
await client2.request("GET", `/events/${enc6(event_id)}/orders/${qs(params)}`)
|
|
45921
46576
|
);
|
|
45922
46577
|
}
|
|
45923
46578
|
);
|
|
@@ -45936,7 +46591,7 @@ function registerEventTools(server, deps) {
|
|
|
45936
46591
|
view,
|
|
45937
46592
|
await client2.request(
|
|
45938
46593
|
"GET",
|
|
45939
|
-
`/events/${
|
|
46594
|
+
`/events/${enc6(event_id)}/ticket_classes/${enc6(ticket_class_id)}/`
|
|
45940
46595
|
)
|
|
45941
46596
|
)
|
|
45942
46597
|
);
|
|
@@ -45955,7 +46610,7 @@ function registerEventTools(server, deps) {
|
|
|
45955
46610
|
view,
|
|
45956
46611
|
await client2.request(
|
|
45957
46612
|
"GET",
|
|
45958
|
-
`/events/${
|
|
46613
|
+
`/events/${enc6(event_id)}/${canned ? "canned_questions" : "questions"}/`
|
|
45959
46614
|
)
|
|
45960
46615
|
)
|
|
45961
46616
|
);
|
|
@@ -45978,7 +46633,7 @@ function registerLookupTools(server, deps) {
|
|
|
45978
46633
|
async ({ order_id, expand, view }) => {
|
|
45979
46634
|
const params = new URLSearchParams();
|
|
45980
46635
|
if (expand) params.set("expand", expand);
|
|
45981
|
-
return viewResponse(view, await client2.request("GET", `/orders/${
|
|
46636
|
+
return viewResponse(view, await client2.request("GET", `/orders/${enc6(order_id)}/${qs(params)}`));
|
|
45982
46637
|
}
|
|
45983
46638
|
);
|
|
45984
46639
|
server.registerTool(
|
|
@@ -45991,7 +46646,7 @@ function registerLookupTools(server, deps) {
|
|
|
45991
46646
|
view: viewArg()
|
|
45992
46647
|
}
|
|
45993
46648
|
},
|
|
45994
|
-
async ({ venue_id, view }) => viewResponse(view, await client2.request("GET", `/venues/${
|
|
46649
|
+
async ({ venue_id, view }) => viewResponse(view, await client2.request("GET", `/venues/${enc6(venue_id)}/`))
|
|
45995
46650
|
);
|
|
45996
46651
|
server.registerTool(
|
|
45997
46652
|
"eb_venue_events",
|
|
@@ -46011,7 +46666,7 @@ function registerLookupTools(server, deps) {
|
|
|
46011
46666
|
if (continuation) params.set("continuation", continuation);
|
|
46012
46667
|
return viewResponse(
|
|
46013
46668
|
view,
|
|
46014
|
-
await client2.request("GET", `/venues/${
|
|
46669
|
+
await client2.request("GET", `/venues/${enc6(venue_id)}/events/${qs(params)}`)
|
|
46015
46670
|
);
|
|
46016
46671
|
}
|
|
46017
46672
|
);
|
|
@@ -46025,7 +46680,7 @@ function registerLookupTools(server, deps) {
|
|
|
46025
46680
|
view: viewArg()
|
|
46026
46681
|
}
|
|
46027
46682
|
},
|
|
46028
|
-
async ({ organizer_id, view }) => viewResponse(view, await client2.request("GET", `/organizers/${
|
|
46683
|
+
async ({ organizer_id, view }) => viewResponse(view, await client2.request("GET", `/organizers/${enc6(organizer_id)}/`))
|
|
46029
46684
|
);
|
|
46030
46685
|
server.registerTool(
|
|
46031
46686
|
"eb_organizer_events",
|
|
@@ -46047,7 +46702,7 @@ function registerLookupTools(server, deps) {
|
|
|
46047
46702
|
if (continuation) params.set("continuation", continuation);
|
|
46048
46703
|
return viewResponse(
|
|
46049
46704
|
view,
|
|
46050
|
-
await client2.request("GET", `/organizers/${
|
|
46705
|
+
await client2.request("GET", `/organizers/${enc6(organizer_id)}/events/${qs(params)}`)
|
|
46051
46706
|
);
|
|
46052
46707
|
}
|
|
46053
46708
|
);
|
|
@@ -46069,7 +46724,7 @@ function registerLookupTools(server, deps) {
|
|
|
46069
46724
|
if (continuation) params.set("continuation", continuation);
|
|
46070
46725
|
return viewResponse(
|
|
46071
46726
|
view,
|
|
46072
|
-
await client2.request("GET", `/series/${
|
|
46727
|
+
await client2.request("GET", `/series/${enc6(series_id)}/events/${qs(params)}`)
|
|
46073
46728
|
);
|
|
46074
46729
|
}
|
|
46075
46730
|
);
|
|
@@ -46083,7 +46738,7 @@ function registerLookupTools(server, deps) {
|
|
|
46083
46738
|
view: viewArg()
|
|
46084
46739
|
}
|
|
46085
46740
|
},
|
|
46086
|
-
async ({ user_id, view }) => viewResponse(view, await client2.request("GET", `/users/${
|
|
46741
|
+
async ({ user_id, view }) => viewResponse(view, await client2.request("GET", `/users/${enc6(user_id)}/`))
|
|
46087
46742
|
);
|
|
46088
46743
|
}
|
|
46089
46744
|
|