fix(deps): ship a zero-warning, minimal install (#1784)

* fix(deps): ship a zero-warning, minimal install

The published package declared 62 runtime `dependencies`, but `dist/cli.mjs`
is a fully-bundled esbuild output that inlines almost all of them. End users
therefore installed ~476 transitive packages — including three subtrees the
bundle never needs at install time, each emitting an install warning:

  - node-domexception (deprecated) via google-auth-library
  - protobufjs (allow-scripts)     via @grpc/* (already bundled into dist)
  - sharp (allow-scripts)          native image module

The repo's `overrides`/`allowScripts` silence these locally, but those are
root-only npm settings and are ignored when the package is installed as a
dependency — so end users saw the warnings.

Core changes:
  - package.json: runtime dependencies trimmed 62 -> 3 (@orama/orama,
    @orama/plugin-data-persistence, @vscode/ripgrep). Bundled packages, plus
    the optional sharp/google-auth-library, move to devDependencies so they
    are built/tested but not shipped.
  - package.json: @anthropic-ai/sdk, @modelcontextprotocol/sdk, react and
    react-reconciler declared as OPTIONAL peerDependencies — externalized by
    the ./sdk bundle but bundled into the CLI. Optional peers keep the CLI
    install minimal and warning-free while still resolving for ./sdk consumers.
  - externals.ts: sharp, google-auth-library and @anthropic-ai/bedrock-sdk
    marked OPTIONAL_RUNTIME_EXTERNALS (loaded on demand, not shipped).
  - validate-externals.ts: runtime deps validate against externals; bundled
    deps validate against dependencies + devDependencies.
  - client.ts: load @anthropic-ai/bedrock-sdk via the runtime importer so
    esbuild no longer inlines it and hoists its static @aws-sdk import into
    the CLI bundle (that was a startup crash for default installs).

Optional-dependency UX (consistent, actionable errors):
  - New src/utils/optionalRuntimeModule.ts exports importRuntimeModule and
    importOptionalRuntimeModule. The optional variant translates a missing
    package (code === 'ERR_MODULE_NOT_FOUND', specifier present in message)
    into "<feature> requires "<pkg>" ... Run `npm i -g <pkg>`". Generic so
    typed call sites keep their module types.
  - Routed ALL optional-package load sites through it (previously only one
    did): google-auth-library (client.ts, auth.ts, geminiAuth.ts),
    @anthropic-ai/foundry-sdk + @azure/identity (client.ts), and the
    @aws-sdk/* Bedrock paths (model/bedrock.ts, tokenEstimation.ts, aws.ts).
  - imageProcessor.ts: sharp-missing error now says `npm i -g sharp`.
  - docs/advanced-setup.md: new "Optional provider packages" table and a
    Vertex note documenting the on-demand installs.
  - Unit test for the helper (friendly error, success path, specifier match,
    raw passthrough).
  - knip.json: ignore google-auth-library (now loaded via runtime string).

Verified on the current tree:
  - tsc, build/validate-externals, knip, and tests all pass.
  - npm pack + install --omit=dev adds 8 packages, zero deprecation/
    allow-scripts/funding warnings; --version/--help/mcp list run.
  - With packages absent, CLAUDE_CODE_USE_BEDROCK and CLAUDE_CODE_USE_VERTEX
    print the friendly `npm i -g <pkg>` error (verified end-to-end).
  - ./sdk imports once its optional peers are present (24 exports, no warns).
  - Bundled ajv + ajv-formats validate with no ajv installed; no unguarded
    native runtime requires (fsevents absent in chokidar 4; bun:sqlite Bun-only).

Trade-off: image reads, AWS Bedrock, Azure Foundry and GCP/Vertex now prompt
a one-time `npm i -g <pkg>` instead of being shipped to every user.

Co-Authored-By: OpenClaude <openclaude@gitlawb.com>

Review fixes (CodeRabbit + jatmn):
- validate-externals: the INTENTIONALLY_BUNDLED exemption is now scoped per
  bundle. The CLI exempts every bundled package; the SDK does NOT exempt
  packages declared as peerDependencies (keyed on package.json, an independent
  source of truth) so dropping react/@anthropic-ai/sdk from SDK_EXTERNALS now
  fails validation instead of silently passing. Added an explicit minimal-
  install contract check: bundled packages must be devDependencies-only — never
  in `dependencies`, and only the SDK-external subset may be optional peers.
  Validation logic extracted to scripts/externalsValidation.ts + tests.
- FileReadTool oversized-image fallback now loads via the shared
  getImageProcessor() (not a raw import('sharp')) and re-throws
  ImageProcessorUnavailableError, so a missing processor surfaces the
  `npm i -g sharp` install hint instead of returning an over-budget image.
- optionalRuntimeModule: match the missing specifier as a QUOTED token, not a
  raw substring, so a missing transitive package whose name contains the
  requested one (sharp vs sharp-libvips, @aws-sdk/client-bedrock vs
  @aws-sdk/client-bedrock-runtime) no longer triggers the wrong install hint.
  Predicate extracted to isMissingSpecifierError() with regression tests.
- docs/advanced-setup.md: the Vertex auth section now shows both documented
  paths (gcloud ADC and a GOOGLE_APPLICATION_CREDENTIALS service-account file).

Review fixes (round 2, CodeRabbit):
- validate-externals: assert the optional-peer install contract — every
  peerDependency must be { optional: true } in peerDependenciesMeta
  (validateOptionalPeers), so losing that flag fails the build instead of
  silently reintroducing install warnings.
- validate-externals: hard-check OPTIONAL_RUNTIME_EXTERNALS placement
  (validateOptionalRuntimeexternals). Anything esbuild can see statically must
  stay external in BOTH bundles (dropping sharp/google-auth-library now fails);
  the runtime-indirection-only subset (new RUNTIME_INDIRECTION_ONLY_EXTERNALS)
  must stay OUT of externals so esbuild never re-exposes their static imports.
- Deeper-dig fix: @anthropic-ai/foundry-sdk was misclassified as
  INTENTIONALLY_BUNDLED, but it is loaded only through the Function indirection
  (esbuild never sees it, so it was never actually bundled) — its sole presence
  in dist is the specifier string. Per the PR's own "Azure Foundry now prompts"
  trade-off it is on-demand, so it now lives in OPTIONAL_RUNTIME_EXTERNALS +
  RUNTIME_INDIRECTION_ONLY_EXTERNALS (mirroring bedrock-sdk). sandbox-runtime is
  genuinely statically imported, so it stays bundled.
- Provider-routing coverage (scripts/optionalRuntimeSpecifiers.test.ts): a
  static scan asserts every importOptionalRuntimeModule specifier is a declared
  OPTIONAL_RUNTIME_EXTERNAL and never also INTENTIONALLY_BUNDLED — the
  invariant that keeps a provider's optional package loadable on demand.
- All new validators extracted to scripts/externalsValidation.ts with tests.

Review fixes (round 3, CodeRabbit):
- client.ts: gate the Vertex google-auth-library import behind the non-skip
  branch. CLAUDE_CODE_SKIP_VERTEX_AUTH (proxy/test) uses a mock GoogleAuth and
  must not require the optional package; it was loaded unconditionally before.
- optionalRuntimeModule: drop the hard-coded `npm i -g`. The helper backs both
  the global CLI and project-local ./sdk consumers, so the hint is now
  context-neutral ("npm install <pkg>" / add -g for the global CLI).
- validate-externals: every SDK_ONLY_EXTERNALS entry must STAY a
  peerDependency (a dropped peer leaves runtimeDeps while the SDK still
  externalizes it); and OPTIONAL_RUNTIME_EXTERNALS must never be shipped (fail
  on overlap with dependencies/peerDependencies). Both with tests + live-verified.
- optionalRuntimeSpecifiers.test: pin the EXACT set of optionally-loaded
  specifiers instead of a >=5 count (a count passes even if a provider path
  regresses).
- attachments: extract tryReadEditedImageAttachment() — background watched-file
  image attachments DEGRADE to null on any failure (incl.
  ImageProcessorUnavailableError) so a missing optional package never aborts a
  turn, while the explicit FileReadTool path still surfaces the install hint.
  Deterministic regression test (bad path -> null).
- docs: Bedrock row notes profile-based auth also needs
  @aws-sdk/credential-providers; install-hint wording matches the new message.

Review fixes (round 4, CodeRabbit):
- attachments: stop sending the raw file path through the analytics
  bypass-cast (tengu_watched_file_compression_failed). Send only the safe
  file extension via getFileExtensionForAnalytics, matching the existing
  tengu_file_read_dedup pattern, so no usernames/project paths can leak.
- externals.ts: corrected the OPTIONAL_RUNTIME_EXTERNALS header comment,
  which still claimed all entries "remain in COMMON_EXTERNALS" — no longer
  true since the indirection-only subset (bedrock/foundry) must stay OUT of
  the externals lists.

(Other CodeRabbit comments on this push re-surface items already addressed in
prior commits: the peerDependenciesMeta-optional check (validateOptionalPeers),
the SDK-peers-present and optional-not-shipped validator rules, the
exact-specifier-set test, the attachments degrade contract + test, and the
context-neutral install hint are all present. The "assert every optional
external is a devDependency" suggestion is intentionally NOT applied: @aws-sdk/*
and @azure/identity are transitive devDeps via bedrock-sdk/foundry-sdk, so a
blanket assertion would be incorrect; source resolution is covered by the
build + tests that import these packages.)

Review fixes (round 5, CodeRabbit):
- attachments: stop leaking file paths via logError in the background-image
  degrade path. readImageWithTokenBudget can throw path-bearing messages
  (e.g. "Image file is empty: <path>") and logError persists message/stack, so
  log only the error TYPE name now. (Analytics payload was already sanitized.)
- attachments: tryReadEditedImageAttachment takes an injectable reader so the
  degrade contract is tested for the EXACT error types — ImageProcessorUnavailableError
  and a path-bearing read error both degrade to null (not just ENOENT) — plus a
  success case. No mocking.
- validate-externals: enforce the source-install half of the optional contract.
  Non-transitive OPTIONAL_RUNTIME_EXTERNALS must be devDependencies so `bun
  install` source builds resolve them. The new TRANSITIVE_OPTIONAL_EXTERNALS
  documents the exemption (@aws-sdk/* via @anthropic-ai/bedrock-sdk, @azure/identity
  via @anthropic-ai/foundry-sdk — provided transitively, not direct devDeps). A
  blanket "all optionals are devDeps" check would have wrongly failed on those.
  Tests + live-verified (dropping sharp from devDependencies now fails).

Review fixes (round 6, CodeRabbit + jatmn):
- optionalRuntimeSpecifiers.test: the call-site scan regex missed
  generic-annotated calls (importOptionalRuntimeModule<...>(...)) in
  model/bedrock.ts and tokenEstimation.ts, so the exact-set assertion was
  incomplete. Regex now allows an optional generic; EXPECTED_SPECIFIERS adds
  @aws-sdk/client-bedrock and @aws-sdk/client-bedrock-runtime (7 total).
- importOptionalRuntimeModule default generic is now <T = unknown> (was any),
  so destructured imports are no longer silently any. Every call site now
  supplies its module type — typeof import('<pkg>') where the package is
  type-resolvable (bedrock-sdk, foundry-sdk, @aws-sdk/credential-providers,
  google-auth-library), and a named minimal-shape alias for @azure/identity
  (not a direct devDep, so typeof import can't resolve it). This gives
  compile-time verification of each provider's module contract (export names,
  shapes) — the structural answer to the "cover the provider branches" ask.
- attachments: tryReadEditedImageAttachment takes injectable {read,log,track};
  a new test asserts the sanitized-telemetry contract directly — the logError
  payload is path-free and the analytics payload carries only `ext`, never the
  edited-image path.

* fix(deps): address optional runtime review findings

* test(deps): isolate optional runtime importer mocks

* fix(deps): clarify AWS optional auth labels

* fix(deps): close optional runtime review gaps

---------

Co-authored-by: jatmn <the@jat.mn>
This commit is contained in:
Kevin Codex
2026-07-07 13:19:39 +08:00
committed by GitHub
co-authored by jatmn
parent 2a506f9f38
commit 2edec9a140
24 changed files with 1575 additions and 319 deletions
+111 -141
View File
@@ -5,18 +5,29 @@
"": {
"name": "@gitlawb/openclaude",
"dependencies": {
"@orama/orama": "^3.1.18",
"@orama/plugin-data-persistence": "^3.1.18",
"@vscode/ripgrep": "^1.17.1",
},
"devDependencies": {
"@alcalzone/ansi-tokenize": "0.3.0",
"@anthropic-ai/bedrock-sdk": "0.29.1",
"@anthropic-ai/foundry-sdk": "0.2.3",
"@anthropic-ai/sandbox-runtime": "0.0.55",
"@anthropic-ai/sdk": "0.94.0",
"@aws-sdk/client-bedrock": "3.1047.0",
"@aws-sdk/client-sts": "3.1047.0",
"@aws-sdk/credential-provider-node": "3.972.41",
"@azure/identity": "^4.13.1",
"@commander-js/extra-typings": "12.1.0",
"@grpc/grpc-js": "^1.14.3",
"@grpc/proto-loader": "^0.8.0",
"@modelcontextprotocol/sdk": "1.29.0",
"@orama/orama": "^3.1.18",
"@orama/plugin-data-persistence": "^3.1.18",
"@vscode/ripgrep": "^1.17.1",
"@smithy/core": "3.24.3",
"@smithy/node-http-handler": "4.7.3",
"@types/bun": "1.3.11",
"@types/node": "25.5.0",
"@types/react": "19.2.14",
"ajv": "8.18.0",
"auto-bind": "5.0.1",
"axios": "1.16.0",
@@ -44,6 +55,7 @@
"indent-string": "5.0.0",
"js-tiktoken": "1.0.21",
"jsonc-parser": "3.3.1",
"knip": "^6.16.1",
"lodash-es": "4.18.1",
"lru-cache": "11.2.7",
"marked": "15.0.12",
@@ -63,6 +75,7 @@
"tree-sitter-wasms": "0.1.13",
"turndown": "7.2.2",
"type-fest": "4.41.0",
"typescript": "5.9.3",
"undici": "7.28.0",
"usehooks-ts": "3.1.1",
"vscode-languageserver-protocol": "3.17.5",
@@ -73,13 +86,18 @@
"yaml": "2.8.3",
"zod": "3.25.76",
},
"devDependencies": {
"@types/bun": "1.3.11",
"@types/node": "25.5.0",
"@types/react": "19.2.14",
"knip": "^6.16.1",
"typescript": "5.9.3",
"peerDependencies": {
"@anthropic-ai/sdk": "^0.94.0",
"@modelcontextprotocol/sdk": "^1.29.0",
"react": "^19.0.0",
"react-reconciler": "^0.33.0",
},
"optionalPeers": [
"@anthropic-ai/sdk",
"@modelcontextprotocol/sdk",
"react",
"react-reconciler",
],
},
},
"overrides": {
@@ -109,10 +127,14 @@
"@aws-crypto/util": ["@aws-crypto/util@4.0.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-utf8-browser": "^3.0.0", "tslib": "^1.11.1" } }, "sha512-2EnmPy2gsFZ6m8bwUQN4jq+IyXV3quHAcwPOS6ZA3k+geujiqI8aRokO2kFJe+idJ/P3v4qWI186rVMo0+zLDQ=="],
"@aws-sdk/client-bedrock": ["@aws-sdk/client-bedrock@3.1047.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.10", "@aws-sdk/credential-provider-node": "^3.972.41", "@aws-sdk/middleware-host-header": "^3.972.11", "@aws-sdk/middleware-logger": "^3.972.10", "@aws-sdk/middleware-recursion-detection": "^3.972.12", "@aws-sdk/middleware-user-agent": "^3.972.40", "@aws-sdk/region-config-resolver": "^3.972.14", "@aws-sdk/token-providers": "3.1047.0", "@aws-sdk/types": "^3.973.8", "@aws-sdk/util-endpoints": "^3.996.9", "@aws-sdk/util-user-agent-browser": "^3.972.11", "@aws-sdk/util-user-agent-node": "^3.973.26", "@smithy/core": "^3.24.1", "@smithy/fetch-http-handler": "^5.4.1", "@smithy/node-http-handler": "^4.7.1", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-t1nSLT/FOv9KCKcrq1/KemqrPCwJUQS2gsN/6C3quZjLMAty16rqiDYLaBYfSQ43uciEi3BRC128vggXSn0YLw=="],
"@aws-sdk/client-bedrock-runtime": ["@aws-sdk/client-bedrock-runtime@3.1047.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.10", "@aws-sdk/credential-provider-node": "^3.972.41", "@aws-sdk/eventstream-handler-node": "^3.972.15", "@aws-sdk/middleware-eventstream": "^3.972.11", "@aws-sdk/middleware-host-header": "^3.972.11", "@aws-sdk/middleware-logger": "^3.972.10", "@aws-sdk/middleware-recursion-detection": "^3.972.12", "@aws-sdk/middleware-user-agent": "^3.972.40", "@aws-sdk/middleware-websocket": "^3.972.18", "@aws-sdk/region-config-resolver": "^3.972.14", "@aws-sdk/token-providers": "3.1047.0", "@aws-sdk/types": "^3.973.8", "@aws-sdk/util-endpoints": "^3.996.9", "@aws-sdk/util-user-agent-browser": "^3.972.11", "@aws-sdk/util-user-agent-node": "^3.973.26", "@smithy/core": "^3.24.1", "@smithy/fetch-http-handler": "^5.4.1", "@smithy/node-http-handler": "^4.7.1", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-b9yUBRqYQ9ADb1ta8nLXKby9pWo0lKozsauPeAT3IemBfReoY7PG7bSqGCoXkljL/H2mjBQXAbMinPaw6xhigA=="],
"@aws-sdk/client-cognito-identity": ["@aws-sdk/client-cognito-identity@3.1047.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.10", "@aws-sdk/credential-provider-node": "^3.972.41", "@aws-sdk/middleware-host-header": "^3.972.11", "@aws-sdk/middleware-logger": "^3.972.10", "@aws-sdk/middleware-recursion-detection": "^3.972.12", "@aws-sdk/middleware-user-agent": "^3.972.40", "@aws-sdk/region-config-resolver": "^3.972.14", "@aws-sdk/types": "^3.973.8", "@aws-sdk/util-endpoints": "^3.996.9", "@aws-sdk/util-user-agent-browser": "^3.972.11", "@aws-sdk/util-user-agent-node": "^3.973.26", "@smithy/core": "^3.24.1", "@smithy/fetch-http-handler": "^5.4.1", "@smithy/node-http-handler": "^4.7.1", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-Uu8cmia5ePWyimT5vXbViF2fP8/RT8261zlfz0dzgAELjCpdyT71B6JObY4IZ5tcKa/IiuKrC5ntRVfNbJFh9w=="],
"@aws-sdk/client-sts": ["@aws-sdk/client-sts@3.1047.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.10", "@aws-sdk/credential-provider-node": "^3.972.41", "@aws-sdk/middleware-host-header": "^3.972.11", "@aws-sdk/middleware-logger": "^3.972.10", "@aws-sdk/middleware-recursion-detection": "^3.972.12", "@aws-sdk/middleware-user-agent": "^3.972.40", "@aws-sdk/region-config-resolver": "^3.972.14", "@aws-sdk/signature-v4-multi-region": "^3.996.26", "@aws-sdk/types": "^3.973.8", "@aws-sdk/util-endpoints": "^3.996.9", "@aws-sdk/util-user-agent-browser": "^3.972.11", "@aws-sdk/util-user-agent-node": "^3.973.26", "@smithy/core": "^3.24.1", "@smithy/fetch-http-handler": "^5.4.1", "@smithy/node-http-handler": "^4.7.1", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-LLeyHPEYlo16wfteaAkYKZx8BrvF+ZqSkYSWodLmtk8xxCAONLOkeHyAofOswQrSetz3BWiu+sd9WNXEvucoPg=="],
"@aws-sdk/core": ["@aws-sdk/core@3.974.10", "", { "dependencies": { "@aws-sdk/types": "^3.973.8", "@aws-sdk/xml-builder": "^3.972.24", "@smithy/core": "^3.24.1", "@smithy/signature-v4": "^5.4.1", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-ZGFFlYynBR78Y/F8b/7y4i4sgW/iGwJSjoM7AZo5Et6vyr4/L0bunN+uzKMsvecCZyqcPp4RRK7Rs17l0kMujg=="],
"@aws-sdk/credential-provider-cognito-identity": ["@aws-sdk/credential-provider-cognito-identity@3.972.33", "", { "dependencies": { "@aws-sdk/nested-clients": "^3.997.8", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.1", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-yPrIm0tgMN44utkOdKe1Bkvt/NRxcWJFdhbC98tTWxWGEBAzB3lksPhGuB8TtKC0/XyfXcGrBPzx/OKXXexa4w=="],
@@ -173,6 +195,28 @@
"@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.4", "", {}, "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ=="],
"@azure/abort-controller": ["@azure/abort-controller@2.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="],
"@azure/core-auth": ["@azure/core-auth@1.10.1", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-util": "^1.13.0", "tslib": "^2.6.2" } }, "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg=="],
"@azure/core-client": ["@azure/core-client@1.10.2", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.10.0", "@azure/core-rest-pipeline": "^1.22.0", "@azure/core-tracing": "^1.3.0", "@azure/core-util": "^1.13.0", "@azure/logger": "^1.3.0", "tslib": "^2.6.2" } }, "sha512-1D2LpsU7y9xrqKjdIbsB7PlrRePw0xsVV8p+AKTlzITrWmscajryfJCdDJB/oGwvDI5HmRo04eMMADB67uwAwQ=="],
"@azure/core-rest-pipeline": ["@azure/core-rest-pipeline@1.24.0", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.10.0", "@azure/core-tracing": "^1.3.0", "@azure/core-util": "^1.13.0", "@azure/logger": "^1.3.0", "@typespec/ts-http-runtime": "^0.3.4", "tslib": "^2.6.2" } }, "sha512-PpLsoDQ3AMmKZ0VU+0GrmqMxgp/sExjlVm4R+nLWngeoEGAzOIPVifaxKGU5gMv+nWELUoHfvrolWD+ZS/nFJg=="],
"@azure/core-tracing": ["@azure/core-tracing@1.3.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ=="],
"@azure/core-util": ["@azure/core-util@1.13.1", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A=="],
"@azure/identity": ["@azure/identity@4.13.1", "", { "dependencies": { "@azure/abort-controller": "^2.0.0", "@azure/core-auth": "^1.9.0", "@azure/core-client": "^1.9.2", "@azure/core-rest-pipeline": "^1.17.0", "@azure/core-tracing": "^1.0.0", "@azure/core-util": "^1.11.0", "@azure/logger": "^1.0.0", "@azure/msal-browser": "^5.5.0", "@azure/msal-node": "^5.1.0", "open": "^10.1.0", "tslib": "^2.2.0" } }, "sha512-5C/2WD5Vb1lHnZS16dNQRPMjN6oV/Upba+C9nBIs15PmOi6A3ZGs4Lr2u60zw4S04gi+u3cEXiqTVP7M4Pz3kw=="],
"@azure/logger": ["@azure/logger@1.3.0", "", { "dependencies": { "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA=="],
"@azure/msal-browser": ["@azure/msal-browser@5.16.0", "", { "dependencies": { "@azure/msal-common": "16.11.0" } }, "sha512-Wc75FGnQgYpsm5jsOqn1H8AXsh8vXruA6vwip1nhjrJxwby7juxKAIVLr7csepmHiwdZGr6EwI5BlSc3PizEtQ=="],
"@azure/msal-common": ["@azure/msal-common@16.11.0", "", {}, "sha512-UikJOtMwkFpZNzTH6Dqk8UTUPbow15zH3e0UjGYZy69lYENW/S05gMLhbxI2eonz66uALhIljvhsSMEb6+O30g=="],
"@azure/msal-node": ["@azure/msal-node@5.3.1", "", { "dependencies": { "@azure/msal-common": "16.11.0", "jsonwebtoken": "^9.0.0" } }, "sha512-sqqv3L1UOI4KDXonNtbxPYUgbSWVXqxvmmb6BUw9n4P/UXgG+cVur3dLWQN4Cz7qQ+UJROCCxMXlksm7gIq0Sw=="],
"@babel/runtime": ["@babel/runtime@7.29.2", "", {}, "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g=="],
"@commander-js/extra-typings": ["@commander-js/extra-typings@12.1.0", "", { "peerDependencies": { "commander": "~12.1.0" } }, "sha512-wf/lwQvWAA0goIghcb91dQYpkLBcyhOhQNqG/VgWhnKzgt+UOMvra7EX/2fv70arm5RW+PUHoQHHDa6/p77Eqg=="],
@@ -427,6 +471,8 @@
"@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="],
"@typespec/ts-http-runtime": ["@typespec/ts-http-runtime@0.3.6", "", { "dependencies": { "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "tslib": "^2.6.2" } }, "sha512-jIXhD0eWQ1JA6ln/5Dltyx22UxWNrw0hZmhy2rlv6m6KgF7kplHx3g0fzi09lNmTJQRR91OlemYp3xFnvDK9og=="],
"@vscode/ripgrep": ["@vscode/ripgrep@1.18.0", "", { "optionalDependencies": { "@vscode/ripgrep-darwin-arm64": "1.18.0", "@vscode/ripgrep-darwin-x64": "1.18.0", "@vscode/ripgrep-linux-arm": "1.18.0", "@vscode/ripgrep-linux-arm64": "1.18.0", "@vscode/ripgrep-linux-ia32": "1.18.0", "@vscode/ripgrep-linux-ppc64": "1.18.0", "@vscode/ripgrep-linux-riscv64": "1.18.0", "@vscode/ripgrep-linux-s390x": "1.18.0", "@vscode/ripgrep-linux-x64": "1.18.0", "@vscode/ripgrep-win32-arm64": "1.18.0", "@vscode/ripgrep-win32-ia32": "1.18.0", "@vscode/ripgrep-win32-x64": "1.18.0" } }, "sha512-ns5lWe44tSfbTMbVUsyB+I1819PVSw4AdpgK0RNkzfWfwy6+3IUNSxwSrfTno1/oWaS/hERNz+XLWVyga2aJBQ=="],
"@vscode/ripgrep-darwin-arm64": ["@vscode/ripgrep-darwin-arm64@1.18.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-r3ktHSvbFycQNF6sl7sNDPocpsI7J+mEzh1IaZFkY0spm3k2Z9t8hPAeOK7+p0l6p6/swkQC14XWX01low+94Q=="],
@@ -489,6 +535,8 @@
"bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
"bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="],
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
@@ -537,6 +585,12 @@
"decamelize": ["decamelize@1.2.0", "", {}, "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="],
"default-browser": ["default-browser@5.5.0", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw=="],
"default-browser-id": ["default-browser-id@5.0.1", "", {}, "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q=="],
"define-lazy-prop": ["define-lazy-prop@3.0.0", "", {}, "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="],
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
@@ -681,6 +735,8 @@
"http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
"http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="],
"https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
"human-signals": ["human-signals@8.0.1", "", {}, "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ=="],
@@ -697,8 +753,12 @@
"ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
"is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
"is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="],
"is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="],
"is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
"is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
@@ -707,6 +767,8 @@
"is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="],
"is-wsl": ["is-wsl@3.1.1", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw=="],
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
"jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="],
@@ -725,6 +787,8 @@
"jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="],
"jsonwebtoken": ["jsonwebtoken@9.0.3", "", { "dependencies": { "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g=="],
"jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="],
"jws": ["jws@4.0.1", "", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="],
@@ -739,6 +803,20 @@
"lodash.debounce": ["lodash.debounce@4.0.8", "", {}, "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="],
"lodash.includes": ["lodash.includes@4.3.0", "", {}, "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="],
"lodash.isboolean": ["lodash.isboolean@3.0.3", "", {}, "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="],
"lodash.isinteger": ["lodash.isinteger@4.0.4", "", {}, "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="],
"lodash.isnumber": ["lodash.isnumber@3.0.3", "", {}, "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="],
"lodash.isplainobject": ["lodash.isplainobject@4.0.6", "", {}, "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="],
"lodash.isstring": ["lodash.isstring@4.0.1", "", {}, "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="],
"lodash.once": ["lodash.once@4.1.1", "", {}, "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="],
"long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
"lru-cache": ["lru-cache@11.2.7", "", {}, "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA=="],
@@ -781,6 +859,8 @@
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
"open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="],
"oxc-parser": ["oxc-parser@0.133.0", "", { "dependencies": { "@oxc-project/types": "^0.133.0" }, "optionalDependencies": { "@oxc-parser/binding-android-arm-eabi": "0.133.0", "@oxc-parser/binding-android-arm64": "0.133.0", "@oxc-parser/binding-darwin-arm64": "0.133.0", "@oxc-parser/binding-darwin-x64": "0.133.0", "@oxc-parser/binding-freebsd-x64": "0.133.0", "@oxc-parser/binding-linux-arm-gnueabihf": "0.133.0", "@oxc-parser/binding-linux-arm-musleabihf": "0.133.0", "@oxc-parser/binding-linux-arm64-gnu": "0.133.0", "@oxc-parser/binding-linux-arm64-musl": "0.133.0", "@oxc-parser/binding-linux-ppc64-gnu": "0.133.0", "@oxc-parser/binding-linux-riscv64-gnu": "0.133.0", "@oxc-parser/binding-linux-riscv64-musl": "0.133.0", "@oxc-parser/binding-linux-s390x-gnu": "0.133.0", "@oxc-parser/binding-linux-x64-gnu": "0.133.0", "@oxc-parser/binding-linux-x64-musl": "0.133.0", "@oxc-parser/binding-openharmony-arm64": "0.133.0", "@oxc-parser/binding-wasm32-wasi": "0.133.0", "@oxc-parser/binding-win32-arm64-msvc": "0.133.0", "@oxc-parser/binding-win32-ia32-msvc": "0.133.0", "@oxc-parser/binding-win32-x64-msvc": "0.133.0" } }, "sha512-661RSx+ZcjBmjBYid+Fpp/2F5EbtildpeoZh5HdgnGs+jZ03nqQEQW8yGkt4BGyOC3OMPDQQRl8M5kqD2/g6jw=="],
"oxc-resolver": ["oxc-resolver@11.20.0", "", { "optionalDependencies": { "@oxc-resolver/binding-android-arm-eabi": "11.20.0", "@oxc-resolver/binding-android-arm64": "11.20.0", "@oxc-resolver/binding-darwin-arm64": "11.20.0", "@oxc-resolver/binding-darwin-x64": "11.20.0", "@oxc-resolver/binding-freebsd-x64": "11.20.0", "@oxc-resolver/binding-linux-arm-gnueabihf": "11.20.0", "@oxc-resolver/binding-linux-arm-musleabihf": "11.20.0", "@oxc-resolver/binding-linux-arm64-gnu": "11.20.0", "@oxc-resolver/binding-linux-arm64-musl": "11.20.0", "@oxc-resolver/binding-linux-ppc64-gnu": "11.20.0", "@oxc-resolver/binding-linux-riscv64-gnu": "11.20.0", "@oxc-resolver/binding-linux-riscv64-musl": "11.20.0", "@oxc-resolver/binding-linux-s390x-gnu": "11.20.0", "@oxc-resolver/binding-linux-x64-gnu": "11.20.0", "@oxc-resolver/binding-linux-x64-musl": "11.20.0", "@oxc-resolver/binding-openharmony-arm64": "11.20.0", "@oxc-resolver/binding-wasm32-wasi": "11.20.0", "@oxc-resolver/binding-win32-arm64-msvc": "11.20.0", "@oxc-resolver/binding-win32-x64-msvc": "11.20.0" } }, "sha512-CblytBiV/a/ZXY34dsVU2NxhIOxMXst8CvDCtyBelVITgd7PLrKzbEbA6oKLdPjvDKDzCiW48qzmzZ+mYaqn+g=="],
@@ -855,6 +935,8 @@
"router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
"run-applescript": ["run-applescript@7.1.0", "", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="],
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
@@ -925,7 +1007,7 @@
"ts-algebra": ["ts-algebra@2.0.0", "", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="],
"tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="],
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"turndown": ["turndown@7.2.2", "", { "dependencies": { "@mixmark-io/domino": "^2.2.0" } }, "sha512-1F7db8BiExOKxjSMU2b7if62D/XOyQyZbPKq/nUwopfgnHlqXHqQ0lvfUTeUIr1lZJzOPFn43dODyMSIfvWRKQ=="],
@@ -971,6 +1053,8 @@
"ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="],
"wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="],
"xml-naming": ["xml-naming@0.1.0", "", {}, "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw=="],
"xss": ["xss@1.0.15", "", { "dependencies": { "commander": "^2.20.3", "cssfilter": "0.0.10" }, "bin": { "xss": "bin/xss" } }, "sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg=="],
@@ -993,232 +1077,118 @@
"@aws-crypto/crc32/@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="],
"@aws-crypto/crc32/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-crypto/sha256-browser/@aws-crypto/sha256-js": ["@aws-crypto/sha256-js@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA=="],
"@aws-crypto/sha256-browser/@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="],
"@aws-crypto/sha256-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
"@aws-crypto/sha256-browser/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-crypto/sha256-js/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="],
"@aws-crypto/supports-web-crypto/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-crypto/util/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="],
"@aws-sdk/client-bedrock/@aws-crypto/sha256-js": ["@aws-crypto/sha256-js@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA=="],
"@aws-sdk/client-bedrock/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/client-bedrock-runtime/@aws-crypto/sha256-js": ["@aws-crypto/sha256-js@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA=="],
"@aws-sdk/client-bedrock-runtime/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/client-bedrock-runtime/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/client-cognito-identity/@aws-crypto/sha256-js": ["@aws-crypto/sha256-js@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA=="],
"@aws-sdk/client-cognito-identity/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/client-cognito-identity/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/client-sts/@aws-crypto/sha256-js": ["@aws-crypto/sha256-js@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA=="],
"@aws-sdk/client-sts/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.4.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g=="],
"@aws-sdk/core/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/core/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/credential-provider-cognito-identity/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/credential-provider-cognito-identity/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/credential-provider-env/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/credential-provider-env/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/credential-provider-http/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/credential-provider-http/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/credential-provider-ini/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/credential-provider-ini/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/credential-provider-login/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/credential-provider-login/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/credential-provider-node/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/credential-provider-node/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/credential-provider-process/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/credential-provider-process/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/credential-provider-sso/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/credential-provider-sso/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/credential-provider-web-identity/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/credential-provider-web-identity/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/credential-providers/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/credential-providers/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/eventstream-handler-node/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/eventstream-handler-node/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/middleware-eventstream/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/middleware-eventstream/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/middleware-host-header/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/middleware-host-header/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/middleware-logger/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/middleware-logger/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/middleware-recursion-detection/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/middleware-recursion-detection/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/middleware-user-agent/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/middleware-user-agent/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/middleware-websocket/@smithy/signature-v4": ["@smithy/signature-v4@5.4.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g=="],
"@aws-sdk/middleware-websocket/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/middleware-websocket/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/nested-clients/@aws-crypto/sha256-js": ["@aws-crypto/sha256-js@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA=="],
"@aws-sdk/nested-clients/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/nested-clients/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/region-config-resolver/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/region-config-resolver/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/signature-v4-multi-region/@smithy/signature-v4": ["@smithy/signature-v4@5.4.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g=="],
"@aws-sdk/signature-v4-multi-region/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/signature-v4-multi-region/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/token-providers/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/token-providers/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/types/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/types/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/util-endpoints/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/util-endpoints/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/util-locate-window/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/util-user-agent-browser/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/util-user-agent-browser/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/util-user-agent-node/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/util-user-agent-node/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/util-utf8-browser/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@aws-sdk/xml-builder/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/xml-builder/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@emnapi/core/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@emnapi/runtime/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@emnapi/wasi-threads/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/abort-controller/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/core/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@smithy/core/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/credential-provider-imds/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@smithy/credential-provider-imds/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/eventstream-codec/@aws-crypto/crc32": ["@aws-crypto/crc32@3.0.0", "", { "dependencies": { "@aws-crypto/util": "^3.0.0", "@aws-sdk/types": "^3.222.0", "tslib": "^1.11.1" } }, "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA=="],
"@smithy/eventstream-codec/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-7iKXR+/4TpLK194pVjKiasIyqMtTYJsgKgM242Y9uzt5dhHnUDvMNb+3xIhRJ9QhvqGii/5cRUt4fJn3dtXNHQ=="],
"@smithy/eventstream-codec/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/eventstream-serde-node/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/eventstream-serde-universal/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/fetch-http-handler/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@smithy/fetch-http-handler/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/is-array-buffer/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/middleware-endpoint/@smithy/util-middleware": ["@smithy/util-middleware@2.2.0", "", { "dependencies": { "@smithy/types": "^2.12.0", "tslib": "^2.6.2" } }, "sha512-L1qpleXf9QD6LwLCJ5jddGkgWyuSvWBkJwWAZ6kFkdifdso+sk3L3O1HdmPvCdnCK3IS4qWyPxev01QMnfHSBw=="],
"@smithy/middleware-endpoint/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/middleware-serde/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/middleware-stack/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/node-config-provider/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/node-http-handler/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@smithy/node-http-handler/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/property-provider/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/protocol-http/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-jtmJMyt1xMD/d8OtbVJ2gFZOSKc+ueYJZPW20ULW1GOp/q/YIM0wNh+u8ZFao9UaIGz4WoPW8hC64qlWLIfoDA=="],
"@smithy/querystring-builder/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/querystring-parser/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/shared-ini-file-loader/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/signature-v4/@smithy/types": ["@smithy/types@3.7.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg=="],
"@smithy/signature-v4/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/smithy-client/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/types/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/url-parser/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/util-base64/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
"@smithy/util-base64/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
"@smithy/util-buffer-from/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/util-hex-encoding/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/util-middleware/@smithy/types": ["@smithy/types@3.7.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg=="],
"@smithy/util-middleware/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@2.5.0", "", { "dependencies": { "@smithy/protocol-http": "^3.3.0", "@smithy/querystring-builder": "^2.2.0", "@smithy/types": "^2.12.0", "@smithy/util-base64": "^2.3.0", "tslib": "^2.6.2" } }, "sha512-BOWEBeppWhLn/no/JxUL/ghTfANTjT7kg3Ww2rPqTUY9R4yHPXxJ9JhMe3Z03LN3aPwiwlpDIUcVw1xDyHqEhw=="],
"@smithy/util-stream/@smithy/node-http-handler": ["@smithy/node-http-handler@2.5.0", "", { "dependencies": { "@smithy/abort-controller": "^2.2.0", "@smithy/protocol-http": "^3.3.0", "@smithy/querystring-builder": "^2.2.0", "@smithy/types": "^2.12.0", "tslib": "^2.6.2" } }, "sha512-mVGyPBzkkGQsPoxQUbxlEfRjrj6FPyA3u3u2VXGr9hT8wilsoQdZdvKpMBFMB8Crfhv5dNkKHIW0Yyuc7eABqA=="],
@@ -1227,16 +1197,8 @@
"@smithy/util-stream/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
"@smithy/util-stream/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/util-uri-escape/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@3.0.0", "", { "dependencies": { "@smithy/is-array-buffer": "^3.0.0", "tslib": "^2.6.2" } }, "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA=="],
"@smithy/util-utf8/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@tybys/wasm-util/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"cli-highlight/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"cli-highlight/yargs": ["yargs@16.2.0", "", { "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } }, "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw=="],
@@ -1275,8 +1237,12 @@
"@aws-sdk/client-bedrock-runtime/@aws-crypto/sha256-js/@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="],
"@aws-sdk/client-bedrock/@aws-crypto/sha256-js/@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="],
"@aws-sdk/client-cognito-identity/@aws-crypto/sha256-js/@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="],
"@aws-sdk/client-sts/@aws-crypto/sha256-js/@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="],
"@aws-sdk/nested-clients/@aws-crypto/sha256-js/@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="],
"@smithy/eventstream-codec/@aws-crypto/crc32/@aws-crypto/util": ["@aws-crypto/util@3.0.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-utf8-browser": "^3.0.0", "tslib": "^1.11.1" } }, "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w=="],
@@ -1317,8 +1283,12 @@
"@aws-sdk/client-bedrock-runtime/@aws-crypto/sha256-js/@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
"@aws-sdk/client-bedrock/@aws-crypto/sha256-js/@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
"@aws-sdk/client-cognito-identity/@aws-crypto/sha256-js/@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
"@aws-sdk/client-sts/@aws-crypto/sha256-js/@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
"@aws-sdk/nested-clients/@aws-crypto/sha256-js/@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
"cli-highlight/yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
+31 -1
View File
@@ -98,10 +98,22 @@ third-party gateways.
Authentication uses Google Application Default Credentials through
`google-auth-library`. There is no `OPENAI_API_KEY`-style API key for this
route. Authenticate with either a service-account file or local ADC:
route. **For global npm installs, install the auth package on demand** (it is
not bundled by default — see [Optional provider packages](#optional-provider-packages)):
```bash
npm i -g google-auth-library
```
Authenticate with either local Application Default Credentials (ADC) or a
service-account key file:
```bash
# Option 1 — local ADC (interactive, uses your own Google account):
gcloud auth application-default login
# Option 2 — service-account key file (headless / CI):
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
```
Minimal setup:
@@ -366,6 +378,24 @@ export OPENAI_MODEL=accounts/fireworks/models/llama-v3p1-70b-instruct
The **OpenClaude VS Code extension** can store the key in Secret Storage and set these variables for you when you launch from the Control Center. See `vscode-extension/openclaude-vscode/README.md`.
## Optional provider packages
To keep the default `npm i -g @gitlawb/openclaude` install small and
warning-free, a few provider SDKs and the native image library are **not
bundled**. They are loaded on demand, and the CLI prints an `npm install <pkg>`
hint (add `-g` for the global CLI) if you enable a feature whose package is
missing. Install only what you need:
| Feature | Trigger | Install |
| --- | --- | --- |
| AWS Bedrock | `CLAUDE_CODE_USE_BEDROCK=1` | `npm i -g @anthropic-ai/bedrock-sdk`. Profile-based auth (`~/.aws/credentials`) additionally needs `@aws-sdk/credential-providers` and `@aws-sdk/client-sts`; model listing needs `@aws-sdk/client-bedrock`. Proxy and skip-auth setups may also need `@aws-sdk/credential-provider-node`, `@smithy/node-http-handler`, or `@smithy/core`. The CLI prints the exact missing package if you hit one. |
| Azure Foundry | `CLAUDE_CODE_USE_FOUNDRY=1` | `npm i -g @anthropic-ai/foundry-sdk @azure/identity` |
| Claude on Vertex AI / Gemini ADC | `CLAUDE_CODE_USE_VERTEX=1` / Gemini ADC auth | `npm i -g google-auth-library` |
| Reading/processing images | reading an image file | `npm i -g sharp` |
When installing OpenClaude from source (`bun install`), all of these are
already present as dev dependencies, so source/dev builds need no extra steps.
## Environment Variables
| Variable | Required | Description |
+2 -1
View File
@@ -32,6 +32,7 @@
],
"ignoreDependencies": [
"@types/bun",
"@anthropic-ai/foundry-sdk"
"@anthropic-ai/foundry-sdk",
"google-auth-library"
]
}
+27 -9
View File
@@ -75,18 +75,29 @@
"prepack": "npm run build"
},
"dependencies": {
"@orama/orama": "^3.1.18",
"@orama/plugin-data-persistence": "^3.1.18",
"@vscode/ripgrep": "^1.17.1"
},
"devDependencies": {
"@alcalzone/ansi-tokenize": "0.3.0",
"@anthropic-ai/bedrock-sdk": "0.29.1",
"@anthropic-ai/foundry-sdk": "0.2.3",
"@anthropic-ai/sandbox-runtime": "0.0.55",
"@anthropic-ai/sdk": "0.94.0",
"@aws-sdk/client-bedrock": "3.1047.0",
"@aws-sdk/client-sts": "3.1047.0",
"@aws-sdk/credential-provider-node": "3.972.41",
"@azure/identity": "^4.13.1",
"@commander-js/extra-typings": "12.1.0",
"@grpc/grpc-js": "^1.14.3",
"@grpc/proto-loader": "^0.8.0",
"@modelcontextprotocol/sdk": "1.29.0",
"@orama/orama": "^3.1.18",
"@orama/plugin-data-persistence": "^3.1.18",
"@vscode/ripgrep": "^1.17.1",
"@smithy/core": "3.24.3",
"@smithy/node-http-handler": "4.7.3",
"@types/bun": "1.3.11",
"@types/node": "25.5.0",
"@types/react": "19.2.14",
"ajv": "8.18.0",
"auto-bind": "5.0.1",
"axios": "1.16.0",
@@ -114,6 +125,7 @@
"indent-string": "5.0.0",
"js-tiktoken": "1.0.21",
"jsonc-parser": "3.3.1",
"knip": "^6.16.1",
"lodash-es": "4.18.1",
"lru-cache": "11.2.7",
"marked": "15.0.12",
@@ -133,6 +145,7 @@
"tree-sitter-wasms": "0.1.13",
"turndown": "7.2.2",
"type-fest": "4.41.0",
"typescript": "5.9.3",
"undici": "7.28.0",
"usehooks-ts": "3.1.1",
"web-tree-sitter": "0.25.10",
@@ -143,12 +156,17 @@
"yaml": "2.8.3",
"zod": "3.25.76"
},
"devDependencies": {
"@types/bun": "1.3.11",
"@types/node": "25.5.0",
"@types/react": "19.2.14",
"knip": "^6.16.1",
"typescript": "5.9.3"
"peerDependencies": {
"@anthropic-ai/sdk": "^0.94.0",
"@modelcontextprotocol/sdk": "^1.29.0",
"react": "^19.0.0",
"react-reconciler": "^0.33.0"
},
"peerDependenciesMeta": {
"@anthropic-ai/sdk": { "optional": true },
"@modelcontextprotocol/sdk": { "optional": true },
"react": { "optional": true },
"react-reconciler": { "optional": true }
},
"engines": {
"node": ">=22.0.0"
+70 -8
View File
@@ -6,7 +6,12 @@
* added here (large packages, native modules, or packages with many exports).
*/
// Packages that should be kept external in ALL bundles (CLI + SDK)
// Packages that should be kept external in ALL bundles (CLI + SDK).
// NOTE: some entries here are ALSO in OPTIONAL_RUNTIME_EXTERNALS below
// (sharp, google-auth-library, @aws-sdk/*, @azure/identity). That overlap is
// intentional: membership here means "never inline into the bundle", while
// membership in OPTIONAL_RUNTIME_EXTERNALS additionally means "not shipped in
// the default install — loaded on demand". A package can be both.
export const COMMON_EXTERNALS: string[] = [
// Native image processing
'sharp',
@@ -14,7 +19,10 @@ export const COMMON_EXTERNALS: string[] = [
'@aws-sdk/client-bedrock',
'@aws-sdk/client-bedrock-runtime',
'@aws-sdk/client-sts',
'@aws-sdk/credential-provider-node',
'@aws-sdk/credential-providers',
'@smithy/core',
'@smithy/node-http-handler',
'@azure/identity',
'google-auth-library',
// @vscode/ripgrep ships a platform-specific binary alongside its
@@ -42,16 +50,67 @@ export const SDK_ONLY_EXTERNALS: string[] = [
'@modelcontextprotocol/sdk',
]
// Packages kept external but NOT listed in package.json dependencies.
// These are dynamically imported at runtime — they're optional and resolved
// from transitive deps or installed by users who need that provider/protocol.
// Optional runtime packages: dynamically imported only when a provider/feature
// needs them, and NOT listed in package.json `dependencies`, so a default
// `npm install -g @gitlawb/openclaude` stays small and warning-free.
//
// Two shapes (see RUNTIME_INDIRECTION_ONLY_EXTERNALS below):
// - Most stay external in both bundles (in COMMON_EXTERNALS) so esbuild never
// inlines them — they ARE referenced where esbuild can see them.
// - The indirection-only subset (@anthropic-ai/{bedrock,foundry}-sdk) is the
// opposite: loaded purely via the runtime importer, so esbuild never sees a
// static reference and they must stay OUT of the externals lists.
export const OPTIONAL_RUNTIME_EXTERNALS: string[] = [
// Cloud provider SDKs (dynamically imported per-provider)
'@aws-sdk/client-bedrock',
'@aws-sdk/client-bedrock-runtime',
'@aws-sdk/client-sts',
'@aws-sdk/credential-provider-node',
'@aws-sdk/credential-providers',
'@smithy/core',
'@smithy/node-http-handler',
'@azure/identity',
// Anthropic Bedrock client — loaded via the runtime importer in
// services/api/client.ts. Not bundled (it statically imports @aws-sdk) and
// not shipped; Bedrock users install it on demand (it pulls @aws-sdk itself).
'@anthropic-ai/bedrock-sdk',
// Anthropic Foundry client — also loaded only via the runtime importer in
// services/api/client.ts (CLAUDE_CODE_USE_FOUNDRY). The Function indirection
// means esbuild never sees it, so it is not bundled; Foundry users install it
// on demand. (It is NOT in COMMON_EXTERNALS for the same reason as bedrock.)
'@anthropic-ai/foundry-sdk',
// GCP/Vertex auth — loaded via runtime import in services/api/client.ts.
// Optional: only Vertex users need it. Its transitive tree (gaxios →
// node-fetch → fetch-blob → node-domexception) is what triggered the
// deprecation warning on install, so we no longer ship it by default.
'google-auth-library',
// Native image processing — loaded via dynamic import in the image tools.
// Optional: only image reads need it, and it carries a native install
// script. Kept opt-in so default installs run no install scripts.
'sharp',
]
// OPTIONAL_RUNTIME_EXTERNALS that are loaded ONLY through the runtime importer
// (the `new Function` indirection in src/utils/optionalRuntimeModule.ts), so
// esbuild never sees a static reference to them. These must NOT appear in the
// externals lists: marking @anthropic-ai/bedrock-sdk external would let esbuild
// keep (and at startup evaluate) its static `@aws-sdk/client-bedrock-runtime`
// import, which is exactly the default-install crash this design avoids. Every
// OTHER optional external IS referenced somewhere esbuild can see (e.g. sharp's
// dynamic import in imageProcessor.ts) and therefore must stay external.
export const RUNTIME_INDIRECTION_ONLY_EXTERNALS: string[] = [
'@anthropic-ai/bedrock-sdk',
'@anthropic-ai/foundry-sdk',
]
// OPTIONAL_RUNTIME_EXTERNALS that are NOT direct devDependencies because they
// are pulled transitively by another optional package's dependency tree, so
// source builds/tests still resolve them. Every OTHER optional external must be
// a direct devDependency (validated) so `bun install` source/dev builds keep
// working.
export const TRANSITIVE_OPTIONAL_EXTERNALS: string[] = [
'@aws-sdk/client-bedrock-runtime',
'@aws-sdk/credential-providers',
]
// Computed full lists
@@ -61,10 +120,13 @@ export const SDK_EXTERNALS: string[] = [...COMMON_EXTERNALS, ...SDK_ONLY_EXTERNA
// Packages intentionally bundled (not external, not flagged by validation)
// These are small utilities that are fine to inline into the output bundle.
export const INTENTIONALLY_BUNDLED: string[] = [
// Test utilities (bundled, not external)
// Anthropic provider variants (bundled, not the main SDK)
'@anthropic-ai/bedrock-sdk',
'@anthropic-ai/foundry-sdk',
// Anthropic provider variants (bundled, not the main SDK).
// NOTE: @anthropic-ai/bedrock-sdk AND @anthropic-ai/foundry-sdk are
// intentionally NOT bundled — they are loaded only via the runtime importer in
// services/api/client.ts (esbuild never sees the specifier), so they live in
// OPTIONAL_RUNTIME_EXTERNALS / RUNTIME_INDIRECTION_ONLY_EXTERNALS and Bedrock /
// Foundry users install them on demand. @anthropic-ai/sandbox-runtime IS
// statically imported (utils/sandbox/sandbox-adapter.ts), so esbuild bundles it.
'@anthropic-ai/sandbox-runtime',
// CLI / TUI utilities
'@alcalzone/ansi-tokenize',
+252
View File
@@ -0,0 +1,252 @@
import { describe, expect, test } from 'bun:test'
import {
bundledExemptionFor,
validateBundleExternals,
validateIntentionallyBundled,
validateOptionalPeers,
validateOptionalRuntimeExternals,
type PkgDeps,
} from './externalsValidation.js'
// Mirrors the real shape: a few packages bundled in both, plus SDK-external peers.
const INTENTIONALLY_BUNDLED = ['chalk', 'zod', 'react', '@anthropic-ai/sdk']
const SDK_ONLY_EXTERNALS = ['react', '@anthropic-ai/sdk']
const COMMON_EXTERNALS = ['sharp', '@vscode/ripgrep']
const SDK_EXTERNALS = [...COMMON_EXTERNALS, ...SDK_ONLY_EXTERNALS]
describe('bundledExemptionFor', () => {
test('CLI exempts every bundled package; SDK excludes peer-provided ones', () => {
const cli = bundledExemptionFor(INTENTIONALLY_BUNDLED, new Set())
expect(cli.has('react')).toBe(true)
const peers = new Set(['react', '@anthropic-ai/sdk'])
const sdk = bundledExemptionFor(INTENTIONALLY_BUNDLED, peers)
expect(sdk.has('chalk')).toBe(true) // bundled in both
expect(sdk.has('react')).toBe(false) // peer => external in SDK, not exempt
expect(sdk.has('@anthropic-ai/sdk')).toBe(false)
})
})
describe('validateBundleExternals', () => {
const runtimeDeps = new Set(['@vscode/ripgrep', 'react', '@anthropic-ai/sdk'])
test('passes when every runtime dep is external or bundled-in-this-bundle', () => {
const sdkExemption = bundledExemptionFor(
INTENTIONALLY_BUNDLED,
new Set(['react', '@anthropic-ai/sdk']),
)
const r = validateBundleExternals('SDK', runtimeDeps, SDK_EXTERNALS, sdkExemption)
expect(r.ok).toBe(true)
})
test('FAILS when an SDK-external peer is dropped from SDK_EXTERNALS', () => {
// The regression Jatmn flagged: drop react from externals but keep it a peer.
const brokenSdkExternals = SDK_EXTERNALS.filter(d => d !== 'react')
const sdkExemption = bundledExemptionFor(
INTENTIONALLY_BUNDLED,
new Set(['react', '@anthropic-ai/sdk']), // peers are independent of externals
)
const r = validateBundleExternals('SDK', runtimeDeps, brokenSdkExternals, sdkExemption)
expect(r.ok).toBe(false)
expect(r.errors.join(' ')).toContain('react')
})
test('a CLI-bundled-but-SDK-external package is still exempt in the CLI', () => {
const cliExemption = bundledExemptionFor(INTENTIONALLY_BUNDLED, new Set())
// CLI externals do not include react (it is bundled into the CLI).
const r = validateBundleExternals('CLI', runtimeDeps, COMMON_EXTERNALS, cliExemption)
expect(r.ok).toBe(true)
})
})
describe('validateIntentionallyBundled', () => {
const healthy: PkgDeps = {
dependencies: { '@vscode/ripgrep': '^1' },
peerDependencies: { react: '*', '@anthropic-ai/sdk': '*' },
devDependencies: {
chalk: '^5',
zod: '^3',
react: '^18',
'@anthropic-ai/sdk': '^0',
},
}
test('passes the real-shaped contract', () => {
const r = validateIntentionallyBundled(healthy, INTENTIONALLY_BUNDLED, SDK_ONLY_EXTERNALS)
expect(r.ok).toBe(true)
})
test('FAILS when a bundled package is shipped as a runtime dependency', () => {
const pkg: PkgDeps = {
...healthy,
dependencies: { ...healthy.dependencies, chalk: '^5' },
}
const r = validateIntentionallyBundled(pkg, INTENTIONALLY_BUNDLED, SDK_ONLY_EXTERNALS)
expect(r.ok).toBe(false)
expect(r.errors.join(' ')).toMatch(/must not be in dependencies.*chalk/)
})
test('FAILS when a bundled-only package is declared as a peerDependency', () => {
const pkg: PkgDeps = {
...healthy,
peerDependencies: { ...healthy.peerDependencies, zod: '^3' }, // zod is not SDK-external
}
const r = validateIntentionallyBundled(pkg, INTENTIONALLY_BUNDLED, SDK_ONLY_EXTERNALS)
expect(r.ok).toBe(false)
expect(r.errors.join(' ')).toMatch(/peerDependencies.*zod/)
})
test('FAILS when a bundled package is missing from devDependencies', () => {
const pkg: PkgDeps = {
...healthy,
devDependencies: { zod: '^3', react: '^18', '@anthropic-ai/sdk': '^0' }, // chalk missing
}
const r = validateIntentionallyBundled(pkg, INTENTIONALLY_BUNDLED, SDK_ONLY_EXTERNALS)
expect(r.ok).toBe(false)
expect(r.errors.join(' ')).toMatch(/devDependencies.*chalk/)
})
test('FAILS when an SDK external drops out of peerDependencies', () => {
const pkg: PkgDeps = {
...healthy,
peerDependencies: { react: '*' }, // @anthropic-ai/sdk no longer a peer
}
const r = validateIntentionallyBundled(pkg, INTENTIONALLY_BUNDLED, SDK_ONLY_EXTERNALS)
expect(r.ok).toBe(false)
expect(r.errors.join(' ')).toMatch(/remain peerDependencies.*@anthropic-ai\/sdk/)
})
})
describe('validateOptionalPeers', () => {
test('passes when every peer is marked optional', () => {
const pkg: PkgDeps = {
peerDependencies: { react: '*', '@anthropic-ai/sdk': '*' },
peerDependenciesMeta: {
react: { optional: true },
'@anthropic-ai/sdk': { optional: true },
},
}
expect(validateOptionalPeers(pkg).ok).toBe(true)
})
test('FAILS when a peer loses its optional flag (warning-free install regresses)', () => {
const pkg: PkgDeps = {
peerDependencies: { react: '*', '@anthropic-ai/sdk': '*' },
peerDependenciesMeta: { react: { optional: true } }, // @anthropic-ai/sdk no longer optional
}
const r = validateOptionalPeers(pkg)
expect(r.ok).toBe(false)
expect(r.errors.join(' ')).toContain('@anthropic-ai/sdk')
})
test('FAILS when peerDependenciesMeta is missing entirely', () => {
const pkg: PkgDeps = { peerDependencies: { react: '*' } }
expect(validateOptionalPeers(pkg).ok).toBe(false)
})
})
describe('validateOptionalRuntimeExternals', () => {
const OPTIONAL = ['sharp', 'google-auth-library', '@anthropic-ai/bedrock-sdk']
const INDIRECTION_ONLY = ['@anthropic-ai/bedrock-sdk']
const cli = ['sharp', 'google-auth-library']
const sdk = ['sharp', 'google-auth-library']
// All non-transitive optionals present as devDeps, so these cases isolate the
// externals-placement behavior from the source-install (devDeps) check.
const healthyDev: PkgDeps = {
devDependencies: {
sharp: '*',
'google-auth-library': '*',
'@anthropic-ai/bedrock-sdk': '*',
},
}
test('passes when esbuild-visible optionals are external and indirection-only is not', () => {
const r = validateOptionalRuntimeExternals(OPTIONAL, cli, sdk, INDIRECTION_ONLY, healthyDev)
expect(r.ok).toBe(true)
})
test('FAILS when an optional external is dropped from the externals lists', () => {
// The regression: sharp removed from CLI/SDK externals would get bundled.
const r = validateOptionalRuntimeExternals(
OPTIONAL,
['google-auth-library'], // sharp dropped from CLI
sdk,
INDIRECTION_ONLY,
healthyDev,
)
expect(r.ok).toBe(false)
expect(r.errors.join(' ')).toMatch(/sharp.*CLI_EXTERNALS/)
})
test('FAILS when the indirection-only package leaks into the externals lists', () => {
// @anthropic-ai/bedrock-sdk as external would re-expose its static @aws-sdk import.
const r = validateOptionalRuntimeExternals(
OPTIONAL,
[...cli, '@anthropic-ai/bedrock-sdk'],
sdk,
INDIRECTION_ONLY,
healthyDev,
)
expect(r.ok).toBe(false)
expect(r.errors.join(' ')).toMatch(/bedrock-sdk.*must NOT/)
})
test('FAILS on a stray indirection-only entry not in the optional set', () => {
const r = validateOptionalRuntimeExternals(OPTIONAL, cli, sdk, ['not-optional-pkg'], healthyDev)
expect(r.ok).toBe(false)
expect(r.errors.join(' ')).toContain('not-optional-pkg')
})
test('FAILS when an optional external is shipped in dependencies', () => {
const pkg: PkgDeps = { dependencies: { sharp: '^0.33' } }
const r = validateOptionalRuntimeExternals(OPTIONAL, cli, sdk, INDIRECTION_ONLY, pkg)
expect(r.ok).toBe(false)
expect(r.errors.join(' ')).toMatch(/must not be shipped.*sharp/)
})
test('FAILS when an optional external is shipped as a peerDependency', () => {
const pkg: PkgDeps = { peerDependencies: { 'google-auth-library': '*' } }
const r = validateOptionalRuntimeExternals(OPTIONAL, cli, sdk, INDIRECTION_ONLY, pkg)
expect(r.ok).toBe(false)
expect(r.errors.join(' ')).toMatch(/must not be shipped.*google-auth-library/)
})
test('FAILS when a non-transitive optional external drops out of devDependencies', () => {
// sharp is directly imported, so it must be a devDependency for source builds.
const pkg: PkgDeps = {
devDependencies: { 'google-auth-library': '*', '@anthropic-ai/bedrock-sdk': '*' }, // sharp missing
}
const r = validateOptionalRuntimeExternals(
OPTIONAL,
cli,
sdk,
INDIRECTION_ONLY,
pkg,
['@example/transitive-optional'], // unrelated transitive exemption
)
expect(r.ok).toBe(false)
expect(r.errors.join(' ')).toMatch(/missing from devDependencies.*sharp/)
})
test('exempts transitive optional externals from the devDependencies check', () => {
const pkg: PkgDeps = {
devDependencies: {
sharp: '*',
'google-auth-library': '*',
'@anthropic-ai/bedrock-sdk': '*',
},
}
// Synthetic transitive optionals are exempt when another optional package
// guarantees them in source installs.
const r = validateOptionalRuntimeExternals(
[...OPTIONAL, '@example/transitive-optional'],
[...cli, '@example/transitive-optional'],
[...sdk, '@example/transitive-optional'],
INDIRECTION_ONLY,
pkg,
['@example/transitive-optional'],
)
expect(r.ok).toBe(true)
})
})
+221
View File
@@ -0,0 +1,221 @@
/**
* Pure validation helpers for the externals/bundling contract, factored out of
* validate-externals.ts so the rules (bundle-specific bundled exemptions, the
* minimal-install dependency placement contract) are unit-testable with
* synthetic package.json / externals inputs.
*/
export type ValidationResult = { ok: boolean; errors: string[] }
export type PkgDeps = {
dependencies?: Record<string, string>
peerDependencies?: Record<string, string>
peerDependenciesMeta?: Record<string, { optional?: boolean }>
devDependencies?: Record<string, string>
}
/**
* The set of INTENTIONALLY_BUNDLED packages that are genuinely inlined into a
* given bundle. A package declared as a peerDependency is provided by the
* consumer, so it must be EXTERNAL in the SDK bundle and is therefore NOT
* exempt there — pass `peerDepNames` (from package.json) for the SDK so the
* exemption stays independent of the externals list it is meant to guard.
*/
export function bundledExemptionFor(
intentionallyBundled: string[],
externalizedHere: ReadonlySet<string>,
): Set<string> {
return new Set(intentionallyBundled.filter(d => !externalizedHere.has(d)))
}
/**
* Every runtime dependency (shipped `dependencies` + `peerDependencies`) must be
* a genuine external for a bundle, unless it is intentionally bundled INTO that
* bundle. Anything else would be missing at runtime for end users.
*/
export function validateBundleExternals(
bundleName: string,
runtimeDeps: ReadonlySet<string>,
externals: string[],
bundledExemption: ReadonlySet<string>,
): ValidationResult {
const externalSet = new Set(externals)
const missing = [...runtimeDeps].filter(
d => !externalSet.has(d) && !bundledExemption.has(d),
)
if (missing.length > 0) {
return {
ok: false,
errors: [
`${bundleName}: Dependencies missing from externals: ${missing.join(', ')}`,
],
}
}
return { ok: true, errors: [] }
}
/**
* The minimal-install contract for INTENTIONALLY_BUNDLED packages:
* - every entry must be a devDependency (available to build, not shipped),
* - none may be a runtime `dependency` (they are inlined; shipping them would
* install them for every user), and
* - only the SDK-externalized subset may be an (optional) peerDependency.
*/
export function validateIntentionallyBundled(
pkg: PkgDeps,
intentionallyBundled: string[],
sdkOnlyExternals: string[],
): ValidationResult {
const directDeps = pkg.dependencies ?? {}
const peerDeps = pkg.peerDependencies ?? {}
const devDeps = pkg.devDependencies ?? {}
const sdkExternalOnly = new Set(sdkOnlyExternals)
const errors: string[] = []
const missingFromDev = intentionallyBundled.filter(dep => !(dep in devDeps))
if (missingFromDev.length > 0) {
errors.push(
`INTENTIONALLY_BUNDLED entries missing from devDependencies: ${missingFromDev.join(', ')}`,
)
}
const shippedAsRuntime = intentionallyBundled.filter(dep => dep in directDeps)
if (shippedAsRuntime.length > 0) {
errors.push(
`INTENTIONALLY_BUNDLED entries must not be in dependencies (they are inlined): ${shippedAsRuntime.join(', ')}`,
)
}
const unexpectedPeers = intentionallyBundled.filter(
dep => dep in peerDeps && !sdkExternalOnly.has(dep),
)
if (unexpectedPeers.length > 0) {
errors.push(
`INTENTIONALLY_BUNDLED entries in peerDependencies that are not SDK externals: ${unexpectedPeers.join(', ')}`,
)
}
// Every SDK-external must STAY a peerDependency: the SDK bundle externalizes
// it, so consumers provide it. If one drops out of peerDependencies it leaves
// runtimeDeps (and other checks stop seeing it) while the SDK still expects it
// resolved at the consumer — a broken SDK publish surface.
const missingPeers = sdkOnlyExternals.filter(dep => !(dep in peerDeps))
if (missingPeers.length > 0) {
errors.push(
`SDK externals must remain peerDependencies (the SDK bundle externalizes them): ${missingPeers.join(', ')}`,
)
}
return { ok: errors.length === 0, errors }
}
/**
* The minimal-install goal depends on every peerDependency being OPTIONAL: a
* non-optional peer makes npm warn (and, on npm 7+, try to install it) for every
* end user. Assert each declared peer is marked `{ optional: true }` in
* peerDependenciesMeta so losing that flag fails the build instead of silently
* regressing the warning-free install.
*/
export function validateOptionalPeers(pkg: PkgDeps): ValidationResult {
const peers = Object.keys(pkg.peerDependencies ?? {})
const meta = pkg.peerDependenciesMeta ?? {}
const notOptional = peers.filter(p => meta[p]?.optional !== true)
if (notOptional.length > 0) {
return {
ok: false,
errors: [
`peerDependencies must be marked optional in peerDependenciesMeta (warning-free install): ${notOptional.join(', ')}`,
],
}
}
return { ok: true, errors: [] }
}
/**
* OPTIONAL_RUNTIME_EXTERNALS are never shipped and never inlined. Anything
* esbuild can see statically must therefore stay external in BOTH bundles;
* dropping one from the externals lists would let esbuild bundle it (a native
* module like sharp) or hoist its transitive imports. The indirection-only
* subset (loaded purely via the runtime importer) is the inverse: it must stay
* OUT of the externals lists, or esbuild would re-introduce its static imports.
*
* Also guards both halves of the install contract: optional packages must never
* be shipped (in dependencies/peerDependencies), and the non-transitive ones
* must be devDependencies so source/dev builds still resolve them.
*/
export function validateOptionalRuntimeExternals(
optionalRuntimeExternals: string[],
cliExternals: string[],
sdkExternals: string[],
indirectionOnly: string[],
pkg: PkgDeps = {},
transitiveExternals: string[] = [],
): ValidationResult {
const cli = new Set(cliExternals)
const sdk = new Set(sdkExternals)
const indirection = new Set(indirectionOnly)
const transitive = new Set(transitiveExternals)
const directDeps = pkg.dependencies ?? {}
const peerDeps = pkg.peerDependencies ?? {}
const devDeps = pkg.devDependencies ?? {}
const errors: string[] = []
// The indirection-only set must be a subset of the optional externals (a
// stray entry would silently exempt something that is not actually optional).
const strayIndirection = indirectionOnly.filter(
p => !optionalRuntimeExternals.includes(p),
)
if (strayIndirection.length > 0) {
errors.push(
`RUNTIME_INDIRECTION_ONLY_EXTERNALS entries not in OPTIONAL_RUNTIME_EXTERNALS: ${strayIndirection.join(', ')}`,
)
}
// Optional runtime externals are loaded on demand and must NEVER be shipped by
// default — listing one in dependencies or peerDependencies installs it for
// every user and breaks the minimal/warning-free install contract.
const shipped = optionalRuntimeExternals.filter(
dep => dep in directDeps || dep in peerDeps,
)
if (shipped.length > 0) {
errors.push(
`OPTIONAL_RUNTIME_EXTERNALS must not be shipped (found in dependencies/peerDependencies): ${shipped.join(', ')}`,
)
}
// Source-install contract: optional packages that source code references
// directly must be devDependencies so `bun install` source/dev builds resolve
// them. The transitive set is exempt (provided by another optional package's
// dependency tree, e.g. @aws-sdk/* via @anthropic-ai/bedrock-sdk).
const missingFromDev = optionalRuntimeExternals.filter(
dep => !transitive.has(dep) && !(dep in devDeps),
)
if (missingFromDev.length > 0) {
errors.push(
`OPTIONAL_RUNTIME_EXTERNALS missing from devDependencies (source builds need them): ${missingFromDev.join(', ')}`,
)
}
for (const dep of optionalRuntimeExternals) {
if (indirection.has(dep)) {
// Must NOT be external (would re-expose its static imports to esbuild).
if (cli.has(dep) || sdk.has(dep)) {
errors.push(
`${dep} is runtime-indirection-only and must NOT be in CLI/SDK externals.`,
)
}
continue
}
// Must stay external in both bundles so it is never inlined.
const missingIn: string[] = []
if (!cli.has(dep)) missingIn.push('CLI_EXTERNALS')
if (!sdk.has(dep)) missingIn.push('SDK_EXTERNALS')
if (missingIn.length > 0) {
errors.push(
`${dep} is an OPTIONAL_RUNTIME_EXTERNAL but missing from ${missingIn.join(' and ')} (it must never be bundled).`,
)
}
}
return { ok: errors.length === 0, errors }
}
+99
View File
@@ -0,0 +1,99 @@
import { describe, expect, test } from 'bun:test'
import { readdirSync, readFileSync, statSync } from 'node:fs'
import { join } from 'node:path'
import {
OPTIONAL_RUNTIME_EXTERNALS,
INTENTIONALLY_BUNDLED,
} from './externals.js'
// Regression coverage for the provider load paths (Bedrock/Foundry/Vertex/Azure/
// AWS): every package routed through importOptionalRuntimeModule MUST be a
// declared optional runtime external. A static scan is the right tool here —
// exercising createClient per provider needs heavy SDK/auth mocking, while the
// thing this PR actually changed is the routing: which packages load on demand
// vs are bundled. A specifier that is NOT optional (e.g. one left in
// INTENTIONALLY_BUNDLED) means esbuild can't see it through the Function
// indirection, so it is neither bundled nor shipped and the feature would break
// for every default install with no install hint.
const SRC = join(import.meta.dirname, '..', 'src')
function walk(dir: string): string[] {
const out: string[] = []
for (const entry of readdirSync(dir)) {
if (entry === 'node_modules' || entry === 'dist') continue
const full = join(dir, entry)
if (statSync(full).isDirectory()) out.push(...walk(full))
else if (/\.tsx?$/.test(entry) && !/\.test\.tsx?$/.test(entry)) out.push(full)
}
return out
}
// Capture the first string argument of every importOptionalRuntimeModule(...)
// call, including test-injectable wrappers. Allow an
// optional generic type argument before the parens —
// `importOptionalRuntimeModule<typeof import('x')>('x', ...)` — and let it span
// lines (the in-between matchers are character classes, which match newlines).
const CALL_RE =
/(?:importOptionalRuntimeModule(?:ForClient)?|optionalRuntimeImporter)(?:<[^>]*>)?\s*\(\s*['"]([^'"]+)['"]/g
function collectSpecifiers(): { specifier: string; file: string }[] {
const found: { specifier: string; file: string }[] = []
for (const file of walk(SRC)) {
const text = readFileSync(file, 'utf8')
for (const m of text.matchAll(CALL_RE)) {
found.push({ specifier: m[1]!, file })
}
}
return found
}
// The concrete packages that must stay behind importOptionalRuntimeModule. Pin
// the exact set (not a count): a count check would still pass if a Bedrock/
// Foundry/Vertex/Azure path regressed while some other optional import kept the
// total up. Adding/removing a provider load site is a deliberate change that
// must update this list.
const EXPECTED_SPECIFIERS = [
'@anthropic-ai/bedrock-sdk',
'@anthropic-ai/foundry-sdk',
'@aws-sdk/client-bedrock',
'@aws-sdk/client-bedrock-runtime',
'@aws-sdk/client-sts',
'@aws-sdk/credential-provider-node',
'@aws-sdk/credential-providers',
'@smithy/core',
'@smithy/node-http-handler',
'@azure/identity',
'google-auth-library',
].sort()
describe('importOptionalRuntimeModule call sites', () => {
const sites = collectSpecifiers()
const optional = new Set(OPTIONAL_RUNTIME_EXTERNALS)
const bundled = new Set(INTENTIONALLY_BUNDLED)
test('the exact set of optionally-loaded packages is the expected one', () => {
const actual = [...new Set(sites.map(s => s.specifier))].sort()
expect(actual).toEqual(EXPECTED_SPECIFIERS)
})
test('every optionally-loaded specifier is a declared OPTIONAL_RUNTIME_EXTERNAL', () => {
const offenders = sites.filter(s => !optional.has(s.specifier))
expect(
offenders,
`Loaded via importOptionalRuntimeModule but not in OPTIONAL_RUNTIME_EXTERNALS ` +
`(scripts/externals.ts): ${offenders.map(o => o.specifier).join(', ')}`,
).toEqual([])
})
test('an optionally-loaded specifier is never also marked INTENTIONALLY_BUNDLED', () => {
// Mutually exclusive: a package the importer resolves from node_modules
// cannot also be inlined into the bundle.
const conflicting = sites.filter(s => bundled.has(s.specifier))
expect(
conflicting,
`Loaded on demand AND marked bundled: ${conflicting.map(o => o.specifier).join(', ')}`,
).toEqual([])
})
})
+58 -48
View File
@@ -5,69 +5,79 @@
* Run as part of the build to catch missing externals early.
*/
import { readFileSync } from 'fs'
import { CLI_EXTERNALS, SDK_EXTERNALS, INTENTIONALLY_BUNDLED, OPTIONAL_RUNTIME_EXTERNALS } from './externals.js'
import { CLI_EXTERNALS, SDK_EXTERNALS, SDK_ONLY_EXTERNALS, INTENTIONALLY_BUNDLED, OPTIONAL_RUNTIME_EXTERNALS, RUNTIME_INDIRECTION_ONLY_EXTERNALS, TRANSITIVE_OPTIONAL_EXTERNALS } from './externals.js'
import {
bundledExemptionFor,
validateBundleExternals,
validateIntentionallyBundled,
validateOptionalPeers,
validateOptionalRuntimeExternals,
} from './externalsValidation.js'
const pkg = JSON.parse(readFileSync('package.json', 'utf8'))
const allDeps = new Set([
// Runtime deps: shipped to users and resolved from node_modules at runtime.
// These must each be a genuine external (the bundle inlines everything else).
const runtimeDeps = new Set<string>([
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {}),
])
const peerDepNames = new Set(Object.keys(pkg.peerDependencies ?? {}))
function validate(bundleName: string, externals: string[]): boolean {
const externalSet = new Set(externals)
const intentionallyBundledSet = new Set(INTENTIONALLY_BUNDLED)
// The bundled allowlist is scoped PER bundle. The CLI inlines every
// INTENTIONALLY_BUNDLED package. In the SDK the optional peers (react,
// @anthropic-ai/sdk, ...) are EXTERNAL — keyed on package.json's
// peerDependencies (an independent source of truth) so dropping one of them
// from SDK_EXTERNALS fails validation instead of silently passing.
const CLI_BUNDLED_EXEMPTION = bundledExemptionFor(INTENTIONALLY_BUNDLED, new Set())
const SDK_BUNDLED_EXEMPTION = bundledExemptionFor(INTENTIONALLY_BUNDLED, peerDepNames)
const missing = [...allDeps].filter(
d => !externalSet.has(d) && !intentionallyBundledSet.has(d),
)
function report(result: { ok: boolean; errors: string[] }): boolean {
for (const err of result.errors) console.error(`${err}`)
return result.ok
}
if (missing.length > 0) {
console.error(`${bundleName}: Dependencies missing from externals:`)
for (const dep of missing) {
console.error(` - ${dep}`)
}
console.error(
`\n Either add them to scripts/externals.ts or to INTENTIONALLY_BUNDLED.`,
)
return false
}
const cliOk = report(
validateBundleExternals('CLI bundle', runtimeDeps, CLI_EXTERNALS, CLI_BUNDLED_EXEMPTION),
)
const sdkOk = report(
validateBundleExternals('SDK bundle', runtimeDeps, SDK_EXTERNALS, SDK_BUNDLED_EXEMPTION),
)
const intentionallyBundledOk = report(
validateIntentionallyBundled(pkg, INTENTIONALLY_BUNDLED, SDK_ONLY_EXTERNALS),
)
const optionalPeersOk = report(validateOptionalPeers(pkg))
const optionalExternalsOk = report(
validateOptionalRuntimeExternals(
OPTIONAL_RUNTIME_EXTERNALS,
CLI_EXTERNALS,
SDK_EXTERNALS,
RUNTIME_INDIRECTION_ONLY_EXTERNALS,
pkg,
TRANSITIVE_OPTIONAL_EXTERNALS,
),
)
// Surface external entries not declared in package.json (informational only).
for (const [name, externals] of [
['CLI bundle', CLI_EXTERNALS],
['SDK bundle', SDK_EXTERNALS],
] as const) {
const optionalSet = new Set(OPTIONAL_RUNTIME_EXTERNALS)
const extra = [...externalSet].filter(d => !allDeps.has(d) && !optionalSet.has(d))
const extra = externals.filter(d => !runtimeDeps.has(d) && !optionalSet.has(d))
if (extra.length > 0) {
console.warn(`⚠️ ${bundleName}: External entries not in package.json (may be ok):`)
for (const dep of extra) {
console.warn(` - ${dep}`)
}
console.warn(`⚠️ ${name}: External entries not in package.json (may be ok): ${extra.join(', ')}`)
}
console.log(`${bundleName}: All dependencies accounted for (${missing.length} missing, ${externalSet.size} external)`)
return true
}
function validateIntentionallyBundled(): boolean {
const stale = INTENTIONALLY_BUNDLED.filter(dep => !allDeps.has(dep))
const allOk =
cliOk && sdkOk && intentionallyBundledOk && optionalPeersOk && optionalExternalsOk
if (stale.length > 0) {
console.error(`❌ INTENTIONALLY_BUNDLED entries not in package.json:`)
for (const dep of stale) {
console.error(` - ${dep}`)
}
console.error(
`\n Remove stale entries from INTENTIONALLY_BUNDLED or add the package back to dependencies.`,
)
return false
}
console.log(`✓ INTENTIONALLY_BUNDLED: All entries still exist in package.json (${INTENTIONALLY_BUNDLED.length} entries)`)
return true
}
const cliOk = validate('CLI bundle', CLI_EXTERNALS)
const sdkOk = validate('SDK bundle', SDK_EXTERNALS)
const intentionallyBundledOk = validateIntentionallyBundled()
if (!cliOk || !sdkOk || !intentionallyBundledOk) {
if (allOk) {
console.log(
`✓ CLI/SDK externals + ${INTENTIONALLY_BUNDLED.length} bundled packages valid (devDependencies-only; SDK peers external & optional; optional externals never bundled).`,
)
} else {
console.error(`\n❌ External list validation failed. Fix scripts/externals.ts before committing.`)
process.exit(1)
}
@@ -0,0 +1,148 @@
import { afterEach, beforeEach, expect, mock, test } from 'bun:test'
import {
acquireSharedMutationLock,
releaseSharedMutationLock,
} from '../../test/sharedMutationLock.js'
import * as optionalRuntimeModule from '../../utils/optionalRuntimeModule.js'
const originalEnv = { ...process.env }
type OptionalImport = typeof optionalRuntimeModule.importOptionalRuntimeModule
function friendlyMissing(specifier: string, feature: string): Error {
return new Error(
`${feature} requires the "${specifier}" package, which is not installed. ` +
`Install it with \`npm install ${specifier}\` (add \`-g\` if you installed the CLI globally) to enable it.`,
)
}
async function importFreshClient(importOptionalRuntimeModule: OptionalImport) {
const client = await import(
`./client.js?optional-runtime=${Date.now()}-${Math.random()}`
)
client._setOptionalRuntimeModuleImporterForTesting(importOptionalRuntimeModule)
return client
}
beforeEach(async () => {
await acquireSharedMutationLock('services/api/client.optionalRuntime.test.ts')
process.env = { ...originalEnv }
;(globalThis as Record<string, unknown>).MACRO = { VERSION: 'test-version' }
delete process.env.CLAUDE_CODE_USE_OPENAI
delete process.env.CLAUDE_CODE_USE_GEMINI
delete process.env.CLAUDE_CODE_USE_GITHUB
delete process.env.CLAUDE_CODE_USE_MISTRAL
delete process.env.CLAUDE_CODE_USE_BEDROCK
delete process.env.CLAUDE_CODE_SKIP_BEDROCK_AUTH
delete process.env.CLAUDE_CODE_USE_FOUNDRY
delete process.env.CLAUDE_CODE_SKIP_FOUNDRY_AUTH
delete process.env.CLAUDE_CODE_USE_VERTEX
delete process.env.CLAUDE_CODE_SKIP_VERTEX_AUTH
delete process.env.ANTHROPIC_API_KEY
delete process.env.ANTHROPIC_AUTH_TOKEN
delete process.env.ANTHROPIC_FOUNDRY_API_KEY
})
afterEach(() => {
try {
process.env = { ...originalEnv }
delete (globalThis as Record<string, unknown>).MACRO
mock.restore()
} finally {
releaseSharedMutationLock()
}
})
test('Bedrock reports the missing provider SDK through the optional runtime helper', async () => {
process.env.CLAUDE_CODE_USE_BEDROCK = '1'
process.env.CLAUDE_CODE_SKIP_BEDROCK_AUTH = '1'
const importOptionalRuntimeModule = mock(async (specifier: string, feature: string) => {
throw friendlyMissing(specifier, feature)
}) as unknown as OptionalImport
const { getAnthropicClient } = await importFreshClient(importOptionalRuntimeModule)
await expect(
getAnthropicClient({ maxRetries: 0, model: 'claude-sonnet-4-6' }),
).rejects.toThrow(/AWS Bedrock requires the "@anthropic-ai\/bedrock-sdk" package/)
expect(importOptionalRuntimeModule).toHaveBeenCalledWith(
'@anthropic-ai/bedrock-sdk',
'AWS Bedrock',
)
})
test('Foundry skip-auth does not load Azure identity', async () => {
process.env.CLAUDE_CODE_USE_FOUNDRY = '1'
process.env.CLAUDE_CODE_SKIP_FOUNDRY_AUTH = '1'
const importOptionalRuntimeModule = mock(async (specifier: string, feature: string) => {
if (specifier === '@azure/identity') {
throw friendlyMissing(specifier, feature)
}
if (specifier === '@anthropic-ai/foundry-sdk') {
return {
AnthropicFoundry: class AnthropicFoundry {
constructor(readonly args: unknown) {}
},
}
}
throw new Error(`unexpected optional import: ${specifier}`)
}) as unknown as OptionalImport
const { getAnthropicClient } = await importFreshClient(importOptionalRuntimeModule)
await getAnthropicClient({ maxRetries: 0, model: 'claude-sonnet-4-6' })
expect(importOptionalRuntimeModule).toHaveBeenCalledWith(
'@anthropic-ai/foundry-sdk',
'Azure Foundry',
)
expect(importOptionalRuntimeModule).not.toHaveBeenCalledWith(
'@azure/identity',
'Azure Foundry authentication',
)
})
test('Foundry real-auth branch reports missing Azure identity through the optional runtime helper', async () => {
process.env.CLAUDE_CODE_USE_FOUNDRY = '1'
const importOptionalRuntimeModule = mock(async (specifier: string, feature: string) => {
if (specifier === '@anthropic-ai/foundry-sdk') {
return {
AnthropicFoundry: class AnthropicFoundry {
constructor(readonly args: unknown) {}
},
}
}
throw friendlyMissing(specifier, feature)
}) as unknown as OptionalImport
const { getAnthropicClient } = await importFreshClient(importOptionalRuntimeModule)
await expect(
getAnthropicClient({ maxRetries: 0, model: 'claude-sonnet-4-6' }),
).rejects.toThrow(/Azure Foundry authentication requires the "@azure\/identity" package/)
expect(importOptionalRuntimeModule).toHaveBeenCalledWith(
'@azure/identity',
'Azure Foundry authentication',
)
})
test('Vertex skip-auth branch does not load google-auth-library', async () => {
process.env.CLAUDE_CODE_USE_VERTEX = '1'
process.env.CLAUDE_CODE_SKIP_VERTEX_AUTH = '1'
const importOptionalRuntimeModule = mock(async (specifier: string, feature: string) => {
throw friendlyMissing(specifier, feature)
}) as unknown as OptionalImport
const { getAnthropicClient } = await importFreshClient(importOptionalRuntimeModule)
await getAnthropicClient({ maxRetries: 0, model: 'claude-sonnet-4-6' })
expect(importOptionalRuntimeModule).not.toHaveBeenCalledWith(
'google-auth-library',
'Vertex AI (GCP) authentication',
)
})
+63 -37
View File
@@ -53,11 +53,18 @@ import {
type ProviderOverride,
} from './authRouting.js'
import { AnthropicVertex } from './vertexClient.js'
import { importOptionalRuntimeModule } from '../../utils/optionalRuntimeModule.js'
const importRuntimeModule = new Function(
'specifier',
'return import(specifier)',
) as (specifier: string) => Promise<any>
type OptionalRuntimeImporter = typeof importOptionalRuntimeModule
let importOptionalRuntimeModuleForClient: OptionalRuntimeImporter =
importOptionalRuntimeModule
export function _setOptionalRuntimeModuleImporterForTesting(
importer?: OptionalRuntimeImporter,
): void {
importOptionalRuntimeModuleForClient = importer ?? importOptionalRuntimeModule
}
/**
* Environment variables for different client types:
@@ -550,7 +557,15 @@ export async function getAnthropicClient({
}) as unknown as Anthropic
}
if (isEnvTruthy(process.env.CLAUDE_CODE_USE_BEDROCK)) {
const { AnthropicBedrock } = await import('@anthropic-ai/bedrock-sdk')
// Loaded via the runtime importer (not a static `import()`), so esbuild
// stays blind to it and does NOT inline @anthropic-ai/bedrock-sdk — which
// statically imports @aws-sdk/client-bedrock-runtime. Inlining would hoist
// that AWS import into the CLI bundle and require it at startup for every
// user. Keeping it lazy means only Bedrock users install the SDK (which
// pulls @aws-sdk transitively).
const { AnthropicBedrock } = await importOptionalRuntimeModuleForClient<
typeof import('@anthropic-ai/bedrock-sdk')
>('@anthropic-ai/bedrock-sdk', 'AWS Bedrock')
// Use region override for small fast model if specified
const awsRegion =
model === getSmallFastModel() &&
@@ -594,9 +609,9 @@ export async function getAnthropicClient({
) as unknown as Anthropic
}
if (isEnvTruthy(process.env.CLAUDE_CODE_USE_FOUNDRY)) {
const { AnthropicFoundry } = await importRuntimeModule(
'@anthropic-ai/foundry-sdk',
)
const { AnthropicFoundry } = await importOptionalRuntimeModuleForClient<
typeof import('@anthropic-ai/foundry-sdk')
>('@anthropic-ai/foundry-sdk', 'Azure Foundry')
// Determine Azure AD token provider based on configuration
// SDK reads ANTHROPIC_FOUNDRY_API_KEY by default
let azureADTokenProvider: (() => Promise<string>) | undefined
@@ -609,7 +624,10 @@ export async function getAnthropicClient({
const {
DefaultAzureCredential: AzureCredential,
getBearerTokenProvider,
} = await importRuntimeModule('@azure/identity')
} =
await importOptionalRuntimeModuleForClient<
typeof import('@azure/identity')
>('@azure/identity', 'Azure Foundry authentication')
azureADTokenProvider = getBearerTokenProvider(
new AzureCredential(),
'https://cognitiveservices.azure.com/.default',
@@ -632,7 +650,6 @@ export async function getAnthropicClient({
await refreshGcpCredentialsIfNeeded()
}
const { GoogleAuth } = await importRuntimeModule('google-auth-library')
// TODO: Cache either GoogleAuth instance or AuthClient to improve performance
// Currently we create a new GoogleAuth instance for every getAnthropicClient() call
// This could cause repeated authentication flows and metadata server checks
@@ -667,33 +684,42 @@ export async function getAnthropicClient({
process.env['GOOGLE_APPLICATION_CREDENTIALS'] ||
process.env['google_application_credentials']
const googleAuth = isEnvTruthy(process.env.CLAUDE_CODE_SKIP_VERTEX_AUTH)
? ({
// Mock GoogleAuth for testing/proxy scenarios
getClient: () => ({
getRequestHeaders: () => ({}),
}),
} as {
getClient: () => {
getRequestHeaders: () => Record<string, string>
}
})
: new GoogleAuth({
scopes: ['https://www.googleapis.com/auth/cloud-platform'],
// Only use ANTHROPIC_VERTEX_PROJECT_ID as last resort fallback
// This prevents the 12-second metadata server timeout when:
// - No project env vars are set AND
// - No credential keyfile is specified AND
// - ADC file exists but lacks project_id field
//
// Risk: If auth project != API target project, this could cause billing/audit issues
// Mitigation: Users can set GOOGLE_CLOUD_PROJECT to override
...(hasProjectEnvVar || hasKeyFile
? {}
: {
projectId: process.env.ANTHROPIC_VERTEX_PROJECT_ID,
}),
})
let googleAuth: {
getClient: () => { getRequestHeaders: () => Record<string, string> }
}
if (isEnvTruthy(process.env.CLAUDE_CODE_SKIP_VERTEX_AUTH)) {
// Mock GoogleAuth for testing/proxy scenarios. This path intentionally
// does NOT load google-auth-library — proxy/test runs must work even when
// the optional package is absent (it is only needed for real auth below).
googleAuth = {
getClient: () => ({
getRequestHeaders: () => ({}),
}),
}
} else {
const { GoogleAuth } = await importOptionalRuntimeModuleForClient<
typeof import('google-auth-library')
>('google-auth-library', 'Vertex AI (GCP) authentication')
// The real GoogleAuth (async getClient) is wider than the minimal shape
// declared above and shared with the mock; AnthropicVertex accepts it at
// runtime, so narrow it back to the shared shape here.
googleAuth = new GoogleAuth({
scopes: ['https://www.googleapis.com/auth/cloud-platform'],
// Only use ANTHROPIC_VERTEX_PROJECT_ID as last resort fallback
// This prevents the 12-second metadata server timeout when:
// - No project env vars are set AND
// - No credential keyfile is specified AND
// - ADC file exists but lacks project_id field
//
// Risk: If auth project != API target project, this could cause billing/audit issues
// Mitigation: Users can set GOOGLE_CLOUD_PROJECT to override
...(hasProjectEnvVar || hasKeyFile
? {}
: {
projectId: process.env.ANTHROPIC_VERTEX_PROJECT_ID,
}),
}) as unknown as typeof googleAuth
}
const vertexArgs = {
...ARGS,
+4 -3
View File
@@ -1,4 +1,5 @@
import type { Anthropic } from '@anthropic-ai/sdk'
import { importOptionalRuntimeModule } from '../utils/optionalRuntimeModule.js'
import type { BetaMessageParam as MessageParam } from '@anthropic-ai/sdk/resources/beta/messages/messages.mjs'
// @aws-sdk/client-bedrock-runtime is imported dynamically in countTokensWithBedrock()
// to defer ~279KB of AWS SDK code until a Bedrock call is actually made
@@ -694,9 +695,9 @@ async function countTokensWithBedrock({
}),
}
const { CountTokensCommand } = await import(
'@aws-sdk/client-bedrock-runtime'
)
const { CountTokensCommand } = await importOptionalRuntimeModule<
typeof import('@aws-sdk/client-bedrock-runtime')
>('@aws-sdk/client-bedrock-runtime', 'AWS Bedrock')
const input: CountTokensCommandInput = {
modelId,
input: {
+14 -8
View File
@@ -48,6 +48,10 @@ import {
ImageResizeError,
maybeResizeAndDownsampleImageBuffer,
} from '../../utils/imageResizer.js'
import {
getImageProcessor,
ImageProcessorUnavailableError,
} from './imageProcessor.js'
import { lazySchema } from '../../utils/lazySchema.js'
import { logError } from '../../utils/log.js'
import { isAutoMemFile } from '../../utils/memoryFileDetection.js'
@@ -1261,15 +1265,13 @@ export async function readImageWithTokenBudget(
}
} catch (e) {
logError(e)
// Fallback: heavily compressed version from the SAME buffer
// Fallback: heavily compressed version from the SAME buffer, loaded via
// the shared optional image processor (NOT a raw import('sharp')). This
// keeps the missing-image-processor contract: when no processor is
// installed, surface the actionable install hint instead of swallowing it
// and returning an image that already exceeded the token budget.
try {
const sharpModule = await import('sharp')
// CJS/ESM interop: prefer .default; some loaders expose the callable
// as the module itself, hence the cast on the fallback.
const sharp =
sharpModule.default ||
(sharpModule as unknown as typeof sharpModule.default)
const sharp = await getImageProcessor()
const fallbackBuffer = await sharp(imageBuffer)
.resize(400, 400, {
fit: 'inside',
@@ -1280,6 +1282,10 @@ export async function readImageWithTokenBudget(
return createImageResponse(fallbackBuffer, 'jpeg', originalSize)
} catch (error) {
// No image processor available → surface the install hint rather than
// returning an over-budget image. Other failures (e.g. a corrupt
// buffer) still degrade gracefully to the original.
if (error instanceof ImageProcessorUnavailableError) throw error
logError(error)
return createImageResponse(imageBuffer, detectedFormat, originalSize)
}
+3 -1
View File
@@ -40,7 +40,9 @@ let imageCreatorModule: { default: SharpCreator } | null = null
*/
export class ImageProcessorUnavailableError extends Error {
constructor() {
super('No image processor available (sharp is not installed)')
super(
'Image support is not installed. Install `sharp` (add `-g` if you installed the CLI globally) to enable reading and processing images.',
)
this.name = 'ImageProcessorUnavailableError'
}
}
+82
View File
@@ -0,0 +1,82 @@
import { describe, expect, test } from 'bun:test'
import { tryReadEditedImageAttachment } from './attachments.js'
import { ImageProcessorUnavailableError } from '../tools/FileReadTool/imageProcessor.js'
import type { readImageWithTokenBudget } from '../tools/FileReadTool/FileReadTool.js'
// Pins the chosen contract for background edited-image attachments: they DEGRADE
// (return null) on any read/compress failure instead of throwing, so a missing
// optional image processor — or any other error — never aborts the turn. The
// explicit FileReadTool path is the opposite (it surfaces ImageProcessorUnavailableError
// so the user sees the install hint); see FileReadTool.readImageWithTokenBudget.
const SECRET_PATH = '/Users/jane.doe/secret-project/edited-image.png'
describe('tryReadEditedImageAttachment', () => {
test('degrades to null when the image processor is unavailable', async () => {
const result = await tryReadEditedImageAttachment(SECRET_PATH, {
read: async () => {
throw new ImageProcessorUnavailableError()
},
})
expect(result).toBeNull()
})
test('degrades to null on a path-bearing read error (without rethrowing)', async () => {
const result = await tryReadEditedImageAttachment(SECRET_PATH, {
read: async () => {
throw new Error(`Image file is empty: ${SECRET_PATH}`)
},
})
expect(result).toBeNull()
})
test('degrades to null when the file cannot be read (real ENOENT)', async () => {
const result = await tryReadEditedImageAttachment(
'/nonexistent/path/definitely-not-a-real-image-xyz.png',
)
expect(result).toBeNull()
})
test('telemetry is sanitized: no file path reaches logError or analytics', async () => {
const logged: Error[] = []
const tracked: Array<[string, Record<string, unknown> | undefined]> = []
await tryReadEditedImageAttachment(SECRET_PATH, {
// Error message intentionally carries the path — it must NOT be forwarded.
read: async () => {
throw new Error(`Image file is empty: ${SECRET_PATH}`)
},
log: err => {
logged.push(err as Error)
},
track: (name, meta) => {
tracked.push([name, meta as Record<string, unknown> | undefined])
},
})
// logError received a generic, path-free error (only the error TYPE name).
expect(logged).toHaveLength(1)
expect(logged[0]!.message).not.toContain(SECRET_PATH)
expect(logged[0]!.message).not.toContain('jane.doe')
// Analytics payload carries only `ext`, never the path or the raw error.
expect(tracked).toHaveLength(1)
const [eventName, meta] = tracked[0]!
expect(eventName).toBe('tengu_watched_file_compression_failed')
expect(meta).toEqual({ ext: 'png' })
expect(JSON.stringify(meta)).not.toContain('jane.doe')
})
test('returns the attachment when the read succeeds', async () => {
const fake = {
file: { base64: 'AAAA', type: 'image/png' },
} as unknown as Awaited<ReturnType<typeof readImageWithTokenBudget>>
const result = await tryReadEditedImageAttachment(SECRET_PATH, {
read: async () => fake,
})
expect(result).toEqual({
type: 'edited_image_file',
filename: SECRET_PATH,
content: fake,
})
})
})
+52 -15
View File
@@ -3,6 +3,7 @@ import {
logEvent,
type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
} from 'src/services/analytics/index.js'
import { getFileExtensionForAnalytics } from 'src/services/analytics/metadata.js'
import {
toolMatchesName,
type Tools,
@@ -2089,6 +2090,54 @@ async function processMcpResourceAttachments(
) as Attachment[]
}
// Read a changed/watched image file for a background diff attachment.
//
// Contract: this path DEGRADES on any failure — returning null skips the
// attachment rather than interrupting the turn. That deliberately includes
// ImageProcessorUnavailableError (no image processor installed): a background
// attachment must never abort the conversation over a missing optional package.
// The explicit FileReadTool path is the opposite — it lets that error surface so
// the user sees the install hint when they directly read an image.
export async function tryReadEditedImageAttachment(
normalizedPath: string,
// Injectable for tests so the degrade path (and its sanitized telemetry) can
// be exercised for a specific error type without a real file.
deps: {
read?: typeof readImageWithTokenBudget
log?: typeof logError
track?: typeof logEvent
} = {},
): Promise<{
type: 'edited_image_file'
filename: string
content: Awaited<ReturnType<typeof readImageWithTokenBudget>>
} | null> {
const read = deps.read ?? readImageWithTokenBudget
const log = deps.log ?? logError
const track = deps.track ?? logEvent
try {
const data = await read(normalizedPath)
return {
type: 'edited_image_file' as const,
filename: normalizedPath,
content: data,
}
} catch (compressionError) {
// Log only the error TYPE, never the raw error: readImageWithTokenBudget can
// throw path-bearing messages/stacks (e.g. "Image file is empty: <path>"),
// and logError persists message/stack, which would leak local file paths.
const errorName =
compressionError instanceof Error ? compressionError.name : 'UnknownError'
log(new Error(`watched-file image attachment skipped (${errorName})`))
// Likewise only the file extension goes to analytics — never the path.
const analyticsExt = getFileExtensionForAnalytics(normalizedPath)
track('tengu_watched_file_compression_failed', {
...(analyticsExt !== undefined && { ext: analyticsExt }),
})
return null
}
}
export async function getChangedFiles(
toolUseContext: ToolUseContext,
): Promise<Attachment[]> {
@@ -2150,22 +2199,10 @@ export async function getChangedFiles(
}
}
// For non-text files (images), apply the same token limit logic as FileReadTool
// For non-text files (images), apply the same token limit logic as
// FileReadTool. Degrades to null on failure (see the helper's contract).
if (result.data.type === 'image') {
try {
const data = await readImageWithTokenBudget(normalizedPath)
return {
type: 'edited_image_file' as const,
filename: normalizedPath,
content: data,
}
} catch (compressionError) {
logError(compressionError)
logEvent('tengu_watched_file_compression_failed', {
file: normalizedPath,
} as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS)
return null
}
return tryReadEditedImageAttachment(normalizedPath)
}
// notebook / pdf / parts — no diff representation; explicitly
+6 -2
View File
@@ -40,6 +40,7 @@ import {
isValidAwsStsOutput,
} from './aws.js'
import { AwsAuthStatusManager } from './awsAuthStatusManager.js'
import { importOptionalRuntimeModule } from './optionalRuntimeModule.js'
import { clearBetasCaches } from './betas.js'
import {
type AccountInfo,
@@ -867,8 +868,11 @@ const GCP_CREDENTIALS_CHECK_TIMEOUT_MS = 5_000
*/
export async function checkGcpCredentialsValid(): Promise<boolean> {
try {
// Dynamically import to avoid loading google-auth-library unnecessarily
const { GoogleAuth } = await import('google-auth-library')
// Dynamically import to avoid loading google-auth-library unnecessarily.
// It is an optional, on-demand dependency (not shipped by default).
const { GoogleAuth } = await importOptionalRuntimeModule<
typeof import('google-auth-library')
>('google-auth-library', 'Vertex AI (GCP) authentication')
const auth = new GoogleAuth({
scopes: ['https://www.googleapis.com/auth/cloud-platform'],
})
+9 -4
View File
@@ -1,4 +1,5 @@
import { logForDebugging } from './debug.js'
import { importOptionalRuntimeModule } from './optionalRuntimeModule.js'
/** AWS short-term credentials format. */
export type AwsCredentials = {
@@ -48,9 +49,11 @@ export function isValidAwsStsOutput(obj: unknown): obj is AwsStsOutput {
/** Throws if STS caller identity cannot be retrieved. */
export async function checkStsCallerIdentity(): Promise<void> {
const { STSClient, GetCallerIdentityCommand } = await import(
'@aws-sdk/client-sts'
)
const { STSClient, GetCallerIdentityCommand } =
await importOptionalRuntimeModule<typeof import('@aws-sdk/client-sts')>(
'@aws-sdk/client-sts',
'AWS credentials',
)
await new STSClient().send(new GetCallerIdentityCommand({}))
}
@@ -61,7 +64,9 @@ export async function checkStsCallerIdentity(): Promise<void> {
export async function clearAwsIniCache(): Promise<void> {
try {
logForDebugging('Clearing AWS credential provider cache')
const { fromIni } = await import('@aws-sdk/credential-providers')
const { fromIni } = await importOptionalRuntimeModule<
typeof import('@aws-sdk/credential-providers')
>('@aws-sdk/credential-providers', 'AWS credentials')
const iniProvider = fromIni({ ignoreCache: true })
await iniProvider() // This updates the global file cache
logForDebugging('AWS credential provider cache refreshed')
@@ -0,0 +1,61 @@
import { afterEach, beforeEach, expect, mock, test } from 'bun:test'
import {
acquireSharedMutationLock,
releaseSharedMutationLock,
} from '../test/sharedMutationLock.js'
import { OptionalRuntimeModuleUnavailableError } from './optionalRuntimeModule.js'
const originalEnv = { ...process.env }
beforeEach(async () => {
await acquireSharedMutationLock('utils/geminiAuth.optionalRuntime.test.ts')
process.env = { ...originalEnv }
delete process.env.GEMINI_API_KEY
delete process.env.GOOGLE_API_KEY
delete process.env.GEMINI_ACCESS_TOKEN
process.env.GEMINI_AUTH_MODE = 'adc'
process.env.GOOGLE_APPLICATION_CREDENTIALS = import.meta.path
})
afterEach(() => {
try {
process.env = { ...originalEnv }
mock.restore()
} finally {
releaseSharedMutationLock()
}
})
test('Gemini ADC reports missing google-auth-library through the optional runtime helper', async () => {
const importOptionalRuntimeModule = mock(async (specifier: string, feature: string) => {
throw new OptionalRuntimeModuleUnavailableError(feature, specifier)
})
const { resolveGeminiCredential } = await import(
`./geminiAuth.ts?optional-runtime=${Date.now()}-${Math.random()}`
)
await expect(
resolveGeminiCredential(process.env, { importOptionalRuntimeModule }),
).rejects.toThrow(/Gemini Application Default Credentials requires the "google-auth-library" package/)
expect(importOptionalRuntimeModule).toHaveBeenCalledWith(
'google-auth-library',
'Gemini Application Default Credentials',
)
})
test('Gemini ADC still degrades to none for credential lookup failures', async () => {
const { resolveGeminiCredential } = await import(
`./geminiAuth.ts?credential-failure=${Date.now()}-${Math.random()}`
)
await expect(
resolveGeminiCredential(process.env, {
createGoogleAuth: async () => ({
getClient: async () => {
throw new Error('ADC token unavailable')
},
}),
}),
).resolves.toEqual({ kind: 'none' })
})
+21 -5
View File
@@ -3,6 +3,10 @@ import { homedir } from 'node:os'
import { join } from 'node:path'
import { memoizeWithTTLAsync } from './memoize.js'
import {
importOptionalRuntimeModule,
isOptionalRuntimeModuleUnavailableError,
} from './optionalRuntimeModule.js'
const GEMINI_ADC_SCOPE = 'https://www.googleapis.com/auth/cloud-platform'
const GEMINI_ADC_CACHE_TTL_MS = 5 * 60 * 1000
@@ -42,6 +46,7 @@ export type GeminiResolvedCredential =
type ResolveGeminiCredentialDeps = {
createGoogleAuth?: () => Promise<GoogleAuthLike>
importOptionalRuntimeModule?: typeof importOptionalRuntimeModule
}
function sanitizeCredential(value: string | undefined | null): string | undefined {
@@ -132,8 +137,13 @@ function normalizeAccessToken(
return sanitizeCredential(value?.token)
}
async function createDefaultGoogleAuth(): Promise<GoogleAuthLike> {
const { GoogleAuth } = await import('google-auth-library')
async function createDefaultGoogleAuth(
optionalRuntimeImporter: typeof importOptionalRuntimeModule =
importOptionalRuntimeModule,
): Promise<GoogleAuthLike> {
const { GoogleAuth } = await optionalRuntimeImporter<
typeof import('google-auth-library')
>('google-auth-library', 'Gemini Application Default Credentials')
return new GoogleAuth({
scopes: [GEMINI_ADC_SCOPE],
}) as GoogleAuthLike
@@ -148,7 +158,10 @@ async function resolveGeminiAdcCredentialUncached(
}
try {
const auth = await (deps.createGoogleAuth ?? createDefaultGoogleAuth)()
const createGoogleAuth =
deps.createGoogleAuth ??
(() => createDefaultGoogleAuth(deps.importOptionalRuntimeModule))
const auth = await createGoogleAuth()
const client = await auth.getClient()
const accessToken = normalizeAccessToken(await client.getAccessToken())
if (!accessToken) {
@@ -167,7 +180,10 @@ async function resolveGeminiAdcCredentialUncached(
credential: accessToken,
...(resolvedProjectId ? { projectId: resolvedProjectId } : {}),
}
} catch {
} catch (error) {
if (isOptionalRuntimeModuleUnavailableError(error)) {
throw error
}
return { kind: 'none' }
}
}
@@ -227,7 +243,7 @@ export async function resolveGeminiCredential(
return { kind: 'none' }
}
if (deps.createGoogleAuth) {
if (deps.createGoogleAuth || deps.importOptionalRuntimeModule) {
return resolveGeminiAdcCredentialUncached(env, deps)
}
+42 -34
View File
@@ -1,4 +1,5 @@
import memoize from 'lodash-es/memoize.js'
import { importOptionalRuntimeModule } from '../optionalRuntimeModule.js'
import { refreshAndGetAwsCredentials } from '../auth.js'
import { getAWSRegion, isEnvTruthy } from '../envUtils.js'
import { logError } from '../log.js'
@@ -9,7 +10,10 @@ export const getBedrockInferenceProfiles = memoize(async function (): Promise<
> {
const [client, { ListInferenceProfilesCommand }] = await Promise.all([
createBedrockClient(),
import('@aws-sdk/client-bedrock'),
importOptionalRuntimeModule<typeof import('@aws-sdk/client-bedrock')>(
'@aws-sdk/client-bedrock',
'AWS Bedrock',
),
])
const allProfiles: Array<{ inferenceProfileId?: string }> = []
let nextToken: string | undefined
@@ -47,8 +51,35 @@ export function findFirstMatch(
return profiles.find(p => p.includes(substring)) ?? null
}
async function getBedrockNoAuthConfig() {
const [{ NodeHttpHandler }, { NoAuthSigner }] = await Promise.all([
importOptionalRuntimeModule<typeof import('@smithy/node-http-handler')>(
'@smithy/node-http-handler',
'AWS Bedrock no-auth support',
),
importOptionalRuntimeModule<typeof import('@smithy/core')>(
'@smithy/core',
'AWS Bedrock no-auth support',
),
])
return {
requestHandler: new NodeHttpHandler(),
httpAuthSchemes: [
{
schemeId: 'smithy.api#noAuth',
identityProvider: () => async () => ({}),
signer: new NoAuthSigner(),
},
],
httpAuthSchemeProvider: () => [{ schemeId: 'smithy.api#noAuth' }],
}
}
async function createBedrockClient() {
const { BedrockClient } = await import('@aws-sdk/client-bedrock')
const { BedrockClient } = await importOptionalRuntimeModule<
typeof import('@aws-sdk/client-bedrock')
>('@aws-sdk/client-bedrock', 'AWS Bedrock')
// Match the Anthropic Bedrock SDK's region behavior exactly:
// - Reads AWS_REGION or AWS_DEFAULT_REGION env vars (not AWS config files)
// - Falls back to 'us-east-1' if neither is set
@@ -63,19 +94,7 @@ async function createBedrockClient() {
endpoint: process.env.ANTHROPIC_BEDROCK_BASE_URL,
}),
...(await getAWSClientProxyConfig()),
...(skipAuth && {
requestHandler: new (
await import('@smithy/node-http-handler')
).NodeHttpHandler(),
httpAuthSchemes: [
{
schemeId: 'smithy.api#noAuth',
identityProvider: () => async () => ({}),
signer: new (await import('@smithy/core')).NoAuthSigner(),
},
],
httpAuthSchemeProvider: () => [{ schemeId: 'smithy.api#noAuth' }],
}),
...(skipAuth ? await getBedrockNoAuthConfig() : {}),
}
if (!skipAuth && !process.env.AWS_BEARER_TOKEN_BEDROCK) {
@@ -94,9 +113,9 @@ async function createBedrockClient() {
}
export async function createBedrockRuntimeClient() {
const { BedrockRuntimeClient } = await import(
'@aws-sdk/client-bedrock-runtime'
)
const { BedrockRuntimeClient } = await importOptionalRuntimeModule<
typeof import('@aws-sdk/client-bedrock-runtime')
>('@aws-sdk/client-bedrock-runtime', 'AWS Bedrock')
const region = getAWSRegion()
const skipAuth = isEnvTruthy(process.env.CLAUDE_CODE_SKIP_BEDROCK_AUTH)
@@ -106,21 +125,7 @@ export async function createBedrockRuntimeClient() {
endpoint: process.env.ANTHROPIC_BEDROCK_BASE_URL,
}),
...(await getAWSClientProxyConfig()),
...(skipAuth && {
// BedrockRuntimeClient defaults to HTTP/2 without fallback
// proxy servers may not support this, so we explicitly force HTTP/1.1
requestHandler: new (
await import('@smithy/node-http-handler')
).NodeHttpHandler(),
httpAuthSchemes: [
{
schemeId: 'smithy.api#noAuth',
identityProvider: () => async () => ({}),
signer: new (await import('@smithy/core')).NoAuthSigner(),
},
],
httpAuthSchemeProvider: () => [{ schemeId: 'smithy.api#noAuth' }],
}),
...(skipAuth ? await getBedrockNoAuthConfig() : {}),
}
if (!skipAuth && !process.env.AWS_BEARER_TOKEN_BEDROCK) {
@@ -144,7 +149,10 @@ export const getInferenceProfileBackingModel = memoize(async function (
try {
const [client, { GetInferenceProfileCommand }] = await Promise.all([
createBedrockClient(),
import('@aws-sdk/client-bedrock'),
importOptionalRuntimeModule<typeof import('@aws-sdk/client-bedrock')>(
'@aws-sdk/client-bedrock',
'AWS Bedrock',
),
])
const command = new GetInferenceProfileCommand({
inferenceProfileIdentifier: profileId,
+99
View File
@@ -0,0 +1,99 @@
import { describe, expect, test } from 'bun:test'
import {
importOptionalRuntimeModule,
importRuntimeModule,
isMissingSpecifierError,
} from './optionalRuntimeModule.js'
function moduleNotFound(message: string): Error {
const e = new Error(message)
;(e as { code?: string }).code = 'ERR_MODULE_NOT_FOUND'
return e
}
describe('importOptionalRuntimeModule', () => {
test('throws an actionable error when the optional package is missing', async () => {
const promise = importOptionalRuntimeModule(
'@openclaude/does-not-exist-xyz',
'Test Feature',
)
await expect(promise).rejects.toThrow(
/Test Feature requires the "@openclaude\/does-not-exist-xyz" package, which is not installed\. Install it with `npm install @openclaude\/does-not-exist-xyz`/,
)
})
test('resolves the module when it is present', async () => {
// node: builtins are always resolvable — exercises the success path. The
// type arg mirrors how production call sites declare the module contract.
const mod = await importOptionalRuntimeModule<typeof import('node:path')>(
'node:path',
'Test Feature',
)
const join = mod.join ?? (mod as { default?: typeof import('node:path') }).default?.join
expect(typeof join).toBe('function')
})
test('does not mask a missing package as the wrong feature specifier', async () => {
// The friendly error must name the specifier we asked for.
try {
await importOptionalRuntimeModule('totally-absent-pkg-123', 'Vertex AI')
throw new Error('expected rejection')
} catch (e) {
expect((e as Error).message).toContain('totally-absent-pkg-123')
expect((e as Error).message).toContain('Vertex AI')
}
})
})
describe('isMissingSpecifierError', () => {
test('matches the exact missing package, quoted', () => {
expect(
isMissingSpecifierError(
moduleNotFound("Cannot find package 'sharp' imported from /x/y.js"),
'sharp',
),
).toBe(true)
expect(
isMissingSpecifierError(
moduleNotFound('Cannot find module "sharp" imported from /x/y.js'),
'sharp',
),
).toBe(true)
})
test('does not misattribute a lookalike transitive package', () => {
// A missing transitive dep whose name CONTAINS the specifier must not be
// reported as the specifier itself.
expect(
isMissingSpecifierError(
moduleNotFound("Cannot find package 'sharp-libvips-dev' imported from /x"),
'sharp',
),
).toBe(false)
expect(
isMissingSpecifierError(
moduleNotFound(
"Cannot find package '@aws-sdk/client-bedrock-runtime' imported from /x",
),
'@aws-sdk/client-bedrock',
),
).toBe(false)
})
test('ignores errors that are not ERR_MODULE_NOT_FOUND', () => {
const other = new Error("Cannot find package 'sharp'")
;(other as { code?: string }).code = 'ERR_SOMETHING_ELSE'
expect(isMissingSpecifierError(other, 'sharp')).toBe(false)
expect(isMissingSpecifierError(undefined, 'sharp')).toBe(false)
})
})
describe('importRuntimeModule', () => {
test('rejects with the raw error for a missing module (no friendly wrapping)', async () => {
const promise = importRuntimeModule('@openclaude/does-not-exist-xyz')
// Raw error wording differs by runtime (Node: "Cannot find package",
// Bun: "Cannot find module") — the point is it is NOT the friendly message.
await expect(promise).rejects.toThrow(/Cannot find (module|package)/)
await expect(promise).rejects.not.toThrow(/npm install/)
})
})
+91
View File
@@ -0,0 +1,91 @@
/**
* Runtime loading for optional, on-demand dependencies.
*
* Some provider SDKs and native helpers are NOT shipped in the default install
* (see OPTIONAL_RUNTIME_EXTERNALS in scripts/externals.ts) — they are loaded
* only when a provider/feature that needs them is actually used. We import them
* through a `new Function` indirection so esbuild cannot see the specifier:
* that keeps the package out of the CLI bundle AND prevents esbuild from
* hoisting the package's own static imports (e.g. @anthropic-ai/bedrock-sdk's
* static `@aws-sdk/client-bedrock-runtime` import) into the bundle, which would
* otherwise make those packages required at startup for every user.
*/
// Hidden from esbuild's static analysis: resolved from node_modules at runtime.
const runtimeImport = new Function(
'specifier',
'return import(specifier)',
) as (specifier: string) => Promise<any>
export class OptionalRuntimeModuleUnavailableError extends Error {
constructor(
readonly feature: string,
readonly specifier: string,
) {
super(
`${feature} requires the "${specifier}" package, which is not installed. ` +
`Install it with \`npm install ${specifier}\` (add \`-g\` if you installed the CLI globally) to enable it.`,
)
this.name = 'OptionalRuntimeModuleUnavailableError'
}
}
export function isOptionalRuntimeModuleUnavailableError(
error: unknown,
): error is OptionalRuntimeModuleUnavailableError {
return error instanceof OptionalRuntimeModuleUnavailableError
}
/** Raw runtime import — rejects with the underlying error if the module is missing. */
export function importRuntimeModule(specifier: string): Promise<any> {
return runtimeImport(specifier)
}
/**
* True only when `error` is the resolver reporting that THIS specifier (not a
* lookalike transitive package) could not be found. Node reports the unresolved
* name quoted — `Cannot find package 'sharp' imported from ...` — so we match
* the quoted token rather than a raw substring. A bare includes() would
* misattribute a missing transitive package whose name merely contains ours
* (`sharp` ⊂ `sharp-libvips`, `@aws-sdk/client-bedrock` ⊂
* `@aws-sdk/client-bedrock-runtime`) and print an install hint for the wrong
* package instead of surfacing the real failure.
*/
export function isMissingSpecifierError(
error: unknown,
specifier: string,
): boolean {
const code = (error as { code?: string } | undefined)?.code
if (code !== 'ERR_MODULE_NOT_FOUND') return false
const message = error instanceof Error ? error.message : String(error)
return (
message.includes(`'${specifier}'`) || message.includes(`"${specifier}"`)
)
}
/**
* Import an optional runtime dependency. If the package itself is not installed,
* throw a clear, actionable error naming the feature and the install command
* instead of a cryptic ERR_MODULE_NOT_FOUND.
*
* The guard checks the error `code` AND that the failing specifier is the
* package we asked for — so a genuine missing-package error is reported with
* the install hint, while a broken transitive dependency inside an installed
* package surfaces its real error rather than a misleading "not installed".
*/
export async function importOptionalRuntimeModule<T = unknown>(
specifier: string,
feature: string,
): Promise<T> {
try {
return (await runtimeImport(specifier)) as T
} catch (e) {
if (isMissingSpecifierError(e, specifier)) {
// Context-neutral install hint: this helper backs both the globally
// installed CLI and the project-local ./sdk consumers, so don't prescribe
// `-g` (which is wrong for a local SDK install).
throw new OptionalRuntimeModuleUnavailableError(feature, specifier)
}
throw e
}
}
+9 -2
View File
@@ -17,6 +17,7 @@ import {
getTLSFetchOptions,
type TLSConfig,
} from './mtls.js'
import { importOptionalRuntimeModule } from './optionalRuntimeModule.js'
// Disable fetch keep-alive after a stale-pool ECONNRESET so retries open a
// fresh TCP connection instead of reusing the dead pooled socket. Sticky for
@@ -401,8 +402,14 @@ export async function getAWSClientProxyConfig(): Promise<object> {
}
const [{ NodeHttpHandler }, { defaultProvider }] = await Promise.all([
import('@smithy/node-http-handler'),
import('@aws-sdk/credential-provider-node'),
importOptionalRuntimeModule<typeof import('@smithy/node-http-handler')>(
'@smithy/node-http-handler',
'AWS proxy support',
),
importOptionalRuntimeModule<typeof import('@aws-sdk/credential-provider-node')>(
'@aws-sdk/credential-provider-node',
'AWS proxy support',
),
])
const agent = createHttpsProxyAgent(proxyUrl)