commit 3ab975a5b3c740fed34f62cdeece6d1dfbc62236 Author: wisdgod Date: Mon Dec 23 08:57:28 2024 +0800 first commit diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..ba48f78 --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +PORT=3000 +ROUTE_PREFIX= +AUTH_TOKEN= +TOKEN_FILE=.token +TOKEN_LIST_FILE=.token-list diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..ad596dc --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,263 @@ +name: Build + +on: + push: + tags: + - 'v*' + +jobs: + build: + name: Build ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + include: + - os: ubuntu-latest + targets: x86_64-unknown-linux-gnu + - os: windows-latest + targets: x86_64-pc-windows-msvc + - os: macos-latest + targets: x86_64-apple-darwin,aarch64-apple-darwin + + steps: + - uses: actions/checkout@v4.2.2 + + - name: Setup Node.js + uses: actions/setup-node@v4.1.0 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: 'scripts/package.json' + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.targets }} + + - name: Install Linux dependencies (x86_64) + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y \ + build-essential \ + protobuf-compiler \ + pkg-config \ + libssl-dev \ + openssl + + # 安装 npm 依赖 + cd scripts && npm install && cd .. + + # 设置 OpenSSL 环境变量 + echo "OPENSSL_DIR=/usr" >> $GITHUB_ENV + echo "OPENSSL_LIB_DIR=/usr/lib/x86_64-linux-gnu" >> $GITHUB_ENV + echo "OPENSSL_INCLUDE_DIR=/usr/include/openssl" >> $GITHUB_ENV + echo "PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig" >> $GITHUB_ENV + + # - name: Set up Docker Buildx + # if: runner.os == 'Linux' + # uses: docker/setup-buildx-action@v3.8.0 + + # - name: Build Linux arm64 + # if: runner.os == 'Linux' + # run: | + # # 启用 QEMU 支持 + # docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + + # # 创建 Dockerfile + # cat > Dockerfile.arm64 << 'EOF' + # FROM arm64v8/ubuntu:22.04 + + # ENV DEBIAN_FRONTEND=noninteractive + + # RUN apt-get update && apt-get install -y \ + # build-essential \ + # curl \ + # pkg-config \ + # libssl-dev \ + # protobuf-compiler \ + # nodejs \ + # npm \ + # git \ + # && rm -rf /var/lib/apt/lists/* + + # # 安装 Rust + # RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + # ENV PATH="/root/.cargo/bin:${PATH}" + + # WORKDIR /build + # COPY . . + + # # 安装 npm 依赖 + # RUN cd scripts && npm install && cd .. + + # # 构建动态链接版本 + # RUN cargo build --release + + # # 构建静态链接版本 + # RUN RUSTFLAGS="-C target-feature=+crt-static" cargo build --release + # EOF + + # # 构建 arm64 版本 + # docker buildx build --platform linux/arm64 -f Dockerfile.arm64 -t builder-arm64 . + + # # 创建临时容器 + # docker create --name temp-container builder-arm64 sh + + # # 复制动态链接版本 + # docker cp temp-container:/build/target/release/cursor-api ./release/cursor-api-aarch64-unknown-linux-gnu + + # # 复制静态链接版本 + # docker cp temp-container:/build/target/release/cursor-api ./release/cursor-api-static-aarch64-unknown-linux-gnu + + # # 清理临时容器 + # docker rm temp-container + + - name: Build Linux x86_64 (Dynamic) + if: runner.os == 'Linux' + run: bash scripts/build.sh + + - name: Build Linux x86_64 (Static) + if: runner.os == 'Linux' + run: bash scripts/build.sh --static + + - name: Install macOS dependencies + if: runner.os == 'macOS' + run: | + brew install \ + protobuf \ + pkg-config \ + openssl@3 + echo "OPENSSL_DIR=$(brew --prefix openssl@3)" >> $GITHUB_ENV + echo "PKG_CONFIG_PATH=$(brew --prefix openssl@3)/lib/pkgconfig" >> $GITHUB_ENV + + - name: Install Windows dependencies + if: runner.os == 'Windows' + run: | + choco install -y protoc + choco install -y openssl + echo "OPENSSL_DIR=C:/Program Files/OpenSSL-Win64" >> $GITHUB_ENV + echo "PKG_CONFIG_PATH=C:/Program Files/OpenSSL-Win64/lib/pkgconfig" >> $GITHUB_ENV + cd scripts && npm install && cd .. + + - name: Build (Dynamic) + if: runner.os != 'Linux' && runner.os != 'FreeBSD' + run: bash scripts/build.sh + + - name: Build (Static) + if: runner.os != 'Linux' && runner.os != 'FreeBSD' + run: bash scripts/build.sh --static + + - name: Upload artifacts + uses: actions/upload-artifact@v4.5.0 + with: + name: binaries-${{ matrix.os }} + path: release/* + retention-days: 1 + + build-freebsd: + name: Build FreeBSD + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4.2.2 + + - name: Build on FreeBSD + uses: vmactions/freebsd-vm@v1.1.5 + with: + usesh: true + prepare: | + # 设置持久化的环境变量 + echo 'export SSL_CERT_FILE=/etc/ssl/cert.pem' >> /root/.profile + echo 'export PATH="/usr/local/bin:$PATH"' >> /root/.profile + + # 安装基础依赖 + pkg update + pkg install -y \ + git \ + curl \ + node20 \ + www/npm \ + protobuf \ + ca_root_nss \ + bash \ + gmake \ + pkgconf \ + openssl + + export SSL_CERT_FILE=/etc/ssl/cert.pem + + # 克隆代码(确保在正确的目录) + cd /root + git clone $GITHUB_SERVER_URL/$GITHUB_REPOSITORY . + + # 然后再进入 scripts 目录 + cd scripts && npm install && cd .. + + # 安装 rustup 和 Rust + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable + + # 设置持久化的 Rust 环境变量 + echo '. "$HOME/.cargo/env"' >> /root/.profile + + # 添加所需的目标支持 + . /root/.profile + rustup target add x86_64-unknown-freebsd + rustup component add rust-src + + run: | + # 加载环境变量 + . /root/.profile + + # 构建 + echo "构建动态链接版本..." + /usr/local/bin/bash scripts/build.sh + + echo "构建静态链接版本..." + /usr/local/bin/bash scripts/build.sh --static + + - name: Upload artifacts + uses: actions/upload-artifact@v4.5.0 + with: + name: binaries-freebsd + path: release/* + retention-days: 1 + + release: + name: Create Release + needs: [build, build-freebsd] + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v4.2.2 + + - name: Download all artifacts + uses: actions/download-artifact@v4.1.8 + with: + path: artifacts + + - name: Prepare release assets + run: | + mkdir release + cd artifacts + for dir in binaries-*; do + cp -r "$dir"/* ../release/ + done + + - name: Generate checksums + run: | + cd release + sha256sum * > SHA256SUMS.txt + + - name: Create Release + uses: softprops/action-gh-release@v2.2.0 + with: + files: | + release/* + draft: false + prerelease: false + generate_release_notes: true + fail_on_unmatched_files: true \ No newline at end of file diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..76a9bf1 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,56 @@ +name: Docker Build and Push + +on: + workflow_dispatch: + # push: + # tags: + # - 'v*' + +env: + IMAGE_NAME: ${{ github.repository_owner }}/cursor-api + +jobs: + build-and-push: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4.2.2 + + - name: Get version from Cargo.toml + if: github.event_name == 'workflow_dispatch' + id: cargo_version + run: | + VERSION=$(grep '^version = ' Cargo.toml | cut -d '"' -f2) + echo "version=v${VERSION}" >> $GITHUB_OUTPUT + + - name: Log in to Docker Hub + uses: docker/login-action@v3.3.0 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@v5.6.1 + with: + images: ${{ env.IMAGE_NAME }} + tags: | + type=raw,value=${{ steps.cargo_version.outputs.version }},enable=${{ github.event_name == 'workflow_dispatch' }} + type=ref,event=tag,enable=${{ github.event_name == 'push' }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3.8.0 + with: + driver-opts: | + image=moby/buildkit:latest + + - name: Build and push Docker image + uses: docker/build-push-action@v6.10.0 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b2a26ae --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +/target +/get-token/target +/*.log +/*.env +/static/tokeninfo.min.html +node_modules +.DS_Store +/.vscode +/.cargo +/.token +/.token-list +/cursor-api +/cursor-api.exe +/release diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..9a776b3 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2086 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" + +[[package]] +name = "async-compression" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" +dependencies = [ + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "async-trait" +version = "0.1.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "axum" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" + +[[package]] +name = "cc" +version = "1.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cursor-api" +version = "0.1.0" +dependencies = [ + "axum", + "base64", + "bytes", + "chrono", + "dotenvy", + "flate2", + "futures", + "hex", + "prost", + "prost-build", + "rand", + "regex", + "reqwest", + "serde", + "serde_json", + "sha2", + "tokio", + "tokio-stream", + "tower-http", + "uuid", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "h2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "http" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "js-sys" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "multimap" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "openssl" +version = "0.10.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f3e5beed80eb580c68e2c600937ac2c4eedabdfd5ef1e5b7ea4f3fba84497b" +dependencies = [ + "heck", + "itertools", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2f1e56baa61e93533aebc21af4d2134b70f66275e0fcdf3cbe43d77ff7e8fc" +dependencies = [ + "prost", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "reqwest" +version = "0.12.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +dependencies = [ + "async-compression", + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "windows-registry", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.38.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.23.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.134" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" +dependencies = [ + "bitflags", + "bytes", + "http", + "pin-project-lite", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +dependencies = [ + "getrandom", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" + +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..3e13bbd --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,50 @@ +[package] +name = "cursor-api" +version = "0.1.0" +edition = "2021" +authors = ["wisdgod "] + +[build-dependencies] +prost-build = "0.13.4" + +[dependencies] +axum = { version = "0.7.9", features = ["json"] } +base64 = { version = "0.22.1", default-features = false, features = ["std"] } +bytes = "1.9.0" +chrono = { version = "0.4.39", features = ["serde"] } +dotenvy = "0.15.7" +flate2 = { version = "1.0.35", default-features = false, features = ["rust_backend"] } +futures = { version = "0.3.31", default-features = false, features = ["std"] } +hex = { version = "0.4.3", default-features = false, features = ["std"] } +prost = "0.13.4" +rand = { version = "0.8.5", default-features = false, features = ["std", "std_rng"] } +regex = { version = "1.11.1", default-features = false, features = ["std", "perf"] } +reqwest = { version = "0.12.9", features = ["json", "gzip", "stream"] } +serde = { version = "1.0.216", features = ["derive"] } +serde_json = { version = "1.0.134", features = ["std"] } +sha2 = { version = "0.10.8", default-features = false } +tokio = { version = "1.42.0", features = ["rt-multi-thread", "macros", "net", "sync", "time"] } +tokio-stream = { version = "0.1.17", features = ["time"] } +tower-http = { version = "0.6.2", features = ["cors"] } +uuid = { version = "1.11.0", features = ["v4"] } + +# 优化设置 +[profile.release] +lto = true # 启用链接时优化 +codegen-units = 1 # 减少并行编译单元以提高优化 +panic = 'abort' # 在 panic 时直接终止,减小二进制大小 +strip = true # 移除调试符号 +opt-level = 3 # 最高优化级别 + +# 构建脚本设置 +[package.metadata.cross.target.x86_64-unknown-linux-gnu] +image = "ghcr.io/cross-rs/x86_64-unknown-linux-gnu:main" + +[package.metadata.cross.target.aarch64-unknown-linux-gnu] +image = "ghcr.io/cross-rs/aarch64-unknown-linux-gnu:main" + +[package.metadata.cross.target.x86_64-apple-darwin] +image = "ghcr.io/cross-rs/x86_64-apple-darwin:main" + +[package.metadata.cross.target.aarch64-apple-darwin] +image = "ghcr.io/cross-rs/aarch64-apple-darwin:main" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6691f24 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,48 @@ +# 构建阶段 +FROM rust:1.83.0-slim-bookworm as builder + +WORKDIR /app + +# 安装构建依赖 +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + build-essential \ + protobuf-compiler \ + pkg-config \ + libssl-dev \ + nodejs \ + npm \ + && rm -rf /var/lib/apt/lists/* + +# 复制项目文件 +COPY . . + +# 构建 +RUN rustup target add x86_64-unknown-linux-gnu && \ + cargo build --target x86_64-unknown-linux-gnu --release && \ + cp target/x86_64-unknown-linux-gnu/release/cursor-api /app/cursor-api + +# 运行阶段 +FROM debian:bookworm-slim + +WORKDIR /app + +ENV TZ=Asia/Shanghai + +# 安装运行时依赖 +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + ca-certificates \ + tzdata \ + && rm -rf /var/lib/apt/lists/* + +# 复制构建产物 +COPY --from=builder /app/cursor-api . + +# 设置默认端口 +ENV PORT=3000 + +# 动态暴露端口 +EXPOSE ${PORT} + +CMD ["./cursor-api"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..41f3614 --- /dev/null +++ b/README.md @@ -0,0 +1,172 @@ +# cursor-api + +## 获取key + +1. 访问 [www.cursor.com](https://www.cursor.com) 并完成注册登录(赠送 250 次快速响应,可通过删除账号再注册重置) +2. 在浏览器中打开开发者工具(F12) +3. 找到 应用-Cookies 中名为 `WorkosCursorSessionToken` 的值并保存(相当于 openai 的密钥) + +## 接口说明 + +### 基础对话(请求格式和响应格式参考 openai) + +- 接口地址:`/v1/chat/completions` +- 请求方法:POST +- 认证方式:Bearer Token(支持两种认证方式) + 1. 使用环境变量 `AUTH_TOKEN` 进行认证 + 2. 使用 `.token` 文件中的令牌列表进行轮询认证 + +### 获取模型列表 + +- 接口地址:`/v1/models` +- 请求方法:GET + +### 获取环境变量中的x-cursor-checksum + +- 接口地址:`/env-checksum` +- 请求方法:GET + +### 获取随机x-cursor-checksum + +- 接口地址:`/checksum` +- 请求方法:GET + +### 健康检查接口 + +- 接口地址:`/` +- 请求方法:GET + +### 获取日志接口 + +- 接口地址:`/logs` +- 请求方法:GET + +### Token管理接口 + +- 获取Token信息页面:`/tokeninfo` +- 更新Token信息:`/update-tokeninfo` +- 获取Token信息:`/get-tokeninfo` + +## 配置说明 + +### 环境变量 + +- `PORT`: 服务器端口号(默认:3000) +- `AUTH_TOKEN`: 认证令牌(必须,用于API认证) +- `ROUTE_PREFIX`: 路由前缀(可选) +- `TOKEN_FILE`: token文件路径(默认:.token) +- `TOKEN_LIST_FILE`: token列表文件路径(默认:.token-list) + +### Token文件格式 + +1. `.token` 文件:每行一个token,支持以下格式: + ``` + token1 + alias::token2 + ``` + +2. `.token-list` 文件:每行为token和checksum的对应关系: + ``` + token1,checksum1 + token2,checksum2 + ``` + +## 部署 + +### 本地部署 + +#### 从源码编译 + +需要安装 Rust 工具链和 protobuf 编译器: + +```bash +# 安装依赖(Debian/Ubuntu) +apt-get install -y build-essential protobuf-compiler + +# 编译并运行 +cargo build --release +./target/release/cursor-api +``` + +#### 使用预编译二进制 + +从 [Releases](https://github.com/wisdgod/cursor-api/releases) 下载对应平台的二进制文件。 + +### Docker 部署 + +#### Docker 运行示例 + +```bash +docker run -d -e PORT=3000 -e AUTH_TOKEN=your_token -p 3000:3000 wisdgod/cursor-api:latest +``` + +#### Docker 构建示例 + +```bash +docker build -t cursor-api . +docker run -p 3000:3000 cursor-api +``` + +### huggingface部署 + +1. duplicate项目: + [huggingface链接](https://huggingface.co/login?next=%2Fspaces%2Fstevenrk%2Fcursor%3Fduplicate%3Dtrue) + +2. 配置环境变量 + + 在你的space中,点击settings,找到`Variables and secrets`,添加Variables + - name: `AUTH_TOKEN` (注意大写) + - value: 你随意 + +3. 重新部署 + + 点击`Factory rebuild`,等待部署完成 + +4. 接口地址(`Embed this Space`中查看): + ``` + https://{username}-{space-name}.hf.space/v1/models + ``` + +## 注意事项 + +1. 请妥善保管您的 AuthToken,不要泄露给他人 +2. 配置 AUTH_TOKEN 环境变量以增加安全性 +3. 本项目仅供学习研究使用,请遵守 Cursor 的使用条款 + +## 开发 + +### 跨平台编译 + +使用提供的构建脚本: + +```bash +# 仅编译当前平台 +./scripts/build.sh + +# 交叉编译所有支持的平台 +./scripts/build.sh --cross +``` + +支持的目标平台: +- x86_64-unknown-linux-gnu +- x86_64-pc-windows-msvc +- aarch64-unknown-linux-gnu +- x86_64-apple-darwin +- aarch64-apple-darwin + +### 获取token + +- 使用 [get-token](https://github.com/wisdgod/cursor-api/tree/main/get-token) 获取读取当前设备token,仅支持windows与macos + +## 鸣谢 + +- [cursor-api](https://github.com/wisdgod/cursor-api) +- [zhx47/cursor-api](https://github.com/zhx47/cursor-api) +- [luolazyandlazy/cursorToApi](https://github.com/luolazyandlazy/cursorToApi) + +## 许可证 + +版权所有 (c) 2024 + +本软件仅供学习和研究使用。未经授权,不得用于商业用途。 +保留所有权利。 \ No newline at end of file diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..5931ce8 --- /dev/null +++ b/build.rs @@ -0,0 +1,60 @@ +use std::io::Result; +use std::path::Path; +use std::process::Command; + +fn check_and_install_deps() -> Result<()> { + let scripts_dir = Path::new("scripts"); + let node_modules = scripts_dir.join("node_modules"); + + // 如果 node_modules 不存在,运行 npm install + if !node_modules.exists() { + println!("cargo:warning=Installing HTML minifier dependencies..."); + + let status = Command::new("npm") + .current_dir(scripts_dir) + .arg("install") + .status()?; + + if !status.success() { + panic!("Failed to install npm dependencies"); + } + println!("cargo:warning=Dependencies installed successfully"); + } + Ok(()) +} + +fn minify_html() -> Result<()> { + println!("cargo:warning=Minifying HTML files..."); + + let status = Command::new("node") + .args(&["scripts/minify-html.js"]) + .status()?; + + if !status.success() { + panic!("HTML minification failed"); + } + Ok(()) +} + +fn main() -> Result<()> { + // Proto 文件处理 + println!("cargo:rerun-if-changed=src/message.proto"); + let mut config = prost_build::Config::new(); + config.type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]"); + config + .compile_protos(&["src/message.proto"], &["src/"]) + .unwrap(); + + // HTML 文件处理 + println!("cargo:rerun-if-changed=static/tokeninfo.html"); + println!("cargo:rerun-if-changed=scripts/minify-html.js"); + println!("cargo:rerun-if-changed=scripts/package.json"); + + // 检查并安装依赖 + check_and_install_deps()?; + + // 运行 HTML 压缩 + minify_html()?; + + Ok(()) +} diff --git a/get-token/Cargo.lock b/get-token/Cargo.lock new file mode 100644 index 0000000..decbdf6 --- /dev/null +++ b/get-token/Cargo.lock @@ -0,0 +1,189 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "cc" +version = "1.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "get-token" +version = "0.1.0" +dependencies = [ + "rusqlite", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rusqlite" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" +dependencies = [ + "bitflags", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "syn" +version = "2.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/get-token/Cargo.toml b/get-token/Cargo.toml new file mode 100644 index 0000000..afe081b --- /dev/null +++ b/get-token/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "get-token" +version = "0.1.0" +edition = "2021" + +[dependencies] +rusqlite = { version = "0.32.1", default-features = false, features = ["bundled"] } + +[profile.release] +lto = true +codegen-units = 1 +panic = 'abort' +strip = true +opt-level = 3 diff --git a/get-token/README.md b/get-token/README.md new file mode 100644 index 0000000..dc650b2 --- /dev/null +++ b/get-token/README.md @@ -0,0 +1,58 @@ +# Cursor Token 获取工具 + +这个工具用于从 Cursor 编辑器的本地数据库中获取访问令牌。 + +## 系统要求 + +- Rust 编程环境 +- Cargo 包管理器 + +## 构建说明 + +### Windows + +1. 安装 Rust + ```powershell + winget install Rustlang.Rust + # 或访问 https://rustup.rs/ 下载安装程序 + ``` + +2. 克隆项目并构建 + ```powershell + git clone + cd get-token + cargo build --release + ``` + +3. 构建完成后,可执行文件位于 `target/release/get-token.exe` + +### macOS + +1. 安装 Rust + ```bash + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + ``` + +2. 克隆项目并构建 + ```bash + git clone + cd get-token + cargo build --release + ``` + +3. 构建完成后,可执行文件位于 `target/release/get-token` + +## 使用方法 + +直接运行编译好的可执行文件即可: + +- Windows: `.\target\release\get-token.exe` +- macOS: `./target/release/get-token` + +程序将自动查找并显示 Cursor 编辑器的访问令牌。 + +## 注意事项 + +- 确保 Cursor 编辑器已经安装并且至少登录过一次 +- Windows 数据库路径:`%USERPROFILE%\AppData\Roaming\Cursor\User\globalStorage\state.vscdb` +- macOS 数据库路径:`~/Library/Application Support/Cursor/User/globalStorage/state.vscdb` \ No newline at end of file diff --git a/get-token/src/main.rs b/get-token/src/main.rs new file mode 100644 index 0000000..98efd11 --- /dev/null +++ b/get-token/src/main.rs @@ -0,0 +1,29 @@ +use rusqlite::Connection; +use std::env; +use std::path::PathBuf; + +fn main() { + let home_dir = env::var("HOME") + .or_else(|_| env::var("USERPROFILE")) + .unwrap(); + let db_path = if cfg!(target_os = "windows") { + PathBuf::from(home_dir).join(r"AppData\Roaming\Cursor\User\globalStorage\state.vscdb") + } else { + PathBuf::from(home_dir) + .join("Library/Application Support/Cursor/User/globalStorage/state.vscdb") + }; + + match Connection::open(&db_path) { + Ok(conn) => { + match conn.query_row( + "SELECT value FROM ItemTable WHERE key = 'cursorAuth/accessToken'", + [], + |row| row.get::<_, String>(0), + ) { + Ok(token) => println!("访问令牌: {}", token.trim()), + Err(err) => eprintln!("获取令牌时出错: {}", err), + } + } + Err(err) => eprintln!("无法打开数据库: {}", err), + } +} diff --git a/scripts/build.ps1 b/scripts/build.ps1 new file mode 100644 index 0000000..06840e0 --- /dev/null +++ b/scripts/build.ps1 @@ -0,0 +1,158 @@ +# ɫ +function Write-Info { Write-Host "[INFO] $args" -ForegroundColor Blue } +function Write-Warn { Write-Host "[WARN] $args" -ForegroundColor Yellow } +function Write-Error { Write-Host "[ERROR] $args" -ForegroundColor Red; exit 1 } + +# ҪĹ +function Test-Requirements { + $tools = @("cargo", "protoc", "npm", "node") + $missing = @() + + foreach ($tool in $tools) { + if (!(Get-Command $tool -ErrorAction SilentlyContinue)) { + $missing += $tool + } + } + + if ($missing.Count -gt 0) { + Write-Error "ȱٱҪ: $($missing -join ', ')" + } +} + +# Test-Requirements º +function Initialize-VSEnvironment { + Write-Info "ڳʼ Visual Studio ..." + + # ֱʹ֪ vcvarsall.bat · + $vcvarsallPath = "E:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" + + if (-not (Test-Path $vcvarsallPath)) { + Write-Error "δҵ vcvarsall.bat: $vcvarsallPath" + return + } + + Write-Info "ʹ vcvarsall.bat ·: $vcvarsallPath" + + # ȡ + $archArg = "x64" + $command = "`"$vcvarsallPath`" $archArg && set" + + try { + $output = cmd /c "$command" 2>&1 + + # Ƿɹִ + if ($LASTEXITCODE -ne 0) { + Write-Error "vcvarsall.bat ִʧܣ˳: $LASTEXITCODE" + return + } + + # µǰ PowerShell ỰĻ + foreach ($line in $output) { + if ($line -match "^([^=]+)=(.*)$") { + $name = $matches[1] + $value = $matches[2] + if (![string]::IsNullOrEmpty($name)) { + Set-Item -Path "env:$name" -Value $value -ErrorAction SilentlyContinue + } + } + } + + Write-Info "Visual Studio ʼ" + } + catch { + Write-Error "ʼ Visual Studio ʱ: $_" + } +} + +# Ϣ +function Show-Help { + Write-Host @" +÷: $(Split-Path $MyInvocation.MyCommand.Path -Leaf) [ѡ] + +ѡ: + --static ʹþ̬ӣĬ϶̬ӣ + --help ʾ˰Ϣ + +Ĭϱ Windows ֵ֧ļܹ (x64 arm64) +"@ +} + +# +function New-Target { + param ( + [string]$target, + [string]$rustflags + ) + + Write-Info "ڹ $target..." + + # ûִй + $env:RUSTFLAGS = $rustflags + cargo build --target $target --release + + # ƶ + $binaryName = "cursor-api" + if ($UseStatic) { + $binaryName += "-static" + } + + $sourcePath = "target/$target/release/cursor-api.exe" + $targetPath = "release/${binaryName}-${target}.exe" + + if (Test-Path $sourcePath) { + Copy-Item $sourcePath $targetPath -Force + Write-Info "ɹ $target" + } + else { + Write-Warn "δҵ: $target" + return $false + } + return $true +} + +# +$UseStatic = $false + +foreach ($arg in $args) { + switch ($arg) { + "--static" { $UseStatic = $true } + "--help" { Show-Help; exit 0 } + default { Write-Error "δ֪: $arg" } + } +} + +# +try { + # + Test-Requirements + + # ʼ Visual Studio + Initialize-VSEnvironment + + # release Ŀ¼ + New-Item -ItemType Directory -Force -Path "release" | Out-Null + + # Ŀƽ̨ + $targets = @( + "x86_64-pc-windows-msvc", + "aarch64-pc-windows-msvc" + ) + + # þ̬ӱ־ + $rustflags = "" + if ($UseStatic) { + $rustflags = "-C target-feature=+crt-static" + } + + Write-Info "ʼ..." + + # Ŀ + foreach ($target in $targets) { + New-Target -target $target -rustflags $rustflags + } + + Write-Info "ɣ" +} +catch { + Write-Error "з: $_" +} \ No newline at end of file diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100644 index 0000000..c9b62fa --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,192 @@ +#!/bin/bash +set -euo pipefail + +# 颜色输出函数 +info() { echo -e "\033[1;34m[INFO]\033[0m $*"; } +warn() { echo -e "\033[1;33m[WARN]\033[0m $*"; } +error() { echo -e "\033[1;31m[ERROR]\033[0m $*" >&2; exit 1; } + +# 检查是否在 Linux 环境 +is_linux() { + [ "$(uname -s)" = "Linux" ] +} + +# 检查必要的工具 +check_requirements() { + local missing_tools=() + + # 基础工具检查 + for tool in cargo protoc npm node; do + if ! command -v "$tool" &>/dev/null; then + missing_tools+=("$tool") + fi + done + + # Linux 特定检查 + if [[ $USE_CROSS == true ]] && ! command -v cross &>/dev/null; then + missing_tools+=("cross") + fi + + if [[ ${#missing_tools[@]} -gt 0 ]]; then + error "缺少必要工具: ${missing_tools[*]}" + fi +} + +# 帮助信息 +show_help() { + cat << EOF +用法: $(basename "$0") [选项] + +选项: + --cross 使用 cross 进行交叉编译(仅在 Linux 上有效) + --static 使用静态链接(默认动态链接) + --help 显示此帮助信息 + +不带参数时只编译当前平台 +EOF +} + +# 并行构建函数 +build_target() { + local target=$1 + local extension="" + local rustflags="${2:-}" + + info "正在构建 $target..." + + # 确定文件后缀 + [[ $target == *"windows"* ]] && extension=".exe" + + # 设置目标特定的环境变量 + local build_env=() + if [[ $target == "aarch64-unknown-linux-gnu" ]]; then + build_env+=( + "CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc" + "CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++" + "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" + "PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig" + "PKG_CONFIG_ALLOW_CROSS=1" + "OPENSSL_DIR=/usr" + "OPENSSL_INCLUDE_DIR=/usr/include" + "OPENSSL_LIB_DIR=/usr/lib/aarch64-linux-gnu" + ) + fi + + # 判断是否使用 cross(仅在 Linux 上) + if [[ $target != "$CURRENT_TARGET" ]]; then + env ${build_env[@]+"${build_env[@]}"} RUSTFLAGS="$rustflags" cargo build --target "$target" --release + else + env ${build_env[@]+"${build_env[@]}"} RUSTFLAGS="$rustflags" cargo build --release + fi + + # 移动编译产物到 release 目录 + local binary_name="cursor-api" + [[ $USE_STATIC == true ]] && binary_name+="-static" + + if [[ -f "target/$target/release/cursor-api$extension" ]]; then + cp "target/$target/release/cursor-api$extension" "release/${binary_name}-$target$extension" + info "完成构建 $target" + else + warn "构建产物未找到: $target" + return 1 + fi +} + +# 获取 CPU 架构 +ARCH=$(uname -m | sed 's/^aarch64\|arm64$/aarch64/;s/^x86_64\|x86-64\|x64\|amd64$/x86_64/') +OS=$(uname -s) + +# 确定当前系统的目标平台 +get_target() { + local arch=$1 + local os=$2 + case "$os" in + "Darwin") echo "${arch}-apple-darwin" ;; + "Linux") echo "${arch}-unknown-linux-gnu" ;; + "MINGW"*|"MSYS"*|"CYGWIN"*|"Windows_NT") echo "${arch}-pc-windows-msvc" ;; + "FreeBSD") echo "x86_64-unknown-freebsd" ;; + *) error "不支持的系统: $os" ;; + esac +} + +# 设置当前目标平台 +CURRENT_TARGET=$(get_target "$ARCH" "$OS") + +# 检查是否成功获取目标平台 +[ -z "$CURRENT_TARGET" ] && error "无法确定当前系统的目标平台" + +# 获取系统对应的所有目标 +get_targets() { + case "$1" in + "linux") echo "x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu" ;; + "windows") echo "x86_64-pc-windows-msvc aarch64-pc-windows-msvc" ;; + "macos") echo "x86_64-apple-darwin aarch64-apple-darwin" ;; + "freebsd") echo "x86_64-unknown-freebsd" ;; + *) error "不支持的系统组: $1" ;; + esac +} + +# 解析参数 +USE_CROSS=false +USE_STATIC=false + +while [[ $# -gt 0 ]]; do + case $1 in + --cross) USE_CROSS=true ;; + --static) USE_STATIC=true ;; + --help) show_help; exit 0 ;; + *) error "未知参数: $1" ;; + esac + shift +done + +# 检查依赖 +check_requirements + +# 确定要构建的目标 +if [[ $USE_CROSS == true ]] && is_linux; then + # 只在 Linux 上使用 cross 进行多架构构建 + TARGETS=($(get_targets "linux")) +else + # 其他系统或不使用 cross 时只构建当前系统的所有架构 + case "$OS" in + "Darwin") TARGETS=($(get_targets "macos")) ;; + "Linux") TARGETS=("$CURRENT_TARGET") ;; + "MINGW"*|"MSYS"*|"CYGWIN"*|"Windows_NT") TARGETS=($(get_targets "windows")) ;; + "FreeBSD") TARGETS=("$CURRENT_TARGET") ;; + *) error "不支持的系统: $OS" ;; + esac +fi + +# 创建 release 目录 +mkdir -p release + +# 设置静态链接标志 +RUSTFLAGS="" +[[ $USE_STATIC == true ]] && RUSTFLAGS="-C target-feature=+crt-static" + +# 并行构建所有目标 +info "开始构建..." +for target in "${TARGETS[@]}"; do + build_target "$target" "$RUSTFLAGS" & +done + +# 等待所有构建完成 +wait + +# 为 macOS 平台创建通用二进制 +if [[ "$OS" == "Darwin" ]] && [[ ${#TARGETS[@]} -gt 1 ]]; then + binary_suffix="" + [[ $USE_STATIC == true ]] && binary_suffix="-static" + + if [[ -f "release/cursor-api${binary_suffix}-x86_64-apple-darwin" ]] && \ + [[ -f "release/cursor-api${binary_suffix}-aarch64-apple-darwin" ]]; then + info "创建 macOS 通用二进制..." + lipo -create \ + "release/cursor-api${binary_suffix}-x86_64-apple-darwin" \ + "release/cursor-api${binary_suffix}-aarch64-apple-darwin" \ + -output "release/cursor-api${binary_suffix}-universal-apple-darwin" + fi +fi + +info "构建完成!" \ No newline at end of file diff --git a/scripts/minify-html.js b/scripts/minify-html.js new file mode 100644 index 0000000..dcf17fa --- /dev/null +++ b/scripts/minify-html.js @@ -0,0 +1,49 @@ +#!/usr/bin/env node + +const { minify } = require('html-minifier-terser'); +const fs = require('fs'); +const path = require('path'); + +// 配置选项 +const options = { + collapseWhitespace: true, + removeComments: true, + removeEmptyAttributes: true, + removeOptionalTags: true, + removeRedundantAttributes: true, + removeScriptTypeAttributes: true, + removeStyleLinkTypeAttributes: true, + minifyCSS: true, + minifyJS: true, + processScripts: ['application/json'], +}; + +// 处理文件 +async function minifyFile(inputPath, outputPath) { + try { + const html = fs.readFileSync(inputPath, 'utf8'); + const minified = await minify(html, options); + fs.writeFileSync(outputPath, minified); + console.log(`✓ Minified ${path.basename(inputPath)} -> ${path.basename(outputPath)}`); + } catch (err) { + console.error(`✗ Error processing ${inputPath}:`, err); + process.exit(1); + } +} + +// 主函数 +async function main() { + const staticDir = path.join(__dirname, '..', 'static'); + const files = [ + ['tokeninfo.html', 'tokeninfo.min.html'], + ]; + + for (const [input, output] of files) { + await minifyFile( + path.join(staticDir, input), + path.join(staticDir, output) + ); + } +} + +main(); \ No newline at end of file diff --git a/scripts/package-lock.json b/scripts/package-lock.json new file mode 100644 index 0000000..b7fec75 --- /dev/null +++ b/scripts/package-lock.json @@ -0,0 +1,265 @@ +{ + "name": "html-minifier-scripts", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "html-minifier-scripts", + "version": "1.0.0", + "dependencies": { + "html-minifier-terser": "^7.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "license": "MIT", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/clean-css": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", + "license": "MIT", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/html-minifier-terser": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", + "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==", + "license": "MIT", + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "~5.3.2", + "commander": "^10.0.0", + "entities": "^4.4.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.15.1" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": "^14.13.1 || >=16.0.0" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/terser": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz", + "integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==", + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + } + } +} diff --git a/scripts/package.json b/scripts/package.json new file mode 100644 index 0000000..7d41d23 --- /dev/null +++ b/scripts/package.json @@ -0,0 +1,11 @@ +{ + "name": "html-minifier-scripts", + "version": "1.0.0", + "private": true, + "engines": { + "node": ">=14.0.0" + }, + "dependencies": { + "html-minifier-terser": "^7.2.0" + } +} \ No newline at end of file diff --git a/scripts/setup-windows.ps1 b/scripts/setup-windows.ps1 new file mode 100644 index 0000000..b4ff0de --- /dev/null +++ b/scripts/setup-windows.ps1 @@ -0,0 +1,31 @@ +# PowerShell Ϊ UTF-8 +[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 +$OutputEncoding = [System.Text.Encoding]::UTF8 + +# ǷԹԱȨ +if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { + Write-Warning "ԹԱȨд˽ű" + exit 1 +} + +# 鲢װ Chocolatey +if (!(Get-Command choco -ErrorAction SilentlyContinue)) { + Write-Output "ڰװ Chocolatey..." + Set-ExecutionPolicy Bypass -Scope Process -Force + [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072 + iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) +} + +# װҪĹ +Write-Output "ڰװҪĹ..." +choco install -y mingw +choco install -y protoc +choco install -y git + +# װ Rust +Write-Output "ڰװ Rust ..." +rustup target add x86_64-pc-windows-msvc +rustup target add x86_64-unknown-linux-gnu +cargo install cross + +Write-Output "װɣ" \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..574be9f --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,256 @@ +use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _}; +use flate2::read::GzDecoder; +use prost::Message; +use rand::{thread_rng, Rng}; +use sha2::{Digest, Sha256}; +use std::io::Read; +use uuid::Uuid; + +pub mod proto { + include!(concat!(env!("OUT_DIR"), "/cursor.rs")); +} + +use proto::{ChatMessage, ResMessage}; + +#[derive(Debug)] +pub struct ChatInput { + pub role: String, + pub content: String, +} + +fn process_chat_inputs(inputs: Vec) -> (String, Vec) { + // 收集 system 和 developer 指令 + let instructions = inputs + .iter() + .filter(|input| input.role == "system" || input.role == "developer") + .map(|input| input.content.clone()) + .collect::>() + .join("\n\n"); + + // 使用默认指令或收集到的指令 + let instructions = if instructions.is_empty() { + "Respond in Chinese by default".to_string() + } else { + instructions + }; + + // 过滤出 user 和 assistant 对话 + let mut chat_inputs: Vec = inputs + .into_iter() + .filter(|input| input.role == "user" || input.role == "assistant") + .collect(); + + // 处理空对话情况 + if chat_inputs.is_empty() { + return ( + instructions, + vec![proto::chat_message::Message { + role: 1, // user + content: " ".to_string(), + message_id: Uuid::new_v4().to_string(), + }], + ); + } + + // 如果第一条是 assistant,插入空的 user 消息 + if chat_inputs + .first() + .map_or(false, |input| input.role == "assistant") + { + chat_inputs.insert( + 0, + ChatInput { + role: "user".to_string(), + content: " ".to_string(), + }, + ); + } + + // 处理连续相同角色的情况 + let mut i = 1; + while i < chat_inputs.len() { + if chat_inputs[i].role == chat_inputs[i - 1].role { + let insert_role = if chat_inputs[i].role == "user" { + "assistant" + } else { + "user" + }; + chat_inputs.insert( + i, + ChatInput { + role: insert_role.to_string(), + content: " ".to_string(), + }, + ); + } + i += 1; + } + + // 确保最后一条是 user + if chat_inputs + .last() + .map_or(false, |input| input.role == "assistant") + { + chat_inputs.push(ChatInput { + role: "user".to_string(), + content: " ".to_string(), + }); + } + + // 转换为 proto messages + let messages = chat_inputs + .into_iter() + .map(|input| proto::chat_message::Message { + role: if input.role == "user" { 1 } else { 2 }, + content: input.content, + message_id: Uuid::new_v4().to_string(), + }) + .collect(); + + (instructions, messages) +} + +pub async fn encode_chat_message( + inputs: Vec, + model_name: &str, +) -> Result, Box> { + let (instructions, messages) = process_chat_inputs(inputs); + + let chat = ChatMessage { + messages, + instructions: Some(proto::chat_message::Instructions { + content: instructions, + }), + project_path: "/path/to/project".to_string(), + model: Some(proto::chat_message::Model { + name: model_name.to_string(), + empty: String::new(), + }), + request_id: Uuid::new_v4().to_string(), + summary: String::new(), + conversation_id: Uuid::new_v4().to_string(), + }; + + let mut encoded = Vec::new(); + chat.encode(&mut encoded)?; + + let len_prefix = format!("{:010x}", encoded.len()).to_uppercase(); + let content = hex::encode_upper(&encoded); + + Ok(hex::decode(len_prefix + &content)?) +} + +pub async fn decode_response(data: &[u8]) -> String { + if let Ok(decoded) = decode_proto_messages(data) { + if !decoded.is_empty() { + return decoded; + } + } + decompress_response(data).await +} + +fn decode_proto_messages(data: &[u8]) -> Result> { + let hex_str = hex::encode(data); + let mut pos = 0; + let mut messages = Vec::new(); + + while pos + 10 <= hex_str.len() { + let msg_len = i64::from_str_radix(&hex_str[pos..pos + 10], 16)?; + pos += 10; + + if pos + (msg_len * 2) as usize > hex_str.len() { + break; + } + + let msg_data = &hex_str[pos..pos + (msg_len * 2) as usize]; + pos += (msg_len * 2) as usize; + + let buffer = hex::decode(msg_data)?; + let response = ResMessage::decode(&buffer[..])?; + messages.push(response.msg); + } + + Ok(messages.join("")) +} + +async fn decompress_response(data: &[u8]) -> String { + if data.len() <= 5 { + return String::new(); + } + + let mut decoder = GzDecoder::new(&data[5..]); + let mut text = String::new(); + + match decoder.read_to_string(&mut text) { + Ok(_) => { + if !text.contains("<|BEGIN_SYSTEM|>") { + text + } else { + String::new() + } + } + Err(_) => String::new(), + } +} + +pub fn generate_random_id( + size: usize, + dict_type: Option<&str>, + custom_chars: Option<&str>, +) -> String { + let charset = match (dict_type, custom_chars) { + (_, Some(chars)) => chars, + (Some("alphabet"), _) => "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", + (Some("max"), _) => "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-", + _ => "0123456789", + }; + + let mut rng = thread_rng(); + (0..size) + .map(|_| { + let idx = rng.gen_range(0..charset.len()); + charset.chars().nth(idx).unwrap() + }) + .collect() +} + +pub fn generate_hash() -> String { + let random_bytes = rand::thread_rng().gen::<[u8; 32]>(); + let mut hasher = Sha256::new(); + hasher.update(random_bytes); + hex::encode(hasher.finalize()) +} + +fn obfuscate_bytes(bytes: &mut Vec) { + let mut prev: u8 = 165; + for (idx, byte) in bytes.iter_mut().enumerate() { + let old_value = *byte; + *byte = (old_value ^ prev).wrapping_add((idx % 256) as u8); + prev = *byte; + } +} + +pub fn generate_checksum(device_id: &str, mac_addr: Option<&str>) -> String { + let timestamp = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() + / 1_000_000; + + let mut timestamp_bytes = vec![ + ((timestamp >> 40) & 255) as u8, + ((timestamp >> 32) & 255) as u8, + ((timestamp >> 24) & 255) as u8, + ((timestamp >> 16) & 255) as u8, + ((timestamp >> 8) & 255) as u8, + (255 & timestamp) as u8, + ]; + + obfuscate_bytes(&mut timestamp_bytes); + let encoded = BASE64.encode(×tamp_bytes); + + match mac_addr { + Some(mac) => format!("{}{}/{}", encoded, device_id, mac), + None => format!("{}{}", encoded, device_id), + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..d8b048e --- /dev/null +++ b/src/main.rs @@ -0,0 +1,661 @@ +use axum::{ + body::Body, + extract::State, + http::{HeaderMap, StatusCode}, + response::{IntoResponse, Response}, + routing::{get, post}, + Json, Router, +}; +use bytes::Bytes; +use chrono::{DateTime, Local, Utc}; +use futures::StreamExt; +use reqwest::Client; +use serde::{Deserialize, Serialize}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::{convert::Infallible, sync::Arc}; +use tokio::sync::Mutex; +use tower_http::cors::CorsLayer; +use uuid::Uuid; + +// 应用状态 +struct AppState { + start_time: DateTime, + version: String, + total_requests: u64, + active_requests: u64, + request_logs: Vec, + route_prefix: String, + token_infos: Vec, +} + +// 模型定义 +#[derive(Serialize, Deserialize, Clone)] +struct Model { + id: String, + created: i64, + object: String, + owned_by: String, +} + +// 请求日志 +#[derive(Serialize, Clone)] +struct RequestLog { + timestamp: DateTime, + model: String, + checksum: String, + auth_token: String, + stream: bool, +} + +// 聊天请求 +#[derive(Deserialize)] +struct ChatRequest { + model: String, + messages: Vec, + #[serde(default)] + stream: bool, +} + +// 添加用于请求的消息结构体 +#[derive(Serialize, Deserialize)] +struct Message { + role: String, + content: String, +} + +// 支持的模型列表 +mod models; +use models::AVAILABLE_MODELS; + +// 用于存储 token 信息 +#[derive(Debug)] +struct TokenInfo { + token: String, + checksum: String, +} + +// TokenUpdateRequest 结构体 +#[derive(Deserialize)] +struct TokenUpdateRequest { + tokens: String, + #[serde(default)] + token_list: Option, +} + +// 自定义错误类型 +#[derive(Debug)] +enum ChatError { + ModelNotSupported(String), + EmptyMessages, + StreamNotSupported(String), + NoTokens, + RequestFailed(String), + Unauthorized, +} + +impl ChatError { + fn to_json(&self) -> serde_json::Value { + let (code, message) = match self { + ChatError::ModelNotSupported(model) => ( + "model_not_supported", + format!("Model '{}' is not supported", model), + ), + ChatError::EmptyMessages => ( + "empty_messages", + "Message array cannot be empty".to_string(), + ), + ChatError::StreamNotSupported(model) => ( + "stream_not_supported", + format!("Streaming is not supported for model '{}'", model), + ), + ChatError::NoTokens => ("no_tokens", "No available tokens".to_string()), + ChatError::RequestFailed(err) => ("request_failed", format!("Request failed: {}", err)), + ChatError::Unauthorized => ("unauthorized", "Invalid authorization token".to_string()), + }; + + serde_json::json!({ + "error": { + "code": code, + "message": message + } + }) + } +} + +#[tokio::main] +async fn main() { + // 加载环境变量 + dotenvy::dotenv().ok(); + + // 处理 token 文件路径 + let token_file = std::env::var("TOKEN_FILE").unwrap_or_else(|_| ".token".to_string()); + + // 加载 tokens + let token_infos = load_tokens(&token_file); + + // 获取路由前缀配置 + let route_prefix = std::env::var("ROUTE_PREFIX").unwrap_or_default(); + + // 初始化应用状态 + let state = Arc::new(Mutex::new(AppState { + start_time: Local::now(), + version: env!("CARGO_PKG_VERSION").to_string(), + total_requests: 0, + active_requests: 0, + request_logs: Vec::new(), + route_prefix: route_prefix.clone(), + token_infos, + })); + + // 设置路由 + let app = Router::new() + .route("/", get(handle_root)) + .route("/tokeninfo", get(handle_tokeninfo_page)) + .route(&format!("{}/v1/models", route_prefix), get(handle_models)) + .route("/checksum", get(handle_checksum)) + .route("/update-tokeninfo", get(handle_update_tokeninfo)) + .route("/get-tokeninfo", post(handle_get_tokeninfo)) + .route("/update-tokeninfo", post(handle_update_tokeninfo_post)) + .route( + &format!("{}/v1/chat/completions", route_prefix), + post(handle_chat), + ) + .route("/logs", get(handle_logs)) + .layer(CorsLayer::permissive()) + .with_state(state); + + // 启动服务器 + let port = std::env::var("PORT").unwrap_or_else(|_| "3000".to_string()); + let addr = format!("0.0.0.0:{}", port); + println!("服务器运行在端口 {}", port); + + let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); + axum::serve(listener, app).await.unwrap(); +} + +// Token 加载函数 +fn load_tokens(token_file: &str) -> Vec { + let token_list_file = + std::env::var("TOKEN_LIST_FILE").unwrap_or_else(|_| ".token-list".to_string()); + + // 读取并规范化 .token 文件 + let tokens = if let Ok(content) = std::fs::read_to_string(token_file) { + let normalized = content.replace("\r\n", "\n"); + if normalized != content { + std::fs::write(token_file, &normalized).unwrap(); + } + normalized + .lines() + .enumerate() + .filter_map(|(idx, line)| { + let parts: Vec<&str> = line.split("::").collect(); + match parts.len() { + 1 => Some(line.to_string()), + 2 => Some(parts[1].to_string()), + _ => { + println!("警告: 第{}行包含多个'::'分隔符,已忽略此行", idx + 1); + None + } + } + }) + .filter(|s| !s.is_empty()) + .collect::>() + } else { + eprintln!("警告: 无法读取token文件 '{}'", token_file); + Vec::new() + }; + + // 读取现有的 token-list + let mut token_map: std::collections::HashMap = + if let Ok(content) = std::fs::read_to_string(&token_list_file) { + content + .split('\n') + .filter(|s| !s.is_empty()) + .filter_map(|line| { + let parts: Vec<&str> = line.split(',').collect(); + if parts.len() == 2 { + Some((parts[0].to_string(), parts[1].to_string())) + } else { + None + } + }) + .collect() + } else { + std::collections::HashMap::new() + }; + + // 为新 token 生成 checksum + for token in tokens { + if !token_map.contains_key(&token) { + let checksum = cursor_api::generate_checksum( + &cursor_api::generate_hash(), + Some(&cursor_api::generate_hash()), + ); + token_map.insert(token, checksum); + } + } + + // 更新 token-list 文件 + let token_list_content = token_map + .iter() + .map(|(token, checksum)| format!("{},{}", token, checksum)) + .collect::>() + .join("\n"); + std::fs::write(token_list_file, token_list_content).unwrap(); + + // 转换为 TokenInfo vector + token_map + .into_iter() + .map(|(token, checksum)| TokenInfo { token, checksum }) + .collect() +} + +// 根路由处理 +async fn handle_root(State(state): State>>) -> Json { + let state = state.lock().await; + let uptime = (Local::now() - state.start_time).num_seconds(); + + Json(serde_json::json!({ + "status": "healthy", + "version": state.version, + "uptime": uptime, + "stats": { + "started": state.start_time, + "totalRequests": state.total_requests, + "activeRequests": state.active_requests, + "memory": { + "heapTotal": 0, + "heapUsed": 0, + "rss": 0 + } + }, + "models": AVAILABLE_MODELS.iter().map(|m| &m.id).collect::>(), + "endpoints": [ + &format!("{}/v1/chat/completions", state.route_prefix), + &format!("{}/v1/models", state.route_prefix), + "/checksum", + "/tokeninfo", + "/update-tokeninfo", + "/get-tokeninfo" + ] + })) +} + +async fn handle_tokeninfo_page() -> impl IntoResponse { + Response::builder() + .header("Content-Type", "text/html") + .body(include_str!("../static/tokeninfo.min.html").to_string()) + .unwrap() +} + +// 模型列表处理 +async fn handle_models() -> Json { + Json(serde_json::json!({ + "object": "list", + "data": AVAILABLE_MODELS.to_vec() + })) +} + +// Checksum 处理 +async fn handle_checksum() -> Json { + let checksum = cursor_api::generate_checksum( + &cursor_api::generate_hash(), + Some(&cursor_api::generate_hash()), + ); + Json(serde_json::json!({ + "checksum": checksum + })) +} + +// 更新 TokenInfo 处理 +async fn handle_update_tokeninfo( + State(state): State>>, +) -> Json { + // 获取当前的 token 文件路径 + let token_file = std::env::var("TOKEN_FILE").unwrap_or_else(|_| ".token".to_string()); + + // 重新加载 tokens + let token_infos = load_tokens(&token_file); + + // 更新应用状态 + { + let mut state = state.lock().await; + state.token_infos = token_infos; + } + + Json(serde_json::json!({ + "status": "success", + "message": "Token list has been reloaded" + })) +} + +// 获取 TokenInfo 处理 +async fn handle_get_tokeninfo( + State(_state): State>>, + headers: HeaderMap, +) -> Result, StatusCode> { + // 验证 AUTH_TOKEN + let auth_header = headers + .get("authorization") + .and_then(|h| h.to_str().ok()) + .and_then(|h| h.strip_prefix("Bearer ")) + .ok_or(StatusCode::UNAUTHORIZED)?; + + let env_token = std::env::var("AUTH_TOKEN").map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + + if auth_header != env_token { + return Err(StatusCode::UNAUTHORIZED); + } + + // 获取文件路径 + let token_file = std::env::var("TOKEN_FILE").unwrap_or_else(|_| ".token".to_string()); + let token_list_file = + std::env::var("TOKEN_LIST_FILE").unwrap_or_else(|_| ".token-list".to_string()); + + // 读取文件内容 + let tokens = std::fs::read_to_string(&token_file).unwrap_or_else(|_| String::new()); + let token_list = std::fs::read_to_string(&token_list_file).unwrap_or_else(|_| String::new()); + + Ok(Json(serde_json::json!({ + "status": "success", + "token_file": token_file, + "token_list_file": token_list_file, + "tokens": tokens, + "token_list": token_list + }))) +} + +async fn handle_update_tokeninfo_post( + State(state): State>>, + headers: HeaderMap, + Json(request): Json, +) -> Result, StatusCode> { + // 验证 AUTH_TOKEN + let auth_header = headers + .get("authorization") + .and_then(|h| h.to_str().ok()) + .and_then(|h| h.strip_prefix("Bearer ")) + .ok_or(StatusCode::UNAUTHORIZED)?; + + let env_token = std::env::var("AUTH_TOKEN").map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + + if auth_header != env_token { + return Err(StatusCode::UNAUTHORIZED); + } + + // 获取文件路径 + let token_file = std::env::var("TOKEN_FILE").unwrap_or_else(|_| ".token".to_string()); + let token_list_file = + std::env::var("TOKEN_LIST_FILE").unwrap_or_else(|_| ".token-list".to_string()); + + // 写入 .token 文件 + std::fs::write(&token_file, &request.tokens).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + + // 如果提供了 token_list,则写入 + if let Some(token_list) = request.token_list { + std::fs::write(&token_list_file, token_list) + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + } + + // 重新加载 tokens + let token_infos = load_tokens(&token_file); + let token_infos_len = token_infos.len(); + + // 更新应用状态 + { + let mut state = state.lock().await; + state.token_infos = token_infos; + } + + Ok(Json(serde_json::json!({ + "status": "success", + "message": "Token files have been updated and reloaded", + "token_file": token_file, + "token_list_file": token_list_file, + "token_count": token_infos_len + }))) +} + +// 日志处理 +async fn handle_logs(State(state): State>>) -> Json { + let state = state.lock().await; + Json(serde_json::json!({ + "total": state.request_logs.len(), + "logs": state.request_logs, + "timestamp": Utc::now(), + "status": "success" + })) +} + +// 聊天处理函数的签名 +async fn handle_chat( + State(state): State>>, + headers: HeaderMap, + Json(request): Json, +) -> Result, (StatusCode, Json)> { + // 验证模型是否支持 + if !AVAILABLE_MODELS.iter().any(|m| m.id == request.model) { + return Err(( + StatusCode::BAD_REQUEST, + Json(ChatError::ModelNotSupported(request.model.clone()).to_json()), + )); + } + + let request_time = Local::now(); + + // 验证请求 + if request.messages.is_empty() { + return Err(( + StatusCode::BAD_REQUEST, + Json(ChatError::EmptyMessages.to_json()), + )); + } + + // 验证 O1 模型不支持流式输出 + if request.model.starts_with("o1") && request.stream { + return Err(( + StatusCode::BAD_REQUEST, + Json(ChatError::StreamNotSupported(request.model.clone()).to_json()), + )); + } + + // 获取并处理认证令牌 + let auth_token = headers + .get("authorization") + .and_then(|h| h.to_str().ok()) + .and_then(|h| h.strip_prefix("Bearer ")) + .ok_or(( + StatusCode::UNAUTHORIZED, + Json(ChatError::Unauthorized.to_json()), + ))?; + + // 验证环境变量中的 AUTH_TOKEN + if let Ok(env_token) = std::env::var("AUTH_TOKEN") { + if auth_token != env_token { + return Err(( + StatusCode::UNAUTHORIZED, + Json(ChatError::Unauthorized.to_json()), + )); + } + } + + // 完整的令牌处理逻辑和对应的 checksum + let (auth_token, checksum) = { + static CURRENT_KEY_INDEX: AtomicUsize = AtomicUsize::new(0); + let state_guard = state.lock().await; + let token_infos = &state_guard.token_infos; + + if token_infos.is_empty() { + return Err(( + StatusCode::SERVICE_UNAVAILABLE, + Json(ChatError::NoTokens.to_json()), + )); + } + + let index = CURRENT_KEY_INDEX.fetch_add(1, Ordering::SeqCst) % token_infos.len(); + let token_info = &token_infos[index]; + (token_info.token.clone(), token_info.checksum.clone()) + }; + + // 更新请求日志 + { + let mut state = state.lock().await; + state.total_requests += 1; + state.active_requests += 1; + state.request_logs.push(RequestLog { + timestamp: request_time, + model: request.model.clone(), + checksum: checksum.clone(), + auth_token: auth_token.clone(), + stream: request.stream, + }); + + if state.request_logs.len() > 100 { + state.request_logs.remove(0); + } + } + + // 消息转换 + let chat_inputs: Vec = request + .messages + .into_iter() + .map(|m| cursor_api::ChatInput { + role: m.role, + content: m.content, + }) + .collect(); + + // 将消息转换为hex格式 + let hex_data = cursor_api::encode_chat_message(chat_inputs, &request.model) + .await + .map_err(|_| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + Json( + ChatError::RequestFailed("Failed to encode chat message".to_string()).to_json(), + ), + ) + })?; + + // 构建请求客户端 + let client = Client::new(); + let request_id = Uuid::new_v4().to_string(); + let response = client + .post("https://api2.cursor.sh/aiserver.v1.AiService/StreamChat") + .header("Content-Type", "application/connect+proto") + .header("Authorization", format!("Bearer {}", auth_token)) + .header("connect-accept-encoding", "gzip,br") + .header("connect-protocol-version", "1") + .header("user-agent", "connect-es/1.4.0") + .header("x-amzn-trace-id", format!("Root={}", &request_id)) + .header("x-cursor-checksum", &checksum) + .header("x-cursor-client-version", "0.42.5") + .header("x-cursor-timezone", "Asia/Shanghai") + .header("x-ghost-mode", "false") + .header("x-request-id", &request_id) + .header("Host", "api2.cursor.sh") + .body(hex_data) + .send() + .await + .map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(ChatError::RequestFailed(format!("Request failed: {}", e)).to_json()), + ) + })?; + + // 释放活动请求计数 + { + let mut state = state.lock().await; + state.active_requests -= 1; + } + + if request.stream { + let response_id = format!("chatcmpl-{}", Uuid::new_v4()); + + let stream = response.bytes_stream().then(move |chunk| { + let response_id = response_id.clone(); + let model = request.model.clone(); + + async move { + let chunk = chunk.unwrap_or_default(); + let text = cursor_api::decode_response(&chunk).await; + + if text.is_empty() { + return Ok::<_, Infallible>(Bytes::from("[DONE]")); + } + + let data = serde_json::json!({ + "id": &response_id, + "object": "chat.completion.chunk", + "created": chrono::Utc::now().timestamp(), + "model": model, + "choices": [{ + "index": 0, + "delta": { + "content": text + } + }] + }); + + Ok::<_, Infallible>(Bytes::from(format!("data: {}\n\n", data.to_string()))) + } + }); + + Ok(Response::builder() + .header("Content-Type", "text/event-stream") + .header("Cache-Control", "no-cache") + .header("Connection", "keep-alive") + .body(Body::from_stream(stream)) + .unwrap()) + } else { + // 非流式响应 + let mut full_text = String::new(); + let mut stream = response.bytes_stream(); + + while let Some(chunk) = stream.next().await { + let chunk = chunk.map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + Json( + ChatError::RequestFailed(format!("Failed to read response chunk: {}", e)) + .to_json(), + ), + ) + })?; + full_text.push_str(&cursor_api::decode_response(&chunk).await); + } + + // 处理文本 + full_text = full_text + .replace( + regex::Regex::new(r"^.*<\|END_USER\|>").unwrap().as_str(), + "", + ) + .replace(regex::Regex::new(r"^\n[a-zA-Z]?").unwrap().as_str(), "") + .trim() + .to_string(); + + let response_data = serde_json::json!({ + "id": format!("chatcmpl-{}", Uuid::new_v4()), + "object": "chat.completion", + "created": chrono::Utc::now().timestamp(), + "model": request.model, + "choices": [{ + "index": 0, + "message": { + "role": "assistant", + "content": full_text + }, + "finish_reason": "stop" + }], + "usage": { + "prompt_tokens": 0, + "completion_tokens": 0, + "total_tokens": 0 + } + }); + + Ok(Response::new(Body::from(response_data.to_string()))) + } +} diff --git a/src/message.proto b/src/message.proto new file mode 100644 index 0000000..561ee16 --- /dev/null +++ b/src/message.proto @@ -0,0 +1,53 @@ +syntax = "proto3"; + +package cursor; + +message ChatMessage { + message FileContent { + message Position { + int32 line = 1; + int32 column = 2; + } + message Range { + Position start = 1; + Position end = 2; + } + + string filename = 1; + string content = 2; + Position position = 3; + string language = 5; + Range range = 6; + int32 length = 8; + int32 type = 9; + int32 error_code = 11; + } + + message Message { + string content = 1; + int32 role = 2; + string message_id = 13; + } + + message Instructions { + string content = 1; + } + + message Model { + string name = 1; + string empty = 4; + } + + // repeated FileContent files = 1; + repeated Message messages = 2; + Instructions instructions = 4; + string projectPath = 5; + Model model = 7; + string requestId = 9; + string summary = 11; // 或许是空的,描述会话做了什么事情,但是不是标题 或许可以当作额外的设定来用 + string conversationId = 15; // 又来一个uuid +} + +message ResMessage { + string msg = 1; +} \ No newline at end of file diff --git a/src/models.rs b/src/models.rs new file mode 100644 index 0000000..aeab6b2 --- /dev/null +++ b/src/models.rs @@ -0,0 +1,133 @@ +use std::sync::LazyLock; +use crate::Model; + +const MODEL_OBJECT: &str = "model"; +const ANTHROPIC: &str = "anthropic"; +const CURSOR: &str = "cursor"; +const GOOGLE: &str = "google"; +const OPENAI: &str = "openai"; + +pub static AVAILABLE_MODELS: LazyLock> = LazyLock::new(|| { + vec![ + Model { + id: "cursor-small".into(), + created: 1706659200, + object: MODEL_OBJECT.into(), + owned_by: CURSOR.into() + }, + Model { + id: "claude-3-opus".into(), + created: 1706659200, + object: MODEL_OBJECT.into(), + owned_by: ANTHROPIC.into() + }, + Model { + id: "cursor-fast".into(), + created: 1706659200, + object: MODEL_OBJECT.into(), + owned_by: CURSOR.into() + }, + Model { + id: "gpt-3.5-turbo".into(), + created: 1706659200, + object: MODEL_OBJECT.into(), + owned_by: OPENAI.into() + }, + Model { + id: "gpt-4-turbo-2024-04-09".into(), + created: 1706659200, + object: MODEL_OBJECT.into(), + owned_by: OPENAI.into() + }, + Model { + id: "gpt-4".into(), + created: 1706659200, + object: MODEL_OBJECT.into(), + owned_by: OPENAI.into() + }, + Model { + id: "gpt-4o-128k".into(), + created: 1706659200, + object: MODEL_OBJECT.into(), + owned_by: OPENAI.into() + }, + Model { + id: "gemini-1.5-flash-500k".into(), + created: 1706659200, + object: MODEL_OBJECT.into(), + owned_by: GOOGLE.into() + }, + Model { + id: "claude-3-haiku-200k".into(), + created: 1706659200, + object: MODEL_OBJECT.into(), + owned_by: ANTHROPIC.into() + }, + Model { + id: "claude-3-5-sonnet-200k".into(), + created: 1706659200, + object: MODEL_OBJECT.into(), + owned_by: ANTHROPIC.into() + }, + Model { + id: "claude-3-5-sonnet-20240620".into(), + created: 1706659200, + object: MODEL_OBJECT.into(), + owned_by: ANTHROPIC.into() + }, + Model { + id: "claude-3-5-sonnet-20241022".into(), + created: 1706659200, + object: MODEL_OBJECT.into(), + owned_by: ANTHROPIC.into() + }, + Model { + id: "gpt-4o-mini".into(), + created: 1706659200, + object: MODEL_OBJECT.into(), + owned_by: OPENAI.into() + }, + Model { + id: "o1-mini".into(), + created: 1706659200, + object: MODEL_OBJECT.into(), + owned_by: OPENAI.into() + }, + Model { + id: "o1-preview".into(), + created: 1706659200, + object: MODEL_OBJECT.into(), + owned_by: OPENAI.into() + }, + Model { + id: "o1".into(), + created: 1706659200, + object: MODEL_OBJECT.into(), + owned_by: OPENAI.into() + }, + Model { + id: "claude-3.5-haiku".into(), + created: 1706659200, + object: MODEL_OBJECT.into(), + owned_by: ANTHROPIC.into() + }, + Model { + id: "gemini-exp-1206".into(), + created: 1706659200, + object: MODEL_OBJECT.into(), + owned_by: GOOGLE.into() + }, + Model { + id: "gemini-2.0-flash-thinking-exp".into(), + created: 1706659200, + object: MODEL_OBJECT.into(), + owned_by: GOOGLE.into() + }, + Model { + id: "gemini-2.0-flash-exp".into(), + created: 1706659200, + object: MODEL_OBJECT.into(), + owned_by: GOOGLE.into() + } + ] +}); \ No newline at end of file diff --git a/static/tokeninfo.html b/static/tokeninfo.html new file mode 100644 index 0000000..5bdaf72 --- /dev/null +++ b/static/tokeninfo.html @@ -0,0 +1,184 @@ + + + + + + + Token 信息管理 + + + + +

Token 信息管理

+ +
+

认证

+ +
+ +
+

Token 配置

+
+ + +
+ +

Token 文件内容

+ + +

Token List 文件内容

+ +
+ +
+ + + + + \ No newline at end of file