mirror of
https://github.com/Gitlawb/openclaude.git
synced 2026-08-24 02:34:15 -05:00
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:
@@ -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
@@ -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 |
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
],
|
||||
"ignoreDependencies": [
|
||||
"@types/bun",
|
||||
"@anthropic-ai/foundry-sdk"
|
||||
"@anthropic-ai/foundry-sdk",
|
||||
"google-auth-library"
|
||||
]
|
||||
}
|
||||
|
||||
+27
-9
@@ -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
@@ -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',
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
@@ -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 }
|
||||
}
|
||||
@@ -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([])
|
||||
})
|
||||
})
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
@@ -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/)
|
||||
})
|
||||
})
|
||||
@@ -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
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user