diff --git a/.github/workflows/build-gateway.yml b/.github/workflows/build-gateway.yml index 516e68c3c..49f230abf 100644 --- a/.github/workflows/build-gateway.yml +++ b/.github/workflows/build-gateway.yml @@ -58,7 +58,7 @@ jobs: uses: docker/build-push-action@v6 with: context: . - file: gateway/Dockerfile + file: Dockerfile.gateway platforms: ${{ matrix.platform.os }} outputs: type=image,name=${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=${{ inputs.dry_run != true }} cache-from: type=gha,scope=gateway-${{ matrix.platform.os }} @@ -160,7 +160,7 @@ jobs: ### Links - - [Gateway README](https://github.com/openabdev/openab/blob/main/gateway/README.md) + - [Gateway Crate](https://github.com/openabdev/openab/tree/main/crates/openab-gateway) - [ADR: Custom Gateway](https://github.com/openabdev/openab/blob/main/docs/adr/custom-gateway.md) EOF sed -i 's/^ //' /tmp/release-notes.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4a2e000e3..fd5a9e13e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,7 @@ on: pull_request: paths: - "src/**" - - "gateway/**" + - "crates/**" - "operator/**" - "Cargo.toml" - "Cargo.lock" @@ -18,7 +18,6 @@ jobs: runs-on: ubuntu-latest outputs: core: ${{ steps.filter.outputs.core }} - gateway: ${{ steps.filter.outputs.gateway }} operator: ${{ steps.filter.outputs.operator }} steps: - uses: actions/checkout@v6 @@ -29,8 +28,7 @@ jobs: BASE=${{ github.event.pull_request.base.sha }} HEAD=${{ github.event.pull_request.head.sha }} CHANGED=$(git diff --name-only "$BASE" "$HEAD") - echo "core=$(echo "$CHANGED" | grep -qE '^(src/|Cargo\.(toml|lock))' && echo true || echo false)" >> "$GITHUB_OUTPUT" - echo "gateway=$(echo "$CHANGED" | grep -q '^gateway/' && echo true || echo false)" >> "$GITHUB_OUTPUT" + echo "core=$(echo "$CHANGED" | grep -qE '^(src/|crates/|Cargo\.(toml|lock))' && echo true || echo false)" >> "$GITHUB_OUTPUT" echo "operator=$(echo "$CHANGED" | grep -q '^operator/' && echo true || echo false)" >> "$GITHUB_OUTPUT" check: @@ -44,33 +42,16 @@ jobs: components: clippy - uses: Swatinem/rust-cache@v2 - name: cargo check - run: cargo check + run: cargo check --workspace - name: cargo clippy - run: cargo clippy -- -D warnings + run: cargo clippy --workspace -- -D warnings + - name: cargo clippy (unified) + run: cargo clippy --workspace --features unified -- -D warnings - name: cargo test - run: cargo test + run: cargo test --workspace - gateway: - needs: changes - if: needs.changes.outputs.gateway == 'true' - runs-on: ubuntu-latest - defaults: - run: - working-directory: gateway - steps: - - uses: actions/checkout@v6 - - uses: dtolnay/rust-toolchain@stable - with: - components: clippy - - uses: Swatinem/rust-cache@v2 - with: - workspaces: gateway - - name: cargo check - run: cargo check - - name: cargo clippy - run: cargo clippy -- -D warnings - - name: cargo test - run: cargo test + # gateway tests are now covered by `cargo test --workspace` in the check job above + # (openab-gateway is a workspace member in crates/openab-gateway/) operator: needs: changes diff --git a/.github/workflows/docker-smoke-test.yml b/.github/workflows/docker-smoke-test.yml index 4f3e084d8..df340d276 100644 --- a/.github/workflows/docker-smoke-test.yml +++ b/.github/workflows/docker-smoke-test.yml @@ -5,6 +5,7 @@ on: paths: - 'Dockerfile*' - 'src/**' + - 'crates/**' - 'Cargo.*' jobs: @@ -13,25 +14,37 @@ jobs: fail-fast: false matrix: variant: - - { dockerfile: Dockerfile, suffix: "", agent: "kiro-cli", agent_args: "acp --trust-all-tools" } - - { dockerfile: Dockerfile.claude, suffix: "-claude", agent: "claude-agent-acp", agent_args: "" } - - { dockerfile: Dockerfile.codex, suffix: "-codex", agent: "codex-acp", agent_args: "" } - - { dockerfile: Dockerfile.gemini, suffix: "-gemini", agent: "gemini", agent_args: "--acp" } - - { dockerfile: Dockerfile.copilot, suffix: "-copilot", agent: "copilot", agent_args: "--acp" } - - { dockerfile: Dockerfile.opencode, suffix: "-opencode", agent: "opencode", agent_args: "acp" } - - { dockerfile: Dockerfile.cursor, suffix: "-cursor", agent: "cursor-agent", agent_args: "acp" } - - { dockerfile: Dockerfile.mimocode, suffix: "-mimocode", agent: "mimo", agent_args: "acp" } - - { dockerfile: Dockerfile.hermes, suffix: "-hermes", agent: "hermes-acp", agent_args: "" } - - { dockerfile: Dockerfile.grok, suffix: "-grok", agent: "grok", agent_args: "agent stdio" } - - { dockerfile: Dockerfile.antigravity, suffix: "-antigravity", agent: "agy-acp", agent_args: "" } - - { dockerfile: Dockerfile.pi, suffix: "-pi", agent: "pi-acp", agent_args: "" } - - { dockerfile: openshell/Dockerfile, suffix: "-native-sandbox", agent: "openab-agent", agent_args: "" } + - { dockerfile: Dockerfile, suffix: "", agent: "kiro-cli", agent_args: "acp --trust-all-tools", build_args: "" } + - { dockerfile: Dockerfile, suffix: "-unified", agent: "kiro-cli", agent_args: "acp --trust-all-tools", build_args: "--build-arg BUILD_MODE=unified" } + - { dockerfile: Dockerfile.claude, suffix: "-claude", agent: "claude-agent-acp", agent_args: "", build_args: "" } + - { dockerfile: Dockerfile.claude, suffix: "-unified-claude", agent: "claude-agent-acp", agent_args: "", build_args: "--build-arg FEATURES=unified" } + - { dockerfile: Dockerfile.codex, suffix: "-codex", agent: "codex-acp", agent_args: "", build_args: "" } + - { dockerfile: Dockerfile.codex, suffix: "-unified-codex", agent: "codex-acp", agent_args: "", build_args: "--build-arg FEATURES=unified" } + - { dockerfile: Dockerfile.gemini, suffix: "-gemini", agent: "gemini", agent_args: "--acp", build_args: "" } + - { dockerfile: Dockerfile.gemini, suffix: "-unified-gemini", agent: "gemini", agent_args: "--acp", build_args: "--build-arg FEATURES=unified" } + - { dockerfile: Dockerfile.copilot, suffix: "-copilot", agent: "copilot", agent_args: "--acp", build_args: "" } + - { dockerfile: Dockerfile.copilot, suffix: "-unified-copilot", agent: "copilot", agent_args: "--acp", build_args: "--build-arg FEATURES=unified" } + - { dockerfile: Dockerfile.opencode, suffix: "-opencode", agent: "opencode", agent_args: "acp", build_args: "" } + - { dockerfile: Dockerfile.opencode, suffix: "-unified-opencode", agent: "opencode", agent_args: "acp", build_args: "--build-arg FEATURES=unified" } + - { dockerfile: Dockerfile.cursor, suffix: "-cursor", agent: "cursor-agent", agent_args: "acp", build_args: "" } + - { dockerfile: Dockerfile.cursor, suffix: "-unified-cursor", agent: "cursor-agent", agent_args: "acp", build_args: "--build-arg FEATURES=unified" } + - { dockerfile: Dockerfile.mimocode, suffix: "-mimocode", agent: "mimo", agent_args: "acp", build_args: "" } + - { dockerfile: Dockerfile.mimocode, suffix: "-unified-mimocode", agent: "mimo", agent_args: "acp", build_args: "--build-arg FEATURES=unified" } + - { dockerfile: Dockerfile.hermes, suffix: "-hermes", agent: "hermes-acp", agent_args: "", build_args: "" } + - { dockerfile: Dockerfile.hermes, suffix: "-unified-hermes", agent: "hermes-acp", agent_args: "", build_args: "--build-arg FEATURES=unified" } + - { dockerfile: Dockerfile.grok, suffix: "-grok", agent: "grok", agent_args: "agent stdio", build_args: "" } + - { dockerfile: Dockerfile.grok, suffix: "-unified-grok", agent: "grok", agent_args: "agent stdio", build_args: "--build-arg FEATURES=unified" } + - { dockerfile: Dockerfile.antigravity, suffix: "-antigravity", agent: "agy-acp", agent_args: "", build_args: "" } + - { dockerfile: Dockerfile.antigravity, suffix: "-unified-antigravity", agent: "agy-acp", agent_args: "", build_args: "--build-arg FEATURES=unified" } + - { dockerfile: Dockerfile.pi, suffix: "-pi", agent: "pi-acp", agent_args: "", build_args: "" } + - { dockerfile: Dockerfile.pi, suffix: "-unified-pi", agent: "pi-acp", agent_args: "", build_args: "--build-arg FEATURES=unified" } + - { dockerfile: openshell/Dockerfile, suffix: "-native-sandbox", agent: "openab-agent", agent_args: "", build_args: "" } runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Build image - run: docker build -t openab-test${{ matrix.variant.suffix }} -f ${{ matrix.variant.dockerfile }} . + run: docker build -t openab-test${{ matrix.variant.suffix }} -f ${{ matrix.variant.dockerfile }} ${{ matrix.variant.build_args }} . - name: Verify openab CMD does not crash run: | diff --git a/.github/workflows/gateway-release-pr.yml b/.github/workflows/gateway-release-pr.yml index 34dbaa791..13412becf 100644 --- a/.github/workflows/gateway-release-pr.yml +++ b/.github/workflows/gateway-release-pr.yml @@ -29,7 +29,7 @@ jobs: - name: Update gateway version run: | VERSION="${{ inputs.version }}" - sed -i "s/^version = .*/version = \"${VERSION}\"/" gateway/Cargo.toml + sed -i "s/^version = .*/version = \"${VERSION}\"/" crates/openab-gateway/Cargo.toml echo "::notice::Gateway release version: ${VERSION}" - name: Create release PR diff --git a/Cargo.lock b/Cargo.lock index 9001c6e9c..fd8de76cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,17 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures 0.2.17", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -100,6 +111,16 @@ dependencies = [ "serde", ] +[[package]] +name = "assert-json-diff" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "async-trait" version = "0.1.89" @@ -119,9 +140,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "aws-config" @@ -145,7 +166,7 @@ dependencies = [ "bytes", "fastrand", "hex", - "http 1.4.0", + "http 1.4.2", "sha1", "time", "tokio", @@ -190,9 +211,9 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.7.4" +version = "1.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ed8e8c52d2dc2390ad9f15647fe663f71e9780b4262c190fbb823a32721566" +checksum = "6c9b9de216a988dd54b754a82a7660cfe14cee4f6782ae4524470972fa0ccb39" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -205,7 +226,7 @@ dependencies = [ "bytes", "bytes-utils", "fastrand", - "http 1.4.0", + "http 1.4.2", "http-body 1.0.1", "percent-encoding", "pin-project-lite", @@ -215,9 +236,9 @@ dependencies = [ [[package]] name = "aws-sdk-secretsmanager" -version = "1.107.0" +version = "1.108.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63da8ec2dca98a68d8bcba971abae5f06e2c9c0017f43097d1ff92cff96adc54" +checksum = "cd24cfd47bda71881c399a7cc4850d5a61727246163d5cd1a7c0b48ef19983cb" dependencies = [ "arc-swap", "aws-credential-types", @@ -233,16 +254,16 @@ dependencies = [ "bytes", "fastrand", "http 0.2.12", - "http 1.4.0", + "http 1.4.2", "regex-lite", "tracing", ] [[package]] name = "aws-sdk-sso" -version = "1.101.0" +version = "1.102.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b647baea49ff551960b904f905681e9b4765a6c4ea08631e89dc52d8bd3f5896" +checksum = "8c82b3ac19f1431854f7ace3a7531674633e286bfdde21976893bfee36fd493b" dependencies = [ "arc-swap", "aws-credential-types", @@ -258,16 +279,16 @@ dependencies = [ "bytes", "fastrand", "http 0.2.12", - "http 1.4.0", + "http 1.4.2", "regex-lite", "tracing", ] [[package]] name = "aws-sdk-ssooidc" -version = "1.103.0" +version = "1.104.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ae401c65ff288aa7873117fe535cd32b7b1bb0bc43751d28901a1d5f20636b9" +checksum = "321000d2b4c5519ee573f73167f612efd7329322d9b26969ad1979f0427f1913" dependencies = [ "arc-swap", "aws-credential-types", @@ -283,16 +304,16 @@ dependencies = [ "bytes", "fastrand", "http 0.2.12", - "http 1.4.0", + "http 1.4.2", "regex-lite", "tracing", ] [[package]] name = "aws-sdk-sts" -version = "1.106.0" +version = "1.107.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c80de7bb7d03e9ca8c9fd7b489f20f3948d3f3be91a7953591347d238115408" +checksum = "3d0d328ba962af23ecfa3c9f23b98d3d35e325fa218d7f13d17a6bf522f8a560" dependencies = [ "arc-swap", "aws-credential-types", @@ -309,7 +330,7 @@ dependencies = [ "aws-types", "fastrand", "http 0.2.12", - "http 1.4.0", + "http 1.4.2", "regex-lite", "tracing", ] @@ -327,9 +348,9 @@ dependencies = [ "bytes", "form_urlencoded", "hex", - "hmac", + "hmac 0.13.0", "http 0.2.12", - "http 1.4.0", + "http 1.4.2", "percent-encoding", "sha2 0.11.0", "time", @@ -359,7 +380,7 @@ dependencies = [ "bytes-utils", "futures-core", "futures-util", - "http 1.4.0", + "http 1.4.2", "http-body 1.0.1", "http-body-util", "percent-encoding", @@ -378,18 +399,18 @@ dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", "h2 0.3.27", - "h2 0.4.14", + "h2 0.4.15", "http 0.2.12", - "http 1.4.0", + "http 1.4.2", "http-body 0.4.6", "hyper 0.14.32", - "hyper 1.9.0", + "hyper 1.10.1", "hyper-rustls 0.24.2", "hyper-rustls 0.27.9", "hyper-util", "pin-project-lite", "rustls 0.21.12", - "rustls 0.23.38", + "rustls 0.23.40", "rustls-native-certs", "rustls-pki-types", "tokio", @@ -444,7 +465,7 @@ dependencies = [ "bytes", "fastrand", "http 0.2.12", - "http 1.4.0", + "http 1.4.2", "http-body 0.4.6", "http-body 1.0.1", "http-body-util", @@ -465,7 +486,7 @@ dependencies = [ "aws-smithy-types", "bytes", "http 0.2.12", - "http 1.4.0", + "http 1.4.2", "pin-project-lite", "tokio", "tracing", @@ -491,21 +512,21 @@ checksum = "7442cb268338f0eb8278140a107c046756aa01093d8ef5e99628d34ae09c94f5" dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", - "http 1.4.0", + "http 1.4.2", ] [[package]] name = "aws-smithy-types" -version = "1.4.9" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53f93074121a1be41317b9aa607143ae17900631f7f59a99f2b905d519d6783b" +checksum = "32b42fcf341259d85ca10fac9a2f6448a8ec691c6955a18e45bc3b71a85fab85" dependencies = [ "base64-simd", "bytes", "bytes-utils", "futures-core", "http 0.2.12", - "http 1.4.0", + "http 1.4.2", "http-body 0.4.6", "http-body 1.0.1", "http-body-util", @@ -544,6 +565,61 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" +dependencies = [ + "axum-core", + "base64", + "bytes", + "form_urlencoded", + "futures-util", + "http 1.4.2", + "http-body 1.0.1", + "http-body-util", + "hyper 1.10.1", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sha1", + "sync_wrapper", + "tokio", + "tokio-tungstenite 0.29.0", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http 1.4.2", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "base64" version = "0.22.1" @@ -562,9 +638,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.11.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" [[package]] name = "block-buffer" @@ -577,18 +653,27 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" dependencies = [ "hybrid-array", ] +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytemuck" @@ -610,9 +695,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" [[package]] name = "bytes-utils" @@ -624,11 +709,20 @@ dependencies = [ "either", ] +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + [[package]] name = "cc" -version = "1.2.60" +version = "1.2.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" +checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" dependencies = [ "find-msvc-tools", "jobserver", @@ -650,9 +744,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.44" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", "js-sys", @@ -672,11 +766,21 @@ dependencies = [ "phf 0.12.1", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common 0.1.7", + "inout", +] + [[package]] name = "clap" -version = "4.6.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", "clap_derive", @@ -696,9 +800,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.6.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ "heck", "proc-macro2", @@ -844,9 +948,27 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" + +[[package]] +name = "deadpool" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0be2b1d1d6ec8d846f05e137292d0b89133caf95ef33695424c09568bdd39b1b" +dependencies = [ + "deadpool-runtime", + "lazy_static", + "num_cpus", + "tokio", +] + +[[package]] +name = "deadpool-runtime" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" [[package]] name = "deranged" @@ -854,7 +976,6 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ - "powerfmt", "serde_core", ] @@ -866,6 +987,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", "crypto-common 0.1.7", + "subtle", ] [[package]] @@ -874,7 +996,7 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ - "block-buffer 0.12.0", + "block-buffer 0.12.1", "const-oid", "crypto-common 0.2.2", "ctutils", @@ -882,9 +1004,9 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", @@ -956,12 +1078,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - [[package]] name = "form_urlencoded" version = "1.2.2" @@ -985,6 +1101,7 @@ checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", + "futures-executor", "futures-io", "futures-sink", "futures-task", @@ -1007,6 +1124,17 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + [[package]] name = "futures-io" version = "0.3.32" @@ -1092,15 +1220,13 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", - "wasip2", - "wasip3", ] [[package]] @@ -1134,16 +1260,16 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.4.0", + "http 1.4.2", "indexmap", "slab", "tokio", @@ -1159,18 +1285,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash", -] - -[[package]] -name = "hashbrown" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "heck" @@ -1178,12 +1295,27 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "hmac" version = "0.13.0" @@ -1206,9 +1338,9 @@ dependencies = [ [[package]] name = "http" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" dependencies = [ "bytes", "itoa", @@ -1232,7 +1364,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.4.0", + "http 1.4.2", ] [[package]] @@ -1243,7 +1375,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.4.0", + "http 1.4.2", "http-body 1.0.1", "pin-project-lite", ] @@ -1262,9 +1394,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hybrid-array" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3944cf8cf766b40e2a1a333ee5e9b563f854d5fa49d6a8ca2764e97c6eddb214" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" dependencies = [ "typenum", ] @@ -1295,18 +1427,19 @@ dependencies = [ [[package]] name = "hyper" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" dependencies = [ "atomic-waker", "bytes", "futures-channel", "futures-core", - "h2 0.4.14", - "http 1.4.0", + "h2 0.4.15", + "http 1.4.2", "http-body 1.0.1", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -1335,15 +1468,15 @@ version = "0.27.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" dependencies = [ - "http 1.4.0", - "hyper 1.9.0", + "http 1.4.2", + "hyper 1.10.1", "hyper-util", - "rustls 0.23.38", + "rustls 0.23.40", "rustls-native-certs", "tokio", "tokio-rustls 0.26.4", "tower-service", - "webpki-roots 1.0.6", + "webpki-roots 1.0.8", ] [[package]] @@ -1356,14 +1489,14 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.4.0", + "http 1.4.2", "http-body 1.0.1", - "hyper 1.9.0", + "hyper 1.10.1", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.3", + "socket2 0.6.4", "tokio", "tower-service", "tracing", @@ -1475,12 +1608,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "idna" version = "1.1.0" @@ -1494,9 +1621,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ "icu_normalizer", "icu_properties", @@ -1537,26 +1664,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.17.0", - "serde", - "serde_core", + "hashbrown 0.17.1", ] [[package]] -name = "ipnet" -version = "2.12.0" +name = "inout" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "block-padding", + "generic-array", +] [[package]] -name = "iri-string" -version = "0.7.12" +name = "ipnet" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" -dependencies = [ - "memchr", - "serde", -] +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" [[package]] name = "is_terminal_polyfill" @@ -1564,6 +1689,15 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" @@ -1582,33 +1716,41 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.95" +version = "0.3.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" +checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" dependencies = [ "cfg-if", "futures-util", - "once_cell", "wasm-bindgen", ] [[package]] -name = "lazy_static" -version = "1.5.0" +name = "jsonwebtoken" +version = "9.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" +dependencies = [ + "base64", + "js-sys", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] [[package]] -name = "leb128fmt" -version = "0.1.0" +name = "lazy_static" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.185" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "linux-raw-sys" @@ -1633,9 +1775,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" [[package]] name = "lru-slab" @@ -1652,11 +1794,17 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" [[package]] name = "mime" @@ -1686,9 +1834,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", "wasi", @@ -1714,11 +1862,21 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-conv" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-integer" @@ -1738,6 +1896,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -1767,11 +1935,12 @@ dependencies = [ "cron", "futures-util", "hex", - "http 1.4.0", + "http 1.4.2", "image", "libc", + "openab-gateway", "pulldown-cmark", - "rand 0.8.5", + "rand 0.8.6", "regex", "reqwest", "rpassword", @@ -1783,7 +1952,7 @@ dependencies = [ "tempfile", "tokio", "tokio-rustls 0.25.0", - "tokio-tungstenite", + "tokio-tungstenite 0.21.0", "toml", "toml_edit", "tracing", @@ -1794,6 +1963,37 @@ dependencies = [ "webpki-roots 0.26.11", ] +[[package]] +name = "openab-gateway" +version = "0.5.4" +dependencies = [ + "aes", + "anyhow", + "axum", + "base64", + "cbc", + "chrono", + "futures-util", + "hmac 0.12.1", + "image", + "jsonwebtoken", + "prost", + "quick-xml", + "reqwest", + "serde", + "serde_json", + "sha1", + "sha2 0.10.9", + "subtle", + "tokio", + "tokio-tungstenite 0.21.0", + "tracing", + "tracing-subscriber", + "urlencoding", + "uuid", + "wiremock", +] + [[package]] name = "openssl-probe" version = "0.2.1" @@ -1829,6 +2029,16 @@ dependencies = [ "windows-link", ] +[[package]] +name = "pem" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +dependencies = [ + "base64", + "serde_core", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -1861,7 +2071,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared 0.11.3", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -1945,29 +2155,42 @@ dependencies = [ ] [[package]] -name = "prettyplease" -version = "0.2.37" +name = "proc-macro2" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ - "proc-macro2", - "syn", + "unicode-ident", ] [[package]] -name = "proc-macro2" -version = "1.0.106" +name = "prost" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" dependencies = [ - "unicode-ident", + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "pulldown-cmark" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a14896dfa883796f1cb410461aef38810ea05f2b2c33c5aded3649095fdad" +checksum = "e9f068eba8e7071c5f9511831b44f32c740d5adf574e990f946ddb53db2f314e" dependencies = [ "bitflags", "memchr", @@ -1976,9 +2199,9 @@ dependencies = [ [[package]] name = "pxfm" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d" +checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f" [[package]] name = "quick-error" @@ -1986,6 +2209,15 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" +[[package]] +name = "quick-xml" +version = "0.37.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +dependencies = [ + "memchr", +] + [[package]] name = "quinn" version = "0.11.9" @@ -1998,8 +2230,8 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.23.38", - "socket2 0.6.3", + "rustls 0.23.40", + "socket2 0.6.4", "thiserror 2.0.18", "tokio", "tracing", @@ -2018,7 +2250,7 @@ dependencies = [ "rand 0.9.4", "ring", "rustc-hash", - "rustls 0.23.38", + "rustls 0.23.40", "rustls-pki-types", "slab", "thiserror 2.0.18", @@ -2036,7 +2268,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.3", + "socket2 0.6.4", "tracing", "windows-sys 0.60.2", ] @@ -2064,9 +2296,9 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -2132,9 +2364,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.3" +version = "1.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" dependencies = [ "aho-corasick", "memchr", @@ -2161,9 +2393,9 @@ checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973" [[package]] name = "regex-syntax" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "reqwest" @@ -2176,10 +2408,10 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "http 1.4.0", + "http 1.4.2", "http-body 1.0.1", "http-body-util", - "hyper 1.9.0", + "hyper 1.10.1", "hyper-rustls 0.27.9", "hyper-util", "js-sys", @@ -2188,7 +2420,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.38", + "rustls 0.23.40", "rustls-pki-types", "serde", "serde_json", @@ -2205,7 +2437,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 1.0.6", + "webpki-roots 1.0.8", ] [[package]] @@ -2224,20 +2456,20 @@ dependencies = [ [[package]] name = "rpassword" -version = "7.4.0" +version = "7.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d4c8b64f049c6721ec8ccec37ddfc3d641c4a7fca57e8f2a89de509c73df39" +checksum = "2da316a15f47e3d053de9cb2c439650bd8fa4aaeb9365f2e5f27f492ff73c196" dependencies = [ "libc", "rtoolbox", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "rtoolbox" -version = "0.0.4" +version = "0.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327b72899159dfae8060c51a1f6aebe955245bcd9cc4997eed0f623caea022e4" +checksum = "50a0e551c1e27e1731aba276dbeaeac73f53c7cd34d1bda485d02bd1e0f36844" dependencies = [ "libc", "windows-sys 0.59.0", @@ -2299,15 +2531,15 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.38" +version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f9466fb2c14ea04357e91413efb882e2a6d4a406e625449bc0a5d360d53a21" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ "aws-lc-rs", "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.103.12", + "rustls-webpki 0.103.13", "subtle", "zeroize", ] @@ -2326,9 +2558,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.0" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ "web-time", "zeroize", @@ -2357,9 +2589,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.12" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8279bb85272c9f10811ae6a6c547ff594d6a7f3c6c6b02ee9726d1d0dcfcdd06" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ "aws-lc-rs", "ring", @@ -2484,9 +2716,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", @@ -2495,6 +2727,17 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + [[package]] name = "serde_spanned" version = "0.6.9" @@ -2541,7 +2784,7 @@ dependencies = [ "serde_json", "time", "tokio", - "tokio-tungstenite", + "tokio-tungstenite 0.21.0", "tracing", "typemap_rev", "url", @@ -2591,9 +2834,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.3.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "signal-hook-registry" @@ -2611,11 +2854,23 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +[[package]] +name = "simple_asn1" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d585997b0ac10be3c5ee635f1bab02d512760d14b7c468801ac8a01d9ae5f1d" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror 2.0.18", + "time", +] + [[package]] name = "siphasher" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" [[package]] name = "slab" @@ -2625,9 +2880,9 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "socket2" @@ -2641,9 +2896,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", "windows-sys 0.61.2", @@ -2669,9 +2924,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.117" +version = "2.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" dependencies = [ "proc-macro2", "quote", @@ -2705,7 +2960,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.2", + "getrandom 0.4.3", "once_cell", "rustix", "windows-sys 0.61.2", @@ -2762,12 +3017,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.47" +version = "0.3.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "711a53c2d47bbd818258c498c8dbfe186a2526c631495cfe7e078567f86b8469" dependencies = [ "deranged", - "itoa", "num-conv", "powerfmt", "serde_core", @@ -2777,15 +3031,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "71c652a3727a9cbb9a02f707f530b618ce00d0ccd762009c8c23bd191df3c17d" dependencies = [ "num-conv", "time-core", @@ -2818,9 +3072,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.52.0" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91135f59b1cbf38c91e73cf3386fca9bb77915c45ce2771460c9d92f0f3d776" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ "bytes", "libc", @@ -2828,7 +3082,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.3", + "socket2 0.6.4", "tokio-macros", "windows-sys 0.61.2", ] @@ -2871,7 +3125,7 @@ version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ - "rustls 0.23.38", + "rustls 0.23.40", "tokio", ] @@ -2887,10 +3141,22 @@ dependencies = [ "rustls-pki-types", "tokio", "tokio-rustls 0.25.0", - "tungstenite", + "tungstenite 0.21.0", "webpki-roots 0.26.11", ] +[[package]] +name = "tokio-tungstenite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f72a05e828585856dacd553fba484c242c46e391fb0e58917c942ee9202915c" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.29.0", +] + [[package]] name = "tokio-util" version = "0.7.18" @@ -2958,24 +3224,25 @@ dependencies = [ "tokio", "tower-layer", "tower-service", + "tracing", ] [[package]] name = "tower-http" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ "bitflags", "bytes", "futures-util", - "http 1.4.0", + "http 1.4.2", "http-body 1.0.1", - "iri-string", "pin-project-lite", "tower", "tower-layer", "tower-service", + "url", ] [[package]] @@ -3080,10 +3347,10 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http 1.4.0", + "http 1.4.2", "httparse", "log", - "rand 0.8.5", + "rand 0.8.6", "rustls 0.22.4", "rustls-pki-types", "sha1", @@ -3092,6 +3359,22 @@ dependencies = [ "utf-8", ] +[[package]] +name = "tungstenite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8" +dependencies = [ + "bytes", + "data-encoding", + "http 1.4.2", + "httparse", + "log", + "rand 0.9.4", + "sha1", + "thiserror 2.0.18", +] + [[package]] name = "typemap_rev" version = "0.3.0" @@ -3100,9 +3383,9 @@ checksum = "74b08b0c1257381af16a5c3605254d529d3e7e109f3c62befc5d168968192998" [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "unicase" @@ -3122,12 +3405,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - [[package]] name = "untrusted" version = "0.9.0" @@ -3173,11 +3450,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.0" +version = "1.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" +checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" dependencies = [ - "getrandom 0.4.2", + "getrandom 0.4.3", "js-sys", "wasm-bindgen", ] @@ -3217,27 +3494,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.118" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" +checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" dependencies = [ "cfg-if", "once_cell", @@ -3248,9 +3516,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.68" +version = "0.4.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8" +checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280" dependencies = [ "js-sys", "wasm-bindgen", @@ -3258,9 +3526,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.118" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" +checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3268,9 +3536,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.118" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" +checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" dependencies = [ "bumpalo", "proc-macro2", @@ -3281,35 +3549,13 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.118" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" +checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - [[package]] name = "wasm-streams" version = "0.4.2" @@ -3323,23 +3569,11 @@ dependencies = [ "web-sys", ] -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - [[package]] name = "web-sys" -version = "0.3.95" +version = "0.3.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d" +checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d" dependencies = [ "js-sys", "wasm-bindgen", @@ -3361,14 +3595,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.6", + "webpki-roots 1.0.8", ] [[package]] name = "webpki-roots" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" dependencies = [ "rustls-pki-types", ] @@ -3613,92 +3847,33 @@ dependencies = [ ] [[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" +name = "wiremock" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +checksum = "08db1edfb05d9b3c1542e521aea074442088292f00b5f28e435c714a98f85031" dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", + "assert-json-diff", + "base64", + "deadpool", + "futures", + "http 1.4.2", + "http-body-util", + "hyper 1.10.1", + "hyper-util", "log", + "once_cell", + "regex", "serde", - "serde_derive", "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", + "tokio", + "url", ] [[package]] -name = "wit-parser" -version = "0.244.0" +name = "wit-bindgen" +version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" [[package]] name = "writeable" @@ -3714,9 +3889,9 @@ checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" [[package]] name = "yoke" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -3737,18 +3912,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.48" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.48" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" dependencies = [ "proc-macro2", "quote", @@ -3757,9 +3932,9 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] @@ -3778,9 +3953,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.8.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" [[package]] name = "zerotrie" diff --git a/Cargo.toml b/Cargo.toml index 162df3490..5bdbdd60f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,6 @@ +[workspace] +members = ["crates/openab-gateway"] + [package] name = "openab" version = "0.8.5" @@ -12,7 +15,7 @@ toml = "0.8" toml_edit = "0.22" tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["json", "env-filter"] } -serenity = { version = "0.12", default-features = false, features = ["client", "gateway", "model", "rustls_backend", "cache"] } +serenity = { version = "0.12", default-features = false, features = ["client", "gateway", "model", "rustls_backend", "cache"], optional = true } uuid = { version = "1", features = ["v4"] } regex = "1" anyhow = "1" @@ -43,10 +46,30 @@ urlencoding = { version = "2", optional = true } hex = { version = "0.4", optional = true } http = { version = "1", optional = true } +# Gateway crate (opt-in for unified binary) +openab-gateway = { path = "crates/openab-gateway", default-features = false, optional = true } + [features] -default = ["secrets-aws", "agentcore"] +default = ["discord", "slack", "secrets-aws", "agentcore"] +discord = ["dep:serenity"] +slack = [] secrets-aws = ["dep:aws-sdk-secretsmanager", "dep:aws-config"] agentcore = ["dep:aws-config", "dep:aws-sigv4", "dep:aws-credential-types", "dep:urlencoding", "dep:hex", "dep:http", "dep:rustls", "dep:tokio-rustls", "dep:webpki-roots"] +# Opt-in: compile all gateway adapters into a single unified binary +unified = ["telegram", "line", "feishu", "googlechat", "wecom", "teams"] + +# Gateway adapters (each pulls in the gateway crate). +# These are meant to be used WITH default features (e.g. `--features telegram`) +# or via the `unified` shortcut. Using `--no-default-features --features telegram` +# gives you ONLY the gateway adapter without Discord/Slack — intentional for +# single-adapter deployments but not the common case. +telegram = ["dep:openab-gateway", "openab-gateway/telegram"] +line = ["dep:openab-gateway", "openab-gateway/line"] +feishu = ["dep:openab-gateway", "openab-gateway/feishu"] +googlechat = ["dep:openab-gateway", "openab-gateway/googlechat"] +wecom = ["dep:openab-gateway", "openab-gateway/wecom"] +teams = ["dep:openab-gateway", "openab-gateway/teams"] + [target.'cfg(unix)'.dependencies] libc = "0.2" diff --git a/Dockerfile b/Dockerfile index cdfde7414..cee571bb7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,30 @@ # --- Build stage --- +ARG BUILD_MODE=default +ARG FEATURES="" + FROM rust:1-bookworm AS builder +ARG BUILD_MODE +ARG FEATURES + WORKDIR /build COPY Cargo.toml Cargo.lock ./ -RUN mkdir src && echo 'fn main() {}' > src/main.rs && cargo build --release && rm -rf src +COPY crates/openab-gateway/Cargo.toml crates/openab-gateway/Cargo.toml +RUN mkdir -p src crates/openab-gateway/src \ + && echo 'fn main() {}' > src/main.rs \ + && echo '' > crates/openab-gateway/src/lib.rs \ + && echo 'fn main() {}' > crates/openab-gateway/src/main.rs \ + && cargo build --release \ + && rm -rf src crates/openab-gateway/src +COPY crates/ crates/ COPY src/ src/ -RUN touch src/main.rs && cargo build --release +RUN touch src/main.rs crates/openab-gateway/src/lib.rs crates/openab-gateway/src/main.rs && \ + if [ "$BUILD_MODE" = "unified" ]; then \ + cargo build --release --features unified; \ + elif [ -n "$FEATURES" ]; then \ + cargo build --release --no-default-features --features "$FEATURES"; \ + else \ + cargo build --release; \ + fi # --- Runtime stage --- FROM debian:bookworm-slim diff --git a/Dockerfile.agentcore b/Dockerfile.agentcore index 815d0ce88..8d4f265f6 100644 --- a/Dockerfile.agentcore +++ b/Dockerfile.agentcore @@ -3,12 +3,26 @@ # Result: ~20MB image — single Rust binary, no Python/pip needed. # --- Build stage --- +ARG FEATURES="" FROM rust:1-bookworm AS builder +ARG FEATURES WORKDIR /build COPY Cargo.toml Cargo.lock ./ -RUN mkdir src && echo 'fn main() {}' > src/main.rs && cargo build --release --features agentcore && rm -rf src +COPY crates/openab-gateway/Cargo.toml crates/openab-gateway/Cargo.toml +RUN mkdir -p src crates/openab-gateway/src \ + && echo 'fn main() {}' > src/main.rs \ + && echo '' > crates/openab-gateway/src/lib.rs \ + && echo 'fn main() {}' > crates/openab-gateway/src/main.rs \ + && cargo build --release --features agentcore \ + && rm -rf src crates/openab-gateway/src +COPY crates/ crates/ COPY src/ src/ -RUN touch src/main.rs && cargo build --release --features agentcore +RUN touch src/main.rs crates/openab-gateway/src/lib.rs crates/openab-gateway/src/main.rs && \ + if [ -n "$FEATURES" ]; then \ + cargo build --release --features "agentcore,$FEATURES"; \ + else \ + cargo build --release --features agentcore; \ + fi # --- Runtime stage --- FROM debian:bookworm-slim diff --git a/Dockerfile.antigravity b/Dockerfile.antigravity index bc16c1e7c..75e00bf74 100644 --- a/Dockerfile.antigravity +++ b/Dockerfile.antigravity @@ -1,10 +1,24 @@ # --- Build openab --- +ARG FEATURES="" FROM rust:1-bookworm AS builder +ARG FEATURES WORKDIR /build COPY Cargo.toml Cargo.lock ./ -RUN mkdir src && echo 'fn main() {}' > src/main.rs && cargo build --release && rm -rf src +COPY crates/openab-gateway/Cargo.toml crates/openab-gateway/Cargo.toml +RUN mkdir -p src crates/openab-gateway/src \ + && echo 'fn main() {}' > src/main.rs \ + && echo '' > crates/openab-gateway/src/lib.rs \ + && echo 'fn main() {}' > crates/openab-gateway/src/main.rs \ + && cargo build --release \ + && rm -rf src crates/openab-gateway/src +COPY crates/ crates/ COPY src/ src/ -RUN touch src/main.rs && cargo build --release +RUN touch src/main.rs crates/openab-gateway/src/lib.rs crates/openab-gateway/src/main.rs && \ + if [ -n "$FEATURES" ]; then \ + cargo build --release --features "$FEATURES"; \ + else \ + cargo build --release; \ + fi # --- Build agy-acp adapter --- FROM rust:1-bookworm AS adapter-builder diff --git a/Dockerfile.claude b/Dockerfile.claude index c6657252d..52393d064 100644 --- a/Dockerfile.claude +++ b/Dockerfile.claude @@ -1,10 +1,24 @@ # --- Build stage --- +ARG FEATURES="" FROM rust:1-bookworm AS builder +ARG FEATURES WORKDIR /build COPY Cargo.toml Cargo.lock ./ -RUN mkdir src && echo 'fn main() {}' > src/main.rs && cargo build --release && rm -rf src +COPY crates/openab-gateway/Cargo.toml crates/openab-gateway/Cargo.toml +RUN mkdir -p src crates/openab-gateway/src \ + && echo 'fn main() {}' > src/main.rs \ + && echo '' > crates/openab-gateway/src/lib.rs \ + && echo 'fn main() {}' > crates/openab-gateway/src/main.rs \ + && cargo build --release \ + && rm -rf src crates/openab-gateway/src +COPY crates/ crates/ COPY src/ src/ -RUN touch src/main.rs && cargo build --release +RUN touch src/main.rs crates/openab-gateway/src/lib.rs crates/openab-gateway/src/main.rs && \ + if [ -n "$FEATURES" ]; then \ + cargo build --release --features "$FEATURES"; \ + else \ + cargo build --release; \ + fi # --- Runtime stage --- FROM node:22-bookworm-slim diff --git a/Dockerfile.codex b/Dockerfile.codex index f6e2f2af6..9e516b047 100644 --- a/Dockerfile.codex +++ b/Dockerfile.codex @@ -1,10 +1,24 @@ # --- Build stage --- +ARG FEATURES="" FROM rust:1-bookworm AS builder +ARG FEATURES WORKDIR /build COPY Cargo.toml Cargo.lock ./ -RUN mkdir src && echo 'fn main() {}' > src/main.rs && cargo build --release && rm -rf src +COPY crates/openab-gateway/Cargo.toml crates/openab-gateway/Cargo.toml +RUN mkdir -p src crates/openab-gateway/src \ + && echo 'fn main() {}' > src/main.rs \ + && echo '' > crates/openab-gateway/src/lib.rs \ + && echo 'fn main() {}' > crates/openab-gateway/src/main.rs \ + && cargo build --release \ + && rm -rf src crates/openab-gateway/src +COPY crates/ crates/ COPY src/ src/ -RUN touch src/main.rs && cargo build --release +RUN touch src/main.rs crates/openab-gateway/src/lib.rs crates/openab-gateway/src/main.rs && \ + if [ -n "$FEATURES" ]; then \ + cargo build --release --features "$FEATURES"; \ + else \ + cargo build --release; \ + fi # --- Runtime stage --- FROM node:22-bookworm-slim diff --git a/Dockerfile.copilot b/Dockerfile.copilot index 23e903898..36e53d3d5 100644 --- a/Dockerfile.copilot +++ b/Dockerfile.copilot @@ -1,10 +1,24 @@ # --- Build stage --- +ARG FEATURES="" FROM rust:1-bookworm AS builder +ARG FEATURES WORKDIR /build COPY Cargo.toml Cargo.lock ./ -RUN mkdir src && echo 'fn main() {}' > src/main.rs && cargo build --release && rm -rf src +COPY crates/openab-gateway/Cargo.toml crates/openab-gateway/Cargo.toml +RUN mkdir -p src crates/openab-gateway/src \ + && echo 'fn main() {}' > src/main.rs \ + && echo '' > crates/openab-gateway/src/lib.rs \ + && echo 'fn main() {}' > crates/openab-gateway/src/main.rs \ + && cargo build --release \ + && rm -rf src crates/openab-gateway/src +COPY crates/ crates/ COPY src/ src/ -RUN touch src/main.rs && cargo build --release +RUN touch src/main.rs crates/openab-gateway/src/lib.rs crates/openab-gateway/src/main.rs && \ + if [ -n "$FEATURES" ]; then \ + cargo build --release --features "$FEATURES"; \ + else \ + cargo build --release; \ + fi # --- Runtime stage --- FROM node:22-bookworm-slim diff --git a/Dockerfile.cursor b/Dockerfile.cursor index 2c408b3b6..d22463590 100644 --- a/Dockerfile.cursor +++ b/Dockerfile.cursor @@ -1,10 +1,24 @@ # --- Build stage --- +ARG FEATURES="" FROM rust:1-bookworm AS builder +ARG FEATURES WORKDIR /build COPY Cargo.toml Cargo.lock ./ -RUN mkdir src && echo 'fn main() {}' > src/main.rs && cargo build --release && rm -rf src +COPY crates/openab-gateway/Cargo.toml crates/openab-gateway/Cargo.toml +RUN mkdir -p src crates/openab-gateway/src \ + && echo 'fn main() {}' > src/main.rs \ + && echo '' > crates/openab-gateway/src/lib.rs \ + && echo 'fn main() {}' > crates/openab-gateway/src/main.rs \ + && cargo build --release \ + && rm -rf src crates/openab-gateway/src +COPY crates/ crates/ COPY src/ src/ -RUN touch src/main.rs && cargo build --release +RUN touch src/main.rs crates/openab-gateway/src/lib.rs crates/openab-gateway/src/main.rs && \ + if [ -n "$FEATURES" ]; then \ + cargo build --release --features "$FEATURES"; \ + else \ + cargo build --release; \ + fi # --- Runtime stage --- FROM debian:bookworm-slim diff --git a/Dockerfile.gateway b/Dockerfile.gateway new file mode 100644 index 000000000..b7015e253 --- /dev/null +++ b/Dockerfile.gateway @@ -0,0 +1,28 @@ +# --- Build stage --- +ARG FEATURES="" +FROM rust:1-bookworm AS builder +ARG FEATURES +WORKDIR /build +COPY Cargo.toml Cargo.lock ./ +COPY crates/openab-gateway/Cargo.toml crates/openab-gateway/Cargo.toml +RUN mkdir -p src crates/openab-gateway/src \ + && echo 'fn main() {}' > src/main.rs \ + && echo '' > crates/openab-gateway/src/lib.rs \ + && echo 'fn main() {}' > crates/openab-gateway/src/main.rs \ + && cargo build --release -p openab-gateway \ + && rm -rf src crates/openab-gateway/src +COPY crates/ crates/ +COPY src/ src/ +RUN touch crates/openab-gateway/src/main.rs crates/openab-gateway/src/lib.rs && \ + if [ -n "$FEATURES" ]; then \ + cargo build --release -p openab-gateway --no-default-features --features "$FEATURES"; \ + else \ + cargo build --release -p openab-gateway; \ + fi + +# --- Runtime stage --- +FROM debian:bookworm-slim +RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates && rm -rf /var/lib/apt/lists/* +COPY --from=builder /build/target/release/openab-gateway /usr/local/bin/openab-gateway +EXPOSE 8080 +ENTRYPOINT ["openab-gateway"] diff --git a/Dockerfile.gemini b/Dockerfile.gemini index c7221608e..76b265ee4 100644 --- a/Dockerfile.gemini +++ b/Dockerfile.gemini @@ -1,10 +1,24 @@ # --- Build stage --- +ARG FEATURES="" FROM rust:1-bookworm AS builder +ARG FEATURES WORKDIR /build COPY Cargo.toml Cargo.lock ./ -RUN mkdir src && echo 'fn main() {}' > src/main.rs && cargo build --release && rm -rf src +COPY crates/openab-gateway/Cargo.toml crates/openab-gateway/Cargo.toml +RUN mkdir -p src crates/openab-gateway/src \ + && echo 'fn main() {}' > src/main.rs \ + && echo '' > crates/openab-gateway/src/lib.rs \ + && echo 'fn main() {}' > crates/openab-gateway/src/main.rs \ + && cargo build --release \ + && rm -rf src crates/openab-gateway/src +COPY crates/ crates/ COPY src/ src/ -RUN touch src/main.rs && cargo build --release +RUN touch src/main.rs crates/openab-gateway/src/lib.rs crates/openab-gateway/src/main.rs && \ + if [ -n "$FEATURES" ]; then \ + cargo build --release --features "$FEATURES"; \ + else \ + cargo build --release; \ + fi # --- Runtime stage --- FROM node:22-bookworm-slim diff --git a/Dockerfile.grok b/Dockerfile.grok index bdd8a6c1a..18d72c7f1 100644 --- a/Dockerfile.grok +++ b/Dockerfile.grok @@ -1,10 +1,24 @@ # --- Build stage --- +ARG FEATURES="" FROM rust:1-bookworm AS builder +ARG FEATURES WORKDIR /build COPY Cargo.toml Cargo.lock ./ -RUN mkdir src && echo 'fn main() {}' > src/main.rs && cargo build --release && rm -rf src +COPY crates/openab-gateway/Cargo.toml crates/openab-gateway/Cargo.toml +RUN mkdir -p src crates/openab-gateway/src \ + && echo 'fn main() {}' > src/main.rs \ + && echo '' > crates/openab-gateway/src/lib.rs \ + && echo 'fn main() {}' > crates/openab-gateway/src/main.rs \ + && cargo build --release \ + && rm -rf src crates/openab-gateway/src +COPY crates/ crates/ COPY src/ src/ -RUN touch src/main.rs && cargo build --release +RUN touch src/main.rs crates/openab-gateway/src/lib.rs crates/openab-gateway/src/main.rs && \ + if [ -n "$FEATURES" ]; then \ + cargo build --release --features "$FEATURES"; \ + else \ + cargo build --release; \ + fi # --- Runtime stage --- FROM debian:bookworm-slim diff --git a/Dockerfile.hermes b/Dockerfile.hermes index 66408f902..d664e833a 100644 --- a/Dockerfile.hermes +++ b/Dockerfile.hermes @@ -1,10 +1,24 @@ # --- Build stage --- +ARG FEATURES="" FROM rust:1-bookworm AS builder +ARG FEATURES WORKDIR /build COPY Cargo.toml Cargo.lock ./ -RUN mkdir src && echo 'fn main() {}' > src/main.rs && cargo build --release && rm -rf src +COPY crates/openab-gateway/Cargo.toml crates/openab-gateway/Cargo.toml +RUN mkdir -p src crates/openab-gateway/src \ + && echo 'fn main() {}' > src/main.rs \ + && echo '' > crates/openab-gateway/src/lib.rs \ + && echo 'fn main() {}' > crates/openab-gateway/src/main.rs \ + && cargo build --release \ + && rm -rf src crates/openab-gateway/src +COPY crates/ crates/ COPY src/ src/ -RUN touch src/main.rs && cargo build --release +RUN touch src/main.rs crates/openab-gateway/src/lib.rs crates/openab-gateway/src/main.rs && \ + if [ -n "$FEATURES" ]; then \ + cargo build --release --features "$FEATURES"; \ + else \ + cargo build --release; \ + fi # --- Runtime stage --- FROM python:3.12-slim-bookworm diff --git a/Dockerfile.mimocode b/Dockerfile.mimocode index 9aadc1de0..7e8f28468 100644 --- a/Dockerfile.mimocode +++ b/Dockerfile.mimocode @@ -1,10 +1,24 @@ # --- Build stage --- +ARG FEATURES="" FROM rust:1-bookworm AS builder +ARG FEATURES WORKDIR /build COPY Cargo.toml Cargo.lock ./ -RUN mkdir src && echo 'fn main() {}' > src/main.rs && cargo build --release && rm -rf src +COPY crates/openab-gateway/Cargo.toml crates/openab-gateway/Cargo.toml +RUN mkdir -p src crates/openab-gateway/src \ + && echo 'fn main() {}' > src/main.rs \ + && echo '' > crates/openab-gateway/src/lib.rs \ + && echo 'fn main() {}' > crates/openab-gateway/src/main.rs \ + && cargo build --release \ + && rm -rf src crates/openab-gateway/src +COPY crates/ crates/ COPY src/ src/ -RUN touch src/main.rs && cargo build --release +RUN touch src/main.rs crates/openab-gateway/src/lib.rs crates/openab-gateway/src/main.rs && \ + if [ -n "$FEATURES" ]; then \ + cargo build --release --features "$FEATURES"; \ + else \ + cargo build --release; \ + fi # --- Runtime stage --- # MiMo-Code (https://github.com/XiaomiMiMo/MiMo-Code) is a fork of OpenCode diff --git a/Dockerfile.native b/Dockerfile.native index 058b91c76..2671ca584 100644 --- a/Dockerfile.native +++ b/Dockerfile.native @@ -1,11 +1,25 @@ # --- Build stage --- +ARG FEATURES="" FROM rust:1-bookworm AS builder +ARG FEATURES WORKDIR /build COPY Cargo.toml Cargo.lock ./ +COPY crates/openab-gateway/Cargo.toml crates/openab-gateway/Cargo.toml COPY openab-agent/ openab-agent/ -RUN mkdir src && echo 'fn main() {}' > src/main.rs && cargo build --release && rm -rf src +RUN mkdir -p src crates/openab-gateway/src \ + && echo 'fn main() {}' > src/main.rs \ + && echo '' > crates/openab-gateway/src/lib.rs \ + && echo 'fn main() {}' > crates/openab-gateway/src/main.rs \ + && cargo build --release \ + && rm -rf src crates/openab-gateway/src +COPY crates/ crates/ COPY src/ src/ -RUN touch src/main.rs && cargo build --release +RUN touch src/main.rs crates/openab-gateway/src/lib.rs crates/openab-gateway/src/main.rs && \ + if [ -n "$FEATURES" ]; then \ + cargo build --release --features "$FEATURES"; \ + else \ + cargo build --release; \ + fi RUN cd openab-agent && cargo build --release # --- Runtime stage --- diff --git a/Dockerfile.opencode b/Dockerfile.opencode index 69d8a8909..8dd243f01 100644 --- a/Dockerfile.opencode +++ b/Dockerfile.opencode @@ -1,10 +1,24 @@ # --- Build stage --- +ARG FEATURES="" FROM rust:1-bookworm AS builder +ARG FEATURES WORKDIR /build COPY Cargo.toml Cargo.lock ./ -RUN mkdir src && echo 'fn main() {}' > src/main.rs && cargo build --release && rm -rf src +COPY crates/openab-gateway/Cargo.toml crates/openab-gateway/Cargo.toml +RUN mkdir -p src crates/openab-gateway/src \ + && echo 'fn main() {}' > src/main.rs \ + && echo '' > crates/openab-gateway/src/lib.rs \ + && echo 'fn main() {}' > crates/openab-gateway/src/main.rs \ + && cargo build --release \ + && rm -rf src crates/openab-gateway/src +COPY crates/ crates/ COPY src/ src/ -RUN touch src/main.rs && cargo build --release +RUN touch src/main.rs crates/openab-gateway/src/lib.rs crates/openab-gateway/src/main.rs && \ + if [ -n "$FEATURES" ]; then \ + cargo build --release --features "$FEATURES"; \ + else \ + cargo build --release; \ + fi # --- Runtime stage --- # node:22-bookworm-slim mirrors the base image used by Dockerfile.claude, diff --git a/Dockerfile.pi b/Dockerfile.pi index f87fc7e8b..7f0ec87f5 100644 --- a/Dockerfile.pi +++ b/Dockerfile.pi @@ -1,10 +1,24 @@ # --- Build stage --- +ARG FEATURES="" FROM rust:1-bookworm AS builder +ARG FEATURES WORKDIR /build COPY Cargo.toml Cargo.lock ./ -RUN mkdir src && echo 'fn main() {}' > src/main.rs && cargo build --release && rm -rf src +COPY crates/openab-gateway/Cargo.toml crates/openab-gateway/Cargo.toml +RUN mkdir -p src crates/openab-gateway/src \ + && echo 'fn main() {}' > src/main.rs \ + && echo '' > crates/openab-gateway/src/lib.rs \ + && echo 'fn main() {}' > crates/openab-gateway/src/main.rs \ + && cargo build --release \ + && rm -rf src crates/openab-gateway/src +COPY crates/ crates/ COPY src/ src/ -RUN touch src/main.rs && cargo build --release +RUN touch src/main.rs crates/openab-gateway/src/lib.rs crates/openab-gateway/src/main.rs && \ + if [ -n "$FEATURES" ]; then \ + cargo build --release --features "$FEATURES"; \ + else \ + cargo build --release; \ + fi # --- Runtime stage --- FROM node:22-bookworm-slim diff --git a/gateway/Cargo.toml b/crates/openab-gateway/Cargo.toml similarity index 80% rename from gateway/Cargo.toml rename to crates/openab-gateway/Cargo.toml index e07dffceb..de8921166 100644 --- a/gateway/Cargo.toml +++ b/crates/openab-gateway/Cargo.toml @@ -2,6 +2,11 @@ name = "openab-gateway" version = "0.5.4" edition = "2021" +license = "MIT" + +[[bin]] +name = "openab-gateway" +path = "src/main.rs" [dependencies] tokio = { version = "1", features = ["full"] } @@ -31,3 +36,12 @@ urlencoding = "2" [dev-dependencies] wiremock = "0.6" + +[features] +default = ["telegram", "line", "feishu", "googlechat", "wecom", "teams"] +telegram = [] +line = [] +feishu = [] +googlechat = [] +wecom = [] +teams = [] diff --git a/crates/openab-gateway/README.md b/crates/openab-gateway/README.md new file mode 100644 index 000000000..8fd4465ea --- /dev/null +++ b/crates/openab-gateway/README.md @@ -0,0 +1,142 @@ +# OpenAB Gateway + +A standalone service that bridges webhook-based platforms and custom event sources to OAB via WebSocket. OAB connects outbound to the gateway — no inbound ports or TLS required on OAB. + +``` + External (HTTPS) Internal (cluster) + ──────────────── ────────────────── + +Telegram ──POST──▶┌─────────────────────┐ +LINE ──POST──▶│ │ +Feishu ──POST──▶│ OpenAB Gateway │◀──WebSocket── OAB Pod +Google ──POST──▶│ :8080 │ (OAB connects out) +WeCom ──POST──▶│ │ +Teams ──POST──▶│ │ + └─────────────────────┘ + +Discord ◀──WebSocket── OAB Pod (unchanged, direct) +Slack ◀──WebSocket── OAB Pod (unchanged, direct) +``` + +The gateway normalizes all inbound events to a unified schema (`openab.gateway.event.v1`), forwards them to OAB over WebSocket, and routes OAB replies back to the originating platform API. + +For architecture details, see [ADR: Custom Gateway](../../docs/adr/custom-gateway.md). + +--- + +## Build Modes + +This crate can run as a **standalone binary** or be compiled into the **unified OAB binary**. + +### Standalone + +```bash +cargo build --release -p openab-gateway +``` + +### Unified (all adapters in one binary) + +```bash +cargo build --release --features unified +``` + +### Single adapter only + +```bash +cargo build --release --no-default-features --features telegram +``` + +### Docker + +```bash +# Standalone gateway +docker build -f Dockerfile.gateway -t openab-gateway . + +# Unified binary +docker build --build-arg BUILD_MODE=unified -t openab:unified . + +# Custom adapter selection +docker build --build-arg FEATURES=telegram,line -t openab:custom . +``` + +--- + +## Environment Variables + +| Variable | Default | Description | +|---|---|---| +| `GATEWAY_LISTEN` | `0.0.0.0:8080` | Listen address | +| `GATEWAY_WS_TOKEN` | (optional) | Token for WebSocket authentication | +| `TELEGRAM_BOT_TOKEN` | (optional) | Telegram Bot API token | +| `TELEGRAM_SECRET_TOKEN` | (optional) | Webhook secret for request validation | +| `TELEGRAM_WEBHOOK_PATH` | `/webhook/telegram` | Webhook endpoint path | +| `TELEGRAM_RICH_MESSAGES` | `true` | Enable Markdown formatting | +| `LINE_CHANNEL_SECRET` | (optional) | LINE channel secret for HMAC verification | +| `LINE_CHANNEL_ACCESS_TOKEN` | (optional) | LINE channel access token | +| `FEISHU_APP_ID` | (optional) | Feishu/Lark App ID | +| `FEISHU_APP_SECRET` | (optional) | Feishu/Lark App Secret | +| `FEISHU_DOMAIN` | `feishu` | `feishu` (China) or `lark` (international) | +| `FEISHU_CONNECTION_MODE` | `websocket` | `websocket` or `webhook` | +| `FEISHU_WEBHOOK_PATH` | `/webhook/feishu` | Webhook endpoint path | +| `GOOGLE_CHAT_ENABLED` | `false` | Set to `true` to enable Google Chat | +| `GOOGLE_CHAT_AUDIENCE` | (optional) | JWT audience for webhook verification | +| `GOOGLE_CHAT_SA_KEY_JSON` | (optional) | Service account key JSON | +| `GOOGLE_CHAT_WEBHOOK_PATH` | `/webhook/googlechat` | Webhook endpoint path | +| `WECOM_CORP_ID` | (required*) | WeCom Corp ID | +| `WECOM_AGENT_ID` | (required*) | WeCom App Agent ID | +| `WECOM_SECRET` | (required*) | WeCom App Secret | +| `WECOM_TOKEN` | (required*) | Callback verification Token | +| `WECOM_ENCODING_AES_KEY` | (required*) | Callback EncodingAESKey | +| `WECOM_WEBHOOK_PATH` | `/webhook/wecom` | Webhook endpoint path | +| `TEAMS_APP_ID` | (optional) | Microsoft Teams App ID | +| `TEAMS_APP_PASSWORD` | (optional) | Microsoft Teams App Password | +| `TEAMS_WEBHOOK_PATH` | `/webhook/teams` | Webhook endpoint path | + +--- + +## Endpoints + +| Path | Description | +|---|---| +| `GET /ws` | WebSocket server (OAB connects here) | +| `GET /health` | Health check | +| `POST /webhook/telegram` | Telegram webhook receiver | +| `POST /webhook/line` | LINE webhook receiver | +| `POST /webhook/feishu` | Feishu webhook receiver | +| `POST /webhook/googlechat` | Google Chat webhook receiver | +| `GET /webhook/wecom` | WeCom callback URL verification | +| `POST /webhook/wecom` | WeCom message callback receiver | +| `POST /webhook/teams` | Microsoft Teams webhook receiver | + +--- + +## OAB Config + +```toml +[gateway] +url = "ws://gateway:8080/ws" +``` + +--- + +## Platform Setup + +- [Telegram](../../docs/telegram.md) +- [LINE](../../docs/line.md) +- [Feishu/Lark](../../docs/feishu.md) +- [Google Chat](../../docs/google-chat.md) +- [WeCom](../../docs/wecom.md) + +--- + +## Feature Flags + +| Flag | Effect | +|------|--------| +| `default` | All adapters enabled | +| `telegram` | Telegram adapter only | +| `line` | LINE adapter only | +| `feishu` | Feishu/Lark adapter only | +| `googlechat` | Google Chat adapter only | +| `wecom` | WeCom adapter only | +| `teams` | Microsoft Teams adapter only | diff --git a/gateway/src/adapters/feishu.rs b/crates/openab-gateway/src/adapters/feishu.rs similarity index 100% rename from gateway/src/adapters/feishu.rs rename to crates/openab-gateway/src/adapters/feishu.rs diff --git a/gateway/src/adapters/googlechat.rs b/crates/openab-gateway/src/adapters/googlechat.rs similarity index 100% rename from gateway/src/adapters/googlechat.rs rename to crates/openab-gateway/src/adapters/googlechat.rs diff --git a/gateway/src/adapters/line.rs b/crates/openab-gateway/src/adapters/line.rs similarity index 100% rename from gateway/src/adapters/line.rs rename to crates/openab-gateway/src/adapters/line.rs diff --git a/crates/openab-gateway/src/adapters/mod.rs b/crates/openab-gateway/src/adapters/mod.rs new file mode 100644 index 000000000..cbacad507 --- /dev/null +++ b/crates/openab-gateway/src/adapters/mod.rs @@ -0,0 +1,12 @@ +#[cfg(feature = "telegram")] +pub mod telegram; +#[cfg(feature = "line")] +pub mod line; +#[cfg(feature = "feishu")] +pub mod feishu; +#[cfg(feature = "googlechat")] +pub mod googlechat; +#[cfg(feature = "wecom")] +pub mod wecom; +#[cfg(feature = "teams")] +pub mod teams; diff --git a/gateway/src/adapters/teams.rs b/crates/openab-gateway/src/adapters/teams.rs similarity index 100% rename from gateway/src/adapters/teams.rs rename to crates/openab-gateway/src/adapters/teams.rs diff --git a/gateway/src/adapters/telegram.rs b/crates/openab-gateway/src/adapters/telegram.rs similarity index 100% rename from gateway/src/adapters/telegram.rs rename to crates/openab-gateway/src/adapters/telegram.rs diff --git a/gateway/src/adapters/wecom.rs b/crates/openab-gateway/src/adapters/wecom.rs similarity index 100% rename from gateway/src/adapters/wecom.rs rename to crates/openab-gateway/src/adapters/wecom.rs diff --git a/crates/openab-gateway/src/lib.rs b/crates/openab-gateway/src/lib.rs new file mode 100644 index 000000000..91d2cda53 --- /dev/null +++ b/crates/openab-gateway/src/lib.rs @@ -0,0 +1,55 @@ +pub mod adapters; +pub(crate) mod media; +pub mod schema; +pub mod store; + +use std::collections::HashMap; +use std::sync::Arc; +use std::time::Instant; +use tokio::sync::{broadcast, Mutex, Semaphore}; + +// --- Reply token cache for LINE hybrid Reply/Push dispatch --- + +/// Cache entry for LINE reply tokens: (replyToken, insertion_time). +pub type ReplyTokenCache = Arc>>; + +/// Maximum age (in seconds) before a cached reply token is considered expired. +pub const REPLY_TOKEN_TTL_SECS: u64 = 50; + +/// Maximum number of cached reply tokens. +pub const REPLY_TOKEN_CACHE_MAX: usize = 10_000; + +/// Maximum number of post-ack LINE webhook payloads processed concurrently. +pub const LINE_WEBHOOK_CONCURRENCY_MAX: usize = 8; + +// --- App state (shared across all adapters) --- + +pub struct AppState { + #[cfg(feature = "telegram")] + pub telegram_bot_token: Option, + #[cfg(feature = "telegram")] + pub telegram_secret_token: Option, + #[cfg(feature = "telegram")] + pub telegram_rich_messages: bool, + #[cfg(feature = "line")] + pub line_channel_secret: Option, + #[cfg(feature = "line")] + pub line_access_token: Option, + #[cfg(feature = "teams")] + pub teams: Option, + #[cfg(feature = "teams")] + pub teams_service_urls: Mutex>, + #[cfg(feature = "feishu")] + pub feishu: Option, + #[cfg(feature = "googlechat")] + pub google_chat: Option, + #[cfg(feature = "wecom")] + pub wecom: Option, + pub ws_token: Option, + pub event_tx: broadcast::Sender, + #[cfg(feature = "line")] + pub reply_token_cache: ReplyTokenCache, + #[cfg(feature = "line")] + pub line_webhook_semaphore: Arc, + pub client: reqwest::Client, +} diff --git a/crates/openab-gateway/src/main.rs b/crates/openab-gateway/src/main.rs new file mode 100644 index 000000000..94e6554e1 --- /dev/null +++ b/crates/openab-gateway/src/main.rs @@ -0,0 +1,423 @@ +use anyhow::Result; +use axum::{ + extract::State, + response::IntoResponse, + routing::{get, post}, + Router, +}; +use futures_util::{SinkExt, StreamExt}; +use openab_gateway::schema::GatewayReply; +use openab_gateway::{ + adapters, AppState, ReplyTokenCache, LINE_WEBHOOK_CONCURRENCY_MAX, REPLY_TOKEN_TTL_SECS, +}; +use std::collections::HashMap; +use std::sync::Arc; +use tokio::sync::{broadcast, Mutex, Semaphore}; +use tracing::{info, warn}; + +// --- WebSocket handler (OAB connects here) --- + +async fn ws_handler( + State(state): State>, + query: axum::extract::Query>, + ws: axum::extract::WebSocketUpgrade, +) -> axum::response::Response { + if let Some(ref expected) = state.ws_token { + let provided = query.get("token").map(|s| s.as_str()); + if provided != Some(expected.as_str()) { + warn!("WebSocket rejected: invalid or missing token"); + return axum::http::StatusCode::UNAUTHORIZED.into_response(); + } + } + ws.on_upgrade(move |socket| handle_oab_connection(state, socket)) +} + +async fn handle_oab_connection(state: Arc, socket: axum::extract::ws::WebSocket) { + use axum::extract::ws::Message; + + let (mut ws_tx, mut ws_rx) = socket.split(); + let mut event_rx = state.event_tx.subscribe(); + + info!("OAB client connected via WebSocket"); + + let send_task = tokio::spawn(async move { + loop { + tokio::select! { + Ok(event_json) = event_rx.recv() => { + if ws_tx.send(Message::Text(event_json.into())).await.is_err() { + break; + } + } + } + } + }); + + let state_for_recv = state.clone(); + let reaction_state: Arc>>> = + Arc::new(Mutex::new(HashMap::new())); + let recv_task = tokio::spawn(async move { + let client = reqwest::Client::new(); + while let Some(Ok(msg)) = ws_rx.next().await { + if let Message::Text(text) = msg { + match serde_json::from_str::(&text) { + Ok(reply) => { + info!( + platform = %reply.platform, + channel = %reply.channel.id, + command = ?reply.command.as_deref(), + "OAB → gateway reply" + ); + match reply.platform.as_str() { + #[cfg(feature = "telegram")] + "telegram" => { + if let Some(ref token) = state_for_recv.telegram_bot_token { + adapters::telegram::handle_reply( + &reply, + token, + &client, + &state_for_recv.event_tx, + &reaction_state, + state_for_recv.telegram_rich_messages, + ) + .await; + } else { + warn!("reply for telegram but adapter not configured"); + } + } + #[cfg(feature = "line")] + "line" => { + if let Some(ref access_token) = state_for_recv.line_access_token { + adapters::line::dispatch_line_reply( + &client, + access_token, + &state_for_recv.reply_token_cache, + &reply, + adapters::line::LINE_API_BASE, + ) + .await; + } else { + warn!("reply for line but adapter not configured"); + } + } + #[cfg(feature = "teams")] + "teams" => { + if let Some(ref teams) = state_for_recv.teams { + adapters::teams::handle_reply( + &reply, + teams, + &state_for_recv.teams_service_urls, + ) + .await; + } else { + warn!("reply for teams but adapter not configured"); + } + } + #[cfg(feature = "feishu")] + "feishu" => { + if let Some(ref feishu) = state_for_recv.feishu { + adapters::feishu::handle_reply( + &reply, + feishu, + &state_for_recv.event_tx, + ) + .await; + } else { + warn!("reply for feishu but adapter not configured"); + } + } + #[cfg(feature = "googlechat")] + "googlechat" => { + if let Some(ref gc) = state_for_recv.google_chat { + gc.handle_reply(&reply, &state_for_recv.event_tx).await; + } else { + warn!("reply for googlechat but adapter not configured"); + } + } + #[cfg(feature = "wecom")] + "wecom" => { + if let Some(ref wecom) = state_for_recv.wecom { + wecom.handle_reply(&reply, &state_for_recv.event_tx).await; + } else { + warn!("reply for wecom but adapter not configured"); + } + } + other => warn!(platform = other, "unknown reply platform"), + } + } + Err(e) => warn!("invalid reply from OAB: {e}"), + } + } + } + }); + + tokio::select! { + _ = send_task => {}, + _ = recv_task => {}, + } + info!("OAB client disconnected"); +} + +async fn health() -> &'static str { + "ok" +} + +#[tokio::main] +async fn main() -> Result<()> { + tracing_subscriber::fmt() + .with_env_filter( + tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()), + ) + .init(); + + let listen_addr = std::env::var("GATEWAY_LISTEN").unwrap_or_else(|_| "0.0.0.0:8080".into()); + let ws_token = std::env::var("GATEWAY_WS_TOKEN").ok(); + + if ws_token.is_none() { + warn!("GATEWAY_WS_TOKEN not set — WebSocket connections are NOT authenticated (insecure)"); + } + + let (event_tx, _) = broadcast::channel::(256); + #[cfg(feature = "line")] + let reply_token_cache: ReplyTokenCache = Arc::new(std::sync::Mutex::new(HashMap::new())); + + let mut app = Router::new() + .route("/ws", get(ws_handler)) + .route("/health", get(health)); + + // Telegram adapter + #[cfg(feature = "telegram")] + let telegram_bot_token = std::env::var("TELEGRAM_BOT_TOKEN").ok(); + #[cfg(feature = "telegram")] + let telegram_secret_token = std::env::var("TELEGRAM_SECRET_TOKEN").ok(); + #[cfg(feature = "telegram")] + let telegram_rich_messages = std::env::var("TELEGRAM_RICH_MESSAGES") + .map(|v| v != "0" && !v.eq_ignore_ascii_case("false")) + .unwrap_or(true); + #[cfg(feature = "telegram")] + if telegram_bot_token.is_some() { + let webhook_path = + std::env::var("TELEGRAM_WEBHOOK_PATH").unwrap_or_else(|_| "/webhook/telegram".into()); + if telegram_secret_token.is_none() { + warn!("TELEGRAM_SECRET_TOKEN not set — webhook requests are NOT validated (insecure)"); + } + info!(path = %webhook_path, "telegram adapter enabled"); + app = app.route(&webhook_path, post(adapters::telegram::webhook)); + } + + // LINE adapter + #[cfg(feature = "line")] + let line_channel_secret = std::env::var("LINE_CHANNEL_SECRET").ok(); + #[cfg(feature = "line")] + let line_access_token = std::env::var("LINE_CHANNEL_ACCESS_TOKEN").ok(); + #[cfg(feature = "line")] + { + info!("line adapter enabled"); + app = app.route("/webhook/line", post(adapters::line::webhook)); + } + + // Teams adapter + #[cfg(feature = "teams")] + let teams = adapters::teams::TeamsConfig::from_env().map(|config| { + let webhook_path = + std::env::var("TEAMS_WEBHOOK_PATH").unwrap_or_else(|_| "/webhook/teams".into()); + info!(path = %webhook_path, "teams adapter enabled"); + adapters::teams::TeamsAdapter::new(config) + }); + + #[cfg(feature = "teams")] + if teams.is_some() { + let webhook_path = + std::env::var("TEAMS_WEBHOOK_PATH").unwrap_or_else(|_| "/webhook/teams".into()); + app = app.route(&webhook_path, post(adapters::teams::webhook)); + } + + // Feishu adapter + #[cfg(feature = "feishu")] + let feishu_config = adapters::feishu::FeishuConfig::from_env(); + #[cfg(feature = "feishu")] + let feishu_ws_mode = feishu_config + .as_ref() + .map(|c| c.connection_mode == adapters::feishu::ConnectionMode::Websocket) + .unwrap_or(false); + #[cfg(feature = "feishu")] + if let Some(ref config) = feishu_config { + match config.connection_mode { + adapters::feishu::ConnectionMode::Websocket => { + info!("feishu adapter enabled (websocket) — will connect after state init"); + } + adapters::feishu::ConnectionMode::Webhook => { + let path = config.webhook_path.clone(); + info!(path = %path, "feishu adapter enabled (webhook)"); + app = app.route(&path, post(adapters::feishu::webhook)); + } + } + } + #[cfg(feature = "feishu")] + let feishu = feishu_config.map(adapters::feishu::FeishuAdapter::new); + #[cfg(feature = "feishu")] + if let Some(ref f) = feishu { + f.resolve_bot_identity().await; + } + + // Google Chat adapter + #[cfg(feature = "googlechat")] + let google_chat = { + let enabled = std::env::var("GOOGLE_CHAT_ENABLED") + .map(|v| v == "true" || v == "1") + .unwrap_or(false); + if enabled { + let token_cache = std::env::var("GOOGLE_CHAT_SA_KEY_JSON") + .ok() + .or_else(|| { + std::env::var("GOOGLE_CHAT_SA_KEY_FILE") + .ok() + .and_then(|path| std::fs::read_to_string(&path).ok()) + }) + .and_then(|json| { + adapters::googlechat::GoogleChatTokenCache::new(&json) + .map_err(|e| warn!("googlechat SA key error: {e}")) + .ok() + }); + let access_token = std::env::var("GOOGLE_CHAT_ACCESS_TOKEN").ok(); + let jwt_verifier = std::env::var("GOOGLE_CHAT_AUDIENCE").ok().map(|aud| { + info!("googlechat webhook JWT verification enabled (audience={aud})"); + adapters::googlechat::GoogleChatJwtVerifier::new(aud) + }); + let webhook_path = std::env::var("GOOGLE_CHAT_WEBHOOK_PATH") + .unwrap_or_else(|_| "/webhook/googlechat".into()); + info!(path = %webhook_path, "googlechat adapter enabled"); + app = app.route(&webhook_path, post(adapters::googlechat::webhook)); + Some(adapters::googlechat::GoogleChatAdapter::new( + token_cache, + access_token, + jwt_verifier, + )) + } else { + None + } + }; + + // WeCom adapter + #[cfg(feature = "wecom")] + let wecom = adapters::wecom::WecomConfig::from_env().map(|config| { + let path = config.webhook_path.clone(); + info!(path = %path, "wecom adapter enabled"); + adapters::wecom::WecomAdapter::new(config) + }); + #[cfg(feature = "wecom")] + if let Some(ref w) = wecom { + app = app + .route( + &w.config.webhook_path, + axum::routing::get(adapters::wecom::verify), + ) + .route(&w.config.webhook_path, post(adapters::wecom::webhook)); + } + + let client = reqwest::Client::builder() + .timeout(std::time::Duration::from_secs(30)) + .build() + .expect("HTTP client must build"); + + let state = Arc::new(AppState { + #[cfg(feature = "telegram")] + telegram_bot_token, + #[cfg(feature = "telegram")] + telegram_secret_token, + #[cfg(feature = "telegram")] + telegram_rich_messages, + #[cfg(feature = "line")] + line_channel_secret, + #[cfg(feature = "line")] + line_access_token, + #[cfg(feature = "teams")] + teams, + #[cfg(feature = "teams")] + teams_service_urls: Mutex::new(HashMap::new()), + #[cfg(feature = "feishu")] + feishu, + #[cfg(feature = "googlechat")] + google_chat, + #[cfg(feature = "wecom")] + wecom, + ws_token, + event_tx, + #[cfg(feature = "line")] + reply_token_cache, + #[cfg(feature = "line")] + line_webhook_semaphore: Arc::new(Semaphore::new(LINE_WEBHOOK_CONCURRENCY_MAX)), + client, + }); + + // Background: sweep expired reply tokens + #[cfg(feature = "line")] + { + let cache_state = state.clone(); + tokio::spawn(async move { + loop { + tokio::time::sleep(std::time::Duration::from_secs(REPLY_TOKEN_TTL_SECS)).await; + let mut cache = cache_state + .reply_token_cache + .lock() + .unwrap_or_else(|e| e.into_inner()); + let before = cache.len(); + cache.retain(|_, (_, t)| t.elapsed().as_secs() < REPLY_TOKEN_TTL_SECS); + let after = cache.len(); + if before != after { + info!( + removed = before - after, + remaining = after, + "reply token cache sweep" + ); + } + } + }); + } + + // Background: cleanup stale Teams service_url entries (TTL: 4 hours) + #[cfg(feature = "teams")] + { + let state_for_cleanup = state.clone(); + tokio::spawn(async move { + loop { + tokio::time::sleep(std::time::Duration::from_secs(300)).await; + let mut urls = state_for_cleanup.teams_service_urls.lock().await; + let before = urls.len(); + urls.retain(|_, (_, t)| t.elapsed().as_secs() < 4 * 3600); + let after = urls.len(); + if before != after { + info!( + removed = before - after, + remaining = after, + "teams service_url cache cleanup" + ); + } + } + }); + } + + let app = app.with_state(state.clone()); + + // Background: evict expired media files + tokio::spawn(openab_gateway::store::eviction_loop()); + + // Spawn feishu WebSocket long-connection if configured + #[cfg(feature = "feishu")] + let _feishu_shutdown_tx = { + let (tx, rx) = tokio::sync::watch::channel(false); + if feishu_ws_mode { + if let Some(ref feishu) = state.feishu { + match adapters::feishu::start_websocket(feishu, state.event_tx.clone(), rx).await { + Ok(_handle) => info!("feishu websocket task spawned"), + Err(e) => tracing::error!(err = %e, "feishu websocket startup failed"), + } + } + } + tx + }; + + info!(addr = %listen_addr, "gateway starting"); + let listener = tokio::net::TcpListener::bind(&listen_addr).await?; + axum::serve(listener, app).await?; + Ok(()) +} diff --git a/gateway/src/media.rs b/crates/openab-gateway/src/media.rs similarity index 100% rename from gateway/src/media.rs rename to crates/openab-gateway/src/media.rs diff --git a/gateway/src/schema.rs b/crates/openab-gateway/src/schema.rs similarity index 100% rename from gateway/src/schema.rs rename to crates/openab-gateway/src/schema.rs diff --git a/gateway/src/store.rs b/crates/openab-gateway/src/store.rs similarity index 100% rename from gateway/src/store.rs rename to crates/openab-gateway/src/store.rs diff --git a/gateway/Cargo.lock b/gateway/Cargo.lock deleted file mode 100644 index c1567f997..000000000 --- a/gateway/Cargo.lock +++ /dev/null @@ -1,2849 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" - -[[package]] -name = "assert-json-diff" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" -dependencies = [ - "serde", - "serde_json", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "axum" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" -dependencies = [ - "axum-core", - "base64", - "bytes", - "form_urlencoded", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "serde_core", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sha1", - "sync_wrapper", - "tokio", - "tokio-tungstenite 0.29.0", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-core" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "sync_wrapper", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "bitflags" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bumpalo" -version = "3.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" - -[[package]] -name = "bytemuck" -version = "1.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "byteorder-lite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" - -[[package]] -name = "bytes" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" - -[[package]] -name = "cbc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" -dependencies = [ - "cipher", -] - -[[package]] -name = "cc" -version = "1.2.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" -dependencies = [ - "find-msvc-tools", - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "chrono" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-link", -] - -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crypto-common" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "data-encoding" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" - -[[package]] -name = "deadpool" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0be2b1d1d6ec8d846f05e137292d0b89133caf95ef33695424c09568bdd39b1b" -dependencies = [ - "deadpool-runtime", - "lazy_static", - "num_cpus", - "tokio", -] - -[[package]] -name = "deadpool-runtime" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" - -[[package]] -name = "deranged" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", - "subtle", -] - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "fdeflate" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "flate2" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futures" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" - -[[package]] -name = "futures-executor" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" - -[[package]] -name = "futures-macro" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" - -[[package]] -name = "futures-task" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" - -[[package]] -name = "futures-util" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi 5.3.0", - "wasip2", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" -dependencies = [ - "cfg-if", - "libc", - "r-efi 6.0.0", - "wasip2", - "wasip3", -] - -[[package]] -name = "gif" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" -dependencies = [ - "color_quant", - "weezl", -] - -[[package]] -name = "h2" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash", -] - -[[package]] -name = "hashbrown" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "http" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" -dependencies = [ - "bytes", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" -dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" -dependencies = [ - "http", - "hyper", - "hyper-util", - "rustls 0.23.39", - "tokio", - "tokio-rustls 0.26.4", - "tower-service", - "webpki-roots 1.0.7", -] - -[[package]] -name = "hyper-util" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" -dependencies = [ - "base64", - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "icu_collections" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" -dependencies = [ - "displaydoc", - "potential_utf", - "utf8_iter", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" - -[[package]] -name = "icu_properties" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" - -[[package]] -name = "icu_provider" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "image" -version = "0.25.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" -dependencies = [ - "bytemuck", - "byteorder-lite", - "color_quant", - "gif", - "image-webp", - "moxcms", - "num-traits", - "png", - "zune-core", - "zune-jpeg", -] - -[[package]] -name = "image-webp" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" -dependencies = [ - "byteorder-lite", - "quick-error", -] - -[[package]] -name = "indexmap" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" -dependencies = [ - "equivalent", - "hashbrown 0.17.0", - "serde", - "serde_core", -] - -[[package]] -name = "inout" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" -dependencies = [ - "block-padding", - "generic-array", -] - -[[package]] -name = "ipnet" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" - -[[package]] -name = "iri-string" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "js-sys" -version = "0.3.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" -dependencies = [ - "cfg-if", - "futures-util", - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "jsonwebtoken" -version = "9.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" -dependencies = [ - "base64", - "js-sys", - "pem", - "ring", - "serde", - "serde_json", - "simple_asn1", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - -[[package]] -name = "libc" -version = "0.2.186" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" - -[[package]] -name = "litemap" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" - -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - -[[package]] -name = "matchers" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" -dependencies = [ - "regex-automata", -] - -[[package]] -name = "matchit" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" - -[[package]] -name = "memchr" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "mio" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.61.2", -] - -[[package]] -name = "moxcms" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" -dependencies = [ - "num-traits", - "pxfm", -] - -[[package]] -name = "nu-ansi-term" -version = "0.50.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-conv" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "openab-gateway" -version = "0.5.4" -dependencies = [ - "aes", - "anyhow", - "axum", - "base64", - "cbc", - "chrono", - "futures-util", - "hmac", - "image", - "jsonwebtoken", - "prost", - "quick-xml", - "reqwest", - "serde", - "serde_json", - "sha1", - "sha2", - "subtle", - "tokio", - "tokio-tungstenite 0.21.0", - "tracing", - "tracing-subscriber", - "urlencoding", - "uuid", - "wiremock", -] - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link", -] - -[[package]] -name = "pem" -version = "3.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" -dependencies = [ - "base64", - "serde_core", -] - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" - -[[package]] -name = "png" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" -dependencies = [ - "bitflags", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "potential_utf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" -dependencies = [ - "zerovec", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn", -] - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "prost" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-derive" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" -dependencies = [ - "anyhow", - "itertools", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pxfm" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f" - -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - -[[package]] -name = "quick-xml" -version = "0.37.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" -dependencies = [ - "memchr", -] - -[[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls 0.23.39", - "socket2", - "thiserror 2.0.18", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" -dependencies = [ - "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand 0.9.4", - "ring", - "rustc-hash", - "rustls 0.23.39", - "rustls-pki-types", - "slab", - "thiserror 2.0.18", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.60.2", -] - -[[package]] -name = "quote" -version = "1.0.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "r-efi" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" - -[[package]] -name = "rand" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.17", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex" -version = "1.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" - -[[package]] -name = "reqwest" -version = "0.12.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" -dependencies = [ - "base64", - "bytes", - "futures-core", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls 0.23.39", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-rustls 0.26.4", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots 1.0.7", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.17", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustc-hash" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" - -[[package]] -name = "rustls" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" -dependencies = [ - "log", - "ring", - "rustls-pki-types", - "rustls-webpki 0.102.8", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls" -version = "0.23.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2c118cb077cca2822033836dfb1b975355dfb784b5e8da48f7b6c5db74e60e" -dependencies = [ - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki 0.103.13", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-pki-types" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" -dependencies = [ - "web-time", - "zeroize", -] - -[[package]] -name = "rustls-webpki" -version = "0.102.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustls-webpki" -version = "0.103.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "semver" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.149" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_path_to_error" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" -dependencies = [ - "itoa", - "serde", - "serde_core", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signal-hook-registry" -version = "1.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" -dependencies = [ - "errno", - "libc", -] - -[[package]] -name = "simd-adler32" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" - -[[package]] -name = "simple_asn1" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d585997b0ac10be3c5ee635f1bab02d512760d14b7c468801ac8a01d9ae5f1d" -dependencies = [ - "num-bigint", - "num-traits", - "thiserror 2.0.18", - "time", -] - -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "socket2" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "2.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" -dependencies = [ - "thiserror-impl 2.0.18", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "time" -version = "0.3.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde_core", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" - -[[package]] -name = "time-macros" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinystr" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.52.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" -dependencies = [ - "bytes", - "libc", - "mio", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-macros" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-rustls" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" -dependencies = [ - "rustls 0.22.4", - "rustls-pki-types", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" -dependencies = [ - "rustls 0.23.39", - "tokio", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" -dependencies = [ - "futures-util", - "log", - "rustls 0.22.4", - "rustls-pki-types", - "tokio", - "tokio-rustls 0.25.0", - "tungstenite 0.21.0", - "webpki-roots 0.26.11", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f72a05e828585856dacd553fba484c242c46e391fb0e58917c942ee9202915c" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite 0.29.0", -] - -[[package]] -name = "tokio-util" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tower" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" -dependencies = [ - "bitflags", - "bytes", - "futures-util", - "http", - "http-body", - "iri-string", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" -dependencies = [ - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing-core" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex-automata", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "tungstenite" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand 0.8.6", - "rustls 0.22.4", - "rustls-pki-types", - "sha1", - "thiserror 1.0.69", - "url", - "utf-8", -] - -[[package]] -name = "tungstenite" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8" -dependencies = [ - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand 0.9.4", - "sha1", - "thiserror 2.0.18", -] - -[[package]] -name = "typenum" -version = "1.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "uuid" -version = "1.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" -dependencies = [ - "getrandom 0.4.2", - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "valuable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.3+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" -dependencies = [ - "wit-bindgen 0.57.1", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen 0.51.0", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - -[[package]] -name = "web-sys" -version = "0.3.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki-roots" -version = "0.26.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" -dependencies = [ - "webpki-roots 1.0.7", -] - -[[package]] -name = "webpki-roots" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "weezl" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - -[[package]] -name = "wiremock" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08db1edfb05d9b3c1542e521aea074442088292f00b5f28e435c714a98f85031" -dependencies = [ - "assert-json-diff", - "base64", - "deadpool", - "futures", - "http", - "http-body-util", - "hyper", - "hyper-util", - "log", - "once_cell", - "regex", - "serde", - "serde_json", - "tokio", - "url", -] - -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen" -version = "0.57.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - -[[package]] -name = "writeable" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" - -[[package]] -name = "yoke" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zerocopy" -version = "0.8.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zerofrom" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" - -[[package]] -name = "zerotrie" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zmij" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" - -[[package]] -name = "zune-core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" - -[[package]] -name = "zune-jpeg" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" -dependencies = [ - "zune-core", -] diff --git a/gateway/Dockerfile b/gateway/Dockerfile deleted file mode 100644 index 8ee8172d0..000000000 --- a/gateway/Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -# --- Build stage --- -FROM rust:1-bookworm AS builder -WORKDIR /build -COPY gateway/Cargo.toml gateway/Cargo.lock ./ -RUN mkdir src && echo 'fn main() {}' > src/main.rs && cargo build --release && rm -rf src -COPY gateway/src/ src/ -RUN touch src/main.rs && cargo build --release - -# --- Runtime stage --- -FROM debian:bookworm-slim -RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates && rm -rf /var/lib/apt/lists/* -COPY --from=builder /build/target/release/openab-gateway /usr/local/bin/openab-gateway -EXPOSE 8080 -ENTRYPOINT ["openab-gateway"] diff --git a/gateway/README.md b/gateway/README.md deleted file mode 100644 index 79c492a28..000000000 --- a/gateway/README.md +++ /dev/null @@ -1,173 +0,0 @@ -# OpenAB Custom Gateway - -A standalone service that bridges webhook-based platforms and custom event sources to OAB via WebSocket. OAB connects outbound to the gateway — no inbound ports or TLS required on OAB. - -``` - External (HTTPS) Internal (cluster) - ──────────────── ────────────────── - -Telegram ──POST──▶┌─────────────────────┐ -LINE ──POST──▶│ │ -GitHub ──POST──▶│ Custom Gateway │◀──WebSocket── OAB Pod -CI/CD ──POST──▶│ :8080 │ (OAB connects out) -curl/cron ──POST──▶│ │ - └─────────────────────┘ - -Discord ◀──WebSocket── OAB Pod (unchanged, direct) -Slack ◀──WebSocket── OAB Pod (unchanged, direct) -``` - -The gateway normalizes all inbound events to a unified schema (`openab.gateway.event.v1`), forwards them to OAB over WebSocket, and routes OAB replies back to the originating platform API. - -For architecture details, see [ADR: Custom Gateway](../docs/adr/custom-gateway.md). - -> **Design note:** The gateway is intentionally NOT included in the OAB container image. It is a separate service with its own build, deployment, and scaling lifecycle. This follows the ADR principle that OAB remains outbound-only and platform-agnostic — all inbound webhook handling and platform credentials live in the gateway. - ---- - -## Quick Start - -```bash -cargo build --release -export TELEGRAM_BOT_TOKEN="your-bot-token" -./target/release/openab-gateway -``` - -### OAB Config - -```toml -[gateway] -url = "ws://gateway:8080/ws" -``` - -### Environment Variables - -| Variable | Default | Description | -|---|---|---| -| `TELEGRAM_BOT_TOKEN` | (required) | Telegram Bot API token | -| `GATEWAY_LISTEN` | `0.0.0.0:8080` | Listen address | -| `TELEGRAM_WEBHOOK_PATH` | `/webhook/telegram` | Webhook endpoint path | -| `LINE_CHANNEL_SECRET` | (optional) | LINE channel secret for webhook HMAC signature verification | -| `LINE_CHANNEL_ACCESS_TOKEN` | (optional) | LINE channel access token for Reply/Push API | -| `FEISHU_APP_ID` | (optional) | Feishu/Lark App ID — enables feishu adapter | -| `FEISHU_APP_SECRET` | (optional) | Feishu/Lark App Secret | -| `FEISHU_DOMAIN` | `feishu` | `feishu` (China) or `lark` (international) | -| `FEISHU_CONNECTION_MODE` | `websocket` | `websocket` (recommended) or `webhook` | -| `FEISHU_WEBHOOK_PATH` | `/webhook/feishu` | Webhook endpoint path | -| `FEISHU_VERIFICATION_TOKEN` | (optional) | Webhook verification token | -| `FEISHU_ENCRYPT_KEY` | (optional) | Webhook encrypt key for AES-256-CBC | -| `FEISHU_ALLOWED_GROUPS` | (optional) | Comma-separated chat_id allowlist | -| `FEISHU_ALLOWED_USERS` | (optional) | Comma-separated open_id allowlist | -| `FEISHU_REQUIRE_MENTION` | `true` | Require @mention in groups | -| `FEISHU_DEDUPE_TTL_SECS` | `300` | Event deduplication cache TTL (seconds) | -| `FEISHU_MESSAGE_LIMIT` | `4000` | Max message length before auto-splitting (bytes) | -| `GOOGLE_CHAT_ENABLED` | `false` | Set to `true` or `1` to enable the Google Chat adapter | -| `GOOGLE_CHAT_AUDIENCE` | (optional) | JWT audience for webhook verification (full webhook URL, e.g. `https://your-domain.com/webhook/googlechat`) | -| `GOOGLE_CHAT_SA_KEY_JSON` | (optional) | Service account key JSON string (enables token auto-refresh) | -| `GOOGLE_CHAT_SA_KEY_FILE` | (optional) | Path to service account key JSON file (alternative to `SA_KEY_JSON`) | -| `GOOGLE_CHAT_ACCESS_TOKEN` | (optional) | Static OAuth2 access token (fallback, expires in 1 hour) | -| `GOOGLE_CHAT_WEBHOOK_PATH` | `/webhook/googlechat` | Webhook endpoint path | -| `WECOM_CORP_ID` | (required*) | WeCom Corp ID — enables wecom adapter | -| `WECOM_AGENT_ID` | (required*) | WeCom App Agent ID | -| `WECOM_SECRET` | (required*) | WeCom App Secret | -| `WECOM_TOKEN` | (required*) | Callback verification Token | -| `WECOM_ENCODING_AES_KEY` | (required*) | Callback EncodingAESKey (43 chars) | -| `WECOM_WEBHOOK_PATH` | `/webhook/wecom` | Webhook endpoint path | -| `WECOM_STREAMING_ENABLED` | `false` | Enable thinking-placeholder + recall streaming (causes brief client flicker) | -| `WECOM_DEBOUNCE_SECS` | `3` | Debounce quiet-period seconds before flushing buffered streamed text | - -### Endpoints - -| Path | Description | -|---|---| -| `POST /webhook/telegram` | Telegram webhook receiver | -| `POST /webhook/line` | LINE webhook receiver | -| `POST /webhook/feishu` | Feishu webhook receiver (when `FEISHU_CONNECTION_MODE=webhook`) | -| `POST /webhook/googlechat` | Google Chat webhook receiver | -| `GET /webhook/wecom` | WeCom callback URL verification | -| `POST /webhook/wecom` | WeCom message callback receiver | -| `GET /ws` | WebSocket server (OAB connects here) | -| `GET /health` | Health check | - ---- - -## Platform Setup - -### Telegram - -1. Create a bot via [@BotFather](https://t.me/BotFather) and get the token. - -2. Start the gateway: - ```bash - export TELEGRAM_BOT_TOKEN="your-token" - ./target/release/openab-gateway - ``` - -3. Expose the gateway over HTTPS (Telegram requires it). Easiest option — Cloudflare Tunnel: - ```bash - cloudflared tunnel --url http://localhost:8080 - ``` - -4. Set the webhook: - ```bash - curl "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/setWebhook?url=https://your-host/webhook/telegram" - ``` - -5. For supergroup forum topics (thread isolation like Discord), give the bot **Manage Topics** permission in the group settings. - -### LINE - -See [docs/line.md](../docs/line.md) for the full setup guide. - -### Feishu/Lark - -See [docs/feishu.md](../docs/feishu.md) for the full setup guide. - -### Google Chat - -See [docs/google-chat.md](../docs/google-chat.md) for the full setup guide. - -### WeCom (企业微信) - -See [docs/wecom.md](../docs/wecom.md) for the full setup guide. - -### Other Platforms - -GitHub webhooks, CI/CD events, monitoring alerts — any HTTP event source can be added as a gateway adapter. See the ADR for the adapter interface. - ---- - -## Custom Event Source - -Any HTTP client can drive an OAB agent session by posting to the webhook endpoint. This turns OAB into an event-driven agent platform — no chat app required. - -### Example: trigger an agent from a cron job - -```bash -curl -X POST http://gateway:8080/webhook/telegram \ - -H "Content-Type: application/json" \ - -d '{ - "message": { - "message_id": 1, - "chat": {"id": 12345, "type": "private"}, - "from": {"id": 99, "first_name": "CronJob", "username": "scheduler", "is_bot": false}, - "text": "run daily security scan on staging" - } - }' -``` - -### Example: generic event (future `/webhook/custom` endpoint) - -Once a generic webhook adapter is added, any JSON payload can trigger an agent: - -```bash -curl -X POST http://gateway:8080/webhook/custom \ - -H "Content-Type: application/json" \ - -d '{ - "channel": "ops-alerts", - "sender": "cloudwatch", - "text": "CPU > 90% on prod-api-3 for 5 minutes, investigate and suggest fix" - }' -``` - -The agent response is delivered back through the gateway to whatever reply mechanism the adapter defines — Telegram message, GitHub comment, Slack DM, PagerDuty note, or simply logged. diff --git a/gateway/src/adapters/mod.rs b/gateway/src/adapters/mod.rs deleted file mode 100644 index 94a2a8a79..000000000 --- a/gateway/src/adapters/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod feishu; -pub mod googlechat; -pub mod line; -pub mod teams; -pub mod telegram; -pub mod wecom; diff --git a/gateway/src/main.rs b/gateway/src/main.rs deleted file mode 100644 index b5cad2f6a..000000000 --- a/gateway/src/main.rs +++ /dev/null @@ -1,801 +0,0 @@ -mod adapters; -mod media; -mod schema; -pub mod store; - -use anyhow::Result; -use axum::{ - extract::State, - response::IntoResponse, - routing::{get, post}, - Router, -}; -use futures_util::{SinkExt, StreamExt}; -use schema::GatewayReply; -use std::collections::HashMap; -use std::sync::Arc; -use std::time::Instant; -use tokio::sync::{broadcast, Mutex, Semaphore}; -use tracing::{info, warn}; - -// --- Reply token cache for LINE hybrid Reply/Push dispatch --- - -/// Cache entry for LINE reply tokens: (replyToken, insertion_time). -/// Uses std::sync::Mutex — critical sections are short (insert/remove/retain) -/// and never held across .await, so async Mutex overhead is unnecessary. -pub type ReplyTokenCache = Arc>>; - -/// Maximum age (in seconds) before a cached reply token is considered expired. -/// LINE tokens are valid for ~1 minute; we use 50s as a conservative margin. -pub const REPLY_TOKEN_TTL_SECS: u64 = 50; - -/// Maximum number of cached reply tokens. Prevents unbounded memory growth -/// if webhooks arrive faster than OAB can reply (e.g. OAB offline, spam burst). -pub const REPLY_TOKEN_CACHE_MAX: usize = 10_000; - -/// Maximum number of post-ack LINE webhook payloads processed concurrently. -/// Keeps image download/decode work bounded during bursts without giving up the -/// fast 200 OK response path. -pub const LINE_WEBHOOK_CONCURRENCY_MAX: usize = 8; - -// --- App state (shared across all adapters) --- - -pub struct AppState { - /// Telegram bot token (None if Telegram disabled) - pub telegram_bot_token: Option, - /// Telegram webhook secret token for request validation - pub telegram_secret_token: Option, - /// Use sendRichMessage for complex content (Bot API 10.1+) - pub telegram_rich_messages: bool, - /// LINE channel secret for signature validation - pub line_channel_secret: Option, - /// LINE channel access token for reply API - pub line_access_token: Option, - /// Teams adapter (None if Teams disabled) - pub teams: Option, - /// service_url cache for Teams reply routing (conversation_id → (service_url, last_seen)) - pub teams_service_urls: Mutex>, - /// Feishu adapter (None if Feishu disabled) - pub feishu: Option, - /// Google Chat adapter (None if Google Chat disabled) - pub google_chat: Option, - pub wecom: Option, - /// WebSocket authentication token - pub ws_token: Option, - /// Broadcast channel: gateway → OAB (events from all platforms) - pub event_tx: broadcast::Sender, - /// Cache: event_id → (LINE replyToken, timestamp). - /// Global across all OAB WebSocket clients. LINE reply tokens are single-use: - /// the first client to `remove()` a token wins the free Reply API call; - /// other clients for the same event naturally fall back to Push API. - pub reply_token_cache: ReplyTokenCache, - /// Limits concurrent post-ack LINE webhook processing so image bursts do not - /// turn into unbounded download/decode work. - pub line_webhook_semaphore: Arc, - /// Shared HTTP client for media downloads and API calls - pub client: reqwest::Client, -} - -// --- WebSocket handler (OAB connects here) --- - -async fn ws_handler( - State(state): State>, - query: axum::extract::Query>, - ws: axum::extract::WebSocketUpgrade, -) -> axum::response::Response { - if let Some(ref expected) = state.ws_token { - let provided = query.get("token").map(|s| s.as_str()); - if provided != Some(expected.as_str()) { - warn!("WebSocket rejected: invalid or missing token"); - return axum::http::StatusCode::UNAUTHORIZED.into_response(); - } - } - ws.on_upgrade(move |socket| handle_oab_connection(state, socket)) -} - -async fn handle_oab_connection(state: Arc, socket: axum::extract::ws::WebSocket) { - use axum::extract::ws::Message; - - let (mut ws_tx, mut ws_rx) = socket.split(); - let mut event_rx = state.event_tx.subscribe(); - - info!("OAB client connected via WebSocket"); - - // Forward gateway events → OAB - let send_task = tokio::spawn(async move { - loop { - tokio::select! { - Ok(event_json) = event_rx.recv() => { - if ws_tx.send(Message::Text(event_json.into())).await.is_err() { - break; - } - } - } - } - }); - - // Receive OAB replies → route to correct platform - let state_for_recv = state.clone(); - // Track per-message reaction state (Telegram replaces all reactions atomically) - let reaction_state: Arc>>> = - Arc::new(Mutex::new(HashMap::new())); - let recv_task = tokio::spawn(async move { - let client = reqwest::Client::new(); - while let Some(Ok(msg)) = ws_rx.next().await { - if let Message::Text(text) = msg { - match serde_json::from_str::(&text) { - Ok(reply) => { - info!( - platform = %reply.platform, - channel = %reply.channel.id, - command = ?reply.command.as_deref(), - "OAB → gateway reply" - ); - match reply.platform.as_str() { - "telegram" => { - if let Some(ref token) = state_for_recv.telegram_bot_token { - adapters::telegram::handle_reply( - &reply, - token, - &client, - &state_for_recv.event_tx, - &reaction_state, - state_for_recv.telegram_rich_messages, - ) - .await; - } else { - warn!("reply for telegram but adapter not configured"); - } - } - "line" => { - if let Some(ref access_token) = state_for_recv.line_access_token { - adapters::line::dispatch_line_reply( - &client, - access_token, - &state_for_recv.reply_token_cache, - &reply, - adapters::line::LINE_API_BASE, - ) - .await; - } else { - warn!("reply for line but adapter not configured"); - } - } - "teams" => { - if let Some(ref teams) = state_for_recv.teams { - adapters::teams::handle_reply( - &reply, - teams, - &state_for_recv.teams_service_urls, - ) - .await; - } else { - warn!("reply for teams but adapter not configured"); - } - } - "feishu" => { - if let Some(ref feishu) = state_for_recv.feishu { - adapters::feishu::handle_reply( - &reply, - feishu, - &state_for_recv.event_tx, - ) - .await; - } else { - warn!("reply for feishu but adapter not configured"); - } - } - "googlechat" => { - if let Some(ref gc) = state_for_recv.google_chat { - gc.handle_reply(&reply, &state_for_recv.event_tx).await; - } else { - warn!("reply for googlechat but adapter not configured"); - } - } - "wecom" => { - if let Some(ref wecom) = state_for_recv.wecom { - wecom.handle_reply(&reply, &state_for_recv.event_tx).await; - } else { - warn!("reply for wecom but adapter not configured"); - } - } - other => warn!(platform = other, "unknown reply platform"), - } - } - Err(e) => warn!("invalid reply from OAB: {e}"), - } - } - } - }); - - tokio::select! { - _ = send_task => {}, - _ = recv_task => {}, - } - info!("OAB client disconnected"); -} - -async fn health() -> &'static str { - "ok" -} - -#[tokio::main] -async fn main() -> Result<()> { - tracing_subscriber::fmt() - .with_env_filter( - tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()), - ) - .init(); - - let listen_addr = std::env::var("GATEWAY_LISTEN").unwrap_or_else(|_| "0.0.0.0:8080".into()); - let ws_token = std::env::var("GATEWAY_WS_TOKEN").ok(); - - if ws_token.is_none() { - warn!("GATEWAY_WS_TOKEN not set — WebSocket connections are NOT authenticated (insecure)"); - } - - let (event_tx, _) = broadcast::channel::(256); - let reply_token_cache: ReplyTokenCache = Arc::new(std::sync::Mutex::new(HashMap::new())); - - let mut app = Router::new() - .route("/ws", get(ws_handler)) - .route("/health", get(health)); - - // Telegram adapter - let telegram_bot_token = std::env::var("TELEGRAM_BOT_TOKEN").ok(); - let telegram_secret_token = std::env::var("TELEGRAM_SECRET_TOKEN").ok(); - let telegram_rich_messages = std::env::var("TELEGRAM_RICH_MESSAGES") - .map(|v| v != "0" && !v.eq_ignore_ascii_case("false")) - .unwrap_or(true); - if telegram_bot_token.is_some() { - let webhook_path = - std::env::var("TELEGRAM_WEBHOOK_PATH").unwrap_or_else(|_| "/webhook/telegram".into()); - if telegram_secret_token.is_none() { - warn!("TELEGRAM_SECRET_TOKEN not set — webhook requests are NOT validated (insecure)"); - } - info!(path = %webhook_path, "telegram adapter enabled"); - app = app.route(&webhook_path, post(adapters::telegram::webhook)); - } - - // LINE adapter — route is always mounted so inbound webhooks are accepted - // even without an access token (signature validation only needs LINE_CHANNEL_SECRET). - let line_channel_secret = std::env::var("LINE_CHANNEL_SECRET").ok(); - let line_access_token = std::env::var("LINE_CHANNEL_ACCESS_TOKEN").ok(); - info!("line adapter enabled"); - app = app.route("/webhook/line", post(adapters::line::webhook)); - - // Teams adapter - let teams = adapters::teams::TeamsConfig::from_env().map(|config| { - info!("teams adapter enabled"); - adapters::teams::TeamsAdapter::new(config) - }); - if teams.is_some() { - let webhook_path = - std::env::var("TEAMS_WEBHOOK_PATH").unwrap_or_else(|_| "/webhook/teams".into()); - info!(path = %webhook_path, "teams webhook registered"); - app = app.route(&webhook_path, post(adapters::teams::webhook)); - } - - // Feishu adapter - let feishu_config = adapters::feishu::FeishuConfig::from_env(); - let feishu_ws_mode = feishu_config - .as_ref() - .map(|c| c.connection_mode == adapters::feishu::ConnectionMode::Websocket) - .unwrap_or(false); - if let Some(ref config) = feishu_config { - match config.connection_mode { - adapters::feishu::ConnectionMode::Websocket => { - info!("feishu adapter enabled (websocket) — will connect after state init"); - } - adapters::feishu::ConnectionMode::Webhook => { - let path = config.webhook_path.clone(); - info!(path = %path, "feishu adapter enabled (webhook)"); - app = app.route(&path, post(adapters::feishu::webhook)); - } - } - } - let feishu = feishu_config.map(adapters::feishu::FeishuAdapter::new); - - // Resolve feishu bot identity early (needed for mention gating in both modes) - if let Some(ref f) = feishu { - f.resolve_bot_identity().await; - } - - // Google Chat adapter - let google_chat_enabled = std::env::var("GOOGLE_CHAT_ENABLED") - .map(|v| v == "true" || v == "1") - .unwrap_or(false); - let google_chat = if google_chat_enabled { - let token_cache = std::env::var("GOOGLE_CHAT_SA_KEY_JSON") - .ok() - .or_else(|| { - std::env::var("GOOGLE_CHAT_SA_KEY_FILE") - .ok() - .and_then(|path| std::fs::read_to_string(&path).ok()) - }) - .and_then(|json| { - adapters::googlechat::GoogleChatTokenCache::new(&json) - .map_err(|e| warn!("googlechat SA key error: {e}")) - .ok() - }); - let access_token = std::env::var("GOOGLE_CHAT_ACCESS_TOKEN").ok(); - let jwt_verifier = std::env::var("GOOGLE_CHAT_AUDIENCE").ok().map(|aud| { - info!("googlechat webhook JWT verification enabled (audience={aud})"); - adapters::googlechat::GoogleChatJwtVerifier::new(aud) - }); - - let webhook_path = std::env::var("GOOGLE_CHAT_WEBHOOK_PATH") - .unwrap_or_else(|_| "/webhook/googlechat".into()); - info!(path = %webhook_path, "googlechat adapter enabled"); - app = app.route(&webhook_path, post(adapters::googlechat::webhook)); - - if token_cache.is_some() { - info!("googlechat service account configured — token auto-refresh enabled"); - } else if access_token.is_some() { - warn!("googlechat using static access token — will expire in ~1 hour"); - } else { - warn!("GOOGLE_CHAT_ACCESS_TOKEN / GOOGLE_CHAT_SA_KEY_JSON not set — replies will be logged but not sent"); - } - if jwt_verifier.is_none() { - warn!( - "GOOGLE_CHAT_AUDIENCE not set — webhook requests are NOT authenticated (insecure)" - ); - } - - Some(adapters::googlechat::GoogleChatAdapter::new( - token_cache, - access_token, - jwt_verifier, - )) - } else { - None - }; - - // WeCom adapter - let wecom = adapters::wecom::WecomConfig::from_env().map(|config| { - let path = config.webhook_path.clone(); - info!(path = %path, "wecom adapter enabled"); - adapters::wecom::WecomAdapter::new(config) - }); - if let Some(ref w) = wecom { - app = app - .route( - &w.config.webhook_path, - axum::routing::get(adapters::wecom::verify), - ) - .route(&w.config.webhook_path, post(adapters::wecom::webhook)); - } - - if telegram_bot_token.is_none() - && line_access_token.is_none() - && teams.is_none() - && feishu.is_none() - && google_chat.is_none() - && wecom.is_none() - { - warn!("no adapters configured — set TELEGRAM_BOT_TOKEN, LINE_CHANNEL_ACCESS_TOKEN, TEAMS_APP_ID + TEAMS_APP_SECRET, FEISHU_APP_ID + FEISHU_APP_SECRET, GOOGLE_CHAT_ENABLED=true, and/or WECOM_CORP_ID + WECOM_SECRET + WECOM_TOKEN + WECOM_ENCODING_AES_KEY + WECOM_AGENT_ID"); - } - - let client = reqwest::Client::builder() - .timeout(std::time::Duration::from_secs(30)) - .build() - .expect("HTTP client must build"); - - let state = Arc::new(AppState { - telegram_bot_token, - telegram_secret_token, - telegram_rich_messages, - line_channel_secret, - line_access_token, - teams, - teams_service_urls: Mutex::new(HashMap::new()), - feishu, - google_chat, - wecom, - ws_token, - event_tx, - reply_token_cache, - line_webhook_semaphore: Arc::new(Semaphore::new(LINE_WEBHOOK_CONCURRENCY_MAX)), - client, - }); - - // Background task: sweep expired reply tokens every REPLY_TOKEN_TTL_SECS - { - let cache_state = state.clone(); - tokio::spawn(async move { - loop { - tokio::time::sleep(std::time::Duration::from_secs(REPLY_TOKEN_TTL_SECS)).await; - let mut cache = cache_state - .reply_token_cache - .lock() - .unwrap_or_else(|e| e.into_inner()); - let before = cache.len(); - cache.retain(|_, (_, t)| t.elapsed().as_secs() < REPLY_TOKEN_TTL_SECS); - let after = cache.len(); - if before != after { - info!( - removed = before - after, - remaining = after, - "reply token cache sweep" - ); - } - } - }); - } - - // Periodic cleanup of stale Teams service_url entries (TTL: 4 hours) - { - let state_for_cleanup = state.clone(); - tokio::spawn(async move { - loop { - tokio::time::sleep(std::time::Duration::from_secs(300)).await; - let mut urls = state_for_cleanup.teams_service_urls.lock().await; - let before = urls.len(); - urls.retain(|_, (_, t)| t.elapsed().as_secs() < 4 * 3600); - let after = urls.len(); - if before != after { - info!( - removed = before - after, - remaining = after, - "teams service_url cache cleanup" - ); - } - } - }); - } - - let app = app.with_state(state.clone()); - - // Background task: evict expired media files (colocate store, TTL 2 min) - tokio::spawn(store::eviction_loop()); - - // Spawn feishu WebSocket long-connection if configured - // feishu_shutdown_tx must remain alive for the lifetime of main() — dropping - // it signals shutdown to the WS task via feishu_shutdown_rx. - let (feishu_shutdown_tx, feishu_shutdown_rx) = tokio::sync::watch::channel(false); - if feishu_ws_mode { - if let Some(ref feishu) = state.feishu { - match adapters::feishu::start_websocket( - feishu, - state.event_tx.clone(), - feishu_shutdown_rx, - ) - .await - { - Ok(_handle) => info!("feishu websocket task spawned"), - Err(e) => tracing::error!(err = %e, "feishu websocket startup failed"), - } - } - } - - info!(addr = %listen_addr, "gateway starting"); - let listener = tokio::net::TcpListener::bind(&listen_addr).await?; - axum::serve(listener, app).await?; - drop(feishu_shutdown_tx); - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - use std::time::Duration; - use wiremock::matchers::{body_json, header, method, path}; - use wiremock::{Mock, MockServer, ResponseTemplate}; - - fn make_reply(event_id: &str) -> schema::GatewayReply { - schema::GatewayReply { - schema: "openab.gateway.reply.v1".into(), - reply_to: event_id.into(), - platform: "line".into(), - channel: schema::ReplyChannel { - id: "U1234".into(), - thread_id: None, - }, - content: schema::Content { - content_type: "text".into(), - text: "hello".into(), - attachments: Vec::new(), - }, - command: None, - request_id: None, - quote_message_id: None, - } - } - - fn make_reply_with_command(event_id: &str, command: &str, text: &str) -> schema::GatewayReply { - schema::GatewayReply { - schema: "openab.gateway.reply.v1".into(), - reply_to: event_id.into(), - platform: "line".into(), - channel: schema::ReplyChannel { - id: "U1234".into(), - thread_id: None, - }, - content: schema::Content { - content_type: "text".into(), - text: text.into(), - attachments: Vec::new(), - }, - command: Some(command.into()), - request_id: None, - quote_message_id: None, - } - } - - fn make_cache() -> ReplyTokenCache { - Arc::new(std::sync::Mutex::new(HashMap::new())) - } - - /// Cache hit: uses Reply API with correct replyToken, bearer token, and message body. - /// Does NOT call Push API. - #[tokio::test] - async fn cache_hit_uses_reply_api() { - let server = MockServer::start().await; - let _reply = Mock::given(method("POST")) - .and(path("/v2/bot/message/reply")) - .and(header("authorization", "Bearer test_access_token")) - .and(body_json(serde_json::json!({ - "replyToken": "tok_abc", - "messages": [{"type": "text", "text": "hello"}] - }))) - .respond_with(ResponseTemplate::new(200).set_body_string("{}")) - .expect(1) - .mount_as_scoped(&server) - .await; - let _push = Mock::given(method("POST")) - .and(path("/v2/bot/message/push")) - .respond_with(ResponseTemplate::new(200)) - .expect(0) - .mount_as_scoped(&server) - .await; - - let cache = make_cache(); - cache - .lock() - .unwrap() - .insert("evt_1".into(), ("tok_abc".into(), Instant::now())); - - let client = reqwest::Client::new(); - let used = adapters::line::dispatch_line_reply( - &client, - "test_access_token", - &cache, - &make_reply("evt_1"), - &server.uri(), - ) - .await; - - assert!(used, "should report Reply API was used"); - } - - /// All unsupported LINE commands should be ignored without consuming the cached reply token. - #[tokio::test] - async fn line_ignores_unsupported_commands_without_touching_cache() { - let unsupported = &["add_reaction", "remove_reaction", "create_topic"]; - - for cmd in unsupported { - let server = MockServer::start().await; - let _reply = Mock::given(method("POST")) - .and(path("/v2/bot/message/reply")) - .respond_with(ResponseTemplate::new(200)) - .expect(0) - .mount_as_scoped(&server) - .await; - let _push = Mock::given(method("POST")) - .and(path("/v2/bot/message/push")) - .respond_with(ResponseTemplate::new(200)) - .expect(0) - .mount_as_scoped(&server) - .await; - - let cache = make_cache(); - cache - .lock() - .unwrap() - .insert("evt_unsup".into(), ("tok_unsup".into(), Instant::now())); - - let client = reqwest::Client::new(); - let used = adapters::line::dispatch_line_reply( - &client, - "test_access_token", - &cache, - &make_reply_with_command("evt_unsup", cmd, "payload"), - &server.uri(), - ) - .await; - - assert!(!used, "{cmd}: should not report reply usage"); - assert!( - cache.lock().unwrap().contains_key("evt_unsup"), - "{cmd}: should not consume the cached reply token" - ); - } - } - - /// Cache miss: falls back to Push API with correct "to", bearer token, and message body. - #[tokio::test] - async fn cache_miss_uses_push_api() { - let server = MockServer::start().await; - let _reply = Mock::given(method("POST")) - .and(path("/v2/bot/message/reply")) - .respond_with(ResponseTemplate::new(200)) - .expect(0) - .mount_as_scoped(&server) - .await; - let _push = Mock::given(method("POST")) - .and(path("/v2/bot/message/push")) - .and(header("authorization", "Bearer test_access_token")) - .and(body_json(serde_json::json!({ - "to": "U1234", - "messages": [{"type": "text", "text": "hello"}] - }))) - .respond_with(ResponseTemplate::new(200).set_body_string("{}")) - .expect(1) - .mount_as_scoped(&server) - .await; - - let cache = make_cache(); - - let client = reqwest::Client::new(); - let used = adapters::line::dispatch_line_reply( - &client, - "test_access_token", - &cache, - &make_reply("evt_miss"), - &server.uri(), - ) - .await; - - assert!(!used, "should report Push API was used (no reply token)"); - } - - /// Expired cached token: falls back to Push API. - #[tokio::test] - async fn expired_token_uses_push_api() { - let server = MockServer::start().await; - let _reply = Mock::given(method("POST")) - .and(path("/v2/bot/message/reply")) - .respond_with(ResponseTemplate::new(200)) - .expect(0) - .mount_as_scoped(&server) - .await; - let _push = Mock::given(method("POST")) - .and(path("/v2/bot/message/push")) - .and(header("authorization", "Bearer test_access_token")) - .and(body_json(serde_json::json!({ - "to": "U1234", - "messages": [{"type": "text", "text": "hello"}] - }))) - .respond_with(ResponseTemplate::new(200).set_body_string("{}")) - .expect(1) - .mount_as_scoped(&server) - .await; - - let cache = make_cache(); - let expired_time = Instant::now() - Duration::from_secs(REPLY_TOKEN_TTL_SECS + 10); - cache - .lock() - .unwrap() - .insert("evt_exp".into(), ("tok_old".into(), expired_time)); - - let client = reqwest::Client::new(); - let used = adapters::line::dispatch_line_reply( - &client, - "test_access_token", - &cache, - &make_reply("evt_exp"), - &server.uri(), - ) - .await; - - assert!(!used, "should report Push API was used (expired token)"); - } - - /// Reply API 400 with invalid/expired reply token: falls back to Push API. - #[tokio::test] - async fn reply_400_invalid_token_falls_back_to_push() { - let server = MockServer::start().await; - let _reply = Mock::given(method("POST")) - .and(path("/v2/bot/message/reply")) - .and(header("authorization", "Bearer test_access_token")) - .respond_with( - ResponseTemplate::new(400).set_body_string(r#"{"message":"Invalid reply token"}"#), - ) - .expect(1) - .mount_as_scoped(&server) - .await; - let _push = Mock::given(method("POST")) - .and(path("/v2/bot/message/push")) - .and(header("authorization", "Bearer test_access_token")) - .and(body_json(serde_json::json!({ - "to": "U1234", - "messages": [{"type": "text", "text": "hello"}] - }))) - .respond_with(ResponseTemplate::new(200).set_body_string("{}")) - .expect(1) - .mount_as_scoped(&server) - .await; - - let cache = make_cache(); - cache - .lock() - .unwrap() - .insert("evt_400".into(), ("tok_bad".into(), Instant::now())); - - let client = reqwest::Client::new(); - let used = adapters::line::dispatch_line_reply( - &client, - "test_access_token", - &cache, - &make_reply("evt_400"), - &server.uri(), - ) - .await; - - assert!(!used, "should fall back to Push on 400 invalid token"); - } - - /// Reply API 5xx: does NOT fall back to Push (duplicate risk). - #[tokio::test] - async fn reply_5xx_does_not_fallback() { - let server = MockServer::start().await; - let _reply = Mock::given(method("POST")) - .and(path("/v2/bot/message/reply")) - .and(header("authorization", "Bearer test_access_token")) - .respond_with(ResponseTemplate::new(500).set_body_string("Internal Server Error")) - .expect(1) - .mount_as_scoped(&server) - .await; - let _push = Mock::given(method("POST")) - .and(path("/v2/bot/message/push")) - .respond_with(ResponseTemplate::new(200)) - .expect(0) - .mount_as_scoped(&server) - .await; - - let cache = make_cache(); - cache - .lock() - .unwrap() - .insert("evt_5xx".into(), ("tok_5xx".into(), Instant::now())); - - let client = reqwest::Client::new(); - let used = adapters::line::dispatch_line_reply( - &client, - "test_access_token", - &cache, - &make_reply("evt_5xx"), - &server.uri(), - ) - .await; - - assert!(used, "should NOT fall back to Push on 5xx"); - } - - /// Reply API network/timeout error: does NOT fall back to Push (duplicate risk). - #[tokio::test] - async fn reply_network_error_does_not_fallback() { - let bad_base = "http://127.0.0.1:1"; - - let cache = make_cache(); - cache - .lock() - .unwrap() - .insert("evt_net".into(), ("tok_net".into(), Instant::now())); - - let client = reqwest::Client::builder() - .timeout(Duration::from_millis(100)) - .build() - .unwrap(); - let used = adapters::line::dispatch_line_reply( - &client, - "test_access_token", - &cache, - &make_reply("evt_net"), - bad_base, - ) - .await; - - assert!(used, "should NOT fall back to Push on network error"); - } -} diff --git a/src/main.rs b/src/main.rs index 0252193b8..d5416ce71 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ mod config; mod cron; mod ctl; mod directives; +#[cfg(feature = "discord")] mod discord; mod dispatch; mod error_display; @@ -18,13 +19,16 @@ mod reactions; mod remind; mod secrets; mod setup; +#[cfg(feature = "slack")] mod slack; mod stt; mod timestamp; use adapter::AdapterRouter; use clap::Parser; +#[cfg(feature = "discord")] use serenity::gateway::GatewayError; +#[cfg(feature = "discord")] use serenity::prelude::*; use std::collections::HashSet; use std::path::PathBuf; @@ -278,11 +282,14 @@ async fn main() -> anyhow::Result<()> { }); // Pre-build shared adapters for cron scheduler (avoids duplicate Http clients / rate-limit buckets) + #[cfg(feature = "discord")] let shared_discord_adapter: Option> = cfg.discord.as_ref().map(|dc| { let http = Arc::new(serenity::http::Http::new(&dc.bot_token)); Arc::new(discord::DiscordAdapter::new(http)) as Arc }); + #[cfg(not(feature = "discord"))] + let shared_discord_adapter: Option> = None; let session_ttl_dur = std::time::Duration::from_secs(ttl_secs); // Initialize multibot cache (persists to $HOME/.openab/cache/threads.json) @@ -294,6 +301,7 @@ async fn main() -> anyhow::Result<()> { .join("threads.json"); let multibot_cache = multibot_cache::MultibotCache::load(multibot_cache_path); + #[cfg(feature = "slack")] let shared_slack_adapter: Option> = cfg.slack.as_ref().map(|s| { Arc::new(slack::SlackAdapter::new( s.bot_token.clone(), @@ -303,6 +311,8 @@ async fn main() -> anyhow::Result<()> { multibot_cache.clone(), )) }); + #[cfg(not(feature = "slack"))] + let shared_slack_adapter: Option> = None; // Shared slot for Discord ShardMessenger (set in ready handler, used by ctl for agent.status) let ctl_shard: Arc> = @@ -342,6 +352,7 @@ async fn main() -> anyhow::Result<()> { cron::validate_cronjobs(&cfg.cron.jobs, &configured_platforms)?; // Spawn Slack adapter (background task) + #[cfg(feature = "slack")] let slack_handle = if let Some(slack_cfg) = cfg.slack { let allow_all_channels = config::resolve_allow_all(slack_cfg.allow_all_channels, &slack_cfg.allowed_channels); @@ -407,6 +418,8 @@ async fn main() -> anyhow::Result<()> { } else { None }; + #[cfg(not(feature = "slack"))] + let slack_handle: Option> = None; // Spawn Gateway adapter (background task) let gateway_handle = if let Some(gw_cfg) = cfg.gateway { @@ -481,9 +494,11 @@ async fn main() -> anyhow::Result<()> { let cron_router = router.clone(); let mut cron_adapters: std::collections::HashMap> = std::collections::HashMap::new(); + #[cfg(feature = "discord")] if let Some(ref a) = shared_discord_adapter { cron_adapters.insert("discord".into(), a.clone()); } + #[cfg(feature = "slack")] if let Some(ref a) = shared_slack_adapter { cron_adapters.insert("slack".into(), a.clone() as Arc); } @@ -506,6 +521,7 @@ async fn main() -> anyhow::Result<()> { }; // Run Discord adapter (foreground, blocking) or wait for ctrl_c + #[cfg(feature = "discord")] if let Some(discord_cfg) = cfg.discord { let allow_all_channels = config::resolve_allow_all( discord_cfg.allow_all_channels, @@ -628,6 +644,12 @@ async fn main() -> anyhow::Result<()> { shutdown_signal().await; info!("shutdown signal received"); } + #[cfg(not(feature = "discord"))] + { + info!("running without discord, press ctrl+c to stop"); + shutdown_signal().await; + info!("shutdown signal received"); + } // Cleanup cleanup_handle.abort();