├── .env.example ├── .github └── workflows │ ├── build.yml │ └── docker.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── README.md ├── build.rs ├── get-token ├── Cargo.lock ├── Cargo.toml ├── README.md └── src │ └── main.rs ├── scripts ├── build.ps1 ├── build.sh ├── minify-html.js ├── package-lock.json ├── package.json └── setup-windows.ps1 ├── src ├── lib.rs ├── main.rs ├── message.proto └── models.rs └── static └── tokeninfo.html /.env.example: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | ROUTE_PREFIX= 3 | AUTH_TOKEN= 4 | TOKEN_FILE=.token 5 | TOKEN_LIST_FILE=.token-list 6 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | build: 10 | name: Build ${{ matrix.os }} 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | matrix: 14 | os: [ubuntu-latest, windows-latest, macos-latest] 15 | include: 16 | - os: ubuntu-latest 17 | targets: x86_64-unknown-linux-gnu 18 | - os: windows-latest 19 | targets: x86_64-pc-windows-msvc 20 | - os: macos-latest 21 | targets: x86_64-apple-darwin,aarch64-apple-darwin 22 | 23 | steps: 24 | - uses: actions/checkout@v4.2.2 25 | 26 | - name: Setup Node.js 27 | uses: actions/setup-node@v4.1.0 28 | with: 29 | node-version: '20' 30 | cache: 'npm' 31 | cache-dependency-path: 'scripts/package.json' 32 | 33 | - name: Install Rust 34 | uses: dtolnay/rust-toolchain@stable 35 | with: 36 | targets: ${{ matrix.targets }} 37 | 38 | - name: Install Linux dependencies (x86_64) 39 | if: runner.os == 'Linux' 40 | run: | 41 | sudo apt-get update 42 | sudo apt-get install -y \ 43 | build-essential \ 44 | protobuf-compiler \ 45 | pkg-config \ 46 | libssl-dev \ 47 | openssl 48 | 49 | # 安装 npm 依赖 50 | cd scripts && npm install && cd .. 51 | 52 | # 设置 OpenSSL 环境变量 53 | echo "OPENSSL_DIR=/usr" >> $GITHUB_ENV 54 | echo "OPENSSL_LIB_DIR=/usr/lib/x86_64-linux-gnu" >> $GITHUB_ENV 55 | echo "OPENSSL_INCLUDE_DIR=/usr/include/openssl" >> $GITHUB_ENV 56 | echo "PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig" >> $GITHUB_ENV 57 | 58 | # - name: Set up Docker Buildx 59 | # if: runner.os == 'Linux' 60 | # uses: docker/setup-buildx-action@v3.8.0 61 | 62 | # - name: Build Linux arm64 63 | # if: runner.os == 'Linux' 64 | # run: | 65 | # # 启用 QEMU 支持 66 | # docker run --rm --privileged multiarch/qemu-user-static --reset -p yes 67 | 68 | # # 创建 Dockerfile 69 | # cat > Dockerfile.arm64 << 'EOF' 70 | # FROM arm64v8/ubuntu:22.04 71 | 72 | # ENV DEBIAN_FRONTEND=noninteractive 73 | 74 | # RUN apt-get update && apt-get install -y \ 75 | # build-essential \ 76 | # curl \ 77 | # pkg-config \ 78 | # libssl-dev \ 79 | # protobuf-compiler \ 80 | # nodejs \ 81 | # npm \ 82 | # git \ 83 | # && rm -rf /var/lib/apt/lists/* 84 | 85 | # # 安装 Rust 86 | # RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 87 | # ENV PATH="/root/.cargo/bin:${PATH}" 88 | 89 | # WORKDIR /build 90 | # COPY . . 91 | 92 | # # 安装 npm 依赖 93 | # RUN cd scripts && npm install && cd .. 94 | 95 | # # 构建动态链接版本 96 | # RUN cargo build --release 97 | 98 | # # 构建静态链接版本 99 | # RUN RUSTFLAGS="-C target-feature=+crt-static" cargo build --release 100 | # EOF 101 | 102 | # # 构建 arm64 版本 103 | # docker buildx build --platform linux/arm64 -f Dockerfile.arm64 -t builder-arm64 . 104 | 105 | # # 创建临时容器 106 | # docker create --name temp-container builder-arm64 sh 107 | 108 | # # 复制动态链接版本 109 | # docker cp temp-container:/build/target/release/cursor-api ./release/cursor-api-aarch64-unknown-linux-gnu 110 | 111 | # # 复制静态链接版本 112 | # docker cp temp-container:/build/target/release/cursor-api ./release/cursor-api-static-aarch64-unknown-linux-gnu 113 | 114 | # # 清理临时容器 115 | # docker rm temp-container 116 | 117 | - name: Build Linux x86_64 (Dynamic) 118 | if: runner.os == 'Linux' 119 | run: bash scripts/build.sh 120 | 121 | - name: Build Linux x86_64 (Static) 122 | if: runner.os == 'Linux' 123 | run: bash scripts/build.sh --static 124 | 125 | - name: Install macOS dependencies 126 | if: runner.os == 'macOS' 127 | run: | 128 | brew install \ 129 | protobuf \ 130 | pkg-config \ 131 | openssl@3 132 | echo "OPENSSL_DIR=$(brew --prefix openssl@3)" >> $GITHUB_ENV 133 | echo "PKG_CONFIG_PATH=$(brew --prefix openssl@3)/lib/pkgconfig" >> $GITHUB_ENV 134 | 135 | - name: Install Windows dependencies 136 | if: runner.os == 'Windows' 137 | run: | 138 | choco install -y protoc 139 | choco install -y openssl 140 | echo "OPENSSL_DIR=C:/Program Files/OpenSSL-Win64" >> $GITHUB_ENV 141 | echo "PKG_CONFIG_PATH=C:/Program Files/OpenSSL-Win64/lib/pkgconfig" >> $GITHUB_ENV 142 | cd scripts && npm install && cd .. 143 | 144 | - name: Build (Dynamic) 145 | if: runner.os != 'Linux' && runner.os != 'FreeBSD' 146 | run: bash scripts/build.sh 147 | 148 | - name: Build (Static) 149 | if: runner.os != 'Linux' && runner.os != 'FreeBSD' 150 | run: bash scripts/build.sh --static 151 | 152 | - name: Upload artifacts 153 | uses: actions/upload-artifact@v4.5.0 154 | with: 155 | name: binaries-${{ matrix.os }} 156 | path: release/* 157 | retention-days: 1 158 | 159 | build-freebsd: 160 | name: Build FreeBSD 161 | runs-on: ubuntu-latest 162 | 163 | steps: 164 | - uses: actions/checkout@v4.2.2 165 | 166 | - name: Build on FreeBSD 167 | uses: vmactions/freebsd-vm@v1.1.5 168 | with: 169 | usesh: true 170 | prepare: | 171 | # 设置持久化的环境变量 172 | echo 'export SSL_CERT_FILE=/etc/ssl/cert.pem' >> /root/.profile 173 | echo 'export PATH="/usr/local/bin:$PATH"' >> /root/.profile 174 | 175 | # 安装基础依赖 176 | pkg update 177 | pkg install -y \ 178 | git \ 179 | curl \ 180 | node20 \ 181 | www/npm \ 182 | protobuf \ 183 | ca_root_nss \ 184 | bash \ 185 | gmake \ 186 | pkgconf \ 187 | openssl 188 | 189 | export SSL_CERT_FILE=/etc/ssl/cert.pem 190 | 191 | # 克隆代码(确保在正确的目录) 192 | cd /root 193 | git clone $GITHUB_SERVER_URL/$GITHUB_REPOSITORY . 194 | 195 | # 然后再进入 scripts 目录 196 | cd scripts && npm install && cd .. 197 | 198 | # 安装 rustup 和 Rust 199 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable 200 | 201 | # 设置持久化的 Rust 环境变量 202 | echo '. "$HOME/.cargo/env"' >> /root/.profile 203 | 204 | # 添加所需的目标支持 205 | . /root/.profile 206 | rustup target add x86_64-unknown-freebsd 207 | rustup component add rust-src 208 | 209 | run: | 210 | # 加载环境变量 211 | . /root/.profile 212 | 213 | # 构建 214 | echo "构建动态链接版本..." 215 | /usr/local/bin/bash scripts/build.sh 216 | 217 | echo "构建静态链接版本..." 218 | /usr/local/bin/bash scripts/build.sh --static 219 | 220 | - name: Upload artifacts 221 | uses: actions/upload-artifact@v4.5.0 222 | with: 223 | name: binaries-freebsd 224 | path: release/* 225 | retention-days: 1 226 | 227 | release: 228 | name: Create Release 229 | needs: [build, build-freebsd] 230 | runs-on: ubuntu-latest 231 | permissions: 232 | contents: write 233 | 234 | steps: 235 | - uses: actions/checkout@v4.2.2 236 | 237 | - name: Download all artifacts 238 | uses: actions/download-artifact@v4.1.8 239 | with: 240 | path: artifacts 241 | 242 | - name: Prepare release assets 243 | run: | 244 | mkdir release 245 | cd artifacts 246 | for dir in binaries-*; do 247 | cp -r "$dir"/* ../release/ 248 | done 249 | 250 | - name: Generate checksums 251 | run: | 252 | cd release 253 | sha256sum * > SHA256SUMS.txt 254 | 255 | - name: Create Release 256 | uses: softprops/action-gh-release@v2.2.0 257 | with: 258 | files: | 259 | release/* 260 | draft: false 261 | prerelease: false 262 | generate_release_notes: true 263 | fail_on_unmatched_files: true -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Docker Build and Push 2 | 3 | on: 4 | workflow_dispatch: 5 | # push: 6 | # tags: 7 | # - 'v*' 8 | 9 | env: 10 | IMAGE_NAME: ${{ github.repository_owner }}/cursor-api 11 | 12 | jobs: 13 | build-and-push: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v4.2.2 19 | 20 | - name: Get version from Cargo.toml 21 | if: github.event_name == 'workflow_dispatch' 22 | id: cargo_version 23 | run: | 24 | VERSION=$(grep '^version = ' Cargo.toml | cut -d '"' -f2) 25 | echo "version=v${VERSION}" >> $GITHUB_OUTPUT 26 | 27 | - name: Log in to Docker Hub 28 | uses: docker/login-action@v3.3.0 29 | with: 30 | username: ${{ secrets.DOCKERHUB_USERNAME }} 31 | password: ${{ secrets.DOCKERHUB_TOKEN }} 32 | 33 | - name: Extract metadata for Docker 34 | id: meta 35 | uses: docker/metadata-action@v5.6.1 36 | with: 37 | images: ${{ env.IMAGE_NAME }} 38 | tags: | 39 | type=raw,value=${{ steps.cargo_version.outputs.version }},enable=${{ github.event_name == 'workflow_dispatch' }} 40 | type=ref,event=tag,enable=${{ github.event_name == 'push' }} 41 | 42 | - name: Set up Docker Buildx 43 | uses: docker/setup-buildx-action@v3.8.0 44 | with: 45 | driver-opts: | 46 | image=moby/buildkit:latest 47 | 48 | - name: Build and push Docker image 49 | uses: docker/build-push-action@v6.10.0 50 | with: 51 | context: . 52 | push: true 53 | tags: ${{ steps.meta.outputs.tags }} 54 | labels: ${{ steps.meta.outputs.labels }} 55 | cache-from: type=gha 56 | cache-to: type=gha,mode=max -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /get-token/target 3 | /*.log 4 | /*.env 5 | /static/tokeninfo.min.html 6 | node_modules 7 | .DS_Store 8 | /.vscode 9 | /.cargo 10 | /.token 11 | /.token-list 12 | /cursor-api 13 | /cursor-api.exe 14 | /release 15 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.24.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "aho-corasick" 22 | version = "1.1.3" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 25 | dependencies = [ 26 | "memchr", 27 | ] 28 | 29 | [[package]] 30 | name = "android-tzdata" 31 | version = "0.1.1" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 34 | 35 | [[package]] 36 | name = "android_system_properties" 37 | version = "0.1.5" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 40 | dependencies = [ 41 | "libc", 42 | ] 43 | 44 | [[package]] 45 | name = "anyhow" 46 | version = "1.0.95" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" 49 | 50 | [[package]] 51 | name = "async-compression" 52 | version = "0.4.18" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" 55 | dependencies = [ 56 | "flate2", 57 | "futures-core", 58 | "memchr", 59 | "pin-project-lite", 60 | "tokio", 61 | ] 62 | 63 | [[package]] 64 | name = "async-trait" 65 | version = "0.1.83" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" 68 | dependencies = [ 69 | "proc-macro2", 70 | "quote", 71 | "syn", 72 | ] 73 | 74 | [[package]] 75 | name = "atomic-waker" 76 | version = "1.1.2" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 79 | 80 | [[package]] 81 | name = "autocfg" 82 | version = "1.4.0" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 85 | 86 | [[package]] 87 | name = "axum" 88 | version = "0.7.9" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" 91 | dependencies = [ 92 | "async-trait", 93 | "axum-core", 94 | "bytes", 95 | "futures-util", 96 | "http", 97 | "http-body", 98 | "http-body-util", 99 | "hyper", 100 | "hyper-util", 101 | "itoa", 102 | "matchit", 103 | "memchr", 104 | "mime", 105 | "percent-encoding", 106 | "pin-project-lite", 107 | "rustversion", 108 | "serde", 109 | "serde_json", 110 | "serde_path_to_error", 111 | "serde_urlencoded", 112 | "sync_wrapper", 113 | "tokio", 114 | "tower", 115 | "tower-layer", 116 | "tower-service", 117 | "tracing", 118 | ] 119 | 120 | [[package]] 121 | name = "axum-core" 122 | version = "0.4.5" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" 125 | dependencies = [ 126 | "async-trait", 127 | "bytes", 128 | "futures-util", 129 | "http", 130 | "http-body", 131 | "http-body-util", 132 | "mime", 133 | "pin-project-lite", 134 | "rustversion", 135 | "sync_wrapper", 136 | "tower-layer", 137 | "tower-service", 138 | "tracing", 139 | ] 140 | 141 | [[package]] 142 | name = "backtrace" 143 | version = "0.3.74" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 146 | dependencies = [ 147 | "addr2line", 148 | "cfg-if", 149 | "libc", 150 | "miniz_oxide", 151 | "object", 152 | "rustc-demangle", 153 | "windows-targets", 154 | ] 155 | 156 | [[package]] 157 | name = "base64" 158 | version = "0.22.1" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 161 | 162 | [[package]] 163 | name = "bitflags" 164 | version = "2.6.0" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 167 | 168 | [[package]] 169 | name = "block-buffer" 170 | version = "0.10.4" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 173 | dependencies = [ 174 | "generic-array", 175 | ] 176 | 177 | [[package]] 178 | name = "bumpalo" 179 | version = "3.16.0" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 182 | 183 | [[package]] 184 | name = "byteorder" 185 | version = "1.5.0" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 188 | 189 | [[package]] 190 | name = "bytes" 191 | version = "1.9.0" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" 194 | 195 | [[package]] 196 | name = "cc" 197 | version = "1.2.5" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e" 200 | dependencies = [ 201 | "shlex", 202 | ] 203 | 204 | [[package]] 205 | name = "cfg-if" 206 | version = "1.0.0" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 209 | 210 | [[package]] 211 | name = "chrono" 212 | version = "0.4.39" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" 215 | dependencies = [ 216 | "android-tzdata", 217 | "iana-time-zone", 218 | "js-sys", 219 | "num-traits", 220 | "serde", 221 | "wasm-bindgen", 222 | "windows-targets", 223 | ] 224 | 225 | [[package]] 226 | name = "core-foundation" 227 | version = "0.9.4" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 230 | dependencies = [ 231 | "core-foundation-sys", 232 | "libc", 233 | ] 234 | 235 | [[package]] 236 | name = "core-foundation-sys" 237 | version = "0.8.7" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 240 | 241 | [[package]] 242 | name = "cpufeatures" 243 | version = "0.2.16" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" 246 | dependencies = [ 247 | "libc", 248 | ] 249 | 250 | [[package]] 251 | name = "crc32fast" 252 | version = "1.4.2" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" 255 | dependencies = [ 256 | "cfg-if", 257 | ] 258 | 259 | [[package]] 260 | name = "crypto-common" 261 | version = "0.1.6" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 264 | dependencies = [ 265 | "generic-array", 266 | "typenum", 267 | ] 268 | 269 | [[package]] 270 | name = "cursor-api" 271 | version = "0.1.0" 272 | dependencies = [ 273 | "axum", 274 | "base64", 275 | "bytes", 276 | "chrono", 277 | "dotenvy", 278 | "flate2", 279 | "futures", 280 | "hex", 281 | "prost", 282 | "prost-build", 283 | "rand", 284 | "regex", 285 | "reqwest", 286 | "serde", 287 | "serde_json", 288 | "sha2", 289 | "tokio", 290 | "tokio-stream", 291 | "tower-http", 292 | "uuid", 293 | ] 294 | 295 | [[package]] 296 | name = "digest" 297 | version = "0.10.7" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 300 | dependencies = [ 301 | "block-buffer", 302 | "crypto-common", 303 | ] 304 | 305 | [[package]] 306 | name = "displaydoc" 307 | version = "0.2.5" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 310 | dependencies = [ 311 | "proc-macro2", 312 | "quote", 313 | "syn", 314 | ] 315 | 316 | [[package]] 317 | name = "dotenvy" 318 | version = "0.15.7" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" 321 | 322 | [[package]] 323 | name = "either" 324 | version = "1.13.0" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" 327 | 328 | [[package]] 329 | name = "encoding_rs" 330 | version = "0.8.35" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" 333 | dependencies = [ 334 | "cfg-if", 335 | ] 336 | 337 | [[package]] 338 | name = "equivalent" 339 | version = "1.0.1" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 342 | 343 | [[package]] 344 | name = "errno" 345 | version = "0.3.10" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" 348 | dependencies = [ 349 | "libc", 350 | "windows-sys 0.59.0", 351 | ] 352 | 353 | [[package]] 354 | name = "fastrand" 355 | version = "2.3.0" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 358 | 359 | [[package]] 360 | name = "fixedbitset" 361 | version = "0.4.2" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" 364 | 365 | [[package]] 366 | name = "flate2" 367 | version = "1.0.35" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" 370 | dependencies = [ 371 | "crc32fast", 372 | "miniz_oxide", 373 | ] 374 | 375 | [[package]] 376 | name = "fnv" 377 | version = "1.0.7" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 380 | 381 | [[package]] 382 | name = "foreign-types" 383 | version = "0.3.2" 384 | source = "registry+https://github.com/rust-lang/crates.io-index" 385 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 386 | dependencies = [ 387 | "foreign-types-shared", 388 | ] 389 | 390 | [[package]] 391 | name = "foreign-types-shared" 392 | version = "0.1.1" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 395 | 396 | [[package]] 397 | name = "form_urlencoded" 398 | version = "1.2.1" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 401 | dependencies = [ 402 | "percent-encoding", 403 | ] 404 | 405 | [[package]] 406 | name = "futures" 407 | version = "0.3.31" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 410 | dependencies = [ 411 | "futures-channel", 412 | "futures-core", 413 | "futures-io", 414 | "futures-sink", 415 | "futures-task", 416 | "futures-util", 417 | ] 418 | 419 | [[package]] 420 | name = "futures-channel" 421 | version = "0.3.31" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 424 | dependencies = [ 425 | "futures-core", 426 | "futures-sink", 427 | ] 428 | 429 | [[package]] 430 | name = "futures-core" 431 | version = "0.3.31" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 434 | 435 | [[package]] 436 | name = "futures-io" 437 | version = "0.3.31" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 440 | 441 | [[package]] 442 | name = "futures-macro" 443 | version = "0.3.31" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 446 | dependencies = [ 447 | "proc-macro2", 448 | "quote", 449 | "syn", 450 | ] 451 | 452 | [[package]] 453 | name = "futures-sink" 454 | version = "0.3.31" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 457 | 458 | [[package]] 459 | name = "futures-task" 460 | version = "0.3.31" 461 | source = "registry+https://github.com/rust-lang/crates.io-index" 462 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 463 | 464 | [[package]] 465 | name = "futures-util" 466 | version = "0.3.31" 467 | source = "registry+https://github.com/rust-lang/crates.io-index" 468 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 469 | dependencies = [ 470 | "futures-channel", 471 | "futures-core", 472 | "futures-io", 473 | "futures-macro", 474 | "futures-sink", 475 | "futures-task", 476 | "memchr", 477 | "pin-project-lite", 478 | "pin-utils", 479 | "slab", 480 | ] 481 | 482 | [[package]] 483 | name = "generic-array" 484 | version = "0.14.7" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 487 | dependencies = [ 488 | "typenum", 489 | "version_check", 490 | ] 491 | 492 | [[package]] 493 | name = "getrandom" 494 | version = "0.2.15" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 497 | dependencies = [ 498 | "cfg-if", 499 | "libc", 500 | "wasi", 501 | ] 502 | 503 | [[package]] 504 | name = "gimli" 505 | version = "0.31.1" 506 | source = "registry+https://github.com/rust-lang/crates.io-index" 507 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 508 | 509 | [[package]] 510 | name = "h2" 511 | version = "0.4.7" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" 514 | dependencies = [ 515 | "atomic-waker", 516 | "bytes", 517 | "fnv", 518 | "futures-core", 519 | "futures-sink", 520 | "http", 521 | "indexmap", 522 | "slab", 523 | "tokio", 524 | "tokio-util", 525 | "tracing", 526 | ] 527 | 528 | [[package]] 529 | name = "hashbrown" 530 | version = "0.15.2" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 533 | 534 | [[package]] 535 | name = "heck" 536 | version = "0.5.0" 537 | source = "registry+https://github.com/rust-lang/crates.io-index" 538 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 539 | 540 | [[package]] 541 | name = "hex" 542 | version = "0.4.3" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 545 | 546 | [[package]] 547 | name = "http" 548 | version = "1.2.0" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" 551 | dependencies = [ 552 | "bytes", 553 | "fnv", 554 | "itoa", 555 | ] 556 | 557 | [[package]] 558 | name = "http-body" 559 | version = "1.0.1" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 562 | dependencies = [ 563 | "bytes", 564 | "http", 565 | ] 566 | 567 | [[package]] 568 | name = "http-body-util" 569 | version = "0.1.2" 570 | source = "registry+https://github.com/rust-lang/crates.io-index" 571 | checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" 572 | dependencies = [ 573 | "bytes", 574 | "futures-util", 575 | "http", 576 | "http-body", 577 | "pin-project-lite", 578 | ] 579 | 580 | [[package]] 581 | name = "httparse" 582 | version = "1.9.5" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" 585 | 586 | [[package]] 587 | name = "httpdate" 588 | version = "1.0.3" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 591 | 592 | [[package]] 593 | name = "hyper" 594 | version = "1.5.2" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" 597 | dependencies = [ 598 | "bytes", 599 | "futures-channel", 600 | "futures-util", 601 | "h2", 602 | "http", 603 | "http-body", 604 | "httparse", 605 | "httpdate", 606 | "itoa", 607 | "pin-project-lite", 608 | "smallvec", 609 | "tokio", 610 | "want", 611 | ] 612 | 613 | [[package]] 614 | name = "hyper-rustls" 615 | version = "0.27.5" 616 | source = "registry+https://github.com/rust-lang/crates.io-index" 617 | checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" 618 | dependencies = [ 619 | "futures-util", 620 | "http", 621 | "hyper", 622 | "hyper-util", 623 | "rustls", 624 | "rustls-pki-types", 625 | "tokio", 626 | "tokio-rustls", 627 | "tower-service", 628 | ] 629 | 630 | [[package]] 631 | name = "hyper-tls" 632 | version = "0.6.0" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" 635 | dependencies = [ 636 | "bytes", 637 | "http-body-util", 638 | "hyper", 639 | "hyper-util", 640 | "native-tls", 641 | "tokio", 642 | "tokio-native-tls", 643 | "tower-service", 644 | ] 645 | 646 | [[package]] 647 | name = "hyper-util" 648 | version = "0.1.10" 649 | source = "registry+https://github.com/rust-lang/crates.io-index" 650 | checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" 651 | dependencies = [ 652 | "bytes", 653 | "futures-channel", 654 | "futures-util", 655 | "http", 656 | "http-body", 657 | "hyper", 658 | "pin-project-lite", 659 | "socket2", 660 | "tokio", 661 | "tower-service", 662 | "tracing", 663 | ] 664 | 665 | [[package]] 666 | name = "iana-time-zone" 667 | version = "0.1.61" 668 | source = "registry+https://github.com/rust-lang/crates.io-index" 669 | checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" 670 | dependencies = [ 671 | "android_system_properties", 672 | "core-foundation-sys", 673 | "iana-time-zone-haiku", 674 | "js-sys", 675 | "wasm-bindgen", 676 | "windows-core", 677 | ] 678 | 679 | [[package]] 680 | name = "iana-time-zone-haiku" 681 | version = "0.1.2" 682 | source = "registry+https://github.com/rust-lang/crates.io-index" 683 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 684 | dependencies = [ 685 | "cc", 686 | ] 687 | 688 | [[package]] 689 | name = "icu_collections" 690 | version = "1.5.0" 691 | source = "registry+https://github.com/rust-lang/crates.io-index" 692 | checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" 693 | dependencies = [ 694 | "displaydoc", 695 | "yoke", 696 | "zerofrom", 697 | "zerovec", 698 | ] 699 | 700 | [[package]] 701 | name = "icu_locid" 702 | version = "1.5.0" 703 | source = "registry+https://github.com/rust-lang/crates.io-index" 704 | checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" 705 | dependencies = [ 706 | "displaydoc", 707 | "litemap", 708 | "tinystr", 709 | "writeable", 710 | "zerovec", 711 | ] 712 | 713 | [[package]] 714 | name = "icu_locid_transform" 715 | version = "1.5.0" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" 718 | dependencies = [ 719 | "displaydoc", 720 | "icu_locid", 721 | "icu_locid_transform_data", 722 | "icu_provider", 723 | "tinystr", 724 | "zerovec", 725 | ] 726 | 727 | [[package]] 728 | name = "icu_locid_transform_data" 729 | version = "1.5.0" 730 | source = "registry+https://github.com/rust-lang/crates.io-index" 731 | checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" 732 | 733 | [[package]] 734 | name = "icu_normalizer" 735 | version = "1.5.0" 736 | source = "registry+https://github.com/rust-lang/crates.io-index" 737 | checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" 738 | dependencies = [ 739 | "displaydoc", 740 | "icu_collections", 741 | "icu_normalizer_data", 742 | "icu_properties", 743 | "icu_provider", 744 | "smallvec", 745 | "utf16_iter", 746 | "utf8_iter", 747 | "write16", 748 | "zerovec", 749 | ] 750 | 751 | [[package]] 752 | name = "icu_normalizer_data" 753 | version = "1.5.0" 754 | source = "registry+https://github.com/rust-lang/crates.io-index" 755 | checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" 756 | 757 | [[package]] 758 | name = "icu_properties" 759 | version = "1.5.1" 760 | source = "registry+https://github.com/rust-lang/crates.io-index" 761 | checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" 762 | dependencies = [ 763 | "displaydoc", 764 | "icu_collections", 765 | "icu_locid_transform", 766 | "icu_properties_data", 767 | "icu_provider", 768 | "tinystr", 769 | "zerovec", 770 | ] 771 | 772 | [[package]] 773 | name = "icu_properties_data" 774 | version = "1.5.0" 775 | source = "registry+https://github.com/rust-lang/crates.io-index" 776 | checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" 777 | 778 | [[package]] 779 | name = "icu_provider" 780 | version = "1.5.0" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" 783 | dependencies = [ 784 | "displaydoc", 785 | "icu_locid", 786 | "icu_provider_macros", 787 | "stable_deref_trait", 788 | "tinystr", 789 | "writeable", 790 | "yoke", 791 | "zerofrom", 792 | "zerovec", 793 | ] 794 | 795 | [[package]] 796 | name = "icu_provider_macros" 797 | version = "1.5.0" 798 | source = "registry+https://github.com/rust-lang/crates.io-index" 799 | checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" 800 | dependencies = [ 801 | "proc-macro2", 802 | "quote", 803 | "syn", 804 | ] 805 | 806 | [[package]] 807 | name = "idna" 808 | version = "1.0.3" 809 | source = "registry+https://github.com/rust-lang/crates.io-index" 810 | checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 811 | dependencies = [ 812 | "idna_adapter", 813 | "smallvec", 814 | "utf8_iter", 815 | ] 816 | 817 | [[package]] 818 | name = "idna_adapter" 819 | version = "1.2.0" 820 | source = "registry+https://github.com/rust-lang/crates.io-index" 821 | checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" 822 | dependencies = [ 823 | "icu_normalizer", 824 | "icu_properties", 825 | ] 826 | 827 | [[package]] 828 | name = "indexmap" 829 | version = "2.7.0" 830 | source = "registry+https://github.com/rust-lang/crates.io-index" 831 | checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" 832 | dependencies = [ 833 | "equivalent", 834 | "hashbrown", 835 | ] 836 | 837 | [[package]] 838 | name = "ipnet" 839 | version = "2.10.1" 840 | source = "registry+https://github.com/rust-lang/crates.io-index" 841 | checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" 842 | 843 | [[package]] 844 | name = "itertools" 845 | version = "0.13.0" 846 | source = "registry+https://github.com/rust-lang/crates.io-index" 847 | checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" 848 | dependencies = [ 849 | "either", 850 | ] 851 | 852 | [[package]] 853 | name = "itoa" 854 | version = "1.0.14" 855 | source = "registry+https://github.com/rust-lang/crates.io-index" 856 | checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" 857 | 858 | [[package]] 859 | name = "js-sys" 860 | version = "0.3.76" 861 | source = "registry+https://github.com/rust-lang/crates.io-index" 862 | checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" 863 | dependencies = [ 864 | "once_cell", 865 | "wasm-bindgen", 866 | ] 867 | 868 | [[package]] 869 | name = "libc" 870 | version = "0.2.169" 871 | source = "registry+https://github.com/rust-lang/crates.io-index" 872 | checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" 873 | 874 | [[package]] 875 | name = "linux-raw-sys" 876 | version = "0.4.14" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" 879 | 880 | [[package]] 881 | name = "litemap" 882 | version = "0.7.4" 883 | source = "registry+https://github.com/rust-lang/crates.io-index" 884 | checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" 885 | 886 | [[package]] 887 | name = "log" 888 | version = "0.4.22" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 891 | 892 | [[package]] 893 | name = "matchit" 894 | version = "0.7.3" 895 | source = "registry+https://github.com/rust-lang/crates.io-index" 896 | checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" 897 | 898 | [[package]] 899 | name = "memchr" 900 | version = "2.7.4" 901 | source = "registry+https://github.com/rust-lang/crates.io-index" 902 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 903 | 904 | [[package]] 905 | name = "mime" 906 | version = "0.3.17" 907 | source = "registry+https://github.com/rust-lang/crates.io-index" 908 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 909 | 910 | [[package]] 911 | name = "miniz_oxide" 912 | version = "0.8.2" 913 | source = "registry+https://github.com/rust-lang/crates.io-index" 914 | checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" 915 | dependencies = [ 916 | "adler2", 917 | ] 918 | 919 | [[package]] 920 | name = "mio" 921 | version = "1.0.3" 922 | source = "registry+https://github.com/rust-lang/crates.io-index" 923 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" 924 | dependencies = [ 925 | "libc", 926 | "wasi", 927 | "windows-sys 0.52.0", 928 | ] 929 | 930 | [[package]] 931 | name = "multimap" 932 | version = "0.10.0" 933 | source = "registry+https://github.com/rust-lang/crates.io-index" 934 | checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" 935 | 936 | [[package]] 937 | name = "native-tls" 938 | version = "0.2.12" 939 | source = "registry+https://github.com/rust-lang/crates.io-index" 940 | checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" 941 | dependencies = [ 942 | "libc", 943 | "log", 944 | "openssl", 945 | "openssl-probe", 946 | "openssl-sys", 947 | "schannel", 948 | "security-framework", 949 | "security-framework-sys", 950 | "tempfile", 951 | ] 952 | 953 | [[package]] 954 | name = "num-traits" 955 | version = "0.2.19" 956 | source = "registry+https://github.com/rust-lang/crates.io-index" 957 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 958 | dependencies = [ 959 | "autocfg", 960 | ] 961 | 962 | [[package]] 963 | name = "object" 964 | version = "0.36.7" 965 | source = "registry+https://github.com/rust-lang/crates.io-index" 966 | checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 967 | dependencies = [ 968 | "memchr", 969 | ] 970 | 971 | [[package]] 972 | name = "once_cell" 973 | version = "1.20.2" 974 | source = "registry+https://github.com/rust-lang/crates.io-index" 975 | checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" 976 | 977 | [[package]] 978 | name = "openssl" 979 | version = "0.10.68" 980 | source = "registry+https://github.com/rust-lang/crates.io-index" 981 | checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" 982 | dependencies = [ 983 | "bitflags", 984 | "cfg-if", 985 | "foreign-types", 986 | "libc", 987 | "once_cell", 988 | "openssl-macros", 989 | "openssl-sys", 990 | ] 991 | 992 | [[package]] 993 | name = "openssl-macros" 994 | version = "0.1.1" 995 | source = "registry+https://github.com/rust-lang/crates.io-index" 996 | checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 997 | dependencies = [ 998 | "proc-macro2", 999 | "quote", 1000 | "syn", 1001 | ] 1002 | 1003 | [[package]] 1004 | name = "openssl-probe" 1005 | version = "0.1.5" 1006 | source = "registry+https://github.com/rust-lang/crates.io-index" 1007 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" 1008 | 1009 | [[package]] 1010 | name = "openssl-sys" 1011 | version = "0.9.104" 1012 | source = "registry+https://github.com/rust-lang/crates.io-index" 1013 | checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" 1014 | dependencies = [ 1015 | "cc", 1016 | "libc", 1017 | "pkg-config", 1018 | "vcpkg", 1019 | ] 1020 | 1021 | [[package]] 1022 | name = "percent-encoding" 1023 | version = "2.3.1" 1024 | source = "registry+https://github.com/rust-lang/crates.io-index" 1025 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 1026 | 1027 | [[package]] 1028 | name = "petgraph" 1029 | version = "0.6.5" 1030 | source = "registry+https://github.com/rust-lang/crates.io-index" 1031 | checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" 1032 | dependencies = [ 1033 | "fixedbitset", 1034 | "indexmap", 1035 | ] 1036 | 1037 | [[package]] 1038 | name = "pin-project-lite" 1039 | version = "0.2.15" 1040 | source = "registry+https://github.com/rust-lang/crates.io-index" 1041 | checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" 1042 | 1043 | [[package]] 1044 | name = "pin-utils" 1045 | version = "0.1.0" 1046 | source = "registry+https://github.com/rust-lang/crates.io-index" 1047 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1048 | 1049 | [[package]] 1050 | name = "pkg-config" 1051 | version = "0.3.31" 1052 | source = "registry+https://github.com/rust-lang/crates.io-index" 1053 | checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" 1054 | 1055 | [[package]] 1056 | name = "ppv-lite86" 1057 | version = "0.2.20" 1058 | source = "registry+https://github.com/rust-lang/crates.io-index" 1059 | checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" 1060 | dependencies = [ 1061 | "zerocopy", 1062 | ] 1063 | 1064 | [[package]] 1065 | name = "prettyplease" 1066 | version = "0.2.25" 1067 | source = "registry+https://github.com/rust-lang/crates.io-index" 1068 | checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" 1069 | dependencies = [ 1070 | "proc-macro2", 1071 | "syn", 1072 | ] 1073 | 1074 | [[package]] 1075 | name = "proc-macro2" 1076 | version = "1.0.92" 1077 | source = "registry+https://github.com/rust-lang/crates.io-index" 1078 | checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" 1079 | dependencies = [ 1080 | "unicode-ident", 1081 | ] 1082 | 1083 | [[package]] 1084 | name = "prost" 1085 | version = "0.13.4" 1086 | source = "registry+https://github.com/rust-lang/crates.io-index" 1087 | checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec" 1088 | dependencies = [ 1089 | "bytes", 1090 | "prost-derive", 1091 | ] 1092 | 1093 | [[package]] 1094 | name = "prost-build" 1095 | version = "0.13.4" 1096 | source = "registry+https://github.com/rust-lang/crates.io-index" 1097 | checksum = "d0f3e5beed80eb580c68e2c600937ac2c4eedabdfd5ef1e5b7ea4f3fba84497b" 1098 | dependencies = [ 1099 | "heck", 1100 | "itertools", 1101 | "log", 1102 | "multimap", 1103 | "once_cell", 1104 | "petgraph", 1105 | "prettyplease", 1106 | "prost", 1107 | "prost-types", 1108 | "regex", 1109 | "syn", 1110 | "tempfile", 1111 | ] 1112 | 1113 | [[package]] 1114 | name = "prost-derive" 1115 | version = "0.13.4" 1116 | source = "registry+https://github.com/rust-lang/crates.io-index" 1117 | checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" 1118 | dependencies = [ 1119 | "anyhow", 1120 | "itertools", 1121 | "proc-macro2", 1122 | "quote", 1123 | "syn", 1124 | ] 1125 | 1126 | [[package]] 1127 | name = "prost-types" 1128 | version = "0.13.4" 1129 | source = "registry+https://github.com/rust-lang/crates.io-index" 1130 | checksum = "cc2f1e56baa61e93533aebc21af4d2134b70f66275e0fcdf3cbe43d77ff7e8fc" 1131 | dependencies = [ 1132 | "prost", 1133 | ] 1134 | 1135 | [[package]] 1136 | name = "quote" 1137 | version = "1.0.37" 1138 | source = "registry+https://github.com/rust-lang/crates.io-index" 1139 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 1140 | dependencies = [ 1141 | "proc-macro2", 1142 | ] 1143 | 1144 | [[package]] 1145 | name = "rand" 1146 | version = "0.8.5" 1147 | source = "registry+https://github.com/rust-lang/crates.io-index" 1148 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1149 | dependencies = [ 1150 | "libc", 1151 | "rand_chacha", 1152 | "rand_core", 1153 | ] 1154 | 1155 | [[package]] 1156 | name = "rand_chacha" 1157 | version = "0.3.1" 1158 | source = "registry+https://github.com/rust-lang/crates.io-index" 1159 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1160 | dependencies = [ 1161 | "ppv-lite86", 1162 | "rand_core", 1163 | ] 1164 | 1165 | [[package]] 1166 | name = "rand_core" 1167 | version = "0.6.4" 1168 | source = "registry+https://github.com/rust-lang/crates.io-index" 1169 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1170 | dependencies = [ 1171 | "getrandom", 1172 | ] 1173 | 1174 | [[package]] 1175 | name = "regex" 1176 | version = "1.11.1" 1177 | source = "registry+https://github.com/rust-lang/crates.io-index" 1178 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 1179 | dependencies = [ 1180 | "aho-corasick", 1181 | "memchr", 1182 | "regex-automata", 1183 | "regex-syntax", 1184 | ] 1185 | 1186 | [[package]] 1187 | name = "regex-automata" 1188 | version = "0.4.9" 1189 | source = "registry+https://github.com/rust-lang/crates.io-index" 1190 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 1191 | dependencies = [ 1192 | "aho-corasick", 1193 | "memchr", 1194 | "regex-syntax", 1195 | ] 1196 | 1197 | [[package]] 1198 | name = "regex-syntax" 1199 | version = "0.8.5" 1200 | source = "registry+https://github.com/rust-lang/crates.io-index" 1201 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 1202 | 1203 | [[package]] 1204 | name = "reqwest" 1205 | version = "0.12.9" 1206 | source = "registry+https://github.com/rust-lang/crates.io-index" 1207 | checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" 1208 | dependencies = [ 1209 | "async-compression", 1210 | "base64", 1211 | "bytes", 1212 | "encoding_rs", 1213 | "futures-core", 1214 | "futures-util", 1215 | "h2", 1216 | "http", 1217 | "http-body", 1218 | "http-body-util", 1219 | "hyper", 1220 | "hyper-rustls", 1221 | "hyper-tls", 1222 | "hyper-util", 1223 | "ipnet", 1224 | "js-sys", 1225 | "log", 1226 | "mime", 1227 | "native-tls", 1228 | "once_cell", 1229 | "percent-encoding", 1230 | "pin-project-lite", 1231 | "rustls-pemfile", 1232 | "serde", 1233 | "serde_json", 1234 | "serde_urlencoded", 1235 | "sync_wrapper", 1236 | "system-configuration", 1237 | "tokio", 1238 | "tokio-native-tls", 1239 | "tokio-util", 1240 | "tower-service", 1241 | "url", 1242 | "wasm-bindgen", 1243 | "wasm-bindgen-futures", 1244 | "wasm-streams", 1245 | "web-sys", 1246 | "windows-registry", 1247 | ] 1248 | 1249 | [[package]] 1250 | name = "ring" 1251 | version = "0.17.8" 1252 | source = "registry+https://github.com/rust-lang/crates.io-index" 1253 | checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" 1254 | dependencies = [ 1255 | "cc", 1256 | "cfg-if", 1257 | "getrandom", 1258 | "libc", 1259 | "spin", 1260 | "untrusted", 1261 | "windows-sys 0.52.0", 1262 | ] 1263 | 1264 | [[package]] 1265 | name = "rustc-demangle" 1266 | version = "0.1.24" 1267 | source = "registry+https://github.com/rust-lang/crates.io-index" 1268 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 1269 | 1270 | [[package]] 1271 | name = "rustix" 1272 | version = "0.38.42" 1273 | source = "registry+https://github.com/rust-lang/crates.io-index" 1274 | checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" 1275 | dependencies = [ 1276 | "bitflags", 1277 | "errno", 1278 | "libc", 1279 | "linux-raw-sys", 1280 | "windows-sys 0.59.0", 1281 | ] 1282 | 1283 | [[package]] 1284 | name = "rustls" 1285 | version = "0.23.20" 1286 | source = "registry+https://github.com/rust-lang/crates.io-index" 1287 | checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" 1288 | dependencies = [ 1289 | "once_cell", 1290 | "rustls-pki-types", 1291 | "rustls-webpki", 1292 | "subtle", 1293 | "zeroize", 1294 | ] 1295 | 1296 | [[package]] 1297 | name = "rustls-pemfile" 1298 | version = "2.2.0" 1299 | source = "registry+https://github.com/rust-lang/crates.io-index" 1300 | checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" 1301 | dependencies = [ 1302 | "rustls-pki-types", 1303 | ] 1304 | 1305 | [[package]] 1306 | name = "rustls-pki-types" 1307 | version = "1.10.1" 1308 | source = "registry+https://github.com/rust-lang/crates.io-index" 1309 | checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" 1310 | 1311 | [[package]] 1312 | name = "rustls-webpki" 1313 | version = "0.102.8" 1314 | source = "registry+https://github.com/rust-lang/crates.io-index" 1315 | checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" 1316 | dependencies = [ 1317 | "ring", 1318 | "rustls-pki-types", 1319 | "untrusted", 1320 | ] 1321 | 1322 | [[package]] 1323 | name = "rustversion" 1324 | version = "1.0.18" 1325 | source = "registry+https://github.com/rust-lang/crates.io-index" 1326 | checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" 1327 | 1328 | [[package]] 1329 | name = "ryu" 1330 | version = "1.0.18" 1331 | source = "registry+https://github.com/rust-lang/crates.io-index" 1332 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 1333 | 1334 | [[package]] 1335 | name = "schannel" 1336 | version = "0.1.27" 1337 | source = "registry+https://github.com/rust-lang/crates.io-index" 1338 | checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" 1339 | dependencies = [ 1340 | "windows-sys 0.59.0", 1341 | ] 1342 | 1343 | [[package]] 1344 | name = "security-framework" 1345 | version = "2.11.1" 1346 | source = "registry+https://github.com/rust-lang/crates.io-index" 1347 | checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" 1348 | dependencies = [ 1349 | "bitflags", 1350 | "core-foundation", 1351 | "core-foundation-sys", 1352 | "libc", 1353 | "security-framework-sys", 1354 | ] 1355 | 1356 | [[package]] 1357 | name = "security-framework-sys" 1358 | version = "2.13.0" 1359 | source = "registry+https://github.com/rust-lang/crates.io-index" 1360 | checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" 1361 | dependencies = [ 1362 | "core-foundation-sys", 1363 | "libc", 1364 | ] 1365 | 1366 | [[package]] 1367 | name = "serde" 1368 | version = "1.0.216" 1369 | source = "registry+https://github.com/rust-lang/crates.io-index" 1370 | checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" 1371 | dependencies = [ 1372 | "serde_derive", 1373 | ] 1374 | 1375 | [[package]] 1376 | name = "serde_derive" 1377 | version = "1.0.216" 1378 | source = "registry+https://github.com/rust-lang/crates.io-index" 1379 | checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" 1380 | dependencies = [ 1381 | "proc-macro2", 1382 | "quote", 1383 | "syn", 1384 | ] 1385 | 1386 | [[package]] 1387 | name = "serde_json" 1388 | version = "1.0.134" 1389 | source = "registry+https://github.com/rust-lang/crates.io-index" 1390 | checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" 1391 | dependencies = [ 1392 | "itoa", 1393 | "memchr", 1394 | "ryu", 1395 | "serde", 1396 | ] 1397 | 1398 | [[package]] 1399 | name = "serde_path_to_error" 1400 | version = "0.1.16" 1401 | source = "registry+https://github.com/rust-lang/crates.io-index" 1402 | checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" 1403 | dependencies = [ 1404 | "itoa", 1405 | "serde", 1406 | ] 1407 | 1408 | [[package]] 1409 | name = "serde_urlencoded" 1410 | version = "0.7.1" 1411 | source = "registry+https://github.com/rust-lang/crates.io-index" 1412 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1413 | dependencies = [ 1414 | "form_urlencoded", 1415 | "itoa", 1416 | "ryu", 1417 | "serde", 1418 | ] 1419 | 1420 | [[package]] 1421 | name = "sha2" 1422 | version = "0.10.8" 1423 | source = "registry+https://github.com/rust-lang/crates.io-index" 1424 | checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" 1425 | dependencies = [ 1426 | "cfg-if", 1427 | "cpufeatures", 1428 | "digest", 1429 | ] 1430 | 1431 | [[package]] 1432 | name = "shlex" 1433 | version = "1.3.0" 1434 | source = "registry+https://github.com/rust-lang/crates.io-index" 1435 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1436 | 1437 | [[package]] 1438 | name = "slab" 1439 | version = "0.4.9" 1440 | source = "registry+https://github.com/rust-lang/crates.io-index" 1441 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1442 | dependencies = [ 1443 | "autocfg", 1444 | ] 1445 | 1446 | [[package]] 1447 | name = "smallvec" 1448 | version = "1.13.2" 1449 | source = "registry+https://github.com/rust-lang/crates.io-index" 1450 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 1451 | 1452 | [[package]] 1453 | name = "socket2" 1454 | version = "0.5.8" 1455 | source = "registry+https://github.com/rust-lang/crates.io-index" 1456 | checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" 1457 | dependencies = [ 1458 | "libc", 1459 | "windows-sys 0.52.0", 1460 | ] 1461 | 1462 | [[package]] 1463 | name = "spin" 1464 | version = "0.9.8" 1465 | source = "registry+https://github.com/rust-lang/crates.io-index" 1466 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 1467 | 1468 | [[package]] 1469 | name = "stable_deref_trait" 1470 | version = "1.2.0" 1471 | source = "registry+https://github.com/rust-lang/crates.io-index" 1472 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1473 | 1474 | [[package]] 1475 | name = "subtle" 1476 | version = "2.6.1" 1477 | source = "registry+https://github.com/rust-lang/crates.io-index" 1478 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1479 | 1480 | [[package]] 1481 | name = "syn" 1482 | version = "2.0.91" 1483 | source = "registry+https://github.com/rust-lang/crates.io-index" 1484 | checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035" 1485 | dependencies = [ 1486 | "proc-macro2", 1487 | "quote", 1488 | "unicode-ident", 1489 | ] 1490 | 1491 | [[package]] 1492 | name = "sync_wrapper" 1493 | version = "1.0.2" 1494 | source = "registry+https://github.com/rust-lang/crates.io-index" 1495 | checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 1496 | dependencies = [ 1497 | "futures-core", 1498 | ] 1499 | 1500 | [[package]] 1501 | name = "synstructure" 1502 | version = "0.13.1" 1503 | source = "registry+https://github.com/rust-lang/crates.io-index" 1504 | checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" 1505 | dependencies = [ 1506 | "proc-macro2", 1507 | "quote", 1508 | "syn", 1509 | ] 1510 | 1511 | [[package]] 1512 | name = "system-configuration" 1513 | version = "0.6.1" 1514 | source = "registry+https://github.com/rust-lang/crates.io-index" 1515 | checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" 1516 | dependencies = [ 1517 | "bitflags", 1518 | "core-foundation", 1519 | "system-configuration-sys", 1520 | ] 1521 | 1522 | [[package]] 1523 | name = "system-configuration-sys" 1524 | version = "0.6.0" 1525 | source = "registry+https://github.com/rust-lang/crates.io-index" 1526 | checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" 1527 | dependencies = [ 1528 | "core-foundation-sys", 1529 | "libc", 1530 | ] 1531 | 1532 | [[package]] 1533 | name = "tempfile" 1534 | version = "3.14.0" 1535 | source = "registry+https://github.com/rust-lang/crates.io-index" 1536 | checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" 1537 | dependencies = [ 1538 | "cfg-if", 1539 | "fastrand", 1540 | "once_cell", 1541 | "rustix", 1542 | "windows-sys 0.59.0", 1543 | ] 1544 | 1545 | [[package]] 1546 | name = "tinystr" 1547 | version = "0.7.6" 1548 | source = "registry+https://github.com/rust-lang/crates.io-index" 1549 | checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" 1550 | dependencies = [ 1551 | "displaydoc", 1552 | "zerovec", 1553 | ] 1554 | 1555 | [[package]] 1556 | name = "tokio" 1557 | version = "1.42.0" 1558 | source = "registry+https://github.com/rust-lang/crates.io-index" 1559 | checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" 1560 | dependencies = [ 1561 | "backtrace", 1562 | "bytes", 1563 | "libc", 1564 | "mio", 1565 | "pin-project-lite", 1566 | "socket2", 1567 | "tokio-macros", 1568 | "windows-sys 0.52.0", 1569 | ] 1570 | 1571 | [[package]] 1572 | name = "tokio-macros" 1573 | version = "2.4.0" 1574 | source = "registry+https://github.com/rust-lang/crates.io-index" 1575 | checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" 1576 | dependencies = [ 1577 | "proc-macro2", 1578 | "quote", 1579 | "syn", 1580 | ] 1581 | 1582 | [[package]] 1583 | name = "tokio-native-tls" 1584 | version = "0.3.1" 1585 | source = "registry+https://github.com/rust-lang/crates.io-index" 1586 | checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 1587 | dependencies = [ 1588 | "native-tls", 1589 | "tokio", 1590 | ] 1591 | 1592 | [[package]] 1593 | name = "tokio-rustls" 1594 | version = "0.26.1" 1595 | source = "registry+https://github.com/rust-lang/crates.io-index" 1596 | checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" 1597 | dependencies = [ 1598 | "rustls", 1599 | "tokio", 1600 | ] 1601 | 1602 | [[package]] 1603 | name = "tokio-stream" 1604 | version = "0.1.17" 1605 | source = "registry+https://github.com/rust-lang/crates.io-index" 1606 | checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" 1607 | dependencies = [ 1608 | "futures-core", 1609 | "pin-project-lite", 1610 | "tokio", 1611 | ] 1612 | 1613 | [[package]] 1614 | name = "tokio-util" 1615 | version = "0.7.13" 1616 | source = "registry+https://github.com/rust-lang/crates.io-index" 1617 | checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" 1618 | dependencies = [ 1619 | "bytes", 1620 | "futures-core", 1621 | "futures-sink", 1622 | "pin-project-lite", 1623 | "tokio", 1624 | ] 1625 | 1626 | [[package]] 1627 | name = "tower" 1628 | version = "0.5.2" 1629 | source = "registry+https://github.com/rust-lang/crates.io-index" 1630 | checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" 1631 | dependencies = [ 1632 | "futures-core", 1633 | "futures-util", 1634 | "pin-project-lite", 1635 | "sync_wrapper", 1636 | "tokio", 1637 | "tower-layer", 1638 | "tower-service", 1639 | "tracing", 1640 | ] 1641 | 1642 | [[package]] 1643 | name = "tower-http" 1644 | version = "0.6.2" 1645 | source = "registry+https://github.com/rust-lang/crates.io-index" 1646 | checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" 1647 | dependencies = [ 1648 | "bitflags", 1649 | "bytes", 1650 | "http", 1651 | "pin-project-lite", 1652 | "tower-layer", 1653 | "tower-service", 1654 | ] 1655 | 1656 | [[package]] 1657 | name = "tower-layer" 1658 | version = "0.3.3" 1659 | source = "registry+https://github.com/rust-lang/crates.io-index" 1660 | checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 1661 | 1662 | [[package]] 1663 | name = "tower-service" 1664 | version = "0.3.3" 1665 | source = "registry+https://github.com/rust-lang/crates.io-index" 1666 | checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 1667 | 1668 | [[package]] 1669 | name = "tracing" 1670 | version = "0.1.41" 1671 | source = "registry+https://github.com/rust-lang/crates.io-index" 1672 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 1673 | dependencies = [ 1674 | "log", 1675 | "pin-project-lite", 1676 | "tracing-core", 1677 | ] 1678 | 1679 | [[package]] 1680 | name = "tracing-core" 1681 | version = "0.1.33" 1682 | source = "registry+https://github.com/rust-lang/crates.io-index" 1683 | checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" 1684 | dependencies = [ 1685 | "once_cell", 1686 | ] 1687 | 1688 | [[package]] 1689 | name = "try-lock" 1690 | version = "0.2.5" 1691 | source = "registry+https://github.com/rust-lang/crates.io-index" 1692 | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 1693 | 1694 | [[package]] 1695 | name = "typenum" 1696 | version = "1.17.0" 1697 | source = "registry+https://github.com/rust-lang/crates.io-index" 1698 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 1699 | 1700 | [[package]] 1701 | name = "unicode-ident" 1702 | version = "1.0.14" 1703 | source = "registry+https://github.com/rust-lang/crates.io-index" 1704 | checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" 1705 | 1706 | [[package]] 1707 | name = "untrusted" 1708 | version = "0.9.0" 1709 | source = "registry+https://github.com/rust-lang/crates.io-index" 1710 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 1711 | 1712 | [[package]] 1713 | name = "url" 1714 | version = "2.5.4" 1715 | source = "registry+https://github.com/rust-lang/crates.io-index" 1716 | checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 1717 | dependencies = [ 1718 | "form_urlencoded", 1719 | "idna", 1720 | "percent-encoding", 1721 | ] 1722 | 1723 | [[package]] 1724 | name = "utf16_iter" 1725 | version = "1.0.5" 1726 | source = "registry+https://github.com/rust-lang/crates.io-index" 1727 | checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" 1728 | 1729 | [[package]] 1730 | name = "utf8_iter" 1731 | version = "1.0.4" 1732 | source = "registry+https://github.com/rust-lang/crates.io-index" 1733 | checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 1734 | 1735 | [[package]] 1736 | name = "uuid" 1737 | version = "1.11.0" 1738 | source = "registry+https://github.com/rust-lang/crates.io-index" 1739 | checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" 1740 | dependencies = [ 1741 | "getrandom", 1742 | ] 1743 | 1744 | [[package]] 1745 | name = "vcpkg" 1746 | version = "0.2.15" 1747 | source = "registry+https://github.com/rust-lang/crates.io-index" 1748 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 1749 | 1750 | [[package]] 1751 | name = "version_check" 1752 | version = "0.9.5" 1753 | source = "registry+https://github.com/rust-lang/crates.io-index" 1754 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 1755 | 1756 | [[package]] 1757 | name = "want" 1758 | version = "0.3.1" 1759 | source = "registry+https://github.com/rust-lang/crates.io-index" 1760 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 1761 | dependencies = [ 1762 | "try-lock", 1763 | ] 1764 | 1765 | [[package]] 1766 | name = "wasi" 1767 | version = "0.11.0+wasi-snapshot-preview1" 1768 | source = "registry+https://github.com/rust-lang/crates.io-index" 1769 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1770 | 1771 | [[package]] 1772 | name = "wasm-bindgen" 1773 | version = "0.2.99" 1774 | source = "registry+https://github.com/rust-lang/crates.io-index" 1775 | checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" 1776 | dependencies = [ 1777 | "cfg-if", 1778 | "once_cell", 1779 | "wasm-bindgen-macro", 1780 | ] 1781 | 1782 | [[package]] 1783 | name = "wasm-bindgen-backend" 1784 | version = "0.2.99" 1785 | source = "registry+https://github.com/rust-lang/crates.io-index" 1786 | checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" 1787 | dependencies = [ 1788 | "bumpalo", 1789 | "log", 1790 | "proc-macro2", 1791 | "quote", 1792 | "syn", 1793 | "wasm-bindgen-shared", 1794 | ] 1795 | 1796 | [[package]] 1797 | name = "wasm-bindgen-futures" 1798 | version = "0.4.49" 1799 | source = "registry+https://github.com/rust-lang/crates.io-index" 1800 | checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" 1801 | dependencies = [ 1802 | "cfg-if", 1803 | "js-sys", 1804 | "once_cell", 1805 | "wasm-bindgen", 1806 | "web-sys", 1807 | ] 1808 | 1809 | [[package]] 1810 | name = "wasm-bindgen-macro" 1811 | version = "0.2.99" 1812 | source = "registry+https://github.com/rust-lang/crates.io-index" 1813 | checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" 1814 | dependencies = [ 1815 | "quote", 1816 | "wasm-bindgen-macro-support", 1817 | ] 1818 | 1819 | [[package]] 1820 | name = "wasm-bindgen-macro-support" 1821 | version = "0.2.99" 1822 | source = "registry+https://github.com/rust-lang/crates.io-index" 1823 | checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" 1824 | dependencies = [ 1825 | "proc-macro2", 1826 | "quote", 1827 | "syn", 1828 | "wasm-bindgen-backend", 1829 | "wasm-bindgen-shared", 1830 | ] 1831 | 1832 | [[package]] 1833 | name = "wasm-bindgen-shared" 1834 | version = "0.2.99" 1835 | source = "registry+https://github.com/rust-lang/crates.io-index" 1836 | checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" 1837 | 1838 | [[package]] 1839 | name = "wasm-streams" 1840 | version = "0.4.2" 1841 | source = "registry+https://github.com/rust-lang/crates.io-index" 1842 | checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" 1843 | dependencies = [ 1844 | "futures-util", 1845 | "js-sys", 1846 | "wasm-bindgen", 1847 | "wasm-bindgen-futures", 1848 | "web-sys", 1849 | ] 1850 | 1851 | [[package]] 1852 | name = "web-sys" 1853 | version = "0.3.76" 1854 | source = "registry+https://github.com/rust-lang/crates.io-index" 1855 | checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" 1856 | dependencies = [ 1857 | "js-sys", 1858 | "wasm-bindgen", 1859 | ] 1860 | 1861 | [[package]] 1862 | name = "windows-core" 1863 | version = "0.52.0" 1864 | source = "registry+https://github.com/rust-lang/crates.io-index" 1865 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 1866 | dependencies = [ 1867 | "windows-targets", 1868 | ] 1869 | 1870 | [[package]] 1871 | name = "windows-registry" 1872 | version = "0.2.0" 1873 | source = "registry+https://github.com/rust-lang/crates.io-index" 1874 | checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" 1875 | dependencies = [ 1876 | "windows-result", 1877 | "windows-strings", 1878 | "windows-targets", 1879 | ] 1880 | 1881 | [[package]] 1882 | name = "windows-result" 1883 | version = "0.2.0" 1884 | source = "registry+https://github.com/rust-lang/crates.io-index" 1885 | checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" 1886 | dependencies = [ 1887 | "windows-targets", 1888 | ] 1889 | 1890 | [[package]] 1891 | name = "windows-strings" 1892 | version = "0.1.0" 1893 | source = "registry+https://github.com/rust-lang/crates.io-index" 1894 | checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" 1895 | dependencies = [ 1896 | "windows-result", 1897 | "windows-targets", 1898 | ] 1899 | 1900 | [[package]] 1901 | name = "windows-sys" 1902 | version = "0.52.0" 1903 | source = "registry+https://github.com/rust-lang/crates.io-index" 1904 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1905 | dependencies = [ 1906 | "windows-targets", 1907 | ] 1908 | 1909 | [[package]] 1910 | name = "windows-sys" 1911 | version = "0.59.0" 1912 | source = "registry+https://github.com/rust-lang/crates.io-index" 1913 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 1914 | dependencies = [ 1915 | "windows-targets", 1916 | ] 1917 | 1918 | [[package]] 1919 | name = "windows-targets" 1920 | version = "0.52.6" 1921 | source = "registry+https://github.com/rust-lang/crates.io-index" 1922 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 1923 | dependencies = [ 1924 | "windows_aarch64_gnullvm", 1925 | "windows_aarch64_msvc", 1926 | "windows_i686_gnu", 1927 | "windows_i686_gnullvm", 1928 | "windows_i686_msvc", 1929 | "windows_x86_64_gnu", 1930 | "windows_x86_64_gnullvm", 1931 | "windows_x86_64_msvc", 1932 | ] 1933 | 1934 | [[package]] 1935 | name = "windows_aarch64_gnullvm" 1936 | version = "0.52.6" 1937 | source = "registry+https://github.com/rust-lang/crates.io-index" 1938 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 1939 | 1940 | [[package]] 1941 | name = "windows_aarch64_msvc" 1942 | version = "0.52.6" 1943 | source = "registry+https://github.com/rust-lang/crates.io-index" 1944 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 1945 | 1946 | [[package]] 1947 | name = "windows_i686_gnu" 1948 | version = "0.52.6" 1949 | source = "registry+https://github.com/rust-lang/crates.io-index" 1950 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1951 | 1952 | [[package]] 1953 | name = "windows_i686_gnullvm" 1954 | version = "0.52.6" 1955 | source = "registry+https://github.com/rust-lang/crates.io-index" 1956 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1957 | 1958 | [[package]] 1959 | name = "windows_i686_msvc" 1960 | version = "0.52.6" 1961 | source = "registry+https://github.com/rust-lang/crates.io-index" 1962 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1963 | 1964 | [[package]] 1965 | name = "windows_x86_64_gnu" 1966 | version = "0.52.6" 1967 | source = "registry+https://github.com/rust-lang/crates.io-index" 1968 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1969 | 1970 | [[package]] 1971 | name = "windows_x86_64_gnullvm" 1972 | version = "0.52.6" 1973 | source = "registry+https://github.com/rust-lang/crates.io-index" 1974 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1975 | 1976 | [[package]] 1977 | name = "windows_x86_64_msvc" 1978 | version = "0.52.6" 1979 | source = "registry+https://github.com/rust-lang/crates.io-index" 1980 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1981 | 1982 | [[package]] 1983 | name = "write16" 1984 | version = "1.0.0" 1985 | source = "registry+https://github.com/rust-lang/crates.io-index" 1986 | checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" 1987 | 1988 | [[package]] 1989 | name = "writeable" 1990 | version = "0.5.5" 1991 | source = "registry+https://github.com/rust-lang/crates.io-index" 1992 | checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" 1993 | 1994 | [[package]] 1995 | name = "yoke" 1996 | version = "0.7.5" 1997 | source = "registry+https://github.com/rust-lang/crates.io-index" 1998 | checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" 1999 | dependencies = [ 2000 | "serde", 2001 | "stable_deref_trait", 2002 | "yoke-derive", 2003 | "zerofrom", 2004 | ] 2005 | 2006 | [[package]] 2007 | name = "yoke-derive" 2008 | version = "0.7.5" 2009 | source = "registry+https://github.com/rust-lang/crates.io-index" 2010 | checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" 2011 | dependencies = [ 2012 | "proc-macro2", 2013 | "quote", 2014 | "syn", 2015 | "synstructure", 2016 | ] 2017 | 2018 | [[package]] 2019 | name = "zerocopy" 2020 | version = "0.7.35" 2021 | source = "registry+https://github.com/rust-lang/crates.io-index" 2022 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 2023 | dependencies = [ 2024 | "byteorder", 2025 | "zerocopy-derive", 2026 | ] 2027 | 2028 | [[package]] 2029 | name = "zerocopy-derive" 2030 | version = "0.7.35" 2031 | source = "registry+https://github.com/rust-lang/crates.io-index" 2032 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 2033 | dependencies = [ 2034 | "proc-macro2", 2035 | "quote", 2036 | "syn", 2037 | ] 2038 | 2039 | [[package]] 2040 | name = "zerofrom" 2041 | version = "0.1.5" 2042 | source = "registry+https://github.com/rust-lang/crates.io-index" 2043 | checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" 2044 | dependencies = [ 2045 | "zerofrom-derive", 2046 | ] 2047 | 2048 | [[package]] 2049 | name = "zerofrom-derive" 2050 | version = "0.1.5" 2051 | source = "registry+https://github.com/rust-lang/crates.io-index" 2052 | checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" 2053 | dependencies = [ 2054 | "proc-macro2", 2055 | "quote", 2056 | "syn", 2057 | "synstructure", 2058 | ] 2059 | 2060 | [[package]] 2061 | name = "zeroize" 2062 | version = "1.8.1" 2063 | source = "registry+https://github.com/rust-lang/crates.io-index" 2064 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 2065 | 2066 | [[package]] 2067 | name = "zerovec" 2068 | version = "0.10.4" 2069 | source = "registry+https://github.com/rust-lang/crates.io-index" 2070 | checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" 2071 | dependencies = [ 2072 | "yoke", 2073 | "zerofrom", 2074 | "zerovec-derive", 2075 | ] 2076 | 2077 | [[package]] 2078 | name = "zerovec-derive" 2079 | version = "0.10.3" 2080 | source = "registry+https://github.com/rust-lang/crates.io-index" 2081 | checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" 2082 | dependencies = [ 2083 | "proc-macro2", 2084 | "quote", 2085 | "syn", 2086 | ] 2087 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cursor-api" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["wisdgod "] 6 | 7 | [build-dependencies] 8 | prost-build = "0.13.4" 9 | 10 | [dependencies] 11 | axum = { version = "0.7.9", features = ["json"] } 12 | base64 = { version = "0.22.1", default-features = false, features = ["std"] } 13 | bytes = "1.9.0" 14 | chrono = { version = "0.4.39", features = ["serde"] } 15 | dotenvy = "0.15.7" 16 | flate2 = { version = "1.0.35", default-features = false, features = ["rust_backend"] } 17 | futures = { version = "0.3.31", default-features = false, features = ["std"] } 18 | hex = { version = "0.4.3", default-features = false, features = ["std"] } 19 | prost = "0.13.4" 20 | rand = { version = "0.8.5", default-features = false, features = ["std", "std_rng"] } 21 | regex = { version = "1.11.1", default-features = false, features = ["std", "perf"] } 22 | reqwest = { version = "0.12.9", features = ["json", "gzip", "stream"] } 23 | serde = { version = "1.0.216", features = ["derive"] } 24 | serde_json = { version = "1.0.134", features = ["std"] } 25 | sha2 = { version = "0.10.8", default-features = false } 26 | tokio = { version = "1.42.0", features = ["rt-multi-thread", "macros", "net", "sync", "time"] } 27 | tokio-stream = { version = "0.1.17", features = ["time"] } 28 | tower-http = { version = "0.6.2", features = ["cors"] } 29 | uuid = { version = "1.11.0", features = ["v4"] } 30 | 31 | # 优化设置 32 | [profile.release] 33 | lto = true # 启用链接时优化 34 | codegen-units = 1 # 减少并行编译单元以提高优化 35 | panic = 'abort' # 在 panic 时直接终止,减小二进制大小 36 | strip = true # 移除调试符号 37 | opt-level = 3 # 最高优化级别 38 | 39 | # 构建脚本设置 40 | [package.metadata.cross.target.x86_64-unknown-linux-gnu] 41 | image = "ghcr.io/cross-rs/x86_64-unknown-linux-gnu:main" 42 | 43 | [package.metadata.cross.target.aarch64-unknown-linux-gnu] 44 | image = "ghcr.io/cross-rs/aarch64-unknown-linux-gnu:main" 45 | 46 | [package.metadata.cross.target.x86_64-apple-darwin] 47 | image = "ghcr.io/cross-rs/x86_64-apple-darwin:main" 48 | 49 | [package.metadata.cross.target.aarch64-apple-darwin] 50 | image = "ghcr.io/cross-rs/aarch64-apple-darwin:main" 51 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # 构建阶段 2 | FROM rust:1.83.0-slim-bookworm as builder 3 | 4 | WORKDIR /app 5 | 6 | # 安装构建依赖 7 | RUN apt-get update && \ 8 | apt-get install -y --no-install-recommends \ 9 | build-essential \ 10 | protobuf-compiler \ 11 | pkg-config \ 12 | libssl-dev \ 13 | nodejs \ 14 | npm \ 15 | && rm -rf /var/lib/apt/lists/* 16 | 17 | # 复制项目文件 18 | COPY . . 19 | 20 | # 构建 21 | RUN rustup target add x86_64-unknown-linux-gnu && \ 22 | cargo build --target x86_64-unknown-linux-gnu --release && \ 23 | cp target/x86_64-unknown-linux-gnu/release/cursor-api /app/cursor-api 24 | 25 | # 运行阶段 26 | FROM debian:bookworm-slim 27 | 28 | WORKDIR /app 29 | 30 | ENV TZ=Asia/Shanghai 31 | 32 | # 安装运行时依赖 33 | RUN apt-get update && \ 34 | apt-get install -y --no-install-recommends \ 35 | ca-certificates \ 36 | tzdata \ 37 | && rm -rf /var/lib/apt/lists/* 38 | 39 | # 复制构建产物 40 | COPY --from=builder /app/cursor-api . 41 | 42 | # 设置默认端口 43 | ENV PORT=3000 44 | 45 | # 动态暴露端口 46 | EXPOSE ${PORT} 47 | 48 | CMD ["./cursor-api"] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cursor-api 2 | 3 | ## 获取key 4 | 5 | 1. 访问 [www.cursor.com](https://www.cursor.com) 并完成注册登录(赠送 250 次快速响应,可通过删除账号再注册重置) 6 | 2. 在浏览器中打开开发者工具(F12) 7 | 3. 找到 应用-Cookies 中名为 `WorkosCursorSessionToken` 的值并保存(相当于 openai 的密钥) 8 | 9 | ## 接口说明 10 | 11 | ### 基础对话(请求格式和响应格式参考 openai) 12 | 13 | - 接口地址:`/v1/chat/completions` 14 | - 请求方法:POST 15 | - 认证方式:Bearer Token(支持两种认证方式) 16 | 1. 使用环境变量 `AUTH_TOKEN` 进行认证 17 | 2. 使用 `.token` 文件中的令牌列表进行轮询认证 18 | 19 | ### 获取模型列表 20 | 21 | - 接口地址:`/v1/models` 22 | - 请求方法:GET 23 | 24 | ### 获取环境变量中的x-cursor-checksum 25 | 26 | - 接口地址:`/env-checksum` 27 | - 请求方法:GET 28 | 29 | ### 获取随机x-cursor-checksum 30 | 31 | - 接口地址:`/checksum` 32 | - 请求方法:GET 33 | 34 | ### 健康检查接口 35 | 36 | - 接口地址:`/` 37 | - 请求方法:GET 38 | 39 | ### 获取日志接口 40 | 41 | - 接口地址:`/logs` 42 | - 请求方法:GET 43 | 44 | ### Token管理接口 45 | 46 | - 获取Token信息页面:`/tokeninfo` 47 | - 更新Token信息:`/update-tokeninfo` 48 | - 获取Token信息:`/get-tokeninfo` 49 | 50 | ## 配置说明 51 | 52 | ### 环境变量 53 | 54 | - `PORT`: 服务器端口号(默认:3000) 55 | - `AUTH_TOKEN`: 认证令牌(必须,用于API认证) 56 | - `ROUTE_PREFIX`: 路由前缀(可选) 57 | - `TOKEN_FILE`: token文件路径(默认:.token) 58 | - `TOKEN_LIST_FILE`: token列表文件路径(默认:.token-list) 59 | 60 | ### Token文件格式 61 | 62 | 1. `.token` 文件:每行一个token,支持以下格式: 63 | ``` 64 | token1 65 | alias::token2 66 | ``` 67 | 68 | 2. `.token-list` 文件:每行为token和checksum的对应关系: 69 | ``` 70 | token1,checksum1 71 | token2,checksum2 72 | ``` 73 | 74 | ## 部署 75 | 76 | ### 本地部署 77 | 78 | #### 从源码编译 79 | 80 | 需要安装 Rust 工具链和 protobuf 编译器: 81 | 82 | ```bash 83 | # 安装依赖(Debian/Ubuntu) 84 | apt-get install -y build-essential protobuf-compiler 85 | 86 | # 编译并运行 87 | cargo build --release 88 | ./target/release/cursor-api 89 | ``` 90 | 91 | #### 使用预编译二进制 92 | 93 | 从 [Releases](https://github.com/wisdgod/cursor-api/releases) 下载对应平台的二进制文件。 94 | 95 | ### Docker 部署 96 | 97 | #### Docker 运行示例 98 | 99 | ```bash 100 | docker run -d -e PORT=3000 -e AUTH_TOKEN=your_token -p 3000:3000 wisdgod/cursor-api:latest 101 | ``` 102 | 103 | #### Docker 构建示例 104 | 105 | ```bash 106 | docker build -t cursor-api . 107 | docker run -p 3000:3000 cursor-api 108 | ``` 109 | 110 | ### huggingface部署 111 | 112 | 1. duplicate项目: 113 | [huggingface链接](https://huggingface.co/login?next=%2Fspaces%2Fstevenrk%2Fcursor%3Fduplicate%3Dtrue) 114 | 115 | 2. 配置环境变量 116 | 117 | 在你的space中,点击settings,找到`Variables and secrets`,添加Variables 118 | - name: `AUTH_TOKEN` (注意大写) 119 | - value: 你随意 120 | 121 | 3. 重新部署 122 | 123 | 点击`Factory rebuild`,等待部署完成 124 | 125 | 4. 接口地址(`Embed this Space`中查看): 126 | ``` 127 | https://{username}-{space-name}.hf.space/v1/models 128 | ``` 129 | 130 | ## 注意事项 131 | 132 | 1. 请妥善保管您的 AuthToken,不要泄露给他人 133 | 2. 配置 AUTH_TOKEN 环境变量以增加安全性 134 | 3. 本项目仅供学习研究使用,请遵守 Cursor 的使用条款 135 | 136 | ## 开发 137 | 138 | ### 跨平台编译 139 | 140 | 使用提供的构建脚本: 141 | 142 | ```bash 143 | # 仅编译当前平台 144 | ./scripts/build.sh 145 | 146 | # 交叉编译所有支持的平台 147 | ./scripts/build.sh --cross 148 | ``` 149 | 150 | 支持的目标平台: 151 | - x86_64-unknown-linux-gnu 152 | - x86_64-pc-windows-msvc 153 | - aarch64-unknown-linux-gnu 154 | - x86_64-apple-darwin 155 | - aarch64-apple-darwin 156 | 157 | ### 获取token 158 | 159 | - 使用 [get-token](https://github.com/wisdgod/cursor-api/tree/main/get-token) 获取读取当前设备token,仅支持windows与macos 160 | 161 | ## 鸣谢 162 | 163 | - [cursor-api](https://github.com/wisdgod/cursor-api) 164 | - [zhx47/cursor-api](https://github.com/zhx47/cursor-api) 165 | - [luolazyandlazy/cursorToApi](https://github.com/luolazyandlazy/cursorToApi) 166 | 167 | ## 许可证 168 | 169 | 版权所有 (c) 2024 170 | 171 | 本软件仅供学习和研究使用。未经授权,不得用于商业用途。 172 | 保留所有权利。 -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::io::Result; 2 | use std::path::Path; 3 | use std::process::Command; 4 | 5 | fn check_and_install_deps() -> Result<()> { 6 | let scripts_dir = Path::new("scripts"); 7 | let node_modules = scripts_dir.join("node_modules"); 8 | 9 | // 如果 node_modules 不存在,运行 npm install 10 | if !node_modules.exists() { 11 | println!("cargo:warning=Installing HTML minifier dependencies..."); 12 | 13 | let status = Command::new("npm") 14 | .current_dir(scripts_dir) 15 | .arg("install") 16 | .status()?; 17 | 18 | if !status.success() { 19 | panic!("Failed to install npm dependencies"); 20 | } 21 | println!("cargo:warning=Dependencies installed successfully"); 22 | } 23 | Ok(()) 24 | } 25 | 26 | fn minify_html() -> Result<()> { 27 | println!("cargo:warning=Minifying HTML files..."); 28 | 29 | let status = Command::new("node") 30 | .args(&["scripts/minify-html.js"]) 31 | .status()?; 32 | 33 | if !status.success() { 34 | panic!("HTML minification failed"); 35 | } 36 | Ok(()) 37 | } 38 | 39 | fn main() -> Result<()> { 40 | // Proto 文件处理 41 | println!("cargo:rerun-if-changed=src/message.proto"); 42 | let mut config = prost_build::Config::new(); 43 | config.type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]"); 44 | config 45 | .compile_protos(&["src/message.proto"], &["src/"]) 46 | .unwrap(); 47 | 48 | // HTML 文件处理 49 | println!("cargo:rerun-if-changed=static/tokeninfo.html"); 50 | println!("cargo:rerun-if-changed=scripts/minify-html.js"); 51 | println!("cargo:rerun-if-changed=scripts/package.json"); 52 | 53 | // 检查并安装依赖 54 | check_and_install_deps()?; 55 | 56 | // 运行 HTML 压缩 57 | minify_html()?; 58 | 59 | Ok(()) 60 | } 61 | -------------------------------------------------------------------------------- /get-token/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "ahash" 7 | version = "0.8.11" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" 10 | dependencies = [ 11 | "cfg-if", 12 | "once_cell", 13 | "version_check", 14 | "zerocopy", 15 | ] 16 | 17 | [[package]] 18 | name = "bitflags" 19 | version = "2.6.0" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 22 | 23 | [[package]] 24 | name = "cc" 25 | version = "1.2.5" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e" 28 | dependencies = [ 29 | "shlex", 30 | ] 31 | 32 | [[package]] 33 | name = "cfg-if" 34 | version = "1.0.0" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 37 | 38 | [[package]] 39 | name = "fallible-iterator" 40 | version = "0.3.0" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" 43 | 44 | [[package]] 45 | name = "fallible-streaming-iterator" 46 | version = "0.1.9" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" 49 | 50 | [[package]] 51 | name = "get-token" 52 | version = "0.1.0" 53 | dependencies = [ 54 | "rusqlite", 55 | ] 56 | 57 | [[package]] 58 | name = "hashbrown" 59 | version = "0.14.5" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 62 | dependencies = [ 63 | "ahash", 64 | ] 65 | 66 | [[package]] 67 | name = "hashlink" 68 | version = "0.9.1" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" 71 | dependencies = [ 72 | "hashbrown", 73 | ] 74 | 75 | [[package]] 76 | name = "libsqlite3-sys" 77 | version = "0.30.1" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" 80 | dependencies = [ 81 | "cc", 82 | "pkg-config", 83 | "vcpkg", 84 | ] 85 | 86 | [[package]] 87 | name = "once_cell" 88 | version = "1.20.2" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" 91 | 92 | [[package]] 93 | name = "pkg-config" 94 | version = "0.3.31" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" 97 | 98 | [[package]] 99 | name = "proc-macro2" 100 | version = "1.0.92" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" 103 | dependencies = [ 104 | "unicode-ident", 105 | ] 106 | 107 | [[package]] 108 | name = "quote" 109 | version = "1.0.37" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 112 | dependencies = [ 113 | "proc-macro2", 114 | ] 115 | 116 | [[package]] 117 | name = "rusqlite" 118 | version = "0.32.1" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" 121 | dependencies = [ 122 | "bitflags", 123 | "fallible-iterator", 124 | "fallible-streaming-iterator", 125 | "hashlink", 126 | "libsqlite3-sys", 127 | "smallvec", 128 | ] 129 | 130 | [[package]] 131 | name = "shlex" 132 | version = "1.3.0" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 135 | 136 | [[package]] 137 | name = "smallvec" 138 | version = "1.13.2" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 141 | 142 | [[package]] 143 | name = "syn" 144 | version = "2.0.91" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035" 147 | dependencies = [ 148 | "proc-macro2", 149 | "quote", 150 | "unicode-ident", 151 | ] 152 | 153 | [[package]] 154 | name = "unicode-ident" 155 | version = "1.0.14" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" 158 | 159 | [[package]] 160 | name = "vcpkg" 161 | version = "0.2.15" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 164 | 165 | [[package]] 166 | name = "version_check" 167 | version = "0.9.5" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 170 | 171 | [[package]] 172 | name = "zerocopy" 173 | version = "0.7.35" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 176 | dependencies = [ 177 | "zerocopy-derive", 178 | ] 179 | 180 | [[package]] 181 | name = "zerocopy-derive" 182 | version = "0.7.35" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 185 | dependencies = [ 186 | "proc-macro2", 187 | "quote", 188 | "syn", 189 | ] 190 | -------------------------------------------------------------------------------- /get-token/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "get-token" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | rusqlite = { version = "0.32.1", default-features = false, features = ["bundled"] } 8 | 9 | [profile.release] 10 | lto = true 11 | codegen-units = 1 12 | panic = 'abort' 13 | strip = true 14 | opt-level = 3 15 | -------------------------------------------------------------------------------- /get-token/README.md: -------------------------------------------------------------------------------- 1 | # Cursor Token 获取工具 2 | 3 | 这个工具用于从 Cursor 编辑器的本地数据库中获取访问令牌。 4 | 5 | ## 系统要求 6 | 7 | - Rust 编程环境 8 | - Cargo 包管理器 9 | 10 | ## 构建说明 11 | 12 | ### Windows 13 | 14 | 1. 安装 Rust 15 | ```powershell 16 | winget install Rustlang.Rust 17 | # 或访问 https://rustup.rs/ 下载安装程序 18 | ``` 19 | 20 | 2. 克隆项目并构建 21 | ```powershell 22 | git clone 23 | cd get-token 24 | cargo build --release 25 | ``` 26 | 27 | 3. 构建完成后,可执行文件位于 `target/release/get-token.exe` 28 | 29 | ### macOS 30 | 31 | 1. 安装 Rust 32 | ```bash 33 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 34 | ``` 35 | 36 | 2. 克隆项目并构建 37 | ```bash 38 | git clone 39 | cd get-token 40 | cargo build --release 41 | ``` 42 | 43 | 3. 构建完成后,可执行文件位于 `target/release/get-token` 44 | 45 | ## 使用方法 46 | 47 | 直接运行编译好的可执行文件即可: 48 | 49 | - Windows: `.\target\release\get-token.exe` 50 | - macOS: `./target/release/get-token` 51 | 52 | 程序将自动查找并显示 Cursor 编辑器的访问令牌。 53 | 54 | ## 注意事项 55 | 56 | - 确保 Cursor 编辑器已经安装并且至少登录过一次 57 | - Windows 数据库路径:`%USERPROFILE%\AppData\Roaming\Cursor\User\globalStorage\state.vscdb` 58 | - macOS 数据库路径:`~/Library/Application Support/Cursor/User/globalStorage/state.vscdb` -------------------------------------------------------------------------------- /get-token/src/main.rs: -------------------------------------------------------------------------------- 1 | use rusqlite::Connection; 2 | use std::env; 3 | use std::path::PathBuf; 4 | 5 | fn main() { 6 | let home_dir = env::var("HOME") 7 | .or_else(|_| env::var("USERPROFILE")) 8 | .unwrap(); 9 | let db_path = if cfg!(target_os = "windows") { 10 | PathBuf::from(home_dir).join(r"AppData\Roaming\Cursor\User\globalStorage\state.vscdb") 11 | } else { 12 | PathBuf::from(home_dir) 13 | .join("Library/Application Support/Cursor/User/globalStorage/state.vscdb") 14 | }; 15 | 16 | match Connection::open(&db_path) { 17 | Ok(conn) => { 18 | match conn.query_row( 19 | "SELECT value FROM ItemTable WHERE key = 'cursorAuth/accessToken'", 20 | [], 21 | |row| row.get::<_, String>(0), 22 | ) { 23 | Ok(token) => println!("访问令牌: {}", token.trim()), 24 | Err(err) => eprintln!("获取令牌时出错: {}", err), 25 | } 26 | } 27 | Err(err) => eprintln!("无法打开数据库: {}", err), 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /scripts/build.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waterwoodwind/cursor-api/3ab975a5b3c740fed34f62cdeece6d1dfbc62236/scripts/build.ps1 -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | # 颜色输出函数 5 | info() { echo -e "\033[1;34m[INFO]\033[0m $*"; } 6 | warn() { echo -e "\033[1;33m[WARN]\033[0m $*"; } 7 | error() { echo -e "\033[1;31m[ERROR]\033[0m $*" >&2; exit 1; } 8 | 9 | # 检查是否在 Linux 环境 10 | is_linux() { 11 | [ "$(uname -s)" = "Linux" ] 12 | } 13 | 14 | # 检查必要的工具 15 | check_requirements() { 16 | local missing_tools=() 17 | 18 | # 基础工具检查 19 | for tool in cargo protoc npm node; do 20 | if ! command -v "$tool" &>/dev/null; then 21 | missing_tools+=("$tool") 22 | fi 23 | done 24 | 25 | # Linux 特定检查 26 | if [[ $USE_CROSS == true ]] && ! command -v cross &>/dev/null; then 27 | missing_tools+=("cross") 28 | fi 29 | 30 | if [[ ${#missing_tools[@]} -gt 0 ]]; then 31 | error "缺少必要工具: ${missing_tools[*]}" 32 | fi 33 | } 34 | 35 | # 帮助信息 36 | show_help() { 37 | cat << EOF 38 | 用法: $(basename "$0") [选项] 39 | 40 | 选项: 41 | --cross 使用 cross 进行交叉编译(仅在 Linux 上有效) 42 | --static 使用静态链接(默认动态链接) 43 | --help 显示此帮助信息 44 | 45 | 不带参数时只编译当前平台 46 | EOF 47 | } 48 | 49 | # 并行构建函数 50 | build_target() { 51 | local target=$1 52 | local extension="" 53 | local rustflags="${2:-}" 54 | 55 | info "正在构建 $target..." 56 | 57 | # 确定文件后缀 58 | [[ $target == *"windows"* ]] && extension=".exe" 59 | 60 | # 设置目标特定的环境变量 61 | local build_env=() 62 | if [[ $target == "aarch64-unknown-linux-gnu" ]]; then 63 | build_env+=( 64 | "CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc" 65 | "CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++" 66 | "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" 67 | "PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig" 68 | "PKG_CONFIG_ALLOW_CROSS=1" 69 | "OPENSSL_DIR=/usr" 70 | "OPENSSL_INCLUDE_DIR=/usr/include" 71 | "OPENSSL_LIB_DIR=/usr/lib/aarch64-linux-gnu" 72 | ) 73 | fi 74 | 75 | # 判断是否使用 cross(仅在 Linux 上) 76 | if [[ $target != "$CURRENT_TARGET" ]]; then 77 | env ${build_env[@]+"${build_env[@]}"} RUSTFLAGS="$rustflags" cargo build --target "$target" --release 78 | else 79 | env ${build_env[@]+"${build_env[@]}"} RUSTFLAGS="$rustflags" cargo build --release 80 | fi 81 | 82 | # 移动编译产物到 release 目录 83 | local binary_name="cursor-api" 84 | [[ $USE_STATIC == true ]] && binary_name+="-static" 85 | 86 | if [[ -f "target/$target/release/cursor-api$extension" ]]; then 87 | cp "target/$target/release/cursor-api$extension" "release/${binary_name}-$target$extension" 88 | info "完成构建 $target" 89 | else 90 | warn "构建产物未找到: $target" 91 | return 1 92 | fi 93 | } 94 | 95 | # 获取 CPU 架构 96 | ARCH=$(uname -m | sed 's/^aarch64\|arm64$/aarch64/;s/^x86_64\|x86-64\|x64\|amd64$/x86_64/') 97 | OS=$(uname -s) 98 | 99 | # 确定当前系统的目标平台 100 | get_target() { 101 | local arch=$1 102 | local os=$2 103 | case "$os" in 104 | "Darwin") echo "${arch}-apple-darwin" ;; 105 | "Linux") echo "${arch}-unknown-linux-gnu" ;; 106 | "MINGW"*|"MSYS"*|"CYGWIN"*|"Windows_NT") echo "${arch}-pc-windows-msvc" ;; 107 | "FreeBSD") echo "x86_64-unknown-freebsd" ;; 108 | *) error "不支持的系统: $os" ;; 109 | esac 110 | } 111 | 112 | # 设置当前目标平台 113 | CURRENT_TARGET=$(get_target "$ARCH" "$OS") 114 | 115 | # 检查是否成功获取目标平台 116 | [ -z "$CURRENT_TARGET" ] && error "无法确定当前系统的目标平台" 117 | 118 | # 获取系统对应的所有目标 119 | get_targets() { 120 | case "$1" in 121 | "linux") echo "x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu" ;; 122 | "windows") echo "x86_64-pc-windows-msvc aarch64-pc-windows-msvc" ;; 123 | "macos") echo "x86_64-apple-darwin aarch64-apple-darwin" ;; 124 | "freebsd") echo "x86_64-unknown-freebsd" ;; 125 | *) error "不支持的系统组: $1" ;; 126 | esac 127 | } 128 | 129 | # 解析参数 130 | USE_CROSS=false 131 | USE_STATIC=false 132 | 133 | while [[ $# -gt 0 ]]; do 134 | case $1 in 135 | --cross) USE_CROSS=true ;; 136 | --static) USE_STATIC=true ;; 137 | --help) show_help; exit 0 ;; 138 | *) error "未知参数: $1" ;; 139 | esac 140 | shift 141 | done 142 | 143 | # 检查依赖 144 | check_requirements 145 | 146 | # 确定要构建的目标 147 | if [[ $USE_CROSS == true ]] && is_linux; then 148 | # 只在 Linux 上使用 cross 进行多架构构建 149 | TARGETS=($(get_targets "linux")) 150 | else 151 | # 其他系统或不使用 cross 时只构建当前系统的所有架构 152 | case "$OS" in 153 | "Darwin") TARGETS=($(get_targets "macos")) ;; 154 | "Linux") TARGETS=("$CURRENT_TARGET") ;; 155 | "MINGW"*|"MSYS"*|"CYGWIN"*|"Windows_NT") TARGETS=($(get_targets "windows")) ;; 156 | "FreeBSD") TARGETS=("$CURRENT_TARGET") ;; 157 | *) error "不支持的系统: $OS" ;; 158 | esac 159 | fi 160 | 161 | # 创建 release 目录 162 | mkdir -p release 163 | 164 | # 设置静态链接标志 165 | RUSTFLAGS="" 166 | [[ $USE_STATIC == true ]] && RUSTFLAGS="-C target-feature=+crt-static" 167 | 168 | # 并行构建所有目标 169 | info "开始构建..." 170 | for target in "${TARGETS[@]}"; do 171 | build_target "$target" "$RUSTFLAGS" & 172 | done 173 | 174 | # 等待所有构建完成 175 | wait 176 | 177 | # 为 macOS 平台创建通用二进制 178 | if [[ "$OS" == "Darwin" ]] && [[ ${#TARGETS[@]} -gt 1 ]]; then 179 | binary_suffix="" 180 | [[ $USE_STATIC == true ]] && binary_suffix="-static" 181 | 182 | if [[ -f "release/cursor-api${binary_suffix}-x86_64-apple-darwin" ]] && \ 183 | [[ -f "release/cursor-api${binary_suffix}-aarch64-apple-darwin" ]]; then 184 | info "创建 macOS 通用二进制..." 185 | lipo -create \ 186 | "release/cursor-api${binary_suffix}-x86_64-apple-darwin" \ 187 | "release/cursor-api${binary_suffix}-aarch64-apple-darwin" \ 188 | -output "release/cursor-api${binary_suffix}-universal-apple-darwin" 189 | fi 190 | fi 191 | 192 | info "构建完成!" -------------------------------------------------------------------------------- /scripts/minify-html.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const { minify } = require('html-minifier-terser'); 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | 7 | // 配置选项 8 | const options = { 9 | collapseWhitespace: true, 10 | removeComments: true, 11 | removeEmptyAttributes: true, 12 | removeOptionalTags: true, 13 | removeRedundantAttributes: true, 14 | removeScriptTypeAttributes: true, 15 | removeStyleLinkTypeAttributes: true, 16 | minifyCSS: true, 17 | minifyJS: true, 18 | processScripts: ['application/json'], 19 | }; 20 | 21 | // 处理文件 22 | async function minifyFile(inputPath, outputPath) { 23 | try { 24 | const html = fs.readFileSync(inputPath, 'utf8'); 25 | const minified = await minify(html, options); 26 | fs.writeFileSync(outputPath, minified); 27 | console.log(`✓ Minified ${path.basename(inputPath)} -> ${path.basename(outputPath)}`); 28 | } catch (err) { 29 | console.error(`✗ Error processing ${inputPath}:`, err); 30 | process.exit(1); 31 | } 32 | } 33 | 34 | // 主函数 35 | async function main() { 36 | const staticDir = path.join(__dirname, '..', 'static'); 37 | const files = [ 38 | ['tokeninfo.html', 'tokeninfo.min.html'], 39 | ]; 40 | 41 | for (const [input, output] of files) { 42 | await minifyFile( 43 | path.join(staticDir, input), 44 | path.join(staticDir, output) 45 | ); 46 | } 47 | } 48 | 49 | main(); -------------------------------------------------------------------------------- /scripts/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "html-minifier-scripts", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "html-minifier-scripts", 9 | "version": "1.0.0", 10 | "dependencies": { 11 | "html-minifier-terser": "^7.2.0" 12 | }, 13 | "engines": { 14 | "node": ">=14.0.0" 15 | } 16 | }, 17 | "node_modules/@jridgewell/gen-mapping": { 18 | "version": "0.3.8", 19 | "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", 20 | "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", 21 | "license": "MIT", 22 | "dependencies": { 23 | "@jridgewell/set-array": "^1.2.1", 24 | "@jridgewell/sourcemap-codec": "^1.4.10", 25 | "@jridgewell/trace-mapping": "^0.3.24" 26 | }, 27 | "engines": { 28 | "node": ">=6.0.0" 29 | } 30 | }, 31 | "node_modules/@jridgewell/resolve-uri": { 32 | "version": "3.1.2", 33 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 34 | "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 35 | "license": "MIT", 36 | "engines": { 37 | "node": ">=6.0.0" 38 | } 39 | }, 40 | "node_modules/@jridgewell/set-array": { 41 | "version": "1.2.1", 42 | "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", 43 | "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", 44 | "license": "MIT", 45 | "engines": { 46 | "node": ">=6.0.0" 47 | } 48 | }, 49 | "node_modules/@jridgewell/source-map": { 50 | "version": "0.3.6", 51 | "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", 52 | "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", 53 | "license": "MIT", 54 | "dependencies": { 55 | "@jridgewell/gen-mapping": "^0.3.5", 56 | "@jridgewell/trace-mapping": "^0.3.25" 57 | } 58 | }, 59 | "node_modules/@jridgewell/sourcemap-codec": { 60 | "version": "1.5.0", 61 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", 62 | "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", 63 | "license": "MIT" 64 | }, 65 | "node_modules/@jridgewell/trace-mapping": { 66 | "version": "0.3.25", 67 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", 68 | "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", 69 | "license": "MIT", 70 | "dependencies": { 71 | "@jridgewell/resolve-uri": "^3.1.0", 72 | "@jridgewell/sourcemap-codec": "^1.4.14" 73 | } 74 | }, 75 | "node_modules/acorn": { 76 | "version": "8.14.0", 77 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", 78 | "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", 79 | "license": "MIT", 80 | "bin": { 81 | "acorn": "bin/acorn" 82 | }, 83 | "engines": { 84 | "node": ">=0.4.0" 85 | } 86 | }, 87 | "node_modules/buffer-from": { 88 | "version": "1.1.2", 89 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 90 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", 91 | "license": "MIT" 92 | }, 93 | "node_modules/camel-case": { 94 | "version": "4.1.2", 95 | "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", 96 | "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", 97 | "license": "MIT", 98 | "dependencies": { 99 | "pascal-case": "^3.1.2", 100 | "tslib": "^2.0.3" 101 | } 102 | }, 103 | "node_modules/clean-css": { 104 | "version": "5.3.3", 105 | "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", 106 | "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", 107 | "license": "MIT", 108 | "dependencies": { 109 | "source-map": "~0.6.0" 110 | }, 111 | "engines": { 112 | "node": ">= 10.0" 113 | } 114 | }, 115 | "node_modules/commander": { 116 | "version": "10.0.1", 117 | "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", 118 | "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", 119 | "license": "MIT", 120 | "engines": { 121 | "node": ">=14" 122 | } 123 | }, 124 | "node_modules/dot-case": { 125 | "version": "3.0.4", 126 | "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", 127 | "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", 128 | "license": "MIT", 129 | "dependencies": { 130 | "no-case": "^3.0.4", 131 | "tslib": "^2.0.3" 132 | } 133 | }, 134 | "node_modules/entities": { 135 | "version": "4.5.0", 136 | "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", 137 | "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", 138 | "license": "BSD-2-Clause", 139 | "engines": { 140 | "node": ">=0.12" 141 | }, 142 | "funding": { 143 | "url": "https://github.com/fb55/entities?sponsor=1" 144 | } 145 | }, 146 | "node_modules/html-minifier-terser": { 147 | "version": "7.2.0", 148 | "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", 149 | "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==", 150 | "license": "MIT", 151 | "dependencies": { 152 | "camel-case": "^4.1.2", 153 | "clean-css": "~5.3.2", 154 | "commander": "^10.0.0", 155 | "entities": "^4.4.0", 156 | "param-case": "^3.0.4", 157 | "relateurl": "^0.2.7", 158 | "terser": "^5.15.1" 159 | }, 160 | "bin": { 161 | "html-minifier-terser": "cli.js" 162 | }, 163 | "engines": { 164 | "node": "^14.13.1 || >=16.0.0" 165 | } 166 | }, 167 | "node_modules/lower-case": { 168 | "version": "2.0.2", 169 | "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", 170 | "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", 171 | "license": "MIT", 172 | "dependencies": { 173 | "tslib": "^2.0.3" 174 | } 175 | }, 176 | "node_modules/no-case": { 177 | "version": "3.0.4", 178 | "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", 179 | "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", 180 | "license": "MIT", 181 | "dependencies": { 182 | "lower-case": "^2.0.2", 183 | "tslib": "^2.0.3" 184 | } 185 | }, 186 | "node_modules/param-case": { 187 | "version": "3.0.4", 188 | "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", 189 | "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", 190 | "license": "MIT", 191 | "dependencies": { 192 | "dot-case": "^3.0.4", 193 | "tslib": "^2.0.3" 194 | } 195 | }, 196 | "node_modules/pascal-case": { 197 | "version": "3.1.2", 198 | "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", 199 | "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", 200 | "license": "MIT", 201 | "dependencies": { 202 | "no-case": "^3.0.4", 203 | "tslib": "^2.0.3" 204 | } 205 | }, 206 | "node_modules/relateurl": { 207 | "version": "0.2.7", 208 | "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", 209 | "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", 210 | "license": "MIT", 211 | "engines": { 212 | "node": ">= 0.10" 213 | } 214 | }, 215 | "node_modules/source-map": { 216 | "version": "0.6.1", 217 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 218 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 219 | "license": "BSD-3-Clause", 220 | "engines": { 221 | "node": ">=0.10.0" 222 | } 223 | }, 224 | "node_modules/source-map-support": { 225 | "version": "0.5.21", 226 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 227 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 228 | "license": "MIT", 229 | "dependencies": { 230 | "buffer-from": "^1.0.0", 231 | "source-map": "^0.6.0" 232 | } 233 | }, 234 | "node_modules/terser": { 235 | "version": "5.37.0", 236 | "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz", 237 | "integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==", 238 | "license": "BSD-2-Clause", 239 | "dependencies": { 240 | "@jridgewell/source-map": "^0.3.3", 241 | "acorn": "^8.8.2", 242 | "commander": "^2.20.0", 243 | "source-map-support": "~0.5.20" 244 | }, 245 | "bin": { 246 | "terser": "bin/terser" 247 | }, 248 | "engines": { 249 | "node": ">=10" 250 | } 251 | }, 252 | "node_modules/terser/node_modules/commander": { 253 | "version": "2.20.3", 254 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 255 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 256 | "license": "MIT" 257 | }, 258 | "node_modules/tslib": { 259 | "version": "2.8.1", 260 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", 261 | "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", 262 | "license": "0BSD" 263 | } 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /scripts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "html-minifier-scripts", 3 | "version": "1.0.0", 4 | "private": true, 5 | "engines": { 6 | "node": ">=14.0.0" 7 | }, 8 | "dependencies": { 9 | "html-minifier-terser": "^7.2.0" 10 | } 11 | } -------------------------------------------------------------------------------- /scripts/setup-windows.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waterwoodwind/cursor-api/3ab975a5b3c740fed34f62cdeece6d1dfbc62236/scripts/setup-windows.ps1 -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _}; 2 | use flate2::read::GzDecoder; 3 | use prost::Message; 4 | use rand::{thread_rng, Rng}; 5 | use sha2::{Digest, Sha256}; 6 | use std::io::Read; 7 | use uuid::Uuid; 8 | 9 | pub mod proto { 10 | include!(concat!(env!("OUT_DIR"), "/cursor.rs")); 11 | } 12 | 13 | use proto::{ChatMessage, ResMessage}; 14 | 15 | #[derive(Debug)] 16 | pub struct ChatInput { 17 | pub role: String, 18 | pub content: String, 19 | } 20 | 21 | fn process_chat_inputs(inputs: Vec) -> (String, Vec) { 22 | // 收集 system 和 developer 指令 23 | let instructions = inputs 24 | .iter() 25 | .filter(|input| input.role == "system" || input.role == "developer") 26 | .map(|input| input.content.clone()) 27 | .collect::>() 28 | .join("\n\n"); 29 | 30 | // 使用默认指令或收集到的指令 31 | let instructions = if instructions.is_empty() { 32 | "Respond in Chinese by default".to_string() 33 | } else { 34 | instructions 35 | }; 36 | 37 | // 过滤出 user 和 assistant 对话 38 | let mut chat_inputs: Vec = inputs 39 | .into_iter() 40 | .filter(|input| input.role == "user" || input.role == "assistant") 41 | .collect(); 42 | 43 | // 处理空对话情况 44 | if chat_inputs.is_empty() { 45 | return ( 46 | instructions, 47 | vec![proto::chat_message::Message { 48 | role: 1, // user 49 | content: " ".to_string(), 50 | message_id: Uuid::new_v4().to_string(), 51 | }], 52 | ); 53 | } 54 | 55 | // 如果第一条是 assistant,插入空的 user 消息 56 | if chat_inputs 57 | .first() 58 | .map_or(false, |input| input.role == "assistant") 59 | { 60 | chat_inputs.insert( 61 | 0, 62 | ChatInput { 63 | role: "user".to_string(), 64 | content: " ".to_string(), 65 | }, 66 | ); 67 | } 68 | 69 | // 处理连续相同角色的情况 70 | let mut i = 1; 71 | while i < chat_inputs.len() { 72 | if chat_inputs[i].role == chat_inputs[i - 1].role { 73 | let insert_role = if chat_inputs[i].role == "user" { 74 | "assistant" 75 | } else { 76 | "user" 77 | }; 78 | chat_inputs.insert( 79 | i, 80 | ChatInput { 81 | role: insert_role.to_string(), 82 | content: " ".to_string(), 83 | }, 84 | ); 85 | } 86 | i += 1; 87 | } 88 | 89 | // 确保最后一条是 user 90 | if chat_inputs 91 | .last() 92 | .map_or(false, |input| input.role == "assistant") 93 | { 94 | chat_inputs.push(ChatInput { 95 | role: "user".to_string(), 96 | content: " ".to_string(), 97 | }); 98 | } 99 | 100 | // 转换为 proto messages 101 | let messages = chat_inputs 102 | .into_iter() 103 | .map(|input| proto::chat_message::Message { 104 | role: if input.role == "user" { 1 } else { 2 }, 105 | content: input.content, 106 | message_id: Uuid::new_v4().to_string(), 107 | }) 108 | .collect(); 109 | 110 | (instructions, messages) 111 | } 112 | 113 | pub async fn encode_chat_message( 114 | inputs: Vec, 115 | model_name: &str, 116 | ) -> Result, Box> { 117 | let (instructions, messages) = process_chat_inputs(inputs); 118 | 119 | let chat = ChatMessage { 120 | messages, 121 | instructions: Some(proto::chat_message::Instructions { 122 | content: instructions, 123 | }), 124 | project_path: "/path/to/project".to_string(), 125 | model: Some(proto::chat_message::Model { 126 | name: model_name.to_string(), 127 | empty: String::new(), 128 | }), 129 | request_id: Uuid::new_v4().to_string(), 130 | summary: String::new(), 131 | conversation_id: Uuid::new_v4().to_string(), 132 | }; 133 | 134 | let mut encoded = Vec::new(); 135 | chat.encode(&mut encoded)?; 136 | 137 | let len_prefix = format!("{:010x}", encoded.len()).to_uppercase(); 138 | let content = hex::encode_upper(&encoded); 139 | 140 | Ok(hex::decode(len_prefix + &content)?) 141 | } 142 | 143 | pub async fn decode_response(data: &[u8]) -> String { 144 | if let Ok(decoded) = decode_proto_messages(data) { 145 | if !decoded.is_empty() { 146 | return decoded; 147 | } 148 | } 149 | decompress_response(data).await 150 | } 151 | 152 | fn decode_proto_messages(data: &[u8]) -> Result> { 153 | let hex_str = hex::encode(data); 154 | let mut pos = 0; 155 | let mut messages = Vec::new(); 156 | 157 | while pos + 10 <= hex_str.len() { 158 | let msg_len = i64::from_str_radix(&hex_str[pos..pos + 10], 16)?; 159 | pos += 10; 160 | 161 | if pos + (msg_len * 2) as usize > hex_str.len() { 162 | break; 163 | } 164 | 165 | let msg_data = &hex_str[pos..pos + (msg_len * 2) as usize]; 166 | pos += (msg_len * 2) as usize; 167 | 168 | let buffer = hex::decode(msg_data)?; 169 | let response = ResMessage::decode(&buffer[..])?; 170 | messages.push(response.msg); 171 | } 172 | 173 | Ok(messages.join("")) 174 | } 175 | 176 | async fn decompress_response(data: &[u8]) -> String { 177 | if data.len() <= 5 { 178 | return String::new(); 179 | } 180 | 181 | let mut decoder = GzDecoder::new(&data[5..]); 182 | let mut text = String::new(); 183 | 184 | match decoder.read_to_string(&mut text) { 185 | Ok(_) => { 186 | if !text.contains("<|BEGIN_SYSTEM|>") { 187 | text 188 | } else { 189 | String::new() 190 | } 191 | } 192 | Err(_) => String::new(), 193 | } 194 | } 195 | 196 | pub fn generate_random_id( 197 | size: usize, 198 | dict_type: Option<&str>, 199 | custom_chars: Option<&str>, 200 | ) -> String { 201 | let charset = match (dict_type, custom_chars) { 202 | (_, Some(chars)) => chars, 203 | (Some("alphabet"), _) => "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 204 | (Some("max"), _) => "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-", 205 | _ => "0123456789", 206 | }; 207 | 208 | let mut rng = thread_rng(); 209 | (0..size) 210 | .map(|_| { 211 | let idx = rng.gen_range(0..charset.len()); 212 | charset.chars().nth(idx).unwrap() 213 | }) 214 | .collect() 215 | } 216 | 217 | pub fn generate_hash() -> String { 218 | let random_bytes = rand::thread_rng().gen::<[u8; 32]>(); 219 | let mut hasher = Sha256::new(); 220 | hasher.update(random_bytes); 221 | hex::encode(hasher.finalize()) 222 | } 223 | 224 | fn obfuscate_bytes(bytes: &mut Vec) { 225 | let mut prev: u8 = 165; 226 | for (idx, byte) in bytes.iter_mut().enumerate() { 227 | let old_value = *byte; 228 | *byte = (old_value ^ prev).wrapping_add((idx % 256) as u8); 229 | prev = *byte; 230 | } 231 | } 232 | 233 | pub fn generate_checksum(device_id: &str, mac_addr: Option<&str>) -> String { 234 | let timestamp = std::time::SystemTime::now() 235 | .duration_since(std::time::UNIX_EPOCH) 236 | .unwrap() 237 | .as_millis() 238 | / 1_000_000; 239 | 240 | let mut timestamp_bytes = vec![ 241 | ((timestamp >> 40) & 255) as u8, 242 | ((timestamp >> 32) & 255) as u8, 243 | ((timestamp >> 24) & 255) as u8, 244 | ((timestamp >> 16) & 255) as u8, 245 | ((timestamp >> 8) & 255) as u8, 246 | (255 & timestamp) as u8, 247 | ]; 248 | 249 | obfuscate_bytes(&mut timestamp_bytes); 250 | let encoded = BASE64.encode(×tamp_bytes); 251 | 252 | match mac_addr { 253 | Some(mac) => format!("{}{}/{}", encoded, device_id, mac), 254 | None => format!("{}{}", encoded, device_id), 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | body::Body, 3 | extract::State, 4 | http::{HeaderMap, StatusCode}, 5 | response::{IntoResponse, Response}, 6 | routing::{get, post}, 7 | Json, Router, 8 | }; 9 | use bytes::Bytes; 10 | use chrono::{DateTime, Local, Utc}; 11 | use futures::StreamExt; 12 | use reqwest::Client; 13 | use serde::{Deserialize, Serialize}; 14 | use std::sync::atomic::{AtomicUsize, Ordering}; 15 | use std::{convert::Infallible, sync::Arc}; 16 | use tokio::sync::Mutex; 17 | use tower_http::cors::CorsLayer; 18 | use uuid::Uuid; 19 | 20 | // 应用状态 21 | struct AppState { 22 | start_time: DateTime, 23 | version: String, 24 | total_requests: u64, 25 | active_requests: u64, 26 | request_logs: Vec, 27 | route_prefix: String, 28 | token_infos: Vec, 29 | } 30 | 31 | // 模型定义 32 | #[derive(Serialize, Deserialize, Clone)] 33 | struct Model { 34 | id: String, 35 | created: i64, 36 | object: String, 37 | owned_by: String, 38 | } 39 | 40 | // 请求日志 41 | #[derive(Serialize, Clone)] 42 | struct RequestLog { 43 | timestamp: DateTime, 44 | model: String, 45 | checksum: String, 46 | auth_token: String, 47 | stream: bool, 48 | } 49 | 50 | // 聊天请求 51 | #[derive(Deserialize)] 52 | struct ChatRequest { 53 | model: String, 54 | messages: Vec, 55 | #[serde(default)] 56 | stream: bool, 57 | } 58 | 59 | // 添加用于请求的消息结构体 60 | #[derive(Serialize, Deserialize)] 61 | struct Message { 62 | role: String, 63 | content: String, 64 | } 65 | 66 | // 支持的模型列表 67 | mod models; 68 | use models::AVAILABLE_MODELS; 69 | 70 | // 用于存储 token 信息 71 | #[derive(Debug)] 72 | struct TokenInfo { 73 | token: String, 74 | checksum: String, 75 | } 76 | 77 | // TokenUpdateRequest 结构体 78 | #[derive(Deserialize)] 79 | struct TokenUpdateRequest { 80 | tokens: String, 81 | #[serde(default)] 82 | token_list: Option, 83 | } 84 | 85 | // 自定义错误类型 86 | #[derive(Debug)] 87 | enum ChatError { 88 | ModelNotSupported(String), 89 | EmptyMessages, 90 | StreamNotSupported(String), 91 | NoTokens, 92 | RequestFailed(String), 93 | Unauthorized, 94 | } 95 | 96 | impl ChatError { 97 | fn to_json(&self) -> serde_json::Value { 98 | let (code, message) = match self { 99 | ChatError::ModelNotSupported(model) => ( 100 | "model_not_supported", 101 | format!("Model '{}' is not supported", model), 102 | ), 103 | ChatError::EmptyMessages => ( 104 | "empty_messages", 105 | "Message array cannot be empty".to_string(), 106 | ), 107 | ChatError::StreamNotSupported(model) => ( 108 | "stream_not_supported", 109 | format!("Streaming is not supported for model '{}'", model), 110 | ), 111 | ChatError::NoTokens => ("no_tokens", "No available tokens".to_string()), 112 | ChatError::RequestFailed(err) => ("request_failed", format!("Request failed: {}", err)), 113 | ChatError::Unauthorized => ("unauthorized", "Invalid authorization token".to_string()), 114 | }; 115 | 116 | serde_json::json!({ 117 | "error": { 118 | "code": code, 119 | "message": message 120 | } 121 | }) 122 | } 123 | } 124 | 125 | #[tokio::main] 126 | async fn main() { 127 | // 加载环境变量 128 | dotenvy::dotenv().ok(); 129 | 130 | // 处理 token 文件路径 131 | let token_file = std::env::var("TOKEN_FILE").unwrap_or_else(|_| ".token".to_string()); 132 | 133 | // 加载 tokens 134 | let token_infos = load_tokens(&token_file); 135 | 136 | // 获取路由前缀配置 137 | let route_prefix = std::env::var("ROUTE_PREFIX").unwrap_or_default(); 138 | 139 | // 初始化应用状态 140 | let state = Arc::new(Mutex::new(AppState { 141 | start_time: Local::now(), 142 | version: env!("CARGO_PKG_VERSION").to_string(), 143 | total_requests: 0, 144 | active_requests: 0, 145 | request_logs: Vec::new(), 146 | route_prefix: route_prefix.clone(), 147 | token_infos, 148 | })); 149 | 150 | // 设置路由 151 | let app = Router::new() 152 | .route("/", get(handle_root)) 153 | .route("/tokeninfo", get(handle_tokeninfo_page)) 154 | .route(&format!("{}/v1/models", route_prefix), get(handle_models)) 155 | .route("/checksum", get(handle_checksum)) 156 | .route("/update-tokeninfo", get(handle_update_tokeninfo)) 157 | .route("/get-tokeninfo", post(handle_get_tokeninfo)) 158 | .route("/update-tokeninfo", post(handle_update_tokeninfo_post)) 159 | .route( 160 | &format!("{}/v1/chat/completions", route_prefix), 161 | post(handle_chat), 162 | ) 163 | .route("/logs", get(handle_logs)) 164 | .layer(CorsLayer::permissive()) 165 | .with_state(state); 166 | 167 | // 启动服务器 168 | let port = std::env::var("PORT").unwrap_or_else(|_| "3000".to_string()); 169 | let addr = format!("0.0.0.0:{}", port); 170 | println!("服务器运行在端口 {}", port); 171 | 172 | let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); 173 | axum::serve(listener, app).await.unwrap(); 174 | } 175 | 176 | // Token 加载函数 177 | fn load_tokens(token_file: &str) -> Vec { 178 | let token_list_file = 179 | std::env::var("TOKEN_LIST_FILE").unwrap_or_else(|_| ".token-list".to_string()); 180 | 181 | // 读取并规范化 .token 文件 182 | let tokens = if let Ok(content) = std::fs::read_to_string(token_file) { 183 | let normalized = content.replace("\r\n", "\n"); 184 | if normalized != content { 185 | std::fs::write(token_file, &normalized).unwrap(); 186 | } 187 | normalized 188 | .lines() 189 | .enumerate() 190 | .filter_map(|(idx, line)| { 191 | let parts: Vec<&str> = line.split("::").collect(); 192 | match parts.len() { 193 | 1 => Some(line.to_string()), 194 | 2 => Some(parts[1].to_string()), 195 | _ => { 196 | println!("警告: 第{}行包含多个'::'分隔符,已忽略此行", idx + 1); 197 | None 198 | } 199 | } 200 | }) 201 | .filter(|s| !s.is_empty()) 202 | .collect::>() 203 | } else { 204 | eprintln!("警告: 无法读取token文件 '{}'", token_file); 205 | Vec::new() 206 | }; 207 | 208 | // 读取现有的 token-list 209 | let mut token_map: std::collections::HashMap = 210 | if let Ok(content) = std::fs::read_to_string(&token_list_file) { 211 | content 212 | .split('\n') 213 | .filter(|s| !s.is_empty()) 214 | .filter_map(|line| { 215 | let parts: Vec<&str> = line.split(',').collect(); 216 | if parts.len() == 2 { 217 | Some((parts[0].to_string(), parts[1].to_string())) 218 | } else { 219 | None 220 | } 221 | }) 222 | .collect() 223 | } else { 224 | std::collections::HashMap::new() 225 | }; 226 | 227 | // 为新 token 生成 checksum 228 | for token in tokens { 229 | if !token_map.contains_key(&token) { 230 | let checksum = cursor_api::generate_checksum( 231 | &cursor_api::generate_hash(), 232 | Some(&cursor_api::generate_hash()), 233 | ); 234 | token_map.insert(token, checksum); 235 | } 236 | } 237 | 238 | // 更新 token-list 文件 239 | let token_list_content = token_map 240 | .iter() 241 | .map(|(token, checksum)| format!("{},{}", token, checksum)) 242 | .collect::>() 243 | .join("\n"); 244 | std::fs::write(token_list_file, token_list_content).unwrap(); 245 | 246 | // 转换为 TokenInfo vector 247 | token_map 248 | .into_iter() 249 | .map(|(token, checksum)| TokenInfo { token, checksum }) 250 | .collect() 251 | } 252 | 253 | // 根路由处理 254 | async fn handle_root(State(state): State>>) -> Json { 255 | let state = state.lock().await; 256 | let uptime = (Local::now() - state.start_time).num_seconds(); 257 | 258 | Json(serde_json::json!({ 259 | "status": "healthy", 260 | "version": state.version, 261 | "uptime": uptime, 262 | "stats": { 263 | "started": state.start_time, 264 | "totalRequests": state.total_requests, 265 | "activeRequests": state.active_requests, 266 | "memory": { 267 | "heapTotal": 0, 268 | "heapUsed": 0, 269 | "rss": 0 270 | } 271 | }, 272 | "models": AVAILABLE_MODELS.iter().map(|m| &m.id).collect::>(), 273 | "endpoints": [ 274 | &format!("{}/v1/chat/completions", state.route_prefix), 275 | &format!("{}/v1/models", state.route_prefix), 276 | "/checksum", 277 | "/tokeninfo", 278 | "/update-tokeninfo", 279 | "/get-tokeninfo" 280 | ] 281 | })) 282 | } 283 | 284 | async fn handle_tokeninfo_page() -> impl IntoResponse { 285 | Response::builder() 286 | .header("Content-Type", "text/html") 287 | .body(include_str!("../static/tokeninfo.min.html").to_string()) 288 | .unwrap() 289 | } 290 | 291 | // 模型列表处理 292 | async fn handle_models() -> Json { 293 | Json(serde_json::json!({ 294 | "object": "list", 295 | "data": AVAILABLE_MODELS.to_vec() 296 | })) 297 | } 298 | 299 | // Checksum 处理 300 | async fn handle_checksum() -> Json { 301 | let checksum = cursor_api::generate_checksum( 302 | &cursor_api::generate_hash(), 303 | Some(&cursor_api::generate_hash()), 304 | ); 305 | Json(serde_json::json!({ 306 | "checksum": checksum 307 | })) 308 | } 309 | 310 | // 更新 TokenInfo 处理 311 | async fn handle_update_tokeninfo( 312 | State(state): State>>, 313 | ) -> Json { 314 | // 获取当前的 token 文件路径 315 | let token_file = std::env::var("TOKEN_FILE").unwrap_or_else(|_| ".token".to_string()); 316 | 317 | // 重新加载 tokens 318 | let token_infos = load_tokens(&token_file); 319 | 320 | // 更新应用状态 321 | { 322 | let mut state = state.lock().await; 323 | state.token_infos = token_infos; 324 | } 325 | 326 | Json(serde_json::json!({ 327 | "status": "success", 328 | "message": "Token list has been reloaded" 329 | })) 330 | } 331 | 332 | // 获取 TokenInfo 处理 333 | async fn handle_get_tokeninfo( 334 | State(_state): State>>, 335 | headers: HeaderMap, 336 | ) -> Result, StatusCode> { 337 | // 验证 AUTH_TOKEN 338 | let auth_header = headers 339 | .get("authorization") 340 | .and_then(|h| h.to_str().ok()) 341 | .and_then(|h| h.strip_prefix("Bearer ")) 342 | .ok_or(StatusCode::UNAUTHORIZED)?; 343 | 344 | let env_token = std::env::var("AUTH_TOKEN").map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 345 | 346 | if auth_header != env_token { 347 | return Err(StatusCode::UNAUTHORIZED); 348 | } 349 | 350 | // 获取文件路径 351 | let token_file = std::env::var("TOKEN_FILE").unwrap_or_else(|_| ".token".to_string()); 352 | let token_list_file = 353 | std::env::var("TOKEN_LIST_FILE").unwrap_or_else(|_| ".token-list".to_string()); 354 | 355 | // 读取文件内容 356 | let tokens = std::fs::read_to_string(&token_file).unwrap_or_else(|_| String::new()); 357 | let token_list = std::fs::read_to_string(&token_list_file).unwrap_or_else(|_| String::new()); 358 | 359 | Ok(Json(serde_json::json!({ 360 | "status": "success", 361 | "token_file": token_file, 362 | "token_list_file": token_list_file, 363 | "tokens": tokens, 364 | "token_list": token_list 365 | }))) 366 | } 367 | 368 | async fn handle_update_tokeninfo_post( 369 | State(state): State>>, 370 | headers: HeaderMap, 371 | Json(request): Json, 372 | ) -> Result, StatusCode> { 373 | // 验证 AUTH_TOKEN 374 | let auth_header = headers 375 | .get("authorization") 376 | .and_then(|h| h.to_str().ok()) 377 | .and_then(|h| h.strip_prefix("Bearer ")) 378 | .ok_or(StatusCode::UNAUTHORIZED)?; 379 | 380 | let env_token = std::env::var("AUTH_TOKEN").map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 381 | 382 | if auth_header != env_token { 383 | return Err(StatusCode::UNAUTHORIZED); 384 | } 385 | 386 | // 获取文件路径 387 | let token_file = std::env::var("TOKEN_FILE").unwrap_or_else(|_| ".token".to_string()); 388 | let token_list_file = 389 | std::env::var("TOKEN_LIST_FILE").unwrap_or_else(|_| ".token-list".to_string()); 390 | 391 | // 写入 .token 文件 392 | std::fs::write(&token_file, &request.tokens).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 393 | 394 | // 如果提供了 token_list,则写入 395 | if let Some(token_list) = request.token_list { 396 | std::fs::write(&token_list_file, token_list) 397 | .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; 398 | } 399 | 400 | // 重新加载 tokens 401 | let token_infos = load_tokens(&token_file); 402 | let token_infos_len = token_infos.len(); 403 | 404 | // 更新应用状态 405 | { 406 | let mut state = state.lock().await; 407 | state.token_infos = token_infos; 408 | } 409 | 410 | Ok(Json(serde_json::json!({ 411 | "status": "success", 412 | "message": "Token files have been updated and reloaded", 413 | "token_file": token_file, 414 | "token_list_file": token_list_file, 415 | "token_count": token_infos_len 416 | }))) 417 | } 418 | 419 | // 日志处理 420 | async fn handle_logs(State(state): State>>) -> Json { 421 | let state = state.lock().await; 422 | Json(serde_json::json!({ 423 | "total": state.request_logs.len(), 424 | "logs": state.request_logs, 425 | "timestamp": Utc::now(), 426 | "status": "success" 427 | })) 428 | } 429 | 430 | // 聊天处理函数的签名 431 | async fn handle_chat( 432 | State(state): State>>, 433 | headers: HeaderMap, 434 | Json(request): Json, 435 | ) -> Result, (StatusCode, Json)> { 436 | // 验证模型是否支持 437 | if !AVAILABLE_MODELS.iter().any(|m| m.id == request.model) { 438 | return Err(( 439 | StatusCode::BAD_REQUEST, 440 | Json(ChatError::ModelNotSupported(request.model.clone()).to_json()), 441 | )); 442 | } 443 | 444 | let request_time = Local::now(); 445 | 446 | // 验证请求 447 | if request.messages.is_empty() { 448 | return Err(( 449 | StatusCode::BAD_REQUEST, 450 | Json(ChatError::EmptyMessages.to_json()), 451 | )); 452 | } 453 | 454 | // 验证 O1 模型不支持流式输出 455 | if request.model.starts_with("o1") && request.stream { 456 | return Err(( 457 | StatusCode::BAD_REQUEST, 458 | Json(ChatError::StreamNotSupported(request.model.clone()).to_json()), 459 | )); 460 | } 461 | 462 | // 获取并处理认证令牌 463 | let auth_token = headers 464 | .get("authorization") 465 | .and_then(|h| h.to_str().ok()) 466 | .and_then(|h| h.strip_prefix("Bearer ")) 467 | .ok_or(( 468 | StatusCode::UNAUTHORIZED, 469 | Json(ChatError::Unauthorized.to_json()), 470 | ))?; 471 | 472 | // 验证环境变量中的 AUTH_TOKEN 473 | if let Ok(env_token) = std::env::var("AUTH_TOKEN") { 474 | if auth_token != env_token { 475 | return Err(( 476 | StatusCode::UNAUTHORIZED, 477 | Json(ChatError::Unauthorized.to_json()), 478 | )); 479 | } 480 | } 481 | 482 | // 完整的令牌处理逻辑和对应的 checksum 483 | let (auth_token, checksum) = { 484 | static CURRENT_KEY_INDEX: AtomicUsize = AtomicUsize::new(0); 485 | let state_guard = state.lock().await; 486 | let token_infos = &state_guard.token_infos; 487 | 488 | if token_infos.is_empty() { 489 | return Err(( 490 | StatusCode::SERVICE_UNAVAILABLE, 491 | Json(ChatError::NoTokens.to_json()), 492 | )); 493 | } 494 | 495 | let index = CURRENT_KEY_INDEX.fetch_add(1, Ordering::SeqCst) % token_infos.len(); 496 | let token_info = &token_infos[index]; 497 | (token_info.token.clone(), token_info.checksum.clone()) 498 | }; 499 | 500 | // 更新请求日志 501 | { 502 | let mut state = state.lock().await; 503 | state.total_requests += 1; 504 | state.active_requests += 1; 505 | state.request_logs.push(RequestLog { 506 | timestamp: request_time, 507 | model: request.model.clone(), 508 | checksum: checksum.clone(), 509 | auth_token: auth_token.clone(), 510 | stream: request.stream, 511 | }); 512 | 513 | if state.request_logs.len() > 100 { 514 | state.request_logs.remove(0); 515 | } 516 | } 517 | 518 | // 消息转换 519 | let chat_inputs: Vec = request 520 | .messages 521 | .into_iter() 522 | .map(|m| cursor_api::ChatInput { 523 | role: m.role, 524 | content: m.content, 525 | }) 526 | .collect(); 527 | 528 | // 将消息转换为hex格式 529 | let hex_data = cursor_api::encode_chat_message(chat_inputs, &request.model) 530 | .await 531 | .map_err(|_| { 532 | ( 533 | StatusCode::INTERNAL_SERVER_ERROR, 534 | Json( 535 | ChatError::RequestFailed("Failed to encode chat message".to_string()).to_json(), 536 | ), 537 | ) 538 | })?; 539 | 540 | // 构建请求客户端 541 | let client = Client::new(); 542 | let request_id = Uuid::new_v4().to_string(); 543 | let response = client 544 | .post("https://api2.cursor.sh/aiserver.v1.AiService/StreamChat") 545 | .header("Content-Type", "application/connect+proto") 546 | .header("Authorization", format!("Bearer {}", auth_token)) 547 | .header("connect-accept-encoding", "gzip,br") 548 | .header("connect-protocol-version", "1") 549 | .header("user-agent", "connect-es/1.4.0") 550 | .header("x-amzn-trace-id", format!("Root={}", &request_id)) 551 | .header("x-cursor-checksum", &checksum) 552 | .header("x-cursor-client-version", "0.42.5") 553 | .header("x-cursor-timezone", "Asia/Shanghai") 554 | .header("x-ghost-mode", "false") 555 | .header("x-request-id", &request_id) 556 | .header("Host", "api2.cursor.sh") 557 | .body(hex_data) 558 | .send() 559 | .await 560 | .map_err(|e| { 561 | ( 562 | StatusCode::INTERNAL_SERVER_ERROR, 563 | Json(ChatError::RequestFailed(format!("Request failed: {}", e)).to_json()), 564 | ) 565 | })?; 566 | 567 | // 释放活动请求计数 568 | { 569 | let mut state = state.lock().await; 570 | state.active_requests -= 1; 571 | } 572 | 573 | if request.stream { 574 | let response_id = format!("chatcmpl-{}", Uuid::new_v4()); 575 | 576 | let stream = response.bytes_stream().then(move |chunk| { 577 | let response_id = response_id.clone(); 578 | let model = request.model.clone(); 579 | 580 | async move { 581 | let chunk = chunk.unwrap_or_default(); 582 | let text = cursor_api::decode_response(&chunk).await; 583 | 584 | if text.is_empty() { 585 | return Ok::<_, Infallible>(Bytes::from("[DONE]")); 586 | } 587 | 588 | let data = serde_json::json!({ 589 | "id": &response_id, 590 | "object": "chat.completion.chunk", 591 | "created": chrono::Utc::now().timestamp(), 592 | "model": model, 593 | "choices": [{ 594 | "index": 0, 595 | "delta": { 596 | "content": text 597 | } 598 | }] 599 | }); 600 | 601 | Ok::<_, Infallible>(Bytes::from(format!("data: {}\n\n", data.to_string()))) 602 | } 603 | }); 604 | 605 | Ok(Response::builder() 606 | .header("Content-Type", "text/event-stream") 607 | .header("Cache-Control", "no-cache") 608 | .header("Connection", "keep-alive") 609 | .body(Body::from_stream(stream)) 610 | .unwrap()) 611 | } else { 612 | // 非流式响应 613 | let mut full_text = String::new(); 614 | let mut stream = response.bytes_stream(); 615 | 616 | while let Some(chunk) = stream.next().await { 617 | let chunk = chunk.map_err(|e| { 618 | ( 619 | StatusCode::INTERNAL_SERVER_ERROR, 620 | Json( 621 | ChatError::RequestFailed(format!("Failed to read response chunk: {}", e)) 622 | .to_json(), 623 | ), 624 | ) 625 | })?; 626 | full_text.push_str(&cursor_api::decode_response(&chunk).await); 627 | } 628 | 629 | // 处理文本 630 | full_text = full_text 631 | .replace( 632 | regex::Regex::new(r"^.*<\|END_USER\|>").unwrap().as_str(), 633 | "", 634 | ) 635 | .replace(regex::Regex::new(r"^\n[a-zA-Z]?").unwrap().as_str(), "") 636 | .trim() 637 | .to_string(); 638 | 639 | let response_data = serde_json::json!({ 640 | "id": format!("chatcmpl-{}", Uuid::new_v4()), 641 | "object": "chat.completion", 642 | "created": chrono::Utc::now().timestamp(), 643 | "model": request.model, 644 | "choices": [{ 645 | "index": 0, 646 | "message": { 647 | "role": "assistant", 648 | "content": full_text 649 | }, 650 | "finish_reason": "stop" 651 | }], 652 | "usage": { 653 | "prompt_tokens": 0, 654 | "completion_tokens": 0, 655 | "total_tokens": 0 656 | } 657 | }); 658 | 659 | Ok(Response::new(Body::from(response_data.to_string()))) 660 | } 661 | } 662 | -------------------------------------------------------------------------------- /src/message.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package cursor; 4 | 5 | message ChatMessage { 6 | message FileContent { 7 | message Position { 8 | int32 line = 1; 9 | int32 column = 2; 10 | } 11 | message Range { 12 | Position start = 1; 13 | Position end = 2; 14 | } 15 | 16 | string filename = 1; 17 | string content = 2; 18 | Position position = 3; 19 | string language = 5; 20 | Range range = 6; 21 | int32 length = 8; 22 | int32 type = 9; 23 | int32 error_code = 11; 24 | } 25 | 26 | message Message { 27 | string content = 1; 28 | int32 role = 2; 29 | string message_id = 13; 30 | } 31 | 32 | message Instructions { 33 | string content = 1; 34 | } 35 | 36 | message Model { 37 | string name = 1; 38 | string empty = 4; 39 | } 40 | 41 | // repeated FileContent files = 1; 42 | repeated Message messages = 2; 43 | Instructions instructions = 4; 44 | string projectPath = 5; 45 | Model model = 7; 46 | string requestId = 9; 47 | string summary = 11; // 或许是空的,描述会话做了什么事情,但是不是标题 或许可以当作额外的设定来用 48 | string conversationId = 15; // 又来一个uuid 49 | } 50 | 51 | message ResMessage { 52 | string msg = 1; 53 | } -------------------------------------------------------------------------------- /src/models.rs: -------------------------------------------------------------------------------- 1 | use std::sync::LazyLock; 2 | use crate::Model; 3 | 4 | const MODEL_OBJECT: &str = "model"; 5 | const ANTHROPIC: &str = "anthropic"; 6 | const CURSOR: &str = "cursor"; 7 | const GOOGLE: &str = "google"; 8 | const OPENAI: &str = "openai"; 9 | 10 | pub static AVAILABLE_MODELS: LazyLock> = LazyLock::new(|| { 11 | vec![ 12 | Model { 13 | id: "cursor-small".into(), 14 | created: 1706659200, 15 | object: MODEL_OBJECT.into(), 16 | owned_by: CURSOR.into() 17 | }, 18 | Model { 19 | id: "claude-3-opus".into(), 20 | created: 1706659200, 21 | object: MODEL_OBJECT.into(), 22 | owned_by: ANTHROPIC.into() 23 | }, 24 | Model { 25 | id: "cursor-fast".into(), 26 | created: 1706659200, 27 | object: MODEL_OBJECT.into(), 28 | owned_by: CURSOR.into() 29 | }, 30 | Model { 31 | id: "gpt-3.5-turbo".into(), 32 | created: 1706659200, 33 | object: MODEL_OBJECT.into(), 34 | owned_by: OPENAI.into() 35 | }, 36 | Model { 37 | id: "gpt-4-turbo-2024-04-09".into(), 38 | created: 1706659200, 39 | object: MODEL_OBJECT.into(), 40 | owned_by: OPENAI.into() 41 | }, 42 | Model { 43 | id: "gpt-4".into(), 44 | created: 1706659200, 45 | object: MODEL_OBJECT.into(), 46 | owned_by: OPENAI.into() 47 | }, 48 | Model { 49 | id: "gpt-4o-128k".into(), 50 | created: 1706659200, 51 | object: MODEL_OBJECT.into(), 52 | owned_by: OPENAI.into() 53 | }, 54 | Model { 55 | id: "gemini-1.5-flash-500k".into(), 56 | created: 1706659200, 57 | object: MODEL_OBJECT.into(), 58 | owned_by: GOOGLE.into() 59 | }, 60 | Model { 61 | id: "claude-3-haiku-200k".into(), 62 | created: 1706659200, 63 | object: MODEL_OBJECT.into(), 64 | owned_by: ANTHROPIC.into() 65 | }, 66 | Model { 67 | id: "claude-3-5-sonnet-200k".into(), 68 | created: 1706659200, 69 | object: MODEL_OBJECT.into(), 70 | owned_by: ANTHROPIC.into() 71 | }, 72 | Model { 73 | id: "claude-3-5-sonnet-20240620".into(), 74 | created: 1706659200, 75 | object: MODEL_OBJECT.into(), 76 | owned_by: ANTHROPIC.into() 77 | }, 78 | Model { 79 | id: "claude-3-5-sonnet-20241022".into(), 80 | created: 1706659200, 81 | object: MODEL_OBJECT.into(), 82 | owned_by: ANTHROPIC.into() 83 | }, 84 | Model { 85 | id: "gpt-4o-mini".into(), 86 | created: 1706659200, 87 | object: MODEL_OBJECT.into(), 88 | owned_by: OPENAI.into() 89 | }, 90 | Model { 91 | id: "o1-mini".into(), 92 | created: 1706659200, 93 | object: MODEL_OBJECT.into(), 94 | owned_by: OPENAI.into() 95 | }, 96 | Model { 97 | id: "o1-preview".into(), 98 | created: 1706659200, 99 | object: MODEL_OBJECT.into(), 100 | owned_by: OPENAI.into() 101 | }, 102 | Model { 103 | id: "o1".into(), 104 | created: 1706659200, 105 | object: MODEL_OBJECT.into(), 106 | owned_by: OPENAI.into() 107 | }, 108 | Model { 109 | id: "claude-3.5-haiku".into(), 110 | created: 1706659200, 111 | object: MODEL_OBJECT.into(), 112 | owned_by: ANTHROPIC.into() 113 | }, 114 | Model { 115 | id: "gemini-exp-1206".into(), 116 | created: 1706659200, 117 | object: MODEL_OBJECT.into(), 118 | owned_by: GOOGLE.into() 119 | }, 120 | Model { 121 | id: "gemini-2.0-flash-thinking-exp".into(), 122 | created: 1706659200, 123 | object: MODEL_OBJECT.into(), 124 | owned_by: GOOGLE.into() 125 | }, 126 | Model { 127 | id: "gemini-2.0-flash-exp".into(), 128 | created: 1706659200, 129 | object: MODEL_OBJECT.into(), 130 | owned_by: GOOGLE.into() 131 | } 132 | ] 133 | }); -------------------------------------------------------------------------------- /static/tokeninfo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Token 信息管理 8 | 74 | 75 | 76 | 77 |

Token 信息管理

78 | 79 |
80 |

认证

81 | 82 |
83 | 84 |
85 |

Token 配置

86 |
87 | 88 | 89 |
90 | 91 |

Token 文件内容

92 | 93 | 94 |

Token List 文件内容

95 | 96 |
97 | 98 |
99 | 100 | 182 | 183 | 184 | --------------------------------------------------------------------------------