@chrischall/eventbrite-mcp 0.3.0 → 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 +1483 -564
- package/dist/version.js +1 -1
- package/package.json +7 -7
- package/server.json +2 -2
package/dist/bundle.js
CHANGED
|
@@ -7287,21 +7287,210 @@ 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",
|
|
7493
|
+
"fetch_in_page",
|
|
7305
7494
|
"read_cookies",
|
|
7306
7495
|
"read_local_storage",
|
|
7307
7496
|
"read_session_storage",
|
|
@@ -7380,6 +7569,143 @@ var init_json_pointer = __esm({
|
|
|
7380
7569
|
}
|
|
7381
7570
|
});
|
|
7382
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
|
+
|
|
7383
7709
|
// node_modules/@fetchproxy/protocol/dist/validate.js
|
|
7384
7710
|
function assertObject(x, label) {
|
|
7385
7711
|
if (typeof x !== "object" || x === null || Array.isArray(x)) {
|
|
@@ -7404,6 +7730,23 @@ function assertBase64(x, label) {
|
|
|
7404
7730
|
if (!BASE64_RE.test(x))
|
|
7405
7731
|
throw new ProtocolError(`${label}: invalid base64`);
|
|
7406
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
|
+
}
|
|
7745
|
+
function assertBoolean(x, label) {
|
|
7746
|
+
if (typeof x !== "boolean") {
|
|
7747
|
+
throw new ProtocolError(`${label}: must be boolean`);
|
|
7748
|
+
}
|
|
7749
|
+
}
|
|
7407
7750
|
function assertPositiveInt(x, label) {
|
|
7408
7751
|
if (typeof x !== "number" || !Number.isInteger(x) || x <= 0) {
|
|
7409
7752
|
throw new ProtocolError(`${label}: expected positive integer`);
|
|
@@ -7697,6 +8040,10 @@ function validateFrame(raw) {
|
|
|
7697
8040
|
return validateEncrypted(raw);
|
|
7698
8041
|
if (t === "pair-pending")
|
|
7699
8042
|
return validatePairPending(raw);
|
|
8043
|
+
if (t === "hello-rejected")
|
|
8044
|
+
return validateHelloRejected(raw);
|
|
8045
|
+
if (t === "extension-disconnected")
|
|
8046
|
+
return { type: "extension-disconnected" };
|
|
7700
8047
|
throw new ProtocolError(`unknown frame type: ${String(t)}`);
|
|
7701
8048
|
}
|
|
7702
8049
|
function validateHello(raw) {
|
|
@@ -7723,6 +8070,9 @@ function validateHello(raw) {
|
|
|
7723
8070
|
if (!HOSTNAME_RE.test(d)) {
|
|
7724
8071
|
throw new ProtocolError(`hello.domains: invalid hostname ${JSON.stringify(d)}`);
|
|
7725
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
|
+
}
|
|
7726
8076
|
}
|
|
7727
8077
|
if (raw.capabilities !== void 0) {
|
|
7728
8078
|
if (!Array.isArray(raw.capabilities)) {
|
|
@@ -7764,12 +8114,24 @@ function validateHello(raw) {
|
|
|
7764
8114
|
if (raw.domSelectors !== void 0) {
|
|
7765
8115
|
assertDomSelectorsArray(raw.domSelectors, "hello.domSelectors");
|
|
7766
8116
|
}
|
|
8117
|
+
if (raw.accepts !== void 0) {
|
|
8118
|
+
if (!Array.isArray(raw.accepts)) {
|
|
8119
|
+
throw new ProtocolError("hello.accepts: expected array");
|
|
8120
|
+
}
|
|
8121
|
+
for (const a of raw.accepts) {
|
|
8122
|
+
if (typeof a !== "string") {
|
|
8123
|
+
throw new ProtocolError(`hello.accepts: entry must be string, got ${typeof a}`);
|
|
8124
|
+
}
|
|
8125
|
+
}
|
|
8126
|
+
}
|
|
7767
8127
|
if (raw.graphqlOps !== void 0) {
|
|
7768
8128
|
assertGraphqlOpsArray(raw.graphqlOps, "hello.graphqlOps");
|
|
7769
8129
|
}
|
|
7770
8130
|
assertBase64(raw.identityX25519Pub, "hello.identityX25519Pub");
|
|
7771
8131
|
assertBase64(raw.identityEd25519Pub, "hello.identityEd25519Pub");
|
|
7772
8132
|
assertBase64(raw.sessionNonce, "hello.sessionNonce");
|
|
8133
|
+
assertBase64Bytes(raw.sessionPub, "hello.sessionPub", 32);
|
|
8134
|
+
assertBase64Bytes(raw.answersExtNonce, "hello.answersExtNonce", 32);
|
|
7773
8135
|
assertBase64(raw.sessionSig, "hello.sessionSig");
|
|
7774
8136
|
return raw;
|
|
7775
8137
|
}
|
|
@@ -7787,11 +8149,34 @@ function validateHello(raw) {
|
|
|
7787
8149
|
}
|
|
7788
8150
|
throw new ProtocolError(`hello.role: must be 'server' or 'extension', got ${String(role)}`);
|
|
7789
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
|
+
}
|
|
7790
8174
|
function validateReady(raw) {
|
|
7791
8175
|
assertString(raw.mcpId, "ready.mcpId");
|
|
7792
8176
|
if (!isValidMcpId(raw.mcpId))
|
|
7793
8177
|
throw new ProtocolError("ready.mcpId: invalid format");
|
|
7794
8178
|
assertBase64(raw.extensionSessionPub, "ready.extensionSessionPub");
|
|
8179
|
+
assertBase64Bytes(raw.mcpSessionPub, "ready.mcpSessionPub", 32);
|
|
7795
8180
|
assertBase64(raw.sessionSig, "ready.sessionSig");
|
|
7796
8181
|
return raw;
|
|
7797
8182
|
}
|
|
@@ -7810,10 +8195,20 @@ function validatePairPending(raw) {
|
|
|
7810
8195
|
throw new ProtocolError("pair-pending.mcpId: invalid format");
|
|
7811
8196
|
assertString(raw.pairCode, "pair-pending.pairCode");
|
|
7812
8197
|
if (!PAIR_CODE_RE.test(raw.pairCode)) {
|
|
7813
|
-
throw new ProtocolError(`pair-pending.pairCode: must match
|
|
8198
|
+
throw new ProtocolError(`pair-pending.pairCode: must match XXXX-XXXX, got ${String(raw.pairCode)}`);
|
|
7814
8199
|
}
|
|
7815
8200
|
return { type: "pair-pending", mcpId: raw.mcpId, pairCode: raw.pairCode };
|
|
7816
8201
|
}
|
|
8202
|
+
function validateHelloRejected(raw) {
|
|
8203
|
+
assertString(raw.mcpId, "hello-rejected.mcpId");
|
|
8204
|
+
if (!isValidMcpId(raw.mcpId))
|
|
8205
|
+
throw new ProtocolError("hello-rejected.mcpId: invalid format");
|
|
8206
|
+
assertString(raw.reason, "hello-rejected.reason");
|
|
8207
|
+
if (raw.reason.length > 200) {
|
|
8208
|
+
throw new ProtocolError("hello-rejected.reason: must be at most 200 characters");
|
|
8209
|
+
}
|
|
8210
|
+
return { type: "hello-rejected", mcpId: raw.mcpId, reason: raw.reason };
|
|
8211
|
+
}
|
|
7817
8212
|
function validateInnerFrame(raw) {
|
|
7818
8213
|
assertObject(raw, "inner");
|
|
7819
8214
|
const t = raw.type;
|
|
@@ -7845,6 +8240,14 @@ function validateInnerRequest(raw) {
|
|
|
7845
8240
|
if (raw.init.body !== void 0) {
|
|
7846
8241
|
assertString(raw.init.body, "inner.init.body");
|
|
7847
8242
|
}
|
|
8243
|
+
if (raw.init.inPage !== void 0) {
|
|
8244
|
+
assertBoolean(raw.init.inPage, "inner.init.inPage");
|
|
8245
|
+
}
|
|
8246
|
+
if (raw.init.credentials !== void 0) {
|
|
8247
|
+
if (raw.init.credentials !== "include" && raw.init.credentials !== "omit") {
|
|
8248
|
+
throw new ProtocolError("inner.init.credentials: must be 'include' or 'omit'");
|
|
8249
|
+
}
|
|
8250
|
+
}
|
|
7848
8251
|
return raw;
|
|
7849
8252
|
}
|
|
7850
8253
|
if (raw.op === "read_cookies") {
|
|
@@ -8241,6 +8644,7 @@ var init_validate = __esm({
|
|
|
8241
8644
|
init_frames();
|
|
8242
8645
|
init_mcp_id();
|
|
8243
8646
|
init_json_pointer();
|
|
8647
|
+
init_public_suffix();
|
|
8244
8648
|
ProtocolError = class extends Error {
|
|
8245
8649
|
constructor(message) {
|
|
8246
8650
|
super(message);
|
|
@@ -8254,10 +8658,10 @@ var init_validate = __esm({
|
|
|
8254
8658
|
HEADER_NAME_RE = /^[A-Za-z0-9_\-]{1,128}$/;
|
|
8255
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;
|
|
8256
8660
|
CAPTURE_PATH_RE = /^\/[A-Za-z0-9._~%\-/]*\*?$/;
|
|
8257
|
-
DOM_SELECTOR_RE = /^[
|
|
8661
|
+
DOM_SELECTOR_RE = /^[^\x00-\x1f\x7f]{1,512}$/;
|
|
8258
8662
|
DOM_ATTRIBUTE_RE = /^[A-Za-z_:][A-Za-z0-9_:.\-]{0,127}$/;
|
|
8259
8663
|
GRAPHQL_OP_NAME_RE = /^[_A-Za-z][_0-9A-Za-z]{0,127}$/;
|
|
8260
|
-
PAIR_CODE_RE = /^\d{
|
|
8664
|
+
PAIR_CODE_RE = /^\d{4}-\d{4}$/;
|
|
8261
8665
|
KNOWN_RESPONSE_OPS = /* @__PURE__ */ new Set([
|
|
8262
8666
|
...KNOWN_CAPABILITIES,
|
|
8263
8667
|
"graphql_query"
|
|
@@ -8265,180 +8669,33 @@ var init_validate = __esm({
|
|
|
8265
8669
|
}
|
|
8266
8670
|
});
|
|
8267
8671
|
|
|
8268
|
-
// node_modules/@fetchproxy/protocol/dist/crypto.js
|
|
8269
|
-
async function exportRaw(key, format) {
|
|
8270
|
-
const buf = await subtle.exportKey(format, key);
|
|
8271
|
-
return new Uint8Array(buf);
|
|
8272
|
-
}
|
|
8273
|
-
async function generateX25519() {
|
|
8274
|
-
const kp = await subtle.generateKey({ name: "X25519" }, true, [
|
|
8275
|
-
"deriveBits"
|
|
8276
|
-
]);
|
|
8277
|
-
const priv = await exportRaw(kp.privateKey, "pkcs8");
|
|
8278
|
-
const pub = await exportRaw(kp.publicKey, "raw");
|
|
8279
|
-
return { privateKey: priv.slice(-32), publicKey: pub };
|
|
8280
|
-
}
|
|
8281
|
-
async function generateEd25519() {
|
|
8282
|
-
const kp = await subtle.generateKey({ name: "Ed25519" }, true, [
|
|
8283
|
-
"sign",
|
|
8284
|
-
"verify"
|
|
8285
|
-
]);
|
|
8286
|
-
const priv = await exportRaw(kp.privateKey, "pkcs8");
|
|
8287
|
-
const pub = await exportRaw(kp.publicKey, "raw");
|
|
8288
|
-
return { privateKey: priv.slice(-32), publicKey: pub };
|
|
8289
|
-
}
|
|
8290
|
-
async function importX25519Priv(raw) {
|
|
8291
|
-
const prefix = new Uint8Array([
|
|
8292
|
-
48,
|
|
8293
|
-
46,
|
|
8294
|
-
2,
|
|
8295
|
-
1,
|
|
8296
|
-
0,
|
|
8297
|
-
48,
|
|
8298
|
-
5,
|
|
8299
|
-
6,
|
|
8300
|
-
3,
|
|
8301
|
-
43,
|
|
8302
|
-
101,
|
|
8303
|
-
110,
|
|
8304
|
-
4,
|
|
8305
|
-
34,
|
|
8306
|
-
4,
|
|
8307
|
-
32
|
|
8308
|
-
]);
|
|
8309
|
-
const pkcs8 = new Uint8Array(prefix.length + raw.length);
|
|
8310
|
-
pkcs8.set(prefix, 0);
|
|
8311
|
-
pkcs8.set(raw, prefix.length);
|
|
8312
|
-
return subtle.importKey("pkcs8", pkcs8, { name: "X25519" }, false, [
|
|
8313
|
-
"deriveBits"
|
|
8314
|
-
]);
|
|
8315
|
-
}
|
|
8316
|
-
async function importX25519Pub(raw) {
|
|
8317
|
-
return subtle.importKey("raw", raw, { name: "X25519" }, false, []);
|
|
8318
|
-
}
|
|
8319
|
-
async function importEd25519Priv(raw) {
|
|
8320
|
-
const prefix = new Uint8Array([
|
|
8321
|
-
48,
|
|
8322
|
-
46,
|
|
8323
|
-
2,
|
|
8324
|
-
1,
|
|
8325
|
-
0,
|
|
8326
|
-
48,
|
|
8327
|
-
5,
|
|
8328
|
-
6,
|
|
8329
|
-
3,
|
|
8330
|
-
43,
|
|
8331
|
-
101,
|
|
8332
|
-
112,
|
|
8333
|
-
4,
|
|
8334
|
-
34,
|
|
8335
|
-
4,
|
|
8336
|
-
32
|
|
8337
|
-
]);
|
|
8338
|
-
const pkcs8 = new Uint8Array(prefix.length + raw.length);
|
|
8339
|
-
pkcs8.set(prefix, 0);
|
|
8340
|
-
pkcs8.set(raw, prefix.length);
|
|
8341
|
-
return subtle.importKey("pkcs8", pkcs8, { name: "Ed25519" }, false, ["sign"]);
|
|
8342
|
-
}
|
|
8343
|
-
async function importEd25519Pub(raw) {
|
|
8344
|
-
return subtle.importKey("raw", raw, { name: "Ed25519" }, false, ["verify"]);
|
|
8345
|
-
}
|
|
8346
|
-
async function ecdhX25519(privRaw, pubRaw) {
|
|
8347
|
-
const priv = await importX25519Priv(privRaw);
|
|
8348
|
-
const pub = await importX25519Pub(pubRaw);
|
|
8349
|
-
const bits = await subtle.deriveBits({ name: "X25519", public: pub }, priv, 256);
|
|
8350
|
-
return new Uint8Array(bits);
|
|
8351
|
-
}
|
|
8352
|
-
async function ed25519Sign(privRaw, msg) {
|
|
8353
|
-
const priv = await importEd25519Priv(privRaw);
|
|
8354
|
-
const sig = await subtle.sign({ name: "Ed25519" }, priv, msg);
|
|
8355
|
-
return new Uint8Array(sig);
|
|
8356
|
-
}
|
|
8357
|
-
async function ed25519Verify(pubRaw, msg, sig) {
|
|
8358
|
-
const pub = await importEd25519Pub(pubRaw);
|
|
8359
|
-
return subtle.verify({ name: "Ed25519" }, pub, sig, msg);
|
|
8360
|
-
}
|
|
8361
|
-
async function hkdfSha256(ikm, salt, info, lenBytes) {
|
|
8362
|
-
const baseKey = await subtle.importKey("raw", ikm, { name: "HKDF" }, false, [
|
|
8363
|
-
"deriveBits"
|
|
8364
|
-
]);
|
|
8365
|
-
const bits = await subtle.deriveBits({
|
|
8366
|
-
name: "HKDF",
|
|
8367
|
-
hash: "SHA-256",
|
|
8368
|
-
salt,
|
|
8369
|
-
info
|
|
8370
|
-
}, baseKey, lenBytes * 8);
|
|
8371
|
-
return new Uint8Array(bits);
|
|
8372
|
-
}
|
|
8373
|
-
async function aesGcmSeal(key, iv, plaintext) {
|
|
8374
|
-
const k = await subtle.importKey("raw", key, { name: "AES-GCM" }, false, [
|
|
8375
|
-
"encrypt"
|
|
8376
|
-
]);
|
|
8377
|
-
const ct = await subtle.encrypt({ name: "AES-GCM", iv }, k, plaintext);
|
|
8378
|
-
return new Uint8Array(ct);
|
|
8379
|
-
}
|
|
8380
|
-
async function aesGcmOpen(key, iv, ciphertext) {
|
|
8381
|
-
const k = await subtle.importKey("raw", key, { name: "AES-GCM" }, false, [
|
|
8382
|
-
"decrypt"
|
|
8383
|
-
]);
|
|
8384
|
-
const pt = await subtle.decrypt({ name: "AES-GCM", iv }, k, ciphertext);
|
|
8385
|
-
return new Uint8Array(pt);
|
|
8386
|
-
}
|
|
8387
|
-
async function sha256(data) {
|
|
8388
|
-
const h = await subtle.digest("SHA-256", data);
|
|
8389
|
-
return new Uint8Array(h);
|
|
8390
|
-
}
|
|
8391
|
-
var subtle;
|
|
8392
|
-
var init_crypto = __esm({
|
|
8393
|
-
"node_modules/@fetchproxy/protocol/dist/crypto.js"() {
|
|
8394
|
-
subtle = globalThis.crypto.subtle;
|
|
8395
|
-
}
|
|
8396
|
-
});
|
|
8397
|
-
|
|
8398
|
-
// node_modules/@fetchproxy/protocol/dist/encoding.js
|
|
8399
|
-
function toB64(bytes) {
|
|
8400
|
-
let s = "";
|
|
8401
|
-
for (let i = 0; i < bytes.length; i++)
|
|
8402
|
-
s += String.fromCharCode(bytes[i]);
|
|
8403
|
-
return btoa(s);
|
|
8404
|
-
}
|
|
8405
|
-
function fromB64(s) {
|
|
8406
|
-
const bin = atob(s);
|
|
8407
|
-
const out = new Uint8Array(bin.length);
|
|
8408
|
-
for (let i = 0; i < bin.length; i++)
|
|
8409
|
-
out[i] = bin.charCodeAt(i);
|
|
8410
|
-
return out;
|
|
8411
|
-
}
|
|
8412
|
-
function concatBytes(a, b) {
|
|
8413
|
-
const out = new Uint8Array(a.length + b.length);
|
|
8414
|
-
out.set(a, 0);
|
|
8415
|
-
out.set(b, a.length);
|
|
8416
|
-
return out;
|
|
8417
|
-
}
|
|
8418
|
-
var init_encoding = __esm({
|
|
8419
|
-
"node_modules/@fetchproxy/protocol/dist/encoding.js"() {
|
|
8420
|
-
}
|
|
8421
|
-
});
|
|
8422
|
-
|
|
8423
8672
|
// node_modules/@fetchproxy/protocol/dist/pair-code.js
|
|
8424
|
-
async function
|
|
8425
|
-
const
|
|
8426
|
-
const
|
|
8427
|
-
const
|
|
8428
|
-
|
|
8429
|
-
|
|
8430
|
-
|
|
8431
|
-
const
|
|
8432
|
-
|
|
8433
|
-
|
|
8434
|
-
}
|
|
8435
|
-
|
|
8436
|
-
|
|
8437
|
-
|
|
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;
|
|
8438
8694
|
var init_pair_code = __esm({
|
|
8439
8695
|
"node_modules/@fetchproxy/protocol/dist/pair-code.js"() {
|
|
8440
8696
|
init_crypto();
|
|
8441
|
-
|
|
8697
|
+
enc2 = new TextEncoder();
|
|
8698
|
+
PAIR_LABEL = "fetchproxy/4/pair";
|
|
8442
8699
|
}
|
|
8443
8700
|
});
|
|
8444
8701
|
|
|
@@ -8448,10 +8705,24 @@ function randomIv() {
|
|
|
8448
8705
|
globalThis.crypto.getRandomValues(iv);
|
|
8449
8706
|
return iv;
|
|
8450
8707
|
}
|
|
8451
|
-
|
|
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) {
|
|
8452
8723
|
const iv = randomIv();
|
|
8453
|
-
const pt =
|
|
8454
|
-
const ct = await aesGcmSeal(sessionKey, iv, pt);
|
|
8724
|
+
const pt = plaintextOf(inner);
|
|
8725
|
+
const ct = await aesGcmSeal(sessionKey, iv, pt, frameAad(mcpId, seq, direction));
|
|
8455
8726
|
return {
|
|
8456
8727
|
type: "frame",
|
|
8457
8728
|
mcpId,
|
|
@@ -8460,18 +8731,18 @@ async function sealInnerFrame(sessionKey, mcpId, seq, inner) {
|
|
|
8460
8731
|
ciphertext: toB64(ct)
|
|
8461
8732
|
};
|
|
8462
8733
|
}
|
|
8463
|
-
async function openEncryptedFrame(sessionKey, frame) {
|
|
8464
|
-
const result = await openEncryptedFrameDetailed(sessionKey, frame);
|
|
8734
|
+
async function openEncryptedFrame(sessionKey, frame, direction) {
|
|
8735
|
+
const result = await openEncryptedFrameDetailed(sessionKey, frame, direction);
|
|
8465
8736
|
if (result.stage === "ok")
|
|
8466
8737
|
return result.inner;
|
|
8467
8738
|
throw result.error instanceof Error ? result.error : new Error(String(result.error));
|
|
8468
8739
|
}
|
|
8469
|
-
async function openEncryptedFrameDetailed(sessionKey, frame) {
|
|
8740
|
+
async function openEncryptedFrameDetailed(sessionKey, frame, direction) {
|
|
8470
8741
|
let pt;
|
|
8471
8742
|
try {
|
|
8472
8743
|
const iv = fromB64(frame.iv);
|
|
8473
8744
|
const ct = fromB64(frame.ciphertext);
|
|
8474
|
-
pt = await aesGcmOpen(sessionKey, iv, ct);
|
|
8745
|
+
pt = await aesGcmOpen(sessionKey, iv, ct, frameAad(frame.mcpId, frame.seq, direction));
|
|
8475
8746
|
} catch (error61) {
|
|
8476
8747
|
return { stage: "decrypt-failed", error: error61 };
|
|
8477
8748
|
}
|
|
@@ -8489,14 +8760,18 @@ async function openEncryptedFrameDetailed(sessionKey, frame) {
|
|
|
8489
8760
|
return { stage: "validation-failed", error: error61, recoveredId };
|
|
8490
8761
|
}
|
|
8491
8762
|
}
|
|
8492
|
-
var
|
|
8763
|
+
var enc3, dec, AES_GCM_TAG_BYTES, IV_B64_BYTES, MAX_FRAME_BYTES;
|
|
8493
8764
|
var init_seal = __esm({
|
|
8494
8765
|
"node_modules/@fetchproxy/protocol/dist/seal.js"() {
|
|
8495
8766
|
init_crypto();
|
|
8496
8767
|
init_encoding();
|
|
8768
|
+
init_frames();
|
|
8497
8769
|
init_validate();
|
|
8498
|
-
|
|
8770
|
+
enc3 = new TextEncoder();
|
|
8499
8771
|
dec = new TextDecoder();
|
|
8772
|
+
AES_GCM_TAG_BYTES = 16;
|
|
8773
|
+
IV_B64_BYTES = 16;
|
|
8774
|
+
MAX_FRAME_BYTES = 42 * 1024 * 1024;
|
|
8500
8775
|
}
|
|
8501
8776
|
});
|
|
8502
8777
|
|
|
@@ -8510,6 +8785,7 @@ var init_dist = __esm({
|
|
|
8510
8785
|
init_pair_code();
|
|
8511
8786
|
init_seal();
|
|
8512
8787
|
init_encoding();
|
|
8788
|
+
init_public_suffix();
|
|
8513
8789
|
init_json_pointer();
|
|
8514
8790
|
}
|
|
8515
8791
|
});
|
|
@@ -12259,7 +12535,7 @@ var init_wrapper = __esm({
|
|
|
12259
12535
|
async function buildServerHello(opts) {
|
|
12260
12536
|
const sessionNonce = new Uint8Array(32);
|
|
12261
12537
|
globalThis.crypto.getRandomValues(sessionNonce);
|
|
12262
|
-
const sig = await ed25519Sign(opts.identity.ed25519Priv,
|
|
12538
|
+
const sig = await ed25519Sign(opts.identity.ed25519Priv, helloSignaturePayload(opts.mcpId, sessionNonce, opts.sessionPub, opts.answersExtNonce));
|
|
12263
12539
|
const hello = {
|
|
12264
12540
|
type: "hello",
|
|
12265
12541
|
protocolVersion: PROTOCOL_VERSION,
|
|
@@ -12272,8 +12548,12 @@ async function buildServerHello(opts) {
|
|
|
12272
12548
|
identityX25519Pub: toB64(opts.identity.x25519Pub),
|
|
12273
12549
|
identityEd25519Pub: toB64(opts.identity.ed25519Pub),
|
|
12274
12550
|
sessionNonce: toB64(sessionNonce),
|
|
12551
|
+
sessionPub: toB64(opts.sessionPub),
|
|
12552
|
+
answersExtNonce: toB64(opts.answersExtNonce),
|
|
12275
12553
|
sessionSig: toB64(sig)
|
|
12276
12554
|
};
|
|
12555
|
+
if (opts.accepts && opts.accepts.length > 0)
|
|
12556
|
+
hello.accepts = [...opts.accepts];
|
|
12277
12557
|
if (opts.cookieKeys && opts.cookieKeys.length > 0) {
|
|
12278
12558
|
hello.cookieKeys = [...opts.cookieKeys];
|
|
12279
12559
|
}
|
|
@@ -12331,14 +12611,31 @@ var init_build_server_hello = __esm({
|
|
|
12331
12611
|
}
|
|
12332
12612
|
});
|
|
12333
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
|
+
|
|
12334
12629
|
// node_modules/@fetchproxy/server/dist/session.js
|
|
12335
|
-
var SessionState;
|
|
12630
|
+
var MAX_INFLIGHT_INBOUND_SEQS, SessionState;
|
|
12336
12631
|
var init_session = __esm({
|
|
12337
12632
|
"node_modules/@fetchproxy/server/dist/session.js"() {
|
|
12633
|
+
MAX_INFLIGHT_INBOUND_SEQS = 1024;
|
|
12338
12634
|
SessionState = class {
|
|
12339
12635
|
sessionKey;
|
|
12340
12636
|
outboundSeq = 0;
|
|
12341
12637
|
lastInboundSeq = 0;
|
|
12638
|
+
inflightInbound = /* @__PURE__ */ new Set();
|
|
12342
12639
|
constructor(sessionKey) {
|
|
12343
12640
|
this.sessionKey = sessionKey;
|
|
12344
12641
|
}
|
|
@@ -12346,17 +12643,55 @@ var init_session = __esm({
|
|
|
12346
12643
|
this.outboundSeq += 1;
|
|
12347
12644
|
return this.outboundSeq;
|
|
12348
12645
|
}
|
|
12349
|
-
|
|
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) {
|
|
12350
12657
|
if (seq <= this.lastInboundSeq)
|
|
12351
12658
|
return false;
|
|
12352
|
-
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);
|
|
12353
12664
|
return true;
|
|
12354
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
|
+
}
|
|
12355
12686
|
};
|
|
12356
12687
|
}
|
|
12357
12688
|
});
|
|
12358
12689
|
|
|
12359
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
|
+
}
|
|
12360
12695
|
async function awaitSessionReady(ready, opts) {
|
|
12361
12696
|
const ms = opts.timeoutMs ?? SESSION_READY_TIMEOUT_MS;
|
|
12362
12697
|
if (ms <= 0)
|
|
@@ -12375,7 +12710,7 @@ async function awaitSessionReady(ready, opts) {
|
|
|
12375
12710
|
clearTimeout(timer);
|
|
12376
12711
|
}
|
|
12377
12712
|
}
|
|
12378
|
-
var SESSION_READY_TIMEOUT_MS, FetchproxySessionNotReadyError;
|
|
12713
|
+
var SESSION_READY_TIMEOUT_MS, FetchproxySessionNotReadyError, FetchproxyHelloRejectedError, MIN_VERSION, FetchproxyProtocolVersionError;
|
|
12379
12714
|
var init_session_ready = __esm({
|
|
12380
12715
|
"node_modules/@fetchproxy/server/dist/session-ready.js"() {
|
|
12381
12716
|
SESSION_READY_TIMEOUT_MS = 3e4;
|
|
@@ -12396,15 +12731,52 @@ var init_session_ready = __esm({
|
|
|
12396
12731
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
12397
12732
|
}
|
|
12398
12733
|
};
|
|
12734
|
+
FetchproxyHelloRejectedError = class extends Error {
|
|
12735
|
+
mcpId;
|
|
12736
|
+
reason;
|
|
12737
|
+
constructor(info) {
|
|
12738
|
+
super(`fetchproxy: the extension refused the connection for "${info.mcpId}": ${info.reason}. This is the extension's own reason \u2014 it is not a timeout, and retrying unchanged will be refused the same way.`);
|
|
12739
|
+
this.name = "FetchproxyHelloRejectedError";
|
|
12740
|
+
this.mcpId = info.mcpId;
|
|
12741
|
+
this.reason = info.reason;
|
|
12742
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
12743
|
+
}
|
|
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
|
+
};
|
|
12399
12762
|
}
|
|
12400
12763
|
});
|
|
12401
12764
|
|
|
12402
12765
|
// node_modules/@fetchproxy/server/dist/identity.js
|
|
12403
12766
|
import { readFile, writeFile, mkdir, chmod } from "node:fs/promises";
|
|
12404
|
-
import { join as join2 } from "node:path";
|
|
12767
|
+
import { isAbsolute, join as join2 } from "node:path";
|
|
12405
12768
|
import { homedir } from "node:os";
|
|
12769
|
+
function envIdentityDir() {
|
|
12770
|
+
const raw = process.env.FETCHPROXY_IDENTITY_DIR;
|
|
12771
|
+
if (raw === void 0)
|
|
12772
|
+
return void 0;
|
|
12773
|
+
const dir = raw.trim();
|
|
12774
|
+
if (dir === "" || !isAbsolute(dir))
|
|
12775
|
+
return void 0;
|
|
12776
|
+
return dir;
|
|
12777
|
+
}
|
|
12406
12778
|
function defaultIdentityDir() {
|
|
12407
|
-
return join2(homedir(), ".fetchproxy", "identity");
|
|
12779
|
+
return envIdentityDir() ?? join2(homedir(), ".fetchproxy", "identity");
|
|
12408
12780
|
}
|
|
12409
12781
|
function safeIdentityFileBase(serverName) {
|
|
12410
12782
|
if (!serverName || serverName === ".." || serverName.includes("..") || !SAFE_PLAIN.test(serverName) && !SAFE_SCOPED.test(serverName)) {
|
|
@@ -12412,42 +12784,57 @@ function safeIdentityFileBase(serverName) {
|
|
|
12412
12784
|
}
|
|
12413
12785
|
return serverName.replace(/\//g, "_");
|
|
12414
12786
|
}
|
|
12415
|
-
|
|
12416
|
-
|
|
12417
|
-
|
|
12418
|
-
|
|
12419
|
-
try {
|
|
12420
|
-
const raw = await readFile(path, "utf8");
|
|
12421
|
-
const j2 = JSON.parse(raw);
|
|
12422
|
-
return {
|
|
12423
|
-
x25519Priv: fromB64(j2.x25519Priv),
|
|
12424
|
-
x25519Pub: fromB64(j2.x25519Pub),
|
|
12425
|
-
ed25519Priv: fromB64(j2.ed25519Priv),
|
|
12426
|
-
ed25519Pub: fromB64(j2.ed25519Pub),
|
|
12427
|
-
createdAt: j2.createdAt
|
|
12428
|
-
};
|
|
12429
|
-
} catch (e) {
|
|
12430
|
-
if (e.code !== "ENOENT")
|
|
12431
|
-
throw e;
|
|
12432
|
-
}
|
|
12787
|
+
function identityFilePath(dir, serverName) {
|
|
12788
|
+
return join2(dir, `${safeIdentityFileBase(serverName)}.json`);
|
|
12789
|
+
}
|
|
12790
|
+
async function generateIdentity() {
|
|
12433
12791
|
const x = await generateX25519();
|
|
12434
12792
|
const ed = await generateEd25519();
|
|
12435
|
-
|
|
12793
|
+
return {
|
|
12436
12794
|
x25519Priv: x.privateKey,
|
|
12437
12795
|
x25519Pub: x.publicKey,
|
|
12438
12796
|
ed25519Priv: ed.privateKey,
|
|
12439
12797
|
ed25519Pub: ed.publicKey,
|
|
12440
12798
|
createdAt: Date.now()
|
|
12441
12799
|
};
|
|
12442
|
-
|
|
12800
|
+
}
|
|
12801
|
+
function serializeIdentity(id) {
|
|
12802
|
+
return JSON.stringify({
|
|
12443
12803
|
x25519Priv: toB64(id.x25519Priv),
|
|
12444
12804
|
x25519Pub: toB64(id.x25519Pub),
|
|
12445
12805
|
ed25519Priv: toB64(id.ed25519Priv),
|
|
12446
12806
|
ed25519Pub: toB64(id.ed25519Pub),
|
|
12447
12807
|
createdAt: id.createdAt
|
|
12808
|
+
}, null, 2);
|
|
12809
|
+
}
|
|
12810
|
+
function parseIdentity(text) {
|
|
12811
|
+
const j = JSON.parse(text);
|
|
12812
|
+
return {
|
|
12813
|
+
x25519Priv: fromB64(j.x25519Priv),
|
|
12814
|
+
x25519Pub: fromB64(j.x25519Pub),
|
|
12815
|
+
ed25519Priv: fromB64(j.ed25519Priv),
|
|
12816
|
+
ed25519Pub: fromB64(j.ed25519Pub),
|
|
12817
|
+
createdAt: j.createdAt
|
|
12448
12818
|
};
|
|
12449
|
-
|
|
12819
|
+
}
|
|
12820
|
+
async function writeIdentityFile(dir, serverName, id) {
|
|
12821
|
+
const path = identityFilePath(dir, serverName);
|
|
12822
|
+
await mkdir(dir, { recursive: true, mode: 448 });
|
|
12823
|
+
await writeFile(path, serializeIdentity(id), { mode: 384 });
|
|
12450
12824
|
await chmod(path, 384);
|
|
12825
|
+
return path;
|
|
12826
|
+
}
|
|
12827
|
+
async function loadOrCreateIdentity(serverName, dir = defaultIdentityDir()) {
|
|
12828
|
+
const path = identityFilePath(dir, serverName);
|
|
12829
|
+
await mkdir(dir, { recursive: true, mode: 448 });
|
|
12830
|
+
try {
|
|
12831
|
+
return parseIdentity(await readFile(path, "utf8"));
|
|
12832
|
+
} catch (e) {
|
|
12833
|
+
if (e.code !== "ENOENT")
|
|
12834
|
+
throw e;
|
|
12835
|
+
}
|
|
12836
|
+
const id = await generateIdentity();
|
|
12837
|
+
await writeIdentityFile(dir, serverName, id);
|
|
12451
12838
|
return id;
|
|
12452
12839
|
}
|
|
12453
12840
|
var SAFE_PLAIN, SAFE_SCOPED;
|
|
@@ -12461,13 +12848,32 @@ var init_identity = __esm({
|
|
|
12461
12848
|
|
|
12462
12849
|
// node_modules/@fetchproxy/server/dist/extension-trust.js
|
|
12463
12850
|
import { readFile as readFile2, writeFile as writeFile2, rename, unlink, mkdir as mkdir2, chmod as chmod2 } from "node:fs/promises";
|
|
12464
|
-
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
|
+
}
|
|
12465
12870
|
function fileExtensionTrust(args) {
|
|
12871
|
+
const dir = resolveTrustDir(args.trustDir, args.dir);
|
|
12466
12872
|
return {
|
|
12467
12873
|
allowNew: args.allowNew,
|
|
12468
|
-
location: extensionTrustPath(args.serverName,
|
|
12469
|
-
read: () => readExtensionPin(args.serverName,
|
|
12470
|
-
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)
|
|
12471
12877
|
};
|
|
12472
12878
|
}
|
|
12473
12879
|
function allowNewExtensionIdentity(explicit, env = process.env) {
|
|
@@ -12494,14 +12900,14 @@ function decideExtensionTrust(args) {
|
|
|
12494
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.`
|
|
12495
12901
|
};
|
|
12496
12902
|
}
|
|
12497
|
-
function extensionTrustPath(serverName, dir =
|
|
12903
|
+
function extensionTrustPath(serverName, dir = defaultTrustDir()) {
|
|
12498
12904
|
return join3(dir, `${safeIdentityFileBase(serverName)}.extension-trust.json`);
|
|
12499
12905
|
}
|
|
12500
12906
|
function extensionTrustPathHint(serverName) {
|
|
12501
12907
|
try {
|
|
12502
12908
|
return extensionTrustPath(serverName);
|
|
12503
12909
|
} catch {
|
|
12504
|
-
return join3(
|
|
12910
|
+
return join3(defaultTrustDir(), "<server-name>.extension-trust.json");
|
|
12505
12911
|
}
|
|
12506
12912
|
}
|
|
12507
12913
|
function isPin(x) {
|
|
@@ -12510,7 +12916,7 @@ function isPin(x) {
|
|
|
12510
12916
|
const r = x;
|
|
12511
12917
|
return typeof r.identityX25519Pub === "string" && typeof r.identityEd25519Pub === "string" && typeof r.pinnedAt === "number";
|
|
12512
12918
|
}
|
|
12513
|
-
async function readExtensionPin(serverName, dir =
|
|
12919
|
+
async function readExtensionPin(serverName, dir = defaultTrustDir()) {
|
|
12514
12920
|
const path = extensionTrustPath(serverName, dir);
|
|
12515
12921
|
let raw;
|
|
12516
12922
|
try {
|
|
@@ -12535,7 +12941,7 @@ async function readExtensionPin(serverName, dir = defaultIdentityDir()) {
|
|
|
12535
12941
|
pinnedAt: parsed.pinnedAt
|
|
12536
12942
|
};
|
|
12537
12943
|
}
|
|
12538
|
-
async function writeExtensionPin(serverName, pin, dir =
|
|
12944
|
+
async function writeExtensionPin(serverName, pin, dir = defaultTrustDir()) {
|
|
12539
12945
|
const path = extensionTrustPath(serverName, dir);
|
|
12540
12946
|
await mkdir2(dir, { recursive: true, mode: 448 });
|
|
12541
12947
|
const tmp = `${path}.tmp`;
|
|
@@ -12543,28 +12949,70 @@ async function writeExtensionPin(serverName, pin, dir = defaultIdentityDir()) {
|
|
|
12543
12949
|
await chmod2(tmp, 384);
|
|
12544
12950
|
await rename(tmp, path);
|
|
12545
12951
|
}
|
|
12546
|
-
var TRUST_NEW_EXTENSION_ENV;
|
|
12952
|
+
var TRUST_DIR_ENV, TRUST_NEW_EXTENSION_ENV;
|
|
12547
12953
|
var init_extension_trust = __esm({
|
|
12548
12954
|
"node_modules/@fetchproxy/server/dist/extension-trust.js"() {
|
|
12549
12955
|
init_identity();
|
|
12956
|
+
TRUST_DIR_ENV = "FETCHPROXY_TRUST_DIR";
|
|
12550
12957
|
TRUST_NEW_EXTENSION_ENV = "FETCHPROXY_TRUST_NEW_EXTENSION";
|
|
12551
12958
|
}
|
|
12552
12959
|
});
|
|
12553
12960
|
|
|
12554
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
|
+
}
|
|
12555
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();
|
|
12556
12993
|
const wss = new import_websocket_server.default({
|
|
12557
12994
|
server: opts.httpServer,
|
|
12995
|
+
maxPayload: opts.maxPayloadBytes ?? MAX_PAYLOAD_BYTES,
|
|
12558
12996
|
verifyClient: (info, cb) => {
|
|
12559
12997
|
const origin = info.req.headers.origin;
|
|
12560
|
-
|
|
12561
|
-
|
|
12998
|
+
const verdict = originVerdict(origin, allowLocalOrigins);
|
|
12999
|
+
if (verdict.allow) {
|
|
13000
|
+
cb(true);
|
|
12562
13001
|
return;
|
|
12563
13002
|
}
|
|
12564
|
-
|
|
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);
|
|
12565
13010
|
}
|
|
12566
13011
|
});
|
|
12567
|
-
const
|
|
13012
|
+
const ownHelloBase = {
|
|
13013
|
+
// 2.6.0: tell the extension it can say WHY it refused a hello, instead of
|
|
13014
|
+
// leaving us to time out and guess.
|
|
13015
|
+
accepts: ["hello-rejected"],
|
|
12568
13016
|
identity: opts.ownIdentity,
|
|
12569
13017
|
mcpId: opts.ownMcpId,
|
|
12570
13018
|
serverName: opts.ownServerName,
|
|
@@ -12580,14 +13028,26 @@ async function startHost(opts) {
|
|
|
12580
13028
|
sessionStoragePointers: opts.ownSessionStoragePointers,
|
|
12581
13029
|
domSelectors: opts.ownDomSelectors,
|
|
12582
13030
|
graphqlOps: opts.ownGraphqlOps
|
|
12583
|
-
}
|
|
12584
|
-
const
|
|
13031
|
+
};
|
|
13032
|
+
const generateSessionKeypair = opts.generateSessionKeypair ?? generateX25519;
|
|
12585
13033
|
let extensionWs = null;
|
|
12586
13034
|
const peers = /* @__PURE__ */ new Map();
|
|
12587
13035
|
const ownInnerListeners = [];
|
|
12588
13036
|
const disconnectListeners = [];
|
|
12589
13037
|
const pendingPairListeners = [];
|
|
12590
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
|
+
}
|
|
12591
13051
|
let ownPendingPairCode = null;
|
|
12592
13052
|
let resolveOwnSession;
|
|
12593
13053
|
let rejectOwnSession;
|
|
@@ -12601,21 +13061,74 @@ async function startHost(opts) {
|
|
|
12601
13061
|
});
|
|
12602
13062
|
}
|
|
12603
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
|
+
}
|
|
12604
13088
|
let extensionHello = null;
|
|
12605
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
|
+
};
|
|
12606
13100
|
wss.on("connection", (ws) => {
|
|
12607
13101
|
let identified = null;
|
|
12608
13102
|
let peerMcpId = null;
|
|
12609
13103
|
let closed = false;
|
|
12610
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
|
+
});
|
|
12611
13117
|
ws.on("message", async (data) => {
|
|
12612
13118
|
try {
|
|
13119
|
+
let raw;
|
|
13120
|
+
try {
|
|
13121
|
+
raw = JSON.parse(data.toString());
|
|
13122
|
+
} catch {
|
|
13123
|
+
ws.close(1002, "protocol error");
|
|
13124
|
+
return;
|
|
13125
|
+
}
|
|
12613
13126
|
let frame;
|
|
12614
13127
|
try {
|
|
12615
|
-
const raw = JSON.parse(data.toString());
|
|
12616
13128
|
frame = validateFrame(raw);
|
|
12617
13129
|
} catch {
|
|
12618
|
-
ws
|
|
13130
|
+
if (!refuseVersionMismatch(ws, raw, identified))
|
|
13131
|
+
ws.close(1002, "protocol error");
|
|
12619
13132
|
return;
|
|
12620
13133
|
}
|
|
12621
13134
|
if (frame.type === "hello" && frame.role === "extension") {
|
|
@@ -12659,25 +13172,43 @@ async function startHost(opts) {
|
|
|
12659
13172
|
identified = "extension";
|
|
12660
13173
|
extensionWs = ws;
|
|
12661
13174
|
extensionHello = frame;
|
|
12662
|
-
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) {
|
|
12663
13201
|
try {
|
|
12664
|
-
|
|
12665
|
-
opts.onPairCode(code);
|
|
13202
|
+
opts.onPairCode(helloPairCode);
|
|
12666
13203
|
} catch (e) {
|
|
12667
13204
|
console.error("[fetchproxy] onPairCode threw:", e);
|
|
12668
13205
|
}
|
|
12669
13206
|
}
|
|
12670
|
-
for (const slot of peers.values())
|
|
12671
|
-
slot.ws.send(JSON.stringify(frame));
|
|
12672
|
-
ws.send(JSON.stringify(ownHello));
|
|
12673
|
-
for (const slot of peers.values()) {
|
|
12674
|
-
ws.send(JSON.stringify(slot.helloFrame));
|
|
12675
|
-
}
|
|
12676
13207
|
return;
|
|
12677
13208
|
}
|
|
12678
13209
|
if (frame.type === "hello" && frame.role === "server") {
|
|
12679
13210
|
const peerEdPub = fromB64(frame.identityEd25519Pub);
|
|
12680
|
-
const peerSigMsg =
|
|
13211
|
+
const peerSigMsg = helloSignaturePayload(frame.mcpId, fromB64(frame.sessionNonce), fromB64(frame.sessionPub), fromB64(frame.answersExtNonce));
|
|
12681
13212
|
const peerSig = fromB64(frame.sessionSig);
|
|
12682
13213
|
let peerSigOk = false;
|
|
12683
13214
|
try {
|
|
@@ -12702,10 +13233,12 @@ async function startHost(opts) {
|
|
|
12702
13233
|
identified = "peer";
|
|
12703
13234
|
peerMcpId = frame.mcpId;
|
|
12704
13235
|
peers.set(frame.mcpId, { ws, helloFrame: frame });
|
|
12705
|
-
if (
|
|
12706
|
-
extensionWs.send(JSON.stringify(frame));
|
|
12707
|
-
if (extensionHello)
|
|
13236
|
+
if (extensionHello && answersNoExtSession(frame.answersExtNonce)) {
|
|
12708
13237
|
ws.send(JSON.stringify(extensionHello));
|
|
13238
|
+
}
|
|
13239
|
+
if (extensionWs && extensionHello && frame.answersExtNonce === extensionHello.sessionNonce) {
|
|
13240
|
+
extensionWs.send(JSON.stringify(frame));
|
|
13241
|
+
}
|
|
12709
13242
|
return;
|
|
12710
13243
|
}
|
|
12711
13244
|
if (frame.type === "ready") {
|
|
@@ -12715,9 +13248,14 @@ async function startHost(opts) {
|
|
|
12715
13248
|
ws.close(1002, "ready before extension hello");
|
|
12716
13249
|
return;
|
|
12717
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
|
+
}
|
|
12718
13256
|
const extEdPub = fromB64(extensionHello.identityEd25519Pub);
|
|
12719
13257
|
const extNonce = fromB64(extensionHello.sessionNonce);
|
|
12720
|
-
const msg = readySignaturePayload(
|
|
13258
|
+
const msg = readySignaturePayload(held.nonce, extNonce, fromB64(frame.extensionSessionPub), held.pub);
|
|
12721
13259
|
const sig = fromB64(frame.sessionSig);
|
|
12722
13260
|
let sigOk = false;
|
|
12723
13261
|
try {
|
|
@@ -12743,8 +13281,9 @@ async function startHost(opts) {
|
|
|
12743
13281
|
}
|
|
12744
13282
|
}
|
|
12745
13283
|
const extPub = fromB64(frame.extensionSessionPub);
|
|
12746
|
-
const shared = await ecdhX25519(
|
|
12747
|
-
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);
|
|
12748
13287
|
if (extensionWs !== ws)
|
|
12749
13288
|
return;
|
|
12750
13289
|
ownSession = new SessionState(key);
|
|
@@ -12760,11 +13299,19 @@ async function startHost(opts) {
|
|
|
12760
13299
|
if (frame.type === "frame") {
|
|
12761
13300
|
if (identified === "extension") {
|
|
12762
13301
|
if (frame.mcpId === opts.ownMcpId) {
|
|
12763
|
-
|
|
13302
|
+
const session = ownSession;
|
|
13303
|
+
if (!session)
|
|
12764
13304
|
return;
|
|
12765
|
-
if (!
|
|
13305
|
+
if (!session.claimInboundSeq(frame.seq))
|
|
12766
13306
|
return;
|
|
12767
|
-
|
|
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);
|
|
12768
13315
|
ownInnerListeners.forEach((cb) => cb(inner));
|
|
12769
13316
|
} else {
|
|
12770
13317
|
const slot = peers.get(frame.mcpId);
|
|
@@ -12776,10 +13323,28 @@ async function startHost(opts) {
|
|
|
12776
13323
|
extensionWs.send(JSON.stringify(frame));
|
|
12777
13324
|
}
|
|
12778
13325
|
}
|
|
13326
|
+
if (frame.type === "hello-rejected" && identified === "extension") {
|
|
13327
|
+
if (frame.mcpId === opts.ownMcpId) {
|
|
13328
|
+
rejectOwnSession(new FetchproxyHelloRejectedError({ mcpId: frame.mcpId, reason: frame.reason }));
|
|
13329
|
+
} else {
|
|
13330
|
+
const slot = peers.get(frame.mcpId);
|
|
13331
|
+
if (slot?.helloFrame.accepts?.includes("hello-rejected")) {
|
|
13332
|
+
slot.ws.send(JSON.stringify(frame));
|
|
13333
|
+
}
|
|
13334
|
+
}
|
|
13335
|
+
}
|
|
12779
13336
|
if (frame.type === "pair-pending" && identified === "extension") {
|
|
12780
13337
|
if (frame.mcpId === opts.ownMcpId) {
|
|
12781
|
-
|
|
12782
|
-
|
|
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));
|
|
12783
13348
|
} else {
|
|
12784
13349
|
const slot = peers.get(frame.mcpId);
|
|
12785
13350
|
if (slot)
|
|
@@ -12796,6 +13361,7 @@ async function startHost(opts) {
|
|
|
12796
13361
|
});
|
|
12797
13362
|
ws.on("close", () => {
|
|
12798
13363
|
closed = true;
|
|
13364
|
+
clearTimeout(handshakeTimer);
|
|
12799
13365
|
if (extensionClaim === ws)
|
|
12800
13366
|
extensionClaim = null;
|
|
12801
13367
|
if (identified === "extension" && extensionWs === ws) {
|
|
@@ -12804,9 +13370,19 @@ async function startHost(opts) {
|
|
|
12804
13370
|
if (!ownSession) {
|
|
12805
13371
|
rejectOwnSession(new Error("extension disconnected before ready"));
|
|
12806
13372
|
}
|
|
12807
|
-
ownSession =
|
|
13373
|
+
ownSession = dropOwnSessionAndEphemeral();
|
|
13374
|
+
ownPendingPairCode = null;
|
|
12808
13375
|
resetSessionPromise();
|
|
12809
13376
|
disconnectListeners.forEach((cb) => cb());
|
|
13377
|
+
const notice = JSON.stringify({ type: "extension-disconnected" });
|
|
13378
|
+
for (const slot of peers.values()) {
|
|
13379
|
+
if (slot.helloFrame.accepts?.includes("extension-disconnected")) {
|
|
13380
|
+
try {
|
|
13381
|
+
slot.ws.send(notice);
|
|
13382
|
+
} catch {
|
|
13383
|
+
}
|
|
13384
|
+
}
|
|
13385
|
+
}
|
|
12810
13386
|
}
|
|
12811
13387
|
if (identified === "peer" && peerMcpId) {
|
|
12812
13388
|
if (peers.get(peerMcpId)?.ws === ws)
|
|
@@ -12833,7 +13409,8 @@ async function startHost(opts) {
|
|
|
12833
13409
|
});
|
|
12834
13410
|
if (!extensionWs)
|
|
12835
13411
|
throw new Error("host: no extension connected");
|
|
12836
|
-
const
|
|
13412
|
+
const plaintext = encodeOutboundInnerFrame(opts.ownMcpId, inner);
|
|
13413
|
+
const sealed = await sealInnerFrame(session.sessionKey, opts.ownMcpId, session.nextOutboundSeq(), plaintext, "s2e");
|
|
12837
13414
|
extensionWs.send(JSON.stringify(sealed));
|
|
12838
13415
|
},
|
|
12839
13416
|
onOwnInner: (cb) => {
|
|
@@ -12845,20 +13422,29 @@ async function startHost(opts) {
|
|
|
12845
13422
|
onPendingPair: (cb) => {
|
|
12846
13423
|
pendingPairListeners.push(cb);
|
|
12847
13424
|
},
|
|
12848
|
-
pendingPairCode: () => ownPendingPairCode
|
|
13425
|
+
pendingPairCode: () => ownPendingPairCode,
|
|
13426
|
+
extensionConnected: () => extensionWs !== null,
|
|
13427
|
+
sessionLinked: () => ownSession !== null
|
|
12849
13428
|
};
|
|
12850
13429
|
}
|
|
12851
|
-
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;
|
|
12852
13431
|
var init_host = __esm({
|
|
12853
13432
|
"node_modules/@fetchproxy/server/dist/host.js"() {
|
|
12854
13433
|
init_wrapper();
|
|
12855
13434
|
init_dist();
|
|
12856
13435
|
init_build_server_hello();
|
|
13436
|
+
init_frame_size();
|
|
12857
13437
|
init_session();
|
|
12858
13438
|
init_session_ready();
|
|
12859
13439
|
init_extension_trust();
|
|
12860
|
-
|
|
12861
|
-
|
|
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();
|
|
12862
13448
|
}
|
|
12863
13449
|
});
|
|
12864
13450
|
|
|
@@ -12866,10 +13452,26 @@ var init_host = __esm({
|
|
|
12866
13452
|
async function startPeer(opts) {
|
|
12867
13453
|
const ws = new import_websocket.default(`ws://${opts.host}:${opts.port}`);
|
|
12868
13454
|
await new Promise((resolve, reject) => {
|
|
12869
|
-
|
|
12870
|
-
|
|
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);
|
|
12871
13469
|
});
|
|
12872
|
-
|
|
13470
|
+
ws.on("error", (e) => {
|
|
13471
|
+
console.warn(`[fetchproxy] peer: socket error: ${String(e)}`);
|
|
13472
|
+
});
|
|
13473
|
+
const generateSessionKeypair = opts.generateSessionKeypair ?? generateX25519;
|
|
13474
|
+
const helloBase = {
|
|
12873
13475
|
identity: opts.identity,
|
|
12874
13476
|
mcpId: opts.mcpId,
|
|
12875
13477
|
serverName: opts.serverName,
|
|
@@ -12884,16 +13486,44 @@ async function startPeer(opts) {
|
|
|
12884
13486
|
domSelectors: opts.domSelectors,
|
|
12885
13487
|
localStoragePointers: opts.localStoragePointers,
|
|
12886
13488
|
sessionStoragePointers: opts.sessionStoragePointers,
|
|
12887
|
-
graphqlOps: opts.graphqlOps
|
|
13489
|
+
graphqlOps: opts.graphqlOps,
|
|
13490
|
+
// 2.5.0: let a host ≥2.5.0 tell us when the extension leaves, so
|
|
13491
|
+
// `extensionConnected()` / `sessionLinked()` can go back to false.
|
|
13492
|
+
// 2.6.0: `hello-rejected` for the same reason, from the extension.
|
|
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
|
|
12888
13504
|
});
|
|
12889
|
-
const sessionNonce = fromB64(hello.sessionNonce);
|
|
12890
13505
|
ws.send(JSON.stringify(hello));
|
|
12891
13506
|
const innerListeners = [];
|
|
12892
13507
|
const renegotiateListeners = [];
|
|
12893
13508
|
const pendingPairListeners = [];
|
|
12894
13509
|
const closeListeners = [];
|
|
12895
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
|
+
}
|
|
12896
13525
|
let pendingPairCode = null;
|
|
13526
|
+
let warnedUnverifiablePairCode = false;
|
|
12897
13527
|
let resolveFirstReady;
|
|
12898
13528
|
let rejectFirstReady;
|
|
12899
13529
|
const sessionPromise = new Promise((resolve, reject) => {
|
|
@@ -12901,58 +13531,61 @@ async function startPeer(opts) {
|
|
|
12901
13531
|
rejectFirstReady = reject;
|
|
12902
13532
|
});
|
|
12903
13533
|
let extensionHello = null;
|
|
12904
|
-
let
|
|
13534
|
+
let extensionGone = false;
|
|
12905
13535
|
let cachedPin = void 0;
|
|
12906
|
-
const
|
|
12907
|
-
if (!
|
|
12908
|
-
|
|
12909
|
-
|
|
12910
|
-
|
|
12911
|
-
|
|
12912
|
-
|
|
12913
|
-
|
|
12914
|
-
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.`);
|
|
12915
|
-
}
|
|
12916
|
-
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;
|
|
12917
13544
|
}
|
|
12918
|
-
|
|
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);
|
|
12919
13552
|
let sigOk = false;
|
|
12920
13553
|
try {
|
|
12921
|
-
sigOk = await ed25519Verify(fromB64(
|
|
13554
|
+
sigOk = await ed25519Verify(fromB64(hello2.identityEd25519Pub), payload, fromB64(sessionSig));
|
|
12922
13555
|
} catch {
|
|
12923
13556
|
sigOk = false;
|
|
12924
13557
|
}
|
|
12925
13558
|
if (!sigOk) {
|
|
12926
13559
|
console.warn(`[fetchproxy] ${opts.serverName}: extension session signature invalid \u2014 refusing (the concentrator may be answering in the browser's place)`);
|
|
12927
|
-
return
|
|
13560
|
+
return null;
|
|
12928
13561
|
}
|
|
12929
13562
|
if (cachedPin === void 0) {
|
|
12930
13563
|
try {
|
|
12931
13564
|
cachedPin = await opts.extensionTrust.read();
|
|
12932
13565
|
} catch (e) {
|
|
12933
13566
|
console.error(`[fetchproxy] ${String(e)}`);
|
|
12934
|
-
return
|
|
13567
|
+
return null;
|
|
12935
13568
|
}
|
|
12936
13569
|
}
|
|
12937
13570
|
const pin = cachedPin;
|
|
12938
13571
|
const outcome = decideExtensionTrust({
|
|
12939
13572
|
pin,
|
|
12940
|
-
hello:
|
|
13573
|
+
hello: hello2,
|
|
12941
13574
|
allowNew: opts.extensionTrust.allowNew,
|
|
12942
13575
|
serverName: opts.serverName,
|
|
12943
13576
|
location: opts.extensionTrust.location
|
|
12944
13577
|
});
|
|
12945
13578
|
if (outcome.decision === "refused") {
|
|
12946
13579
|
console.warn(outcome.message);
|
|
12947
|
-
return
|
|
13580
|
+
return null;
|
|
12948
13581
|
}
|
|
12949
13582
|
if (outcome.decision === "replace")
|
|
12950
13583
|
console.warn(outcome.message);
|
|
12951
13584
|
if (outcome.decision !== "pinned") {
|
|
12952
13585
|
try {
|
|
12953
13586
|
const written = {
|
|
12954
|
-
identityX25519Pub:
|
|
12955
|
-
identityEd25519Pub:
|
|
13587
|
+
identityX25519Pub: hello2.identityX25519Pub,
|
|
13588
|
+
identityEd25519Pub: hello2.identityEd25519Pub,
|
|
12956
13589
|
pinnedAt: Date.now()
|
|
12957
13590
|
};
|
|
12958
13591
|
await opts.extensionTrust.write(written);
|
|
@@ -12961,28 +13594,65 @@ async function startPeer(opts) {
|
|
|
12961
13594
|
console.error(`[fetchproxy] could not persist the extension pin: ${String(e)}`);
|
|
12962
13595
|
}
|
|
12963
13596
|
}
|
|
12964
|
-
return
|
|
13597
|
+
return hello2;
|
|
12965
13598
|
};
|
|
12966
13599
|
const onMessage = async (data) => {
|
|
13600
|
+
let raw;
|
|
12967
13601
|
try {
|
|
12968
|
-
|
|
13602
|
+
raw = JSON.parse(data.toString());
|
|
12969
13603
|
const frame = validateFrame(raw);
|
|
12970
13604
|
if (frame.type === "hello" && frame.role === "extension") {
|
|
12971
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));
|
|
13622
|
+
return;
|
|
13623
|
+
}
|
|
13624
|
+
if (frame.type === "extension-disconnected") {
|
|
13625
|
+
extensionHello = null;
|
|
13626
|
+
extensionGone = true;
|
|
13627
|
+
dropSessionEphemeral();
|
|
13628
|
+
pendingPairCode = null;
|
|
12972
13629
|
return;
|
|
12973
13630
|
}
|
|
12974
13631
|
if (frame.type === "ready" && frame.mcpId === opts.mcpId) {
|
|
12975
|
-
const
|
|
12976
|
-
|
|
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) {
|
|
12977
13640
|
ws.close(1008, "extension identity refused");
|
|
12978
13641
|
rejectFirstReady(new Error("peer: extension identity refused"));
|
|
12979
13642
|
return;
|
|
12980
13643
|
}
|
|
12981
13644
|
const extPub = fromB64(frame.extensionSessionPub);
|
|
12982
|
-
const
|
|
12983
|
-
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
|
+
}
|
|
12984
13653
|
const isRenegotiation = session !== null;
|
|
12985
13654
|
session = new SessionState(sessionKey);
|
|
13655
|
+
extensionGone = false;
|
|
12986
13656
|
pendingPairCode = null;
|
|
12987
13657
|
if (isRenegotiation) {
|
|
12988
13658
|
renegotiateListeners.forEach((cb) => cb());
|
|
@@ -12991,17 +13661,46 @@ async function startPeer(opts) {
|
|
|
12991
13661
|
}
|
|
12992
13662
|
return;
|
|
12993
13663
|
}
|
|
13664
|
+
if (frame.type === "hello-rejected" && frame.mcpId === opts.mcpId) {
|
|
13665
|
+
rejectFirstReady(new FetchproxyHelloRejectedError({ mcpId: frame.mcpId, reason: frame.reason }));
|
|
13666
|
+
}
|
|
12994
13667
|
if (frame.type === "pair-pending" && frame.mcpId === opts.mcpId) {
|
|
12995
|
-
|
|
12996
|
-
|
|
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));
|
|
12997
13685
|
return;
|
|
12998
13686
|
}
|
|
12999
13687
|
if (frame.type === "frame" && frame.mcpId === opts.mcpId) {
|
|
13000
|
-
|
|
13688
|
+
const inboundSession = session;
|
|
13689
|
+
if (!inboundSession)
|
|
13001
13690
|
return;
|
|
13002
|
-
if (!
|
|
13691
|
+
if (!inboundSession.claimInboundSeq(frame.seq))
|
|
13003
13692
|
return;
|
|
13004
|
-
|
|
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);
|
|
13005
13704
|
if (result.stage === "ok") {
|
|
13006
13705
|
innerListeners.forEach((cb) => cb(result.inner));
|
|
13007
13706
|
} else if (result.stage === "decrypt-failed") {
|
|
@@ -13018,12 +13717,25 @@ async function startPeer(opts) {
|
|
|
13018
13717
|
}
|
|
13019
13718
|
}
|
|
13020
13719
|
} catch (e) {
|
|
13021
|
-
|
|
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))));
|
|
13022
13729
|
}
|
|
13023
13730
|
};
|
|
13024
13731
|
ws.on("message", onMessage);
|
|
13025
13732
|
ws.once("close", () => {
|
|
13026
13733
|
rejectFirstReady(new Error("peer WS closed before ready"));
|
|
13734
|
+
dropSessionEphemeral();
|
|
13735
|
+
if (bootstrapPriv) {
|
|
13736
|
+
bootstrapPriv.fill(0);
|
|
13737
|
+
bootstrapPriv = null;
|
|
13738
|
+
}
|
|
13027
13739
|
closeListeners.forEach((cb) => cb());
|
|
13028
13740
|
});
|
|
13029
13741
|
sessionPromise.catch(() => {
|
|
@@ -13037,7 +13749,8 @@ async function startPeer(opts) {
|
|
|
13037
13749
|
pendingPairCode: () => pendingPairCode
|
|
13038
13750
|
});
|
|
13039
13751
|
const s = session;
|
|
13040
|
-
const
|
|
13752
|
+
const plaintext = encodeOutboundInnerFrame(opts.mcpId, inner);
|
|
13753
|
+
const sealed = await sealInnerFrame(s.sessionKey, opts.mcpId, s.nextOutboundSeq(), plaintext, "s2e");
|
|
13041
13754
|
ws.send(JSON.stringify(sealed));
|
|
13042
13755
|
},
|
|
13043
13756
|
onInner: (cb) => {
|
|
@@ -13050,6 +13763,8 @@ async function startPeer(opts) {
|
|
|
13050
13763
|
pendingPairListeners.push(cb);
|
|
13051
13764
|
},
|
|
13052
13765
|
pendingPairCode: () => pendingPairCode,
|
|
13766
|
+
extensionConnected: () => extensionHello !== null,
|
|
13767
|
+
sessionLinked: () => session !== null && !extensionGone,
|
|
13053
13768
|
onClose: (cb) => {
|
|
13054
13769
|
closeListeners.push(cb);
|
|
13055
13770
|
},
|
|
@@ -13057,16 +13772,17 @@ async function startPeer(opts) {
|
|
|
13057
13772
|
};
|
|
13058
13773
|
return handle;
|
|
13059
13774
|
}
|
|
13060
|
-
var
|
|
13775
|
+
var enc5;
|
|
13061
13776
|
var init_peer = __esm({
|
|
13062
13777
|
"node_modules/@fetchproxy/server/dist/peer.js"() {
|
|
13063
13778
|
init_wrapper();
|
|
13064
13779
|
init_dist();
|
|
13065
13780
|
init_build_server_hello();
|
|
13781
|
+
init_frame_size();
|
|
13066
13782
|
init_session();
|
|
13067
13783
|
init_session_ready();
|
|
13068
13784
|
init_extension_trust();
|
|
13069
|
-
|
|
13785
|
+
enc5 = new TextEncoder();
|
|
13070
13786
|
}
|
|
13071
13787
|
});
|
|
13072
13788
|
|
|
@@ -13078,7 +13794,7 @@ function classifyFetchError(error61) {
|
|
|
13078
13794
|
if (/^tab fetch failed:/.test(error61)) {
|
|
13079
13795
|
return "tab_fetch_failed";
|
|
13080
13796
|
}
|
|
13081
|
-
if (/^fetch threw:/.test(error61)) {
|
|
13797
|
+
if (/^(in-page )?fetch threw:/.test(error61)) {
|
|
13082
13798
|
return "tab_fetch_failed";
|
|
13083
13799
|
}
|
|
13084
13800
|
if (/^no tab matching /.test(error61)) {
|
|
@@ -13102,8 +13818,14 @@ var init_error_kind = __esm({
|
|
|
13102
13818
|
|
|
13103
13819
|
// node_modules/@fetchproxy/server/dist/classify-bridge-error.js
|
|
13104
13820
|
function classifyBridgeError(err) {
|
|
13821
|
+
if (err instanceof FetchproxyHelloRejectedError)
|
|
13822
|
+
return "hello_rejected";
|
|
13823
|
+
if (err instanceof FetchproxySessionNotReadyError)
|
|
13824
|
+
return "session_not_ready";
|
|
13105
13825
|
if (err instanceof FetchproxyTimeoutError)
|
|
13106
13826
|
return "timeout";
|
|
13827
|
+
if (err instanceof FetchproxyWaitedError)
|
|
13828
|
+
return "timeout";
|
|
13107
13829
|
if (err instanceof FetchproxyBridgeDownError)
|
|
13108
13830
|
return "bridge_down";
|
|
13109
13831
|
if (err instanceof FetchproxyHttpError)
|
|
@@ -13115,6 +13837,7 @@ function classifyBridgeError(err) {
|
|
|
13115
13837
|
var init_classify_bridge_error = __esm({
|
|
13116
13838
|
"node_modules/@fetchproxy/server/dist/classify-bridge-error.js"() {
|
|
13117
13839
|
init_ws_server();
|
|
13840
|
+
init_session_ready();
|
|
13118
13841
|
}
|
|
13119
13842
|
});
|
|
13120
13843
|
|
|
@@ -13140,13 +13863,32 @@ function envWsHost() {
|
|
|
13140
13863
|
return void 0;
|
|
13141
13864
|
return host;
|
|
13142
13865
|
}
|
|
13143
|
-
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);
|
|
13144
13879
|
if (SCOPE_REJECTION.test(error61))
|
|
13145
13880
|
return new FetchproxyScopeError(error61);
|
|
13881
|
+
if (TAB_OPENING_REJECTION.test(error61))
|
|
13882
|
+
return new FetchproxyTabOpeningError(error61);
|
|
13146
13883
|
if (NO_TAB_REJECTION.test(error61))
|
|
13147
13884
|
return new FetchproxyNoTabError(error61);
|
|
13148
13885
|
return new FetchproxyProtocolError(error61);
|
|
13149
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
|
+
}
|
|
13150
13892
|
function normalizeCookiePath(path) {
|
|
13151
13893
|
if (path === void 0 || path === "")
|
|
13152
13894
|
return void 0;
|
|
@@ -13182,7 +13924,7 @@ function assertUrlInDomains(field, url2, domains) {
|
|
|
13182
13924
|
const declared = domains.map((d) => JSON.stringify(d)).join(", ");
|
|
13183
13925
|
throw new Error(`FetchproxyServer: ${field} host "${host}" is outside declared domains [${declared}] \u2014 must be one of them or a subdomain`);
|
|
13184
13926
|
}
|
|
13185
|
-
var FetchproxyProtocolError, FetchproxyHttpError, FetchproxyBridgeDownError, FetchproxyHintedError, FetchproxyScopeError, FetchproxyNoTabError, SCOPE_REJECTION, NO_TAB_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;
|
|
13186
13928
|
var init_ws_server = __esm({
|
|
13187
13929
|
"node_modules/@fetchproxy/server/dist/ws-server.js"() {
|
|
13188
13930
|
init_dist();
|
|
@@ -13258,8 +14000,22 @@ var init_ws_server = __esm({
|
|
|
13258
14000
|
this.name = "FetchproxyNoTabError";
|
|
13259
14001
|
}
|
|
13260
14002
|
};
|
|
14003
|
+
FetchproxyTabOpeningError = class extends FetchproxyHintedError {
|
|
14004
|
+
constructor(originalError) {
|
|
14005
|
+
super(originalError, "the extension is opening that tab now \u2014 retry in a few seconds. You do not need to open one yourself, and this is not a version problem.");
|
|
14006
|
+
this.name = "FetchproxyTabOpeningError";
|
|
14007
|
+
}
|
|
14008
|
+
};
|
|
14009
|
+
FetchproxyWaitedError = class extends FetchproxyHintedError {
|
|
14010
|
+
constructor(originalError, op) {
|
|
14011
|
+
super(originalError, waitedHint(op));
|
|
14012
|
+
this.name = "FetchproxyWaitedError";
|
|
14013
|
+
}
|
|
14014
|
+
};
|
|
13261
14015
|
SCOPE_REJECTION = /not in declared/;
|
|
13262
|
-
NO_TAB_REJECTION = /no tab matching (?!.*content script loaded)/;
|
|
14016
|
+
NO_TAB_REJECTION = /no tab matching (?!.*(content script loaded|one is still opening))/;
|
|
14017
|
+
TAB_OPENING_REJECTION = /no tab matching .*one is still opening/;
|
|
14018
|
+
VERB_DEADLINE_GRACE_MS = 15e3;
|
|
13263
14019
|
FetchproxyTimeoutError = class extends FetchproxyProtocolError {
|
|
13264
14020
|
url;
|
|
13265
14021
|
timeoutMs;
|
|
@@ -13277,8 +14033,21 @@ var init_ws_server = __esm({
|
|
|
13277
14033
|
* the first attempt.
|
|
13278
14034
|
*/
|
|
13279
14035
|
retryAttempted;
|
|
14036
|
+
/**
|
|
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.
|
|
14045
|
+
*/
|
|
14046
|
+
requestedTimeoutMs;
|
|
13280
14047
|
constructor(args) {
|
|
13281
|
-
|
|
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.` : ""));
|
|
13282
14051
|
this.name = "FetchproxyTimeoutError";
|
|
13283
14052
|
this.url = args.url;
|
|
13284
14053
|
this.timeoutMs = args.timeoutMs;
|
|
@@ -13286,6 +14055,7 @@ var init_ws_server = __esm({
|
|
|
13286
14055
|
this.port = args.port ?? 0;
|
|
13287
14056
|
this.elapsedMs = args.elapsedMs ?? args.timeoutMs;
|
|
13288
14057
|
this.retryAttempted = args.retryAttempted ?? false;
|
|
14058
|
+
this.requestedTimeoutMs = args.requestedTimeoutMs ?? null;
|
|
13289
14059
|
}
|
|
13290
14060
|
};
|
|
13291
14061
|
SUBDOMAIN_LABEL_RE = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
|
|
@@ -13375,6 +14145,11 @@ var init_ws_server = __esm({
|
|
|
13375
14145
|
if (!Array.isArray(opts.domains) || opts.domains.length === 0) {
|
|
13376
14146
|
throw new Error("FetchproxyServer: opts.domains must be a non-empty array of hostnames");
|
|
13377
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
|
+
}
|
|
13378
14153
|
let capabilities;
|
|
13379
14154
|
if (opts.capabilities === void 0) {
|
|
13380
14155
|
capabilities = ["fetch"];
|
|
@@ -13440,6 +14215,7 @@ var init_ws_server = __esm({
|
|
|
13440
14215
|
// back-door is `0` (explicit opt-out) if a caller genuinely wants
|
|
13441
14216
|
// the legacy hang-forever / fail-once-on-SW-eviction behavior.
|
|
13442
14217
|
fetchTimeoutMs: opts.fetchTimeoutMs ?? 3e4,
|
|
14218
|
+
verbDeadlineGraceMs: opts.verbDeadlineGraceMs ?? VERB_DEADLINE_GRACE_MS,
|
|
13443
14219
|
bridgeReviveDelayMs: opts.bridgeReviveDelayMs ?? 2e3,
|
|
13444
14220
|
// 0.10.0+ (#72): keep-alive defaults to 25s — round-3 #71 cohort
|
|
13445
14221
|
// wave showed every Pattern A consumer was opting into this same
|
|
@@ -13458,6 +14234,7 @@ var init_ws_server = __esm({
|
|
|
13458
14234
|
keepAliveIntervalMs: opts.keepAliveIntervalMs ?? 2e4,
|
|
13459
14235
|
keepAliveMaxIdleMs: opts.keepAliveMaxIdleMs ?? 5 * 60 * 1e3,
|
|
13460
14236
|
identityDir: opts.identityDir,
|
|
14237
|
+
trustDir: opts.trustDir,
|
|
13461
14238
|
allowNewExtensionIdentity: opts.allowNewExtensionIdentity,
|
|
13462
14239
|
requireExtensionIdentity: opts.requireExtensionIdentity,
|
|
13463
14240
|
onPairCode: opts.onPairCode
|
|
@@ -13694,6 +14471,7 @@ var init_ws_server = __esm({
|
|
|
13694
14471
|
lastFailureReason: this.lastFailureReason,
|
|
13695
14472
|
consecutiveFailures: this.consecutiveFailures,
|
|
13696
14473
|
lastExtensionMessageAt: this.lastExtensionMessageAt,
|
|
14474
|
+
session: this.sessionSnapshot(),
|
|
13697
14475
|
keepAlive: {
|
|
13698
14476
|
enabled: intervalMs > 0,
|
|
13699
14477
|
intervalMs,
|
|
@@ -13733,7 +14511,9 @@ var init_ws_server = __esm({
|
|
|
13733
14511
|
}
|
|
13734
14512
|
/**
|
|
13735
14513
|
* #208: this MCP's pin on the extension's identity, stored beside its own
|
|
13736
|
-
* 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.
|
|
13737
14517
|
*
|
|
13738
14518
|
* `allowNewExtensionIdentity` falls back to an environment variable when the
|
|
13739
14519
|
* caller expressed no opinion, because the thirteen MCPs that construct this
|
|
@@ -13745,6 +14525,7 @@ var init_ws_server = __esm({
|
|
|
13745
14525
|
return fileExtensionTrust({
|
|
13746
14526
|
serverName: this.opts.serverName,
|
|
13747
14527
|
dir: this.opts.identityDir,
|
|
14528
|
+
trustDir: this.opts.trustDir,
|
|
13748
14529
|
allowNew: allowNewExtensionIdentity(this.opts.allowNewExtensionIdentity)
|
|
13749
14530
|
});
|
|
13750
14531
|
}
|
|
@@ -13829,9 +14610,9 @@ var init_ws_server = __esm({
|
|
|
13829
14610
|
async _fetchOnceWithTimeout(init) {
|
|
13830
14611
|
const id = this.nextRequestId++;
|
|
13831
14612
|
const inner = { type: "request", id, op: "fetch", init };
|
|
13832
|
-
const pending = new Promise((resolve) => {
|
|
14613
|
+
const pending = this.guardPending(new Promise((resolve) => {
|
|
13833
14614
|
this.pending.set(id, resolve);
|
|
13834
|
-
});
|
|
14615
|
+
}));
|
|
13835
14616
|
await this.sendInnerFrame(inner);
|
|
13836
14617
|
const timeoutMs = this.opts.fetchTimeoutMs;
|
|
13837
14618
|
if (timeoutMs === void 0 || timeoutMs <= 0)
|
|
@@ -13878,10 +14659,40 @@ var init_ws_server = __esm({
|
|
|
13878
14659
|
* already surface for fetch timeouts). `0`/unset opts out (unbounded),
|
|
13879
14660
|
* matching the fetch path.
|
|
13880
14661
|
*/
|
|
13881
|
-
|
|
13882
|
-
|
|
13883
|
-
|
|
14662
|
+
/**
|
|
14663
|
+
* Register a verb's pending promise and keep an early rejection from being
|
|
14664
|
+
* fatal.
|
|
14665
|
+
*
|
|
14666
|
+
* Every verb stores its resolver, then AWAITS `sendInnerFrame` before the
|
|
14667
|
+
* caller attaches a handler — and that send does real work (sealing, a
|
|
14668
|
+
* socket write), so it yields across event-loop turns. Anything that drains
|
|
14669
|
+
* the pending maps inside that window rejects a promise nothing is
|
|
14670
|
+
* listening to yet: a refused pairing (`rejectAllPending(pairingErrorMessage
|
|
14671
|
+
* (code))`), an extension disconnect, a `close()`. Under Node's default
|
|
14672
|
+
* `--unhandled-rejections=throw` that ends the PROCESS, so an MCP that
|
|
14673
|
+
* should have reported "approve pair code NNN-NN" dies instead, and each
|
|
14674
|
+
* respawn mints a fresh code nobody can approve (#329).
|
|
14675
|
+
*
|
|
14676
|
+
* The no-op catch is a listener, not a swallow. `.catch()` derives a NEW
|
|
14677
|
+
* promise and leaves this one's rejection intact, so `_withVerbTimeout` and
|
|
14678
|
+
* the caller still see it — the only thing that changes is that the
|
|
14679
|
+
* rejection is never unhandled.
|
|
14680
|
+
*
|
|
14681
|
+
* This is the same hazard `_withVerbTimeout`'s timer already guards against
|
|
14682
|
+
* from the other side, where it drops the resolver "so a late bridge
|
|
14683
|
+
* response doesn't become an unhandled promise that crashes the host".
|
|
14684
|
+
*/
|
|
14685
|
+
guardPending(pending) {
|
|
14686
|
+
pending.catch(() => {
|
|
14687
|
+
});
|
|
14688
|
+
return pending;
|
|
14689
|
+
}
|
|
14690
|
+
async _withVerbTimeout(pending, pendingMap, id, url2, requestedTimeoutMs) {
|
|
14691
|
+
const transportMs = this.opts.fetchTimeoutMs;
|
|
14692
|
+
if (transportMs === void 0 || transportMs <= 0)
|
|
13884
14693
|
return pending;
|
|
14694
|
+
const graceMs = this.opts.verbDeadlineGraceMs;
|
|
14695
|
+
const timeoutMs = verbDeadlineMs(transportMs, requestedTimeoutMs, graceMs);
|
|
13885
14696
|
let timer;
|
|
13886
14697
|
const start = Date.now();
|
|
13887
14698
|
try {
|
|
@@ -13896,7 +14707,9 @@ var init_ws_server = __esm({
|
|
|
13896
14707
|
role: this.role,
|
|
13897
14708
|
port: this.opts.port,
|
|
13898
14709
|
elapsedMs: Date.now() - start,
|
|
13899
|
-
retryAttempted: false
|
|
14710
|
+
retryAttempted: false,
|
|
14711
|
+
graceMs,
|
|
14712
|
+
...requestedTimeoutMs !== void 0 ? { requestedTimeoutMs } : {}
|
|
13900
14713
|
}));
|
|
13901
14714
|
}, timeoutMs);
|
|
13902
14715
|
})
|
|
@@ -13956,9 +14769,9 @@ var init_ws_server = __esm({
|
|
|
13956
14769
|
if (opts.subdomain !== void 0)
|
|
13957
14770
|
assertSubdomainLabel(opts.subdomain);
|
|
13958
14771
|
const baseDomain = this.resolveBaseDomain(opts.domain);
|
|
13959
|
-
const
|
|
14772
|
+
const isAbsolute3 = path.startsWith("http://") || path.startsWith("https://");
|
|
13960
14773
|
let host;
|
|
13961
|
-
if (
|
|
14774
|
+
if (isAbsolute3) {
|
|
13962
14775
|
try {
|
|
13963
14776
|
host = new URL(path).host;
|
|
13964
14777
|
} catch {
|
|
@@ -13967,7 +14780,7 @@ var init_ws_server = __esm({
|
|
|
13967
14780
|
} else {
|
|
13968
14781
|
host = opts.subdomain ? `${opts.subdomain}.${baseDomain}` : baseDomain;
|
|
13969
14782
|
}
|
|
13970
|
-
const url2 =
|
|
14783
|
+
const url2 = isAbsolute3 ? path : `https://${host}${path}`;
|
|
13971
14784
|
assertUrlInDomains("request url", url2, this.opts.domains);
|
|
13972
14785
|
let tabUrl = `https://${host}/`;
|
|
13973
14786
|
if (opts.viaTab !== void 0) {
|
|
@@ -13984,7 +14797,15 @@ var init_ws_server = __esm({
|
|
|
13984
14797
|
method,
|
|
13985
14798
|
tabUrl,
|
|
13986
14799
|
headers: opts.headers,
|
|
13987
|
-
body: opts.body
|
|
14800
|
+
body: opts.body,
|
|
14801
|
+
// Spread rather than `inPage: opts.inPage`: the wire's validator treats
|
|
14802
|
+
// the key's PRESENCE as the request, and a literal `undefined` would
|
|
14803
|
+
// both serialize away inconsistently and read as "asked for" here.
|
|
14804
|
+
...opts.inPage === true ? { inPage: true } : {},
|
|
14805
|
+
// Spread for the reason `inPage` is: only a deliberate 'omit' goes on
|
|
14806
|
+
// the wire, so an untouched request serialises byte-identically to
|
|
14807
|
+
// every version before this option existed.
|
|
14808
|
+
...opts.credentials === "omit" ? { credentials: "omit" } : {}
|
|
13988
14809
|
};
|
|
13989
14810
|
const result = await this.fetch(init);
|
|
13990
14811
|
if (!result.ok) {
|
|
@@ -14158,7 +14979,11 @@ var init_ws_server = __esm({
|
|
|
14158
14979
|
last_success_at: health.lastSuccessAt,
|
|
14159
14980
|
last_failure_at: health.lastFailureAt,
|
|
14160
14981
|
last_failure_reason: health.lastFailureReason,
|
|
14161
|
-
consecutive_failures: health.consecutiveFailures
|
|
14982
|
+
consecutive_failures: health.consecutiveFailures,
|
|
14983
|
+
session_state: health.session.state,
|
|
14984
|
+
pending_pair_code: health.session.pairCode,
|
|
14985
|
+
extension_connected: health.session.extensionConnected,
|
|
14986
|
+
last_extension_message_at: health.lastExtensionMessageAt
|
|
14162
14987
|
},
|
|
14163
14988
|
...error61 ? { error: error61 } : {}
|
|
14164
14989
|
};
|
|
@@ -14208,9 +15033,9 @@ var init_ws_server = __esm({
|
|
|
14208
15033
|
const tabUrl = `https://${host}/`;
|
|
14209
15034
|
inner = { type: "request", id, op: "read_cookies", init: { tabUrl } };
|
|
14210
15035
|
}
|
|
14211
|
-
const pending = new Promise((resolve) => {
|
|
15036
|
+
const pending = this.guardPending(new Promise((resolve) => {
|
|
14212
15037
|
this.pendingReadCookies.set(id, resolve);
|
|
14213
|
-
});
|
|
15038
|
+
}));
|
|
14214
15039
|
await this.sendInnerFrame(inner);
|
|
14215
15040
|
const result = await this._withVerbTimeout(pending, this.pendingReadCookies, id, `https://${host}`);
|
|
14216
15041
|
if (!result.ok) {
|
|
@@ -14267,9 +15092,9 @@ var init_ws_server = __esm({
|
|
|
14267
15092
|
...cookiePath !== void 0 ? { path: cookiePath } : {}
|
|
14268
15093
|
}
|
|
14269
15094
|
};
|
|
14270
|
-
const pending = new Promise((resolve, reject) => {
|
|
15095
|
+
const pending = this.guardPending(new Promise((resolve, reject) => {
|
|
14271
15096
|
this.pendingWriteCookies.set(id, { resolve, reject });
|
|
14272
|
-
});
|
|
15097
|
+
}));
|
|
14273
15098
|
await this.sendInnerFrame(inner);
|
|
14274
15099
|
return this._withVerbTimeout(pending, this.pendingWriteCookies, id, `https://${host}`);
|
|
14275
15100
|
}
|
|
@@ -14330,9 +15155,9 @@ var init_ws_server = __esm({
|
|
|
14330
15155
|
...opts.pointers ? { pointers: { ...opts.pointers } } : {}
|
|
14331
15156
|
}
|
|
14332
15157
|
};
|
|
14333
|
-
const pending = new Promise((resolve, reject) => {
|
|
15158
|
+
const pending = this.guardPending(new Promise((resolve, reject) => {
|
|
14334
15159
|
this.pendingStorage.set(id, { resolve, reject });
|
|
14335
|
-
});
|
|
15160
|
+
}));
|
|
14336
15161
|
await this.sendInnerFrame(inner);
|
|
14337
15162
|
return this._withVerbTimeout(pending, this.pendingStorage, id, `https://${host}`);
|
|
14338
15163
|
}
|
|
@@ -14435,11 +15260,20 @@ var init_ws_server = __esm({
|
|
|
14435
15260
|
...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {}
|
|
14436
15261
|
}
|
|
14437
15262
|
};
|
|
14438
|
-
const pending = new Promise((resolve, reject) => {
|
|
15263
|
+
const pending = this.guardPending(new Promise((resolve, reject) => {
|
|
14439
15264
|
this.pendingCapture.set(id, { resolve, reject });
|
|
14440
|
-
});
|
|
15265
|
+
}));
|
|
14441
15266
|
await this.sendInnerFrame(inner);
|
|
14442
|
-
return this._withVerbTimeout(
|
|
15267
|
+
return this._withVerbTimeout(
|
|
15268
|
+
pending,
|
|
15269
|
+
this.pendingCapture,
|
|
15270
|
+
id,
|
|
15271
|
+
`https://${opts.host}${opts.path ?? "/*"}`,
|
|
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.
|
|
15275
|
+
opts.timeoutMs
|
|
15276
|
+
);
|
|
14443
15277
|
}
|
|
14444
15278
|
/**
|
|
14445
15279
|
* Snapshot the redirect target URL of the next request the browser
|
|
@@ -14520,11 +15354,20 @@ var init_ws_server = __esm({
|
|
|
14520
15354
|
...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {}
|
|
14521
15355
|
}
|
|
14522
15356
|
};
|
|
14523
|
-
const pending = new Promise((resolve, reject) => {
|
|
15357
|
+
const pending = this.guardPending(new Promise((resolve, reject) => {
|
|
14524
15358
|
this.pendingRedirect.set(id, { resolve, reject });
|
|
14525
|
-
});
|
|
15359
|
+
}));
|
|
14526
15360
|
await this.sendInnerFrame(inner);
|
|
14527
|
-
return this._withVerbTimeout(
|
|
15361
|
+
return this._withVerbTimeout(
|
|
15362
|
+
pending,
|
|
15363
|
+
this.pendingRedirect,
|
|
15364
|
+
id,
|
|
15365
|
+
`https://${opts.host}${opts.path ?? "/*"}`,
|
|
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.
|
|
15369
|
+
opts.timeoutMs
|
|
15370
|
+
);
|
|
14528
15371
|
}
|
|
14529
15372
|
/**
|
|
14530
15373
|
* Download `url` through the BROWSER's own network stack via
|
|
@@ -14602,11 +15445,22 @@ var init_ws_server = __esm({
|
|
|
14602
15445
|
...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {}
|
|
14603
15446
|
}
|
|
14604
15447
|
};
|
|
14605
|
-
const pending = new Promise((resolve, reject) => {
|
|
15448
|
+
const pending = this.guardPending(new Promise((resolve, reject) => {
|
|
14606
15449
|
this.pendingDownload.set(id, { resolve, reject });
|
|
14607
|
-
});
|
|
15450
|
+
}));
|
|
14608
15451
|
await this.sendInnerFrame(inner);
|
|
14609
|
-
return this._withVerbTimeout(
|
|
15452
|
+
return this._withVerbTimeout(
|
|
15453
|
+
pending,
|
|
15454
|
+
this.pendingDownload,
|
|
15455
|
+
id,
|
|
15456
|
+
opts.url,
|
|
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.
|
|
15462
|
+
opts.timeoutMs
|
|
15463
|
+
);
|
|
14610
15464
|
}
|
|
14611
15465
|
/**
|
|
14612
15466
|
* 0.4.0+: read declared IndexedDB keys from the user's signed-in
|
|
@@ -14650,9 +15504,9 @@ var init_ws_server = __esm({
|
|
|
14650
15504
|
keys: [...opts.keys]
|
|
14651
15505
|
}
|
|
14652
15506
|
};
|
|
14653
|
-
const pending = new Promise((resolve, reject) => {
|
|
15507
|
+
const pending = this.guardPending(new Promise((resolve, reject) => {
|
|
14654
15508
|
this.pendingIdb.set(id, { resolve, reject });
|
|
14655
|
-
});
|
|
15509
|
+
}));
|
|
14656
15510
|
await this.sendInnerFrame(inner);
|
|
14657
15511
|
return this._withVerbTimeout(pending, this.pendingIdb, id, origin);
|
|
14658
15512
|
}
|
|
@@ -14690,9 +15544,9 @@ var init_ws_server = __esm({
|
|
|
14690
15544
|
op: "read_dom",
|
|
14691
15545
|
init: { origin, names: [...opts.names] }
|
|
14692
15546
|
};
|
|
14693
|
-
const pending = new Promise((resolve, reject) => {
|
|
15547
|
+
const pending = this.guardPending(new Promise((resolve, reject) => {
|
|
14694
15548
|
this.pendingStorage.set(id, { resolve, reject });
|
|
14695
|
-
});
|
|
15549
|
+
}));
|
|
14696
15550
|
await this.sendInnerFrame(inner);
|
|
14697
15551
|
return this._withVerbTimeout(pending, this.pendingStorage, id, origin);
|
|
14698
15552
|
}
|
|
@@ -14736,9 +15590,9 @@ var init_ws_server = __esm({
|
|
|
14736
15590
|
...opts.tabUrl !== void 0 ? { tabUrl: opts.tabUrl } : {}
|
|
14737
15591
|
}
|
|
14738
15592
|
};
|
|
14739
|
-
const pending = new Promise((resolve, reject) => {
|
|
15593
|
+
const pending = this.guardPending(new Promise((resolve, reject) => {
|
|
14740
15594
|
this.pendingGraphql.set(id, { resolve, reject });
|
|
14741
|
-
});
|
|
15595
|
+
}));
|
|
14742
15596
|
await this.sendInnerFrame(inner);
|
|
14743
15597
|
return this._withVerbTimeout(pending, this.pendingGraphql, id, opts.name);
|
|
14744
15598
|
}
|
|
@@ -14833,7 +15687,7 @@ var init_ws_server = __esm({
|
|
|
14833
15687
|
captureCb.reject(new FetchproxyProtocolError(`unexpected ${String(inner.op)} response on capture awaiter`));
|
|
14834
15688
|
}
|
|
14835
15689
|
} else {
|
|
14836
|
-
captureCb.reject(protocolErrorFrom(inner.error));
|
|
15690
|
+
captureCb.reject(protocolErrorFrom(inner.error, "capture"));
|
|
14837
15691
|
}
|
|
14838
15692
|
return;
|
|
14839
15693
|
}
|
|
@@ -14847,7 +15701,7 @@ var init_ws_server = __esm({
|
|
|
14847
15701
|
redirectCb.reject(new FetchproxyProtocolError(`unexpected ${String(inner.op)} response on capture_redirect awaiter`));
|
|
14848
15702
|
}
|
|
14849
15703
|
} else {
|
|
14850
|
-
redirectCb.reject(protocolErrorFrom(inner.error));
|
|
15704
|
+
redirectCb.reject(protocolErrorFrom(inner.error, "capture_redirect"));
|
|
14851
15705
|
}
|
|
14852
15706
|
return;
|
|
14853
15707
|
}
|
|
@@ -14875,7 +15729,7 @@ var init_ws_server = __esm({
|
|
|
14875
15729
|
downloadCb.reject(new FetchproxyProtocolError(`unexpected ${String(inner.op)} response on download awaiter`));
|
|
14876
15730
|
}
|
|
14877
15731
|
} else {
|
|
14878
|
-
downloadCb.reject(protocolErrorFrom(inner.error));
|
|
15732
|
+
downloadCb.reject(protocolErrorFrom(inner.error, "download"));
|
|
14879
15733
|
}
|
|
14880
15734
|
return;
|
|
14881
15735
|
}
|
|
@@ -14975,6 +15829,35 @@ var init_ws_server = __esm({
|
|
|
14975
15829
|
return this.peerHandle.pendingPairCode();
|
|
14976
15830
|
return null;
|
|
14977
15831
|
}
|
|
15832
|
+
/**
|
|
15833
|
+
* 2.5.0: the extension link as {@link BridgeSessionState}, read off
|
|
15834
|
+
* whichever handle is live. A pending pair code outranks "extension not
|
|
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
|
|
15837
|
+
* socket closed, a peer told by a 2.5.0+ host) the handle has already
|
|
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.
|
|
15850
|
+
*/
|
|
15851
|
+
sessionSnapshot() {
|
|
15852
|
+
const handle = this.hostHandle ?? this.peerHandle;
|
|
15853
|
+
const pairCode = this.currentPendingPairCode();
|
|
15854
|
+
if (!handle || this.role === null) {
|
|
15855
|
+
return { state: "not_listening", pairCode, extensionConnected: false };
|
|
15856
|
+
}
|
|
15857
|
+
const extensionConnected = handle.extensionConnected();
|
|
15858
|
+
const state = handle.sessionLinked() ? "linked" : pairCode !== null ? "pair_pending" : !extensionConnected ? "extension_disconnected" : "no_session";
|
|
15859
|
+
return { state, pairCode, extensionConnected };
|
|
15860
|
+
}
|
|
14978
15861
|
/**
|
|
14979
15862
|
* 0.5.2+: throw `FetchproxyProtocolError` with the actionable pair-code
|
|
14980
15863
|
* message if the bridge is waiting on user approval. Used by the verb
|
|
@@ -15346,6 +16229,10 @@ function credentialHint(arm, prefix, hostLabel, source) {
|
|
|
15346
16229
|
return `No credential resolved. Nothing was available to authenticate with \u2014 sign in and reconnect the connector so ${prefix} receives a token, or set the documented environment variable.`;
|
|
15347
16230
|
case "credential_rejected":
|
|
15348
16231
|
return `${hostLabel} rejected the credential from '${source}'. It is present but no longer valid \u2014 most often expired or revoked upstream. Re-authenticate and reconnect; retrying will not fix it.`;
|
|
16232
|
+
case "session_expired":
|
|
16233
|
+
return `The credential from '${source}' is configured, but no session is live \u2014 ${hostLabel} served a sign-in page rather than the data. Sign in again; a cookie-session portal expires these on its own, so this recurs between uses.`;
|
|
16234
|
+
case "verification_pending":
|
|
16235
|
+
return `${hostLabel} is holding the sign-in on a second factor rather than refusing it. Supply the verification code the ACCOUNT HOLDER received \u2014 the credential itself is not the problem, so changing it will not help.`;
|
|
15349
16236
|
case "timeout":
|
|
15350
16237
|
return `The credential from '${source}' resolved, but ${hostLabel} did not answer in time. Usually transient \u2014 retry. If it persists, ${hostLabel} is slow or unreachable from here.`;
|
|
15351
16238
|
case "http":
|
|
@@ -15356,12 +16243,102 @@ function credentialHint(arm, prefix, hostLabel, source) {
|
|
|
15356
16243
|
return `Unexpected failure \u2014 see error.message.`;
|
|
15357
16244
|
}
|
|
15358
16245
|
}
|
|
16246
|
+
function credentialHealthcheckDescription(hostLabel) {
|
|
16247
|
+
return `Resolves the credential the way real tools do, then makes one authenticated request to ${hostLabel}. Reports which source supplied the credential, whether ${hostLabel} accepted it, the round-trip time, and a plain-English hint distinguishing 'no credential' from 'credential rejected' from 'a ${hostLabel}-side problem'. Read-only; never returns the credential itself.`;
|
|
16248
|
+
}
|
|
16249
|
+
async function runCredentialHealthcheck(args) {
|
|
16250
|
+
const { prefix, hostLabel, probePath, resolveCredential, probeFn, classifyThrown, hints } = args;
|
|
16251
|
+
const probeUrl = probePath ? `https://${hostLabel}${probePath.startsWith("/") ? "" : "/"}${probePath}` : void 0;
|
|
16252
|
+
let probeStarted = 0;
|
|
16253
|
+
let state;
|
|
16254
|
+
try {
|
|
16255
|
+
state = await resolveCredential();
|
|
16256
|
+
} catch (e) {
|
|
16257
|
+
const classified = classifyThrown?.(e);
|
|
16258
|
+
const result2 = {
|
|
16259
|
+
ok: false,
|
|
16260
|
+
// Still false, and still no source: a classification explains WHY
|
|
16261
|
+
// nothing resolved, it does not invent a credential that did.
|
|
16262
|
+
credential: { source: null, resolved: false },
|
|
16263
|
+
// No `url`: nothing was probed, and naming one implies it was tried.
|
|
16264
|
+
probe: { elapsed_ms: 0 },
|
|
16265
|
+
error: {
|
|
16266
|
+
kind: classified?.kind ?? "no_credential",
|
|
16267
|
+
message: truncateErrorMessage(messageOf(e)),
|
|
16268
|
+
...classified?.detail !== void 0 ? { detail: classified.detail } : {}
|
|
16269
|
+
},
|
|
16270
|
+
// The hint must follow the KIND beside it. Falling back to
|
|
16271
|
+
// `no_credential`'s copy under a classified kind would state a cause
|
|
16272
|
+
// the kind contradicts — the same disagreement this path exists to
|
|
16273
|
+
// remove. So: an inline hint wins; else the classified arm's own
|
|
16274
|
+
// copy (consumer override first); else, for a kind this module has
|
|
16275
|
+
// no copy for, the neutral `unknown` text rather than one that
|
|
16276
|
+
// asserts a cause; else the unclassified `no_credential` default.
|
|
16277
|
+
hint: classified?.hint ?? (isArm(classified?.kind) ? hints?.[classified.kind] ?? credentialHint(classified.kind, prefix, hostLabel, null) : classified !== void 0 ? credentialHint("unknown", prefix, hostLabel, null) : hints?.no_credential ?? credentialHint("no_credential", prefix, hostLabel, null))
|
|
16278
|
+
};
|
|
16279
|
+
return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
|
|
16280
|
+
}
|
|
16281
|
+
const credential = {
|
|
16282
|
+
source: state.source,
|
|
16283
|
+
resolved: state.source !== null,
|
|
16284
|
+
...state.detail !== void 0 ? { detail: state.detail } : {}
|
|
16285
|
+
};
|
|
16286
|
+
if (!credential.resolved) {
|
|
16287
|
+
const result2 = {
|
|
16288
|
+
ok: false,
|
|
16289
|
+
credential,
|
|
16290
|
+
probe: { elapsed_ms: 0 },
|
|
16291
|
+
error: { kind: "no_credential", message: "no credential source resolved" },
|
|
16292
|
+
hint: hints?.no_credential ?? credentialHint("no_credential", prefix, hostLabel, null)
|
|
16293
|
+
};
|
|
16294
|
+
return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
|
|
16295
|
+
}
|
|
16296
|
+
let arm = "ok";
|
|
16297
|
+
let error61;
|
|
16298
|
+
let status;
|
|
16299
|
+
let customHint;
|
|
16300
|
+
probeStarted = Date.now();
|
|
16301
|
+
try {
|
|
16302
|
+
await probeFn();
|
|
16303
|
+
} catch (e) {
|
|
16304
|
+
status = statusOf(e);
|
|
16305
|
+
const aborted2 = e instanceof Error && e.name === "AbortError";
|
|
16306
|
+
arm = status === 401 || status === 403 ? "credential_rejected" : status !== void 0 ? "http" : aborted2 || /timeout|timed out|ETIMEDOUT/i.test(messageOf(e)) ? "timeout" : /fetch failed|ENOTFOUND|ECONNREFUSED|ECONNRESET|network/i.test(messageOf(e)) ? "transport" : "unknown";
|
|
16307
|
+
let kind = arm;
|
|
16308
|
+
let detail;
|
|
16309
|
+
const custom2 = classifyThrown?.(e);
|
|
16310
|
+
if (custom2) {
|
|
16311
|
+
kind = custom2.kind;
|
|
16312
|
+
customHint = custom2.hint;
|
|
16313
|
+
detail = custom2.detail;
|
|
16314
|
+
arm = isArm(kind) ? kind : "unknown";
|
|
16315
|
+
}
|
|
16316
|
+
error61 = {
|
|
16317
|
+
kind,
|
|
16318
|
+
// Redacted AND bounded before it reaches the result: an upstream
|
|
16319
|
+
// failure routinely quotes what it was sent, and a healthcheck is
|
|
16320
|
+
// the tool people paste into a chat when something is broken.
|
|
16321
|
+
message: truncateErrorMessage(messageOf(e)),
|
|
16322
|
+
...detail !== void 0 ? { detail } : {}
|
|
16323
|
+
};
|
|
16324
|
+
}
|
|
16325
|
+
const result = {
|
|
16326
|
+
ok: error61 === void 0,
|
|
16327
|
+
credential,
|
|
16328
|
+
probe: {
|
|
16329
|
+
...probeUrl ? { url: probeUrl } : {},
|
|
16330
|
+
elapsed_ms: Date.now() - probeStarted,
|
|
16331
|
+
...status !== void 0 ? { status } : {}
|
|
16332
|
+
},
|
|
16333
|
+
...error61 ? { error: error61 } : {},
|
|
16334
|
+
hint: customHint ?? hints?.[arm] ?? credentialHint(arm, prefix, hostLabel, state.source)
|
|
16335
|
+
};
|
|
16336
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
16337
|
+
}
|
|
15359
16338
|
function registerCredentialHealthcheckTool(args) {
|
|
15360
|
-
|
|
15361
|
-
const probeUrl = probePath ? `https://${hostLabel}${probePath}` : void 0;
|
|
15362
|
-
server.registerTool(`${prefix}_healthcheck`, {
|
|
16339
|
+
args.server.registerTool(`${args.prefix}_healthcheck`, {
|
|
15363
16340
|
title: "Verify credentials and upstream reachability",
|
|
15364
|
-
description:
|
|
16341
|
+
description: `${credentialHealthcheckDescription(args.hostLabel)} Call this when a real tool fails and you want to know which hop broke.`,
|
|
15365
16342
|
annotations: {
|
|
15366
16343
|
title: "Verify credentials and upstream reachability",
|
|
15367
16344
|
readOnlyHint: true,
|
|
@@ -15369,92 +16346,7 @@ function registerCredentialHealthcheckTool(args) {
|
|
|
15369
16346
|
openWorldHint: true
|
|
15370
16347
|
},
|
|
15371
16348
|
inputSchema: {}
|
|
15372
|
-
}, async () =>
|
|
15373
|
-
let probeStarted = 0;
|
|
15374
|
-
let state;
|
|
15375
|
-
try {
|
|
15376
|
-
state = await resolveCredential();
|
|
15377
|
-
} catch (e) {
|
|
15378
|
-
const classified = classifyThrown?.(e);
|
|
15379
|
-
const result2 = {
|
|
15380
|
-
ok: false,
|
|
15381
|
-
// Still false, and still no source: a classification explains WHY
|
|
15382
|
-
// nothing resolved, it does not invent a credential that did.
|
|
15383
|
-
credential: { source: null, resolved: false },
|
|
15384
|
-
// No `url`: nothing was probed, and naming one implies it was tried.
|
|
15385
|
-
probe: { elapsed_ms: 0 },
|
|
15386
|
-
error: {
|
|
15387
|
-
kind: classified?.kind ?? "no_credential",
|
|
15388
|
-
message: truncateErrorMessage(messageOf(e)),
|
|
15389
|
-
...classified?.detail !== void 0 ? { detail: classified.detail } : {}
|
|
15390
|
-
},
|
|
15391
|
-
// The hint must follow the KIND beside it. Falling back to
|
|
15392
|
-
// `no_credential`'s copy under a classified kind would state a cause
|
|
15393
|
-
// the kind contradicts — the same disagreement this path exists to
|
|
15394
|
-
// remove. So: an inline hint wins; else the classified arm's own
|
|
15395
|
-
// copy (consumer override first); else, for a kind this module has
|
|
15396
|
-
// no copy for, the neutral `unknown` text rather than one that
|
|
15397
|
-
// asserts a cause; else the unclassified `no_credential` default.
|
|
15398
|
-
hint: classified?.hint ?? (isArm(classified?.kind) ? hints?.[classified.kind] ?? credentialHint(classified.kind, prefix, hostLabel, null) : classified !== void 0 ? credentialHint("unknown", prefix, hostLabel, null) : hints?.no_credential ?? credentialHint("no_credential", prefix, hostLabel, null))
|
|
15399
|
-
};
|
|
15400
|
-
return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
|
|
15401
|
-
}
|
|
15402
|
-
const credential = {
|
|
15403
|
-
source: state.source,
|
|
15404
|
-
resolved: state.source !== null,
|
|
15405
|
-
...state.detail !== void 0 ? { detail: state.detail } : {}
|
|
15406
|
-
};
|
|
15407
|
-
if (!credential.resolved) {
|
|
15408
|
-
const result2 = {
|
|
15409
|
-
ok: false,
|
|
15410
|
-
credential,
|
|
15411
|
-
probe: { elapsed_ms: 0 },
|
|
15412
|
-
error: { kind: "no_credential", message: "no credential source resolved" },
|
|
15413
|
-
hint: hints?.no_credential ?? credentialHint("no_credential", prefix, hostLabel, null)
|
|
15414
|
-
};
|
|
15415
|
-
return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
|
|
15416
|
-
}
|
|
15417
|
-
let arm = "ok";
|
|
15418
|
-
let error61;
|
|
15419
|
-
let status;
|
|
15420
|
-
let customHint;
|
|
15421
|
-
probeStarted = Date.now();
|
|
15422
|
-
try {
|
|
15423
|
-
await probeFn();
|
|
15424
|
-
} catch (e) {
|
|
15425
|
-
status = statusOf(e);
|
|
15426
|
-
const aborted2 = e instanceof Error && e.name === "AbortError";
|
|
15427
|
-
arm = status === 401 || status === 403 ? "credential_rejected" : status !== void 0 ? "http" : aborted2 || /timeout|timed out|ETIMEDOUT/i.test(messageOf(e)) ? "timeout" : /fetch failed|ENOTFOUND|ECONNREFUSED|ECONNRESET|network/i.test(messageOf(e)) ? "transport" : "unknown";
|
|
15428
|
-
let kind = arm;
|
|
15429
|
-
let detail;
|
|
15430
|
-
const custom2 = classifyThrown?.(e);
|
|
15431
|
-
if (custom2) {
|
|
15432
|
-
kind = custom2.kind;
|
|
15433
|
-
customHint = custom2.hint;
|
|
15434
|
-
detail = custom2.detail;
|
|
15435
|
-
}
|
|
15436
|
-
error61 = {
|
|
15437
|
-
kind,
|
|
15438
|
-
// Redacted AND bounded before it reaches the result: an upstream
|
|
15439
|
-
// failure routinely quotes what it was sent, and a healthcheck is
|
|
15440
|
-
// the tool people paste into a chat when something is broken.
|
|
15441
|
-
message: truncateErrorMessage(messageOf(e)),
|
|
15442
|
-
...detail !== void 0 ? { detail } : {}
|
|
15443
|
-
};
|
|
15444
|
-
}
|
|
15445
|
-
const result = {
|
|
15446
|
-
ok: error61 === void 0,
|
|
15447
|
-
credential,
|
|
15448
|
-
probe: {
|
|
15449
|
-
...probeUrl ? { url: probeUrl } : {},
|
|
15450
|
-
elapsed_ms: Date.now() - probeStarted,
|
|
15451
|
-
...status !== void 0 ? { status } : {}
|
|
15452
|
-
},
|
|
15453
|
-
...error61 ? { error: error61 } : {},
|
|
15454
|
-
hint: customHint ?? hints?.[arm] ?? credentialHint(arm, prefix, hostLabel, state.source)
|
|
15455
|
-
};
|
|
15456
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
15457
|
-
});
|
|
16349
|
+
}, async () => runCredentialHealthcheck(args));
|
|
15458
16350
|
}
|
|
15459
16351
|
var CREDENTIAL_ARMS;
|
|
15460
16352
|
var init_healthcheck = __esm({
|
|
@@ -15464,6 +16356,8 @@ var init_healthcheck = __esm({
|
|
|
15464
16356
|
"ok",
|
|
15465
16357
|
"no_credential",
|
|
15466
16358
|
"credential_rejected",
|
|
16359
|
+
"session_expired",
|
|
16360
|
+
"verification_pending",
|
|
15467
16361
|
"timeout",
|
|
15468
16362
|
"http",
|
|
15469
16363
|
"transport",
|
|
@@ -15485,6 +16379,7 @@ __export(fetchproxy_exports, {
|
|
|
15485
16379
|
TokenBucket: () => TokenBucket,
|
|
15486
16380
|
backoffDelayMs: () => backoffDelayMs,
|
|
15487
16381
|
bridgeErrorInfo: () => bridgeErrorInfo,
|
|
16382
|
+
bridgeHealthcheckDescription: () => bridgeHealthcheckDescription,
|
|
15488
16383
|
chunk: () => chunk,
|
|
15489
16384
|
classifyBotWall: () => classifyBotWall,
|
|
15490
16385
|
classifyBridgeError: () => classifyBridgeError,
|
|
@@ -15497,9 +16392,11 @@ __export(fetchproxy_exports, {
|
|
|
15497
16392
|
extractImgTags: () => extractImgTags,
|
|
15498
16393
|
lastPathSegment: () => lastPathSegment,
|
|
15499
16394
|
mapWithConcurrency: () => mapWithConcurrency,
|
|
16395
|
+
registerAdaptiveHealthcheckTool: () => registerAdaptiveHealthcheckTool,
|
|
15500
16396
|
registerBridgeHealthcheckTool: () => registerBridgeHealthcheckTool,
|
|
15501
16397
|
registerCredentialHealthcheckTool: () => registerCredentialHealthcheckTool,
|
|
15502
16398
|
retryOnceOnTimeout: () => retryOnceOnTimeout,
|
|
16399
|
+
runBridgeHealthcheck: () => runBridgeHealthcheck,
|
|
15503
16400
|
sleep: () => sleep,
|
|
15504
16401
|
withDeadline: () => withDeadline
|
|
15505
16402
|
});
|
|
@@ -15535,9 +16432,9 @@ function createFetchproxyTransport(opts) {
|
|
|
15535
16432
|
async start() {
|
|
15536
16433
|
await server.listen();
|
|
15537
16434
|
if (logListening) {
|
|
15538
|
-
console.error(`[${serverOpts.serverName}:bridge]
|
|
16435
|
+
console.error(`[${serverOpts.serverName}:bridge] ready \u2014 127.0.0.1:${server.bridgeHealth().port} binds on the first request (version=${serverOpts.version})`);
|
|
15539
16436
|
} else if (debug) {
|
|
15540
|
-
console.error(`[${serverOpts.serverName}:bridge]
|
|
16437
|
+
console.error(`[${serverOpts.serverName}:bridge] ready \u2014 binds on the first request (version=${serverOpts.version})`);
|
|
15541
16438
|
}
|
|
15542
16439
|
},
|
|
15543
16440
|
async close() {
|
|
@@ -15716,13 +16613,122 @@ function projectBridgeStatus(health) {
|
|
|
15716
16613
|
} : {}
|
|
15717
16614
|
};
|
|
15718
16615
|
}
|
|
15719
|
-
function
|
|
15720
|
-
|
|
15721
|
-
|
|
16616
|
+
function probeDisplayUrl(hostLabel, probePath) {
|
|
16617
|
+
return `https://${hostLabel}${probePath.startsWith("/") ? "" : "/"}${probePath}`;
|
|
16618
|
+
}
|
|
16619
|
+
function bridgeHealthcheckDescription(hostLabel, probePath) {
|
|
16620
|
+
return `Round-trips a small public ${hostLabel} URL (${probePath}) through the fetchproxy bridge and returns diagnostics: the bridge's role (host/peer/null), port, version, the extension link (linked / pair pending / not attached / never answered), the elapsed round-trip time, and a plain-English hint distinguishing 'bridge never came up' from 'extension not connected' from 'real ${hostLabel}-side problem'. Read-only, no auth required.`;
|
|
16621
|
+
}
|
|
16622
|
+
async function runBridgeHealthcheck(args) {
|
|
16623
|
+
const { prefix, probePath, hostLabel, probeFn, classifyThrown, hints, path } = args;
|
|
16624
|
+
const probeUrl = probeDisplayUrl(hostLabel, probePath);
|
|
15722
16625
|
const resolveTransport = () => typeof args.transport === "function" ? args.transport() : args.transport;
|
|
15723
|
-
|
|
16626
|
+
let probeBody = "";
|
|
16627
|
+
let thrown;
|
|
16628
|
+
const wrappedProbe = async (p) => {
|
|
16629
|
+
try {
|
|
16630
|
+
probeBody = await probeFn(p);
|
|
16631
|
+
return probeBody;
|
|
16632
|
+
} catch (e) {
|
|
16633
|
+
thrown = e;
|
|
16634
|
+
throw e;
|
|
16635
|
+
}
|
|
16636
|
+
};
|
|
16637
|
+
let ok;
|
|
16638
|
+
let elapsedMs;
|
|
16639
|
+
let bridge;
|
|
16640
|
+
let rawError;
|
|
16641
|
+
let pathNow;
|
|
16642
|
+
if (path) {
|
|
16643
|
+
const start = Date.now();
|
|
16644
|
+
try {
|
|
16645
|
+
await wrappedProbe(probePath);
|
|
16646
|
+
ok = true;
|
|
16647
|
+
} catch (e) {
|
|
16648
|
+
ok = false;
|
|
16649
|
+
rawError = { kind: classifyBridgeError(e), message: truncateErrorMessage(messageOf(e)) };
|
|
16650
|
+
}
|
|
16651
|
+
elapsedMs = Date.now() - start;
|
|
16652
|
+
pathNow = path();
|
|
16653
|
+
const transport2 = resolveTransport();
|
|
16654
|
+
bridge = transport2 ? projectBridgeStatus(transport2.status()) : void 0;
|
|
16655
|
+
} else {
|
|
16656
|
+
const transport2 = resolveTransport();
|
|
16657
|
+
if (!transport2) {
|
|
16658
|
+
throw new Error("bridge healthcheck: transport() returned nothing and no `path` was supplied \u2014 a bridge-only healthcheck needs its bridge.");
|
|
16659
|
+
}
|
|
16660
|
+
const probeResult = await transport2.runProbe(wrappedProbe, probePath);
|
|
16661
|
+
ok = probeResult.ok;
|
|
16662
|
+
elapsedMs = probeResult.elapsed_ms;
|
|
16663
|
+
bridge = {
|
|
16664
|
+
...probeResult.bridge,
|
|
16665
|
+
last_extension_message_at: transport2.status().lastExtensionMessageAt
|
|
16666
|
+
};
|
|
16667
|
+
rawError = probeResult.error;
|
|
16668
|
+
}
|
|
16669
|
+
const probe = ok ? { url: probeUrl, elapsed_ms: elapsedMs, status: 200, body_length: probeBody.length } : { url: probeUrl, elapsed_ms: elapsedMs };
|
|
16670
|
+
let error61;
|
|
16671
|
+
let bridgeHint;
|
|
16672
|
+
let customHint;
|
|
16673
|
+
let customDetail;
|
|
16674
|
+
if (rawError) {
|
|
16675
|
+
let kind = rawError.kind === "other" ? "unknown" : rawError.kind;
|
|
16676
|
+
if (thrown instanceof FetchproxySessionNotReadyError) {
|
|
16677
|
+
kind = "session_not_ready";
|
|
16678
|
+
bridgeHint = thrown.hint;
|
|
16679
|
+
} else if (thrown instanceof FetchproxyBridgeDownError) {
|
|
16680
|
+
bridgeHint = thrown.hint;
|
|
16681
|
+
}
|
|
16682
|
+
if (thrown !== void 0 && classifyThrown) {
|
|
16683
|
+
const custom2 = classifyThrown(thrown);
|
|
16684
|
+
if (custom2) {
|
|
16685
|
+
kind = custom2.kind;
|
|
16686
|
+
customHint = custom2.hint;
|
|
16687
|
+
customDetail = custom2.detail;
|
|
16688
|
+
}
|
|
16689
|
+
}
|
|
16690
|
+
error61 = {
|
|
16691
|
+
kind,
|
|
16692
|
+
message: rawError.message,
|
|
16693
|
+
...bridgeHint !== void 0 ? { bridge_hint: bridgeHint } : {},
|
|
16694
|
+
...customDetail !== void 0 ? { detail: customDetail } : {}
|
|
16695
|
+
};
|
|
16696
|
+
}
|
|
16697
|
+
const direct = pathNow ? pathNow.transport === "direct" : bridge === void 0;
|
|
16698
|
+
const arm = ok ? "ok" : error61?.kind === "session_not_ready" ? "session_not_ready" : error61?.kind === "bridge_down" ? "bridge_down" : direct ? "direct" : bridge === void 0 || bridge.role === null ? "no_role" : error61?.kind === "timeout" ? "timeout" : error61?.kind === "protocol" || error61?.kind === "http" ? "protocol" : "unknown";
|
|
16699
|
+
const defaultHint = healthcheckHint({
|
|
16700
|
+
ok,
|
|
16701
|
+
role: bridge?.role ?? null,
|
|
16702
|
+
// The real configured port from bridgeHealth(), never a literal 37149.
|
|
16703
|
+
port: bridge?.port ?? null,
|
|
16704
|
+
hostLabel,
|
|
16705
|
+
prefix,
|
|
16706
|
+
probePath,
|
|
16707
|
+
errorKind: error61?.kind,
|
|
16708
|
+
bridgeHint: error61?.kind === "bridge_down" ? bridgeHint : void 0,
|
|
16709
|
+
session: {
|
|
16710
|
+
...bridge?.session_state !== void 0 ? { state: bridge.session_state } : {},
|
|
16711
|
+
pairCode: bridge?.pending_pair_code ?? (thrown instanceof FetchproxySessionNotReadyError ? thrown.pairCode : null)
|
|
16712
|
+
},
|
|
16713
|
+
direct
|
|
16714
|
+
});
|
|
16715
|
+
const result = {
|
|
16716
|
+
ok,
|
|
16717
|
+
...bridge ? { bridge } : {},
|
|
16718
|
+
...pathNow ? { transport: pathNow } : {},
|
|
16719
|
+
probe,
|
|
16720
|
+
...error61 ? { error: error61 } : {},
|
|
16721
|
+
// Precedence: classifyThrown's hint > per-arm override > default ladder.
|
|
16722
|
+
hint: customHint ?? hints?.[arm] ?? defaultHint
|
|
16723
|
+
};
|
|
16724
|
+
return {
|
|
16725
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
16726
|
+
};
|
|
16727
|
+
}
|
|
16728
|
+
function registerBridgeHealthcheckTool(args) {
|
|
16729
|
+
args.server.registerTool(`${args.prefix}_healthcheck`, {
|
|
15724
16730
|
title: "Verify the fetchproxy bridge end-to-end",
|
|
15725
|
-
description:
|
|
16731
|
+
description: `${bridgeHealthcheckDescription(args.hostLabel, args.probePath)} Call this when a real tool fails and you want to know which hop broke.`,
|
|
15726
16732
|
annotations: {
|
|
15727
16733
|
title: "Verify the fetchproxy bridge end-to-end",
|
|
15728
16734
|
readOnlyHint: true,
|
|
@@ -15730,109 +16736,21 @@ function registerBridgeHealthcheckTool(args) {
|
|
|
15730
16736
|
openWorldHint: true
|
|
15731
16737
|
},
|
|
15732
16738
|
inputSchema: {}
|
|
15733
|
-
}, async () =>
|
|
15734
|
-
|
|
15735
|
-
|
|
15736
|
-
|
|
15737
|
-
|
|
15738
|
-
|
|
15739
|
-
|
|
15740
|
-
|
|
15741
|
-
|
|
15742
|
-
|
|
15743
|
-
|
|
15744
|
-
|
|
15745
|
-
|
|
15746
|
-
|
|
15747
|
-
|
|
15748
|
-
let rawError;
|
|
15749
|
-
let pathNow;
|
|
15750
|
-
if (path) {
|
|
15751
|
-
const start = Date.now();
|
|
15752
|
-
try {
|
|
15753
|
-
await wrappedProbe(probePath);
|
|
15754
|
-
ok = true;
|
|
15755
|
-
} catch (e) {
|
|
15756
|
-
ok = false;
|
|
15757
|
-
rawError = { kind: classifyBridgeError(e), message: truncateErrorMessage(messageOf(e)) };
|
|
15758
|
-
}
|
|
15759
|
-
elapsedMs = Date.now() - start;
|
|
15760
|
-
pathNow = path();
|
|
15761
|
-
const transport2 = resolveTransport();
|
|
15762
|
-
bridge = transport2 ? projectBridgeStatus(transport2.status()) : void 0;
|
|
15763
|
-
} else {
|
|
15764
|
-
const transport2 = resolveTransport();
|
|
15765
|
-
if (!transport2) {
|
|
15766
|
-
throw new Error("registerBridgeHealthcheckTool: transport() returned nothing and no `path` was supplied \u2014 a bridge-only healthcheck needs its bridge.");
|
|
15767
|
-
}
|
|
15768
|
-
const probeResult = await transport2.runProbe(wrappedProbe, probePath);
|
|
15769
|
-
ok = probeResult.ok;
|
|
15770
|
-
elapsedMs = probeResult.elapsed_ms;
|
|
15771
|
-
bridge = {
|
|
15772
|
-
...probeResult.bridge,
|
|
15773
|
-
last_extension_message_at: transport2.status().lastExtensionMessageAt
|
|
15774
|
-
};
|
|
15775
|
-
rawError = probeResult.error;
|
|
15776
|
-
}
|
|
15777
|
-
const probe = ok ? { url: probeUrl, elapsed_ms: elapsedMs, status: 200, body_length: probeBody.length } : { url: probeUrl, elapsed_ms: elapsedMs };
|
|
15778
|
-
let error61;
|
|
15779
|
-
let bridgeHint;
|
|
15780
|
-
let customHint;
|
|
15781
|
-
let customDetail;
|
|
15782
|
-
if (rawError) {
|
|
15783
|
-
let kind = rawError.kind === "other" ? "unknown" : rawError.kind;
|
|
15784
|
-
if (thrown instanceof FetchproxySessionNotReadyError) {
|
|
15785
|
-
kind = "session_not_ready";
|
|
15786
|
-
bridgeHint = thrown.hint;
|
|
15787
|
-
} else if (thrown instanceof FetchproxyBridgeDownError) {
|
|
15788
|
-
bridgeHint = thrown.hint;
|
|
15789
|
-
}
|
|
15790
|
-
if (thrown !== void 0 && classifyThrown) {
|
|
15791
|
-
const custom2 = classifyThrown(thrown);
|
|
15792
|
-
if (custom2) {
|
|
15793
|
-
kind = custom2.kind;
|
|
15794
|
-
customHint = custom2.hint;
|
|
15795
|
-
customDetail = custom2.detail;
|
|
15796
|
-
}
|
|
15797
|
-
}
|
|
15798
|
-
error61 = {
|
|
15799
|
-
kind,
|
|
15800
|
-
message: rawError.message,
|
|
15801
|
-
...bridgeHint !== void 0 ? { bridge_hint: bridgeHint } : {},
|
|
15802
|
-
...customDetail !== void 0 ? { detail: customDetail } : {}
|
|
15803
|
-
};
|
|
15804
|
-
}
|
|
15805
|
-
const direct = pathNow ? pathNow.transport === "direct" : bridge === void 0;
|
|
15806
|
-
const arm = ok ? "ok" : error61?.kind === "session_not_ready" ? "session_not_ready" : error61?.kind === "bridge_down" ? "bridge_down" : direct ? "direct" : bridge === void 0 || bridge.role === null ? "no_role" : error61?.kind === "timeout" ? "timeout" : error61?.kind === "protocol" || error61?.kind === "http" ? "protocol" : "unknown";
|
|
15807
|
-
const defaultHint = healthcheckHint({
|
|
15808
|
-
ok,
|
|
15809
|
-
role: bridge?.role ?? null,
|
|
15810
|
-
// The real configured port from bridgeHealth(), never a literal 37149.
|
|
15811
|
-
port: bridge?.port ?? null,
|
|
15812
|
-
hostLabel,
|
|
15813
|
-
prefix,
|
|
15814
|
-
probePath,
|
|
15815
|
-
errorKind: error61?.kind,
|
|
15816
|
-
bridgeHint: error61?.kind === "bridge_down" ? bridgeHint : void 0,
|
|
15817
|
-
session: {
|
|
15818
|
-
...bridge?.session_state !== void 0 ? { state: bridge.session_state } : {},
|
|
15819
|
-
pairCode: bridge?.pending_pair_code ?? (thrown instanceof FetchproxySessionNotReadyError ? thrown.pairCode : null)
|
|
15820
|
-
},
|
|
15821
|
-
direct
|
|
15822
|
-
});
|
|
15823
|
-
const result = {
|
|
15824
|
-
ok,
|
|
15825
|
-
...bridge ? { bridge } : {},
|
|
15826
|
-
...pathNow ? { transport: pathNow } : {},
|
|
15827
|
-
probe,
|
|
15828
|
-
...error61 ? { error: error61 } : {},
|
|
15829
|
-
// Precedence: classifyThrown's hint > per-arm override > default ladder.
|
|
15830
|
-
hint: customHint ?? hints?.[arm] ?? defaultHint
|
|
15831
|
-
};
|
|
15832
|
-
return {
|
|
15833
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
15834
|
-
};
|
|
15835
|
-
});
|
|
16739
|
+
}, async () => runBridgeHealthcheck(args));
|
|
16740
|
+
}
|
|
16741
|
+
function registerAdaptiveHealthcheckTool(args) {
|
|
16742
|
+
const { server, prefix, hostLabel, usingBridge, bridge, credential } = args;
|
|
16743
|
+
server.registerTool(`${prefix}_healthcheck`, {
|
|
16744
|
+
title: "Verify this server can reach its upstream",
|
|
16745
|
+
description: `Reports which hop is broken when a real tool fails, for whichever path this server is actually using. Relaying through your signed-in browser tab: ${bridgeHealthcheckDescription(hostLabel, bridge.probePath)} Signing in server-side with configured credentials: ${credentialHealthcheckDescription(hostLabel)}`,
|
|
16746
|
+
annotations: {
|
|
16747
|
+
title: "Verify this server can reach its upstream",
|
|
16748
|
+
readOnlyHint: true,
|
|
16749
|
+
idempotentHint: true,
|
|
16750
|
+
openWorldHint: true
|
|
16751
|
+
},
|
|
16752
|
+
inputSchema: {}
|
|
16753
|
+
}, async () => usingBridge() ? runBridgeHealthcheck({ server, prefix, hostLabel, ...bridge }) : runCredentialHealthcheck({ server, prefix, hostLabel, ...credential }));
|
|
15836
16754
|
}
|
|
15837
16755
|
var PLACEHOLDER_RE2;
|
|
15838
16756
|
var init_fetchproxy = __esm({
|
|
@@ -15841,6 +16759,7 @@ var init_fetchproxy = __esm({
|
|
|
15841
16759
|
init_errors();
|
|
15842
16760
|
init_dist2();
|
|
15843
16761
|
init_healthcheck();
|
|
16762
|
+
init_healthcheck();
|
|
15844
16763
|
PLACEHOLDER_RE2 = /^\$\{[^}]*\}$/;
|
|
15845
16764
|
}
|
|
15846
16765
|
});
|
|
@@ -22542,12 +23461,12 @@ var $ZodE164 = /* @__PURE__ */ $constructor("$ZodE164", (inst, def) => {
|
|
|
22542
23461
|
$ZodStringFormat.init(inst, def);
|
|
22543
23462
|
});
|
|
22544
23463
|
var CC_SANITIZE = /[- ]/g;
|
|
22545
|
-
function isLuhnAlgo(
|
|
22546
|
-
let length =
|
|
23464
|
+
function isLuhnAlgo(digits2) {
|
|
23465
|
+
let length = digits2.length;
|
|
22547
23466
|
let bit = 1;
|
|
22548
23467
|
let sum = 0;
|
|
22549
23468
|
while (length) {
|
|
22550
|
-
const value = +
|
|
23469
|
+
const value = +digits2[--length];
|
|
22551
23470
|
bit ^= 1;
|
|
22552
23471
|
sum += bit ? [0, 2, 4, 6, 8, 1, 3, 5, 7, 9][value] : value;
|
|
22553
23472
|
}
|
|
@@ -37279,8 +38198,8 @@ function hex2(_params) {
|
|
|
37279
38198
|
return _stringFormat(ZodCustomStringFormat, "hex", regexes_exports.hex, _params);
|
|
37280
38199
|
}
|
|
37281
38200
|
function hash(alg, params) {
|
|
37282
|
-
const
|
|
37283
|
-
const format = `${alg}_${
|
|
38201
|
+
const enc7 = params?.enc ?? "hex";
|
|
38202
|
+
const format = `${alg}_${enc7}`;
|
|
37284
38203
|
const regex = regexes_exports[format];
|
|
37285
38204
|
if (!regex)
|
|
37286
38205
|
throw new Error(`Unrecognized hash format: ${format}`);
|
|
@@ -44754,7 +45673,7 @@ var pageSchema = {
|
|
|
44754
45673
|
init_errors();
|
|
44755
45674
|
|
|
44756
45675
|
// src/version.ts
|
|
44757
|
-
var VERSION = "0.3.
|
|
45676
|
+
var VERSION = "0.3.2";
|
|
44758
45677
|
|
|
44759
45678
|
// src/client.ts
|
|
44760
45679
|
import { dirname, join } from "path";
|
|
@@ -45313,7 +46232,7 @@ var schemaEventStatus = external_exports.enum(["all", "live", "draft", "started"
|
|
|
45313
46232
|
function qs(params) {
|
|
45314
46233
|
return params.size > 0 ? `?${params}` : "";
|
|
45315
46234
|
}
|
|
45316
|
-
function
|
|
46235
|
+
function enc6(id) {
|
|
45317
46236
|
if (/^\.+$/.test(id)) {
|
|
45318
46237
|
throw new McpToolError(`Invalid id '${id}'.`, {
|
|
45319
46238
|
hint: "Ids must be Eventbrite object ids (normally digits), not path segments."
|
|
@@ -45395,7 +46314,7 @@ function registerAccountTools(server, deps) {
|
|
|
45395
46314
|
if (continuation) params.set("continuation", continuation);
|
|
45396
46315
|
const data = await client2.request(
|
|
45397
46316
|
"GET",
|
|
45398
|
-
`/organizations/${
|
|
46317
|
+
`/organizations/${enc6(org_id)}/events/${qs(params)}`
|
|
45399
46318
|
);
|
|
45400
46319
|
return viewResponse(view, data);
|
|
45401
46320
|
}
|
|
@@ -45418,7 +46337,7 @@ function registerAccountTools(server, deps) {
|
|
|
45418
46337
|
if (continuation) params.set("continuation", continuation);
|
|
45419
46338
|
const data = await client2.request(
|
|
45420
46339
|
"GET",
|
|
45421
|
-
`/organizations/${
|
|
46340
|
+
`/organizations/${enc6(org_id)}/attendees/${qs(params)}`
|
|
45422
46341
|
);
|
|
45423
46342
|
return viewResponse(view, data);
|
|
45424
46343
|
}
|
|
@@ -45439,7 +46358,7 @@ function registerAccountTools(server, deps) {
|
|
|
45439
46358
|
if (continuation) params.set("continuation", continuation);
|
|
45440
46359
|
const data = await client2.request(
|
|
45441
46360
|
"GET",
|
|
45442
|
-
`/organizations/${
|
|
46361
|
+
`/organizations/${enc6(org_id)}/orders/${qs(params)}`
|
|
45443
46362
|
);
|
|
45444
46363
|
return viewResponse(view, data);
|
|
45445
46364
|
}
|
|
@@ -45479,7 +46398,7 @@ function registerAccountTools(server, deps) {
|
|
|
45479
46398
|
if (continuation) params.set("continuation", continuation);
|
|
45480
46399
|
return viewResponse(
|
|
45481
46400
|
view,
|
|
45482
|
-
await client2.request("GET", `/organizations/${
|
|
46401
|
+
await client2.request("GET", `/organizations/${enc6(org_id)}/${noun}/${qs(params)}`)
|
|
45483
46402
|
);
|
|
45484
46403
|
}
|
|
45485
46404
|
);
|
|
@@ -45509,7 +46428,7 @@ function registerAccountTools(server, deps) {
|
|
|
45509
46428
|
if (continuation) params.set("continuation", continuation);
|
|
45510
46429
|
return viewResponse(
|
|
45511
46430
|
view,
|
|
45512
|
-
await client2.request("GET", `/organizations/${
|
|
46431
|
+
await client2.request("GET", `/organizations/${enc6(org_id)}/reports/${kind}/${qs(params)}`)
|
|
45513
46432
|
);
|
|
45514
46433
|
}
|
|
45515
46434
|
);
|
|
@@ -45613,7 +46532,7 @@ function registerEventTools(server, deps) {
|
|
|
45613
46532
|
if (continuation) params.set("continuation", continuation);
|
|
45614
46533
|
return viewResponse(
|
|
45615
46534
|
view,
|
|
45616
|
-
await client2.request("GET", `/events/${
|
|
46535
|
+
await client2.request("GET", `/events/${enc6(event_id)}/attendees/${qs(params)}`)
|
|
45617
46536
|
);
|
|
45618
46537
|
}
|
|
45619
46538
|
);
|
|
@@ -45630,7 +46549,7 @@ function registerEventTools(server, deps) {
|
|
|
45630
46549
|
},
|
|
45631
46550
|
async ({ event_id, attendee_id, view }) => viewResponse(
|
|
45632
46551
|
view,
|
|
45633
|
-
await client2.request("GET", `/events/${
|
|
46552
|
+
await client2.request("GET", `/events/${enc6(event_id)}/attendees/${enc6(attendee_id)}/`)
|
|
45634
46553
|
)
|
|
45635
46554
|
);
|
|
45636
46555
|
server.registerTool(
|
|
@@ -45653,7 +46572,7 @@ function registerEventTools(server, deps) {
|
|
|
45653
46572
|
if (continuation) params.set("continuation", continuation);
|
|
45654
46573
|
return viewResponse(
|
|
45655
46574
|
view,
|
|
45656
|
-
await client2.request("GET", `/events/${
|
|
46575
|
+
await client2.request("GET", `/events/${enc6(event_id)}/orders/${qs(params)}`)
|
|
45657
46576
|
);
|
|
45658
46577
|
}
|
|
45659
46578
|
);
|
|
@@ -45672,7 +46591,7 @@ function registerEventTools(server, deps) {
|
|
|
45672
46591
|
view,
|
|
45673
46592
|
await client2.request(
|
|
45674
46593
|
"GET",
|
|
45675
|
-
`/events/${
|
|
46594
|
+
`/events/${enc6(event_id)}/ticket_classes/${enc6(ticket_class_id)}/`
|
|
45676
46595
|
)
|
|
45677
46596
|
)
|
|
45678
46597
|
);
|
|
@@ -45691,7 +46610,7 @@ function registerEventTools(server, deps) {
|
|
|
45691
46610
|
view,
|
|
45692
46611
|
await client2.request(
|
|
45693
46612
|
"GET",
|
|
45694
|
-
`/events/${
|
|
46613
|
+
`/events/${enc6(event_id)}/${canned ? "canned_questions" : "questions"}/`
|
|
45695
46614
|
)
|
|
45696
46615
|
)
|
|
45697
46616
|
);
|
|
@@ -45714,7 +46633,7 @@ function registerLookupTools(server, deps) {
|
|
|
45714
46633
|
async ({ order_id, expand, view }) => {
|
|
45715
46634
|
const params = new URLSearchParams();
|
|
45716
46635
|
if (expand) params.set("expand", expand);
|
|
45717
|
-
return viewResponse(view, await client2.request("GET", `/orders/${
|
|
46636
|
+
return viewResponse(view, await client2.request("GET", `/orders/${enc6(order_id)}/${qs(params)}`));
|
|
45718
46637
|
}
|
|
45719
46638
|
);
|
|
45720
46639
|
server.registerTool(
|
|
@@ -45727,7 +46646,7 @@ function registerLookupTools(server, deps) {
|
|
|
45727
46646
|
view: viewArg()
|
|
45728
46647
|
}
|
|
45729
46648
|
},
|
|
45730
|
-
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)}/`))
|
|
45731
46650
|
);
|
|
45732
46651
|
server.registerTool(
|
|
45733
46652
|
"eb_venue_events",
|
|
@@ -45747,7 +46666,7 @@ function registerLookupTools(server, deps) {
|
|
|
45747
46666
|
if (continuation) params.set("continuation", continuation);
|
|
45748
46667
|
return viewResponse(
|
|
45749
46668
|
view,
|
|
45750
|
-
await client2.request("GET", `/venues/${
|
|
46669
|
+
await client2.request("GET", `/venues/${enc6(venue_id)}/events/${qs(params)}`)
|
|
45751
46670
|
);
|
|
45752
46671
|
}
|
|
45753
46672
|
);
|
|
@@ -45761,7 +46680,7 @@ function registerLookupTools(server, deps) {
|
|
|
45761
46680
|
view: viewArg()
|
|
45762
46681
|
}
|
|
45763
46682
|
},
|
|
45764
|
-
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)}/`))
|
|
45765
46684
|
);
|
|
45766
46685
|
server.registerTool(
|
|
45767
46686
|
"eb_organizer_events",
|
|
@@ -45783,7 +46702,7 @@ function registerLookupTools(server, deps) {
|
|
|
45783
46702
|
if (continuation) params.set("continuation", continuation);
|
|
45784
46703
|
return viewResponse(
|
|
45785
46704
|
view,
|
|
45786
|
-
await client2.request("GET", `/organizers/${
|
|
46705
|
+
await client2.request("GET", `/organizers/${enc6(organizer_id)}/events/${qs(params)}`)
|
|
45787
46706
|
);
|
|
45788
46707
|
}
|
|
45789
46708
|
);
|
|
@@ -45805,7 +46724,7 @@ function registerLookupTools(server, deps) {
|
|
|
45805
46724
|
if (continuation) params.set("continuation", continuation);
|
|
45806
46725
|
return viewResponse(
|
|
45807
46726
|
view,
|
|
45808
|
-
await client2.request("GET", `/series/${
|
|
46727
|
+
await client2.request("GET", `/series/${enc6(series_id)}/events/${qs(params)}`)
|
|
45809
46728
|
);
|
|
45810
46729
|
}
|
|
45811
46730
|
);
|
|
@@ -45819,7 +46738,7 @@ function registerLookupTools(server, deps) {
|
|
|
45819
46738
|
view: viewArg()
|
|
45820
46739
|
}
|
|
45821
46740
|
},
|
|
45822
|
-
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)}/`))
|
|
45823
46742
|
);
|
|
45824
46743
|
}
|
|
45825
46744
|
|