diff --git a/.gitignore b/.gitignore index e4beb1271..9a2584940 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,5 @@ tauri.key tauri.key.pub /target/ src-tauri/target/ + +openhuman-skills diff --git a/app/src-tauri/Cargo.lock b/app/src-tauri/Cargo.lock index 892adf185..78328dc32 100644 --- a/app/src-tauri/Cargo.lock +++ b/app/src-tauri/Cargo.lock @@ -4,29 +4,15 @@ version = 4 [[package]] name = "OpenHuman" -version = "0.49.19" +version = "0.49.20" dependencies = [ - "anyhow", - "chrono", - "dirs 5.0.1", "env_logger", - "futures-util", "log", - "once_cell", - "reqwest 0.12.28", - "rustls", - "serde", "serde_json", "tauri", "tauri-build", - "tauri-plugin-autostart", "tauri-plugin-deep-link", - "tauri-plugin-notification", - "tauri-plugin-opener", - "tauri-plugin-os", "tokio", - "urlencoding", - "uuid", ] [[package]] @@ -124,137 +110,6 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" -[[package]] -name = "async-broadcast" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" -dependencies = [ - "event-listener", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-channel" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-executor" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "pin-project-lite", - "slab", -] - -[[package]] -name = "async-io" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" -dependencies = [ - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite", - "parking", - "polling", - "rustix", - "slab", - "windows-sys 0.61.2", -] - -[[package]] -name = "async-lock" -version = "3.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" -dependencies = [ - "event-listener", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-process" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" -dependencies = [ - "async-channel", - "async-io", - "async-lock", - "async-signal", - "async-task", - "blocking", - "cfg-if", - "event-listener", - "futures-lite", - "rustix", -] - -[[package]] -name = "async-recursion" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "async-signal" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" -dependencies = [ - "async-io", - "async-lock", - "atomic-waker", - "cfg-if", - "futures-core", - "futures-io", - "rustix", - "signal-hook-registry", - "slab", - "windows-sys 0.61.2", -] - -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "atk" version = "0.18.2" @@ -284,45 +139,12 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "auto-launch" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f012b8cc0c850f34117ec8252a44418f2e34a2cf501de89e29b241ae5f79471" -dependencies = [ - "dirs 4.0.0", - "thiserror 1.0.69", - "winreg 0.10.1", -] - [[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "aws-lc-rs" -version = "1.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc" -dependencies = [ - "aws-lc-sys", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.39.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a25cf98105baa966497416dbd42565ce3a8cf8dbfd59803ec9ad46f3126399" -dependencies = [ - "cc", - "cmake", - "dunce", - "fs_extra", -] - [[package]] name = "base64" version = "0.21.7" @@ -383,19 +205,6 @@ dependencies = [ "objc2", ] -[[package]] -name = "blocking" -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" -dependencies = [ - "async-channel", - "async-task", - "futures-io", - "futures-lite", - "piper", -] - [[package]] name = "brotli" version = "8.0.2" @@ -518,8 +327,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1" dependencies = [ "find-msvc-tools", - "jobserver", - "libc", "shlex", ] @@ -556,12 +363,6 @@ 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" @@ -569,22 +370,11 @@ 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 0.2.1", ] -[[package]] -name = "cmake" -version = "0.1.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" -dependencies = [ - "cc", -] - [[package]] name = "colorchoice" version = "1.0.5" @@ -601,15 +391,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "const-random" version = "0.1.18" @@ -671,7 +452,7 @@ dependencies = [ "bitflags 2.11.0", "core-foundation", "core-graphics-types", - "foreign-types 0.5.0", + "foreign-types", "libc", ] @@ -873,54 +654,13 @@ dependencies = [ "crypto-common", ] -[[package]] -name = "dirs" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" -dependencies = [ - "dirs-sys 0.3.7", -] - -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys 0.4.1", -] - [[package]] name = "dirs" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ - "dirs-sys 0.5.0", -] - -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users 0.4.6", - "winapi", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users 0.4.6", - "windows-sys 0.48.0", + "dirs-sys", ] [[package]] @@ -931,7 +671,7 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", - "redox_users 0.5.2", + "redox_users", "windows-sys 0.61.2", ] @@ -1052,7 +792,7 @@ dependencies = [ "rustc_version", "toml 0.9.12+spec-1.1.0", "vswhom", - "winreg 0.55.0", + "winreg", ] [[package]] @@ -1061,33 +801,6 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" -[[package]] -name = "endi" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" - -[[package]] -name = "enumflags2" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" -dependencies = [ - "enumflags2_derive", - "serde", -] - -[[package]] -name = "enumflags2_derive" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "env_filter" version = "1.0.1" @@ -1138,27 +851,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" -dependencies = [ - "event-listener", - "pin-project-lite", -] - [[package]] name = "fastrand" version = "2.3.0" @@ -1218,15 +910,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared 0.1.1", -] - [[package]] name = "foreign-types" version = "0.5.0" @@ -1234,7 +917,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ "foreign-types-macros", - "foreign-types-shared 0.3.1", + "foreign-types-shared", ] [[package]] @@ -1248,12 +931,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "foreign-types-shared" version = "0.3.1" @@ -1269,12 +946,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - [[package]] name = "futf" version = "0.1.5" @@ -1292,7 +963,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", - "futures-sink", ] [[package]] @@ -1318,19 +988,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" -[[package]] -name = "futures-lite" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - [[package]] name = "futures-macro" version = "0.3.32" @@ -1488,16 +1145,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "gethostname" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" -dependencies = [ - "rustix", - "windows-link 0.2.1", -] - [[package]] name = "getrandom" version = "0.1.16" @@ -1516,10 +1163,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi 0.11.1+wasi-snapshot-preview1", - "wasm-bindgen", ] [[package]] @@ -1529,11 +1174,9 @@ 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]] @@ -1697,25 +1340,6 @@ dependencies = [ "syn 2.0.117", ] -[[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 2.13.0", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "hashbrown" version = "0.12.3" @@ -1755,12 +1379,6 @@ 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" @@ -1838,7 +1456,6 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "h2", "http", "http-body", "httparse", @@ -1850,39 +1467,6 @@ dependencies = [ "want", ] -[[package]] -name = "hyper-rustls" -version = "0.27.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" -dependencies = [ - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", - "webpki-roots", -] - -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - [[package]] name = "hyper-util" version = "0.1.20" @@ -2102,25 +1686,6 @@ dependencies = [ "serde", ] -[[package]] -name = "is-docker" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" -dependencies = [ - "once_cell", -] - -[[package]] -name = "is-wsl" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" -dependencies = [ - "is-docker", - "once_cell", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -2224,16 +1789,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] - [[package]] name = "js-sys" version = "0.3.92" @@ -2346,12 +1901,6 @@ dependencies = [ "libc", ] -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" - [[package]] name = "litemap" version = "0.8.1" @@ -2373,30 +1922,12 @@ 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 = "mac" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" -[[package]] -name = "mac-notification-sys" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29a16783dd1a47849b8c8133c9cd3eb2112cfbc6901670af3dba47c8bbfb07d3" -dependencies = [ - "cc", - "objc2", - "objc2-foundation", - "time", -] - [[package]] name = "markup5ever" version = "0.14.1" @@ -2460,16 +1991,6 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - [[package]] name = "miniz_oxide" version = "0.8.9" @@ -2512,23 +2033,6 @@ dependencies = [ "windows-sys 0.60.2", ] -[[package]] -name = "native-tls" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "ndk" version = "0.9.0" @@ -2565,38 +2069,12 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" -[[package]] -name = "nix" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" -dependencies = [ - "bitflags 2.11.0", - "cfg-if", - "cfg_aliases", - "libc", -] - [[package]] name = "nodrop" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" -[[package]] -name = "notify-rust" -version = "4.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21af20a1b50be5ac5861f74af1a863da53a11c38684d9818d82f1c42f7fdc6c2" -dependencies = [ - "futures-lite", - "log", - "mac-notification-sys", - "serde", - "tauri-winrt-notification", - "zbus", -] - [[package]] name = "num-conv" version = "0.2.1" @@ -2657,27 +2135,6 @@ dependencies = [ "objc2-foundation", ] -[[package]] -name = "objc2-cloud-kit" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" -dependencies = [ - "bitflags 2.11.0", - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-core-data" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" -dependencies = [ - "objc2", - "objc2-foundation", -] - [[package]] name = "objc2-core-foundation" version = "0.3.2" @@ -2702,38 +2159,6 @@ dependencies = [ "objc2-io-surface", ] -[[package]] -name = "objc2-core-image" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" -dependencies = [ - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-core-location" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" -dependencies = [ - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-core-text" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" -dependencies = [ - "bitflags 2.11.0", - "objc2", - "objc2-core-foundation", - "objc2-core-graphics", -] - [[package]] name = "objc2-encode" version = "4.1.0" @@ -2757,7 +2182,6 @@ checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ "bitflags 2.11.0", "block2", - "libc", "objc2", "objc2-core-foundation", ] @@ -2792,27 +2216,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ "bitflags 2.11.0", - "block2", "objc2", - "objc2-cloud-kit", - "objc2-core-data", "objc2-core-foundation", - "objc2-core-graphics", - "objc2-core-image", - "objc2-core-location", - "objc2-core-text", - "objc2-foundation", - "objc2-quartz-core", - "objc2-user-notifications", -] - -[[package]] -name = "objc2-user-notifications" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" -dependencies = [ - "objc2", "objc2-foundation", ] @@ -2842,62 +2247,6 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" -[[package]] -name = "open" -version = "5.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43bb73a7fa3799b198970490a51174027ba0d4ec504b03cd08caf513d40024bc" -dependencies = [ - "dunce", - "is-wsl", - "libc", - "pathdiff", -] - -[[package]] -name = "openssl" -version = "0.10.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf" -dependencies = [ - "bitflags 2.11.0", - "cfg-if", - "foreign-types 0.3.2", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "openssl-probe" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" - -[[package]] -name = "openssl-sys" -version = "0.9.112" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "option-ext" version = "0.2.0" @@ -2914,32 +2263,6 @@ dependencies = [ "hashbrown 0.14.5", ] -[[package]] -name = "ordered-stream" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" -dependencies = [ - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "os_info" -version = "3.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4022a17595a00d6a369236fdae483f0de7f0a339960a53118b818238e132224" -dependencies = [ - "android_system_properties", - "log", - "nix", - "objc2", - "objc2-foundation", - "objc2-ui-kit", - "serde", - "windows-sys 0.61.2", -] - [[package]] name = "pango" version = "0.18.3" @@ -2965,12 +2288,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - [[package]] name = "parking_lot" version = "0.12.5" @@ -2994,12 +2311,6 @@ dependencies = [ "windows-link 0.2.1", ] -[[package]] -name = "pathdiff" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" - [[package]] name = "percent-encoding" version = "2.3.2" @@ -3205,17 +2516,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "piper" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" -dependencies = [ - "atomic-waker", - "fastrand", - "futures-io", -] - [[package]] name = "pkg-config" version = "0.3.32" @@ -3230,7 +2530,7 @@ checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" dependencies = [ "base64 0.22.1", "indexmap 2.13.0", - "quick-xml 0.38.4", + "quick-xml", "serde", "time", ] @@ -3248,20 +2548,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "polling" -version = "3.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" -dependencies = [ - "cfg-if", - "concurrent-queue", - "hermit-abi", - "pin-project-lite", - "rustix", - "windows-sys 0.61.2", -] - [[package]] name = "portable-atomic" version = "1.13.1" @@ -3385,15 +2671,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "quick-xml" -version = "0.37.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" -dependencies = [ - "memchr", -] - [[package]] name = "quick-xml" version = "0.38.4" @@ -3403,61 +2680,6 @@ 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", - "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.2", - "ring", - "rustc-hash", - "rustls", - "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" @@ -3504,16 +2726,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.5", -] - [[package]] name = "rand_chacha" version = "0.2.2" @@ -3534,16 +2746,6 @@ dependencies = [ "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.5.1" @@ -3562,15 +2764,6 @@ 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 = "rand_hc" version = "0.2.0" @@ -3604,17 +2797,6 @@ dependencies = [ "bitflags 2.11.0", ] -[[package]] -name = "redox_users" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom 0.2.17", - "libredox", - "thiserror 1.0.69", -] - [[package]] name = "redox_users" version = "0.5.2" @@ -3675,53 +2857,6 @@ 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 0.22.1", - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-tls", - "hyper-util", - "js-sys", - "log", - "mime_guess", - "native-tls", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-native-tls", - "tokio-rustls", - "tokio-util", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams 0.4.2", - "web-sys", - "webpki-roots", -] - [[package]] name = "reqwest" version = "0.13.2" @@ -3752,24 +2887,10 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-streams 0.5.0", + "wasm-streams", "web-sys", ] -[[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 = "rust-ini" version = "0.21.3" @@ -3795,69 +2916,12 @@ dependencies = [ "semver", ] -[[package]] -name = "rustix" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" -dependencies = [ - "bitflags 2.11.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls" -version = "0.23.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" -dependencies = [ - "aws-lc-rs", - "log", - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "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.103.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" -dependencies = [ - "aws-lc-rs", - "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 = "same-file" version = "1.0.6" @@ -3867,15 +2931,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "schannel" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" -dependencies = [ - "windows-sys 0.61.2", -] - [[package]] name = "schemars" version = "0.8.22" @@ -3933,29 +2988,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "security-framework" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" -dependencies = [ - "bitflags 2.11.0", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "selectors" version = "0.24.0" @@ -4098,18 +3130,6 @@ dependencies = [ "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 = "serde_with" version = "3.18.0" @@ -4358,12 +3378,6 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - [[package]] name = "swift-rs" version = "1.0.7" @@ -4417,15 +3431,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "sys-locale" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" -dependencies = [ - "libc", -] - [[package]] name = "system-deps" version = "6.2.2" @@ -4503,7 +3508,7 @@ dependencies = [ "anyhow", "bytes", "cookie", - "dirs 6.0.0", + "dirs", "dunce", "embed_plist", "getrandom 0.3.4", @@ -4524,7 +3529,7 @@ dependencies = [ "percent-encoding", "plist", "raw-window-handle", - "reqwest 0.13.2", + "reqwest", "serde", "serde_json", "serde_repr", @@ -4553,7 +3558,7 @@ checksum = "4bbc990d1dbf57a8e1c7fa2327f2a614d8b757805603c1b9ba5c81bade09fd4d" dependencies = [ "anyhow", "cargo_toml", - "dirs 6.0.0", + "dirs", "glob", "heck 0.5.0", "json-patch", @@ -4625,20 +3630,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "tauri-plugin-autostart" -version = "2.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459383cebc193cdd03d1ba4acc40f2c408a7abce419d64bdcd2d745bc2886f70" -dependencies = [ - "auto-launch", - "serde", - "serde_json", - "tauri", - "tauri-plugin", - "thiserror 2.0.18", -] - [[package]] name = "tauri-plugin-deep-link" version = "2.4.7" @@ -4660,65 +3651,6 @@ dependencies = [ "windows-result 0.3.4", ] -[[package]] -name = "tauri-plugin-notification" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01fc2c5ff41105bd1f7242d8201fdf3efd70749b82fa013a17f2126357d194cc" -dependencies = [ - "log", - "notify-rust", - "rand 0.9.2", - "serde", - "serde_json", - "serde_repr", - "tauri", - "tauri-plugin", - "thiserror 2.0.18", - "time", - "url", -] - -[[package]] -name = "tauri-plugin-opener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc624469b06f59f5a29f874bbc61a2ed737c0f9c23ef09855a292c389c42e83f" -dependencies = [ - "dunce", - "glob", - "objc2-app-kit", - "objc2-foundation", - "open", - "schemars 0.8.22", - "serde", - "serde_json", - "tauri", - "tauri-plugin", - "thiserror 2.0.18", - "url", - "windows", - "zbus", -] - -[[package]] -name = "tauri-plugin-os" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8f08346c8deb39e96f86973da0e2d76cbb933d7ac9b750f6dc4daf955a6f997" -dependencies = [ - "gethostname", - "log", - "os_info", - "serde", - "serde_json", - "serialize-to-javascript", - "sys-locale", - "tauri", - "tauri-plugin", - "thiserror 2.0.18", -] - [[package]] name = "tauri-runtime" version = "2.10.1" @@ -4819,31 +3751,6 @@ dependencies = [ "toml 0.9.12+spec-1.1.0", ] -[[package]] -name = "tauri-winrt-notification" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b1e66e07de489fe43a46678dd0b8df65e0c973909df1b60ba33874e297ba9b9" -dependencies = [ - "quick-xml 0.37.5", - "thiserror 2.0.18", - "windows", - "windows-version", -] - -[[package]] -name = "tempfile" -version = "3.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" -dependencies = [ - "fastrand", - "getrandom 0.4.2", - "once_cell", - "rustix", - "windows-sys 0.61.2", -] - [[package]] name = "tendril" version = "0.4.3" @@ -4955,21 +3862,6 @@ dependencies = [ "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.50.0" @@ -4979,45 +3871,12 @@ 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.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" -dependencies = [ - "rustls", - "tokio", -] - [[package]] name = "tokio-util" version = "0.7.18" @@ -5219,7 +4078,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5e85aa143ceb072062fc4d6356c1b520a51d636e7bc8e77ec94be3608e5e80c" dependencies = [ "crossbeam-channel", - "dirs 6.0.0", + "dirs", "libappindicator", "muda", "objc2", @@ -5252,17 +4111,6 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" -[[package]] -name = "uds_windows" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" -dependencies = [ - "memoffset", - "tempfile", - "windows-sys 0.61.2", -] - [[package]] name = "unic-char-property" version = "0.9.0" @@ -5304,12 +4152,6 @@ dependencies = [ "unic-common", ] -[[package]] -name = "unicase" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" - [[package]] name = "unicode-ident" version = "1.0.24" @@ -5328,12 +4170,6 @@ 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" @@ -5347,12 +4183,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - [[package]] name = "urlpattern" version = "0.3.0" @@ -5395,12 +4225,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version-compare" version = "0.2.1" @@ -5559,19 +4383,6 @@ dependencies = [ "wasmparser", ] -[[package]] -name = "wasm-streams" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - [[package]] name = "wasm-streams" version = "0.5.0" @@ -5607,16 +4418,6 @@ dependencies = [ "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 = "web_atoms" version = "0.2.3" @@ -5673,15 +4474,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "webpki-roots" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "webview2-com" version = "0.38.2" @@ -5923,24 +4715,6 @@ dependencies = [ "windows-targets 0.42.2", ] -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[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.59.0" @@ -5983,21 +4757,6 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -6055,12 +4814,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -6079,12 +4832,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -6103,12 +4850,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -6139,12 +4880,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -6163,12 +4898,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -6187,12 +4916,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -6211,12 +4934,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -6243,9 +4960,6 @@ name = "winnow" version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" -dependencies = [ - "memchr", -] [[package]] name = "winnow" @@ -6256,15 +4970,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi", -] - [[package]] name = "winreg" version = "0.55.0" @@ -6379,7 +5084,7 @@ dependencies = [ "block2", "cookie", "crossbeam-channel", - "dirs 6.0.0", + "dirs", "dom_query", "dpi", "dunce", @@ -6457,67 +5162,6 @@ dependencies = [ "synstructure", ] -[[package]] -name = "zbus" -version = "5.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca82f95dbd3943a40a53cfded6c2d0a2ca26192011846a1810c4256ef92c60bc" -dependencies = [ - "async-broadcast", - "async-executor", - "async-io", - "async-lock", - "async-process", - "async-recursion", - "async-task", - "async-trait", - "blocking", - "enumflags2", - "event-listener", - "futures-core", - "futures-lite", - "hex", - "libc", - "ordered-stream", - "rustix", - "serde", - "serde_repr", - "tracing", - "uds_windows", - "uuid", - "windows-sys 0.61.2", - "winnow 0.7.15", - "zbus_macros", - "zbus_names", - "zvariant", -] - -[[package]] -name = "zbus_macros" -version = "5.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897e79616e84aac4b2c46e9132a4f63b93105d54fe8c0e8f6bffc21fa8d49222" -dependencies = [ - "proc-macro-crate 3.5.0", - "proc-macro2", - "quote", - "syn 2.0.117", - "zbus_names", - "zvariant", - "zvariant_utils", -] - -[[package]] -name = "zbus_names" -version = "4.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" -dependencies = [ - "serde", - "winnow 0.7.15", - "zvariant", -] - [[package]] name = "zerocopy" version = "0.8.48" @@ -6559,12 +5203,6 @@ dependencies = [ "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.3" @@ -6603,43 +5241,3 @@ name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" - -[[package]] -name = "zvariant" -version = "5.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5708299b21903bbe348e94729f22c49c55d04720a004aa350f1f9c122fd2540b" -dependencies = [ - "endi", - "enumflags2", - "serde", - "winnow 0.7.15", - "zvariant_derive", - "zvariant_utils", -] - -[[package]] -name = "zvariant_derive" -version = "5.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b59b012ebe9c46656f9cc08d8da8b4c726510aef12559da3e5f1bf72780752c" -dependencies = [ - "proc-macro-crate 3.5.0", - "proc-macro2", - "quote", - "syn 2.0.117", - "zvariant_utils", -] - -[[package]] -name = "zvariant_utils" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "syn 2.0.117", - "winnow 0.7.15", -] diff --git a/app/src-tauri/Cargo.toml b/app/src-tauri/Cargo.toml index 1c61af301..6002305bf 100644 --- a/app/src-tauri/Cargo.toml +++ b/app/src-tauri/Cargo.toml @@ -27,28 +27,13 @@ serde_json = "1" [dependencies] # Tauri core and plugins tauri = { version = "2.10", features = ["macos-private-api"] } -tauri-plugin-opener = "2" tauri-plugin-deep-link = "2.0.0" -tauri-plugin-os = "2" -serde = { version = "1", features = ["derive"] } serde_json = "1" - -reqwest = { version = "0.12", default-features = false, features = ["json", "blocking", "rustls-tls", "native-tls", "stream", "http2", "multipart", "socks"] } -tokio = { version = "1", features = ["full", "sync"] } -once_cell = "1.19" +tokio = { version = "1", features = ["rt-multi-thread", "process", "sync", "time", "net"] } log = "0.4" env_logger = "0.11" -dirs = "5" -uuid = { version = "1", features = ["v4"] } -anyhow = "1.0" -chrono = { version = "0.4", features = ["serde"] } -futures-util = "0.3" -urlencoding = "2.1" -rustls = { version = "0.23", features = ["ring"] } -tauri-plugin-autostart = "2" -tauri-plugin-notification = "2" [features] # This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!! diff --git a/app/src-tauri/capabilities/default.json b/app/src-tauri/capabilities/default.json index a664e6e85..45007890f 100644 --- a/app/src-tauri/capabilities/default.json +++ b/app/src-tauri/capabilities/default.json @@ -6,16 +6,6 @@ "windows": ["main"], "permissions": [ "core:default", - "opener:default", - "deep-link:default", - "os:default", - "autostart:default", - "autostart:allow-enable", - "autostart:allow-disable", - "autostart:allow-is-enabled", - "notification:default", - "notification:allow-notify", - "notification:allow-request-permission", - "notification:allow-is-permission-granted" + "deep-link:default" ] } diff --git a/app/src-tauri/src/commands/core_relay.rs b/app/src-tauri/src/commands/core_relay.rs deleted file mode 100644 index ff0e3e46e..000000000 --- a/app/src-tauri/src/commands/core_relay.rs +++ /dev/null @@ -1,103 +0,0 @@ -use crate::core_process::CoreProcessHandle; -use serde::Deserialize; -use serde_json::Value; -use tauri::Manager; - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct CoreRpcRelayRequest { - pub method: String, - #[serde(default)] - pub params: Value, - #[serde(default)] - pub service_managed: bool, -} - -async fn invoke_core_cli_call(method: &str, params: Value) -> Result<(), String> { - let core_bin = crate::core_process::default_core_bin() - .ok_or_else(|| "openhuman core binary not found".to_string())?; - let params_json = - serde_json::to_string(¶ms).map_err(|e| format!("failed to serialize params: {e}"))?; - - let output = tokio::process::Command::new(core_bin) - .arg("call") - .arg("--method") - .arg(method) - .arg("--params") - .arg(params_json) - .output() - .await - .map_err(|e| format!("failed to run core cli call {method}: {e}"))?; - - if !output.status.success() { - let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string(); - let stdout = String::from_utf8_lossy(&output.stdout).trim().to_string(); - return Err(format!( - "core cli call {method} failed with status {}. stdout: {} stderr: {}", - output.status, stdout, stderr - )); - } - - Ok(()) -} - -/// Whether to install/start the OS background service (launchd, etc.) when core RPC is down. -/// Disabled in debug/dev builds so `tauri dev` does not mutate system services; the shell still -/// spawns a child `openhuman core run` via [`crate::core_process::CoreProcessHandle`]. -/// Set `OPENHUMAN_SKIP_SERVICE_AUTOSTART=1` to skip in release builds too. -pub(crate) fn should_autostart_service_managed_core() -> bool { - if std::env::var("OPENHUMAN_SKIP_SERVICE_AUTOSTART") - .map(|v| v == "1" || v.eq_ignore_ascii_case("true")) - .unwrap_or(false) - { - return false; - } - if cfg!(debug_assertions) { - return false; - } - true -} - -pub(crate) async fn ensure_service_managed_core_running() -> Result<(), String> { - if crate::core_rpc::ping().await { - return Ok(()); - } - - let _ = invoke_core_cli_call("openhuman.service_install", serde_json::json!({})).await; - let _ = invoke_core_cli_call("openhuman.service_start", serde_json::json!({})).await; - - for _ in 0..40 { - if crate::core_rpc::ping().await { - return Ok(()); - } - tokio::time::sleep(std::time::Duration::from_millis(250)).await; - } - - Err( - "OpenHuman Core daemon did not become ready. Confirm the background service is running." - .to_string(), - ) -} - -#[tauri::command] -pub async fn core_rpc_relay( - app: tauri::AppHandle, - request: CoreRpcRelayRequest, -) -> Result { - if request.service_managed { - ensure_service_managed_core_running().await?; - } else { - let core = app - .try_state::() - .ok_or_else(|| "core process handle is not available".to_string())?; - let handle: CoreProcessHandle = (*core).clone(); - handle.ensure_running().await?; - } - - crate::core_rpc::call::(&request.method, request.params).await -} - -#[tauri::command] -pub fn core_rpc_url() -> String { - crate::core_rpc::resolved_rpc_url() -} diff --git a/app/src-tauri/src/commands/mod.rs b/app/src-tauri/src/commands/mod.rs deleted file mode 100644 index b7dfeb396..000000000 --- a/app/src-tauri/src/commands/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -pub mod core_relay; -pub mod openhuman; - -#[cfg(desktop)] -pub mod window; - -pub use core_relay::*; -pub use openhuman::*; - -#[cfg(desktop)] -pub use window::*; diff --git a/app/src-tauri/src/commands/openhuman.rs b/app/src-tauri/src/commands/openhuman.rs deleted file mode 100644 index 8c2d9ad31..000000000 --- a/app/src-tauri/src/commands/openhuman.rs +++ /dev/null @@ -1,356 +0,0 @@ -use crate::core_process::CoreProcessHandle; -use once_cell::sync::Lazy; -use serde::{Deserialize, Serialize}; -use serde_json::Value; -use std::path::PathBuf; -use tauri::{AppHandle, Emitter, Manager}; -use tokio::sync::Mutex; -use uuid::Uuid; - -const DAEMON_HOST_CONFIG_FILE: &str = "daemon_host_config.json"; - -static WEB_CHAT_CLIENT_ID: Lazy = Lazy::new(|| Uuid::new_v4().to_string()); -static WEB_CHAT_STREAM_TASK: Lazy>>> = - Lazy::new(|| Mutex::new(None)); - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct DaemonHostConfig { - pub show_tray: bool, -} - -impl Default for DaemonHostConfig { - fn default() -> Self { - Self { show_tray: true } - } -} - -fn daemon_host_config_path(app: &AppHandle) -> PathBuf { - app.path() - .app_data_dir() - .unwrap_or_else(|_| { - dirs::home_dir() - .unwrap_or_else(|| PathBuf::from(".")) - .join(".openhuman") - }) - .join(DAEMON_HOST_CONFIG_FILE) -} - -async fn load_daemon_host_config(app: &AppHandle) -> DaemonHostConfig { - let path = daemon_host_config_path(app); - let Ok(contents) = tokio::fs::read_to_string(path).await else { - return DaemonHostConfig::default(); - }; - serde_json::from_str::(&contents).unwrap_or_default() -} - -async fn save_daemon_host_config(app: &AppHandle, config: &DaemonHostConfig) -> Result<(), String> { - let path = daemon_host_config_path(app); - if let Some(parent) = path.parent() { - tokio::fs::create_dir_all(parent) - .await - .map_err(|e| format!("failed to create daemon host config directory: {e}"))?; - } - let bytes = serde_json::to_vec_pretty(config) - .map_err(|e| format!("failed to serialize daemon host config: {e}"))?; - tokio::fs::write(path, bytes) - .await - .map_err(|e| format!("failed to write daemon host config: {e}")) -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum ServiceState { - Running, - Stopped, - NotInstalled, - Unknown(String), -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ServiceStatus { - pub state: ServiceState, - pub unit_path: Option, - pub label: String, - pub details: Option, -} - -#[derive(Debug, Deserialize)] -struct RpcCommandResponse { - result: T, -} - -async fn ensure_core_running(app: &AppHandle) -> Result<(), String> { - let core = app - .try_state::() - .ok_or_else(|| "core process handle is not available".to_string())?; - let handle: CoreProcessHandle = (*core).clone(); - handle.ensure_running().await -} - -async fn call_service_method(app: &AppHandle, method: &str) -> Result { - ensure_core_running(app).await?; - let response = - crate::core_rpc::call::>(method, serde_json::json!({})) - .await?; - Ok(response.result) -} - -#[derive(Debug, Deserialize)] -struct WebChatSseEvent { - event: String, - client_id: String, - thread_id: String, - request_id: String, - full_response: Option, - message: Option, - error_type: Option, - tool_name: Option, - skill_id: Option, - args: Option, - output: Option, - success: Option, - round: Option, -} - -fn core_events_url() -> String { - let rpc_url = crate::core_rpc::resolved_rpc_url(); - if rpc_url.ends_with("/rpc") { - rpc_url.trim_end_matches("/rpc").to_string() + "/events" - } else { - rpc_url + "/events" - } -} - -fn parse_sse_block(block: &str) -> Option { - let mut event_name: Option = None; - let mut data_lines: Vec<&str> = Vec::new(); - for line in block.lines() { - if let Some(rest) = line.strip_prefix("event:") { - event_name = Some(rest.trim().to_string()); - } else if let Some(rest) = line.strip_prefix("data:") { - data_lines.push(rest.trim_start()); - } - } - - if data_lines.is_empty() { - return None; - } - - let data = data_lines.join("\n"); - let mut parsed: WebChatSseEvent = serde_json::from_str(&data).ok()?; - if parsed.event.is_empty() { - parsed.event = event_name.unwrap_or_default(); - } - Some(parsed) -} - -fn emit_web_chat_event(app: &AppHandle, event: WebChatSseEvent) { - if event.client_id != *WEB_CHAT_CLIENT_ID { - return; - } - - match event.event.as_str() { - "chat_done" => { - let payload = serde_json::json!({ - "thread_id": event.thread_id, - "full_response": event.full_response.unwrap_or_default(), - "rounds_used": 0, - "total_input_tokens": 0, - "total_output_tokens": 0, - "request_id": event.request_id, - }); - let _ = app.emit("chat:done", payload); - } - "chat_error" => { - let payload = serde_json::json!({ - "thread_id": event.thread_id, - "message": event.message.unwrap_or_else(|| "Unknown chat error".to_string()), - "error_type": event.error_type.unwrap_or_else(|| "inference".to_string()), - "round": serde_json::Value::Null, - "request_id": event.request_id, - }); - let _ = app.emit("chat:error", payload); - } - "tool_call" => { - let payload = serde_json::json!({ - "thread_id": event.thread_id, - "tool_name": event.tool_name.unwrap_or_else(|| "unknown".to_string()), - "skill_id": event.skill_id.unwrap_or_else(|| "web_channel".to_string()), - "args": event.args.unwrap_or_else(|| serde_json::json!({})), - "round": event.round.unwrap_or(0), - "request_id": event.request_id, - }); - let _ = app.emit("chat:tool_call", payload); - } - "tool_result" => { - let payload = serde_json::json!({ - "thread_id": event.thread_id, - "tool_name": event.tool_name.unwrap_or_else(|| "unknown".to_string()), - "skill_id": event.skill_id.unwrap_or_else(|| "web_channel".to_string()), - "output": event.output.unwrap_or_default(), - "success": event.success.unwrap_or(true), - "round": event.round.unwrap_or(0), - "request_id": event.request_id, - }); - let _ = app.emit("chat:tool_result", payload); - } - _ => {} - } -} - -async fn ensure_web_chat_stream(app: &AppHandle) -> Result<(), String> { - let mut guard = WEB_CHAT_STREAM_TASK.lock().await; - if guard.is_some() { - return Ok(()); - } - - let app_handle = app.clone(); - let client_id = WEB_CHAT_CLIENT_ID.clone(); - let url = format!( - "{}?client_id={}", - core_events_url(), - urlencoding::encode(&client_id) - ); - - let task = tokio::spawn(async move { - let client = reqwest::Client::new(); - let mut backoff_ms: u64 = 500; - - loop { - let response = match client.get(&url).send().await { - Ok(resp) if resp.status().is_success() => resp, - Ok(resp) => { - log::warn!( - "[web-channel] SSE stream HTTP error status={} for {}", - resp.status(), - url - ); - tokio::time::sleep(std::time::Duration::from_millis(backoff_ms)).await; - backoff_ms = (backoff_ms * 2).min(10_000); - continue; - } - Err(err) => { - log::warn!("[web-channel] SSE connect error: {}", err); - tokio::time::sleep(std::time::Duration::from_millis(backoff_ms)).await; - backoff_ms = (backoff_ms * 2).min(10_000); - continue; - } - }; - - backoff_ms = 500; - let mut stream = response.bytes_stream(); - let mut buffer = String::new(); - - use futures_util::StreamExt as _; - while let Some(item) = stream.next().await { - let chunk = match item { - Ok(chunk) => chunk, - Err(err) => { - log::warn!("[web-channel] SSE stream read error: {}", err); - break; - } - }; - - let text = match std::str::from_utf8(&chunk) { - Ok(s) => s, - Err(_) => continue, - }; - buffer.push_str(text); - - while let Some(split_index) = buffer.find("\n\n") { - let block = buffer[..split_index].to_string(); - buffer = buffer[split_index + 2..].to_string(); - if let Some(event) = parse_sse_block(block.trim()) { - emit_web_chat_event(&app_handle, event); - } - } - } - } - }); - - *guard = Some(task); - Ok(()) -} - -#[tauri::command] -pub async fn chat_send( - app: AppHandle, - thread_id: String, - message: String, - model: String, - auth_token: String, - backend_url: String, - messages: Vec, - notion_context: Option, -) -> Result<(), String> { - ensure_web_chat_stream(&app).await?; - - let _ = (&auth_token, &backend_url, &messages, ¬ion_context); - - let params = serde_json::json!({ - "client_id": WEB_CHAT_CLIENT_ID.as_str(), - "thread_id": thread_id, - "message": message, - "model_override": model, - }); - - let _: Value = crate::core_rpc::call("openhuman.channel_web_chat", params).await?; - Ok(()) -} - -#[tauri::command] -pub async fn chat_cancel(thread_id: String) -> Result { - let params = serde_json::json!({ - "client_id": WEB_CHAT_CLIENT_ID.as_str(), - "thread_id": thread_id, - }); - let response: Value = crate::core_rpc::call("openhuman.channel_web_cancel", params).await?; - let cancelled = response - .get("result") - .and_then(|v| v.get("cancelled")) - .or_else(|| response.get("cancelled")) - .and_then(Value::as_bool) - .unwrap_or(false); - Ok(cancelled) -} - -#[tauri::command] -pub async fn openhuman_get_daemon_host_config(app: AppHandle) -> Result { - Ok(load_daemon_host_config(&app).await) -} - -#[tauri::command] -pub async fn openhuman_set_daemon_host_config( - app: AppHandle, - show_tray: bool, -) -> Result { - let mut cfg = load_daemon_host_config(&app).await; - cfg.show_tray = show_tray; - save_daemon_host_config(&app, &cfg).await?; - Ok(cfg) -} - -#[tauri::command] -pub async fn openhuman_service_install(app: AppHandle) -> Result { - call_service_method(&app, "openhuman.service_install").await -} - -#[tauri::command] -pub async fn openhuman_service_start(app: AppHandle) -> Result { - call_service_method(&app, "openhuman.service_start").await -} - -#[tauri::command] -pub async fn openhuman_service_stop(app: AppHandle) -> Result { - call_service_method(&app, "openhuman.service_stop").await -} - -#[tauri::command] -pub async fn openhuman_service_status(app: AppHandle) -> Result { - call_service_method(&app, "openhuman.service_status").await -} - -#[tauri::command] -pub async fn openhuman_service_uninstall(app: AppHandle) -> Result { - call_service_method(&app, "openhuman.service_uninstall").await -} diff --git a/app/src-tauri/src/commands/window.rs b/app/src-tauri/src/commands/window.rs deleted file mode 100644 index cd3eb227e..000000000 --- a/app/src-tauri/src/commands/window.rs +++ /dev/null @@ -1,80 +0,0 @@ -use tauri::{AppHandle, Manager}; - -/// Show the main window -#[tauri::command] -pub fn show_window(app: AppHandle) { - if let Some(window) = app.get_webview_window("main") { - let _ = window.show(); - let _ = window.unminimize(); - let _ = window.set_focus(); - } -} - -/// Hide the main window -#[tauri::command] -pub fn hide_window(app: AppHandle) { - if let Some(window) = app.get_webview_window("main") { - let _ = window.hide(); - } -} - -/// Toggle window visibility -#[tauri::command] -pub fn toggle_window(app: AppHandle) { - if let Some(window) = app.get_webview_window("main") { - match window.is_visible() { - Ok(true) => { - let _ = window.hide(); - } - Ok(false) | Err(_) => { - let _ = window.show(); - let _ = window.unminimize(); - let _ = window.set_focus(); - } - } - } -} - -/// Check if window is visible -#[tauri::command] -pub fn is_window_visible(app: AppHandle) -> bool { - app.get_webview_window("main") - .and_then(|w| w.is_visible().ok()) - .unwrap_or(false) -} - -/// Minimize the main window -#[tauri::command] -pub fn minimize_window(app: AppHandle) { - if let Some(window) = app.get_webview_window("main") { - let _ = window.minimize(); - } -} - -/// Maximize or unmaximize the main window -#[tauri::command] -pub fn maximize_window(app: AppHandle) { - if let Some(window) = app.get_webview_window("main") { - if window.is_maximized().unwrap_or(false) { - let _ = window.unmaximize(); - } else { - let _ = window.maximize(); - } - } -} - -/// Close the main window (triggers minimize on macOS if configured) -#[tauri::command] -pub fn close_window(app: AppHandle) { - if let Some(window) = app.get_webview_window("main") { - let _ = window.close(); - } -} - -/// Set window title -#[tauri::command] -pub fn set_window_title(app: AppHandle, title: String) { - if let Some(window) = app.get_webview_window("main") { - let _ = window.set_title(&title); - } -} diff --git a/app/src-tauri/src/core_process.rs b/app/src-tauri/src/core_process.rs index 4579d6ddb..5f0e71e21 100644 --- a/app/src-tauri/src/core_process.rs +++ b/app/src-tauri/src/core_process.rs @@ -1,9 +1,11 @@ use std::path::PathBuf; use std::sync::Arc; +use tokio::net::TcpStream; use tokio::process::{Child, Command}; use tokio::sync::Mutex; use tokio::task::JoinHandle; +use tokio::time::{timeout, Duration}; #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum CoreRunMode { @@ -35,8 +37,19 @@ impl CoreProcessHandle { format!("http://127.0.0.1:{}/rpc", self.port) } + async fn is_rpc_port_open(&self) -> bool { + matches!( + timeout( + Duration::from_millis(150), + TcpStream::connect(("127.0.0.1", self.port)), + ) + .await, + Ok(Ok(_)) + ) + } + pub async fn ensure_running(&self) -> Result<(), String> { - if crate::core_rpc::ping().await { + if self.is_rpc_port_open().await { log::info!( "[core] found existing core rpc endpoint at {}", self.rpc_url() @@ -115,7 +128,7 @@ impl CoreProcessHandle { } for _ in 0..40 { - if crate::core_rpc::ping().await { + if self.is_rpc_port_open().await { log::info!("[core] core rpc became ready at {}", self.rpc_url()); return Ok(()); } diff --git a/app/src-tauri/src/core_rpc.rs b/app/src-tauri/src/core_rpc.rs deleted file mode 100644 index 0bd245faf..000000000 --- a/app/src-tauri/src/core_rpc.rs +++ /dev/null @@ -1,90 +0,0 @@ -use serde::de::DeserializeOwned; -use serde::{Deserialize, Serialize}; - -const DEFAULT_CORE_RPC_URL: &str = "http://127.0.0.1:7788/rpc"; - -#[derive(Debug, Serialize)] -struct RpcRequest<'a> { - jsonrpc: &'static str, - id: u64, - method: &'a str, - params: serde_json::Value, -} - -#[derive(Debug, Deserialize)] -struct RpcResponse { - #[allow(dead_code)] - jsonrpc: String, - #[allow(dead_code)] - id: serde_json::Value, - result: Option, - error: Option, -} - -#[derive(Debug, Deserialize)] -struct RpcError { - #[allow(dead_code)] - code: i64, - message: String, - data: Option, -} - -fn rpc_url() -> String { - std::env::var("OPENHUMAN_CORE_RPC_URL").unwrap_or_else(|_| DEFAULT_CORE_RPC_URL.to_string()) -} - -pub fn resolved_rpc_url() -> String { - rpc_url() -} - -pub async fn call( - method: &str, - params: serde_json::Value, -) -> Result { - let client = reqwest::Client::new(); - let req = RpcRequest { - jsonrpc: "2.0", - id: 1, - method, - params, - }; - - let resp = client - .post(rpc_url()) - .json(&req) - .send() - .await - .map_err(|e| format!("core rpc request failed: {e}"))?; - - let status = resp.status(); - if !status.is_success() { - let text = resp.text().await.unwrap_or_default(); - return Err(format!("core rpc http error {status}: {text}")); - } - - let payload: RpcResponse = resp - .json() - .await - .map_err(|e| format!("core rpc decode failed: {e}"))?; - - if let Some(err) = payload.error { - let data = err.data.map(|d| format!(" ({d})")).unwrap_or_default(); - return Err(format!("{}{}", err.message, data)); - } - - payload - .result - .ok_or_else(|| "core rpc missing result".to_string()) -} - -pub async fn ping() -> bool { - #[derive(Deserialize)] - struct Pong { - ok: bool, - } - - match call::("core.ping", serde_json::json!({})).await { - Ok(p) => p.ok, - Err(_) => false, - } -} diff --git a/app/src-tauri/src/lib.rs b/app/src-tauri/src/lib.rs index 635fe6639..ca4773782 100644 --- a/app/src-tauri/src/lib.rs +++ b/app/src-tauri/src/lib.rs @@ -1,539 +1,59 @@ -//! OpenHuman Desktop Application -//! -//! This is the Rust backend for the cross-platform crypto community platform. -//! It provides deep link handling, core process RPC relay, window management, -//! and AI configuration helpers. - #[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))] compile_error!("src-tauri host is desktop-only. Non-desktop targets are not supported."); -mod commands; mod core_process; -mod core_rpc; -mod utils; -use commands::*; -use serde::Serialize; -use serde_json::json; -use std::collections::HashMap; -use std::path::PathBuf; -use tauri::{AppHandle, Emitter, Manager, RunEvent}; -use tokio::time::{interval, Duration}; +use tauri::{Manager, RunEvent}; -// register_all() is only for Windows/Linux; macOS uses Info.plist (see docs). #[cfg(any(windows, target_os = "linux"))] use tauri_plugin_deep_link::DeepLinkExt; -/// Demo command - can be removed in production #[tauri::command] -fn greet(name: &str) -> String { - format!("Hello, {}! You've been greeted from Rust!", name) -} - -#[derive(Debug, Clone, Serialize)] -#[serde(rename_all = "camelCase")] -struct AIPreview { - soul: AIPreviewSoul, - tools: AIPreviewTools, - metadata: AIPreviewMetadata, -} - -#[derive(Debug, Clone, Serialize)] -#[serde(rename_all = "camelCase")] -struct AIPreviewSoul { - raw: String, - name: String, - description: String, - personality_preview: Vec, - safety_rules_preview: Vec, - loaded_at: i64, -} - -#[derive(Debug, Clone, Serialize)] -#[serde(rename_all = "camelCase")] -struct AIPreviewTools { - raw: String, - total_tools: usize, - active_skills: usize, - skills_preview: Vec, - loaded_at: i64, -} - -#[derive(Debug, Clone, Serialize)] -#[serde(rename_all = "camelCase")] -struct AIPreviewMetadata { - loaded_at: i64, - loading_duration: i64, - has_fallbacks: bool, - sources: AIPreviewSources, - errors: Vec, -} - -#[derive(Debug, Clone, Serialize)] -#[serde(rename_all = "camelCase")] -struct AIPreviewSources { - soul: String, - tools: String, -} - -fn now_ms() -> i64 { - chrono::Utc::now().timestamp_millis() -} - -fn extract_section(raw: &str, heading: &str) -> String { - let marker = format!("## {heading}"); - let Some(start) = raw.find(&marker) else { - return String::new(); - }; - let body = &raw[start + marker.len()..]; - if let Some(next_idx) = body.find("\n## ") { - body[..next_idx].trim().to_string() - } else { - body.trim().to_string() - } -} - -fn parse_soul_preview(raw: String, loaded_at: i64) -> AIPreviewSoul { - let name = raw - .lines() - .find_map(|line| line.strip_prefix("# ").map(|s| s.trim().to_string())) - .unwrap_or_else(|| "OpenHuman".to_string()); - - let description = raw - .lines() - .map(str::trim) - .find(|line| !line.is_empty() && !line.starts_with('#')) - .unwrap_or("AI assistant") - .to_string(); - - let personality_preview = extract_section(&raw, "Personality") - .lines() - .filter_map(|line| line.trim().strip_prefix("- **")) - .filter_map(|line| { - let mut parts = line.splitn(2, "**:"); - let trait_name = parts.next()?.trim(); - let detail = parts.next().unwrap_or("").trim(); - Some(format!("{trait_name}: {detail}")) - }) - .take(3) - .collect::>(); - - let safety_rules_preview = extract_section(&raw, "Safety Rules") - .lines() - .filter_map(|line| { - let trimmed = line.trim(); - let dot_idx = trimmed.find('.')?; - let (prefix, rest) = trimmed.split_at(dot_idx); - if prefix.chars().all(|c| c.is_ascii_digit()) { - Some(rest.trim_start_matches('.').trim().to_string()) - } else { - None - } - }) - .take(3) - .collect::>(); - - AIPreviewSoul { - raw, - name, - description, - personality_preview, - safety_rules_preview, - loaded_at, - } -} - -fn parse_tools_preview(raw: String, loaded_at: i64) -> AIPreviewTools { - let mut current_skill = "General".to_string(); - let mut skill_counts: HashMap = HashMap::new(); - let mut total_tools = 0usize; - - for line in raw.lines() { - let trimmed = line.trim(); - if let Some(title) = trimmed.strip_prefix("### ") { - if let Some(skill_title) = title.strip_suffix(" Tools") { - current_skill = skill_title.trim().to_string(); - skill_counts.entry(current_skill.clone()).or_insert(0); - } - continue; - } - if trimmed.starts_with("#### ") { - total_tools += 1; - *skill_counts.entry(current_skill.clone()).or_insert(0) += 1; - } - } - - let mut skills = skill_counts.into_iter().collect::>(); - let active_skills = skills.len(); - skills.sort_by(|a, b| b.1.cmp(&a.1).then_with(|| a.0.cmp(&b.0))); - let skills_preview = skills - .into_iter() - .take(6) - .map(|(name, count)| format!("{name} ({count})")) - .collect::>(); - - AIPreviewTools { - raw, - total_tools, - active_skills, - skills_preview, - loaded_at, - } -} - -fn resolve_ai_directory(app: &tauri::AppHandle) -> Option<(PathBuf, &'static str)> { - if let Ok(resource_dir) = app.path().resource_dir() { - if let Some(ai_dir) = utils::dev_paths::bundled_openclaw_prompts_dir(&resource_dir) { - return Some((ai_dir, "bundled")); - } - } - - if let Ok(cwd) = std::env::current_dir() { - if let Some(path) = utils::dev_paths::repo_ai_prompts_dir(&cwd) { - return Some((path, "bundled")); - } - - let fallback = cwd.join("ai"); - if fallback.is_dir() { - return Some((fallback, "bundled")); - } - } - - None -} - -fn build_ai_preview(app: &tauri::AppHandle) -> AIPreview { - let started = now_ms(); - let loaded_at = now_ms(); - let mut errors = Vec::new(); - let mut soul_raw = String::new(); - let mut tools_raw = String::new(); - let mut source = "bundled".to_string(); - - if let Some((ai_dir, resolved_source)) = resolve_ai_directory(app) { - source = resolved_source.to_string(); - let soul_path = ai_dir.join("SOUL.md"); - let tools_path = ai_dir.join("TOOLS.md"); - soul_raw = std::fs::read_to_string(&soul_path).unwrap_or_else(|e| { - errors.push(format!("Failed to read SOUL.md: {e}")); - String::new() - }); - tools_raw = std::fs::read_to_string(&tools_path).unwrap_or_else(|e| { - errors.push(format!("Failed to read TOOLS.md: {e}")); - String::new() - }); - } else { - errors.push("AI config directory not found".to_string()); - } - - let soul = parse_soul_preview(soul_raw, loaded_at); - let tools = parse_tools_preview(tools_raw, loaded_at); - let done = now_ms(); - - AIPreview { - soul, - tools, - metadata: AIPreviewMetadata { - loaded_at: done, - loading_duration: done - started, - has_fallbacks: false, - sources: AIPreviewSources { - soul: source.clone(), - tools: source, - }, - errors, - }, - } -} - -#[tauri::command] -async fn ai_get_config(app: tauri::AppHandle) -> Result { - Ok(build_ai_preview(&app)) -} - -#[tauri::command] -async fn ai_refresh_config(app: tauri::AppHandle) -> Result { - Ok(build_ai_preview(&app)) -} - -/// Write AI configuration files to `src/openhuman/agent/prompts` in the repo (dev resolution from cwd). -#[tauri::command] -async fn write_ai_config_file(filename: String, content: String) -> Result { - use std::env; - - // Determine runtime working directory - let current_dir = - env::current_dir().map_err(|e| format!("Failed to get current directory: {e}"))?; - - // Ensure filename is safe (only allow .md files) - if !filename.ends_with(".md") { - return Err("Only .md files are allowed".to_string()); - } - - // Prevent path traversal by checking for dangerous characters - if filename.contains("..") || filename.contains("/") || filename.contains("\\") { - return Err("Invalid filename: path traversal not allowed".to_string()); - } - - let ai_dir = utils::dev_paths::repo_ai_prompts_dir(¤t_dir).unwrap_or_else(|| { - current_dir - .join("src") - .join("openhuman") - .join("agent") - .join("prompts") - }); - let file_path = ai_dir.join(&filename); - - // Ensure ai directory exists - std::fs::create_dir_all(&ai_dir).map_err(|e| format!("Failed to create ai directory: {e}"))?; - - // Write the file - std::fs::write(&file_path, content) - .map_err(|e| format!("Failed to write file {}: {e}", filename))?; - - Ok(true) +fn core_rpc_url() -> String { + std::env::var("OPENHUMAN_CORE_RPC_URL") + .unwrap_or_else(|_| "http://127.0.0.1:7788/rpc".to_string()) } fn is_daemon_mode() -> bool { std::env::args().any(|arg| arg == "daemon" || arg == "--daemon") } -/// Poll core RPC health and bridge updates to frontend Tauri events. -async fn watch_daemon_health_rpc(app_handle: AppHandle) { - let mut interval = interval(Duration::from_secs(2)); - let mut last_snapshot: Option = None; - let mut had_error = false; - - log::info!("[openhuman] Watching daemon health via core RPC (openhuman.health_snapshot)"); - - loop { - interval.tick().await; - - match crate::core_rpc::call::("openhuman.health_snapshot", json!({})) - .await - { - Ok(raw_payload) => { - // RpcOutcome may be wrapped as {"result": {...}, "logs": [...]}; normalize to snapshot. - let snapshot = raw_payload.get("result").cloned().unwrap_or(raw_payload); - - if last_snapshot.as_ref() != Some(&snapshot) { - if let Err(e) = app_handle.emit("openhuman:health", &snapshot) { - log::error!("[openhuman] Failed to emit health event from RPC: {e}"); - } else { - last_snapshot = Some(snapshot); - } - } - - if had_error { - had_error = false; - log::info!("[openhuman] Health RPC polling recovered"); - } - } - Err(e) => { - if !had_error { - had_error = true; - log::debug!("[openhuman] Health RPC not ready yet: {e}"); - } - } - } - } -} - pub fn run() { - if let Err(err) = rustls::crypto::ring::default_provider().install_default() { - log::warn!( - "[app] rustls crypto provider not installed (already set?): {:?}", - err - ); - } else { - log::info!("[app] rustls crypto provider installed (ring)"); - } - let daemon_mode = is_daemon_mode(); - // Initialize logger - { - use env_logger::fmt::style::{AnsiColor, Style}; - use std::io::Write; + let default_filter = std::env::var("RUST_LOG").unwrap_or_else(|_| "info".to_string()); + let _ = env_logger::Builder::new() + .parse_filters(&default_filter) + .try_init(); - let default_filter = std::env::var("RUST_LOG") - .unwrap_or_else(|_| "info,tungstenite=warn,tokio_tungstenite=warn,reqwest=warn,rusqlite=warn,hyper=warn,h2=warn".to_string()); - - let write_style = std::env::var("RUST_LOG_STYLE") - .map(|v| match v.as_str() { - "never" => env_logger::fmt::WriteStyle::Never, - _ => env_logger::fmt::WriteStyle::Always, - }) - .unwrap_or(env_logger::fmt::WriteStyle::Always); - - let _ = env_logger::Builder::new() - .parse_filters(&default_filter) - .write_style(write_style) - .format(|buf, record| { - let timestamp = buf.timestamp_millis() - .to_string(); - // Strip the date prefix, keep only HH:MM:SS.mmm - let time_only = timestamp.split('T') - .nth(1) - .and_then(|t| t.strip_suffix('Z')) - .unwrap_or(×tamp); - let level = record.level(); - - // Level colors - let level_style = match level { - log::Level::Error => Style::new().fg_color(Some(AnsiColor::Red.into())).bold(), - log::Level::Warn => Style::new().fg_color(Some(AnsiColor::Yellow.into())).bold(), - log::Level::Info => Style::new().fg_color(Some(AnsiColor::Green.into())), - log::Level::Debug => Style::new().fg_color(Some(AnsiColor::BrightBlack.into())), - log::Level::Trace => Style::new().fg_color(Some(AnsiColor::BrightBlack.into())), - }; - - let msg = format!("{}", record.args()); - - // Extract tag from message (e.g. "[socket-mgr]", "[skill:x]") - let (tag, rest) = if msg.starts_with('[') { - if let Some(end) = msg.find(']') { - let tag = &msg[..=end]; - let rest = msg[end + 1..].trim_start(); - (Some(tag.to_string()), rest.to_string()) - } else { - (None, msg) - } - } else { - (None, msg) - }; - - // Tag-based colors - let tag_style = if let Some(ref t) = tag { - let t_lower = t.to_lowercase(); - if t_lower.contains("socket") { - Style::new().fg_color(Some(AnsiColor::Blue.into())).bold() - } else if t_lower.contains("runtime") { - Style::new().fg_color(Some(AnsiColor::Cyan.into())).bold() - } else if t_lower.contains("skill") { - Style::new().fg_color(Some(AnsiColor::Green.into())).bold() - } else if t_lower.contains("ping") || t_lower.contains("cron") { - Style::new().fg_color(Some(AnsiColor::Yellow.into())).bold() - } else if t_lower.contains("app") { - Style::new().fg_color(Some(AnsiColor::White.into())).bold() - } else if t_lower.contains("ai") { - Style::new().fg_color(Some(AnsiColor::BrightMagenta.into())).bold() - } else { - Style::new().fg_color(Some(AnsiColor::BrightBlack.into())) - } - } else { - Style::new() - }; - - let dim = Style::new().fg_color(Some(AnsiColor::BrightBlack.into())); - - if let Some(ref t) = tag { - writeln!( - buf, - "{dim}{time_only}{dim:#} {level_style}{level:<5}{level_style:#} {tag_style}{t}{tag_style:#} {rest}" - ) - } else { - writeln!( - buf, - "{dim}{time_only}{dim:#} {level_style}{level:<5}{level_style:#} {rest}" - ) - } - }) - .try_init(); - } - - let mut builder = tauri::Builder::default() - // Plugins - .plugin(tauri_plugin_opener::init()) + tauri::Builder::default() .plugin(tauri_plugin_deep_link::init()) - .plugin(tauri_plugin_os::init()); - - // Add desktop-only plugins (autostart, notification) - #[cfg(desktop)] - { - builder = builder - .plugin(tauri_plugin_autostart::init( - tauri_plugin_autostart::MacosLauncher::LaunchAgent, - Some(vec!["--daemon"]), - )) - .plugin(tauri_plugin_notification::init()); - } - - builder - // Setup .setup(move |app| { - // Register schemes at runtime (Windows/Linux only). macOS uses bundle Info.plist. #[cfg(any(windows, target_os = "linux"))] { app.deep_link().register_all()?; } - // macOS-specific: Handle window close event to minimize to tray - #[cfg(target_os = "macos")] - { - if let Some(window) = app.get_webview_window("main") { - let app_handle = app.handle().clone(); - window.on_window_event(move |event| { - if let tauri::WindowEvent::CloseRequested { api, .. } = event { - // Prevent the window from closing, hide it instead - api.prevent_close(); - if let Some(win) = app_handle.get_webview_window("main") { - let _ = win.hide(); - } - } - }); - } - } - - // Bridge daemon health via core RPC; optionally ensure OS background service (release only). - { - let app_handle_for_watcher = app.handle().clone(); - tauri::async_runtime::spawn(async move { - watch_daemon_health_rpc(app_handle_for_watcher).await; - }); - if commands::core_relay::should_autostart_service_managed_core() { - tauri::async_runtime::spawn(async move { - match commands::core_relay::ensure_service_managed_core_running().await { - Ok(()) => { - log::info!("[openhuman] Core background service ensured via core RPC"); - } - Err(e) => { - log::error!( - "[openhuman] Failed to ensure core background service: {e}" - ); - } - } - }); + let core_run_mode = core_process::default_core_run_mode(daemon_mode); + let core_bin = if matches!(core_run_mode, core_process::CoreRunMode::ChildProcess) { + core_process::default_core_bin() + } else { + None + }; + let core_handle = core_process::CoreProcessHandle::new( + core_process::default_core_port(), + core_bin, + core_run_mode, + ); + std::env::set_var("OPENHUMAN_CORE_RPC_URL", core_handle.rpc_url()); + app.manage(core_handle.clone()); + tauri::async_runtime::spawn(async move { + if let Err(err) = core_handle.ensure_running().await { + log::error!("[core] failed to start core process: {err}"); } else { - log::info!( - "[openhuman] Skipping OS background service autostart (dev build or OPENHUMAN_SKIP_SERVICE_AUTOSTART)" - ); + log::info!("[core] core process ready"); } - } - - // Start/ensure standalone core process for business logic RPC. - { - let core_run_mode = core_process::default_core_run_mode(daemon_mode); - let core_bin = if matches!(core_run_mode, core_process::CoreRunMode::ChildProcess) { - core_process::default_core_bin() - } else { - None - }; - let core_handle = core_process::CoreProcessHandle::new( - core_process::default_core_port(), - core_bin, - core_run_mode, - ); - std::env::set_var("OPENHUMAN_CORE_RPC_URL", core_handle.rpc_url()); - app.manage(core_handle.clone()); - tauri::async_runtime::spawn(async move { - if let Err(err) = core_handle.ensure_running().await { - log::error!("[core] failed to start core process: {err}"); - } else { - log::info!("[core] core process ready"); - } - }); - } + }); if daemon_mode { if let Some(window) = app.get_webview_window("main") { @@ -543,39 +63,7 @@ pub fn run() { Ok(()) }) - // Register all commands (desktop build lists handlers explicitly below). - .invoke_handler({ - #[cfg(desktop)] - { - tauri::generate_handler![ - greet, - // AI config file writing - write_ai_config_file, - ai_get_config, - ai_refresh_config, - core_rpc_relay, - core_rpc_url, - show_window, - hide_window, - toggle_window, - is_window_visible, - minimize_window, - maximize_window, - close_window, - set_window_title, - // OpenHuman local host commands (core RPC uses core_rpc_relay) - openhuman_get_daemon_host_config, - openhuman_set_daemon_host_config, - openhuman_service_install, - openhuman_service_start, - openhuman_service_stop, - openhuman_service_status, - openhuman_service_uninstall, - chat_send, - chat_cancel, - ] - } - }) + .invoke_handler(tauri::generate_handler![core_rpc_url]) .build({ let mut context = tauri::generate_context!(); if daemon_mode { @@ -584,48 +72,38 @@ pub fn run() { context }) .expect("error while building tauri application") - .run(move |app_handle, event| { - match event { - // Handle macOS Dock icon click (reopen event) - #[cfg(target_os = "macos")] - RunEvent::Reopen { .. } => { - if !daemon_mode { - if let Some(window) = app_handle.get_webview_window("main") { - let _ = window.show(); - let _ = window.unminimize(); - let _ = window.set_focus(); - } + .run(move |app_handle, event| match event { + #[cfg(target_os = "macos")] + RunEvent::Reopen { .. } => { + if !daemon_mode { + if let Some(window) = app_handle.get_webview_window("main") { + let _ = window.show(); + let _ = window.unminimize(); + let _ = window.set_focus(); } } - - // Stop the core sidecar we spawned so dev/prod do not leave orphan `openhuman core run`. - RunEvent::Exit => { - log::info!("[app] Exit event received, shutting down"); - - if let Some(core) = app_handle.try_state::() { - let core = core.inner().clone(); - tauri::async_runtime::block_on(async move { - core.shutdown().await; - }); - } - } - - _ => { - let _ = app_handle; - } } + RunEvent::Exit => { + if let Some(core) = app_handle.try_state::() { + let core = core.inner().clone(); + tauri::async_runtime::block_on(async move { + core.shutdown().await; + }); + } + } + _ => {} }); } -pub fn run_core_from_args(args: &[String]) -> anyhow::Result<()> { +pub fn run_core_from_args(args: &[String]) -> Result<(), String> { let core_bin = crate::core_process::default_core_bin() - .ok_or_else(|| anyhow::anyhow!("openhuman core binary not found"))?; + .ok_or_else(|| "openhuman core binary not found".to_string())?; let status = std::process::Command::new(core_bin) .args(args) .status() - .map_err(|e| anyhow::anyhow!("failed to execute core binary: {e}"))?; + .map_err(|e| format!("failed to execute core binary: {e}"))?; if !status.success() { - anyhow::bail!("core binary exited with status {status}"); + return Err(format!("core binary exited with status {status}")); } Ok(()) } diff --git a/app/src-tauri/src/utils/dev_paths.rs b/app/src-tauri/src/utils/dev_paths.rs deleted file mode 100644 index 37526391e..000000000 --- a/app/src-tauri/src/utils/dev_paths.rs +++ /dev/null @@ -1,49 +0,0 @@ -//! Dev-time path resolution for bundled / repo AI prompts (desktop host). - -use std::path::{Path, PathBuf}; - -/// Best-effort path to repo `src/openhuman/agent/prompts` when running from a checkout. -pub fn repo_ai_prompts_dir(cwd: &Path) -> Option { - let prompts = cwd - .join("src") - .join("openhuman") - .join("agent") - .join("prompts"); - if prompts.is_dir() { - return Some(prompts); - } - let from_app = cwd - .join("..") - .join("src") - .join("openhuman") - .join("agent") - .join("prompts"); - if from_app.is_dir() { - return Some(from_app); - } - let app_prompts = cwd - .join("app") - .join("src") - .join("openhuman") - .join("agent") - .join("prompts"); - if app_prompts.is_dir() { - return Some(app_prompts); - } - None -} - -/// Bundled OpenClaw-style prompts inside the packaged app resource dir. -pub fn bundled_openclaw_prompts_dir(resource_dir: &Path) -> Option { - let candidates = [ - resource_dir.join("openhuman").join("agent").join("prompts"), - resource_dir.join("ai").join("prompts"), - resource_dir.join("_up_").join("ai").join("prompts"), - ]; - for p in candidates { - if p.is_dir() { - return Some(p); - } - } - None -} diff --git a/app/src-tauri/src/utils/mod.rs b/app/src-tauri/src/utils/mod.rs deleted file mode 100644 index 5fd135168..000000000 --- a/app/src-tauri/src/utils/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod dev_paths; diff --git a/app/src/AppRoutes.tsx b/app/src/AppRoutes.tsx index e7b4dbe4f..76b0d88e8 100644 --- a/app/src/AppRoutes.tsx +++ b/app/src/AppRoutes.tsx @@ -17,6 +17,7 @@ import Skills from './pages/Skills'; import Welcome from './pages/Welcome'; import { selectHasEncryptionKey, selectIsOnboarded } from './store/authSelectors'; import { useAppSelector } from './store/hooks'; +import { DEV_FORCE_ONBOARDING } from './utils/config'; import { DEFAULT_WORKSPACE_ONBOARDING_FLAG, openhumanWorkspaceOnboardingFlagExists, @@ -33,7 +34,9 @@ const OnboardingRoute = ({ }: OnboardingRouteProps) => { const isOnboarded = useAppSelector(selectIsOnboarded); const hasEncryptionKey = useAppSelector(selectHasEncryptionKey); - const shouldSkipOnboarding = isOnboarded || hasWorkspaceOnboardingFlag; + const shouldSkipOnboarding = DEV_FORCE_ONBOARDING + ? false + : isOnboarded || hasWorkspaceOnboardingFlag; if (isWorkspaceFlagLoading) return ; if (shouldSkipOnboarding && !hasEncryptionKey) return ; @@ -61,7 +64,9 @@ const HomeRoute = ({ hasWorkspaceOnboardingFlag, isWorkspaceFlagLoading }: HomeR const isOnboarded = useAppSelector(selectIsOnboarded); const hasEncryptionKey = useAppSelector(selectHasEncryptionKey); - const shouldSkipOnboarding = isOnboarded || hasWorkspaceOnboardingFlag; + const shouldSkipOnboarding = DEV_FORCE_ONBOARDING + ? false + : isOnboarded || hasWorkspaceOnboardingFlag; // While user profile is still loading, show Home (avoid flash to onboarding) if (!user) return ; @@ -83,6 +88,11 @@ const AppRoutes = () => { const [isWorkspaceFlagLoading, setIsWorkspaceFlagLoading] = useState(true); useEffect(() => { + if (DEV_FORCE_ONBOARDING) { + setHasWorkspaceOnboardingFlag(false); + setIsWorkspaceFlagLoading(false); + return; + } let mounted = true; const loadWorkspaceFlag = async () => { try { diff --git a/app/src/services/daemonHealthService.ts b/app/src/services/daemonHealthService.ts index c1d7f6551..7ec7a89a3 100644 --- a/app/src/services/daemonHealthService.ts +++ b/app/src/services/daemonHealthService.ts @@ -1,11 +1,9 @@ /** * Daemon Health Service * - * Manages health monitoring for the openhuman daemon by listening to - * 'openhuman:health' events emitted by the Rust backend every 5 seconds. + * Manages health monitoring for the openhuman daemon by polling + * `openhuman.health_snapshot` over core RPC from the frontend. */ -import { listen, type UnlistenFn } from '@tauri-apps/api/event'; - import { store } from '../store'; import { type ComponentHealth, @@ -14,55 +12,52 @@ import { setHealthTimeoutId, updateHealthSnapshot, } from '../store/daemonSlice'; +import { callCoreRpc } from './coreRpcClient'; export class DaemonHealthService { private healthTimeoutId: ReturnType | null = null; private readonly HEALTH_TIMEOUT_MS = 30000; // 30 seconds - private healthEventListener: UnlistenFn | null = null; + private pollingIntervalId: ReturnType | null = null; + private readonly POLL_MS = 2000; /** * Setup health event listener from the Rust daemon. * Should be called once when the app starts in Tauri mode. */ - async setupHealthListener(): Promise { - console.log('[DaemonHealth] setupHealthListener() called - starting setup process'); - try { - // console.log('[DaemonHealth] About to call listen() for openhuman:health event'); - // console.log('[DaemonHealth] Setting up openhuman:health event listener'); + async setupHealthListener(): Promise<(() => void) | null> { + if (this.pollingIntervalId) { + return () => this.cleanup(); + } - this.healthEventListener = await listen('openhuman:health', event => { - // console.log('[DaemonHealth] Received health event:', event.payload); - - const healthSnapshot = this.parseHealthSnapshot(event.payload); + const pollOnce = async () => { + try { + const payload = await callCoreRpc({ method: 'openhuman.health_snapshot' }); + const healthSnapshot = this.parseHealthSnapshot(payload); if (healthSnapshot) { this.updateReduxFromHealth(healthSnapshot); this.startHealthTimeout(); - } else { - console.warn('[DaemonHealth] Failed to parse health snapshot:', event.payload); } - }); - console.log('[DaemonHealth] openhuman:health listener created successfully'); + } catch { + // Health endpoint can fail while sidecar is starting; timeout will mark disconnected. + } + }; - // Start initial timeout - // console.log('[DaemonHealth] Starting health timeout'); - this.startHealthTimeout(); - // console.log('[DaemonHealth] Health timeout started'); + await pollOnce(); + this.pollingIntervalId = setInterval(() => { + void pollOnce(); + }, this.POLL_MS); + this.startHealthTimeout(); - // console.log('[DaemonHealth] Health listener setup complete'); - return this.healthEventListener; - } catch (error) { - console.error('[DaemonHealth] Failed to setup health listener:', error); - return null; - } + return () => this.cleanup(); } /** * Cleanup the health event listener. */ cleanup(): void { - if (this.healthEventListener) { - this.healthEventListener(); - this.healthEventListener = null; + if (this.pollingIntervalId) { + clearInterval(this.pollingIntervalId); + this.pollingIntervalId = null; } if (this.healthTimeoutId) { diff --git a/app/src/utils/config.ts b/app/src/utils/config.ts index a80ae9780..06f638f8c 100644 --- a/app/src/utils/config.ts +++ b/app/src/utils/config.ts @@ -5,5 +5,9 @@ export const CORE_RPC_URL = export const IS_DEV = import.meta.env.DEV; +/** Dev only: skip `.skip_onboarding` workspace check and ignore onboarded state so `/onboarding` always shows. Set `VITE_DEV_FORCE_ONBOARDING=true` in `.env.local`. */ +export const DEV_FORCE_ONBOARDING = + import.meta.env.DEV && import.meta.env.VITE_DEV_FORCE_ONBOARDING === 'true'; + export const SKILLS_GITHUB_REPO = import.meta.env.VITE_SKILLS_GITHUB_REPO || 'tinyhumansai/openhuman-skills'; diff --git a/app/src/utils/desktopDeepLinkListener.ts b/app/src/utils/desktopDeepLinkListener.ts index 6a59d44bd..7609b5b2e 100644 --- a/app/src/utils/desktopDeepLinkListener.ts +++ b/app/src/utils/desktopDeepLinkListener.ts @@ -1,4 +1,5 @@ -import { isTauri as coreIsTauri, invoke } from '@tauri-apps/api/core'; +import { isTauri as coreIsTauri } from '@tauri-apps/api/core'; +import { getCurrentWindow } from '@tauri-apps/api/window'; import { getCurrent, onOpenUrl } from '@tauri-apps/plugin-deep-link'; import { skillManager } from '../lib/skills/manager'; @@ -35,6 +36,17 @@ function getCurrentUserId(): string | null { } } +const focusMainWindow = async () => { + try { + const window = getCurrentWindow(); + await window.show(); + await window.unminimize(); + await window.setFocus(); + } catch (err) { + console.warn('[DeepLink] Failed to focus window:', err); + } +}; + /** * Handle an `openhuman://auth?token=...` deep link for login. */ @@ -48,11 +60,7 @@ const handleAuthDeepLink = async (parsed: URL) => { console.log('[DeepLink] Received auth token', token); - try { - await invoke('show_window'); - } catch (err) { - console.warn('[DeepLink] Failed to show window:', err); - } + await focusMainWindow(); if (key === 'auth') { store.dispatch(setToken(token)); @@ -72,11 +80,7 @@ const handleAuthDeepLink = async (parsed: URL) => { const handlePaymentDeepLink = async (parsed: URL) => { const path = parsed.pathname.replace(/^\/+/, ''); - try { - await invoke('show_window'); - } catch { - // Not fatal - } + await focusMainWindow(); if (path === 'success') { const sessionId = parsed.searchParams.get('session_id'); @@ -110,11 +114,7 @@ const handleOAuthDeepLink = async (parsed: URL) => { // pathname is "/success" or "/error" (hostname is "oauth") const path = parsed.pathname.replace(/^\/+/, ''); - try { - await invoke('show_window'); - } catch { - // Not fatal - } + await focusMainWindow(); if (path === 'success') { const integrationId = parsed.searchParams.get('integrationId'); diff --git a/app/src/utils/tauriCommands.ts b/app/src/utils/tauriCommands.ts index ab6d82c30..213b3327e 100644 --- a/app/src/utils/tauriCommands.ts +++ b/app/src/utils/tauriCommands.ts @@ -4,6 +4,7 @@ * Helper functions for invoking Tauri commands from the frontend. */ import { isTauri as coreIsTauri, invoke } from '@tauri-apps/api/core'; +import { getCurrentWindow } from '@tauri-apps/api/window'; import { callCoreRpc } from '../services/coreRpcClient'; @@ -86,7 +87,10 @@ export async function showWindow(): Promise { return; } - await invoke('show_window'); + const window = getCurrentWindow(); + await window.show(); + await window.unminimize(); + await window.setFocus(); } /** @@ -97,7 +101,7 @@ export async function hideWindow(): Promise { return; } - await invoke('hide_window'); + await getCurrentWindow().hide(); } /** @@ -108,7 +112,15 @@ export async function toggleWindow(): Promise { return; } - await invoke('toggle_window'); + const window = getCurrentWindow(); + const visible = await window.isVisible(); + if (visible) { + await window.hide(); + return; + } + await window.show(); + await window.unminimize(); + await window.setFocus(); } /** @@ -119,7 +131,7 @@ export async function isWindowVisible(): Promise { return true; // In browser, window is always visible } - return await invoke('is_window_visible'); + return await getCurrentWindow().isVisible(); } /** @@ -130,7 +142,7 @@ export async function minimizeWindow(): Promise { return; } - await invoke('minimize_window'); + await getCurrentWindow().minimize(); } /** @@ -141,7 +153,8 @@ export async function maximizeWindow(): Promise { return; } - await invoke('maximize_window'); + const window = getCurrentWindow(); + await window.toggleMaximize(); } /** @@ -152,7 +165,7 @@ export async function closeWindow(): Promise { return; } - await invoke('close_window'); + await getCurrentWindow().close(); } /** @@ -164,7 +177,7 @@ export async function setWindowTitle(title: string): Promise { return; } - await invoke('set_window_title', { title }); + await getCurrentWindow().setTitle(title); } // --- Memory Commands --- @@ -348,10 +361,6 @@ export interface CommandResponse { logs: string[]; } -function wrapCommandResult(result: T): CommandResponse { - return { result, logs: [] }; -} - export interface SkillSnapshot { skill_id: string; name: string; @@ -1254,17 +1263,28 @@ export async function openhumanLocalAiDownloadAsset( } export async function aiGetConfig(): Promise { - if (!isTauri()) { - throw new Error('Not running in Tauri'); - } - return await invoke('ai_get_config'); + return { + soul: { + raw: '', + name: 'OpenHuman', + description: 'AI assistant', + personalityPreview: [], + safetyRulesPreview: [], + loadedAt: Date.now(), + }, + tools: { raw: '', totalTools: 0, activeSkills: 0, skillsPreview: [], loadedAt: Date.now() }, + metadata: { + loadedAt: Date.now(), + loadingDuration: 0, + hasFallbacks: true, + sources: { soul: 'frontend', tools: 'frontend' }, + errors: ['AI prompt preview has been moved out of the Tauri host.'], + }, + }; } export async function aiRefreshConfig(): Promise { - if (!isTauri()) { - throw new Error('Not running in Tauri'); - } - return await invoke('ai_refresh_config'); + return aiGetConfig(); } export async function openhumanEncryptSecret(plaintext: string): Promise> { @@ -1344,44 +1364,36 @@ export async function openhumanServiceInstall(): Promise>('core_rpc_relay', { - request: { method: 'openhuman.service_install', params: {} }, - }); + return await callCoreRpc>({ method: 'openhuman.service_install' }); } export async function openhumanServiceStart(): Promise> { if (!isTauri()) { throw new Error('Not running in Tauri'); } - return await invoke>('core_rpc_relay', { - request: { method: 'openhuman.service_start', params: {} }, - }); + return await callCoreRpc>({ method: 'openhuman.service_start' }); } export async function openhumanServiceStop(): Promise> { if (!isTauri()) { throw new Error('Not running in Tauri'); } - return await invoke>('core_rpc_relay', { - request: { method: 'openhuman.service_stop', params: {} }, - }); + return await callCoreRpc>({ method: 'openhuman.service_stop' }); } export async function openhumanServiceStatus(): Promise> { if (!isTauri()) { throw new Error('Not running in Tauri'); } - return await invoke>('core_rpc_relay', { - request: { method: 'openhuman.service_status', params: {} }, - }); + return await callCoreRpc>({ method: 'openhuman.service_status' }); } export async function openhumanServiceUninstall(): Promise> { if (!isTauri()) { throw new Error('Not running in Tauri'); } - return await invoke>('core_rpc_relay', { - request: { method: 'openhuman.service_uninstall', params: {} }, + return await callCoreRpc>({ + method: 'openhuman.service_uninstall', }); } @@ -1389,8 +1401,8 @@ export async function openhumanAgentServerStatus(): Promise>('core_rpc_relay', { - request: { method: 'openhuman.agent_server_status', params: {} }, + return await callCoreRpc>({ + method: 'openhuman.agent_server_status', }); } @@ -1398,8 +1410,9 @@ export async function openhumanGetDaemonHostConfig(): Promise('openhuman_get_daemon_host_config'); - return wrapCommandResult(result); + return await callCoreRpc>({ + method: 'openhuman.service_daemon_host_get', + }); } export async function openhumanSetDaemonHostConfig( @@ -1408,8 +1421,10 @@ export async function openhumanSetDaemonHostConfig( if (!isTauri()) { throw new Error('Not running in Tauri'); } - const result = await invoke('openhuman_set_daemon_host_config', { showTray }); - return wrapCommandResult(result); + return await callCoreRpc>({ + method: 'openhuman.service_daemon_host_set', + params: { show_tray: showTray }, + }); } export async function openhumanAccessibilityStatus(): Promise< diff --git a/src/openhuman/service/ops.rs b/src/openhuman/service/ops.rs index 4f93564af..40b895c50 100644 --- a/src/openhuman/service/ops.rs +++ b/src/openhuman/service/ops.rs @@ -1,7 +1,8 @@ //! JSON-RPC / CLI controller surface for platform service install/lifecycle. use crate::openhuman::config::Config; -use crate::openhuman::service::{self, ServiceStatus}; +use crate::openhuman::service::daemon_host::DaemonHostConfig; +use crate::openhuman::service::{self, daemon_host, ServiceStatus}; use crate::rpc::RpcOutcome; pub async fn service_install(config: &Config) -> Result, String> { @@ -31,3 +32,25 @@ pub async fn service_uninstall(config: &Config) -> Result Result, String> { + let config_dir = config + .config_path + .parent() + .ok_or_else(|| "failed to resolve config directory".to_string())?; + let current = daemon_host::load_for_config_dir(config_dir).await; + Ok(RpcOutcome::single_log(current, "daemon host config loaded")) +} + +pub async fn daemon_host_set( + config: &Config, + show_tray: bool, +) -> Result, String> { + let config_dir = config + .config_path + .parent() + .ok_or_else(|| "failed to resolve config directory".to_string())?; + let next = DaemonHostConfig { show_tray }; + daemon_host::save_for_config_dir(config_dir, &next).await?; + Ok(RpcOutcome::single_log(next, "daemon host config saved")) +} diff --git a/src/openhuman/service/schemas.rs b/src/openhuman/service/schemas.rs index 0f591bbfe..1a4ab4b37 100644 --- a/src/openhuman/service/schemas.rs +++ b/src/openhuman/service/schemas.rs @@ -1,3 +1,4 @@ +use serde::Deserialize; use serde_json::{Map, Value}; use crate::core::all::{ControllerFuture, RegisteredController}; @@ -12,6 +13,8 @@ pub fn all_controller_schemas() -> Vec { schemas("stop"), schemas("status"), schemas("uninstall"), + schemas("daemon_host_get"), + schemas("daemon_host_set"), ] } @@ -37,6 +40,14 @@ pub fn all_registered_controllers() -> Vec { schema: schemas("uninstall"), handler: handle_uninstall, }, + RegisteredController { + schema: schemas("daemon_host_get"), + handler: handle_daemon_host_get, + }, + RegisteredController { + schema: schemas("daemon_host_set"), + handler: handle_daemon_host_set, + }, ] } @@ -60,6 +71,35 @@ pub fn schemas(function: &str) -> ControllerSchema { required: true, }], }, + "daemon_host_get" => ControllerSchema { + namespace: "service", + function: "daemon_host_get", + description: "Read daemon host UI preferences.", + inputs: vec![], + outputs: vec![FieldSchema { + name: "status", + ty: TypeSchema::Ref("DaemonHostConfig"), + comment: "Daemon host preference payload.", + required: true, + }], + }, + "daemon_host_set" => ControllerSchema { + namespace: "service", + function: "daemon_host_set", + description: "Update daemon host UI preferences.", + inputs: vec![FieldSchema { + name: "show_tray", + ty: TypeSchema::Bool, + comment: "Show tray icon in daemon-host mode.", + required: true, + }], + outputs: vec![FieldSchema { + name: "status", + ty: TypeSchema::Ref("DaemonHostConfig"), + comment: "Updated daemon host preference payload.", + required: true, + }], + }, _ => ControllerSchema { namespace: "service", function: "unknown", @@ -110,6 +150,27 @@ fn handle_uninstall(_params: Map) -> ControllerFuture { }) } +#[derive(Debug, Deserialize)] +struct DaemonHostSetParams { + show_tray: bool, +} + +fn handle_daemon_host_get(_params: Map) -> ControllerFuture { + Box::pin(async move { + let config = config_rpc::load_config_with_timeout().await?; + to_json(crate::openhuman::service::rpc::daemon_host_get(&config).await?) + }) +} + +fn handle_daemon_host_set(params: Map) -> ControllerFuture { + Box::pin(async move { + let payload: DaemonHostSetParams = + serde_json::from_value(Value::Object(params)).map_err(|e| e.to_string())?; + let config = config_rpc::load_config_with_timeout().await?; + to_json(crate::openhuman::service::rpc::daemon_host_set(&config, payload.show_tray).await?) + }) +} + fn to_json(outcome: RpcOutcome) -> Result { outcome.into_cli_compatible_json() }