diff --git a/modules/babylonlabs-io-btc-staking-ts/README.md b/modules/babylonlabs-io-btc-staking-ts/README.md
index cecbcaff73..dfd8b1d100 100644
--- a/modules/babylonlabs-io-btc-staking-ts/README.md
+++ b/modules/babylonlabs-io-btc-staking-ts/README.md
@@ -1 +1 @@
-BitGo Fork of https://github.com/babylonlabs-io/btc-staking-ts/tree/v0.4.0-rc.2
\ No newline at end of file
+BitGo Fork of https://github.com/babylonlabs-io/btc-staking-ts/tree/v1.0.3
\ No newline at end of file
diff --git a/modules/babylonlabs-io-btc-staking-ts/src/index.ts b/modules/babylonlabs-io-btc-staking-ts/src/index.ts
index 026c91c5e6..883c517ce8 100644
--- a/modules/babylonlabs-io-btc-staking-ts/src/index.ts
+++ b/modules/babylonlabs-io-btc-staking-ts/src/index.ts
@@ -1,13 +1,21 @@
-export { StakingScriptData, Staking } from './staking';
-export type { StakingScripts } from './staking';
-export { ObservableStaking, ObservableStakingScriptData } from './staking/observable';
-export * from './staking/transactions';
-export * from './types';
-export * from './utils/btc';
-export * from './utils/babylon';
-export * from './utils/staking';
-export * from './utils/utxo/findInputUTXO';
-export * from './utils/utxo/getPsbtInputFields';
-export * from './utils/utxo/getScriptType';
-export { getBabylonParamByBtcHeight, getBabylonParamByVersion } from './utils/staking/param';
-export * from './staking/manager';
+export { StakingScriptData, Staking } from "./staking";
+export type { StakingScripts } from "./staking";
+export {
+ ObservableStaking,
+ ObservableStakingScriptData,
+} from "./staking/observable";
+export * from "./staking/transactions";
+export * from "./types";
+export * from "./utils/btc";
+export * from "./utils/utxo/findInputUTXO";
+export * from "./utils/utxo/getPsbtInputFields";
+export * from "./utils/utxo/getScriptType";
+export {
+ getBabylonParamByBtcHeight,
+ getBabylonParamByVersion,
+} from "./utils/staking/param";
+export * from "./staking/manager";
+
+// BitGo-specific exports
+export * from "./utils/babylon";
+export * from "./utils/staking";
\ No newline at end of file
diff --git a/package.json b/package.json
index ab2a00c26b..66b6dd2872 100644
--- a/package.json
+++ b/package.json
@@ -24,7 +24,7 @@
"eslint-plugin-import": "^2.19.1",
"eslint-plugin-jsdoc": "^33.0.0",
"eslint-plugin-prettier": "^3.4.0",
- "execa": "^5.0.0",
+ "execa": "^9.6.0",
"formidable": "3.5.4",
"glob": "^7.1.3",
"html-webpack-plugin": "^5.5.0",
diff --git a/scripts/vendor-github-repo.ts b/scripts/vendor-github-repo.ts
deleted file mode 100644
index 6122e332ec..0000000000
--- a/scripts/vendor-github-repo.ts
+++ /dev/null
@@ -1,112 +0,0 @@
-import { ChildProcess, execFile } from 'child_process';
-import * as fs from 'fs/promises';
-import * as tmp from 'tmp';
-import * as yargs from 'yargs';
-
-function isErrorExists(e: NodeJS.ErrnoException): boolean {
- return e.code === 'EEXIST';
-}
-
-type GithubSource = {
- org: string;
- repo: string;
-} & ({ tag: string } | { ref: string });
-
-async function wait(p: ChildProcess): Promise {
- p.stderr?.pipe(process.stderr);
- p.stdout?.pipe(process.stdout);
- return new Promise((resolve, reject) => {
- p.on('exit', (code) => {
- if (code === 0) {
- resolve();
- } else {
- reject(new Error(`Process exited with code ${code}`));
- }
- });
- });
-}
-
-function getUrl(lib: GithubSource): string {
- if ('tag' in lib) {
- const { org, repo, tag } = lib;
- return `https://github.com/${org}/${repo}/archive/refs/tags/${tag}.tar.gz`;
- }
- if ('ref' in lib) {
- const { org, repo, ref } = lib;
- return `https://github.com/${org}/${repo}/tarball/${ref}`;
- }
- throw new Error('Unsupported lib');
-}
-
-function getArchivePath(lib: GithubSource): string {
- return tmp.fileSync({ postfix: `-${lib.repo}.tar.gz` }).name;
-}
-
-async function fetchArchive(lib: GithubSource, outfile: string): Promise {
- try {
- const result = await fs.stat(outfile);
- if (result.size > 0) {
- console.log(`Archive already exists: ${outfile}`);
- return;
- }
- } catch (e) {}
- const url = getUrl(lib);
- const result = await fetch(url);
- if (!result.ok) {
- throw new Error(`Failed to fetch ${url}: ${result.status} ${result.statusText}`);
- }
- await fs.writeFile(outfile, Buffer.from(await result.arrayBuffer()));
-}
-
-async function extractArchive(archivePath: string, targetDir: string): Promise {
- try {
- await fs.mkdir(targetDir, { recursive: true });
- } catch (e) {
- if (!isErrorExists(e)) {
- throw e;
- }
- }
- await wait(execFile('tar', ['-C', targetDir, '--strip-components', '1', '-xzf', archivePath]));
-}
-
-type VendorConfig = GithubSource & {
- targetDir: string;
-};
-
-async function main(cfgs: VendorConfig[]) {
- for (const cfg of cfgs) {
- const archivePath = getArchivePath(cfg);
- await fetchArchive(cfg, archivePath);
- await extractArchive(archivePath, cfg.targetDir);
- }
-}
-
-const vendorConfigs: VendorConfig[] = [
- {
- org: 'babylonlabs-io',
- repo: 'btc-staking-ts',
- tag: 'v1.0.3',
- targetDir: 'modules/babylonlabs-io-btc-staking-ts',
- },
-];
-
-yargs
- .command({
- command: 'vendor',
- builder(a) {
- return a.options({ name: { type: 'string' } });
- },
- async handler(a) {
- const matches = vendorConfigs.filter((cfg) => a.name === cfg.repo);
- if (matches.length === 0) {
- throw new Error(`no such vendor config ${a.name}`);
- }
- if (matches.length > 1) {
- throw new Error(`ambiguous vendor config ${a.name}`);
- }
- await main(matches);
- },
- })
- .help()
- .strict()
- .demandCommand().argv;
diff --git a/scripts/vendor-github-repo/VendorConfig.ts b/scripts/vendor-github-repo/VendorConfig.ts
new file mode 100644
index 0000000000..eb5e42ae97
--- /dev/null
+++ b/scripts/vendor-github-repo/VendorConfig.ts
@@ -0,0 +1,9 @@
+export type GithubSource = {
+ org: string;
+ repo: string;
+} & ({ tag: string } | { ref: string });
+
+export type VendorConfig = GithubSource & {
+ targetDir: string;
+ postExtract?: (src: GithubSource, targetDir: string) => Promise;
+};
diff --git a/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-add-BitGo-specific.patch b/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-add-BitGo-specific.patch
new file mode 100644
index 0000000000..87d6e72683
--- /dev/null
+++ b/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-add-BitGo-specific.patch
@@ -0,0 +1,35 @@
+From 1ffd7fac756de23859d3f1b61c26619c597e5021 Mon Sep 17 00:00:00 2001
+From: Otto Allmendinger
+Date: Thu, 3 Jul 2025 11:03:24 +0200
+Subject: [PATCH] fix(babylonlabs-io-btc-staking-ts): add BitGo-specific
+ exports
+
+Add exports for babylon and staking utility functions needed by BitGo
+integration code.
+
+Issue: BTC-2143
+
+Co-authored-by: llm-git
+---
+ modules/babylonlabs-io-btc-staking-ts/src/index.ts | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/modules/babylonlabs-io-btc-staking-ts/src/index.ts b/modules/babylonlabs-io-btc-staking-ts/src/index.ts
+index cd14e42be..883c517ce 100644
+--- a/modules/babylonlabs-io-btc-staking-ts/src/index.ts
++++ b/modules/babylonlabs-io-btc-staking-ts/src/index.ts
+@@ -14,4 +14,8 @@ export {
+ getBabylonParamByBtcHeight,
+ getBabylonParamByVersion,
+ } from "./utils/staking/param";
+-export * from "./staking/manager";
+\ No newline at end of file
++export * from "./staking/manager";
++
++// BitGo-specific exports
++export * from "./utils/babylon";
++export * from "./utils/staking";
+\ No newline at end of file
+--
+2.43.0
+
diff --git a/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-add-build-dir-to-g.patch b/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-add-build-dir-to-g.patch
new file mode 100644
index 0000000000..67c92c1786
--- /dev/null
+++ b/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-add-build-dir-to-g.patch
@@ -0,0 +1,27 @@
+From a5f486a9d30209bf2a6d24abee61236d0aae7be9 Mon Sep 17 00:00:00 2001
+From: Otto Allmendinger
+Date: Thu, 3 Jul 2025 13:28:38 +0200
+Subject: [PATCH] fix(babylonlabs-io-btc-staking-ts): add build dir to
+ gitignore
+
+Issue: BTC-2143
+
+Co-authored-by: llm-git
+---
+ modules/babylonlabs-io-btc-staking-ts/.gitignore | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/modules/babylonlabs-io-btc-staking-ts/.gitignore b/modules/babylonlabs-io-btc-staking-ts/.gitignore
+index 2f621f05c..6b0417d7d 100644
+--- a/modules/babylonlabs-io-btc-staking-ts/.gitignore
++++ b/modules/babylonlabs-io-btc-staking-ts/.gitignore
+@@ -206,3 +206,5 @@ $RECYCLE.BIN/
+ # End of https://www.toptal.com/developers/gitignore/api/node,macos,windows
+ *.swp
+ *.swo
++
++build/
+\ No newline at end of file
+--
+2.43.0
+
diff --git a/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-change-class-field.patch b/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-change-class-field.patch
new file mode 100644
index 0000000000..07fc8a6166
--- /dev/null
+++ b/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-change-class-field.patch
@@ -0,0 +1,57 @@
+From 75323a260f5b9031ce9c653ad3fe17755a4e25d0 Mon Sep 17 00:00:00 2001
+From: Otto Allmendinger
+Date: Thu, 3 Jul 2025 11:03:39 +0200
+Subject: [PATCH] fix(babylonlabs-io-btc-staking-ts): change class fields and
+ methods visibility
+
+Changed class fields and methods visibility from private to protected or
+public to allow better extensibility for subclasses.
+
+Issue: BTC-2143
+
+Co-authored-by: llm-git
+---
+ .../src/staking/manager.ts | 13 +++++++------
+ 1 file changed, 7 insertions(+), 6 deletions(-)
+
+diff --git a/modules/babylonlabs-io-btc-staking-ts/src/staking/manager.ts b/modules/babylonlabs-io-btc-staking-ts/src/staking/manager.ts
+index 778f3e700..af3dad5d0 100644
+--- a/modules/babylonlabs-io-btc-staking-ts/src/staking/manager.ts
++++ b/modules/babylonlabs-io-btc-staking-ts/src/staking/manager.ts
+@@ -93,10 +93,10 @@ interface InclusionProof {
+ }
+
+ export class BabylonBtcStakingManager {
+- private stakingParams: VersionedStakingParams[];
+- private btcProvider: BtcProvider;
+- private network: networks.Network;
+- private babylonProvider: BabylonProvider;
++ protected stakingParams: VersionedStakingParams[];
++ protected btcProvider: BtcProvider;
++ protected network: networks.Network;
++ protected babylonProvider: BabylonProvider;
+
+ constructor(
+ network: networks.Network,
+@@ -624,7 +624,8 @@ export class BabylonBtcStakingManager {
+
+ /**
+ * Creates a proof of possession for the staker based on ECDSA signature.
+- * @param bech32Address - The staker's bech32 address.
++ * @param bech32Address - The staker's bech32 address on the babylon chain
++ * @param stakerBtcAddress - The staker's BTC address.
+ * @returns The proof of possession.
+ */
+ async createProofOfPossession(
+@@ -709,7 +710,7 @@ export class BabylonBtcStakingManager {
+ * @param inclusionProof - The inclusion proof of the staking transaction.
+ * @returns The protobuf message.
+ */
+- private async createBtcDelegationMsg(
++ public async createBtcDelegationMsg(
+ stakingInstance: Staking,
+ stakingInput: StakingInputs,
+ stakingTx: Transaction,
+--
+2.43.0
+
diff --git a/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-simplify-README-to.patch b/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-simplify-README-to.patch
new file mode 100644
index 0000000000..f2603ff557
--- /dev/null
+++ b/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-simplify-README-to.patch
@@ -0,0 +1,59 @@
+From fd94e62d8bdb05bbdd742b6b7dddf163f1a7dd35 Mon Sep 17 00:00:00 2001
+From: Otto Allmendinger
+Date: Thu, 3 Jul 2025 11:01:15 +0200
+Subject: [PATCH] fix(babylonlabs-io-btc-staking-ts): simplify README to
+ identify as fork
+
+Issue: BTC-2143
+
+Co-authored-by: llm-git
+---
+ .../babylonlabs-io-btc-staking-ts/README.md | 37 +------------------
+ 1 file changed, 1 insertion(+), 36 deletions(-)
+
+diff --git a/modules/babylonlabs-io-btc-staking-ts/README.md b/modules/babylonlabs-io-btc-staking-ts/README.md
+index ed35980ea..cecbcaff7 100644
+--- a/modules/babylonlabs-io-btc-staking-ts/README.md
++++ b/modules/babylonlabs-io-btc-staking-ts/README.md
+@@ -1,36 +1 @@
+-
+-
+-
@babylonlabs-io/btc-staking-ts
+- Babylon Bitcoin Staking Protocol
+- TypeScript library
+-
+-
+-
+-
+-
+-
+-## Installation
+-
+-```console
+-npm i @babylonlabs-io/btc-staking-ts
+-```
+-
+-## Version Release
+-
+-### Stable version
+-
+-Stable release versions are manually released from the main branch.
+-
+-### Canary version
+-
+-A canary version is a pre-release version from `dev` branch.
+-Make sure all changes are added and committed before running the command below:
+-
+-```console
+-npm run version:canary
+-```
+-
+-## Usage Guide
+-
+-Details on the usage of the library can be found
+-on the [usage guide](./docs/usage.md).
++BitGo Fork of https://github.com/babylonlabs-io/btc-staking-ts/tree/v1.0.3
+\ No newline at end of file
+--
+2.43.0
+
diff --git a/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-update-package-met.patch b/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-update-package-met.patch
new file mode 100644
index 0000000000..f70268d6a4
--- /dev/null
+++ b/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-update-package-met.patch
@@ -0,0 +1,111 @@
+From 77fe384ba5a4b62ed4e65b7fc8bd7bc0ccadc3f0 Mon Sep 17 00:00:00 2001
+From: Otto Allmendinger
+Date: Thu, 3 Jul 2025 11:01:36 +0200
+Subject: [PATCH] fix(babylonlabs-io-btc-staking-ts): update package metadata
+ for BitGo fork
+
+Update package name, version, and dependencies to support BitGo's fork of the
+babylonlabs BTC staking library. Simplified build configuration and updated
+Node.js version requirements.
+
+Issue: BTC-2143
+
+Co-authored-by: llm-git
+---
+ .../package.json | 55 +++++--------------
+ 1 file changed, 13 insertions(+), 42 deletions(-)
+
+diff --git a/modules/babylonlabs-io-btc-staking-ts/package.json b/modules/babylonlabs-io-btc-staking-ts/package.json
+index f3034eec2..34be5ad5b 100644
+--- a/modules/babylonlabs-io-btc-staking-ts/package.json
++++ b/modules/babylonlabs-io-btc-staking-ts/package.json
+@@ -1,20 +1,20 @@
+ {
+- "name": "@babylonlabs-io/btc-staking-ts",
+- "version": "1.0.3",
++ "name": "@bitgo/babylonlabs-io-btc-staking-ts",
++ "version": "1.2.0",
+ "description": "Library exposing methods for the creation and consumption of Bitcoin transactions pertaining to Babylon's Bitcoin Staking protocol.",
+ "module": "dist/index.js",
+ "main": "dist/index.cjs",
+ "typings": "dist/index.d.ts",
+ "type": "module",
++ "exports": {
++ "import": "./dist/index.js",
++ "require": "./dist/index.cjs"
++ },
+ "scripts": {
+- "generate-types": "dts-bundle-generator -o ./dist/index.d.ts ./src/index.ts",
++ "generate-types": "dts-bundle-generator -o ./dist/index.d.cts ./src/index.ts --no-check",
+ "build": "node build.js && npm run generate-types",
+- "format": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"",
+- "format:fix": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"",
+- "lint": "eslint ./src --fix",
+ "prepare": "husky",
+ "prepublishOnly": "npm run build",
+- "test": "jest --verbose",
+ "version:canary": "npm version prerelease --preid=canary"
+ },
+ "files": [
+@@ -27,51 +27,22 @@
+ "btc-staking"
+ ],
+ "engines": {
+- "node": "22.3.0"
+- },
+- "husky": {
+- "hooks": {
+- "pre-commit": "lint-staged"
+- }
+- },
+- "lint-staged": {
+- "src/**/*.ts": [
+- "prettier --write",
+- "eslint --fix"
+- ],
+- "tests/**/*.ts": [
+- "prettier --write",
+- "eslint --fix"
+- ]
++ "node": ">=18 < 23"
+ },
+ "author": "Babylon Labs Ltd.",
+ "license": "SEE LICENSE IN LICENSE",
+ "devDependencies": {
+- "@types/jest": "^29.5.12",
+- "@types/node": "^20.11.30",
+- "@typescript-eslint/parser": "^7.4.0",
+ "dts-bundle-generator": "^9.3.1",
+- "ecpair": "^2.1.0",
+- "esbuild": "^0.20.2",
+- "eslint": "^8.57.0",
+- "eslint-config-prettier": "^9.1.0",
+- "eslint-plugin-prettier": "^5.1.3",
+- "husky": "^9.0.11",
+- "jest": "^29.7.0",
+- "lint-staged": "^15.2.7",
+- "prettier": "^3.2.5",
+- "prettier-plugin-organize-imports": "^3.2.4",
+- "ts-jest": "^29.1.4",
+- "typescript": "^5.4.5",
+- "typescript-eslint": "^7.4.0"
++ "esbuild": "^0.20.2"
+ },
+ "dependencies": {
+- "@babylonlabs-io/babylon-proto-ts": "1.0.1",
++ "@babylonlabs-io/babylon-proto-ts": "1.0.0",
+ "@bitcoin-js/tiny-secp256k1-asmjs": "2.2.3",
+ "@cosmjs/encoding": "^0.33.0",
+- "bitcoinjs-lib": "6.1.5"
++ "bip174": "=2.1.1",
++ "bitcoinjs-lib": "^6.1.7"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+-}
+\ No newline at end of file
++}
+--
+2.43.0
+
diff --git a/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-update-tsconfig-fo.patch b/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-update-tsconfig-fo.patch
new file mode 100644
index 0000000000..2de122bce7
--- /dev/null
+++ b/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-update-tsconfig-fo.patch
@@ -0,0 +1,39 @@
+From 1f802177441fd09087b4ecfac254dac214dd29bf Mon Sep 17 00:00:00 2001
+From: Otto Allmendinger
+Date: Thu, 3 Jul 2025 11:03:55 +0200
+Subject: [PATCH] fix(babylonlabs-io-btc-staking-ts): update tsconfig for
+ correct builds
+
+Update tsconfig.json to include composite mode, specify outDir to build
+folder, and exclude build from compilation. This fixes issues with the
+vendor script.
+
+Issue: BTC-2143
+
+Co-authored-by: llm-git
+---
+ modules/babylonlabs-io-btc-staking-ts/tsconfig.json | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/modules/babylonlabs-io-btc-staking-ts/tsconfig.json b/modules/babylonlabs-io-btc-staking-ts/tsconfig.json
+index 838bfdba1..965f1a3de 100644
+--- a/modules/babylonlabs-io-btc-staking-ts/tsconfig.json
++++ b/modules/babylonlabs-io-btc-staking-ts/tsconfig.json
+@@ -7,10 +7,13 @@
+ "strict": true,
+ "skipLibCheck": true,
+ "isolatedModules": true,
+- "declaration": true
++ "declaration": true,
++ "composite": true,
++ "outDir": "./build",
+ },
+ "exclude": [
+ "node_modules",
++ "build",
+ "dist",
+ "**/*.test.ts"
+ ]
+--
+2.43.0
+
diff --git a/scripts/vendor-github-repo/btc-staking-ts/postExtract.ts b/scripts/vendor-github-repo/btc-staking-ts/postExtract.ts
new file mode 100644
index 0000000000..2a29a4e048
--- /dev/null
+++ b/scripts/vendor-github-repo/btc-staking-ts/postExtract.ts
@@ -0,0 +1,38 @@
+import * as fs from 'fs/promises';
+
+import { GithubSource } from '../VendorConfig';
+import { applyPatchesFromDir } from '../git';
+
+// we only want to vendor prod source code
+// remove dev and test files
+const removePaths = [
+ '.eslintrc.json',
+ '.github/',
+ '.husky/',
+ '.npmrc',
+ '.nvmrc',
+ '.prettierignore',
+ '.prettierrc.json',
+ 'docs/',
+ 'tests/',
+];
+
+async function removeDevAndTestFiles(targetDir: string): Promise {
+ for (const path of removePaths) {
+ console.log(`Removing dev/test file: ${path}`);
+ const fullPath = `${targetDir}/${path}`;
+ await fs.rm(fullPath, { recursive: true, force: true });
+ }
+}
+
+export default async function hook(gitSource: GithubSource, targetDir: string): Promise {
+ if (!('tag' in gitSource)) {
+ throw new Error('unsupported github source, expected tag');
+ }
+ if (gitSource.tag !== 'v1.0.3') {
+ throw new Error(`unexpected tag ${gitSource.tag}, expected v1.0.3`);
+ }
+
+ await removeDevAndTestFiles(targetDir);
+ await applyPatchesFromDir(gitSource, targetDir);
+}
diff --git a/scripts/vendor-github-repo/exec.ts b/scripts/vendor-github-repo/exec.ts
new file mode 100644
index 0000000000..01887c6d93
--- /dev/null
+++ b/scripts/vendor-github-repo/exec.ts
@@ -0,0 +1,19 @@
+import { ChildProcess } from 'child_process';
+
+/**
+ * Executes a child process and waits for it to finish.
+ * Pipes the process's stdout and stderr to the parent process's stdout and stderr.
+ */
+export async function wait(p: ChildProcess): Promise {
+ p.stderr?.pipe(process.stderr);
+ p.stdout?.pipe(process.stdout);
+ return new Promise((resolve, reject) => {
+ p.on('exit', (code) => {
+ if (code === 0) {
+ resolve();
+ } else {
+ reject(new Error(`Process exited with code ${code}`));
+ }
+ });
+ });
+}
diff --git a/scripts/vendor-github-repo/git.ts b/scripts/vendor-github-repo/git.ts
new file mode 100644
index 0000000000..8168880b72
--- /dev/null
+++ b/scripts/vendor-github-repo/git.ts
@@ -0,0 +1,56 @@
+import * as fs from 'fs/promises';
+import { execa } from 'execa';
+
+import { GithubSource } from './VendorConfig';
+
+export async function getCommitsFromRange(from: string, to: string): Promise {
+ try {
+ const { stdout } = await execa('git', ['log', '--pretty=format:%H', `${from}..${to}`]);
+ return stdout.split('\n').filter((commit) => commit.trim() !== '');
+ } catch (error) {
+ throw new Error(`Failed to get commits from range ${from} to ${to}: ${error}`);
+ }
+}
+
+export async function getCommitsFromSpec(spec: string): Promise {
+ if (spec.includes('..')) {
+ const [from, to, ...rest] = spec.split('..');
+ if (rest.length > 0) {
+ throw new Error(`Invalid commit range specifier: ${spec}`);
+ }
+ return getCommitsFromRange(from, to);
+ }
+ return [spec];
+}
+
+export function getPatchDir(cfg: { repo: string }): string {
+ return `${__dirname}/${cfg.repo}/patches`;
+}
+
+export async function createPatchForCommit(cfg: GithubSource, commit: string, targetDir: string): Promise {
+ const patchDir = getPatchDir(cfg);
+ console.log(`Creating patch for commit ${commit} in ${cfg.repo} at ${patchDir}`);
+ await execa('git', ['format-patch', '-1', commit, '--output-directory', getPatchDir(cfg)], {
+ cwd: targetDir,
+ stdio: 'inherit',
+ });
+}
+
+export async function applyPatch(cfg: GithubSource, patchFile: string, targetDir: string): Promise {
+ console.log(`Applying patch ${patchFile} to ${cfg.repo}`);
+ await execa('git', ['apply', patchFile], {
+ cwd: targetDir,
+ stdio: 'inherit',
+ });
+}
+
+export async function applyPatchesFromDir(cfg: GithubSource, targetDir: string): Promise {
+ const patchDir = getPatchDir(cfg);
+ console.log(`Applying patches from ${patchDir} to ${cfg.repo}`);
+ const patchFiles = await fs.readdir(patchDir);
+ for (const patchFile of patchFiles) {
+ if (patchFile.endsWith('.patch')) {
+ await applyPatch(cfg, `${patchDir}/${patchFile}`, targetDir);
+ }
+ }
+}
diff --git a/scripts/vendor-github-repo/main.ts b/scripts/vendor-github-repo/main.ts
new file mode 100644
index 0000000000..6ed5f45e53
--- /dev/null
+++ b/scripts/vendor-github-repo/main.ts
@@ -0,0 +1,144 @@
+import { execa, ResultPromise } from 'execa';
+import * as fs from 'fs/promises';
+import * as tmp from 'tmp';
+import * as yargs from 'yargs';
+import { GithubSource, VendorConfig } from './VendorConfig';
+import { createPatchForCommit, getCommitsFromRange, getCommitsFromSpec } from './git';
+
+function isErrorExists(e: NodeJS.ErrnoException): boolean {
+ return e.code === 'EEXIST';
+}
+
+function getUrl(lib: GithubSource): string {
+ if ('tag' in lib) {
+ const { org, repo, tag } = lib;
+ return `https://github.com/${org}/${repo}/archive/refs/tags/${tag}.tar.gz`;
+ }
+ if ('ref' in lib) {
+ const { org, repo, ref } = lib;
+ return `https://github.com/${org}/${repo}/tarball/${ref}`;
+ }
+ throw new Error('Unsupported lib');
+}
+
+function getArchivePath(lib: GithubSource): string {
+ return tmp.fileSync({ postfix: `-${lib.repo}.tar.gz` }).name;
+}
+
+async function fetchArchive(lib: GithubSource, outfile: string): Promise {
+ try {
+ const result = await fs.stat(outfile);
+ if (result.size > 0) {
+ console.log(`Archive already exists: ${outfile}`);
+ return;
+ }
+ } catch (e) {}
+ const url = getUrl(lib);
+ const result = await fetch(url);
+ if (!result.ok) {
+ throw new Error(`Failed to fetch ${url}: ${result.status} ${result.statusText}`);
+ }
+ await fs.writeFile(outfile, Buffer.from(await result.arrayBuffer()));
+}
+
+function pipe(cmd: ResultPromise): ResultPromise {
+ cmd.stdout?.pipe(process.stdout);
+ cmd.stderr?.pipe(process.stderr);
+ return cmd;
+}
+
+async function extractArchive(archivePath: string, targetDir: string): Promise {
+ try {
+ await fs.mkdir(targetDir, { recursive: true });
+ } catch (e) {
+ if (!isErrorExists(e)) {
+ throw e;
+ }
+ }
+ await pipe(execa('tar', ['-C', targetDir, '--strip-components', '1', '-xzf', archivePath]));
+}
+
+async function cmdVendor(cfgs: VendorConfig[]) {
+ for (const cfg of cfgs) {
+ const archivePath = getArchivePath(cfg);
+ await fetchArchive(cfg, archivePath);
+ await extractArchive(archivePath, cfg.targetDir);
+ if (cfg.postExtract) {
+ console.log(`Running post-extract hook for ${cfg.repo}`);
+ await cfg.postExtract(cfg, cfg.targetDir);
+ }
+ }
+}
+
+async function cmdGeneratePatches(cfgs: VendorConfig[], spec: string) {
+ for (const cfg of cfgs) {
+ const commitRange = await getCommitsFromSpec(spec);
+ for (const commit of commitRange) {
+ await createPatchForCommit(cfg, commit, cfg.targetDir);
+ }
+ }
+}
+
+const vendorConfigs: VendorConfig[] = [
+ {
+ org: 'babylonlabs-io',
+ repo: 'btc-staking-ts',
+ tag: 'v1.0.3',
+ targetDir: 'modules/babylonlabs-io-btc-staking-ts',
+ async postExtract(githubSource: GithubSource, targetDir: string) {
+ const m = await import('./btc-staking-ts/postExtract');
+ await m.default(githubSource, targetDir);
+ },
+ },
+];
+
+function getMatches(name: string): VendorConfig[] {
+ const matches = vendorConfigs.filter((cfg) => name === cfg.repo);
+ if (matches.length === 0) {
+ throw new Error(`no such vendor config ${name}`);
+ }
+ if (matches.length > 1) {
+ throw new Error(`ambiguous vendor config ${name}`);
+ }
+ return vendorConfigs.filter((cfg) => cfg.repo === name);
+}
+
+const optName = {
+ type: 'string',
+ demand: true,
+ description: 'Name of the vendor config to use, e.g. btc-staking-ts',
+} as const;
+
+yargs
+ .command({
+ command: 'vendor',
+ describe: 'Vendor a github repo',
+ builder(a) {
+ return a.options({
+ name: optName,
+ });
+ },
+ async handler(a) {
+ await cmdVendor(getMatches(a.name));
+ },
+ })
+ .command({
+ command: 'generate-patches',
+ describe: 'Convert a commit range to a set of patches',
+ builder(a) {
+ return a.options({
+ name: optName,
+ commitSpec: {
+ type: 'string',
+ demand: true,
+ description: 'Commit range in the form of "from..to", e.g. "v1.0.0..v1.0.3" or a single commit "v1.0.3"',
+ },
+ });
+ },
+ async handler(a) {
+ await cmdGeneratePatches(getMatches(a.name), a.commitSpec);
+ },
+ })
+ .help()
+ .strict()
+ .demandCommand().argv;
diff --git a/yarn.lock b/yarn.lock
index 97338874b2..b98901384d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4971,6 +4971,11 @@
"@noble/hashes" "~1.7.1"
"@scure/base" "~1.2.4"
+"@sec-ant/readable-stream@^0.4.1":
+ version "0.4.1"
+ resolved "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz#60de891bb126abfdc5410fdc6166aca065f10a0c"
+ integrity sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==
+
"@sideway/address@^4.1.5":
version "4.1.5"
resolved "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5"
@@ -5042,6 +5047,11 @@
resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f"
integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==
+"@sindresorhus/merge-streams@^4.0.0":
+ version "4.0.0"
+ resolved "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz#abb11d99aeb6d27f1b563c38147a72d50058e339"
+ integrity sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==
+
"@sinonjs/commons@^1", "@sinonjs/commons@^1.0.2", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.4.0", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.3":
version "1.8.6"
resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz#80c516a4dc264c2a69115e7578d62581ff455ed9"
@@ -11091,6 +11101,24 @@ execa@^8.0.1:
signal-exit "^4.1.0"
strip-final-newline "^3.0.0"
+execa@^9.6.0:
+ version "9.6.0"
+ resolved "https://registry.npmjs.org/execa/-/execa-9.6.0.tgz#38665530e54e2e018384108322f37f35ae74f3bc"
+ integrity sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==
+ 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"
+
executable@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c"
@@ -11418,6 +11446,13 @@ figures@3.2.0, figures@^3.0.0, figures@^3.2.0:
dependencies:
escape-string-regexp "^1.0.5"
+figures@^6.1.0:
+ version "6.1.0"
+ resolved "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz#935479f51865fa7479f6fa94fc6fc7ac14e62c4a"
+ integrity sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==
+ dependencies:
+ is-unicode-supported "^2.0.0"
+
file-entry-cache@^6.0.1:
version "6.0.1"
resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
@@ -11920,6 +11955,14 @@ get-stream@^8.0.1:
resolved "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2"
integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==
+get-stream@^9.0.0:
+ version "9.0.1"
+ resolved "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz#95157d21df8eb90d1647102b63039b1df60ebd27"
+ integrity sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==
+ dependencies:
+ "@sec-ant/readable-stream" "^0.4.1"
+ is-stream "^4.0.1"
+
get-symbol-description@^1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz#7bdd54e0befe8ffc9f3b4e203220d9f1e881b6ee"
@@ -12740,6 +12783,11 @@ human-signals@^5.0.0:
resolved "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28"
integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==
+human-signals@^8.0.1:
+ version "8.0.1"
+ resolved "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz#f08bb593b6d1db353933d06156cedec90abe51fb"
+ integrity sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==
+
humanize-duration@^3.24.0:
version "3.32.1"
resolved "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.32.1.tgz#922beff5da36fb1cee3de26ada24c592b0fe519b"
@@ -13306,6 +13354,11 @@ is-plain-obj@^3.0.0:
resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7"
integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==
+is-plain-obj@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0"
+ integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==
+
is-plain-object@^2.0.4:
version "2.0.4"
resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
@@ -13375,6 +13428,11 @@ is-stream@^3.0.0:
resolved "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac"
integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==
+is-stream@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz#375cf891e16d2e4baec250b85926cffc14720d9b"
+ integrity sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==
+
is-string@^1.0.7, is-string@^1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9"
@@ -13423,6 +13481,11 @@ is-unicode-supported@^0.1.0:
resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7"
integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==
+is-unicode-supported@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz#09f0ab0de6d3744d48d265ebb98f65d11f2a9b3a"
+ integrity sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==
+
is-weakmap@^2.0.2:
version "2.0.2"
resolved "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd"
@@ -15817,6 +15880,14 @@ npm-run-path@^5.1.0:
dependencies:
path-key "^4.0.0"
+npm-run-path@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz#25cfdc4eae04976f3349c0b1afc089052c362537"
+ integrity sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==
+ dependencies:
+ path-key "^4.0.0"
+ unicorn-magic "^0.3.0"
+
npmlog@^6.0.0, npmlog@^6.0.2:
version "6.0.2"
resolved "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830"
@@ -16488,6 +16559,11 @@ parse-json@^5.0.0, parse-json@^5.2.0:
json-parse-even-better-errors "^2.3.0"
lines-and-columns "^1.1.6"
+parse-ms@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz#c0c058edd47c2a590151a718990533fd62803df4"
+ integrity sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==
+
parse-passwd@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
@@ -17142,6 +17218,13 @@ pretty-format@^27.0.2:
ansi-styles "^5.0.0"
react-is "^17.0.1"
+pretty-ms@^9.2.0:
+ version "9.2.0"
+ resolved "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz#e14c0aad6493b69ed63114442a84133d7e560ef0"
+ integrity sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==
+ dependencies:
+ parse-ms "^4.0.0"
+
proc-log@^2.0.0, proc-log@^2.0.1:
version "2.0.1"
resolved "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz#8f3f69a1f608de27878f91f5c688b225391cb685"
@@ -19458,6 +19541,11 @@ strip-final-newline@^3.0.0:
resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd"
integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==
+strip-final-newline@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz#35a369ec2ac43df356e3edd5dcebb6429aa1fa5c"
+ integrity sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==
+
strip-hex-prefix@1.0.0, strip-hex-prefix@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f"
@@ -20406,6 +20494,11 @@ unicorn-magic@^0.1.0:
resolved "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4"
integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==
+unicorn-magic@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz#4efd45c85a69e0dd576d25532fbfa22aa5c8a104"
+ integrity sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==
+
unique-filename@^2.0.0:
version "2.0.1"
resolved "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2"
@@ -21722,6 +21815,11 @@ yocto-queue@^1.0.0:
resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz#36d7c4739f775b3cbc28e6136e21aa057adec418"
integrity sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==
+yoctocolors@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz#e0167474e9fbb9e8b3ecca738deaa61dd12e56fc"
+ integrity sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==
+
zod@^3.21.4:
version "3.25.67"
resolved "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz#62987e4078e2ab0f63b491ef0c4f33df24236da8"