diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..ea5a55b --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +bunx lint-staged diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..1b8ac88 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +# Ignore artifacts: +build +coverage diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/.prettierrc @@ -0,0 +1 @@ +{} diff --git a/README.md b/README.md index 52773f1..707a86f 100644 --- a/README.md +++ b/README.md @@ -16,10 +16,10 @@ A Site Reliability Engineering (SRE) bot that integrates with Slack, GitHub, and - Docker and Docker Compose - PostgreSQL (via Docker or local installation) - Accounts and API keys for: - - OpenAI - - Slack - - GitHub - - Checkly + - OpenAI + - Slack + - GitHub + - Checkly ## Setup @@ -32,21 +32,25 @@ Duplicate the `env.example` file in the root directory and add your keys as per The project uses PostgreSQL as its database. To set it up using Docker: 1. Start the PostgreSQL container: + ```bash docker compose up -d ``` 2. Run database migrations: + ```bash npm run db:migrate ``` 3. (Optional) To explore the database using Prisma Studio: + ```bash npm run db:studio ``` To reset the database if needed: + ```bash docker compose down -v # Remove containers and volumes docker compose up -d # Start fresh @@ -56,41 +60,48 @@ npm run db:migrate # Run migrations again ### 3. Installation 1. Install dependencies: + ```bash npm install ``` 2. Initialize the OpenAI assistant: + - First, get your OpenAI API key from https://platform.openai.com/api-keys - Add the API key to your .env file - Create the assistant by running: + ```bash npx ts-node scripts/init-assistant.ts ``` -Go to https://platform.openai.com/assistants to find your assistant ID +Go to https://platform.openai.com/assistants to find your assistant ID ## Running the Application There are several ways to run the application depending on your needs: ### Development Mode + ```bash npm run dev ``` ### Slack Bot Only + ```bash npm run bot:start ``` ### Production Mode + ```bash npm run build npm start ``` ### Running Tests + ```bash npm test ``` @@ -121,27 +132,30 @@ npm test ## External Service Setup ### Slack Setup + 1. Create a new Slack app in your workspace: https://api.slack.com/apps 2. Configure Bot Token Scopes: - - chat:write - - app_mentions:read - - commands + - chat:write + - app_mentions:read + - commands 3. Install the app to your workspace 4. Copy the signing secret, bot token, and app token to your .env file ### GitHub Setup + 1. Create a Personal Access Token with repo permissions 2. Configure webhook in your organization/repository: - - Payload URL: your-server/github-webhook - - Content type: application/json - - Secret: Same as GH_WEBHOOK_SECRET in .env - - Events: Release events + - Payload URL: your-server/github-webhook + - Content type: application/json + - Secret: Same as GH_WEBHOOK_SECRET in .env + - Events: Release events ### Checkly Setup -1. Get your API key and Account ID from Checkly: https://app.checklyhq.com/ + +1. Get your API key and Account ID from Checkly: https://app.checklyhq.com/ 2. Configure webhook in Checkly: - - URL: your-server/checkly-webhook - - Select relevant alert types + - URL: your-server/checkly-webhook + - Select relevant alert types ## License diff --git a/bun.lock b/bun.lock index ec01f67..d783df6 100644 --- a/bun.lock +++ b/bun.lock @@ -6,8 +6,14 @@ "dependencies": { "@ai-sdk/openai": "0.0.72", "@notionhq/client": "2.2.15", + "@opentelemetry/api-logs": "^0.57.1", + "@opentelemetry/auto-instrumentations-node": "^0.56.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/sdk-logs": "^0.57.1", + "@opentelemetry/sdk-node": "^0.57.1", "@prisma/client": "5.22.0", "@slack/bolt": "4.1.0", + "@vercel/otel": "^1.10.1", "ai": "3.4.33", "axios": "1.7.9", "class-transformer": "0.5.1", @@ -15,6 +21,8 @@ "dotenv": "16.4.5", "express": "4.21.1", "gray-matter": "4.0.3", + "langfuse": "3.34.1", + "langfuse-vercel": "^3.34.1", "llamaindex": "0.8.5", "moment": "2.30.1", "next": "15.0.3", @@ -400,6 +408,156 @@ "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], + "@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.57.1", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-I4PHczeujhQAQv6ZBzqHYEUiggZL4IdSMixtVD3EYqbdrjujE7kRfI5QohjlPoJm8BvenoW5YaTMWRrbpot6tg=="], + + "@opentelemetry/auto-instrumentations-node": ["@opentelemetry/auto-instrumentations-node@0.56.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/instrumentation-amqplib": "^0.46.0", "@opentelemetry/instrumentation-aws-lambda": "^0.50.2", "@opentelemetry/instrumentation-aws-sdk": "^0.49.0", "@opentelemetry/instrumentation-bunyan": "^0.45.0", "@opentelemetry/instrumentation-cassandra-driver": "^0.45.0", "@opentelemetry/instrumentation-connect": "^0.43.0", "@opentelemetry/instrumentation-cucumber": "^0.14.0", "@opentelemetry/instrumentation-dataloader": "^0.16.0", "@opentelemetry/instrumentation-dns": "^0.43.0", "@opentelemetry/instrumentation-express": "^0.47.0", "@opentelemetry/instrumentation-fastify": "^0.44.1", "@opentelemetry/instrumentation-fs": "^0.19.0", "@opentelemetry/instrumentation-generic-pool": "^0.43.0", "@opentelemetry/instrumentation-graphql": "^0.47.0", "@opentelemetry/instrumentation-grpc": "^0.57.0", "@opentelemetry/instrumentation-hapi": "^0.45.1", "@opentelemetry/instrumentation-http": "^0.57.0", "@opentelemetry/instrumentation-ioredis": "^0.47.0", "@opentelemetry/instrumentation-kafkajs": "^0.7.0", "@opentelemetry/instrumentation-knex": "^0.44.0", "@opentelemetry/instrumentation-koa": "^0.47.0", "@opentelemetry/instrumentation-lru-memoizer": "^0.44.0", "@opentelemetry/instrumentation-memcached": "^0.43.0", "@opentelemetry/instrumentation-mongodb": "^0.51.0", "@opentelemetry/instrumentation-mongoose": "^0.46.0", "@opentelemetry/instrumentation-mysql": "^0.45.0", "@opentelemetry/instrumentation-mysql2": "^0.45.1", "@opentelemetry/instrumentation-nestjs-core": "^0.44.0", "@opentelemetry/instrumentation-net": "^0.43.0", "@opentelemetry/instrumentation-pg": "^0.51.0", "@opentelemetry/instrumentation-pino": "^0.46.0", "@opentelemetry/instrumentation-redis": "^0.46.0", "@opentelemetry/instrumentation-redis-4": "^0.46.0", "@opentelemetry/instrumentation-restify": "^0.45.0", "@opentelemetry/instrumentation-router": "^0.44.0", "@opentelemetry/instrumentation-socket.io": "^0.46.0", "@opentelemetry/instrumentation-tedious": "^0.18.0", "@opentelemetry/instrumentation-undici": "^0.10.0", "@opentelemetry/instrumentation-winston": "^0.44.0", "@opentelemetry/resource-detector-alibaba-cloud": "^0.30.0", "@opentelemetry/resource-detector-aws": "^1.11.0", "@opentelemetry/resource-detector-azure": "^0.6.0", "@opentelemetry/resource-detector-container": "^0.6.0", "@opentelemetry/resource-detector-gcp": "^0.33.0", "@opentelemetry/resources": "^1.24.0", "@opentelemetry/sdk-node": "^0.57.0" }, "peerDependencies": { "@opentelemetry/api": "^1.4.1" } }, "sha512-d1X3DQY0+VmhNUir/3U3JO6Uh0FOSm8G91zsPzVVKc6NGDwmHP6Dn7PMVH70O6FZ0yErzlHqRx8vkNiAsTWt5A=="], + + "@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@1.30.1", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA=="], + + "@opentelemetry/core": ["@opentelemetry/core@1.30.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ=="], + + "@opentelemetry/exporter-logs-otlp-grpc": ["@opentelemetry/exporter-logs-otlp-grpc@0.57.1", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "1.30.1", "@opentelemetry/otlp-grpc-exporter-base": "0.57.1", "@opentelemetry/otlp-transformer": "0.57.1", "@opentelemetry/sdk-logs": "0.57.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-RL8qmZH1H/H7Hbj0xKxF0Gg8kX9ic0aoMS3Kv5kj864lWxlpuR5YtGGn5OjGYwCmq6nYbsNy257fFp1U63pABw=="], + + "@opentelemetry/exporter-logs-otlp-http": ["@opentelemetry/exporter-logs-otlp-http@0.57.1", "", { "dependencies": { "@opentelemetry/api-logs": "0.57.1", "@opentelemetry/core": "1.30.1", "@opentelemetry/otlp-exporter-base": "0.57.1", "@opentelemetry/otlp-transformer": "0.57.1", "@opentelemetry/sdk-logs": "0.57.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-u8Cr6yDX57/n89aSJwAQNHQIYodcl6o8jTcaPKNktMvNfd7ny3R7aE7GKBC5Wg0zejP9heBgyN0OGwrPhptx7A=="], + + "@opentelemetry/exporter-logs-otlp-proto": ["@opentelemetry/exporter-logs-otlp-proto@0.57.1", "", { "dependencies": { "@opentelemetry/api-logs": "0.57.1", "@opentelemetry/core": "1.30.1", "@opentelemetry/otlp-exporter-base": "0.57.1", "@opentelemetry/otlp-transformer": "0.57.1", "@opentelemetry/resources": "1.30.1", "@opentelemetry/sdk-logs": "0.57.1", "@opentelemetry/sdk-trace-base": "1.30.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-WtR85NHdIVrIFfsK5bwx7miGG5WzOsuT4BNmuZ3EfZ0veowkrgoUSynsNnXW1YFXL6QhPbScjUfeTjnnV9bnIQ=="], + + "@opentelemetry/exporter-metrics-otlp-grpc": ["@opentelemetry/exporter-metrics-otlp-grpc@0.57.1", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "1.30.1", "@opentelemetry/exporter-metrics-otlp-http": "0.57.1", "@opentelemetry/otlp-exporter-base": "0.57.1", "@opentelemetry/otlp-grpc-exporter-base": "0.57.1", "@opentelemetry/otlp-transformer": "0.57.1", "@opentelemetry/resources": "1.30.1", "@opentelemetry/sdk-metrics": "1.30.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-8B7k5q4AUldbfvubcHApg1XQaio/cO/VUWsM5PSaRP2fsjGNwbn2ih04J3gLD+AmgslvyuDcA2SZiDXEKwAxtQ=="], + + "@opentelemetry/exporter-metrics-otlp-http": ["@opentelemetry/exporter-metrics-otlp-http@0.57.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/otlp-exporter-base": "0.57.1", "@opentelemetry/otlp-transformer": "0.57.1", "@opentelemetry/resources": "1.30.1", "@opentelemetry/sdk-metrics": "1.30.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-jpKYVZY7fdwTdy+eAy/Mp9DZMaQpj7caMzlo3QqQDSJx5FZEY6zWzgcKvDvF6h+gdHE7LgUjaPOvJVUs354jJg=="], + + "@opentelemetry/exporter-metrics-otlp-proto": ["@opentelemetry/exporter-metrics-otlp-proto@0.57.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/exporter-metrics-otlp-http": "0.57.1", "@opentelemetry/otlp-exporter-base": "0.57.1", "@opentelemetry/otlp-transformer": "0.57.1", "@opentelemetry/resources": "1.30.1", "@opentelemetry/sdk-metrics": "1.30.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-53AJmYJr8lypU6kAQT1/FVKR2QKcxRp4Gd54L3oF9hc2fw/FtvVfXV+PelB+qL318PqUlVjVtDOa4SQ5tAREfA=="], + + "@opentelemetry/exporter-prometheus": ["@opentelemetry/exporter-prometheus@0.57.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/resources": "1.30.1", "@opentelemetry/sdk-metrics": "1.30.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-lwwOQzyvhzioGCYmIh7mXo+RLSoEVhuO0dFzWeEiQhFkjSUOPgKQKNTgYtl2KO1L7XIbHp5LIgn4nZrYx191Rg=="], + + "@opentelemetry/exporter-trace-otlp-grpc": ["@opentelemetry/exporter-trace-otlp-grpc@0.57.1", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "1.30.1", "@opentelemetry/otlp-grpc-exporter-base": "0.57.1", "@opentelemetry/otlp-transformer": "0.57.1", "@opentelemetry/resources": "1.30.1", "@opentelemetry/sdk-trace-base": "1.30.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-a9/4w2nyfehxMA64VGcZ4OXePGLjTz9H/dvqbOzVmIBZe9R6bkOeT68M9WoxAEdUZcJDK8XS3EloJId1rjPrag=="], + + "@opentelemetry/exporter-trace-otlp-http": ["@opentelemetry/exporter-trace-otlp-http@0.57.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/otlp-exporter-base": "0.57.1", "@opentelemetry/otlp-transformer": "0.57.1", "@opentelemetry/resources": "1.30.1", "@opentelemetry/sdk-trace-base": "1.30.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-43dLEjlf6JGxpVt9RaRlJAvjHG1wGsbAuNd67RIDy/95zfKk2aNovtiGUgFdS/kcvgvS90upIUbgn0xUd9JjMg=="], + + "@opentelemetry/exporter-trace-otlp-proto": ["@opentelemetry/exporter-trace-otlp-proto@0.57.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/otlp-exporter-base": "0.57.1", "@opentelemetry/otlp-transformer": "0.57.1", "@opentelemetry/resources": "1.30.1", "@opentelemetry/sdk-trace-base": "1.30.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-REN6UZTNoP3Tb7vuCEy+yAjNmJGi7MLqCMdDoUSbsWGwpopxtSnsbkfVfLPsZAsumWkcq0p8p6lYvqUBDhUqIA=="], + + "@opentelemetry/exporter-zipkin": ["@opentelemetry/exporter-zipkin@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/resources": "1.30.1", "@opentelemetry/sdk-trace-base": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-6S2QIMJahIquvFaaxmcwpvQQRD/YFaMTNoIxrfPIPOeITN+a8lfEcPDxNxn8JDAaxkg+4EnXhz8upVDYenoQjA=="], + + "@opentelemetry/instrumentation": ["@opentelemetry/instrumentation@0.57.1", "", { "dependencies": { "@opentelemetry/api-logs": "0.57.1", "@types/shimmer": "^1.2.0", "import-in-the-middle": "^1.8.1", "require-in-the-middle": "^7.1.1", "semver": "^7.5.2", "shimmer": "^1.2.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-SgHEKXoVxOjc20ZYusPG3Fh+RLIZTSa4x8QtD3NfgAUDyqdFFS9W1F2ZVbZkqDCdyMcQG02Ok4duUGLHJXHgbA=="], + + "@opentelemetry/instrumentation-amqplib": ["@opentelemetry/instrumentation-amqplib@0.46.0", "", { "dependencies": { "@opentelemetry/core": "^1.8.0", "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-04VHHV1KIN/c1wLWwzmLI02d/welgscBJ4BuDqrHaxd+ZIdlVXK9UYQsYf3JwSeF52z/4YoSzr8bfdVBSWoMAg=="], + + "@opentelemetry/instrumentation-aws-lambda": ["@opentelemetry/instrumentation-aws-lambda@0.50.2", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/aws-lambda": "8.10.143" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-jz1a7t2q0SsiztEMyZjFLEFC4pOQ+1C588gWzl878k9Qr6TI1Wu3sa7/dikxJmeRIETcOTUilaa2Otxh6HUVlA=="], + + "@opentelemetry/instrumentation-aws-sdk": ["@opentelemetry/instrumentation-aws-sdk@0.49.0", "", { "dependencies": { "@opentelemetry/core": "^1.8.0", "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/propagation-utils": "^0.30.15", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-m3yC3ni4Yo8tggbZgygS/ccAP9e/EYqsMwzooHiIymbnyZwDAB7kMZ3OrjcLVPCFx9gjNMDKW4MdwOPC0vTEeQ=="], + + "@opentelemetry/instrumentation-bunyan": ["@opentelemetry/instrumentation-bunyan@0.45.0", "", { "dependencies": { "@opentelemetry/api-logs": "^0.57.0", "@opentelemetry/instrumentation": "^0.57.0", "@types/bunyan": "1.8.9" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-K3ZleoOxKUzGjt0TfAT1jfSNcgyt7+toqjhWymPf2tsGUETXxaxGDzAoNepWcfIkgPauJLPpRLLKcP6LjYLILw=="], + + "@opentelemetry/instrumentation-cassandra-driver": ["@opentelemetry/instrumentation-cassandra-driver@0.45.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-IKoA0lLfF7EyIL85MfqzvfAa/Oz9zHNFXwzSiQ6Iqej89BMyOm3eYaAsyUDAvgiLG12M189temMMyRuR07YsZg=="], + + "@opentelemetry/instrumentation-connect": ["@opentelemetry/instrumentation-connect@0.43.0", "", { "dependencies": { "@opentelemetry/core": "^1.8.0", "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/connect": "3.4.36" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Q57JGpH6T4dkYHo9tKXONgLtxzsh1ZEW5M9A/OwKrZFyEpLqWgjhcZ3hIuVvDlhb426iDF1f9FPToV/mi5rpeA=="], + + "@opentelemetry/instrumentation-cucumber": ["@opentelemetry/instrumentation-cucumber@0.14.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-i/GlurL1IM+CnbmItW8kx59YxAp0wu/YQkzQQRU/YGmUjym5g+/dOVjnk/K46lAU49Nn1XyFd7S3ZNf83PHL2Q=="], + + "@opentelemetry/instrumentation-dataloader": ["@opentelemetry/instrumentation-dataloader@0.16.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.57.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-88+qCHZC02up8PwKHk0UQKLLqGGURzS3hFQBZC7PnGwReuoKjHXS1o29H58S+QkXJpkTr2GACbx8j6mUoGjNPA=="], + + "@opentelemetry/instrumentation-dns": ["@opentelemetry/instrumentation-dns@0.43.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.57.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-bGXTyBpjSYt6B7LEj0zMfWkoveGpYf5pVEgTZmDacsG49RdfdCH5PYt3C8MEMwYEFtu2dGdKdKa2LHfefIIDdg=="], + + "@opentelemetry/instrumentation-express": ["@opentelemetry/instrumentation-express@0.47.0", "", { "dependencies": { "@opentelemetry/core": "^1.8.0", "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-XFWVx6k0XlU8lu6cBlCa29ONtVt6ADEjmxtyAyeF2+rifk8uBJbk1La0yIVfI0DoKURGbaEDTNelaXG9l/lNNQ=="], + + "@opentelemetry/instrumentation-fastify": ["@opentelemetry/instrumentation-fastify@0.44.1", "", { "dependencies": { "@opentelemetry/core": "^1.8.0", "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-RoVeMGKcNttNfXMSl6W4fsYoCAYP1vi6ZAWIGhBY+o7R9Y0afA7f9JJL0j8LHbyb0P0QhSYk+6O56OwI2k4iRQ=="], + + "@opentelemetry/instrumentation-fs": ["@opentelemetry/instrumentation-fs@0.19.0", "", { "dependencies": { "@opentelemetry/core": "^1.8.0", "@opentelemetry/instrumentation": "^0.57.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-JGwmHhBkRT2G/BYNV1aGI+bBjJu4fJUD/5/Jat0EWZa2ftrLV3YE8z84Fiij/wK32oMZ88eS8DI4ecLGZhpqsQ=="], + + "@opentelemetry/instrumentation-generic-pool": ["@opentelemetry/instrumentation-generic-pool@0.43.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.57.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-at8GceTtNxD1NfFKGAuwtqM41ot/TpcLh+YsGe4dhf7gvv1HW/ZWdq6nfRtS6UjIvZJOokViqLPJ3GVtZItAnQ=="], + + "@opentelemetry/instrumentation-graphql": ["@opentelemetry/instrumentation-graphql@0.47.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.57.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Cc8SMf+nLqp0fi8oAnooNEfwZWFnzMiBHCGmDFYqmgjPylyLmi83b+NiTns/rKGwlErpW0AGPt0sMpkbNlzn8w=="], + + "@opentelemetry/instrumentation-grpc": ["@opentelemetry/instrumentation-grpc@0.57.1", "", { "dependencies": { "@opentelemetry/instrumentation": "0.57.1", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-tZ0LO6hxLCnQfSS03BpYWc+kZpqFJJUbYb+GfEr5YJ1/YrOtRP8lCpC8AC1QIVmqGn+Vlxjkn3tSifNHsk9enw=="], + + "@opentelemetry/instrumentation-hapi": ["@opentelemetry/instrumentation-hapi@0.45.1", "", { "dependencies": { "@opentelemetry/core": "^1.8.0", "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-VH6mU3YqAKTePPfUPwfq4/xr049774qWtfTuJqVHoVspCLiT3bW+fCQ1toZxt6cxRPYASoYaBsMA3CWo8B8rcw=="], + + "@opentelemetry/instrumentation-http": ["@opentelemetry/instrumentation-http@0.57.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/instrumentation": "0.57.1", "@opentelemetry/semantic-conventions": "1.28.0", "forwarded-parse": "2.1.2", "semver": "^7.5.2" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-ThLmzAQDs7b/tdKI3BV2+yawuF09jF111OFsovqT1Qj3D8vjwKBwhi/rDE5xethwn4tSXtZcJ9hBsVAlWFQZ7g=="], + + "@opentelemetry/instrumentation-ioredis": ["@opentelemetry/instrumentation-ioredis@0.47.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/redis-common": "^0.36.2", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-4HqP9IBC8e7pW9p90P3q4ox0XlbLGme65YTrA3UTLvqvo4Z6b0puqZQP203YFu8m9rE/luLfaG7/xrwwqMUpJw=="], + + "@opentelemetry/instrumentation-kafkajs": ["@opentelemetry/instrumentation-kafkajs@0.7.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-LB+3xiNzc034zHfCtgs4ITWhq6Xvdo8bsq7amR058jZlf2aXXDrN9SV4si4z2ya9QX4tz6r4eZJwDkXOp14/AQ=="], + + "@opentelemetry/instrumentation-knex": ["@opentelemetry/instrumentation-knex@0.44.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-SlT0+bLA0Lg3VthGje+bSZatlGHw/vwgQywx0R/5u9QC59FddTQSPJeWNw29M6f8ScORMeUOOTwihlQAn4GkJQ=="], + + "@opentelemetry/instrumentation-koa": ["@opentelemetry/instrumentation-koa@0.47.0", "", { "dependencies": { "@opentelemetry/core": "^1.8.0", "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-HFdvqf2+w8sWOuwtEXayGzdZ2vWpCKEQv5F7+2DSA74Te/Cv4rvb2E5So5/lh+ok4/RAIPuvCbCb/SHQFzMmbw=="], + + "@opentelemetry/instrumentation-lru-memoizer": ["@opentelemetry/instrumentation-lru-memoizer@0.44.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.57.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Tn7emHAlvYDFik3vGU0mdwvWJDwtITtkJ+5eT2cUquct6nIs+H8M47sqMJkCpyPe5QIBJoTOHxmc6mj9lz6zDw=="], + + "@opentelemetry/instrumentation-memcached": ["@opentelemetry/instrumentation-memcached@0.43.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/memcached": "^2.2.6" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-qjldZMBpfxKwI4ODytX6raF1WE+Qov0wTW4+tkofjas1b8e0WmVs+Pw4/YlmjJNOKRLD1usYkP7QlmPLvyzZSA=="], + + "@opentelemetry/instrumentation-mongodb": ["@opentelemetry/instrumentation-mongodb@0.51.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-cMKASxCX4aFxesoj3WK8uoQ0YUrRvnfxaO72QWI2xLu5ZtgX/QvdGBlU3Ehdond5eb74c2s1cqRQUIptBnKz1g=="], + + "@opentelemetry/instrumentation-mongoose": ["@opentelemetry/instrumentation-mongoose@0.46.0", "", { "dependencies": { "@opentelemetry/core": "^1.8.0", "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-mtVv6UeaaSaWTeZtLo4cx4P5/ING2obSqfWGItIFSunQBrYROfhuVe7wdIrFUs2RH1tn2YYpAJyMaRe/bnTTIQ=="], + + "@opentelemetry/instrumentation-mysql": ["@opentelemetry/instrumentation-mysql@0.45.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/mysql": "2.15.26" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-tWWyymgwYcTwZ4t8/rLDfPYbOTF3oYB8SxnYMtIQ1zEf5uDm90Ku3i6U/vhaMyfHNlIHvDhvJh+qx5Nc4Z3Acg=="], + + "@opentelemetry/instrumentation-mysql2": ["@opentelemetry/instrumentation-mysql2@0.45.1", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@opentelemetry/sql-common": "^0.40.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-9R/vxEc02vlSqyQSmXRTvFMZVht8vgSJokKhiWA3z8Idu0mmdKFKeHiuW5yRGxM/WOi+7DWqQfYM7zw/cJc3sA=="], + + "@opentelemetry/instrumentation-nestjs-core": ["@opentelemetry/instrumentation-nestjs-core@0.44.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-t16pQ7A4WYu1yyQJZhRKIfUNvl5PAaF2pEteLvgJb/BWdd1oNuU1rOYt4S825kMy+0q4ngiX281Ss9qiwHfxFQ=="], + + "@opentelemetry/instrumentation-net": ["@opentelemetry/instrumentation-net@0.43.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-jFzYpCGg1+s4uePNC86GcdzsYzDZpfVMDsHNZzw5MX6tMWyc2jtiXBFWed41HpWOtkIRU/SJd7KR0k1WjNZRuQ=="], + + "@opentelemetry/instrumentation-pg": ["@opentelemetry/instrumentation-pg@0.51.0", "", { "dependencies": { "@opentelemetry/core": "^1.26.0", "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@opentelemetry/sql-common": "^0.40.1", "@types/pg": "8.6.1", "@types/pg-pool": "2.0.6" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-/NStIcUWUofc11dL7tSgMk25NqvhtbHDCncgm+yc4iJF8Ste2Q/lwUitjfxqj4qWM280uFmBEtcmtMMjbjRU7Q=="], + + "@opentelemetry/instrumentation-pino": ["@opentelemetry/instrumentation-pino@0.46.0", "", { "dependencies": { "@opentelemetry/api-logs": "^0.57.0", "@opentelemetry/core": "^1.25.0", "@opentelemetry/instrumentation": "^0.57.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-TFjW24fwc/5KafDZuXbdViGiTym/6U6tDnOEkM5K9LIKsySMWb8xNIVE7y/6B8zDwImncEssNN1t42NixQJqug=="], + + "@opentelemetry/instrumentation-redis": ["@opentelemetry/instrumentation-redis@0.46.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/redis-common": "^0.36.2", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-dXgSf+h+v3Bl4/NYzcSHG0NtqbXz74ph9J1ZBwxTnaB79u+C+ntfqtNt9jklIEAEZ1jR0jRCsVbiZyOpoCpTOg=="], + + "@opentelemetry/instrumentation-redis-4": ["@opentelemetry/instrumentation-redis-4@0.46.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/redis-common": "^0.36.2", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-aTUWbzbFMFeRODn3720TZO0tsh/49T8H3h8vVnVKJ+yE36AeW38Uj/8zykQ/9nO8Vrtjr5yKuX3uMiG/W8FKNw=="], + + "@opentelemetry/instrumentation-restify": ["@opentelemetry/instrumentation-restify@0.45.0", "", { "dependencies": { "@opentelemetry/core": "^1.8.0", "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-CJ5vq14Plh4W4382Jd/jpNEJStqwqbCzZH1Op4EZVPxXhYOwCafgyflOqjxXSzTvqzhaPDT+A079ix5ebQUlYw=="], + + "@opentelemetry/instrumentation-router": ["@opentelemetry/instrumentation-router@0.44.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-rmQZKYcof4M6vQjwtrlfybQo7BSD0mxkXdhfNHWxFjxOFGw9i7EuXSYLnThcVAqNnJ1EljzZiHzaJiq5Ehcb3A=="], + + "@opentelemetry/instrumentation-socket.io": ["@opentelemetry/instrumentation-socket.io@0.46.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-BU3XGT63ziF0S9Ky0YevCuMhHUq6U+Wi1g/piJcB16nOqlfd1SW6EACl5LrUe+aNZk2qIXfuS7YV8R+H99+XQQ=="], + + "@opentelemetry/instrumentation-tedious": ["@opentelemetry/instrumentation-tedious@0.18.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.57.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/tedious": "^4.0.14" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-9zhjDpUDOtD+coeADnYEJQ0IeLVCj7w/hqzIutdp5NqS1VqTAanaEfsEcSypyvYv5DX3YOsTUoF+nr2wDXPETA=="], + + "@opentelemetry/instrumentation-undici": ["@opentelemetry/instrumentation-undici@0.10.0", "", { "dependencies": { "@opentelemetry/core": "^1.8.0", "@opentelemetry/instrumentation": "^0.57.0" }, "peerDependencies": { "@opentelemetry/api": "^1.7.0" } }, "sha512-vm+V255NGw9gaSsPD6CP0oGo8L55BffBc8KnxqsMuc6XiAD1L8SFNzsW0RHhxJFqy9CJaJh+YiJ5EHXuZ5rZBw=="], + + "@opentelemetry/instrumentation-winston": ["@opentelemetry/instrumentation-winston@0.44.0", "", { "dependencies": { "@opentelemetry/api-logs": "^0.57.0", "@opentelemetry/instrumentation": "^0.57.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-2uIrdmDIU9qJuHHKXTI3Gef+tNQmKtcwXDA6S0tm+KpKgkMwZB6AC0rNmGNQsxbGJSORj0NJvy5TVvk6jjsaqg=="], + + "@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.57.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/otlp-transformer": "0.57.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-GNBJAEYfeiYJQ3O2dvXgiNZ/qjWrBxSb1L1s7iV/jKBRGMN3Nv+miTk2SLeEobF5E5ZK4rVcHKlBZ71bPVIv/g=="], + + "@opentelemetry/otlp-grpc-exporter-base": ["@opentelemetry/otlp-grpc-exporter-base@0.57.1", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "1.30.1", "@opentelemetry/otlp-exporter-base": "0.57.1", "@opentelemetry/otlp-transformer": "0.57.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-wWflmkDhH/3wf6yEqPmzmqA6r+A8+LQABfIVZC0jDGtWVJj6eCWcGHU41UxupMbbsgjZRLYtWDilaCHOjmR7gg=="], + + "@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.57.1", "", { "dependencies": { "@opentelemetry/api-logs": "0.57.1", "@opentelemetry/core": "1.30.1", "@opentelemetry/resources": "1.30.1", "@opentelemetry/sdk-logs": "0.57.1", "@opentelemetry/sdk-metrics": "1.30.1", "@opentelemetry/sdk-trace-base": "1.30.1", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-EX67y+ukNNfFrOLyjYGw8AMy0JPIlEX1dW60SGUNZWW2hSQyyolX7EqFuHP5LtXLjJHNfzx5SMBVQ3owaQCNDw=="], + + "@opentelemetry/propagation-utils": ["@opentelemetry/propagation-utils@0.30.15", "", { "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-nQ30K+eXTkd9Kt8yep9FPrqogS712GvdkV6R1T+xZMSZnFrRCyZuWxMtP3+s3hrK2HWw3ti4lsIfBzsHWYiyrA=="], + + "@opentelemetry/propagator-b3": ["@opentelemetry/propagator-b3@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-oATwWWDIJzybAZ4pO76ATN5N6FFbOA1otibAVlS8v90B4S1wClnhRUk7K+2CHAwN1JKYuj4jh/lpCEG5BAqFuQ=="], + + "@opentelemetry/propagator-jaeger": ["@opentelemetry/propagator-jaeger@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-Pj/BfnYEKIOImirH76M4hDaBSx6HyZ2CXUqk+Kj02m6BB80c/yo4BdWkn/1gDFfU+YPY+bPR2U0DKBfdxCKwmg=="], + + "@opentelemetry/redis-common": ["@opentelemetry/redis-common@0.36.2", "", {}, "sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g=="], + + "@opentelemetry/resource-detector-alibaba-cloud": ["@opentelemetry/resource-detector-alibaba-cloud@0.30.0", "", { "dependencies": { "@opentelemetry/core": "^1.26.0", "@opentelemetry/resources": "^1.10.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-CniMuVcJENb7e6ljXC8BuE8xyHKV6kjHjFzAjbeK7BIq2JSPOqfvC+jjhUYnnSGFnDyoZxJCIbt6XIdwPWRPhg=="], + + "@opentelemetry/resource-detector-aws": ["@opentelemetry/resource-detector-aws@1.11.0", "", { "dependencies": { "@opentelemetry/core": "^1.0.0", "@opentelemetry/resources": "^1.10.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-j7qQ75enAJrlSPkPowasScuukZ2ffFG659rhxOpUM4dBe/O8Jpq+dy4pIdFtjWKkM9i7LgisdUt/GW7wGIWoEQ=="], + + "@opentelemetry/resource-detector-azure": ["@opentelemetry/resource-detector-azure@0.6.0", "", { "dependencies": { "@opentelemetry/core": "^1.25.1", "@opentelemetry/resources": "^1.10.1", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-cQbR/x9IhCYk47GWt4uC1G5yQN8JJ02Ec8uT38fj7uIXRbAARulwGr7Ax0dUo0eAtXEKQ+fXdzkLR1Am8cw4mg=="], + + "@opentelemetry/resource-detector-container": ["@opentelemetry/resource-detector-container@0.6.0", "", { "dependencies": { "@opentelemetry/core": "^1.26.0", "@opentelemetry/resources": "^1.10.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-HxOzOsGlIjAbnTjwRBWQOsjrQIZ4NnQaaBc6noO8fW0v9ahyRxzwDFVr/3X1kSYLnpr2RGeWmMGDX6VcHECsLA=="], + + "@opentelemetry/resource-detector-gcp": ["@opentelemetry/resource-detector-gcp@0.33.0", "", { "dependencies": { "@opentelemetry/core": "^1.0.0", "@opentelemetry/resources": "^1.10.0", "@opentelemetry/semantic-conventions": "^1.27.0", "gcp-metadata": "^6.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-y368hq2UM6j42Py7xlR4rTfl+wC4CdGNGT38nqW+6BwGTQso0NC/GeifcwqorEKs/JWU9azA6XNDyUBNEjFpGA=="], + + "@opentelemetry/resources": ["@opentelemetry/resources@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA=="], + + "@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.57.1", "", { "dependencies": { "@opentelemetry/api-logs": "0.57.1", "@opentelemetry/core": "1.30.1", "@opentelemetry/resources": "1.30.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-jGdObb/BGWu6Peo3cL3skx/Rl1Ak/wDDO3vpPrrThGbqE7isvkCsX6uE+OAt8Ayjm9YC8UGkohWbLR09JmM0FA=="], + + "@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/resources": "1.30.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog=="], + + "@opentelemetry/sdk-node": ["@opentelemetry/sdk-node@0.57.1", "", { "dependencies": { "@opentelemetry/api-logs": "0.57.1", "@opentelemetry/core": "1.30.1", "@opentelemetry/exporter-logs-otlp-grpc": "0.57.1", "@opentelemetry/exporter-logs-otlp-http": "0.57.1", "@opentelemetry/exporter-logs-otlp-proto": "0.57.1", "@opentelemetry/exporter-metrics-otlp-grpc": "0.57.1", "@opentelemetry/exporter-metrics-otlp-http": "0.57.1", "@opentelemetry/exporter-metrics-otlp-proto": "0.57.1", "@opentelemetry/exporter-prometheus": "0.57.1", "@opentelemetry/exporter-trace-otlp-grpc": "0.57.1", "@opentelemetry/exporter-trace-otlp-http": "0.57.1", "@opentelemetry/exporter-trace-otlp-proto": "0.57.1", "@opentelemetry/exporter-zipkin": "1.30.1", "@opentelemetry/instrumentation": "0.57.1", "@opentelemetry/resources": "1.30.1", "@opentelemetry/sdk-logs": "0.57.1", "@opentelemetry/sdk-metrics": "1.30.1", "@opentelemetry/sdk-trace-base": "1.30.1", "@opentelemetry/sdk-trace-node": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-0i25YQCpNiE1RDiaZ6ECO3Hgd6DIJeyHyA2AY9C4szMdZV5cM2m8/nrwK6fyNZdOEjRd54D/FkyP3aqZVIPGvg=="], + + "@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/resources": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg=="], + + "@opentelemetry/sdk-trace-node": ["@opentelemetry/sdk-trace-node@1.30.1", "", { "dependencies": { "@opentelemetry/context-async-hooks": "1.30.1", "@opentelemetry/core": "1.30.1", "@opentelemetry/propagator-b3": "1.30.1", "@opentelemetry/propagator-jaeger": "1.30.1", "@opentelemetry/sdk-trace-base": "1.30.1", "semver": "^7.5.2" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-cBjYOINt1JxXdpw1e5MlHmFRc5fgj4GW/86vsKFxJCJ8AL4PdVtYH41gWwl4qd4uQjqEL1oJVrXkSy5cnduAnQ=="], + + "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], + + "@opentelemetry/sql-common": ["@opentelemetry/sql-common@0.40.1", "", { "dependencies": { "@opentelemetry/core": "^1.1.0" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0" } }, "sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg=="], + "@petamoriken/float16": ["@petamoriken/float16@3.8.7", "", {}, "sha512-/Ri4xDDpe12NT6Ex/DRgHzLlobiQXEW/hmG08w1wj/YU7hLemk97c+zHQFp0iZQ9r7YqgLEXZR2sls4HxBf9NA=="], "@pinecone-database/pinecone": ["@pinecone-database/pinecone@3.0.3", "", { "dependencies": { "encoding": "^0.1.13" } }, "sha512-0cAG0d/6knVZgVyXM1II4qG3dyOepLuAQsCXTOJomdA7iQxf+/Om9mq9Cw4QObr56oZ+lqtptlw5qz0BQaBX2Q=="], @@ -562,7 +720,7 @@ "@tsconfig/node16": ["@tsconfig/node16@1.0.4", "", {}, "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA=="], - "@types/aws-lambda": ["@types/aws-lambda@8.10.145", "", {}, "sha512-dtByW6WiFk5W5Jfgz1VM+YPA21xMXTuSFoLYIDY0L44jDLLflVPtZkYuu3/YxpGcvjzKFBZLU+GyKjR0HOYtyw=="], + "@types/aws-lambda": ["@types/aws-lambda@8.10.143", "", {}, "sha512-u5vzlcR14ge/4pMTTMDQr3MF0wEe38B2F9o84uC4F43vN5DGTy63npRrB6jQhyt+C0lGv4ZfiRcRkqJoZuPnmg=="], "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], @@ -576,6 +734,8 @@ "@types/btoa-lite": ["@types/btoa-lite@1.0.2", "", {}, "sha512-ZYbcE2x7yrvNFJiU7xJGrpF/ihpkM7zKgw8bha3LNJSesvTtUNxbpzaT7WXBIryf6jovisrxTBvymxMeLLj1Mg=="], + "@types/bunyan": ["@types/bunyan@1.8.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-ZqS9JGpBxVOvsawzmVt30sP++gSQMTejCkIAQ3VdadOcRE8izTyW66hufvwLeH+YEGP6Js2AW7Gz+RMyvrEbmw=="], + "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], "@types/diff-match-patch": ["@types/diff-match-patch@1.0.36", "", {}, "sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg=="], @@ -604,14 +764,20 @@ "@types/long": ["@types/long@4.0.2", "", {}, "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA=="], + "@types/memcached": ["@types/memcached@2.2.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-AM9smvZN55Gzs2wRrqeMHVP7KE8KWgCJO/XL5yCly2xF6EKa4YlbpK+cLSAH4NG/Ah64HrlegmGqW8kYws7Vxg=="], + "@types/mime": ["@types/mime@1.3.5", "", {}, "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="], + "@types/mysql": ["@types/mysql@2.15.26", "", { "dependencies": { "@types/node": "*" } }, "sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ=="], + "@types/node": ["@types/node@22.9.0", "", { "dependencies": { "undici-types": "~6.19.8" } }, "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ=="], "@types/node-fetch": ["@types/node-fetch@2.6.11", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.0" } }, "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g=="], "@types/pg": ["@types/pg@8.11.10", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^4.0.1" } }, "sha512-LczQUW4dbOQzsH2RQ5qoeJ6qJPdrcM/DcMLoqWQkMLMsq83J5lAX3LXjdkWdpscFy67JSOWDnh7Ny/sPFykmkg=="], + "@types/pg-pool": ["@types/pg-pool@2.0.6", "", { "dependencies": { "@types/pg": "*" } }, "sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ=="], + "@types/prop-types": ["@types/prop-types@15.7.13", "", {}, "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA=="], "@types/qs": ["@types/qs@6.9.17", "", {}, "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ=="], @@ -626,8 +792,12 @@ "@types/serve-static": ["@types/serve-static@1.15.7", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "*" } }, "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw=="], + "@types/shimmer": ["@types/shimmer@1.2.0", "", {}, "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg=="], + "@types/stack-utils": ["@types/stack-utils@2.0.3", "", {}, "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="], + "@types/tedious": ["@types/tedious@4.0.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw=="], + "@types/tough-cookie": ["@types/tough-cookie@4.0.5", "", {}, "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA=="], "@types/triple-beam": ["@types/triple-beam@1.3.5", "", {}, "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="], @@ -648,6 +818,8 @@ "@upstash/vector": ["@upstash/vector@1.1.7", "", {}, "sha512-yxgXdH/Z2uX8XUVrLeo6ZNci2Z8EzXGwjIZ8EZwctF5+oiBIKwz8tsh/yUesyGrbuQ+ViBPGtZWV5vLFbp649w=="], + "@vercel/otel": ["@vercel/otel@1.10.1", "", { "peerDependencies": { "@opentelemetry/api": "^1.7.0", "@opentelemetry/api-logs": ">=0.46.0 && <1.0.0", "@opentelemetry/instrumentation": ">=0.46.0 && <1.0.0", "@opentelemetry/resources": "^1.19.0", "@opentelemetry/sdk-logs": ">=0.46.0 && <1.0.0", "@opentelemetry/sdk-metrics": "^1.19.0", "@opentelemetry/sdk-trace-base": "^1.19.0" } }, "sha512-jZrHSAP03WVUfu1yid4AkYAZ/pwde/txVBSovk5kxQ99Bmb8sqMmQ9RJCSbWuV1EvbTimrVotAP0Devi/qHXgw=="], + "@vladfrangu/async_event_emitter": ["@vladfrangu/async_event_emitter@2.4.6", "", {}, "sha512-RaI5qZo6D2CVS6sTHFKg1v5Ohq/+Bo2LZ5gzUEwZ/WkHhwtGTCB/sVLw8ijOkAUxasZ+WshN/Rzj4ywsABJ5ZA=="], "@vue/compiler-core": ["@vue/compiler-core@3.5.12", "", { "dependencies": { "@babel/parser": "^7.25.3", "@vue/shared": "3.5.12", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw=="], @@ -684,6 +856,8 @@ "acorn": ["acorn@8.14.0", "", { "bin": "bin/acorn" }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="], + "acorn-import-attributes": ["acorn-import-attributes@1.9.5", "", { "peerDependencies": { "acorn": "^8" } }, "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ=="], + "acorn-typescript": ["acorn-typescript@1.4.13", "", { "peerDependencies": { "acorn": ">=8.9.0" } }, "sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q=="], "acorn-walk": ["acorn-walk@8.3.4", "", { "dependencies": { "acorn": "^8.11.0" } }, "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g=="], @@ -1052,6 +1226,8 @@ "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-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="], @@ -1072,7 +1248,7 @@ "gaxios": ["gaxios@6.7.1", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", "node-fetch": "^2.6.9", "uuid": "^9.0.1" } }, "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ=="], - "gcp-metadata": ["gcp-metadata@5.3.0", "", { "dependencies": { "gaxios": "^5.0.0", "json-bigint": "^1.0.0" } }, "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w=="], + "gcp-metadata": ["gcp-metadata@6.1.0", "", { "dependencies": { "gaxios": "^6.0.0", "json-bigint": "^1.0.0" } }, "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg=="], "generic-pool": ["generic-pool@3.9.0", "", {}, "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g=="], @@ -1154,6 +1330,8 @@ "immediate": ["immediate@3.0.6", "", {}, "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="], + "import-in-the-middle": ["import-in-the-middle@1.12.0", "", { "dependencies": { "acorn": "^8.8.2", "acorn-import-attributes": "^1.9.5", "cjs-module-lexer": "^1.2.2", "module-details-from-path": "^1.0.3" } }, "sha512-yAgSE7GmtRcu4ZUSFX/4v69UGXwugFFSdIQJ14LHPOPPQrWv8Y7O9PHsw8Ovk7bKCLe4sjXMbZFqGFcLHpZ89w=="], + "import-local": ["import-local@3.2.0", "", { "dependencies": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" }, "bin": { "import-local-fixture": "fixtures/cli.js" } }, "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA=="], "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], @@ -1310,6 +1488,12 @@ "kuler": ["kuler@2.0.0", "", {}, "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="], + "langfuse": ["langfuse@3.34.1", "", { "dependencies": { "langfuse-core": "^3.34.1" } }, "sha512-vIzQjzHra29RuiGvgpMg3zaYZ9iwRl0zAbGM3ogMViMcg7meixt53vd2tVVYJzcXPxNxOkOAxBomryrownIRzg=="], + + "langfuse-core": ["langfuse-core@3.35.1", "", { "dependencies": { "mustache": "^4.2.0" } }, "sha512-BBbtdI3yiG3OScoZ5NhzLtxSGIQTd3UKYbw9hAw5lzros47YYPoJLcSpLaKiK3xg+6fcWR0/RaNKRnXwuHTwVQ=="], + + "langfuse-vercel": ["langfuse-vercel@3.35.1", "", { "dependencies": { "langfuse": "^3.35.1", "langfuse-core": "^3.35.1" }, "peerDependencies": { "ai": ">=3.2.44" } }, "sha512-/OAjHnX2n/6KgkzMj70rgsdbufvmSFKB9pCzWbC07b7qkY/l4wfjM73oRwoHJ+SHswpXMWHnbTxpTTgcfSHhCg=="], + "leac": ["leac@0.6.0", "", {}, "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg=="], "leven": ["leven@3.1.0", "", {}, "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A=="], @@ -1414,6 +1598,8 @@ "mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="], + "module-details-from-path": ["module-details-from-path@1.0.3", "", {}, "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A=="], + "moment": ["moment@2.30.1", "", {}, "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how=="], "mongodb": ["mongodb@6.10.0", "", { "dependencies": { "@mongodb-js/saslprep": "^1.1.5", "bson": "^6.7.0", "mongodb-connection-string-url": "^3.0.0" }, "peerDependencies": { "@aws-sdk/credential-providers": "^3.188.0", "@mongodb-js/zstd": "^1.1.0", "gcp-metadata": "^5.2.0", "kerberos": "^2.0.1", "mongodb-client-encryption": ">=6.0.0 <7", "snappy": "^7.2.2", "socks": "^2.7.1" }, "optionalPeers": ["@mongodb-js/zstd", "kerberos", "mongodb-client-encryption", "snappy", "socks"] }, "sha512-gP9vduuYWb9ZkDM546M+MP2qKVk5ZG2wPF63OvSRuUbqCR+11ZCAE1mOfllhlAG0wcoJY5yDL/rV3OmYEwXIzg=="], @@ -1422,6 +1608,8 @@ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "mustache": ["mustache@4.2.0", "", { "bin": { "mustache": "bin/mustache" } }, "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ=="], + "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], "nan": ["nan@2.22.0", "", {}, "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw=="], @@ -1640,6 +1828,8 @@ "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@7.5.0", "", { "dependencies": { "debug": "^4.3.5", "module-details-from-path": "^1.0.3", "resolve": "^1.22.8" } }, "sha512-/Tvpny/RVVicqlYTKwt/GtpZRsPG1CmJNhxVKGz+Sy/4MONfXCVNK69MFgGKdUt0/324q3ClI2dICcPgISrC8g=="], + "requires-port": ["requires-port@1.0.0", "", {}, "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="], "resolve": ["resolve@1.22.8", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="], @@ -1700,6 +1890,8 @@ "shell-quote": ["shell-quote@1.8.1", "", {}, "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA=="], + "shimmer": ["shimmer@1.2.1", "", {}, "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw=="], + "side-channel": ["side-channel@1.0.6", "", { "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.4", "object-inspect": "^1.13.1" } }, "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA=="], "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], @@ -1918,6 +2110,8 @@ "xmlbuilder": ["xmlbuilder@10.1.1", "", {}, "sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg=="], + "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], @@ -1988,10 +2182,16 @@ "@octokit/auth-unauthenticated/@octokit/types": ["@octokit/types@12.6.0", "", { "dependencies": { "@octokit/openapi-types": "^20.0.0" } }, "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw=="], + "@octokit/oauth-app/@types/aws-lambda": ["@types/aws-lambda@8.10.145", "", {}, "sha512-dtByW6WiFk5W5Jfgz1VM+YPA21xMXTuSFoLYIDY0L44jDLLflVPtZkYuu3/YxpGcvjzKFBZLU+GyKjR0HOYtyw=="], + "@octokit/plugin-retry/@octokit/types": ["@octokit/types@12.6.0", "", { "dependencies": { "@octokit/openapi-types": "^20.0.0" } }, "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw=="], "@octokit/plugin-throttling/@octokit/types": ["@octokit/types@12.6.0", "", { "dependencies": { "@octokit/openapi-types": "^20.0.0" } }, "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw=="], + "@opentelemetry/instrumentation-connect/@types/connect": ["@types/connect@3.4.36", "", { "dependencies": { "@types/node": "*" } }, "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w=="], + + "@opentelemetry/instrumentation-pg/@types/pg": ["@types/pg@8.6.1", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w=="], + "@rauschma/stringio/@types/node": ["@types/node@10.17.60", "", {}, "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw=="], "@slack/bolt/@types/express": ["@types/express@4.17.21", "", { "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", "@types/serve-static": "*" } }, "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ=="], @@ -2068,10 +2268,6 @@ "gaxios/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], - "gcp-metadata/gaxios": ["gaxios@5.1.3", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^5.0.0", "is-stream": "^2.0.0", "node-fetch": "^2.6.9" } }, "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA=="], - - "google-auth-library/gcp-metadata": ["gcp-metadata@6.1.0", "", { "dependencies": { "gaxios": "^6.0.0", "json-bigint": "^1.0.0" } }, "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg=="], - "groq-sdk/@types/node": ["@types/node@18.19.64", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-955mDqvO2vFf/oL7V3WiUtiz+BugyX8uVbaT2H8oj3+8dRyH2FLiNdowe7eNqRM7IOIZvzDH76EoAT+gwm6aIQ=="], "http-proxy-agent/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], @@ -2092,6 +2288,8 @@ "jszip/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + "langfuse-vercel/langfuse": ["langfuse@3.35.1", "", { "dependencies": { "langfuse-core": "^3.35.1" } }, "sha512-aVkEV6kUaoZvfwZfgp6doodrO1o+IuUgFgM9XZzQlcXU/3epz5jE24RD/lOtRc0KF+ra0QvvVLjbzSh5RmLaUg=="], + "lint-staged/chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="], "lint-staged/yaml": ["yaml@2.7.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA=="], @@ -2106,6 +2304,8 @@ "minizlib/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], + "mongodb/gcp-metadata": ["gcp-metadata@5.3.0", "", { "dependencies": { "gaxios": "^5.0.0", "json-bigint": "^1.0.0" } }, "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w=="], + "mongodb-connection-string-url/whatwg-url": ["whatwg-url@13.0.0", "", { "dependencies": { "tr46": "^4.1.1", "webidl-conversions": "^7.0.0" } }, "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig=="], "nodemon/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], @@ -2198,6 +2398,8 @@ "@octokit/plugin-throttling/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@20.0.0", "", {}, "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA=="], + "@opentelemetry/instrumentation-pg/@types/pg/pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="], + "@slack/bolt/@types/express/@types/express-serve-static-core": ["@types/express-serve-static-core@4.19.6", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A=="], "@slack/bolt/express/accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], @@ -2252,10 +2454,6 @@ "fs-minipass/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], - "gcp-metadata/gaxios/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], - - "gcp-metadata/gaxios/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], - "groq-sdk/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], "jest-changed-files/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], @@ -2288,6 +2486,8 @@ "mongodb-connection-string-url/whatwg-url/webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="], + "mongodb/gcp-metadata/gaxios": ["gaxios@5.1.3", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^5.0.0", "is-stream": "^2.0.0", "node-fetch": "^2.6.9" } }, "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA=="], + "nodemon/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], "onnx-proto/protobufjs/long": ["long@4.0.0", "", {}, "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="], @@ -2310,6 +2510,14 @@ "@aws-sdk/signature-v4/@smithy/signature-v4/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@1.1.0", "", { "dependencies": { "@smithy/is-array-buffer": "^1.1.0", "tslib": "^2.5.0" } }, "sha512-9m6NXE0ww+ra5HKHCHig20T+FAwxBAm7DIdwc/767uGWbRcY720ybgPacQNB96JMOI7xVr/CDa3oMzKmW4a+kw=="], + "@opentelemetry/instrumentation-pg/@types/pg/pg-types/postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="], + + "@opentelemetry/instrumentation-pg/@types/pg/pg-types/postgres-bytea": ["postgres-bytea@1.0.0", "", {}, "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="], + + "@opentelemetry/instrumentation-pg/@types/pg/pg-types/postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="], + + "@opentelemetry/instrumentation-pg/@types/pg/pg-types/postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="], + "@slack/bolt/express/accepts/negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], "@slack/bolt/express/body-parser/debug": ["debug@3.1.0", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g=="], @@ -2336,11 +2544,11 @@ "colorspace/color/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], - "gcp-metadata/gaxios/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], + "jest-changed-files/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], - "gcp-metadata/gaxios/https-proxy-agent/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], + "mongodb/gcp-metadata/gaxios/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], - "jest-changed-files/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], + "mongodb/gcp-metadata/gaxios/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], "prebuild-install/tar-fs/tar-stream/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], @@ -2352,6 +2560,10 @@ "@slack/bolt/express/send/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + "mongodb/gcp-metadata/gaxios/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], + + "mongodb/gcp-metadata/gaxios/https-proxy-agent/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], + "@slack/bolt/express/body-parser/type-is/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], } } diff --git a/jest.config.js b/jest.config.js index 3b4204d..db31174 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,5 +1,5 @@ module.exports = { - preset: 'ts-jest', - testEnvironment: 'node', - testMatch: ['**/*.spec.ts'], - }; \ No newline at end of file + preset: "ts-jest", + testEnvironment: "node", + testMatch: ["**/*.spec.ts"], +}; diff --git a/nodemon.json b/nodemon.json index fdc27e6..78e77c4 100644 --- a/nodemon.json +++ b/nodemon.json @@ -1,5 +1,5 @@ { - "watch": ["src"], - "ext": "ts", - "exec": "concurrently \"npx tsc --watch\" \"ts-node src/index.ts\"" - } \ No newline at end of file + "watch": ["src"], + "ext": "ts", + "exec": "concurrently \"npx tsc --watch\" \"ts-node src/index.ts\"" +} diff --git a/package.json b/package.json index 5458916..54b5cf5 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "db:generate": "prisma generate", "postinstall": "prisma generate", "vercel-build": "prisma generate && prisma migrate deploy && next build", - "smee": "npx smee-client" + "smee": "npx smee-client", + "prepare": "husky" }, "author": "", "license": "ISC", @@ -56,12 +57,18 @@ "@types/jest": "29.5.14", "@types/react": "18.3.12", "concurrently": "9.1.0", + "husky": "^9.1.7", "jest": "29.7.0", + "lint-staged": "^15.4.3", "nodemon": "3.1.7", + "prettier": "3.4.2", "prisma": "5.22.0", "ts-jest": "29.2.5", "ts-node": "10.9.2" }, + "lint-staged": { + "**/*": "prettier --write --ignore-unknown" + }, "name": "srebot", "version": "1.0.0", "description": "bajo construcción", diff --git a/scripts/init-assistant.ts b/scripts/init-assistant.ts index c353a0b..c922423 100644 --- a/scripts/init-assistant.ts +++ b/scripts/init-assistant.ts @@ -1,7 +1,7 @@ import { getOpenaiClient } from "../src/ai/openai"; import { initConfig } from "../src/lib/init-config"; -initConfig() +initConfig(); const openai = getOpenaiClient(); @@ -20,7 +20,7 @@ async function main() { await createAssistant("sre-assistant"); } else { const sreAssistant = assistants.data.find( - (assistant) => assistant.name === "sre-assistant" + (assistant) => assistant.name === "sre-assistant", ); if (!sreAssistant) { diff --git a/src/aggregator/ContextAggregator.ts b/src/aggregator/ContextAggregator.ts index 1cae0a1..8fe7f23 100644 --- a/src/aggregator/ContextAggregator.ts +++ b/src/aggregator/ContextAggregator.ts @@ -30,7 +30,12 @@ export interface CheckContext { export class CheckContextAggregator { alert: WebhookAlertDto; - plugins = [checklyAggregator, githubAggregator, slackChannelAggregator, knowledgeAggregator]; + plugins = [ + checklyAggregator, + githubAggregator, + slackChannelAggregator, + knowledgeAggregator, + ]; constructor(alert: WebhookAlertDto) { this.alert = alert; @@ -42,11 +47,11 @@ export class CheckContextAggregator { return plugin.fetchContext(this.alert).catch((error) => { console.error( `Error fetching context from ${plugin.name ?? "unknown plugin"}:`, - error + error, ); return []; }); - }) + }), ).then((results) => results.flat()); } } diff --git a/src/aggregator/checkly-aggregator.spec.ts b/src/aggregator/checkly-aggregator.spec.ts index 733de19..b397c8e 100644 --- a/src/aggregator/checkly-aggregator.spec.ts +++ b/src/aggregator/checkly-aggregator.spec.ts @@ -34,8 +34,8 @@ describe("ChecklyService", () => { $UUID: "380b94c2-2c56-4f1d-904a-a6122d96722a", moment: "January 15, 2025", }, - { enableImplicitConversion: true } - ) + { enableImplicitConversion: true }, + ), ); expect(context).toBeDefined(); diff --git a/src/aggregator/checkly-aggregator.ts b/src/aggregator/checkly-aggregator.ts index 4d8ccbc..e1c5e15 100644 --- a/src/aggregator/checkly-aggregator.ts +++ b/src/aggregator/checkly-aggregator.ts @@ -1,7 +1,10 @@ import { CheckContext, ContextKey } from "./ContextAggregator"; import { checkly } from "../checkly/client"; import { WebhookAlertDto } from "../checkly/alertDTO"; -import { mapCheckResultToContextValue, mapCheckToContextValue, } from "../checkly/utils"; +import { + mapCheckResultToContextValue, + mapCheckToContextValue, +} from "../checkly/utils"; export const checklyAggregator = { name: "Checkly", @@ -29,7 +32,7 @@ export const checklyAggregator = { makeCheckContext(ContextKey.ChecklyCheck, mapCheckToContextValue(check)), makeCheckContext( ContextKey.ChecklyResults, - mapCheckResultToContextValue(results) + mapCheckResultToContextValue(results), ), makeCheckContext(ContextKey.ChecklyLogs, logs), ] as CheckContext[]; diff --git a/src/aggregator/github-aggregator.spec.ts b/src/aggregator/github-aggregator.spec.ts index dde6ccc..94016dd 100644 --- a/src/aggregator/github-aggregator.spec.ts +++ b/src/aggregator/github-aggregator.spec.ts @@ -34,8 +34,8 @@ describe.skip("GithubAggregator", () => { $UUID: "test-uuid", moment: "March 15, 2024", }, - { enableImplicitConversion: true } - ) + { enableImplicitConversion: true }, + ), ) .catch((error) => { console.error("Error fetching context:", error); diff --git a/src/aggregator/knowledge-aggregator.ts b/src/aggregator/knowledge-aggregator.ts index d8ea0c0..4bd89ab 100644 --- a/src/aggregator/knowledge-aggregator.ts +++ b/src/aggregator/knowledge-aggregator.ts @@ -1,26 +1,29 @@ import { WebhookAlertDto } from "../checkly/alertDTO"; import { CheckContext, ContextKey } from "./ContextAggregator"; -import { getAllDocuments, KnowledgeDocument, } from "../knowledge-base/knowledgeBase"; +import { + getAllDocuments, + KnowledgeDocument, +} from "../knowledge-base/knowledgeBase"; -const transformDocument = (document: KnowledgeDocument, checkId: string): CheckContext => { +const transformDocument = ( + document: KnowledgeDocument, + checkId: string, +): CheckContext => { return { checkId, value: document.content, - source: 'knowledge', - key: ContextKey.Knowledge.replace( - "$documentSlug", - document.slug - ), + source: "knowledge", + key: ContextKey.Knowledge.replace("$documentSlug", document.slug), analysis: document.summary, } as CheckContext; -} +}; export const knowledgeAggregator = { name: "Knowledge", fetchContext: async (alert: WebhookAlertDto): Promise => { - console.log('Aggregating Knowledge Context...'); - const documents = await getAllDocuments() + console.log("Aggregating Knowledge Context..."); + const documents = await getAllDocuments(); return documents.map((doc) => transformDocument(doc, alert.CHECK_ID)); }, -} +}; diff --git a/src/ai/Assistant.ts b/src/ai/Assistant.ts index e1f149f..2644c39 100644 --- a/src/ai/Assistant.ts +++ b/src/ai/Assistant.ts @@ -79,7 +79,7 @@ export class BaseAssistant { */ public async runStream( runContext?: RunContext | null, - runConfig?: Partial + runConfig?: Partial, ): Promise { this.runContext = runContext ?? this.runContext ?? { @@ -103,7 +103,7 @@ export class BaseAssistant { */ public async runSync( runContext?: RunContext | null, - runConfig?: Partial + runConfig?: Partial, ): Promise { this.runContext = runContext ?? this.runContext ?? { @@ -128,7 +128,7 @@ export class BaseAssistant { */ public async handleRunResult( runResult: Run, - options: RunOptions = { stream: true } + options: RunOptions = { stream: true }, ): Promise { if (!requiresToolAction(runResult)) { await this._onAfterRun(runResult); @@ -144,7 +144,7 @@ export class BaseAssistant { */ public async addMessage( message: string, - options?: Partial + options?: Partial, ): Promise { try { return await openai.beta.threads.messages.create(this.threadId, { @@ -166,7 +166,7 @@ export class BaseAssistant { */ public async addFile( file: Buffer, - purpose: "vision" | "assistants" + purpose: "vision" | "assistants", ): Promise { return await openai.files.create({ file: await toFile(file, "screenshot.png", { @@ -244,7 +244,7 @@ export class BaseAssistant { */ private async submitToolOutputsStream( runId: string, - toolOutputs: RunSubmitToolOutputsParams.ToolOutput[] + toolOutputs: RunSubmitToolOutputsParams.ToolOutput[], ): Promise { return openai.beta.threads.runs.submitToolOutputsStream( this.threadId, @@ -252,7 +252,7 @@ export class BaseAssistant { { tool_outputs: toolOutputs, stream: true, - } + }, ); } @@ -260,7 +260,7 @@ export class BaseAssistant { * Execute a tool call */ private async runTool( - toolCall: RequiredActionFunctionToolCall + toolCall: RequiredActionFunctionToolCall, ): Promise { try { const parameters = JSON.parse(toolCall.function.arguments); @@ -297,7 +297,7 @@ export class BaseAssistant { * Process tool calls and generate outputs */ private async processToolCalls( - run: Run + run: Run, ): Promise { if (!run.required_action?.submit_tool_outputs.tool_calls) { return []; @@ -313,8 +313,8 @@ export class BaseAssistant { const toolOutput = formatToolOutput(toolCall.id, output); this.sendToolDataMessage(toolCall, toolOutput); return toolOutput; - } - ) + }, + ), ); } @@ -324,7 +324,7 @@ export class BaseAssistant { private async submitToolOutputsAndContinue( run: Run, toolOutputs: RunSubmitToolOutputsParams.ToolOutput[], - options: RunOptions + options: RunOptions, ): Promise { if (options.stream) { const runStream = await this.submitToolOutputsStream(run.id, toolOutputs); @@ -336,7 +336,7 @@ export class BaseAssistant { const nextRun = await openai.beta.threads.runs.submitToolOutputsAndPoll( this.threadId, run.id, - { tool_outputs: toolOutputs } + { tool_outputs: toolOutputs }, ); return this.handleRunResult(nextRun, options); @@ -345,7 +345,7 @@ export class BaseAssistant { private sendToolDataMessage( toolCall: RequiredActionFunctionToolCall, - toolOutput: RunSubmitToolOutputsParams.ToolOutput + toolOutput: RunSubmitToolOutputsParams.ToolOutput, ): void { this.runContext?.sendDataMessage?.({ id: toolOutput.tool_call_id, diff --git a/src/ai/Tool.ts b/src/ai/Tool.ts index 85e33e5..3e1d132 100644 --- a/src/ai/Tool.ts +++ b/src/ai/Tool.ts @@ -6,7 +6,10 @@ import { BaseAssistant } from "./Assistant"; // Custom error class for tool-related errors export class ToolError extends Error { - constructor(message: string, public readonly code: string) { + constructor( + message: string, + public readonly code: string, + ) { super(message); this.name = "ToolError"; } @@ -18,7 +21,7 @@ export class ToolError extends Error { export abstract class Tool< TParams extends z.ZodType = z.ZodType, TOutput extends z.ZodType = z.ZodType, - TAgent extends BaseAssistant = BaseAssistant + TAgent extends BaseAssistant = BaseAssistant, > { description: string; parameters: TParams; @@ -118,7 +121,7 @@ export abstract class Tool< * Abstract method to implement tool's core functionality */ protected abstract execute( - input: z.infer + input: z.infer, ): Promise> | z.infer; /** @@ -136,13 +139,13 @@ export abstract class Tool< if (!config.description?.trim()) { throw new ToolError( "Tool description is required", - "INVALID_DESCRIPTION" + "INVALID_DESCRIPTION", ); } if (!config.parameters) { throw new ToolError( "Tool parameters schema is required", - "INVALID_PARAMETERS" + "INVALID_PARAMETERS", ); } if (!config.agent) { @@ -155,7 +158,7 @@ export abstract class Tool< */ private async executeWithRetry( input: z.infer, - attempt: number = 1 + attempt: number = 1, ): Promise> { try { if (this.timeout) { @@ -168,7 +171,7 @@ export abstract class Tool< } // Exponential backoff await new Promise((resolve) => - setTimeout(resolve, Math.pow(2, attempt) * 1000) + setTimeout(resolve, Math.pow(2, attempt) * 1000), ); return this.executeWithRetry(input, attempt + 1); } @@ -178,7 +181,7 @@ export abstract class Tool< * Execute with timeout */ private async executeWithTimeout( - input: z.infer + input: z.infer, ): Promise> { if (!this.timeout) return this.execute(input); @@ -187,8 +190,8 @@ export abstract class Tool< new Promise((_, reject) => setTimeout( () => reject(new ToolError("Execution timeout", "TIMEOUT")), - this.timeout - ) + this.timeout, + ), ), ]); } diff --git a/src/ai/utils.ts b/src/ai/utils.ts index 4c69316..6fdf577 100644 --- a/src/ai/utils.ts +++ b/src/ai/utils.ts @@ -32,7 +32,7 @@ export const cancelRun = async (threadId: string): Promise => { export const formatToolOutput = ( toolCallId: string, - output: unknown + output: unknown, ): RunSubmitToolOutputsParams.ToolOutput => { return { output: JSON.stringify(output), @@ -42,7 +42,7 @@ export const formatToolOutput = ( export const handleToolError = ( toolCallId: string, - error: Error + error: Error, ): RunSubmitToolOutputsParams.ToolOutput => { return { output: stringify({ error: error.message ?? "Unknown error" }), @@ -52,7 +52,7 @@ export const handleToolError = ( export const getRunMessages = async ( threadId: string, - runId: string + runId: string, ): Promise => { const messages = await openai.beta.threads.messages.list(threadId, { run_id: runId, @@ -64,7 +64,7 @@ export const getRunMessages = async ( export const getMessageHistory = async ( threadId: string, cursor: string, - limit: number = 30 + limit: number = 30, ): Promise => { const messages = await openai.beta.threads.messages.list(threadId, { after: cursor, @@ -76,7 +76,7 @@ export const getMessageHistory = async ( export const getRunSteps = async ( threadId: string, - runId: string + runId: string, ): Promise => { const steps = await openai.beta.threads.runs.steps.list(threadId, runId, { order: "asc", @@ -86,7 +86,7 @@ export const getRunSteps = async ( export const getThreadRuns = async ( threadId: string, - limit: number = 30 + limit: number = 30, ): Promise => { const runs = await openai.beta.threads.runs.list(threadId, { limit, @@ -100,7 +100,7 @@ export const getThreadSteps = async (threadId: string): Promise => { runs.map(async (run) => { const steps = await getRunSteps(threadId, run.id); return steps.map((step) => ({ ...step, runId: run.id })); - }) + }), ); return steps.flat(); }; diff --git a/src/checkly/PrometheusParser.ts b/src/checkly/PrometheusParser.ts index 6539f9d..c14a6e5 100644 --- a/src/checkly/PrometheusParser.ts +++ b/src/checkly/PrometheusParser.ts @@ -1,39 +1,45 @@ -import { PrometheusMetric, PrometheusMetricValue } from './PrometheusMetric'; +import { PrometheusMetric, PrometheusMetricValue } from "./PrometheusMetric"; export class PrometheusParser { static parse(input: string): PrometheusMetric[] { - const lines = input.split('\n'); + const lines = input.split("\n"); const metrics: PrometheusMetric[] = []; let currentMetric: PrometheusMetric | null = null; for (const line of lines) { - if (line.startsWith('# HELP')) { - const parts = line.split(' '); + if (line.startsWith("# HELP")) { + const parts = line.split(" "); const metricName = parts[2]; - const help = parts.slice(3).join(' '); - currentMetric = new PrometheusMetric(metricName, help, ''); + const help = parts.slice(3).join(" "); + currentMetric = new PrometheusMetric(metricName, help, ""); metrics.push(currentMetric); - } else if (line.startsWith('# TYPE')) { - const parts = line.split(' '); + } else if (line.startsWith("# TYPE")) { + const parts = line.split(" "); const metricName = parts[2]; const type = parts[3]; - currentMetric = metrics.find(m => m.metricName === metricName) || null; + currentMetric = + metrics.find((m) => m.metricName === metricName) || null; if (currentMetric) { currentMetric.type = type; } - } else if (line.trim() !== '') { - const lastSpaceIndex = line.lastIndexOf(' '); + } else if (line.trim() !== "") { + const lastSpaceIndex = line.lastIndexOf(" "); const valuePart = line.substring(lastSpaceIndex + 1); const metricPart = line.substring(0, lastSpaceIndex); - const metricName = metricPart.split('{')[0]; - const labelsPart = metricPart.split('{')[1]?.split('}')[0]; + const metricName = metricPart.split("{")[0]; + const labelsPart = metricPart.split("{")[1]?.split("}")[0]; const labels = labelsPart - ? Object.fromEntries(labelsPart.split(',').map(l => l.split('=').map(s => s.replace(/"/g, '')))) + ? Object.fromEntries( + labelsPart + .split(",") + .map((l) => l.split("=").map((s) => s.replace(/"/g, ""))), + ) : {}; const value = parseFloat(valuePart); - currentMetric = metrics.find(m => m.metricName === metricName) || null; + currentMetric = + metrics.find((m) => m.metricName === metricName) || null; if (currentMetric) { currentMetric.addValue(new PrometheusMetricValue(labels, value)); } diff --git a/src/checkly/alertDTO.ts b/src/checkly/alertDTO.ts index afccc6c..0f21ef0 100644 --- a/src/checkly/alertDTO.ts +++ b/src/checkly/alertDTO.ts @@ -1,5 +1,13 @@ -import { Transform } from 'class-transformer'; -import { IsArray, IsDate, IsEnum, IsNumber, IsOptional, IsString, IsUUID, } from 'class-validator'; +import { Transform } from "class-transformer"; +import { + IsArray, + IsDate, + IsEnum, + IsNumber, + IsOptional, + IsString, + IsUUID, +} from "class-validator"; /** * Enum representing the different alert types. @@ -11,52 +19,50 @@ export enum AlertType { /** * Nothing to see here, keep moving. */ - NO_ALERT = 'NO_ALERT', + NO_ALERT = "NO_ALERT", /** * Send directly, if threshold is “alert after 1 failure”. */ - ALERT_DEGRADED = 'ALERT_DEGRADED', + ALERT_DEGRADED = "ALERT_DEGRADED", /** * Send directly, if threshold is “alert after 1 failure”. */ - ALERT_FAILURE = 'ALERT_FAILURE', + ALERT_FAILURE = "ALERT_FAILURE", /** * i.e. when threshold is “alert after 2 failures” or “after 5 minutes”. */ - ALERT_DEGRADED_REMAIN = 'ALERT_DEGRADED_REMAIN', + ALERT_DEGRADED_REMAIN = "ALERT_DEGRADED_REMAIN", /** * Send but only if you received a degraded notification before. */ - ALERT_DEGRADED_RECOVERY = 'ALERT_DEGRADED_RECOVERY', + ALERT_DEGRADED_RECOVERY = "ALERT_DEGRADED_RECOVERY", /** * This is an escalation, it overrides any threshold setting. We send this even if you already received degraded notifications. */ - ALERT_DEGRADED_FAILURE = 'ALERT_DEGRADED_FAILURE', + ALERT_DEGRADED_FAILURE = "ALERT_DEGRADED_FAILURE", /** * i.e. when threshold is “alert after 2 failures” or “after 5 minutes”. */ - ALERT_FAILURE_REMAIN = 'ALERT_FAILURE_REMAIN', + ALERT_FAILURE_REMAIN = "ALERT_FAILURE_REMAIN", /** * This is a deescalation, it overrides any thresholds settings. We send this even if you already received failure notifications. */ - ALERT_FAILURE_DEGRADED = 'ALERT_FAILURE_DEGRADED', + ALERT_FAILURE_DEGRADED = "ALERT_FAILURE_DEGRADED", /** * Send directly. */ - ALERT_RECOVERY = 'ALERT_RECOVERY', + ALERT_RECOVERY = "ALERT_RECOVERY", } - export class WebhookAlertDto { - @IsString() CHECK_NAME: string; @@ -114,7 +120,7 @@ export class WebhookAlertDto { return []; } // If the value is a valid stringified JSON array, parse it - const parsed = typeof value === 'string' ? JSON.parse(value) : value; + const parsed = typeof value === "string" ? JSON.parse(value) : value; // Return the value only if it's a valid array, otherwise return an empty array return Array.isArray(parsed) ? parsed : []; diff --git a/src/checkly/bla.ts b/src/checkly/bla.ts index 2a95fab..48199c0 100644 --- a/src/checkly/bla.ts +++ b/src/checkly/bla.ts @@ -1,22 +1,22 @@ const exampleAlert = { - CHECK_NAME: 'fail50', - CHECK_ID: 'b68422ae-6528-45a5-85a6-e85e1be9de2e', - CHECK_TYPE: 'MULTI_STEP', - GROUP_NAME: '', - ALERT_TITLE: 'fail50 has failed', - ALERT_TYPE: 'ALERT_FAILURE', - CHECK_RESULT_ID: '995b7d3c-d42a-443a-a8b3-194319436ba7', - RESPONSE_TIME: '1649', - API_CHECK_RESPONSE_STATUS_CODE: '', - API_CHECK_RESPONSE_STATUS_TEXT: '', - RUN_LOCATION: 'Frankfurt', + CHECK_NAME: "fail50", + CHECK_ID: "b68422ae-6528-45a5-85a6-e85e1be9de2e", + CHECK_TYPE: "MULTI_STEP", + GROUP_NAME: "", + ALERT_TITLE: "fail50 has failed", + ALERT_TYPE: "ALERT_FAILURE", + CHECK_RESULT_ID: "995b7d3c-d42a-443a-a8b3-194319436ba7", + RESPONSE_TIME: "1649", + API_CHECK_RESPONSE_STATUS_CODE: "", + API_CHECK_RESPONSE_STATUS_TEXT: "", + RUN_LOCATION: "Frankfurt", RESULT_LINK: - 'https://app.checklyhq.com/checks/b68422ae-6528-45a5-85a6-e85e1be9de2e/results/multi_step/995b7d3c-d42a-443a-a8b3-194319436ba7', - SSL_DAYS_REMAINING: '', - SSL_CHECK_DOMAIN: '', - STARTED_AT: '2024-10-09T13:30:22.741Z', - TAGS: '', - $RANDOM_NUMBER: '271', - $UUID: '94a5dc1e-9d84-42d5-8a9c-e0fd859616d9', - moment: 'October 09, 2024', + "https://app.checklyhq.com/checks/b68422ae-6528-45a5-85a6-e85e1be9de2e/results/multi_step/995b7d3c-d42a-443a-a8b3-194319436ba7", + SSL_DAYS_REMAINING: "", + SSL_CHECK_DOMAIN: "", + STARTED_AT: "2024-10-09T13:30:22.741Z", + TAGS: "", + $RANDOM_NUMBER: "271", + $UUID: "94a5dc1e-9d84-42d5-8a9c-e0fd859616d9", + moment: "October 09, 2024", }; diff --git a/src/checkly/checklyclient.spec.ts b/src/checkly/checklyclient.spec.ts index 4c66a65..297807b 100644 --- a/src/checkly/checklyclient.spec.ts +++ b/src/checkly/checklyclient.spec.ts @@ -1,28 +1,24 @@ -import { ChecklyClient } from './checklyclient'; -import 'dotenv/config'; - +import { ChecklyClient } from "./checklyclient"; +import "dotenv/config"; jest.setTimeout(30000); -describe('ChecklyService', () => { +describe("ChecklyService", () => { const client: ChecklyClient = new ChecklyClient(); - beforeEach(async () => { - }); + beforeEach(async () => {}); - it('can download all checks', async () => { - const result = await client.getChecks(); - expect(result).toBeDefined(); - const activated = result.filter((r) => r.activated); - expect(activated).toBeDefined(); - } - ); - it('can find activated checks', async () => { - const result = await client.getActivatedChecks(); - expect(result).toBeDefined(); - } - ); + it("can download all checks", async () => { + const result = await client.getChecks(); + expect(result).toBeDefined(); + const activated = result.filter((r) => r.activated); + expect(activated).toBeDefined(); + }); + it("can find activated checks", async () => { + const result = await client.getActivatedChecks(); + expect(result).toBeDefined(); + }); - it('get failed results', async () => { + it("get failed results", async () => { const s = await client.getActivatedChecks(); const result = await client.getCheckResults(s[1].id, true, 100); @@ -30,13 +26,13 @@ describe('ChecklyService', () => { expect(result).toBeDefined(); }); - it('should be defined', async () => { + it("should be defined", async () => { const checks = await client.getChecks(); const result = await client.getCheck(checks[0].id); expect(result).toBeDefined(); }); - it('can download prometheus metrics', async () => { + it("can download prometheus metrics", async () => { const result = await client.getPrometheusCheckStatus(); expect(result).toBeDefined(); }); @@ -52,7 +48,4 @@ describe('ChecklyService', () => { ); }); */ - }); - - diff --git a/src/checkly/checklyclient.ts b/src/checkly/checklyclient.ts index 72fbfe1..41c2638 100644 --- a/src/checkly/checklyclient.ts +++ b/src/checkly/checklyclient.ts @@ -1,8 +1,8 @@ -import { plainToClass, plainToInstance } from 'class-transformer'; -import * as fs from 'fs'; -import fetch from 'node-fetch'; -import { Check, CheckGroup, CheckResult } from './models'; -import { PrometheusParser } from './PrometheusParser'; +import { plainToClass, plainToInstance } from "class-transformer"; +import * as fs from "fs"; +import fetch from "node-fetch"; +import { Check, CheckGroup, CheckResult } from "./models"; +import { PrometheusParser } from "./PrometheusParser"; interface ChecklyClientOptions { accountId?: string; @@ -35,10 +35,13 @@ export class ChecklyClient { constructor(options: ChecklyClientOptions = {}) { this.accountId = options.accountId || process.env.CHECKLY_ACCOUNT_ID!; this.apiKey = options.apiKey || process.env.CHECKLY_API_KEY!; - this.checklyApiUrl = options.checklyApiUrl || 'https://api.checklyhq.com/v1/'; - this.checklyPrometheusKey = options.checklyPrometheusKey || process.env.PROMETHEUS_INTEGRATION_KEY!; - this.prometheusIntegrationUrl = options.prometheusIntegrationUrl || `https://api.checklyhq.com/accounts/${this.accountId}/v2/prometheus/metrics`; - + this.checklyApiUrl = + options.checklyApiUrl || "https://api.checklyhq.com/v1/"; + this.checklyPrometheusKey = + options.checklyPrometheusKey || process.env.PROMETHEUS_INTEGRATION_KEY!; + this.prometheusIntegrationUrl = + options.prometheusIntegrationUrl || + `https://api.checklyhq.com/accounts/${this.accountId}/v2/prometheus/metrics`; } async getCheck(checkid: string): Promise { @@ -47,20 +50,20 @@ export class ChecklyClient { } async getChecks(): Promise { - return this.getPaginatedDownload('checks', Check); + return this.getPaginatedDownload("checks", Check); } async getActivatedChecks(): Promise { const results = await Promise.all([ - this.getPaginatedDownload('checks', Check), - this.getPaginatedDownload('check-groups', CheckGroup) - ]) + this.getPaginatedDownload("checks", Check), + this.getPaginatedDownload("check-groups", CheckGroup), + ]); const groups = results[1]; const groupMap = new Map(); - groups.forEach(group => { + groups.forEach((group) => { groupMap.set(group.id, group); }); - const s = results[0].map(check => { + const s = results[0].map((check) => { if (check.activated && !check.groupId) { return check; } @@ -70,17 +73,20 @@ export class ChecklyClient { return check; } } - }) + }); return s.filter((x) => x !== undefined) as Check[]; } - async getPaginatedDownload(path: string, type: { new(): T }): Promise { + async getPaginatedDownload( + path: string, + type: { new (): T }, + ): Promise { const limit = 100; let page = 1; const result = Array(); while (true) { let url = `${this.checklyApiUrl}${path}?limit=${limit}&page=${page}`; - const checks = await this.makeRequest(url, type) as T[]; + const checks = (await this.makeRequest(url, type)) as T[]; result.push(...checks); if (checks.length < 100) { break; @@ -98,13 +104,13 @@ export class ChecklyClient { return this.makeRequest(url, CheckResult) as Promise; } - async makeRequest(url: string, type: { new(): T }): Promise { + async makeRequest(url: string, type: { new (): T }): Promise { try { const response = await fetch(url, { - method: 'GET', // Optional, default is 'GET' + method: "GET", // Optional, default is 'GET' headers: { Authorization: `Bearer ${this.apiKey}`, // Add Authorization header - 'X-Checkly-Account': this.accountId, // Add custom X-Checkly-Account header + "X-Checkly-Account": this.accountId, // Add custom X-Checkly-Account header }, }); if (!response.ok) { @@ -126,10 +132,10 @@ export class ChecklyClient { async downloadAsset(assetUrl: string, outputFilePath: string): Promise { const url = assetUrl; const response = await fetch(url, { - method: 'GET', + method: "GET", headers: { Authorization: `Bearer ${this.apiKey}`, - 'X-Checkly-Account': this.accountId, + "X-Checkly-Account": this.accountId, }, }); @@ -140,29 +146,33 @@ export class ChecklyClient { const fileStream = fs.createWriteStream(outputFilePath); return new Promise((resolve, reject) => { response!.body!.pipe(fileStream); - response!.body!.on('error', (err: Error) => { + response!.body!.on("error", (err: Error) => { reject(err); }); - fileStream.on('finish', () => { + fileStream.on("finish", () => { resolve(); }); }); } // Uses the last 6 hours as a time frame - async getCheckResults(checkid: string, hasFailures?: boolean, limit?: number): Promise { + async getCheckResults( + checkid: string, + hasFailures?: boolean, + limit?: number, + ): Promise { limit = limit || 100; - let hasFailuresQuery = ''; + let hasFailuresQuery = ""; if (hasFailures !== undefined) { hasFailuresQuery = `hasFailures=${hasFailures}&`; } const url = `https://api.checklyhq.com/v1/check-results/${checkid}?limit=${limit}&page=1&${hasFailuresQuery}resultType=FINAL`; const response = await fetch(url, { - method: 'GET', + method: "GET", headers: { - accept: 'application/json', + accept: "application/json", Authorization: `Bearer ${this.apiKey}`, - 'X-Checkly-Account': this.accountId, + "X-Checkly-Account": this.accountId, }, }); @@ -177,11 +187,11 @@ export class ChecklyClient { async getPrometheusCheckStatus() { try { const response = await fetch(this.prometheusIntegrationUrl, { - method: 'GET', + method: "GET", headers: { - 'Authorization': `Bearer ${this.checklyPrometheusKey}` - } - }) + Authorization: `Bearer ${this.checklyPrometheusKey}`, + }, + }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); @@ -189,17 +199,27 @@ export class ChecklyClient { const text = await response.text(); const metrics = PrometheusParser.parse(text); - const statusmetric = metrics.filter(m => m.metricName === 'checkly_check_status')[0]; + const statusmetric = metrics.filter( + (m) => m.metricName === "checkly_check_status", + )[0]; - const ac = statusmetric.values.filter(v => v.labels.activated === 'true'); + const ac = statusmetric.values.filter( + (v) => v.labels.activated === "true", + ); // status is either failing, passing or degraded // the value is 1 if status is true, 0 if false - const failing = ac.filter(v => v.labels.status === 'failing' && v.value === 1); - const passing = ac.filter(v => v.labels.status === 'passing' && v.value === 1); - const degraded = ac.filter(v => v.labels.status === 'degraded' && v.value === 1); + const failing = ac.filter( + (v) => v.labels.status === "failing" && v.value === 1, + ); + const passing = ac.filter( + (v) => v.labels.status === "passing" && v.value === 1, + ); + const degraded = ac.filter( + (v) => v.labels.status === "degraded" && v.value === 1, + ); return { failing, passing, degraded }; } catch (error) { - console.error('Error fetching Prometheus metrics:', error); + console.error("Error fetching Prometheus metrics:", error); throw error; } } diff --git a/src/checkly/models.ts b/src/checkly/models.ts index f612a97..2f36d2b 100644 --- a/src/checkly/models.ts +++ b/src/checkly/models.ts @@ -201,7 +201,7 @@ export class CheckResult { return jobLog .map( - (logEntry) => `${logEntry.time} - ${logEntry.level}: ${logEntry.msg}` + (logEntry) => `${logEntry.time} - ${logEntry.level}: ${logEntry.msg}`, ) .join("\n"); } diff --git a/src/checkly/utils.ts b/src/checkly/utils.ts index 2c57ef9..303b388 100644 --- a/src/checkly/utils.ts +++ b/src/checkly/utils.ts @@ -33,7 +33,10 @@ export const mapCheckResultToContextValue = (result: CheckResult) => { }; }; -export const getLastCheckResult = async (checkId: string, hasFailures: boolean) => { +export const getLastCheckResult = async ( + checkId: string, + hasFailures: boolean, +) => { const results = await checkly.getCheckResults(checkId, hasFailures, 1); return results[0]; }; diff --git a/src/github/agent.spec.ts b/src/github/agent.spec.ts index 418f725..47db349 100644 --- a/src/github/agent.spec.ts +++ b/src/github/agent.spec.ts @@ -1,7 +1,7 @@ -import dotenv from 'dotenv'; -import { createOpenAI, OpenAIProvider } from '@ai-sdk/openai'; -import { GithubAgent } from './agent'; -import GitHubAPI from './github'; +import dotenv from "dotenv"; +import { createOpenAI, OpenAIProvider } from "@ai-sdk/openai"; +import { GithubAgent } from "./agent"; +import GitHubAPI from "./github"; dotenv.config(); @@ -10,7 +10,7 @@ const CHECKLY_GITHUB_TOKEN = process.env.CHECKLY_GITHUB_TOKEN!; jest.setTimeout(120000); // Set timeout to 120 seconds -describe('GithubAgent Tests', () => { +describe("GithubAgent Tests", () => { let openai: OpenAIProvider; let github: GitHubAPI; @@ -19,15 +19,23 @@ describe('GithubAgent Tests', () => { github = new GitHubAPI(CHECKLY_GITHUB_TOKEN); }); - it.skip('should summarize a single release', async () => { - let agent = new GithubAgent(openai('gpt-4o'), github); - let response = await agent.summarizeRelease('checkly', 'checkly-webapp', '2024-11-15-12.56.18', '2024-11-15-11.29.32'); + it.skip("should summarize a single release", async () => { + let agent = new GithubAgent(openai("gpt-4o"), github); + let response = await agent.summarizeRelease( + "checkly", + "checkly-webapp", + "2024-11-15-12.56.18", + "2024-11-15-11.29.32", + ); console.log(response); }); - it.skip('should summarize releases by prompt', async () => { - let agent = new GithubAgent(openai('gpt-4o'), github); - let response = await agent.summarizeReleases('what changed in the ui since yesterday', 'checkly'); + it.skip("should summarize releases by prompt", async () => { + let agent = new GithubAgent(openai("gpt-4o"), github); + let response = await agent.summarizeReleases( + "what changed in the ui since yesterday", + "checkly", + ); console.log(response); }); -}); \ No newline at end of file +}); diff --git a/src/github/github.spec.ts b/src/github/github.spec.ts index fe76101..6fd7ef2 100644 --- a/src/github/github.spec.ts +++ b/src/github/github.spec.ts @@ -1,35 +1,43 @@ -import 'dotenv/config'; -import GitHubAPI from './github'; +import "dotenv/config"; +import GitHubAPI from "./github"; const CHECKLY_GITHUB_TOKEN = process.env.CHECKLY_GITHUB_TOKEN!; -describe('GitHub API Tests', () => { - - it.skip('should print the api usage', async () => { +describe("GitHub API Tests", () => { + it.skip("should print the api usage", async () => { const githubAPI = new GitHubAPI(CHECKLY_GITHUB_TOKEN); await githubAPI.checkRateLimit(); }); - it.skip('should return the latest releases for checkly', async () => { + it.skip("should return the latest releases for checkly", async () => { const githubAPI = new GitHubAPI(CHECKLY_GITHUB_TOKEN); - const org = 'checkly'; - const repo = 'checkly-backend'; + const org = "checkly"; + const repo = "checkly-backend"; const _24h_ago = new Date(Date.now() - 24 * 60 * 60 * 1000); const releases = await githubAPI.queryLatestReleases(org, repo, _24h_ago); - let diff = await githubAPI.getDiffBetweenTags(org, repo, releases[0].tag, releases[1].tag); + let diff = await githubAPI.getDiffBetweenTags( + org, + repo, + releases[0].tag, + releases[1].tag, + ); expect(diff).toBeDefined(); }); - it.skip('should return the latest releases with diffs for checkly', async () => { + it.skip("should return the latest releases with diffs for checkly", async () => { const githubAPI = new GitHubAPI(CHECKLY_GITHUB_TOKEN); - const org = 'checkly'; - const repo = 'checkly-backend'; + const org = "checkly"; + const repo = "checkly-backend"; const _24h_ago = new Date(Date.now() - 24 * 60 * 60 * 1000); - const releasesWithDiffs = await githubAPI.queryLatestReleasesWithDiffs(org, repo, _24h_ago); + const releasesWithDiffs = await githubAPI.queryLatestReleasesWithDiffs( + org, + repo, + _24h_ago, + ); expect(releasesWithDiffs).toBeDefined(); }); }); diff --git a/src/github/github.ts b/src/github/github.ts index 0b7ba04..77506d5 100644 --- a/src/github/github.ts +++ b/src/github/github.ts @@ -1,15 +1,14 @@ import { Octokit } from "octokit"; import { Endpoints } from "@octokit/types"; -import type { - RestEndpointMethodTypes -} from "@octokit/plugin-rest-endpoint-methods/dist-types/generated/parameters-and-response-types"; +import type { RestEndpointMethodTypes } from "@octokit/plugin-rest-endpoint-methods/dist-types/generated/parameters-and-response-types"; type Repository = Endpoints["GET /repos/{owner}/{repo}"]["response"]["data"]; type ListReleasesResponse = Endpoints["GET /repos/{owner}/{repo}/releases"]["response"]["data"]; export type CompareCommitsResponse = Endpoints["GET /repos/{owner}/{repo}/compare/{base}...{head}"]["response"]["data"]; -export type GithubDeploymentInstance = RestEndpointMethodTypes["repos"]["listDeployments"]["response"]["data"][0] +export type GithubDeploymentInstance = + RestEndpointMethodTypes["repos"]["listDeployments"]["response"]["data"][0]; class GitHubAPI { private octokit: Octokit; @@ -33,7 +32,11 @@ class GitHubAPI { } } - async getPreviousReleaseTag(org: string, repoName: string, release: string):Promise { + async getPreviousReleaseTag( + org: string, + repoName: string, + release: string, + ): Promise { try { const { data: releases } = await this.octokit.rest.repos.listReleases({ owner: org, @@ -59,28 +62,29 @@ class GitHubAPI { repoName: string, environment: string, currentDeploymentId: number, - currentDeploymentSha: string - ): Promise { + currentDeploymentSha: string, + ): Promise { try { - const { data: deployments } = await this.octokit.rest.repos.listDeployments({ - owner: org, - repo: repoName, - environment, - per_page: 100, // Fetch up to 100 deployments at once (max allowed by GitHub) - }); + const { data: deployments } = + await this.octokit.rest.repos.listDeployments({ + owner: org, + repo: repoName, + environment, + per_page: 100, // Fetch up to 100 deployments at once (max allowed by GitHub) + }); // Find the index of the current deployment const currentDeploymentIndex = deployments.findIndex( - (d) => d.id === currentDeploymentId + (d) => d.id === currentDeploymentId, ); if (currentDeploymentIndex === -1) { throw new Error(`Deployment with ID ${currentDeploymentId} not found`); } - const previousDeployment = deployments.slice(currentDeploymentIndex).find( - (d => d.sha !== currentDeploymentSha), - ) + const previousDeployment = deployments + .slice(currentDeploymentIndex) + .find((d) => d.sha !== currentDeploymentSha); return previousDeployment || null; } catch (error) { @@ -140,7 +144,7 @@ class GitHubAPI { org: string, repo: string, baseTag: string, - headTag: string + headTag: string, ): Promise { try { const { data: diff } = await this.octokit.rest.repos.compareCommits({ @@ -159,7 +163,7 @@ class GitHubAPI { async queryLatestReleasesWithDiffs( org: string, repoName: string, - since: Date + since: Date, ) { const releases = await this.queryLatestReleases(org, repoName, since); @@ -174,10 +178,10 @@ class GitHubAPI { org, repoName, previousRelease.tag, - release.tag + release.tag, ); return { release, diff }; - }) + }), ); return releaseDiffs; @@ -186,7 +190,7 @@ class GitHubAPI { async getCommits( owner: string, repo: string, - options: { since?: string } = {} + options: { since?: string } = {}, ) { try { const { data: commits } = await this.octokit.rest.repos.listCommits({ @@ -204,7 +208,7 @@ class GitHubAPI { async getPullRequests( owner: string, repo: string, - options: { state?: "open" | "closed" | "all" } = { state: "all" } + options: { state?: "open" | "closed" | "all" } = { state: "all" }, ) { try { const { data: pullRequests } = await this.octokit.rest.pulls.list({ diff --git a/src/github/slackBlock.ts b/src/github/slackBlock.ts index 0648bc8..23f10ac 100644 --- a/src/github/slackBlock.ts +++ b/src/github/slackBlock.ts @@ -9,15 +9,15 @@ export const releaseHeader = { export const divider = { type: "divider" }; export const createReleaseBlock = function ({ - release, - releaseUrl, - diffUrl, - date, - repo, - repoUrl, - authors, - summary, - }: { + release, + releaseUrl, + diffUrl, + date, + repo, + repoUrl, + authors, + summary, +}: { release: string; releaseUrl: string; diffUrl: string; @@ -62,15 +62,15 @@ export const createReleaseBlock = function ({ }; export const createDeploymentBlock = function ({ - diffUrl, - date, - repo, - repoUrl, - authors, - summary, - environment, - deploymentUrl - }: { + diffUrl, + date, + repo, + repoUrl, + authors, + summary, + environment, + deploymentUrl, +}: { diffUrl: string; date: string; repo: string; @@ -78,7 +78,7 @@ export const createDeploymentBlock = function ({ repoUrl: string; authors: string[]; summary: string; - deploymentUrl: string + deploymentUrl: string; }) { return { blocks: [ @@ -112,4 +112,4 @@ export const createDeploymentBlock = function ({ }, ], }; -} +}; diff --git a/src/grafana/grafanaclient.spec.ts b/src/grafana/grafanaclient.spec.ts index 9fc28df..cc21a6e 100644 --- a/src/grafana/grafanaclient.spec.ts +++ b/src/grafana/grafanaclient.spec.ts @@ -1,27 +1,26 @@ -import { GrafanaClient } from './grafanaclient'; -import 'dotenv/config'; +import { GrafanaClient } from "./grafanaclient"; +import "dotenv/config"; const grafanaApiKey = process.env.GRAFANA_API_KEY!; const grafanaUrl = process.env.GRAFANA_INSTANCE_URL!; -const isGithubActions = process.env.GITHUB_ACTIONS === 'true'; +const isGithubActions = process.env.GITHUB_ACTIONS === "true"; const maybe = !isGithubActions ? describe : describe.skip; -maybe('GrafanaClient', () => { +maybe("GrafanaClient", () => { let grafanaClient: GrafanaClient; beforeAll(() => { grafanaClient = new GrafanaClient(grafanaUrl, grafanaApiKey); }); - it('should get dashboards', async () => { + it("should get dashboards", async () => { const dashboards = await grafanaClient.getDashboards(); expect(dashboards).toBeDefined(); - + expect(Array.isArray(dashboards)).toBe(true); - const db = 'Runners Overview' + const db = "Runners Overview"; const dashboard = await grafanaClient.getDashboardUrlByName(db); expect(dashboard).toBeDefined(); }); - -}); \ No newline at end of file +}); diff --git a/src/grafana/grafanaclient.ts b/src/grafana/grafanaclient.ts index 9beb9c9..22610c9 100644 --- a/src/grafana/grafanaclient.ts +++ b/src/grafana/grafanaclient.ts @@ -1,26 +1,30 @@ export class GrafanaClient { - private readonly grafanaUrl: string; - private readonly grafanaApiKey: string; + private readonly grafanaUrl: string; + private readonly grafanaApiKey: string; - constructor(grafanaUrl: string, grafanaApiKey: string) { - this.grafanaUrl = grafanaUrl; - this.grafanaApiKey = grafanaApiKey; - } + constructor(grafanaUrl: string, grafanaApiKey: string) { + this.grafanaUrl = grafanaUrl; + this.grafanaApiKey = grafanaApiKey; + } - async getDashboardUrlByName(dashboardName: string): Promise { + async getDashboardUrlByName(dashboardName: string): Promise { const dashboards = await this.getDashboards(); - const runners = dashboards.filter(d=>d.type==='dash-db' ).filter(d=>d.title.toLowerCase().includes(dashboardName.toLowerCase()))[0] - return runners.url - } + const runners = dashboards + .filter((d) => d.type === "dash-db") + .filter((d) => + d.title.toLowerCase().includes(dashboardName.toLowerCase()), + )[0]; + return runners.url; + } - async getDashboards(): Promise { - const url = `${this.grafanaUrl}/api/search`; + async getDashboards(): Promise { + const url = `${this.grafanaUrl}/api/search`; const response = await fetch(url, { - method: 'GET', + method: "GET", headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${this.grafanaApiKey}` - } + "Content-Type": "application/json", + Authorization: `Bearer ${this.grafanaApiKey}`, + }, }); if (!response.ok) { @@ -28,5 +32,5 @@ export class GrafanaClient { } return response.json(); - } -} \ No newline at end of file + } +} diff --git a/src/grafana/lokiclient.spec.ts b/src/grafana/lokiclient.spec.ts index aad3fa4..d0524cd 100644 --- a/src/grafana/lokiclient.spec.ts +++ b/src/grafana/lokiclient.spec.ts @@ -1,41 +1,40 @@ -import { LokiClient } from './lokiclient'; -import 'dotenv/config'; +import { LokiClient } from "./lokiclient"; +import "dotenv/config"; const lokiApiKey = process.env.LOKI_API_KEY!; const user = process.env.LOKI_USER!; const lokiUrl = process.env.LOKI_URL!; -const isGithubActions = process.env.GITHUB_ACTIONS === 'true'; +const isGithubActions = process.env.GITHUB_ACTIONS === "true"; const maybe = !isGithubActions ? describe : describe.skip; jest.setTimeout(30000); -maybe('LokiClient', () => { +maybe("LokiClient", () => { let lokiClient: LokiClient; beforeAll(() => { lokiClient = new LokiClient(lokiUrl, lokiApiKey, user, "staging"); }); - it ('can count logs by level for a service', async () => { + it("can count logs by level for a service", async () => { const service = "checkly-api"; - const rangeMinutes = 60*12; + const rangeMinutes = 60 * 12; const data = await lokiClient.getLogCountByLevel(service, rangeMinutes); expect(data).toBeDefined(); console.log(JSON.stringify(data.data.result)); - expect(data).toHaveProperty('data'); + expect(data).toHaveProperty("data"); //console.log(JSON.stringify(data.data.result[0].values)); - }) + }); - it('should get available services', async () => { + it("should get available services", async () => { const services = await lokiClient.getAllValuesForLabel("app"); expect(services).toBeDefined(); expect(services.length).toBeGreaterThan(0); //console.log(services); }); - - it('should run a query and return results', async () => { + + it("should run a query and return results", async () => { const services = lokiClient.getAllValuesForLabel("app"); const data = await lokiClient.getErrorsForService(services[1], 10); expect(data).toBeDefined(); - expect(data).toHaveProperty('data'); + expect(data).toHaveProperty("data"); //console.log(JSON.stringify(data.data.result[0].values)); }); }); - \ No newline at end of file diff --git a/src/grafana/lokiclient.ts b/src/grafana/lokiclient.ts index a627077..e574e95 100644 --- a/src/grafana/lokiclient.ts +++ b/src/grafana/lokiclient.ts @@ -1,94 +1,104 @@ export class LokiClient { - private readonly lokiUrl: string; - private readonly lokiApiKey: string; - private readonly environment: string; - user: string; - - constructor(lokiUrl: string, lokiApiKey: string, user: string, environment: string) { - this.lokiUrl = lokiUrl; - this.lokiApiKey = lokiApiKey; - this.environment = environment; - this.user = user; - } - - queryError(service: string): string { - return `{app="${service}", env="${this.environment}"} |= "error"`; - } + private readonly lokiUrl: string; + private readonly lokiApiKey: string; + private readonly environment: string; + user: string; - async getLogCountByLevel(app: string, rangeMinutes: number): Promise { - const query = `sum by (detected_level) (count_over_time({app="${app}", env="${this.environment}"}[5m]))`; - const end = new Date(); - const start = new Date(end.getTime() - rangeMinutes * 60 * 1000); - const data = await this.queryLoki(query, start.toISOString(), end.toISOString()); - return data; - } + constructor( + lokiUrl: string, + lokiApiKey: string, + user: string, + environment: string, + ) { + this.lokiUrl = lokiUrl; + this.lokiApiKey = lokiApiKey; + this.environment = environment; + this.user = user; + } - async getAllEnvironments(): Promise { - return this.getAllValuesForLabel('env'); - } + queryError(service: string): string { + return `{app="${service}", env="${this.environment}"} |= "error"`; + } - - async getAllApps(): Promise { - return this.getAllValuesForLabel('app'); - } + async getLogCountByLevel(app: string, rangeMinutes: number): Promise { + const query = `sum by (detected_level) (count_over_time({app="${app}", env="${this.environment}"}[5m]))`; + const end = new Date(); + const start = new Date(end.getTime() - rangeMinutes * 60 * 1000); + const data = await this.queryLoki( + query, + start.toISOString(), + end.toISOString(), + ); + return data; + } + + async getAllEnvironments(): Promise { + return this.getAllValuesForLabel("env"); + } + + async getAllApps(): Promise { + return this.getAllValuesForLabel("app"); + } - /** - * This function gets all available values for a label in Loki. - * @returns - */ - async getAllValuesForLabel(label: string): Promise { - const url = new URL(`${this.lokiUrl}/loki/api/v1/label/${label}/values`); - const authHeader = 'Basic ' + btoa(`${this.user}:${this.lokiApiKey}`); - - const response = await fetch(url.toString(), { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - 'Authorization': authHeader - } - }); - - if (!response.ok) { - throw new Error(`Error fetching available services: ${response.statusText}`); - } - - const data = await response.json(); - return data.data; // Assuming the response structure is { "status": "success", "data": ["app1", "app2", ...] } - } + /** + * This function gets all available values for a label in Loki. + * @returns + */ + async getAllValuesForLabel(label: string): Promise { + const url = new URL(`${this.lokiUrl}/loki/api/v1/label/${label}/values`); + const authHeader = "Basic " + btoa(`${this.user}:${this.lokiApiKey}`); - async getErrorsForService(service: string, rangeMinutes: number) { - // Get the current time and subtract "rangeMinutes" minutes - const end = new Date(); - const start = new Date(end.getTime() - rangeMinutes * 60 * 1000); - - // Convert to ISO string format - const startISOString = start.toISOString(); - const endISOString = end.toISOString(); - const query = this.queryError(service); - return this.queryLoki(query, startISOString, endISOString); + const response = await fetch(url.toString(), { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: authHeader, + }, + }); + + if (!response.ok) { + throw new Error( + `Error fetching available services: ${response.statusText}`, + ); } - async queryLoki(query: string, start: string, end: string): Promise { - const url = new URL(`${this.lokiUrl}/loki/api/v1/query_range`); - url.searchParams.append('query', query); - url.searchParams.append('start', start); - url.searchParams.append('end', end); - const authHeader = 'Basic ' + btoa(`${this.user}:${this.lokiApiKey}`); - - const response = await fetch(url.toString(), { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - Authorization: authHeader, - }, - }); - - if (!response.ok) { - const errorText = await response.text(); - throw new Error( - `Error querying Loki: ${response.status} ${response.statusText} - ${errorText}`, - ); - } - //https://grafana.com/docs/loki/latest/reference/loki-http-api/#query-logs-within-a-range-of-time - return response.json(); + + const data = await response.json(); + return data.data; // Assuming the response structure is { "status": "success", "data": ["app1", "app2", ...] } + } + + async getErrorsForService(service: string, rangeMinutes: number) { + // Get the current time and subtract "rangeMinutes" minutes + const end = new Date(); + const start = new Date(end.getTime() - rangeMinutes * 60 * 1000); + + // Convert to ISO string format + const startISOString = start.toISOString(); + const endISOString = end.toISOString(); + const query = this.queryError(service); + return this.queryLoki(query, startISOString, endISOString); + } + async queryLoki(query: string, start: string, end: string): Promise { + const url = new URL(`${this.lokiUrl}/loki/api/v1/query_range`); + url.searchParams.append("query", query); + url.searchParams.append("start", start); + url.searchParams.append("end", end); + const authHeader = "Basic " + btoa(`${this.user}:${this.lokiApiKey}`); + + const response = await fetch(url.toString(), { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: authHeader, + }, + }); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error( + `Error querying Loki: ${response.status} ${response.statusText} - ${errorText}`, + ); } - } \ No newline at end of file + //https://grafana.com/docs/loki/latest/reference/loki-http-api/#query-logs-within-a-range-of-time + return response.json(); + } +} diff --git a/src/instrumentation.ts b/src/instrumentation.ts index 8a4bb35..bb5f828 100644 --- a/src/instrumentation.ts +++ b/src/instrumentation.ts @@ -1,5 +1,5 @@ -import 'reflect-metadata'; +import "reflect-metadata"; export function register() { - console.log('Registering instrumentation'); + console.log("Registering instrumentation"); } diff --git a/src/knowledge-base/knowledgeBase.ts b/src/knowledge-base/knowledgeBase.ts index faceaef..8997453 100644 --- a/src/knowledge-base/knowledgeBase.ts +++ b/src/knowledge-base/knowledgeBase.ts @@ -2,11 +2,11 @@ import { fetchDocumentsFromKnowledgeBase } from "../notion/notion"; export type KnowledgeDocument = { content: string; - title: string - slug: string - summary: string -} + title: string; + slug: string; + summary: string; +}; export const getAllDocuments = async (): Promise => { - return fetchDocumentsFromKnowledgeBase() -} + return fetchDocumentsFromKnowledgeBase(); +}; diff --git a/src/langfuse/index.ts b/src/langfuse/index.ts index fd1ff97..bece002 100644 --- a/src/langfuse/index.ts +++ b/src/langfuse/index.ts @@ -26,7 +26,7 @@ export const startLangfuseTelemetrySDK = () => { telemetrySDK.start(); } else { console.warn( - "LANGFUSE_SECRET_KEY, LANGFUSE_BASEURL and LANGFUSE_PUBLIC_KEY are not set. Langfuse observability will not be available." + "LANGFUSE_SECRET_KEY, LANGFUSE_BASEURL and LANGFUSE_PUBLIC_KEY are not set. Langfuse observability will not be available.", ); } }; diff --git a/src/langfuse/utils.ts b/src/langfuse/utils.ts index f5d105c..e624775 100644 --- a/src/langfuse/utils.ts +++ b/src/langfuse/utils.ts @@ -26,7 +26,7 @@ export function formatMessageContent(message: Message): MessageContent { */ export function createBaseInput( run: Run, - history: Message[] + history: Message[], ): MessageContent[] { return [ { role: "system", content: run.instructions }, @@ -39,7 +39,7 @@ export function createBaseInput( */ export function processStepOutput( step: RunStep, - messages: Message[] + messages: Message[], ): MessageContent[] { const output: MessageContent[] = []; @@ -71,7 +71,7 @@ export function processStepOutput( */ export async function traceRunSteps( runTrace: LangfuseTraceClient, - run: Run + run: Run, ): Promise { const messages = await getRunMessages(run.thread_id, run.id); const historyCall = getMessageHistory(run.thread_id, messages[0].id); diff --git a/src/lib/init-config.ts b/src/lib/init-config.ts index a4ef48b..2c4c346 100644 --- a/src/lib/init-config.ts +++ b/src/lib/init-config.ts @@ -4,6 +4,6 @@ import path from "path"; // Load the .env file // This allows us to run any file as an entry point from any working directory export const initConfig = () => { - const envPath = path.resolve(__dirname, "../..", '.env'); + const envPath = path.resolve(__dirname, "../..", ".env"); dotenv.config({ path: envPath }); -} +}; diff --git a/src/routes/checklywebhook.ts b/src/routes/checklywebhook.ts index 0beedb0..2e80900 100644 --- a/src/routes/checklywebhook.ts +++ b/src/routes/checklywebhook.ts @@ -51,7 +51,7 @@ router.post("/", async (req: Request, res: Response) => { if (exisingAlert && !!(process.env.PREVENT_DUPLICATE_ALERTS === "true")) { console.log( "Alert already processed", - !!(process.env.PREVENT_DUPLICATE_ALERTS === "true") + !!(process.env.PREVENT_DUPLICATE_ALERTS === "true"), ); res.status(200).json({ message: "Alert already processed", @@ -94,7 +94,7 @@ router.post("/", async (req: Request, res: Response) => { }); const checkResults = context.find( - (c) => c.key === ContextKey.ChecklyResults + (c) => c.key === ContextKey.ChecklyResults, ); const thread = await getOpenaiClient().beta.threads.create({ @@ -149,7 +149,7 @@ router.post("/", async (req: Request, res: Response) => { { type: "mrkdwn", text: `:date: *${new Date( - alertDto.STARTED_AT + alertDto.STARTED_AT, ).toLocaleString()}*`, }, { diff --git a/src/routes/githubwebhook.spec.ts b/src/routes/githubwebhook.spec.ts index 50124bd..cc828f2 100644 --- a/src/routes/githubwebhook.spec.ts +++ b/src/routes/githubwebhook.spec.ts @@ -24,7 +24,7 @@ describe("Load github releases into db", () => { let summary = await githubAgent.summarizeReleases( `what has changed in the ${repo} within the last ${timeframe}`, - org + org, ); for (const release of summary.releases) { @@ -44,7 +44,7 @@ describe("Load github releases into db", () => { diffUrl: release.diffLink, authors, summary: release.summary, - } + }, }); } }, 600000000); diff --git a/src/routes/githubwebhook.ts b/src/routes/githubwebhook.ts index ae721fb..35bd64a 100644 --- a/src/routes/githubwebhook.ts +++ b/src/routes/githubwebhook.ts @@ -1,16 +1,24 @@ -import { Prisma } from '@prisma/client'; +import { Prisma } from "@prisma/client"; import crypto from "crypto"; import timers from "node:timers/promises"; import express, { Request, Response } from "express"; -import { DeploymentStatusEvent, ReleaseEvent, WebhookEvent, WebhookEventName } from "@octokit/webhooks-types"; +import { + DeploymentStatusEvent, + ReleaseEvent, + WebhookEvent, + WebhookEventName, +} from "@octokit/webhooks-types"; import { App, LogLevel } from "@slack/bolt"; import { getOpenaiSDKClient } from "../ai/openai"; import GitHubAPI, { CompareCommitsResponse } from "../github/github"; import { GithubAgent } from "../github/agent"; -import { createDeploymentBlock, createReleaseBlock } from "../github/slackBlock"; +import { + createDeploymentBlock, + createReleaseBlock, +} from "../github/slackBlock"; import moment from "moment"; -import { prisma } from '../prisma'; +import { prisma } from "../prisma"; const GH_WEBHOOK_SECRET = process.env.GH_WEBHOOK_SECRET || "your_secret"; @@ -30,7 +38,9 @@ const CHECKLY_GITHUB_TOKEN = process.env.CHECKLY_GITHUB_TOKEN!; const ignoredRepos = new Set(process.env.IGNORED_REPOS?.split(",") || []); // Environments to ignore (passed as a comma-separated list, for example "staging,Preview") -const ignoredEnvironments = new Set(process.env.IGNORED_ENVIRONMENTS?.split(",") || []); +const ignoredEnvironments = new Set( + process.env.IGNORED_ENVIRONMENTS?.split(",") || [], +); const github = new GitHubAPI(CHECKLY_GITHUB_TOKEN); @@ -59,245 +69,255 @@ router.get("/", (req: Request, res: Response) => { const withRetry = async (fn: () => Promise, attempts = 2): Promise => { try { - return await fn() + return await fn(); } catch (err) { if (attempts <= 0) { - throw err + throw err; } - await timers.setTimeout(1000) - return await withRetry(fn, attempts - 1) + await timers.setTimeout(1000); + return await withRetry(fn, attempts - 1); } -} +}; const pullAuthors = (diff: CompareCommitsResponse): string[] => { - return [...new Set(diff.commits - .map((c) => c.author) - .filter((author) => author !== null) - .map((author) => author.login))]; -} + return [ + ...new Set( + diff.commits + .map((c) => c.author) + .filter((author) => author !== null) + .map((author) => author.login), + ), + ]; +}; -router.post( - "/", - async (req: Request, res: Response) => { - if (!verifySignature(req, res, Buffer.from(JSON.stringify(req.body)))) { - res.status(401).send("Signature verification failed"); - return; - } +router.post("/", async (req: Request, res: Response) => { + if (!verifySignature(req, res, Buffer.from(JSON.stringify(req.body)))) { + res.status(401).send("Signature verification failed"); + return; + } - const event = req.headers["x-github-event"] as WebhookEventName; - const payload = req.body as WebhookEvent; + const event = req.headers["x-github-event"] as WebhookEventName; + const payload = req.body as WebhookEvent; - switch (event) { - case "ping": - console.log("Ping event received"); + switch (event) { + case "ping": + console.log("Ping event received"); + res.status(200).send("Webhook received"); + break; + case "deployment_status": + console.log("Deployment event received"); + const deploymentEvent = payload as DeploymentStatusEvent; + + if (deploymentEvent.deployment_status.state !== "success") { res.status(200).send("Webhook received"); - break; - case "deployment_status": - console.log("Deployment event received"); - const deploymentEvent = payload as DeploymentStatusEvent; + return; + } + + try { + // Extract deployment details + const { repository, deployment } = deploymentEvent; + const repositoryName = repository.name; + if (ignoredRepos.has(repositoryName)) { + console.log( + `Ignoring deployment event for repository ${repositoryName}`, + ); + res.status(200).send("Ignoring deployment event"); + return; + } - if (deploymentEvent.deployment_status.state !== "success") { - res.status(200).send("Webhook received"); + const environment = deployment.environment || "unknown"; + if (ignoredEnvironments.has(environment)) { + console.log( + `Ignoring deployment event for environment ${environment}`, + ); + res.status(200).send("Ignoring deployment event"); return; } - try { - // Extract deployment details - const { - repository, - deployment, - } = deploymentEvent; - const repositoryName = repository.name; - if (ignoredRepos.has(repositoryName)) { - console.log(`Ignoring deployment event for repository ${repositoryName}`); - res.status(200).send("Ignoring deployment event"); - return; - } - - const environment = deployment.environment || "unknown"; - if (ignoredEnvironments.has(environment)) { - console.log(`Ignoring deployment event for environment ${environment}`); - res.status(200).send("Ignoring deployment event"); - return; - } - - const organizationName = deploymentEvent.organization?.login || repository.owner.login; - - // Check if a deployment with the same sha, repo, org, and environment already exists - const existingDeployment = await prisma.deployment.findFirst({ - where: { - sha: deployment.sha, - repo: repositoryName, - org: repository.owner.login, - environment: environment, - }, - }); - - if (existingDeployment) { - console.log( - `Deployment with sha ${deployment.sha} already exists for ${repositoryName} in ${deployment.environment}. Skipping.` - ); - res.status(200).send("Duplicate deployment event, skipping."); - return; - } - - const previousDeployment = await withRetry(() => github.getPreviousDeployment( - organizationName, - repositoryName, - deployment.environment, - deployment.id, - deployment.sha - )); - if (previousDeployment === null) { - console.log(`No previous deployment found for ${repositoryName} in ${deployment.environment}.`); - return - } - - const diffUrl = `https://github.com/${repository.owner.login}/${repositoryName}/compare/${previousDeployment.sha || ''}...${deployment.sha}`; - const deploymentData = { - org: repository.owner.login, + const organizationName = + deploymentEvent.organization?.login || repository.owner.login; + + // Check if a deployment with the same sha, repo, org, and environment already exists + const existingDeployment = await prisma.deployment.findFirst({ + where: { + sha: deployment.sha, repo: repositoryName, - repoUrl: repository.html_url, + org: repository.owner.login, environment: environment, - sha: deployment.sha, - deploymentUrl: deploymentEvent.deployment.url, - diffUrl, - }; - console.log("Saving deployment to the database:", deploymentData); + }, + }); + + if (existingDeployment) { + console.log( + `Deployment with sha ${deployment.sha} already exists for ${repositoryName} in ${deployment.environment}. Skipping.`, + ); + res.status(200).send("Duplicate deployment event, skipping."); + return; + } - const diffSummary = await githubAgent.summarizeDeployment( + const previousDeployment = await withRetry(() => + github.getPreviousDeployment( organizationName, repositoryName, + deployment.environment, + deployment.id, deployment.sha, - previousDeployment.sha + ), + ); + if (previousDeployment === null) { + console.log( + `No previous deployment found for ${repositoryName} in ${deployment.environment}.`, ); - - // Save deployment to the database - await prisma.deployment.create({ - data: { - ...deploymentData, - rawEvent: deploymentEvent as unknown as Prisma.InputJsonValue, - summary: diffSummary.summary, - createdAt: new Date(deployment.created_at), - }, - }); - - console.log("Deployment saved successfully."); - - const date = moment(deployment.created_at).fromNow(); - const authors = pullAuthors(diffSummary.diff); - - const deploymentBlocks = createDeploymentBlock({ - diffUrl, - authors, - date, - environment, - repo: repositoryName, - repoUrl: deploymentData.repoUrl, - deploymentUrl: deployment.url, - summary: diffSummary.summary, - }).blocks - console.log('Posting a message to Slack'); - await app.client.chat.postMessage({ - channel: process.env.SLACK_RELEASE_CHANNEL_ID as string, - text: `New Deployment in ${deployment.environment} Environment: in ${organizationName}/${repositoryName}`, - metadata: { - event_type: "deployment-summary", - event_payload: {}, - }, - blocks: deploymentBlocks - }); - - res.status(200).send("Deployment event processed successfully"); - } catch (error) { - console.error("Error processing deployment event:", error); - res.status(500).send("Error processing deployment event"); - } - break; - case "release": - let releaseEvent = payload as ReleaseEvent; - if (releaseEvent.action !== "published") { - res.status(200).send("Webhook received"); - return; - } - const repoName = releaseEvent.repository.name; - if (ignoredRepos.has(repoName)) { - console.log(`Ignoring release event for repository ${repoName}`); - res.status(200).send("Ignoring release event"); return; } - console.log( - "Release event received:", - releaseEvent.repository.owner.login + const diffUrl = `https://github.com/${repository.owner.login}/${repositoryName}/compare/${previousDeployment.sha || ""}...${deployment.sha}`; + const deploymentData = { + org: repository.owner.login, + repo: repositoryName, + repoUrl: repository.html_url, + environment: environment, + sha: deployment.sha, + deploymentUrl: deploymentEvent.deployment.url, + diffUrl, + }; + console.log("Saving deployment to the database:", deploymentData); + + const diffSummary = await githubAgent.summarizeDeployment( + organizationName, + repositoryName, + deployment.sha, + previousDeployment.sha, ); - const previousRelease = await withRetry(() => github.getPreviousReleaseTag( - releaseEvent.repository.owner.login, - releaseEvent.repository.name, - releaseEvent.release.tag_name - )); - - const release = await githubAgent.summarizeRelease( - releaseEvent.repository.owner.login, - repoName, - releaseEvent.release.tag_name, - previousRelease - ); - const date = moment(releaseEvent.release.published_at).fromNow(); - const authors = pullAuthors(release.diff) - let releaseName = releaseEvent.release.name || releaseEvent.release.tag_name; - let releaseBlocks = createReleaseBlock({ - release: releaseName, - releaseUrl: releaseEvent.release.html_url, - diffUrl: release.diff.html_url, - date, - repo: repoName, - repoUrl: releaseEvent.repository.html_url, - authors, - summary: release.summary, - }).blocks; - - console.log('Creating a new release in the database'); - const createdRelease = await prisma.release.create({ - data: { - name: releaseName, - releaseUrl: releaseEvent.release.html_url, - publishedAt: releaseEvent.release.published_at, - org: releaseEvent.repository.owner.login, - repo: repoName, - repoUrl: releaseEvent.repository.html_url, - tag: releaseEvent.release.tag_name, - diffUrl: release.diff.html_url, - authors, - summary: release.summary, - } - }); - await prisma.rawRelease.create({ + // Save deployment to the database + await prisma.deployment.create({ data: { - body: releaseEvent as unknown as Prisma.InputJsonValue, - releaseId: createdRelease.id, + ...deploymentData, + rawEvent: deploymentEvent as unknown as Prisma.InputJsonValue, + summary: diffSummary.summary, + createdAt: new Date(deployment.created_at), }, }); - console.log('Posting a message to Slack'); + console.log("Deployment saved successfully."); + + const date = moment(deployment.created_at).fromNow(); + const authors = pullAuthors(diffSummary.diff); + + const deploymentBlocks = createDeploymentBlock({ + diffUrl, + authors, + date, + environment, + repo: repositoryName, + repoUrl: deploymentData.repoUrl, + deploymentUrl: deployment.url, + summary: diffSummary.summary, + }).blocks; + console.log("Posting a message to Slack"); await app.client.chat.postMessage({ channel: process.env.SLACK_RELEASE_CHANNEL_ID as string, - text: `New release: ${releaseEvent.release.name} in ${releaseEvent.repository.owner.login}/${repoName}`, + text: `New Deployment in ${deployment.environment} Environment: in ${organizationName}/${repositoryName}`, metadata: { - event_type: "release-summary", + event_type: "deployment-summary", event_payload: {}, }, - blocks: releaseBlocks, + blocks: deploymentBlocks, }); + res.status(200).send("Deployment event processed successfully"); + } catch (error) { + console.error("Error processing deployment event:", error); + res.status(500).send("Error processing deployment event"); + } + break; + case "release": + let releaseEvent = payload as ReleaseEvent; + if (releaseEvent.action !== "published") { res.status(200).send("Webhook received"); - break; - default: - console.log("Unhandled event received:", event); - res.status(200).send("Webhook received"); - } + return; + } + const repoName = releaseEvent.repository.name; + if (ignoredRepos.has(repoName)) { + console.log(`Ignoring release event for repository ${repoName}`); + res.status(200).send("Ignoring release event"); + return; + } + + console.log( + "Release event received:", + releaseEvent.repository.owner.login, + ); + + const previousRelease = await withRetry(() => + github.getPreviousReleaseTag( + releaseEvent.repository.owner.login, + releaseEvent.repository.name, + releaseEvent.release.tag_name, + ), + ); + + const release = await githubAgent.summarizeRelease( + releaseEvent.repository.owner.login, + repoName, + releaseEvent.release.tag_name, + previousRelease, + ); + const date = moment(releaseEvent.release.published_at).fromNow(); + const authors = pullAuthors(release.diff); + let releaseName = + releaseEvent.release.name || releaseEvent.release.tag_name; + let releaseBlocks = createReleaseBlock({ + release: releaseName, + releaseUrl: releaseEvent.release.html_url, + diffUrl: release.diff.html_url, + date, + repo: repoName, + repoUrl: releaseEvent.repository.html_url, + authors, + summary: release.summary, + }).blocks; + + console.log("Creating a new release in the database"); + const createdRelease = await prisma.release.create({ + data: { + name: releaseName, + releaseUrl: releaseEvent.release.html_url, + publishedAt: releaseEvent.release.published_at, + org: releaseEvent.repository.owner.login, + repo: repoName, + repoUrl: releaseEvent.repository.html_url, + tag: releaseEvent.release.tag_name, + diffUrl: release.diff.html_url, + authors, + summary: release.summary, + }, + }); + await prisma.rawRelease.create({ + data: { + body: releaseEvent as unknown as Prisma.InputJsonValue, + releaseId: createdRelease.id, + }, + }); + + console.log("Posting a message to Slack"); + await app.client.chat.postMessage({ + channel: process.env.SLACK_RELEASE_CHANNEL_ID as string, + text: `New release: ${releaseEvent.release.name} in ${releaseEvent.repository.owner.login}/${repoName}`, + metadata: { + event_type: "release-summary", + event_payload: {}, + }, + blocks: releaseBlocks, + }); + + res.status(200).send("Webhook received"); + break; + default: + console.log("Unhandled event received:", event); + res.status(200).send("Webhook received"); } -); +}); export default router; diff --git a/src/slackbot/app.ts b/src/slackbot/app.ts index 96297ec..b32847b 100644 --- a/src/slackbot/app.ts +++ b/src/slackbot/app.ts @@ -51,7 +51,7 @@ app.command("/srebot-releases", async ({ command, ack, respond }) => { let releases = summaries.releases.sort( (a, b) => - new Date(b.release_date).getTime() - new Date(a.release_date).getTime() + new Date(b.release_date).getTime() - new Date(a.release_date).getTime(), ); let response = [releaseHeader].concat( releases @@ -77,7 +77,7 @@ app.command("/srebot-releases", async ({ command, ack, respond }) => { } return prev.concat([releaseDivider]).concat(curr); - }) + }), ); await respond({ @@ -115,13 +115,13 @@ app.event("app_mention", async ({ event, context }) => { console.log( "Starting to analyse the alert message:", - messageTextWithSender + messageTextWithSender, ); const { responseText } = await getAlertAnalysis( messageTextWithSender, event.channel, - event.thread_ts + event.thread_ts, ); await app.client.chat.postMessage({ @@ -196,9 +196,9 @@ app.event("app_mention", async ({ event, context }) => { msg.content .filter((c) => c.type === "text") .map((c) => (c as any).text.value) - .join("") - ) - ) + .join(""), + ), + ), ); } catch (error) { console.error("Error processing app mention:", error); @@ -214,7 +214,7 @@ app.event("app_mention", async ({ event, context }) => { async function getAlertAnalysis( messageText: string, targetChannel: string, - threadTs: string + threadTs: string, ) { console.log("Starting to analyse the alert message"); const response = await analyseAlert(messageText, targetChannel, threadTs); @@ -240,7 +240,7 @@ async function getAlertAnalysis( responseText += `\n\nAffected components:\n${response.affectedComponents ?.map( (affected) => - `- \`${affected.component}\` in \`${affected.environment}\` environment` + `- \`${affected.component}\` in \`${affected.environment}\` environment`, ) .join("\n")}`; } @@ -313,7 +313,7 @@ if (process.env.OPS_CHANNEL_ID) { if (shouldIgnoreMessageBasedOnSender) { console.log( "Ignoring message from non-bot user. If you want to allow messages from non-bot users, set ALLOW_NON_BOT_MESSAGES=true in your environment variables. Event subtype:", - event.subtype + event.subtype, ); return; } @@ -321,7 +321,7 @@ if (process.env.OPS_CHANNEL_ID) { const { responseText, response } = await getAlertAnalysis( messageTextWithSender, targetChannel, - event.ts + event.ts, ); const alertRecord = await prisma.alert.create({ @@ -368,6 +368,6 @@ if (process.env.OPS_CHANNEL_ID) { } catch (error) { console.error("Error responding to message:", error); } - } + }, ); } diff --git a/src/slackbot/config.ts b/src/slackbot/config.ts index 525369a..ed8aa15 100644 --- a/src/slackbot/config.ts +++ b/src/slackbot/config.ts @@ -25,12 +25,12 @@ export const validateConfig = (config: SlackConfig): void => { ]; const missingVars = requiredEnvVars.filter( - (varName) => !process.env[varName] + (varName) => !process.env[varName], ); if (missingVars.length > 0) { throw new Error( - `Missing required environment variables: ${missingVars.join(", ")}` + `Missing required environment variables: ${missingVars.join(", ")}`, ); } }; diff --git a/src/slackbot/index.ts b/src/slackbot/index.ts index 9a96346..c22d678 100644 --- a/src/slackbot/index.ts +++ b/src/slackbot/index.ts @@ -1,5 +1,5 @@ -import 'dotenv/config'; -import { app } from './app'; +import "dotenv/config"; +import { app } from "./app"; app.error(async (error) => { // Check the details of the error to handle cases where you should retry sending a message or stop the app @@ -8,5 +8,5 @@ app.error(async (error) => { (async () => { await app.start(); - console.log('⚡️ Bolt app is running!'); + console.log("⚡️ Bolt app is running!"); })(); diff --git a/src/sre-assistant/tools/GitHubTool.ts b/src/sre-assistant/tools/GitHubTool.ts index 6713020..3e63f0e 100644 --- a/src/sre-assistant/tools/GitHubTool.ts +++ b/src/sre-assistant/tools/GitHubTool.ts @@ -14,14 +14,14 @@ const parameters = createToolParameters( repo: z .string() .describe( - "The full_name of the repository to get information about (e.g. 'checkly/checkly-cli')" + "The full_name of the repository to get information about (e.g. 'checkly/checkly-cli')", ) .optional(), - }) + }), ); const outputSchema = createToolOutput( - z.string().describe("The response from the GitHub API") + z.string().describe("The response from the GitHub API"), ); export class GitHubTool extends Tool< @@ -58,11 +58,11 @@ export class GitHubTool extends Tool< patch: f.patch, url: f.blob_url, })), - })) + })), ).slice(0, MAX_RESPONSE_LENGTH); } else if (input.action === "listRepositories") { const repos = await githubApi.queryRepositories( - process.env.GITHUB_ORG as string + process.env.GITHUB_ORG as string, ); return stringify( repos.map((r) => ({ @@ -70,7 +70,7 @@ export class GitHubTool extends Tool< description: r.description, last_pushed: r.pushed_at, url: r.html_url, - })) + })), ); } diff --git a/src/sre-assistant/tools/GithubAgentInteractionTool.ts b/src/sre-assistant/tools/GithubAgentInteractionTool.ts index 0ed716e..01e8d40 100644 --- a/src/sre-assistant/tools/GithubAgentInteractionTool.ts +++ b/src/sre-assistant/tools/GithubAgentInteractionTool.ts @@ -16,9 +16,9 @@ const parameters = createToolParameters( request: z .string() .describe( - "A request for the GitHub NLP agent. For example: what changed in the ui since yesterday" + "A request for the GitHub NLP agent. For example: what changed in the ui since yesterday", ), - }) + }), ); const outputSchema = createToolOutput(z.string()); diff --git a/src/sre-assistant/tools/KnowledgeTool.ts b/src/sre-assistant/tools/KnowledgeTool.ts index 771d791..e06fe58 100644 --- a/src/sre-assistant/tools/KnowledgeTool.ts +++ b/src/sre-assistant/tools/KnowledgeTool.ts @@ -6,22 +6,19 @@ import { getAllDocuments } from "../../knowledge-base/knowledgeBase"; const parameters = createToolParameters( z.object({ action: z - .enum([ - "listDocuments", - "getOneDocument", - ]) + .enum(["listDocuments", "getOneDocument"]) .describe("The action to perform on the Knowledge Base"), documentSlug: z .string() .describe( - "The slug of the Document to get information about. Omit this field for the 'listDocuments' action. Required for the 'getOneDocument'" + "The slug of the Document to get information about. Omit this field for the 'listDocuments' action. Required for the 'getOneDocument'", ) .optional(), - }) + }), ); const outputSchema = createToolOutput( - z.string().describe("The response from the Knowledge Base") + z.string().describe("The response from the Knowledge Base"), ); export class KnowledgeTool extends Tool< @@ -46,17 +43,21 @@ export class KnowledgeTool extends Tool< if (input.action === "listDocuments") { const documents = await getAllDocuments(); - return JSON.stringify(documents.map((doc) => ({ - slug: doc.slug, - title: doc.title, - summary: doc.summary, - }))); + return JSON.stringify( + documents.map((doc) => ({ + slug: doc.slug, + title: doc.title, + summary: doc.summary, + })), + ); } else if (input.action === "getOneDocument") { if (!input.documentSlug) { return "Document slug is required"; } - const document = await getAllDocuments().then(docs => docs.find(doc => doc.slug === input.documentSlug)); + const document = await getAllDocuments().then((docs) => + docs.find((doc) => doc.slug === input.documentSlug), + ); if (!document) { return `Document for slug: ${input.documentSlug} not found`; diff --git a/tsconfig.json b/tsconfig.json index e4977a8..5d96f26 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,11 +18,7 @@ "strictBindCallApply": false, "forceConsistentCasingInFileNames": false, "noFallthroughCasesInSwitch": false, - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "strict": false, //"noEmit": true, @@ -37,12 +33,6 @@ } ] }, - "include": [ - "src/**/*.ts", - "tests/**/*.ts", - ".next/types/**/*.ts" - ], - "exclude": [ - "node_modules" - ] + "include": ["src/**/*.ts", "tests/**/*.ts", ".next/types/**/*.ts"], + "exclude": ["node_modules"] }