mirror of
https://github.com/aiclientproxy/proxycast.git
synced 2026-10-24 23:31:47 +08:00
feat: finalize image search workflow and release v0.76.0
This commit is contained in:
Generated
+515
-21
@@ -2,6 +2,163 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "actix-codec"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-http"
|
||||
version = "3.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f860ee6746d0c5b682147b2f7f8ef036d4f92fe518251a3a35ffa3650eafdf0e"
|
||||
dependencies = [
|
||||
"actix-codec",
|
||||
"actix-rt",
|
||||
"actix-service",
|
||||
"actix-utils",
|
||||
"bitflags 2.10.0",
|
||||
"bytes",
|
||||
"bytestring",
|
||||
"derive_more 2.1.1",
|
||||
"encoding_rs",
|
||||
"foldhash 0.1.5",
|
||||
"futures-core",
|
||||
"http 0.2.12",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"language-tags",
|
||||
"mime",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"smallvec",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-router"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14f8c75c51892f18d9c46150c5ac7beb81c95f78c8b83a634d49f4ca32551fe7"
|
||||
dependencies = [
|
||||
"bytestring",
|
||||
"cfg-if",
|
||||
"http 0.2.12",
|
||||
"regex-lite",
|
||||
"serde",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-rt"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92589714878ca59a7626ea19734f0e07a6a875197eec751bb5d3f99e64998c63"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-server"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a65064ea4a457eaf07f2fba30b4c695bf43b721790e9530d26cb6f9019ff7502"
|
||||
dependencies = [
|
||||
"actix-rt",
|
||||
"actix-service",
|
||||
"actix-utils",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"mio 1.1.1",
|
||||
"socket2 0.5.10",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-service"
|
||||
version = "2.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e46f36bf0e5af44bdc4bdb36fbbd421aa98c79a9bce724e1edeb3894e10dc7f"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-utils"
|
||||
version = "3.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8"
|
||||
dependencies = [
|
||||
"local-waker",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-web"
|
||||
version = "4.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff87453bc3b56e9b2b23c1cc0b1be8797184accf51d2abe0f8a33ec275d316bf"
|
||||
dependencies = [
|
||||
"actix-codec",
|
||||
"actix-http",
|
||||
"actix-router",
|
||||
"actix-rt",
|
||||
"actix-server",
|
||||
"actix-service",
|
||||
"actix-utils",
|
||||
"bytes",
|
||||
"bytestring",
|
||||
"cfg-if",
|
||||
"derive_more 2.1.1",
|
||||
"encoding_rs",
|
||||
"foldhash 0.1.5",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"impl-more",
|
||||
"itoa",
|
||||
"language-tags",
|
||||
"log",
|
||||
"mime",
|
||||
"once_cell",
|
||||
"pin-project-lite",
|
||||
"regex-lite",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"smallvec",
|
||||
"socket2 0.6.2",
|
||||
"time",
|
||||
"tracing",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.25.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.1"
|
||||
@@ -1083,6 +1240,21 @@ dependencies = [
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.7"
|
||||
@@ -1518,6 +1690,15 @@ dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytestring"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "113b4343b5f6617e7ad401ced8de3cc8b012e73a594347c307b90db3e9271289"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bzip2"
|
||||
version = "0.4.4"
|
||||
@@ -1923,6 +2104,15 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cookie"
|
||||
version = "0.18.1"
|
||||
@@ -2416,6 +2606,16 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "debugid"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.7.10"
|
||||
@@ -2474,13 +2674,36 @@ version = "0.99.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"convert_case 0.4.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version 0.4.1",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
|
||||
dependencies = [
|
||||
"derive_more-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more-impl"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
|
||||
dependencies = [
|
||||
"convert_case 0.10.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version 0.4.1",
|
||||
"syn 2.0.114",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
@@ -3030,6 +3253,18 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
|
||||
[[package]]
|
||||
name = "findshlibs"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.5.7"
|
||||
@@ -3503,6 +3738,12 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.32.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
|
||||
|
||||
[[package]]
|
||||
name = "gio"
|
||||
version = "0.18.4"
|
||||
@@ -4085,7 +4326,7 @@ dependencies = [
|
||||
"js-sys",
|
||||
"log",
|
||||
"wasm-bindgen",
|
||||
"windows-core 0.57.0",
|
||||
"windows-core 0.62.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4273,6 +4514,12 @@ dependencies = [
|
||||
"tiff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "impl-more"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2"
|
||||
|
||||
[[package]]
|
||||
name = "include_dir"
|
||||
version = "0.7.4"
|
||||
@@ -4665,6 +4912,12 @@ dependencies = [
|
||||
"selectors 0.24.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "language-tags"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
@@ -4818,6 +5071,12 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
|
||||
|
||||
[[package]]
|
||||
name = "local-waker"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.14"
|
||||
@@ -5422,7 +5681,7 @@ version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7"
|
||||
dependencies = [
|
||||
"proc-macro-crate 2.0.2",
|
||||
"proc-macro-crate 3.4.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.114",
|
||||
@@ -5609,6 +5868,16 @@ dependencies = [
|
||||
"objc2-foundation 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-location"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009"
|
||||
dependencies = [
|
||||
"objc2 0.6.3",
|
||||
"objc2-foundation 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-text"
|
||||
version = "0.3.2"
|
||||
@@ -5750,8 +6019,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"block2 0.6.2",
|
||||
"objc2 0.6.3",
|
||||
"objc2-cloud-kit",
|
||||
"objc2-core-data 0.3.2",
|
||||
"objc2-core-foundation",
|
||||
"objc2-core-graphics",
|
||||
"objc2-core-image 0.3.2",
|
||||
"objc2-core-location",
|
||||
"objc2-core-text",
|
||||
"objc2-foundation 0.3.2",
|
||||
"objc2-quartz-core 0.3.2",
|
||||
"objc2-user-notifications",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-user-notifications"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e"
|
||||
dependencies = [
|
||||
"objc2 0.6.3",
|
||||
"objc2-foundation 0.3.2",
|
||||
]
|
||||
|
||||
@@ -5771,6 +6059,15 @@ dependencies = [
|
||||
"objc2-security",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.37.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oboe"
|
||||
version = "0.6.1"
|
||||
@@ -6003,6 +6300,22 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "os_info"
|
||||
version = "3.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4022a17595a00d6a369236fdae483f0de7f0a339960a53118b818238e132224"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"log",
|
||||
"nix 0.30.1",
|
||||
"objc2 0.6.3",
|
||||
"objc2-foundation 0.3.2",
|
||||
"objc2-ui-kit",
|
||||
"serde",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "os_pipe"
|
||||
version = "1.2.3"
|
||||
@@ -6677,7 +6990,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.13.0",
|
||||
"itertools 0.14.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.114",
|
||||
@@ -6685,7 +6998,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proxycast"
|
||||
version = "0.75.0"
|
||||
version = "0.76.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arboard",
|
||||
@@ -6745,6 +7058,7 @@ dependencies = [
|
||||
"rusqlite",
|
||||
"rustls-pemfile 2.2.0",
|
||||
"scopeguard",
|
||||
"sentry",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
@@ -6785,7 +7099,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proxycast-agent"
|
||||
version = "0.75.0"
|
||||
version = "0.76.0"
|
||||
dependencies = [
|
||||
"aster-core",
|
||||
"async-trait",
|
||||
@@ -6809,7 +7123,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proxycast-config"
|
||||
version = "0.75.0"
|
||||
version = "0.76.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"parking_lot",
|
||||
@@ -6825,7 +7139,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proxycast-core"
|
||||
version = "0.75.0"
|
||||
version = "0.76.0"
|
||||
dependencies = [
|
||||
"aster-models",
|
||||
"async-trait",
|
||||
@@ -6865,7 +7179,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proxycast-credential"
|
||||
version = "0.75.0"
|
||||
version = "0.76.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"base64 0.22.1",
|
||||
@@ -6900,7 +7214,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proxycast-infra"
|
||||
version = "0.75.0"
|
||||
version = "0.76.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"dashmap 5.5.3",
|
||||
@@ -6920,7 +7234,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proxycast-mcp"
|
||||
version = "0.75.0"
|
||||
version = "0.76.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"glob",
|
||||
@@ -6951,7 +7265,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proxycast-processor"
|
||||
version = "0.75.0"
|
||||
version = "0.76.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"parking_lot",
|
||||
@@ -6970,7 +7284,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proxycast-providers"
|
||||
version = "0.75.0"
|
||||
version = "0.76.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
@@ -7022,7 +7336,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proxycast-server"
|
||||
version = "0.75.0"
|
||||
version = "0.76.0"
|
||||
dependencies = [
|
||||
"async-stream",
|
||||
"axum 0.7.9",
|
||||
@@ -7065,7 +7379,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proxycast-server-utils"
|
||||
version = "0.75.0"
|
||||
version = "0.76.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"futures",
|
||||
@@ -7080,7 +7394,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proxycast-services"
|
||||
version = "0.75.0"
|
||||
version = "0.76.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"aster-core",
|
||||
@@ -7121,7 +7435,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proxycast-skills"
|
||||
version = "0.75.0"
|
||||
version = "0.76.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"dirs 5.0.1",
|
||||
@@ -7137,7 +7451,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proxycast-terminal"
|
||||
version = "0.75.0"
|
||||
version = "0.76.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
@@ -7164,7 +7478,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proxycast-websocket"
|
||||
version = "0.75.0"
|
||||
version = "0.76.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -7813,6 +8127,12 @@ dependencies = [
|
||||
"ordered-multimap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
@@ -8147,7 +8467,7 @@ checksum = "0c37578180969d00692904465fb7f6b3d50b9a2b952b87c23d0e2e5cb5013416"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cssparser 0.29.6",
|
||||
"derive_more",
|
||||
"derive_more 0.99.20",
|
||||
"fxhash",
|
||||
"log",
|
||||
"phf 0.8.0",
|
||||
@@ -8165,7 +8485,7 @@ checksum = "4eb30575f3638fc8f6815f448d50cb1a2e255b0897985c8c59f4d37b72a07b06"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"cssparser 0.31.2",
|
||||
"derive_more",
|
||||
"derive_more 0.99.20",
|
||||
"fxhash",
|
||||
"log",
|
||||
"new_debug_unreachable",
|
||||
@@ -8207,6 +8527,127 @@ version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "sentry"
|
||||
version = "0.43.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc02afbba55340d6394968cb621384dbb9a0ad72d1b45e3e403099702a8bcf61"
|
||||
dependencies = [
|
||||
"httpdate",
|
||||
"native-tls",
|
||||
"reqwest 0.12.28",
|
||||
"sentry-actix",
|
||||
"sentry-backtrace",
|
||||
"sentry-contexts",
|
||||
"sentry-core",
|
||||
"sentry-debug-images",
|
||||
"sentry-panic",
|
||||
"sentry-tracing",
|
||||
"tokio",
|
||||
"ureq",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sentry-actix"
|
||||
version = "0.43.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5c9138631299216c4b85be2e54b9ca3c40e54dabeac4fa71ee4a680d536e083"
|
||||
dependencies = [
|
||||
"actix-http",
|
||||
"actix-web",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"sentry-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sentry-backtrace"
|
||||
version = "0.43.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f288792b92dfb3e8887554d2d11418863d5fabcd1cfad6756a8f88a5b963b76"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"regex",
|
||||
"sentry-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sentry-contexts"
|
||||
version = "0.43.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b28e6208b0f6e28a3d08a6e46b1127fefe75185decb969b8e1e0bf8cce6e06d"
|
||||
dependencies = [
|
||||
"hostname",
|
||||
"libc",
|
||||
"os_info",
|
||||
"rustc_version 0.4.1",
|
||||
"sentry-core",
|
||||
"uname",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sentry-core"
|
||||
version = "0.43.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67600a47b5aa005cb701454d56a289135a80654eebf1c1d9f076c3817a249bb8"
|
||||
dependencies = [
|
||||
"rand 0.9.2",
|
||||
"sentry-types",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sentry-debug-images"
|
||||
version = "0.43.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb27e7e12008f0c80446089efe050f3333a333aafc9eb16fe9800b7a183cf20c"
|
||||
dependencies = [
|
||||
"findshlibs",
|
||||
"sentry-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sentry-panic"
|
||||
version = "0.43.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce0bae293ecf6afaaa00dc63de3cc89b49ef774f06b9cf18dfd9c652009d3461"
|
||||
dependencies = [
|
||||
"sentry-backtrace",
|
||||
"sentry-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sentry-tracing"
|
||||
version = "0.43.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c044617771f460a3c2ca7cf8f516c3341ba886ca38d53cb550997d8bf636a3c"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"sentry-backtrace",
|
||||
"sentry-core",
|
||||
"tracing-core",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sentry-types"
|
||||
version = "0.43.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa369b6dc823ba5bddd69d16620f19229eb5df865b9483cf4dabb608891bb9c2"
|
||||
dependencies = [
|
||||
"debugid",
|
||||
"hex",
|
||||
"rand 0.9.2",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.18",
|
||||
"time",
|
||||
"url",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
@@ -10262,6 +10703,15 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uname"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unarray"
|
||||
version = "0.1.4"
|
||||
@@ -10366,6 +10816,12 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "unicode_categories"
|
||||
version = "0.1.1"
|
||||
@@ -10394,6 +10850,35 @@ version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "ureq"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdc97a28575b85cfedf2a7e7d3cc64b3e11bd8ac766666318003abbacc7a21fc"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"der",
|
||||
"log",
|
||||
"native-tls",
|
||||
"percent-encoding",
|
||||
"rustls-pki-types",
|
||||
"ureq-proto",
|
||||
"utf-8",
|
||||
"webpki-root-certs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ureq-proto"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"http 1.4.0",
|
||||
"httparse",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.8"
|
||||
@@ -10792,6 +11277,15 @@ dependencies = [
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-root-certs"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.25.4"
|
||||
|
||||
@@ -3,7 +3,7 @@ members = ["crates/*"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "0.75.0"
|
||||
version = "0.76.0"
|
||||
edition = "2021"
|
||||
authors = ["coso"]
|
||||
repository = "https://github.com/aiclientproxy/proxycast"
|
||||
@@ -50,6 +50,7 @@ thiserror = "1"
|
||||
# 日志
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = "0.3"
|
||||
sentry = "0.43"
|
||||
|
||||
# HTTP 服务器
|
||||
axum = { version = "0.7", features = ["ws"] }
|
||||
@@ -189,7 +190,7 @@ version = "2.4"
|
||||
|
||||
[package]
|
||||
name = "proxycast"
|
||||
version = "0.75.0"
|
||||
version = "0.76.0"
|
||||
description = "AI API Proxy Desktop App"
|
||||
authors = ["you"]
|
||||
edition = "2021"
|
||||
@@ -252,6 +253,7 @@ thiserror.workspace = true
|
||||
# 日志
|
||||
tracing.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
sentry.workspace = true
|
||||
|
||||
# HTTP 服务器
|
||||
axum.workspace = true
|
||||
|
||||
@@ -22,14 +22,14 @@ pub use path_utils::{collapse_tilde, contains_tilde, expand_tilde};
|
||||
pub use types::{
|
||||
generate_secure_api_key, AmpConfig, AmpModelMapping, ApiKeyEntry, AsrCredentialEntry,
|
||||
AsrProviderType, AssistantConfig, AssistantProfile, BaiduConfig, ChannelsConfig,
|
||||
ChatAppearanceConfig, Config, ContentCreatorConfig, ConversationSettings, CredentialEntry,
|
||||
CredentialPoolConfig, CustomProviderConfig, DeliveryConfig, EndpointProvidersConfig,
|
||||
ExperimentalFeatures, GeminiApiKeyEntry, HeartbeatExecutionMode, HeartbeatSecurityConfig,
|
||||
HeartbeatSettings, HintRouteSettingsEntry, HintRouterSettings, ImageGenConfig,
|
||||
InjectionRuleConfig, InjectionSettings, LoggingConfig, MemoryAutoConfig, MemoryConfig,
|
||||
MemoryProfileConfig, MemoryResolveConfig, MemorySourcesConfig, ModelInfo, ModelsConfig,
|
||||
NativeAgentConfig, NavigationConfig, OpenAIAsrConfig, PairingSettings, ProviderConfig,
|
||||
ProviderModelsConfig, ProvidersConfig, QuotaExceededConfig, RateLimitSettings,
|
||||
ChatAppearanceConfig, Config, ContentCreatorConfig, ConversationSettings, CrashReportingConfig,
|
||||
CredentialEntry, CredentialPoolConfig, CustomProviderConfig, DeliveryConfig,
|
||||
EndpointProvidersConfig, ExperimentalFeatures, GeminiApiKeyEntry, HeartbeatExecutionMode,
|
||||
HeartbeatSecurityConfig, HeartbeatSettings, HintRouteSettingsEntry, HintRouterSettings,
|
||||
ImageGenConfig, InjectionRuleConfig, InjectionSettings, LoggingConfig, MemoryAutoConfig,
|
||||
MemoryConfig, MemoryProfileConfig, MemoryResolveConfig, MemorySourcesConfig, ModelInfo,
|
||||
ModelsConfig, NativeAgentConfig, NavigationConfig, OpenAIAsrConfig, PairingSettings,
|
||||
ProviderConfig, ProviderModelsConfig, ProvidersConfig, QuotaExceededConfig, RateLimitSettings,
|
||||
RemoteManagementConfig, RetrySettings, RoutingConfig, ScreenshotChatConfig, SearchEngine,
|
||||
ServerConfig, TaskSchedule, TlsConfig, UpdateCheckConfig, UserProfile, VertexApiKeyEntry,
|
||||
VertexModelAlias, VoiceConfig, VoiceInputConfig, VoiceInstruction, VoiceOutputConfig,
|
||||
|
||||
@@ -429,6 +429,9 @@ pub struct Config {
|
||||
/// 速率限制配置
|
||||
#[serde(default)]
|
||||
pub rate_limit: RateLimitSettings,
|
||||
/// 崩溃上报配置(Sentry 协议兼容)
|
||||
#[serde(default)]
|
||||
pub crash_reporting: CrashReportingConfig,
|
||||
/// 对话管理配置
|
||||
#[serde(default)]
|
||||
pub conversation: ConversationSettings,
|
||||
@@ -1285,6 +1288,50 @@ pub struct LoggingConfig {
|
||||
pub include_request_body: bool,
|
||||
}
|
||||
|
||||
/// 崩溃上报配置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct CrashReportingConfig {
|
||||
/// 是否启用崩溃上报
|
||||
#[serde(default = "default_crash_reporting_enabled")]
|
||||
pub enabled: bool,
|
||||
/// Sentry DSN(为空时不发送远端)
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub dsn: Option<String>,
|
||||
/// 上报环境(如 prod/dev)
|
||||
#[serde(default = "default_crash_reporting_environment")]
|
||||
pub environment: String,
|
||||
/// 采样率(0.0 - 1.0)
|
||||
#[serde(default = "default_crash_reporting_sample_rate")]
|
||||
pub sample_rate: f64,
|
||||
/// 是否发送可能包含 PII 的默认字段
|
||||
#[serde(default)]
|
||||
pub send_pii: bool,
|
||||
}
|
||||
|
||||
fn default_crash_reporting_enabled() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn default_crash_reporting_environment() -> String {
|
||||
"production".to_string()
|
||||
}
|
||||
|
||||
fn default_crash_reporting_sample_rate() -> f64 {
|
||||
1.0
|
||||
}
|
||||
|
||||
impl Default for CrashReportingConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: default_crash_reporting_enabled(),
|
||||
dsn: None,
|
||||
environment: default_crash_reporting_environment(),
|
||||
sample_rate: default_crash_reporting_sample_rate(),
|
||||
send_pii: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn default_logging_enabled() -> bool {
|
||||
true
|
||||
}
|
||||
@@ -1788,6 +1835,7 @@ impl Default for Config {
|
||||
assistant: AssistantConfig::default(),
|
||||
user_profile: UserProfile::default(),
|
||||
rate_limit: RateLimitSettings::default(),
|
||||
crash_reporting: CrashReportingConfig::default(),
|
||||
conversation: ConversationSettings::default(),
|
||||
hint_router: HintRouterSettings::default(),
|
||||
pairing: PairingSettings::default(),
|
||||
@@ -2054,6 +2102,12 @@ pub struct ImageGenConfig {
|
||||
/// 自动下载生成的图像
|
||||
#[serde(default)]
|
||||
pub auto_download: Option<bool>,
|
||||
/// 图片搜索(Pexels)API Key
|
||||
#[serde(default)]
|
||||
pub image_search_pexels_api_key: Option<String>,
|
||||
/// 图片搜索(Pixabay)API Key
|
||||
#[serde(default)]
|
||||
pub image_search_pixabay_api_key: Option<String>,
|
||||
}
|
||||
|
||||
/// 助理配置
|
||||
@@ -2139,6 +2193,11 @@ mod unit_tests {
|
||||
assert_eq!(config.auth_dir, "~/.proxycast/auth");
|
||||
assert!(config.credential_pool.kiro.is_empty());
|
||||
assert!(config.credential_pool.openai.is_empty());
|
||||
assert!(config.crash_reporting.enabled);
|
||||
assert!(config.crash_reporting.dsn.is_none());
|
||||
assert_eq!(config.crash_reporting.environment, "production");
|
||||
assert_eq!(config.crash_reporting.sample_rate, 1.0);
|
||||
assert!(!config.crash_reporting.send_pii);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::app::types::{AppState, LogState};
|
||||
use crate::app::utils::is_valid_bind_host;
|
||||
use crate::config::{
|
||||
self,
|
||||
observer::{ConfigChangeEvent, RoutingChangeEvent},
|
||||
observer::{ConfigChangeEvent, FullReloadEvent, RoutingChangeEvent},
|
||||
ConfigChangeSource, GlobalConfigManagerState,
|
||||
};
|
||||
|
||||
@@ -21,6 +21,7 @@ pub async fn get_config(state: tauri::State<'_, AppState>) -> Result<config::Con
|
||||
#[tauri::command]
|
||||
pub async fn save_config(
|
||||
state: tauri::State<'_, AppState>,
|
||||
config_manager: tauri::State<'_, GlobalConfigManagerState>,
|
||||
config: config::Config,
|
||||
) -> Result<(), String> {
|
||||
let host = config.server.host.to_lowercase();
|
||||
@@ -46,17 +47,30 @@ pub async fn save_config(
|
||||
return Err("安全限制:不允许开启远程管理功能".to_string());
|
||||
}
|
||||
|
||||
let mut s = state.write().await;
|
||||
s.config = config.clone();
|
||||
{
|
||||
let mut s = state.write().await;
|
||||
s.config = config.clone();
|
||||
}
|
||||
|
||||
match config::save_config(&config) {
|
||||
let save_result = config::save_config(&config).map_err(|e| e.to_string());
|
||||
match save_result {
|
||||
Ok(()) => {
|
||||
let full_reload_event = ConfigChangeEvent::FullReload(FullReloadEvent {
|
||||
timestamp_ms: chrono::Utc::now().timestamp_millis() as u64,
|
||||
source: ConfigChangeSource::FrontendUI,
|
||||
});
|
||||
config_manager
|
||||
.0
|
||||
.subject()
|
||||
.notify_event(full_reload_event)
|
||||
.await;
|
||||
|
||||
tracing::info!("[CONFIG] 配置保存成功: host={}", config.server.host);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("[CONFIG] 配置保存失败: {}", e);
|
||||
Err(e.to_string())
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,26 @@
|
||||
|
||||
use crate::app::types::LogState;
|
||||
use crate::logger;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
/// 前端异常上报参数
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct FrontendCrashReport {
|
||||
pub message: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub name: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub stack: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub component: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub workflow_step: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub creation_mode: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub context: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
/// 获取日志
|
||||
#[tauri::command]
|
||||
@@ -17,3 +37,74 @@ pub async fn clear_logs(logs: tauri::State<'_, LogState>) -> Result<(), String>
|
||||
logs.write().await.clear();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 写入前端异常到本地日志并同步到崩溃上报后端
|
||||
#[tauri::command]
|
||||
pub async fn report_frontend_crash(
|
||||
logs: tauri::State<'_, LogState>,
|
||||
report: FrontendCrashReport,
|
||||
) -> Result<(), String> {
|
||||
let sanitized_message = logger::sanitize_log_message(&report.message);
|
||||
let sanitized_component = report
|
||||
.component
|
||||
.as_deref()
|
||||
.map(logger::sanitize_log_message)
|
||||
.unwrap_or_else(|| "unknown".to_string());
|
||||
let sanitized_step = report
|
||||
.workflow_step
|
||||
.as_deref()
|
||||
.map(logger::sanitize_log_message)
|
||||
.unwrap_or_else(|| "unknown".to_string());
|
||||
let sanitized_mode = report
|
||||
.creation_mode
|
||||
.as_deref()
|
||||
.map(logger::sanitize_log_message)
|
||||
.unwrap_or_else(|| "unknown".to_string());
|
||||
|
||||
let stack_preview = report
|
||||
.stack
|
||||
.as_deref()
|
||||
.map(logger::sanitize_log_message)
|
||||
.map(|stack| stack.lines().take(3).collect::<Vec<_>>().join(" | "))
|
||||
.unwrap_or_default();
|
||||
|
||||
logs.write().await.add(
|
||||
"error",
|
||||
&format!(
|
||||
"[FrontendCrash] component={sanitized_component} step={sanitized_step} mode={sanitized_mode} message={sanitized_message} stack={stack_preview}"
|
||||
),
|
||||
);
|
||||
|
||||
let mut merged_context = match report.context {
|
||||
Some(Value::Object(context)) => context,
|
||||
Some(other) => {
|
||||
let mut context = Map::new();
|
||||
context.insert("raw_context".to_string(), other);
|
||||
context
|
||||
}
|
||||
None => Map::new(),
|
||||
};
|
||||
|
||||
if let Some(name) = report.name.as_deref() {
|
||||
merged_context.insert(
|
||||
"error_name".to_string(),
|
||||
Value::String(logger::sanitize_log_message(name)),
|
||||
);
|
||||
}
|
||||
if let Some(stack) = report.stack.as_deref() {
|
||||
merged_context.insert(
|
||||
"error_stack".to_string(),
|
||||
Value::String(logger::sanitize_log_message(stack)),
|
||||
);
|
||||
}
|
||||
|
||||
crate::crash_reporting::capture_frontend_report(
|
||||
&sanitized_message,
|
||||
report.component.as_deref(),
|
||||
report.workflow_step.as_deref(),
|
||||
report.creation_mode.as_deref(),
|
||||
Some(Value::Object(merged_context)),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ pub fn run() {
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化崩溃上报(保持 guard 生命周期直到应用退出)
|
||||
let _crash_reporting_guard = crate::crash_reporting::init_from_config(&config);
|
||||
|
||||
// 初始化所有应用状态
|
||||
let states = match bootstrap::init_states(&config) {
|
||||
Ok(s) => s,
|
||||
@@ -765,6 +768,7 @@ pub fn run() {
|
||||
// Log commands (from app::commands)
|
||||
app_commands::get_logs,
|
||||
app_commands::clear_logs,
|
||||
app_commands::report_frontend_crash,
|
||||
// API test commands (from app::commands)
|
||||
app_commands::test_api,
|
||||
app_commands::get_available_models,
|
||||
@@ -1301,6 +1305,9 @@ pub fn run() {
|
||||
commands::material_cmd::get_material_content,
|
||||
commands::material_cmd::get_material_count,
|
||||
commands::material_cmd::get_materials_content,
|
||||
// Image search commands
|
||||
commands::image_search_cmd::search_pixabay_images,
|
||||
commands::image_search_cmd::search_web_images,
|
||||
// Video generation commands
|
||||
commands::video_generation_cmd::create_video_generation_task,
|
||||
commands::video_generation_cmd::get_video_generation_task,
|
||||
|
||||
@@ -0,0 +1,481 @@
|
||||
//! 图片搜索相关的 Tauri 命令
|
||||
//!
|
||||
//! 提供在线图片搜索功能,支持 Pixabay API 和联网搜索(Pexels)。
|
||||
|
||||
use crate::app::AppState;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tauri::State;
|
||||
|
||||
/// Pixabay 搜索请求
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PixabaySearchRequest {
|
||||
/// 搜索关键词
|
||||
pub query: String,
|
||||
/// 页码(从 1 开始)
|
||||
pub page: u32,
|
||||
/// 每页数量
|
||||
#[serde(alias = "per_page")]
|
||||
pub per_page: u32,
|
||||
/// 方向筛选:horizontal, vertical
|
||||
pub orientation: Option<String>,
|
||||
}
|
||||
|
||||
/// Pixabay 搜索响应
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PixabaySearchResponse {
|
||||
/// 总结果数
|
||||
pub total: u32,
|
||||
/// 总命中数
|
||||
pub total_hits: u32,
|
||||
/// 搜索结果
|
||||
pub hits: Vec<PixabayHit>,
|
||||
}
|
||||
|
||||
/// Pixabay 图片信息
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PixabayHit {
|
||||
/// 图片 ID
|
||||
pub id: u64,
|
||||
/// 预览图 URL
|
||||
pub preview_url: String,
|
||||
/// 大图 URL
|
||||
pub large_image_url: String,
|
||||
/// 图片宽度
|
||||
pub image_width: u32,
|
||||
/// 图片高度
|
||||
pub image_height: u32,
|
||||
/// 标签(逗号分隔)
|
||||
pub tags: String,
|
||||
/// 来源页面 URL
|
||||
pub page_url: String,
|
||||
/// 作者名称
|
||||
pub user: String,
|
||||
}
|
||||
|
||||
/// 联网图片搜索请求
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct WebImageSearchRequest {
|
||||
/// 搜索关键词
|
||||
pub query: String,
|
||||
/// 页码(从 1 开始)
|
||||
pub page: u32,
|
||||
/// 每页数量
|
||||
#[serde(alias = "per_page")]
|
||||
pub per_page: u32,
|
||||
/// 方向筛选:landscape, portrait, square
|
||||
pub aspect: Option<String>,
|
||||
}
|
||||
|
||||
/// 联网图片搜索响应(统一格式)
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct WebImageSearchResponse {
|
||||
/// 总结果数
|
||||
pub total: u32,
|
||||
/// 搜索结果
|
||||
pub hits: Vec<WebImageHit>,
|
||||
/// 来源提供者标识
|
||||
pub provider: String,
|
||||
}
|
||||
|
||||
/// 联网图片信息
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct WebImageHit {
|
||||
/// 图片 ID
|
||||
pub id: String,
|
||||
/// 缩略图 URL
|
||||
pub thumbnail_url: String,
|
||||
/// 原图 URL
|
||||
pub content_url: String,
|
||||
/// 图片宽度
|
||||
pub width: u32,
|
||||
/// 图片高度
|
||||
pub height: u32,
|
||||
/// 标题/描述
|
||||
pub name: String,
|
||||
/// 来源页面 URL
|
||||
pub host_page_url: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
struct PexelsSearchResponse {
|
||||
total_results: u32,
|
||||
photos: Vec<PexelsPhoto>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
struct PexelsPhoto {
|
||||
id: u64,
|
||||
width: u32,
|
||||
height: u32,
|
||||
url: String,
|
||||
alt: Option<String>,
|
||||
src: PexelsPhotoSrc,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
struct PexelsPhotoSrc {
|
||||
tiny: Option<String>,
|
||||
small: Option<String>,
|
||||
medium: Option<String>,
|
||||
large: Option<String>,
|
||||
large2x: Option<String>,
|
||||
landscape: Option<String>,
|
||||
portrait: Option<String>,
|
||||
original: Option<String>,
|
||||
}
|
||||
|
||||
/// 获取 Pixabay API Key(优先配置,其次环境变量)
|
||||
async fn get_pixabay_api_key(app_state: State<'_, AppState>) -> Option<String> {
|
||||
let key_from_config = {
|
||||
let state = app_state.read().await;
|
||||
state.config.image_gen.image_search_pixabay_api_key.clone()
|
||||
};
|
||||
|
||||
key_from_config
|
||||
.and_then(|key| {
|
||||
let trimmed = key.trim();
|
||||
if trimmed.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(trimmed.to_string())
|
||||
}
|
||||
})
|
||||
.or_else(|| {
|
||||
std::env::var("PIXABAY_API_KEY").ok().and_then(|key| {
|
||||
let trimmed = key.trim();
|
||||
if trimmed.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(trimmed.to_string())
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// 获取 Pexels API Key(优先配置,其次环境变量)
|
||||
async fn get_pexels_api_key(app_state: State<'_, AppState>) -> Option<String> {
|
||||
let key_from_config = {
|
||||
let state = app_state.read().await;
|
||||
state.config.image_gen.image_search_pexels_api_key.clone()
|
||||
};
|
||||
|
||||
key_from_config
|
||||
.and_then(|key| {
|
||||
let trimmed = key.trim();
|
||||
if trimmed.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(trimmed.to_string())
|
||||
}
|
||||
})
|
||||
.or_else(|| {
|
||||
std::env::var("PEXELS_API_KEY").ok().and_then(|key| {
|
||||
let trimmed = key.trim();
|
||||
if trimmed.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(trimmed.to_string())
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn map_aspect_to_pexels_orientation(aspect: Option<&str>) -> Option<&'static str> {
|
||||
match aspect.unwrap_or_default() {
|
||||
"landscape" => Some("landscape"),
|
||||
"portrait" => Some("portrait"),
|
||||
"square" => Some("square"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn map_pexels_to_web_response(resp: PexelsSearchResponse) -> WebImageSearchResponse {
|
||||
let hits = resp
|
||||
.photos
|
||||
.into_iter()
|
||||
.filter_map(|photo| {
|
||||
let content_url = photo
|
||||
.src
|
||||
.large2x
|
||||
.clone()
|
||||
.or(photo.src.large.clone())
|
||||
.or(photo.src.original.clone())
|
||||
.or(photo.src.landscape.clone())
|
||||
.or(photo.src.portrait.clone())
|
||||
.or(photo.src.medium.clone())
|
||||
.or(photo.src.small.clone())
|
||||
.or(photo.src.tiny.clone())?;
|
||||
let thumbnail_url = photo
|
||||
.src
|
||||
.medium
|
||||
.clone()
|
||||
.or(photo.src.small.clone())
|
||||
.or(photo.src.tiny.clone())
|
||||
.or(photo.src.landscape.clone())
|
||||
.or(photo.src.portrait.clone())
|
||||
.or(photo.src.large.clone())
|
||||
.or(photo.src.original.clone())
|
||||
.unwrap_or_else(|| content_url.clone());
|
||||
let name = photo
|
||||
.alt
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|name| !name.is_empty())
|
||||
.map(str::to_string)
|
||||
.unwrap_or_else(|| "Pexels Image".to_string());
|
||||
|
||||
Some(WebImageHit {
|
||||
id: photo.id.to_string(),
|
||||
thumbnail_url,
|
||||
content_url,
|
||||
width: photo.width,
|
||||
height: photo.height,
|
||||
name,
|
||||
host_page_url: photo.url,
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
WebImageSearchResponse {
|
||||
total: resp.total_results,
|
||||
hits,
|
||||
provider: "pexels".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// 搜索 Pixabay 图片
|
||||
///
|
||||
/// 通过 Pixabay API 搜索在线图片。
|
||||
///
|
||||
/// # 参数
|
||||
/// - `req`: 搜索请求,包含关键词、页码、每页数量等
|
||||
///
|
||||
/// # 返回
|
||||
/// - 成功返回搜索结果
|
||||
/// - 失败返回错误信息
|
||||
#[tauri::command]
|
||||
pub async fn search_pixabay_images(
|
||||
app_state: State<'_, AppState>,
|
||||
req: PixabaySearchRequest,
|
||||
) -> Result<PixabaySearchResponse, String> {
|
||||
let api_key = get_pixabay_api_key(app_state)
|
||||
.await
|
||||
.ok_or_else(|| "未配置 Pixabay API Key,请先在设置 → 系统 → 网络搜索中配置".to_string())?;
|
||||
|
||||
// 构建 URL
|
||||
let url = format!(
|
||||
"https://pixabay.com/api/?key={}&q={}&page={}&per_page={}&image_type=photo&safesearch=true",
|
||||
api_key,
|
||||
urlencoding::encode(&req.query),
|
||||
req.page,
|
||||
req.per_page
|
||||
);
|
||||
|
||||
// 添加 orientation 参数
|
||||
let url = if let Some(orientation) = &req.orientation {
|
||||
if !orientation.is_empty() {
|
||||
format!("{}&orientation={}", url, orientation)
|
||||
} else {
|
||||
url
|
||||
}
|
||||
} else {
|
||||
url
|
||||
};
|
||||
|
||||
// 发起请求
|
||||
let response = reqwest::get(&url)
|
||||
.await
|
||||
.map_err(|e| format!("请求失败: {}", e))?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
let status = response.status();
|
||||
let body = response.text().await.unwrap_or_default();
|
||||
let detail = serde_json::from_str::<serde_json::Value>(&body)
|
||||
.ok()
|
||||
.and_then(|value| {
|
||||
value
|
||||
.get("error")
|
||||
.and_then(|error| error.as_str())
|
||||
.map(str::to_string)
|
||||
})
|
||||
.unwrap_or_else(|| body.chars().take(120).collect::<String>());
|
||||
return Err(format!("Pixabay API 返回错误: HTTP {} {}", status, detail));
|
||||
}
|
||||
|
||||
let json: serde_json::Value = response
|
||||
.json()
|
||||
.await
|
||||
.map_err(|e| format!("解析响应失败: {}", e))?;
|
||||
|
||||
// 映射响应
|
||||
let total = json["total"].as_u64().map(|v| v as u32).unwrap_or(0);
|
||||
let total_hits = json["totalHits"].as_u64().map(|v| v as u32).unwrap_or(0);
|
||||
|
||||
let hits = json["hits"]
|
||||
.as_array()
|
||||
.unwrap_or(&vec![])
|
||||
.iter()
|
||||
.filter_map(|hit| {
|
||||
Some(PixabayHit {
|
||||
id: hit["id"].as_u64()? as u64,
|
||||
preview_url: hit["previewURL"].as_str()?.to_string(),
|
||||
large_image_url: hit["largeImageURL"].as_str()?.to_string(),
|
||||
image_width: hit["imageWidth"].as_u64()? as u32,
|
||||
image_height: hit["imageHeight"].as_u64()? as u32,
|
||||
tags: hit["tags"].as_str().unwrap_or("").to_string(),
|
||||
page_url: hit["pageURL"].as_str().unwrap_or("").to_string(),
|
||||
user: hit["user"].as_str().unwrap_or("").to_string(),
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(PixabaySearchResponse {
|
||||
total,
|
||||
total_hits,
|
||||
hits,
|
||||
})
|
||||
}
|
||||
|
||||
/// 联网搜索图片(Pexels)
|
||||
#[tauri::command]
|
||||
pub async fn search_web_images(
|
||||
app_state: State<'_, AppState>,
|
||||
req: WebImageSearchRequest,
|
||||
) -> Result<WebImageSearchResponse, String> {
|
||||
let api_key = get_pexels_api_key(app_state)
|
||||
.await
|
||||
.ok_or_else(|| "未配置 Pexels API Key,请先在设置 → 系统 → 网络搜索中配置".to_string())?;
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
let mut query = vec![
|
||||
("query", req.query.clone()),
|
||||
("page", req.page.to_string()),
|
||||
("per_page", req.per_page.to_string()),
|
||||
];
|
||||
|
||||
if let Some(orientation) = map_aspect_to_pexels_orientation(req.aspect.as_deref()) {
|
||||
query.push(("orientation", orientation.to_string()));
|
||||
}
|
||||
|
||||
let response = client
|
||||
.get("https://api.pexels.com/v1/search")
|
||||
.header("Authorization", api_key)
|
||||
.header(
|
||||
reqwest::header::USER_AGENT,
|
||||
"Mozilla/5.0 (compatible; ProxyCast/0.75; +https://github.com/aiclientproxy/proxycast)",
|
||||
)
|
||||
.header(reqwest::header::ACCEPT, "application/json")
|
||||
.query(&query)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| format!("请求 Pexels 失败: {e}"))?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
let status = response.status();
|
||||
if status.as_u16() == 401 || status.as_u16() == 403 {
|
||||
return Err("Pexels API Key 无效或无权限,请检查设置".to_string());
|
||||
}
|
||||
return Err(format!("Pexels API 返回错误: HTTP {status}"));
|
||||
}
|
||||
|
||||
let body: PexelsSearchResponse = response
|
||||
.json()
|
||||
.await
|
||||
.map_err(|e| format!("解析 Pexels 响应失败: {e}"))?;
|
||||
|
||||
Ok(map_pexels_to_web_response(body))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_pixabay_request_serialization() {
|
||||
let req = PixabaySearchRequest {
|
||||
query: "nature".to_string(),
|
||||
page: 1,
|
||||
per_page: 20,
|
||||
orientation: Some("horizontal".to_string()),
|
||||
};
|
||||
|
||||
let json = serde_json::to_string(&req).unwrap();
|
||||
assert!(json.contains("nature"));
|
||||
assert!(json.contains("horizontal"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_web_request_serialization() {
|
||||
let req = WebImageSearchRequest {
|
||||
query: "city".to_string(),
|
||||
page: 2,
|
||||
per_page: 30,
|
||||
aspect: Some("landscape".to_string()),
|
||||
};
|
||||
|
||||
let json = serde_json::to_string(&req).unwrap();
|
||||
assert!(json.contains("city"));
|
||||
assert!(json.contains("landscape"));
|
||||
assert!(json.contains("\"page\":2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map_aspect_to_pexels_orientation() {
|
||||
assert_eq!(
|
||||
map_aspect_to_pexels_orientation(Some("landscape")),
|
||||
Some("landscape")
|
||||
);
|
||||
assert_eq!(
|
||||
map_aspect_to_pexels_orientation(Some("portrait")),
|
||||
Some("portrait")
|
||||
);
|
||||
assert_eq!(
|
||||
map_aspect_to_pexels_orientation(Some("square")),
|
||||
Some("square")
|
||||
);
|
||||
assert_eq!(map_aspect_to_pexels_orientation(Some("all")), None);
|
||||
assert_eq!(map_aspect_to_pexels_orientation(None), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map_pexels_to_web_response() {
|
||||
let resp = PexelsSearchResponse {
|
||||
total_results: 2,
|
||||
photos: vec![PexelsPhoto {
|
||||
id: 123,
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
url: "https://www.pexels.com/photo/test".to_string(),
|
||||
alt: Some(" 城市夜景 ".to_string()),
|
||||
src: PexelsPhotoSrc {
|
||||
tiny: None,
|
||||
small: None,
|
||||
medium: Some("https://images.pexels.com/medium.jpg".to_string()),
|
||||
large: Some("https://images.pexels.com/large.jpg".to_string()),
|
||||
large2x: Some("https://images.pexels.com/large2x.jpg".to_string()),
|
||||
landscape: None,
|
||||
portrait: None,
|
||||
original: None,
|
||||
},
|
||||
}],
|
||||
};
|
||||
|
||||
let mapped = map_pexels_to_web_response(resp);
|
||||
assert_eq!(mapped.provider, "pexels");
|
||||
assert_eq!(mapped.total, 2);
|
||||
assert_eq!(mapped.hits.len(), 1);
|
||||
let hit = &mapped.hits[0];
|
||||
assert_eq!(hit.id, "123");
|
||||
assert_eq!(hit.name, "城市夜景");
|
||||
assert_eq!(hit.content_url, "https://images.pexels.com/large2x.jpg");
|
||||
assert_eq!(hit.thumbnail_url, "https://images.pexels.com/medium.jpg");
|
||||
assert_eq!(hit.width, 1920);
|
||||
assert_eq!(hit.height, 1080);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ pub mod external_tools_cmd;
|
||||
pub mod file_upload_cmd;
|
||||
pub mod general_chat_cmd;
|
||||
pub mod heartbeat_cmd;
|
||||
pub mod image_search_cmd;
|
||||
pub mod injection_cmd;
|
||||
pub mod kiro_local;
|
||||
pub mod machine_id_cmd;
|
||||
|
||||
@@ -8,7 +8,8 @@ mod tauri_observer;
|
||||
|
||||
// 从 proxycast-config crate 重新导出被使用的类型
|
||||
pub use proxycast_config::observer::events::{
|
||||
ConfigChangeEvent, ConfigChangeSource, EndpointProvidersChangeEvent, RoutingChangeEvent,
|
||||
ConfigChangeEvent, ConfigChangeSource, EndpointProvidersChangeEvent, FullReloadEvent,
|
||||
RoutingChangeEvent,
|
||||
};
|
||||
|
||||
// Tauri 相关实现
|
||||
|
||||
@@ -205,6 +205,7 @@ fn arb_config() -> impl Strategy<Value = Config> {
|
||||
pairing: proxycast_core::config::PairingSettings::default(),
|
||||
heartbeat: proxycast_core::config::HeartbeatSettings::default(),
|
||||
channels: proxycast_core::config::ChannelsConfig::default(),
|
||||
crash_reporting: proxycast_core::config::CrashReportingConfig::default(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -458,6 +459,7 @@ fn arb_valid_config() -> impl Strategy<Value = Config> {
|
||||
pairing: proxycast_core::config::PairingSettings::default(),
|
||||
heartbeat: proxycast_core::config::HeartbeatSettings::default(),
|
||||
channels: proxycast_core::config::ChannelsConfig::default(),
|
||||
crash_reporting: proxycast_core::config::CrashReportingConfig::default(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -522,6 +524,7 @@ fn arb_invalid_config() -> impl Strategy<Value = Config> {
|
||||
pairing: proxycast_core::config::PairingSettings::default(),
|
||||
heartbeat: proxycast_core::config::HeartbeatSettings::default(),
|
||||
channels: proxycast_core::config::ChannelsConfig::default(),
|
||||
crash_reporting: proxycast_core::config::CrashReportingConfig::default(),
|
||||
};
|
||||
// 根据类型使配置无效
|
||||
match invalid_type {
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
//! 崩溃上报初始化与上报辅助(Sentry 协议兼容)
|
||||
|
||||
use proxycast_core::config::{Config, CrashReportingConfig};
|
||||
use serde_json::Value;
|
||||
|
||||
/// 根据配置初始化 Sentry 客户端
|
||||
pub fn init_from_config(config: &Config) -> Option<sentry::ClientInitGuard> {
|
||||
init(&config.crash_reporting)
|
||||
}
|
||||
|
||||
fn init(config: &CrashReportingConfig) -> Option<sentry::ClientInitGuard> {
|
||||
if !config.enabled {
|
||||
tracing::info!("[CrashReporting] 已禁用崩溃上报");
|
||||
return None;
|
||||
}
|
||||
|
||||
let dsn = config
|
||||
.dsn
|
||||
.as_ref()
|
||||
.map(|value| value.trim())
|
||||
.filter(|value| !value.is_empty())?;
|
||||
|
||||
let sample_rate = config.sample_rate.clamp(0.0, 1.0) as f32;
|
||||
let environment = config.environment.trim();
|
||||
let environment = if environment.is_empty() {
|
||||
"production"
|
||||
} else {
|
||||
environment
|
||||
};
|
||||
|
||||
let guard = sentry::init((
|
||||
dsn,
|
||||
sentry::ClientOptions {
|
||||
release: sentry::release_name!(),
|
||||
environment: Some(environment.to_string().into()),
|
||||
sample_rate,
|
||||
send_default_pii: config.send_pii,
|
||||
attach_stacktrace: true,
|
||||
..Default::default()
|
||||
},
|
||||
));
|
||||
|
||||
sentry::configure_scope(|scope| {
|
||||
scope.set_tag("platform", std::env::consts::OS);
|
||||
scope.set_tag("arch", std::env::consts::ARCH);
|
||||
scope.set_tag("app", "proxycast");
|
||||
scope.set_tag("app_version", env!("CARGO_PKG_VERSION"));
|
||||
});
|
||||
|
||||
tracing::info!(
|
||||
"[CrashReporting] Sentry 已初始化: env={}, sample_rate={}",
|
||||
environment,
|
||||
sample_rate
|
||||
);
|
||||
|
||||
Some(guard)
|
||||
}
|
||||
|
||||
/// 记录来自前端的崩溃/异常事件(在已初始化时发送到 Sentry)
|
||||
pub fn capture_frontend_report(
|
||||
message: &str,
|
||||
component: Option<&str>,
|
||||
workflow_step: Option<&str>,
|
||||
creation_mode: Option<&str>,
|
||||
metadata: Option<Value>,
|
||||
) {
|
||||
if message.trim().is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
sentry::with_scope(
|
||||
|scope| {
|
||||
scope.set_tag("origin", "frontend");
|
||||
if let Some(component_name) = component.filter(|value| !value.trim().is_empty()) {
|
||||
scope.set_tag("component", component_name.to_string());
|
||||
}
|
||||
if let Some(step) = workflow_step.filter(|value| !value.trim().is_empty()) {
|
||||
scope.set_tag("workflow_step", step.to_string());
|
||||
}
|
||||
if let Some(mode) = creation_mode.filter(|value| !value.trim().is_empty()) {
|
||||
scope.set_tag("creation_mode", mode.to_string());
|
||||
}
|
||||
if let Some(extra) = metadata {
|
||||
scope.set_extra("frontend_report", sentry::protocol::Value::from(extra));
|
||||
}
|
||||
},
|
||||
|| {
|
||||
sentry::capture_message(message, sentry::Level::Error);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -46,6 +46,7 @@ pub mod voice;
|
||||
// 内部模块
|
||||
mod commands;
|
||||
mod config;
|
||||
mod crash_reporting;
|
||||
mod data;
|
||||
#[cfg(debug_assertions)]
|
||||
#[allow(dead_code)]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "ProxyCast",
|
||||
"version": "0.75.0",
|
||||
"version": "0.76.0",
|
||||
"identifier": "com.proxycast.app",
|
||||
"build": {
|
||||
"beforeDevCommand": "npm run dev",
|
||||
|
||||
Reference in New Issue
Block a user