@nimblebrain/synapse 0.20.0 → 0.21.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{check-JEM5PN64.cjs → check-53RMGOVV.cjs} +58 -12
- package/dist/check-53RMGOVV.cjs.map +1 -0
- package/dist/{check-YDZUQT6A.js → check-CSE4XQRL.js} +58 -12
- package/dist/check-CSE4XQRL.js.map +1 -0
- package/dist/codegen/cli.cjs +1 -1
- package/dist/codegen/cli.js +1 -1
- package/package.json +1 -1
- package/dist/check-JEM5PN64.cjs.map +0 -1
- package/dist/check-YDZUQT6A.js.map +0 -1
|
@@ -16,6 +16,7 @@ var EXTENSION = "A client uses MCP Apps only when both sides declare io.modelcon
|
|
|
16
16
|
var CHALLENGE = "Sign-in starts from a 401 whose WWW-Authenticate carries resource_metadata.";
|
|
17
17
|
var PRM_RESOURCE = "The protected-resource metadata's resource must equal the connector URL exactly.";
|
|
18
18
|
var ISSUER = "The authorization server's metadata issuer must equal the advertised authorization server exactly (RFC 8414).";
|
|
19
|
+
var SPEC_CSP = "ui.csp is the frame's security policy for Claude and the NimbleBrain host, and OpenAI documents it as generally preferred for new UI; measured 2026-09-18 in ChatGPT developer mode, the frame ran under ChatGPT's own default sandbox policy whichever dialect the resource carried.";
|
|
19
20
|
var PROFILES = {
|
|
20
21
|
nimblebrain: {
|
|
21
22
|
name: "nimblebrain",
|
|
@@ -64,11 +65,15 @@ var PROFILES = {
|
|
|
64
65
|
"tool-resource-uri": { severity: "error", why: RESOLVES },
|
|
65
66
|
"tool-ui-meta": { severity: "error", why: UI_META },
|
|
66
67
|
"extension-declared": { severity: "error", why: EXTENSION },
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
// A requirement the vendor documents and the host was not observed to
|
|
69
|
+
// enforce is what `warn` is for. Neither dialect has been observed to
|
|
70
|
+
// produce an enforced policy, so both sit at warn.
|
|
71
|
+
"resource-csp": { severity: "warn", why: SPEC_CSP },
|
|
72
|
+
"resource-openai-csp": {
|
|
73
|
+
severity: "warn",
|
|
74
|
+
why: "OpenAI documents openai/widgetCSP (snake_case origin lists) as a legacy compatibility surface, and it is still the only way to declare redirect_domains. Measured 2026-09-18 in developer mode, a resource carrying it beside ui.csp produced no enforced policy: the component ran under ChatGPT's default sandbox CSP either way. Declare it for the documented requirement; it is not known to change what the host enforces."
|
|
70
75
|
},
|
|
71
|
-
"resource-data-fonts": {
|
|
76
|
+
"resource-openai-data-fonts": {
|
|
72
77
|
severity: "warn",
|
|
73
78
|
why: "A data: font is blocked by a security policy that does not declare it."
|
|
74
79
|
},
|
|
@@ -159,7 +164,9 @@ var SESSION_CHECKS = [
|
|
|
159
164
|
"tool-resource-uri",
|
|
160
165
|
"tool-ui-meta",
|
|
161
166
|
"resource-csp",
|
|
167
|
+
"resource-openai-csp",
|
|
162
168
|
"resource-data-fonts",
|
|
169
|
+
"resource-openai-data-fonts",
|
|
163
170
|
"tool-security-schemes"
|
|
164
171
|
];
|
|
165
172
|
async function openSession(url, token, onResponse) {
|
|
@@ -305,7 +312,9 @@ function checkSession(s, authRequired, add) {
|
|
|
305
312
|
badMeta.length ? badMeta.join("; ") : "tool ui metadata is well-formed"
|
|
306
313
|
);
|
|
307
314
|
const noCsp = [];
|
|
315
|
+
const noOpenAiCsp = [];
|
|
308
316
|
const dataFonts = [];
|
|
317
|
+
const openAiDataFonts = [];
|
|
309
318
|
for (const [uri, contents] of s.reads) {
|
|
310
319
|
if (contents instanceof Error) continue;
|
|
311
320
|
for (const c of contents) {
|
|
@@ -314,13 +323,32 @@ function checkSession(s, authRequired, add) {
|
|
|
314
323
|
const csp = ui?.csp;
|
|
315
324
|
if (csp === void 0) {
|
|
316
325
|
noCsp.push(`${uri} declares no ui.csp`);
|
|
317
|
-
} else if (!
|
|
326
|
+
} else if (!isOriginLists(csp)) {
|
|
318
327
|
noCsp.push(`${uri}: ui.csp is not a map of origin lists`);
|
|
319
328
|
}
|
|
329
|
+
const openAi = openAiCsp(c) ?? openAiCsp(listed);
|
|
330
|
+
if (openAi === void 0) {
|
|
331
|
+
noOpenAiCsp.push(`${uri} declares no ${OPENAI_CSP_KEY}`);
|
|
332
|
+
} else if (!isOriginLists(openAi)) {
|
|
333
|
+
noOpenAiCsp.push(`${uri}: ${OPENAI_CSP_KEY} is not a map of origin lists`);
|
|
334
|
+
} else {
|
|
335
|
+
const camel = SPEC_CSP_KEYS.filter((k) => k in openAi);
|
|
336
|
+
if (camel.length) {
|
|
337
|
+
noOpenAiCsp.push(
|
|
338
|
+
`${uri}: ${OPENAI_CSP_KEY} names ${camel.join(", ")}, which OpenAI does not document for it`
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
320
342
|
const html = "text" in c ? c.text : Buffer.from(c.blob, "base64").toString("utf-8");
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
343
|
+
if (hasDataFont(html)) {
|
|
344
|
+
if (!declaresData(csp, "resourceDomains")) {
|
|
345
|
+
dataFonts.push(`${uri} loads a data: font, and ui.csp.resourceDomains omits data:`);
|
|
346
|
+
}
|
|
347
|
+
if (!declaresData(openAi, "resource_domains")) {
|
|
348
|
+
openAiDataFonts.push(
|
|
349
|
+
`${uri} loads a data: font, and ${OPENAI_CSP_KEY}.resource_domains omits data:`
|
|
350
|
+
);
|
|
351
|
+
}
|
|
324
352
|
}
|
|
325
353
|
}
|
|
326
354
|
}
|
|
@@ -329,10 +357,20 @@ function checkSession(s, authRequired, add) {
|
|
|
329
357
|
noCsp.length ? "fail" : "pass",
|
|
330
358
|
noCsp.length ? noCsp.join("; ") : "every UI resource declares ui.csp"
|
|
331
359
|
);
|
|
360
|
+
add(
|
|
361
|
+
"resource-openai-csp",
|
|
362
|
+
noOpenAiCsp.length ? "fail" : "pass",
|
|
363
|
+
noOpenAiCsp.length ? noOpenAiCsp.join("; ") : `every UI resource declares ${OPENAI_CSP_KEY}`
|
|
364
|
+
);
|
|
332
365
|
add(
|
|
333
366
|
"resource-data-fonts",
|
|
334
367
|
dataFonts.length ? "fail" : "pass",
|
|
335
|
-
dataFonts.length ? dataFonts.join("; ") : "no
|
|
368
|
+
dataFonts.length ? dataFonts.join("; ") : "no data: font ui.csp omits"
|
|
369
|
+
);
|
|
370
|
+
add(
|
|
371
|
+
"resource-openai-data-fonts",
|
|
372
|
+
openAiDataFonts.length ? "fail" : "pass",
|
|
373
|
+
openAiDataFonts.length ? openAiDataFonts.join("; ") : `no data: font ${OPENAI_CSP_KEY} omits`
|
|
336
374
|
);
|
|
337
375
|
}
|
|
338
376
|
if (!authRequired) {
|
|
@@ -410,9 +448,17 @@ function resourceUi(item) {
|
|
|
410
448
|
const ui = item?._meta?.ui;
|
|
411
449
|
return ui && typeof ui === "object" ? ui : void 0;
|
|
412
450
|
}
|
|
413
|
-
|
|
451
|
+
var OPENAI_CSP_KEY = "openai/widgetCSP";
|
|
452
|
+
var SPEC_CSP_KEYS = ["connectDomains", "resourceDomains"];
|
|
453
|
+
function openAiCsp(item) {
|
|
454
|
+
return item?._meta?.[OPENAI_CSP_KEY];
|
|
455
|
+
}
|
|
456
|
+
function isOriginLists(value) {
|
|
414
457
|
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.values(value).every((v) => Array.isArray(v) && v.every((o) => typeof o === "string"));
|
|
415
458
|
}
|
|
459
|
+
function declaresData(csp, key) {
|
|
460
|
+
return isOriginLists(csp) && (csp[key] ?? []).includes("data:");
|
|
461
|
+
}
|
|
416
462
|
function hasSecuritySchemes(tool) {
|
|
417
463
|
const schemes = tool._meta?.securitySchemes ?? tool.securitySchemes;
|
|
418
464
|
return Array.isArray(schemes) && schemes.length > 0;
|
|
@@ -431,5 +477,5 @@ exports.applyProfiles = applyProfiles;
|
|
|
431
477
|
exports.formatReport = formatReport;
|
|
432
478
|
exports.isTargetName = isTargetName;
|
|
433
479
|
exports.runChecks = runChecks;
|
|
434
|
-
//# sourceMappingURL=check-
|
|
435
|
-
//# sourceMappingURL=check-
|
|
480
|
+
//# sourceMappingURL=check-53RMGOVV.cjs.map
|
|
481
|
+
//# sourceMappingURL=check-53RMGOVV.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/check/profiles.ts","../src/check/index.ts"],"names":["RESOURCE_MIME_TYPE","Client","EXTENSION_ID","StreamableHTTPClientTransport","ResultSchema","ListToolsResultSchema","extractWWWAuthenticateParams","discoverOAuthProtectedResourceMetadata","discoverAuthorizationServerMetadata","RESOURCE_URI_META_KEY"],"mappings":";;;;;;;;;;;AAuCA,IAAM,SAAA,GAAY,uDAAA;AAClB,IAAM,QAAA,GAAW,uEAAA;AACjB,IAAM,OAAA,GACJ,6GAAA;AACF,IAAM,SAAA,GAAY,iFAAA;AAClB,IAAM,SAAA,GAAY,6EAAA;AAClB,IAAM,YAAA,GACJ,kFAAA;AACF,IAAM,MAAA,GACJ,+GAAA;AACF,IAAM,QAAA,GACJ,uRAAA;AAEK,IAAM,QAAA,GAAwC;AAAA,EACnD,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,aAAA;AAAA,IACN,WAAA,EAAa,uBAAA;AAAA,IACb,KAAA,EAAO;AAAA,MACL,kBAAA,EAAoB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,SAAA,EAAU;AAAA,MACxD,mBAAA,EAAqB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,QAAA,EAAS;AAAA,MACxD,cAAA,EAAgB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,OAAA,EAAQ;AAAA;AAAA;AAAA,MAGlD,oBAAA,EAAsB,EAAE,QAAA,EAAU,MAAA,EAAQ,KAAK,SAAA,EAAU;AAAA;AAAA,MAEzD,gBAAA,EAAkB,EAAE,QAAA,EAAU,MAAA,EAAQ,KAAK,SAAA,EAAU;AAAA,MACrD,wBAAA,EAA0B,EAAE,QAAA,EAAU,MAAA,EAAQ,KAAK,YAAA,EAAa;AAAA,MAChE,aAAA,EAAe,EAAE,QAAA,EAAU,MAAA,EAAQ,KAAK,MAAA;AAAO;AACjD,GACF;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM,QAAA;AAAA,IACN,WAAA,EAAa,+BAAA;AAAA,IACb,KAAA,EAAO;AAAA,MACL,kBAAA,EAAoB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,SAAA,EAAU;AAAA,MACxD,mBAAA,EAAqB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,QAAA,EAAS;AAAA,MACxD,cAAA,EAAgB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,OAAA,EAAQ;AAAA,MAClD,oBAAA,EAAsB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,SAAA,EAAU;AAAA,MAC1D,cAAA,EAAgB;AAAA,QACd,QAAA,EAAU,MAAA;AAAA,QACV,GAAA,EAAK;AAAA,OACP;AAAA,MACA,qBAAA,EAAuB;AAAA,QACrB,QAAA,EAAU,OAAA;AAAA,QACV,GAAA,EAAK;AAAA,OACP;AAAA,MACA,gBAAA,EAAkB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,SAAA,EAAU;AAAA,MACtD,wBAAA,EAA0B,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,YAAA,EAAa;AAAA;AAAA;AAAA,MAGjE,aAAA,EAAe,EAAE,QAAA,EAAU,MAAA,EAAQ,KAAK,MAAA;AAAO;AACjD,GACF;AAAA,EACA,OAAA,EAAS;AAAA,IACP,IAAA,EAAM,SAAA;AAAA,IACN,WAAA,EAAa,eAAA;AAAA,IACb,KAAA,EAAO;AAAA,MACL,kBAAA,EAAoB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,SAAA,EAAU;AAAA,MACxD,mBAAA,EAAqB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,QAAA,EAAS;AAAA,MACxD,cAAA,EAAgB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,OAAA,EAAQ;AAAA,MAClD,oBAAA,EAAsB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,SAAA,EAAU;AAAA;AAAA;AAAA;AAAA,MAI1D,cAAA,EAAgB,EAAE,QAAA,EAAU,MAAA,EAAQ,KAAK,QAAA,EAAS;AAAA,MAClD,qBAAA,EAAuB;AAAA,QACrB,QAAA,EAAU,MAAA;AAAA,QACV,GAAA,EAAK;AAAA,OACP;AAAA,MACA,4BAAA,EAA8B;AAAA,QAC5B,QAAA,EAAU,MAAA;AAAA,QACV,GAAA,EAAK;AAAA,OACP;AAAA,MACA,gBAAA,EAAkB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,SAAA,EAAU;AAAA,MACtD,wBAAA,EAA0B,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,YAAA,EAAa;AAAA,MACjE,aAAA,EAAe,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,MAAA,EAAO;AAAA,MAChD,uBAAA,EAAyB;AAAA,QACvB,QAAA,EAAU,OAAA;AAAA,QACV,GAAA,EAAK;AAAA;AACP;AACF;AAEJ;AAEO,SAAS,aAAa,KAAA,EAAoC;AAC/D,EAAA,OAAO,MAAA,CAAO,MAAA,CAAO,QAAA,EAAU,KAAK,CAAA;AACtC;;;ACpEA,IAAM,SAAA,GAAY,OAAA;AAClB,IAAM,6BAAa,IAAI,GAAA,CAAI,CAAC,OAAA,EAAS,KAAK,CAAC,CAAA;AAY3C,eAAsB,SAAA,CAAU,GAAA,EAAa,OAAA,GAAwB,EAAC,EAA2B;AAC/F,EAAA,MAAM,UAAyB,EAAC;AAChC,EAAA,MAAM,GAAA,GAAM,CAAC,EAAA,EAAa,MAAA,EAAqB,MAAA,KAC7C,OAAA,CAAQ,IAAA,CAAK,EAAE,EAAA,EAAI,MAAA,EAAQ,MAAA,EAAQ,CAAA;AAIrC,EAAA,IAAI,SAAA;AACJ,EAAA,IAAI,OAAA;AACJ,EAAA,IAAI,YAAA;AACJ,EAAA,IAAI;AACF,IAAA,OAAA,GAAU,MAAM,WAAA,CAAY,GAAA,EAAK,KAAA,CAAA,EAAW,CAAC,GAAA,KAAQ;AACnD,MAAA,IAAI,GAAA,CAAI,MAAA,KAAW,GAAA,EAAK,SAAA,KAAc,GAAA;AAAA,IACxC,CAAC,CAAA;AAAA,EACH,SAAS,GAAA,EAAK;AACZ,IAAA,YAAA,GAAe,GAAA;AAAA,EACjB;AAEA,EAAA,MAAM,eAAe,SAAA,KAAc,MAAA;AACnC,EAAA,IAAI,YAAA,IAAgB,QAAQ,KAAA,EAAO;AACjC,IAAA,IAAI;AACF,MAAA,OAAA,GAAU,MAAM,WAAA,CAAY,GAAA,EAAK,OAAA,CAAQ,KAAK,CAAA;AAC9C,MAAA,YAAA,GAAe,KAAA,CAAA;AAAA,IACjB,SAAS,GAAA,EAAK;AACZ,MAAA,YAAA,GAAe,GAAA;AAAA,IACjB;AAAA,EACF;AAGA,EAAA,IAAI,CAAC,OAAA,KAAY,CAAC,YAAA,IAAgB,QAAQ,KAAA,CAAA,EAAQ;AAChD,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,qBAAA,EAAwB,GAAG,CAAA,EAAG,YAAA,GAAe,0BAA0B,EAAE,CAAA,EAAA,EAAK,OAAA,CAAQ,YAAY,CAAC,CAAA;AAAA,KACrG;AAAA,EACF;AAEA,EAAA,MAAM,SAAA,CAAU,GAAA,EAAK,SAAA,EAAW,GAAG,CAAA;AAEnC,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,KAAA,MAAW,MAAM,cAAA,EAAgB;AAC/B,MAAA,GAAA,CAAI,EAAA,EAAI,QAAQ,kDAAkD,CAAA;AAAA,IACpE;AACA,IAAA,OAAO,OAAA;AAAA,EACT;AAEA,EAAA,YAAA,CAAa,OAAA,EAAS,cAAc,GAAG,CAAA;AACvC,EAAA,OAAO,OAAA;AACT;AAGO,SAAS,aAAA,CAAc,SAAwB,OAAA,EAAuC;AAC3F,EAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,CAAC,MAAA,KAAW;AAC7B,IAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,MAAM,CAAA,CAAE,KAAA;AAC/B,IAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,OAAA,CAAQ,CAAC,CAAA,KAAM;AACpC,MAAA,MAAM,IAAA,GAAO,KAAA,CAAM,CAAA,CAAE,EAAE,CAAA;AACvB,MAAA,OAAO,IAAA,GAAO,CAAC,EAAE,GAAG,CAAA,EAAG,QAAA,EAAU,IAAA,CAAK,QAAA,EAAU,GAAA,EAAK,IAAA,CAAK,GAAA,EAAK,IAAI,EAAC;AAAA,IACtE,CAAC,CAAA;AACD,IAAA,OAAO;AAAA,MACL,MAAA;AAAA,MACA,OAAA,EAAS,MAAA;AAAA,MACT,MAAA,EAAQ,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,KAAM,EAAE,MAAA,KAAW,MAAA,IAAU,CAAA,CAAE,QAAA,KAAa,OAAO;AAAA,KAC1E;AAAA,EACF,CAAC,CAAA;AACH;AAGO,SAAS,YAAA,CAAa,KAAa,OAAA,EAAiC;AACzE,EAAA,MAAM,QAAQ,IAAA,CAAK,GAAA,CAAI,GAAG,OAAA,CAAQ,OAAA,CAAQ,CAAC,CAAA,KAAM,CAAA,CAAE,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;AACtF,EAAA,MAAM,KAAA,GAAQ,CAAC,CAAA,cAAA,EAAiB,GAAG,CAAA,CAAE,CAAA;AACrC,EAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,IAAA,KAAA,CAAM,IAAA,CAAK,EAAA,EAAI,CAAA,EAAG,MAAA,CAAO,MAAM,KAAK,MAAA,CAAO,MAAA,GAAS,MAAA,GAAS,IAAI,CAAA,CAAE,CAAA;AACnE,IAAA,KAAA,MAAW,CAAA,IAAK,OAAO,OAAA,EAAS;AAC9B,MAAA,MAAM,KAAA,GACJ,CAAA,CAAE,MAAA,KAAW,MAAA,GAAU,CAAA,CAAE,QAAA,KAAa,OAAA,GAAU,MAAA,GAAS,MAAA,GAAU,CAAA,CAAE,MAAA,CAAO,WAAA,EAAY;AAC1F,MAAA,KAAA,CAAM,IAAA,CAAK,CAAA,EAAA,EAAK,KAAA,CAAM,MAAA,CAAO,CAAC,CAAC,CAAA,EAAA,EAAK,CAAA,CAAE,EAAA,CAAG,OAAO,KAAK,CAAC,CAAA,EAAA,EAAK,CAAA,CAAE,MAAM,CAAA,CAAE,CAAA;AACrE,MAAA,IAAI,CAAA,CAAE,MAAA,KAAW,MAAA,EAAQ,KAAA,CAAM,IAAA,CAAK,CAAA,QAAA,EAAW,EAAA,CAAG,MAAA,CAAO,KAAK,CAAC,CAAA,EAAA,EAAK,CAAA,CAAE,GAAG,CAAA,CAAE,CAAA;AAAA,IAC7E;AAAA,EACF;AACA,EAAA,OAAO,KAAA,CAAM,KAAK,IAAI,CAAA;AACxB;AAMA,IAAM,cAAA,GAA4B;AAAA,EAChC,oBAAA;AAAA,EACA,kBAAA;AAAA,EACA,mBAAA;AAAA,EACA,cAAA;AAAA,EACA,cAAA;AAAA,EACA,qBAAA;AAAA,EACA,qBAAA;AAAA,EACA,4BAAA;AAAA,EACA;AACF,CAAA;AAEA,eAAe,WAAA,CACb,GAAA,EACA,KAAA,EACA,UAAA,EACkB;AAGlB,EAAA,MAAM,EAAA,GAA8B,EAAE,SAAA,EAAW,CAACA,yBAAkB,CAAA,EAAE;AACtE,EAAA,MAAM,SAAS,IAAIC,eAAA;AAAA,IACjB,EAAE,IAAA,EAAM,eAAA,EAAiB,OAAA,EAAS,GAAA,EAAI;AAAA,IACtC,EAAE,YAAA,EAAc,EAAE,UAAA,EAAY,EAAE,CAACC,mBAAY,GAAG,EAAA,EAAG,EAAE;AAAE,GACzD;AACA,EAAA,MAAM,YAAY,IAAIC,+CAAA,CAA8B,IAAI,GAAA,CAAI,GAAG,CAAA,EAAG;AAAA,IAChE,WAAA,EAAa,KAAA,GAAQ,EAAE,OAAA,EAAS,EAAE,eAAe,CAAA,OAAA,EAAU,KAAK,CAAA,CAAA,EAAG,EAAE,GAAI,MAAA;AAAA,IACzE,KAAA,EAAO,OAAO,KAAA,EAAO,IAAA,KAAS;AAC5B,MAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,KAAA,EAAO,IAAI,CAAA;AACnC,MAAA,UAAA,GAAa,GAAG,CAAA;AAChB,MAAA,OAAO,GAAA;AAAA,IACT;AAAA,GACD,CAAA;AACD,EAAA,MAAM,MAAA,CAAO,QAAQ,SAAS,CAAA;AAC9B,EAAA,IAAI;AACF,IAAA,MAAM,YAAA,GAAe,OAAO,qBAAA,EAAsB;AAClD,IAAA,MAAM,QAAgB,EAAC;AACvB,IAAA,MAAM,YAAwB,EAAC;AAC/B,IAAA,IAAI,MAAA;AACJ,IAAA,IAAI,cAAc,KAAA,EAAO;AACvB,MAAA,GAAG;AAKD,QAAA,MAAM,GAAA,GAAM,MAAM,MAAA,CAAO,OAAA;AAAA,UACvB,EAAE,QAAQ,YAAA,EAAc,MAAA,EAAQ,SAAS,EAAE,MAAA,EAAO,GAAI,EAAC,EAAE;AAAA,UACzDC;AAAA,SACF;AACA,QAAA,MAAM,IAAA,GAAOC,8BAAA,CAAsB,KAAA,CAAM,GAAG,CAAA;AAC5C,QAAA,MAAM,QAAA,GAAY,GAAA,CAAmD,KAAA,IAAS,EAAC;AAC/E,QAAA,IAAA,CAAK,KAAA,CAAM,OAAA,CAAQ,CAAC,IAAA,EAAM,CAAA,KAAM;AAC9B,UAAA,MAAM,OAAA,GAAU,QAAA,CAAS,CAAC,CAAA,EAAG,eAAA;AAC7B,UAAA,IAAI,OAAA,KAAY,KAAA,CAAA,EAAY,IAAA,CAAiC,eAAA,GAAkB,OAAA;AAAA,QACjF,CAAC,CAAA;AACD,QAAA,KAAA,CAAM,IAAA,CAAK,GAAG,IAAA,CAAK,KAAK,CAAA;AACxB,QAAA,MAAA,GAAS,IAAA,CAAK,UAAA;AAAA,MAChB,CAAA,QAAS,MAAA;AAAA,IACX;AACA,IAAA,IAAI,cAAc,SAAA,EAAW;AAC3B,MAAA,GAAG;AACD,QAAA,MAAM,OAAO,MAAM,MAAA,CAAO,aAAA,CAAc,EAAE,QAAQ,CAAA;AAClD,QAAA,SAAA,CAAU,IAAA,CAAK,GAAG,IAAA,CAAK,SAAS,CAAA;AAChC,QAAA,MAAA,GAAS,IAAA,CAAK,UAAA;AAAA,MAChB,CAAA,QAAS,MAAA;AAAA,IACX;AAEA,IAAA,MAAM,OAAO,IAAI,GAAA,CAAI,SAAA,CAAU,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,GAAG,CAAA,CAAE,OAAO,CAAC,CAAA,KAAM,EAAE,UAAA,CAAW,SAAS,CAAC,CAAC,CAAA;AACvF,IAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,MAAA,MAAM,GAAA,GAAM,MAAA,CAAO,IAAI,CAAA,EAAG,WAAA;AAC1B,MAAA,IAAI,OAAO,GAAA,KAAQ,QAAA,EAAU,IAAA,CAAK,IAAI,GAAG,CAAA;AAAA,IAC3C;AACA,IAAA,MAAM,KAAA,uBAAY,GAAA,EAAwC;AAC1D,IAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,MAAA,IAAI;AACF,QAAA,KAAA,CAAM,GAAA,CAAI,MAAM,MAAM,MAAA,CAAO,aAAa,EAAE,GAAA,EAAK,CAAA,EAAG,QAAQ,CAAA;AAAA,MAC9D,SAAS,GAAA,EAAK;AACZ,QAAA,KAAA,CAAM,GAAA,CAAI,GAAA,EAAK,GAAA,YAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,KAAA,CAAM,MAAA,CAAO,GAAG,CAAC,CAAC,CAAA;AAAA,MACpE;AAAA,IACF;AACA,IAAA,OAAO,EAAE,YAAA,EAAc,KAAA,EAAO,SAAA,EAAW,KAAA,EAAM;AAAA,EACjD,CAAA,SAAE;AACA,IAAA,MAAM,OAAO,KAAA,EAAM;AAAA,EACrB;AACF;AAEA,SAAS,YAAA,CACP,CAAA,EACA,YAAA,EACA,GAAA,EACM;AACN,EAAA,MAAM,UAAA,GAAa,CAAA,CAAE,KAAA,CAAM,MAAA,CAAO,CAAC,MAAM,MAAA,CAAO,CAAC,CAAA,EAAG,WAAA,KAAgB,MAAS,CAAA;AAC7E,EAAA,MAAM,WAAA,GAAc,CAAA,CAAE,SAAA,CAAU,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,GAAA,CAAI,UAAA,CAAW,SAAS,CAAC,CAAA;AACzE,EAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,MAAA,GAAS,CAAA,IAAK,WAAA,CAAY,SAAS,CAAA,IAAK,CAAA,CAAE,KAAA,CAAM,IAAA,CAAK,aAAa,CAAA;AAE3F,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,KAAA,MAAW,MAAM,cAAA,EAAgB;AAC/B,MAAA,IAAI,EAAA,KAAO,uBAAA,EAAyB,GAAA,CAAI,EAAA,EAAI,QAAQ,0BAA0B,CAAA;AAAA,IAChF;AAAA,EACF,CAAA,MAAO;AAEL,IAAA,MAAM,QAAA,GAAW,CAAA,CAAE,YAAA,EAAc,UAAA,GAAaH,mBAAY,CAAA,KAAM,MAAA;AAChE,IAAA,GAAA;AAAA,MACE,oBAAA;AAAA,MACA,WAAW,MAAA,GAAS,MAAA;AAAA,MACpB,QAAA,GACI,CAAA,EAAGA,mBAAY,CAAA,YAAA,CAAA,GACf,+BAA+BA,mBAAY,CAAA,2BAAA;AAAA,KACjD;AAGA,IAAA,MAAM,YAAsB,EAAC;AAC7B,IAAA,KAAA,MAAW,KAAK,WAAA,EAAa;AAC3B,MAAA,IAAI,CAAA,CAAE,QAAA,KAAa,MAAA,IAAa,CAAA,CAAE,aAAaF,yBAAA,EAAoB;AACjE,QAAA,SAAA,CAAU,KAAK,CAAA,EAAG,CAAA,CAAE,GAAG,CAAA,cAAA,EAAiB,CAAA,CAAE,QAAQ,CAAA,CAAE,CAAA;AAAA,MACtD;AAAA,IACF;AACA,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,QAAQ,CAAA,IAAK,EAAE,KAAA,EAAO;AACrC,MAAA,IAAI,oBAAoB,KAAA,EAAO;AAC/B,MAAA,KAAA,MAAW,KAAK,QAAA,EAAU;AACxB,QAAA,IAAI,CAAA,CAAE,aAAaA,yBAAA,EAAoB;AACrC,UAAA,SAAA,CAAU,KAAK,CAAA,EAAG,GAAG,iBAAiB,CAAA,CAAE,QAAA,IAAY,cAAc,CAAA,CAAE,CAAA;AAAA,QACtE;AAAA,MACF;AAAA,IACF;AACA,IAAA,GAAA;AAAA,MACE,kBAAA;AAAA,MACA,SAAA,CAAU,SAAS,MAAA,GAAS,MAAA;AAAA,MAC5B,SAAA,CAAU,MAAA,GACN,CAAA,EAAG,SAAA,CAAU,IAAA,CAAK,IAAI,CAAC,CAAA,WAAA,EAAcA,yBAAkB,CAAA,CAAA,GACvD,CAAA,wBAAA,EAA2BA,yBAAkB,CAAA;AAAA,KACnD;AAGA,IAAA,MAAM,aAAuB,EAAC;AAC9B,IAAA,KAAA,MAAW,IAAA,IAAQ,EAAE,KAAA,EAAO;AAC1B,MAAA,MAAM,GAAA,GAAM,MAAA,CAAO,IAAI,CAAA,EAAG,WAAA;AAC1B,MAAA,IAAI,QAAQ,MAAA,EAAW;AACrB,QAAA,IAAI,aAAA,CAAc,IAAI,CAAA,EAAG;AACvB,UAAA,UAAA,CAAW,IAAA,CAAK,CAAA,EAAG,IAAA,CAAK,IAAI,CAAA,kDAAA,CAAoD,CAAA;AAAA,QAClF;AACA,QAAA;AAAA,MACF;AACA,MAAA,IAAI,OAAO,GAAA,KAAQ,QAAA,IAAY,CAAC,GAAA,CAAI,UAAA,CAAW,SAAS,CAAA,EAAG;AACzD,QAAA,UAAA,CAAW,IAAA,CAAK,GAAG,IAAA,CAAK,IAAI,oBAAoB,IAAA,CAAK,SAAA,CAAU,GAAG,CAAC,CAAA,mBAAA,CAAqB,CAAA;AACxF,QAAA;AAAA,MACF;AACA,MAAA,MAAM,IAAA,GAAO,CAAA,CAAE,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AAC5B,MAAA,IAAI,IAAA,YAAgB,KAAA,IAAS,CAAC,IAAA,EAAM,MAAA,EAAQ;AAC1C,QAAA,UAAA,CAAW,IAAA;AAAA,UACT,CAAA,EAAG,IAAA,CAAK,IAAI,CAAA,EAAA,EAAK,GAAG,CAAA,eAAA,EAAkB,IAAA,YAAgB,KAAA,GAAQ,CAAA,EAAA,EAAK,IAAA,CAAK,OAAO,CAAA,CAAA,CAAA,GAAM,EAAE,CAAA;AAAA,SACzF;AAAA,MACF;AAAA,IACF;AACA,IAAA,IAAI,UAAA,CAAW,MAAA,KAAW,CAAA,IAAK,UAAA,CAAW,WAAW,CAAA,EAAG;AACtD,MAAA,UAAA,CAAW,KAAK,2CAA2C,CAAA;AAAA,IAC7D;AACA,IAAA,GAAA;AAAA,MACE,mBAAA;AAAA,MACA,UAAA,CAAW,SAAS,MAAA,GAAS,MAAA;AAAA,MAC7B,UAAA,CAAW,SACP,UAAA,CAAW,IAAA,CAAK,IAAI,CAAA,GACpB,CAAA,EAAG,WAAW,MAAM,CAAA,gCAAA;AAAA,KAC1B;AAGA,IAAA,MAAM,UAAoB,EAAC;AAC3B,IAAA,KAAA,MAAW,IAAA,IAAQ,EAAE,KAAA,EAAO;AAC1B,MAAA,MAAM,EAAA,GAAK,OAAO,IAAI,CAAA;AACtB,MAAA,IAAI,CAAC,EAAA,EAAI;AACT,MAAA,MAAM,MAAM,EAAA,CAAG,UAAA;AACf,MAAA,IACE,QAAQ,MAAA,KACP,CAAC,MAAM,OAAA,CAAQ,GAAG,KAAK,GAAA,CAAI,MAAA,KAAW,KAAK,CAAC,GAAA,CAAI,MAAM,CAAC,CAAA,KAAM,WAAW,GAAA,CAAI,CAAC,CAAC,CAAA,CAAA,EAC/E;AACA,QAAA,OAAA,CAAQ,IAAA,CAAK,GAAG,IAAA,CAAK,IAAI,mBAAmB,IAAA,CAAK,SAAA,CAAU,GAAG,CAAC,CAAA,CAAE,CAAA;AAAA,MACnE;AACA,MAAA,KAAA,MAAW,GAAA,IAAO,CAAC,KAAA,EAAO,aAAa,CAAA,EAAY;AACjD,QAAA,IAAK,EAAA,CAA+B,GAAG,CAAA,KAAM,MAAA,EAAW;AACtD,UAAA,OAAA,CAAQ,KAAK,CAAA,EAAG,IAAA,CAAK,IAAI,CAAA,KAAA,EAAQ,GAAG,CAAA,sCAAA,CAAwC,CAAA;AAAA,QAC9E;AAAA,MACF;AAAA,IACF;AACA,IAAA,GAAA;AAAA,MACE,cAAA;AAAA,MACA,OAAA,CAAQ,SAAS,MAAA,GAAS,MAAA;AAAA,MAC1B,OAAA,CAAQ,MAAA,GAAS,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAA,GAAI;AAAA,KACxC;AAGA,IAAA,MAAM,QAAkB,EAAC;AACzB,IAAA,MAAM,cAAwB,EAAC;AAC/B,IAAA,MAAM,YAAsB,EAAC;AAC7B,IAAA,MAAM,kBAA4B,EAAC;AACnC,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,QAAQ,CAAA,IAAK,EAAE,KAAA,EAAO;AACrC,MAAA,IAAI,oBAAoB,KAAA,EAAO;AAC/B,MAAA,KAAA,MAAW,KAAK,QAAA,EAAU;AACxB,QAAA,MAAM,MAAA,GAAS,EAAE,SAAA,CAAU,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,QAAQ,GAAG,CAAA;AAEpD,QAAA,MAAM,EAAA,GAAM,UAAA,CAAW,CAAC,CAAA,IAAK,WAAW,MAAM,CAAA;AAC9C,QAAA,MAAM,MAAM,EAAA,EAAI,GAAA;AAChB,QAAA,IAAI,QAAQ,MAAA,EAAW;AACrB,UAAA,KAAA,CAAM,IAAA,CAAK,CAAA,EAAG,GAAG,CAAA,mBAAA,CAAqB,CAAA;AAAA,QACxC,CAAA,MAAA,IAAW,CAAC,aAAA,CAAc,GAAG,CAAA,EAAG;AAC9B,UAAA,KAAA,CAAM,IAAA,CAAK,CAAA,EAAG,GAAG,CAAA,qCAAA,CAAuC,CAAA;AAAA,QAC1D;AACA,QAAA,MAAM,MAAA,GAAS,SAAA,CAAU,CAAC,CAAA,IAAK,UAAU,MAAM,CAAA;AAC/C,QAAA,IAAI,WAAW,MAAA,EAAW;AACxB,UAAA,WAAA,CAAY,IAAA,CAAK,CAAA,EAAG,GAAG,CAAA,aAAA,EAAgB,cAAc,CAAA,CAAE,CAAA;AAAA,QACzD,CAAA,MAAA,IAAW,CAAC,aAAA,CAAc,MAAM,CAAA,EAAG;AACjC,UAAA,WAAA,CAAY,IAAA,CAAK,CAAA,EAAG,GAAG,CAAA,EAAA,EAAK,cAAc,CAAA,6BAAA,CAA+B,CAAA;AAAA,QAC3E,CAAA,MAAO;AACL,UAAA,MAAM,QAAQ,aAAA,CAAc,MAAA,CAAO,CAAC,CAAA,KAAM,KAAK,MAAM,CAAA;AACrD,UAAA,IAAI,MAAM,MAAA,EAAQ;AAChB,YAAA,WAAA,CAAY,IAAA;AAAA,cACV,CAAA,EAAG,GAAG,CAAA,EAAA,EAAK,cAAc,UAAU,KAAA,CAAM,IAAA,CAAK,IAAI,CAAC,CAAA,uCAAA;AAAA,aACrD;AAAA,UACF;AAAA,QACF;AACA,QAAA,MAAM,IAAA,GAAO,MAAA,IAAU,CAAA,GAAI,CAAA,CAAE,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,CAAA,CAAE,IAAA,EAAM,QAAQ,CAAA,CAAE,QAAA,CAAS,OAAO,CAAA;AAClF,QAAA,IAAI,WAAA,CAAY,IAAI,CAAA,EAAG;AACrB,UAAA,IAAI,CAAC,YAAA,CAAa,GAAA,EAAK,iBAAiB,CAAA,EAAG;AACzC,YAAA,SAAA,CAAU,IAAA,CAAK,CAAA,EAAG,GAAG,CAAA,2DAAA,CAA6D,CAAA;AAAA,UACpF;AACA,UAAA,IAAI,CAAC,YAAA,CAAa,MAAA,EAAQ,kBAAkB,CAAA,EAAG;AAC7C,YAAA,eAAA,CAAgB,IAAA;AAAA,cACd,CAAA,EAAG,GAAG,CAAA,yBAAA,EAA4B,cAAc,CAAA,6BAAA;AAAA,aAClD;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,IAAA,GAAA;AAAA,MACE,cAAA;AAAA,MACA,KAAA,CAAM,SAAS,MAAA,GAAS,MAAA;AAAA,MACxB,KAAA,CAAM,MAAA,GAAS,KAAA,CAAM,IAAA,CAAK,IAAI,CAAA,GAAI;AAAA,KACpC;AACA,IAAA,GAAA;AAAA,MACE,qBAAA;AAAA,MACA,WAAA,CAAY,SAAS,MAAA,GAAS,MAAA;AAAA,MAC9B,YAAY,MAAA,GAAS,WAAA,CAAY,KAAK,IAAI,CAAA,GAAI,8BAA8B,cAAc,CAAA;AAAA,KAC5F;AACA,IAAA,GAAA;AAAA,MACE,qBAAA;AAAA,MACA,SAAA,CAAU,SAAS,MAAA,GAAS,MAAA;AAAA,MAC5B,SAAA,CAAU,MAAA,GAAS,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA,GAAI;AAAA,KAC5C;AACA,IAAA,GAAA;AAAA,MACE,4BAAA;AAAA,MACA,eAAA,CAAgB,SAAS,MAAA,GAAS,MAAA;AAAA,MAClC,gBAAgB,MAAA,GAAS,eAAA,CAAgB,KAAK,IAAI,CAAA,GAAI,iBAAiB,cAAc,CAAA,MAAA;AAAA,KACvF;AAAA,EACF;AAGA,EAAA,IAAI,CAAC,YAAA,EAAc;AACjB,IAAA,GAAA,CAAI,uBAAA,EAAyB,QAAQ,kCAAkC,CAAA;AAAA,EACzE,CAAA,MAAO;AACL,IAAA,MAAM,OAAA,GAAU,CAAA,CAAE,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA,KAAM,CAAC,kBAAA,CAAmB,CAAC,CAAC,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,IAAI,CAAA;AAC/E,IAAA,GAAA;AAAA,MACE,uBAAA;AAAA,MACA,OAAA,CAAQ,SAAS,MAAA,GAAS,MAAA;AAAA,MAC1B,QAAQ,MAAA,GACJ,CAAA,uBAAA,EAA0B,QAAQ,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA,GAC5C;AAAA,KACN;AAAA,EACF;AACF;AAMA,eAAe,SAAA,CACb,GAAA,EACA,SAAA,EACA,GAAA,EACe;AACf,EAAA,IAAI,CAAC,SAAA,EAAW;AACd,IAAA,MAAM,MAAA,GAAS,6CAAA;AACf,IAAA,GAAA,CAAI,gBAAA,EAAkB,QAAQ,MAAM,CAAA;AACpC,IAAA,GAAA,CAAI,wBAAA,EAA0B,QAAQ,MAAM,CAAA;AAC5C,IAAA,GAAA,CAAI,aAAA,EAAe,QAAQ,MAAM,CAAA;AACjC,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,EAAE,mBAAA,EAAoB,GAAIM,oCAAA,CAA6B,SAAS,CAAA;AACtE,EAAA,GAAA;AAAA,IACE,gBAAA;AAAA,IACA,sBAAsB,MAAA,GAAS,MAAA;AAAA,IAC/B,mBAAA,GACI,CAAA,4BAAA,EAA+B,mBAAA,CAAoB,IAAI,CAAA,CAAA,GACvD,CAAA,mDAAA,EAAsD,IAAA,CAAK,SAAA,CAAU,SAAA,CAAU,OAAA,CAAQ,GAAA,CAAI,kBAAkB,CAAC,CAAC,CAAA,CAAA;AAAA,GACrH;AAEA,EAAA,IAAI,GAAA;AACJ,EAAA,IAAI;AAGF,IAAA,GAAA,GAAM,MAAMC,8CAAA,CAAuC,GAAA,EAAK,EAAE,qBAAqB,CAAA;AAAA,EACjF,SAAS,GAAA,EAAK;AACZ,IAAA,GAAA;AAAA,MACE,wBAAA;AAAA,MACA,MAAA;AAAA,MACA,CAAA,wCAAA,EAA2C,OAAA,CAAQ,GAAG,CAAC,CAAA;AAAA,KACzD;AACA,IAAA,GAAA,CAAI,aAAA,EAAe,QAAQ,gCAAgC,CAAA;AAC3D,IAAA;AAAA,EACF;AACA,EAAA,GAAA;AAAA,IACE,wBAAA;AAAA,IACA,GAAA,CAAI,QAAA,KAAa,GAAA,GAAM,MAAA,GAAS,MAAA;AAAA,IAChC,IAAI,QAAA,KAAa,GAAA,GACb,CAAA,gBAAA,EAAmB,GAAG,KACtB,CAAA,YAAA,EAAe,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI,QAAQ,CAAC,CAAA,MAAA,EAAS,IAAA,CAAK,SAAA,CAAU,GAAG,CAAC,CAAA;AAAA,GAC7E;AAEA,EAAA,MAAM,UAAA,GAAa,GAAA,CAAI,qBAAA,GAAwB,CAAC,CAAA;AAChD,EAAA,IAAI,CAAC,UAAA,EAAY;AACf,IAAA,GAAA,CAAI,aAAA,EAAe,QAAQ,4DAA4D,CAAA;AACvF,IAAA;AAAA,EACF;AACA,EAAA,IAAI;AACF,IAAA,MAAM,QAAA,GAAW,MAAMC,2CAAA,CAAoC,UAAU,CAAA;AACrE,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,GAAA,CAAI,aAAA,EAAe,MAAA,EAAQ,CAAA,2CAAA,EAA8C,UAAU,CAAA,CAAE,CAAA;AAAA,IACvF,CAAA,MAAA,IAAW,QAAA,CAAS,MAAA,KAAW,UAAA,EAAY;AACzC,MAAA,GAAA;AAAA,QACE,aAAA;AAAA,QACA,MAAA;AAAA,QACA,CAAA,UAAA,EAAa,IAAA,CAAK,SAAA,CAAU,QAAA,CAAS,MAAM,CAAC,CAAA,gBAAA,EAAmB,IAAA,CAAK,SAAA,CAAU,UAAU,CAAC,CAAA;AAAA,OAC3F;AAAA,IACF,CAAA,MAAO;AACL,MAAA,GAAA,CAAI,aAAA,EAAe,MAAA,EAAQ,CAAA,cAAA,EAAiB,UAAU,CAAA,CAAE,CAAA;AAAA,IAC1D;AAAA,EACF,SAAS,GAAA,EAAK;AACZ,IAAA,GAAA,CAAI,eAAe,MAAA,EAAQ,CAAA,0CAAA,EAA6C,OAAA,CAAQ,GAAG,CAAC,CAAA,CAAE,CAAA;AAAA,EACxF;AACF;AAMA,SAAS,OAAO,IAAA,EAAuC;AACrD,EAAA,MAAM,EAAA,GAAK,KAAK,KAAA,EAAO,EAAA;AACvB,EAAA,OAAO,EAAA,IAAM,OAAO,EAAA,KAAO,QAAA,GAAY,EAAA,GAAuB,MAAA;AAChE;AAEA,SAAS,cAAc,IAAA,EAAqB;AAC1C,EAAA,OACE,IAAA,CAAK,QAAQC,4BAAqB,CAAA,KAAM,UAAa,MAAA,CAAO,IAAI,GAAG,WAAA,KAAgB,MAAA;AAEvF;AAEA,SAAS,WAAW,IAAA,EAAgE;AAClF,EAAA,MAAM,EAAA,GAAK,MAAM,KAAA,EAAO,EAAA;AACxB,EAAA,OAAO,EAAA,IAAM,OAAO,EAAA,KAAO,QAAA,GAAW,EAAA,GAAK,MAAA;AAC7C;AASA,IAAM,cAAA,GAAiB,kBAAA;AAGvB,IAAM,aAAA,GAAgB,CAAC,gBAAA,EAAkB,iBAAiB,CAAA;AAE1D,SAAS,UAAU,IAAA,EAAgE;AACjF,EAAA,OAAO,IAAA,EAAM,QAAQ,cAAc,CAAA;AACrC;AAGA,SAAS,cAAc,KAAA,EAAmD;AACxE,EAAA,OACE,OAAO,KAAA,KAAU,QAAA,IACjB,KAAA,KAAU,IAAA,IACV,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,IACpB,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,KAAM,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA,IAAK,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,KAAM,OAAO,CAAA,KAAM,QAAQ,CAAC,CAAA;AAE/F;AAGA,SAAS,YAAA,CAAa,KAAc,GAAA,EAAsD;AACxF,EAAA,OAAO,aAAA,CAAc,GAAG,CAAA,IAAA,CAAM,GAAA,CAAI,GAAG,CAAA,IAAK,EAAC,EAAG,QAAA,CAAS,OAAO,CAAA;AAChE;AAEA,SAAS,mBAAmB,IAAA,EAAqB;AAC/C,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,KAAA,EAAO,eAAA,IAAoB,IAAA,CAAiC,eAAA;AACjF,EAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,IAAK,QAAQ,MAAA,GAAS,CAAA;AACpD;AAEA,IAAM,cAAA,GAAiB,2EAAA;AACvB,IAAM,kBAAA,GAAqB,wCAAA;AAE3B,SAAS,YAAY,IAAA,EAAuB;AAC1C,EAAA,OAAO,eAAe,IAAA,CAAK,IAAI,CAAA,IAAK,kBAAA,CAAmB,KAAK,IAAI,CAAA;AAClE;AAEA,SAAS,QAAQ,GAAA,EAAsB;AACrC,EAAA,OAAO,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,OAAO,GAAG,CAAA;AACxD","file":"check-53RMGOVV.cjs","sourcesContent":["/**\n * Store profiles for `synapse check`: which checks each host applies, and how\n * hard. A check a profile does not list is not run for that target.\n *\n * `error` fails the run. `warn` is reported and does not: it marks a\n * requirement the host is documented to have but is not known to enforce, so a\n * failure is a risk rather than a certain break.\n */\n\nexport type CheckId =\n | \"extension-declared\"\n | \"ui-resource-mime\"\n | \"tool-resource-uri\"\n | \"tool-ui-meta\"\n | \"resource-csp\"\n | \"resource-openai-csp\"\n | \"resource-data-fonts\"\n | \"resource-openai-data-fonts\"\n | \"auth-challenge\"\n | \"auth-resource-metadata\"\n | \"auth-issuer\"\n | \"tool-security-schemes\";\n\nexport type Severity = \"error\" | \"warn\";\n\nexport interface ProfileRule {\n severity: Severity;\n /** The host requirement behind the rule, printed when the check fails. */\n why: string;\n}\n\nexport interface Profile {\n name: TargetName;\n description: string;\n rules: Partial<Record<CheckId, ProfileRule>>;\n}\n\nexport type TargetName = \"nimblebrain\" | \"claude\" | \"chatgpt\";\n\nconst SPEC_MIME = \"UI resources are served as text/html;profile=mcp-app.\";\nconst RESOLVES = \"A tool's ui.resourceUri must name a ui:// resource the server serves.\";\nconst UI_META =\n \"ui.visibility is a non-empty subset of model/app; csp and permissions belong on the resource, not the tool.\";\nconst EXTENSION = \"A client uses MCP Apps only when both sides declare io.modelcontextprotocol/ui.\";\nconst CHALLENGE = \"Sign-in starts from a 401 whose WWW-Authenticate carries resource_metadata.\";\nconst PRM_RESOURCE =\n \"The protected-resource metadata's resource must equal the connector URL exactly.\";\nconst ISSUER =\n \"The authorization server's metadata issuer must equal the advertised authorization server exactly (RFC 8414).\";\nconst SPEC_CSP =\n \"ui.csp is the frame's security policy for Claude and the NimbleBrain host, and OpenAI documents it as generally preferred for new UI; measured 2026-09-18 in ChatGPT developer mode, the frame ran under ChatGPT's own default sandbox policy whichever dialect the resource carried.\";\n\nexport const PROFILES: Record<TargetName, Profile> = {\n nimblebrain: {\n name: \"nimblebrain\",\n description: \"The NimbleBrain host.\",\n rules: {\n \"ui-resource-mime\": { severity: \"error\", why: SPEC_MIME },\n \"tool-resource-uri\": { severity: \"error\", why: RESOLVES },\n \"tool-ui-meta\": { severity: \"error\", why: UI_META },\n // The host resolves an app by its ui:// scheme and does not gate on the\n // declaration, so a missing one is a portability risk, not a break.\n \"extension-declared\": { severity: \"warn\", why: EXTENSION },\n // Only reported when the server challenges at all.\n \"auth-challenge\": { severity: \"warn\", why: CHALLENGE },\n \"auth-resource-metadata\": { severity: \"warn\", why: PRM_RESOURCE },\n \"auth-issuer\": { severity: \"warn\", why: ISSUER },\n },\n },\n claude: {\n name: \"claude\",\n description: \"claude.ai and Claude Desktop.\",\n rules: {\n \"ui-resource-mime\": { severity: \"error\", why: SPEC_MIME },\n \"tool-resource-uri\": { severity: \"error\", why: RESOLVES },\n \"tool-ui-meta\": { severity: \"error\", why: UI_META },\n \"extension-declared\": { severity: \"error\", why: EXTENSION },\n \"resource-csp\": {\n severity: \"warn\",\n why: \"Claude builds the frame's security policy from ui.csp; an undeclared origin is blocked.\",\n },\n \"resource-data-fonts\": {\n severity: \"error\",\n why: \"Claude's default security policy blocks data: fonts unless the resource declares them.\",\n },\n \"auth-challenge\": { severity: \"error\", why: CHALLENGE },\n \"auth-resource-metadata\": { severity: \"error\", why: PRM_RESOURCE },\n // Claude has been observed to accept an issuer that differs by a trailing\n // slash, so this is a correctness warning for Claude rather than a break.\n \"auth-issuer\": { severity: \"warn\", why: ISSUER },\n },\n },\n chatgpt: {\n name: \"chatgpt\",\n description: \"ChatGPT apps.\",\n rules: {\n \"ui-resource-mime\": { severity: \"error\", why: SPEC_MIME },\n \"tool-resource-uri\": { severity: \"error\", why: RESOLVES },\n \"tool-ui-meta\": { severity: \"error\", why: UI_META },\n \"extension-declared\": { severity: \"error\", why: EXTENSION },\n // A requirement the vendor documents and the host was not observed to\n // enforce is what `warn` is for. Neither dialect has been observed to\n // produce an enforced policy, so both sit at warn.\n \"resource-csp\": { severity: \"warn\", why: SPEC_CSP },\n \"resource-openai-csp\": {\n severity: \"warn\",\n why: \"OpenAI documents openai/widgetCSP (snake_case origin lists) as a legacy compatibility surface, and it is still the only way to declare redirect_domains. Measured 2026-09-18 in developer mode, a resource carrying it beside ui.csp produced no enforced policy: the component ran under ChatGPT's default sandbox CSP either way. Declare it for the documented requirement; it is not known to change what the host enforces.\",\n },\n \"resource-openai-data-fonts\": {\n severity: \"warn\",\n why: \"A data: font is blocked by a security policy that does not declare it.\",\n },\n \"auth-challenge\": { severity: \"error\", why: CHALLENGE },\n \"auth-resource-metadata\": { severity: \"error\", why: PRM_RESOURCE },\n \"auth-issuer\": { severity: \"error\", why: ISSUER },\n \"tool-security-schemes\": {\n severity: \"error\",\n why: \"ChatGPT prompts for sign-in mid-conversation only for tools that declare securitySchemes.\",\n },\n },\n },\n};\n\nexport function isTargetName(value: string): value is TargetName {\n return Object.hasOwn(PROFILES, value);\n}\n","/**\n * `synapse check` — verify, from outside, what a host sees of a running MCP\n * server: the UI resources and their metadata, the MCP Apps extension\n * declaration, and the OAuth discovery surface a host signs in through.\n *\n * Every check runs once against the server; each target's profile then decides\n * which results count and how hard (see `./profiles.ts`).\n */\n\nimport type {\n McpUiClientCapabilities,\n McpUiResourceMeta,\n McpUiToolMeta,\n} from \"@modelcontextprotocol/ext-apps\";\nimport {\n EXTENSION_ID,\n RESOURCE_MIME_TYPE,\n RESOURCE_URI_META_KEY,\n} from \"@modelcontextprotocol/ext-apps/server\";\nimport {\n discoverAuthorizationServerMetadata,\n discoverOAuthProtectedResourceMetadata,\n extractWWWAuthenticateParams,\n} from \"@modelcontextprotocol/sdk/client/auth.js\";\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\nimport type {\n ReadResourceResult,\n Resource,\n ServerCapabilities,\n Tool,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { ListToolsResultSchema, ResultSchema } from \"@modelcontextprotocol/sdk/types.js\";\nimport { type CheckId, PROFILES, type Severity, type TargetName } from \"./profiles.js\";\n\nexport { type CheckId, isTargetName, PROFILES, type Profile, type TargetName } from \"./profiles.js\";\n\nexport type CheckStatus = \"pass\" | \"fail\" | \"skip\";\n\nexport interface CheckResult {\n id: CheckId;\n status: CheckStatus;\n detail: string;\n}\n\nexport interface TargetReport {\n target: TargetName;\n results: Array<CheckResult & { severity: Severity; why: string }>;\n /** True when any `error`-severity check failed. */\n failed: boolean;\n}\n\nexport interface CheckOptions {\n /** Bearer token for a server that requires auth. Without one, checks that need a session are skipped. */\n token?: string;\n}\n\nconst UI_SCHEME = \"ui://\";\nconst VISIBILITY = new Set([\"model\", \"app\"]);\n\ntype ResourceContents = ReadResourceResult[\"contents\"][number];\n\ninterface Session {\n capabilities: ServerCapabilities | undefined;\n tools: Tool[];\n resources: Resource[];\n reads: Map<string, ResourceContents[] | Error>;\n}\n\n/** Run every check against `url` once. */\nexport async function runChecks(url: string, options: CheckOptions = {}): Promise<CheckResult[]> {\n const results: CheckResult[] = [];\n const add = (id: CheckId, status: CheckStatus, detail: string) =>\n results.push({ id, status, detail });\n\n // Connect without credentials first: the response to that is what a host\n // sees before sign-in, and it decides whether the server requires auth.\n let challenge: Response | undefined;\n let session: Session | undefined;\n let connectError: unknown;\n try {\n session = await openSession(url, undefined, (res) => {\n if (res.status === 401) challenge ??= res;\n });\n } catch (err) {\n connectError = err;\n }\n\n const authRequired = challenge !== undefined;\n if (authRequired && options.token) {\n try {\n session = await openSession(url, options.token);\n connectError = undefined;\n } catch (err) {\n connectError = err;\n }\n }\n\n // A server that cannot be reached is not a server that passes.\n if (!session && (!authRequired || options.token)) {\n throw new Error(\n `could not connect to ${url}${authRequired ? \" with the given token\" : \"\"}: ${message(connectError)}`,\n );\n }\n\n await checkAuth(url, challenge, add);\n\n if (!session) {\n for (const id of SESSION_CHECKS) {\n add(id, \"skip\", \"the server requires auth; pass --token to run it\");\n }\n return results;\n }\n\n checkSession(session, authRequired, add);\n return results;\n}\n\n/** Apply each target's profile to one set of results. */\nexport function applyProfiles(results: CheckResult[], targets: TargetName[]): TargetReport[] {\n return targets.map((target) => {\n const rules = PROFILES[target].rules;\n const scored = results.flatMap((r) => {\n const rule = rules[r.id];\n return rule ? [{ ...r, severity: rule.severity, why: rule.why }] : [];\n });\n return {\n target,\n results: scored,\n failed: scored.some((r) => r.status === \"fail\" && r.severity === \"error\"),\n };\n });\n}\n\n/** One line per check, grouped by target. */\nexport function formatReport(url: string, reports: TargetReport[]): string {\n const width = Math.max(...reports.flatMap((r) => r.results.map((c) => c.id.length)), 0);\n const lines = [`synapse check ${url}`];\n for (const report of reports) {\n lines.push(\"\", `${report.target}: ${report.failed ? \"FAIL\" : \"ok\"}`);\n for (const r of report.results) {\n const label =\n r.status === \"fail\" ? (r.severity === \"error\" ? \"FAIL\" : \"WARN\") : r.status.toUpperCase();\n lines.push(` ${label.padEnd(4)} ${r.id.padEnd(width)} ${r.detail}`);\n if (r.status === \"fail\") lines.push(` ${\"\".padEnd(width)} ${r.why}`);\n }\n }\n return lines.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// session\n// ---------------------------------------------------------------------------\n\nconst SESSION_CHECKS: CheckId[] = [\n \"extension-declared\",\n \"ui-resource-mime\",\n \"tool-resource-uri\",\n \"tool-ui-meta\",\n \"resource-csp\",\n \"resource-openai-csp\",\n \"resource-data-fonts\",\n \"resource-openai-data-fonts\",\n \"tool-security-schemes\",\n];\n\nasync function openSession(\n url: string,\n token: string | undefined,\n onResponse?: (res: Response) => void,\n): Promise<Session> {\n // Declare MCP Apps support, so a server that gates its UI on the client's\n // capability shows the UI a host would see.\n const ui: McpUiClientCapabilities = { mimeTypes: [RESOURCE_MIME_TYPE] };\n const client = new Client(\n { name: \"synapse-check\", version: \"1\" },\n { capabilities: { extensions: { [EXTENSION_ID]: ui } } },\n );\n const transport = new StreamableHTTPClientTransport(new URL(url), {\n requestInit: token ? { headers: { Authorization: `Bearer ${token}` } } : undefined,\n fetch: async (input, init) => {\n const res = await fetch(input, init);\n onResponse?.(res);\n return res;\n },\n });\n await client.connect(transport);\n try {\n const capabilities = client.getServerCapabilities();\n const tools: Tool[] = [];\n const resources: Resource[] = [];\n let cursor: string | undefined;\n if (capabilities?.tools) {\n do {\n // Requested raw rather than through `listTools()`: the SDK's tool schema\n // strips fields it does not model, and ChatGPT documents `securitySchemes`\n // at the tool's top level as well as in `_meta`. The one field is copied\n // back onto each parsed tool so `hasSecuritySchemes` can see either place.\n const raw = await client.request(\n { method: \"tools/list\", params: cursor ? { cursor } : {} },\n ResultSchema,\n );\n const page = ListToolsResultSchema.parse(raw);\n const rawTools = (raw as { tools?: Array<Record<string, unknown>> }).tools ?? [];\n page.tools.forEach((tool, i) => {\n const schemes = rawTools[i]?.securitySchemes;\n if (schemes !== undefined) (tool as Record<string, unknown>).securitySchemes = schemes;\n });\n tools.push(...page.tools);\n cursor = page.nextCursor;\n } while (cursor);\n }\n if (capabilities?.resources) {\n do {\n const page = await client.listResources({ cursor });\n resources.push(...page.resources);\n cursor = page.nextCursor;\n } while (cursor);\n }\n\n const uris = new Set(resources.map((r) => r.uri).filter((u) => u.startsWith(UI_SCHEME)));\n for (const tool of tools) {\n const uri = toolUi(tool)?.resourceUri;\n if (typeof uri === \"string\") uris.add(uri);\n }\n const reads = new Map<string, ResourceContents[] | Error>();\n for (const uri of uris) {\n try {\n reads.set(uri, (await client.readResource({ uri })).contents);\n } catch (err) {\n reads.set(uri, err instanceof Error ? err : new Error(String(err)));\n }\n }\n return { capabilities, tools, resources, reads };\n } finally {\n await client.close();\n }\n}\n\nfunction checkSession(\n s: Session,\n authRequired: boolean,\n add: (id: CheckId, status: CheckStatus, detail: string) => void,\n): void {\n const boundTools = s.tools.filter((t) => toolUi(t)?.resourceUri !== undefined);\n const uiResources = s.resources.filter((r) => r.uri.startsWith(UI_SCHEME));\n const hasUi = boundTools.length > 0 || uiResources.length > 0 || s.tools.some(legacyBinding);\n\n if (!hasUi) {\n for (const id of SESSION_CHECKS) {\n if (id !== \"tool-security-schemes\") add(id, \"skip\", \"the server exposes no UI\");\n }\n } else {\n // extension-declared\n const declared = s.capabilities?.extensions?.[EXTENSION_ID] !== undefined;\n add(\n \"extension-declared\",\n declared ? \"pass\" : \"fail\",\n declared\n ? `${EXTENSION_ID} is declared`\n : `the server does not declare ${EXTENSION_ID} in capabilities.extensions`,\n );\n\n // ui-resource-mime: every ui:// resource, as listed and as read.\n const wrongMime: string[] = [];\n for (const r of uiResources) {\n if (r.mimeType !== undefined && r.mimeType !== RESOURCE_MIME_TYPE) {\n wrongMime.push(`${r.uri} is listed as ${r.mimeType}`);\n }\n }\n for (const [uri, contents] of s.reads) {\n if (contents instanceof Error) continue;\n for (const c of contents) {\n if (c.mimeType !== RESOURCE_MIME_TYPE) {\n wrongMime.push(`${uri} is served as ${c.mimeType ?? \"no MIME type\"}`);\n }\n }\n }\n add(\n \"ui-resource-mime\",\n wrongMime.length ? \"fail\" : \"pass\",\n wrongMime.length\n ? `${wrongMime.join(\"; \")}; expected ${RESOURCE_MIME_TYPE}`\n : `every ui:// resource is ${RESOURCE_MIME_TYPE}`,\n );\n\n // tool-resource-uri\n const unresolved: string[] = [];\n for (const tool of s.tools) {\n const uri = toolUi(tool)?.resourceUri;\n if (uri === undefined) {\n if (legacyBinding(tool)) {\n unresolved.push(`${tool.name} binds its UI only through the deprecated flat key`);\n }\n continue;\n }\n if (typeof uri !== \"string\" || !uri.startsWith(UI_SCHEME)) {\n unresolved.push(`${tool.name}: ui.resourceUri ${JSON.stringify(uri)} is not a ui:// URI`);\n continue;\n }\n const read = s.reads.get(uri);\n if (read instanceof Error || !read?.length) {\n unresolved.push(\n `${tool.name}: ${uri} cannot be read${read instanceof Error ? ` (${read.message})` : \"\"}`,\n );\n }\n }\n if (boundTools.length === 0 && unresolved.length === 0) {\n unresolved.push(\"no tool binds a UI through ui.resourceUri\");\n }\n add(\n \"tool-resource-uri\",\n unresolved.length ? \"fail\" : \"pass\",\n unresolved.length\n ? unresolved.join(\"; \")\n : `${boundTools.length} tool(s) bind a UI that resolves`,\n );\n\n // tool-ui-meta\n const badMeta: string[] = [];\n for (const tool of s.tools) {\n const ui = toolUi(tool);\n if (!ui) continue;\n const vis = ui.visibility as unknown;\n if (\n vis !== undefined &&\n (!Array.isArray(vis) || vis.length === 0 || !vis.every((v) => VISIBILITY.has(v)))\n ) {\n badMeta.push(`${tool.name}: ui.visibility ${JSON.stringify(vis)}`);\n }\n for (const key of [\"csp\", \"permissions\"] as const) {\n if ((ui as Record<string, unknown>)[key] !== undefined) {\n badMeta.push(`${tool.name}: ui.${key} is on the tool, where hosts ignore it`);\n }\n }\n }\n add(\n \"tool-ui-meta\",\n badMeta.length ? \"fail\" : \"pass\",\n badMeta.length ? badMeta.join(\"; \") : \"tool ui metadata is well-formed\",\n );\n\n // The frame's security policy, in each dialect a host reads, per UI resource.\n const noCsp: string[] = [];\n const noOpenAiCsp: string[] = [];\n const dataFonts: string[] = [];\n const openAiDataFonts: string[] = [];\n for (const [uri, contents] of s.reads) {\n if (contents instanceof Error) continue;\n for (const c of contents) {\n const listed = s.resources.find((r) => r.uri === uri);\n // Hosts read the content item's metadata, and fall back to the list entry.\n const ui = (resourceUi(c) ?? resourceUi(listed)) as McpUiResourceMeta | undefined;\n const csp = ui?.csp as unknown;\n if (csp === undefined) {\n noCsp.push(`${uri} declares no ui.csp`);\n } else if (!isOriginLists(csp)) {\n noCsp.push(`${uri}: ui.csp is not a map of origin lists`);\n }\n const openAi = openAiCsp(c) ?? openAiCsp(listed);\n if (openAi === undefined) {\n noOpenAiCsp.push(`${uri} declares no ${OPENAI_CSP_KEY}`);\n } else if (!isOriginLists(openAi)) {\n noOpenAiCsp.push(`${uri}: ${OPENAI_CSP_KEY} is not a map of origin lists`);\n } else {\n const camel = SPEC_CSP_KEYS.filter((k) => k in openAi);\n if (camel.length) {\n noOpenAiCsp.push(\n `${uri}: ${OPENAI_CSP_KEY} names ${camel.join(\", \")}, which OpenAI does not document for it`,\n );\n }\n }\n const html = \"text\" in c ? c.text : Buffer.from(c.blob, \"base64\").toString(\"utf-8\");\n if (hasDataFont(html)) {\n if (!declaresData(csp, \"resourceDomains\")) {\n dataFonts.push(`${uri} loads a data: font, and ui.csp.resourceDomains omits data:`);\n }\n if (!declaresData(openAi, \"resource_domains\")) {\n openAiDataFonts.push(\n `${uri} loads a data: font, and ${OPENAI_CSP_KEY}.resource_domains omits data:`,\n );\n }\n }\n }\n }\n add(\n \"resource-csp\",\n noCsp.length ? \"fail\" : \"pass\",\n noCsp.length ? noCsp.join(\"; \") : \"every UI resource declares ui.csp\",\n );\n add(\n \"resource-openai-csp\",\n noOpenAiCsp.length ? \"fail\" : \"pass\",\n noOpenAiCsp.length ? noOpenAiCsp.join(\"; \") : `every UI resource declares ${OPENAI_CSP_KEY}`,\n );\n add(\n \"resource-data-fonts\",\n dataFonts.length ? \"fail\" : \"pass\",\n dataFonts.length ? dataFonts.join(\"; \") : \"no data: font ui.csp omits\",\n );\n add(\n \"resource-openai-data-fonts\",\n openAiDataFonts.length ? \"fail\" : \"pass\",\n openAiDataFonts.length ? openAiDataFonts.join(\"; \") : `no data: font ${OPENAI_CSP_KEY} omits`,\n );\n }\n\n // tool-security-schemes: a question about auth, not about UI.\n if (!authRequired) {\n add(\"tool-security-schemes\", \"skip\", \"the server does not require auth\");\n } else {\n const missing = s.tools.filter((t) => !hasSecuritySchemes(t)).map((t) => t.name);\n add(\n \"tool-security-schemes\",\n missing.length ? \"fail\" : \"pass\",\n missing.length\n ? `no securitySchemes on: ${missing.join(\", \")}`\n : \"every tool declares securitySchemes\",\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// auth\n// ---------------------------------------------------------------------------\n\nasync function checkAuth(\n url: string,\n challenge: Response | undefined,\n add: (id: CheckId, status: CheckStatus, detail: string) => void,\n): Promise<void> {\n if (!challenge) {\n const reason = \"the server answered without a 401 challenge\";\n add(\"auth-challenge\", \"skip\", reason);\n add(\"auth-resource-metadata\", \"skip\", reason);\n add(\"auth-issuer\", \"skip\", reason);\n return;\n }\n\n const { resourceMetadataUrl } = extractWWWAuthenticateParams(challenge);\n add(\n \"auth-challenge\",\n resourceMetadataUrl ? \"pass\" : \"fail\",\n resourceMetadataUrl\n ? `401 names resource_metadata ${resourceMetadataUrl.href}`\n : `401 WWW-Authenticate carries no resource_metadata (${JSON.stringify(challenge.headers.get(\"www-authenticate\"))})`,\n );\n\n let prm: Awaited<ReturnType<typeof discoverOAuthProtectedResourceMetadata>>;\n try {\n // Without resource_metadata, a client falls back to the well-known path,\n // so the rest of the surface is still worth reporting.\n prm = await discoverOAuthProtectedResourceMetadata(url, { resourceMetadataUrl });\n } catch (err) {\n add(\n \"auth-resource-metadata\",\n \"fail\",\n `protected-resource metadata unreadable: ${message(err)}`,\n );\n add(\"auth-issuer\", \"skip\", \"no protected-resource metadata\");\n return;\n }\n add(\n \"auth-resource-metadata\",\n prm.resource === url ? \"pass\" : \"fail\",\n prm.resource === url\n ? `resource equals ${url}`\n : `resource is ${JSON.stringify(prm.resource)}, not ${JSON.stringify(url)}`,\n );\n\n const advertised = prm.authorization_servers?.[0];\n if (!advertised) {\n add(\"auth-issuer\", \"fail\", \"protected-resource metadata lists no authorization_servers\");\n return;\n }\n try {\n const metadata = await discoverAuthorizationServerMetadata(advertised);\n if (!metadata) {\n add(\"auth-issuer\", \"fail\", `no authorization server metadata found for ${advertised}`);\n } else if (metadata.issuer !== advertised) {\n add(\n \"auth-issuer\",\n \"fail\",\n `issuer is ${JSON.stringify(metadata.issuer)}, advertised as ${JSON.stringify(advertised)}`,\n );\n } else {\n add(\"auth-issuer\", \"pass\", `issuer equals ${advertised}`);\n }\n } catch (err) {\n add(\"auth-issuer\", \"fail\", `authorization server metadata unreadable: ${message(err)}`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// helpers\n// ---------------------------------------------------------------------------\n\nfunction toolUi(tool: Tool): McpUiToolMeta | undefined {\n const ui = tool._meta?.ui;\n return ui && typeof ui === \"object\" ? (ui as McpUiToolMeta) : undefined;\n}\n\nfunction legacyBinding(tool: Tool): boolean {\n return (\n tool._meta?.[RESOURCE_URI_META_KEY] !== undefined && toolUi(tool)?.resourceUri === undefined\n );\n}\n\nfunction resourceUi(item: { _meta?: Record<string, unknown> } | undefined): unknown {\n const ui = item?._meta?.ui;\n return ui && typeof ui === \"object\" ? ui : undefined;\n}\n\n/**\n * ChatGPT's dialect of the frame's security policy. It is a sibling of `ui`\n * in the resource's `_meta`, not a key inside it, and its origin lists are\n * spelled `connect_domains`/`resource_domains`. OpenAI documents it as a legacy\n * compatibility key and `ui.csp` as generally preferred for new UI. The two are\n * checked separately, and a target's profile sets each one's severity for its host.\n */\nconst OPENAI_CSP_KEY = \"openai/widgetCSP\";\n\n/** The spec's spellings, which under `openai/widgetCSP` name origins nothing reads. */\nconst SPEC_CSP_KEYS = [\"connectDomains\", \"resourceDomains\"];\n\nfunction openAiCsp(item: { _meta?: Record<string, unknown> } | undefined): unknown {\n return item?._meta?.[OPENAI_CSP_KEY];\n}\n\n/** The `{ name: [origin, ...] }` shape both CSP dialects have. */\nfunction isOriginLists(value: unknown): value is Record<string, string[]> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n Object.values(value).every((v) => Array.isArray(v) && v.every((o) => typeof o === \"string\"))\n );\n}\n\n/** Whether `csp` allows `data:` under the key naming static assets in its dialect. */\nfunction declaresData(csp: unknown, key: \"resourceDomains\" | \"resource_domains\"): boolean {\n return isOriginLists(csp) && (csp[key] ?? []).includes(\"data:\");\n}\n\nfunction hasSecuritySchemes(tool: Tool): boolean {\n const schemes = tool._meta?.securitySchemes ?? (tool as Record<string, unknown>).securitySchemes;\n return Array.isArray(schemes) && schemes.length > 0;\n}\n\nconst DATA_FONT_MIME = /data:(?:font\\/|application\\/(?:x-)?font|application\\/vnd\\.ms-fontobject)/i;\nconst FONT_FACE_DATA_URL = /@font-face\\s*{[^}]*url\\(\\s*[\"']?data:/i;\n\nfunction hasDataFont(html: string): boolean {\n return DATA_FONT_MIME.test(html) || FONT_FACE_DATA_URL.test(html);\n}\n\nfunction message(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n"]}
|
|
@@ -14,6 +14,7 @@ var EXTENSION = "A client uses MCP Apps only when both sides declare io.modelcon
|
|
|
14
14
|
var CHALLENGE = "Sign-in starts from a 401 whose WWW-Authenticate carries resource_metadata.";
|
|
15
15
|
var PRM_RESOURCE = "The protected-resource metadata's resource must equal the connector URL exactly.";
|
|
16
16
|
var ISSUER = "The authorization server's metadata issuer must equal the advertised authorization server exactly (RFC 8414).";
|
|
17
|
+
var SPEC_CSP = "ui.csp is the frame's security policy for Claude and the NimbleBrain host, and OpenAI documents it as generally preferred for new UI; measured 2026-09-18 in ChatGPT developer mode, the frame ran under ChatGPT's own default sandbox policy whichever dialect the resource carried.";
|
|
17
18
|
var PROFILES = {
|
|
18
19
|
nimblebrain: {
|
|
19
20
|
name: "nimblebrain",
|
|
@@ -62,11 +63,15 @@ var PROFILES = {
|
|
|
62
63
|
"tool-resource-uri": { severity: "error", why: RESOLVES },
|
|
63
64
|
"tool-ui-meta": { severity: "error", why: UI_META },
|
|
64
65
|
"extension-declared": { severity: "error", why: EXTENSION },
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
// A requirement the vendor documents and the host was not observed to
|
|
67
|
+
// enforce is what `warn` is for. Neither dialect has been observed to
|
|
68
|
+
// produce an enforced policy, so both sit at warn.
|
|
69
|
+
"resource-csp": { severity: "warn", why: SPEC_CSP },
|
|
70
|
+
"resource-openai-csp": {
|
|
71
|
+
severity: "warn",
|
|
72
|
+
why: "OpenAI documents openai/widgetCSP (snake_case origin lists) as a legacy compatibility surface, and it is still the only way to declare redirect_domains. Measured 2026-09-18 in developer mode, a resource carrying it beside ui.csp produced no enforced policy: the component ran under ChatGPT's default sandbox CSP either way. Declare it for the documented requirement; it is not known to change what the host enforces."
|
|
68
73
|
},
|
|
69
|
-
"resource-data-fonts": {
|
|
74
|
+
"resource-openai-data-fonts": {
|
|
70
75
|
severity: "warn",
|
|
71
76
|
why: "A data: font is blocked by a security policy that does not declare it."
|
|
72
77
|
},
|
|
@@ -157,7 +162,9 @@ var SESSION_CHECKS = [
|
|
|
157
162
|
"tool-resource-uri",
|
|
158
163
|
"tool-ui-meta",
|
|
159
164
|
"resource-csp",
|
|
165
|
+
"resource-openai-csp",
|
|
160
166
|
"resource-data-fonts",
|
|
167
|
+
"resource-openai-data-fonts",
|
|
161
168
|
"tool-security-schemes"
|
|
162
169
|
];
|
|
163
170
|
async function openSession(url, token, onResponse) {
|
|
@@ -303,7 +310,9 @@ function checkSession(s, authRequired, add) {
|
|
|
303
310
|
badMeta.length ? badMeta.join("; ") : "tool ui metadata is well-formed"
|
|
304
311
|
);
|
|
305
312
|
const noCsp = [];
|
|
313
|
+
const noOpenAiCsp = [];
|
|
306
314
|
const dataFonts = [];
|
|
315
|
+
const openAiDataFonts = [];
|
|
307
316
|
for (const [uri, contents] of s.reads) {
|
|
308
317
|
if (contents instanceof Error) continue;
|
|
309
318
|
for (const c of contents) {
|
|
@@ -312,13 +321,32 @@ function checkSession(s, authRequired, add) {
|
|
|
312
321
|
const csp = ui?.csp;
|
|
313
322
|
if (csp === void 0) {
|
|
314
323
|
noCsp.push(`${uri} declares no ui.csp`);
|
|
315
|
-
} else if (!
|
|
324
|
+
} else if (!isOriginLists(csp)) {
|
|
316
325
|
noCsp.push(`${uri}: ui.csp is not a map of origin lists`);
|
|
317
326
|
}
|
|
327
|
+
const openAi = openAiCsp(c) ?? openAiCsp(listed);
|
|
328
|
+
if (openAi === void 0) {
|
|
329
|
+
noOpenAiCsp.push(`${uri} declares no ${OPENAI_CSP_KEY}`);
|
|
330
|
+
} else if (!isOriginLists(openAi)) {
|
|
331
|
+
noOpenAiCsp.push(`${uri}: ${OPENAI_CSP_KEY} is not a map of origin lists`);
|
|
332
|
+
} else {
|
|
333
|
+
const camel = SPEC_CSP_KEYS.filter((k) => k in openAi);
|
|
334
|
+
if (camel.length) {
|
|
335
|
+
noOpenAiCsp.push(
|
|
336
|
+
`${uri}: ${OPENAI_CSP_KEY} names ${camel.join(", ")}, which OpenAI does not document for it`
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
318
340
|
const html = "text" in c ? c.text : Buffer.from(c.blob, "base64").toString("utf-8");
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
341
|
+
if (hasDataFont(html)) {
|
|
342
|
+
if (!declaresData(csp, "resourceDomains")) {
|
|
343
|
+
dataFonts.push(`${uri} loads a data: font, and ui.csp.resourceDomains omits data:`);
|
|
344
|
+
}
|
|
345
|
+
if (!declaresData(openAi, "resource_domains")) {
|
|
346
|
+
openAiDataFonts.push(
|
|
347
|
+
`${uri} loads a data: font, and ${OPENAI_CSP_KEY}.resource_domains omits data:`
|
|
348
|
+
);
|
|
349
|
+
}
|
|
322
350
|
}
|
|
323
351
|
}
|
|
324
352
|
}
|
|
@@ -327,10 +355,20 @@ function checkSession(s, authRequired, add) {
|
|
|
327
355
|
noCsp.length ? "fail" : "pass",
|
|
328
356
|
noCsp.length ? noCsp.join("; ") : "every UI resource declares ui.csp"
|
|
329
357
|
);
|
|
358
|
+
add(
|
|
359
|
+
"resource-openai-csp",
|
|
360
|
+
noOpenAiCsp.length ? "fail" : "pass",
|
|
361
|
+
noOpenAiCsp.length ? noOpenAiCsp.join("; ") : `every UI resource declares ${OPENAI_CSP_KEY}`
|
|
362
|
+
);
|
|
330
363
|
add(
|
|
331
364
|
"resource-data-fonts",
|
|
332
365
|
dataFonts.length ? "fail" : "pass",
|
|
333
|
-
dataFonts.length ? dataFonts.join("; ") : "no
|
|
366
|
+
dataFonts.length ? dataFonts.join("; ") : "no data: font ui.csp omits"
|
|
367
|
+
);
|
|
368
|
+
add(
|
|
369
|
+
"resource-openai-data-fonts",
|
|
370
|
+
openAiDataFonts.length ? "fail" : "pass",
|
|
371
|
+
openAiDataFonts.length ? openAiDataFonts.join("; ") : `no data: font ${OPENAI_CSP_KEY} omits`
|
|
334
372
|
);
|
|
335
373
|
}
|
|
336
374
|
if (!authRequired) {
|
|
@@ -408,9 +446,17 @@ function resourceUi(item) {
|
|
|
408
446
|
const ui = item?._meta?.ui;
|
|
409
447
|
return ui && typeof ui === "object" ? ui : void 0;
|
|
410
448
|
}
|
|
411
|
-
|
|
449
|
+
var OPENAI_CSP_KEY = "openai/widgetCSP";
|
|
450
|
+
var SPEC_CSP_KEYS = ["connectDomains", "resourceDomains"];
|
|
451
|
+
function openAiCsp(item) {
|
|
452
|
+
return item?._meta?.[OPENAI_CSP_KEY];
|
|
453
|
+
}
|
|
454
|
+
function isOriginLists(value) {
|
|
412
455
|
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.values(value).every((v) => Array.isArray(v) && v.every((o) => typeof o === "string"));
|
|
413
456
|
}
|
|
457
|
+
function declaresData(csp, key) {
|
|
458
|
+
return isOriginLists(csp) && (csp[key] ?? []).includes("data:");
|
|
459
|
+
}
|
|
414
460
|
function hasSecuritySchemes(tool) {
|
|
415
461
|
const schemes = tool._meta?.securitySchemes ?? tool.securitySchemes;
|
|
416
462
|
return Array.isArray(schemes) && schemes.length > 0;
|
|
@@ -425,5 +471,5 @@ function message(err) {
|
|
|
425
471
|
}
|
|
426
472
|
|
|
427
473
|
export { PROFILES, applyProfiles, formatReport, isTargetName, runChecks };
|
|
428
|
-
//# sourceMappingURL=check-
|
|
429
|
-
//# sourceMappingURL=check-
|
|
474
|
+
//# sourceMappingURL=check-CSE4XQRL.js.map
|
|
475
|
+
//# sourceMappingURL=check-CSE4XQRL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/check/profiles.ts","../src/check/index.ts"],"names":[],"mappings":";;;;;;;;;AAuCA,IAAM,SAAA,GAAY,uDAAA;AAClB,IAAM,QAAA,GAAW,uEAAA;AACjB,IAAM,OAAA,GACJ,6GAAA;AACF,IAAM,SAAA,GAAY,iFAAA;AAClB,IAAM,SAAA,GAAY,6EAAA;AAClB,IAAM,YAAA,GACJ,kFAAA;AACF,IAAM,MAAA,GACJ,+GAAA;AACF,IAAM,QAAA,GACJ,uRAAA;AAEK,IAAM,QAAA,GAAwC;AAAA,EACnD,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,aAAA;AAAA,IACN,WAAA,EAAa,uBAAA;AAAA,IACb,KAAA,EAAO;AAAA,MACL,kBAAA,EAAoB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,SAAA,EAAU;AAAA,MACxD,mBAAA,EAAqB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,QAAA,EAAS;AAAA,MACxD,cAAA,EAAgB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,OAAA,EAAQ;AAAA;AAAA;AAAA,MAGlD,oBAAA,EAAsB,EAAE,QAAA,EAAU,MAAA,EAAQ,KAAK,SAAA,EAAU;AAAA;AAAA,MAEzD,gBAAA,EAAkB,EAAE,QAAA,EAAU,MAAA,EAAQ,KAAK,SAAA,EAAU;AAAA,MACrD,wBAAA,EAA0B,EAAE,QAAA,EAAU,MAAA,EAAQ,KAAK,YAAA,EAAa;AAAA,MAChE,aAAA,EAAe,EAAE,QAAA,EAAU,MAAA,EAAQ,KAAK,MAAA;AAAO;AACjD,GACF;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM,QAAA;AAAA,IACN,WAAA,EAAa,+BAAA;AAAA,IACb,KAAA,EAAO;AAAA,MACL,kBAAA,EAAoB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,SAAA,EAAU;AAAA,MACxD,mBAAA,EAAqB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,QAAA,EAAS;AAAA,MACxD,cAAA,EAAgB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,OAAA,EAAQ;AAAA,MAClD,oBAAA,EAAsB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,SAAA,EAAU;AAAA,MAC1D,cAAA,EAAgB;AAAA,QACd,QAAA,EAAU,MAAA;AAAA,QACV,GAAA,EAAK;AAAA,OACP;AAAA,MACA,qBAAA,EAAuB;AAAA,QACrB,QAAA,EAAU,OAAA;AAAA,QACV,GAAA,EAAK;AAAA,OACP;AAAA,MACA,gBAAA,EAAkB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,SAAA,EAAU;AAAA,MACtD,wBAAA,EAA0B,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,YAAA,EAAa;AAAA;AAAA;AAAA,MAGjE,aAAA,EAAe,EAAE,QAAA,EAAU,MAAA,EAAQ,KAAK,MAAA;AAAO;AACjD,GACF;AAAA,EACA,OAAA,EAAS;AAAA,IACP,IAAA,EAAM,SAAA;AAAA,IACN,WAAA,EAAa,eAAA;AAAA,IACb,KAAA,EAAO;AAAA,MACL,kBAAA,EAAoB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,SAAA,EAAU;AAAA,MACxD,mBAAA,EAAqB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,QAAA,EAAS;AAAA,MACxD,cAAA,EAAgB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,OAAA,EAAQ;AAAA,MAClD,oBAAA,EAAsB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,SAAA,EAAU;AAAA;AAAA;AAAA;AAAA,MAI1D,cAAA,EAAgB,EAAE,QAAA,EAAU,MAAA,EAAQ,KAAK,QAAA,EAAS;AAAA,MAClD,qBAAA,EAAuB;AAAA,QACrB,QAAA,EAAU,MAAA;AAAA,QACV,GAAA,EAAK;AAAA,OACP;AAAA,MACA,4BAAA,EAA8B;AAAA,QAC5B,QAAA,EAAU,MAAA;AAAA,QACV,GAAA,EAAK;AAAA,OACP;AAAA,MACA,gBAAA,EAAkB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,SAAA,EAAU;AAAA,MACtD,wBAAA,EAA0B,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,YAAA,EAAa;AAAA,MACjE,aAAA,EAAe,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,MAAA,EAAO;AAAA,MAChD,uBAAA,EAAyB;AAAA,QACvB,QAAA,EAAU,OAAA;AAAA,QACV,GAAA,EAAK;AAAA;AACP;AACF;AAEJ;AAEO,SAAS,aAAa,KAAA,EAAoC;AAC/D,EAAA,OAAO,MAAA,CAAO,MAAA,CAAO,QAAA,EAAU,KAAK,CAAA;AACtC;;;ACpEA,IAAM,SAAA,GAAY,OAAA;AAClB,IAAM,6BAAa,IAAI,GAAA,CAAI,CAAC,OAAA,EAAS,KAAK,CAAC,CAAA;AAY3C,eAAsB,SAAA,CAAU,GAAA,EAAa,OAAA,GAAwB,EAAC,EAA2B;AAC/F,EAAA,MAAM,UAAyB,EAAC;AAChC,EAAA,MAAM,GAAA,GAAM,CAAC,EAAA,EAAa,MAAA,EAAqB,MAAA,KAC7C,OAAA,CAAQ,IAAA,CAAK,EAAE,EAAA,EAAI,MAAA,EAAQ,MAAA,EAAQ,CAAA;AAIrC,EAAA,IAAI,SAAA;AACJ,EAAA,IAAI,OAAA;AACJ,EAAA,IAAI,YAAA;AACJ,EAAA,IAAI;AACF,IAAA,OAAA,GAAU,MAAM,WAAA,CAAY,GAAA,EAAK,KAAA,CAAA,EAAW,CAAC,GAAA,KAAQ;AACnD,MAAA,IAAI,GAAA,CAAI,MAAA,KAAW,GAAA,EAAK,SAAA,KAAc,GAAA;AAAA,IACxC,CAAC,CAAA;AAAA,EACH,SAAS,GAAA,EAAK;AACZ,IAAA,YAAA,GAAe,GAAA;AAAA,EACjB;AAEA,EAAA,MAAM,eAAe,SAAA,KAAc,MAAA;AACnC,EAAA,IAAI,YAAA,IAAgB,QAAQ,KAAA,EAAO;AACjC,IAAA,IAAI;AACF,MAAA,OAAA,GAAU,MAAM,WAAA,CAAY,GAAA,EAAK,OAAA,CAAQ,KAAK,CAAA;AAC9C,MAAA,YAAA,GAAe,KAAA,CAAA;AAAA,IACjB,SAAS,GAAA,EAAK;AACZ,MAAA,YAAA,GAAe,GAAA;AAAA,IACjB;AAAA,EACF;AAGA,EAAA,IAAI,CAAC,OAAA,KAAY,CAAC,YAAA,IAAgB,QAAQ,KAAA,CAAA,EAAQ;AAChD,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,qBAAA,EAAwB,GAAG,CAAA,EAAG,YAAA,GAAe,0BAA0B,EAAE,CAAA,EAAA,EAAK,OAAA,CAAQ,YAAY,CAAC,CAAA;AAAA,KACrG;AAAA,EACF;AAEA,EAAA,MAAM,SAAA,CAAU,GAAA,EAAK,SAAA,EAAW,GAAG,CAAA;AAEnC,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,KAAA,MAAW,MAAM,cAAA,EAAgB;AAC/B,MAAA,GAAA,CAAI,EAAA,EAAI,QAAQ,kDAAkD,CAAA;AAAA,IACpE;AACA,IAAA,OAAO,OAAA;AAAA,EACT;AAEA,EAAA,YAAA,CAAa,OAAA,EAAS,cAAc,GAAG,CAAA;AACvC,EAAA,OAAO,OAAA;AACT;AAGO,SAAS,aAAA,CAAc,SAAwB,OAAA,EAAuC;AAC3F,EAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,CAAC,MAAA,KAAW;AAC7B,IAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,MAAM,CAAA,CAAE,KAAA;AAC/B,IAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,OAAA,CAAQ,CAAC,CAAA,KAAM;AACpC,MAAA,MAAM,IAAA,GAAO,KAAA,CAAM,CAAA,CAAE,EAAE,CAAA;AACvB,MAAA,OAAO,IAAA,GAAO,CAAC,EAAE,GAAG,CAAA,EAAG,QAAA,EAAU,IAAA,CAAK,QAAA,EAAU,GAAA,EAAK,IAAA,CAAK,GAAA,EAAK,IAAI,EAAC;AAAA,IACtE,CAAC,CAAA;AACD,IAAA,OAAO;AAAA,MACL,MAAA;AAAA,MACA,OAAA,EAAS,MAAA;AAAA,MACT,MAAA,EAAQ,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,KAAM,EAAE,MAAA,KAAW,MAAA,IAAU,CAAA,CAAE,QAAA,KAAa,OAAO;AAAA,KAC1E;AAAA,EACF,CAAC,CAAA;AACH;AAGO,SAAS,YAAA,CAAa,KAAa,OAAA,EAAiC;AACzE,EAAA,MAAM,QAAQ,IAAA,CAAK,GAAA,CAAI,GAAG,OAAA,CAAQ,OAAA,CAAQ,CAAC,CAAA,KAAM,CAAA,CAAE,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;AACtF,EAAA,MAAM,KAAA,GAAQ,CAAC,CAAA,cAAA,EAAiB,GAAG,CAAA,CAAE,CAAA;AACrC,EAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,IAAA,KAAA,CAAM,IAAA,CAAK,EAAA,EAAI,CAAA,EAAG,MAAA,CAAO,MAAM,KAAK,MAAA,CAAO,MAAA,GAAS,MAAA,GAAS,IAAI,CAAA,CAAE,CAAA;AACnE,IAAA,KAAA,MAAW,CAAA,IAAK,OAAO,OAAA,EAAS;AAC9B,MAAA,MAAM,KAAA,GACJ,CAAA,CAAE,MAAA,KAAW,MAAA,GAAU,CAAA,CAAE,QAAA,KAAa,OAAA,GAAU,MAAA,GAAS,MAAA,GAAU,CAAA,CAAE,MAAA,CAAO,WAAA,EAAY;AAC1F,MAAA,KAAA,CAAM,IAAA,CAAK,CAAA,EAAA,EAAK,KAAA,CAAM,MAAA,CAAO,CAAC,CAAC,CAAA,EAAA,EAAK,CAAA,CAAE,EAAA,CAAG,OAAO,KAAK,CAAC,CAAA,EAAA,EAAK,CAAA,CAAE,MAAM,CAAA,CAAE,CAAA;AACrE,MAAA,IAAI,CAAA,CAAE,MAAA,KAAW,MAAA,EAAQ,KAAA,CAAM,IAAA,CAAK,CAAA,QAAA,EAAW,EAAA,CAAG,MAAA,CAAO,KAAK,CAAC,CAAA,EAAA,EAAK,CAAA,CAAE,GAAG,CAAA,CAAE,CAAA;AAAA,IAC7E;AAAA,EACF;AACA,EAAA,OAAO,KAAA,CAAM,KAAK,IAAI,CAAA;AACxB;AAMA,IAAM,cAAA,GAA4B;AAAA,EAChC,oBAAA;AAAA,EACA,kBAAA;AAAA,EACA,mBAAA;AAAA,EACA,cAAA;AAAA,EACA,cAAA;AAAA,EACA,qBAAA;AAAA,EACA,qBAAA;AAAA,EACA,4BAAA;AAAA,EACA;AACF,CAAA;AAEA,eAAe,WAAA,CACb,GAAA,EACA,KAAA,EACA,UAAA,EACkB;AAGlB,EAAA,MAAM,EAAA,GAA8B,EAAE,SAAA,EAAW,CAAC,kBAAkB,CAAA,EAAE;AACtE,EAAA,MAAM,SAAS,IAAI,MAAA;AAAA,IACjB,EAAE,IAAA,EAAM,eAAA,EAAiB,OAAA,EAAS,GAAA,EAAI;AAAA,IACtC,EAAE,YAAA,EAAc,EAAE,UAAA,EAAY,EAAE,CAAC,YAAY,GAAG,EAAA,EAAG,EAAE;AAAE,GACzD;AACA,EAAA,MAAM,YAAY,IAAI,6BAAA,CAA8B,IAAI,GAAA,CAAI,GAAG,CAAA,EAAG;AAAA,IAChE,WAAA,EAAa,KAAA,GAAQ,EAAE,OAAA,EAAS,EAAE,eAAe,CAAA,OAAA,EAAU,KAAK,CAAA,CAAA,EAAG,EAAE,GAAI,MAAA;AAAA,IACzE,KAAA,EAAO,OAAO,KAAA,EAAO,IAAA,KAAS;AAC5B,MAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,KAAA,EAAO,IAAI,CAAA;AACnC,MAAA,UAAA,GAAa,GAAG,CAAA;AAChB,MAAA,OAAO,GAAA;AAAA,IACT;AAAA,GACD,CAAA;AACD,EAAA,MAAM,MAAA,CAAO,QAAQ,SAAS,CAAA;AAC9B,EAAA,IAAI;AACF,IAAA,MAAM,YAAA,GAAe,OAAO,qBAAA,EAAsB;AAClD,IAAA,MAAM,QAAgB,EAAC;AACvB,IAAA,MAAM,YAAwB,EAAC;AAC/B,IAAA,IAAI,MAAA;AACJ,IAAA,IAAI,cAAc,KAAA,EAAO;AACvB,MAAA,GAAG;AAKD,QAAA,MAAM,GAAA,GAAM,MAAM,MAAA,CAAO,OAAA;AAAA,UACvB,EAAE,QAAQ,YAAA,EAAc,MAAA,EAAQ,SAAS,EAAE,MAAA,EAAO,GAAI,EAAC,EAAE;AAAA,UACzD;AAAA,SACF;AACA,QAAA,MAAM,IAAA,GAAO,qBAAA,CAAsB,KAAA,CAAM,GAAG,CAAA;AAC5C,QAAA,MAAM,QAAA,GAAY,GAAA,CAAmD,KAAA,IAAS,EAAC;AAC/E,QAAA,IAAA,CAAK,KAAA,CAAM,OAAA,CAAQ,CAAC,IAAA,EAAM,CAAA,KAAM;AAC9B,UAAA,MAAM,OAAA,GAAU,QAAA,CAAS,CAAC,CAAA,EAAG,eAAA;AAC7B,UAAA,IAAI,OAAA,KAAY,KAAA,CAAA,EAAY,IAAA,CAAiC,eAAA,GAAkB,OAAA;AAAA,QACjF,CAAC,CAAA;AACD,QAAA,KAAA,CAAM,IAAA,CAAK,GAAG,IAAA,CAAK,KAAK,CAAA;AACxB,QAAA,MAAA,GAAS,IAAA,CAAK,UAAA;AAAA,MAChB,CAAA,QAAS,MAAA;AAAA,IACX;AACA,IAAA,IAAI,cAAc,SAAA,EAAW;AAC3B,MAAA,GAAG;AACD,QAAA,MAAM,OAAO,MAAM,MAAA,CAAO,aAAA,CAAc,EAAE,QAAQ,CAAA;AAClD,QAAA,SAAA,CAAU,IAAA,CAAK,GAAG,IAAA,CAAK,SAAS,CAAA;AAChC,QAAA,MAAA,GAAS,IAAA,CAAK,UAAA;AAAA,MAChB,CAAA,QAAS,MAAA;AAAA,IACX;AAEA,IAAA,MAAM,OAAO,IAAI,GAAA,CAAI,SAAA,CAAU,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,GAAG,CAAA,CAAE,OAAO,CAAC,CAAA,KAAM,EAAE,UAAA,CAAW,SAAS,CAAC,CAAC,CAAA;AACvF,IAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,MAAA,MAAM,GAAA,GAAM,MAAA,CAAO,IAAI,CAAA,EAAG,WAAA;AAC1B,MAAA,IAAI,OAAO,GAAA,KAAQ,QAAA,EAAU,IAAA,CAAK,IAAI,GAAG,CAAA;AAAA,IAC3C;AACA,IAAA,MAAM,KAAA,uBAAY,GAAA,EAAwC;AAC1D,IAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,MAAA,IAAI;AACF,QAAA,KAAA,CAAM,GAAA,CAAI,MAAM,MAAM,MAAA,CAAO,aAAa,EAAE,GAAA,EAAK,CAAA,EAAG,QAAQ,CAAA;AAAA,MAC9D,SAAS,GAAA,EAAK;AACZ,QAAA,KAAA,CAAM,GAAA,CAAI,GAAA,EAAK,GAAA,YAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,KAAA,CAAM,MAAA,CAAO,GAAG,CAAC,CAAC,CAAA;AAAA,MACpE;AAAA,IACF;AACA,IAAA,OAAO,EAAE,YAAA,EAAc,KAAA,EAAO,SAAA,EAAW,KAAA,EAAM;AAAA,EACjD,CAAA,SAAE;AACA,IAAA,MAAM,OAAO,KAAA,EAAM;AAAA,EACrB;AACF;AAEA,SAAS,YAAA,CACP,CAAA,EACA,YAAA,EACA,GAAA,EACM;AACN,EAAA,MAAM,UAAA,GAAa,CAAA,CAAE,KAAA,CAAM,MAAA,CAAO,CAAC,MAAM,MAAA,CAAO,CAAC,CAAA,EAAG,WAAA,KAAgB,MAAS,CAAA;AAC7E,EAAA,MAAM,WAAA,GAAc,CAAA,CAAE,SAAA,CAAU,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,GAAA,CAAI,UAAA,CAAW,SAAS,CAAC,CAAA;AACzE,EAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,MAAA,GAAS,CAAA,IAAK,WAAA,CAAY,SAAS,CAAA,IAAK,CAAA,CAAE,KAAA,CAAM,IAAA,CAAK,aAAa,CAAA;AAE3F,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,KAAA,MAAW,MAAM,cAAA,EAAgB;AAC/B,MAAA,IAAI,EAAA,KAAO,uBAAA,EAAyB,GAAA,CAAI,EAAA,EAAI,QAAQ,0BAA0B,CAAA;AAAA,IAChF;AAAA,EACF,CAAA,MAAO;AAEL,IAAA,MAAM,QAAA,GAAW,CAAA,CAAE,YAAA,EAAc,UAAA,GAAa,YAAY,CAAA,KAAM,MAAA;AAChE,IAAA,GAAA;AAAA,MACE,oBAAA;AAAA,MACA,WAAW,MAAA,GAAS,MAAA;AAAA,MACpB,QAAA,GACI,CAAA,EAAG,YAAY,CAAA,YAAA,CAAA,GACf,+BAA+B,YAAY,CAAA,2BAAA;AAAA,KACjD;AAGA,IAAA,MAAM,YAAsB,EAAC;AAC7B,IAAA,KAAA,MAAW,KAAK,WAAA,EAAa;AAC3B,MAAA,IAAI,CAAA,CAAE,QAAA,KAAa,MAAA,IAAa,CAAA,CAAE,aAAa,kBAAA,EAAoB;AACjE,QAAA,SAAA,CAAU,KAAK,CAAA,EAAG,CAAA,CAAE,GAAG,CAAA,cAAA,EAAiB,CAAA,CAAE,QAAQ,CAAA,CAAE,CAAA;AAAA,MACtD;AAAA,IACF;AACA,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,QAAQ,CAAA,IAAK,EAAE,KAAA,EAAO;AACrC,MAAA,IAAI,oBAAoB,KAAA,EAAO;AAC/B,MAAA,KAAA,MAAW,KAAK,QAAA,EAAU;AACxB,QAAA,IAAI,CAAA,CAAE,aAAa,kBAAA,EAAoB;AACrC,UAAA,SAAA,CAAU,KAAK,CAAA,EAAG,GAAG,iBAAiB,CAAA,CAAE,QAAA,IAAY,cAAc,CAAA,CAAE,CAAA;AAAA,QACtE;AAAA,MACF;AAAA,IACF;AACA,IAAA,GAAA;AAAA,MACE,kBAAA;AAAA,MACA,SAAA,CAAU,SAAS,MAAA,GAAS,MAAA;AAAA,MAC5B,SAAA,CAAU,MAAA,GACN,CAAA,EAAG,SAAA,CAAU,IAAA,CAAK,IAAI,CAAC,CAAA,WAAA,EAAc,kBAAkB,CAAA,CAAA,GACvD,CAAA,wBAAA,EAA2B,kBAAkB,CAAA;AAAA,KACnD;AAGA,IAAA,MAAM,aAAuB,EAAC;AAC9B,IAAA,KAAA,MAAW,IAAA,IAAQ,EAAE,KAAA,EAAO;AAC1B,MAAA,MAAM,GAAA,GAAM,MAAA,CAAO,IAAI,CAAA,EAAG,WAAA;AAC1B,MAAA,IAAI,QAAQ,MAAA,EAAW;AACrB,QAAA,IAAI,aAAA,CAAc,IAAI,CAAA,EAAG;AACvB,UAAA,UAAA,CAAW,IAAA,CAAK,CAAA,EAAG,IAAA,CAAK,IAAI,CAAA,kDAAA,CAAoD,CAAA;AAAA,QAClF;AACA,QAAA;AAAA,MACF;AACA,MAAA,IAAI,OAAO,GAAA,KAAQ,QAAA,IAAY,CAAC,GAAA,CAAI,UAAA,CAAW,SAAS,CAAA,EAAG;AACzD,QAAA,UAAA,CAAW,IAAA,CAAK,GAAG,IAAA,CAAK,IAAI,oBAAoB,IAAA,CAAK,SAAA,CAAU,GAAG,CAAC,CAAA,mBAAA,CAAqB,CAAA;AACxF,QAAA;AAAA,MACF;AACA,MAAA,MAAM,IAAA,GAAO,CAAA,CAAE,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AAC5B,MAAA,IAAI,IAAA,YAAgB,KAAA,IAAS,CAAC,IAAA,EAAM,MAAA,EAAQ;AAC1C,QAAA,UAAA,CAAW,IAAA;AAAA,UACT,CAAA,EAAG,IAAA,CAAK,IAAI,CAAA,EAAA,EAAK,GAAG,CAAA,eAAA,EAAkB,IAAA,YAAgB,KAAA,GAAQ,CAAA,EAAA,EAAK,IAAA,CAAK,OAAO,CAAA,CAAA,CAAA,GAAM,EAAE,CAAA;AAAA,SACzF;AAAA,MACF;AAAA,IACF;AACA,IAAA,IAAI,UAAA,CAAW,MAAA,KAAW,CAAA,IAAK,UAAA,CAAW,WAAW,CAAA,EAAG;AACtD,MAAA,UAAA,CAAW,KAAK,2CAA2C,CAAA;AAAA,IAC7D;AACA,IAAA,GAAA;AAAA,MACE,mBAAA;AAAA,MACA,UAAA,CAAW,SAAS,MAAA,GAAS,MAAA;AAAA,MAC7B,UAAA,CAAW,SACP,UAAA,CAAW,IAAA,CAAK,IAAI,CAAA,GACpB,CAAA,EAAG,WAAW,MAAM,CAAA,gCAAA;AAAA,KAC1B;AAGA,IAAA,MAAM,UAAoB,EAAC;AAC3B,IAAA,KAAA,MAAW,IAAA,IAAQ,EAAE,KAAA,EAAO;AAC1B,MAAA,MAAM,EAAA,GAAK,OAAO,IAAI,CAAA;AACtB,MAAA,IAAI,CAAC,EAAA,EAAI;AACT,MAAA,MAAM,MAAM,EAAA,CAAG,UAAA;AACf,MAAA,IACE,QAAQ,MAAA,KACP,CAAC,MAAM,OAAA,CAAQ,GAAG,KAAK,GAAA,CAAI,MAAA,KAAW,KAAK,CAAC,GAAA,CAAI,MAAM,CAAC,CAAA,KAAM,WAAW,GAAA,CAAI,CAAC,CAAC,CAAA,CAAA,EAC/E;AACA,QAAA,OAAA,CAAQ,IAAA,CAAK,GAAG,IAAA,CAAK,IAAI,mBAAmB,IAAA,CAAK,SAAA,CAAU,GAAG,CAAC,CAAA,CAAE,CAAA;AAAA,MACnE;AACA,MAAA,KAAA,MAAW,GAAA,IAAO,CAAC,KAAA,EAAO,aAAa,CAAA,EAAY;AACjD,QAAA,IAAK,EAAA,CAA+B,GAAG,CAAA,KAAM,MAAA,EAAW;AACtD,UAAA,OAAA,CAAQ,KAAK,CAAA,EAAG,IAAA,CAAK,IAAI,CAAA,KAAA,EAAQ,GAAG,CAAA,sCAAA,CAAwC,CAAA;AAAA,QAC9E;AAAA,MACF;AAAA,IACF;AACA,IAAA,GAAA;AAAA,MACE,cAAA;AAAA,MACA,OAAA,CAAQ,SAAS,MAAA,GAAS,MAAA;AAAA,MAC1B,OAAA,CAAQ,MAAA,GAAS,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAA,GAAI;AAAA,KACxC;AAGA,IAAA,MAAM,QAAkB,EAAC;AACzB,IAAA,MAAM,cAAwB,EAAC;AAC/B,IAAA,MAAM,YAAsB,EAAC;AAC7B,IAAA,MAAM,kBAA4B,EAAC;AACnC,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,QAAQ,CAAA,IAAK,EAAE,KAAA,EAAO;AACrC,MAAA,IAAI,oBAAoB,KAAA,EAAO;AAC/B,MAAA,KAAA,MAAW,KAAK,QAAA,EAAU;AACxB,QAAA,MAAM,MAAA,GAAS,EAAE,SAAA,CAAU,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,QAAQ,GAAG,CAAA;AAEpD,QAAA,MAAM,EAAA,GAAM,UAAA,CAAW,CAAC,CAAA,IAAK,WAAW,MAAM,CAAA;AAC9C,QAAA,MAAM,MAAM,EAAA,EAAI,GAAA;AAChB,QAAA,IAAI,QAAQ,MAAA,EAAW;AACrB,UAAA,KAAA,CAAM,IAAA,CAAK,CAAA,EAAG,GAAG,CAAA,mBAAA,CAAqB,CAAA;AAAA,QACxC,CAAA,MAAA,IAAW,CAAC,aAAA,CAAc,GAAG,CAAA,EAAG;AAC9B,UAAA,KAAA,CAAM,IAAA,CAAK,CAAA,EAAG,GAAG,CAAA,qCAAA,CAAuC,CAAA;AAAA,QAC1D;AACA,QAAA,MAAM,MAAA,GAAS,SAAA,CAAU,CAAC,CAAA,IAAK,UAAU,MAAM,CAAA;AAC/C,QAAA,IAAI,WAAW,MAAA,EAAW;AACxB,UAAA,WAAA,CAAY,IAAA,CAAK,CAAA,EAAG,GAAG,CAAA,aAAA,EAAgB,cAAc,CAAA,CAAE,CAAA;AAAA,QACzD,CAAA,MAAA,IAAW,CAAC,aAAA,CAAc,MAAM,CAAA,EAAG;AACjC,UAAA,WAAA,CAAY,IAAA,CAAK,CAAA,EAAG,GAAG,CAAA,EAAA,EAAK,cAAc,CAAA,6BAAA,CAA+B,CAAA;AAAA,QAC3E,CAAA,MAAO;AACL,UAAA,MAAM,QAAQ,aAAA,CAAc,MAAA,CAAO,CAAC,CAAA,KAAM,KAAK,MAAM,CAAA;AACrD,UAAA,IAAI,MAAM,MAAA,EAAQ;AAChB,YAAA,WAAA,CAAY,IAAA;AAAA,cACV,CAAA,EAAG,GAAG,CAAA,EAAA,EAAK,cAAc,UAAU,KAAA,CAAM,IAAA,CAAK,IAAI,CAAC,CAAA,uCAAA;AAAA,aACrD;AAAA,UACF;AAAA,QACF;AACA,QAAA,MAAM,IAAA,GAAO,MAAA,IAAU,CAAA,GAAI,CAAA,CAAE,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,CAAA,CAAE,IAAA,EAAM,QAAQ,CAAA,CAAE,QAAA,CAAS,OAAO,CAAA;AAClF,QAAA,IAAI,WAAA,CAAY,IAAI,CAAA,EAAG;AACrB,UAAA,IAAI,CAAC,YAAA,CAAa,GAAA,EAAK,iBAAiB,CAAA,EAAG;AACzC,YAAA,SAAA,CAAU,IAAA,CAAK,CAAA,EAAG,GAAG,CAAA,2DAAA,CAA6D,CAAA;AAAA,UACpF;AACA,UAAA,IAAI,CAAC,YAAA,CAAa,MAAA,EAAQ,kBAAkB,CAAA,EAAG;AAC7C,YAAA,eAAA,CAAgB,IAAA;AAAA,cACd,CAAA,EAAG,GAAG,CAAA,yBAAA,EAA4B,cAAc,CAAA,6BAAA;AAAA,aAClD;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,IAAA,GAAA;AAAA,MACE,cAAA;AAAA,MACA,KAAA,CAAM,SAAS,MAAA,GAAS,MAAA;AAAA,MACxB,KAAA,CAAM,MAAA,GAAS,KAAA,CAAM,IAAA,CAAK,IAAI,CAAA,GAAI;AAAA,KACpC;AACA,IAAA,GAAA;AAAA,MACE,qBAAA;AAAA,MACA,WAAA,CAAY,SAAS,MAAA,GAAS,MAAA;AAAA,MAC9B,YAAY,MAAA,GAAS,WAAA,CAAY,KAAK,IAAI,CAAA,GAAI,8BAA8B,cAAc,CAAA;AAAA,KAC5F;AACA,IAAA,GAAA;AAAA,MACE,qBAAA;AAAA,MACA,SAAA,CAAU,SAAS,MAAA,GAAS,MAAA;AAAA,MAC5B,SAAA,CAAU,MAAA,GAAS,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA,GAAI;AAAA,KAC5C;AACA,IAAA,GAAA;AAAA,MACE,4BAAA;AAAA,MACA,eAAA,CAAgB,SAAS,MAAA,GAAS,MAAA;AAAA,MAClC,gBAAgB,MAAA,GAAS,eAAA,CAAgB,KAAK,IAAI,CAAA,GAAI,iBAAiB,cAAc,CAAA,MAAA;AAAA,KACvF;AAAA,EACF;AAGA,EAAA,IAAI,CAAC,YAAA,EAAc;AACjB,IAAA,GAAA,CAAI,uBAAA,EAAyB,QAAQ,kCAAkC,CAAA;AAAA,EACzE,CAAA,MAAO;AACL,IAAA,MAAM,OAAA,GAAU,CAAA,CAAE,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA,KAAM,CAAC,kBAAA,CAAmB,CAAC,CAAC,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,IAAI,CAAA;AAC/E,IAAA,GAAA;AAAA,MACE,uBAAA;AAAA,MACA,OAAA,CAAQ,SAAS,MAAA,GAAS,MAAA;AAAA,MAC1B,QAAQ,MAAA,GACJ,CAAA,uBAAA,EAA0B,QAAQ,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA,GAC5C;AAAA,KACN;AAAA,EACF;AACF;AAMA,eAAe,SAAA,CACb,GAAA,EACA,SAAA,EACA,GAAA,EACe;AACf,EAAA,IAAI,CAAC,SAAA,EAAW;AACd,IAAA,MAAM,MAAA,GAAS,6CAAA;AACf,IAAA,GAAA,CAAI,gBAAA,EAAkB,QAAQ,MAAM,CAAA;AACpC,IAAA,GAAA,CAAI,wBAAA,EAA0B,QAAQ,MAAM,CAAA;AAC5C,IAAA,GAAA,CAAI,aAAA,EAAe,QAAQ,MAAM,CAAA;AACjC,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,EAAE,mBAAA,EAAoB,GAAI,4BAAA,CAA6B,SAAS,CAAA;AACtE,EAAA,GAAA;AAAA,IACE,gBAAA;AAAA,IACA,sBAAsB,MAAA,GAAS,MAAA;AAAA,IAC/B,mBAAA,GACI,CAAA,4BAAA,EAA+B,mBAAA,CAAoB,IAAI,CAAA,CAAA,GACvD,CAAA,mDAAA,EAAsD,IAAA,CAAK,SAAA,CAAU,SAAA,CAAU,OAAA,CAAQ,GAAA,CAAI,kBAAkB,CAAC,CAAC,CAAA,CAAA;AAAA,GACrH;AAEA,EAAA,IAAI,GAAA;AACJ,EAAA,IAAI;AAGF,IAAA,GAAA,GAAM,MAAM,sCAAA,CAAuC,GAAA,EAAK,EAAE,qBAAqB,CAAA;AAAA,EACjF,SAAS,GAAA,EAAK;AACZ,IAAA,GAAA;AAAA,MACE,wBAAA;AAAA,MACA,MAAA;AAAA,MACA,CAAA,wCAAA,EAA2C,OAAA,CAAQ,GAAG,CAAC,CAAA;AAAA,KACzD;AACA,IAAA,GAAA,CAAI,aAAA,EAAe,QAAQ,gCAAgC,CAAA;AAC3D,IAAA;AAAA,EACF;AACA,EAAA,GAAA;AAAA,IACE,wBAAA;AAAA,IACA,GAAA,CAAI,QAAA,KAAa,GAAA,GAAM,MAAA,GAAS,MAAA;AAAA,IAChC,IAAI,QAAA,KAAa,GAAA,GACb,CAAA,gBAAA,EAAmB,GAAG,KACtB,CAAA,YAAA,EAAe,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI,QAAQ,CAAC,CAAA,MAAA,EAAS,IAAA,CAAK,SAAA,CAAU,GAAG,CAAC,CAAA;AAAA,GAC7E;AAEA,EAAA,MAAM,UAAA,GAAa,GAAA,CAAI,qBAAA,GAAwB,CAAC,CAAA;AAChD,EAAA,IAAI,CAAC,UAAA,EAAY;AACf,IAAA,GAAA,CAAI,aAAA,EAAe,QAAQ,4DAA4D,CAAA;AACvF,IAAA;AAAA,EACF;AACA,EAAA,IAAI;AACF,IAAA,MAAM,QAAA,GAAW,MAAM,mCAAA,CAAoC,UAAU,CAAA;AACrE,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,GAAA,CAAI,aAAA,EAAe,MAAA,EAAQ,CAAA,2CAAA,EAA8C,UAAU,CAAA,CAAE,CAAA;AAAA,IACvF,CAAA,MAAA,IAAW,QAAA,CAAS,MAAA,KAAW,UAAA,EAAY;AACzC,MAAA,GAAA;AAAA,QACE,aAAA;AAAA,QACA,MAAA;AAAA,QACA,CAAA,UAAA,EAAa,IAAA,CAAK,SAAA,CAAU,QAAA,CAAS,MAAM,CAAC,CAAA,gBAAA,EAAmB,IAAA,CAAK,SAAA,CAAU,UAAU,CAAC,CAAA;AAAA,OAC3F;AAAA,IACF,CAAA,MAAO;AACL,MAAA,GAAA,CAAI,aAAA,EAAe,MAAA,EAAQ,CAAA,cAAA,EAAiB,UAAU,CAAA,CAAE,CAAA;AAAA,IAC1D;AAAA,EACF,SAAS,GAAA,EAAK;AACZ,IAAA,GAAA,CAAI,eAAe,MAAA,EAAQ,CAAA,0CAAA,EAA6C,OAAA,CAAQ,GAAG,CAAC,CAAA,CAAE,CAAA;AAAA,EACxF;AACF;AAMA,SAAS,OAAO,IAAA,EAAuC;AACrD,EAAA,MAAM,EAAA,GAAK,KAAK,KAAA,EAAO,EAAA;AACvB,EAAA,OAAO,EAAA,IAAM,OAAO,EAAA,KAAO,QAAA,GAAY,EAAA,GAAuB,MAAA;AAChE;AAEA,SAAS,cAAc,IAAA,EAAqB;AAC1C,EAAA,OACE,IAAA,CAAK,QAAQ,qBAAqB,CAAA,KAAM,UAAa,MAAA,CAAO,IAAI,GAAG,WAAA,KAAgB,MAAA;AAEvF;AAEA,SAAS,WAAW,IAAA,EAAgE;AAClF,EAAA,MAAM,EAAA,GAAK,MAAM,KAAA,EAAO,EAAA;AACxB,EAAA,OAAO,EAAA,IAAM,OAAO,EAAA,KAAO,QAAA,GAAW,EAAA,GAAK,MAAA;AAC7C;AASA,IAAM,cAAA,GAAiB,kBAAA;AAGvB,IAAM,aAAA,GAAgB,CAAC,gBAAA,EAAkB,iBAAiB,CAAA;AAE1D,SAAS,UAAU,IAAA,EAAgE;AACjF,EAAA,OAAO,IAAA,EAAM,QAAQ,cAAc,CAAA;AACrC;AAGA,SAAS,cAAc,KAAA,EAAmD;AACxE,EAAA,OACE,OAAO,KAAA,KAAU,QAAA,IACjB,KAAA,KAAU,IAAA,IACV,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,IACpB,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,KAAM,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA,IAAK,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,KAAM,OAAO,CAAA,KAAM,QAAQ,CAAC,CAAA;AAE/F;AAGA,SAAS,YAAA,CAAa,KAAc,GAAA,EAAsD;AACxF,EAAA,OAAO,aAAA,CAAc,GAAG,CAAA,IAAA,CAAM,GAAA,CAAI,GAAG,CAAA,IAAK,EAAC,EAAG,QAAA,CAAS,OAAO,CAAA;AAChE;AAEA,SAAS,mBAAmB,IAAA,EAAqB;AAC/C,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,KAAA,EAAO,eAAA,IAAoB,IAAA,CAAiC,eAAA;AACjF,EAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,IAAK,QAAQ,MAAA,GAAS,CAAA;AACpD;AAEA,IAAM,cAAA,GAAiB,2EAAA;AACvB,IAAM,kBAAA,GAAqB,wCAAA;AAE3B,SAAS,YAAY,IAAA,EAAuB;AAC1C,EAAA,OAAO,eAAe,IAAA,CAAK,IAAI,CAAA,IAAK,kBAAA,CAAmB,KAAK,IAAI,CAAA;AAClE;AAEA,SAAS,QAAQ,GAAA,EAAsB;AACrC,EAAA,OAAO,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,OAAO,GAAG,CAAA;AACxD","file":"check-CSE4XQRL.js","sourcesContent":["/**\n * Store profiles for `synapse check`: which checks each host applies, and how\n * hard. A check a profile does not list is not run for that target.\n *\n * `error` fails the run. `warn` is reported and does not: it marks a\n * requirement the host is documented to have but is not known to enforce, so a\n * failure is a risk rather than a certain break.\n */\n\nexport type CheckId =\n | \"extension-declared\"\n | \"ui-resource-mime\"\n | \"tool-resource-uri\"\n | \"tool-ui-meta\"\n | \"resource-csp\"\n | \"resource-openai-csp\"\n | \"resource-data-fonts\"\n | \"resource-openai-data-fonts\"\n | \"auth-challenge\"\n | \"auth-resource-metadata\"\n | \"auth-issuer\"\n | \"tool-security-schemes\";\n\nexport type Severity = \"error\" | \"warn\";\n\nexport interface ProfileRule {\n severity: Severity;\n /** The host requirement behind the rule, printed when the check fails. */\n why: string;\n}\n\nexport interface Profile {\n name: TargetName;\n description: string;\n rules: Partial<Record<CheckId, ProfileRule>>;\n}\n\nexport type TargetName = \"nimblebrain\" | \"claude\" | \"chatgpt\";\n\nconst SPEC_MIME = \"UI resources are served as text/html;profile=mcp-app.\";\nconst RESOLVES = \"A tool's ui.resourceUri must name a ui:// resource the server serves.\";\nconst UI_META =\n \"ui.visibility is a non-empty subset of model/app; csp and permissions belong on the resource, not the tool.\";\nconst EXTENSION = \"A client uses MCP Apps only when both sides declare io.modelcontextprotocol/ui.\";\nconst CHALLENGE = \"Sign-in starts from a 401 whose WWW-Authenticate carries resource_metadata.\";\nconst PRM_RESOURCE =\n \"The protected-resource metadata's resource must equal the connector URL exactly.\";\nconst ISSUER =\n \"The authorization server's metadata issuer must equal the advertised authorization server exactly (RFC 8414).\";\nconst SPEC_CSP =\n \"ui.csp is the frame's security policy for Claude and the NimbleBrain host, and OpenAI documents it as generally preferred for new UI; measured 2026-09-18 in ChatGPT developer mode, the frame ran under ChatGPT's own default sandbox policy whichever dialect the resource carried.\";\n\nexport const PROFILES: Record<TargetName, Profile> = {\n nimblebrain: {\n name: \"nimblebrain\",\n description: \"The NimbleBrain host.\",\n rules: {\n \"ui-resource-mime\": { severity: \"error\", why: SPEC_MIME },\n \"tool-resource-uri\": { severity: \"error\", why: RESOLVES },\n \"tool-ui-meta\": { severity: \"error\", why: UI_META },\n // The host resolves an app by its ui:// scheme and does not gate on the\n // declaration, so a missing one is a portability risk, not a break.\n \"extension-declared\": { severity: \"warn\", why: EXTENSION },\n // Only reported when the server challenges at all.\n \"auth-challenge\": { severity: \"warn\", why: CHALLENGE },\n \"auth-resource-metadata\": { severity: \"warn\", why: PRM_RESOURCE },\n \"auth-issuer\": { severity: \"warn\", why: ISSUER },\n },\n },\n claude: {\n name: \"claude\",\n description: \"claude.ai and Claude Desktop.\",\n rules: {\n \"ui-resource-mime\": { severity: \"error\", why: SPEC_MIME },\n \"tool-resource-uri\": { severity: \"error\", why: RESOLVES },\n \"tool-ui-meta\": { severity: \"error\", why: UI_META },\n \"extension-declared\": { severity: \"error\", why: EXTENSION },\n \"resource-csp\": {\n severity: \"warn\",\n why: \"Claude builds the frame's security policy from ui.csp; an undeclared origin is blocked.\",\n },\n \"resource-data-fonts\": {\n severity: \"error\",\n why: \"Claude's default security policy blocks data: fonts unless the resource declares them.\",\n },\n \"auth-challenge\": { severity: \"error\", why: CHALLENGE },\n \"auth-resource-metadata\": { severity: \"error\", why: PRM_RESOURCE },\n // Claude has been observed to accept an issuer that differs by a trailing\n // slash, so this is a correctness warning for Claude rather than a break.\n \"auth-issuer\": { severity: \"warn\", why: ISSUER },\n },\n },\n chatgpt: {\n name: \"chatgpt\",\n description: \"ChatGPT apps.\",\n rules: {\n \"ui-resource-mime\": { severity: \"error\", why: SPEC_MIME },\n \"tool-resource-uri\": { severity: \"error\", why: RESOLVES },\n \"tool-ui-meta\": { severity: \"error\", why: UI_META },\n \"extension-declared\": { severity: \"error\", why: EXTENSION },\n // A requirement the vendor documents and the host was not observed to\n // enforce is what `warn` is for. Neither dialect has been observed to\n // produce an enforced policy, so both sit at warn.\n \"resource-csp\": { severity: \"warn\", why: SPEC_CSP },\n \"resource-openai-csp\": {\n severity: \"warn\",\n why: \"OpenAI documents openai/widgetCSP (snake_case origin lists) as a legacy compatibility surface, and it is still the only way to declare redirect_domains. Measured 2026-09-18 in developer mode, a resource carrying it beside ui.csp produced no enforced policy: the component ran under ChatGPT's default sandbox CSP either way. Declare it for the documented requirement; it is not known to change what the host enforces.\",\n },\n \"resource-openai-data-fonts\": {\n severity: \"warn\",\n why: \"A data: font is blocked by a security policy that does not declare it.\",\n },\n \"auth-challenge\": { severity: \"error\", why: CHALLENGE },\n \"auth-resource-metadata\": { severity: \"error\", why: PRM_RESOURCE },\n \"auth-issuer\": { severity: \"error\", why: ISSUER },\n \"tool-security-schemes\": {\n severity: \"error\",\n why: \"ChatGPT prompts for sign-in mid-conversation only for tools that declare securitySchemes.\",\n },\n },\n },\n};\n\nexport function isTargetName(value: string): value is TargetName {\n return Object.hasOwn(PROFILES, value);\n}\n","/**\n * `synapse check` — verify, from outside, what a host sees of a running MCP\n * server: the UI resources and their metadata, the MCP Apps extension\n * declaration, and the OAuth discovery surface a host signs in through.\n *\n * Every check runs once against the server; each target's profile then decides\n * which results count and how hard (see `./profiles.ts`).\n */\n\nimport type {\n McpUiClientCapabilities,\n McpUiResourceMeta,\n McpUiToolMeta,\n} from \"@modelcontextprotocol/ext-apps\";\nimport {\n EXTENSION_ID,\n RESOURCE_MIME_TYPE,\n RESOURCE_URI_META_KEY,\n} from \"@modelcontextprotocol/ext-apps/server\";\nimport {\n discoverAuthorizationServerMetadata,\n discoverOAuthProtectedResourceMetadata,\n extractWWWAuthenticateParams,\n} from \"@modelcontextprotocol/sdk/client/auth.js\";\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\nimport type {\n ReadResourceResult,\n Resource,\n ServerCapabilities,\n Tool,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { ListToolsResultSchema, ResultSchema } from \"@modelcontextprotocol/sdk/types.js\";\nimport { type CheckId, PROFILES, type Severity, type TargetName } from \"./profiles.js\";\n\nexport { type CheckId, isTargetName, PROFILES, type Profile, type TargetName } from \"./profiles.js\";\n\nexport type CheckStatus = \"pass\" | \"fail\" | \"skip\";\n\nexport interface CheckResult {\n id: CheckId;\n status: CheckStatus;\n detail: string;\n}\n\nexport interface TargetReport {\n target: TargetName;\n results: Array<CheckResult & { severity: Severity; why: string }>;\n /** True when any `error`-severity check failed. */\n failed: boolean;\n}\n\nexport interface CheckOptions {\n /** Bearer token for a server that requires auth. Without one, checks that need a session are skipped. */\n token?: string;\n}\n\nconst UI_SCHEME = \"ui://\";\nconst VISIBILITY = new Set([\"model\", \"app\"]);\n\ntype ResourceContents = ReadResourceResult[\"contents\"][number];\n\ninterface Session {\n capabilities: ServerCapabilities | undefined;\n tools: Tool[];\n resources: Resource[];\n reads: Map<string, ResourceContents[] | Error>;\n}\n\n/** Run every check against `url` once. */\nexport async function runChecks(url: string, options: CheckOptions = {}): Promise<CheckResult[]> {\n const results: CheckResult[] = [];\n const add = (id: CheckId, status: CheckStatus, detail: string) =>\n results.push({ id, status, detail });\n\n // Connect without credentials first: the response to that is what a host\n // sees before sign-in, and it decides whether the server requires auth.\n let challenge: Response | undefined;\n let session: Session | undefined;\n let connectError: unknown;\n try {\n session = await openSession(url, undefined, (res) => {\n if (res.status === 401) challenge ??= res;\n });\n } catch (err) {\n connectError = err;\n }\n\n const authRequired = challenge !== undefined;\n if (authRequired && options.token) {\n try {\n session = await openSession(url, options.token);\n connectError = undefined;\n } catch (err) {\n connectError = err;\n }\n }\n\n // A server that cannot be reached is not a server that passes.\n if (!session && (!authRequired || options.token)) {\n throw new Error(\n `could not connect to ${url}${authRequired ? \" with the given token\" : \"\"}: ${message(connectError)}`,\n );\n }\n\n await checkAuth(url, challenge, add);\n\n if (!session) {\n for (const id of SESSION_CHECKS) {\n add(id, \"skip\", \"the server requires auth; pass --token to run it\");\n }\n return results;\n }\n\n checkSession(session, authRequired, add);\n return results;\n}\n\n/** Apply each target's profile to one set of results. */\nexport function applyProfiles(results: CheckResult[], targets: TargetName[]): TargetReport[] {\n return targets.map((target) => {\n const rules = PROFILES[target].rules;\n const scored = results.flatMap((r) => {\n const rule = rules[r.id];\n return rule ? [{ ...r, severity: rule.severity, why: rule.why }] : [];\n });\n return {\n target,\n results: scored,\n failed: scored.some((r) => r.status === \"fail\" && r.severity === \"error\"),\n };\n });\n}\n\n/** One line per check, grouped by target. */\nexport function formatReport(url: string, reports: TargetReport[]): string {\n const width = Math.max(...reports.flatMap((r) => r.results.map((c) => c.id.length)), 0);\n const lines = [`synapse check ${url}`];\n for (const report of reports) {\n lines.push(\"\", `${report.target}: ${report.failed ? \"FAIL\" : \"ok\"}`);\n for (const r of report.results) {\n const label =\n r.status === \"fail\" ? (r.severity === \"error\" ? \"FAIL\" : \"WARN\") : r.status.toUpperCase();\n lines.push(` ${label.padEnd(4)} ${r.id.padEnd(width)} ${r.detail}`);\n if (r.status === \"fail\") lines.push(` ${\"\".padEnd(width)} ${r.why}`);\n }\n }\n return lines.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// session\n// ---------------------------------------------------------------------------\n\nconst SESSION_CHECKS: CheckId[] = [\n \"extension-declared\",\n \"ui-resource-mime\",\n \"tool-resource-uri\",\n \"tool-ui-meta\",\n \"resource-csp\",\n \"resource-openai-csp\",\n \"resource-data-fonts\",\n \"resource-openai-data-fonts\",\n \"tool-security-schemes\",\n];\n\nasync function openSession(\n url: string,\n token: string | undefined,\n onResponse?: (res: Response) => void,\n): Promise<Session> {\n // Declare MCP Apps support, so a server that gates its UI on the client's\n // capability shows the UI a host would see.\n const ui: McpUiClientCapabilities = { mimeTypes: [RESOURCE_MIME_TYPE] };\n const client = new Client(\n { name: \"synapse-check\", version: \"1\" },\n { capabilities: { extensions: { [EXTENSION_ID]: ui } } },\n );\n const transport = new StreamableHTTPClientTransport(new URL(url), {\n requestInit: token ? { headers: { Authorization: `Bearer ${token}` } } : undefined,\n fetch: async (input, init) => {\n const res = await fetch(input, init);\n onResponse?.(res);\n return res;\n },\n });\n await client.connect(transport);\n try {\n const capabilities = client.getServerCapabilities();\n const tools: Tool[] = [];\n const resources: Resource[] = [];\n let cursor: string | undefined;\n if (capabilities?.tools) {\n do {\n // Requested raw rather than through `listTools()`: the SDK's tool schema\n // strips fields it does not model, and ChatGPT documents `securitySchemes`\n // at the tool's top level as well as in `_meta`. The one field is copied\n // back onto each parsed tool so `hasSecuritySchemes` can see either place.\n const raw = await client.request(\n { method: \"tools/list\", params: cursor ? { cursor } : {} },\n ResultSchema,\n );\n const page = ListToolsResultSchema.parse(raw);\n const rawTools = (raw as { tools?: Array<Record<string, unknown>> }).tools ?? [];\n page.tools.forEach((tool, i) => {\n const schemes = rawTools[i]?.securitySchemes;\n if (schemes !== undefined) (tool as Record<string, unknown>).securitySchemes = schemes;\n });\n tools.push(...page.tools);\n cursor = page.nextCursor;\n } while (cursor);\n }\n if (capabilities?.resources) {\n do {\n const page = await client.listResources({ cursor });\n resources.push(...page.resources);\n cursor = page.nextCursor;\n } while (cursor);\n }\n\n const uris = new Set(resources.map((r) => r.uri).filter((u) => u.startsWith(UI_SCHEME)));\n for (const tool of tools) {\n const uri = toolUi(tool)?.resourceUri;\n if (typeof uri === \"string\") uris.add(uri);\n }\n const reads = new Map<string, ResourceContents[] | Error>();\n for (const uri of uris) {\n try {\n reads.set(uri, (await client.readResource({ uri })).contents);\n } catch (err) {\n reads.set(uri, err instanceof Error ? err : new Error(String(err)));\n }\n }\n return { capabilities, tools, resources, reads };\n } finally {\n await client.close();\n }\n}\n\nfunction checkSession(\n s: Session,\n authRequired: boolean,\n add: (id: CheckId, status: CheckStatus, detail: string) => void,\n): void {\n const boundTools = s.tools.filter((t) => toolUi(t)?.resourceUri !== undefined);\n const uiResources = s.resources.filter((r) => r.uri.startsWith(UI_SCHEME));\n const hasUi = boundTools.length > 0 || uiResources.length > 0 || s.tools.some(legacyBinding);\n\n if (!hasUi) {\n for (const id of SESSION_CHECKS) {\n if (id !== \"tool-security-schemes\") add(id, \"skip\", \"the server exposes no UI\");\n }\n } else {\n // extension-declared\n const declared = s.capabilities?.extensions?.[EXTENSION_ID] !== undefined;\n add(\n \"extension-declared\",\n declared ? \"pass\" : \"fail\",\n declared\n ? `${EXTENSION_ID} is declared`\n : `the server does not declare ${EXTENSION_ID} in capabilities.extensions`,\n );\n\n // ui-resource-mime: every ui:// resource, as listed and as read.\n const wrongMime: string[] = [];\n for (const r of uiResources) {\n if (r.mimeType !== undefined && r.mimeType !== RESOURCE_MIME_TYPE) {\n wrongMime.push(`${r.uri} is listed as ${r.mimeType}`);\n }\n }\n for (const [uri, contents] of s.reads) {\n if (contents instanceof Error) continue;\n for (const c of contents) {\n if (c.mimeType !== RESOURCE_MIME_TYPE) {\n wrongMime.push(`${uri} is served as ${c.mimeType ?? \"no MIME type\"}`);\n }\n }\n }\n add(\n \"ui-resource-mime\",\n wrongMime.length ? \"fail\" : \"pass\",\n wrongMime.length\n ? `${wrongMime.join(\"; \")}; expected ${RESOURCE_MIME_TYPE}`\n : `every ui:// resource is ${RESOURCE_MIME_TYPE}`,\n );\n\n // tool-resource-uri\n const unresolved: string[] = [];\n for (const tool of s.tools) {\n const uri = toolUi(tool)?.resourceUri;\n if (uri === undefined) {\n if (legacyBinding(tool)) {\n unresolved.push(`${tool.name} binds its UI only through the deprecated flat key`);\n }\n continue;\n }\n if (typeof uri !== \"string\" || !uri.startsWith(UI_SCHEME)) {\n unresolved.push(`${tool.name}: ui.resourceUri ${JSON.stringify(uri)} is not a ui:// URI`);\n continue;\n }\n const read = s.reads.get(uri);\n if (read instanceof Error || !read?.length) {\n unresolved.push(\n `${tool.name}: ${uri} cannot be read${read instanceof Error ? ` (${read.message})` : \"\"}`,\n );\n }\n }\n if (boundTools.length === 0 && unresolved.length === 0) {\n unresolved.push(\"no tool binds a UI through ui.resourceUri\");\n }\n add(\n \"tool-resource-uri\",\n unresolved.length ? \"fail\" : \"pass\",\n unresolved.length\n ? unresolved.join(\"; \")\n : `${boundTools.length} tool(s) bind a UI that resolves`,\n );\n\n // tool-ui-meta\n const badMeta: string[] = [];\n for (const tool of s.tools) {\n const ui = toolUi(tool);\n if (!ui) continue;\n const vis = ui.visibility as unknown;\n if (\n vis !== undefined &&\n (!Array.isArray(vis) || vis.length === 0 || !vis.every((v) => VISIBILITY.has(v)))\n ) {\n badMeta.push(`${tool.name}: ui.visibility ${JSON.stringify(vis)}`);\n }\n for (const key of [\"csp\", \"permissions\"] as const) {\n if ((ui as Record<string, unknown>)[key] !== undefined) {\n badMeta.push(`${tool.name}: ui.${key} is on the tool, where hosts ignore it`);\n }\n }\n }\n add(\n \"tool-ui-meta\",\n badMeta.length ? \"fail\" : \"pass\",\n badMeta.length ? badMeta.join(\"; \") : \"tool ui metadata is well-formed\",\n );\n\n // The frame's security policy, in each dialect a host reads, per UI resource.\n const noCsp: string[] = [];\n const noOpenAiCsp: string[] = [];\n const dataFonts: string[] = [];\n const openAiDataFonts: string[] = [];\n for (const [uri, contents] of s.reads) {\n if (contents instanceof Error) continue;\n for (const c of contents) {\n const listed = s.resources.find((r) => r.uri === uri);\n // Hosts read the content item's metadata, and fall back to the list entry.\n const ui = (resourceUi(c) ?? resourceUi(listed)) as McpUiResourceMeta | undefined;\n const csp = ui?.csp as unknown;\n if (csp === undefined) {\n noCsp.push(`${uri} declares no ui.csp`);\n } else if (!isOriginLists(csp)) {\n noCsp.push(`${uri}: ui.csp is not a map of origin lists`);\n }\n const openAi = openAiCsp(c) ?? openAiCsp(listed);\n if (openAi === undefined) {\n noOpenAiCsp.push(`${uri} declares no ${OPENAI_CSP_KEY}`);\n } else if (!isOriginLists(openAi)) {\n noOpenAiCsp.push(`${uri}: ${OPENAI_CSP_KEY} is not a map of origin lists`);\n } else {\n const camel = SPEC_CSP_KEYS.filter((k) => k in openAi);\n if (camel.length) {\n noOpenAiCsp.push(\n `${uri}: ${OPENAI_CSP_KEY} names ${camel.join(\", \")}, which OpenAI does not document for it`,\n );\n }\n }\n const html = \"text\" in c ? c.text : Buffer.from(c.blob, \"base64\").toString(\"utf-8\");\n if (hasDataFont(html)) {\n if (!declaresData(csp, \"resourceDomains\")) {\n dataFonts.push(`${uri} loads a data: font, and ui.csp.resourceDomains omits data:`);\n }\n if (!declaresData(openAi, \"resource_domains\")) {\n openAiDataFonts.push(\n `${uri} loads a data: font, and ${OPENAI_CSP_KEY}.resource_domains omits data:`,\n );\n }\n }\n }\n }\n add(\n \"resource-csp\",\n noCsp.length ? \"fail\" : \"pass\",\n noCsp.length ? noCsp.join(\"; \") : \"every UI resource declares ui.csp\",\n );\n add(\n \"resource-openai-csp\",\n noOpenAiCsp.length ? \"fail\" : \"pass\",\n noOpenAiCsp.length ? noOpenAiCsp.join(\"; \") : `every UI resource declares ${OPENAI_CSP_KEY}`,\n );\n add(\n \"resource-data-fonts\",\n dataFonts.length ? \"fail\" : \"pass\",\n dataFonts.length ? dataFonts.join(\"; \") : \"no data: font ui.csp omits\",\n );\n add(\n \"resource-openai-data-fonts\",\n openAiDataFonts.length ? \"fail\" : \"pass\",\n openAiDataFonts.length ? openAiDataFonts.join(\"; \") : `no data: font ${OPENAI_CSP_KEY} omits`,\n );\n }\n\n // tool-security-schemes: a question about auth, not about UI.\n if (!authRequired) {\n add(\"tool-security-schemes\", \"skip\", \"the server does not require auth\");\n } else {\n const missing = s.tools.filter((t) => !hasSecuritySchemes(t)).map((t) => t.name);\n add(\n \"tool-security-schemes\",\n missing.length ? \"fail\" : \"pass\",\n missing.length\n ? `no securitySchemes on: ${missing.join(\", \")}`\n : \"every tool declares securitySchemes\",\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// auth\n// ---------------------------------------------------------------------------\n\nasync function checkAuth(\n url: string,\n challenge: Response | undefined,\n add: (id: CheckId, status: CheckStatus, detail: string) => void,\n): Promise<void> {\n if (!challenge) {\n const reason = \"the server answered without a 401 challenge\";\n add(\"auth-challenge\", \"skip\", reason);\n add(\"auth-resource-metadata\", \"skip\", reason);\n add(\"auth-issuer\", \"skip\", reason);\n return;\n }\n\n const { resourceMetadataUrl } = extractWWWAuthenticateParams(challenge);\n add(\n \"auth-challenge\",\n resourceMetadataUrl ? \"pass\" : \"fail\",\n resourceMetadataUrl\n ? `401 names resource_metadata ${resourceMetadataUrl.href}`\n : `401 WWW-Authenticate carries no resource_metadata (${JSON.stringify(challenge.headers.get(\"www-authenticate\"))})`,\n );\n\n let prm: Awaited<ReturnType<typeof discoverOAuthProtectedResourceMetadata>>;\n try {\n // Without resource_metadata, a client falls back to the well-known path,\n // so the rest of the surface is still worth reporting.\n prm = await discoverOAuthProtectedResourceMetadata(url, { resourceMetadataUrl });\n } catch (err) {\n add(\n \"auth-resource-metadata\",\n \"fail\",\n `protected-resource metadata unreadable: ${message(err)}`,\n );\n add(\"auth-issuer\", \"skip\", \"no protected-resource metadata\");\n return;\n }\n add(\n \"auth-resource-metadata\",\n prm.resource === url ? \"pass\" : \"fail\",\n prm.resource === url\n ? `resource equals ${url}`\n : `resource is ${JSON.stringify(prm.resource)}, not ${JSON.stringify(url)}`,\n );\n\n const advertised = prm.authorization_servers?.[0];\n if (!advertised) {\n add(\"auth-issuer\", \"fail\", \"protected-resource metadata lists no authorization_servers\");\n return;\n }\n try {\n const metadata = await discoverAuthorizationServerMetadata(advertised);\n if (!metadata) {\n add(\"auth-issuer\", \"fail\", `no authorization server metadata found for ${advertised}`);\n } else if (metadata.issuer !== advertised) {\n add(\n \"auth-issuer\",\n \"fail\",\n `issuer is ${JSON.stringify(metadata.issuer)}, advertised as ${JSON.stringify(advertised)}`,\n );\n } else {\n add(\"auth-issuer\", \"pass\", `issuer equals ${advertised}`);\n }\n } catch (err) {\n add(\"auth-issuer\", \"fail\", `authorization server metadata unreadable: ${message(err)}`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// helpers\n// ---------------------------------------------------------------------------\n\nfunction toolUi(tool: Tool): McpUiToolMeta | undefined {\n const ui = tool._meta?.ui;\n return ui && typeof ui === \"object\" ? (ui as McpUiToolMeta) : undefined;\n}\n\nfunction legacyBinding(tool: Tool): boolean {\n return (\n tool._meta?.[RESOURCE_URI_META_KEY] !== undefined && toolUi(tool)?.resourceUri === undefined\n );\n}\n\nfunction resourceUi(item: { _meta?: Record<string, unknown> } | undefined): unknown {\n const ui = item?._meta?.ui;\n return ui && typeof ui === \"object\" ? ui : undefined;\n}\n\n/**\n * ChatGPT's dialect of the frame's security policy. It is a sibling of `ui`\n * in the resource's `_meta`, not a key inside it, and its origin lists are\n * spelled `connect_domains`/`resource_domains`. OpenAI documents it as a legacy\n * compatibility key and `ui.csp` as generally preferred for new UI. The two are\n * checked separately, and a target's profile sets each one's severity for its host.\n */\nconst OPENAI_CSP_KEY = \"openai/widgetCSP\";\n\n/** The spec's spellings, which under `openai/widgetCSP` name origins nothing reads. */\nconst SPEC_CSP_KEYS = [\"connectDomains\", \"resourceDomains\"];\n\nfunction openAiCsp(item: { _meta?: Record<string, unknown> } | undefined): unknown {\n return item?._meta?.[OPENAI_CSP_KEY];\n}\n\n/** The `{ name: [origin, ...] }` shape both CSP dialects have. */\nfunction isOriginLists(value: unknown): value is Record<string, string[]> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n Object.values(value).every((v) => Array.isArray(v) && v.every((o) => typeof o === \"string\"))\n );\n}\n\n/** Whether `csp` allows `data:` under the key naming static assets in its dialect. */\nfunction declaresData(csp: unknown, key: \"resourceDomains\" | \"resource_domains\"): boolean {\n return isOriginLists(csp) && (csp[key] ?? []).includes(\"data:\");\n}\n\nfunction hasSecuritySchemes(tool: Tool): boolean {\n const schemes = tool._meta?.securitySchemes ?? (tool as Record<string, unknown>).securitySchemes;\n return Array.isArray(schemes) && schemes.length > 0;\n}\n\nconst DATA_FONT_MIME = /data:(?:font\\/|application\\/(?:x-)?font|application\\/vnd\\.ms-fontobject)/i;\nconst FONT_FACE_DATA_URL = /@font-face\\s*{[^}]*url\\(\\s*[\"']?data:/i;\n\nfunction hasDataFont(html: string): boolean {\n return DATA_FONT_MIME.test(html) || FONT_FACE_DATA_URL.test(html);\n}\n\nfunction message(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n"]}
|
package/dist/codegen/cli.cjs
CHANGED
|
@@ -86,7 +86,7 @@ async function runPreview(args2) {
|
|
|
86
86
|
});
|
|
87
87
|
}
|
|
88
88
|
async function runCheck(args2) {
|
|
89
|
-
const { applyProfiles, formatReport, isTargetName, PROFILES, runChecks } = await import('../check-
|
|
89
|
+
const { applyProfiles, formatReport, isTargetName, PROFILES, runChecks } = await import('../check-53RMGOVV.cjs');
|
|
90
90
|
const flags = parseFlags(args2, ["target", "token"]);
|
|
91
91
|
const json = args2.includes("--json");
|
|
92
92
|
const positional = args2.filter(
|
package/dist/codegen/cli.js
CHANGED
|
@@ -84,7 +84,7 @@ async function runPreview(args2) {
|
|
|
84
84
|
});
|
|
85
85
|
}
|
|
86
86
|
async function runCheck(args2) {
|
|
87
|
-
const { applyProfiles, formatReport, isTargetName, PROFILES, runChecks } = await import('../check-
|
|
87
|
+
const { applyProfiles, formatReport, isTargetName, PROFILES, runChecks } = await import('../check-CSE4XQRL.js');
|
|
88
88
|
const flags = parseFlags(args2, ["target", "token"]);
|
|
89
89
|
const json = args2.includes("--json");
|
|
90
90
|
const positional = args2.filter(
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/check/profiles.ts","../src/check/index.ts"],"names":["RESOURCE_MIME_TYPE","Client","EXTENSION_ID","StreamableHTTPClientTransport","ResultSchema","ListToolsResultSchema","extractWWWAuthenticateParams","discoverOAuthProtectedResourceMetadata","discoverAuthorizationServerMetadata","RESOURCE_URI_META_KEY"],"mappings":";;;;;;;;;;;AAqCA,IAAM,SAAA,GAAY,uDAAA;AAClB,IAAM,QAAA,GAAW,uEAAA;AACjB,IAAM,OAAA,GACJ,6GAAA;AACF,IAAM,SAAA,GAAY,iFAAA;AAClB,IAAM,SAAA,GAAY,6EAAA;AAClB,IAAM,YAAA,GACJ,kFAAA;AACF,IAAM,MAAA,GACJ,+GAAA;AAEK,IAAM,QAAA,GAAwC;AAAA,EACnD,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,aAAA;AAAA,IACN,WAAA,EAAa,uBAAA;AAAA,IACb,KAAA,EAAO;AAAA,MACL,kBAAA,EAAoB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,SAAA,EAAU;AAAA,MACxD,mBAAA,EAAqB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,QAAA,EAAS;AAAA,MACxD,cAAA,EAAgB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,OAAA,EAAQ;AAAA;AAAA;AAAA,MAGlD,oBAAA,EAAsB,EAAE,QAAA,EAAU,MAAA,EAAQ,KAAK,SAAA,EAAU;AAAA;AAAA,MAEzD,gBAAA,EAAkB,EAAE,QAAA,EAAU,MAAA,EAAQ,KAAK,SAAA,EAAU;AAAA,MACrD,wBAAA,EAA0B,EAAE,QAAA,EAAU,MAAA,EAAQ,KAAK,YAAA,EAAa;AAAA,MAChE,aAAA,EAAe,EAAE,QAAA,EAAU,MAAA,EAAQ,KAAK,MAAA;AAAO;AACjD,GACF;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM,QAAA;AAAA,IACN,WAAA,EAAa,+BAAA;AAAA,IACb,KAAA,EAAO;AAAA,MACL,kBAAA,EAAoB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,SAAA,EAAU;AAAA,MACxD,mBAAA,EAAqB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,QAAA,EAAS;AAAA,MACxD,cAAA,EAAgB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,OAAA,EAAQ;AAAA,MAClD,oBAAA,EAAsB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,SAAA,EAAU;AAAA,MAC1D,cAAA,EAAgB;AAAA,QACd,QAAA,EAAU,MAAA;AAAA,QACV,GAAA,EAAK;AAAA,OACP;AAAA,MACA,qBAAA,EAAuB;AAAA,QACrB,QAAA,EAAU,OAAA;AAAA,QACV,GAAA,EAAK;AAAA,OACP;AAAA,MACA,gBAAA,EAAkB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,SAAA,EAAU;AAAA,MACtD,wBAAA,EAA0B,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,YAAA,EAAa;AAAA;AAAA;AAAA,MAGjE,aAAA,EAAe,EAAE,QAAA,EAAU,MAAA,EAAQ,KAAK,MAAA;AAAO;AACjD,GACF;AAAA,EACA,OAAA,EAAS;AAAA,IACP,IAAA,EAAM,SAAA;AAAA,IACN,WAAA,EAAa,eAAA;AAAA,IACb,KAAA,EAAO;AAAA,MACL,kBAAA,EAAoB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,SAAA,EAAU;AAAA,MACxD,mBAAA,EAAqB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,QAAA,EAAS;AAAA,MACxD,cAAA,EAAgB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,OAAA,EAAQ;AAAA,MAClD,oBAAA,EAAsB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,SAAA,EAAU;AAAA,MAC1D,cAAA,EAAgB;AAAA,QACd,QAAA,EAAU,OAAA;AAAA,QACV,GAAA,EAAK;AAAA,OACP;AAAA,MACA,qBAAA,EAAuB;AAAA,QACrB,QAAA,EAAU,MAAA;AAAA,QACV,GAAA,EAAK;AAAA,OACP;AAAA,MACA,gBAAA,EAAkB,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,SAAA,EAAU;AAAA,MACtD,wBAAA,EAA0B,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,YAAA,EAAa;AAAA,MACjE,aAAA,EAAe,EAAE,QAAA,EAAU,OAAA,EAAS,KAAK,MAAA,EAAO;AAAA,MAChD,uBAAA,EAAyB;AAAA,QACvB,QAAA,EAAU,OAAA;AAAA,QACV,GAAA,EAAK;AAAA;AACP;AACF;AAEJ;AAEO,SAAS,aAAa,KAAA,EAAoC;AAC/D,EAAA,OAAO,MAAA,CAAO,MAAA,CAAO,QAAA,EAAU,KAAK,CAAA;AACtC;;;AC5DA,IAAM,SAAA,GAAY,OAAA;AAClB,IAAM,6BAAa,IAAI,GAAA,CAAI,CAAC,OAAA,EAAS,KAAK,CAAC,CAAA;AAY3C,eAAsB,SAAA,CAAU,GAAA,EAAa,OAAA,GAAwB,EAAC,EAA2B;AAC/F,EAAA,MAAM,UAAyB,EAAC;AAChC,EAAA,MAAM,GAAA,GAAM,CAAC,EAAA,EAAa,MAAA,EAAqB,MAAA,KAC7C,OAAA,CAAQ,IAAA,CAAK,EAAE,EAAA,EAAI,MAAA,EAAQ,MAAA,EAAQ,CAAA;AAIrC,EAAA,IAAI,SAAA;AACJ,EAAA,IAAI,OAAA;AACJ,EAAA,IAAI,YAAA;AACJ,EAAA,IAAI;AACF,IAAA,OAAA,GAAU,MAAM,WAAA,CAAY,GAAA,EAAK,KAAA,CAAA,EAAW,CAAC,GAAA,KAAQ;AACnD,MAAA,IAAI,GAAA,CAAI,MAAA,KAAW,GAAA,EAAK,SAAA,KAAc,GAAA;AAAA,IACxC,CAAC,CAAA;AAAA,EACH,SAAS,GAAA,EAAK;AACZ,IAAA,YAAA,GAAe,GAAA;AAAA,EACjB;AAEA,EAAA,MAAM,eAAe,SAAA,KAAc,MAAA;AACnC,EAAA,IAAI,YAAA,IAAgB,QAAQ,KAAA,EAAO;AACjC,IAAA,IAAI;AACF,MAAA,OAAA,GAAU,MAAM,WAAA,CAAY,GAAA,EAAK,OAAA,CAAQ,KAAK,CAAA;AAC9C,MAAA,YAAA,GAAe,KAAA,CAAA;AAAA,IACjB,SAAS,GAAA,EAAK;AACZ,MAAA,YAAA,GAAe,GAAA;AAAA,IACjB;AAAA,EACF;AAGA,EAAA,IAAI,CAAC,OAAA,KAAY,CAAC,YAAA,IAAgB,QAAQ,KAAA,CAAA,EAAQ;AAChD,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,qBAAA,EAAwB,GAAG,CAAA,EAAG,YAAA,GAAe,0BAA0B,EAAE,CAAA,EAAA,EAAK,OAAA,CAAQ,YAAY,CAAC,CAAA;AAAA,KACrG;AAAA,EACF;AAEA,EAAA,MAAM,SAAA,CAAU,GAAA,EAAK,SAAA,EAAW,GAAG,CAAA;AAEnC,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,KAAA,MAAW,MAAM,cAAA,EAAgB;AAC/B,MAAA,GAAA,CAAI,EAAA,EAAI,QAAQ,kDAAkD,CAAA;AAAA,IACpE;AACA,IAAA,OAAO,OAAA;AAAA,EACT;AAEA,EAAA,YAAA,CAAa,OAAA,EAAS,cAAc,GAAG,CAAA;AACvC,EAAA,OAAO,OAAA;AACT;AAGO,SAAS,aAAA,CAAc,SAAwB,OAAA,EAAuC;AAC3F,EAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,CAAC,MAAA,KAAW;AAC7B,IAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,MAAM,CAAA,CAAE,KAAA;AAC/B,IAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,OAAA,CAAQ,CAAC,CAAA,KAAM;AACpC,MAAA,MAAM,IAAA,GAAO,KAAA,CAAM,CAAA,CAAE,EAAE,CAAA;AACvB,MAAA,OAAO,IAAA,GAAO,CAAC,EAAE,GAAG,CAAA,EAAG,QAAA,EAAU,IAAA,CAAK,QAAA,EAAU,GAAA,EAAK,IAAA,CAAK,GAAA,EAAK,IAAI,EAAC;AAAA,IACtE,CAAC,CAAA;AACD,IAAA,OAAO;AAAA,MACL,MAAA;AAAA,MACA,OAAA,EAAS,MAAA;AAAA,MACT,MAAA,EAAQ,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,KAAM,EAAE,MAAA,KAAW,MAAA,IAAU,CAAA,CAAE,QAAA,KAAa,OAAO;AAAA,KAC1E;AAAA,EACF,CAAC,CAAA;AACH;AAGO,SAAS,YAAA,CAAa,KAAa,OAAA,EAAiC;AACzE,EAAA,MAAM,QAAQ,IAAA,CAAK,GAAA,CAAI,GAAG,OAAA,CAAQ,OAAA,CAAQ,CAAC,CAAA,KAAM,CAAA,CAAE,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;AACtF,EAAA,MAAM,KAAA,GAAQ,CAAC,CAAA,cAAA,EAAiB,GAAG,CAAA,CAAE,CAAA;AACrC,EAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,IAAA,KAAA,CAAM,IAAA,CAAK,EAAA,EAAI,CAAA,EAAG,MAAA,CAAO,MAAM,KAAK,MAAA,CAAO,MAAA,GAAS,MAAA,GAAS,IAAI,CAAA,CAAE,CAAA;AACnE,IAAA,KAAA,MAAW,CAAA,IAAK,OAAO,OAAA,EAAS;AAC9B,MAAA,MAAM,KAAA,GACJ,CAAA,CAAE,MAAA,KAAW,MAAA,GAAU,CAAA,CAAE,QAAA,KAAa,OAAA,GAAU,MAAA,GAAS,MAAA,GAAU,CAAA,CAAE,MAAA,CAAO,WAAA,EAAY;AAC1F,MAAA,KAAA,CAAM,IAAA,CAAK,CAAA,EAAA,EAAK,KAAA,CAAM,MAAA,CAAO,CAAC,CAAC,CAAA,EAAA,EAAK,CAAA,CAAE,EAAA,CAAG,OAAO,KAAK,CAAC,CAAA,EAAA,EAAK,CAAA,CAAE,MAAM,CAAA,CAAE,CAAA;AACrE,MAAA,IAAI,CAAA,CAAE,MAAA,KAAW,MAAA,EAAQ,KAAA,CAAM,IAAA,CAAK,CAAA,QAAA,EAAW,EAAA,CAAG,MAAA,CAAO,KAAK,CAAC,CAAA,EAAA,EAAK,CAAA,CAAE,GAAG,CAAA,CAAE,CAAA;AAAA,IAC7E;AAAA,EACF;AACA,EAAA,OAAO,KAAA,CAAM,KAAK,IAAI,CAAA;AACxB;AAMA,IAAM,cAAA,GAA4B;AAAA,EAChC,oBAAA;AAAA,EACA,kBAAA;AAAA,EACA,mBAAA;AAAA,EACA,cAAA;AAAA,EACA,cAAA;AAAA,EACA,qBAAA;AAAA,EACA;AACF,CAAA;AAEA,eAAe,WAAA,CACb,GAAA,EACA,KAAA,EACA,UAAA,EACkB;AAGlB,EAAA,MAAM,EAAA,GAA8B,EAAE,SAAA,EAAW,CAACA,yBAAkB,CAAA,EAAE;AACtE,EAAA,MAAM,SAAS,IAAIC,eAAA;AAAA,IACjB,EAAE,IAAA,EAAM,eAAA,EAAiB,OAAA,EAAS,GAAA,EAAI;AAAA,IACtC,EAAE,YAAA,EAAc,EAAE,UAAA,EAAY,EAAE,CAACC,mBAAY,GAAG,EAAA,EAAG,EAAE;AAAE,GACzD;AACA,EAAA,MAAM,YAAY,IAAIC,+CAAA,CAA8B,IAAI,GAAA,CAAI,GAAG,CAAA,EAAG;AAAA,IAChE,WAAA,EAAa,KAAA,GAAQ,EAAE,OAAA,EAAS,EAAE,eAAe,CAAA,OAAA,EAAU,KAAK,CAAA,CAAA,EAAG,EAAE,GAAI,MAAA;AAAA,IACzE,KAAA,EAAO,OAAO,KAAA,EAAO,IAAA,KAAS;AAC5B,MAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,KAAA,EAAO,IAAI,CAAA;AACnC,MAAA,UAAA,GAAa,GAAG,CAAA;AAChB,MAAA,OAAO,GAAA;AAAA,IACT;AAAA,GACD,CAAA;AACD,EAAA,MAAM,MAAA,CAAO,QAAQ,SAAS,CAAA;AAC9B,EAAA,IAAI;AACF,IAAA,MAAM,YAAA,GAAe,OAAO,qBAAA,EAAsB;AAClD,IAAA,MAAM,QAAgB,EAAC;AACvB,IAAA,MAAM,YAAwB,EAAC;AAC/B,IAAA,IAAI,MAAA;AACJ,IAAA,IAAI,cAAc,KAAA,EAAO;AACvB,MAAA,GAAG;AAKD,QAAA,MAAM,GAAA,GAAM,MAAM,MAAA,CAAO,OAAA;AAAA,UACvB,EAAE,QAAQ,YAAA,EAAc,MAAA,EAAQ,SAAS,EAAE,MAAA,EAAO,GAAI,EAAC,EAAE;AAAA,UACzDC;AAAA,SACF;AACA,QAAA,MAAM,IAAA,GAAOC,8BAAA,CAAsB,KAAA,CAAM,GAAG,CAAA;AAC5C,QAAA,MAAM,QAAA,GAAY,GAAA,CAAmD,KAAA,IAAS,EAAC;AAC/E,QAAA,IAAA,CAAK,KAAA,CAAM,OAAA,CAAQ,CAAC,IAAA,EAAM,CAAA,KAAM;AAC9B,UAAA,MAAM,OAAA,GAAU,QAAA,CAAS,CAAC,CAAA,EAAG,eAAA;AAC7B,UAAA,IAAI,OAAA,KAAY,KAAA,CAAA,EAAY,IAAA,CAAiC,eAAA,GAAkB,OAAA;AAAA,QACjF,CAAC,CAAA;AACD,QAAA,KAAA,CAAM,IAAA,CAAK,GAAG,IAAA,CAAK,KAAK,CAAA;AACxB,QAAA,MAAA,GAAS,IAAA,CAAK,UAAA;AAAA,MAChB,CAAA,QAAS,MAAA;AAAA,IACX;AACA,IAAA,IAAI,cAAc,SAAA,EAAW;AAC3B,MAAA,GAAG;AACD,QAAA,MAAM,OAAO,MAAM,MAAA,CAAO,aAAA,CAAc,EAAE,QAAQ,CAAA;AAClD,QAAA,SAAA,CAAU,IAAA,CAAK,GAAG,IAAA,CAAK,SAAS,CAAA;AAChC,QAAA,MAAA,GAAS,IAAA,CAAK,UAAA;AAAA,MAChB,CAAA,QAAS,MAAA;AAAA,IACX;AAEA,IAAA,MAAM,OAAO,IAAI,GAAA,CAAI,SAAA,CAAU,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,GAAG,CAAA,CAAE,OAAO,CAAC,CAAA,KAAM,EAAE,UAAA,CAAW,SAAS,CAAC,CAAC,CAAA;AACvF,IAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,MAAA,MAAM,GAAA,GAAM,MAAA,CAAO,IAAI,CAAA,EAAG,WAAA;AAC1B,MAAA,IAAI,OAAO,GAAA,KAAQ,QAAA,EAAU,IAAA,CAAK,IAAI,GAAG,CAAA;AAAA,IAC3C;AACA,IAAA,MAAM,KAAA,uBAAY,GAAA,EAAwC;AAC1D,IAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,MAAA,IAAI;AACF,QAAA,KAAA,CAAM,GAAA,CAAI,MAAM,MAAM,MAAA,CAAO,aAAa,EAAE,GAAA,EAAK,CAAA,EAAG,QAAQ,CAAA;AAAA,MAC9D,SAAS,GAAA,EAAK;AACZ,QAAA,KAAA,CAAM,GAAA,CAAI,GAAA,EAAK,GAAA,YAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,KAAA,CAAM,MAAA,CAAO,GAAG,CAAC,CAAC,CAAA;AAAA,MACpE;AAAA,IACF;AACA,IAAA,OAAO,EAAE,YAAA,EAAc,KAAA,EAAO,SAAA,EAAW,KAAA,EAAM;AAAA,EACjD,CAAA,SAAE;AACA,IAAA,MAAM,OAAO,KAAA,EAAM;AAAA,EACrB;AACF;AAEA,SAAS,YAAA,CACP,CAAA,EACA,YAAA,EACA,GAAA,EACM;AACN,EAAA,MAAM,UAAA,GAAa,CAAA,CAAE,KAAA,CAAM,MAAA,CAAO,CAAC,MAAM,MAAA,CAAO,CAAC,CAAA,EAAG,WAAA,KAAgB,MAAS,CAAA;AAC7E,EAAA,MAAM,WAAA,GAAc,CAAA,CAAE,SAAA,CAAU,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,GAAA,CAAI,UAAA,CAAW,SAAS,CAAC,CAAA;AACzE,EAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,MAAA,GAAS,CAAA,IAAK,WAAA,CAAY,SAAS,CAAA,IAAK,CAAA,CAAE,KAAA,CAAM,IAAA,CAAK,aAAa,CAAA;AAE3F,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,KAAA,MAAW,MAAM,cAAA,EAAgB;AAC/B,MAAA,IAAI,EAAA,KAAO,uBAAA,EAAyB,GAAA,CAAI,EAAA,EAAI,QAAQ,0BAA0B,CAAA;AAAA,IAChF;AAAA,EACF,CAAA,MAAO;AAEL,IAAA,MAAM,QAAA,GAAW,CAAA,CAAE,YAAA,EAAc,UAAA,GAAaH,mBAAY,CAAA,KAAM,MAAA;AAChE,IAAA,GAAA;AAAA,MACE,oBAAA;AAAA,MACA,WAAW,MAAA,GAAS,MAAA;AAAA,MACpB,QAAA,GACI,CAAA,EAAGA,mBAAY,CAAA,YAAA,CAAA,GACf,+BAA+BA,mBAAY,CAAA,2BAAA;AAAA,KACjD;AAGA,IAAA,MAAM,YAAsB,EAAC;AAC7B,IAAA,KAAA,MAAW,KAAK,WAAA,EAAa;AAC3B,MAAA,IAAI,CAAA,CAAE,QAAA,KAAa,MAAA,IAAa,CAAA,CAAE,aAAaF,yBAAA,EAAoB;AACjE,QAAA,SAAA,CAAU,KAAK,CAAA,EAAG,CAAA,CAAE,GAAG,CAAA,cAAA,EAAiB,CAAA,CAAE,QAAQ,CAAA,CAAE,CAAA;AAAA,MACtD;AAAA,IACF;AACA,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,QAAQ,CAAA,IAAK,EAAE,KAAA,EAAO;AACrC,MAAA,IAAI,oBAAoB,KAAA,EAAO;AAC/B,MAAA,KAAA,MAAW,KAAK,QAAA,EAAU;AACxB,QAAA,IAAI,CAAA,CAAE,aAAaA,yBAAA,EAAoB;AACrC,UAAA,SAAA,CAAU,KAAK,CAAA,EAAG,GAAG,iBAAiB,CAAA,CAAE,QAAA,IAAY,cAAc,CAAA,CAAE,CAAA;AAAA,QACtE;AAAA,MACF;AAAA,IACF;AACA,IAAA,GAAA;AAAA,MACE,kBAAA;AAAA,MACA,SAAA,CAAU,SAAS,MAAA,GAAS,MAAA;AAAA,MAC5B,SAAA,CAAU,MAAA,GACN,CAAA,EAAG,SAAA,CAAU,IAAA,CAAK,IAAI,CAAC,CAAA,WAAA,EAAcA,yBAAkB,CAAA,CAAA,GACvD,CAAA,wBAAA,EAA2BA,yBAAkB,CAAA;AAAA,KACnD;AAGA,IAAA,MAAM,aAAuB,EAAC;AAC9B,IAAA,KAAA,MAAW,IAAA,IAAQ,EAAE,KAAA,EAAO;AAC1B,MAAA,MAAM,GAAA,GAAM,MAAA,CAAO,IAAI,CAAA,EAAG,WAAA;AAC1B,MAAA,IAAI,QAAQ,MAAA,EAAW;AACrB,QAAA,IAAI,aAAA,CAAc,IAAI,CAAA,EAAG;AACvB,UAAA,UAAA,CAAW,IAAA,CAAK,CAAA,EAAG,IAAA,CAAK,IAAI,CAAA,kDAAA,CAAoD,CAAA;AAAA,QAClF;AACA,QAAA;AAAA,MACF;AACA,MAAA,IAAI,OAAO,GAAA,KAAQ,QAAA,IAAY,CAAC,GAAA,CAAI,UAAA,CAAW,SAAS,CAAA,EAAG;AACzD,QAAA,UAAA,CAAW,IAAA,CAAK,GAAG,IAAA,CAAK,IAAI,oBAAoB,IAAA,CAAK,SAAA,CAAU,GAAG,CAAC,CAAA,mBAAA,CAAqB,CAAA;AACxF,QAAA;AAAA,MACF;AACA,MAAA,MAAM,IAAA,GAAO,CAAA,CAAE,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AAC5B,MAAA,IAAI,IAAA,YAAgB,KAAA,IAAS,CAAC,IAAA,EAAM,MAAA,EAAQ;AAC1C,QAAA,UAAA,CAAW,IAAA;AAAA,UACT,CAAA,EAAG,IAAA,CAAK,IAAI,CAAA,EAAA,EAAK,GAAG,CAAA,eAAA,EAAkB,IAAA,YAAgB,KAAA,GAAQ,CAAA,EAAA,EAAK,IAAA,CAAK,OAAO,CAAA,CAAA,CAAA,GAAM,EAAE,CAAA;AAAA,SACzF;AAAA,MACF;AAAA,IACF;AACA,IAAA,IAAI,UAAA,CAAW,MAAA,KAAW,CAAA,IAAK,UAAA,CAAW,WAAW,CAAA,EAAG;AACtD,MAAA,UAAA,CAAW,KAAK,2CAA2C,CAAA;AAAA,IAC7D;AACA,IAAA,GAAA;AAAA,MACE,mBAAA;AAAA,MACA,UAAA,CAAW,SAAS,MAAA,GAAS,MAAA;AAAA,MAC7B,UAAA,CAAW,SACP,UAAA,CAAW,IAAA,CAAK,IAAI,CAAA,GACpB,CAAA,EAAG,WAAW,MAAM,CAAA,gCAAA;AAAA,KAC1B;AAGA,IAAA,MAAM,UAAoB,EAAC;AAC3B,IAAA,KAAA,MAAW,IAAA,IAAQ,EAAE,KAAA,EAAO;AAC1B,MAAA,MAAM,EAAA,GAAK,OAAO,IAAI,CAAA;AACtB,MAAA,IAAI,CAAC,EAAA,EAAI;AACT,MAAA,MAAM,MAAM,EAAA,CAAG,UAAA;AACf,MAAA,IACE,QAAQ,MAAA,KACP,CAAC,MAAM,OAAA,CAAQ,GAAG,KAAK,GAAA,CAAI,MAAA,KAAW,KAAK,CAAC,GAAA,CAAI,MAAM,CAAC,CAAA,KAAM,WAAW,GAAA,CAAI,CAAC,CAAC,CAAA,CAAA,EAC/E;AACA,QAAA,OAAA,CAAQ,IAAA,CAAK,GAAG,IAAA,CAAK,IAAI,mBAAmB,IAAA,CAAK,SAAA,CAAU,GAAG,CAAC,CAAA,CAAE,CAAA;AAAA,MACnE;AACA,MAAA,KAAA,MAAW,GAAA,IAAO,CAAC,KAAA,EAAO,aAAa,CAAA,EAAY;AACjD,QAAA,IAAK,EAAA,CAA+B,GAAG,CAAA,KAAM,MAAA,EAAW;AACtD,UAAA,OAAA,CAAQ,KAAK,CAAA,EAAG,IAAA,CAAK,IAAI,CAAA,KAAA,EAAQ,GAAG,CAAA,sCAAA,CAAwC,CAAA;AAAA,QAC9E;AAAA,MACF;AAAA,IACF;AACA,IAAA,GAAA;AAAA,MACE,cAAA;AAAA,MACA,OAAA,CAAQ,SAAS,MAAA,GAAS,MAAA;AAAA,MAC1B,OAAA,CAAQ,MAAA,GAAS,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAA,GAAI;AAAA,KACxC;AAGA,IAAA,MAAM,QAAkB,EAAC;AACzB,IAAA,MAAM,YAAsB,EAAC;AAC7B,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,QAAQ,CAAA,IAAK,EAAE,KAAA,EAAO;AACrC,MAAA,IAAI,oBAAoB,KAAA,EAAO;AAC/B,MAAA,KAAA,MAAW,KAAK,QAAA,EAAU;AACxB,QAAA,MAAM,MAAA,GAAS,EAAE,SAAA,CAAU,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,QAAQ,GAAG,CAAA;AAEpD,QAAA,MAAM,EAAA,GAAM,UAAA,CAAW,CAAC,CAAA,IAAK,WAAW,MAAM,CAAA;AAC9C,QAAA,MAAM,MAAM,EAAA,EAAI,GAAA;AAChB,QAAA,IAAI,QAAQ,MAAA,EAAW;AACrB,UAAA,KAAA,CAAM,IAAA,CAAK,CAAA,EAAG,GAAG,CAAA,mBAAA,CAAqB,CAAA;AAAA,QACxC,CAAA,MAAA,IAAW,CAAC,KAAA,CAAM,GAAG,CAAA,EAAG;AACtB,UAAA,KAAA,CAAM,IAAA,CAAK,CAAA,EAAG,GAAG,CAAA,qCAAA,CAAuC,CAAA;AAAA,QAC1D;AACA,QAAA,MAAM,IAAA,GAAO,MAAA,IAAU,CAAA,GAAI,CAAA,CAAE,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,CAAA,CAAE,IAAA,EAAM,QAAQ,CAAA,CAAE,QAAA,CAAS,OAAO,CAAA;AAClF,QAAA,MAAM,YAAA,GAAe,MAAM,GAAG,CAAA,IAAA,CAAM,IAAI,eAAA,IAAmB,EAAC,EAAG,QAAA,CAAS,OAAO,CAAA;AAC/E,QAAA,IAAI,WAAA,CAAY,IAAI,CAAA,IAAK,CAAC,YAAA,EAAc;AACtC,UAAA,SAAA,CAAU,IAAA,CAAK,CAAA,EAAG,GAAG,CAAA,2DAAA,CAA6D,CAAA;AAAA,QACpF;AAAA,MACF;AAAA,IACF;AACA,IAAA,GAAA;AAAA,MACE,cAAA;AAAA,MACA,KAAA,CAAM,SAAS,MAAA,GAAS,MAAA;AAAA,MACxB,KAAA,CAAM,MAAA,GAAS,KAAA,CAAM,IAAA,CAAK,IAAI,CAAA,GAAI;AAAA,KACpC;AACA,IAAA,GAAA;AAAA,MACE,qBAAA;AAAA,MACA,SAAA,CAAU,SAAS,MAAA,GAAS,MAAA;AAAA,MAC5B,SAAA,CAAU,MAAA,GAAS,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA,GAAI;AAAA,KAC5C;AAAA,EACF;AAGA,EAAA,IAAI,CAAC,YAAA,EAAc;AACjB,IAAA,GAAA,CAAI,uBAAA,EAAyB,QAAQ,kCAAkC,CAAA;AAAA,EACzE,CAAA,MAAO;AACL,IAAA,MAAM,OAAA,GAAU,CAAA,CAAE,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA,KAAM,CAAC,kBAAA,CAAmB,CAAC,CAAC,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,IAAI,CAAA;AAC/E,IAAA,GAAA;AAAA,MACE,uBAAA;AAAA,MACA,OAAA,CAAQ,SAAS,MAAA,GAAS,MAAA;AAAA,MAC1B,QAAQ,MAAA,GACJ,CAAA,uBAAA,EAA0B,QAAQ,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA,GAC5C;AAAA,KACN;AAAA,EACF;AACF;AAMA,eAAe,SAAA,CACb,GAAA,EACA,SAAA,EACA,GAAA,EACe;AACf,EAAA,IAAI,CAAC,SAAA,EAAW;AACd,IAAA,MAAM,MAAA,GAAS,6CAAA;AACf,IAAA,GAAA,CAAI,gBAAA,EAAkB,QAAQ,MAAM,CAAA;AACpC,IAAA,GAAA,CAAI,wBAAA,EAA0B,QAAQ,MAAM,CAAA;AAC5C,IAAA,GAAA,CAAI,aAAA,EAAe,QAAQ,MAAM,CAAA;AACjC,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,EAAE,mBAAA,EAAoB,GAAIM,oCAAA,CAA6B,SAAS,CAAA;AACtE,EAAA,GAAA;AAAA,IACE,gBAAA;AAAA,IACA,sBAAsB,MAAA,GAAS,MAAA;AAAA,IAC/B,mBAAA,GACI,CAAA,4BAAA,EAA+B,mBAAA,CAAoB,IAAI,CAAA,CAAA,GACvD,CAAA,mDAAA,EAAsD,IAAA,CAAK,SAAA,CAAU,SAAA,CAAU,OAAA,CAAQ,GAAA,CAAI,kBAAkB,CAAC,CAAC,CAAA,CAAA;AAAA,GACrH;AAEA,EAAA,IAAI,GAAA;AACJ,EAAA,IAAI;AAGF,IAAA,GAAA,GAAM,MAAMC,8CAAA,CAAuC,GAAA,EAAK,EAAE,qBAAqB,CAAA;AAAA,EACjF,SAAS,GAAA,EAAK;AACZ,IAAA,GAAA;AAAA,MACE,wBAAA;AAAA,MACA,MAAA;AAAA,MACA,CAAA,wCAAA,EAA2C,OAAA,CAAQ,GAAG,CAAC,CAAA;AAAA,KACzD;AACA,IAAA,GAAA,CAAI,aAAA,EAAe,QAAQ,gCAAgC,CAAA;AAC3D,IAAA;AAAA,EACF;AACA,EAAA,GAAA;AAAA,IACE,wBAAA;AAAA,IACA,GAAA,CAAI,QAAA,KAAa,GAAA,GAAM,MAAA,GAAS,MAAA;AAAA,IAChC,IAAI,QAAA,KAAa,GAAA,GACb,CAAA,gBAAA,EAAmB,GAAG,KACtB,CAAA,YAAA,EAAe,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI,QAAQ,CAAC,CAAA,MAAA,EAAS,IAAA,CAAK,SAAA,CAAU,GAAG,CAAC,CAAA;AAAA,GAC7E;AAEA,EAAA,MAAM,UAAA,GAAa,GAAA,CAAI,qBAAA,GAAwB,CAAC,CAAA;AAChD,EAAA,IAAI,CAAC,UAAA,EAAY;AACf,IAAA,GAAA,CAAI,aAAA,EAAe,QAAQ,4DAA4D,CAAA;AACvF,IAAA;AAAA,EACF;AACA,EAAA,IAAI;AACF,IAAA,MAAM,QAAA,GAAW,MAAMC,2CAAA,CAAoC,UAAU,CAAA;AACrE,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,GAAA,CAAI,aAAA,EAAe,MAAA,EAAQ,CAAA,2CAAA,EAA8C,UAAU,CAAA,CAAE,CAAA;AAAA,IACvF,CAAA,MAAA,IAAW,QAAA,CAAS,MAAA,KAAW,UAAA,EAAY;AACzC,MAAA,GAAA;AAAA,QACE,aAAA;AAAA,QACA,MAAA;AAAA,QACA,CAAA,UAAA,EAAa,IAAA,CAAK,SAAA,CAAU,QAAA,CAAS,MAAM,CAAC,CAAA,gBAAA,EAAmB,IAAA,CAAK,SAAA,CAAU,UAAU,CAAC,CAAA;AAAA,OAC3F;AAAA,IACF,CAAA,MAAO;AACL,MAAA,GAAA,CAAI,aAAA,EAAe,MAAA,EAAQ,CAAA,cAAA,EAAiB,UAAU,CAAA,CAAE,CAAA;AAAA,IAC1D;AAAA,EACF,SAAS,GAAA,EAAK;AACZ,IAAA,GAAA,CAAI,eAAe,MAAA,EAAQ,CAAA,0CAAA,EAA6C,OAAA,CAAQ,GAAG,CAAC,CAAA,CAAE,CAAA;AAAA,EACxF;AACF;AAMA,SAAS,OAAO,IAAA,EAAuC;AACrD,EAAA,MAAM,EAAA,GAAK,KAAK,KAAA,EAAO,EAAA;AACvB,EAAA,OAAO,EAAA,IAAM,OAAO,EAAA,KAAO,QAAA,GAAY,EAAA,GAAuB,MAAA;AAChE;AAEA,SAAS,cAAc,IAAA,EAAqB;AAC1C,EAAA,OACE,IAAA,CAAK,QAAQC,4BAAqB,CAAA,KAAM,UAAa,MAAA,CAAO,IAAI,GAAG,WAAA,KAAgB,MAAA;AAEvF;AAEA,SAAS,WAAW,IAAA,EAAgE;AAClF,EAAA,MAAM,EAAA,GAAK,MAAM,KAAA,EAAO,EAAA;AACxB,EAAA,OAAO,EAAA,IAAM,OAAO,EAAA,KAAO,QAAA,GAAW,EAAA,GAAK,MAAA;AAC7C;AAEA,SAAS,MAAM,KAAA,EAAgE;AAC7E,EAAA,OACE,OAAO,KAAA,KAAU,QAAA,IACjB,KAAA,KAAU,IAAA,IACV,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,IACpB,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,KAAM,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA,IAAK,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,KAAM,OAAO,CAAA,KAAM,QAAQ,CAAC,CAAA;AAE/F;AAEA,SAAS,mBAAmB,IAAA,EAAqB;AAC/C,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,KAAA,EAAO,eAAA,IAAoB,IAAA,CAAiC,eAAA;AACjF,EAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,IAAK,QAAQ,MAAA,GAAS,CAAA;AACpD;AAEA,IAAM,cAAA,GAAiB,2EAAA;AACvB,IAAM,kBAAA,GAAqB,wCAAA;AAE3B,SAAS,YAAY,IAAA,EAAuB;AAC1C,EAAA,OAAO,eAAe,IAAA,CAAK,IAAI,CAAA,IAAK,kBAAA,CAAmB,KAAK,IAAI,CAAA;AAClE;AAEA,SAAS,QAAQ,GAAA,EAAsB;AACrC,EAAA,OAAO,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,OAAO,GAAG,CAAA;AACxD","file":"check-JEM5PN64.cjs","sourcesContent":["/**\n * Store profiles for `synapse check`: which checks each host applies, and how\n * hard. A check a profile does not list is not run for that target.\n *\n * `error` fails the run. `warn` is reported and does not: it marks a\n * requirement the host is documented to have but is not known to enforce, so a\n * failure is a risk rather than a certain break.\n */\n\nexport type CheckId =\n | \"extension-declared\"\n | \"ui-resource-mime\"\n | \"tool-resource-uri\"\n | \"tool-ui-meta\"\n | \"resource-csp\"\n | \"resource-data-fonts\"\n | \"auth-challenge\"\n | \"auth-resource-metadata\"\n | \"auth-issuer\"\n | \"tool-security-schemes\";\n\nexport type Severity = \"error\" | \"warn\";\n\nexport interface ProfileRule {\n severity: Severity;\n /** The host requirement behind the rule, printed when the check fails. */\n why: string;\n}\n\nexport interface Profile {\n name: TargetName;\n description: string;\n rules: Partial<Record<CheckId, ProfileRule>>;\n}\n\nexport type TargetName = \"nimblebrain\" | \"claude\" | \"chatgpt\";\n\nconst SPEC_MIME = \"UI resources are served as text/html;profile=mcp-app.\";\nconst RESOLVES = \"A tool's ui.resourceUri must name a ui:// resource the server serves.\";\nconst UI_META =\n \"ui.visibility is a non-empty subset of model/app; csp and permissions belong on the resource, not the tool.\";\nconst EXTENSION = \"A client uses MCP Apps only when both sides declare io.modelcontextprotocol/ui.\";\nconst CHALLENGE = \"Sign-in starts from a 401 whose WWW-Authenticate carries resource_metadata.\";\nconst PRM_RESOURCE =\n \"The protected-resource metadata's resource must equal the connector URL exactly.\";\nconst ISSUER =\n \"The authorization server's metadata issuer must equal the advertised authorization server exactly (RFC 8414).\";\n\nexport const PROFILES: Record<TargetName, Profile> = {\n nimblebrain: {\n name: \"nimblebrain\",\n description: \"The NimbleBrain host.\",\n rules: {\n \"ui-resource-mime\": { severity: \"error\", why: SPEC_MIME },\n \"tool-resource-uri\": { severity: \"error\", why: RESOLVES },\n \"tool-ui-meta\": { severity: \"error\", why: UI_META },\n // The host resolves an app by its ui:// scheme and does not gate on the\n // declaration, so a missing one is a portability risk, not a break.\n \"extension-declared\": { severity: \"warn\", why: EXTENSION },\n // Only reported when the server challenges at all.\n \"auth-challenge\": { severity: \"warn\", why: CHALLENGE },\n \"auth-resource-metadata\": { severity: \"warn\", why: PRM_RESOURCE },\n \"auth-issuer\": { severity: \"warn\", why: ISSUER },\n },\n },\n claude: {\n name: \"claude\",\n description: \"claude.ai and Claude Desktop.\",\n rules: {\n \"ui-resource-mime\": { severity: \"error\", why: SPEC_MIME },\n \"tool-resource-uri\": { severity: \"error\", why: RESOLVES },\n \"tool-ui-meta\": { severity: \"error\", why: UI_META },\n \"extension-declared\": { severity: \"error\", why: EXTENSION },\n \"resource-csp\": {\n severity: \"warn\",\n why: \"Claude builds the frame's security policy from ui.csp; an undeclared origin is blocked.\",\n },\n \"resource-data-fonts\": {\n severity: \"error\",\n why: \"Claude's default security policy blocks data: fonts unless the resource declares them.\",\n },\n \"auth-challenge\": { severity: \"error\", why: CHALLENGE },\n \"auth-resource-metadata\": { severity: \"error\", why: PRM_RESOURCE },\n // Claude has been observed to accept an issuer that differs by a trailing\n // slash, so this is a correctness warning for Claude rather than a break.\n \"auth-issuer\": { severity: \"warn\", why: ISSUER },\n },\n },\n chatgpt: {\n name: \"chatgpt\",\n description: \"ChatGPT apps.\",\n rules: {\n \"ui-resource-mime\": { severity: \"error\", why: SPEC_MIME },\n \"tool-resource-uri\": { severity: \"error\", why: RESOLVES },\n \"tool-ui-meta\": { severity: \"error\", why: UI_META },\n \"extension-declared\": { severity: \"error\", why: EXTENSION },\n \"resource-csp\": {\n severity: \"error\",\n why: \"ChatGPT reads the frame's security policy from ui.csp, and app submission requires it.\",\n },\n \"resource-data-fonts\": {\n severity: \"warn\",\n why: \"A data: font is blocked by a security policy that does not declare it.\",\n },\n \"auth-challenge\": { severity: \"error\", why: CHALLENGE },\n \"auth-resource-metadata\": { severity: \"error\", why: PRM_RESOURCE },\n \"auth-issuer\": { severity: \"error\", why: ISSUER },\n \"tool-security-schemes\": {\n severity: \"error\",\n why: \"ChatGPT prompts for sign-in mid-conversation only for tools that declare securitySchemes.\",\n },\n },\n },\n};\n\nexport function isTargetName(value: string): value is TargetName {\n return Object.hasOwn(PROFILES, value);\n}\n","/**\n * `synapse check` — verify, from outside, what a host sees of a running MCP\n * server: the UI resources and their metadata, the MCP Apps extension\n * declaration, and the OAuth discovery surface a host signs in through.\n *\n * Every check runs once against the server; each target's profile then decides\n * which results count and how hard (see `./profiles.ts`).\n */\n\nimport type {\n McpUiClientCapabilities,\n McpUiResourceMeta,\n McpUiToolMeta,\n} from \"@modelcontextprotocol/ext-apps\";\nimport {\n EXTENSION_ID,\n RESOURCE_MIME_TYPE,\n RESOURCE_URI_META_KEY,\n} from \"@modelcontextprotocol/ext-apps/server\";\nimport {\n discoverAuthorizationServerMetadata,\n discoverOAuthProtectedResourceMetadata,\n extractWWWAuthenticateParams,\n} from \"@modelcontextprotocol/sdk/client/auth.js\";\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\nimport type {\n ReadResourceResult,\n Resource,\n ServerCapabilities,\n Tool,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { ListToolsResultSchema, ResultSchema } from \"@modelcontextprotocol/sdk/types.js\";\nimport { type CheckId, PROFILES, type Severity, type TargetName } from \"./profiles.js\";\n\nexport { type CheckId, isTargetName, PROFILES, type Profile, type TargetName } from \"./profiles.js\";\n\nexport type CheckStatus = \"pass\" | \"fail\" | \"skip\";\n\nexport interface CheckResult {\n id: CheckId;\n status: CheckStatus;\n detail: string;\n}\n\nexport interface TargetReport {\n target: TargetName;\n results: Array<CheckResult & { severity: Severity; why: string }>;\n /** True when any `error`-severity check failed. */\n failed: boolean;\n}\n\nexport interface CheckOptions {\n /** Bearer token for a server that requires auth. Without one, checks that need a session are skipped. */\n token?: string;\n}\n\nconst UI_SCHEME = \"ui://\";\nconst VISIBILITY = new Set([\"model\", \"app\"]);\n\ntype ResourceContents = ReadResourceResult[\"contents\"][number];\n\ninterface Session {\n capabilities: ServerCapabilities | undefined;\n tools: Tool[];\n resources: Resource[];\n reads: Map<string, ResourceContents[] | Error>;\n}\n\n/** Run every check against `url` once. */\nexport async function runChecks(url: string, options: CheckOptions = {}): Promise<CheckResult[]> {\n const results: CheckResult[] = [];\n const add = (id: CheckId, status: CheckStatus, detail: string) =>\n results.push({ id, status, detail });\n\n // Connect without credentials first: the response to that is what a host\n // sees before sign-in, and it decides whether the server requires auth.\n let challenge: Response | undefined;\n let session: Session | undefined;\n let connectError: unknown;\n try {\n session = await openSession(url, undefined, (res) => {\n if (res.status === 401) challenge ??= res;\n });\n } catch (err) {\n connectError = err;\n }\n\n const authRequired = challenge !== undefined;\n if (authRequired && options.token) {\n try {\n session = await openSession(url, options.token);\n connectError = undefined;\n } catch (err) {\n connectError = err;\n }\n }\n\n // A server that cannot be reached is not a server that passes.\n if (!session && (!authRequired || options.token)) {\n throw new Error(\n `could not connect to ${url}${authRequired ? \" with the given token\" : \"\"}: ${message(connectError)}`,\n );\n }\n\n await checkAuth(url, challenge, add);\n\n if (!session) {\n for (const id of SESSION_CHECKS) {\n add(id, \"skip\", \"the server requires auth; pass --token to run it\");\n }\n return results;\n }\n\n checkSession(session, authRequired, add);\n return results;\n}\n\n/** Apply each target's profile to one set of results. */\nexport function applyProfiles(results: CheckResult[], targets: TargetName[]): TargetReport[] {\n return targets.map((target) => {\n const rules = PROFILES[target].rules;\n const scored = results.flatMap((r) => {\n const rule = rules[r.id];\n return rule ? [{ ...r, severity: rule.severity, why: rule.why }] : [];\n });\n return {\n target,\n results: scored,\n failed: scored.some((r) => r.status === \"fail\" && r.severity === \"error\"),\n };\n });\n}\n\n/** One line per check, grouped by target. */\nexport function formatReport(url: string, reports: TargetReport[]): string {\n const width = Math.max(...reports.flatMap((r) => r.results.map((c) => c.id.length)), 0);\n const lines = [`synapse check ${url}`];\n for (const report of reports) {\n lines.push(\"\", `${report.target}: ${report.failed ? \"FAIL\" : \"ok\"}`);\n for (const r of report.results) {\n const label =\n r.status === \"fail\" ? (r.severity === \"error\" ? \"FAIL\" : \"WARN\") : r.status.toUpperCase();\n lines.push(` ${label.padEnd(4)} ${r.id.padEnd(width)} ${r.detail}`);\n if (r.status === \"fail\") lines.push(` ${\"\".padEnd(width)} ${r.why}`);\n }\n }\n return lines.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// session\n// ---------------------------------------------------------------------------\n\nconst SESSION_CHECKS: CheckId[] = [\n \"extension-declared\",\n \"ui-resource-mime\",\n \"tool-resource-uri\",\n \"tool-ui-meta\",\n \"resource-csp\",\n \"resource-data-fonts\",\n \"tool-security-schemes\",\n];\n\nasync function openSession(\n url: string,\n token: string | undefined,\n onResponse?: (res: Response) => void,\n): Promise<Session> {\n // Declare MCP Apps support, so a server that gates its UI on the client's\n // capability shows the UI a host would see.\n const ui: McpUiClientCapabilities = { mimeTypes: [RESOURCE_MIME_TYPE] };\n const client = new Client(\n { name: \"synapse-check\", version: \"1\" },\n { capabilities: { extensions: { [EXTENSION_ID]: ui } } },\n );\n const transport = new StreamableHTTPClientTransport(new URL(url), {\n requestInit: token ? { headers: { Authorization: `Bearer ${token}` } } : undefined,\n fetch: async (input, init) => {\n const res = await fetch(input, init);\n onResponse?.(res);\n return res;\n },\n });\n await client.connect(transport);\n try {\n const capabilities = client.getServerCapabilities();\n const tools: Tool[] = [];\n const resources: Resource[] = [];\n let cursor: string | undefined;\n if (capabilities?.tools) {\n do {\n // Requested raw rather than through `listTools()`: the SDK's tool schema\n // strips fields it does not model, and ChatGPT documents `securitySchemes`\n // at the tool's top level as well as in `_meta`. The one field is copied\n // back onto each parsed tool so `hasSecuritySchemes` can see either place.\n const raw = await client.request(\n { method: \"tools/list\", params: cursor ? { cursor } : {} },\n ResultSchema,\n );\n const page = ListToolsResultSchema.parse(raw);\n const rawTools = (raw as { tools?: Array<Record<string, unknown>> }).tools ?? [];\n page.tools.forEach((tool, i) => {\n const schemes = rawTools[i]?.securitySchemes;\n if (schemes !== undefined) (tool as Record<string, unknown>).securitySchemes = schemes;\n });\n tools.push(...page.tools);\n cursor = page.nextCursor;\n } while (cursor);\n }\n if (capabilities?.resources) {\n do {\n const page = await client.listResources({ cursor });\n resources.push(...page.resources);\n cursor = page.nextCursor;\n } while (cursor);\n }\n\n const uris = new Set(resources.map((r) => r.uri).filter((u) => u.startsWith(UI_SCHEME)));\n for (const tool of tools) {\n const uri = toolUi(tool)?.resourceUri;\n if (typeof uri === \"string\") uris.add(uri);\n }\n const reads = new Map<string, ResourceContents[] | Error>();\n for (const uri of uris) {\n try {\n reads.set(uri, (await client.readResource({ uri })).contents);\n } catch (err) {\n reads.set(uri, err instanceof Error ? err : new Error(String(err)));\n }\n }\n return { capabilities, tools, resources, reads };\n } finally {\n await client.close();\n }\n}\n\nfunction checkSession(\n s: Session,\n authRequired: boolean,\n add: (id: CheckId, status: CheckStatus, detail: string) => void,\n): void {\n const boundTools = s.tools.filter((t) => toolUi(t)?.resourceUri !== undefined);\n const uiResources = s.resources.filter((r) => r.uri.startsWith(UI_SCHEME));\n const hasUi = boundTools.length > 0 || uiResources.length > 0 || s.tools.some(legacyBinding);\n\n if (!hasUi) {\n for (const id of SESSION_CHECKS) {\n if (id !== \"tool-security-schemes\") add(id, \"skip\", \"the server exposes no UI\");\n }\n } else {\n // extension-declared\n const declared = s.capabilities?.extensions?.[EXTENSION_ID] !== undefined;\n add(\n \"extension-declared\",\n declared ? \"pass\" : \"fail\",\n declared\n ? `${EXTENSION_ID} is declared`\n : `the server does not declare ${EXTENSION_ID} in capabilities.extensions`,\n );\n\n // ui-resource-mime: every ui:// resource, as listed and as read.\n const wrongMime: string[] = [];\n for (const r of uiResources) {\n if (r.mimeType !== undefined && r.mimeType !== RESOURCE_MIME_TYPE) {\n wrongMime.push(`${r.uri} is listed as ${r.mimeType}`);\n }\n }\n for (const [uri, contents] of s.reads) {\n if (contents instanceof Error) continue;\n for (const c of contents) {\n if (c.mimeType !== RESOURCE_MIME_TYPE) {\n wrongMime.push(`${uri} is served as ${c.mimeType ?? \"no MIME type\"}`);\n }\n }\n }\n add(\n \"ui-resource-mime\",\n wrongMime.length ? \"fail\" : \"pass\",\n wrongMime.length\n ? `${wrongMime.join(\"; \")}; expected ${RESOURCE_MIME_TYPE}`\n : `every ui:// resource is ${RESOURCE_MIME_TYPE}`,\n );\n\n // tool-resource-uri\n const unresolved: string[] = [];\n for (const tool of s.tools) {\n const uri = toolUi(tool)?.resourceUri;\n if (uri === undefined) {\n if (legacyBinding(tool)) {\n unresolved.push(`${tool.name} binds its UI only through the deprecated flat key`);\n }\n continue;\n }\n if (typeof uri !== \"string\" || !uri.startsWith(UI_SCHEME)) {\n unresolved.push(`${tool.name}: ui.resourceUri ${JSON.stringify(uri)} is not a ui:// URI`);\n continue;\n }\n const read = s.reads.get(uri);\n if (read instanceof Error || !read?.length) {\n unresolved.push(\n `${tool.name}: ${uri} cannot be read${read instanceof Error ? ` (${read.message})` : \"\"}`,\n );\n }\n }\n if (boundTools.length === 0 && unresolved.length === 0) {\n unresolved.push(\"no tool binds a UI through ui.resourceUri\");\n }\n add(\n \"tool-resource-uri\",\n unresolved.length ? \"fail\" : \"pass\",\n unresolved.length\n ? unresolved.join(\"; \")\n : `${boundTools.length} tool(s) bind a UI that resolves`,\n );\n\n // tool-ui-meta\n const badMeta: string[] = [];\n for (const tool of s.tools) {\n const ui = toolUi(tool);\n if (!ui) continue;\n const vis = ui.visibility as unknown;\n if (\n vis !== undefined &&\n (!Array.isArray(vis) || vis.length === 0 || !vis.every((v) => VISIBILITY.has(v)))\n ) {\n badMeta.push(`${tool.name}: ui.visibility ${JSON.stringify(vis)}`);\n }\n for (const key of [\"csp\", \"permissions\"] as const) {\n if ((ui as Record<string, unknown>)[key] !== undefined) {\n badMeta.push(`${tool.name}: ui.${key} is on the tool, where hosts ignore it`);\n }\n }\n }\n add(\n \"tool-ui-meta\",\n badMeta.length ? \"fail\" : \"pass\",\n badMeta.length ? badMeta.join(\"; \") : \"tool ui metadata is well-formed\",\n );\n\n // resource-csp and resource-data-fonts, per UI resource read.\n const noCsp: string[] = [];\n const dataFonts: string[] = [];\n for (const [uri, contents] of s.reads) {\n if (contents instanceof Error) continue;\n for (const c of contents) {\n const listed = s.resources.find((r) => r.uri === uri);\n // Hosts read the content item's metadata, and fall back to the list entry.\n const ui = (resourceUi(c) ?? resourceUi(listed)) as McpUiResourceMeta | undefined;\n const csp = ui?.csp as unknown;\n if (csp === undefined) {\n noCsp.push(`${uri} declares no ui.csp`);\n } else if (!isCsp(csp)) {\n noCsp.push(`${uri}: ui.csp is not a map of origin lists`);\n }\n const html = \"text\" in c ? c.text : Buffer.from(c.blob, \"base64\").toString(\"utf-8\");\n const declaresData = isCsp(csp) && (csp.resourceDomains ?? []).includes(\"data:\");\n if (hasDataFont(html) && !declaresData) {\n dataFonts.push(`${uri} loads a data: font, and ui.csp.resourceDomains omits data:`);\n }\n }\n }\n add(\n \"resource-csp\",\n noCsp.length ? \"fail\" : \"pass\",\n noCsp.length ? noCsp.join(\"; \") : \"every UI resource declares ui.csp\",\n );\n add(\n \"resource-data-fonts\",\n dataFonts.length ? \"fail\" : \"pass\",\n dataFonts.length ? dataFonts.join(\"; \") : \"no undeclared data: fonts\",\n );\n }\n\n // tool-security-schemes: a question about auth, not about UI.\n if (!authRequired) {\n add(\"tool-security-schemes\", \"skip\", \"the server does not require auth\");\n } else {\n const missing = s.tools.filter((t) => !hasSecuritySchemes(t)).map((t) => t.name);\n add(\n \"tool-security-schemes\",\n missing.length ? \"fail\" : \"pass\",\n missing.length\n ? `no securitySchemes on: ${missing.join(\", \")}`\n : \"every tool declares securitySchemes\",\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// auth\n// ---------------------------------------------------------------------------\n\nasync function checkAuth(\n url: string,\n challenge: Response | undefined,\n add: (id: CheckId, status: CheckStatus, detail: string) => void,\n): Promise<void> {\n if (!challenge) {\n const reason = \"the server answered without a 401 challenge\";\n add(\"auth-challenge\", \"skip\", reason);\n add(\"auth-resource-metadata\", \"skip\", reason);\n add(\"auth-issuer\", \"skip\", reason);\n return;\n }\n\n const { resourceMetadataUrl } = extractWWWAuthenticateParams(challenge);\n add(\n \"auth-challenge\",\n resourceMetadataUrl ? \"pass\" : \"fail\",\n resourceMetadataUrl\n ? `401 names resource_metadata ${resourceMetadataUrl.href}`\n : `401 WWW-Authenticate carries no resource_metadata (${JSON.stringify(challenge.headers.get(\"www-authenticate\"))})`,\n );\n\n let prm: Awaited<ReturnType<typeof discoverOAuthProtectedResourceMetadata>>;\n try {\n // Without resource_metadata, a client falls back to the well-known path,\n // so the rest of the surface is still worth reporting.\n prm = await discoverOAuthProtectedResourceMetadata(url, { resourceMetadataUrl });\n } catch (err) {\n add(\n \"auth-resource-metadata\",\n \"fail\",\n `protected-resource metadata unreadable: ${message(err)}`,\n );\n add(\"auth-issuer\", \"skip\", \"no protected-resource metadata\");\n return;\n }\n add(\n \"auth-resource-metadata\",\n prm.resource === url ? \"pass\" : \"fail\",\n prm.resource === url\n ? `resource equals ${url}`\n : `resource is ${JSON.stringify(prm.resource)}, not ${JSON.stringify(url)}`,\n );\n\n const advertised = prm.authorization_servers?.[0];\n if (!advertised) {\n add(\"auth-issuer\", \"fail\", \"protected-resource metadata lists no authorization_servers\");\n return;\n }\n try {\n const metadata = await discoverAuthorizationServerMetadata(advertised);\n if (!metadata) {\n add(\"auth-issuer\", \"fail\", `no authorization server metadata found for ${advertised}`);\n } else if (metadata.issuer !== advertised) {\n add(\n \"auth-issuer\",\n \"fail\",\n `issuer is ${JSON.stringify(metadata.issuer)}, advertised as ${JSON.stringify(advertised)}`,\n );\n } else {\n add(\"auth-issuer\", \"pass\", `issuer equals ${advertised}`);\n }\n } catch (err) {\n add(\"auth-issuer\", \"fail\", `authorization server metadata unreadable: ${message(err)}`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// helpers\n// ---------------------------------------------------------------------------\n\nfunction toolUi(tool: Tool): McpUiToolMeta | undefined {\n const ui = tool._meta?.ui;\n return ui && typeof ui === \"object\" ? (ui as McpUiToolMeta) : undefined;\n}\n\nfunction legacyBinding(tool: Tool): boolean {\n return (\n tool._meta?.[RESOURCE_URI_META_KEY] !== undefined && toolUi(tool)?.resourceUri === undefined\n );\n}\n\nfunction resourceUi(item: { _meta?: Record<string, unknown> } | undefined): unknown {\n const ui = item?._meta?.ui;\n return ui && typeof ui === \"object\" ? ui : undefined;\n}\n\nfunction isCsp(value: unknown): value is NonNullable<McpUiResourceMeta[\"csp\"]> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n Object.values(value).every((v) => Array.isArray(v) && v.every((o) => typeof o === \"string\"))\n );\n}\n\nfunction hasSecuritySchemes(tool: Tool): boolean {\n const schemes = tool._meta?.securitySchemes ?? (tool as Record<string, unknown>).securitySchemes;\n return Array.isArray(schemes) && schemes.length > 0;\n}\n\nconst DATA_FONT_MIME = /data:(?:font\\/|application\\/(?:x-)?font|application\\/vnd\\.ms-fontobject)/i;\nconst FONT_FACE_DATA_URL = /@font-face\\s*{[^}]*url\\(\\s*[\"']?data:/i;\n\nfunction hasDataFont(html: string): boolean {\n return DATA_FONT_MIME.test(html) || FONT_FACE_DATA_URL.test(html);\n}\n\nfunction message(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/check/profiles.ts","../src/check/index.ts"],"names":[],"mappings":"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","file":"check-YDZUQT6A.js","sourcesContent":["/**\n * Store profiles for `synapse check`: which checks each host applies, and how\n * hard. A check a profile does not list is not run for that target.\n *\n * `error` fails the run. `warn` is reported and does not: it marks a\n * requirement the host is documented to have but is not known to enforce, so a\n * failure is a risk rather than a certain break.\n */\n\nexport type CheckId =\n | \"extension-declared\"\n | \"ui-resource-mime\"\n | \"tool-resource-uri\"\n | \"tool-ui-meta\"\n | \"resource-csp\"\n | \"resource-data-fonts\"\n | \"auth-challenge\"\n | \"auth-resource-metadata\"\n | \"auth-issuer\"\n | \"tool-security-schemes\";\n\nexport type Severity = \"error\" | \"warn\";\n\nexport interface ProfileRule {\n severity: Severity;\n /** The host requirement behind the rule, printed when the check fails. */\n why: string;\n}\n\nexport interface Profile {\n name: TargetName;\n description: string;\n rules: Partial<Record<CheckId, ProfileRule>>;\n}\n\nexport type TargetName = \"nimblebrain\" | \"claude\" | \"chatgpt\";\n\nconst SPEC_MIME = \"UI resources are served as text/html;profile=mcp-app.\";\nconst RESOLVES = \"A tool's ui.resourceUri must name a ui:// resource the server serves.\";\nconst UI_META =\n \"ui.visibility is a non-empty subset of model/app; csp and permissions belong on the resource, not the tool.\";\nconst EXTENSION = \"A client uses MCP Apps only when both sides declare io.modelcontextprotocol/ui.\";\nconst CHALLENGE = \"Sign-in starts from a 401 whose WWW-Authenticate carries resource_metadata.\";\nconst PRM_RESOURCE =\n \"The protected-resource metadata's resource must equal the connector URL exactly.\";\nconst ISSUER =\n \"The authorization server's metadata issuer must equal the advertised authorization server exactly (RFC 8414).\";\n\nexport const PROFILES: Record<TargetName, Profile> = {\n nimblebrain: {\n name: \"nimblebrain\",\n description: \"The NimbleBrain host.\",\n rules: {\n \"ui-resource-mime\": { severity: \"error\", why: SPEC_MIME },\n \"tool-resource-uri\": { severity: \"error\", why: RESOLVES },\n \"tool-ui-meta\": { severity: \"error\", why: UI_META },\n // The host resolves an app by its ui:// scheme and does not gate on the\n // declaration, so a missing one is a portability risk, not a break.\n \"extension-declared\": { severity: \"warn\", why: EXTENSION },\n // Only reported when the server challenges at all.\n \"auth-challenge\": { severity: \"warn\", why: CHALLENGE },\n \"auth-resource-metadata\": { severity: \"warn\", why: PRM_RESOURCE },\n \"auth-issuer\": { severity: \"warn\", why: ISSUER },\n },\n },\n claude: {\n name: \"claude\",\n description: \"claude.ai and Claude Desktop.\",\n rules: {\n \"ui-resource-mime\": { severity: \"error\", why: SPEC_MIME },\n \"tool-resource-uri\": { severity: \"error\", why: RESOLVES },\n \"tool-ui-meta\": { severity: \"error\", why: UI_META },\n \"extension-declared\": { severity: \"error\", why: EXTENSION },\n \"resource-csp\": {\n severity: \"warn\",\n why: \"Claude builds the frame's security policy from ui.csp; an undeclared origin is blocked.\",\n },\n \"resource-data-fonts\": {\n severity: \"error\",\n why: \"Claude's default security policy blocks data: fonts unless the resource declares them.\",\n },\n \"auth-challenge\": { severity: \"error\", why: CHALLENGE },\n \"auth-resource-metadata\": { severity: \"error\", why: PRM_RESOURCE },\n // Claude has been observed to accept an issuer that differs by a trailing\n // slash, so this is a correctness warning for Claude rather than a break.\n \"auth-issuer\": { severity: \"warn\", why: ISSUER },\n },\n },\n chatgpt: {\n name: \"chatgpt\",\n description: \"ChatGPT apps.\",\n rules: {\n \"ui-resource-mime\": { severity: \"error\", why: SPEC_MIME },\n \"tool-resource-uri\": { severity: \"error\", why: RESOLVES },\n \"tool-ui-meta\": { severity: \"error\", why: UI_META },\n \"extension-declared\": { severity: \"error\", why: EXTENSION },\n \"resource-csp\": {\n severity: \"error\",\n why: \"ChatGPT reads the frame's security policy from ui.csp, and app submission requires it.\",\n },\n \"resource-data-fonts\": {\n severity: \"warn\",\n why: \"A data: font is blocked by a security policy that does not declare it.\",\n },\n \"auth-challenge\": { severity: \"error\", why: CHALLENGE },\n \"auth-resource-metadata\": { severity: \"error\", why: PRM_RESOURCE },\n \"auth-issuer\": { severity: \"error\", why: ISSUER },\n \"tool-security-schemes\": {\n severity: \"error\",\n why: \"ChatGPT prompts for sign-in mid-conversation only for tools that declare securitySchemes.\",\n },\n },\n },\n};\n\nexport function isTargetName(value: string): value is TargetName {\n return Object.hasOwn(PROFILES, value);\n}\n","/**\n * `synapse check` — verify, from outside, what a host sees of a running MCP\n * server: the UI resources and their metadata, the MCP Apps extension\n * declaration, and the OAuth discovery surface a host signs in through.\n *\n * Every check runs once against the server; each target's profile then decides\n * which results count and how hard (see `./profiles.ts`).\n */\n\nimport type {\n McpUiClientCapabilities,\n McpUiResourceMeta,\n McpUiToolMeta,\n} from \"@modelcontextprotocol/ext-apps\";\nimport {\n EXTENSION_ID,\n RESOURCE_MIME_TYPE,\n RESOURCE_URI_META_KEY,\n} from \"@modelcontextprotocol/ext-apps/server\";\nimport {\n discoverAuthorizationServerMetadata,\n discoverOAuthProtectedResourceMetadata,\n extractWWWAuthenticateParams,\n} from \"@modelcontextprotocol/sdk/client/auth.js\";\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\nimport type {\n ReadResourceResult,\n Resource,\n ServerCapabilities,\n Tool,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { ListToolsResultSchema, ResultSchema } from \"@modelcontextprotocol/sdk/types.js\";\nimport { type CheckId, PROFILES, type Severity, type TargetName } from \"./profiles.js\";\n\nexport { type CheckId, isTargetName, PROFILES, type Profile, type TargetName } from \"./profiles.js\";\n\nexport type CheckStatus = \"pass\" | \"fail\" | \"skip\";\n\nexport interface CheckResult {\n id: CheckId;\n status: CheckStatus;\n detail: string;\n}\n\nexport interface TargetReport {\n target: TargetName;\n results: Array<CheckResult & { severity: Severity; why: string }>;\n /** True when any `error`-severity check failed. */\n failed: boolean;\n}\n\nexport interface CheckOptions {\n /** Bearer token for a server that requires auth. Without one, checks that need a session are skipped. */\n token?: string;\n}\n\nconst UI_SCHEME = \"ui://\";\nconst VISIBILITY = new Set([\"model\", \"app\"]);\n\ntype ResourceContents = ReadResourceResult[\"contents\"][number];\n\ninterface Session {\n capabilities: ServerCapabilities | undefined;\n tools: Tool[];\n resources: Resource[];\n reads: Map<string, ResourceContents[] | Error>;\n}\n\n/** Run every check against `url` once. */\nexport async function runChecks(url: string, options: CheckOptions = {}): Promise<CheckResult[]> {\n const results: CheckResult[] = [];\n const add = (id: CheckId, status: CheckStatus, detail: string) =>\n results.push({ id, status, detail });\n\n // Connect without credentials first: the response to that is what a host\n // sees before sign-in, and it decides whether the server requires auth.\n let challenge: Response | undefined;\n let session: Session | undefined;\n let connectError: unknown;\n try {\n session = await openSession(url, undefined, (res) => {\n if (res.status === 401) challenge ??= res;\n });\n } catch (err) {\n connectError = err;\n }\n\n const authRequired = challenge !== undefined;\n if (authRequired && options.token) {\n try {\n session = await openSession(url, options.token);\n connectError = undefined;\n } catch (err) {\n connectError = err;\n }\n }\n\n // A server that cannot be reached is not a server that passes.\n if (!session && (!authRequired || options.token)) {\n throw new Error(\n `could not connect to ${url}${authRequired ? \" with the given token\" : \"\"}: ${message(connectError)}`,\n );\n }\n\n await checkAuth(url, challenge, add);\n\n if (!session) {\n for (const id of SESSION_CHECKS) {\n add(id, \"skip\", \"the server requires auth; pass --token to run it\");\n }\n return results;\n }\n\n checkSession(session, authRequired, add);\n return results;\n}\n\n/** Apply each target's profile to one set of results. */\nexport function applyProfiles(results: CheckResult[], targets: TargetName[]): TargetReport[] {\n return targets.map((target) => {\n const rules = PROFILES[target].rules;\n const scored = results.flatMap((r) => {\n const rule = rules[r.id];\n return rule ? [{ ...r, severity: rule.severity, why: rule.why }] : [];\n });\n return {\n target,\n results: scored,\n failed: scored.some((r) => r.status === \"fail\" && r.severity === \"error\"),\n };\n });\n}\n\n/** One line per check, grouped by target. */\nexport function formatReport(url: string, reports: TargetReport[]): string {\n const width = Math.max(...reports.flatMap((r) => r.results.map((c) => c.id.length)), 0);\n const lines = [`synapse check ${url}`];\n for (const report of reports) {\n lines.push(\"\", `${report.target}: ${report.failed ? \"FAIL\" : \"ok\"}`);\n for (const r of report.results) {\n const label =\n r.status === \"fail\" ? (r.severity === \"error\" ? \"FAIL\" : \"WARN\") : r.status.toUpperCase();\n lines.push(` ${label.padEnd(4)} ${r.id.padEnd(width)} ${r.detail}`);\n if (r.status === \"fail\") lines.push(` ${\"\".padEnd(width)} ${r.why}`);\n }\n }\n return lines.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// session\n// ---------------------------------------------------------------------------\n\nconst SESSION_CHECKS: CheckId[] = [\n \"extension-declared\",\n \"ui-resource-mime\",\n \"tool-resource-uri\",\n \"tool-ui-meta\",\n \"resource-csp\",\n \"resource-data-fonts\",\n \"tool-security-schemes\",\n];\n\nasync function openSession(\n url: string,\n token: string | undefined,\n onResponse?: (res: Response) => void,\n): Promise<Session> {\n // Declare MCP Apps support, so a server that gates its UI on the client's\n // capability shows the UI a host would see.\n const ui: McpUiClientCapabilities = { mimeTypes: [RESOURCE_MIME_TYPE] };\n const client = new Client(\n { name: \"synapse-check\", version: \"1\" },\n { capabilities: { extensions: { [EXTENSION_ID]: ui } } },\n );\n const transport = new StreamableHTTPClientTransport(new URL(url), {\n requestInit: token ? { headers: { Authorization: `Bearer ${token}` } } : undefined,\n fetch: async (input, init) => {\n const res = await fetch(input, init);\n onResponse?.(res);\n return res;\n },\n });\n await client.connect(transport);\n try {\n const capabilities = client.getServerCapabilities();\n const tools: Tool[] = [];\n const resources: Resource[] = [];\n let cursor: string | undefined;\n if (capabilities?.tools) {\n do {\n // Requested raw rather than through `listTools()`: the SDK's tool schema\n // strips fields it does not model, and ChatGPT documents `securitySchemes`\n // at the tool's top level as well as in `_meta`. The one field is copied\n // back onto each parsed tool so `hasSecuritySchemes` can see either place.\n const raw = await client.request(\n { method: \"tools/list\", params: cursor ? { cursor } : {} },\n ResultSchema,\n );\n const page = ListToolsResultSchema.parse(raw);\n const rawTools = (raw as { tools?: Array<Record<string, unknown>> }).tools ?? [];\n page.tools.forEach((tool, i) => {\n const schemes = rawTools[i]?.securitySchemes;\n if (schemes !== undefined) (tool as Record<string, unknown>).securitySchemes = schemes;\n });\n tools.push(...page.tools);\n cursor = page.nextCursor;\n } while (cursor);\n }\n if (capabilities?.resources) {\n do {\n const page = await client.listResources({ cursor });\n resources.push(...page.resources);\n cursor = page.nextCursor;\n } while (cursor);\n }\n\n const uris = new Set(resources.map((r) => r.uri).filter((u) => u.startsWith(UI_SCHEME)));\n for (const tool of tools) {\n const uri = toolUi(tool)?.resourceUri;\n if (typeof uri === \"string\") uris.add(uri);\n }\n const reads = new Map<string, ResourceContents[] | Error>();\n for (const uri of uris) {\n try {\n reads.set(uri, (await client.readResource({ uri })).contents);\n } catch (err) {\n reads.set(uri, err instanceof Error ? err : new Error(String(err)));\n }\n }\n return { capabilities, tools, resources, reads };\n } finally {\n await client.close();\n }\n}\n\nfunction checkSession(\n s: Session,\n authRequired: boolean,\n add: (id: CheckId, status: CheckStatus, detail: string) => void,\n): void {\n const boundTools = s.tools.filter((t) => toolUi(t)?.resourceUri !== undefined);\n const uiResources = s.resources.filter((r) => r.uri.startsWith(UI_SCHEME));\n const hasUi = boundTools.length > 0 || uiResources.length > 0 || s.tools.some(legacyBinding);\n\n if (!hasUi) {\n for (const id of SESSION_CHECKS) {\n if (id !== \"tool-security-schemes\") add(id, \"skip\", \"the server exposes no UI\");\n }\n } else {\n // extension-declared\n const declared = s.capabilities?.extensions?.[EXTENSION_ID] !== undefined;\n add(\n \"extension-declared\",\n declared ? \"pass\" : \"fail\",\n declared\n ? `${EXTENSION_ID} is declared`\n : `the server does not declare ${EXTENSION_ID} in capabilities.extensions`,\n );\n\n // ui-resource-mime: every ui:// resource, as listed and as read.\n const wrongMime: string[] = [];\n for (const r of uiResources) {\n if (r.mimeType !== undefined && r.mimeType !== RESOURCE_MIME_TYPE) {\n wrongMime.push(`${r.uri} is listed as ${r.mimeType}`);\n }\n }\n for (const [uri, contents] of s.reads) {\n if (contents instanceof Error) continue;\n for (const c of contents) {\n if (c.mimeType !== RESOURCE_MIME_TYPE) {\n wrongMime.push(`${uri} is served as ${c.mimeType ?? \"no MIME type\"}`);\n }\n }\n }\n add(\n \"ui-resource-mime\",\n wrongMime.length ? \"fail\" : \"pass\",\n wrongMime.length\n ? `${wrongMime.join(\"; \")}; expected ${RESOURCE_MIME_TYPE}`\n : `every ui:// resource is ${RESOURCE_MIME_TYPE}`,\n );\n\n // tool-resource-uri\n const unresolved: string[] = [];\n for (const tool of s.tools) {\n const uri = toolUi(tool)?.resourceUri;\n if (uri === undefined) {\n if (legacyBinding(tool)) {\n unresolved.push(`${tool.name} binds its UI only through the deprecated flat key`);\n }\n continue;\n }\n if (typeof uri !== \"string\" || !uri.startsWith(UI_SCHEME)) {\n unresolved.push(`${tool.name}: ui.resourceUri ${JSON.stringify(uri)} is not a ui:// URI`);\n continue;\n }\n const read = s.reads.get(uri);\n if (read instanceof Error || !read?.length) {\n unresolved.push(\n `${tool.name}: ${uri} cannot be read${read instanceof Error ? ` (${read.message})` : \"\"}`,\n );\n }\n }\n if (boundTools.length === 0 && unresolved.length === 0) {\n unresolved.push(\"no tool binds a UI through ui.resourceUri\");\n }\n add(\n \"tool-resource-uri\",\n unresolved.length ? \"fail\" : \"pass\",\n unresolved.length\n ? unresolved.join(\"; \")\n : `${boundTools.length} tool(s) bind a UI that resolves`,\n );\n\n // tool-ui-meta\n const badMeta: string[] = [];\n for (const tool of s.tools) {\n const ui = toolUi(tool);\n if (!ui) continue;\n const vis = ui.visibility as unknown;\n if (\n vis !== undefined &&\n (!Array.isArray(vis) || vis.length === 0 || !vis.every((v) => VISIBILITY.has(v)))\n ) {\n badMeta.push(`${tool.name}: ui.visibility ${JSON.stringify(vis)}`);\n }\n for (const key of [\"csp\", \"permissions\"] as const) {\n if ((ui as Record<string, unknown>)[key] !== undefined) {\n badMeta.push(`${tool.name}: ui.${key} is on the tool, where hosts ignore it`);\n }\n }\n }\n add(\n \"tool-ui-meta\",\n badMeta.length ? \"fail\" : \"pass\",\n badMeta.length ? badMeta.join(\"; \") : \"tool ui metadata is well-formed\",\n );\n\n // resource-csp and resource-data-fonts, per UI resource read.\n const noCsp: string[] = [];\n const dataFonts: string[] = [];\n for (const [uri, contents] of s.reads) {\n if (contents instanceof Error) continue;\n for (const c of contents) {\n const listed = s.resources.find((r) => r.uri === uri);\n // Hosts read the content item's metadata, and fall back to the list entry.\n const ui = (resourceUi(c) ?? resourceUi(listed)) as McpUiResourceMeta | undefined;\n const csp = ui?.csp as unknown;\n if (csp === undefined) {\n noCsp.push(`${uri} declares no ui.csp`);\n } else if (!isCsp(csp)) {\n noCsp.push(`${uri}: ui.csp is not a map of origin lists`);\n }\n const html = \"text\" in c ? c.text : Buffer.from(c.blob, \"base64\").toString(\"utf-8\");\n const declaresData = isCsp(csp) && (csp.resourceDomains ?? []).includes(\"data:\");\n if (hasDataFont(html) && !declaresData) {\n dataFonts.push(`${uri} loads a data: font, and ui.csp.resourceDomains omits data:`);\n }\n }\n }\n add(\n \"resource-csp\",\n noCsp.length ? \"fail\" : \"pass\",\n noCsp.length ? noCsp.join(\"; \") : \"every UI resource declares ui.csp\",\n );\n add(\n \"resource-data-fonts\",\n dataFonts.length ? \"fail\" : \"pass\",\n dataFonts.length ? dataFonts.join(\"; \") : \"no undeclared data: fonts\",\n );\n }\n\n // tool-security-schemes: a question about auth, not about UI.\n if (!authRequired) {\n add(\"tool-security-schemes\", \"skip\", \"the server does not require auth\");\n } else {\n const missing = s.tools.filter((t) => !hasSecuritySchemes(t)).map((t) => t.name);\n add(\n \"tool-security-schemes\",\n missing.length ? \"fail\" : \"pass\",\n missing.length\n ? `no securitySchemes on: ${missing.join(\", \")}`\n : \"every tool declares securitySchemes\",\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// auth\n// ---------------------------------------------------------------------------\n\nasync function checkAuth(\n url: string,\n challenge: Response | undefined,\n add: (id: CheckId, status: CheckStatus, detail: string) => void,\n): Promise<void> {\n if (!challenge) {\n const reason = \"the server answered without a 401 challenge\";\n add(\"auth-challenge\", \"skip\", reason);\n add(\"auth-resource-metadata\", \"skip\", reason);\n add(\"auth-issuer\", \"skip\", reason);\n return;\n }\n\n const { resourceMetadataUrl } = extractWWWAuthenticateParams(challenge);\n add(\n \"auth-challenge\",\n resourceMetadataUrl ? \"pass\" : \"fail\",\n resourceMetadataUrl\n ? `401 names resource_metadata ${resourceMetadataUrl.href}`\n : `401 WWW-Authenticate carries no resource_metadata (${JSON.stringify(challenge.headers.get(\"www-authenticate\"))})`,\n );\n\n let prm: Awaited<ReturnType<typeof discoverOAuthProtectedResourceMetadata>>;\n try {\n // Without resource_metadata, a client falls back to the well-known path,\n // so the rest of the surface is still worth reporting.\n prm = await discoverOAuthProtectedResourceMetadata(url, { resourceMetadataUrl });\n } catch (err) {\n add(\n \"auth-resource-metadata\",\n \"fail\",\n `protected-resource metadata unreadable: ${message(err)}`,\n );\n add(\"auth-issuer\", \"skip\", \"no protected-resource metadata\");\n return;\n }\n add(\n \"auth-resource-metadata\",\n prm.resource === url ? \"pass\" : \"fail\",\n prm.resource === url\n ? `resource equals ${url}`\n : `resource is ${JSON.stringify(prm.resource)}, not ${JSON.stringify(url)}`,\n );\n\n const advertised = prm.authorization_servers?.[0];\n if (!advertised) {\n add(\"auth-issuer\", \"fail\", \"protected-resource metadata lists no authorization_servers\");\n return;\n }\n try {\n const metadata = await discoverAuthorizationServerMetadata(advertised);\n if (!metadata) {\n add(\"auth-issuer\", \"fail\", `no authorization server metadata found for ${advertised}`);\n } else if (metadata.issuer !== advertised) {\n add(\n \"auth-issuer\",\n \"fail\",\n `issuer is ${JSON.stringify(metadata.issuer)}, advertised as ${JSON.stringify(advertised)}`,\n );\n } else {\n add(\"auth-issuer\", \"pass\", `issuer equals ${advertised}`);\n }\n } catch (err) {\n add(\"auth-issuer\", \"fail\", `authorization server metadata unreadable: ${message(err)}`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// helpers\n// ---------------------------------------------------------------------------\n\nfunction toolUi(tool: Tool): McpUiToolMeta | undefined {\n const ui = tool._meta?.ui;\n return ui && typeof ui === \"object\" ? (ui as McpUiToolMeta) : undefined;\n}\n\nfunction legacyBinding(tool: Tool): boolean {\n return (\n tool._meta?.[RESOURCE_URI_META_KEY] !== undefined && toolUi(tool)?.resourceUri === undefined\n );\n}\n\nfunction resourceUi(item: { _meta?: Record<string, unknown> } | undefined): unknown {\n const ui = item?._meta?.ui;\n return ui && typeof ui === \"object\" ? ui : undefined;\n}\n\nfunction isCsp(value: unknown): value is NonNullable<McpUiResourceMeta[\"csp\"]> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n Object.values(value).every((v) => Array.isArray(v) && v.every((o) => typeof o === \"string\"))\n );\n}\n\nfunction hasSecuritySchemes(tool: Tool): boolean {\n const schemes = tool._meta?.securitySchemes ?? (tool as Record<string, unknown>).securitySchemes;\n return Array.isArray(schemes) && schemes.length > 0;\n}\n\nconst DATA_FONT_MIME = /data:(?:font\\/|application\\/(?:x-)?font|application\\/vnd\\.ms-fontobject)/i;\nconst FONT_FACE_DATA_URL = /@font-face\\s*{[^}]*url\\(\\s*[\"']?data:/i;\n\nfunction hasDataFont(html: string): boolean {\n return DATA_FONT_MIME.test(html) || FONT_FACE_DATA_URL.test(html);\n}\n\nfunction message(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n"]}
|