diff --git a/package-lock.json b/package-lock.json index 3223ea93..28bdd8a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -88,6 +88,9 @@ "auto-changelog": "^2.5.0", "autoprefixer": "^10.4.16", "concurrently": "^8.2.2", + "cross-env": "^10.1.0", + "electron": "^38.0.0", + "electron-builder": "^26.15.3", "eslint": "^9.39.3", "eslint-import-resolver-typescript": "^4.4.4", "eslint-plugin-boundaries": "^6.0.2", @@ -1118,6 +1121,600 @@ "node": ">=10" } }, + "node_modules/@electron/asar": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.4.1.tgz", + "integrity": "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^5.0.0", + "glob": "^7.1.6", + "minimatch": "^3.0.4" + }, + "bin": { + "asar": "bin/asar.js" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/@electron/asar/node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@electron/asar/node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@electron/asar/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@electron/asar/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@electron/fuses": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@electron/fuses/-/fuses-1.8.0.tgz", + "integrity": "sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.1", + "fs-extra": "^9.0.1", + "minimist": "^1.2.5" + }, + "bin": { + "electron-fuses": "dist/bin.js" + } + }, + "node_modules/@electron/fuses/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/fuses/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/fuses/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/get": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", + "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "global-agent": "^3.0.0" + } + }, + "node_modules/@electron/notarize": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.5.0.tgz", + "integrity": "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.1", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/notarize/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/notarize/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/notarize/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/osx-sign": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.3.tgz", + "integrity": "sha512-KZ8mhXvWv2rIEgMbWZ4y33bDHyUKMXnx4M0sTyPNK/vcB81ImdeY9Ggdqy0SWbMDgmbqyQ+phgejh6V3R2QuSg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "compare-version": "^0.1.2", + "debug": "^4.3.4", + "fs-extra": "^10.0.0", + "isbinaryfile": "^4.0.8", + "minimist": "^1.2.6", + "plist": "^3.0.5" + }, + "bin": { + "electron-osx-flat": "bin/electron-osx-flat.js", + "electron-osx-sign": "bin/electron-osx-sign.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@electron/osx-sign/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/@electron/osx-sign/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/osx-sign/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/rebuild": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-4.0.4.tgz", + "integrity": "sha512-Rzc39XPdk/+/wBG8MfwAHohXflep0ITUfulb6Rgz3R0NeSB1noE+E9/M/cb8ftCAiyDD9PPhLuuWgE1GaInbKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@malept/cross-spawn-promise": "^2.0.0", + "debug": "^4.1.1", + "node-abi": "^4.2.0", + "node-api-version": "^0.2.1", + "node-gyp": "^12.2.0", + "read-binary-file-arch": "^1.0.6" + }, + "bin": { + "electron-rebuild": "lib/cli.js" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@electron/rebuild/node_modules/abbrev": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", + "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@electron/rebuild/node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@electron/rebuild/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/@electron/rebuild/node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@electron/rebuild/node_modules/node-abi": { + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-4.31.0.tgz", + "integrity": "sha512-Erq5w/t3syw3s4sDsUaX4QttIdBPsGKTT1DTRsCkTonGggczhlDKm/wDX3o+HPJpQ41EjXCbcmXf0tgr5YZJXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.6.3" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@electron/rebuild/node_modules/node-gyp": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.4.0.tgz", + "integrity": "sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "graceful-fs": "^4.2.6", + "nopt": "^9.0.0", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "tar": "^7.5.4", + "tinyglobby": "^0.2.12", + "undici": "^6.25.0", + "which": "^6.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@electron/rebuild/node_modules/nopt": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", + "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^4.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@electron/rebuild/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@electron/rebuild/node_modules/semver": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/rebuild/node_modules/tar": { + "version": "7.5.16", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.16.tgz", + "integrity": "sha512-56adEpPMouktRlBLXiaYFFzZ/3+JXa8P9n7WbR+ibIjtviN55mEaOkiysCnPnWm+7kkui1Dn8J9l+g6zV8731w==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@electron/rebuild/node_modules/undici": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.26.0.tgz", + "integrity": "sha512-4yqz8a3n5HmGTlsbADNtr/dJlhkh/55Rq798G6ibiULcXbDtaLpTl1pvdqcbFfeoj3iSi52lePFM7h9H21cw/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/@electron/rebuild/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@electron/rebuild/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@electron/universal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.3.tgz", + "integrity": "sha512-Wn9sPYIVFRFl5HmwMJkARCCf7rqK/EurkfQ/rJZ14mHP3iYTjZSIOSVonEAnhWeAXwtw7zOekGRlc6yTtZ0t+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron/asar": "^3.3.1", + "@malept/cross-spawn-promise": "^2.0.0", + "debug": "^4.3.1", + "dir-compare": "^4.2.0", + "fs-extra": "^11.1.1", + "minimatch": "^9.0.3", + "plist": "^3.1.0" + }, + "engines": { + "node": ">=16.4" + } + }, + "node_modules/@electron/universal/node_modules/fs-extra": { + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.5.tgz", + "integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@electron/universal/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/universal/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/windows-sign": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@electron/windows-sign/-/windows-sign-1.2.2.tgz", + "integrity": "sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "dependencies": { + "cross-dirname": "^0.1.0", + "debug": "^4.3.4", + "fs-extra": "^11.1.1", + "minimist": "^1.2.8", + "postject": "^1.0.0-alpha.6" + }, + "bin": { + "electron-windows-sign": "bin/electron-windows-sign.js" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@electron/windows-sign/node_modules/fs-extra": { + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.5.tgz", + "integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@electron/windows-sign/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/windows-sign/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/@emnapi/core": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", @@ -1152,6 +1749,13 @@ "tslib": "^2.4.0" } }, + "node_modules/@epic-web/invariant": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz", + "integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==", + "dev": true, + "license": "MIT" + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.8", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", @@ -2467,6 +3071,19 @@ "node": ">=12" } }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.12", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", @@ -2591,6 +3208,84 @@ "@lezer/lr": "^1.0.0" } }, + "node_modules/@malept/cross-spawn-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", + "integrity": "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/@malept/flatpak-bundler": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", + "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.0", + "lodash": "^4.17.15", + "tmp-promise": "^3.0.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/@marijn/find-cluster-break": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", @@ -3024,6 +3719,19 @@ "@tybys/wasm-util": "^0.10.0" } }, + "node_modules/@noble/hashes": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.2.0.tgz", + "integrity": "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3482,6 +4190,58 @@ "node": ">=16" } }, + "node_modules/@peculiar/asn1-schema": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.8.0.tgz", + "integrity": "sha512-7YT0U/ze0tF2QOBbE15gKZwy5tvgGyLRiRHLzhlbOpf7BT032oBSd0haZqXn5W6l26WLlu3dyxzjM+2638/z2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/utils": "^2.0.2", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/json-schema": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.12.tgz", + "integrity": "sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@peculiar/utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@peculiar/utils/-/utils-2.0.3.tgz", + "integrity": "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/webcrypto": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.7.1.tgz", + "integrity": "sha512-ODOov0sGMJMf3jPonOkgGqPknTsu+DdQ7kD++gz8aI+aFMOMHFbWAA2taqXXVTdP+OTOQR/znGvSpmkeI0WTYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/json-schema": "^1.1.12", + "@peculiar/utils": "^2.0.2", + "tslib": "^2.8.1", + "webcrypto-core": "^1.9.2" + }, + "engines": { + "node": ">=14.18.0" + } + }, "node_modules/@phun-ky/typeof": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@phun-ky/typeof/-/typeof-2.0.3.tgz", @@ -4325,6 +5085,19 @@ "url": "https://ko-fi.com/dangreen" } }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, "node_modules/@stablelib/base64": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", @@ -4332,6 +5105,19 @@ "license": "MIT", "peer": true }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@tailwindcss/typography": { "version": "0.5.16", "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.16.tgz", @@ -4431,6 +5217,19 @@ "@types/node": "*" } }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -4500,6 +5299,16 @@ "@types/send": "*" } }, + "node_modules/@types/fs-extra": { + "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/hast": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", @@ -4509,6 +5318,13 @@ "@types/unist": "*" } }, + "node_modules/@types/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/http-errors": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", @@ -4529,6 +5345,16 @@ "integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==", "license": "MIT" }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", @@ -4608,6 +5434,16 @@ "@types/react": "^18.0.0" } }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/send": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", @@ -4652,6 +5488,17 @@ "@types/node": "*" } }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.56.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz", @@ -5308,6 +6155,16 @@ "yauzl": "^2.9.2" } }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.13", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz", + "integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/@xterm/addon-clipboard": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.1.0.tgz", @@ -5564,6 +6421,386 @@ "node": ">= 8" } }, + "node_modules/app-builder-lib": { + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-26.15.3.tgz", + "integrity": "sha512-2VnyWkqsP5v5XbBhL3tD5Syx8iNPBYsoU7kY4S2fz7wg8Rj/nztWKCUzGKaFRTv0Xwf3/H058CR1Kvtd/3lRow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron/asar": "3.4.1", + "@electron/fuses": "^1.8.0", + "@electron/get": "^3.0.0", + "@electron/notarize": "2.5.0", + "@electron/osx-sign": "1.3.3", + "@electron/rebuild": "^4.0.4", + "@electron/universal": "2.0.3", + "@malept/flatpak-bundler": "^0.4.0", + "@noble/hashes": "^2.2.0", + "@peculiar/webcrypto": "^1.7.1", + "@types/fs-extra": "9.0.13", + "ajv": "^8.18.0", + "asn1js": "^3.0.10", + "async-exit-hook": "^2.0.1", + "builder-util": "26.15.3", + "builder-util-runtime": "9.7.0", + "chromium-pickle-js": "^0.2.0", + "ci-info": "4.3.1", + "debug": "^4.3.4", + "dotenv": "^16.4.5", + "dotenv-expand": "^11.0.6", + "ejs": "^3.1.8", + "electron-publish": "26.15.3", + "fs-extra": "^10.1.0", + "hosted-git-info": "^4.1.0", + "isbinaryfile": "^5.0.0", + "jiti": "^2.4.2", + "js-yaml": "^4.1.0", + "json5": "^2.2.3", + "lazy-val": "^1.0.5", + "minimatch": "^10.2.5", + "pkijs": "^3.4.0", + "plist": "3.1.0", + "proper-lockfile": "^4.1.2", + "resedit": "^1.7.0", + "semver": "~7.7.3", + "tar": "^7.5.7", + "temp-file": "^3.4.0", + "tiny-async-pool": "1.3.0", + "unzipper": "^0.12.3", + "which": "^5.0.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "dmg-builder": "26.15.3", + "electron-builder-squirrel-windows": "26.15.3" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/get": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-3.1.0.tgz", + "integrity": "sha512-F+nKc0xW+kVbBRhFzaMgPy3KwmuNTYX1fx6+FxxoSnNgwYX6LD7AKBTWkU0MQ6IBoe7dz069CNkR673sPAgkCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=14" + }, + "optionalDependencies": { + "global-agent": "^3.0.0" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/get/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/get/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/app-builder-lib/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/app-builder-lib/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/app-builder-lib/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/app-builder-lib/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/app-builder-lib/node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/app-builder-lib/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/app-builder-lib/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/app-builder-lib/node_modules/fs-extra/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/app-builder-lib/node_modules/fs-extra/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/app-builder-lib/node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/app-builder-lib/node_modules/isexe": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/app-builder-lib/node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/app-builder-lib/node_modules/js-yaml": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/app-builder-lib/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/app-builder-lib/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/app-builder-lib/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/app-builder-lib/node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/app-builder-lib/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/app-builder-lib/node_modules/tar": { + "version": "7.5.16", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.16.tgz", + "integrity": "sha512-56adEpPMouktRlBLXiaYFFzZ/3+JXa8P9n7WbR+ibIjtviN55mEaOkiysCnPnWm+7kkui1Dn8J9l+g6zV8731w==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/app-builder-lib/node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/app-builder-lib/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/app-builder-lib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, "node_modules/append-field": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", @@ -5770,6 +7007,21 @@ "safer-buffer": "^2.1.0" } }, + "node_modules/asn1js": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.10.tgz", + "integrity": "sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.5", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/ast-types": { "version": "0.13.4", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", @@ -5783,6 +7035,23 @@ "node": ">=4" } }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-exit-hook": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", + "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", @@ -5813,6 +7082,23 @@ "node": ">= 4" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/attr-accept": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz", @@ -5920,6 +7206,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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==", + "dev": true, + "license": "MIT" + }, "node_modules/bail": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", @@ -6032,6 +7325,13 @@ "readable-stream": "^3.4.0" } }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, "node_modules/bn.js": { "version": "4.12.3", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz", @@ -6077,6 +7377,15 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -6176,6 +7485,114 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "license": "MIT" }, + "node_modules/builder-util": { + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-26.15.3.tgz", + "integrity": "sha512-q2hn7Mbo2nFNkVekPiHFx6Nfo3hURmES3tfBn+k5Pqxl2RkmP3QGqZUhH/q9Pch/4G05NRhPjDlVj1O8q4Txvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/debug": "^4.1.6", + "builder-util-runtime": "9.7.0", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.6", + "debug": "^4.3.4", + "fs-extra": "^10.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "js-yaml": "^4.1.0", + "sanitize-filename": "^1.6.3", + "source-map-support": "^0.5.19", + "stat-mode": "^1.0.0", + "temp-file": "^3.4.0", + "tiny-async-pool": "1.3.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/builder-util-runtime": { + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.7.0.tgz", + "integrity": "sha512-g/kR520giAFYkSXTzcmF3kqQq7wi8F6N6SzeDgZrqTBN+VHdmgWOyTdD1yD7AATDId/yXLvuP34CxW46/BwCdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "sax": "^1.2.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/builder-util/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/builder-util/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/builder-util/node_modules/js-yaml": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/builder-util/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/builder-util/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/bundle-name": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", @@ -6212,6 +7629,16 @@ "node": ">= 0.8" } }, + "node_modules/bytestreamjs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.1.tgz", + "integrity": "sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/c12": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/c12/-/c12-3.3.0.tgz", @@ -6295,6 +7722,51 @@ "dev": true, "license": "ISC" }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -6491,10 +7963,17 @@ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "license": "ISC" }, + "node_modules/chromium-pickle-js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", + "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==", + "dev": true, + "license": "MIT" + }, "node_modules/ci-info": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", - "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", + "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", "dev": true, "funding": [ { @@ -6690,6 +8169,29 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clone-response/node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -6780,6 +8282,19 @@ "dev": true, "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/comma-separated-tokens": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", @@ -6820,6 +8335,16 @@ "dot-prop": "^5.1.0" } }, + "node_modules/compare-version": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", + "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -7168,6 +8693,33 @@ "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", "license": "MIT" }, + "node_modules/cross-dirname": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cross-dirname/-/cross-dirname-0.1.0.tgz", + "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/cross-env": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz", + "integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@epic-web/invariant": "^1.0.0", + "cross-spawn": "^7.0.6" + }, + "bin": { + "cross-env": "dist/bin/cross-env.js", + "cross-env-shell": "dist/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -7372,6 +8924,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -7443,6 +9005,16 @@ "node": ">= 14" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -7487,6 +9059,14 @@ "node": ">=8" } }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/detect-node-es": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", @@ -7512,6 +9092,41 @@ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", "license": "Apache-2.0" }, + "node_modules/dir-compare": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-4.2.0.tgz", + "integrity": "sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimatch": "^3.0.5", + "p-limit": "^3.1.0 " + } + }, + "node_modules/dir-compare/node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/dir-compare/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -7531,6 +9146,87 @@ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", "license": "MIT" }, + "node_modules/dmg-builder": { + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-26.15.3.tgz", + "integrity": "sha512-O3zJUFUYHJKgzPqioHxfxzBzlSC1eXCSr79gMSBKBP5AgjjpmrydMsMLotEg9fAJF36vdUncb+4ndRNxoPdlSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "app-builder-lib": "26.15.3", + "builder-util": "26.15.3", + "fs-extra": "^10.1.0", + "js-yaml": "^4.1.0" + } + }, + "node_modules/dmg-builder/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/dmg-builder/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dmg-builder/node_modules/js-yaml": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/dmg-builder/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/dmg-builder/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -7579,6 +9275,35 @@ "url": "https://dotenvx.com" } }, + "node_modules/dotenv-expand": { + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", + "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -7593,6 +9318,49 @@ "node": ">= 0.4" } }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexer2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -7614,6 +9382,187 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron": { + "version": "38.8.6", + "resolved": "https://registry.npmjs.org/electron/-/electron-38.8.6.tgz", + "integrity": "sha512-lyBhcVi9QYAZL6FO6r5twAWAjWnYomo3iVDvrb5SJZlq928BGemHOKG0tPIq41NOLaCu9f3XdEEjMkjQPjprRg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@electron/get": "^2.0.0", + "@types/node": "^22.7.7", + "extract-zip": "^2.0.1" + }, + "bin": { + "electron": "cli.js" + }, + "engines": { + "node": ">= 12.20.55" + } + }, + "node_modules/electron-builder": { + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-26.15.3.tgz", + "integrity": "sha512-a1KM5heqS3gQCZzizXEI8RjJy3QVogULPdeSknt76uLDpBIW/HDGsMg/XgP0riP6PI9COsRvFITKKGDqA8fJxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "app-builder-lib": "26.15.3", + "builder-util": "26.15.3", + "builder-util-runtime": "9.7.0", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "dmg-builder": "26.15.3", + "fs-extra": "^10.1.0", + "lazy-val": "^1.0.5", + "simple-update-notifier": "2.0.0", + "yargs": "^17.6.2" + }, + "bin": { + "electron-builder": "cli.js", + "install-app-deps": "install-app-deps.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/electron-builder-squirrel-windows": { + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-26.15.3.tgz", + "integrity": "sha512-Jc19XPV9y9+2bAdZPkXuVNGNIEFBq9poHC61l8Kv6FdK7DRG3+Ic0rerC0DXOaeHNz8yW0fg/JnF8GQROOF5MA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "app-builder-lib": "26.15.3", + "builder-util": "26.15.3", + "electron-winstaller": "5.4.0" + } + }, + "node_modules/electron-builder/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-builder/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-builder/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron-publish": { + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-26.15.3.tgz", + "integrity": "sha512-g/2bn8YTavY4cuS5F+jOS7zmZbXXBV8KZ8yHKfJjFPoKtzBqrpCdNPxBd3tqdBwP7BVd0lGzf7Bk2s0KesWZ4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/fs-extra": "^9.0.11", + "aws4": "^1.13.2", + "builder-util": "26.15.3", + "builder-util-runtime": "9.7.0", + "chalk": "^4.1.2", + "form-data": "^4.0.5", + "fs-extra": "^10.1.0", + "lazy-val": "^1.0.5", + "mime": "^2.5.2" + } + }, + "node_modules/electron-publish/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-publish/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-publish/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/electron-publish/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.190", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.190.tgz", @@ -7621,6 +9570,44 @@ "dev": true, "license": "ISC" }, + "node_modules/electron-winstaller": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/electron-winstaller/-/electron-winstaller-5.4.0.tgz", + "integrity": "sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@electron/asar": "^3.2.1", + "debug": "^4.1.1", + "fs-extra": "^7.0.1", + "lodash": "^4.17.21", + "temp": "^0.9.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "@electron/windows-sign": "^1.1.2" + } + }, + "node_modules/electron-winstaller/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -7901,6 +9888,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/esbuild": { "version": "0.25.8", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", @@ -8745,6 +10740,43 @@ "node": ">=0.10.0" } }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/fast-content-type-parse": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", @@ -8904,6 +10936,29 @@ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", "license": "MIT" }, + "node_modules/filelist": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", + "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -9019,6 +11074,46 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/format": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", @@ -9065,6 +11160,21 @@ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "license": "MIT" }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, "node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -9098,6 +11208,13 @@ "dev": true, "license": "ISC" }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -9403,6 +11520,39 @@ "node": ">=10.13.0" } }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/global-agent/node_modules/semver": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "dev": true, + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/global-directory": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", @@ -9492,6 +11642,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -9617,9 +11793,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -9977,6 +12153,20 @@ "node": ">= 14" } }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, "node_modules/https-proxy-agent": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", @@ -10188,6 +12378,18 @@ "node": ">=8" } }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -10900,6 +13102,19 @@ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "license": "MIT" }, + "node_modules/isbinaryfile": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.7.tgz", + "integrity": "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -10956,6 +13171,24 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jiti": { "version": "1.21.7", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", @@ -11062,6 +13295,14 @@ "dev": true, "license": "MIT" }, + "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==", + "dev": true, + "license": "ISC", + "optional": true + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -11075,6 +13316,16 @@ "node": ">=6" } }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -11232,6 +13483,13 @@ "node": ">=0.10.0" } }, + "node_modules/lazy-val": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", + "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", + "dev": true, + "license": "MIT" + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -11669,6 +13927,16 @@ "loose-envify": "cli.js" } }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/lowlight": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", @@ -11749,6 +14017,34 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/matcher/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -13171,6 +15467,29 @@ "node": "^18 || ^20 || >= 21" } }, + "node_modules/node-api-version": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.1.tgz", + "integrity": "sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + } + }, + "node_modules/node-api-version/node_modules/semver": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/node-exports-info": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", @@ -13292,6 +15611,13 @@ "node": "^16.13.0 || >=18.0.0" } }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, "node_modules/node-pty": { "version": "1.2.0-beta.12", "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.2.0-beta.12.tgz", @@ -13378,6 +15704,19 @@ "node": ">=0.10.0" } }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/npm-run-path": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", @@ -13712,6 +16051,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -13931,6 +16280,16 @@ "node": ">=8" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -13991,6 +16350,21 @@ "dev": true, "license": "MIT" }, + "node_modules/pe-library": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-0.4.1.tgz", + "integrity": "sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -14062,6 +16436,37 @@ "pathe": "^2.0.3" } }, + "node_modules/pkijs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.4.0.tgz", + "integrity": "sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@noble/hashes": "1.4.0", + "asn1js": "^3.0.6", + "bytestreamjs": "^2.0.1", + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/pkijs/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/plimit-lit": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/plimit-lit/-/plimit-lit-1.6.1.tgz", @@ -14075,6 +16480,21 @@ "node": ">=12" } }, + "node_modules/plist": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", + "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=10.4.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -14241,6 +16661,36 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "license": "MIT" }, + "node_modules/postject": { + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/postject/-/postject-1.0.0-alpha.6.tgz", + "integrity": "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "commander": "^9.4.0" + }, + "bin": { + "postject": "dist/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/postject/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, "node_modules/prebuild-install": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", @@ -14302,6 +16752,16 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "license": "MIT" }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/promise-retry": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", @@ -14327,6 +16787,25 @@ "react-is": "^16.13.1" } }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/proper-lockfile/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, "node_modules/property-information": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", @@ -14413,6 +16892,26 @@ "node": ">=6" } }, + "node_modules/pvtsutils": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", + "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + } + }, + "node_modules/pvutils": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz", + "integrity": "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -14458,6 +16957,19 @@ ], "license": "MIT" }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -14750,6 +17262,19 @@ "react": ">= 0.14.0" } }, + "node_modules/read-binary-file-arch": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz", + "integrity": "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "bin": { + "read-binary-file-arch": "cli.js" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -15217,6 +17742,24 @@ "node": ">=0.10.0" } }, + "node_modules/resedit": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/resedit/-/resedit-1.7.2.tgz", + "integrity": "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pe-library": "^0.4.1" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -15237,6 +17780,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true, + "license": "MIT" + }, "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -15257,6 +17807,19 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/restore-cursor": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", @@ -15301,6 +17864,97 @@ "dev": true, "license": "MIT" }, + "node_modules/rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/roarr/node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, "node_modules/rollup": { "version": "4.45.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", @@ -15520,6 +18174,26 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/sanitize-filename": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.4.tgz", + "integrity": "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg==", + "dev": true, + "license": "WTFPL OR ISC", + "dependencies": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -15552,6 +18226,14 @@ "semver": "bin/semver.js" } }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/send": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", @@ -15600,6 +18282,37 @@ "node": ">= 0.8" } }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-error/node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/serve-static": { "version": "1.16.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", @@ -16225,6 +18938,32 @@ "is-arrayish": "^0.3.1" } }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -16341,6 +19080,17 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/space-separated-tokens": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", @@ -16433,6 +19183,16 @@ "fast-sha256": "^1.3.0" } }, + "node_modules/stat-mode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", + "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -16781,6 +19541,19 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "node": ">= 8.0" + } + }, "node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -17003,6 +19776,70 @@ "dev": true, "license": "ISC" }, + "node_modules/temp": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", + "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "mkdirp": "^0.5.1", + "rimraf": "~2.6.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/temp-file": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", + "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-exit-hook": "^2.0.1", + "fs-extra": "^10.0.0" + } + }, + "node_modules/temp-file/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/temp-file/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/temp-file/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -17024,6 +19861,26 @@ "node": ">=0.8" } }, + "node_modules/tiny-async-pool": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tiny-async-pool/-/tiny-async-pool-1.3.0.tgz", + "integrity": "sha512-01EAw5EDrcVrdgyCLgoSPvqznC0sVxDSVeiOz09FUpjh71G79VCqneOr+xvt7T1r76CF6ZZfPjHorN2+d+3mqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^5.5.0" + } + }, + "node_modules/tiny-async-pool/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, "node_modules/tinyexec": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", @@ -17082,6 +19939,26 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tmp": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/tmp-promise": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tmp": "^0.2.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -17139,6 +20016,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", + "dev": true, + "license": "WTFPL", + "dependencies": { + "utf8-byte-length": "^1.0.1" + } + }, "node_modules/ts-algebra": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", @@ -18149,6 +21036,16 @@ "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", "license": "ISC" }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -18193,6 +21090,58 @@ "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, + "node_modules/unzipper": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.12.3.tgz", + "integrity": "sha512-PZ8hTS+AqcGxsaQntl3IRBw65QrBI6lxzqDEL7IAo/XCEqRTKGfOX56Vea5TH9SZczRVxuzk1re04z/YjuYCJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bluebird": "~3.7.2", + "duplexer2": "~0.1.4", + "fs-extra": "^11.2.0", + "graceful-fs": "^4.2.2", + "node-int64": "^0.4.0" + } + }, + "node_modules/unzipper/node_modules/fs-extra": { + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.5.tgz", + "integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/unzipper/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/unzipper/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", @@ -18296,6 +21245,13 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/utf8-byte-length": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", + "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", + "dev": true, + "license": "(WTFPL OR MIT)" + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -18554,6 +21510,20 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/webcrypto-core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.9.2.tgz", + "integrity": "sha512-gsXecm82UQNlTBURJGuqOWy1Ww08S3kZUcr3aOJS02Pk0xLtkfeUAVC0u0xhgdonFme80edSJUIJyuvL/7250Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/json-schema": "^1.1.12", + "@peculiar/utils": "^2.0.2", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -18852,6 +21822,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index 2220c0f9..f378f79b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "@cloudcli-ai/cloudcli", "version": "1.34.0", + "productName": "CloudCLI", "description": "A web-based UI for Claude Code CLI", "type": "module", "main": "dist-server/server/index.js", @@ -8,6 +9,7 @@ "cloudcli": "dist-server/server/cli.js" }, "files": [ + "electron/", "server/", "shared/", "public/api-docs.html", @@ -30,6 +32,10 @@ "server:dev": "tsx --tsconfig server/tsconfig.json server/index.js", "server:dev-watch": "tsx watch --tsconfig server/tsconfig.json server/index.js", "client": "vite", + "desktop": "electron electron/main.js", + "desktop:dev": "cross-env ELECTRON_DEV_URL=http://127.0.0.1:5173 electron electron/main.js", + "desktop:pack": "npm run build && electron-builder --dir", + "desktop:dist:mac": "npm run build && electron-builder --mac dmg zip", "build": "npm run build:client && npm run build:server", "build:client": "vite build", "prebuild:server": "node -e \"require('node:fs').rmSync('dist-server', { recursive: true, force: true })\"", @@ -45,6 +51,53 @@ "prepare": "husky", "update:platform": "./update-platform.sh" }, + "build": { + "appId": "ai.cloudcli.desktop", + "productName": "CloudCLI", + "artifactName": "CloudCLI-${version}-${arch}.${ext}", + "directories": { + "output": "release" + }, + "extraMetadata": { + "main": "electron/main.js" + }, + "files": [ + "electron/", + "public/", + "dist/", + "dist-server/", + "shared/", + "server/", + "package.json" + ], + "protocols": [ + { + "name": "CloudCLI", + "schemes": [ + "cloudcli" + ] + } + ], + "mac": { + "category": "public.app-category.developer-tools", + "target": [ + "dmg", + "zip" + ], + "extendInfo": { + "CFBundleName": "CloudCLI", + "CFBundleDisplayName": "CloudCLI", + "CFBundleURLTypes": [ + { + "CFBundleURLName": "CloudCLI", + "CFBundleURLSchemes": [ + "cloudcli" + ] + } + ] + } + } + }, "keywords": [ "claude code", "claude-code", @@ -141,6 +194,9 @@ "auto-changelog": "^2.5.0", "autoprefixer": "^10.4.16", "concurrently": "^8.2.2", + "cross-env": "^10.1.0", + "electron": "^38.0.0", + "electron-builder": "^26.15.3", "eslint": "^9.39.3", "eslint-import-resolver-typescript": "^4.4.4", "eslint-plugin-boundaries": "^6.0.2", diff --git a/server/browser-use-mcp.ts b/server/browser-use-mcp.ts new file mode 100644 index 00000000..238bff35 --- /dev/null +++ b/server/browser-use-mcp.ts @@ -0,0 +1,384 @@ +#!/usr/bin/env node +import './load-env.js'; + +type JsonRpcRequest = { + jsonrpc: '2.0'; + id?: string | number | null; + method: string; + params?: Record; +}; + +type ToolDefinition = { + name: string; + description: string; + inputSchema: Record; +}; + +const textResponse = (text: string) => ({ + content: [{ type: 'text', text }], +}); + +const jsonResponse = (value: unknown) => textResponse(JSON.stringify(value, null, 2)); + +const readString = (value: unknown, name: string): string => { + if (typeof value !== 'string' || value.trim() === '') { + throw new Error(`${name} is required.`); + } + return value.trim(); +}; + +const readOptionalString = (value: unknown): string | undefined => + typeof value === 'string' && value.trim() ? value.trim() : undefined; + +const readNumber = (value: unknown): number | undefined => + typeof value === 'number' && Number.isFinite(value) ? value : undefined; + +const apiUrl = (process.env.CLOUDCLI_BROWSER_USE_API_URL || 'http://127.0.0.1:3001/api/browser-use-mcp').replace(/\/$/, ''); +const apiToken = process.env.CLOUDCLI_BROWSER_USE_MCP_TOKEN || ''; +const API_TIMEOUT_MS = Number.parseInt(process.env.CLOUDCLI_BROWSER_USE_API_TIMEOUT_MS || '60000', 10); + +async function callBrowserUseApi(toolName: string, input: Record) { + if (!apiToken) { + throw new Error('CLOUDCLI_BROWSER_USE_MCP_TOKEN is not configured.'); + } + + const response = await fetch(`${apiUrl}/tools/${encodeURIComponent(toolName)}`, { + method: 'POST', + headers: { + Authorization: `Bearer ${apiToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(input), + signal: AbortSignal.timeout(API_TIMEOUT_MS), + }); + const data = await response.json() as { success?: boolean; data?: unknown; error?: string }; + if (!response.ok || data.success === false) { + throw new Error(data.error || `Browser API request failed (${response.status})`); + } + return data.data; +} + +const sessionIdSchema = { + type: 'object', + properties: { + sessionId: { type: 'string', description: 'Browser session id.' }, + }, + required: ['sessionId'], +}; + +const tools: ToolDefinition[] = [ + { + name: 'browser_create_session', + description: 'Create a temporary Browser session that the agent can control. Optionally provide a background profileName to reuse cookies and storage.', + inputSchema: { + type: 'object', + properties: { + profileName: { type: 'string', description: 'Optional background profile name for persistent browser storage.' }, + }, + }, + }, + { + name: 'browser_list_sessions', + description: 'List Browser sessions currently available to agents.', + inputSchema: { type: 'object', properties: {} }, + }, + { + name: 'browser_snapshot', + description: 'Capture current page metadata, screenshot data URL, and visible body text for a Browser session.', + inputSchema: sessionIdSchema, + }, + { + name: 'browser_take_screenshot', + description: 'Capture the latest screenshot for a Browser session.', + inputSchema: sessionIdSchema, + }, + { + name: 'browser_navigate', + description: 'Navigate a Browser session to an HTTP or HTTPS URL.', + inputSchema: { + type: 'object', + properties: { + sessionId: { type: 'string' }, + url: { type: 'string' }, + }, + required: ['sessionId', 'url'], + }, + }, + { + name: 'browser_click', + description: 'Click an element by CSS selector, visible text, or x/y coordinates.', + inputSchema: { + type: 'object', + properties: { + sessionId: { type: 'string' }, + selector: { type: 'string' }, + text: { type: 'string' }, + x: { type: 'number' }, + y: { type: 'number' }, + }, + required: ['sessionId'], + }, + }, + { + name: 'browser_type', + description: 'Type text into the focused page or fill a CSS selector. Set submit to press Enter after typing.', + inputSchema: { + type: 'object', + properties: { + sessionId: { type: 'string' }, + selector: { type: 'string' }, + text: { type: 'string' }, + submit: { type: 'boolean' }, + }, + required: ['sessionId', 'text'], + }, + }, + { + name: 'browser_fill_form', + description: 'Fill multiple form fields using CSS selectors.', + inputSchema: { + type: 'object', + properties: { + sessionId: { type: 'string' }, + fields: { + type: 'array', + items: { + type: 'object', + properties: { + selector: { type: 'string' }, + value: { type: 'string' }, + }, + required: ['selector', 'value'], + }, + }, + }, + required: ['sessionId', 'fields'], + }, + }, + { + name: 'browser_press_key', + description: 'Press a keyboard key, for example Enter, Escape, Tab, or Control+A.', + inputSchema: { + type: 'object', + properties: { + sessionId: { type: 'string' }, + key: { type: 'string' }, + }, + required: ['sessionId', 'key'], + }, + }, + { + name: 'browser_select_option', + description: 'Select option values in a select element found by CSS selector.', + inputSchema: { + type: 'object', + properties: { + sessionId: { type: 'string' }, + selector: { type: 'string' }, + values: { type: 'array', items: { type: 'string' } }, + }, + required: ['sessionId', 'selector', 'values'], + }, + }, + { + name: 'browser_wait_for', + description: 'Wait for visible text, a URL pattern, or a short timeout.', + inputSchema: { + type: 'object', + properties: { + sessionId: { type: 'string' }, + text: { type: 'string' }, + url: { type: 'string' }, + timeoutMs: { type: 'number' }, + }, + required: ['sessionId'], + }, + }, + { + name: 'browser_tabs', + description: 'List, open, select, or close tabs in a Browser session.', + inputSchema: { + type: 'object', + properties: { + sessionId: { type: 'string' }, + action: { type: 'string', enum: ['list', 'new', 'select', 'close'] }, + index: { type: 'number' }, + url: { type: 'string' }, + }, + required: ['sessionId'], + }, + }, + { + name: 'browser_close_session', + description: 'Stop a Browser session controlled by agents.', + inputSchema: sessionIdSchema, + }, +]; + +async function callTool(name: string, args: Record) { + switch (name) { + case 'browser_create_session': + return jsonResponse(await callBrowserUseApi(name, { + profileName: readOptionalString(args.profileName), + })); + case 'browser_list_sessions': + return jsonResponse(await callBrowserUseApi(name, {})); + case 'browser_snapshot': + return jsonResponse(await callBrowserUseApi(name, { sessionId: readString(args.sessionId, 'sessionId') })); + case 'browser_take_screenshot': { + return jsonResponse(await callBrowserUseApi(name, { sessionId: readString(args.sessionId, 'sessionId') })); + } + case 'browser_navigate': + return jsonResponse(await callBrowserUseApi(name, { + sessionId: readString(args.sessionId, 'sessionId'), + url: readString(args.url, 'url'), + })); + case 'browser_click': + return jsonResponse(await callBrowserUseApi(name, { + sessionId: readString(args.sessionId, 'sessionId'), + selector: readOptionalString(args.selector), + text: readOptionalString(args.text), + x: readNumber(args.x), + y: readNumber(args.y), + })); + case 'browser_type': + return jsonResponse(await callBrowserUseApi(name, { + sessionId: readString(args.sessionId, 'sessionId'), + selector: readOptionalString(args.selector), + text: readString(args.text, 'text'), + submit: args.submit === true, + })); + case 'browser_fill_form': { + const fields = Array.isArray(args.fields) + ? args.fields.map((field) => { + const record = field as Record; + return { + selector: readString(record.selector, 'field.selector'), + value: readString(record.value, 'field.value'), + }; + }) + : []; + return jsonResponse(await callBrowserUseApi(name, { + sessionId: readString(args.sessionId, 'sessionId'), + fields, + })); + } + case 'browser_press_key': + return jsonResponse(await callBrowserUseApi(name, { + sessionId: readString(args.sessionId, 'sessionId'), + key: readString(args.key, 'key'), + })); + case 'browser_select_option': + return jsonResponse(await callBrowserUseApi(name, { + sessionId: readString(args.sessionId, 'sessionId'), + selector: readString(args.selector, 'selector'), + values: Array.isArray(args.values) ? args.values.filter((value): value is string => typeof value === 'string') : [], + })); + case 'browser_wait_for': + return jsonResponse(await callBrowserUseApi(name, { + sessionId: readString(args.sessionId, 'sessionId'), + text: readOptionalString(args.text), + url: readOptionalString(args.url), + timeoutMs: readNumber(args.timeoutMs), + })); + case 'browser_tabs': + return jsonResponse(await callBrowserUseApi(name, { + sessionId: readString(args.sessionId, 'sessionId'), + action: args.action === 'new' || args.action === 'select' || args.action === 'close' || args.action === 'list' + ? args.action + : undefined, + index: readNumber(args.index), + url: readOptionalString(args.url), + })); + case 'browser_close_session': + return jsonResponse(await callBrowserUseApi(name, { sessionId: readString(args.sessionId, 'sessionId') })); + default: + throw new Error(`Unknown tool: ${name}`); + } +} + +async function handleMessage(message: JsonRpcRequest) { + if (message.method === 'initialize') { + return { + protocolVersion: '2024-11-05', + capabilities: { tools: {} }, + serverInfo: { name: 'cloudcli-browser', version: '1.0.0' }, + }; + } + + if (message.method === 'tools/list') { + return { tools }; + } + + if (message.method === 'tools/call') { + const params = message.params || {}; + const name = readString(params.name, 'name'); + const args = (params.arguments && typeof params.arguments === 'object' + ? params.arguments + : {}) as Record; + return callTool(name, args); + } + + if (message.method.startsWith('notifications/')) { + return undefined; + } + + throw new Error(`Unsupported method: ${message.method}`); +} + +function writeMessage(message: Record) { + // MCP stdio transport uses newline-delimited JSON (one JSON-RPC message per line, + // no embedded newlines). This is NOT the LSP Content-Length framing. + process.stdout.write(`${JSON.stringify(message)}\n`); +} + +function sendResult(id: string | number | null | undefined, result: unknown) { + if (id === undefined) { + return; + } + writeMessage({ jsonrpc: '2.0', id, result }); +} + +function sendError(id: string | number | null | undefined, error: unknown) { + if (id === undefined) { + return; + } + writeMessage({ + jsonrpc: '2.0', + id, + error: { + code: -32000, + message: error instanceof Error ? error.message : String(error), + }, + }); +} + +let buffer = ''; + +process.stdin.on('data', (chunk) => { + buffer += chunk.toString('utf8'); + let newlineIndex: number; + while ((newlineIndex = buffer.indexOf('\n')) !== -1) { + const rawMessage = buffer.slice(0, newlineIndex).trim(); + buffer = buffer.slice(newlineIndex + 1); + if (!rawMessage) { + continue; + } + + void (async () => { + let request: JsonRpcRequest; + try { + request = JSON.parse(rawMessage) as JsonRpcRequest; + } catch (error) { + sendError(null, error); + return; + } + try { + const result = await handleMessage(request); + sendResult(request.id, result); + } catch (error) { + sendError(request.id, error); + } + })(); + } +}); diff --git a/server/cli.js b/server/cli.js index 9fa99ae3..0be3bf72 100755 --- a/server/cli.js +++ b/server/cli.js @@ -8,6 +8,7 @@ * (no args) - Start the server (default) * start - Start the server * sandbox - Manage Docker sandbox environments + * browser-use-mcp - Run Browser MCP stdio server * status - Show configuration and data locations * help - Show help information * version - Show version information @@ -154,12 +155,13 @@ Usage: cloudcli [command] [options] Commands: - start Start the CloudCLI server (default) - sandbox Manage Docker sandbox environments - status Show configuration and data locations - update Update to the latest version - help Show this help information - version Show version information + start Start the CloudCLI server (default) + sandbox Manage Docker sandbox environments + browser-use-mcp Run the Browser MCP stdio server + status Show configuration and data locations + update Update to the latest version + help Show this help information + version Show version information Options: -p, --port Set server port (default: 3001) @@ -605,6 +607,10 @@ async function startServer() { await import('./index.js'); } +async function startBrowserUseMcp() { + await import('./browser-use-mcp.js'); +} + // Parse CLI arguments function parseArgs(args) { const parsed = { command: 'start', options: {} }; @@ -658,6 +664,9 @@ async function main() { case 'sandbox': await sandboxCommand(remainingArgs || []); break; + case 'browser-use-mcp': + await startBrowserUseMcp(); + break; case 'status': case 'info': showStatus(); diff --git a/server/index.js b/server/index.js index 9ebe74d6..f4c9e25e 100755 --- a/server/index.js +++ b/server/index.js @@ -61,6 +61,9 @@ import userRoutes from './routes/user.js'; import geminiRoutes from './routes/gemini.js'; import pluginsRoutes from './routes/plugins.js'; import providerRoutes from './modules/providers/provider.routes.js'; +import browserUseRoutes from './modules/browser-use/browser-use.routes.js'; +import browserUseMcpRoutes from './modules/browser-use/browser-use-mcp.routes.js'; +import { browserUseService } from './modules/browser-use/browser-use.service.js'; import { startEnabledPluginServers, stopAllPlugins, getPluginPort } from './utils/plugin-process-manager.js'; import { initializeDatabase, projectsDb, sessionsDb } from './modules/database/index.js'; import { configureWebPush } from './services/vapid-keys.js'; @@ -193,6 +196,12 @@ app.use('/api/gemini', authenticateToken, geminiRoutes); // Plugins API Routes (protected) app.use('/api/plugins', authenticateToken, pluginsRoutes); +// Browser MCP bridge API (local token protected) +app.use('/api/browser-use-mcp', browserUseMcpRoutes); + +// Browser API Routes (protected) +app.use('/api/browser-use', authenticateToken, browserUseRoutes); + // Unified provider MCP routes (protected) app.use('/api/providers', authenticateToken, providerRoutes); @@ -1704,12 +1713,21 @@ async function startServer() { await closeSessionsWatcher(); // Clean up plugin processes on shutdown - const shutdownPlugins = async () => { - await stopAllPlugins(); + const shutdownRuntimeServices = async () => { + try { + await browserUseService.stopAllSessions(); + } catch (err) { + console.error('[Browser] Error stopping sessions during shutdown:', err?.message || err); + } + try { + await stopAllPlugins(); + } catch (err) { + console.error('[Plugins] Error stopping plugins during shutdown:', err?.message || err); + } process.exit(0); }; - process.on('SIGTERM', () => void shutdownPlugins()); - process.on('SIGINT', () => void shutdownPlugins()); + process.on('SIGTERM', () => void shutdownRuntimeServices()); + process.on('SIGINT', () => void shutdownRuntimeServices()); } catch (error) { console.error('[ERROR] Failed to start server:', error); process.exit(1); diff --git a/server/modules/browser-use/browser-use-mcp.routes.ts b/server/modules/browser-use/browser-use-mcp.routes.ts new file mode 100644 index 00000000..060bb028 --- /dev/null +++ b/server/modules/browser-use/browser-use-mcp.routes.ts @@ -0,0 +1,120 @@ +import express from 'express'; + +import { browserUseService } from '@/modules/browser-use/browser-use.service.js'; + +const router = express.Router(); + +function readBearerToken(header: unknown): string | null { + if (typeof header !== 'string') { + return null; + } + const match = /^Bearer\s+(\S.*)$/i.exec(header.trim()); + return match?.[1]?.trim() || null; +} + +router.use((req, res, next) => { + const expected = browserUseService.getMcpToken(); + const token = readBearerToken(req.headers.authorization) || String(req.headers['x-browser-use-mcp-token'] || ''); + if (!token || token !== expected) { + res.status(401).json({ success: false, error: 'Invalid Browser MCP token.' }); + return; + } + next(); +}); + +router.post('/tools/:toolName', async (req, res) => { + try { + const input = (req.body && typeof req.body === 'object' ? req.body : {}) as Record; + const sessionId = typeof input.sessionId === 'string' ? input.sessionId : ''; + const toolName = req.params.toolName; + let result: unknown; + + switch (toolName) { + case 'browser_create_session': + result = await browserUseService.createAgentSession({ + profileName: typeof input.profileName === 'string' ? input.profileName : null, + }); + break; + case 'browser_list_sessions': + result = await browserUseService.listAgentSessions(); + break; + case 'browser_snapshot': + case 'browser_take_screenshot': + result = await browserUseService.agentSnapshot(sessionId); + break; + case 'browser_navigate': + result = await browserUseService.agentNavigate(sessionId, String(input.url || '')); + break; + case 'browser_click': + result = await browserUseService.agentClick(sessionId, { + selector: typeof input.selector === 'string' ? input.selector : undefined, + text: typeof input.text === 'string' ? input.text : undefined, + x: typeof input.x === 'number' ? input.x : undefined, + y: typeof input.y === 'number' ? input.y : undefined, + }); + break; + case 'browser_type': + result = await browserUseService.agentType(sessionId, { + selector: typeof input.selector === 'string' ? input.selector : undefined, + text: String(input.text || ''), + submit: input.submit === true, + }); + break; + case 'browser_fill_form': + result = await browserUseService.agentFillForm( + sessionId, + Array.isArray(input.fields) + ? input.fields.map((field) => { + const record = field as Record; + return { + selector: String(record.selector || ''), + value: String(record.value || ''), + }; + }) + : [], + ); + break; + case 'browser_press_key': + result = await browserUseService.agentPressKey(sessionId, String(input.key || '')); + break; + case 'browser_select_option': + result = await browserUseService.agentSelectOption( + sessionId, + String(input.selector || ''), + Array.isArray(input.values) ? input.values.filter((value): value is string => typeof value === 'string') : [], + ); + break; + case 'browser_wait_for': + result = await browserUseService.agentWaitFor(sessionId, { + text: typeof input.text === 'string' ? input.text : undefined, + url: typeof input.url === 'string' ? input.url : undefined, + timeoutMs: typeof input.timeoutMs === 'number' ? input.timeoutMs : undefined, + }); + break; + case 'browser_tabs': + result = await browserUseService.agentTabs(sessionId, { + action: input.action === 'new' || input.action === 'select' || input.action === 'close' || input.action === 'list' + ? input.action + : undefined, + index: typeof input.index === 'number' ? input.index : undefined, + url: typeof input.url === 'string' ? input.url : undefined, + }); + break; + case 'browser_close_session': + result = await browserUseService.agentStopSession(sessionId); + break; + default: + res.status(404).json({ success: false, error: `Unknown Browser MCP tool "${toolName}".` }); + return; + } + + res.json({ success: true, data: result }); + } catch (error) { + res.status(400).json({ + success: false, + error: error instanceof Error ? error.message : 'Browser MCP tool failed.', + }); + } +}); + +export default router; diff --git a/server/modules/browser-use/browser-use.routes.ts b/server/modules/browser-use/browser-use.routes.ts new file mode 100644 index 00000000..ad428079 --- /dev/null +++ b/server/modules/browser-use/browser-use.routes.ts @@ -0,0 +1,96 @@ +import express from 'express'; + +import { browserUseService } from '@/modules/browser-use/browser-use.service.js'; + +const router = express.Router(); + +function readParam(value: string | string[] | undefined): string { + return Array.isArray(value) ? value[0] || '' : value || ''; +} + +router.get('/status', async (_req, res) => { + try { + res.json({ success: true, data: await browserUseService.getStatus() }); + } catch (error) { + res.status(500).json({ + success: false, + error: error instanceof Error ? error.message : 'Failed to load Browser status.', + }); + } +}); + +router.get('/settings', async (_req, res) => { + try { + res.json({ success: true, data: { settings: await browserUseService.getSettings() } }); + } catch (error) { + res.status(500).json({ + success: false, + error: error instanceof Error ? error.message : 'Failed to load Browser settings.', + }); + } +}); + +router.put('/settings', async (req, res) => { + try { + const settings = await browserUseService.updateSettings(req.body || {}); + res.json({ success: true, data: { settings } }); + } catch (error) { + res.status(400).json({ + success: false, + error: error instanceof Error ? error.message : 'Failed to save Browser settings.', + }); + } +}); + +router.post('/runtime/install', async (_req, res) => { + try { + const result = await browserUseService.installRuntime(); + res.status(result.success ? 200 : 500).json({ + success: result.success, + data: result, + error: result.success ? undefined : result.message, + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error instanceof Error ? error.message : 'Failed to install Browser runtime.', + }); + } +}); + +router.get('/sessions', async (_req, res) => { + try { + res.json({ success: true, data: { sessions: await browserUseService.listSessions() } }); + } catch (error) { + res.status(401).json({ + success: false, + error: error instanceof Error ? error.message : 'Failed to list browser sessions.', + }); + } +}); + +router.post('/sessions/:sessionId/stop', async (req, res) => { + try { + const result = await browserUseService.stopSession(readParam(req.params.sessionId)); + res.json({ success: true, data: result }); + } catch (error) { + res.status(400).json({ + success: false, + error: error instanceof Error ? error.message : 'Failed to stop browser session.', + }); + } +}); + +router.delete('/sessions/:sessionId', async (req, res) => { + try { + const result = await browserUseService.deleteSession(readParam(req.params.sessionId)); + res.json({ success: true, data: result }); + } catch (error) { + res.status(400).json({ + success: false, + error: error instanceof Error ? error.message : 'Failed to delete browser session.', + }); + } +}); + +export default router; diff --git a/server/modules/browser-use/browser-use.service.ts b/server/modules/browser-use/browser-use.service.ts new file mode 100644 index 00000000..280ff730 --- /dev/null +++ b/server/modules/browser-use/browser-use.service.ts @@ -0,0 +1,836 @@ +import { createRequire } from 'node:module'; +import { randomBytes, randomUUID } from 'node:crypto'; +import { spawn } from 'node:child_process'; +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; + +import { appConfigDb } from '@/modules/database/index.js'; +import { providerMcpService } from '@/modules/providers/index.js'; +import { getModuleDir } from '@/utils/runtime-paths.js'; + +const require = createRequire(import.meta.url); +const __dirname = getModuleDir(import.meta.url); +const IS_PLATFORM = process.env.VITE_IS_PLATFORM === 'true'; +const MAX_SESSIONS_PER_OWNER = Number.parseInt(process.env.CLOUDCLI_BROWSER_USE_MAX_SESSIONS_PER_OWNER || '3', 10); +const SESSION_TTL_MS = Number.parseInt(process.env.CLOUDCLI_BROWSER_USE_SESSION_TTL_MS || String(30 * 60 * 1000), 10); +const BROWSER_USE_SETTINGS_KEY = 'browser_use_settings'; +const BROWSER_USE_MCP_TOKEN_KEY = 'browser_use_mcp_token'; + +type BrowserUseRuntime = 'cloud' | 'local'; +type BrowserUseSessionStatus = 'ready' | 'stopped' | 'unavailable'; + +type BrowserUseSession = { + id: string; + ownerId: string; + createdBy: 'agent'; + runtime: BrowserUseRuntime; + status: BrowserUseSessionStatus; + url: string | null; + title: string | null; + screenshotDataUrl: string | null; + createdAt: string; + updatedAt: string; + lastAction: string | null; + message: string | null; + profileName: string | null; + viewport: { + width: number; + height: number; + } | null; + cursor: { + x: number; + y: number; + actor: 'agent'; + } | null; +}; + +type PublicBrowserUseSession = Omit; + +type RuntimeHandle = { + browser?: any; + context?: any; + page?: any; +}; + +type BrowserUseSettings = { + enabled: boolean; +}; + +type RuntimeReadiness = { + playwright: any | null; + playwrightInstalled: boolean; + chromiumInstalled: boolean; + chromiumExecutablePath: string | null; + installInProgress: boolean; + installMessage: string | null; +}; + +type RuntimeProbe = Omit; + +const sessions = new Map(); +const handles = new Map(); +let installPromise: Promise<{ success: boolean; message: string }> | null = null; +let lastInstallMessage: string | null = null; +let runtimeProbeCache: { value: RuntimeProbe; updatedAt: number } | null = null; + +const DEFAULT_SETTINGS: BrowserUseSettings = { + enabled: false, +}; +const AGENT_OWNER_ID = 'agent'; +const PROFILE_ROOT = path.join(os.homedir(), '.cloudcli', 'browser-use', 'profiles'); +const MCP_SERVER_NAME = 'cloudcli-browser'; +const LEGACY_MCP_SERVER_NAMES = ['cloudcli-browser-use']; +const RUNTIME_READINESS_CACHE_TTL_MS = 30_000; + +function getRuntime(): BrowserUseRuntime { + return IS_PLATFORM ? 'cloud' : 'local'; +} + +function readSettings(): BrowserUseSettings { + try { + const raw = appConfigDb.get(BROWSER_USE_SETTINGS_KEY); + if (!raw) { + return DEFAULT_SETTINGS; + } + + const parsed = JSON.parse(raw) as Partial; + return { + enabled: parsed.enabled === true, + }; + } catch (error: any) { + console.warn('[Browser] Failed to read settings:', error?.message || error); + return DEFAULT_SETTINGS; + } +} + +function writeSettings(settings: BrowserUseSettings): BrowserUseSettings { + const normalized = { + enabled: settings.enabled === true, + }; + + appConfigDb.set(BROWSER_USE_SETTINGS_KEY, JSON.stringify(normalized)); + return normalized; +} + +function getOrCreateMcpToken(): string { + const existing = appConfigDb.get(BROWSER_USE_MCP_TOKEN_KEY); + if (existing) { + return existing; + } + const token = randomBytes(32).toString('hex'); + appConfigDb.set(BROWSER_USE_MCP_TOKEN_KEY, token); + return token; +} + +function getSetupMessage(settings: BrowserUseSettings, readiness: RuntimeReadiness): string { + if (!settings.enabled) { + return 'Browser is disabled in settings.'; + } + + if (!readiness.playwrightInstalled) { + return 'Install Playwright and Chromium to use browser sessions.'; + } + + if (!readiness.chromiumInstalled) { + return 'Playwright is installed, but Chromium is missing. Install the Chromium runtime to continue.'; + } + + return readiness.installMessage || 'Browser runtime is not ready.'; +} + +function getPlaywright(): any | null { + try { + return require('playwright'); + } catch { + return null; + } +} + +function getMcpCommand(): { command: string; args: string[] } { + const serverDir = path.resolve(__dirname, '..', '..'); + const mcpScriptPath = path.join(serverDir, 'browser-use-mcp.js'); + if (fs.existsSync(mcpScriptPath)) { + return { + command: process.execPath, + args: [mcpScriptPath], + }; + } + + return { + command: 'cloudcli', + args: ['browser-use-mcp'], + }; +} + +function getMcpApiUrl(): string { + const port = process.env.SERVER_PORT || process.env.PORT || '3001'; + return `http://127.0.0.1:${port}/api/browser-use-mcp`; +} + +async function removeMcpServerFromAllProviders(name: string) { + const results = await providerMcpService.removeMcpServerFromAllProviders({ + name, + scope: 'user', + }); + return results.map((result) => ({ ...result, name })); +} + +function normalizeProfileName(profileName?: string | null): string | null { + const normalized = String(profileName || '').trim(); + if (!normalized) { + return null; + } + + return normalized.slice(0, 80); +} + +function getProfilePath(profileName: string): string { + const safeName = profileName + .toLowerCase() + .replace(/[^a-z0-9._-]+/g, '-') + .replace(/^-+|-+$/g, '') + .slice(0, 80) || 'default'; + return path.join(PROFILE_ROOT, safeName); +} + +function probeRuntime(): RuntimeProbe { + const playwright = getPlaywright(); + const readiness: RuntimeProbe = { + playwright, + playwrightInstalled: Boolean(playwright), + chromiumInstalled: false, + chromiumExecutablePath: null, + }; + + if (!playwright) { + return readiness; + } + + try { + const executablePath = playwright.chromium.executablePath(); + readiness.chromiumExecutablePath = executablePath; + readiness.chromiumInstalled = Boolean(executablePath && fs.existsSync(executablePath)); + } catch { + readiness.chromiumInstalled = false; + } + + return readiness; +} + +function getRuntimeReadiness(options: { force?: boolean } = {}): RuntimeReadiness { + const now = Date.now(); + const cachedProbe = runtimeProbeCache; + const canUseCache = !options.force + && !installPromise + && cachedProbe + && now - cachedProbe.updatedAt < RUNTIME_READINESS_CACHE_TTL_MS; + const probe = canUseCache ? cachedProbe.value : probeRuntime(); + + if (!canUseCache && !installPromise) { + runtimeProbeCache = { value: probe, updatedAt: now }; + } + + return { + ...probe, + installInProgress: Boolean(installPromise), + installMessage: lastInstallMessage, + }; +} + +const INSTALL_COMMAND_TIMEOUT_MS = Number.parseInt( + process.env.CLOUDCLI_BROWSER_USE_INSTALL_TIMEOUT_MS || String(10 * 60 * 1000), + 10, +); + +function runCommand(command: string, args: string[]): Promise { + return new Promise((resolve, reject) => { + const child = spawn(command, args, { + cwd: process.cwd(), + env: process.env, + shell: false, + stdio: ['ignore', 'pipe', 'pipe'], + }); + const output: string[] = []; + let settled = false; + const finish = (fn: () => void) => { + if (settled) { + return; + } + settled = true; + clearTimeout(timer); + fn(); + }; + + const timer = setTimeout(() => { + child.kill('SIGKILL'); + finish(() => reject(new Error( + `${command} ${args.join(' ')} timed out after ${INSTALL_COMMAND_TIMEOUT_MS}ms.`, + ))); + }, INSTALL_COMMAND_TIMEOUT_MS); + timer.unref?.(); + + child.stdout.on('data', (chunk) => output.push(String(chunk))); + child.stderr.on('data', (chunk) => output.push(String(chunk))); + child.on('error', (error) => finish(() => reject(error))); + child.on('close', (code) => finish(() => { + if (code === 0) { + resolve(); + return; + } + + reject(new Error(output.join('').trim() || `${command} ${args.join(' ')} exited with code ${code}`)); + })); + }); +} + +function formatInstallError(error: unknown): string { + const message = error instanceof Error ? error.message : String(error); + if (message.includes('sudo') && message.includes('password')) { + return 'Installing Chromium system dependencies requires administrator privileges. Run `npx playwright install-deps chromium` on the machine where CloudCLI runs, then try again.'; + } + return message || 'Failed to install Browser runtime.'; +} + +async function installRuntime(): Promise<{ success: boolean; message: string }> { + if (installPromise) { + return installPromise; + } + + const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm'; + runtimeProbeCache = null; + installPromise = (async () => { + try { + lastInstallMessage = 'Installing Playwright package...'; + await runCommand(npmCommand, ['install', '--no-save', '--no-package-lock', 'playwright']); + + if (process.platform === 'linux') { + lastInstallMessage = 'Installing Chromium system dependencies...'; + await runCommand(npmCommand, ['exec', '--', 'playwright', 'install-deps', 'chromium']); + } + + lastInstallMessage = 'Installing Chromium runtime...'; + await runCommand(npmCommand, ['exec', '--', 'playwright', 'install', 'chromium']); + + lastInstallMessage = 'Browser runtime installed.'; + return { success: true, message: lastInstallMessage }; + } catch (error) { + lastInstallMessage = formatInstallError(error); + return { success: false, message: lastInstallMessage }; + } + })(); + + try { + return await installPromise; + } finally { + installPromise = null; + runtimeProbeCache = null; + } +} + +function normalizeUrl(rawUrl: string): string { + const trimmed = rawUrl.trim(); + if (!trimmed) { + throw new Error('URL is required.'); + } + + const withProtocol = /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(trimmed) + ? trimmed + : `https://${trimmed}`; + const parsed = new URL(withProtocol); + if (!['http:', 'https:'].includes(parsed.protocol)) { + throw new Error('Only http and https URLs are supported.'); + } + + return parsed.toString(); +} + +function publicSession(session: BrowserUseSession): PublicBrowserUseSession { + const { ownerId: _ownerId, ...publicFields } = session; + return publicFields; +} + +function ownerSessions(ownerId: string): BrowserUseSession[] { + return [...sessions.values()].filter((session) => session.ownerId === ownerId); +} + +async function closeHandle(sessionId: string): Promise { + const handle = handles.get(sessionId); + handles.delete(sessionId); + await handle?.context?.close?.().catch(() => undefined); + await handle?.browser?.close().catch(() => undefined); +} + +async function expireStaleSessions(now = Date.now()): Promise { + await Promise.all([...sessions.values()].map(async (session) => { + if (session.status !== 'ready') { + return; + } + + const updatedAt = Date.parse(session.updatedAt); + if (!Number.isFinite(updatedAt) || now - updatedAt <= SESSION_TTL_MS) { + return; + } + + await closeHandle(session.id); + session.status = 'stopped'; + session.updatedAt = new Date(now).toISOString(); + session.lastAction = 'expire'; + session.message = 'Browser session expired after inactivity.'; + })); +} + +async function captureSession(session: BrowserUseSession, page: any): Promise { + const screenshot = await page.screenshot({ type: 'jpeg', quality: 72, fullPage: false }); + session.screenshotDataUrl = `data:image/jpeg;base64,${Buffer.from(screenshot).toString('base64')}`; + session.title = await page.title().catch(() => null); + session.url = page.url() || session.url; + session.viewport = page.viewportSize?.() || session.viewport; + session.updatedAt = new Date().toISOString(); +} + +async function getActionPoint(page: any, input: { selector?: string; text?: string; x?: number; y?: number }) { + if (typeof input.x === 'number' && typeof input.y === 'number') { + return { x: input.x, y: input.y }; + } + + const locator = input.selector + ? page.locator(input.selector).first() + : input.text + ? page.getByText(input.text, { exact: false }).first() + : null; + + if (!locator) { + return null; + } + + const box = await locator.boundingBox().catch(() => null); + if (!box) { + return null; + } + + return { + x: Math.round(box.x + box.width / 2), + y: Math.round(box.y + box.height / 2), + }; +} + +export const browserUseService = { + async getSettings() { + return readSettings(); + }, + + async updateSettings(settings: Partial) { + const current = readSettings(); + const nextSettings = { + enabled: typeof settings.enabled === 'boolean' ? settings.enabled : current.enabled, + }; + + const next = writeSettings(nextSettings); + if (next.enabled) { + await this.registerAgentMcp(); + } else if (current.enabled) { + await this.unregisterAgentMcp(); + await this.stopAllSessions(); + } + return next; + }, + + async getStatus() { + const settings = readSettings(); + const readiness = getRuntimeReadiness(); + const available = settings.enabled && readiness.playwrightInstalled && readiness.chromiumInstalled; + + return { + enabled: settings.enabled, + runtime: getRuntime(), + available, + playwrightInstalled: readiness.playwrightInstalled, + chromiumInstalled: readiness.chromiumInstalled, + installInProgress: readiness.installInProgress, + sessionCount: sessions.size, + message: available + ? 'Browser runtime is available.' + : getSetupMessage(settings, readiness), + }; + }, + + async registerAgentMcp() { + const { command, args } = getMcpCommand(); + await Promise.all(LEGACY_MCP_SERVER_NAMES.map((name) => removeMcpServerFromAllProviders(name))); + const results = await providerMcpService.addMcpServerToAllProviders({ + name: MCP_SERVER_NAME, + scope: 'user', + transport: 'stdio', + command, + args, + env: { + CLOUDCLI_BROWSER_USE_MCP_TOKEN: getOrCreateMcpToken(), + CLOUDCLI_BROWSER_USE_API_URL: getMcpApiUrl(), + }, + }); + return { name: MCP_SERVER_NAME, command, args, results }; + }, + + getMcpToken() { + return getOrCreateMcpToken(); + }, + + async unregisterAgentMcp() { + const results = (await Promise.all( + [MCP_SERVER_NAME, ...LEGACY_MCP_SERVER_NAMES].map((name) => removeMcpServerFromAllProviders(name)), + )).flat(); + return { name: MCP_SERVER_NAME, results }; + }, + + async installRuntime() { + const result = await installRuntime(); + return { + ...result, + status: await this.getStatus(), + }; + }, + + async listSessions() { + await expireStaleSessions(); + return [...sessions.values()] + .filter((session) => session.ownerId === AGENT_OWNER_ID) + .map(publicSession); + }, + + async createAgentSession(options?: { profileName?: string | null }) { + const settings = readSettings(); + if (!settings.enabled) { + throw new Error('Browser agent tools are disabled.'); + } + + await expireStaleSessions(); + const profileName = normalizeProfileName(options?.profileName); + + const now = new Date().toISOString(); + const session: BrowserUseSession = { + id: randomUUID(), + ownerId: AGENT_OWNER_ID, + createdBy: 'agent', + runtime: getRuntime(), + status: 'unavailable', + url: null, + title: null, + screenshotDataUrl: null, + createdAt: now, + updatedAt: now, + lastAction: 'create', + message: null, + profileName, + viewport: { width: 1440, height: 900 }, + cursor: null, + }; + + const activeOwnerSessions = ownerSessions(AGENT_OWNER_ID).filter((item) => item.status === 'ready'); + if (activeOwnerSessions.length >= MAX_SESSIONS_PER_OWNER) { + throw new Error(`Browser is limited to ${MAX_SESSIONS_PER_OWNER} active agent sessions.`); + } + + const readiness = getRuntimeReadiness(); + if (!settings.enabled || !readiness.playwrightInstalled || !readiness.chromiumInstalled || !readiness.playwright) { + session.message = getSetupMessage(settings, readiness); + sessions.set(session.id, session); + return publicSession(session); + } + + let browser: any | undefined; + let context: any | undefined; + let page: any; + const launchOptions = { + headless: true, + args: ['--disable-dev-shm-usage'], + }; + const contextOptions = { + viewport: { width: 1440, height: 900 }, + serviceWorkers: 'block', + }; + + if (profileName) { + fs.mkdirSync(PROFILE_ROOT, { recursive: true }); + context = await readiness.playwright.chromium.launchPersistentContext(getProfilePath(profileName), { + ...launchOptions, + ...contextOptions, + }); + page = context.pages()[0] || await context.newPage(); + } else { + browser = await readiness.playwright.chromium.launch(launchOptions); + context = await browser.newContext(contextOptions); + page = await context.newPage(); + } + session.status = 'ready'; + session.message = 'Browser session is ready.'; + sessions.set(session.id, session); + handles.set(session.id, { browser, context, page }); + await captureSession(session, page); + return publicSession(session); + }, + + async listAgentSessions() { + const settings = readSettings(); + if (!settings.enabled) { + return []; + } + await expireStaleSessions(); + return [...sessions.values()] + .filter((session) => session.ownerId === AGENT_OWNER_ID) + .map(publicSession); + }, + + async getAgentSession(sessionId: string) { + const settings = readSettings(); + if (!settings.enabled) { + throw new Error('Browser agent tools are disabled.'); + } + const session = sessions.get(sessionId); + if (!session || session.ownerId !== AGENT_OWNER_ID) { + throw new Error('Browser session not found.'); + } + return session; + }, + + async agentNavigate(sessionId: string, rawUrl: string) { + await this.getAgentSession(sessionId); + await expireStaleSessions(); + + const session = sessions.get(sessionId); + if (!session || session.ownerId !== AGENT_OWNER_ID) { + throw new Error('Browser session not found.'); + } + + if (session.status !== 'ready') { + throw new Error(session.message || 'Browser session is not available.'); + } + + const handle = handles.get(sessionId); + if (!handle?.page) { + throw new Error('Browser runtime handle is not available.'); + } + + const url = normalizeUrl(rawUrl); + await handle.page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30_000 }); + session.lastAction = `navigate:${url}`; + session.cursor = null; + await captureSession(session, handle.page); + return publicSession(session); + }, + + async agentSnapshot(sessionId: string) { + const session = await this.getAgentSession(sessionId); + const handle = handles.get(sessionId); + if (!handle?.page) { + throw new Error('Browser runtime handle is not available.'); + } + await captureSession(session, handle.page); + const text = await handle.page.locator('body').innerText({ timeout: 5_000 }).catch(() => ''); + return { + session: publicSession(session), + text: text.slice(0, 30_000), + }; + }, + + async agentClick(sessionId: string, input: { selector?: string; text?: string; x?: number; y?: number }) { + const session = await this.getAgentSession(sessionId); + const handle = handles.get(sessionId); + if (!handle?.page) { + throw new Error('Browser runtime handle is not available.'); + } + const point = await getActionPoint(handle.page, input); + + if (input.selector) { + await handle.page.locator(input.selector).first().click({ timeout: 10_000 }); + } else if (input.text) { + await handle.page.getByText(input.text, { exact: false }).first().click({ timeout: 10_000 }); + } else if (typeof input.x === 'number' && typeof input.y === 'number') { + await handle.page.mouse.click(input.x, input.y); + } else { + throw new Error('Provide selector, text, or x/y coordinates.'); + } + + session.lastAction = 'click'; + session.cursor = point ? { ...point, actor: 'agent' } : null; + await captureSession(session, handle.page); + return publicSession(session); + }, + + async agentType(sessionId: string, input: { selector?: string; text: string; submit?: boolean }) { + const session = await this.getAgentSession(sessionId); + const handle = handles.get(sessionId); + if (!handle?.page) { + throw new Error('Browser runtime handle is not available.'); + } + + if (input.selector) { + await handle.page.locator(input.selector).first().fill(input.text, { timeout: 10_000 }); + session.cursor = await getActionPoint(handle.page, input).then((point) => ( + point ? { ...point, actor: 'agent' as const } : null + )); + } else { + await handle.page.keyboard.type(input.text); + } + if (input.submit) { + await handle.page.keyboard.press('Enter'); + } + + session.lastAction = 'type'; + await captureSession(session, handle.page); + return publicSession(session); + }, + + async agentFillForm(sessionId: string, fields: Array<{ selector: string; value: string }>) { + const session = await this.getAgentSession(sessionId); + const handle = handles.get(sessionId); + if (!handle?.page) { + throw new Error('Browser runtime handle is not available.'); + } + for (const field of fields) { + await handle.page.locator(field.selector).first().fill(field.value, { timeout: 10_000 }); + } + session.lastAction = 'fill_form'; + if (fields[0]) { + session.cursor = await getActionPoint(handle.page, { selector: fields[0].selector }).then((point) => ( + point ? { ...point, actor: 'agent' as const } : null + )); + } + await captureSession(session, handle.page); + return publicSession(session); + }, + + async agentPressKey(sessionId: string, key: string) { + const session = await this.getAgentSession(sessionId); + const handle = handles.get(sessionId); + if (!handle?.page) { + throw new Error('Browser runtime handle is not available.'); + } + await handle.page.keyboard.press(key); + session.lastAction = `press_key:${key}`; + await captureSession(session, handle.page); + return publicSession(session); + }, + + async agentSelectOption(sessionId: string, selector: string, values: string[]) { + const session = await this.getAgentSession(sessionId); + const handle = handles.get(sessionId); + if (!handle?.page) { + throw new Error('Browser runtime handle is not available.'); + } + await handle.page.locator(selector).first().selectOption(values, { timeout: 10_000 }); + session.lastAction = 'select_option'; + session.cursor = await getActionPoint(handle.page, { selector }).then((point) => ( + point ? { ...point, actor: 'agent' as const } : null + )); + await captureSession(session, handle.page); + return publicSession(session); + }, + + async agentWaitFor(sessionId: string, input: { text?: string; url?: string; timeoutMs?: number }) { + const session = await this.getAgentSession(sessionId); + const handle = handles.get(sessionId); + if (!handle?.page) { + throw new Error('Browser runtime handle is not available.'); + } + const timeout = Math.max(250, Math.min(input.timeoutMs || 5_000, 30_000)); + if (input.text) { + await handle.page.getByText(input.text, { exact: false }).first().waitFor({ timeout }); + } else if (input.url) { + await handle.page.waitForURL(input.url, { timeout }); + } else { + await handle.page.waitForTimeout(timeout); + } + session.lastAction = 'wait_for'; + await captureSession(session, handle.page); + return publicSession(session); + }, + + async agentTabs(sessionId: string, input: { action?: 'list' | 'new' | 'select' | 'close'; index?: number; url?: string }) { + const session = await this.getAgentSession(sessionId); + const handle = handles.get(sessionId); + if (!handle?.context || !handle?.page) { + throw new Error('Browser runtime handle is not available.'); + } + const action = input.action || 'list'; + if (action === 'new') { + const page = await handle.context.newPage(); + handles.set(sessionId, { ...handle, page }); + if (input.url) { + await this.agentNavigate(sessionId, input.url); + } + } else if (action === 'select') { + const page = handle.context.pages()[input.index || 0]; + if (!page) { + throw new Error('Tab not found.'); + } + handles.set(sessionId, { ...handle, page }); + } else if (action === 'close') { + const pages = handle.context.pages(); + const page = pages[input.index ?? pages.indexOf(handle.page)]; + if (!page) { + throw new Error('Tab not found.'); + } + await page.close(); + handles.set(sessionId, { ...handle, page: handle.context.pages()[0] || await handle.context.newPage() }); + } + const updatedHandle = handles.get(sessionId); + await captureSession(session, updatedHandle?.page || handle.page); + return { + session: publicSession(session), + tabs: handle.context.pages().map((page: any, index: number) => ({ + index, + url: page.url(), + active: page === (updatedHandle?.page || handle.page), + })), + }; + }, + + async stopSession(sessionId: string) { + const session = sessions.get(sessionId); + if (!session || session.ownerId !== AGENT_OWNER_ID) { + return { stopped: false }; + } + + await closeHandle(sessionId); + + session.status = 'stopped'; + session.updatedAt = new Date().toISOString(); + session.lastAction = 'stop'; + session.message = 'Browser session stopped. Create a new session to continue browsing.'; + return { stopped: true, session: publicSession(session) }; + }, + + async deleteSession(sessionId: string) { + const session = sessions.get(sessionId); + if (!session || session.ownerId !== AGENT_OWNER_ID) { + return { deleted: false }; + } + + await closeHandle(sessionId); + sessions.delete(sessionId); + return { deleted: true, sessionId }; + }, + + async agentStopSession(sessionId: string) { + await this.getAgentSession(sessionId); + return this.stopSession(sessionId); + }, + + async stopAllSessions() { + await Promise.all([...sessions.keys()].map(async (sessionId) => { + await closeHandle(sessionId); + const session = sessions.get(sessionId); + if (session) { + session.status = 'stopped'; + session.updatedAt = new Date().toISOString(); + session.lastAction = 'shutdown'; + session.message = 'Browser session stopped during server shutdown.'; + } + })); + }, +}; + +process.once('beforeExit', () => { + void browserUseService.stopAllSessions(); +}); diff --git a/server/modules/browser-use/tests/browser-use.service.test.ts b/server/modules/browser-use/tests/browser-use.service.test.ts new file mode 100644 index 00000000..b15365f1 --- /dev/null +++ b/server/modules/browser-use/tests/browser-use.service.test.ts @@ -0,0 +1,10 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; + +import { browserUseService } from '@/modules/browser-use/browser-use.service.js'; + +test('browser monitor list starts empty without agent sessions', async () => { + const sessions = await browserUseService.listSessions(); + + assert.deepEqual(sessions, []); +}); diff --git a/server/modules/providers/index.ts b/server/modules/providers/index.ts index 0d8d8edd..e057e200 100644 --- a/server/modules/providers/index.ts +++ b/server/modules/providers/index.ts @@ -1,5 +1,6 @@ export { sessionSynchronizerService } from './services/session-synchronizer.service.js'; export { providerSkillsService } from './services/skills.service.js'; +export { providerMcpService } from './services/mcp.service.js'; export { initializeSessionsWatcher } from './services/sessions-watcher.service.js'; export { closeSessionsWatcher } from './services/sessions-watcher.service.js'; diff --git a/server/modules/providers/services/mcp.service.ts b/server/modules/providers/services/mcp.service.ts index da7a5665..6e34e472 100644 --- a/server/modules/providers/services/mcp.service.ts +++ b/server/modules/providers/services/mcp.service.ts @@ -80,4 +80,30 @@ export const providerMcpService = { return results; }, + + /** + * Removes one MCP server from every provider. Mirrors `addMcpServerToAllProviders` + * by iterating the live provider registry, so callers stay in sync with which + * providers exist instead of maintaining their own provider list. + */ + async removeMcpServerFromAllProviders( + input: { name: string; scope?: McpScope; workspacePath?: string }, + ): Promise> { + const results: Array<{ provider: LLMProvider; removed: boolean; error?: string }> = []; + const providers = providerRegistry.listProviders(); + for (const provider of providers) { + try { + const result = await provider.mcp.removeServer(input); + results.push({ provider: provider.id, removed: result.removed }); + } catch (error) { + results.push({ + provider: provider.id, + removed: false, + error: error instanceof Error ? error.message : 'Unknown error', + }); + } + } + + return results; + }, }; diff --git a/src/components/app/AppContent.tsx b/src/components/app/AppContent.tsx index 8445a0b2..6cae890e 100644 --- a/src/components/app/AppContent.tsx +++ b/src/components/app/AppContent.tsx @@ -71,7 +71,6 @@ function AppContentInner() { setActiveTab, setSidebarOpen, setIsInputFocused, - setShowSettings, openSettings, refreshProjectsSilently, registerOptimisticSession, @@ -247,7 +246,7 @@ function AppContentInner() { onSessionEstablished={(targetSessionId, context) => registerOptimisticSession({ sessionId: targetSessionId, ...context }) } - onShowSettings={() => setShowSettings(true)} + onShowSettings={openSettings} externalMessageUpdate={externalMessageUpdate} newSessionTrigger={newSessionTrigger} /> diff --git a/src/components/browser-use/index.ts b/src/components/browser-use/index.ts new file mode 100644 index 00000000..a4e5be12 --- /dev/null +++ b/src/components/browser-use/index.ts @@ -0,0 +1 @@ +export { default as BrowserUsePanel } from './view/BrowserUsePanel'; diff --git a/src/components/browser-use/view/BrowserUsePanel.tsx b/src/components/browser-use/view/BrowserUsePanel.tsx new file mode 100644 index 00000000..b5b5f2a9 --- /dev/null +++ b/src/components/browser-use/view/BrowserUsePanel.tsx @@ -0,0 +1,536 @@ +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { + Bot, + Clock3, + Download, + Expand, + ExternalLink, + Loader2, + MonitorPlay, + RefreshCw, + Settings, + Square, + Trash2, + X, +} from 'lucide-react'; + +import { cn } from '../../../lib/utils'; +import { Badge, Button } from '../../../shared/view/ui'; +import { authenticatedFetch } from '../../../utils/api'; +import type { SettingsMainTab } from '../../settings/types/types'; + +type BrowserUseStatus = { + enabled: boolean; + available: boolean; + playwrightInstalled: boolean; + chromiumInstalled: boolean; + installInProgress: boolean; + sessionCount: number; + message: string; +}; + +type BrowserUseSession = { + id: string; + status: 'ready' | 'stopped' | 'unavailable'; + url: string | null; + title: string | null; + screenshotDataUrl: string | null; + createdAt: string; + updatedAt: string; + lastAction: string | null; + message: string | null; + createdBy: 'agent'; + profileName: string | null; + viewport: { + width: number; + height: number; + } | null; + cursor: { + x: number; + y: number; + actor: 'agent'; + } | null; +}; + +type BrowserUsePanelProps = { + isVisible: boolean; + onShowSettings?: (tab?: SettingsMainTab) => void; +}; + +async function readJson(response: Response): Promise { + const data = await response.json(); + if (!response.ok || data.success === false) { + throw new Error(data.error || data.details || `Request failed (${response.status})`); + } + return data as T; +} + +function formatRelativeTime(value: string | null): string { + if (!value) return 'Never'; + + const timestamp = Date.parse(value); + if (!Number.isFinite(timestamp)) return 'Unknown'; + + const elapsedSeconds = Math.max(0, Math.round((Date.now() - timestamp) / 1000)); + if (elapsedSeconds < 10) return 'Just now'; + if (elapsedSeconds < 60) return `${elapsedSeconds}s ago`; + const elapsedMinutes = Math.round(elapsedSeconds / 60); + if (elapsedMinutes < 60) return `${elapsedMinutes}m ago`; + const elapsedHours = Math.round(elapsedMinutes / 60); + if (elapsedHours < 24) return `${elapsedHours}h ago`; + return `${Math.round(elapsedHours / 24)}d ago`; +} + +function getDomain(url: string | null): string { + if (!url) return 'No page loaded'; + + try { + return new URL(url).hostname; + } catch { + return url; + } +} + +function formatAction(action: string | null): string { + if (!action) return 'Waiting'; + return action.replace(/_/g, ' ').replace(/:/g, ': '); +} + +function getStatusTone(status: BrowserUseSession['status']): string { + if (status === 'ready') { + return 'border-primary/30 bg-primary/5 text-foreground'; + } + if (status === 'stopped') { + return 'border-border bg-muted text-muted-foreground'; + } + return 'border-border bg-background text-muted-foreground'; +} + +function getRuntimeTone(status: BrowserUseStatus | null, installing: boolean): string { + if (!status?.enabled) return 'border-border bg-muted text-muted-foreground'; + if (status.available) return 'border-primary/30 bg-primary/5 text-foreground'; + if (status.installInProgress || installing) return 'border-primary/30 bg-primary/5 text-foreground'; + return 'border-border bg-background text-muted-foreground'; +} + +function getStatusDot(status: BrowserUseSession['status']): string { + if (status === 'ready') return 'bg-primary'; + if (status === 'stopped') return 'bg-muted-foreground/50'; + return 'bg-border'; +} + +const PROMPTS = [ + 'Use Browser to inspect the checkout flow and report any broken UI states.', + 'Open with Browser, interact with the page, and summarize what changed after each step.', +]; + +export default function BrowserUsePanel({ isVisible, onShowSettings }: BrowserUsePanelProps) { + const [status, setStatus] = useState(null); + const [sessions, setSessions] = useState([]); + const [selectedSessionId, setSelectedSessionId] = useState(null); + const [isRefreshing, setIsRefreshing] = useState(false); + const [isBusy, setIsBusy] = useState(false); + const [isInstalling, setIsInstalling] = useState(false); + const [isFullscreen, setIsFullscreen] = useState(false); + const [error, setError] = useState(null); + + const selectedSession = useMemo( + () => sessions.find((session) => session.id === selectedSessionId) || sessions[0] || null, + [selectedSessionId, sessions], + ); + + const activeSessions = sessions.filter((session) => session.status === 'ready'); + const needsBrowserBinaries = Boolean(status?.enabled && (!status.playwrightInstalled || !status.chromiumInstalled)); + const runtimeLabel = !status?.enabled + ? 'Disabled' + : status.available + ? 'Ready' + : status.installInProgress || isInstalling + ? 'Installing' + : 'Setup required'; + + const cursorStyle = selectedSession?.cursor && selectedSession.viewport + ? { + left: `${(selectedSession.cursor.x / selectedSession.viewport.width) * 100}%`, + top: `${(selectedSession.cursor.y / selectedSession.viewport.height) * 100}%`, + } + : null; + + const refresh = useCallback(async () => { + setIsRefreshing(true); + try { + const [statusResponse, sessionsResponse] = await Promise.all([ + authenticatedFetch('/api/browser-use/status'), + authenticatedFetch('/api/browser-use/sessions'), + ]); + const statusData = await readJson<{ data: BrowserUseStatus }>(statusResponse); + const sessionsData = await readJson<{ data: { sessions: BrowserUseSession[] } }>(sessionsResponse); + const nextSessions = sessionsData.data.sessions; + setStatus(statusData.data); + setSessions(nextSessions); + setSelectedSessionId((current) => ( + current && nextSessions.some((session) => session.id === current) + ? current + : nextSessions[0]?.id || null + )); + setError(null); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to load Browser'); + } finally { + setIsRefreshing(false); + } + }, []); + + useEffect(() => { + if (!isVisible) return; + void refresh(); + }, [isVisible, refresh]); + + const runAction = useCallback(async (action: () => Promise) => { + setIsBusy(true); + setError(null); + try { + await action(); + await refresh(); + } catch (err) { + setError(err instanceof Error ? err.message : 'Browser action failed'); + } finally { + setIsBusy(false); + } + }, [refresh]); + + const stopSession = () => runAction(async () => { + if (!selectedSession) return; + const response = await authenticatedFetch(`/api/browser-use/sessions/${selectedSession.id}/stop`, { method: 'POST' }); + await readJson(response); + }); + + const deleteSession = () => runAction(async () => { + if (!selectedSession) return; + const response = await authenticatedFetch(`/api/browser-use/sessions/${selectedSession.id}`, { method: 'DELETE' }); + await readJson(response); + setIsFullscreen(false); + }); + + const installBrowserBinaries = () => runAction(async () => { + setIsInstalling(true); + try { + const response = await authenticatedFetch('/api/browser-use/runtime/install', { method: 'POST' }); + await readJson(response); + } finally { + setIsInstalling(false); + } + }); + + const renderSessionItem = (session: BrowserUseSession) => { + const isSelected = selectedSession?.id === session.id; + return ( + + ); + }; + + const renderEmptyState = () => ( +
+
+
+
+ +
+
+
+ {status?.enabled ? 'No browser sessions yet' : 'Browser is disabled'} +
+

+ {status?.enabled + ? 'Agent browser sessions appear here while an AI task is using Browser.' + : 'Enable Browser in settings to let agents open monitored browser sessions.'} +

+
+
+ + {needsBrowserBinaries && ( +
+
Runtime setup required
+

{status?.message}

+ +
+ )} + +
+ {PROMPTS.map((prompt) => ( +
+
+ + Prompt +
+

{prompt}

+
+ ))} +
+
+
+ ); + + const renderBrowserSurface = (fullscreen = false) => ( +
+ {selectedSession?.screenshotDataUrl ? ( +
+ Browser session screenshot + {cursorStyle && ( +
+
+
+ )} +
+ ) : ( +
+ +
{selectedSession?.message || 'Waiting for screenshot'}
+

The next agent browser snapshot will render here.

+
+ )} +
+ ); + + return ( +
+
+
+
+ +

Browser

+ + {runtimeLabel} + +
+

Monitor browser sessions opened by AI agents.

+
+
+ {onShowSettings && ( + + )} + +
+
+ + {error && ( +
+ {error} +
+ )} + + {sessions.length > 0 && ( +
+
+ {sessions.map((session) => ( + + ))} +
+
+ )} + +
+
+
+
+ {activeSessions.length} active + / + {sessions.length} total +
+
+ Updated {formatRelativeTime(selectedSession?.updatedAt || null)} +
+
+ + {sessions.length === 0 ? ( + renderEmptyState() + ) : ( +
+
+
+ + {selectedSession?.status || 'empty'} + +
+
+ {selectedSession?.title || getDomain(selectedSession?.url || null)} +
+
+ + {selectedSession?.url || 'No page loaded'} +
+
+
+ {formatAction(selectedSession?.lastAction || null)} +
+ + + +
+ {renderBrowserSurface()} +
+
+ )} +
+ + +
+ + {isFullscreen && selectedSession && ( +
+
+
+
{selectedSession.title || selectedSession.url || 'Browser session'}
+ +
+ {renderBrowserSurface(true)} +
+
+ )} +
+ ); +} diff --git a/src/components/main-content/types/types.ts b/src/components/main-content/types/types.ts index a7398795..6fd3ac7d 100644 --- a/src/components/main-content/types/types.ts +++ b/src/components/main-content/types/types.ts @@ -7,6 +7,7 @@ import type { SessionActivityMap, } from '../../../hooks/useSessionProtection'; import type { SessionEstablishedContext, SessionNavigationOptions } from '../../chat/types/types'; +import type { SettingsMainTab } from '../../settings/types/types'; export type TaskMasterTask = { id: string | number; @@ -53,7 +54,7 @@ export type MainContentProps = { processingSessions: SessionActivityMap; onNavigateToSession: (targetSessionId: string, options?: SessionNavigationOptions) => void; onSessionEstablished: (sessionId: string, context: SessionEstablishedContext) => void; - onShowSettings: () => void; + onShowSettings: (tab?: SettingsMainTab) => void; externalMessageUpdate: number; newSessionTrigger: number; }; @@ -64,6 +65,7 @@ export type MainContentHeaderProps = { selectedProject: Project; selectedSession: ProjectSession | null; shouldShowTasksTab: boolean; + shouldShowBrowserTab: boolean; isMobile: boolean; onMenuClick: () => void; }; diff --git a/src/components/main-content/view/MainContent.tsx b/src/components/main-content/view/MainContent.tsx index c085daee..9d780623 100644 --- a/src/components/main-content/view/MainContent.tsx +++ b/src/components/main-content/view/MainContent.tsx @@ -1,15 +1,17 @@ -import React, { useEffect } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import ChatInterface from '../../chat/view/ChatInterface'; import FileTree from '../../file-tree/view/FileTree'; import StandaloneShell from '../../standalone-shell/view/StandaloneShell'; import GitPanel from '../../git-panel/view/GitPanel'; import PluginTabContent from '../../plugins/view/PluginTabContent'; +import { BrowserUsePanel } from '../../browser-use'; import type { MainContentProps } from '../types/types'; import { useTaskMaster } from '../../../contexts/TaskMasterContext'; import { usePaletteOpsRegister } from '../../../contexts/PaletteOpsContext'; import { useTasksSettings } from '../../../contexts/TasksSettingsContext'; import { useUiPreferences } from '../../../hooks/useUiPreferences'; +import { authenticatedFetch } from '../../../utils/api'; import { useEditorSidebar } from '../../code-editor/hooks/useEditorSidebar'; import EditorSidebar from '../../code-editor/view/EditorSidebar'; import type { Project } from '../../../types/app'; @@ -55,8 +57,10 @@ function MainContent({ const { currentProject, setCurrentProject } = useTaskMaster() as TaskMasterContextValue; const { tasksEnabled, isTaskMasterInstalled } = useTasksSettings() as TasksSettingsContextValue; + const [browserUseEnabled, setBrowserUseEnabled] = useState(false); const shouldShowTasksTab = Boolean(tasksEnabled && isTaskMasterInstalled); + const shouldShowBrowserTab = browserUseEnabled; const { editingFile, @@ -90,6 +94,28 @@ function MainContent({ } }, [shouldShowTasksTab, activeTab, setActiveTab]); + const loadBrowserUseSettings = useCallback(async () => { + try { + const response = await authenticatedFetch('/api/browser-use/settings'); + const data = await response.json(); + setBrowserUseEnabled(Boolean(response.ok && data?.success !== false && data?.data?.settings?.enabled)); + } catch { + setBrowserUseEnabled(false); + } + }, []); + + useEffect(() => { + void loadBrowserUseSettings(); + window.addEventListener('browserUseSettingsChanged', loadBrowserUseSettings); + return () => window.removeEventListener('browserUseSettingsChanged', loadBrowserUseSettings); + }, [loadBrowserUseSettings]); + + useEffect(() => { + if (!shouldShowBrowserTab && activeTab === 'browser') { + setActiveTab('chat'); + } + }, [shouldShowBrowserTab, activeTab, setActiveTab]); + usePaletteOpsRegister({ openFile: (filePath: string) => { setActiveTab('files'); @@ -113,6 +139,7 @@ function MainContent({ selectedProject={selectedProject} selectedSession={selectedSession} shouldShowTasksTab={shouldShowTasksTab} + shouldShowBrowserTab={shouldShowBrowserTab} isMobile={isMobile} onMenuClick={onMenuClick} /> @@ -171,7 +198,11 @@ function MainContent({ {shouldShowTasksTab && } -
+ {shouldShowBrowserTab && activeTab === 'browser' && ( +
+ +
+ )} {activeTab.startsWith('plugin:') && (
diff --git a/src/components/main-content/view/subcomponents/MainContentHeader.tsx b/src/components/main-content/view/subcomponents/MainContentHeader.tsx index a9025c2b..f75013ce 100644 --- a/src/components/main-content/view/subcomponents/MainContentHeader.tsx +++ b/src/components/main-content/view/subcomponents/MainContentHeader.tsx @@ -10,6 +10,7 @@ export default function MainContentHeader({ selectedProject, selectedSession, shouldShowTasksTab, + shouldShowBrowserTab, isMobile, onMenuClick, }: MainContentHeaderProps) { @@ -59,6 +60,7 @@ export default function MainContentHeader({ activeTab={activeTab} setActiveTab={setActiveTab} shouldShowTasksTab={shouldShowTasksTab} + shouldShowBrowserTab={shouldShowBrowserTab} />
{canScrollRight && ( diff --git a/src/components/main-content/view/subcomponents/MainContentTabSwitcher.tsx b/src/components/main-content/view/subcomponents/MainContentTabSwitcher.tsx index 51a5d649..bffe39d6 100644 --- a/src/components/main-content/view/subcomponents/MainContentTabSwitcher.tsx +++ b/src/components/main-content/view/subcomponents/MainContentTabSwitcher.tsx @@ -1,6 +1,7 @@ -import { MessageSquare, Terminal, Folder, GitBranch, ClipboardCheck, type LucideIcon } from 'lucide-react'; +import { MessageSquare, Terminal, Folder, GitBranch, ClipboardCheck, MonitorPlay, type LucideIcon } from 'lucide-react'; import type { Dispatch, SetStateAction } from 'react'; import { useTranslation } from 'react-i18next'; + import { Tooltip, PillBar, Pill } from '../../../../shared/view/ui'; import type { AppTab } from '../../../../types/app'; import { usePlugins } from '../../../../contexts/PluginsContext'; @@ -10,6 +11,7 @@ type MainContentTabSwitcherProps = { activeTab: AppTab; setActiveTab: Dispatch>; shouldShowTasksTab: boolean; + shouldShowBrowserTab: boolean; }; type BuiltInTab = { @@ -36,6 +38,13 @@ const BASE_TABS: BuiltInTab[] = [ { kind: 'builtin', id: 'git', labelKey: 'tabs.git', icon: GitBranch }, ]; +const BROWSER_TAB: BuiltInTab = { + kind: 'builtin', + id: 'browser', + labelKey: 'tabs.browser', + icon: MonitorPlay, +}; + const TASKS_TAB: BuiltInTab = { kind: 'builtin', id: 'tasks', @@ -47,11 +56,16 @@ export default function MainContentTabSwitcher({ activeTab, setActiveTab, shouldShowTasksTab, + shouldShowBrowserTab, }: MainContentTabSwitcherProps) { const { t } = useTranslation(); const { plugins } = usePlugins(); - const builtInTabs: BuiltInTab[] = shouldShowTasksTab ? [...BASE_TABS, TASKS_TAB] : BASE_TABS; + const builtInTabs: BuiltInTab[] = [ + ...BASE_TABS, + ...(shouldShowBrowserTab ? [BROWSER_TAB] : []), + ...(shouldShowTasksTab ? [TASKS_TAB] : []), + ]; const pluginTabs: PluginTab[] = plugins .filter((p) => p.enabled) diff --git a/src/components/main-content/view/subcomponents/MainContentTitle.tsx b/src/components/main-content/view/subcomponents/MainContentTitle.tsx index 6cc88ba1..b05da336 100644 --- a/src/components/main-content/view/subcomponents/MainContentTitle.tsx +++ b/src/components/main-content/view/subcomponents/MainContentTitle.tsx @@ -1,4 +1,5 @@ import { useTranslation } from 'react-i18next'; + import SessionProviderLogo from '../../../llm-logo-provider/SessionProviderLogo'; import type { AppTab, Project, ProjectSession } from '../../../../types/app'; import { usePlugins } from '../../../../contexts/PluginsContext'; @@ -27,6 +28,10 @@ function getTabTitle(activeTab: AppTab, shouldShowTasksTab: boolean, t: (key: st return 'TaskMaster'; } + if (activeTab === 'browser') { + return 'Browser'; + } + return 'Project'; } diff --git a/src/components/mcp/view/McpServers.tsx b/src/components/mcp/view/McpServers.tsx index 8ec9d03e..a77117fe 100644 --- a/src/components/mcp/view/McpServers.tsx +++ b/src/components/mcp/view/McpServers.tsx @@ -52,6 +52,11 @@ const getServerKey = (server: ProviderMcpServer): string => ( `${server.provider}:${server.scope}:${server.workspacePath || 'global'}:${server.name}` ); +// Servers prefixed with `cloudcli-` are written and removed automatically by a +// CloudCLI feature toggle (e.g. the Browser tab), not added by the user. They are +// shown read-only so users don't edit/delete them out of sync with the feature. +const isManagedServer = (server: ProviderMcpServer): boolean => server.name.startsWith('cloudcli-'); + function ConfigLine({ label, children }: { label: string; children: string }) { if (!children) { return null; @@ -177,65 +182,92 @@ export default function McpServers({ selectedProvider, currentProjects }: McpSer
Loading MCP servers...
)} - {servers.map((server) => ( -
-
-
-
- {getTransportIcon(server.transport)} - {server.name} - - {server.transport || 'stdio'} - - - {getScopeLabel(server.scope)} - - {server.projectDisplayName && ( - - {server.projectDisplayName} - - )} + {servers.map((server) => { + const managed = isManagedServer(server); + + return ( +
+
+
+
+ {!managed && getTransportIcon(server.transport)} + {server.name} + {!managed && ( + <> + + {server.transport || 'stdio'} + + + {getScopeLabel(server.scope)} + + {server.projectDisplayName && ( + + {server.projectDisplayName} + + )} + + )} + {managed && ( + + + {t('mcpServers.managed.badge', { defaultValue: 'Managed' })} + + )} +
+ +
+ {!managed && ( + <> + {server.command || ''} + {server.url || ''} + {(server.args || []).join(' ')} + {server.cwd || ''} + {server.env && Object.keys(server.env).length > 0 && ( + + {Object.entries(server.env).map(([key, value]) => `${key}=${maskSecret(value)}`).join(', ')} + + )} + {server.envVars && server.envVars.length > 0 && ( + {server.envVars.join(', ')} + )} + + )} + {managed && ( +
+ {t('mcpServers.managed.hint', { + defaultValue: 'Managed by CloudCLI.', + })} +
+ )} +
-
- {server.command || ''} - {server.url || ''} - {(server.args || []).join(' ')} - {server.cwd || ''} - {server.env && Object.keys(server.env).length > 0 && ( - - {Object.entries(server.env).map(([key, value]) => `${key}=${maskSecret(value)}`).join(', ')} - - )} - {server.envVars && server.envVars.length > 0 && ( - {server.envVars.join(', ')} - )} -
-
- -
- - + {!managed && ( +
+ + +
+ )}
-
- ))} + ); + })} {!isLoading && !isLoadingProjectScopes && servers.length === 0 && (
{t('mcpServers.empty')}
diff --git a/src/components/settings/constants/constants.ts b/src/components/settings/constants/constants.ts index 7d4c353d..8e5bfebe 100644 --- a/src/components/settings/constants/constants.ts +++ b/src/components/settings/constants/constants.ts @@ -6,6 +6,7 @@ import { Info, KeyRound, ListChecks, + MonitorPlay, Palette, Plug, } from 'lucide-react'; @@ -32,6 +33,7 @@ export const SETTINGS_MAIN_TABS: SettingsMainTabMeta[] = [ { id: 'git', label: 'Git', keywords: 'git github commits', icon: GitBranch }, { id: 'api', label: 'API Tokens', keywords: 'api tokens auth keys', icon: KeyRound }, { id: 'tasks', label: 'Tasks', keywords: 'tasks taskmaster', icon: ListChecks }, + { id: 'browser', label: 'Browser', keywords: 'browser playwright chromium automation', icon: MonitorPlay }, { id: 'notifications', label: 'Notifications', keywords: 'notifications alerts push', icon: Bell }, { id: 'plugins', label: 'Plugins', keywords: 'plugins extensions integrations', icon: Plug }, { id: 'about', label: 'About', keywords: 'about version info', icon: Info }, diff --git a/src/components/settings/hooks/useSettingsController.ts b/src/components/settings/hooks/useSettingsController.ts index 70e9ed1c..a172b831 100644 --- a/src/components/settings/hooks/useSettingsController.ts +++ b/src/components/settings/hooks/useSettingsController.ts @@ -54,7 +54,7 @@ type NotificationPreferencesResponse = { type ActiveLoginProvider = AgentProvider | ''; -const KNOWN_MAIN_TABS: SettingsMainTab[] = ['agents', 'appearance', 'git', 'api', 'tasks', 'notifications', 'plugins']; +const KNOWN_MAIN_TABS: SettingsMainTab[] = ['agents', 'appearance', 'git', 'api', 'tasks', 'browser', 'notifications', 'plugins', 'about']; const normalizeMainTab = (tab: string): SettingsMainTab => { // Keep backwards compatibility with older callers that still pass "tools". diff --git a/src/components/settings/types/types.ts b/src/components/settings/types/types.ts index f68cacc4..672be1ee 100644 --- a/src/components/settings/types/types.ts +++ b/src/components/settings/types/types.ts @@ -3,7 +3,7 @@ import type { Dispatch, SetStateAction } from 'react'; import type { LLMProvider } from '../../../types/app'; import type { ProviderAuthStatus } from '../../provider-auth/types'; -export type SettingsMainTab = 'agents' | 'appearance' | 'git' | 'api' | 'tasks' | 'notifications' | 'plugins' | 'about'; +export type SettingsMainTab = 'agents' | 'appearance' | 'git' | 'api' | 'tasks' | 'browser' | 'notifications' | 'plugins' | 'about'; export type AgentProvider = LLMProvider; export type AgentCategory = 'account' | 'permissions' | 'mcp'; export type ProjectSortOrder = 'name' | 'date'; diff --git a/src/components/settings/view/Settings.tsx b/src/components/settings/view/Settings.tsx index 8340a547..800440e0 100644 --- a/src/components/settings/view/Settings.tsx +++ b/src/components/settings/view/Settings.tsx @@ -7,6 +7,7 @@ import AgentsSettingsTab from '../view/tabs/agents-settings/AgentsSettingsTab'; import AppearanceSettingsTab from '../view/tabs/AppearanceSettingsTab'; import CredentialsSettingsTab from '../view/tabs/api-settings/CredentialsSettingsTab'; import GitSettingsTab from '../view/tabs/git-settings/GitSettingsTab'; +import BrowserUseSettingsTab from '../view/tabs/browser-use-settings/BrowserUseSettingsTab'; import NotificationsSettingsTab from '../view/tabs/NotificationsSettingsTab'; import TasksSettingsTab from '../view/tabs/tasks-settings/TasksSettingsTab'; import PluginSettingsTab from '../../plugins/view/PluginSettingsTab'; @@ -139,17 +140,19 @@ function Settings({ isOpen, onClose, projects = [], initialTab = 'agents' }: Set {activeTab === 'tasks' && } - {activeTab === 'notifications' && ( - - )} + {activeTab === 'browser' && } + + {activeTab === 'notifications' && ( + + )} {activeTab === 'api' && } diff --git a/src/components/settings/view/SettingsSidebar.tsx b/src/components/settings/view/SettingsSidebar.tsx index 149c1492..dde32a9e 100644 --- a/src/components/settings/view/SettingsSidebar.tsx +++ b/src/components/settings/view/SettingsSidebar.tsx @@ -1,4 +1,4 @@ -import { Bell, Bot, GitBranch, Info, Key, ListChecks, Palette, Puzzle } from 'lucide-react'; +import { Bell, Bot, GitBranch, Info, Key, ListChecks, MonitorPlay, Palette, Puzzle } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { cn } from '../../../lib/utils'; import { PillBar, Pill } from '../../../shared/view/ui'; @@ -21,6 +21,7 @@ const NAV_ITEMS: NavItem[] = [ { id: 'git', labelKey: 'mainTabs.git', icon: GitBranch }, { id: 'api', labelKey: 'mainTabs.apiTokens', icon: Key }, { id: 'tasks', labelKey: 'mainTabs.tasks', icon: ListChecks }, + { id: 'browser', labelKey: 'mainTabs.browser', icon: MonitorPlay }, { id: 'plugins', labelKey: 'mainTabs.plugins', icon: Puzzle }, { id: 'notifications', labelKey: 'mainTabs.notifications', icon: Bell }, { id: 'about', labelKey: 'mainTabs.about', icon: Info }, diff --git a/src/components/settings/view/tabs/browser-use-settings/BrowserUseSettingsTab.tsx b/src/components/settings/view/tabs/browser-use-settings/BrowserUseSettingsTab.tsx new file mode 100644 index 00000000..da94ffe2 --- /dev/null +++ b/src/components/settings/view/tabs/browser-use-settings/BrowserUseSettingsTab.tsx @@ -0,0 +1,185 @@ +import { useCallback, useEffect, useState } from 'react'; +import { Download, Loader2 } from 'lucide-react'; + +import { Button } from '../../../../../shared/view/ui'; +import { authenticatedFetch } from '../../../../../utils/api'; +import SettingsCard from '../../SettingsCard'; +import SettingsRow from '../../SettingsRow'; +import SettingsSection from '../../SettingsSection'; +import SettingsToggle from '../../SettingsToggle'; + +type BrowserUseSettings = { + enabled: boolean; +}; + +type BrowserUseStatus = { + enabled: boolean; + available: boolean; + playwrightInstalled: boolean; + chromiumInstalled: boolean; + installInProgress: boolean; + message: string; +}; + +async function readJson(response: Response): Promise { + const data = await response.json(); + if (!response.ok || data.success === false) { + throw new Error(data.error || data.details || `Request failed (${response.status})`); + } + return data as T; +} + +export default function BrowserUseSettingsTab() { + const [settings, setSettings] = useState(null); + const [status, setStatus] = useState(null); + const [isSettingsLoading, setIsSettingsLoading] = useState(true); + const [isStatusLoading, setIsStatusLoading] = useState(true); + const [isSaving, setIsSaving] = useState(false); + const [isInstalling, setIsInstalling] = useState(false); + const [error, setError] = useState(null); + + const loadSettings = useCallback(async () => { + const settingsResponse = await authenticatedFetch('/api/browser-use/settings'); + const settingsData = await readJson<{ data: { settings: BrowserUseSettings } }>(settingsResponse); + setSettings(settingsData.data.settings); + }, []); + + const loadStatus = useCallback(async () => { + const statusResponse = await authenticatedFetch('/api/browser-use/status'); + const statusData = await readJson<{ data: BrowserUseStatus }>(statusResponse); + setStatus(statusData.data); + }, []); + + useEffect(() => { + setError(null); + setIsSettingsLoading(true); + setIsStatusLoading(true); + + void loadSettings() + .catch((err) => setError(err instanceof Error ? err.message : 'Failed to load Browser settings')) + .finally(() => setIsSettingsLoading(false)); + + void loadStatus() + .catch((err) => setError(err instanceof Error ? err.message : 'Failed to load Browser status')) + .finally(() => setIsStatusLoading(false)); + }, [loadSettings, loadStatus]); + + const updateSettings = async (nextSettings: Partial) => { + setIsSaving(true); + setError(null); + try { + const response = await authenticatedFetch('/api/browser-use/settings', { + method: 'PUT', + body: JSON.stringify(nextSettings), + }); + const data = await readJson<{ data: { settings: BrowserUseSettings } }>(response); + setSettings(data.data.settings); + window.dispatchEvent(new Event('browserUseSettingsChanged')); + setIsStatusLoading(true); + await loadStatus(); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to save Browser settings'); + } finally { + setIsStatusLoading(false); + setIsSaving(false); + } + }; + + const installBrowserBinaries = async () => { + setIsInstalling(true); + setError(null); + try { + const response = await authenticatedFetch('/api/browser-use/runtime/install', { method: 'POST' }); + await readJson(response); + setIsStatusLoading(true); + await loadStatus(); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to install browser runtime'); + } finally { + setIsStatusLoading(false); + setIsInstalling(false); + } + }; + + const browserEnabled = settings?.enabled === true; + const needsBrowserBinaries = Boolean(browserEnabled && status && (!status.playwrightInstalled || !status.chromiumInstalled)); + const runtimeLabel = (installed?: boolean) => { + if (isStatusLoading && !status) { + return 'checking...'; + } + return installed ? 'installed' : 'missing'; + }; + + return ( +
+ + + + {isSettingsLoading && !settings ? ( + + ) : ( + void updateSettings({ enabled: value })} + ariaLabel="Enable Browser" + disabled={isSaving} + /> + )} + + +
+
+ + Playwright: {runtimeLabel(status?.playwrightInstalled)} + + + Chromium: {runtimeLabel(status?.chromiumInstalled)} + + + Status: {isStatusLoading && !status ? 'checking...' : status?.available ? 'ready' : browserEnabled ? 'setup required' : 'disabled'} + +
+ + {needsBrowserBinaries && ( +
+
+
Browser runtime required
+

+ {status?.message || 'Install the browser runtime before agents can create Browser sessions.'} +

+
+ + +
+ )} + + {error && ( +
+ {error} +
+ )} +
+
+
+
+ ); +} diff --git a/src/components/sidebar/view/subcomponents/SidebarFooter.tsx b/src/components/sidebar/view/subcomponents/SidebarFooter.tsx index b024be30..ef1c7178 100644 --- a/src/components/sidebar/view/subcomponents/SidebarFooter.tsx +++ b/src/components/sidebar/view/subcomponents/SidebarFooter.tsx @@ -69,7 +69,7 @@ export default function SidebarFooter({ onClick={onShowVersionModal} >
- +
@@ -145,12 +145,12 @@ export default function SidebarFooter({ href={GITHUB_ISSUES_URL} target="_blank" rel="noopener noreferrer" - className="flex h-12 w-full items-center gap-3.5 rounded-xl bg-muted/40 px-4 transition-all hover:bg-muted/60 active:scale-[0.98]" + className="flex h-10 w-full items-center gap-3 rounded-xl bg-muted/40 px-3.5 transition-all hover:bg-muted/60 active:scale-[0.98]" > -
- +
+
- {t('actions.reportIssue')} + {t('actions.reportIssue')}
@@ -160,25 +160,25 @@ export default function SidebarFooter({ href={DISCORD_INVITE_URL} target="_blank" rel="noopener noreferrer" - className="flex h-12 w-full items-center gap-3.5 rounded-xl bg-muted/40 px-4 transition-all hover:bg-muted/60 active:scale-[0.98]" + className="flex h-10 w-full items-center gap-3 rounded-xl bg-muted/40 px-3.5 transition-all hover:bg-muted/60 active:scale-[0.98]" > -
- +
+
- {t('actions.joinCommunity')} + {t('actions.joinCommunity')}
{/* Mobile settings */}
diff --git a/src/hooks/useProjectsState.ts b/src/hooks/useProjectsState.ts index d5561fbb..a2cdbd06 100644 --- a/src/hooks/useProjectsState.ts +++ b/src/hooks/useProjectsState.ts @@ -324,7 +324,7 @@ const removeSessionFromProject = (project: Project, sessionIdToDelete: string): return updatedProject; }; -const VALID_TABS: Set = new Set(['chat', 'files', 'shell', 'git', 'tasks', 'preview']); +const VALID_TABS: Set = new Set(['chat', 'files', 'shell', 'git', 'tasks', 'browser']); const isValidTab = (tab: string): tab is AppTab => { return VALID_TABS.has(tab) || tab.startsWith('plugin:'); @@ -776,7 +776,7 @@ export function useProjectsState({ (session: ProjectSession) => { setSelectedSession(session); - if (activeTab === 'tasks' || activeTab === 'preview') { + if (activeTab === 'tasks' || activeTab === 'browser') { setActiveTab('chat'); } diff --git a/src/i18n/locales/de/common.json b/src/i18n/locales/de/common.json index 94260cff..636d2e8d 100644 --- a/src/i18n/locales/de/common.json +++ b/src/i18n/locales/de/common.json @@ -22,7 +22,8 @@ "shell": "Terminal", "files": "Dateien", "git": "Quellcodeverwaltung", - "tasks": "Aufgaben" + "tasks": "Aufgaben", + "browser": "Browser" }, "status": { "loading": "Lädt...", diff --git a/src/i18n/locales/en/common.json b/src/i18n/locales/en/common.json index f35eb3c5..9137da9a 100644 --- a/src/i18n/locales/en/common.json +++ b/src/i18n/locales/en/common.json @@ -22,7 +22,8 @@ "shell": "Shell", "files": "Files", "git": "Source Control", - "tasks": "Tasks" + "tasks": "Tasks", + "browser": "Browser" }, "status": { "loading": "Loading...", diff --git a/src/i18n/locales/en/settings.json b/src/i18n/locales/en/settings.json index d5bc7900..c9513adf 100644 --- a/src/i18n/locales/en/settings.json +++ b/src/i18n/locales/en/settings.json @@ -94,6 +94,7 @@ "git": "Git", "apiTokens": "API & Tokens", "tasks": "Tasks", + "browser": "Browser", "notifications": "Notifications", "plugins": "Plugins", "about": "About" @@ -450,6 +451,10 @@ "edit": "Edit server", "delete": "Delete server" }, + "managed": { + "badge": "Managed", + "hint": "Managed by CloudCLI." + }, "help": { "title": "About Codex MCP", "description": "Codex supports stdio-based MCP servers. You can add servers that extend Codex's capabilities with additional tools and resources." diff --git a/src/i18n/locales/it/common.json b/src/i18n/locales/it/common.json index 7993c69d..79f9f675 100644 --- a/src/i18n/locales/it/common.json +++ b/src/i18n/locales/it/common.json @@ -22,7 +22,8 @@ "shell": "Terminale", "files": "File", "git": "Controllo Versione", - "tasks": "Attività" + "tasks": "Attività", + "browser": "Browser" }, "status": { "loading": "Caricamento...", diff --git a/src/i18n/locales/ja/common.json b/src/i18n/locales/ja/common.json index 0651e25c..498ee46c 100644 --- a/src/i18n/locales/ja/common.json +++ b/src/i18n/locales/ja/common.json @@ -22,7 +22,8 @@ "shell": "シェル", "files": "ファイル", "git": "ソース管理", - "tasks": "タスク" + "tasks": "タスク", + "browser": "Browser" }, "status": { "loading": "読み込み中...", diff --git a/src/i18n/locales/ko/common.json b/src/i18n/locales/ko/common.json index fcf1de52..03244458 100644 --- a/src/i18n/locales/ko/common.json +++ b/src/i18n/locales/ko/common.json @@ -22,7 +22,8 @@ "shell": "Shell", "files": "파일", "git": "소스 관리", - "tasks": "작업" + "tasks": "작업", + "browser": "Browser" }, "status": { "loading": "로딩 중...", diff --git a/src/i18n/locales/ru/common.json b/src/i18n/locales/ru/common.json index 24331f4a..fc71abe1 100644 --- a/src/i18n/locales/ru/common.json +++ b/src/i18n/locales/ru/common.json @@ -22,7 +22,8 @@ "shell": "Терминал", "files": "Файлы", "git": "Система контроля версий", - "tasks": "Задачи" + "tasks": "Задачи", + "browser": "Browser" }, "status": { "loading": "Загрузка...", diff --git a/src/i18n/locales/tr/common.json b/src/i18n/locales/tr/common.json index 3b9a6d27..f1fa66b9 100644 --- a/src/i18n/locales/tr/common.json +++ b/src/i18n/locales/tr/common.json @@ -22,7 +22,8 @@ "shell": "Shell", "files": "Dosyalar", "git": "Kaynak Kontrolü", - "tasks": "Görevler" + "tasks": "Görevler", + "browser": "Browser" }, "status": { "loading": "Yükleniyor...", diff --git a/src/i18n/locales/zh-CN/common.json b/src/i18n/locales/zh-CN/common.json index 05e0369d..69cd159a 100644 --- a/src/i18n/locales/zh-CN/common.json +++ b/src/i18n/locales/zh-CN/common.json @@ -22,7 +22,8 @@ "shell": "终端", "files": "文件", "git": "源代码管理", - "tasks": "任务" + "tasks": "任务", + "browser": "Browser" }, "status": { "loading": "加载中...", diff --git a/src/i18n/locales/zh-TW/common.json b/src/i18n/locales/zh-TW/common.json index ea05e41e..419be285 100644 --- a/src/i18n/locales/zh-TW/common.json +++ b/src/i18n/locales/zh-TW/common.json @@ -22,7 +22,8 @@ "shell": "終端機", "files": "檔案", "git": "版本控制", - "tasks": "任務" + "tasks": "任務", + "browser": "Browser" }, "status": { "loading": "載入中...", diff --git a/src/types/app.ts b/src/types/app.ts index 42b9d28b..f81c3e26 100644 --- a/src/types/app.ts +++ b/src/types/app.ts @@ -17,7 +17,7 @@ export type ProviderModelsCacheInfo = { source: 'memory' | 'disk' | 'fresh'; }; -export type AppTab = 'chat' | 'files' | 'shell' | 'git' | 'tasks' | 'preview' | `plugin:${string}`; +export type AppTab = 'chat' | 'files' | 'shell' | 'git' | 'tasks' | 'browser' | `plugin:${string}`; export interface ProjectSession { id: string;