diff --git a/Cargo.lock b/Cargo.lock index 1cbd499..06cfd31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -225,7 +225,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -261,61 +261,6 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "ashpd" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df" -dependencies = [ - "enumflags2", - "futures-channel", - "futures-util", - "rand 0.9.2", - "raw-window-handle", - "serde", - "serde_repr", - "tokio", - "url", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "zbus", -] - -[[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-recursion" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.113", -] - -[[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.113", -] - [[package]] name = "atk" version = "0.18.2" @@ -411,6 +356,28 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "aws-lc-rs" +version = "1.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e84ce723ab67259cfeb9877c6a639ee9eb7a27b28123abd71db7f0d5d0cc9d86" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a442ece363113bd4bd4c8b18977a7798dd4d3c3383f34fb61936960e8f4ad8" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + [[package]] name = "base64" version = "0.21.7" @@ -501,6 +468,16 @@ dependencies = [ "alloc-stdlib", ] +[[package]] +name = "bstr" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "built" version = "0.8.0" @@ -530,7 +507,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -687,14 +664,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" dependencies = [ "serde", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.11+spec-1.1.0", ] [[package]] name = "cc" -version = "1.2.51" +version = "1.2.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" +checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932" dependencies = [ "find-msvc-tools", "jobserver", @@ -753,9 +730,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" dependencies = [ "iana-time-zone", "js-sys", @@ -817,6 +794,15 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "cmake" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +dependencies = [ + "cc", +] + [[package]] name = "color_quant" version = "1.1.0" @@ -1079,7 +1065,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -1089,7 +1075,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -1119,7 +1105,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -1130,7 +1116,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -1267,9 +1253,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "deranged" @@ -1289,7 +1275,7 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -1302,7 +1288,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -1323,7 +1309,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.113", + "syn 2.0.114", "unicode-xid", ] @@ -1405,7 +1391,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -1437,7 +1423,7 @@ checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -1536,7 +1522,7 @@ dependencies = [ "cc", "memchr", "rustc_version", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.11+spec-1.1.0", "vswhom", "winreg", ] @@ -1556,12 +1542,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "endi" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" - [[package]] name = "enum-as-inner" version = "0.6.1" @@ -1571,28 +1551,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.113", -] - -[[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.113", + "syn 2.0.114", ] [[package]] @@ -1605,6 +1564,12 @@ dependencies = [ "regex", ] +[[package]] +name = "env_home" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" + [[package]] name = "env_logger" version = "0.11.8" @@ -1635,7 +1600,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -1665,27 +1630,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 = "exr" version = "1.74.0" @@ -1730,7 +1674,7 @@ checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -1754,15 +1698,15 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" +checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" [[package]] name = "flate2" -version = "1.1.5" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" +checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" dependencies = [ "crc32fast", "miniz_oxide", @@ -1837,7 +1781,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -1855,6 +1799,12 @@ 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" @@ -1872,6 +1822,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -1897,19 +1848,6 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" -[[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.31" @@ -1918,7 +1856,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -2339,13 +2277,15 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.1+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -2355,9 +2295,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasip2", + "wasm-bindgen", ] [[package]] @@ -2417,9 +2359,9 @@ dependencies = [ [[package]] name = "glam" -version = "0.30.9" +version = "0.30.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd47b05dddf0005d850e5644cae7f2b14ac3df487979dbfff3b56f20b1a6ae46" +checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9" dependencies = [ "mint", ] @@ -2479,7 +2421,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -2492,7 +2434,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -2592,7 +2534,26 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", +] + +[[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]] @@ -2628,6 +2589,15 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "heck" version = "0.4.1" @@ -2734,6 +2704,7 @@ dependencies = [ "bytes", "futures-channel", "futures-core", + "h2", "http", "http-body", "httparse", @@ -2745,6 +2716,22 @@ 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", +] + [[package]] name = "hyper-util" version = "0.1.19" @@ -2764,9 +2751,11 @@ dependencies = [ "percent-encoding", "pin-project-lite", "socket2", + "system-configuration", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -2931,8 +2920,8 @@ dependencies = [ "rayon", "rgb", "tiff", - "zune-core 0.5.0", - "zune-jpeg 0.5.8", + "zune-core 0.5.1", + "zune-jpeg 0.5.9", ] [[package]] @@ -2964,9 +2953,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", "hashbrown 0.16.1", @@ -3010,7 +2999,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -3131,12 +3120,14 @@ dependencies = [ "intent-classifier", "kira", "log", + "mlua", "nnnoiseless", "once_cell", "parking_lot", "platform-dirs", "pv_recorder", "rand 0.8.5", + "reqwest 0.13.1", "rodio", "rustpotter", "seqdiff", @@ -3144,11 +3135,13 @@ dependencies = [ "serde_json", "serde_yaml", "sha2", + "tempfile", "tokio", "tokio-tungstenite", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.11+spec-1.1.0", "unic-langid", "vosk", + "winrt-notification", ] [[package]] @@ -3199,9 +3192,9 @@ dependencies = [ [[package]] name = "jiff" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a87d9b8105c23642f50cbbae03d1f75d8422c5cb98ce7ee9271f7ff7505be6b8" +checksum = "e67e8da4c49d6d9909fe03361f9b620f58898859f5c7aded68351e85e71ecf50" dependencies = [ "jiff-static", "log", @@ -3212,13 +3205,13 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b787bebb543f8969132630c51fd0afab173a86c6abae56ff3b9e5e3e3f9f6e58" +checksum = "e0c84ee7f197eca9a86c6fd6cb771e55eb991632f15f2bc3ca6ec838929e6e78" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -3255,9 +3248,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.83" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" dependencies = [ "once_cell", "wasm-bindgen", @@ -3321,7 +3314,7 @@ checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2" dependencies = [ "cssparser", "html5ever", - "indexmap 2.12.1", + "indexmap 2.13.0", "selectors", ] @@ -3363,9 +3356,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.179" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5a2d376baa530d1238d133232d15e239abad80d05838b4b59354e5268af431f" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "libfuzzer-sys" @@ -3513,6 +3506,31 @@ dependencies = [ "imgref", ] +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "lua-src" +version = "548.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdc4e1aff422ad5f08cffb4719603dcdbc2be2307f4c1510d7aab74b7fa88ca8" +dependencies = [ + "cc", +] + +[[package]] +name = "luajit-src" +version = "210.6.6+707c12b" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a86cc925d4053d0526ae7f5bc765dbd0d7a5d1a63d43974f4966cb349ca63295" +dependencies = [ + "cc", + "which", +] + [[package]] name = "mac" version = "0.1.1" @@ -3550,7 +3568,7 @@ checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -3633,6 +3651,39 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "mlua" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "935ac67539907efcd7198137eb7358e052555f77fe1b2916600a2249351f2b33" +dependencies = [ + "bstr", + "either", + "erased-serde", + "futures-util", + "libc", + "mlua-sys", + "num-traits", + "parking_lot", + "rustc-hash", + "rustversion", + "serde", + "serde-value", +] + +[[package]] +name = "mlua-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c968af21bf6b19fc9ca8e7b85ee16f86e4c9e3d0591de101a5608086bda0ad8" +dependencies = [ + "cc", + "cfg-if", + "lua-src", + "luajit-src", + "pkg-config", +] + [[package]] name = "mock_instant" version = "0.6.0" @@ -3707,19 +3758,6 @@ 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.10.0", - "cfg-if", - "cfg_aliases", - "libc", - "memoffset", -] - [[package]] name = "nnnoiseless" version = "0.5.2" @@ -3825,7 +3863,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -3898,7 +3936,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -4381,6 +4419,12 @@ dependencies = [ "pathdiff", ] +[[package]] +name = "openssl-probe" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f50d9b3dabb09ecd771ad0aa242ca6894994c130308ca3d7684634df8037391" + [[package]] name = "option-ext" version = "0.2.0" @@ -4406,16 +4450,6 @@ dependencies = [ "num-traits", ] -[[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_pipe" version = "1.2.3" @@ -4466,12 +4500,6 @@ dependencies = [ "system-deps 6.2.2", ] -[[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" @@ -4629,7 +4657,7 @@ dependencies = [ "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -4676,7 +4704,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -4713,7 +4741,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" dependencies = [ "base64 0.22.1", - "indexmap 2.12.1", + "indexmap 2.13.0", "quick-xml", "serde", "time", @@ -4874,9 +4902,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.104" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0" +checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" dependencies = [ "unicode-ident", ] @@ -4897,7 +4925,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" dependencies = [ "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -4969,10 +4997,66 @@ dependencies = [ ] [[package]] -name = "quote" -version = "1.0.42" +name = "quinn" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.17", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "aws-lc-rs", + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.17", + "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.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" dependencies = [ "proc-macro2", ] @@ -5015,7 +5099,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -5045,7 +5129,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -5063,14 +5147,14 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", ] [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ "getrandom 0.3.4", ] @@ -5245,7 +5329,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "libredox", "thiserror 1.0.69", ] @@ -5256,7 +5340,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "libredox", "thiserror 2.0.17", ] @@ -5278,7 +5362,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -5346,12 +5430,53 @@ dependencies = [ ] [[package]] -name = "rfd" -version = "0.15.4" +name = "reqwest" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2bee61e6cffa4635c72d7d81a84294e28f0930db0ddcb0f66d10244674ebed" +checksum = "04e9018c9d814e5f30cc16a0f03271aeab3571e609612d9fe78c1aa8d11c2f62" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "rfd" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15ad77d9e70a92437d8f74c35d99b4e4691128df018833e99f90bcd36152672" dependencies = [ - "ashpd", "block2 0.6.2", "dispatch2", "glib-sys 0.18.1", @@ -5367,7 +5492,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -5376,6 +5501,20 @@ version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" +[[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 = "rodio" version = "0.21.1" @@ -5461,6 +5600,81 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "rustls" +version = "0.23.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +dependencies = [ + "aws-lc-rs", + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[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-platform-verifier" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.103.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustpotter" version = "3.0.2" @@ -5506,6 +5720,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "schemars" version = "0.8.22" @@ -5554,7 +5777,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -5582,6 +5805,29 @@ dependencies = [ "tiny-skia", ] +[[package]] +name = "security-framework" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +dependencies = [ + "bitflags 2.10.0", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "selectors" version = "0.24.0" @@ -5683,7 +5929,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -5694,14 +5940,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] name = "serde_json" -version = "1.0.148" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3084b546a1dd6289475996f182a22aba973866ea8e8b02c51d9f46b1336a22da" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", @@ -5718,7 +5964,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -5761,7 +6007,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.12.1", + "indexmap 2.13.0", "schemars 0.9.0", "schemars 1.2.0", "serde_core", @@ -5779,7 +6025,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -5788,7 +6034,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.12.1", + "indexmap 2.13.0", "itoa", "ryu", "serde", @@ -5814,7 +6060,7 @@ checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -6047,12 +6293,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strength_reduce" version = "0.2.4" @@ -6102,6 +6342,33 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7ac893c7d471c8a21f31cfe213ec4f6d9afeed25537c772e08ef3f005f8729e" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339f799d8b549e3744c7ac7feb216383e4005d94bdb22561b3ab8f3b808ae9fb" +dependencies = [ + "heck 0.3.3", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[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" @@ -6273,9 +6540,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.113" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678faa00651c9eb72dd2020cbdf275d92eccb2400d568e419efdd64838145cb4" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -6299,7 +6566,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -6344,6 +6611,27 @@ dependencies = [ "windows 0.61.3", ] +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.10.0", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "system-deps" version = "6.2.2" @@ -6366,7 +6654,7 @@ dependencies = [ "cfg-expr 0.20.5", "heck 0.5.0", "pkg-config", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.11+spec-1.1.0", "version-compare", ] @@ -6432,7 +6720,7 @@ checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -6477,7 +6765,7 @@ dependencies = [ "percent-encoding", "plist", "raw-window-handle", - "reqwest", + "reqwest 0.12.28", "serde", "serde_json", "serde_repr", @@ -6516,7 +6804,7 @@ dependencies = [ "serde_json", "tauri-utils", "tauri-winres", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.11+spec-1.1.0", "walkdir", ] @@ -6538,7 +6826,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "syn 2.0.113", + "syn 2.0.114", "tauri-utils", "thiserror 2.0.17", "time", @@ -6556,7 +6844,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", "tauri-codegen", "tauri-utils", ] @@ -6574,15 +6862,15 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.11+spec-1.1.0", "walkdir", ] [[package]] name = "tauri-plugin-dialog" -version = "2.4.2" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "313f8138692ddc4a2127c4c9607d616a46f5c042e77b3722450866da0aad2f19" +checksum = "9204b425d9be8d12aa60c2a83a289cf7d1caae40f57f336ed1155b3a5c0e359b" dependencies = [ "log", "raw-window-handle", @@ -6598,9 +6886,9 @@ dependencies = [ [[package]] name = "tauri-plugin-fs" -version = "2.4.4" +version = "2.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47df422695255ecbe7bac7012440eddaeefd026656171eac9559f5243d3230d9" +checksum = "ed390cc669f937afeb8b28032ce837bac8ea023d975a2e207375ec05afaf1804" dependencies = [ "anyhow", "dunce", @@ -6614,15 +6902,15 @@ dependencies = [ "tauri-plugin", "tauri-utils", "thiserror 2.0.17", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.11+spec-1.1.0", "url", ] [[package]] name = "tauri-plugin-shell" -version = "2.3.3" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c374b6db45f2a8a304f0273a15080d98c70cde86178855fc24653ba657a1144c" +checksum = "39b76f884a3937e04b631ffdc3be506088fa979369d25147361352f2f352e5ed" dependencies = [ "encoding_rs", "log", @@ -6722,7 +7010,7 @@ dependencies = [ "serde_with", "swift-rs", "thiserror 2.0.17", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.11+spec-1.1.0", "url", "urlpattern", "uuid", @@ -6737,7 +7025,7 @@ checksum = "1087b111fe2b005e42dbdc1990fc18593234238d47453b0c99b7de1c9ab2c1e0" dependencies = [ "dunce", "embed-resource", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.11+spec-1.1.0", ] [[package]] @@ -6805,7 +7093,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -6816,17 +7104,17 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] name = "thread-id" -version = "5.0.0" +version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99043e46c5a15af379c06add30d9c93a6c0e8849de00d244c4a2c417da128d80" +checksum = "2010d27add3f3240c1fef7959f46c814487b216baee662af53be645ba7831c07" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -6845,30 +7133,30 @@ dependencies = [ [[package]] name = "time" -version = "0.3.44" +version = "0.3.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd" dependencies = [ "deranged", "itoa", "num-conv", "powerfmt", - "serde", + "serde_core", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" +checksum = "8b36ee98fd31ec7426d599183e8fe26932a8dc1fb76ddb6214d05493377d34ca" [[package]] name = "time-macros" -version = "0.2.24" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +checksum = "71e552d1249bf61ac2a52db88179fd0673def1e1ad8243a00d9ec9ed71fee3dd" dependencies = [ "num-conv", "time-core", @@ -6910,6 +7198,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +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.49.0" @@ -6924,7 +7227,6 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "tracing", "windows-sys 0.61.2", ] @@ -6936,7 +7238,17 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", ] [[package]] @@ -6953,9 +7265,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.17" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", @@ -6978,11 +7290,11 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.10+spec-1.1.0" +version = "0.9.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0825052159284a1a8b4d6c0c86cbc801f2da5afd2b225fa548c72f2e74002f48" +checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" dependencies = [ - "indexmap 2.12.1", + "indexmap 2.13.0", "serde_core", "serde_spanned 1.0.4", "toml_datetime 0.7.5+spec-1.1.0", @@ -7015,7 +7327,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.12.1", + "indexmap 2.13.0", "toml_datetime 0.6.3", "winnow 0.5.40", ] @@ -7026,7 +7338,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ - "indexmap 2.12.1", + "indexmap 2.13.0", "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.3", @@ -7039,7 +7351,7 @@ version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ - "indexmap 2.12.1", + "indexmap 2.13.0", "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "winnow 0.7.14", @@ -7062,9 +7374,9 @@ checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" [[package]] name = "tower" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", @@ -7124,7 +7436,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -7227,17 +7539,6 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" -[[package]] -name = "uds_windows" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" -dependencies = [ - "memoffset", - "tempfile", - "winapi", -] - [[package]] name = "ug" version = "0.4.0" @@ -7343,15 +7644,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] -name = "url" -version = "2.5.7" +name = "untrusted" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", "percent-encoding", "serde", + "serde_derive", ] [[package]] @@ -7490,18 +7798,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.1+wasi-0.2.4" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" dependencies = [ "cfg-if", "once_cell", @@ -7512,11 +7820,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.56" +version = "0.4.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" +checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" dependencies = [ "cfg-if", + "futures-util", "js-sys", "once_cell", "wasm-bindgen", @@ -7525,9 +7834,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -7535,22 +7844,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" dependencies = [ "unicode-ident", ] @@ -7679,9 +7988,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.83" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" dependencies = [ "js-sys", "wasm-bindgen", @@ -7742,10 +8051,19 @@ dependencies = [ ] [[package]] -name = "webview2-com" -version = "0.38.0" +name = "webpki-root-certs" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4ba622a989277ef3886dd5afb3e280e3dd6d974b766118950a08f8f678ad6a4" +checksum = "36a29fc0408b113f68cf32637857ab740edfafdf460c326cd2afaa2d84cc05dc" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webview2-com" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a" dependencies = [ "webview2-com-macros", "webview2-com-sys", @@ -7757,20 +8075,20 @@ dependencies = [ [[package]] name = "webview2-com-macros" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" +checksum = "67a921c1b6914c367b2b823cd4cde6f96beec77d30a939c8199bb377cf9b9b54" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] name = "webview2-com-sys" -version = "0.38.0" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36695906a1b53a3bf5c4289621efedac12b73eeb0b89e7e1a89b517302d5d75c" +checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" dependencies = [ "thiserror 2.0.17", "windows 0.61.3", @@ -7783,6 +8101,17 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" +[[package]] +name = "which" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fabb953106c3c8eea8306e4393700d7657561cb43122571b172bbfb7c7ba1d" +dependencies = [ + "env_home", + "rustix 1.1.3", + "winsafe", +] + [[package]] name = "winapi" version = "0.3.9" @@ -7829,6 +8158,18 @@ dependencies = [ "windows-version", ] +[[package]] +name = "windows" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9f39345ae0c8ab072c0ac7fe8a8b411636aa34f89be19ddd0d9226544f13944" +dependencies = [ + "windows_i686_gnu 0.24.0", + "windows_i686_msvc 0.24.0", + "windows_x86_64_gnu 0.24.0", + "windows_x86_64_msvc 0.24.0", +] + [[package]] name = "windows" version = "0.54.0" @@ -7916,7 +8257,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -7927,7 +8268,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -7952,6 +8293,17 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + [[package]] name = "windows-result" version = "0.1.2" @@ -8144,6 +8496,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" +[[package]] +name = "windows_i686_gnu" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0866510a3eca9aed73a077490bbbf03e5eaac4e1fd70849d89539e5830501fd" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -8174,6 +8532,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" +[[package]] +name = "windows_i686_msvc" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf0ffed56b7e9369a29078d2ab3aaeceea48eb58999d2cff3aa2494a275b95c6" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -8192,6 +8556,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" +[[package]] +name = "windows_x86_64_gnu" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384a173630588044205a2993b6864a2f56e5a8c1e7668c07b93ec18cf4888dc4" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -8228,6 +8598,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" +[[package]] +name = "windows_x86_64_msvc" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd8f062d8ca5446358159d79a90be12c543b3a965c847c8f3eedf14b321d399" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -8327,10 +8703,27 @@ dependencies = [ ] [[package]] -name = "wit-bindgen" -version = "0.46.0" +name = "winrt-notification" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "007a0353840b23e0c6dc73e5b962ff58ed7f6bc9ceff3ce7fe6fbad8d496edf4" +dependencies = [ + "strum", + "windows 0.24.0", + "xml-rs", +] + +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" [[package]] name = "writeable" @@ -8450,6 +8843,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" +[[package]] +name = "xml-rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" + [[package]] name = "y4m" version = "0.8.0" @@ -8487,7 +8886,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", "synstructure", ] @@ -8499,84 +8898,28 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", "synstructure", ] -[[package]] -name = "zbus" -version = "5.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b622b18155f7a93d1cd2dc8c01d2d6a44e08fb9ebb7b3f9e6ed101488bad6c91" -dependencies = [ - "async-broadcast", - "async-recursion", - "async-trait", - "enumflags2", - "event-listener", - "futures-core", - "futures-lite", - "hex", - "nix", - "ordered-stream", - "serde", - "serde_repr", - "tokio", - "tracing", - "uds_windows", - "uuid", - "windows-sys 0.61.2", - "winnow 0.7.14", - "zbus_macros", - "zbus_names", - "zvariant", -] - -[[package]] -name = "zbus_macros" -version = "5.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cdb94821ca8a87ca9c298b5d1cbd80e2a8b67115d99f6e4551ac49e42b6a314" -dependencies = [ - "proc-macro-crate 3.4.0", - "proc-macro2", - "quote", - "syn 2.0.113", - "zbus_names", - "zvariant", - "zvariant_utils", -] - -[[package]] -name = "zbus_names" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" -dependencies = [ - "serde", - "static_assertions", - "winnow 0.7.14", - "zvariant", -] - [[package]] name = "zerocopy" -version = "0.8.31" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.31" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -8596,10 +8939,16 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", "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" @@ -8631,7 +8980,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.113", + "syn 2.0.114", ] [[package]] @@ -8644,16 +8993,16 @@ dependencies = [ "crc32fast", "crossbeam-utils", "displaydoc", - "indexmap 2.12.1", + "indexmap 2.13.0", "num_enum", "thiserror 1.0.69", ] [[package]] name = "zmij" -version = "1.0.9" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee2a72b10d087f75fb2e1c2c7343e308fe6970527c22a41caf8372e165ff5c1" +checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea" [[package]] name = "zune-core" @@ -8663,9 +9012,9 @@ checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" [[package]] name = "zune-core" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "111f7d9820f05fd715df3144e254d6fc02ee4088b0644c0ffd0efc9e6d9d2773" +checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" [[package]] name = "zune-inflate" @@ -8687,50 +9036,9 @@ dependencies = [ [[package]] name = "zune-jpeg" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35aee689668bf9bd6f6f3a6c60bb29ba1244b3b43adfd50edd554a371da37d5" +checksum = "87c86acb70a85b2c16f071f171847d1945e8f44812630463cd14ec83900ad01c" dependencies = [ - "zune-core 0.5.0", -] - -[[package]] -name = "zvariant" -version = "5.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2be61892e4f2b1772727be11630a62664a1826b62efa43a6fe7449521cb8744c" -dependencies = [ - "endi", - "enumflags2", - "serde", - "url", - "winnow 0.7.14", - "zvariant_derive", - "zvariant_utils", -] - -[[package]] -name = "zvariant_derive" -version = "5.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da58575a1b2b20766513b1ec59d8e2e68db2745379f961f86650655e862d2006" -dependencies = [ - "proc-macro-crate 3.4.0", - "proc-macro2", - "quote", - "syn 2.0.113", - "zvariant_utils", -] - -[[package]] -name = "zvariant_utils" -version = "3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6949d142f89f6916deca2232cf26a8afacf2b9fdc35ce766105e104478be599" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "syn 2.0.113", - "winnow 0.7.14", + "zune-core 0.5.1", ] diff --git a/Cargo.toml b/Cargo.toml index eaa710e..099ada9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,4 +40,8 @@ futures-util = "0.3" fluent = "0.17.0" fluent-bundle = "0.16.0" unic-langid = "0.9" -chrono = "0.4" \ No newline at end of file +chrono = "0.4" +mlua = { version = "0.11.5", features = ["lua54", "vendored", "async", "serde"] } +reqwest = { version = "0.13.1", features = ["blocking", "json"] } +tempfile = "^3.24" +winrt-notification = "0.5" \ No newline at end of file diff --git a/crates/jarvis-app/src/app.rs b/crates/jarvis-app/src/app.rs index 4d17473..8cc0ed7 100644 --- a/crates/jarvis-app/src/app.rs +++ b/crates/jarvis-app/src/app.rs @@ -306,7 +306,7 @@ fn execute_command(text: &str, rt: &tokio::runtime::Runtime) -> bool { if let Some((cmd_path, cmd_config)) = cmd_result { info!("Command found: {:?}", cmd_path); - match commands::execute_command(&cmd_path, &cmd_config) { + match commands::execute_command(&cmd_path, &cmd_config, Some(&text)) { Ok(chain) => { info!("Command executed successfully"); // voices::play_ok(); diff --git a/crates/jarvis-core/Cargo.toml b/crates/jarvis-core/Cargo.toml index ac90755..9ffe142 100644 --- a/crates/jarvis-core/Cargo.toml +++ b/crates/jarvis-core/Cargo.toml @@ -38,7 +38,16 @@ intent-classifier = { version = "0.1.0", optional = true } tokio = { version = "1", features = ["sync"], optional = true } +mlua = { workspace = true, optional = true } +reqwest = { workspace = true, optional = true } +tempfile.workspace = true + +[target.'cfg(windows)'.dependencies] +winrt-notification = { workspace = true, optional = true } + [features] default = ["jarvis_app"] -jarvis_app = ["vosk", "intent-classifier", "tokio", "nnnoiseless", "tokio-tungstenite", "futures-util"] -intent = ["intent-classifier", "tokio"] \ No newline at end of file +jarvis_app = ["vosk", "intent-classifier", "tokio", "nnnoiseless", "tokio-tungstenite", "futures-util", "lua"] +intent = ["intent-classifier", "tokio"] +lua = ["mlua", "reqwest", "winrt-notification"] +lua_only = ["lua", "tokio"] \ No newline at end of file diff --git a/crates/jarvis-core/src/commands.rs b/crates/jarvis-core/src/commands.rs index e5e8b09..eced6f5 100644 --- a/crates/jarvis-core/src/commands.rs +++ b/crates/jarvis-core/src/commands.rs @@ -10,6 +10,8 @@ pub use structs::*; use crate::{config, i18n, APP_DIR}; +#[cfg(feature = "lua")] +use crate::lua::{self, SandboxLevel, CommandContext}; pub fn parse_commands() -> Result, String> { let mut commands: Vec = Vec::new(); @@ -189,10 +191,20 @@ pub fn execute_cli(cmd: &str, args: &[String]) -> std::io::Result { } } -pub fn execute_command(cmd_path: &Path, cmd_config: &JCommand) -> Result { - match cmd_config.action.as_str() { +pub fn execute_command(cmd_path: &PathBuf, cmd_config: &JCommand, phrase: Option<&str>) -> Result { + match cmd_config.cmd_type.as_str() { + + // BRUH "voice" => Ok(true), + // LUA command + #[cfg(feature = "lua")] + "lua" => { + execute_lua_command(cmd_path, cmd_config, phrase) + } + + // AutoHotkey command + // @TODO: Consider adding ahk source files execution? "ahk" => { let exe_path_absolute = Path::new(&cmd_config.exe_path); let exe_path_local = cmd_path.join(&cmd_config.exe_path); @@ -208,23 +220,80 @@ pub fn execute_command(cmd_path: &Path, cmd_config: &JCommand) -> Result { execute_cli(&cmd_config.cli_cmd, &cmd_config.cli_args) .map(|_| true) .map_err(|e| format!("CLI command error: {}", e)) } + // TERMINATOR command (T1000) "terminate" => { std::thread::sleep(Duration::from_secs(2)); std::process::exit(0); } + // STOP CHANING "stop_chaining" => Ok(false), - - _ => Err(format!("Unknown command type: {}", cmd_config.action)), + + // other + _ => { + error!("Command type unknown: {}", cmd_config.cmd_type); + Err(format!("Command type unknown: {}", cmd_config.cmd_type).into()) + } } } pub fn list_paths(commands: &[JCommandsList]) -> Vec<&Path> { commands.iter().map(|x| x.path.as_path()).collect() } + +#[cfg(feature = "lua")] +fn execute_lua_command( + cmd_path: &PathBuf, + cmd_config: &JCommand, + phrase: Option<&str>, +) -> Result { + // get script path + let script_name = if cmd_config.script.is_empty() { + "script.lua" + } else { + &cmd_config.script + }; + + let script_path = cmd_path.join(script_name); + + if !script_path.exists() { + return Err(format!("Lua script not found: {}", script_path.display())); + } + + // parse sandbox level + let sandbox = SandboxLevel::from_str(&cmd_config.sandbox); + + // create context + let context = CommandContext { + phrase: phrase.unwrap_or("").to_string(), + command_id: cmd_config.id.clone(), + command_path: cmd_path.clone(), + language: i18n::get_language(), + }; + + // get timeout + let timeout = Duration::from_millis(cmd_config.timeout); + + info!("Executing Lua command: {} (sandbox: {:?}, timeout: {:?})", + cmd_config.id, sandbox, timeout); + + // execute + match lua::execute(&script_path, context, sandbox, timeout) { + Ok(result) => { + info!("Lua command {} completed (chain: {})", cmd_config.id, result.chain); + Ok(result.chain) + } + Err(e) => { + error!("Lua command {} failed: {}", cmd_config.id, e); + Err(e.to_string()) + } + } +} \ No newline at end of file diff --git a/crates/jarvis-core/src/commands/structs.rs b/crates/jarvis-core/src/commands/structs.rs index c8a5226..a7f2736 100644 --- a/crates/jarvis-core/src/commands/structs.rs +++ b/crates/jarvis-core/src/commands/structs.rs @@ -15,26 +15,42 @@ pub struct JCommandsList { #[derive(Serialize, Deserialize, Debug)] pub struct JCommand { pub id: String, - pub action: String, + + // Available command types are: "lua", "ahk", "cli", "voice", "terminate", "stop_chaining" + #[serde(rename = "type")] + pub cmd_type: String, #[serde(default)] pub description: String, + // for "ahk" type #[serde(default)] pub exe_path: String, - #[serde(default)] pub exe_args: Vec, + // for "cli" type #[serde(default)] pub cli_cmd: String, - #[serde(default)] pub cli_args: Vec, // #[serde(default)] // pub sounds: Vec, + // for "lua" type + #[serde(default)] + pub script: String, + + // Lua sandbox level: "minimal", "standard", "full" + // basically this is an access level + #[serde(default)] + pub sandbox: String, + + // Script timeout in milliseconds (default 10000 = 10s) + #[serde(default)] + pub timeout: u64, + // Multi-language sounds #[serde(default)] pub sounds: HashMap>, @@ -57,12 +73,20 @@ impl Clone for JCommand { fn clone(&self) -> Self { Self { id: self.id.clone(), - action: self.action.clone(), + + cmd_type: self.cmd_type.clone(), description: self.description.clone(), + exe_path: self.exe_path.clone(), exe_args: self.exe_args.clone(), + cli_cmd: self.cli_cmd.clone(), cli_args: self.cli_args.clone(), + + script: self.script.clone(), + sandbox: self.sandbox.clone(), + timeout: self.timeout.clone(), + sounds: self.sounds.clone(), phrases: self.phrases.clone(), diff --git a/crates/jarvis-core/src/config.rs b/crates/jarvis-core/src/config.rs index 3cf699c..c29df90 100644 --- a/crates/jarvis-core/src/config.rs +++ b/crates/jarvis-core/src/config.rs @@ -175,6 +175,9 @@ pub const GAIN_MAX: f32 = 3.0; // maximum gain multiplier // nnnoiseless frame size (fixed by library) pub const NNNOISELESS_FRAME_SIZE: usize = 480; +// LUA +pub const DEFAULT_LUA_SANDBOX: &str = "standard"; +pub const DEFAULT_LUA_TIMEOUT: u64 = 10000; // ms // ETC pub const CMD_RATIO_THRESHOLD: f64 = 65f64; diff --git a/crates/jarvis-core/src/lib.rs b/crates/jarvis-core/src/lib.rs index 43f5381..37cd78e 100644 --- a/crates/jarvis-core/src/lib.rs +++ b/crates/jarvis-core/src/lib.rs @@ -38,6 +38,9 @@ pub mod voices; pub mod audio_buffer; +#[cfg(feature = "lua")] +pub mod lua; + // shared statics // pub static APP_DIR: Lazy = Lazy::new(|| std::env::current_dir().unwrap()); pub static APP_DIR: Lazy = Lazy::new(|| { diff --git a/crates/jarvis-core/src/lua.rs b/crates/jarvis-core/src/lua.rs new file mode 100644 index 0000000..9653867 --- /dev/null +++ b/crates/jarvis-core/src/lua.rs @@ -0,0 +1,28 @@ +mod engine; +mod sandbox; +mod error; +mod api; + +mod structs; +pub use structs::*; + +pub use engine::LuaEngine; +pub use sandbox::SandboxLevel; +pub use error::LuaError; + +use std::path::PathBuf; +use std::time::Duration; + +#[cfg(test)] +mod tests; + +// Execute a Lua command script +pub fn execute( + script_path: &PathBuf, + context: CommandContext, + sandbox: SandboxLevel, + timeout: Duration, +) -> Result { + let engine = LuaEngine::new(sandbox)?; + engine.execute(script_path, context, timeout) +} \ No newline at end of file diff --git a/crates/jarvis-core/src/lua/api.rs b/crates/jarvis-core/src/lua/api.rs new file mode 100644 index 0000000..8bda5a4 --- /dev/null +++ b/crates/jarvis-core/src/lua/api.rs @@ -0,0 +1,7 @@ +pub mod core; +pub mod audio; +pub mod context; +pub mod http; +pub mod fs; +pub mod state; +pub mod system; \ No newline at end of file diff --git a/crates/jarvis-core/src/lua/api/_new_api_module_example.rs b/crates/jarvis-core/src/lua/api/_new_api_module_example.rs new file mode 100644 index 0000000..7c2112b --- /dev/null +++ b/crates/jarvis-core/src/lua/api/_new_api_module_example.rs @@ -0,0 +1,37 @@ +// QUICK HELP ON HOW TO ADD NEW LUA API MODULE +// +// # 1. DEFINE NEW API MODULE FILE + +use mlua::{Lua, Table}; +use crate::lua::error::LuaError; + +pub fn register(lua: &Lua, jarvis: &Table) -> mlua::Result<()> { + let mymodule = lua.create_table()?; + + // add functions + let my_fn = lua.create_function(|_, arg: String| { + // implementation + Ok(format!("Result: {}", arg)) + })?; + mymodule.set("my_function", my_fn)?; + + jarvis.set("mymodule", mymodule)?; + + Ok(()) +} + +// # 2. ADD NEW API MODULE TO mod.rs + +pub mod mymodule; + + +// # 3. REGISTER NEW MODULE IN engine.rs +// in register_api() +api::mymodule::register(&self.lua, &jarvis)?; + +// # 4. YOU CAN ALSO DEFINE ASYNC FUNCTIONS INSTEAD +let async_fn = lua.create_async_function(|_, url: String| async move { + let response = reqwest::get(&url).await + .map_err(|e| mlua::Error::runtime(e.to_string()))?; + Ok(response.text().await.unwrap_or_default()) +})?; \ No newline at end of file diff --git a/crates/jarvis-core/src/lua/api/audio.rs b/crates/jarvis-core/src/lua/api/audio.rs new file mode 100644 index 0000000..706f4df --- /dev/null +++ b/crates/jarvis-core/src/lua/api/audio.rs @@ -0,0 +1,77 @@ +// Audio Lua API: something sound related, apparently :3 + +use mlua::{Lua, Table}; +use crate::voices::{self, Reaction}; + +pub fn register(lua: &Lua, jarvis: &Table) -> mlua::Result<()> { + let audio = lua.create_table()?; + + // jarvis.audio.play(reaction) + // reactions: "ok", "reply", "greet", "not_found", "error", "goodbye", "thanks" + let play_fn = lua.create_function(|_, reaction: String| { + let reaction_enum = match reaction.to_lowercase().as_str() { + "ok" => Reaction::Ok, + "reply" => Reaction::Reply, + "greet" => Reaction::Greet, + "not_found" => Reaction::NotFound, + "error" => Reaction::Error, + "goodbye" => Reaction::Goodbye, + "thanks" => Reaction::Thanks, + // "joke" => Reaction::Joke, NO PUN INTENDED :3 + _ => { + log::warn!("[Lua] Unknown reaction: {}", reaction); + return Ok(false); + } + }; + + voices::play(reaction_enum); + Ok(true) + })?; + audio.set("play", play_fn)?; + + // jarvis.audio.play_ok() + let play_ok_fn = lua.create_function(|_, ()| { + voices::play_ok(); + Ok(()) + })?; + audio.set("play_ok", play_ok_fn)?; + + // jarvis.audio.play_reply() + let play_reply_fn = lua.create_function(|_, ()| { + voices::play_reply(); + Ok(()) + })?; + audio.set("play_reply", play_reply_fn)?; + + // jarvis.audio.play_error() + let play_error_fn = lua.create_function(|_, ()| { + voices::play_error(); + Ok(()) + })?; + audio.set("play_error", play_error_fn)?; + + // jarvis.audio.play_not_found() + let play_not_found_fn = lua.create_function(|_, ()| { + voices::play_not_found(); + Ok(()) + })?; + audio.set("play_not_found", play_not_found_fn)?; + + // jarvis.audio.play_greet() + let play_greet_fn = lua.create_function(|_, ()| { + voices::play_greet(); + Ok(()) + })?; + audio.set("play_greet", play_greet_fn)?; + + // jarvis.audio.play_goodbye() + let play_goodbye_fn = lua.create_function(|_, ()| { + voices::play_goodbye(); + Ok(()) + })?; + audio.set("play_goodbye", play_goodbye_fn)?; + + jarvis.set("audio", audio)?; + + Ok(()) +} \ No newline at end of file diff --git a/crates/jarvis-core/src/lua/api/context.rs b/crates/jarvis-core/src/lua/api/context.rs new file mode 100644 index 0000000..c74bb45 --- /dev/null +++ b/crates/jarvis-core/src/lua/api/context.rs @@ -0,0 +1,31 @@ +// Context Lua API: read-only command context + +use mlua::{Lua, Table}; +use crate::lua::{CommandContext}; + +pub fn register(lua: &Lua, jarvis: &Table, ctx: &CommandContext) -> mlua::Result<()> { + let context = lua.create_table()?; + + // read-only context values + context.set("phrase", ctx.phrase.clone())?; + context.set("command_id", ctx.command_id.clone())?; + context.set("command_path", ctx.command_path.to_string_lossy().to_string())?; + context.set("language", ctx.language.clone())?; + + // time info + let time = lua.create_table()?; + let now = chrono::Local::now(); + time.set("year", now.format("%Y").to_string())?; + time.set("month", now.format("%m").to_string())?; + time.set("day", now.format("%d").to_string())?; + time.set("hour", now.format("%H").to_string())?; + time.set("minute", now.format("%M").to_string())?; + time.set("second", now.format("%S").to_string())?; + time.set("weekday", now.format("%A").to_string())?; + time.set("timestamp", now.timestamp())?; + context.set("time", time)?; + + jarvis.set("context", context)?; + + Ok(()) +} \ No newline at end of file diff --git a/crates/jarvis-core/src/lua/api/core.rs b/crates/jarvis-core/src/lua/api/core.rs new file mode 100644 index 0000000..fa05bae --- /dev/null +++ b/crates/jarvis-core/src/lua/api/core.rs @@ -0,0 +1,50 @@ +// Core Lua API: log, sleep, print, etc. + +use mlua::{Lua, Table, MultiValue}; + +pub fn register(lua: &Lua, jarvis: &Table) -> mlua::Result<()> { + + // @ jarvis.log(level, message) + // log something + let log_fn = lua.create_function(|_, (level, message): (String, String)| { + match level.to_lowercase().as_str() { + "debug" => log::debug!("[Lua] {}", message), + "info" => log::info!("[Lua] {}", message), + "warn" => log::warn!("[Lua] {}", message), + "error" => log::error!("[Lua] {}", message), + _ => log::info!("[Lua] {}", message), + } + Ok(()) + })?; + jarvis.set("log", log_fn)?; + + // @ jarvis.print(...) + // simple print + let print_fn = lua.create_function(|_, args: MultiValue| { + let parts: Vec = args.iter() + .map(|v| format!("{:?}", v)) + .collect(); + log::info!("[Lua] {}", parts.join(" ")); + Ok(()) + })?; + jarvis.set("print", print_fn)?; + + // @ jarvis.sleep(ms) + // ..zZZ + let sleep_fn = lua.create_function(|_, ms: u64| { + std::thread::sleep(std::time::Duration::from_millis(ms)); + Ok(()) + })?; + jarvis.set("sleep", sleep_fn)?; + + // @ jarvis.speak(text) + // @TODO: update when TTS will be implemented + let speak_fn = lua.create_function(|_, text: String| { + log::info!("[Lua] SPEAK: {}", text); + // pass + Ok(()) + })?; + jarvis.set("speak", speak_fn)?; + + Ok(()) +} \ No newline at end of file diff --git a/crates/jarvis-core/src/lua/api/fs.rs b/crates/jarvis-core/src/lua/api/fs.rs new file mode 100644 index 0000000..23e002e --- /dev/null +++ b/crates/jarvis-core/src/lua/api/fs.rs @@ -0,0 +1,212 @@ +// File System Lua API: read, write, list (sandboxed) + +use mlua::{Lua, Table}; +use std::path::{Path, PathBuf}; +use std::fs; + +use crate::lua::sandbox::SandboxLevel; + +pub fn register( + lua: &Lua, + jarvis: &Table, + command_path: &PathBuf, + sandbox: SandboxLevel, +) -> mlua::Result<()> { + let fs_table = lua.create_table()?; + + let cmd_path = command_path.clone(); + let sandbox_level = sandbox; + + // jarvis.fs.read(path) + let cmd_path_read = cmd_path.clone(); + let read_fn = lua.create_function(move |_, path: String| { + let full_path = resolve_path(&cmd_path_read, &path, sandbox_level)?; + + fs::read_to_string(&full_path) + .map_err(|e| mlua::Error::runtime(format!("Read error: {}", e))) + })?; + fs_table.set("read", read_fn)?; + + // jarvis.fs.read_bytes(path) + let cmd_path_read_bytes = cmd_path.clone(); + let read_bytes_fn = lua.create_function(move |lua, path: String| { + let full_path = resolve_path(&cmd_path_read_bytes, &path, sandbox_level)?; + + let bytes = fs::read(&full_path) + .map_err(|e| mlua::Error::runtime(format!("Read error: {}", e)))?; + + Ok(lua.create_string(&bytes)?) + })?; + fs_table.set("read_bytes", read_bytes_fn)?; + + // jarvis.fs.write(path, content) + let cmd_path_write = cmd_path.clone(); + let write_fn = lua.create_function(move |_, (path, content): (String, String)| { + if !sandbox_level.allows_fs_write() { + return Err(mlua::Error::runtime("Write not allowed in this sandbox")); + } + + let full_path = resolve_path(&cmd_path_write, &path, sandbox_level)?; + + // ensure parent directory exists + if let Some(parent) = full_path.parent() { + fs::create_dir_all(parent) + .map_err(|e| mlua::Error::runtime(format!("Create dir error: {}", e)))?; + } + + fs::write(&full_path, content) + .map_err(|e| mlua::Error::runtime(format!("Write error: {}", e)))?; + + Ok(true) + })?; + fs_table.set("write", write_fn)?; + + // jarvis.fs.append(path, content) + let cmd_path_append = cmd_path.clone(); + let append_fn = lua.create_function(move |_, (path, content): (String, String)| { + if !sandbox_level.allows_fs_write() { + return Err(mlua::Error::runtime("Write not allowed in this sandbox")); + } + + let full_path = resolve_path(&cmd_path_append, &path, sandbox_level)?; + + use std::io::Write; + let mut file = fs::OpenOptions::new() + .create(true) + .append(true) + .open(&full_path) + .map_err(|e| mlua::Error::runtime(format!("Open error: {}", e)))?; + + file.write_all(content.as_bytes()) + .map_err(|e| mlua::Error::runtime(format!("Write error: {}", e)))?; + + Ok(true) + })?; + fs_table.set("append", append_fn)?; + + // jarvis.fs.exists(path) + let cmd_path_exists = cmd_path.clone(); + let exists_fn = lua.create_function(move |_, path: String| { + let full_path = resolve_path(&cmd_path_exists, &path, sandbox_level)?; + Ok(full_path.exists()) + })?; + fs_table.set("exists", exists_fn)?; + + // jarvis.fs.is_file(path) + let cmd_path_is_file = cmd_path.clone(); + let is_file_fn = lua.create_function(move |_, path: String| { + let full_path = resolve_path(&cmd_path_is_file, &path, sandbox_level)?; + Ok(full_path.is_file()) + })?; + fs_table.set("is_file", is_file_fn)?; + + // jarvis.fs.is_dir(path) + let cmd_path_is_dir = cmd_path.clone(); + let is_dir_fn = lua.create_function(move |_, path: String| { + let full_path = resolve_path(&cmd_path_is_dir, &path, sandbox_level)?; + Ok(full_path.is_dir()) + })?; + fs_table.set("is_dir", is_dir_fn)?; + + // jarvis.fs.list(path) + let cmd_path_list = cmd_path.clone(); + let list_fn = lua.create_function(move |lua, path: Option| { + let full_path = if let Some(p) = path { + resolve_path(&cmd_path_list, &p, sandbox_level)? + } else { + cmd_path_list.clone() + }; + + let result = lua.create_table()?; + + let entries = fs::read_dir(&full_path) + .map_err(|e| mlua::Error::runtime(format!("List error: {}", e)))?; + + let mut idx = 1; + for entry in entries { + if let Ok(entry) = entry { + let item = lua.create_table()?; + item.set("name", entry.file_name().to_string_lossy().to_string())?; + item.set("path", entry.path().to_string_lossy().to_string())?; + item.set("is_file", entry.path().is_file())?; + item.set("is_dir", entry.path().is_dir())?; + + result.set(idx, item)?; + idx += 1; + } + } + + Ok(result) + })?; + fs_table.set("list", list_fn)?; + + // jarvis.fs.mkdir(path) + let cmd_path_mkdir = cmd_path.clone(); + let mkdir_fn = lua.create_function(move |_, path: String| { + if !sandbox_level.allows_fs_write() { + return Err(mlua::Error::runtime("Write not allowed in this sandbox")); + } + + let full_path = resolve_path(&cmd_path_mkdir, &path, sandbox_level)?; + + fs::create_dir_all(&full_path) + .map_err(|e| mlua::Error::runtime(format!("Mkdir error: {}", e)))?; + + Ok(true) + })?; + fs_table.set("mkdir", mkdir_fn)?; + + // jarvis.fs.remove(path) + let cmd_path_remove = cmd_path.clone(); + let remove_fn = lua.create_function(move |_, path: String| { + if !sandbox_level.allows_fs_write() { + return Err(mlua::Error::runtime("Write not allowed in this sandbox")); + } + + let full_path = resolve_path(&cmd_path_remove, &path, sandbox_level)?; + + if full_path.is_dir() { + fs::remove_dir_all(&full_path) + .map_err(|e| mlua::Error::runtime(format!("Remove error: {}", e)))?; + } else { + fs::remove_file(&full_path) + .map_err(|e| mlua::Error::runtime(format!("Remove error: {}", e)))?; + } + + Ok(true) + })?; + fs_table.set("remove", remove_fn)?; + + jarvis.set("fs", fs_table)?; + + Ok(()) +} + +// Resolve path relative to command folder, with sandbox checks +fn resolve_path(command_path: &PathBuf, path: &str, sandbox: SandboxLevel) -> mlua::Result { + let path = Path::new(path); + + // if absolute path, check sandbox allows it + if path.is_absolute() { + if !sandbox.allows_expanded_paths() { + return Err(mlua::Error::runtime("Absolute paths not allowed in this sandbox")); + } + return Ok(path.to_path_buf()); + } + + // relative path - resolve against command folder + let resolved = command_path.join(path); + + // canonicalize to resolve ../ etc and check it's still within command folder + let canonical = resolved.canonicalize() + .unwrap_or_else(|_| resolved.clone()); + + let cmd_canonical = command_path.canonicalize() + .unwrap_or_else(|_| command_path.clone()); + + if !sandbox.allows_expanded_paths() && !canonical.starts_with(&cmd_canonical) { + return Err(mlua::Error::runtime("Path escapes command folder")); + } + + Ok(resolved) +} \ No newline at end of file diff --git a/crates/jarvis-core/src/lua/api/http.rs b/crates/jarvis-core/src/lua/api/http.rs new file mode 100644 index 0000000..a7691ba --- /dev/null +++ b/crates/jarvis-core/src/lua/api/http.rs @@ -0,0 +1,226 @@ +// HTTP Lua API: GET, POST, JSON requests + +use mlua::{Lua, Table, Value}; +use std::collections::HashMap; +use std::time::Duration; + +pub fn register(lua: &Lua, jarvis: &Table) -> mlua::Result<()> { + let http = lua.create_table()?; + + // jarvis.http.get(url, headers?) + let get_fn = lua.create_function(|lua, (url, headers): (String, Option)| { + http_request(lua, "GET", &url, None, headers) + })?; + http.set("get", get_fn)?; + + // jarvis.http.post(url, body, headers?) + let post_fn = lua.create_function(|lua, (url, body, headers): (String, Option, Option
)| { + http_request(lua, "POST", &url, body, headers) + })?; + http.set("post", post_fn)?; + + // jarvis.http.post_json(url, data, headers?) + let post_json_fn = lua.create_function(|lua, (url, data, headers): (String, Table, Option
)| { + // convert Lua table to JSON string + let json_value = table_to_json(lua, data)?; + let body = serde_json::to_string(&json_value) + .map_err(|e| mlua::Error::runtime(e.to_string()))?; + + // add content-type header + let mut header_map: HashMap = HashMap::new(); + header_map.insert("Content-Type".to_string(), "application/json".to_string()); + + if let Some(h) = headers { + for pair in h.pairs::() { + if let Ok((k, v)) = pair { + header_map.insert(k, v); + } + } + } + + http_request_with_headers(lua, "POST", &url, Some(body), header_map) + })?; + http.set("post_json", post_json_fn)?; + + // jarvis.http.json(url) - GET + parse JSON + let json_fn = lua.create_function(|lua, url: String| { + let client = reqwest::blocking::Client::builder() + .timeout(Duration::from_secs(30)) + .build() + .map_err(|e| mlua::Error::runtime(e.to_string()))?; + + let response = client.get(&url) + .send() + .map_err(|e| mlua::Error::runtime(e.to_string()))?; + + if response.status().is_success() { + let json: serde_json::Value = response.json() + .map_err(|e| mlua::Error::runtime(e.to_string()))?; + json_to_lua(lua, json) + } else { + Ok(Value::Nil) + } + })?; + http.set("json", json_fn)?; + + jarvis.set("http", http)?; + + Ok(()) +} + +fn http_request( + lua: &Lua, + method: &str, + url: &str, + body: Option, + headers: Option
, +) -> mlua::Result
{ + let header_map: HashMap = if let Some(h) = headers { + h.pairs::() + .filter_map(|r| r.ok()) + .collect() + } else { + HashMap::new() + }; + + http_request_with_headers(lua, method, url, body, header_map) +} + +fn http_request_with_headers( + lua: &Lua, + method: &str, + url: &str, + body: Option, + headers: HashMap, +) -> mlua::Result
{ + let client = reqwest::blocking::Client::builder() + .timeout(Duration::from_secs(30)) + .build() + .map_err(|e| mlua::Error::runtime(e.to_string()))?; + + let mut request = match method { + "POST" => client.post(url), + "PUT" => client.put(url), + "DELETE" => client.delete(url), + _ => client.get(url), + }; + + for (k, v) in headers { + request = request.header(&k, &v); + } + + if let Some(b) = body { + request = request.body(b); + } + + let result = lua.create_table()?; + + match request.send() { + Ok(response) => { + result.set("ok", response.status().is_success())?; + result.set("status", response.status().as_u16())?; + + // get headers + let headers_table = lua.create_table()?; + for (name, value) in response.headers() { + if let Ok(v) = value.to_str() { + headers_table.set(name.as_str(), v)?; + } + } + result.set("headers", headers_table)?; + + // get body + match response.text() { + Ok(text) => result.set("body", text)?, + Err(e) => result.set("body", format!("Error reading body: {}", e))?, + } + } + Err(e) => { + result.set("ok", false)?; + result.set("status", 0)?; + result.set("error", e.to_string())?; + result.set("body", "")?; + } + } + + Ok(result) +} + +// Convert Lua table to serde_json::Value +fn table_to_json(lua: &Lua, table: Table) -> mlua::Result { + use serde_json::{Value as JsonValue, Map, Number}; + + // check if it's an array (sequential integer keys starting from 1) + let is_array = table.clone().pairs::() + .filter_map(|r| r.ok()) + .enumerate() + .all(|(i, (k, _))| k == (i + 1) as i64); + + if is_array && table.len()? > 0 { + let arr: Vec = table.sequence_values::() + .filter_map(|r| r.ok()) + .map(|v| lua_to_json(lua, v)) + .collect::, _>>()?; + Ok(JsonValue::Array(arr)) + } else { + let mut map = Map::new(); + for pair in table.pairs::() { + let (k, v) = pair?; + map.insert(k, lua_to_json(lua, v)?); + } + Ok(JsonValue::Object(map)) + } +} + +// Convert Lua Value to serde_json::Value +fn lua_to_json(lua: &Lua, value: Value) -> mlua::Result { + use serde_json::{Value as JsonValue, Number}; + + match value { + Value::Nil => Ok(JsonValue::Null), + Value::Boolean(b) => Ok(JsonValue::Bool(b)), + Value::Integer(i) => Ok(JsonValue::Number(Number::from(i))), + Value::Number(n) => { + Number::from_f64(n) + .map(JsonValue::Number) + .ok_or_else(|| mlua::Error::runtime("Invalid float")) + } + Value::String(s) => Ok(JsonValue::String(s.to_str()?.to_string())), + Value::Table(t) => table_to_json(lua, t), + _ => Err(mlua::Error::runtime("Unsupported type for JSON")), + } +} + +// Convert serde_json::Value to Lua Value +fn json_to_lua(lua: &Lua, json: serde_json::Value) -> mlua::Result { + use serde_json::Value as JsonValue; + + match json { + JsonValue::Null => Ok(Value::Nil), + JsonValue::Bool(b) => Ok(Value::Boolean(b)), + JsonValue::Number(n) => { + if let Some(i) = n.as_i64() { + Ok(Value::Integer(i)) + } else if let Some(f) = n.as_f64() { + Ok(Value::Number(f)) + } else { + Ok(Value::Nil) + } + } + JsonValue::String(s) => Ok(Value::String(lua.create_string(&s)?)), + JsonValue::Array(arr) => { + let table = lua.create_table()?; + for (i, v) in arr.into_iter().enumerate() { + table.set(i + 1, json_to_lua(lua, v)?)?; + } + Ok(Value::Table(table)) + } + JsonValue::Object(map) => { + let table = lua.create_table()?; + for (k, v) in map { + table.set(k, json_to_lua(lua, v)?)?; + } + Ok(Value::Table(table)) + } + } +} \ No newline at end of file diff --git a/crates/jarvis-core/src/lua/api/settings.rs b/crates/jarvis-core/src/lua/api/settings.rs new file mode 100644 index 0000000..e69de29 diff --git a/crates/jarvis-core/src/lua/api/state.rs b/crates/jarvis-core/src/lua/api/state.rs new file mode 100644 index 0000000..7916b7d --- /dev/null +++ b/crates/jarvis-core/src/lua/api/state.rs @@ -0,0 +1,184 @@ +// State Lua API: persistent key-value storage per command + +use mlua::{Lua, Table, Result, Value}; +use std::path::PathBuf; +use std::fs; +use std::collections::HashMap; + +const STATE_FILE: &str = ".state.json"; + +pub fn register(lua: &Lua, jarvis: &Table, command_path: &PathBuf) -> mlua::Result<()> { + let state = lua.create_table()?; + let state_path = command_path.join(STATE_FILE); + + // jarvis.state.get(key) + let state_path_get = state_path.clone(); + let get_fn = lua.create_function(move |lua, key: String| { + let data = load_state(&state_path_get); + + if let Some(value) = data.get(&key) { + json_to_lua_value(lua, value.clone()) + } else { + Ok(Value::Nil) + } + })?; + state.set("get", get_fn)?; + + // jarvis.state.set(key, value) + let state_path_set = state_path.clone(); + let set_fn = lua.create_function(move |_, (key, value): (String, Value)| { + let mut data = load_state(&state_path_set); + + let json_value = lua_to_json_value(value)?; + data.insert(key, json_value); + + save_state(&state_path_set, &data)?; + Ok(true) + })?; + state.set("set", set_fn)?; + + // jarvis.state.delete(key) + let state_path_delete = state_path.clone(); + let delete_fn = lua.create_function(move |_, key: String| { + let mut data = load_state(&state_path_delete); + let existed = data.remove(&key).is_some(); + save_state(&state_path_delete, &data)?; + Ok(existed) + })?; + state.set("delete", delete_fn)?; + + // jarvis.state.clear() + let state_path_clear = state_path.clone(); + let clear_fn = lua.create_function(move |_, ()| { + let data: HashMap = HashMap::new(); + save_state(&state_path_clear, &data)?; + Ok(true) + })?; + state.set("clear", clear_fn)?; + + // jarvis.state.keys() + let state_path_keys = state_path.clone(); + let keys_fn = lua.create_function(move |lua, ()| { + let data = load_state(&state_path_keys); + let table = lua.create_table()?; + + for (i, key) in data.keys().enumerate() { + table.set(i + 1, key.clone())?; + } + + Ok(table) + })?; + state.set("keys", keys_fn)?; + + // jarvis.state.all() + let state_path_all = state_path.clone(); + let all_fn = lua.create_function(move |lua, ()| { + let data = load_state(&state_path_all); + let table = lua.create_table()?; + + for (key, value) in data { + let lua_value = json_to_lua_value(lua, value)?; + table.set(key, lua_value)?; + } + + Ok(table) + })?; + state.set("all", all_fn)?; + + jarvis.set("state", state)?; + + Ok(()) +} + +fn load_state(path: &PathBuf) -> HashMap { + if !path.exists() { + return HashMap::new(); + } + + fs::read_to_string(path) + .ok() + .and_then(|s| serde_json::from_str(&s).ok()) + .unwrap_or_default() +} + +fn save_state(path: &PathBuf, data: &HashMap) -> mlua::Result<()> { + let json = serde_json::to_string_pretty(data) + .map_err(|e| mlua::Error::runtime(e.to_string()))?; + + fs::write(path, json) + .map_err(|e| mlua::Error::runtime(e.to_string()))?; + + Ok(()) +} + +fn lua_to_json_value(value: Value) -> mlua::Result { + use serde_json::Value as JsonValue; + + match value { + Value::Nil => Ok(JsonValue::Null), + Value::Boolean(b) => Ok(JsonValue::Bool(b)), + Value::Integer(i) => Ok(JsonValue::Number(i.into())), + Value::Number(n) => { + serde_json::Number::from_f64(n) + .map(JsonValue::Number) + .ok_or_else(|| mlua::Error::runtime("Invalid float")) + } + Value::String(s) => Ok(JsonValue::String(s.to_str()?.to_string())), + Value::Table(t) => { + // check if array + let is_array = t.clone().pairs::() + .filter_map(|r| r.ok()) + .enumerate() + .all(|(i, (k, _))| k == (i + 1) as i64); + + if is_array && t.len().unwrap_or(0) > 0 { + let arr: Vec = t.sequence_values::() + .filter_map(|r| r.ok()) + .map(lua_to_json_value) + .collect::>>()?; + Ok(JsonValue::Array(arr)) + } else { + let mut map = serde_json::Map::new(); + for pair in t.pairs::() { + let (k, v) = pair?; + map.insert(k, lua_to_json_value(v)?); + } + Ok(JsonValue::Object(map)) + } + } + _ => Err(mlua::Error::runtime("Unsupported type for state")), + } +} + +fn json_to_lua_value(lua: &Lua, json: serde_json::Value) -> mlua::Result { + use serde_json::Value as JsonValue; + + match json { + JsonValue::Null => Ok(Value::Nil), + JsonValue::Bool(b) => Ok(Value::Boolean(b)), + JsonValue::Number(n) => { + if let Some(i) = n.as_i64() { + Ok(Value::Integer(i)) + } else if let Some(f) = n.as_f64() { + Ok(Value::Number(f)) + } else { + Ok(Value::Nil) + } + } + JsonValue::String(s) => Ok(Value::String(lua.create_string(&s)?)), + JsonValue::Array(arr) => { + let table = lua.create_table()?; + for (i, v) in arr.into_iter().enumerate() { + table.set(i + 1, json_to_lua_value(lua, v)?)?; + } + Ok(Value::Table(table)) + } + JsonValue::Object(map) => { + let table = lua.create_table()?; + for (k, v) in map { + table.set(k, json_to_lua_value(lua, v)?)?; + } + Ok(Value::Table(table)) + } + } +} \ No newline at end of file diff --git a/crates/jarvis-core/src/lua/api/system.rs b/crates/jarvis-core/src/lua/api/system.rs new file mode 100644 index 0000000..2722db8 --- /dev/null +++ b/crates/jarvis-core/src/lua/api/system.rs @@ -0,0 +1,240 @@ +// System Lua API: exec, open, clipboard, notify + +use mlua::{Lua, Table}; +use std::process::Command; + +use crate::lua::sandbox::SandboxLevel; + +pub fn register(lua: &Lua, jarvis: &Table, sandbox: SandboxLevel) -> mlua::Result<()> { + let system = lua.create_table()?; + + // jarvis.system.open(url_or_path) - always available + let open_fn = lua.create_function(|_, target: String| { + let result = if cfg!(target_os = "windows") { + Command::new("cmd") + .args(["/C", "start", "", &target]) + .spawn() + } else if cfg!(target_os = "macos") { + Command::new("open") + .arg(&target) + .spawn() + } else { + Command::new("xdg-open") + .arg(&target) + .spawn() + }; + + match result { + Ok(_) => Ok(true), + Err(e) => { + log::warn!("[Lua] Failed to open {}: {}", target, e); + Ok(false) + } + } + })?; + system.set("open", open_fn)?; + + // jarvis.system.exec(cmd, args?) - only in full sandbox + if sandbox.allows_exec() { + let exec_fn = lua.create_function(|lua, (cmd, args): (String, Option
)| { + let mut command = if cfg!(target_os = "windows") { + let mut c = Command::new("cmd"); + c.args(["/C", &cmd]); + c + } else { + let mut c = Command::new("sh"); + c.args(["-c", &cmd]); + c + }; + + // add extra args if provided + if let Some(args_table) = args { + for pair in args_table.sequence_values::() { + if let Ok(arg) = pair { + command.arg(arg); + } + } + } + + let output = command.output() + .map_err(|e| mlua::Error::runtime(e.to_string()))?; + + let result = lua.create_table()?; + result.set("success", output.status.success())?; + result.set("code", output.status.code().unwrap_or(-1))?; + result.set("stdout", String::from_utf8_lossy(&output.stdout).to_string())?; + result.set("stderr", String::from_utf8_lossy(&output.stderr).to_string())?; + + Ok(result) + })?; + system.set("exec", exec_fn)?; + } + + // jarvis.system.notify(title, message) - always available + let notify_fn = lua.create_function(|_, (title, message): (String, String)| { + log::info!("[Lua] NOTIFY: {} - {}", title, message); + + // platform-specific notification + #[cfg(target_os = "windows")] + { + use winrt_notification::{Toast, Duration as ToastDuration}; + + if let Err(e) = Toast::new(Toast::POWERSHELL_APP_ID) + .title(&title) + .text1(&message) + .duration(ToastDuration::Short) + .show() + { + log::warn!("[Lua] Failed to show toast notification: {}", e); + // fallback to msg.exe + let _ = Command::new("msg") + .args(["*", "/time:10", &format!("{}: {}", title, message)]) + .spawn(); + } + } + + #[cfg(target_os = "linux")] + { + let _ = Command::new("notify-send") + .args([&title, &message]) + .spawn(); + } + + #[cfg(target_os = "macos")] + { + let script = format!( + r#"display notification "{}" with title "{}""#, + message.replace("\"", "\\\""), + title.replace("\"", "\\\"") + ); + let _ = Command::new("osascript") + .args(["-e", &script]) + .spawn(); + } + + Ok(true) + })?; + system.set("notify", notify_fn)?; + + // jarvis.system.clipboard - subtable + let clipboard = lua.create_table()?; + + // jarvis.system.clipboard.get() - always available + let clipboard_get_fn = lua.create_function(|_, ()| { + #[cfg(target_os = "windows")] + { + let output = Command::new("powershell") + .args(["-Command", "Get-Clipboard"]) + .output() + .map_err(|e| mlua::Error::runtime(e.to_string()))?; + + Ok(String::from_utf8_lossy(&output.stdout).trim().to_string()) + } + + #[cfg(target_os = "linux")] + { + let output = Command::new("xclip") + .args(["-selection", "clipboard", "-o"]) + .output() + .or_else(|_| { + Command::new("xsel") + .args(["--clipboard", "--output"]) + .output() + }) + .map_err(|e| mlua::Error::runtime(e.to_string()))?; + + Ok(String::from_utf8_lossy(&output.stdout).to_string()) + } + + #[cfg(target_os = "macos")] + { + let output = Command::new("pbpaste") + .output() + .map_err(|e| mlua::Error::runtime(e.to_string()))?; + + Ok(String::from_utf8_lossy(&output.stdout).to_string()) + } + + #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))] + { + Err(mlua::Error::runtime("Clipboard not supported on this platform")) + } + })?; + clipboard.set("get", clipboard_get_fn)?; + + // jarvis.system.clipboard.set(text) - only in full sandbox + if sandbox.allows_clipboard_write() { + let clipboard_set_fn = lua.create_function(|_, text: String| { + #[cfg(target_os = "windows")] + { + let script = format!("Set-Clipboard -Value '{}'", text.replace("'", "''")); + Command::new("powershell") + .args(["-Command", &script]) + .output() + .map_err(|e| mlua::Error::runtime(e.to_string()))?; + } + + #[cfg(target_os = "linux")] + { + use std::io::Write; + let mut child = Command::new("xclip") + .args(["-selection", "clipboard"]) + .stdin(std::process::Stdio::piped()) + .spawn() + .or_else(|_| { + Command::new("xsel") + .args(["--clipboard", "--input"]) + .stdin(std::process::Stdio::piped()) + .spawn() + }) + .map_err(|e| mlua::Error::runtime(e.to_string()))?; + + if let Some(stdin) = child.stdin.as_mut() { + stdin.write_all(text.as_bytes()) + .map_err(|e| mlua::Error::runtime(e.to_string()))?; + } + } + + #[cfg(target_os = "macos")] + { + use std::io::Write; + let mut child = Command::new("pbcopy") + .stdin(std::process::Stdio::piped()) + .spawn() + .map_err(|e| mlua::Error::runtime(e.to_string()))?; + + if let Some(stdin) = child.stdin.as_mut() { + stdin.write_all(text.as_bytes()) + .map_err(|e| mlua::Error::runtime(e.to_string()))?; + } + } + + Ok(true) + })?; + clipboard.set("set", clipboard_set_fn)?; + } + + system.set("clipboard", clipboard)?; + + // jarvis.system.env(name) - get environment variable (always available) + let env_fn = lua.create_function(|_, name: String| { + Ok(std::env::var(&name).ok()) + })?; + system.set("env", env_fn)?; + + // jarvis.system.platform - read-only string + let platform = if cfg!(target_os = "windows") { + "windows" + } else if cfg!(target_os = "macos") { + "macos" + } else if cfg!(target_os = "linux") { + "linux" + } else { + "unknown" + }; + system.set("platform", platform)?; + + jarvis.set("system", system)?; + + Ok(()) +} \ No newline at end of file diff --git a/crates/jarvis-core/src/lua/engine.rs b/crates/jarvis-core/src/lua/engine.rs new file mode 100644 index 0000000..b85c5f2 --- /dev/null +++ b/crates/jarvis-core/src/lua/engine.rs @@ -0,0 +1,171 @@ +use mlua::{Lua, Result as LuaResult, Value, StdLib}; +use std::path::PathBuf; +use std::time::{Duration, Instant}; +use std::fs; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, mpsc}; + +use super::sandbox::SandboxLevel; +use super::error::LuaError; +use super::{CommandContext, CommandResult}; +use super::api; + +pub struct LuaEngine { + lua: Lua, + sandbox: SandboxLevel, +} + + + +impl LuaEngine { + pub fn new(sandbox: SandboxLevel) -> Result { + // select which standard libraries to load based on sandbox access level + let std_libs = match sandbox { + SandboxLevel::Minimal => { + StdLib::TABLE | StdLib::STRING | StdLib::MATH + } + SandboxLevel::Standard => { + StdLib::TABLE | StdLib::STRING | StdLib::MATH | StdLib::UTF8 + } + SandboxLevel::Full => { + StdLib::TABLE | StdLib::STRING | StdLib::MATH | StdLib::UTF8 | StdLib::OS + } + }; + + let lua = Lua::new_with(std_libs, mlua::LuaOptions::default()) + .map_err(|e| LuaError::InitError(e.to_string()))?; + + // remove dangerous globals regardless of sandbox + { + let globals = lua.globals(); + + // always remove these + let _ = globals.set("loadfile", Value::Nil); + let _ = globals.set("dofile", Value::Nil); + let _ = globals.set("load", Value::Nil); + let _ = globals.set("loadstring", Value::Nil); + + // remove io unless full sandbox + if !matches!(sandbox, SandboxLevel::Full) { + let _ = globals.set("io", Value::Nil); + } + + // remove os.execute, os.exit, os.setlocale even in full mode + // for SECURITY REASONS!!! + if matches!(sandbox, SandboxLevel::Full) { + if let Ok(os) = globals.get::("os") { + let _ = os.set("execute", Value::Nil); + let _ = os.set("exit", Value::Nil); + let _ = os.set("remove", Value::Nil); + let _ = os.set("rename", Value::Nil); + let _ = os.set("setlocale", Value::Nil); + } + } + } + + Ok(Self { lua, sandbox }) + } + + // Register all jarvis APIs + fn register_api(&self, context: &CommandContext) -> Result<(), LuaError> { + let globals = self.lua.globals(); + + // main jarvis table + let jarvis = self.lua.create_table() + .map_err(|e| LuaError::InitError(e.to_string()))?; + + // always register core APIs + api::core::register(&self.lua, &jarvis)?; + api::audio::register(&self.lua, &jarvis)?; + api::context::register(&self.lua, &jarvis, context)?; + + // sandbox-controlled APIs + if self.sandbox.allows_http() { + api::http::register(&self.lua, &jarvis)?; + } + + if self.sandbox.allows_state() { + api::state::register(&self.lua, &jarvis, &context.command_path)?; + } + + if self.sandbox.allows_fs() { + api::fs::register(&self.lua, &jarvis, &context.command_path, self.sandbox)?; + } + + api::system::register(&self.lua, &jarvis, self.sandbox)?; + + globals.set("jarvis", jarvis) + .map_err(|e| LuaError::InitError(e.to_string()))?; + + Ok(()) + } + + // Main LUA executor + pub fn execute( + &self, + script_path: &PathBuf, + context: CommandContext, + timeout: Duration, + ) -> Result { + // register APIs + self.register_api(&context)?; + + // load script + let script_content = fs::read_to_string(script_path) + .map_err(|e| LuaError::LoadError(format!("{}: {}", script_path.display(), e)))?; + + let script_name = script_path.file_name() + .unwrap() + .to_string_lossy() + .to_string(); + + + // set up timeout hook + let start = std::time::Instant::now(); + self.lua.set_hook(mlua::HookTriggers { + every_nth_instruction: Some(1000), + ..Default::default() + }, move |_lua, _debug| { + if start.elapsed() > timeout { + Err(mlua::Error::runtime("Script timeout")) + } else { + Ok(mlua::VmState::Continue) + } + }).map_err(|e| LuaError::InitError(e.to_string()))?; + + // execute script + let result = self.lua.load(&script_content) + .set_name(&script_name) + .eval::(); + + // remove hook + let _ = self.lua.remove_hook(); + + // result + match result { + Ok(value) => Ok(self.parse_result(value)), + Err(e) => { + if e.to_string().contains("timeout") { + Err(LuaError::Timeout) + } else { + Err(LuaError::RuntimeError(e.to_string())) + } + } + } + } + + // Parse Lua return value into CommandResult + fn parse_result(&self, value: Value) -> CommandResult { + match value { + // return { chain = false } + Value::Table(t) => { + let chain = t.get::("chain").unwrap_or(true); + CommandResult { chain } + } + // return false (shorthand for no chain) + Value::Boolean(chain) => CommandResult { chain }, + // return nil or no return = chain + _ => CommandResult::default(), + } + } +} \ No newline at end of file diff --git a/crates/jarvis-core/src/lua/error.rs b/crates/jarvis-core/src/lua/error.rs new file mode 100644 index 0000000..2888101 --- /dev/null +++ b/crates/jarvis-core/src/lua/error.rs @@ -0,0 +1,49 @@ +use std::fmt; + +#[derive(Debug)] +pub enum LuaError { + // Failed to create Lua VM + InitError(String), + + // Failed to load script file + LoadError(String), + + // Script execution error + RuntimeError(String), + + // Script exceeded timeout + Timeout, + + // Sandbox violation + SandboxViolation(String), + + // IO error + IoError(std::io::Error), +} + +impl fmt::Display for LuaError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + LuaError::InitError(msg) => write!(f, "Lua init error: {}", msg), + LuaError::LoadError(msg) => write!(f, "Script load error: {}", msg), + LuaError::RuntimeError(msg) => write!(f, "Runtime error: {}", msg), + LuaError::Timeout => write!(f, "Script timeout"), + LuaError::SandboxViolation(msg) => write!(f, "Sandbox violation: {}", msg), + LuaError::IoError(e) => write!(f, "IO error: {}", e), + } + } +} + +impl std::error::Error for LuaError {} + +impl From for LuaError { + fn from(e: mlua::Error) -> Self { + LuaError::RuntimeError(e.to_string()) + } +} + +impl From for LuaError { + fn from(e: std::io::Error) -> Self { + LuaError::IoError(e) + } +} \ No newline at end of file diff --git a/crates/jarvis-core/src/lua/sandbox.rs b/crates/jarvis-core/src/lua/sandbox.rs new file mode 100644 index 0000000..3e54b7e --- /dev/null +++ b/crates/jarvis-core/src/lua/sandbox.rs @@ -0,0 +1,65 @@ +use serde::{Deserialize, Serialize}; + +// Sandbox level controlling what APIs are available +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum SandboxLevel { + // Minimal: only core APIs (log, speak, audio, context) + Minimal, + + // Standard: + http, state, fs (command folder only) + Standard, + + // Full: + system.exec, expanded fs access + Full, +} + +impl SandboxLevel { + pub fn from_str(s: &str) -> Self { + match s.to_lowercase().as_str() { + "minimal" => SandboxLevel::Minimal, + "full" => SandboxLevel::Full, + _ => SandboxLevel::Standard, + } + } + + // Can use HTTP API + pub fn allows_http(&self) -> bool { + matches!(self, SandboxLevel::Standard | SandboxLevel::Full) + } + + // Can use persistent state API + pub fn allows_state(&self) -> bool { + matches!(self, SandboxLevel::Standard | SandboxLevel::Full) + } + + // Can use file system API + pub fn allows_fs(&self) -> bool { + matches!(self, SandboxLevel::Standard | SandboxLevel::Full) + } + + // Can write files + pub fn allows_fs_write(&self) -> bool { + matches!(self, SandboxLevel::Standard | SandboxLevel::Full) + } + + // Can execute system commands + pub fn allows_exec(&self) -> bool { + matches!(self, SandboxLevel::Full) + } + + // Can access clipboard write + pub fn allows_clipboard_write(&self) -> bool { + matches!(self, SandboxLevel::Full) + } + + // Can access paths outside command folder + pub fn allows_expanded_paths(&self) -> bool { + matches!(self, SandboxLevel::Full) + } +} + +impl Default for SandboxLevel { + fn default() -> Self { + SandboxLevel::Standard + } +} \ No newline at end of file diff --git a/crates/jarvis-core/src/lua/structs.rs b/crates/jarvis-core/src/lua/structs.rs new file mode 100644 index 0000000..3cc9f11 --- /dev/null +++ b/crates/jarvis-core/src/lua/structs.rs @@ -0,0 +1,30 @@ +use std::path::PathBuf; + +// Context passed to Lua scripts +#[derive(Debug, Clone)] +pub struct CommandContext { + // The phrase that triggered the command + pub phrase: String, + + // Command ID + pub command_id: String, + + // Path to command folder + pub command_path: PathBuf, + + // Current language + pub language: String, +} + +// Result returned from Lua script execution +#[derive(Debug, Clone)] +pub struct CommandResult { + // Whether to continue chaining commands + pub chain: bool, +} + +impl Default for CommandResult { + fn default() -> Self { + Self { chain: true } + } +} \ No newline at end of file diff --git a/crates/jarvis-core/src/lua/tests.rs b/crates/jarvis-core/src/lua/tests.rs new file mode 100644 index 0000000..e558a69 --- /dev/null +++ b/crates/jarvis-core/src/lua/tests.rs @@ -0,0 +1,111 @@ +#[cfg(test)] +mod tests { + use crate::lua::{CommandContext, LuaError, SandboxLevel, execute}; + + use std::path::PathBuf; + use std::time::Duration; + use tempfile::tempdir; + use std::fs; + + fn create_test_context(cmd_path: PathBuf) -> CommandContext { + CommandContext { + phrase: "test phrase".to_string(), + command_id: "test_cmd".to_string(), + command_path: cmd_path, + language: "en".to_string(), + } + } + + #[test] + fn test_minimal_sandbox() { + let dir = tempdir().unwrap(); + let script_path = dir.path().join("test.lua"); + + fs::write(&script_path, r#" + jarvis.log("info", "test log") + return { chain = false } + "#).unwrap(); + + let context = create_test_context(dir.path().to_path_buf()); + let result = execute( + &script_path, + context, + SandboxLevel::Minimal, + Duration::from_secs(5), + ); + + assert!(result.is_ok()); + assert_eq!(result.unwrap().chain, false); + } + + #[test] + fn test_state_persistence() { + let dir = tempdir().unwrap(); + let script_path = dir.path().join("test.lua"); + + // first run - set state + fs::write(&script_path, r#" + jarvis.state.set("key", "value") + return true + "#).unwrap(); + + let context = create_test_context(dir.path().to_path_buf()); + execute(&script_path, context, SandboxLevel::Standard, Duration::from_secs(5)).unwrap(); + + // second run - read state + fs::write(&script_path, r#" + local val = jarvis.state.get("key") + if val == "value" then + return true + else + error("State not persisted") + end + "#).unwrap(); + + let context = create_test_context(dir.path().to_path_buf()); + let result = execute(&script_path, context, SandboxLevel::Standard, Duration::from_secs(5)); + + assert!(result.is_ok()); + } + + #[test] + fn test_timeout() { + let dir = tempdir().unwrap(); + let script_path = dir.path().join("test.lua"); + + fs::write(&script_path, r#" + while true do end + "#).unwrap(); + + let context = create_test_context(dir.path().to_path_buf()); + let result = execute( + &script_path, + context, + SandboxLevel::Minimal, + Duration::from_millis(100), + ); + + assert!(matches!(result, Err(LuaError::Timeout))); + } + + #[test] + fn test_sandbox_fs_escape() { + let dir = tempdir().unwrap(); + let script_path = dir.path().join("test.lua"); + + fs::write(&script_path, r#" + local ok, err = pcall(function() + jarvis.fs.read("../../../etc/passwd") + end) + if ok then + error("Should have been blocked") + end + return true + "#).unwrap(); + + let context = create_test_context(dir.path().to_path_buf()); + let result = execute(&script_path, context, SandboxLevel::Standard, Duration::from_secs(5)); + + assert!(result.is_ok()); + } +} \ No newline at end of file diff --git a/crates/jarvis-core/src/voices.rs b/crates/jarvis-core/src/voices.rs index abfab95..28506f1 100644 --- a/crates/jarvis-core/src/voices.rs +++ b/crates/jarvis-core/src/voices.rs @@ -7,7 +7,8 @@ use parking_lot::RwLock; use crate::{DB, SOUND_DIR, audio, config, time}; -pub mod structs; +mod structs; +pub use structs::*; static VOICES: OnceCell> = OnceCell::new(); static CURRENT_VOICE_ID: OnceCell> = OnceCell::new(); diff --git a/crates/jarvis-gui/src/tauri_commands/voices.rs b/crates/jarvis-gui/src/tauri_commands/voices.rs index 3d98411..bc05e63 100644 --- a/crates/jarvis-gui/src/tauri_commands/voices.rs +++ b/crates/jarvis-gui/src/tauri_commands/voices.rs @@ -1,4 +1,4 @@ -use jarvis_core::voices::{self, structs::VoiceConfig}; +use jarvis_core::voices::{self, VoiceConfig}; #[tauri::command] pub fn list_voices() -> Vec { diff --git a/post_build.py b/post_build.py index 8d65dff..39a5e15 100644 --- a/post_build.py +++ b/post_build.py @@ -6,6 +6,7 @@ import os from pathlib import Path import shutil +import sys # some config vars # format: (source, destination_name) @@ -30,6 +31,9 @@ TARGET_DIRS = ( ABS_PATH = os.getcwd() + "/" +# check for force flag +force_overwrite = "-force" in sys.argv + for tdir in TARGET_DIRS: tdir = ABS_PATH + tdir @@ -52,7 +56,12 @@ for tdir in TARGET_DIRS: full_target_dir_path = os.path.join(tdir, target_name) if os.path.isdir(full_target_dir_path): - print("[-] Directory already exists, skipping: ", src, "->", target_name) + if force_overwrite: + shutil.rmtree(full_target_dir_path) + shutil.copytree(src_path, full_target_dir_path) + print("[+] Directory overwritten: ", src, "->", target_name) + else: + print("[-] Directory already exists, skipping: ", src, "->", target_name) else: shutil.copytree(src_path, full_target_dir_path) print("[+] Directory copied: ", src, "->", target_name) @@ -63,11 +72,16 @@ for tdir in TARGET_DIRS: full_target_file_path = os.path.join(tdir, target_name) if os.path.isfile(full_target_file_path): - print("[-] File already exists, skipping: ", src, "->", target_name) + if force_overwrite: + os.remove(full_target_file_path) + shutil.copy(src_path, full_target_file_path) + print("[+] File overwritten: ", src, "->", target_name) + else: + print("[-] File already exists, skipping: ", src, "->", target_name) else: shutil.copy(src_path, full_target_file_path) print("[+] File copied: ", src, "->", target_name) else: print("[?] Unknown entity to copy: ", src) - print("Post compile build done.") \ No newline at end of file + print("Post compile build done.") diff --git a/resources/commands/browser/command.toml b/resources/commands/browser/command.toml index 13de7fb..d190402 100644 --- a/resources/commands/browser/command.toml +++ b/resources/commands/browser/command.toml @@ -1,6 +1,6 @@ [[commands]] id = "browser_open" -action = "ahk" +type = "ahk" exe_path = "ahk/Run browser.exe" sounds.ru = ["ok1", "ok2", "ok3", "ok4"] sounds.en = ["ok1", "ok2", "ok3"] @@ -36,7 +36,7 @@ phrases.ua = [ [[commands]] id = "browser_close" -action = "ahk" +type = "ahk" exe_path = "ahk/Close browser.exe" sounds.ru = ["ok1", "ok2", "ok3", "ok4"] sounds.en = ["ok1", "ok2", "ok3"] @@ -64,7 +64,7 @@ phrases.ua = [ [[commands]] id = "open_google" -action = "ahk" +type = "ahk" exe_path = "ahk/Run website.exe" exe_args = ["http://google.com"] sounds.ru = ["ok1", "ok2", "ok3", "ok4"] diff --git a/resources/commands/counter/command.toml b/resources/commands/counter/command.toml new file mode 100644 index 0000000..acd01f4 --- /dev/null +++ b/resources/commands/counter/command.toml @@ -0,0 +1,13 @@ +[[commands]] +id = "counter" +type = "lua" +script = "script.lua" +sandbox = "standard" +timeout = 5000 +phrases.ru = [ + "счётчик" +] +phrases.en = [ + "counter", + "count", +] \ No newline at end of file diff --git a/resources/commands/counter/script.lua b/resources/commands/counter/script.lua new file mode 100644 index 0000000..06ef702 --- /dev/null +++ b/resources/commands/counter/script.lua @@ -0,0 +1,16 @@ +-- simple counter demonstrating state persistence + +local count = jarvis.state.get("count") or 0 +count = count + 1 +jarvis.state.set("count", count) + +local lang = jarvis.context.language +local msg = lang == "ru" + and "Счётчик: " .. count + or "Counter: " .. count + +jarvis.log("info", msg) +jarvis.system.notify("Counter", tostring(count)) +jarvis.audio.play_ok() + +return { chain = true } \ No newline at end of file diff --git a/resources/commands/hello/command.toml b/resources/commands/hello/command.toml new file mode 100644 index 0000000..d3c9d70 --- /dev/null +++ b/resources/commands/hello/command.toml @@ -0,0 +1,14 @@ +[[commands]] +id = "hello" +type = "lua" +script = "script.lua" +sandbox = "minimal" +timeout = 5000 +phrases.ru = [ + "привет", + "здравствуй", +] +phrases.en = [ + "hello", + "hi", +] \ No newline at end of file diff --git a/resources/commands/hello/script.lua b/resources/commands/hello/script.lua new file mode 100644 index 0000000..57e17b8 --- /dev/null +++ b/resources/commands/hello/script.lua @@ -0,0 +1,21 @@ +-- simple test hello command + +local lang = jarvis.context.language +local hour = tonumber(jarvis.context.time.hour) + +-- determine greeting based on time +local greeting +if hour >= 5 and hour < 12 then + greeting = lang == "ru" and "Доброе утро" or "Good morning" +elseif hour >= 12 and hour < 17 then + greeting = lang == "ru" and "Добрый день" or "Good afternoon" +elseif hour >= 17 and hour < 22 then + greeting = lang == "ru" and "Добрый вечер" or "Good evening" +else + greeting = lang == "ru" and "Доброй ночи" or "Good night" +end + +jarvis.log("info", "Greeting user: " .. greeting) +jarvis.audio.play_reply() + +return { chain = true } \ No newline at end of file diff --git a/resources/commands/weather/command.toml b/resources/commands/weather/command.toml index 98e4b22..3bfc7bf 100644 --- a/resources/commands/weather/command.toml +++ b/resources/commands/weather/command.toml @@ -1,7 +1,9 @@ [[commands]] id = "weather" -action = "cli" -cli_cmd = "weather.py" +type = "lua" +script = "script.lua" +timeout = 5000 +sandbox = "standard" [commands.phrases] ru = [ @@ -15,4 +17,18 @@ en = [ [commands.sounds] ru = ["weather_ru_1", "weather_ru_2"] -en = ["weather_en_1", "weather_en_2"] \ No newline at end of file +en = ["weather_en_1", "weather_en_2"] + + + +[[commands]] +id = "set_city" +type = "lua" +script = "set_city.lua" +sandbox = "standard" +timeout = 5000 +phrases = [ + "установи город", + "set city", + "change city", +] \ No newline at end of file diff --git a/resources/commands/weather/script.lua b/resources/commands/weather/script.lua new file mode 100644 index 0000000..460f1da --- /dev/null +++ b/resources/commands/weather/script.lua @@ -0,0 +1,29 @@ +-- weather command using wttr.in API + +local lang = jarvis.context.language + +-- get saved city or use default +local city = jarvis.state.get("city") or "Moscow" + +jarvis.log("info", "Fetching weather for: " .. city) + +-- build URL +local url = "https://wttr.in/" .. city .. "?format=3&lang=" .. lang + +-- make request +local response = jarvis.http.get(url) + +if response.ok then + jarvis.log("info", "Weather: " .. response.body) + + -- show notification + local title = lang == "ru" and "Погода" or "Weather" + jarvis.system.notify(title, response.body) + + jarvis.audio.play_ok() +else + jarvis.log("error", "Failed to fetch weather: " .. (response.error or "unknown error")) + jarvis.audio.play_error() +end + +return { chain = false } \ No newline at end of file diff --git a/resources/commands/weather/set_city.lua b/resources/commands/weather/set_city.lua new file mode 100644 index 0000000..98d8142 --- /dev/null +++ b/resources/commands/weather/set_city.lua @@ -0,0 +1,32 @@ +-- set city for weather command + +local phrase = jarvis.context.phrase +local lang = jarvis.context.language + +-- try to extract city name from phrase +-- this is a simple example - you might want better parsing +local city = phrase:match("город%s+(.+)") or phrase:match("city%s+(.+)") + +if city then + city = city:gsub("^%s*(.-)%s*$", "%1") -- trim + + -- save to state (shared with weather command) + jarvis.state.set("city", city) + + local msg = lang == "ru" + and "Город установлен: " .. city + or "City set to: " .. city + + jarvis.log("info", msg) + jarvis.system.notify("Jarvis", msg) + jarvis.audio.play_ok() +else + local msg = lang == "ru" + and "Не удалось определить город" + or "Could not determine city" + + jarvis.log("warn", msg) + jarvis.audio.play_not_found() +end + +return { chain = false } \ No newline at end of file