@caplets/core 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth.d.ts +2 -1
- package/dist/caplet-files-bundle.d.ts +62 -0
- package/dist/caplet-sets.d.ts +2 -0
- package/dist/caplet-source.js +96 -4
- package/dist/cli/add.d.ts +10 -0
- package/dist/cli/auth.d.ts +22 -0
- package/dist/cli/commands.d.ts +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/code-mode/diagnostics-builtins.generated.d.ts +1 -0
- package/dist/code-mode/platform-entry.d.ts +1 -0
- package/dist/code-mode/platform-host.d.ts +6 -0
- package/dist/code-mode/platform-runtime.generated.d.ts +1 -0
- package/dist/code-mode.js +18 -13
- package/dist/{completion-DnQujlrc.js → completion-BC4BNWo0.js} +14 -2
- package/dist/config/paths.d.ts +2 -0
- package/dist/config-runtime.d.ts +13 -1
- package/dist/config-runtime.js +44 -0
- package/dist/config.d.ts +25 -1
- package/dist/engine.d.ts +3 -0
- package/dist/google-discovery/index.d.ts +5 -0
- package/dist/google-discovery/manager.d.ts +37 -0
- package/dist/google-discovery/operations.d.ts +32 -0
- package/dist/google-discovery/request.d.ts +5 -0
- package/dist/google-discovery/schema.d.ts +2 -0
- package/dist/google-discovery/types.d.ts +70 -0
- package/dist/http/response.d.ts +14 -0
- package/dist/http-actions.d.ts +3 -0
- package/dist/index.js +100 -18
- package/dist/media/artifacts.d.ts +24 -0
- package/dist/media/index.d.ts +2 -0
- package/dist/media/input.d.ts +29 -0
- package/dist/native/service.d.ts +1 -0
- package/dist/native.js +1 -1
- package/dist/{observed-output-shapes-CL5MFXwM.js → observed-output-shapes-D2k2-q8K.js} +9 -0
- package/dist/observed-output-shapes.js +1 -1
- package/dist/openapi.d.ts +2 -0
- package/dist/registry.d.ts +6 -0
- package/dist/runtime-plan.js +1 -1
- package/dist/runtime.d.ts +2 -0
- package/dist/{service-4_chB2wu.js → service-CSRCJfpA.js} +1711 -187
- package/dist/tools.d.ts +2 -1
- package/package.json +9 -2
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { A as safeParseAsync$1, C as toJSONSchema, D as parse$3, E as $ZodType, F as NEVER, M as defineLazy, N as normalizeParams, O as parseAsync, P as $constructor, S as datetime, T as $ZodObject, _ as record, a as any, b as unknown, c as custom, d as literal, f as looseObject, g as preprocess, h as optional, i as _null, j as clone, k as safeParse$1, l as discriminatedUnion, m as object$1, o as array, p as number$1, r as _enum, s as boolean, t as ZodNumber$1, u as intersection, v as string, w as _coercedNumber, x as url, y as union } from "./schemas-C0PNPwjS.js";
|
|
2
2
|
import { a as isAllowedHttpBaseUrl, c as validateHttpActionHeaders, d as errorResult, f as redactSecrets, i as SERVER_ID_PATTERN, n as HEADER_NAME_PATTERN, o as isAllowedRemoteUrl, p as toSafeError, r as HTTP_BASE_URL_PATTERN, s as isUrl, t as FORBIDDEN_HEADERS, u as CapletsError } from "./validation-DgxCzt-A.js";
|
|
3
3
|
import { generatedToolInputJsonSchema, generatedToolInputJsonSchemaForCaplet, generatedToolInputSchemaForCaplet, mcpOperations, operations } from "./generated-tool-input-schema.js";
|
|
4
|
-
import { f as observedOutputShapeKey, i as observeOutputShape, r as normalizedObservableValue, t as usefulOutputSchema, u as FileObservedOutputShapeStore } from "./observed-output-shapes-
|
|
4
|
+
import { f as observedOutputShapeKey, i as observeOutputShape, r as normalizedObservableValue, t as usefulOutputSchema, u as FileObservedOutputShapeStore } from "./observed-output-shapes-D2k2-q8K.js";
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
|
-
import { accessSync, chmodSync, constants, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, watch, writeFileSync } from "node:fs";
|
|
6
|
+
import { accessSync, chmodSync, constants, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, watch, writeFileSync } from "node:fs";
|
|
7
7
|
import { basename, delimiter, dirname, extname, isAbsolute, join, parse, posix, relative, resolve, sep, win32 } from "node:path";
|
|
8
8
|
import { spawn } from "node:child_process";
|
|
9
9
|
import process$1 from "node:process";
|
|
@@ -11,6 +11,7 @@ import { PassThrough } from "node:stream";
|
|
|
11
11
|
import { createServer } from "node:http";
|
|
12
12
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
13
13
|
import { homedir } from "node:os";
|
|
14
|
+
import { readFile } from "node:fs/promises";
|
|
14
15
|
import ts from "typescript";
|
|
15
16
|
import { getQuickJS, shouldInterruptAfterDeadline } from "quickjs-emscripten";
|
|
16
17
|
import { Buffer as Buffer$1 } from "node:buffer";
|
|
@@ -17241,6 +17242,9 @@ function defaultConfigPath(env = process.env, home = homedir(), platform = proce
|
|
|
17241
17242
|
function defaultAuthDir(env = process.env, home = homedir(), platform = process.platform) {
|
|
17242
17243
|
return (platform === "win32" ? win32.join : posix.join)(defaultStateBaseDir(env, home, platform), "caplets", "auth");
|
|
17243
17244
|
}
|
|
17245
|
+
function defaultArtifactDir(env = process.env, home = homedir(), platform = process.platform) {
|
|
17246
|
+
return (platform === "win32" ? win32.join : posix.join)(defaultStateBaseDir(env, home, platform), "caplets", "artifacts");
|
|
17247
|
+
}
|
|
17244
17248
|
function defaultCompletionCacheDir(env = process.env, home = homedir(), platform = process.platform) {
|
|
17245
17249
|
const pathJoin = platform === "win32" ? win32.join : posix.join;
|
|
17246
17250
|
return platform === "win32" ? pathJoin(defaultCacheBaseDir(env, home, platform), "caplets", "cache", "completions") : pathJoin(defaultCacheBaseDir(env, home, platform), "caplets", "completions");
|
|
@@ -17251,6 +17255,7 @@ function defaultObservedOutputShapeCacheDir(env = process.env, home = homedir(),
|
|
|
17251
17255
|
}
|
|
17252
17256
|
const DEFAULT_CONFIG_PATH = defaultConfigPath();
|
|
17253
17257
|
const DEFAULT_AUTH_DIR = defaultAuthDir();
|
|
17258
|
+
const DEFAULT_ARTIFACT_DIR = defaultArtifactDir();
|
|
17254
17259
|
const DEFAULT_COMPLETION_CACHE_DIR = defaultCompletionCacheDir();
|
|
17255
17260
|
const DEFAULT_OBSERVED_OUTPUT_SHAPE_CACHE_DIR = defaultObservedOutputShapeCacheDir();
|
|
17256
17261
|
const PROJECT_CONFIG_FILE = join(".caplets", "config.json");
|
|
@@ -17568,7 +17573,7 @@ async function startGenericOAuthFlow(target, options) {
|
|
|
17568
17573
|
assertAllowedAuthUrl(authorizationEndpoint, "authorization endpoint", allowLoopbackHttp);
|
|
17569
17574
|
assertAllowedAuthUrl(tokenEndpoint, "token endpoint", allowLoopbackHttp);
|
|
17570
17575
|
const client = await resolveGenericClient(target, authConfig, metadata, redirectUri, allowLoopbackHttp);
|
|
17571
|
-
const scope = scopesFor(authConfig);
|
|
17576
|
+
const scope = scopesFor(authConfig, target.resolvedScopes);
|
|
17572
17577
|
const authorizationUrl = new URL(authorizationEndpoint);
|
|
17573
17578
|
authorizationUrl.searchParams.set("response_type", "code");
|
|
17574
17579
|
authorizationUrl.searchParams.set("client_id", client.clientId);
|
|
@@ -17617,6 +17622,7 @@ async function startGenericOAuthFlow(target, options) {
|
|
|
17617
17622
|
metadata: redactSecrets({
|
|
17618
17623
|
protectedResource: target.url ?? target.baseUrl ?? target.specUrl,
|
|
17619
17624
|
authorizationServer: metadata,
|
|
17625
|
+
requestedScopes: scope?.split(/\s+/u).filter(Boolean),
|
|
17620
17626
|
dynamicClient: client.dynamic ? { client_id: client.clientId } : void 0
|
|
17621
17627
|
})
|
|
17622
17628
|
}), options.authDir);
|
|
@@ -17645,7 +17651,7 @@ async function runGenericOAuthFlow(target, options = {}) {
|
|
|
17645
17651
|
assertAllowedAuthUrl(authorizationEndpoint, "authorization endpoint", allowLoopbackHttp);
|
|
17646
17652
|
assertAllowedAuthUrl(tokenEndpoint, "token endpoint", allowLoopbackHttp);
|
|
17647
17653
|
const client = await resolveGenericClient(target, authConfig, metadata, redirectUri, allowLoopbackHttp);
|
|
17648
|
-
const scope = scopesFor(authConfig);
|
|
17654
|
+
const scope = scopesFor(authConfig, target.resolvedScopes);
|
|
17649
17655
|
const authorizationUrl = new URL(authorizationEndpoint);
|
|
17650
17656
|
authorizationUrl.searchParams.set("response_type", "code");
|
|
17651
17657
|
authorizationUrl.searchParams.set("client_id", client.clientId);
|
|
@@ -17695,6 +17701,7 @@ async function runGenericOAuthFlow(target, options = {}) {
|
|
|
17695
17701
|
metadata: redactSecrets({
|
|
17696
17702
|
protectedResource: target.url ?? target.baseUrl ?? target.specUrl,
|
|
17697
17703
|
authorizationServer: metadata,
|
|
17704
|
+
requestedScopes: scope?.split(/\s+/u).filter(Boolean),
|
|
17698
17705
|
dynamicClient: client.dynamic ? { client_id: client.clientId } : void 0
|
|
17699
17706
|
})
|
|
17700
17707
|
});
|
|
@@ -17903,7 +17910,7 @@ async function refreshGenericOAuthBundle(target, authConfig, bundle, authDir) {
|
|
|
17903
17910
|
refreshToken: asString(tokenResponse.refresh_token) ?? bundle.refreshToken,
|
|
17904
17911
|
tokenType: asString(tokenResponse.token_type) ?? bundle.tokenType,
|
|
17905
17912
|
expiresAt: refreshedExpiresAt(tokenResponse.expires_in, bundle.expiresAt),
|
|
17906
|
-
scope: asString(tokenResponse.scope) ?? bundle.scope ?? scopesFor(authConfig),
|
|
17913
|
+
scope: asString(tokenResponse.scope) ?? bundle.scope ?? scopesFor(authConfig, target.resolvedScopes),
|
|
17907
17914
|
idToken: idToken ?? bundle.idToken,
|
|
17908
17915
|
issuer: asString(idClaims?.iss) ?? bundle.issuer ?? metadata.issuer ?? authConfig.issuer,
|
|
17909
17916
|
subject: asString(idClaims?.sub) ?? bundle.subject,
|
|
@@ -17969,7 +17976,7 @@ function assertAllowedAuthUrl(value, label, allowLoopbackHttp = false) {
|
|
|
17969
17976
|
function assertTokenBundleMatchesTarget(bundle, target, authConfig) {
|
|
17970
17977
|
const configuredClientId = authConfig.clientId ?? authConfig.clientMetadataUrl;
|
|
17971
17978
|
const expectedOrigin = protectedResourceOrigin(target, authConfig);
|
|
17972
|
-
if (bundle.authType !== authConfig.type || expectedOrigin && bundle.protectedResourceOrigin !== expectedOrigin || configuredClientId && bundle.clientId !== configuredClientId || authConfig.issuer && bundle.issuer !== authConfig.issuer) throw new CapletsError("AUTH_REQUIRED", `OAuth credentials for ${target.server} do not match the configured backend`, {
|
|
17979
|
+
if (bundle.authType !== authConfig.type || expectedOrigin && bundle.protectedResourceOrigin !== expectedOrigin || configuredClientId && bundle.clientId !== configuredClientId || authConfig.issuer && bundle.issuer !== authConfig.issuer || tokenBundleMissingScopes(bundle, authConfig, target.resolvedScopes)) throw new CapletsError("AUTH_REQUIRED", `OAuth credentials for ${target.server} do not match the configured backend`, {
|
|
17973
17980
|
server: target.server,
|
|
17974
17981
|
backend: target.backend,
|
|
17975
17982
|
authType: authConfig.type,
|
|
@@ -17997,8 +18004,39 @@ function isLoopbackHttpUrl(value) {
|
|
|
17997
18004
|
"::1"
|
|
17998
18005
|
].includes(url.hostname);
|
|
17999
18006
|
}
|
|
18000
|
-
function
|
|
18007
|
+
function tokenBundleMissingScopes(bundle, authConfig, resolvedScopes) {
|
|
18008
|
+
const required = requiredStoredScopes(authConfig, resolvedScopes);
|
|
18009
|
+
if (required.length === 0) return false;
|
|
18010
|
+
const metadataScopes = requestedScopesFromMetadata(bundle.metadata);
|
|
18011
|
+
const actual = new Set(bundle.scope?.split(/\s+/u).filter(Boolean) ?? metadataScopes ?? []);
|
|
18012
|
+
return required.some((scope) => ![...actual].some((grantedScope) => oauthScopeSatisfies(grantedScope, scope)));
|
|
18013
|
+
}
|
|
18014
|
+
function oauthScopeSatisfies(grantedScope, requiredScope) {
|
|
18015
|
+
if (grantedScope === requiredScope) return true;
|
|
18016
|
+
const googleScopePrefix = "https://www.googleapis.com/auth/";
|
|
18017
|
+
if (!grantedScope.startsWith(googleScopePrefix) || !requiredScope.startsWith(googleScopePrefix)) return false;
|
|
18018
|
+
return requiredScope.startsWith(`${grantedScope}.`);
|
|
18019
|
+
}
|
|
18020
|
+
function requiredStoredScopes(authConfig, resolvedScopes) {
|
|
18021
|
+
if (authConfig.scopes?.length) return authConfig.scopes;
|
|
18022
|
+
return resolvedScopes?.length ? [...new Set(resolvedScopes)].sort() : [];
|
|
18023
|
+
}
|
|
18024
|
+
function requestedScopesFromMetadata(metadata) {
|
|
18025
|
+
if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) return void 0;
|
|
18026
|
+
const value = metadata.requestedScopes;
|
|
18027
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string") ? value : void 0;
|
|
18028
|
+
}
|
|
18029
|
+
function scopesFor(authConfig, resolvedScopes) {
|
|
18001
18030
|
if (authConfig.scopes?.length) return authConfig.scopes.join(" ");
|
|
18031
|
+
if (resolvedScopes?.length) {
|
|
18032
|
+
const apiScopes = [...new Set(resolvedScopes)].sort();
|
|
18033
|
+
return authConfig.type === "oidc" ? [
|
|
18034
|
+
"openid",
|
|
18035
|
+
"profile",
|
|
18036
|
+
"email",
|
|
18037
|
+
...apiScopes
|
|
18038
|
+
].join(" ") : apiScopes.join(" ");
|
|
18039
|
+
}
|
|
18002
18040
|
return authConfig.type === "oidc" ? "openid profile email" : void 0;
|
|
18003
18041
|
}
|
|
18004
18042
|
function validateOidcToken(authConfig, metadata, idToken, claims, clientId) {
|
|
@@ -18589,8 +18627,8 @@ function compactToolSafetyHints(tool) {
|
|
|
18589
18627
|
};
|
|
18590
18628
|
}
|
|
18591
18629
|
function compactToolSchemaHints(tool) {
|
|
18592
|
-
const schema = isRecord$
|
|
18593
|
-
const properties = isRecord$
|
|
18630
|
+
const schema = isRecord$5(tool.inputSchema) ? tool.inputSchema : void 0;
|
|
18631
|
+
const properties = isRecord$5(schema?.properties) ? schema.properties : {};
|
|
18594
18632
|
const acceptedArgs = Object.keys(properties).sort();
|
|
18595
18633
|
const requiredArgs = Array.isArray(schema?.required) ? schema.required.filter((value) => typeof value === "string").sort() : [];
|
|
18596
18634
|
const argsTemplate = compactArgsTemplate(properties, requiredArgs, acceptedArgs);
|
|
@@ -18614,7 +18652,7 @@ function compactArgsTemplate(properties, requiredArgs, acceptedArgs) {
|
|
|
18614
18652
|
if (templateArgs.length === 0 || templateArgs.length > 4) return void 0;
|
|
18615
18653
|
if (requiredArgs.length === 0 && acceptedArgs.length > 3) return void 0;
|
|
18616
18654
|
const entries = templateArgs.flatMap((name) => {
|
|
18617
|
-
const value = placeholderForSchema(isRecord$
|
|
18655
|
+
const value = placeholderForSchema(isRecord$5(properties[name]) ? properties[name] : void 0);
|
|
18618
18656
|
return value === void 0 ? [] : [[name, value]];
|
|
18619
18657
|
});
|
|
18620
18658
|
return entries.length === templateArgs.length ? Object.fromEntries(entries) : void 0;
|
|
@@ -18634,13 +18672,13 @@ function placeholderForSchema(schema) {
|
|
|
18634
18672
|
}
|
|
18635
18673
|
}
|
|
18636
18674
|
function compactToolSelectionHints(tool) {
|
|
18637
|
-
if (!isRecord$
|
|
18675
|
+
if (!isRecord$5(tool)) return {};
|
|
18638
18676
|
return {
|
|
18639
18677
|
...typeof tool.useWhen === "string" && tool.useWhen.trim() ? { useWhen: tool.useWhen.trim() } : {},
|
|
18640
18678
|
...typeof tool.avoidWhen === "string" && tool.avoidWhen.trim() ? { avoidWhen: tool.avoidWhen.trim() } : {}
|
|
18641
18679
|
};
|
|
18642
18680
|
}
|
|
18643
|
-
function isRecord$
|
|
18681
|
+
function isRecord$5(value) {
|
|
18644
18682
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
18645
18683
|
}
|
|
18646
18684
|
function sameServerConfig(left, right) {
|
|
@@ -18715,7 +18753,7 @@ function markdownCallToolResultContent(result, context = {}) {
|
|
|
18715
18753
|
return textContent(renderStructuredMarkdown(result, context));
|
|
18716
18754
|
}
|
|
18717
18755
|
function hasRenderableStructuredContent(value) {
|
|
18718
|
-
if (!isRecord$
|
|
18756
|
+
if (!isRecord$4(value)) return false;
|
|
18719
18757
|
return Object.keys(value).some((key) => key !== "caplets" && key !== "elapsedMs");
|
|
18720
18758
|
}
|
|
18721
18759
|
function renderStructuredMarkdown(value, context) {
|
|
@@ -18739,7 +18777,7 @@ function markdownTitle(context) {
|
|
|
18739
18777
|
return parts.length > 0 ? `# ${parts.join(" ")}` : "# Result";
|
|
18740
18778
|
}
|
|
18741
18779
|
function renderHttpMarkdown(value, title) {
|
|
18742
|
-
const record = asRecord$
|
|
18780
|
+
const record = asRecord$3(value) ?? {};
|
|
18743
18781
|
const lines = [
|
|
18744
18782
|
title,
|
|
18745
18783
|
"",
|
|
@@ -18762,8 +18800,8 @@ function renderHttpMarkdown(value, title) {
|
|
|
18762
18800
|
return lines.join("\n");
|
|
18763
18801
|
}
|
|
18764
18802
|
function renderGraphQlMarkdown(value, title) {
|
|
18765
|
-
const record = asRecord$
|
|
18766
|
-
const body = asRecord$
|
|
18803
|
+
const record = asRecord$3(value) ?? {};
|
|
18804
|
+
const body = asRecord$3(record.body);
|
|
18767
18805
|
if (!body || !("data" in body) && !("errors" in body)) return renderHttpMarkdown(value, title);
|
|
18768
18806
|
const lines = [
|
|
18769
18807
|
title,
|
|
@@ -18789,7 +18827,7 @@ function renderGraphQlMarkdown(value, title) {
|
|
|
18789
18827
|
return lines.join("\n");
|
|
18790
18828
|
}
|
|
18791
18829
|
function renderCliMarkdown(value, title) {
|
|
18792
|
-
const record = asRecord$
|
|
18830
|
+
const record = asRecord$3(value) ?? {};
|
|
18793
18831
|
const lines = [
|
|
18794
18832
|
title,
|
|
18795
18833
|
"",
|
|
@@ -18816,14 +18854,14 @@ function renderCliMarkdown(value, title) {
|
|
|
18816
18854
|
return lines.join("\n");
|
|
18817
18855
|
}
|
|
18818
18856
|
function renderDiscoveryWrapper(value, context, title) {
|
|
18819
|
-
const result = asRecord$
|
|
18857
|
+
const result = asRecord$3(value.result);
|
|
18820
18858
|
const lines = [title, ""];
|
|
18821
18859
|
let renderedKnownWrapper = true;
|
|
18822
18860
|
if (context.operation === "tools" || context.operation === "search_tools") lines.push("## Tools", "", renderNamedList(arrayValue$1(result?.items ?? result?.tools), "tool"), "");
|
|
18823
18861
|
else if (context.operation === "resources" || context.operation === "search_resources") lines.push("## Resources", "", renderNamedList(arrayValue$1(result?.items ?? result?.resources ?? result?.matches), "uri"), "");
|
|
18824
18862
|
else if (context.operation === "resource_templates") lines.push("## Resource Templates", "", renderNamedList(arrayValue$1(result?.items ?? result?.resourceTemplates), "uriTemplate"), "");
|
|
18825
18863
|
else if (context.operation === "prompts" || context.operation === "search_prompts") lines.push("## Prompts", "", renderNamedList(arrayValue$1(result?.items ?? result?.prompts), "prompt"), "");
|
|
18826
|
-
else if (context.operation === "describe_tool") lines.push("## Tool", "", renderToolSummary(asRecord$
|
|
18864
|
+
else if (context.operation === "describe_tool") lines.push("## Tool", "", renderToolSummary(asRecord$3(result?.tool)), "");
|
|
18827
18865
|
else if (context.operation === "check") lines.push("## Backend Status", "", renderBackendStatus(result), "");
|
|
18828
18866
|
else if (context.operation === "inspect") lines.push("## Caplet", "", renderCapletSummary(result), "");
|
|
18829
18867
|
else renderedKnownWrapper = false;
|
|
@@ -18833,7 +18871,7 @@ function renderDiscoveryWrapper(value, context, title) {
|
|
|
18833
18871
|
return lines.join("\n");
|
|
18834
18872
|
}
|
|
18835
18873
|
function renderErrorMarkdown(value, title) {
|
|
18836
|
-
const error = asRecord$
|
|
18874
|
+
const error = asRecord$3(asRecord$3(value)?.error) ?? asRecord$3(value);
|
|
18837
18875
|
const code = typeof error?.code === "string" ? error.code : "Error";
|
|
18838
18876
|
const message = typeof error?.message === "string" ? error.message : "Tool call failed.";
|
|
18839
18877
|
return [
|
|
@@ -18849,21 +18887,21 @@ function renderErrorMarkdown(value, title) {
|
|
|
18849
18887
|
].join("\n");
|
|
18850
18888
|
}
|
|
18851
18889
|
function isDiscoveryWrapper(value) {
|
|
18852
|
-
return isRecord$
|
|
18890
|
+
return isRecord$4(value) && "result" in value;
|
|
18853
18891
|
}
|
|
18854
18892
|
function isErrorStructuredContent(value) {
|
|
18855
|
-
return isRecord$
|
|
18893
|
+
return isRecord$4(value) && "error" in value;
|
|
18856
18894
|
}
|
|
18857
18895
|
function isHttpLikeResult(value) {
|
|
18858
|
-
return isRecord$
|
|
18896
|
+
return isRecord$4(value) && ("status" in value || "statusText" in value || "body" in value);
|
|
18859
18897
|
}
|
|
18860
18898
|
function isGraphQlHttpResult(value) {
|
|
18861
18899
|
if (!isHttpLikeResult(value)) return false;
|
|
18862
|
-
const body = asRecord$
|
|
18900
|
+
const body = asRecord$3(value.body);
|
|
18863
18901
|
return Boolean(body && ("data" in body || "errors" in body));
|
|
18864
18902
|
}
|
|
18865
18903
|
function isCliResult(value) {
|
|
18866
|
-
return isRecord$
|
|
18904
|
+
return isRecord$4(value) && ("exitCode" in value || "stdout" in value || "stderr" in value);
|
|
18867
18905
|
}
|
|
18868
18906
|
function renderBodyValue(value) {
|
|
18869
18907
|
if (value === void 0) return "_No response body._";
|
|
@@ -18894,7 +18932,7 @@ function textBlocksToString(content) {
|
|
|
18894
18932
|
}
|
|
18895
18933
|
function renderNamedList(items, nameKey) {
|
|
18896
18934
|
if (items.length === 0) return "_No items._";
|
|
18897
|
-
const records = items.map((item) => asRecord$
|
|
18935
|
+
const records = items.map((item) => asRecord$3(item));
|
|
18898
18936
|
const commonDescriptionSuffix = repeatedDescriptionSuffix(records.map((record) => stringValue$1(record?.description)).filter((description) => Boolean(description)));
|
|
18899
18937
|
const renderedItems = records.map((record, index) => {
|
|
18900
18938
|
const name = stringValue$1(record?.[nameKey]) ?? stringValue$1(record?.name) ?? `Item ${index + 1}`;
|
|
@@ -18931,8 +18969,8 @@ function compactListHints(record) {
|
|
|
18931
18969
|
const acceptedArgs = stringArrayValue(record.acceptedArgs);
|
|
18932
18970
|
if (requiredArgs.length > 0) hints.push(`required args: ${requiredArgs.join(", ")}`);
|
|
18933
18971
|
else if (acceptedArgs.length > 0) hints.push(`args: ${acceptedArgs.join(", ")}`);
|
|
18934
|
-
if (isRecord$
|
|
18935
|
-
if (isRecord$
|
|
18972
|
+
if (isRecord$4(record.argsTemplate)) hints.push(`args template: ${compactJsonText(record.argsTemplate, 160)}`);
|
|
18973
|
+
if (isRecord$4(record.callTemplate)) hints.push(`call: ${compactJsonText(record.callTemplate, 220)}`);
|
|
18936
18974
|
if (record.supportsFields === true) hints.push("supports fields");
|
|
18937
18975
|
if (record.readOnlyHint === true) hints.push("read-only");
|
|
18938
18976
|
if (record.destructiveHint === true) hints.push("destructive");
|
|
@@ -18981,7 +19019,7 @@ function renderCapletSummary(result) {
|
|
|
18981
19019
|
"name",
|
|
18982
19020
|
"description"
|
|
18983
19021
|
]) if (result[key] !== void 0) lines.push(`- **${humanizeKey(key)}:** ${String(result[key])}`);
|
|
18984
|
-
const backend = asRecord$
|
|
19022
|
+
const backend = asRecord$3(result.backend);
|
|
18985
19023
|
if (backend?.type !== void 0) lines.push(`- **Backend:** \`${String(backend.type)}\``);
|
|
18986
19024
|
return lines.length > 0 ? lines.join("\n") : jsonFence(result);
|
|
18987
19025
|
}
|
|
@@ -19001,10 +19039,10 @@ function stringArrayValue(value) {
|
|
|
19001
19039
|
function humanizeKey(key) {
|
|
19002
19040
|
return key.replace(/([A-Z])/gu, " $1").replace(/^./u, (char) => char.toUpperCase());
|
|
19003
19041
|
}
|
|
19004
|
-
function asRecord$
|
|
19005
|
-
return isRecord$
|
|
19042
|
+
function asRecord$3(value) {
|
|
19043
|
+
return isRecord$4(value) ? value : void 0;
|
|
19006
19044
|
}
|
|
19007
|
-
function isRecord$
|
|
19045
|
+
function isRecord$4(value) {
|
|
19008
19046
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
19009
19047
|
}
|
|
19010
19048
|
//#endregion
|
|
@@ -26100,6 +26138,36 @@ const capletOpenApiEndpointSchema = object$1({
|
|
|
26100
26138
|
});
|
|
26101
26139
|
validateEndpointAuthHeaders$1(endpoint.auth, ctx);
|
|
26102
26140
|
});
|
|
26141
|
+
const capletGoogleDiscoveryOperationFilterSchema = array(string().trim().min(1).max(160));
|
|
26142
|
+
const capletGoogleDiscoveryApiSchema = object$1({
|
|
26143
|
+
discoveryPath: string().min(1).optional().describe("Local Google Discovery document path."),
|
|
26144
|
+
discoveryUrl: string().min(1).optional().describe("Remote Google Discovery document URL."),
|
|
26145
|
+
baseUrl: string().min(1).optional().describe("Override base URL for Google API requests."),
|
|
26146
|
+
auth: capletEndpointAuthSchema.describe("Explicit Google API request auth config. Use {\"type\":\"none\"} for public APIs."),
|
|
26147
|
+
requestTimeoutMs: number$1().int().positive().optional().describe("Timeout in milliseconds for Google API HTTP requests."),
|
|
26148
|
+
operationCacheTtlMs: number$1().int().nonnegative().optional().describe("Milliseconds Google Discovery operation metadata stays fresh. Set 0 to refresh every time."),
|
|
26149
|
+
includeOperations: capletGoogleDiscoveryOperationFilterSchema.optional(),
|
|
26150
|
+
excludeOperations: capletGoogleDiscoveryOperationFilterSchema.optional(),
|
|
26151
|
+
disabled: boolean().optional().describe("When true, omit this Caplet from discovery."),
|
|
26152
|
+
projectBinding: capletProjectBindingSchema.optional(),
|
|
26153
|
+
runtime: capletRuntimeRequirementsSchema.optional()
|
|
26154
|
+
}).strict().superRefine((api, ctx) => {
|
|
26155
|
+
if (Boolean(api.discoveryPath) === Boolean(api.discoveryUrl)) ctx.addIssue({
|
|
26156
|
+
code: "custom",
|
|
26157
|
+
message: "googleDiscoveryApi must define exactly one discovery source: discoveryPath or discoveryUrl"
|
|
26158
|
+
});
|
|
26159
|
+
if (api.discoveryUrl && !hasEnvReference$2(api.discoveryUrl) && !isAllowedRemoteUrl(api.discoveryUrl)) ctx.addIssue({
|
|
26160
|
+
code: "custom",
|
|
26161
|
+
path: ["discoveryUrl"],
|
|
26162
|
+
message: "Google Discovery discoveryUrl must use https except loopback development urls"
|
|
26163
|
+
});
|
|
26164
|
+
if (api.baseUrl && !hasEnvReference$2(api.baseUrl) && !isAllowedHttpBaseUrl(api.baseUrl)) ctx.addIssue({
|
|
26165
|
+
code: "custom",
|
|
26166
|
+
path: ["baseUrl"],
|
|
26167
|
+
message: "Google Discovery baseUrl must use https except loopback development urls and must not include credentials, query, or fragment"
|
|
26168
|
+
});
|
|
26169
|
+
validateEndpointAuthHeaders$1(api.auth, ctx);
|
|
26170
|
+
});
|
|
26103
26171
|
const capletGraphQlOperationSchema = object$1({
|
|
26104
26172
|
document: string().min(1).optional().describe("Inline GraphQL operation document."),
|
|
26105
26173
|
documentPath: string().min(1).optional().describe("Path to a GraphQL operation document."),
|
|
@@ -26243,7 +26311,7 @@ const capletSetSchema = object$1({
|
|
|
26243
26311
|
});
|
|
26244
26312
|
});
|
|
26245
26313
|
const capletFileSchema = object$1({
|
|
26246
|
-
$schema: string().
|
|
26314
|
+
$schema: string().optional().describe("Optional JSON Schema for editor validation."),
|
|
26247
26315
|
name: string().trim().min(1).max(80).describe("Human-readable Caplet display name."),
|
|
26248
26316
|
description: string().describe("Compact capability description shown before the full Caplet card is disclosed.").refine((value) => value.trim().length >= 10, "description must contain at least 10 non-whitespace characters").refine((value) => value.length <= 1500, "description must be at most 1500 characters"),
|
|
26249
26317
|
tags: array(string().trim().min(1).max(80)).optional().describe("Optional tags for grouping or searching Caplets."),
|
|
@@ -26255,14 +26323,15 @@ const capletFileSchema = object$1({
|
|
|
26255
26323
|
runtime: capletRuntimeRequirementsSchema.optional(),
|
|
26256
26324
|
mcpServer: capletMcpServerSchema.describe("MCP server backend configuration for this Caplet.").optional(),
|
|
26257
26325
|
openapiEndpoint: capletOpenApiEndpointSchema.describe("OpenAPI endpoint backend configuration for this Caplet.").optional(),
|
|
26326
|
+
googleDiscoveryApi: capletGoogleDiscoveryApiSchema.describe("Google Discovery API backend configuration for this Caplet.").optional(),
|
|
26258
26327
|
graphqlEndpoint: capletGraphQlEndpointSchema.describe("GraphQL endpoint backend configuration for this Caplet.").optional(),
|
|
26259
26328
|
httpApi: capletHttpApiSchema.describe("HTTP API backend configuration for this Caplet.").optional(),
|
|
26260
26329
|
cliTools: capletCliToolsSchema.describe("CLI tools backend configuration for this Caplet.").optional(),
|
|
26261
26330
|
capletSet: capletSetSchema.describe("Nested Caplet collection backend configuration for this Caplet.").optional()
|
|
26262
26331
|
}).strict().superRefine((frontmatter, ctx) => {
|
|
26263
|
-
if (Number(Boolean(frontmatter.mcpServer)) + Number(Boolean(frontmatter.openapiEndpoint)) + Number(Boolean(frontmatter.graphqlEndpoint)) + Number(Boolean(frontmatter.httpApi)) + Number(Boolean(frontmatter.cliTools)) + Number(Boolean(frontmatter.capletSet)) !== 1) ctx.addIssue({
|
|
26332
|
+
if (Number(Boolean(frontmatter.mcpServer)) + Number(Boolean(frontmatter.openapiEndpoint)) + Number(Boolean(frontmatter.googleDiscoveryApi)) + Number(Boolean(frontmatter.graphqlEndpoint)) + Number(Boolean(frontmatter.httpApi)) + Number(Boolean(frontmatter.cliTools)) + Number(Boolean(frontmatter.capletSet)) !== 1) ctx.addIssue({
|
|
26264
26333
|
code: "custom",
|
|
26265
|
-
message: "Caplet file must define exactly one backend: mcpServer, openapiEndpoint, graphqlEndpoint, httpApi, cliTools, or capletSet"
|
|
26334
|
+
message: "Caplet file must define exactly one backend: mcpServer, openapiEndpoint, googleDiscoveryApi, graphqlEndpoint, httpApi, cliTools, or capletSet"
|
|
26266
26335
|
});
|
|
26267
26336
|
});
|
|
26268
26337
|
function loadCapletFilesFromMap(input) {
|
|
@@ -26281,13 +26350,14 @@ function loadCapletFilesFromMap(input) {
|
|
|
26281
26350
|
function buildCapletFileLoadResultFromEntries(root, candidates, readConfig, warnings) {
|
|
26282
26351
|
const servers = {};
|
|
26283
26352
|
const openapiEndpoints = {};
|
|
26353
|
+
const googleDiscoveryApis = {};
|
|
26284
26354
|
const graphqlEndpoints = {};
|
|
26285
26355
|
const httpApis = {};
|
|
26286
26356
|
const cliTools = {};
|
|
26287
26357
|
const capletSets = {};
|
|
26288
26358
|
const paths = {};
|
|
26289
26359
|
function hasId(id) {
|
|
26290
|
-
return Boolean(servers[id] || openapiEndpoints[id] || graphqlEndpoints[id] || httpApis[id] || cliTools[id] || capletSets[id]);
|
|
26360
|
+
return Boolean(servers[id] || openapiEndpoints[id] || googleDiscoveryApis[id] || graphqlEndpoints[id] || httpApis[id] || cliTools[id] || capletSets[id]);
|
|
26291
26361
|
}
|
|
26292
26362
|
for (const candidate of candidates) {
|
|
26293
26363
|
if (hasId(candidate.id)) {
|
|
@@ -26314,6 +26384,9 @@ function buildCapletFileLoadResultFromEntries(root, candidates, readConfig, warn
|
|
|
26314
26384
|
if (isPlainObject$6(config) && config.backend === "openapi") {
|
|
26315
26385
|
const { backend: _backend, ...endpoint } = config;
|
|
26316
26386
|
openapiEndpoints[candidate.id] = endpoint;
|
|
26387
|
+
} else if (isPlainObject$6(config) && config.backend === "googleDiscovery") {
|
|
26388
|
+
const { backend: _backend, ...api } = config;
|
|
26389
|
+
googleDiscoveryApis[candidate.id] = api;
|
|
26317
26390
|
} else if (isPlainObject$6(config) && config.backend === "graphql") {
|
|
26318
26391
|
const { backend: _backend, ...endpoint } = config;
|
|
26319
26392
|
graphqlEndpoints[candidate.id] = endpoint;
|
|
@@ -26330,6 +26403,7 @@ function buildCapletFileLoadResultFromEntries(root, candidates, readConfig, warn
|
|
|
26330
26403
|
}
|
|
26331
26404
|
const hasServers = Object.keys(servers).length > 0;
|
|
26332
26405
|
const hasOpenApi = Object.keys(openapiEndpoints).length > 0;
|
|
26406
|
+
const hasGoogleDiscovery = Object.keys(googleDiscoveryApis).length > 0;
|
|
26333
26407
|
const hasGraphQl = Object.keys(graphqlEndpoints).length > 0;
|
|
26334
26408
|
const hasHttpApis = Object.keys(httpApis).length > 0;
|
|
26335
26409
|
const hasCliTools = Object.keys(cliTools).length > 0;
|
|
@@ -26337,6 +26411,7 @@ function buildCapletFileLoadResultFromEntries(root, candidates, readConfig, warn
|
|
|
26337
26411
|
const config = {
|
|
26338
26412
|
...hasServers ? { mcpServers: servers } : {},
|
|
26339
26413
|
...hasOpenApi ? { openapiEndpoints } : {},
|
|
26414
|
+
...hasGoogleDiscovery ? { googleDiscoveryApis } : {},
|
|
26340
26415
|
...hasGraphQl ? { graphqlEndpoints } : {},
|
|
26341
26416
|
...hasHttpApis ? { httpApis } : {},
|
|
26342
26417
|
...hasCliTools ? { cliTools } : {},
|
|
@@ -26396,6 +26471,15 @@ function capletToServerConfig(frontmatter, body, baseDir, normalizePath) {
|
|
|
26396
26471
|
...sharedCapletFields(frontmatter),
|
|
26397
26472
|
body
|
|
26398
26473
|
};
|
|
26474
|
+
if (frontmatter.googleDiscoveryApi) return {
|
|
26475
|
+
...frontmatter.googleDiscoveryApi,
|
|
26476
|
+
discoveryPath: normalizePath(frontmatter.googleDiscoveryApi.discoveryPath, baseDir),
|
|
26477
|
+
backend: "googleDiscovery",
|
|
26478
|
+
name: frontmatter.name,
|
|
26479
|
+
description: frontmatter.description,
|
|
26480
|
+
...sharedCapletFields(frontmatter),
|
|
26481
|
+
body
|
|
26482
|
+
};
|
|
26399
26483
|
if (frontmatter.graphqlEndpoint) return {
|
|
26400
26484
|
...frontmatter.graphqlEndpoint,
|
|
26401
26485
|
schemaPath: normalizePath(frontmatter.graphqlEndpoint.schemaPath, baseDir),
|
|
@@ -26844,6 +26928,28 @@ const publicOpenApiEndpointSchema = object$1({
|
|
|
26844
26928
|
disabled: boolean().default(false).describe("When true, omit this OpenAPI Caplet from discovery.")
|
|
26845
26929
|
}).strict();
|
|
26846
26930
|
const normalizedOpenApiEndpointSchema = publicOpenApiEndpointSchema.extend({ body: string().optional() });
|
|
26931
|
+
const operationFilterSchema = array(string().trim().min(1).max(160));
|
|
26932
|
+
const publicGoogleDiscoveryApiSchema = object$1({
|
|
26933
|
+
name: string().trim().min(1).max(80).describe("Human-readable Google Discovery API display name."),
|
|
26934
|
+
description: string().describe("Capability description shown to agents before Google Discovery operations are disclosed.").refine((value) => value.trim().length >= 10, "description must contain at least 10 non-whitespace characters").refine((value) => value.length <= 1500, "description must be at most 1500 characters"),
|
|
26935
|
+
discoveryPath: string().min(1).optional().describe("Local Google Discovery document path."),
|
|
26936
|
+
discoveryUrl: string().url().optional().describe("Remote Google Discovery document URL."),
|
|
26937
|
+
baseUrl: string().url().optional().describe("Override base URL for Google API requests."),
|
|
26938
|
+
includeOperations: operationFilterSchema.optional(),
|
|
26939
|
+
excludeOperations: operationFilterSchema.optional(),
|
|
26940
|
+
auth: openApiAuthSchema.describe("Explicit Google API request auth config. Use {\"type\":\"none\"} for public APIs."),
|
|
26941
|
+
tags: array(string().trim().min(1).max(80)).optional(),
|
|
26942
|
+
exposure: exposureSchema.optional(),
|
|
26943
|
+
shadowing: shadowingSchema,
|
|
26944
|
+
...agentSelectionHintsSchema,
|
|
26945
|
+
setup: setupSchema.optional(),
|
|
26946
|
+
projectBinding: projectBindingSchema.optional(),
|
|
26947
|
+
runtime: runtimeRequirementsSchema.optional(),
|
|
26948
|
+
requestTimeoutMs: number$1().int().positive().default(6e4).describe("Timeout in milliseconds for Google Discovery HTTP requests."),
|
|
26949
|
+
operationCacheTtlMs: number$1().int().nonnegative().default(3e4).describe("Milliseconds Google Discovery operation metadata stays fresh. Set 0 to refresh every time."),
|
|
26950
|
+
disabled: boolean().default(false).describe("When true, omit this Google Discovery Caplet from discovery.")
|
|
26951
|
+
}).strict();
|
|
26952
|
+
const normalizedGoogleDiscoveryApiSchema = publicGoogleDiscoveryApiSchema.extend({ body: string().optional() });
|
|
26847
26953
|
const graphQlOperationSchema = object$1({
|
|
26848
26954
|
document: string().min(1).optional().describe("Inline GraphQL operation document."),
|
|
26849
26955
|
documentPath: string().min(1).optional().describe("Path to a GraphQL operation document."),
|
|
@@ -26996,9 +27102,9 @@ const publicCapletSetSchema = object$1({
|
|
|
26996
27102
|
});
|
|
26997
27103
|
});
|
|
26998
27104
|
const normalizedCapletSetSchema = publicCapletSetSchema.extend({ body: string().optional() });
|
|
26999
|
-
function configSchemaFor(serverValueSchema, openApiEndpointValueSchema, graphQlEndpointValueSchema, httpApiValueSchema, cliToolsValueSchema, capletSetValueSchema) {
|
|
27105
|
+
function configSchemaFor(serverValueSchema, openApiEndpointValueSchema, googleDiscoveryApiValueSchema, graphQlEndpointValueSchema, httpApiValueSchema, cliToolsValueSchema, capletSetValueSchema) {
|
|
27000
27106
|
return object$1({
|
|
27001
|
-
$schema: string().
|
|
27107
|
+
$schema: string().optional().describe("Optional JSON Schema for editor validation."),
|
|
27002
27108
|
version: literal(1).default(1).describe("Caplets config schema version."),
|
|
27003
27109
|
defaultSearchLimit: number$1().int().positive().default(20).describe("Default maximum number of same-server search results."),
|
|
27004
27110
|
maxSearchLimit: number$1().int().positive().max(50).default(50).describe("Maximum accepted search_tools limit."),
|
|
@@ -27024,6 +27130,7 @@ function configSchemaFor(serverValueSchema, openApiEndpointValueSchema, graphQlE
|
|
|
27024
27130
|
}).describe("Global Caplets runtime options."),
|
|
27025
27131
|
mcpServers: record(string().regex(SERVER_ID_PATTERN), serverValueSchema).default({}).describe("Downstream MCP servers keyed by stable server ID."),
|
|
27026
27132
|
openapiEndpoints: record(string().regex(SERVER_ID_PATTERN), openApiEndpointValueSchema).default({}).describe("OpenAPI endpoints keyed by stable Caplet ID."),
|
|
27133
|
+
googleDiscoveryApis: record(string().regex(SERVER_ID_PATTERN), googleDiscoveryApiValueSchema).default({}).describe("Google Discovery APIs keyed by stable Caplet ID."),
|
|
27027
27134
|
graphqlEndpoints: record(string().regex(SERVER_ID_PATTERN), graphQlEndpointValueSchema).default({}).describe("GraphQL endpoints keyed by stable Caplet ID."),
|
|
27028
27135
|
httpApis: record(string().regex(SERVER_ID_PATTERN), httpApiValueSchema).default({}).describe("HTTP APIs keyed by stable Caplet ID."),
|
|
27029
27136
|
cliTools: record(string().regex(SERVER_ID_PATTERN), cliToolsValueSchema).default({}).describe("CLI tools keyed by stable Caplet ID."),
|
|
@@ -27139,9 +27246,60 @@ function configSchemaFor(serverValueSchema, openApiEndpointValueSchema, graphQlE
|
|
|
27139
27246
|
});
|
|
27140
27247
|
}
|
|
27141
27248
|
}
|
|
27249
|
+
for (const [api, rawValue] of Object.entries(config.googleDiscoveryApis)) {
|
|
27250
|
+
const raw = rawValue;
|
|
27251
|
+
const duplicateBackend = config.mcpServers[api] ? "mcpServers" : config.openapiEndpoints[api] ? "openapiEndpoints" : config.graphqlEndpoints[api] ? "graphqlEndpoints" : config.httpApis[api] ? "httpApis" : config.cliTools[api] ? "cliTools" : config.capletSets[api] ? "capletSets" : void 0;
|
|
27252
|
+
if (duplicateBackend) ctx.addIssue({
|
|
27253
|
+
code: "custom",
|
|
27254
|
+
path: ["googleDiscoveryApis", api],
|
|
27255
|
+
message: `Caplet ID ${api} is already used by ${duplicateBackend}`
|
|
27256
|
+
});
|
|
27257
|
+
if (!SERVER_ID_PATTERN.test(api)) ctx.addIssue({
|
|
27258
|
+
code: "custom",
|
|
27259
|
+
path: ["googleDiscoveryApis", api],
|
|
27260
|
+
message: "Google Discovery API ID must match ^[a-zA-Z0-9_-]{1,64}$"
|
|
27261
|
+
});
|
|
27262
|
+
if (Boolean(raw.discoveryPath) === Boolean(raw.discoveryUrl)) ctx.addIssue({
|
|
27263
|
+
code: "custom",
|
|
27264
|
+
path: ["googleDiscoveryApis", api],
|
|
27265
|
+
message: "Google Discovery API must define exactly one discovery source: discoveryPath or discoveryUrl"
|
|
27266
|
+
});
|
|
27267
|
+
if (raw.discoveryUrl && !isAllowedRemoteUrl(raw.discoveryUrl)) ctx.addIssue({
|
|
27268
|
+
code: "custom",
|
|
27269
|
+
path: [
|
|
27270
|
+
"googleDiscoveryApis",
|
|
27271
|
+
api,
|
|
27272
|
+
"discoveryUrl"
|
|
27273
|
+
],
|
|
27274
|
+
message: "Google Discovery API discoveryUrl must use https except loopback development urls"
|
|
27275
|
+
});
|
|
27276
|
+
if (raw.baseUrl && !isAllowedHttpBaseUrl(raw.baseUrl)) ctx.addIssue({
|
|
27277
|
+
code: "custom",
|
|
27278
|
+
path: [
|
|
27279
|
+
"googleDiscoveryApis",
|
|
27280
|
+
api,
|
|
27281
|
+
"baseUrl"
|
|
27282
|
+
],
|
|
27283
|
+
message: "Google Discovery API baseUrl must use https except loopback development urls and must not include credentials, query, or fragment"
|
|
27284
|
+
});
|
|
27285
|
+
if (raw.auth?.type === "headers") for (const headerName of Object.keys(raw.auth.headers)) {
|
|
27286
|
+
const normalized = headerName.toLowerCase();
|
|
27287
|
+
if (!HEADER_NAME_PATTERN.test(headerName) || FORBIDDEN_HEADERS.has(normalized)) ctx.addIssue({
|
|
27288
|
+
code: "custom",
|
|
27289
|
+
path: [
|
|
27290
|
+
"googleDiscoveryApis",
|
|
27291
|
+
api,
|
|
27292
|
+
"auth",
|
|
27293
|
+
"headers",
|
|
27294
|
+
headerName
|
|
27295
|
+
],
|
|
27296
|
+
message: `header ${headerName} is not allowed`
|
|
27297
|
+
});
|
|
27298
|
+
}
|
|
27299
|
+
}
|
|
27142
27300
|
for (const [endpoint, rawValue] of Object.entries(config.graphqlEndpoints)) {
|
|
27143
27301
|
const raw = rawValue;
|
|
27144
|
-
const duplicateBackend = config.mcpServers[endpoint] ? "mcpServers" : config.openapiEndpoints[endpoint] ? "openapiEndpoints" : void 0;
|
|
27302
|
+
const duplicateBackend = config.mcpServers[endpoint] ? "mcpServers" : config.openapiEndpoints[endpoint] ? "openapiEndpoints" : config.googleDiscoveryApis[endpoint] ? "googleDiscoveryApis" : void 0;
|
|
27145
27303
|
if (duplicateBackend) ctx.addIssue({
|
|
27146
27304
|
code: "custom",
|
|
27147
27305
|
path: ["graphqlEndpoints", endpoint],
|
|
@@ -27183,7 +27341,7 @@ function configSchemaFor(serverValueSchema, openApiEndpointValueSchema, graphQlE
|
|
|
27183
27341
|
}
|
|
27184
27342
|
for (const [endpoint, rawValue] of Object.entries(config.httpApis)) {
|
|
27185
27343
|
const raw = rawValue;
|
|
27186
|
-
const duplicateBackend = config.mcpServers[endpoint] ? "mcpServers" : config.openapiEndpoints[endpoint] ? "openapiEndpoints" : config.graphqlEndpoints[endpoint] ? "graphqlEndpoints" : void 0;
|
|
27344
|
+
const duplicateBackend = config.mcpServers[endpoint] ? "mcpServers" : config.openapiEndpoints[endpoint] ? "openapiEndpoints" : config.googleDiscoveryApis[endpoint] ? "googleDiscoveryApis" : config.graphqlEndpoints[endpoint] ? "graphqlEndpoints" : void 0;
|
|
27187
27345
|
if (duplicateBackend) ctx.addIssue({
|
|
27188
27346
|
code: "custom",
|
|
27189
27347
|
path: ["httpApis", endpoint],
|
|
@@ -27218,7 +27376,7 @@ function configSchemaFor(serverValueSchema, openApiEndpointValueSchema, graphQlE
|
|
|
27218
27376
|
}
|
|
27219
27377
|
for (const [server, rawValue] of Object.entries(config.cliTools)) {
|
|
27220
27378
|
const raw = rawValue;
|
|
27221
|
-
const duplicateBackend = config.mcpServers[server] ? "mcpServers" : config.openapiEndpoints[server] ? "openapiEndpoints" : config.graphqlEndpoints[server] ? "graphqlEndpoints" : config.httpApis[server] ? "httpApis" : void 0;
|
|
27379
|
+
const duplicateBackend = config.mcpServers[server] ? "mcpServers" : config.openapiEndpoints[server] ? "openapiEndpoints" : config.googleDiscoveryApis[server] ? "googleDiscoveryApis" : config.graphqlEndpoints[server] ? "graphqlEndpoints" : config.httpApis[server] ? "httpApis" : void 0;
|
|
27222
27380
|
if (duplicateBackend) ctx.addIssue({
|
|
27223
27381
|
code: "custom",
|
|
27224
27382
|
path: ["cliTools", server],
|
|
@@ -27242,7 +27400,7 @@ function configSchemaFor(serverValueSchema, openApiEndpointValueSchema, graphQlE
|
|
|
27242
27400
|
}
|
|
27243
27401
|
for (const [server, rawValue] of Object.entries(config.capletSets)) {
|
|
27244
27402
|
const raw = rawValue;
|
|
27245
|
-
const duplicateBackend = config.mcpServers[server] ? "mcpServers" : config.openapiEndpoints[server] ? "openapiEndpoints" : config.graphqlEndpoints[server] ? "graphqlEndpoints" : config.httpApis[server] ? "httpApis" : config.cliTools[server] ? "cliTools" : void 0;
|
|
27403
|
+
const duplicateBackend = config.mcpServers[server] ? "mcpServers" : config.openapiEndpoints[server] ? "openapiEndpoints" : config.googleDiscoveryApis[server] ? "googleDiscoveryApis" : config.graphqlEndpoints[server] ? "graphqlEndpoints" : config.httpApis[server] ? "httpApis" : config.cliTools[server] ? "cliTools" : void 0;
|
|
27246
27404
|
if (duplicateBackend) ctx.addIssue({
|
|
27247
27405
|
code: "custom",
|
|
27248
27406
|
path: ["capletSets", server],
|
|
@@ -27261,8 +27419,8 @@ function configSchemaFor(serverValueSchema, openApiEndpointValueSchema, graphQlE
|
|
|
27261
27419
|
}
|
|
27262
27420
|
});
|
|
27263
27421
|
}
|
|
27264
|
-
const configFileSchema = configSchemaFor(publicServerSchema, publicOpenApiEndpointSchema, publicGraphQlEndpointSchema, publicHttpApiSchema, publicCliToolsSchema, publicCapletSetSchema);
|
|
27265
|
-
const normalizedConfigFileSchema = configSchemaFor(normalizedServerSchema, normalizedOpenApiEndpointSchema, normalizedGraphQlEndpointSchema, normalizedHttpApiSchema, normalizedCliToolsSchema, normalizedCapletSetSchema);
|
|
27422
|
+
const configFileSchema = configSchemaFor(publicServerSchema, publicOpenApiEndpointSchema, publicGoogleDiscoveryApiSchema, publicGraphQlEndpointSchema, publicHttpApiSchema, publicCliToolsSchema, publicCapletSetSchema);
|
|
27423
|
+
const normalizedConfigFileSchema = configSchemaFor(normalizedServerSchema, normalizedOpenApiEndpointSchema, normalizedGoogleDiscoveryApiSchema, normalizedGraphQlEndpointSchema, normalizedHttpApiSchema, normalizedCliToolsSchema, normalizedCapletSetSchema);
|
|
27266
27424
|
function loadConfig(path = resolveConfigPath(), projectPath = resolveProjectConfigPath()) {
|
|
27267
27425
|
return loadConfigWithSources(path, projectPath).config;
|
|
27268
27426
|
}
|
|
@@ -27303,7 +27461,7 @@ function loadConfigWithSources(path = resolveConfigPath(), projectPath = resolve
|
|
|
27303
27461
|
path: projectCaplets.paths
|
|
27304
27462
|
}
|
|
27305
27463
|
} : void 0
|
|
27306
|
-
], `Caplets config not found at ${path} or ${projectPath}`, "Caplets config must define at least one MCP server, OpenAPI endpoint, GraphQL endpoint, HTTP API, CLI tools backend, or Caplet set");
|
|
27464
|
+
], `Caplets config not found at ${path} or ${projectPath}`, "Caplets config must define at least one MCP server, OpenAPI endpoint, Google Discovery API, GraphQL endpoint, HTTP API, CLI tools backend, or Caplet set");
|
|
27307
27465
|
}
|
|
27308
27466
|
function loadGlobalConfig(path = resolveConfigPath()) {
|
|
27309
27467
|
const userConfig = existsSync(path) ? readPublicConfigInput(path) : void 0;
|
|
@@ -27345,7 +27503,7 @@ function buildConfigWithSources(inputs, notFoundMessage, emptyMessage) {
|
|
|
27345
27503
|
try {
|
|
27346
27504
|
const { input, sources, shadows } = mergeConfigInputsWithSources(...inputs);
|
|
27347
27505
|
const config = parseConfig(input);
|
|
27348
|
-
if (emptyMessage && Object.keys(config.mcpServers).length === 0 && Object.keys(config.openapiEndpoints).length === 0 && Object.keys(config.graphqlEndpoints).length === 0 && Object.keys(config.httpApis).length === 0 && Object.keys(config.cliTools).length === 0 && Object.keys(config.capletSets).length === 0) throw new CapletsError("CONFIG_INVALID", emptyMessage);
|
|
27506
|
+
if (emptyMessage && Object.keys(config.mcpServers).length === 0 && Object.keys(config.openapiEndpoints).length === 0 && Object.keys(config.googleDiscoveryApis).length === 0 && Object.keys(config.graphqlEndpoints).length === 0 && Object.keys(config.httpApis).length === 0 && Object.keys(config.cliTools).length === 0 && Object.keys(config.capletSets).length === 0) throw new CapletsError("CONFIG_INVALID", emptyMessage);
|
|
27349
27507
|
return {
|
|
27350
27508
|
config,
|
|
27351
27509
|
sources,
|
|
@@ -27434,7 +27592,7 @@ function loadIsolatedConfig(options) {
|
|
|
27434
27592
|
defaultSearchLimit: options.defaultSearchLimit,
|
|
27435
27593
|
maxSearchLimit: options.maxSearchLimit
|
|
27436
27594
|
}));
|
|
27437
|
-
if (Object.keys(config.mcpServers).length === 0 && Object.keys(config.openapiEndpoints).length === 0 && Object.keys(config.graphqlEndpoints).length === 0 && Object.keys(config.httpApis).length === 0 && Object.keys(config.cliTools).length === 0 && Object.keys(config.capletSets).length === 0) throw new CapletsError("CONFIG_INVALID", "Nested Caplet set must define at least one Caplet");
|
|
27595
|
+
if (Object.keys(config.mcpServers).length === 0 && Object.keys(config.openapiEndpoints).length === 0 && Object.keys(config.googleDiscoveryApis).length === 0 && Object.keys(config.graphqlEndpoints).length === 0 && Object.keys(config.httpApis).length === 0 && Object.keys(config.cliTools).length === 0 && Object.keys(config.capletSets).length === 0) throw new CapletsError("CONFIG_INVALID", "Nested Caplet set must define at least one Caplet");
|
|
27438
27596
|
return config;
|
|
27439
27597
|
}
|
|
27440
27598
|
function resolveProjectCapletsRootForConfigPath(projectPath) {
|
|
@@ -27456,6 +27614,7 @@ function normalizeLocalPaths(input, baseDir) {
|
|
|
27456
27614
|
return stripUndefined({
|
|
27457
27615
|
...input,
|
|
27458
27616
|
openapiEndpoints: normalizeEndpointPaths(input.openapiEndpoints, baseDir, normalizeOpenApiPath),
|
|
27617
|
+
googleDiscoveryApis: normalizeEndpointPaths(input.googleDiscoveryApis, baseDir, normalizeGoogleDiscoveryPath),
|
|
27459
27618
|
graphqlEndpoints: normalizeEndpointPaths(input.graphqlEndpoints, baseDir, normalizeGraphQlPath),
|
|
27460
27619
|
cliTools: normalizeEndpointPaths(input.cliTools, baseDir, normalizeCliToolsPaths),
|
|
27461
27620
|
capletSets: normalizeEndpointPaths(input.capletSets, baseDir, normalizeCapletSetPaths)
|
|
@@ -27471,6 +27630,12 @@ function normalizeOpenApiPath(endpoint, baseDir) {
|
|
|
27471
27630
|
specPath: normalizeLocalPath(endpoint.specPath, baseDir)
|
|
27472
27631
|
};
|
|
27473
27632
|
}
|
|
27633
|
+
function normalizeGoogleDiscoveryPath(endpoint, baseDir) {
|
|
27634
|
+
return {
|
|
27635
|
+
...endpoint,
|
|
27636
|
+
discoveryPath: normalizeLocalPath(endpoint.discoveryPath, baseDir)
|
|
27637
|
+
};
|
|
27638
|
+
}
|
|
27474
27639
|
function normalizeGraphQlPath(endpoint, baseDir) {
|
|
27475
27640
|
const operations = isPlainObject$5(endpoint.operations) ? Object.fromEntries(Object.entries(endpoint.operations).map(([name, operation]) => [name, isPlainObject$5(operation) ? {
|
|
27476
27641
|
...operation,
|
|
@@ -27506,6 +27671,7 @@ function normalizeLocalPath(value, baseDir) {
|
|
|
27506
27671
|
}
|
|
27507
27672
|
function rejectProjectConfigExecutableBackendMaps(input, path) {
|
|
27508
27673
|
if (input.openapiEndpoints && Object.keys(input.openapiEndpoints).length > 0) throw new CapletsError("CONFIG_INVALID", `Project config at ${path} cannot define executable backend map openapiEndpoints; use project Markdown Caplet files or user config instead`);
|
|
27674
|
+
if (input.googleDiscoveryApis && Object.keys(input.googleDiscoveryApis).length > 0) throw new CapletsError("CONFIG_INVALID", `Project config at ${path} cannot define executable backend map googleDiscoveryApis; use project Markdown Caplet files or user config instead`);
|
|
27509
27675
|
if (input.graphqlEndpoints && Object.keys(input.graphqlEndpoints).length > 0) throw new CapletsError("CONFIG_INVALID", `Project config at ${path} cannot define executable backend map graphqlEndpoints; use project Markdown Caplet files or user config instead`);
|
|
27510
27676
|
if (input.httpApis && Object.keys(input.httpApis).length > 0) throw new CapletsError("CONFIG_INVALID", `Project config at ${path} cannot define executable backend map httpApis; use project Markdown Caplet files or user config instead`);
|
|
27511
27677
|
if (input.cliTools && Object.keys(input.cliTools).length > 0) throw new CapletsError("CONFIG_INVALID", `Project config at ${path} cannot define executable backend map cliTools; use project Markdown Caplet files or user config instead`);
|
|
@@ -27527,6 +27693,10 @@ function mergeConfigInputs(...inputs) {
|
|
|
27527
27693
|
...merged?.openapiEndpoints,
|
|
27528
27694
|
...input.openapiEndpoints
|
|
27529
27695
|
},
|
|
27696
|
+
googleDiscoveryApis: {
|
|
27697
|
+
...merged?.googleDiscoveryApis,
|
|
27698
|
+
...input.googleDiscoveryApis
|
|
27699
|
+
},
|
|
27530
27700
|
graphqlEndpoints: {
|
|
27531
27701
|
...merged?.graphqlEndpoints,
|
|
27532
27702
|
...input.graphqlEndpoints
|
|
@@ -27570,6 +27740,7 @@ function mergeConfigInputsWithSources(...inputs) {
|
|
|
27570
27740
|
function removeCapletId(input, id) {
|
|
27571
27741
|
const { [id]: _mcpServer, ...mcpServers } = input.mcpServers ?? {};
|
|
27572
27742
|
const { [id]: _openapiEndpoint, ...openapiEndpoints } = input.openapiEndpoints ?? {};
|
|
27743
|
+
const { [id]: _googleDiscoveryApi, ...googleDiscoveryApis } = input.googleDiscoveryApis ?? {};
|
|
27573
27744
|
const { [id]: _graphqlEndpoint, ...graphqlEndpoints } = input.graphqlEndpoints ?? {};
|
|
27574
27745
|
const { [id]: _httpApi, ...httpApis } = input.httpApis ?? {};
|
|
27575
27746
|
const { [id]: _cliTools, ...cliTools } = input.cliTools ?? {};
|
|
@@ -27578,6 +27749,7 @@ function removeCapletId(input, id) {
|
|
|
27578
27749
|
...input,
|
|
27579
27750
|
mcpServers,
|
|
27580
27751
|
openapiEndpoints,
|
|
27752
|
+
googleDiscoveryApis,
|
|
27581
27753
|
graphqlEndpoints,
|
|
27582
27754
|
httpApis,
|
|
27583
27755
|
cliTools,
|
|
@@ -27588,6 +27760,7 @@ function capletIds(input) {
|
|
|
27588
27760
|
return [
|
|
27589
27761
|
...Object.keys(input.mcpServers ?? {}),
|
|
27590
27762
|
...Object.keys(input.openapiEndpoints ?? {}),
|
|
27763
|
+
...Object.keys(input.googleDiscoveryApis ?? {}),
|
|
27591
27764
|
...Object.keys(input.graphqlEndpoints ?? {}),
|
|
27592
27765
|
...Object.keys(input.httpApis ?? {}),
|
|
27593
27766
|
...Object.keys(input.cliTools ?? {}),
|
|
@@ -27619,6 +27792,12 @@ function parseConfig(input) {
|
|
|
27619
27792
|
server,
|
|
27620
27793
|
backend: "openapi"
|
|
27621
27794
|
});
|
|
27795
|
+
const googleDiscoveryApis = {};
|
|
27796
|
+
for (const [server, raw] of Object.entries(parsed.data.googleDiscoveryApis)) googleDiscoveryApis[server] = stripUndefined({
|
|
27797
|
+
...raw,
|
|
27798
|
+
server,
|
|
27799
|
+
backend: "googleDiscovery"
|
|
27800
|
+
});
|
|
27622
27801
|
const graphqlEndpoints = {};
|
|
27623
27802
|
for (const [server, raw] of Object.entries(parsed.data.graphqlEndpoints)) graphqlEndpoints[server] = stripUndefined({
|
|
27624
27803
|
...raw,
|
|
@@ -27655,6 +27834,7 @@ function parseConfig(input) {
|
|
|
27655
27834
|
},
|
|
27656
27835
|
mcpServers: servers,
|
|
27657
27836
|
openapiEndpoints,
|
|
27837
|
+
googleDiscoveryApis,
|
|
27658
27838
|
graphqlEndpoints,
|
|
27659
27839
|
httpApis,
|
|
27660
27840
|
cliTools,
|
|
@@ -27687,7 +27867,7 @@ function interpolateConfig(value, path = []) {
|
|
|
27687
27867
|
return value;
|
|
27688
27868
|
}
|
|
27689
27869
|
function isPublicMetadataPath(path) {
|
|
27690
|
-
if (path.length < 3 || path[0] !== "mcpServers" && path[0] !== "openapiEndpoints" && path[0] !== "graphqlEndpoints" && path[0] !== "httpApis" && path[0] !== "cliTools" && path[0] !== "capletSets") return false;
|
|
27870
|
+
if (path.length < 3 || path[0] !== "mcpServers" && path[0] !== "openapiEndpoints" && path[0] !== "googleDiscoveryApis" && path[0] !== "graphqlEndpoints" && path[0] !== "httpApis" && path[0] !== "cliTools" && path[0] !== "capletSets") return false;
|
|
27691
27871
|
return NON_INTERPOLATED_SERVER_FIELDS.has(path[2] ?? "");
|
|
27692
27872
|
}
|
|
27693
27873
|
function isPlainObject$5(value) {
|
|
@@ -27700,6 +27880,1272 @@ function interpolateEnv(value) {
|
|
|
27700
27880
|
return value.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_match, name) => process.env[name] ?? "").replace(/\$env:([A-Za-z_][A-Za-z0-9_]*)/g, (_match, name) => process.env[name] ?? "");
|
|
27701
27881
|
}
|
|
27702
27882
|
//#endregion
|
|
27883
|
+
//#region src/google-discovery/schema.ts
|
|
27884
|
+
function googleDiscoverySchemaToJsonSchema(value, schemas = {}, seen = /* @__PURE__ */ new Set()) {
|
|
27885
|
+
if (!value) return {};
|
|
27886
|
+
if (value.$ref) {
|
|
27887
|
+
const target = schemas[value.$ref];
|
|
27888
|
+
if (!target || seen.has(value.$ref)) return {
|
|
27889
|
+
type: "object",
|
|
27890
|
+
additionalProperties: true
|
|
27891
|
+
};
|
|
27892
|
+
return googleDiscoverySchemaToJsonSchema(target, schemas, new Set([...seen, value.$ref]));
|
|
27893
|
+
}
|
|
27894
|
+
const type = discoveryTypeToJsonSchemaType(value.type);
|
|
27895
|
+
const converted = {};
|
|
27896
|
+
if (value.description) converted.description = collapseWhitespace$1(value.description);
|
|
27897
|
+
if (type) converted.type = type;
|
|
27898
|
+
if (value.format) converted.format = value.format;
|
|
27899
|
+
if (value.enum) converted.enum = value.enum;
|
|
27900
|
+
const defaultValue = convertedDefault(value.default, type);
|
|
27901
|
+
if (defaultValue !== void 0) converted.default = defaultValue;
|
|
27902
|
+
if (value.repeated) return {
|
|
27903
|
+
...converted.description ? { description: converted.description } : {},
|
|
27904
|
+
type: "array",
|
|
27905
|
+
items: omit(converted, ["description", "default"])
|
|
27906
|
+
};
|
|
27907
|
+
if (value.items) converted.items = googleDiscoverySchemaToJsonSchema(value.items, schemas, seen);
|
|
27908
|
+
if (value.properties) {
|
|
27909
|
+
converted.type = converted.type ?? "object";
|
|
27910
|
+
converted.properties = Object.fromEntries(Object.entries(value.properties).map(([key, schema]) => [key, googleDiscoverySchemaToJsonSchema(schema, schemas, seen)]));
|
|
27911
|
+
converted.additionalProperties = false;
|
|
27912
|
+
}
|
|
27913
|
+
if (typeof value.additionalProperties === "boolean") converted.additionalProperties = value.additionalProperties;
|
|
27914
|
+
else if (value.additionalProperties) converted.additionalProperties = googleDiscoverySchemaToJsonSchema(value.additionalProperties, schemas, seen);
|
|
27915
|
+
return converted;
|
|
27916
|
+
}
|
|
27917
|
+
function discoveryTypeToJsonSchemaType(type) {
|
|
27918
|
+
if (type === "any") return "object";
|
|
27919
|
+
return type;
|
|
27920
|
+
}
|
|
27921
|
+
function convertedDefault(value, type) {
|
|
27922
|
+
if (value === void 0) return void 0;
|
|
27923
|
+
if (type === "boolean" && typeof value === "string") return value === "true";
|
|
27924
|
+
if ((type === "integer" || type === "number") && typeof value === "string") {
|
|
27925
|
+
const number = Number(value);
|
|
27926
|
+
return Number.isFinite(number) ? number : value;
|
|
27927
|
+
}
|
|
27928
|
+
return value;
|
|
27929
|
+
}
|
|
27930
|
+
function collapseWhitespace$1(value) {
|
|
27931
|
+
return value.replace(/\s+/gu, " ").trim();
|
|
27932
|
+
}
|
|
27933
|
+
function omit(value, keys) {
|
|
27934
|
+
return Object.fromEntries(Object.entries(value).filter(([key]) => !keys.includes(key)));
|
|
27935
|
+
}
|
|
27936
|
+
//#endregion
|
|
27937
|
+
//#region src/google-discovery/operations.ts
|
|
27938
|
+
function discoveryOperations(options) {
|
|
27939
|
+
const document = validateGoogleDiscoveryDocument(options.document);
|
|
27940
|
+
const schemas = document.schemas ?? {};
|
|
27941
|
+
return collectDocumentMethods(document).map((entry) => operationFromMethod(options.server, document, schemas, entry)).filter((operation) => isIncluded(operation.name, options.includeOperations)).filter((operation) => !isExcluded(operation.name, options.excludeOperations)).sort((left, right) => left.name.localeCompare(right.name));
|
|
27942
|
+
}
|
|
27943
|
+
function googleDiscoveryScopesForOperations(operations) {
|
|
27944
|
+
return [...new Set(operations.flatMap((operation) => operation.scopes))].sort();
|
|
27945
|
+
}
|
|
27946
|
+
function validateGoogleDiscoveryDocument(value) {
|
|
27947
|
+
if (!isRecord$3(value)) throw new Error("Invalid Google Discovery document: expected an object");
|
|
27948
|
+
if (value.kind !== void 0 && value.kind !== "discovery#restDescription") throw new Error("Invalid Google Discovery document: expected kind discovery#restDescription");
|
|
27949
|
+
if (value.resources !== void 0 && !isRecord$3(value.resources)) throw new Error("Invalid Google Discovery document: expected resources object");
|
|
27950
|
+
if (value.methods !== void 0 && !isRecord$3(value.methods)) throw new Error("Invalid Google Discovery document: expected methods object");
|
|
27951
|
+
if (!isRecord$3(value.resources) && !isRecord$3(value.methods)) throw new Error("Invalid Google Discovery document: expected resources or methods object");
|
|
27952
|
+
if (value.schemas !== void 0 && !isRecord$3(value.schemas)) throw new Error("Invalid Google Discovery document: expected schemas object");
|
|
27953
|
+
if (value.parameters !== void 0 && !isRecord$3(value.parameters)) throw new Error("Invalid Google Discovery document: expected parameters object");
|
|
27954
|
+
return value;
|
|
27955
|
+
}
|
|
27956
|
+
function collectDocumentMethods(document) {
|
|
27957
|
+
return [...Object.entries(document.methods ?? {}).filter((entry) => isRecord$3(entry[1])).map(([methodKey, method]) => ({
|
|
27958
|
+
resourcePath: [],
|
|
27959
|
+
methodKey,
|
|
27960
|
+
method
|
|
27961
|
+
})), ...collectMethods(document.resources ?? {})];
|
|
27962
|
+
}
|
|
27963
|
+
function collectMethods(resources, resourcePath = []) {
|
|
27964
|
+
const entries = [];
|
|
27965
|
+
for (const [resourceName, resource] of Object.entries(resources)) {
|
|
27966
|
+
if (!isRecord$3(resource)) continue;
|
|
27967
|
+
const nextPath = [...resourcePath, resourceName];
|
|
27968
|
+
for (const [methodKey, method] of Object.entries(resource.methods ?? {})) if (isRecord$3(method)) entries.push({
|
|
27969
|
+
resourcePath: nextPath,
|
|
27970
|
+
methodKey,
|
|
27971
|
+
method
|
|
27972
|
+
});
|
|
27973
|
+
entries.push(...collectMethods(resource.resources ?? {}, nextPath));
|
|
27974
|
+
}
|
|
27975
|
+
return entries;
|
|
27976
|
+
}
|
|
27977
|
+
function operationFromMethod(server, document, schemas, entry) {
|
|
27978
|
+
const method = normalizedHttpMethod(entry.method.httpMethod);
|
|
27979
|
+
const name = entry.method.id ?? [
|
|
27980
|
+
server,
|
|
27981
|
+
...entry.resourcePath,
|
|
27982
|
+
entry.methodKey
|
|
27983
|
+
].join(".");
|
|
27984
|
+
const scopes = selectGoogleDiscoveryScopes(entry.method.scopes);
|
|
27985
|
+
const inputSchema = buildInputSchema(document.parameters ?? {}, entry.method, schemas);
|
|
27986
|
+
const bodyOutputSchema = entry.method.response?.$ref ? googleDiscoverySchemaToJsonSchema(entry.method.response, schemas) : void 0;
|
|
27987
|
+
const outputSchema = bodyOutputSchema ? structuredOutputSchema$1(bodyOutputSchema) : void 0;
|
|
27988
|
+
const mediaUpload = entry.method.mediaUpload?.accept || entry.method.mediaUpload?.maxSize ? {
|
|
27989
|
+
...entry.method.mediaUpload.accept ? { accept: entry.method.mediaUpload.accept } : {},
|
|
27990
|
+
...entry.method.mediaUpload.maxSize ? { maxSize: entry.method.mediaUpload.maxSize } : {}
|
|
27991
|
+
} : void 0;
|
|
27992
|
+
return {
|
|
27993
|
+
name,
|
|
27994
|
+
method,
|
|
27995
|
+
path: entry.method.path ?? entry.method.flatPath ?? "",
|
|
27996
|
+
...entry.method.description ? { description: collapseWhitespace(entry.method.description) } : {},
|
|
27997
|
+
inputSchema,
|
|
27998
|
+
...outputSchema ? { outputSchema } : {},
|
|
27999
|
+
readOnlyHint: method === "get" || method === "head",
|
|
28000
|
+
destructiveHint: method === "delete" || /\.(delete|emptyTrash)$/u.test(name),
|
|
28001
|
+
scopes,
|
|
28002
|
+
supportsMediaUpload: entry.method.supportsMediaUpload === true,
|
|
28003
|
+
supportsMediaDownload: entry.method.supportsMediaDownload === true,
|
|
28004
|
+
...mediaUpload ? { mediaUpload } : {},
|
|
28005
|
+
mediaUploadProtocols: mediaUploadProtocols(entry.method),
|
|
28006
|
+
parameterOrder: entry.method.parameterOrder ?? []
|
|
28007
|
+
};
|
|
28008
|
+
}
|
|
28009
|
+
function mediaUploadProtocols(method) {
|
|
28010
|
+
const protocols = { ...method.mediaUpload?.protocols };
|
|
28011
|
+
if (!protocols.multipart && protocols.simple?.multipart === true) protocols.multipart = protocols.simple;
|
|
28012
|
+
return protocols;
|
|
28013
|
+
}
|
|
28014
|
+
function selectGoogleDiscoveryScopes(scopes) {
|
|
28015
|
+
const preferred = [...new Set(scopes ?? [])].sort().toSorted(compareScopePreference)[0];
|
|
28016
|
+
return preferred ? [preferred] : [];
|
|
28017
|
+
}
|
|
28018
|
+
function compareScopePreference(left, right) {
|
|
28019
|
+
return scopePreferenceRank(left) - scopePreferenceRank(right) || right.length - left.length || left.localeCompare(right);
|
|
28020
|
+
}
|
|
28021
|
+
function scopePreferenceRank(scope) {
|
|
28022
|
+
const suffix = scope.toLowerCase().split("/").pop() ?? scope.toLowerCase();
|
|
28023
|
+
const tokens = suffix.split(/[._:-]+/u);
|
|
28024
|
+
if (tokens.includes("readonly")) return 0;
|
|
28025
|
+
if (tokens.includes("file")) return 1;
|
|
28026
|
+
if (tokens.includes("metadata") || tokens.includes("appdata")) return 2;
|
|
28027
|
+
if (tokens.includes("read")) return 3;
|
|
28028
|
+
if (suffix === "cloud-platform") return 5;
|
|
28029
|
+
return 4;
|
|
28030
|
+
}
|
|
28031
|
+
function structuredOutputSchema$1(bodySchema) {
|
|
28032
|
+
return {
|
|
28033
|
+
type: "object",
|
|
28034
|
+
additionalProperties: false,
|
|
28035
|
+
required: [
|
|
28036
|
+
"status",
|
|
28037
|
+
"statusText",
|
|
28038
|
+
"headers"
|
|
28039
|
+
],
|
|
28040
|
+
properties: {
|
|
28041
|
+
status: { type: "number" },
|
|
28042
|
+
statusText: { type: "string" },
|
|
28043
|
+
headers: {
|
|
28044
|
+
type: "object",
|
|
28045
|
+
additionalProperties: false,
|
|
28046
|
+
required: ["content-type"],
|
|
28047
|
+
properties: { "content-type": { type: "string" } }
|
|
28048
|
+
},
|
|
28049
|
+
body: bodySchema
|
|
28050
|
+
}
|
|
28051
|
+
};
|
|
28052
|
+
}
|
|
28053
|
+
function buildInputSchema(globalParameters, method, schemas) {
|
|
28054
|
+
const groups = /* @__PURE__ */ new Map();
|
|
28055
|
+
const requiredByGroup = /* @__PURE__ */ new Map();
|
|
28056
|
+
const parameters = {
|
|
28057
|
+
...globalParameters,
|
|
28058
|
+
...method.parameters
|
|
28059
|
+
};
|
|
28060
|
+
for (const [name, parameter] of Object.entries(parameters)) {
|
|
28061
|
+
const location = parameter.location ?? "query";
|
|
28062
|
+
const group = groups.get(location) ?? {};
|
|
28063
|
+
group[name] = googleDiscoverySchemaToJsonSchema(parameter, schemas);
|
|
28064
|
+
groups.set(location, group);
|
|
28065
|
+
if (parameter.required === true) {
|
|
28066
|
+
const required = requiredByGroup.get(location) ?? [];
|
|
28067
|
+
required.push(name);
|
|
28068
|
+
requiredByGroup.set(location, required);
|
|
28069
|
+
}
|
|
28070
|
+
}
|
|
28071
|
+
if (method.request?.$ref) groups.set("body", googleDiscoverySchemaToJsonSchema(method.request, schemas));
|
|
28072
|
+
if (method.supportsMediaUpload === true) groups.set("media", {
|
|
28073
|
+
type: "object",
|
|
28074
|
+
additionalProperties: false,
|
|
28075
|
+
properties: {
|
|
28076
|
+
path: { type: "string" },
|
|
28077
|
+
artifact: { type: "string" },
|
|
28078
|
+
dataUrl: { type: "string" },
|
|
28079
|
+
mimeType: { type: "string" },
|
|
28080
|
+
filename: { type: "string" }
|
|
28081
|
+
}
|
|
28082
|
+
});
|
|
28083
|
+
const properties = {};
|
|
28084
|
+
const required = [];
|
|
28085
|
+
for (const location of [
|
|
28086
|
+
"path",
|
|
28087
|
+
"query",
|
|
28088
|
+
"header",
|
|
28089
|
+
"body",
|
|
28090
|
+
"media"
|
|
28091
|
+
]) {
|
|
28092
|
+
const group = groups.get(location);
|
|
28093
|
+
if (!group) continue;
|
|
28094
|
+
if ((location === "body" || location === "media") && isJsonSchemaObject(group)) properties[location] = group;
|
|
28095
|
+
else {
|
|
28096
|
+
const groupRequired = requiredByGroup.get(location) ?? [];
|
|
28097
|
+
properties[location] = {
|
|
28098
|
+
type: "object",
|
|
28099
|
+
...groupRequired.length > 0 ? { required: groupRequired } : {},
|
|
28100
|
+
properties: group,
|
|
28101
|
+
additionalProperties: false
|
|
28102
|
+
};
|
|
28103
|
+
}
|
|
28104
|
+
if (location === "path" || requiredByGroup.has(location)) required.push(location);
|
|
28105
|
+
}
|
|
28106
|
+
if (method.supportsMediaDownload === true) {
|
|
28107
|
+
properties.filename = { type: "string" };
|
|
28108
|
+
properties.outputPath = { type: "string" };
|
|
28109
|
+
}
|
|
28110
|
+
return {
|
|
28111
|
+
type: "object",
|
|
28112
|
+
...required.length > 0 ? { required } : {},
|
|
28113
|
+
properties,
|
|
28114
|
+
additionalProperties: false
|
|
28115
|
+
};
|
|
28116
|
+
}
|
|
28117
|
+
function normalizedHttpMethod(method) {
|
|
28118
|
+
const normalized = method?.toLowerCase();
|
|
28119
|
+
if (normalized === "get" || normalized === "put" || normalized === "post" || normalized === "delete" || normalized === "patch" || normalized === "head") return normalized;
|
|
28120
|
+
return "get";
|
|
28121
|
+
}
|
|
28122
|
+
function isIncluded(name, includeOperations) {
|
|
28123
|
+
return !includeOperations?.length || includeOperations.some((pattern) => globMatches(pattern, name));
|
|
28124
|
+
}
|
|
28125
|
+
function isExcluded(name, excludeOperations) {
|
|
28126
|
+
return excludeOperations?.some((pattern) => globMatches(pattern, name)) === true;
|
|
28127
|
+
}
|
|
28128
|
+
function globMatches(pattern, name) {
|
|
28129
|
+
const patternSegments = pattern.split(".");
|
|
28130
|
+
const nameSegments = name.split(".");
|
|
28131
|
+
if (patternSegments[0] === "*" && patternSegments.length < nameSegments.length) {
|
|
28132
|
+
const suffix = patternSegments.slice(1);
|
|
28133
|
+
return suffix.every((segment, index) => segment === nameSegments[nameSegments.length - suffix.length + index]);
|
|
28134
|
+
}
|
|
28135
|
+
if (patternSegments.length !== nameSegments.length) return false;
|
|
28136
|
+
return patternSegments.every((segment, index) => segment === "*" || segment === nameSegments[index]);
|
|
28137
|
+
}
|
|
28138
|
+
function isJsonSchemaObject(value) {
|
|
28139
|
+
return value.type === "object" || "properties" in value || "additionalProperties" in value;
|
|
28140
|
+
}
|
|
28141
|
+
function isRecord$3(value) {
|
|
28142
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
28143
|
+
}
|
|
28144
|
+
function collapseWhitespace(value) {
|
|
28145
|
+
return value.replace(/\s+/gu, " ").trim();
|
|
28146
|
+
}
|
|
28147
|
+
//#endregion
|
|
28148
|
+
//#region src/google-discovery/request.ts
|
|
28149
|
+
function buildGoogleDiscoveryUrl(api, operation, args) {
|
|
28150
|
+
const base = api.baseUrl;
|
|
28151
|
+
validateBaseUrl$1(api, base);
|
|
28152
|
+
const url = buildOperationUrl$1(base, substitutePath$2(operation.path, asRecord$2(args.path), operation));
|
|
28153
|
+
appendQueryArgs(url, args);
|
|
28154
|
+
return url;
|
|
28155
|
+
}
|
|
28156
|
+
function buildGoogleDiscoveryUploadUrl(api, operation, uploadPath, uploadType, args) {
|
|
28157
|
+
const base = api.baseUrl;
|
|
28158
|
+
validateBaseUrl$1(api, base);
|
|
28159
|
+
const url = buildUploadOperationUrl(base, substitutePath$2(uploadPath, asRecord$2(args.path), operation));
|
|
28160
|
+
appendQueryArgs(url, args);
|
|
28161
|
+
url.searchParams.set("uploadType", uploadType);
|
|
28162
|
+
return url;
|
|
28163
|
+
}
|
|
28164
|
+
function buildJsonRequestInit(operation, args, headers) {
|
|
28165
|
+
for (const [key, value] of Object.entries(asRecord$2(args.header))) if (value !== void 0 && value !== null) {
|
|
28166
|
+
const normalized = key.toLowerCase();
|
|
28167
|
+
if (FORBIDDEN_HEADERS.has(normalized)) throw new CapletsError("REQUEST_INVALID", `Header ${key} cannot be supplied by arguments`);
|
|
28168
|
+
headers.set(key, serializeGoogleDiscoveryValue("header", key, value));
|
|
28169
|
+
}
|
|
28170
|
+
if ("body" in args) {
|
|
28171
|
+
headers.set("content-type", "application/json");
|
|
28172
|
+
return {
|
|
28173
|
+
method: operation.method.toUpperCase(),
|
|
28174
|
+
headers,
|
|
28175
|
+
body: JSON.stringify(args.body),
|
|
28176
|
+
redirect: "manual"
|
|
28177
|
+
};
|
|
28178
|
+
}
|
|
28179
|
+
return {
|
|
28180
|
+
method: operation.method.toUpperCase(),
|
|
28181
|
+
headers,
|
|
28182
|
+
redirect: "manual"
|
|
28183
|
+
};
|
|
28184
|
+
}
|
|
28185
|
+
function validateBaseUrl$1(api, base) {
|
|
28186
|
+
if (!base) throw new CapletsError("CONFIG_INVALID", `${api.server} is missing Google Discovery baseUrl`);
|
|
28187
|
+
if (!isAllowedRemoteUrl(base)) throw new CapletsError("CONFIG_INVALID", `${api.server} Google Discovery baseUrl is not allowed`);
|
|
28188
|
+
const url = new URL(base);
|
|
28189
|
+
if (url.username || url.password || url.search || url.hash) throw new CapletsError("CONFIG_INVALID", `${api.server} Google Discovery baseUrl must not include credentials, query, or fragment`);
|
|
28190
|
+
}
|
|
28191
|
+
function buildOperationUrl$1(base, operationPath) {
|
|
28192
|
+
if (/^[a-z][a-z0-9+.-]*:/iu.test(operationPath) || operationPath.startsWith("//")) throw new CapletsError("CONFIG_INVALID", "Google Discovery operation path cannot change origin");
|
|
28193
|
+
const baseUrl = new URL(base);
|
|
28194
|
+
const basePath = baseUrl.pathname.replace(/\/+$/u, "");
|
|
28195
|
+
const relativePath = operationPath.replace(/^\/+/u, "");
|
|
28196
|
+
assertSafeRelativePath(relativePath);
|
|
28197
|
+
baseUrl.pathname = [basePath, relativePath].filter(Boolean).join("/");
|
|
28198
|
+
assertInsideBasePath(baseUrl, basePath);
|
|
28199
|
+
return baseUrl;
|
|
28200
|
+
}
|
|
28201
|
+
function buildUploadOperationUrl(base, uploadPath) {
|
|
28202
|
+
if (/^[a-z][a-z0-9+.-]*:/iu.test(uploadPath) || uploadPath.startsWith("//")) throw new CapletsError("CONFIG_INVALID", "Google Discovery upload path cannot change origin");
|
|
28203
|
+
const baseUrl = new URL(base);
|
|
28204
|
+
const relativePath = uploadPath.replace(/^\/+/u, "");
|
|
28205
|
+
assertSafeRelativePath(relativePath);
|
|
28206
|
+
return uploadPath.startsWith("/") ? new URL(`/${relativePath}`, baseUrl.origin) : buildOperationUrl$1(base, uploadPath);
|
|
28207
|
+
}
|
|
28208
|
+
function appendQueryArgs(url, args) {
|
|
28209
|
+
for (const [key, value] of Object.entries(asRecord$2(args.query))) {
|
|
28210
|
+
if (value === void 0 || value === null) continue;
|
|
28211
|
+
if (Array.isArray(value)) {
|
|
28212
|
+
for (const entry of value) url.searchParams.append(key, serializeGoogleDiscoveryValue("query", key, entry));
|
|
28213
|
+
continue;
|
|
28214
|
+
}
|
|
28215
|
+
url.searchParams.append(key, serializeGoogleDiscoveryValue("query", key, value));
|
|
28216
|
+
}
|
|
28217
|
+
}
|
|
28218
|
+
function substitutePath$2(path, values, operation) {
|
|
28219
|
+
return path.replace(/\{([^}]+)\}/gu, (_match, expression) => {
|
|
28220
|
+
const reserved = expression.startsWith("+");
|
|
28221
|
+
const name = reserved ? expression.slice(1) : expression;
|
|
28222
|
+
const value = values[name];
|
|
28223
|
+
if (value === void 0 || value === null || value === "") throw new CapletsError("REQUEST_INVALID", `Missing required path parameter ${name}`, { tool: operation.name });
|
|
28224
|
+
const serialized = serializeGoogleDiscoveryValue("path", name, value);
|
|
28225
|
+
return reserved ? encodeReservedPathValue(serialized) : encodeURIComponent(serialized);
|
|
28226
|
+
});
|
|
28227
|
+
}
|
|
28228
|
+
function encodeReservedPathValue(value) {
|
|
28229
|
+
return value.split("/").map((segment) => encodeURIComponent(segment)).join("/");
|
|
28230
|
+
}
|
|
28231
|
+
function assertSafeRelativePath(path) {
|
|
28232
|
+
for (const segment of path.split("/")) {
|
|
28233
|
+
const decoded = safeDecodePathSegment(segment);
|
|
28234
|
+
if (decoded === "." || decoded === "..") throw new CapletsError("CONFIG_INVALID", "Google Discovery operation path cannot escape baseUrl");
|
|
28235
|
+
}
|
|
28236
|
+
}
|
|
28237
|
+
function assertInsideBasePath(url, basePath) {
|
|
28238
|
+
const normalizedBase = basePath === "" ? "/" : `${basePath}/`;
|
|
28239
|
+
if (normalizedBase === "/") return;
|
|
28240
|
+
const pathname = url.pathname.endsWith("/") ? url.pathname : `${url.pathname}/`;
|
|
28241
|
+
if (pathname !== normalizedBase && !pathname.startsWith(normalizedBase)) throw new CapletsError("CONFIG_INVALID", "Google Discovery operation path cannot escape baseUrl");
|
|
28242
|
+
}
|
|
28243
|
+
function safeDecodePathSegment(segment) {
|
|
28244
|
+
try {
|
|
28245
|
+
return decodeURIComponent(segment);
|
|
28246
|
+
} catch {
|
|
28247
|
+
return segment;
|
|
28248
|
+
}
|
|
28249
|
+
}
|
|
28250
|
+
function serializeGoogleDiscoveryValue(location, name, value) {
|
|
28251
|
+
switch (typeof value) {
|
|
28252
|
+
case "string":
|
|
28253
|
+
case "number":
|
|
28254
|
+
case "boolean": return String(value);
|
|
28255
|
+
default: throw new CapletsError("REQUEST_INVALID", `Google Discovery ${location} parameter ${name} must be a string, number, or boolean`);
|
|
28256
|
+
}
|
|
28257
|
+
}
|
|
28258
|
+
function asRecord$2(value) {
|
|
28259
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
28260
|
+
}
|
|
28261
|
+
//#endregion
|
|
28262
|
+
//#region src/media/artifacts.ts
|
|
28263
|
+
function artifactUri(capletId, callId, filename) {
|
|
28264
|
+
return `caplets://artifacts/${encodeURIComponent(capletId)}/${encodeURIComponent(callId)}/${encodeURIComponent(filename)}`;
|
|
28265
|
+
}
|
|
28266
|
+
async function writeMediaArtifact(input) {
|
|
28267
|
+
const rootDir = resolve(input.rootDir ?? DEFAULT_ARTIFACT_DIR);
|
|
28268
|
+
const capletId = requiredSafePathSegment(input.capletId, "capletId");
|
|
28269
|
+
const callId = safePathSegment(input.callId ?? defaultCallId(), "call");
|
|
28270
|
+
const filename = safeFilename(input.suggestedFilename ?? (input.outputPath ? basename(input.outputPath) : "response.bin"));
|
|
28271
|
+
const target = input.outputPath ? assertInsideRoot(rootDir, input.outputPath) : assertInsideRoot(rootDir, resolve(rootDir, capletId, callId, filename));
|
|
28272
|
+
rejectSymlinkPathComponents(rootDir, target, true);
|
|
28273
|
+
const uriParts = input.outputPath ? uriPartsForOutputPath(rootDir, target) : {
|
|
28274
|
+
capletId,
|
|
28275
|
+
callId,
|
|
28276
|
+
filename: safeFilename(basename(target))
|
|
28277
|
+
};
|
|
28278
|
+
const bytes = Buffer.from(input.bytes);
|
|
28279
|
+
mkdirSync(dirname(target), {
|
|
28280
|
+
recursive: true,
|
|
28281
|
+
mode: 448
|
|
28282
|
+
});
|
|
28283
|
+
writeFileSync(target, bytes, { mode: 384 });
|
|
28284
|
+
chmodSync(target, 384);
|
|
28285
|
+
const artifactFilename = uriParts.filename;
|
|
28286
|
+
writeArtifactMetadata(target, input.mimeType ? { mimeType: input.mimeType } : {});
|
|
28287
|
+
return {
|
|
28288
|
+
uri: artifactUri(uriParts.capletId, uriParts.callId, artifactFilename),
|
|
28289
|
+
...input.exposeLocalPath === false ? {} : { path: target },
|
|
28290
|
+
filename: artifactFilename,
|
|
28291
|
+
...input.mimeType ? { mimeType: input.mimeType } : {},
|
|
28292
|
+
byteLength: bytes.byteLength,
|
|
28293
|
+
sha256: sha256(bytes)
|
|
28294
|
+
};
|
|
28295
|
+
}
|
|
28296
|
+
function resolveMediaArtifact(uri, options = {}) {
|
|
28297
|
+
const parsed = parseArtifactUri(uri);
|
|
28298
|
+
const rootDir = resolve(options.artifactRoot ?? DEFAULT_ARTIFACT_DIR);
|
|
28299
|
+
const path = assertInsideRoot(rootDir, resolve(rootDir, parsed.capletId, parsed.callId, parsed.filename));
|
|
28300
|
+
rejectSymlinkPathComponents(rootDir, path, true);
|
|
28301
|
+
if (!existsSync(path)) throw new CapletsError("REQUEST_INVALID", "Media artifact was not found");
|
|
28302
|
+
const stat = statSync(path);
|
|
28303
|
+
if (!stat.isFile()) throw new CapletsError("REQUEST_INVALID", "Media artifact must resolve to a file");
|
|
28304
|
+
if (options.maxBytes !== void 0 && stat.size > options.maxBytes) throw new CapletsError("REQUEST_INVALID", `media exceeds byte limit ${options.maxBytes}`);
|
|
28305
|
+
const bytes = readFileSync(path);
|
|
28306
|
+
const metadata = readArtifactMetadata(path);
|
|
28307
|
+
return {
|
|
28308
|
+
uri,
|
|
28309
|
+
path,
|
|
28310
|
+
filename: parsed.filename,
|
|
28311
|
+
...metadata?.mimeType ? { mimeType: metadata.mimeType } : {},
|
|
28312
|
+
byteLength: bytes.byteLength,
|
|
28313
|
+
sha256: sha256(bytes)
|
|
28314
|
+
};
|
|
28315
|
+
}
|
|
28316
|
+
function parseArtifactUri(uri) {
|
|
28317
|
+
let url;
|
|
28318
|
+
try {
|
|
28319
|
+
url = new URL(uri);
|
|
28320
|
+
} catch {
|
|
28321
|
+
throw new CapletsError("REQUEST_INVALID", "Media artifact URI is invalid");
|
|
28322
|
+
}
|
|
28323
|
+
if (url.protocol !== "caplets:" || url.hostname !== "artifacts") throw new CapletsError("REQUEST_INVALID", "Media artifact URI must start with caplets://artifacts/");
|
|
28324
|
+
const parts = url.pathname.split("/").filter(Boolean);
|
|
28325
|
+
if (parts.length !== 3) throw new CapletsError("REQUEST_INVALID", "Media artifact URI is missing required parts");
|
|
28326
|
+
return {
|
|
28327
|
+
capletId: decodeSafePathSegment(parts[0], "capletId"),
|
|
28328
|
+
callId: decodeSafePathSegment(parts[1], "callId"),
|
|
28329
|
+
filename: decodeSafeFilename(parts[2])
|
|
28330
|
+
};
|
|
28331
|
+
}
|
|
28332
|
+
function decodeSafePathSegment(value, label) {
|
|
28333
|
+
const decoded = decodeArtifactUriPart(value);
|
|
28334
|
+
const safe = safePathSegment(decoded, "");
|
|
28335
|
+
if (!safe || safe !== decoded) throw new CapletsError("REQUEST_INVALID", `Media artifact URI ${label} is invalid`);
|
|
28336
|
+
return safe;
|
|
28337
|
+
}
|
|
28338
|
+
function decodeSafeFilename(value) {
|
|
28339
|
+
const decoded = decodeArtifactUriPart(value);
|
|
28340
|
+
const safe = safeFilename(decoded);
|
|
28341
|
+
if (safe !== decoded) throw new CapletsError("REQUEST_INVALID", "Media artifact URI filename is invalid");
|
|
28342
|
+
return safe;
|
|
28343
|
+
}
|
|
28344
|
+
function decodeArtifactUriPart(value) {
|
|
28345
|
+
try {
|
|
28346
|
+
return decodeURIComponent(value);
|
|
28347
|
+
} catch {
|
|
28348
|
+
throw new CapletsError("REQUEST_INVALID", "Media artifact URI contains invalid encoding");
|
|
28349
|
+
}
|
|
28350
|
+
}
|
|
28351
|
+
function assertInsideRoot(rootDir, candidate) {
|
|
28352
|
+
if (!isAbsolute(candidate)) throw new CapletsError("REQUEST_INVALID", "Media artifact outputPath must be absolute");
|
|
28353
|
+
const resolvedRoot = resolve(rootDir);
|
|
28354
|
+
const resolved = resolve(candidate);
|
|
28355
|
+
const rel = relative(resolvedRoot, resolved);
|
|
28356
|
+
if (rel.startsWith("..") || isAbsolute(rel)) throw new CapletsError("REQUEST_INVALID", "Media artifact outputPath must stay inside the artifact root");
|
|
28357
|
+
return resolved;
|
|
28358
|
+
}
|
|
28359
|
+
function rejectSymlinkPathComponents(rootDir, target, includeTarget) {
|
|
28360
|
+
const resolvedRoot = resolve(rootDir);
|
|
28361
|
+
rejectSymlinkRoot(resolvedRoot);
|
|
28362
|
+
const parts = relative(resolvedRoot, resolve(target)).split(/[\\/]+/u).filter(Boolean);
|
|
28363
|
+
let current = resolvedRoot;
|
|
28364
|
+
const limit = includeTarget ? parts.length : Math.max(0, parts.length - 1);
|
|
28365
|
+
for (let index = 0; index < limit; index += 1) {
|
|
28366
|
+
current = resolve(current, parts[index]);
|
|
28367
|
+
try {
|
|
28368
|
+
if (lstatSync(current).isSymbolicLink()) throw new CapletsError("REQUEST_INVALID", "Media artifact path must not contain symlinks");
|
|
28369
|
+
} catch (error) {
|
|
28370
|
+
if (error instanceof CapletsError) throw error;
|
|
28371
|
+
if (error.code === "ENOENT") return;
|
|
28372
|
+
throw error;
|
|
28373
|
+
}
|
|
28374
|
+
}
|
|
28375
|
+
}
|
|
28376
|
+
function rejectSymlinkRoot(rootDir) {
|
|
28377
|
+
try {
|
|
28378
|
+
if (lstatSync(rootDir).isSymbolicLink()) throw new CapletsError("REQUEST_INVALID", "Media artifact root must not be a symlink");
|
|
28379
|
+
} catch (error) {
|
|
28380
|
+
if (error instanceof CapletsError) throw error;
|
|
28381
|
+
if (error.code === "ENOENT") return;
|
|
28382
|
+
throw error;
|
|
28383
|
+
}
|
|
28384
|
+
}
|
|
28385
|
+
function uriPartsForOutputPath(rootDir, target) {
|
|
28386
|
+
const parts = relative(resolve(rootDir), target).split(/[\\/]+/u).filter(Boolean);
|
|
28387
|
+
if (parts.length !== 3) throw new CapletsError("REQUEST_INVALID", "Media artifact outputPath must be under <artifact-root>/<caplet-id>/<call-id>/<filename>");
|
|
28388
|
+
return {
|
|
28389
|
+
capletId: requireAlreadySafePathSegment(parts[0], "capletId"),
|
|
28390
|
+
callId: requireAlreadySafePathSegment(parts[1], "callId"),
|
|
28391
|
+
filename: requireAlreadySafeFilename(parts[2])
|
|
28392
|
+
};
|
|
28393
|
+
}
|
|
28394
|
+
function requiredSafePathSegment(value, label) {
|
|
28395
|
+
const safe = safePathSegment(value, "");
|
|
28396
|
+
if (!safe) throw new CapletsError("REQUEST_INVALID", `Media artifact ${label} is required`);
|
|
28397
|
+
return safe;
|
|
28398
|
+
}
|
|
28399
|
+
function requireAlreadySafePathSegment(value, label) {
|
|
28400
|
+
const safe = requiredSafePathSegment(value, label);
|
|
28401
|
+
if (safe !== value) throw new CapletsError("REQUEST_INVALID", `Media artifact outputPath ${label} is invalid`);
|
|
28402
|
+
return safe;
|
|
28403
|
+
}
|
|
28404
|
+
function requireAlreadySafeFilename(value) {
|
|
28405
|
+
const safe = safeFilename(value);
|
|
28406
|
+
if (safe !== value) throw new CapletsError("REQUEST_INVALID", "Media artifact outputPath filename is invalid");
|
|
28407
|
+
return safe;
|
|
28408
|
+
}
|
|
28409
|
+
function safePathSegment(value, fallback) {
|
|
28410
|
+
return safeFilename(value, fallback);
|
|
28411
|
+
}
|
|
28412
|
+
function safeFilename(value, fallback = "response.bin") {
|
|
28413
|
+
const name = basename(value).trim().replace(/[^\w.-]+/gu, "_");
|
|
28414
|
+
return name && name !== "." && name !== ".." ? name : fallback;
|
|
28415
|
+
}
|
|
28416
|
+
function defaultCallId() {
|
|
28417
|
+
return `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/gu, "-")}-${randomUUID()}`;
|
|
28418
|
+
}
|
|
28419
|
+
function sha256(bytes) {
|
|
28420
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
28421
|
+
}
|
|
28422
|
+
function artifactMetadataPath(path) {
|
|
28423
|
+
return `${path}.caplets.json`;
|
|
28424
|
+
}
|
|
28425
|
+
function writeArtifactMetadata(path, metadata) {
|
|
28426
|
+
const metadataPath = artifactMetadataPath(path);
|
|
28427
|
+
if (!metadata.mimeType) {
|
|
28428
|
+
rmSync(metadataPath, { force: true });
|
|
28429
|
+
return;
|
|
28430
|
+
}
|
|
28431
|
+
writeFileSync(metadataPath, `${JSON.stringify(metadata)}\n`, { mode: 384 });
|
|
28432
|
+
chmodSync(metadataPath, 384);
|
|
28433
|
+
}
|
|
28434
|
+
function readArtifactMetadata(path) {
|
|
28435
|
+
const metadataPath = artifactMetadataPath(path);
|
|
28436
|
+
if (!existsSync(metadataPath)) return void 0;
|
|
28437
|
+
let parsed;
|
|
28438
|
+
try {
|
|
28439
|
+
parsed = JSON.parse(readFileSync(metadataPath, "utf8"));
|
|
28440
|
+
} catch {
|
|
28441
|
+
throw new CapletsError("REQUEST_INVALID", "Media artifact metadata is invalid");
|
|
28442
|
+
}
|
|
28443
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new CapletsError("REQUEST_INVALID", "Media artifact metadata is invalid");
|
|
28444
|
+
const value = parsed;
|
|
28445
|
+
return typeof value.mimeType === "string" && value.mimeType ? { mimeType: value.mimeType } : {};
|
|
28446
|
+
}
|
|
28447
|
+
//#endregion
|
|
28448
|
+
//#region src/media/input.ts
|
|
28449
|
+
const DEFAULT_MAX_MEDIA_BYTES = 100 * 1024 * 1024;
|
|
28450
|
+
async function readMediaInput(input, options = {}) {
|
|
28451
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) throw new CapletsError("REQUEST_INVALID", "media must be an object");
|
|
28452
|
+
const media = input;
|
|
28453
|
+
if ([
|
|
28454
|
+
"path",
|
|
28455
|
+
"artifact",
|
|
28456
|
+
"dataUrl"
|
|
28457
|
+
].filter((key) => typeof media[key] === "string").length !== 1) throw new CapletsError("REQUEST_INVALID", "media must define exactly one of path, artifact, or dataUrl");
|
|
28458
|
+
const filename = typeof media.filename === "string" ? media.filename : void 0;
|
|
28459
|
+
const mimeType = typeof media.mimeType === "string" ? media.mimeType : void 0;
|
|
28460
|
+
if (typeof media.path === "string") {
|
|
28461
|
+
if (options.allowLocalPaths === false) throw new CapletsError("REQUEST_INVALID", "media.path is not available in this runtime");
|
|
28462
|
+
enforceSize(statMediaFile(media.path).size, options.maxBytes);
|
|
28463
|
+
const resolvedFilename = filename ?? basename(media.path);
|
|
28464
|
+
const resolvedMimeType = mimeType ?? mimeTypeFromFilename(resolvedFilename);
|
|
28465
|
+
return {
|
|
28466
|
+
bytes: readMediaFile(media.path),
|
|
28467
|
+
filename: resolvedFilename,
|
|
28468
|
+
...resolvedMimeType ? { mimeType: resolvedMimeType } : {}
|
|
28469
|
+
};
|
|
28470
|
+
}
|
|
28471
|
+
if (typeof media.artifact === "string") {
|
|
28472
|
+
const artifactOptions = {};
|
|
28473
|
+
if (options.artifactRoot !== void 0) artifactOptions.artifactRoot = options.artifactRoot;
|
|
28474
|
+
artifactOptions.maxBytes = options.maxBytes ?? DEFAULT_MAX_MEDIA_BYTES;
|
|
28475
|
+
const artifact = resolveMediaArtifact(media.artifact, artifactOptions);
|
|
28476
|
+
if (!artifact.path) throw new CapletsError("REQUEST_INVALID", "Media artifact cannot be read from this runtime");
|
|
28477
|
+
const resolvedMimeType = mimeType ?? artifact.mimeType;
|
|
28478
|
+
return {
|
|
28479
|
+
bytes: readMediaFile(artifact.path),
|
|
28480
|
+
filename: filename ?? artifact.filename,
|
|
28481
|
+
...resolvedMimeType ? { mimeType: resolvedMimeType } : {}
|
|
28482
|
+
};
|
|
28483
|
+
}
|
|
28484
|
+
const dataUrlOptions = {};
|
|
28485
|
+
if (filename !== void 0) dataUrlOptions.filename = filename;
|
|
28486
|
+
if (mimeType !== void 0) dataUrlOptions.mimeType = mimeType;
|
|
28487
|
+
if (options.maxBytes !== void 0) dataUrlOptions.maxBytes = options.maxBytes;
|
|
28488
|
+
return readDataUrl(media.dataUrl, dataUrlOptions);
|
|
28489
|
+
}
|
|
28490
|
+
function mimeTypeFromFilename(filename) {
|
|
28491
|
+
switch (extname(filename).toLowerCase()) {
|
|
28492
|
+
case ".csv": return "text/csv";
|
|
28493
|
+
case ".gif": return "image/gif";
|
|
28494
|
+
case ".htm":
|
|
28495
|
+
case ".html": return "text/html";
|
|
28496
|
+
case ".jpeg":
|
|
28497
|
+
case ".jpg": return "image/jpeg";
|
|
28498
|
+
case ".json": return "application/json";
|
|
28499
|
+
case ".pdf": return "application/pdf";
|
|
28500
|
+
case ".png": return "image/png";
|
|
28501
|
+
case ".txt": return "text/plain";
|
|
28502
|
+
case ".webp": return "image/webp";
|
|
28503
|
+
case ".xml": return "application/xml";
|
|
28504
|
+
default: return;
|
|
28505
|
+
}
|
|
28506
|
+
}
|
|
28507
|
+
function readDataUrl(dataUrl, options) {
|
|
28508
|
+
const match = /^data:([^;,]+)(?:;[^,;=]+=[^,;]*)*;base64,([A-Za-z0-9+/=]*)$/u.exec(dataUrl);
|
|
28509
|
+
if (!match) throw new CapletsError("REQUEST_INVALID", "media.dataUrl must be a base64 data URL");
|
|
28510
|
+
const dataMimeType = match[1] ?? "";
|
|
28511
|
+
const base64 = match[2] ?? "";
|
|
28512
|
+
if (!dataMimeType || !isStrictBase64(base64)) throw new CapletsError("REQUEST_INVALID", "media.dataUrl must be a base64 data URL");
|
|
28513
|
+
enforceSize(decodedBase64Length(base64), options.maxBytes);
|
|
28514
|
+
return {
|
|
28515
|
+
bytes: Buffer.from(base64, "base64"),
|
|
28516
|
+
filename: options.filename ?? "media.bin",
|
|
28517
|
+
mimeType: options.mimeType ?? dataMimeType
|
|
28518
|
+
};
|
|
28519
|
+
}
|
|
28520
|
+
function isStrictBase64(value) {
|
|
28521
|
+
return value.length % 4 === 0 && /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test(value);
|
|
28522
|
+
}
|
|
28523
|
+
function decodedBase64Length(value) {
|
|
28524
|
+
const padding = value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0;
|
|
28525
|
+
return value.length / 4 * 3 - padding;
|
|
28526
|
+
}
|
|
28527
|
+
function statMediaFile(path) {
|
|
28528
|
+
try {
|
|
28529
|
+
const stat = statSync(path);
|
|
28530
|
+
if (!stat.isFile()) throw new CapletsError("REQUEST_INVALID", "media.path must reference a file");
|
|
28531
|
+
return stat;
|
|
28532
|
+
} catch (error) {
|
|
28533
|
+
if (error instanceof CapletsError) throw error;
|
|
28534
|
+
throw new CapletsError("REQUEST_INVALID", "media.path could not be read");
|
|
28535
|
+
}
|
|
28536
|
+
}
|
|
28537
|
+
function readMediaFile(path) {
|
|
28538
|
+
try {
|
|
28539
|
+
return readFileSync(path);
|
|
28540
|
+
} catch {
|
|
28541
|
+
throw new CapletsError("REQUEST_INVALID", "media file could not be read");
|
|
28542
|
+
}
|
|
28543
|
+
}
|
|
28544
|
+
function enforceSize(size, maxBytes = DEFAULT_MAX_MEDIA_BYTES) {
|
|
28545
|
+
if (size > maxBytes) throw new CapletsError("REQUEST_INVALID", `media exceeds byte limit ${maxBytes}`);
|
|
28546
|
+
}
|
|
28547
|
+
function parseHttpBody(contentType, text) {
|
|
28548
|
+
if (!text) return;
|
|
28549
|
+
const mime = contentType.split(";")[0]?.toLowerCase().trim() ?? "";
|
|
28550
|
+
if (mime !== "application/json" && !mime.endsWith("+json") && !mime.endsWith("/json")) return text;
|
|
28551
|
+
try {
|
|
28552
|
+
return JSON.parse(text);
|
|
28553
|
+
} catch {
|
|
28554
|
+
return text;
|
|
28555
|
+
}
|
|
28556
|
+
}
|
|
28557
|
+
async function readLimitedText(response, options) {
|
|
28558
|
+
if (!response.body) return "";
|
|
28559
|
+
const reader = response.body.getReader();
|
|
28560
|
+
const chunks = [];
|
|
28561
|
+
let bytes = 0;
|
|
28562
|
+
while (true) {
|
|
28563
|
+
const { done, value } = await reader.read();
|
|
28564
|
+
if (done) break;
|
|
28565
|
+
if (value) {
|
|
28566
|
+
bytes += value.byteLength;
|
|
28567
|
+
if (bytes > (options.maxBytes ?? 1048576)) {
|
|
28568
|
+
await reader.cancel();
|
|
28569
|
+
throw new CapletsError("DOWNSTREAM_PROTOCOL_ERROR", options.errorMessage);
|
|
28570
|
+
}
|
|
28571
|
+
chunks.push(value);
|
|
28572
|
+
}
|
|
28573
|
+
}
|
|
28574
|
+
return new TextDecoder().decode(Buffer.concat(chunks));
|
|
28575
|
+
}
|
|
28576
|
+
function isAbortError(error) {
|
|
28577
|
+
return error instanceof DOMException && error.name === "AbortError";
|
|
28578
|
+
}
|
|
28579
|
+
//#endregion
|
|
28580
|
+
//#region src/http/response.ts
|
|
28581
|
+
async function readHttpLikeResponse(response, options) {
|
|
28582
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
28583
|
+
const mimeType = mimeFromContentType(contentType);
|
|
28584
|
+
const maxInlineBytes = options.maxInlineBytes ?? 1048576;
|
|
28585
|
+
const maxBytes = options.maxBytes ?? 1048576;
|
|
28586
|
+
const method = options.method?.toUpperCase();
|
|
28587
|
+
await rejectOversizedContentLength(response, maxBytes, method);
|
|
28588
|
+
if (method === "HEAD") return responseEnvelope(response, contentType);
|
|
28589
|
+
if (!options.forceArtifact && shouldInline(response, mimeType)) {
|
|
28590
|
+
const inline = await readInlineCandidate(response, {
|
|
28591
|
+
maxInlineBytes,
|
|
28592
|
+
maxBytes
|
|
28593
|
+
});
|
|
28594
|
+
if (!inline.exceeded) return responseEnvelope(response, contentType, parseHttpBody(contentType, new TextDecoder().decode(inline.bytes)));
|
|
28595
|
+
return responseEnvelope(response, contentType, { artifact: await writeResponseArtifact(response, options, mimeType, inline.bytes) });
|
|
28596
|
+
}
|
|
28597
|
+
return responseEnvelope(response, contentType, { artifact: await writeResponseArtifact(response, options, mimeType, await readBoundedBytes(response, maxBytes)) });
|
|
28598
|
+
}
|
|
28599
|
+
async function readInlineCandidate(response, options) {
|
|
28600
|
+
if (!response.body) return {
|
|
28601
|
+
bytes: Buffer.alloc(0),
|
|
28602
|
+
exceeded: false
|
|
28603
|
+
};
|
|
28604
|
+
const reader = response.body.getReader();
|
|
28605
|
+
const chunks = [];
|
|
28606
|
+
let bytes = 0;
|
|
28607
|
+
let exceeded = false;
|
|
28608
|
+
while (true) {
|
|
28609
|
+
const { done, value } = await reader.read();
|
|
28610
|
+
if (done) break;
|
|
28611
|
+
if (value) {
|
|
28612
|
+
bytes += value.byteLength;
|
|
28613
|
+
if (bytes > options.maxBytes) {
|
|
28614
|
+
await reader.cancel();
|
|
28615
|
+
throw responseExceededLimit(options.maxBytes);
|
|
28616
|
+
}
|
|
28617
|
+
if (bytes > options.maxInlineBytes) exceeded = true;
|
|
28618
|
+
chunks.push(value);
|
|
28619
|
+
}
|
|
28620
|
+
}
|
|
28621
|
+
return {
|
|
28622
|
+
bytes: Buffer.concat(chunks),
|
|
28623
|
+
exceeded
|
|
28624
|
+
};
|
|
28625
|
+
}
|
|
28626
|
+
async function readBoundedBytes(response, maxBytes) {
|
|
28627
|
+
if (!response.body) return Buffer.alloc(0);
|
|
28628
|
+
const reader = response.body.getReader();
|
|
28629
|
+
const chunks = [];
|
|
28630
|
+
let bytes = 0;
|
|
28631
|
+
while (true) {
|
|
28632
|
+
const { done, value } = await reader.read();
|
|
28633
|
+
if (done) break;
|
|
28634
|
+
if (value) {
|
|
28635
|
+
bytes += value.byteLength;
|
|
28636
|
+
if (bytes > maxBytes) {
|
|
28637
|
+
await reader.cancel();
|
|
28638
|
+
throw responseExceededLimit(maxBytes);
|
|
28639
|
+
}
|
|
28640
|
+
chunks.push(value);
|
|
28641
|
+
}
|
|
28642
|
+
}
|
|
28643
|
+
return Buffer.concat(chunks);
|
|
28644
|
+
}
|
|
28645
|
+
async function rejectOversizedContentLength(response, maxBytes, method) {
|
|
28646
|
+
if (method === "HEAD") return;
|
|
28647
|
+
const contentLength = response.headers.get("content-length");
|
|
28648
|
+
if (!contentLength) return;
|
|
28649
|
+
const byteLength = Number.parseInt(contentLength, 10);
|
|
28650
|
+
if (Number.isFinite(byteLength) && byteLength > maxBytes) {
|
|
28651
|
+
await response.body?.cancel().catch(() => {});
|
|
28652
|
+
throw responseExceededLimit(maxBytes);
|
|
28653
|
+
}
|
|
28654
|
+
}
|
|
28655
|
+
function responseExceededLimit(maxBytes) {
|
|
28656
|
+
return new CapletsError("DOWNSTREAM_PROTOCOL_ERROR", `HTTP response exceeded byte limit ${maxBytes}`);
|
|
28657
|
+
}
|
|
28658
|
+
async function writeResponseArtifact(response, options, mimeType, bytes) {
|
|
28659
|
+
return await writeMediaArtifact({
|
|
28660
|
+
capletId: options.capletId,
|
|
28661
|
+
...options.artifactDir ? { rootDir: options.artifactDir } : {},
|
|
28662
|
+
...response.ok && options.outputPath ? { outputPath: options.outputPath } : {},
|
|
28663
|
+
...options.exposeLocalPath === false ? { exposeLocalPath: false } : {},
|
|
28664
|
+
suggestedFilename: options.filename ?? filenameFromContentDisposition(response) ?? "response.bin",
|
|
28665
|
+
...mimeType ? { mimeType } : {},
|
|
28666
|
+
bytes
|
|
28667
|
+
});
|
|
28668
|
+
}
|
|
28669
|
+
function responseEnvelope(response, contentType, body) {
|
|
28670
|
+
return {
|
|
28671
|
+
status: response.status,
|
|
28672
|
+
statusText: response.statusText,
|
|
28673
|
+
headers: { "content-type": contentType },
|
|
28674
|
+
...body === void 0 ? {} : { body }
|
|
28675
|
+
};
|
|
28676
|
+
}
|
|
28677
|
+
function shouldInline(response, mimeType) {
|
|
28678
|
+
if (isAttachment(response)) return false;
|
|
28679
|
+
return mimeType === "" || mimeType === "application/json" || mimeType.endsWith("+json") || mimeType.endsWith("/json") || mimeType.startsWith("text/");
|
|
28680
|
+
}
|
|
28681
|
+
function isAttachment(response) {
|
|
28682
|
+
return /\battachment\b/iu.test(response.headers.get("content-disposition") ?? "");
|
|
28683
|
+
}
|
|
28684
|
+
function mimeFromContentType(contentType) {
|
|
28685
|
+
return contentType.split(";")[0]?.toLowerCase().trim() ?? "";
|
|
28686
|
+
}
|
|
28687
|
+
function filenameFromContentDisposition(response) {
|
|
28688
|
+
const contentDisposition = response.headers.get("content-disposition");
|
|
28689
|
+
if (!contentDisposition) return;
|
|
28690
|
+
return parseRfc5987Filename(contentDisposition) ?? parseQuotedFilename(contentDisposition);
|
|
28691
|
+
}
|
|
28692
|
+
function parseRfc5987Filename(contentDisposition) {
|
|
28693
|
+
const value = /(?:^|;)\s*filename\*=([^;]+)/iu.exec(contentDisposition)?.[1]?.trim();
|
|
28694
|
+
if (!value) return;
|
|
28695
|
+
const encoded = value.replace(/^UTF-8''/iu, "");
|
|
28696
|
+
try {
|
|
28697
|
+
return decodeURIComponent(encoded.replace(/^"|"$/gu, ""));
|
|
28698
|
+
} catch {
|
|
28699
|
+
return encoded;
|
|
28700
|
+
}
|
|
28701
|
+
}
|
|
28702
|
+
function parseQuotedFilename(contentDisposition) {
|
|
28703
|
+
const quoted = /(?:^|;)\s*filename="([^"]+)"/iu.exec(contentDisposition)?.[1];
|
|
28704
|
+
if (quoted) return quoted;
|
|
28705
|
+
return /(?:^|;)\s*filename=([^;]+)/iu.exec(contentDisposition)?.[1]?.trim();
|
|
28706
|
+
}
|
|
28707
|
+
//#endregion
|
|
28708
|
+
//#region src/google-discovery/manager.ts
|
|
28709
|
+
const DEFAULT_RESUMABLE_THRESHOLD_BYTES = 8 * 1024 * 1024;
|
|
28710
|
+
const DEFAULT_MEDIA_RESPONSE_MAX_BYTES = 100 * 1024 * 1024;
|
|
28711
|
+
const DEFAULT_DISCOVERY_DOCUMENT_MAX_BYTES = 20 * 1024 * 1024;
|
|
28712
|
+
var GoogleDiscoveryManager = class {
|
|
28713
|
+
registry;
|
|
28714
|
+
options;
|
|
28715
|
+
cache = /* @__PURE__ */ new Map();
|
|
28716
|
+
constructor(registry, options = {}) {
|
|
28717
|
+
this.registry = registry;
|
|
28718
|
+
this.options = options;
|
|
28719
|
+
}
|
|
28720
|
+
updateRegistry(registry) {
|
|
28721
|
+
this.registry = registry;
|
|
28722
|
+
}
|
|
28723
|
+
invalidate(serverId) {
|
|
28724
|
+
this.cache.delete(serverId);
|
|
28725
|
+
}
|
|
28726
|
+
async checkApi(api) {
|
|
28727
|
+
const startedAt = Date.now();
|
|
28728
|
+
try {
|
|
28729
|
+
const operations = await this.refreshOperations(api, true);
|
|
28730
|
+
this.registry.setStatus(api.server, "available");
|
|
28731
|
+
return {
|
|
28732
|
+
id: api.server,
|
|
28733
|
+
status: "available",
|
|
28734
|
+
toolCount: operations.length,
|
|
28735
|
+
elapsedMs: Date.now() - startedAt
|
|
28736
|
+
};
|
|
28737
|
+
} catch (error) {
|
|
28738
|
+
const safe = toSafeError(error, "SERVER_UNAVAILABLE");
|
|
28739
|
+
this.registry.setStatus(api.server, "unavailable", safe);
|
|
28740
|
+
return {
|
|
28741
|
+
id: api.server,
|
|
28742
|
+
status: "unavailable",
|
|
28743
|
+
elapsedMs: Date.now() - startedAt,
|
|
28744
|
+
error: safe
|
|
28745
|
+
};
|
|
28746
|
+
}
|
|
28747
|
+
}
|
|
28748
|
+
async listTools(api) {
|
|
28749
|
+
return (await this.refreshOperations(api, false)).map((operation) => this.toTool(operation));
|
|
28750
|
+
}
|
|
28751
|
+
async getTool(api, toolName) {
|
|
28752
|
+
return this.toTool(await this.getOperation(api, toolName));
|
|
28753
|
+
}
|
|
28754
|
+
async callTool(api, toolName, args) {
|
|
28755
|
+
const operation = await this.getOperation(api, toolName);
|
|
28756
|
+
const requestApi = await this.resolveRequestApi(api);
|
|
28757
|
+
if (operation.supportsMediaUpload && "media" in args) return this.callMediaUpload(requestApi, operation, args);
|
|
28758
|
+
const url = buildGoogleDiscoveryUrl(requestApi, operation, shouldRequestMediaDownload(operation, args) ? withMediaDownloadQuery(args) : args);
|
|
28759
|
+
const init = buildJsonRequestInit(operation, args, new Headers(await authHeaders$2(requestApi, this.options.authDir, operation.scopes)));
|
|
28760
|
+
const controller = new AbortController();
|
|
28761
|
+
const timeout = setTimeout(() => controller.abort(), requestApi.requestTimeoutMs);
|
|
28762
|
+
try {
|
|
28763
|
+
const response = await fetch(url, {
|
|
28764
|
+
...init,
|
|
28765
|
+
signal: controller.signal
|
|
28766
|
+
});
|
|
28767
|
+
if (response.status >= 300 && response.status < 400) throw new CapletsError("DOWNSTREAM_PROTOCOL_ERROR", "Google Discovery request returned a redirect", {
|
|
28768
|
+
server: requestApi.server,
|
|
28769
|
+
status: response.status,
|
|
28770
|
+
location: response.headers.get("location") ? "[REDACTED]" : void 0
|
|
28771
|
+
});
|
|
28772
|
+
if (response.status === 401 || response.status === 403) throw googleAuthError(requestApi, response);
|
|
28773
|
+
const parsed = await readHttpLikeResponse(response, {
|
|
28774
|
+
capletId: requestApi.server,
|
|
28775
|
+
method: operation.method,
|
|
28776
|
+
...this.options.artifactDir ? { artifactDir: this.options.artifactDir } : {},
|
|
28777
|
+
...this.options.exposeLocalArtifactPaths === false ? { exposeLocalPath: false } : {},
|
|
28778
|
+
...typeof args.filename === "string" ? { filename: args.filename } : {},
|
|
28779
|
+
...typeof args.outputPath === "string" ? { outputPath: args.outputPath } : {},
|
|
28780
|
+
maxBytes: DEFAULT_MEDIA_RESPONSE_MAX_BYTES,
|
|
28781
|
+
...operation.supportsMediaDownload && (typeof args.filename === "string" || typeof args.outputPath === "string") ? { forceArtifact: true } : {}
|
|
28782
|
+
});
|
|
28783
|
+
return {
|
|
28784
|
+
content: markdownStructuredContent(parsed, {
|
|
28785
|
+
title: `${requestApi.name} call_tool ${toolName}`,
|
|
28786
|
+
backend: "googleDiscovery",
|
|
28787
|
+
operation: "call_tool",
|
|
28788
|
+
tool: toolName
|
|
28789
|
+
}),
|
|
28790
|
+
structuredContent: parsed,
|
|
28791
|
+
isError: !response.ok
|
|
28792
|
+
};
|
|
28793
|
+
} catch (error) {
|
|
28794
|
+
if (isAbortError(error)) throw new CapletsError("TOOL_CALL_TIMEOUT", `Google Discovery request timed out for ${requestApi.server}/${toolName}`);
|
|
28795
|
+
if (error instanceof CapletsError) throw error;
|
|
28796
|
+
throw new CapletsError("DOWNSTREAM_TOOL_ERROR", `Google Discovery request failed for ${requestApi.server}/${toolName}`, toSafeError(error));
|
|
28797
|
+
} finally {
|
|
28798
|
+
clearTimeout(timeout);
|
|
28799
|
+
}
|
|
28800
|
+
}
|
|
28801
|
+
async callMediaUpload(api, operation, args) {
|
|
28802
|
+
const mediaOptions = {};
|
|
28803
|
+
if (this.options.artifactDir) mediaOptions.artifactRoot = this.options.artifactDir;
|
|
28804
|
+
if (this.options.exposeLocalArtifactPaths === false) mediaOptions.allowLocalPaths = false;
|
|
28805
|
+
const media = await readMediaInput(args.media, mediaOptions);
|
|
28806
|
+
const headers = new Headers(await authHeaders$2(api, this.options.authDir, operation.scopes));
|
|
28807
|
+
const protocol = selectUploadProtocol(operation, media, args);
|
|
28808
|
+
const controller = new AbortController();
|
|
28809
|
+
const timeout = setTimeout(() => controller.abort(), api.requestTimeoutMs);
|
|
28810
|
+
try {
|
|
28811
|
+
const response = protocol === "resumable" ? await this.callResumableUpload(api, operation, args, media, headers, controller.signal) : await this.callSingleUpload(api, operation, args, media, headers, protocol, controller.signal);
|
|
28812
|
+
const parsed = await readHttpLikeResponse(response, {
|
|
28813
|
+
capletId: api.server,
|
|
28814
|
+
method: operation.method,
|
|
28815
|
+
...this.options.artifactDir ? { artifactDir: this.options.artifactDir } : {},
|
|
28816
|
+
...this.options.exposeLocalArtifactPaths === false ? { exposeLocalPath: false } : {}
|
|
28817
|
+
});
|
|
28818
|
+
return {
|
|
28819
|
+
content: markdownStructuredContent(parsed, {
|
|
28820
|
+
title: `${api.name} call_tool ${operation.name}`,
|
|
28821
|
+
backend: "googleDiscovery",
|
|
28822
|
+
operation: "call_tool",
|
|
28823
|
+
tool: operation.name
|
|
28824
|
+
}),
|
|
28825
|
+
structuredContent: parsed,
|
|
28826
|
+
isError: !response.ok
|
|
28827
|
+
};
|
|
28828
|
+
} catch (error) {
|
|
28829
|
+
if (isAbortError(error)) throw new CapletsError("TOOL_CALL_TIMEOUT", `Google Discovery request timed out for ${api.server}/${operation.name}`);
|
|
28830
|
+
if (error instanceof CapletsError) throw error;
|
|
28831
|
+
throw new CapletsError("DOWNSTREAM_TOOL_ERROR", `Google Discovery request failed for ${api.server}/${operation.name}`, toSafeError(error));
|
|
28832
|
+
} finally {
|
|
28833
|
+
clearTimeout(timeout);
|
|
28834
|
+
}
|
|
28835
|
+
}
|
|
28836
|
+
async callSingleUpload(api, operation, args, media, headers, protocol, signal) {
|
|
28837
|
+
const upload = operation.mediaUploadProtocols[protocol];
|
|
28838
|
+
if (!upload?.path) throw new CapletsError("CONFIG_INVALID", `Google Discovery ${protocol} upload path is missing`);
|
|
28839
|
+
return fetchGoogleRequest(api, operation, buildGoogleDiscoveryUploadUrl(api, operation, upload.path, protocol === "simple" ? "media" : "multipart", args), protocol === "simple" ? simpleUploadInit(operation, media, headers) : multipartUploadInit(operation, args.body, media, headers), { signal });
|
|
28840
|
+
}
|
|
28841
|
+
async callResumableUpload(api, operation, args, media, headers, signal) {
|
|
28842
|
+
const upload = operation.mediaUploadProtocols.resumable;
|
|
28843
|
+
if (!upload?.path) throw new CapletsError("CONFIG_INVALID", "Google Discovery resumable upload path is missing");
|
|
28844
|
+
const contentType = safeMediaContentType(media.mimeType);
|
|
28845
|
+
const startUrl = buildGoogleDiscoveryUploadUrl(api, operation, upload.path, "resumable", args);
|
|
28846
|
+
headers.set("content-type", "application/json; charset=UTF-8");
|
|
28847
|
+
headers.set("x-upload-content-type", contentType);
|
|
28848
|
+
headers.set("x-upload-content-length", String(media.bytes.byteLength));
|
|
28849
|
+
const started = await fetchGoogleRequest(api, operation, startUrl, {
|
|
28850
|
+
method: operation.method.toUpperCase(),
|
|
28851
|
+
headers,
|
|
28852
|
+
body: JSON.stringify(args.body ?? {}),
|
|
28853
|
+
redirect: "manual"
|
|
28854
|
+
}, { signal });
|
|
28855
|
+
if (!started.ok) return started;
|
|
28856
|
+
const location = started.headers.get("location");
|
|
28857
|
+
if (!location) throw new CapletsError("DOWNSTREAM_PROTOCOL_ERROR", "Google resumable upload missing Location");
|
|
28858
|
+
const uploadUrl = new URL(location);
|
|
28859
|
+
const uploadHeaders = new Headers();
|
|
28860
|
+
uploadHeaders.set("content-type", contentType);
|
|
28861
|
+
uploadHeaders.set("content-length", String(media.bytes.byteLength));
|
|
28862
|
+
uploadHeaders.set("content-range", resumableContentRange(media.bytes.byteLength));
|
|
28863
|
+
copySessionAuthorization(api, startUrl, uploadUrl, headers, uploadHeaders);
|
|
28864
|
+
return fetchGoogleRequest(api, operation, uploadUrl, {
|
|
28865
|
+
method: "PUT",
|
|
28866
|
+
headers: uploadHeaders,
|
|
28867
|
+
body: media.bytes,
|
|
28868
|
+
redirect: "manual"
|
|
28869
|
+
}, { signal });
|
|
28870
|
+
}
|
|
28871
|
+
compact(_api, tool) {
|
|
28872
|
+
return {
|
|
28873
|
+
name: tool.name,
|
|
28874
|
+
...tool.description ? { description: tool.description } : {},
|
|
28875
|
+
hasInputSchema: Boolean(tool.inputSchema),
|
|
28876
|
+
hasOutputSchema: Boolean(tool.outputSchema),
|
|
28877
|
+
supportsFields: Boolean(tool.outputSchema),
|
|
28878
|
+
...compactToolSelectionHints(tool),
|
|
28879
|
+
...compactToolSchemaHints(tool),
|
|
28880
|
+
...compactToolSafetyHints(tool)
|
|
28881
|
+
};
|
|
28882
|
+
}
|
|
28883
|
+
search(api, tools, query, limit) {
|
|
28884
|
+
return searchToolList(tools, query, limit, (tool) => this.compact(api, tool));
|
|
28885
|
+
}
|
|
28886
|
+
async resolveAuthScopes(api) {
|
|
28887
|
+
return googleDiscoveryScopesForOperations(await this.refreshOperations(api, false));
|
|
28888
|
+
}
|
|
28889
|
+
async getOperation(api, toolName) {
|
|
28890
|
+
const operations = await this.refreshOperations(api, false);
|
|
28891
|
+
const operation = operations.find((candidate) => candidate.name === toolName);
|
|
28892
|
+
if (!operation) throw new CapletsError("TOOL_NOT_FOUND", `Tool ${toolName} was not found on ${api.server}`, {
|
|
28893
|
+
server: api.server,
|
|
28894
|
+
tool: toolName,
|
|
28895
|
+
suggestions: operations.map((candidate) => candidate.name).filter((name) => name.toLocaleLowerCase().includes(toolName.toLocaleLowerCase()[0] ?? "")).slice(0, 5)
|
|
28896
|
+
});
|
|
28897
|
+
return operation;
|
|
28898
|
+
}
|
|
28899
|
+
async refreshOperations(api, force) {
|
|
28900
|
+
const cached = this.cache.get(api.server);
|
|
28901
|
+
const cacheKey = googleDiscoveryCacheKey(api);
|
|
28902
|
+
const now = Date.now();
|
|
28903
|
+
const isFresh = cached?.operations && cached.cacheKey === cacheKey && cached.fetchedAt !== void 0 && api.operationCacheTtlMs > 0 && now - cached.fetchedAt <= api.operationCacheTtlMs;
|
|
28904
|
+
if (!force && isFresh) return cached.operations ?? [];
|
|
28905
|
+
try {
|
|
28906
|
+
const document = await loadGoogleDiscoveryDocument(api, this.options.authDir);
|
|
28907
|
+
const baseUrl = googleDiscoveryBaseUrl(api, document);
|
|
28908
|
+
const operations = discoveryOperations({
|
|
28909
|
+
server: api.server,
|
|
28910
|
+
document,
|
|
28911
|
+
...api.includeOperations ? { includeOperations: api.includeOperations } : {},
|
|
28912
|
+
...api.excludeOperations ? { excludeOperations: api.excludeOperations } : {}
|
|
28913
|
+
});
|
|
28914
|
+
this.cache.set(api.server, {
|
|
28915
|
+
operations,
|
|
28916
|
+
...baseUrl ? { baseUrl } : {},
|
|
28917
|
+
fetchedAt: Date.now(),
|
|
28918
|
+
cacheKey
|
|
28919
|
+
});
|
|
28920
|
+
this.registry.setStatus(api.server, "available");
|
|
28921
|
+
return operations;
|
|
28922
|
+
} catch (error) {
|
|
28923
|
+
const safe = toSafeError(error, "DOWNSTREAM_PROTOCOL_ERROR");
|
|
28924
|
+
this.registry.setStatus(api.server, "unavailable", safe);
|
|
28925
|
+
throw new CapletsError(safe.code, `Could not load Google Discovery operations for ${api.server}`, safe);
|
|
28926
|
+
}
|
|
28927
|
+
}
|
|
28928
|
+
toTool(operation) {
|
|
28929
|
+
return {
|
|
28930
|
+
name: operation.name,
|
|
28931
|
+
...operation.description ? { description: operation.description } : {},
|
|
28932
|
+
inputSchema: operation.inputSchema,
|
|
28933
|
+
...operation.outputSchema ? { outputSchema: operation.outputSchema } : {},
|
|
28934
|
+
annotations: {
|
|
28935
|
+
readOnlyHint: operation.readOnlyHint,
|
|
28936
|
+
destructiveHint: operation.destructiveHint
|
|
28937
|
+
}
|
|
28938
|
+
};
|
|
28939
|
+
}
|
|
28940
|
+
async resolveBaseUrl(api) {
|
|
28941
|
+
await this.refreshOperations(api, false);
|
|
28942
|
+
return this.cache.get(api.server)?.baseUrl;
|
|
28943
|
+
}
|
|
28944
|
+
async resolveRequestApi(api) {
|
|
28945
|
+
if (api.baseUrl) return api;
|
|
28946
|
+
const baseUrl = await this.resolveBaseUrl(api);
|
|
28947
|
+
if (!baseUrl) throw new CapletsError("CONFIG_INVALID", `${api.server} is missing Google Discovery baseUrl`);
|
|
28948
|
+
return {
|
|
28949
|
+
...api,
|
|
28950
|
+
baseUrl
|
|
28951
|
+
};
|
|
28952
|
+
}
|
|
28953
|
+
};
|
|
28954
|
+
function selectUploadProtocol(operation, media, args) {
|
|
28955
|
+
if (media.bytes.byteLength > DEFAULT_RESUMABLE_THRESHOLD_BYTES && operation.mediaUploadProtocols.resumable) return "resumable";
|
|
28956
|
+
if ("body" in args) {
|
|
28957
|
+
if (operation.mediaUploadProtocols.multipart) return "multipart";
|
|
28958
|
+
if (operation.mediaUploadProtocols.resumable) return "resumable";
|
|
28959
|
+
throw new CapletsError("CONFIG_INVALID", "Google Discovery media upload metadata requires multipart or resumable upload");
|
|
28960
|
+
}
|
|
28961
|
+
if (operation.mediaUploadProtocols.simple) return "simple";
|
|
28962
|
+
if (operation.mediaUploadProtocols.resumable) return "resumable";
|
|
28963
|
+
throw new CapletsError("CONFIG_INVALID", "Google Discovery media upload has no supported protocol");
|
|
28964
|
+
}
|
|
28965
|
+
function simpleUploadInit(operation, media, headers) {
|
|
28966
|
+
headers.set("content-type", safeMediaContentType(media.mimeType));
|
|
28967
|
+
headers.set("content-length", String(media.bytes.byteLength));
|
|
28968
|
+
return {
|
|
28969
|
+
method: operation.method.toUpperCase(),
|
|
28970
|
+
headers,
|
|
28971
|
+
body: media.bytes,
|
|
28972
|
+
redirect: "manual"
|
|
28973
|
+
};
|
|
28974
|
+
}
|
|
28975
|
+
function multipartUploadInit(operation, body, media, headers) {
|
|
28976
|
+
const boundary = `caplets_${randomUUID().replace(/-/gu, "")}`;
|
|
28977
|
+
const contentType = safeMediaContentType(media.mimeType);
|
|
28978
|
+
const payload = Buffer.concat([
|
|
28979
|
+
Buffer.from(`--${boundary}\r\ncontent-type: application/json; charset=UTF-8\r\n\r\n${JSON.stringify(body ?? {})}\r\n`),
|
|
28980
|
+
Buffer.from(`--${boundary}\r\ncontent-type: ${contentType}\r\n\r\n`),
|
|
28981
|
+
media.bytes,
|
|
28982
|
+
Buffer.from(`\r\n--${boundary}--\r\n`)
|
|
28983
|
+
]);
|
|
28984
|
+
headers.set("content-type", `multipart/related; boundary=${boundary}`);
|
|
28985
|
+
headers.set("content-length", String(payload.byteLength));
|
|
28986
|
+
return {
|
|
28987
|
+
method: operation.method.toUpperCase(),
|
|
28988
|
+
headers,
|
|
28989
|
+
body: payload,
|
|
28990
|
+
redirect: "manual"
|
|
28991
|
+
};
|
|
28992
|
+
}
|
|
28993
|
+
function resumableContentRange(byteLength) {
|
|
28994
|
+
return byteLength === 0 ? "bytes */0" : `bytes 0-${byteLength - 1}/${byteLength}`;
|
|
28995
|
+
}
|
|
28996
|
+
function safeMediaContentType(mimeType) {
|
|
28997
|
+
const contentType = mimeType ?? "application/octet-stream";
|
|
28998
|
+
if (/[\r\n]/u.test(contentType)) throw new CapletsError("REQUEST_INVALID", "media.mimeType must not contain line breaks");
|
|
28999
|
+
return contentType;
|
|
29000
|
+
}
|
|
29001
|
+
function googleDiscoveryBaseUrl(api, document) {
|
|
29002
|
+
if (api.baseUrl) return api.baseUrl;
|
|
29003
|
+
if (document.baseUrl) return document.baseUrl;
|
|
29004
|
+
if (document.rootUrl && document.servicePath) return new URL(document.servicePath, document.rootUrl).toString();
|
|
29005
|
+
}
|
|
29006
|
+
async function fetchGoogleRequest(api, operation, url, init, options = {}) {
|
|
29007
|
+
const controller = options.signal ? void 0 : new AbortController();
|
|
29008
|
+
const timeout = controller ? setTimeout(() => controller.abort(), api.requestTimeoutMs) : void 0;
|
|
29009
|
+
try {
|
|
29010
|
+
const response = await fetch(url, {
|
|
29011
|
+
...init,
|
|
29012
|
+
signal: options.signal ?? controller.signal
|
|
29013
|
+
});
|
|
29014
|
+
if (response.status >= 300 && response.status < 400) throw new CapletsError("DOWNSTREAM_PROTOCOL_ERROR", "Google Discovery request returned a redirect", {
|
|
29015
|
+
server: api.server,
|
|
29016
|
+
status: response.status,
|
|
29017
|
+
location: response.headers.get("location") ? "[REDACTED]" : void 0
|
|
29018
|
+
});
|
|
29019
|
+
if (response.status === 401 || response.status === 403) throw googleAuthError(api, response);
|
|
29020
|
+
return response;
|
|
29021
|
+
} catch (error) {
|
|
29022
|
+
if (isAbortError(error)) throw new CapletsError("TOOL_CALL_TIMEOUT", `Google Discovery request timed out for ${api.server}/${operation.name}`);
|
|
29023
|
+
throw error;
|
|
29024
|
+
} finally {
|
|
29025
|
+
if (timeout) clearTimeout(timeout);
|
|
29026
|
+
}
|
|
29027
|
+
}
|
|
29028
|
+
async function loadGoogleDiscoveryDocument(api, authDir) {
|
|
29029
|
+
const source = await loadGoogleDiscoverySource(api, authDir);
|
|
29030
|
+
let parsed;
|
|
29031
|
+
try {
|
|
29032
|
+
parsed = JSON.parse(source);
|
|
29033
|
+
} catch (error) {
|
|
29034
|
+
throw new CapletsError("DOWNSTREAM_PROTOCOL_ERROR", "Google Discovery document is not JSON", {
|
|
29035
|
+
server: api.server,
|
|
29036
|
+
error: error instanceof Error ? error.message : String(error)
|
|
29037
|
+
});
|
|
29038
|
+
}
|
|
29039
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new CapletsError("DOWNSTREAM_PROTOCOL_ERROR", "Google Discovery document is not an object");
|
|
29040
|
+
return parsed;
|
|
29041
|
+
}
|
|
29042
|
+
async function loadGoogleDiscoverySource(api, authDir) {
|
|
29043
|
+
if (api.discoveryPath) return readFile(api.discoveryPath, "utf8");
|
|
29044
|
+
if (!api.discoveryUrl) throw new CapletsError("CONFIG_INVALID", `${api.server} is missing Google Discovery document source`);
|
|
29045
|
+
return fetchDiscoverySource(api, await discoveryAuthHeaders(api, authDir));
|
|
29046
|
+
}
|
|
29047
|
+
async function fetchDiscoverySource(api, headers) {
|
|
29048
|
+
const controller = new AbortController();
|
|
29049
|
+
const timeout = setTimeout(() => controller.abort(), api.requestTimeoutMs);
|
|
29050
|
+
try {
|
|
29051
|
+
const response = await fetch(api.discoveryUrl, {
|
|
29052
|
+
headers,
|
|
29053
|
+
redirect: "manual",
|
|
29054
|
+
signal: controller.signal
|
|
29055
|
+
});
|
|
29056
|
+
if (response.status >= 300 && response.status < 400) throw new CapletsError("DOWNSTREAM_PROTOCOL_ERROR", "Google Discovery document request returned a redirect");
|
|
29057
|
+
if (!response.ok) throw new CapletsError("DOWNSTREAM_PROTOCOL_ERROR", "Google Discovery document request failed", { status: response.status });
|
|
29058
|
+
return await readLimitedText(response, {
|
|
29059
|
+
maxBytes: DEFAULT_DISCOVERY_DOCUMENT_MAX_BYTES,
|
|
29060
|
+
errorMessage: "Google Discovery document exceeded byte limit"
|
|
29061
|
+
});
|
|
29062
|
+
} catch (error) {
|
|
29063
|
+
if (isAbortError(error)) throw new CapletsError("TOOL_CALL_TIMEOUT", "Google Discovery document request timed out");
|
|
29064
|
+
throw error;
|
|
29065
|
+
} finally {
|
|
29066
|
+
clearTimeout(timeout);
|
|
29067
|
+
}
|
|
29068
|
+
}
|
|
29069
|
+
async function discoveryAuthHeaders(api, authDir) {
|
|
29070
|
+
if (api.auth.type === "none") return {};
|
|
29071
|
+
if (api.auth.type === "oauth2" || api.auth.type === "oidc") {
|
|
29072
|
+
const protectedOrigin = discoveryProtectedResourceOrigin(api, authDir);
|
|
29073
|
+
if (!protectedOrigin || !shouldSendDiscoveryAuth(api, protectedOrigin)) return {};
|
|
29074
|
+
const bundle = readTokenBundle(api.server, authDir);
|
|
29075
|
+
if (!bundle?.accessToken && !bundle?.refreshToken) return {};
|
|
29076
|
+
return genericOAuthHeaders({
|
|
29077
|
+
...api,
|
|
29078
|
+
baseUrl: protectedOrigin
|
|
29079
|
+
}, authDir);
|
|
29080
|
+
}
|
|
29081
|
+
if (!api.baseUrl || !shouldSendDiscoveryAuth(api, new URL(api.baseUrl).origin)) return {};
|
|
29082
|
+
return authHeaders$2(api, authDir);
|
|
29083
|
+
}
|
|
29084
|
+
async function authHeaders$2(api, authDir, resolvedScopes) {
|
|
29085
|
+
switch (api.auth.type) {
|
|
29086
|
+
case "none": return {};
|
|
29087
|
+
case "bearer": return { authorization: `Bearer ${api.auth.token}` };
|
|
29088
|
+
case "headers": return api.auth.headers;
|
|
29089
|
+
case "oauth2":
|
|
29090
|
+
case "oidc": return genericOAuthHeaders({
|
|
29091
|
+
...api,
|
|
29092
|
+
resolvedScopes
|
|
29093
|
+
}, authDir);
|
|
29094
|
+
}
|
|
29095
|
+
}
|
|
29096
|
+
function discoveryProtectedResourceOrigin(api, authDir) {
|
|
29097
|
+
if (api.baseUrl) return new URL(api.baseUrl).origin;
|
|
29098
|
+
return readTokenBundle(api.server, authDir)?.protectedResourceOrigin;
|
|
29099
|
+
}
|
|
29100
|
+
function copySessionAuthorization(api, startUrl, uploadUrl, source, target) {
|
|
29101
|
+
const authorization = source.get("authorization");
|
|
29102
|
+
if (!authorization || !isAllowedUploadSessionOrigin(api, startUrl, uploadUrl)) return;
|
|
29103
|
+
target.set("authorization", authorization);
|
|
29104
|
+
}
|
|
29105
|
+
function isAllowedUploadSessionOrigin(api, startUrl, uploadUrl) {
|
|
29106
|
+
if (uploadUrl.origin === startUrl.origin) return true;
|
|
29107
|
+
if (api.baseUrl && uploadUrl.origin === new URL(api.baseUrl).origin) return true;
|
|
29108
|
+
return isGoogleApiOrigin(uploadUrl);
|
|
29109
|
+
}
|
|
29110
|
+
function isGoogleApiOrigin(url) {
|
|
29111
|
+
return url.protocol === "https:" && url.hostname.endsWith(".googleapis.com");
|
|
29112
|
+
}
|
|
29113
|
+
function shouldRequestMediaDownload(operation, args) {
|
|
29114
|
+
return operation.supportsMediaDownload && (typeof args.filename === "string" || typeof args.outputPath === "string");
|
|
29115
|
+
}
|
|
29116
|
+
function withMediaDownloadQuery(args) {
|
|
29117
|
+
const query = args.query && typeof args.query === "object" && !Array.isArray(args.query) ? args.query : {};
|
|
29118
|
+
return {
|
|
29119
|
+
...args,
|
|
29120
|
+
query: {
|
|
29121
|
+
...query,
|
|
29122
|
+
alt: "media"
|
|
29123
|
+
}
|
|
29124
|
+
};
|
|
29125
|
+
}
|
|
29126
|
+
function googleAuthError(api, response) {
|
|
29127
|
+
return new CapletsError(response.status === 401 ? "AUTH_REQUIRED" : "AUTH_FAILED", "Google Discovery authentication failed", {
|
|
29128
|
+
server: api.server,
|
|
29129
|
+
status: response.status,
|
|
29130
|
+
message: response.statusText,
|
|
29131
|
+
authType: api.auth.type,
|
|
29132
|
+
challenge: response.headers.get("www-authenticate") ? "[REDACTED]" : void 0,
|
|
29133
|
+
...api.auth.type === "oauth2" || api.auth.type === "oidc" ? { nextAction: "run_caplets_auth_login" } : {}
|
|
29134
|
+
});
|
|
29135
|
+
}
|
|
29136
|
+
function shouldSendDiscoveryAuth(api, protectedResourceOrigin) {
|
|
29137
|
+
return Boolean(api.discoveryUrl && new URL(api.discoveryUrl).origin === protectedResourceOrigin);
|
|
29138
|
+
}
|
|
29139
|
+
function googleDiscoveryCacheKey(api) {
|
|
29140
|
+
return JSON.stringify({
|
|
29141
|
+
discoveryPath: api.discoveryPath,
|
|
29142
|
+
discoveryUrl: api.discoveryUrl,
|
|
29143
|
+
baseUrl: api.baseUrl,
|
|
29144
|
+
includeOperations: api.includeOperations,
|
|
29145
|
+
excludeOperations: api.excludeOperations
|
|
29146
|
+
});
|
|
29147
|
+
}
|
|
29148
|
+
//#endregion
|
|
27703
29149
|
//#region ../../node_modules/.pnpm/graphql@16.14.2/node_modules/graphql/version.js
|
|
27704
29150
|
var require_version = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
27705
29151
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -48092,7 +49538,7 @@ var require_utilities = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
48092
49538
|
var _resolveSchemaCoordinate = require_resolveSchemaCoordinate();
|
|
48093
49539
|
}));
|
|
48094
49540
|
//#endregion
|
|
48095
|
-
//#region src/
|
|
49541
|
+
//#region src/graphql.ts
|
|
48096
49542
|
var import_graphql = (/* @__PURE__ */ __commonJSMin(((exports) => {
|
|
48097
49543
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48098
49544
|
Object.defineProperty(exports, "BREAK", {
|
|
@@ -49394,40 +50840,6 @@ var import_graphql = (/* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
49394
50840
|
var _index5 = require_error();
|
|
49395
50841
|
var _index6 = require_utilities();
|
|
49396
50842
|
})))();
|
|
49397
|
-
function parseHttpBody(contentType, text) {
|
|
49398
|
-
if (!text) return;
|
|
49399
|
-
const mime = contentType.split(";")[0]?.toLowerCase().trim() ?? "";
|
|
49400
|
-
if (mime !== "application/json" && !mime.endsWith("+json") && !mime.endsWith("/json")) return text;
|
|
49401
|
-
try {
|
|
49402
|
-
return JSON.parse(text);
|
|
49403
|
-
} catch {
|
|
49404
|
-
return text;
|
|
49405
|
-
}
|
|
49406
|
-
}
|
|
49407
|
-
async function readLimitedText(response, options) {
|
|
49408
|
-
if (!response.body) return "";
|
|
49409
|
-
const reader = response.body.getReader();
|
|
49410
|
-
const chunks = [];
|
|
49411
|
-
let bytes = 0;
|
|
49412
|
-
while (true) {
|
|
49413
|
-
const { done, value } = await reader.read();
|
|
49414
|
-
if (done) break;
|
|
49415
|
-
if (value) {
|
|
49416
|
-
bytes += value.byteLength;
|
|
49417
|
-
if (bytes > (options.maxBytes ?? 1048576)) {
|
|
49418
|
-
await reader.cancel();
|
|
49419
|
-
throw new CapletsError("DOWNSTREAM_PROTOCOL_ERROR", options.errorMessage);
|
|
49420
|
-
}
|
|
49421
|
-
chunks.push(value);
|
|
49422
|
-
}
|
|
49423
|
-
}
|
|
49424
|
-
return new TextDecoder().decode(Buffer.concat(chunks));
|
|
49425
|
-
}
|
|
49426
|
-
function isAbortError(error) {
|
|
49427
|
-
return error instanceof DOMException && error.name === "AbortError";
|
|
49428
|
-
}
|
|
49429
|
-
//#endregion
|
|
49430
|
-
//#region src/graphql.ts
|
|
49431
50843
|
const GRAPHQL_METHOD = "POST";
|
|
49432
50844
|
const SCALAR_JSON_SCHEMA = {
|
|
49433
50845
|
String: { type: "string" },
|
|
@@ -49931,7 +51343,17 @@ var HttpActionManager = class {
|
|
|
49931
51343
|
status: response.status,
|
|
49932
51344
|
location: response.headers.get("location") ? "[REDACTED]" : void 0
|
|
49933
51345
|
});
|
|
49934
|
-
const parsed =
|
|
51346
|
+
const parsed = {
|
|
51347
|
+
...await readHttpLikeResponse(response, {
|
|
51348
|
+
capletId: api.server,
|
|
51349
|
+
method: operation.method,
|
|
51350
|
+
...this.options.artifactDir ? { artifactDir: this.options.artifactDir } : {},
|
|
51351
|
+
...this.options.exposeLocalArtifactPaths === false ? { exposeLocalPath: false } : {},
|
|
51352
|
+
maxInlineBytes: this.options.maxInlineBytes ?? api.maxResponseBytes,
|
|
51353
|
+
maxBytes: api.maxResponseBytes
|
|
51354
|
+
}),
|
|
51355
|
+
elapsedMs: Date.now() - startedAt
|
|
51356
|
+
};
|
|
49935
51357
|
return {
|
|
49936
51358
|
content: markdownStructuredContent(parsed, {
|
|
49937
51359
|
title: `${api.name} call_tool ${toolName}`,
|
|
@@ -50097,23 +51519,6 @@ async function authHeaders$1(api, authDir) {
|
|
|
50097
51519
|
}, authDir);
|
|
50098
51520
|
}
|
|
50099
51521
|
}
|
|
50100
|
-
async function readResponse$1(response, api, elapsedMs) {
|
|
50101
|
-
const contentType = response.headers.get("content-type") ?? "";
|
|
50102
|
-
const body = parseHttpBody(contentType, await readLimitedText(response, {
|
|
50103
|
-
maxBytes: maxResponseBytes(api),
|
|
50104
|
-
errorMessage: "HTTP action response exceeded byte limit"
|
|
50105
|
-
}));
|
|
50106
|
-
return {
|
|
50107
|
-
status: response.status,
|
|
50108
|
-
statusText: response.statusText,
|
|
50109
|
-
headers: { "content-type": contentType },
|
|
50110
|
-
...body === void 0 ? {} : { body },
|
|
50111
|
-
elapsedMs
|
|
50112
|
-
};
|
|
50113
|
-
}
|
|
50114
|
-
function maxResponseBytes(api) {
|
|
50115
|
-
return api.maxResponseBytes;
|
|
50116
|
-
}
|
|
50117
51522
|
function validateBaseUrl(api) {
|
|
50118
51523
|
if (!isAllowedRemoteUrl(api.baseUrl)) throw new CapletsError("CONFIG_INVALID", `${api.server} HTTP API baseUrl is not allowed`);
|
|
50119
51524
|
const url = new URL(api.baseUrl);
|
|
@@ -59853,6 +61258,7 @@ const HTTP_METHODS = [
|
|
|
59853
61258
|
"patch",
|
|
59854
61259
|
"trace"
|
|
59855
61260
|
];
|
|
61261
|
+
const DEFAULT_OPENAPI_RESPONSE_MAX_BYTES = 100 * 1024 * 1024;
|
|
59856
61262
|
const JSON_CONTENT_TYPES = ["application/json"];
|
|
59857
61263
|
const FORBIDDEN_ARGUMENT_HEADERS = new Set([
|
|
59858
61264
|
"accept",
|
|
@@ -59939,7 +61345,13 @@ var OpenApiManager = class {
|
|
|
59939
61345
|
challenge: response.headers.get("www-authenticate") ? "[REDACTED]" : void 0,
|
|
59940
61346
|
...endpoint.auth.type === "oauth2" || endpoint.auth.type === "oidc" ? { nextAction: "run_caplets_auth_login" } : {}
|
|
59941
61347
|
});
|
|
59942
|
-
const parsed = await
|
|
61348
|
+
const parsed = await readHttpLikeResponse(response, {
|
|
61349
|
+
capletId: endpoint.server,
|
|
61350
|
+
method: operation.method,
|
|
61351
|
+
...this.options.artifactDir ? { artifactDir: this.options.artifactDir } : {},
|
|
61352
|
+
...this.options.exposeLocalArtifactPaths === false ? { exposeLocalPath: false } : {},
|
|
61353
|
+
maxBytes: DEFAULT_OPENAPI_RESPONSE_MAX_BYTES
|
|
61354
|
+
});
|
|
59943
61355
|
return {
|
|
59944
61356
|
content: markdownStructuredContent(parsed, {
|
|
59945
61357
|
title: `${endpoint.name} call_tool ${toolName}`,
|
|
@@ -60268,16 +61680,6 @@ async function authHeaders(endpoint, authDir) {
|
|
|
60268
61680
|
function shouldSendSpecAuth(endpoint) {
|
|
60269
61681
|
return Boolean(endpoint.specUrl && endpoint.baseUrl && new URL(endpoint.specUrl).origin === new URL(endpoint.baseUrl).origin);
|
|
60270
61682
|
}
|
|
60271
|
-
async function readResponse(response) {
|
|
60272
|
-
const contentType = response.headers.get("content-type") ?? "";
|
|
60273
|
-
const body = parseHttpBody(contentType, await readLimitedText(response, { errorMessage: "OpenAPI response exceeded byte limit" }));
|
|
60274
|
-
return {
|
|
60275
|
-
status: response.status,
|
|
60276
|
-
statusText: response.statusText,
|
|
60277
|
-
headers: { "content-type": contentType },
|
|
60278
|
-
...body === void 0 ? {} : { body }
|
|
60279
|
-
};
|
|
60280
|
-
}
|
|
60281
61683
|
async function fetchWithLimit(url, timeoutMs, headers = {}) {
|
|
60282
61684
|
const controller = new AbortController();
|
|
60283
61685
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -60342,7 +61744,7 @@ var ServerRegistry = class {
|
|
|
60342
61744
|
return this.allCaplets().filter((server) => !server.disabled);
|
|
60343
61745
|
}
|
|
60344
61746
|
get(serverId) {
|
|
60345
|
-
const server = this.config.mcpServers[serverId] ?? this.config.openapiEndpoints[serverId] ?? this.config.graphqlEndpoints[serverId] ?? this.config.httpApis[serverId] ?? this.config.cliTools[serverId] ?? this.config.capletSets[serverId];
|
|
61747
|
+
const server = this.config.mcpServers[serverId] ?? this.config.openapiEndpoints[serverId] ?? this.config.googleDiscoveryApis?.[serverId] ?? this.config.graphqlEndpoints[serverId] ?? this.config.httpApis[serverId] ?? this.config.cliTools[serverId] ?? this.config.capletSets[serverId];
|
|
60346
61748
|
return server?.disabled ? void 0 : server;
|
|
60347
61749
|
}
|
|
60348
61750
|
require(serverId) {
|
|
@@ -60388,6 +61790,7 @@ var ServerRegistry = class {
|
|
|
60388
61790
|
return [
|
|
60389
61791
|
...Object.values(this.config.mcpServers),
|
|
60390
61792
|
...Object.values(this.config.openapiEndpoints),
|
|
61793
|
+
...Object.values(this.config.googleDiscoveryApis ?? {}),
|
|
60391
61794
|
...Object.values(this.config.graphqlEndpoints),
|
|
60392
61795
|
...Object.values(this.config.httpApis),
|
|
60393
61796
|
...Object.values(this.config.cliTools),
|
|
@@ -60403,6 +61806,13 @@ function backendDetail(server) {
|
|
|
60403
61806
|
operationCacheTtlMs: server.operationCacheTtlMs,
|
|
60404
61807
|
source: server.specPath ? "specPath" : "specUrl"
|
|
60405
61808
|
};
|
|
61809
|
+
if (server.backend === "googleDiscovery") return {
|
|
61810
|
+
type: "googleDiscovery",
|
|
61811
|
+
disabled: server.disabled,
|
|
61812
|
+
requestTimeoutMs: server.requestTimeoutMs,
|
|
61813
|
+
operationCacheTtlMs: server.operationCacheTtlMs,
|
|
61814
|
+
source: googleDiscoverySource(server)
|
|
61815
|
+
};
|
|
60406
61816
|
if (server.backend === "graphql") return {
|
|
60407
61817
|
type: "graphql",
|
|
60408
61818
|
disabled: server.disabled,
|
|
@@ -60439,6 +61849,9 @@ function backendDetail(server) {
|
|
|
60439
61849
|
toolCacheTtlMs: server.toolCacheTtlMs
|
|
60440
61850
|
};
|
|
60441
61851
|
}
|
|
61852
|
+
function googleDiscoverySource(server) {
|
|
61853
|
+
return server.discoveryPath ? "discoveryPath" : "discoveryUrl";
|
|
61854
|
+
}
|
|
60442
61855
|
function capletSetSource(server) {
|
|
60443
61856
|
return server.configPath && server.capletsRoot ? "both" : server.configPath ? "configPath" : "capletsRoot";
|
|
60444
61857
|
}
|
|
@@ -60560,14 +61973,14 @@ const ajv = new import_ajv.default({
|
|
|
60560
61973
|
});
|
|
60561
61974
|
const compiledValidators = /* @__PURE__ */ new WeakMap();
|
|
60562
61975
|
const MAX_SCHEMA_ERRORS = 8;
|
|
60563
|
-
async function handleServerTool(server, request, registry, downstream, openapi, graphql, http, cli, caplets, options = {}) {
|
|
61976
|
+
async function handleServerTool(server, request, registry, downstream, openapi, graphql, http, cli, caplets, options = {}, googleDiscovery) {
|
|
60564
61977
|
const startedAt = Date.now();
|
|
60565
61978
|
const parsed = validateOperationRequest(request, registry.config.options.maxSearchLimit, server.backend);
|
|
60566
61979
|
switch (parsed.operation) {
|
|
60567
61980
|
case "inspect": return jsonResult(registry.detail(server), metadataFor(server, "inspect", void 0, startedAt));
|
|
60568
|
-
case "check": return jsonResult(await backendFor(server, downstream, openapi, graphql, http, cli, caplets).check(server), metadataFor(server, "check", void 0, startedAt));
|
|
61981
|
+
case "check": return jsonResult(await backendFor(server, downstream, openapi, graphql, http, cli, caplets, googleDiscovery).check(server), metadataFor(server, "check", void 0, startedAt));
|
|
60569
61982
|
case "tools": {
|
|
60570
|
-
const backend = backendFor(server, downstream, openapi, graphql, http, cli, caplets);
|
|
61983
|
+
const backend = backendFor(server, downstream, openapi, graphql, http, cli, caplets, googleDiscovery);
|
|
60571
61984
|
const page = pageItems((await backend.listTools(server)).map((tool) => backend.compact(server, tool)), parsed, registry.config.options.maxSearchLimit);
|
|
60572
61985
|
return jsonResult({
|
|
60573
61986
|
id: server.server,
|
|
@@ -60576,7 +61989,7 @@ async function handleServerTool(server, request, registry, downstream, openapi,
|
|
|
60576
61989
|
}, metadataFor(server, "tools", void 0, startedAt));
|
|
60577
61990
|
}
|
|
60578
61991
|
case "search_tools": {
|
|
60579
|
-
const backend = backendFor(server, downstream, openapi, graphql, http, cli, caplets);
|
|
61992
|
+
const backend = backendFor(server, downstream, openapi, graphql, http, cli, caplets, googleDiscovery);
|
|
60580
61993
|
const tools = await backend.listTools(server);
|
|
60581
61994
|
const limit = parsed.limit ?? registry.config.options.defaultSearchLimit;
|
|
60582
61995
|
const page = pageItems(backend.search(server, tools, parsed.query, limit), parsed, registry.config.options.maxSearchLimit);
|
|
@@ -60588,7 +62001,7 @@ async function handleServerTool(server, request, registry, downstream, openapi,
|
|
|
60588
62001
|
}, metadataFor(server, "search_tools", void 0, startedAt));
|
|
60589
62002
|
}
|
|
60590
62003
|
case "describe_tool": {
|
|
60591
|
-
const tool = await backendFor(server, downstream, openapi, graphql, http, cli, caplets).getTool(server, parsed.name);
|
|
62004
|
+
const tool = await backendFor(server, downstream, openapi, graphql, http, cli, caplets, googleDiscovery).getTool(server, parsed.name);
|
|
60592
62005
|
const observedOutputShape = await readObservedOutputShape(options, server, parsed.name, tool.outputSchema);
|
|
60593
62006
|
return jsonResult({
|
|
60594
62007
|
id: server.server,
|
|
@@ -60598,7 +62011,7 @@ async function handleServerTool(server, request, registry, downstream, openapi,
|
|
|
60598
62011
|
}, metadataFor(server, "describe_tool", parsed.name, startedAt));
|
|
60599
62012
|
}
|
|
60600
62013
|
case "call_tool": {
|
|
60601
|
-
const backend = backendFor(server, downstream, openapi, graphql, http, cli, caplets);
|
|
62014
|
+
const backend = backendFor(server, downstream, openapi, graphql, http, cli, caplets, googleDiscovery);
|
|
60602
62015
|
const tool = await maybeGetToolForValidation(backend, server, parsed.name);
|
|
60603
62016
|
validateToolArgsForAgent(tool, parsed.name, parsed.args);
|
|
60604
62017
|
if (parsed.fields === void 0) {
|
|
@@ -61156,6 +62569,7 @@ function projectCallToolResult(result, outputSchema, fields, context = {}) {
|
|
|
61156
62569
|
if (result.isError === true) return result;
|
|
61157
62570
|
const structuredContent = result.structuredContent;
|
|
61158
62571
|
if (!isPlainObject$2(structuredContent)) throw new CapletsError("DOWNSTREAM_PROTOCOL_ERROR", "Field selection requires the downstream tool to return object structuredContent");
|
|
62572
|
+
if (hasArtifactPlaceholderForSelectedFields(structuredContent, fields)) throw new CapletsError("REQUEST_INVALID", "Field selection cannot project from an artifact response. Retry without fields and read the returned artifact.");
|
|
61159
62573
|
const projected = projectStructuredContent(structuredContent, outputSchema, fields);
|
|
61160
62574
|
return {
|
|
61161
62575
|
...result,
|
|
@@ -61163,10 +62577,19 @@ function projectCallToolResult(result, outputSchema, fields, context = {}) {
|
|
|
61163
62577
|
structuredContent: projected
|
|
61164
62578
|
};
|
|
61165
62579
|
}
|
|
62580
|
+
function hasArtifactPlaceholderForSelectedFields(structuredContent, fields) {
|
|
62581
|
+
const body = structuredContent.body;
|
|
62582
|
+
return isPlainObject$2(body) && isPlainObject$2(body.artifact) && isArtifactPlaceholder(body.artifact) && fields.some((field) => field === "body" || field.startsWith("body."));
|
|
62583
|
+
}
|
|
62584
|
+
function isArtifactPlaceholder(value) {
|
|
62585
|
+
return typeof value.uri === "string" && value.uri.startsWith("caplets://artifacts/") && typeof value.byteLength === "number" && typeof value.sha256 === "string";
|
|
62586
|
+
}
|
|
61166
62587
|
function extractArtifacts(result) {
|
|
61167
|
-
if (!isPlainObject$2(result)
|
|
62588
|
+
if (!isPlainObject$2(result)) return [];
|
|
61168
62589
|
const artifacts = [];
|
|
61169
62590
|
const seen = /* @__PURE__ */ new Set();
|
|
62591
|
+
addStructuredArtifact(artifacts, seen, result.structuredContent);
|
|
62592
|
+
if (!Array.isArray(result.content)) return artifacts;
|
|
61170
62593
|
for (const item of result.content) {
|
|
61171
62594
|
if (!isPlainObject$2(item) || item.type !== "text" || typeof item.text !== "string") continue;
|
|
61172
62595
|
const text = item.text;
|
|
@@ -61195,6 +62618,21 @@ function extractArtifacts(result) {
|
|
|
61195
62618
|
}
|
|
61196
62619
|
return artifacts;
|
|
61197
62620
|
}
|
|
62621
|
+
function addStructuredArtifact(artifacts, seen, structuredContent) {
|
|
62622
|
+
if (!isPlainObject$2(structuredContent)) return;
|
|
62623
|
+
const body = structuredContent.body;
|
|
62624
|
+
if (!isPlainObject$2(body) || !isPlainObject$2(body.artifact)) return;
|
|
62625
|
+
const path = typeof body.artifact.path === "string" ? body.artifact.path : void 0;
|
|
62626
|
+
const uri = typeof body.artifact.uri === "string" ? body.artifact.uri : void 0;
|
|
62627
|
+
const displayPath = path ?? uri;
|
|
62628
|
+
if (!displayPath || seen.has(displayPath)) return;
|
|
62629
|
+
seen.add(displayPath);
|
|
62630
|
+
artifacts.push({
|
|
62631
|
+
kind: "file",
|
|
62632
|
+
displayPath,
|
|
62633
|
+
pathResolution: path ? "absolute" : "relative-to-mcp-server"
|
|
62634
|
+
});
|
|
62635
|
+
}
|
|
61198
62636
|
function parseMarkdownLinks(text) {
|
|
61199
62637
|
const links = [];
|
|
61200
62638
|
let index = 0;
|
|
@@ -61286,7 +62724,7 @@ function artifactKindFromText(text) {
|
|
|
61286
62724
|
function isPlainObject$2(value) {
|
|
61287
62725
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
61288
62726
|
}
|
|
61289
|
-
function backendFor(server, downstream, openapi, graphql, http, cli, caplets) {
|
|
62727
|
+
function backendFor(server, downstream, openapi, graphql, http, cli, caplets, googleDiscovery) {
|
|
61290
62728
|
if (server.backend === "mcp") return {
|
|
61291
62729
|
check: (...args) => downstream.checkServer(...args),
|
|
61292
62730
|
listTools: (...args) => downstream.listTools(...args),
|
|
@@ -61295,6 +62733,17 @@ function backendFor(server, downstream, openapi, graphql, http, cli, caplets) {
|
|
|
61295
62733
|
compact: (...args) => downstream.compact(...args),
|
|
61296
62734
|
search: (...args) => downstream.search(...args)
|
|
61297
62735
|
};
|
|
62736
|
+
if (server.backend === "googleDiscovery") {
|
|
62737
|
+
if (!googleDiscovery) throw new CapletsError("INTERNAL_ERROR", "Google Discovery manager is not configured");
|
|
62738
|
+
return {
|
|
62739
|
+
check: (...args) => googleDiscovery.checkApi(...args),
|
|
62740
|
+
listTools: (...args) => googleDiscovery.listTools(...args),
|
|
62741
|
+
getTool: (...args) => googleDiscovery.getTool(...args),
|
|
62742
|
+
callTool: (...args) => googleDiscovery.callTool(...args),
|
|
62743
|
+
compact: (...args) => googleDiscovery.compact(...args),
|
|
62744
|
+
search: (...args) => googleDiscovery.search(...args)
|
|
62745
|
+
};
|
|
62746
|
+
}
|
|
61298
62747
|
if (server.backend === "graphql") {
|
|
61299
62748
|
if (!graphql) throw new CapletsError("INTERNAL_ERROR", "GraphQL manager is not configured");
|
|
61300
62749
|
return {
|
|
@@ -61419,7 +62868,7 @@ var CapletSetManager = class CapletSetManager {
|
|
|
61419
62868
|
suggestions: nearbyCapletNames(child.registry.enabledServers(), toolName)
|
|
61420
62869
|
});
|
|
61421
62870
|
try {
|
|
61422
|
-
return await handleServerTool(caplet, args, child.registry, child.downstream, child.openapi, child.graphql, child.http, child.cli, child.capletSets);
|
|
62871
|
+
return await handleServerTool(caplet, args, child.registry, child.downstream, child.openapi, child.graphql, child.http, child.cli, child.capletSets, {}, child.googleDiscovery);
|
|
61423
62872
|
} catch (error) {
|
|
61424
62873
|
return errorResult(error);
|
|
61425
62874
|
}
|
|
@@ -61468,17 +62917,22 @@ var CapletSetManager = class CapletSetManager {
|
|
|
61468
62917
|
defaultSearchLimit: config.defaultSearchLimit,
|
|
61469
62918
|
maxSearchLimit: config.maxSearchLimit
|
|
61470
62919
|
}));
|
|
61471
|
-
const
|
|
62920
|
+
const sharedOptions = {
|
|
62921
|
+
...this.options.authDir ? { authDir: this.options.authDir } : {},
|
|
62922
|
+
...this.options.artifactDir ? { artifactDir: this.options.artifactDir } : {},
|
|
62923
|
+
...this.options.exposeLocalArtifactPaths === false ? { exposeLocalArtifactPaths: false } : {}
|
|
62924
|
+
};
|
|
61472
62925
|
const childAncestry = new Set([...ancestry, cacheKey]);
|
|
61473
62926
|
child = {
|
|
61474
62927
|
registry,
|
|
61475
|
-
downstream: new DownstreamManager(registry,
|
|
61476
|
-
openapi: new OpenApiManager(registry,
|
|
61477
|
-
graphql: new GraphQLManager(registry,
|
|
61478
|
-
http: new HttpActionManager(registry,
|
|
62928
|
+
downstream: new DownstreamManager(registry, sharedOptions),
|
|
62929
|
+
openapi: new OpenApiManager(registry, sharedOptions),
|
|
62930
|
+
graphql: new GraphQLManager(registry, sharedOptions),
|
|
62931
|
+
http: new HttpActionManager(registry, sharedOptions),
|
|
61479
62932
|
cli: new CliToolsManager(registry),
|
|
62933
|
+
googleDiscovery: new GoogleDiscoveryManager(registry, sharedOptions),
|
|
61480
62934
|
capletSets: new CapletSetManager(registry, {
|
|
61481
|
-
...
|
|
62935
|
+
...sharedOptions,
|
|
61482
62936
|
ancestry: childAncestry
|
|
61483
62937
|
}),
|
|
61484
62938
|
cacheKey,
|
|
@@ -61777,6 +63231,7 @@ var CapletsEngine = class {
|
|
|
61777
63231
|
registry;
|
|
61778
63232
|
downstream;
|
|
61779
63233
|
openapi;
|
|
63234
|
+
googleDiscovery;
|
|
61780
63235
|
graphql;
|
|
61781
63236
|
http;
|
|
61782
63237
|
cli;
|
|
@@ -61806,11 +63261,12 @@ var CapletsEngine = class {
|
|
|
61806
63261
|
const config = this.configLoader(this.paths.configPath, this.paths.projectConfigPath);
|
|
61807
63262
|
this.registry = new ServerRegistry(config);
|
|
61808
63263
|
this.downstream = new DownstreamManager(this.registry, selectAuthOptions(options.authDir));
|
|
61809
|
-
this.openapi = new OpenApiManager(this.registry,
|
|
63264
|
+
this.openapi = new OpenApiManager(this.registry, selectHttpLikeOptions(options));
|
|
63265
|
+
this.googleDiscovery = new GoogleDiscoveryManager(this.registry, selectHttpLikeOptions(options));
|
|
61810
63266
|
this.graphql = new GraphQLManager(this.registry, selectAuthOptions(options.authDir));
|
|
61811
|
-
this.http = new HttpActionManager(this.registry,
|
|
63267
|
+
this.http = new HttpActionManager(this.registry, selectHttpLikeOptions(options));
|
|
61812
63268
|
this.cli = new CliToolsManager(this.registry);
|
|
61813
|
-
this.capletSets = new CapletSetManager(this.registry,
|
|
63269
|
+
this.capletSets = new CapletSetManager(this.registry, selectHttpLikeOptions(options));
|
|
61814
63270
|
this.watchDebounceMs = options.watchDebounceMs ?? 250;
|
|
61815
63271
|
this.watchEnabled = options.watch ?? true;
|
|
61816
63272
|
this.writeErr = options.writeErr ?? ((value) => process.stderr.write(value));
|
|
@@ -61874,7 +63330,7 @@ var CapletsEngine = class {
|
|
|
61874
63330
|
observedOutputShapeStore: this.observedOutputShapeStore,
|
|
61875
63331
|
observedOutputShapeScope: this.observedOutputShapeScope,
|
|
61876
63332
|
projectFingerprint: this.projectFingerprint
|
|
61877
|
-
});
|
|
63333
|
+
}, this.googleDiscovery);
|
|
61878
63334
|
} catch (error) {
|
|
61879
63335
|
return errorResult(error);
|
|
61880
63336
|
}
|
|
@@ -61906,7 +63362,7 @@ var CapletsEngine = class {
|
|
|
61906
63362
|
}
|
|
61907
63363
|
}
|
|
61908
63364
|
async completeCliWords(words) {
|
|
61909
|
-
const { completeCliWords } = await import("./completion-
|
|
63365
|
+
const { completeCliWords } = await import("./completion-BC4BNWo0.js").then((n) => n.r);
|
|
61910
63366
|
return await completeCliWords(words, {
|
|
61911
63367
|
config: this.registry.config,
|
|
61912
63368
|
managers: {
|
|
@@ -61957,16 +63413,16 @@ var CapletsEngine = class {
|
|
|
61957
63413
|
}
|
|
61958
63414
|
}
|
|
61959
63415
|
async listCompletionTools(server) {
|
|
61960
|
-
return (server.backend === "mcp" ? await this.downstream.listTools(server) : server.backend === "openapi" ? await this.openapi.listTools(server) : server.backend === "graphql" ? await this.graphql.listTools(server) : server.backend === "http" ? await this.http.listTools(server) : server.backend === "cli" ? await this.cli.listTools(server) : await this.capletSets.listTools(server)).map((tool) => ({
|
|
63416
|
+
return (server.backend === "mcp" ? await this.downstream.listTools(server) : server.backend === "openapi" ? await this.openapi.listTools(server) : server.backend === "googleDiscovery" ? await this.googleDiscovery.listTools(server) : server.backend === "graphql" ? await this.graphql.listTools(server) : server.backend === "http" ? await this.http.listTools(server) : server.backend === "cli" ? await this.cli.listTools(server) : await this.capletSets.listTools(server)).map((tool) => ({
|
|
61961
63417
|
name: tool.name,
|
|
61962
63418
|
...tool.description ? { description: tool.description } : {}
|
|
61963
63419
|
}));
|
|
61964
63420
|
}
|
|
61965
63421
|
async listTools(server) {
|
|
61966
|
-
return server.backend === "mcp" ? await this.downstream.listTools(server) : server.backend === "openapi" ? await this.openapi.listTools(server) : server.backend === "graphql" ? await this.graphql.listTools(server) : server.backend === "http" ? await this.http.listTools(server) : server.backend === "cli" ? await this.cli.listTools(server) : await this.capletSets.listTools(server);
|
|
63422
|
+
return server.backend === "mcp" ? await this.downstream.listTools(server) : server.backend === "openapi" ? await this.openapi.listTools(server) : server.backend === "googleDiscovery" ? await this.googleDiscovery.listTools(server) : server.backend === "graphql" ? await this.graphql.listTools(server) : server.backend === "http" ? await this.http.listTools(server) : server.backend === "cli" ? await this.cli.listTools(server) : await this.capletSets.listTools(server);
|
|
61967
63423
|
}
|
|
61968
63424
|
async callTool(server, toolName, args) {
|
|
61969
|
-
return server.backend === "mcp" ? await this.downstream.callTool(server, toolName, args) : server.backend === "openapi" ? await this.openapi.callTool(server, toolName, args) : server.backend === "graphql" ? await this.graphql.callTool(server, toolName, args) : server.backend === "http" ? await this.http.callTool(server, toolName, args) : server.backend === "cli" ? await this.cli.callTool(server, toolName, args) : await this.capletSets.callTool(server, toolName, args);
|
|
63425
|
+
return server.backend === "mcp" ? await this.downstream.callTool(server, toolName, args) : server.backend === "openapi" ? await this.openapi.callTool(server, toolName, args) : server.backend === "googleDiscovery" ? await this.googleDiscovery.callTool(server, toolName, args) : server.backend === "graphql" ? await this.graphql.callTool(server, toolName, args) : server.backend === "http" ? await this.http.callTool(server, toolName, args) : server.backend === "cli" ? await this.cli.callTool(server, toolName, args) : await this.capletSets.callTool(server, toolName, args);
|
|
61970
63426
|
}
|
|
61971
63427
|
async optionalMcpList(caplet, list) {
|
|
61972
63428
|
try {
|
|
@@ -61992,6 +63448,7 @@ var CapletsEngine = class {
|
|
|
61992
63448
|
this.registry = nextRegistry;
|
|
61993
63449
|
this.downstream.updateRegistry(nextRegistry);
|
|
61994
63450
|
this.openapi.updateRegistry(nextRegistry);
|
|
63451
|
+
this.googleDiscovery.updateRegistry(nextRegistry);
|
|
61995
63452
|
this.graphql.updateRegistry(nextRegistry);
|
|
61996
63453
|
this.http.updateRegistry(nextRegistry);
|
|
61997
63454
|
this.cli.updateRegistry(nextRegistry);
|
|
@@ -62045,6 +63502,7 @@ var CapletsEngine = class {
|
|
|
62045
63502
|
if (!(serializeCaplet(before) !== serializeCaplet(after))) continue;
|
|
62046
63503
|
if (before?.backend === "mcp") await this.downstream.closeServer(serverId);
|
|
62047
63504
|
if (before?.backend === "openapi" || after?.backend === "openapi" || !after) this.openapi.invalidate(serverId);
|
|
63505
|
+
if (before?.backend === "googleDiscovery" || after?.backend === "googleDiscovery" || !after) this.googleDiscovery.invalidate(serverId);
|
|
62048
63506
|
if (before?.backend === "graphql" || after?.backend === "graphql" || !after) this.graphql.invalidate(serverId);
|
|
62049
63507
|
if (before?.backend === "http" || after?.backend === "http" || !after) this.http.invalidate(serverId);
|
|
62050
63508
|
if (before?.backend === "cli" || after?.backend === "cli" || !after) this.cli.invalidate(serverId);
|
|
@@ -62104,6 +63562,13 @@ var CapletsEngine = class {
|
|
|
62104
63562
|
function selectAuthOptions(authDir) {
|
|
62105
63563
|
return authDir ? { authDir } : {};
|
|
62106
63564
|
}
|
|
63565
|
+
function selectHttpLikeOptions(options) {
|
|
63566
|
+
return {
|
|
63567
|
+
...selectAuthOptions(options.authDir),
|
|
63568
|
+
...options.artifactDir ? { artifactDir: options.artifactDir } : {},
|
|
63569
|
+
...options.exposeLocalArtifactPaths === false ? { exposeLocalArtifactPaths: false } : {}
|
|
63570
|
+
};
|
|
63571
|
+
}
|
|
62107
63572
|
function safeProjectFingerprint() {
|
|
62108
63573
|
try {
|
|
62109
63574
|
return fingerprintProjectRoot(findProjectRoot());
|
|
@@ -62146,6 +63611,7 @@ function allCaplets(config) {
|
|
|
62146
63611
|
return [
|
|
62147
63612
|
...Object.values(config.mcpServers),
|
|
62148
63613
|
...Object.values(config.openapiEndpoints),
|
|
63614
|
+
...Object.values(config.googleDiscoveryApis ?? {}),
|
|
62149
63615
|
...Object.values(config.graphqlEndpoints),
|
|
62150
63616
|
...Object.values(config.httpApis),
|
|
62151
63617
|
...Object.values(config.cliTools),
|
|
@@ -62243,7 +63709,7 @@ function capletHintText(caplet) {
|
|
|
62243
63709
|
].filter((value) => Boolean(value)).join(" ");
|
|
62244
63710
|
}
|
|
62245
63711
|
function minifyCodeModeDeclarationText(value) {
|
|
62246
|
-
return value.replace(/^\s*export\s*\{\s*\}\s*;?\s*/u, "").replace(/\r\n?/gu, "\n").split("\n").map((line) => line.trim()).filter(Boolean).join(" ").replace(/\s+/gu, " ").replace(/\s*([{}()[\]:;,|&=])\s*/gu, "$1").replace(/\s*<\s*/gu, "<").replace(/\s*>\s*/gu, ">").replace(/\?\s*:/gu, "?:").trim();
|
|
63712
|
+
return value.replace(/^\s*export\s*\{\s*\}\s*;?\s*/u, "").replace(/\s*export\s*\{\s*\}\s*;?\s*$/u, "").replace(/\r\n?/gu, "\n").split("\n").map((line) => line.trim()).filter(Boolean).join(" ").replace(/\s+/gu, " ").replace(/\s*([{}()[\]:;,|&=])\s*/gu, "$1").replace(/\s*<\s*/gu, "<").replace(/\s*>\s*/gu, ">").replace(/\?\s*:/gu, "?:").trim();
|
|
62247
63713
|
}
|
|
62248
63714
|
function codeModeDeclarationHash(declaration) {
|
|
62249
63715
|
return [
|
|
@@ -62869,6 +64335,9 @@ function isPlainObject$1(value) {
|
|
|
62869
64335
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
62870
64336
|
}
|
|
62871
64337
|
//#endregion
|
|
64338
|
+
//#region src/code-mode/diagnostics-builtins.generated.ts
|
|
64339
|
+
const CODE_MODE_DIAGNOSTICS_BUILTINS_DECLARATION = "type CodeModeBufferEncoding=\"utf8\"|\"utf-8\"|\"base64\"|\"base64url\"|\"hex\";interface CodeModeBuffer{readonly byteLength:number;readonly length:number;toString(encoding?:CodeModeBufferEncoding):string;toUint8Array():Uint8Array;}interface CodeModeBufferConstructor{from(input:string|ArrayLike<number>|ArrayBuffer|ArrayBufferView|CodeModeBuffer,encoding?:CodeModeBufferEncoding,):CodeModeBuffer;isBuffer(value:unknown):value is CodeModeBuffer;byteLength(input:string|ArrayLike<number>|ArrayBuffer|ArrayBufferView|CodeModeBuffer,encoding?:CodeModeBufferEncoding,):number;}declare const Buffer:CodeModeBufferConstructor;declare function atob(input:string):string;declare function btoa(input:string):string;type CodeModeURLSearchParamsInit=|string|Array<[string,string]>|Record<string,string>|URLSearchParams;declare class URLSearchParams{constructor(init?:CodeModeURLSearchParamsInit);append(name:string,value:string):void;delete(name:string):void;entries():IterableIterator<[string,string]>;forEach(callback:(value:string,key:string,parent:URLSearchParams)=>void):void;get(name:string):string|null;getAll(name:string):string[];has(name:string):boolean;keys():IterableIterator<string>;set(name:string,value:string):void;toString():string;values():IterableIterator<string>;[Symbol.iterator]():IterableIterator<[string,string]>;}declare class URL{constructor(input:string|URL,base?:string|URL);readonly hash:string;readonly host:string;readonly hostname:string;readonly href:string;readonly origin:string;readonly password:string;readonly pathname:string;readonly port:string;readonly protocol:string;readonly search:string;readonly searchParams:URLSearchParams;readonly username:string;toJSON():string;toString():string;}declare class TextEncoder{readonly encoding:\"utf-8\";encode(input?:string):Uint8Array;}declare class TextDecoder{readonly encoding:\"utf-8\";constructor(label?:string);decode(input?:ArrayBuffer|ArrayBufferView):string;}interface CodeModeCrypto{randomUUID():string;getRandomValues<T extends ArrayBufferView>(typedArray:T):T;}declare const crypto:CodeModeCrypto;declare function structuredClone<T>(value:T):T;type CodeModeHeadersInit=Headers|Record<string,string>|Array<[string,string]>;declare class Headers{constructor(init?:CodeModeHeadersInit);append(name:string,value:string):void;delete(name:string):void;entries():IterableIterator<[string,string]>;forEach(callback:(value:string,key:string,parent:Headers)=>void):void;get(name:string):string|null;has(name:string):boolean;keys():IterableIterator<string>;set(name:string,value:string):void;values():IterableIterator<string>;[Symbol.iterator]():IterableIterator<[string,string]>;}type CodeModeBlobPart=string|ArrayBuffer|ArrayBufferView|Blob;type CodeModeEndingType=\"transparent\"|\"native\";declare class Blob{constructor(parts?:CodeModeBlobPart[],options?:{type?:string;endings?:CodeModeEndingType},);readonly size:number;readonly type:string;arrayBuffer():Promise<ArrayBuffer>;slice(start?:number,end?:number,type?:string):Blob;text():Promise<string>;}declare class File extends Blob{constructor(parts:CodeModeBlobPart[],name:string,options?:{type?:string;lastModified?:number},);readonly lastModified:number;readonly name:string;}declare class FormData{constructor();append(name:string,value:string|Blob,filename?:string):void;delete(name:string):void;entries():IterableIterator<[string,string|File]>;get(name:string):string|File|null;getAll(name:string):Array<string|File>;has(name:string):boolean;keys():IterableIterator<string>;set(name:string,value:string|Blob,filename?:string):void;values():IterableIterator<string|File>;[Symbol.iterator]():IterableIterator<[string,string|File]>;}type CodeModeReadableStreamReadResult<T>=|{done:false;value:T}|{done:true;value?:undefined};interface ReadableStreamDefaultController<T>{enqueue(value:T):void;close():void;}interface ReadableStreamDefaultReader<T>{read():Promise<CodeModeReadableStreamReadResult<T>>;}declare class ReadableStream<T=unknown>{constructor(source?:{start?:(controller:ReadableStreamDefaultController<T>)=>void});getReader():ReadableStreamDefaultReader<T>;}interface WritableStreamDefaultWriter<T>{write(chunk:T):Promise<void>;close():Promise<void>;}declare class WritableStream<T=unknown>{constructor(sink?:{write?:(chunk:T)=>unknown;close?:()=>unknown});getWriter():WritableStreamDefaultWriter<T>;}interface TransformStreamDefaultController<T>{enqueue(value:T):void;}declare class TransformStream<I=unknown,O=unknown>{constructor(transformer?:{transform?:(chunk:I,controller:TransformStreamDefaultController<O>)=>void;flush?:(controller:ReadableStreamDefaultController<O>)=>void;});readonly readable:ReadableStream<O>;readonly writable:WritableStream<I>;}type CodeModeAbortListener=(event:{type:\"abort\";target:AbortSignal})=>void;declare class AbortSignal{readonly aborted:boolean;readonly reason:unknown;onabort:CodeModeAbortListener|null;addEventListener(type:\"abort\",listener:CodeModeAbortListener|null):void;removeEventListener(type:\"abort\",listener:CodeModeAbortListener|null):void;dispatchEvent(event:{type:\"abort\";target:AbortSignal}):boolean;throwIfAborted():void;static abort(reason?:unknown):AbortSignal;}declare class AbortController{readonly signal:AbortSignal;abort(reason?:unknown):void;}type CodeModeBodyInit=Blob|FormData|string|ArrayBuffer|ArrayBufferView|null|undefined;interface Body{readonly body:null;readonly bodyUsed:boolean;arrayBuffer():Promise<ArrayBuffer>;blob():Promise<Blob>;formData():Promise<FormData>;json():Promise<unknown>;text():Promise<string>;}declare class Request implements Body{constructor(input:string|URL|Request,init?:{method?:string;headers?:CodeModeHeadersInit;body?:CodeModeBodyInit;signal?:AbortSignal;},);readonly body:null;readonly bodyUsed:boolean;readonly headers:Headers;readonly method:string;readonly signal:AbortSignal;readonly url:string;arrayBuffer():Promise<ArrayBuffer>;blob():Promise<Blob>;clone():Request;formData():Promise<FormData>;json():Promise<unknown>;text():Promise<string>;}declare class Response implements Body{constructor(body?:CodeModeBodyInit,init?:{headers?:CodeModeHeadersInit;status?:number;statusText?:string},);readonly body:null;readonly bodyUsed:boolean;readonly headers:Headers;readonly ok:boolean;readonly redirected:false;readonly status:number;readonly statusText:string;readonly type:\"default\";readonly url:\"\";arrayBuffer():Promise<ArrayBuffer>;blob():Promise<Blob>;clone():Response;formData():Promise<FormData>;json():Promise<unknown>;text():Promise<string>;static json(data:unknown,init?:{headers?:CodeModeHeadersInit;status?:number;statusText?:string},):Response;}declare function queueMicrotask(callback:()=>void):void;type CodeModeTimerHandler=(...args:unknown[])=>void;declare function setTimeout(callback:CodeModeTimerHandler,delay?:number,...args:unknown[]):number;declare function clearTimeout(timerId:number):void;declare function setInterval(callback:CodeModeTimerHandler,delay?:number,...args:unknown[]):number;declare function clearInterval(timerId:number):void;";
|
|
64340
|
+
//#endregion
|
|
62872
64341
|
//#region src/code-mode/static-analysis.ts
|
|
62873
64342
|
var import_lib = (/* @__PURE__ */ __commonJSMin(((exports) => {
|
|
62874
64343
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -75461,7 +76930,7 @@ const PARSER_OPTIONS = {
|
|
|
75461
76930
|
]
|
|
75462
76931
|
};
|
|
75463
76932
|
function hasDirectFetchCall(code) {
|
|
75464
|
-
return hasMatchingAstNode(code,
|
|
76933
|
+
return hasMatchingAstNode(code, isDirectFetchCallNode);
|
|
75465
76934
|
}
|
|
75466
76935
|
function hasExecutableImport(code) {
|
|
75467
76936
|
return hasMatchingAstNode(code, isExecutableImportNode);
|
|
@@ -75470,9 +76939,9 @@ function hasMatchingAstNode(code, predicate) {
|
|
|
75470
76939
|
const ast = parseCode(code);
|
|
75471
76940
|
if (!ast) return false;
|
|
75472
76941
|
let found = false;
|
|
75473
|
-
visitAst(ast, (node) => {
|
|
76942
|
+
visitAst(ast, (node, parent) => {
|
|
75474
76943
|
if (found) return;
|
|
75475
|
-
found = predicate(node);
|
|
76944
|
+
found = predicate(node, parent);
|
|
75476
76945
|
});
|
|
75477
76946
|
return found;
|
|
75478
76947
|
}
|
|
@@ -75490,11 +76959,10 @@ function isExecutableImportNode(node) {
|
|
|
75490
76959
|
function isCallExpression(node) {
|
|
75491
76960
|
return (node.type === "CallExpression" || node.type === "OptionalCallExpression") && "callee" in node;
|
|
75492
76961
|
}
|
|
75493
|
-
function
|
|
75494
|
-
if (!isNode(
|
|
75495
|
-
if (
|
|
75496
|
-
|
|
75497
|
-
return false;
|
|
76962
|
+
function isDirectFetchCallNode(node) {
|
|
76963
|
+
if (!isCallExpression(node) || !isNode(node.callee)) return false;
|
|
76964
|
+
if (isIdentifierNamed(node.callee, "fetch")) return true;
|
|
76965
|
+
return (node.callee.type === "MemberExpression" || node.callee.type === "OptionalMemberExpression") && isGlobalFetchMember(node.callee);
|
|
75498
76966
|
}
|
|
75499
76967
|
function isGlobalFetchMember(node) {
|
|
75500
76968
|
if (!isIdentifierNamed(node.object, "globalThis", "window", "self")) return false;
|
|
@@ -75504,16 +76972,22 @@ function isGlobalFetchMember(node) {
|
|
|
75504
76972
|
function isExportDeclaration(node) {
|
|
75505
76973
|
return node.type === "ExportAllDeclaration" || node.type === "ExportDefaultDeclaration" || node.type === "ExportNamedDeclaration";
|
|
75506
76974
|
}
|
|
75507
|
-
function visitAst(value, visit) {
|
|
76975
|
+
function visitAst(value, visit, parent) {
|
|
75508
76976
|
if (!isNode(value)) return;
|
|
75509
|
-
visit(value);
|
|
76977
|
+
visit(value, parent);
|
|
75510
76978
|
for (const [key, child] of Object.entries(value)) {
|
|
75511
76979
|
if (key === "loc" || key === "start" || key === "end" || key === "extra") continue;
|
|
75512
76980
|
if (Array.isArray(child)) {
|
|
75513
|
-
for (const item of child) visitAst(item, visit
|
|
76981
|
+
for (const item of child) visitAst(item, visit, {
|
|
76982
|
+
node: value,
|
|
76983
|
+
key
|
|
76984
|
+
});
|
|
75514
76985
|
continue;
|
|
75515
76986
|
}
|
|
75516
|
-
visitAst(child, visit
|
|
76987
|
+
visitAst(child, visit, {
|
|
76988
|
+
node: value,
|
|
76989
|
+
key
|
|
76990
|
+
});
|
|
75517
76991
|
}
|
|
75518
76992
|
}
|
|
75519
76993
|
function isNode(value) {
|
|
@@ -75557,7 +77031,7 @@ function diagnoseCodeModeTypeScript(input) {
|
|
|
75557
77031
|
const host = createVirtualCompilerHost(compilerOptions, {
|
|
75558
77032
|
[CODE_FILE]: wrappedCode,
|
|
75559
77033
|
[DECLARATION_FILE]: input.declaration,
|
|
75560
|
-
[AMBIENT_FILE]:
|
|
77034
|
+
[AMBIENT_FILE]: CODE_MODE_DIAGNOSTICS_BUILTINS_DECLARATION
|
|
75561
77035
|
});
|
|
75562
77036
|
const program = ts.createProgram([
|
|
75563
77037
|
CODE_FILE,
|
|
@@ -75603,7 +77077,7 @@ function preflightDiagnostics(code) {
|
|
|
75603
77077
|
if (hasDirectFetchCall(code)) diagnostics.push({
|
|
75604
77078
|
code: "FETCH_UNAVAILABLE",
|
|
75605
77079
|
severity: "error",
|
|
75606
|
-
message: "Direct fetch is not available in Code Mode; use a Caplet instead."
|
|
77080
|
+
message: "Direct fetch is not available in Code Mode; use a Caplet instead. Cannot find name 'fetch'."
|
|
75607
77081
|
});
|
|
75608
77082
|
return diagnostics;
|
|
75609
77083
|
}
|
|
@@ -75640,24 +77114,83 @@ function formatDiagnostic(diagnostic, syntacticDiagnostic = false) {
|
|
|
75640
77114
|
} : {}
|
|
75641
77115
|
};
|
|
75642
77116
|
}
|
|
75643
|
-
|
|
75644
|
-
|
|
75645
|
-
|
|
75646
|
-
|
|
75647
|
-
|
|
75648
|
-
|
|
75649
|
-
|
|
75650
|
-
|
|
75651
|
-
|
|
75652
|
-
|
|
75653
|
-
|
|
75654
|
-
|
|
75655
|
-
|
|
75656
|
-
|
|
75657
|
-
|
|
75658
|
-
|
|
77117
|
+
//#endregion
|
|
77118
|
+
//#region src/code-mode/platform-host.ts
|
|
77119
|
+
const MAX_RANDOM_VALUES_BYTES = 65536;
|
|
77120
|
+
function installCodeModePlatformHost(context, pendingDeferreds, _options) {
|
|
77121
|
+
const timers = /* @__PURE__ */ new Map();
|
|
77122
|
+
const randomUuidBridge = context.newFunction("__caplets_platform_random_uuid", () => {
|
|
77123
|
+
return context.newString(randomUUID());
|
|
77124
|
+
});
|
|
77125
|
+
context.setProp(context.global, "__caplets_platform_random_uuid", randomUuidBridge);
|
|
77126
|
+
randomUuidBridge.dispose();
|
|
77127
|
+
const randomValuesBridge = context.newFunction("__caplets_platform_random_values", (lengthHandle) => {
|
|
77128
|
+
const length = context.dump(lengthHandle);
|
|
77129
|
+
if (!Number.isSafeInteger(length) || length < 0) return context.newError("Random byte length must be a non-negative safe integer");
|
|
77130
|
+
if (length > MAX_RANDOM_VALUES_BYTES) return context.newError("Random byte length cannot exceed 65,536 bytes");
|
|
77131
|
+
return numberArrayHandle(context, [...randomBytes(length)]);
|
|
77132
|
+
});
|
|
77133
|
+
context.setProp(context.global, "__caplets_platform_random_values", randomValuesBridge);
|
|
77134
|
+
randomValuesBridge.dispose();
|
|
77135
|
+
const sleepBridge = context.newFunction("__caplets_platform_sleep", (timerIdHandle, delayHandle) => {
|
|
77136
|
+
const timerId = context.dump(timerIdHandle);
|
|
77137
|
+
const delayMs = context.dump(delayHandle);
|
|
77138
|
+
const deferred = context.newPromise();
|
|
77139
|
+
pendingDeferreds.add(deferred);
|
|
77140
|
+
deferred.settled.finally(() => pendingDeferreds.delete(deferred));
|
|
77141
|
+
const timeout = setTimeout(() => {
|
|
77142
|
+
timers.delete(timerId);
|
|
77143
|
+
resolveTimer(context, deferred, true);
|
|
77144
|
+
}, Math.max(0, Number(delayMs) || 0));
|
|
77145
|
+
const existing = timers.get(timerId);
|
|
77146
|
+
if (existing) {
|
|
77147
|
+
clearTimeout(existing.timeout);
|
|
77148
|
+
resolveTimer(context, existing.deferred, false);
|
|
77149
|
+
}
|
|
77150
|
+
timers.set(timerId, {
|
|
77151
|
+
deferred,
|
|
77152
|
+
timeout
|
|
77153
|
+
});
|
|
77154
|
+
return deferred.handle;
|
|
77155
|
+
});
|
|
77156
|
+
context.setProp(context.global, "__caplets_platform_sleep", sleepBridge);
|
|
77157
|
+
sleepBridge.dispose();
|
|
77158
|
+
const clearTimerBridge = context.newFunction("__caplets_platform_clear_timer", (timerIdHandle) => {
|
|
77159
|
+
const timerId = context.dump(timerIdHandle);
|
|
77160
|
+
const timer = timers.get(timerId);
|
|
77161
|
+
if (!timer) return context.false;
|
|
77162
|
+
timers.delete(timerId);
|
|
77163
|
+
clearTimeout(timer.timeout);
|
|
77164
|
+
resolveTimer(context, timer.deferred, false);
|
|
77165
|
+
return context.true;
|
|
77166
|
+
});
|
|
77167
|
+
context.setProp(context.global, "__caplets_platform_clear_timer", clearTimerBridge);
|
|
77168
|
+
clearTimerBridge.dispose();
|
|
77169
|
+
return { dispose() {
|
|
77170
|
+
for (const timer of timers.values()) {
|
|
77171
|
+
clearTimeout(timer.timeout);
|
|
77172
|
+
resolveTimer(context, timer.deferred, false);
|
|
77173
|
+
}
|
|
77174
|
+
timers.clear();
|
|
77175
|
+
} };
|
|
77176
|
+
}
|
|
77177
|
+
function numberArrayHandle(context, values) {
|
|
77178
|
+
const arrayHandle = context.newArray();
|
|
77179
|
+
for (let index = 0; index < values.length; index += 1) {
|
|
77180
|
+
const valueHandle = context.newNumber(values[index] ?? 0);
|
|
77181
|
+
context.setProp(arrayHandle, index, valueHandle);
|
|
77182
|
+
valueHandle.dispose();
|
|
77183
|
+
}
|
|
77184
|
+
return arrayHandle;
|
|
77185
|
+
}
|
|
77186
|
+
function resolveTimer(context, deferred, fired) {
|
|
77187
|
+
if (!deferred.alive) return;
|
|
77188
|
+
deferred.resolve(fired ? context.true : context.false);
|
|
75659
77189
|
}
|
|
75660
77190
|
//#endregion
|
|
77191
|
+
//#region src/code-mode/platform-runtime.generated.ts
|
|
77192
|
+
const CODE_MODE_PLATFORM_RUNTIME_SOURCE = "(function() {\n //#region node_modules/.pnpm/@ungap+structured-clone@1.3.1/node_modules/@ungap/structured-clone/esm/deserialize.js\n const env = typeof self === \"object\" ? self : globalThis;\n const guard = (name, init) => {\n switch (name) {\n case \"Function\":\n case \"SharedWorker\":\n case \"Worker\":\n case \"eval\":\n case \"setInterval\":\n case \"setTimeout\": throw new TypeError(\"unable to deserialize \" + name);\n }\n return new env[name](init);\n };\n const deserializer = ($, _) => {\n const as = (out, index) => {\n $.set(index, out);\n return out;\n };\n const unpair = (index) => {\n if ($.has(index)) return $.get(index);\n const [type, value] = _[index];\n switch (type) {\n case 0:\n case -1: return as(value, index);\n case 1: {\n const arr = as([], index);\n for (const index of value) arr.push(unpair(index));\n return arr;\n }\n case 2: {\n const object = as({}, index);\n for (const [key, index] of value) object[unpair(key)] = unpair(index);\n return object;\n }\n case 3: return as(new Date(value), index);\n case 4: {\n const { source, flags } = value;\n return as(new RegExp(source, flags), index);\n }\n case 5: {\n const map = as(/* @__PURE__ */ new Map(), index);\n for (const [key, index] of value) map.set(unpair(key), unpair(index));\n return map;\n }\n case 6: {\n const set = as(/* @__PURE__ */ new Set(), index);\n for (const index of value) set.add(unpair(index));\n return set;\n }\n case 7: {\n const { name, message } = value;\n return as(guard(name, message), index);\n }\n case 8: return as(BigInt(value), index);\n case \"BigInt\": return as(Object(BigInt(value)), index);\n case \"ArrayBuffer\": return as(new Uint8Array(value).buffer, value);\n case \"DataView\": {\n const { buffer } = new Uint8Array(value);\n return as(new DataView(buffer), value);\n }\n }\n return as(guard(type, value), index);\n };\n return unpair;\n };\n /**\n * @typedef {Array<string,any>} Record a type representation\n */\n /**\n * Returns a deserialized value from a serialized array of Records.\n * @param {Record[]} serialized a previously serialized value.\n * @returns {any}\n */\n const deserialize = (serialized) => deserializer(/* @__PURE__ */ new Map(), serialized)(0);\n //#endregion\n //#region node_modules/.pnpm/@ungap+structured-clone@1.3.1/node_modules/@ungap/structured-clone/esm/serialize.js\n const EMPTY = \"\";\n const { toString } = {};\n const { keys } = Object;\n const typeOf = (value) => {\n const type = typeof value;\n if (type !== \"object\" || !value) return [0, type];\n const asString = toString.call(value).slice(8, -1);\n switch (asString) {\n case \"Array\": return [1, EMPTY];\n case \"Object\": return [2, EMPTY];\n case \"Date\": return [3, EMPTY];\n case \"RegExp\": return [4, EMPTY];\n case \"Map\": return [5, EMPTY];\n case \"Set\": return [6, EMPTY];\n case \"DataView\": return [1, asString];\n }\n if (asString.includes(\"Array\")) return [1, asString];\n if (asString.includes(\"Error\")) return [7, asString];\n return [2, asString];\n };\n const shouldSkip = ([TYPE, type]) => TYPE === 0 && (type === \"function\" || type === \"symbol\");\n const serializer = (strict, json, $, _) => {\n const as = (out, value) => {\n const index = _.push(out) - 1;\n $.set(value, index);\n return index;\n };\n const pair = (value) => {\n if ($.has(value)) return $.get(value);\n let [TYPE, type] = typeOf(value);\n switch (TYPE) {\n case 0: {\n let entry = value;\n switch (type) {\n case \"bigint\":\n TYPE = 8;\n entry = value.toString();\n break;\n case \"function\":\n case \"symbol\":\n if (strict) throw new TypeError(\"unable to serialize \" + type);\n entry = null;\n break;\n case \"undefined\": return as([-1], value);\n }\n return as([TYPE, entry], value);\n }\n case 1: {\n if (type) {\n let spread = value;\n if (type === \"DataView\") spread = new Uint8Array(value.buffer);\n else if (type === \"ArrayBuffer\") spread = new Uint8Array(value);\n return as([type, [...spread]], value);\n }\n const arr = [];\n const index = as([TYPE, arr], value);\n for (const entry of value) arr.push(pair(entry));\n return index;\n }\n case 2: {\n if (type) switch (type) {\n case \"BigInt\": return as([type, value.toString()], value);\n case \"Boolean\":\n case \"Number\":\n case \"String\": return as([type, value.valueOf()], value);\n }\n if (json && \"toJSON\" in value) return pair(value.toJSON());\n const entries = [];\n const index = as([TYPE, entries], value);\n for (const key of keys(value)) if (strict || !shouldSkip(typeOf(value[key]))) entries.push([pair(key), pair(value[key])]);\n return index;\n }\n case 3: return as([TYPE, value.toISOString()], value);\n case 4: {\n const { source, flags } = value;\n return as([TYPE, {\n source,\n flags\n }], value);\n }\n case 5: {\n const entries = [];\n const index = as([TYPE, entries], value);\n for (const [key, entry] of value) if (strict || !(shouldSkip(typeOf(key)) || shouldSkip(typeOf(entry)))) entries.push([pair(key), pair(entry)]);\n return index;\n }\n case 6: {\n const entries = [];\n const index = as([TYPE, entries], value);\n for (const entry of value) if (strict || !shouldSkip(typeOf(entry))) entries.push(pair(entry));\n return index;\n }\n }\n const { message } = value;\n return as([TYPE, {\n name: type,\n message\n }], value);\n };\n return pair;\n };\n /**\n * @typedef {Array<string,any>} Record a type representation\n */\n /**\n * Returns an array of serialized Records.\n * @param {any} value a serializable value.\n * @param {{json?: boolean, lossy?: boolean}?} options an object with a `lossy` or `json` property that,\n * if `true`, will not throw errors on incompatible types, and behave more\n * like JSON stringify would behave. Symbol and Function will be discarded.\n * @returns {Record[]}\n */\n const serialize = (value, { json, lossy } = {}) => {\n const _ = [];\n return serializer(!(json || lossy), !!json, /* @__PURE__ */ new Map(), _)(value), _;\n };\n //#endregion\n //#region node_modules/.pnpm/@ungap+structured-clone@1.3.1/node_modules/@ungap/structured-clone/esm/index.js\n /**\n * @typedef {Array<string,any>} Record a type representation\n */\n /**\n * Returns an array of serialized Records.\n * @param {any} any a serializable value.\n * @param {{transfer?: any[], json?: boolean, lossy?: boolean}?} options an object with\n * a transfer option (ignored when polyfilled) and/or non standard fields that\n * fallback to the polyfill if present.\n * @returns {Record[]}\n */\n var esm_default = typeof structuredClone === \"function\" ? (any, options) => options && (\"json\" in options || \"lossy\" in options) ? deserialize(serialize(any, options)) : structuredClone(any) : (any, options) => deserialize(serialize(any, options));\n //#endregion\n //#region node_modules/.pnpm/formdata-node@6.0.3/node_modules/formdata-node/lib/form-data.js\n var __accessCheck = (obj, member, msg) => {\n if (!member.has(obj)) throw TypeError(\"Cannot \" + msg);\n };\n var __privateGet = (obj, member, getter) => {\n __accessCheck(obj, member, \"read from private field\");\n return getter ? getter.call(obj) : member.get(obj);\n };\n var __privateAdd = (obj, member, value) => {\n if (member.has(obj)) throw TypeError(\"Cannot add the same private member more than once\");\n member instanceof WeakSet ? member.add(obj) : member.set(obj, value);\n };\n var __privateSet = (obj, member, value, setter) => {\n __accessCheck(obj, member, \"write to private field\");\n setter ? setter.call(obj, value) : member.set(obj, value);\n return value;\n };\n var __privateMethod = (obj, member, method) => {\n __accessCheck(obj, member, \"access private method\");\n return method;\n };\n var isFunction = (value) => typeof value === \"function\";\n var isObject = (value) => typeof value === \"object\" && value != null && !Array.isArray(value);\n var isAsyncIterable = (value) => isObject(value) && isFunction(value[Symbol.asyncIterator]);\n var MAX_CHUNK_SIZE = 65536;\n async function* clonePart(value) {\n if (value.byteLength <= MAX_CHUNK_SIZE) {\n yield value;\n return;\n }\n let offset = 0;\n while (offset < value.byteLength) {\n const size = Math.min(value.byteLength - offset, MAX_CHUNK_SIZE);\n const buffer = value.buffer.slice(offset, offset + size);\n offset += buffer.byteLength;\n yield new Uint8Array(buffer);\n }\n }\n async function* readStream(readable) {\n const reader = readable.getReader();\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n yield value;\n }\n }\n async function* chunkStream(stream) {\n for await (const value of stream) yield* clonePart(value);\n }\n var getStreamIterator = (source) => {\n if (isAsyncIterable(source)) return chunkStream(source);\n if (isFunction(source.getReader)) return chunkStream(readStream(source));\n throw new TypeError(\"Unsupported data source: Expected either ReadableStream or async iterable.\");\n };\n async function* consumeNodeBlob(blob) {\n let position = 0;\n while (position !== blob.size) {\n const buffer = await blob.slice(position, Math.min(blob.size, position + MAX_CHUNK_SIZE)).arrayBuffer();\n position += buffer.byteLength;\n yield new Uint8Array(buffer);\n }\n }\n async function* consumeBlobParts(parts, clone = false) {\n for (const part of parts) if (ArrayBuffer.isView(part)) if (clone) yield* clonePart(part);\n else yield part;\n else if (isFunction(part.stream)) yield* getStreamIterator(part.stream());\n else yield* consumeNodeBlob(part);\n }\n function* sliceBlob(blobParts, blobSize, start = 0, end) {\n end ??= blobSize;\n let relativeStart = start < 0 ? Math.max(blobSize + start, 0) : Math.min(start, blobSize);\n let relativeEnd = end < 0 ? Math.max(blobSize + end, 0) : Math.min(end, blobSize);\n const span = Math.max(relativeEnd - relativeStart, 0);\n let added = 0;\n for (const part of blobParts) {\n if (added >= span) break;\n const partSize = ArrayBuffer.isView(part) ? part.byteLength : part.size;\n if (relativeStart && partSize <= relativeStart) {\n relativeStart -= partSize;\n relativeEnd -= partSize;\n } else {\n let chunk;\n if (ArrayBuffer.isView(part)) {\n chunk = part.subarray(relativeStart, Math.min(partSize, relativeEnd));\n added += chunk.byteLength;\n } else {\n chunk = part.slice(relativeStart, Math.min(partSize, relativeEnd));\n added += chunk.size;\n }\n relativeEnd -= partSize;\n relativeStart = 0;\n yield chunk;\n }\n }\n }\n var _parts, _type, _size;\n var _Blob = class _Blob {\n /**\n * Returns a new [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) object.\n * The content of the blob consists of the concatenation of the values given in the parameter array.\n *\n * @param blobParts An `Array` strings, or [`ArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer), [`ArrayBufferView`](https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView), [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) objects, or a mix of any of such objects, that will be put inside the [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob).\n * @param options An optional object of type `BlobPropertyBag`.\n */\n constructor(blobParts = [], options = {}) {\n /**\n * An `Array` of [`ArrayBufferView`](https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView) or [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) objects, or a mix of any of such objects, that will be put inside the [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob).\n */\n __privateAdd(this, _parts, []);\n /**\n * Returns the [`MIME type`](https://developer.mozilla.org/en-US/docs/Glossary/MIME_type) of the [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) or [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File).\n */\n __privateAdd(this, _type, \"\");\n /**\n * Returns the size of the [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) or [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File) in bytes.\n */\n __privateAdd(this, _size, 0);\n options ??= {};\n if (typeof blobParts !== \"object\" || blobParts === null) throw new TypeError(\"Failed to construct 'Blob': The provided value cannot be converted to a sequence.\");\n if (!isFunction(blobParts[Symbol.iterator])) throw new TypeError(\"Failed to construct 'Blob': The object must have a callable @@iterator property.\");\n if (typeof options !== \"object\" && !isFunction(options)) throw new TypeError(\"Failed to construct 'Blob': parameter 2 cannot convert to dictionary.\");\n const encoder = new TextEncoder();\n for (const raw of blobParts) {\n let part;\n if (ArrayBuffer.isView(raw)) part = new Uint8Array(raw.buffer.slice(raw.byteOffset, raw.byteOffset + raw.byteLength));\n else if (raw instanceof ArrayBuffer) part = new Uint8Array(raw.slice(0));\n else if (raw instanceof _Blob) part = raw;\n else part = encoder.encode(String(raw));\n __privateSet(this, _size, __privateGet(this, _size) + (ArrayBuffer.isView(part) ? part.byteLength : part.size));\n __privateGet(this, _parts).push(part);\n }\n const type = options.type === void 0 ? \"\" : String(options.type);\n __privateSet(this, _type, /^[\\x20-\\x7E]*$/.test(type) ? type : \"\");\n }\n static [Symbol.hasInstance](value) {\n return Boolean(value && typeof value === \"object\" && isFunction(value.constructor) && (isFunction(value.stream) || isFunction(value.arrayBuffer)) && /^(Blob|File)$/.test(value[Symbol.toStringTag]));\n }\n /**\n * Returns the [`MIME type`](https://developer.mozilla.org/en-US/docs/Glossary/MIME_type) of the [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) or [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File).\n */\n get type() {\n return __privateGet(this, _type);\n }\n /**\n * Returns the size of the [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) or [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File) in bytes.\n */\n get size() {\n return __privateGet(this, _size);\n }\n /**\n * Creates and returns a new [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) object which contains data from a subset of the blob on which it's called.\n *\n * @param start An index into the Blob indicating the first byte to include in the new Blob. If you specify a negative value, it's treated as an offset from the end of the Blob toward the beginning. For example, -10 would be the 10th from last byte in the Blob. The default value is 0. If you specify a value for start that is larger than the size of the source Blob, the returned Blob has size 0 and contains no data.\n * @param end An index into the Blob indicating the first byte that will *not* be included in the new Blob (i.e. the byte exactly at this index is not included). If you specify a negative value, it's treated as an offset from the end of the Blob toward the beginning. For example, -10 would be the 10th from last byte in the Blob. The default value is size.\n * @param contentType The content type to assign to the new Blob; this will be the value of its type property. The default value is an empty string.\n */\n slice(start, end, contentType) {\n return new _Blob(sliceBlob(__privateGet(this, _parts), this.size, start, end), { type: contentType });\n }\n /**\n * Returns a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) that resolves with a string containing the contents of the blob, interpreted as UTF-8.\n */\n async text() {\n const decoder = new TextDecoder();\n let result = \"\";\n for await (const chunk of consumeBlobParts(__privateGet(this, _parts))) result += decoder.decode(chunk, { stream: true });\n result += decoder.decode();\n return result;\n }\n /**\n * Returns a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) that resolves with the contents of the blob as binary data contained in an [`ArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer).\n */\n async arrayBuffer() {\n const view = new Uint8Array(this.size);\n let offset = 0;\n for await (const chunk of consumeBlobParts(__privateGet(this, _parts))) {\n view.set(chunk, offset);\n offset += chunk.length;\n }\n return view.buffer;\n }\n /**\n * Returns a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) which upon reading returns the data contained within the [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob).\n */\n stream() {\n const iterator = consumeBlobParts(__privateGet(this, _parts), true);\n return new ReadableStream({\n async pull(controller) {\n const { value, done } = await iterator.next();\n if (done) return queueMicrotask(() => controller.close());\n controller.enqueue(value);\n },\n async cancel() {\n await iterator.return();\n }\n });\n }\n get [Symbol.toStringTag]() {\n return \"Blob\";\n }\n };\n _parts = /* @__PURE__ */ new WeakMap();\n _type = /* @__PURE__ */ new WeakMap();\n _size = /* @__PURE__ */ new WeakMap();\n var Blob = _Blob;\n Object.defineProperties(Blob.prototype, {\n type: { enumerable: true },\n size: { enumerable: true },\n slice: { enumerable: true },\n stream: { enumerable: true },\n text: { enumerable: true },\n arrayBuffer: { enumerable: true }\n });\n var isBlob = (value) => value instanceof Blob;\n var _name, _lastModified;\n var File = class extends Blob {\n /**\n * Creates a new File instance.\n *\n * @param fileBits An `Array` strings, or [`ArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer), [`ArrayBufferView`](https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView), [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) objects, or a mix of any of such objects, that will be put inside the [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File).\n * @param name The name of the file.\n * @param options An options object containing optional attributes for the file.\n */\n constructor(fileBits, name, options = {}) {\n super(fileBits, options);\n /**\n * Returns the name of the file referenced by the File object.\n */\n __privateAdd(this, _name, void 0);\n /**\n * The last modified date of the file as the number of milliseconds since the Unix epoch (January 1, 1970 at midnight). Files without a known last modified date return the current date.\n */\n __privateAdd(this, _lastModified, 0);\n if (arguments.length < 2) throw new TypeError(`Failed to construct 'File': 2 arguments required, but only ${arguments.length} present.`);\n __privateSet(this, _name, String(name));\n const lastModified = options.lastModified === void 0 ? Date.now() : Number(options.lastModified);\n if (!Number.isNaN(lastModified)) __privateSet(this, _lastModified, lastModified);\n }\n static [Symbol.hasInstance](value) {\n return value instanceof Blob && value[Symbol.toStringTag] === \"File\" && typeof value.name === \"string\";\n }\n /**\n * Name of the file referenced by the File object.\n */\n get name() {\n return __privateGet(this, _name);\n }\n /* c8 ignore next 3 */\n get webkitRelativePath() {\n return \"\";\n }\n /**\n * The last modified date of the file as the number of milliseconds since the Unix epoch (January 1, 1970 at midnight). Files without a known last modified date return the current date.\n */\n get lastModified() {\n return __privateGet(this, _lastModified);\n }\n get [Symbol.toStringTag]() {\n return \"File\";\n }\n };\n _name = /* @__PURE__ */ new WeakMap();\n _lastModified = /* @__PURE__ */ new WeakMap();\n var isFile = (value) => value instanceof File;\n var _entries, _setEntry, setEntry_fn;\n var FormData = class {\n constructor() {\n __privateAdd(this, _setEntry);\n /**\n * Stores internal data for every entry\n */\n __privateAdd(this, _entries, /* @__PURE__ */ new Map());\n }\n static [Symbol.hasInstance](value) {\n if (!value) return false;\n const val = value;\n return Boolean(isFunction(val.constructor) && val[Symbol.toStringTag] === \"FormData\" && isFunction(val.append) && isFunction(val.set) && isFunction(val.get) && isFunction(val.getAll) && isFunction(val.has) && isFunction(val.delete) && isFunction(val.entries) && isFunction(val.values) && isFunction(val.keys) && isFunction(val[Symbol.iterator]) && isFunction(val.forEach));\n }\n /**\n * Appends a new value onto an existing key inside a FormData object,\n * or adds the key if it does not already exist.\n *\n * The difference between `set()` and `append()` is that if the specified key already exists, `set()` will overwrite all existing values with the new one, whereas `append()` will append the new value onto the end of the existing set of values.\n *\n * @param name The name of the field whose data is contained in `value`.\n * @param value The field's value. This can be [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob)\n or [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File). If none of these are specified the value is converted to a string.\n * @param fileName The filename reported to the server, when a Blob or File is passed as the second parameter. The default filename for Blob objects is \"blob\". The default filename for File objects is the file's filename.\n */\n append(name, value, fileName) {\n __privateMethod(this, _setEntry, setEntry_fn).call(this, {\n name,\n fileName,\n append: true,\n rawValue: value,\n argsLength: arguments.length\n });\n }\n /**\n * Set a new value for an existing key inside FormData,\n * or add the new field if it does not already exist.\n *\n * @param name The name of the field whose data is contained in `value`.\n * @param value The field's value. This can be [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob)\n or [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File). If none of these are specified the value is converted to a string.\n * @param fileName The filename reported to the server, when a Blob or File is passed as the second parameter. The default filename for Blob objects is \"blob\". The default filename for File objects is the file's filename.\n *\n */\n set(name, value, fileName) {\n __privateMethod(this, _setEntry, setEntry_fn).call(this, {\n name,\n fileName,\n append: false,\n rawValue: value,\n argsLength: arguments.length\n });\n }\n /**\n * Returns the first value associated with a given key from within a `FormData` object.\n * If you expect multiple values and want all of them, use the `getAll()` method instead.\n *\n * @param {string} name A name of the value you want to retrieve.\n *\n * @returns A `FormDataEntryValue` containing the value. If the key doesn't exist, the method returns null.\n */\n get(name) {\n const field = __privateGet(this, _entries).get(String(name));\n if (!field) return null;\n return field[0];\n }\n /**\n * Returns all the values associated with a given key from within a `FormData` object.\n *\n * @param {string} name A name of the value you want to retrieve.\n *\n * @returns An array of `FormDataEntryValue` whose key matches the value passed in the `name` parameter. If the key doesn't exist, the method returns an empty list.\n */\n getAll(name) {\n const field = __privateGet(this, _entries).get(String(name));\n if (!field) return [];\n return field.slice();\n }\n /**\n * Returns a boolean stating whether a `FormData` object contains a certain key.\n *\n * @param name A string representing the name of the key you want to test for.\n *\n * @return A boolean value.\n */\n has(name) {\n return __privateGet(this, _entries).has(String(name));\n }\n /**\n * Deletes a key and its value(s) from a `FormData` object.\n *\n * @param name The name of the key you want to delete.\n */\n delete(name) {\n __privateGet(this, _entries).delete(String(name));\n }\n /**\n * Returns an [`iterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) allowing to go through all keys contained in this `FormData` object.\n * Each key is a `string`.\n */\n *keys() {\n for (const key of __privateGet(this, _entries).keys()) yield key;\n }\n /**\n * Returns an [`iterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) allowing to go through the `FormData` key/value pairs.\n * The key of each pair is a string; the value is a [`FormDataValue`](https://developer.mozilla.org/en-US/docs/Web/API/FormDataEntryValue).\n */\n *entries() {\n for (const name of this.keys()) {\n const values = this.getAll(name);\n for (const value of values) yield [name, value];\n }\n }\n /**\n * Returns an [`iterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) allowing to go through all values contained in this object `FormData` object.\n * Each value is a [`FormDataValue`](https://developer.mozilla.org/en-US/docs/Web/API/FormDataEntryValue).\n */\n *values() {\n for (const [, value] of this) yield value;\n }\n /**\n * An alias for FormData#entries()\n */\n [Symbol.iterator]() {\n return this.entries();\n }\n /**\n * Executes given callback function for each field of the FormData instance\n */\n forEach(callback, thisArg) {\n for (const [name, value] of this) callback.call(thisArg, value, name, this);\n }\n get [Symbol.toStringTag]() {\n return \"FormData\";\n }\n };\n _entries = /* @__PURE__ */ new WeakMap();\n _setEntry = /* @__PURE__ */ new WeakSet();\n setEntry_fn = function({ name, rawValue, append, fileName, argsLength }) {\n const methodName = append ? \"append\" : \"set\";\n if (argsLength < 2) throw new TypeError(`Failed to execute '${methodName}' on 'FormData': 2 arguments required, but only ${argsLength} present.`);\n name = String(name);\n let value;\n if (isFile(rawValue)) value = fileName === void 0 ? rawValue : new File([rawValue], fileName, {\n type: rawValue.type,\n lastModified: rawValue.lastModified\n });\n else if (isBlob(rawValue)) value = new File([rawValue], fileName === void 0 ? \"blob\" : fileName, { type: rawValue.type });\n else if (fileName) throw new TypeError(`Failed to execute '${methodName}' on 'FormData': parameter 2 is not of type 'Blob'.`);\n else value = String(rawValue);\n const values = __privateGet(this, _entries).get(name);\n if (!values) {\n __privateGet(this, _entries).set(name, [value]);\n return;\n }\n if (!append) {\n __privateGet(this, _entries).set(name, [value]);\n return;\n }\n values.push(value);\n };\n /*! Based on fetch-blob. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> & David Frank */\n //#endregion\n //#region node_modules/.pnpm/set-cookie-parser@3.1.0/node_modules/set-cookie-parser/lib/set-cookie.js\n var defaultParseOptions = {\n decodeValues: true,\n map: false,\n silent: false,\n split: \"auto\"\n };\n function isForbiddenKey(key) {\n return typeof key !== \"string\" || key in {};\n }\n function createNullObj() {\n return Object.create(null);\n }\n function isNonEmptyString(str) {\n return typeof str === \"string\" && !!str.trim();\n }\n function parseString(setCookieValue, options) {\n var parts = setCookieValue.split(\";\").filter(isNonEmptyString);\n var parsed = parseNameValuePair(parts.shift());\n var name = parsed.name;\n var value = parsed.value;\n options = options ? Object.assign({}, defaultParseOptions, options) : defaultParseOptions;\n if (isForbiddenKey(name)) return null;\n try {\n value = options.decodeValues ? decodeURIComponent(value) : value;\n } catch (e) {\n console.error(\"set-cookie-parser: failed to decode cookie value. Set options.decodeValues=false to disable decoding.\", e);\n }\n var cookie = createNullObj();\n cookie.name = name;\n cookie.value = value;\n parts.forEach(function(part) {\n var sides = part.split(\"=\");\n var key = sides.shift().trimLeft().toLowerCase();\n if (isForbiddenKey(key)) return;\n var value = sides.join(\"=\");\n if (key === \"expires\") cookie.expires = new Date(value);\n else if (key === \"max-age\") {\n var n = parseInt(value, 10);\n if (!Number.isNaN(n)) cookie.maxAge = n;\n } else if (key === \"secure\") cookie.secure = true;\n else if (key === \"httponly\") cookie.httpOnly = true;\n else if (key === \"samesite\") cookie.sameSite = value;\n else if (key === \"partitioned\") cookie.partitioned = true;\n else if (key) cookie[key] = value;\n });\n return cookie;\n }\n function parseNameValuePair(nameValuePairStr) {\n var name = \"\";\n var value = \"\";\n var nameValueArr = nameValuePairStr.split(\"=\");\n if (nameValueArr.length > 1) {\n name = nameValueArr.shift();\n value = nameValueArr.join(\"=\");\n } else value = nameValuePairStr;\n return {\n name,\n value\n };\n }\n function parseSetCookie(input, options) {\n options = options ? Object.assign({}, defaultParseOptions, options) : defaultParseOptions;\n if (!input) if (!options.map) return [];\n else return createNullObj();\n if (input.headers) if (typeof input.headers.getSetCookie === \"function\") input = input.headers.getSetCookie();\n else if (input.headers[\"set-cookie\"]) input = input.headers[\"set-cookie\"];\n else {\n var sch = input.headers[Object.keys(input.headers).find(function(key) {\n return key.toLowerCase() === \"set-cookie\";\n })];\n if (!sch && input.headers.cookie && !options.silent) console.warn(\"Warning: set-cookie-parser appears to have been called on a request object. It is designed to parse Set-Cookie headers from responses, not Cookie headers from requests. Set the option {silent: true} to suppress this warning.\");\n input = sch;\n }\n var split = options.split;\n var isArray = Array.isArray(input);\n if (split === \"auto\") split = !isArray;\n if (!isArray) input = [input];\n input = input.filter(isNonEmptyString);\n if (split) input = input.map(splitCookiesString).flat();\n if (!options.map) return input.map(function(str) {\n return parseString(str, options);\n }).filter(Boolean);\n else {\n var cookies = createNullObj();\n return input.reduce(function(cookies, str) {\n var cookie = parseString(str, options);\n if (cookie && !isForbiddenKey(cookie.name)) cookies[cookie.name] = cookie;\n return cookies;\n }, cookies);\n }\n }\n function splitCookiesString(cookiesString) {\n if (Array.isArray(cookiesString)) return cookiesString;\n if (typeof cookiesString !== \"string\") return [];\n var cookiesStrings = [];\n var pos = 0;\n var start;\n var ch;\n var lastComma;\n var nextStart;\n var cookiesSeparatorFound;\n function skipWhitespace() {\n while (pos < cookiesString.length && /\\s/.test(cookiesString.charAt(pos))) pos += 1;\n return pos < cookiesString.length;\n }\n function notSpecialChar() {\n ch = cookiesString.charAt(pos);\n return ch !== \"=\" && ch !== \";\" && ch !== \",\";\n }\n while (pos < cookiesString.length) {\n start = pos;\n cookiesSeparatorFound = false;\n while (skipWhitespace()) {\n ch = cookiesString.charAt(pos);\n if (ch === \",\") {\n lastComma = pos;\n pos += 1;\n skipWhitespace();\n nextStart = pos;\n while (pos < cookiesString.length && notSpecialChar()) pos += 1;\n if (pos < cookiesString.length && cookiesString.charAt(pos) === \"=\") {\n cookiesSeparatorFound = true;\n pos = nextStart;\n cookiesStrings.push(cookiesString.substring(start, lastComma));\n start = pos;\n } else pos = lastComma + 1;\n } else pos += 1;\n }\n if (!cookiesSeparatorFound || pos >= cookiesString.length) cookiesStrings.push(cookiesString.substring(start, cookiesString.length));\n }\n return cookiesStrings;\n }\n parseSetCookie.parseSetCookie = parseSetCookie;\n parseSetCookie.parse = parseSetCookie;\n parseSetCookie.parseString = parseString;\n parseSetCookie.splitCookiesString = splitCookiesString;\n //#endregion\n //#region node_modules/.pnpm/headers-polyfill@5.0.1/node_modules/headers-polyfill/lib/index.mjs\n const HEADERS_INVALID_CHARACTERS = /[^a-z0-9\\-#$%&'*+.^_`|~]/i;\n function normalizeHeaderName(name) {\n if (HEADERS_INVALID_CHARACTERS.test(name) || name.trim() === \"\") throw new TypeError(\"Invalid character in header field name\");\n return name.trim().toLowerCase();\n }\n const charCodesToRemove = [\n String.fromCharCode(10),\n String.fromCharCode(13),\n String.fromCharCode(9),\n String.fromCharCode(32)\n ];\n const HEADER_VALUE_REMOVE_REGEXP = new RegExp(`(^[${charCodesToRemove.join(\"\")}]|$[${charCodesToRemove.join(\"\")}])`, \"g\");\n /**\n * Normalize the given header value.\n * @see https://fetch.spec.whatwg.org/#concept-header-value-normalize\n */\n function normalizeHeaderValue(value) {\n return value.replace(HEADER_VALUE_REMOVE_REGEXP, \"\");\n }\n /**\n * Validate the given header name.\n * @see https://fetch.spec.whatwg.org/#header-name\n */\n function isValidHeaderName(value) {\n if (typeof value !== \"string\") return false;\n if (value.length === 0) return false;\n for (let i = 0; i < value.length; i++) {\n const character = value.charCodeAt(i);\n if (character > 127 || !isToken(character)) return false;\n }\n return true;\n }\n function isToken(value) {\n return ![\n 127,\n 32,\n \"(\",\n \")\",\n \"<\",\n \">\",\n \"@\",\n \",\",\n \";\",\n \":\",\n \"\\\\\",\n \"\\\"\",\n \"/\",\n \"[\",\n \"]\",\n \"?\",\n \"=\",\n \"{\",\n \"}\"\n ].includes(value);\n }\n /**\n * Validate the given header value.\n * @see https://fetch.spec.whatwg.org/#header-value\n */\n function isValidHeaderValue(value) {\n if (typeof value !== \"string\") return false;\n if (value.trim() !== value) return false;\n for (let i = 0; i < value.length; i++) {\n const character = value.charCodeAt(i);\n if (character === 0 || character === 10 || character === 13) return false;\n }\n return true;\n }\n let _Symbol$toStringTag;\n const NORMALIZED_HEADERS = Symbol(\"normalizedHeaders\");\n const RAW_HEADER_NAMES = Symbol(\"rawHeaderNames\");\n const HEADER_VALUE_DELIMITER = \", \";\n var Headers = class Headers {\n constructor(init) {\n this[NORMALIZED_HEADERS] = {};\n this[RAW_HEADER_NAMES] = /* @__PURE__ */ new Map();\n this[_Symbol$toStringTag] = \"Headers\";\n /**\n * @note Cannot necessarily check if the `init` is an instance of the\n * `Headers` because that class may not be defined in Node or jsdom.\n */\n if ([\"Headers\", \"HeadersPolyfill\"].includes(init?.constructor?.name) || init instanceof Headers || typeof globalThis.Headers !== \"undefined\" && init instanceof globalThis.Headers) init.forEach((value, name) => {\n this.append(name, value);\n }, this);\n else if (Array.isArray(init)) init.forEach(([name, value]) => {\n this.append(name, Array.isArray(value) ? value.join(HEADER_VALUE_DELIMITER) : value);\n });\n else if (init) Object.getOwnPropertyNames(init).forEach((name) => {\n const value = init[name];\n this.append(name, Array.isArray(value) ? value.join(HEADER_VALUE_DELIMITER) : value);\n });\n }\n [(_Symbol$toStringTag = Symbol.toStringTag, Symbol.iterator)]() {\n return this.entries();\n }\n *keys() {\n for (const [name] of this.entries()) yield name;\n }\n *values() {\n for (const [, value] of this.entries()) yield value;\n }\n *entries() {\n let sortedKeys = Object.keys(this[NORMALIZED_HEADERS]).sort((a, b) => a.localeCompare(b));\n for (const name of sortedKeys) if (name === \"set-cookie\") for (const value of this.getSetCookie()) yield [name, value];\n else yield [name, this.get(name)];\n }\n /**\n * Returns a boolean stating whether a `Headers` object contains a certain header.\n */\n has(name) {\n if (!isValidHeaderName(name)) throw new TypeError(`Invalid header name \"${name}\"`);\n return this[NORMALIZED_HEADERS].hasOwnProperty(normalizeHeaderName(name));\n }\n /**\n * Returns a `ByteString` sequence of all the values of a header with a given name.\n */\n get(name) {\n if (!isValidHeaderName(name)) throw TypeError(`Invalid header name \"${name}\"`);\n return this[NORMALIZED_HEADERS][normalizeHeaderName(name)] ?? null;\n }\n /**\n * Sets a new value for an existing header inside a `Headers` object, or adds the header if it does not already exist.\n */\n set(name, value) {\n if (!isValidHeaderName(name) || !isValidHeaderValue(value)) return;\n const normalizedName = normalizeHeaderName(name);\n const normalizedValue = normalizeHeaderValue(value);\n this[NORMALIZED_HEADERS][normalizedName] = normalizeHeaderValue(normalizedValue);\n this[RAW_HEADER_NAMES].set(normalizedName, name);\n }\n /**\n * Appends a new value onto an existing header inside a `Headers` object, or adds the header if it does not already exist.\n */\n append(name, value) {\n if (!isValidHeaderName(name) || !isValidHeaderValue(value)) return;\n const normalizedName = normalizeHeaderName(name);\n const normalizedValue = normalizeHeaderValue(value);\n let resolvedValue = this.has(normalizedName) ? `${this.get(normalizedName)}, ${normalizedValue}` : normalizedValue;\n this.set(name, resolvedValue);\n }\n /**\n * Deletes a header from the `Headers` object.\n */\n delete(name) {\n if (!isValidHeaderName(name)) return;\n if (!this.has(name)) return;\n const normalizedName = normalizeHeaderName(name);\n delete this[NORMALIZED_HEADERS][normalizedName];\n this[RAW_HEADER_NAMES].delete(normalizedName);\n }\n /**\n * Traverses the `Headers` object,\n * calling the given callback for each header.\n */\n forEach(callback, thisArg) {\n for (const [name, value] of this.entries()) callback.call(thisArg, value, name, this);\n }\n /**\n * Returns an array containing the values\n * of all Set-Cookie headers associated\n * with a response\n */\n getSetCookie() {\n const setCookieHeader = this.get(\"set-cookie\");\n if (setCookieHeader === null) return [];\n if (setCookieHeader === \"\") return [\"\"];\n return splitCookiesString(setCookieHeader);\n }\n };\n //#endregion\n //#region packages/core/src/code-mode/platform-entry.ts\n const platformBridgeGlobalThis = globalThis;\n const capletsPlatformHost = {\n randomUUID: platformBridgeGlobalThis.__caplets_platform_random_uuid,\n randomValues: platformBridgeGlobalThis.__caplets_platform_random_values,\n sleep: platformBridgeGlobalThis.__caplets_platform_sleep,\n clearTimer: platformBridgeGlobalThis.__caplets_platform_clear_timer\n };\n delete platformBridgeGlobalThis.__caplets_platform_random_uuid;\n delete platformBridgeGlobalThis.__caplets_platform_random_values;\n delete platformBridgeGlobalThis.__caplets_platform_sleep;\n delete platformBridgeGlobalThis.__caplets_platform_clear_timer;\n const DISABLED_FETCH_MESSAGE = \"Direct fetch is not available in Code Mode; use a Caplet instead.\";\n const BASE64_ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n const base64Lookup = new Map(BASE64_ALPHABET.split(\"\").map((char, index) => [char, index]));\n function utf8Encode(input) {\n const encoded = encodeURIComponent(input);\n const bytes = [];\n for (let index = 0; index < encoded.length; index += 1) {\n if (encoded[index] === \"%\") {\n bytes.push(Number.parseInt(encoded.slice(index + 1, index + 3), 16));\n index += 2;\n continue;\n }\n bytes.push(encoded.charCodeAt(index));\n }\n return Uint8Array.from(bytes);\n }\n function utf8Decode(input) {\n let output = \"\";\n for (let index = 0; index < input.length; index += 1) {\n const first = input[index] ?? 0;\n if (first < 128) {\n output += String.fromCharCode(first);\n continue;\n }\n if (first >= 194 && first <= 223) {\n const second = input[index + 1];\n if (isUtf8Continuation(second)) {\n output += String.fromCodePoint((first & 31) << 6 | second & 63);\n index += 1;\n continue;\n }\n output += \"�\";\n continue;\n }\n if (first >= 224 && first <= 239) {\n const second = input[index + 1];\n const third = input[index + 2];\n if (isValidUtf8SecondForThreeByte(first, second) && isUtf8Continuation(third)) {\n output += String.fromCodePoint((first & 15) << 12 | (second & 63) << 6 | third & 63);\n index += 2;\n continue;\n }\n output += \"�\";\n if (isValidUtf8SecondForThreeByte(first, second)) index += 1;\n continue;\n }\n if (first >= 240 && first <= 244) {\n const second = input[index + 1];\n const third = input[index + 2];\n const fourth = input[index + 3];\n if (isValidUtf8SecondForFourByte(first, second) && isUtf8Continuation(third) && isUtf8Continuation(fourth)) {\n output += String.fromCodePoint((first & 7) << 18 | (second & 63) << 12 | (third & 63) << 6 | fourth & 63);\n index += 3;\n continue;\n }\n output += \"�\";\n if (isValidUtf8SecondForFourByte(first, second)) index += isUtf8Continuation(third) ? 2 : 1;\n continue;\n }\n output += \"�\";\n }\n return output;\n }\n function isUtf8Continuation(value) {\n return value !== void 0 && value >= 128 && value <= 191;\n }\n function isValidUtf8SecondForThreeByte(first, second) {\n if (second === void 0) return false;\n if (first === 224) return second >= 160 && second <= 191;\n if (first === 237) return second >= 128 && second <= 159;\n return second >= 128 && second <= 191;\n }\n function isValidUtf8SecondForFourByte(first, second) {\n if (second === void 0) return false;\n if (first === 240) return second >= 144 && second <= 191;\n if (first === 244) return second >= 128 && second <= 143;\n return second >= 128 && second <= 191;\n }\n var TextEncoderShim = class {\n encoding = \"utf-8\";\n encode(input = \"\") {\n return utf8Encode(String(input));\n }\n };\n var TextDecoderShim = class {\n encoding;\n constructor(label = \"utf-8\") {\n const normalized = label.toLowerCase();\n if (![\n \"utf-8\",\n \"utf8\",\n \"unicode-1-1-utf-8\"\n ].includes(normalized)) throw new TypeError(`Unsupported encoding: ${label}`);\n this.encoding = \"utf-8\";\n }\n decode(input) {\n if (input === void 0) return \"\";\n return utf8Decode(copyBytes(input));\n }\n };\n const textEncoder = new TextEncoderShim();\n const textDecoder = new TextDecoderShim();\n var URLSearchParamsShim = class URLSearchParamsShim {\n #entries = [];\n #onChange;\n constructor(init = \"\", onChange) {\n this.#onChange = onChange;\n if (typeof init === \"string\") {\n const source = init.startsWith(\"?\") ? init.slice(1) : init;\n if (!source) return;\n for (const pair of source.split(\"&\")) {\n if (!pair) continue;\n const separatorIndex = pair.indexOf(\"=\");\n const key = separatorIndex >= 0 ? pair.slice(0, separatorIndex) : pair;\n const value = separatorIndex >= 0 ? pair.slice(separatorIndex + 1) : \"\";\n this.#entries.push([decodeUrlParam(key), decodeUrlParam(value)]);\n }\n return;\n }\n if (init instanceof URLSearchParamsShim) {\n this.#entries = [...init.#entries];\n return;\n }\n if (Array.isArray(init)) {\n this.#entries = init.map(([key, value]) => [String(key), String(value)]);\n return;\n }\n this.#entries = Object.entries(init).map(([key, value]) => [key, String(value)]);\n }\n append(name, value) {\n this.#entries.push([String(name), String(value)]);\n this.#onChange?.();\n }\n delete(name) {\n const normalized = String(name);\n this.#entries = this.#entries.filter(([key]) => key !== normalized);\n this.#onChange?.();\n }\n entries() {\n return this.#entries[Symbol.iterator]();\n }\n forEach(callback) {\n for (const [key, value] of this.#entries) callback(value, key, this);\n }\n get(name) {\n return this.#entries.find(([key]) => key === String(name))?.[1] ?? null;\n }\n getAll(name) {\n return this.#entries.filter(([key]) => key === String(name)).map(([, value]) => value);\n }\n has(name) {\n return this.#entries.some(([key]) => key === String(name));\n }\n keys() {\n return this.#entries.map(([key]) => key)[Symbol.iterator]();\n }\n set(name, value) {\n const normalizedName = String(name);\n const normalizedValue = String(value);\n const next = [];\n let replaced = false;\n for (const entry of this.#entries) {\n if (entry[0] !== normalizedName) {\n next.push(entry);\n continue;\n }\n if (!replaced) {\n next.push([normalizedName, normalizedValue]);\n replaced = true;\n }\n }\n if (!replaced) next.push([normalizedName, normalizedValue]);\n this.#entries = next;\n this.#onChange?.();\n }\n toString() {\n return this.#entries.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join(\"&\");\n }\n values() {\n return this.#entries.map(([, value]) => value)[Symbol.iterator]();\n }\n [Symbol.iterator]() {\n return this.entries();\n }\n };\n function decodeUrlParam(value) {\n return decodeURIComponent(value.replace(/\\+/gu, \" \"));\n }\n function parseAbsoluteUrl(input) {\n const match = /^(?<protocol>[a-zA-Z][a-zA-Z\\d+.-]*:)\\/\\/(?<host>[^/?#]*)(?<pathname>[^?#]*)?(?<search>\\?[^#]*)?(?<hash>#.*)?$/u.exec(input);\n if (!match?.groups?.protocol || !match.groups.host) return;\n return {\n protocol: match.groups.protocol,\n host: match.groups.host,\n pathname: match.groups.pathname || \"/\",\n search: match.groups.search || \"\",\n hash: match.groups.hash || \"\"\n };\n }\n function normalizePathname(pathname) {\n const segments = [];\n for (const part of pathname.split(\"/\")) {\n if (!part || part === \".\") continue;\n if (part === \"..\") {\n segments.pop();\n continue;\n }\n segments.push(part);\n }\n return `/${segments.join(\"/\")}`;\n }\n function resolveUrl(input, base) {\n const absolute = parseAbsoluteUrl(input);\n if (absolute) return absolute;\n if (base === void 0) throw new TypeError(`Invalid URL: ${input}`);\n const baseUrl = base instanceof URLShim ? base : new URLShim(String(base));\n const basePath = baseUrl.pathname.endsWith(\"/\") ? baseUrl.pathname : baseUrl.pathname.slice(0, baseUrl.pathname.lastIndexOf(\"/\") + 1);\n const [beforeHash, rawHash = \"\"] = input.split(\"#\");\n const hasExplicitSearch = beforeHash?.includes(\"?\") ?? false;\n const [rawPath, rawSearch = \"\"] = (beforeHash ?? \"\").split(\"?\");\n const pathname = rawPath === \"\" && (input === \"\" || input.startsWith(\"?\") || input.startsWith(\"#\")) ? baseUrl.pathname : rawPath?.startsWith(\"/\") ? rawPath : `${basePath}${rawPath || \"\"}`;\n return {\n protocol: baseUrl.protocol,\n host: baseUrl.host,\n pathname: normalizePathname(pathname),\n search: hasExplicitSearch ? `?${rawSearch}` : rawPath === \"\" && (input === \"\" || input.startsWith(\"#\")) ? baseUrl.search : \"\",\n hash: rawHash ? `#${rawHash}` : \"\"\n };\n }\n var URLShim = class {\n host;\n hostname;\n origin;\n password = \"\";\n port;\n protocol;\n searchParams;\n username = \"\";\n hash;\n pathname;\n #search;\n constructor(input, base) {\n const parsed = resolveUrl(String(input), base);\n this.protocol = parsed.protocol;\n this.host = parsed.host;\n const portIndex = this.host.lastIndexOf(\":\");\n this.hostname = portIndex >= 0 ? this.host.slice(0, portIndex) : this.host;\n this.port = portIndex >= 0 ? this.host.slice(portIndex + 1) : \"\";\n this.pathname = parsed.pathname;\n this.#search = parsed.search;\n this.hash = parsed.hash;\n this.origin = `${this.protocol}//${this.host}`;\n this.searchParams = new URLSearchParamsShim(this.#search, () => {\n const next = this.searchParams.toString();\n this.#search = next ? `?${next}` : \"\";\n });\n }\n get href() {\n return `${this.origin}${this.pathname}${this.#search}${this.hash}`;\n }\n get search() {\n return this.#search;\n }\n toString() {\n return this.href;\n }\n toJSON() {\n return this.href;\n }\n };\n var ReadableStreamShim = class {\n #queue = [];\n #closed = false;\n #pulling = false;\n #source;\n #controller;\n #pending;\n constructor(source = {}) {\n this.#source = source;\n this.#controller = {\n enqueue: (value) => {\n if (this.#pending) {\n this.#pending.resolve({\n done: false,\n value\n });\n this.#pending = void 0;\n return;\n }\n this.#queue.push(value);\n },\n close: () => {\n this.#closed = true;\n if (this.#pending) {\n this.#pending.resolve({ done: true });\n this.#pending = void 0;\n }\n }\n };\n source.start?.(this.#controller);\n }\n async #pull() {\n if (this.#pulling || this.#closed || !this.#source.pull) return;\n this.#pulling = true;\n try {\n await this.#source.pull(this.#controller);\n } finally {\n this.#pulling = false;\n }\n }\n getReader() {\n return { read: async () => {\n if (this.#queue.length > 0) return {\n done: false,\n value: this.#queue.shift()\n };\n await this.#pull();\n if (this.#queue.length > 0) return {\n done: false,\n value: this.#queue.shift()\n };\n if (this.#closed) return {\n done: true,\n value: void 0\n };\n return await new Promise((resolve) => {\n this.#pending = { resolve };\n this.#pull();\n });\n } };\n }\n };\n var WritableStreamShim = class {\n #sink;\n constructor(sink = {}) {\n this.#sink = sink;\n }\n getWriter() {\n return {\n write: async (chunk) => {\n await this.#sink.write?.(chunk);\n },\n close: async () => {\n await this.#sink.close?.();\n }\n };\n }\n };\n var TransformStreamShim = class {\n readable;\n writable;\n constructor(transformer = {}) {\n const queue = [];\n let closed = false;\n let pending;\n const controller = {\n enqueue: (value) => {\n if (pending) {\n pending.resolve({\n done: false,\n value\n });\n pending = void 0;\n return;\n }\n queue.push(value);\n },\n close: () => {\n closed = true;\n if (pending) {\n pending.resolve({ done: true });\n pending = void 0;\n }\n }\n };\n this.readable = { getReader() {\n return { read: async () => {\n if (queue.length > 0) return {\n done: false,\n value: queue.shift()\n };\n if (closed) return {\n done: true,\n value: void 0\n };\n return await new Promise((resolve) => {\n pending = { resolve };\n });\n } };\n } };\n this.writable = new WritableStreamShim({\n write: async (chunk) => {\n transformer.transform?.(chunk, controller);\n },\n close: async () => {\n transformer.flush?.(controller);\n controller.close();\n }\n });\n }\n };\n function definePlatformGlobal(name, value, options = {}) {\n if (!options.overwrite && name in globalThis) return;\n Object.defineProperty(globalThis, name, {\n value,\n writable: true,\n configurable: true\n });\n }\n function formatLogArg(value) {\n if (typeof value === \"string\") return value;\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n }\n function formatLogLine(args) {\n return args.map(formatLogArg).join(\" \");\n }\n const platformConsole = {\n log: (...args) => __caplets_log(\"log\", formatLogLine(args)),\n info: (...args) => __caplets_log(\"info\", formatLogLine(args)),\n warn: (...args) => __caplets_log(\"warn\", formatLogLine(args)),\n error: (...args) => __caplets_log(\"error\", formatLogLine(args)),\n debug: (...args) => __caplets_log(\"debug\", formatLogLine(args))\n };\n function normalizeEncoding(encoding) {\n const normalized = (encoding ?? \"utf8\").toLowerCase();\n switch (normalized) {\n case \"utf8\":\n case \"utf-8\":\n case \"base64\":\n case \"base64url\":\n case \"hex\": return normalized;\n default: throw new TypeError(`Unsupported Buffer encoding: ${encoding}`);\n }\n }\n function hexToBytes(value) {\n if (value.length % 2 !== 0) throw new TypeError(\"Invalid hex string length\");\n const bytes = new Uint8Array(value.length / 2);\n for (let index = 0; index < value.length; index += 2) {\n const parsed = Number.parseInt(value.slice(index, index + 2), 16);\n if (Number.isNaN(parsed)) throw new TypeError(\"Invalid hex string\");\n bytes[index / 2] = parsed;\n }\n return bytes;\n }\n function bytesToHex(bytes) {\n return Array.from(bytes, (value) => value.toString(16).padStart(2, \"0\")).join(\"\");\n }\n function base64ToBytes(value, encoding) {\n let normalized = value.replace(/\\s+/gu, \"\");\n if (encoding === \"base64url\") normalized = normalized.replace(/-/gu, \"+\").replace(/_/gu, \"/\");\n const padding = normalized.length % 4;\n if (padding === 1) throw new TypeError(\"Invalid base64 string\");\n if (padding > 0) normalized = normalized.padEnd(normalized.length + (4 - padding), \"=\");\n const output = [];\n for (let index = 0; index < normalized.length; index += 4) {\n const values = normalized.slice(index, index + 4).split(\"\").map((char) => char === \"=\" ? 64 : base64Lookup.get(char) ?? NaN);\n if (values.some((entry) => Number.isNaN(entry))) throw new TypeError(\"Invalid base64 string\");\n const [a = 0, b = 0, c = 64, d = 64] = values;\n const triple = a << 18 | b << 12 | (c & 63) << 6 | d & 63;\n output.push(triple >> 16 & 255);\n if (c !== 64) output.push(triple >> 8 & 255);\n if (d !== 64) output.push(triple & 255);\n }\n return Uint8Array.from(output);\n }\n function bytesToBase64(bytes, encoding) {\n let output = \"\";\n for (let index = 0; index < bytes.length; index += 3) {\n const a = bytes[index] ?? 0;\n const b = bytes[index + 1] ?? 0;\n const c = bytes[index + 2] ?? 0;\n const triple = a << 16 | b << 8 | c;\n output += BASE64_ALPHABET[triple >> 18 & 63];\n output += BASE64_ALPHABET[triple >> 12 & 63];\n output += index + 1 < bytes.length ? BASE64_ALPHABET[triple >> 6 & 63] : \"=\";\n output += index + 2 < bytes.length ? BASE64_ALPHABET[triple & 63] : \"=\";\n }\n if (encoding === \"base64url\") return output.replace(/\\+/gu, \"-\").replace(/\\//gu, \"_\").replace(/=+$/gu, \"\");\n return output;\n }\n function copyBytes(input) {\n if (input instanceof ArrayBuffer) return new Uint8Array(input.slice(0));\n return new Uint8Array(input.buffer.slice(input.byteOffset, input.byteOffset + input.byteLength));\n }\n function toBytes(input, encoding) {\n if (input instanceof BufferShim) return input.toUint8Array();\n if (typeof input === \"string\") switch (normalizeEncoding(encoding)) {\n case \"utf8\":\n case \"utf-8\": return textEncoder.encode(input);\n case \"base64\":\n case \"base64url\": return base64ToBytes(input, normalizeEncoding(encoding));\n case \"hex\": return hexToBytes(input);\n }\n if (input instanceof ArrayBuffer || ArrayBuffer.isView(input)) return copyBytes(input);\n if (Array.isArray(input)) return Uint8Array.from(input);\n throw new TypeError(\"Buffer.from only supports strings, arrays, ArrayBuffers, and typed arrays\");\n }\n var BufferShim = class BufferShim {\n #bytes;\n byteLength;\n length;\n constructor(bytes) {\n this.#bytes = bytes;\n this.byteLength = bytes.byteLength;\n this.length = bytes.length;\n }\n static from(input, encoding) {\n return new BufferShim(toBytes(input, encoding));\n }\n static isBuffer(value) {\n return value instanceof BufferShim;\n }\n static byteLength(input, encoding) {\n return toBytes(input, encoding).byteLength;\n }\n toUint8Array() {\n return new Uint8Array(this.#bytes);\n }\n toString(encoding) {\n switch (normalizeEncoding(encoding)) {\n case \"utf8\":\n case \"utf-8\": return textDecoder.decode(this.#bytes);\n case \"base64\":\n case \"base64url\": return bytesToBase64(this.#bytes, normalizeEncoding(encoding));\n case \"hex\": return bytesToHex(this.#bytes);\n }\n }\n };\n function atobShim(input) {\n return Array.from(base64ToBytes(String(input), \"base64\"), (value) => String.fromCharCode(value)).join(\"\");\n }\n function btoaShim(input) {\n const bytes = new Uint8Array(input.length);\n for (let index = 0; index < input.length; index += 1) {\n const codePoint = input.charCodeAt(index);\n if (codePoint > 255) throw new TypeError(\"The string to be encoded contains characters outside of Latin1\");\n bytes[index] = codePoint;\n }\n return bytesToBase64(bytes, \"base64\");\n }\n function queueMicrotaskShim(callback) {\n Promise.resolve().then(callback);\n }\n var AbortSignalShim = class AbortSignalShim {\n aborted = false;\n reason;\n onabort = null;\n #listeners = /* @__PURE__ */ new Set();\n addEventListener(type, listener) {\n if (type === \"abort\" && listener) this.#listeners.add(listener);\n }\n removeEventListener(type, listener) {\n if (type === \"abort\" && listener) this.#listeners.delete(listener);\n }\n dispatchEvent(event) {\n if (event.type !== \"abort\") return true;\n this.onabort?.(event);\n for (const listener of this.#listeners) listener(event);\n return true;\n }\n throwIfAborted() {\n if (this.aborted) throw this.reason ?? /* @__PURE__ */ new Error(\"Operation was aborted\");\n }\n static abort(reason) {\n const signal = new AbortSignalShim();\n signal.abort(reason);\n return signal;\n }\n abort(reason) {\n if (this.aborted) return;\n this.aborted = true;\n this.reason = reason;\n this.dispatchEvent({\n type: \"abort\",\n target: this\n });\n }\n };\n var AbortControllerShim = class {\n signal = new AbortSignalShim();\n abort(reason) {\n this.signal.abort(reason);\n }\n };\n function cloneFormData(input) {\n const clone = new FormData();\n for (const [name, value] of input.entries()) {\n if (value instanceof File) {\n clone.append(name, new File([value], value.name, { type: value.type }), value.name);\n continue;\n }\n clone.append(name, value);\n }\n return clone;\n }\n function cloneBodyValue(input) {\n if (input === null || input === void 0 || typeof input === \"string\") return input;\n if (input instanceof FormData) return cloneFormData(input);\n if (input instanceof Blob) return input.slice(0, input.size, input.type);\n if (input instanceof ArrayBuffer || ArrayBuffer.isView(input)) return copyBytes(input);\n return input;\n }\n function blobFromBody(input) {\n if (input instanceof Blob) return input;\n if (typeof input === \"string\") return new Blob([input], { type: \"text/plain;charset=utf-8\" });\n if (input instanceof FormData) throw new TypeError(\"FormData body reading is not supported in Code Mode\");\n if (input instanceof ArrayBuffer || ArrayBuffer.isView(input)) return new Blob([copyBytes(input)]);\n return new Blob([]);\n }\n var BodyMixin = class {\n _bodyInit;\n bodyUsed = false;\n body = null;\n constructor(body) {\n this._bodyInit = cloneBodyValue(body);\n }\n async arrayBuffer() {\n this.bodyUsed = true;\n return await blobFromBody(this._bodyInit).arrayBuffer();\n }\n async blob() {\n this.bodyUsed = true;\n return blobFromBody(this._bodyInit);\n }\n async formData() {\n this.bodyUsed = true;\n if (this._bodyInit instanceof FormData) return cloneFormData(this._bodyInit);\n throw new TypeError(\"Body does not contain FormData\");\n }\n async json() {\n return JSON.parse(await this.text());\n }\n async text() {\n this.bodyUsed = true;\n return await blobFromBody(this._bodyInit).text();\n }\n };\n function toHeaders(init) {\n return init instanceof Headers ? new Headers(init) : new Headers(init ?? {});\n }\n function normalizeMethod(method) {\n return String(method ?? \"GET\").toUpperCase();\n }\n var RequestShim = class RequestShim extends BodyMixin {\n headers;\n method;\n signal;\n url;\n constructor(input, init = {}) {\n const sourceBody = input instanceof RequestShim ? input._bodyInit : void 0;\n super(init.body ?? sourceBody);\n if (input instanceof RequestShim) this.url = input.url;\n else this.url = input instanceof URLShim ? input.href : new URLShim(String(input)).href;\n this.method = normalizeMethod(init.method ?? (input instanceof RequestShim ? input.method : \"GET\"));\n this.headers = toHeaders(init.headers ?? (input instanceof RequestShim ? input.headers : void 0));\n this.signal = init.signal ?? (input instanceof RequestShim ? input.signal : new AbortControllerShim().signal);\n }\n clone() {\n return new RequestShim(this, {\n method: this.method,\n headers: this.headers,\n body: this._bodyInit,\n signal: this.signal\n });\n }\n };\n var ResponseShim = class ResponseShim extends BodyMixin {\n headers;\n ok;\n redirected = false;\n status;\n statusText;\n type = \"default\";\n url = \"\";\n constructor(body, init = {}) {\n super(body);\n this.status = init.status ?? 200;\n this.statusText = init.statusText ?? \"\";\n this.headers = toHeaders(init.headers);\n this.ok = this.status >= 200 && this.status <= 299;\n }\n clone() {\n return new ResponseShim(this._bodyInit, {\n headers: this.headers,\n status: this.status,\n statusText: this.statusText\n });\n }\n static json(data, init) {\n const headers = toHeaders(init?.headers);\n if (!headers.has(\"content-type\")) headers.set(\"content-type\", \"application/json\");\n const responseInit = { headers };\n if (init?.status !== void 0) responseInit.status = init.status;\n if (init?.statusText !== void 0) responseInit.statusText = init.statusText;\n return new ResponseShim(JSON.stringify(data), responseInit);\n }\n };\n function disabledFetch() {\n throw new Error(DISABLED_FETCH_MESSAGE);\n }\n const integerTypedArrayConstructors = new Set([\n Int8Array,\n Uint8Array,\n Uint8ClampedArray,\n Int16Array,\n Uint16Array,\n Int32Array,\n Uint32Array,\n typeof BigInt64Array === \"undefined\" ? void 0 : BigInt64Array,\n typeof BigUint64Array === \"undefined\" ? void 0 : BigUint64Array\n ]);\n var QuotaExceededErrorShim = class extends Error {\n constructor(message) {\n super(message);\n this.name = \"QuotaExceededError\";\n }\n };\n const platformCrypto = {\n randomUUID() {\n const randomUUID = capletsPlatformHost.randomUUID;\n if (!randomUUID) throw new Error(\"Code Mode platform random UUID bridge is not installed\");\n return randomUUID();\n },\n getRandomValues(typedArray) {\n if (!ArrayBuffer.isView(typedArray) || typedArray instanceof DataView) throw new TypeError(\"crypto.getRandomValues requires an integer typed array\");\n if (!integerTypedArrayConstructors.has(typedArray.constructor)) throw new TypeError(\"crypto.getRandomValues requires an integer typed array\");\n if (typedArray.byteLength > 65536) throw new QuotaExceededErrorShim(\"crypto.getRandomValues cannot generate more than 65,536 bytes\");\n const randomValues = capletsPlatformHost.randomValues;\n if (!randomValues) throw new Error(\"Code Mode platform random values bridge is not installed\");\n const bytes = randomValues(typedArray.byteLength);\n new Uint8Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength).set(bytes);\n return typedArray;\n }\n };\n let nextTimerId = 1;\n const activeTimers = /* @__PURE__ */ new Set();\n function normalizeTimerDelay(delay) {\n const value = Number(delay ?? 0);\n if (!Number.isFinite(value) || value <= 0) return 0;\n return Math.trunc(value);\n }\n function setTimeoutShim(callback, delay, ...args) {\n if (typeof callback !== \"function\") throw new TypeError(\"setTimeout callback must be a function\");\n const timerId = nextTimerId++;\n activeTimers.add(timerId);\n const sleep = capletsPlatformHost.sleep;\n if (!sleep) throw new Error(\"Code Mode platform sleep bridge is not installed\");\n sleep(timerId, normalizeTimerDelay(delay)).then((fired) => {\n activeTimers.delete(timerId);\n if (fired) callback(...args);\n });\n return timerId;\n }\n function clearTimeoutShim(timerId) {\n const id = Number(timerId);\n activeTimers.delete(id);\n capletsPlatformHost.clearTimer?.(id);\n }\n function setIntervalShim(callback, delay, ...args) {\n if (typeof callback !== \"function\") throw new TypeError(\"setInterval callback must be a function\");\n const timerId = nextTimerId++;\n const intervalDelay = normalizeTimerDelay(delay);\n activeTimers.add(timerId);\n const tick = () => {\n const sleep = capletsPlatformHost.sleep;\n if (!sleep) throw new Error(\"Code Mode platform sleep bridge is not installed\");\n sleep(timerId, intervalDelay).then((fired) => {\n if (!fired || !activeTimers.has(timerId)) {\n activeTimers.delete(timerId);\n return;\n }\n callback(...args);\n if (activeTimers.has(timerId)) tick();\n });\n };\n tick();\n return timerId;\n }\n function clearIntervalShim(timerId) {\n clearTimeoutShim(timerId);\n }\n definePlatformGlobal(\"atob\", atobShim);\n definePlatformGlobal(\"btoa\", btoaShim);\n definePlatformGlobal(\"Buffer\", BufferShim);\n definePlatformGlobal(\"TextEncoder\", TextEncoderShim);\n definePlatformGlobal(\"TextDecoder\", TextDecoderShim);\n definePlatformGlobal(\"URL\", URLShim);\n definePlatformGlobal(\"URLSearchParams\", URLSearchParamsShim);\n definePlatformGlobal(\"structuredClone\", esm_default);\n definePlatformGlobal(\"Headers\", Headers);\n definePlatformGlobal(\"Blob\", Blob);\n definePlatformGlobal(\"File\", File);\n definePlatformGlobal(\"FormData\", FormData);\n definePlatformGlobal(\"ReadableStream\", ReadableStreamShim);\n definePlatformGlobal(\"WritableStream\", WritableStreamShim);\n definePlatformGlobal(\"TransformStream\", TransformStreamShim);\n definePlatformGlobal(\"AbortController\", AbortControllerShim);\n definePlatformGlobal(\"AbortSignal\", AbortSignalShim);\n definePlatformGlobal(\"Request\", RequestShim);\n definePlatformGlobal(\"Response\", ResponseShim);\n definePlatformGlobal(\"crypto\", platformCrypto);\n definePlatformGlobal(\"setTimeout\", setTimeoutShim);\n definePlatformGlobal(\"clearTimeout\", clearTimeoutShim);\n definePlatformGlobal(\"setInterval\", setIntervalShim);\n definePlatformGlobal(\"clearInterval\", clearIntervalShim);\n definePlatformGlobal(\"queueMicrotask\", queueMicrotaskShim);\n definePlatformGlobal(\"console\", platformConsole);\n definePlatformGlobal(\"fetch\", disabledFetch, { overwrite: true });\n //#endregion\n})();";
|
|
77193
|
+
//#endregion
|
|
75661
77194
|
//#region src/code-mode/sandbox.ts
|
|
75662
77195
|
var QuickJsCodeModeSandbox = class {
|
|
75663
77196
|
async run(input) {
|
|
@@ -75686,6 +77219,7 @@ async function evaluateInQuickJs(input) {
|
|
|
75686
77219
|
});
|
|
75687
77220
|
context.setProp(context.global, "__caplets_log", logBridge);
|
|
75688
77221
|
logBridge.dispose();
|
|
77222
|
+
const platformHost = installCodeModePlatformHost(context, pendingDeferreds, {});
|
|
75689
77223
|
const invokeBridge = createInvokeBridge(context, pendingDeferreds, input.invoke, deadlineMs, timeoutMs);
|
|
75690
77224
|
context.setProp(context.global, "__caplets_invoke", invokeBridge);
|
|
75691
77225
|
invokeBridge.dispose();
|
|
@@ -75745,6 +77279,7 @@ async function evaluateInQuickJs(input) {
|
|
|
75745
77279
|
};
|
|
75746
77280
|
} finally {
|
|
75747
77281
|
stateHandle.dispose();
|
|
77282
|
+
platformHost.dispose();
|
|
75748
77283
|
}
|
|
75749
77284
|
} finally {
|
|
75750
77285
|
for (const deferred of pendingDeferreds) if (deferred.alive) deferred.dispose();
|
|
@@ -75804,19 +77339,7 @@ function buildExecutionSource(code, capletIds) {
|
|
|
75804
77339
|
} }).outputText;
|
|
75805
77340
|
return [
|
|
75806
77341
|
"\"use strict\";",
|
|
75807
|
-
|
|
75808
|
-
" if (typeof value === 'string') return value;",
|
|
75809
|
-
" try { return JSON.stringify(value); } catch { return String(value); }",
|
|
75810
|
-
"};",
|
|
75811
|
-
"const __formatLogLine = (args) => args.map(__formatLogArg).join(' ');",
|
|
75812
|
-
"const console = {",
|
|
75813
|
-
" log: (...args) => __caplets_log('log', __formatLogLine(args)),",
|
|
75814
|
-
" info: (...args) => __caplets_log('info', __formatLogLine(args)),",
|
|
75815
|
-
" warn: (...args) => __caplets_log('warn', __formatLogLine(args)),",
|
|
75816
|
-
" error: (...args) => __caplets_log('error', __formatLogLine(args)),",
|
|
75817
|
-
" debug: (...args) => __caplets_log('debug', __formatLogLine(args)),",
|
|
75818
|
-
"};",
|
|
75819
|
-
"const fetch = () => { throw new Error('fetch is disabled in Code Mode'); };",
|
|
77342
|
+
CODE_MODE_PLATFORM_RUNTIME_SOURCE,
|
|
75820
77343
|
"const __invoke = (capletId, method, args) => Promise.resolve(__caplets_invoke(capletId, method, args)).then(JSON.parse);",
|
|
75821
77344
|
"const __handle = (capletId) => ({",
|
|
75822
77345
|
" id: capletId,",
|
|
@@ -77831,7 +79354,7 @@ var DefaultNativeCapletsService = class {
|
|
|
77831
79354
|
...options,
|
|
77832
79355
|
writeErr: this.writeErr
|
|
77833
79356
|
});
|
|
77834
|
-
this.postReloadRefresh = this.refreshExposureSnapshot({ emitToolsChanged: this.
|
|
79357
|
+
this.postReloadRefresh = this.refreshExposureSnapshot({ emitToolsChanged: this.hasSnapshotBackedDirectExposure() });
|
|
77835
79358
|
this.unsubscribeEngineReload = this.engine.onReload(() => {
|
|
77836
79359
|
this.postReloadRefresh = this.refreshExposureSnapshot({ emitToolsChanged: true });
|
|
77837
79360
|
});
|
|
@@ -77900,13 +79423,13 @@ var DefaultNativeCapletsService = class {
|
|
|
77900
79423
|
...action.outputSchema ? { outputSchema: action.outputSchema } : {},
|
|
77901
79424
|
...action.annotations ? { annotations: action.annotations } : {}
|
|
77902
79425
|
}));
|
|
77903
|
-
if (caplet.backend === "mcp") return [...snapshot?.directTools.filter((entry) => entry.caplet.server === caplet.server).map((entry) => this.
|
|
79426
|
+
if (caplet.backend === "mcp") return [...snapshot?.directTools.filter((entry) => entry.caplet.server === caplet.server).map((entry) => this.directDiscoveredTool(caplet, entry)) ?? [], ...mcpPrimitiveNativeTools(caplet, snapshot).map((operationName) => this.directNativeTool(caplet, operationName, {
|
|
77904
79427
|
description: `MCP ${operationName.replace(/_/g, " ")}.`,
|
|
77905
79428
|
inputSchema: nativeMcpPrimitiveInputSchema(operationName)
|
|
77906
79429
|
}))];
|
|
77907
|
-
return [];
|
|
79430
|
+
return snapshot?.directTools.filter((entry) => entry.caplet.server === caplet.server).map((entry) => this.directDiscoveredTool(caplet, entry)) ?? [];
|
|
77908
79431
|
}
|
|
77909
|
-
|
|
79432
|
+
directDiscoveredTool(caplet, entry) {
|
|
77910
79433
|
return this.directNativeTool(caplet, entry.downstreamName, {
|
|
77911
79434
|
...entry.tool.description ? { description: entry.tool.description } : {},
|
|
77912
79435
|
...entry.tool.inputSchema ? { inputSchema: entry.tool.inputSchema } : {},
|
|
@@ -77948,9 +79471,10 @@ var DefaultNativeCapletsService = class {
|
|
|
77948
79471
|
this.writeErr(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
77949
79472
|
}
|
|
77950
79473
|
}
|
|
77951
|
-
|
|
79474
|
+
hasSnapshotBackedDirectExposure() {
|
|
77952
79475
|
return this.engine.enabledServers().some((caplet) => {
|
|
77953
|
-
if (caplet.
|
|
79476
|
+
if (caplet.setup || caplet.projectBinding?.required) return false;
|
|
79477
|
+
if (caplet.backend === "http" || caplet.backend === "cli") return false;
|
|
77954
79478
|
return resolveExposure(caplet.exposure, this.engine.currentConfig().options.exposure).direct;
|
|
77955
79479
|
});
|
|
77956
79480
|
}
|
|
@@ -78447,4 +79971,4 @@ function errorMessage(error) {
|
|
|
78447
79971
|
return error instanceof Error ? error.message : String(error);
|
|
78448
79972
|
}
|
|
78449
79973
|
//#endregion
|
|
78450
|
-
export {
|
|
79974
|
+
export { loadConfigWithSources as $, ListResourcesRequestSchema as $t, codeModeRunParamsSchema as A, assertClientRequestTaskCapability as At, generateCodeModeRunToolDescription as B, CreateMessageResultWithToolsSchema as Bt, nativeCapletPromptGuidance as C, __exportAll as Cn, defaultStateBaseDir as Ct, nativeCodeModeToolId as D, resolveProjectConfigPath as Dt, nativeCapletsSystemGuidance as E, resolveProjectCapletsRoot as Et, listCodeModeCallableCaplets as F, toJsonSchemaCompat as Ft, directResourceUriMatchesTemplate as G, ErrorCode as Gt, CapletsEngine as H, DEFAULT_NEGOTIATED_PROTOCOL_VERSION as Ht, CodeModeLogStore as I, CallToolRequestSchema as It, handleServerTool as J, InitializedNotificationSchema as Jt, findProjectRoot as K, GetPromptRequestSchema as Kt, redactCodeModeLogText as L, CallToolResultSchema as Lt, QuickJsCodeModeSandbox as M, AjvJsonSchemaValidator as Mt, diagnoseCodeModeTypeScript as N, Protocol as Nt, nativeCodeModeToolName as O, ReadBuffer as Ot, createCodeModeCapletsApi as P, mergeCapabilities as Pt, loadConfig as Q, ListResourceTemplatesRequestSchema as Qt, codeModeDeclarationHash as R, CompleteRequestSchema as Rt, resolveCapletsServer as S, safeParseAsync as Sn, defaultConfigBaseDir as St, nativeCapletToolName as T, resolveConfigPath as Tt, resolveExposure as U, ElicitResultSchema as Ut, minifyCodeModeDeclarationText as V, CreateTaskResultSchema as Vt, decodeDirectResourceUri as W, EmptyResultSchema as Wt, capabilityDescription as X, LATEST_PROTOCOL_VERSION as Xt, ServerRegistry as Y, JSONRPCMessageSchema as Yt, GoogleDiscoveryManager as Z, ListPromptsRequestSchema as Zt, resolveHostedCloudRemote as _, isSchemaOptional as _n, readTokenBundle as _t, projectBindingError as a, SUPPORTED_PROTOCOL_VERSIONS as an, validateCapletFile as at, parseServerBaseUrl as b, objectFromShape as bn, DEFAULT_OBSERVED_OUTPUT_SHAPE_CACHE_DIR as bt, cloudAuthPath as c, assertCompleteRequestResourceTemplate as cn, markdownCallToolResultContent as ct, CloudAuthClient as d, isJSONRPCRequest as dn, runGenericOAuthFlow as dt, ListRootsResultSchema as en, loadGlobalConfig as et, RemoteNativeCapletsService as f, isJSONRPCResultResponse as fn, runOAuthFlow as ft, resolveCapletsRemote as g, getSchemaDescription as gn, isTokenBundleExpired as gt, resolveNativeCapletsServiceOptions as h, getParseErrorMessage as hn, deleteTokenBundle as ht, ProjectBindingError as i, ReadResourceRequestSchema as in, discoverCapletFiles as it, runCodeMode as j, assertToolsCallTaskCapability as jt, codeModeRunInputSchema as k, serializeMessage as kt, migrateCredentials as l, isInitializeRequest as ln, markdownStructuredContent as lt, buildProjectSyncManifest as m, getObjectShape as mn, startOAuthFlow as mt, resolveRemoteSelection as n, LoggingLevelSchema as nn, loadProjectConfig as nt, projectBindingRecovery as o, SetLevelRequestSchema as on, loadCapletFilesFromMap as ot, createSdkRemoteCapletsClient as p, getLiteralValue as pn, startGenericOAuthFlow as pt, fingerprintProjectRoot as q, InitializeRequestSchema as qt, PROJECT_BINDING_ERROR_CODES as r, McpError as rn, parseConfig as rt, CloudAuthStore as s, assertCompleteRequestPrompt as sn, hasRenderableStructuredContent as st, createNativeCapletsService as t, ListToolsRequestSchema as tn, loadLocalOverlayConfigWithSources as tt, redactedCloudAuthStatus as u, isJSONRPCErrorResponse as un, refreshOAuthTokenBundle as ut, resolveRemoteMode as v, isZ4Schema as vn, DEFAULT_AUTH_DIR as vt, nativeCapletToolDescription as w, resolveCapletsRoot as wt, resolveCapletsMode as x, safeParse as xn, defaultCacheBaseDir as xt, controlUrlForBase as y, normalizeObjectSchema as yn, DEFAULT_COMPLETION_CACHE_DIR as yt, generateCodeModeDeclarations as z, CreateMessageResultSchema as zt };
|