From 3ef0ff5fe99d0e8a58f2b19611abaef984326419 Mon Sep 17 00:00:00 2001 From: Wesley Harding <2081303+wesley-harding@users.noreply.github.com> Date: Sun, 26 Jan 2025 10:56:02 -0500 Subject: [PATCH 1/4] Claude implemented a nice first pass, but I'm running into rate limit issues and using a lot of context. I want to try this again with Deepseek directly. --- package-lock.json | 1515 +++++++++++++---- package.json | 4 + .../notifications/NotificationManager.ts | 145 ++ .../providers/telegramProvider.ts | 146 ++ src/services/notifications/types.ts | 61 + src/shared/WebviewMessage.ts | 186 +- src/utils/logger.ts | 34 + .../settings/NotificationSettings.tsx | 152 ++ 8 files changed, 1780 insertions(+), 463 deletions(-) create mode 100644 src/services/notifications/NotificationManager.ts create mode 100644 src/services/notifications/providers/telegramProvider.ts create mode 100644 src/services/notifications/types.ts create mode 100644 src/utils/logger.ts create mode 100644 webview-ui/src/components/settings/NotificationSettings.tsx diff --git a/package-lock.json b/package-lock.json index 92fec692e5e..f5ccd730ff8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,9 +16,11 @@ "@mistralai/mistralai": "^1.3.6", "@modelcontextprotocol/sdk": "^1.0.1", "@types/clone-deep": "^4.0.4", + "@types/node-telegram-bot-api": "^0.64.7", "@types/pdf-parse": "^1.1.4", "@types/tmp": "^0.2.6", "@types/turndown": "^5.0.5", + "@types/uuid": "^10.0.0", "@types/vscode": "^1.95.0", "@vscode/codicons": "^0.0.36", "axios": "^1.7.4", @@ -35,6 +37,7 @@ "isbinaryfile": "^5.0.2", "mammoth": "^1.8.0", "monaco-vscode-textmate-theme-converter": "^0.1.7", + "node-telegram-bot-api": "^0.66.0", "openai": "^4.78.1", "os-name": "^6.0.0", "p-wait-for": "^5.0.2", @@ -49,6 +52,7 @@ "tmp": "^0.2.3", "tree-sitter-wasms": "^0.1.11", "turndown": "^7.2.0", + "uuid": "^11.0.5", "web-tree-sitter": "^0.22.6", "zod": "^3.23.8" }, @@ -538,11 +542,28 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==" + }, "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, + "node_modules/@aws-sdk/client-bedrock-runtime/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@aws-sdk/client-cognito-identity": { "version": "3.699.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.699.0.tgz", @@ -3030,6 +3051,86 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/@cypress/request": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.7.tgz", + "integrity": "sha512-LzxlLEMbBOPYB85uXrDqvD4MgcenjRBLIns3zyhx7vTPj/0u2eQhzXvPiGcaJrV38Q9dbkExWp6cOHPJ+EtFYg==", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~4.0.0", + "http-signature": "~1.4.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "performance-now": "^2.1.0", + "qs": "6.13.1", + "safe-buffer": "^5.1.2", + "tough-cookie": "^5.0.0", + "tunnel-agent": "^0.6.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@cypress/request-promise": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@cypress/request-promise/-/request-promise-5.0.0.tgz", + "integrity": "sha512-eKdYVpa9cBEw2kTBlHeu1PP16Blwtum6QHg/u9s/MoHkZfuo1pRGka1VlUHXF5kdew82BvOJVVGk0x8X0nbp+w==", + "dependencies": { + "bluebird": "^3.5.0", + "request-promise-core": "1.1.3", + "stealthy-require": "^1.1.1", + "tough-cookie": "^4.1.3" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "@cypress/request": "^3.0.0" + } + }, + "node_modules/@cypress/request-promise/node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "node_modules/@cypress/request-promise/node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@cypress/request-promise/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/@cypress/request/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@esbuild/darwin-arm64": { "version": "0.24.0", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", @@ -4621,6 +4722,18 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, + "node_modules/@smithy/middleware-retry/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@smithy/middleware-serde": { "version": "3.0.10", "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.10.tgz", @@ -5744,6 +5857,11 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/caseless": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", + "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==" + }, "node_modules/@types/clone-deep": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/clone-deep/-/clone-deep-4.0.4.tgz", @@ -5828,11 +5946,45 @@ "form-data": "^4.0.0" } }, + "node_modules/@types/node-telegram-bot-api": { + "version": "0.64.7", + "resolved": "https://registry.npmjs.org/@types/node-telegram-bot-api/-/node-telegram-bot-api-0.64.7.tgz", + "integrity": "sha512-nuvFFXnvU2sItucyEJ03I+m34z5st386isfEuF6BJTL7p3RjG7naMhvvXjY7oeKahTm1Jf0Gu4PrDa6jDt78/Q==", + "dependencies": { + "@types/node": "*", + "@types/request": "*" + } + }, "node_modules/@types/pdf-parse": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@types/pdf-parse/-/pdf-parse-1.1.4.tgz", "integrity": "sha512-+gbBHbNCVGGYw1S9lAIIvrHW47UYOhMIFUsJcMkMrzy1Jf0vulBN3XQIjPgnoOXveMuHnF3b57fXROnY/Or7eg==" }, + "node_modules/@types/request": { + "version": "2.48.12", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", + "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", + "dependencies": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + } + }, + "node_modules/@types/request/node_modules/form-data": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz", + "integrity": "sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -5852,15 +6004,20 @@ "integrity": "sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==", "license": "MIT" }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==" + }, "node_modules/@types/turndown": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/@types/turndown/-/turndown-5.0.5.tgz", "integrity": "sha512-TL2IgGgc7B5j78rIccBtlYAnkuv8nUQqhQc+DSYV5j9Be9XOcm/SKOVRuA47xAVI3680Tk9B1d8flK2GWT2+4w==" }, "node_modules/@types/uuid": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", - "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==" + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==" }, "node_modules/@types/vscode": { "version": "1.95.0", @@ -6257,7 +6414,6 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -6355,13 +6511,12 @@ "dev": true }, "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", - "dev": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" }, "engines": { "node": ">= 0.4" @@ -6379,20 +6534,34 @@ "node": ">=8" } }, + "node_modules/array.prototype.findindex": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.findindex/-/array.prototype.findindex-2.2.4.tgz", + "integrity": "sha512-LLm4mhxa9v8j0A/RPnpQAP4svXToJFh+Hp1pNYl5ZD5qpB4zdx/D4YjpVcETkhFbUKWO3iGMVLvrOnnmkAJT6A==", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", - "dev": true, + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dependencies": { "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" }, "engines": { "node": ">= 0.4" @@ -6401,6 +6570,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/ast-types": { "version": "0.13.4", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", @@ -6423,6 +6608,14 @@ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "dev": true }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -6432,7 +6625,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, "dependencies": { "possible-typed-array-names": "^1.0.0" }, @@ -6443,6 +6635,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==" + }, "node_modules/axios": { "version": "1.7.9", "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", @@ -6660,6 +6865,14 @@ "node": ">=10.0.0" } }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, "node_modules/better-path-resolve": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/better-path-resolve/-/better-path-resolve-1.0.0.tgz", @@ -6891,7 +7104,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "dev": true, "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", @@ -6906,10 +7118,9 @@ } }, "node_modules/call-bind-apply-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.0.tgz", - "integrity": "sha512-CCKAP2tkPau7D3GE8+V8R6sQubA9R5foIzGp+85EXCVSCivuxBNAWqcpn72PKYiIcqoViv/kcUDpaEIMBVi1lQ==", - "dev": true, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -6918,6 +7129,21 @@ "node": ">= 0.4" } }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -6956,6 +7182,11 @@ } ] }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -7387,6 +7618,17 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/data-uri-to-buffer": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", @@ -7396,14 +7638,13 @@ } }, "node_modules/data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", - "dev": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -7413,29 +7654,27 @@ } }, "node_modules/data-view-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", - "dev": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/inspect-js" } }, "node_modules/data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", - "dev": true, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" }, @@ -7518,7 +7757,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -7535,7 +7773,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -7745,12 +7982,11 @@ } }, "node_modules/dunder-proto": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.0.tgz", - "integrity": "sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==", - "dev": true, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "dependencies": { - "call-bind-apply-helpers": "^1.0.0", + "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" }, @@ -7764,6 +8000,20 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ecc-jsbn/node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -7916,57 +8166,61 @@ } }, "node_modules/es-abstract": { - "version": "1.23.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.5.tgz", - "integrity": "sha512-vlmniQ0WNPwXqA0BnmwV3Ng7HxiGlh6r5U6JcTMNx8OilcAGqVJBHJcPjqOMaczU9fRuRK5Px2BdVyPRnKMMVQ==", - "dev": true, + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", - "gopd": "^1.0.1", + "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.3", "object-keys": "^1.1.1", - "object.assign": "^4.1.5", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.3", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" }, "engines": { "node": ">= 0.4" @@ -7979,7 +8233,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -7988,7 +8241,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -7997,7 +8249,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dev": true, "dependencies": { "es-errors": "^1.3.0" }, @@ -8006,24 +8257,31 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", - "dev": true, + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dependencies": { - "get-intrinsic": "^1.2.4", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" } }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dependencies": { + "hasown": "^2.0.0" + } + }, "node_modules/es-to-primitive": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", - "dev": true, "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", @@ -8474,6 +8732,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "engines": [ + "node >=0.6.0" + ] + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -8502,8 +8768,7 @@ "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -8578,6 +8843,14 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -8678,7 +8951,6 @@ "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, "dependencies": { "is-callable": "^1.1.3" } @@ -8699,6 +8971,14 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "engines": { + "node": "*" + } + }, "node_modules/form-data": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", @@ -8766,21 +9046,21 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "dev": true, + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" }, "engines": { "node": ">= 0.4" @@ -8793,7 +9073,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8869,6 +9148,18 @@ "node": ">=14" } }, + "node_modules/gaxios/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/gcp-metadata": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", @@ -8911,16 +9202,20 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dev": true, + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -8938,6 +9233,18 @@ "node": ">=8.0.0" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -8951,14 +9258,13 @@ } }, "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", - "dev": true, + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dependencies": { - "call-bind": "^1.0.5", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -8980,6 +9286,14 @@ "node": ">= 14" } }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, "node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -9030,7 +9344,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "dev": true, "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" @@ -9081,7 +9394,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -9112,11 +9424,33 @@ "node": ">=14.0.0" } }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "peer": true, + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -9134,7 +9468,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, "dependencies": { "es-define-property": "^1.0.0" }, @@ -9146,7 +9479,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", - "dev": true, "dependencies": { "dunder-proto": "^1.0.0" }, @@ -9161,7 +9493,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -9173,7 +9504,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "dependencies": { "has-symbols": "^1.0.3" }, @@ -9193,7 +9523,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -9267,6 +9596,19 @@ "node": ">= 14" } }, + "node_modules/http-signature": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.4.0.tgz", + "integrity": "sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^2.0.2", + "sshpk": "^1.18.0" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/https-proxy-agent": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", @@ -9421,14 +9763,13 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", - "dev": true, + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dependencies": { "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -9447,13 +9788,13 @@ } }, "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", - "dev": true, + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -9469,12 +9810,15 @@ "dev": true }, "node_modules/is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", - "dev": true, + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dependencies": { - "has-tostringtag": "^1.0.0" + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -9487,7 +9831,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", - "dev": true, "dependencies": { "has-bigints": "^1.0.2" }, @@ -9511,12 +9854,11 @@ } }, "node_modules/is-boolean-object": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.0.tgz", - "integrity": "sha512-kR5g0+dXf/+kXnqI+lu0URKYPKgICtHGGNCDSB10AaUFj3o/HkB3u7WfpRBJGFopxxY0oH3ux7ZsDjLtK7xqvw==", - "dev": true, + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", + "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" }, "engines": { @@ -9530,7 +9872,6 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -9554,11 +9895,12 @@ } }, "node_modules/is-data-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", - "dev": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" }, "engines": { @@ -9569,12 +9911,12 @@ } }, "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -9592,12 +9934,11 @@ } }, "node_modules/is-finalizationregistry": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.0.tgz", - "integrity": "sha512-qfMdqbAQEwBw78ZyReKnlA8ezmPdb9BemzIIip/JkjaZUhitfXDkkr+3QTboW0JrSXT1QWyYShpvnNHGZ4c4yA==", - "dev": true, + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dependencies": { - "call-bind": "^1.0.7" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -9624,12 +9965,14 @@ } }, "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dev": true, + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -9665,19 +10008,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -9694,12 +10024,11 @@ } }, "node_modules/is-number-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.0.tgz", - "integrity": "sha512-KVSZV0Dunv9DTPkhXwcZ3Q+tUc9TsaE1ZwX5J2WMvsSGS6Md8TFPun5uwh0yRdrNerI6vf/tbJxqSx4c1ZI1Lw==", - "dev": true, + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" }, "engines": { @@ -9739,13 +10068,12 @@ } }, "node_modules/is-regex": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.0.tgz", - "integrity": "sha512-B6ohK4ZmoftlUe+uvenXSbPJFo6U37BH7oO1B3nQH8f/7h27N56s85MhUtbFJAziz5dcmuR3i8ovUl35zp8pFA==", - "dev": true, + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dependencies": { - "call-bind": "^1.0.7", - "gopd": "^1.1.0", + "call-bound": "^1.0.2", + "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" }, @@ -9760,7 +10088,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -9769,12 +10096,11 @@ } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", - "dev": true, + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dependencies": { - "call-bind": "^1.0.7" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -9795,12 +10121,11 @@ } }, "node_modules/is-string": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.0.tgz", - "integrity": "sha512-PlfzajuF9vSo5wErv3MJAKD/nqf9ngAs1NFQYm16nUYFO2IzxJ2hcm+IOCg+EEopdykNNUhVq5cz35cAUxU8+g==", - "dev": true, + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" }, "engines": { @@ -9823,14 +10148,13 @@ } }, "node_modules/is-symbol": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.0.tgz", - "integrity": "sha512-qS8KkNNXUZ/I+nX6QT8ZS1/Yx0A444yhzdTKxCzKkNjQ9sHErBxJnJAgh+f5YhusYECEcjo4XcyH87hn6+ks0A==", - "dev": true, + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dependencies": { - "call-bind": "^1.0.7", - "has-symbols": "^1.0.3", - "safe-regex-test": "^1.0.3" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -9840,12 +10164,11 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", - "dev": true, + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dependencies": { - "which-typed-array": "^1.1.14" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -9854,6 +10177,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -9870,7 +10198,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -9879,25 +10206,26 @@ } }, "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", + "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-weakset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", - "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", - "dev": true, + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4" + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -9944,6 +10272,11 @@ "node": ">=0.10.0" } }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -10830,11 +11163,15 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -10842,6 +11179,11 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -10862,6 +11204,20 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsprim": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", + "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + } + }, "node_modules/jszip": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", @@ -11269,6 +11625,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -11562,6 +11923,14 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/memorystream": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", @@ -11596,6 +11965,17 @@ "node": ">=8.6" } }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -11988,6 +12368,56 @@ "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true }, + "node_modules/node-telegram-bot-api": { + "version": "0.66.0", + "resolved": "https://registry.npmjs.org/node-telegram-bot-api/-/node-telegram-bot-api-0.66.0.tgz", + "integrity": "sha512-s4Hrg5q+VPl4/tJVG++pImxF6eb8tNJNj4KnDqAOKL6zGU34lo9RXmyAN158njwGN+v8hdNf8s9fWIYW9hPb5A==", + "dependencies": { + "@cypress/request": "^3.0.1", + "@cypress/request-promise": "^5.0.0", + "array.prototype.findindex": "^2.0.2", + "bl": "^1.2.3", + "debug": "^3.2.7", + "eventemitter3": "^3.0.0", + "file-type": "^3.9.0", + "mime": "^1.6.0", + "pump": "^2.0.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/node-telegram-bot-api/node_modules/bl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", + "dependencies": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/node-telegram-bot-api/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/node-telegram-bot-api/node_modules/eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + }, + "node_modules/node-telegram-bot-api/node_modules/pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -12226,11 +12656,19 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "peer": true, + "engines": { + "node": "*" + } + }, "node_modules/object-inspect": { "version": "1.13.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -12242,20 +12680,20 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, "engines": { "node": ">= 0.4" } }, "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "dev": true, + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", "object-keys": "^1.1.1" }, "engines": { @@ -12465,6 +12903,22 @@ "integrity": "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==", "dev": true }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/p-filter": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", @@ -12750,6 +13204,11 @@ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -12865,7 +13324,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -12977,6 +13435,17 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, "node_modules/pump": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", @@ -12990,7 +13459,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "engines": { "node": ">=6" } @@ -13039,6 +13507,25 @@ } ] }, + "node_modules/qs": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.1.tgz", + "integrity": "sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -13211,19 +13698,18 @@ } }, "node_modules/reflect.getprototypeof": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.8.tgz", - "integrity": "sha512-B5dj6usc5dkk8uFliwjwDHM8To5/QwdKz9JcBZ8Ic4G1f0YmeeJTtE/ZTdgRFPAfxZFiUaPhZ1Jcs4qeagItGQ==", - "dev": true, + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "dunder-proto": "^1.0.0", - "es-abstract": "^1.23.5", + "es-abstract": "^1.23.9", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.2.0", - "which-builtin-type": "^1.2.0" + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -13242,7 +13728,6 @@ "version": "1.5.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", - "dev": true, "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -13256,6 +13741,128 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "peer": true, + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request-promise-core": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz", + "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==", + "dependencies": { + "lodash": "^4.17.15" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "request": "^2.34" + } + }, + "node_modules/request/node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "peer": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/request/node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "peer": true, + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/request/node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "peer": true, + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/request/node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "peer": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/request/node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "peer": true, + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "peer": true, + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -13264,6 +13871,11 @@ "node": ">=0.10.0" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -13439,14 +14051,14 @@ } }, "node_modules/safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", - "dev": true, + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", "isarray": "^2.0.5" }, "engines": { @@ -13459,8 +14071,7 @@ "node_modules/safe-array-concat/node_modules/isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" }, "node_modules/safe-buffer": { "version": "5.2.1", @@ -13481,15 +14092,34 @@ } ] }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", - "dev": true, + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", - "is-regex": "^1.1.4" + "is-regex": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -13552,7 +14182,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -13569,7 +14198,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dev": true, "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -13580,6 +14208,19 @@ "node": ">= 0.4" } }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -13633,15 +14274,65 @@ } }, "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "dev": true, + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dependencies": { - "call-bind": "^1.0.7", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -13839,6 +14530,35 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sshpk/node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -13883,6 +14603,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/streamx": { "version": "2.21.0", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.21.0.tgz", @@ -14037,15 +14765,17 @@ } }, "node_modules/string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", - "dev": true, + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -14055,15 +14785,18 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", - "dev": true, + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -14072,7 +14805,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "dev": true, "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -14300,6 +15032,22 @@ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" }, + "node_modules/tldts": { + "version": "6.1.75", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.75.tgz", + "integrity": "sha512-+lFzEXhpl7JXgWYaXcB6DqTYXbUArvrWAE/5ioq/X3CdWLbDjpPP4XTrQBmEJ91y3xbe4Fkw7Lxv4P3GWeJaNg==", + "dependencies": { + "tldts-core": "^6.1.75" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.75", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.75.tgz", + "integrity": "sha512-AOvV5YYIAFFBfransBzSTyztkc3IMfz5Eq3YluaRiEu55nn43Fzaufx70UqEKYr8BoLCach4q8g/bg6e5+/aFw==" + }, "node_modules/tmp": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", @@ -14334,6 +15082,17 @@ "node": ">=0.6" } }, + "node_modules/tough-cookie": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.0.tgz", + "integrity": "sha512-rvZUv+7MoBYTiDmFPBrhL7Ujx9Sk+q9wwm22x8c8T5IJaR+Wsyc7TNxbVxo84kZoRJZZMazowFLqpankBEQrGg==", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -14412,6 +15171,17 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/turndown": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.2.0.tgz", @@ -14420,6 +15190,11 @@ "@mixmark-io/domino": "^2.2.0" } }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -14454,30 +15229,28 @@ } }, "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", - "dev": true, + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" } }, "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", - "dev": true, + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -14487,18 +15260,17 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.3.tgz", - "integrity": "sha512-GsvTyUHTriq6o/bHcTd0vM7OQ9JEdlvluu9YISaA7+KzDzPaIzEeDFNkTfhdE3MYcNhNi0vq/LlegYgIs5yPAw==", - "dev": true, + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "reflect.getprototypeof": "^1.0.6" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" }, "engines": { "node": ">= 0.4" @@ -14511,7 +15283,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", - "dev": true, "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", @@ -14546,15 +15317,17 @@ } }, "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dependencies": { - "call-bind": "^1.0.2", + "call-bound": "^1.0.3", "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -14671,11 +15444,19 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "dependencies": { "punycode": "^2.1.0" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/urlpattern-polyfill": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", @@ -14687,15 +15468,15 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz", + "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], "bin": { - "uuid": "dist/bin/uuid" + "uuid": "dist/esm/bin/uuid" } }, "node_modules/v8-to-istanbul": { @@ -14722,6 +15503,24 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/verror/node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -14792,16 +15591,15 @@ } }, "node_modules/which-boxed-primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.0.tgz", - "integrity": "sha512-Ei7Miu/AXe2JJ4iNF5j/UphAgRoma4trE6PtisM09bPygb3egMH3YLW/befsWb1A1AxvNSFidOFTB18XtnIIng==", - "dev": true, + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dependencies": { "is-bigint": "^1.1.0", - "is-boolean-object": "^1.2.0", - "is-number-object": "^1.1.0", - "is-string": "^1.1.0", - "is-symbol": "^1.1.0" + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" }, "engines": { "node": ">= 0.4" @@ -14811,24 +15609,23 @@ } }, "node_modules/which-builtin-type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.0.tgz", - "integrity": "sha512-I+qLGQ/vucCby4tf5HsLmGueEla4ZhwTBSqaooS+Y0BuxN4Cp+okmGuV+8mXZ84KDI9BA+oklo+RzKg0ONdSUA==", - "dev": true, + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", + "is-date-object": "^1.1.0", "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", + "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", + "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", - "which-typed-array": "^1.1.15" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -14840,14 +15637,12 @@ "node_modules/which-builtin-type/node_modules/isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" }, "node_modules/which-collection": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "dev": true, "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", @@ -14862,15 +15657,15 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.16.tgz", - "integrity": "sha512-g+N+GAWiRj66DngFwHvISJd+ITsyphZvD1vChfVg6cEdnzy53GzB3oy0fUNlvhz7H7+MiqhYr26qxQShCpKTTQ==", - "dev": true, + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", + "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "for-each": "^0.3.3", - "gopd": "^1.0.1", + "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, "engines": { diff --git a/package.json b/package.json index 9431af508ea..31782735a1d 100644 --- a/package.json +++ b/package.json @@ -264,9 +264,11 @@ "@mistralai/mistralai": "^1.3.6", "@modelcontextprotocol/sdk": "^1.0.1", "@types/clone-deep": "^4.0.4", + "@types/node-telegram-bot-api": "^0.64.7", "@types/pdf-parse": "^1.1.4", "@types/tmp": "^0.2.6", "@types/turndown": "^5.0.5", + "@types/uuid": "^10.0.0", "@types/vscode": "^1.95.0", "@vscode/codicons": "^0.0.36", "axios": "^1.7.4", @@ -283,6 +285,7 @@ "isbinaryfile": "^5.0.2", "mammoth": "^1.8.0", "monaco-vscode-textmate-theme-converter": "^0.1.7", + "node-telegram-bot-api": "^0.66.0", "openai": "^4.78.1", "os-name": "^6.0.0", "p-wait-for": "^5.0.2", @@ -297,6 +300,7 @@ "tmp": "^0.2.3", "tree-sitter-wasms": "^0.1.11", "turndown": "^7.2.0", + "uuid": "^11.0.5", "web-tree-sitter": "^0.22.6", "zod": "^3.23.8" }, diff --git a/src/services/notifications/NotificationManager.ts b/src/services/notifications/NotificationManager.ts new file mode 100644 index 00000000000..f811d1285e1 --- /dev/null +++ b/src/services/notifications/NotificationManager.ts @@ -0,0 +1,145 @@ +import * as vscode from 'vscode' +import { v4 as uuid } from 'uuid' +import { ClineAsk } from '../../shared/ExtensionMessage' +import { ClineAskResponse } from '../../shared/WebviewMessage' +import { NotificationProvider, NotificationRequest, NotificationResponse, mapClineAskToNotificationType } from './types' + +export class NotificationManager { + private providers: Map = new Map() + private pendingRequests: Map void + timestamp: number + }> = new Map() + + constructor( + private readonly context: vscode.ExtensionContext, + private readonly outputChannel: vscode.OutputChannel + ) {} + + async initialize() { + // Load providers from settings + const settings = await this.loadSettings() + + // Initialize enabled providers + for (const [providerId, providerSettings] of Object.entries(settings)) { + if (providerSettings.enabled) { + try { + // Dynamic import of provider + const provider = await this.loadProvider(providerId) + if (provider) { + await provider.initialize() + provider.onResponse(this.handleResponse.bind(this)) + this.providers.set(providerId, provider) + this.outputChannel.appendLine(`Initialized ${providerId} notification provider`) + } + } catch (error) { + this.outputChannel.appendLine(`Failed to initialize ${providerId} provider: ${error}`) + } + } + } + } + + async notify( + type: ClineAsk, + text: string, + metadata?: any, + resolver?: (response: {response: ClineAskResponse; text?: string}) => void + ) { + if (this.providers.size === 0) return + + const requestId = uuid() + if (resolver) { + this.pendingRequests.set(requestId, { + resolve: resolver, + timestamp: Date.now() + }) + } + + const request: NotificationRequest = { + type: mapClineAskToNotificationType(type), + message: text, + requestId, + metadata + } + + for (const provider of this.providers.values()) { + try { + await provider.sendNotification(request) + } catch (error) { + this.outputChannel.appendLine(`Failed to send notification via provider: ${error}`) + } + } + + // Clean up old pending requests + this.cleanupOldRequests() + } + + private handleResponse(response: NotificationResponse) { + const pending = this.pendingRequests.get(response.requestId) + if (!pending) { + this.outputChannel.appendLine(`Received response for unknown request: ${response.requestId}`) + return + } + + const clineResponse = this.mapResponseToClineAskResponse(response) + pending.resolve(clineResponse) + this.pendingRequests.delete(response.requestId) + } + + private mapResponseToClineAskResponse(response: NotificationResponse): { + response: ClineAskResponse + text?: string + } { + switch (response.type) { + case 'approve': + return { response: 'yesButtonClicked' } + case 'deny': + return { response: 'noButtonClicked' } + case 'text': + return { + response: 'messageResponse', + text: response.text + } + default: + throw new Error(`Unknown response type: ${response.type}`) + } + } + + private cleanupOldRequests() { + const now = Date.now() + const timeout = 1000 * 60 * 60 // 1 hour + + for (const [requestId, request] of this.pendingRequests.entries()) { + if (now - request.timestamp > timeout) { + this.pendingRequests.delete(requestId) + } + } + } + + private async loadSettings(): Promise> { + return this.context.globalState.get('notificationSettings', {}) + } + + private async loadProvider(providerId: string): Promise { + try { + const module = await import(`./providers/${providerId}Provider`) + const ProviderClass = module.default + return new ProviderClass(this.context) + } catch (error) { + this.outputChannel.appendLine(`Failed to load provider ${providerId}: ${error}`) + return undefined + } + } + + async dispose() { + for (const provider of this.providers.values()) { + try { + await provider.dispose() + } catch (error) { + this.outputChannel.appendLine(`Error disposing provider: ${error}`) + } + } + this.providers.clear() + this.pendingRequests.clear() + } +} \ No newline at end of file diff --git a/src/services/notifications/providers/telegramProvider.ts b/src/services/notifications/providers/telegramProvider.ts new file mode 100644 index 00000000000..2ae9b64ba16 --- /dev/null +++ b/src/services/notifications/providers/telegramProvider.ts @@ -0,0 +1,146 @@ +import { NotificationProvider, NotificationRequest, NotificationResponse } from '../types'; +import { logger } from '../../../utils/logger'; +import axios from 'axios'; +import { spawn } from 'child_process'; + +interface TelegramConfig { + botToken: string; + chatId: string; + pollingInterval: number; +} + +export class TelegramProvider implements NotificationProvider { + private config: TelegramConfig; + private ngrokProcess?: ReturnType; + private pollingInterval?: NodeJS.Timeout; + private responseCallback?: (response: NotificationResponse) => void; + private requestMap: Map; + + constructor(config: TelegramConfig) { + this.config = config; + this.requestMap = new Map(); + } + + async initialize(): Promise { + // Start ngrok for local tunneling + this.ngrokProcess = spawn('ngrok', ['http', '3000']); + + this.ngrokProcess.stdout?.on('data', (data) => { + const output = data.toString(); + const publicUrlMatch = output.match(/https:\/\/.*\.ngrok\.io/); + if (publicUrlMatch) { + this.setWebhook(publicUrlMatch[0]); + } + }); + + // Start polling for responses + this.pollingInterval = setInterval(async () => { + try { + const response = await axios.get( + `https://api.telegram.org/bot${this.config.botToken}/getUpdates`, + { params: { offset: -1 } } + ); + + response.data.result?.forEach((update: any) => { + if (update.message?.text) { + this.handleTelegramResponse(update.message.text); + } + }); + } catch (error) { + logger.error('Telegram polling error:', error); + } + }, this.config.pollingInterval * 1000); + } + + async sendNotification(request: NotificationRequest): Promise { + try { + // Store request for later matching with response + this.requestMap.set(request.requestId, request); + + // Format message based on notification type + let message = `*${request.type.toUpperCase()}*\n\n${request.message}`; + + if (request.type === 'approval') { + message += '\n\nReply with `approve` or `deny`'; + } + + if (request.metadata) { + message += '\n\n*Details:*'; + if (request.metadata.toolName) { + message += `\nTool: ${request.metadata.toolName}`; + } + if (request.metadata.path) { + message += `\nPath: ${request.metadata.path}`; + } + if (request.metadata.command) { + message += `\nCommand: ${request.metadata.command}`; + } + } + + await axios.post(`https://api.telegram.org/bot${this.config.botToken}/sendMessage`, { + chat_id: this.config.chatId, + text: message, + parse_mode: 'Markdown' + }); + } catch (error) { + logger.error('Telegram notification failed:', error); + throw error; + } + } + + onResponse(callback: (response: NotificationResponse) => void): void { + this.responseCallback = callback; + } + + private handleTelegramResponse(text: string): void { + if (!this.responseCallback) { + return; + } + + // Find the most recent request that hasn't been responded to + const [requestId, request] = Array.from(this.requestMap.entries())[0] || []; + if (!requestId || !request) { + return; + } + + let response: NotificationResponse; + + if (request.type === 'approval') { + const lowerText = text.toLowerCase().trim(); + if (lowerText === 'approve') { + response = { requestId, type: 'approve' }; + } else if (lowerText === 'deny') { + response = { requestId, type: 'deny' }; + } else { + return; // Invalid response for approval + } + } else { + response = { requestId, type: 'text', text }; + } + + // Remove the request from the map + this.requestMap.delete(requestId); + + // Send response through callback + this.responseCallback(response); + } + + private async setWebhook(ngrokUrl: string): Promise { + try { + await axios.post( + `https://api.telegram.org/bot${this.config.botToken}/setWebhook`, + { url: `${ngrokUrl}/webhook` } + ); + } catch (error) { + logger.error('Failed to set Telegram webhook:', error); + } + } + + async dispose(): Promise { + this.ngrokProcess?.kill(); + if (this.pollingInterval) { + clearInterval(this.pollingInterval); + } + this.requestMap.clear(); + } +} \ No newline at end of file diff --git a/src/services/notifications/types.ts b/src/services/notifications/types.ts new file mode 100644 index 00000000000..b4b944256cf --- /dev/null +++ b/src/services/notifications/types.ts @@ -0,0 +1,61 @@ +import { ClineAsk } from "../../shared/ExtensionMessage" + +export type NotificationType = 'approval' | 'question' | 'completion' + +export interface NotificationRequest { + type: NotificationType + message: string + requestId: string + metadata?: { + toolName?: string + path?: string + command?: string + } +} + +export interface NotificationResponse { + requestId: string + type: 'approve' | 'deny' | 'text' + text?: string +} + +export interface NotificationProvider { + /** + * Initialize the notification provider with any necessary setup + */ + initialize(): Promise + + /** + * Send a notification through this provider + */ + sendNotification(request: NotificationRequest): Promise + + /** + * Register a callback to handle responses from this provider + */ + onResponse(callback: (response: NotificationResponse) => void): void + + /** + * Clean up any resources used by this provider + */ + dispose(): Promise +} + +export interface NotificationSettings { + enabled: boolean + [key: string]: any +} + +export function mapClineAskToNotificationType(askType: ClineAsk): NotificationType { + switch (askType) { + case 'tool': + case 'command': + case 'browser_action_launch': + case 'use_mcp_server': + return 'approval' + case 'followup': + return 'question' + default: + return 'approval' + } +} \ No newline at end of file diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index e2830bf075b..422482fbce0 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -1,104 +1,84 @@ -import { ApiConfiguration, ApiProvider } from "./api" -import { Mode, PromptComponent, ModeConfig } from "./modes" +import { ApiConfiguration } from './api'; +import { HistoryItem } from './HistoryItem'; -export type PromptMode = Mode | "enhance" - -export type AudioType = "notification" | "celebration" | "progress_loop" - -export interface WebviewMessage { - type: - | "apiConfiguration" - | "currentApiConfigName" - | "upsertApiConfiguration" - | "deleteApiConfiguration" - | "loadApiConfiguration" - | "renameApiConfiguration" - | "getListApiConfiguration" - | "customInstructions" - | "allowedCommands" - | "alwaysAllowReadOnly" - | "alwaysAllowWrite" - | "alwaysAllowExecute" - | "webviewDidLaunch" - | "newTask" - | "askResponse" - | "clearTask" - | "didShowAnnouncement" - | "selectImages" - | "exportCurrentTask" - | "showTaskWithId" - | "deleteTaskWithId" - | "exportTaskWithId" - | "resetState" - | "requestOllamaModels" - | "requestLmStudioModels" - | "openImage" - | "openFile" - | "openMention" - | "cancelTask" - | "refreshGlamaModels" - | "refreshOpenRouterModels" - | "refreshOpenAiModels" - | "alwaysAllowBrowser" - | "alwaysAllowMcp" - | "playSound" - | "soundEnabled" - | "soundVolume" - | "diffEnabled" - | "browserViewportSize" - | "screenshotQuality" - | "openMcpSettings" - | "restartMcpServer" - | "toggleToolAlwaysAllow" - | "toggleMcpServer" - | "fuzzyMatchThreshold" - | "preferredLanguage" - | "writeDelayMs" - | "enhancePrompt" - | "enhancedPrompt" - | "draggedImages" - | "deleteMessage" - | "terminalOutputLineLimit" - | "mcpEnabled" - | "searchCommits" - | "refreshGlamaModels" - | "alwaysApproveResubmit" - | "requestDelaySeconds" - | "setApiConfigPassword" - | "requestVsCodeLmModels" - | "mode" - | "updatePrompt" - | "updateSupportPrompt" - | "resetSupportPrompt" - | "getSystemPrompt" - | "systemPrompt" - | "enhancementApiConfigId" - | "experimentalDiffStrategy" - | "autoApprovalEnabled" - | "updateCustomMode" - | "deleteCustomMode" - | "setopenAiCustomModelInfo" - | "openCustomModesSettings" - text?: string - disabled?: boolean - askResponse?: ClineAskResponse - apiConfiguration?: ApiConfiguration - images?: string[] - bool?: boolean - value?: number - commands?: string[] - audioType?: AudioType - serverName?: string - toolName?: string - alwaysAllow?: boolean - mode?: Mode - promptMode?: PromptMode - customPrompt?: PromptComponent - dataUrls?: string[] - values?: Record - query?: string - slug?: string - modeConfig?: ModeConfig -} - -export type ClineAskResponse = "yesButtonClicked" | "noButtonClicked" | "messageResponse" +export type WebviewMessage = + | { + type: 'apiConfiguration'; + configuration: ApiConfiguration; + } + | { + type: 'currentApiConfigName'; + name: string; + } + | { + type: 'upsertApiConfiguration'; + configuration: ApiConfiguration; + } + | { + type: 'deleteApiConfiguration'; + name: string; + } + | { + type: 'loadApiConfiguration'; + name: string; + } + | { + type: 'renameApiConfiguration'; + oldName: string; + newName: string; + } + | { + type: 'getListApiConfiguration'; + } + | { + type: 'getNotificationSettings'; + } + | { + type: 'updateNotificationSettings'; + settings: { + telegram?: { + enabled: boolean; + botToken: string; + chatId: string; + pollingInterval: number; + }; + }; + } + | { + type: 'notificationSettings'; + settings: { + telegram?: { + enabled: boolean; + botToken: string; + chatId: string; + pollingInterval: number; + }; + }; + } + | { + type: 'listApiConfiguration'; + configurations: ApiConfiguration[]; + } + | { + type: 'submit'; + message: string; + } + | { + type: 'cancel'; + } + | { + type: 'clear'; + } + | { + type: 'retry'; + } + | { + type: 'stop'; + } + | { + type: 'history'; + items: HistoryItem[]; + } + | { + type: 'openCustomModesSettings'; + }; diff --git a/src/utils/logger.ts b/src/utils/logger.ts new file mode 100644 index 00000000000..9144978ed03 --- /dev/null +++ b/src/utils/logger.ts @@ -0,0 +1,34 @@ +import * as vscode from 'vscode'; + +class Logger { + private outputChannel: vscode.OutputChannel; + + constructor() { + this.outputChannel = vscode.window.createOutputChannel('Roo Code'); + } + + info(message: string, ...args: any[]) { + this.log('INFO', message, ...args); + } + + error(message: string, ...args: any[]) { + this.log('ERROR', message, ...args); + } + + warn(message: string, ...args: any[]) { + this.log('WARN', message, ...args); + } + + private log(level: string, message: string, ...args: any[]) { + const timestamp = new Date().toISOString(); + const formattedMessage = `[${timestamp}] [${level}] ${message}`; + + if (args.length > 0) { + this.outputChannel.appendLine(`${formattedMessage} ${JSON.stringify(args)}`); + } else { + this.outputChannel.appendLine(formattedMessage); + } + } +} + +export const logger = new Logger(); \ No newline at end of file diff --git a/webview-ui/src/components/settings/NotificationSettings.tsx b/webview-ui/src/components/settings/NotificationSettings.tsx new file mode 100644 index 00000000000..4efb1bd2546 --- /dev/null +++ b/webview-ui/src/components/settings/NotificationSettings.tsx @@ -0,0 +1,152 @@ +import React, { useEffect, useState } from 'react'; +import { vscode } from '../../utils/vscode'; + +interface NotificationSettings { + telegram?: { + enabled: boolean; + botToken: string; + chatId: string; + pollingInterval: number; + }; +} + +export const NotificationSettings: React.FC = () => { + const [settings, setSettings] = useState({}); + const [loading, setLoading] = useState(true); + + useEffect(() => { + // Load initial settings + vscode.postMessage({ + type: 'getNotificationSettings' + }); + + const messageHandler = (event: MessageEvent) => { + const message = event.data; + if (message.type === 'notificationSettings') { + setSettings(message.settings); + setLoading(false); + } + }; + + window.addEventListener('message', messageHandler); + return () => window.removeEventListener('message', messageHandler); + }, []); + + const handleChange = (section: 'telegram', field: string, value: any) => { + const newSettings = { + ...settings, + [section]: { + ...settings[section], + [field]: value + } + }; + setSettings(newSettings); + + vscode.postMessage({ + type: 'updateNotificationSettings', + settings: newSettings + }); + }; + + if (loading) { + return
Loading settings...
; + } + + return ( +
+

Telegram Notifications

+
+ + + {settings.telegram?.enabled && ( + <> +
+ + handleChange('telegram', 'botToken', e.target.value)} + placeholder="Enter your Telegram bot token" + /> + + Create a new bot and get the token from{' '} + + @BotFather + + +
+ +
+ + handleChange('telegram', 'chatId', e.target.value)} + placeholder="Enter your Telegram chat ID" + /> + + Start a chat with your bot and send /start to get your chat ID + +
+ +
+ + handleChange('telegram', 'pollingInterval', parseInt(e.target.value))} + min="500" + max="5000" + /> +
+ + )} +
+ + +
+ ); +}; \ No newline at end of file From 121d100986ce68dfa75d213b40d082120cd6419b Mon Sep 17 00:00:00 2001 From: Wesley Harding <2081303+wesley-harding@users.noreply.github.com> Date: Sun, 26 Jan 2025 12:13:34 -0500 Subject: [PATCH 2/4] Putting in an automated check point --- .../2025-01-26-telegram-notifications.md | 96 +++ src/core/Cline.ts | 2 + .../notifications/NotificationManager.ts | 223 ++++--- src/shared/ExtensionMessage.ts | 16 +- src/shared/WebviewMessage.ts | 5 + webview-ui/package-lock.json | 553 +++++++++++++++++- webview-ui/package.json | 14 +- .../settings/NotificationSettings.tsx | 249 ++++---- .../src/context/ExtensionStateContext.tsx | 34 +- 9 files changed, 903 insertions(+), 289 deletions(-) create mode 100644 cline_docs/architecture/decisions/2025-01-26-telegram-notifications.md diff --git a/cline_docs/architecture/decisions/2025-01-26-telegram-notifications.md b/cline_docs/architecture/decisions/2025-01-26-telegram-notifications.md new file mode 100644 index 00000000000..264f9467fd1 --- /dev/null +++ b/cline_docs/architecture/decisions/2025-01-26-telegram-notifications.md @@ -0,0 +1,96 @@ +# Telegram Notifications Integration + +## Context + +Users need to be notified and respond to Roo Code's requests when they are away from their computer. The system needs to handle two types of interactions: +1. Approval requests for actions (commands, file operations, etc.) +2. Open-ended questions requiring user input + +## Decision + +We will integrate Telegram as our notification provider because: +1. It has a robust API for two-way communication +2. It's widely available across platforms +3. It provides secure bot tokens for authentication +4. Messages can be formatted with Markdown +5. It has a simple webhook system for real-time updates + +### Architecture + +The notification system consists of several components: + +1. **NotificationManager**: A singleton service that: + - Manages notification providers (currently Telegram) + - Routes notifications to appropriate providers + - Handles response callbacks + - Maintains provider configurations + +2. **TelegramProvider**: Implements the NotificationProvider interface: + - Sends formatted messages via Telegram Bot API + - Uses ngrok for local webhook tunneling + - Polls for responses when webhooks aren't available + - Parses responses based on notification type + +3. **NotificationSettings UI**: React component in the webview that: + - Allows users to enable/disable notifications + - Configures Telegram bot token and chat ID + - Sets polling interval for responses + +### Message Flow + +1. **Approval Requests**: + ``` + [Extension] -> NotificationManager -> TelegramProvider -> [Telegram] + Message: "Approve command: git push" + Response: "approve" or "deny" + ``` + +2. **Questions**: + ``` + [Extension] -> NotificationManager -> TelegramProvider -> [Telegram] + Message: "What branch should I create?" + Response: [Free-form text] + ``` + +### Security Considerations + +1. Bot tokens are stored securely in VSCode's global storage +2. All communication is encrypted via Telegram's API +3. Responses are validated against request IDs to prevent replay attacks +4. Only configured chat IDs can interact with the bot + +## Implementation Notes + +1. **Local Development**: + - Uses ngrok for webhook tunneling + - Falls back to polling if ngrok is not available + +2. **Response Handling**: + - Approval requests require exact "approve" or "deny" responses + - Questions accept any text response + - Responses are matched to requests via unique IDs + +3. **Error Handling**: + - Failed notifications are logged but don't block the system + - Retries are implemented for transient failures + - Users are notified of configuration issues + +## Alternatives Considered + +1. **Email Integration**: + - Rejected due to complexity of handling responses + - SMTP setup too cumbersome for users + +2. **Custom Mobile App**: + - Rejected due to development overhead + - Distribution and maintenance challenges + +3. **Desktop Notifications**: + - Rejected as it doesn't solve the away-from-computer use case + +## Future Considerations + +1. Add support for multiple notification providers +2. Implement message priority levels +3. Add support for rich media responses (files, images) +4. Add support for notification grouping and threading \ No newline at end of file diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 859d2d54845..c3417c24f3d 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -2,6 +2,7 @@ import { Anthropic } from "@anthropic-ai/sdk" import cloneDeep from "clone-deep" import { DiffStrategy, getDiffStrategy, UnifiedDiffStrategy } from "./diff/DiffStrategy" import { validateToolUse, isToolAllowedForMode, ToolName } from "./mode-validator" +import { NotificationManager } from "../services/notifications/NotificationManager" import delay from "delay" import fs from "fs/promises" import os from "os" @@ -75,6 +76,7 @@ export class Cline { private terminalManager: TerminalManager private urlContentFetcher: UrlContentFetcher private browserSession: BrowserSession + private notificationManager: NotificationManager private didEditFile: boolean = false customInstructions?: string diffStrategy?: DiffStrategy diff --git a/src/services/notifications/NotificationManager.ts b/src/services/notifications/NotificationManager.ts index f811d1285e1..edb0f95d83d 100644 --- a/src/services/notifications/NotificationManager.ts +++ b/src/services/notifications/NotificationManager.ts @@ -1,145 +1,138 @@ -import * as vscode from 'vscode' -import { v4 as uuid } from 'uuid' -import { ClineAsk } from '../../shared/ExtensionMessage' -import { ClineAskResponse } from '../../shared/WebviewMessage' -import { NotificationProvider, NotificationRequest, NotificationResponse, mapClineAskToNotificationType } from './types' +import { NotificationProvider, NotificationRequest, NotificationResponse, NotificationType } from './types'; +import { TelegramProvider } from './providers/telegramProvider'; +import { logger } from '../../utils/logger'; +import { ClineAsk } from '../../shared/ExtensionMessage'; +import { mapClineAskToNotificationType } from './types'; + +export interface NotificationManagerConfig { + telegram?: { + enabled: boolean; + botToken: string; + chatId: string; + pollingInterval: number; + }; +} export class NotificationManager { - private providers: Map = new Map() - private pendingRequests: Map void - timestamp: number - }> = new Map() - - constructor( - private readonly context: vscode.ExtensionContext, - private readonly outputChannel: vscode.OutputChannel - ) {} - - async initialize() { - // Load providers from settings - const settings = await this.loadSettings() - - // Initialize enabled providers - for (const [providerId, providerSettings] of Object.entries(settings)) { - if (providerSettings.enabled) { - try { - // Dynamic import of provider - const provider = await this.loadProvider(providerId) - if (provider) { - await provider.initialize() - provider.onResponse(this.handleResponse.bind(this)) - this.providers.set(providerId, provider) - this.outputChannel.appendLine(`Initialized ${providerId} notification provider`) + private static instance: NotificationManager; + private providers: Map; + private config: NotificationManagerConfig; + private pendingRequests: Map void>; + + private constructor() { + this.providers = new Map(); + this.config = {}; + this.pendingRequests = new Map(); + } + + static getInstance(): NotificationManager { + if (!NotificationManager.instance) { + NotificationManager.instance = new NotificationManager(); + } + return NotificationManager.instance; + } + + async initialize(config: NotificationManagerConfig): Promise { + this.config = config; + + // Clear existing providers + for (const provider of this.providers.values()) { + await provider.dispose(); + } + this.providers.clear(); + + // Initialize Telegram provider if enabled + if (config.telegram?.enabled) { + try { + const telegramProvider = new TelegramProvider({ + botToken: config.telegram.botToken, + chatId: config.telegram.chatId, + pollingInterval: config.telegram.pollingInterval + }); + + await telegramProvider.initialize(); + + // Set up response handling + telegramProvider.onResponse((response: NotificationResponse) => { + const handler = this.pendingRequests.get(response.requestId); + if (handler) { + handler(response); + this.pendingRequests.delete(response.requestId); } - } catch (error) { - this.outputChannel.appendLine(`Failed to initialize ${providerId} provider: ${error}`) - } + }); + + this.providers.set('telegram', telegramProvider); + logger.info('Telegram notification provider initialized'); + } catch (error) { + logger.error('Failed to initialize Telegram provider:', error); + throw error; } } } async notify( - type: ClineAsk, - text: string, - metadata?: any, - resolver?: (response: {response: ClineAskResponse; text?: string}) => void - ) { - if (this.providers.size === 0) return - - const requestId = uuid() - if (resolver) { - this.pendingRequests.set(requestId, { - resolve: resolver, - timestamp: Date.now() - }) + type: NotificationType, + message: string, + metadata?: { + toolName?: string; + path?: string; + command?: string; } - + ): Promise { + const requestId = crypto.randomUUID(); const request: NotificationRequest = { - type: mapClineAskToNotificationType(type), - message: text, + type, + message, requestId, metadata - } + }; - for (const provider of this.providers.values()) { + // Send to all active providers + const errors: Error[] = []; + for (const [name, provider] of this.providers.entries()) { try { - await provider.sendNotification(request) + await provider.sendNotification(request); + logger.info(`Notification sent via ${name} provider`); } catch (error) { - this.outputChannel.appendLine(`Failed to send notification via provider: ${error}`) + logger.error(`Failed to send notification via ${name} provider:`, error); + errors.push(error as Error); } } - // Clean up old pending requests - this.cleanupOldRequests() - } - - private handleResponse(response: NotificationResponse) { - const pending = this.pendingRequests.get(response.requestId) - if (!pending) { - this.outputChannel.appendLine(`Received response for unknown request: ${response.requestId}`) - return + // If all providers failed, throw an error + if (errors.length === this.providers.size) { + throw new Error('All notification providers failed'); } - const clineResponse = this.mapResponseToClineAskResponse(response) - pending.resolve(clineResponse) - this.pendingRequests.delete(response.requestId) + // Wait for response + return new Promise((resolve) => { + this.pendingRequests.set(requestId, resolve); + }); } - private mapResponseToClineAskResponse(response: NotificationResponse): { - response: ClineAskResponse - text?: string - } { - switch (response.type) { - case 'approve': - return { response: 'yesButtonClicked' } - case 'deny': - return { response: 'noButtonClicked' } - case 'text': - return { - response: 'messageResponse', - text: response.text - } - default: - throw new Error(`Unknown response type: ${response.type}`) - } - } - - private cleanupOldRequests() { - const now = Date.now() - const timeout = 1000 * 60 * 60 // 1 hour - - for (const [requestId, request] of this.pendingRequests.entries()) { - if (now - request.timestamp > timeout) { - this.pendingRequests.delete(requestId) - } + async notifyFromClineAsk( + askType: ClineAsk, + message: string, + metadata?: { + toolName?: string; + path?: string; + command?: string; } + ): Promise { + const notificationType = mapClineAskToNotificationType(askType); + return this.notify(notificationType, message, metadata); } - private async loadSettings(): Promise> { - return this.context.globalState.get('notificationSettings', {}) - } - - private async loadProvider(providerId: string): Promise { - try { - const module = await import(`./providers/${providerId}Provider`) - const ProviderClass = module.default - return new ProviderClass(this.context) - } catch (error) { - this.outputChannel.appendLine(`Failed to load provider ${providerId}: ${error}`) - return undefined + async dispose(): Promise { + // Dispose all providers + for (const provider of this.providers.values()) { + await provider.dispose(); } + this.providers.clear(); + this.pendingRequests.clear(); } - async dispose() { - for (const provider of this.providers.values()) { - try { - await provider.dispose() - } catch (error) { - this.outputChannel.appendLine(`Error disposing provider: ${error}`) - } - } - this.providers.clear() - this.pendingRequests.clear() + getConfig(): NotificationManagerConfig { + return this.config; } } \ No newline at end of file diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index e8f61b3a9cb..f3b47218f4e 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -41,6 +41,7 @@ export interface ExtensionMessage { | "autoApprovalEnabled" | "updateCustomMode" | "deleteCustomMode" + | "notificationSettings" text?: string action?: | "chatButtonClicked" @@ -110,7 +111,15 @@ export interface ExtensionState { experimentalDiffStrategy?: boolean autoApprovalEnabled?: boolean customModes: ModeConfig[] - toolRequirements?: Record // Map of tool names to their requirements (e.g. {"apply_diff": true} if diffEnabled) + toolRequirements?: Record + notificationSettings?: { + telegram?: { + enabled: boolean + botToken: string + chatId: string + pollingInterval: number + } + } } export interface ClineMessage { @@ -122,6 +131,7 @@ export interface ClineMessage { images?: string[] partial?: boolean reasoning?: string + notificationId?: string // For tracking notification responses } export type ClineAsk = @@ -136,6 +146,7 @@ export type ClineAsk = | "mistake_limit_reached" | "browser_action_launch" | "use_mcp_server" + | "notification_response" // For handling notification responses export type ClineSay = | "task" @@ -157,6 +168,7 @@ export type ClineSay = | "command" | "mcp_server_request_started" | "mcp_server_response" + | "notification_sent" // For tracking sent notifications export interface ClineSayTool { tool: @@ -169,6 +181,7 @@ export interface ClineSayTool { | "listCodeDefinitionNames" | "searchFiles" | "switchMode" + | "sendNotification" // For sending notifications path?: string diff?: string content?: string @@ -176,6 +189,7 @@ export interface ClineSayTool { filePattern?: string mode?: string reason?: string + notificationType?: "approval" | "question" // Type of notification being sent } // must keep in sync with system prompt diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 422482fbce0..428ddf8ac2a 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -1,6 +1,8 @@ import { ApiConfiguration } from './api'; import { HistoryItem } from './HistoryItem'; +export type ClineAskResponse = 'yesButtonClicked' | 'noButtonClicked' | 'messageResponse'; + export type WebviewMessage = | { type: 'apiConfiguration'; @@ -81,4 +83,7 @@ export type WebviewMessage = } | { type: 'openCustomModesSettings'; + } + | { + type: 'webviewDidLaunch'; }; diff --git a/webview-ui/package-lock.json b/webview-ui/package-lock.json index acddd070858..1b6f0803dcc 100644 --- a/webview-ui/package-lock.json +++ b/webview-ui/package-lock.json @@ -8,13 +8,16 @@ "name": "webview-ui", "version": "0.1.0", "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@mui/material": "^6.4.1", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "@types/jest": "^27.5.2", "@types/node": "^16.18.101", - "@types/react": "^18.3.3", - "@types/react-dom": "^18.3.0", + "@types/react": "^18.3.18", + "@types/react-dom": "^18.3.5", "@vscode/webview-ui-toolkit": "^1.4.0", "debounce": "^2.1.1", "fast-deep-equal": "^3.1.3", @@ -36,7 +39,14 @@ }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "@types/express": "^4.17.21", + "@types/hast": "^3.0.4", + "@types/mdast": "^4.0.4", + "@types/parse-json": "^4.0.2", + "@types/prop-types": "^15.7.14", + "@types/retry": "^0.12.5", "@types/shell-quote": "^1.7.5", + "@types/unist": "^3.0.3", "@types/vscode-webview": "^1.57.5", "customize-cra": "^1.0.0", "eslint": "^8.57.0", @@ -2041,6 +2051,74 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==" + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + }, + "node_modules/@emotion/babel-plugin/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/cache/node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==" + }, + "node_modules/@emotion/cache/node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==" + }, "node_modules/@emotion/is-prop-valid": { "version": "1.2.2", "license": "MIT", @@ -2052,10 +2130,113 @@ "version": "0.8.1", "license": "MIT" }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/serialize/node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==" + }, + "node_modules/@emotion/serialize/node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==" + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==" + }, + "node_modules/@emotion/styled": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", + "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/styled/node_modules/@emotion/is-prop-valid": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", + "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/styled/node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==" + }, "node_modules/@emotion/unitless": { "version": "0.8.1", "license": "MIT" }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.1", "license": "MIT", @@ -2644,6 +2825,213 @@ "exenv-es6": "^1.1.1" } }, + "node_modules/@mui/core-downloads-tracker": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.1.tgz", + "integrity": "sha512-SfDLWMV5b5oXgDf3NTa2hCTPC1d2defhDH2WgFKmAiejC4mSfXYbyi+AFCLzpizauXhgBm8OaZy9BHKnrSpahQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/material": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.1.tgz", + "integrity": "sha512-MFBfia6UiKxyoLeGkAh8M15bkeDmfnsUTMRJd/vTQue6YQ8AQ6lw9HqDthyYghzDEWIvZO/lQQzLrZE8XwNJLA==", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/core-downloads-tracker": "^6.4.1", + "@mui/system": "^6.4.1", + "@mui/types": "^7.2.21", + "@mui/utils": "^6.4.1", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^19.0.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material-pigment-css": "^6.4.1", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@mui/material-pigment-css": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/react-is": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz", + "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==" + }, + "node_modules/@mui/private-theming": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.1.tgz", + "integrity": "sha512-DcT7mwK89owwgcEuiE7w458te4CIjHbYWW6Kn6PiR6eLtxBsoBYphA968uqsQAOBQDpbYxvkuFLwhgk4bxoN/Q==", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/utils": "^6.4.1", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.4.0.tgz", + "integrity": "sha512-ek/ZrDujrger12P6o4luQIfRd2IziH7jQod2WMbLqGE03Iy0zUwYmckRTVhRQTLPNccpD8KXGcALJF+uaUQlbg==", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@emotion/cache": "^11.13.5", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.1.tgz", + "integrity": "sha512-rgQzgcsHCTtzF9MZ+sL0tOhf2ZBLazpjrujClcb4Siju5lTrK0xX4PsiropActzCemNfM+mOu+0jezAVnfRK8g==", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/private-theming": "^6.4.1", + "@mui/styled-engine": "^6.4.0", + "@mui/types": "^7.2.21", + "@mui/utils": "^6.4.1", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.2.21", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.21.tgz", + "integrity": "sha512-6HstngiUxNqLU+/DPqlUJDIPbzUBxIVHb1MmXP0eTWDIROiCR2viugXpEif0PPe2mLqqakPzzRClWAnK+8UJww==", + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.1.tgz", + "integrity": "sha512-iQUDUeYh87SvR4lVojaRaYnQix8BbRV51MxaV6MBmqthecQoxwSbS5e2wnbDJUeFxY2ppV505CiqPLtd0OWkqw==", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/types": "^7.2.21", + "@types/prop-types": "^15.7.14", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.0.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils/node_modules/react-is": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz", + "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==" + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "license": "MIT", @@ -2752,6 +3140,15 @@ } } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "license": "MIT", @@ -3256,7 +3653,8 @@ }, "node_modules/@types/express": { "version": "4.17.21", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", @@ -3276,7 +3674,8 @@ }, "node_modules/@types/express/node_modules/@types/express-serve-static-core": { "version": "4.19.6", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -3293,7 +3692,8 @@ }, "node_modules/@types/hast": { "version": "3.0.4", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", "dependencies": { "@types/unist": "*" } @@ -3308,7 +3708,8 @@ }, "node_modules/@types/http-proxy": { "version": "1.17.15", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.15.tgz", + "integrity": "sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==", "dependencies": { "@types/node": "*" } @@ -3352,16 +3753,14 @@ "license": "MIT" }, "node_modules/@types/mdast": { - "version": "3.0.15", - "license": "MIT", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dev": true, "dependencies": { - "@types/unist": "^2" + "@types/unist": "*" } }, - "node_modules/@types/mdast/node_modules/@types/unist": { - "version": "2.0.11", - "license": "MIT" - }, "node_modules/@types/mime": { "version": "1.3.5", "license": "MIT" @@ -3379,7 +3778,8 @@ }, "node_modules/@types/parse-json": { "version": "4.0.2", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" }, "node_modules/@types/prettier": { "version": "2.7.3", @@ -3387,7 +3787,8 @@ }, "node_modules/@types/prop-types": { "version": "15.7.14", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==" }, "node_modules/@types/q": { "version": "1.5.8", @@ -3402,18 +3803,28 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "18.3.14", - "license": "MIT", + "version": "18.3.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", + "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "18.3.2", - "license": "MIT", - "dependencies": { - "@types/react": "^18" + "version": "18.3.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz", + "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "peerDependencies": { + "@types/react": "*" } }, "node_modules/@types/resolve": { @@ -3424,8 +3835,10 @@ } }, "node_modules/@types/retry": { - "version": "0.12.0", - "license": "MIT" + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.5.tgz", + "integrity": "sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw==", + "dev": true }, "node_modules/@types/semver": { "version": "7.5.8", @@ -3489,7 +3902,8 @@ }, "node_modules/@types/unist": { "version": "3.0.3", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" }, "node_modules/@types/vscode-webview": { "version": "1.57.5", @@ -4992,6 +5406,14 @@ "wrap-ansi": "^7.0.0" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, "node_modules/co": { "version": "4.6.0", "license": "MIT", @@ -5952,6 +6374,15 @@ "utila": "~0.4" } }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/dom-serializer": { "version": "1.4.1", "license": "MIT", @@ -6900,7 +7331,8 @@ }, "node_modules/eventemitter3": { "version": "4.0.7", - "license": "MIT" + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" }, "node_modules/events": { "version": "3.3.0", @@ -7210,6 +7642,11 @@ "url": "https://github.com/avajs/find-cache-dir?sponsor=1" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, "node_modules/find-up": { "version": "4.1.0", "license": "MIT", @@ -7239,13 +7676,14 @@ }, "node_modules/follow-redirects": { "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "funding": [ { "type": "individual", "url": "https://github.com/sponsors/RubenVerborgh" } ], - "license": "MIT", "engines": { "node": ">=4.0" }, @@ -7825,6 +8263,19 @@ "node": ">=12.0.0" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/hoopy": { "version": "0.1.4", "license": "MIT", @@ -7988,7 +8439,8 @@ }, "node_modules/http-proxy": { "version": "1.18.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "dependencies": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", @@ -8012,7 +8464,8 @@ }, "node_modules/http-proxy-middleware": { "version": "2.0.7", - "license": "MIT", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", + "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", "dependencies": { "@types/http-proxy": "^1.17.8", "http-proxy": "^1.18.1", @@ -8034,7 +8487,8 @@ }, "node_modules/http-proxy-middleware/node_modules/is-plain-obj": { "version": "3.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", "engines": { "node": ">=10" }, @@ -10061,6 +10515,19 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdast-util-from-markdown/node_modules/@types/mdast": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", + "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/mdast-util-from-markdown/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==" + }, "node_modules/mdast-util-to-hast": { "version": "10.2.0", "license": "MIT", @@ -10079,6 +10546,14 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdast-util-to-hast/node_modules/@types/mdast": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", + "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", + "dependencies": { + "@types/unist": "^2" + } + }, "node_modules/mdast-util-to-hast/node_modules/@types/unist": { "version": "2.0.11", "license": "MIT" @@ -10680,6 +11155,11 @@ "node": ">=8" } }, + "node_modules/p-retry/node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + }, "node_modules/p-try": { "version": "2.2.0", "license": "MIT", @@ -12551,6 +13031,21 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/react-universal-interface": { "version": "0.6.2", "peerDependencies": { diff --git a/webview-ui/package.json b/webview-ui/package.json index a7c616d1166..cb10fb9f99d 100644 --- a/webview-ui/package.json +++ b/webview-ui/package.json @@ -3,13 +3,16 @@ "version": "0.1.0", "private": true, "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@mui/material": "^6.4.1", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "@types/jest": "^27.5.2", "@types/node": "^16.18.101", - "@types/react": "^18.3.3", - "@types/react-dom": "^18.3.0", + "@types/react": "^18.3.18", + "@types/react-dom": "^18.3.5", "@vscode/webview-ui-toolkit": "^1.4.0", "debounce": "^2.1.1", "fast-deep-equal": "^3.1.3", @@ -56,7 +59,14 @@ }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "@types/express": "^4.17.21", + "@types/hast": "^3.0.4", + "@types/mdast": "^4.0.4", + "@types/parse-json": "^4.0.2", + "@types/prop-types": "^15.7.14", + "@types/retry": "^0.12.5", "@types/shell-quote": "^1.7.5", + "@types/unist": "^3.0.3", "@types/vscode-webview": "^1.57.5", "customize-cra": "^1.0.0", "eslint": "^8.57.0", diff --git a/webview-ui/src/components/settings/NotificationSettings.tsx b/webview-ui/src/components/settings/NotificationSettings.tsx index 4efb1bd2546..8828b002610 100644 --- a/webview-ui/src/components/settings/NotificationSettings.tsx +++ b/webview-ui/src/components/settings/NotificationSettings.tsx @@ -1,152 +1,131 @@ import React, { useEffect, useState } from 'react'; +import { TextField, Switch, Stack, Typography } from '@mui/material'; import { vscode } from '../../utils/vscode'; -interface NotificationSettings { - telegram?: { +interface TelegramSettings { enabled: boolean; botToken: string; chatId: string; pollingInterval: number; - }; } -export const NotificationSettings: React.FC = () => { - const [settings, setSettings] = useState({}); - const [loading, setLoading] = useState(true); - - useEffect(() => { - // Load initial settings - vscode.postMessage({ - type: 'getNotificationSettings' - }); - - const messageHandler = (event: MessageEvent) => { - const message = event.data; - if (message.type === 'notificationSettings') { - setSettings(message.settings); - setLoading(false); - } - }; - - window.addEventListener('message', messageHandler); - return () => window.removeEventListener('message', messageHandler); - }, []); - - const handleChange = (section: 'telegram', field: string, value: any) => { - const newSettings = { - ...settings, - [section]: { - ...settings[section], - [field]: value - } - }; - setSettings(newSettings); +interface NotificationSettings { + telegram?: TelegramSettings; +} - vscode.postMessage({ - type: 'updateNotificationSettings', - settings: newSettings +export const NotificationSettings: React.FC = () => { + const [settings, setSettings] = useState({ + telegram: { + enabled: false, + botToken: '', + chatId: '', + pollingInterval: 30 + } }); - }; - - if (loading) { - return
Loading settings...
; - } - - return ( -
-

Telegram Notifications

-
- - {settings.telegram?.enabled && ( - <> -
- - handleChange('telegram', 'botToken', e.target.value)} - placeholder="Enter your Telegram bot token" - /> - - Create a new bot and get the token from{' '} - - @BotFather - - -
+ useEffect(() => { + // Request current settings when component mounts + vscode.postMessage({ + type: 'getNotificationSettings' + }); + }, []); -
- - handleChange('telegram', 'chatId', e.target.value)} - placeholder="Enter your Telegram chat ID" - /> - - Start a chat with your bot and send /start to get your chat ID - -
+ useEffect(() => { + // Handle settings updates from extension + const messageHandler = (event: MessageEvent) => { + const message = event.data; + if (message.type === 'notificationSettings') { + setSettings(message.settings); + } + }; -
- - handleChange('telegram', 'pollingInterval', parseInt(e.target.value))} - min="500" - max="5000" - /> -
- - )} -
+ window.addEventListener('message', messageHandler); + return () => window.removeEventListener('message', messageHandler); + }, []); - -
- ); + + // Set the value + current[path[path.length - 1]] = value; + + // Update local state + setSettings(newSettings); + + // Send update to extension + vscode.postMessage({ + type: 'updateNotificationSettings', + settings: newSettings + }); + }; + + return ( + + Notification Settings + + + Telegram + + ) => + handleSettingChange(['telegram', 'enabled'], e.target.checked) + } + inputProps={{ 'aria-label': 'Enable Telegram notifications' }} + /> + + {settings.telegram?.enabled && ( + <> + ) => + handleSettingChange(['telegram', 'botToken'], e.target.value) + } + fullWidth + type="password" + helperText="Your Telegram bot token from @BotFather" + /> + + ) => + handleSettingChange(['telegram', 'chatId'], e.target.value) + } + fullWidth + helperText="Your Telegram chat ID (message @userinfobot to get it)" + /> + + ) => { + const value = parseInt(e.target.value); + if (!isNaN(value) && value > 0) { + handleSettingChange(['telegram', 'pollingInterval'], value); + } + }} + type="number" + inputProps={{ min: 1 }} + fullWidth + helperText="How often to check for responses (in seconds)" + /> + + )} + + + ); }; \ No newline at end of file diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index 2d9fda05179..3e81b68d901 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -69,6 +69,7 @@ export interface ExtensionStateContextType extends ExtensionState { handleInputChange: (field: keyof ApiConfiguration) => (event: any) => void customModes: ModeConfig[] setCustomModes: (value: ModeConfig[]) => void + setNotificationSettings: (settings: NonNullable) => void } export const ExtensionStateContext = createContext(undefined) @@ -101,6 +102,14 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode experimentalDiffStrategy: false, autoApprovalEnabled: false, customModes: [], + notificationSettings: { + telegram: { + enabled: false, + botToken: '', + chatId: '', + pollingInterval: 30 + } + } }) const [didHydrateState, setDidHydrateState] = useState(false) @@ -126,8 +135,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode setState((currentState) => { vscode.postMessage({ type: "upsertApiConfiguration", - text: currentState.currentApiConfigName, - apiConfiguration: apiConfig, + configuration: apiConfig, }) return currentState // No state update needed }) @@ -138,8 +146,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode setState((currentState) => { vscode.postMessage({ type: "upsertApiConfiguration", - text: currentState.currentApiConfigName, - apiConfiguration: { ...currentState.apiConfiguration, [field]: event.target.value }, + configuration: { ...currentState.apiConfiguration, [field]: event.target.value }, }) return currentState // No state update needed }) @@ -176,7 +183,6 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode case "partialMessage": { const partialMessage = message.partialMessage! setState((prevState) => { - // worth noting it will never be possible for a more up-to-date message to be sent here or in normal messages post since the presentAssistantContent function uses lock const lastIndex = findLastIndex(prevState.clineMessages, (msg) => msg.ts === partialMessage.ts) if (lastIndex !== -1) { const newClineMessages = [...prevState.clineMessages] @@ -190,7 +196,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode case "glamaModels": { const updatedModels = message.glamaModels ?? {} setGlamaModels({ - [glamaDefaultModelId]: glamaDefaultModelInfo, // in case the extension sent a model list without the default model + [glamaDefaultModelId]: glamaDefaultModelInfo, ...updatedModels, }) break @@ -198,7 +204,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode case "openRouterModels": { const updatedModels = message.openRouterModels ?? {} setOpenRouterModels({ - [openRouterDefaultModelId]: openRouterDefaultModelInfo, // in case the extension sent a model list without the default model + [openRouterDefaultModelId]: openRouterDefaultModelInfo, ...updatedModels, }) break @@ -216,6 +222,13 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode setListApiConfigMeta(message.listApiConfig ?? []) break } + case "notificationSettings": { + setState((prevState) => ({ + ...prevState, + notificationSettings: message.settings + })) + break + } } }, [setListApiConfigMeta], @@ -282,6 +295,13 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode setAutoApprovalEnabled: (value) => setState((prevState) => ({ ...prevState, autoApprovalEnabled: value })), handleInputChange, setCustomModes: (value) => setState((prevState) => ({ ...prevState, customModes: value })), + setNotificationSettings: (value) => { + setState((prevState) => ({ ...prevState, notificationSettings: value })) + vscode.postMessage({ + type: "updateNotificationSettings", + settings: value + }) + } } return {children} From 2e3d5a52cade2fa1b784348c6f3170b13b979f59 Mon Sep 17 00:00:00 2001 From: Wesley Harding <2081303+wesley-harding@users.noreply.github.com> Date: Sun, 26 Jan 2025 12:56:27 -0500 Subject: [PATCH 3/4] Supposedly "finished" product --- src/core/Cline.ts | 58 ++++ src/core/webview/ClineProvider.ts | 229 ++++++++++----- .../notifications/NotificationManager.ts | 130 +++------ .../providers/telegramProvider.ts | 266 ++++++++++------- src/services/notifications/types.ts | 60 ++-- src/shared/ExtensionMessage.ts | 2 + src/shared/WebviewMessage.ts | 274 ++++++++++++++++-- .../settings/NotificationSettings.tsx | 147 ++++------ .../src/components/settings/SettingsView.tsx | 75 +++-- 9 files changed, 817 insertions(+), 424 deletions(-) diff --git a/src/core/Cline.ts b/src/core/Cline.ts index c3417c24f3d..46fc498b3e2 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -3,6 +3,7 @@ import cloneDeep from "clone-deep" import { DiffStrategy, getDiffStrategy, UnifiedDiffStrategy } from "./diff/DiffStrategy" import { validateToolUse, isToolAllowedForMode, ToolName } from "./mode-validator" import { NotificationManager } from "../services/notifications/NotificationManager" +import { logger } from "../utils/logger" import delay from "delay" import fs from "fs/promises" import os from "os" @@ -128,6 +129,21 @@ export class Cline { this.terminalManager = new TerminalManager() this.urlContentFetcher = new UrlContentFetcher(provider.context) this.browserSession = new BrowserSession(provider.context) + this.notificationManager = NotificationManager.getInstance() + + // Initialize notification manager with settings + provider.getState().then(state => { + if (state.notificationSettings?.telegram?.enabled) { + this.notificationManager.initialize({ + telegram: state.notificationSettings.telegram + }).catch(error => { + logger.error('Failed to initialize notification manager:', error); + }) + } + }).catch(error => { + logger.error('Failed to get notification settings:', error); + }) + this.customInstructions = customInstructions this.diffEnabled = enableDiff ?? false this.fuzzyMatchThreshold = fuzzyMatchThreshold ?? 1.0 @@ -268,6 +284,45 @@ export class Cline { if (this.abort) { throw new Error("Roo Code instance aborted") } + + // Extract metadata for notifications + const metadata = (() => { + const lastMessage = this.clineMessages.at(-1); + if (!lastMessage) return undefined; + + if (lastMessage.type === 'say' && lastMessage.say === 'tool') { + const toolInfo = JSON.parse(lastMessage.text || '{}'); + return { + toolName: toolInfo.tool, + path: toolInfo.path, + command: toolInfo.command + }; + } + return undefined; + })(); + + // Send notification if applicable and not partial + if (!partial && text) { + try { + const notificationResponse = await this.notificationManager.notifyFromClineAsk(type, text, metadata); + + // Handle notification response + switch (notificationResponse.type) { + case 'approve': + return { response: 'yesButtonClicked' }; + case 'deny': + return { response: 'noButtonClicked' }; + case 'text': + return { + response: 'messageResponse', + text: notificationResponse.text + }; + } + } catch (error) { + logger.error('Failed to send notification:', error); + // Fall through to normal webview handling + } + } let askTs: number if (partial !== undefined) { const lastMessage = this.clineMessages.at(-1) @@ -711,6 +766,9 @@ export class Cline { this.terminalManager.disposeAll() this.urlContentFetcher.closeBrowser() this.browserSession.closeBrowser() + this.notificationManager.dispose().catch(error => { + logger.error('Failed to dispose notification manager:', error); + }) } // Tools diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 45368e0ffc8..25a24a36574 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -41,6 +41,8 @@ import { getCommitInfo, searchCommits, getWorkingState } from "../../utils/git" import { ConfigManager } from "../config/ConfigManager" import { CustomModesManager } from "../config/CustomModesManager" import { CustomSupportPrompts, supportPrompt } from "../../shared/support-prompt" +import { TelegramProvider } from "../../services/notifications/providers/telegramProvider" +import { NotificationProvider } from "../../services/notifications/types" import { ACTION_NAMES } from "../CodeActionProvider" @@ -120,6 +122,16 @@ type GlobalStateKey = | "experimentalDiffStrategy" | "autoApprovalEnabled" | "customModes" // Array of custom modes + | "notificationSettings" // Notification settings including Telegram configuration + +interface NotificationSettings { + telegram?: { + enabled: boolean; + botToken: string; + chatId: string; + pollingInterval: number; + }; +} export const GlobalFileNames = { apiConversationHistory: "api_conversation_history.json", @@ -141,6 +153,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { private latestAnnouncementId = "jan-21-2025-custom-modes" // update to some unique identifier when we add a new announcement configManager: ConfigManager customModesManager: CustomModesManager + private notificationProvider?: NotificationProvider constructor( readonly context: vscode.ExtensionContext, @@ -180,6 +193,10 @@ export class ClineProvider implements vscode.WebviewViewProvider { this.mcpHub?.dispose() this.mcpHub = undefined this.customModesManager?.dispose() + if (this.notificationProvider) { + await this.notificationProvider.dispose() + this.notificationProvider = undefined + } this.outputChannel.appendLine("Disposed all disposables") ClineProvider.activeInstances.delete(this) } @@ -222,9 +239,23 @@ export class ClineProvider implements vscode.WebviewViewProvider { this.outputChannel.appendLine("Resolving webview view") this.view = webviewView - // Initialize sound enabled state - this.getState().then(({ soundEnabled }) => { + // Initialize sound enabled state and notification provider + this.getState().then(({ soundEnabled, notificationSettings }) => { setSoundEnabled(soundEnabled ?? false) + + // Initialize notification provider if enabled + if (notificationSettings?.telegram?.enabled) { + const provider = new TelegramProvider({ + botToken: notificationSettings.telegram.botToken, + chatId: notificationSettings.telegram.chatId, + pollingInterval: notificationSettings.telegram.pollingInterval + }); + provider.initialize().then(() => { + this.notificationProvider = provider; + }).catch(error => { + console.error('Failed to initialize notification provider:', error); + }); + } }) webviewView.webview.options = { @@ -559,8 +590,8 @@ export class ClineProvider implements vscode.WebviewViewProvider { await this.initClineWithTask(message.text, message.images) break case "apiConfiguration": - if (message.apiConfiguration) { - await this.updateApiConfiguration(message.apiConfiguration) + if (message.configuration) { + await this.updateApiConfiguration(message.configuration) } await this.postStateToWebview() break @@ -1063,100 +1094,90 @@ export class ClineProvider implements vscode.WebviewViewProvider { break } case "upsertApiConfiguration": - if (message.text && message.apiConfiguration) { - try { - await this.configManager.saveConfig(message.text, message.apiConfiguration) - let listApiConfig = await this.configManager.listConfig() + try { + await this.configManager.saveConfig(message.configuration.name, message.configuration) + let listApiConfig = await this.configManager.listConfig() - await Promise.all([ - this.updateGlobalState("listApiConfigMeta", listApiConfig), - this.updateApiConfiguration(message.apiConfiguration), - this.updateGlobalState("currentApiConfigName", message.text), - ]) + await Promise.all([ + this.updateGlobalState("listApiConfigMeta", listApiConfig), + this.updateApiConfiguration(message.configuration), + this.updateGlobalState("currentApiConfigName", message.configuration.name), + ]) - await this.postStateToWebview() - } catch (error) { - console.error("Error create new api configuration:", error) - vscode.window.showErrorMessage("Failed to create api configuration") - } + await this.postStateToWebview() + } catch (error) { + console.error("Error create new api configuration:", error) + vscode.window.showErrorMessage("Failed to create api configuration") } break case "renameApiConfiguration": - if (message.values && message.apiConfiguration) { - try { - const { oldName, newName } = message.values - - await this.configManager.saveConfig(newName, message.apiConfiguration) - await this.configManager.deleteConfig(oldName) + try { + await this.configManager.saveConfig(message.newName, message.configuration) + await this.configManager.deleteConfig(message.oldName) - let listApiConfig = await this.configManager.listConfig() - const config = listApiConfig?.find((c) => c.name === newName) + let listApiConfig = await this.configManager.listConfig() + const config = listApiConfig?.find((c) => c.name === message.newName) - // Update listApiConfigMeta first to ensure UI has latest data - await this.updateGlobalState("listApiConfigMeta", listApiConfig) + // Update listApiConfigMeta first to ensure UI has latest data + await this.updateGlobalState("listApiConfigMeta", listApiConfig) - await Promise.all([this.updateGlobalState("currentApiConfigName", newName)]) + await Promise.all([this.updateGlobalState("currentApiConfigName", message.newName)]) - await this.postStateToWebview() - } catch (error) { - console.error("Error create new api configuration:", error) - vscode.window.showErrorMessage("Failed to create api configuration") - } + await this.postStateToWebview() + } catch (error) { + console.error("Error create new api configuration:", error) + vscode.window.showErrorMessage("Failed to create api configuration") } break case "loadApiConfiguration": - if (message.text) { - try { - const apiConfig = await this.configManager.loadConfig(message.text) - const listApiConfig = await this.configManager.listConfig() + try { + const apiConfig = await this.configManager.loadConfig(message.name) + const listApiConfig = await this.configManager.listConfig() - await Promise.all([ - this.updateGlobalState("listApiConfigMeta", listApiConfig), - this.updateGlobalState("currentApiConfigName", message.text), - this.updateApiConfiguration(apiConfig), - ]) + await Promise.all([ + this.updateGlobalState("listApiConfigMeta", listApiConfig), + this.updateGlobalState("currentApiConfigName", message.name), + this.updateApiConfiguration(apiConfig), + ]) - await this.postStateToWebview() - } catch (error) { - console.error("Error load api configuration:", error) - vscode.window.showErrorMessage("Failed to load api configuration") - } + await this.postStateToWebview() + } catch (error) { + console.error("Error load api configuration:", error) + vscode.window.showErrorMessage("Failed to load api configuration") } break case "deleteApiConfiguration": - if (message.text) { - const answer = await vscode.window.showInformationMessage( - "Are you sure you want to delete this configuration profile?", - { modal: true }, - "Yes", - ) + const answer = await vscode.window.showInformationMessage( + "Are you sure you want to delete this configuration profile?", + { modal: true }, + "Yes", + ) - if (answer !== "Yes") { - break - } + if (answer !== "Yes") { + break + } - try { - await this.configManager.deleteConfig(message.text) - const listApiConfig = await this.configManager.listConfig() - - // Update listApiConfigMeta first to ensure UI has latest data - await this.updateGlobalState("listApiConfigMeta", listApiConfig) - - // If this was the current config, switch to first available - let currentApiConfigName = await this.getGlobalState("currentApiConfigName") - if (message.text === currentApiConfigName && listApiConfig?.[0]?.name) { - const apiConfig = await this.configManager.loadConfig(listApiConfig[0].name) - await Promise.all([ - this.updateGlobalState("currentApiConfigName", listApiConfig[0].name), - this.updateApiConfiguration(apiConfig), - ]) - } + try { + await this.configManager.deleteConfig(message.name) + const listApiConfig = await this.configManager.listConfig() - await this.postStateToWebview() - } catch (error) { - console.error("Error delete api configuration:", error) - vscode.window.showErrorMessage("Failed to delete api configuration") + // Update listApiConfigMeta first to ensure UI has latest data + await this.updateGlobalState("listApiConfigMeta", listApiConfig) + + // If this was the current config, switch to first available + let currentApiConfigName = await this.getGlobalState("currentApiConfigName") + if (message.name === currentApiConfigName && listApiConfig?.[0]?.name) { + const apiConfig = await this.configManager.loadConfig(listApiConfig[0].name) + await Promise.all([ + this.updateGlobalState("currentApiConfigName", listApiConfig[0].name), + this.updateApiConfiguration(apiConfig), + ]) } + + await this.postStateToWebview() + } catch (error) { + console.error("Error delete api configuration:", error) + vscode.window.showErrorMessage("Failed to delete api configuration") } break case "getListApiConfiguration": @@ -1177,6 +1198,57 @@ export class ClineProvider implements vscode.WebviewViewProvider { } await this.postStateToWebview() break + case "updateNotificationSettings": + await this.updateGlobalState("notificationSettings", message.settings) + // Initialize or update notification provider + if (message.settings?.telegram?.enabled) { + const provider = new TelegramProvider({ + botToken: message.settings.telegram.botToken, + chatId: message.settings.telegram.chatId, + pollingInterval: message.settings.telegram.pollingInterval + }); + await provider.initialize(); + // Set up response handler + provider.onResponse(async (response) => { + if (this.cline) { + await this.cline.handleWebviewAskResponse( + 'notification_response', + response.type === 'text' ? response.text : response.type, + [`notificationId:${response.requestId}`] + ); + } + }); + // Store provider reference for cleanup + if (this.notificationProvider) { + await this.notificationProvider.dispose(); + } + this.notificationProvider = provider; + } else if (this.notificationProvider) { + await this.notificationProvider.dispose(); + this.notificationProvider = undefined; + } + await this.postStateToWebview() + break + case "sendNotification": + if (this.notificationProvider && message.notificationType && message.text) { + try { + await this.notificationProvider.sendNotification({ + type: message.notificationType, + message: message.text, + requestId: message.requestId || 'pending', + metadata: message.metadata + }); + // Post success message + await this.postMessageToWebview({ + type: "notification_sent", + text: "Notification sent successfully" + }); + } catch (error) { + console.error("Failed to send notification:", error); + vscode.window.showErrorMessage("Failed to send notification"); + } + } + break case "updateCustomMode": if (message.modeConfig) { await this.customModesManager.updateCustomMode(message.modeConfig.slug, message.modeConfig) @@ -1991,6 +2063,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { experimentalDiffStrategy, autoApprovalEnabled, customModes, + notificationSettings, ] = await Promise.all([ this.getGlobalState("apiProvider") as Promise, this.getGlobalState("apiModelId") as Promise, @@ -2060,6 +2133,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { this.getGlobalState("experimentalDiffStrategy") as Promise, this.getGlobalState("autoApprovalEnabled") as Promise, this.customModesManager.getCustomModes(), + this.getGlobalState("notificationSettings") as Promise, ]) let apiProvider: ApiProvider @@ -2175,6 +2249,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { experimentalDiffStrategy: experimentalDiffStrategy ?? false, autoApprovalEnabled: autoApprovalEnabled ?? false, customModes, + notificationSettings, } } diff --git a/src/services/notifications/NotificationManager.ts b/src/services/notifications/NotificationManager.ts index edb0f95d83d..044a9a7adeb 100644 --- a/src/services/notifications/NotificationManager.ts +++ b/src/services/notifications/NotificationManager.ts @@ -1,10 +1,10 @@ import { NotificationProvider, NotificationRequest, NotificationResponse, NotificationType } from './types'; import { TelegramProvider } from './providers/telegramProvider'; -import { logger } from '../../utils/logger'; import { ClineAsk } from '../../shared/ExtensionMessage'; import { mapClineAskToNotificationType } from './types'; +import { logger } from '../../utils/logger'; -export interface NotificationManagerConfig { +interface NotificationSettings { telegram?: { enabled: boolean; botToken: string; @@ -15,124 +15,90 @@ export interface NotificationManagerConfig { export class NotificationManager { private static instance: NotificationManager; - private providers: Map; - private config: NotificationManagerConfig; - private pendingRequests: Map void>; + private provider?: NotificationProvider; + private pendingRequests: Map void> = new Map(); - private constructor() { - this.providers = new Map(); - this.config = {}; - this.pendingRequests = new Map(); - } + private constructor() {} - static getInstance(): NotificationManager { + public static getInstance(): NotificationManager { if (!NotificationManager.instance) { NotificationManager.instance = new NotificationManager(); } return NotificationManager.instance; } - async initialize(config: NotificationManagerConfig): Promise { - this.config = config; - - // Clear existing providers - for (const provider of this.providers.values()) { - await provider.dispose(); + public async initialize(settings: NotificationSettings): Promise { + // Clean up existing provider if any + if (this.provider) { + await this.provider.dispose(); + this.provider = undefined; } - this.providers.clear(); - // Initialize Telegram provider if enabled - if (config.telegram?.enabled) { + // Initialize new provider if enabled + if (settings.telegram?.enabled) { try { - const telegramProvider = new TelegramProvider({ - botToken: config.telegram.botToken, - chatId: config.telegram.chatId, - pollingInterval: config.telegram.pollingInterval + const provider = new TelegramProvider({ + botToken: settings.telegram.botToken, + chatId: settings.telegram.chatId, + pollingInterval: settings.telegram.pollingInterval }); - await telegramProvider.initialize(); - - // Set up response handling - telegramProvider.onResponse((response: NotificationResponse) => { - const handler = this.pendingRequests.get(response.requestId); - if (handler) { - handler(response); + // Set up response handler + provider.onResponse((response) => { + const resolver = this.pendingRequests.get(response.requestId); + if (resolver) { + resolver(response); this.pendingRequests.delete(response.requestId); } }); - this.providers.set('telegram', telegramProvider); - logger.info('Telegram notification provider initialized'); + await provider.initialize(); + this.provider = provider; } catch (error) { - logger.error('Failed to initialize Telegram provider:', error); + logger.error('Failed to initialize notification provider:', error); throw error; } } } - async notify( - type: NotificationType, - message: string, + public async notifyFromClineAsk( + askType: ClineAsk, + text: string, metadata?: { toolName?: string; path?: string; command?: string; } ): Promise { - const requestId = crypto.randomUUID(); - const request: NotificationRequest = { - type, - message, - requestId, - metadata - }; - - // Send to all active providers - const errors: Error[] = []; - for (const [name, provider] of this.providers.entries()) { - try { - await provider.sendNotification(request); - logger.info(`Notification sent via ${name} provider`); - } catch (error) { - logger.error(`Failed to send notification via ${name} provider:`, error); - errors.push(error as Error); - } + if (!this.provider) { + throw new Error('No notification provider initialized'); } - // If all providers failed, throw an error - if (errors.length === this.providers.size) { - throw new Error('All notification providers failed'); - } + const notificationType = mapClineAskToNotificationType(askType); + const requestId = Math.random().toString(36).substring(7); - // Wait for response - return new Promise((resolve) => { + // Create a promise that will resolve when we get a response + const responsePromise = new Promise((resolve) => { this.pendingRequests.set(requestId, resolve); }); - } - async notifyFromClineAsk( - askType: ClineAsk, - message: string, - metadata?: { - toolName?: string; - path?: string; - command?: string; - } - ): Promise { - const notificationType = mapClineAskToNotificationType(askType); - return this.notify(notificationType, message, metadata); + // Send the notification + await this.provider.sendNotification({ + type: notificationType, + message: text, + requestId, + metadata + }); + + // Wait for response + return responsePromise; } - async dispose(): Promise { - // Dispose all providers - for (const provider of this.providers.values()) { - await provider.dispose(); + public async dispose(): Promise { + if (this.provider) { + await this.provider.dispose(); + this.provider = undefined; } - this.providers.clear(); this.pendingRequests.clear(); } - - getConfig(): NotificationManagerConfig { - return this.config; - } } \ No newline at end of file diff --git a/src/services/notifications/providers/telegramProvider.ts b/src/services/notifications/providers/telegramProvider.ts index 2ae9b64ba16..83e8c37764f 100644 --- a/src/services/notifications/providers/telegramProvider.ts +++ b/src/services/notifications/providers/telegramProvider.ts @@ -1,7 +1,9 @@ import { NotificationProvider, NotificationRequest, NotificationResponse } from '../types'; import { logger } from '../../../utils/logger'; -import axios from 'axios'; import { spawn } from 'child_process'; +import * as os from 'os'; +import * as fs from 'fs'; +import * as path from 'path'; interface TelegramConfig { botToken: string; @@ -11,136 +13,206 @@ interface TelegramConfig { export class TelegramProvider implements NotificationProvider { private config: TelegramConfig; - private ngrokProcess?: ReturnType; - private pollingInterval?: NodeJS.Timeout; private responseCallback?: (response: NotificationResponse) => void; - private requestMap: Map; + private pollingProcess?: ReturnType; + private ngrokProcess?: ReturnType; + private webhookUrl?: string; constructor(config: TelegramConfig) { this.config = config; - this.requestMap = new Map(); } async initialize(): Promise { - // Start ngrok for local tunneling - this.ngrokProcess = spawn('ngrok', ['http', '3000']); + // Try to set up ngrok for webhooks + try { + this.ngrokProcess = spawn('ngrok', ['http', '3000']); + + // Wait for ngrok to start and get the public URL + await new Promise((resolve, reject) => { + let output = ''; + this.ngrokProcess?.stdout?.on('data', (data) => { + output += data.toString(); + const match = output.match(/https:\/\/[^\.]+\.ngrok\.io/); + if (match) { + this.webhookUrl = match[0] + '/telegram-webhook'; + resolve(); + } + }); + + // Timeout after 10 seconds + setTimeout(() => reject(new Error('Ngrok startup timeout')), 10000); + }); + + // Set up webhook + await fetch(`https://api.telegram.org/bot${this.config.botToken}/setWebhook`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ url: this.webhookUrl }) + }); + + logger.info('Telegram webhook set up successfully'); + } catch (error) { + logger.warn('Failed to set up Telegram webhook, falling back to polling:', error); + this.startPolling(); + } + } + + private startPolling(): void { + // Create a simple Python script for polling + const script = ` +import os +import time +import json +import requests +from datetime import datetime, timedelta + +BOT_TOKEN = '${this.config.botToken}' +CHAT_ID = '${this.config.chatId}' +POLL_INTERVAL = ${this.config.pollingInterval} + +last_update_id = 0 + +while True: + try: + response = requests.get( + f'https://api.telegram.org/bot{BOT_TOKEN}/getUpdates', + params={'offset': last_update_id + 1, 'timeout': 30} + ) + updates = response.json().get('result', []) - this.ngrokProcess.stdout?.on('data', (data) => { - const output = data.toString(); - const publicUrlMatch = output.match(/https:\/\/.*\.ngrok\.io/); - if (publicUrlMatch) { - this.setWebhook(publicUrlMatch[0]); - } - }); + for update in updates: + last_update_id = update['update_id'] + message = update.get('message', {}) + + if str(message.get('chat', {}).get('id')) != CHAT_ID: + continue + + text = message.get('text', '').lower() + + # Look for response markers in the message + if text == 'approve': + print(json.dumps({ + 'type': 'approve', + 'requestId': 'pending' # Will be matched by message proximity + })) + elif text == 'deny': + print(json.dumps({ + 'type': 'deny', + 'requestId': 'pending' + })) + else: + print(json.dumps({ + 'type': 'text', + 'text': text, + 'requestId': 'pending' + })) + + except Exception as e: + print(f"Error: {str(e)}", file=sys.stderr) + time.sleep(POLL_INTERVAL) + continue - // Start polling for responses - this.pollingInterval = setInterval(async () => { + time.sleep(POLL_INTERVAL) +`; + + // Save script to temporary file + const fs = require('fs'); + const path = require('path'); + const scriptPath = path.join(os.tmpdir(), 'telegram-poll.py'); + fs.writeFileSync(scriptPath, script); + + // Start polling process + this.pollingProcess = spawn('python3', [scriptPath]); + + // Handle responses + this.pollingProcess.stdout?.on('data', (data) => { try { - const response = await axios.get( - `https://api.telegram.org/bot${this.config.botToken}/getUpdates`, - { params: { offset: -1 } } - ); - - response.data.result?.forEach((update: any) => { - if (update.message?.text) { - this.handleTelegramResponse(update.message.text); - } - }); + const response = JSON.parse(data.toString()); + if (this.responseCallback) { + this.responseCallback(response); + } } catch (error) { - logger.error('Telegram polling error:', error); + logger.error('Error parsing polling response:', error); } - }, this.config.pollingInterval * 1000); + }); + + this.pollingProcess.stderr?.on('data', (data) => { + logger.error('Polling error:', data.toString()); + }); } async sendNotification(request: NotificationRequest): Promise { + const message = this.formatMessage(request); + try { - // Store request for later matching with response - this.requestMap.set(request.requestId, request); - - // Format message based on notification type - let message = `*${request.type.toUpperCase()}*\n\n${request.message}`; - - if (request.type === 'approval') { - message += '\n\nReply with `approve` or `deny`'; - } + const response = await fetch(`https://api.telegram.org/bot${this.config.botToken}/sendMessage`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + chat_id: this.config.chatId, + text: message, + parse_mode: 'Markdown' + }) + }); - if (request.metadata) { - message += '\n\n*Details:*'; - if (request.metadata.toolName) { - message += `\nTool: ${request.metadata.toolName}`; - } - if (request.metadata.path) { - message += `\nPath: ${request.metadata.path}`; - } - if (request.metadata.command) { - message += `\nCommand: ${request.metadata.command}`; - } + if (!response.ok) { + throw new Error(`Telegram API error: ${response.statusText}`); } - - await axios.post(`https://api.telegram.org/bot${this.config.botToken}/sendMessage`, { - chat_id: this.config.chatId, - text: message, - parse_mode: 'Markdown' - }); } catch (error) { - logger.error('Telegram notification failed:', error); + logger.error('Failed to send Telegram notification:', error); throw error; } } - onResponse(callback: (response: NotificationResponse) => void): void { - this.responseCallback = callback; - } - - private handleTelegramResponse(text: string): void { - if (!this.responseCallback) { - return; - } - - // Find the most recent request that hasn't been responded to - const [requestId, request] = Array.from(this.requestMap.entries())[0] || []; - if (!requestId || !request) { - return; - } - - let response: NotificationResponse; + private formatMessage(request: NotificationRequest): string { + let message = ''; + // Add context about what's being requested if (request.type === 'approval') { - const lowerText = text.toLowerCase().trim(); - if (lowerText === 'approve') { - response = { requestId, type: 'approve' }; - } else if (lowerText === 'deny') { - response = { requestId, type: 'deny' }; - } else { - return; // Invalid response for approval + message += '🔐 *Approval Required*\n\n'; + if (request.metadata?.toolName) { + message += `Tool: \`${request.metadata.toolName}\`\n`; } + if (request.metadata?.path) { + message += `Path: \`${request.metadata.path}\`\n`; + } + if (request.metadata?.command) { + message += `Command: \`${request.metadata.command}\`\n`; + } + message += '\nReply with `approve` or `deny`\n\n'; } else { - response = { requestId, type: 'text', text }; + message += '❓ *Question*\n\n'; + message += 'Reply with your answer\n\n'; } - // Remove the request from the map - this.requestMap.delete(requestId); - - // Send response through callback - this.responseCallback(response); + // Add the actual message + message += `${request.message}`; + + return message; } - private async setWebhook(ngrokUrl: string): Promise { - try { - await axios.post( - `https://api.telegram.org/bot${this.config.botToken}/setWebhook`, - { url: `${ngrokUrl}/webhook` } - ); - } catch (error) { - logger.error('Failed to set Telegram webhook:', error); - } + onResponse(callback: (response: NotificationResponse) => void): void { + this.responseCallback = callback; } async dispose(): Promise { - this.ngrokProcess?.kill(); - if (this.pollingInterval) { - clearInterval(this.pollingInterval); + // Clean up webhook if it was set + if (this.webhookUrl) { + try { + await fetch(`https://api.telegram.org/bot${this.config.botToken}/deleteWebhook`); + } catch (error) { + logger.error('Error cleaning up webhook:', error); + } + } + + // Kill polling process if it exists + if (this.pollingProcess) { + this.pollingProcess.kill(); + } + + // Kill ngrok process if it exists + if (this.ngrokProcess) { + this.ngrokProcess.kill(); } - this.requestMap.clear(); } } \ No newline at end of file diff --git a/src/services/notifications/types.ts b/src/services/notifications/types.ts index b4b944256cf..b3b1c7bc578 100644 --- a/src/services/notifications/types.ts +++ b/src/services/notifications/types.ts @@ -1,61 +1,41 @@ -import { ClineAsk } from "../../shared/ExtensionMessage" +import { ClineAsk } from '../../shared/ExtensionMessage'; -export type NotificationType = 'approval' | 'question' | 'completion' +export type NotificationType = 'approval' | 'question'; export interface NotificationRequest { - type: NotificationType - message: string - requestId: string + type: NotificationType; + message: string; + requestId: string; metadata?: { - toolName?: string - path?: string - command?: string - } + toolName?: string; + path?: string; + command?: string; + }; } export interface NotificationResponse { - requestId: string - type: 'approve' | 'deny' | 'text' - text?: string + requestId: string; + type: 'approve' | 'deny' | 'text'; + text?: string; } export interface NotificationProvider { - /** - * Initialize the notification provider with any necessary setup - */ - initialize(): Promise - - /** - * Send a notification through this provider - */ - sendNotification(request: NotificationRequest): Promise - - /** - * Register a callback to handle responses from this provider - */ - onResponse(callback: (response: NotificationResponse) => void): void - - /** - * Clean up any resources used by this provider - */ - dispose(): Promise -} - -export interface NotificationSettings { - enabled: boolean - [key: string]: any + initialize(): Promise; + sendNotification(request: NotificationRequest): Promise; + onResponse(callback: (response: NotificationResponse) => void): void; + dispose(): Promise; } export function mapClineAskToNotificationType(askType: ClineAsk): NotificationType { switch (askType) { - case 'tool': case 'command': + case 'tool': case 'browser_action_launch': case 'use_mcp_server': - return 'approval' + return 'approval'; case 'followup': - return 'question' + return 'question'; default: - return 'approval' + return 'question'; } } \ No newline at end of file diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index f3b47218f4e..d7bd35b8aa0 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -42,6 +42,8 @@ export interface ExtensionMessage { | "updateCustomMode" | "deleteCustomMode" | "notificationSettings" + | "updateNotificationSettings" + | "notification_sent" text?: string action?: | "chatButtonClicked" diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 428ddf8ac2a..d11b2ee79e7 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -1,12 +1,18 @@ import { ApiConfiguration } from './api'; import { HistoryItem } from './HistoryItem'; +import { Mode, ModeConfig } from './modes'; +import { ExtensionState } from './ExtensionMessage'; -export type ClineAskResponse = 'yesButtonClicked' | 'noButtonClicked' | 'messageResponse'; +export type ClineAskResponse = 'yesButtonClicked' | 'noButtonClicked' | 'messageResponse' | 'notification_response'; + +export type ApiConfigurationWithName = ApiConfiguration & { + name: string; +}; export type WebviewMessage = | { type: 'apiConfiguration'; - configuration: ApiConfiguration; + configuration: ApiConfigurationWithName; } | { type: 'currentApiConfigName'; @@ -14,7 +20,7 @@ export type WebviewMessage = } | { type: 'upsertApiConfiguration'; - configuration: ApiConfiguration; + configuration: ApiConfigurationWithName; } | { type: 'deleteApiConfiguration'; @@ -28,6 +34,7 @@ export type WebviewMessage = type: 'renameApiConfiguration'; oldName: string; newName: string; + configuration: ApiConfigurationWithName; } | { type: 'getListApiConfiguration'; @@ -37,26 +44,23 @@ export type WebviewMessage = } | { type: 'updateNotificationSettings'; - settings: { - telegram?: { - enabled: boolean; - botToken: string; - chatId: string; - pollingInterval: number; - }; - }; + settings: NonNullable; } | { type: 'notificationSettings'; - settings: { - telegram?: { - enabled: boolean; - botToken: string; - chatId: string; - pollingInterval: number; - }; + settings: NonNullable; + } + | { + type: 'sendNotification'; + notificationType: 'approval' | 'question'; + text: string; + requestId?: string; + metadata?: { + toolName?: string; + path?: string; + command?: string; }; - } + } | { type: 'listApiConfiguration'; configurations: ApiConfiguration[]; @@ -86,4 +90,236 @@ export type WebviewMessage = } | { type: 'webviewDidLaunch'; + } + | { + type: 'newTask'; + text?: string; + images?: string[]; + } + | { + type: 'customInstructions'; + text?: string; + } + | { + type: 'alwaysAllowReadOnly'; + bool?: boolean; + } + | { + type: 'alwaysAllowWrite'; + bool?: boolean; + } + | { + type: 'alwaysAllowExecute'; + bool?: boolean; + } + | { + type: 'alwaysAllowBrowser'; + bool?: boolean; + } + | { + type: 'alwaysAllowMcp'; + bool?: boolean; + } + | { + type: 'askResponse'; + askResponse?: ClineAskResponse; + text?: string; + images?: string[]; + } + | { + type: 'clearTask'; + } + | { + type: 'didShowAnnouncement'; + } + | { + type: 'selectImages'; + } + | { + type: 'exportCurrentTask'; + } + | { + type: 'showTaskWithId'; + text: string; + } + | { + type: 'deleteTaskWithId'; + text: string; + } + | { + type: 'exportTaskWithId'; + text: string; + } + | { + type: 'resetState'; + } + | { + type: 'requestOllamaModels'; + text?: string; + } + | { + type: 'requestLmStudioModels'; + text?: string; + } + | { + type: 'requestVsCodeLmModels'; + } + | { + type: 'refreshGlamaModels'; + } + | { + type: 'refreshOpenRouterModels'; + } + | { + type: 'refreshOpenAiModels'; + values?: { + baseUrl?: string; + apiKey?: string; + }; + } + | { + type: 'openImage'; + text: string; + } + | { + type: 'openFile'; + text: string; + values?: { + create?: boolean; + content?: string; + }; + } + | { + type: 'openMention'; + text?: string; + } + | { + type: 'cancelTask'; + } + | { + type: 'allowedCommands'; + commands: string[]; + } + | { + type: 'openMcpSettings'; + } + | { + type: 'restartMcpServer'; + text: string; + } + | { + type: 'toggleToolAlwaysAllow'; + serverName: string; + toolName: string; + alwaysAllow: boolean; + } + | { + type: 'toggleMcpServer'; + serverName: string; + disabled: boolean; + } + | { + type: 'mcpEnabled'; + bool?: boolean; + } + | { + type: 'playSound'; + audioType?: string; + } + | { + type: 'soundEnabled'; + bool?: boolean; + } + | { + type: 'soundVolume'; + value?: number; + } + | { + type: 'diffEnabled'; + bool?: boolean; + } + | { + type: 'browserViewportSize'; + text?: string; + } + | { + type: 'fuzzyMatchThreshold'; + value?: number; + } + | { + type: 'alwaysApproveResubmit'; + bool?: boolean; + } + | { + type: 'requestDelaySeconds'; + value?: number; + } + | { + type: 'preferredLanguage'; + text?: string; + } + | { + type: 'writeDelayMs'; + value?: number; + } + | { + type: 'terminalOutputLineLimit'; + value?: number; + } + | { + type: 'mode'; + text?: Mode; + } + | { + type: 'updateSupportPrompt'; + values?: Record; + } + | { + type: 'resetSupportPrompt'; + text?: string; + } + | { + type: 'updatePrompt'; + promptMode?: string; + customPrompt?: any; + } + | { + type: 'deleteMessage'; + value?: number; + } + | { + type: 'screenshotQuality'; + value?: number; + } + | { + type: 'enhancementApiConfigId'; + text?: string; + } + | { + type: 'autoApprovalEnabled'; + bool?: boolean; + } + | { + type: 'enhancePrompt'; + text?: string; + } + | { + type: 'getSystemPrompt'; + mode?: Mode; + } + | { + type: 'searchCommits'; + query?: string; + } + | { + type: 'experimentalDiffStrategy'; + bool?: boolean; + } + | { + type: 'updateCustomMode'; + modeConfig?: ModeConfig; + } + | { + type: 'deleteCustomMode'; + slug?: string; }; diff --git a/webview-ui/src/components/settings/NotificationSettings.tsx b/webview-ui/src/components/settings/NotificationSettings.tsx index 8828b002610..a1f9f0db2ab 100644 --- a/webview-ui/src/components/settings/NotificationSettings.tsx +++ b/webview-ui/src/components/settings/NotificationSettings.tsx @@ -1,6 +1,5 @@ -import React, { useEffect, useState } from 'react'; +import React from 'react'; import { TextField, Switch, Stack, Typography } from '@mui/material'; -import { vscode } from '../../utils/vscode'; interface TelegramSettings { enabled: boolean; @@ -13,36 +12,12 @@ interface NotificationSettings { telegram?: TelegramSettings; } -export const NotificationSettings: React.FC = () => { - const [settings, setSettings] = useState({ - telegram: { - enabled: false, - botToken: '', - chatId: '', - pollingInterval: 30 - } - }); - - useEffect(() => { - // Request current settings when component mounts - vscode.postMessage({ - type: 'getNotificationSettings' - }); - }, []); - - useEffect(() => { - // Handle settings updates from extension - const messageHandler = (event: MessageEvent) => { - const message = event.data; - if (message.type === 'notificationSettings') { - setSettings(message.settings); - } - }; - - window.addEventListener('message', messageHandler); - return () => window.removeEventListener('message', messageHandler); - }, []); +interface NotificationSettingsProps { + settings: NotificationSettings; + onChange: (settings: NotificationSettings) => void; +} +export const NotificationSettings: React.FC = ({ settings, onChange }) => { const handleSettingChange = ( path: string[], value: string | boolean | number @@ -61,71 +36,61 @@ export const NotificationSettings: React.FC = () => { // Set the value current[path[path.length - 1]] = value; - // Update local state - setSettings(newSettings); - - // Send update to extension - vscode.postMessage({ - type: 'updateNotificationSettings', - settings: newSettings - }); + // Call onChange prop with updated settings + onChange(newSettings); }; return ( - - Notification Settings + + Telegram - - Telegram - - ) => - handleSettingChange(['telegram', 'enabled'], e.target.checked) - } - inputProps={{ 'aria-label': 'Enable Telegram notifications' }} - /> - - {settings.telegram?.enabled && ( - <> - ) => - handleSettingChange(['telegram', 'botToken'], e.target.value) - } - fullWidth - type="password" - helperText="Your Telegram bot token from @BotFather" - /> - - ) => - handleSettingChange(['telegram', 'chatId'], e.target.value) + ) => + handleSettingChange(['telegram', 'enabled'], e.target.checked) + } + inputProps={{ 'aria-label': 'Enable Telegram notifications' }} + /> + + {settings.telegram?.enabled && ( + <> + ) => + handleSettingChange(['telegram', 'botToken'], e.target.value) + } + fullWidth + type="password" + helperText="Your Telegram bot token from @BotFather" + /> + + ) => + handleSettingChange(['telegram', 'chatId'], e.target.value) + } + fullWidth + helperText="Your Telegram chat ID (message @userinfobot to get it)" + /> + + ) => { + const value = parseInt(e.target.value); + if (!isNaN(value) && value > 0) { + handleSettingChange(['telegram', 'pollingInterval'], value); } - fullWidth - helperText="Your Telegram chat ID (message @userinfobot to get it)" - /> - - ) => { - const value = parseInt(e.target.value); - if (!isNaN(value) && value > 0) { - handleSettingChange(['telegram', 'pollingInterval'], value); - } - }} - type="number" - inputProps={{ min: 1 }} - fullWidth - helperText="How often to check for responses (in seconds)" - /> - - )} - + }} + type="number" + inputProps={{ min: 1 }} + fullWidth + helperText="How often to check for responses (in seconds)" + /> + + )} ); }; \ No newline at end of file diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index 81a929cc8d1..c4881478178 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -5,6 +5,7 @@ import { validateApiConfiguration, validateModelId } from "../../utils/validate" import { vscode } from "../../utils/vscode" import ApiOptions from "./ApiOptions" import ApiConfigManager from "./ApiConfigManager" +import { NotificationSettings } from "./NotificationSettings" type SettingsViewProps = { onDone: () => void @@ -53,6 +54,7 @@ const SettingsView = ({ onDone }: SettingsViewProps) => { listApiConfigMeta, experimentalDiffStrategy, setExperimentalDiffStrategy, + notificationSettings, } = useExtensionState() const [apiErrorMessage, setApiErrorMessage] = useState(undefined) const [modelIdErrorMessage, setModelIdErrorMessage] = useState(undefined) @@ -65,10 +67,13 @@ const SettingsView = ({ onDone }: SettingsViewProps) => { setApiErrorMessage(apiValidationResult) setModelIdErrorMessage(modelIdValidationResult) if (!apiValidationResult && !modelIdValidationResult) { - vscode.postMessage({ - type: "apiConfiguration", - apiConfiguration, - }) + if (apiConfiguration && currentApiConfigName) { + const configWithName = { ...apiConfiguration, name: currentApiConfigName }; + vscode.postMessage({ + type: "apiConfiguration", + configuration: configWithName, + }); + } vscode.postMessage({ type: "alwaysAllowReadOnly", bool: alwaysAllowReadOnly }) vscode.postMessage({ type: "alwaysAllowWrite", bool: alwaysAllowWrite }) vscode.postMessage({ type: "alwaysAllowExecute", bool: alwaysAllowExecute }) @@ -86,13 +91,27 @@ const SettingsView = ({ onDone }: SettingsViewProps) => { vscode.postMessage({ type: "mcpEnabled", bool: mcpEnabled }) vscode.postMessage({ type: "alwaysApproveResubmit", bool: alwaysApproveResubmit }) vscode.postMessage({ type: "requestDelaySeconds", value: requestDelaySeconds }) - vscode.postMessage({ type: "currentApiConfigName", text: currentApiConfigName }) + if (currentApiConfigName) { + vscode.postMessage({ type: "currentApiConfigName", name: currentApiConfigName }); + if (apiConfiguration) { + vscode.postMessage({ + type: "upsertApiConfiguration", + configuration: { ...apiConfiguration, name: currentApiConfigName }, + }); + } + } + vscode.postMessage({ type: "experimentalDiffStrategy", bool: experimentalDiffStrategy }) vscode.postMessage({ - type: "upsertApiConfiguration", - text: currentApiConfigName, - apiConfiguration, + type: "updateNotificationSettings", + settings: notificationSettings || { + telegram: { + enabled: false, + botToken: '', + chatId: '', + pollingInterval: 30 + } + } }) - vscode.postMessage({ type: "experimentalDiffStrategy", bool: experimentalDiffStrategy }) onDone() } } @@ -162,28 +181,30 @@ const SettingsView = ({ onDone }: SettingsViewProps) => { onSelectConfig={(configName: string) => { vscode.postMessage({ type: "loadApiConfiguration", - text: configName, + name: configName, }) }} onDeleteConfig={(configName: string) => { vscode.postMessage({ type: "deleteApiConfiguration", - text: configName, + name: configName, }) }} onRenameConfig={(oldName: string, newName: string) => { vscode.postMessage({ type: "renameApiConfiguration", - values: { oldName, newName }, - apiConfiguration, + oldName, + newName, + configuration: { ...apiConfiguration, name: newName }, }) }} onUpsertConfig={(configName: string) => { - vscode.postMessage({ - type: "upsertApiConfiguration", - text: configName, - apiConfiguration, - }) + if (apiConfiguration) { + vscode.postMessage({ + type: "upsertApiConfiguration", + configuration: { ...apiConfiguration, name: configName }, + }); + } }} /> @@ -532,6 +553,24 @@ const SettingsView = ({ onDone }: SettingsViewProps) => { )} +
+ { + vscode.postMessage({ + type: 'updateNotificationSettings', + settings: newSettings + }); + }} + /> +
From 0defd31e72561d073de529b8934321289f89e35e Mon Sep 17 00:00:00 2001 From: Wesley Harding <2081303+wesley-harding@users.noreply.github.com> Date: Sun, 26 Jan 2025 13:03:06 -0500 Subject: [PATCH 4/4] Supposedly "Passed" QA --- .../__tests__/NotificationManager.test.ts | 119 +++++++++++++ .../__tests__/telegramProvider.test.ts | 165 ++++++++++++++++++ .../providers/telegramProvider.ts | 21 ++- 3 files changed, 303 insertions(+), 2 deletions(-) create mode 100644 src/services/notifications/__tests__/NotificationManager.test.ts create mode 100644 src/services/notifications/__tests__/telegramProvider.test.ts diff --git a/src/services/notifications/__tests__/NotificationManager.test.ts b/src/services/notifications/__tests__/NotificationManager.test.ts new file mode 100644 index 00000000000..10236e0d87a --- /dev/null +++ b/src/services/notifications/__tests__/NotificationManager.test.ts @@ -0,0 +1,119 @@ +import { NotificationManager } from '../NotificationManager'; +import { TelegramProvider } from '../providers/telegramProvider'; +import { NotificationResponse } from '../types'; + +// Mock TelegramProvider +jest.mock('../providers/telegramProvider'); + +describe('NotificationManager', () => { + let notificationManager: NotificationManager; + + beforeEach(() => { + // Reset mocks + jest.clearAllMocks(); + // Get a fresh instance + notificationManager = NotificationManager.getInstance(); + }); + + afterEach(async () => { + await notificationManager.dispose(); + }); + + it('should be a singleton', () => { + const instance1 = NotificationManager.getInstance(); + const instance2 = NotificationManager.getInstance(); + expect(instance1).toBe(instance2); + }); + + it('should initialize telegram provider when enabled', async () => { + const settings = { + telegram: { + enabled: true, + botToken: 'test-token', + chatId: 'test-chat-id', + pollingInterval: 30 + } + }; + + await notificationManager.initialize(settings); + + expect(TelegramProvider).toHaveBeenCalledWith({ + botToken: 'test-token', + chatId: 'test-chat-id', + pollingInterval: 30 + }); + }); + + it('should not initialize provider when disabled', async () => { + const settings = { + telegram: { + enabled: false, + botToken: 'test-token', + chatId: 'test-chat-id', + pollingInterval: 30 + } + }; + + await notificationManager.initialize(settings); + + expect(TelegramProvider).not.toHaveBeenCalled(); + }); + + it('should send notifications and handle responses', async () => { + // Setup mock response + const mockResponse: NotificationResponse = { + type: 'approve', + requestId: 'test-id' + }; + + // Mock provider implementation + (TelegramProvider as jest.Mock).mockImplementation(() => ({ + initialize: jest.fn(), + sendNotification: jest.fn(), + onResponse: jest.fn(callback => callback(mockResponse)), + dispose: jest.fn() + })); + + // Initialize with settings + await notificationManager.initialize({ + telegram: { + enabled: true, + botToken: 'test-token', + chatId: 'test-chat-id', + pollingInterval: 30 + } + }); + + // Send notification and get response + const response = await notificationManager.notifyFromClineAsk( + 'command', + 'Test message', + { command: 'test command' } + ); + + expect(response).toEqual(mockResponse); + }); + + it('should clean up provider on dispose', async () => { + const mockDispose = jest.fn(); + (TelegramProvider as jest.Mock).mockImplementation(() => ({ + initialize: jest.fn(), + sendNotification: jest.fn(), + onResponse: jest.fn(), + dispose: mockDispose + })); + + await notificationManager.initialize({ + telegram: { + enabled: true, + botToken: 'test-token', + chatId: 'test-chat-id', + pollingInterval: 30 + } + }); + + await notificationManager.dispose(); + + expect(mockDispose).toHaveBeenCalled(); + }); +}); \ No newline at end of file diff --git a/src/services/notifications/__tests__/telegramProvider.test.ts b/src/services/notifications/__tests__/telegramProvider.test.ts new file mode 100644 index 00000000000..224acf80698 --- /dev/null +++ b/src/services/notifications/__tests__/telegramProvider.test.ts @@ -0,0 +1,165 @@ +import { TelegramProvider } from '../providers/telegramProvider'; +import { spawn } from 'child_process'; +import * as fs from 'fs'; +import fetch from 'node-fetch'; + +jest.mock('child_process'); +jest.mock('fs'); +jest.mock('node-fetch'); + +describe('TelegramProvider', () => { + const mockConfig = { + botToken: 'test-token', + chatId: 'test-chat-id', + pollingInterval: 30 + }; + + let provider: TelegramProvider; + + beforeEach(() => { + jest.clearAllMocks(); + provider = new TelegramProvider(mockConfig); + }); + + afterEach(async () => { + await provider.dispose(); + }); + + describe('initialize', () => { + it('should set up webhook when ngrok is available', async () => { + // Mock successful ngrok startup + const mockNgrok = { + stdout: { + on: jest.fn((event, callback) => { + if (event === 'data') { + callback('Forwarding https://test.ngrok.io -> localhost:3000'); + } + }) + }, + kill: jest.fn() + }; + (spawn as jest.Mock).mockReturnValue(mockNgrok); + + // Mock successful webhook setup + (fetch as unknown as jest.Mock).mockResolvedValue({ ok: true }); + + await provider.initialize(); + + // Verify ngrok was started + expect(spawn).toHaveBeenCalledWith('ngrok', ['http', '3000']); + + // Verify webhook was set + expect(fetch).toHaveBeenCalledWith( + 'https://api.telegram.org/bottest-token/setWebhook', + expect.any(Object) + ); + }); + + it('should fall back to polling when webhook setup fails', async () => { + // Mock ngrok failure + (spawn as jest.Mock).mockImplementation(() => { + throw new Error('ngrok not found'); + }); + + // Mock Python check + (spawn as jest.Mock).mockImplementationOnce((cmd, args) => ({ + on: (event: string, callback: (code: number) => void) => { + if (event === 'close') callback(0); + } + })); + + // Mock polling process + const mockPolling = { + stdout: { on: jest.fn() }, + stderr: { on: jest.fn() }, + kill: jest.fn() + }; + (spawn as jest.Mock).mockReturnValue(mockPolling); + + await provider.initialize(); + + // Verify Python dependencies were checked + expect(spawn).toHaveBeenCalledWith('python3', ['-c', 'import requests']); + + // Verify polling script was created + expect(fs.writeFileSync).toHaveBeenCalled(); + + // Verify polling was started + expect(spawn).toHaveBeenCalledWith('python3', expect.any(Array)); + }); + }); + + describe('sendNotification', () => { + it('should send formatted messages to Telegram API', async () => { + (fetch as unknown as jest.Mock).mockResolvedValue({ ok: true }); + + await provider.sendNotification({ + type: 'approval', + message: 'Test message', + requestId: 'test-id', + metadata: { + toolName: 'test-tool', + path: 'test/path', + command: 'test command' + } + }); + + expect(fetch).toHaveBeenCalledWith( + 'https://api.telegram.org/bottest-token/sendMessage', + expect.objectContaining({ + method: 'POST', + headers: { 'Content-Type': 'application/json' } + }) + ); + + // Verify message formatting + const callArgs = (fetch as unknown as jest.Mock).mock.calls[0][1]; + const body = JSON.parse(callArgs.body); + expect(body.text).toContain('🔐 *Approval Required*'); + expect(body.text).toContain('Tool: `test-tool`'); + expect(body.text).toContain('Path: `test/path`'); + expect(body.text).toContain('Command: `test command`'); + expect(body.text).toContain('Test message'); + }); + + it('should handle API errors', async () => { + (fetch as unknown as jest.Mock).mockResolvedValue({ + ok: false, + statusText: 'Bad Request' + }); + + await expect(provider.sendNotification({ + type: 'question', + message: 'Test message', + requestId: 'test-id' + })).rejects.toThrow('Telegram API error: Bad Request'); + }); + }); + + describe('dispose', () => { + it('should clean up all processes and webhook', async () => { + const mockProcesses = { + kill: jest.fn() + }; + (spawn as jest.Mock).mockReturnValue(mockProcesses); + + // Set up provider with webhook + (fetch as unknown as jest.Mock).mockResolvedValue({ ok: true }); + await provider.initialize(); + + // Reset fetch mock for dispose call + (fetch as unknown as jest.Mock).mockClear(); + + // Dispose + await provider.dispose(); + + // Verify webhook was deleted + expect(fetch).toHaveBeenCalledWith( + 'https://api.telegram.org/bottest-token/deleteWebhook' + ); + + // Verify processes were killed + expect(mockProcesses.kill).toHaveBeenCalled(); + }); + }); +}); \ No newline at end of file diff --git a/src/services/notifications/providers/telegramProvider.ts b/src/services/notifications/providers/telegramProvider.ts index 83e8c37764f..2e87b976bdb 100644 --- a/src/services/notifications/providers/telegramProvider.ts +++ b/src/services/notifications/providers/telegramProvider.ts @@ -53,11 +53,28 @@ export class TelegramProvider implements NotificationProvider { logger.info('Telegram webhook set up successfully'); } catch (error) { logger.warn('Failed to set up Telegram webhook, falling back to polling:', error); - this.startPolling(); + await this.startPolling(); } } - private startPolling(): void { + private async startPolling(): Promise { + // Check for Python3 and requests + try { + await new Promise((resolve, reject) => { + const check = spawn('python3', ['-c', 'import requests']); + check.on('close', (code) => { + if (code === 0) { + resolve(undefined); + } else { + reject(new Error('Python3 or requests library not found. Please install with: pip3 install requests')); + } + }); + }); + } catch (error) { + logger.error('Failed to start polling:', error); + throw error; + } + // Create a simple Python script for polling const script = ` import os