From c5e5ae170c521f926deebe7d29a8df2afb5e7ae7 Mon Sep 17 00:00:00 2001 From: Steven Enamakel <31011319+senamakel@users.noreply.github.com> Date: Tue, 31 Mar 2026 14:16:12 -0700 Subject: [PATCH] =?UTF-8?q?ci:=20speed=20up=20GitHub=20Actions=20builds=20?= =?UTF-8?q?(~14m=20=E2=86=92=20~3-5m=20warm)=20(#136)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: add CI profile for faster compilation in Cargo.toml files - Introduced a new `[profile.ci]` section in both root and Tauri Cargo.toml files to optimize build settings for continuous integration. - Adjusted compilation parameters to prioritize speed over runtime performance, including reduced optimization level and enabled code generation units. * refactor(tests): update agent test setup to return temporary directory - Modified the `build_agent_with` function calls in the agent tests to return a temporary directory alongside the agent instance, improving resource management during tests. - Ensured consistency in test setup across multiple test functions. * chore: update .gitignore to include fastembed_cache - Added 'workflow' and '.fastembed_cache' to the .gitignore file to prevent unnecessary files from being tracked in the repository. * test: enhance dispatch routing tests with panic handling Updated the `dispatch_routes_memory_doc_ingest` test to use `AssertUnwindSafe` and `catch_unwind` for better handling of potential panics during execution. This ensures that the test verifies route existence even if the handler encounters a panic, improving robustness against shared state issues in concurrent tests. * ci: speed up builds with rust-cache, sccache, mold linker, and CI profile - Replace manual Cargo registry cache with Swatinem/rust-cache@v2 (caches target/ directories for both core and Tauri crates) - Add mozilla-actions/sccache for cross-branch compilation caching - Install mold linker on Linux for faster linking - Use --profile ci for sidecar build (opt-level 1, codegen-units 16) - Override release profile env vars for Tauri build with CI-tuned settings - Add --bundles none to CI build (skip unused deb/appimage packaging) - Restrict push triggers to main branch only (PRs already cover feature branches, preventing duplicate runs) Co-Authored-By: Claude Opus 4.6 (1M context) * fix(ci): unset RUSTC_WRAPPER for cargo fmt to avoid sccache errors The sccache-action sets RUSTC_WRAPPER globally for the job. cargo fmt invokes rustc through sccache which fails if the GHA cache service is unavailable. Clear RUSTC_WRAPPER for fmt steps and remove redundant per-step env overrides (the action already sets them job-wide). Co-Authored-By: Claude Opus 4.6 (1M context) * refactor: implement Default trait for various structs and enums - Added Default implementations for ConnectionStatus, AgentBuilder, NativeRuntime, AutocompleteEngine, CliChannel, ActionTracker, SkillStatus, and ExtractionMode to streamline object initialization. - Simplified condition checks in several places by replacing map_or with is_some_and and is_none_or for better readability and performance. - Updated various instances of string handling in message sending to remove unnecessary conversions. This refactor enhances code clarity and consistency across the codebase. * style: clean up whitespace and improve code formatting - Removed unnecessary blank lines in several files to enhance code readability. - Simplified condition checks by consolidating method calls into single lines for better clarity. - Improved formatting in various functions to maintain consistency across the codebase. * fix(ci): use --bundles deb instead of unsupported none value Tauri CLI on this version doesn't support 'none' as a bundle type. Use 'deb' as the lightest single bundle to minimize packaging time. Co-Authored-By: Claude Opus 4.6 (1M context) * feat(ci): add Dockerfile and CI workflows for building and pushing Docker images - Introduced a Dockerfile to set up a CI environment with necessary dependencies for Tauri, Rust, Node.js, and sccache. - Created a new workflow to build and push the CI Docker image to GitHub Container Registry on main branch pushes. - Updated existing workflows to utilize the new Docker image for building and testing, enhancing consistency and efficiency in CI processes. * chore(ci): update container image references in CI workflows - Removed unnecessary permissions for packages in build, test, and typecheck workflows. - Updated container image references to use a specific digest instead of the latest tag for improved stability and reproducibility in CI processes. * fix(ci): use correct amd64 Docker image digest Previous digest was from arm64 build (Mac). Rebuilt with --platform linux/amd64 for GitHub Actions runners. Co-Authored-By: Claude Opus 4.6 (1M context) * fix(ci): use tag instead of digest for Docker image reference GHCR doesn't support pulling OCI index manifests by digest reliably. Use the rust-1.93.0 tag which is pinned to a specific Rust version and resolves correctly on amd64 CI runners. Co-Authored-By: Claude Opus 4.6 (1M context) * fix(ci): rename Docker package to openhuman_ci Move from nested ghcr.io/tinyhumansai/openhuman/ci-runner to ghcr.io/tinyhumansai/openhuman_ci to avoid GHCR nested package manifest resolution issues. Co-Authored-By: Claude Opus 4.6 (1M context) * fix(ci): remove sccache env vars from container jobs sccache can't access the GHA cache API from inside a Docker container (missing ACTIONS_CACHE_URL/ACTIONS_RUNTIME_TOKEN). Swatinem/rust-cache already caches target/ which provides the main build speedup. Co-Authored-By: Claude Opus 4.6 (1M context) * chore: update installer smoke workflow to trigger on main branch pushes - Added a trigger for the installer smoke workflow to run on pushes to the main branch, enhancing CI coverage for mainline changes. * fix: enhance Sentry DSN retrieval logic - Updated the Sentry DSN retrieval process to include an additional fallback option using `option_env!`, ensuring that the DSN can be sourced from both environment variables and optional configuration, improving robustness in observability setup. * chore: add OPENHUMAN_SENTRY_DSN to release workflow and example secrets - Included the OPENHUMAN_SENTRY_DSN variable in the release workflow configuration to enhance observability setup. - Updated the ci-secrets.example.json file to include a placeholder for OPENHUMAN_SENTRY_DSN, providing clarity for developers on required environment variables. --------- Co-authored-by: Claude Opus 4.6 (1M context) --- .github/Dockerfile | 43 ++++++++++ .github/workflows/build.yml | 51 ++++-------- .github/workflows/docker-ci-image.yml | 38 +++++++++ .github/workflows/installer-smoke.yml | 1 + .github/workflows/release.yml | 1 + .github/workflows/test.yml | 78 +++---------------- .github/workflows/typecheck.yml | 32 ++------ .gitignore | 3 +- Cargo.toml | 10 +++ app/src-tauri/Cargo.toml | 10 +++ scripts/ci-secrets.example.json | 1 + src/api/models/socket.rs | 8 +- src/core/socketio.rs | 2 +- src/main.rs | 2 + src/openhuman/agent/agent.rs | 6 ++ src/openhuman/agent/host_runtime.rs | 6 ++ src/openhuman/agent/loop_/history.rs | 4 +- src/openhuman/agent/loop_/tool_loop.rs | 2 +- src/openhuman/agent/multimodal.rs | 5 +- src/openhuman/autocomplete/core.rs | 9 ++- src/openhuman/channels/cli.rs | 6 ++ .../channels/controllers/definitions.rs | 2 +- src/openhuman/channels/providers/dingtalk.rs | 4 +- src/openhuman/channels/providers/discord.rs | 8 +- src/openhuman/channels/providers/lark.rs | 8 +- src/openhuman/channels/providers/qq.rs | 8 +- src/openhuman/channels/runtime/dispatch.rs | 2 +- src/openhuman/config/schema/load.rs | 5 +- src/openhuman/memory/embeddings.rs | 1 - src/openhuman/memory/ingestion.rs | 12 +-- src/openhuman/memory/relex.rs | 16 ++-- src/openhuman/migration/core.rs | 10 +-- src/openhuman/providers/compatible.rs | 10 +-- src/openhuman/providers/ops.rs | 2 +- src/openhuman/screen_intelligence/capture.rs | 2 +- src/openhuman/security/policy.rs | 6 ++ src/openhuman/service/core.rs | 4 +- .../quickjs_libs/qjs_ops/ops_storage.rs | 50 +++++------- src/openhuman/skills/registry_ops.rs | 6 +- src/openhuman/skills/socket_manager.rs | 4 +- src/openhuman/skills/types.rs | 8 +- src/openhuman/skills/utils.rs | 2 +- src/rpc/dispatch.rs | 2 + 43 files changed, 253 insertions(+), 237 deletions(-) create mode 100644 .github/Dockerfile create mode 100644 .github/workflows/docker-ci-image.yml diff --git a/.github/Dockerfile b/.github/Dockerfile new file mode 100644 index 000000000..9ae630a8a --- /dev/null +++ b/.github/Dockerfile @@ -0,0 +1,43 @@ +FROM ubuntu:22.04 + +ENV DEBIAN_FRONTEND=noninteractive + +# System deps for Tauri + mold linker + clang (for mold) +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + curl \ + ca-certificates \ + git \ + pkg-config \ + libgtk-3-dev \ + libwebkit2gtk-4.1-dev \ + libappindicator3-dev \ + librsvg2-dev \ + patchelf \ + mold \ + clang \ + libssl-dev \ + && rm -rf /var/lib/apt/lists/* + +# Rust 1.93.0 with minimal profile + fmt/clippy +ENV RUSTUP_HOME=/usr/local/rustup \ + CARGO_HOME=/usr/local/cargo \ + PATH="/usr/local/cargo/bin:$PATH" +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- \ + -y --default-toolchain 1.93.0 --profile minimal \ + -c rustfmt -c clippy \ + -t x86_64-unknown-linux-gnu + +# Node.js 24.x + yarn +RUN curl -fsSL https://deb.nodesource.com/setup_24.x | bash - \ + && apt-get install -y --no-install-recommends nodejs \ + && npm install -g yarn \ + && rm -rf /var/lib/apt/lists/* + +# sccache (pre-installed so the action only needs to configure it) +RUN curl -fsSL https://github.com/mozilla/sccache/releases/download/v0.10.0/sccache-v0.10.0-x86_64-unknown-linux-musl.tar.gz \ + | tar xz -C /usr/local/bin --strip-components=1 sccache-v0.10.0-x86_64-unknown-linux-musl/sccache \ + && chmod +x /usr/local/bin/sccache + +# Verify installs +RUN rustc --version && cargo --version && node --version && yarn --version && mold --version && sccache --version diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3b0f646b1..e7e63b860 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,6 +2,7 @@ name: Build on: push: + branches: [main] pull_request: permissions: @@ -16,6 +17,8 @@ jobs: build: name: Build Tauri App runs-on: ubuntu-22.04 + container: + image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0 steps: - name: Checkout code uses: actions/checkout@v4 @@ -23,38 +26,13 @@ jobs: fetch-depth: 1 submodules: true - - name: Setup Node.js 24.x - uses: actions/setup-node@v4 + - name: Cache Rust build artifacts + uses: Swatinem/rust-cache@v2 with: - node-version: 24.x - cache: "yarn" - - - name: Install Rust (rust-toolchain.toml) - uses: dtolnay/rust-toolchain@1.93.0 - with: - targets: x86_64-unknown-linux-gnu - - - name: Install Tauri dependencies - run: | - sudo apt-get update - sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf - - # Skip first 7 lines of Cargo.lock (workspace package version bumps) so the key tracks dependency changes only - - name: Cargo.lock fingerprint (deps only) - id: cargo-lock-fingerprint - shell: bash - run: | - echo "hash=$(tail -n +8 Cargo.lock | openssl dgst -sha256 | awk '{print $2}')" >> "$GITHUB_OUTPUT" - - - name: Cache Cargo registry and git sources - uses: actions/cache@v4 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - key: ${{ runner.os }}-cargo-registry-${{ steps.cargo-lock-fingerprint.outputs.hash }} - restore-keys: | - ${{ runner.os }}-cargo-registry- + workspaces: | + . -> target + app/src-tauri -> target + cache-on-failure: true - name: Cache node modules id: yarn-cache @@ -70,18 +48,23 @@ jobs: run: yarn install --frozen-lockfile - name: Build sidecar core binary - run: cargo build --manifest-path Cargo.toml --release --target x86_64-unknown-linux-gnu --bin openhuman-core + run: cargo build --profile ci --target x86_64-unknown-linux-gnu --bin openhuman-core - name: Stage sidecar for Tauri bundler run: | mkdir -p app/src-tauri/binaries - cp target/x86_64-unknown-linux-gnu/release/openhuman-core app/src-tauri/binaries/openhuman-core-x86_64-unknown-linux-gnu + cp target/x86_64-unknown-linux-gnu/ci/openhuman-core app/src-tauri/binaries/openhuman-core-x86_64-unknown-linux-gnu chmod +x app/src-tauri/binaries/openhuman-core-x86_64-unknown-linux-gnu - name: Build Tauri app working-directory: app run: | TAURI_CONFIG_OVERRIDE='{"plugins":{"updater":{"active":false}}}' - yarn tauri build -c "$TAURI_CONFIG_OVERRIDE" + yarn tauri build -c "$TAURI_CONFIG_OVERRIDE" --bundles deb env: NODE_ENV: production + CARGO_PROFILE_RELEASE_OPT_LEVEL: "1" + CARGO_PROFILE_RELEASE_CODEGEN_UNITS: "16" + CARGO_PROFILE_RELEASE_LTO: "false" + CARGO_PROFILE_RELEASE_STRIP: "true" + CARGO_PROFILE_RELEASE_DEBUG: "false" diff --git a/.github/workflows/docker-ci-image.yml b/.github/workflows/docker-ci-image.yml new file mode 100644 index 000000000..30a140b30 --- /dev/null +++ b/.github/workflows/docker-ci-image.yml @@ -0,0 +1,38 @@ +name: Build CI Docker Image + +on: + push: + branches: [main] + paths: + - ".github/Dockerfile" + - ".github/workflows/docker-ci-image.yml" + workflow_dispatch: + +permissions: + contents: read + packages: write + +jobs: + build-image: + name: Build and push CI image + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: .github + file: .github/Dockerfile + push: true + tags: | + ghcr.io/tinyhumansai/openhuman_ci:latest + ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0 diff --git a/.github/workflows/installer-smoke.yml b/.github/workflows/installer-smoke.yml index ac95bc949..0244b2e3a 100644 --- a/.github/workflows/installer-smoke.yml +++ b/.github/workflows/installer-smoke.yml @@ -2,6 +2,7 @@ name: Installer Smoke on: push: + branches: [main] pull_request: workflow_dispatch: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 926461442..70a06064a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -375,6 +375,7 @@ jobs: MATRIX_TARGET: ${{ matrix.settings.target }} CORE_MANIFEST: ${{ steps.core-paths.outputs.core_manifest }} CORE_BIN_NAME: ${{ steps.core-paths.outputs.core_bin_name }} + OPENHUMAN_SENTRY_DSN: ${{ vars.OPENHUMAN_SENTRY_DSN }} - name: Stage sidecar for Tauri bundler shell: bash diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 81404a710..c70d1dcba 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,6 +2,7 @@ name: Test on: push: + branches: [main] pull_request: permissions: @@ -58,39 +59,21 @@ jobs: rust-tests: name: Rust Tests + Quality runs-on: ubuntu-22.04 + container: + image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0 steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 1 - - name: Install Rust (rust-toolchain.toml) - uses: dtolnay/rust-toolchain@1.93.0 + - name: Cache Rust build artifacts + uses: Swatinem/rust-cache@v2 with: - components: rustfmt, clippy - targets: x86_64-unknown-linux-gnu - - - name: Install Tauri build dependencies - run: | - sudo apt-get update - sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf - - # Match build.yml: lockfile body only (skip workspace version header noise) - - name: Cargo.lock fingerprint (deps only) - id: cargo-lock-fingerprint - shell: bash - run: | - echo "hash=$(tail -n +8 Cargo.lock | openssl dgst -sha256 | awk '{print $2}')" >> "$GITHUB_OUTPUT" - - - name: Cache Cargo registry and git sources - uses: actions/cache@v4 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - key: ${{ runner.os }}-rust-test-cargo-${{ steps.cargo-lock-fingerprint.outputs.hash }} - restore-keys: | - ${{ runner.os }}-rust-test-cargo- + workspaces: | + . -> target + app/src-tauri -> target + cache-on-failure: true - name: Check formatting (cargo fmt) run: cargo fmt --all -- --check @@ -102,52 +85,13 @@ jobs: run: cargo test -p openhuman - name: Build sidecar core binary - run: cargo build --manifest-path Cargo.toml --release --target x86_64-unknown-linux-gnu --bin openhuman-core + run: cargo build --profile ci --target x86_64-unknown-linux-gnu --bin openhuman-core - name: Stage sidecar for Tauri shell tests run: | mkdir -p app/src-tauri/binaries - cp target/x86_64-unknown-linux-gnu/release/openhuman-core app/src-tauri/binaries/openhuman-core-x86_64-unknown-linux-gnu + cp target/x86_64-unknown-linux-gnu/ci/openhuman-core app/src-tauri/binaries/openhuman-core-x86_64-unknown-linux-gnu chmod +x app/src-tauri/binaries/openhuman-core-x86_64-unknown-linux-gnu - name: Test Tauri shell (OpenHuman) run: cargo test --manifest-path app/src-tauri/Cargo.toml - - # e2e-macos: - # name: E2E (macOS / Appium) - # runs-on: macos-latest - # timeout-minutes: 90 - # steps: - # - name: Checkout code - # uses: actions/checkout@v4 - # with: - # fetch-depth: 1 - # submodules: recursive - - # - name: Setup Node.js 24.x - # uses: actions/setup-node@v4 - # with: - # node-version: 24.x - # cache: "yarn" - - # - name: Install Rust (rust-toolchain.toml) - # uses: dtolnay/rust-toolchain@1.93.0 - - # - name: Install dependencies - # run: yarn install --frozen-lockfile - - # - name: Ensure .env exists for E2E build - # run: | - # touch .env - # touch app/.env - - # - name: Install Appium and mac2 driver - # run: | - # npm install -g appium - # appium driver install mac2 - - # - name: Build E2E app bundle - # run: yarn workspace openhuman-app test:e2e:build - - # - name: Run all E2E flows - # run: yarn workspace openhuman-app test:e2e:all:flows diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index 0e6225346..ce9e2ca95 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -2,6 +2,7 @@ name: Type Check on: push: + branches: [main] pull_request: permissions: @@ -61,37 +62,20 @@ jobs: rust-quality: name: Rust Quality (fmt + clippy) runs-on: ubuntu-22.04 + container: + image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0 steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 1 - - name: Install Rust (rust-toolchain.toml) - uses: dtolnay/rust-toolchain@1.93.0 + - name: Cache Rust build artifacts + uses: Swatinem/rust-cache@v2 with: - components: rustfmt, clippy - - - name: Install Tauri build dependencies - run: | - sudo apt-get update - sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf - - - name: Cargo.lock fingerprint (deps only) - id: cargo-lock-fingerprint - shell: bash - run: | - echo "hash=$(tail -n +8 Cargo.lock | openssl dgst -sha256 | awk '{print $2}')" >> "$GITHUB_OUTPUT" - - - name: Cache Cargo registry and git sources - uses: actions/cache@v4 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - key: ${{ runner.os }}-cargo-registry-${{ steps.cargo-lock-fingerprint.outputs.hash }} - restore-keys: | - ${{ runner.os }}-cargo-registry- + workspaces: | + . -> target + cache-on-failure: true - name: Check formatting (cargo fmt) run: cargo fmt --all -- --check diff --git a/.gitignore b/.gitignore index 28485b570..e60004bbc 100644 --- a/.gitignore +++ b/.gitignore @@ -57,4 +57,5 @@ tauri.key.pub /target/ src-tauri/target/ -workflow \ No newline at end of file +workflow +.fastembed_cache diff --git a/Cargo.toml b/Cargo.toml index 003568010..49ef87dfe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -120,3 +120,13 @@ landlock = ["sandbox-landlock"] probe = ["dep:probe-rs"] rag-pdf = ["dep:pdf-extract"] whatsapp-web = ["dep:wa-rs", "dep:wa-rs-core", "dep:wa-rs-binary", "dep:wa-rs-proto", "dep:wa-rs-ureq-http", "dep:wa-rs-tokio-transport", "serde-big-array"] + +# Fast CI builds: trade runtime perf for compile speed +[profile.ci] +inherits = "release" +opt-level = 1 +codegen-units = 16 +lto = false +incremental = false +strip = true +debug = false diff --git a/app/src-tauri/Cargo.toml b/app/src-tauri/Cargo.toml index 6e544db7a..1f032895c 100644 --- a/app/src-tauri/Cargo.toml +++ b/app/src-tauri/Cargo.toml @@ -40,3 +40,13 @@ env_logger = "0.11" # This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!! custom-protocol = ["tauri/custom-protocol"] sandbox-bubblewrap = [] + +# Fast CI builds: trade runtime perf for compile speed +[profile.ci] +inherits = "release" +opt-level = 1 +codegen-units = 16 +lto = false +incremental = false +strip = true +debug = false diff --git a/scripts/ci-secrets.example.json b/scripts/ci-secrets.example.json index a7afd3d30..8391e318c 100644 --- a/scripts/ci-secrets.example.json +++ b/scripts/ci-secrets.example.json @@ -19,6 +19,7 @@ "UPDATER_ENDPOINT": "", "VITE_BACKEND_URL": "https://localhost:5005", "VITE_SKILLS_GITHUB_REPO": "", + "OPENHUMAN_SENTRY_DSN": "", "VITE_SENTRY_DSN": "", "VITE_DEBUG": "true" } diff --git a/src/api/models/socket.rs b/src/api/models/socket.rs index 6ffea61e3..49e1e7435 100644 --- a/src/api/models/socket.rs +++ b/src/api/models/socket.rs @@ -3,7 +3,9 @@ use serde::{Deserialize, Serialize}; /// Socket connection status #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] +#[derive(Default)] pub enum ConnectionStatus { + #[default] Disconnected, Connecting, Connected, @@ -11,12 +13,6 @@ pub enum ConnectionStatus { Error, } -impl Default for ConnectionStatus { - fn default() -> Self { - Self::Disconnected - } -} - /// Socket connection state emitted to frontend #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SocketState { diff --git a/src/core/socketio.rs b/src/core/socketio.rs index ce4d7dee9..2b676a15e 100644 --- a/src/core/socketio.rs +++ b/src/core/socketio.rs @@ -247,7 +247,7 @@ fn emit_with_aliases(socket: &SocketRef, name: &str, payload: &serde_json::Value } fn emit_room_with_aliases(io: &SocketIo, room: &str, name: &str, payload: &serde_json::Value) { - let _ = io.to(room.to_string()).emit(name.to_string(), payload); + let _ = io.to(room.to_string()).emit(name, payload); if let Some(alias) = event_alias(name) { let _ = io.to(room.to_string()).emit(alias, payload); } diff --git a/src/main.rs b/src/main.rs index f164eb86c..f2f1f9fb2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,8 @@ fn main() { dsn: std::env::var("OPENHUMAN_SENTRY_DSN") .ok() .filter(|s| !s.is_empty()) + .or_else(|| option_env!("OPENHUMAN_SENTRY_DSN").map(|s| s.to_string())) + .filter(|s| !s.is_empty()) .and_then(|s| s.parse().ok()), release: Some(std::borrow::Cow::Borrowed(env!("CARGO_PKG_VERSION"))), environment: Some(if cfg!(debug_assertions) { diff --git a/src/openhuman/agent/agent.rs b/src/openhuman/agent/agent.rs index cba70389f..942874ad5 100644 --- a/src/openhuman/agent/agent.rs +++ b/src/openhuman/agent/agent.rs @@ -54,6 +54,12 @@ pub struct AgentBuilder { available_hints: Option>, } +impl Default for AgentBuilder { + fn default() -> Self { + Self::new() + } +} + impl AgentBuilder { pub fn new() -> Self { Self { diff --git a/src/openhuman/agent/host_runtime.rs b/src/openhuman/agent/host_runtime.rs index 4bde8dcaf..a06d2c092 100644 --- a/src/openhuman/agent/host_runtime.rs +++ b/src/openhuman/agent/host_runtime.rs @@ -7,6 +7,12 @@ pub use crate::openhuman::agent::traits::RuntimeAdapter; pub struct NativeRuntime; +impl Default for NativeRuntime { + fn default() -> Self { + Self::new() + } +} + impl NativeRuntime { pub const fn new() -> Self { Self diff --git a/src/openhuman/agent/loop_/history.rs b/src/openhuman/agent/loop_/history.rs index 602f11db1..d4425767b 100644 --- a/src/openhuman/agent/loop_/history.rs +++ b/src/openhuman/agent/loop_/history.rs @@ -27,7 +27,7 @@ pub(crate) fn autosave_memory_key(prefix: &str) -> String { /// Preserves the system prompt (first message if role=system) and the most recent messages. pub(crate) fn trim_history(history: &mut Vec, max_history: usize) { // Nothing to trim if within limit - let has_system = history.first().map_or(false, |m| m.role == "system"); + let has_system = history.first().is_some_and(|m| m.role == "system"); let non_system_count = if has_system { history.len() - 1 } else { @@ -74,7 +74,7 @@ pub(crate) async fn auto_compact_history( max_history: usize, config: &Config, ) -> Result { - let has_system = history.first().map_or(false, |m| m.role == "system"); + let has_system = history.first().is_some_and(|m| m.role == "system"); let non_system_count = if has_system { history.len().saturating_sub(1) } else { diff --git a/src/openhuman/agent/loop_/tool_loop.rs b/src/openhuman/agent/loop_/tool_loop.rs index 11b60357a..f51bcca21 100644 --- a/src/openhuman/agent/loop_/tool_loop.rs +++ b/src/openhuman/agent/loop_/tool_loop.rs @@ -226,7 +226,7 @@ pub(crate) async fn run_tool_call_loop( if r.success { scrub_credentials(&r.output) } else { - format!("Error: {}", r.error.unwrap_or_else(|| r.output)) + format!("Error: {}", r.error.unwrap_or(r.output)) } } Err(e) => { diff --git a/src/openhuman/agent/multimodal.rs b/src/openhuman/agent/multimodal.rs index 6fe514d1f..cb2e3c082 100644 --- a/src/openhuman/agent/multimodal.rs +++ b/src/openhuman/agent/multimodal.rs @@ -364,10 +364,7 @@ fn validate_size(source: &str, size_bytes: usize, max_bytes: usize) -> anyhow::R } fn validate_mime(source: &str, mime: &str) -> anyhow::Result<()> { - if ALLOWED_IMAGE_MIME_TYPES - .iter() - .any(|allowed| *allowed == mime) - { + if ALLOWED_IMAGE_MIME_TYPES.contains(&mime) { return Ok(()); } diff --git a/src/openhuman/autocomplete/core.rs b/src/openhuman/autocomplete/core.rs index d60a3db4d..066db8841 100644 --- a/src/openhuman/autocomplete/core.rs +++ b/src/openhuman/autocomplete/core.rs @@ -241,6 +241,12 @@ pub struct AutocompleteEngine { inner: Mutex, } +impl Default for AutocompleteEngine { + fn default() -> Self { + Self::new() + } +} + impl AutocompleteEngine { pub fn new() -> Self { Self { @@ -908,8 +914,7 @@ fn show_overflow_badge( fn escape_osascript_text(raw: &str) -> String { raw.replace('\\', "\\\\") .replace('\"', "\\\"") - .replace('\n', " ") - .replace('\r', " ") + .replace(['\n', '\r'], " ") } #[cfg(target_os = "macos")] diff --git a/src/openhuman/channels/cli.rs b/src/openhuman/channels/cli.rs index e42f1891f..d490cd1cb 100644 --- a/src/openhuman/channels/cli.rs +++ b/src/openhuman/channels/cli.rs @@ -6,6 +6,12 @@ use uuid::Uuid; /// Console channel — stdin/stdout, not used in the web UI, zero deps pub struct CliChannel; +impl Default for CliChannel { + fn default() -> Self { + Self::new() + } +} + impl CliChannel { pub fn new() -> Self { Self diff --git a/src/openhuman/channels/controllers/definitions.rs b/src/openhuman/channels/controllers/definitions.rs index 2187297a1..d70185d2a 100644 --- a/src/openhuman/channels/controllers/definitions.rs +++ b/src/openhuman/channels/controllers/definitions.rs @@ -130,7 +130,7 @@ impl ChannelDefinition { .filter(|f| { credentials .get(f.key) - .map_or(true, |v| v.as_str().map_or(false, |s| s.is_empty())) + .is_none_or(|v| v.as_str().is_some_and(|s| s.is_empty())) }) .map(|f| f.key) .collect(); diff --git a/src/openhuman/channels/providers/dingtalk.rs b/src/openhuman/channels/providers/dingtalk.rs index d05b37f93..8b935414c 100644 --- a/src/openhuman/channels/providers/dingtalk.rs +++ b/src/openhuman/channels/providers/dingtalk.rs @@ -195,7 +195,7 @@ impl Channel for DingTalkChannel { "data": "", }); - if let Err(e) = write.send(Message::Text(pong.to_string().into())).await { + if let Err(e) = write.send(Message::Text(pong.to_string())).await { tracing::warn!("DingTalk: failed to send pong: {e}"); break; } @@ -262,7 +262,7 @@ impl Channel for DingTalkChannel { "message": "OK", "data": "", }); - let _ = write.send(Message::Text(ack.to_string().into())).await; + let _ = write.send(Message::Text(ack.to_string())).await; let channel_msg = ChannelMessage { id: Uuid::new_v4().to_string(), diff --git a/src/openhuman/channels/providers/discord.rs b/src/openhuman/channels/providers/discord.rs index 2190b78de..52ba84675 100644 --- a/src/openhuman/channels/providers/discord.rs +++ b/src/openhuman/channels/providers/discord.rs @@ -272,9 +272,7 @@ impl Channel for DiscordChannel { } } }); - write - .send(Message::Text(identify.to_string().into())) - .await?; + write.send(Message::Text(identify.to_string())).await?; tracing::info!("Discord: connected and identified"); @@ -303,7 +301,7 @@ impl Channel for DiscordChannel { _ = hb_rx.recv() => { let d = if sequence >= 0 { json!(sequence) } else { json!(null) }; let hb = json!({"op": 1, "d": d}); - if write.send(Message::Text(hb.to_string().into())).await.is_err() { + if write.send(Message::Text(hb.to_string())).await.is_err() { break; } } @@ -331,7 +329,7 @@ impl Channel for DiscordChannel { 1 => { let d = if sequence >= 0 { json!(sequence) } else { json!(null) }; let hb = json!({"op": 1, "d": d}); - if write.send(Message::Text(hb.to_string().into())).await.is_err() { + if write.send(Message::Text(hb.to_string())).await.is_err() { break; } continue; diff --git a/src/openhuman/channels/providers/lark.rs b/src/openhuman/channels/providers/lark.rs index 9ff6c8151..8b4af0f2c 100644 --- a/src/openhuman/channels/providers/lark.rs +++ b/src/openhuman/channels/providers/lark.rs @@ -288,7 +288,7 @@ impl LarkChannel { payload: None, }; if write - .send(WsMsg::Binary(initial_ping.encode_to_vec().into())) + .send(WsMsg::Binary(initial_ping.encode_to_vec())) .await .is_err() { @@ -309,7 +309,7 @@ impl LarkChannel { headers: vec![PbHeader { key: "type".into(), value: "ping".into() }], payload: None, }; - if write.send(WsMsg::Binary(ping.encode_to_vec().into())).await.is_err() { + if write.send(WsMsg::Binary(ping.encode_to_vec())).await.is_err() { tracing::warn!("Lark: ping failed, reconnecting"); break; } @@ -378,7 +378,7 @@ impl LarkChannel { let mut ack = frame.clone(); ack.payload = Some(br#"{"code":200,"headers":{},"data":[]}"#.to_vec()); ack.headers.push(PbHeader { key: "biz_rt".into(), value: "0".into() }); - let _ = write.send(WsMsg::Binary(ack.encode_to_vec().into())).await; + let _ = write.send(WsMsg::Binary(ack.encode_to_vec())).await; } // Fragment reassembly @@ -739,7 +739,7 @@ impl LarkChannel { let token_ok = payload .get("token") .and_then(|t| t.as_str()) - .map_or(true, |t| t == state.verification_token); + .is_none_or(|t| t == state.verification_token); if !token_ok { return (StatusCode::FORBIDDEN, "invalid token").into_response(); diff --git a/src/openhuman/channels/providers/qq.rs b/src/openhuman/channels/providers/qq.rs index 174eda51e..b4902ac91 100644 --- a/src/openhuman/channels/providers/qq.rs +++ b/src/openhuman/channels/providers/qq.rs @@ -263,9 +263,7 @@ impl Channel for QQChannel { } } }); - write - .send(Message::Text(identify.to_string().into())) - .await?; + write.send(Message::Text(identify.to_string())).await?; tracing::info!("QQ: connected and identified"); @@ -290,7 +288,7 @@ impl Channel for QQChannel { let d = if sequence >= 0 { json!(sequence) } else { json!(null) }; let hb = json!({"op": 1, "d": d}); if write - .send(Message::Text(hb.to_string().into())) + .send(Message::Text(hb.to_string())) .await .is_err() { @@ -322,7 +320,7 @@ impl Channel for QQChannel { let d = if sequence >= 0 { json!(sequence) } else { json!(null) }; let hb = json!({"op": 1, "d": d}); if write - .send(Message::Text(hb.to_string().into())) + .send(Message::Text(hb.to_string())) .await .is_err() { diff --git a/src/openhuman/channels/runtime/dispatch.rs b/src/openhuman/channels/runtime/dispatch.rs index fb78ef034..970115a9d 100644 --- a/src/openhuman/channels/runtime/dispatch.rs +++ b/src/openhuman/channels/runtime/dispatch.rs @@ -145,7 +145,7 @@ pub(crate) async fn process_channel_message( // Determine if this channel supports streaming draft updates let use_streaming = target_channel .as_ref() - .map_or(false, |ch| ch.supports_draft_updates()); + .is_some_and(|ch| ch.supports_draft_updates()); // Set up streaming channel if supported let (delta_tx, delta_rx) = if use_streaming { diff --git a/src/openhuman/config/schema/load.rs b/src/openhuman/config/schema/load.rs index cc31fd26f..92dd2e0bf 100644 --- a/src/openhuman/config/schema/load.rs +++ b/src/openhuman/config/schema/load.rs @@ -592,7 +592,10 @@ impl Config { } } - if let Ok(dsn) = std::env::var("OPENHUMAN_SENTRY_DSN") { + let dsn_value = std::env::var("OPENHUMAN_SENTRY_DSN") + .ok() + .or_else(|| option_env!("OPENHUMAN_SENTRY_DSN").map(|s| s.to_string())); + if let Some(dsn) = dsn_value { let dsn = dsn.trim(); if !dsn.is_empty() { self.observability.sentry_dsn = Some(dsn.to_string()); diff --git a/src/openhuman/memory/embeddings.rs b/src/openhuman/memory/embeddings.rs index 111465354..a8c7a9553 100644 --- a/src/openhuman/memory/embeddings.rs +++ b/src/openhuman/memory/embeddings.rs @@ -150,7 +150,6 @@ fn ensure_fastembed_ort_dylib_path() { if bundled.exists() { env::set_var("ORT_DYLIB_PATH", bundled); - return; } } diff --git a/src/openhuman/memory/ingestion.rs b/src/openhuman/memory/ingestion.rs index e1b3cda6b..3635a157c 100644 --- a/src/openhuman/memory/ingestion.rs +++ b/src/openhuman/memory/ingestion.rs @@ -14,17 +14,13 @@ const DEFAULT_CHUNK_TOKENS: usize = 225; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] +#[derive(Default)] pub enum ExtractionMode { + #[default] Sentence, Chunk, } -impl Default for ExtractionMode { - fn default() -> Self { - Self::Sentence - } -} - impl ExtractionMode { fn as_str(self) -> &'static str { match self { @@ -282,7 +278,7 @@ fn type_allowed(actual: &str, allowed: &[&str]) -> bool { fn resolve_person_alias(name: &str, known_people: &HashMap) -> String { let upper = name.to_uppercase(); - known_people.get(&upper).cloned().unwrap_or_else(|| upper) + known_people.get(&upper).cloned().unwrap_or(upper) } impl ExtractionAccumulator { @@ -1297,7 +1293,7 @@ async fn parse_document( for unit in build_units(&chunks, config.extraction_mode) { if let Some(ref runtime) = relex_runtime { - apply_model_extraction(&runtime, &unit, &mut accumulator, config); + apply_model_extraction(runtime, &unit, &mut accumulator, config); } if let Some(captures) = recipient_regex().captures(&unit.text) { diff --git a/src/openhuman/memory/relex.rs b/src/openhuman/memory/relex.rs index 83d5369dc..1e8740570 100644 --- a/src/openhuman/memory/relex.rs +++ b/src/openhuman/memory/relex.rs @@ -5,6 +5,7 @@ use std::sync::OnceLock; use anyhow::{anyhow, Context, Result}; use futures_util::TryStreamExt; +#[cfg(target_os = "windows")] use glob::glob; use ndarray::{Array, Array2, Array3, Array4, Ix2, Ix3, Ix4}; use ort::{ @@ -433,10 +434,11 @@ async fn resolve_bundle_dir(model_name: &str) -> Option { return Some(bundle_dir); } - if uses_default_bundle(model_name) { - if ensure_managed_bundle(&managed_dir).await.is_ok() && bundle_complete(&managed_dir) { - return Some(managed_dir); - } + if uses_default_bundle(model_name) + && ensure_managed_bundle(&managed_dir).await.is_ok() + && bundle_complete(&managed_dir) + { + return Some(managed_dir); } None @@ -482,6 +484,7 @@ fn model_file_path(bundle_dir: &Path) -> Option { None } +#[allow(unused_variables)] fn ensure_ort_dylib_path(bundle_dir: &Path) { if env::var_os("ORT_DYLIB_PATH").is_some() { return; @@ -505,7 +508,6 @@ fn ensure_ort_dylib_path(bundle_dir: &Path) { let runtime_lib = candidate.join(ORT_DYLIB_FILE_NAME); if runtime_lib.exists() { env::set_var("ORT_DYLIB_PATH", runtime_lib); - return; } } @@ -788,8 +790,8 @@ fn decode_entity_spans( ) -> Vec { let mut spans = Vec::new(); let num_words = tokens.len(); - let width_count = logits.shape().get(2).copied().unwrap_or_default() as usize; - let class_count = logits.shape().get(3).copied().unwrap_or_default() as usize; + let width_count = logits.shape().get(2).copied().unwrap_or_default(); + let class_count = logits.shape().get(3).copied().unwrap_or_default(); for start in 0..num_words { let actual_max_width = max_width diff --git a/src/openhuman/migration/core.rs b/src/openhuman/migration/core.rs index 08d608c34..d21425cf1 100644 --- a/src/openhuman/migration/core.rs +++ b/src/openhuman/migration/core.rs @@ -385,12 +385,10 @@ fn pick_column_expr<'a>( } fn pick_optional_column_expr<'a>(columns: &'a [String], candidates: &[&'a str]) -> Option<&'a str> { - for candidate in candidates { - if columns.iter().any(|c| c.eq_ignore_ascii_case(candidate)) { - return Some(candidate); - } - } - None + candidates + .iter() + .find(|&candidate| columns.iter().any(|c| c.eq_ignore_ascii_case(candidate))) + .map(|v| v as _) } async fn next_available_key(memory: &dyn Memory, key: &str) -> Result { diff --git a/src/openhuman/providers/compatible.rs b/src/openhuman/providers/compatible.rs index 41c7cb267..36b2e77b1 100644 --- a/src/openhuman/providers/compatible.rs +++ b/src/openhuman/providers/compatible.rs @@ -1138,10 +1138,7 @@ impl Provider for OpenAiCompatibleProvider { // If tool_calls are present, serialize the full message as JSON // so parse_tool_calls can handle the OpenAI-style format if c.message.tool_calls.is_some() - && c.message - .tool_calls - .as_ref() - .map_or(false, |t| !t.is_empty()) + && c.message.tool_calls.as_ref().is_some_and(|t| !t.is_empty()) { serde_json::to_string(&c.message) .unwrap_or_else(|_| c.message.effective_content()) @@ -1245,10 +1242,7 @@ impl Provider for OpenAiCompatibleProvider { // If tool_calls are present, serialize the full message as JSON // so parse_tool_calls can handle the OpenAI-style format if c.message.tool_calls.is_some() - && c.message - .tool_calls - .as_ref() - .map_or(false, |t| !t.is_empty()) + && c.message.tool_calls.as_ref().is_some_and(|t| !t.is_empty()) { serde_json::to_string(&c.message) .unwrap_or_else(|_| c.message.effective_content()) diff --git a/src/openhuman/providers/ops.rs b/src/openhuman/providers/ops.rs index 0daaa0277..8dbac0d64 100644 --- a/src/openhuman/providers/ops.rs +++ b/src/openhuman/providers/ops.rs @@ -170,7 +170,7 @@ pub fn create_backend_inference_provider( ) -> anyhow::Result> { let resolved = resolve_provider_credential(INFERENCE_BACKEND_ID, api_key) .map(|v| String::from_utf8(v.into_bytes()).unwrap_or_default()); - let key = resolved.as_ref().map(String::as_str); + let key = resolved.as_deref(); Ok(Box::new(openhuman_backend::OpenHumanBackendProvider::new( api_url, key, options, ))) diff --git a/src/openhuman/screen_intelligence/capture.rs b/src/openhuman/screen_intelligence/capture.rs index 568f3922a..c67bacd06 100644 --- a/src/openhuman/screen_intelligence/capture.rs +++ b/src/openhuman/screen_intelligence/capture.rs @@ -146,7 +146,7 @@ pub(crate) fn capture_screen_image_ref_for_context( } let encoded = BASE64_STANDARD.encode(bytes); - return Ok(format!("data:image/png;base64,{encoded}")); + Ok(format!("data:image/png;base64,{encoded}")) } #[cfg(not(target_os = "macos"))] diff --git a/src/openhuman/security/policy.rs b/src/openhuman/security/policy.rs index 4fedc9d41..0f5aa5d96 100644 --- a/src/openhuman/security/policy.rs +++ b/src/openhuman/security/policy.rs @@ -39,6 +39,12 @@ pub struct ActionTracker { actions: Mutex>, } +impl Default for ActionTracker { + fn default() -> Self { + Self::new() + } +} + impl ActionTracker { pub fn new() -> Self { Self { diff --git a/src/openhuman/service/core.rs b/src/openhuman/service/core.rs index 1a4c244d0..22755ec46 100644 --- a/src/openhuman/service/core.rs +++ b/src/openhuman/service/core.rs @@ -33,7 +33,7 @@ pub fn install(config: &Config) -> Result { #[cfg(target_os = "macos")] { macos::install(config)?; - return status(config); + status(config) } #[cfg(target_os = "linux")] { @@ -72,7 +72,7 @@ pub fn stop(config: &Config) -> Result { #[cfg(target_os = "macos")] { macos::stop(config)?; - return status(config); + status(config) } #[cfg(target_os = "linux")] { diff --git a/src/openhuman/skills/quickjs_libs/qjs_ops/ops_storage.rs b/src/openhuman/skills/quickjs_libs/qjs_ops/ops_storage.rs index 1a4b9af4e..32d491ffe 100644 --- a/src/openhuman/skills/quickjs_libs/qjs_ops/ops_storage.rs +++ b/src/openhuman/skills/quickjs_libs/qjs_ops/ops_storage.rs @@ -22,7 +22,7 @@ pub fn register<'js>( Function::new( ctx.clone(), move |name: String, version: u32| -> rquickjs::Result { - let result = s.open_database(&name, version).map_err(|e| js_err(e))?; + let result = s.open_database(&name, version).map_err(js_err)?; serde_json::to_string(&result).map_err(|e| js_err(e.to_string())) }, ), @@ -44,7 +44,7 @@ pub fn register<'js>( ops.set( "idb_delete_database", Function::new(ctx.clone(), move |name: String| -> rquickjs::Result<()> { - s.delete_database(&name).map_err(|e| js_err(e)) + s.delete_database(&name).map_err(js_err) }), )?; } @@ -64,7 +64,7 @@ pub fn register<'js>( let key_path = opts["keyPath"].as_str(); let auto_increment = opts["autoIncrement"].as_bool().unwrap_or(false); s.create_object_store(&db_name, &store_name, key_path, auto_increment) - .map_err(|e| js_err(e)) + .map_err(js_err) }, ), )?; @@ -77,8 +77,7 @@ pub fn register<'js>( Function::new( ctx.clone(), move |db_name: String, store_name: String| -> rquickjs::Result<()> { - s.delete_object_store(&db_name, &store_name) - .map_err(|e| js_err(e)) + s.delete_object_store(&db_name, &store_name).map_err(js_err) }, ), )?; @@ -96,9 +95,7 @@ pub fn register<'js>( -> rquickjs::Result { let key_val: serde_json::Value = serde_json::from_str(&key).map_err(|e| js_err(e.to_string()))?; - let result = s - .get(&db_name, &store_name, &key_val) - .map_err(|e| js_err(e))?; + let result = s.get(&db_name, &store_name, &key_val).map_err(js_err)?; serde_json::to_string(&result).map_err(|e| js_err(e.to_string())) }, ), @@ -121,7 +118,7 @@ pub fn register<'js>( let value_val: serde_json::Value = serde_json::from_str(&value).map_err(|e| js_err(e.to_string()))?; s.put(&db_name, &store_name, &key_val, &value_val) - .map_err(|e| js_err(e)) + .map_err(js_err) }, ), )?; @@ -136,8 +133,7 @@ pub fn register<'js>( move |db_name: String, store_name: String, key: String| -> rquickjs::Result<()> { let key_val: serde_json::Value = serde_json::from_str(&key).map_err(|e| js_err(e.to_string()))?; - s.delete(&db_name, &store_name, &key_val) - .map_err(|e| js_err(e)) + s.delete(&db_name, &store_name, &key_val).map_err(js_err) }, ), )?; @@ -150,7 +146,7 @@ pub fn register<'js>( Function::new( ctx.clone(), move |db_name: String, store_name: String| -> rquickjs::Result<()> { - s.clear(&db_name, &store_name).map_err(|e| js_err(e)) + s.clear(&db_name, &store_name).map_err(js_err) }, ), )?; @@ -166,9 +162,7 @@ pub fn register<'js>( store_name: String, count: Option| -> rquickjs::Result { - let result = s - .get_all(&db_name, &store_name, count) - .map_err(|e| js_err(e))?; + let result = s.get_all(&db_name, &store_name, count).map_err(js_err)?; serde_json::to_string(&result).map_err(|e| js_err(e.to_string())) }, ), @@ -187,7 +181,7 @@ pub fn register<'js>( -> rquickjs::Result { let result = s .get_all_keys(&db_name, &store_name, count) - .map_err(|e| js_err(e))?; + .map_err(js_err)?; serde_json::to_string(&result).map_err(|e| js_err(e.to_string())) }, ), @@ -201,7 +195,7 @@ pub fn register<'js>( Function::new( ctx.clone(), move |db_name: String, store_name: String| -> rquickjs::Result { - s.count(&db_name, &store_name).map_err(|e| js_err(e)) + s.count(&db_name, &store_name).map_err(js_err) }, ), )?; @@ -226,7 +220,7 @@ pub fn register<'js>( }; let rows = s .skill_db_exec(&sc.skill_id, &sql, ¶ms) - .map_err(|e| js_err(e))?; + .map_err(js_err)?; Ok(rows as i64) }, ), @@ -248,7 +242,7 @@ pub fn register<'js>( }; let result = s .skill_db_get(&sc.skill_id, &sql, ¶ms) - .map_err(|e| js_err(e))?; + .map_err(js_err)?; serde_json::to_string(&result).map_err(|e| js_err(e.to_string())) }, ), @@ -270,7 +264,7 @@ pub fn register<'js>( }; let result = s .skill_db_all(&sc.skill_id, &sql, ¶ms) - .map_err(|e| js_err(e))?; + .map_err(js_err)?; serde_json::to_string(&result).map_err(|e| js_err(e.to_string())) }, ), @@ -285,7 +279,7 @@ pub fn register<'js>( Function::new( ctx.clone(), move |key: String| -> rquickjs::Result { - let result = s.skill_kv_get(&sc.skill_id, &key).map_err(|e| js_err(e))?; + let result = s.skill_kv_get(&sc.skill_id, &key).map_err(js_err)?; serde_json::to_string(&result).map_err(|e| js_err(e.to_string())) }, ), @@ -302,8 +296,7 @@ pub fn register<'js>( move |key: String, value_json: String| -> rquickjs::Result<()> { let value: serde_json::Value = serde_json::from_str(&value_json).map_err(|e| js_err(e.to_string()))?; - s.skill_kv_set(&sc.skill_id, &key, &value) - .map_err(|e| js_err(e)) + s.skill_kv_set(&sc.skill_id, &key, &value).map_err(js_err) }, ), )?; @@ -321,9 +314,7 @@ pub fn register<'js>( Function::new( ctx.clone(), move |key: String| -> rquickjs::Result { - let result = s - .skill_store_get(&sc.skill_id, &key) - .map_err(|e| js_err(e))?; + let result = s.skill_store_get(&sc.skill_id, &key).map_err(js_err)?; serde_json::to_string(&result).map_err(|e| js_err(e.to_string())) }, ), @@ -341,7 +332,7 @@ pub fn register<'js>( let value: serde_json::Value = serde_json::from_str(&value_json).map_err(|e| js_err(e.to_string()))?; s.skill_store_set(&sc.skill_id, &key, &value) - .map_err(|e| js_err(e)) + .map_err(js_err) }, ), )?; @@ -353,8 +344,7 @@ pub fn register<'js>( ops.set( "store_delete", Function::new(ctx.clone(), move |key: String| -> rquickjs::Result<()> { - s.skill_store_delete(&sc.skill_id, &key) - .map_err(|e| js_err(e)) + s.skill_store_delete(&sc.skill_id, &key).map_err(js_err) }), )?; } @@ -365,7 +355,7 @@ pub fn register<'js>( ops.set( "store_keys", Function::new(ctx.clone(), move || -> rquickjs::Result { - let keys = s.skill_store_keys(&sc.skill_id).map_err(|e| js_err(e))?; + let keys = s.skill_store_keys(&sc.skill_id).map_err(js_err)?; serde_json::to_string(&keys).map_err(|e| js_err(e.to_string())) }), )?; diff --git a/src/openhuman/skills/registry_ops.rs b/src/openhuman/skills/registry_ops.rs index d29add621..dc0fdd0e0 100644 --- a/src/openhuman/skills/registry_ops.rs +++ b/src/openhuman/skills/registry_ops.rs @@ -131,8 +131,8 @@ pub async fn registry_search( let mut results: Vec = Vec::new(); - let include_core = category.map_or(true, |c| c == "core"); - let include_third_party = category.map_or(true, |c| c == "third_party"); + let include_core = category.is_none_or(|c| c == "core"); + let include_third_party = category.is_none_or(|c| c == "third_party"); if include_core { results.extend( @@ -343,7 +343,7 @@ pub async fn skills_list_available( .map(|s| s.version.clone()); let update_available = installed_version .as_ref() - .map_or(false, |v| !v.is_empty() && *v != entry.version); + .is_some_and(|v| !v.is_empty() && *v != entry.version); available.push(AvailableSkillEntry { registry: entry, diff --git a/src/openhuman/skills/socket_manager.rs b/src/openhuman/skills/socket_manager.rs index c347c1f23..70ed24980 100644 --- a/src/openhuman/skills/socket_manager.rs +++ b/src/openhuman/skills/socket_manager.rs @@ -327,7 +327,7 @@ async fn run_connection( // 5. Send Socket.IO CONNECT with auth let connect_payload = json!({"token": token}); let connect_msg = format!("40{}", serde_json::to_string(&connect_payload).unwrap()); - if let Err(e) = ws_write.send(WsMessage::Text(connect_msg.into())).await { + if let Err(e) = ws_write.send(WsMessage::Text(connect_msg)).await { return ConnectionOutcome::Failed(format!("Send SIO CONNECT: {e}")); } @@ -386,7 +386,7 @@ async fn run_connection( outgoing = emit_rx.recv() => { match outgoing { Some(msg) => { - if let Err(e) = ws_write.send(WsMessage::Text(msg.into())).await { + if let Err(e) = ws_write.send(WsMessage::Text(msg)).await { return ConnectionOutcome::Lost(format!("Send failed: {e}")); } } diff --git a/src/openhuman/skills/types.rs b/src/openhuman/skills/types.rs index adb7877de..9c78568ff 100644 --- a/src/openhuman/skills/types.rs +++ b/src/openhuman/skills/types.rs @@ -6,8 +6,10 @@ use std::collections::HashMap; /// Status of a running skill instance. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] +#[derive(Default)] pub enum SkillStatus { /// Skill is registered but not yet started. + #[default] Pending, /// Skill is currently initializing (loading JS, running init()). Initializing, @@ -21,12 +23,6 @@ pub enum SkillStatus { Error, } -impl Default for SkillStatus { - fn default() -> Self { - Self::Pending - } -} - /// Messages sent to a skill instance's message loop. #[derive(Debug)] pub enum SkillMessage { diff --git a/src/openhuman/skills/utils.rs b/src/openhuman/skills/utils.rs index 11889ed7f..ff8759b16 100644 --- a/src/openhuman/skills/utils.rs +++ b/src/openhuman/skills/utils.rs @@ -62,7 +62,7 @@ where .enable_all() .build() .map_err(|e| format!("Failed to create runtime: {}", e)) - .and_then(|rt| Ok(rt.block_on(future))); + .map(|rt| rt.block_on(future)); let result = match runtime_result { Ok(value) => Ok(value), diff --git a/src/rpc/dispatch.rs b/src/rpc/dispatch.rs index 91a431e10..600dc9435 100644 --- a/src/rpc/dispatch.rs +++ b/src/rpc/dispatch.rs @@ -21,7 +21,9 @@ pub async fn try_dispatch( #[cfg(test)] mod tests { + use futures_util::FutureExt; use serde_json::json; + use std::panic::AssertUnwindSafe; use super::try_dispatch;