chore(deps): upgrade @sentry/node to 11.9.0 (M-14) (#2402)

* chore(deps): upgrade @sentry/node to 11.9.0 (M-14)

* fix(sentry): omit hostname from sanitized error reports

* test(sentry): cover nonessential-traffic opt-out on its own

* fix(sentry): bound Sentry flush before shutdown exit

* fix(sentry): leave tracesSampleRate unset so spans stay disabled

* fix(shutdown): include Sentry drain in failsafe budget and test pending-flush exits
This commit is contained in:
JATMN
2026-10-24 14:52:22 -07:00
committed by GitHub
parent 1d6337dd2a
commit ef78478fc0
9 changed files with 420 additions and 95 deletions
+9
View File
@@ -288,6 +288,15 @@ new watcher for subsequent subscriptions because closing is terminal. Its
Node.js 22.22 minimum is covered by the supported Node.js `>=24.22.1` runtime.
Regenerate `bun.lock` with `bun install` after updating the exact pin.
Keep `@sentry/node` pinned exactly to `11.9.0` and regenerate `bun.lock` with
`bun install` after updating it. The optional reporter uses a lazy ESM import
after trust is established. Keep default integrations and tracing disabled,
set `attachStacktrace: false`, and explicitly disable `dataCollection` categories
so only vetted messages are reported. Do not add automatic instrumentation or
an OpenTelemetry provider to this path. Run `bun test src/utils/sentry.test.ts`
when updating the SDK or initialization. Its Node.js minimum is covered by the
supported runtime; self-hosted Sentry requires 26.4.2 or later.
The root `overrides` block also controls transitive resolutions:
- `ip-address`: `10.7.4` sets the security floor for the MCP SDK's
+65 -45
View File
@@ -23,7 +23,7 @@
"@grpc/grpc-js": "1.14.6",
"@grpc/proto-loader": "0.8.1",
"@modelcontextprotocol/sdk": "1.32.1",
"@sentry/node": "10.70.0",
"@sentry/node": "11.9.0",
"@smithy/core": "3.36.2",
"@smithy/node-http-handler": "4.12.1",
"@types/bun": "1.4.4",
@@ -121,12 +121,6 @@
"@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.137.0", "", { "dependencies": { "@opentelemetry/api": "^1.4.1", "json-schema-to-ts": "^3.1.1", "standardwebhooks": "^1.0.0" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-YpEnbPz8kWF1BWthwUCnRhci7OvDLSUO8NFKoevoPkjNkK9CJeibZITgxr9vhqOP8Le61e5tNDHFTxw1dc6Png=="],
"@apm-js-collab/code-transformer": ["@apm-js-collab/code-transformer@0.18.1", "", { "dependencies": { "@types/estree": "^1.0.8", "astring": "^1.9.0", "esquery": "^1.7.0", "meriyah": "^6.1.4", "semifies": "^1.0.0", "source-map": "^0.6.0" }, "bin": { "code-transformer": "cli.js" } }, "sha512-u1Hb6bHjWtkSpiprwVP6YaHC1DTN4RAU3zYkUDUe7WMnJwdyU1pwTL9dFKiSJB9IiLue/EQovmyx6xhU7FFtAQ=="],
"@apm-js-collab/code-transformer-bundler-plugins": ["@apm-js-collab/code-transformer-bundler-plugins@0.7.4", "", { "dependencies": { "@apm-js-collab/code-transformer": "^0.18.1", "es-module-lexer": "^2.1.0", "magic-string": "^0.30.21", "module-details-from-path": "^1.0.4" } }, "sha512-nAfOeZPSUAQvJa1iFT/5oCrTm5YQhMMrfCNthNnaXHZiOQhu1KGuLoIx7HtbAi3wfwaBYLaICPIeenIaEwcXIg=="],
"@apm-js-collab/tracing-hooks": ["@apm-js-collab/tracing-hooks@0.13.0", "", { "dependencies": { "@apm-js-collab/code-transformer": "^0.18.0", "debug": "^4.4.1", "module-details-from-path": "^1.0.4" } }, "sha512-mTvWz9rnQwx1U3h0XPTHaX7bgfkpipLLTQyjlC2cdhQpQEuoLT0AGzoydeoq2NxfEVv6fWOOETcSbb2nptleyw=="],
"@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=="],
"@aws-crypto/sha256-browser": ["@aws-crypto/sha256-browser@5.2.0", "", { "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw=="],
@@ -345,20 +339,6 @@
"@opentelemetry/api": ["@opentelemetry/api@1.9.1", "", {}, "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q=="],
"@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.220.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-CmVa4ImJ+ynfrPMNaAXHET6Bhb44SwzmfyVJFq9ni2jgXJR/l7C6gfVFddNmHP+ZOkP9cf4f9DBe68qVLTHc9w=="],
"@opentelemetry/core": ["@opentelemetry/core@2.10.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-/wNZ8twnEQQA4HoHu22+vcsdru6pWPWxW+7w+FlxT6Id7PE/WIbZmVKkte+PF72e0F2dnImFeHD2syyE1Mw6MQ=="],
"@opentelemetry/instrumentation": ["@opentelemetry/instrumentation@0.220.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.220.0", "import-in-the-middle": "^3.0.0", "require-in-the-middle": "^8.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-xQx3E2WxP1mDvKzxLxX+CTCtNLa560YJZ3087qYHerl2YmiKpv7AH+dAy7vmx+eVrZ5BwhfWUAVoKOoxCNHcpw=="],
"@opentelemetry/resources": ["@opentelemetry/resources@2.10.0", "", { "dependencies": { "@opentelemetry/core": "2.10.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-q6MMm2zhggzsHVNbabYwut+a6nbuQQe3URUoxaojM/8K1IBfwwPzvxIjNi2/lI1TFe+fMHMW9MWhrtDLEXEnkA=="],
"@opentelemetry/sdk-trace": ["@opentelemetry/sdk-trace@2.10.0", "", { "dependencies": { "@opentelemetry/core": "2.10.0", "@opentelemetry/resources": "2.10.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-MfQGq3GRmTh5fM/y+OjaO0vj6+luCB1XO2gfXCalKCfgKw0eHL++sm75DNweC6ohlp+aFvACqeE0fYayqdRaoQ=="],
"@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.10.0", "", { "dependencies": { "@opentelemetry/core": "2.10.0", "@opentelemetry/resources": "2.10.0", "@opentelemetry/sdk-trace": "2.10.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-GuYQQT7QD2EeO8lcZLRQzcbOyhqAzL+6WWTKTU9mSUBYBazkEDl+VrQcXQhbB08OWM9anD1aHleVadzulpOaUQ=="],
"@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.43.0", "", {}, "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg=="],
"@orama/orama": ["@orama/orama@3.1.18", "", {}, "sha512-a61ljmRVVyG5MC/698C8/FfFDw5a8LOIvyOLW5fztgUXqUpc1jOfQzOitSCbge657OgXXThmY3Tk8fpiDb4UcA=="],
"@orama/plugin-data-persistence": ["@orama/plugin-data-persistence@3.1.18", "", { "dependencies": { "@msgpack/msgpack": "^3.1.2", "@orama/orama": "3.1.18", "dpack": "^0.6.22", "seqproto": "^0.2.3" } }, "sha512-pfBbpK96VRW/7IkdMHn2HaW3/+4k2C9Uwyup0IONNuz5bG3L1orCNFZPBmu+zcokOU2YH+IAVuQz6MlvqOe3iw=="],
@@ -465,17 +445,19 @@
"@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="],
"@sentry/conventions": ["@sentry/conventions@0.16.0", "", {}, "sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ=="],
"@sentry/bundler-plugins": ["@sentry/bundler-plugins@11.9.0", "", { "dependencies": { "@sentry/core": "11.9.0", "dotenv": "^17.4.2", "glob": "^13.0.6", "magic-string": "~0.30.8 || ^1.0.0", "oxc-parser": "^0.152.0", "sentry": "^0.45.0", "supports-color": "^8.1.1" }, "peerDependencies": { "rollup": ">=3.2.0", "webpack": ">=5.1.0" }, "optionalPeers": ["rollup", "webpack"] }, "sha512-wpz/gcM/6rld/wb4Mhf8kbDN9ngG5myKQSZC+45gzqR1w+GcdfVnYkp3sHL9AV4mrwL8skhTItdUghMwMTknKg=="],
"@sentry/core": ["@sentry/core@10.70.0", "", { "dependencies": { "@sentry/conventions": "^0.16.0" } }, "sha512-ozhCTDqg89oB4XmWfAwuHshABpvT7AkRpaPnogopPfMAaI61G1t8EKCJ4W7aum8JSBonlfyjPCyW5oYZFm0KvA=="],
"@sentry/conventions": ["@sentry/conventions@0.27.0", "", {}, "sha512-Mrqd/o7n/z7qWSYfQQA7x4fFTA0IqOG6JNsbBpnC97khPwZk7n6DPtOO7YgaYplSl8R8QLRbBz9Xv2lVhToSAg=="],
"@sentry/node": ["@sentry/node@10.70.0", "", { "dependencies": { "@opentelemetry/api": "^1.9.1", "@opentelemetry/instrumentation": "^0.220.0", "@opentelemetry/sdk-trace-base": "^2.9.0", "@sentry/conventions": "^0.16.0", "@sentry/core": "10.70.0", "@sentry/node-core": "10.70.0", "@sentry/opentelemetry": "10.70.0", "@sentry/server-utils": "10.70.0", "import-in-the-middle": "^3.0.0" } }, "sha512-SPOOVxmKTVIEtqvOKkQT163e/pOwucjS7OPsCHyRs8sFR4nfBNu0EThplyqnvqd5BWBMTPH6WTBQfo+QWHV+HA=="],
"@sentry/core": ["@sentry/core@11.9.0", "", { "dependencies": { "@sentry/conventions": "^0.27.0" } }, "sha512-t0dZyHILt2XVC4Fe9UmAty6YRfOuQv95cvWeNl+HlW4LphjF5/jkALV3cJCUMdhpblYELgIJC4U/S/GoYuI2bA=="],
"@sentry/node-core": ["@sentry/node-core@10.70.0", "", { "dependencies": { "@sentry/conventions": "^0.16.0", "@sentry/core": "10.70.0", "@sentry/opentelemetry": "10.70.0", "import-in-the-middle": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/core": "^1.30.1 || ^2.1.0", "@opentelemetry/exporter-trace-otlp-http": ">=0.57.0 <1", "@opentelemetry/instrumentation": ">=0.57.1 <1", "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/core", "@opentelemetry/exporter-trace-otlp-http", "@opentelemetry/instrumentation", "@opentelemetry/sdk-trace-base"] }, "sha512-oPOEVVNxv5WHtckx2i06Wi9FLWyvOg/1DUeX732jZ4iqT2nupINaMH4nF4f4kSvUThFnxkFSRQxwqOxgzMKhKA=="],
"@sentry/node": ["@sentry/node@11.9.0", "", { "dependencies": { "@opentelemetry/api": "^1.9.1", "@sentry/bundler-plugins": "11.9.0", "@sentry/conventions": "^0.27.0", "@sentry/core": "11.9.0", "@sentry/opentelemetry": "11.9.0", "@sentry/server-runtime-injection": "11.9.0", "@sentry/server-utils": "11.9.0" } }, "sha512-cz219PzVaAPTlQ4bRBTRBCOvxbZkXK0KkqyqWPV6OmSn6vWn935CTfOs+MKUc1A+locvB8i9OfBIbO6v03BBuQ=="],
"@sentry/opentelemetry": ["@sentry/opentelemetry@10.70.0", "", { "dependencies": { "@sentry/conventions": "^0.16.0", "@sentry/core": "10.70.0" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/core": "^1.30.1 || ^2.1.0", "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0" } }, "sha512-UNV/2tqypcUK6FDzerAsFJn1Km/c4VZCYkUZDNbnV5S0cwAq2BYKMo4M5vovaLDBQlxA+Wk9ovbxi5wYjjl9fw=="],
"@sentry/opentelemetry": ["@sentry/opentelemetry@11.9.0", "", { "dependencies": { "@sentry/conventions": "^0.27.0", "@sentry/core": "11.9.0" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0" } }, "sha512-FMCkAbEfBpFterrkR7k4P8LEabUAEHjZMbu+ycDjbnI1U4pT22tpwcFJOt06orFkacoWNsYT9+EdfmkvaCKtSA=="],
"@sentry/server-utils": ["@sentry/server-utils@10.70.0", "", { "dependencies": { "@apm-js-collab/code-transformer-bundler-plugins": "^0.7.3", "@apm-js-collab/tracing-hooks": "^0.13.0", "@sentry/conventions": "^0.16.0", "@sentry/core": "10.70.0", "meriyah": "^6.1.4" } }, "sha512-rzegZjMFFgCp3o+N8+XU13rfSvz4B+f8rU0ijBGrQcHdMNyfsFDTu1UTm262JofmrV2u+s+D0u0vFTnqtOGkbA=="],
"@sentry/server-runtime-injection": ["@sentry/server-runtime-injection@11.9.0", "", { "dependencies": { "@sentry/core": "11.9.0", "@sentry/server-utils": "11.9.0" } }, "sha512-Q2G5xaeoiYwGheCZyeqZ/v//SZYjRLlITlanTsZM/fox1qk7cmbDX7+o4q+JkApVV9VuXQZVxppaVCsBglfDKA=="],
"@sentry/server-utils": ["@sentry/server-utils@11.9.0", "", { "dependencies": { "@opentelemetry/api": "^1.9.1", "@sentry/conventions": "^0.27.0", "@sentry/core": "11.9.0" } }, "sha512-xHETix5jAjs/4tY+b9RCejEU7O49pNBiGgCWX0KFRMSpWbw9moqQ8i8N62bS7L+/BJnBcZ5SZ1nweXh7w787Qg=="],
"@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="],
@@ -619,8 +601,6 @@
"ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
"astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="],
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
"auto-bind": ["auto-bind@5.0.1", "", {}, "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg=="],
@@ -661,8 +641,6 @@
"chokidar": ["chokidar@6.0.0", "", { "dependencies": { "readdirp": "^5.1.1" } }, "sha512-Ec2Z7wpcKBiMpR7DxteoqS/sq5G5d2A4fpxzPv6+fD7xNNdrTghn7L11YwV5EN1B3i72Erc3XVT5FkWIDx90+A=="],
"cjs-module-lexer": ["cjs-module-lexer@2.2.1", "", {}, "sha512-Ca8swihM+/4yKecYHY52kgJd300hi2lADU/a1RxNTRe+RJ9jvqQlESpbz9DnG9mowez8qwXHB8qYdIUw9e+F5Q=="],
"cli-boxes": ["cli-boxes@4.0.1", "", {}, "sha512-5IOn+jcCEHEraYolBPs/sT4BxYCe2nHg374OPiItB1O96KZFseS2gthU4twyYzeDcFew4DaUM/xwc5BQf08JJw=="],
"cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
@@ -715,6 +693,8 @@
"dijkstrajs": ["dijkstrajs@1.0.3", "", {}, "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="],
"dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="],
"dpack": ["dpack@0.6.22", "", {}, "sha512-WGPNlW2OAE7Bj0eODMpAHUcEqxrlg01e9OFZDxQodminIgC194/cRHT7K04Z1j7AUEWTeeplYGrIv/xRdwU9Hg=="],
"duck-duck-scrape": ["duck-duck-scrape@2.2.7", "", { "dependencies": { "html-entities": "^2.3.3", "needle": "^3.2.0" } }, "sha512-BEcglwnfx5puJl90KQfX+Q2q5vCguqyMpZcSRPBWk8OY55qWwV93+E+7DbIkrGDW4qkqPfUvtOUdi0lXz6lEMQ=="],
@@ -735,8 +715,6 @@
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
"es-module-lexer": ["es-module-lexer@2.3.2", "", {}, "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw=="],
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
@@ -841,6 +819,8 @@
"get-tsconfig": ["get-tsconfig@4.14.3", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA=="],
"glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="],
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
"google-auth-library": ["google-auth-library@11.3.1", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^8.3.0", "gcp-metadata": "^9.0.0", "google-logging-utils": "^2.0.0", "jws": "^4.0.0" } }, "sha512-mQS0R2Ev/DKkzuWSYtDeq15uhDeaWuT2qzLYqJ41LmooxVKHmn5ut3pc6dlHY6wfHW/D7FBmOYjAtkjaD1A7qA=="],
@@ -893,8 +873,6 @@
"ignore": ["ignore@7.0.13", "", {}, "sha512-2E8Q6rh+1RRSq/YdaGsNfXsEZy9FVqv6BSSrkmZJP7BnaoPnRfKL+YaofPvP+5rzdk144qSm6i4+hvPgUcbV4Q=="],
"import-in-the-middle": ["import-in-the-middle@3.3.3", "", { "dependencies": { "cjs-module-lexer": "^2.2.0", "es-module-lexer": "^2.2.0", "module-details-from-path": "^1.0.4" } }, "sha512-AiohS3H80sXO6owEltjGX+glb7qXaDhBoJb9XcQVH4UI207xu/bDLUcadVKp7Qe576reg9yr/PXZjV5qx8gfbA=="],
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
"indent-string": ["indent-string@5.0.0", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="],
@@ -997,17 +975,15 @@
"merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
"meriyah": ["meriyah@6.1.4", "", {}, "sha512-Sz8FzjzI0kN13GK/6MVEsVzMZEPvOhnmmI1lU5+/1cGOiK3QUahntrNNtdVeihrO7t9JpoH75iMNXg6R6uWflQ=="],
"mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
"mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
"minimatch": ["minimatch@10.2.6", "", { "dependencies": { "brace-expansion": "^5.0.8" } }, "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A=="],
"mnemonist": ["mnemonist@0.39.8", "", { "dependencies": { "obliterator": "^2.0.1" } }, "sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ=="],
"minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="],
"module-details-from-path": ["module-details-from-path@1.0.4", "", {}, "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w=="],
"mnemonist": ["mnemonist@0.39.8", "", { "dependencies": { "obliterator": "^2.0.1" } }, "sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
@@ -1061,6 +1037,8 @@
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
"path-scurry": ["path-scurry@2.0.2", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg=="],
"path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="],
"picomatch": ["picomatch@4.0.7", "", {}, "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA=="],
@@ -1109,8 +1087,6 @@
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
"require-in-the-middle": ["require-in-the-middle@8.0.1", "", { "dependencies": { "debug": "^4.3.5", "module-details-from-path": "^1.0.3" } }, "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ=="],
"require-main-filename": ["require-main-filename@2.0.0", "", {}, "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="],
"resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
@@ -1129,12 +1105,12 @@
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
"semifies": ["semifies@1.0.0", "", {}, "sha512-xXR3KGeoxTNWPD4aBvL5NUpMTT7WMANr3EWnaS190QVkY52lqqcVRD7Q05UVbBhiWDGWMlJEUam9m7uFFGVScw=="],
"semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="],
"send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="],
"sentry": ["sentry@0.45.0", "", { "bin": { "sentry": "dist/bin.cjs" } }, "sha512-kgmEbLlC22lL7uneeUm9Sjz65rp5cvwYOMTxXbZCmBum4sbVre/lLT3zTV3kwXHJ9/BM8ZS0/GhZe9Cl1t6WyA=="],
"seqproto": ["seqproto@0.2.3", "", {}, "sha512-HpNyPYl7DJa2a6XQJ+MJAc6ft6Y9ZU+zRiuvTHFpLPeqvapcTAGFJyhMEIN7Y7VXhWT6NEdNVhbXFHwtaCOfCw=="],
"serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="],
@@ -1163,8 +1139,6 @@
"smol-toml": ["smol-toml@1.9.1", "", {}, "sha512-j7WyPdf0CJoutMzMoPLF/bVXHtKZIT9Rb4eGeEbWw8X2t5oirjPgpL+OpaENZrILYaXqYwnVH+qgUwVbl+pUJQ=="],
"source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
"standardwebhooks": ["standardwebhooks@1.1.1", "", { "dependencies": { "@stablelib/base64": "^1.0.0", "fast-sha256": "^1.3.0" } }, "sha512-bCbX9ZEyFkWPsRz7Bl3NuQUJohmwGSev/yhr7vhaGPlc4AfIrspIRa6cPTBuI1ItmrTDJ4d/S2hCsfe4+vQGnQ=="],
"statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
@@ -1437,6 +1411,10 @@
"@img/sharp-wasm32/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="],
"@sentry/bundler-plugins/oxc-parser": ["oxc-parser@0.152.0", "", { "dependencies": { "@oxc-project/types": "^0.152.0" }, "optionalDependencies": { "@oxc-parser/binding-android-arm-eabi": "0.152.0", "@oxc-parser/binding-android-arm64": "0.152.0", "@oxc-parser/binding-darwin-arm64": "0.152.0", "@oxc-parser/binding-darwin-x64": "0.152.0", "@oxc-parser/binding-freebsd-x64": "0.152.0", "@oxc-parser/binding-linux-arm-gnueabihf": "0.152.0", "@oxc-parser/binding-linux-arm-musleabihf": "0.152.0", "@oxc-parser/binding-linux-arm64-gnu": "0.152.0", "@oxc-parser/binding-linux-arm64-musl": "0.152.0", "@oxc-parser/binding-linux-ppc64-gnu": "0.152.0", "@oxc-parser/binding-linux-riscv64-gnu": "0.152.0", "@oxc-parser/binding-linux-riscv64-musl": "0.152.0", "@oxc-parser/binding-linux-s390x-gnu": "0.152.0", "@oxc-parser/binding-linux-x64-gnu": "0.152.0", "@oxc-parser/binding-linux-x64-musl": "0.152.0", "@oxc-parser/binding-openharmony-arm64": "0.152.0", "@oxc-parser/binding-win32-arm64-msvc": "0.152.0", "@oxc-parser/binding-win32-ia32-msvc": "0.152.0", "@oxc-parser/binding-win32-x64-msvc": "0.152.0" } }, "sha512-ZztsZgaCWmPeKYMdoTT5e0oV/oxD1NeLWNR3kAtr/G707cUPYaaozRlVDHLCGnszHJRszN9mJwmPNpqgwloC0A=="],
"@sentry/bundler-plugins/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
"@smithy/core/@smithy/types": ["@smithy/types@4.19.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-r7jh49VJxGerfAcTQA6gXcKc+98zOp/tqRwzYjgOE+iSQsP6cEU1hq2QzbuipmP68QtYdY9wKEhiCQZIzHgZ4Q=="],
"@smithy/credential-provider-imds/@smithy/types": ["@smithy/types@4.19.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-r7jh49VJxGerfAcTQA6gXcKc+98zOp/tqRwzYjgOE+iSQsP6cEU1hq2QzbuipmP68QtYdY9wKEhiCQZIzHgZ4Q=="],
@@ -1539,6 +1517,48 @@
"@aws-sdk/nested-clients/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4": ["@smithy/signature-v4@5.7.4", "", { "dependencies": { "@smithy/core": "^3.35.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-tHy0K0VtqNd5Y7Y41h0a0Lhh0L1GzC08dTWg0F7vRJWFtTENg7IZikf3wQkanYIRdb7ngoIPMTmqgUi401fEeQ=="],
"@sentry/bundler-plugins/oxc-parser/@oxc-parser/binding-android-arm-eabi": ["@oxc-parser/binding-android-arm-eabi@0.152.0", "", { "os": "android", "cpu": "arm" }, "sha512-393VtirINkae4XHBTkq/yQxhOwEr4V+r0et37P2fp+sYZSdYgZTRDPo5Um8vBh0QjMZ+RmaMy4PP0qwr4cTMwA=="],
"@sentry/bundler-plugins/oxc-parser/@oxc-parser/binding-android-arm64": ["@oxc-parser/binding-android-arm64@0.152.0", "", { "os": "android", "cpu": "arm64" }, "sha512-iYFcExATJNZ0rZj0o02THbD1Nc9jAW0nVgIQPee6ejbSASpbZXlIMw8jxtpzaG14JQTWwGSZ6U5dEtRJXE6V3Q=="],
"@sentry/bundler-plugins/oxc-parser/@oxc-parser/binding-darwin-arm64": ["@oxc-parser/binding-darwin-arm64@0.152.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-fnU+DfUzi5/rFy0LzmipYRz06PAUUkOS8kCJQ3Eg6oqlunC1XZXoffFqxmdL51RmcbYtUG3P8DVG1U5fckoa2A=="],
"@sentry/bundler-plugins/oxc-parser/@oxc-parser/binding-darwin-x64": ["@oxc-parser/binding-darwin-x64@0.152.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-Y3U2okryWTs7hDld6UhjJwLNf8/do+x4g4C+1U5SiGwrTLwbO83WJYJA8+l9iFSilWBgnfpd/+U76tfeNxUHzA=="],
"@sentry/bundler-plugins/oxc-parser/@oxc-parser/binding-freebsd-x64": ["@oxc-parser/binding-freebsd-x64@0.152.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-XkhkRrrPffEw+ZLv/d/5pEni0buxinm/E/N6fLbJCunD1DBbeCTtVP23ESV+4CWCWdSz397asks1ViGslpmWiQ=="],
"@sentry/bundler-plugins/oxc-parser/@oxc-parser/binding-linux-arm-gnueabihf": ["@oxc-parser/binding-linux-arm-gnueabihf@0.152.0", "", { "os": "linux", "cpu": "arm" }, "sha512-MLzsRRbdfmdzo6JEhbC1yKU9ItfvM8tEf18HM32jCiNYZEz6J3bRBRl3cTkKR0vBdrk9ZVlcARXtwHccs+epDA=="],
"@sentry/bundler-plugins/oxc-parser/@oxc-parser/binding-linux-arm-musleabihf": ["@oxc-parser/binding-linux-arm-musleabihf@0.152.0", "", { "os": "linux", "cpu": "arm" }, "sha512-HHerTEJz7Iy10Ji6BGsk4gqG6PU0UzFuBu91jIEOX89FnrI5ECWSEr7BLpkYQhp7U0QFtMnBhNfQKI0hYZK78w=="],
"@sentry/bundler-plugins/oxc-parser/@oxc-parser/binding-linux-arm64-gnu": ["@oxc-parser/binding-linux-arm64-gnu@0.152.0", "", { "os": "linux", "cpu": "arm64", "libc": "glibc" }, "sha512-9Bb8GyzZ2uSLzRDZ27usaKokr07mxiBe2dDF5b6bd2janmaJTT1gL51UfP7AU30yFiXrJvcRyFKREAgKwZYs0w=="],
"@sentry/bundler-plugins/oxc-parser/@oxc-parser/binding-linux-arm64-musl": ["@oxc-parser/binding-linux-arm64-musl@0.152.0", "", { "os": "linux", "cpu": "arm64", "libc": "musl" }, "sha512-om7iDMUroI74A/lfRRWY9/Ev3VWrVsrUZ4tyvyh8raBey/sdcfyAD7tYWc+iykAiotnmftyKTpUKFr1QnaBrAQ=="],
"@sentry/bundler-plugins/oxc-parser/@oxc-parser/binding-linux-ppc64-gnu": ["@oxc-parser/binding-linux-ppc64-gnu@0.152.0", "", { "os": "linux", "cpu": "ppc64", "libc": "glibc" }, "sha512-Lf1OL2lMTqwDMFC6jnRb30B+WYHKV0XT+bPHcFM6xHtNVwxpobiTzUIvKH6a0LWpvfwyru8Yu6zCGHqqFHH+gg=="],
"@sentry/bundler-plugins/oxc-parser/@oxc-parser/binding-linux-riscv64-gnu": ["@oxc-parser/binding-linux-riscv64-gnu@0.152.0", "", { "os": "linux", "cpu": "none", "libc": "glibc" }, "sha512-BYWSMQK+YJ9ZILStPcmmiSVq8KEG9Qdq/c4BHPC3rUEq/fEDCVjUIwdj5ZzjgZ9P9is9PKu0tcAXzq1ZXg4urQ=="],
"@sentry/bundler-plugins/oxc-parser/@oxc-parser/binding-linux-riscv64-musl": ["@oxc-parser/binding-linux-riscv64-musl@0.152.0", "", { "os": "linux", "cpu": "none", "libc": "musl" }, "sha512-d2LqHNNNqdH185sRK5dPKWZu201JrNu9/CRFuVEuT+ht7n/QaVblEMWT9MALQPrDWYvhG10P+NSKJFquDWZ09w=="],
"@sentry/bundler-plugins/oxc-parser/@oxc-parser/binding-linux-s390x-gnu": ["@oxc-parser/binding-linux-s390x-gnu@0.152.0", "", { "os": "linux", "cpu": "s390x", "libc": "glibc" }, "sha512-8gBeHt5rA/8+pKhHl+zLggfa3L/V/L2at96p7ty6pBE7id6lFO19nFUc15u2PcjC4OYeYK465MKy1fhhbpFIGw=="],
"@sentry/bundler-plugins/oxc-parser/@oxc-parser/binding-linux-x64-gnu": ["@oxc-parser/binding-linux-x64-gnu@0.152.0", "", { "os": "linux", "cpu": "x64", "libc": "glibc" }, "sha512-ltgrSF6L+yPTVsIdaAyqKq3zTqzj/1VlNbxWZuxAe8flR3sWdyNsKvdXukoO8/ucJbLickhmieRBge+o/e8Ugw=="],
"@sentry/bundler-plugins/oxc-parser/@oxc-parser/binding-linux-x64-musl": ["@oxc-parser/binding-linux-x64-musl@0.152.0", "", { "os": "linux", "cpu": "x64", "libc": "musl" }, "sha512-LcMp6BvrBnewSfdkLHoSqidhoAq4syKOTBV5TYHWd+2VnGkfD/nPRRVIzTBt/Lkbt7pfOFDGtkiTntFS0p6sQQ=="],
"@sentry/bundler-plugins/oxc-parser/@oxc-parser/binding-openharmony-arm64": ["@oxc-parser/binding-openharmony-arm64@0.152.0", "", { "os": "none", "cpu": "arm64" }, "sha512-jbhoMCslVxzRCRNQ8TSHxjYdSKRvk8O6ciLVDkahJvmyHDzBqba/bMQHM6ULKj/rgHNNbSzlIiakdx8SEfkJzA=="],
"@sentry/bundler-plugins/oxc-parser/@oxc-parser/binding-win32-arm64-msvc": ["@oxc-parser/binding-win32-arm64-msvc@0.152.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-JZYVlhv36mr/nertnRB7vchJECOBb1Lz6lltalILsB+o9M/is+H6BEDRLQKdqA4JKs6NjJszo46+iM5sfwj6jQ=="],
"@sentry/bundler-plugins/oxc-parser/@oxc-parser/binding-win32-ia32-msvc": ["@oxc-parser/binding-win32-ia32-msvc@0.152.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-1X9oganvVsunRH0lH10UU05N+H9atpX6WsOybByUjwnfcd3h/0tYwvuv16XuHglrIUOnxV2TodbLVEwwafd64g=="],
"@sentry/bundler-plugins/oxc-parser/@oxc-parser/binding-win32-x64-msvc": ["@oxc-parser/binding-win32-x64-msvc@0.152.0", "", { "os": "win32", "cpu": "x64" }, "sha512-sw0M80/dn9rcpu+Rqqph/C+MjvDDce6MlXmFjsNQtPEymha0eJy3ml6+foSUTqFfpDIDyHZZ4eoJ43IoMe8bJw=="],
"@sentry/bundler-plugins/oxc-parser/@oxc-project/types": ["@oxc-project/types@0.152.0", "", {}, "sha512-oM/5rLBm2tPkg0iBgkH/FOeR3PCDpY19GTgAZjMFM8h9WI9VW7cLgzp6nwtarYKmovavIQZ+Fe/RKX/8C8O/Rw=="],
"@sentry/bundler-plugins/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
"@typespec/ts-http-runtime/https-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
"axios/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
+4 -2
View File
@@ -407,7 +407,7 @@ missing. Install only what you need:
| 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 (clipboard paste, drag-and-drop paths, FileReadTool local decode) | local image decode — **not** pre-encoded URL/base64 passthrough | `npm i -g sharp` (intentionally opt-in; prebuilt releases load it externally only when processing requires it) |
| Optional error reporting | `SENTRY_DSN` is set | `npm i -g @sentry/node`. Without this package installed, setting `SENTRY_DSN` has no effect and reporting is silently disabled. |
| Optional error reporting | `SENTRY_DSN` is set | `npm i -g @sentry/node@11.9.0`. Without this package installed, setting `SENTRY_DSN` has no effect and reporting is silently disabled. |
When installing OpenClaude from source (`bun install`), all of these are
already present as dev dependencies, so source/dev builds need no extra steps.
@@ -693,6 +693,8 @@ Notes:
- Reporting is skipped even when `SENTRY_DSN` is set if `DISABLE_TELEMETRY` or
`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` is set, matching the same privacy
levels used for existing telemetry (see [Runtime Hardening](#runtime-hardening)).
- Automatic integrations, tracing, message stack traces, and SDK data collection
are disabled. Self-hosted Sentry requires version 26.4.2 or later.
- Only sanitized, telemetry-safe error messages are sent — never raw error
messages, which may contain file paths or other identifying information.
- `@sentry/node` is an optional dev dependency and is **not included** in the
@@ -702,7 +704,7 @@ Notes:
disabled (no error, no crash). Install it explicitly with:
```bash
npm i -g @sentry/node
npm i -g @sentry/node@11.9.0
```
Source builds (`bun install`) already include it as a dev dependency, so no
+1 -1
View File
@@ -103,7 +103,7 @@
"@grpc/grpc-js": "1.14.6",
"@grpc/proto-loader": "0.8.1",
"@modelcontextprotocol/sdk": "1.32.1",
"@sentry/node": "10.70.0",
"@sentry/node": "11.9.0",
"@smithy/core": "3.36.2",
"@smithy/node-http-handler": "4.12.1",
"@types/bun": "1.4.4",
+166
View File
@@ -0,0 +1,166 @@
import assert from 'node:assert/strict'
import { jest, spyOn } from 'bun:test'
import * as Sentry from '@sentry/node'
import { snapshotEnv } from '../envSnapshot.js'
import {
getPendingShutdownForTesting,
gracefulShutdown,
gracefulShutdownSync,
resetShutdownState,
} from '../../utils/gracefulShutdown.js'
import { initializeSentry } from '../../utils/sentry.js'
import { registerCleanup } from '../../utils/cleanupRegistry.js'
import { sleep } from '../../utils/sleep.js'
const mode = process.argv[2]
const restoreEnv = snapshotEnv([
'NODE_ENV',
'SENTRY_DSN',
'DISABLE_TELEMETRY',
'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC',
'CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS',
], { clear: true })
const originalExit = process.exit
const exitError = new Error('mocked process exit')
let exitCode: number | undefined
let flushCalls = 0
let flushTimeout: number | undefined
let flushed = false
let flushedBeforeExit = false
let unregisterCleanup: (() => void) | undefined
let restoreHooks: (() => void) | undefined
let sessionEndHooksStarted = false
let notifyExit!: () => void
const exitObserved = new Promise<void>(resolve => { notifyExit = resolve })
let notifyFlushStarted!: () => void
const flushStarted = new Promise<void>(resolve => { notifyFlushStarted = resolve })
let releaseFlush!: () => void
const pendingFlush = new Promise<boolean>(resolve => {
releaseFlush = () => {
flushed = true
resolve(true)
}
})
try {
process.env.NODE_ENV = 'test'
process.env.SENTRY_DSN = 'https://example@o0.ingest.sentry.io/0'
await initializeSentry()
const client = Sentry.getClient()
assert(client, 'SDK was not initialized')
// Keep the real SDK and application modules, replacing only the flush I/O.
// No events are sent to the network in this fixture.
client.flush = timeout => {
flushCalls++
flushTimeout = timeout
if (mode === 'enabled' || mode === 'blocked') jest.useFakeTimers()
notifyFlushStarted()
if (mode === 'throws') throw new Error('flush threw')
if (mode === 'rejects') return Promise.reject(new Error('flush rejected'))
return pendingFlush
}
if (mode === 'dsn-unset') delete process.env.SENTRY_DSN
if (mode === 'telemetry-disabled') process.env.DISABLE_TELEMETRY = '1'
if (mode === 'nonessential-disabled') {
process.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = '1'
}
if (mode === 'default-budget') {
const hooks = await import('../../utils/hooks.js')
assert.equal(hooks.getSessionEndHookTimeoutMs(), 1500)
// Consume the real cleanup and default hook budgets before the Sentry drain.
const hookSpy = spyOn(hooks, 'executeSessionEndHooks')
.mockImplementation(() => sleep(1500))
restoreHooks = () => { hookSpy.mockRestore() }
unregisterCleanup = registerCleanup(() => sleep(2000))
jest.useFakeTimers()
}
if (mode === 'failsafe' || mode === 'sync-rejection') {
const hooks = await import('../../utils/hooks.js')
if (mode === 'failsafe') {
// A hook that ignores its abort signal leaves only the failsafe able to exit.
const hookSpy = spyOn(hooks, 'executeSessionEndHooks').mockImplementation(() => {
sessionEndHooksStarted = true
return new Promise<void>(() => {})
})
restoreHooks = () => { hookSpy.mockRestore() }
} else {
// Fail before the failsafe is armed or the normal-path flush can run.
const timeoutSpy = spyOn(hooks, 'getSessionEndHookTimeoutMs').mockImplementation(() => {
throw new Error('shutdown setup failed')
})
restoreHooks = () => { timeoutSpy.mockRestore() }
}
jest.useFakeTimers()
}
process.exit = ((code?: number) => {
exitCode = code
flushedBeforeExit = flushed
notifyExit()
// The failsafe runs in an async timer callback; avoid an unhandled rejection.
if (mode === 'failsafe') return undefined as never
throw exitError
}) as typeof process.exit
let shutdown: Promise<void>
if (mode === 'sync-rejection') {
gracefulShutdownSync(7)
const pending = getPendingShutdownForTesting()
assert(pending, 'sync shutdown must expose its catch-handler completion')
shutdown = pending
} else {
shutdown = gracefulShutdown(7).catch(error => {
if (error !== exitError) throw error
})
}
if (mode === 'failsafe') {
// Drive the actual armed timer while the normal shutdown stays in its hook.
for (let elapsed = 0; elapsed < 10_000 && flushCalls === 0; elapsed += 10) {
jest.advanceTimersByTime(10)
for (let i = 0; i < 20; i++) await Promise.resolve()
}
assert(sessionEndHooksStarted, 'normal shutdown must be stuck in its hook')
assert.equal(flushCalls, 1, 'failsafe must begin a Sentry drain')
}
if (mode === 'default-budget') {
// Advance through shutdown until the normal-path flush signals readiness.
// Yield between ticks so each awaited shutdown stage can schedule its timers.
for (let elapsed = 0; elapsed < 5000 && flushCalls === 0; elapsed += 10) {
jest.advanceTimersByTime(10)
for (let i = 0; i < 20; i++) await Promise.resolve()
}
assert.equal(flushCalls, 1, 'normal-path Sentry flush must start')
assert.equal(exitCode, undefined, 'shutdown must wait for flush')
// Cross the old 5s failsafe deadline while the normal flush is still pending.
jest.advanceTimersByTime(1999)
for (let i = 0; i < 20; i++) await Promise.resolve()
assert.equal(flushCalls, 1, 'failsafe must not start another flush during the normal drain')
assert.equal(exitCode, undefined, 'shutdown must not exit before the normal flush finishes')
releaseFlush()
}
if (mode === 'enabled' || mode === 'blocked' || mode === 'failsafe' || mode === 'sync-rejection') {
await Promise.race([
flushStarted,
shutdown.then(() => { throw new Error('exited without flushing Sentry') }),
])
assert.equal(exitCode, undefined, 'shutdown must wait for flush')
jest.advanceTimersByTime(1999)
// Let an incorrectly early deadline propagate through the awaited helpers.
for (let i = 0; i < 5; i++) await Promise.resolve()
assert.equal(exitCode, undefined, 'flush deadline must not expire early')
if (mode === 'blocked') jest.advanceTimersByTime(1)
else releaseFlush()
}
// The failsafe exits despite the deliberately hung hook in normal shutdown.
if (mode === 'failsafe') await exitObserved
else await shutdown
console.log(JSON.stringify({ flushCalls, flushTimeout, exitCode, flushedBeforeExit }))
} finally {
unregisterCleanup?.()
restoreHooks?.()
jest.useRealTimers()
process.exit = originalExit
process.exitCode = 0
resetShutdownState()
restoreEnv()
}
+41
View File
@@ -0,0 +1,41 @@
import { expect, test } from 'bun:test'
import { fileURLToPath } from 'node:url'
test.each([
['enabled', 1, true],
['default-budget', 1, true],
['failsafe', 1, true],
['sync-rejection', 1, true],
['dsn-unset', 0, false],
['telemetry-disabled', 0, false],
['nonessential-disabled', 0, false],
['blocked', 1, false],
['throws', 1, false],
['rejects', 1, false],
] as const)('graceful shutdown handles Sentry flush: %s', async (mode, flushCalls, flushedBeforeExit) => {
// Isolate SDK state, process.exit, and fake timers from the shared test runner.
// The test deadline leaves time to collect output after the child deadline.
const child = Bun.spawn([
process.execPath,
fileURLToPath(new URL('../test/fixtures/sentryShutdown.fixture.ts', import.meta.url)),
mode,
], {
cwd: fileURLToPath(new URL('../../', import.meta.url)),
stdout: 'pipe',
stderr: 'pipe',
timeout: 10_000,
})
const [stdout, stderr, exitCode] = await Promise.all([
new Response(child.stdout).text(),
new Response(child.stderr).text(),
child.exited,
])
expect(stderr).toBe('')
expect(exitCode).toBe(0)
expect(JSON.parse(stdout)).toEqual({
flushCalls,
...(flushCalls ? { flushTimeout: 2000 } : {}),
exitCode: 7,
flushedBeforeExit,
})
}, 15_000)
+13 -5
View File
@@ -30,7 +30,11 @@ import {
wrapForMultiplexer,
} from '../ink/termio/osc.js'
import { shutdownDatadog } from '../services/analytics/datadog.js'
import { reportErrorToSentry } from './sentry.js'
import {
flushSentry,
reportErrorToSentry,
SENTRY_FLUSH_TIMEOUT_MS,
} from './sentry.js'
import {
type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
logEvent,
@@ -370,10 +374,11 @@ export function gracefulShutdownSync(
process.exitCode = exitCode
pendingShutdown = gracefulShutdown(exitCode, reason, options)
.catch(error => {
.catch(async error => {
logForDebugging(`Graceful shutdown failed: ${error}`, { level: 'error' })
cleanupTerminalModes()
printResumeHint()
await flushSentry()
forceExit(exitCode)
})
// Prevent unhandled rejection: forceExit re-throws in test mode,
@@ -441,14 +446,16 @@ export async function gracefulShutdown(
// Failsafe: guarantee process exits even if cleanup hangs (e.g., MCP connections).
// Runs cleanupTerminalModes first so a hung cleanup doesn't leave the terminal dirty.
// Budget = max(5s, hook budget + 3.5s headroom for cleanup + analytics flush).
// Budget = max(5s, hook budget + 3.5s cleanup/analytics headroom + Sentry drain).
// If cleanup hangs, the failsafe gives Sentry one bounded drain before exit.
failsafeTimer = setTimeout(
code => {
async code => {
cleanupTerminalModes(true)
printResumeHint()
await flushSentry()
forceExit(code)
},
Math.max(5000, sessionEndTimeoutMs + 3500),
Math.max(5000, sessionEndTimeoutMs + 3500 + SENTRY_FLUSH_TIMEOUT_MS),
exitCode,
)
failsafeTimer.unref()
@@ -558,6 +565,7 @@ export async function gracefulShutdown(
flushInterruptionTrace('graceful_shutdown')
await Promise.race([waitForInterruptionTraceFlush(), sleep(500)])
await flushSentry()
forceExit(exitCode)
}
+73 -40
View File
@@ -1,25 +1,20 @@
import { afterEach, beforeEach, expect, mock, test } from 'bun:test'
import { afterEach, beforeEach, expect, test } from 'bun:test'
import { hostname } from 'node:os'
import { fileURLToPath } from 'node:url'
import { snapshotEnv } from '../test/envSnapshot.js'
const ORIGINAL_SENTRY_DSN = process.env.SENTRY_DSN
const ORIGINAL_DISABLE_TELEMETRY = process.env.DISABLE_TELEMETRY
let restoreEnv: () => void
beforeEach(() => {
delete process.env.SENTRY_DSN
delete process.env.DISABLE_TELEMETRY
restoreEnv = snapshotEnv([
'SENTRY_DSN',
'DISABLE_TELEMETRY',
'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC',
], { clear: true })
})
afterEach(() => {
if (ORIGINAL_SENTRY_DSN === undefined) {
delete process.env.SENTRY_DSN
} else {
process.env.SENTRY_DSN = ORIGINAL_SENTRY_DSN
}
if (ORIGINAL_DISABLE_TELEMETRY === undefined) {
delete process.env.DISABLE_TELEMETRY
} else {
process.env.DISABLE_TELEMETRY = ORIGINAL_DISABLE_TELEMETRY
}
mock.restore()
restoreEnv()
})
test('isSentryEnabled is false when SENTRY_DSN is unset', async () => {
@@ -40,6 +35,13 @@ test('isSentryEnabled is false when SENTRY_DSN is set but DISABLE_TELEMETRY is s
expect(isSentryEnabled()).toBe(false)
})
test('isSentryEnabled is false when SENTRY_DSN is set but CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC is set', async () => {
process.env.SENTRY_DSN = 'https://example@o0.ingest.sentry.io/0'
process.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = '1'
const { isSentryEnabled } = await import('./sentry.js')
expect(isSentryEnabled()).toBe(false)
})
test('reportErrorToSentry does not throw when Sentry is disabled', async () => {
const { reportErrorToSentry } = await import('./sentry.js')
const { TelemetrySafeError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS: TelemetrySafeError } =
@@ -60,28 +62,59 @@ test('reportErrorToSentry does not throw for a plain (non-sanitized) Error', asy
).not.toThrow()
})
test('reportErrorToSentry only reports TelemetrySafeError, never a raw Error message', async () => {
process.env.SENTRY_DSN = 'https://example@o0.ingest.sentry.io/0'
test('reportErrorToSentry only reports sanitized messages without stacks using the real SDK', async () => {
// A fresh process avoids shared SDK state and module mocks from other tests.
// Leave time for the child deadline and reading its output during cleanup.
const child = Bun.spawn([process.execPath, '--eval', `
import * as Sentry from '@sentry/node'
import { flushSentry, initializeSentry, reportErrorToSentry } from '${new URL('./sentry.ts', import.meta.url).href}'
import { TelemetrySafeError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS as TelemetrySafeError } from '${new URL('./errors.ts', import.meta.url).href}'
const captureMessage = mock(() => {})
mock.module('@sentry/node', () => ({
init: mock(() => {}),
captureMessage,
}))
const { initializeSentry, reportErrorToSentry } = await import('./sentry.js')
const { TelemetrySafeError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS: TelemetrySafeError } =
await import('./errors.js')
await initializeSentry()
// A plain Error must never be reported — its message may contain file paths.
reportErrorToSentry(new Error('raw error with /some/file/path'))
expect(captureMessage).not.toHaveBeenCalled()
// A TelemetrySafeError reports only its sanitized telemetryMessage.
reportErrorToSentry(
new TelemetrySafeError('full message with /some/file/path', 'sanitized message'),
)
expect(captureMessage).toHaveBeenCalledWith('sanitized message', 'error')
})
await initializeSentry()
const client = Sentry.getClient()
if (!client) throw new Error('SDK was not initialized')
const transport = client.getTransport()
if (!transport) throw new Error('SDK transport was not initialized')
const events = []
// Inspect outgoing envelopes without sending any network requests.
transport.send = async envelope => {
for (const [header, event] of envelope[1]) {
if (header.type === 'event') events.push(event)
}
return { statusCode: 200 }
}
reportErrorToSentry(new Error('raw error with /some/file/path'))
reportErrorToSentry(new TelemetrySafeError('full message with /some/file/path', 'sanitized message'))
await flushSentry()
const options = client.getOptions()
console.log(JSON.stringify({ events, integrations: options.integrations.map(i => i.name), tracesSampleRate: options.tracesSampleRate }))
await Sentry.close(1000)
`], {
cwd: fileURLToPath(new URL('../../', import.meta.url)),
env: { ...process.env, SENTRY_DSN: 'https://example@o0.ingest.sentry.io/0' },
stdout: 'pipe',
stderr: 'pipe',
timeout: 10_000,
})
const [stdout, stderr, exitCode] = await Promise.all([
new Response(child.stdout).text(),
new Response(child.stderr).text(),
child.exited,
])
expect(stderr).toBe('')
expect(exitCode).toBe(0)
const result = JSON.parse(stdout)
expect(result.integrations).toEqual([])
expect(result.tracesSampleRate).toBeUndefined()
expect(result.events).toHaveLength(1)
expect(result.events[0].message).toBe('sanitized message')
expect(result.events[0].level).toBe('error')
expect(result.events[0].exception).toBeUndefined()
expect(result.events[0].stacktrace).toBeUndefined()
expect(result.events[0].user).toBeUndefined()
expect(result.events[0].request).toBeUndefined()
expect(result.events[0].server_name).toBeUndefined()
expect(result.events[0].hostname).toBeUndefined()
expect(stdout).not.toContain(hostname())
expect(stdout).not.toContain('/some/file/path')
}, 15_000)
+48 -2
View File
@@ -9,6 +9,9 @@
*/
import { isTelemetryDisabled } from './privacyLevel.js'
import { TelemetrySafeError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS as TelemetrySafeError } from './errors.js'
import { sleep } from './sleep.js'
export const SENTRY_FLUSH_TIMEOUT_MS = 2000
let sentryInitialized = false
let sentryModule: typeof import('@sentry/node') | null = null
@@ -36,7 +39,30 @@ export async function initializeSentry(): Promise<void> {
sentryModule.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV ?? 'production',
tracesSampleRate: 0,
// Leave tracing sample options unset; even a zero rate enables span recording.
// Keep v11's span streaming and runtime instrumentation out of this path.
traceLifecycle: 'static',
enableOpenTelemetrySetup: false,
enableRuntimeChannelInjection: false,
// v11 attaches synthetic stacks to messages and collects more data by
// default. Keep this reporter limited to the vetted message.
attachStacktrace: false,
// The SDK otherwise adds os.hostname() as server_name to every event.
includeServerName: false,
dataCollection: {
userInfo: false,
cookies: false,
httpHeaders: false,
httpBodies: [],
urlQueryParams: false,
graphQL: { document: false, variables: false },
genAI: { inputs: false, outputs: false },
mcp: { inputs: false, outputs: false },
databaseQueryData: false,
queues: false,
stackFrameVariables: false,
frameContextLines: 0,
},
// Disable Sentry's automatic uncaughtException/unhandledRejection
// integrations. Those hooks report raw error content, bypassing the
// TelemetrySafeError sanitization in reportErrorToSentry(). Only
@@ -69,4 +95,24 @@ export function reportErrorToSentry(error: unknown): void {
} catch {
// Reporting must never throw.
}
}
}
/** Drain opted-in reports before exit, without allowing the SDK to delay shutdown indefinitely. */
export async function flushSentry(): Promise<void> {
if (!sentryModule || !isSentryEnabled()) {
return
}
const deadline = new AbortController()
try {
// The SDK timeout does not bound every stage of its flush pipeline.
await Promise.race([
sentryModule.flush(SENTRY_FLUSH_TIMEOUT_MS),
sleep(SENTRY_FLUSH_TIMEOUT_MS, deadline.signal),
])
} catch {
// Reporting failures must never prevent process exit.
} finally {
deadline.abort()
}
}