diff --git a/package-lock.json b/package-lock.json index 36b632e5af..ab847dedd2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1727,29 +1727,6 @@ "node": ">=8" } }, - "node_modules/@joshua.litt/get-ripgrep": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@joshua.litt/get-ripgrep/-/get-ripgrep-0.0.3.tgz", - "integrity": "sha512-rycdieAKKqXi2bsM7G2ayDiNk5CAX8ZOzsTQsirfOqUKPef04Xw40BWGGyimaOOuvPgLWYt3tPnLLG3TvPXi5Q==", - "license": "MIT", - "dependencies": { - "@lvce-editor/verror": "^1.6.0", - "execa": "^9.5.2", - "extract-zip": "^2.0.1", - "fs-extra": "^11.3.0", - "got": "^14.4.5", - "path-exists": "^5.0.0", - "xdg-basedir": "^5.1.0" - } - }, - "node_modules/@joshua.litt/get-ripgrep/node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -1932,12 +1909,6 @@ "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", "license": "MIT" }, - "node_modules/@lvce-editor/verror": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@lvce-editor/verror/-/verror-1.7.0.tgz", - "integrity": "sha512-+LGuAEIC2L7pbvkyAQVWM2Go0dAy+UWEui28g07zNtZsCBhm+gusBK8PNwLJLV5Jay+TyUYuwLIbJdjLLzqEBg==", - "license": "MIT" - }, "node_modules/@lydell/node-pty": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@lydell/node-pty/-/node-pty-1.1.0.tgz", @@ -3590,18 +3561,6 @@ "url": "https://ko-fi.com/killymxi" } }, - "node_modules/@sindresorhus/is": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.0.2.tgz", - "integrity": "sha512-d9xRovfKNz1SKieM0qJdO+PQonjnnIfSNWfHYnBSJ9hkjm0ZPw6HlxscDXYstp3z+7V2GOFHc+J0CYrYTjqCJw==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, "node_modules/@sindresorhus/merge-streams": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", @@ -3614,18 +3573,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@szmarczak/http-timer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", - "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", - "license": "MIT", - "dependencies": { - "defer-to-connect": "^2.0.1" - }, - "engines": { - "node": ">=14.16" - } - }, "node_modules/@textlint/ast-node-types": { "version": "15.2.2", "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.2.2.tgz", @@ -3931,12 +3878,6 @@ "integrity": "sha512-pUY3cKH/Nm2yYrEmDlPR1mR7yszjGx4DrwPjQ702C4/D5CwHuZTgZdIdwPkRbcuhs7BAh2L5rg3CL5cbRiGTCQ==", "license": "MIT" }, - "node_modules/@types/http-cache-semantics": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", - "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", - "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", @@ -6008,33 +5949,6 @@ "node": ">=8" } }, - "node_modules/cacheable-lookup": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", - "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", - "license": "MIT", - "engines": { - "node": ">=14.16" - } - }, - "node_modules/cacheable-request": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-12.0.1.tgz", - "integrity": "sha512-Yo9wGIQUaAfIbk+qY0X4cDQgCosecfBe3V9NSyeY4qPC2SAkbCS4Xj79VP8WOzitpJUZKc/wsRCYF5ariDIwkg==", - "license": "MIT", - "dependencies": { - "@types/http-cache-semantics": "^4.0.4", - "get-stream": "^9.0.1", - "http-cache-semantics": "^4.1.1", - "keyv": "^4.5.4", - "mimic-response": "^4.0.0", - "normalize-url": "^8.0.1", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -6968,33 +6882,6 @@ } } }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/deep-eql": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", @@ -7057,15 +6944,6 @@ "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==", - "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", @@ -8428,9 +8306,9 @@ } }, "node_modules/execa": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.0.tgz", - "integrity": "sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.1.tgz", + "integrity": "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==", "license": "MIT", "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", @@ -8950,15 +8828,6 @@ "node": ">= 6" } }, - "node_modules/form-data-encoder": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-4.1.0.tgz", - "integrity": "sha512-G6NsmEW15s0Uw9XnCg+33H3ViYRyiM0hMrMhhqQOR8NFc5GhYrI+6I3u7OTw7b91J2g8rtvMBZJDbcGb2YUniw==", - "license": "MIT", - "engines": { - "node": ">= 18" - } - }, "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", @@ -9586,43 +9455,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/got": { - "version": "14.4.8", - "resolved": "https://registry.npmjs.org/got/-/got-14.4.8.tgz", - "integrity": "sha512-vxwU4HuR0BIl+zcT1LYrgBjM+IJjNElOjCzs0aPgHorQyr/V6H6Y73Sn3r3FOlUffvWD+Q5jtRuGWaXkU8Jbhg==", - "license": "MIT", - "dependencies": { - "@sindresorhus/is": "^7.0.1", - "@szmarczak/http-timer": "^5.0.1", - "cacheable-lookup": "^7.0.0", - "cacheable-request": "^12.0.1", - "decompress-response": "^6.0.0", - "form-data-encoder": "^4.0.2", - "http2-wrapper": "^2.2.1", - "lowercase-keys": "^3.0.0", - "p-cancelable": "^4.0.1", - "responselike": "^3.0.0", - "type-fest": "^4.26.1" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/got/node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -9878,12 +9710,6 @@ "entities": "^4.4.0" } }, - "node_modules/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", - "license": "BSD-2-Clause" - }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", @@ -9917,19 +9743,6 @@ "node": ">= 14" } }, - "node_modules/http2-wrapper": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", - "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", - "license": "MIT", - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.2.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", @@ -11040,6 +10853,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, "license": "MIT" }, "node_modules/json-parse-better-errors": { @@ -11235,6 +11049,7 @@ "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, "license": "MIT", "dependencies": { "json-buffer": "3.0.1" @@ -11699,18 +11514,6 @@ "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", "license": "MIT" }, - "node_modules/lowercase-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", - "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/lowlight": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-3.3.0.tgz", @@ -11981,18 +11784,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mimic-response": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", - "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/minimatch": { "version": "10.2.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", @@ -12371,18 +12162,6 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/normalize-url": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.2.tgz", - "integrity": "sha512-Ee/R3SyN4BuynXcnTaekmaVdbDAEiNrHqjQIA37mHU8G9pf7aaAD4ZX3XjBLo6rsdcxA/gtkcNYZLt30ACgynw==", - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/npm-normalize-package-bin": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", @@ -12895,15 +12674,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/p-cancelable": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-4.0.1.tgz", - "integrity": "sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==", - "license": "MIT", - "engines": { - "node": ">=14.16" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -13743,18 +13513,6 @@ ], "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==", - "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", @@ -14195,12 +13953,6 @@ "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==", - "license": "MIT" - }, "node_modules/resolve-dir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", @@ -14235,21 +13987,6 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, - "node_modules/responselike": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", - "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", - "license": "MIT", - "dependencies": { - "lowercase-keys": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/restore-cursor": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", @@ -17644,18 +17381,6 @@ } } }, - "node_modules/xdg-basedir": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", - "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/xml2js": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", @@ -18079,44 +17804,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "packages/cli/node_modules/execa": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.1.tgz", - "integrity": "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==", - "license": "MIT", - "dependencies": { - "@sindresorhus/merge-streams": "^4.0.0", - "cross-spawn": "^7.0.6", - "figures": "^6.1.0", - "get-stream": "^9.0.0", - "human-signals": "^8.0.1", - "is-plain-obj": "^4.1.0", - "is-stream": "^4.0.1", - "npm-run-path": "^6.0.0", - "pretty-ms": "^9.2.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^4.0.0", - "yoctocolors": "^2.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.5.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "packages/cli/node_modules/is-stream": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", - "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "packages/cli/node_modules/string-width": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", @@ -18162,7 +17849,6 @@ "@google/genai": "1.30.0", "@grpc/grpc-js": "^1.14.3", "@iarna/toml": "^2.2.5", - "@joshua.litt/get-ripgrep": "^0.0.3", "@modelcontextprotocol/sdk": "^1.23.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/api-logs": "^0.211.0", @@ -18190,6 +17876,7 @@ "diff": "^8.0.3", "dotenv": "^17.2.4", "dotenv-expand": "^12.0.3", + "execa": "^9.6.1", "fast-levenshtein": "^2.0.6", "fdir": "^6.4.6", "fzf": "^0.5.2", diff --git a/packages/core/package.json b/packages/core/package.json index d404e6c581..dc18347a04 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -20,7 +20,8 @@ "typecheck": "tsc --noEmit" }, "files": [ - "dist" + "dist", + "vendor" ], "dependencies": { "@a2a-js/sdk": "0.3.11", @@ -31,7 +32,6 @@ "@google/genai": "1.30.0", "@grpc/grpc-js": "^1.14.3", "@iarna/toml": "^2.2.5", - "@joshua.litt/get-ripgrep": "^0.0.3", "@modelcontextprotocol/sdk": "^1.23.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/api-logs": "^0.211.0", @@ -59,6 +59,7 @@ "diff": "^8.0.3", "dotenv": "^17.2.4", "dotenv-expand": "^12.0.3", + "execa": "^9.6.1", "fast-levenshtein": "^2.0.6", "fdir": "^6.4.6", "fzf": "^0.5.2", diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts index c19cc257c3..a9c0b813ee 100644 --- a/packages/core/src/config/config.ts +++ b/packages/core/src/config/config.ts @@ -3552,6 +3552,7 @@ export class Config implements McpContext, AgentLoopContext { registry.registerTool(new RipGrepTool(this, this.messageBus)), ); } else { + debugLogger.warn(`Ripgrep is not available. Falling back to GrepTool.`); logRipgrepFallback(this, new RipgrepFallbackEvent(errorString)); maybeRegister(GrepTool, () => registry.registerTool(new GrepTool(this, this.messageBus)), diff --git a/packages/core/src/tools/ripGrep.test.ts b/packages/core/src/tools/ripGrep.test.ts index 000e3db3e1..9ad575833a 100644 --- a/packages/core/src/tools/ripGrep.test.ts +++ b/packages/core/src/tools/ripGrep.test.ts @@ -4,20 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { - describe, - it, - expect, - beforeEach, - afterEach, - afterAll, - vi, -} from 'vitest'; +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import { canUseRipgrep, RipGrepTool, ensureRgPath, type RipGrepToolParams, + getRipgrepPath, } from './ripGrep.js'; import type { GrepResult } from './tools.js'; import path from 'node:path'; @@ -25,18 +18,21 @@ import { isSubpath } from '../utils/paths.js'; import fs from 'node:fs/promises'; import os from 'node:os'; import type { Config } from '../config/config.js'; -import { Storage } from '../config/storage.js'; import { GEMINI_IGNORE_FILE_NAME } from '../config/constants.js'; import { createMockWorkspaceContext } from '../test-utils/mockWorkspaceContext.js'; import { spawn, type ChildProcess } from 'node:child_process'; import { PassThrough, Readable } from 'node:stream'; import EventEmitter from 'node:events'; -import { downloadRipGrep } from '@joshua.litt/get-ripgrep'; import { createMockMessageBus } from '../test-utils/mock-message-bus.js'; -// Mock dependencies for canUseRipgrep -vi.mock('@joshua.litt/get-ripgrep', () => ({ - downloadRipGrep: vi.fn(), -})); +import { fileExists } from '../utils/fileUtils.js'; + +vi.mock('../utils/fileUtils.js', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + fileExists: vi.fn(), + }; +}); // Mock child_process for ripgrep calls vi.mock('child_process', () => ({ @@ -44,161 +40,42 @@ vi.mock('child_process', () => ({ })); const mockSpawn = vi.mocked(spawn); -const downloadRipGrepMock = vi.mocked(downloadRipGrep); -const originalGetGlobalBinDir = Storage.getGlobalBinDir.bind(Storage); -const storageSpy = vi.spyOn(Storage, 'getGlobalBinDir'); - -function getRipgrepBinaryName() { - return process.platform === 'win32' ? 'rg.exe' : 'rg'; -} describe('canUseRipgrep', () => { - let tempRootDir: string; - let binDir: string; - - beforeEach(async () => { - downloadRipGrepMock.mockReset(); - downloadRipGrepMock.mockResolvedValue(undefined); - tempRootDir = await fs.mkdtemp(path.join(os.tmpdir(), 'ripgrep-bin-')); - binDir = path.join(tempRootDir, 'bin'); - await fs.mkdir(binDir, { recursive: true }); - storageSpy.mockImplementation(() => binDir); - }); - - afterEach(async () => { - storageSpy.mockImplementation(() => originalGetGlobalBinDir()); - await fs.rm(tempRootDir, { recursive: true, force: true }); + beforeEach(() => { + vi.mocked(fileExists).mockReset(); }); it('should return true if ripgrep already exists', async () => { - const existingPath = path.join(binDir, getRipgrepBinaryName()); - await fs.writeFile(existingPath, ''); - + vi.mocked(fileExists).mockResolvedValue(true); const result = await canUseRipgrep(); expect(result).toBe(true); - expect(downloadRipGrepMock).not.toHaveBeenCalled(); }); - it('should download ripgrep and return true if it does not exist initially', async () => { - const expectedPath = path.join(binDir, getRipgrepBinaryName()); - - downloadRipGrepMock.mockImplementation(async () => { - await fs.writeFile(expectedPath, ''); - }); - + it('should return false if file does not exist', async () => { + vi.mocked(fileExists).mockResolvedValue(false); const result = await canUseRipgrep(); - - expect(result).toBe(true); - expect(downloadRipGrep).toHaveBeenCalledWith(binDir); - await expect(fs.access(expectedPath)).resolves.toBeUndefined(); - }); - - it('should return false if download fails and file does not exist', async () => { - const result = await canUseRipgrep(); - expect(result).toBe(false); - expect(downloadRipGrep).toHaveBeenCalledWith(binDir); - }); - - it('should propagate errors from downloadRipGrep', async () => { - const error = new Error('Download failed'); - downloadRipGrepMock.mockRejectedValue(error); - - await expect(canUseRipgrep()).rejects.toThrow(error); - expect(downloadRipGrep).toHaveBeenCalledWith(binDir); - }); - - it('should only download once when called concurrently', async () => { - const expectedPath = path.join(binDir, getRipgrepBinaryName()); - - downloadRipGrepMock.mockImplementation( - () => - new Promise((resolve, reject) => { - setTimeout(() => { - fs.writeFile(expectedPath, '') - .then(() => resolve()) - .catch(reject); - }, 0); - }), - ); - - const firstCall = ensureRgPath(); - const secondCall = ensureRgPath(); - - const [pathOne, pathTwo] = await Promise.all([firstCall, secondCall]); - - expect(pathOne).toBe(expectedPath); - expect(pathTwo).toBe(expectedPath); - expect(downloadRipGrepMock).toHaveBeenCalledTimes(1); - await expect(fs.access(expectedPath)).resolves.toBeUndefined(); }); }); describe('ensureRgPath', () => { - let tempRootDir: string; - let binDir: string; - - beforeEach(async () => { - downloadRipGrepMock.mockReset(); - downloadRipGrepMock.mockResolvedValue(undefined); - tempRootDir = await fs.mkdtemp(path.join(os.tmpdir(), 'ripgrep-bin-')); - binDir = path.join(tempRootDir, 'bin'); - await fs.mkdir(binDir, { recursive: true }); - storageSpy.mockImplementation(() => binDir); - }); - - afterEach(async () => { - storageSpy.mockImplementation(() => originalGetGlobalBinDir()); - await fs.rm(tempRootDir, { recursive: true, force: true }); + beforeEach(() => { + vi.mocked(fileExists).mockReset(); }); it('should return rg path if ripgrep already exists', async () => { - const existingPath = path.join(binDir, getRipgrepBinaryName()); - await fs.writeFile(existingPath, ''); - + vi.mocked(fileExists).mockResolvedValue(true); const rgPath = await ensureRgPath(); - expect(rgPath).toBe(existingPath); - expect(downloadRipGrep).not.toHaveBeenCalled(); + expect(rgPath).toBe(await getRipgrepPath()); }); - it('should return rg path if ripgrep is downloaded successfully', async () => { - const expectedPath = path.join(binDir, getRipgrepBinaryName()); - - downloadRipGrepMock.mockImplementation(async () => { - await fs.writeFile(expectedPath, ''); - }); - - const rgPath = await ensureRgPath(); - expect(rgPath).toBe(expectedPath); - expect(downloadRipGrep).toHaveBeenCalledTimes(1); - await expect(fs.access(expectedPath)).resolves.toBeUndefined(); + it('should throw an error if ripgrep cannot be used', async () => { + vi.mocked(fileExists).mockResolvedValue(false); + await expect(ensureRgPath()).rejects.toThrow( + /Cannot find bundled ripgrep binary/, + ); }); - - it('should throw an error if ripgrep cannot be used after download attempt', async () => { - await expect(ensureRgPath()).rejects.toThrow('Cannot use ripgrep.'); - expect(downloadRipGrep).toHaveBeenCalledTimes(1); - }); - - it('should propagate errors from downloadRipGrep', async () => { - const error = new Error('Download failed'); - downloadRipGrepMock.mockRejectedValue(error); - - await expect(ensureRgPath()).rejects.toThrow(error); - expect(downloadRipGrep).toHaveBeenCalledWith(binDir); - }); - - it.runIf(process.platform === 'win32')( - 'should detect ripgrep when only rg.exe exists on Windows', - async () => { - const expectedRgExePath = path.join(binDir, 'rg.exe'); - await fs.writeFile(expectedRgExePath, ''); - - const rgPath = await ensureRgPath(); - expect(rgPath).toBe(expectedRgExePath); - expect(downloadRipGrep).not.toHaveBeenCalled(); - await expect(fs.access(expectedRgExePath)).resolves.toBeUndefined(); - }, - ); }); // Helper function to create mock spawn implementations @@ -247,9 +124,6 @@ function createMockSpawn( describe('RipGrepTool', () => { let tempRootDir: string; - let tempBinRoot: string; - let binDir: string; - let ripgrepBinaryPath: string; let grepTool: RipGrepTool; const abortSignal = new AbortController().signal; @@ -266,19 +140,12 @@ describe('RipGrepTool', () => { } as unknown as Config; beforeEach(async () => { - downloadRipGrepMock.mockReset(); - downloadRipGrepMock.mockResolvedValue(undefined); mockSpawn.mockReset(); mockSpawn.mockImplementation(createMockSpawn()); - tempBinRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'ripgrep-bin-')); - binDir = path.join(tempBinRoot, 'bin'); - await fs.mkdir(binDir, { recursive: true }); - const binaryName = process.platform === 'win32' ? 'rg.exe' : 'rg'; - ripgrepBinaryPath = path.join(binDir, binaryName); - await fs.writeFile(ripgrepBinaryPath, ''); - storageSpy.mockImplementation(() => binDir); tempRootDir = await fs.mkdtemp(path.join(os.tmpdir(), 'grep-tool-root-')); + vi.mocked(fileExists).mockResolvedValue(true); + mockConfig = { getTargetDir: () => tempRootDir, getWorkspaceContext: () => createMockWorkspaceContext(tempRootDir), @@ -335,9 +202,7 @@ describe('RipGrepTool', () => { }); afterEach(async () => { - storageSpy.mockImplementation(() => originalGetGlobalBinDir()); await fs.rm(tempRootDir, { recursive: true, force: true }); - await fs.rm(tempBinRoot, { recursive: true, force: true }); }); describe('validateToolParams', () => { @@ -834,16 +699,16 @@ describe('RipGrepTool', () => { }); it('should throw an error if ripgrep is not available', async () => { - await fs.rm(ripgrepBinaryPath, { force: true }); - downloadRipGrepMock.mockResolvedValue(undefined); + vi.mocked(fileExists).mockResolvedValue(false); const params: RipGrepToolParams = { pattern: 'world' }; const invocation = grepTool.build(params); - expect(await invocation.execute({ abortSignal })).toStrictEqual({ - llmContent: 'Error during grep search operation: Cannot use ripgrep.', - returnDisplay: 'Error: Cannot use ripgrep.', - }); + const result = await invocation.execute({ abortSignal }); + expect(result.llmContent).toContain('Cannot find bundled ripgrep binary'); + + // restore the mock for subsequent tests + vi.mocked(fileExists).mockResolvedValue(true); }); }); @@ -2080,6 +1945,68 @@ describe('RipGrepTool', () => { }); }); -afterAll(() => { - storageSpy.mockRestore(); +describe('getRipgrepPath', () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + describe('OS/Architecture Resolution', () => { + it.each([ + { platform: 'darwin', arch: 'arm64', expectedBin: 'rg-darwin-arm64' }, + { platform: 'darwin', arch: 'x64', expectedBin: 'rg-darwin-x64' }, + { platform: 'linux', arch: 'arm64', expectedBin: 'rg-linux-arm64' }, + { platform: 'linux', arch: 'x64', expectedBin: 'rg-linux-x64' }, + { platform: 'win32', arch: 'x64', expectedBin: 'rg-win32-x64.exe' }, + ])( + 'should map $platform $arch to $expectedBin', + async ({ platform, arch, expectedBin }) => { + vi.spyOn(os, 'platform').mockReturnValue(platform as NodeJS.Platform); + vi.spyOn(os, 'arch').mockReturnValue(arch); + vi.mocked(fileExists).mockImplementation(async (checkPath) => + checkPath.endsWith(expectedBin), + ); + + const resolvedPath = await getRipgrepPath(); + expect(resolvedPath).not.toBeNull(); + expect(resolvedPath?.endsWith(expectedBin)).toBe(true); + }, + ); + }); + + describe('Path Fallback Logic', () => { + beforeEach(() => { + vi.spyOn(os, 'platform').mockReturnValue('linux'); + vi.spyOn(os, 'arch').mockReturnValue('x64'); + }); + + it('should resolve the SEA (flattened) path first', async () => { + vi.mocked(fileExists).mockImplementation(async (checkPath) => + checkPath.includes(path.normalize('tools/vendor/ripgrep')), + ); + + const resolvedPath = await getRipgrepPath(); + expect(resolvedPath).not.toBeNull(); + expect(resolvedPath).toContain(path.normalize('tools/vendor/ripgrep')); + }); + + it('should fall back to the Dev path if SEA path is missing', async () => { + vi.mocked(fileExists).mockImplementation( + async (checkPath) => + checkPath.includes(path.normalize('core/vendor/ripgrep')) && + !checkPath.includes('tools'), + ); + + const resolvedPath = await getRipgrepPath(); + expect(resolvedPath).not.toBeNull(); + expect(resolvedPath).toContain(path.normalize('core/vendor/ripgrep')); + expect(resolvedPath).not.toContain('tools'); + }); + + it('should return null if binary is missing from both paths', async () => { + vi.mocked(fileExists).mockResolvedValue(false); + + const resolvedPath = await getRipgrepPath(); + expect(resolvedPath).toBeNull(); + }); + }); }); diff --git a/packages/core/src/tools/ripGrep.ts b/packages/core/src/tools/ripGrep.ts index 4449a7a08a..c2ae482289 100644 --- a/packages/core/src/tools/ripGrep.ts +++ b/packages/core/src/tools/ripGrep.ts @@ -8,7 +8,8 @@ import type { MessageBus } from '../confirmation-bus/message-bus.js'; import fs from 'node:fs'; import fsPromises from 'node:fs/promises'; import path from 'node:path'; -import { downloadRipGrep } from '@joshua.litt/get-ripgrep'; +import os from 'node:os'; +import { fileURLToPath } from 'node:url'; import { BaseDeclarativeTool, BaseToolInvocation, @@ -22,7 +23,6 @@ import { makeRelative, shortenPath } from '../utils/paths.js'; import { getErrorMessage, isNodeError } from '../utils/errors.js'; import type { Config } from '../config/config.js'; import { fileExists } from '../utils/fileUtils.js'; -import { Storage } from '../config/storage.js'; import { GREP_TOOL_NAME } from './tool-names.js'; import { debugLogger } from '../utils/debugLogger.js'; import { @@ -39,73 +39,48 @@ import { RIP_GREP_DEFINITION } from './definitions/coreTools.js'; import { resolveToolDeclaration } from './definitions/resolver.js'; import { type GrepMatch, formatGrepResults } from './grep-utils.js'; -function getRgCandidateFilenames(): readonly string[] { - return process.platform === 'win32' ? ['rg.exe', 'rg'] : ['rg']; -} +const __dirname = path.dirname(fileURLToPath(import.meta.url)); -async function resolveExistingRgPath(): Promise { - const binDir = Storage.getGlobalBinDir(); - for (const fileName of getRgCandidateFilenames()) { - const candidatePath = path.join(binDir, fileName); - if (await fileExists(candidatePath)) { - return candidatePath; +export async function getRipgrepPath(): Promise { + const platform = os.platform(); + const arch = os.arch(); + + // Map to the correct bundled binary + const binName = `rg-${platform}-${arch}${platform === 'win32' ? '.exe' : ''}`; + + const candidatePaths = [ + // 1. SEA runtime layout: everything is flattened into the root dir + path.resolve(__dirname, 'vendor/ripgrep', binName), + // 2. Dev/Dist layout: packages/core/dist/tools/ripGrep.js -> packages/core/vendor/ripgrep + path.resolve(__dirname, '../../vendor/ripgrep', binName), + ]; + + for (const candidate of candidatePaths) { + if (await fileExists(candidate)) { + return candidate; } } + return null; } -let ripgrepAcquisitionPromise: Promise | null = null; /** - * Ensures a ripgrep binary is available. - * - * NOTE: - * - The Gemini CLI currently prefers a managed ripgrep binary downloaded - * into its global bin directory. - * - Even if ripgrep is available on the system PATH, it is intentionally - * not used at this time. - * - * Preference for system-installed ripgrep is blocked on: - * - checksum verification of external binaries - * - internalization of the get-ripgrep dependency - * - * See: - * - feat(core): Prefer rg in system path (#11847) - * - Move get-ripgrep to third_party (#12099) - */ -async function ensureRipgrepAvailable(): Promise { - const existingPath = await resolveExistingRgPath(); - if (existingPath) { - return existingPath; - } - if (!ripgrepAcquisitionPromise) { - ripgrepAcquisitionPromise = (async () => { - try { - await downloadRipGrep(Storage.getGlobalBinDir()); - return await resolveExistingRgPath(); - } finally { - ripgrepAcquisitionPromise = null; - } - })(); - } - return ripgrepAcquisitionPromise; -} - -/** - * Checks if `rg` exists, if not then attempt to download it. + * Checks if `rg` exists in the bundled vendor directory. */ export async function canUseRipgrep(): Promise { - return (await ensureRipgrepAvailable()) !== null; + const binPath = await getRipgrepPath(); + return binPath !== null; } /** - * Ensures `rg` is downloaded, or throws. + * Ensures `rg` is available, or throws. */ export async function ensureRgPath(): Promise { - const downloadedPath = await ensureRipgrepAvailable(); - if (downloadedPath) { - return downloadedPath; + const binPath = await getRipgrepPath(); + if (binPath !== null) { + return binPath; } - throw new Error('Cannot use ripgrep.'); + throw new Error(`Cannot find bundled ripgrep binary.`); } /** diff --git a/packages/core/vendor/ripgrep/rg-darwin-arm64 b/packages/core/vendor/ripgrep/rg-darwin-arm64 new file mode 100755 index 0000000000..e163565822 Binary files /dev/null and b/packages/core/vendor/ripgrep/rg-darwin-arm64 differ diff --git a/packages/core/vendor/ripgrep/rg-darwin-x64 b/packages/core/vendor/ripgrep/rg-darwin-x64 new file mode 100755 index 0000000000..ef047368a7 Binary files /dev/null and b/packages/core/vendor/ripgrep/rg-darwin-x64 differ diff --git a/packages/core/vendor/ripgrep/rg-linux-arm64 b/packages/core/vendor/ripgrep/rg-linux-arm64 new file mode 100755 index 0000000000..38c7ec9ae0 Binary files /dev/null and b/packages/core/vendor/ripgrep/rg-linux-arm64 differ diff --git a/packages/core/vendor/ripgrep/rg-linux-x64 b/packages/core/vendor/ripgrep/rg-linux-x64 new file mode 100755 index 0000000000..acf3d8ef76 Binary files /dev/null and b/packages/core/vendor/ripgrep/rg-linux-x64 differ diff --git a/packages/core/vendor/ripgrep/rg-win32-x64.exe b/packages/core/vendor/ripgrep/rg-win32-x64.exe new file mode 100644 index 0000000000..bd0e08ee46 Binary files /dev/null and b/packages/core/vendor/ripgrep/rg-win32-x64.exe differ diff --git a/scripts/copy_bundle_assets.js b/scripts/copy_bundle_assets.js index ef6a68e58d..667e911f0e 100644 --- a/scripts/copy_bundle_assets.js +++ b/scripts/copy_bundle_assets.js @@ -108,4 +108,16 @@ if (!existsSync(bundleMcpSrc)) { cpSync(bundleMcpSrc, bundleMcpDest, { recursive: true, dereference: true }); console.log('Copied bundled chrome-devtools-mcp to bundle/bundled/'); +// 7. Copy pre-built ripgrep vendor binaries +const ripgrepVendorSrc = join(root, 'packages/core/vendor/ripgrep'); +const ripgrepVendorDest = join(bundleDir, 'vendor', 'ripgrep'); +if (existsSync(ripgrepVendorSrc)) { + mkdirSync(ripgrepVendorDest, { recursive: true }); + cpSync(ripgrepVendorSrc, ripgrepVendorDest, { + recursive: true, + dereference: true, + }); + console.log('Copied ripgrep vendor binaries to bundle/vendor/ripgrep/'); +} + console.log('Assets copied to bundle/'); diff --git a/scripts/download-ripgrep-binaries.ts b/scripts/download-ripgrep-binaries.ts new file mode 100644 index 0000000000..969d69c7eb --- /dev/null +++ b/scripts/download-ripgrep-binaries.ts @@ -0,0 +1,146 @@ +/** + * @license + * Copyright 2026 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview This script downloads pre-built ripgrep binaries for all supported + * architectures and platforms. These binaries are checked into the repository + * under packages/core/vendor/ripgrep. + * + * Maintainers should periodically run this script to upgrade the version + * of ripgrep being distributed. + * + * Usage: npx tsx scripts/download-ripgrep-binaries.ts + */ + +import fs from 'node:fs'; +import fsPromises from 'node:fs/promises'; +import path from 'node:path'; +import { pipeline } from 'node:stream/promises'; +import { fileURLToPath } from 'node:url'; +import { createWriteStream } from 'node:fs'; +import { Readable } from 'node:stream'; +import type { ReadableStream } from 'node:stream/web'; +import { execFileSync } from 'node:child_process'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const CORE_VENDOR_DIR = path.join(__dirname, '../packages/core/vendor/ripgrep'); +const VERSION = 'v13.0.0-10'; + +interface Target { + platform: string; + arch: string; + file: string; +} + +const targets: Target[] = [ + { platform: 'darwin', arch: 'arm64', file: 'aarch64-apple-darwin.tar.gz' }, + { platform: 'darwin', arch: 'x64', file: 'x86_64-apple-darwin.tar.gz' }, + { + platform: 'linux', + arch: 'arm64', + file: 'aarch64-unknown-linux-gnu.tar.gz', + }, + { platform: 'linux', arch: 'x64', file: 'x86_64-unknown-linux-musl.tar.gz' }, + { platform: 'win32', arch: 'x64', file: 'x86_64-pc-windows-msvc.zip' }, +]; + +async function downloadBinary() { + await fsPromises.mkdir(CORE_VENDOR_DIR, { recursive: true }); + + for (const target of targets) { + const url = `https://github.com/microsoft/ripgrep-prebuilt/releases/download/${VERSION}/ripgrep-${VERSION}-${target.file}`; + const archivePath = path.join(CORE_VENDOR_DIR, target.file); + const binName = `rg-${target.platform}-${target.arch}${target.platform === 'win32' ? '.exe' : ''}`; + const finalBinPath = path.join(CORE_VENDOR_DIR, binName); + + if (fs.existsSync(finalBinPath)) { + console.log(`[Cache] ${binName} already exists.`); + continue; + } + + console.log(`[Download] ${url} -> ${archivePath}`); + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Failed to fetch ${url}: ${response.statusText}`); + } + + if (!response.body) { + throw new Error(`Response body is null for ${url}`); + } + + const fileStream = createWriteStream(archivePath); + + // Node 18+ global fetch response.body is a ReadableStream (web stream) + // pipeline(Readable.fromWeb(response.body), fileStream) works in Node 18+ + await pipeline( + Readable.fromWeb(response.body as ReadableStream), + fileStream, + ); + + console.log(`[Extract] Extracting ${archivePath}...`); + // Extract using shell commands for simplicity + if (target.file.endsWith('.tar.gz')) { + execFileSync('tar', ['-xzf', archivePath, '-C', CORE_VENDOR_DIR]); + // Microsoft's ripgrep release extracts directly to `rg` inside the current directory sometimes + const sourceBin = path.join(CORE_VENDOR_DIR, 'rg'); + if (fs.existsSync(sourceBin)) { + await fsPromises.rename(sourceBin, finalBinPath); + } else { + // Fallback for sub-directory if it happens + const extractedDirName = `ripgrep-${VERSION}-${target.file.replace('.tar.gz', '')}`; + const fallbackSourceBin = path.join( + CORE_VENDOR_DIR, + extractedDirName, + 'rg', + ); + if (fs.existsSync(fallbackSourceBin)) { + await fsPromises.rename(fallbackSourceBin, finalBinPath); + await fsPromises.rm(path.join(CORE_VENDOR_DIR, extractedDirName), { + recursive: true, + force: true, + }); + } else { + throw new Error( + `Could not find extracted 'rg' binary for ${target.platform} ${target.arch}`, + ); + } + } + } else if (target.file.endsWith('.zip')) { + execFileSync('unzip', ['-o', '-q', archivePath, '-d', CORE_VENDOR_DIR]); + const sourceBin = path.join(CORE_VENDOR_DIR, 'rg.exe'); + if (fs.existsSync(sourceBin)) { + await fsPromises.rename(sourceBin, finalBinPath); + } else { + const extractedDirName = `ripgrep-${VERSION}-${target.file.replace('.zip', '')}`; + const fallbackSourceBin = path.join( + CORE_VENDOR_DIR, + extractedDirName, + 'rg.exe', + ); + if (fs.existsSync(fallbackSourceBin)) { + await fsPromises.rename(fallbackSourceBin, finalBinPath); + await fsPromises.rm(path.join(CORE_VENDOR_DIR, extractedDirName), { + recursive: true, + force: true, + }); + } else { + throw new Error( + `Could not find extracted 'rg.exe' binary for ${target.platform} ${target.arch}`, + ); + } + } + } + + // Clean up archive + await fsPromises.unlink(archivePath); + console.log(`[Success] Saved to ${finalBinPath}`); + } +} + +downloadBinary().catch((err) => { + console.error(err); + process.exit(1); +});