cte пре 9 месеци
родитељ
комит
f108dfaeb8

+ 5 - 0
.changeset/little-parents-shake.md

@@ -0,0 +1,5 @@
+---
+"roo-cline": patch
+---
+
+Aider-inspired polyglot benchmarks

+ 45 - 0
.dockerignore

@@ -0,0 +1,45 @@
+# Version control
+# .git/
+# .gitignore
+# .gitattributes
+# .git-blame-ignore-revs
+# .gitconfig
+
+# Build artifacts
+bin/
+dist/
+**/dist/
+out/
+**/out/
+
+# Dependencies
+node_modules/
+**/node_modules/
+
+# Test and development files
+coverage/
+**/.vscode-test/
+
+# Configuration files
+# .env*
+knip.json
+.husky/
+
+# CI/CD
+# .changeset/
+# .github/
+# ellipsis.yaml
+
+# OS specific
+.DS_Store
+
+# Logs
+logs/
+*.log
+
+# Nix
+# flake.lock
+# flake.nix
+
+# Monorepo
+benchmark/exercises/

+ 1 - 0
.vscodeignore

@@ -5,6 +5,7 @@
 .vscode-test/**
 out/**
 out-integration/**
+benchmark/**
 e2e/**
 node_modules/**
 src/**

+ 2 - 0
benchmark/.env.local.sample

@@ -0,0 +1,2 @@
+OPENROUTER_API_KEY=sk-or-v1-...
+POSTHOG_API_KEY=phc_...

+ 89 - 0
benchmark/Dockerfile

@@ -0,0 +1,89 @@
+# docker build -f Dockerfile.base -t roo-code-benchmark-base ..
+# docker build -f Dockerfile -t roo-code-benchmark ..
+# docker run -d -it -p 3000:3000 -v /tmp/benchmarks.db:/tmp/benchmarks.db roo-code-benchmark
+# docker exec -it $(docker ps --filter "ancestor=roo-code-benchmark" -q) /bin/bash
+
+FROM ubuntu:latest
+
+# Install dependencies
+RUN apt update && apt install -y sudo curl git vim jq
+
+# Create a `vscode` user
+RUN useradd -m vscode -s /bin/bash && \
+  echo "vscode ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/vscode && \
+  chmod 0440 /etc/sudoers.d/vscode
+
+# Install VS Code
+# https://code.visualstudio.com/docs/setup/linux
+RUN apt install -y wget gpg apt-transport-https
+RUN wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > packages.microsoft.gpg
+RUN install -D -o root -g root -m 644 packages.microsoft.gpg /etc/apt/keyrings/packages.microsoft.gpg
+RUN echo "deb [arch=amd64,arm64,armhf signed-by=/etc/apt/keyrings/packages.microsoft.gpg] https://packages.microsoft.com/repos/code stable main" | tee /etc/apt/sources.list.d/vscode.list > /dev/null
+RUN rm -f packages.microsoft.gpg
+RUN apt update && apt install -y code
+
+# Install Xvfb
+RUN apt install -y xvfb
+
+# [cpp] Install cmake 3.28.3
+RUN apt install -y cmake
+
+# [go] Install Go 1.22.2
+RUN apt install -y golang-go
+
+# [java] Install Java 21
+RUN apt install -y default-jre
+
+# [javascript] Install Node.js v18.20.6
+RUN curl -sL https://deb.nodesource.com/setup_18.x | bash -
+RUN apt update && apt install -y nodejs
+RUN npm install -g corepack@latest
+
+# [python] Install Python 3.12.3 and uv 0.6.6
+RUN apt install -y python3 python3-venv python3-dev python3-pip
+
+# [rust] Install Rust 1.85
+RUN curl https://sh.rustup.rs -sSf | bash -s -- -y
+RUN echo 'source $HOME/.cargo/env' >> $HOME/.bashrc
+
+WORKDIR /home/vscode
+USER vscode
+
+# Enable corepack and install pnpm for the vscode user
+RUN corepack enable
+RUN yes y | pnpm --version
+
+COPY benchmark/entrypoint.sh /usr/local/bin/entrypoint.sh
+
+# Copy and build dependencies
+COPY --chown=vscode:vscode package*.json /home/vscode/repo/
+COPY --chown=vscode:vscode webview-ui/package*.json /home/vscode/repo/webview-ui/
+COPY --chown=vscode:vscode e2e/package*.json /home/vscode/repo/e2e/
+COPY --chown=vscode:vscode benchmark/package*.json /home/vscode/repo/benchmark/
+WORKDIR /home/vscode/repo
+RUN npm run install:all
+
+# Copy and build benchmark runner
+COPY --chown=vscode:vscode . /home/vscode/repo
+WORKDIR /home/vscode/repo/benchmark
+RUN npm run build
+
+# Copy exercises
+WORKDIR /home/vscode
+RUN git clone https://github.com/cte/Roo-Code-Benchmark.git exercises
+
+# Prepare exercises
+WORKDIR /home/vscode/exercises/python
+RUN curl -LsSf https://astral.sh/uv/install.sh | sh
+RUN /home/vscode/.local/bin/uv sync
+
+# Build web-ui
+WORKDIR /home/vscode/exercises/web-ui
+RUN echo "DB_FILE_NAME=file:/tmp/benchmarks.db" > .env
+RUN pnpm install
+RUN npx drizzle-kit push
+
+# Run web-ui
+EXPOSE 3000
+ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
+CMD ["/usr/bin/pnpm", "dev"]

+ 51 - 0
benchmark/README.md

@@ -0,0 +1,51 @@
+# Benchmark Harness
+
+Configure ENV vars (OpenRouter, PostHog, etc):
+
+```sh
+cp .env.local.sample .env.local
+# Update ENV vars as needed.
+```
+
+Build and run a Docker image with the development environment needed to run the
+benchmarks (C++, Go, Java, Node.js, Python & Rust):
+
+```sh
+npm run docker:start
+```
+
+Run an exercise:
+
+```sh
+npm run docker:benchmark -- -e exercises/javascript/binary
+```
+
+Select and run an exercise:
+
+```sh
+npm run cli
+```
+
+Select and run an exercise for a specific language:
+
+```sh
+npm run cli -- run rust
+```
+
+Run all exercises for a language:
+
+```sh
+npm run cli -- run rust all
+```
+
+Run all exercises:
+
+```sh
+npm run cli -- run all
+```
+
+Run all exercises using a specific runId (useful for re-trying when an unexpected error occurs):
+
+```sh
+npm run cli -- run all --runId 1
+```

+ 4 - 0
benchmark/entrypoint.sh

@@ -0,0 +1,4 @@
+#!/bin/bash
+
+npx drizzle-kit push
+exec "$@"

+ 2493 - 0
benchmark/package-lock.json

@@ -0,0 +1,2493 @@
+{
+	"name": "benchmark",
+	"version": "0.1.0",
+	"lockfileVersion": 3,
+	"requires": true,
+	"packages": {
+		"": {
+			"name": "benchmark",
+			"version": "0.1.0",
+			"devDependencies": {
+				"@vscode/test-electron": "^2.4.0",
+				"gluegun": "^5.1.2",
+				"tsx": "^4.19.3",
+				"typescript": "^5.4.5",
+				"yargs": "^17.7.2"
+			}
+		},
+		"node_modules/@babel/code-frame": {
+			"version": "7.26.2",
+			"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
+			"integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"@babel/helper-validator-identifier": "^7.25.9",
+				"js-tokens": "^4.0.0",
+				"picocolors": "^1.0.0"
+			},
+			"engines": {
+				"node": ">=6.9.0"
+			}
+		},
+		"node_modules/@babel/helper-validator-identifier": {
+			"version": "7.25.9",
+			"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
+			"integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=6.9.0"
+			}
+		},
+		"node_modules/@esbuild/aix-ppc64": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz",
+			"integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==",
+			"cpu": [
+				"ppc64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"aix"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@esbuild/android-arm": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz",
+			"integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==",
+			"cpu": [
+				"arm"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@esbuild/android-arm64": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz",
+			"integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@esbuild/android-x64": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz",
+			"integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@esbuild/darwin-arm64": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz",
+			"integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@esbuild/darwin-x64": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz",
+			"integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@esbuild/freebsd-arm64": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz",
+			"integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"freebsd"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@esbuild/freebsd-x64": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz",
+			"integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"freebsd"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@esbuild/linux-arm": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz",
+			"integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==",
+			"cpu": [
+				"arm"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@esbuild/linux-arm64": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz",
+			"integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@esbuild/linux-ia32": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz",
+			"integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==",
+			"cpu": [
+				"ia32"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@esbuild/linux-loong64": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz",
+			"integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==",
+			"cpu": [
+				"loong64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@esbuild/linux-mips64el": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz",
+			"integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==",
+			"cpu": [
+				"mips64el"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@esbuild/linux-ppc64": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz",
+			"integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==",
+			"cpu": [
+				"ppc64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@esbuild/linux-riscv64": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz",
+			"integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==",
+			"cpu": [
+				"riscv64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@esbuild/linux-s390x": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz",
+			"integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==",
+			"cpu": [
+				"s390x"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@esbuild/linux-x64": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz",
+			"integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@esbuild/netbsd-arm64": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz",
+			"integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"netbsd"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@esbuild/netbsd-x64": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz",
+			"integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"netbsd"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@esbuild/openbsd-arm64": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz",
+			"integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"openbsd"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@esbuild/openbsd-x64": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz",
+			"integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"openbsd"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@esbuild/sunos-x64": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz",
+			"integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"sunos"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@esbuild/win32-arm64": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz",
+			"integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@esbuild/win32-ia32": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz",
+			"integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==",
+			"cpu": [
+				"ia32"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@esbuild/win32-x64": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz",
+			"integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@types/parse-json": {
+			"version": "4.0.2",
+			"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
+			"integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/@vscode/test-electron": {
+			"version": "2.4.1",
+			"resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.4.1.tgz",
+			"integrity": "sha512-Gc6EdaLANdktQ1t+zozoBVRynfIsMKMc94Svu1QreOBC8y76x4tvaK32TljrLi1LI2+PK58sDVbL7ALdqf3VRQ==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"http-proxy-agent": "^7.0.2",
+				"https-proxy-agent": "^7.0.5",
+				"jszip": "^3.10.1",
+				"ora": "^7.0.1",
+				"semver": "^7.6.2"
+			},
+			"engines": {
+				"node": ">=16"
+			}
+		},
+		"node_modules/agent-base": {
+			"version": "7.1.3",
+			"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
+			"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">= 14"
+			}
+		},
+		"node_modules/ansi-colors": {
+			"version": "4.1.3",
+			"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
+			"integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/ansi-regex": {
+			"version": "6.1.0",
+			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
+			"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/ansi-regex?sponsor=1"
+			}
+		},
+		"node_modules/ansi-styles": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+			"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"color-convert": "^2.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/ansi-styles?sponsor=1"
+			}
+		},
+		"node_modules/apisauce": {
+			"version": "2.1.6",
+			"resolved": "https://registry.npmjs.org/apisauce/-/apisauce-2.1.6.tgz",
+			"integrity": "sha512-MdxR391op/FucS2YQRfB/NMRyCnHEPDd4h17LRIuVYi0BpGmMhpxc0shbOpfs5ahABuBEffNCGal5EcsydbBWg==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"axios": "^0.21.4"
+			}
+		},
+		"node_modules/app-module-path": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/app-module-path/-/app-module-path-2.2.0.tgz",
+			"integrity": "sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ==",
+			"dev": true,
+			"license": "BSD-2-Clause"
+		},
+		"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/axios": {
+			"version": "0.21.4",
+			"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
+			"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"follow-redirects": "^1.14.0"
+			}
+		},
+		"node_modules/balanced-match": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+			"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/base64-js": {
+			"version": "1.5.1",
+			"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+			"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			],
+			"license": "MIT"
+		},
+		"node_modules/bl": {
+			"version": "5.1.0",
+			"resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz",
+			"integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"buffer": "^6.0.3",
+				"inherits": "^2.0.4",
+				"readable-stream": "^3.4.0"
+			}
+		},
+		"node_modules/bl/node_modules/readable-stream": {
+			"version": "3.6.2",
+			"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+			"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"inherits": "^2.0.3",
+				"string_decoder": "^1.1.1",
+				"util-deprecate": "^1.0.1"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/brace-expansion": {
+			"version": "1.1.11",
+			"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+			"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"balanced-match": "^1.0.0",
+				"concat-map": "0.0.1"
+			}
+		},
+		"node_modules/buffer": {
+			"version": "6.0.3",
+			"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+			"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			],
+			"license": "MIT",
+			"dependencies": {
+				"base64-js": "^1.3.1",
+				"ieee754": "^1.2.1"
+			}
+		},
+		"node_modules/callsites": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+			"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/chalk": {
+			"version": "4.1.2",
+			"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+			"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"ansi-styles": "^4.1.0",
+				"supports-color": "^7.1.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/chalk?sponsor=1"
+			}
+		},
+		"node_modules/cli-cursor": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz",
+			"integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"restore-cursor": "^4.0.0"
+			},
+			"engines": {
+				"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/cli-spinners": {
+			"version": "2.9.2",
+			"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
+			"integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=6"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/cli-table3": {
+			"version": "0.6.0",
+			"resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz",
+			"integrity": "sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"object-assign": "^4.1.0",
+				"string-width": "^4.2.0"
+			},
+			"engines": {
+				"node": "10.* || >= 12.*"
+			},
+			"optionalDependencies": {
+				"colors": "^1.1.2"
+			}
+		},
+		"node_modules/cliui": {
+			"version": "8.0.1",
+			"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+			"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+			"dev": true,
+			"license": "ISC",
+			"dependencies": {
+				"string-width": "^4.2.0",
+				"strip-ansi": "^6.0.1",
+				"wrap-ansi": "^7.0.0"
+			},
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/cliui/node_modules/ansi-regex": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+			"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/cliui/node_modules/strip-ansi": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+			"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"ansi-regex": "^5.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/clone": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+			"integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=0.8"
+			}
+		},
+		"node_modules/color-convert": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+			"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"color-name": "~1.1.4"
+			},
+			"engines": {
+				"node": ">=7.0.0"
+			}
+		},
+		"node_modules/color-name": {
+			"version": "1.1.4",
+			"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+			"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/colors": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
+			"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=0.1.90"
+			}
+		},
+		"node_modules/concat-map": {
+			"version": "0.0.1",
+			"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+			"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/core-util-is": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+			"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/cosmiconfig": {
+			"version": "7.0.1",
+			"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz",
+			"integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"@types/parse-json": "^4.0.0",
+				"import-fresh": "^3.2.1",
+				"parse-json": "^5.0.0",
+				"path-type": "^4.0.0",
+				"yaml": "^1.10.0"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/cross-spawn": {
+			"version": "7.0.3",
+			"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+			"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"path-key": "^3.1.0",
+				"shebang-command": "^2.0.0",
+				"which": "^2.0.1"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/debug": {
+			"version": "4.4.0",
+			"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
+			"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"ms": "^2.1.3"
+			},
+			"engines": {
+				"node": ">=6.0"
+			},
+			"peerDependenciesMeta": {
+				"supports-color": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/defaults": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
+			"integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"clone": "^1.0.2"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/eastasianwidth": {
+			"version": "0.2.0",
+			"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+			"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/ejs": {
+			"version": "3.1.8",
+			"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz",
+			"integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==",
+			"dev": true,
+			"license": "Apache-2.0",
+			"dependencies": {
+				"jake": "^10.8.5"
+			},
+			"bin": {
+				"ejs": "bin/cli.js"
+			},
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/emoji-regex": {
+			"version": "8.0.0",
+			"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+			"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/enquirer": {
+			"version": "2.3.6",
+			"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
+			"integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"ansi-colors": "^4.1.1"
+			},
+			"engines": {
+				"node": ">=8.6"
+			}
+		},
+		"node_modules/error-ex": {
+			"version": "1.3.2",
+			"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+			"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"is-arrayish": "^0.2.1"
+			}
+		},
+		"node_modules/esbuild": {
+			"version": "0.25.0",
+			"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz",
+			"integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==",
+			"dev": true,
+			"hasInstallScript": true,
+			"license": "MIT",
+			"bin": {
+				"esbuild": "bin/esbuild"
+			},
+			"engines": {
+				"node": ">=18"
+			},
+			"optionalDependencies": {
+				"@esbuild/aix-ppc64": "0.25.0",
+				"@esbuild/android-arm": "0.25.0",
+				"@esbuild/android-arm64": "0.25.0",
+				"@esbuild/android-x64": "0.25.0",
+				"@esbuild/darwin-arm64": "0.25.0",
+				"@esbuild/darwin-x64": "0.25.0",
+				"@esbuild/freebsd-arm64": "0.25.0",
+				"@esbuild/freebsd-x64": "0.25.0",
+				"@esbuild/linux-arm": "0.25.0",
+				"@esbuild/linux-arm64": "0.25.0",
+				"@esbuild/linux-ia32": "0.25.0",
+				"@esbuild/linux-loong64": "0.25.0",
+				"@esbuild/linux-mips64el": "0.25.0",
+				"@esbuild/linux-ppc64": "0.25.0",
+				"@esbuild/linux-riscv64": "0.25.0",
+				"@esbuild/linux-s390x": "0.25.0",
+				"@esbuild/linux-x64": "0.25.0",
+				"@esbuild/netbsd-arm64": "0.25.0",
+				"@esbuild/netbsd-x64": "0.25.0",
+				"@esbuild/openbsd-arm64": "0.25.0",
+				"@esbuild/openbsd-x64": "0.25.0",
+				"@esbuild/sunos-x64": "0.25.0",
+				"@esbuild/win32-arm64": "0.25.0",
+				"@esbuild/win32-ia32": "0.25.0",
+				"@esbuild/win32-x64": "0.25.0"
+			}
+		},
+		"node_modules/escalade": {
+			"version": "3.2.0",
+			"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+			"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/escape-string-regexp": {
+			"version": "1.0.5",
+			"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+			"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=0.8.0"
+			}
+		},
+		"node_modules/execa": {
+			"version": "5.1.1",
+			"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+			"integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"cross-spawn": "^7.0.3",
+				"get-stream": "^6.0.0",
+				"human-signals": "^2.1.0",
+				"is-stream": "^2.0.0",
+				"merge-stream": "^2.0.0",
+				"npm-run-path": "^4.0.1",
+				"onetime": "^5.1.2",
+				"signal-exit": "^3.0.3",
+				"strip-final-newline": "^2.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sindresorhus/execa?sponsor=1"
+			}
+		},
+		"node_modules/filelist": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
+			"integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==",
+			"dev": true,
+			"license": "Apache-2.0",
+			"dependencies": {
+				"minimatch": "^5.0.1"
+			}
+		},
+		"node_modules/filelist/node_modules/brace-expansion": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+			"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"balanced-match": "^1.0.0"
+			}
+		},
+		"node_modules/filelist/node_modules/minimatch": {
+			"version": "5.1.6",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+			"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+			"dev": true,
+			"license": "ISC",
+			"dependencies": {
+				"brace-expansion": "^2.0.1"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/follow-redirects": {
+			"version": "1.15.9",
+			"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
+			"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "individual",
+					"url": "https://github.com/sponsors/RubenVerborgh"
+				}
+			],
+			"license": "MIT",
+			"engines": {
+				"node": ">=4.0"
+			},
+			"peerDependenciesMeta": {
+				"debug": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/fs-jetpack": {
+			"version": "4.3.1",
+			"resolved": "https://registry.npmjs.org/fs-jetpack/-/fs-jetpack-4.3.1.tgz",
+			"integrity": "sha512-dbeOK84F6BiQzk2yqqCVwCPWTxAvVGJ3fMQc6E2wuEohS28mR6yHngbrKuVCK1KHRx/ccByDylqu4H5PCP2urQ==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"minimatch": "^3.0.2",
+				"rimraf": "^2.6.3"
+			}
+		},
+		"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",
+			"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+			"dev": true,
+			"hasInstallScript": true,
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"engines": {
+				"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+			}
+		},
+		"node_modules/get-caller-file": {
+			"version": "2.0.5",
+			"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+			"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+			"dev": true,
+			"license": "ISC",
+			"engines": {
+				"node": "6.* || 8.* || >= 10.*"
+			}
+		},
+		"node_modules/get-stream": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+			"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/get-tsconfig": {
+			"version": "4.10.0",
+			"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz",
+			"integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"resolve-pkg-maps": "^1.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+			}
+		},
+		"node_modules/glob": {
+			"version": "7.2.3",
+			"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+			"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+			"deprecated": "Glob versions prior to v9 are no longer supported",
+			"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/gluegun": {
+			"version": "5.2.0",
+			"resolved": "https://registry.npmjs.org/gluegun/-/gluegun-5.2.0.tgz",
+			"integrity": "sha512-jSUM5xUy2ztYFQANne17OUm/oAd7qSX7EBksS9bQDt9UvLPqcEkeWUebmaposb8Tx7eTTD8uJVWGRe6PYSsYkg==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"apisauce": "^2.1.5",
+				"app-module-path": "^2.2.0",
+				"cli-table3": "0.6.0",
+				"colors": "1.4.0",
+				"cosmiconfig": "7.0.1",
+				"cross-spawn": "7.0.3",
+				"ejs": "3.1.8",
+				"enquirer": "2.3.6",
+				"execa": "5.1.1",
+				"fs-jetpack": "4.3.1",
+				"lodash.camelcase": "^4.3.0",
+				"lodash.kebabcase": "^4.1.1",
+				"lodash.lowercase": "^4.3.0",
+				"lodash.lowerfirst": "^4.3.1",
+				"lodash.pad": "^4.5.1",
+				"lodash.padend": "^4.6.1",
+				"lodash.padstart": "^4.6.1",
+				"lodash.repeat": "^4.1.0",
+				"lodash.snakecase": "^4.1.1",
+				"lodash.startcase": "^4.4.0",
+				"lodash.trim": "^4.5.1",
+				"lodash.trimend": "^4.5.1",
+				"lodash.trimstart": "^4.5.1",
+				"lodash.uppercase": "^4.3.0",
+				"lodash.upperfirst": "^4.3.1",
+				"ora": "4.0.2",
+				"pluralize": "^8.0.0",
+				"semver": "7.3.5",
+				"which": "2.0.2",
+				"yargs-parser": "^21.0.0"
+			},
+			"bin": {
+				"gluegun": "bin/gluegun"
+			}
+		},
+		"node_modules/gluegun/node_modules/ansi-regex": {
+			"version": "4.1.1",
+			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+			"integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/gluegun/node_modules/ansi-styles": {
+			"version": "3.2.1",
+			"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+			"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"color-convert": "^1.9.0"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/gluegun/node_modules/chalk": {
+			"version": "2.4.2",
+			"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+			"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"ansi-styles": "^3.2.1",
+				"escape-string-regexp": "^1.0.5",
+				"supports-color": "^5.3.0"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/gluegun/node_modules/cli-cursor": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+			"integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"restore-cursor": "^3.1.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/gluegun/node_modules/color-convert": {
+			"version": "1.9.3",
+			"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+			"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"color-name": "1.1.3"
+			}
+		},
+		"node_modules/gluegun/node_modules/color-name": {
+			"version": "1.1.3",
+			"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+			"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/gluegun/node_modules/has-flag": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+			"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/gluegun/node_modules/is-interactive": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
+			"integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/gluegun/node_modules/ora": {
+			"version": "4.0.2",
+			"resolved": "https://registry.npmjs.org/ora/-/ora-4.0.2.tgz",
+			"integrity": "sha512-YUOZbamht5mfLxPmk4M35CD/5DuOkAacxlEUbStVXpBAt4fyhBf+vZHI/HRkI++QUp3sNoeA2Gw4C+hi4eGSig==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"chalk": "^2.4.2",
+				"cli-cursor": "^3.1.0",
+				"cli-spinners": "^2.2.0",
+				"is-interactive": "^1.0.0",
+				"log-symbols": "^3.0.0",
+				"strip-ansi": "^5.2.0",
+				"wcwidth": "^1.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/gluegun/node_modules/restore-cursor": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+			"integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"onetime": "^5.1.0",
+				"signal-exit": "^3.0.2"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/gluegun/node_modules/semver": {
+			"version": "7.3.5",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
+			"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+			"dev": true,
+			"license": "ISC",
+			"dependencies": {
+				"lru-cache": "^6.0.0"
+			},
+			"bin": {
+				"semver": "bin/semver.js"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/gluegun/node_modules/strip-ansi": {
+			"version": "5.2.0",
+			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+			"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"ansi-regex": "^4.1.0"
+			},
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/gluegun/node_modules/supports-color": {
+			"version": "5.5.0",
+			"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+			"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"has-flag": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/has-flag": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+			"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/http-proxy-agent": {
+			"version": "7.0.2",
+			"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+			"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"agent-base": "^7.1.0",
+				"debug": "^4.3.4"
+			},
+			"engines": {
+				"node": ">= 14"
+			}
+		},
+		"node_modules/https-proxy-agent": {
+			"version": "7.0.6",
+			"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+			"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"agent-base": "^7.1.2",
+				"debug": "4"
+			},
+			"engines": {
+				"node": ">= 14"
+			}
+		},
+		"node_modules/human-signals": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+			"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+			"dev": true,
+			"license": "Apache-2.0",
+			"engines": {
+				"node": ">=10.17.0"
+			}
+		},
+		"node_modules/ieee754": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+			"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			],
+			"license": "BSD-3-Clause"
+		},
+		"node_modules/immediate": {
+			"version": "3.0.6",
+			"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+			"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/import-fresh": {
+			"version": "3.3.1",
+			"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+			"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"parent-module": "^1.0.0",
+				"resolve-from": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=6"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"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",
+			"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+			"dev": true,
+			"license": "ISC"
+		},
+		"node_modules/is-arrayish": {
+			"version": "0.2.1",
+			"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+			"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/is-fullwidth-code-point": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+			"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/is-interactive": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz",
+			"integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/is-stream": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+			"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=8"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/isarray": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+			"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/isexe": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+			"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+			"dev": true,
+			"license": "ISC"
+		},
+		"node_modules/jake": {
+			"version": "10.9.2",
+			"resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz",
+			"integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==",
+			"dev": true,
+			"license": "Apache-2.0",
+			"dependencies": {
+				"async": "^3.2.3",
+				"chalk": "^4.0.2",
+				"filelist": "^1.0.4",
+				"minimatch": "^3.1.2"
+			},
+			"bin": {
+				"jake": "bin/cli.js"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/js-tokens": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+			"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/json-parse-even-better-errors": {
+			"version": "2.3.1",
+			"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+			"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/jszip": {
+			"version": "3.10.1",
+			"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
+			"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
+			"dev": true,
+			"license": "(MIT OR GPL-3.0-or-later)",
+			"dependencies": {
+				"lie": "~3.3.0",
+				"pako": "~1.0.2",
+				"readable-stream": "~2.3.6",
+				"setimmediate": "^1.0.5"
+			}
+		},
+		"node_modules/lie": {
+			"version": "3.3.0",
+			"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+			"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"immediate": "~3.0.5"
+			}
+		},
+		"node_modules/lines-and-columns": {
+			"version": "1.2.4",
+			"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+			"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/lodash.camelcase": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+			"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/lodash.kebabcase": {
+			"version": "4.1.1",
+			"resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz",
+			"integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/lodash.lowercase": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/lodash.lowercase/-/lodash.lowercase-4.3.0.tgz",
+			"integrity": "sha512-UcvP1IZYyDKyEL64mmrwoA1AbFu5ahojhTtkOUr1K9dbuxzS9ev8i4TxMMGCqRC9TE8uDaSoufNAXxRPNTseVA==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/lodash.lowerfirst": {
+			"version": "4.3.1",
+			"resolved": "https://registry.npmjs.org/lodash.lowerfirst/-/lodash.lowerfirst-4.3.1.tgz",
+			"integrity": "sha512-UUKX7VhP1/JL54NXg2aq/E1Sfnjjes8fNYTNkPU8ZmsaVeBvPHKdbNaN79Re5XRL01u6wbq3j0cbYZj71Fcu5w==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/lodash.pad": {
+			"version": "4.5.1",
+			"resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz",
+			"integrity": "sha512-mvUHifnLqM+03YNzeTBS1/Gr6JRFjd3rRx88FHWUvamVaT9k2O/kXha3yBSOwB9/DTQrSTLJNHvLBBt2FdX7Mg==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/lodash.padend": {
+			"version": "4.6.1",
+			"resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz",
+			"integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/lodash.padstart": {
+			"version": "4.6.1",
+			"resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz",
+			"integrity": "sha512-sW73O6S8+Tg66eY56DBk85aQzzUJDtpoXFBgELMd5P/SotAguo+1kYO6RuYgXxA4HJH3LFTFPASX6ET6bjfriw==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/lodash.repeat": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/lodash.repeat/-/lodash.repeat-4.1.0.tgz",
+			"integrity": "sha512-eWsgQW89IewS95ZOcr15HHCX6FVDxq3f2PNUIng3fyzsPev9imFQxIYdFZ6crl8L56UR6ZlGDLcEb3RZsCSSqw==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/lodash.snakecase": {
+			"version": "4.1.1",
+			"resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
+			"integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/lodash.startcase": {
+			"version": "4.4.0",
+			"resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz",
+			"integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/lodash.trim": {
+			"version": "4.5.1",
+			"resolved": "https://registry.npmjs.org/lodash.trim/-/lodash.trim-4.5.1.tgz",
+			"integrity": "sha512-nJAlRl/K+eiOehWKDzoBVrSMhK0K3A3YQsUNXHQa5yIrKBAhsZgSu3KoAFoFT+mEgiyBHddZ0pRk1ITpIp90Wg==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/lodash.trimend": {
+			"version": "4.5.1",
+			"resolved": "https://registry.npmjs.org/lodash.trimend/-/lodash.trimend-4.5.1.tgz",
+			"integrity": "sha512-lsD+k73XztDsMBKPKvzHXRKFNMohTjoTKIIo4ADLn5dA65LZ1BqlAvSXhR2rPEC3BgAUQnzMnorqDtqn2z4IHA==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/lodash.trimstart": {
+			"version": "4.5.1",
+			"resolved": "https://registry.npmjs.org/lodash.trimstart/-/lodash.trimstart-4.5.1.tgz",
+			"integrity": "sha512-b/+D6La8tU76L/61/aN0jULWHkT0EeJCmVstPBn/K9MtD2qBW83AsBNrr63dKuWYwVMO7ucv13QNO/Ek/2RKaQ==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/lodash.uppercase": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/lodash.uppercase/-/lodash.uppercase-4.3.0.tgz",
+			"integrity": "sha512-+Nbnxkj7s8K5U8z6KnEYPGUOGp3woZbB7Ecs7v3LkkjLQSm2kP9SKIILitN1ktn2mB/tmM9oSlku06I+/lH7QA==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/lodash.upperfirst": {
+			"version": "4.3.1",
+			"resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz",
+			"integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/log-symbols": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
+			"integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"chalk": "^2.4.2"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/log-symbols/node_modules/ansi-styles": {
+			"version": "3.2.1",
+			"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+			"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"color-convert": "^1.9.0"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/log-symbols/node_modules/chalk": {
+			"version": "2.4.2",
+			"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+			"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"ansi-styles": "^3.2.1",
+				"escape-string-regexp": "^1.0.5",
+				"supports-color": "^5.3.0"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/log-symbols/node_modules/color-convert": {
+			"version": "1.9.3",
+			"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+			"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"color-name": "1.1.3"
+			}
+		},
+		"node_modules/log-symbols/node_modules/color-name": {
+			"version": "1.1.3",
+			"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+			"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/log-symbols/node_modules/has-flag": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+			"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/log-symbols/node_modules/supports-color": {
+			"version": "5.5.0",
+			"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+			"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"has-flag": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"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/merge-stream": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+			"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/mimic-fn": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+			"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/minimatch": {
+			"version": "3.1.2",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+			"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+			"dev": true,
+			"license": "ISC",
+			"dependencies": {
+				"brace-expansion": "^1.1.7"
+			},
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/ms": {
+			"version": "2.1.3",
+			"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+			"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/npm-run-path": {
+			"version": "4.0.1",
+			"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+			"integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"path-key": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/object-assign": {
+			"version": "4.1.1",
+			"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+			"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/once": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+			"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+			"dev": true,
+			"license": "ISC",
+			"dependencies": {
+				"wrappy": "1"
+			}
+		},
+		"node_modules/onetime": {
+			"version": "5.1.2",
+			"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+			"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"mimic-fn": "^2.1.0"
+			},
+			"engines": {
+				"node": ">=6"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/ora": {
+			"version": "7.0.1",
+			"resolved": "https://registry.npmjs.org/ora/-/ora-7.0.1.tgz",
+			"integrity": "sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"chalk": "^5.3.0",
+				"cli-cursor": "^4.0.0",
+				"cli-spinners": "^2.9.0",
+				"is-interactive": "^2.0.0",
+				"is-unicode-supported": "^1.3.0",
+				"log-symbols": "^5.1.0",
+				"stdin-discarder": "^0.1.0",
+				"string-width": "^6.1.0",
+				"strip-ansi": "^7.1.0"
+			},
+			"engines": {
+				"node": ">=16"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/ora/node_modules/chalk": {
+			"version": "5.4.1",
+			"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
+			"integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": "^12.17.0 || ^14.13 || >=16.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/chalk?sponsor=1"
+			}
+		},
+		"node_modules/ora/node_modules/emoji-regex": {
+			"version": "10.4.0",
+			"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
+			"integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/ora/node_modules/is-unicode-supported": {
+			"version": "1.3.0",
+			"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz",
+			"integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/ora/node_modules/log-symbols": {
+			"version": "5.1.0",
+			"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz",
+			"integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"chalk": "^5.0.0",
+				"is-unicode-supported": "^1.1.0"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/ora/node_modules/string-width": {
+			"version": "6.1.0",
+			"resolved": "https://registry.npmjs.org/string-width/-/string-width-6.1.0.tgz",
+			"integrity": "sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"eastasianwidth": "^0.2.0",
+				"emoji-regex": "^10.2.1",
+				"strip-ansi": "^7.0.1"
+			},
+			"engines": {
+				"node": ">=16"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/pako": {
+			"version": "1.0.11",
+			"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+			"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+			"dev": true,
+			"license": "(MIT AND Zlib)"
+		},
+		"node_modules/parent-module": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+			"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"callsites": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/parse-json": {
+			"version": "5.2.0",
+			"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+			"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"@babel/code-frame": "^7.0.0",
+				"error-ex": "^1.3.1",
+				"json-parse-even-better-errors": "^2.3.0",
+				"lines-and-columns": "^1.1.6"
+			},
+			"engines": {
+				"node": ">=8"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"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",
+			"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/path-type": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+			"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/picocolors": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+			"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+			"dev": true,
+			"license": "ISC"
+		},
+		"node_modules/pluralize": {
+			"version": "8.0.0",
+			"resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz",
+			"integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/process-nextick-args": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+			"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"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/require-directory": {
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+			"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/resolve-from": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+			"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/resolve-pkg-maps": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+			"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+			"dev": true,
+			"license": "MIT",
+			"funding": {
+				"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+			}
+		},
+		"node_modules/restore-cursor": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
+			"integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"onetime": "^5.1.0",
+				"signal-exit": "^3.0.2"
+			},
+			"engines": {
+				"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/rimraf": {
+			"version": "2.7.1",
+			"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+			"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+			"deprecated": "Rimraf versions prior to v4 are no longer supported",
+			"dev": true,
+			"license": "ISC",
+			"dependencies": {
+				"glob": "^7.1.3"
+			},
+			"bin": {
+				"rimraf": "bin.js"
+			}
+		},
+		"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/semver": {
+			"version": "7.7.1",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
+			"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
+			"dev": true,
+			"license": "ISC",
+			"bin": {
+				"semver": "bin/semver.js"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/setimmediate": {
+			"version": "1.0.5",
+			"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+			"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/shebang-command": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+			"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"shebang-regex": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/shebang-regex": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+			"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"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/stdin-discarder": {
+			"version": "0.1.0",
+			"resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz",
+			"integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"bl": "^5.0.0"
+			},
+			"engines": {
+				"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"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/string-width": {
+			"version": "4.2.3",
+			"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+			"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"emoji-regex": "^8.0.0",
+				"is-fullwidth-code-point": "^3.0.0",
+				"strip-ansi": "^6.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/string-width/node_modules/ansi-regex": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+			"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/string-width/node_modules/strip-ansi": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+			"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"ansi-regex": "^5.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/strip-ansi": {
+			"version": "7.1.0",
+			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+			"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"ansi-regex": "^6.0.1"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/strip-ansi?sponsor=1"
+			}
+		},
+		"node_modules/strip-final-newline": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+			"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/supports-color": {
+			"version": "7.2.0",
+			"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+			"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"has-flag": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/tsx": {
+			"version": "4.19.3",
+			"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.3.tgz",
+			"integrity": "sha512-4H8vUNGNjQ4V2EOoGw005+c+dGuPSnhpPBPHBtsZdGZBk/iJb4kguGlPWaZTZ3q5nMtFOEsY0nRDlh9PJyd6SQ==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"esbuild": "~0.25.0",
+				"get-tsconfig": "^4.7.5"
+			},
+			"bin": {
+				"tsx": "dist/cli.mjs"
+			},
+			"engines": {
+				"node": ">=18.0.0"
+			},
+			"optionalDependencies": {
+				"fsevents": "~2.3.3"
+			}
+		},
+		"node_modules/typescript": {
+			"version": "5.8.2",
+			"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
+			"integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
+			"dev": true,
+			"license": "Apache-2.0",
+			"bin": {
+				"tsc": "bin/tsc",
+				"tsserver": "bin/tsserver"
+			},
+			"engines": {
+				"node": ">=14.17"
+			}
+		},
+		"node_modules/util-deprecate": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+			"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+			"dev": true,
+			"license": "MIT"
+		},
+		"node_modules/wcwidth": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
+			"integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"defaults": "^1.0.3"
+			}
+		},
+		"node_modules/which": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+			"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+			"dev": true,
+			"license": "ISC",
+			"dependencies": {
+				"isexe": "^2.0.0"
+			},
+			"bin": {
+				"node-which": "bin/node-which"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/wrap-ansi": {
+			"version": "7.0.0",
+			"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+			"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"ansi-styles": "^4.0.0",
+				"string-width": "^4.1.0",
+				"strip-ansi": "^6.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+			}
+		},
+		"node_modules/wrap-ansi/node_modules/ansi-regex": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+			"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+			"dev": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/wrap-ansi/node_modules/strip-ansi": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+			"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"ansi-regex": "^5.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/wrappy": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+			"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+			"dev": true,
+			"license": "ISC"
+		},
+		"node_modules/y18n": {
+			"version": "5.0.8",
+			"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+			"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+			"dev": true,
+			"license": "ISC",
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"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/yaml": {
+			"version": "1.10.2",
+			"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+			"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+			"dev": true,
+			"license": "ISC",
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/yargs": {
+			"version": "17.7.2",
+			"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+			"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+			"dev": true,
+			"license": "MIT",
+			"dependencies": {
+				"cliui": "^8.0.1",
+				"escalade": "^3.1.1",
+				"get-caller-file": "^2.0.5",
+				"require-directory": "^2.1.1",
+				"string-width": "^4.2.3",
+				"y18n": "^5.0.5",
+				"yargs-parser": "^21.1.1"
+			},
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/yargs-parser": {
+			"version": "21.1.1",
+			"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+			"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+			"dev": true,
+			"license": "ISC",
+			"engines": {
+				"node": ">=12"
+			}
+		}
+	}
+}

+ 30 - 0
benchmark/package.json

@@ -0,0 +1,30 @@
+{
+	"name": "benchmark",
+	"version": "0.1.0",
+	"private": true,
+	"main": "out/run.js",
+	"scripts": {
+		"build": "npm run compile && cd .. && npm run compile && npm run build:webview",
+		"lint": "eslint src --ext ts",
+		"check-types": "tsc --noEmit",
+		"compile": "rm -rf out && tsc -p tsconfig.json",
+		"cli": "npm run compile && npx dotenvx run -f .env.local -- tsx src/cli.ts",
+		"clean": "rimraf out",
+		"clean:exercises": "cd exercises && git checkout -f && git clean -fd",
+		"docker:build": "docker build -f Dockerfile -t roo-code-benchmark ..",
+		"docker:run": "touch /tmp/benchmarks.db && docker run -d -it -p 3000:3000 -v /tmp/benchmarks.db:/tmp/benchmarks.db roo-code-benchmark",
+		"docker:start": "npm run docker:build && npm run docker:run",
+		"docker:shell": "docker exec -it $(docker ps --filter \"ancestor=roo-code-benchmark\" -q) /bin/bash",
+		"docker:cli": "docker exec -it -w /home/vscode/repo/benchmark $(docker ps --filter \"ancestor=roo-code-benchmark\" -q) xvfb-run npm run cli --",
+		"docker:stop": "docker stop $(docker ps --filter \"ancestor=roo-code-benchmark\" -q)",
+		"docker:rm": "docker rm $(docker ps -a --filter \"ancestor=roo-code-benchmark\" -q)",
+		"docker:clean": "npm run docker:stop && npm run docker:rm"
+	},
+	"devDependencies": {
+		"@vscode/test-electron": "^2.4.0",
+		"gluegun": "^5.1.2",
+		"tsx": "^4.19.3",
+		"typescript": "^5.4.5",
+		"yargs": "^17.7.2"
+	}
+}

+ 17 - 0
benchmark/prompts/cpp.md

@@ -0,0 +1,17 @@
+Your job is to complete a coding exercise described by `.docs/instructions.md`.
+
+A file with the implementation stubbed out has been created for you, along with a test file.
+
+To successfully complete the exercise, you must pass all the tests in the test file.
+
+To confirm that your solution is correct, you can compile your code and run the tests with:
+
+```
+mkdir -p build && cd build
+cmake -G "Unix Makefiles" -DEXERCISM_RUN_ALL_TESTS=1 ..
+make
+```
+
+Note that running `make` will compile the tests and generate compile time errors. Once the errors are fixed, runningn `make` will build and run the tests.
+
+Do not alter the test file; it should be run as-is.

+ 7 - 0
benchmark/prompts/go.md

@@ -0,0 +1,7 @@
+Your job is to complete a coding exercise described by `.docs/instructions.md`.
+
+A file with the implementation stubbed out has been created for you, along with a test file.
+
+To successfully complete the exercise, you must pass all the tests in the test file.
+
+To confirm that your solution is correct, run the tests with `go test`. Do not alter the test file; it should be run as-is.

+ 7 - 0
benchmark/prompts/java.md

@@ -0,0 +1,7 @@
+Your job is to complete a coding exercise described by `.docs/instructions.md`.
+
+A file with the implementation stubbed out has been created for you, along with a test file.
+
+To successfully complete the exercise, you must pass all the tests in the test file.
+
+To confirm that your solution is correct, run the tests with `./gradlew test`. Do not alter the test file; it should be run as-is.

+ 9 - 0
benchmark/prompts/javascript.md

@@ -0,0 +1,9 @@
+Your job is to complete a coding exercise described by `.docs/instructions.md`.
+
+A file with the implementation stubbed out has been created for you, along with a test file.
+
+To successfully complete the exercise, you must pass all the tests in the test file.
+
+To confirm that your solution is correct, run the tests with `pnpm test`. Do not alter the test file; it should be run as-is.
+
+Before running the tests make sure your environment is set up by running `pnpm install` to install the dependencies.

+ 7 - 0
benchmark/prompts/python.md

@@ -0,0 +1,7 @@
+Your job is to complete a coding exercise described by `.docs/instructions.md`.
+
+A file with the implementation stubbed out has been created for you, along with a test file.
+
+To successfully complete the exercise, you must pass all the tests in the test file.
+
+To confirm that your solution is correct, run the tests with `uv run python3 -m pytest -o markers=task [name]_test.py`. Do not alter the test file; it should be run as-is.

+ 7 - 0
benchmark/prompts/rust.md

@@ -0,0 +1,7 @@
+Your job is to complete a coding exercise described by `.docs/instructions.md`.
+
+A file with the implementation stubbed out has been created for you, along with a test file.
+
+To successfully complete the exercise, you must pass all the tests in the test file.
+
+To confirm that your solution is correct, run the tests with `cargo test`. Do not alter the test file; it should be run as-is.

+ 171 - 0
benchmark/src/cli.ts

@@ -0,0 +1,171 @@
+import * as fs from "fs"
+import * as path from "path"
+
+import { build, filesystem, GluegunPrompt } from "gluegun"
+import { runTests } from "@vscode/test-electron"
+
+// console.log(__dirname)
+// <...>/Roo-Code/benchmark/src
+
+const extensionDevelopmentPath = path.resolve(__dirname, "../../")
+const extensionTestsPath = path.resolve(__dirname, "../out/runExercise")
+const promptsPath = path.resolve(__dirname, "../prompts")
+const exercisesPath = path.resolve(__dirname, "../../../exercises")
+const languages = ["cpp", "go", "java", "javascript", "python", "rust"]
+
+async function runAll({ runId, model }: { runId: number; model: string }) {
+	for (const language of languages) {
+		await runLanguage({ runId, model, language })
+	}
+}
+
+async function runLanguage({ runId, model, language }: { runId: number; model: string; language: string }) {
+	const languagePath = path.resolve(exercisesPath, language)
+
+	if (!fs.existsSync(languagePath)) {
+		console.error(`Language directory ${languagePath} does not exist`)
+		process.exit(1)
+	}
+
+	const exercises = filesystem
+		.subdirectories(languagePath)
+		.map((exercise) => path.basename(exercise))
+		.filter((exercise) => !exercise.startsWith("."))
+
+	for (const exercise of exercises) {
+		await runExercise({ runId, model, language, exercise })
+	}
+}
+
+async function runExercise({
+	runId,
+	model,
+	language,
+	exercise,
+}: {
+	runId: number
+	model: string
+	language: string
+	exercise: string
+}) {
+	const workspacePath = path.resolve(exercisesPath, language, exercise)
+	const promptPath = path.resolve(promptsPath, `${language}.md`)
+
+	const extensionTestsEnv = {
+		PROMPT_PATH: promptPath,
+		WORKSPACE_PATH: workspacePath,
+		OPENROUTER_MODEL_ID: model,
+		RUN_ID: runId.toString(),
+	}
+
+	if (fs.existsSync(path.resolve(workspacePath, "usage.json"))) {
+		console.log(`Test result exists for ${language} / ${exercise}, skipping`)
+		return
+	}
+
+	console.log(`Running ${language} / ${exercise}`)
+
+	await runTests({
+		extensionDevelopmentPath,
+		extensionTestsPath,
+		launchArgs: [workspacePath, "--disable-extensions"],
+		extensionTestsEnv,
+	})
+}
+
+async function askLanguage(prompt: GluegunPrompt) {
+	const languages = filesystem.subdirectories(exercisesPath)
+
+	if (languages.length === 0) {
+		throw new Error(`No languages found in ${exercisesPath}`)
+	}
+
+	const { language } = await prompt.ask<{ language: string }>({
+		type: "select",
+		name: "language",
+		message: "Which language?",
+		choices: languages.map((language) => path.basename(language)).filter((language) => !language.startsWith(".")),
+	})
+
+	return language
+}
+
+async function askExercise(prompt: GluegunPrompt, language: string) {
+	const exercises = filesystem.subdirectories(path.join(exercisesPath, language))
+
+	if (exercises.length === 0) {
+		throw new Error(`No exercises found for ${language}`)
+	}
+
+	const { exercise } = await prompt.ask<{ exercise: string }>({
+		type: "select",
+		name: "exercise",
+		message: "Which exercise?",
+		choices: exercises.map((exercise) => path.basename(exercise)),
+	})
+
+	return exercise
+}
+
+async function createRun({ model }: { model: string }): Promise<{ id: number; model: string }> {
+	const response = await fetch("http://localhost:3000/api/runs", {
+		method: "POST",
+		body: JSON.stringify({ model }),
+	})
+
+	if (!response.ok) {
+		throw new Error(`Failed to create run: ${response.statusText}`)
+	}
+
+	const {
+		run: [run],
+	} = await response.json()
+	return run
+}
+
+async function main() {
+	const cli = build()
+		.brand("benchmark-runner")
+		.src(__dirname)
+		.help()
+		.version()
+		.command({
+			name: "run",
+			run: ({ config, parameters }) => {
+				config.language = parameters.first
+				config.exercise = parameters.second
+
+				if (parameters.options["runId"]) {
+					config.runId = parameters.options["runId"]
+				}
+			},
+		})
+		.defaultCommand() // Use the default command if no args.
+		.create()
+
+	const { print, prompt, config } = await cli.run(process.argv)
+
+	try {
+		const model = "anthropic/claude-3.7-sonnet"
+		const runId = config.runId ? Number(config.runId) : (await createRun({ model })).id
+
+		if (config.language === "all") {
+			console.log("Running all exercises for all languages")
+			await runAll({ runId, model })
+		} else if (config.exercise === "all") {
+			console.log(`Running all exercises for ${config.language}`)
+			await runLanguage({ runId, model, language: config.language })
+		} else {
+			const language = config.language || (await askLanguage(prompt))
+			const exercise = config.exercise || (await askExercise(prompt, language))
+			await runExercise({ runId, model, language, exercise })
+		}
+
+		process.exit(0)
+	} catch (error) {
+		print.error(error)
+		process.exit(1)
+	}
+}
+
+main()

+ 94 - 0
benchmark/src/runExercise.ts

@@ -0,0 +1,94 @@
+import * as fs from "fs/promises"
+import * as path from "path"
+
+import * as vscode from "vscode"
+
+import { RooCodeAPI, TokenUsage } from "../../src/exports/roo-code"
+
+import { waitUntilReady, waitUntilCompleted, sleep } from "./utils"
+
+export async function run() {
+	/**
+	 * Validate environment variables.
+	 */
+
+	const runId = process.env.RUN_ID
+	const openRouterApiKey = process.env.OPENROUTER_API_KEY
+	const openRouterModelId = process.env.OPENROUTER_MODEL_ID
+	const promptPath = process.env.PROMPT_PATH
+	const workspacePath = process.env.WORKSPACE_PATH
+
+	if (!runId || !openRouterApiKey || !openRouterModelId || !promptPath || !workspacePath) {
+		throw new Error("ENV not configured.")
+	}
+
+	const prompt = await fs.readFile(promptPath, "utf-8")
+
+	/**
+	 * Activate the extension.
+	 */
+
+	const extension = vscode.extensions.getExtension<RooCodeAPI>("RooVeterinaryInc.roo-cline")
+
+	if (!extension) {
+		throw new Error("Extension not found.")
+	}
+
+	const api = extension.isActive ? extension.exports : await extension.activate()
+
+	/**
+	 * Wait for the Roo Code to be ready to accept tasks.
+	 */
+
+	await waitUntilReady({ api })
+
+	/**
+	 * Configure Roo Code as needed.
+	 *
+	 * Use Claude 3.7 Sonnet via OpenRouter.
+	 * Don't require approval for anything.
+	 * Run any command without approval.
+	 * Disable checkpoints (for performance).
+	 */
+
+	await api.setConfiguration({
+		apiProvider: "openrouter",
+		openRouterApiKey,
+		openRouterModelId,
+		autoApprovalEnabled: true,
+		alwaysAllowReadOnly: true,
+		alwaysAllowWrite: true,
+		alwaysAllowExecute: true,
+		alwaysAllowBrowser: true,
+		alwaysApproveResubmit: true,
+		alwaysAllowMcp: true,
+		alwaysAllowModeSwitch: true,
+		enableCheckpoints: false,
+	})
+
+	await vscode.workspace
+		.getConfiguration("roo-cline")
+		.update("allowedCommands", ["*"], vscode.ConfigurationTarget.Global)
+
+	await sleep(2_000)
+
+	/**
+	 * Run the task and wait up to 10 minutes for it to complete.
+	 */
+
+	const startTime = Date.now()
+	const taskId = await api.startNewTask(prompt)
+
+	let usage: TokenUsage | undefined = undefined
+
+	try {
+		usage = await waitUntilCompleted({ api, taskId, timeout: 5 * 60 * 1_000 }) // 5m
+	} catch (e) {
+		usage = api.getTokenUsage(taskId)
+	}
+
+	if (usage) {
+		const content = JSON.stringify({ runId: parseInt(runId), ...usage, duration: Date.now() - startTime }, null, 2)
+		await fs.writeFile(path.resolve(workspacePath, "usage.json"), content)
+	}
+}

+ 111 - 0
benchmark/src/utils.ts

@@ -0,0 +1,111 @@
+import * as vscode from "vscode"
+
+import { RooCodeAPI, TokenUsage } from "../../src/exports/roo-code"
+
+type WaitForOptions = {
+	timeout?: number
+	interval?: number
+}
+
+export const waitFor = (
+	condition: (() => Promise<boolean>) | (() => boolean),
+	{ timeout = 30_000, interval = 250 }: WaitForOptions = {},
+) => {
+	let timeoutId: NodeJS.Timeout | undefined = undefined
+
+	return Promise.race([
+		new Promise<void>((resolve) => {
+			const check = async () => {
+				const result = condition()
+				const isSatisfied = result instanceof Promise ? await result : result
+
+				if (isSatisfied) {
+					if (timeoutId) {
+						clearTimeout(timeoutId)
+						timeoutId = undefined
+					}
+
+					resolve()
+				} else {
+					setTimeout(check, interval)
+				}
+			}
+
+			check()
+		}),
+		new Promise((_, reject) => {
+			timeoutId = setTimeout(() => {
+				reject(new Error(`Timeout after ${Math.floor(timeout / 1000)}s`))
+			}, timeout)
+		}),
+	])
+}
+
+type WaitUntilReadyOptions = WaitForOptions & {
+	api: RooCodeAPI
+}
+
+export const waitUntilReady = async ({ api, ...options }: WaitUntilReadyOptions) => {
+	await vscode.commands.executeCommand("roo-cline.SidebarProvider.focus")
+	await waitFor(() => api.isReady(), options)
+}
+
+type WaitUntilAbortedOptions = WaitForOptions & {
+	api: RooCodeAPI
+	taskId: string
+}
+
+export const waitUntilAborted = async ({ api, taskId, ...options }: WaitUntilAbortedOptions) => {
+	const set = new Set<string>()
+	api.on("taskAborted", (taskId) => set.add(taskId))
+	await waitFor(() => set.has(taskId), options)
+}
+
+type WaitUntilCompletedOptions = WaitForOptions & {
+	api: RooCodeAPI
+	taskId: string
+}
+
+export const waitUntilCompleted = async ({ api, taskId, ...options }: WaitUntilCompletedOptions) => {
+	const map = new Map<string, TokenUsage>()
+	api.on("taskCompleted", (taskId, usage) => map.set(taskId, usage))
+	await waitFor(() => map.has(taskId), options)
+	return map.get(taskId)
+}
+
+export const waitForCompletion = async ({
+	api,
+	taskId,
+	...options
+}: WaitUntilReadyOptions & {
+	taskId: string
+}) => waitFor(() => !!getCompletion({ api, taskId }), options)
+
+export const getCompletion = ({ api, taskId }: { api: RooCodeAPI; taskId: string }) =>
+	api.getMessages(taskId).find(({ say, partial }) => say === "completion_result" && partial === false)
+
+type WaitForMessageOptions = WaitUntilReadyOptions & {
+	taskId: string
+	include: string
+	exclude?: string
+}
+
+export const waitForMessage = async ({ api, taskId, include, exclude, ...options }: WaitForMessageOptions) =>
+	waitFor(() => !!getMessage({ api, taskId, include, exclude }), options)
+
+type GetMessageOptions = {
+	api: RooCodeAPI
+	taskId: string
+	include: string
+	exclude?: string
+}
+
+export const getMessage = ({ api, taskId, include, exclude }: GetMessageOptions) =>
+	api
+		.getMessages(taskId)
+		.find(
+			({ type, text }) =>
+				type === "say" && text && text.includes(include) && (!exclude || !text.includes(exclude)),
+		)
+
+export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))

+ 16 - 0
benchmark/tsconfig.json

@@ -0,0 +1,16 @@
+{
+	"compilerOptions": {
+		"module": "CommonJS",
+		"moduleResolution": "Node",
+		"esModuleInterop": true,
+		"target": "ES2022",
+		"lib": ["ES2022", "ESNext.Disposable", "DOM"],
+		"sourceMap": true,
+		"strict": true,
+		"skipLibCheck": true,
+		"useUnknownInCatchVariables": false,
+		"outDir": "out"
+	},
+	"include": ["src", "../src/exports/roo-code.d.ts"],
+	"exclude": ["**/node_modules/**", "out"]
+}

+ 1 - 0
knip.json

@@ -14,6 +14,7 @@
 		"out/**",
 		"bin/**",
 		"e2e/**",
+		"benchmark/**",
 		"src/activate/**",
 		"src/exports/**",
 		"src/extension.ts",

+ 11 - 3
package.json

@@ -280,16 +280,19 @@
 		"install:all": "npm install npm-run-all && npm run install:_all",
 		"install:_all": "npm-run-all -p install-*",
 		"install-extension": "npm install",
-		"install-webview-ui": "cd webview-ui && npm install",
+		"install-webview": "cd webview-ui && npm install",
 		"install-e2e": "cd e2e && npm install",
+		"install-benchmark": "cd benchmark && npm install",
 		"lint": "npm-run-all -p lint:*",
 		"lint:extension": "eslint src --ext ts",
-		"lint:webview-ui": "cd webview-ui && npm run lint",
+		"lint:webview": "cd webview-ui && npm run lint",
 		"lint:e2e": "cd e2e && npm run lint",
+		"lint:benchmark": "cd benchmark && npm run lint",
 		"check-types": "npm-run-all -p check-types:*",
 		"check-types:extension": "tsc --noEmit",
-		"check-types:webview-ui": "cd webview-ui && npm run check-types",
+		"check-types:webview": "cd webview-ui && npm run check-types",
 		"check-types:e2e": "cd e2e && npm run check-types",
+		"check-types:benchmark": "cd benchmark && npm run check-types",
 		"package": "npm run build:webview && npm run check-types && npm run lint && node esbuild.js --production",
 		"pretest": "npm run compile",
 		"dev": "cd webview-ui && npm run dev",
@@ -308,6 +311,11 @@
 		"watch-tests": "tsc -p . -w --outDir out",
 		"changeset": "changeset",
 		"knip": "knip --include files",
+		"clean": "npm-run-all -p clean:*",
+		"clean:extension": "rimraf bin dist out",
+		"clean:webview": "cd webview-ui && npm run clean",
+		"clean:e2e": "cd e2e && npm run clean",
+		"clean:benchmark": "cd benchmark && npm run clean",
 		"update-contributors": "node scripts/update-contributors.js"
 	},
 	"dependencies": {

+ 1 - 1
src/api/providers/openrouter.ts

@@ -177,7 +177,7 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH
 
 		if (lastError) {
 			console.error(
-				`Failed to fetch OpenRouter generation details after ${Date.now() - startTime}ms (${genId})`,
+				`Failed to fetch OpenRouter generation details after attempt #${attempt} (${Date.now() - startTime}ms) [${genId}]`,
 				lastError,
 			)
 		}

+ 38 - 30
src/core/Cline.ts

@@ -12,6 +12,7 @@ import getFolderSize from "get-folder-size"
 import { serializeError } from "serialize-error"
 import * as vscode from "vscode"
 
+import { TokenUsage } from "../exports/roo-code"
 import { ApiHandler, buildApiHandler } from "../api"
 import { ApiStream } from "../api/transform/stream"
 import { DIFF_VIEW_URI_SCHEME, DiffViewProvider } from "../integrations/editor/DiffViewProvider"
@@ -92,6 +93,8 @@ export type ClineEvents = {
 	taskAskResponded: []
 	taskAborted: []
 	taskSpawned: [taskId: string]
+	taskCompleted: [taskId: string, usage: TokenUsage]
+	taskTokenUsageUpdated: [taskId: string, usage: TokenUsage]
 }
 
 export type ClineOptions = {
@@ -351,13 +354,19 @@ export class Cline extends EventEmitter<ClineEvents> {
 		this.emit("message", { action: "updated", message: partialMessage })
 	}
 
+	private getTokenUsage() {
+		const usage = getApiMetrics(combineApiRequests(combineCommandSequences(this.clineMessages.slice(1))))
+		this.emit("taskTokenUsageUpdated", this.taskId, usage)
+		return usage
+	}
+
 	private async saveClineMessages() {
 		try {
 			const taskDir = await this.ensureTaskDirectoryExists()
 			const filePath = path.join(taskDir, GlobalFileNames.uiMessages)
 			await fs.writeFile(filePath, JSON.stringify(this.clineMessages))
 			// combined as they are in ChatView
-			const apiMetrics = getApiMetrics(combineApiRequests(combineCommandSequences(this.clineMessages.slice(1))))
+			const apiMetrics = this.getTokenUsage()
 			const taskMessage = this.clineMessages[0] // first message is always the task say
 			const lastRelevantMessage =
 				this.clineMessages[
@@ -2925,26 +2934,6 @@ export class Cline extends EventEmitter<ClineEvents> {
 					}
 
 					case "attempt_completion": {
-						/*
-						this.consecutiveMistakeCount = 0
-						let resultToSend = result
-						if (command) {
-							await this.say("completion_result", resultToSend)
-							// TODO: currently we don't handle if this command fails, it could be useful to let cline know and retry
-							const [didUserReject, commandResult] = await this.executeCommand(command, true)
-							// if we received non-empty string, the command was rejected or failed
-							if (commandResult) {
-								return [didUserReject, commandResult]
-							}
-							resultToSend = ""
-						}
-						const { response, text, images } = await this.ask("completion_result", resultToSend) // this prompts webview to show 'new task' button, and enable text input (which would be the 'text' here)
-						if (response === "yesButtonClicked") {
-							return [false, ""] // signals to recursive loop to stop (for now this never happens since yesButtonClicked will trigger a new task)
-						}
-						await this.say("user_feedback", text ?? "", images)
-						return [
-						*/
 						const result: string | undefined = block.params.result
 						const command: string | undefined = block.params.command
 						try {
@@ -2996,34 +2985,42 @@ export class Cline extends EventEmitter<ClineEvents> {
 									)
 									break
 								}
+
 								this.consecutiveMistakeCount = 0
 
 								let commandResult: ToolResponse | undefined
+
 								if (command) {
 									if (lastMessage && lastMessage.ask !== "command") {
-										// havent sent a command message yet so first send completion_result then command
+										// Haven't sent a command message yet so
+										// first send completion_result then command.
 										await this.say("completion_result", result, undefined, false)
-										telemetryService.captureTaskCompleted(this.taskId)
 									}
 
-									// complete command message
+									// Complete command message.
 									const didApprove = await askApproval("command", command)
+
 									if (!didApprove) {
 										break
 									}
+
 									const [userRejected, execCommandResult] = await this.executeCommandTool(command!)
+
 									if (userRejected) {
 										this.didRejectTool = true
 										pushToolResult(execCommandResult)
 										break
 									}
-									// user didn't reject, but the command may have output
+
+									// User didn't reject, but the command may have output.
 									commandResult = execCommandResult
 								} else {
 									await this.say("completion_result", result, undefined, false)
-									telemetryService.captureTaskCompleted(this.taskId)
 								}
 
+								telemetryService.captureTaskCompleted(this.taskId)
+								this.emit("taskCompleted", this.taskId, this.getTokenUsage())
+
 								if (this.parentTask) {
 									const didApprove = await askFinishSubTaskApproval()
 
@@ -3036,15 +3033,22 @@ export class Cline extends EventEmitter<ClineEvents> {
 									break
 								}
 
-								// we already sent completion_result says, an empty string asks relinquishes control over button and field
+								// We already sent completion_result says, an
+								// empty string asks relinquishes control over
+								// button and field.
 								const { response, text, images } = await this.ask("completion_result", "", false)
+
+								// Signals to recursive loop to stop (for now
+								// this never happens since yesButtonClicked
+								// will trigger a new task).
 								if (response === "yesButtonClicked") {
-									pushToolResult("") // signals to recursive loop to stop (for now this never happens since yesButtonClicked will trigger a new task)
+									pushToolResult("")
 									break
 								}
-								await this.say("user_feedback", text ?? "", images)
 
+								await this.say("user_feedback", text ?? "", images)
 								const toolResults: (Anthropic.TextBlockParam | Anthropic.ImageBlockParam)[] = []
+
 								if (commandResult) {
 									if (typeof commandResult === "string") {
 										toolResults.push({ type: "text", text: commandResult })
@@ -3052,17 +3056,20 @@ export class Cline extends EventEmitter<ClineEvents> {
 										toolResults.push(...commandResult)
 									}
 								}
+
 								toolResults.push({
 									type: "text",
 									text: `The user has provided feedback on the results. Consider their input to continue the task, and then attempt completion again.\n<feedback>\n${text}\n</feedback>`,
 								})
+
 								toolResults.push(...formatResponse.imageBlocks(images))
+
 								this.userMessageContent.push({
 									type: "text",
 									text: `${toolDescription()} Result:`,
 								})
-								this.userMessageContent.push(...toolResults)
 
+								this.userMessageContent.push(...toolResults)
 								break
 							}
 						} catch (error) {
@@ -3071,6 +3078,7 @@ export class Cline extends EventEmitter<ClineEvents> {
 						}
 					}
 				}
+
 				break
 		}
 

+ 15 - 3
src/exports/api.ts

@@ -3,13 +3,14 @@ import * as vscode from "vscode"
 
 import { ClineProvider } from "../core/webview/ClineProvider"
 
-import { RooCodeAPI, RooCodeEvents, ConfigurationValues } from "./roo-code"
+import { RooCodeAPI, RooCodeEvents, ConfigurationValues, TokenUsage } from "./roo-code"
 import { MessageHistory } from "./message-history"
 
 export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
 	private readonly outputChannel: vscode.OutputChannel
 	private readonly provider: ClineProvider
 	private readonly history: MessageHistory
+	private readonly tokenUsage: Record<string, TokenUsage>
 
 	constructor(outputChannel: vscode.OutputChannel, provider: ClineProvider) {
 		super()
@@ -17,6 +18,7 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
 		this.outputChannel = outputChannel
 		this.provider = provider
 		this.history = new MessageHistory()
+		this.tokenUsage = {}
 
 		this.provider.on("clineAdded", (cline) => {
 			cline.on("message", (message) => this.emit("message", { taskId: cline.taskId, ...message }))
@@ -39,6 +41,8 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
 				this.history.update(taskId, message)
 			}
 		})
+
+		this.on("taskTokenUsageUpdated", (taskId, usage) => (this.tokenUsage[taskId] = usage))
 	}
 
 	public async startNewTask(text?: string, images?: string[]) {
@@ -51,6 +55,10 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
 		return cline.taskId
 	}
 
+	public getCurrentTaskStack() {
+		return this.provider.getCurrentTaskStack()
+	}
+
 	public async clearCurrentTask(lastMessage?: string) {
 		await this.provider.finishSubTask(lastMessage)
 	}
@@ -84,7 +92,11 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
 		return this.history.getMessages(taskId)
 	}
 
-	public getCurrentTaskStack(): string[] {
-		return this.provider.getCurrentTaskStack()
+	public getTokenUsage(taskId: string) {
+		return this.tokenUsage[taskId]
+	}
+
+	public log(message: string) {
+		this.outputChannel.appendLine(message)
 	}
 }

+ 27 - 3
src/exports/roo-code.d.ts

@@ -1,5 +1,14 @@
 import { EventEmitter } from "events"
 
+export interface TokenUsage {
+	totalTokensIn: number
+	totalTokensOut: number
+	totalCacheWrites?: number
+	totalCacheReads?: number
+	totalCost: number
+	contextTokens: number
+}
+
 export interface RooCodeEvents {
 	message: [{ taskId: string; action: "created" | "updated"; message: ClineMessage }]
 	taskStarted: [taskId: string]
@@ -8,6 +17,8 @@ export interface RooCodeEvents {
 	taskAskResponded: [taskId: string]
 	taskAborted: [taskId: string]
 	taskSpawned: [taskId: string, childTaskId: string]
+	taskCompleted: [taskId: string, usage: TokenUsage]
+	taskTokenUsageUpdated: [taskId: string, usage: TokenUsage]
 }
 
 export interface RooCodeAPI extends EventEmitter<RooCodeEvents> {
@@ -19,6 +30,12 @@ export interface RooCodeAPI extends EventEmitter<RooCodeEvents> {
 	 */
 	startNewTask(task?: string, images?: string[]): Promise<string>
 
+	/**
+	 * Returns the current task stack.
+	 * @returns An array of task IDs.
+	 */
+	getCurrentTaskStack(): string[]
+
 	/**
 	 * Clears the current task.
 	 */
@@ -65,10 +82,17 @@ export interface RooCodeAPI extends EventEmitter<RooCodeEvents> {
 	getMessages(taskId: string): ClineMessage[]
 
 	/**
-	 * Returns the current task stack.
-	 * @returns An array of task IDs.
+	 * Returns the token usage for a given task.
+	 * @param taskId The ID of the task.
+	 * @returns A TokenUsage object.
 	 */
-	getCurrentTaskStack(): string[]
+	getTokenUsage(taskId: string): TokenUsage
+
+	/**
+	 * Logs a message to the output channel.
+	 * @param message The message to log.
+	 */
+	log(message: string): void
 }
 
 export type ClineAsk =

+ 4 - 11
src/shared/getApiMetrics.ts

@@ -1,13 +1,6 @@
-import { ClineMessage } from "./ExtensionMessage"
+import { TokenUsage } from "../exports/roo-code"
 
-interface ApiMetrics {
-	totalTokensIn: number
-	totalTokensOut: number
-	totalCacheWrites?: number
-	totalCacheReads?: number
-	totalCost: number
-	contextTokens: number // Total tokens in conversation (last message's tokensIn + tokensOut + cacheWrites + cacheReads)
-}
+import { ClineMessage } from "./ExtensionMessage"
 
 /**
  * Calculates API metrics from an array of ClineMessages.
@@ -26,8 +19,8 @@ interface ApiMetrics {
  * const { totalTokensIn, totalTokensOut, totalCost } = getApiMetrics(messages);
  * // Result: { totalTokensIn: 10, totalTokensOut: 20, totalCost: 0.005 }
  */
-export function getApiMetrics(messages: ClineMessage[]): ApiMetrics {
-	const result: ApiMetrics = {
+export function getApiMetrics(messages: ClineMessage[]) {
+	const result: TokenUsage = {
 		totalTokensIn: 0,
 		totalTokensOut: 0,
 		totalCacheWrites: undefined,

+ 3 - 2
webview-ui/package.json

@@ -6,13 +6,14 @@
 	"scripts": {
 		"lint": "eslint src --ext ts,tsx",
 		"lint-fix": "eslint src --ext ts,tsx --fix",
-		"check-types": "tsc --noEmit",
+		"check-types": "tsc",
 		"test": "jest",
 		"dev": "vite",
 		"build": "tsc -b && vite build",
 		"preview": "vite preview",
 		"storybook": "storybook dev -p 6006",
-		"build-storybook": "storybook build"
+		"build-storybook": "storybook build",
+		"clean": "rimraf build"
 	},
 	"dependencies": {
 		"@radix-ui/react-alert-dialog": "^1.1.6",