@caplets/core 0.22.0 → 0.23.0

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