@caplets/core 0.21.1 → 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 +66 -0
- package/dist/caplet-sets.d.ts +2 -0
- package/dist/caplet-source.js +101 -4
- package/dist/cli/add.d.ts +10 -0
- package/dist/cli/auth.d.ts +24 -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/types.d.ts +1 -0
- package/dist/code-mode.js +18 -13
- package/dist/{completion-Cb-pshjL.js → completion-BC4BNWo0.js} +14 -2
- package/dist/config/paths.d.ts +2 -0
- package/dist/config-runtime.d.ts +15 -1
- package/dist/config-runtime.js +46 -0
- package/dist/config.d.ts +32 -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 +111 -26
- 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/remote.d.ts +1 -0
- package/dist/native/service.d.ts +2 -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-lkrQheFA.js → service-CSRCJfpA.js} +1776 -215
- 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
|
|
@@ -25967,6 +26005,7 @@ const capletExposureSchema = _enum([
|
|
|
25967
26005
|
"direct_and_code_mode",
|
|
25968
26006
|
"progressive_and_code_mode"
|
|
25969
26007
|
]).describe("How this Caplet is exposed to agents.");
|
|
26008
|
+
const capletShadowingSchema = _enum(["forbid", "allow"]).describe("Whether attached local Caplets may shadow this remote Caplet ID.");
|
|
25970
26009
|
const capletEndpointAuthSchema = discriminatedUnion("type", [
|
|
25971
26010
|
object$1({ type: literal("none") }).strict(),
|
|
25972
26011
|
object$1({
|
|
@@ -26099,6 +26138,36 @@ const capletOpenApiEndpointSchema = object$1({
|
|
|
26099
26138
|
});
|
|
26100
26139
|
validateEndpointAuthHeaders$1(endpoint.auth, ctx);
|
|
26101
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
|
+
});
|
|
26102
26171
|
const capletGraphQlOperationSchema = object$1({
|
|
26103
26172
|
document: string().min(1).optional().describe("Inline GraphQL operation document."),
|
|
26104
26173
|
documentPath: string().min(1).optional().describe("Path to a GraphQL operation document."),
|
|
@@ -26242,25 +26311,27 @@ const capletSetSchema = object$1({
|
|
|
26242
26311
|
});
|
|
26243
26312
|
});
|
|
26244
26313
|
const capletFileSchema = object$1({
|
|
26245
|
-
$schema: string().
|
|
26314
|
+
$schema: string().optional().describe("Optional JSON Schema for editor validation."),
|
|
26246
26315
|
name: string().trim().min(1).max(80).describe("Human-readable Caplet display name."),
|
|
26247
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"),
|
|
26248
26317
|
tags: array(string().trim().min(1).max(80)).optional().describe("Optional tags for grouping or searching Caplets."),
|
|
26249
26318
|
exposure: capletExposureSchema.optional(),
|
|
26319
|
+
shadowing: capletShadowingSchema.optional(),
|
|
26250
26320
|
...capletAgentSelectionHintsSchema,
|
|
26251
26321
|
setup: capletSetupSchema.optional(),
|
|
26252
26322
|
projectBinding: capletProjectBindingSchema.optional(),
|
|
26253
26323
|
runtime: capletRuntimeRequirementsSchema.optional(),
|
|
26254
26324
|
mcpServer: capletMcpServerSchema.describe("MCP server backend configuration for this Caplet.").optional(),
|
|
26255
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(),
|
|
26256
26327
|
graphqlEndpoint: capletGraphQlEndpointSchema.describe("GraphQL endpoint backend configuration for this Caplet.").optional(),
|
|
26257
26328
|
httpApi: capletHttpApiSchema.describe("HTTP API backend configuration for this Caplet.").optional(),
|
|
26258
26329
|
cliTools: capletCliToolsSchema.describe("CLI tools backend configuration for this Caplet.").optional(),
|
|
26259
26330
|
capletSet: capletSetSchema.describe("Nested Caplet collection backend configuration for this Caplet.").optional()
|
|
26260
26331
|
}).strict().superRefine((frontmatter, ctx) => {
|
|
26261
|
-
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({
|
|
26262
26333
|
code: "custom",
|
|
26263
|
-
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"
|
|
26264
26335
|
});
|
|
26265
26336
|
});
|
|
26266
26337
|
function loadCapletFilesFromMap(input) {
|
|
@@ -26279,13 +26350,14 @@ function loadCapletFilesFromMap(input) {
|
|
|
26279
26350
|
function buildCapletFileLoadResultFromEntries(root, candidates, readConfig, warnings) {
|
|
26280
26351
|
const servers = {};
|
|
26281
26352
|
const openapiEndpoints = {};
|
|
26353
|
+
const googleDiscoveryApis = {};
|
|
26282
26354
|
const graphqlEndpoints = {};
|
|
26283
26355
|
const httpApis = {};
|
|
26284
26356
|
const cliTools = {};
|
|
26285
26357
|
const capletSets = {};
|
|
26286
26358
|
const paths = {};
|
|
26287
26359
|
function hasId(id) {
|
|
26288
|
-
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]);
|
|
26289
26361
|
}
|
|
26290
26362
|
for (const candidate of candidates) {
|
|
26291
26363
|
if (hasId(candidate.id)) {
|
|
@@ -26312,6 +26384,9 @@ function buildCapletFileLoadResultFromEntries(root, candidates, readConfig, warn
|
|
|
26312
26384
|
if (isPlainObject$6(config) && config.backend === "openapi") {
|
|
26313
26385
|
const { backend: _backend, ...endpoint } = config;
|
|
26314
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;
|
|
26315
26390
|
} else if (isPlainObject$6(config) && config.backend === "graphql") {
|
|
26316
26391
|
const { backend: _backend, ...endpoint } = config;
|
|
26317
26392
|
graphqlEndpoints[candidate.id] = endpoint;
|
|
@@ -26328,6 +26403,7 @@ function buildCapletFileLoadResultFromEntries(root, candidates, readConfig, warn
|
|
|
26328
26403
|
}
|
|
26329
26404
|
const hasServers = Object.keys(servers).length > 0;
|
|
26330
26405
|
const hasOpenApi = Object.keys(openapiEndpoints).length > 0;
|
|
26406
|
+
const hasGoogleDiscovery = Object.keys(googleDiscoveryApis).length > 0;
|
|
26331
26407
|
const hasGraphQl = Object.keys(graphqlEndpoints).length > 0;
|
|
26332
26408
|
const hasHttpApis = Object.keys(httpApis).length > 0;
|
|
26333
26409
|
const hasCliTools = Object.keys(cliTools).length > 0;
|
|
@@ -26335,6 +26411,7 @@ function buildCapletFileLoadResultFromEntries(root, candidates, readConfig, warn
|
|
|
26335
26411
|
const config = {
|
|
26336
26412
|
...hasServers ? { mcpServers: servers } : {},
|
|
26337
26413
|
...hasOpenApi ? { openapiEndpoints } : {},
|
|
26414
|
+
...hasGoogleDiscovery ? { googleDiscoveryApis } : {},
|
|
26338
26415
|
...hasGraphQl ? { graphqlEndpoints } : {},
|
|
26339
26416
|
...hasHttpApis ? { httpApis } : {},
|
|
26340
26417
|
...hasCliTools ? { cliTools } : {},
|
|
@@ -26394,6 +26471,15 @@ function capletToServerConfig(frontmatter, body, baseDir, normalizePath) {
|
|
|
26394
26471
|
...sharedCapletFields(frontmatter),
|
|
26395
26472
|
body
|
|
26396
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
|
+
};
|
|
26397
26483
|
if (frontmatter.graphqlEndpoint) return {
|
|
26398
26484
|
...frontmatter.graphqlEndpoint,
|
|
26399
26485
|
schemaPath: normalizePath(frontmatter.graphqlEndpoint.schemaPath, baseDir),
|
|
@@ -26444,6 +26530,7 @@ function sharedCapletFields(frontmatter) {
|
|
|
26444
26530
|
return {
|
|
26445
26531
|
...frontmatter.tags ? { tags: frontmatter.tags } : {},
|
|
26446
26532
|
...frontmatter.exposure ? { exposure: frontmatter.exposure } : {},
|
|
26533
|
+
...frontmatter.shadowing ? { shadowing: frontmatter.shadowing } : {},
|
|
26447
26534
|
...frontmatter.useWhen ? { useWhen: frontmatter.useWhen } : {},
|
|
26448
26535
|
...frontmatter.avoidWhen ? { avoidWhen: frontmatter.avoidWhen } : {},
|
|
26449
26536
|
...frontmatter.setup ? { setup: frontmatter.setup } : {},
|
|
@@ -26794,6 +26881,7 @@ const exposureSchema = _enum([
|
|
|
26794
26881
|
"direct_and_code_mode",
|
|
26795
26882
|
"progressive_and_code_mode"
|
|
26796
26883
|
]).describe("How this Caplet is exposed to agents.");
|
|
26884
|
+
const shadowingSchema = _enum(["forbid", "allow"]).default("forbid").describe("Whether attached local Caplets may shadow this remote Caplet ID.");
|
|
26797
26885
|
const publicServerSchema = object$1({
|
|
26798
26886
|
name: string().trim().min(1).max(80).describe("Human-readable server display name."),
|
|
26799
26887
|
description: string().describe("Capability description shown to agents before downstream tools 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"),
|
|
@@ -26810,6 +26898,7 @@ const publicServerSchema = object$1({
|
|
|
26810
26898
|
auth: remoteAuthSchema.optional(),
|
|
26811
26899
|
tags: array(string().trim().min(1).max(80)).optional(),
|
|
26812
26900
|
exposure: exposureSchema.optional(),
|
|
26901
|
+
shadowing: shadowingSchema,
|
|
26813
26902
|
...agentSelectionHintsSchema,
|
|
26814
26903
|
setup: setupSchema.optional(),
|
|
26815
26904
|
projectBinding: projectBindingSchema.optional(),
|
|
@@ -26829,6 +26918,7 @@ const publicOpenApiEndpointSchema = object$1({
|
|
|
26829
26918
|
auth: openApiAuthSchema.describe("Explicit OpenAPI request auth config. Use {\"type\":\"none\"} for public APIs."),
|
|
26830
26919
|
tags: array(string().trim().min(1).max(80)).optional(),
|
|
26831
26920
|
exposure: exposureSchema.optional(),
|
|
26921
|
+
shadowing: shadowingSchema,
|
|
26832
26922
|
...agentSelectionHintsSchema,
|
|
26833
26923
|
setup: setupSchema.optional(),
|
|
26834
26924
|
projectBinding: projectBindingSchema.optional(),
|
|
@@ -26838,6 +26928,28 @@ const publicOpenApiEndpointSchema = object$1({
|
|
|
26838
26928
|
disabled: boolean().default(false).describe("When true, omit this OpenAPI Caplet from discovery.")
|
|
26839
26929
|
}).strict();
|
|
26840
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() });
|
|
26841
26953
|
const graphQlOperationSchema = object$1({
|
|
26842
26954
|
document: string().min(1).optional().describe("Inline GraphQL operation document."),
|
|
26843
26955
|
documentPath: string().min(1).optional().describe("Path to a GraphQL operation document."),
|
|
@@ -26861,6 +26973,7 @@ const publicGraphQlEndpointSchema = object$1({
|
|
|
26861
26973
|
auth: openApiAuthSchema.describe("Explicit GraphQL request auth config. Use {\"type\":\"none\"} for public APIs."),
|
|
26862
26974
|
tags: array(string().trim().min(1).max(80)).optional(),
|
|
26863
26975
|
exposure: exposureSchema.optional(),
|
|
26976
|
+
shadowing: shadowingSchema,
|
|
26864
26977
|
...agentSelectionHintsSchema,
|
|
26865
26978
|
setup: setupSchema.optional(),
|
|
26866
26979
|
projectBinding: projectBindingSchema.optional(),
|
|
@@ -26912,6 +27025,7 @@ const publicHttpApiSchema = object$1({
|
|
|
26912
27025
|
actions: record(string().regex(SERVER_ID_PATTERN), httpActionSchema).refine((actions) => Object.keys(actions).length > 0, "HTTP API must define at least one action").describe("Configured HTTP actions keyed by stable tool name."),
|
|
26913
27026
|
tags: array(string().trim().min(1).max(80)).optional(),
|
|
26914
27027
|
exposure: exposureSchema.optional(),
|
|
27028
|
+
shadowing: shadowingSchema,
|
|
26915
27029
|
...agentSelectionHintsSchema,
|
|
26916
27030
|
setup: setupSchema.optional(),
|
|
26917
27031
|
projectBinding: projectBindingSchema.optional(),
|
|
@@ -26950,6 +27064,7 @@ const publicCliToolsSchema = object$1({
|
|
|
26950
27064
|
env: record(string(), string()).optional().describe("Default environment variables for CLI actions."),
|
|
26951
27065
|
tags: array(string().trim().min(1).max(80)).optional(),
|
|
26952
27066
|
exposure: exposureSchema.optional(),
|
|
27067
|
+
shadowing: shadowingSchema,
|
|
26953
27068
|
...agentSelectionHintsSchema,
|
|
26954
27069
|
setup: setupSchema.optional(),
|
|
26955
27070
|
projectBinding: projectBindingSchema.optional(),
|
|
@@ -26969,6 +27084,7 @@ const publicCapletSetSchema = object$1({
|
|
|
26969
27084
|
toolCacheTtlMs: number$1().int().nonnegative().default(3e4).describe("Milliseconds child Caplet metadata stays fresh. Set 0 to refresh every time."),
|
|
26970
27085
|
tags: array(string().trim().min(1).max(80)).optional(),
|
|
26971
27086
|
exposure: exposureSchema.optional(),
|
|
27087
|
+
shadowing: shadowingSchema,
|
|
26972
27088
|
...agentSelectionHintsSchema,
|
|
26973
27089
|
setup: setupSchema.optional(),
|
|
26974
27090
|
projectBinding: projectBindingSchema.optional(),
|
|
@@ -26986,9 +27102,9 @@ const publicCapletSetSchema = object$1({
|
|
|
26986
27102
|
});
|
|
26987
27103
|
});
|
|
26988
27104
|
const normalizedCapletSetSchema = publicCapletSetSchema.extend({ body: string().optional() });
|
|
26989
|
-
function configSchemaFor(serverValueSchema, openApiEndpointValueSchema, graphQlEndpointValueSchema, httpApiValueSchema, cliToolsValueSchema, capletSetValueSchema) {
|
|
27105
|
+
function configSchemaFor(serverValueSchema, openApiEndpointValueSchema, googleDiscoveryApiValueSchema, graphQlEndpointValueSchema, httpApiValueSchema, cliToolsValueSchema, capletSetValueSchema) {
|
|
26990
27106
|
return object$1({
|
|
26991
|
-
$schema: string().
|
|
27107
|
+
$schema: string().optional().describe("Optional JSON Schema for editor validation."),
|
|
26992
27108
|
version: literal(1).default(1).describe("Caplets config schema version."),
|
|
26993
27109
|
defaultSearchLimit: number$1().int().positive().default(20).describe("Default maximum number of same-server search results."),
|
|
26994
27110
|
maxSearchLimit: number$1().int().positive().max(50).default(50).describe("Maximum accepted search_tools limit."),
|
|
@@ -27014,6 +27130,7 @@ function configSchemaFor(serverValueSchema, openApiEndpointValueSchema, graphQlE
|
|
|
27014
27130
|
}).describe("Global Caplets runtime options."),
|
|
27015
27131
|
mcpServers: record(string().regex(SERVER_ID_PATTERN), serverValueSchema).default({}).describe("Downstream MCP servers keyed by stable server ID."),
|
|
27016
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."),
|
|
27017
27134
|
graphqlEndpoints: record(string().regex(SERVER_ID_PATTERN), graphQlEndpointValueSchema).default({}).describe("GraphQL endpoints keyed by stable Caplet ID."),
|
|
27018
27135
|
httpApis: record(string().regex(SERVER_ID_PATTERN), httpApiValueSchema).default({}).describe("HTTP APIs keyed by stable Caplet ID."),
|
|
27019
27136
|
cliTools: record(string().regex(SERVER_ID_PATTERN), cliToolsValueSchema).default({}).describe("CLI tools keyed by stable Caplet ID."),
|
|
@@ -27129,9 +27246,60 @@ function configSchemaFor(serverValueSchema, openApiEndpointValueSchema, graphQlE
|
|
|
27129
27246
|
});
|
|
27130
27247
|
}
|
|
27131
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
|
+
}
|
|
27132
27300
|
for (const [endpoint, rawValue] of Object.entries(config.graphqlEndpoints)) {
|
|
27133
27301
|
const raw = rawValue;
|
|
27134
|
-
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;
|
|
27135
27303
|
if (duplicateBackend) ctx.addIssue({
|
|
27136
27304
|
code: "custom",
|
|
27137
27305
|
path: ["graphqlEndpoints", endpoint],
|
|
@@ -27173,7 +27341,7 @@ function configSchemaFor(serverValueSchema, openApiEndpointValueSchema, graphQlE
|
|
|
27173
27341
|
}
|
|
27174
27342
|
for (const [endpoint, rawValue] of Object.entries(config.httpApis)) {
|
|
27175
27343
|
const raw = rawValue;
|
|
27176
|
-
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;
|
|
27177
27345
|
if (duplicateBackend) ctx.addIssue({
|
|
27178
27346
|
code: "custom",
|
|
27179
27347
|
path: ["httpApis", endpoint],
|
|
@@ -27208,7 +27376,7 @@ function configSchemaFor(serverValueSchema, openApiEndpointValueSchema, graphQlE
|
|
|
27208
27376
|
}
|
|
27209
27377
|
for (const [server, rawValue] of Object.entries(config.cliTools)) {
|
|
27210
27378
|
const raw = rawValue;
|
|
27211
|
-
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;
|
|
27212
27380
|
if (duplicateBackend) ctx.addIssue({
|
|
27213
27381
|
code: "custom",
|
|
27214
27382
|
path: ["cliTools", server],
|
|
@@ -27232,7 +27400,7 @@ function configSchemaFor(serverValueSchema, openApiEndpointValueSchema, graphQlE
|
|
|
27232
27400
|
}
|
|
27233
27401
|
for (const [server, rawValue] of Object.entries(config.capletSets)) {
|
|
27234
27402
|
const raw = rawValue;
|
|
27235
|
-
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;
|
|
27236
27404
|
if (duplicateBackend) ctx.addIssue({
|
|
27237
27405
|
code: "custom",
|
|
27238
27406
|
path: ["capletSets", server],
|
|
@@ -27251,8 +27419,8 @@ function configSchemaFor(serverValueSchema, openApiEndpointValueSchema, graphQlE
|
|
|
27251
27419
|
}
|
|
27252
27420
|
});
|
|
27253
27421
|
}
|
|
27254
|
-
const configFileSchema = configSchemaFor(publicServerSchema, publicOpenApiEndpointSchema, publicGraphQlEndpointSchema, publicHttpApiSchema, publicCliToolsSchema, publicCapletSetSchema);
|
|
27255
|
-
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);
|
|
27256
27424
|
function loadConfig(path = resolveConfigPath(), projectPath = resolveProjectConfigPath()) {
|
|
27257
27425
|
return loadConfigWithSources(path, projectPath).config;
|
|
27258
27426
|
}
|
|
@@ -27293,7 +27461,7 @@ function loadConfigWithSources(path = resolveConfigPath(), projectPath = resolve
|
|
|
27293
27461
|
path: projectCaplets.paths
|
|
27294
27462
|
}
|
|
27295
27463
|
} : void 0
|
|
27296
|
-
], `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");
|
|
27297
27465
|
}
|
|
27298
27466
|
function loadGlobalConfig(path = resolveConfigPath()) {
|
|
27299
27467
|
const userConfig = existsSync(path) ? readPublicConfigInput(path) : void 0;
|
|
@@ -27335,7 +27503,7 @@ function buildConfigWithSources(inputs, notFoundMessage, emptyMessage) {
|
|
|
27335
27503
|
try {
|
|
27336
27504
|
const { input, sources, shadows } = mergeConfigInputsWithSources(...inputs);
|
|
27337
27505
|
const config = parseConfig(input);
|
|
27338
|
-
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);
|
|
27339
27507
|
return {
|
|
27340
27508
|
config,
|
|
27341
27509
|
sources,
|
|
@@ -27424,7 +27592,7 @@ function loadIsolatedConfig(options) {
|
|
|
27424
27592
|
defaultSearchLimit: options.defaultSearchLimit,
|
|
27425
27593
|
maxSearchLimit: options.maxSearchLimit
|
|
27426
27594
|
}));
|
|
27427
|
-
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");
|
|
27428
27596
|
return config;
|
|
27429
27597
|
}
|
|
27430
27598
|
function resolveProjectCapletsRootForConfigPath(projectPath) {
|
|
@@ -27446,6 +27614,7 @@ function normalizeLocalPaths(input, baseDir) {
|
|
|
27446
27614
|
return stripUndefined({
|
|
27447
27615
|
...input,
|
|
27448
27616
|
openapiEndpoints: normalizeEndpointPaths(input.openapiEndpoints, baseDir, normalizeOpenApiPath),
|
|
27617
|
+
googleDiscoveryApis: normalizeEndpointPaths(input.googleDiscoveryApis, baseDir, normalizeGoogleDiscoveryPath),
|
|
27449
27618
|
graphqlEndpoints: normalizeEndpointPaths(input.graphqlEndpoints, baseDir, normalizeGraphQlPath),
|
|
27450
27619
|
cliTools: normalizeEndpointPaths(input.cliTools, baseDir, normalizeCliToolsPaths),
|
|
27451
27620
|
capletSets: normalizeEndpointPaths(input.capletSets, baseDir, normalizeCapletSetPaths)
|
|
@@ -27461,6 +27630,12 @@ function normalizeOpenApiPath(endpoint, baseDir) {
|
|
|
27461
27630
|
specPath: normalizeLocalPath(endpoint.specPath, baseDir)
|
|
27462
27631
|
};
|
|
27463
27632
|
}
|
|
27633
|
+
function normalizeGoogleDiscoveryPath(endpoint, baseDir) {
|
|
27634
|
+
return {
|
|
27635
|
+
...endpoint,
|
|
27636
|
+
discoveryPath: normalizeLocalPath(endpoint.discoveryPath, baseDir)
|
|
27637
|
+
};
|
|
27638
|
+
}
|
|
27464
27639
|
function normalizeGraphQlPath(endpoint, baseDir) {
|
|
27465
27640
|
const operations = isPlainObject$5(endpoint.operations) ? Object.fromEntries(Object.entries(endpoint.operations).map(([name, operation]) => [name, isPlainObject$5(operation) ? {
|
|
27466
27641
|
...operation,
|
|
@@ -27496,6 +27671,7 @@ function normalizeLocalPath(value, baseDir) {
|
|
|
27496
27671
|
}
|
|
27497
27672
|
function rejectProjectConfigExecutableBackendMaps(input, path) {
|
|
27498
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`);
|
|
27499
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`);
|
|
27500
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`);
|
|
27501
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`);
|
|
@@ -27517,6 +27693,10 @@ function mergeConfigInputs(...inputs) {
|
|
|
27517
27693
|
...merged?.openapiEndpoints,
|
|
27518
27694
|
...input.openapiEndpoints
|
|
27519
27695
|
},
|
|
27696
|
+
googleDiscoveryApis: {
|
|
27697
|
+
...merged?.googleDiscoveryApis,
|
|
27698
|
+
...input.googleDiscoveryApis
|
|
27699
|
+
},
|
|
27520
27700
|
graphqlEndpoints: {
|
|
27521
27701
|
...merged?.graphqlEndpoints,
|
|
27522
27702
|
...input.graphqlEndpoints
|
|
@@ -27560,6 +27740,7 @@ function mergeConfigInputsWithSources(...inputs) {
|
|
|
27560
27740
|
function removeCapletId(input, id) {
|
|
27561
27741
|
const { [id]: _mcpServer, ...mcpServers } = input.mcpServers ?? {};
|
|
27562
27742
|
const { [id]: _openapiEndpoint, ...openapiEndpoints } = input.openapiEndpoints ?? {};
|
|
27743
|
+
const { [id]: _googleDiscoveryApi, ...googleDiscoveryApis } = input.googleDiscoveryApis ?? {};
|
|
27563
27744
|
const { [id]: _graphqlEndpoint, ...graphqlEndpoints } = input.graphqlEndpoints ?? {};
|
|
27564
27745
|
const { [id]: _httpApi, ...httpApis } = input.httpApis ?? {};
|
|
27565
27746
|
const { [id]: _cliTools, ...cliTools } = input.cliTools ?? {};
|
|
@@ -27568,6 +27749,7 @@ function removeCapletId(input, id) {
|
|
|
27568
27749
|
...input,
|
|
27569
27750
|
mcpServers,
|
|
27570
27751
|
openapiEndpoints,
|
|
27752
|
+
googleDiscoveryApis,
|
|
27571
27753
|
graphqlEndpoints,
|
|
27572
27754
|
httpApis,
|
|
27573
27755
|
cliTools,
|
|
@@ -27578,6 +27760,7 @@ function capletIds(input) {
|
|
|
27578
27760
|
return [
|
|
27579
27761
|
...Object.keys(input.mcpServers ?? {}),
|
|
27580
27762
|
...Object.keys(input.openapiEndpoints ?? {}),
|
|
27763
|
+
...Object.keys(input.googleDiscoveryApis ?? {}),
|
|
27581
27764
|
...Object.keys(input.graphqlEndpoints ?? {}),
|
|
27582
27765
|
...Object.keys(input.httpApis ?? {}),
|
|
27583
27766
|
...Object.keys(input.cliTools ?? {}),
|
|
@@ -27609,6 +27792,12 @@ function parseConfig(input) {
|
|
|
27609
27792
|
server,
|
|
27610
27793
|
backend: "openapi"
|
|
27611
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
|
+
});
|
|
27612
27801
|
const graphqlEndpoints = {};
|
|
27613
27802
|
for (const [server, raw] of Object.entries(parsed.data.graphqlEndpoints)) graphqlEndpoints[server] = stripUndefined({
|
|
27614
27803
|
...raw,
|
|
@@ -27645,6 +27834,7 @@ function parseConfig(input) {
|
|
|
27645
27834
|
},
|
|
27646
27835
|
mcpServers: servers,
|
|
27647
27836
|
openapiEndpoints,
|
|
27837
|
+
googleDiscoveryApis,
|
|
27648
27838
|
graphqlEndpoints,
|
|
27649
27839
|
httpApis,
|
|
27650
27840
|
cliTools,
|
|
@@ -27677,7 +27867,7 @@ function interpolateConfig(value, path = []) {
|
|
|
27677
27867
|
return value;
|
|
27678
27868
|
}
|
|
27679
27869
|
function isPublicMetadataPath(path) {
|
|
27680
|
-
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;
|
|
27681
27871
|
return NON_INTERPOLATED_SERVER_FIELDS.has(path[2] ?? "");
|
|
27682
27872
|
}
|
|
27683
27873
|
function isPlainObject$5(value) {
|
|
@@ -27690,6 +27880,1272 @@ function interpolateEnv(value) {
|
|
|
27690
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] ?? "");
|
|
27691
27881
|
}
|
|
27692
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
|
|
27693
29149
|
//#region ../../node_modules/.pnpm/graphql@16.14.2/node_modules/graphql/version.js
|
|
27694
29150
|
var require_version = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
27695
29151
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -48082,7 +49538,7 @@ var require_utilities = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
48082
49538
|
var _resolveSchemaCoordinate = require_resolveSchemaCoordinate();
|
|
48083
49539
|
}));
|
|
48084
49540
|
//#endregion
|
|
48085
|
-
//#region src/
|
|
49541
|
+
//#region src/graphql.ts
|
|
48086
49542
|
var import_graphql = (/* @__PURE__ */ __commonJSMin(((exports) => {
|
|
48087
49543
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48088
49544
|
Object.defineProperty(exports, "BREAK", {
|
|
@@ -49384,40 +50840,6 @@ var import_graphql = (/* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
49384
50840
|
var _index5 = require_error();
|
|
49385
50841
|
var _index6 = require_utilities();
|
|
49386
50842
|
})))();
|
|
49387
|
-
function parseHttpBody(contentType, text) {
|
|
49388
|
-
if (!text) return;
|
|
49389
|
-
const mime = contentType.split(";")[0]?.toLowerCase().trim() ?? "";
|
|
49390
|
-
if (mime !== "application/json" && !mime.endsWith("+json") && !mime.endsWith("/json")) return text;
|
|
49391
|
-
try {
|
|
49392
|
-
return JSON.parse(text);
|
|
49393
|
-
} catch {
|
|
49394
|
-
return text;
|
|
49395
|
-
}
|
|
49396
|
-
}
|
|
49397
|
-
async function readLimitedText(response, options) {
|
|
49398
|
-
if (!response.body) return "";
|
|
49399
|
-
const reader = response.body.getReader();
|
|
49400
|
-
const chunks = [];
|
|
49401
|
-
let bytes = 0;
|
|
49402
|
-
while (true) {
|
|
49403
|
-
const { done, value } = await reader.read();
|
|
49404
|
-
if (done) break;
|
|
49405
|
-
if (value) {
|
|
49406
|
-
bytes += value.byteLength;
|
|
49407
|
-
if (bytes > (options.maxBytes ?? 1048576)) {
|
|
49408
|
-
await reader.cancel();
|
|
49409
|
-
throw new CapletsError("DOWNSTREAM_PROTOCOL_ERROR", options.errorMessage);
|
|
49410
|
-
}
|
|
49411
|
-
chunks.push(value);
|
|
49412
|
-
}
|
|
49413
|
-
}
|
|
49414
|
-
return new TextDecoder().decode(Buffer.concat(chunks));
|
|
49415
|
-
}
|
|
49416
|
-
function isAbortError(error) {
|
|
49417
|
-
return error instanceof DOMException && error.name === "AbortError";
|
|
49418
|
-
}
|
|
49419
|
-
//#endregion
|
|
49420
|
-
//#region src/graphql.ts
|
|
49421
50843
|
const GRAPHQL_METHOD = "POST";
|
|
49422
50844
|
const SCALAR_JSON_SCHEMA = {
|
|
49423
50845
|
String: { type: "string" },
|
|
@@ -49921,7 +51343,17 @@ var HttpActionManager = class {
|
|
|
49921
51343
|
status: response.status,
|
|
49922
51344
|
location: response.headers.get("location") ? "[REDACTED]" : void 0
|
|
49923
51345
|
});
|
|
49924
|
-
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
|
+
};
|
|
49925
51357
|
return {
|
|
49926
51358
|
content: markdownStructuredContent(parsed, {
|
|
49927
51359
|
title: `${api.name} call_tool ${toolName}`,
|
|
@@ -50087,23 +51519,6 @@ async function authHeaders$1(api, authDir) {
|
|
|
50087
51519
|
}, authDir);
|
|
50088
51520
|
}
|
|
50089
51521
|
}
|
|
50090
|
-
async function readResponse$1(response, api, elapsedMs) {
|
|
50091
|
-
const contentType = response.headers.get("content-type") ?? "";
|
|
50092
|
-
const body = parseHttpBody(contentType, await readLimitedText(response, {
|
|
50093
|
-
maxBytes: maxResponseBytes(api),
|
|
50094
|
-
errorMessage: "HTTP action response exceeded byte limit"
|
|
50095
|
-
}));
|
|
50096
|
-
return {
|
|
50097
|
-
status: response.status,
|
|
50098
|
-
statusText: response.statusText,
|
|
50099
|
-
headers: { "content-type": contentType },
|
|
50100
|
-
...body === void 0 ? {} : { body },
|
|
50101
|
-
elapsedMs
|
|
50102
|
-
};
|
|
50103
|
-
}
|
|
50104
|
-
function maxResponseBytes(api) {
|
|
50105
|
-
return api.maxResponseBytes;
|
|
50106
|
-
}
|
|
50107
51522
|
function validateBaseUrl(api) {
|
|
50108
51523
|
if (!isAllowedRemoteUrl(api.baseUrl)) throw new CapletsError("CONFIG_INVALID", `${api.server} HTTP API baseUrl is not allowed`);
|
|
50109
51524
|
const url = new URL(api.baseUrl);
|
|
@@ -59843,6 +61258,7 @@ const HTTP_METHODS = [
|
|
|
59843
61258
|
"patch",
|
|
59844
61259
|
"trace"
|
|
59845
61260
|
];
|
|
61261
|
+
const DEFAULT_OPENAPI_RESPONSE_MAX_BYTES = 100 * 1024 * 1024;
|
|
59846
61262
|
const JSON_CONTENT_TYPES = ["application/json"];
|
|
59847
61263
|
const FORBIDDEN_ARGUMENT_HEADERS = new Set([
|
|
59848
61264
|
"accept",
|
|
@@ -59929,7 +61345,13 @@ var OpenApiManager = class {
|
|
|
59929
61345
|
challenge: response.headers.get("www-authenticate") ? "[REDACTED]" : void 0,
|
|
59930
61346
|
...endpoint.auth.type === "oauth2" || endpoint.auth.type === "oidc" ? { nextAction: "run_caplets_auth_login" } : {}
|
|
59931
61347
|
});
|
|
59932
|
-
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
|
+
});
|
|
59933
61355
|
return {
|
|
59934
61356
|
content: markdownStructuredContent(parsed, {
|
|
59935
61357
|
title: `${endpoint.name} call_tool ${toolName}`,
|
|
@@ -60258,16 +61680,6 @@ async function authHeaders(endpoint, authDir) {
|
|
|
60258
61680
|
function shouldSendSpecAuth(endpoint) {
|
|
60259
61681
|
return Boolean(endpoint.specUrl && endpoint.baseUrl && new URL(endpoint.specUrl).origin === new URL(endpoint.baseUrl).origin);
|
|
60260
61682
|
}
|
|
60261
|
-
async function readResponse(response) {
|
|
60262
|
-
const contentType = response.headers.get("content-type") ?? "";
|
|
60263
|
-
const body = parseHttpBody(contentType, await readLimitedText(response, { errorMessage: "OpenAPI response exceeded byte limit" }));
|
|
60264
|
-
return {
|
|
60265
|
-
status: response.status,
|
|
60266
|
-
statusText: response.statusText,
|
|
60267
|
-
headers: { "content-type": contentType },
|
|
60268
|
-
...body === void 0 ? {} : { body }
|
|
60269
|
-
};
|
|
60270
|
-
}
|
|
60271
61683
|
async function fetchWithLimit(url, timeoutMs, headers = {}) {
|
|
60272
61684
|
const controller = new AbortController();
|
|
60273
61685
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -60332,7 +61744,7 @@ var ServerRegistry = class {
|
|
|
60332
61744
|
return this.allCaplets().filter((server) => !server.disabled);
|
|
60333
61745
|
}
|
|
60334
61746
|
get(serverId) {
|
|
60335
|
-
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];
|
|
60336
61748
|
return server?.disabled ? void 0 : server;
|
|
60337
61749
|
}
|
|
60338
61750
|
require(serverId) {
|
|
@@ -60378,6 +61790,7 @@ var ServerRegistry = class {
|
|
|
60378
61790
|
return [
|
|
60379
61791
|
...Object.values(this.config.mcpServers),
|
|
60380
61792
|
...Object.values(this.config.openapiEndpoints),
|
|
61793
|
+
...Object.values(this.config.googleDiscoveryApis ?? {}),
|
|
60381
61794
|
...Object.values(this.config.graphqlEndpoints),
|
|
60382
61795
|
...Object.values(this.config.httpApis),
|
|
60383
61796
|
...Object.values(this.config.cliTools),
|
|
@@ -60393,6 +61806,13 @@ function backendDetail(server) {
|
|
|
60393
61806
|
operationCacheTtlMs: server.operationCacheTtlMs,
|
|
60394
61807
|
source: server.specPath ? "specPath" : "specUrl"
|
|
60395
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
|
+
};
|
|
60396
61816
|
if (server.backend === "graphql") return {
|
|
60397
61817
|
type: "graphql",
|
|
60398
61818
|
disabled: server.disabled,
|
|
@@ -60429,6 +61849,9 @@ function backendDetail(server) {
|
|
|
60429
61849
|
toolCacheTtlMs: server.toolCacheTtlMs
|
|
60430
61850
|
};
|
|
60431
61851
|
}
|
|
61852
|
+
function googleDiscoverySource(server) {
|
|
61853
|
+
return server.discoveryPath ? "discoveryPath" : "discoveryUrl";
|
|
61854
|
+
}
|
|
60432
61855
|
function capletSetSource(server) {
|
|
60433
61856
|
return server.configPath && server.capletsRoot ? "both" : server.configPath ? "configPath" : "capletsRoot";
|
|
60434
61857
|
}
|
|
@@ -60550,14 +61973,14 @@ const ajv = new import_ajv.default({
|
|
|
60550
61973
|
});
|
|
60551
61974
|
const compiledValidators = /* @__PURE__ */ new WeakMap();
|
|
60552
61975
|
const MAX_SCHEMA_ERRORS = 8;
|
|
60553
|
-
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) {
|
|
60554
61977
|
const startedAt = Date.now();
|
|
60555
61978
|
const parsed = validateOperationRequest(request, registry.config.options.maxSearchLimit, server.backend);
|
|
60556
61979
|
switch (parsed.operation) {
|
|
60557
61980
|
case "inspect": return jsonResult(registry.detail(server), metadataFor(server, "inspect", void 0, startedAt));
|
|
60558
|
-
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));
|
|
60559
61982
|
case "tools": {
|
|
60560
|
-
const backend = backendFor(server, downstream, openapi, graphql, http, cli, caplets);
|
|
61983
|
+
const backend = backendFor(server, downstream, openapi, graphql, http, cli, caplets, googleDiscovery);
|
|
60561
61984
|
const page = pageItems((await backend.listTools(server)).map((tool) => backend.compact(server, tool)), parsed, registry.config.options.maxSearchLimit);
|
|
60562
61985
|
return jsonResult({
|
|
60563
61986
|
id: server.server,
|
|
@@ -60566,7 +61989,7 @@ async function handleServerTool(server, request, registry, downstream, openapi,
|
|
|
60566
61989
|
}, metadataFor(server, "tools", void 0, startedAt));
|
|
60567
61990
|
}
|
|
60568
61991
|
case "search_tools": {
|
|
60569
|
-
const backend = backendFor(server, downstream, openapi, graphql, http, cli, caplets);
|
|
61992
|
+
const backend = backendFor(server, downstream, openapi, graphql, http, cli, caplets, googleDiscovery);
|
|
60570
61993
|
const tools = await backend.listTools(server);
|
|
60571
61994
|
const limit = parsed.limit ?? registry.config.options.defaultSearchLimit;
|
|
60572
61995
|
const page = pageItems(backend.search(server, tools, parsed.query, limit), parsed, registry.config.options.maxSearchLimit);
|
|
@@ -60578,7 +62001,7 @@ async function handleServerTool(server, request, registry, downstream, openapi,
|
|
|
60578
62001
|
}, metadataFor(server, "search_tools", void 0, startedAt));
|
|
60579
62002
|
}
|
|
60580
62003
|
case "describe_tool": {
|
|
60581
|
-
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);
|
|
60582
62005
|
const observedOutputShape = await readObservedOutputShape(options, server, parsed.name, tool.outputSchema);
|
|
60583
62006
|
return jsonResult({
|
|
60584
62007
|
id: server.server,
|
|
@@ -60588,7 +62011,7 @@ async function handleServerTool(server, request, registry, downstream, openapi,
|
|
|
60588
62011
|
}, metadataFor(server, "describe_tool", parsed.name, startedAt));
|
|
60589
62012
|
}
|
|
60590
62013
|
case "call_tool": {
|
|
60591
|
-
const backend = backendFor(server, downstream, openapi, graphql, http, cli, caplets);
|
|
62014
|
+
const backend = backendFor(server, downstream, openapi, graphql, http, cli, caplets, googleDiscovery);
|
|
60592
62015
|
const tool = await maybeGetToolForValidation(backend, server, parsed.name);
|
|
60593
62016
|
validateToolArgsForAgent(tool, parsed.name, parsed.args);
|
|
60594
62017
|
if (parsed.fields === void 0) {
|
|
@@ -61146,6 +62569,7 @@ function projectCallToolResult(result, outputSchema, fields, context = {}) {
|
|
|
61146
62569
|
if (result.isError === true) return result;
|
|
61147
62570
|
const structuredContent = result.structuredContent;
|
|
61148
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.");
|
|
61149
62573
|
const projected = projectStructuredContent(structuredContent, outputSchema, fields);
|
|
61150
62574
|
return {
|
|
61151
62575
|
...result,
|
|
@@ -61153,10 +62577,19 @@ function projectCallToolResult(result, outputSchema, fields, context = {}) {
|
|
|
61153
62577
|
structuredContent: projected
|
|
61154
62578
|
};
|
|
61155
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
|
+
}
|
|
61156
62587
|
function extractArtifacts(result) {
|
|
61157
|
-
if (!isPlainObject$2(result)
|
|
62588
|
+
if (!isPlainObject$2(result)) return [];
|
|
61158
62589
|
const artifacts = [];
|
|
61159
62590
|
const seen = /* @__PURE__ */ new Set();
|
|
62591
|
+
addStructuredArtifact(artifacts, seen, result.structuredContent);
|
|
62592
|
+
if (!Array.isArray(result.content)) return artifacts;
|
|
61160
62593
|
for (const item of result.content) {
|
|
61161
62594
|
if (!isPlainObject$2(item) || item.type !== "text" || typeof item.text !== "string") continue;
|
|
61162
62595
|
const text = item.text;
|
|
@@ -61185,6 +62618,21 @@ function extractArtifacts(result) {
|
|
|
61185
62618
|
}
|
|
61186
62619
|
return artifacts;
|
|
61187
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
|
+
}
|
|
61188
62636
|
function parseMarkdownLinks(text) {
|
|
61189
62637
|
const links = [];
|
|
61190
62638
|
let index = 0;
|
|
@@ -61276,7 +62724,7 @@ function artifactKindFromText(text) {
|
|
|
61276
62724
|
function isPlainObject$2(value) {
|
|
61277
62725
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
61278
62726
|
}
|
|
61279
|
-
function backendFor(server, downstream, openapi, graphql, http, cli, caplets) {
|
|
62727
|
+
function backendFor(server, downstream, openapi, graphql, http, cli, caplets, googleDiscovery) {
|
|
61280
62728
|
if (server.backend === "mcp") return {
|
|
61281
62729
|
check: (...args) => downstream.checkServer(...args),
|
|
61282
62730
|
listTools: (...args) => downstream.listTools(...args),
|
|
@@ -61285,6 +62733,17 @@ function backendFor(server, downstream, openapi, graphql, http, cli, caplets) {
|
|
|
61285
62733
|
compact: (...args) => downstream.compact(...args),
|
|
61286
62734
|
search: (...args) => downstream.search(...args)
|
|
61287
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
|
+
}
|
|
61288
62747
|
if (server.backend === "graphql") {
|
|
61289
62748
|
if (!graphql) throw new CapletsError("INTERNAL_ERROR", "GraphQL manager is not configured");
|
|
61290
62749
|
return {
|
|
@@ -61409,7 +62868,7 @@ var CapletSetManager = class CapletSetManager {
|
|
|
61409
62868
|
suggestions: nearbyCapletNames(child.registry.enabledServers(), toolName)
|
|
61410
62869
|
});
|
|
61411
62870
|
try {
|
|
61412
|
-
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);
|
|
61413
62872
|
} catch (error) {
|
|
61414
62873
|
return errorResult(error);
|
|
61415
62874
|
}
|
|
@@ -61458,17 +62917,22 @@ var CapletSetManager = class CapletSetManager {
|
|
|
61458
62917
|
defaultSearchLimit: config.defaultSearchLimit,
|
|
61459
62918
|
maxSearchLimit: config.maxSearchLimit
|
|
61460
62919
|
}));
|
|
61461
|
-
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
|
+
};
|
|
61462
62925
|
const childAncestry = new Set([...ancestry, cacheKey]);
|
|
61463
62926
|
child = {
|
|
61464
62927
|
registry,
|
|
61465
|
-
downstream: new DownstreamManager(registry,
|
|
61466
|
-
openapi: new OpenApiManager(registry,
|
|
61467
|
-
graphql: new GraphQLManager(registry,
|
|
61468
|
-
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),
|
|
61469
62932
|
cli: new CliToolsManager(registry),
|
|
62933
|
+
googleDiscovery: new GoogleDiscoveryManager(registry, sharedOptions),
|
|
61470
62934
|
capletSets: new CapletSetManager(registry, {
|
|
61471
|
-
...
|
|
62935
|
+
...sharedOptions,
|
|
61472
62936
|
ancestry: childAncestry
|
|
61473
62937
|
}),
|
|
61474
62938
|
cacheKey,
|
|
@@ -61767,6 +63231,7 @@ var CapletsEngine = class {
|
|
|
61767
63231
|
registry;
|
|
61768
63232
|
downstream;
|
|
61769
63233
|
openapi;
|
|
63234
|
+
googleDiscovery;
|
|
61770
63235
|
graphql;
|
|
61771
63236
|
http;
|
|
61772
63237
|
cli;
|
|
@@ -61796,11 +63261,12 @@ var CapletsEngine = class {
|
|
|
61796
63261
|
const config = this.configLoader(this.paths.configPath, this.paths.projectConfigPath);
|
|
61797
63262
|
this.registry = new ServerRegistry(config);
|
|
61798
63263
|
this.downstream = new DownstreamManager(this.registry, selectAuthOptions(options.authDir));
|
|
61799
|
-
this.openapi = new OpenApiManager(this.registry,
|
|
63264
|
+
this.openapi = new OpenApiManager(this.registry, selectHttpLikeOptions(options));
|
|
63265
|
+
this.googleDiscovery = new GoogleDiscoveryManager(this.registry, selectHttpLikeOptions(options));
|
|
61800
63266
|
this.graphql = new GraphQLManager(this.registry, selectAuthOptions(options.authDir));
|
|
61801
|
-
this.http = new HttpActionManager(this.registry,
|
|
63267
|
+
this.http = new HttpActionManager(this.registry, selectHttpLikeOptions(options));
|
|
61802
63268
|
this.cli = new CliToolsManager(this.registry);
|
|
61803
|
-
this.capletSets = new CapletSetManager(this.registry,
|
|
63269
|
+
this.capletSets = new CapletSetManager(this.registry, selectHttpLikeOptions(options));
|
|
61804
63270
|
this.watchDebounceMs = options.watchDebounceMs ?? 250;
|
|
61805
63271
|
this.watchEnabled = options.watch ?? true;
|
|
61806
63272
|
this.writeErr = options.writeErr ?? ((value) => process.stderr.write(value));
|
|
@@ -61864,7 +63330,7 @@ var CapletsEngine = class {
|
|
|
61864
63330
|
observedOutputShapeStore: this.observedOutputShapeStore,
|
|
61865
63331
|
observedOutputShapeScope: this.observedOutputShapeScope,
|
|
61866
63332
|
projectFingerprint: this.projectFingerprint
|
|
61867
|
-
});
|
|
63333
|
+
}, this.googleDiscovery);
|
|
61868
63334
|
} catch (error) {
|
|
61869
63335
|
return errorResult(error);
|
|
61870
63336
|
}
|
|
@@ -61896,7 +63362,7 @@ var CapletsEngine = class {
|
|
|
61896
63362
|
}
|
|
61897
63363
|
}
|
|
61898
63364
|
async completeCliWords(words) {
|
|
61899
|
-
const { completeCliWords } = await import("./completion-
|
|
63365
|
+
const { completeCliWords } = await import("./completion-BC4BNWo0.js").then((n) => n.r);
|
|
61900
63366
|
return await completeCliWords(words, {
|
|
61901
63367
|
config: this.registry.config,
|
|
61902
63368
|
managers: {
|
|
@@ -61947,16 +63413,16 @@ var CapletsEngine = class {
|
|
|
61947
63413
|
}
|
|
61948
63414
|
}
|
|
61949
63415
|
async listCompletionTools(server) {
|
|
61950
|
-
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) => ({
|
|
61951
63417
|
name: tool.name,
|
|
61952
63418
|
...tool.description ? { description: tool.description } : {}
|
|
61953
63419
|
}));
|
|
61954
63420
|
}
|
|
61955
63421
|
async listTools(server) {
|
|
61956
|
-
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);
|
|
61957
63423
|
}
|
|
61958
63424
|
async callTool(server, toolName, args) {
|
|
61959
|
-
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);
|
|
61960
63426
|
}
|
|
61961
63427
|
async optionalMcpList(caplet, list) {
|
|
61962
63428
|
try {
|
|
@@ -61982,6 +63448,7 @@ var CapletsEngine = class {
|
|
|
61982
63448
|
this.registry = nextRegistry;
|
|
61983
63449
|
this.downstream.updateRegistry(nextRegistry);
|
|
61984
63450
|
this.openapi.updateRegistry(nextRegistry);
|
|
63451
|
+
this.googleDiscovery.updateRegistry(nextRegistry);
|
|
61985
63452
|
this.graphql.updateRegistry(nextRegistry);
|
|
61986
63453
|
this.http.updateRegistry(nextRegistry);
|
|
61987
63454
|
this.cli.updateRegistry(nextRegistry);
|
|
@@ -62035,6 +63502,7 @@ var CapletsEngine = class {
|
|
|
62035
63502
|
if (!(serializeCaplet(before) !== serializeCaplet(after))) continue;
|
|
62036
63503
|
if (before?.backend === "mcp") await this.downstream.closeServer(serverId);
|
|
62037
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);
|
|
62038
63506
|
if (before?.backend === "graphql" || after?.backend === "graphql" || !after) this.graphql.invalidate(serverId);
|
|
62039
63507
|
if (before?.backend === "http" || after?.backend === "http" || !after) this.http.invalidate(serverId);
|
|
62040
63508
|
if (before?.backend === "cli" || after?.backend === "cli" || !after) this.cli.invalidate(serverId);
|
|
@@ -62094,6 +63562,13 @@ var CapletsEngine = class {
|
|
|
62094
63562
|
function selectAuthOptions(authDir) {
|
|
62095
63563
|
return authDir ? { authDir } : {};
|
|
62096
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
|
+
}
|
|
62097
63572
|
function safeProjectFingerprint() {
|
|
62098
63573
|
try {
|
|
62099
63574
|
return fingerprintProjectRoot(findProjectRoot());
|
|
@@ -62136,6 +63611,7 @@ function allCaplets(config) {
|
|
|
62136
63611
|
return [
|
|
62137
63612
|
...Object.values(config.mcpServers),
|
|
62138
63613
|
...Object.values(config.openapiEndpoints),
|
|
63614
|
+
...Object.values(config.googleDiscoveryApis ?? {}),
|
|
62139
63615
|
...Object.values(config.graphqlEndpoints),
|
|
62140
63616
|
...Object.values(config.httpApis),
|
|
62141
63617
|
...Object.values(config.cliTools),
|
|
@@ -62233,7 +63709,7 @@ function capletHintText(caplet) {
|
|
|
62233
63709
|
].filter((value) => Boolean(value)).join(" ");
|
|
62234
63710
|
}
|
|
62235
63711
|
function minifyCodeModeDeclarationText(value) {
|
|
62236
|
-
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();
|
|
62237
63713
|
}
|
|
62238
63714
|
function codeModeDeclarationHash(declaration) {
|
|
62239
63715
|
return [
|
|
@@ -62859,6 +64335,9 @@ function isPlainObject$1(value) {
|
|
|
62859
64335
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
62860
64336
|
}
|
|
62861
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
|
|
62862
64341
|
//#region src/code-mode/static-analysis.ts
|
|
62863
64342
|
var import_lib = (/* @__PURE__ */ __commonJSMin(((exports) => {
|
|
62864
64343
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -75451,7 +76930,7 @@ const PARSER_OPTIONS = {
|
|
|
75451
76930
|
]
|
|
75452
76931
|
};
|
|
75453
76932
|
function hasDirectFetchCall(code) {
|
|
75454
|
-
return hasMatchingAstNode(code,
|
|
76933
|
+
return hasMatchingAstNode(code, isDirectFetchCallNode);
|
|
75455
76934
|
}
|
|
75456
76935
|
function hasExecutableImport(code) {
|
|
75457
76936
|
return hasMatchingAstNode(code, isExecutableImportNode);
|
|
@@ -75460,9 +76939,9 @@ function hasMatchingAstNode(code, predicate) {
|
|
|
75460
76939
|
const ast = parseCode(code);
|
|
75461
76940
|
if (!ast) return false;
|
|
75462
76941
|
let found = false;
|
|
75463
|
-
visitAst(ast, (node) => {
|
|
76942
|
+
visitAst(ast, (node, parent) => {
|
|
75464
76943
|
if (found) return;
|
|
75465
|
-
found = predicate(node);
|
|
76944
|
+
found = predicate(node, parent);
|
|
75466
76945
|
});
|
|
75467
76946
|
return found;
|
|
75468
76947
|
}
|
|
@@ -75480,11 +76959,10 @@ function isExecutableImportNode(node) {
|
|
|
75480
76959
|
function isCallExpression(node) {
|
|
75481
76960
|
return (node.type === "CallExpression" || node.type === "OptionalCallExpression") && "callee" in node;
|
|
75482
76961
|
}
|
|
75483
|
-
function
|
|
75484
|
-
if (!isNode(
|
|
75485
|
-
if (
|
|
75486
|
-
|
|
75487
|
-
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);
|
|
75488
76966
|
}
|
|
75489
76967
|
function isGlobalFetchMember(node) {
|
|
75490
76968
|
if (!isIdentifierNamed(node.object, "globalThis", "window", "self")) return false;
|
|
@@ -75494,16 +76972,22 @@ function isGlobalFetchMember(node) {
|
|
|
75494
76972
|
function isExportDeclaration(node) {
|
|
75495
76973
|
return node.type === "ExportAllDeclaration" || node.type === "ExportDefaultDeclaration" || node.type === "ExportNamedDeclaration";
|
|
75496
76974
|
}
|
|
75497
|
-
function visitAst(value, visit) {
|
|
76975
|
+
function visitAst(value, visit, parent) {
|
|
75498
76976
|
if (!isNode(value)) return;
|
|
75499
|
-
visit(value);
|
|
76977
|
+
visit(value, parent);
|
|
75500
76978
|
for (const [key, child] of Object.entries(value)) {
|
|
75501
76979
|
if (key === "loc" || key === "start" || key === "end" || key === "extra") continue;
|
|
75502
76980
|
if (Array.isArray(child)) {
|
|
75503
|
-
for (const item of child) visitAst(item, visit
|
|
76981
|
+
for (const item of child) visitAst(item, visit, {
|
|
76982
|
+
node: value,
|
|
76983
|
+
key
|
|
76984
|
+
});
|
|
75504
76985
|
continue;
|
|
75505
76986
|
}
|
|
75506
|
-
visitAst(child, visit
|
|
76987
|
+
visitAst(child, visit, {
|
|
76988
|
+
node: value,
|
|
76989
|
+
key
|
|
76990
|
+
});
|
|
75507
76991
|
}
|
|
75508
76992
|
}
|
|
75509
76993
|
function isNode(value) {
|
|
@@ -75547,7 +77031,7 @@ function diagnoseCodeModeTypeScript(input) {
|
|
|
75547
77031
|
const host = createVirtualCompilerHost(compilerOptions, {
|
|
75548
77032
|
[CODE_FILE]: wrappedCode,
|
|
75549
77033
|
[DECLARATION_FILE]: input.declaration,
|
|
75550
|
-
[AMBIENT_FILE]:
|
|
77034
|
+
[AMBIENT_FILE]: CODE_MODE_DIAGNOSTICS_BUILTINS_DECLARATION
|
|
75551
77035
|
});
|
|
75552
77036
|
const program = ts.createProgram([
|
|
75553
77037
|
CODE_FILE,
|
|
@@ -75593,7 +77077,7 @@ function preflightDiagnostics(code) {
|
|
|
75593
77077
|
if (hasDirectFetchCall(code)) diagnostics.push({
|
|
75594
77078
|
code: "FETCH_UNAVAILABLE",
|
|
75595
77079
|
severity: "error",
|
|
75596
|
-
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'."
|
|
75597
77081
|
});
|
|
75598
77082
|
return diagnostics;
|
|
75599
77083
|
}
|
|
@@ -75630,23 +77114,82 @@ function formatDiagnostic(diagnostic, syntacticDiagnostic = false) {
|
|
|
75630
77114
|
} : {}
|
|
75631
77115
|
};
|
|
75632
77116
|
}
|
|
75633
|
-
|
|
75634
|
-
|
|
75635
|
-
|
|
75636
|
-
|
|
75637
|
-
|
|
75638
|
-
|
|
75639
|
-
|
|
75640
|
-
|
|
75641
|
-
|
|
75642
|
-
|
|
75643
|
-
|
|
75644
|
-
|
|
75645
|
-
|
|
75646
|
-
|
|
75647
|
-
|
|
75648
|
-
|
|
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
|
+
} };
|
|
75649
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);
|
|
77189
|
+
}
|
|
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})();";
|
|
75650
77193
|
//#endregion
|
|
75651
77194
|
//#region src/code-mode/sandbox.ts
|
|
75652
77195
|
var QuickJsCodeModeSandbox = class {
|
|
@@ -75676,6 +77219,7 @@ async function evaluateInQuickJs(input) {
|
|
|
75676
77219
|
});
|
|
75677
77220
|
context.setProp(context.global, "__caplets_log", logBridge);
|
|
75678
77221
|
logBridge.dispose();
|
|
77222
|
+
const platformHost = installCodeModePlatformHost(context, pendingDeferreds, {});
|
|
75679
77223
|
const invokeBridge = createInvokeBridge(context, pendingDeferreds, input.invoke, deadlineMs, timeoutMs);
|
|
75680
77224
|
context.setProp(context.global, "__caplets_invoke", invokeBridge);
|
|
75681
77225
|
invokeBridge.dispose();
|
|
@@ -75735,6 +77279,7 @@ async function evaluateInQuickJs(input) {
|
|
|
75735
77279
|
};
|
|
75736
77280
|
} finally {
|
|
75737
77281
|
stateHandle.dispose();
|
|
77282
|
+
platformHost.dispose();
|
|
75738
77283
|
}
|
|
75739
77284
|
} finally {
|
|
75740
77285
|
for (const deferred of pendingDeferreds) if (deferred.alive) deferred.dispose();
|
|
@@ -75794,19 +77339,7 @@ function buildExecutionSource(code, capletIds) {
|
|
|
75794
77339
|
} }).outputText;
|
|
75795
77340
|
return [
|
|
75796
77341
|
"\"use strict\";",
|
|
75797
|
-
|
|
75798
|
-
" if (typeof value === 'string') return value;",
|
|
75799
|
-
" try { return JSON.stringify(value); } catch { return String(value); }",
|
|
75800
|
-
"};",
|
|
75801
|
-
"const __formatLogLine = (args) => args.map(__formatLogArg).join(' ');",
|
|
75802
|
-
"const console = {",
|
|
75803
|
-
" log: (...args) => __caplets_log('log', __formatLogLine(args)),",
|
|
75804
|
-
" info: (...args) => __caplets_log('info', __formatLogLine(args)),",
|
|
75805
|
-
" warn: (...args) => __caplets_log('warn', __formatLogLine(args)),",
|
|
75806
|
-
" error: (...args) => __caplets_log('error', __formatLogLine(args)),",
|
|
75807
|
-
" debug: (...args) => __caplets_log('debug', __formatLogLine(args)),",
|
|
75808
|
-
"};",
|
|
75809
|
-
"const fetch = () => { throw new Error('fetch is disabled in Code Mode'); };",
|
|
77342
|
+
CODE_MODE_PLATFORM_RUNTIME_SOURCE,
|
|
75810
77343
|
"const __invoke = (capletId, method, args) => Promise.resolve(__caplets_invoke(capletId, method, args)).then(JSON.parse);",
|
|
75811
77344
|
"const __handle = (capletId) => ({",
|
|
75812
77345
|
" id: capletId,",
|
|
@@ -76978,6 +78511,7 @@ function remoteToolToNativeTool(tool) {
|
|
|
76978
78511
|
return {
|
|
76979
78512
|
caplet: capletId,
|
|
76980
78513
|
...sourceCaplet && sourceCaplet !== capletId ? { sourceCaplet } : {},
|
|
78514
|
+
...tool.shadowing ? { shadowing: tool.shadowing } : {},
|
|
76981
78515
|
toolName,
|
|
76982
78516
|
title: tool.title ?? capletId,
|
|
76983
78517
|
description: [
|
|
@@ -76991,7 +78525,8 @@ function remoteToolToNativeTool(tool) {
|
|
|
76991
78525
|
...tool.codeModeCaplets ? { codeModeCaplets: tool.codeModeCaplets.map((caplet) => ({
|
|
76992
78526
|
id: caplet.capletId,
|
|
76993
78527
|
name: caplet.name,
|
|
76994
|
-
description: caplet.description ?? ""
|
|
78528
|
+
description: caplet.description ?? "",
|
|
78529
|
+
shadowing: caplet.shadowing
|
|
76995
78530
|
})) } : {},
|
|
76996
78531
|
inputSchema,
|
|
76997
78532
|
...isPlainObject(tool.outputSchema) ? { outputSchema: tool.outputSchema } : {},
|
|
@@ -77125,6 +78660,7 @@ function primitiveInvokeInput(capletId, operation, input) {
|
|
|
77125
78660
|
}
|
|
77126
78661
|
function toolsFromManifest(manifest) {
|
|
77127
78662
|
const codeModeMarker = attachCodeModeMarker(manifest);
|
|
78663
|
+
const codeModeShadowing = manifest.codeModeCaplets.some((entry) => entry.shadowing === "forbid") ? "forbid" : "allow";
|
|
77128
78664
|
return [
|
|
77129
78665
|
...manifest.caplets.map((entry) => ({
|
|
77130
78666
|
name: entry.capletId,
|
|
@@ -77132,6 +78668,7 @@ function toolsFromManifest(manifest) {
|
|
|
77132
78668
|
title: entry.title ?? entry.name,
|
|
77133
78669
|
description: entry.description,
|
|
77134
78670
|
inputSchema: entry.inputSchema,
|
|
78671
|
+
shadowing: entry.shadowing,
|
|
77135
78672
|
...codeModeMarker
|
|
77136
78673
|
})),
|
|
77137
78674
|
...manifest.tools.map((entry) => ({
|
|
@@ -77143,6 +78680,7 @@ function toolsFromManifest(manifest) {
|
|
|
77143
78680
|
inputSchema: entry.inputSchema,
|
|
77144
78681
|
outputSchema: entry.outputSchema,
|
|
77145
78682
|
annotations: entry.annotations,
|
|
78683
|
+
shadowing: entry.shadowing,
|
|
77146
78684
|
...codeModeMarker
|
|
77147
78685
|
})),
|
|
77148
78686
|
...primitiveToolsFromManifest(manifest, codeModeMarker),
|
|
@@ -77153,6 +78691,7 @@ function toolsFromManifest(manifest) {
|
|
|
77153
78691
|
description: "Remote Caplets available to locally-run attached Code Mode.",
|
|
77154
78692
|
codeModeRun: true,
|
|
77155
78693
|
codeModeCaplets: manifest.codeModeCaplets,
|
|
78694
|
+
shadowing: codeModeShadowing,
|
|
77156
78695
|
inputSchema: codeModeRunInputJsonSchema()
|
|
77157
78696
|
}] : []
|
|
77158
78697
|
];
|
|
@@ -77163,46 +78702,50 @@ function attachCodeModeMarker(manifest) {
|
|
|
77163
78702
|
function primitiveToolsFromManifest(manifest, codeModeMarker) {
|
|
77164
78703
|
const directToolNames = new Set(manifest.tools.map((entry) => entry.name));
|
|
77165
78704
|
const byCaplet = /* @__PURE__ */ new Map();
|
|
77166
|
-
const entryFor = (capletId) => {
|
|
78705
|
+
const entryFor = (capletId, shadowing) => {
|
|
77167
78706
|
const existing = byCaplet.get(capletId);
|
|
77168
|
-
if (existing)
|
|
78707
|
+
if (existing) {
|
|
78708
|
+
if (shadowing === "forbid") existing.shadowing = "forbid";
|
|
78709
|
+
return existing;
|
|
78710
|
+
}
|
|
77169
78711
|
const next = {
|
|
77170
78712
|
resources: false,
|
|
77171
78713
|
resourceTemplates: false,
|
|
77172
78714
|
prompts: false,
|
|
77173
|
-
completions: false
|
|
78715
|
+
completions: false,
|
|
78716
|
+
shadowing
|
|
77174
78717
|
};
|
|
77175
78718
|
byCaplet.set(capletId, next);
|
|
77176
78719
|
return next;
|
|
77177
78720
|
};
|
|
77178
|
-
for (const entry of manifest.resources) entryFor(entry.capletId).resources = true;
|
|
77179
|
-
for (const entry of manifest.resourceTemplates) entryFor(entry.capletId).resourceTemplates = true;
|
|
77180
|
-
for (const entry of manifest.prompts) entryFor(entry.capletId).prompts = true;
|
|
77181
|
-
for (const entry of manifest.completions) entryFor(entry.capletId).completions = true;
|
|
78721
|
+
for (const entry of manifest.resources) entryFor(entry.capletId, entry.shadowing).resources = true;
|
|
78722
|
+
for (const entry of manifest.resourceTemplates) entryFor(entry.capletId, entry.shadowing).resourceTemplates = true;
|
|
78723
|
+
for (const entry of manifest.prompts) entryFor(entry.capletId, entry.shadowing).prompts = true;
|
|
78724
|
+
for (const entry of manifest.completions) entryFor(entry.capletId, entry.shadowing).completions = true;
|
|
77182
78725
|
const tools = [];
|
|
77183
|
-
const addPrimitiveTool = (capletId, operation) => {
|
|
78726
|
+
const addPrimitiveTool = (capletId, operation, shadowing) => {
|
|
77184
78727
|
const name = `${capletId}__${operation}`;
|
|
77185
78728
|
if (directToolNames.has(name)) return;
|
|
77186
|
-
tools.push(primitiveTool(capletId, operation, codeModeMarker));
|
|
78729
|
+
tools.push(primitiveTool(capletId, operation, shadowing, codeModeMarker));
|
|
77187
78730
|
};
|
|
77188
78731
|
for (const [capletId, flags] of byCaplet) {
|
|
77189
78732
|
if (flags.resources) {
|
|
77190
|
-
addPrimitiveTool(capletId, "list_resources");
|
|
77191
|
-
addPrimitiveTool(capletId, "read_resource");
|
|
78733
|
+
addPrimitiveTool(capletId, "list_resources", flags.shadowing);
|
|
78734
|
+
addPrimitiveTool(capletId, "read_resource", flags.shadowing);
|
|
77192
78735
|
}
|
|
77193
78736
|
if (flags.resourceTemplates) {
|
|
77194
|
-
addPrimitiveTool(capletId, "list_resource_templates");
|
|
77195
|
-
addPrimitiveTool(capletId, "read_resource");
|
|
78737
|
+
addPrimitiveTool(capletId, "list_resource_templates", flags.shadowing);
|
|
78738
|
+
addPrimitiveTool(capletId, "read_resource", flags.shadowing);
|
|
77196
78739
|
}
|
|
77197
78740
|
if (flags.prompts) {
|
|
77198
|
-
addPrimitiveTool(capletId, "list_prompts");
|
|
77199
|
-
addPrimitiveTool(capletId, "get_prompt");
|
|
78741
|
+
addPrimitiveTool(capletId, "list_prompts", flags.shadowing);
|
|
78742
|
+
addPrimitiveTool(capletId, "get_prompt", flags.shadowing);
|
|
77200
78743
|
}
|
|
77201
|
-
if (flags.completions) addPrimitiveTool(capletId, "complete");
|
|
78744
|
+
if (flags.completions) addPrimitiveTool(capletId, "complete", flags.shadowing);
|
|
77202
78745
|
}
|
|
77203
78746
|
return [...new Map(tools.map((tool) => [tool.name, tool])).values()];
|
|
77204
78747
|
}
|
|
77205
|
-
function primitiveTool(capletId, operation, codeModeMarker) {
|
|
78748
|
+
function primitiveTool(capletId, operation, shadowing, codeModeMarker) {
|
|
77206
78749
|
return {
|
|
77207
78750
|
name: `${capletId}__${operation}`,
|
|
77208
78751
|
capletId,
|
|
@@ -77210,6 +78753,7 @@ function primitiveTool(capletId, operation, codeModeMarker) {
|
|
|
77210
78753
|
title: operation,
|
|
77211
78754
|
description: `MCP ${operation.replace(/_/g, " ")}.`,
|
|
77212
78755
|
inputSchema: primitiveInputSchema(operation),
|
|
78756
|
+
shadowing,
|
|
77213
78757
|
...codeModeMarker
|
|
77214
78758
|
};
|
|
77215
78759
|
}
|
|
@@ -77810,7 +79354,7 @@ var DefaultNativeCapletsService = class {
|
|
|
77810
79354
|
...options,
|
|
77811
79355
|
writeErr: this.writeErr
|
|
77812
79356
|
});
|
|
77813
|
-
this.postReloadRefresh = this.refreshExposureSnapshot({ emitToolsChanged: this.
|
|
79357
|
+
this.postReloadRefresh = this.refreshExposureSnapshot({ emitToolsChanged: this.hasSnapshotBackedDirectExposure() });
|
|
77814
79358
|
this.unsubscribeEngineReload = this.engine.onReload(() => {
|
|
77815
79359
|
this.postReloadRefresh = this.refreshExposureSnapshot({ emitToolsChanged: true });
|
|
77816
79360
|
});
|
|
@@ -77879,13 +79423,13 @@ var DefaultNativeCapletsService = class {
|
|
|
77879
79423
|
...action.outputSchema ? { outputSchema: action.outputSchema } : {},
|
|
77880
79424
|
...action.annotations ? { annotations: action.annotations } : {}
|
|
77881
79425
|
}));
|
|
77882
|
-
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, {
|
|
77883
79427
|
description: `MCP ${operationName.replace(/_/g, " ")}.`,
|
|
77884
79428
|
inputSchema: nativeMcpPrimitiveInputSchema(operationName)
|
|
77885
79429
|
}))];
|
|
77886
|
-
return [];
|
|
79430
|
+
return snapshot?.directTools.filter((entry) => entry.caplet.server === caplet.server).map((entry) => this.directDiscoveredTool(caplet, entry)) ?? [];
|
|
77887
79431
|
}
|
|
77888
|
-
|
|
79432
|
+
directDiscoveredTool(caplet, entry) {
|
|
77889
79433
|
return this.directNativeTool(caplet, entry.downstreamName, {
|
|
77890
79434
|
...entry.tool.description ? { description: entry.tool.description } : {},
|
|
77891
79435
|
...entry.tool.inputSchema ? { inputSchema: entry.tool.inputSchema } : {},
|
|
@@ -77902,6 +79446,7 @@ var DefaultNativeCapletsService = class {
|
|
|
77902
79446
|
});
|
|
77903
79447
|
return {
|
|
77904
79448
|
caplet: routeId,
|
|
79449
|
+
sourceCaplet: caplet.server,
|
|
77905
79450
|
toolName,
|
|
77906
79451
|
title: operationName,
|
|
77907
79452
|
description: options.description ?? "",
|
|
@@ -77926,9 +79471,10 @@ var DefaultNativeCapletsService = class {
|
|
|
77926
79471
|
this.writeErr(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
77927
79472
|
}
|
|
77928
79473
|
}
|
|
77929
|
-
|
|
79474
|
+
hasSnapshotBackedDirectExposure() {
|
|
77930
79475
|
return this.engine.enabledServers().some((caplet) => {
|
|
77931
|
-
if (caplet.
|
|
79476
|
+
if (caplet.setup || caplet.projectBinding?.required) return false;
|
|
79477
|
+
if (caplet.backend === "http" || caplet.backend === "cli") return false;
|
|
77932
79478
|
return resolveExposure(caplet.exposure, this.engine.currentConfig().options.exposure).direct;
|
|
77933
79479
|
});
|
|
77934
79480
|
}
|
|
@@ -78069,6 +79615,7 @@ function codeModeRunNativeTool(capletTools) {
|
|
|
78069
79615
|
id: tool.caplet,
|
|
78070
79616
|
name: tool.title,
|
|
78071
79617
|
description: tool.description,
|
|
79618
|
+
...tool.shadowing ? { shadowing: tool.shadowing } : {},
|
|
78072
79619
|
...tool.useWhen ? { useWhen: tool.useWhen } : {},
|
|
78073
79620
|
...tool.avoidWhen ? { avoidWhen: tool.avoidWhen } : {}
|
|
78074
79621
|
}));
|
|
@@ -78103,6 +79650,7 @@ function codeModeCallableNativeTools(tools, options) {
|
|
|
78103
79650
|
toolName: tool?.toolName ?? nativeCapletToolName(caplet.id),
|
|
78104
79651
|
title: caplet.name,
|
|
78105
79652
|
description: caplet.description,
|
|
79653
|
+
...caplet.shadowing ? { shadowing: caplet.shadowing } : {},
|
|
78106
79654
|
...caplet.useWhen ? { useWhen: caplet.useWhen } : {},
|
|
78107
79655
|
...caplet.avoidWhen ? { avoidWhen: caplet.avoidWhen } : {},
|
|
78108
79656
|
promptGuidance: tool?.promptGuidance ?? []
|
|
@@ -78275,9 +79823,7 @@ var CompositeNativeCapletsService = class {
|
|
|
78275
79823
|
}
|
|
78276
79824
|
async execute(capletId, request) {
|
|
78277
79825
|
if (capletId === "code_mode") return await executeCodeModeRunNative(this, request);
|
|
78278
|
-
|
|
78279
|
-
const remoteHasCaplet = serviceHasCaplet(this.remote, capletId);
|
|
78280
|
-
if (localHasCaplet && !remoteHasCaplet) return await this.local.execute(capletId, request);
|
|
79826
|
+
if (this.localCanExecute(capletId)) return await this.local.execute(capletId, request);
|
|
78281
79827
|
return await this.remote.execute(capletId, request);
|
|
78282
79828
|
}
|
|
78283
79829
|
async reload() {
|
|
@@ -78320,8 +79866,8 @@ var CompositeNativeCapletsService = class {
|
|
|
78320
79866
|
mergeTools() {
|
|
78321
79867
|
const allLocalTools = this.local.listTools();
|
|
78322
79868
|
const allRemoteTools = this.remote.listTools();
|
|
78323
|
-
const remoteCodeModeTools =
|
|
78324
|
-
const remoteIds =
|
|
79869
|
+
const remoteCodeModeTools = remoteCodeModeCallableNativeTools(allRemoteTools);
|
|
79870
|
+
const remoteIds = remoteSuppressedCapletIds(allRemoteTools, remoteCodeModeTools);
|
|
78325
79871
|
const localTools = allLocalTools.filter((tool) => tool.codeModeRun !== true && !remoteIds.has(tool.sourceCaplet ?? tool.caplet));
|
|
78326
79872
|
this.warnShadowedLocalCaplets(allLocalTools, remoteIds);
|
|
78327
79873
|
const localCodeModeTools = codeModeCallableNativeTools(allLocalTools, { fallbackToVisible: false }).filter((tool) => !remoteIds.has(tool.caplet));
|
|
@@ -78329,6 +79875,10 @@ var CompositeNativeCapletsService = class {
|
|
|
78329
79875
|
const codeModeTools = [...remoteCodeModeTools, ...localCodeModeTools];
|
|
78330
79876
|
return [...mergedTools, ...codeModeTools.length > 0 ? [codeModeRunNativeTool(codeModeTools)] : []];
|
|
78331
79877
|
}
|
|
79878
|
+
localCanExecute(capletId) {
|
|
79879
|
+
const remoteIds = remoteSuppressedCapletIds(this.remote.listTools());
|
|
79880
|
+
return localExecutionKeys(this.local.listTools(), capletId).some((key) => !remoteIds.has(key));
|
|
79881
|
+
}
|
|
78332
79882
|
warnShadowedLocalCaplets(localTools, remoteIds) {
|
|
78333
79883
|
const localIds = new Set([...localTools.filter((tool) => tool.codeModeRun !== true).map((tool) => tool.sourceCaplet ?? tool.caplet), ...codeModeCallableNativeTools(localTools, { fallbackToVisible: false }).map((tool) => tool.caplet)]);
|
|
78334
79884
|
for (const capletId of localIds) {
|
|
@@ -78347,11 +79897,22 @@ var CompositeNativeCapletsService = class {
|
|
|
78347
79897
|
}
|
|
78348
79898
|
}
|
|
78349
79899
|
};
|
|
78350
|
-
function
|
|
78351
|
-
return
|
|
78352
|
-
|
|
78353
|
-
|
|
78354
|
-
|
|
79900
|
+
function remoteCodeModeCallableNativeTools(tools) {
|
|
79901
|
+
return codeModeCallableNativeTools(tools, { fallbackToVisible: true });
|
|
79902
|
+
}
|
|
79903
|
+
function remoteSuppressedCapletIds(allRemoteTools, remoteCodeModeTools = remoteCodeModeCallableNativeTools(allRemoteTools)) {
|
|
79904
|
+
return new Set([...allRemoteTools.filter((tool) => tool.codeModeRun !== true && tool.shadowing !== "allow").map((tool) => tool.sourceCaplet ?? tool.caplet), ...remoteCodeModeTools.filter((tool) => tool.shadowing !== "allow").map((tool) => tool.caplet)].filter((caplet) => caplet !== nativeCodeModeToolId));
|
|
79905
|
+
}
|
|
79906
|
+
function localExecutionKeys(tools, capletId) {
|
|
79907
|
+
const keys = [];
|
|
79908
|
+
for (const tool of tools) {
|
|
79909
|
+
if (tool.codeModeRun) {
|
|
79910
|
+
for (const caplet of tool.codeModeCaplets ?? []) if (caplet.id === capletId) keys.push(caplet.id);
|
|
79911
|
+
continue;
|
|
79912
|
+
}
|
|
79913
|
+
if (tool.caplet === capletId || tool.sourceCaplet === capletId) keys.push(tool.sourceCaplet ?? tool.caplet);
|
|
79914
|
+
}
|
|
79915
|
+
return keys;
|
|
78355
79916
|
}
|
|
78356
79917
|
function createProjectBindingSessionManager(cloud, local, options) {
|
|
78357
79918
|
if (!cloud) return;
|
|
@@ -78410,4 +79971,4 @@ function errorMessage(error) {
|
|
|
78410
79971
|
return error instanceof Error ? error.message : String(error);
|
|
78411
79972
|
}
|
|
78412
79973
|
//#endregion
|
|
78413
|
-
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 };
|