From 4d6dddef8bdbeb64e5ebd080b4f944a0d2c62937 Mon Sep 17 00:00:00 2001 From: betegon Date: Tue, 17 Feb 2026 20:48:57 +0100 Subject: [PATCH 01/26] feat(init): add init command for guided Sentry project setup Adds `sentry init` wizard that walks users through project setup via the Mastra API, handling DSN configuration, SDK installation prompts, and local file operations. Co-Authored-By: Claude Opus 4.6 --- .gitignore | 1 + bun.lock | 438 +++++++++++++++++++++++++++++++--- package.json | 1 + src/app.ts | 2 + src/commands/init.ts | 89 +++++++ src/lib/init/constants.ts | 8 + src/lib/init/formatters.ts | 120 ++++++++++ src/lib/init/interactive.ts | 181 ++++++++++++++ src/lib/init/local-ops.ts | 267 +++++++++++++++++++++ src/lib/init/types.ts | 101 ++++++++ src/lib/init/wizard-runner.ts | 115 +++++++++ 11 files changed, 1288 insertions(+), 35 deletions(-) create mode 100644 src/commands/init.ts create mode 100644 src/lib/init/constants.ts create mode 100644 src/lib/init/formatters.ts create mode 100644 src/lib/init/interactive.ts create mode 100644 src/lib/init/local-ops.ts create mode 100644 src/lib/init/types.ts create mode 100644 src/lib/init/wizard-runner.ts diff --git a/.gitignore b/.gitignore index e9259aae..9e4b370a 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json .cache *.tsbuildinfo .turbo +.mastra # docs docs/dist diff --git a/bun.lock b/bun.lock index 2d6f6a6e..323ae911 100644 --- a/bun.lock +++ b/bun.lock @@ -3,9 +3,9 @@ "configVersion": 1, "workspaces": { "": { - "name": "sentry", "devDependencies": { "@biomejs/biome": "2.3.8", + "@mastra/client-js": "^1.4.0", "@sentry/api": "^0.1.0", "@sentry/bun": "10.39.0", "@sentry/esbuild-plugin": "^2.23.0", @@ -37,17 +37,33 @@ "@stricli/core@1.2.5": "patches/@stricli%2Fcore@1.2.5.patch", }, "packages": { + "@a2a-js/sdk": ["@a2a-js/sdk@0.2.5", "", { "dependencies": { "@types/cors": "^2.8.17", "@types/express": "^4.17.23", "body-parser": "^2.2.0", "cors": "^2.8.5", "express": "^4.21.2", "uuid": "^11.1.0" } }, "sha512-VTDuRS5V0ATbJ/LkaQlisMnTAeYKXAK6scMguVBstf+KIBQ7HIuKhiXLv+G/hvejkV+THoXzoNifInAkU81P1g=="], + + "@ai-sdk/provider": ["@ai-sdk/provider@2.0.1", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng=="], + + "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.2.8", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA=="], + + "@ai-sdk/provider-utils-v5": ["@ai-sdk/provider-utils@3.0.20", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-iXHVe0apM2zUEzauqJwqmpC37A5rihrStAih5Ks+JE32iTe4LZ58y17UGBjpQQTCRw9YxMeo2UFLxLpBluyvLQ=="], + + "@ai-sdk/provider-utils-v6": ["@ai-sdk/provider-utils@4.0.0", "", { "dependencies": { "@ai-sdk/provider": "3.0.0", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-HyCyOls9I3a3e38+gtvOJOEjuw9KRcvbBnCL5GBuSmJvS9Jh9v3fz7pRC6ha1EUo/ZH1zwvLWYXBMtic8MTguA=="], + + "@ai-sdk/provider-v5": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="], + + "@ai-sdk/provider-v6": ["@ai-sdk/provider@3.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-m9ka3ptkPQbaHHZHqDXDF9C9B5/Mav0KTdky1k2HZ3/nrW2t1AgObxIVPyGDWQNS9FXT/FS6PIoSjpcP/No8rQ=="], + + "@ai-sdk/ui-utils-v5": ["@ai-sdk/ui-utils@1.2.11", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w=="], + "@apm-js-collab/code-transformer": ["@apm-js-collab/code-transformer@0.8.2", "", {}, "sha512-YRjJjNq5KFSjDUoqu5pFUWrrsvGOxl6c3bu+uMFc9HNNptZ2rNU/TI2nLw4jnhQNtka972Ee2m3uqbvDQtPeCA=="], "@apm-js-collab/tracing-hooks": ["@apm-js-collab/tracing-hooks@0.3.1", "", { "dependencies": { "@apm-js-collab/code-transformer": "^0.8.0", "debug": "^4.4.1", "module-details-from-path": "^1.0.4" } }, "sha512-Vu1CbmPURlN5fTboVuKMoJjbO5qcq9fA5YXpskx3dXe/zTBvjODFoerw+69rVBlRLrJpwPqSDqEuJDEKIrTldw=="], - "@babel/code-frame": ["@babel/code-frame@7.28.6", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q=="], + "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], - "@babel/compat-data": ["@babel/compat-data@7.28.6", "", {}, "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg=="], + "@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="], - "@babel/core": ["@babel/core@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/template": "^7.28.6", "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw=="], + "@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="], - "@babel/generator": ["@babel/generator@7.28.6", "", { "dependencies": { "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw=="], + "@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="], @@ -65,13 +81,13 @@ "@babel/helpers": ["@babel/helpers@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="], - "@babel/parser": ["@babel/parser@7.28.6", "", { "dependencies": { "@babel/types": "^7.28.6" }, "bin": "./bin/babel-parser.js" }, "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ=="], + "@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="], "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="], - "@babel/traverse": ["@babel/traverse@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.6", "@babel/template": "^7.28.6", "@babel/types": "^7.28.6", "debug": "^4.3.1" } }, "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg=="], + "@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="], - "@babel/types": ["@babel/types@7.28.6", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg=="], + "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], "@biomejs/biome": ["@biomejs/biome@2.3.8", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.8", "@biomejs/cli-darwin-x64": "2.3.8", "@biomejs/cli-linux-arm64": "2.3.8", "@biomejs/cli-linux-arm64-musl": "2.3.8", "@biomejs/cli-linux-x64": "2.3.8", "@biomejs/cli-linux-x64-musl": "2.3.8", "@biomejs/cli-win32-arm64": "2.3.8", "@biomejs/cli-win32-x64": "2.3.8" }, "bin": { "biome": "bin/biome" } }, "sha512-Qjsgoe6FEBxWAUzwFGFrB+1+M8y/y5kwmg5CHac+GSVOdmOIqsAiXM5QMVGZJ1eCUCLlPZtq4aFAQ0eawEUuUA=="], @@ -147,9 +163,11 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], - "@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="], + "@hono/node-server": ["@hono/node-server@1.19.9", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw=="], + + "@isaacs/cliui": ["@isaacs/cliui@9.0.0", "", {}, "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg=="], - "@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="], + "@isaacs/ttlcache": ["@isaacs/ttlcache@2.1.4", "", {}, "sha512-7kMz0BJpMvgAMkyglums7B2vtrn5g0a0am77JY0GjkZZNetOBCFn7AG7gKCwT0QPiXyxW7YIQSgtARknUEOcxQ=="], "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], @@ -161,13 +179,25 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + "@lukeed/csprng": ["@lukeed/csprng@1.1.0", "", {}, "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA=="], + + "@lukeed/uuid": ["@lukeed/uuid@2.0.1", "", { "dependencies": { "@lukeed/csprng": "^1.1.0" } }, "sha512-qC72D4+CDdjGqJvkFMMEAtancHUQ7/d/tAiHf64z8MopFDmcrtbcJuerDtFceuAfQJ2pDSfCKCtbqoGBNnwg0w=="], + + "@mastra/client-js": ["@mastra/client-js@1.4.0", "", { "dependencies": { "@lukeed/uuid": "^2.0.1", "@mastra/core": "1.4.0", "@mastra/schema-compat": "1.1.0", "json-schema": "^0.4.0" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-nCRO//j7qy7ZQwbdFdFgDce493caX0o9makNIvMuSjCBRJwdxj2k1YP10nR24OsG7sLvnoZLKYxgu5zt5n4vFw=="], + + "@mastra/core": ["@mastra/core@1.4.0", "", { "dependencies": { "@a2a-js/sdk": "~0.2.4", "@ai-sdk/provider-utils-v5": "npm:@ai-sdk/provider-utils@3.0.20", "@ai-sdk/provider-utils-v6": "npm:@ai-sdk/provider-utils@4.0.0", "@ai-sdk/provider-v5": "npm:@ai-sdk/provider@2.0.0", "@ai-sdk/provider-v6": "npm:@ai-sdk/provider@3.0.0", "@ai-sdk/ui-utils-v5": "npm:@ai-sdk/ui-utils@1.2.11", "@isaacs/ttlcache": "^2.1.4", "@lukeed/uuid": "^2.0.1", "@mastra/schema-compat": "1.1.0", "@modelcontextprotocol/sdk": "^1.17.5", "@sindresorhus/slugify": "^2.2.1", "dotenv": "^17.2.3", "gray-matter": "^4.0.3", "hono": "^4.11.3", "hono-openapi": "^1.1.1", "js-tiktoken": "^1.0.21", "json-schema": "^0.4.0", "lru-cache": "^11.2.2", "p-map": "^7.0.3", "p-retry": "^7.1.0", "radash": "^12.1.1", "xxhash-wasm": "^1.1.0" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-dyCUFozUGXwyNLl5zRZbKFKemp4gfk+vTsrfgv9M08OlXl2AuLgc+J6Yyj9gFwBVCTgxPaKUtu3QaDOiproXrg=="], + + "@mastra/schema-compat": ["@mastra/schema-compat@1.1.0", "", { "dependencies": { "json-schema-to-zod": "^2.7.0", "zod-from-json-schema": "^0.5.0", "zod-from-json-schema-v3": "npm:zod-from-json-schema@^0.0.5", "zod-to-json-schema": "^3.24.6" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-2v8nTaAC/279jHs0ux2Emp+lNgBFq3QeNbZCGSHFeeBhbqqM5aWJCPY2Xgw8Z/dY3mTpFxBbmXQz3oRyYStnqg=="], + + "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="], + "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], "@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.211.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg=="], - "@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@2.5.0", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-uOXpVX0ZjO7heSVjhheW2XEPrhQAWr2BScDPoZ9UDycl5iuHG+Usyc3AIfG6kZeC1GyLpMInpQ6X5+9n69yOFw=="], + "@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@2.5.1", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-MHbu8XxCHcBn6RwvCt2Vpn1WnLMNECfNKYB14LI5XypcgH4IE0/DiVifVR9tAkwPMyLXN8dOoPJfya3IryLQVw=="], - "@opentelemetry/core": ["@opentelemetry/core@2.5.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ=="], + "@opentelemetry/core": ["@opentelemetry/core@2.5.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-Dwlc+3HAZqpgTYq0MUyZABjFkcrKTePwuiFVLjahGD8cx3enqihmpAmdgNFO1R4m/sIe5afjJrA25Prqy4NXlA=="], "@opentelemetry/instrumentation": ["@opentelemetry/instrumentation@0.211.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.211.0", "import-in-the-middle": "^2.0.0", "require-in-the-middle": "^8.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q=="], @@ -217,9 +247,9 @@ "@opentelemetry/redis-common": ["@opentelemetry/redis-common@0.38.2", "", {}, "sha512-1BCcU93iwSRZvDAgwUxC/DV4T/406SkMfxGqu5ojc3AvNI+I9GhV7v0J1HljsczuuhcnFLYqD5VmwVXfCGHzxA=="], - "@opentelemetry/resources": ["@opentelemetry/resources@2.5.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-F8W52ApePshpoSrfsSk1H2yJn9aKjCrbpQF1M9Qii0GHzbfVeFUB+rc3X4aggyZD8x9Gu3Slua+s6krmq6Dt8g=="], + "@opentelemetry/resources": ["@opentelemetry/resources@2.5.1", "", { "dependencies": { "@opentelemetry/core": "2.5.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-BViBCdE/GuXRlp9k7nS1w6wJvY5fnFX5XvuEtWsTAOQFIO89Eru7lGW3WbfbxtCuZ/GbrJfAziXG0w0dpxL7eQ=="], - "@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.5.0", "", { "dependencies": { "@opentelemetry/core": "2.5.0", "@opentelemetry/resources": "2.5.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-VzRf8LzotASEyNDUxTdaJ9IRJ1/h692WyArDBInf5puLCjxbICD6XkHgpuudis56EndyS7LYFmtTMny6UABNdQ=="], + "@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.5.1", "", { "dependencies": { "@opentelemetry/core": "2.5.1", "@opentelemetry/resources": "2.5.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-iZH3Gw8cxQn0gjpOjJMmKLd9GIaNh/E3v3ST67vyzLSxHBs14HsG4dy7jMYyC5WXGdBVEcM7U/XTF5hCQxjDMw=="], "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.39.0", "", {}, "sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg=="], @@ -261,19 +291,43 @@ "@sentry/opentelemetry": ["@sentry/opentelemetry@10.39.0", "", { "dependencies": { "@sentry/core": "10.39.0" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.1.0", "@opentelemetry/core": "^1.30.1 || ^2.1.0", "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0", "@opentelemetry/semantic-conventions": "^1.39.0" } }, "sha512-eU8t/pyxjy7xYt6PNCVxT+8SJw5E3pnupdcUNN4ClqG4O5lX4QCDLtId48ki7i30VqrLtR7vmCHMSvqXXdvXPA=="], + "@sindresorhus/slugify": ["@sindresorhus/slugify@2.2.1", "", { "dependencies": { "@sindresorhus/transliterate": "^1.0.0", "escape-string-regexp": "^5.0.0" } }, "sha512-MkngSCRZ8JdSOCHRaYd+D01XhvU3Hjy6MGl06zhOk614hp9EOAp5gIkBeQg7wtmxpitU6eAL4kdiRMcJa2dlrw=="], + + "@sindresorhus/transliterate": ["@sindresorhus/transliterate@1.6.0", "", { "dependencies": { "escape-string-regexp": "^5.0.0" } }, "sha512-doH1gimEu3A46VX6aVxpHTeHrytJAG6HgdxntYnCFiIFHEM/ZGpG8KiZGBChchjQmG0XFIBL552kBTjVcMZXwQ=="], + + "@standard-community/standard-json": ["@standard-community/standard-json@0.3.5", "", { "peerDependencies": { "@standard-schema/spec": "^1.0.0", "@types/json-schema": "^7.0.15", "@valibot/to-json-schema": "^1.3.0", "arktype": "^2.1.20", "effect": "^3.16.8", "quansync": "^0.2.11", "sury": "^10.0.0", "typebox": "^1.0.17", "valibot": "^1.1.0", "zod": "^3.25.0 || ^4.0.0", "zod-to-json-schema": "^3.24.5" }, "optionalPeers": ["@valibot/to-json-schema", "arktype", "effect", "sury", "typebox", "valibot", "zod", "zod-to-json-schema"] }, "sha512-4+ZPorwDRt47i+O7RjyuaxHRK/37QY/LmgxlGrRrSTLYoFatEOzvqIc85GTlM18SFZ5E91C+v0o/M37wZPpUHA=="], + + "@standard-community/standard-openapi": ["@standard-community/standard-openapi@0.2.9", "", { "peerDependencies": { "@standard-community/standard-json": "^0.3.5", "@standard-schema/spec": "^1.0.0", "arktype": "^2.1.20", "effect": "^3.17.14", "openapi-types": "^12.1.3", "sury": "^10.0.0", "typebox": "^1.0.0", "valibot": "^1.1.0", "zod": "^3.25.0 || ^4.0.0", "zod-openapi": "^4" }, "optionalPeers": ["arktype", "effect", "sury", "typebox", "valibot", "zod", "zod-openapi"] }, "sha512-htj+yldvN1XncyZi4rehbf9kLbu8os2Ke/rfqoZHCMHuw34kiF3LP/yQPdA0tQ940y8nDq3Iou8R3wG+AGGyvg=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + "@stricli/auto-complete": ["@stricli/auto-complete@1.2.5", "", { "dependencies": { "@stricli/core": "^1.2.5" }, "bin": { "auto-complete": "dist/bin/cli.js" } }, "sha512-C6G88Hh4lUWBwiqsxbcA4I1ricSQwiLaOziTWW3NmBoX7WGTW7i7RvyooXMpZk1YMLf2olv5Odxmg127ik1DKQ=="], "@stricli/core": ["@stricli/core@1.2.5", "", {}, "sha512-+afyztQW7fwWkqmU2WQZbdc3LjnZThWYdtE0l+hykZ1Rvy7YGxZSvsVCS/wZ/2BNv117pQ9TU1GZZRIcPnB4tw=="], - "@trpc/server": ["@trpc/server@11.8.1", "", { "peerDependencies": { "typescript": ">=5.7.2" } }, "sha512-P4rzZRpEL7zDFgjxK65IdyH0e41FMFfTkQkuq0BA5tKcr7E6v9/v38DEklCpoDN6sPiB1Sigy/PUEzHENhswDA=="], + "@trpc/server": ["@trpc/server@11.10.0", "", { "peerDependencies": { "typescript": ">=5.7.2" } }, "sha512-zZjTrR6He61e5TiT7e/bQqab/jRcXBZM8Fg78Yoo8uh5pz60dzzbYuONNUCOkafv5ppXVMms4NHYfNZgzw50vg=="], + + "@types/body-parser": ["@types/body-parser@1.19.6", "", { "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g=="], - "@types/bun": ["@types/bun@1.3.6", "", { "dependencies": { "bun-types": "1.3.6" } }, "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA=="], + "@types/bun": ["@types/bun@1.3.9", "", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="], "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], + "@types/cors": ["@types/cors@2.8.19", "", { "dependencies": { "@types/node": "*" } }, "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg=="], + + "@types/express": ["@types/express@4.17.25", "", { "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", "@types/serve-static": "^1" } }, "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw=="], + + "@types/express-serve-static-core": ["@types/express-serve-static-core@4.19.8", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA=="], + + "@types/http-errors": ["@types/http-errors@2.0.5", "", {}, "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/mime": ["@types/mime@1.3.5", "", {}, "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="], + "@types/mysql": ["@types/mysql@2.15.27", "", { "dependencies": { "@types/node": "*" } }, "sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA=="], - "@types/node": ["@types/node@22.19.7", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw=="], + "@types/node": ["@types/node@22.19.11", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w=="], "@types/pg": ["@types/pg@8.15.6", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ=="], @@ -281,110 +335,260 @@ "@types/qrcode-terminal": ["@types/qrcode-terminal@0.12.2", "", {}, "sha512-v+RcIEJ+Uhd6ygSQ0u5YYY7ZM+la7GgPbs0V/7l/kFs2uO4S8BcIUEMoP7za4DNIqNnUD5npf0A/7kBhrCKG5Q=="], + "@types/qs": ["@types/qs@6.14.0", "", {}, "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ=="], + + "@types/range-parser": ["@types/range-parser@1.2.7", "", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="], + "@types/semver": ["@types/semver@7.7.1", "", {}, "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA=="], + "@types/send": ["@types/send@0.17.6", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og=="], + + "@types/serve-static": ["@types/serve-static@1.15.10", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "<1" } }, "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw=="], + "@types/tedious": ["@types/tedious@4.0.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw=="], + "accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="], + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], "acorn-import-attributes": ["acorn-import-attributes@1.9.5", "", { "peerDependencies": { "acorn": "^8" } }, "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ=="], "agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], + "ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], + + "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], + "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], + "argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + + "array-flatten": ["array-flatten@1.1.1", "", {}, "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="], + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - "baseline-browser-mapping": ["baseline-browser-mapping@2.9.18", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-e23vBV1ZLfjb9apvfPk4rHVu2ry6RIr2Wfs+O324okSidrX7pTAnEJPCh/O5BtRlr7QtZI7ktOP3vsqr7Z5XoA=="], + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + + "baseline-browser-mapping": ["baseline-browser-mapping@2.9.19", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg=="], "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], "binpunch": ["binpunch@1.0.0", "", { "bin": { "binpunch": "dist/cli.js" } }, "sha512-ghxdoerLN3WN64kteDJuL4d9dy7gbvcqoADNRWBk6aQ5FrYH1EmPmREAdcdIdTNAA3uW3V38Env5OqH2lj+i+g=="], + "body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="], + "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], - "bun-types": ["bun-types@1.3.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ=="], + "bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="], + + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], - "caniuse-lite": ["caniuse-lite@1.0.30001766", "", {}, "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA=="], + "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=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "caniuse-lite": ["caniuse-lite@1.0.30001770", "", {}, "sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw=="], "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], "chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], - "citty": ["citty@0.2.0", "", {}, "sha512-8csy5IBFI2ex2hTVpaHN2j+LNE199AgiI7y4dMintrr8i0lQiFn+0AWMZrWdHKIgMOer65f8IThysYhoReqjWA=="], + "citty": ["citty@0.2.1", "", {}, "sha512-kEV95lFBhQgtogAPlQfJJ0WGVSokvLr/UEoFPiKKOXF7pl98HfUVUD0ejsuTCld/9xH9vogSywZ5KqHzXrZpqg=="], "cjs-module-lexer": ["cjs-module-lexer@2.2.0", "", {}, "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ=="], - "commander": ["commander@14.0.2", "", {}, "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ=="], + "commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], + + "content-disposition": ["content-disposition@0.5.4", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ=="], + + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + + "cookie-signature": ["cookie-signature@1.0.7", "", {}, "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA=="], + + "cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], - "dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], - "electron-to-chromium": ["electron-to-chromium@1.5.278", "", {}, "sha512-dQ0tM1svDRQOwxnXxm+twlGTjr9Upvt8UFWAgmLsxEzFQxhbti4VwxmMjsDxVC51Zo84swW7FVCXEV+VAkhuPw=="], + "destroy": ["destroy@1.2.0", "", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="], + + "dotenv": ["dotenv@17.3.1", "", {}, "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + + "electron-to-chromium": ["electron-to-chromium@1.5.286", "", {}, "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A=="], + + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + + "escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + + "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], + + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + + "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], + + "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="], + + "express": ["express@4.22.1", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "~1.20.3", "content-disposition": "~0.5.4", "content-type": "~1.0.4", "cookie": "~0.7.1", "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "~1.3.1", "fresh": "~0.5.2", "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", "qs": "~6.14.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", "serve-static": "~1.16.2", "setprototypeof": "1.2.0", "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g=="], + + "express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + + "extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="], + "fast-check": ["fast-check@4.5.3", "", { "dependencies": { "pure-rand": "^7.0.0" } }, "sha512-IE9csY7lnhxBnA8g/WI5eg/hygA6MGWJMSNfFRrBlXUciADEhS1EDB0SIsMSvzubzIlOBbVITSsypCsW717poA=="], + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + "finalhandler": ["finalhandler@1.3.2", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "statuses": "~2.0.2", "unpipe": "~1.0.0" } }, "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg=="], + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], + "forwarded-parse": ["forwarded-parse@2.1.2", "", {}, "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw=="], + "fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="], + "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], - "glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="], + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "glob": ["glob@13.0.4", "", { "dependencies": { "minimatch": "^10.2.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-KACie1EOs9BIOMtenFaxwmYODWA3/fTfGSUnLhMJpXRntu1g+uL/Xvub5f8SCTppvo9q62Qy4LeOoUiaL54G5A=="], "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "gray-matter": ["gray-matter@4.0.3", "", { "dependencies": { "js-yaml": "^3.13.1", "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" } }, "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "hono": ["hono@4.11.9", "", {}, "sha512-Eaw2YTGM6WOxA6CXbckaEvslr2Ne4NFsKrvc0v97JD5awbmeBLO5w9Ho9L9kmKonrwF9RJlW6BxT1PVv/agBHQ=="], + + "hono-openapi": ["hono-openapi@1.2.0", "", { "peerDependencies": { "@hono/standard-validator": "^0.2.0", "@standard-community/standard-json": "^0.3.5", "@standard-community/standard-openapi": "^0.2.9", "@types/json-schema": "^7.0.15", "hono": "^4.8.3", "openapi-types": "^12.1.3" }, "optionalPeers": ["@hono/standard-validator", "hono"] }, "sha512-t3u4v8YCltExDl4d9cLqg/mcrYFSs9Gjb5puF1CePPrvv1JQOo1Kc50HAmGt47CWHIoc/W8Q9LY3t3yqU0dxFw=="], + + "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=="], + "https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], + "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], + "ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], "import-in-the-middle": ["import-in-the-middle@2.0.6", "", { "dependencies": { "acorn": "^8.15.0", "acorn-import-attributes": "^1.9.5", "cjs-module-lexer": "^2.2.0", "module-details-from-path": "^1.0.4" } }, "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw=="], + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + + "ip-address": ["ip-address@10.0.1", "", {}, "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA=="], + + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="], + "is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="], + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + "is-network-error": ["is-network-error@1.3.0", "", {}, "sha512-6oIwpsgRfnDiyEDLMay/GqCl3HoAtH5+RUKW29gYkL0QA+ipzpDLA16yQs7/RHCSu+BwgbJaOUqa4A99qNVQVw=="], + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + "jackspeak": ["jackspeak@4.2.3", "", { "dependencies": { "@isaacs/cliui": "^9.0.0" } }, "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg=="], + + "jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="], + + "js-tiktoken": ["js-tiktoken@1.0.21", "", { "dependencies": { "base64-js": "^1.5.1" } }, "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g=="], + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + "js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="], + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], + + "json-schema-to-zod": ["json-schema-to-zod@2.7.0", "", { "bin": { "json-schema-to-zod": "dist/cjs/cli.js" } }, "sha512-eW59l3NQ6sa3HcB+Ahf7pP6iGU7MY4we5JsPqXQ2ZcIPF8QxSg/lkY8lN0Js/AG0NjMbk+nZGUfHlceiHF+bwQ=="], + + "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="], + "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], "jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="], + "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="], + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], - "lru-cache": ["lru-cache@11.2.4", "", {}, "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg=="], + "lru-cache": ["lru-cache@11.2.6", "", {}, "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ=="], "magic-string": ["magic-string@0.30.8", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ=="], + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], + + "merge-descriptors": ["merge-descriptors@1.0.3", "", {}, "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ=="], + + "methods": ["methods@1.1.2", "", {}, "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="], + + "mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="], + + "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@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], @@ -393,24 +597,48 @@ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + + "negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], + "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], - "nypm": ["nypm@0.6.4", "", { "dependencies": { "citty": "^0.2.0", "pathe": "^2.0.3", "tinyexec": "^1.0.2" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-1TvCKjZyyklN+JJj2TS3P4uSQEInrM/HkkuSXsEzm1ApPgBffOn8gFguNnZf07r/1X6vlryfIqMUkJKQMzlZiw=="], + "nypm": ["nypm@0.6.5", "", { "dependencies": { "citty": "^0.2.0", "pathe": "^2.0.3", "tinyexec": "^1.0.2" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], - "p-limit": ["p-limit@7.2.0", "", { "dependencies": { "yocto-queue": "^1.2.1" } }, "sha512-ATHLtwoTNDloHRFFxFJdHnG6n2WUeFjaR8XQMFdKIv0xkXjrER8/iG9iu265jOM95zXHAfv9oTkqhrfbIzosrQ=="], + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], + + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + + "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="], + + "p-limit": ["p-limit@7.3.0", "", { "dependencies": { "yocto-queue": "^1.2.1" } }, "sha512-7cIXg/Z0M5WZRblrsOla88S4wAK+zOQQWeBYfV3qJuJXMr+LnbYjaadrFaS0JILfEDPVqHyKnZ1Z/1d6J9VVUw=="], "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + "p-map": ["p-map@7.0.4", "", {}, "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ=="], + + "p-retry": ["p-retry@7.1.1", "", { "dependencies": { "is-network-error": "^1.1.0" } }, "sha512-J5ApzjyRkkf601HpEeykoiCvzHQjWxPAHhyjFcEUP2SWq0+35NKh8TLhpLw+Dkq5TZBFvUM6UigdE9hIVYTl5w=="], + "parse-ms": ["parse-ms@4.0.0", "", {}, "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw=="], + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + "path-scurry": ["path-scurry@2.0.1", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA=="], + "path-to-regexp": ["path-to-regexp@0.1.12", "", {}, "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="], + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], "pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="], @@ -423,6 +651,8 @@ "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + "pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="], + "postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="], "postgres-bytea": ["postgres-bytea@1.0.1", "", {}, "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ=="], @@ -435,47 +665,105 @@ "progress": ["progress@2.0.3", "", {}, "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="], + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], + "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], "pure-rand": ["pure-rand@7.0.1", "", {}, "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ=="], "qrcode-terminal": ["qrcode-terminal@0.12.0", "", { "bin": { "qrcode-terminal": "./bin/qrcode-terminal.js" } }, "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ=="], + "qs": ["qs@6.15.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ=="], + + "quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="], + + "radash": ["radash@12.1.1", "", {}, "sha512-h36JMxKRqrAxVD8201FrCpyeNuUY9Y5zZwujr20fFO77tpUtGa6EZzfKw/3WaiBX95fq7+MpsuMLNdSnORAwSA=="], + + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], + + "raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], + "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], + "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=="], - "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + "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=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "section-matter": ["section-matter@1.0.0", "", { "dependencies": { "extend-shallow": "^2.0.1", "kind-of": "^6.0.0" } }, "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA=="], + + "secure-json-parse": ["secure-json-parse@2.7.0", "", {}, "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw=="], + + "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + + "send": ["send@0.19.2", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "~0.5.2", "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "~2.4.1", "range-parser": "~1.2.1", "statuses": "~2.0.2" } }, "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg=="], + + "serve-static": ["serve-static@1.16.3", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "~0.19.1" } }, "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA=="], + + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + + "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], + "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], + + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + + "strip-bom-string": ["strip-bom-string@1.0.0", "", {}, "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g=="], + "tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="], "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], "trpc-cli": ["trpc-cli@0.12.2", "", { "dependencies": { "commander": "^14.0.0" }, "peerDependencies": { "@orpc/server": "^1.0.0", "@trpc/server": "^10.45.2 || ^11.0.1", "@valibot/to-json-schema": "^1.1.0", "effect": "^3.14.2 || ^4.0.0", "valibot": "^1.1.0", "zod": "^3.24.0 || ^4.0.0" }, "optionalPeers": ["@orpc/server", "@trpc/server", "@valibot/to-json-schema", "effect", "valibot", "zod"], "bin": { "trpc-cli": "dist/bin.js" } }, "sha512-kGNCiyOimGlfcZFImbWzFF2Nn3TMnenwUdyuckiN5SEaceJbIac7+Iau3WsVHjQpoNgugFruZMDOKf8GNQNtJw=="], + "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "ultracite": ["ultracite@6.3.10", "", { "dependencies": { "@clack/prompts": "^0.11.0", "@trpc/server": "^11.7.2", "deepmerge": "^4.3.1", "glob": "^13.0.0", "jsonc-parser": "^3.3.1", "nypm": "^0.6.2", "trpc-cli": "^0.12.1", "zod": "^4.1.13" }, "bin": { "ultracite": "dist/index.js" } }, "sha512-I41KoWl09PklvXTdN4JWgs+6Z6n5PERDJGj1hOQXYEMbmKXZLrulG2QAZNEMJ9pdGwtcGk/MevpllWYXM5Wq3A=="], "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + "unplugin": ["unplugin@1.0.1", "", { "dependencies": { "acorn": "^8.8.1", "chokidar": "^3.5.3", "webpack-sources": "^3.2.3", "webpack-virtual-modules": "^0.5.0" } }, "sha512-aqrHaVBWW1JVKBHmGo33T5TxeL0qWzfvjWokObHA9bYmN7eNDkwOxmLjhioHl9878qDFMAaT51XNroRyuz7WxA=="], "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], + "utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="], + "uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], "uuidv7": ["uuidv7@1.1.0", "", { "bin": { "uuidv7": "cli.js" } }, "sha512-2VNnOC0+XQlwogChUDzy6pe8GQEys9QFZBGOh54l6qVfwoCUwwRvk7rDTgaIsRgsF5GFa5oiNg8LqXE3jofBBg=="], + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], - "webpack-sources": ["webpack-sources@3.3.3", "", {}, "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg=="], + "webpack-sources": ["webpack-sources@3.3.4", "", {}, "sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q=="], "webpack-virtual-modules": ["webpack-virtual-modules@0.5.0", "", {}, "sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw=="], @@ -483,39 +771,95 @@ "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], + "xxhash-wasm": ["xxhash-wasm@1.1.0", "", {}, "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA=="], + "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], "yocto-queue": ["yocto-queue@1.2.2", "", {}, "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ=="], "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "zod-from-json-schema": ["zod-from-json-schema@0.5.2", "", { "dependencies": { "zod": "^4.0.17" } }, "sha512-/dNaicfdhJTOuUd4RImbLUE2g5yrSzzDjI/S6C2vO2ecAGZzn9UcRVgtyLSnENSmAOBRiSpUdzDS6fDWX3Z35g=="], + + "zod-from-json-schema-v3": ["zod-from-json-schema@0.0.5", "", { "dependencies": { "zod": "^3.24.2" } }, "sha512-zYEoo86M1qpA1Pq6329oSyHLS785z/mTwfr9V1Xf/ZLhuuBGaMlDGu/pDVGVUe4H4oa1EFgWZT53DP0U3oT9CQ=="], + + "zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="], + + "@a2a-js/sdk/uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="], + + "@ai-sdk/provider-utils/@ai-sdk/provider": ["@ai-sdk/provider@1.1.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg=="], + + "@ai-sdk/provider-utils-v6/@ai-sdk/provider": ["@ai-sdk/provider@3.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-m9ka3ptkPQbaHHZHqDXDF9C9B5/Mav0KTdky1k2HZ3/nrW2t1AgObxIVPyGDWQNS9FXT/FS6PIoSjpcP/No8rQ=="], + + "@ai-sdk/ui-utils-v5/@ai-sdk/provider": ["@ai-sdk/provider@1.1.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg=="], + "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "@modelcontextprotocol/sdk/express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="], + + "@modelcontextprotocol/sdk/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + + "@opentelemetry/instrumentation-http/@opentelemetry/core": ["@opentelemetry/core@2.5.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ=="], + "@prisma/instrumentation/@opentelemetry/instrumentation": ["@opentelemetry/instrumentation@0.207.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.207.0", "import-in-the-middle": "^2.0.0", "require-in-the-middle": "^8.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-y6eeli9+TLKnznrR8AZlQMSJT7wILpXH+6EYq5Vf/4Ao+huI7EedxQHwRgVUOMLFbe7VFDvHJrX9/f4lcwnJsA=="], + "@sentry/bundler-plugin-core/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], + "@sentry/bundler-plugin-core/glob": ["glob@9.3.5", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="], + "accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "bun-types/@types/node": ["@types/node@20.19.30", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g=="], + "express/body-parser": ["body-parser@1.20.4", "", { "dependencies": { "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "~1.2.0", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", "qs": "~6.14.0", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" } }, "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA=="], - "glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="], + "express/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + + "express/qs": ["qs@6.14.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q=="], + + "express/type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="], + + "finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + + "glob/minimatch": ["minimatch@10.2.1", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-MClCe8IL5nRRmawL6ib/eT4oLyeKMGCghibcDWK+J0hh0Q8kqSdia6BvbRMVk6mPa6WqUa5uR2oxt6C5jd533A=="], "p-locate/p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], "readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "ultracite/zod": ["zod@4.3.5", "", {}, "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g=="], + "router/path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="], - "@prisma/instrumentation/@opentelemetry/instrumentation/@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.207.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-lAb0jQRVyleQQGiuuvCOTDVspc14nx6XJjP4FspJ1sNARo3Regq4ZZbrc3rN4b1TYSuUCvgH+UXUPug4SLOqEQ=="], + "send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], - "@prisma/instrumentation/@opentelemetry/instrumentation/import-in-the-middle": ["import-in-the-middle@2.0.5", "", { "dependencies": { "acorn": "^8.15.0", "acorn-import-attributes": "^1.9.5", "cjs-module-lexer": "^2.2.0", "module-details-from-path": "^1.0.4" } }, "sha512-0InH9/4oDCBRzWXhpOqusspLBrVfK1vPvbn9Wxl8DAQ8yyx5fWJRETICSwkiAMaYntjJAMBP1R4B6cQnEUYVEA=="], + "ultracite/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + + "zod-from-json-schema/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + + "@modelcontextprotocol/sdk/express/accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], + + "@modelcontextprotocol/sdk/express/content-disposition": ["content-disposition@1.0.1", "", {}, "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q=="], + + "@modelcontextprotocol/sdk/express/cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], + + "@modelcontextprotocol/sdk/express/finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="], + + "@modelcontextprotocol/sdk/express/fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], + + "@modelcontextprotocol/sdk/express/merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], + + "@modelcontextprotocol/sdk/express/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=="], + + "@modelcontextprotocol/sdk/express/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=="], + + "@prisma/instrumentation/@opentelemetry/instrumentation/@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.207.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-lAb0jQRVyleQQGiuuvCOTDVspc14nx6XJjP4FspJ1sNARo3Regq4ZZbrc3rN4b1TYSuUCvgH+UXUPug4SLOqEQ=="], "@sentry/bundler-plugin-core/glob/minimatch": ["minimatch@8.0.4", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA=="], @@ -523,10 +867,34 @@ "@sentry/bundler-plugin-core/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + "accepts/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "express/body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], + + "express/body-parser/raw-body": ["raw-body@2.5.3", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "unpipe": "~1.0.0" } }, "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA=="], + + "express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + + "express/type-is/media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="], + + "express/type-is/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + + "glob/minimatch/brace-expansion": ["brace-expansion@5.0.2", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw=="], + "p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + "send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + + "@modelcontextprotocol/sdk/express/accepts/negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + "@sentry/bundler-plugin-core/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], "@sentry/bundler-plugin-core/glob/path-scurry/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + + "express/type-is/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.2", "", { "dependencies": { "jackspeak": "^4.2.3" } }, "sha512-x0K50QvKQ97fdEz2kPehIerj+YTeptKF9hyYkKf6egnwmMWAkADiO0QCzSp0R5xN8FTZgYaBfSaue46Ej62nMg=="], } } diff --git a/package.json b/package.json index 17be1a23..fd680571 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ }, "devDependencies": { "@biomejs/biome": "2.3.8", + "@mastra/client-js": "^1.4.0", "@sentry/api": "^0.1.0", "@sentry/bun": "10.39.0", "@sentry/esbuild-plugin": "^2.23.0", diff --git a/src/app.ts b/src/app.ts index 91d2a302..75895828 100644 --- a/src/app.ts +++ b/src/app.ts @@ -11,6 +11,7 @@ import { authRoute } from "./commands/auth/index.js"; import { cliRoute } from "./commands/cli/index.js"; import { eventRoute } from "./commands/event/index.js"; import { helpCommand } from "./commands/help.js"; +import { initCommand } from "./commands/init.js"; import { issueRoute } from "./commands/issue/index.js"; import { listCommand as issueListCommand } from "./commands/issue/list.js"; import { logRoute } from "./commands/log/index.js"; @@ -43,6 +44,7 @@ export const routes = buildRouteMap({ event: eventRoute, log: logRoute, trace: traceRoute, + init: initCommand, api: apiCommand, issues: issueListCommand, orgs: orgListCommand, diff --git a/src/commands/init.ts b/src/commands/init.ts new file mode 100644 index 00000000..022b2b87 --- /dev/null +++ b/src/commands/init.ts @@ -0,0 +1,89 @@ +/** + * sentry init + * + * Initialize Sentry in a project using the remote wizard workflow. + * Communicates with the Mastra API via suspend/resume to perform + * local filesystem operations and interactive prompts. + */ + +import path from "node:path"; +import type { SentryContext } from "../context.js"; +import { buildCommand } from "../lib/command.js"; +import { runWizard } from "../lib/init/wizard-runner.js"; + +type InitFlags = { + readonly force: boolean; + readonly yes: boolean; + readonly "dry-run": boolean; + readonly features?: string; +}; + +export const initCommand = buildCommand({ + docs: { + brief: "Initialize Sentry in your project", + fullDescription: + "Runs the Sentry setup wizard to detect your project's framework, " + + "install the SDK, and configure error monitoring. Uses a remote " + + "workflow that coordinates local file operations through the CLI.", + }, + parameters: { + positional: { + kind: "tuple", + parameters: [ + { + placeholder: "directory", + brief: "Project directory (default: current directory)", + parse: String, + optional: true, + }, + ], + }, + flags: { + force: { + kind: "boolean", + brief: "Continue even if Sentry is already installed", + default: false, + }, + yes: { + kind: "boolean", + brief: "Non-interactive mode (accept defaults)", + default: false, + }, + "dry-run": { + kind: "boolean", + brief: "Preview changes without applying them", + default: false, + }, + features: { + kind: "parsed", + parse: String, + brief: "Comma-separated features: errors,tracing,logs,replay,metrics", + optional: true, + placeholder: "list", + }, + }, + aliases: { + y: "yes", + }, + }, + async func(this: SentryContext, flags: InitFlags, directory?: string) { + const targetDir = directory + ? path.resolve(this.cwd, directory) + : this.cwd; + const featuresList = flags.features + ?.split(",") + .map((f) => f.trim()) + .filter(Boolean); + + await runWizard({ + directory: targetDir, + force: flags.force, + yes: flags.yes, + dryRun: flags["dry-run"], + features: featuresList, + stdout: this.stdout, + stderr: this.stderr, + stdin: this.stdin, + }); + }, +}); diff --git a/src/lib/init/constants.ts b/src/lib/init/constants.ts new file mode 100644 index 00000000..842a9416 --- /dev/null +++ b/src/lib/init/constants.ts @@ -0,0 +1,8 @@ +export const MASTRA_API_URL = + process.env.SENTRY_WIZARD_API_URL ?? "http://localhost:4111"; + +export const WORKFLOW_ID = "sentry-wizard"; + +export const MAX_FILE_BYTES = 262144; // 256KB per file +export const MAX_STDOUT_BYTES = 65536; // 64KB stdout/stderr truncation +export const DEFAULT_COMMAND_TIMEOUT_MS = 120000; // 2 minutes diff --git a/src/lib/init/formatters.ts b/src/lib/init/formatters.ts new file mode 100644 index 00000000..fa004c99 --- /dev/null +++ b/src/lib/init/formatters.ts @@ -0,0 +1,120 @@ +/** + * Output Formatters + * + * Format wizard progress, results, and errors for terminal display. + */ + +import type { Writer } from "../../types/index.js"; + +const STEP_LABELS: Record = { + "discover-context": "Analyzing project structure", + "select-target-app": "Selecting target application", + "resolve-dir": "Resolving project directory", + "check-existing-sentry": "Checking for existing Sentry installation", + "detect-platform": "Detecting platform and framework", + "ensure-sentry-project": "Setting up Sentry project", + "select-features": "Selecting features", + "determine-pm": "Detecting package manager", + "install-deps": "Installing dependencies", + "plan-codemods": "Planning code modifications", + "apply-codemods": "Applying code modifications", + "verify-changes": "Verifying changes", + "add-example-trigger": "Example error trigger", + "open-sentry-ui": "Finishing up", +}; + +export function formatProgress( + stdout: Writer, + stepId: string, + payload?: unknown, +): void { + const label = STEP_LABELS[stepId] ?? stepId; + const payloadType = (payload as any)?.type as string | undefined; + const operation = (payload as any)?.operation as string | undefined; + + let detail = ""; + if (payloadType === "local-op" && operation) { + detail = ` (${operation})`; + } + + stdout.write(`> ${label}${detail}...\n`); +} + +export function formatResult( + stdout: Writer, + result: Record, +): void { + const output = result.result ?? result; + + stdout.write("\nSentry SDK installed successfully!\n\n"); + + if (output.platform) { + stdout.write(` Platform: ${output.platform}\n`); + } + if (output.projectDir) { + stdout.write(` Directory: ${output.projectDir}\n`); + } + if (output.features?.length) { + stdout.write(` Features: ${output.features.join(", ")}\n`); + } + if (output.commands?.length) { + stdout.write(` Commands: ${output.commands.join("; ")}\n`); + } + if (output.sentryProjectUrl) { + stdout.write(` Project: ${output.sentryProjectUrl}\n`); + } + if (output.docsUrl) { + stdout.write(` Docs: ${output.docsUrl}\n`); + } + + if (output.changedFiles?.length) { + stdout.write("\n Changed files:\n"); + for (const f of output.changedFiles) { + const icon = f.action === "create" ? "+" : f.action === "delete" ? "-" : "~"; + stdout.write(` ${icon} ${f.path}\n`); + } + } + + if (output.warnings?.length) { + stdout.write("\n Warnings:\n"); + for (const w of output.warnings) { + stdout.write(` ! ${w}\n`); + } + } + + stdout.write("\n"); +} + +export function formatError( + stderr: Writer, + result: Record, +): void { + const message = + result.error ?? result.result?.message ?? "Wizard failed with an unknown error"; + const exitCode = result.result?.exitCode ?? 1; + + stderr.write(`\nError: ${message}\n`); + + // Provide actionable suggestions based on exit code + if (exitCode === 10) { + stderr.write(" Hint: Use --force to override existing Sentry installation.\n"); + } else if (exitCode === 20) { + stderr.write(" Hint: Could not detect your project's platform. Check that the directory contains a valid project.\n"); + } else if (exitCode === 30) { + const commands = result.result?.commands as string[] | undefined; + if (commands?.length) { + stderr.write(" You can install dependencies manually:\n"); + for (const cmd of commands) { + stderr.write(` $ ${cmd}\n`); + } + } + } else if (exitCode === 50) { + stderr.write(" Hint: Fix the verification issues and run 'sentry init' again.\n"); + } + + if (result.result?.docsUrl) { + stderr.write(` Docs: ${result.result.docsUrl}\n`); + } + + stderr.write("\n"); +} diff --git a/src/lib/init/interactive.ts b/src/lib/init/interactive.ts new file mode 100644 index 00000000..3a6cdcb8 --- /dev/null +++ b/src/lib/init/interactive.ts @@ -0,0 +1,181 @@ +/** + * Interactive Dispatcher + * + * Handles interactive prompts from the remote workflow. + * Supports select, multi-select, and confirm prompts. + * Respects --yes flag for non-interactive mode. + */ + +import type { WizardOptions, InteractivePayload } from "./types.js"; + +export async function handleInteractive( + payload: InteractivePayload, + options: WizardOptions, +): Promise> { + const { kind } = payload; + + switch (kind) { + case "select": + return handleSelect(payload, options); + case "multi-select": + return handleMultiSelect(payload, options); + case "confirm": + return handleConfirm(payload, options); + default: + return { cancelled: true }; + } +} + +async function handleSelect( + payload: InteractivePayload, + options: WizardOptions, +): Promise> { + const apps = (payload.apps as Array<{ name: string; path: string; framework?: string }>) ?? []; + const items = (payload.options as string[]) ?? apps.map((a) => a.name); + + if (items.length === 0) { + return { cancelled: true }; + } + + // --yes: auto-pick if exactly one option + if (options.yes) { + if (items.length === 1) { + return { selectedApp: items[0] }; + } + options.stderr.write( + "Error: --yes requires exactly one option for selection, but found " + + `${items.length}. Run interactively to choose.\n`, + ); + return { cancelled: true }; + } + + options.stdout.write(`\n${payload.prompt}\n`); + for (let i = 0; i < items.length; i++) { + const app = apps[i]; + const extra = app?.framework ? ` (${app.framework})` : ""; + options.stdout.write(` ${i + 1}. ${items[i]}${extra}\n`); + } + + const answer = await readLine(options, `Choose [1-${items.length}]: `); + const idx = Number.parseInt(answer.trim(), 10) - 1; + + if (idx >= 0 && idx < items.length) { + return { selectedApp: items[idx] }; + } + + options.stderr.write("Invalid selection.\n"); + return { cancelled: true }; +} + +async function handleMultiSelect( + payload: InteractivePayload, + options: WizardOptions, +): Promise> { + const available = + (payload.availableFeatures as string[]) ?? + (payload.options as string[]) ?? + []; + + if (available.length === 0) { + return { features: [] }; + } + + // --yes: select all available features + if (options.yes) { + return { features: available }; + } + + options.stdout.write(`\n${payload.prompt}\n`); + for (let i = 0; i < available.length; i++) { + options.stdout.write(` ${i + 1}. ${available[i]}\n`); + } + + const answer = await readLine( + options, + `Choose (comma-separated, or "all") [1-${available.length}]: `, + ); + + if (answer.trim().toLowerCase() === "all") { + return { features: available }; + } + + const indices = answer + .split(",") + .map((s) => Number.parseInt(s.trim(), 10) - 1) + .filter((i) => i >= 0 && i < available.length); + + const selected = [...new Set(indices.map((i) => available[i]))]; + return { features: selected }; +} + +async function handleConfirm( + payload: InteractivePayload, + options: WizardOptions, +): Promise> { + // --yes: auto-confirm + if (options.yes) { + // For "add example trigger" → default to true + // For "verification issues" → default to continue + if (payload.prompt.includes("example")) { + return { addExample: true }; + } + return { action: "continue" }; + } + + options.stdout.write(`\n${payload.prompt} [Y/n] `); + + const answer = await readLine(options, ""); + const confirmed = + answer.trim() === "" || + answer.trim().toLowerCase() === "y" || + answer.trim().toLowerCase() === "yes"; + + // Determine which field to set based on the prompt + if (payload.prompt.includes("example")) { + return { addExample: confirmed }; + } + return { action: confirmed ? "continue" : "stop" }; +} + +function readLine( + options: WizardOptions, + prompt: string, +): Promise { + return new Promise((resolve) => { + if (prompt) { + options.stdout.write(prompt); + } + + const { stdin } = options; + const wasRaw = stdin.isRaw; + + // Handle piped stdin (non-TTY) + if (!stdin.isTTY) { + let data = ""; + const onData = (chunk: Buffer) => { + data += chunk.toString(); + if (data.includes("\n")) { + stdin.removeListener("data", onData); + resolve(data.split("\n")[0] ?? ""); + } + }; + stdin.on("data", onData); + stdin.resume(); + return; + } + + // TTY mode: read a line + stdin.setRawMode?.(false); + stdin.resume(); + stdin.setEncoding("utf-8"); + + const onData = (chunk: string) => { + stdin.removeListener("data", onData); + stdin.pause(); + if (wasRaw !== undefined) stdin.setRawMode?.(wasRaw); + resolve(chunk.trim()); + }; + + stdin.once("data", onData); + }); +} diff --git a/src/lib/init/local-ops.ts b/src/lib/init/local-ops.ts new file mode 100644 index 00000000..16314053 --- /dev/null +++ b/src/lib/init/local-ops.ts @@ -0,0 +1,267 @@ +/** + * Local Operations Dispatcher + * + * Handles filesystem and shell operations requested by the remote workflow. + * All operations are sandboxed to the workflow's cwd directory. + */ + +import fs from "node:fs"; +import path from "node:path"; +import { spawn } from "node:child_process"; +import { + MAX_FILE_BYTES, + MAX_STDOUT_BYTES, + DEFAULT_COMMAND_TIMEOUT_MS, +} from "./constants.js"; +import type { + WizardOptions, + LocalOpPayload, + LocalOpResult, + ListDirPayload, + ReadFilesPayload, + FileExistsBatchPayload, + RunCommandsPayload, + ApplyPatchsetPayload, +} from "./types.js"; + +/** + * Resolve a path relative to cwd and verify it's inside cwd. + * Rejects path traversal attempts. + */ +function safePath(cwd: string, relative: string): string { + const resolved = path.resolve(cwd, relative); + const normalizedCwd = path.resolve(cwd); + if (!resolved.startsWith(normalizedCwd + path.sep) && resolved !== normalizedCwd) { + throw new Error(`Path "${relative}" resolves outside project directory`); + } + return resolved; +} + +export async function handleLocalOp( + payload: LocalOpPayload, + _options: WizardOptions, +): Promise { + try { + switch (payload.operation) { + case "list-dir": + return await listDir(payload); + case "read-files": + return await readFiles(payload); + case "file-exists-batch": + return await fileExistsBatch(payload); + case "run-commands": + return await runCommands(payload); + case "apply-patchset": + return await applyPatchset(payload); + default: + return { ok: false, error: `Unknown operation: ${(payload as any).operation}` }; + } + } catch (error) { + return { + ok: false, + error: error instanceof Error ? error.message : String(error), + }; + } +} + +async function listDir(payload: ListDirPayload): Promise { + const { cwd, params } = payload; + const targetPath = safePath(cwd, params.path); + const maxDepth = params.maxDepth ?? 3; + const maxEntries = params.maxEntries ?? 500; + const recursive = params.recursive ?? false; + + const entries: Array<{ name: string; path: string; type: "file" | "directory" }> = []; + + function walk(dir: string, depth: number): void { + if (entries.length >= maxEntries) return; + if (depth > maxDepth) return; + + let dirEntries: fs.Dirent[]; + try { + dirEntries = fs.readdirSync(dir, { withFileTypes: true }); + } catch { + return; + } + + for (const entry of dirEntries) { + if (entries.length >= maxEntries) return; + + const relPath = path.relative(cwd, path.join(dir, entry.name)); + const type = entry.isDirectory() ? "directory" : "file"; + entries.push({ name: entry.name, path: relPath, type }); + + if (recursive && entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") { + walk(path.join(dir, entry.name), depth + 1); + } + } + } + + walk(targetPath, 0); + return { ok: true, data: { entries } }; +} + +async function readFiles(payload: ReadFilesPayload): Promise { + const { cwd, params } = payload; + const maxBytes = params.maxBytes ?? MAX_FILE_BYTES; + const files: Record = {}; + + for (const filePath of params.paths) { + try { + const absPath = safePath(cwd, filePath); + const stat = fs.statSync(absPath); + if (stat.size > maxBytes) { + // Read only up to maxBytes + const buffer = Buffer.alloc(maxBytes); + const fd = fs.openSync(absPath, "r"); + fs.readSync(fd, buffer, 0, maxBytes, 0); + fs.closeSync(fd); + files[filePath] = buffer.toString("utf-8"); + } else { + files[filePath] = fs.readFileSync(absPath, "utf-8"); + } + } catch { + files[filePath] = null; + } + } + + return { ok: true, data: { files } }; +} + +async function fileExistsBatch( + payload: FileExistsBatchPayload, +): Promise { + const { cwd, params } = payload; + const exists: Record = {}; + + for (const filePath of params.paths) { + try { + const absPath = safePath(cwd, filePath); + exists[filePath] = fs.existsSync(absPath); + } catch { + exists[filePath] = false; + } + } + + return { ok: true, data: { exists } }; +} + +async function runCommands(payload: RunCommandsPayload): Promise { + const { cwd, params } = payload; + const timeoutMs = params.timeoutMs ?? DEFAULT_COMMAND_TIMEOUT_MS; + + const results: Array<{ + command: string; + exitCode: number; + stdout: string; + stderr: string; + }> = []; + + for (const command of params.commands) { + const result = await runSingleCommand(command, cwd, timeoutMs); + results.push(result); + if (result.exitCode !== 0) { + return { + ok: false, + error: `Command "${command}" failed with exit code ${result.exitCode}: ${result.stderr}`, + data: { results }, + }; + } + } + + return { ok: true, data: { results } }; +} + +function runSingleCommand( + command: string, + cwd: string, + timeoutMs: number, +): Promise<{ command: string; exitCode: number; stdout: string; stderr: string }> { + return new Promise((resolve) => { + const child = spawn("sh", ["-c", command], { + cwd, + stdio: ["ignore", "pipe", "pipe"], + timeout: timeoutMs, + }); + + const stdoutChunks: Buffer[] = []; + const stderrChunks: Buffer[] = []; + let stdoutLen = 0; + let stderrLen = 0; + + child.stdout.on("data", (chunk: Buffer) => { + if (stdoutLen < MAX_STDOUT_BYTES) { + stdoutChunks.push(chunk); + stdoutLen += chunk.length; + } + }); + + child.stderr.on("data", (chunk: Buffer) => { + if (stderrLen < MAX_STDOUT_BYTES) { + stderrChunks.push(chunk); + stderrLen += chunk.length; + } + }); + + child.on("error", (err) => { + resolve({ + command, + exitCode: 1, + stdout: "", + stderr: err.message, + }); + }); + + child.on("close", (code) => { + const stdout = Buffer.concat(stdoutChunks) + .toString("utf-8") + .slice(0, MAX_STDOUT_BYTES); + const stderr = Buffer.concat(stderrChunks) + .toString("utf-8") + .slice(0, MAX_STDOUT_BYTES); + resolve({ command, exitCode: code ?? 1, stdout, stderr }); + }); + }); +} + +async function applyPatchset( + payload: ApplyPatchsetPayload, +): Promise { + const { cwd, params } = payload; + const applied: Array<{ path: string; action: string }> = []; + + for (const patch of params.patches) { + const absPath = safePath(cwd, patch.path); + + switch (patch.action) { + case "create": { + // Ensure parent directory exists + const dir = path.dirname(absPath); + fs.mkdirSync(dir, { recursive: true }); + fs.writeFileSync(absPath, patch.patch, "utf-8"); + applied.push({ path: patch.path, action: "create" }); + break; + } + case "modify": { + if (!fs.existsSync(absPath)) { + return { + ok: false, + error: `Cannot modify "${patch.path}": file does not exist`, + }; + } + fs.writeFileSync(absPath, patch.patch, "utf-8"); + applied.push({ path: patch.path, action: "modify" }); + break; + } + case "delete": { + if (fs.existsSync(absPath)) { + fs.unlinkSync(absPath); + } + applied.push({ path: patch.path, action: "delete" }); + break; + } + } + } + + return { ok: true, data: { applied } }; +} diff --git a/src/lib/init/types.ts b/src/lib/init/types.ts new file mode 100644 index 00000000..5c52f30e --- /dev/null +++ b/src/lib/init/types.ts @@ -0,0 +1,101 @@ +import type { Writer } from "../../types/index.js"; + +export interface WizardOptions { + directory: string; + force: boolean; + yes: boolean; + dryRun: boolean; + features?: string[]; + stdout: Writer; + stderr: Writer; + stdin: NodeJS.ReadStream & { fd: 0 }; +} + +// ── Local-op suspend payloads ────────────────────────────── + +export type LocalOpPayload = + | ListDirPayload + | ReadFilesPayload + | FileExistsBatchPayload + | RunCommandsPayload + | ApplyPatchsetPayload; + +export interface ListDirPayload { + type: "local-op"; + operation: "list-dir"; + cwd: string; + params: { + path: string; + recursive?: boolean; + maxDepth?: number; + maxEntries?: number; + }; +} + +export interface ReadFilesPayload { + type: "local-op"; + operation: "read-files"; + cwd: string; + params: { + paths: string[]; + maxBytes?: number; + }; +} + +export interface FileExistsBatchPayload { + type: "local-op"; + operation: "file-exists-batch"; + cwd: string; + params: { + paths: string[]; + }; +} + +export interface RunCommandsPayload { + type: "local-op"; + operation: "run-commands"; + cwd: string; + params: { + commands: string[]; + timeoutMs?: number; + }; +} + +export interface ApplyPatchsetPayload { + type: "local-op"; + operation: "apply-patchset"; + cwd: string; + params: { + patches: Array<{ + path: string; + action: "create" | "modify" | "delete"; + patch: string; + }>; + }; +} + +export interface LocalOpResult { + ok: boolean; + error?: string; + data?: unknown; +} + +// ── Interactive suspend payloads ─────────────────────────── + +export interface InteractivePayload { + type: "interactive"; + prompt: string; + kind: "select" | "multi-select" | "confirm"; + [key: string]: unknown; +} + +// ── Workflow run result ──────────────────────────────────── + +export interface WorkflowRunResult { + status: "suspended" | "success" | "failed"; + suspended?: string[][]; + steps?: Record; + suspendPayload?: unknown; + result?: unknown; + error?: string; +} diff --git a/src/lib/init/wizard-runner.ts b/src/lib/init/wizard-runner.ts new file mode 100644 index 00000000..9ab13066 --- /dev/null +++ b/src/lib/init/wizard-runner.ts @@ -0,0 +1,115 @@ +/** + * Wizard Runner + * + * Main suspend/resume loop that drives the remote Mastra workflow. + * Each iteration: check status → if suspended, perform local-op or + * interactive prompt → resume with result → repeat. + */ + +import { MastraClient } from "@mastra/client-js"; +import { MASTRA_API_URL, WORKFLOW_ID } from "./constants.js"; +import { formatProgress, formatResult, formatError } from "./formatters.js"; +import { handleLocalOp } from "./local-ops.js"; +import { handleInteractive } from "./interactive.js"; +import type { + WizardOptions, + LocalOpPayload, + InteractivePayload, +} from "./types.js"; + +export async function runWizard(options: WizardOptions): Promise { + const { directory, force, yes, dryRun, features, stdout, stderr } = options; + + const client = new MastraClient({ baseUrl: MASTRA_API_URL }); + const workflow = client.getWorkflow(WORKFLOW_ID); + const run = await workflow.createRun(); + + let result = await run.startAsync({ + inputData: { directory, force, yes, dryRun, features }, + }); + + // Track multi-suspend phases per step + const stepPhases = new Map(); + + while ((result as any).status === "suspended") { + // Extract step ID and suspend payload + const stepPath = + (result as any).suspended?.[0] ?? + (result as any).activePaths?.[0] ?? + []; + const stepId: string = stepPath[stepPath.length - 1] ?? "unknown"; + + const payload = extractSuspendPayload(result as Record, stepId); + if (!payload) { + stderr.write(`Error: No suspend payload found for step "${stepId}"\n`); + break; + } + + formatProgress(stdout, stepId, payload); + + let resumeData: Record; + const payloadType = (payload as any).type as string; + + if (payloadType === "local-op") { + const localResult = await handleLocalOp( + payload as LocalOpPayload, + options, + ); + + // Track phase progression for multi-suspend steps + const phase = (stepPhases.get(stepId) ?? 0) + 1; + stepPhases.set(stepId, phase); + const phaseNames = ["read-files", "analyze", "done"]; + resumeData = { + ...localResult, + _phase: phaseNames[Math.min(phase - 1, phaseNames.length - 1)], + }; + } else if (payloadType === "interactive") { + const interactiveResult = await handleInteractive( + payload as InteractivePayload, + options, + ); + const phase = (stepPhases.get(stepId) ?? 0) + 1; + stepPhases.set(stepId, phase); + resumeData = { + ...interactiveResult, + _phase: "apply", + }; + } else { + stderr.write(`Error: Unknown suspend payload type "${payloadType}"\n`); + break; + } + + result = await run.resumeAsync({ + step: stepId, + resumeData, + }); + } + + const resultObj = result as Record; + if (resultObj.status === "success") { + formatResult(stdout, resultObj); + } else { + formatError(stderr, resultObj); + } +} + +function extractSuspendPayload( + result: Record, + stepId: string, +): unknown | undefined { + // Try step-specific payload first + const stepPayload = result.steps?.[stepId]?.suspendPayload; + if (stepPayload) return stepPayload; + + // Try top-level suspend payload + if (result.suspendPayload) return result.suspendPayload; + + // Try nested in activePaths data + for (const key of Object.keys(result.steps ?? {})) { + const step = result.steps[key]; + if (step?.suspendPayload) return step.suspendPayload; + } + + return undefined; +} From 8146d8b279119c464f5018671ca337f645779d14 Mon Sep 17 00:00:00 2001 From: betegon Date: Tue, 17 Feb 2026 21:20:05 +0100 Subject: [PATCH 02/26] feat(init): pass tracing options to Mastra workflow runs Sends tags and metadata (CLI version, OS, arch, node version) with startAsync and resumeAsync calls so workflow runs are visible and filterable in Mastra Studio. Co-Authored-By: Claude Opus 4.6 --- src/lib/init/wizard-runner.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/lib/init/wizard-runner.ts b/src/lib/init/wizard-runner.ts index 9ab13066..64b021fb 100644 --- a/src/lib/init/wizard-runner.ts +++ b/src/lib/init/wizard-runner.ts @@ -7,6 +7,7 @@ */ import { MastraClient } from "@mastra/client-js"; +import { CLI_VERSION } from "../constants.js"; import { MASTRA_API_URL, WORKFLOW_ID } from "./constants.js"; import { formatProgress, formatResult, formatError } from "./formatters.js"; import { handleLocalOp } from "./local-ops.js"; @@ -20,12 +21,24 @@ import type { export async function runWizard(options: WizardOptions): Promise { const { directory, force, yes, dryRun, features, stdout, stderr } = options; + const tracingOptions = { + tags: ["sentry-cli", "init-wizard"], + metadata: { + cliVersion: CLI_VERSION, + os: process.platform, + arch: process.arch, + nodeVersion: process.version, + dryRun, + }, + }; + const client = new MastraClient({ baseUrl: MASTRA_API_URL }); const workflow = client.getWorkflow(WORKFLOW_ID); const run = await workflow.createRun(); let result = await run.startAsync({ inputData: { directory, force, yes, dryRun, features }, + tracingOptions, }); // Track multi-suspend phases per step @@ -83,6 +96,7 @@ export async function runWizard(options: WizardOptions): Promise { result = await run.resumeAsync({ step: stepId, resumeData, + tracingOptions, }); } From 57f902fba5ff09150a2ac9db47b4a417e24012de Mon Sep 17 00:00:00 2001 From: betegon Date: Tue, 17 Feb 2026 22:19:57 +0100 Subject: [PATCH 03/26] feat(init): generate unique trace ID for each wizard run Import randomBytes and generate a hex trace ID so all suspend/resume calls within a single wizard run share one trace. Co-Authored-By: Claude Opus 4.6 --- src/lib/init/wizard-runner.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/init/wizard-runner.ts b/src/lib/init/wizard-runner.ts index 64b021fb..ff176484 100644 --- a/src/lib/init/wizard-runner.ts +++ b/src/lib/init/wizard-runner.ts @@ -6,6 +6,7 @@ * interactive prompt → resume with result → repeat. */ +import { randomBytes } from "node:crypto"; import { MastraClient } from "@mastra/client-js"; import { CLI_VERSION } from "../constants.js"; import { MASTRA_API_URL, WORKFLOW_ID } from "./constants.js"; @@ -22,6 +23,7 @@ export async function runWizard(options: WizardOptions): Promise { const { directory, force, yes, dryRun, features, stdout, stderr } = options; const tracingOptions = { + traceId: randomBytes(16).toString("hex"), tags: ["sentry-cli", "init-wizard"], metadata: { cliVersion: CLI_VERSION, From 0c5e4403ccc5c642c3212bfd06b20722769bfc67 Mon Sep 17 00:00:00 2001 From: betegon Date: Tue, 17 Feb 2026 22:20:08 +0100 Subject: [PATCH 04/26] fix(init): flatten nested workflow spans with shared parent span ID Add a synthetic parentSpanId to tracingOptions so all workflow run spans become siblings under the same parent instead of nesting by timestamp containment. Co-Authored-By: Claude Opus 4.6 --- src/lib/init/wizard-runner.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/init/wizard-runner.ts b/src/lib/init/wizard-runner.ts index ff176484..feeb28ce 100644 --- a/src/lib/init/wizard-runner.ts +++ b/src/lib/init/wizard-runner.ts @@ -24,6 +24,7 @@ export async function runWizard(options: WizardOptions): Promise { const tracingOptions = { traceId: randomBytes(16).toString("hex"), + parentSpanId: randomBytes(8).toString("hex"), tags: ["sentry-cli", "init-wizard"], metadata: { cliVersion: CLI_VERSION, From d60e3b23af0392e663a7cc574334f2d08adee4ba Mon Sep 17 00:00:00 2001 From: betegon Date: Wed, 18 Feb 2026 11:08:02 +0100 Subject: [PATCH 05/26] fix(init): remove unnecessary parentSpanId from tracing options The parentSpanId was creating artificial nesting - let the workflow engine handle span hierarchy naturally. Co-Authored-By: Claude Opus 4.6 --- src/lib/init/wizard-runner.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/init/wizard-runner.ts b/src/lib/init/wizard-runner.ts index feeb28ce..ff176484 100644 --- a/src/lib/init/wizard-runner.ts +++ b/src/lib/init/wizard-runner.ts @@ -24,7 +24,6 @@ export async function runWizard(options: WizardOptions): Promise { const tracingOptions = { traceId: randomBytes(16).toString("hex"), - parentSpanId: randomBytes(8).toString("hex"), tags: ["sentry-cli", "init-wizard"], metadata: { cliVersion: CLI_VERSION, From 3d39f6169cfc4efdc261ed4e284356e6b5be4ec6 Mon Sep 17 00:00:00 2001 From: betegon Date: Wed, 18 Feb 2026 12:03:57 +0100 Subject: [PATCH 06/26] feat(init): show ASCII banner and make error monitoring required Display the branded SENTRY ASCII banner before the intro line for visual consistency with `sentry --help`. Make the "errors" feature always enabled in the feature multi-select so users cannot deselect error monitoring. Co-Authored-By: Claude Opus 4.6 --- src/lib/help.ts | 2 +- src/lib/init/interactive.ts | 161 ++++++++++-------------- src/lib/init/wizard-runner.ts | 222 ++++++++++++++++++++++------------ 3 files changed, 209 insertions(+), 176 deletions(-) diff --git a/src/lib/help.ts b/src/lib/help.ts index 40873f43..bc632118 100644 --- a/src/lib/help.ts +++ b/src/lib/help.ts @@ -36,7 +36,7 @@ const BANNER_GRADIENT = [ * Format the banner with a vertical gradient effect. * Each row gets progressively darker purple. */ -function formatBanner(): string { +export function formatBanner(): string { return BANNER_ROWS.map((row, i) => { const color = BANNER_GRADIENT[i] ?? "#B4A4DE"; return chalk.hex(color)(row); diff --git a/src/lib/init/interactive.ts b/src/lib/init/interactive.ts index 3a6cdcb8..7c90efa7 100644 --- a/src/lib/init/interactive.ts +++ b/src/lib/init/interactive.ts @@ -6,21 +6,23 @@ * Respects --yes flag for non-interactive mode. */ -import type { WizardOptions, InteractivePayload } from "./types.js"; +import { confirm, log, multiselect, select } from "@clack/prompts"; +import { abortIfCancelled } from "./clack-utils.js"; +import type { InteractivePayload, WizardOptions } from "./types.js"; export async function handleInteractive( payload: InteractivePayload, - options: WizardOptions, + options: WizardOptions ): Promise> { const { kind } = payload; switch (kind) { case "select": - return handleSelect(payload, options); + return await handleSelect(payload, options); case "multi-select": - return handleMultiSelect(payload, options); + return await handleMultiSelect(payload, options); case "confirm": - return handleConfirm(payload, options); + return await handleConfirm(payload, options); default: return { cancelled: true }; } @@ -28,48 +30,49 @@ export async function handleInteractive( async function handleSelect( payload: InteractivePayload, - options: WizardOptions, + options: WizardOptions ): Promise> { - const apps = (payload.apps as Array<{ name: string; path: string; framework?: string }>) ?? []; + const apps = + (payload.apps as Array<{ + name: string; + path: string; + framework?: string; + }>) ?? []; const items = (payload.options as string[]) ?? apps.map((a) => a.name); if (items.length === 0) { return { cancelled: true }; } - // --yes: auto-pick if exactly one option if (options.yes) { if (items.length === 1) { + log.info(`Auto-selected: ${items[0]}`); return { selectedApp: items[0] }; } - options.stderr.write( - "Error: --yes requires exactly one option for selection, but found " + - `${items.length}. Run interactively to choose.\n`, + log.error( + `--yes requires exactly one option for selection, but found ${items.length}. Run interactively to choose.` ); return { cancelled: true }; } - options.stdout.write(`\n${payload.prompt}\n`); - for (let i = 0; i < items.length; i++) { - const app = apps[i]; - const extra = app?.framework ? ` (${app.framework})` : ""; - options.stdout.write(` ${i + 1}. ${items[i]}${extra}\n`); - } - - const answer = await readLine(options, `Choose [1-${items.length}]: `); - const idx = Number.parseInt(answer.trim(), 10) - 1; - - if (idx >= 0 && idx < items.length) { - return { selectedApp: items[idx] }; - } + const selected = await select({ + message: payload.prompt, + options: items.map((item, i) => { + const app = apps[i]; + return { + value: item, + label: item, + hint: app?.framework ?? undefined, + }; + }), + }); - options.stderr.write("Invalid selection.\n"); - return { cancelled: true }; + return { selectedApp: abortIfCancelled(selected) }; } async function handleMultiSelect( payload: InteractivePayload, - options: WizardOptions, + options: WizardOptions ): Promise> { const available = (payload.availableFeatures as string[]) ?? @@ -80,102 +83,60 @@ async function handleMultiSelect( return { features: [] }; } - // --yes: select all available features + const requiredFeature = "errors"; + const hasRequired = available.includes(requiredFeature); + if (options.yes) { + log.info(`Auto-selected all features: ${available.join(", ")}`); return { features: available }; } - options.stdout.write(`\n${payload.prompt}\n`); - for (let i = 0; i < available.length; i++) { - options.stdout.write(` ${i + 1}. ${available[i]}\n`); + if (hasRequired) { + log.info("Error monitoring is always enabled."); } - const answer = await readLine( - options, - `Choose (comma-separated, or "all") [1-${available.length}]: `, - ); + const optional = available.filter((f) => f !== requiredFeature); - if (answer.trim().toLowerCase() === "all") { - return { features: available }; - } + const selected = await multiselect({ + message: payload.prompt, + options: optional.map((feature) => ({ + value: feature, + label: feature, + })), + initialValues: optional, + required: false, + }); - const indices = answer - .split(",") - .map((s) => Number.parseInt(s.trim(), 10) - 1) - .filter((i) => i >= 0 && i < available.length); + const chosen = abortIfCancelled(selected); + if (hasRequired && !chosen.includes(requiredFeature)) { + chosen.unshift(requiredFeature); + } - const selected = [...new Set(indices.map((i) => available[i]))]; - return { features: selected }; + return { features: chosen }; } async function handleConfirm( payload: InteractivePayload, - options: WizardOptions, + options: WizardOptions ): Promise> { - // --yes: auto-confirm if (options.yes) { - // For "add example trigger" → default to true - // For "verification issues" → default to continue if (payload.prompt.includes("example")) { + log.info("Auto-confirmed: adding example trigger"); return { addExample: true }; } + log.info("Auto-confirmed: continuing"); return { action: "continue" }; } - options.stdout.write(`\n${payload.prompt} [Y/n] `); + const confirmed = await confirm({ + message: payload.prompt, + initialValue: true, + }); - const answer = await readLine(options, ""); - const confirmed = - answer.trim() === "" || - answer.trim().toLowerCase() === "y" || - answer.trim().toLowerCase() === "yes"; + const value = abortIfCancelled(confirmed); - // Determine which field to set based on the prompt if (payload.prompt.includes("example")) { - return { addExample: confirmed }; + return { addExample: value }; } - return { action: confirmed ? "continue" : "stop" }; -} - -function readLine( - options: WizardOptions, - prompt: string, -): Promise { - return new Promise((resolve) => { - if (prompt) { - options.stdout.write(prompt); - } - - const { stdin } = options; - const wasRaw = stdin.isRaw; - - // Handle piped stdin (non-TTY) - if (!stdin.isTTY) { - let data = ""; - const onData = (chunk: Buffer) => { - data += chunk.toString(); - if (data.includes("\n")) { - stdin.removeListener("data", onData); - resolve(data.split("\n")[0] ?? ""); - } - }; - stdin.on("data", onData); - stdin.resume(); - return; - } - - // TTY mode: read a line - stdin.setRawMode?.(false); - stdin.resume(); - stdin.setEncoding("utf-8"); - - const onData = (chunk: string) => { - stdin.removeListener("data", onData); - stdin.pause(); - if (wasRaw !== undefined) stdin.setRawMode?.(wasRaw); - resolve(chunk.trim()); - }; - - stdin.once("data", onData); - }); + return { action: value ? "continue" : "stop" }; } diff --git a/src/lib/init/wizard-runner.ts b/src/lib/init/wizard-runner.ts index ff176484..af171144 100644 --- a/src/lib/init/wizard-runner.ts +++ b/src/lib/init/wizard-runner.ts @@ -7,20 +7,99 @@ */ import { randomBytes } from "node:crypto"; +import { cancel, intro, log, spinner } from "@clack/prompts"; import { MastraClient } from "@mastra/client-js"; import { CLI_VERSION } from "../constants.js"; +import { formatBanner } from "../help.js"; +import { STEP_LABELS, WizardCancelledError } from "./clack-utils.js"; import { MASTRA_API_URL, WORKFLOW_ID } from "./constants.js"; -import { formatProgress, formatResult, formatError } from "./formatters.js"; -import { handleLocalOp } from "./local-ops.js"; +import { formatError, formatResult } from "./formatters.js"; import { handleInteractive } from "./interactive.js"; +import { handleLocalOp } from "./local-ops.js"; import type { - WizardOptions, - LocalOpPayload, InteractivePayload, + LocalOpPayload, + WizardOptions, + WorkflowRunResult, } from "./types.js"; +type StepSpinner = ReturnType; + +type StepContext = { + payload: unknown; + stepId: string; + s: StepSpinner; + options: WizardOptions; +}; + +function nextPhase( + stepPhases: Map, + stepId: string, + names: string[] +): string { + const phase = (stepPhases.get(stepId) ?? 0) + 1; + stepPhases.set(stepId, phase); + return names[Math.min(phase - 1, names.length - 1)] ?? "done"; +} + +async function handleSuspendedStep( + ctx: StepContext, + stepPhases: Map +): Promise> { + const { payload, stepId, s, options } = ctx; + const { type: payloadType, operation } = payload as { + type: string; + operation?: string; + }; + const label = STEP_LABELS[stepId] ?? stepId; + + if (payloadType === "local-op") { + const detail = operation ? ` (${operation})` : ""; + s.message(`${label}${detail}...`); + + const localResult = await handleLocalOp(payload as LocalOpPayload, options); + + return { + ...localResult, + _phase: nextPhase(stepPhases, stepId, ["read-files", "analyze", "done"]), + }; + } + + if (payloadType === "interactive") { + s.stop(label); + + const interactiveResult = await handleInteractive( + payload as InteractivePayload, + options + ); + + s.start("Processing..."); + + return { + ...interactiveResult, + _phase: nextPhase(stepPhases, stepId, ["apply"]), + }; + } + + s.stop("Error", 1); + log.error(`Unknown suspend payload type "${payloadType}"`); + cancel("Setup failed"); + throw new WizardCancelledError(); +} + +function errorMessage(err: unknown): string { + return err instanceof Error ? err.message : String(err); +} + export async function runWizard(options: WizardOptions): Promise { - const { directory, force, yes, dryRun, features, stdout, stderr } = options; + const { directory, force, yes, dryRun, features } = options; + + process.stderr.write(`\n${formatBanner()}\n\n`); + intro("sentry init"); + + if (dryRun) { + log.warn("Dry-run mode: no files will be modified."); + } const tracingOptions = { traceId: randomBytes(16).toString("hex"), @@ -38,94 +117,87 @@ export async function runWizard(options: WizardOptions): Promise { const workflow = client.getWorkflow(WORKFLOW_ID); const run = await workflow.createRun(); - let result = await run.startAsync({ - inputData: { directory, force, yes, dryRun, features }, - tracingOptions, - }); + const s = spinner(); - // Track multi-suspend phases per step - const stepPhases = new Map(); - - while ((result as any).status === "suspended") { - // Extract step ID and suspend payload - const stepPath = - (result as any).suspended?.[0] ?? - (result as any).activePaths?.[0] ?? - []; - const stepId: string = stepPath[stepPath.length - 1] ?? "unknown"; - - const payload = extractSuspendPayload(result as Record, stepId); - if (!payload) { - stderr.write(`Error: No suspend payload found for step "${stepId}"\n`); - break; - } - - formatProgress(stdout, stepId, payload); + let result: WorkflowRunResult; + try { + s.start("Connecting to wizard..."); + result = (await run.startAsync({ + inputData: { directory, force, yes, dryRun, features }, + tracingOptions, + })) as WorkflowRunResult; + } catch (err) { + s.stop("Connection failed", 1); + log.error(errorMessage(err)); + cancel("Setup failed"); + return; + } - let resumeData: Record; - const payloadType = (payload as any).type as string; + const stepPhases = new Map(); - if (payloadType === "local-op") { - const localResult = await handleLocalOp( - payload as LocalOpPayload, - options, + try { + while (result.status === "suspended") { + const stepPath = result.suspended?.at(0) ?? []; + const stepId: string = stepPath.at(-1) ?? "unknown"; + + const payload = extractSuspendPayload(result, stepId); + if (!payload) { + s.stop("Error", 1); + log.error(`No suspend payload found for step "${stepId}"`); + cancel("Setup failed"); + return; + } + + const resumeData = await handleSuspendedStep( + { payload, stepId, s, options }, + stepPhases ); - // Track phase progression for multi-suspend steps - const phase = (stepPhases.get(stepId) ?? 0) + 1; - stepPhases.set(stepId, phase); - const phaseNames = ["read-files", "analyze", "done"]; - resumeData = { - ...localResult, - _phase: phaseNames[Math.min(phase - 1, phaseNames.length - 1)], - }; - } else if (payloadType === "interactive") { - const interactiveResult = await handleInteractive( - payload as InteractivePayload, - options, - ); - const phase = (stepPhases.get(stepId) ?? 0) + 1; - stepPhases.set(stepId, phase); - resumeData = { - ...interactiveResult, - _phase: "apply", - }; - } else { - stderr.write(`Error: Unknown suspend payload type "${payloadType}"\n`); - break; + result = (await run.resumeAsync({ + step: stepId, + resumeData, + tracingOptions, + })) as WorkflowRunResult; } - - result = await run.resumeAsync({ - step: stepId, - resumeData, - tracingOptions, - }); + } catch (err) { + if (err instanceof WizardCancelledError) { + return; + } + s.stop("Cancelled", 1); + log.error(errorMessage(err)); + cancel("Setup failed"); + return; } - const resultObj = result as Record; - if (resultObj.status === "success") { - formatResult(stdout, resultObj); + s.stop("Done"); + + const output = result as unknown as Record; + if (result.status === "success") { + formatResult(output); } else { - formatError(stderr, resultObj); + formatError(output); } } function extractSuspendPayload( - result: Record, - stepId: string, + result: WorkflowRunResult, + stepId: string ): unknown | undefined { - // Try step-specific payload first const stepPayload = result.steps?.[stepId]?.suspendPayload; - if (stepPayload) return stepPayload; + if (stepPayload) { + return stepPayload; + } - // Try top-level suspend payload - if (result.suspendPayload) return result.suspendPayload; + if (result.suspendPayload) { + return result.suspendPayload; + } - // Try nested in activePaths data for (const key of Object.keys(result.steps ?? {})) { - const step = result.steps[key]; - if (step?.suspendPayload) return step.suspendPayload; + const step = result.steps?.[key]; + if (step?.suspendPayload) { + return step.suspendPayload; + } } - return undefined; + return; } From 11cdf6c1b8070118ef1955f7eedac5f9132bd645 Mon Sep 17 00:00:00 2001 From: betegon Date: Wed, 18 Feb 2026 18:47:32 +0100 Subject: [PATCH 07/26] fix(init): improve wizard UX for already-installed case, feature prompt, and source maps hint Route success-with-exitCode results to formatError so the --force hint is shown when Sentry is already installed. Fold the "Error Monitoring is always included" note into the multiselect prompt. Use a more approachable Source Maps hint. Co-Authored-By: Claude Opus 4.6 --- src/lib/init/clack-utils.ts | 74 +++++++++++++++++++++++++++++++++++ src/lib/init/interactive.ts | 24 ++++++++---- src/lib/init/wizard-runner.ts | 23 +++++++++-- 3 files changed, 109 insertions(+), 12 deletions(-) create mode 100644 src/lib/init/clack-utils.ts diff --git a/src/lib/init/clack-utils.ts b/src/lib/init/clack-utils.ts new file mode 100644 index 00000000..a99e3869 --- /dev/null +++ b/src/lib/init/clack-utils.ts @@ -0,0 +1,74 @@ +/** + * Clack Utilities + * + * Shared helpers for the clack-based init wizard UI. + */ + +import { cancel, isCancel } from "@clack/prompts"; + +export class WizardCancelledError extends Error { + constructor() { + super("Setup cancelled."); + this.name = "WizardCancelledError"; + } +} + +export function abortIfCancelled(value: T | symbol): T { + if (isCancel(value)) { + cancel( + "Setup cancelled. Visit https://docs.sentry.io/platforms/ to set up manually." + ); + throw new WizardCancelledError(); + } + return value as T; +} + +export const FEATURE_INFO: Record = { + errorMonitoring: { + label: "Error Monitoring", + hint: "Automatic error and crash reporting", + }, + performanceMonitoring: { + label: "Performance Monitoring", + hint: "Transaction and span tracing", + }, + sessionReplay: { + label: "Session Replay", + hint: "Visual replay of user sessions", + }, + profiling: { + label: "Profiling", + hint: "Code-level performance insights", + }, + logs: { label: "Logging", hint: "Structured log ingestion" }, + metrics: { label: "Custom Metrics", hint: "Track custom business metrics" }, + sourceMaps: { + label: "Source Maps", + hint: "See original source code in production errors", + }, +}; + +export function featureLabel(id: string): string { + return FEATURE_INFO[id]?.label ?? id; +} + +export function featureHint(id: string): string | undefined { + return FEATURE_INFO[id]?.hint; +} + +export const STEP_LABELS: Record = { + "discover-context": "Analyzing project structure", + "select-target-app": "Selecting target application", + "resolve-dir": "Resolving project directory", + "check-existing-sentry": "Checking for existing Sentry installation", + "detect-platform": "Detecting platform and framework", + "ensure-sentry-project": "Setting up Sentry project", + "select-features": "Selecting features", + "determine-pm": "Detecting package manager", + "install-deps": "Installing dependencies", + "plan-codemods": "Planning code modifications", + "apply-codemods": "Applying code modifications", + "verify-changes": "Verifying changes", + "add-example-trigger": "Example error trigger", + "open-sentry-ui": "Finishing up", +}; diff --git a/src/lib/init/interactive.ts b/src/lib/init/interactive.ts index 7c90efa7..6662e176 100644 --- a/src/lib/init/interactive.ts +++ b/src/lib/init/interactive.ts @@ -7,7 +7,8 @@ */ import { confirm, log, multiselect, select } from "@clack/prompts"; -import { abortIfCancelled } from "./clack-utils.js"; +import chalk from "chalk"; +import { abortIfCancelled, featureHint, featureLabel } from "./clack-utils.js"; import type { InteractivePayload, WizardOptions } from "./types.js"; export async function handleInteractive( @@ -83,25 +84,32 @@ async function handleMultiSelect( return { features: [] }; } - const requiredFeature = "errors"; + const requiredFeature = "errorMonitoring"; const hasRequired = available.includes(requiredFeature); if (options.yes) { - log.info(`Auto-selected all features: ${available.join(", ")}`); + log.info( + `Auto-selected all features: ${available.map(featureLabel).join(", ")}` + ); return { features: available }; } + const optional = available.filter((f) => f !== requiredFeature); + + const hints: string[] = []; if (hasRequired) { - log.info("Error monitoring is always enabled."); + hints.push( + chalk.dim(` ${featureLabel(requiredFeature)} is always included`) + ); } - - const optional = available.filter((f) => f !== requiredFeature); + hints.push(chalk.dim(" space=toggle, a=all, enter=confirm")); const selected = await multiselect({ - message: payload.prompt, + message: `${payload.prompt}\n${hints.join("\n")}`, options: optional.map((feature) => ({ value: feature, - label: feature, + label: featureLabel(feature), + hint: featureHint(feature), })), initialValues: optional, required: false, diff --git a/src/lib/init/wizard-runner.ts b/src/lib/init/wizard-runner.ts index af171144..2a12ca76 100644 --- a/src/lib/init/wizard-runner.ts +++ b/src/lib/init/wizard-runner.ts @@ -94,6 +94,14 @@ function errorMessage(err: unknown): string { export async function runWizard(options: WizardOptions): Promise { const { directory, force, yes, dryRun, features } = options; + if (!(yes || process.stdin.isTTY)) { + process.stderr.write( + "Error: Interactive mode requires a terminal. Use --yes for non-interactive mode.\n" + ); + process.exitCode = 1; + return; + } + process.stderr.write(`\n${formatBanner()}\n\n`); intro("sentry init"); @@ -169,13 +177,20 @@ export async function runWizard(options: WizardOptions): Promise { return; } - s.stop("Done"); + handleFinalResult(result, s); +} +function handleFinalResult(result: WorkflowRunResult, s: StepSpinner): void { const output = result as unknown as Record; - if (result.status === "success") { - formatResult(output); - } else { + const inner = (output.result as Record) ?? output; + const hasError = result.status !== "success" || inner.exitCode; + + if (hasError) { + s.stop("Failed", 1); formatError(output); + } else { + s.stop("Done"); + formatResult(output); } } From 049fc95dd687f1e11547b0f7859c21ec20e88c6b Mon Sep 17 00:00:00 2001 From: betegon Date: Wed, 18 Feb 2026 19:47:37 +0100 Subject: [PATCH 08/26] feat(init): add AI transparency note and review reminder to wizard Show a non-blocking info note about AI usage with a docs link before the first network call, and a review reminder before the success outro. Extract SENTRY_DOCS_URL constant to share between wizard-runner and clack-utils cancel message. Co-Authored-By: Claude Opus 4.6 --- src/lib/init/clack-utils.ts | 5 +- src/lib/init/constants.ts | 8 +- src/lib/init/formatters.ts | 149 ++++++++++++++++------------------ src/lib/init/wizard-runner.ts | 7 +- 4 files changed, 84 insertions(+), 85 deletions(-) diff --git a/src/lib/init/clack-utils.ts b/src/lib/init/clack-utils.ts index a99e3869..ea4eeb0e 100644 --- a/src/lib/init/clack-utils.ts +++ b/src/lib/init/clack-utils.ts @@ -5,6 +5,7 @@ */ import { cancel, isCancel } from "@clack/prompts"; +import { SENTRY_DOCS_URL } from "./constants.js"; export class WizardCancelledError extends Error { constructor() { @@ -15,9 +16,7 @@ export class WizardCancelledError extends Error { export function abortIfCancelled(value: T | symbol): T { if (isCancel(value)) { - cancel( - "Setup cancelled. Visit https://docs.sentry.io/platforms/ to set up manually." - ); + cancel(`Setup cancelled. Visit ${SENTRY_DOCS_URL} to set up manually.`); throw new WizardCancelledError(); } return value as T; diff --git a/src/lib/init/constants.ts b/src/lib/init/constants.ts index 842a9416..effb5101 100644 --- a/src/lib/init/constants.ts +++ b/src/lib/init/constants.ts @@ -3,6 +3,8 @@ export const MASTRA_API_URL = export const WORKFLOW_ID = "sentry-wizard"; -export const MAX_FILE_BYTES = 262144; // 256KB per file -export const MAX_STDOUT_BYTES = 65536; // 64KB stdout/stderr truncation -export const DEFAULT_COMMAND_TIMEOUT_MS = 120000; // 2 minutes +export const SENTRY_DOCS_URL = "https://docs.sentry.io/platforms/"; + +export const MAX_FILE_BYTES = 262_144; // 256KB per file +export const MAX_STDOUT_BYTES = 65_536; // 64KB stdout/stderr truncation +export const DEFAULT_COMMAND_TIMEOUT_MS = 120_000; // 2 minutes diff --git a/src/lib/init/formatters.ts b/src/lib/init/formatters.ts index fa004c99..46062c71 100644 --- a/src/lib/init/formatters.ts +++ b/src/lib/init/formatters.ts @@ -1,120 +1,113 @@ /** * Output Formatters * - * Format wizard progress, results, and errors for terminal display. + * Format wizard results and errors for terminal display using clack. */ -import type { Writer } from "../../types/index.js"; - -const STEP_LABELS: Record = { - "discover-context": "Analyzing project structure", - "select-target-app": "Selecting target application", - "resolve-dir": "Resolving project directory", - "check-existing-sentry": "Checking for existing Sentry installation", - "detect-platform": "Detecting platform and framework", - "ensure-sentry-project": "Setting up Sentry project", - "select-features": "Selecting features", - "determine-pm": "Detecting package manager", - "install-deps": "Installing dependencies", - "plan-codemods": "Planning code modifications", - "apply-codemods": "Applying code modifications", - "verify-changes": "Verifying changes", - "add-example-trigger": "Example error trigger", - "open-sentry-ui": "Finishing up", -}; - -export function formatProgress( - stdout: Writer, - stepId: string, - payload?: unknown, -): void { - const label = STEP_LABELS[stepId] ?? stepId; - const payloadType = (payload as any)?.type as string | undefined; - const operation = (payload as any)?.operation as string | undefined; - - let detail = ""; - if (payloadType === "local-op" && operation) { - detail = ` (${operation})`; - } +import { cancel, log, note, outro } from "@clack/prompts"; +import { featureLabel } from "./clack-utils.js"; - stdout.write(`> ${label}${detail}...\n`); -} +type WizardOutput = Record; -export function formatResult( - stdout: Writer, - result: Record, -): void { - const output = result.result ?? result; +function fileActionIcon(action: string): string { + if (action === "create") { + return "+"; + } + if (action === "delete") { + return "-"; + } + return "~"; +} - stdout.write("\nSentry SDK installed successfully!\n\n"); +function buildSummaryLines(output: WizardOutput): string[] { + const lines: string[] = []; if (output.platform) { - stdout.write(` Platform: ${output.platform}\n`); + lines.push(`Platform: ${output.platform}`); } if (output.projectDir) { - stdout.write(` Directory: ${output.projectDir}\n`); + lines.push(`Directory: ${output.projectDir}`); } - if (output.features?.length) { - stdout.write(` Features: ${output.features.join(", ")}\n`); + + const features = output.features as string[] | undefined; + if (features?.length) { + lines.push(`Features: ${features.map(featureLabel).join(", ")}`); } - if (output.commands?.length) { - stdout.write(` Commands: ${output.commands.join("; ")}\n`); + + const commands = output.commands as string[] | undefined; + if (commands?.length) { + lines.push(`Commands: ${commands.join("; ")}`); } if (output.sentryProjectUrl) { - stdout.write(` Project: ${output.sentryProjectUrl}\n`); + lines.push(`Project: ${output.sentryProjectUrl}`); } if (output.docsUrl) { - stdout.write(` Docs: ${output.docsUrl}\n`); + lines.push(`Docs: ${output.docsUrl}`); } - if (output.changedFiles?.length) { - stdout.write("\n Changed files:\n"); - for (const f of output.changedFiles) { - const icon = f.action === "create" ? "+" : f.action === "delete" ? "-" : "~"; - stdout.write(` ${icon} ${f.path}\n`); + const changedFiles = output.changedFiles as + | Array<{ action: string; path: string }> + | undefined; + if (changedFiles?.length) { + lines.push(""); + lines.push("Changed files:"); + for (const f of changedFiles) { + lines.push(` ${fileActionIcon(f.action)} ${f.path}`); } } - if (output.warnings?.length) { - stdout.write("\n Warnings:\n"); - for (const w of output.warnings) { - stdout.write(` ! ${w}\n`); + return lines; +} + +export function formatResult(result: WizardOutput): void { + const output = (result.result as WizardOutput) ?? result; + const lines = buildSummaryLines(output); + + if (lines.length > 0) { + note(lines.join("\n"), "Setup complete"); + } + + const warnings = output.warnings as string[] | undefined; + if (warnings?.length) { + for (const w of warnings) { + log.warn(w); } } - stdout.write("\n"); + log.info("Please review the changes above before committing."); + + outro("Sentry SDK installed successfully!"); } -export function formatError( - stderr: Writer, - result: Record, -): void { +export function formatError(result: WizardOutput): void { + const inner = result.result as WizardOutput | undefined; const message = - result.error ?? result.result?.message ?? "Wizard failed with an unknown error"; - const exitCode = result.result?.exitCode ?? 1; + result.error ?? inner?.message ?? "Wizard failed with an unknown error"; + const exitCode = (inner?.exitCode as number) ?? 1; - stderr.write(`\nError: ${message}\n`); + log.error(String(message)); - // Provide actionable suggestions based on exit code if (exitCode === 10) { - stderr.write(" Hint: Use --force to override existing Sentry installation.\n"); + log.warn("Hint: Use --force to override existing Sentry installation."); } else if (exitCode === 20) { - stderr.write(" Hint: Could not detect your project's platform. Check that the directory contains a valid project.\n"); + log.warn( + "Hint: Could not detect your project's platform. Check that the directory contains a valid project." + ); } else if (exitCode === 30) { - const commands = result.result?.commands as string[] | undefined; + const commands = inner?.commands as string[] | undefined; if (commands?.length) { - stderr.write(" You can install dependencies manually:\n"); - for (const cmd of commands) { - stderr.write(` $ ${cmd}\n`); - } + log.warn( + `You can install dependencies manually:\n${commands.map((cmd) => ` $ ${cmd}`).join("\n")}` + ); } } else if (exitCode === 50) { - stderr.write(" Hint: Fix the verification issues and run 'sentry init' again.\n"); + log.warn("Hint: Fix the verification issues and run 'sentry init' again."); } - if (result.result?.docsUrl) { - stderr.write(` Docs: ${result.result.docsUrl}\n`); + const docsUrl = inner?.docsUrl; + if (docsUrl) { + log.info(`Docs: ${docsUrl}`); } - stderr.write("\n"); + cancel("Setup failed"); } diff --git a/src/lib/init/wizard-runner.ts b/src/lib/init/wizard-runner.ts index 2a12ca76..b24988b8 100644 --- a/src/lib/init/wizard-runner.ts +++ b/src/lib/init/wizard-runner.ts @@ -12,7 +12,7 @@ import { MastraClient } from "@mastra/client-js"; import { CLI_VERSION } from "../constants.js"; import { formatBanner } from "../help.js"; import { STEP_LABELS, WizardCancelledError } from "./clack-utils.js"; -import { MASTRA_API_URL, WORKFLOW_ID } from "./constants.js"; +import { MASTRA_API_URL, SENTRY_DOCS_URL, WORKFLOW_ID } from "./constants.js"; import { formatError, formatResult } from "./formatters.js"; import { handleInteractive } from "./interactive.js"; import { handleLocalOp } from "./local-ops.js"; @@ -109,6 +109,11 @@ export async function runWizard(options: WizardOptions): Promise { log.warn("Dry-run mode: no files will be modified."); } + log.info( + "This wizard uses AI to analyze your project and configure Sentry." + + `\nFor manual setup: ${SENTRY_DOCS_URL}` + ); + const tracingOptions = { traceId: randomBytes(16).toString("hex"), tags: ["sentry-cli", "init-wizard"], From 1e76a55314bdb17417ffc3c88137fa7c6f181d53 Mon Sep 17 00:00:00 2001 From: Aditya Mathur <57684218+MathurAditya724@users.noreply.github.com> Date: Thu, 19 Feb 2026 17:16:04 +0530 Subject: [PATCH 09/26] fix: added auth headers in the mastra client (#264) Co-authored-by: github-actions[bot] --- plugins/sentry-cli/skills/sentry-cli/SKILL.md | 14 ++++ src/commands/init.ts | 4 +- src/lib/init/local-ops.ts | 77 +++++++++++++------ src/lib/init/types.ts | 36 ++++----- src/lib/init/wizard-runner.ts | 7 +- 5 files changed, 91 insertions(+), 47 deletions(-) diff --git a/plugins/sentry-cli/skills/sentry-cli/SKILL.md b/plugins/sentry-cli/skills/sentry-cli/SKILL.md index a2ba5469..e614abf9 100644 --- a/plugins/sentry-cli/skills/sentry-cli/SKILL.md +++ b/plugins/sentry-cli/skills/sentry-cli/SKILL.md @@ -571,6 +571,20 @@ View details of a specific trace - `-w, --web - Open in browser` - `--spans - Span tree depth limit (number, "all" for unlimited, "no" to disable) - (default: "3")` +### Init + +Initialize Sentry in your project + +#### `sentry init ` + +Initialize Sentry in your project + +**Flags:** +- `--force - Continue even if Sentry is already installed` +- `-y, --yes - Non-interactive mode (accept defaults)` +- `--dry-run - Preview changes without applying them` +- `--features - Comma-separated features: errors,tracing,logs,replay,metrics` + ### Issues List issues in a project diff --git a/src/commands/init.ts b/src/commands/init.ts index 022b2b87..3aacfb62 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -67,9 +67,7 @@ export const initCommand = buildCommand({ }, }, async func(this: SentryContext, flags: InitFlags, directory?: string) { - const targetDir = directory - ? path.resolve(this.cwd, directory) - : this.cwd; + const targetDir = directory ? path.resolve(this.cwd, directory) : this.cwd; const featuresList = flags.features ?.split(",") .map((f) => f.trim()) diff --git a/src/lib/init/local-ops.ts b/src/lib/init/local-ops.ts index 16314053..84fef095 100644 --- a/src/lib/init/local-ops.ts +++ b/src/lib/init/local-ops.ts @@ -5,23 +5,23 @@ * All operations are sandboxed to the workflow's cwd directory. */ +import { spawn } from "node:child_process"; import fs from "node:fs"; import path from "node:path"; -import { spawn } from "node:child_process"; import { + DEFAULT_COMMAND_TIMEOUT_MS, MAX_FILE_BYTES, MAX_STDOUT_BYTES, - DEFAULT_COMMAND_TIMEOUT_MS, } from "./constants.js"; import type { - WizardOptions, + ApplyPatchsetPayload, + FileExistsBatchPayload, + ListDirPayload, LocalOpPayload, LocalOpResult, - ListDirPayload, ReadFilesPayload, - FileExistsBatchPayload, RunCommandsPayload, - ApplyPatchsetPayload, + WizardOptions, } from "./types.js"; /** @@ -31,7 +31,10 @@ import type { function safePath(cwd: string, relative: string): string { const resolved = path.resolve(cwd, relative); const normalizedCwd = path.resolve(cwd); - if (!resolved.startsWith(normalizedCwd + path.sep) && resolved !== normalizedCwd) { + if ( + !resolved.startsWith(normalizedCwd + path.sep) && + resolved !== normalizedCwd + ) { throw new Error(`Path "${relative}" resolves outside project directory`); } return resolved; @@ -39,7 +42,7 @@ function safePath(cwd: string, relative: string): string { export async function handleLocalOp( payload: LocalOpPayload, - _options: WizardOptions, + _options: WizardOptions ): Promise { try { switch (payload.operation) { @@ -54,7 +57,13 @@ export async function handleLocalOp( case "apply-patchset": return await applyPatchset(payload); default: - return { ok: false, error: `Unknown operation: ${(payload as any).operation}` }; + return { + ok: false, + error: `Unknown operation: ${ + // biome-ignore lint/suspicious/noExplicitAny: payload is of type LocalOpPayload + (payload as any).operation + }`, + }; } } catch (error) { return { @@ -64,18 +73,24 @@ export async function handleLocalOp( } } -async function listDir(payload: ListDirPayload): Promise { +function listDir(payload: ListDirPayload): LocalOpResult { const { cwd, params } = payload; const targetPath = safePath(cwd, params.path); const maxDepth = params.maxDepth ?? 3; const maxEntries = params.maxEntries ?? 500; const recursive = params.recursive ?? false; - const entries: Array<{ name: string; path: string; type: "file" | "directory" }> = []; + const entries: Array<{ + name: string; + path: string; + type: "file" | "directory"; + }> = []; + // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: walking the directory tree is a complex operation function walk(dir: string, depth: number): void { - if (entries.length >= maxEntries) return; - if (depth > maxDepth) return; + if (entries.length >= maxEntries || depth > maxDepth) { + return; + } let dirEntries: fs.Dirent[]; try { @@ -85,13 +100,20 @@ async function listDir(payload: ListDirPayload): Promise { } for (const entry of dirEntries) { - if (entries.length >= maxEntries) return; + if (entries.length >= maxEntries) { + return; + } const relPath = path.relative(cwd, path.join(dir, entry.name)); const type = entry.isDirectory() ? "directory" : "file"; entries.push({ name: entry.name, path: relPath, type }); - if (recursive && entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") { + if ( + recursive && + entry.isDirectory() && + !entry.name.startsWith(".") && + entry.name !== "node_modules" + ) { walk(path.join(dir, entry.name), depth + 1); } } @@ -101,7 +123,7 @@ async function listDir(payload: ListDirPayload): Promise { return { ok: true, data: { entries } }; } -async function readFiles(payload: ReadFilesPayload): Promise { +function readFiles(payload: ReadFilesPayload): LocalOpResult { const { cwd, params } = payload; const maxBytes = params.maxBytes ?? MAX_FILE_BYTES; const files: Record = {}; @@ -128,9 +150,7 @@ async function readFiles(payload: ReadFilesPayload): Promise { return { ok: true, data: { files } }; } -async function fileExistsBatch( - payload: FileExistsBatchPayload, -): Promise { +function fileExistsBatch(payload: FileExistsBatchPayload): LocalOpResult { const { cwd, params } = payload; const exists: Record = {}; @@ -146,7 +166,9 @@ async function fileExistsBatch( return { ok: true, data: { exists } }; } -async function runCommands(payload: RunCommandsPayload): Promise { +async function runCommands( + payload: RunCommandsPayload +): Promise { const { cwd, params } = payload; const timeoutMs = params.timeoutMs ?? DEFAULT_COMMAND_TIMEOUT_MS; @@ -175,8 +197,13 @@ async function runCommands(payload: RunCommandsPayload): Promise function runSingleCommand( command: string, cwd: string, - timeoutMs: number, -): Promise<{ command: string; exitCode: number; stdout: string; stderr: string }> { + timeoutMs: number +): Promise<{ + command: string; + exitCode: number; + stdout: string; + stderr: string; +}> { return new Promise((resolve) => { const child = spawn("sh", ["-c", command], { cwd, @@ -224,9 +251,7 @@ function runSingleCommand( }); } -async function applyPatchset( - payload: ApplyPatchsetPayload, -): Promise { +function applyPatchset(payload: ApplyPatchsetPayload): LocalOpResult { const { cwd, params } = payload; const applied: Array<{ path: string; action: string }> = []; @@ -260,6 +285,8 @@ async function applyPatchset( applied.push({ path: patch.path, action: "delete" }); break; } + default: + break; } } diff --git a/src/lib/init/types.ts b/src/lib/init/types.ts index 5c52f30e..9add1df9 100644 --- a/src/lib/init/types.ts +++ b/src/lib/init/types.ts @@ -1,6 +1,6 @@ import type { Writer } from "../../types/index.js"; -export interface WizardOptions { +export type WizardOptions = { directory: string; force: boolean; yes: boolean; @@ -9,7 +9,7 @@ export interface WizardOptions { stdout: Writer; stderr: Writer; stdin: NodeJS.ReadStream & { fd: 0 }; -} +}; // ── Local-op suspend payloads ────────────────────────────── @@ -20,7 +20,7 @@ export type LocalOpPayload = | RunCommandsPayload | ApplyPatchsetPayload; -export interface ListDirPayload { +export type ListDirPayload = { type: "local-op"; operation: "list-dir"; cwd: string; @@ -30,9 +30,9 @@ export interface ListDirPayload { maxDepth?: number; maxEntries?: number; }; -} +}; -export interface ReadFilesPayload { +export type ReadFilesPayload = { type: "local-op"; operation: "read-files"; cwd: string; @@ -40,18 +40,18 @@ export interface ReadFilesPayload { paths: string[]; maxBytes?: number; }; -} +}; -export interface FileExistsBatchPayload { +export type FileExistsBatchPayload = { type: "local-op"; operation: "file-exists-batch"; cwd: string; params: { paths: string[]; }; -} +}; -export interface RunCommandsPayload { +export type RunCommandsPayload = { type: "local-op"; operation: "run-commands"; cwd: string; @@ -59,9 +59,9 @@ export interface RunCommandsPayload { commands: string[]; timeoutMs?: number; }; -} +}; -export interface ApplyPatchsetPayload { +export type ApplyPatchsetPayload = { type: "local-op"; operation: "apply-patchset"; cwd: string; @@ -72,30 +72,30 @@ export interface ApplyPatchsetPayload { patch: string; }>; }; -} +}; -export interface LocalOpResult { +export type LocalOpResult = { ok: boolean; error?: string; data?: unknown; -} +}; // ── Interactive suspend payloads ─────────────────────────── -export interface InteractivePayload { +export type InteractivePayload = { type: "interactive"; prompt: string; kind: "select" | "multi-select" | "confirm"; [key: string]: unknown; -} +}; // ── Workflow run result ──────────────────────────────────── -export interface WorkflowRunResult { +export type WorkflowRunResult = { status: "suspended" | "success" | "failed"; suspended?: string[][]; steps?: Record; suspendPayload?: unknown; result?: unknown; error?: string; -} +}; diff --git a/src/lib/init/wizard-runner.ts b/src/lib/init/wizard-runner.ts index b24988b8..17f5a7f2 100644 --- a/src/lib/init/wizard-runner.ts +++ b/src/lib/init/wizard-runner.ts @@ -10,6 +10,7 @@ import { randomBytes } from "node:crypto"; import { cancel, intro, log, spinner } from "@clack/prompts"; import { MastraClient } from "@mastra/client-js"; import { CLI_VERSION } from "../constants.js"; +import { getAuthToken } from "../db/auth.js"; import { formatBanner } from "../help.js"; import { STEP_LABELS, WizardCancelledError } from "./clack-utils.js"; import { MASTRA_API_URL, SENTRY_DOCS_URL, WORKFLOW_ID } from "./constants.js"; @@ -126,7 +127,11 @@ export async function runWizard(options: WizardOptions): Promise { }, }; - const client = new MastraClient({ baseUrl: MASTRA_API_URL }); + const token = getAuthToken(); + const client = new MastraClient({ + baseUrl: MASTRA_API_URL, + headers: token ? { Authorization: `Bearer ${token}` } : {}, + }); const workflow = client.getWorkflow(WORKFLOW_ID); const run = await workflow.createRun(); From 077119af0abe351b29e6f6339e48ea0029140314 Mon Sep 17 00:00:00 2001 From: betegon Date: Mon, 23 Feb 2026 17:25:24 +0100 Subject: [PATCH 10/26] fix(init): update MASTRA_API_URL to production worker endpoint Co-Authored-By: Claude Opus 4.6 --- src/lib/init/constants.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/init/constants.ts b/src/lib/init/constants.ts index effb5101..385ac180 100644 --- a/src/lib/init/constants.ts +++ b/src/lib/init/constants.ts @@ -1,5 +1,6 @@ export const MASTRA_API_URL = - process.env.SENTRY_WIZARD_API_URL ?? "http://localhost:4111"; + process.env.SENTRY_WIZARD_API_URL ?? + "http://sentry-init-agent.getsentry.workers.dev"; export const WORKFLOW_ID = "sentry-wizard"; From 350530f77ac42506e332094aeabf0eab1e5a8b46 Mon Sep 17 00:00:00 2001 From: betegon Date: Mon, 23 Feb 2026 17:25:30 +0100 Subject: [PATCH 11/26] feat(init): add eval test dependencies and biome config Add @anthropic-ai/sdk and openai as devDependencies for the LLM-as-judge eval framework. Add opencode-lore dependency. Exclude test/init-eval/templates from biome linting since they are fixture apps, not source code. Co-Authored-By: Claude Opus 4.6 --- biome.jsonc | 2 +- bun.lock | 60 +++++++++++++++++++++++++++++++++++++++++++--------- package.json | 6 ++++++ 3 files changed, 57 insertions(+), 11 deletions(-) diff --git a/biome.jsonc b/biome.jsonc index a010d302..292b4d49 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -2,7 +2,7 @@ "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", "extends": ["ultracite/core"], "files": { - "includes": ["!docs"] + "includes": ["!docs", "!test/init-eval/templates"] }, "javascript": { "globals": ["Bun"] diff --git a/bun.lock b/bun.lock index 323ae911..b0ee83e6 100644 --- a/bun.lock +++ b/bun.lock @@ -4,6 +4,7 @@ "workspaces": { "": { "devDependencies": { + "@anthropic-ai/sdk": "^0.39.0", "@biomejs/biome": "2.3.8", "@mastra/client-js": "^1.4.0", "@sentry/api": "^0.1.0", @@ -21,6 +22,7 @@ "esbuild": "^0.25.0", "fast-check": "^4.5.3", "ignore": "^7.0.5", + "openai": "^6.22.0", "p-limit": "^7.2.0", "pretty-ms": "^9.3.0", "qrcode-terminal": "^0.12.0", @@ -53,6 +55,8 @@ "@ai-sdk/ui-utils-v5": ["@ai-sdk/ui-utils@1.2.11", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w=="], + "@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.39.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-eMyDIPRZbt1CCLErRCi3exlAvNkBtRe+kW5vvJyef93PmNr/clstYgHhtvmkxN82nlKgzyGPCyGxrm0JQ1ZIdg=="], + "@apm-js-collab/code-transformer": ["@apm-js-collab/code-transformer@0.8.2", "", {}, "sha512-YRjJjNq5KFSjDUoqu5pFUWrrsvGOxl6c3bu+uMFc9HNNptZ2rNU/TI2nLw4jnhQNtka972Ee2m3uqbvDQtPeCA=="], "@apm-js-collab/tracing-hooks": ["@apm-js-collab/tracing-hooks@0.3.1", "", { "dependencies": { "@apm-js-collab/code-transformer": "^0.8.0", "debug": "^4.4.1", "module-details-from-path": "^1.0.4" } }, "sha512-Vu1CbmPURlN5fTboVuKMoJjbO5qcq9fA5YXpskx3dXe/zTBvjODFoerw+69rVBlRLrJpwPqSDqEuJDEKIrTldw=="], @@ -329,6 +333,8 @@ "@types/node": ["@types/node@22.19.11", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w=="], + "@types/node-fetch": ["@types/node-fetch@2.6.13", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.4" } }, "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw=="], + "@types/pg": ["@types/pg@8.15.6", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ=="], "@types/pg-pool": ["@types/pg-pool@2.0.7", "", { "dependencies": { "@types/pg": "*" } }, "sha512-U4CwmGVQcbEuqpyju8/ptOKg6gEC+Tqsvj2xS9o1g71bUh8twxnC6ZL5rZKCsGN0iyH0CwgUyc9VR5owNQF9Ng=="], @@ -347,6 +353,8 @@ "@types/tedious": ["@types/tedious@4.0.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw=="], + "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], + "accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="], "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], @@ -355,6 +363,8 @@ "agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], + "agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="], + "ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], @@ -365,6 +375,8 @@ "array-flatten": ["array-flatten@1.1.1", "", {}, "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="], + "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], @@ -401,6 +413,8 @@ "cjs-module-lexer": ["cjs-module-lexer@2.2.0", "", {}, "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ=="], + "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], + "commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], "content-disposition": ["content-disposition@0.5.4", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ=="], @@ -421,6 +435,8 @@ "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], + "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], "destroy": ["destroy@1.2.0", "", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="], @@ -441,6 +457,8 @@ "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=="], + "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], @@ -453,6 +471,8 @@ "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="], + "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="], @@ -477,6 +497,12 @@ "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + "form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="], + + "form-data-encoder": ["form-data-encoder@1.7.2", "", {}, "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="], + + "formdata-node": ["formdata-node@4.4.1", "", { "dependencies": { "node-domexception": "1.0.0", "web-streams-polyfill": "4.0.0-beta.3" } }, "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ=="], + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], "forwarded-parse": ["forwarded-parse@2.1.2", "", {}, "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw=="], @@ -505,6 +531,8 @@ "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], "hono": ["hono@4.11.9", "", {}, "sha512-Eaw2YTGM6WOxA6CXbckaEvslr2Ne4NFsKrvc0v97JD5awbmeBLO5w9Ho9L9kmKonrwF9RJlW6BxT1PVv/agBHQ=="], @@ -515,6 +543,8 @@ "https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], + "humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="], + "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], "ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], @@ -585,9 +615,9 @@ "mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="], - "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], - "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], "minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], @@ -601,6 +631,8 @@ "negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], + "node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="], + "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], @@ -617,6 +649,8 @@ "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + "openai": ["openai@6.22.0", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-7Yvy17F33Bi9RutWbsaYt5hJEEJ/krRPOrwan+f9aCPuMat1WVsb2VNSII5W1EksKT6fF69TG/xj4XzodK3JZw=="], + "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="], "p-limit": ["p-limit@7.3.0", "", { "dependencies": { "yocto-queue": "^1.2.1" } }, "sha512-7cIXg/Z0M5WZRblrsOla88S4wAK+zOQQWeBYfV3qJuJXMr+LnbYjaadrFaS0JILfEDPVqHyKnZ1Z/1d6J9VVUw=="], @@ -761,6 +795,8 @@ "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + "web-streams-polyfill": ["web-streams-polyfill@4.0.0-beta.3", "", {}, "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug=="], + "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], "webpack-sources": ["webpack-sources@3.3.4", "", {}, "sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q=="], @@ -797,6 +833,8 @@ "@ai-sdk/ui-utils-v5/@ai-sdk/provider": ["@ai-sdk/provider@1.1.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg=="], + "@anthropic-ai/sdk/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="], + "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], @@ -815,8 +853,6 @@ "@sentry/bundler-plugin-core/glob": ["glob@9.3.5", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="], - "accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], - "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "express/body-parser": ["body-parser@1.20.4", "", { "dependencies": { "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "~1.2.0", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", "qs": "~6.14.0", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" } }, "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA=="], @@ -839,10 +875,14 @@ "send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + "type-is/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], + "ultracite/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], "zod-from-json-schema/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "@anthropic-ai/sdk/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + "@modelcontextprotocol/sdk/express/accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], "@modelcontextprotocol/sdk/express/content-disposition": ["content-disposition@1.0.1", "", {}, "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q=="], @@ -855,6 +895,8 @@ "@modelcontextprotocol/sdk/express/merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], + "@modelcontextprotocol/sdk/express/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], + "@modelcontextprotocol/sdk/express/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=="], "@modelcontextprotocol/sdk/express/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=="], @@ -867,8 +909,6 @@ "@sentry/bundler-plugin-core/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], - "accepts/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], - "express/body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], "express/body-parser/raw-body": ["raw-body@2.5.3", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "unpipe": "~1.0.0" } }, "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA=="], @@ -877,8 +917,6 @@ "express/type-is/media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="], - "express/type-is/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], - "finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "glob/minimatch/brace-expansion": ["brace-expansion@5.0.2", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw=="], @@ -887,14 +925,16 @@ "send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + "type-is/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + "@modelcontextprotocol/sdk/express/accepts/negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + "@modelcontextprotocol/sdk/express/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + "@sentry/bundler-plugin-core/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], "@sentry/bundler-plugin-core/glob/path-scurry/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], - "express/type-is/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], - "glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.2", "", { "dependencies": { "jackspeak": "^4.2.3" } }, "sha512-x0K50QvKQ97fdEz2kPehIerj+YTeptKF9hyYkKf6egnwmMWAkADiO0QCzSp0R5xN8FTZgYaBfSaue46Ej62nMg=="], } } diff --git a/package.json b/package.json index fd680571..555ce054 100644 --- a/package.json +++ b/package.json @@ -21,11 +21,13 @@ "test:unit": "bun test test/lib test/commands test/types --coverage --coverage-reporter=lcov", "test:isolated": "bun test test/isolated", "test:e2e": "bun test test/e2e", + "test:init-eval": "bun test test/init-eval --timeout 600000", "generate:skill": "bun run script/generate-skill.ts", "check:skill": "bun run script/check-skill.ts", "check:deps": "bun run script/check-no-deps.ts" }, "devDependencies": { + "@anthropic-ai/sdk": "^0.39.0", "@biomejs/biome": "2.3.8", "@mastra/client-js": "^1.4.0", "@sentry/api": "^0.1.0", @@ -43,6 +45,7 @@ "esbuild": "^0.25.0", "fast-check": "^4.5.3", "ignore": "^7.0.5", + "openai": "^6.22.0", "p-limit": "^7.2.0", "pretty-ms": "^9.3.0", "qrcode-terminal": "^0.12.0", @@ -64,5 +67,8 @@ "packageManager": "bun@1.3.9", "patchedDependencies": { "@stricli/core@1.2.5": "patches/@stricli%2Fcore@1.2.5.patch" + }, + "dependencies": { + "opencode-lore": "^0.1.0" } } From 4e1269f72b75ed15c61686b03695445c17f87ad1 Mon Sep 17 00:00:00 2001 From: betegon Date: Mon, 23 Feb 2026 17:25:37 +0100 Subject: [PATCH 12/26] feat(init): add init-eval test suite Add LLM-as-judge eval tests for the init wizard across all five platforms (Express, Next.js, Flask, React+Vite, SvelteKit). Each test runs the wizard end-to-end and asserts on SDK installation, Sentry.init presence, build success, and documentation accuracy via an LLM judge. Includes template apps, helper utilities (assertions, doc-fetcher, judge, platform configs), and feature-docs.json mapping. Co-Authored-By: Claude Opus 4.6 --- test/init-eval/express.eval.test.ts | 3 + test/init-eval/feature-docs.json | 59 ++++++++ test/init-eval/helpers/assertions.ts | 98 ++++++++++++ test/init-eval/helpers/create-eval-suite.ts | 57 +++++++ test/init-eval/helpers/docs-fetcher.ts | 60 ++++++++ test/init-eval/helpers/judge.ts | 143 ++++++++++++++++++ test/init-eval/helpers/platforms.ts | 129 ++++++++++++++++ test/init-eval/helpers/run-wizard.ts | 117 ++++++++++++++ test/init-eval/helpers/test-env.ts | 46 ++++++ test/init-eval/nextjs.eval.test.ts | 3 + test/init-eval/python-flask.eval.test.ts | 3 + test/init-eval/react-vite.eval.test.ts | 3 + test/init-eval/sveltekit.eval.test.ts | 3 + .../templates/express-app/.gitignore | 2 + .../templates/express-app/package.json | 17 +++ .../templates/express-app/src/index.ts | 12 ++ .../templates/express-app/tsconfig.json | 16 ++ .../init-eval/templates/nextjs-app/.gitignore | 3 + .../templates/nextjs-app/next.config.ts | 5 + .../templates/nextjs-app/package.json | 21 +++ .../templates/nextjs-app/src/app/layout.tsx | 16 ++ .../templates/nextjs-app/src/app/page.tsx | 3 + .../templates/nextjs-app/tsconfig.json | 21 +++ .../templates/python-flask-app/.gitignore | 4 + .../templates/python-flask-app/app.py | 12 ++ .../python-flask-app/requirements.txt | 1 + .../templates/react-vite-app/.gitignore | 2 + .../templates/react-vite-app/index.html | 12 ++ .../templates/react-vite-app/package.json | 22 +++ .../templates/react-vite-app/src/app.tsx | 5 + .../templates/react-vite-app/src/main.tsx | 9 ++ .../templates/react-vite-app/tsconfig.json | 19 +++ .../templates/react-vite-app/vite.config.ts | 6 + .../templates/sveltekit-app/.gitignore | 3 + .../templates/sveltekit-app/package.json | 18 +++ .../sveltekit-app/src/routes/+page.svelte | 1 + .../templates/sveltekit-app/svelte.config.js | 12 ++ .../templates/sveltekit-app/tsconfig.json | 14 ++ .../templates/sveltekit-app/vite.config.ts | 6 + 39 files changed, 986 insertions(+) create mode 100644 test/init-eval/express.eval.test.ts create mode 100644 test/init-eval/feature-docs.json create mode 100644 test/init-eval/helpers/assertions.ts create mode 100644 test/init-eval/helpers/create-eval-suite.ts create mode 100644 test/init-eval/helpers/docs-fetcher.ts create mode 100644 test/init-eval/helpers/judge.ts create mode 100644 test/init-eval/helpers/platforms.ts create mode 100644 test/init-eval/helpers/run-wizard.ts create mode 100644 test/init-eval/helpers/test-env.ts create mode 100644 test/init-eval/nextjs.eval.test.ts create mode 100644 test/init-eval/python-flask.eval.test.ts create mode 100644 test/init-eval/react-vite.eval.test.ts create mode 100644 test/init-eval/sveltekit.eval.test.ts create mode 100644 test/init-eval/templates/express-app/.gitignore create mode 100644 test/init-eval/templates/express-app/package.json create mode 100644 test/init-eval/templates/express-app/src/index.ts create mode 100644 test/init-eval/templates/express-app/tsconfig.json create mode 100644 test/init-eval/templates/nextjs-app/.gitignore create mode 100644 test/init-eval/templates/nextjs-app/next.config.ts create mode 100644 test/init-eval/templates/nextjs-app/package.json create mode 100644 test/init-eval/templates/nextjs-app/src/app/layout.tsx create mode 100644 test/init-eval/templates/nextjs-app/src/app/page.tsx create mode 100644 test/init-eval/templates/nextjs-app/tsconfig.json create mode 100644 test/init-eval/templates/python-flask-app/.gitignore create mode 100644 test/init-eval/templates/python-flask-app/app.py create mode 100644 test/init-eval/templates/python-flask-app/requirements.txt create mode 100644 test/init-eval/templates/react-vite-app/.gitignore create mode 100644 test/init-eval/templates/react-vite-app/index.html create mode 100644 test/init-eval/templates/react-vite-app/package.json create mode 100644 test/init-eval/templates/react-vite-app/src/app.tsx create mode 100644 test/init-eval/templates/react-vite-app/src/main.tsx create mode 100644 test/init-eval/templates/react-vite-app/tsconfig.json create mode 100644 test/init-eval/templates/react-vite-app/vite.config.ts create mode 100644 test/init-eval/templates/sveltekit-app/.gitignore create mode 100644 test/init-eval/templates/sveltekit-app/package.json create mode 100644 test/init-eval/templates/sveltekit-app/src/routes/+page.svelte create mode 100644 test/init-eval/templates/sveltekit-app/svelte.config.js create mode 100644 test/init-eval/templates/sveltekit-app/tsconfig.json create mode 100644 test/init-eval/templates/sveltekit-app/vite.config.ts diff --git a/test/init-eval/express.eval.test.ts b/test/init-eval/express.eval.test.ts new file mode 100644 index 00000000..258c8849 --- /dev/null +++ b/test/init-eval/express.eval.test.ts @@ -0,0 +1,3 @@ +import { createEvalSuite } from "./helpers/create-eval-suite"; + +createEvalSuite("express"); diff --git a/test/init-eval/feature-docs.json b/test/init-eval/feature-docs.json new file mode 100644 index 00000000..87f94496 --- /dev/null +++ b/test/init-eval/feature-docs.json @@ -0,0 +1,59 @@ +{ + "$comment": [ + "Maps each platform to doc URLs used as ground truth by the LLM judge.", + "", + "Structure: { platformId: { featureName: [url, ...] } }", + " - Platform keys must match the `id` from helpers/platforms.ts", + " - Feature names are free-form strings (e.g. 'getting-started', 'errors')", + " - Each feature maps to an array of doc URLs (multiple pages allowed)", + " - Empty arrays mean 'no docs yet'; the judge skips the follows-docs criterion", + "", + "To add a new platform: add a key with its feature objects.", + "To add a new feature: add a new key under the platform with a URL array." + ], + + "nextjs": { + "getting-started": [], + "errors": [], + "logs": [], + "tracing": [], + "replay": [], + "metrics": [], + "sourcemaps": [], + "profiling": [] + }, + + "express": { + "getting-started": ["https://docs.sentry.io/platforms/javascript/guides/express/"], + "errors": ["https://docs.sentry.io/platforms/javascript/guides/express/usage/"], + "tracing": ["https://docs.sentry.io/platforms/javascript/guides/express/tracing/"], + "logs": ["https://docs.sentry.io/platforms/javascript/guides/express/logs/"], + "metrics": ["https://docs.sentry.io/platforms/javascript/guides/express/metrics/"], + "profiling": ["https://docs.sentry.io/platforms/javascript/guides/express/profiling/"] + }, + + "python-flask": { + "getting-started": [], + "errors": [], + "tracing": [], + "logs": [], + "metrics": [], + "profiling": [] + }, + + "sveltekit": { + "getting-started": [], + "errors": [], + "sourcemaps": [], + "replay": [], + "tracing": [] + }, + + "react-vite": { + "getting-started": [], + "errors": [], + "sourcemaps": [], + "replay": [], + "tracing": [] + } +} diff --git a/test/init-eval/helpers/assertions.ts b/test/init-eval/helpers/assertions.ts new file mode 100644 index 00000000..52633b95 --- /dev/null +++ b/test/init-eval/helpers/assertions.ts @@ -0,0 +1,98 @@ +import { execSync } from "node:child_process"; +import { readFileSync } from "node:fs"; +import { join } from "node:path"; +import type { Platform } from "./platforms.js"; +import type { WizardResult } from "./run-wizard.js"; + +export type AssertionFailure = { + check: string; + message: string; +}; + +/** + * Run hard pass/fail assertions on the wizard result. + * Returns an array of failures (empty = all passed). + */ +export function runAssertions( + projectDir: string, + platform: Platform, + result: WizardResult +): AssertionFailure[] { + const failures: AssertionFailure[] = []; + + // 1. Exit code 0 + if (result.exitCode !== 0) { + failures.push({ + check: "exit-code", + message: `Expected exit code 0, got ${result.exitCode}.\nstderr: ${result.stderr.slice(0, 500)}`, + }); + } + + // 2. SDK in dependencies + try { + const depContent = readFileSync( + join(projectDir, platform.depFile), + "utf-8" + ); + if (!depContent.includes(platform.sdkPackage)) { + failures.push({ + check: "sdk-installed", + message: `${platform.sdkPackage} not found in ${platform.depFile}`, + }); + } + } catch { + failures.push({ + check: "sdk-installed", + message: `Could not read ${platform.depFile}`, + }); + } + + // 3. Sentry.init present in changed or new files + const allContent = result.diff + Object.values(result.newFiles).join("\n"); + if (!platform.initPattern.test(allContent)) { + failures.push({ + check: "init-present", + message: `${platform.initPattern} not found in any changed or new files`, + }); + } + + // 4. No placeholder DSNs + const placeholderPatterns = [ + /___PUBLIC_DSN___/, + /YOUR_DSN_HERE/, + /https:\/\/examplePublicKey@o0\.ingest\.sentry\.io\/0/, + ]; + for (const pat of placeholderPatterns) { + if (pat.test(allContent)) { + failures.push({ + check: "no-placeholder-dsn", + message: `Placeholder DSN found: ${pat.source}`, + }); + } + } + + // 5. Project builds (if buildCmd set) + if (platform.buildCmd) { + try { + // Install deps first (wizard may have added new ones) + execSync(platform.installCmd, { + cwd: projectDir, + stdio: "pipe", + timeout: 120_000, + }); + execSync(platform.buildCmd, { + cwd: projectDir, + stdio: "pipe", + timeout: 120_000, + }); + } catch (err) { + const msg = err instanceof Error ? err.message : String(err); + failures.push({ + check: "build-succeeds", + message: `Build failed: ${msg.slice(0, 500)}`, + }); + } + } + + return failures; +} diff --git a/test/init-eval/helpers/create-eval-suite.ts b/test/init-eval/helpers/create-eval-suite.ts new file mode 100644 index 00000000..ca8ac2a1 --- /dev/null +++ b/test/init-eval/helpers/create-eval-suite.ts @@ -0,0 +1,57 @@ +import { afterAll, describe, expect, test } from "bun:test"; +import { runAssertions } from "./assertions"; +import { fetchDocsContent } from "./docs-fetcher"; +import { judgeFeature } from "./judge"; +import { getPlatform, WIZARD_FEATURE_IDS } from "./platforms"; +import { runWizard, type WizardResult } from "./run-wizard"; +import { createTestEnv } from "./test-env"; + +/** + * Creates a standard eval test suite for a given platform. + * + * Runs the wizard once with all features, then: + * 1. Code-based hard assertions (deterministic) + * 2. Per-feature LLM judge calls (one test per feature) + */ +export function createEvalSuite(platformId: string) { + const p = getPlatform(platformId); + const env = createTestEnv(p.templateDir); + let result: WizardResult; + + // Only pass features that are valid wizard --features flag values + const wizardFeatures = p.docs + .map((d) => d.feature) + .filter((f) => WIZARD_FEATURE_IDS.has(f)); + + afterAll(() => env.cleanup()); + + describe(`eval: ${p.name}`, () => { + test( + "wizard completes", + async () => { + result = await runWizard(env.projectDir, p, wizardFeatures); + expect(result.exitCode).toBe(0); + }, + p.timeout + ); + + test("hard assertions pass", async () => { + const failures = runAssertions(env.projectDir, p, result); + if (failures.length > 0) { + console.log("Assertion failures:", JSON.stringify(failures, null, 2)); + } + expect(failures).toEqual([]); + }, 120_000); + + // Per-feature LLM judge — one test per feature + for (const doc of p.docs) { + test(`judge: ${doc.feature}`, async () => { + const docsContent = await fetchDocsContent(doc.docsUrls); + const verdict = await judgeFeature(result, p, doc, docsContent); + if (verdict) { + expect(verdict.score).toBeGreaterThanOrEqual(0.5); + } + }, 60_000); + } + }); +} diff --git a/test/init-eval/helpers/docs-fetcher.ts b/test/init-eval/helpers/docs-fetcher.ts new file mode 100644 index 00000000..4fb9936d --- /dev/null +++ b/test/init-eval/helpers/docs-fetcher.ts @@ -0,0 +1,60 @@ +/** + * Fetch Sentry documentation pages and extract plain text for use as + * ground-truth reference material in LLM judge prompts. + * + * Accepts an array of URLs — fetches all in parallel and concatenates results. + * Returns "(no docs provided)" when the array is empty. + */ +export async function fetchDocsContent(urls: string[]): Promise { + if (urls.length === 0) { + return "(no docs provided)"; + } + + // Restore real fetch — test preload mocks it to catch accidental network + // calls, but we need real HTTP to reach docs.sentry.io. + const realFetch = (globalThis as { __originalFetch?: typeof fetch }) + .__originalFetch; + if (realFetch) { + globalThis.fetch = realFetch; + } + + const charBudgetPerUrl = Math.floor(6000 / urls.length); + + const results = await Promise.all( + urls.map((url) => fetchOne(url, charBudgetPerUrl)) + ); + return results.join("\n\n---\n\n"); +} + +async function fetchOne(url: string, charLimit: number): Promise { + try { + const res = await fetch(url, { + headers: { "User-Agent": "sentry-init-eval/1.0" }, + }); + + if (!res.ok) { + return `(failed to fetch ${url}: ${res.status})`; + } + + const html = await res.text(); + + // Strip HTML tags, collapse whitespace + const text = html + .replace(//gi, "") + .replace(//gi, "") + .replace(/<[^>]+>/g, " ") + .replace(/ /g, " ") + .replace(/&/g, "&") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/"/g, '"') + .replace(/'/g, "'") + .replace(/\s+/g, " ") + .trim(); + + return text.slice(0, charLimit); + } catch (err) { + const msg = err instanceof Error ? err.message : String(err); + return `(failed to fetch ${url}: ${msg})`; + } +} diff --git a/test/init-eval/helpers/judge.ts b/test/init-eval/helpers/judge.ts new file mode 100644 index 00000000..4b8ee863 --- /dev/null +++ b/test/init-eval/helpers/judge.ts @@ -0,0 +1,143 @@ +import type { FeatureDoc, Platform } from "./platforms.js"; +import type { WizardResult } from "./run-wizard.js"; + +export type JudgeCriterion = { + name: string; + /** true = pass, false = fail, "unknown" = judge can't determine */ + pass: boolean | "unknown"; + reason: string; +}; + +export type JudgeVerdict = { + criteria: JudgeCriterion[]; + /** Score from 0-1, computed only over criteria that aren't "unknown" */ + score: number; + summary: string; +}; + +/** + * Use an LLM judge to evaluate whether a **single feature** was correctly set + * up by the wizard. Returns null if OPENAI_API_KEY is not set. + * + * `docsContent` is the pre-fetched plain-text documentation to include as + * ground truth in the prompt. + */ +export async function judgeFeature( + result: WizardResult, + platform: Platform, + feature: FeatureDoc, + docsContent: string +): Promise { + const apiKey = process.env.OPENAI_API_KEY; + if (!apiKey) { + console.log( + ` [judge:${feature.feature}] Skipping LLM judge (no OPENAI_API_KEY set)` + ); + return null; + } + + // Restore real fetch — test preload mocks it to catch accidental network + // calls, but we need real HTTP for the OpenAI API. + const realFetch = (globalThis as { __originalFetch?: typeof fetch }) + .__originalFetch; + if (realFetch) { + globalThis.fetch = realFetch; + } + + // Dynamic import so we don't fail when the package isn't installed + const { default: OpenAI } = await import("openai"); + const client = new OpenAI({ apiKey }); + + const newFilesSection = Object.entries(result.newFiles) + .map(([path, content]) => `### ${path}\n\`\`\`\n${content}\n\`\`\``) + .join("\n\n"); + + const prompt = `You are evaluating whether **${feature.feature}** was correctly set up in a **${platform.name}** project by a Sentry SDK wizard. + +## Official Sentry documentation for ${feature.feature} +${docsContent} + +## Changes made by wizard (git diff) +\`\`\`diff +${result.diff.slice(0, 20_000)} +\`\`\` + +## New files created by wizard +${newFilesSection.slice(0, 20_000) || "(none)"} + +## Wizard output +stdout: ${result.stdout.slice(0, 2000)} +stderr: ${result.stderr.slice(0, 2000)} + +Score each criterion as true (pass), false (fail), or "unknown" (cannot determine from the available information): +1. **feature-initialized** — The ${feature.feature} feature is correctly initialized per the documentation +2. **correct-imports** — Correct imports and SDK packages used for ${feature.feature} +3. **no-syntax-errors** — No syntax errors or broken imports in ${feature.feature}-related code +4. **follows-docs** — ${feature.feature} configuration follows documentation recommendations + +Return ONLY valid JSON with this structure: +{ + "criteria": [ + {"name": "feature-initialized", "pass": true, "reason": "..."}, + {"name": "correct-imports", "pass": true, "reason": "..."}, + {"name": "no-syntax-errors", "pass": true, "reason": "..."}, + {"name": "follows-docs", "pass": "unknown", "reason": "..."} + ], + "summary": "Brief overall assessment of ${feature.feature} setup" +}`; + + const response = await client.chat.completions.create({ + model: "gpt-4o", + max_tokens: 1024, + messages: [{ role: "user", content: prompt }], + }); + + const text = response.choices[0]?.message?.content ?? ""; + + // Extract JSON from response (handle markdown code blocks) + const jsonMatch = text.match(/\{[\s\S]*\}/); + if (!jsonMatch) { + console.log( + ` [judge:${feature.feature}] Failed to parse judge response:`, + text.slice(0, 200) + ); + return null; + } + + let parsed: { criteria: JudgeCriterion[]; summary: string }; + try { + parsed = JSON.parse(jsonMatch[0]); + } catch { + console.log( + ` [judge:${feature.feature}] Invalid JSON in response:`, + jsonMatch[0].slice(0, 200) + ); + return null; + } + + // Score: ignore "unknown" criteria, only count pass/fail + const gradable = parsed.criteria.filter((c) => c.pass !== "unknown"); + const passing = gradable.filter((c) => c.pass === true).length; + const total = gradable.length; + const score = total > 0 ? passing / total : 0; + + const verdict: JudgeVerdict = { + criteria: parsed.criteria, + score, + summary: parsed.summary, + }; + + // Log for visibility in test output + console.log( + ` [judge:${feature.feature}] Score: ${passing}/${total} (${(score * 100).toFixed(0)}%)` + ); + for (const c of parsed.criteria) { + let icon = "FAIL"; + if (c.pass === "unknown") icon = "SKIP"; + else if (c.pass === true) icon = "PASS"; + console.log(` ${icon} ${c.name}: ${c.reason}`); + } + console.log(` [judge:${feature.feature}] Summary: ${parsed.summary}`); + + return verdict; +} diff --git a/test/init-eval/helpers/platforms.ts b/test/init-eval/helpers/platforms.ts new file mode 100644 index 00000000..402e2c53 --- /dev/null +++ b/test/init-eval/helpers/platforms.ts @@ -0,0 +1,129 @@ +import { readFileSync } from "node:fs"; +import { join } from "node:path"; + +export type FeatureId = + | "errors" + | "tracing" + | "logs" + | "replay" + | "metrics" + | "sourcemaps" + | "profiling"; + +/** Feature IDs that are valid for the wizard `--features` flag. */ +export const WIZARD_FEATURE_IDS: Set = new Set([ + "errors", + "tracing", + "logs", + "replay", + "metrics", + "sourcemaps", + "profiling", +]); + +export type FeatureDoc = { + feature: string; + docsUrls: string[]; +}; + +export type Platform = { + id: string; + name: string; + templateDir: string; + sdkPackage: string; + depFile: string; + docs: FeatureDoc[]; + buildCmd?: string; + installCmd: string; + initPattern: RegExp; + timeout: number; +}; + +const TEMPLATES_DIR = join(import.meta.dir, "../templates"); + +/** Load feature docs from the external JSON config. */ +const featureDocsRaw: Record> = JSON.parse( + readFileSync(join(import.meta.dir, "../feature-docs.json"), "utf-8") +); + +function getDocs(platformId: string): FeatureDoc[] { + const entry = featureDocsRaw[platformId]; + if (!entry) { + throw new Error( + `No feature docs found for platform "${platformId}" in feature-docs.json` + ); + } + return Object.entries(entry).map(([feature, urls]) => ({ + feature, + docsUrls: urls, + })); +} + +export const PLATFORMS: Platform[] = [ + { + id: "nextjs", + name: "Next.js", + templateDir: join(TEMPLATES_DIR, "nextjs-app"), + sdkPackage: "@sentry/nextjs", + depFile: "package.json", + docs: getDocs("nextjs"), + installCmd: "npm install", + buildCmd: "npm run build", + initPattern: /Sentry\.init/, + timeout: 300_000, + }, + { + id: "express", + name: "Express", + templateDir: join(TEMPLATES_DIR, "express-app"), + sdkPackage: "@sentry/node", + depFile: "package.json", + docs: getDocs("express"), + installCmd: "npm install", + buildCmd: "npx tsc --noEmit", + initPattern: /Sentry\.init/, + timeout: 300_000, + }, + { + id: "python-flask", + name: "Flask", + templateDir: join(TEMPLATES_DIR, "python-flask-app"), + sdkPackage: "sentry-sdk", + depFile: "requirements.txt", + docs: getDocs("python-flask"), + installCmd: "pip install -r requirements.txt", + buildCmd: "python -m compileall -q .", + initPattern: /sentry_sdk\.init/, + timeout: 300_000, + }, + { + id: "sveltekit", + name: "SvelteKit", + templateDir: join(TEMPLATES_DIR, "sveltekit-app"), + sdkPackage: "@sentry/sveltekit", + depFile: "package.json", + docs: getDocs("sveltekit"), + installCmd: "npm install", + buildCmd: "npm run build", + initPattern: /Sentry\.init/, + timeout: 300_000, + }, + { + id: "react-vite", + name: "React + Vite", + templateDir: join(TEMPLATES_DIR, "react-vite-app"), + sdkPackage: "@sentry/react", + depFile: "package.json", + docs: getDocs("react-vite"), + installCmd: "npm install", + buildCmd: "npm run build", + initPattern: /Sentry\.init/, + timeout: 300_000, + }, +]; + +export function getPlatform(id: string): Platform { + const p = PLATFORMS.find((entry) => entry.id === id); + if (!p) throw new Error(`Unknown platform: ${id}`); + return p; +} diff --git a/test/init-eval/helpers/run-wizard.ts b/test/init-eval/helpers/run-wizard.ts new file mode 100644 index 00000000..a7561816 --- /dev/null +++ b/test/init-eval/helpers/run-wizard.ts @@ -0,0 +1,117 @@ +import { execSync } from "node:child_process"; +import { readFileSync } from "node:fs"; +import { join, resolve } from "node:path"; +import { getCliCommand } from "../../fixture.js"; +import type { Platform } from "./platforms.js"; + +/** Root of the CLI repo (three levels up from this file). */ +const CLI_ROOT = resolve(import.meta.dir, "../../.."); + +export type WizardResult = { + exitCode: number; + stdout: string; + stderr: string; + diff: string; + newFiles: Record; +}; + +/** + * Run `sentry init --yes --force` on a project directory and capture results. + * When `features` is provided, passes `--features ` to the wizard. + */ +export async function runWizard( + projectDir: string, + platform: Platform, + features?: string[] +): Promise { + // Resolve relative paths (e.g. "src/bin.ts") against the CLI repo root, + // since the wizard spawns with cwd set to the temp project directory. + const cmd = getCliCommand().map((part) => + part.includes("/") ? resolve(CLI_ROOT, part) : part + ); + const mastraUrl = process.env.MASTRA_API_URL; + if (!mastraUrl) { + throw new Error("MASTRA_API_URL env var is required to run init evals"); + } + + // Install dependencies first so the wizard sees a realistic project + try { + execSync(platform.installCmd, { + cwd: projectDir, + stdio: "pipe", + timeout: 120_000, + }); + // Commit lock files so they don't show up in the diff + execSync("git add -A && git commit -m deps --no-gpg-sign --allow-empty", { + cwd: projectDir, + stdio: "pipe", + env: { + ...process.env, + GIT_AUTHOR_NAME: "test", + GIT_AUTHOR_EMAIL: "test@test.com", + GIT_COMMITTER_NAME: "test", + GIT_COMMITTER_EMAIL: "test@test.com", + }, + }); + } catch { + // Some templates (e.g. Python) might not need install + } + + const initArgs = [...cmd, "init", "--yes", "--force"]; + if (features && features.length > 0) { + initArgs.push("--features", features.join(",")); + } + + const proc = Bun.spawn(initArgs, { + cwd: projectDir, + stdout: "pipe", + stderr: "pipe", + env: { + ...process.env, + // Override the hardcoded Mastra URL to point at local/test server + SENTRY_WIZARD_API_URL: mastraUrl, + // Disable telemetry + SENTRY_CLI_NO_TELEMETRY: "1", + }, + }); + + const [stdout, stderr] = await Promise.all([ + new Response(proc.stdout).text(), + new Response(proc.stderr).text(), + ]); + + const exitCode = await proc.exited; + + // Capture git diff (staged + unstaged changes since last commit) + let diff = ""; + try { + diff = execSync("git diff HEAD", { + cwd: projectDir, + encoding: "utf-8", + maxBuffer: 1024 * 1024, + }); + } catch { + // No diff available + } + + // Capture new untracked files + const newFiles: Record = {}; + try { + const untracked = execSync("git ls-files --others --exclude-standard", { + cwd: projectDir, + encoding: "utf-8", + }).trim(); + + for (const file of untracked.split("\n").filter(Boolean)) { + try { + newFiles[file] = readFileSync(join(projectDir, file), "utf-8"); + } catch { + // Binary or unreadable + } + } + } catch { + // No untracked files + } + + return { exitCode, stdout, stderr, diff, newFiles }; +} diff --git a/test/init-eval/helpers/test-env.ts b/test/init-eval/helpers/test-env.ts new file mode 100644 index 00000000..94abfb33 --- /dev/null +++ b/test/init-eval/helpers/test-env.ts @@ -0,0 +1,46 @@ +import { execSync } from "node:child_process"; +import { cpSync, mkdirSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +export type TestEnv = { + projectDir: string; + cleanup: () => void; +}; + +/** + * Copy a template project into an isolated temp directory with git initialized. + * Returns the project dir and a cleanup function. + */ +export function createTestEnv(templateDir: string): TestEnv { + const rand = Math.random().toString(36).slice(2, 8); + const name = templateDir.split("/").pop() ?? "project"; + const projectDir = join(tmpdir(), "sentry-init-eval", `${name}-${rand}`); + + mkdirSync(projectDir, { recursive: true }); + cpSync(templateDir, projectDir, { recursive: true }); + + // Initialize git so we can diff after the wizard runs + execSync("git init && git add -A && git commit -m init --no-gpg-sign", { + cwd: projectDir, + stdio: "pipe", + env: { + ...process.env, + GIT_AUTHOR_NAME: "test", + GIT_AUTHOR_EMAIL: "test@test.com", + GIT_COMMITTER_NAME: "test", + GIT_COMMITTER_EMAIL: "test@test.com", + }, + }); + + const cleanup = () => { + if (process.env.KEEP_TEMP) return; + try { + rmSync(projectDir, { recursive: true, force: true }); + } catch { + // ignore + } + }; + + return { projectDir, cleanup }; +} diff --git a/test/init-eval/nextjs.eval.test.ts b/test/init-eval/nextjs.eval.test.ts new file mode 100644 index 00000000..1938f0d5 --- /dev/null +++ b/test/init-eval/nextjs.eval.test.ts @@ -0,0 +1,3 @@ +import { createEvalSuite } from "./helpers/create-eval-suite"; + +createEvalSuite("nextjs"); diff --git a/test/init-eval/python-flask.eval.test.ts b/test/init-eval/python-flask.eval.test.ts new file mode 100644 index 00000000..95e1528e --- /dev/null +++ b/test/init-eval/python-flask.eval.test.ts @@ -0,0 +1,3 @@ +import { createEvalSuite } from "./helpers/create-eval-suite"; + +createEvalSuite("python-flask"); diff --git a/test/init-eval/react-vite.eval.test.ts b/test/init-eval/react-vite.eval.test.ts new file mode 100644 index 00000000..e4d844c8 --- /dev/null +++ b/test/init-eval/react-vite.eval.test.ts @@ -0,0 +1,3 @@ +import { createEvalSuite } from "./helpers/create-eval-suite"; + +createEvalSuite("react-vite"); diff --git a/test/init-eval/sveltekit.eval.test.ts b/test/init-eval/sveltekit.eval.test.ts new file mode 100644 index 00000000..b44beae8 --- /dev/null +++ b/test/init-eval/sveltekit.eval.test.ts @@ -0,0 +1,3 @@ +import { createEvalSuite } from "./helpers/create-eval-suite"; + +createEvalSuite("sveltekit"); diff --git a/test/init-eval/templates/express-app/.gitignore b/test/init-eval/templates/express-app/.gitignore new file mode 100644 index 00000000..b9470778 --- /dev/null +++ b/test/init-eval/templates/express-app/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +dist/ diff --git a/test/init-eval/templates/express-app/package.json b/test/init-eval/templates/express-app/package.json new file mode 100644 index 00000000..60915acd --- /dev/null +++ b/test/init-eval/templates/express-app/package.json @@ -0,0 +1,17 @@ +{ + "name": "express-app", + "version": "0.1.0", + "private": true, + "scripts": { + "start": "node dist/index.js", + "build": "tsc" + }, + "dependencies": { + "express": "^4.21.0" + }, + "devDependencies": { + "@types/express": "^5", + "@types/node": "^22", + "typescript": "^5" + } +} diff --git a/test/init-eval/templates/express-app/src/index.ts b/test/init-eval/templates/express-app/src/index.ts new file mode 100644 index 00000000..ed2c1ef0 --- /dev/null +++ b/test/init-eval/templates/express-app/src/index.ts @@ -0,0 +1,12 @@ +import express from "express"; + +const app = express(); +const port = process.env.PORT || 3000; + +app.get("/", (_req, res) => { + res.json({ message: "Hello World" }); +}); + +app.listen(port, () => { + console.log(`Server running on port ${port}`); +}); diff --git a/test/init-eval/templates/express-app/tsconfig.json b/test/init-eval/templates/express-app/tsconfig.json new file mode 100644 index 00000000..856416bb --- /dev/null +++ b/test/init-eval/templates/express-app/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "commonjs", + "lib": ["ES2022"], + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/test/init-eval/templates/nextjs-app/.gitignore b/test/init-eval/templates/nextjs-app/.gitignore new file mode 100644 index 00000000..7c8ed234 --- /dev/null +++ b/test/init-eval/templates/nextjs-app/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +.next/ +out/ diff --git a/test/init-eval/templates/nextjs-app/next.config.ts b/test/init-eval/templates/nextjs-app/next.config.ts new file mode 100644 index 00000000..cb651cdc --- /dev/null +++ b/test/init-eval/templates/nextjs-app/next.config.ts @@ -0,0 +1,5 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = {}; + +export default nextConfig; diff --git a/test/init-eval/templates/nextjs-app/package.json b/test/init-eval/templates/nextjs-app/package.json new file mode 100644 index 00000000..22af217c --- /dev/null +++ b/test/init-eval/templates/nextjs-app/package.json @@ -0,0 +1,21 @@ +{ + "name": "nextjs-app", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "next": "^15.1.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@types/node": "^22", + "@types/react": "^19", + "@types/react-dom": "^19", + "typescript": "^5" + } +} diff --git a/test/init-eval/templates/nextjs-app/src/app/layout.tsx b/test/init-eval/templates/nextjs-app/src/app/layout.tsx new file mode 100644 index 00000000..588e8851 --- /dev/null +++ b/test/init-eval/templates/nextjs-app/src/app/layout.tsx @@ -0,0 +1,16 @@ +export const metadata = { + title: "Test App", + description: "A test Next.js app", +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + {children} + + ); +} diff --git a/test/init-eval/templates/nextjs-app/src/app/page.tsx b/test/init-eval/templates/nextjs-app/src/app/page.tsx new file mode 100644 index 00000000..aa58cf37 --- /dev/null +++ b/test/init-eval/templates/nextjs-app/src/app/page.tsx @@ -0,0 +1,3 @@ +export default function Home() { + return

Hello World

; +} diff --git a/test/init-eval/templates/nextjs-app/tsconfig.json b/test/init-eval/templates/nextjs-app/tsconfig.json new file mode 100644 index 00000000..fba2bf37 --- /dev/null +++ b/test/init-eval/templates/nextjs-app/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [{ "name": "next" }], + "paths": { "@/*": ["./src/*"] } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/test/init-eval/templates/python-flask-app/.gitignore b/test/init-eval/templates/python-flask-app/.gitignore new file mode 100644 index 00000000..65776d12 --- /dev/null +++ b/test/init-eval/templates/python-flask-app/.gitignore @@ -0,0 +1,4 @@ +__pycache__/ +*.py[cod] +.venv/ +venv/ diff --git a/test/init-eval/templates/python-flask-app/app.py b/test/init-eval/templates/python-flask-app/app.py new file mode 100644 index 00000000..cfa9e477 --- /dev/null +++ b/test/init-eval/templates/python-flask-app/app.py @@ -0,0 +1,12 @@ +from flask import Flask + +app = Flask(__name__) + + +@app.route("/") +def hello(): + return {"message": "Hello World"} + + +if __name__ == "__main__": + app.run(debug=True) diff --git a/test/init-eval/templates/python-flask-app/requirements.txt b/test/init-eval/templates/python-flask-app/requirements.txt new file mode 100644 index 00000000..001e7c4a --- /dev/null +++ b/test/init-eval/templates/python-flask-app/requirements.txt @@ -0,0 +1 @@ +flask>=3.0 diff --git a/test/init-eval/templates/react-vite-app/.gitignore b/test/init-eval/templates/react-vite-app/.gitignore new file mode 100644 index 00000000..b9470778 --- /dev/null +++ b/test/init-eval/templates/react-vite-app/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +dist/ diff --git a/test/init-eval/templates/react-vite-app/index.html b/test/init-eval/templates/react-vite-app/index.html new file mode 100644 index 00000000..71e5e784 --- /dev/null +++ b/test/init-eval/templates/react-vite-app/index.html @@ -0,0 +1,12 @@ + + + + + + React App + + +
+ + + diff --git a/test/init-eval/templates/react-vite-app/package.json b/test/init-eval/templates/react-vite-app/package.json new file mode 100644 index 00000000..669dc475 --- /dev/null +++ b/test/init-eval/templates/react-vite-app/package.json @@ -0,0 +1,22 @@ +{ + "name": "react-vite-app", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@types/react": "^19", + "@types/react-dom": "^19", + "@vitejs/plugin-react": "^4.3.0", + "typescript": "^5", + "vite": "^6.0.0" + } +} diff --git a/test/init-eval/templates/react-vite-app/src/app.tsx b/test/init-eval/templates/react-vite-app/src/app.tsx new file mode 100644 index 00000000..be4a1202 --- /dev/null +++ b/test/init-eval/templates/react-vite-app/src/app.tsx @@ -0,0 +1,5 @@ +function App() { + return

Hello World

; +} + +export default App; diff --git a/test/init-eval/templates/react-vite-app/src/main.tsx b/test/init-eval/templates/react-vite-app/src/main.tsx new file mode 100644 index 00000000..74ab28da --- /dev/null +++ b/test/init-eval/templates/react-vite-app/src/main.tsx @@ -0,0 +1,9 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./app"; + +ReactDOM.createRoot(document.getElementById("root")!).render( + + + , +); diff --git a/test/init-eval/templates/react-vite-app/tsconfig.json b/test/init-eval/templates/react-vite-app/tsconfig.json new file mode 100644 index 00000000..9e82e3ff --- /dev/null +++ b/test/init-eval/templates/react-vite-app/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/test/init-eval/templates/react-vite-app/vite.config.ts b/test/init-eval/templates/react-vite-app/vite.config.ts new file mode 100644 index 00000000..58676f78 --- /dev/null +++ b/test/init-eval/templates/react-vite-app/vite.config.ts @@ -0,0 +1,6 @@ +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; + +export default defineConfig({ + plugins: [react()], +}); diff --git a/test/init-eval/templates/sveltekit-app/.gitignore b/test/init-eval/templates/sveltekit-app/.gitignore new file mode 100644 index 00000000..31fda85b --- /dev/null +++ b/test/init-eval/templates/sveltekit-app/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +.svelte-kit/ +build/ diff --git a/test/init-eval/templates/sveltekit-app/package.json b/test/init-eval/templates/sveltekit-app/package.json new file mode 100644 index 00000000..77ae4f30 --- /dev/null +++ b/test/init-eval/templates/sveltekit-app/package.json @@ -0,0 +1,18 @@ +{ + "name": "sveltekit-app", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview" + }, + "devDependencies": { + "@sveltejs/adapter-auto": "^4.0.0", + "@sveltejs/kit": "^2.16.0", + "@sveltejs/vite-plugin-svelte": "^5.0.0", + "svelte": "^5.0.0", + "typescript": "^5", + "vite": "^6.0.0" + } +} diff --git a/test/init-eval/templates/sveltekit-app/src/routes/+page.svelte b/test/init-eval/templates/sveltekit-app/src/routes/+page.svelte new file mode 100644 index 00000000..f3e333e8 --- /dev/null +++ b/test/init-eval/templates/sveltekit-app/src/routes/+page.svelte @@ -0,0 +1 @@ +

Hello World

diff --git a/test/init-eval/templates/sveltekit-app/svelte.config.js b/test/init-eval/templates/sveltekit-app/svelte.config.js new file mode 100644 index 00000000..3fc56b9c --- /dev/null +++ b/test/init-eval/templates/sveltekit-app/svelte.config.js @@ -0,0 +1,12 @@ +import adapter from "@sveltejs/adapter-auto"; +import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + preprocess: vitePreprocess(), + kit: { + adapter: adapter(), + }, +}; + +export default config; diff --git a/test/init-eval/templates/sveltekit-app/tsconfig.json b/test/init-eval/templates/sveltekit-app/tsconfig.json new file mode 100644 index 00000000..43447105 --- /dev/null +++ b/test/init-eval/templates/sveltekit-app/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + } +} diff --git a/test/init-eval/templates/sveltekit-app/vite.config.ts b/test/init-eval/templates/sveltekit-app/vite.config.ts new file mode 100644 index 00000000..80864b9d --- /dev/null +++ b/test/init-eval/templates/sveltekit-app/vite.config.ts @@ -0,0 +1,6 @@ +import { sveltekit } from "@sveltejs/kit/vite"; +import { defineConfig } from "vite"; + +export default defineConfig({ + plugins: [sveltekit()], +}); From 476bcbc2169c043c71e4b89a08b7b9e25b29873d Mon Sep 17 00:00:00 2001 From: betegon Date: Mon, 23 Feb 2026 17:25:43 +0100 Subject: [PATCH 13/26] ci: add workflow_dispatch CI job for init-eval tests Add a separate workflow for running init-eval tests on demand. Supports running a single platform or all platforms via matrix. Uses the init-eval GitHub environment for MASTRA_API_URL and OPENAI_API_KEY secrets. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/init-eval.yml | 47 +++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 .github/workflows/init-eval.yml diff --git a/.github/workflows/init-eval.yml b/.github/workflows/init-eval.yml new file mode 100644 index 00000000..f3e02e85 --- /dev/null +++ b/.github/workflows/init-eval.yml @@ -0,0 +1,47 @@ +name: Init Eval + +on: + workflow_dispatch: + inputs: + platform: + description: "Platform to evaluate (or 'all')" + required: true + default: all + type: choice + options: + - all + - express + - nextjs + - python-flask + - react-vite + - sveltekit + +jobs: + eval: + name: Eval ${{ matrix.platform }} + runs-on: ubuntu-latest + environment: init-eval + strategy: + fail-fast: false + matrix: + platform: ${{ inputs.platform == 'all' + && fromJson('["express","nextjs","python-flask","react-vite","sveltekit"]') + || fromJson(format('["{0}"]', inputs.platform)) }} + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - uses: actions/cache@v4 + id: cache + with: + path: node_modules + key: node-modules-${{ hashFiles('bun.lock', 'patches/**') }} + - if: steps.cache.outputs.cache-hit != 'true' + run: bun install --frozen-lockfile + - name: Run eval + env: + MASTRA_API_URL: ${{ secrets.MASTRA_API_URL }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + run: bun test ./test/init-eval/${{ matrix.platform }}.eval.test.ts --timeout 600000 From 193a467b8a2857fc96cef3e1d7c1233fa797fe3f Mon Sep 17 00:00:00 2001 From: betegon Date: Mon, 23 Feb 2026 19:15:37 +0100 Subject: [PATCH 14/26] fix(init): use .md URL conversion in eval docs-fetcher Store python-fastapi doc URLs as base paths (with trailing slash) like other platforms, and convert to .md at fetch time. This mirrors the pattern in cli-init-api and lets us return clean markdown directly instead of stripping HTML tags. Co-Authored-By: Claude Opus 4.6 --- test/init-eval/feature-docs.json | 8 +++++++ test/init-eval/helpers/docs-fetcher.ts | 30 +++++++++----------------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/test/init-eval/feature-docs.json b/test/init-eval/feature-docs.json index 87f94496..d2fb118b 100644 --- a/test/init-eval/feature-docs.json +++ b/test/init-eval/feature-docs.json @@ -41,6 +41,14 @@ "profiling": [] }, + "python-fastapi": { + "getting-started": ["https://docs.sentry.io/platforms/python/integrations/fastapi/"], + "errors": ["https://docs.sentry.io/platforms/python/integrations/fastapi/"], + "tracing": ["https://docs.sentry.io/platforms/python/integrations/fastapi/"], + "logs": ["https://docs.sentry.io/platforms/python/integrations/fastapi/"], + "profiling": ["https://docs.sentry.io/platforms/python/integrations/fastapi/"] + }, + "sveltekit": { "getting-started": [], "errors": [], diff --git a/test/init-eval/helpers/docs-fetcher.ts b/test/init-eval/helpers/docs-fetcher.ts index 4fb9936d..e4fe2325 100644 --- a/test/init-eval/helpers/docs-fetcher.ts +++ b/test/init-eval/helpers/docs-fetcher.ts @@ -1,3 +1,7 @@ +function toMarkdownUrl(docsUrl: string): string { + return docsUrl.replace(/\/+$/, "") + ".md"; +} + /** * Fetch Sentry documentation pages and extract plain text for use as * ground-truth reference material in LLM judge prompts. @@ -27,34 +31,20 @@ export async function fetchDocsContent(urls: string[]): Promise { } async function fetchOne(url: string, charLimit: number): Promise { + const mdUrl = toMarkdownUrl(url); try { - const res = await fetch(url, { + const res = await fetch(mdUrl, { headers: { "User-Agent": "sentry-init-eval/1.0" }, }); if (!res.ok) { - return `(failed to fetch ${url}: ${res.status})`; + return `(failed to fetch ${mdUrl}: ${res.status})`; } - const html = await res.text(); - - // Strip HTML tags, collapse whitespace - const text = html - .replace(//gi, "") - .replace(//gi, "") - .replace(/<[^>]+>/g, " ") - .replace(/ /g, " ") - .replace(/&/g, "&") - .replace(/</g, "<") - .replace(/>/g, ">") - .replace(/"/g, '"') - .replace(/'/g, "'") - .replace(/\s+/g, " ") - .trim(); - - return text.slice(0, charLimit); + const text = await res.text(); + return text.trim().slice(0, charLimit); } catch (err) { const msg = err instanceof Error ? err.message : String(err); - return `(failed to fetch ${url}: ${msg})`; + return `(failed to fetch ${mdUrl}: ${msg})`; } } From bcb10f285d890b2913621a0652345a5791a45f5d Mon Sep 17 00:00:00 2001 From: betegon Date: Mon, 23 Feb 2026 20:02:21 +0100 Subject: [PATCH 15/26] feat(init): add flask and python profiling doc URLs Add Sentry doc URLs for python-flask (getting-started, errors, tracing, logs, profiling) and add the shared python/profiling page to both flask and fastapi profiling entries. Co-Authored-By: Claude Opus 4.6 --- test/init-eval/feature-docs.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/init-eval/feature-docs.json b/test/init-eval/feature-docs.json index d2fb118b..7eda00c0 100644 --- a/test/init-eval/feature-docs.json +++ b/test/init-eval/feature-docs.json @@ -33,12 +33,12 @@ }, "python-flask": { - "getting-started": [], - "errors": [], - "tracing": [], - "logs": [], + "getting-started": ["https://docs.sentry.io/platforms/python/integrations/flask/"], + "errors": ["https://docs.sentry.io/platforms/python/integrations/flask/"], + "tracing": ["https://docs.sentry.io/platforms/python/integrations/flask/"], + "logs": ["https://docs.sentry.io/platforms/python/integrations/flask/"], "metrics": [], - "profiling": [] + "profiling": ["https://docs.sentry.io/platforms/python/integrations/flask/", "https://docs.sentry.io/platforms/python/profiling/"] }, "python-fastapi": { @@ -46,7 +46,7 @@ "errors": ["https://docs.sentry.io/platforms/python/integrations/fastapi/"], "tracing": ["https://docs.sentry.io/platforms/python/integrations/fastapi/"], "logs": ["https://docs.sentry.io/platforms/python/integrations/fastapi/"], - "profiling": ["https://docs.sentry.io/platforms/python/integrations/fastapi/"] + "profiling": ["https://docs.sentry.io/platforms/python/integrations/fastapi/", "https://docs.sentry.io/platforms/python/profiling/"] }, "sveltekit": { From 129e7b75bc7a6fde47bf5fb1a749d31147a9a9d1 Mon Sep 17 00:00:00 2001 From: betegon Date: Mon, 23 Feb 2026 20:54:08 +0100 Subject: [PATCH 16/26] feat(init): add nextjs doc URLs for eval ground truth Add Sentry doc URLs for all nextjs features: getting-started, errors, logs, tracing, session replay, metrics, and profiling (browser + node). Sourcemaps left empty for now. Co-Authored-By: Claude Opus 4.6 --- test/init-eval/feature-docs.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/init-eval/feature-docs.json b/test/init-eval/feature-docs.json index 7eda00c0..080a2c76 100644 --- a/test/init-eval/feature-docs.json +++ b/test/init-eval/feature-docs.json @@ -13,14 +13,14 @@ ], "nextjs": { - "getting-started": [], - "errors": [], - "logs": [], - "tracing": [], - "replay": [], - "metrics": [], + "getting-started": ["https://docs.sentry.io/platforms/javascript/guides/nextjs/", "https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/", "https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/pages-router/"], + "errors": ["https://docs.sentry.io/platforms/javascript/guides/nextjs/", "https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/", "https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/pages-router/", "https://docs.sentry.io/platforms/javascript/guides/nextjs/capturing-errors/"], + "logs": ["https://docs.sentry.io/platforms/javascript/guides/nextjs/logs/"], + "tracing": ["https://docs.sentry.io/platforms/javascript/guides/nextjs/tracing/"], + "replay": ["https://docs.sentry.io/platforms/javascript/guides/nextjs/session-replay/"], + "metrics": ["https://docs.sentry.io/platforms/javascript/guides/nextjs/metrics/"], "sourcemaps": [], - "profiling": [] + "profiling": ["https://docs.sentry.io/platforms/javascript/guides/nextjs/profiling/browser/", "https://docs.sentry.io/platforms/javascript/guides/nextjs/profiling/node/"] }, "express": { From b6c10b79343821dff491e8d844fed82987b25053 Mon Sep 17 00:00:00 2001 From: betegon Date: Mon, 23 Feb 2026 20:58:28 +0100 Subject: [PATCH 17/26] feat(init): add sveltekit doc URLs for eval ground truth Add Sentry doc URLs for sveltekit features and add missing logs, metrics, and profiling features to the platform entry. Co-Authored-By: Claude Opus 4.6 --- test/init-eval/feature-docs.json | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/test/init-eval/feature-docs.json b/test/init-eval/feature-docs.json index 080a2c76..e649e214 100644 --- a/test/init-eval/feature-docs.json +++ b/test/init-eval/feature-docs.json @@ -50,11 +50,14 @@ }, "sveltekit": { - "getting-started": [], - "errors": [], + "getting-started": ["https://docs.sentry.io/platforms/javascript/guides/sveltekit/", "https://docs.sentry.io/platforms/javascript/guides/sveltekit/manual-setup/"], + "errors": ["https://docs.sentry.io/platforms/javascript/guides/sveltekit/", "https://docs.sentry.io/platforms/javascript/guides/sveltekit/manual-setup/", "https://docs.sentry.io/platforms/javascript/guides/sveltekit/usage/"], "sourcemaps": [], - "replay": [], - "tracing": [] + "replay": ["https://docs.sentry.io/platforms/javascript/guides/sveltekit/session-replay/"], + "tracing": ["https://docs.sentry.io/platforms/javascript/guides/sveltekit/tracing/"], + "logs": ["https://docs.sentry.io/platforms/javascript/guides/sveltekit/logs/"], + "metrics": ["https://docs.sentry.io/platforms/javascript/guides/sveltekit/metrics/"], + "profiling": ["https://docs.sentry.io/platforms/javascript/guides/sveltekit/profiling/"] }, "react-vite": { From a8156c0b5a7c146758ba3ceaf0ccf94a38eb591a Mon Sep 17 00:00:00 2001 From: betegon Date: Mon, 23 Feb 2026 21:01:08 +0100 Subject: [PATCH 18/26] feat(init): add react-vite doc URLs for eval ground truth Add Sentry doc URLs for react-vite features and add missing logs, metrics, and profiling features to the platform entry. Co-Authored-By: Claude Opus 4.6 --- test/init-eval/feature-docs.json | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/test/init-eval/feature-docs.json b/test/init-eval/feature-docs.json index e649e214..16855def 100644 --- a/test/init-eval/feature-docs.json +++ b/test/init-eval/feature-docs.json @@ -61,10 +61,13 @@ }, "react-vite": { - "getting-started": [], - "errors": [], + "getting-started": ["https://docs.sentry.io/platforms/javascript/guides/react/"], + "errors": ["https://docs.sentry.io/platforms/javascript/guides/react/", "https://docs.sentry.io/platforms/javascript/guides/react/usage/"], "sourcemaps": [], - "replay": [], - "tracing": [] + "replay": ["https://docs.sentry.io/platforms/javascript/guides/react/session-replay/"], + "tracing": ["https://docs.sentry.io/platforms/javascript/guides/react/tracing/"], + "logs": ["https://docs.sentry.io/platforms/javascript/guides/react/logs/"], + "metrics": ["https://docs.sentry.io/platforms/javascript/guides/react/metrics/"], + "profiling": ["https://docs.sentry.io/platforms/javascript/guides/react/profiling/"] } } From 1d29ebdd17fa82d5a8cfa2837f1f25adf2b76e3e Mon Sep 17 00:00:00 2001 From: betegon Date: Mon, 23 Feb 2026 22:07:52 +0100 Subject: [PATCH 19/26] fix(init): use venv for flask build check and remove opencode-lore dep Flask eval was using bare `pip install` which fails when pip isn't on PATH. Use the same venv pattern as fastapi. Also remove accidental opencode-lore runtime dependency. Co-Authored-By: Claude Opus 4.6 --- package.json | 3 --- test/init-eval/helpers/platforms.ts | 16 ++++++++++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 555ce054..3ebecbce 100644 --- a/package.json +++ b/package.json @@ -67,8 +67,5 @@ "packageManager": "bun@1.3.9", "patchedDependencies": { "@stricli/core@1.2.5": "patches/@stricli%2Fcore@1.2.5.patch" - }, - "dependencies": { - "opencode-lore": "^0.1.0" } } diff --git a/test/init-eval/helpers/platforms.ts b/test/init-eval/helpers/platforms.ts index 402e2c53..471caf61 100644 --- a/test/init-eval/helpers/platforms.ts +++ b/test/init-eval/helpers/platforms.ts @@ -91,8 +91,20 @@ export const PLATFORMS: Platform[] = [ sdkPackage: "sentry-sdk", depFile: "requirements.txt", docs: getDocs("python-flask"), - installCmd: "pip install -r requirements.txt", - buildCmd: "python -m compileall -q .", + installCmd: "python -m venv .venv && .venv/bin/pip install -r requirements.txt", + buildCmd: ".venv/bin/python -m compileall -q .", + initPattern: /sentry_sdk\.init/, + timeout: 300_000, + }, + { + id: "python-fastapi", + name: "FastAPI", + templateDir: join(TEMPLATES_DIR, "python-fastapi-app"), + sdkPackage: "sentry-sdk", + depFile: "requirements.txt", + docs: getDocs("python-fastapi"), + installCmd: "python -m venv .venv && .venv/bin/pip install -r requirements.txt", + buildCmd: ".venv/bin/python -m compileall -q .", initPattern: /sentry_sdk\.init/, timeout: 300_000, }, From ce9614f80515270c32e2ea036603442be7e701ab Mon Sep 17 00:00:00 2001 From: betegon Date: Mon, 23 Feb 2026 22:12:49 +0100 Subject: [PATCH 20/26] feat(init): add python-fastapi eval test and gitignore package-lock Co-Authored-By: Claude Opus 4.6 --- .github/workflows/init-eval.yml | 3 ++- .gitignore | 1 + test/init-eval/python-fastapi.eval.test.ts | 3 +++ test/init-eval/templates/python-fastapi-app/.gitignore | 4 ++++ test/init-eval/templates/python-fastapi-app/main.py | 8 ++++++++ .../templates/python-fastapi-app/requirements.txt | 2 ++ 6 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 test/init-eval/python-fastapi.eval.test.ts create mode 100644 test/init-eval/templates/python-fastapi-app/.gitignore create mode 100644 test/init-eval/templates/python-fastapi-app/main.py create mode 100644 test/init-eval/templates/python-fastapi-app/requirements.txt diff --git a/.github/workflows/init-eval.yml b/.github/workflows/init-eval.yml index f3e02e85..d6b23e51 100644 --- a/.github/workflows/init-eval.yml +++ b/.github/workflows/init-eval.yml @@ -12,6 +12,7 @@ on: - all - express - nextjs + - python-fastapi - python-flask - react-vite - sveltekit @@ -25,7 +26,7 @@ jobs: fail-fast: false matrix: platform: ${{ inputs.platform == 'all' - && fromJson('["express","nextjs","python-flask","react-vite","sveltekit"]') + && fromJson('["express","nextjs","python-fastapi","python-flask","react-vite","sveltekit"]') || fromJson(format('["{0}"]', inputs.platform)) }} steps: - uses: actions/checkout@v4 diff --git a/.gitignore b/.gitignore index 9e4b370a..6649b97d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # dependencies (bun install) node_modules +package-lock.json # output out diff --git a/test/init-eval/python-fastapi.eval.test.ts b/test/init-eval/python-fastapi.eval.test.ts new file mode 100644 index 00000000..8849f8ff --- /dev/null +++ b/test/init-eval/python-fastapi.eval.test.ts @@ -0,0 +1,3 @@ +import { createEvalSuite } from "./helpers/create-eval-suite"; + +createEvalSuite("python-fastapi"); diff --git a/test/init-eval/templates/python-fastapi-app/.gitignore b/test/init-eval/templates/python-fastapi-app/.gitignore new file mode 100644 index 00000000..65776d12 --- /dev/null +++ b/test/init-eval/templates/python-fastapi-app/.gitignore @@ -0,0 +1,4 @@ +__pycache__/ +*.py[cod] +.venv/ +venv/ diff --git a/test/init-eval/templates/python-fastapi-app/main.py b/test/init-eval/templates/python-fastapi-app/main.py new file mode 100644 index 00000000..469a6503 --- /dev/null +++ b/test/init-eval/templates/python-fastapi-app/main.py @@ -0,0 +1,8 @@ +from fastapi import FastAPI + +app = FastAPI() + + +@app.get("/") +def hello(): + return {"message": "Hello World"} diff --git a/test/init-eval/templates/python-fastapi-app/requirements.txt b/test/init-eval/templates/python-fastapi-app/requirements.txt new file mode 100644 index 00000000..c5573034 --- /dev/null +++ b/test/init-eval/templates/python-fastapi-app/requirements.txt @@ -0,0 +1,2 @@ +fastapi>=0.115.0 +uvicorn>=0.34.0 From 3227619ed50b4fb8a91b0e686ef198c5c8319ced Mon Sep 17 00:00:00 2001 From: betegon Date: Mon, 23 Feb 2026 22:14:01 +0100 Subject: [PATCH 21/26] style(init): fix lint formatting in eval test files Co-Authored-By: Claude Opus 4.6 --- test/init-eval/feature-docs.json | 132 +++++++++++++++++++------ test/init-eval/helpers/docs-fetcher.ts | 2 +- test/init-eval/helpers/platforms.ts | 6 +- 3 files changed, 107 insertions(+), 33 deletions(-) diff --git a/test/init-eval/feature-docs.json b/test/init-eval/feature-docs.json index 16855def..f987f4c0 100644 --- a/test/init-eval/feature-docs.json +++ b/test/init-eval/feature-docs.json @@ -13,61 +13,133 @@ ], "nextjs": { - "getting-started": ["https://docs.sentry.io/platforms/javascript/guides/nextjs/", "https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/", "https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/pages-router/"], - "errors": ["https://docs.sentry.io/platforms/javascript/guides/nextjs/", "https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/", "https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/pages-router/", "https://docs.sentry.io/platforms/javascript/guides/nextjs/capturing-errors/"], + "getting-started": [ + "https://docs.sentry.io/platforms/javascript/guides/nextjs/", + "https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/", + "https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/pages-router/" + ], + "errors": [ + "https://docs.sentry.io/platforms/javascript/guides/nextjs/", + "https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/", + "https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/pages-router/", + "https://docs.sentry.io/platforms/javascript/guides/nextjs/capturing-errors/" + ], "logs": ["https://docs.sentry.io/platforms/javascript/guides/nextjs/logs/"], - "tracing": ["https://docs.sentry.io/platforms/javascript/guides/nextjs/tracing/"], - "replay": ["https://docs.sentry.io/platforms/javascript/guides/nextjs/session-replay/"], - "metrics": ["https://docs.sentry.io/platforms/javascript/guides/nextjs/metrics/"], + "tracing": [ + "https://docs.sentry.io/platforms/javascript/guides/nextjs/tracing/" + ], + "replay": [ + "https://docs.sentry.io/platforms/javascript/guides/nextjs/session-replay/" + ], + "metrics": [ + "https://docs.sentry.io/platforms/javascript/guides/nextjs/metrics/" + ], "sourcemaps": [], - "profiling": ["https://docs.sentry.io/platforms/javascript/guides/nextjs/profiling/browser/", "https://docs.sentry.io/platforms/javascript/guides/nextjs/profiling/node/"] + "profiling": [ + "https://docs.sentry.io/platforms/javascript/guides/nextjs/profiling/browser/", + "https://docs.sentry.io/platforms/javascript/guides/nextjs/profiling/node/" + ] }, "express": { - "getting-started": ["https://docs.sentry.io/platforms/javascript/guides/express/"], - "errors": ["https://docs.sentry.io/platforms/javascript/guides/express/usage/"], - "tracing": ["https://docs.sentry.io/platforms/javascript/guides/express/tracing/"], - "logs": ["https://docs.sentry.io/platforms/javascript/guides/express/logs/"], - "metrics": ["https://docs.sentry.io/platforms/javascript/guides/express/metrics/"], - "profiling": ["https://docs.sentry.io/platforms/javascript/guides/express/profiling/"] + "getting-started": [ + "https://docs.sentry.io/platforms/javascript/guides/express/" + ], + "errors": [ + "https://docs.sentry.io/platforms/javascript/guides/express/usage/" + ], + "tracing": [ + "https://docs.sentry.io/platforms/javascript/guides/express/tracing/" + ], + "logs": [ + "https://docs.sentry.io/platforms/javascript/guides/express/logs/" + ], + "metrics": [ + "https://docs.sentry.io/platforms/javascript/guides/express/metrics/" + ], + "profiling": [ + "https://docs.sentry.io/platforms/javascript/guides/express/profiling/" + ] }, "python-flask": { - "getting-started": ["https://docs.sentry.io/platforms/python/integrations/flask/"], + "getting-started": [ + "https://docs.sentry.io/platforms/python/integrations/flask/" + ], "errors": ["https://docs.sentry.io/platforms/python/integrations/flask/"], "tracing": ["https://docs.sentry.io/platforms/python/integrations/flask/"], "logs": ["https://docs.sentry.io/platforms/python/integrations/flask/"], "metrics": [], - "profiling": ["https://docs.sentry.io/platforms/python/integrations/flask/", "https://docs.sentry.io/platforms/python/profiling/"] + "profiling": [ + "https://docs.sentry.io/platforms/python/integrations/flask/", + "https://docs.sentry.io/platforms/python/profiling/" + ] }, "python-fastapi": { - "getting-started": ["https://docs.sentry.io/platforms/python/integrations/fastapi/"], + "getting-started": [ + "https://docs.sentry.io/platforms/python/integrations/fastapi/" + ], "errors": ["https://docs.sentry.io/platforms/python/integrations/fastapi/"], - "tracing": ["https://docs.sentry.io/platforms/python/integrations/fastapi/"], + "tracing": [ + "https://docs.sentry.io/platforms/python/integrations/fastapi/" + ], "logs": ["https://docs.sentry.io/platforms/python/integrations/fastapi/"], - "profiling": ["https://docs.sentry.io/platforms/python/integrations/fastapi/", "https://docs.sentry.io/platforms/python/profiling/"] + "profiling": [ + "https://docs.sentry.io/platforms/python/integrations/fastapi/", + "https://docs.sentry.io/platforms/python/profiling/" + ] }, "sveltekit": { - "getting-started": ["https://docs.sentry.io/platforms/javascript/guides/sveltekit/", "https://docs.sentry.io/platforms/javascript/guides/sveltekit/manual-setup/"], - "errors": ["https://docs.sentry.io/platforms/javascript/guides/sveltekit/", "https://docs.sentry.io/platforms/javascript/guides/sveltekit/manual-setup/", "https://docs.sentry.io/platforms/javascript/guides/sveltekit/usage/"], + "getting-started": [ + "https://docs.sentry.io/platforms/javascript/guides/sveltekit/", + "https://docs.sentry.io/platforms/javascript/guides/sveltekit/manual-setup/" + ], + "errors": [ + "https://docs.sentry.io/platforms/javascript/guides/sveltekit/", + "https://docs.sentry.io/platforms/javascript/guides/sveltekit/manual-setup/", + "https://docs.sentry.io/platforms/javascript/guides/sveltekit/usage/" + ], "sourcemaps": [], - "replay": ["https://docs.sentry.io/platforms/javascript/guides/sveltekit/session-replay/"], - "tracing": ["https://docs.sentry.io/platforms/javascript/guides/sveltekit/tracing/"], - "logs": ["https://docs.sentry.io/platforms/javascript/guides/sveltekit/logs/"], - "metrics": ["https://docs.sentry.io/platforms/javascript/guides/sveltekit/metrics/"], - "profiling": ["https://docs.sentry.io/platforms/javascript/guides/sveltekit/profiling/"] + "replay": [ + "https://docs.sentry.io/platforms/javascript/guides/sveltekit/session-replay/" + ], + "tracing": [ + "https://docs.sentry.io/platforms/javascript/guides/sveltekit/tracing/" + ], + "logs": [ + "https://docs.sentry.io/platforms/javascript/guides/sveltekit/logs/" + ], + "metrics": [ + "https://docs.sentry.io/platforms/javascript/guides/sveltekit/metrics/" + ], + "profiling": [ + "https://docs.sentry.io/platforms/javascript/guides/sveltekit/profiling/" + ] }, "react-vite": { - "getting-started": ["https://docs.sentry.io/platforms/javascript/guides/react/"], - "errors": ["https://docs.sentry.io/platforms/javascript/guides/react/", "https://docs.sentry.io/platforms/javascript/guides/react/usage/"], + "getting-started": [ + "https://docs.sentry.io/platforms/javascript/guides/react/" + ], + "errors": [ + "https://docs.sentry.io/platforms/javascript/guides/react/", + "https://docs.sentry.io/platforms/javascript/guides/react/usage/" + ], "sourcemaps": [], - "replay": ["https://docs.sentry.io/platforms/javascript/guides/react/session-replay/"], - "tracing": ["https://docs.sentry.io/platforms/javascript/guides/react/tracing/"], + "replay": [ + "https://docs.sentry.io/platforms/javascript/guides/react/session-replay/" + ], + "tracing": [ + "https://docs.sentry.io/platforms/javascript/guides/react/tracing/" + ], "logs": ["https://docs.sentry.io/platforms/javascript/guides/react/logs/"], - "metrics": ["https://docs.sentry.io/platforms/javascript/guides/react/metrics/"], - "profiling": ["https://docs.sentry.io/platforms/javascript/guides/react/profiling/"] + "metrics": [ + "https://docs.sentry.io/platforms/javascript/guides/react/metrics/" + ], + "profiling": [ + "https://docs.sentry.io/platforms/javascript/guides/react/profiling/" + ] } } diff --git a/test/init-eval/helpers/docs-fetcher.ts b/test/init-eval/helpers/docs-fetcher.ts index e4fe2325..6964320a 100644 --- a/test/init-eval/helpers/docs-fetcher.ts +++ b/test/init-eval/helpers/docs-fetcher.ts @@ -1,5 +1,5 @@ function toMarkdownUrl(docsUrl: string): string { - return docsUrl.replace(/\/+$/, "") + ".md"; + return `${docsUrl.replace(/\/+$/, "")}.md`; } /** diff --git a/test/init-eval/helpers/platforms.ts b/test/init-eval/helpers/platforms.ts index 471caf61..bae33107 100644 --- a/test/init-eval/helpers/platforms.ts +++ b/test/init-eval/helpers/platforms.ts @@ -91,7 +91,8 @@ export const PLATFORMS: Platform[] = [ sdkPackage: "sentry-sdk", depFile: "requirements.txt", docs: getDocs("python-flask"), - installCmd: "python -m venv .venv && .venv/bin/pip install -r requirements.txt", + installCmd: + "python -m venv .venv && .venv/bin/pip install -r requirements.txt", buildCmd: ".venv/bin/python -m compileall -q .", initPattern: /sentry_sdk\.init/, timeout: 300_000, @@ -103,7 +104,8 @@ export const PLATFORMS: Platform[] = [ sdkPackage: "sentry-sdk", depFile: "requirements.txt", docs: getDocs("python-fastapi"), - installCmd: "python -m venv .venv && .venv/bin/pip install -r requirements.txt", + installCmd: + "python -m venv .venv && .venv/bin/pip install -r requirements.txt", buildCmd: ".venv/bin/python -m compileall -q .", initPattern: /sentry_sdk\.init/, timeout: 300_000, From 04ae63dfba890d06a1fbcb38089eb3b58e3f4ba1 Mon Sep 17 00:00:00 2001 From: betegon Date: Mon, 23 Feb 2026 22:16:36 +0100 Subject: [PATCH 22/26] ci(init): add minimal permissions to init-eval workflow Restrict GITHUB_TOKEN to contents:read as flagged by CodeQL. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/init-eval.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/init-eval.yml b/.github/workflows/init-eval.yml index d6b23e51..56711468 100644 --- a/.github/workflows/init-eval.yml +++ b/.github/workflows/init-eval.yml @@ -22,6 +22,8 @@ jobs: name: Eval ${{ matrix.platform }} runs-on: ubuntu-latest environment: init-eval + permissions: + contents: read strategy: fail-fast: false matrix: From 4d7787cbd3529937738551c73123b4de800aa908 Mon Sep 17 00:00:00 2001 From: betegon Date: Tue, 24 Feb 2026 11:16:38 +0100 Subject: [PATCH 23/26] fix(init): update sveltekit template and use python3 in eval tests Update SvelteKit template with working deps (adapter-node, latest svelte/vite) and add required src files (app.d.ts, app.html). Use python3 instead of python for venv creation in Flask/FastAPI platforms. Add --concurrency 6 to init-eval test runner. Co-Authored-By: Claude Opus 4.6 --- package.json | 2 +- test/init-eval/helpers/platforms.ts | 4 +-- .../templates/sveltekit-app/package.json | 18 +++++++------ .../templates/sveltekit-app/src/app.d.ts | 13 ++++++++++ .../templates/sveltekit-app/src/app.html | 11 ++++++++ .../templates/sveltekit-app/svelte.config.js | 10 +++----- .../templates/sveltekit-app/tsconfig.json | 25 ++++++++++--------- 7 files changed, 54 insertions(+), 29 deletions(-) create mode 100644 test/init-eval/templates/sveltekit-app/src/app.d.ts create mode 100644 test/init-eval/templates/sveltekit-app/src/app.html diff --git a/package.json b/package.json index 3ebecbce..71e93045 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "test:unit": "bun test test/lib test/commands test/types --coverage --coverage-reporter=lcov", "test:isolated": "bun test test/isolated", "test:e2e": "bun test test/e2e", - "test:init-eval": "bun test test/init-eval --timeout 600000", + "test:init-eval": "bun test test/init-eval --timeout 600000 --concurrency 6", "generate:skill": "bun run script/generate-skill.ts", "check:skill": "bun run script/check-skill.ts", "check:deps": "bun run script/check-no-deps.ts" diff --git a/test/init-eval/helpers/platforms.ts b/test/init-eval/helpers/platforms.ts index bae33107..df5228ce 100644 --- a/test/init-eval/helpers/platforms.ts +++ b/test/init-eval/helpers/platforms.ts @@ -92,7 +92,7 @@ export const PLATFORMS: Platform[] = [ depFile: "requirements.txt", docs: getDocs("python-flask"), installCmd: - "python -m venv .venv && .venv/bin/pip install -r requirements.txt", + "python3 -m venv .venv && .venv/bin/pip install -r requirements.txt", buildCmd: ".venv/bin/python -m compileall -q .", initPattern: /sentry_sdk\.init/, timeout: 300_000, @@ -105,7 +105,7 @@ export const PLATFORMS: Platform[] = [ depFile: "requirements.txt", docs: getDocs("python-fastapi"), installCmd: - "python -m venv .venv && .venv/bin/pip install -r requirements.txt", + "python3 -m venv .venv && .venv/bin/pip install -r requirements.txt", buildCmd: ".venv/bin/python -m compileall -q .", initPattern: /sentry_sdk\.init/, timeout: 300_000, diff --git a/test/init-eval/templates/sveltekit-app/package.json b/test/init-eval/templates/sveltekit-app/package.json index 77ae4f30..e58b151f 100644 --- a/test/init-eval/templates/sveltekit-app/package.json +++ b/test/init-eval/templates/sveltekit-app/package.json @@ -1,18 +1,20 @@ { "name": "sveltekit-app", - "version": "0.1.0", "private": true, + "version": "0.0.1", + "type": "module", "scripts": { "dev": "vite dev", "build": "vite build", - "preview": "vite preview" + "preview": "vite preview", + "prepare": "svelte-kit sync || echo ''" }, "devDependencies": { - "@sveltejs/adapter-auto": "^4.0.0", - "@sveltejs/kit": "^2.16.0", - "@sveltejs/vite-plugin-svelte": "^5.0.0", - "svelte": "^5.0.0", - "typescript": "^5", - "vite": "^6.0.0" + "@sveltejs/adapter-node": "^5.0.0", + "@sveltejs/kit": "^2.50.2", + "@sveltejs/vite-plugin-svelte": "^6.2.4", + "svelte": "^5.51.0", + "typescript": "^5.9.3", + "vite": "^7.3.1" } } diff --git a/test/init-eval/templates/sveltekit-app/src/app.d.ts b/test/init-eval/templates/sveltekit-app/src/app.d.ts new file mode 100644 index 00000000..da08e6da --- /dev/null +++ b/test/init-eval/templates/sveltekit-app/src/app.d.ts @@ -0,0 +1,13 @@ +// See https://svelte.dev/docs/kit/types#app.d.ts +// for information about these interfaces +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } +} + +export {}; diff --git a/test/init-eval/templates/sveltekit-app/src/app.html b/test/init-eval/templates/sveltekit-app/src/app.html new file mode 100644 index 00000000..f273cc58 --- /dev/null +++ b/test/init-eval/templates/sveltekit-app/src/app.html @@ -0,0 +1,11 @@ + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/test/init-eval/templates/sveltekit-app/svelte.config.js b/test/init-eval/templates/sveltekit-app/svelte.config.js index 3fc56b9c..6bfb3c40 100644 --- a/test/init-eval/templates/sveltekit-app/svelte.config.js +++ b/test/init-eval/templates/sveltekit-app/svelte.config.js @@ -1,12 +1,10 @@ -import adapter from "@sveltejs/adapter-auto"; -import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; +import adapter from '@sveltejs/adapter-node'; /** @type {import('@sveltejs/kit').Config} */ const config = { - preprocess: vitePreprocess(), - kit: { - adapter: adapter(), - }, + kit: { + adapter: adapter() + } }; export default config; diff --git a/test/init-eval/templates/sveltekit-app/tsconfig.json b/test/init-eval/templates/sveltekit-app/tsconfig.json index 43447105..feea18bf 100644 --- a/test/init-eval/templates/sveltekit-app/tsconfig.json +++ b/test/init-eval/templates/sveltekit-app/tsconfig.json @@ -1,14 +1,15 @@ { - "extends": "./.svelte-kit/tsconfig.json", - "compilerOptions": { - "allowJs": true, - "checkJs": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "sourceMap": true, - "strict": true, - "moduleResolution": "bundler" - } + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "rewriteRelativeImportExtensions": true, + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + } } From 102baa64c555633d49e26eb2d9c561a79155962e Mon Sep 17 00:00:00 2001 From: betegon Date: Tue, 24 Feb 2026 11:42:44 +0100 Subject: [PATCH 24/26] ci(init): run init-eval on PRs and main pushes Add push/pull_request triggers so the eval runs automatically alongside other CI checks. Keep workflow_dispatch for manual single-platform runs. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/init-eval.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/init-eval.yml b/.github/workflows/init-eval.yml index 56711468..8e0d3b8c 100644 --- a/.github/workflows/init-eval.yml +++ b/.github/workflows/init-eval.yml @@ -1,6 +1,9 @@ name: Init Eval on: + push: + branches: [main] + pull_request: workflow_dispatch: inputs: platform: @@ -17,6 +20,10 @@ on: - react-vite - sveltekit +concurrency: + group: init-eval-${{ github.ref }} + cancel-in-progress: true + jobs: eval: name: Eval ${{ matrix.platform }} @@ -27,9 +34,9 @@ jobs: strategy: fail-fast: false matrix: - platform: ${{ inputs.platform == 'all' - && fromJson('["express","nextjs","python-fastapi","python-flask","react-vite","sveltekit"]') - || fromJson(format('["{0}"]', inputs.platform)) }} + platform: ${{ (inputs.platform && inputs.platform != 'all') + && fromJson(format('["{0}"]', inputs.platform)) + || fromJson('["express","nextjs","python-fastapi","python-flask","react-vite","sveltekit"]') }} steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v2 From 51f8968d179fcd30ee4e013707aac7692f7b4120 Mon Sep 17 00:00:00 2001 From: betegon Date: Tue, 24 Feb 2026 12:09:15 +0100 Subject: [PATCH 25/26] Revert "ci(init): run init-eval on PRs and main pushes" This reverts commit 102baa64c555633d49e26eb2d9c561a79155962e. --- .github/workflows/init-eval.yml | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/.github/workflows/init-eval.yml b/.github/workflows/init-eval.yml index 8e0d3b8c..56711468 100644 --- a/.github/workflows/init-eval.yml +++ b/.github/workflows/init-eval.yml @@ -1,9 +1,6 @@ name: Init Eval on: - push: - branches: [main] - pull_request: workflow_dispatch: inputs: platform: @@ -20,10 +17,6 @@ on: - react-vite - sveltekit -concurrency: - group: init-eval-${{ github.ref }} - cancel-in-progress: true - jobs: eval: name: Eval ${{ matrix.platform }} @@ -34,9 +27,9 @@ jobs: strategy: fail-fast: false matrix: - platform: ${{ (inputs.platform && inputs.platform != 'all') - && fromJson(format('["{0}"]', inputs.platform)) - || fromJson('["express","nextjs","python-fastapi","python-flask","react-vite","sveltekit"]') }} + platform: ${{ inputs.platform == 'all' + && fromJson('["express","nextjs","python-fastapi","python-flask","react-vite","sveltekit"]') + || fromJson(format('["{0}"]', inputs.platform)) }} steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v2 From d5d0b22d3264c335d08792104dd795deafdeeda7 Mon Sep 17 00:00:00 2001 From: betegon Date: Tue, 24 Feb 2026 12:09:22 +0100 Subject: [PATCH 26/26] ci(init): remove init-eval workflow for now Co-Authored-By: Claude Opus 4.6 --- .github/workflows/init-eval.yml | 50 --------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 .github/workflows/init-eval.yml diff --git a/.github/workflows/init-eval.yml b/.github/workflows/init-eval.yml deleted file mode 100644 index 56711468..00000000 --- a/.github/workflows/init-eval.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: Init Eval - -on: - workflow_dispatch: - inputs: - platform: - description: "Platform to evaluate (or 'all')" - required: true - default: all - type: choice - options: - - all - - express - - nextjs - - python-fastapi - - python-flask - - react-vite - - sveltekit - -jobs: - eval: - name: Eval ${{ matrix.platform }} - runs-on: ubuntu-latest - environment: init-eval - permissions: - contents: read - strategy: - fail-fast: false - matrix: - platform: ${{ inputs.platform == 'all' - && fromJson('["express","nextjs","python-fastapi","python-flask","react-vite","sveltekit"]') - || fromJson(format('["{0}"]', inputs.platform)) }} - steps: - - uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v2 - - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - uses: actions/cache@v4 - id: cache - with: - path: node_modules - key: node-modules-${{ hashFiles('bun.lock', 'patches/**') }} - - if: steps.cache.outputs.cache-hit != 'true' - run: bun install --frozen-lockfile - - name: Run eval - env: - MASTRA_API_URL: ${{ secrets.MASTRA_API_URL }} - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - run: bun test ./test/init-eval/${{ matrix.platform }}.eval.test.ts --timeout 600000