diff --git a/.gitignore b/.gitignore
index 15fce72a1..2b01a18b4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,8 @@ node_modules/
# Build
dist/
src-tauri/target/
+*.exe
+*.pdb
# IDE
.vscode/
diff --git a/package-lock.json b/package-lock.json
index 883b7749d..27a32e53b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,7 @@
"name": "proxycast",
"version": "0.18.0",
"dependencies": {
+ "@fabianlars/tauri-plugin-oauth": "^2",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-label": "^2.1.0",
@@ -17,16 +18,18 @@
"@radix-ui/react-tabs": "^1.1.1",
"@radix-ui/react-toast": "^1.2.2",
"@radix-ui/react-tooltip": "^1.1.3",
- "@tauri-apps/api": "^2.0.0",
+ "@tauri-apps/api": "^2.9.1",
"@tauri-apps/plugin-dialog": "^2.4.2",
"@tauri-apps/plugin-shell": "^2.0.0",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
+ "date-fns": "^4.1.0",
"lucide-react": "^0.460.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
+ "react-router-dom": "^7.11.0",
"sonner": "^2.0.7",
- "tailwind-merge": "^2.5.4"
+ "tailwind-merge": "^2.6.0"
},
"devDependencies": {
"@eslint/js": "^9.15.0",
@@ -1191,6 +1194,15 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
+ "node_modules/@fabianlars/tauri-plugin-oauth": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@fabianlars/tauri-plugin-oauth/-/tauri-plugin-oauth-2.0.0.tgz",
+ "integrity": "sha512-I1s08ZXrsFuYfNWusAcpLyiCfr5TCvaBrRuKfTG+XQrcaqnAcwjdWH0U5J9QWuMDLwCUMnVxdobtMJzPR8raxQ==",
+ "license": "MIT OR Apache-2.0",
+ "dependencies": {
+ "@tauri-apps/api": "^2.0.3"
+ }
+ },
"node_modules/@fast-check/vitest": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/@fast-check/vitest/-/vitest-0.2.4.tgz",
@@ -4072,6 +4084,19 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/cookie": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
+ "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
"node_modules/cosmiconfig": {
"version": "8.3.6",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz",
@@ -4177,6 +4202,16 @@
"node": ">=20"
}
},
+ "node_modules/date-fns": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
+ "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/kossnocorp"
+ }
+ },
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@@ -5954,6 +5989,44 @@
}
}
},
+ "node_modules/react-router": {
+ "version": "7.11.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.11.0.tgz",
+ "integrity": "sha512-uI4JkMmjbWCZc01WVP2cH7ZfSzH91JAZUDd7/nIprDgWxBV1TkkmLToFh7EbMTcMak8URFRa2YoBL/W8GWnCTQ==",
+ "license": "MIT",
+ "dependencies": {
+ "cookie": "^1.0.1",
+ "set-cookie-parser": "^2.6.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "7.11.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.11.0.tgz",
+ "integrity": "sha512-e49Ir/kMGRzFOOrYQBdoitq3ULigw4lKbAyKusnvtDu2t4dBX4AGYPrzNvorXmVuOyeakai6FUPW5MmibvVG8g==",
+ "license": "MIT",
+ "dependencies": {
+ "react-router": "7.11.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ }
+ },
"node_modules/react-style-singleton": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
@@ -6156,6 +6229,12 @@
"semver": "bin/semver.js"
}
},
+ "node_modules/set-cookie-parser": {
+ "version": "2.7.2",
+ "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
+ "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
+ "license": "MIT"
+ },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
diff --git a/package.json b/package.json
index 128ecae4b..26a167e36 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "proxycast",
"private": true,
- "version": "0.18.0",
+ "version": "0.19.0",
"type": "module",
"repository": {
"type": "git",
@@ -29,16 +29,19 @@
"@radix-ui/react-tabs": "^1.1.1",
"@radix-ui/react-toast": "^1.2.2",
"@radix-ui/react-tooltip": "^1.1.3",
- "@tauri-apps/api": "^2.0.0",
+ "@tauri-apps/api": "^2.9.1",
"@tauri-apps/plugin-dialog": "^2.4.2",
"@tauri-apps/plugin-shell": "^2.0.0",
+ "@fabianlars/tauri-plugin-oauth": "^2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
+ "date-fns": "^4.1.0",
"lucide-react": "^0.460.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
+ "react-router-dom": "^7.11.0",
"sonner": "^2.0.7",
- "tailwind-merge": "^2.5.4"
+ "tailwind-merge": "^2.6.0"
},
"devDependencies": {
"@eslint/js": "^9.15.0",
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index 87e231041..345462805 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -70,6 +70,26 @@ version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
+[[package]]
+name = "arboard"
+version = "3.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf"
+dependencies = [
+ "clipboard-win",
+ "image",
+ "log",
+ "objc2",
+ "objc2-app-kit",
+ "objc2-core-foundation",
+ "objc2-core-graphics",
+ "objc2-foundation",
+ "parking_lot",
+ "percent-encoding",
+ "windows-sys 0.60.2",
+ "x11rb",
+]
+
[[package]]
name = "arc-swap"
version = "1.7.1"
@@ -109,6 +129,79 @@ dependencies = [
"pin-project-lite",
]
+[[package]]
+name = "async-channel"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2"
+dependencies = [
+ "concurrent-queue",
+ "event-listener-strategy",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-executor"
+version = "1.13.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8"
+dependencies = [
+ "async-task",
+ "concurrent-queue",
+ "fastrand",
+ "futures-lite",
+ "pin-project-lite",
+ "slab",
+]
+
+[[package]]
+name = "async-io"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "concurrent-queue",
+ "futures-io",
+ "futures-lite",
+ "parking",
+ "polling",
+ "rustix",
+ "slab",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "async-lock"
+version = "3.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311"
+dependencies = [
+ "event-listener",
+ "event-listener-strategy",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-process"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75"
+dependencies = [
+ "async-channel",
+ "async-io",
+ "async-lock",
+ "async-signal",
+ "async-task",
+ "blocking",
+ "cfg-if",
+ "event-listener",
+ "futures-lite",
+ "rustix",
+]
+
[[package]]
name = "async-recursion"
version = "1.1.1"
@@ -120,6 +213,24 @@ dependencies = [
"syn 2.0.111",
]
+[[package]]
+name = "async-signal"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c"
+dependencies = [
+ "async-io",
+ "async-lock",
+ "atomic-waker",
+ "cfg-if",
+ "futures-core",
+ "futures-io",
+ "rustix",
+ "signal-hook-registry",
+ "slab",
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "async-stream"
version = "0.3.6"
@@ -142,6 +253,12 @@ dependencies = [
"syn 2.0.111",
]
+[[package]]
+name = "async-task"
+version = "4.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
+
[[package]]
name = "async-trait"
version = "0.1.89"
@@ -364,6 +481,12 @@ dependencies = [
"serde_core",
]
+[[package]]
+name = "block"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
+
[[package]]
name = "block-buffer"
version = "0.10.4"
@@ -382,6 +505,19 @@ dependencies = [
"objc2",
]
+[[package]]
+name = "blocking"
+version = "1.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21"
+dependencies = [
+ "async-channel",
+ "async-task",
+ "futures-io",
+ "futures-lite",
+ "piper",
+]
+
[[package]]
name = "brotli"
version = "8.0.2"
@@ -609,6 +745,15 @@ dependencies = [
"inout",
]
+[[package]]
+name = "clipboard-win"
+version = "5.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4"
+dependencies = [
+ "error-code",
+]
+
[[package]]
name = "cmake"
version = "0.1.56"
@@ -618,6 +763,35 @@ dependencies = [
"cc",
]
+[[package]]
+name = "cocoa"
+version = "0.26.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad36507aeb7e16159dfe68db81ccc27571c3ccd4b76fb2fb72fc59e7a4b1b64c"
+dependencies = [
+ "bitflags 2.10.0",
+ "block",
+ "cocoa-foundation",
+ "core-foundation 0.10.1",
+ "core-graphics",
+ "foreign-types 0.5.0",
+ "libc",
+ "objc",
+]
+
+[[package]]
+name = "cocoa-foundation"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81411967c50ee9a1fc11365f8c585f863a22a9697c89239c452292c40ba79b0d"
+dependencies = [
+ "bitflags 2.10.0",
+ "block",
+ "core-foundation 0.10.1",
+ "core-graphics-types",
+ "objc",
+]
+
[[package]]
name = "combine"
version = "4.6.7"
@@ -637,6 +811,26 @@ dependencies = [
"crossbeam-utils",
]
+[[package]]
+name = "const-random"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
+dependencies = [
+ "const-random-macro",
+]
+
+[[package]]
+name = "const-random-macro"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
+dependencies = [
+ "getrandom 0.2.16",
+ "once_cell",
+ "tiny-keccak",
+]
+
[[package]]
name = "constant_time_eq"
version = "0.1.5"
@@ -742,6 +936,12 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+[[package]]
+name = "crunchy"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
+
[[package]]
name = "crypto-common"
version = "0.1.7"
@@ -1000,6 +1200,15 @@ dependencies = [
"syn 2.0.111",
]
+[[package]]
+name = "dlv-list"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f"
+dependencies = [
+ "const-random",
+]
+
[[package]]
name = "downcast-rs"
version = "1.2.1"
@@ -1125,6 +1334,12 @@ dependencies = [
"windows-sys 0.61.2",
]
+[[package]]
+name = "error-code"
+version = "3.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"
+
[[package]]
name = "event-listener"
version = "5.4.1"
@@ -1544,6 +1759,16 @@ dependencies = [
"version_check",
]
+[[package]]
+name = "gethostname"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8"
+dependencies = [
+ "rustix",
+ "windows-link 0.2.1",
+]
+
[[package]]
name = "getrandom"
version = "0.1.16"
@@ -1787,6 +2012,12 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+[[package]]
+name = "hermit-abi"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
+
[[package]]
name = "hex"
version = "0.4.3"
@@ -1937,7 +2168,7 @@ dependencies = [
"tokio",
"tower-service",
"tracing",
- "windows-registry",
+ "windows-registry 0.6.1",
]
[[package]]
@@ -2092,6 +2323,7 @@ dependencies = [
"byteorder-lite",
"num-traits",
"png",
+ "tiff",
]
[[package]]
@@ -2251,6 +2483,12 @@ dependencies = [
"libc",
]
+[[package]]
+name = "jpeg-decoder"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07"
+
[[package]]
name = "js-sys"
version = "0.3.83"
@@ -2437,6 +2675,15 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
+[[package]]
+name = "malloc_buf"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "markup5ever"
version = "0.14.1"
@@ -2691,6 +2938,15 @@ dependencies = [
"syn 2.0.111",
]
+[[package]]
+name = "objc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
+dependencies = [
+ "malloc_buf",
+]
+
[[package]]
name = "objc2"
version = "0.6.3"
@@ -2971,6 +3227,16 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
+[[package]]
+name = "ordered-multimap"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79"
+dependencies = [
+ "dlv-list",
+ "hashbrown 0.14.5",
+]
+
[[package]]
name = "ordered-stream"
version = "0.2.0"
@@ -3226,6 +3492,17 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+[[package]]
+name = "piper"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066"
+dependencies = [
+ "atomic-waker",
+ "fastrand",
+ "futures-io",
+]
+
[[package]]
name = "pkg-config"
version = "0.3.32"
@@ -3258,6 +3535,20 @@ dependencies = [
"miniz_oxide",
]
+[[package]]
+name = "polling"
+version = "3.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218"
+dependencies = [
+ "cfg-if",
+ "concurrent-queue",
+ "hermit-abi",
+ "pin-project-lite",
+ "rustix",
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "potential_utf"
version = "0.1.4"
@@ -3377,9 +3668,10 @@ dependencies = [
[[package]]
name = "proxycast"
-version = "0.18.0"
+version = "0.19.0"
dependencies = [
"anyhow",
+ "arboard",
"async-stream",
"async-trait",
"axum",
@@ -3387,6 +3679,7 @@ dependencies = [
"base64 0.22.1",
"bytes",
"chrono",
+ "cocoa",
"dashmap",
"dirs 5.0.1",
"flate2",
@@ -3395,6 +3688,7 @@ dependencies = [
"indexmap 2.12.1",
"md5",
"notify",
+ "objc",
"once_cell",
"open",
"parking_lot",
@@ -3413,8 +3707,10 @@ dependencies = [
"tauri",
"tauri-build",
"tauri-plugin-autostart",
+ "tauri-plugin-deep-link",
"tauri-plugin-dialog",
"tauri-plugin-shell",
+ "tauri-plugin-single-instance",
"tempfile",
"thiserror 1.0.69",
"tiktoken-rs",
@@ -3427,6 +3723,8 @@ dependencies = [
"url",
"urlencoding",
"uuid",
+ "winapi",
+ "windows 0.56.0",
"zip",
]
@@ -3770,6 +4068,16 @@ dependencies = [
"smallvec",
]
+[[package]]
+name = "rust-ini"
+version = "0.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7"
+dependencies = [
+ "cfg-if",
+ "ordered-multimap",
+]
+
[[package]]
name = "rustc-hash"
version = "1.1.0"
@@ -4528,7 +4836,7 @@ dependencies = [
"tao-macros",
"unicode-segmentation",
"url",
- "windows",
+ "windows 0.61.3",
"windows-core 0.61.2",
"windows-version",
"x11-dl",
@@ -4600,7 +4908,7 @@ dependencies = [
"webkit2gtk",
"webview2-com",
"window-vibrancy",
- "windows",
+ "windows 0.61.3",
]
[[package]]
@@ -4697,6 +5005,27 @@ dependencies = [
"thiserror 2.0.17",
]
+[[package]]
+name = "tauri-plugin-deep-link"
+version = "2.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e82759f7c7d51de3cbde51c04b3f2332de52436ed84541182cd8944b04e9e73"
+dependencies = [
+ "dunce",
+ "plist",
+ "rust-ini",
+ "serde",
+ "serde_json",
+ "tauri",
+ "tauri-plugin",
+ "tauri-utils",
+ "thiserror 2.0.17",
+ "tracing",
+ "url",
+ "windows-registry 0.5.3",
+ "windows-result 0.3.4",
+]
+
[[package]]
name = "tauri-plugin-dialog"
version = "2.4.2"
@@ -4758,6 +5087,21 @@ dependencies = [
"tokio",
]
+[[package]]
+name = "tauri-plugin-single-instance"
+version = "2.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd707f8c86b4e3004e2c141fa24351f1909ba40ce1b8437e30d5ed5277dd3710"
+dependencies = [
+ "serde",
+ "serde_json",
+ "tauri",
+ "thiserror 2.0.17",
+ "tracing",
+ "windows-sys 0.60.2",
+ "zbus",
+]
+
[[package]]
name = "tauri-runtime"
version = "2.9.2"
@@ -4780,7 +5124,7 @@ dependencies = [
"url",
"webkit2gtk",
"webview2-com",
- "windows",
+ "windows 0.61.3",
]
[[package]]
@@ -4806,7 +5150,7 @@ dependencies = [
"url",
"webkit2gtk",
"webview2-com",
- "windows",
+ "windows 0.61.3",
"wry",
]
@@ -4932,6 +5276,17 @@ dependencies = [
"cfg-if",
]
+[[package]]
+name = "tiff"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
+dependencies = [
+ "flate2",
+ "jpeg-decoder",
+ "weezl",
+]
+
[[package]]
name = "tiktoken-rs"
version = "0.6.0"
@@ -4979,6 +5334,15 @@ dependencies = [
"time-core",
]
+[[package]]
+name = "tiny-keccak"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
+dependencies = [
+ "crunchy",
+]
+
[[package]]
name = "tinystr"
version = "0.8.2"
@@ -5770,10 +6134,10 @@ checksum = "d4ba622a989277ef3886dd5afb3e280e3dd6d974b766118950a08f8f678ad6a4"
dependencies = [
"webview2-com-macros",
"webview2-com-sys",
- "windows",
+ "windows 0.61.3",
"windows-core 0.61.2",
- "windows-implement",
- "windows-interface",
+ "windows-implement 0.60.2",
+ "windows-interface 0.59.3",
]
[[package]]
@@ -5794,10 +6158,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36695906a1b53a3bf5c4289621efedac12b73eeb0b89e7e1a89b517302d5d75c"
dependencies = [
"thiserror 2.0.17",
- "windows",
+ "windows 0.61.3",
"windows-core 0.61.2",
]
+[[package]]
+name = "weezl"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
+
[[package]]
name = "winapi"
version = "0.3.9"
@@ -5844,6 +6214,16 @@ dependencies = [
"windows-version",
]
+[[package]]
+name = "windows"
+version = "0.56.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132"
+dependencies = [
+ "windows-core 0.56.0",
+ "windows-targets 0.52.6",
+]
+
[[package]]
name = "windows"
version = "0.61.3"
@@ -5866,14 +6246,26 @@ dependencies = [
"windows-core 0.61.2",
]
+[[package]]
+name = "windows-core"
+version = "0.56.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6"
+dependencies = [
+ "windows-implement 0.56.0",
+ "windows-interface 0.56.0",
+ "windows-result 0.1.2",
+ "windows-targets 0.52.6",
+]
+
[[package]]
name = "windows-core"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
dependencies = [
- "windows-implement",
- "windows-interface",
+ "windows-implement 0.60.2",
+ "windows-interface 0.59.3",
"windows-link 0.1.3",
"windows-result 0.3.4",
"windows-strings 0.4.2",
@@ -5885,8 +6277,8 @@ version = "0.62.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
dependencies = [
- "windows-implement",
- "windows-interface",
+ "windows-implement 0.60.2",
+ "windows-interface 0.59.3",
"windows-link 0.2.1",
"windows-result 0.4.1",
"windows-strings 0.5.1",
@@ -5903,6 +6295,17 @@ dependencies = [
"windows-threading",
]
+[[package]]
+name = "windows-implement"
+version = "0.56.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.111",
+]
+
[[package]]
name = "windows-implement"
version = "0.60.2"
@@ -5914,6 +6317,17 @@ dependencies = [
"syn 2.0.111",
]
+[[package]]
+name = "windows-interface"
+version = "0.56.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.111",
+]
+
[[package]]
name = "windows-interface"
version = "0.59.3"
@@ -5947,6 +6361,17 @@ dependencies = [
"windows-link 0.1.3",
]
+[[package]]
+name = "windows-registry"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e"
+dependencies = [
+ "windows-link 0.1.3",
+ "windows-result 0.3.4",
+ "windows-strings 0.4.2",
+]
+
[[package]]
name = "windows-registry"
version = "0.6.1"
@@ -5958,6 +6383,15 @@ dependencies = [
"windows-strings 0.5.1",
]
+[[package]]
+name = "windows-result"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
[[package]]
name = "windows-result"
version = "0.3.4"
@@ -6397,7 +6831,7 @@ dependencies = [
"webkit2gtk",
"webkit2gtk-sys",
"webview2-com",
- "windows",
+ "windows 0.61.3",
"windows-core 0.61.2",
"windows-version",
"x11-dl",
@@ -6424,6 +6858,23 @@ dependencies = [
"pkg-config",
]
+[[package]]
+name = "x11rb"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414"
+dependencies = [
+ "gethostname",
+ "rustix",
+ "x11rb-protocol",
+]
+
+[[package]]
+name = "x11rb-protocol"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
+
[[package]]
name = "yoke"
version = "0.8.1"
@@ -6454,8 +6905,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b622b18155f7a93d1cd2dc8c01d2d6a44e08fb9ebb7b3f9e6ed101488bad6c91"
dependencies = [
"async-broadcast",
+ "async-executor",
+ "async-io",
+ "async-lock",
+ "async-process",
"async-recursion",
+ "async-task",
"async-trait",
+ "blocking",
"enumflags2",
"event-listener",
"futures-core",
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index 410c96a5d..c50a1dccf 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "proxycast"
-version = "0.18.0"
+version = "0.19.0"
description = "AI API Proxy Desktop App"
authors = ["you"]
edition = "2021"
@@ -19,6 +19,7 @@ tauri = { version = "2", features = ["tray-icon", "image-png"] }
tauri-plugin-shell = "2"
tauri-plugin-autostart = "2"
tauri-plugin-dialog = "2"
+tauri-plugin-single-instance = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["full"] }
@@ -61,6 +62,39 @@ open = "5"
url = "2"
once_cell = "1"
tokio-util = "0.7"
+arboard = "3"
+
+# Platform specific dependencies for browser interceptor
+
+# Windows specific dependencies for browser interceptor
+[target.'cfg(windows)'.dependencies]
+windows = { version = "0.56", features = [
+ "Win32_Foundation",
+ "Win32_System_Registry",
+ "Win32_System_Threading",
+ "Win32_System_ProcessStatus",
+ "Win32_UI_Shell",
+ "Win32_UI_WindowsAndMessaging",
+ "Win32_System_LibraryLoader",
+ "Win32_System_Memory",
+ "Win32_System_Diagnostics_ToolHelp",
+ "Win32_Security",
+] }
+winapi = { version = "0.3", features = [
+ "winuser",
+ "winreg",
+ "processthreadsapi",
+ "handleapi",
+ "shellapi",
+ "psapi",
+ "tlhelp32",
+] }
+
+# macOS specific dependencies for browser interceptor
+[target.'cfg(target_os = "macos")'.dependencies]
+cocoa = "0.26"
+objc = "0.2"
+tauri-plugin-deep-link = "2"
[dev-dependencies]
proptest = "1"
diff --git a/src-tauri/Info.plist b/src-tauri/Info.plist
new file mode 100644
index 000000000..ef33f4c57
--- /dev/null
+++ b/src-tauri/Info.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ CFBundleURLTypes
+
+
+ CFBundleURLName
+ Web URL
+ CFBundleURLSchemes
+
+ http
+ https
+
+
+
+ CFBundleURLName
+ ProxyCast URL
+ CFBundleURLSchemes
+
+ proxycast
+
+
+
+
+
diff --git a/src-tauri/gen/schemas/acl-manifests.json b/src-tauri/gen/schemas/acl-manifests.json
index 7f023fd90..17f497fad 100644
--- a/src-tauri/gen/schemas/acl-manifests.json
+++ b/src-tauri/gen/schemas/acl-manifests.json
@@ -1 +1 @@
-{"autostart":{"default_permission":{"identifier":"default","description":"This permission set configures if your\napplication can enable or disable auto\nstarting the application on boot.\n\n#### Granted Permissions\n\nIt allows all to check, enable and\ndisable the automatic start on boot.\n\n","permissions":["allow-enable","allow-disable","allow-is-enabled"]},"permissions":{"allow-disable":{"identifier":"allow-disable","description":"Enables the disable command without any pre-configured scope.","commands":{"allow":["disable"],"deny":[]}},"allow-enable":{"identifier":"allow-enable","description":"Enables the enable command without any pre-configured scope.","commands":{"allow":["enable"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"deny-disable":{"identifier":"deny-disable","description":"Denies the disable command without any pre-configured scope.","commands":{"allow":[],"deny":["disable"]}},"deny-enable":{"identifier":"deny-enable","description":"Denies the enable command without any pre-configured scope.","commands":{"allow":[],"deny":["enable"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}}},"permission_sets":{},"global_scope_schema":null},"core":{"default_permission":{"identifier":"default","description":"Default core plugins set.","permissions":["core:path:default","core:event:default","core:window:default","core:webview:default","core:app:default","core:image:default","core:resources:default","core:menu:default","core:tray:default"]},"permissions":{},"permission_sets":{},"global_scope_schema":null},"core:app":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-version","allow-name","allow-tauri-version","allow-identifier","allow-bundle-type","allow-register-listener","allow-remove-listener"]},"permissions":{"allow-app-hide":{"identifier":"allow-app-hide","description":"Enables the app_hide command without any pre-configured scope.","commands":{"allow":["app_hide"],"deny":[]}},"allow-app-show":{"identifier":"allow-app-show","description":"Enables the app_show command without any pre-configured scope.","commands":{"allow":["app_show"],"deny":[]}},"allow-bundle-type":{"identifier":"allow-bundle-type","description":"Enables the bundle_type command without any pre-configured scope.","commands":{"allow":["bundle_type"],"deny":[]}},"allow-default-window-icon":{"identifier":"allow-default-window-icon","description":"Enables the default_window_icon command without any pre-configured scope.","commands":{"allow":["default_window_icon"],"deny":[]}},"allow-fetch-data-store-identifiers":{"identifier":"allow-fetch-data-store-identifiers","description":"Enables the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":["fetch_data_store_identifiers"],"deny":[]}},"allow-identifier":{"identifier":"allow-identifier","description":"Enables the identifier command without any pre-configured scope.","commands":{"allow":["identifier"],"deny":[]}},"allow-name":{"identifier":"allow-name","description":"Enables the name command without any pre-configured scope.","commands":{"allow":["name"],"deny":[]}},"allow-register-listener":{"identifier":"allow-register-listener","description":"Enables the register_listener command without any pre-configured scope.","commands":{"allow":["register_listener"],"deny":[]}},"allow-remove-data-store":{"identifier":"allow-remove-data-store","description":"Enables the remove_data_store command without any pre-configured scope.","commands":{"allow":["remove_data_store"],"deny":[]}},"allow-remove-listener":{"identifier":"allow-remove-listener","description":"Enables the remove_listener command without any pre-configured scope.","commands":{"allow":["remove_listener"],"deny":[]}},"allow-set-app-theme":{"identifier":"allow-set-app-theme","description":"Enables the set_app_theme command without any pre-configured scope.","commands":{"allow":["set_app_theme"],"deny":[]}},"allow-set-dock-visibility":{"identifier":"allow-set-dock-visibility","description":"Enables the set_dock_visibility command without any pre-configured scope.","commands":{"allow":["set_dock_visibility"],"deny":[]}},"allow-tauri-version":{"identifier":"allow-tauri-version","description":"Enables the tauri_version command without any pre-configured scope.","commands":{"allow":["tauri_version"],"deny":[]}},"allow-version":{"identifier":"allow-version","description":"Enables the version command without any pre-configured scope.","commands":{"allow":["version"],"deny":[]}},"deny-app-hide":{"identifier":"deny-app-hide","description":"Denies the app_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["app_hide"]}},"deny-app-show":{"identifier":"deny-app-show","description":"Denies the app_show command without any pre-configured scope.","commands":{"allow":[],"deny":["app_show"]}},"deny-bundle-type":{"identifier":"deny-bundle-type","description":"Denies the bundle_type command without any pre-configured scope.","commands":{"allow":[],"deny":["bundle_type"]}},"deny-default-window-icon":{"identifier":"deny-default-window-icon","description":"Denies the default_window_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["default_window_icon"]}},"deny-fetch-data-store-identifiers":{"identifier":"deny-fetch-data-store-identifiers","description":"Denies the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch_data_store_identifiers"]}},"deny-identifier":{"identifier":"deny-identifier","description":"Denies the identifier command without any pre-configured scope.","commands":{"allow":[],"deny":["identifier"]}},"deny-name":{"identifier":"deny-name","description":"Denies the name command without any pre-configured scope.","commands":{"allow":[],"deny":["name"]}},"deny-register-listener":{"identifier":"deny-register-listener","description":"Denies the register_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["register_listener"]}},"deny-remove-data-store":{"identifier":"deny-remove-data-store","description":"Denies the remove_data_store command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_data_store"]}},"deny-remove-listener":{"identifier":"deny-remove-listener","description":"Denies the remove_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_listener"]}},"deny-set-app-theme":{"identifier":"deny-set-app-theme","description":"Denies the set_app_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_app_theme"]}},"deny-set-dock-visibility":{"identifier":"deny-set-dock-visibility","description":"Denies the set_dock_visibility command without any pre-configured scope.","commands":{"allow":[],"deny":["set_dock_visibility"]}},"deny-tauri-version":{"identifier":"deny-tauri-version","description":"Denies the tauri_version command without any pre-configured scope.","commands":{"allow":[],"deny":["tauri_version"]}},"deny-version":{"identifier":"deny-version","description":"Denies the version command without any pre-configured scope.","commands":{"allow":[],"deny":["version"]}}},"permission_sets":{},"global_scope_schema":null},"core:event":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-listen","allow-unlisten","allow-emit","allow-emit-to"]},"permissions":{"allow-emit":{"identifier":"allow-emit","description":"Enables the emit command without any pre-configured scope.","commands":{"allow":["emit"],"deny":[]}},"allow-emit-to":{"identifier":"allow-emit-to","description":"Enables the emit_to command without any pre-configured scope.","commands":{"allow":["emit_to"],"deny":[]}},"allow-listen":{"identifier":"allow-listen","description":"Enables the listen command without any pre-configured scope.","commands":{"allow":["listen"],"deny":[]}},"allow-unlisten":{"identifier":"allow-unlisten","description":"Enables the unlisten command without any pre-configured scope.","commands":{"allow":["unlisten"],"deny":[]}},"deny-emit":{"identifier":"deny-emit","description":"Denies the emit command without any pre-configured scope.","commands":{"allow":[],"deny":["emit"]}},"deny-emit-to":{"identifier":"deny-emit-to","description":"Denies the emit_to command without any pre-configured scope.","commands":{"allow":[],"deny":["emit_to"]}},"deny-listen":{"identifier":"deny-listen","description":"Denies the listen command without any pre-configured scope.","commands":{"allow":[],"deny":["listen"]}},"deny-unlisten":{"identifier":"deny-unlisten","description":"Denies the unlisten command without any pre-configured scope.","commands":{"allow":[],"deny":["unlisten"]}}},"permission_sets":{},"global_scope_schema":null},"core:image":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-from-bytes","allow-from-path","allow-rgba","allow-size"]},"permissions":{"allow-from-bytes":{"identifier":"allow-from-bytes","description":"Enables the from_bytes command without any pre-configured scope.","commands":{"allow":["from_bytes"],"deny":[]}},"allow-from-path":{"identifier":"allow-from-path","description":"Enables the from_path command without any pre-configured scope.","commands":{"allow":["from_path"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-rgba":{"identifier":"allow-rgba","description":"Enables the rgba command without any pre-configured scope.","commands":{"allow":["rgba"],"deny":[]}},"allow-size":{"identifier":"allow-size","description":"Enables the size command without any pre-configured scope.","commands":{"allow":["size"],"deny":[]}},"deny-from-bytes":{"identifier":"deny-from-bytes","description":"Denies the from_bytes command without any pre-configured scope.","commands":{"allow":[],"deny":["from_bytes"]}},"deny-from-path":{"identifier":"deny-from-path","description":"Denies the from_path command without any pre-configured scope.","commands":{"allow":[],"deny":["from_path"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-rgba":{"identifier":"deny-rgba","description":"Denies the rgba command without any pre-configured scope.","commands":{"allow":[],"deny":["rgba"]}},"deny-size":{"identifier":"deny-size","description":"Denies the size command without any pre-configured scope.","commands":{"allow":[],"deny":["size"]}}},"permission_sets":{},"global_scope_schema":null},"core:menu":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-append","allow-prepend","allow-insert","allow-remove","allow-remove-at","allow-items","allow-get","allow-popup","allow-create-default","allow-set-as-app-menu","allow-set-as-window-menu","allow-text","allow-set-text","allow-is-enabled","allow-set-enabled","allow-set-accelerator","allow-set-as-windows-menu-for-nsapp","allow-set-as-help-menu-for-nsapp","allow-is-checked","allow-set-checked","allow-set-icon"]},"permissions":{"allow-append":{"identifier":"allow-append","description":"Enables the append command without any pre-configured scope.","commands":{"allow":["append"],"deny":[]}},"allow-create-default":{"identifier":"allow-create-default","description":"Enables the create_default command without any pre-configured scope.","commands":{"allow":["create_default"],"deny":[]}},"allow-get":{"identifier":"allow-get","description":"Enables the get command without any pre-configured scope.","commands":{"allow":["get"],"deny":[]}},"allow-insert":{"identifier":"allow-insert","description":"Enables the insert command without any pre-configured scope.","commands":{"allow":["insert"],"deny":[]}},"allow-is-checked":{"identifier":"allow-is-checked","description":"Enables the is_checked command without any pre-configured scope.","commands":{"allow":["is_checked"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-items":{"identifier":"allow-items","description":"Enables the items command without any pre-configured scope.","commands":{"allow":["items"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-popup":{"identifier":"allow-popup","description":"Enables the popup command without any pre-configured scope.","commands":{"allow":["popup"],"deny":[]}},"allow-prepend":{"identifier":"allow-prepend","description":"Enables the prepend command without any pre-configured scope.","commands":{"allow":["prepend"],"deny":[]}},"allow-remove":{"identifier":"allow-remove","description":"Enables the remove command without any pre-configured scope.","commands":{"allow":["remove"],"deny":[]}},"allow-remove-at":{"identifier":"allow-remove-at","description":"Enables the remove_at command without any pre-configured scope.","commands":{"allow":["remove_at"],"deny":[]}},"allow-set-accelerator":{"identifier":"allow-set-accelerator","description":"Enables the set_accelerator command without any pre-configured scope.","commands":{"allow":["set_accelerator"],"deny":[]}},"allow-set-as-app-menu":{"identifier":"allow-set-as-app-menu","description":"Enables the set_as_app_menu command without any pre-configured scope.","commands":{"allow":["set_as_app_menu"],"deny":[]}},"allow-set-as-help-menu-for-nsapp":{"identifier":"allow-set-as-help-menu-for-nsapp","description":"Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_help_menu_for_nsapp"],"deny":[]}},"allow-set-as-window-menu":{"identifier":"allow-set-as-window-menu","description":"Enables the set_as_window_menu command without any pre-configured scope.","commands":{"allow":["set_as_window_menu"],"deny":[]}},"allow-set-as-windows-menu-for-nsapp":{"identifier":"allow-set-as-windows-menu-for-nsapp","description":"Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_windows_menu_for_nsapp"],"deny":[]}},"allow-set-checked":{"identifier":"allow-set-checked","description":"Enables the set_checked command without any pre-configured scope.","commands":{"allow":["set_checked"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-text":{"identifier":"allow-set-text","description":"Enables the set_text command without any pre-configured scope.","commands":{"allow":["set_text"],"deny":[]}},"allow-text":{"identifier":"allow-text","description":"Enables the text command without any pre-configured scope.","commands":{"allow":["text"],"deny":[]}},"deny-append":{"identifier":"deny-append","description":"Denies the append command without any pre-configured scope.","commands":{"allow":[],"deny":["append"]}},"deny-create-default":{"identifier":"deny-create-default","description":"Denies the create_default command without any pre-configured scope.","commands":{"allow":[],"deny":["create_default"]}},"deny-get":{"identifier":"deny-get","description":"Denies the get command without any pre-configured scope.","commands":{"allow":[],"deny":["get"]}},"deny-insert":{"identifier":"deny-insert","description":"Denies the insert command without any pre-configured scope.","commands":{"allow":[],"deny":["insert"]}},"deny-is-checked":{"identifier":"deny-is-checked","description":"Denies the is_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["is_checked"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-items":{"identifier":"deny-items","description":"Denies the items command without any pre-configured scope.","commands":{"allow":[],"deny":["items"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-popup":{"identifier":"deny-popup","description":"Denies the popup command without any pre-configured scope.","commands":{"allow":[],"deny":["popup"]}},"deny-prepend":{"identifier":"deny-prepend","description":"Denies the prepend command without any pre-configured scope.","commands":{"allow":[],"deny":["prepend"]}},"deny-remove":{"identifier":"deny-remove","description":"Denies the remove command without any pre-configured scope.","commands":{"allow":[],"deny":["remove"]}},"deny-remove-at":{"identifier":"deny-remove-at","description":"Denies the remove_at command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_at"]}},"deny-set-accelerator":{"identifier":"deny-set-accelerator","description":"Denies the set_accelerator command without any pre-configured scope.","commands":{"allow":[],"deny":["set_accelerator"]}},"deny-set-as-app-menu":{"identifier":"deny-set-as-app-menu","description":"Denies the set_as_app_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_app_menu"]}},"deny-set-as-help-menu-for-nsapp":{"identifier":"deny-set-as-help-menu-for-nsapp","description":"Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_help_menu_for_nsapp"]}},"deny-set-as-window-menu":{"identifier":"deny-set-as-window-menu","description":"Denies the set_as_window_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_window_menu"]}},"deny-set-as-windows-menu-for-nsapp":{"identifier":"deny-set-as-windows-menu-for-nsapp","description":"Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_windows_menu_for_nsapp"]}},"deny-set-checked":{"identifier":"deny-set-checked","description":"Denies the set_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["set_checked"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-text":{"identifier":"deny-set-text","description":"Denies the set_text command without any pre-configured scope.","commands":{"allow":[],"deny":["set_text"]}},"deny-text":{"identifier":"deny-text","description":"Denies the text command without any pre-configured scope.","commands":{"allow":[],"deny":["text"]}}},"permission_sets":{},"global_scope_schema":null},"core:path":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-resolve-directory","allow-resolve","allow-normalize","allow-join","allow-dirname","allow-extname","allow-basename","allow-is-absolute"]},"permissions":{"allow-basename":{"identifier":"allow-basename","description":"Enables the basename command without any pre-configured scope.","commands":{"allow":["basename"],"deny":[]}},"allow-dirname":{"identifier":"allow-dirname","description":"Enables the dirname command without any pre-configured scope.","commands":{"allow":["dirname"],"deny":[]}},"allow-extname":{"identifier":"allow-extname","description":"Enables the extname command without any pre-configured scope.","commands":{"allow":["extname"],"deny":[]}},"allow-is-absolute":{"identifier":"allow-is-absolute","description":"Enables the is_absolute command without any pre-configured scope.","commands":{"allow":["is_absolute"],"deny":[]}},"allow-join":{"identifier":"allow-join","description":"Enables the join command without any pre-configured scope.","commands":{"allow":["join"],"deny":[]}},"allow-normalize":{"identifier":"allow-normalize","description":"Enables the normalize command without any pre-configured scope.","commands":{"allow":["normalize"],"deny":[]}},"allow-resolve":{"identifier":"allow-resolve","description":"Enables the resolve command without any pre-configured scope.","commands":{"allow":["resolve"],"deny":[]}},"allow-resolve-directory":{"identifier":"allow-resolve-directory","description":"Enables the resolve_directory command without any pre-configured scope.","commands":{"allow":["resolve_directory"],"deny":[]}},"deny-basename":{"identifier":"deny-basename","description":"Denies the basename command without any pre-configured scope.","commands":{"allow":[],"deny":["basename"]}},"deny-dirname":{"identifier":"deny-dirname","description":"Denies the dirname command without any pre-configured scope.","commands":{"allow":[],"deny":["dirname"]}},"deny-extname":{"identifier":"deny-extname","description":"Denies the extname command without any pre-configured scope.","commands":{"allow":[],"deny":["extname"]}},"deny-is-absolute":{"identifier":"deny-is-absolute","description":"Denies the is_absolute command without any pre-configured scope.","commands":{"allow":[],"deny":["is_absolute"]}},"deny-join":{"identifier":"deny-join","description":"Denies the join command without any pre-configured scope.","commands":{"allow":[],"deny":["join"]}},"deny-normalize":{"identifier":"deny-normalize","description":"Denies the normalize command without any pre-configured scope.","commands":{"allow":[],"deny":["normalize"]}},"deny-resolve":{"identifier":"deny-resolve","description":"Denies the resolve command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve"]}},"deny-resolve-directory":{"identifier":"deny-resolve-directory","description":"Denies the resolve_directory command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve_directory"]}}},"permission_sets":{},"global_scope_schema":null},"core:resources":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-close"]},"permissions":{"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}}},"permission_sets":{},"global_scope_schema":null},"core:tray":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-get-by-id","allow-remove-by-id","allow-set-icon","allow-set-menu","allow-set-tooltip","allow-set-title","allow-set-visible","allow-set-temp-dir-path","allow-set-icon-as-template","allow-set-show-menu-on-left-click"]},"permissions":{"allow-get-by-id":{"identifier":"allow-get-by-id","description":"Enables the get_by_id command without any pre-configured scope.","commands":{"allow":["get_by_id"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-remove-by-id":{"identifier":"allow-remove-by-id","description":"Enables the remove_by_id command without any pre-configured scope.","commands":{"allow":["remove_by_id"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-icon-as-template":{"identifier":"allow-set-icon-as-template","description":"Enables the set_icon_as_template command without any pre-configured scope.","commands":{"allow":["set_icon_as_template"],"deny":[]}},"allow-set-menu":{"identifier":"allow-set-menu","description":"Enables the set_menu command without any pre-configured scope.","commands":{"allow":["set_menu"],"deny":[]}},"allow-set-show-menu-on-left-click":{"identifier":"allow-set-show-menu-on-left-click","description":"Enables the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":["set_show_menu_on_left_click"],"deny":[]}},"allow-set-temp-dir-path":{"identifier":"allow-set-temp-dir-path","description":"Enables the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":["set_temp_dir_path"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-tooltip":{"identifier":"allow-set-tooltip","description":"Enables the set_tooltip command without any pre-configured scope.","commands":{"allow":["set_tooltip"],"deny":[]}},"allow-set-visible":{"identifier":"allow-set-visible","description":"Enables the set_visible command without any pre-configured scope.","commands":{"allow":["set_visible"],"deny":[]}},"deny-get-by-id":{"identifier":"deny-get-by-id","description":"Denies the get_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["get_by_id"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-remove-by-id":{"identifier":"deny-remove-by-id","description":"Denies the remove_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_by_id"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-icon-as-template":{"identifier":"deny-set-icon-as-template","description":"Denies the set_icon_as_template command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon_as_template"]}},"deny-set-menu":{"identifier":"deny-set-menu","description":"Denies the set_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_menu"]}},"deny-set-show-menu-on-left-click":{"identifier":"deny-set-show-menu-on-left-click","description":"Denies the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":[],"deny":["set_show_menu_on_left_click"]}},"deny-set-temp-dir-path":{"identifier":"deny-set-temp-dir-path","description":"Denies the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":[],"deny":["set_temp_dir_path"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-tooltip":{"identifier":"deny-set-tooltip","description":"Denies the set_tooltip command without any pre-configured scope.","commands":{"allow":[],"deny":["set_tooltip"]}},"deny-set-visible":{"identifier":"deny-set-visible","description":"Denies the set_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible"]}}},"permission_sets":{},"global_scope_schema":null},"core:webview":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-webviews","allow-webview-position","allow-webview-size","allow-internal-toggle-devtools"]},"permissions":{"allow-clear-all-browsing-data":{"identifier":"allow-clear-all-browsing-data","description":"Enables the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":["clear_all_browsing_data"],"deny":[]}},"allow-create-webview":{"identifier":"allow-create-webview","description":"Enables the create_webview command without any pre-configured scope.","commands":{"allow":["create_webview"],"deny":[]}},"allow-create-webview-window":{"identifier":"allow-create-webview-window","description":"Enables the create_webview_window command without any pre-configured scope.","commands":{"allow":["create_webview_window"],"deny":[]}},"allow-get-all-webviews":{"identifier":"allow-get-all-webviews","description":"Enables the get_all_webviews command without any pre-configured scope.","commands":{"allow":["get_all_webviews"],"deny":[]}},"allow-internal-toggle-devtools":{"identifier":"allow-internal-toggle-devtools","description":"Enables the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":["internal_toggle_devtools"],"deny":[]}},"allow-print":{"identifier":"allow-print","description":"Enables the print command without any pre-configured scope.","commands":{"allow":["print"],"deny":[]}},"allow-reparent":{"identifier":"allow-reparent","description":"Enables the reparent command without any pre-configured scope.","commands":{"allow":["reparent"],"deny":[]}},"allow-set-webview-auto-resize":{"identifier":"allow-set-webview-auto-resize","description":"Enables the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":["set_webview_auto_resize"],"deny":[]}},"allow-set-webview-background-color":{"identifier":"allow-set-webview-background-color","description":"Enables the set_webview_background_color command without any pre-configured scope.","commands":{"allow":["set_webview_background_color"],"deny":[]}},"allow-set-webview-focus":{"identifier":"allow-set-webview-focus","description":"Enables the set_webview_focus command without any pre-configured scope.","commands":{"allow":["set_webview_focus"],"deny":[]}},"allow-set-webview-position":{"identifier":"allow-set-webview-position","description":"Enables the set_webview_position command without any pre-configured scope.","commands":{"allow":["set_webview_position"],"deny":[]}},"allow-set-webview-size":{"identifier":"allow-set-webview-size","description":"Enables the set_webview_size command without any pre-configured scope.","commands":{"allow":["set_webview_size"],"deny":[]}},"allow-set-webview-zoom":{"identifier":"allow-set-webview-zoom","description":"Enables the set_webview_zoom command without any pre-configured scope.","commands":{"allow":["set_webview_zoom"],"deny":[]}},"allow-webview-close":{"identifier":"allow-webview-close","description":"Enables the webview_close command without any pre-configured scope.","commands":{"allow":["webview_close"],"deny":[]}},"allow-webview-hide":{"identifier":"allow-webview-hide","description":"Enables the webview_hide command without any pre-configured scope.","commands":{"allow":["webview_hide"],"deny":[]}},"allow-webview-position":{"identifier":"allow-webview-position","description":"Enables the webview_position command without any pre-configured scope.","commands":{"allow":["webview_position"],"deny":[]}},"allow-webview-show":{"identifier":"allow-webview-show","description":"Enables the webview_show command without any pre-configured scope.","commands":{"allow":["webview_show"],"deny":[]}},"allow-webview-size":{"identifier":"allow-webview-size","description":"Enables the webview_size command without any pre-configured scope.","commands":{"allow":["webview_size"],"deny":[]}},"deny-clear-all-browsing-data":{"identifier":"deny-clear-all-browsing-data","description":"Denies the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":[],"deny":["clear_all_browsing_data"]}},"deny-create-webview":{"identifier":"deny-create-webview","description":"Denies the create_webview command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview"]}},"deny-create-webview-window":{"identifier":"deny-create-webview-window","description":"Denies the create_webview_window command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview_window"]}},"deny-get-all-webviews":{"identifier":"deny-get-all-webviews","description":"Denies the get_all_webviews command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_webviews"]}},"deny-internal-toggle-devtools":{"identifier":"deny-internal-toggle-devtools","description":"Denies the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_devtools"]}},"deny-print":{"identifier":"deny-print","description":"Denies the print command without any pre-configured scope.","commands":{"allow":[],"deny":["print"]}},"deny-reparent":{"identifier":"deny-reparent","description":"Denies the reparent command without any pre-configured scope.","commands":{"allow":[],"deny":["reparent"]}},"deny-set-webview-auto-resize":{"identifier":"deny-set-webview-auto-resize","description":"Denies the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_auto_resize"]}},"deny-set-webview-background-color":{"identifier":"deny-set-webview-background-color","description":"Denies the set_webview_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_background_color"]}},"deny-set-webview-focus":{"identifier":"deny-set-webview-focus","description":"Denies the set_webview_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_focus"]}},"deny-set-webview-position":{"identifier":"deny-set-webview-position","description":"Denies the set_webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_position"]}},"deny-set-webview-size":{"identifier":"deny-set-webview-size","description":"Denies the set_webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_size"]}},"deny-set-webview-zoom":{"identifier":"deny-set-webview-zoom","description":"Denies the set_webview_zoom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_zoom"]}},"deny-webview-close":{"identifier":"deny-webview-close","description":"Denies the webview_close command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_close"]}},"deny-webview-hide":{"identifier":"deny-webview-hide","description":"Denies the webview_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_hide"]}},"deny-webview-position":{"identifier":"deny-webview-position","description":"Denies the webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_position"]}},"deny-webview-show":{"identifier":"deny-webview-show","description":"Denies the webview_show command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_show"]}},"deny-webview-size":{"identifier":"deny-webview-size","description":"Denies the webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_size"]}}},"permission_sets":{},"global_scope_schema":null},"core:window":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-windows","allow-scale-factor","allow-inner-position","allow-outer-position","allow-inner-size","allow-outer-size","allow-is-fullscreen","allow-is-minimized","allow-is-maximized","allow-is-focused","allow-is-decorated","allow-is-resizable","allow-is-maximizable","allow-is-minimizable","allow-is-closable","allow-is-visible","allow-is-enabled","allow-title","allow-current-monitor","allow-primary-monitor","allow-monitor-from-point","allow-available-monitors","allow-cursor-position","allow-theme","allow-is-always-on-top","allow-internal-toggle-maximize"]},"permissions":{"allow-available-monitors":{"identifier":"allow-available-monitors","description":"Enables the available_monitors command without any pre-configured scope.","commands":{"allow":["available_monitors"],"deny":[]}},"allow-center":{"identifier":"allow-center","description":"Enables the center command without any pre-configured scope.","commands":{"allow":["center"],"deny":[]}},"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"allow-create":{"identifier":"allow-create","description":"Enables the create command without any pre-configured scope.","commands":{"allow":["create"],"deny":[]}},"allow-current-monitor":{"identifier":"allow-current-monitor","description":"Enables the current_monitor command without any pre-configured scope.","commands":{"allow":["current_monitor"],"deny":[]}},"allow-cursor-position":{"identifier":"allow-cursor-position","description":"Enables the cursor_position command without any pre-configured scope.","commands":{"allow":["cursor_position"],"deny":[]}},"allow-destroy":{"identifier":"allow-destroy","description":"Enables the destroy command without any pre-configured scope.","commands":{"allow":["destroy"],"deny":[]}},"allow-get-all-windows":{"identifier":"allow-get-all-windows","description":"Enables the get_all_windows command without any pre-configured scope.","commands":{"allow":["get_all_windows"],"deny":[]}},"allow-hide":{"identifier":"allow-hide","description":"Enables the hide command without any pre-configured scope.","commands":{"allow":["hide"],"deny":[]}},"allow-inner-position":{"identifier":"allow-inner-position","description":"Enables the inner_position command without any pre-configured scope.","commands":{"allow":["inner_position"],"deny":[]}},"allow-inner-size":{"identifier":"allow-inner-size","description":"Enables the inner_size command without any pre-configured scope.","commands":{"allow":["inner_size"],"deny":[]}},"allow-internal-toggle-maximize":{"identifier":"allow-internal-toggle-maximize","description":"Enables the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":["internal_toggle_maximize"],"deny":[]}},"allow-is-always-on-top":{"identifier":"allow-is-always-on-top","description":"Enables the is_always_on_top command without any pre-configured scope.","commands":{"allow":["is_always_on_top"],"deny":[]}},"allow-is-closable":{"identifier":"allow-is-closable","description":"Enables the is_closable command without any pre-configured scope.","commands":{"allow":["is_closable"],"deny":[]}},"allow-is-decorated":{"identifier":"allow-is-decorated","description":"Enables the is_decorated command without any pre-configured scope.","commands":{"allow":["is_decorated"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-is-focused":{"identifier":"allow-is-focused","description":"Enables the is_focused command without any pre-configured scope.","commands":{"allow":["is_focused"],"deny":[]}},"allow-is-fullscreen":{"identifier":"allow-is-fullscreen","description":"Enables the is_fullscreen command without any pre-configured scope.","commands":{"allow":["is_fullscreen"],"deny":[]}},"allow-is-maximizable":{"identifier":"allow-is-maximizable","description":"Enables the is_maximizable command without any pre-configured scope.","commands":{"allow":["is_maximizable"],"deny":[]}},"allow-is-maximized":{"identifier":"allow-is-maximized","description":"Enables the is_maximized command without any pre-configured scope.","commands":{"allow":["is_maximized"],"deny":[]}},"allow-is-minimizable":{"identifier":"allow-is-minimizable","description":"Enables the is_minimizable command without any pre-configured scope.","commands":{"allow":["is_minimizable"],"deny":[]}},"allow-is-minimized":{"identifier":"allow-is-minimized","description":"Enables the is_minimized command without any pre-configured scope.","commands":{"allow":["is_minimized"],"deny":[]}},"allow-is-resizable":{"identifier":"allow-is-resizable","description":"Enables the is_resizable command without any pre-configured scope.","commands":{"allow":["is_resizable"],"deny":[]}},"allow-is-visible":{"identifier":"allow-is-visible","description":"Enables the is_visible command without any pre-configured scope.","commands":{"allow":["is_visible"],"deny":[]}},"allow-maximize":{"identifier":"allow-maximize","description":"Enables the maximize command without any pre-configured scope.","commands":{"allow":["maximize"],"deny":[]}},"allow-minimize":{"identifier":"allow-minimize","description":"Enables the minimize command without any pre-configured scope.","commands":{"allow":["minimize"],"deny":[]}},"allow-monitor-from-point":{"identifier":"allow-monitor-from-point","description":"Enables the monitor_from_point command without any pre-configured scope.","commands":{"allow":["monitor_from_point"],"deny":[]}},"allow-outer-position":{"identifier":"allow-outer-position","description":"Enables the outer_position command without any pre-configured scope.","commands":{"allow":["outer_position"],"deny":[]}},"allow-outer-size":{"identifier":"allow-outer-size","description":"Enables the outer_size command without any pre-configured scope.","commands":{"allow":["outer_size"],"deny":[]}},"allow-primary-monitor":{"identifier":"allow-primary-monitor","description":"Enables the primary_monitor command without any pre-configured scope.","commands":{"allow":["primary_monitor"],"deny":[]}},"allow-request-user-attention":{"identifier":"allow-request-user-attention","description":"Enables the request_user_attention command without any pre-configured scope.","commands":{"allow":["request_user_attention"],"deny":[]}},"allow-scale-factor":{"identifier":"allow-scale-factor","description":"Enables the scale_factor command without any pre-configured scope.","commands":{"allow":["scale_factor"],"deny":[]}},"allow-set-always-on-bottom":{"identifier":"allow-set-always-on-bottom","description":"Enables the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":["set_always_on_bottom"],"deny":[]}},"allow-set-always-on-top":{"identifier":"allow-set-always-on-top","description":"Enables the set_always_on_top command without any pre-configured scope.","commands":{"allow":["set_always_on_top"],"deny":[]}},"allow-set-background-color":{"identifier":"allow-set-background-color","description":"Enables the set_background_color command without any pre-configured scope.","commands":{"allow":["set_background_color"],"deny":[]}},"allow-set-badge-count":{"identifier":"allow-set-badge-count","description":"Enables the set_badge_count command without any pre-configured scope.","commands":{"allow":["set_badge_count"],"deny":[]}},"allow-set-badge-label":{"identifier":"allow-set-badge-label","description":"Enables the set_badge_label command without any pre-configured scope.","commands":{"allow":["set_badge_label"],"deny":[]}},"allow-set-closable":{"identifier":"allow-set-closable","description":"Enables the set_closable command without any pre-configured scope.","commands":{"allow":["set_closable"],"deny":[]}},"allow-set-content-protected":{"identifier":"allow-set-content-protected","description":"Enables the set_content_protected command without any pre-configured scope.","commands":{"allow":["set_content_protected"],"deny":[]}},"allow-set-cursor-grab":{"identifier":"allow-set-cursor-grab","description":"Enables the set_cursor_grab command without any pre-configured scope.","commands":{"allow":["set_cursor_grab"],"deny":[]}},"allow-set-cursor-icon":{"identifier":"allow-set-cursor-icon","description":"Enables the set_cursor_icon command without any pre-configured scope.","commands":{"allow":["set_cursor_icon"],"deny":[]}},"allow-set-cursor-position":{"identifier":"allow-set-cursor-position","description":"Enables the set_cursor_position command without any pre-configured scope.","commands":{"allow":["set_cursor_position"],"deny":[]}},"allow-set-cursor-visible":{"identifier":"allow-set-cursor-visible","description":"Enables the set_cursor_visible command without any pre-configured scope.","commands":{"allow":["set_cursor_visible"],"deny":[]}},"allow-set-decorations":{"identifier":"allow-set-decorations","description":"Enables the set_decorations command without any pre-configured scope.","commands":{"allow":["set_decorations"],"deny":[]}},"allow-set-effects":{"identifier":"allow-set-effects","description":"Enables the set_effects command without any pre-configured scope.","commands":{"allow":["set_effects"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-focus":{"identifier":"allow-set-focus","description":"Enables the set_focus command without any pre-configured scope.","commands":{"allow":["set_focus"],"deny":[]}},"allow-set-focusable":{"identifier":"allow-set-focusable","description":"Enables the set_focusable command without any pre-configured scope.","commands":{"allow":["set_focusable"],"deny":[]}},"allow-set-fullscreen":{"identifier":"allow-set-fullscreen","description":"Enables the set_fullscreen command without any pre-configured scope.","commands":{"allow":["set_fullscreen"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-ignore-cursor-events":{"identifier":"allow-set-ignore-cursor-events","description":"Enables the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":["set_ignore_cursor_events"],"deny":[]}},"allow-set-max-size":{"identifier":"allow-set-max-size","description":"Enables the set_max_size command without any pre-configured scope.","commands":{"allow":["set_max_size"],"deny":[]}},"allow-set-maximizable":{"identifier":"allow-set-maximizable","description":"Enables the set_maximizable command without any pre-configured scope.","commands":{"allow":["set_maximizable"],"deny":[]}},"allow-set-min-size":{"identifier":"allow-set-min-size","description":"Enables the set_min_size command without any pre-configured scope.","commands":{"allow":["set_min_size"],"deny":[]}},"allow-set-minimizable":{"identifier":"allow-set-minimizable","description":"Enables the set_minimizable command without any pre-configured scope.","commands":{"allow":["set_minimizable"],"deny":[]}},"allow-set-overlay-icon":{"identifier":"allow-set-overlay-icon","description":"Enables the set_overlay_icon command without any pre-configured scope.","commands":{"allow":["set_overlay_icon"],"deny":[]}},"allow-set-position":{"identifier":"allow-set-position","description":"Enables the set_position command without any pre-configured scope.","commands":{"allow":["set_position"],"deny":[]}},"allow-set-progress-bar":{"identifier":"allow-set-progress-bar","description":"Enables the set_progress_bar command without any pre-configured scope.","commands":{"allow":["set_progress_bar"],"deny":[]}},"allow-set-resizable":{"identifier":"allow-set-resizable","description":"Enables the set_resizable command without any pre-configured scope.","commands":{"allow":["set_resizable"],"deny":[]}},"allow-set-shadow":{"identifier":"allow-set-shadow","description":"Enables the set_shadow command without any pre-configured scope.","commands":{"allow":["set_shadow"],"deny":[]}},"allow-set-simple-fullscreen":{"identifier":"allow-set-simple-fullscreen","description":"Enables the set_simple_fullscreen command without any pre-configured scope.","commands":{"allow":["set_simple_fullscreen"],"deny":[]}},"allow-set-size":{"identifier":"allow-set-size","description":"Enables the set_size command without any pre-configured scope.","commands":{"allow":["set_size"],"deny":[]}},"allow-set-size-constraints":{"identifier":"allow-set-size-constraints","description":"Enables the set_size_constraints command without any pre-configured scope.","commands":{"allow":["set_size_constraints"],"deny":[]}},"allow-set-skip-taskbar":{"identifier":"allow-set-skip-taskbar","description":"Enables the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":["set_skip_taskbar"],"deny":[]}},"allow-set-theme":{"identifier":"allow-set-theme","description":"Enables the set_theme command without any pre-configured scope.","commands":{"allow":["set_theme"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-title-bar-style":{"identifier":"allow-set-title-bar-style","description":"Enables the set_title_bar_style command without any pre-configured scope.","commands":{"allow":["set_title_bar_style"],"deny":[]}},"allow-set-visible-on-all-workspaces":{"identifier":"allow-set-visible-on-all-workspaces","description":"Enables the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":["set_visible_on_all_workspaces"],"deny":[]}},"allow-show":{"identifier":"allow-show","description":"Enables the show command without any pre-configured scope.","commands":{"allow":["show"],"deny":[]}},"allow-start-dragging":{"identifier":"allow-start-dragging","description":"Enables the start_dragging command without any pre-configured scope.","commands":{"allow":["start_dragging"],"deny":[]}},"allow-start-resize-dragging":{"identifier":"allow-start-resize-dragging","description":"Enables the start_resize_dragging command without any pre-configured scope.","commands":{"allow":["start_resize_dragging"],"deny":[]}},"allow-theme":{"identifier":"allow-theme","description":"Enables the theme command without any pre-configured scope.","commands":{"allow":["theme"],"deny":[]}},"allow-title":{"identifier":"allow-title","description":"Enables the title command without any pre-configured scope.","commands":{"allow":["title"],"deny":[]}},"allow-toggle-maximize":{"identifier":"allow-toggle-maximize","description":"Enables the toggle_maximize command without any pre-configured scope.","commands":{"allow":["toggle_maximize"],"deny":[]}},"allow-unmaximize":{"identifier":"allow-unmaximize","description":"Enables the unmaximize command without any pre-configured scope.","commands":{"allow":["unmaximize"],"deny":[]}},"allow-unminimize":{"identifier":"allow-unminimize","description":"Enables the unminimize command without any pre-configured scope.","commands":{"allow":["unminimize"],"deny":[]}},"deny-available-monitors":{"identifier":"deny-available-monitors","description":"Denies the available_monitors command without any pre-configured scope.","commands":{"allow":[],"deny":["available_monitors"]}},"deny-center":{"identifier":"deny-center","description":"Denies the center command without any pre-configured scope.","commands":{"allow":[],"deny":["center"]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}},"deny-create":{"identifier":"deny-create","description":"Denies the create command without any pre-configured scope.","commands":{"allow":[],"deny":["create"]}},"deny-current-monitor":{"identifier":"deny-current-monitor","description":"Denies the current_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["current_monitor"]}},"deny-cursor-position":{"identifier":"deny-cursor-position","description":"Denies the cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["cursor_position"]}},"deny-destroy":{"identifier":"deny-destroy","description":"Denies the destroy command without any pre-configured scope.","commands":{"allow":[],"deny":["destroy"]}},"deny-get-all-windows":{"identifier":"deny-get-all-windows","description":"Denies the get_all_windows command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_windows"]}},"deny-hide":{"identifier":"deny-hide","description":"Denies the hide command without any pre-configured scope.","commands":{"allow":[],"deny":["hide"]}},"deny-inner-position":{"identifier":"deny-inner-position","description":"Denies the inner_position command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_position"]}},"deny-inner-size":{"identifier":"deny-inner-size","description":"Denies the inner_size command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_size"]}},"deny-internal-toggle-maximize":{"identifier":"deny-internal-toggle-maximize","description":"Denies the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_maximize"]}},"deny-is-always-on-top":{"identifier":"deny-is-always-on-top","description":"Denies the is_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["is_always_on_top"]}},"deny-is-closable":{"identifier":"deny-is-closable","description":"Denies the is_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_closable"]}},"deny-is-decorated":{"identifier":"deny-is-decorated","description":"Denies the is_decorated command without any pre-configured scope.","commands":{"allow":[],"deny":["is_decorated"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-is-focused":{"identifier":"deny-is-focused","description":"Denies the is_focused command without any pre-configured scope.","commands":{"allow":[],"deny":["is_focused"]}},"deny-is-fullscreen":{"identifier":"deny-is-fullscreen","description":"Denies the is_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["is_fullscreen"]}},"deny-is-maximizable":{"identifier":"deny-is-maximizable","description":"Denies the is_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximizable"]}},"deny-is-maximized":{"identifier":"deny-is-maximized","description":"Denies the is_maximized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximized"]}},"deny-is-minimizable":{"identifier":"deny-is-minimizable","description":"Denies the is_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimizable"]}},"deny-is-minimized":{"identifier":"deny-is-minimized","description":"Denies the is_minimized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimized"]}},"deny-is-resizable":{"identifier":"deny-is-resizable","description":"Denies the is_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_resizable"]}},"deny-is-visible":{"identifier":"deny-is-visible","description":"Denies the is_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["is_visible"]}},"deny-maximize":{"identifier":"deny-maximize","description":"Denies the maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["maximize"]}},"deny-minimize":{"identifier":"deny-minimize","description":"Denies the minimize command without any pre-configured scope.","commands":{"allow":[],"deny":["minimize"]}},"deny-monitor-from-point":{"identifier":"deny-monitor-from-point","description":"Denies the monitor_from_point command without any pre-configured scope.","commands":{"allow":[],"deny":["monitor_from_point"]}},"deny-outer-position":{"identifier":"deny-outer-position","description":"Denies the outer_position command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_position"]}},"deny-outer-size":{"identifier":"deny-outer-size","description":"Denies the outer_size command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_size"]}},"deny-primary-monitor":{"identifier":"deny-primary-monitor","description":"Denies the primary_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["primary_monitor"]}},"deny-request-user-attention":{"identifier":"deny-request-user-attention","description":"Denies the request_user_attention command without any pre-configured scope.","commands":{"allow":[],"deny":["request_user_attention"]}},"deny-scale-factor":{"identifier":"deny-scale-factor","description":"Denies the scale_factor command without any pre-configured scope.","commands":{"allow":[],"deny":["scale_factor"]}},"deny-set-always-on-bottom":{"identifier":"deny-set-always-on-bottom","description":"Denies the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_bottom"]}},"deny-set-always-on-top":{"identifier":"deny-set-always-on-top","description":"Denies the set_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_top"]}},"deny-set-background-color":{"identifier":"deny-set-background-color","description":"Denies the set_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_background_color"]}},"deny-set-badge-count":{"identifier":"deny-set-badge-count","description":"Denies the set_badge_count command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_count"]}},"deny-set-badge-label":{"identifier":"deny-set-badge-label","description":"Denies the set_badge_label command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_label"]}},"deny-set-closable":{"identifier":"deny-set-closable","description":"Denies the set_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_closable"]}},"deny-set-content-protected":{"identifier":"deny-set-content-protected","description":"Denies the set_content_protected command without any pre-configured scope.","commands":{"allow":[],"deny":["set_content_protected"]}},"deny-set-cursor-grab":{"identifier":"deny-set-cursor-grab","description":"Denies the set_cursor_grab command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_grab"]}},"deny-set-cursor-icon":{"identifier":"deny-set-cursor-icon","description":"Denies the set_cursor_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_icon"]}},"deny-set-cursor-position":{"identifier":"deny-set-cursor-position","description":"Denies the set_cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_position"]}},"deny-set-cursor-visible":{"identifier":"deny-set-cursor-visible","description":"Denies the set_cursor_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_visible"]}},"deny-set-decorations":{"identifier":"deny-set-decorations","description":"Denies the set_decorations command without any pre-configured scope.","commands":{"allow":[],"deny":["set_decorations"]}},"deny-set-effects":{"identifier":"deny-set-effects","description":"Denies the set_effects command without any pre-configured scope.","commands":{"allow":[],"deny":["set_effects"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-focus":{"identifier":"deny-set-focus","description":"Denies the set_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focus"]}},"deny-set-focusable":{"identifier":"deny-set-focusable","description":"Denies the set_focusable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focusable"]}},"deny-set-fullscreen":{"identifier":"deny-set-fullscreen","description":"Denies the set_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_fullscreen"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-ignore-cursor-events":{"identifier":"deny-set-ignore-cursor-events","description":"Denies the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":[],"deny":["set_ignore_cursor_events"]}},"deny-set-max-size":{"identifier":"deny-set-max-size","description":"Denies the set_max_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_max_size"]}},"deny-set-maximizable":{"identifier":"deny-set-maximizable","description":"Denies the set_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_maximizable"]}},"deny-set-min-size":{"identifier":"deny-set-min-size","description":"Denies the set_min_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_min_size"]}},"deny-set-minimizable":{"identifier":"deny-set-minimizable","description":"Denies the set_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_minimizable"]}},"deny-set-overlay-icon":{"identifier":"deny-set-overlay-icon","description":"Denies the set_overlay_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_overlay_icon"]}},"deny-set-position":{"identifier":"deny-set-position","description":"Denies the set_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_position"]}},"deny-set-progress-bar":{"identifier":"deny-set-progress-bar","description":"Denies the set_progress_bar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_progress_bar"]}},"deny-set-resizable":{"identifier":"deny-set-resizable","description":"Denies the set_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_resizable"]}},"deny-set-shadow":{"identifier":"deny-set-shadow","description":"Denies the set_shadow command without any pre-configured scope.","commands":{"allow":[],"deny":["set_shadow"]}},"deny-set-simple-fullscreen":{"identifier":"deny-set-simple-fullscreen","description":"Denies the set_simple_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_simple_fullscreen"]}},"deny-set-size":{"identifier":"deny-set-size","description":"Denies the set_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size"]}},"deny-set-size-constraints":{"identifier":"deny-set-size-constraints","description":"Denies the set_size_constraints command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size_constraints"]}},"deny-set-skip-taskbar":{"identifier":"deny-set-skip-taskbar","description":"Denies the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_skip_taskbar"]}},"deny-set-theme":{"identifier":"deny-set-theme","description":"Denies the set_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_theme"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-title-bar-style":{"identifier":"deny-set-title-bar-style","description":"Denies the set_title_bar_style command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title_bar_style"]}},"deny-set-visible-on-all-workspaces":{"identifier":"deny-set-visible-on-all-workspaces","description":"Denies the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible_on_all_workspaces"]}},"deny-show":{"identifier":"deny-show","description":"Denies the show command without any pre-configured scope.","commands":{"allow":[],"deny":["show"]}},"deny-start-dragging":{"identifier":"deny-start-dragging","description":"Denies the start_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_dragging"]}},"deny-start-resize-dragging":{"identifier":"deny-start-resize-dragging","description":"Denies the start_resize_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_resize_dragging"]}},"deny-theme":{"identifier":"deny-theme","description":"Denies the theme command without any pre-configured scope.","commands":{"allow":[],"deny":["theme"]}},"deny-title":{"identifier":"deny-title","description":"Denies the title command without any pre-configured scope.","commands":{"allow":[],"deny":["title"]}},"deny-toggle-maximize":{"identifier":"deny-toggle-maximize","description":"Denies the toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["toggle_maximize"]}},"deny-unmaximize":{"identifier":"deny-unmaximize","description":"Denies the unmaximize command without any pre-configured scope.","commands":{"allow":[],"deny":["unmaximize"]}},"deny-unminimize":{"identifier":"deny-unminimize","description":"Denies the unminimize command without any pre-configured scope.","commands":{"allow":[],"deny":["unminimize"]}}},"permission_sets":{},"global_scope_schema":null},"dialog":{"default_permission":{"identifier":"default","description":"This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n","permissions":["allow-ask","allow-confirm","allow-message","allow-save","allow-open"]},"permissions":{"allow-ask":{"identifier":"allow-ask","description":"Enables the ask command without any pre-configured scope.","commands":{"allow":["ask"],"deny":[]}},"allow-confirm":{"identifier":"allow-confirm","description":"Enables the confirm command without any pre-configured scope.","commands":{"allow":["confirm"],"deny":[]}},"allow-message":{"identifier":"allow-message","description":"Enables the message command without any pre-configured scope.","commands":{"allow":["message"],"deny":[]}},"allow-open":{"identifier":"allow-open","description":"Enables the open command without any pre-configured scope.","commands":{"allow":["open"],"deny":[]}},"allow-save":{"identifier":"allow-save","description":"Enables the save command without any pre-configured scope.","commands":{"allow":["save"],"deny":[]}},"deny-ask":{"identifier":"deny-ask","description":"Denies the ask command without any pre-configured scope.","commands":{"allow":[],"deny":["ask"]}},"deny-confirm":{"identifier":"deny-confirm","description":"Denies the confirm command without any pre-configured scope.","commands":{"allow":[],"deny":["confirm"]}},"deny-message":{"identifier":"deny-message","description":"Denies the message command without any pre-configured scope.","commands":{"allow":[],"deny":["message"]}},"deny-open":{"identifier":"deny-open","description":"Denies the open command without any pre-configured scope.","commands":{"allow":[],"deny":["open"]}},"deny-save":{"identifier":"deny-save","description":"Denies the save command without any pre-configured scope.","commands":{"allow":[],"deny":["save"]}}},"permission_sets":{},"global_scope_schema":null},"shell":{"default_permission":{"identifier":"default","description":"This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n","permissions":["allow-open"]},"permissions":{"allow-execute":{"identifier":"allow-execute","description":"Enables the execute command without any pre-configured scope.","commands":{"allow":["execute"],"deny":[]}},"allow-kill":{"identifier":"allow-kill","description":"Enables the kill command without any pre-configured scope.","commands":{"allow":["kill"],"deny":[]}},"allow-open":{"identifier":"allow-open","description":"Enables the open command without any pre-configured scope.","commands":{"allow":["open"],"deny":[]}},"allow-spawn":{"identifier":"allow-spawn","description":"Enables the spawn command without any pre-configured scope.","commands":{"allow":["spawn"],"deny":[]}},"allow-stdin-write":{"identifier":"allow-stdin-write","description":"Enables the stdin_write command without any pre-configured scope.","commands":{"allow":["stdin_write"],"deny":[]}},"deny-execute":{"identifier":"deny-execute","description":"Denies the execute command without any pre-configured scope.","commands":{"allow":[],"deny":["execute"]}},"deny-kill":{"identifier":"deny-kill","description":"Denies the kill command without any pre-configured scope.","commands":{"allow":[],"deny":["kill"]}},"deny-open":{"identifier":"deny-open","description":"Denies the open command without any pre-configured scope.","commands":{"allow":[],"deny":["open"]}},"deny-spawn":{"identifier":"deny-spawn","description":"Denies the spawn command without any pre-configured scope.","commands":{"allow":[],"deny":["spawn"]}},"deny-stdin-write":{"identifier":"deny-stdin-write","description":"Denies the stdin_write command without any pre-configured scope.","commands":{"allow":[],"deny":["stdin_write"]}}},"permission_sets":{},"global_scope_schema":{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"additionalProperties":false,"properties":{"args":{"allOf":[{"$ref":"#/definitions/ShellScopeEntryAllowedArgs"}],"description":"The allowed arguments for the command execution."},"cmd":{"description":"The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.","type":"string"},"name":{"description":"The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.","type":"string"}},"required":["cmd","name"],"type":"object"},{"additionalProperties":false,"properties":{"args":{"allOf":[{"$ref":"#/definitions/ShellScopeEntryAllowedArgs"}],"description":"The allowed arguments for the command execution."},"name":{"description":"The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.","type":"string"},"sidecar":{"description":"If this command is a sidecar command.","type":"boolean"}},"required":["name","sidecar"],"type":"object"}],"definitions":{"ShellScopeEntryAllowedArg":{"anyOf":[{"description":"A non-configurable argument that is passed to the command in the order it was specified.","type":"string"},{"additionalProperties":false,"description":"A variable that is set while calling the command from the webview API.","properties":{"raw":{"default":false,"description":"Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.\n\nThis means the regex will not match on the entire string by default, which might be exploited if your regex allow unexpected input to be considered valid. When using this option, make sure your regex is correct.","type":"boolean"},"validator":{"description":"[regex] validator to require passed values to conform to an expected input.\n\nThis will require the argument value passed to this variable to match the `validator` regex before it will be executed.\n\nThe regex string is by default surrounded by `^...$` to match the full string. For example the `https?://\\w+` regex would be registered as `^https?://\\w+$`.\n\n[regex]: ","type":"string"}},"required":["validator"],"type":"object"}],"description":"A command argument allowed to be executed by the webview API."},"ShellScopeEntryAllowedArgs":{"anyOf":[{"description":"Use a simple boolean to allow all or disable all arguments to this command configuration.","type":"boolean"},{"description":"A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.","items":{"$ref":"#/definitions/ShellScopeEntryAllowedArg"},"type":"array"}],"description":"A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration."}},"description":"Shell scope entry.","title":"ShellScopeEntry"}}}
\ No newline at end of file
+{"autostart":{"default_permission":{"identifier":"default","description":"This permission set configures if your\napplication can enable or disable auto\nstarting the application on boot.\n\n#### Granted Permissions\n\nIt allows all to check, enable and\ndisable the automatic start on boot.\n\n","permissions":["allow-enable","allow-disable","allow-is-enabled"]},"permissions":{"allow-disable":{"identifier":"allow-disable","description":"Enables the disable command without any pre-configured scope.","commands":{"allow":["disable"],"deny":[]}},"allow-enable":{"identifier":"allow-enable","description":"Enables the enable command without any pre-configured scope.","commands":{"allow":["enable"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"deny-disable":{"identifier":"deny-disable","description":"Denies the disable command without any pre-configured scope.","commands":{"allow":[],"deny":["disable"]}},"deny-enable":{"identifier":"deny-enable","description":"Denies the enable command without any pre-configured scope.","commands":{"allow":[],"deny":["enable"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}}},"permission_sets":{},"global_scope_schema":null},"core":{"default_permission":{"identifier":"default","description":"Default core plugins set.","permissions":["core:path:default","core:event:default","core:window:default","core:webview:default","core:app:default","core:image:default","core:resources:default","core:menu:default","core:tray:default"]},"permissions":{},"permission_sets":{},"global_scope_schema":null},"core:app":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-version","allow-name","allow-tauri-version","allow-identifier","allow-bundle-type","allow-register-listener","allow-remove-listener"]},"permissions":{"allow-app-hide":{"identifier":"allow-app-hide","description":"Enables the app_hide command without any pre-configured scope.","commands":{"allow":["app_hide"],"deny":[]}},"allow-app-show":{"identifier":"allow-app-show","description":"Enables the app_show command without any pre-configured scope.","commands":{"allow":["app_show"],"deny":[]}},"allow-bundle-type":{"identifier":"allow-bundle-type","description":"Enables the bundle_type command without any pre-configured scope.","commands":{"allow":["bundle_type"],"deny":[]}},"allow-default-window-icon":{"identifier":"allow-default-window-icon","description":"Enables the default_window_icon command without any pre-configured scope.","commands":{"allow":["default_window_icon"],"deny":[]}},"allow-fetch-data-store-identifiers":{"identifier":"allow-fetch-data-store-identifiers","description":"Enables the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":["fetch_data_store_identifiers"],"deny":[]}},"allow-identifier":{"identifier":"allow-identifier","description":"Enables the identifier command without any pre-configured scope.","commands":{"allow":["identifier"],"deny":[]}},"allow-name":{"identifier":"allow-name","description":"Enables the name command without any pre-configured scope.","commands":{"allow":["name"],"deny":[]}},"allow-register-listener":{"identifier":"allow-register-listener","description":"Enables the register_listener command without any pre-configured scope.","commands":{"allow":["register_listener"],"deny":[]}},"allow-remove-data-store":{"identifier":"allow-remove-data-store","description":"Enables the remove_data_store command without any pre-configured scope.","commands":{"allow":["remove_data_store"],"deny":[]}},"allow-remove-listener":{"identifier":"allow-remove-listener","description":"Enables the remove_listener command without any pre-configured scope.","commands":{"allow":["remove_listener"],"deny":[]}},"allow-set-app-theme":{"identifier":"allow-set-app-theme","description":"Enables the set_app_theme command without any pre-configured scope.","commands":{"allow":["set_app_theme"],"deny":[]}},"allow-set-dock-visibility":{"identifier":"allow-set-dock-visibility","description":"Enables the set_dock_visibility command without any pre-configured scope.","commands":{"allow":["set_dock_visibility"],"deny":[]}},"allow-tauri-version":{"identifier":"allow-tauri-version","description":"Enables the tauri_version command without any pre-configured scope.","commands":{"allow":["tauri_version"],"deny":[]}},"allow-version":{"identifier":"allow-version","description":"Enables the version command without any pre-configured scope.","commands":{"allow":["version"],"deny":[]}},"deny-app-hide":{"identifier":"deny-app-hide","description":"Denies the app_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["app_hide"]}},"deny-app-show":{"identifier":"deny-app-show","description":"Denies the app_show command without any pre-configured scope.","commands":{"allow":[],"deny":["app_show"]}},"deny-bundle-type":{"identifier":"deny-bundle-type","description":"Denies the bundle_type command without any pre-configured scope.","commands":{"allow":[],"deny":["bundle_type"]}},"deny-default-window-icon":{"identifier":"deny-default-window-icon","description":"Denies the default_window_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["default_window_icon"]}},"deny-fetch-data-store-identifiers":{"identifier":"deny-fetch-data-store-identifiers","description":"Denies the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch_data_store_identifiers"]}},"deny-identifier":{"identifier":"deny-identifier","description":"Denies the identifier command without any pre-configured scope.","commands":{"allow":[],"deny":["identifier"]}},"deny-name":{"identifier":"deny-name","description":"Denies the name command without any pre-configured scope.","commands":{"allow":[],"deny":["name"]}},"deny-register-listener":{"identifier":"deny-register-listener","description":"Denies the register_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["register_listener"]}},"deny-remove-data-store":{"identifier":"deny-remove-data-store","description":"Denies the remove_data_store command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_data_store"]}},"deny-remove-listener":{"identifier":"deny-remove-listener","description":"Denies the remove_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_listener"]}},"deny-set-app-theme":{"identifier":"deny-set-app-theme","description":"Denies the set_app_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_app_theme"]}},"deny-set-dock-visibility":{"identifier":"deny-set-dock-visibility","description":"Denies the set_dock_visibility command without any pre-configured scope.","commands":{"allow":[],"deny":["set_dock_visibility"]}},"deny-tauri-version":{"identifier":"deny-tauri-version","description":"Denies the tauri_version command without any pre-configured scope.","commands":{"allow":[],"deny":["tauri_version"]}},"deny-version":{"identifier":"deny-version","description":"Denies the version command without any pre-configured scope.","commands":{"allow":[],"deny":["version"]}}},"permission_sets":{},"global_scope_schema":null},"core:event":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-listen","allow-unlisten","allow-emit","allow-emit-to"]},"permissions":{"allow-emit":{"identifier":"allow-emit","description":"Enables the emit command without any pre-configured scope.","commands":{"allow":["emit"],"deny":[]}},"allow-emit-to":{"identifier":"allow-emit-to","description":"Enables the emit_to command without any pre-configured scope.","commands":{"allow":["emit_to"],"deny":[]}},"allow-listen":{"identifier":"allow-listen","description":"Enables the listen command without any pre-configured scope.","commands":{"allow":["listen"],"deny":[]}},"allow-unlisten":{"identifier":"allow-unlisten","description":"Enables the unlisten command without any pre-configured scope.","commands":{"allow":["unlisten"],"deny":[]}},"deny-emit":{"identifier":"deny-emit","description":"Denies the emit command without any pre-configured scope.","commands":{"allow":[],"deny":["emit"]}},"deny-emit-to":{"identifier":"deny-emit-to","description":"Denies the emit_to command without any pre-configured scope.","commands":{"allow":[],"deny":["emit_to"]}},"deny-listen":{"identifier":"deny-listen","description":"Denies the listen command without any pre-configured scope.","commands":{"allow":[],"deny":["listen"]}},"deny-unlisten":{"identifier":"deny-unlisten","description":"Denies the unlisten command without any pre-configured scope.","commands":{"allow":[],"deny":["unlisten"]}}},"permission_sets":{},"global_scope_schema":null},"core:image":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-from-bytes","allow-from-path","allow-rgba","allow-size"]},"permissions":{"allow-from-bytes":{"identifier":"allow-from-bytes","description":"Enables the from_bytes command without any pre-configured scope.","commands":{"allow":["from_bytes"],"deny":[]}},"allow-from-path":{"identifier":"allow-from-path","description":"Enables the from_path command without any pre-configured scope.","commands":{"allow":["from_path"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-rgba":{"identifier":"allow-rgba","description":"Enables the rgba command without any pre-configured scope.","commands":{"allow":["rgba"],"deny":[]}},"allow-size":{"identifier":"allow-size","description":"Enables the size command without any pre-configured scope.","commands":{"allow":["size"],"deny":[]}},"deny-from-bytes":{"identifier":"deny-from-bytes","description":"Denies the from_bytes command without any pre-configured scope.","commands":{"allow":[],"deny":["from_bytes"]}},"deny-from-path":{"identifier":"deny-from-path","description":"Denies the from_path command without any pre-configured scope.","commands":{"allow":[],"deny":["from_path"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-rgba":{"identifier":"deny-rgba","description":"Denies the rgba command without any pre-configured scope.","commands":{"allow":[],"deny":["rgba"]}},"deny-size":{"identifier":"deny-size","description":"Denies the size command without any pre-configured scope.","commands":{"allow":[],"deny":["size"]}}},"permission_sets":{},"global_scope_schema":null},"core:menu":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-append","allow-prepend","allow-insert","allow-remove","allow-remove-at","allow-items","allow-get","allow-popup","allow-create-default","allow-set-as-app-menu","allow-set-as-window-menu","allow-text","allow-set-text","allow-is-enabled","allow-set-enabled","allow-set-accelerator","allow-set-as-windows-menu-for-nsapp","allow-set-as-help-menu-for-nsapp","allow-is-checked","allow-set-checked","allow-set-icon"]},"permissions":{"allow-append":{"identifier":"allow-append","description":"Enables the append command without any pre-configured scope.","commands":{"allow":["append"],"deny":[]}},"allow-create-default":{"identifier":"allow-create-default","description":"Enables the create_default command without any pre-configured scope.","commands":{"allow":["create_default"],"deny":[]}},"allow-get":{"identifier":"allow-get","description":"Enables the get command without any pre-configured scope.","commands":{"allow":["get"],"deny":[]}},"allow-insert":{"identifier":"allow-insert","description":"Enables the insert command without any pre-configured scope.","commands":{"allow":["insert"],"deny":[]}},"allow-is-checked":{"identifier":"allow-is-checked","description":"Enables the is_checked command without any pre-configured scope.","commands":{"allow":["is_checked"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-items":{"identifier":"allow-items","description":"Enables the items command without any pre-configured scope.","commands":{"allow":["items"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-popup":{"identifier":"allow-popup","description":"Enables the popup command without any pre-configured scope.","commands":{"allow":["popup"],"deny":[]}},"allow-prepend":{"identifier":"allow-prepend","description":"Enables the prepend command without any pre-configured scope.","commands":{"allow":["prepend"],"deny":[]}},"allow-remove":{"identifier":"allow-remove","description":"Enables the remove command without any pre-configured scope.","commands":{"allow":["remove"],"deny":[]}},"allow-remove-at":{"identifier":"allow-remove-at","description":"Enables the remove_at command without any pre-configured scope.","commands":{"allow":["remove_at"],"deny":[]}},"allow-set-accelerator":{"identifier":"allow-set-accelerator","description":"Enables the set_accelerator command without any pre-configured scope.","commands":{"allow":["set_accelerator"],"deny":[]}},"allow-set-as-app-menu":{"identifier":"allow-set-as-app-menu","description":"Enables the set_as_app_menu command without any pre-configured scope.","commands":{"allow":["set_as_app_menu"],"deny":[]}},"allow-set-as-help-menu-for-nsapp":{"identifier":"allow-set-as-help-menu-for-nsapp","description":"Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_help_menu_for_nsapp"],"deny":[]}},"allow-set-as-window-menu":{"identifier":"allow-set-as-window-menu","description":"Enables the set_as_window_menu command without any pre-configured scope.","commands":{"allow":["set_as_window_menu"],"deny":[]}},"allow-set-as-windows-menu-for-nsapp":{"identifier":"allow-set-as-windows-menu-for-nsapp","description":"Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_windows_menu_for_nsapp"],"deny":[]}},"allow-set-checked":{"identifier":"allow-set-checked","description":"Enables the set_checked command without any pre-configured scope.","commands":{"allow":["set_checked"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-text":{"identifier":"allow-set-text","description":"Enables the set_text command without any pre-configured scope.","commands":{"allow":["set_text"],"deny":[]}},"allow-text":{"identifier":"allow-text","description":"Enables the text command without any pre-configured scope.","commands":{"allow":["text"],"deny":[]}},"deny-append":{"identifier":"deny-append","description":"Denies the append command without any pre-configured scope.","commands":{"allow":[],"deny":["append"]}},"deny-create-default":{"identifier":"deny-create-default","description":"Denies the create_default command without any pre-configured scope.","commands":{"allow":[],"deny":["create_default"]}},"deny-get":{"identifier":"deny-get","description":"Denies the get command without any pre-configured scope.","commands":{"allow":[],"deny":["get"]}},"deny-insert":{"identifier":"deny-insert","description":"Denies the insert command without any pre-configured scope.","commands":{"allow":[],"deny":["insert"]}},"deny-is-checked":{"identifier":"deny-is-checked","description":"Denies the is_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["is_checked"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-items":{"identifier":"deny-items","description":"Denies the items command without any pre-configured scope.","commands":{"allow":[],"deny":["items"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-popup":{"identifier":"deny-popup","description":"Denies the popup command without any pre-configured scope.","commands":{"allow":[],"deny":["popup"]}},"deny-prepend":{"identifier":"deny-prepend","description":"Denies the prepend command without any pre-configured scope.","commands":{"allow":[],"deny":["prepend"]}},"deny-remove":{"identifier":"deny-remove","description":"Denies the remove command without any pre-configured scope.","commands":{"allow":[],"deny":["remove"]}},"deny-remove-at":{"identifier":"deny-remove-at","description":"Denies the remove_at command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_at"]}},"deny-set-accelerator":{"identifier":"deny-set-accelerator","description":"Denies the set_accelerator command without any pre-configured scope.","commands":{"allow":[],"deny":["set_accelerator"]}},"deny-set-as-app-menu":{"identifier":"deny-set-as-app-menu","description":"Denies the set_as_app_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_app_menu"]}},"deny-set-as-help-menu-for-nsapp":{"identifier":"deny-set-as-help-menu-for-nsapp","description":"Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_help_menu_for_nsapp"]}},"deny-set-as-window-menu":{"identifier":"deny-set-as-window-menu","description":"Denies the set_as_window_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_window_menu"]}},"deny-set-as-windows-menu-for-nsapp":{"identifier":"deny-set-as-windows-menu-for-nsapp","description":"Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_windows_menu_for_nsapp"]}},"deny-set-checked":{"identifier":"deny-set-checked","description":"Denies the set_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["set_checked"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-text":{"identifier":"deny-set-text","description":"Denies the set_text command without any pre-configured scope.","commands":{"allow":[],"deny":["set_text"]}},"deny-text":{"identifier":"deny-text","description":"Denies the text command without any pre-configured scope.","commands":{"allow":[],"deny":["text"]}}},"permission_sets":{},"global_scope_schema":null},"core:path":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-resolve-directory","allow-resolve","allow-normalize","allow-join","allow-dirname","allow-extname","allow-basename","allow-is-absolute"]},"permissions":{"allow-basename":{"identifier":"allow-basename","description":"Enables the basename command without any pre-configured scope.","commands":{"allow":["basename"],"deny":[]}},"allow-dirname":{"identifier":"allow-dirname","description":"Enables the dirname command without any pre-configured scope.","commands":{"allow":["dirname"],"deny":[]}},"allow-extname":{"identifier":"allow-extname","description":"Enables the extname command without any pre-configured scope.","commands":{"allow":["extname"],"deny":[]}},"allow-is-absolute":{"identifier":"allow-is-absolute","description":"Enables the is_absolute command without any pre-configured scope.","commands":{"allow":["is_absolute"],"deny":[]}},"allow-join":{"identifier":"allow-join","description":"Enables the join command without any pre-configured scope.","commands":{"allow":["join"],"deny":[]}},"allow-normalize":{"identifier":"allow-normalize","description":"Enables the normalize command without any pre-configured scope.","commands":{"allow":["normalize"],"deny":[]}},"allow-resolve":{"identifier":"allow-resolve","description":"Enables the resolve command without any pre-configured scope.","commands":{"allow":["resolve"],"deny":[]}},"allow-resolve-directory":{"identifier":"allow-resolve-directory","description":"Enables the resolve_directory command without any pre-configured scope.","commands":{"allow":["resolve_directory"],"deny":[]}},"deny-basename":{"identifier":"deny-basename","description":"Denies the basename command without any pre-configured scope.","commands":{"allow":[],"deny":["basename"]}},"deny-dirname":{"identifier":"deny-dirname","description":"Denies the dirname command without any pre-configured scope.","commands":{"allow":[],"deny":["dirname"]}},"deny-extname":{"identifier":"deny-extname","description":"Denies the extname command without any pre-configured scope.","commands":{"allow":[],"deny":["extname"]}},"deny-is-absolute":{"identifier":"deny-is-absolute","description":"Denies the is_absolute command without any pre-configured scope.","commands":{"allow":[],"deny":["is_absolute"]}},"deny-join":{"identifier":"deny-join","description":"Denies the join command without any pre-configured scope.","commands":{"allow":[],"deny":["join"]}},"deny-normalize":{"identifier":"deny-normalize","description":"Denies the normalize command without any pre-configured scope.","commands":{"allow":[],"deny":["normalize"]}},"deny-resolve":{"identifier":"deny-resolve","description":"Denies the resolve command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve"]}},"deny-resolve-directory":{"identifier":"deny-resolve-directory","description":"Denies the resolve_directory command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve_directory"]}}},"permission_sets":{},"global_scope_schema":null},"core:resources":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-close"]},"permissions":{"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}}},"permission_sets":{},"global_scope_schema":null},"core:tray":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-get-by-id","allow-remove-by-id","allow-set-icon","allow-set-menu","allow-set-tooltip","allow-set-title","allow-set-visible","allow-set-temp-dir-path","allow-set-icon-as-template","allow-set-show-menu-on-left-click"]},"permissions":{"allow-get-by-id":{"identifier":"allow-get-by-id","description":"Enables the get_by_id command without any pre-configured scope.","commands":{"allow":["get_by_id"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-remove-by-id":{"identifier":"allow-remove-by-id","description":"Enables the remove_by_id command without any pre-configured scope.","commands":{"allow":["remove_by_id"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-icon-as-template":{"identifier":"allow-set-icon-as-template","description":"Enables the set_icon_as_template command without any pre-configured scope.","commands":{"allow":["set_icon_as_template"],"deny":[]}},"allow-set-menu":{"identifier":"allow-set-menu","description":"Enables the set_menu command without any pre-configured scope.","commands":{"allow":["set_menu"],"deny":[]}},"allow-set-show-menu-on-left-click":{"identifier":"allow-set-show-menu-on-left-click","description":"Enables the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":["set_show_menu_on_left_click"],"deny":[]}},"allow-set-temp-dir-path":{"identifier":"allow-set-temp-dir-path","description":"Enables the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":["set_temp_dir_path"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-tooltip":{"identifier":"allow-set-tooltip","description":"Enables the set_tooltip command without any pre-configured scope.","commands":{"allow":["set_tooltip"],"deny":[]}},"allow-set-visible":{"identifier":"allow-set-visible","description":"Enables the set_visible command without any pre-configured scope.","commands":{"allow":["set_visible"],"deny":[]}},"deny-get-by-id":{"identifier":"deny-get-by-id","description":"Denies the get_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["get_by_id"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-remove-by-id":{"identifier":"deny-remove-by-id","description":"Denies the remove_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_by_id"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-icon-as-template":{"identifier":"deny-set-icon-as-template","description":"Denies the set_icon_as_template command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon_as_template"]}},"deny-set-menu":{"identifier":"deny-set-menu","description":"Denies the set_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_menu"]}},"deny-set-show-menu-on-left-click":{"identifier":"deny-set-show-menu-on-left-click","description":"Denies the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":[],"deny":["set_show_menu_on_left_click"]}},"deny-set-temp-dir-path":{"identifier":"deny-set-temp-dir-path","description":"Denies the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":[],"deny":["set_temp_dir_path"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-tooltip":{"identifier":"deny-set-tooltip","description":"Denies the set_tooltip command without any pre-configured scope.","commands":{"allow":[],"deny":["set_tooltip"]}},"deny-set-visible":{"identifier":"deny-set-visible","description":"Denies the set_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible"]}}},"permission_sets":{},"global_scope_schema":null},"core:webview":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-webviews","allow-webview-position","allow-webview-size","allow-internal-toggle-devtools"]},"permissions":{"allow-clear-all-browsing-data":{"identifier":"allow-clear-all-browsing-data","description":"Enables the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":["clear_all_browsing_data"],"deny":[]}},"allow-create-webview":{"identifier":"allow-create-webview","description":"Enables the create_webview command without any pre-configured scope.","commands":{"allow":["create_webview"],"deny":[]}},"allow-create-webview-window":{"identifier":"allow-create-webview-window","description":"Enables the create_webview_window command without any pre-configured scope.","commands":{"allow":["create_webview_window"],"deny":[]}},"allow-get-all-webviews":{"identifier":"allow-get-all-webviews","description":"Enables the get_all_webviews command without any pre-configured scope.","commands":{"allow":["get_all_webviews"],"deny":[]}},"allow-internal-toggle-devtools":{"identifier":"allow-internal-toggle-devtools","description":"Enables the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":["internal_toggle_devtools"],"deny":[]}},"allow-print":{"identifier":"allow-print","description":"Enables the print command without any pre-configured scope.","commands":{"allow":["print"],"deny":[]}},"allow-reparent":{"identifier":"allow-reparent","description":"Enables the reparent command without any pre-configured scope.","commands":{"allow":["reparent"],"deny":[]}},"allow-set-webview-auto-resize":{"identifier":"allow-set-webview-auto-resize","description":"Enables the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":["set_webview_auto_resize"],"deny":[]}},"allow-set-webview-background-color":{"identifier":"allow-set-webview-background-color","description":"Enables the set_webview_background_color command without any pre-configured scope.","commands":{"allow":["set_webview_background_color"],"deny":[]}},"allow-set-webview-focus":{"identifier":"allow-set-webview-focus","description":"Enables the set_webview_focus command without any pre-configured scope.","commands":{"allow":["set_webview_focus"],"deny":[]}},"allow-set-webview-position":{"identifier":"allow-set-webview-position","description":"Enables the set_webview_position command without any pre-configured scope.","commands":{"allow":["set_webview_position"],"deny":[]}},"allow-set-webview-size":{"identifier":"allow-set-webview-size","description":"Enables the set_webview_size command without any pre-configured scope.","commands":{"allow":["set_webview_size"],"deny":[]}},"allow-set-webview-zoom":{"identifier":"allow-set-webview-zoom","description":"Enables the set_webview_zoom command without any pre-configured scope.","commands":{"allow":["set_webview_zoom"],"deny":[]}},"allow-webview-close":{"identifier":"allow-webview-close","description":"Enables the webview_close command without any pre-configured scope.","commands":{"allow":["webview_close"],"deny":[]}},"allow-webview-hide":{"identifier":"allow-webview-hide","description":"Enables the webview_hide command without any pre-configured scope.","commands":{"allow":["webview_hide"],"deny":[]}},"allow-webview-position":{"identifier":"allow-webview-position","description":"Enables the webview_position command without any pre-configured scope.","commands":{"allow":["webview_position"],"deny":[]}},"allow-webview-show":{"identifier":"allow-webview-show","description":"Enables the webview_show command without any pre-configured scope.","commands":{"allow":["webview_show"],"deny":[]}},"allow-webview-size":{"identifier":"allow-webview-size","description":"Enables the webview_size command without any pre-configured scope.","commands":{"allow":["webview_size"],"deny":[]}},"deny-clear-all-browsing-data":{"identifier":"deny-clear-all-browsing-data","description":"Denies the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":[],"deny":["clear_all_browsing_data"]}},"deny-create-webview":{"identifier":"deny-create-webview","description":"Denies the create_webview command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview"]}},"deny-create-webview-window":{"identifier":"deny-create-webview-window","description":"Denies the create_webview_window command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview_window"]}},"deny-get-all-webviews":{"identifier":"deny-get-all-webviews","description":"Denies the get_all_webviews command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_webviews"]}},"deny-internal-toggle-devtools":{"identifier":"deny-internal-toggle-devtools","description":"Denies the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_devtools"]}},"deny-print":{"identifier":"deny-print","description":"Denies the print command without any pre-configured scope.","commands":{"allow":[],"deny":["print"]}},"deny-reparent":{"identifier":"deny-reparent","description":"Denies the reparent command without any pre-configured scope.","commands":{"allow":[],"deny":["reparent"]}},"deny-set-webview-auto-resize":{"identifier":"deny-set-webview-auto-resize","description":"Denies the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_auto_resize"]}},"deny-set-webview-background-color":{"identifier":"deny-set-webview-background-color","description":"Denies the set_webview_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_background_color"]}},"deny-set-webview-focus":{"identifier":"deny-set-webview-focus","description":"Denies the set_webview_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_focus"]}},"deny-set-webview-position":{"identifier":"deny-set-webview-position","description":"Denies the set_webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_position"]}},"deny-set-webview-size":{"identifier":"deny-set-webview-size","description":"Denies the set_webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_size"]}},"deny-set-webview-zoom":{"identifier":"deny-set-webview-zoom","description":"Denies the set_webview_zoom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_zoom"]}},"deny-webview-close":{"identifier":"deny-webview-close","description":"Denies the webview_close command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_close"]}},"deny-webview-hide":{"identifier":"deny-webview-hide","description":"Denies the webview_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_hide"]}},"deny-webview-position":{"identifier":"deny-webview-position","description":"Denies the webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_position"]}},"deny-webview-show":{"identifier":"deny-webview-show","description":"Denies the webview_show command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_show"]}},"deny-webview-size":{"identifier":"deny-webview-size","description":"Denies the webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_size"]}}},"permission_sets":{},"global_scope_schema":null},"core:window":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-windows","allow-scale-factor","allow-inner-position","allow-outer-position","allow-inner-size","allow-outer-size","allow-is-fullscreen","allow-is-minimized","allow-is-maximized","allow-is-focused","allow-is-decorated","allow-is-resizable","allow-is-maximizable","allow-is-minimizable","allow-is-closable","allow-is-visible","allow-is-enabled","allow-title","allow-current-monitor","allow-primary-monitor","allow-monitor-from-point","allow-available-monitors","allow-cursor-position","allow-theme","allow-is-always-on-top","allow-internal-toggle-maximize"]},"permissions":{"allow-available-monitors":{"identifier":"allow-available-monitors","description":"Enables the available_monitors command without any pre-configured scope.","commands":{"allow":["available_monitors"],"deny":[]}},"allow-center":{"identifier":"allow-center","description":"Enables the center command without any pre-configured scope.","commands":{"allow":["center"],"deny":[]}},"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"allow-create":{"identifier":"allow-create","description":"Enables the create command without any pre-configured scope.","commands":{"allow":["create"],"deny":[]}},"allow-current-monitor":{"identifier":"allow-current-monitor","description":"Enables the current_monitor command without any pre-configured scope.","commands":{"allow":["current_monitor"],"deny":[]}},"allow-cursor-position":{"identifier":"allow-cursor-position","description":"Enables the cursor_position command without any pre-configured scope.","commands":{"allow":["cursor_position"],"deny":[]}},"allow-destroy":{"identifier":"allow-destroy","description":"Enables the destroy command without any pre-configured scope.","commands":{"allow":["destroy"],"deny":[]}},"allow-get-all-windows":{"identifier":"allow-get-all-windows","description":"Enables the get_all_windows command without any pre-configured scope.","commands":{"allow":["get_all_windows"],"deny":[]}},"allow-hide":{"identifier":"allow-hide","description":"Enables the hide command without any pre-configured scope.","commands":{"allow":["hide"],"deny":[]}},"allow-inner-position":{"identifier":"allow-inner-position","description":"Enables the inner_position command without any pre-configured scope.","commands":{"allow":["inner_position"],"deny":[]}},"allow-inner-size":{"identifier":"allow-inner-size","description":"Enables the inner_size command without any pre-configured scope.","commands":{"allow":["inner_size"],"deny":[]}},"allow-internal-toggle-maximize":{"identifier":"allow-internal-toggle-maximize","description":"Enables the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":["internal_toggle_maximize"],"deny":[]}},"allow-is-always-on-top":{"identifier":"allow-is-always-on-top","description":"Enables the is_always_on_top command without any pre-configured scope.","commands":{"allow":["is_always_on_top"],"deny":[]}},"allow-is-closable":{"identifier":"allow-is-closable","description":"Enables the is_closable command without any pre-configured scope.","commands":{"allow":["is_closable"],"deny":[]}},"allow-is-decorated":{"identifier":"allow-is-decorated","description":"Enables the is_decorated command without any pre-configured scope.","commands":{"allow":["is_decorated"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-is-focused":{"identifier":"allow-is-focused","description":"Enables the is_focused command without any pre-configured scope.","commands":{"allow":["is_focused"],"deny":[]}},"allow-is-fullscreen":{"identifier":"allow-is-fullscreen","description":"Enables the is_fullscreen command without any pre-configured scope.","commands":{"allow":["is_fullscreen"],"deny":[]}},"allow-is-maximizable":{"identifier":"allow-is-maximizable","description":"Enables the is_maximizable command without any pre-configured scope.","commands":{"allow":["is_maximizable"],"deny":[]}},"allow-is-maximized":{"identifier":"allow-is-maximized","description":"Enables the is_maximized command without any pre-configured scope.","commands":{"allow":["is_maximized"],"deny":[]}},"allow-is-minimizable":{"identifier":"allow-is-minimizable","description":"Enables the is_minimizable command without any pre-configured scope.","commands":{"allow":["is_minimizable"],"deny":[]}},"allow-is-minimized":{"identifier":"allow-is-minimized","description":"Enables the is_minimized command without any pre-configured scope.","commands":{"allow":["is_minimized"],"deny":[]}},"allow-is-resizable":{"identifier":"allow-is-resizable","description":"Enables the is_resizable command without any pre-configured scope.","commands":{"allow":["is_resizable"],"deny":[]}},"allow-is-visible":{"identifier":"allow-is-visible","description":"Enables the is_visible command without any pre-configured scope.","commands":{"allow":["is_visible"],"deny":[]}},"allow-maximize":{"identifier":"allow-maximize","description":"Enables the maximize command without any pre-configured scope.","commands":{"allow":["maximize"],"deny":[]}},"allow-minimize":{"identifier":"allow-minimize","description":"Enables the minimize command without any pre-configured scope.","commands":{"allow":["minimize"],"deny":[]}},"allow-monitor-from-point":{"identifier":"allow-monitor-from-point","description":"Enables the monitor_from_point command without any pre-configured scope.","commands":{"allow":["monitor_from_point"],"deny":[]}},"allow-outer-position":{"identifier":"allow-outer-position","description":"Enables the outer_position command without any pre-configured scope.","commands":{"allow":["outer_position"],"deny":[]}},"allow-outer-size":{"identifier":"allow-outer-size","description":"Enables the outer_size command without any pre-configured scope.","commands":{"allow":["outer_size"],"deny":[]}},"allow-primary-monitor":{"identifier":"allow-primary-monitor","description":"Enables the primary_monitor command without any pre-configured scope.","commands":{"allow":["primary_monitor"],"deny":[]}},"allow-request-user-attention":{"identifier":"allow-request-user-attention","description":"Enables the request_user_attention command without any pre-configured scope.","commands":{"allow":["request_user_attention"],"deny":[]}},"allow-scale-factor":{"identifier":"allow-scale-factor","description":"Enables the scale_factor command without any pre-configured scope.","commands":{"allow":["scale_factor"],"deny":[]}},"allow-set-always-on-bottom":{"identifier":"allow-set-always-on-bottom","description":"Enables the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":["set_always_on_bottom"],"deny":[]}},"allow-set-always-on-top":{"identifier":"allow-set-always-on-top","description":"Enables the set_always_on_top command without any pre-configured scope.","commands":{"allow":["set_always_on_top"],"deny":[]}},"allow-set-background-color":{"identifier":"allow-set-background-color","description":"Enables the set_background_color command without any pre-configured scope.","commands":{"allow":["set_background_color"],"deny":[]}},"allow-set-badge-count":{"identifier":"allow-set-badge-count","description":"Enables the set_badge_count command without any pre-configured scope.","commands":{"allow":["set_badge_count"],"deny":[]}},"allow-set-badge-label":{"identifier":"allow-set-badge-label","description":"Enables the set_badge_label command without any pre-configured scope.","commands":{"allow":["set_badge_label"],"deny":[]}},"allow-set-closable":{"identifier":"allow-set-closable","description":"Enables the set_closable command without any pre-configured scope.","commands":{"allow":["set_closable"],"deny":[]}},"allow-set-content-protected":{"identifier":"allow-set-content-protected","description":"Enables the set_content_protected command without any pre-configured scope.","commands":{"allow":["set_content_protected"],"deny":[]}},"allow-set-cursor-grab":{"identifier":"allow-set-cursor-grab","description":"Enables the set_cursor_grab command without any pre-configured scope.","commands":{"allow":["set_cursor_grab"],"deny":[]}},"allow-set-cursor-icon":{"identifier":"allow-set-cursor-icon","description":"Enables the set_cursor_icon command without any pre-configured scope.","commands":{"allow":["set_cursor_icon"],"deny":[]}},"allow-set-cursor-position":{"identifier":"allow-set-cursor-position","description":"Enables the set_cursor_position command without any pre-configured scope.","commands":{"allow":["set_cursor_position"],"deny":[]}},"allow-set-cursor-visible":{"identifier":"allow-set-cursor-visible","description":"Enables the set_cursor_visible command without any pre-configured scope.","commands":{"allow":["set_cursor_visible"],"deny":[]}},"allow-set-decorations":{"identifier":"allow-set-decorations","description":"Enables the set_decorations command without any pre-configured scope.","commands":{"allow":["set_decorations"],"deny":[]}},"allow-set-effects":{"identifier":"allow-set-effects","description":"Enables the set_effects command without any pre-configured scope.","commands":{"allow":["set_effects"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-focus":{"identifier":"allow-set-focus","description":"Enables the set_focus command without any pre-configured scope.","commands":{"allow":["set_focus"],"deny":[]}},"allow-set-focusable":{"identifier":"allow-set-focusable","description":"Enables the set_focusable command without any pre-configured scope.","commands":{"allow":["set_focusable"],"deny":[]}},"allow-set-fullscreen":{"identifier":"allow-set-fullscreen","description":"Enables the set_fullscreen command without any pre-configured scope.","commands":{"allow":["set_fullscreen"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-ignore-cursor-events":{"identifier":"allow-set-ignore-cursor-events","description":"Enables the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":["set_ignore_cursor_events"],"deny":[]}},"allow-set-max-size":{"identifier":"allow-set-max-size","description":"Enables the set_max_size command without any pre-configured scope.","commands":{"allow":["set_max_size"],"deny":[]}},"allow-set-maximizable":{"identifier":"allow-set-maximizable","description":"Enables the set_maximizable command without any pre-configured scope.","commands":{"allow":["set_maximizable"],"deny":[]}},"allow-set-min-size":{"identifier":"allow-set-min-size","description":"Enables the set_min_size command without any pre-configured scope.","commands":{"allow":["set_min_size"],"deny":[]}},"allow-set-minimizable":{"identifier":"allow-set-minimizable","description":"Enables the set_minimizable command without any pre-configured scope.","commands":{"allow":["set_minimizable"],"deny":[]}},"allow-set-overlay-icon":{"identifier":"allow-set-overlay-icon","description":"Enables the set_overlay_icon command without any pre-configured scope.","commands":{"allow":["set_overlay_icon"],"deny":[]}},"allow-set-position":{"identifier":"allow-set-position","description":"Enables the set_position command without any pre-configured scope.","commands":{"allow":["set_position"],"deny":[]}},"allow-set-progress-bar":{"identifier":"allow-set-progress-bar","description":"Enables the set_progress_bar command without any pre-configured scope.","commands":{"allow":["set_progress_bar"],"deny":[]}},"allow-set-resizable":{"identifier":"allow-set-resizable","description":"Enables the set_resizable command without any pre-configured scope.","commands":{"allow":["set_resizable"],"deny":[]}},"allow-set-shadow":{"identifier":"allow-set-shadow","description":"Enables the set_shadow command without any pre-configured scope.","commands":{"allow":["set_shadow"],"deny":[]}},"allow-set-simple-fullscreen":{"identifier":"allow-set-simple-fullscreen","description":"Enables the set_simple_fullscreen command without any pre-configured scope.","commands":{"allow":["set_simple_fullscreen"],"deny":[]}},"allow-set-size":{"identifier":"allow-set-size","description":"Enables the set_size command without any pre-configured scope.","commands":{"allow":["set_size"],"deny":[]}},"allow-set-size-constraints":{"identifier":"allow-set-size-constraints","description":"Enables the set_size_constraints command without any pre-configured scope.","commands":{"allow":["set_size_constraints"],"deny":[]}},"allow-set-skip-taskbar":{"identifier":"allow-set-skip-taskbar","description":"Enables the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":["set_skip_taskbar"],"deny":[]}},"allow-set-theme":{"identifier":"allow-set-theme","description":"Enables the set_theme command without any pre-configured scope.","commands":{"allow":["set_theme"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-title-bar-style":{"identifier":"allow-set-title-bar-style","description":"Enables the set_title_bar_style command without any pre-configured scope.","commands":{"allow":["set_title_bar_style"],"deny":[]}},"allow-set-visible-on-all-workspaces":{"identifier":"allow-set-visible-on-all-workspaces","description":"Enables the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":["set_visible_on_all_workspaces"],"deny":[]}},"allow-show":{"identifier":"allow-show","description":"Enables the show command without any pre-configured scope.","commands":{"allow":["show"],"deny":[]}},"allow-start-dragging":{"identifier":"allow-start-dragging","description":"Enables the start_dragging command without any pre-configured scope.","commands":{"allow":["start_dragging"],"deny":[]}},"allow-start-resize-dragging":{"identifier":"allow-start-resize-dragging","description":"Enables the start_resize_dragging command without any pre-configured scope.","commands":{"allow":["start_resize_dragging"],"deny":[]}},"allow-theme":{"identifier":"allow-theme","description":"Enables the theme command without any pre-configured scope.","commands":{"allow":["theme"],"deny":[]}},"allow-title":{"identifier":"allow-title","description":"Enables the title command without any pre-configured scope.","commands":{"allow":["title"],"deny":[]}},"allow-toggle-maximize":{"identifier":"allow-toggle-maximize","description":"Enables the toggle_maximize command without any pre-configured scope.","commands":{"allow":["toggle_maximize"],"deny":[]}},"allow-unmaximize":{"identifier":"allow-unmaximize","description":"Enables the unmaximize command without any pre-configured scope.","commands":{"allow":["unmaximize"],"deny":[]}},"allow-unminimize":{"identifier":"allow-unminimize","description":"Enables the unminimize command without any pre-configured scope.","commands":{"allow":["unminimize"],"deny":[]}},"deny-available-monitors":{"identifier":"deny-available-monitors","description":"Denies the available_monitors command without any pre-configured scope.","commands":{"allow":[],"deny":["available_monitors"]}},"deny-center":{"identifier":"deny-center","description":"Denies the center command without any pre-configured scope.","commands":{"allow":[],"deny":["center"]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}},"deny-create":{"identifier":"deny-create","description":"Denies the create command without any pre-configured scope.","commands":{"allow":[],"deny":["create"]}},"deny-current-monitor":{"identifier":"deny-current-monitor","description":"Denies the current_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["current_monitor"]}},"deny-cursor-position":{"identifier":"deny-cursor-position","description":"Denies the cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["cursor_position"]}},"deny-destroy":{"identifier":"deny-destroy","description":"Denies the destroy command without any pre-configured scope.","commands":{"allow":[],"deny":["destroy"]}},"deny-get-all-windows":{"identifier":"deny-get-all-windows","description":"Denies the get_all_windows command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_windows"]}},"deny-hide":{"identifier":"deny-hide","description":"Denies the hide command without any pre-configured scope.","commands":{"allow":[],"deny":["hide"]}},"deny-inner-position":{"identifier":"deny-inner-position","description":"Denies the inner_position command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_position"]}},"deny-inner-size":{"identifier":"deny-inner-size","description":"Denies the inner_size command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_size"]}},"deny-internal-toggle-maximize":{"identifier":"deny-internal-toggle-maximize","description":"Denies the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_maximize"]}},"deny-is-always-on-top":{"identifier":"deny-is-always-on-top","description":"Denies the is_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["is_always_on_top"]}},"deny-is-closable":{"identifier":"deny-is-closable","description":"Denies the is_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_closable"]}},"deny-is-decorated":{"identifier":"deny-is-decorated","description":"Denies the is_decorated command without any pre-configured scope.","commands":{"allow":[],"deny":["is_decorated"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-is-focused":{"identifier":"deny-is-focused","description":"Denies the is_focused command without any pre-configured scope.","commands":{"allow":[],"deny":["is_focused"]}},"deny-is-fullscreen":{"identifier":"deny-is-fullscreen","description":"Denies the is_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["is_fullscreen"]}},"deny-is-maximizable":{"identifier":"deny-is-maximizable","description":"Denies the is_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximizable"]}},"deny-is-maximized":{"identifier":"deny-is-maximized","description":"Denies the is_maximized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximized"]}},"deny-is-minimizable":{"identifier":"deny-is-minimizable","description":"Denies the is_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimizable"]}},"deny-is-minimized":{"identifier":"deny-is-minimized","description":"Denies the is_minimized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimized"]}},"deny-is-resizable":{"identifier":"deny-is-resizable","description":"Denies the is_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_resizable"]}},"deny-is-visible":{"identifier":"deny-is-visible","description":"Denies the is_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["is_visible"]}},"deny-maximize":{"identifier":"deny-maximize","description":"Denies the maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["maximize"]}},"deny-minimize":{"identifier":"deny-minimize","description":"Denies the minimize command without any pre-configured scope.","commands":{"allow":[],"deny":["minimize"]}},"deny-monitor-from-point":{"identifier":"deny-monitor-from-point","description":"Denies the monitor_from_point command without any pre-configured scope.","commands":{"allow":[],"deny":["monitor_from_point"]}},"deny-outer-position":{"identifier":"deny-outer-position","description":"Denies the outer_position command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_position"]}},"deny-outer-size":{"identifier":"deny-outer-size","description":"Denies the outer_size command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_size"]}},"deny-primary-monitor":{"identifier":"deny-primary-monitor","description":"Denies the primary_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["primary_monitor"]}},"deny-request-user-attention":{"identifier":"deny-request-user-attention","description":"Denies the request_user_attention command without any pre-configured scope.","commands":{"allow":[],"deny":["request_user_attention"]}},"deny-scale-factor":{"identifier":"deny-scale-factor","description":"Denies the scale_factor command without any pre-configured scope.","commands":{"allow":[],"deny":["scale_factor"]}},"deny-set-always-on-bottom":{"identifier":"deny-set-always-on-bottom","description":"Denies the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_bottom"]}},"deny-set-always-on-top":{"identifier":"deny-set-always-on-top","description":"Denies the set_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_top"]}},"deny-set-background-color":{"identifier":"deny-set-background-color","description":"Denies the set_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_background_color"]}},"deny-set-badge-count":{"identifier":"deny-set-badge-count","description":"Denies the set_badge_count command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_count"]}},"deny-set-badge-label":{"identifier":"deny-set-badge-label","description":"Denies the set_badge_label command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_label"]}},"deny-set-closable":{"identifier":"deny-set-closable","description":"Denies the set_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_closable"]}},"deny-set-content-protected":{"identifier":"deny-set-content-protected","description":"Denies the set_content_protected command without any pre-configured scope.","commands":{"allow":[],"deny":["set_content_protected"]}},"deny-set-cursor-grab":{"identifier":"deny-set-cursor-grab","description":"Denies the set_cursor_grab command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_grab"]}},"deny-set-cursor-icon":{"identifier":"deny-set-cursor-icon","description":"Denies the set_cursor_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_icon"]}},"deny-set-cursor-position":{"identifier":"deny-set-cursor-position","description":"Denies the set_cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_position"]}},"deny-set-cursor-visible":{"identifier":"deny-set-cursor-visible","description":"Denies the set_cursor_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_visible"]}},"deny-set-decorations":{"identifier":"deny-set-decorations","description":"Denies the set_decorations command without any pre-configured scope.","commands":{"allow":[],"deny":["set_decorations"]}},"deny-set-effects":{"identifier":"deny-set-effects","description":"Denies the set_effects command without any pre-configured scope.","commands":{"allow":[],"deny":["set_effects"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-focus":{"identifier":"deny-set-focus","description":"Denies the set_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focus"]}},"deny-set-focusable":{"identifier":"deny-set-focusable","description":"Denies the set_focusable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focusable"]}},"deny-set-fullscreen":{"identifier":"deny-set-fullscreen","description":"Denies the set_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_fullscreen"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-ignore-cursor-events":{"identifier":"deny-set-ignore-cursor-events","description":"Denies the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":[],"deny":["set_ignore_cursor_events"]}},"deny-set-max-size":{"identifier":"deny-set-max-size","description":"Denies the set_max_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_max_size"]}},"deny-set-maximizable":{"identifier":"deny-set-maximizable","description":"Denies the set_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_maximizable"]}},"deny-set-min-size":{"identifier":"deny-set-min-size","description":"Denies the set_min_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_min_size"]}},"deny-set-minimizable":{"identifier":"deny-set-minimizable","description":"Denies the set_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_minimizable"]}},"deny-set-overlay-icon":{"identifier":"deny-set-overlay-icon","description":"Denies the set_overlay_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_overlay_icon"]}},"deny-set-position":{"identifier":"deny-set-position","description":"Denies the set_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_position"]}},"deny-set-progress-bar":{"identifier":"deny-set-progress-bar","description":"Denies the set_progress_bar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_progress_bar"]}},"deny-set-resizable":{"identifier":"deny-set-resizable","description":"Denies the set_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_resizable"]}},"deny-set-shadow":{"identifier":"deny-set-shadow","description":"Denies the set_shadow command without any pre-configured scope.","commands":{"allow":[],"deny":["set_shadow"]}},"deny-set-simple-fullscreen":{"identifier":"deny-set-simple-fullscreen","description":"Denies the set_simple_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_simple_fullscreen"]}},"deny-set-size":{"identifier":"deny-set-size","description":"Denies the set_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size"]}},"deny-set-size-constraints":{"identifier":"deny-set-size-constraints","description":"Denies the set_size_constraints command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size_constraints"]}},"deny-set-skip-taskbar":{"identifier":"deny-set-skip-taskbar","description":"Denies the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_skip_taskbar"]}},"deny-set-theme":{"identifier":"deny-set-theme","description":"Denies the set_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_theme"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-title-bar-style":{"identifier":"deny-set-title-bar-style","description":"Denies the set_title_bar_style command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title_bar_style"]}},"deny-set-visible-on-all-workspaces":{"identifier":"deny-set-visible-on-all-workspaces","description":"Denies the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible_on_all_workspaces"]}},"deny-show":{"identifier":"deny-show","description":"Denies the show command without any pre-configured scope.","commands":{"allow":[],"deny":["show"]}},"deny-start-dragging":{"identifier":"deny-start-dragging","description":"Denies the start_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_dragging"]}},"deny-start-resize-dragging":{"identifier":"deny-start-resize-dragging","description":"Denies the start_resize_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_resize_dragging"]}},"deny-theme":{"identifier":"deny-theme","description":"Denies the theme command without any pre-configured scope.","commands":{"allow":[],"deny":["theme"]}},"deny-title":{"identifier":"deny-title","description":"Denies the title command without any pre-configured scope.","commands":{"allow":[],"deny":["title"]}},"deny-toggle-maximize":{"identifier":"deny-toggle-maximize","description":"Denies the toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["toggle_maximize"]}},"deny-unmaximize":{"identifier":"deny-unmaximize","description":"Denies the unmaximize command without any pre-configured scope.","commands":{"allow":[],"deny":["unmaximize"]}},"deny-unminimize":{"identifier":"deny-unminimize","description":"Denies the unminimize command without any pre-configured scope.","commands":{"allow":[],"deny":["unminimize"]}}},"permission_sets":{},"global_scope_schema":null},"deep-link":{"default_permission":{"identifier":"default","description":"Allows reading the opened deep link via the get_current command","permissions":["allow-get-current"]},"permissions":{"allow-get-current":{"identifier":"allow-get-current","description":"Enables the get_current command without any pre-configured scope.","commands":{"allow":["get_current"],"deny":[]}},"allow-is-registered":{"identifier":"allow-is-registered","description":"Enables the is_registered command without any pre-configured scope.","commands":{"allow":["is_registered"],"deny":[]}},"allow-register":{"identifier":"allow-register","description":"Enables the register command without any pre-configured scope.","commands":{"allow":["register"],"deny":[]}},"allow-unregister":{"identifier":"allow-unregister","description":"Enables the unregister command without any pre-configured scope.","commands":{"allow":["unregister"],"deny":[]}},"deny-get-current":{"identifier":"deny-get-current","description":"Denies the get_current command without any pre-configured scope.","commands":{"allow":[],"deny":["get_current"]}},"deny-is-registered":{"identifier":"deny-is-registered","description":"Denies the is_registered command without any pre-configured scope.","commands":{"allow":[],"deny":["is_registered"]}},"deny-register":{"identifier":"deny-register","description":"Denies the register command without any pre-configured scope.","commands":{"allow":[],"deny":["register"]}},"deny-unregister":{"identifier":"deny-unregister","description":"Denies the unregister command without any pre-configured scope.","commands":{"allow":[],"deny":["unregister"]}}},"permission_sets":{},"global_scope_schema":null},"dialog":{"default_permission":{"identifier":"default","description":"This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n","permissions":["allow-ask","allow-confirm","allow-message","allow-save","allow-open"]},"permissions":{"allow-ask":{"identifier":"allow-ask","description":"Enables the ask command without any pre-configured scope.","commands":{"allow":["ask"],"deny":[]}},"allow-confirm":{"identifier":"allow-confirm","description":"Enables the confirm command without any pre-configured scope.","commands":{"allow":["confirm"],"deny":[]}},"allow-message":{"identifier":"allow-message","description":"Enables the message command without any pre-configured scope.","commands":{"allow":["message"],"deny":[]}},"allow-open":{"identifier":"allow-open","description":"Enables the open command without any pre-configured scope.","commands":{"allow":["open"],"deny":[]}},"allow-save":{"identifier":"allow-save","description":"Enables the save command without any pre-configured scope.","commands":{"allow":["save"],"deny":[]}},"deny-ask":{"identifier":"deny-ask","description":"Denies the ask command without any pre-configured scope.","commands":{"allow":[],"deny":["ask"]}},"deny-confirm":{"identifier":"deny-confirm","description":"Denies the confirm command without any pre-configured scope.","commands":{"allow":[],"deny":["confirm"]}},"deny-message":{"identifier":"deny-message","description":"Denies the message command without any pre-configured scope.","commands":{"allow":[],"deny":["message"]}},"deny-open":{"identifier":"deny-open","description":"Denies the open command without any pre-configured scope.","commands":{"allow":[],"deny":["open"]}},"deny-save":{"identifier":"deny-save","description":"Denies the save command without any pre-configured scope.","commands":{"allow":[],"deny":["save"]}}},"permission_sets":{},"global_scope_schema":null},"shell":{"default_permission":{"identifier":"default","description":"This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n","permissions":["allow-open"]},"permissions":{"allow-execute":{"identifier":"allow-execute","description":"Enables the execute command without any pre-configured scope.","commands":{"allow":["execute"],"deny":[]}},"allow-kill":{"identifier":"allow-kill","description":"Enables the kill command without any pre-configured scope.","commands":{"allow":["kill"],"deny":[]}},"allow-open":{"identifier":"allow-open","description":"Enables the open command without any pre-configured scope.","commands":{"allow":["open"],"deny":[]}},"allow-spawn":{"identifier":"allow-spawn","description":"Enables the spawn command without any pre-configured scope.","commands":{"allow":["spawn"],"deny":[]}},"allow-stdin-write":{"identifier":"allow-stdin-write","description":"Enables the stdin_write command without any pre-configured scope.","commands":{"allow":["stdin_write"],"deny":[]}},"deny-execute":{"identifier":"deny-execute","description":"Denies the execute command without any pre-configured scope.","commands":{"allow":[],"deny":["execute"]}},"deny-kill":{"identifier":"deny-kill","description":"Denies the kill command without any pre-configured scope.","commands":{"allow":[],"deny":["kill"]}},"deny-open":{"identifier":"deny-open","description":"Denies the open command without any pre-configured scope.","commands":{"allow":[],"deny":["open"]}},"deny-spawn":{"identifier":"deny-spawn","description":"Denies the spawn command without any pre-configured scope.","commands":{"allow":[],"deny":["spawn"]}},"deny-stdin-write":{"identifier":"deny-stdin-write","description":"Denies the stdin_write command without any pre-configured scope.","commands":{"allow":[],"deny":["stdin_write"]}}},"permission_sets":{},"global_scope_schema":{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"additionalProperties":false,"properties":{"args":{"allOf":[{"$ref":"#/definitions/ShellScopeEntryAllowedArgs"}],"description":"The allowed arguments for the command execution."},"cmd":{"description":"The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.","type":"string"},"name":{"description":"The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.","type":"string"}},"required":["cmd","name"],"type":"object"},{"additionalProperties":false,"properties":{"args":{"allOf":[{"$ref":"#/definitions/ShellScopeEntryAllowedArgs"}],"description":"The allowed arguments for the command execution."},"name":{"description":"The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.","type":"string"},"sidecar":{"description":"If this command is a sidecar command.","type":"boolean"}},"required":["name","sidecar"],"type":"object"}],"definitions":{"ShellScopeEntryAllowedArg":{"anyOf":[{"description":"A non-configurable argument that is passed to the command in the order it was specified.","type":"string"},{"additionalProperties":false,"description":"A variable that is set while calling the command from the webview API.","properties":{"raw":{"default":false,"description":"Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.\n\nThis means the regex will not match on the entire string by default, which might be exploited if your regex allow unexpected input to be considered valid. When using this option, make sure your regex is correct.","type":"boolean"},"validator":{"description":"[regex] validator to require passed values to conform to an expected input.\n\nThis will require the argument value passed to this variable to match the `validator` regex before it will be executed.\n\nThe regex string is by default surrounded by `^...$` to match the full string. For example the `https?://\\w+` regex would be registered as `^https?://\\w+$`.\n\n[regex]: ","type":"string"}},"required":["validator"],"type":"object"}],"description":"A command argument allowed to be executed by the webview API."},"ShellScopeEntryAllowedArgs":{"anyOf":[{"description":"Use a simple boolean to allow all or disable all arguments to this command configuration.","type":"boolean"},{"description":"A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.","items":{"$ref":"#/definitions/ShellScopeEntryAllowedArg"},"type":"array"}],"description":"A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration."}},"description":"Shell scope entry.","title":"ShellScopeEntry"}}}
\ No newline at end of file
diff --git a/src-tauri/gen/schemas/desktop-schema.json b/src-tauri/gen/schemas/desktop-schema.json
index 0a45d9c6b..0ad9f7d35 100644
--- a/src-tauri/gen/schemas/desktop-schema.json
+++ b/src-tauri/gen/schemas/desktop-schema.json
@@ -2396,6 +2396,60 @@
"const": "core:window:deny-unminimize",
"markdownDescription": "Denies the unminimize command without any pre-configured scope."
},
+ {
+ "description": "Allows reading the opened deep link via the get_current command\n#### This default permission set includes:\n\n- `allow-get-current`",
+ "type": "string",
+ "const": "deep-link:default",
+ "markdownDescription": "Allows reading the opened deep link via the get_current command\n#### This default permission set includes:\n\n- `allow-get-current`"
+ },
+ {
+ "description": "Enables the get_current command without any pre-configured scope.",
+ "type": "string",
+ "const": "deep-link:allow-get-current",
+ "markdownDescription": "Enables the get_current command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_registered command without any pre-configured scope.",
+ "type": "string",
+ "const": "deep-link:allow-is-registered",
+ "markdownDescription": "Enables the is_registered command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the register command without any pre-configured scope.",
+ "type": "string",
+ "const": "deep-link:allow-register",
+ "markdownDescription": "Enables the register command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the unregister command without any pre-configured scope.",
+ "type": "string",
+ "const": "deep-link:allow-unregister",
+ "markdownDescription": "Enables the unregister command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the get_current command without any pre-configured scope.",
+ "type": "string",
+ "const": "deep-link:deny-get-current",
+ "markdownDescription": "Denies the get_current command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_registered command without any pre-configured scope.",
+ "type": "string",
+ "const": "deep-link:deny-is-registered",
+ "markdownDescription": "Denies the is_registered command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the register command without any pre-configured scope.",
+ "type": "string",
+ "const": "deep-link:deny-register",
+ "markdownDescription": "Denies the register command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the unregister command without any pre-configured scope.",
+ "type": "string",
+ "const": "deep-link:deny-unregister",
+ "markdownDescription": "Denies the unregister command without any pre-configured scope."
+ },
{
"description": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-ask`\n- `allow-confirm`\n- `allow-message`\n- `allow-save`\n- `allow-open`",
"type": "string",
diff --git a/src-tauri/gen/schemas/macOS-schema.json b/src-tauri/gen/schemas/macOS-schema.json
index 0a45d9c6b..0ad9f7d35 100644
--- a/src-tauri/gen/schemas/macOS-schema.json
+++ b/src-tauri/gen/schemas/macOS-schema.json
@@ -2396,6 +2396,60 @@
"const": "core:window:deny-unminimize",
"markdownDescription": "Denies the unminimize command without any pre-configured scope."
},
+ {
+ "description": "Allows reading the opened deep link via the get_current command\n#### This default permission set includes:\n\n- `allow-get-current`",
+ "type": "string",
+ "const": "deep-link:default",
+ "markdownDescription": "Allows reading the opened deep link via the get_current command\n#### This default permission set includes:\n\n- `allow-get-current`"
+ },
+ {
+ "description": "Enables the get_current command without any pre-configured scope.",
+ "type": "string",
+ "const": "deep-link:allow-get-current",
+ "markdownDescription": "Enables the get_current command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_registered command without any pre-configured scope.",
+ "type": "string",
+ "const": "deep-link:allow-is-registered",
+ "markdownDescription": "Enables the is_registered command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the register command without any pre-configured scope.",
+ "type": "string",
+ "const": "deep-link:allow-register",
+ "markdownDescription": "Enables the register command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the unregister command without any pre-configured scope.",
+ "type": "string",
+ "const": "deep-link:allow-unregister",
+ "markdownDescription": "Enables the unregister command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the get_current command without any pre-configured scope.",
+ "type": "string",
+ "const": "deep-link:deny-get-current",
+ "markdownDescription": "Denies the get_current command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_registered command without any pre-configured scope.",
+ "type": "string",
+ "const": "deep-link:deny-is-registered",
+ "markdownDescription": "Denies the is_registered command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the register command without any pre-configured scope.",
+ "type": "string",
+ "const": "deep-link:deny-register",
+ "markdownDescription": "Denies the register command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the unregister command without any pre-configured scope.",
+ "type": "string",
+ "const": "deep-link:deny-unregister",
+ "markdownDescription": "Denies the unregister command without any pre-configured scope."
+ },
{
"description": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-ask`\n- `allow-confirm`\n- `allow-message`\n- `allow-save`\n- `allow-open`",
"type": "string",
diff --git a/src-tauri/src/browser_interceptor/config.rs b/src-tauri/src/browser_interceptor/config.rs
new file mode 100644
index 000000000..93d42e08a
--- /dev/null
+++ b/src-tauri/src/browser_interceptor/config.rs
@@ -0,0 +1,266 @@
+use chrono::{DateTime, Utc};
+use serde::{Deserialize, Serialize};
+use std::collections::HashMap;
+
+/// 拦截器状态
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct InterceptorState {
+ pub enabled: bool,
+ pub active_hooks: Vec,
+ pub intercepted_count: u32,
+ pub last_activity: Option>,
+ pub can_restore: bool, // 是否可以恢复正常状态
+}
+
+impl Default for InterceptorState {
+ fn default() -> Self {
+ Self {
+ enabled: false,
+ active_hooks: Vec::new(),
+ intercepted_count: 0,
+ last_activity: None,
+ can_restore: false,
+ }
+ }
+}
+
+/// 被拦截的 URL 信息
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct InterceptedUrl {
+ pub id: String,
+ pub url: String,
+ pub source_process: String,
+ pub timestamp: DateTime,
+ pub copied: bool,
+ pub opened_in_browser: bool,
+ pub dismissed: bool,
+}
+
+impl InterceptedUrl {
+ pub fn new(url: String, source_process: String) -> Self {
+ Self {
+ id: uuid::Uuid::new_v4().to_string(),
+ url,
+ source_process,
+ timestamp: Utc::now(),
+ copied: false,
+ opened_in_browser: false,
+ dismissed: false,
+ }
+ }
+}
+
+/// 指纹浏览器配置
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct FingerprintBrowserConfig {
+ pub enabled: bool,
+ pub executable_path: String,
+ pub profile_path: String,
+ pub additional_args: Vec,
+}
+
+impl Default for FingerprintBrowserConfig {
+ fn default() -> Self {
+ Self {
+ enabled: false,
+ executable_path: String::new(),
+ profile_path: String::new(),
+ additional_args: Vec::new(),
+ }
+ }
+}
+
+/// 恢复机制配置
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct RecoveryConfig {
+ pub backup_system_state: bool,
+ pub emergency_recovery_hotkey: String,
+ pub auto_recovery_on_crash: bool,
+ pub recovery_timeout: u64, // 恢复操作超时时间(秒)
+}
+
+impl Default for RecoveryConfig {
+ fn default() -> Self {
+ Self {
+ backup_system_state: true,
+ emergency_recovery_hotkey: "Ctrl+Alt+Shift+R".to_string(),
+ auto_recovery_on_crash: true,
+ recovery_timeout: 30,
+ }
+ }
+}
+
+/// 浏览器拦截器配置
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct BrowserInterceptorConfig {
+ pub enabled: bool,
+ pub target_processes: Vec,
+ pub url_patterns: Vec,
+ pub excluded_processes: Vec,
+ pub notification_enabled: bool,
+ pub auto_copy_to_clipboard: bool,
+ pub auto_launch_browser: bool,
+ pub restore_on_exit: bool,
+ pub temporary_disable_timeout: Option, // 临时禁用超时(秒)
+ pub fingerprint_browser: FingerprintBrowserConfig,
+ pub recovery: RecoveryConfig,
+}
+
+impl Default for BrowserInterceptorConfig {
+ fn default() -> Self {
+ Self {
+ enabled: false,
+ target_processes: vec![
+ "kiro".to_string(),
+ "kiro.exe".to_string(),
+ "cursor".to_string(),
+ "cursor.exe".to_string(),
+ "code".to_string(),
+ "code.exe".to_string(),
+ "Kiro".to_string(), // macOS 应用名称通常首字母大写
+ "Cursor".to_string(),
+ "Visual Studio Code".to_string(),
+ ],
+ url_patterns: vec![
+ "https://auth.*".to_string(),
+ "https://*/oauth/*".to_string(),
+ "https://accounts.google.com/*".to_string(),
+ "https://github.com/login/*".to_string(),
+ "https://login.microsoftonline.com/*".to_string(),
+ ],
+ excluded_processes: vec![
+ "explorer.exe".to_string(),
+ "winlogon.exe".to_string(),
+ "system".to_string(),
+ "chrome.exe".to_string(),
+ "firefox.exe".to_string(),
+ "safari".to_string(),
+ "Safari".to_string(), // macOS Safari
+ "Google Chrome".to_string(), // macOS Chrome
+ "Firefox".to_string(), // macOS Firefox
+ ],
+ notification_enabled: true,
+ auto_copy_to_clipboard: true,
+ auto_launch_browser: false,
+ restore_on_exit: true,
+ temporary_disable_timeout: Some(300), // 5分钟
+ fingerprint_browser: FingerprintBrowserConfig::default(),
+ recovery: RecoveryConfig::default(),
+ }
+ }
+}
+
+impl BrowserInterceptorConfig {
+ /// 检查进程是否在目标列表中
+ pub fn is_target_process(&self, process_name: &str) -> bool {
+ self.target_processes.iter().any(|pattern| {
+ // 支持简单的通配符匹配
+ if pattern.contains('*') {
+ // TODO: 实现更复杂的模式匹配
+ process_name.contains(&pattern.replace('*', ""))
+ } else {
+ process_name.eq_ignore_ascii_case(pattern)
+ }
+ })
+ }
+
+ /// 检查进程是否在排除列表中
+ pub fn is_excluded_process(&self, process_name: &str) -> bool {
+ self.excluded_processes.iter().any(|pattern| {
+ if pattern.contains('*') {
+ process_name.contains(&pattern.replace('*', ""))
+ } else {
+ process_name.eq_ignore_ascii_case(pattern)
+ }
+ })
+ }
+
+ /// 检查 URL 是否匹配拦截模式
+ pub fn matches_url_pattern(&self, url: &str) -> bool {
+ self.url_patterns.iter().any(|pattern| {
+ // 简单的模式匹配实现
+ if pattern.contains('*') {
+ let parts: Vec<&str> = pattern.split('*').collect();
+ if parts.len() == 2 {
+ url.starts_with(parts[0]) && url.ends_with(parts[1])
+ } else {
+ // 更复杂的模式匹配
+ url.contains(&pattern.replace('*', ""))
+ }
+ } else {
+ url.starts_with(pattern)
+ }
+ })
+ }
+
+ /// 验证配置的有效性
+ pub fn validate(&self) -> Result<(), String> {
+ if self.target_processes.is_empty() {
+ return Err("目标进程列表不能为空".to_string());
+ }
+
+ if self.url_patterns.is_empty() {
+ return Err("URL 模式列表不能为空".to_string());
+ }
+
+ if self.fingerprint_browser.enabled && self.fingerprint_browser.executable_path.is_empty() {
+ return Err("启用指纹浏览器时必须指定可执行文件路径".to_string());
+ }
+
+ if let Some(timeout) = self.temporary_disable_timeout {
+ if timeout == 0 {
+ return Err("临时禁用超时时间必须大于 0".to_string());
+ }
+ }
+
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_is_target_process() {
+ let config = BrowserInterceptorConfig::default();
+
+ assert!(config.is_target_process("kiro"));
+ assert!(config.is_target_process("KIRO.EXE"));
+ assert!(config.is_target_process("cursor"));
+ assert!(!config.is_target_process("notepad"));
+ }
+
+ #[test]
+ fn test_is_excluded_process() {
+ let config = BrowserInterceptorConfig::default();
+
+ assert!(config.is_excluded_process("chrome.exe"));
+ assert!(config.is_excluded_process("FIREFOX.EXE"));
+ assert!(!config.is_excluded_process("kiro"));
+ }
+
+ #[test]
+ fn test_matches_url_pattern() {
+ let config = BrowserInterceptorConfig::default();
+
+ assert!(config.matches_url_pattern("https://accounts.google.com/oauth/authorize"));
+ assert!(config.matches_url_pattern("https://github.com/login/oauth"));
+ assert!(config.matches_url_pattern("https://auth.example.com/login"));
+ assert!(!config.matches_url_pattern("https://example.com/normal-page"));
+ }
+
+ #[test]
+ fn test_config_validation() {
+ let mut config = BrowserInterceptorConfig::default();
+ assert!(config.validate().is_ok());
+
+ config.target_processes.clear();
+ assert!(config.validate().is_err());
+
+ config = BrowserInterceptorConfig::default();
+ config.fingerprint_browser.enabled = true;
+ config.fingerprint_browser.executable_path = String::new();
+ assert!(config.validate().is_err());
+ }
+}
diff --git a/src-tauri/src/browser_interceptor/interceptor.rs b/src-tauri/src/browser_interceptor/interceptor.rs
new file mode 100644
index 000000000..a7da381ba
--- /dev/null
+++ b/src-tauri/src/browser_interceptor/interceptor.rs
@@ -0,0 +1,460 @@
+use crate::browser_interceptor::{
+ BrowserInterceptorConfig, BrowserInterceptorError, InterceptedUrl, NotificationService, Result,
+ StateManager, UrlManager,
+};
+use std::sync::Arc;
+use tokio::sync::RwLock;
+
+/// 浏览器拦截器主结构
+pub struct BrowserInterceptor {
+ config: Arc>,
+ state_manager: Arc,
+ url_manager: Arc,
+ notification_service: Arc>,
+ #[cfg(target_os = "windows")]
+ windows_interceptor: Option,
+ #[cfg(target_os = "macos")]
+ macos_interceptor: Option,
+ #[cfg(target_os = "linux")]
+ linux_interceptor: Option,
+}
+
+impl BrowserInterceptor {
+ /// 创建新的浏览器拦截器实例
+ pub fn new(config: BrowserInterceptorConfig) -> Self {
+ Self {
+ config: Arc::new(RwLock::new(config)),
+ state_manager: Arc::new(StateManager::new()),
+ url_manager: Arc::new(UrlManager::new()),
+ notification_service: Arc::new(RwLock::new(NotificationService::new())),
+ #[cfg(target_os = "windows")]
+ windows_interceptor: None,
+ #[cfg(target_os = "macos")]
+ macos_interceptor: None,
+ #[cfg(target_os = "linux")]
+ linux_interceptor: None,
+ }
+ }
+
+ /// 启动拦截器
+ pub async fn start(&mut self) -> Result<()> {
+ tracing::info!("开始启动浏览器拦截器...");
+
+ let config = self.config.read().await;
+
+ if !config.enabled {
+ tracing::error!("拦截器配置中 enabled 为 false");
+ return Err(BrowserInterceptorError::InterceptorError(
+ "拦截器未启用".to_string(),
+ ));
+ }
+
+ // 验证配置
+ tracing::info!("验证配置...");
+ config.validate().map_err(|e| {
+ tracing::error!("配置验证失败: {}", e);
+ BrowserInterceptorError::ConfigError(format!("配置验证失败: {}", e))
+ })?;
+
+ drop(config); // 释放读锁
+
+ // 启用状态管理器
+ tracing::info!("启用状态管理器...");
+ self.state_manager.enable_interceptor().await.map_err(|e| {
+ tracing::error!("启用状态管理器失败: {}", e);
+ e
+ })?;
+
+ // 启动平台特定的拦截器
+ tracing::info!("启动平台拦截器...");
+ self.start_platform_interceptor().await.map_err(|e| {
+ tracing::error!("启动平台拦截器失败: {}", e);
+ e
+ })?;
+
+ // 发送启用通知
+ tracing::info!("发送启用通知...");
+ let notification_service = self.notification_service.read().await;
+ notification_service.notify_interceptor_enabled().await?;
+
+ tracing::info!("浏览器拦截器已成功启动");
+ Ok(())
+ }
+
+ /// 停止拦截器
+ pub async fn stop(&mut self) -> Result<()> {
+ // 停止平台特定的拦截器
+ self.stop_platform_interceptor().await?;
+
+ // 禁用状态管理器
+ self.state_manager.disable_interceptor().await?;
+
+ // 发送禁用通知
+ let notification_service = self.notification_service.read().await;
+ notification_service.notify_interceptor_disabled().await?;
+
+ tracing::info!("浏览器拦截器已停止");
+ Ok(())
+ }
+
+ /// 恢复正常浏览器行为
+ pub async fn restore_normal_behavior(&mut self) -> Result<()> {
+ // 停止拦截
+ self.stop_platform_interceptor().await?;
+
+ // 恢复系统状态
+ self.state_manager.restore_normal_behavior().await?;
+
+ // 发送恢复通知
+ let notification_service = self.notification_service.read().await;
+ notification_service.notify_system_restored().await?;
+
+ tracing::info!("已恢复正常浏览器行为");
+ Ok(())
+ }
+
+ /// 临时禁用拦截器
+ pub async fn temporary_disable(&mut self, duration_seconds: u64) -> Result<()> {
+ self.state_manager
+ .temporary_disable(duration_seconds)
+ .await?;
+
+ // 临时停止平台拦截器
+ self.stop_platform_interceptor().await?;
+
+ tracing::info!("拦截器已临时禁用 {} 秒", duration_seconds);
+ Ok(())
+ }
+
+ /// 处理拦截到的浏览器启动请求
+ pub async fn handle_browser_launch(&self, url: String, source_process: String) -> Result {
+ let config = self.config.read().await;
+
+ // 检查是否应该拦截这个进程
+ if !self.should_intercept(&config, &source_process, &url) {
+ return Ok(false); // 不拦截,让浏览器正常启动
+ }
+
+ drop(config); // 释放读锁
+
+ // 添加到拦截列表
+ let url_id = self
+ .url_manager
+ .add_intercepted_url(url.clone(), source_process.clone())?;
+
+ // 更新拦截计数
+ self.state_manager.increment_intercept_count()?;
+
+ // 创建拦截的 URL 对象用于通知
+ let intercepted_url = InterceptedUrl::new(url, source_process);
+
+ // 发送通知
+ let notification_service = self.notification_service.read().await;
+ notification_service
+ .notify_url_intercepted(&intercepted_url)
+ .await?;
+
+ // 如果启用了自动复制到剪贴板
+ let config = self.config.read().await;
+ if config.auto_copy_to_clipboard {
+ self.copy_to_clipboard(&intercepted_url.url).await?;
+ self.url_manager.mark_as_copied(&url_id)?;
+ }
+
+ tracing::info!(
+ "已拦截浏览器启动: {} (来源: {})",
+ intercepted_url.url,
+ intercepted_url.source_process
+ );
+ Ok(true) // 已拦截
+ }
+
+ /// 获取当前状态
+ pub async fn get_state(&self) -> Result {
+ self.state_manager.get_state()
+ }
+
+ /// 获取拦截的 URL 列表
+ pub async fn get_intercepted_urls(&self) -> Result> {
+ self.url_manager.get_intercepted_urls()
+ }
+
+ /// 获取历史记录
+ pub async fn get_history(&self, limit: Option) -> Result> {
+ self.url_manager.get_history(limit)
+ }
+
+ /// 复制 URL 到剪贴板
+ pub async fn copy_url_to_clipboard(&self, url_id: &str) -> Result<()> {
+ if let Some(intercepted_url) = self.url_manager.get_intercepted_url(url_id)? {
+ self.copy_to_clipboard(&intercepted_url.url).await?;
+ self.url_manager.mark_as_copied(url_id)?;
+ tracing::info!("URL {} 已复制到剪贴板", url_id);
+ }
+ Ok(())
+ }
+
+ /// 在指纹浏览器中打开 URL
+ pub async fn open_in_fingerprint_browser(&self, url_id: &str) -> Result<()> {
+ let config = self.config.read().await;
+
+ if !config.fingerprint_browser.enabled {
+ return Err(BrowserInterceptorError::InterceptorError(
+ "指纹浏览器未启用".to_string(),
+ ));
+ }
+
+ if let Some(intercepted_url) = self.url_manager.get_intercepted_url(url_id)? {
+ self.launch_fingerprint_browser(&config.fingerprint_browser, &intercepted_url.url)
+ .await?;
+ self.url_manager.mark_as_opened(url_id)?;
+ tracing::info!("URL {} 已在指纹浏览器中打开", url_id);
+ }
+
+ Ok(())
+ }
+
+ /// 忽略指定的 URL
+ pub async fn dismiss_url(&self, url_id: &str) -> Result<()> {
+ self.url_manager.dismiss_url(url_id)?;
+ tracing::info!("URL {} 已被忽略", url_id);
+ Ok(())
+ }
+
+ /// 更新配置
+ pub async fn update_config(&self, new_config: BrowserInterceptorConfig) -> Result<()> {
+ // 验证新配置
+ new_config
+ .validate()
+ .map_err(|e| BrowserInterceptorError::ConfigError(format!("配置验证失败: {}", e)))?;
+
+ let mut config = self.config.write().await;
+ *config = new_config;
+
+ tracing::info!("浏览器拦截器配置已更新");
+ Ok(())
+ }
+
+ /// 检查是否应该拦截指定的进程和 URL
+ fn should_intercept(
+ &self,
+ config: &BrowserInterceptorConfig,
+ process_name: &str,
+ url: &str,
+ ) -> bool {
+ // 检查是否在排除列表中
+ if config.is_excluded_process(process_name) {
+ return false;
+ }
+
+ // 检查是否是目标进程
+ if !config.is_target_process(process_name) {
+ return false;
+ }
+
+ // 检查 URL 是否匹配模式
+ config.matches_url_pattern(url)
+ }
+
+ /// 启动平台特定的拦截器
+ async fn start_platform_interceptor(&mut self) -> Result<()> {
+ #[cfg(target_os = "windows")]
+ {
+ // 创建 URL 处理器闭包
+ let url_manager = Arc::clone(&self.url_manager);
+ let state_manager = Arc::clone(&self.state_manager);
+
+ let url_handler = move |intercepted_url: InterceptedUrl| {
+ let url_manager = Arc::clone(&url_manager);
+ let state_manager = Arc::clone(&state_manager);
+
+ tokio::spawn(async move {
+ if let Err(e) = url_manager
+ .add_intercepted_url(intercepted_url.url, intercepted_url.source_process)
+ {
+ tracing::error!("添加拦截 URL 失败: {}", e);
+ }
+ if let Err(e) = state_manager.increment_intercept_count() {
+ tracing::error!("更新拦截计数失败: {}", e);
+ }
+ });
+ };
+
+ let mut interceptor =
+ crate::browser_interceptor::platform::windows::WindowsInterceptor::new(url_handler);
+ interceptor.start().await?;
+ self.windows_interceptor = Some(interceptor);
+ }
+
+ #[cfg(target_os = "macos")]
+ {
+ let url_manager = Arc::clone(&self.url_manager);
+ let state_manager = Arc::clone(&self.state_manager);
+
+ let url_handler = move |intercepted_url: InterceptedUrl| {
+ let url_manager = Arc::clone(&url_manager);
+ let state_manager = Arc::clone(&state_manager);
+
+ tokio::spawn(async move {
+ if let Err(e) = url_manager
+ .add_intercepted_url(intercepted_url.url, intercepted_url.source_process)
+ {
+ tracing::error!("添加拦截 URL 失败: {}", e);
+ }
+ if let Err(e) = state_manager.increment_intercept_count() {
+ tracing::error!("更新拦截计数失败: {}", e);
+ }
+ });
+ };
+
+ let mut interceptor =
+ crate::browser_interceptor::platform::macos::MacOSInterceptor::new(url_handler);
+ interceptor.start().await?;
+ self.macos_interceptor = Some(interceptor);
+ }
+
+ #[cfg(target_os = "linux")]
+ {
+ let url_manager = Arc::clone(&self.url_manager);
+ let state_manager = Arc::clone(&self.state_manager);
+
+ let url_handler = move |intercepted_url: InterceptedUrl| {
+ let url_manager = Arc::clone(&url_manager);
+ let state_manager = Arc::clone(&state_manager);
+
+ tokio::spawn(async move {
+ if let Err(e) = url_manager
+ .add_intercepted_url(intercepted_url.url, intercepted_url.source_process)
+ {
+ tracing::error!("添加拦截 URL 失败: {}", e);
+ }
+ if let Err(e) = state_manager.increment_intercept_count() {
+ tracing::error!("更新拦截计数失败: {}", e);
+ }
+ });
+ };
+
+ let mut interceptor =
+ crate::browser_interceptor::platform::linux::LinuxInterceptor::new(url_handler);
+ interceptor.start().await?;
+ self.linux_interceptor = Some(interceptor);
+ }
+
+ Ok(())
+ }
+
+ /// 停止平台特定的拦截器
+ async fn stop_platform_interceptor(&mut self) -> Result<()> {
+ #[cfg(target_os = "windows")]
+ {
+ if let Some(ref mut interceptor) = self.windows_interceptor {
+ interceptor.stop().await?;
+ }
+ }
+
+ #[cfg(target_os = "macos")]
+ {
+ if let Some(ref mut interceptor) = self.macos_interceptor {
+ interceptor.stop().await?;
+ }
+ }
+
+ #[cfg(target_os = "linux")]
+ {
+ if let Some(ref mut interceptor) = self.linux_interceptor {
+ interceptor.stop().await?;
+ }
+ }
+
+ Ok(())
+ }
+
+ /// 复制文本到剪贴板
+ async fn copy_to_clipboard(&self, text: &str) -> Result<()> {
+ match arboard::Clipboard::new() {
+ Ok(mut clipboard) => {
+ if let Err(e) = clipboard.set_text(text) {
+ return Err(BrowserInterceptorError::InterceptorError(format!(
+ "复制到剪贴板失败: {}",
+ e
+ )));
+ }
+ tracing::info!("已复制到剪贴板: {}", text);
+ Ok(())
+ }
+ Err(e) => Err(BrowserInterceptorError::InterceptorError(format!(
+ "创建剪贴板实例失败: {}",
+ e
+ ))),
+ }
+ }
+
+ /// 启动指纹浏览器
+ async fn launch_fingerprint_browser(
+ &self,
+ browser_config: &crate::browser_interceptor::config::FingerprintBrowserConfig,
+ url: &str,
+ ) -> Result<()> {
+ if browser_config.executable_path.is_empty() {
+ return Err(BrowserInterceptorError::ConfigError(
+ "指纹浏览器可执行文件路径未配置".to_string(),
+ ));
+ }
+
+ // 构建启动命令
+ let mut command = std::process::Command::new(&browser_config.executable_path);
+
+ // 添加 URL 参数
+ command.arg(url);
+
+ // 添加额外的参数
+ for arg in &browser_config.additional_args {
+ command.arg(arg);
+ }
+
+ // 如果配置了配置文件路径
+ if !browser_config.profile_path.is_empty() {
+ command
+ .arg("--user-data-dir")
+ .arg(&browser_config.profile_path);
+ }
+
+ // 异步启动进程
+ match command.spawn() {
+ Ok(mut child) => {
+ // 在后台等待进程完成
+ tokio::spawn(async move {
+ match child.wait() {
+ Ok(status) => {
+ if status.success() {
+ tracing::info!("指纹浏览器启动成功");
+ } else {
+ tracing::error!("指纹浏览器退出异常: {}", status);
+ }
+ }
+ Err(e) => {
+ tracing::error!("等待指纹浏览器进程失败: {}", e);
+ }
+ }
+ });
+
+ tracing::info!(
+ "已启动指纹浏览器: {} -> {}",
+ browser_config.executable_path,
+ url
+ );
+ Ok(())
+ }
+ Err(e) => Err(BrowserInterceptorError::InterceptorError(format!(
+ "启动指纹浏览器失败: {}",
+ e
+ ))),
+ }
+ }
+}
+
+impl Default for BrowserInterceptor {
+ fn default() -> Self {
+ Self::new(BrowserInterceptorConfig::default())
+ }
+}
diff --git a/src-tauri/src/browser_interceptor/mod.rs b/src-tauri/src/browser_interceptor/mod.rs
new file mode 100644
index 000000000..9335b16a1
--- /dev/null
+++ b/src-tauri/src/browser_interceptor/mod.rs
@@ -0,0 +1,57 @@
+pub mod config;
+pub mod interceptor;
+pub mod notification_service;
+pub mod state_manager;
+pub mod url_manager;
+
+#[cfg(target_os = "windows")]
+pub mod platform {
+ pub mod windows;
+}
+
+#[cfg(target_os = "macos")]
+pub mod platform {
+ pub mod macos;
+}
+
+#[cfg(target_os = "linux")]
+pub mod platform {
+ pub mod linux;
+}
+
+// 重新导出主要类型和函数
+pub use config::{BrowserInterceptorConfig, InterceptedUrl, InterceptorState};
+pub use interceptor::BrowserInterceptor;
+pub use notification_service::NotificationService;
+pub use state_manager::StateManager;
+pub use url_manager::UrlManager;
+
+use serde::{Deserialize, Serialize};
+use std::error::Error;
+use std::fmt;
+
+/// 浏览器拦截器错误类型
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum BrowserInterceptorError {
+ ConfigError(String),
+ InterceptorError(String),
+ StateError(String),
+ PlatformError(String),
+ NotificationError(String),
+}
+
+impl fmt::Display for BrowserInterceptorError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ BrowserInterceptorError::ConfigError(msg) => write!(f, "配置错误: {}", msg),
+ BrowserInterceptorError::InterceptorError(msg) => write!(f, "拦截器错误: {}", msg),
+ BrowserInterceptorError::StateError(msg) => write!(f, "状态管理错误: {}", msg),
+ BrowserInterceptorError::PlatformError(msg) => write!(f, "平台错误: {}", msg),
+ BrowserInterceptorError::NotificationError(msg) => write!(f, "通知错误: {}", msg),
+ }
+ }
+}
+
+impl Error for BrowserInterceptorError {}
+
+pub type Result = std::result::Result;
diff --git a/src-tauri/src/browser_interceptor/notification_service.rs b/src-tauri/src/browser_interceptor/notification_service.rs
new file mode 100644
index 000000000..a35ddfe6e
--- /dev/null
+++ b/src-tauri/src/browser_interceptor/notification_service.rs
@@ -0,0 +1,307 @@
+use crate::browser_interceptor::{BrowserInterceptorError, InterceptedUrl, Result};
+use serde::{Deserialize, Serialize};
+use std::time::Duration;
+
+/// 通知类型
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum NotificationType {
+ UrlIntercepted,
+ InterceptorEnabled,
+ InterceptorDisabled,
+ SystemRestored,
+ Error,
+}
+
+/// 通知消息
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct NotificationMessage {
+ pub id: String,
+ pub notification_type: NotificationType,
+ pub title: String,
+ pub message: String,
+ pub url: Option,
+ pub source_process: Option,
+ pub timestamp: chrono::DateTime,
+ pub auto_dismiss_after: Option,
+}
+
+impl NotificationMessage {
+ pub fn new(notification_type: NotificationType, title: String, message: String) -> Self {
+ Self {
+ id: uuid::Uuid::new_v4().to_string(),
+ notification_type,
+ title,
+ message,
+ url: None,
+ source_process: None,
+ timestamp: chrono::Utc::now(),
+ auto_dismiss_after: Some(Duration::from_secs(30)),
+ }
+ }
+
+ pub fn with_url(mut self, url: String) -> Self {
+ self.url = Some(url);
+ self
+ }
+
+ pub fn with_source_process(mut self, source_process: String) -> Self {
+ self.source_process = Some(source_process);
+ self
+ }
+
+ pub fn with_auto_dismiss(mut self, duration: Option) -> Self {
+ self.auto_dismiss_after = duration;
+ self
+ }
+}
+
+/// 通知服务
+pub struct NotificationService {
+ enabled: bool,
+ show_url_preview: bool,
+}
+
+impl NotificationService {
+ pub fn new() -> Self {
+ Self {
+ enabled: true,
+ show_url_preview: true,
+ }
+ }
+
+ /// 设置通知是否启用
+ pub fn set_enabled(&mut self, enabled: bool) {
+ self.enabled = enabled;
+ }
+
+ /// 设置是否显示 URL 预览
+ pub fn set_show_url_preview(&mut self, show_preview: bool) {
+ self.show_url_preview = show_preview;
+ }
+
+ /// 发送 URL 拦截通知
+ pub async fn notify_url_intercepted(&self, intercepted_url: &InterceptedUrl) -> Result<()> {
+ if !self.enabled {
+ return Ok(());
+ }
+
+ let title = format!("已拦截来自 {} 的 URL", intercepted_url.source_process);
+ let message = if self.show_url_preview {
+ format!("URL: {}", self.truncate_url(&intercepted_url.url, 100))
+ } else {
+ "点击查看详情".to_string()
+ };
+
+ let notification =
+ NotificationMessage::new(NotificationType::UrlIntercepted, title, message)
+ .with_url(intercepted_url.url.clone())
+ .with_source_process(intercepted_url.source_process.clone());
+
+ self.send_notification(notification).await
+ }
+
+ /// 发送拦截器启用通知
+ pub async fn notify_interceptor_enabled(&self) -> Result<()> {
+ if !self.enabled {
+ return Ok(());
+ }
+
+ let notification = NotificationMessage::new(
+ NotificationType::InterceptorEnabled,
+ "浏览器拦截器已启用".to_string(),
+ "现在会拦截目标应用的浏览器启动请求".to_string(),
+ );
+
+ self.send_notification(notification).await
+ }
+
+ /// 发送拦截器禁用通知
+ pub async fn notify_interceptor_disabled(&self) -> Result<()> {
+ if !self.enabled {
+ return Ok(());
+ }
+
+ let notification = NotificationMessage::new(
+ NotificationType::InterceptorDisabled,
+ "浏览器拦截器已禁用".to_string(),
+ "应用将正常打开默认浏览器".to_string(),
+ );
+
+ self.send_notification(notification).await
+ }
+
+ /// 发送系统恢复通知
+ pub async fn notify_system_restored(&self) -> Result<()> {
+ if !self.enabled {
+ return Ok(());
+ }
+
+ let notification = NotificationMessage::new(
+ NotificationType::SystemRestored,
+ "系统已恢复正常".to_string(),
+ "浏览器行为已恢复到原始状态".to_string(),
+ );
+
+ self.send_notification(notification).await
+ }
+
+ /// 发送错误通知
+ pub async fn notify_error(&self, error_message: &str) -> Result<()> {
+ if !self.enabled {
+ return Ok(());
+ }
+
+ let notification = NotificationMessage::new(
+ NotificationType::Error,
+ "浏览器拦截器错误".to_string(),
+ error_message.to_string(),
+ )
+ .with_auto_dismiss(Some(Duration::from_secs(60))); // 错误通知显示更长时间
+
+ self.send_notification(notification).await
+ }
+
+ /// 发送通知的具体实现
+ async fn send_notification(&self, notification: NotificationMessage) -> Result<()> {
+ // 记录日志
+ tracing::info!(
+ "发送通知: {} - {}",
+ notification.title,
+ notification.message
+ );
+
+ // 发送系统通知
+ self.send_system_notification(¬ification).await?;
+
+ // 发送到前端(通过事件系统)
+ self.send_frontend_notification(¬ification).await?;
+
+ Ok(())
+ }
+
+ /// 发送系统通知
+ async fn send_system_notification(&self, notification: &NotificationMessage) -> Result<()> {
+ #[cfg(target_os = "windows")]
+ {
+ self.send_windows_notification(notification).await?;
+ }
+
+ #[cfg(target_os = "macos")]
+ {
+ self.send_macos_notification(notification).await?;
+ }
+
+ #[cfg(target_os = "linux")]
+ {
+ self.send_linux_notification(notification).await?;
+ }
+
+ Ok(())
+ }
+
+ /// 发送到前端
+ async fn send_frontend_notification(&self, notification: &NotificationMessage) -> Result<()> {
+ // TODO: 通过 Tauri 事件系统发送到前端
+ // tauri::emit_all("browser-interceptor-notification", notification)
+ // .map_err(|e| BrowserInterceptorError::NotificationError(format!("发送前端通知失败: {}", e)))?;
+
+ tracing::debug!("前端通知已发送: {}", notification.id);
+ Ok(())
+ }
+
+ /// Windows 系统通知
+ #[cfg(target_os = "windows")]
+ async fn send_windows_notification(&self, notification: &NotificationMessage) -> Result<()> {
+ // TODO: 使用 Windows Toast 通知 API
+ // 可以使用 winrt-notification 或 windows-rs crate
+ tracing::debug!("Windows 通知: {}", notification.title);
+ Ok(())
+ }
+
+ /// macOS 系统通知
+ #[cfg(target_os = "macos")]
+ async fn send_macos_notification(&self, notification: &NotificationMessage) -> Result<()> {
+ // TODO: 使用 macOS 通知中心
+ // 可以使用 mac-notification-sys crate
+ tracing::debug!("macOS 通知: {}", notification.title);
+ Ok(())
+ }
+
+ /// Linux 系统通知
+ #[cfg(target_os = "linux")]
+ async fn send_linux_notification(&self, notification: &NotificationMessage) -> Result<()> {
+ // TODO: 使用 libnotify 或 D-Bus
+ // 可以使用 notify-rust crate
+ tracing::debug!("Linux 通知: {}", notification.title);
+ Ok(())
+ }
+
+ /// 截断 URL 以适应通知显示
+ fn truncate_url(&self, url: &str, max_length: usize) -> String {
+ if url.len() <= max_length {
+ url.to_string()
+ } else {
+ format!("{}...", &url[..max_length.saturating_sub(3)])
+ }
+ }
+}
+
+impl Default for NotificationService {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use chrono::Utc;
+
+ #[test]
+ fn test_notification_message_creation() {
+ let notification = NotificationMessage::new(
+ NotificationType::UrlIntercepted,
+ "Test Title".to_string(),
+ "Test Message".to_string(),
+ )
+ .with_url("https://example.com".to_string())
+ .with_source_process("test_app".to_string());
+
+ assert_eq!(notification.title, "Test Title");
+ assert_eq!(notification.message, "Test Message");
+ assert_eq!(notification.url, Some("https://example.com".to_string()));
+ assert_eq!(notification.source_process, Some("test_app".to_string()));
+ assert!(notification.auto_dismiss_after.is_some());
+ }
+
+ #[test]
+ fn test_truncate_url() {
+ let service = NotificationService::new();
+
+ let short_url = "https://example.com";
+ assert_eq!(service.truncate_url(short_url, 100), short_url);
+
+ let long_url = "https://example.com/very/long/path/that/exceeds/the/maximum/length/limit";
+ let truncated = service.truncate_url(long_url, 20);
+ assert_eq!(truncated.len(), 20);
+ assert!(truncated.ends_with("..."));
+ }
+
+ #[tokio::test]
+ async fn test_notify_url_intercepted() {
+ let service = NotificationService::new();
+ let intercepted_url = InterceptedUrl {
+ id: "test-id".to_string(),
+ url: "https://accounts.google.com/oauth/authorize".to_string(),
+ source_process: "kiro".to_string(),
+ timestamp: Utc::now(),
+ copied: false,
+ opened_in_browser: false,
+ dismissed: false,
+ };
+
+ // 这个测试主要验证函数不会 panic
+ let result = service.notify_url_intercepted(&intercepted_url).await;
+ assert!(result.is_ok());
+ }
+}
diff --git a/src-tauri/src/browser_interceptor/platform/linux.rs b/src-tauri/src/browser_interceptor/platform/linux.rs
new file mode 100644
index 000000000..02b06648f
--- /dev/null
+++ b/src-tauri/src/browser_interceptor/platform/linux.rs
@@ -0,0 +1,71 @@
+use crate::browser_interceptor::{BrowserInterceptorError, InterceptedUrl, Result};
+
+/// Linux 平台的浏览器拦截器
+pub struct LinuxInterceptor {
+ running: bool,
+}
+
+impl LinuxInterceptor {
+ pub fn new(_url_handler: F) -> Self
+ where
+ F: Fn(InterceptedUrl) + Send + Sync + 'static,
+ {
+ Self { running: false }
+ }
+
+ /// 启动拦截
+ pub async fn start(&mut self) -> Result<()> {
+ // TODO: 实现 Linux 平台的浏览器拦截
+ // 可以使用 xdg-open 拦截或 D-Bus 监听
+ self.running = true;
+ tracing::info!("Linux 浏览器拦截器已启动 (占位符)");
+ Ok(())
+ }
+
+ /// 停止拦截
+ pub async fn stop(&mut self) -> Result<()> {
+ if !self.running {
+ return Ok(());
+ }
+
+ self.running = false;
+ tracing::info!("Linux 浏览器拦截器已停止");
+ Ok(())
+ }
+
+ /// 检查是否正在拦截
+ pub fn is_running(&self) -> bool {
+ self.running
+ }
+
+ /// 恢复系统默认设置
+ pub async fn restore_system_defaults(&self) -> Result<()> {
+ tracing::info!("Linux 系统默认设置已恢复");
+ Ok(())
+ }
+
+ /// 临时禁用拦截
+ pub async fn temporarily_disable(&mut self) -> Result<()> {
+ tracing::info!("Linux 拦截器已临时禁用");
+ Ok(())
+ }
+
+ /// 重新启用拦截
+ pub async fn re_enable(&mut self) -> Result<()> {
+ tracing::info!("Linux 拦截器已重新启用");
+ Ok(())
+ }
+}
+
+impl Drop for LinuxInterceptor {
+ fn drop(&mut self) {
+ if self.running {
+ tokio::runtime::Handle::try_current().map(|handle| {
+ handle.block_on(async {
+ let _ = self.stop().await;
+ let _ = self.restore_system_defaults().await;
+ })
+ });
+ }
+ }
+}
diff --git a/src-tauri/src/browser_interceptor/platform/macos.rs b/src-tauri/src/browser_interceptor/platform/macos.rs
new file mode 100644
index 000000000..ff00e0597
--- /dev/null
+++ b/src-tauri/src/browser_interceptor/platform/macos.rs
@@ -0,0 +1,429 @@
+use crate::browser_interceptor::{BrowserInterceptorError, InterceptedUrl, Result};
+use once_cell::sync::Lazy;
+use std::process::Command;
+use std::sync::{Arc, Mutex};
+use tokio::sync::mpsc;
+
+/// 全局 URL sender,用于从 deep-link 事件接收 URL(使用 Mutex 保证线程安全)
+static GLOBAL_URL_SENDER: Lazy>>> =
+ Lazy::new(|| Mutex::new(None));
+
+/// macOS 平台的浏览器拦截器(基于设置默认浏览器 + Deep Link)
+pub struct MacOSInterceptor {
+ running: bool,
+ url_sender: Option>,
+ original_default_browser: Option,
+ url_handler: Option>,
+}
+
+impl MacOSInterceptor {
+ pub fn new(url_handler: F) -> Self
+ where
+ F: Fn(InterceptedUrl) + Send + Sync + 'static,
+ {
+ let (tx, mut rx) = mpsc::unbounded_channel();
+ let handler = Arc::new(url_handler);
+ let handler_clone = handler.clone();
+
+ // 启动后台任务处理拦截的 URL
+ tokio::spawn(async move {
+ while let Some(intercepted_url) = rx.recv().await {
+ handler_clone(intercepted_url);
+ }
+ });
+
+ Self {
+ running: false,
+ url_sender: Some(tx),
+ original_default_browser: None,
+ url_handler: Some(handler),
+ }
+ }
+
+ /// 启动拦截
+ pub async fn start(&mut self) -> Result<()> {
+ if self.running {
+ tracing::info!("macOS 拦截器已在运行中,跳过启动");
+ return Ok(());
+ }
+
+ tracing::info!("正在启动 macOS 浏览器拦截器...");
+
+ // 1. 保存当前默认浏览器
+ self.original_default_browser = self.get_default_browser().await;
+ tracing::info!("当前默认浏览器: {:?}", self.original_default_browser);
+
+ // 2. 设置全局 URL sender
+ if let Some(ref sender) = self.url_sender {
+ if let Ok(mut global_sender) = GLOBAL_URL_SENDER.lock() {
+ *global_sender = Some(sender.clone());
+ }
+ }
+
+ // 3. 将 ProxyCast 设置为默认浏览器
+ self.set_as_default_browser().await?;
+
+ self.running = true;
+ tracing::info!("macOS 浏览器拦截器已启动");
+ tracing::info!("提示:现在所有 http/https URL 打开请求都会被 ProxyCast 拦截");
+
+ Ok(())
+ }
+
+ /// 获取当前默认浏览器的 Bundle ID
+ async fn get_default_browser(&self) -> Option {
+ // 使用 Swift 调用 LSCopyDefaultHandlerForURLScheme 获取真实的默认浏览器
+ let swift_code = r#"
+import Foundation
+import CoreServices
+
+if let handler = LSCopyDefaultHandlerForURLScheme("https" as CFString) {
+ print(handler.takeRetainedValue() as String)
+} else if let handler = LSCopyDefaultHandlerForURLScheme("http" as CFString) {
+ print(handler.takeRetainedValue() as String)
+} else {
+ print("")
+}
+"#;
+
+ let output = Command::new("swift")
+ .args(["-e", swift_code])
+ .output()
+ .ok()?;
+
+ if output.status.success() {
+ let bundle_id = String::from_utf8_lossy(&output.stdout).trim().to_string();
+ if !bundle_id.is_empty() && !bundle_id.contains("proxycast") {
+ tracing::info!("检测到当前默认浏览器: {}", bundle_id);
+ return Some(bundle_id);
+ }
+ // 如果当前是 ProxyCast,说明之前已设置过,需要检测用户常用的浏览器
+ if bundle_id.contains("proxycast") {
+ tracing::info!("当前默认浏览器是 ProxyCast,尝试检测用户常用浏览器");
+ return self.detect_installed_browser().await;
+ }
+ }
+
+ // 备选方法:使用 duti 查询
+ let duti_output = Command::new("duti").args(["-x", "https"]).output().ok();
+
+ if let Some(output) = duti_output {
+ if output.status.success() {
+ let stdout = String::from_utf8_lossy(&output.stdout);
+ // duti -x 输出格式:第三行是 bundle id
+ for line in stdout.lines() {
+ let trimmed = line.trim();
+ if (trimmed.starts_with("com.") || trimmed.starts_with("org."))
+ && !trimmed.contains("proxycast")
+ {
+ tracing::info!("通过 duti 检测到默认浏览器: {}", trimmed);
+ return Some(trimmed.to_string());
+ }
+ }
+ }
+ }
+
+ // 最后尝试检测已安装的浏览器,优先返回 Chrome
+ self.detect_installed_browser().await
+ }
+
+ /// 将 ProxyCast 设置为默认浏览器
+ async fn set_as_default_browser(&self) -> Result<()> {
+ tracing::info!("正在将 ProxyCast 设置为默认浏览器...");
+
+ // 使用 Swift 代码通过 Launch Services API 设置默认处理程序
+ // 由于 Rust 直接调用 Launch Services 较复杂,这里使用 osascript 辅助
+
+ // 方法 1: 使用 duti 工具(如果已安装)
+ let duti_result = Command::new("duti")
+ .args(["-s", "com.proxycast.app", "http", "all"])
+ .output();
+
+ if let Ok(output) = duti_result {
+ if output.status.success() {
+ let _ = Command::new("duti")
+ .args(["-s", "com.proxycast.app", "https", "all"])
+ .output();
+ tracing::info!("已通过 duti 设置默认浏览器");
+ return Ok(());
+ }
+ }
+
+ // 方法 2: 使用 Swift 脚本
+ let swift_code = r#"
+import Foundation
+import CoreServices
+
+let bundleId = "com.proxycast.app" as CFString
+
+// 设置 HTTP handler
+LSSetDefaultHandlerForURLScheme("http" as CFString, bundleId)
+
+// 设置 HTTPS handler
+LSSetDefaultHandlerForURLScheme("https" as CFString, bundleId)
+
+print("OK")
+"#;
+
+ let output = Command::new("swift")
+ .args(["-e", swift_code])
+ .output()
+ .map_err(|e| {
+ BrowserInterceptorError::PlatformError(format!("执行 Swift 脚本失败: {}", e))
+ })?;
+
+ if output.status.success() {
+ tracing::info!("已通过 Launch Services API 设置默认浏览器");
+ return Ok(());
+ }
+
+ // 方法 3: 提示用户手动设置
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ tracing::warn!("自动设置默认浏览器失败: {}", stderr);
+ tracing::info!("请手动在系统设置中将 ProxyCast 设置为默认浏览器");
+
+ // 打开系统设置
+ let _ = Command::new("open")
+ .args(["x-apple.systempreferences:com.apple.preference.general"])
+ .output();
+
+ Ok(())
+ }
+
+ /// 恢复原来的默认浏览器
+ async fn restore_default_browser(&self) -> Result<()> {
+ // 获取要恢复的浏览器
+ let browser_id = match &self.original_default_browser {
+ Some(id) if !id.contains("proxycast") => id.clone(),
+ _ => {
+ // 如果没有记录原始浏览器,尝试检测已安装的浏览器
+ self.detect_installed_browser()
+ .await
+ .unwrap_or_else(|| "com.google.Chrome".to_string())
+ }
+ };
+
+ tracing::info!("正在恢复默认浏览器为: {}", browser_id);
+
+ // 使用 duti
+ let duti_result = Command::new("duti")
+ .args(["-s", &browser_id, "http", "all"])
+ .output();
+
+ if let Ok(output) = duti_result {
+ if output.status.success() {
+ let _ = Command::new("duti")
+ .args(["-s", &browser_id, "https", "all"])
+ .output();
+ tracing::info!("已通过 duti 恢复默认浏览器为: {}", browser_id);
+ return Ok(());
+ }
+ }
+
+ // 使用 Swift
+ let swift_code = format!(
+ r#"
+import Foundation
+import CoreServices
+
+let bundleId = "{}" as CFString
+LSSetDefaultHandlerForURLScheme("http" as CFString, bundleId)
+LSSetDefaultHandlerForURLScheme("https" as CFString, bundleId)
+print("OK")
+"#,
+ browser_id
+ );
+
+ let output = Command::new("swift").args(["-e", &swift_code]).output();
+
+ if let Ok(out) = output {
+ if out.status.success() {
+ tracing::info!("已通过 Swift 恢复默认浏览器为: {}", browser_id);
+ } else {
+ let stderr = String::from_utf8_lossy(&out.stderr);
+ tracing::warn!("Swift 恢复默认浏览器失败: {}", stderr);
+ }
+ }
+
+ Ok(())
+ }
+
+ /// 检测已安装的浏览器(用于恢复时选择)
+ async fn detect_installed_browser(&self) -> Option {
+ // 按优先级排序的浏览器列表
+ let browsers = [
+ ("com.google.Chrome", "/Applications/Google Chrome.app"),
+ ("org.mozilla.firefox", "/Applications/Firefox.app"),
+ ("com.microsoft.edgemac", "/Applications/Microsoft Edge.app"),
+ ("com.brave.Browser", "/Applications/Brave Browser.app"),
+ ("com.apple.Safari", "/Applications/Safari.app"),
+ ];
+
+ for (bundle_id, app_path) in &browsers {
+ // 直接检查应用是否存在
+ if std::path::Path::new(app_path).exists() {
+ tracing::info!("检测到已安装的浏览器: {} ({})", bundle_id, app_path);
+ return Some(bundle_id.to_string());
+ }
+ }
+
+ // Safari 总是存在
+ Some("com.apple.Safari".to_string())
+ }
+
+ /// 停止拦截
+ pub async fn stop(&mut self) -> Result<()> {
+ if !self.running {
+ return Ok(());
+ }
+
+ tracing::info!("正在停止 macOS 浏览器拦截器...");
+
+ // 清除全局 URL sender
+ if let Ok(mut global_sender) = GLOBAL_URL_SENDER.lock() {
+ *global_sender = None;
+ }
+
+ // 恢复默认浏览器
+ self.restore_default_browser().await?;
+
+ self.running = false;
+ tracing::info!("macOS 浏览器拦截器已停止");
+ Ok(())
+ }
+
+ /// 检查是否正在拦截
+ pub fn is_running(&self) -> bool {
+ self.running
+ }
+
+ /// 获取当前监听的端口(不再使用,保留接口兼容性)
+ pub fn get_port(&self) -> Option {
+ None
+ }
+
+ /// 恢复系统默认设置
+ pub async fn restore_system_defaults(&self) -> Result<()> {
+ self.restore_default_browser().await
+ }
+
+ /// 临时禁用拦截
+ pub async fn temporarily_disable(&mut self) -> Result<()> {
+ if !self.running {
+ return Ok(());
+ }
+
+ tracing::info!("正在临时禁用 macOS 浏览器拦截器...");
+
+ // 临时恢复默认浏览器
+ self.restore_default_browser().await?;
+
+ tracing::info!("macOS 浏览器拦截器已临时禁用");
+ Ok(())
+ }
+
+ /// 重新启用拦截
+ pub async fn re_enable(&mut self) -> Result<()> {
+ if !self.running {
+ return Err(BrowserInterceptorError::InterceptorError(
+ "拦截器未运行,无法重新启用".to_string(),
+ ));
+ }
+
+ tracing::info!("正在重新启用 macOS 浏览器拦截器...");
+
+ // 重新设置为默认浏览器
+ self.set_as_default_browser().await?;
+
+ tracing::info!("macOS 浏览器拦截器已重新启用");
+ Ok(())
+ }
+}
+
+impl Drop for MacOSInterceptor {
+ fn drop(&mut self) {
+ if self.running {
+ tracing::info!("macOS 浏览器拦截器资源已清理");
+ // 注意:在 drop 中无法使用 async,恢复操作在 stop() 中完成
+ }
+ }
+}
+
+/// 处理从 deep-link 接收到的 URL(由 lib.rs 中的事件监听器调用)
+pub fn handle_deep_link_url(url: String) {
+ tracing::info!("收到 deep-link URL: {}", url);
+
+ // 检查是否是 http/https URL
+ if !url.starts_with("http://") && !url.starts_with("https://") {
+ tracing::debug!("忽略非 HTTP URL: {}", url);
+ return;
+ }
+
+ // 尝试识别来源进程(macOS 上较难获取,使用默认值)
+ let source_process = detect_source_process(&url);
+
+ let intercepted_url = InterceptedUrl::new(url, source_process);
+
+ // 发送到处理器
+ if let Ok(global_sender) = GLOBAL_URL_SENDER.lock() {
+ if let Some(ref sender) = *global_sender {
+ match sender.send(intercepted_url) {
+ Ok(_) => tracing::debug!("URL 已发送到处理器"),
+ Err(e) => tracing::error!("发送 URL 到处理器失败: {:?}", e),
+ }
+ } else {
+ tracing::warn!("拦截器未运行,忽略 URL");
+ }
+ } else {
+ tracing::error!("无法获取 URL sender 锁");
+ }
+}
+
+/// 尝试检测 URL 的来源进程
+fn detect_source_process(url: &str) -> String {
+ // 基于 URL 特征推测来源
+ if url.contains("kiro") || url.contains("amazon") || url.contains("aws") {
+ return "Kiro".to_string();
+ }
+ if url.contains("cursor") || url.contains("anysphere") {
+ return "Cursor".to_string();
+ }
+ if url.contains("vscode") || url.contains("microsoft") || url.contains("visualstudio") {
+ return "VSCode".to_string();
+ }
+ if url.contains("claude") || url.contains("anthropic") {
+ return "Claude App".to_string();
+ }
+ if url.contains("github") {
+ return "GitHub App".to_string();
+ }
+ if url.contains("google") || url.contains("accounts.google") {
+ return "OAuth Request".to_string();
+ }
+
+ // 尝试获取前台应用
+ if let Ok(output) = Command::new("osascript")
+ .args([
+ "-e",
+ "tell application \"System Events\" to get the name of first process whose frontmost is true",
+ ])
+ .output()
+ {
+ if output.status.success() {
+ let app_name = String::from_utf8_lossy(&output.stdout).trim().to_string();
+ if !app_name.is_empty() && app_name != "ProxyCast" {
+ return app_name;
+ }
+ }
+ }
+
+ "Unknown App".to_string()
+}
+
+/// 检查拦截器是否有活跃的 URL sender
+pub fn is_interceptor_active() -> bool {
+ GLOBAL_URL_SENDER
+ .lock()
+ .map(|sender| sender.is_some())
+ .unwrap_or(false)
+}
diff --git a/src-tauri/src/browser_interceptor/platform/windows.rs b/src-tauri/src/browser_interceptor/platform/windows.rs
new file mode 100644
index 000000000..010f84ffc
--- /dev/null
+++ b/src-tauri/src/browser_interceptor/platform/windows.rs
@@ -0,0 +1,454 @@
+use crate::browser_interceptor::{BrowserInterceptorError, InterceptedUrl, Result};
+use chrono::Utc;
+use std::collections::HashMap;
+use std::sync::{Arc, Mutex};
+use std::thread;
+use std::time::Duration;
+use uuid::Uuid;
+
+#[cfg(windows)]
+use {
+ std::ffi::{OsStr, OsString},
+ std::iter::once,
+ std::os::windows::ffi::OsStrExt,
+ std::ptr,
+ windows::{
+ core::*, Win32::Foundation::*, Win32::System::Registry::*, Win32::System::Threading::*,
+ Win32::UI::Shell::*,
+ },
+};
+
+/// Windows 平台的浏览器拦截器
+pub struct WindowsInterceptor {
+ running: bool,
+ original_browser: Option,
+ temp_exe_path: Option,
+ intercepted_urls_handler: Arc>>,
+ monitor_thread: Option>,
+}
+
+#[cfg(windows)]
+impl WindowsInterceptor {
+ pub fn new(url_handler: F) -> Self
+ where
+ F: Fn(InterceptedUrl) + Send + Sync + 'static,
+ {
+ Self {
+ running: false,
+ original_browser: None,
+ temp_exe_path: None,
+ intercepted_urls_handler: Arc::new(Mutex::new(Box::new(url_handler))),
+ monitor_thread: None,
+ }
+ }
+
+ /// 启动拦截
+ pub async fn start(&mut self) -> Result<()> {
+ if self.running {
+ return Err(BrowserInterceptorError::AlreadyRunning);
+ }
+
+ // 备份当前默认浏览器设置
+ self.backup_default_browser().await?;
+
+ // 创建临时拦截程序
+ self.create_interceptor_executable().await?;
+
+ // 设置我们的程序为默认浏览器
+ self.set_as_default_browser().await?;
+
+ // 启动进程监控
+ self.start_process_monitoring().await?;
+
+ self.running = true;
+ tracing::info!("Windows 浏览器拦截器已启动");
+ Ok(())
+ }
+
+ /// 停止拦截
+ pub async fn stop(&mut self) -> Result<()> {
+ if !self.running {
+ return Ok(());
+ }
+
+ // 停止进程监控
+ if let Some(handle) = self.monitor_thread.take() {
+ // 发送停止信号,等待线程结束
+ handle.join().ok();
+ }
+
+ // 恢复原始默认浏览器
+ self.restore_default_browser().await?;
+
+ // 清理临时文件
+ self.cleanup_temp_files().await?;
+
+ self.running = false;
+ tracing::info!("Windows 浏览器拦截器已停止");
+ Ok(())
+ }
+
+ /// 备份当前默认浏览器设置
+ async fn backup_default_browser(&mut self) -> Result<()> {
+ unsafe {
+ let key_path = to_wide_chars(
+ r"Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice",
+ );
+ let mut key: HKEY = HKEY::default();
+
+ let result = RegOpenKeyExW(
+ HKEY_CURRENT_USER,
+ PCWSTR::from_raw(key_path.as_ptr()),
+ 0,
+ KEY_READ,
+ &mut key,
+ );
+
+ if result.is_ok() {
+ let mut buffer = vec![0u16; 1024];
+ let mut buffer_size = buffer.len() * 2;
+
+ let result = RegQueryValueExW(
+ key,
+ PCWSTR::from_raw(to_wide_chars("ProgId").as_ptr()),
+ None,
+ None,
+ Some(buffer.as_mut_ptr() as *mut u8),
+ Some(&mut buffer_size as *mut u32),
+ );
+
+ RegCloseKey(key);
+
+ if result.is_ok() {
+ let prog_id = String::from_utf16_lossy(&buffer[..buffer_size / 2 - 1]);
+ self.original_browser = Some(prog_id);
+ tracing::info!(
+ "备份原始默认浏览器: {}",
+ self.original_browser.as_ref().unwrap()
+ );
+ }
+ }
+ }
+
+ Ok(())
+ }
+
+ /// 创建临时的拦截器可执行文件
+ async fn create_interceptor_executable(&mut self) -> Result<()> {
+ // 创建一个简单的拦截器程序,用于接收 URL 参数
+ let temp_dir = std::env::temp_dir();
+ let exe_path = temp_dir.join("proxycast_browser_interceptor.exe");
+
+ // 创建拦截器脚本内容(批处理脚本)
+ let bat_content = format!(
+ r#"@echo off
+echo URL被拦截: %1 >> "{}\proxycast_intercepted_urls.log"
+"#,
+ temp_dir.to_string_lossy()
+ );
+
+ let bat_path = temp_dir.join("proxycast_browser_interceptor.bat");
+ std::fs::write(&bat_path, bat_content)?;
+
+ // 创建一个简单的可执行文件包装器
+ // 这里我们使用批处理文件,在真实环境中应该编译一个专用的小程序
+ self.temp_exe_path = Some(bat_path.to_string_lossy().to_string());
+
+ Ok(())
+ }
+
+ /// 设置我们的程序为默认浏览器
+ async fn set_as_default_browser(&self) -> Result<()> {
+ if let Some(exe_path) = &self.temp_exe_path {
+ unsafe {
+ // 注册我们的程序为HTTP处理器
+ let prog_id = "ProxyCastInterceptor";
+ let key_path = format!(r"Software\Classes\{}", prog_id);
+
+ self.set_registry_string(&key_path, "", "ProxyCast Browser Interceptor")
+ .await?;
+
+ let command_path = format!(r"{}\shell\open\command", key_path);
+ let command_value = format!(r#"{} "%1""#, exe_path);
+ self.set_registry_string(&command_path, "", &command_value)
+ .await?;
+
+ // 设置为HTTP协议的默认处理器
+ let http_key = r"Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice";
+ self.set_registry_string(http_key, "ProgId", prog_id)
+ .await?;
+
+ // 设置为HTTPS协议的默认处理器
+ let https_key = r"Software\Microsoft\Windows\Shell\Associations\UrlAssociations\https\UserChoice";
+ self.set_registry_string(https_key, "ProgId", prog_id)
+ .await?;
+
+ tracing::info!("已设置 ProxyCast 为临时默认浏览器");
+ }
+ }
+
+ Ok(())
+ }
+
+ /// 设置注册表字符串值
+ async fn set_registry_string(
+ &self,
+ key_path: &str,
+ value_name: &str,
+ value_data: &str,
+ ) -> Result<()> {
+ unsafe {
+ let key_path_wide = to_wide_chars(key_path);
+ let mut key: HKEY = HKEY::default();
+
+ let result = RegCreateKeyExW(
+ HKEY_CURRENT_USER,
+ PCWSTR::from_raw(key_path_wide.as_ptr()),
+ 0,
+ PCWSTR::null(),
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ None,
+ &mut key,
+ None,
+ );
+
+ if result.is_ok() {
+ let value_name_wide = to_wide_chars(value_name);
+ let value_data_wide = to_wide_chars(value_data);
+
+ RegSetValueExW(
+ key,
+ PCWSTR::from_raw(value_name_wide.as_ptr()),
+ 0,
+ REG_SZ,
+ Some(value_data_wide.as_ptr() as *const u8),
+ value_data_wide.len() * 2,
+ );
+
+ RegCloseKey(key);
+ }
+ }
+
+ Ok(())
+ }
+
+ /// 启动进程监控
+ async fn start_process_monitoring(&mut self) -> Result<()> {
+ let handler = Arc::clone(&self.intercepted_urls_handler);
+ let temp_dir = std::env::temp_dir();
+ let log_file = temp_dir.join("proxycast_intercepted_urls.log");
+
+ let handle = thread::spawn(move || {
+ loop {
+ // 检查拦截日志文件
+ if let Ok(content) = std::fs::read_to_string(&log_file) {
+ for line in content.lines() {
+ if line.starts_with("URL被拦截: ") {
+ let url = line.replace("URL被拦截: ", "");
+ if !url.is_empty() && should_intercept_url(&url) {
+ let intercepted = InterceptedUrl {
+ id: Uuid::new_v4().to_string(),
+ url: url.clone(),
+ source_process: "Unknown".to_string(),
+ timestamp: Utc::now(),
+ copied: false,
+ opened_in_browser: false,
+ dismissed: false,
+ };
+
+ // 调用处理器
+ if let Ok(handler_guard) = handler.lock() {
+ handler_guard(intercepted);
+ }
+ }
+ }
+ }
+
+ // 清空日志文件避免重复处理
+ std::fs::write(&log_file, "").ok();
+ }
+
+ thread::sleep(Duration::from_millis(1000));
+ }
+ });
+
+ self.monitor_thread = Some(handle);
+ Ok(())
+ }
+
+ /// 恢复原始默认浏览器
+ async fn restore_default_browser(&self) -> Result<()> {
+ if let Some(original_browser) = &self.original_browser {
+ // 恢复HTTP协议处理器
+ let http_key =
+ r"Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice";
+ self.set_registry_string(http_key, "ProgId", original_browser)
+ .await?;
+
+ // 恢复HTTPS协议处理器
+ let https_key =
+ r"Software\Microsoft\Windows\Shell\Associations\UrlAssociations\https\UserChoice";
+ self.set_registry_string(https_key, "ProgId", original_browser)
+ .await?;
+
+ tracing::info!("已恢复原始默认浏览器: {}", original_browser);
+ }
+
+ Ok(())
+ }
+
+ /// 清理临时文件
+ async fn cleanup_temp_files(&self) -> Result<()> {
+ if let Some(exe_path) = &self.temp_exe_path {
+ std::fs::remove_file(exe_path).ok();
+ }
+
+ let temp_dir = std::env::temp_dir();
+ let log_file = temp_dir.join("proxycast_intercepted_urls.log");
+ std::fs::remove_file(log_file).ok();
+
+ Ok(())
+ }
+
+ /// 检查是否正在拦截
+ pub fn is_running(&self) -> bool {
+ self.running
+ }
+
+ /// 恢复系统默认设置
+ pub async fn restore_system_defaults(&self) -> Result<()> {
+ // 恢复默认浏览器设置
+ self.restore_default_browser().await?;
+ tracing::info!("系统默认设置已恢复");
+ Ok(())
+ }
+
+ /// 临时禁用拦截
+ pub async fn temporarily_disable(&mut self) -> Result<()> {
+ if let Some(original_browser) = &self.original_browser {
+ self.restore_default_browser().await?;
+ tracing::info!("拦截器已临时禁用");
+ }
+ Ok(())
+ }
+
+ /// 重新启用拦截
+ pub async fn re_enable(&mut self) -> Result<()> {
+ self.set_as_default_browser().await?;
+ tracing::info!("拦截器已重新启用");
+ Ok(())
+ }
+}
+
+#[cfg(not(windows))]
+impl WindowsInterceptor {
+ pub fn new(_url_handler: F) -> Self
+ where
+ F: Fn(InterceptedUrl) + Send + Sync + 'static,
+ {
+ Self {
+ running: false,
+ original_browser: None,
+ temp_exe_path: None,
+ intercepted_urls_handler: Arc::new(Mutex::new(Box::new(|_| {}))),
+ monitor_thread: None,
+ }
+ }
+
+ pub async fn start(&mut self) -> Result<()> {
+ Err(BrowserInterceptorError::UnsupportedPlatform(
+ "Windows interceptor only supports Windows platform".to_string(),
+ ))
+ }
+
+ pub async fn stop(&mut self) -> Result<()> {
+ Ok(())
+ }
+
+ pub fn is_running(&self) -> bool {
+ false
+ }
+
+ pub async fn restore_system_defaults(&self) -> Result<()> {
+ Ok(())
+ }
+
+ pub async fn temporarily_disable(&mut self) -> Result<()> {
+ Ok(())
+ }
+
+ pub async fn re_enable(&mut self) -> Result<()> {
+ Ok(())
+ }
+}
+
+impl Drop for WindowsInterceptor {
+ fn drop(&mut self) {
+ if self.running {
+ // 在析构时恢复系统默认设置
+ tokio::runtime::Handle::try_current().map(|handle| {
+ handle.block_on(async {
+ let _ = self.stop().await;
+ let _ = self.restore_system_defaults().await;
+ })
+ });
+ }
+ }
+}
+
+// Windows 特定的辅助函数
+fn to_wide_chars(s: &str) -> Vec {
+ OsStr::new(s).encode_wide().chain(once(0)).collect()
+}
+
+/// 检查进程是否为目标应用
+pub fn is_target_process(process_name: &str) -> bool {
+ let target_processes = [
+ "kiro",
+ "kiro.exe",
+ "cursor",
+ "cursor.exe",
+ "code",
+ "code.exe",
+ ];
+ target_processes
+ .iter()
+ .any(|&target| process_name.to_lowercase().contains(&target.to_lowercase()))
+}
+
+/// 检查 URL 是否匹配拦截模式
+pub fn should_intercept_url(url: &str) -> bool {
+ let patterns = [
+ "https://auth.",
+ "https://accounts.google.com",
+ "https://github.com/login",
+ "https://login.microsoftonline.com",
+ "/oauth/",
+ "/auth/",
+ "localhost:8080/auth", // OAuth 回调地址
+ ];
+
+ patterns.iter().any(|&pattern| url.contains(pattern))
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_is_target_process() {
+ assert!(is_target_process("kiro.exe"));
+ assert!(is_target_process("cursor"));
+ assert!(is_target_process("code.exe"));
+ assert!(!is_target_process("notepad.exe"));
+ }
+
+ #[test]
+ fn test_should_intercept_url() {
+ assert!(should_intercept_url("https://accounts.google.com/oauth"));
+ assert!(should_intercept_url("https://github.com/login/oauth"));
+ assert!(should_intercept_url("localhost:8080/auth/callback"));
+ assert!(!should_intercept_url("https://example.com"));
+ }
+}
diff --git a/src-tauri/src/browser_interceptor/state_manager.rs b/src-tauri/src/browser_interceptor/state_manager.rs
new file mode 100644
index 000000000..5586a3725
--- /dev/null
+++ b/src-tauri/src/browser_interceptor/state_manager.rs
@@ -0,0 +1,504 @@
+use crate::browser_interceptor::{BrowserInterceptorError, InterceptorState, Result};
+use chrono::Utc;
+use std::sync::{Arc, RwLock};
+use tokio::time::{Duration, Instant};
+
+#[cfg(windows)]
+use {
+ std::ffi::OsStr,
+ std::iter::once,
+ std::os::windows::ffi::OsStrExt,
+ windows::{core::*, Win32::Foundation::*, Win32::System::Registry::*},
+};
+
+/// 状态管理器,负责管理拦截器的状态和恢复机制
+pub struct StateManager {
+ state: Arc>,
+ original_system_state: Arc>>,
+ temporary_disable_timer: Arc>>,
+}
+
+/// 系统原始状态备份
+#[derive(Debug, Clone)]
+pub struct SystemState {
+ pub default_browser: Option,
+ pub registry_backup: std::collections::HashMap,
+ pub environment_backup: std::collections::HashMap,
+ pub timestamp: chrono::DateTime,
+}
+
+impl StateManager {
+ pub fn new() -> Self {
+ Self {
+ state: Arc::new(RwLock::new(InterceptorState::default())),
+ original_system_state: Arc::new(RwLock::new(None)),
+ temporary_disable_timer: Arc::new(RwLock::new(None)),
+ }
+ }
+
+ /// 获取当前状态
+ pub fn get_state(&self) -> Result {
+ self.state
+ .read()
+ .map_err(|e| BrowserInterceptorError::StateError(format!("读取状态失败: {}", e)))
+ .map(|state| state.clone())
+ }
+
+ /// 启用拦截器
+ pub async fn enable_interceptor(&self) -> Result<()> {
+ // 备份系统状态
+ self.backup_system_state().await?;
+
+ // 更新状态
+ {
+ let mut state = self
+ .state
+ .write()
+ .map_err(|e| BrowserInterceptorError::StateError(format!("写入状态失败: {}", e)))?;
+
+ state.enabled = true;
+ state.can_restore = true;
+ state.last_activity = Some(Utc::now());
+ }
+
+ tracing::info!("浏览器拦截器已启用");
+ Ok(())
+ }
+
+ /// 禁用拦截器
+ pub async fn disable_interceptor(&self) -> Result<()> {
+ {
+ let mut state = self
+ .state
+ .write()
+ .map_err(|e| BrowserInterceptorError::StateError(format!("写入状态失败: {}", e)))?;
+
+ state.enabled = false;
+ state.active_hooks.clear();
+ state.last_activity = Some(Utc::now());
+ }
+
+ tracing::info!("浏览器拦截器已禁用");
+ Ok(())
+ }
+
+ /// 临时禁用拦截器
+ pub async fn temporary_disable(&self, duration_seconds: u64) -> Result<()> {
+ self.disable_interceptor().await?;
+
+ // 设置定时器
+ {
+ let mut timer = self.temporary_disable_timer.write().map_err(|e| {
+ BrowserInterceptorError::StateError(format!("设置定时器失败: {}", e))
+ })?;
+ *timer = Some(Instant::now() + Duration::from_secs(duration_seconds));
+ }
+
+ // 启动后台任务来重新启用
+ let state_manager = self.clone();
+ tokio::spawn(async move {
+ tokio::time::sleep(Duration::from_secs(duration_seconds)).await;
+ if let Err(e) = state_manager.enable_interceptor().await {
+ tracing::error!("自动重新启用拦截器失败: {}", e);
+ } else {
+ tracing::info!("拦截器已自动重新启用");
+ }
+ });
+
+ tracing::info!("拦截器已临时禁用 {} 秒", duration_seconds);
+ Ok(())
+ }
+
+ /// 恢复正常浏览器行为
+ pub async fn restore_normal_behavior(&self) -> Result<()> {
+ // 先禁用拦截器
+ self.disable_interceptor().await?;
+
+ // 恢复系统状态
+ self.restore_system_state().await?;
+
+ {
+ let mut state = self
+ .state
+ .write()
+ .map_err(|e| BrowserInterceptorError::StateError(format!("写入状态失败: {}", e)))?;
+ state.can_restore = false;
+ }
+
+ tracing::info!("已恢复正常浏览器行为");
+ Ok(())
+ }
+
+ /// 增加拦截计数
+ pub fn increment_intercept_count(&self) -> Result<()> {
+ let mut state = self
+ .state
+ .write()
+ .map_err(|e| BrowserInterceptorError::StateError(format!("写入状态失败: {}", e)))?;
+
+ state.intercepted_count += 1;
+ state.last_activity = Some(Utc::now());
+
+ Ok(())
+ }
+
+ /// 添加活跃钩子
+ pub fn add_active_hook(&self, hook_name: String) -> Result<()> {
+ let mut state = self
+ .state
+ .write()
+ .map_err(|e| BrowserInterceptorError::StateError(format!("写入状态失败: {}", e)))?;
+
+ if !state.active_hooks.contains(&hook_name) {
+ state.active_hooks.push(hook_name);
+ }
+
+ Ok(())
+ }
+
+ /// 移除活跃钩子
+ pub fn remove_active_hook(&self, hook_name: &str) -> Result<()> {
+ let mut state = self
+ .state
+ .write()
+ .map_err(|e| BrowserInterceptorError::StateError(format!("写入状态失败: {}", e)))?;
+
+ state.active_hooks.retain(|h| h != hook_name);
+
+ Ok(())
+ }
+
+ /// 备份系统状态
+ async fn backup_system_state(&self) -> Result<()> {
+ let system_state = SystemState {
+ default_browser: self.get_default_browser().await?,
+ registry_backup: self.backup_registry_keys().await?,
+ environment_backup: self.backup_environment_variables().await?,
+ timestamp: Utc::now(),
+ };
+
+ {
+ let mut backup = self.original_system_state.write().map_err(|e| {
+ BrowserInterceptorError::StateError(format!("备份系统状态失败: {}", e))
+ })?;
+ *backup = Some(system_state);
+ }
+
+ tracing::info!("系统状态已备份");
+ Ok(())
+ }
+
+ /// 恢复系统状态
+ async fn restore_system_state(&self) -> Result<()> {
+ let backup = {
+ let backup_guard = self.original_system_state.read().map_err(|e| {
+ BrowserInterceptorError::StateError(format!("读取备份状态失败: {}", e))
+ })?;
+ backup_guard.clone()
+ };
+
+ if let Some(system_state) = backup {
+ // 恢复默认浏览器
+ if let Some(default_browser) = &system_state.default_browser {
+ self.restore_default_browser(default_browser).await?;
+ }
+
+ // 恢复注册表项
+ self.restore_registry_keys(&system_state.registry_backup)
+ .await?;
+
+ // 恢复环境变量
+ self.restore_environment_variables(&system_state.environment_backup)
+ .await?;
+
+ tracing::info!("系统状态已恢复到 {} 的备份", system_state.timestamp);
+ } else {
+ tracing::warn!("没有找到系统状态备份");
+ }
+
+ Ok(())
+ }
+
+ /// 获取默认浏览器(平台特定实现)
+ async fn get_default_browser(&self) -> Result